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 */ 18package org.jivesoftware.smack.proxy; 19 20import java.io.IOException; 21import java.io.InputStream; 22import java.io.OutputStream; 23import java.net.InetAddress; 24import java.net.Socket; 25import java.net.UnknownHostException; 26import javax.net.SocketFactory; 27 28/** 29 * Socket factory for socks4 proxy 30 * 31 * @author Atul Aggarwal 32 */ 33public 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