aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java/net/sourceforge/jsocks/Socks5Proxy.java
blob: aa9c643a26b72233e5aae5b8ca8a048905e606ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
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(new Integer(methodId)) != null);
      }else{//Add the method, or rewrite old one
	authMethods.put(new Integer(methodId),method);
      }
      return true;
   }
   
   /**
    Get authentication method, which corresponds to given method id
    @param methodId Authentication method id.
    @return Implementation for given method or null, if one was not set.
   */
   public Authentication getAuthenticationMethod(int methodId){
      Object method = authMethods.get(new Integer(methodId));
      if(method == null) return null;
      return (Authentication)method;
   }

   /**
    Creates a clone of this Proxy.
   */
   @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
//=================

   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;
    }
   /**
    *
    *
    */
   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)((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);
   }

}