/* * @(#)SocksClient.java 0.3-3 06/05/2001 * * This file is part of the HTTPClient package * Copyright (C) 1996-2001 Ronald Tschalär * * 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 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307, USA * * For questions, suggestions, bug-reports, enhancement-requests etc. * I may be contacted at: * * ronald@innovation.ch * * The HTTPClient's home page is located at: * * http://www.innovation.ch/java/HTTPClient/ * */ package HTTPClient; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.ByteArrayOutputStream; import java.net.Socket; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; /** * This class implements a SOCKS Client. Supports both versions 4 and 5. * GSSAPI however is not yet implemented. *
Usage is as follows: somewhere in the initialization code (and before
* the first socket creation call) create a SocksClient instance. Then replace
* each socket creation call
*
* sock = new Socket(host, port);
*
* with
*
* sock = socks_client.getSocket(host, port);
*
* (where socks_client is the above created SocksClient instance).
* That's all.
*
* @version 0.3-3 06/05/2001
* @author Ronald Tschalär
*/
class SocksClient
{
/** the host the socks server sits on */
private String socks_host;
/** the port the socks server listens on */
private int socks_port;
/** the version of socks that the server handles */
private int socks_version;
/** socks commands */
private final static byte CONNECT = 1,
BIND = 2,
UDP_ASS = 3;
/** socks version 5 authentication methods */
private final static byte NO_AUTH = 0,
GSSAPI = 1,
USERPWD = 2,
NO_ACC = (byte) 0xFF;
/** socks version 5 address types */
private final static byte IP_V4 = 1,
DMNAME = 3,
IP_V6 = 4;
// Constructors
/**
* Creates a new SOCKS Client using the specified host and port for
* the server. Will try to establish the SOCKS version used when
* establishing the first connection.
*
* @param host the host the SOCKS server is sitting on.
* @param port the port the SOCKS server is listening on.
*/
SocksClient(String host, int port)
{
this.socks_host = host;
this.socks_port = port;
this.socks_version = -1; // as yet unknown
}
/**
* Creates a new SOCKS Client using the specified host and port for
* the server.
*
* @param host the host the SOCKS server is sitting on.
* @param port the port the SOCKS server is listening on.
* @param version the version the SOCKS server is using.
* @exception SocksException if the version is invalid (Currently allowed
* are: 4 and 5).
*/
SocksClient(String host, int port, int version) throws SocksException
{
this.socks_host = host;
this.socks_port = port;
if (version != 4 && version != 5)
throw new SocksException("SOCKS Version not supported: "+version);
this.socks_version = version;
}
// Methods
/**
* Initiates a connection to the socks server, does the startup
* protocol and returns a socket ready for talking.
*
* @param host the host you wish to connect to
* @param port the port you wish to connect to
* @return a Socket with a connection via socks to the desired host/port
* @exception IOException if any socket operation fails
*/
Socket getSocket(String host, int port) throws IOException
{
return getSocket(host, port, null, -1);
}
/**
* Initiates a connection to the socks server, does the startup
* protocol and returns a socket ready for talking.
*
* @param host the host you wish to connect to
* @param port the port you wish to connect to
* @param localAddr the local address to bind to
* @param localPort the local port to bind to
* @return a Socket with a connection via socks to the desired host/port
* @exception IOException if any socket operation fails
*/
Socket getSocket(String host, int port, InetAddress localAddr,
int localPort) throws IOException
{
Socket sock = null;
try
{
Log.write(Log.SOCKS, "Socks: contacting server on " +
socks_host + ":" + socks_port);
// create socket and streams
sock = connect(socks_host, socks_port, localAddr, localPort);
InputStream inp = sock.getInputStream();
OutputStream out = sock.getOutputStream();
// setup connection depending on socks version
switch (socks_version)
{
case 4:
v4ProtExchg(inp, out, host, port);
break;
case 5:
v5ProtExchg(inp, out, host, port);
break;
case -1:
// Ok, let's try and figure it out
try
{
v4ProtExchg(inp, out, host, port);
socks_version = 4;
}
catch (SocksException se)
{
Log.write(Log.SOCKS, "Socks: V4 request failed: " +
se.getMessage());
sock.close();
sock = connect(socks_host, socks_port, localAddr,
localPort);
inp = sock.getInputStream();
out = sock.getOutputStream();
v5ProtExchg(inp, out, host, port);
socks_version = 5;
}
break;
default:
throw new Error("SocksClient internal error: unknown " +
"version "+socks_version);
}
Log.write(Log.SOCKS, "Socks: connection established.");
return sock;
}
catch (IOException ioe)
{
if (sock != null)
{
try { sock.close(); }
catch (IOException ee) {}
}
throw ioe;
}
}
/**
* Connect to the host/port, trying all addresses assciated with that
* host.
*
* @param host the host you wish to connect to
* @param port the port you wish to connect to
* @param localAddr the local address to bind to
* @param localPort the local port to bind to
* @return the Socket
* @exception IOException if the connection could not be established
*/
private static final Socket connect(String host, int port,
InetAddress localAddr, int localPort)
throws IOException
{
InetAddress[] addr_list = InetAddress.getAllByName(host);
for (int idx=0; idx