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}