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