Home | History | Annotate | Download | only in proxy
      1 /**
      2  * $RCSfile$
      3  * $Revision$
      4  * $Date$
      5  *
      6  * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *     http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 package org.jivesoftware.smack.proxy;
     19 
     20 import java.io.IOException;
     21 import java.io.InputStream;
     22 import java.io.OutputStream;
     23 import java.net.InetAddress;
     24 import java.net.Socket;
     25 import java.net.UnknownHostException;
     26 import javax.net.SocketFactory;
     27 
     28 /**
     29  * Socket factory for Socks5 proxy
     30  *
     31  * @author Atul Aggarwal
     32  */
     33 public class Socks5ProxySocketFactory
     34     extends SocketFactory
     35 {
     36     private ProxyInfo proxy;
     37 
     38     public Socks5ProxySocketFactory(ProxyInfo proxy)
     39     {
     40         this.proxy = proxy;
     41     }
     42 
     43     public Socket createSocket(String host, int port)
     44         throws IOException, UnknownHostException
     45     {
     46         return socks5ProxifiedSocket(host,port);
     47     }
     48 
     49     public Socket createSocket(String host ,int port, InetAddress localHost,
     50                                 int localPort)
     51         throws IOException, UnknownHostException
     52     {
     53 
     54         return socks5ProxifiedSocket(host,port);
     55 
     56     }
     57 
     58     public Socket createSocket(InetAddress host, int port)
     59         throws IOException
     60     {
     61 
     62         return socks5ProxifiedSocket(host.getHostAddress(),port);
     63 
     64     }
     65 
     66     public Socket createSocket( InetAddress address, int port,
     67                                 InetAddress localAddress, int localPort)
     68         throws IOException
     69     {
     70 
     71         return socks5ProxifiedSocket(address.getHostAddress(),port);
     72 
     73     }
     74 
     75     private Socket socks5ProxifiedSocket(String host, int port)
     76         throws IOException
     77     {
     78         Socket socket = null;
     79         InputStream in = null;
     80         OutputStream out = null;
     81         String proxy_host = proxy.getProxyAddress();
     82         int proxy_port = proxy.getProxyPort();
     83         String user = proxy.getProxyUsername();
     84         String passwd = proxy.getProxyPassword();
     85 
     86         try
     87         {
     88             socket=new Socket(proxy_host, proxy_port);
     89             in=socket.getInputStream();
     90             out=socket.getOutputStream();
     91 
     92             socket.setTcpNoDelay(true);
     93 
     94             byte[] buf=new byte[1024];
     95             int index=0;
     96 
     97 /*
     98                    +----+----------+----------+
     99                    |VER | NMETHODS | METHODS  |
    100                    +----+----------+----------+
    101                    | 1  |    1     | 1 to 255 |
    102                    +----+----------+----------+
    103 
    104    The VER field is set to X'05' for this version of the protocol.  The
    105    NMETHODS field contains the number of method identifier octets that
    106    appear in the METHODS field.
    107 
    108    The values currently defined for METHOD are:
    109 
    110           o  X'00' NO AUTHENTICATION REQUIRED
    111           o  X'01' GSSAPI
    112           o  X'02' USERNAME/PASSWORD
    113           o  X'03' to X'7F' IANA ASSIGNED
    114           o  X'80' to X'FE' RESERVED FOR PRIVATE METHODS
    115           o  X'FF' NO ACCEPTABLE METHODS
    116 */
    117 
    118             buf[index++]=5;
    119 
    120             buf[index++]=2;
    121             buf[index++]=0;           // NO AUTHENTICATION REQUIRED
    122             buf[index++]=2;           // USERNAME/PASSWORD
    123 
    124             out.write(buf, 0, index);
    125 
    126 /*
    127     The server selects from one of the methods given in METHODS, and
    128     sends a METHOD selection message:
    129 
    130                          +----+--------+
    131                          |VER | METHOD |
    132                          +----+--------+
    133                          | 1  |   1    |
    134                          +----+--------+
    135 */
    136       //in.read(buf, 0, 2);
    137             fill(in, buf, 2);
    138 
    139             boolean check=false;
    140             switch((buf[1])&0xff)
    141             {
    142                 case 0:                // NO AUTHENTICATION REQUIRED
    143                     check=true;
    144                     break;
    145                 case 2:                // USERNAME/PASSWORD
    146                     if(user==null || passwd==null)
    147                     {
    148                         break;
    149                     }
    150 
    151 /*
    152    Once the SOCKS V5 server has started, and the client has selected the
    153    Username/Password Authentication protocol, the Username/Password
    154    subnegotiation begins.  This begins with the client producing a
    155    Username/Password request:
    156 
    157            +----+------+----------+------+----------+
    158            |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
    159            +----+------+----------+------+----------+
    160            | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
    161            +----+------+----------+------+----------+
    162 
    163    The VER field contains the current version of the subnegotiation,
    164    which is X'01'. The ULEN field contains the length of the UNAME field
    165    that follows. The UNAME field contains the username as known to the
    166    source operating system. The PLEN field contains the length of the
    167    PASSWD field that follows. The PASSWD field contains the password
    168    association with the given UNAME.
    169 */
    170                     index=0;
    171                     buf[index++]=1;
    172                     buf[index++]=(byte)(user.length());
    173                     System.arraycopy(user.getBytes(), 0, buf, index,
    174                         user.length());
    175                     index+=user.length();
    176                     buf[index++]=(byte)(passwd.length());
    177                     System.arraycopy(passwd.getBytes(), 0, buf, index,
    178                         passwd.length());
    179                     index+=passwd.length();
    180 
    181                     out.write(buf, 0, index);
    182 
    183 /*
    184    The server verifies the supplied UNAME and PASSWD, and sends the
    185    following response:
    186 
    187                         +----+--------+
    188                         |VER | STATUS |
    189                         +----+--------+
    190                         | 1  |   1    |
    191                         +----+--------+
    192 
    193    A STATUS field of X'00' indicates success. If the server returns a
    194    `failure' (STATUS value other than X'00') status, it MUST close the
    195    connection.
    196 */
    197                     //in.read(buf, 0, 2);
    198                     fill(in, buf, 2);
    199                     if(buf[1]==0)
    200                     {
    201                         check=true;
    202                     }
    203                     break;
    204                 default:
    205             }
    206 
    207             if(!check)
    208             {
    209                 try
    210                 {
    211                     socket.close();
    212                 }
    213                 catch(Exception eee)
    214                 {
    215                 }
    216                 throw new ProxyException(ProxyInfo.ProxyType.SOCKS5,
    217                     "fail in SOCKS5 proxy");
    218             }
    219 
    220 /*
    221       The SOCKS request is formed as follows:
    222 
    223         +----+-----+-------+------+----------+----------+
    224         |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
    225         +----+-----+-------+------+----------+----------+
    226         | 1  |  1  | X'00' |  1   | Variable |    2     |
    227         +----+-----+-------+------+----------+----------+
    228 
    229       Where:
    230 
    231       o  VER    protocol version: X'05'
    232       o  CMD
    233          o  CONNECT X'01'
    234          o  BIND X'02'
    235          o  UDP ASSOCIATE X'03'
    236       o  RSV    RESERVED
    237          o  ATYP   address type of following address
    238          o  IP V4 address: X'01'
    239          o  DOMAINNAME: X'03'
    240          o  IP V6 address: X'04'
    241       o  DST.ADDR       desired destination address
    242       o  DST.PORT desired destination port in network octet
    243          order
    244 */
    245 
    246             index=0;
    247             buf[index++]=5;
    248             buf[index++]=1;       // CONNECT
    249             buf[index++]=0;
    250 
    251             byte[] hostb=host.getBytes();
    252             int len=hostb.length;
    253             buf[index++]=3;      // DOMAINNAME
    254             buf[index++]=(byte)(len);
    255             System.arraycopy(hostb, 0, buf, index, len);
    256             index+=len;
    257             buf[index++]=(byte)(port>>>8);
    258             buf[index++]=(byte)(port&0xff);
    259 
    260             out.write(buf, 0, index);
    261 
    262 /*
    263    The SOCKS request information is sent by the client as soon as it has
    264    established a connection to the SOCKS server, and completed the
    265    authentication negotiations.  The server evaluates the request, and
    266    returns a reply formed as follows:
    267 
    268         +----+-----+-------+------+----------+----------+
    269         |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
    270         +----+-----+-------+------+----------+----------+
    271         | 1  |  1  | X'00' |  1   | Variable |    2     |
    272         +----+-----+-------+------+----------+----------+
    273 
    274    Where:
    275 
    276    o  VER    protocol version: X'05'
    277    o  REP    Reply field:
    278       o  X'00' succeeded
    279       o  X'01' general SOCKS server failure
    280       o  X'02' connection not allowed by ruleset
    281       o  X'03' Network unreachable
    282       o  X'04' Host unreachable
    283       o  X'05' Connection refused
    284       o  X'06' TTL expired
    285       o  X'07' Command not supported
    286       o  X'08' Address type not supported
    287       o  X'09' to X'FF' unassigned
    288     o  RSV    RESERVED
    289     o  ATYP   address type of following address
    290       o  IP V4 address: X'01'
    291       o  DOMAINNAME: X'03'
    292       o  IP V6 address: X'04'
    293     o  BND.ADDR       server bound address
    294     o  BND.PORT       server bound port in network octet order
    295 */
    296 
    297       //in.read(buf, 0, 4);
    298             fill(in, buf, 4);
    299 
    300             if(buf[1]!=0)
    301             {
    302                 try
    303                 {
    304                     socket.close();
    305                 }
    306                 catch(Exception eee)
    307                 {
    308                 }
    309                 throw new ProxyException(ProxyInfo.ProxyType.SOCKS5,
    310                     "server returns "+buf[1]);
    311             }
    312 
    313             switch(buf[3]&0xff)
    314             {
    315                 case 1:
    316                     //in.read(buf, 0, 6);
    317                     fill(in, buf, 6);
    318                     break;
    319                 case 3:
    320                     //in.read(buf, 0, 1);
    321                     fill(in, buf, 1);
    322                     //in.read(buf, 0, buf[0]+2);
    323                     fill(in, buf, (buf[0]&0xff)+2);
    324                     break;
    325                 case 4:
    326                     //in.read(buf, 0, 18);
    327                     fill(in, buf, 18);
    328                     break;
    329                 default:
    330             }
    331             return socket;
    332 
    333         }
    334         catch(RuntimeException e)
    335         {
    336             throw e;
    337         }
    338         catch(Exception e)
    339         {
    340             try
    341             {
    342                 if(socket!=null)
    343                 {
    344                     socket.close();
    345                 }
    346             }
    347             catch(Exception eee)
    348             {
    349             }
    350             String message="ProxySOCKS5: "+e.toString();
    351             if(e instanceof Throwable)
    352             {
    353                 throw new ProxyException(ProxyInfo.ProxyType.SOCKS5,message,
    354                     (Throwable)e);
    355             }
    356             throw new IOException(message);
    357         }
    358     }
    359 
    360     private void fill(InputStream in, byte[] buf, int len)
    361       throws IOException
    362     {
    363         int s=0;
    364         while(s<len)
    365         {
    366             int i=in.read(buf, s, len-s);
    367             if(i<=0)
    368             {
    369                 throw new ProxyException(ProxyInfo.ProxyType.SOCKS5, "stream " +
    370                     "is closed");
    371             }
    372             s+=i;
    373         }
    374     }
    375 }
    376