From e5d76a5a77bab200f4eb4ef9b7ccd412610e851e Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Fri, 23 Jan 2009 19:17:27 +0000
Subject: [PATCH] beginnings of outproxy configuration and routing

---
 .../i2p/i2ptunnel/socks/I2PSOCKSTunnel.java   | 48 +++++++++++++-
 .../net/i2p/i2ptunnel/socks/SOCKS5Server.java | 62 ++++++++++++++++---
 2 files changed, 101 insertions(+), 9 deletions(-)

diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSTunnel.java
index 627cd0616f..be398f770e 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSTunnel.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/I2PSOCKSTunnel.java
@@ -7,6 +7,12 @@
 package net.i2p.i2ptunnel.socks;
 
 import java.net.Socket;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
 
 import net.i2p.client.streaming.I2PSocket;
 import net.i2p.data.Destination;
@@ -20,7 +26,7 @@ import net.i2p.util.Log;
 public class I2PSOCKSTunnel extends I2PTunnelClientBase {
 
     private static final Log _log = new Log(I2PSOCKSTunnel.class);
-
+    private HashMap<String, List<String>> proxies = null;  // port# + "" or "default" -> hostname list
     protected Destination outProxyDest = null;
 
     //public I2PSOCKSTunnel(int localPort, Logging l, boolean ownDest) {
@@ -36,7 +42,7 @@ public class I2PSOCKSTunnel extends I2PTunnelClientBase {
         }
 
         setName(getLocalPort() + " -> SOCKSTunnel");
-
+        parseOptions();
         startRunning();
 
         notifyEvent("openSOCKSTunnelResult", "ok");
@@ -53,4 +59,42 @@ public class I2PSOCKSTunnel extends I2PTunnelClientBase {
             closeSocket(s);
         }
     }
+
+    private static final String PROP_PROXY = "i2ptunnel.socks.proxy.";
+    private void parseOptions() {
+        Properties opts = getTunnel().getClientOptions();
+        proxies = new HashMap(0);
+        for (Map.Entry e : opts.entrySet()) {
+           String prop = (String)e.getKey();
+           if ((!prop.startsWith(PROP_PROXY)) || prop.length() <= PROP_PROXY.length())
+              continue;
+           String port = prop.substring(PROP_PROXY.length());
+           List proxyList = new ArrayList(1);
+           StringTokenizer tok = new StringTokenizer((String)e.getValue(), ", \t");
+           while (tok.hasMoreTokens()) {
+               String proxy = tok.nextToken().trim();
+               if (proxy.endsWith(".i2p"))
+                   proxyList.add(proxy);
+               else
+                   _log.error("Non-i2p SOCKS outproxy: " + proxy);
+           }
+           proxies.put(port, proxyList);
+        }
+    }
+
+    public HashMap<String, List<String>> getProxyMap() {
+        return proxies;
+    }
+
+    public List<String> getProxies(int port) {
+        List<String> rv = proxies.get(port + "");
+        if (rv == null)
+            rv = getDefaultProxies();
+        return rv;
+    }
+
+    public List<String> getDefaultProxies() {
+        return proxies.get("default");
+    }
+
 }
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS5Server.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS5Server.java
index fea3285a65..252d4e1aa8 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS5Server.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKS5Server.java
@@ -13,7 +13,9 @@ 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;
@@ -33,7 +35,6 @@ public class SOCKS5Server extends SOCKSServer {
     private static final int SOCKS_VERSION_5 = 0x05;
 
     private Socket clientSock = null;
-
     private boolean setupCompleted = false;
 
     /**
@@ -274,6 +275,13 @@ public class SOCKS5Server extends SOCKSServer {
             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, SOCKS5, HTTP CONNECT...).
@@ -285,24 +293,64 @@ public class SOCKS5Server extends SOCKSServer {
                 // Let's not due a new Dest for every request, huh?
                 //I2PSocketManager sm = I2PSocketManagerFactory.createManager();
                 //destSock = sm.connect(I2PTunnel.destFromName(connHostName), null);
-                // TODO get the streaming lib options in there
                 destSock = t.createI2PSocket(I2PTunnel.destFromName(connHostName));
-                confirmConnection();
-                _log.debug("connection confirmed - exchanging data...");
+            } 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_NOT_ALLOWED_BY_RULESET, AddressType.DOMAINNAME, null, "0.0.0.0", 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";
+                _log.error(err);
+                try {
+                    sendRequestReply(Reply.CONNECTION_NOT_ALLOWED_BY_RULESET, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out);
+                } catch (IOException ioe) {}
+                throw new SOCKSException(err);
             } else {
-                // if (connPort == 80) ...
-                _log.error("We don't support outproxies (yet)");
-                throw new SOCKSException("Ouproxies not supported (yet)");
+                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_NOT_ALLOWED_BY_RULESET, AddressType.DOMAINNAME, null, "0.0.0.0", 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.HOST_UNREACHABLE, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out);
+            } catch (IOException ioe) {}
             throw new SOCKSException("Error in destination format");
         } catch (SocketException e) {
+            try {
+                sendRequestReply(Reply.HOST_UNREACHABLE, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out);
+            } catch (IOException ioe) {}
             throw new SOCKSException("Error connecting ("
                                      + e.getMessage() + ")");
         } catch (IOException e) {
+            try {
+                sendRequestReply(Reply.HOST_UNREACHABLE, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out);
+            } catch (IOException ioe) {}
             throw new SOCKSException("Error connecting ("
                                      + e.getMessage() + ")");
         } catch (I2PException e) {
+            try {
+                sendRequestReply(Reply.HOST_UNREACHABLE, AddressType.DOMAINNAME, null, "0.0.0.0", 0, out);
+            } catch (IOException ioe) {}
             throw new SOCKSException("Error connecting ("
                                      + e.getMessage() + ")");
         }
-- 
GitLab