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