I2P Address: [http://git.idk.i2p]

Skip to content
Snippets Groups Projects
Commit 3603cc23 authored by zzz's avatar zzz
Browse files

add socks 4/4a support

parent f4c3607c
No related branches found
No related tags found
No related merge requests found
/* I2PSOCKSTunnel is released under the terms of the GNU GPL,
* with an additional exception. For further details, see the
* licensing terms in I2PTunnel.java.
*
* Copyright (c) 2004 by human
*/
package net.i2p.i2ptunnel.socks;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.data.DataFormatException;
import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.util.HexDump;
import net.i2p.util.Log;
/*
* Class that manages SOCKS 4/4a connections, and forwards them to
* destination hosts or (eventually) some outproxy.
*
* @author zzz modded from SOCKS5Server
*/
public class SOCKS4aServer extends SOCKSServer {
private static final Log _log = new Log(SOCKS4aServer.class);
private Socket clientSock = null;
private boolean setupCompleted = false;
/**
* Create a SOCKS4a server that communicates with the client using
* the specified socket. This method should not be invoked
* directly: new SOCKS4aServer objects should be created by using
* SOCKSServerFactory.createSOCSKServer(). It is assumed that the
* SOCKS VER field has been stripped from the input stream of the
* client socket.
*
* @param clientSock client socket
*/
public SOCKS4aServer(Socket clientSock) {
this.clientSock = clientSock;
}
public Socket getClientSocket() throws SOCKSException {
setupServer();
return clientSock;
}
protected void setupServer() throws SOCKSException {
if (setupCompleted) { return; }
DataInputStream in;
DataOutputStream out;
try {
in = new DataInputStream(clientSock.getInputStream());
out = new DataOutputStream(clientSock.getOutputStream());
manageRequest(in, out);
} catch (IOException e) {
throw new SOCKSException("Connection error (" + e.getMessage() + ")");
}
setupCompleted = true;
}
/**
* SOCKS4a request management. This method assumes that all the
* stuff preceding or enveloping the actual request
* has been stripped out of the input/output streams.
*/
private void manageRequest(DataInputStream in, DataOutputStream out) throws IOException, SOCKSException {
int command = in.readByte() & 0xff;
switch (command) {
case Command.CONNECT:
break;
case Command.BIND:
_log.debug("BIND command is not supported!");
sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out);
throw new SOCKSException("BIND command not supported");
default:
_log.debug("unknown command in request (" + Integer.toHexString(command) + ")");
sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out);
throw new SOCKSException("Invalid command in request");
}
connPort = in.readUnsignedShort();
if (connPort == 0) {
_log.debug("trying to connect to TCP port 0? Dropping!");
sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out);
throw new SOCKSException("Invalid port number in request");
}
connHostName = new String("");
boolean alreadyWarned = false;
for (int i = 0; i < 4; ++i) {
int octet = in.readByte() & 0xff;
connHostName += Integer.toString(octet);
if (i != 3) {
connHostName += ".";
if (octet != 0 && !alreadyWarned) {
_log.warn("IPV4 address type in request: " + connHostName + ". Is your client secure?");
alreadyWarned = true;
}
}
}
// discard user name
readString(in);
// SOCKS 4a
if (connHostName.startsWith("0.0.0.") && !connHostName.equals("0.0.0.0"))
connHostName = readString(in);
}
private String readString(DataInputStream in) throws IOException {
StringBuffer sb = new StringBuffer(16);
char c;
while ((c = (char) (in.readByte() & 0xff)) != 0)
sb.append(c);
return sb.toString();
}
protected void confirmConnection() throws SOCKSException {
DataInputStream in;
DataOutputStream out;
try {
out = new DataOutputStream(clientSock.getOutputStream());
sendRequestReply(Reply.SUCCEEDED, InetAddress.getByName("127.0.0.1"), 1, out);
} catch (IOException e) {
throw new SOCKSException("Connection error (" + e.getMessage() + ")");
}
}
/**
* Send the specified reply to a request of the client. Either
* one of inetAddr or domainName can be null, depending on
* addressType.
*/
private void sendRequestReply(int replyCode, InetAddress inetAddr,
int bindPort, DataOutputStream out) throws IOException {
ByteArrayOutputStream reps = new ByteArrayOutputStream();
DataOutputStream dreps = new DataOutputStream(reps);
// Reserved byte, should be 0x00
dreps.write(0x00);
dreps.write(replyCode);
dreps.writeShort(bindPort);
dreps.write(inetAddr.getAddress());
byte[] reply = reps.toByteArray();
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Sending request reply:\n" + HexDump.dump(reply));
}
out.write(reply);
}
/**
* Get an I2PSocket that can be used to send/receive 8-bit clean data
* to/from the destination of the SOCKS connection.
*
* @return an I2PSocket connected with the destination
*/
public I2PSocket getDestinationI2PSocket(I2PSOCKSTunnel t) throws SOCKSException {
setupServer();
if (connHostName == null) {
_log.error("BUG: destination host name has not been initialized!");
throw new SOCKSException("BUG! See the logs!");
}
if (connPort == 0) {
_log.error("BUG: destination port has not been initialized!");
throw new SOCKSException("BUG! See the logs!");
}
DataOutputStream out; // for errors
try {
out = new DataOutputStream(clientSock.getOutputStream());
} catch (IOException e) {
throw new SOCKSException("Connection error (" + e.getMessage() + ")");
}
// FIXME: here we should read our config file, select an
// outproxy, and instantiate the proper socket class that
// handles the outproxy itself (SOCKS4a, SOCKS4a, HTTP CONNECT...).
I2PSocket destSock;
try {
if (connHostName.toLowerCase().endsWith(".i2p")) {
_log.debug("connecting to " + connHostName + "...");
// Let's not due a new Dest for every request, huh?
//I2PSocketManager sm = I2PSocketManagerFactory.createManager();
//destSock = sm.connect(I2PTunnel.destFromName(connHostName), null);
destSock = t.createI2PSocket(I2PTunnel.destFromName(connHostName));
} else if ("localhost".equals(connHostName) || "127.0.0.1".equals(connHostName)) {
String err = "No localhost accesses allowed through the Socks Proxy";
_log.error(err);
try {
sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out);
} catch (IOException ioe) {}
throw new SOCKSException(err);
} else if (connPort == 80) {
// rewrite GET line to include hostname??? or add Host: line???
// or forward to local eepProxy (but that's a Socket not an I2PSocket)
// use eepProxy configured outproxies?
String err = "No handler for HTTP outproxy implemented - to: " + connHostName;
_log.error(err);
try {
sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out);
} catch (IOException ioe) {}
throw new SOCKSException(err);
} else {
List<String> proxies = t.getProxies(connPort);
if (proxies == null || proxies.size() <= 0) {
String err = "No outproxy configured for port " + connPort + " and no default configured either";
_log.error(err);
try {
sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out);
} catch (IOException ioe) {}
throw new SOCKSException(err);
}
int p = I2PAppContext.getGlobalContext().random().nextInt(proxies.size());
String proxy = proxies.get(p);
_log.debug("connecting to port " + connPort + " proxy " + proxy + " for " + connHostName + "...");
// this isn't going to work, these need to be socks outproxies so we need
// to do a socks session to them?
destSock = t.createI2PSocket(I2PTunnel.destFromName(proxy));
}
confirmConnection();
_log.debug("connection confirmed - exchanging data...");
} catch (DataFormatException e) {
try {
sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out);
} catch (IOException ioe) {}
throw new SOCKSException("Error in destination format");
} catch (SocketException e) {
try {
sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out);
} catch (IOException ioe) {}
throw new SOCKSException("Error connecting ("
+ e.getMessage() + ")");
} catch (IOException e) {
try {
sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out);
} catch (IOException ioe) {}
throw new SOCKSException("Error connecting ("
+ e.getMessage() + ")");
} catch (I2PException e) {
try {
sendRequestReply(Reply.CONNECTION_REFUSED, InetAddress.getByName("127.0.0.1"), 0, out);
} catch (IOException ioe) {}
throw new SOCKSException("Error connecting ("
+ e.getMessage() + ")");
}
return destSock;
}
/*
* Some namespaces to enclose SOCKS protocol codes
*/
private static class Command {
private static final int CONNECT = 0x01;
private static final int BIND = 0x02;
}
private static class Reply {
private static final int SUCCEEDED = 0x5a;
private static final int CONNECTION_REFUSED = 0x5b;
}
}
......@@ -44,6 +44,10 @@ public class SOCKSServerFactory {
int socksVer = in.readByte();
switch (socksVer) {
case 0x04:
// SOCKS version 4/4a
serv = new SOCKS4aServer(s);
break;
case 0x05:
// SOCKS version 5
serv = new SOCKS5Server(s);
......
......@@ -384,7 +384,7 @@ public class IndexBean {
else if ("ircclient".equals(internalType)) return "IRC client";
else if ("server".equals(internalType)) return "Standard server";
else if ("httpserver".equals(internalType)) return "HTTP server";
else if ("sockstunnel".equals(internalType)) return "SOCKS 5 proxy";
else if ("sockstunnel".equals(internalType)) return "SOCKS 4/4a/5 proxy";
else if ("connectclient".equals(internalType)) return "CONNECT/SSL/HTTPS proxy";
else if ("ircserver".equals(internalType)) return "IRC server";
else return internalType;
......
......@@ -148,7 +148,7 @@
<option value="client">Standard</option>
<option value="httpclient">HTTP</option>
<option value="ircclient">IRC</option>
<option value="sockstunnel">SOCKS 5</option>
<option value="sockstunnel">SOCKS 4/4a/5</option>
<option value="connectclient">CONNECT</option>
</select>
<input class="control" type="submit" value="Create" />
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment