diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigReseedHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigReseedHandler.java
index ab5e83408..483bb9a7b 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigReseedHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigReseedHandler.java
@@ -153,7 +153,10 @@ public class ConfigReseedHandler extends FormHandler {
changes.put(Reseeder.PROP_SSL_DISABLE,
Boolean.toString(disabled));
saveBoolean(Reseeder.PROP_PROXY_ENABLE, "enable");
- saveBoolean(Reseeder.PROP_SPROXY_ENABLE, "senable");
+ String pmode = getJettyString("pmode");
+ boolean senable = pmode != null && pmode.length() > 0;
+ changes.put(Reseeder.PROP_SPROXY_ENABLE, Boolean.toString(senable));
+ saveString(Reseeder.PROP_SPROXY_TYPE, "pmode");
if (_context.router().saveConfig(changes, removes))
addFormNotice(_t("Configuration saved successfully."));
else
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigReseedHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigReseedHelper.java
index 80907610c..ed2f83f07 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigReseedHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigReseedHelper.java
@@ -60,6 +60,18 @@ public class ConfigReseedHelper extends HelperBase {
return "";
}
+ /** @since 0.9.33 */
+ public String pmodeChecked(int mode) {
+ String c = _context.getProperty(Reseeder.PROP_SPROXY_TYPE, "HTTP");
+ boolean disabled = !_context.getBooleanProperty(Reseeder.PROP_SPROXY_ENABLE);
+ if ((mode == 0 && disabled) ||
+ (mode == 1 && !disabled && c.equals("HTTP")) ||
+ (mode == 2 && !disabled && c.equals("SOCKS4")) ||
+ (mode == 3 && !disabled && c.equals("SOCKS5")))
+ return CHECKED;
+ return "";
+ }
+
public String getEnable() {
return getChecked(Reseeder.PROP_PROXY_ENABLE);
}
@@ -69,9 +81,11 @@ public class ConfigReseedHelper extends HelperBase {
return getChecked(Reseeder.PROP_PROXY_AUTH_ENABLE);
}
+/****
public String getSenable() {
return getChecked(Reseeder.PROP_SPROXY_ENABLE);
}
+****/
/** @since 0.8.9 */
public String getSauth() {
diff --git a/apps/routerconsole/jsp/configreseed.jsp b/apps/routerconsole/jsp/configreseed.jsp
index e35902542..675a71c61 100644
--- a/apps/routerconsole/jsp/configreseed.jsp
+++ b/apps/routerconsole/jsp/configreseed.jsp
@@ -108,6 +108,31 @@
" />
diff --git a/core/java/src/net/i2p/util/EepGet.java b/core/java/src/net/i2p/util/EepGet.java
index 33ad0b6d7..19c19e6f0 100644
--- a/core/java/src/net/i2p/util/EepGet.java
+++ b/core/java/src/net/i2p/util/EepGet.java
@@ -1737,6 +1737,12 @@ public class EepGet {
rv.put("response", '"' + PasswordManager.md5Hex(kd) + '"');
return rv;
}
+
+ /** @since 0.9.33 */
+ public String getUsername() { return username; }
+
+ /** @since 0.9.33 */
+ public String getPassword() { return password; }
}
/**
diff --git a/core/java/src/net/i2p/util/SSLEepGet.java b/core/java/src/net/i2p/util/SSLEepGet.java
index f5b04879a..d9e90e71b 100644
--- a/core/java/src/net/i2p/util/SSLEepGet.java
+++ b/core/java/src/net/i2p/util/SSLEepGet.java
@@ -71,6 +71,8 @@ import net.i2p.I2PAppContext;
import net.i2p.crypto.CertUtil;
import net.i2p.crypto.KeyStoreUtil;
import net.i2p.data.DataHelper;
+import net.i2p.socks.SOCKS4Client;
+import net.i2p.socks.SOCKS5Client;
/**
* HTTPS only, no retries, no min and max size options, no timeout option
@@ -104,7 +106,7 @@ public class SSLEepGet extends EepGet {
* Not all may be supported.
* @since 0.9.33
*/
- public enum ProxyType { NONE, HTTP, HTTPS, INTERNAL, SOCKS4, SOCKS5 }
+ public enum ProxyType { NONE, HTTP, HTTPS, INTERNAL, SOCKS4, SOCKS5, TRANSPARENT }
/**
@@ -244,9 +246,10 @@ public class SSLEepGet extends EepGet {
int saveCerts = 0;
boolean noVerify = false;
String proxyHost = "127.0.0.1";
- int proxyPort = 80;
+ int proxyPort = 0;
+ ProxyType ptype = ProxyType.HTTP;
boolean error = false;
- Getopt g = new Getopt("ssleepget", args, "p:sz");
+ Getopt g = new Getopt("ssleepget", args, "p:y:sz");
try {
int c;
while ((c = g.getopt()) != -1) {
@@ -265,6 +268,19 @@ public class SSLEepGet extends EepGet {
}
break;
+ case 'y':
+ String y = g.getOptarg().toUpperCase(Locale.US);
+ if (y.equals("HTTP") || y.equals("HTTPS")) {
+ // already set
+ } else if (y.equals("SOCKS4")) {
+ ptype = ProxyType.SOCKS4;
+ } else if (y.equals("SOCKS5")) {
+ ptype = ProxyType.SOCKS5;
+ } else {
+ error = true;
+ }
+ break;
+
case 's':
saveCerts++;
break;
@@ -294,10 +310,17 @@ public class SSLEepGet extends EepGet {
String saveAs = suggestName(url);
SSLEepGet get;
- if (proxyHost != null)
- get = new SSLEepGet(I2PAppContext.getGlobalContext(), ProxyType.HTTP, proxyHost, proxyPort, saveAs, url);
- else
+ if (proxyHost != null) {
+ if (proxyPort == 0) {
+ if (ptype == ProxyType.HTTP)
+ proxyPort = 8080;
+ else
+ proxyPort = 1080;
+ }
+ get = new SSLEepGet(I2PAppContext.getGlobalContext(), ptype, proxyHost, proxyPort, saveAs, url);
+ } else {
get = new SSLEepGet(I2PAppContext.getGlobalContext(), saveAs, url);
+ }
if (saveCerts > 0)
get._saveCerts = saveCerts;
if (noVerify)
@@ -309,8 +332,9 @@ public class SSLEepGet extends EepGet {
}
private static void usage() {
- System.err.println("Usage: SSLEepGet [-psz] https://url\n" +
- " -p proxyHost[:proxyPort] // default port 80\n" +
+ System.err.println("Usage: SSLEepGet [-psyz] https://url\n" +
+ " -p proxyHost[:proxyPort] // default port 8080 for HTTPS and 1080 for SOCKS\n" +
+ " -y HTTPS|SOCKS4|SOCKS5 // proxy type, default HTTPS if proxyHost is set\n" +
" -s save unknown certs\n" +
" -s -s save all certs\n" +
" -z bypass hostname verification");
@@ -667,9 +691,35 @@ public class SSLEepGet extends EepGet {
port = 443;
if (_shouldProxy) {
- if (_proxyType != ProxyType.HTTP)
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("Connecting to " + _proxyType + " proxy");
+ switch (_proxyType) {
+ case HTTP:
+ httpProxyConnect(host, port);
+ break;
+
+ case SOCKS4:
+ socksProxyConnect(false, host, port);
+ break;
+
+ case SOCKS5:
+ socksProxyConnect(true, host, port);
+ break;
+
+ case HTTPS:
+ case INTERNAL:
+ case TRANSPARENT:
+ default:
throw new IOException("Unsupported proxy type " + _proxyType);
- httpProxyConnect(host, port);
+ }
+
+ // wrap the socket in an SSLSocket
+ if (_sslContext != null)
+ _proxy = _sslContext.getSocketFactory().createSocket(_proxy, host, port, true);
+ else
+ _proxy = ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(_proxy, host, port, true);
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug(_proxyType + " proxy headers read completely");
} else {
// Warning, createSocket() followed by connect(InetSocketAddress)
// disables SNI, at least on Java 7.
@@ -735,9 +785,12 @@ public class SSLEepGet extends EepGet {
/**
* Connect to a HTTP proxy.
+ * Proxy address must be in _proxyHost and _proxyPort.
* Side effects: Sets _proxy, _proxyIn, _proxyOut,
* and other globals via readHeaders()
*
+ * @param host what the proxy should connect to
+ * @param port what the proxy should connect to
* @since 0.9.33
*/
private void httpProxyConnect(String host, int port) throws IOException {
@@ -777,20 +830,44 @@ public class SSLEepGet extends EepGet {
throw new IOException("Timed out reading the proxy headers");
if (_responseCode == 407) {
// TODO
- throw new IOException("Proxy auth unsupported");
+ throw new IOException("Authorization unsupported on HTTP Proxy");
} else if (_responseCode != 200) {
// readHeaders() will throw on most errors, but here we ensure it is 200
throw new IOException("Invalid proxy response: " + _responseCode + ' ' + _responseText);
}
if (_redirectLocation != null)
throw new IOException("Proxy redirect not allowed");
- if (_log.shouldLog(Log.DEBUG))
- _log.debug("proxy headers read completely");
+ }
- // wrap the socket in an SSLSocket
- if (_sslContext != null)
- _proxy = _sslContext.getSocketFactory().createSocket(_proxy, host, port, true);
- else
- _proxy = ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(_proxy, host, port, true);
+ /**
+ * Connect to a SOCKS proxy.
+ * Proxy address must be in _proxyHost and _proxyPort.
+ * Side effects: Sets _proxy, _proxyIn, _proxyOut,
+ * and other globals via readHeaders()
+ *
+ * @param host what the proxy should connect to
+ * @param port what the proxy should connect to
+ * @since 0.9.33
+ */
+ private void socksProxyConnect(boolean isSocks5, String host, int port) throws IOException {
+ if (_fetchHeaderTimeout > 0) {
+ _proxy = new Socket();
+ _proxy.setSoTimeout(_fetchHeaderTimeout);
+ _proxy.connect(new InetSocketAddress(_proxyHost, _proxyPort), _fetchHeaderTimeout);
+ } else {
+ _proxy = new Socket(_proxyHost, _proxyPort);
+ }
+ if (_authState != null) {
+ if (!isSocks5)
+ throw new IOException("Authorization unsupported on SOCKS 4");
+ SOCKS5Client.connect(_proxy, host, port, _authState.getUsername(), _authState.getPassword());
+ } else {
+ if (isSocks5)
+ SOCKS5Client.connect(_proxy, host, port);
+ else
+ SOCKS4Client.connect(_proxy, host, port);
+ }
+ _proxyIn = _proxy.getInputStream();
+ _proxyOut = _proxy.getOutputStream();
}
}
diff --git a/history.txt b/history.txt
index 7d0bbcd6f..3657535f1 100644
--- a/history.txt
+++ b/history.txt
@@ -1,5 +1,10 @@
-2017-11-16 zzz
+2017-11-18 zzz
+ * Reseed: Add SOCKS proxy support (ticket #1130)
+
+2017-11-17 zzz
+ * Addressbook: Fix adding alternates after importing an empty book (ticket #2072)
* Reseed: Add HTTPS proxy support (ticket #423)
+ * SOCKS: Move code from i2ptunnel to core, in prep for SSLEepGet use (ticket #1130)
2017-11-16 zzz
* Console: Hide Reseed HTTP proxy options if no HTTP URLs (ticket #2007)
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index 0725033fa..f6e6df3a5 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION;
- public final static long BUILD = 3;
+ public final static long BUILD = 4;
/** for example "-test" */
public final static String EXTRA = "";
diff --git a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java
index 8a7b68ca7..00c9c3e32 100644
--- a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java
+++ b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java
@@ -145,6 +145,8 @@ public class Reseeder {
public static final String PROP_SPROXY_USERNAME = "router.reseedSSLProxy.username";
public static final String PROP_SPROXY_PASSWORD = "router.reseedSSLProxy.password";
public static final String PROP_SPROXY_AUTH_ENABLE = "router.reseedSSLProxy.authEnable";
+ /** @since 0.9.33 */
+ public static final String PROP_SPROXY_TYPE = "router.reseedSSLProxyType";
/** @since 0.9 */
public static final String PROP_DISABLE = "router.reseedDisable";
@@ -921,10 +923,11 @@ public class Reseeder {
boolean ssl = url.toString().startsWith("https");
if (ssl) {
boolean shouldProxy = _sproxyHost != null && _sproxyHost.length() > 0 && _sproxyPort > 0;
+ SSLEepGet.ProxyType ptype = getProxyType();
SSLEepGet sslget;
if (_sslState == null) {
if (shouldProxy)
- sslget = new SSLEepGet(_context, SSLEepGet.ProxyType.HTTP, _sproxyHost, _sproxyPort,
+ sslget = new SSLEepGet(_context, ptype, _sproxyHost, _sproxyPort,
baos, url.toString());
else
sslget = new SSLEepGet(_context, baos, url.toString());
@@ -932,7 +935,7 @@ public class Reseeder {
_sslState = sslget.getSSLState();
} else {
if (shouldProxy)
- sslget = new SSLEepGet(_context, SSLEepGet.ProxyType.HTTP, _sproxyHost, _sproxyPort,
+ sslget = new SSLEepGet(_context, ptype, _sproxyHost, _sproxyPort,
baos, url.toString(), _sslState);
else
sslget = new SSLEepGet(_context, baos, url.toString(), _sslState);
@@ -980,10 +983,11 @@ public class Reseeder {
boolean ssl = url.toString().startsWith("https");
if (ssl) {
boolean shouldProxy = _sproxyHost != null && _sproxyHost.length() > 0 && _sproxyPort > 0;
+ SSLEepGet.ProxyType ptype = getProxyType();
SSLEepGet sslget;
if (_sslState == null) {
if (shouldProxy)
- sslget = new SSLEepGet(_context, SSLEepGet.ProxyType.HTTP, _sproxyHost, _sproxyPort,
+ sslget = new SSLEepGet(_context, ptype, _sproxyHost, _sproxyPort,
out.getPath(), url.toString());
else
sslget = new SSLEepGet(_context, out.getPath(), url.toString());
@@ -991,7 +995,7 @@ public class Reseeder {
_sslState = sslget.getSSLState();
} else {
if (shouldProxy)
- sslget = new SSLEepGet(_context, SSLEepGet.ProxyType.HTTP, _sproxyHost, _sproxyPort,
+ sslget = new SSLEepGet(_context, ptype, _sproxyHost, _sproxyPort,
out.getPath(), url.toString(), _sslState);
else
sslget = new SSLEepGet(_context, out.getPath(), url.toString(), _sslState);
@@ -1060,6 +1064,19 @@ public class Reseeder {
return true;
}
+ /**
+ * @throws IOException if unknown, default is HTTP
+ * @return non-null
+ * @since 0.9.33
+ */
+ private SSLEepGet.ProxyType getProxyType() throws IOException {
+ String sptype = _context.getProperty(PROP_SPROXY_TYPE, "HTTP").toUpperCase(Locale.US);
+ try {
+ return SSLEepGet.ProxyType.valueOf(sptype);
+ } catch (IllegalArgumentException iae) {
+ throw new IOException("Unsupported proxy type " + sptype);
+ }
+ }
}
private static final String BUNDLE_NAME = "net.i2p.router.web.messages";
|