0
|
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 }
|