/src/org/jivesoftware/smack/proxy/Socks5ProxySocketFactory.java
Java | 358 lines | 187 code | 37 blank | 134 comment | 8 complexity | 0a75573c66ed0b3c3fb89c3ad462ba0b MD5 | raw file
- package org.jivesoftware.smack.proxy;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.net.InetAddress;
- import java.net.Socket;
- import java.net.UnknownHostException;
- import javax.net.SocketFactory;
- /**
- * Socket factory for Socks5 proxy
- *
- * @author Atul Aggarwal
- */
- public class Socks5ProxySocketFactory
- extends SocketFactory
- {
- private ProxyInfo proxy;
-
- public Socks5ProxySocketFactory(ProxyInfo proxy)
- {
- this.proxy = proxy;
- }
- public Socket createSocket(String host, int port)
- throws IOException, UnknownHostException
- {
- return socks5ProxifiedSocket(host,port);
- }
- public Socket createSocket(String host ,int port, InetAddress localHost,
- int localPort)
- throws IOException, UnknownHostException
- {
-
- return socks5ProxifiedSocket(host,port);
-
- }
- public Socket createSocket(InetAddress host, int port)
- throws IOException
- {
-
- return socks5ProxifiedSocket(host.getHostAddress(),port);
-
- }
- public Socket createSocket( InetAddress address, int port,
- InetAddress localAddress, int localPort)
- throws IOException
- {
-
- return socks5ProxifiedSocket(address.getHostAddress(),port);
-
- }
-
- private Socket socks5ProxifiedSocket(String host, int port)
- throws IOException
- {
- Socket socket = null;
- InputStream in = null;
- OutputStream out = null;
- String proxy_host = proxy.getProxyAddress();
- int proxy_port = proxy.getProxyPort();
- String user = proxy.getProxyUsername();
- String passwd = proxy.getProxyPassword();
-
- try
- {
- socket=new Socket(proxy_host, proxy_port);
- in=socket.getInputStream();
- out=socket.getOutputStream();
- socket.setTcpNoDelay(true);
- byte[] buf=new byte[1024];
- int index=0;
- /*
- +----+----------+----------+
- |VER | NMETHODS | METHODS |
- +----+----------+----------+
- | 1 | 1 | 1 to 255 |
- +----+----------+----------+
- The VER field is set to X'05' for this version of the protocol. The
- NMETHODS field contains the number of method identifier octets that
- appear in the METHODS field.
- The values currently defined for METHOD are:
- o X'00' NO AUTHENTICATION REQUIRED
- o X'01' GSSAPI
- o X'02' USERNAME/PASSWORD
- o X'03' to X'7F' IANA ASSIGNED
- o X'80' to X'FE' RESERVED FOR PRIVATE METHODS
- o X'FF' NO ACCEPTABLE METHODS
- */
- buf[index++]=5;
- buf[index++]=2;
- buf[index++]=0; // NO AUTHENTICATION REQUIRED
- buf[index++]=2; // USERNAME/PASSWORD
- out.write(buf, 0, index);
- /*
- The server selects from one of the methods given in METHODS, and
- sends a METHOD selection message:
- +----+--------+
- |VER | METHOD |
- +----+--------+
- | 1 | 1 |
- +----+--------+
- */
- //in.read(buf, 0, 2);
- fill(in, buf, 2);
- boolean check=false;
- switch((buf[1])&0xff)
- {
- case 0: // NO AUTHENTICATION REQUIRED
- check=true;
- break;
- case 2: // USERNAME/PASSWORD
- if(user==null || passwd==null)
- {
- break;
- }
- /*
- Once the SOCKS V5 server has started, and the client has selected the
- Username/Password Authentication protocol, the Username/Password
- subnegotiation begins. This begins with the client producing a
- Username/Password request:
- +----+------+----------+------+----------+
- |VER | ULEN | UNAME | PLEN | PASSWD |
- +----+------+----------+------+----------+
- | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
- +----+------+----------+------+----------+
- The VER field contains the current version of the subnegotiation,
- which is X'01'. The ULEN field contains the length of the UNAME field
- that follows. The UNAME field contains the username as known to the
- source operating system. The PLEN field contains the length of the
- PASSWD field that follows. The PASSWD field contains the password
- association with the given UNAME.
- */
- index=0;
- buf[index++]=1;
- buf[index++]=(byte)(user.length());
- System.arraycopy(user.getBytes(), 0, buf, index,
- user.length());
- index+=user.length();
- buf[index++]=(byte)(passwd.length());
- System.arraycopy(passwd.getBytes(), 0, buf, index,
- passwd.length());
- index+=passwd.length();
- out.write(buf, 0, index);
- /*
- The server verifies the supplied UNAME and PASSWD, and sends the
- following response:
- +----+--------+
- |VER | STATUS |
- +----+--------+
- | 1 | 1 |
- +----+--------+
- A STATUS field of X'00' indicates success. If the server returns a
- `failure' (STATUS value other than X'00') status, it MUST close the
- connection.
- */
- //in.read(buf, 0, 2);
- fill(in, buf, 2);
- if(buf[1]==0)
- {
- check=true;
- }
- break;
- default:
- }
- if(!check)
- {
- try
- {
- socket.close();
- }
- catch(Exception eee)
- {
- }
- throw new ProxyException(ProxyInfo.ProxyType.SOCKS5,
- "fail in SOCKS5 proxy");
- }
- /*
- The SOCKS request is formed as follows:
- +----+-----+-------+------+----------+----------+
- |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
- +----+-----+-------+------+----------+----------+
- | 1 | 1 | X'00' | 1 | Variable | 2 |
- +----+-----+-------+------+----------+----------+
- Where:
- o VER protocol version: X'05'
- o CMD
- o CONNECT X'01'
- o BIND X'02'
- o UDP ASSOCIATE X'03'
- o RSV RESERVED
- o ATYP address type of following address
- o IP V4 address: X'01'
- o DOMAINNAME: X'03'
- o IP V6 address: X'04'
- o DST.ADDR desired destination address
- o DST.PORT desired destination port in network octet
- order
- */
-
- index=0;
- buf[index++]=5;
- buf[index++]=1; // CONNECT
- buf[index++]=0;
- byte[] hostb=host.getBytes();
- int len=hostb.length;
- buf[index++]=3; // DOMAINNAME
- buf[index++]=(byte)(len);
- System.arraycopy(hostb, 0, buf, index, len);
- index+=len;
- buf[index++]=(byte)(port>>>8);
- buf[index++]=(byte)(port&0xff);
- out.write(buf, 0, index);
- /*
- The SOCKS request information is sent by the client as soon as it has
- established a connection to the SOCKS server, and completed the
- authentication negotiations. The server evaluates the request, and
- returns a reply formed as follows:
- +----+-----+-------+------+----------+----------+
- |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
- +----+-----+-------+------+----------+----------+
- | 1 | 1 | X'00' | 1 | Variable | 2 |
- +----+-----+-------+------+----------+----------+
- Where:
- o VER protocol version: X'05'
- o REP Reply field:
- o X'00' succeeded
- o X'01' general SOCKS server failure
- o X'02' connection not allowed by ruleset
- o X'03' Network unreachable
- o X'04' Host unreachable
- o X'05' Connection refused
- o X'06' TTL expired
- o X'07' Command not supported
- o X'08' Address type not supported
- o X'09' to X'FF' unassigned
- o RSV RESERVED
- o ATYP address type of following address
- o IP V4 address: X'01'
- o DOMAINNAME: X'03'
- o IP V6 address: X'04'
- o BND.ADDR server bound address
- o BND.PORT server bound port in network octet order
- */
- //in.read(buf, 0, 4);
- fill(in, buf, 4);
- if(buf[1]!=0)
- {
- try
- {
- socket.close();
- }
- catch(Exception eee)
- {
- }
- throw new ProxyException(ProxyInfo.ProxyType.SOCKS5,
- "server returns "+buf[1]);
- }
- switch(buf[3]&0xff)
- {
- case 1:
- //in.read(buf, 0, 6);
- fill(in, buf, 6);
- break;
- case 3:
- //in.read(buf, 0, 1);
- fill(in, buf, 1);
- //in.read(buf, 0, buf[0]+2);
- fill(in, buf, (buf[0]&0xff)+2);
- break;
- case 4:
- //in.read(buf, 0, 18);
- fill(in, buf, 18);
- break;
- default:
- }
- return socket;
-
- }
- catch(RuntimeException e)
- {
- throw e;
- }
- catch(Exception e)
- {
- try
- {
- if(socket!=null)
- {
- socket.close();
- }
- }
- catch(Exception eee)
- {
- }
- String message="ProxySOCKS5: "+e.toString();
- if(e instanceof Throwable)
- {
- throw new ProxyException(ProxyInfo.ProxyType.SOCKS5,message,
- (Throwable)e);
- }
- throw new IOException(message);
- }
- }
-
- private void fill(InputStream in, byte[] buf, int len)
- throws IOException
- {
- int s=0;
- while(s<len)
- {
- int i=in.read(buf, s, len-s);
- if(i<=0)
- {
- throw new ProxyException(ProxyInfo.ProxyType.SOCKS5, "stream " +
- "is closed");
- }
- s+=i;
- }
- }
- }