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 }