001/** 002 * 003 * Copyright the original author or authors 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smack.proxy; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.io.OutputStream; 022import java.net.InetAddress; 023import java.net.Socket; 024import java.net.UnknownHostException; 025 026import javax.net.SocketFactory; 027 028/** 029 * Socket factory for socks4 proxy 030 * 031 * @author Atul Aggarwal 032 */ 033public class Socks4ProxySocketFactory 034 extends SocketFactory 035{ 036 private ProxyInfo proxy; 037 038 public Socks4ProxySocketFactory(ProxyInfo proxy) 039 { 040 this.proxy = proxy; 041 } 042 043 public Socket createSocket(String host, int port) 044 throws IOException, UnknownHostException 045 { 046 return socks4ProxifiedSocket(host,port); 047 048 } 049 050 public Socket createSocket(String host ,int port, InetAddress localHost, 051 int localPort) 052 throws IOException, UnknownHostException 053 { 054 return socks4ProxifiedSocket(host,port); 055 } 056 057 public Socket createSocket(InetAddress host, int port) 058 throws IOException 059 { 060 return socks4ProxifiedSocket(host.getHostAddress(),port); 061 } 062 063 public Socket createSocket( InetAddress address, int port, 064 InetAddress localAddress, int localPort) 065 throws IOException 066 { 067 return socks4ProxifiedSocket(address.getHostAddress(),port); 068 069 } 070 071 @SuppressWarnings("resource") 072 private Socket socks4ProxifiedSocket(String host, int port) 073 throws IOException 074 { 075 Socket socket = null; 076 InputStream in = null; 077 OutputStream out = null; 078 String proxy_host = proxy.getProxyAddress(); 079 int proxy_port = proxy.getProxyPort(); 080 String user = proxy.getProxyUsername(); 081 082 try 083 { 084 socket=new Socket(proxy_host, proxy_port); 085 in=socket.getInputStream(); 086 out=socket.getOutputStream(); 087 socket.setTcpNoDelay(true); 088 089 byte[] buf=new byte[1024]; 090 int index=0; 091 092 /* 093 1) CONNECT 094 095 The client connects to the SOCKS server and sends a CONNECT request when 096 it wants to establish a connection to an application server. The client 097 includes in the request packet the IP address and the port number of the 098 destination host, and userid, in the following format. 099 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}