From 86d5ebc27babbb4b9b3130d8a59e662af11522c8 Mon Sep 17 00:00:00 2001 From: Nathan Freitas Date: Sun, 25 Oct 2009 04:59:39 +0000 Subject: [PATCH] added asocks subproject to android svn tree svn:r20837 --- asocks/BUILD | 3 + asocks/build.xml | 22 + asocks/src/net/sourceforge/jsocks/SOCKS.java | 257 ++++++++ .../src/net/sourceforge/jsocks/SocksEcho.gif | Bin 0 -> 926 bytes .../src/net/sourceforge/jsocks/SocksEcho.java | 386 ++++++++++++ .../src/net/sourceforge/jsocks/SocksEcho.lnk | Bin 0 -> 335 bytes .../jsocks/SocksServerException.java | 19 + asocks/src/net/sourceforge/jsocks/index.html | 15 + .../jsocks/socks/Authentication.java | 34 ++ .../jsocks/socks/AuthenticationException.java | 92 +++ .../jsocks/socks/AuthenticationNone.java | 17 + .../sourceforge/jsocks/socks/InetRange.java | 442 ++++++++++++++ .../net/sourceforge/jsocks/socks/Proxy.java | 491 +++++++++++++++ .../jsocks/socks/ProxyMessage.java | 119 ++++ .../sourceforge/jsocks/socks/ProxyServer.java | 569 ++++++++++++++++++ .../jsocks/socks/Socks4Message.java | 161 +++++ .../sourceforge/jsocks/socks/Socks4Proxy.java | 123 ++++ .../jsocks/socks/Socks5DatagramSocket.java | 492 +++++++++++++++ .../jsocks/socks/Socks5Message.java | 292 +++++++++ .../sourceforge/jsocks/socks/Socks5Proxy.java | 253 ++++++++ .../jsocks/socks/SocksException.java | 78 +++ .../jsocks/socks/SocksServerSocket.java | 213 +++++++ .../sourceforge/jsocks/socks/SocksSocket.java | 337 +++++++++++ .../jsocks/socks/UDPEncapsulation.java | 30 + .../jsocks/socks/UDPRelayServer.java | 217 +++++++ .../socks/UserPasswordAuthentication.java | 75 +++ .../jsocks/socks/server/Ident.java | 161 +++++ .../socks/server/IdentAuthenticator.java | 153 +++++ .../socks/server/ServerAuthenticator.java | 120 ++++ .../socks/server/ServerAuthenticatorNone.java | 172 ++++++ .../server/UserPasswordAuthenticator.java | 73 +++ .../jsocks/socks/server/UserValidation.java | 21 + .../src/net/sourceforge/jsocks/test/Echo.java | 125 ++++ .../sourceforge/jsocks/test/SocksTest.java | 129 ++++ .../sourceforge/jsocks/test/SocksUDPEcho.java | 103 ++++ .../sourceforge/jsocks/test/TestClient.java | 241 ++++++++ .../sourceforge/jsocks/test/TestServer.java | 83 +++ .../sourceforge/jsocks/test/TestService.java | 275 +++++++++ .../net/sourceforge/jsocks/test/UDPEcho.java | 151 +++++ .../net/sourceforge/jsocks/test/UPSOCKS.java | 41 ++ 40 files changed, 6585 insertions(+) create mode 100644 asocks/BUILD create mode 100644 asocks/build.xml create mode 100644 asocks/src/net/sourceforge/jsocks/SOCKS.java create mode 100644 asocks/src/net/sourceforge/jsocks/SocksEcho.gif create mode 100644 asocks/src/net/sourceforge/jsocks/SocksEcho.java create mode 100644 asocks/src/net/sourceforge/jsocks/SocksEcho.lnk create mode 100644 asocks/src/net/sourceforge/jsocks/SocksServerException.java create mode 100644 asocks/src/net/sourceforge/jsocks/index.html create mode 100644 asocks/src/net/sourceforge/jsocks/socks/Authentication.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/AuthenticationException.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/AuthenticationNone.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/InetRange.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/Proxy.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/ProxyMessage.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/ProxyServer.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/Socks4Message.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/Socks4Proxy.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/Socks5DatagramSocket.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/Socks5Message.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/Socks5Proxy.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/SocksException.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/SocksServerSocket.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/SocksSocket.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/UDPEncapsulation.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/UDPRelayServer.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/UserPasswordAuthentication.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/server/Ident.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/server/IdentAuthenticator.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/server/ServerAuthenticator.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/server/ServerAuthenticatorNone.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/server/UserPasswordAuthenticator.java create mode 100644 asocks/src/net/sourceforge/jsocks/socks/server/UserValidation.java create mode 100644 asocks/src/net/sourceforge/jsocks/test/Echo.java create mode 100644 asocks/src/net/sourceforge/jsocks/test/SocksTest.java create mode 100644 asocks/src/net/sourceforge/jsocks/test/SocksUDPEcho.java create mode 100644 asocks/src/net/sourceforge/jsocks/test/TestClient.java create mode 100644 asocks/src/net/sourceforge/jsocks/test/TestServer.java create mode 100644 asocks/src/net/sourceforge/jsocks/test/TestService.java create mode 100644 asocks/src/net/sourceforge/jsocks/test/UDPEcho.java create mode 100644 asocks/src/net/sourceforge/jsocks/test/UPSOCKS.java diff --git a/asocks/BUILD b/asocks/BUILD new file mode 100644 index 00000000..1f6f62ff --- /dev/null +++ b/asocks/BUILD @@ -0,0 +1,3 @@ +ant compile +ant jar +cp bin/jar/asocks.jar ../Orbot/libs diff --git a/asocks/build.xml b/asocks/build.xml new file mode 100644 index 00000000..c173da69 --- /dev/null +++ b/asocks/build.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/asocks/src/net/sourceforge/jsocks/SOCKS.java b/asocks/src/net/sourceforge/jsocks/SOCKS.java new file mode 100644 index 00000000..d5eec87d --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/SOCKS.java @@ -0,0 +1,257 @@ +package net.sourceforge.jsocks; + +import java.util.*; +import java.io.*; +import java.net.InetAddress; +import java.net.UnknownHostException; + +import net.*; +import net.sourceforge.jsocks.socks.*; +import net.sourceforge.jsocks.socks.server.*; + +public class SOCKS{ + + static public void usage(){ + System.out.println( + "Usage: java SOCKS [inifile1 inifile2 ...]\n"+ + "If none inifile is given, uses socks.properties.\n" + ); + } + + static public void main(String[] args){ + + String[] file_names; + int port=1080; + String logFile = null; + String host = null; + + IdentAuthenticator auth = new IdentAuthenticator(); + OutputStream log = null; + InetAddress localIP = null; + + if(args.length == 0){ + file_names = new String[1]; + file_names[0] = "socks.properties"; + }else{ + file_names = args; + } + + + inform("Loading properties"); + for(int i=0;i=0){ + ProxyServer.setIddleTimeout(val); + inform("Setting iddle timeout to "+val+" ms."); + } + val = readInt(props,"acceptTimeout"); + if(val>=0){ + ProxyServer.setAcceptTimeout(val); + inform("Setting accept timeout to "+val+" ms."); + } + val = readInt(props,"udpTimeout"); + if(val>=0){ + ProxyServer.setUDPTimeout(val); + inform("Setting udp timeout to "+val+" ms."); + } + + val = readInt(props,"datagramSize"); + if(val>=0){ + ProxyServer.setDatagramSize(val); + inform("Setting datagram size to "+val+" bytes."); + } + + proxyInit(props); + + } + + /** + Initialises proxy, if any specified. + */ + static void proxyInit(Properties props){ + String proxy_list; + Proxy proxy = null; + StringTokenizer st; + + proxy_list = (String) props.get("proxy"); + if(proxy_list == null) return; + + st = new StringTokenizer(proxy_list,";"); + while(st.hasMoreTokens()){ + String proxy_entry = st.nextToken(); + + Proxy p = Proxy.parseProxy(proxy_entry); + + if(p == null) + exit("Can't parse proxy entry:"+proxy_entry); + + + inform("Adding Proxy:"+p); + + if(proxy != null) + p.setChainProxy(proxy); + + proxy = p; + + } + if(proxy == null) return; //Empty list + + String direct_hosts = (String) props.get("directHosts"); + if(direct_hosts!=null){ + InetRange ir = parseInetRange(direct_hosts); + inform("Setting direct hosts:"+ir); + proxy.setDirect(ir); + } + + + ProxyServer.setProxy(proxy); + } + /** + Inits range from the string of semicolon separated ranges. + */ + static InetRange parseInetRange(String source){ + InetRange irange = new InetRange(); + + StringTokenizer st = new StringTokenizer(source,";"); + while(st.hasMoreTokens()) + irange.add(st.nextToken()); + + return irange; + } + + /** + Integer representaion of the property named name, or -1 if one + is not found. + */ + static int readInt(Properties props,String name){ + int result = -1; + String val = (String) props.get(name); + if(val==null) return -1; + StringTokenizer st = new StringTokenizer(val); + if(!st.hasMoreElements()) return -1; + try{ + result = Integer.parseInt(st.nextToken()); + }catch(NumberFormatException nfe){ + inform("Bad value for "+name+":"+val); + } + return result; + } + +//Display functions +/////////////////// + + public static void inform(String s){ + System.out.println(s); + } + + static void exit(String msg){ + System.err.println("Error:"+msg); + System.err.println("Aborting operation"); + System.exit(0); + } +} diff --git a/asocks/src/net/sourceforge/jsocks/SocksEcho.gif b/asocks/src/net/sourceforge/jsocks/SocksEcho.gif new file mode 100644 index 0000000000000000000000000000000000000000..701d39a6fe03e99d9d361076d51aeb6d33a9e972 GIT binary patch literal 926 zcmZ?wbhEHb6krfw_|Cw<0s<{8EWcPsK#ACq&hgk;Vt9&XpHMsT3 yin2xQx}tt?f{Op7Cnq*PaPAbI*k 0){ + print(new String(buf,0,bytes_read)); + } + + } + private void startUDP() throws IOException{ + udp_sock = new Socks5DatagramSocket(proxy,0,null); + println("UDP started on "+udp_sock.getLocalAddress()+":"+ + udp_sock.getLocalPort()); + status("UDP:"+udp_sock.getLocalAddress().getHostAddress()+":" + +udp_sock.getLocalPort()); + } + + private void doUDPPipe() throws IOException{ + DatagramPacket dp = new DatagramPacket(new byte[MAX_DATAGRAM_SIZE], + MAX_DATAGRAM_SIZE); + while(true){ + udp_sock.receive(dp); + print("UDP\n"+ + "From:"+dp.getAddress()+":"+dp.getPort()+"\n"+ + "\n"+ + //Java 1.2 + //new String(dp.getData(),dp.getOffset(),dp.getLength())+"\n" + //Java 1.1 + new String(dp.getData(),0,dp.getLength())+"\n" + ); + dp.setLength(MAX_DATAGRAM_SIZE); + } + } + + private void sendUDP(String message,String host,int port){ + if(!udp_sock.isProxyAlive(100)){ + status("Proxy closed connection"); + abort_connection(); + return; + } + + try{ + byte[] data = message.getBytes(); + DatagramPacket dp = new DatagramPacket(data,data.length,null,port); + udp_sock.send(dp,host); + }catch(UnknownHostException uhe){ + status("Host "+host+" has no DNS entry."); + }catch(IOException ioe){ + status("IOException:"+ioe); + abort_connection(); + } + + } + + private void send(String s){ + try{ + out.write(s.getBytes()); + }catch(IOException io_ex){ + println("IOException:"+io_ex); + abort_connection(); + } + } + + + + + + +// Main +//////////////////////////////////// + public static void main(String[] args){ + SocksEcho socksecho = new SocksEcho(); + + } + }//end class diff --git a/asocks/src/net/sourceforge/jsocks/SocksEcho.lnk b/asocks/src/net/sourceforge/jsocks/SocksEcho.lnk new file mode 100644 index 0000000000000000000000000000000000000000..b79fefa0bf2e4427c880364a392458aeb36803cb GIT binary patch literal 335 zcmeZaU|?VrVFHp23S|!0ZO`U4U2vh?#*HsFOhzh(W4iVtm2^{QZ3lW1Rhcef|9y0 + + + + + + Moma + + + + + < \ No newline at end of file diff --git a/asocks/src/net/sourceforge/jsocks/socks/Authentication.java b/asocks/src/net/sourceforge/jsocks/socks/Authentication.java new file mode 100644 index 00000000..fc581e7b --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/Authentication.java @@ -0,0 +1,34 @@ +package net.sourceforge.jsocks.socks; + +/** + The Authentication interface provides for performing method specific + authentication for SOCKS5 connections. +*/ +public interface Authentication{ + /** + This method is called when SOCKS5 server have selected a particular + authentication method, for whch an implementaion have been registered. + +

+ This method should return an array {inputstream,outputstream + [,UDPEncapsulation]}. The reason for that is that SOCKS5 protocol + allows to have method specific encapsulation of data on the socket for + purposes of integrity or security. And this encapsulation should be + performed by those streams returned from the method. It is also possible + to encapsulate datagrams. If authentication method supports such + encapsulation an instance of the UDPEncapsulation interface should be + returned as third element of the array, otherwise either null should be + returned as third element, or array should contain only 2 elements. + + @param methodId Authentication method selected by the server. + @param proxySocket Socket used to conect to the proxy. + @return Two or three element array containing + Input/Output streams which should be used on this connection. + Third argument is optional and should contain an instance + of UDPEncapsulation. It should be provided if the authentication + method used requires any encapsulation to be done on the + datagrams. + */ + Object[] doSocksAuthentication(int methodId,java.net.Socket proxySocket) + throws java.io.IOException; +} diff --git a/asocks/src/net/sourceforge/jsocks/socks/AuthenticationException.java b/asocks/src/net/sourceforge/jsocks/socks/AuthenticationException.java new file mode 100644 index 00000000..999b5475 --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/AuthenticationException.java @@ -0,0 +1,92 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package net.sourceforge.jsocks.socks; + +/** + * Exception thrown by various socks classes to indicate errors + * with protocol or unsuccessful server responses. + * + * @author rayc@google.com (Ray Colline) + */ +public class AuthenticationException extends Throwable { + + private AuthErrorType errorType; + private String errorString; + + /** + * Create an AuthenticationException with the specified type + * + * @param errorType an enum denoting what kind of auth error. + */ + public AuthenticationException(AuthErrorType errorType) { + this.errorType = errorType; + this.errorString = errorType.toString(); + } + + /** + * Create an AuthenticationException with both the specified type and + * a free-form message + * + * @param errorType an enum denoting what kind of auth error. + * @param errorString a specific string detailing the error. + */ + public AuthenticationException(AuthErrorType errorType, + String errorString) { + this.errorType = errorType; + this.errorString = errorString; + } + + /** + * Get the error type associated with this exception. + * + * @return errorType the type associated with this exception. + */ + public AuthErrorType getErrorType() { + return errorType; + } + + /** + * Get human readable representation of this exception. + * + * @return String representation of this exception. + */ + @Override + public String toString() { + return errorString; + } + + /** + * Returns the message associated with this exception + * + * @return String the error string. + */ + @Override + public String getMessage() { + return errorString; + } + + /** + * List of Authentication error types. + * + * @author rayc@google.com (Ray Colline) + */ + public enum AuthErrorType { + MALFORMED_REQUEST, + PASSWORD_TOO_LONG; + } + +} diff --git a/asocks/src/net/sourceforge/jsocks/socks/AuthenticationNone.java b/asocks/src/net/sourceforge/jsocks/socks/AuthenticationNone.java new file mode 100644 index 00000000..83bc582c --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/AuthenticationNone.java @@ -0,0 +1,17 @@ +package net.sourceforge.jsocks.socks; + +/** + SOCKS5 none authentication. Dummy class does almost nothing. +*/ +public class AuthenticationNone implements Authentication{ + + public Object[] doSocksAuthentication(int methodId, + java.net.Socket proxySocket) + throws java.io.IOException{ + + if(methodId!=0) return null; + + return new Object[] { proxySocket.getInputStream(), + proxySocket.getOutputStream()}; + } +} diff --git a/asocks/src/net/sourceforge/jsocks/socks/InetRange.java b/asocks/src/net/sourceforge/jsocks/socks/InetRange.java new file mode 100644 index 00000000..4e27255e --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/InetRange.java @@ -0,0 +1,442 @@ +package net.sourceforge.jsocks.socks; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.*; + +/** + * Class InetRange provides the means of defining the range of inetaddresses. + * It's used by Proxy class to store and look up addresses of machines, that + * should be contacted directly rather then through the proxy. + *

+ * InetRange provides several methods to add either standalone addresses, or + * ranges (e.g. 100.200.300.0:100.200.300.255, which covers all addresses + * on on someones local network). It also provides methods for checking wether + * given address is in this range. Any number of ranges and standalone + * addresses can be added to the range. + */ +public class InetRange implements Cloneable{ + + Hashtable host_names; + Vector all; + Vector end_names; + + boolean useSeparateThread = true; + + /** + * Creates the empty range. + */ + public InetRange(){ + all = new Vector(); + host_names = new Hashtable(); + end_names = new Vector(); + } + + /** + * Adds another host or range to this range. + The String can be one of those: +

    +
  • Host name. eg.(Athena.myhost.com or 45.54.56.65) + +
  • Range in the form .myhost.net.au
    + In which case anything that ends with .myhost.net.au will + be considered in the range. + +
  • Range in the form ddd.ddd.ddd.
    + This will be treated as range ddd.ddd.ddd.0 to ddd.ddd.ddd.255. + It is not necessary to specify 3 first bytes you can use just + one or two. For example 130. will cover address between 130.0.0.0 + and 13.255.255.255. + +
  • Range in the form host_from[: \t\n\r\f]host_to.
    + That is two hostnames or ips separated by either whitespace + or colon. +
+ */ + public synchronized boolean add(String s){ + if(s == null) return false; + + s = s.trim(); + if(s.length() == 0) return false; + + Object[] entry; + + if(s.charAt(s.length()-1) == '.'){ + //thing like: 111.222.33. + //it is being treated as range 111.222.33.000 - 111.222.33.255 + + int[] addr = ip2intarray(s); + long from,to; + from = to = 0; + + if(addr == null) return false; + for(int i = 0; i< 4;++i){ + if(addr[i]>=0) + from += (((long)addr[i]) << 8*(3-i)); + else{ + to = from; + while(i<4) + to += 255l << 8*(3-i++); + break; + } + } + entry = new Object[] {s,null,new Long(from),new Long(to)}; + all.addElement(entry); + + }else if(s.charAt(0) == '.'){ + //Thing like: .myhost.com + + end_names.addElement(s); + all.addElement(new Object[]{s,null,null,null}); + }else{ + StringTokenizer tokens = new StringTokenizer(s," \t\r\n\f:"); + if(tokens.countTokens() > 1){ + entry = new Object[] {s,null,null,null}; + resolve(entry,tokens.nextToken(),tokens.nextToken()); + all.addElement(entry); + }else{ + entry = new Object[] {s,null,null,null}; + all.addElement(entry); + host_names.put(s,entry); + resolve(entry); + } + + } + + return true; + } + + /** + * Adds another ip for this range. + @param ip IP os the host which should be added to this range. + */ + public synchronized void add(InetAddress ip){ + long from, to; + from = to = ip2long(ip); + all.addElement(new Object[]{ip.getHostName(),ip,new Long(from), + new Long(to)}); + } + + /** + * Adds another range of ips for this range.Any host with ip address + greater than or equal to the address of from and smaller than or equal + to the address of to will be included in the range. + @param from IP from where range starts(including). + @param to IP where range ends(including). + */ + public synchronized void add(InetAddress from,InetAddress to){ + all.addElement(new Object[]{from.getHostAddress()+":"+to.getHostAddress() + ,null,new Long(ip2long(from)), + new Long(ip2long(to))}); + } + + /** + * Checks wether the givan host is in the range. Attempts to resolve + host name if required. + @param host Host name to check. + @return true If host is in the range, false otherwise. + * @see InetRange#contains(String,boolean) + */ + public synchronized boolean contains(String host){ + return contains(host,true); + } + + /** + * Checks wether the given host is in the range. + *

+ * Algorithm:
+ *

    + *
  1. Look up if the hostname is in the range (in the Hashtable). + *
  2. Check if it ends with one of the speciefied endings. + *
  3. Check if it is ip(eg.130.220.35.98). If it is check if it is + * in the range. + *
  4. If attemptResolve is true, host is name, rather than ip, and + * all previous attempts failed, try to resolve the hostname, and + * check wether the ip associated with the host is in the range.It + * also repeats all previos steps with the hostname obtained from + * InetAddress, but the name is not allways the full name,it is + * quite likely to be the same. Well it was on my machine. + *
+ @param host Host name to check. + @param attemptResolve Wether to lookup ip address which corresponds + to the host,if required. + @return true If host is in the range, false otherwise. + */ + public synchronized boolean contains(String host,boolean attemptResolve){ + if(all.size() ==0) return false; //Empty range + + host = host.trim(); + if(host.length() == 0) return false; + + if(checkHost(host)) return true; + if(checkHostEnding(host)) return true; + + long l = host2long(host); + if(l >=0) return contains(l); + + if(!attemptResolve) return false; + + try{ + InetAddress ip = InetAddress.getByName(host); + return contains(ip); + }catch(UnknownHostException uhe){ + + } + + return false; + } + + /** + * Checks wether the given ip is in the range. + @param ip Address of the host to check. + @return true If host is in the range, false otherwise. + */ + public synchronized boolean contains(InetAddress ip){ + if(checkHostEnding(ip.getHostName())) return true; + if(checkHost(ip.getHostName())) return true; + return contains(ip2long(ip)); + } + /** + Get all entries in the range as strings.
+ These strings can be used to delete entries from the range + with remove function. + @return Array of entries as strings. + @see InetRange#remove(String) + */ + public synchronized String[] getAll(){ + int size = all.size(); + Object entry[]; + String all_names[] = new String[size]; + + for(int i=0;i + @param s Entry to remove. + @return true if successfull. + */ + public synchronized boolean remove(String s){ + Iterator iterator = all.iterator(); + + while(iterator.hasNext()){ + Object[] entry = (Object[]) iterator.next(); + if(s.equals(entry[0])){ + all.removeElement(entry); + end_names.removeElement(s); + host_names.remove(s); + return true; + } + } + return false; + } + + /** Get string representaion of this Range.*/ + public String toString(){ + String all[] = getAll(); + if(all.length == 0) return ""; + + String s = all[0]; + for(int i=1;i= ip) return true; + + } + return false; + } + + private boolean checkHost(String host){ + return host_names.containsKey(host); + } + private boolean checkHostEnding(String host){ + Iterator iterator = end_names.iterator(); + while(iterator.hasNext()){ + if(host.endsWith((String) iterator.next())) return true; + } + return false; + } + private void resolve(Object[] entry){ + //First check if it's in the form ddd.ddd.ddd.ddd. + long ip = host2long((String) entry[0]); + if(ip >= 0){ + entry[2] = entry[3] = new Long(ip); + }else{ + InetRangeResolver res = new InetRangeResolver(entry); + res.resolve(useSeparateThread); + } + } + private void resolve(Object[] entry,String from,String to){ + long f,t; + if((f=host2long(from))>= 0 && (t=host2long(to)) >= 0){ + entry[2] = new Long(f); + entry[3] = new Long(t); + }else{ + InetRangeResolver res = new InetRangeResolver(entry,from,to); + res.resolve(useSeparateThread); + } + } + + + +//Class methods +/////////////// + + //Converts ipv4 to long value(unsigned int) + /////////////////////////////////////////// + static long ip2long(InetAddress ip){ + long l=0; + byte[] addr = ip.getAddress(); + + if(addr.length ==4){ //IPV4 + for(int i=0;i<4;++i) + l += (((long)addr[i] &0xFF) << 8*(3-i)); + }else{ //IPV6 + return 0; //Have no idea how to deal with those + } + return l; + } + + + long host2long(String host){ + long ip=0; + + //check if it's ddd.ddd.ddd.ddd + if(!Character.isDigit(host.charAt(0))) return -1; + + int[] addr = ip2intarray(host); + if(addr == null) return -1; + + for(int i=0;i=0 ? addr[i] : 0)) << 8*(3-i); + + return ip; + } + + static int[] ip2intarray(String host){ + int[] address = {-1,-1,-1,-1}; + int i=0; + StringTokenizer tokens = new StringTokenizer(host,"."); + if(tokens.countTokens() > 4) return null; + while(tokens.hasMoreTokens()){ + try{ + address[i++] = Integer.parseInt(tokens.nextToken()) & 0xFF; + }catch(NumberFormatException nfe){ + return null; + } + + } + return address; + } + + +/* +//* This was the test main function +//********************************** + + public static void main(String args[])throws UnknownHostException{ + int i; + + InetRange ir = new InetRange(); + + + for(i=0;i + Defaults: port = 1080.
+ If user specified but not password, creates Socks4Proxy, if user + not specified creates Socks5Proxy, if both user and password are + speciefied creates Socks5Proxy with user/password authentication. + @param proxy_entry String in the form host[:port:user:password] + @return Proxy created from the string, null if entry was somehow + invalid(host unknown for example, or empty string) + */ + public static Proxy parseProxy(String proxy_entry){ + + String proxy_host; + int proxy_port = 1080; + String proxy_user = null; + String proxy_password = null; + Proxy proxy; + + java.util.StringTokenizer st = new java.util.StringTokenizer( + proxy_entry,":"); + if(st.countTokens() < 1) return null; + + proxy_host = st.nextToken(); + if(st.hasMoreTokens()) + try{ + proxy_port = Integer.parseInt(st.nextToken().trim()); + }catch(NumberFormatException nfe){} + + if(st.hasMoreTokens()) + proxy_user = st.nextToken(); + + if(st.hasMoreTokens()) + proxy_password = st.nextToken(); + + try{ + if(proxy_user == null) + proxy = new Socks5Proxy(proxy_host,proxy_port); + else if(proxy_password == null) + proxy = new Socks4Proxy(proxy_host,proxy_port,proxy_user); + else{ + proxy = new Socks5Proxy(proxy_host,proxy_port); + UserPasswordAuthentication upa = new UserPasswordAuthentication( + proxy_user, proxy_password); + + ((Socks5Proxy)proxy).setAuthenticationMethod(upa.METHOD_ID,upa); + } + }catch(UnknownHostException uhe){ + return null; + } + + return proxy; + } + + +//Protected Methods +//================= + + protected void startSession()throws SocksException{ + try{ + if(chainProxy == null) + proxySocket = new Socket(proxyIP,proxyPort); + else if(proxyIP != null) + proxySocket = new SocksSocket(chainProxy,proxyIP,proxyPort); + else + proxySocket = new SocksSocket(chainProxy,proxyHost,proxyPort); + + in = proxySocket.getInputStream(); + out = proxySocket.getOutputStream(); + }catch(SocksException se){ + throw se; + }catch(IOException io_ex){ + throw new SocksException(SOCKS_PROXY_IO_ERROR,""+io_ex); + } + } + + protected abstract Proxy copy(); + protected abstract ProxyMessage formMessage(int cmd,InetAddress ip,int port); + protected abstract ProxyMessage formMessage(int cmd,String host,int port) + throws UnknownHostException; + protected abstract ProxyMessage formMessage(InputStream in) + throws SocksException, + IOException; + + + protected ProxyMessage connect(InetAddress ip,int port) + throws SocksException{ + try{ + startSession(); + ProxyMessage request = formMessage(SOCKS_CMD_CONNECT, + ip,port); + return exchange(request); + }catch(SocksException se){ + endSession(); + throw se; + } + } + protected ProxyMessage connect(String host,int port) + throws UnknownHostException,SocksException{ + try{ + startSession(); + ProxyMessage request = formMessage(SOCKS_CMD_CONNECT, + host,port); + return exchange(request); + }catch(SocksException se){ + endSession(); + throw se; + } + } + + protected ProxyMessage bind(InetAddress ip,int port) + throws SocksException{ + try{ + startSession(); + ProxyMessage request = formMessage(SOCKS_CMD_BIND, + ip,port); + return exchange(request); + }catch(SocksException se){ + endSession(); + throw se; + } + } + protected ProxyMessage bind(String host,int port) + throws UnknownHostException,SocksException{ + try{ + startSession(); + ProxyMessage request = formMessage(SOCKS_CMD_BIND, + host,port); + return exchange(request); + }catch(SocksException se){ + endSession(); + throw se; + } + } + + protected ProxyMessage accept() + throws IOException,SocksException{ + ProxyMessage msg; + try{ + msg = formMessage(in); + }catch(InterruptedIOException iioe){ + throw iioe; + }catch(IOException io_ex){ + endSession(); + throw new SocksException(SOCKS_PROXY_IO_ERROR,"While Trying accept:" + +io_ex); + } + return msg; + } + + protected ProxyMessage udpAssociate(InetAddress ip,int port) + throws SocksException{ + try{ + startSession(); + ProxyMessage request = formMessage(SOCKS_CMD_UDP_ASSOCIATE, + ip,port); + if(request != null) + return exchange(request); + }catch(SocksException se){ + endSession(); + throw se; + } + //Only get here if request was null + endSession(); + throw new SocksException(SOCKS_METHOD_NOTSUPPORTED, + "This version of proxy does not support UDP associate, use version 5"); + } + protected ProxyMessage udpAssociate(String host,int port) + throws UnknownHostException,SocksException{ + try{ + startSession(); + ProxyMessage request = formMessage(SOCKS_CMD_UDP_ASSOCIATE, + host,port); + if(request != null) return exchange(request); + }catch(SocksException se){ + endSession(); + throw se; + } + //Only get here if request was null + endSession(); + throw new SocksException(SOCKS_METHOD_NOTSUPPORTED, + "This version of proxy does not support UDP associate, use version 5"); + } + + + protected void endSession(){ + try{ + if(proxySocket!=null) proxySocket.close(); + proxySocket = null; + }catch(IOException io_ex){ + } + } + + /** + *Sends the request to SOCKS server + */ + protected void sendMsg(ProxyMessage msg)throws SocksException, + IOException{ + msg.write(out); + } + + /** + * Reads the reply from the SOCKS server + */ + protected ProxyMessage readMsg()throws SocksException, + IOException{ + return formMessage(in); + } + /** + *Sends the request reads reply and returns it + *throws exception if something wrong with IO + *or the reply code is not zero + */ + protected ProxyMessage exchange(ProxyMessage request) + throws SocksException{ + ProxyMessage reply; + try{ + request.write(out); + reply = formMessage(in); + }catch(SocksException s_ex){ + throw s_ex; + }catch(IOException ioe){ + throw(new SocksException(SOCKS_PROXY_IO_ERROR,""+ioe)); + } + return reply; + } + + +//Private methods +//=============== + + +//Constants + + public static final int SOCKS_SUCCESS =0; + public static final int SOCKS_FAILURE =1; + public static final int SOCKS_NOT_ALLOWED_BY_RULESET =2; + public static final int SOCKS_BADNETWORK =3; + public static final int SOCKS_HOST_UNREACHABLE =4; + public static final int SOCKS_CONNECTION_REFUSED =5; + public static final int SOCKS_TTL_EXPIRE =6; + public static final int SOCKS_CMD_NOT_SUPPORTED =7; + public static final int SOCKS_ADDR_NOT_SUPPORTED =8; + + public static final int SOCKS_NO_PROXY =1<<16; + public static final int SOCKS_PROXY_NO_CONNECT =2<<16; + public static final int SOCKS_PROXY_IO_ERROR =3<<16; + public static final int SOCKS_AUTH_NOT_SUPPORTED =4<<16; + public static final int SOCKS_AUTH_FAILURE =5<<16; + public static final int SOCKS_JUST_ERROR =6<<16; + + public static final int SOCKS_DIRECT_FAILED =7<<16; + public static final int SOCKS_METHOD_NOTSUPPORTED =8<<16; + + + static final int SOCKS_CMD_CONNECT =0x1; + static final int SOCKS_CMD_BIND =0x2; + static final int SOCKS_CMD_UDP_ASSOCIATE =0x3; + +} diff --git a/asocks/src/net/sourceforge/jsocks/socks/ProxyMessage.java b/asocks/src/net/sourceforge/jsocks/socks/ProxyMessage.java new file mode 100644 index 00000000..2f692edf --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/ProxyMessage.java @@ -0,0 +1,119 @@ +package net.sourceforge.jsocks.socks; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.DataInputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + Abstract class which describes SOCKS4/5 response/request. +*/ +public abstract class ProxyMessage{ + /** Host as an IP address */ + public InetAddress ip=null; + /** SOCKS version, or version of the response for SOCKS4*/ + public int version; + /** Port field of the request/response*/ + public int port; + /** Request/response code as an int*/ + public int command; + /** Host as string.*/ + public String host=null; + /** User field for SOCKS4 request messages*/ + public String user=null; + /** Connection ID */ + private String connectionId = "N/A"; + + ProxyMessage(int command,InetAddress ip,int port){ + this.command = command; + this.ip = ip; + this.port = port; + } + + ProxyMessage(){ + } + + + /** + Initialises Message from the stream. Reads server response from + given stream. + @param in Input stream to read response from. + @throws SocksException If server response code is not SOCKS_SUCCESS(0), or + if any error with protocol occurs. + @throws IOException If any error happens with I/O. + */ + public abstract void read(InputStream in) + throws SocksException, + IOException; + + + /** + Initialises Message from the stream. Reads server response or client + request from given stream. + + @param in Input stream to read response from. + @param clinetMode If true read server response, else read client request. + @throws SocksException If server response code is not SOCKS_SUCCESS(0) and + reading in client mode, or if any error with protocol occurs. + @throws IOException If any error happens with I/O. + */ + public abstract void read(InputStream in,boolean client_mode) + throws SocksException, + IOException; + + + /** + Writes the message to the stream. + @param out Output stream to which message should be written. + */ + public abstract void write(OutputStream out)throws SocksException, + IOException; + + /** + Get the Address field of this message as InetAddress object. + @return Host address or null, if one can't be determined. + */ + public InetAddress getInetAddress() throws UnknownHostException{ + return ip; + } + + + /** + Get string representaion of this message. + @return string representation of this message. + */ + public String toString(){ + return + "Proxy Message:\n"+ + "Version:"+ version+"\n"+ + "Command:"+ command+"\n"+ + "IP: "+ ip+"\n"+ + "Port: "+ port+"\n"+ + "User: "+ user+"\n" ; + } + + public String getConnectionId() { + return connectionId; + } + + public void setConnectionId(String connectionId) { + this.connectionId = connectionId; + } + +//Package methods +////////////////// + + static final String bytes2IPV4(byte[] addr,int offset){ + String hostName = ""+(addr[offset] & 0xFF); + for(int i = offset+1;i + In order to use it you will need to implement ServerAuthenticator + interface. There is an implementation of this interface which does + no authentication ServerAuthenticatorNone, but it is very dangerous + to use, as it will give access to your local network to anybody + in the world. One should never use this authentication scheme unless + one have pretty good reason to do so. + There is a couple of other authentication schemes in socks.server package. + @see socks.server.ServerAuthenticator +*/ +public class ProxyServer implements Runnable{ + + ServerAuthenticator auth; + ProxyMessage msg = null; + + Socket sock=null,remote_sock=null; + ServerSocket ss=null; + UDPRelayServer relayServer = null; + InputStream in,remote_in; + OutputStream out,remote_out; + + int mode; + static final int START_MODE = 0; + static final int ACCEPT_MODE = 1; + static final int PIPE_MODE = 2; + static final int ABORT_MODE = 3; + + static final int BUF_SIZE = 8192; + + Thread pipe_thread1,pipe_thread2; + long lastReadTime; + + static int iddleTimeout = 180000; //3 minutes + static int acceptTimeout = 180000; //3 minutes + + + static Proxy proxy; + + private String connectionId; + + +//Public Constructors +///////////////////// + + /** + Creates a proxy server with given Authentication scheme. + @param auth Authentication scheme to be used. + */ + public ProxyServer(ServerAuthenticator auth){ + this.auth = auth; + this.connectionId = newConnectionId(); + } + +//Other constructors +//////////////////// + + ProxyServer(ServerAuthenticator auth,Socket s, String connectionId){ + this.auth = auth; + this.sock = s; + this.connectionId = connectionId; + mode = START_MODE; + } + +//Public methods +///////////////// + + /** + Set proxy. +

+ Allows Proxy chaining so that one Proxy server is connected to another + and so on. If proxy supports SOCKSv4, then only some SOCKSv5 requests + can be handled, UDP would not work, however CONNECT and BIND will be + translated. + + @param p Proxy which should be used to handle user requests. + */ + public static void setProxy(Proxy p){ + proxy =p; + UDPRelayServer.proxy = proxy; + } + + /** + Get proxy. + @return Proxy wich is used to handle user requests. + */ + public static Proxy getProxy(){ + return proxy; + } + + /** + Sets the timeout for connections, how long shoud server wait + for data to arrive before dropping the connection.
+ Zero timeout implies infinity.
+ Default timeout is 3 minutes. + */ + public static void setIddleTimeout(int timeout){ + iddleTimeout = timeout; + } + /** + Sets the timeout for BIND command, how long the server should + wait for the incoming connection.
+ Zero timeout implies infinity.
+ Default timeout is 3 minutes. + */ + public static void setAcceptTimeout(int timeout){ + acceptTimeout = timeout; + } + + /** + Sets the timeout for UDPRelay server.
+ Zero timeout implies infinity.
+ Default timeout is 3 minutes. + */ + public static void setUDPTimeout(int timeout){ + UDPRelayServer.setTimeout(timeout); + } + + /** + Sets the size of the datagrams used in the UDPRelayServer.
+ Default size is 64K, a bit more than maximum possible size of the + datagram. + */ + public static void setDatagramSize(int size){ + UDPRelayServer.setDatagramSize(size); + } + + + /** + Start the Proxy server at given port.
+ This methods blocks. + */ + public void start(int port){ + start(port,5,null); + } + + /** + Create a server with the specified port, listen backlog, and local + IP address to bind to. The localIP argument can be used on a multi-homed + host for a ServerSocket that will only accept connect requests to one of + its addresses. If localIP is null, it will default accepting connections + on any/all local addresses. The port must be between 0 and 65535, + inclusive.
+ This methods blocks. + */ + public void start(int port,int backlog,InetAddress localIP){ + try{ + ss = new ServerSocket(port,backlog,localIP); + while(true){ + Socket s = ss.accept(); + String connectionId = newConnectionId(); + ProxyServer ps = new ProxyServer(auth,s, connectionId); + (new Thread(ps)).start(); + } + }catch(IOException ioe){ + ioe.printStackTrace(); + } + } + + /** + * Creates new unique ID for this connection. + * + * @return a random-enough ID. + */ + private String newConnectionId() { + // return "[" + RandomStringUtils.randomAlphanumeric(4) + "]"; + return "[" + Math.random()*new java.util.Date().getTime() + "]"; + } + + /** + Stop server operation.It would be wise to interrupt thread running the + server afterwards. + */ + public void stop(){ + try{ + if(ss != null) ss.close(); + }catch(IOException ioe){ + } + } + +//Runnable interface +//////////////////// + public void run(){ + switch(mode){ + case START_MODE: + try{ + startSession(); + }catch(IOException ioe){ + handleException(ioe); + ioe.printStackTrace(); + }finally{ + abort(); + if(auth!=null) auth.endSession(); + } + break; + case ACCEPT_MODE: + try{ + doAccept(); + mode = PIPE_MODE; + pipe_thread1.interrupt(); //Tell other thread that connection have + //been accepted. + pipe(remote_in,out); + }catch(IOException ioe){ + //log("Accept exception:"+ioe); + handleException(ioe); + }finally{ + abort(); + } + break; + case PIPE_MODE: + try{ + pipe(remote_in,out); + }catch(IOException ioe){ + }finally{ + abort(); + } + break; + case ABORT_MODE: + break; + default: + } + } + +//Private methods +///////////////// + private void startSession() throws IOException{ + sock.setSoTimeout(iddleTimeout); + + try{ + auth = auth.startSession(sock); + }catch(IOException ioe){ + auth = null; + return; + } + + if(auth == null){ //Authentication failed + return; + } + + in = auth.getInputStream(); + out = auth.getOutputStream(); + + msg = readMsg(in); + // Set the connection ID in the message. + msg.setConnectionId(connectionId); + handleRequest(msg); + } + + private void handleRequest(ProxyMessage msg) + throws IOException{ + if(!auth.checkRequest(msg)) { + ProxyMessage response = new Socks5Message( + Proxy.SOCKS_NOT_ALLOWED_BY_RULESET); + response.write(out); + abort(); + throw new SocksException(Proxy.SOCKS_NOT_ALLOWED_BY_RULESET); + } + + if(msg.ip == null){ + if(msg instanceof Socks5Message){ + msg.ip = InetAddress.getByName(msg.host); + }else + throw new SocksException(Proxy.SOCKS_FAILURE); + } + + switch(msg.command){ + case Proxy.SOCKS_CMD_CONNECT: + onConnect(msg); + break; + case Proxy.SOCKS_CMD_BIND: + onBind(msg); + break; + case Proxy.SOCKS_CMD_UDP_ASSOCIATE: + onUDP(msg); + break; + default: + throw new SocksException(Proxy.SOCKS_CMD_NOT_SUPPORTED); + } + } + + private void handleException(IOException ioe){ + //If we couldn't read the request, return; + if(msg == null) return; + //If have been aborted by other thread + if(mode == ABORT_MODE) return; + //If the request was successfully completed, but exception happened later + if(mode == PIPE_MODE) return; + + int error_code = Proxy.SOCKS_FAILURE; + + if(ioe instanceof SocksException) + error_code = ((SocksException)ioe).errCode; + else if(ioe instanceof NoRouteToHostException) + error_code = Proxy.SOCKS_HOST_UNREACHABLE; + else if(ioe instanceof ConnectException) + error_code = Proxy.SOCKS_CONNECTION_REFUSED; + else if(ioe instanceof InterruptedIOException) + error_code = Proxy.SOCKS_TTL_EXPIRE; + + if(error_code > Proxy.SOCKS_ADDR_NOT_SUPPORTED || error_code < 0){ + error_code = Proxy.SOCKS_FAILURE; + } + + sendErrorMessage(error_code); + } + + private void onConnect(ProxyMessage msg) throws IOException{ + Socket s; + ProxyMessage response = null; + + if(proxy == null) + s = new Socket(msg.ip,msg.port); + else + s = new SocksSocket(proxy,msg.ip,msg.port); + + + if(msg instanceof Socks5Message){ + response = new Socks5Message(Proxy.SOCKS_SUCCESS, + s.getLocalAddress(), + s.getLocalPort()); + }else{ + response = new Socks4Message(Socks4Message.REPLY_OK, + s.getLocalAddress(),s.getLocalPort()); + + } + response.write(out); + startPipe(s); + } + + private void onBind(ProxyMessage msg) throws IOException{ + ProxyMessage response = null; + + if(proxy == null) + ss = new ServerSocket(0); + else + ss = new SocksServerSocket(proxy, msg.ip, msg.port); + + ss.setSoTimeout(acceptTimeout); + + + if(msg.version == 5) + response = new Socks5Message(Proxy.SOCKS_SUCCESS,ss.getInetAddress(), + ss.getLocalPort()); + else + response = new Socks4Message(Socks4Message.REPLY_OK, + ss.getInetAddress(), + ss.getLocalPort()); + response.write(out); + + mode = ACCEPT_MODE; + + pipe_thread1 = Thread.currentThread(); + pipe_thread2 = new Thread(this); + pipe_thread2.start(); + + //Make timeout infinit. + sock.setSoTimeout(0); + int eof=0; + + try{ + while((eof=in.read())>=0){ + if(mode != ACCEPT_MODE){ + if(mode != PIPE_MODE) return;//Accept failed + + remote_out.write(eof); + break; + } + } + }catch(EOFException eofe){ + //System.out.println("EOF exception"); + return;//Connection closed while we were trying to accept. + }catch(InterruptedIOException iioe){ + //Accept thread interrupted us. + //System.out.println("Interrupted"); + if(mode != PIPE_MODE) + return;//If accept thread was not successfull return. + }finally{ + //System.out.println("Finnaly!"); + } + + if(eof < 0)//Connection closed while we were trying to accept; + return; + + //Do not restore timeout, instead timeout is set on the + //remote socket. It does not make any difference. + + pipe(in,remote_out); + } + + private void onUDP(ProxyMessage msg) throws IOException{ + if(msg.ip.getHostAddress().equals("0.0.0.0")) + msg.ip = sock.getInetAddress(); + relayServer = new UDPRelayServer(msg.ip,msg.port, + Thread.currentThread(),sock,auth); + + ProxyMessage response; + + response = new Socks5Message(Proxy.SOCKS_SUCCESS, + relayServer.relayIP,relayServer.relayPort); + + response.write(out); + + relayServer.start(); + + //Make timeout infinit. + sock.setSoTimeout(0); + try{ + while(in.read()>=0) /*do nothing*/; + }catch(EOFException eofe){ + } + } + +//Private methods +////////////////// + + private void doAccept() throws IOException{ + Socket s; + long startTime = System.currentTimeMillis(); + + while(true){ + s = ss.accept(); + if(s.getInetAddress().equals(msg.ip)){ + //got the connection from the right host + //Close listenning socket. + ss.close(); + break; + }else if(ss instanceof SocksServerSocket){ + //We can't accept more then one connection + s.close(); + ss.close(); + throw new SocksException(Proxy.SOCKS_FAILURE); + }else{ + if(acceptTimeout!=0){ //If timeout is not infinit + int newTimeout = acceptTimeout-(int)(System.currentTimeMillis()- + startTime); + if(newTimeout <= 0) throw new InterruptedIOException( + "In doAccept()"); + ss.setSoTimeout(newTimeout); + } + s.close(); //Drop all connections from other hosts + } + } + + //Accepted connection + remote_sock = s; + remote_in = s.getInputStream(); + remote_out = s.getOutputStream(); + + //Set timeout + remote_sock.setSoTimeout(iddleTimeout); + + + ProxyMessage response; + + if(msg.version == 5) + response = new Socks5Message(Proxy.SOCKS_SUCCESS, s.getInetAddress(), + s.getPort()); + else + response = new Socks4Message(Socks4Message.REPLY_OK, + s.getInetAddress(), s.getPort()); + response.write(out); + } + + private ProxyMessage readMsg(InputStream in) throws IOException{ + PushbackInputStream push_in; + if(in instanceof PushbackInputStream) + push_in = (PushbackInputStream) in; + else + push_in = new PushbackInputStream(in); + + int version = push_in.read(); + push_in.unread(version); + + + ProxyMessage msg; + + if(version == 5){ + msg = new Socks5Message(push_in,false); + }else if(version == 4){ + msg = new Socks4Message(push_in,false); + }else{ + throw new SocksException(Proxy.SOCKS_FAILURE); + } + return msg; + } + + private void startPipe(Socket s){ + mode = PIPE_MODE; + remote_sock = s; + try{ + remote_in = s.getInputStream(); + remote_out = s.getOutputStream(); + pipe_thread1 = Thread.currentThread(); + pipe_thread2 = new Thread(this); + pipe_thread2.start(); + pipe(in,remote_out); + }catch(IOException ioe){ + } + } + + private void sendErrorMessage(int error_code){ + ProxyMessage err_msg; + if(msg instanceof Socks4Message) + err_msg = new Socks4Message(Socks4Message.REPLY_REJECTED); + else + err_msg = new Socks5Message(error_code); + try{ + err_msg.write(out); + }catch(IOException ioe){} + } + + private synchronized void abort(){ + if(mode == ABORT_MODE) return; + mode = ABORT_MODE; + try{ + if(remote_sock != null) remote_sock.close(); + if(sock != null) sock.close(); + if(relayServer!=null) relayServer.stop(); + if(ss!=null) ss.close(); + if(pipe_thread1 != null) pipe_thread1.interrupt(); + if(pipe_thread2 != null) pipe_thread2.interrupt(); + }catch(IOException ioe){} + } + + private void pipe(InputStream in,OutputStream out) throws IOException{ + lastReadTime = System.currentTimeMillis(); + byte[] buf = new byte[BUF_SIZE]; + int len = 0; + while(len >= 0){ + try{ + if(len!=0){ + out.write(buf,0,len); + out.flush(); + } + len= in.read(buf); + lastReadTime = System.currentTimeMillis(); + }catch(InterruptedIOException iioe){ + if(iddleTimeout == 0) return;//Other thread interrupted us. + long timeSinceRead = System.currentTimeMillis() - lastReadTime; + if(timeSinceRead >= iddleTimeout - 1000) //-1s for adjustment. + return; + len = 0; + + } + } + } + static final String command_names[] = {"CONNECT","BIND","UDP_ASSOCIATE"}; + + static final String command2String(int cmd){ + if(cmd > 0 && cmd < 4) return command_names[cmd-1]; + else return "Unknown Command "+cmd; + } +} diff --git a/asocks/src/net/sourceforge/jsocks/socks/Socks4Message.java b/asocks/src/net/sourceforge/jsocks/socks/Socks4Message.java new file mode 100644 index 00000000..1dbf66ec --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/Socks4Message.java @@ -0,0 +1,161 @@ +package net.sourceforge.jsocks.socks; + +import java.io.*; +import java.net.InetAddress; +import java.net.UnknownHostException; + +import net.*; + +/** + SOCKS4 Reply/Request message. +*/ + +class Socks4Message extends ProxyMessage{ + + private byte[] msgBytes; + private int msgLength; + + /** + * Server failed reply, cmd command for failed request + */ + public Socks4Message(int cmd){ + super(cmd,null,0); + this.user = null; + + msgLength = 2; + msgBytes = new byte[2]; + + msgBytes[0] = (byte) 0; + msgBytes[1] = (byte) command; + } + + /** + * Server successfull reply + */ + public Socks4Message(int cmd,InetAddress ip,int port){ + this(0,cmd,ip,port,null); + } + + /** + * Client request + */ + public Socks4Message(int cmd,InetAddress ip,int port,String user){ + this(SOCKS_VERSION,cmd,ip,port,user); + } + + /** + * Most general constructor + */ + public Socks4Message(int version, int cmd, + InetAddress ip,int port,String user){ + super(cmd,ip,port); + this.user = user; + this.version = version; + + msgLength = user == null?8:9+user.length(); + msgBytes = new byte[msgLength]; + + msgBytes[0] = (byte) version; + msgBytes[1] = (byte) command; + msgBytes[2] = (byte) (port >> 8); + msgBytes[3] = (byte) port; + + byte[] addr; + + if(ip != null) + addr = ip.getAddress(); + else{ + addr = new byte[4]; + addr[0]=addr[1]=addr[2]=addr[3]=0; + } + System.arraycopy(addr,0,msgBytes,4,4); + + if(user != null){ + byte[] buf = user.getBytes(); + System.arraycopy(buf,0,msgBytes,8,buf.length); + msgBytes[msgBytes.length -1 ] = 0; + } + } + + /** + *Initialise from the stream + *If clientMode is true attempts to read a server response + *otherwise reads a client request + *see read for more detail + */ + public Socks4Message(InputStream in, boolean clientMode) throws IOException{ + msgBytes = null; + read(in,clientMode); + } + + public void read(InputStream in) throws IOException{ + read(in,true); + } + + public void read(InputStream in, boolean clientMode) throws IOException{ + DataInputStream d_in = new DataInputStream(in); + version= d_in.readUnsignedByte(); + command = d_in.readUnsignedByte(); + if(clientMode && command != REPLY_OK){ + String errMsg; + if(command >REPLY_OK && command < REPLY_BAD_IDENTD) + errMsg = replyMessage[command-REPLY_OK]; + else + errMsg = "Unknown Reply Code"; + throw new SocksException(command,errMsg); + } + port = d_in.readUnsignedShort(); + byte[] addr = new byte[4]; + d_in.readFully(addr); + ip=bytes2IP(addr); + host = ip.getHostName(); + if(!clientMode){ + int b = in.read(); + //Hope there are no idiots with user name bigger than this + byte[] userBytes = new byte[256]; + int i = 0; + for(i =0;i0;++i){ + userBytes[i] = (byte) b; + b = in.read(); + } + user = new String(userBytes,0,i); + } + } + public void write(OutputStream out) throws IOException{ + if(msgBytes == null){ + Socks4Message msg = new Socks4Message(version,command,ip,port,user); + msgBytes = msg.msgBytes; + msgLength = msg.msgLength; + } + out.write(msgBytes); + } + + //Class methods + static InetAddress bytes2IP(byte[] addr){ + String s = bytes2IPV4(addr,0); + try{ + return InetAddress.getByName(s); + }catch(UnknownHostException uh_ex){ + return null; + } + } + + //Constants + + static final String[] replyMessage ={ + "Request Granted", + "Request Rejected or Failed", + "Failed request, can't connect to Identd", + "Failed request, bad user name"}; + + static final int SOCKS_VERSION = 4; + + public final static int REQUEST_CONNECT = 1; + public final static int REQUEST_BIND = 2; + + public final static int REPLY_OK = 90; + public final static int REPLY_REJECTED = 91; + public final static int REPLY_NO_CONNECT = 92; + public final static int REPLY_BAD_IDENTD = 93; + +} diff --git a/asocks/src/net/sourceforge/jsocks/socks/Socks4Proxy.java b/asocks/src/net/sourceforge/jsocks/socks/Socks4Proxy.java new file mode 100644 index 00000000..b6906617 --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/Socks4Proxy.java @@ -0,0 +1,123 @@ +package net.sourceforge.jsocks.socks; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.io.*; + +import net.*; + +/** + Proxy which describes SOCKS4 proxy. +*/ + +public class Socks4Proxy extends Proxy implements Cloneable{ + +//Data members + String user; + +//Public Constructors +//==================== + + /** + Creates the SOCKS4 proxy + @param p Proxy to use to connect to this proxy, allows proxy chaining. + @param proxyHost Address of the proxy server. + @param proxyPort Port of the proxy server + @param user User name to use for identification purposes. + @throws UnknownHostException If proxyHost can't be resolved. + */ + public Socks4Proxy(Proxy p,String proxyHost,int proxyPort,String user) + throws UnknownHostException{ + super(p,proxyHost,proxyPort); + this.user = new String(user); + version = 4; + } + + /** + Creates the SOCKS4 proxy + @param proxyHost Address of the proxy server. + @param proxyPort Port of the proxy server + @param user User name to use for identification purposes. + @throws UnknownHostException If proxyHost can't be resolved. + */ + public Socks4Proxy(String proxyHost,int proxyPort,String user) + throws UnknownHostException{ + this(null,proxyHost,proxyPort,user); + } + + /** + Creates the SOCKS4 proxy + @param p Proxy to use to connect to this proxy, allows proxy chaining. + @param proxyIP Address of the proxy server. + @param proxyPort Port of the proxy server + @param user User name to use for identification purposes. + */ + public Socks4Proxy(Proxy p,InetAddress proxyIP,int proxyPort,String user){ + super(p,proxyIP,proxyPort); + this.user = new String(user); + version = 4; + } + + /** + Creates the SOCKS4 proxy + @param proxyIP Address of the proxy server. + @param proxyPort Port of the proxy server + @param user User name to use for identification purposes. + */ + public Socks4Proxy(InetAddress proxyIP,int proxyPort,String user){ + this(null,proxyIP,proxyPort,user); + } + +//Public instance methods +//======================== + + /** + * Creates a clone of this proxy. Changes made to the clone should not + * affect this object. + */ + public Object clone(){ + Socks4Proxy newProxy = new Socks4Proxy(proxyIP,proxyPort,user); + newProxy.directHosts = (InetRange)directHosts.clone(); + newProxy.chainProxy = chainProxy; + return newProxy; + } + + +//Public Static(Class) Methods +//============================== + + +//Protected Methods +//================= + + protected Proxy copy(){ + Socks4Proxy copy = new Socks4Proxy(proxyIP,proxyPort,user); + copy.directHosts = this.directHosts; + copy.chainProxy = chainProxy; + return copy; + } + + protected ProxyMessage formMessage(int cmd,InetAddress ip,int port){ + switch(cmd){ + case SOCKS_CMD_CONNECT: + cmd = Socks4Message.REQUEST_CONNECT; + break; + case SOCKS_CMD_BIND: + cmd = Socks4Message.REQUEST_BIND; + break; + default: + return null; + } + return new Socks4Message(cmd,ip,port,user); + } + protected ProxyMessage formMessage(int cmd,String host,int port) + throws UnknownHostException{ + return formMessage(cmd,InetAddress.getByName(host),port); + } + protected ProxyMessage formMessage(InputStream in) + throws SocksException, + IOException{ + return new Socks4Message(in,true); + } + +} diff --git a/asocks/src/net/sourceforge/jsocks/socks/Socks5DatagramSocket.java b/asocks/src/net/sourceforge/jsocks/socks/Socks5DatagramSocket.java new file mode 100644 index 00000000..b2f09e21 --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/Socks5DatagramSocket.java @@ -0,0 +1,492 @@ +package net.sourceforge.jsocks.socks; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.io.*; + +import net.*; + +/** + Datagram socket to interract through the firewall.
+ Can be used same way as the normal DatagramSocket. One should + be carefull though with the datagram sizes used, as additional data + is present in both incomming and outgoing datagrams. +

+ SOCKS5 protocol allows to send host address as either: +

    +
  • IPV4, normal 4 byte address. (10 bytes header size) +
  • IPV6, version 6 ip address (not supported by Java as for now). + 22 bytes header size. +
  • Host name,(7+length of the host name bytes header size). +
+ As with other Socks equivalents, direct addresses are handled + transparently, that is data will be send directly when required + by the proxy settings. +

+ NOTE:
+ Unlike other SOCKS Sockets, it does not support proxy chaining, + and will throw an exception if proxy has a chain proxy attached. The + reason for that is not my laziness, but rather the restrictions of + the SOCKSv5 protocol. Basicaly SOCKSv5 proxy server, needs to know from + which host:port datagrams will be send for association, and returns address + to which datagrams should be send by the client, but it does not + inform client from which host:port it is going to send datagrams, in fact + there is even no guarantee they will be send at all and from the same address + each time. + + */ +public class Socks5DatagramSocket extends DatagramSocket{ + + InetAddress relayIP; + int relayPort; + Socks5Proxy proxy; + private boolean server_mode = false; + UDPEncapsulation encapsulation; + + + /** + Construct Datagram socket for communication over SOCKS5 proxy + server. This constructor uses default proxy, the one set with + Proxy.setDefaultProxy() method. If default proxy is not set or + it is set to version4 proxy, which does not support datagram + forwarding, throws SocksException. + + */ + public Socks5DatagramSocket() throws SocksException, + IOException{ + this(Proxy.defaultProxy,0,null); + } + /** + Construct Datagram socket for communication over SOCKS5 proxy + server. And binds it to the specified local port. + This constructor uses default proxy, the one set with + Proxy.setDefaultProxy() method. If default proxy is not set or + it is set to version4 proxy, which does not support datagram + forwarding, throws SocksException. + */ + public Socks5DatagramSocket(int port) throws SocksException, + IOException{ + this(Proxy.defaultProxy,port,null); + } + /** + Construct Datagram socket for communication over SOCKS5 proxy + server. And binds it to the specified local port and address. + This constructor uses default proxy, the one set with + Proxy.setDefaultProxy() method. If default proxy is not set or + it is set to version4 proxy, which does not support datagram + forwarding, throws SocksException. + */ + public Socks5DatagramSocket(int port,InetAddress ip) throws SocksException, + IOException{ + this(Proxy.defaultProxy,port,ip); + } + + /** + Constructs datagram socket for communication over specified proxy. + And binds it to the given local address and port. Address of null + and port of 0, signify any availabale port/address. + Might throw SocksException, if: +

    +
  1. Given version of proxy does not support UDP_ASSOCIATE. +
  2. Proxy can't be reached. +
  3. Authorization fails. +
  4. Proxy does not want to perform udp forwarding, for any reason. +
+ Might throw IOException if binding dtagram socket to given address/port + fails. + See java.net.DatagramSocket for more details. + */ + public Socks5DatagramSocket(Proxy p,int port,InetAddress ip) + throws SocksException, + IOException{ + super(port,ip); + if(p == null) throw new SocksException(Proxy.SOCKS_NO_PROXY); + if(!(p instanceof Socks5Proxy)) + throw new SocksException(-1,"Datagram Socket needs Proxy version 5"); + + if(p.chainProxy != null) + throw new SocksException(Proxy.SOCKS_JUST_ERROR, + "Datagram Sockets do not support proxy chaining."); + + proxy =(Socks5Proxy) p.copy(); + + ProxyMessage msg = proxy.udpAssociate(super.getLocalAddress(), + super.getLocalPort()); + relayIP = msg.ip; + if(relayIP.getHostAddress().equals("0.0.0.0")) relayIP = proxy.proxyIP; + relayPort = msg.port; + + encapsulation = proxy.udp_encapsulation; + + //debug("Datagram Socket:"+getLocalAddress()+":"+getLocalPort()+"\n"); + //debug("Socks5Datagram: "+relayIP+":"+relayPort+"\n"); + } + + /** + Used by UDPRelayServer. + */ + Socks5DatagramSocket(boolean server_mode,UDPEncapsulation encapsulation, + InetAddress relayIP,int relayPort) + throws IOException{ + super(); + this.server_mode = server_mode; + this.relayIP = relayIP; + this.relayPort = relayPort; + this.encapsulation = encapsulation; + this.proxy = null; + } + + /** + Sends the Datagram either through the proxy or directly depending + on current proxy settings and destination address.
+ + NOTE: DatagramPacket size should be at least 10 bytes less + than the systems limit. + +

+ See documentation on java.net.DatagramSocket + for full details on how to use this method. + @param dp Datagram to send. + @throws IOException If error happens with I/O. + */ + public void send(DatagramPacket dp) throws IOException{ + //If the host should be accessed directly, send it as is. + if(!server_mode && proxy.isDirect(dp.getAddress())){ + super.send(dp); + //debug("Sending directly:"); + return; + } + + byte[] head = formHeader(dp.getAddress(),dp.getPort()); + byte[] buf = new byte[head.length + dp.getLength()]; + byte[] data = dp.getData(); + //Merge head and data + System.arraycopy(head,0,buf,0,head.length); + //System.arraycopy(data,dp.getOffset(),buf,head.length,dp.getLength()); + System.arraycopy(data,0,buf,head.length,dp.getLength()); + + if(encapsulation != null) + buf = encapsulation.udpEncapsulate(buf,true); + + super.send(new DatagramPacket(buf,buf.length,relayIP,relayPort)); + } + /** + This method allows to send datagram packets with address type DOMAINNAME. + SOCKS5 allows to specify host as names rather than ip addresses.Using + this method one can send udp datagrams through the proxy, without having + to know the ip address of the destination host. +

+ If proxy specified for that socket has an option resolveAddrLocally set + to true host will be resolved, and the datagram will be send with address + type IPV4, if resolve fails, UnknownHostException is thrown. + @param dp Datagram to send, it should contain valid port and data + @param host Host name to which datagram should be send. + @throws IOException If error happens with I/O, or the host can't be + resolved when proxy settings say that hosts should be resolved locally. + @see Socks5Proxy#resolveAddrLocally(boolean) + */ + public void send(DatagramPacket dp, String host) throws IOException{ + if(proxy.isDirect(host)){ + dp.setAddress(InetAddress.getByName(host)); + super.send(dp); + return; + } + + if(((Socks5Proxy)proxy).resolveAddrLocally){ + dp.setAddress(InetAddress.getByName(host)); + } + + byte[] head = formHeader(host,dp.getPort()); + byte[] buf = new byte[head.length + dp.getLength()]; + byte[] data = dp.getData(); + //Merge head and data + System.arraycopy(head,0,buf,0,head.length); + //System.arraycopy(data,dp.getOffset(),buf,head.length,dp.getLength()); + System.arraycopy(data,0,buf,head.length,dp.getLength()); + + if(encapsulation != null) + buf = encapsulation.udpEncapsulate(buf,true); + + super.send(new DatagramPacket(buf,buf.length,relayIP,relayPort)); + } + + /** + * Receives udp packet. If packet have arrived from the proxy relay server, + * it is processed and address and port of the packet are set to the + * address and port of sending host.
+ * If the packet arrived from anywhere else it is not changed.
+ * NOTE: DatagramPacket size should be at least 10 bytes bigger + * than the largest packet you expect (this is for IPV4 addresses). + * For hostnames and IPV6 it is even more. + @param dp Datagram in which all relevent information will be copied. + */ + public void receive(DatagramPacket dp) throws IOException{ + super.receive(dp); + + if(server_mode){ + //Drop all datagrams not from relayIP/relayPort + int init_length = dp.getLength(); + int initTimeout = getSoTimeout(); + long startTime = System.currentTimeMillis(); + + while(!relayIP.equals(dp.getAddress()) || + relayPort != dp.getPort()){ + + //Restore datagram size + dp.setLength(init_length); + + //If there is a non-infinit timeout on this socket + //Make sure that it happens no matter how often unexpected + //packets arrive. + if(initTimeout != 0){ + int newTimeout = initTimeout - (int)(System.currentTimeMillis() - + startTime); + if(newTimeout <= 0) throw new InterruptedIOException( + "In Socks5DatagramSocket->receive()"); + setSoTimeout(newTimeout); + } + + super.receive(dp); + } + + //Restore timeout settings + if(initTimeout != 0) setSoTimeout(initTimeout); + + }else if(!relayIP.equals(dp.getAddress()) || + relayPort != dp.getPort()) + return; // Recieved direct packet + //If the datagram is not from the relay server, return it it as is. + + byte[] data; + data = dp.getData(); + + if(encapsulation != null) + data = encapsulation.udpEncapsulate(data,false); + + int offset = 0; //Java 1.1 + //int offset = dp.getOffset(); //Java 1.2 + + ByteArrayInputStream bIn = new ByteArrayInputStream(data,offset, + dp.getLength()); + + + ProxyMessage msg = new Socks5Message(bIn); + dp.setPort(msg.port); + dp.setAddress(msg.getInetAddress()); + + //what wasn't read by the Message is the data + int data_length = bIn.available(); + //Shift data to the left + System.arraycopy(data,offset+dp.getLength()-data_length, + data,offset,data_length); + + + dp.setLength(data_length); + } + + /** + * Returns port assigned by the proxy, to which datagrams are relayed. + * It is not the same port to which other party should send datagrams. + @return Port assigned by socks server to which datagrams are send + for association. + */ + public int getLocalPort(){ + if(server_mode) return super.getLocalPort(); + return relayPort; + } + /** + * Address assigned by the proxy, to which datagrams are send for relay. + * It is not necesseraly the same address, to which other party should send + * datagrams. + @return Address to which datagrams are send for association. + */ + public InetAddress getLocalAddress(){ + if(server_mode) return super.getLocalAddress(); + return relayIP; + } + + /** + * Closes datagram socket, and proxy connection. + */ + public void close(){ + if(!server_mode) proxy.endSession(); + super.close(); + } + + /** + This method checks wether proxy still runs udp forwarding service + for this socket. +

+ This methods checks wether the primary connection to proxy server + is active. If it is, chances are that proxy continues to forward + datagrams being send from this socket. If it was closed, most likely + datagrams are no longer being forwarded by the server. +

+ Proxy might decide to stop forwarding datagrams, in which case it + should close primary connection. This method allows to check, wether + this have been done. +

+ You can specify timeout for which we should be checking EOF condition + on the primary connection. Timeout is in milliseconds. Specifying 0 as + timeout implies infinity, in which case method will block, until + connection to proxy is closed or an error happens, and then return false. +

+ One possible scenario is to call isProxyactive(0) in separate thread, + and once it returned notify other threads about this event. + + @param timeout For how long this method should block, before returning. + @return true if connection to proxy is active, false if eof or error + condition have been encountered on the connection. + */ + public boolean isProxyAlive(int timeout){ + if(server_mode) return false; + if(proxy != null){ + try{ + proxy.proxySocket.setSoTimeout(timeout); + + int eof = proxy.in.read(); + if(eof < 0) return false; // EOF encountered. + else return true; // This really should not happen + + }catch(InterruptedIOException iioe){ + return true; // read timed out. + }catch(IOException ioe){ + return false; + } + } + return false; + } + +//PRIVATE METHODS +////////////////// + + + private byte[] formHeader(InetAddress ip, int port){ + Socks5Message request = new Socks5Message(0,ip,port); + request.data[0] = 0; + return request.data; + } + + + private byte[] formHeader(String host,int port){ + Socks5Message request = new Socks5Message(0,host,port); + request.data[0] = 0; + return request.data; + } + + +/*====================================================================== + +//Mainly Test functions +////////////////////// + + private String bytes2String(byte[] b){ + String s=""; + char[] hex_digit = { '0','1','2','3','4','5','6','7','8','9', + 'A','B','C','D','E','F'}; + for(int i=0;i> 4; + int i2 = b[i] & 0xF; + s+=hex_digit[i1]; + s+=hex_digit[i2]; + s+=" "; + } + return s; + } + private static final void debug(String s){ + if(DEBUG) + System.out.print(s); + } + + private static final boolean DEBUG = true; + + + public static void usage(){ + System.err.print( + "Usage: java Socks.SocksDatagramSocket host port [socksHost socksPort]\n"); + } + + static final int defaultProxyPort = 1080; //Default Port + static final String defaultProxyHost = "www-proxy"; //Default proxy + + public static void main(String args[]){ + int port; + String host; + int proxyPort; + String proxyHost; + InetAddress ip; + + if(args.length > 1 && args.length < 5){ + try{ + + host = args[0]; + port = Integer.parseInt(args[1]); + + proxyPort =(args.length > 3)? Integer.parseInt(args[3]) + : defaultProxyPort; + + host = args[0]; + ip = InetAddress.getByName(host); + + proxyHost =(args.length > 2)? args[2] + : defaultProxyHost; + + Proxy.setDefaultProxy(proxyHost,proxyPort); + Proxy p = Proxy.getDefaultProxy(); + p.addDirect("lux"); + + + DatagramSocket ds = new Socks5DatagramSocket(); + + + BufferedReader in = new BufferedReader( + new InputStreamReader(System.in)); + String s; + + System.out.print("Enter line:"); + s = in.readLine(); + + while(s != null){ + byte[] data = (s+"\r\n").getBytes(); + DatagramPacket dp = new DatagramPacket(data,0,data.length, + ip,port); + System.out.println("Sending to: "+ip+":"+port); + ds.send(dp); + dp = new DatagramPacket(new byte[1024],1024); + + System.out.println("Trying to recieve on port:"+ + ds.getLocalPort()); + ds.receive(dp); + System.out.print("Recieved:\n"+ + "From:"+dp.getAddress()+":"+dp.getPort()+ + "\n\n"+ + new String(dp.getData(),dp.getOffset(),dp.getLength())+"\n" + ); + System.out.print("Enter line:"); + s = in.readLine(); + + } + ds.close(); + System.exit(1); + + }catch(SocksException s_ex){ + System.err.println("SocksException:"+s_ex); + s_ex.printStackTrace(); + System.exit(1); + }catch(IOException io_ex){ + io_ex.printStackTrace(); + System.exit(1); + }catch(NumberFormatException num_ex){ + usage(); + num_ex.printStackTrace(); + System.exit(1); + } + + }else{ + usage(); + } + } +*/ + +} diff --git a/asocks/src/net/sourceforge/jsocks/socks/Socks5Message.java b/asocks/src/net/sourceforge/jsocks/socks/Socks5Message.java new file mode 100644 index 00000000..e649004d --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/Socks5Message.java @@ -0,0 +1,292 @@ +package net.sourceforge.jsocks.socks; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.DataInputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + SOCKS5 request/response message. +*/ + +class Socks5Message extends ProxyMessage{ + /** Address type of given message*/ + public int addrType; + + byte[] data; + + /** + Server error response. + @param cmd Error code. + */ + public Socks5Message(int cmd){ + super(cmd,null,0); + data = new byte[3]; + data[0] = SOCKS_VERSION; //Version. + data[1] = (byte)cmd; //Reply code for some kind of failure. + data[2] = 0; //Reserved byte. + } + + /** + Construct client request or server response. + @param cmd - Request/Response code. + @param ip - IP field. + @paarm port - port field. + */ + public Socks5Message(int cmd,InetAddress ip,int port){ + super(cmd,ip,port); + this.host = ip==null?"0.0.0.0":ip.getHostName(); + this.version = SOCKS_VERSION; + + byte[] addr; + + if(ip == null){ + addr = new byte[4]; + addr[0]=addr[1]=addr[2]=addr[3]=0; + }else + addr = ip.getAddress(); + + addrType = addr.length == 4 ? SOCKS_ATYP_IPV4 + : SOCKS_ATYP_IPV6; + + data = new byte[6+addr.length]; + data[0] = (byte) SOCKS_VERSION; //Version + data[1] = (byte) command; //Command + data[2] = (byte) 0; //Reserved byte + data[3] = (byte) addrType; //Address type + + //Put Address + System.arraycopy(addr,0,data,4,addr.length); + //Put port + data[data.length-2] = (byte)(port>>8); + data[data.length-1] = (byte)(port); + } + + + /** + Construct client request or server response. + @param cmd - Request/Response code. + @param hostName - IP field as hostName, uses ADDR_TYPE of HOSTNAME. + @paarm port - port field. + */ + public Socks5Message(int cmd,String hostName,int port){ + super(cmd,null,port); + this.host = hostName; + this.version = SOCKS_VERSION; + + //System.out.println("Doing ATYP_DOMAINNAME"); + + addrType = SOCKS_ATYP_DOMAINNAME; + byte addr[] = hostName.getBytes(); + + data =new byte[7+addr.length]; + data[0] = (byte) SOCKS_VERSION; //Version + data[1] = (byte) command; //Command + data[2] = (byte) 0; //Reserved byte + data[3] = (byte) SOCKS_ATYP_DOMAINNAME; //Address type + data[4] = (byte) addr.length; //Length of the address + + //Put Address + System.arraycopy(addr,0,data,5,addr.length); + //Put port + data[data.length-2] = (byte)(port >>8); + data[data.length-1] = (byte)(port); + } + + /** + Initialises Message from the stream. Reads server response from + given stream. + @param in Input stream to read response from. + @throws SocksException If server response code is not SOCKS_SUCCESS(0), or + if any error with protocol occurs. + @throws IOException If any error happens with I/O. + */ + public Socks5Message(InputStream in) throws SocksException, + IOException{ + this(in,true); + } + + /** + Initializes Message from the stream. Reads server response or client + request from given stream. + + @param in Input stream to read response from. + @param clientMode If true read server response, else read client request. + @throws SocksException If server response code is not SOCKS_SUCCESS(0) and + reading in client mode, or if any error with protocol occurs. + @throws IOException If any error happens with I/O. + */ + public Socks5Message(InputStream in,boolean clientMode)throws SocksException, + IOException{ + read(in,clientMode); + } + + + /** + Initialises Message from the stream. Reads server response from + given stream. + @param in Input stream to read response from. + @throws SocksException If server response code is not SOCKS_SUCCESS(0), or + if any error with protocol occurs. + @throws IOException If any error happens with I/O. + */ + public void read(InputStream in) throws SocksException, + IOException{ + read(in,true); + } + + + /** + Initializes Message from the stream. Reads server response or client + request from given stream. + + @param in Input stream to read response from. + @param clientMode If true read server response, else read client request. + @throws SocksException If server response code is not SOCKS_SUCCESS(0) and + reading in client mode, or if any error with protocol occurs. + @throws IOException If any error happens with I/O. + */ + @Override + public void read(InputStream in,boolean clientMode) throws SocksException, + IOException{ + data = null; + ip = null; + + DataInputStream di = new DataInputStream(in); + + version = di.readUnsignedByte(); + command = di.readUnsignedByte(); + if(clientMode && command != 0) + throw new SocksException(command); + + int reserved = di.readUnsignedByte(); + addrType = di.readUnsignedByte(); + + byte addr[]; + + switch(addrType){ + case SOCKS_ATYP_IPV4: + addr = new byte[4]; + di.readFully(addr); + host = bytes2IPV4(addr,0); + break; + case SOCKS_ATYP_IPV6: + addr = new byte[SOCKS_IPV6_LENGTH];//I believe it is 16 bytes,huge! + di.readFully(addr); + host = bytes2IPV6(addr,0); + break; + case SOCKS_ATYP_DOMAINNAME: + //System.out.println("Reading ATYP_DOMAINNAME"); + addr = new byte[di.readUnsignedByte()];//Next byte shows the length + di.readFully(addr); + host = new String(addr); + break; + default: + throw(new SocksException(Proxy.SOCKS_JUST_ERROR)); + } + + port = di.readUnsignedShort(); + + if(addrType != SOCKS_ATYP_DOMAINNAME && doResolveIP){ + try{ + ip = InetAddress.getByName(host); + }catch(UnknownHostException uh_ex){ + } + } + } + + /** + Writes the message to the stream. + @param out Output stream to which message should be written. + */ + public void write(OutputStream out)throws SocksException, + IOException{ + if(data == null){ + Socks5Message msg; + + if(addrType == SOCKS_ATYP_DOMAINNAME) + msg = new Socks5Message(command,host,port); + else{ + if(ip == null){ + try{ + ip = InetAddress.getByName(host); + }catch(UnknownHostException uh_ex){ + throw new SocksException(Proxy.SOCKS_JUST_ERROR); + } + } + msg = new Socks5Message(command,ip,port); + } + data = msg.data; + } + out.write(data); + } + + /** + Returns IP field of the message as IP, if the message was created + with ATYP of HOSTNAME, it will attempt to resolve the hostname, + which might fail. + @throws UnknownHostException if host can't be resolved. + */ + public InetAddress getInetAddress() throws UnknownHostException{ + if(ip!=null) return ip; + + return (ip=InetAddress.getByName(host)); + } + + /** + Returns string representation of the message. + */ + public String toString(){ + String s= + "Socks5Message:"+"\n"+ + "VN "+version+"\n"+ + "CMD "+command+"\n"+ + "ATYP "+addrType+"\n"+ + "ADDR "+host+"\n"+ + "PORT "+port+"\n"; + return s; + } + + + /** + *Wether to resolve hostIP returned from SOCKS server + *that is wether to create InetAddress object from the + *hostName string + */ + static public boolean resolveIP(){ return doResolveIP;} + + /** + *Wether to resolve hostIP returned from SOCKS server + *that is wether to create InetAddress object from the + *hostName string + *@param doResolve Wether to resolve hostIP from SOCKS server. + *@return Previous value. + */ + static public boolean resolveIP(boolean doResolve){ + boolean old = doResolveIP; + doResolveIP = doResolve; + return old; + } + + /* + private static final void debug(String s){ + if(DEBUG) + System.out.print(s); + } + private static final boolean DEBUG = false; + */ + + //SOCKS5 constants + public static final int SOCKS_VERSION =5; + + public static final int SOCKS_ATYP_IPV4 =0x1; //Where is 2?? + public static final int SOCKS_ATYP_DOMAINNAME =0x3; //!!!!rfc1928 + public static final int SOCKS_ATYP_IPV6 =0x4; + + public static final int SOCKS_IPV6_LENGTH =16; + + static boolean doResolveIP = true; + +} diff --git a/asocks/src/net/sourceforge/jsocks/socks/Socks5Proxy.java b/asocks/src/net/sourceforge/jsocks/socks/Socks5Proxy.java new file mode 100644 index 00000000..49601edc --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/Socks5Proxy.java @@ -0,0 +1,253 @@ +package net.sourceforge.jsocks.socks; + +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.io.*; +import java.util.Hashtable; +import java.util.Enumeration; + +import net.*; + +/** + SOCKS5 Proxy. +*/ + +public class Socks5Proxy extends Proxy implements Cloneable{ + +//Data members + private Hashtable authMethods = new Hashtable(); + private int selectedMethod; + + boolean resolveAddrLocally = true; + UDPEncapsulation udp_encapsulation=null; + + +//Public Constructors +//==================== + + /** + Creates SOCKS5 proxy. + @param p Proxy to use to connect to this proxy, allows proxy chaining. + @param proxyHost Host on which a Proxy server runs. + @param proxyPort Port on which a Proxy server listens for connections. + @throws UnknownHostException If proxyHost can't be resolved. + */ + public Socks5Proxy(Proxy p,String proxyHost,int proxyPort) + throws UnknownHostException{ + super(p,proxyHost,proxyPort); + version = 5; + setAuthenticationMethod(0,new AuthenticationNone()); + } + + /** + Creates SOCKS5 proxy. + @param proxyHost Host on which a Proxy server runs. + @param proxyPort Port on which a Proxy server listens for connections. + @throws UnknownHostException If proxyHost can't be resolved. + */ + public Socks5Proxy(String proxyHost,int proxyPort) + throws UnknownHostException{ + this(null,proxyHost,proxyPort); + } + + + /** + Creates SOCKS5 proxy. + @param p Proxy to use to connect to this proxy, allows proxy chaining. + @param proxyIP Host on which a Proxy server runs. + @param proxyPort Port on which a Proxy server listens for connections. + */ + public Socks5Proxy(Proxy p,InetAddress proxyIP,int proxyPort){ + super(p,proxyIP,proxyPort); + version = 5; + setAuthenticationMethod(0,new AuthenticationNone()); + } + + /** + Creates SOCKS5 proxy. + @param proxyIP Host on which a Proxy server runs. + @param proxyPort Port on which a Proxy server listens for connections. + */ + public Socks5Proxy(InetAddress proxyIP,int proxyPort){ + this(null,proxyIP,proxyPort); + } + +//Public instance methods +//======================== + + + /** + * Wether to resolve address locally or to let proxy do so. +

+ SOCKS5 protocol allows to send host names rather then IPs in the + requests, this option controls wether the hostnames should be send + to the proxy server as names, or should they be resolved locally. + @param doResolve Wether to perform resolution locally. + @return Previous settings. + */ + public boolean resolveAddrLocally(boolean doResolve){ + boolean old = resolveAddrLocally; + resolveAddrLocally = doResolve; + return old; + } + /** + Get current setting on how the addresses should be handled. + @return Current setting for address resolution. + @see Socks5Proxy#resolveAddrLocally(boolean doResolve) + */ + public boolean resolveAddrLocally(){ + return resolveAddrLocally; + } + + /** + Adds another authentication method. + @param methodId Authentication method id, see rfc1928 + @param method Implementation of Authentication + @see Authentication + */ + public boolean setAuthenticationMethod(int methodId, + Authentication method){ + if(methodId<0 || methodId > 255) + return false; + if(method == null){ + //Want to remove a particular method + return (authMethods.remove(new Integer(methodId)) != null); + }else{//Add the method, or rewrite old one + authMethods.put(new Integer(methodId),method); + } + return true; + } + + /** + Get authentication method, which corresponds to given method id + @param methodId Authentication method id. + @return Implementation for given method or null, if one was not set. + */ + public Authentication getAuthenticationMethod(int methodId){ + Object method = authMethods.get(new Integer(methodId)); + if(method == null) return null; + return (Authentication)method; + } + + /** + Creates a clone of this Proxy. + */ + public Object clone(){ + Socks5Proxy newProxy = new Socks5Proxy(proxyIP,proxyPort); + newProxy.authMethods = (Hashtable) this.authMethods.clone(); + newProxy.directHosts = (InetRange)directHosts.clone(); + newProxy.resolveAddrLocally = resolveAddrLocally; + newProxy.chainProxy = chainProxy; + return newProxy; + } + +//Public Static(Class) Methods +//============================== + + +//Protected Methods +//================= + + protected Proxy copy(){ + Socks5Proxy copy = new Socks5Proxy(proxyIP,proxyPort); + copy.authMethods = this.authMethods; //same Hash, no copy + copy.directHosts = this.directHosts; + copy.chainProxy = this.chainProxy; + copy.resolveAddrLocally = this.resolveAddrLocally; + return copy; + } + /** + * + * + */ + protected void startSession()throws SocksException{ + super.startSession(); + Authentication auth; + Socket ps = proxySocket; //The name is too long + + try{ + byte nMethods = (byte) authMethods.size(); //Number of methods + + byte[] buf = new byte[2+nMethods]; //2 is for VER,NMETHODS + buf[0] = (byte) version; + buf[1] = nMethods; //Number of methods + int i=2; + + Enumeration ids = authMethods.keys(); + while(ids.hasMoreElements()) + buf[i++] = (byte)((Integer)ids.nextElement()).intValue(); + + out.write(buf); + out.flush(); + + int versionNumber = in.read(); + selectedMethod = in.read(); + + if(versionNumber < 0 || selectedMethod < 0){ + //EOF condition was reached + endSession(); + throw(new SocksException(SOCKS_PROXY_IO_ERROR, + "Connection to proxy lost.")); + } + if(versionNumber < version){ + //What should we do?? + } + if(selectedMethod == 0xFF){ //No method selected + ps.close(); + throw ( new SocksException(SOCKS_AUTH_NOT_SUPPORTED)); + } + + auth = getAuthenticationMethod(selectedMethod); + if(auth == null){ + //This shouldn't happen, unless method was removed by other + //thread, or the server stuffed up + throw(new SocksException(SOCKS_JUST_ERROR, + "Speciefied Authentication not found!")); + } + Object[] in_out = auth.doSocksAuthentication(selectedMethod,ps); + if(in_out == null){ + //Authentication failed by some reason + throw(new SocksException(SOCKS_AUTH_FAILURE)); + } + //Most authentication methods are expected to return + //simply the input/output streams associated with + //the socket. However if the auth. method requires + //some kind of encryption/decryption being done on the + //connection it should provide classes to handle I/O. + + in = (InputStream) in_out[0]; + out = (OutputStream) in_out[1]; + if(in_out.length > 2) + udp_encapsulation = (UDPEncapsulation) in_out[2]; + + }catch(SocksException s_ex){ + throw s_ex; + }catch(UnknownHostException uh_ex){ + throw(new SocksException(SOCKS_PROXY_NO_CONNECT)); + }catch(SocketException so_ex){ + throw(new SocksException(SOCKS_PROXY_NO_CONNECT)); + }catch(IOException io_ex){ + //System.err.println(io_ex); + throw(new SocksException(SOCKS_PROXY_IO_ERROR,""+io_ex)); + } + } + + protected ProxyMessage formMessage(int cmd,InetAddress ip,int port){ + return new Socks5Message(cmd,ip,port); + } + protected ProxyMessage formMessage(int cmd,String host,int port) + throws UnknownHostException{ + if(resolveAddrLocally) + return formMessage(cmd,InetAddress.getByName(host),port); + else + return new Socks5Message(cmd,host,port); + } + protected ProxyMessage formMessage(InputStream in) + throws SocksException, + IOException{ + return new Socks5Message(in); + } + +} diff --git a/asocks/src/net/sourceforge/jsocks/socks/SocksException.java b/asocks/src/net/sourceforge/jsocks/socks/SocksException.java new file mode 100644 index 00000000..cf66c57d --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/SocksException.java @@ -0,0 +1,78 @@ +package net.sourceforge.jsocks.socks; + +/** + Exception thrown by various socks classes to indicate errors + with protocol or unsuccessful server responses. +*/ +public class SocksException extends java.io.IOException{ + /** + Construct a SocksException with given error code. +

+ Tries to look up message which corresponds to this error code. + @param errCode Error code for this exception. + */ + public SocksException(int errCode){ + this.errCode = errCode; + if((errCode >> 16) == 0){ + //Server reply error message + errString = errCode <= serverReplyMessage.length ? + serverReplyMessage[errCode] : + UNASSIGNED_ERROR_MESSAGE; + }else{ + //Local error + errCode = (errCode >> 16) -1; + errString = errCode <= localErrorMessage.length ? + localErrorMessage[errCode] : + UNASSIGNED_ERROR_MESSAGE; + } + } + /** + Constructs a SocksException with given error code and message. + @param errCode Error code. + @param errString Error Message. + */ + public SocksException(int errCode,String errString){ + this.errCode = errCode; + this.errString = errString; + } + /** + Get the error code associated with this exception. + @return Error code associated with this exception. + */ + public int getErrorCode(){ + return errCode; + } + /** + Get human readable representation of this exception. + @return String represntation of this exception. + */ + public String toString(){ + return errString; + } + + static final String UNASSIGNED_ERROR_MESSAGE = + "Unknown error message"; + static final String serverReplyMessage[] = { + "Succeeded", + "General SOCKS server failure", + "Connection not allowed by ruleset", + "Network unreachable", + "Host unreachable", + "Connection refused", + "TTL expired", + "Command not supported", + "Address type not supported" }; + + static final String localErrorMessage[] ={ + "SOCKS server not specified", + "Unable to contact SOCKS server", + "IO error", + "None of Authentication methods are supported", + "Authentication failed", + "General SOCKS fault" }; + + String errString; + int errCode; + +}//End of SocksException class + diff --git a/asocks/src/net/sourceforge/jsocks/socks/SocksServerSocket.java b/asocks/src/net/sourceforge/jsocks/socks/SocksServerSocket.java new file mode 100644 index 00000000..a67d87d8 --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/SocksServerSocket.java @@ -0,0 +1,213 @@ +package net.sourceforge.jsocks.socks; + +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.io.*; + +import net.*; + +/** + SocksServerSocket allows to accept connections from one particular + host through the SOCKS4 or SOCKS5 proxy. +*/ +public class SocksServerSocket extends ServerSocket{ + //Data members + protected Proxy proxy; + protected String localHost; + protected InetAddress localIP; + protected int localPort; + + boolean doing_direct = false; + InetAddress remoteAddr; + + /** + * Creates ServerSocket capable of accepting one connection + * through the firewall, uses default Proxy. + *@param host Host from which the connection should be recieved. + *@param port Port number of the primary connection. + */ + public SocksServerSocket(String host,int port) + throws SocksException,UnknownHostException,IOException{ + this(Proxy.defaultProxy,host,port); + } + /** + *Creates ServerSocket capable of accepting one connection + *through the firewall, uses given proxy. + *@param p Proxy object to use. + *@param host Host from which the connection should be recieved. + *@param port Port number of the primary connection. + */ + public SocksServerSocket(Proxy p,String host,int port) + throws SocksException,UnknownHostException,IOException{ + + + super(0); + if(p == null) throw new SocksException(Proxy.SOCKS_NO_PROXY); + //proxy=p; + proxy = p.copy(); + if(proxy.isDirect(host)){ + remoteAddr = InetAddress.getByName(host); + proxy = null; + doDirect(); + }else{ + processReply(proxy.bind(host,port)); + } + } + + /** + * Creates ServerSocket capable of accepting one connection + * through the firewall, uses default Proxy. + *@param ip Host from which the connection should be recieved. + *@param port Port number of the primary connection. + */ + public SocksServerSocket(InetAddress ip, int port) throws SocksException, + IOException{ + this(Proxy.defaultProxy,ip,port); + } + + /** + *Creates ServerSocket capable of accepting one connection + *through the firewall, uses given proxy. + *@param p Proxy object to use. + *@param ip Host from which the connection should be recieved. + *@param port Port number of the primary connection. + */ + public SocksServerSocket(Proxy p,InetAddress ip, int port) + throws SocksException,IOException{ + super(0); + + if(p == null) throw new SocksException(Proxy.SOCKS_NO_PROXY); + this.proxy = p.copy(); + + if(proxy.isDirect(ip)){ + remoteAddr = ip; + doDirect(); + }else{ + processReply(proxy.bind(ip,port)); + } + } + + + /** + * Accepts the incoming connection. + */ + public Socket accept() throws IOException{ + Socket s; + + if(!doing_direct){ + if(proxy == null) return null; + + ProxyMessage msg = proxy.accept(); + s = msg.ip == null? new SocksSocket(msg.host,msg.port,proxy) + : new SocksSocket(msg.ip,msg.port,proxy); + //Set timeout back to 0 + proxy.proxySocket.setSoTimeout(0); + }else{ //Direct Connection + + //Mimic the proxy behaviour, + //only accept connections from the speciefed host. + while(true){ + s = super.accept(); + if(s.getInetAddress().equals(remoteAddr)){ + //got the connection from the right host + //Close listenning socket. + break; + }else + s.close(); //Drop all connections from other hosts + } + + } + proxy = null; + //Return accepted socket + return s; + } + + /** + * Closes the connection to proxy if socket have not been accepted, if + * the direct connection is used, closes direct ServerSocket. If the + * client socket have been allready accepted, does nothing. + */ + public void close() throws IOException{ + super.close(); + if(proxy != null) proxy.endSession(); + proxy = null; + } + + /** + Get the name of the host proxy is using to listen for incoming + connection. +

+ Usefull when address is returned by proxy as the hostname. + @return the hostname of the address proxy is using to listen + for incoming connection. + */ + public String getHost(){ + return localHost; + } + + /** + * Get address assigned by proxy to listen for incomming + * connections, or the local machine address if doing direct + * connection. + */ + public InetAddress getInetAddress(){ + if(localIP == null){ + try{ + localIP = InetAddress.getByName(localHost); + }catch(UnknownHostException e){ + return null; + } + } + return localIP; + } + + /** + * Get port assigned by proxy to listen for incoming connections, or + the port chosen by local system, if accepting directly. + */ + public int getLocalPort(){ + return localPort; + } + + /** + Set Timeout. + + @param timeout Amount of time in milliseconds, accept should wait for + incoming connection before failing with exception. + Zero timeout implies infinity. + */ + public void setSoTimeout(int timeout) throws SocketException{ + super.setSoTimeout(timeout); + if(!doing_direct) proxy.proxySocket.setSoTimeout(timeout); + } + + +//Private Methods +////////////////// + + private void processReply(ProxyMessage reply)throws SocksException{ + localPort = reply.port; + /* + * If the server have assigned same host as it was contacted on + * it might return an address of all zeros + */ + if(reply.host.equals("0.0.0.0")){ + localIP = proxy.proxyIP; + localHost = localIP.getHostName(); + }else{ + localHost = reply.host; + localIP = reply.ip; + } + } + + private void doDirect(){ + doing_direct = true; + localPort = super.getLocalPort(); + localIP = super.getInetAddress(); + localHost = localIP.getHostName(); + } + +} diff --git a/asocks/src/net/sourceforge/jsocks/socks/SocksSocket.java b/asocks/src/net/sourceforge/jsocks/socks/SocksSocket.java new file mode 100644 index 00000000..d35e0c89 --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/SocksSocket.java @@ -0,0 +1,337 @@ +package net.sourceforge.jsocks.socks; + +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.io.*; + +import net.*; + +/** + * SocksSocket tryies to look very similar to normal Socket, + * while allowing connections through the SOCKS4 or 5 proxy. + * To use this class you will have to identify proxy you need + * to use, Proxy class allows you to set default proxy, which + * will be used by all Socks aware sockets. You can also create + * either Socks4Proxy or Socks5Proxy, and use them by passing to the + * appropriate constructors. + *

+ * Using Socks package can be as easy as that: + * + *


+ *
+ *     import Socks.*;
+ *     ....
+ *
+ *     try{
+ *        //Specify SOCKS5 proxy
+ *        Proxy.setDefaultProxy("socks-proxy",1080);
+ *
+ *        //OR you still use SOCKS4
+ *        //Code below uses SOCKS4 proxy
+ *        //Proxy.setDefaultProxy("socks-proxy",1080,userName);
+ *
+ *        Socket s = SocksSocket("some.host.of.mine",13);
+ *        readTimeFromSock(s);
+ *     }catch(SocksException sock_ex){
+ *        //Usually it will turn in more or less meaningfull message
+ *        System.err.println("SocksException:"+sock_ex);
+ *     }
+ *
+ * 
+ *

+ * However if the need exist for more control, like resolving addresses + * remotely, or using some non-trivial authentication schemes, it can be done. + */ + +public class SocksSocket extends Socket{ + //Data members + protected Proxy proxy; + protected String localHost, remoteHost; + protected InetAddress localIP, remoteIP; + protected int localPort,remotePort; + + private Socket directSock = null; + + /** + * Tryies to connect to given host and port + * using default proxy. If no default proxy speciefied + * it throws SocksException with error code SOCKS_NO_PROXY. + @param host Machine to connect to. + @param port Port to which to connect. + * @see SocksSocket#SocksSocket(Proxy,String,int) + * @see Socks5Proxy#resolveAddrLocally + */ + public SocksSocket(String host,int port) + throws SocksException,UnknownHostException{ + this(Proxy.defaultProxy,host,port); + } + /** + * Connects to host port using given proxy server. + @param p Proxy to use. + @param host Machine to connect to. + @param port Port to which to connect. + @throws UnknownHostException + If one of the following happens: +

    + +
  1. Proxy settings say that address should be resolved locally, but + this fails. +
  2. Proxy settings say that the host should be contacted directly but + host name can't be resolved. +
+ @throws SocksException + If one of the following happens: +
    +
  • Proxy is is null. +
  • Proxy settings say that the host should be contacted directly but + this fails. +
  • Socks Server can't be contacted. +
  • Authentication fails. +
  • Connection is not allowed by the SOCKS proxy. +
  • SOCKS proxy can't establish the connection. +
  • Any IO error occured. +
  • Any protocol error occured. +
+ @throws IOexception if anything is wrong with I/O. + @see Socks5Proxy#resolveAddrLocally + */ + public SocksSocket(Proxy p,String host,int port) + throws SocksException,UnknownHostException{ + + + if(p == null) throw new SocksException(Proxy.SOCKS_NO_PROXY); + //proxy=p; + proxy = p.copy(); + remoteHost = host; + remotePort = port; + if(proxy.isDirect(host)){ + remoteIP = InetAddress.getByName(host); + doDirect(); + } + else + processReply(proxy.connect(host,port)); + } + + + /** + * Tryies to connect to given ip and port + * using default proxy. If no default proxy speciefied + * it throws SocksException with error code SOCKS_NO_PROXY. + @param ip Machine to connect to. + @param port Port to which to connect. + * @see SocksSocket#SocksSocket(Proxy,String,int) + */ + public SocksSocket(InetAddress ip, int port) throws SocksException{ + this(Proxy.defaultProxy,ip,port); + } + + /** + Connects to given ip and port using given Proxy server. + @param p Proxy to use. + @param ip Machine to connect to. + @param port Port to which to connect. + + */ + public SocksSocket(Proxy p,InetAddress ip, int port) throws SocksException{ + if(p == null) throw new SocksException(Proxy.SOCKS_NO_PROXY); + this.proxy = p.copy(); + this.remoteIP = ip; + this.remotePort = port; + this.remoteHost = ip.getHostName(); + if(proxy.isDirect(remoteIP)) + doDirect(); + else + processReply(proxy.connect(ip,port)); + } + + + /** + * These 2 constructors are used by the SocksServerSocket. + * This socket simply overrides remoteHost, remotePort + */ + protected SocksSocket(String host,int port,Proxy proxy){ + this.remotePort = port; + this.proxy = proxy; + this.localIP = proxy.proxySocket.getLocalAddress(); + this.localPort = proxy.proxySocket.getLocalPort(); + this.remoteHost = host; + } + protected SocksSocket(InetAddress ip,int port,Proxy proxy){ + remoteIP = ip; + remotePort = port; + this.proxy = proxy; + this.localIP = proxy.proxySocket.getLocalAddress(); + this.localPort = proxy.proxySocket.getLocalPort(); + remoteHost = remoteIP.getHostName(); + } + + /** + * Same as Socket + */ + public void close() throws IOException{ + if(proxy!= null)proxy.endSession(); + proxy = null; + } + /** + * Same as Socket + */ + public InputStream getInputStream(){ + return proxy.in; + } + /** + * Same as Socket + */ + public OutputStream getOutputStream(){ + return proxy.out; + } + /** + * Same as Socket + */ + public int getPort(){ + return remotePort; + } + /** + * Returns remote host name, it is usefull in cases when addresses + * are resolved by proxy, and we can't create InetAddress object. + @return The name of the host this socket is connected to. + */ + public String getHost(){ + return remoteHost; + } + /** + * Get remote host as InetAddress object, might return null if + * addresses are resolved by proxy, and it is not possible to resolve + * it locally + @return Ip address of the host this socket is connected to, or null + if address was returned by the proxy as DOMAINNAME and can't be + resolved locally. + */ + public InetAddress getInetAddress(){ + if(remoteIP == null){ + try{ + remoteIP = InetAddress.getByName(remoteHost); + }catch(UnknownHostException e){ + return null; + } + } + return remoteIP; + } + + /** + * Get the port assigned by the proxy for the socket, not + * the port on locall machine as in Socket. + @return Port of the socket used on the proxy server. + */ + public int getLocalPort(){ + return localPort; + } + + /** + * Get address assigned by proxy to make a remote connection, + * it might be different from the host specified for the proxy. + * Can return null if socks server returned this address as hostname + * and it can't be resolved locally, use getLocalHost() then. + @return Address proxy is using to make a connection. + */ + public InetAddress getLocalAddress(){ + if(localIP == null){ + try{ + localIP = InetAddress.getByName(localHost); + }catch(UnknownHostException e){ + return null; + } + } + return localIP; + } + /** + Get name of the host, proxy has assigned to make a remote connection + for this socket. This method is usefull when proxy have returned + address as hostname, and we can't resolve it on this machine. + @return The name of the host proxy is using to make a connection. + */ + public String getLocalHost(){ + return localHost; + } + + /** + Same as socket. + */ + public void setSoLinger(boolean on,int val) throws SocketException{ + proxy.proxySocket.setSoLinger(on,val); + } + /** + Same as socket. + */ + public int getSoLinger(int timeout) throws SocketException{ + return proxy.proxySocket.getSoLinger(); + } + /** + Same as socket. + */ + public void setSoTimeout(int timeout) throws SocketException{ + proxy.proxySocket.setSoTimeout(timeout); + } + /** + Same as socket. + */ + public int getSoTimeout(int timeout) throws SocketException{ + return proxy.proxySocket.getSoTimeout(); + } + /** + Same as socket. + */ + public void setTcpNoDelay(boolean on) throws SocketException{ + proxy.proxySocket.setTcpNoDelay(on); + } + /** + Same as socket. + */ + public boolean getTcpNoDelay() throws SocketException{ + return proxy.proxySocket.getTcpNoDelay(); + } + + /** + Get string representation of the socket. + */ + public String toString(){ + if(directSock!=null) return "Direct connection:"+directSock; + return ("Proxy:"+proxy+";"+"addr:"+remoteHost+",port:"+remotePort + +",localport:"+localPort); + + } + +//Private Methods +////////////////// + + private void processReply(ProxyMessage reply)throws SocksException{ + localPort = reply.port; + /* + * If the server have assigned same host as it was contacted on + * it might return an address of all zeros + */ + if(reply.host.equals("0.0.0.0")){ + localIP = proxy.proxyIP; + localHost = localIP.getHostName(); + }else{ + localHost = reply.host; + localIP = reply.ip; + } + } + private void doDirect()throws SocksException{ + try{ + //System.out.println("IP:"+remoteIP+":"+remotePort); + directSock = new Socket(remoteIP,remotePort); + proxy.out = directSock.getOutputStream(); + proxy.in = directSock.getInputStream(); + proxy.proxySocket = directSock; + localIP = directSock.getLocalAddress(); + localPort = directSock.getLocalPort(); + }catch(IOException io_ex){ + throw new SocksException(Proxy.SOCKS_DIRECT_FAILED, + "Direct connect failed:"+io_ex); + } + } + +} diff --git a/asocks/src/net/sourceforge/jsocks/socks/UDPEncapsulation.java b/asocks/src/net/sourceforge/jsocks/socks/UDPEncapsulation.java new file mode 100644 index 00000000..441a210d --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/UDPEncapsulation.java @@ -0,0 +1,30 @@ +package net.sourceforge.jsocks.socks; + +/** + This interface provides for datagram encapsulation for SOCKSv5 protocol. +

+ SOCKSv5 allows for datagrams to be encapsulated for purposes of integrity + and/or authenticity. How it should be done is aggreed during the + authentication stage, and is authentication dependent. This interface is + provided to allow this encapsulation. + @see Authentication +*/ +public interface UDPEncapsulation{ + + /** + This method should provide any authentication depended transformation + on datagrams being send from/to the client. + + @param data Datagram data (including any SOCKS related bytes), to be + encapsulated/decapsulated. + @param out Wether the data is being send out. If true method should + encapsulate/encrypt data, otherwise it should decapsulate/ + decrypt data. + @throw IOException if for some reason data can be transformed correctly. + @return Should return byte array containing data after transformation. + It is possible to return same array as input, if transformation + only involves bit mangling, and no additional data is being + added or removed. + */ + byte[] udpEncapsulate(byte[] data, boolean out) throws java.io.IOException; +} diff --git a/asocks/src/net/sourceforge/jsocks/socks/UDPRelayServer.java b/asocks/src/net/sourceforge/jsocks/socks/UDPRelayServer.java new file mode 100644 index 00000000..48f94d0b --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/UDPRelayServer.java @@ -0,0 +1,217 @@ +package net.sourceforge.jsocks.socks; + +import net.*; +import net.sourceforge.jsocks.socks.server.*; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.io.*; + +/** + UDP Relay server, used by ProxyServer to perform udp forwarding. +*/ +class UDPRelayServer implements Runnable{ + + + DatagramSocket client_sock; + DatagramSocket remote_sock; + + Socket controlConnection; + + int relayPort; + InetAddress relayIP; + + Thread pipe_thread1,pipe_thread2; + Thread master_thread; + + ServerAuthenticator auth; + + long lastReadTime; + + // private static final Logger LOG = Logger.getLogger(UDPRelayServer.class); + + static Proxy proxy = null; + static int datagramSize = 0xFFFF;//64K, a bit more than max udp size + static int iddleTimeout = 180000;//3 minutes + + + /** + Constructs UDP relay server to communicate with client + on given ip and port. + @param clientIP Address of the client from whom datagrams + will be recieved and to whom they will be forwarded. + @param clientPort Clients port. + @param master_thread Thread which will be interrupted, when + UDP relay server stoppes for some reason. + @param controlConnection Socket which will be closed, before + interrupting the master thread, it is introduced due to a bug + in windows JVM which does not throw InterruptedIOException in + threads which block in I/O operation. + */ + public UDPRelayServer(InetAddress clientIP,int clientPort, + Thread master_thread, + Socket controlConnection, + ServerAuthenticator auth) + throws IOException{ + this.master_thread = master_thread; + this.controlConnection = controlConnection; + this.auth = auth; + + client_sock = new Socks5DatagramSocket(true,auth.getUdpEncapsulation(), + clientIP,clientPort); + relayPort = client_sock.getLocalPort(); + relayIP = client_sock.getLocalAddress(); + + if(relayIP.getHostAddress().equals("0.0.0.0")) + relayIP = InetAddress.getLocalHost(); + + if(proxy == null) + remote_sock = new DatagramSocket(); + else + remote_sock = new Socks5DatagramSocket(proxy,0,null); + } + + +//Public methods +///////////////// + + + /** + Sets the timeout for UDPRelay server.
+ Zero timeout implies infinity.
+ Default timeout is 3 minutes. + */ + + static public void setTimeout(int timeout){ + iddleTimeout = timeout; + } + + + /** + Sets the size of the datagrams used in the UDPRelayServer.
+ Default size is 64K, a bit more than maximum possible size of the + datagram. + */ + static public void setDatagramSize(int size){ + datagramSize = size; + } + + /** + Port to which client should send datagram for association. + */ + public int getRelayPort(){ + return relayPort; + } + /** + IP address to which client should send datagrams for association. + */ + public InetAddress getRelayIP(){ + return relayIP; + } + + /** + Starts udp relay server. + Spawns two threads of execution and returns. + */ + public void start() throws IOException{ + remote_sock.setSoTimeout(iddleTimeout); + client_sock.setSoTimeout(iddleTimeout); + + log("Starting UDP relay server on "+relayIP+":"+relayPort); + log("Remote socket "+remote_sock.getLocalAddress()+":"+ + remote_sock.getLocalPort()); + + pipe_thread1 = new Thread(this,"pipe1"); + pipe_thread2 = new Thread(this,"pipe2"); + + lastReadTime = System.currentTimeMillis(); + + pipe_thread1.start(); + pipe_thread2.start(); + } + + /** + Stops Relay server. +

+ Does not close control connection, does not interrupt master_thread. + */ + public synchronized void stop(){ + master_thread = null; + controlConnection = null; + abort(); + } + +//Runnable interface +//////////////////// + public void run(){ + try{ + if(Thread.currentThread().getName().equals("pipe1")) + pipe(remote_sock,client_sock,false); + else + pipe(client_sock,remote_sock,true); + }catch(IOException ioe){ + }finally{ + abort(); + log("UDP Pipe thread "+Thread.currentThread().getName()+" stopped."); + } + + } + +//Private methods +///////////////// + private synchronized void abort(){ + if(pipe_thread1 == null) return; + + log("Aborting UDP Relay Server"); + + remote_sock.close(); + client_sock.close(); + + if(controlConnection != null) + try{ controlConnection.close();} catch(IOException ioe){} + + if(master_thread!=null) master_thread.interrupt(); + + pipe_thread1.interrupt(); + pipe_thread2.interrupt(); + + pipe_thread1 = null; + } + + + static private void log(String s){ + + } + + private void pipe(DatagramSocket from,DatagramSocket to,boolean out) + throws IOException{ + byte[] data = new byte[datagramSize]; + DatagramPacket dp = new DatagramPacket(data,data.length); + + while(true){ + try{ + from.receive(dp); + lastReadTime = System.currentTimeMillis(); + + if(auth.checkRequest(dp,out)) + to.send(dp); + + }catch(UnknownHostException uhe){ + log("Dropping datagram for unknown host"); + }catch(InterruptedIOException iioe){ + //log("Interrupted: "+iioe); + //If we were interrupted by other thread. + if(iddleTimeout == 0) return; + + //If last datagram was received, long time ago, return. + long timeSinceRead = System.currentTimeMillis() - lastReadTime; + if(timeSinceRead >= iddleTimeout -100) //-100 for adjustment + return; + } + dp.setLength(data.length); + } + } +} diff --git a/asocks/src/net/sourceforge/jsocks/socks/UserPasswordAuthentication.java b/asocks/src/net/sourceforge/jsocks/socks/UserPasswordAuthentication.java new file mode 100644 index 00000000..bbca762e --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/UserPasswordAuthentication.java @@ -0,0 +1,75 @@ +package net.sourceforge.jsocks.socks; + +/** + SOCKS5 User Password authentication scheme. +*/ +public class UserPasswordAuthentication implements Authentication{ + + /**SOCKS ID for User/Password authentication method*/ + public final static int METHOD_ID = 2; + + String userName, password; + byte[] request; + + /** + Create an instance of UserPasswordAuthentication. + @param userName User Name to send to SOCKS server. + @param password Password to send to SOCKS server. + */ + public UserPasswordAuthentication(String userName,String password){ + this.userName = userName; + this.password = password; + formRequest(); + } + /** Get the user name. + @return User name. + */ + public String getUser(){ + return userName; + } + /** Get password + @return Password + */ + public String getPassword(){ + return password; + } + /** + Does User/Password authentication as defined in rfc1929. + @return An array containnig in, out streams, or null if authentication + fails. + */ + public Object[] doSocksAuthentication(int methodId, + java.net.Socket proxySocket) + throws java.io.IOException{ + + if(methodId != METHOD_ID) return null; + + java.io.InputStream in = proxySocket.getInputStream(); + java.io.OutputStream out = proxySocket.getOutputStream(); + + out.write(request); + int version = in.read(); + if(version < 0) return null; //Server closed connection + int status = in.read(); + if(status != 0) return null; //Server closed connection, or auth failed. + + return new Object[] {in,out}; + } + +//Private methods +////////////////// + +/** Convert UserName password in to binary form, ready to be send to server*/ + private void formRequest(){ + byte[] user_bytes = userName.getBytes(); + byte[] password_bytes = password.getBytes(); + + request = new byte[3+user_bytes.length+password_bytes.length]; + request[0] = (byte) 1; + request[1] = (byte) user_bytes.length; + System.arraycopy(user_bytes,0,request,2,user_bytes.length); + request[2+user_bytes.length] = (byte) password_bytes.length; + System.arraycopy(password_bytes,0, + request,3+user_bytes.length,password_bytes.length); + } +} diff --git a/asocks/src/net/sourceforge/jsocks/socks/server/Ident.java b/asocks/src/net/sourceforge/jsocks/socks/server/Ident.java new file mode 100644 index 00000000..2ac0f24b --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/server/Ident.java @@ -0,0 +1,161 @@ +package net.sourceforge.jsocks.socks.server; + +import java.net.ConnectException; +import java.net.Socket; +import java.io.*; +import java.util.StringTokenizer; + +import net.*; + +/** + Class Ident provides means to obtain user name of the owner of the socket + on remote machine, providing remote machine runs identd daemon. +

+ To use it: +

+   Socket s = ss.accept();
+   Ident id = new Ident(s);
+   if(id.successful) goUseUser(id.userName);
+   else handleIdentError(id.errorCode,id.errorMessage)
+   
+*/ +public class Ident{ + + /** Error Message can be null.*/ + public String errorMessage; + /** Host type as returned by daemon, can be null, if error happened*/ + public String hostType; + /** User name as returned by the identd daemon, or null, if it failed*/ + public String userName; + + /** If this is true then userName and hostType contain valid values. + Else errorCode contain the error code, and errorMessage contains + the corresponding message. + */ + public boolean successful; + /** Error code*/ + public int errorCode; + /** Identd on port 113 can't be contacted*/ + public static final int ERR_NO_CONNECT = 1; + /** Connection timed out*/ + public static final int ERR_TIMEOUT = 2; + /** Identd daemon responded with ERROR, in this case errorMessage + contains the string explanation, as send by the daemon. + */ + public static final int ERR_PROTOCOL = 3; + /** + When parsing server response protocol error happened. + */ + public static final int ERR_PROTOCOL_INCORRECT = 4; + + + /** Maximum amount of time we should wait before dropping the + connection to identd server.Setting it to 0 implies infinit + timeout. + */ + public static final int connectionTimeout = 10000; + + + /** + Constructor tries to connect to Identd daemon on the host of the + given socket, and retrieve user name of the owner of given socket + connection on remote machine. After constructor returns public + fields are initialised to whatever the server returned. +

+ If user name was successfully retrieved successful is set to true, + and userName and hostType are set to whatever server returned. If + however for some reason user name was not obtained, successful is set + to false and errorCode contains the code explaining the reason of + failure, and errorMessage contains human readable explanation. +

+ Constructor may block, for a while. + @param s Socket whose ownership on remote end should be obtained. + */ + public Ident(Socket s ){ + Socket sock = null; + successful = false; //We are pessimistic + + try{ + sock = new Socket(s.getInetAddress(),113); + sock.setSoTimeout(connectionTimeout); + byte[] request = (""+s.getPort()+" , "+ + s.getLocalPort()+"\r\n").getBytes(); + + sock.getOutputStream().write(request); + + BufferedReader in = new BufferedReader( + new InputStreamReader(sock.getInputStream())); + + parseResponse(in.readLine()); + + }catch(InterruptedIOException iioe){ + errorCode = ERR_TIMEOUT; + errorMessage = "Connection to identd timed out."; + }catch(ConnectException ce){ + errorCode = ERR_NO_CONNECT; + errorMessage = "Connection to identd server failed."; + + }catch(IOException ioe){ + errorCode = ERR_NO_CONNECT; + errorMessage = ""+ioe; + }finally{ + try{ if(sock!=null) sock.close();}catch(IOException ioe){}; + } + } + + private void parseResponse(String response){ + if(response == null){ + errorCode = ERR_PROTOCOL_INCORRECT; + errorMessage = "Identd server closed connection."; + return; + } + + StringTokenizer st = new StringTokenizer(response,":"); + if(st.countTokens() < 3){ + errorCode = ERR_PROTOCOL_INCORRECT; + errorMessage = "Can't parse server response."; + return; + } + + st.nextToken(); //Discard first token, it's basically what we have send + String command = st.nextToken().trim().toUpperCase(); + + if(command.equals("USERID") && st.countTokens() >= 2){ + successful = true; + hostType = st.nextToken().trim(); + userName = st.nextToken("").substring(1);//Get all that is left + }else if(command.equals("ERROR")){ + errorCode = ERR_PROTOCOL; + errorMessage = st.nextToken(); + }else{ + errorCode = ERR_PROTOCOL_INCORRECT; + System.out.println("Opa!"); + errorMessage = "Can't parse server response."; + } + + + } + +/////////////////////////////////////////////// +//USED for Testing +/* + public static void main(String[] args) throws IOException{ + + Socket s = null; + s = new Socket("gp101-16", 1391); + + Ident id = new Ident(s); + if(id.successful){ + System.out.println("User: "+id.userName); + System.out.println("HostType: "+id.hostType); + }else{ + System.out.println("ErrorCode: "+id.errorCode); + System.out.println("ErrorMessage: "+id.errorMessage); + + } + + if(s!= null) s.close(); + } +//*/ + +} diff --git a/asocks/src/net/sourceforge/jsocks/socks/server/IdentAuthenticator.java b/asocks/src/net/sourceforge/jsocks/socks/server/IdentAuthenticator.java new file mode 100644 index 00000000..22bc1f06 --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/server/IdentAuthenticator.java @@ -0,0 +1,153 @@ +package net.sourceforge.jsocks.socks.server; + +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Vector; +import java.net.InetAddress; +import java.net.Socket; +import java.io.*; + +import net.*; +import net.sourceforge.jsocks.socks.InetRange; +import net.sourceforge.jsocks.socks.ProxyMessage; + +/** + An implementation of socks.ServerAuthentication which provides + simple authentication based on the host from which the connection + is made and the name of the user on the remote machine, as reported + by identd daemon on the remote machine. +

+ It can also be used to provide authentication based only on the contacting + host address. +*/ + +public class IdentAuthenticator extends ServerAuthenticatorNone{ + /** Vector of InetRanges */ + Vector hosts; + + /** Vector of user hashes*/ + Vector users; + + String user; + + + /** + Constructs empty IdentAuthenticator. + */ + public IdentAuthenticator(){ + hosts = new Vector(); + users = new Vector(); + } + /** + Used to create instances returned from startSession. + @param in Input stream. + @param out OutputStream. + @param user Username associated with this connection,could be + null if name was not required. + */ + IdentAuthenticator(InputStream in,OutputStream out, String user){ + super(in,out); + this.user = user; + } + + /** + Adds range of addresses from which connection is allowed. Hashtable + users should contain user names as keys and anything as values + (value is not used and will be ignored). + @param hostRange Range of ip addresses from which connection is allowed. + @param users Hashtable of users for whom connection is allowed, or null + to indicate that anybody is allowed to connect from the hosts within given + range. + */ + public synchronized void add(InetRange hostRange,Hashtable users){ + this.hosts.addElement(hostRange); + this.users.addElement(users); + } + + /** + Grants permission only to those users, who connect from one of the + hosts registered with add(InetRange,Hashtable) and whose names, as + reported by identd daemon, are listed for the host the connection + came from. + */ + public ServerAuthenticator startSession(Socket s) + throws IOException{ + + int ind = getRangeIndex(s.getInetAddress()); + String user = null; + + //System.out.println("getRangeReturned:"+ind); + + if(ind < 0) return null; //Host is not on the list. + + ServerAuthenticatorNone auth = (ServerAuthenticatorNone) + super.startSession(s); + + //System.out.println("super.startSession() returned:"+auth); + if(auth == null) return null; + + //do the authentication + + Hashtable user_names = (Hashtable) users.elementAt(ind); + + if(user_names != null){ //If need to do authentication + Ident ident; + ident = new Ident(s); + //If can't obtain user name, fail + if(!ident.successful) return null; + //If user name is not listed for this address, fail + if(!user_names.containsKey(ident.userName)) return null; + user = ident.userName; + } + return new IdentAuthenticator(auth.in,auth.out,user); + + } + /** + For SOCKS5 requests allways returns true. For SOCKS4 requests + checks wether the user name supplied in the request corresponds + to the name obtained from the ident daemon. + */ + public boolean checkRequest(ProxyMessage msg,java.net.Socket s){ + //If it's version 5 request, or if anybody is permitted, return true; + if(msg.version == 5 || user == null) + return true; + + if(msg.version != 4) return false; //Who knows? + + return user.equals(msg.user); + } + + /** Get String representaion of the IdentAuthenticator.*/ + public String toString(){ + String s = ""; + + for(int i=0;i + At this point no data have been extracted from the connection. It is + responsibility of this method to ensure that the next byte in the + stream after this method have been called is the first byte of the + socks request message. For SOCKSv4 there is no authentication data and + the first byte in the stream is part of the request. With SOCKSv5 however + there is an authentication data first. It is expected that implementaions + will process this authentication data. +

+ If authentication was successful an instance of ServerAuthentication + should be returned, it later will be used by the server to perform + authorization and some other things. If authentication fails null should + be returned, or an exception may be thrown. + + @param s Accepted Socket. + @return An instance of ServerAuthenticator to be used for this connection + or null + */ + ServerAuthenticator startSession(Socket s) throws IOException; + + /** + This method should return input stream which should be used on the + accepted socket. +

+ SOCKSv5 allows to have multiple authentication methods, and these methods + might require some kind of transformations being made on the data. +

+ This method is called on the object returned from the startSession + function. + */ + InputStream getInputStream(); + /** + This method should return output stream to use to write to the accepted + socket. +

+ SOCKSv5 allows to have multiple authentication methods, and these methods + might require some kind of transformations being made on the data. +

+ This method is called on the object returned from the startSession + function. + */ + OutputStream getOutputStream(); + + /** + This method should return UDPEncapsulation, which should be used + on the datagrams being send in/out. +

+ If no transformation should be done on the datagrams, this method + should return null. +

+ This method is called on the object returned from the startSession + function. + */ + + UDPEncapsulation getUdpEncapsulation(); + + /** + This method is called when a request have been read. +

+ Implementation should decide wether to grant request or not. Returning + true implies granting the request, false means request should be rejected. +

+ This method is called on the object returned from the startSession + function. + @param msg Request message. + @return true to grant request, false to reject it. + */ + boolean checkRequest(ProxyMessage msg); + + /** + This method is called when datagram is received by the server. +

+ Implementaions should decide wether it should be forwarded or dropped. + It is expecteed that implementation will use datagram address and port + information to make a decision, as well as anything else. Address and + port of the datagram are always correspond to remote machine. It is + either destination or source address. If out is true address is destination + address, else it is a source address, address of the machine from which + datagram have been received for the client. +

+ Implementaions should return true if the datagram is to be forwarded, and + false if the datagram should be dropped. +

+ This method is called on the object returned from the startSession + function. + + @param out If true the datagram is being send out(from the client), + otherwise it is an incoming datagram. + @return True to forward datagram false drop it silently. + */ + boolean checkRequest(DatagramPacket dp, boolean out); + + /** + This method is called when session is completed. Either due to normal + termination or due to any error condition. +

+ This method is called on the object returned from the startSession + function. + */ + void endSession(); +} diff --git a/asocks/src/net/sourceforge/jsocks/socks/server/ServerAuthenticatorNone.java b/asocks/src/net/sourceforge/jsocks/socks/server/ServerAuthenticatorNone.java new file mode 100644 index 00000000..55a84ec3 --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/socks/server/ServerAuthenticatorNone.java @@ -0,0 +1,172 @@ +package net.sourceforge.jsocks.socks.server; + +import net.sourceforge.jsocks.socks.ProxyMessage; +import net.sourceforge.jsocks.socks.UDPEncapsulation; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PushbackInputStream; +import java.net.Socket; + +/** + An implementation of ServerAuthenticator, which does not do + any authentication. +

+ Warning!!
Should not be +used on machines which are not behind the firewall. +

+It is only provided to make implementing other authentication schemes +easier.
+For Example:

+   class MyAuth extends socks.server.ServerAuthenticator{
+    ...
+    public ServerAuthenticator startSession(java.net.Socket s){
+      if(!checkHost(s.getInetAddress()) return null;
+      return super.startSession(s);
+    }
+
+    boolean checkHost(java.net.Inetaddress addr){
+      boolean allow;
+      //Do it somehow
+      return allow;
+    }
+   }
+
+*/ +public class ServerAuthenticatorNone implements ServerAuthenticator{ + + static final byte[] socks5response = {5,0}; + + InputStream in; + OutputStream out; + + /** + Creates new instance of the ServerAuthenticatorNone. + */ + public ServerAuthenticatorNone(){ + this.in = null; + this.out = null; + } + /** + Constructs new ServerAuthenticatorNone object suitable for returning + from the startSession function. + @param in Input stream to return from getInputStream method. + @param out Output stream to return from getOutputStream method. + */ + public ServerAuthenticatorNone(InputStream in, OutputStream out){ + this.in = in; + this.out = out; + } + /** + Grants access to everyone.Removes authentication related bytes from + the stream, when a SOCKS5 connection is being made, selects an + authentication NONE. + */ + public ServerAuthenticator startSession(Socket s) + throws IOException{ + + PushbackInputStream in = new PushbackInputStream(s.getInputStream()); + OutputStream out = s.getOutputStream(); + + int version = in.read(); + if(version == 5){ + if(!selectSocks5Authentication(in,out,0)) + return null; + }else if(version == 4){ + //Else it is the request message allready, version 4 + in.unread(version); + }else + return null; + + + return new ServerAuthenticatorNone(in,out); + } + + /** + Get input stream. + @return Input stream speciefied in the constructor. + */ + public InputStream getInputStream(){ + return in; + } + /** + Get output stream. + @return Output stream speciefied in the constructor. + */ + public OutputStream getOutputStream(){ + return out; + } + /** + Allways returns null. + @return null + */ + public UDPEncapsulation getUdpEncapsulation(){ + return null; + } + + /** + * Checks the request. For this stub always return true. + * + * @returns true if request is OK or false if its not. + */ + public boolean checkRequest(ProxyMessage msg) { + return true; + } + + /** + * Checks the request. For this stub always return true. + */ + public boolean checkRequest(java.net.DatagramPacket dp, boolean out){ + return true; + } + + /** + Does nothing. + */ + public void endSession(){ + } + + /** + Convinience routine for selecting SOCKSv5 authentication. +

+ This method reads in authentication methods that client supports, + checks wether it supports given method. If it does, the notification + method is written back to client, that this method have been chosen + for authentication. If given method was not found, authentication + failure message is send to client ([5,FF]). + @param in Input stream, version byte should be removed from the stream + before calling this method. + @param out Output stream. + @param methodId Method which should be selected. + @return true if methodId was found, false otherwise. + */ + static public boolean selectSocks5Authentication(InputStream in, + OutputStream out, + int methodId) + throws IOException{ + + int num_methods = in.read(); + if (num_methods <= 0) return false; + byte method_ids[] = new byte[num_methods]; + byte response[] = new byte[2]; + boolean found = false; + + response[0] = (byte) 5; //SOCKS version + response[1] = (byte) 0xFF; //Not found, we are pessimistic + + int bread = 0; //bytes read so far + while(bread < num_methods) + bread += in.read(method_ids,bread,num_methods-bread); + + for(int i=0;i 0){ + System.out.write(buf,0,bytes_read); + System.out.flush(); + } + }catch(IOException io_ex){ + io_ex.printStackTrace(); + } + } + + public static void usage(){ + System.err.print( + "Usage: java Echo host port [peerHost peerPort]\n"); + } + + + public static void main(String args[]){ + int port; + String host,peerHost; + int peerPort; + Echo echo = null; + + if(args.length > 1){ + try{ + + host = args[0]; + port = Integer.parseInt(args[1]); + + if(args.length ==4){ + peerHost = args[2]; + peerPort =Integer.parseInt(args[3]); + echo = new Echo(host,port,peerHost,peerPort); + }else{ + echo = new Echo(host,port); + } + + Thread thread = new Thread(echo); + thread.start(); + + BufferedReader in = new BufferedReader( + new InputStreamReader(System.in)); + String s; + + s = in.readLine(); + Thread.currentThread().setPriority(Thread.NORM_PRIORITY); + while(s != null){ + echo.send(s+"\r\n"); + s = in.readLine(); + } + }catch(IOException io_ex){ + io_ex.printStackTrace(); + System.exit(1); + }catch(NumberFormatException num_ex){ + usage(); + num_ex.printStackTrace(); + System.exit(1); + }finally{ + if(echo!=null) try{echo.ss.close();}catch(Exception e){} + } + + }else{ + usage(); + } + } + +}//End of class diff --git a/asocks/src/net/sourceforge/jsocks/test/SocksTest.java b/asocks/src/net/sourceforge/jsocks/test/SocksTest.java new file mode 100644 index 00000000..173bf367 --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/test/SocksTest.java @@ -0,0 +1,129 @@ +package net.sourceforge.jsocks.test; + +import java.io.*; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import net.sourceforge.jsocks.socks.*; +//import net.sourceforge.jsocks.socks.Proxy; + +/** SOCKS aware echo client*/ + +public class SocksTest implements Runnable{ + + private int port; + private InetAddress hostIP; + + private Socket ss; + private InputStream in; + private OutputStream out; + + private static final int BUF_SIZE = 1024; + static final int defaultProxyPort = 1080; //Default Port + static final String defaultProxyHost = "www-proxy"; //Default proxy + + public SocksTest(String host,int port) + throws IOException,UnknownHostException,SocksException{ + this.port = port; + + ss = new SocksSocket(host, port); + out = ss.getOutputStream(); + in = ss.getInputStream(); + System.out.println("Connected..."); + System.out.println("TO: "+host+":"+port); + System.out.println("ViaProxy: "+ss.getLocalAddress().getHostAddress() + +":"+ss.getLocalPort()); + + } + + public void close()throws IOException{ + ss.close(); + } + public void send(String s) throws IOException{ + out.write(s.getBytes()); + } + + public void run(){ + byte[] buf = new byte[1024]; + int bytes_read; + try{ + while((bytes_read = in.read(buf)) > 0){ + System.out.write(buf,0,bytes_read); + } + }catch(IOException io_ex){ + io_ex.printStackTrace(); + } + } + + public static void usage(){ + System.err.print( + "Usage: java SocksTest host port [socksHost socksPort]\n"); + } + + + public static void main(String args[]){ + int port; + String host; + int proxyPort; + String proxyHost; + + if(args.length > 1 && args.length < 5){ + try{ + + host = args[0]; + port = Integer.parseInt(args[1]); + + proxyPort =(args.length > 3)? Integer.parseInt(args[3]) + : defaultProxyPort; + + host = args[0]; + proxyHost =(args.length > 2)? args[2] + : defaultProxyHost; + + Proxy.setDefaultProxy(proxyHost,proxyPort,"KOUKY001"); + //Proxy.setDefaultProxy(proxyHost,proxyPort); + InetRange inetRange = new InetRange(); + inetRange.add(InetAddress.getByName("localhost")); + Proxy.getDefaultProxy().setDirect(inetRange); + + + SocksTest st = new SocksTest(host,port); + Thread thread = new Thread(st); + thread.start(); + + BufferedReader in = new BufferedReader( + new InputStreamReader(System.in)); + String s; + + s = in.readLine(); + while(s != null){ + st.send(s+"\r\n"); + //try{ + //Thread.currentThread().sleep(10); + //}catch(InterruptedException i_ex){ + //} + s = in.readLine(); + } + st.close(); + System.exit(1); + + }catch(SocksException s_ex){ + System.err.println("SocksException:"+s_ex); + s_ex.printStackTrace(); + System.exit(1); + }catch(IOException io_ex){ + io_ex.printStackTrace(); + System.exit(1); + }catch(NumberFormatException num_ex){ + usage(); + num_ex.printStackTrace(); + System.exit(1); + } + + }else{ + usage(); + } + } + +}//End of class diff --git a/asocks/src/net/sourceforge/jsocks/test/SocksUDPEcho.java b/asocks/src/net/sourceforge/jsocks/test/SocksUDPEcho.java new file mode 100644 index 00000000..aee6fbe6 --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/test/SocksUDPEcho.java @@ -0,0 +1,103 @@ +package net.sourceforge.jsocks.test; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.io.*; + +import net.sourceforge.jsocks.socks.*; + +/** SOCKS aware UDP echo client.
+ Reads input line by line and sends it to given on command line + host and port, using given proxy, then blocks until reply datagram + recieved, not really echo, single threaded client, I just used it + for testing before UDP actually worked. +*/ +public class SocksUDPEcho{ + + public static void usage(){ + System.err.print( + "Usage: java SocksUDPEcho host port [socksHost socksPort]\n"); + } + + static final int defaultProxyPort = 1080; //Default Port + static final String defaultProxyHost = "www-proxy"; //Default proxy + + public static void main(String args[]){ + int port; + String host; + int proxyPort; + String proxyHost; + InetAddress ip; + + if(args.length > 1 && args.length < 5){ + try{ + + host = args[0]; + port = Integer.parseInt(args[1]); + + proxyPort =(args.length > 3)? Integer.parseInt(args[3]) + : defaultProxyPort; + + host = args[0]; + ip = InetAddress.getByName(host); + + proxyHost =(args.length > 2)? args[2] + : defaultProxyHost; + + Proxy.setDefaultProxy(proxyHost,proxyPort); + Proxy p = Proxy.getDefaultProxy(); + p.addDirect("lux"); + + + DatagramSocket ds = new Socks5DatagramSocket(); + + + BufferedReader in = new BufferedReader( + new InputStreamReader(System.in)); + String s; + + System.out.print("Enter line:"); + s = in.readLine(); + + while(s != null){ + byte[] data = (s+"\r\n").getBytes(); + DatagramPacket dp = new DatagramPacket(data,0,data.length, + ip,port); + System.out.println("Sending to: "+ip+":"+port); + ds.send(dp); + dp = new DatagramPacket(new byte[1024],1024); + + System.out.println("Trying to recieve on port:"+ + ds.getLocalPort()); + ds.receive(dp); + System.out.print("Recieved:\n"+ + "From:"+dp.getAddress()+":"+dp.getPort()+ + "\n\n"+ + new String(dp.getData(),dp.getOffset(),dp.getLength())+"\n" + ); + System.out.print("Enter line:"); + s = in.readLine(); + + } + ds.close(); + System.exit(1); + + }catch(SocksException s_ex){ + System.err.println("SocksException:"+s_ex); + s_ex.printStackTrace(); + System.exit(1); + }catch(IOException io_ex){ + io_ex.printStackTrace(); + System.exit(1); + }catch(NumberFormatException num_ex){ + usage(); + num_ex.printStackTrace(); + System.exit(1); + } + + }else{ + usage(); + } + } +} diff --git a/asocks/src/net/sourceforge/jsocks/test/TestClient.java b/asocks/src/net/sourceforge/jsocks/test/TestClient.java new file mode 100644 index 00000000..e53ca06d --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/test/TestClient.java @@ -0,0 +1,241 @@ +package net.sourceforge.jsocks.test; + +import java.io.*; +import java.net.InetAddress; +import java.net.ServerSocket; + +import net.sourceforge.jsocks.socks.*; +import net.sourceforge.jsocks.socks.Proxy; + +public class TestClient extends TestService{ + /** Proxy which should be used*/ + Proxy proxy; + /** Host on which TestServer is running*/ + String testHost; + + int timeout = 15000; + int acceptTimeout = 0; + + BufferedReader in; + Writer out; + + public TestClient(Proxy p,String testHost){ + this.proxy = p; + this.testHost = testHost; + if(log == null) log = System.out; + } + + public void start(){ + connectTests(true); + acceptTests(true); + udpTests(true); + + connectTests(false); + acceptTests(false); + udpTests(false); + } + + void connectTests(boolean useString){ + try{ + open(ECHO, useString); + testEcho(); + s.close(); + + open(DISCARD, useString); + testDiscard(); + s.close(); + + open(CHARGEN, useString); + + for(int i = 0; i< 3;){ + try{ + testChargen(); + break; + }catch(InterruptedIOException ioe){ + log("IO interrupted:"+i); + i++; + } + } + + s.close(); + + }catch(IOException ioe){ + ioe.printStackTrace(); + } + + } + + void acceptTests(boolean useString){ + try{ + testAccept(ECHO, useString); + testEcho(); + s.close(); + + testAccept(DISCARD, useString); + testDiscard(); + s.close(); + + testAccept(CHARGEN, useString); + + for(int i = 0; i< 3;){ + try{ + testChargen(); + break; + }catch(InterruptedIOException ioe){ + log("IO interrupted:"+i); + i++; + } + } + s.close(); + + }catch(IOException ioe){ + ioe.printStackTrace(); + } + } + + void udpTests(boolean useString){ + log("Udp tests are not yet implemented"); + } + + void testEcho() throws IOException{ + log("Testing echo."); + for(int i=0;i<5;++i){ + out.write("String number "+i+"\r\n"); + out.flush(); + log("Echo:"+in.readLine());; + } + log("Echo finished"); + } + + void testDiscard() throws IOException{ + log("Testing discard"); + for(int i =0; i < 5;++i){ + log("Sending discard message:"+i); + out.write("Discard message:"+i+"\r\n"); + out.flush(); + } + log("Discard finished"); + } + + void testChargen() throws IOException{ + log("Testing chargen"); + String s; + s = in.readLine(); + while(s!=null){ + log("ChGen:"+s); + s = in.readLine(); + } + log("Chargen finished."); + } + + void testAccept(int service,boolean useString)throws IOException{ + open(CONNECT,useString); + + log("Testing accept"); + ServerSocket ss; + + if(useString) + ss = new SocksServerSocket(proxy,testHost,servicePorts[service]); + else + ss = new SocksServerSocket(proxy,InetAddress.getByName(testHost), + servicePorts[service]); + log("Listenning on "+ss.getInetAddress()+":"+ss.getLocalPort()); + ss.setSoTimeout(acceptTimeout); + + out.write(""+ss.getLocalPort()+" "+service+"\r\n"); + out.flush(); + + String line = in.readLine(); + if(line != null){ + log("Accept failed:"+line); + } + + s.close(); + + s = ss.accept(); + log("Accepted:"+s); + + s.setSoTimeout(timeout); + + out = new OutputStreamWriter(s.getOutputStream()); + in = new BufferedReader(new InputStreamReader(s.getInputStream())); + + ss.close(); + } + + void open(int service,boolean useString) throws IOException{ + + if(!useString){ + s = new SocksSocket(proxy,InetAddress.getByName(testHost), + servicePorts[service]); + }else{ + s = new SocksSocket(proxy,testHost,servicePorts[service]); + } + + s.setSoTimeout(timeout); + + out = new OutputStreamWriter(s.getOutputStream()); + in = new BufferedReader(new InputStreamReader(s.getInputStream())); + } + + //Main function + /////////////// + + static void usage(){ + System.err.println( + "Usage: java Testclient testhost proxy [directhosts]"); + } + + static Proxy initProxy(String ps){ + java.util.StringTokenizer st = new java.util.StringTokenizer(ps,",;"); + Proxy proxy = null; + while(st.hasMoreElements()){ + String entry = st.nextToken(); + Proxy p = Proxy.parseProxy(entry); + if( p == null){ + log("Proxy "+entry+" invalid."); + return null; + } + p.setChainProxy(proxy); + proxy = p; + } + return proxy; + } + static void addDirectHosts(Proxy p, String directHosts){ + java.util.StringTokenizer st = new java.util.StringTokenizer( + directHosts,",;"); + + while(st.hasMoreElements()){ + String entry = st.nextToken(); + log("Adding direct host:"+entry); + p.addDirect(entry); + } + } + + public static void main(String[] argv){ + if(argv.length < 2){ + usage(); + return; + } + + log = System.out; + + String testHost = argv[0]; + String proxyHost = argv[1]; + String directHosts = argv.length >2 ? argv[2] : null; + + Proxy p = initProxy(proxyHost); + if(p == null){ + log("Can't init proxy."); + return; + } + if(directHosts!=null) addDirectHosts(p,directHosts); + + if(p instanceof Socks5Proxy) + ((Socks5Proxy) p).resolveAddrLocally(false); + + TestClient tc = new TestClient(p,testHost); + tc.start(); + + } +} diff --git a/asocks/src/net/sourceforge/jsocks/test/TestServer.java b/asocks/src/net/sourceforge/jsocks/test/TestServer.java new file mode 100644 index 00000000..de825ebf --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/test/TestServer.java @@ -0,0 +1,83 @@ +package net.sourceforge.jsocks.test; + +import java.io.*; +import java.net.ServerSocket; +import java.net.Socket; + +import net.*; + +/** + Server to used perform tests for SOCKS library. +*/ +public class TestServer implements Runnable{ + static PrintStream log = null; + + int service; + + + /** + Creates a TestServer object which will listen on the port associated + with given service. + + @param service Service to provide + */ + public TestServer(int service){ + this.service = service; + } + + public void run(){ + try{ + server(service); + }catch(IOException ioe){ + log("Exception:"+ioe); + ioe.printStackTrace(); + } + } + //Static functions + ///////////////// + + + /** + Listens on the port associated with given service. +

+ When connection is accepted, speciefied service is performed. + It is being done in separate thread. + @return Never returns. + */ + static public void server(int service) throws IOException{ + ServerSocket ss = new ServerSocket(TestService.servicePorts[service]); + Socket s; + + s = ss.accept(); + while(s!=null){ + TestService st = new TestService(s,service); + Thread t = new Thread(st); + t.start(); + s = ss.accept(); + } + } + + + /** + Performs logging. + */ + static synchronized void log(String s){ + if(log != null) log.println(s); + } + + //Main Function + /////////////// + public static void main(String[] args){ + log = System.out; + TestService.log = log; + + TestServer st; + for( int i = 0; i< TestService.serviceNames.length;++i){ + log("Starting service "+TestService.serviceNames[i]+" at port "+ + TestService.servicePorts[i]+"."); + st = new TestServer(i); + Thread t = new Thread(st); + t.start(); + } + } +} diff --git a/asocks/src/net/sourceforge/jsocks/test/TestService.java b/asocks/src/net/sourceforge/jsocks/test/TestService.java new file mode 100644 index 00000000..8360f18f --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/test/TestService.java @@ -0,0 +1,275 @@ +package net.sourceforge.jsocks.test; + +import java.io.*; +import java.net.Socket; + +import net.*; + +/** + Server to used perform tests for SOCKS library. +*/ +public class TestService implements Runnable{ + static final String chargenSequence = " !\"#$%&'()*+,-./0123456789:;<=>?@"+ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefg"; + + static final String serviceNames[] = {"echo","discard","chargen","connect"}; + static final int servicePorts[] = {5678,5679,5680,5681}; + + static final int ECHO = 0; + static final int DISCARD = 1; + static final int CHARGEN = 2; + static final int CONNECT = 3; + + static final int BUF_SIZE = 1024; + + static final int CHARGEN_WAIT = 1000; //1 second + static final int MAX_WAIT = 60000; //1 minute + + static PrintStream log = null; + + //Class constants + Socket s; + int service; + + /** + Creates new TestService object, which will perform particular + service on given socket. + + @param s Socket on which to perform service. + @param service Service which to provide. + */ + public TestService(Socket s, int service){ + this.s = s; + this.service = service; + } + + /** + Default constructor. + */ + public TestService(){ + this.s = null; + this.service = -1; + } + + public void run(){ + try{ + serve(s,service); + }catch(IOException ioe){ + log("Exception:"+ioe); + ioe.printStackTrace(); + } + try{ s.close();}catch(IOException ioe){} + } + + //Static functions + ///////////////// + + /** + Maps service name to the integer id, does it in simple + linear search manner. + @param serviceName Name of the service whose id one needs. + @return Integer identifier for this servuce, or -1, if service + can't be found. + */ + static public int getServiceId(String serviceName){ + serviceName = serviceName.toLowerCase(); + for(int i = 0;i < serviceNames.length;++i) + if(serviceName.equals(serviceNames[i])) + return i; + + //Couldn't find one. + return -1; + } + + /** + Performs given service on given socket. +

+ Simply looks up and calls associated method. + @param s Socket on which to perform service. + @param service Id of the service to perform. + @return true if service have been found, false otherwise. + */ + static public boolean serve(Socket s, int service) throws IOException{ + switch(service){ + case ECHO: + echo(s); + break; + case DISCARD: + discard(s); + break; + case CHARGEN: + chargen(s,CHARGEN_WAIT,MAX_WAIT); + break; + case CONNECT: + connect(s); + break; + default: + log("Unknown service:"+service); + return false; + } + return true; + } + /** + Echos any input on the socket to the output. + Echo is being done line by line. + @param s Socket on which to perform service. + */ + static public void echo(Socket s) throws IOException{ + BufferedReader in = new BufferedReader(new InputStreamReader( + s.getInputStream())); + OutputStream out = s.getOutputStream(); + + log("Starting \"echo\" on "+s); + + String line = in.readLine(); + while(line != null){ + out.write((line+"\n").getBytes()); + log(line); + line = in.readLine(); + } + + log("Echo done."); + } + + /** + Reads input from the socket, and does not write anything back. + logs input in line by line fashion. + @param s Socket on which to perform service. + */ + static public void discard(Socket s) throws IOException{ + BufferedReader in = new BufferedReader(new InputStreamReader( + s.getInputStream())); + log("Starting discard on "+s); + + String line = in.readLine(); + while(line != null){ + log(line); + line = in.readLine(); + } + + log("Discard finished."); + } + + /** + Generates characters and sends them to the socket. +

+ Unlike usual chargen (port 19), each next line of the generated + output is send after increasingly larger time intervals. It starts + from wait_time (ms), and each next time wait time is doubled. + Eg. 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 ... well + you got the idea. +

+ It starts if either connection is clsoed or the wait time grows + bigger than max_wait. + + @param s Socket on which to perform service. + @param wait_time Time in ms, from which timing sequence should begin. + @param max_wait Time in ms, after reaching timeout greater than this + value, chargen will stop. + */ + static public void chargen(Socket s,long wait_time,long max_wait) + throws IOException{ + byte[] buf = chargenSequence.getBytes(); + int pos = 0; + OutputStream out = s.getOutputStream(); + InputStream in = s.getInputStream(); + s.setSoTimeout(100); //0.1 ms + + log("Starting \"chargen\" on "+s); + while(true){ + log("Sending message."); + out.write(buf,pos,buf.length - pos); + out.write(buf,0,pos); + out.write("\n".getBytes()); + pos++; + try{ + if(wait_time > max_wait) break; + + log("Going to sleep for "+wait_time+" ms."); + Thread.currentThread().sleep(wait_time); + wait_time *= 2; + if(in.read() < 0) break; //Connection closed + }catch(InterruptedException ie){ + }catch(InterruptedIOException ioe){ + } + } + log("Chargen finished."); + } + + /** + Models connect back situation. +

+ Reads a line from the socket connection, line should be in the + form port service_id. Connects back to the remote host to port + specified in the line, if successful performs a service speciefied + by id on that new connection. If accept was successful closes the + control connection, else outputs error message, and then closes + the connection. + + @param s Control connection. + */ + static public void connect(Socket s)throws IOException{ + String line = null; + Socket sock; + int port; + int service_id; + + BufferedReader in = new BufferedReader(new InputStreamReader( + s.getInputStream())); + OutputStream out = s.getOutputStream(); + + log("Starting \"connect\" on "+s); + line = in.readLine(); + if(line == null) return; //They closed connection + + java.util.StringTokenizer st = new java.util.StringTokenizer(line); + if(st.countTokens() < 2){ //We need at least 'port' and "id" + out.write("Expect: port serviceId.\n".getBytes()); + log("Invalid arguments."); + return; + } + try{ + port = Integer.parseInt(st.nextToken()); + service_id = Integer.parseInt(st.nextToken()); + }catch(NumberFormatException nfe){ + out.write("Expect: port serviceId.\n".getBytes()); + log("Invalid arguments."); + return; + } + try{ + log("Connecting to "+s.getInetAddress()+":"+port); + sock = new Socket(s.getInetAddress(),port); + }catch(IOException ioe){ + out.write(("Connect to "+s.getInetAddress()+ + ":"+port+" failed").getBytes()); + log("Connect failed."); + return; + } + s.close(); + log("About to serve "+service_id); + serve(sock,service_id); + } + + /** + Pipes data from the input stream to the output. + @param in Input stream. + @param out Output stream. + */ + static public void pipe(InputStream in, OutputStream out) + throws IOException{ + byte[] buf = new byte[BUF_SIZE]; + int bread = 0; + while(bread >= 0){ + bread = in.read(buf); + out.write(buf,0,bread); + } + } + + /** + Performs logging. + */ + static synchronized void log(String s){ + if(log != null) log.println(s); + } + +} diff --git a/asocks/src/net/sourceforge/jsocks/test/UDPEcho.java b/asocks/src/net/sourceforge/jsocks/test/UDPEcho.java new file mode 100644 index 00000000..ea0c1f1d --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/test/UDPEcho.java @@ -0,0 +1,151 @@ +package net.sourceforge.jsocks.test; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.io.*; + +import net.*; +/** + Plain SOCKS unaware UDP echo server and client. +*/ +public class UDPEcho implements Runnable{ + + private int port; + private InetAddress hostIP; + private DatagramSocket sock; + + private static final int BUF_SIZE = 1024; + + + public UDPEcho(String host,int port) + throws IOException,UnknownHostException{ + this.hostIP = InetAddress.getByName(host); + this.port = port; + sock = new DatagramSocket(); + System.out.println("UDP: "+sock.getLocalAddress()+":"+ + sock.getLocalPort()); + //sock.connect(hostIP,port); + } + + public void send(String s) throws IOException{ + System.out.println("Sending:"+s); + DatagramPacket packet = new DatagramPacket(s.getBytes(), + s.length(), + hostIP, + port); + sock.send(packet); + } + + public void run(){ + byte[] buf = new byte[BUF_SIZE]; + DatagramPacket incomingData = new DatagramPacket(buf,buf.length); + try{ + while(true){ + sock.receive(incomingData); + System.out.println("UDP From:"+ + incomingData.getAddress().getHostAddress()+":"+ + incomingData.getPort()); + System.out.println(new String(incomingData.getData(), + 0,incomingData.getLength())); + System.out.flush(); + incomingData.setLength(buf.length); + } + }catch(IOException io_ex){ + io_ex.printStackTrace(); + } + } + + public static void usage(){ + System.err.print( + "Usage: java UDPEcho host port\n"+ + "OR\n"+ + "Usage: java UDPEcho port\n"); + } + + public static void doEcho(int port)throws IOException{ + byte[] buf = new byte[BUF_SIZE]; + DatagramPacket packet = new DatagramPacket(buf,buf.length); + DatagramSocket sock = new DatagramSocket(port); + + System.out.println("Starting UDP echo on"+ + sock.getLocalAddress().getHostAddress()+ + ":"+sock.getLocalPort()); + while(true){ + try{ + sock.receive(packet); + sock.send(packet); + System.out.print( + "UDP From: "+packet.getAddress().getHostAddress()+":"+ + packet.getPort()+ + "\n"+ + new String(packet.getData(),0,packet.getLength())+ + "\n" + ); + System.out.flush(); + + packet.setLength(buf.length); + //packet = new DatagramPacket(buf,buf.length); + }catch(IOException io_ex){ + } + } + } + + public static void main(String args[]){ + int port; + String host; + + if(args.length == 1){ + try{ + port = Integer.parseInt(args[0]); + + doEcho(port); + + }catch(IOException io_ex){ + io_ex.printStackTrace(); + System.exit(1); + + }catch(NumberFormatException num_ex){ + num_ex.printStackTrace(); + System.exit(1); + } + }else if(args.length == 2){ + try{ + host = args[0]; + port = Integer.parseInt(args[1]); + + UDPEcho ut = new UDPEcho(host,port); + Thread thread = new Thread(ut); + thread.start(); + + BufferedReader in = new BufferedReader( + new InputStreamReader(System.in)); + String s; + System.out.print("Enter datagram:"); + s = in.readLine(); + while(s != null){ + ut.send(s); + try{ + Thread.currentThread().sleep(100); + }catch(InterruptedException i_ex){ + } + System.out.print("Enter datagram:"); + s = in.readLine(); + } + System.exit(1); + + }catch(IOException io_ex){ + io_ex.printStackTrace(); + System.exit(1); + }catch(NumberFormatException num_ex){ + num_ex.printStackTrace(); + System.exit(1); + } + + }else{ + usage(); + } + } + +}//End of class diff --git a/asocks/src/net/sourceforge/jsocks/test/UPSOCKS.java b/asocks/src/net/sourceforge/jsocks/test/UPSOCKS.java new file mode 100644 index 00000000..6b7002e8 --- /dev/null +++ b/asocks/src/net/sourceforge/jsocks/test/UPSOCKS.java @@ -0,0 +1,41 @@ +package net.sourceforge.jsocks.test; + +import net.sourceforge.jsocks.socks.*; +import net.sourceforge.jsocks.socks.server.*; + +import java.net.Socket; + +/** Test file for UserPasswordAuthentictor */ + +public class UPSOCKS implements UserValidation{ + String user, password; + + UPSOCKS(String user,String password){ + this.user = user; + this.password = password; + } + + public boolean isUserValid(String user,String password,Socket s){ + System.err.println("User:"+user+"\tPassword:"+password); + System.err.println("Socket:"+s); + return (user.equals(this.user) && password.equals(this.password)); + } + + public static void main(String args[]){ + String user, password; + + if(args.length == 2){ + user = args[0]; + password = args[1]; + }else{ + user = "user"; + password = "password"; + } + + UPSOCKS us = new UPSOCKS(user,password); + UserPasswordAuthenticator auth = new UserPasswordAuthenticator(us); + ProxyServer server = new ProxyServer(auth); + + server.start(1080); + } +}