/* * This file is part of the OWASP Proxy, a free intercepting proxy library. * Copyright (C) 2008-2010 Rogan Dawes <rogan@dawes.za.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to: * The Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ package org.owasp.proxy.socks.impl; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.UnknownHostException; import org.owasp.proxy.util.AsciiString; /** * SOCKS5 request/response message. */ public class Socks5Message extends ProxyMessage { /** Address type of given message */ public int addrType; byte[] data; /** * Server error response. * * @param cmd * Error code. */ public Socks5Message(int cmd) { super(cmd, null, 0); data = new byte[3]; data[0] = SOCKS_VERSION; // Version. data[1] = (byte) cmd; // Reply code for some kind of failure. data[2] = 0; // Reserved byte. } /** * Construct client request or server response. * * @param cmd * - Request/Response code. * @param ip * - IP field. * @paarm port - port field. */ public Socks5Message(int cmd, InetAddress ip, int port) { super(cmd, ip, port); this.host = ip == null ? "0.0.0.0" : ip.getHostName(); this.version = SOCKS_VERSION; byte[] addr; if (ip == null) { addr = new byte[4]; addr[0] = addr[1] = addr[2] = addr[3] = 0; } else addr = ip.getAddress(); addrType = addr.length == 4 ? SOCKS_ATYP_IPV4 : SOCKS_ATYP_IPV6; data = new byte[6 + addr.length]; data[0] = (byte) SOCKS_VERSION; // Version data[1] = (byte) command; // Command data[2] = (byte) 0; // Reserved byte data[3] = (byte) addrType; // Address type // Put Address System.arraycopy(addr, 0, data, 4, addr.length); // Put port data[data.length - 2] = (byte) (port >> 8); data[data.length - 1] = (byte) (port); } /** * Construct client request or server response. * * @param cmd * - Request/Response code. * @param hostName * - IP field as hostName, uses ADDR_TYPE of HOSTNAME. * @paarm port - port field. */ public Socks5Message(int cmd, String hostName, int port) { super(cmd, null, port); this.host = hostName; this.version = SOCKS_VERSION; // System.out.println("Doing ATYP_DOMAINNAME"); addrType = SOCKS_ATYP_DOMAINNAME; byte addr[] = hostName.getBytes(); data = new byte[7 + addr.length]; data[0] = (byte) SOCKS_VERSION; // Version data[1] = (byte) command; // Command data[2] = (byte) 0; // Reserved byte data[3] = (byte) SOCKS_ATYP_DOMAINNAME; // Address type data[4] = (byte) addr.length; // Length of the address // Put Address System.arraycopy(addr, 0, data, 5, addr.length); // Put port data[data.length - 2] = (byte) (port >> 8); data[data.length - 1] = (byte) (port); } /** * Initialises Message from the stream. Reads server response from given stream. * * @param in * Input stream to read response from. * @throws SocksException * If server response code is not SOCKS_SUCCESS(0), or if any error with protocol occurs. * @throws IOException * If any error happens with I/O. */ public Socks5Message(InputStream in) throws SocksException, IOException { this(in, true); } /** * Initialises Message from the stream. Reads server response or client request from given stream. * * @param in * Input stream to read response from. * @param clinetMode * If true read server response, else read client request. * @throws SocksException * If server response code is not SOCKS_SUCCESS(0) and reading in client mode, or if any error with * protocol occurs. * @throws IOException * If any error happens with I/O. */ public Socks5Message(InputStream in, boolean clientMode) throws SocksException, IOException { read(in, clientMode); } /** * Initialises Message from the stream. Reads server response from given stream. * * @param in * Input stream to read response from. * @throws SocksException * If server response code is not SOCKS_SUCCESS(0), or if any error with protocol occurs. * @throws IOException * If any error happens with I/O. */ public void read(InputStream in) throws SocksException, IOException { read(in, true); } /** * Initialises Message from the stream. Reads server response or client request from given stream. * * @param in * Input stream to read response from. * @param clinetMode * If true read server response, else read client request. * @throws SocksException * If server response code is not SOCKS_SUCCESS(0) and reading in client mode, or if any error with * protocol occurs. * @throws IOException * If any error happens with I/O. */ public void read(InputStream in, boolean clientMode) throws SocksException, IOException { data = null; ip = null; DataInputStream di = new DataInputStream(in); version = di.readUnsignedByte(); command = di.readUnsignedByte(); if (clientMode && command != 0) throw new SocksException(command); di.readUnsignedByte(); // reserved addrType = di.readUnsignedByte(); byte addr[]; switch (addrType) { case SOCKS_ATYP_IPV4: addr = new byte[4]; di.readFully(addr); host = InetAddress.getByAddress(addr).getHostAddress(); break; case SOCKS_ATYP_IPV6: addr = new byte[SOCKS_IPV6_LENGTH]; di.readFully(addr); host = InetAddress.getByAddress(addr).getHostAddress(); break; case SOCKS_ATYP_DOMAINNAME: addr = new byte[di.readUnsignedByte()];// Next byte shows the length di.readFully(addr); host = AsciiString.create(addr); break; default: throw (new SocksException(SocksConstants.SOCKS_JUST_ERROR)); } port = di.readUnsignedShort(); if (addrType != SOCKS_ATYP_DOMAINNAME && doResolveIP) { try { ip = InetAddress.getByName(host); } catch (UnknownHostException uh_ex) { } } } /** * Writes the message to the stream. * * @param out * Output stream to which message should be written. */ public void write(OutputStream out) throws SocksException, IOException { if (data == null) { Socks5Message msg; if (addrType == SOCKS_ATYP_DOMAINNAME) msg = new Socks5Message(command, host, port); else { if (ip == null) { try { ip = InetAddress.getByName(host); } catch (UnknownHostException uh_ex) { throw new SocksException( SocksConstants.SOCKS_JUST_ERROR); } } msg = new Socks5Message(command, ip, port); } data = msg.data; } out.write(data); } /** * Returns IP field of the message as IP, if the message was created with ATYP of HOSTNAME, it will attempt to * resolve the hostname, which might fail. * * @throws UnknownHostException * if host can't be resolved. */ public InetAddress getInetAddress() throws UnknownHostException { if (ip != null) return ip; return (ip = InetAddress.getByName(host)); } /** * Returns string representation of the message. */ public String toString() { String s = "Socks5Message:" + "\n" + "VN " + version + "\n" + "CMD " + command + "\n" + "ATYP " + addrType + "\n" + "ADDR " + host + "\n" + "PORT " + port + "\n"; return s; } /** * Wether to resolve hostIP returned from SOCKS server that is wether to create InetAddress object from the hostName * string */ static public boolean resolveIP() { return doResolveIP; } /** * Wether to resolve hostIP returned from SOCKS server that is wether to create InetAddress object from the hostName * string * * @param doResolve * Wether to resolve hostIP from SOCKS server. * @return Previous value. */ static public boolean resolveIP(boolean doResolve) { boolean old = doResolveIP; doResolveIP = doResolve; return old; } // SOCKS5 constants public static final int SOCKS_VERSION = 5; public static final int SOCKS_ATYP_IPV4 = 0x1; // Where is 2?? public static final int SOCKS_ATYP_DOMAINNAME = 0x3; // !!!!rfc1928 public static final int SOCKS_ATYP_IPV6 = 0x4; public static final int SOCKS_IPV6_LENGTH = 16; static boolean doResolveIP = true; }