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 socks4 proxy
     30  *
     31  * @author Atul Aggarwal
     32  */
     33 public class Socks4ProxySocketFactory
     34     extends SocketFactory
     35 {
     36     private ProxyInfo proxy;
     37 
     38     public Socks4ProxySocketFactory(ProxyInfo proxy)
     39     {
     40         this.proxy = proxy;
     41     }
     42 
     43     public Socket createSocket(String host, int port)
     44         throws IOException, UnknownHostException
     45     {
     46         return socks4ProxifiedSocket(host,port);
     47 
     48     }
     49 
     50     public Socket createSocket(String host ,int port, InetAddress localHost,
     51                                 int localPort)
     52         throws IOException, UnknownHostException
     53     {
     54         return socks4ProxifiedSocket(host,port);
     55     }
     56 
     57     public Socket createSocket(InetAddress host, int port)
     58         throws IOException
     59     {
     60         return socks4ProxifiedSocket(host.getHostAddress(),port);
     61     }
     62 
     63     public Socket createSocket( InetAddress address, int port,
     64                                 InetAddress localAddress, int localPort)
     65         throws IOException
     66     {
     67         return socks4ProxifiedSocket(address.getHostAddress(),port);
     68 
     69     }
     70 
     71     private Socket socks4ProxifiedSocket(String host, int port)
     72         throws IOException
     73     {
     74         Socket socket = null;
     75         InputStream in = null;
     76         OutputStream out = null;
     77         String proxy_host = proxy.getProxyAddress();
     78         int proxy_port = proxy.getProxyPort();
     79         String user = proxy.getProxyUsername();
     80         String passwd = proxy.getProxyPassword();
     81 
     82         try
     83         {
     84             socket=new Socket(proxy_host, proxy_port);
     85             in=socket.getInputStream();
     86             out=socket.getOutputStream();
     87             socket.setTcpNoDelay(true);
     88 
     89             byte[] buf=new byte[1024];
     90             int index=0;
     91 
     92     /*
     93     1) CONNECT
     94 
     95     The client connects to the SOCKS server and sends a CONNECT request when
     96     it wants to establish a connection to an application server. The client
     97     includes in the request packet the IP address and the port number of the
     98     destination host, and userid, in the following format.
     99 
    100            +----+----+----+----+----+----+----+----+----+----+....+----+
    101            | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
    102            +----+----+----+----+----+----+----+----+----+----+....+----+
    103     # of bytes:   1    1      2              4           variable       1
    104 
    105     VN is the SOCKS protocol version number and should be 4. CD is the
    106     SOCKS command code and should be 1 for CONNECT request. NULL is a byte
    107     of all zero bits.
    108     */
    109 
    110             index=0;
    111             buf[index++]=4;
    112             buf[index++]=1;
    113 
    114             buf[index++]=(byte)(port>>>8);
    115             buf[index++]=(byte)(port&0xff);
    116 
    117             try
    118             {
    119                 InetAddress addr=InetAddress.getByName(host);
    120                 byte[] byteAddress = addr.getAddress();
    121                 for (int i = 0; i < byteAddress.length; i++)
    122                 {
    123                     buf[index++]=byteAddress[i];
    124                 }
    125             }
    126             catch(UnknownHostException uhe)
    127             {
    128                 throw new ProxyException(ProxyInfo.ProxyType.SOCKS4,
    129                     uhe.toString(), uhe);
    130             }
    131 
    132             if(user!=null)
    133             {
    134                 System.arraycopy(user.getBytes(), 0, buf, index, user.length());
    135                 index+=user.length();
    136             }
    137             buf[index++]=0;
    138             out.write(buf, 0, index);
    139 
    140     /*
    141     The SOCKS server checks to see whether such a request should be granted
    142     based on any combination of source IP address, destination IP address,
    143     destination port number, the userid, and information it may obtain by
    144     consulting IDENT, cf. RFC 1413.  If the request is granted, the SOCKS
    145     server makes a connection to the specified port of the destination host.
    146     A reply packet is sent to the client when this connection is established,
    147     or when the request is rejected or the operation fails.
    148 
    149            +----+----+----+----+----+----+----+----+
    150            | VN | CD | DSTPORT |      DSTIP        |
    151            +----+----+----+----+----+----+----+----+
    152     # of bytes:   1    1      2              4
    153 
    154     VN is the version of the reply code and should be 0. CD is the result
    155     code with one of the following values:
    156 
    157     90: request granted
    158     91: request rejected or failed
    159     92: request rejected becasue SOCKS server cannot connect to
    160     identd on the client
    161     93: request rejected because the client program and identd
    162     report different user-ids
    163 
    164     The remaining fields are ignored.
    165     */
    166 
    167             int len=6;
    168             int s=0;
    169             while(s<len)
    170             {
    171                 int i=in.read(buf, s, len-s);
    172                 if(i<=0)
    173                 {
    174                     throw new ProxyException(ProxyInfo.ProxyType.SOCKS4,
    175                         "stream is closed");
    176                 }
    177                 s+=i;
    178             }
    179             if(buf[0]!=0)
    180             {
    181                 throw new ProxyException(ProxyInfo.ProxyType.SOCKS4,
    182                     "server returns VN "+buf[0]);
    183             }
    184             if(buf[1]!=90)
    185             {
    186                 try
    187                 {
    188                     socket.close();
    189                 }
    190                 catch(Exception eee)
    191                 {
    192                 }
    193                 String message="ProxySOCKS4: server returns CD "+buf[1];
    194                 throw new ProxyException(ProxyInfo.ProxyType.SOCKS4,message);
    195             }
    196             byte[] temp = new byte[2];
    197             in.read(temp, 0, 2);
    198             return socket;
    199         }
    200         catch(RuntimeException e)
    201         {
    202             throw e;
    203         }
    204         catch(Exception e)
    205         {
    206             try
    207             {
    208                 if(socket!=null)socket.close();
    209             }
    210             catch(Exception eee)
    211             {
    212             }
    213             throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, e.toString());
    214         }
    215     }
    216 }
    217