diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSIRCTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSIRCTunnel.java index 8b9f70591..7ad03a021 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSIRCTunnel.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSIRCTunnel.java @@ -14,6 +14,7 @@ import net.i2p.i2ptunnel.I2PTunnel; import net.i2p.i2ptunnel.irc.IrcInboundFilter; import net.i2p.i2ptunnel.irc.IrcOutboundFilter; import net.i2p.i2ptunnel.Logging; +import net.i2p.socks.SOCKSException; import net.i2p.util.EventDispatcher; import net.i2p.util.I2PAppThread; import net.i2p.util.Log; diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSTunnel.java index ab6b7a7c1..94cc817e7 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSTunnel.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSTunnel.java @@ -21,6 +21,7 @@ import net.i2p.i2ptunnel.I2PTunnel; import net.i2p.i2ptunnel.I2PTunnelClientBase; import net.i2p.i2ptunnel.I2PTunnelRunner; import net.i2p.i2ptunnel.Logging; +import net.i2p.socks.SOCKSException; import net.i2p.util.EventDispatcher; import net.i2p.util.Log; diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS4aServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS4aServer.java index f32cdabfc..a2789b188 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS4aServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS4aServer.java @@ -12,7 +12,6 @@ import java.io.DataOutputStream; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; -import java.net.SocketException; import java.util.List; import java.util.Locale; import java.util.Properties; @@ -26,8 +25,10 @@ import net.i2p.client.streaming.I2PSocket; import net.i2p.client.streaming.I2PSocketOptions; import net.i2p.data.DataFormatException; import net.i2p.data.Destination; +import static net.i2p.socks.SOCKS4Constants.*; import net.i2p.util.HexDump; import net.i2p.util.Log; +import net.i2p.socks.SOCKSException; /* * Class that manages SOCKS 4/4a connections, and forwards them to @@ -286,12 +287,6 @@ class SOCKS4aServer extends SOCKSServer { sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out); } catch (IOException ioe) {} throw new SOCKSException("Error in destination format"); - } catch (SocketException e) { - try { - sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out); - } catch (IOException ioe) {} - throw new SOCKSException("Error connecting (" - + e.getMessage() + ")"); } catch (IOException e) { try { sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out); @@ -308,17 +303,4 @@ class SOCKS4aServer extends SOCKSServer { return destSock; } - - /* - * Some namespaces to enclose SOCKS protocol codes - */ - private static class Command { - private static final int CONNECT = 0x01; - private static final int BIND = 0x02; - } - - private static class Reply { - private static final int SUCCEEDED = 0x5a; - private static final int CONNECTION_REFUSED = 0x5b; - } } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS5Server.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS5Server.java index f3d5246fd..f82ad339e 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS5Server.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS5Server.java @@ -9,10 +9,11 @@ package net.i2p.i2ptunnel.socks; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; +import java.io.InputStream; import java.io.IOException; +import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; -import java.net.SocketException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; @@ -31,8 +32,11 @@ import net.i2p.data.DataHelper; import net.i2p.data.Destination; import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase; import net.i2p.i2ptunnel.I2PTunnel; +import static net.i2p.socks.SOCKS5Constants.*; import net.i2p.util.HexDump; import net.i2p.util.Log; +import net.i2p.socks.SOCKS5Client; +import net.i2p.socks.SOCKSException; /* * Class that manages SOCKS5 connections, and forwards them to @@ -42,8 +46,6 @@ import net.i2p.util.Log; */ class SOCKS5Server extends SOCKSServer { - private static final int SOCKS_VERSION_5 = 0x05; - private boolean setupCompleted = false; private final boolean authRequired; @@ -95,7 +97,7 @@ class SOCKS5Server extends SOCKSServer { * SOCKS5 connection initialization. This method assumes that * SOCKS "VER" field has been stripped from the input stream. */ - private void init(DataInputStream in, DataOutputStream out) throws IOException, SOCKSException { + private void init(DataInputStream in, DataOutputStream out) throws IOException { int nMethods = in.readUnsignedByte(); int method = Method.NO_ACCEPTABLE_METHODS; @@ -129,7 +131,7 @@ class SOCKS5Server extends SOCKSServer { * 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 { + private void verifyPassword(DataInputStream in, DataOutputStream out) throws IOException { int c = in.readUnsignedByte(); if (c != AUTH_VERSION) { _log.logAlways(Log.WARN, "SOCKS proxy authentication failed"); @@ -171,7 +173,7 @@ class SOCKS5Server extends SOCKSServer { * initialization, integrity/confidentiality encapsulations, etc) * has been stripped out of the input/output streams. */ - private int manageRequest(DataInputStream in, DataOutputStream out) throws IOException, SOCKSException { + private int manageRequest(DataInputStream in, DataOutputStream out) throws IOException { int socksVer = in.readUnsignedByte(); if (socksVer != SOCKS_VERSION_5) { _log.debug("error in SOCKS5 request (protocol != 5?)"); @@ -449,13 +451,6 @@ class SOCKS5Server extends SOCKSServer { sendRequestReply(Reply.HOST_UNREACHABLE, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out); } catch (IOException ioe) {} throw new SOCKSException("Error in destination format"); - } catch (SocketException e) { - if (_log.shouldLog(Log.WARN)) - _log.warn("socks error", e); - try { - sendRequestReply(Reply.HOST_UNREACHABLE, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out); - } catch (IOException ioe) {} - throw new SOCKSException("Error connecting: " + e); } catch (IOException e) { if (_log.shouldLog(Log.WARN)) _log.warn("socks error", e); @@ -480,7 +475,7 @@ class SOCKS5Server extends SOCKSServer { * @return open socket or throws error * @since 0.8.2 */ - private I2PSocket outproxyConnect(I2PSOCKSTunnel tun, String proxy) throws IOException, SOCKSException, DataFormatException, I2PException { + private I2PSocket outproxyConnect(I2PSOCKSTunnel tun, String proxy) throws IOException, I2PException { Properties overrides = new Properties(); overrides.setProperty("option.i2p.streaming.connectDelay", "1000"); I2PSocketOptions proxyOpts = tun.buildOptions(overrides); @@ -488,10 +483,11 @@ class SOCKS5Server extends SOCKSServer { if (dest == null) throw new SOCKSException("Outproxy not found"); I2PSocket destSock = tun.createI2PSocket(dest, proxyOpts); - DataOutputStream out = null; - DataInputStream in = null; + OutputStream out = null; + InputStream in = null; try { - out = new DataOutputStream(destSock.getOutputStream()); + out = destSock.getOutputStream(); + in = destSock.getInputStream(); boolean authAvail = Boolean.parseBoolean(props.getProperty(I2PTunnelHTTPClientBase.PROP_OUTPROXY_AUTH)); String configUser = null; String configPW = null; @@ -501,110 +497,14 @@ class SOCKS5Server extends SOCKSServer { if (configUser == null || configPW == null) { configUser = props.getProperty(I2PTunnelHTTPClientBase.PROP_OUTPROXY_USER); configPW = props.getProperty(I2PTunnelHTTPClientBase.PROP_OUTPROXY_PW); - if (configUser == null || configPW == null) - authAvail = false; } } - - // send the init - out.writeByte(SOCKS_VERSION_5); - if (authAvail) { - out.writeByte(2); - out.writeByte(Method.NO_AUTH_REQUIRED); - out.writeByte(Method.USERNAME_PASSWORD); - } else { - out.writeByte(1); - out.writeByte(Method.NO_AUTH_REQUIRED); - } - out.flush(); - - // read init reply - in = new DataInputStream(destSock.getInputStream()); - // is this right or should we not try to do 5-to-4 conversion? - int hisVersion = in.readByte(); - if (hisVersion != SOCKS_VERSION_5 /* && addrtype == AddressType.DOMAINNAME */ ) - throw new SOCKSException("SOCKS Outproxy is not Version 5"); - //else if (hisVersion != 4) - // throw new SOCKSException("Unsupported SOCKS Outproxy Version"); - - int method = in.readByte(); - if (method == Method.NO_AUTH_REQUIRED) { - // good - } else if (method == Method.USERNAME_PASSWORD) { - if (authAvail) { - // send the auth - out.writeByte(AUTH_VERSION); - byte[] user = configUser.getBytes("UTF-8"); - byte[] pw = configPW.getBytes("UTF-8"); - out.writeByte(user.length); - out.write(user); - out.writeByte(pw.length); - out.write(pw); - out.flush(); - // read the auth reply - if (in.readByte() != AUTH_VERSION) - throw new SOCKSException("Bad auth version from outproxy"); - if (in.readByte() != AUTH_SUCCESS) - throw new SOCKSException("Outproxy authorization failure"); - } else { - throw new SOCKSException("Outproxy requires authorization, please configure username/password"); - } - } else { - throw new SOCKSException("Outproxy authorization failure"); - } - - // send the connect command - out.writeByte(SOCKS_VERSION_5); - out.writeByte(Command.CONNECT); - out.writeByte(0); // reserved - out.writeByte(addressType); - if (addressType == AddressType.IPV4) { - out.write(InetAddress.getByName(connHostName).getAddress()); - } else if (addressType == AddressType.DOMAINNAME) { - byte[] d = connHostName.getBytes("ISO-8859-1"); - out.writeByte(d.length); - out.write(d); - } else { - // shouldn't happen - throw new SOCKSException("Unknown address type for outproxy?"); - } - out.writeShort(connPort); - out.flush(); - - // read the connect reply - hisVersion = in.readByte(); - if (hisVersion != SOCKS_VERSION_5) - throw new SOCKSException("Outproxy response is not Version 5"); - int reply = in.readByte(); - in.readByte(); // reserved - int type = in.readByte(); - int count = 0; - if (type == AddressType.IPV4) { - count = 4; - } else if (type == AddressType.DOMAINNAME) { - count = in.readUnsignedByte(); - } else if (type == AddressType.IPV6) { - count = 16; - } else { - throw new SOCKSException("Unsupported address type in outproxy response"); - } - byte[] addr = new byte[count]; - in.readFully(addr); // address - in.readUnsignedShort(); // port - if (reply != Reply.SUCCEEDED) - throw new SOCKSException("Outproxy rejected request, response = " + reply); - // throw away the address in the response - // todo pass the response through? + SOCKS5Client.connect(in, out, connHostName, connPort, configUser, configPW); } catch (IOException e) { try { destSock.close(); } catch (IOException ioe) {} if (in != null) try { in.close(); } catch (IOException ioe) {} if (out != null) try { out.close(); } catch (IOException ioe) {} throw e; - } catch (SOCKSException e) { - try { destSock.close(); } catch (IOException ioe) {} - if (in != null) try { in.close(); } catch (IOException ioe) {} - if (out != null) try { out.close(); } catch (IOException ioe) {} - throw e; } // that's it, caller will send confirmation to our client return destSock; @@ -651,7 +551,6 @@ class SOCKS5Server extends SOCKSServer { if (command != Command.UDP_ASSOCIATE) break; } catch (IOException ioe) { break; } - catch (SOCKSException ioe) { break; } } for (Integer i : ports) @@ -663,41 +562,4 @@ class SOCKS5Server extends SOCKSServer { // throw new SOCKSException("End of UDP Processing"); } - - /* - * Some namespaces to enclose SOCKS protocol codes - */ - 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; - } - - private static class AddressType { - private static final int IPV4 = 0x01; - private static final int DOMAINNAME = 0x03; - private static final int IPV6 = 0x04; - } - - private static class Command { - private static final int CONNECT = 0x01; - private static final int BIND = 0x02; - private static final int UDP_ASSOCIATE = 0x03; - } - - private static class Reply { - private static final int SUCCEEDED = 0x00; - private static final int GENERAL_SOCKS_SERVER_FAILURE = 0x01; - private static final int CONNECTION_NOT_ALLOWED_BY_RULESET = 0x02; - private static final int NETWORK_UNREACHABLE = 0x03; - private static final int HOST_UNREACHABLE = 0x04; - private static final int CONNECTION_REFUSED = 0x05; - private static final int TTL_EXPIRED = 0x06; - 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; } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServer.java index 34f79567f..8479739f4 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServer.java @@ -15,6 +15,7 @@ import net.i2p.app.ClientAppManager; import net.i2p.app.Outproxy; import net.i2p.client.streaming.I2PSocket; import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase; +import net.i2p.socks.SOCKSException; import net.i2p.util.Log; /** diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServerFactory.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServerFactory.java index 416c0a62b..a9672295f 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServerFactory.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServerFactory.java @@ -15,6 +15,7 @@ import java.util.Properties; import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase; +import net.i2p.socks.SOCKSException; /** * Factory class for creating SOCKS forwarders through I2P diff --git a/core/java/src/net/i2p/socks/SOCKS4Client.java b/core/java/src/net/i2p/socks/SOCKS4Client.java new file mode 100644 index 000000000..f3caf8727 --- /dev/null +++ b/core/java/src/net/i2p/socks/SOCKS4Client.java @@ -0,0 +1,109 @@ +/* I2PSOCKSTunnel is released under the terms of the GNU GPL, + * with an additional exception. For further details, see the + * licensing terms in I2PTunnel.java. + * + * Copyright (c) 2004 by human + */ +package net.i2p.socks; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; + +import org.apache.http.conn.util.InetAddressUtils; + +import static net.i2p.socks.SOCKS4Constants.*; + +/** + * A simple SOCKS 4/4a client. + * Note: Caller is advised to setSoTimeout on the socket. Not done here. + * + * @since 0.9.33 adapted from net.i2p.i2ptunnel.socks.SOCKS5Server + */ +public class SOCKS4Client { + + private SOCKS4Client() {} + + /** + * Act as a SOCKS 4 client to connect to a proxy + * + * Will throw and close sock on all errors. + * Caller must close sock on success. + * + * @param sock socket to the proxy + * @param connHostName hostname for the proxy to connect to + * @param connPort port for the proxy to connect to + */ + public static void connect(Socket sock, String connHostName, int connPort) throws IOException { + InputStream in = null; + OutputStream out = null; + try { + in = sock.getInputStream(); + out = sock.getOutputStream(); + connect(in, out, connHostName, connPort); + } catch (IOException e) { + try { sock.close(); } catch (IOException ioe) {} + if (in != null) try { in.close(); } catch (IOException ioe) {} + if (out != null) try { out.close(); } catch (IOException ioe) {} + throw e; + } + } + + /** + * Act as a SOCKS 4 client to connect to a proxy + * + * Will throw and close pin and pout on all errors. + * Caller must close pin and pout on success. + * + * @param connHostName hostname for the proxy to connect to + * @param connPort port for the proxy to connect to + */ + public static void connect(InputStream pin, OutputStream pout, String connHostName, int connPort) throws IOException { + DataOutputStream out = null; + DataInputStream in = null; + try { + out = new DataOutputStream(pout); + + // send the init + out.writeByte(SOCKS_VERSION_4); + out.writeByte(Command.CONNECT); + out.writeShort(connPort); + boolean isIPv4; + if (InetAddressUtils.isIPv4Address(connHostName)) { + isIPv4 = true; + out.write(InetAddress.getByName(connHostName).getAddress()); + } else if (InetAddressUtils.isIPv6Address(connHostName)) { + throw new SOCKSException("IPv6 not supported in SOCKS 4"); + } else { + isIPv4 = false; + out.writeInt(1); // 0.0.0.1 + } + out.writeByte(0); // empty username + if (!isIPv4) { + byte[] d = connHostName.getBytes("ISO-8859-1"); + out.write(d); + out.writeByte(0); + } + out.flush(); + + // read init reply + in = new DataInputStream(pin); + in.readByte(); // dummy + int reply = in.readByte(); + if (reply != Reply.SUCCEEDED) + throw new SOCKSException("Proxy rejected request, response = " + reply); + // throw away the address in the response + // todo pass the response through? + in.readShort(); // port + in.readInt(); // IP + } catch (IOException e) { + if (in != null) try { in.close(); } catch (IOException ioe) {} + if (out != null) try { out.close(); } catch (IOException ioe) {} + throw e; + } + } +} diff --git a/core/java/src/net/i2p/socks/SOCKS4Constants.java b/core/java/src/net/i2p/socks/SOCKS4Constants.java new file mode 100644 index 000000000..5378961a5 --- /dev/null +++ b/core/java/src/net/i2p/socks/SOCKS4Constants.java @@ -0,0 +1,30 @@ +/* I2PSOCKSTunnel is released under the terms of the GNU GPL, + * with an additional exception. For further details, see the + * licensing terms in I2PTunnel.java. + * + * Copyright (c) 2004 by human + */ +package net.i2p.socks; + +/** + * @since 0.9.33 Moved out of net.i2p.i2ptunnel.socks.SOCKS4aServer + */ +public class SOCKS4Constants { + + private SOCKS4Constants() {} + + public static final int SOCKS_VERSION_4 = 0x04; + + /* + * Some namespaces to enclose SOCKS protocol codes + */ + public static class Command { + public static final int CONNECT = 0x01; + public static final int BIND = 0x02; + } + + public static class Reply { + public static final int SUCCEEDED = 0x5a; + public static final int CONNECTION_REFUSED = 0x5b; + } +} diff --git a/core/java/src/net/i2p/socks/SOCKS5Client.java b/core/java/src/net/i2p/socks/SOCKS5Client.java new file mode 100644 index 000000000..594afd29f --- /dev/null +++ b/core/java/src/net/i2p/socks/SOCKS5Client.java @@ -0,0 +1,207 @@ +/* I2PSOCKSTunnel is released under the terms of the GNU GPL, + * with an additional exception. For further details, see the + * licensing terms in I2PTunnel.java. + * + * Copyright (c) 2004 by human + */ +package net.i2p.socks; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; + +import org.apache.http.conn.util.InetAddressUtils; + +import static net.i2p.socks.SOCKS5Constants.*; + +/** + * A simple SOCKS 5 client. + * Note: Caller is advised to setSoTimeout on the socket. Not done here. + * + * @since 0.9.33 adapted from net.i2p.i2ptunnel.socks.SOCKS5Server + */ +public class SOCKS5Client { + + private SOCKS5Client() {} + + /** + * Act as a SOCKS 5 client to connect to a proxy + * + * Will throw and close sock on all errors. + * Caller must close sock on success. + * + * @param sock socket to the proxy + * @param connHostName hostname for the proxy to connect to + * @param connPort port for the proxy to connect to + */ + public static void connect(Socket sock, String connHostName, int connPort) throws IOException { + connect(sock, connHostName, connPort, null, null); + } + + /** + * Act as a SOCKS 5 client to connect to a proxy + * + * Will throw and close sock on all errors. + * Caller must close sock on success. + * + * @param sock socket to the proxy + * @param connHostName hostname for the proxy to connect to + * @param connPort port for the proxy to connect to + * @param configUser username for proxy authentication or null + * @param configPW password for proxy authentication or null + */ + public static void connect(Socket sock, String connHostName, int connPort, String configUser, String configPW) throws IOException { + InputStream in = null; + OutputStream out = null; + try { + in = sock.getInputStream(); + out = sock.getOutputStream(); + connect(in, out, connHostName, connPort, configUser, configPW); + } catch (IOException e) { + try { sock.close(); } catch (IOException ioe) {} + if (in != null) try { in.close(); } catch (IOException ioe) {} + if (out != null) try { out.close(); } catch (IOException ioe) {} + throw e; + } + } + + /** + * Act as a SOCKS 5 client to connect to a proxy + * + * Will throw and close pin and pout on all errors. + * Caller must close pin and pout on success. + * + * @param pin input stream from the proxy + * @param pout output stream to the proxy + * @param connHostName hostname for the proxy to connect to + * @param connPort port for the proxy to connect to + */ + public static void connect(InputStream pin, OutputStream pout, String connHostName, int connPort) throws IOException { + connect(pin, pout, connHostName, connPort, null, null); + } + + /** + * Act as a SOCKS 5 client to connect to a proxy + * + * Will throw and close pin and pout on all errors. + * Caller must close pin and pout on success. + * + * @param pin input stream from the proxy + * @param pout output stream to the proxy + * @param connHostName hostname for the proxy to connect to + * @param connPort port for the proxy to connect to + * @param configUser username for proxy authentication or null + * @param configPW password for proxy authentication or null + */ + public static void connect(InputStream pin, OutputStream pout, String connHostName, int connPort, String configUser, String configPW) throws IOException { + DataOutputStream out = null; + DataInputStream in = null; + try { + out = new DataOutputStream(pout); + boolean authAvail = configUser != null && configPW != null; + + // send the init + out.writeByte(SOCKS_VERSION_5); + if (authAvail) { + out.writeByte(2); + out.writeByte(Method.NO_AUTH_REQUIRED); + out.writeByte(Method.USERNAME_PASSWORD); + } else { + out.writeByte(1); + out.writeByte(Method.NO_AUTH_REQUIRED); + } + out.flush(); + + // read init reply + in = new DataInputStream(pin); + // is this right or should we not try to do 5-to-4 conversion? + int hisVersion = in.readByte(); + if (hisVersion != SOCKS_VERSION_5 /* && addrtype == AddressType.DOMAINNAME */ ) + throw new SOCKSException("SOCKS proxy is not Version 5"); + //else if (hisVersion != 4) + // throw new SOCKSException("Unsupported SOCKS Proxy Version"); + + int method = in.readByte(); + if (method == Method.NO_AUTH_REQUIRED) { + // good + } else if (method == Method.USERNAME_PASSWORD) { + if (authAvail) { + // send the auth + out.writeByte(AUTH_VERSION); + byte[] user = configUser.getBytes("UTF-8"); + byte[] pw = configPW.getBytes("UTF-8"); + out.writeByte(user.length); + out.write(user); + out.writeByte(pw.length); + out.write(pw); + out.flush(); + // read the auth reply + if (in.readByte() != AUTH_VERSION) + throw new SOCKSException("Bad auth version from proxy"); + if (in.readByte() != AUTH_SUCCESS) + throw new SOCKSException("Proxy authorization failure"); + } else { + throw new SOCKSException("Proxy requires authorization, please configure username/password"); + } + } else { + throw new SOCKSException("Proxy authorization failure"); + } + + int addressType; + if (InetAddressUtils.isIPv4Address(connHostName)) + addressType = AddressType.IPV4; + else if (InetAddressUtils.isIPv6Address(connHostName)) + addressType = AddressType.IPV6; + else + addressType = AddressType.DOMAINNAME; + + // send the connect command + out.writeByte(SOCKS_VERSION_5); + out.writeByte(Command.CONNECT); + out.writeByte(0); // reserved + out.writeByte(addressType); + if (addressType == AddressType.IPV4 || addressType == AddressType.IPV6) { + out.write(InetAddress.getByName(connHostName).getAddress()); + } else { + byte[] d = connHostName.getBytes("ISO-8859-1"); + out.writeByte(d.length); + out.write(d); + } + out.writeShort(connPort); + out.flush(); + + // read the connect reply + hisVersion = in.readByte(); + if (hisVersion != SOCKS_VERSION_5) + throw new SOCKSException("Proxy response is not Version 5"); + int reply = in.readByte(); + in.readByte(); // reserved + int type = in.readByte(); + int count = 0; + if (type == AddressType.IPV4) { + count = 4; + } else if (type == AddressType.DOMAINNAME) { + count = in.readUnsignedByte(); + } else if (type == AddressType.IPV6) { + count = 16; + } else { + throw new SOCKSException("Unsupported address type in proxy response"); + } + byte[] addr = new byte[count]; + in.readFully(addr); // address + in.readUnsignedShort(); // port + if (reply != Reply.SUCCEEDED) + throw new SOCKSException("Proxy rejected request, response = " + reply); + // throw away the address in the response + // todo pass the response through? + } catch (IOException e) { + if (in != null) try { in.close(); } catch (IOException ioe) {} + if (out != null) try { out.close(); } catch (IOException ioe) {} + throw e; + } + } +} diff --git a/core/java/src/net/i2p/socks/SOCKS5Constants.java b/core/java/src/net/i2p/socks/SOCKS5Constants.java new file mode 100644 index 000000000..76bfc3d21 --- /dev/null +++ b/core/java/src/net/i2p/socks/SOCKS5Constants.java @@ -0,0 +1,54 @@ +/* I2PSOCKSTunnel is released under the terms of the GNU GPL, + * with an additional exception. For further details, see the + * licensing terms in I2PTunnel.java. + * + * Copyright (c) 2004 by human + */ +package net.i2p.socks; + +/** + * @since 0.9.33 Moved out of net.i2p.i2ptunnel.socks.SOCKS5Server + */ +public class SOCKS5Constants { + + private SOCKS5Constants() {} + + public static final int SOCKS_VERSION_5 = 0x05; + + /* + * Some namespaces to enclose SOCKS protocol codes + */ + public static class Method { + public static final int NO_AUTH_REQUIRED = 0x00; + public static final int USERNAME_PASSWORD = 0x02; + public static final int NO_ACCEPTABLE_METHODS = 0xff; + } + + public static class AddressType { + public static final int IPV4 = 0x01; + public static final int DOMAINNAME = 0x03; + public static final int IPV6 = 0x04; + } + + public static class Command { + public static final int CONNECT = 0x01; + public static final int BIND = 0x02; + public static final int UDP_ASSOCIATE = 0x03; + } + + public static class Reply { + public static final int SUCCEEDED = 0x00; + public static final int GENERAL_SOCKS_SERVER_FAILURE = 0x01; + public static final int CONNECTION_NOT_ALLOWED_BY_RULESET = 0x02; + public static final int NETWORK_UNREACHABLE = 0x03; + public static final int HOST_UNREACHABLE = 0x04; + public static final int CONNECTION_REFUSED = 0x05; + public static final int TTL_EXPIRED = 0x06; + public static final int COMMAND_NOT_SUPPORTED = 0x07; + public static final int ADDRESS_TYPE_NOT_SUPPORTED = 0x08; + } + + public static final int AUTH_VERSION = 1; + public static final int AUTH_SUCCESS = 0; + public static final int AUTH_FAILURE = 1; +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSException.java b/core/java/src/net/i2p/socks/SOCKSException.java similarity index 72% rename from apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSException.java rename to core/java/src/net/i2p/socks/SOCKSException.java index 3b663dd25..008f87ebc 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSException.java +++ b/core/java/src/net/i2p/socks/SOCKSException.java @@ -4,14 +4,17 @@ * * Copyright (c) 2004 by human */ -package net.i2p.i2ptunnel.socks; +package net.i2p.socks; + +import java.io.IOException; /** * Exception thrown by socket methods * * @author human + * @since 0.9.33 moved from net.i2p.i2ptunnel.socks, and changed to extend IOException */ -public class SOCKSException extends Exception { +public class SOCKSException extends IOException { public SOCKSException() { super(); diff --git a/core/java/src/net/i2p/socks/package.html b/core/java/src/net/i2p/socks/package.html new file mode 100644 index 000000000..314bf9238 --- /dev/null +++ b/core/java/src/net/i2p/socks/package.html @@ -0,0 +1,8 @@ + + +

+Constants and clients for SOCKS. +Pulled out of net.i2p.i2ptunnel.socks for use by SSLEepGet as of 0.9.33. +

+ +