0
|
1 package net.sourceforge.jsocks;
|
|
2 import java.io.IOException;
|
|
3 import java.io.InputStream;
|
|
4 import java.io.OutputStream;
|
|
5 import java.net.InetAddress;
|
|
6 import java.net.Socket;
|
|
7 import java.net.SocketException;
|
|
8 import java.net.UnknownHostException;
|
|
9 import java.util.Enumeration;
|
|
10 import java.util.Hashtable;
|
|
11
|
|
12 /**
|
|
13 SOCKS5 Proxy.
|
|
14 */
|
|
15
|
|
16 public class Socks5Proxy extends Proxy implements Cloneable {
|
|
17
|
|
18 //Data members
|
|
19 private Hashtable<Integer, Authentication> authMethods = new Hashtable<Integer, Authentication>();
|
|
20 private int selectedMethod;
|
|
21
|
|
22 boolean resolveAddrLocally = true;
|
|
23 UDPEncapsulation udp_encapsulation = null;
|
|
24
|
|
25
|
|
26 //Public Constructors
|
|
27 //====================
|
|
28
|
|
29 /**
|
|
30 Creates SOCKS5 proxy.
|
|
31 @param proxyHost Host on which a Proxy server runs.
|
|
32 @param proxyPort Port on which a Proxy server listens for connections.
|
|
33 @throws UnknownHostException If proxyHost can't be resolved.
|
|
34 */
|
|
35 public Socks5Proxy(String proxyHost, int proxyPort)
|
|
36 throws UnknownHostException {
|
|
37 super(proxyHost, proxyPort);
|
|
38 version = 5;
|
|
39 setAuthenticationMethod(0, new AuthenticationNone());
|
|
40 }
|
|
41
|
|
42
|
|
43 /**
|
|
44 Creates SOCKS5 proxy.
|
|
45 @param proxyIP Host on which a Proxy server runs.
|
|
46 @param proxyPort Port on which a Proxy server listens for connections.
|
|
47 */
|
|
48 public Socks5Proxy(InetAddress proxyIP, int proxyPort) {
|
|
49 super(proxyIP, proxyPort);
|
|
50 version = 5;
|
|
51 setAuthenticationMethod(0, new AuthenticationNone());
|
|
52 }
|
|
53
|
|
54
|
|
55 //Public instance methods
|
|
56 //========================
|
|
57
|
|
58
|
|
59 /**
|
|
60 * Wether to resolve address locally or to let proxy do so.
|
|
61 <p>
|
|
62 SOCKS5 protocol allows to send host names rather then IPs in the
|
|
63 requests, this option controls wether the hostnames should be send
|
|
64 to the proxy server as names, or should they be resolved locally.
|
|
65 @param doResolve Wether to perform resolution locally.
|
|
66 @return Previous settings.
|
|
67 */
|
|
68 public boolean resolveAddrLocally(boolean doResolve) {
|
|
69 boolean old = resolveAddrLocally;
|
|
70 resolveAddrLocally = doResolve;
|
|
71 return old;
|
|
72 }
|
|
73 /**
|
|
74 Get current setting on how the addresses should be handled.
|
|
75 @return Current setting for address resolution.
|
|
76 @see Socks5Proxy#resolveAddrLocally(boolean doResolve)
|
|
77 */
|
|
78 public boolean resolveAddrLocally() {
|
|
79 return resolveAddrLocally;
|
|
80 }
|
|
81
|
|
82 /**
|
|
83 Adds another authentication method.
|
|
84 @param methodId Authentication method id, see rfc1928
|
|
85 @param method Implementation of Authentication
|
|
86 @see Authentication
|
|
87 */
|
|
88 public boolean setAuthenticationMethod(int methodId,
|
|
89 Authentication method) {
|
|
90 if (methodId < 0 || methodId > 255)
|
|
91 return false;
|
|
92
|
|
93 if (method == null) {
|
|
94 //Want to remove a particular method
|
|
95 return (authMethods.remove(Integer.valueOf(methodId)) != null);
|
|
96 }
|
|
97 else {//Add the method, or rewrite old one
|
|
98 authMethods.put(Integer.valueOf(methodId), method);
|
|
99 }
|
|
100
|
|
101 return true;
|
|
102 }
|
|
103
|
|
104 /**
|
|
105 Get authentication method, which corresponds to given method id
|
|
106 @param methodId Authentication method id.
|
|
107 @return Implementation for given method or null, if one was not set.
|
|
108 */
|
|
109 public Authentication getAuthenticationMethod(int methodId) {
|
|
110 Object method = authMethods.get(Integer.valueOf(methodId));
|
|
111
|
|
112 if (method == null) return null;
|
|
113
|
|
114 return (Authentication)method;
|
|
115 }
|
|
116
|
|
117 /**
|
|
118 Creates a clone of this Proxy.
|
|
119 */
|
|
120 @Override
|
|
121 @SuppressWarnings("unchecked")
|
|
122 public Object clone() {
|
|
123 Socks5Proxy newProxy = new Socks5Proxy(proxyIP, proxyPort);
|
|
124 newProxy.authMethods = (Hashtable<Integer, Authentication>) this.authMethods.clone();
|
|
125 newProxy.resolveAddrLocally = resolveAddrLocally;
|
|
126 newProxy.chainProxy = chainProxy;
|
|
127 return newProxy;
|
|
128 }
|
|
129
|
|
130 //Public Static(Class) Methods
|
|
131 //==============================
|
|
132
|
|
133
|
|
134 //Protected Methods
|
|
135 //=================
|
|
136
|
|
137 @Override
|
|
138 protected Proxy copy() {
|
|
139 Socks5Proxy copy = new Socks5Proxy(proxyIP, proxyPort);
|
|
140 copy.authMethods = this.authMethods; //same Hash, no copy
|
|
141 copy.chainProxy = this.chainProxy;
|
|
142 copy.resolveAddrLocally = this.resolveAddrLocally;
|
|
143 return copy;
|
|
144 }
|
|
145 /**
|
|
146 *
|
|
147 *
|
|
148 */
|
|
149 @Override
|
|
150 protected void startSession()throws SocksException {
|
|
151 super.startSession();
|
|
152 Authentication auth;
|
|
153 Socket ps = proxySocket; //The name is too long
|
|
154
|
|
155 try {
|
|
156 byte nMethods = (byte) authMethods.size(); //Number of methods
|
|
157 byte[] buf = new byte[2 + nMethods]; //2 is for VER,NMETHODS
|
|
158 buf[0] = (byte) version;
|
|
159 buf[1] = nMethods; //Number of methods
|
|
160 int i = 2;
|
|
161 Enumeration<Integer> ids = authMethods.keys();
|
|
162
|
|
163 while (ids.hasMoreElements())
|
|
164 buf[i++] = (byte)ids.nextElement().intValue();
|
|
165
|
|
166 out.write(buf);
|
|
167 out.flush();
|
|
168 int versionNumber = in.read();
|
|
169 selectedMethod = in.read();
|
|
170
|
|
171 if (versionNumber < 0 || selectedMethod < 0) {
|
|
172 //EOF condition was reached
|
|
173 endSession();
|
|
174 throw(new SocksException(SOCKS_PROXY_IO_ERROR,
|
|
175 "Connection to proxy lost."));
|
|
176 }
|
|
177
|
|
178 if (versionNumber < version) {
|
|
179 //What should we do??
|
|
180 }
|
|
181
|
|
182 if (selectedMethod == 0xFF) { //No method selected
|
|
183 ps.close();
|
|
184 throw(new SocksException(SOCKS_AUTH_NOT_SUPPORTED));
|
|
185 }
|
|
186
|
|
187 auth = getAuthenticationMethod(selectedMethod);
|
|
188
|
|
189 if (auth == null) {
|
|
190 //This shouldn't happen, unless method was removed by other
|
|
191 //thread, or the server stuffed up
|
|
192 throw(new SocksException(SOCKS_JUST_ERROR,
|
|
193 "Speciefied Authentication not found!"));
|
|
194 }
|
|
195
|
|
196 Object[] in_out = auth.doSocksAuthentication(selectedMethod, ps);
|
|
197
|
|
198 if (in_out == null) {
|
|
199 //Authentication failed by some reason
|
|
200 throw(new SocksException(SOCKS_AUTH_FAILURE));
|
|
201 }
|
|
202
|
|
203 //Most authentication methods are expected to return
|
|
204 //simply the input/output streams associated with
|
|
205 //the socket. However if the auth. method requires
|
|
206 //some kind of encryption/decryption being done on the
|
|
207 //connection it should provide classes to handle I/O.
|
|
208 in = (InputStream) in_out[0];
|
|
209 out = (OutputStream) in_out[1];
|
|
210
|
|
211 if (in_out.length > 2)
|
|
212 udp_encapsulation = (UDPEncapsulation) in_out[2];
|
|
213 }
|
|
214 catch (SocksException s_ex) {
|
|
215 throw s_ex;
|
|
216 }
|
|
217 catch (UnknownHostException uh_ex) {
|
|
218 throw(new SocksException(SOCKS_PROXY_NO_CONNECT));
|
|
219 }
|
|
220 catch (SocketException so_ex) {
|
|
221 throw(new SocksException(SOCKS_PROXY_NO_CONNECT));
|
|
222 }
|
|
223 catch (IOException io_ex) {
|
|
224 //System.err.println(io_ex);
|
|
225 throw(new SocksException(SOCKS_PROXY_IO_ERROR, "" + io_ex));
|
|
226 }
|
|
227 }
|
|
228
|
|
229 @Override
|
|
230 protected ProxyMessage formMessage(int cmd, InetAddress ip, int port) {
|
|
231 return new Socks5Message(cmd, ip, port);
|
|
232 }
|
|
233 @Override
|
|
234 protected ProxyMessage formMessage(int cmd, String host, int port)
|
|
235 throws UnknownHostException {
|
|
236 if (resolveAddrLocally)
|
|
237 return formMessage(cmd, InetAddress.getByName(host), port);
|
|
238 else
|
|
239 return new Socks5Message(cmd, host, port);
|
|
240 }
|
|
241 @Override
|
|
242 protected ProxyMessage formMessage(InputStream in)
|
|
243 throws SocksException,
|
|
244 IOException {
|
|
245 return new Socks5Message(in);
|
|
246 }
|
|
247
|
|
248 }
|