Mercurial > 510Connectbot
comparison src/net/sourceforge/jsocks/Socks5DatagramSocket.java @ 0:0ce5cc452d02
initial version
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Thu, 22 May 2014 10:41:19 -0700 |
parents | |
children | 205ee2873330 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:0ce5cc452d02 |
---|---|
1 package net.sourceforge.jsocks; | |
2 import java.net.*; | |
3 import java.io.*; | |
4 | |
5 /** | |
6 Datagram socket to interract through the firewall.<BR> | |
7 Can be used same way as the normal DatagramSocket. One should | |
8 be carefull though with the datagram sizes used, as additional data | |
9 is present in both incomming and outgoing datagrams. | |
10 <p> | |
11 SOCKS5 protocol allows to send host address as either: | |
12 <ul> | |
13 <li> IPV4, normal 4 byte address. (10 bytes header size) | |
14 <li> IPV6, version 6 ip address (not supported by Java as for now). | |
15 22 bytes header size. | |
16 <li> Host name,(7+length of the host name bytes header size). | |
17 </ul> | |
18 As with other Socks equivalents, direct addresses are handled | |
19 transparently, that is data will be send directly when required | |
20 by the proxy settings. | |
21 <p> | |
22 <b>NOTE:</b><br> | |
23 Unlike other SOCKS Sockets, it <b>does not</b> support proxy chaining, | |
24 and will throw an exception if proxy has a chain proxy attached. The | |
25 reason for that is not my laziness, but rather the restrictions of | |
26 the SOCKSv5 protocol. Basicaly SOCKSv5 proxy server, needs to know from | |
27 which host:port datagrams will be send for association, and returns address | |
28 to which datagrams should be send by the client, but it does not | |
29 inform client from which host:port it is going to send datagrams, in fact | |
30 there is even no guarantee they will be send at all and from the same address | |
31 each time. | |
32 | |
33 */ | |
34 public class Socks5DatagramSocket extends DatagramSocket { | |
35 | |
36 InetAddress relayIP; | |
37 int relayPort; | |
38 Socks5Proxy proxy; | |
39 private boolean server_mode = false; | |
40 UDPEncapsulation encapsulation; | |
41 | |
42 | |
43 /** | |
44 Construct Datagram socket for communication over SOCKS5 proxy | |
45 server. This constructor uses default proxy, the one set with | |
46 Proxy.setDefaultProxy() method. If default proxy is not set or | |
47 it is set to version4 proxy, which does not support datagram | |
48 forwarding, throws SocksException. | |
49 | |
50 */ | |
51 public Socks5DatagramSocket() throws SocksException, | |
52 IOException { | |
53 this(Proxy.defaultProxy, 0, null); | |
54 } | |
55 /** | |
56 Construct Datagram socket for communication over SOCKS5 proxy | |
57 server. And binds it to the specified local port. | |
58 This constructor uses default proxy, the one set with | |
59 Proxy.setDefaultProxy() method. If default proxy is not set or | |
60 it is set to version4 proxy, which does not support datagram | |
61 forwarding, throws SocksException. | |
62 */ | |
63 public Socks5DatagramSocket(int port) throws SocksException, | |
64 IOException { | |
65 this(Proxy.defaultProxy, port, null); | |
66 } | |
67 /** | |
68 Construct Datagram socket for communication over SOCKS5 proxy | |
69 server. And binds it to the specified local port and address. | |
70 This constructor uses default proxy, the one set with | |
71 Proxy.setDefaultProxy() method. If default proxy is not set or | |
72 it is set to version4 proxy, which does not support datagram | |
73 forwarding, throws SocksException. | |
74 */ | |
75 public Socks5DatagramSocket(int port, InetAddress ip) throws SocksException, | |
76 IOException { | |
77 this(Proxy.defaultProxy, port, ip); | |
78 } | |
79 | |
80 /** | |
81 Constructs datagram socket for communication over specified proxy. | |
82 And binds it to the given local address and port. Address of null | |
83 and port of 0, signify any availabale port/address. | |
84 Might throw SocksException, if: | |
85 <ol> | |
86 <li> Given version of proxy does not support UDP_ASSOCIATE. | |
87 <li> Proxy can't be reached. | |
88 <li> Authorization fails. | |
89 <li> Proxy does not want to perform udp forwarding, for any reason. | |
90 </ol> | |
91 Might throw IOException if binding dtagram socket to given address/port | |
92 fails. | |
93 See java.net.DatagramSocket for more details. | |
94 */ | |
95 public Socks5DatagramSocket(Proxy p, int port, InetAddress ip) | |
96 throws SocksException, | |
97 IOException { | |
98 super(port, ip); | |
99 | |
100 if (p == null) throw new SocksException(Proxy.SOCKS_NO_PROXY); | |
101 | |
102 if (!(p instanceof Socks5Proxy)) | |
103 throw new SocksException(-1, "Datagram Socket needs Proxy version 5"); | |
104 | |
105 if (p.chainProxy != null) | |
106 throw new SocksException(Proxy.SOCKS_JUST_ERROR, | |
107 "Datagram Sockets do not support proxy chaining."); | |
108 | |
109 proxy = (Socks5Proxy) p.copy(); | |
110 ProxyMessage msg = proxy.udpAssociate(super.getLocalAddress(), | |
111 super.getLocalPort()); | |
112 relayIP = msg.ip; | |
113 | |
114 if (relayIP.getHostAddress().equals("0.0.0.0")) relayIP = proxy.proxyIP; | |
115 | |
116 relayPort = msg.port; | |
117 encapsulation = proxy.udp_encapsulation; | |
118 //debug("Datagram Socket:"+getLocalAddress()+":"+getLocalPort()+"\n"); | |
119 //debug("Socks5Datagram: "+relayIP+":"+relayPort+"\n"); | |
120 } | |
121 | |
122 /** | |
123 Used by UDPRelayServer. | |
124 */ | |
125 Socks5DatagramSocket(boolean server_mode, UDPEncapsulation encapsulation, | |
126 InetAddress relayIP, int relayPort) | |
127 throws IOException { | |
128 super(); | |
129 this.server_mode = server_mode; | |
130 this.relayIP = relayIP; | |
131 this.relayPort = relayPort; | |
132 this.encapsulation = encapsulation; | |
133 this.proxy = null; | |
134 } | |
135 | |
136 /** | |
137 Sends the Datagram either through the proxy or directly depending | |
138 on current proxy settings and destination address. <BR> | |
139 | |
140 <B> NOTE: </B> DatagramPacket size should be at least 10 bytes less | |
141 than the systems limit. | |
142 | |
143 <P> | |
144 See documentation on java.net.DatagramSocket | |
145 for full details on how to use this method. | |
146 @param dp Datagram to send. | |
147 @throws IOException If error happens with I/O. | |
148 */ | |
149 public void send(DatagramPacket dp) throws IOException { | |
150 //If the host should be accessed directly, send it as is. | |
151 if (!server_mode) { | |
152 super.send(dp); | |
153 //debug("Sending directly:"); | |
154 return; | |
155 } | |
156 | |
157 byte[] head = formHeader(dp.getAddress(), dp.getPort()); | |
158 byte[] buf = new byte[head.length + dp.getLength()]; | |
159 byte[] data = dp.getData(); | |
160 //Merge head and data | |
161 System.arraycopy(head, 0, buf, 0, head.length); | |
162 //System.arraycopy(data,dp.getOffset(),buf,head.length,dp.getLength()); | |
163 System.arraycopy(data, 0, buf, head.length, dp.getLength()); | |
164 | |
165 if (encapsulation != null) | |
166 buf = encapsulation.udpEncapsulate(buf, true); | |
167 | |
168 super.send(new DatagramPacket(buf, buf.length, relayIP, relayPort)); | |
169 } | |
170 /** | |
171 This method allows to send datagram packets with address type DOMAINNAME. | |
172 SOCKS5 allows to specify host as names rather than ip addresses.Using | |
173 this method one can send udp datagrams through the proxy, without having | |
174 to know the ip address of the destination host. | |
175 <p> | |
176 If proxy specified for that socket has an option resolveAddrLocally set | |
177 to true host will be resolved, and the datagram will be send with address | |
178 type IPV4, if resolve fails, UnknownHostException is thrown. | |
179 @param dp Datagram to send, it should contain valid port and data | |
180 @param host Host name to which datagram should be send. | |
181 @throws IOException If error happens with I/O, or the host can't be | |
182 resolved when proxy settings say that hosts should be resolved locally. | |
183 @see Socks5Proxy#resolveAddrLocally(boolean) | |
184 */ | |
185 public void send(DatagramPacket dp, String host) throws IOException { | |
186 dp.setAddress(InetAddress.getByName(host)); | |
187 super.send(dp); | |
188 } | |
189 | |
190 /** | |
191 * Receives udp packet. If packet have arrived from the proxy relay server, | |
192 * it is processed and address and port of the packet are set to the | |
193 * address and port of sending host.<BR> | |
194 * If the packet arrived from anywhere else it is not changed.<br> | |
195 * <B> NOTE: </B> DatagramPacket size should be at least 10 bytes bigger | |
196 * than the largest packet you expect (this is for IPV4 addresses). | |
197 * For hostnames and IPV6 it is even more. | |
198 @param dp Datagram in which all relevent information will be copied. | |
199 */ | |
200 public void receive(DatagramPacket dp) throws IOException { | |
201 super.receive(dp); | |
202 | |
203 if (server_mode) { | |
204 //Drop all datagrams not from relayIP/relayPort | |
205 int init_length = dp.getLength(); | |
206 int initTimeout = getSoTimeout(); | |
207 long startTime = System.currentTimeMillis(); | |
208 | |
209 while (!relayIP.equals(dp.getAddress()) || | |
210 relayPort != dp.getPort()) { | |
211 //Restore datagram size | |
212 dp.setLength(init_length); | |
213 | |
214 //If there is a non-infinit timeout on this socket | |
215 //Make sure that it happens no matter how often unexpected | |
216 //packets arrive. | |
217 if (initTimeout != 0) { | |
218 int newTimeout = initTimeout - (int)(System.currentTimeMillis() - | |
219 startTime); | |
220 | |
221 if (newTimeout <= 0) throw new InterruptedIOException( | |
222 "In Socks5DatagramSocket->receive()"); | |
223 | |
224 setSoTimeout(newTimeout); | |
225 } | |
226 | |
227 super.receive(dp); | |
228 } | |
229 | |
230 //Restore timeout settings | |
231 if (initTimeout != 0) setSoTimeout(initTimeout); | |
232 } | |
233 else if (!relayIP.equals(dp.getAddress()) || | |
234 relayPort != dp.getPort()) | |
235 return; // Recieved direct packet | |
236 | |
237 //If the datagram is not from the relay server, return it it as is. | |
238 byte[] data; | |
239 data = dp.getData(); | |
240 | |
241 if (encapsulation != null) | |
242 data = encapsulation.udpEncapsulate(data, false); | |
243 | |
244 int offset = 0; //Java 1.1 | |
245 //int offset = dp.getOffset(); //Java 1.2 | |
246 ByteArrayInputStream bIn = new ByteArrayInputStream(data, offset, | |
247 dp.getLength()); | |
248 ProxyMessage msg = new Socks5Message(bIn); | |
249 dp.setPort(msg.port); | |
250 dp.setAddress(msg.getInetAddress()); | |
251 //what wasn't read by the Message is the data | |
252 int data_length = bIn.available(); | |
253 //Shift data to the left | |
254 System.arraycopy(data, offset + dp.getLength() - data_length, | |
255 data, offset, data_length); | |
256 dp.setLength(data_length); | |
257 } | |
258 | |
259 /** | |
260 * Returns port assigned by the proxy, to which datagrams are relayed. | |
261 * It is not the same port to which other party should send datagrams. | |
262 @return Port assigned by socks server to which datagrams are send | |
263 for association. | |
264 */ | |
265 public int getLocalPort() { | |
266 if (server_mode) return super.getLocalPort(); | |
267 | |
268 return relayPort; | |
269 } | |
270 /** | |
271 * Address assigned by the proxy, to which datagrams are send for relay. | |
272 * It is not necesseraly the same address, to which other party should send | |
273 * datagrams. | |
274 @return Address to which datagrams are send for association. | |
275 */ | |
276 public InetAddress getLocalAddress() { | |
277 if (server_mode) return super.getLocalAddress(); | |
278 | |
279 return relayIP; | |
280 } | |
281 | |
282 /** | |
283 * Closes datagram socket, and proxy connection. | |
284 */ | |
285 public void close() { | |
286 if (!server_mode) proxy.endSession(); | |
287 | |
288 super.close(); | |
289 } | |
290 | |
291 /** | |
292 This method checks wether proxy still runs udp forwarding service | |
293 for this socket. | |
294 <p> | |
295 This methods checks wether the primary connection to proxy server | |
296 is active. If it is, chances are that proxy continues to forward | |
297 datagrams being send from this socket. If it was closed, most likely | |
298 datagrams are no longer being forwarded by the server. | |
299 <p> | |
300 Proxy might decide to stop forwarding datagrams, in which case it | |
301 should close primary connection. This method allows to check, wether | |
302 this have been done. | |
303 <p> | |
304 You can specify timeout for which we should be checking EOF condition | |
305 on the primary connection. Timeout is in milliseconds. Specifying 0 as | |
306 timeout implies infinity, in which case method will block, until | |
307 connection to proxy is closed or an error happens, and then return false. | |
308 <p> | |
309 One possible scenario is to call isProxyactive(0) in separate thread, | |
310 and once it returned notify other threads about this event. | |
311 | |
312 @param timeout For how long this method should block, before returning. | |
313 @return true if connection to proxy is active, false if eof or error | |
314 condition have been encountered on the connection. | |
315 */ | |
316 public boolean isProxyAlive(int timeout) { | |
317 if (server_mode) return false; | |
318 | |
319 if (proxy != null) { | |
320 try { | |
321 proxy.proxySocket.setSoTimeout(timeout); | |
322 int eof = proxy.in.read(); | |
323 | |
324 if (eof < 0) return false; // EOF encountered. | |
325 else return true; // This really should not happen | |
326 } | |
327 catch (InterruptedIOException iioe) { | |
328 return true; // read timed out. | |
329 } | |
330 catch (IOException ioe) { | |
331 return false; | |
332 } | |
333 } | |
334 | |
335 return false; | |
336 } | |
337 | |
338 //PRIVATE METHODS | |
339 ////////////////// | |
340 | |
341 | |
342 private byte[] formHeader(InetAddress ip, int port) { | |
343 Socks5Message request = new Socks5Message(0, ip, port); | |
344 request.data[0] = 0; | |
345 return request.data; | |
346 } | |
347 | |
348 | |
349 /*====================================================================== | |
350 | |
351 //Mainly Test functions | |
352 ////////////////////// | |
353 | |
354 private String bytes2String(byte[] b){ | |
355 String s=""; | |
356 char[] hex_digit = { '0','1','2','3','4','5','6','7','8','9', | |
357 'A','B','C','D','E','F'}; | |
358 for(int i=0;i<b.length;++i){ | |
359 int i1 = (b[i] & 0xF0) >> 4; | |
360 int i2 = b[i] & 0xF; | |
361 s+=hex_digit[i1]; | |
362 s+=hex_digit[i2]; | |
363 s+=" "; | |
364 } | |
365 return s; | |
366 } | |
367 private static final void debug(String s){ | |
368 if(DEBUG) | |
369 System.out.print(s); | |
370 } | |
371 | |
372 private static final boolean DEBUG = true; | |
373 | |
374 | |
375 public static void usage(){ | |
376 System.err.print( | |
377 "Usage: java Socks.SocksDatagramSocket host port [socksHost socksPort]\n"); | |
378 } | |
379 | |
380 static final int defaultProxyPort = 1080; //Default Port | |
381 static final String defaultProxyHost = "www-proxy"; //Default proxy | |
382 | |
383 public static void main(String args[]){ | |
384 int port; | |
385 String host; | |
386 int proxyPort; | |
387 String proxyHost; | |
388 InetAddress ip; | |
389 | |
390 if(args.length > 1 && args.length < 5){ | |
391 try{ | |
392 | |
393 host = args[0]; | |
394 port = Integer.parseInt(args[1]); | |
395 | |
396 proxyPort =(args.length > 3)? Integer.parseInt(args[3]) | |
397 : defaultProxyPort; | |
398 | |
399 host = args[0]; | |
400 ip = InetAddress.getByName(host); | |
401 | |
402 proxyHost =(args.length > 2)? args[2] | |
403 : defaultProxyHost; | |
404 | |
405 Proxy.setDefaultProxy(proxyHost,proxyPort); | |
406 Proxy p = Proxy.getDefaultProxy(); | |
407 p.addDirect("lux"); | |
408 | |
409 | |
410 DatagramSocket ds = new Socks5DatagramSocket(); | |
411 | |
412 | |
413 BufferedReader in = new BufferedReader( | |
414 new InputStreamReader(System.in)); | |
415 String s; | |
416 | |
417 System.out.print("Enter line:"); | |
418 s = in.readLine(); | |
419 | |
420 while(s != null){ | |
421 byte[] data = (s+"\r\n").getBytes(); | |
422 DatagramPacket dp = new DatagramPacket(data,0,data.length, | |
423 ip,port); | |
424 System.out.println("Sending to: "+ip+":"+port); | |
425 ds.send(dp); | |
426 dp = new DatagramPacket(new byte[1024],1024); | |
427 | |
428 System.out.println("Trying to recieve on port:"+ | |
429 ds.getLocalPort()); | |
430 ds.receive(dp); | |
431 System.out.print("Recieved:\n"+ | |
432 "From:"+dp.getAddress()+":"+dp.getPort()+ | |
433 "\n\n"+ | |
434 new String(dp.getData(),dp.getOffset(),dp.getLength())+"\n" | |
435 ); | |
436 System.out.print("Enter line:"); | |
437 s = in.readLine(); | |
438 | |
439 } | |
440 ds.close(); | |
441 System.exit(1); | |
442 | |
443 }catch(SocksException s_ex){ | |
444 System.err.println("SocksException:"+s_ex); | |
445 s_ex.printStackTrace(); | |
446 System.exit(1); | |
447 }catch(IOException io_ex){ | |
448 io_ex.printStackTrace(); | |
449 System.exit(1); | |
450 }catch(NumberFormatException num_ex){ | |
451 usage(); | |
452 num_ex.printStackTrace(); | |
453 System.exit(1); | |
454 } | |
455 | |
456 }else{ | |
457 usage(); | |
458 } | |
459 } | |
460 */ | |
461 | |
462 } |