* I2PTunnel SOCKS and SOCKS IRC clients:

- Add local proxy username/password authorization
This commit is contained in:
zzz
2010-11-14 14:22:45 +00:00
parent 7cbf74d3f2
commit cd621f2b4b
4 changed files with 88 additions and 19 deletions

View File

@@ -47,7 +47,7 @@ public class I2PSOCKSIRCTunnel extends I2PSOCKSTunnel {
protected void clientConnectionRun(Socket s) {
try {
//_log.error("SOCKS IRC Tunnel Start");
SOCKSServer serv = SOCKSServerFactory.createSOCKSServer(s);
SOCKSServer serv = SOCKSServerFactory.createSOCKSServer(s, getTunnel().getClientOptions());
Socket clientSock = serv.getClientSocket();
I2PSocket destSock = serv.getDestinationI2PSocket(this);
StringBuffer expectedPong = new StringBuffer();

View File

@@ -51,7 +51,7 @@ public class I2PSOCKSTunnel extends I2PTunnelClientBase {
protected void clientConnectionRun(Socket s) {
try {
SOCKSServer serv = SOCKSServerFactory.createSOCKSServer(s);
SOCKSServer serv = SOCKSServerFactory.createSOCKSServer(s, getTunnel().getClientOptions());
Socket clientSock = serv.getClientSocket();
I2PSocket destSock = serv.getDestinationI2PSocket(this);
new I2PTunnelRunner(clientSock, destSock, sockLock, null, mySockets);

View File

@@ -16,12 +16,14 @@ import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase;
import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.util.HexDump;
import net.i2p.util.Log;
@@ -37,8 +39,10 @@ public class SOCKS5Server extends SOCKSServer {
private static final int SOCKS_VERSION_5 = 0x05;
private Socket clientSock = null;
private final Socket clientSock;
private final Properties props;
private boolean setupCompleted = false;
private final boolean authRequired;
/**
* Create a SOCKS5 server that communicates with the client using
@@ -49,9 +53,15 @@ public class SOCKS5Server extends SOCKSServer {
* client socket.
*
* @param clientSock client socket
* @param props non-null
*/
public SOCKS5Server(Socket clientSock) {
public SOCKS5Server(Socket clientSock, Properties props) {
this.clientSock = clientSock;
this.props = props;
this.authRequired =
Boolean.valueOf(props.getProperty(I2PTunnelHTTPClientBase.PROP_AUTH)).booleanValue() &&
props.containsKey(I2PTunnelHTTPClientBase.PROP_USER) &&
props.containsKey(I2PTunnelHTTPClientBase.PROP_PW);
}
public Socket getClientSocket() throws SOCKSException {
@@ -90,25 +100,64 @@ public class SOCKS5Server extends SOCKSServer {
for (int i = 0; i < nMethods; ++i) {
int meth = in.readByte() & 0xff;
if (meth == Method.NO_AUTH_REQUIRED) {
if (((!authRequired) && meth == Method.NO_AUTH_REQUIRED) ||
(authRequired && meth == Method.USERNAME_PASSWORD)) {
// That's fine, we do support this method
method = meth;
}
}
boolean canContinue = false;
switch (method) {
case Method.NO_AUTH_REQUIRED:
case Method.USERNAME_PASSWORD:
_log.debug("username/password authentication required");
sendInitReply(Method.USERNAME_PASSWORD, out);
verifyPassword(in, out);
return;
case Method.NO_AUTH_REQUIRED:
_log.debug("no authentication required");
sendInitReply(Method.NO_AUTH_REQUIRED, out);
return;
default:
default:
_log.debug("no suitable authentication methods found (" + Integer.toHexString(method) + ")");
sendInitReply(Method.NO_ACCEPTABLE_METHODS, out);
throw new SOCKSException("Unsupported authentication method");
}
}
/**
* Wait for the username/password message and verify or throw SOCKSException on failure
* @since 0.8.2
*/
private void verifyPassword(DataInputStream in, DataOutputStream out) throws IOException, SOCKSException {
int c = in.readByte() & 0xff;
if (c != AUTH_VERSION)
throw new SOCKSException("Unsupported authentication version");
c = in.readByte() & 0xff;
if (c <= 0)
throw new SOCKSException("Bad authentication");
byte[] user = new byte[c];
in.readFully(user);
c = in.readByte() & 0xff;
if (c <= 0)
throw new SOCKSException("Bad authentication");
byte[] pw = new byte[c];
in.readFully(pw);
// Hopefully these are in UTF-8, since that's what our config file is in
// these throw UnsupportedEncodingException which is an IOE
String u = new String(user, "UTF-8");
String p = new String(pw, "UTF-8");
String configUser = props.getProperty(I2PTunnelHTTPClientBase.PROP_USER);
String configPW = props.getProperty(I2PTunnelHTTPClientBase.PROP_PW);
if ((!u.equals(configUser)) || (!p.equals(configPW))) {
_log.error("SOCKS authorization failure");
sendAuthReply(AUTH_FAILURE, out);
throw new SOCKSException("SOCKS authorization failure");
}
if (_log.shouldLog(Log.INFO))
_log.info("SOCKS authorization success, user: " + u);
sendAuthReply(AUTH_SUCCESS, out);
}
/**
* SOCKS5 request management. This method assumes that all the
* stuff preceding or enveloping the actual request (e.g. protocol
@@ -213,17 +262,24 @@ public class SOCKS5Server extends SOCKSServer {
* Send the specified reply during SOCKS5 initialization
*/
private void sendInitReply(int replyCode, DataOutputStream out) throws IOException {
ByteArrayOutputStream reps = new ByteArrayOutputStream();
reps.write(SOCKS_VERSION_5);
reps.write(replyCode);
byte[] reply = reps.toByteArray();
if (_log.shouldLog(Log.DEBUG)) {
byte[] reply = new byte[2];
reply[0] = SOCKS_VERSION_5;
reply[1] = (byte) replyCode;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending init reply:\n" + HexDump.dump(reply));
}
out.write(reply);
}
/**
* Send the specified reply during SOCKS5 authorization
* @since 0.8.2
*/
private void sendAuthReply(int replyCode, DataOutputStream out) throws IOException {
byte[] reply = new byte[2];
reply[0] = AUTH_VERSION;
reply[1] = (byte) replyCode;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending auth reply:\n" + HexDump.dump(reply));
out.write(reply);
}
@@ -435,6 +491,7 @@ public class SOCKS5Server extends SOCKSServer {
*/
private static class Method {
private static final int NO_AUTH_REQUIRED = 0x00;
private static final int USERNAME_PASSWORD = 0x02;
private static final int NO_ACCEPTABLE_METHODS = 0xff;
}
@@ -461,4 +518,8 @@ public class SOCKS5Server extends SOCKSServer {
private static final int COMMAND_NOT_SUPPORTED = 0x07;
private static final int ADDRESS_TYPE_NOT_SUPPORTED = 0x08;
}
private static final int AUTH_VERSION = 1;
private static final int AUTH_SUCCESS = 0;
private static final int AUTH_FAILURE = 1;
}

View File

@@ -10,7 +10,9 @@ import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Properties;
import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase;
import net.i2p.util.Log;
/**
@@ -35,8 +37,9 @@ public class SOCKSServerFactory {
* provided sockets's input stream.
*
* @param s a Socket used to choose the SOCKS server type
* @param props non-null
*/
public static SOCKSServer createSOCKSServer(Socket s) throws SOCKSException {
public static SOCKSServer createSOCKSServer(Socket s, Properties props) throws SOCKSException {
SOCKSServer serv;
try {
@@ -46,11 +49,16 @@ public class SOCKSServerFactory {
switch (socksVer) {
case 0x04:
// SOCKS version 4/4a
if (Boolean.valueOf(props.getProperty(I2PTunnelHTTPClientBase.PROP_AUTH)).booleanValue() &&
props.containsKey(I2PTunnelHTTPClientBase.PROP_USER) &&
props.containsKey(I2PTunnelHTTPClientBase.PROP_PW)) {
throw new SOCKSException("SOCKS 4/4a not supported when authorization is required");
}
serv = new SOCKS4aServer(s);
break;
case 0x05:
// SOCKS version 5
serv = new SOCKS5Server(s);
serv = new SOCKS5Server(s, props);
break;
case 'C':
case 'G':