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

Revision 3102, 18.1 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.io.*;
3import java.net.*;
4import java.security.MessageDigest;
5import java.security.NoSuchAlgorithmException;
6
7import nu.fw.jeti.plugins.filetransfer.socks5.Socks5Send;
8import nu.fw.jeti.util.I18N;
9import nu.fw.jeti.util.Popups;
10
11
12/**
13    SOCKS4 and SOCKS5 proxy, handles both protocols simultaniously.
14    Implements all SOCKS commands, including UDP relaying.
15    <p>
16    In order to use it you will need to implement ServerAuthenticator
17    interface. There is an implementation of this interface which does
18    no authentication ServerAuthenticatorNone, but it is very dangerous
19    to use, as it will give access to your local network to anybody
20    in the world. One should never use this authentication scheme unless
21    one have pretty good reason to do so.
22    There is a couple of other authentication schemes in socks.server package.
23    @see socks.server.ServerAuthenticator
24*/
25public class ProxyServer implements Runnable{
26
27   ServerAuthenticator auth;
28   ProxyMessage msg = null;
29
30   Socket sock=null,remote_sock=null;
31   ServerSocket ss=null;
32   UDPRelayServer relayServer = null;
33   InputStream in; //,remote_in;
34   OutputStream out;//,remote_out;
35   File file;
36   String sha1Digest;
37   Thread pst;
38   Socks5Send send;
39   
40   int mode;
41   static final int START_MODE  = 0;
42   static final int ACCEPT_MODE = 1;
43   static final int PIPE_MODE   = 2;
44   static final int ABORT_MODE  = 3;
45
46   static final int BUF_SIZE    = 8192;
47
48  // Thread pipe_thread1,pipe_thread2;
49   long lastReadTime;
50
51   static int iddleTimeout      = 180000; //3 minutes
52   static int acceptTimeout     = 180000; //3 minutes
53
54   static PrintStream log = null;
55   static Proxy proxy;
56
57
58//Public Constructors
59/////////////////////
60
61
62   /**
63    Creates a proxy server with given Authentication scheme.
64    @param auth Authentication scheme to be used.
65    */
66   public ProxyServer(ServerAuthenticator auth){
67     this.auth = auth;
68   }
69
70//Other constructors
71////////////////////
72
73  public  ProxyServer(ServerAuthenticator auth,Socket s,File file,String digest,Socks5Send send){
74      this.auth  = auth;
75      this.sock  = s;
76      this.file = file;
77      this.send = send;
78      sha1Digest = digest;
79      mode = START_MODE;
80      setLog(System.out);
81      pst = new Thread(this);
82          pst.start();
83   }
84
85//Public methods
86/////////////////
87
88   /**
89     Set the logging stream. Specifying null disables logging.
90   */
91   public static void setLog(OutputStream out){
92      if(out == null){
93        log = null;
94      }else{
95        log = new PrintStream(out,true);
96      }
97
98      UDPRelayServer.log = log;
99   }
100   
101   /**
102    Set proxy.
103    <p>
104    Allows Proxy chaining so that one Proxy server is connected to another
105    and so on. If proxy supports SOCKSv4, then only some SOCKSv5 requests
106    can be handled, UDP would not work, however CONNECT and BIND will be
107    translated.
108
109    @param p Proxy which should be used to handle user requests.
110   */
111   public static void setProxy(Proxy p){
112      proxy =p;
113      UDPRelayServer.proxy = proxy;
114   }
115
116   /**
117    Get proxy.
118    @return Proxy wich is used to handle user requests.
119   */
120   public static Proxy getProxy(){
121      return proxy;
122   }
123
124   /**
125    Sets the timeout for connections, how long shoud server wait
126    for data to arrive before dropping the connection.<br>
127    Zero timeout implies infinity.<br>
128    Default timeout is 3 minutes.
129   */
130   public static void setIddleTimeout(int timeout){
131      iddleTimeout = timeout;
132   }
133   /**
134    Sets the timeout for BIND command, how long the server should
135    wait for the incoming connection.<br>
136    Zero timeout implies infinity.<br>
137    Default timeout is 3 minutes.
138   */
139   public static void setAcceptTimeout(int timeout){
140      acceptTimeout = timeout;
141   }
142
143   /**
144    Sets the timeout for UDPRelay server.<br>
145    Zero timeout implies infinity.<br>
146    Default timeout is 3 minutes.
147   */
148   public static void setUDPTimeout(int timeout){
149      UDPRelayServer.setTimeout(timeout);
150   }
151
152   /**
153     Sets the size of the datagrams used in the UDPRelayServer.<br>
154     Default size is 64K, a bit more than maximum possible size of the
155     datagram.
156    */
157   public static void setDatagramSize(int size){
158      UDPRelayServer.setDatagramSize(size);
159   }
160
161
162   /**
163     Start the Proxy server at given port.<br>
164     This methods blocks.
165    */
166   public void start(int port){
167      start(port,5,null);
168   }
169
170   /**
171     Create a server with the specified port, listen backlog, and local
172     IP address to bind to. The localIP argument can be used on a multi-homed
173     host for a ServerSocket that will only accept connect requests to one of
174     its addresses. If localIP is null, it will default accepting connections
175     on any/all local addresses. The port must be between 0 and 65535,
176     inclusive. <br>
177     This methods blocks.
178    */
179   public void start(int port,int backlog,InetAddress localIP){
180      try{
181        ss = new ServerSocket(port,backlog,localIP);
182        log("Starting SOCKS Proxy on:"+ss.getInetAddress().getHostAddress()+":"
183                                      +ss.getLocalPort());
184        while(true){
185          Socket s = ss.accept();
186          log("Accepted from:"+s.getInetAddress().getHostName()+":"
187                              +s.getPort());
188          ProxyServer ps = new ProxyServer(auth,s,null,null,null);
189          (new Thread(ps)).start();
190        }
191      }catch(IOException ioe){
192        ioe.printStackTrace();
193      }finally{
194      }
195   }
196
197   /**
198     Stop server operation.It would be wise to interrupt thread running the
199     server afterwards.
200    */
201   public void stop(){
202     try{
203       if(ss != null) ss.close();
204     }catch(IOException ioe){
205     }
206     pst.interrupt();
207   }
208
209//Runnable interface
210////////////////////
211   public void run(){
212      switch(mode){
213        case START_MODE:
214         try{
215           startSession();
216         }catch(IOException ioe){
217           handleException(ioe);
218           //ioe.printStackTrace();
219         }finally{
220           abort();
221           if(auth!=null) auth.endSession();
222           log("Main thread(client->remote)stopped.");
223         }
224        break;
225        case ACCEPT_MODE:
226          try{
227            doAccept();
228            mode = PIPE_MODE;
229            //pipe_thread1.interrupt(); //Tell other thread that connection have
230                                      //been accepted.
231           // System.out.println("accept pipe");
232           // pipe(remote_in,out);
233          }catch(IOException ioe){
234            //log("Accept exception:"+ioe);
235            handleException(ioe);
236          }finally{
237            abort();
238            log("Accept thread(remote->client) stopped");
239          }
240        break;
241        case PIPE_MODE:
242         try{
243                //System.out.println("pipe mode");
244            //pipe(remote_in,out);
245   
246        // }catch(IOException ioe){
247         }finally{
248            abort();
249            log("Support thread(remote->client) stopped");
250         }
251        break;
252        case ABORT_MODE:
253        break;
254        default:
255         log("Unexpected MODE "+mode);
256      }
257   }
258
259//Private methods
260/////////////////
261   private void startSession() throws IOException{
262     sock.setSoTimeout(iddleTimeout);
263
264     try{
265        auth = auth.startSession(sock);
266     }catch(IOException ioe){
267       log("Auth throwed exception:"+ioe);
268       auth = null;
269       return;
270     }
271
272     if(auth == null){ //Authentication failed
273        log("Authentication failed");
274        return;
275     }
276
277     in = auth.getInputStream();
278     out = auth.getOutputStream();
279
280     msg = readMsg(in);
281     handleRequest(msg);
282   }
283   
284   private void handleRequest(ProxyMessage msg)
285                throws IOException{
286      if(!auth.checkRequest(msg)) throw new
287                                  SocksException(Proxy.SOCKS_FAILURE);
288
289      if(msg.ip == null){
290        if(msg instanceof Socks5Message){
291         // msg.ip = InetAddress.getByName(msg.host);
292        }else
293          throw new SocksException(Proxy.SOCKS_FAILURE);
294      }
295      log(msg);
296
297      switch(msg.command){
298        case Proxy.SOCKS_CMD_CONNECT:
299          onConnect(msg);
300        break;
301        case Proxy.SOCKS_CMD_BIND:
302          //onBind(msg);
303        break;
304        case Proxy.SOCKS_CMD_UDP_ASSOCIATE:
305  //        onUDP(msg);
306        break;
307        default:
308          throw new SocksException(Proxy.SOCKS_CMD_NOT_SUPPORTED);
309      }
310   }
311
312   private void handleException(IOException ioe){
313       
314        Popups.messagePopup(I18N.gettext("filetransfer.Problem_during_file_transfer,_transfer_aborted"),I18N.gettext("filetransfer.File_Transfer"));
315        send.finished();
316       
317        ioe.printStackTrace();
318       
319      //If we couldn't read the request, return;
320      if(msg == null) return;
321      //If have been aborted by other thread
322      if(mode == ABORT_MODE) return;
323      //If the request was successfully completed, but exception happened later
324      if(mode == PIPE_MODE) return;
325
326      int error_code = Proxy.SOCKS_FAILURE;
327
328      if(ioe instanceof SocksException)
329          error_code = ((SocksException)ioe).errCode;
330      else if(ioe instanceof NoRouteToHostException)
331          error_code = Proxy.SOCKS_HOST_UNREACHABLE;
332      else if(ioe instanceof ConnectException)
333          error_code = Proxy.SOCKS_CONNECTION_REFUSED;
334      else if(ioe instanceof InterruptedIOException)
335          error_code = Proxy.SOCKS_TTL_EXPIRE;
336
337      if(error_code > Proxy.SOCKS_ADDR_NOT_SUPPORTED || error_code < 0){
338          error_code = Proxy.SOCKS_FAILURE;
339      }
340
341      sendErrorMessage(error_code);
342   }
343
344   private void onConnect(ProxyMessage msg) throws IOException{
345      Socket s;
346      ProxyMessage response = null;
347
348//      if(proxy == null)
349//         s = new Socket(msg.ip,msg.port);
350//      else
351//         s = new SocksSocket(proxy,msg.ip,msg.port);
352
353    //  System.out.println(sha1Digest);
354    //  System.out.println(msg.host);
355     
356     if(msg.host.equals(sha1Digest) && msg.port==0)
357     {
358                 log("Connected to "+msg.host+":"+msg.port);
359       
360              if(msg instanceof Socks5Message){
361                response = new Socks5Message(Proxy.SOCKS_SUCCESS,
362                                                                                msg.host,
363                                                msg.port);
364              }
365               
366              //response = new Socks4Message(Socks4Message.REPLY_OK, s.getLocalAddress(),s.getLocalPort());
367                     
368              response.write(out);
369              startPipe(null);
370          }
371     else
372     {
373        System.out.println("invalid entry");
374        throw new ConnectException();
375     }
376       
377   }
378
379   private void onBind(ProxyMessage msg) throws IOException{
380      ProxyMessage response = null;
381
382      if(proxy == null)
383        ss = new ServerSocket(0);
384      else
385        ss = new SocksServerSocket(proxy, msg.ip, msg.port);
386
387      ss.setSoTimeout(acceptTimeout);
388
389      log("Trying accept on "+ss.getInetAddress()+":"+ss.getLocalPort());
390
391      if(msg.version == 5)
392         response = new Socks5Message(Proxy.SOCKS_SUCCESS,ss.getInetAddress(),
393                                                          ss.getLocalPort());
394      else
395         response = new Socks4Message(Socks4Message.REPLY_OK,
396                                      ss.getInetAddress(),
397                                      ss.getLocalPort());
398      response.write(out);
399
400      mode = ACCEPT_MODE;
401
402      //pipe_thread1 = Thread.currentThread();
403      //pipe_thread2 = new Thread(this);
404      //pipe_thread2.start();
405
406      //Make timeout infinit.
407      sock.setSoTimeout(0);
408      int eof=0;
409
410      try{
411        while((eof=in.read())>=0){
412          if(mode != ACCEPT_MODE){
413            if(mode != PIPE_MODE) return;//Accept failed
414
415            //remote_out.write(eof);
416            break;
417          }
418        }
419      }catch(EOFException eofe){
420        //System.out.println("EOF exception");
421        return;//Connection closed while we were trying to accept.
422      }catch(InterruptedIOException iioe){
423        //Accept thread interrupted us.
424        //System.out.println("Interrupted");
425        if(mode != PIPE_MODE)
426          return;//If accept thread was not successfull return.
427      }finally{
428        //System.out.println("Finnaly!");
429      }
430
431      if(eof < 0)//Connection closed while we were trying to accept;
432        return;
433     
434      //Do not restore timeout, instead timeout is set on the
435      //remote socket. It does not make any difference.
436
437     // pipe(in,remote_out);
438   }
439
440   private void onUDP(ProxyMessage msg) throws IOException{
441      if(msg.ip.getHostAddress().equals("0.0.0.0"))
442         msg.ip = sock.getInetAddress();
443      log("Creating UDP relay server for "+msg.ip+":"+msg.port);
444      relayServer = new UDPRelayServer(msg.ip,msg.port,
445                        Thread.currentThread(),sock,auth);
446
447      ProxyMessage response;
448
449      response = new Socks5Message(Proxy.SOCKS_SUCCESS,
450                                   relayServer.relayIP,relayServer.relayPort);
451
452      response.write(out);
453
454      relayServer.start();
455
456      //Make timeout infinit.
457      sock.setSoTimeout(0);
458      try{
459        while(in.read()>=0) /*do nothing*/;
460      }catch(EOFException eofe){
461      }
462   }
463
464//Private methods
465//////////////////
466
467   private void doAccept() throws IOException{
468   //   System.out.println("doaccept");
469      Socket s;
470      long startTime = System.currentTimeMillis();
471
472      while(true){
473         s = ss.accept();
474         if(s.getInetAddress().equals(msg.ip)){
475            //got the connection from the right host
476            //Close listenning socket.
477            ss.close();
478            break;
479         }else if(ss instanceof SocksServerSocket){
480            //We can't accept more then one connection
481            s.close();
482            ss.close();
483            throw new SocksException(Proxy.SOCKS_FAILURE);
484         }else{
485            if(acceptTimeout!=0){ //If timeout is not infinit
486               int newTimeout = acceptTimeout-(int)(System.currentTimeMillis()-
487                                                           startTime);
488               if(newTimeout <= 0) throw new InterruptedIOException(
489                                "In doAccept()");
490               ss.setSoTimeout(newTimeout);
491            }
492            s.close(); //Drop all connections from other hosts
493         }
494      }
495
496      //Accepted connection
497      remote_sock = s;
498  //    remote_in = s.getInputStream();
499   //   remote_out = s.getOutputStream();
500
501      //Set timeout
502      remote_sock.setSoTimeout(iddleTimeout);
503
504      log("Accepted from "+s.getInetAddress()+":"+s.getPort());
505
506      ProxyMessage response;
507
508      if(msg.version == 5)
509         response = new Socks5Message(Proxy.SOCKS_SUCCESS, s.getInetAddress(),
510                                                           s.getPort());
511      else
512         response = new Socks4Message(Socks4Message.REPLY_OK,
513                                      s.getInetAddress(), s.getPort());
514      response.write(out);
515   }
516
517   private ProxyMessage readMsg(InputStream in) throws IOException{
518      PushbackInputStream push_in; 
519      if(in instanceof PushbackInputStream)
520         push_in = (PushbackInputStream) in;
521      else
522         push_in = new PushbackInputStream(in);
523
524      int version = push_in.read();
525      push_in.unread(version);
526
527
528      ProxyMessage msg;
529
530      if(version == 5){
531        msg = new Socks5Message(push_in,false);
532      }else if(version == 4){
533        msg = new Socks4Message(push_in,false);
534      }else{
535        throw new SocksException(Proxy.SOCKS_FAILURE);
536      }
537      return msg;
538   }
539
540   private void startPipe(Socket s){
541      mode = PIPE_MODE;
542      remote_sock = s;
543      try{
544  ///       remote_in = s.getInputStream();
545  //       remote_out = s.getOutputStream();
546        // pipe_thread1 = Thread.currentThread();
547        // pipe_thread2 = new Thread(this);
548        // pipe_thread2.start();
549        // pipe(in,remote_out);
550        pipe(out);
551      }catch(IOException ioe){
552      }
553   }
554
555   private void sendErrorMessage(int error_code){
556      ProxyMessage err_msg;
557      if(msg instanceof Socks4Message)
558         err_msg = new Socks4Message(Socks4Message.REPLY_REJECTED);
559      else
560         err_msg = new Socks5Message(error_code);
561      try{
562         err_msg.write(out);
563      }catch(IOException ioe){}
564   }
565
566   private synchronized void abort(){
567      if(mode == ABORT_MODE) return;
568      mode = ABORT_MODE;
569      try{
570         log("Aborting operation");
571         if(remote_sock != null) remote_sock.close();
572         if(sock != null) sock.close();
573         if(relayServer!=null) relayServer.stop();
574         if(ss!=null) ss.close();
575  //       if(pipe_thread1 != null) pipe_thread1.interrupt();
576        // if(pipe_thread2 != null) pipe_thread2.interrupt();
577      }catch(IOException ioe){}
578   }
579
580   static final void log(String s){
581     if(log != null){
582       log.println(s);
583       log.flush();
584     }
585   }
586
587   static final void log(ProxyMessage msg){
588      log("Request version:"+msg.version+
589          "\tCommand: "+command2String(msg.command));
590      log("IP:"+msg.ip +"\tPort:"+msg.port+
591         (msg.version==4?"\tUser:"+msg.user:""));
592   }
593   
594   private void pipe(OutputStream out) throws IOException
595        {
596                InputStream is = new FileInputStream(file.getAbsolutePath());
597                byte[] buf = new byte[BUF_SIZE];
598                //System.out.println("begin sending");
599                try
600                {
601                        int n;
602                        while ((n = is.read(buf)) > 0)
603                        {
604                                out.write(buf, 0, n);
605                                out.flush();
606                                send.addBytes(n);
607                                if (Thread.interrupted()) { return; }
608                                Thread.yield();
609                        }
610                        send.finished();
611                } finally
612                {
613                        is.close();
614                }
615        }
616   
617
618//   private void pipe(InputStream in,OutputStream out) throws IOException{
619//      lastReadTime = System.currentTimeMillis();
620//      byte[] buf = new byte[BUF_SIZE];
621//      int len = 0;
622//      while(len >= 0){
623//         try{
624//           if(len!=0){
625//             out.write(buf,0,len);
626//             out.flush();
627//           }
628//           len= in.read(buf);
629//           lastReadTime = System.currentTimeMillis();
630//         }catch(InterruptedIOException iioe){
631//           if(iddleTimeout == 0) return;//Other thread interrupted us.
632//           long timeSinceRead = System.currentTimeMillis() - lastReadTime;
633//           if(timeSinceRead >= iddleTimeout - 1000) //-1s for adjustment.
634//              return;
635//           len = 0;
636//
637//         }
638//      }
639//   }
640   static final String command_names[] = {"CONNECT","BIND","UDP_ASSOCIATE"};
641
642   static final String command2String(int cmd){
643      if(cmd > 0 && cmd < 4) return command_names[cmd-1];
644      else return "Unknown Command "+cmd;
645   }
646}
Note: See TracBrowser for help on using the repository browser.