changeset 349:205ee2873330

update jsocks to 2011-03-19
author Carl Byington <carl@five-ten-sg.com>
date Fri, 01 Aug 2014 11:23:10 -0700
parents 29076621bab0
children 72de889ecfe7
files TODO src/net/sourceforge/jsocks/Authentication.java src/net/sourceforge/jsocks/AuthenticationNone.java src/net/sourceforge/jsocks/CProxy.java src/net/sourceforge/jsocks/InetRange.java src/net/sourceforge/jsocks/Proxy.java src/net/sourceforge/jsocks/ProxyMessage.java src/net/sourceforge/jsocks/ProxyServer.java src/net/sourceforge/jsocks/Socks4Message.java src/net/sourceforge/jsocks/Socks4Proxy.java src/net/sourceforge/jsocks/Socks5DatagramSocket.java src/net/sourceforge/jsocks/Socks5Message.java src/net/sourceforge/jsocks/Socks5Proxy.java src/net/sourceforge/jsocks/SocksDialog.java src/net/sourceforge/jsocks/SocksException.java src/net/sourceforge/jsocks/SocksServerSocket.java src/net/sourceforge/jsocks/SocksSocket.java src/net/sourceforge/jsocks/UDPEncapsulation.java src/net/sourceforge/jsocks/UDPRelayServer.java src/net/sourceforge/jsocks/UserPasswordAuthentication.java src/net/sourceforge/jsocks/server/Ident.java src/net/sourceforge/jsocks/server/IdentAuthenticator.java src/net/sourceforge/jsocks/server/ServerAuthenticator.java src/net/sourceforge/jsocks/server/ServerAuthenticatorNone.java src/net/sourceforge/jsocks/server/UserPasswordAuthenticator.java src/net/sourceforge/jsocks/server/UserValidation.java
diffstat 26 files changed, 5242 insertions(+), 3604 deletions(-) [+]
line wrap: on
line diff
--- a/TODO	Fri Aug 01 10:25:44 2014 -0700
+++ b/TODO	Fri Aug 01 11:23:10 2014 -0700
@@ -51,8 +51,14 @@
 
 ==================================
 
+update jsocks to 2011-03-19 version
+
+==================================
+
 TODO:
 
+change all System.*.println -> android log.d() calls
+
 possible merge of irssi?
 
 https://github.com/irssiconnectbot/irssiconnectbot
