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.InetSocketAddress; 024import java.net.Socket; 025 026import org.jivesoftware.smack.util.StringUtils; 027 028/** 029 * Socket factory for socks4 proxy. 030 * 031 * @author Atul Aggarwal 032 */ 033public class Socks4ProxySocketConnection implements ProxySocketConnection { 034 private final ProxyInfo proxy; 035 036 Socks4ProxySocketConnection(ProxyInfo proxy) 037 { 038 this.proxy = proxy; 039 } 040 041 @Override 042 public void connect(Socket socket, String host, int port, int timeout) 043 throws IOException { 044 InputStream in = null; 045 OutputStream out = null; 046 String proxy_host = proxy.getProxyAddress(); 047 int proxy_port = proxy.getProxyPort(); 048 String user = proxy.getProxyUsername(); 049 050 try 051 { 052 socket.connect(new InetSocketAddress(proxy_host, proxy_port), timeout); 053 in = socket.getInputStream(); 054 out = socket.getOutputStream(); 055 socket.setTcpNoDelay(true); 056 057 byte[] buf = new byte[1024]; 058 int index = 0; 059 060 /* 061 1) CONNECT 062 063 The client connects to the SOCKS server and sends a CONNECT request when 064 it wants to establish a connection to an application server. The client 065 includes in the request packet the IP address and the port number of the 066 destination host, and userid, in the following format. 067 068 +----+----+----+----+----+----+----+----+----+----+....+----+ 069 | VN | CD | DSTPORT | DSTIP | USERID |NULL| 070 +----+----+----+----+----+----+----+----+----+----+....+----+ 071 # of bytes: 1 1 2 4 variable 1 072 073 VN is the SOCKS protocol version number and should be 4. CD is the 074 SOCKS command code and should be 1 for CONNECT request. NULL is a byte 075 of all zero bits. 076 */ 077 078 index = 0; 079 buf[index++] = 4; 080 buf[index++] = 1; 081 082 buf[index++] = (byte) (port >>> 8); 083 buf[index++] = (byte) (port & 0xff); 084 085 InetAddress inetAddress = InetAddress.getByName(proxy_host); 086 byte[] byteAddress = inetAddress.getAddress(); 087 for (int i = 0; i < byteAddress.length; i++) 088 { 089 buf[index++] = byteAddress[i]; 090 } 091 092 if (user != null) 093 { 094 byte[] userBytes = user.getBytes(StringUtils.UTF8); 095 System.arraycopy(userBytes, 0, buf, index, user.length()); 096 index += user.length(); 097 } 098 buf[index++] = 0; 099 out.write(buf, 0, index); 100 101 /* 102 The SOCKS server checks to see whether such a request should be granted 103 based on any combination of source IP address, destination IP address, 104 destination port number, the userid, and information it may obtain by 105 consulting IDENT, cf. RFC 1413. If the request is granted, the SOCKS 106 server makes a connection to the specified port of the destination host. 107 A reply packet is sent to the client when this connection is established, 108 or when the request is rejected or the operation fails. 109 110 +----+----+----+----+----+----+----+----+ 111 | VN | CD | DSTPORT | DSTIP | 112 +----+----+----+----+----+----+----+----+ 113 # of bytes: 1 1 2 4 114 115 VN is the version of the reply code and should be 0. CD is the result 116 code with one of the following values: 117 118 90: request granted 119 91: request rejected or failed 120 92: request rejected because SOCKS server cannot connect to 121 identd on the client 122 93: request rejected because the client program and identd 123 report different user-ids 124 125 The remaining fields are ignored. 126 */ 127 128 int len = 6; 129 int s = 0; 130 while (s < len) 131 { 132 int i = in.read(buf, s, len - s); 133 if (i <= 0) 134 { 135 throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, 136 "stream is closed"); 137 } 138 s += i; 139 } 140 if (buf[0] != 0) 141 { 142 throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, 143 "server returns VN " + buf[0]); 144 } 145 if (buf[1] != 90) 146 { 147 try 148 { 149 socket.close(); 150 } 151 catch (Exception eee) 152 { 153 } 154 String message = "ProxySOCKS4: server returns CD " + buf[1]; 155 throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, message); 156 } 157 byte[] temp = new byte[2]; 158 in.read(temp, 0, 2); 159 } 160 catch (RuntimeException e) 161 { 162 throw e; 163 } 164 catch (Exception e) 165 { 166 try 167 { 168 socket.close(); 169 } 170 catch (Exception eee) 171 { 172 } 173 throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, e.toString()); 174 } 175 } 176 177}