source: branches/2.2/jabberit_messenger/java_source/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Socks5DatagramSocket.java @ 3102

Revision 3102, 16.7 KB checked in by amuller, 14 years ago (diff)

Ticket #986 - Efetuado merge para o Branch 2.2( atualizacao do modulo)

  • Property svn:executable set to *
Line 
1package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
2import java.net.*;
3import 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 */
34public 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 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 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}
Note: See TracBrowser for help on using the repository browser.