--- a/src/net/sourceforge/jsocks/Authentication.java	Fri Aug 01 10:25:44 2014 -0700
+++ b/src/net/sourceforge/jsocks/Authentication.java	Fri Aug 01 11:23:10 2014 -0700
@@ -1,34 +1,34 @@
-package net.sourceforge.jsocks;
-
-/**
- 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.
-
-      <p>
-      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;
-}
+package net.sourceforge.jsocks;
+
+/**
+ 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.
+
+     <p>
+     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;
+}
--- a/src/net/sourceforge/jsocks/AuthenticationNone.java	Fri Aug 01 10:25:44 2014 -0700
+++ b/src/net/sourceforge/jsocks/AuthenticationNone.java	Fri Aug 01 11:23:10 2014 -0700
@@ -1,17 +1,17 @@
-package net.sourceforge.jsocks;
-
-/**
-  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()
-                            };
-    }
-}
+package net.sourceforge.jsocks;
+
+/**
+  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()};
+   }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/sourceforge/jsocks/CProxy.java	Fri Aug 01 11:23:10 2014 -0700
@@ -0,0 +1,488 @@
+package net.sourceforge.jsocks;
+import java.net.*;
+import java.io.*;
+import java.util.Hashtable;
+import java.util.Enumeration;
+
+/**
+  Abstract class CProxy, base for classes Socks4Proxy and Socks5Proxy.
+  Defines methods for specifying default proxy, to be
+  used by all classes of this package.
+*/
+
+public abstract class CProxy{
+
+//Data members
+   protected InetRange directHosts = new InetRange();
+
+   protected InetAddress proxyIP = null;
+   protected String proxyHost = null;
+   protected int proxyPort;
+   protected Socket proxySocket = null;
+
+   protected InputStream in;
+   protected OutputStream out;
+
+   protected int version;
+
+   protected CProxy chainProxy = null;
+
+
+//Protected static/class variables
+   protected static CProxy defaultProxy = null;
+
+//Constructors
+//====================
+   CProxy(CProxy chainProxy,
+         String proxyHost,int proxyPort)throws UnknownHostException{ 
+      this.chainProxy = chainProxy;
+      this.proxyHost = proxyHost;
+
+      if(chainProxy == null)
+         this.proxyIP   = InetAddress.getByName(proxyHost);
+      
+      this.proxyPort = proxyPort;
+   }
+
+  
+   CProxy(String proxyHost,int proxyPort)throws UnknownHostException{
+      this(null,proxyHost,proxyPort);
+   }
+
+   CProxy(CProxy chainProxy,InetAddress proxyIP,int proxyPort){
+      this.chainProxy = chainProxy;
+      this.proxyIP = proxyIP;
+      this.proxyPort = proxyPort;
+   }
+
+   CProxy(InetAddress proxyIP,int proxyPort){
+      this(null,proxyIP,proxyPort);
+   }
+
+   CProxy(CProxy p){
+      this.proxyIP = p.proxyIP;
+      this.proxyPort = p.proxyPort;
+      this.version = p.version;
+      this.directHosts = p.directHosts;
+   }
+
+//Public instance methods
+//========================
+
+   /**
+      Get the port on which proxy server is running.
+    * @return CProxy port.
+    */
+   public int getPort(){
+     return proxyPort;
+   }
+   /**
+      Get the ip address of the proxy server host.
+    * @return CProxy InetAddress.
+    */
+   public InetAddress getInetAddress(){
+     return proxyIP;
+   }
+   /**
+    * Adds given ip to the list of direct addresses.
+    * This machine will be accessed without using proxy.
+    */
+   public void addDirect(InetAddress ip){
+      directHosts.add(ip);
+   }
+   /**
+    * Adds host to the list of direct addresses.
+    * This machine will be accessed without using proxy.
+    */
+   public boolean addDirect(String host){
+      return directHosts.add(host);
+   }
+   /**
+    * Adds given range of addresses to the lsit of direct addresses,
+    * machines within this range will be accessed without using proxy.
+    */
+   public void addDirect(InetAddress from,InetAddress to){
+      directHosts.add(from,to);
+   }
+   /**
+    * Sets given InetRange as the list of direct address, previous
+    * list will be discarded, any changes done previously with
+    * addDirect(Inetaddress) will be lost.
+    * The machines in this range will be accessed without using proxy.
+    * @param ir InetRange which should be used to look up direct addresses.
+    * @see InetRange
+    */
+   public void setDirect(InetRange ir){
+     directHosts = ir;
+   }
+
+   /**
+      Get the list of direct hosts.
+    * @return Current range of direct address as InetRange object.
+    * @see InetRange
+    */
+   public InetRange getDirect(){
+     return directHosts;
+   }
+   /**
+      Check wether the given host is on the list of direct address.
+      @param host Host name to check.
+    * @return true if the given host is specified as the direct addresses.
+    */
+    public boolean isDirect(String host){
+        return directHosts.contains(host);
+    }
+   /**
+      Check wether the given host is on the list of direct addresses.
+      @param host Host address to check.
+    * @return true if the given host is specified as the direct address.
+    */
+    public boolean isDirect(InetAddress host){
+        return directHosts.contains(host);
+    }
+    /**
+      Set the proxy which should be used to connect to given proxy.
+      @param chainProxy CProxy to use to connect to this proxy.
+    */
+    public void setChainProxy(CProxy chainProxy){
+        this.chainProxy = chainProxy;
+    }
+    
+    /**
+      Get proxy which is used to connect to this proxy.
+      @return CProxy which is used to connect to this proxy, or null
+              if proxy is to be contacted directly.
+    */
+    public CProxy getChainProxy(){
+       return chainProxy;
+    }
+
+    /**
+       Get string representation of this proxy.
+     * @returns string in the form:proxyHost:proxyPort \t Version versionNumber
+     */
+    public String toString(){
+       return (""+proxyIP.getHostName()+":"+proxyPort+"\tVersion "+version);
+    }
+
+
+//Public Static(Class) Methods
+//==============================
+
+   /**
+    * Sets SOCKS4 proxy as default.
+      @param hostName Host name on which SOCKS4 server is running.
+      @param port Port on which SOCKS4 server is running.
+      @param user Username to use for communications with proxy.
+    */
+   public static void setDefaultProxy(String hostName,int port,String user)
+                             throws UnknownHostException{
+      defaultProxy = new Socks4Proxy(hostName,port,user);
+   }
+
+   /**
+    * Sets SOCKS4 proxy as default.
+      @param ipAddress Host address on which SOCKS4 server is running.
+      @param port Port on which SOCKS4 server is running.
+      @param user Username to use for communications with proxy.
+    */
+   public static void setDefaultProxy(InetAddress ipAddress,int port,
+                                      String user){
+      defaultProxy = new Socks4Proxy(ipAddress,port,user);
+   }
+   /**
+    * Sets SOCKS5 proxy as default.
+    * Default proxy only supports no-authentication.
+      @param hostName Host name on which SOCKS5 server is running.
+      @param port Port on which SOCKS5 server is running.
+    */
+   public static void setDefaultProxy(String hostName,int port)
+                             throws UnknownHostException{
+      defaultProxy = new Socks5Proxy(hostName,port);
+   }
+   /**
+    * Sets SOCKS5 proxy as default.
+    * Default proxy only supports no-authentication.
+      @param ipAddress Host address on which SOCKS5 server is running.
+      @param port Port on which SOCKS5 server is running.
+    */
+   public static void setDefaultProxy(InetAddress ipAddress,int port){
+      defaultProxy = new Socks5Proxy(ipAddress,port);
+   }
+   /**
+    * Sets default proxy.
+      @param p CProxy to use as default proxy.
+    */
+   public static void setDefaultProxy(CProxy p){
+     defaultProxy = p;
+   }
+
+   /**
+      Get current default proxy.
+    * @return Current default proxy, or null if none is set.
+    */
+   public static CProxy getDefaultProxy(){
+     return defaultProxy;
+   }
+
+   /**
+     Parses strings in the form: host[:port:user:password], and creates
+     proxy from information obtained from parsing.
+     <p>
+     Defaults: port = 1080.<br>
+     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 CProxy created from the string, null if entry was somehow
+             invalid(host unknown for example, or empty string)
+   */
+   public static CProxy parseProxy(String proxy_entry){
+
+      String proxy_host;
+      int proxy_port = 1080;
+      String proxy_user = null;
+      String proxy_password = null;
+      CProxy 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 CProxy 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_BADCONNECT		=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;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/sourceforge/jsocks/InetRange.java	Fri Aug 01 11:23:10 2014 -0700
@@ -0,0 +1,440 @@
+package net.sourceforge.jsocks;
+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.
+ * <P>
+ * 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:
+        <UL>
+        <li> Host name. eg.(Athena.myhost.com or 45.54.56.65)
+        
+        <li> Range in the form .myhost.net.au <BR>
+             In which case anything that ends with .myhost.net.au will
+             be considered in the range.
+
+        <li> Range in the form ddd.ddd.ddd. <BR>
+             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.
+
+        <li> Range in the form host_from[: \t\n\r\f]host_to. <br>
+             That is two hostnames or ips separated by either whitespace 
+             or colon.
+        </UL>
+     */
+    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.
+     *  <P>
+     *  Algorithm: <BR>
+     *  <ol>
+     *  <li>Look up if the hostname is in the range (in the Hashtable).
+     *  <li>Check if it ends with one of the speciefied endings.
+     *  <li>Check if it is ip(eg.130.220.35.98). If it is check if it is
+     *      in the range.
+     *  <li>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.
+     *  </ol>
+       @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. <BR>
+       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<size;++i){
+          entry = (Object[]) all.elementAt(i);
+          all_names[i] = (String) entry[0];
+       }
+       return all_names;
+    }
+    /**
+      Removes an entry from this range.<BR>
+      @param s Entry to remove.
+      @return true if successfull.
+     */
+    public synchronized boolean remove(String s){
+      Enumeration eEnum = all.elements();
+      while(eEnum.hasMoreElements()){
+        Object[] entry = (Object[]) eEnum.nextElement();
+        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<all.length;++i)
+          s += "; "+all[i];
+       return s;
+    }
+
+    /** Creates a clone of this Object*/
+    public Object clone(){
+      InetRange new_range = new InetRange();
+      new_range.all = (Vector)all.clone();
+      new_range.end_names = (Vector) end_names.clone();
+      new_range.host_names = (Hashtable)host_names.clone();
+      return new_range;
+    }
+
+
+//Private methods
+/////////////////
+    /**
+     * Same as previous but used internally, to avoid
+     * unnecessary convertion of IPs, when checking subranges
+     */
+    private synchronized boolean contains(long ip){
+       Enumeration eEnum = all.elements();
+       while(eEnum.hasMoreElements()){
+         Object[] obj = (Object[]) eEnum.nextElement();
+         Long from = obj[2]==null?null:(Long)obj[2];
+         Long to   = obj[3]==null?null:(Long)obj[3];
+         if(from != null && from.longValue()<= ip 
+                         && to.longValue() >= ip) return true;
+
+       }
+       return false;
+    }
+
+    private boolean checkHost(String host){
+       return host_names.containsKey(host);
+    }
+    private boolean checkHostEnding(String host){
+       Enumeration eEnum = end_names.elements();
+       while(eEnum.hasMoreElements()){
+          if(host.endsWith((String) eEnum.nextElement())) 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<addr.length;++i)
+          ip += ((long)(addr[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<args.length;++i){
+         System.out.println("Adding:" + args[i]);
+         ir.add(args[i]);
+       }
+
+       String host;
+       java.io.DataInputStream din = new java.io.DataInputStream(System.in);
+       try{
+          host = din.readLine();
+          while(host!=null){
+            if(ir.contains(host)){
+              System.out.println("Range contains ip:"+host);
+            }else{
+              System.out.println(host+" is not in the range");
+            }
+            host = din.readLine();
+          }
+       }catch(java.io.IOException io_ex){
+          io_ex.printStackTrace();
+       }
+    }
+********************/
+
+}
+
+
+class InetRangeResolver implements Runnable{
+
+   Object[] entry;
+
+   String from, to;
+
+   InetRangeResolver(Object[] entry){
+      this.entry = entry;
+      from = to = null;
+   }
+   InetRangeResolver(Object[] entry,String from,String to){
+      this.entry = entry;
+      this.from  = from;
+      this.to    = to;
+   }
+   public final void resolve(){
+     resolve(true);
+   }
+   public final void resolve(boolean inSeparateThread){
+     if(inSeparateThread){
+       Thread t = new Thread(this);
+       t.start();
+     }else
+       run();
+
+   }
+   public void run(){
+     try{
+        if(from == null){
+          InetAddress ip = InetAddress.getByName((String) entry[0]);
+          entry[1] = ip;
+          Long l = new Long(InetRange.ip2long(ip));
+          entry[2] = entry[3] = l;
+        }else{
+          InetAddress f = InetAddress.getByName(from);
+          InetAddress t = InetAddress.getByName(to);
+          entry[2] = new Long(InetRange.ip2long(f));
+          entry[3] = new Long(InetRange.ip2long(t));
+
+        }
+     }catch(UnknownHostException uhe){
+        //System.err.println("Resolve failed for "+from+','+to+','+entry[0]);
+     }
+   }
+
+}
--- a/src/net/sourceforge/jsocks/Proxy.java	Fri Aug 01 10:25:44 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,428 +0,0 @@
-package net.sourceforge.jsocks;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.UnknownHostException;
-
-/**
-  Abstract class Proxy, base for classes Socks4Proxy and Socks5Proxy.
-  Defines methods for specifying default proxy, to be
-  used by all classes of this package.
-*/
-
-public abstract class Proxy {
-
-//Data members
-    //protected InetRange directHosts = new InetRange();
-
-    protected InetAddress proxyIP = null;
-    protected String proxyHost = null;
-    protected int proxyPort;
-    protected Socket proxySocket = null;
-
-    protected InputStream in;
-    protected OutputStream out;
-
-    protected int version;
-
-    protected Proxy chainProxy = null;
-
-
-//Protected static/class variables
-    protected static Proxy defaultProxy = null;
-
-//Constructors
-//====================
-    Proxy(String proxyHost, int proxyPort) throws UnknownHostException {
-        this.proxyHost = proxyHost;
-        this.proxyIP = InetAddress.getByName(proxyHost);
-        this.proxyPort = proxyPort;
-    }
-
-    Proxy(Proxy chainProxy, InetAddress proxyIP, int proxyPort) {
-        this.chainProxy = chainProxy;
-        this.proxyIP = proxyIP;
-        this.proxyPort = proxyPort;
-    }
-
-    Proxy(InetAddress proxyIP, int proxyPort) {
-        this.proxyIP = proxyIP;
-        this.proxyPort = proxyPort;
-    }
-
-    Proxy(Proxy p) {
-        this.proxyIP = p.proxyIP;
-        this.proxyPort = p.proxyPort;
-        this.version = p.version;
-    }
-
-//Public instance methods
-//========================
-
-    /**
-       Get the port on which proxy server is running.
-     * @return Proxy port.
-     */
-    public int getPort() {
-        return proxyPort;
-    }
-    /**
-       Get the ip address of the proxy server host.
-     * @return Proxy InetAddress.
-     */
-    public InetAddress getInetAddress() {
-        return proxyIP;
-    }
-
-    /**
-       Get string representation of this proxy.
-     * @returns string in the form:proxyHost:proxyPort \t Version versionNumber
-     */
-    public String toString() {
-        return ("" + proxyIP.getHostName() + ":" + proxyPort + "\tVersion " + version);
-    }
-
-
-//Public Static(Class) Methods
-//==============================
-
-    /**
-     * Sets SOCKS4 proxy as default.
-       @param hostName Host name on which SOCKS4 server is running.
-       @param port Port on which SOCKS4 server is running.
-       @param user Username to use for communications with proxy.
-     */
-    public static void setDefaultProxy(String hostName, int port, String user)
-    throws UnknownHostException {
-        defaultProxy = new Socks4Proxy(hostName, port, user);
-    }
-
-    /**
-     * Sets SOCKS4 proxy as default.
-       @param ipAddress Host address on which SOCKS4 server is running.
-       @param port Port on which SOCKS4 server is running.
-       @param user Username to use for communications with proxy.
-     */
-    public static void setDefaultProxy(InetAddress ipAddress, int port,
-                                       String user) {
-        defaultProxy = new Socks4Proxy(ipAddress, port, user);
-    }
-    /**
-     * Sets SOCKS5 proxy as default.
-     * Default proxy only supports no-authentication.
-       @param hostName Host name on which SOCKS5 server is running.
-       @param port Port on which SOCKS5 server is running.
-     */
-    public static void setDefaultProxy(String hostName, int port)
-    throws UnknownHostException {
-        defaultProxy = new Socks5Proxy(hostName, port);
-    }
-    /**
-     * Sets SOCKS5 proxy as default.
-     * Default proxy only supports no-authentication.
-       @param ipAddress Host address on which SOCKS5 server is running.
-       @param port Port on which SOCKS5 server is running.
-     */
-    public static void setDefaultProxy(InetAddress ipAddress, int port) {
-        defaultProxy = new Socks5Proxy(ipAddress, port);
-    }
-    /**
-     * Sets default proxy.
-       @param p Proxy to use as default proxy.
-     */
-    public static void setDefaultProxy(Proxy p) {
-        defaultProxy = p;
-    }
-
-    /**
-       Get current default proxy.
-     * @return Current default proxy, or null if none is set.
-     */
-    public static Proxy getDefaultProxy() {
-        return defaultProxy;
-    }
-
-    /**
-      Parses strings in the form: host[:port:user:password], and creates
-      proxy from information obtained from parsing.
-      <p>
-      Defaults: port = 1080.<br>
-      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 {
-            proxySocket = new Socket(proxyIP, 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_BADCONNECT     = 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;
-
-
-    public static final int SOCKS_CMD_CONNECT        = 0x1;
-    static final int SOCKS_CMD_BIND      = 0x2;
-    static final int SOCKS_CMD_UDP_ASSOCIATE = 0x3;
-
-}
--- a/src/net/sourceforge/jsocks/ProxyMessage.java	Fri Aug 01 10:25:44 2014 -0700
+++ b/src/net/sourceforge/jsocks/ProxyMessage.java	Fri Aug 01 11:23:10 2014 -0700
@@ -1,111 +1,110 @@
-package net.sourceforge.jsocks;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-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;
-
-    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" ;
-    }
-
-//Package methods
-//////////////////
-
-    static final String bytes2IPV4(byte[] addr, int offset) {
-        String hostName = "" + (addr[offset] & 0xFF);
-
-        for (int i = offset + 1; i < offset + 4; ++i)
-            hostName += "." + (addr[i] & 0xFF);
-
-        return hostName;
-    }
-
-    static final String bytes2IPV6(byte[] addr, int offset) {
-        //Have no idea how they look like!
-        return null;
-    }
-
-}
+package net.sourceforge.jsocks;
+
+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;
+
+   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" ;
+   }
+
+//Package methods
+//////////////////
+
+   static final String bytes2IPV4(byte[] addr,int offset){
+      String hostName = ""+(addr[offset] & 0xFF);
+      for(int i = offset+1;i<offset+4;++i)
+        hostName+="."+(addr[i] & 0xFF);
+      return hostName;
+   }
+
+   static final String bytes2IPV6(byte[] addr,int offset){
+     //Have no idea how they look like!
+     return null;
+   }
+
+}
--- a/src/net/sourceforge/jsocks/ProxyServer.java	Fri Aug 01 10:25:44 2014 -0700
+++ b/src/net/sourceforge/jsocks/ProxyServer.java	Fri Aug 01 11:23:10 2014 -0700
@@ -1,639 +1,604 @@
-package net.sourceforge.jsocks;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.io.PushbackInputStream;
-import java.net.ConnectException;
-import java.net.InetAddress;
-import java.net.NoRouteToHostException;
-import java.net.ServerSocket;
-import java.net.Socket;
-
-import net.sourceforge.jsocks.server.ServerAuthenticator;
-
-/**
-    SOCKS4 and SOCKS5 proxy, handles both protocols simultaniously.
-    Implements all SOCKS commands, including UDP relaying.
-    <p>
-    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;
-
-    protected static int iddleTimeout    = 180000; //3 minutes
-    static int acceptTimeout = 180000; //3 minutes
-
-    static PrintStream log = null;
-    static Proxy proxy;
-
-
-//Public Constructors
-/////////////////////
-
-
-    /**
-     Creates a proxy server with given Authentication scheme.
-     @param auth Authentication scheme to be used.
-     */
-    public ProxyServer(ServerAuthenticator auth) {
-        this.auth = auth;
-    }
-
-//Other constructors
-////////////////////
-
-    protected ProxyServer(ServerAuthenticator auth, Socket s) {
-        this.auth  = auth;
-        this.sock  = s;
-        mode = START_MODE;
-    }
-
-//Public methods
-/////////////////
-
-    /**
-      Set the logging stream. Specifying null disables logging.
-    */
-    public static void setLog(OutputStream out) {
-        if (out == null) {
-            log = null;
-        }
-        else {
-            log = new PrintStream(out, true);
-        }
-
-        UDPRelayServer.log = log;
-    }
-
-    /**
-     Set proxy.
-     <p>
-     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.<br>
-     Zero timeout implies infinity.<br>
-     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.<br>
-     Zero timeout implies infinity.<br>
-     Default timeout is 3 minutes.
-    */
-    public static void setAcceptTimeout(int timeout) {
-        acceptTimeout = timeout;
-    }
-
-    /**
-     Sets the timeout for UDPRelay server.<br>
-     Zero timeout implies infinity.<br>
-     Default timeout is 3 minutes.
-    */
-    public static void setUDPTimeout(int timeout) {
-        UDPRelayServer.setTimeout(timeout);
-    }
-
-    /**
-      Sets the size of the datagrams used in the UDPRelayServer.<br>
-      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.<br>
-      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. <br>
-      This methods blocks.
-     */
-    public void start(int port, int backlog, InetAddress localIP) {
-        try {
-            ss = new ServerSocket(port, backlog, localIP);
-            log("Starting SOCKS Proxy on:" + ss.getInetAddress().getHostAddress() + ":"
-                + ss.getLocalPort());
-
-            while (true) {
-                Socket s = ss.accept();
-                log("Accepted from:" + s.getInetAddress().getHostName() + ":"
-                    + s.getPort());
-                ProxyServer ps = new ProxyServer(auth, s);
-                (new Thread(ps)).start();
-            }
-        }
-        catch (IOException ioe) {
-            ioe.printStackTrace();
-        }
-        finally {
-        }
-    }
-
-    /**
-      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();
-
-                    log("Main thread(client->remote)stopped.");
-                }
-
-                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();
-                    log("Accept thread(remote->client) stopped");
-                }
-
-                break;
-
-            case PIPE_MODE:
-                try {
-                    pipe(remote_in, out);
-                }
-                catch (IOException ioe) {
-                }
-                finally {
-                    abort();
-                    log("Support thread(remote->client) stopped");
-                }
-
-                break;
-
-            case ABORT_MODE:
-                break;
-
-            default:
-                log("Unexpected MODE " + mode);
-        }
-    }
-
-//Private methods
-/////////////////
-    private void startSession() throws IOException {
-        sock.setSoTimeout(iddleTimeout);
-
-        try {
-            auth = auth.startSession(sock);
-        }
-        catch (IOException ioe) {
-            log("Auth throwed exception:" + ioe);
-            auth = null;
-            return;
-        }
-
-        if (auth == null) { //Authentication failed
-            log("Authentication failed");
-            return;
-        }
-
-        in = auth.getInputStream();
-        out = auth.getOutputStream();
-        msg = readMsg(in);
-        handleRequest(msg);
-    }
-
-    protected void handleRequest(ProxyMessage msg)
-    throws IOException {
-        if (!auth.checkRequest(msg)) throw new
-            SocksException(Proxy.SOCKS_FAILURE);
-
-        if (msg.ip == null) {
-            if (msg instanceof Socks5Message) {
-                msg.ip = InetAddress.getByName(msg.host);
-            }
-            else
-                throw new SocksException(Proxy.SOCKS_FAILURE);
-        }
-
-        log(msg);
-
-        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;
-        s = new Socket(msg.ip, msg.port);
-        log("Connected to " + s.getInetAddress() + ":" + s.getPort());
-
-        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);
-        log("Trying accept on " + ss.getInetAddress() + ":" + ss.getLocalPort());
-
-        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();
-
-        log("Creating UDP relay server for " + msg.ip + ":" + msg.port);
-        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);
-        log("Accepted from " + s.getInetAddress() + ":" + s.getPort());
-        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);
-    }
-
-    protected 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 {
-            log("Aborting operation");
-
-            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) {}
-    }
-
-    static final void log(String s) {
-        if (log != null) {
-            log.println(s);
-            log.flush();
-        }
-    }
-
-    static final void log(ProxyMessage msg) {
-        log("Request version:" + msg.version +
-            "\tCommand: " + command2String(msg.command));
-        log("IP:" + msg.ip + "\tPort:" + msg.port +
-            (msg.version == 4 ? "\tUser:" + msg.user : ""));
-    }
-
-    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;
-    }
-}
+package net.sourceforge.jsocks;
+import net.sourceforge.jsocks.server.ServerAuthenticator;
+import java.net.*;
+import java.io.*;
+
+/**
+    SOCKS4 and SOCKS5 proxy, handles both protocols simultaniously.
+    Implements all SOCKS commands, including UDP relaying.
+    <p>
+    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 PrintStream log = null;
+   static CProxy proxy;
+
+
+//Public Constructors
+/////////////////////
+
+
+   /**
+    Creates a proxy server with given Authentication scheme.
+    @param auth Authentication scheme to be used.
+    */
+   public ProxyServer(ServerAuthenticator auth){
+     this.auth = auth;
+   }
+
+//Other constructors
+////////////////////
+
+   ProxyServer(ServerAuthenticator auth,Socket s){
+      this.auth  = auth;
+      this.sock  = s;
+      mode = START_MODE;
+   }
+
+//Public methods
+/////////////////
+
+   /**
+     Set the logging stream. Specifying null disables logging.
+   */
+   public static void setLog(OutputStream out){
+      if(out == null){
+        log = null;
+      }else{
+        log = new PrintStream(out,true);
+      }
+
+      UDPRelayServer.log = log;
+   }
+   
+   /**
+    Set proxy.
+    <p>
+    Allows CProxy chaining so that one CProxy 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 CProxy which should be used to handle user requests.
+   */
+   public static void setProxy(CProxy p){
+      proxy =p;
+      UDPRelayServer.proxy = proxy;
+   }
+
+   /**
+    Get proxy.
+    @return CProxy wich is used to handle user requests.
+   */
+   public static CProxy getProxy(){
+      return proxy;
+   }
+
+   /**
+    Sets the timeout for connections, how long shoud server wait
+    for data to arrive before dropping the connection.<br>
+    Zero timeout implies infinity.<br>
+    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.<br>
+    Zero timeout implies infinity.<br>
+    Default timeout is 3 minutes.
+   */
+   public static void setAcceptTimeout(int timeout){
+      acceptTimeout = timeout;
+   }
+
+   /**
+    Sets the timeout for UDPRelay server.<br>
+    Zero timeout implies infinity.<br>
+    Default timeout is 3 minutes.
+   */
+   public static void setUDPTimeout(int timeout){
+      UDPRelayServer.setTimeout(timeout);
+   }
+
+   /**
+     Sets the size of the datagrams used in the UDPRelayServer.<br>
+     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 CProxy server at given port.<br>
+     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. <br>
+     This methods blocks. 
+    */
+   public void start(int port,int backlog,InetAddress localIP){
+      try{
+        ss = new ServerSocket(port,backlog,localIP);
+        log("Starting SOCKS Proxy on:"+ss.getInetAddress().getHostAddress()+":"
+                                      +ss.getLocalPort());
+        while(true){
+          Socket s = ss.accept();
+          log("Accepted from:"+s.getInetAddress().getHostName()+":"
+                              +s.getPort());
+          ProxyServer ps = new ProxyServer(auth,s);
+          (new Thread(ps)).start();
+        }
+      }catch(IOException ioe){
+        ioe.printStackTrace();
+      }finally{
+      }
+   }
+
+   /**
+     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();
+           log("Main thread(client->remote)stopped.");
+         }
+        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();
+            log("Accept thread(remote->client) stopped");
+          }
+        break;
+        case PIPE_MODE:
+         try{
+            pipe(remote_in,out);
+         }catch(IOException ioe){
+         }finally{
+            abort();
+            log("Support thread(remote->client) stopped");
+         }
+        break;
+        case ABORT_MODE:
+        break;
+        default:
+         log("Unexpected MODE "+mode);
+      }
+   }
+
+//Private methods
+/////////////////
+   private void startSession() throws IOException{
+     sock.setSoTimeout(iddleTimeout);
+
+     try{
+        auth = auth.startSession(sock);
+     }catch(IOException ioe){
+       log("Auth throwed exception:"+ioe);
+       auth = null;
+       return;
+     }
+
+     if(auth == null){ //Authentication failed
+        log("Authentication failed");
+        return;
+     }
+
+     in = auth.getInputStream();
+     out = auth.getOutputStream();
+
+     msg = readMsg(in);
+     handleRequest(msg);
+   }
+   
+   private void handleRequest(ProxyMessage msg)
+                throws IOException{
+      if(!auth.checkRequest(msg)) throw new 
+                                  SocksException(CProxy.SOCKS_FAILURE);
+
+      if(msg.ip == null){
+        if(msg instanceof Socks5Message){
+          msg.ip = InetAddress.getByName(msg.host);
+        }else
+          throw new SocksException(CProxy.SOCKS_FAILURE);
+      }
+      log(msg);
+
+      switch(msg.command){
+        case CProxy.SOCKS_CMD_CONNECT:
+          onConnect(msg);
+        break;
+        case CProxy.SOCKS_CMD_BIND:
+          onBind(msg);
+        break;
+        case CProxy.SOCKS_CMD_UDP_ASSOCIATE:
+          onUDP(msg);
+        break;
+        default:
+          throw new SocksException(CProxy.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 = CProxy.SOCKS_FAILURE;
+
+      if(ioe instanceof SocksException)
+          error_code = ((SocksException)ioe).errCode;
+      else if(ioe instanceof NoRouteToHostException)
+          error_code = CProxy.SOCKS_HOST_UNREACHABLE;
+      else if(ioe instanceof ConnectException)
+          error_code = CProxy.SOCKS_CONNECTION_REFUSED;
+      else if(ioe instanceof InterruptedIOException)
+          error_code = CProxy.SOCKS_TTL_EXPIRE;
+
+      if(error_code > CProxy.SOCKS_ADDR_NOT_SUPPORTED || error_code < 0){
+          error_code = CProxy.SOCKS_FAILURE;
+      }
+
+      sendErrorMessage(error_code);
+   }
+
+    private void onConnect(ProxyMessage msg) throws IOException {
+        Socket s = null;
+        ProxyMessage response = null;
+        int iSock5Cmd = CProxy.SOCKS_FAILURE;    //defaulting to failure
+        int iSock4Msg = Socks4Message.REPLY_NO_CONNECT;
+        InetAddress sIp = null; int iPort = 0;
+
+        try {
+            if (proxy == null) {
+                s = new Socket(msg.ip, msg.port);
+            } else {
+                s = new SocksSocket(proxy, msg.ip, msg.port);
+            }
+            log("Connected to " + s.getInetAddress() + ":" + s.getPort());
+
+            iSock5Cmd = CProxy.SOCKS_SUCCESS; iSock4Msg = Socks4Message.REPLY_OK;
+            sIp = s.getInetAddress(); iPort = s.getPort();
+
+        }
+        catch (Exception sE) {
+            log("Failed connecting to remote socket. Exception: " + sE.getLocalizedMessage());
+
+            //TBD Pick proper socks error for corresponding socket error, below is too generic
+            iSock5Cmd = CProxy.SOCKS_CONNECTION_REFUSED; iSock4Msg = Socks4Message.REPLY_NO_CONNECT;
+        }
+
+        if (msg instanceof Socks5Message) {
+            response = new Socks5Message(iSock5Cmd, sIp, iPort);
+        } else {
+            response = new Socks4Message(iSock4Msg, sIp, iPort);
+        }
+
+        response.write(out);
+
+        if (s != null) {
+            startPipe(s);
+        }
+        else {
+            throw (new RuntimeException("onConnect() Failed to create Socket()"));
+        }
+
+        return;
+    }
+
+
+   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);
+
+      log("Trying accept on "+ss.getInetAddress()+":"+ss.getLocalPort());
+
+      if(msg.version == 5)
+         response = new Socks5Message(CProxy.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();
+      log("Creating UDP relay server for "+msg.ip+":"+msg.port);
+      relayServer = new UDPRelayServer(msg.ip,msg.port,
+                        Thread.currentThread(),sock,auth);
+
+      ProxyMessage response;
+
+      response = new Socks5Message(CProxy.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(CProxy.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);
+
+      log("Accepted from "+s.getInetAddress()+":"+s.getPort());
+
+      ProxyMessage response;
+
+      if(msg.version == 5)
+         response = new Socks5Message(CProxy.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(CProxy.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{
+         log("Aborting operation");
+         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){}
+   }
+
+   static final void log(String s){
+     if(log != null){
+       log.println(s);
+       log.flush();
+     }
+   }
+
+   static final void log(ProxyMessage msg){
+      log("Request version:"+msg.version+
+          "\tCommand: "+command2String(msg.command));
+      log("IP:"+msg.ip +"\tPort:"+msg.port+
+         (msg.version==4?"\tUser:"+msg.user:""));
+   }
+
+   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;
+   }
+}
--- a/src/net/sourceforge/jsocks/Socks4Message.java	Fri Aug 01 10:25:44 2014 -0700
+++ b/src/net/sourceforge/jsocks/Socks4Message.java	Fri Aug 01 11:23:10 2014 -0700
@@ -1,182 +1,157 @@
-package net.sourceforge.jsocks;
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
-  SOCKS4 Reply/Request message.
-*/
-
-public 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);
-    }
-
-    @Override
-    public void read(InputStream in) throws IOException {
-        read(in, true);
-    }
-
-    @Override
-    public void read(InputStream in, boolean clientMode) throws IOException {
-        boolean mode4a = false;
-        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);
-
-        if (addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] != 0)
-            mode4a = true;
-        else {
-            ip = bytes2IP(addr);
-            host = ip.getHostName();
-        }
-
-        if (!clientMode) {
-            StringBuilder sb = new StringBuilder();
-            int b;
-
-            while ((b = in.read()) != 0)
-                sb.append((char) b);
-
-            user = sb.toString();
-
-            if (mode4a) {
-                sb.setLength(0);
-
-                while ((b = in.read()) != 0)
-                    sb.append((char) b);
-
-                host = sb.toString();
-            }
-        }
-    }
-    @Override
-    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;
-
-}
+package net.sourceforge.jsocks;
+import java.io.*;
+import java.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;i<userBytes.length && b>0;++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;
+
+}
--- a/src/net/sourceforge/jsocks/Socks4Proxy.java	Fri Aug 01 10:25:44 2014 -0700
+++ b/src/net/sourceforge/jsocks/Socks4Proxy.java	Fri Aug 01 11:23:10 2014 -0700
@@ -1,110 +1,121 @@
-package net.sourceforge.jsocks;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
-  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(String proxyHost, int proxyPort, String user)
-    throws UnknownHostException {
-        super(proxyHost, proxyPort);
-        this.user = new String(user);
-        version = 4;
-    }
-
-    /**
-      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.chainProxy = chainProxy;
-        return newProxy;
-    }
-
-
-//Public Static(Class) Methods
-//==============================
-
-
-//Protected Methods
-//=================
-
-    protected Proxy copy() {
-        Socks4Proxy copy = new Socks4Proxy(proxyIP, proxyPort, user);
-        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);
-    }
-
-}
+package net.sourceforge.jsocks;
+import java.net.*;
+import java.io.*;
+import java.util.Hashtable;
+import java.util.Enumeration;
+
+/**
+  CProxy which describes SOCKS4 proxy.
+*/
+
+public class Socks4Proxy extends CProxy implements Cloneable{
+
+//Data members
+   String user;
+
+//Public Constructors
+//====================
+
+   /**
+     Creates the SOCKS4 proxy
+     @param p CProxy 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(CProxy 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 CProxy 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(CProxy 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 CProxy 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);
+   }
+
+}
--- a/src/net/sourceforge/jsocks/Socks5DatagramSocket.java	Fri Aug 01 10:25:44 2014 -0700
+++ b/src/net/sourceforge/jsocks/Socks5DatagramSocket.java	Fri Aug 01 11:23:10 2014 -0700
@@ -1,462 +1,487 @@
-package net.sourceforge.jsocks;
-import java.net.*;
-import java.io.*;
-
-/**
-  Datagram socket to interract through the firewall.<BR>
-  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.
-   <p>
-   SOCKS5 protocol allows to send host address as either:
-   <ul>
-    <li> IPV4, normal 4 byte address. (10 bytes header size)
-    <li> IPV6, version 6 ip address (not supported by Java as for now).
-         22 bytes header size.
-    <li> Host name,(7+length of the host name bytes header size).
-   </ul>
-  As with other Socks equivalents, direct addresses are handled
-  transparently, that is data will be send directly when required
-  by the proxy settings.
-  <p>
-  <b>NOTE:</b><br>
-  Unlike other SOCKS Sockets, it <b>does not</b> 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:
-      <ol>
-       <li> Given version of proxy does not support UDP_ASSOCIATE.
-       <li> Proxy can't be reached.
-       <li> Authorization fails.
-       <li> Proxy does not want to perform udp forwarding, for any reason.
-      </ol>
-      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. <BR>
-
-      <B> NOTE: </B> DatagramPacket size should be at least 10 bytes less
-                     than the systems limit.
-
-      <P>
-      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) {
-            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.
-      <p>
-      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 {
-        dp.setAddress(InetAddress.getByName(host));
-        super.send(dp);
-    }
-
-    /**
-     * 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.<BR>
-     * If the packet arrived from anywhere else it is not changed.<br>
-     * <B> NOTE: </B> 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.
-      <p>
-      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.
-      <p>
-      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.
-      <p>
-      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.
-      <p>
-      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;
-    }
-
-
-    /*======================================================================
-
-    //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<b.length;++i){
-              int i1 = (b[i] & 0xF0) >> 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();
-          }
-       }
-    */
-
-}
+package net.sourceforge.jsocks;
+import java.net.*;
+import java.io.*;
+
+/**
+  Datagram socket to interract through the firewall.<BR>
+  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.
+   <p>
+   SOCKS5 protocol allows to send host address as either:
+   <ul>
+    <li> IPV4, normal 4 byte address. (10 bytes header size)
+    <li> IPV6, version 6 ip address (not supported by Java as for now).
+         22 bytes header size.
+    <li> Host name,(7+length of the host name bytes header size).
+   </ul>
+  As with other Socks equivalents, direct addresses are handled
+  transparently, that is data will be send directly when required 
+  by the proxy settings.
+  <p>
+  <b>NOTE:</b><br>
+  Unlike other SOCKS Sockets, it <b>does not</b> 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
+      CProxy.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(CProxy.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
+      CProxy.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(CProxy.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
+      CProxy.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(CProxy.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:
+     <ol>
+      <li> Given version of proxy does not support UDP_ASSOCIATE.
+      <li> CProxy can't be reached.
+      <li> Authorization fails.
+      <li> CProxy does not want to perform udp forwarding, for any reason.
+     </ol>
+     Might throw IOException if binding dtagram socket to given address/port
+     fails.
+     See java.net.DatagramSocket for more details.
+    */
+   public Socks5DatagramSocket(CProxy p,int port,InetAddress ip)
+                                        throws SocksException,
+                                               IOException{
+      super(port,ip);
+      if(p == null) throw new SocksException(CProxy.SOCKS_NO_PROXY);
+      if(!(p instanceof Socks5Proxy)) 
+         throw new SocksException(-1,"Datagram Socket needs Proxy version 5");
+
+      if(p.chainProxy != null)
+           throw new SocksException(CProxy.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. <BR>
+
+     <B> NOTE: </B> DatagramPacket size should be at least 10 bytes less
+                    than the systems limit.
+
+     <P>
+     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.
+     <p> 
+     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.<BR>
+    * If the packet arrived from anywhere else it is not changed.<br>
+    * <B> NOTE: </B> 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.
+     <p>
+     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.
+     <p>
+     CProxy might decide to stop forwarding datagrams, in which case it
+     should close primary connection. This method allows to check, wether
+     this have been done.
+     <p>
+     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.
+     <p>
+     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<b.length;++i){
+          int i1 = (b[i] & 0xF0) >> 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;
+
+	     CProxy.setDefaultProxy(proxyHost,proxyPort);
+	     CProxy p = CProxy.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();
+      }
+   }
+*/
+
+}
--- a/src/net/sourceforge/jsocks/Socks5Message.java	Fri Aug 01 10:25:44 2014 -0700
+++ b/src/net/sourceforge/jsocks/Socks5Message.java	Fri Aug 01 11:23:10 2014 -0700
@@ -1,292 +1,291 @@
-package net.sourceforge.jsocks;
-
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- SOCKS5 request/response message.
-*/
-
-public 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);
-    }
-
-    /**
-      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 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);
-    }
-
-
-    /**
-      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 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);
-
-        @SuppressWarnings("unused")
-        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;
-
-}
+package net.sourceforge.jsocks;
+
+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);
+   }
+
+   /**
+     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 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);
+   }
+
+
+   /**
+     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 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(CProxy.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(CProxy.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;
+
+}
--- a/src/net/sourceforge/jsocks/Socks5Proxy.java	Fri Aug 01 10:25:44 2014 -0700
+++ b/src/net/sourceforge/jsocks/Socks5Proxy.java	Fri Aug 01 11:23:10 2014 -0700
@@ -1,248 +1,248 @@
-package net.sourceforge.jsocks;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.util.Enumeration;
-import java.util.Hashtable;
-
-/**
- SOCKS5 Proxy.
-*/
-
-public class Socks5Proxy extends Proxy implements Cloneable {
-
-//Data members
-    private Hashtable<Integer, Authentication> authMethods = new Hashtable<Integer, Authentication>();
-    private int selectedMethod;
-
-    boolean resolveAddrLocally = true;
-    UDPEncapsulation udp_encapsulation = null;
-
-
-//Public Constructors
-//====================
-
-    /**
-      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 {
-        super(proxyHost, 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) {
-        super(proxyIP, proxyPort);
-        version = 5;
-        setAuthenticationMethod(0, new AuthenticationNone());
-    }
-
-
-//Public instance methods
-//========================
-
-
-    /**
-     * Wether to resolve address locally or to let proxy do so.
-       <p>
-       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(Integer.valueOf(methodId)) != null);
-        }
-        else {//Add the method, or rewrite old one
-            authMethods.put(Integer.valueOf(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(Integer.valueOf(methodId));
-
-        if (method == null) return null;
-
-        return (Authentication)method;
-    }
-
-    /**
-     Creates a clone of this Proxy.
-    */
-    @Override
-    @SuppressWarnings("unchecked")
-    public Object clone() {
-        Socks5Proxy newProxy = new Socks5Proxy(proxyIP, proxyPort);
-        newProxy.authMethods = (Hashtable<Integer, Authentication>) this.authMethods.clone();
-        newProxy.resolveAddrLocally = resolveAddrLocally;
-        newProxy.chainProxy = chainProxy;
-        return newProxy;
-    }
-
-//Public Static(Class) Methods
-//==============================
-
-
-//Protected Methods
-//=================
-
-    @Override
-    protected Proxy copy() {
-        Socks5Proxy copy = new Socks5Proxy(proxyIP, proxyPort);
-        copy.authMethods = this.authMethods; //same Hash, no copy
-        copy.chainProxy = this.chainProxy;
-        copy.resolveAddrLocally = this.resolveAddrLocally;
-        return copy;
-    }
-    /**
-     *
-     *
-     */
-    @Override
-    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<Integer> ids = authMethods.keys();
-
-            while (ids.hasMoreElements())
-                buf[i++] = (byte)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));
-        }
-    }
-
-    @Override
-    protected ProxyMessage formMessage(int cmd, InetAddress ip, int port) {
-        return new Socks5Message(cmd, ip, port);
-    }
-    @Override
-    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);
-    }
-    @Override
-    protected ProxyMessage formMessage(InputStream in)
-    throws SocksException,
-        IOException {
-        return new Socks5Message(in);
-    }
-
-}
+package net.sourceforge.jsocks;
+import java.net.*;
+import java.io.*;
+import java.util.Hashtable;
+import java.util.Enumeration;
+
+/**
+ SOCKS5 CProxy.
+*/
+
+public class Socks5Proxy extends CProxy 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 CProxy to use to connect to this proxy, allows proxy chaining.
+     @param proxyHost Host on which a CProxy server runs.
+     @param proxyPort Port on which a CProxy server listens for connections.
+     @throws UnknownHostException If proxyHost can't be resolved.
+   */
+   public Socks5Proxy(CProxy 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 CProxy server runs.
+     @param proxyPort Port on which a CProxy 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 CProxy to use to connect to this proxy, allows proxy chaining.
+     @param proxyIP Host on which a CProxy server runs.
+     @param proxyPort Port on which a CProxy server listens for connections.
+   */
+   public Socks5Proxy(CProxy p,InetAddress proxyIP,int proxyPort){
+      super(p,proxyIP,proxyPort);
+      version = 5;
+      setAuthenticationMethod(0,new AuthenticationNone());
+   }
+
+   /**
+     Creates SOCKS5 proxy.
+     @param proxyIP Host on which a CProxy server runs.
+     @param proxyPort Port on which a CProxy 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.
+      <p>
+      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 CProxy.
+   */
+   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 CProxy 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);
+   }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/sourceforge/jsocks/SocksDialog.java	Fri Aug 01 11:23:10 2014 -0700
@@ -0,0 +1,615 @@
+package net.sourceforge.jsocks;
+import java.awt.*;
+import java.awt.event.*;
+
+/**
+  Socks configuration dialog.<br>
+  Class which provides GUI means of getting CProxy configuration
+  from the user.
+ */
+public class SocksDialog extends Dialog
+                         implements WindowListener,
+                                    ItemListener,
+                                    ActionListener,
+                                    Runnable{
+
+//GUI components
+   TextField host_text,
+             port_text,
+             user_text,
+             password_text,
+             direct_text;
+   Button add_button,
+          remove_button,
+          cancel_button,
+          ok_button,
+          dismiss_button;
+   List direct_list;
+   Checkbox socks4radio,
+            socks5radio,
+            none_check,
+            up_check,
+            gssapi_check;
+
+   Dialog warning_dialog;
+   Label warning_label;
+
+   String host,user,password;
+   int port;
+   Thread net_thread = null;
+   
+   //CheckboxGroups
+   CheckboxGroup socks_group = new CheckboxGroup();
+
+   CProxy proxy;
+   InetRange ir;
+
+   final static int COMMAND_MODE = 0;
+   final static int OK_MODE = 1;
+
+   int mode;
+
+   /**Wether to resolve addresses in separate thread.
+      <p>
+      Default value is true, however on some JVMs, namely one from
+      the Microsoft, it doesn't want to work properly, separate thread
+      can't close the dialog opened in GUI thread, and everuthing else
+      is then crashes.
+      <p>
+      When setting this variable to false, SocksDialog will block while
+      trying to look up proxy host, and if this takes considerable amount
+      of time it might be annoying to user.
+
+   */
+   public static boolean useThreads = true;
+
+
+// Constructors
+////////////////////////////////////
+   /**
+     Creates SOCKS configuration dialog.<br>
+     Uses default initialisation:<br>
+     CProxy host: socks-proxy <br>
+     CProxy port: 1080 <br>
+     Version: 5<br>
+
+    */
+   public SocksDialog(Frame parent){
+      this(parent,null);
+   }
+   /**
+    Creates SOCKS configuration dialog and initialises it
+    to given proxy.
+    */
+   public SocksDialog(Frame parent,CProxy init_proxy){
+     super(parent,"Proxy Configuration",true);
+     warning_dialog = new Dialog(parent,"Warning",true);
+
+     guiInit();
+     setResizable(false);
+     addWindowListener(this);
+     Component[] comps = getComponents();
+     for(int i=0;i<comps.length;++i){
+        if(comps[i] instanceof Button)
+           ((Button)comps[i]).addActionListener(this);
+        else if(comps[i] instanceof TextField)
+           ((TextField) comps[i]).addActionListener(this);
+        else if(comps[i] instanceof Checkbox){
+           ((Checkbox)comps[i]).addItemListener(this);
+        }
+     }
+     proxy = init_proxy;
+     if(proxy != null) doInit(proxy);
+     else ir = new InetRange();
+
+     dismiss_button.addActionListener(this);
+     warning_dialog.addWindowListener(this);
+   }
+
+//Public Methods
+////////////////
+
+   /**
+     Displays SOCKS configuartion dialog.
+     <p>
+     Returns initialised proxy object, or null if user cancels dialog
+     by either pressing Cancel or closing the dialog window.
+    */
+   public CProxy getProxy(){
+      mode = COMMAND_MODE;
+      pack();
+      show();
+      return proxy;
+   }
+
+   /**
+     Initialises dialog to given proxy and displays SOCKS configuartion dialog.
+     <p>
+     Returns initialised proxy object, or null if user cancels dialog
+     by either pressing Cancel or closing the dialog window.
+    */
+    public CProxy getProxy(CProxy p){
+      if(p != null){
+         doInit(p);
+      }
+      mode = COMMAND_MODE;
+      pack();
+      show();
+      return proxy;
+   }
+
+// WindowListener Interface
+/////////////////////////////////
+   public void windowActivated(java.awt.event.WindowEvent e){
+   }
+   public void windowDeactivated(java.awt.event.WindowEvent e){
+   }
+   public void windowOpened(java.awt.event.WindowEvent e){
+   }
+   public void windowClosing(java.awt.event.WindowEvent e){
+      Window source = e.getWindow();
+      if(source == this){
+         onCancel();
+      }else if(source == warning_dialog){
+         onDismiss();
+      }
+   }
+   public void windowClosed(java.awt.event.WindowEvent e){
+   }
+   public void windowIconified(java.awt.event.WindowEvent e){
+   }
+   public void windowDeiconified(java.awt.event.WindowEvent e){
+   }
+
+//ActionListener interface
+///////////////////////////
+   public void actionPerformed(ActionEvent ae){
+
+      Object source = ae.getSource();
+
+      if(source == cancel_button )
+         onCancel();
+      else if(source == add_button || source == direct_text)
+         onAdd();
+      else if(source == remove_button)
+         onRemove();
+      else if(source == dismiss_button)
+         onDismiss();
+      else if(source == ok_button || source instanceof TextField)
+         onOK();
+   }
+//ItemListener interface
+////////////////////////
+   public void itemStateChanged(ItemEvent ie){
+      Object source = ie.getSource();
+      //System.out.println("ItemEvent:"+source);
+      if(source == socks5radio || source == socks4radio)
+         onSocksChange();
+      else if(source == up_check)
+         onUPChange();
+
+   }
+//Runnable interface
+////////////////////
+   /**
+     Resolves proxy address in other thread, to avoid
+     annoying blocking in GUI thread.
+   */
+   public void run(){
+
+      if(!initProxy()){
+         //Check if we have been aborted
+         if(mode != OK_MODE) return;
+         if(net_thread != Thread.currentThread()) return;
+
+         mode = COMMAND_MODE;
+         warning_label.setText("Look up failed.");
+         warning_label.invalidate();
+         return;
+      }
+
+      //System.out.println("Done!");
+      while(!warning_dialog.isShowing())
+           ; /* do nothing*/;
+
+      warning_dialog.dispose();
+      //dispose(); //End Dialog
+   }
+
+//Private Methods
+///////////////////
+   private void onOK(){
+      host = host_text.getText().trim();
+      user = user_text.getText();
+      password = password_text.getText();
+
+      if(host.length() == 0){
+        warn("Proxy host is not set!");
+        return;
+      }
+      if(socks_group.getSelectedCheckbox() == socks4radio){
+        if(user.length() == 0){
+          warn("User name is not set");
+          return;
+        }
+
+      }else{
+        if(up_check.getState()){
+          if(user.length() == 0){
+            warn("User name is not set.");
+            return;
+          }
+          if(password.length()==0){
+            warn("Password is not set.");
+            return;
+          }
+        }else if(!none_check.getState()){
+          warn("Please select at least one Authentication Method.");
+          return;
+        }
+      }
+
+      try{
+         port = Integer.parseInt(port_text.getText());
+      }catch(NumberFormatException nfe){
+         warn("Proxy port is invalid!");
+         return;
+      }
+
+      mode = OK_MODE;
+
+      if(useThreads){
+         net_thread = new Thread(this);
+         net_thread.start();
+
+         info("Looking up host: "+host);
+         //System.out.println("Info returned.");
+      }else if(!initProxy()){
+           warn("Proxy host is invalid.");
+           mode = COMMAND_MODE;
+         }
+
+      if(mode == OK_MODE) dispose();
+   }
+
+   private void onCancel(){
+      //System.out.println("Cancel");
+      proxy = null;
+      dispose();
+   }
+
+   private void onAdd(){
+      String s = direct_text.getText();
+      s.trim();
+      if(s.length() == 0 ) return;
+      //Check for Duplicate
+      String[] direct_hosts = direct_list.getItems();
+      for(int i=0;i<direct_hosts.length;++i)
+         if(direct_hosts[i].equals(s)) return;
+
+      direct_list.add(s);
+      ir.add(s);
+   }
+   private void onRemove(){
+      int index = direct_list.getSelectedIndex();
+      if(index < 0) return;
+      ir.remove(direct_list.getItem(index));
+      direct_list.remove(index);
+      direct_list.select(index);
+   }
+
+   private void onSocksChange(){
+      Object selected = socks_group.getSelectedCheckbox();
+      if(selected == socks4radio){
+         user_text.setEnabled(true);
+         password_text.setEnabled(false);
+         none_check.setEnabled(false);
+         up_check.setEnabled(false);
+      }else{
+         if(up_check.getState()){
+           user_text.setEnabled(true);
+           password_text.setEnabled(true);
+         }else{
+           user_text.setEnabled(false);
+           password_text.setEnabled(false);
+         }
+         none_check.setEnabled(true);
+         up_check.setEnabled(true);
+      }
+      //System.out.println("onSocksChange:"+selected);
+   }
+   private void onUPChange(){
+      //System.out.println("onUPChange");
+      if(up_check.getState()){
+        user_text.setEnabled(true);
+        password_text.setEnabled(true);
+      }else{
+        user_text.setEnabled(false);
+        password_text.setEnabled(false);
+      }
+   }
+   private void onDismiss(){
+     warning_dialog.dispose();
+     if(mode == OK_MODE){
+       mode = COMMAND_MODE;
+       if(net_thread!=null) net_thread.interrupt();
+     }
+   }
+
+   private void doInit(CProxy p){
+     if(p.version == 5){
+       socks_group.setSelectedCheckbox(socks5radio);
+       onSocksChange();
+       if(((Socks5Proxy)p).getAuthenticationMethod(0)!=null)
+          none_check.setState(true);
+       UserPasswordAuthentication auth = (UserPasswordAuthentication) 
+                                  ((Socks5Proxy)p).getAuthenticationMethod(2);
+       if(auth!=null){
+          user_text.setText(auth.getUser());
+          password_text.setText(auth.getPassword());
+          up_check.setState(true);
+          onUPChange();
+       }
+     }else{
+       socks_group.setSelectedCheckbox(socks4radio);
+       onSocksChange();
+       user_text.setText(((Socks4Proxy)p).user);
+     }
+     ir = (InetRange)(p.directHosts.clone());
+     String[] direct_hosts = ir.getAll();
+     direct_list.removeAll();
+     for(int i=0;i<direct_hosts.length;++i)
+       direct_list.add(direct_hosts[i]);
+
+     host_text.setText(p.proxyIP.getHostName());
+     port_text.setText(""+p.proxyPort);
+
+   }
+
+   private boolean initProxy(){
+      try{
+         if(socks_group.getSelectedCheckbox() == socks5radio){
+            proxy = new Socks5Proxy(host,port);
+            if(up_check.getState())
+              ((Socks5Proxy)proxy).setAuthenticationMethod(2,
+                           new UserPasswordAuthentication(user,password));
+            if(!none_check.getState())
+              ((Socks5Proxy)proxy).setAuthenticationMethod(0,null);
+         }
+         else
+            proxy = new Socks4Proxy(host,port,user);
+      }catch(java.net.UnknownHostException uhe){
+         return false;
+      }
+      proxy.directHosts = ir;
+      return true;
+   }
+   private void info(String s){
+     msgBox("Info",s);
+   }
+
+   private void warn(String s){
+     msgBox("Warning",s);
+   }
+   
+   private void msgBox(String title, String message){
+     warning_label.setText(message);
+     warning_label.invalidate();
+     warning_dialog.setTitle(title);
+     warning_dialog.pack();
+     warning_dialog.show();
+   }
+
+/*======================================================================
+  Form:
+  Table:
+          +---+-------+---+---+
+          |   |       |   |   |
+          +---+-------+---+---+
+          |           |       |
+          +---+-------+-------+
+          |   |       |       |
+          +---+-------+-------+
+          |   |       |       |
+          +---+-------+-------+
+          |           |       |
+          +-----------+-------+
+          |           |       |
+          |           +---+---+
+          |           |   |   |
+          +-----------+---+---+
+          |           |   |   |
+          +---+---+---+---+---+
+          |   |   |   |   |   |
+          +---+---+---+---+---+
+*/
+
+   void guiInit(){
+      //Some default names used
+      Label label;
+      Container container;
+      Font font;
+
+      GridBagConstraints c = new GridBagConstraints();
+
+      font = new Font("Dialog",Font.PLAIN,12);
+      
+      container = this;
+      //container = new Panel();
+      container.setLayout(new GridBagLayout());
+      container.setFont(font);
+      container.setBackground(SystemColor.menu);
+      c.insets = new Insets(3,3,3,3);
+      
+      c.gridx=0; c.gridy=0;
+      c.gridwidth=1; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTHEAST;
+      label = new Label("Host:");
+      container.add(label,c);
+      
+      c.gridx=1; c.gridy=0;
+      c.gridwidth=2; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTHWEST;
+      host_text = new TextField("socks-proxy",15);
+      container.add(host_text,c);
+      
+      c.gridx=3; c.gridy=0;
+      c.gridwidth=1; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTHEAST;
+      label = new Label("Port:");
+      container.add(label,c);
+      
+      c.gridx=4; c.gridy=0;
+      c.gridwidth=1; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTHWEST;
+      port_text = new TextField("1080",5);
+      container.add(port_text,c);
+      
+      c.gridx=0; c.gridy=1;
+      c.gridwidth=3; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTH;
+      socks4radio = new Checkbox("Socks4",socks_group,false);
+      //1.0 compatible code
+      //socks4radio = new Checkbox("Socks4",false);
+      //socks4radio.setCheckboxGroup(socks_group);
+      socks4radio.setFont(new Font(font.getName(),Font.BOLD,14));
+      container.add(socks4radio,c);
+      
+      c.gridx=3; c.gridy=1;
+      c.gridwidth=2; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTH;
+      socks5radio = new Checkbox("Socks5",socks_group,true);
+      //1.0 compatible code
+      //socks5radio = new Checkbox("Socks5",true);
+      //socks5radio.setCheckboxGroup(socks_group);
+      socks5radio.setFont(new Font(font.getName(),Font.BOLD,14));
+      container.add(socks5radio,c);
+      
+      c.gridx=0; c.gridy=2;
+      c.gridwidth=1; c.gridheight=1;
+      c.anchor=GridBagConstraints.EAST;
+      label = new Label("User Id:");
+      container.add(label,c);
+      
+      c.gridx=1; c.gridy=2;
+      c.gridwidth=2; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTHWEST;
+      user_text = new TextField("",15);
+      user_text.setEnabled(false);
+      container.add(user_text,c);
+      
+      c.gridx=3; c.gridy=2;
+      c.gridwidth=2; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTH;
+      label = new Label("Authentication");
+      label.setFont(new Font(font.getName(),Font.BOLD,14));
+      container.add(label,c);
+      
+      c.gridx=0; c.gridy=3;
+      c.gridwidth=1; c.gridheight=1;
+      c.anchor=GridBagConstraints.EAST;
+      label = new Label("Password:");
+      container.add(label,c);
+      
+      c.gridx=1; c.gridy=3;
+      c.gridwidth=2; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTHWEST;
+      password_text = new TextField("",15);
+      password_text.setEchoChar('*');
+      password_text.setEnabled(false);
+      //password_text.setEchoCharacter('*');//1.0
+      container.add(password_text,c);
+      
+      c.gridx=3; c.gridy=3;
+      c.gridwidth=2; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTHWEST;
+      none_check = new Checkbox("None",true);
+      container.add(none_check,c);
+      
+      c.gridx=0; c.gridy=4;
+      c.gridwidth=3; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTH;
+      label = new Label("Direct Hosts");
+      label.setFont(new Font(font.getName(),Font.BOLD,14));
+      container.add(label,c);
+      
+      c.gridx=3; c.gridy=4;
+      c.gridwidth=2; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTHWEST;
+      up_check = new Checkbox("User/Password",false);
+      container.add(up_check,c);
+      
+      c.gridx=0; c.gridy=5;
+      c.gridwidth=3; c.gridheight=2;
+      c.anchor=GridBagConstraints.NORTHWEST;
+      c.fill = GridBagConstraints.BOTH;
+      direct_list = new List(3);
+      container.add(direct_list,c);
+      
+      c.gridx=3; c.gridy=5;
+      c.gridwidth=2; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTHWEST;
+      gssapi_check = new Checkbox("GSSAPI",false);
+      gssapi_check.setEnabled(false);
+      container.add(gssapi_check,c);
+      
+      c.gridx=0; c.gridy=7;
+      c.gridwidth=3; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTHWEST;
+      direct_text = new TextField("",25);
+      container.add(direct_text,c);
+      
+      c.gridx=3; c.gridy=7;
+      c.gridwidth=1; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTH;
+      add_button = new Button("Add");
+      container.add(add_button,c);
+      
+      c.gridx=3; c.gridy=6;
+      c.gridwidth=1; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTH;
+      remove_button = new Button("Remove");
+      container.add(remove_button,c);
+      
+      c.gridx=1; c.gridy=8;
+      c.gridwidth=1; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTH;
+      cancel_button = new Button("Cancel");
+      container.add(cancel_button,c);
+      
+      c.gridx=0; c.gridy=8;
+      c.gridwidth=1; c.gridheight=1;
+      c.anchor=GridBagConstraints.NORTHWEST;
+      ok_button = new Button("OK");
+      container.add(ok_button,c);
+
+      //up_check.setEnabled(false);
+
+      //Warning Dialog
+      dismiss_button = new Button("Dismiss");
+      warning_label  = new Label("",Label.CENTER);
+      warning_label.setFont(new Font("Dialog",Font.BOLD,15));
+
+      Panel p = new Panel();
+      p.add(dismiss_button);
+      warning_dialog.add(p,BorderLayout.SOUTH);
+      warning_dialog.add(warning_label,BorderLayout.CENTER);
+      warning_dialog.setResizable(false);
+   }//end guiInit
+
+/*
+// Main 
+////////////////////////////////////
+   public static void main(String[] args) throws Exception{
+      Frame f = new Frame("Test for SocksDialog");
+      f.add("Center", new Label("Fill the Dialog"));
+      SocksDialog socksdialog = new SocksDialog(f);
+      f.pack();
+      f.show();
+      f.addWindowListener(socksdialog);
+      CProxy p = socksdialog.getProxy();
+      System.out.println("Selected: "+p);
+   }
+*/
+
+}//end class
--- a/src/net/sourceforge/jsocks/SocksException.java	Fri Aug 01 10:25:44 2014 -0700
+++ b/src/net/sourceforge/jsocks/SocksException.java	Fri Aug 01 11:23:10 2014 -0700
@@ -1,84 +1,78 @@
-package net.sourceforge.jsocks;
-
-/**
- Exception thrown by various socks classes to indicate errors
- with protocol or unsuccessful server responses.
-*/
-public class SocksException extends java.io.IOException {
-    private static final long serialVersionUID = 6141184566248512277L;
-
-    /**
-     Construct a SocksException with given error code.
-     <p>
-     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;
-    public int errCode;
-
-}//End of SocksException class
-
+package net.sourceforge.jsocks;
+
+/**
+ Exception thrown by various socks classes to indicate errors
+ with protocol or unsuccessfull server responses.
+*/
+public class SocksException extends java.io.IOException{
+   /**
+    Construct a SocksException with given errorcode.
+    <p>
+    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
+
--- a/src/net/sourceforge/jsocks/SocksServerSocket.java	Fri Aug 01 10:25:44 2014 -0700
+++ b/src/net/sourceforge/jsocks/SocksServerSocket.java	Fri Aug 01 11:23:10 2014 -0700
@@ -1,170 +1,207 @@
-package net.sourceforge.jsocks;
-
-import java.net.*;
-import java.io.*;
-
-/**
-   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 given 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 {
-        super(0);
-        remoteAddr = InetAddress.getByName(host);
-        doDirect();
-    }
-
-    /**
-     * 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 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);
-        remoteAddr = ip;
-        doDirect();
-    }
-
-
-    /**
-     * 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.
-      <P>
-      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 doDirect() {
-        doing_direct = true;
-        localPort = super.getLocalPort();
-        localIP = super.getInetAddress();
-        localHost = localIP.getHostName();
-    }
-
-}
+package net.sourceforge.jsocks;
+
+import java.net.*;
+import java.io.*;
+
+/**
+   SocksServerSocket allows to accept connections from one particular
+   host through the SOCKS4 or SOCKS5 proxy.
+*/
+public class SocksServerSocket extends ServerSocket{
+   //Data members
+   protected CProxy 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 CProxy.
+    *@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(CProxy.defaultProxy,host,port);
+   }
+   /**
+    *Creates ServerSocket capable of accepting one connection
+    *through the firewall, uses given proxy.
+    *@param p    CProxy object to use.
+    *@param host Host from which the connection should be recieved.
+    *@param port Port number of the primary connection.
+    */
+   public SocksServerSocket(CProxy p,String host,int port)
+	  throws SocksException,UnknownHostException,IOException{
+
+
+      super(0);
+      if(p == null) throw new SocksException(CProxy.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 CProxy.
+    *@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(CProxy.defaultProxy,ip,port);
+   }
+
+   /**
+    *Creates ServerSocket capable of accepting one connection
+    *through the firewall, uses given proxy.
+    *@param p    CProxy object to use.
+    *@param ip   Host from which the connection should be recieved.
+    *@param port Port number of the primary connection.
+    */
+   public SocksServerSocket(CProxy p,InetAddress ip, int port)
+          throws SocksException,IOException{
+      super(0);
+
+      if(p == null) throw new SocksException(CProxy.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.
+     <P>
+     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();
+   }
+
+}
--- a/src/net/sourceforge/jsocks/SocksSocket.java	Fri Aug 01 10:25:44 2014 -0700
+++ b/src/net/sourceforge/jsocks/SocksSocket.java	Fri Aug 01 11:23:10 2014 -0700
@@ -1,297 +1,332 @@
-package net.sourceforge.jsocks;
-
-import java.net.*;
-import java.io.*;
-
-/**
- * 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.
- * <P>
- * Using Socks package can be as easy as that:
- *
- * <pre><tt>
- *
- *     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);
- *     }
- *
- * </tt></pre>
- *<P>
- * 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:
-       <ol>
-
-       <li> Proxy settings say that address should be resolved locally, but
-            this fails.
-       <li> Proxy settings say that the host should be contacted directly but
-            host name can't be resolved.
-       </ol>
-       @throws SocksException
-       If one of the following happens:
-       <ul>
-        <li> Proxy is is null.
-        <li> Proxy settings say that the host should be contacted directly but
-             this fails.
-        <li> Socks Server can't be contacted.
-        <li> Authentication fails.
-        <li> Connection is not allowed by the SOCKS proxy.
-        <li> SOCKS proxy can't establish the connection.
-        <li> Any IO error occured.
-        <li> Any protocol error occured.
-       </ul>
-       @throws IOexception if anything is wrong with I/O.
-       @see Socks5Proxy#resolveAddrLocally
-     */
-    public SocksSocket(Proxy p, String host, int port) throws SocksException,
-        UnknownHostException {
-        remoteHost = host;
-        remotePort = port;
-        remoteIP = InetAddress.getByName(host);
-        doDirect();
-    }
-
-    /**
-       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(InetAddress ip, int port) throws SocksException {
-        this.remoteIP = ip;
-        this.remotePort = port;
-        this.remoteHost = ip.getHostName();
-        doDirect();
-    }
-
-
-    /**
-     * 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 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);
-        }
-    }
-
-}
+package net.sourceforge.jsocks;
+
+import java.net.*;
+import java.io.*;
+
+/**
+ * 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, CProxy 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.
+ * <P>
+ * Using Socks package can be as easy as that:
+ *
+ * <pre><tt>
+ *
+ *     import Socks.*;
+ *     ....
+ *
+ *     try{
+ *        //Specify SOCKS5 proxy
+ *        CProxy.setDefaultProxy("socks-proxy",1080);
+ *
+ *        //OR you still use SOCKS4
+ *        //Code below uses SOCKS4 proxy
+ *        //CProxy.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);
+ *     }
+ *
+ * </tt></pre>
+ *<P>
+ * 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 CProxy 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(CProxy,String,int)
+    * @see Socks5Proxy#resolveAddrLocally
+    */
+   public SocksSocket(String host,int port)
+	  throws SocksException,UnknownHostException{
+      this(CProxy.defaultProxy,host,port);
+   }
+   /**
+    * Connects to host port using given proxy server.
+      @param p CProxy to use.
+      @param host Machine to connect to.
+      @param port Port to which to connect.
+      @throws UnknownHostException 
+      If one of the following happens:
+      <ol>
+
+      <li> CProxy settings say that address should be resolved locally, but
+           this fails.
+      <li> CProxy settings say that the host should be contacted directly but
+           host name can't be resolved. 
+      </ol>
+      @throws SocksException
+      If one of the following happens:
+      <ul>
+       <li> CProxy is is null.
+       <li> CProxy settings say that the host should be contacted directly but
+            this fails.
+       <li> Socks Server can't be contacted.
+       <li> Authentication fails.
+       <li> Connection is not allowed by the SOCKS proxy.
+       <li> SOCKS proxy can't establish the connection.
+       <li> Any IO error occured.
+       <li> Any protocol error occured.
+      </ul>
+      @throws IOexception if anything is wrong with I/O.
+      @see Socks5Proxy#resolveAddrLocally
+    */
+   public SocksSocket(CProxy p,String host,int port)
+	  throws SocksException,UnknownHostException{
+
+
+      if(p == null) throw new SocksException(CProxy.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(CProxy,String,int)
+    */
+   public SocksSocket(InetAddress ip, int port) throws SocksException{
+      this(CProxy.defaultProxy,ip,port);
+   }
+
+   /**
+      Connects to given ip and port using given CProxy server.
+      @param p CProxy to use.
+      @param ip Machine to connect to.
+      @param port Port to which to connect.
+
+    */
+   public SocksSocket(CProxy p,InetAddress ip, int port) throws SocksException{
+      if(p == null) throw new SocksException(CProxy.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,CProxy 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,CProxy 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(CProxy.SOCKS_DIRECT_FAILED,
+                                  "Direct connect failed:"+io_ex);
+      }
+   }
+
+}
--- a/src/net/sourceforge/jsocks/UDPEncapsulation.java	Fri Aug 01 10:25:44 2014 -0700
+++ b/src/net/sourceforge/jsocks/UDPEncapsulation.java	Fri Aug 01 11:23:10 2014 -0700
@@ -1,29 +1,29 @@
-package net.sourceforge.jsocks;
-/**
- This interface provides for datagram encapsulation for SOCKSv5 protocol.
- <p>
- 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;
-}
+package net.sourceforge.jsocks;
+/**
+ This interface provides for datagram encapsulation for SOCKSv5 protocol.
+ <p>
+ 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;
+}
--- a/src/net/sourceforge/jsocks/UDPRelayServer.java	Fri Aug 01 10:25:44 2014 -0700
+++ b/src/net/sourceforge/jsocks/UDPRelayServer.java	Fri Aug 01 11:23:10 2014 -0700
@@ -1,212 +1,212 @@
-package net.sourceforge.jsocks;
-import net.sourceforge.jsocks.server.*;
-import java.net.*;
-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;
-
-    static PrintStream log = null;
-    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.<br>
-     Zero timeout implies infinity.<br>
-     Default timeout is 3 minutes.
-     */
-
-    static public void setTimeout(int timeout) {
-        iddleTimeout = timeout;
-    }
-
-
-    /**
-      Sets the size of the datagrams used in the UDPRelayServer.<br>
-      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.
-     <p>
-     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) {
-        if (log != null) {
-            log.println(s);
-            log.flush();
-        }
-    }
-
-    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);
-        }
-    }
-}
+package net.sourceforge.jsocks;
+import net.sourceforge.jsocks.server.*;
+import java.net.*;
+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;
+
+    static PrintStream log = null;
+    static CProxy 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.<br>
+    Zero timeout implies infinity.<br>
+    Default timeout is 3 minutes.
+    */
+
+    static public void setTimeout(int timeout){
+      iddleTimeout = timeout;
+    }
+
+
+   /**
+     Sets the size of the datagrams used in the UDPRelayServer.<br>
+     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.
+     <p>
+     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){
+      if(log != null){
+        log.println(s);
+        log.flush();
+      }
+    }
+
+    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);
+       }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/sourceforge/jsocks/UserPasswordAuthentication.java	Fri Aug 01 11:23:10 2014 -0700
@@ -0,0 +1,75 @@
+package net.sourceforge.jsocks;
+
+/**
+  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);
+   }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/sourceforge/jsocks/server/Ident.java	Fri Aug 01 11:23:10 2014 -0700
@@ -0,0 +1,158 @@
+package socks.server;
+import net.sourceforge.jsocks.*;
+import java.net.*;
+import java.io.*;
+import java.util.StringTokenizer;
+
+/**
+ Class Ident provides means to obtain user name of the owner of the socket 
+ on remote machine, providing remote machine runs identd daemon.
+ <p>
+ To use it:
+   <tt><pre>
+   Socket s = ss.accept();
+   Ident id = new Ident(s);
+   if(id.successful) goUseUser(id.userName);
+   else handleIdentError(id.errorCode,id.errorMessage)
+   </pre></tt>
+*/
+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.
+     <p>
+     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.
+     <p>
+     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();
+    }
+//*/
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/sourceforge/jsocks/server/IdentAuthenticator.java	Fri Aug 01 11:23:10 2014 -0700
@@ -0,0 +1,150 @@
+package socks.server;
+import net.sourceforge.jsocks.InetRange;
+import net.sourceforge.jsocks.ProxyMessage;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.Enumeration;
+import java.net.*;
+import java.io.*;
+
+/**
+  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.
+  <p>
+  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<hosts.size();++i)
+        s += "Range:"+hosts.elementAt(i)+"\nUsers:"+userNames(i)+"\n";
+     return s;
+  }
+
+//Private Methods
+//////////////////
+  private int getRangeIndex(InetAddress ip){
+     int index = 0;
+     Enumeration eEnum = hosts.elements();
+     while(eEnum.hasMoreElements()){
+       InetRange ir = (InetRange) eEnum.nextElement();
+       if(ir.contains(ip)) return index;
+       index++;
+     }
+     return -1; //Not found
+  }
+
+  private String userNames(int i){
+    if(users.elementAt(i) == null) return "Everybody is permitted.";
+
+    Enumeration eEnum = ((Hashtable)users.elementAt(i)).keys();
+    if(!eEnum.hasMoreElements()) return "";
+    String s = eEnum.nextElement().toString();
+    while(eEnum.hasMoreElements())
+       s += "; "+eEnum.nextElement();
+
+    return s;
+  }
+
+}
--- a/src/net/sourceforge/jsocks/server/ServerAuthenticator.java	Fri Aug 01 10:25:44 2014 -0700
+++ b/src/net/sourceforge/jsocks/server/ServerAuthenticator.java	Fri Aug 01 11:23:10 2014 -0700
@@ -1,120 +1,119 @@
-package net.sourceforge.jsocks.server;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.DatagramPacket;
-import java.net.Socket;
-
-import net.sourceforge.jsocks.ProxyMessage;
-import net.sourceforge.jsocks.UDPEncapsulation;
-
-/**
- Classes implementing this interface should provide socks server with
- authentication and authorization of users.
-**/
-public interface ServerAuthenticator {
-
-    /**
-      This method is called when a new connection accepted by the server.
-      <p>
-      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.
-      <p>
-      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.
-     <p>
-     SOCKSv5 allows to have multiple authentication methods, and these methods
-     might require some kind of transformations being made on the data.
-     <p>
-     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.
-     <p>
-     SOCKSv5 allows to have multiple authentication methods, and these methods
-     might require some kind of transformations being made on the data.
-     <p>
-     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.
-     <p>
-     If no transformation should be done on the datagrams, this method
-     should return null.
-     <p>
-     This method is called on the object returned from the startSession
-     function.
-    */
-
-    UDPEncapsulation getUdpEncapsulation();
-
-    /**
-     This method is called when a request have been read.
-     <p>
-     Implementation should decide wether to grant request or not. Returning
-     true implies granting the request, false means request should be rejected.
-     <p>
-     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.
-     <p>
-     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.
-     <p>
-     Implementaions should return true if the datagram is to be forwarded, and
-     false if the datagram should be dropped.
-     <p>
-     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.
-     <p>
-     This method is called on the object returned from the startSession
-     function.
-    */
-    void endSession();
-}
+package socks.server;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.DatagramPacket;
+import net.sourceforge.jsocks.ProxyMessage;
+import net.sourceforge.jsocks.UDPEncapsulation;
+
+/**
+ Classes implementing this interface should provide socks server with
+ authentication and authorization of users.
+**/
+public interface ServerAuthenticator{
+
+   /**
+     This method is called when a new connection accepted by the server.
+     <p>
+     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. 
+     <p>
+     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.
+    <p>
+    SOCKSv5 allows to have multiple authentication methods, and these methods
+    might require some kind of transformations being made on the data.
+    <p>
+    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.
+    <p>
+    SOCKSv5 allows to have multiple authentication methods, and these methods
+    might require some kind of transformations being made on the data.
+    <p>
+    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.
+    <p>
+    If no transformation should be done on the datagrams, this method
+    should return null.
+    <p>
+    This method is called on the object returned from the startSession
+    function.
+   */
+
+   UDPEncapsulation getUdpEncapsulation();
+
+   /**
+    This method is called when a request have been read.
+    <p>
+    Implementation should decide wether to grant request or not. Returning
+    true implies granting the request, false means request should be rejected.
+    <p>
+    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.
+    <p>
+    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.
+    <p>
+    Implementaions should return true if the datagram is to be forwarded, and
+    false if the datagram should be dropped.
+    <p>
+    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.
+    <p>
+    This method is called on the object returned from the startSession
+    function.
+   */
+   void endSession();
+}
--- a/src/net/sourceforge/jsocks/server/ServerAuthenticatorNone.java	Fri Aug 01 10:25:44 2014 -0700
+++ b/src/net/sourceforge/jsocks/server/ServerAuthenticatorNone.java	Fri Aug 01 11:23:10 2014 -0700
@@ -1,169 +1,170 @@
-package net.sourceforge.jsocks.server;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PushbackInputStream;
-import java.net.Socket;
-
-import net.sourceforge.jsocks.ProxyMessage;
-import net.sourceforge.jsocks.UDPEncapsulation;
-
-/**
- An implementation of ServerAuthenticator, which does <b>not</b> do
- any authentication.
-<P>
-<FONT size="+3" color ="FF0000"> Warning!!</font><br> Should not be
-used on machines which are not behind the firewall.
-<p>
-It is only provided to make implementing other authentication schemes
-easier.<br>
-For Example: <tt><pre>
-   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;
-    }
-   }
-</pre></tt>
-*/
-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;
-    }
-
-    /**
-     Allways returns true.
-    */
-    public boolean checkRequest(ProxyMessage msg) {
-        return true;
-    }
-
-    /**
-     Allways returns true.
-    */
-    public boolean checkRequest(java.net.DatagramPacket dp, boolean out) {
-        return true;
-    }
-
-    /**
-     Does nothing.
-     */
-    public void endSession() {
-    }
-
-    /**
-      Convinience routine for selecting SOCKSv5 authentication.
-      <p>
-      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 < num_methods; ++i)
-            if (method_ids[i] == methodId) {
-                found = true;
-                response[1] = (byte) methodId;
-                break;
-            }
-
-        out.write(response);
-        return found;
-    }
-}
+package socks.server;
+import net.sourceforge.jsocks.ProxyMessage;
+import net.sourceforge.jsocks.UDPEncapsulation;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.DataInputStream;
+import java.io.OutputStream;
+import java.io.PushbackInputStream;
+import java.net.Socket;
+
+/**
+ An implementation of ServerAuthenticator, which does <b>not</b> do
+ any authentication.
+<P>
+<FONT size="+3" color ="FF0000"> Warning!!</font><br> Should not be
+used on machines which are not behind the firewall.
+<p>
+It is only provided to make implementing other authentication schemes
+easier.<br>
+For Example: <tt><pre>
+   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;
+    }
+   }
+</pre></tt>
+*/
+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;
+   }
+
+   /**
+    Allways returns true.
+   */
+   public boolean checkRequest(ProxyMessage msg){
+     return true;
+   }
+
+   /**
+    Allways returns true.
+   */
+   public boolean checkRequest(java.net.DatagramPacket dp, boolean out){
+     return true;
+   }
+
+   /**
+    Does nothing.
+    */
+   public void endSession(){
+   }
+
+   /**
+     Convinience routine for selecting SOCKSv5 authentication.
+     <p>
+     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<num_methods;++i)
+         if(method_ids[i] == methodId){
+           found = true;
+           response[1] = (byte) methodId;
+           break;
+         }
+
+      out.write(response);
+      return found;
+   }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/sourceforge/jsocks/server/UserPasswordAuthenticator.java	Fri Aug 01 11:23:10 2014 -0700
@@ -0,0 +1,73 @@
+package socks.server;
+
+import net.sourceforge.jsocks.ProxyMessage;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+  This class implements SOCKS5 User/Password authentication scheme as
+  defined in rfc1929,the server side of it.
+*/
+public class UserPasswordAuthenticator extends  ServerAuthenticatorNone{
+
+   static final int METHOD_ID = 2;
+
+   UserValidation validator;
+
+   /**
+    Construct a new UserPasswordAuthentication object, with given
+    UserVlaidation scheme.
+
+    @param v UserValidation to use for validating users.
+   */
+   public UserPasswordAuthenticator(UserValidation validator){
+      this.validator = validator;
+   }
+
+   public ServerAuthenticator startSession(Socket s) throws IOException{
+     InputStream in = s.getInputStream();
+     OutputStream out = s.getOutputStream();
+
+     if(in.read() != 5) return null; //Drop non version 5 messages.
+
+     if(!selectSocks5Authentication(in,out,METHOD_ID)) 
+       return null;
+     if(!doUserPasswordAuthentication(s,in,out))
+       return null;
+
+     return new ServerAuthenticatorNone(in,out);
+   }
+
+
+//Private Methods
+//////////////////
+
+   private boolean doUserPasswordAuthentication(Socket s,
+                                                InputStream in,
+                                                OutputStream out) 
+                                                throws IOException{
+     int version = in.read();
+     if(version != 1) return false;
+     int ulen = in.read();
+     if(ulen < 0) return false;
+     byte[] user = new byte[ulen];
+     in.read(user);
+     int plen = in.read();
+     if(plen < 0) return false;
+     byte[] password = new byte[plen];
+     in.read(password);
+
+     if(validator.isUserValid(new String(user), new String(password),s)){
+       //System.out.println("user valid");
+       out.write(new byte[]{1,0});
+     }else{
+       //System.out.println("user invalid");
+       out.write(new byte[]{1,1});
+       return false;
+     }
+
+     return true;
+   }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/net/sourceforge/jsocks/server/UserValidation.java	Fri Aug 01 11:23:10 2014 -0700
@@ -0,0 +1,21 @@
+package socks.server;
+
+/**
+  Interface which provides for user validation, based on user name
+  password and where it connects from.
+*/
+public interface UserValidation{
+    /**
+     Implementations of this interface are expected to use some or all
+     of the information provided plus any information they can extract
+     from other sources to decide wether given user should be allowed
+     access to SOCKS server, or whatever you use it for.
+
+     @return true to indicate user is valid, false otherwise.
+     @param username User whom implementation should validate.
+     @param password Password this user provided.
+     @param connection Socket which user used to connect to the server.
+    */
+    boolean isUserValid(String username,String password,
+                        java.net.Socket connection);
+}