KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > socks > Socks5DatagramSocket


1 package socks;
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       if(p == null) throw new SocksException(Proxy.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(Proxy.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 JavaDoc 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      Proxy 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 JavaDoc 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          Proxy.setDefaultProxy(proxyHost,proxyPort);
431          Proxy p = Proxy.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 }
488
Popular Tags