diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS4aServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS4aServer.java
new file mode 100644
index 0000000000000000000000000000000000000000..2745cb0fa9f7dcf73bc9e60e350f2cd18be14e84
--- /dev/null
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS4aServer.java
@@ -0,0 +1,283 @@
+/* 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;
+    }
+}
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServerFactory.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServerFactory.java
index 67a52d6889e9dd6f3aa5a8cdcc99b6e5302cccd9..80dfacb6a0b03b00713111c32ad6d861522816b3 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServerFactory.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServerFactory.java
@@ -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);
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
index 87d8e26f69c162f98dba45ce6ec4020ccb5c0ffb..045ea5e583cca17ddc3a469cdac10a6844b5530e 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
@@ -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;
diff --git a/apps/i2ptunnel/jsp/index.jsp b/apps/i2ptunnel/jsp/index.jsp
index 77303267c981bf47fbc83260d48a000cdbbfdd8f..7787eb1f522b77826d0e1ee70c0694046b28ffaa 100644
--- a/apps/i2ptunnel/jsp/index.jsp
+++ b/apps/i2ptunnel/jsp/index.jsp
@@ -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" />