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.ByteArrayOutputStream; 020import java.io.DataInputStream; 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.OutputStream; 024import java.net.InetAddress; 025import java.net.InetSocketAddress; 026import java.net.Socket; 027import java.nio.charset.StandardCharsets; 028 029import org.jivesoftware.smack.util.OutputStreamUtil; 030 031/** 032 * Socket factory for socks4 proxy. 033 * 034 * @author Atul Aggarwal 035 */ 036public class Socks4ProxySocketConnection implements ProxySocketConnection { 037 private final ProxyInfo proxy; 038 039 Socks4ProxySocketConnection(ProxyInfo proxy) { 040 this.proxy = proxy; 041 } 042 043 @Override 044 public void connect(Socket socket, String host, int port, int timeout) 045 throws IOException { 046 String proxy_host = proxy.getProxyAddress(); 047 int proxy_port = proxy.getProxyPort(); 048 String user = proxy.getProxyUsername(); 049 050 socket.connect(new InetSocketAddress(proxy_host, proxy_port), timeout); 051 InputStream in = socket.getInputStream(); 052 DataInputStream dis = new DataInputStream(in); 053 OutputStream out = socket.getOutputStream(); 054 055 ByteArrayOutputStream outBuf = new ByteArrayOutputStream(); 056 byte[] inBuf; 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 outBuf.write(4); 077 outBuf.write(1); 078 079 outBuf.write(port >>> 8); 080 outBuf.write(port & 0xff); 081 082 InetAddress inetAddress = InetAddress.getByName(proxy_host); 083 byte[] byteAddress = inetAddress.getAddress(); 084 outBuf.write(byteAddress); 085 086 if (user != null) { 087 byte[] userBytes = user.getBytes(StandardCharsets.UTF_8); 088 outBuf.write(userBytes); 089 } 090 outBuf.write(0); 091 OutputStreamUtil.writeResetAndFlush(outBuf, out); 092 093 /* 094 The SOCKS server checks to see whether such a request should be granted 095 based on any combination of source IP address, destination IP address, 096 destination port number, the userid, and information it may obtain by 097 consulting IDENT, cf. RFC 1413. If the request is granted, the SOCKS 098 server makes a connection to the specified port of the destination host. 099 A reply packet is sent to the client when this connection is established, 100 or when the request is rejected or the operation fails. 101 102 +----+----+----+----+----+----+----+----+ 103 | VN | CD | DSTPORT | DSTIP | 104 +----+----+----+----+----+----+----+----+ 105 # of bytes: 1 1 2 4 106 107 VN is the version of the reply code and should be 0. CD is the result 108 code with one of the following values: 109 110 90: request granted 111 91: request rejected or failed 112 92: request rejected because SOCKS server cannot connect to 113 identd on the client 114 93: request rejected because the client program and identd 115 report different user-ids 116 117 The remaining fields are ignored. 118 */ 119 120 inBuf = new byte[6]; 121 dis.readFully(inBuf); 122 if (inBuf[0] != 0) { 123 throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, 124 "server returns VN " + inBuf[0]); 125 } 126 if (inBuf[1] != 90) { 127 String message = "ProxySOCKS4: server returns CD " + inBuf[1]; 128 throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, message); 129 } 130 inBuf = new byte[2]; 131 dis.readFully(inBuf); 132 } 133}