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 }