Compare commits

...

10 Commits

8 changed files with 830 additions and 366 deletions

View File

@ -9,6 +9,7 @@ import java.util.Properties;
import net.i2p.router.RouterContext;
import net.i2p.router.web.PluginStarter;
import net.i2p.update.*;
import net.i2p.util.SystemVersion;
/**
* Check for or download an updated version of a plugin.
@ -48,6 +49,8 @@ class PluginUpdateHandler implements Checker, Updater {
xpi2pURL = props.getProperty("updateURL");
List<URI> updateSources = null;
if (xpi2pURL != null) {
xpi2pURL = xpi2pURL.replace("$OS", SystemVersion.getOS());
xpi2pURL = xpi2pURL.replace("$ARCH", SystemVersion.getArch());
try {
updateSources = Collections.singletonList(new URI(xpi2pURL));
} catch (URISyntaxException use) {}

View File

@ -838,6 +838,8 @@ public class PluginStarter implements Runnable {
argVal[i] = argVal[i].replace("$I2P", ctx.getBaseDir().getAbsolutePath());
argVal[i] = argVal[i].replace("$CONFIG", ctx.getConfigDir().getAbsolutePath());
argVal[i] = argVal[i].replace("$PLUGIN", pluginDir.getAbsolutePath());
argVal[i] = argVal[i].replace("$OS", SystemVersion.getOS());
argVal[i] = argVal[i].replace("$ARCH", SystemVersion.getArch());
}
}
ClientApp ca = ctx.routerAppManager().getClientApp(app.className, argVal);
@ -877,6 +879,8 @@ public class PluginStarter implements Runnable {
argVal[i] = argVal[i].replace("$I2P", ctx.getBaseDir().getAbsolutePath());
argVal[i] = argVal[i].replace("$CONFIG", ctx.getConfigDir().getAbsolutePath());
argVal[i] = argVal[i].replace("$PLUGIN", pluginDir.getAbsolutePath());
argVal[i] = argVal[i].replace("$OS", SystemVersion.getOS());
argVal[i] = argVal[i].replace("$ARCH", SystemVersion.getArch());
}
}
@ -887,6 +891,8 @@ public class PluginStarter implements Runnable {
cp = cp.replace("$I2P", ctx.getBaseDir().getAbsolutePath());
cp = cp.replace("$CONFIG", ctx.getConfigDir().getAbsolutePath());
cp = cp.replace("$PLUGIN", pluginDir.getAbsolutePath());
cp = cp.replace("$OS", SystemVersion.getOS());
cp = cp.replace("$ARCH", SystemVersion.getArch());
}
// Old way - add for the whole JVM

View File

@ -27,7 +27,12 @@ public class ConfigReseedHandler extends FormHandler {
ReseedChecker checker = _context.netDb().reseedChecker();
if (_action.equals(_t("Save changes and reseed now"))) {
saveChanges();
if (!checker.requestReseed()) {
if (checker.onionReseedsConfigured()) {
if (!checker.requestOnionReseed()) {
addFormError(_t("Onion reseeding is already in progress"));
addCheckerStatus(checker);
}
} else if (!checker.requestReseed()) {
addFormError(_t("Reseeding is already in progress"));
addCheckerStatus(checker);
} else {
@ -51,15 +56,17 @@ public class ConfigReseedHandler extends FormHandler {
return;
}
try {
if (!checker.requestReseed(url)) {
addFormError(_t("Reseeding is already in progress"));
if (url.getHost().endsWith(".onion")) {
if (!checker.requestOnionReseed(url)) {
addFormError(_t("Tor not available, unable to perform onion reseed"));
addCheckerStatus(checker);
} else {
// wait a while for completion but not forever
for (int i = 0; i < 40; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException ie) {}
} catch (InterruptedException ie) {
}
if (!checker.inProgress())
break;
}
@ -71,6 +78,29 @@ public class ConfigReseedHandler extends FormHandler {
}
}
}
}else{
if (!checker.requestReseed(url)) {
addFormError(_t("Reseeding is already in progress"));
addCheckerStatus(checker);
} else {
// wait a while for completion but not forever
for (int i = 0; i < 40; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
if (!checker.inProgress())
break;
}
if (!addCheckerStatus(checker)) {
if (checker.inProgress()) {
addFormNotice(_t("Reseed in progress, check sidebar for status"));
} else {
addFormNotice(_t("Reseed complete, check sidebar for status"));
}
}
}
}
} catch (IllegalArgumentException iae) {
addFormError(_t("Bad URL {0}", val) + " - " + iae.getLocalizedMessage());
}
@ -98,12 +128,17 @@ public class ConfigReseedHandler extends FormHandler {
} finally {
// it's really a ByteArrayInputStream but we'll play along...
if (in != null)
try { in.close(); } catch (IOException ioe) {}
try {
in.close();
} catch (IOException ioe) {
}
}
} else if (_action.equals(_t("Save changes"))) {
saveChanges();
} else if (_action.equals(_t("Reset URL list"))) {
resetUrlList();
} else if (_action.equals(_t("Reset Onion URL list"))) {
resetOnionUrlList();
}
// addFormError(_t("Unsupported") + ' ' + _action + '.');
}
@ -133,6 +168,13 @@ public class ConfigReseedHandler extends FormHandler {
addFormError(_t("Error saving the configuration (applied but not saved) - please see the error logs"));
}
private void resetOnionUrlList() {
if (_context.router().saveConfig(Reseeder.PROP_ONION_RESEED_URL, null))
addFormNotice(_t("URL list reset successfully"));
else
addFormError(_t("Error saving the configuration (applied but not saved) - please see the error logs"));
}
/** @since 0.8.9 */
private void saveString(String config, String param) {
String val = getJettyString(param);
@ -169,6 +211,16 @@ public class ConfigReseedHandler extends FormHandler {
changes.put(Reseeder.PROP_RESEED_URL, url);
}
}
String onionurl = getJettyString("onionReseedURL");
if (onionurl != null) {
onionurl = onionurl.trim().replace("\r\n", ",").replace("\n", ",");
if (onionurl.length() <= 0) {
addFormNotice("Restoring default URLs");
removes.add(Reseeder.PROP_ONION_RESEED_URL);
} else {
changes.put(Reseeder.PROP_ONION_RESEED_URL, onionurl);
}
}
String mode = getJettyString("mode");
boolean req = "1".equals(mode);
boolean disabled = "2".equals(mode);

View File

@ -84,9 +84,9 @@ public class ConfigReseedHelper extends HelperBase {
}
/****
public String getSenable() {
return getChecked(Reseeder.PROP_SPROXY_ENABLE);
}
* public String getSenable() {
* return getChecked(Reseeder.PROP_SPROXY_ENABLE);
* }
****/
/** @since 0.8.9 */
@ -95,7 +95,22 @@ public class ConfigReseedHelper extends HelperBase {
}
private List<String> reseedList() {
String urls = _context.getProperty(Reseeder.PROP_RESEED_URL, Reseeder.DEFAULT_SEED_URL + ',' + Reseeder.DEFAULT_SSL_SEED_URL);
String urls = _context.getProperty(Reseeder.PROP_RESEED_URL,
Reseeder.DEFAULT_SEED_URL + ',' + Reseeder.DEFAULT_SSL_SEED_URL);
StringTokenizer tok = new StringTokenizer(urls, " ,\r\n");
List<String> URLList = new ArrayList<String>(16);
while (tok.hasMoreTokens()) {
String s = tok.nextToken().trim();
if (s.length() > 0)
URLList.add(s);
}
return URLList;
}
private List<String> onionReseedList() {
String urls = _context.getProperty(Reseeder.PROP_ONION_RESEED_URL);
if (urls == null)
return Collections.emptyList();
StringTokenizer tok = new StringTokenizer(urls, " ,\r\n");
List<String> URLList = new ArrayList<String>(16);
while (tok.hasMoreTokens()) {
@ -118,6 +133,18 @@ public class ConfigReseedHelper extends HelperBase {
return buf.toString();
}
public String getOnionReseedURL() {
List<String> URLList = onionReseedList();
Collections.sort(URLList);
StringBuilder buf = new StringBuilder();
for (String s : URLList) {
if (buf.length() > 0)
buf.append('\n');
buf.append(s);
}
return buf.toString();
}
/**
* @return true only if we have both http and https URLs
* @since 0.9.33

View File

@ -154,6 +154,11 @@
<div class="formaction" id="resetreseed"><input type="submit" name="action" class="reload" value="<%=intl._t("Reset URL list")%>" /></div>
</td></tr>
<tr><td align="right"><b><%=intl._t("Onion Reseed URLs")%>:</b></td>
<td><textarea wrap="off" name="onionReseedURL" cols="60" rows="7" spellcheck="false"><jsp:getProperty name="reseedHelper" property="onionReseedURL" /></textarea>
<div class="formaction" id="resetreseed"><input type="submit" name="action" class="reload" value="<%=intl._t("Reset Onion URL list")%>" /></div>
</td></tr>
<% if (reseedHelper.shouldShowHTTPSProxy()) { %>
<tr><td align="right"><b><%=intl._t("Proxy type for HTTPS reseed URLs")%>:</b></td>
<td><label><input type="radio" class="optbox" name="pmode" value="" <%=reseedHelper.pmodeChecked(0) %> >

View File

@ -117,6 +117,35 @@ public abstract class SystemVersion {
}
}
public static String getOS() {
if (isWindows())
return "windows";
if (isMac())
return "mac";
if (isGNU())
return "linux"; /* actually... */
if (isLinuxService())
return "linux";
if (isAndroid())
return "android";
return "unknown";
}
public static String getArch() {
if (is64Bit()){
if (isARM())
return "arm64";
if (isX86())
return "x86_64";
}
if (isARM())
return "arm";
if (isX86())
return "x86";
return "unknown";
}
public static boolean isWindows() {
return _isWin;
}

View File

@ -2,7 +2,10 @@ package net.i2p.router.networkdb.reseed;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URI;
import java.util.concurrent.atomic.AtomicBoolean;
@ -11,6 +14,7 @@ import net.i2p.router.RouterContext;
import net.i2p.util.Addresses;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer;
import static net.i2p.socks.SOCKS5Constants.*;
/**
* Moved from RouterConsoleRunner.java
@ -29,6 +33,8 @@ public class ReseedChecker {
private final RouterContext _context;
private final Log _log;
private final AtomicBoolean _inProgress = new AtomicBoolean();
private volatile String _torHost = "127.0.0.1";
private volatile int _torSOCKSPort = 9050;
private volatile String _lastStatus = "";
private volatile String _lastError = "";
private volatile boolean _networkLogged;
@ -98,7 +104,8 @@ public class ReseedChecker {
}
// we check the i2p installation directory for a flag telling us not to reseed,
// but also check the home directory for that flag too, since new users installing i2p
// but also check the home directory for that flag too, since new users
// installing i2p
// don't have an installation directory that they can put the flag in yet.
File noReseedFile = new File(new File(System.getProperty("user.home")), ".i2pnoreseed");
File noReseedFileAlt1 = new File(new File(System.getProperty("user.home")), "noreseed.i2p");
@ -159,6 +166,48 @@ public class ReseedChecker {
}
}
/**
* Start a reseed from the onion URL pool
*
* @return true if a reseed was started, false if already in progress
* @since 0.9.53
*/
public boolean requestOnionReseed(){
if (!onionReseedsConfigured())
return false;
if (_inProgress.compareAndSet(false, true)) {
_alreadyRun = true;
try {
Reseeder reseeder = new Reseeder(_context, this);
reseeder.requestOnionReseed();
return true;
} catch (Throwable t) {
_log.error("Reseed failed to start", t);
done();
return false;
}
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Reseed already in progress");
return false;
}
}
/**
* Determine if a list of onion reseeds are configured with i2p.onionReseedURL
*
* @return true if at least one onion reseed is configured.
* @since 0.9.53
*/
public boolean onionReseedsConfigured() {
String url = _context.getProperty(Reseeder.PROP_ONION_RESEED_URL);
if (url == null)
return false;
if (url.length() < 1)
return false;
return true;
}
/**
* Start a reseed from a zip or su3 URI.
*
@ -166,11 +215,169 @@ public class ReseedChecker {
* @throws IllegalArgumentException if it doesn't end with zip or su3
* @since 0.9.19
*/
public boolean requestReseed(URI url) throws IllegalArgumentException {
public boolean requestReseed(String uri) {
URI newURI = URI.create(uri);
return requestReseed(newURI, null, -1);
}
/**
* Start a reseed from a zip or su3 URI.
* If Tor is available, use it, otherwise return false.
*
* @return true if a reseed was started, false if already in progress
* @throws IllegalArgumentException if it doesn't end with zip or su3
* @since 0.9.52
*/
public boolean requestOnionReseed(URI uri) {
int proxyPort = torSOCKSPort();
String proxyHost = torHost();
if (testTor(proxyHost, proxyPort)) {
return requestReseed(uri, proxyHost, proxyPort);
} else {
return false;
}
}
private boolean testTor() {
return testTor(this._torHost, this._torSOCKSPort);
}
private boolean testTor(String phost, int pport) { // throws IOException {
// test that the socks 5 proxy is there and auth, if any, works
Socket s = null;
OutputStream out = null;
InputStream in = null;
String _phost = phost;
int _pport = pport;
String _puser = ""; //"reseed";
String _ppw = "";
try {
s = new Socket();
s.connect(new InetSocketAddress(_phost, _pport), 10 * 1000);
out = s.getOutputStream();
boolean authAvail = _puser != null && _ppw != null;
// send the init
out.write(SOCKS_VERSION_5);
if (authAvail) {
out.write(2);
out.write(Method.USERNAME_PASSWORD);
} else {
out.write(1);
}
out.write(Method.NO_AUTH_REQUIRED);
out.flush();
// read init reply
in = s.getInputStream();
int hisVersion = in.read();
if (hisVersion != SOCKS_VERSION_5)
return false;
int method = in.read();
if (method == Method.NO_AUTH_REQUIRED) {
// good
} else if (method == Method.USERNAME_PASSWORD) {
if (authAvail) {
// send the auth
out.write(AUTH_VERSION);
byte[] user = _puser.getBytes("UTF-8");
byte[] pw = _ppw.getBytes("UTF-8");
out.write(user.length);
out.write(user);
out.write(pw.length);
out.write(pw);
out.flush();
// read the auth reply
if (in.read() != AUTH_VERSION)
return false;
if (in.read() != AUTH_SUCCESS)
return false;
} else {
return false;
}
} else {
return false;
}
} catch (IOException ioe) {
return false;
} finally {
if (in != null)
try {
in.close();
} catch (IOException ioe) {
return false;
}
if (out != null)
try {
out.close();
} catch (IOException ioe) {
return false;
}
if (s != null)
try {
s.close();
} catch (IOException ioe) {
return false;
}
}
return true;
}
/**
* Detect if Tor is running on the host. If it is, return the hostname.
*
* @return hostname of Tor, or null if Tor is not running
* @since 0.9.52
*/
public String torHost() {
if (testTor()) {
_torHost = "127.0.0.1";
}
return _torHost;
}
/**
* Detect if Tor is running on the host. If it is, return the SOCKSport
*
* @return SOCKSport of Tor, or -1 if Tor is not running
* @since 0.9.52
*/
public int torSOCKSPort() {
if (testTor()) {
_torSOCKSPort = 9050;
}
return _torSOCKSPort;
}
/**
* Start a reseed from a zip or su3 URI.
*
* @return true if a reseed was started, false if already in progress
* @throws IllegalArgumentException if it doesn't end with zip or su3
* @since 0.9.52
*/
public boolean requestReseed(URI uri) {
return requestReseed(uri, null, -1);
}
public boolean requestReseed(URI url, String proxyHost, int proxyPort) throws IllegalArgumentException {
if (url.getHost().endsWith(".onion")) {
if (proxyHost == null && testTor()) {
proxyHost = torHost();
} else {
throw new IllegalArgumentException("Onion reseed requires a proxy host");
}
if (proxyPort <= 0 && testTor()) {
proxyPort = torSOCKSPort();
} else {
throw new IllegalArgumentException("Onion reseed requires a proxy port");
}
}
if (_inProgress.compareAndSet(false, true)) {
Reseeder reseeder = new Reseeder(_context, this);
try {
reseeder.requestReseed(url);
reseeder.requestReseed(url, proxyHost, proxyPort);
return true;
} catch (IllegalArgumentException iae) {
if (iae.getMessage() != null)
@ -214,7 +421,8 @@ public class ReseedChecker {
}
}
/** .
/**
* .
* Is a reseed in progress?
*
* @since 0.9

View File

@ -40,7 +40,8 @@ import net.i2p.util.SystemVersion;
import net.i2p.util.Translate;
/**
* Moved from ReseedHandler in routerconsole. See ReseedChecker for additional comments.
* Moved from ReseedHandler in routerconsole. See ReseedChecker for additional
* comments.
*
* Handler to deal with reseed requests. This will reseed from the URLs
* specified below unless the I2P configuration property "i2p.reseedURL" is
@ -54,15 +55,22 @@ public class Reseeder {
private final Log _log;
private final ReseedChecker _checker;
// Reject unreasonably big files, because we download into a ByteArrayOutputStream.
// Reject unreasonably big files, because we download into a
// ByteArrayOutputStream.
private static final long MAX_RESEED_RESPONSE_SIZE = 2 * 1024 * 1024;
private static final long MAX_SU3_RESPONSE_SIZE = 1024 * 1024;
/** limit to spend on a single host, to avoid getting stuck on one that is seriously overloaded */
/**
* limit to spend on a single host, to avoid getting stuck on one that is
* seriously overloaded
*/
private static final int MAX_TIME_PER_HOST = 7 * 60 * 1000;
private static final long MAX_FILE_AGE = 30 * 24 * 60 * 60 * 1000L;
/** Don't disable this! */
private static final boolean ENABLE_SU3 = true;
/** if false, use su3 only, and disable fallback reading directory index and individual dat files */
/**
* if false, use su3 only, and disable fallback reading directory index and
* individual dat files
*/
private static final boolean ENABLE_NON_SU3 = false;
private static final int MIN_RI_WANTED = 100;
private static final int MIN_RESEED_SERVERS = 2;
@ -70,12 +78,14 @@ public class Reseeder {
private static final String NETID_PARAM = "?netid=";
/**
* NOTE - URLs that are in both the standard and SSL groups must use the same hostname,
* NOTE - URLs that are in both the standard and SSL groups must use the same
* hostname,
* so the reseed process will not download from both.
* Ports are supported as of 0.9.14.
*
* NOTE - Each seedURL must be a directory, it must end with a '/',
* it can't end with 'index.html', for example. Both because of how individual file
* it can't end with 'index.html', for example. Both because of how individual
* file
* URLs are constructed, and because SSLEepGet doesn't follow redirects.
*/
public static final String DEFAULT_SEED_URL =
@ -109,14 +119,17 @@ public class Reseeder {
public static final String DEFAULT_SSL_SEED_URL =
// newest first, please add new ones at the top
//
// https url:port, ending with "/" // certificates/reseed/ // certificates/ssl/ // notes
// ---------------------------------- ------------------------ ------------------------- ---------------
// https url:port, ending with "/" // certificates/reseed/ // certificates/ssl/
// // notes
// ---------------------------------- ------------------------
// ------------------------- ---------------
"https://reseed2.i2p.net/" + ',' + // echelon3_at_mail.i2p.crt // CA
"https://banana.incognet.io/" + ',' + // rambler_at_mail.i2p.crt // CA
"https://reseed.diva.exchange/" + ',' + // reseed_at_diva.exchange.crt // CA
"https://reseed.i2pgit.org/" + ',' + // hankhill19580_at_gmail.com.crt // CA
"https://i2p.novg.net/" + ',' + // igor_at_novg.net.crt // CA // Java 8+ only
"https://i2pseed.creativecowpat.net:8443/" + ',' + // creativecowpat_at_mail.i2p.crt // i2pseed.creativecowpat.net.crt // Java 7+
"https://i2pseed.creativecowpat.net:8443/" + ',' + // creativecowpat_at_mail.i2p.crt //
// i2pseed.creativecowpat.net.crt // Java 7+
"https://reseed.onion.im/" + ',' + // lazygravy_at_mail.i2p // CA // Java 8+ only
"https://reseed.memcpy.io/"; // hottuna_at_mail.i2p.crt // CA // SNI required
@ -130,8 +143,11 @@ public class Reseeder {
public static final String PROP_SSL_DISABLE = "router.reseedSSLDisable";
/** @since 0.8.2 */
public static final String PROP_SSL_REQUIRED = "router.reseedSSLRequired";
public static final String PROP_ONION_SSL_REQUIRED = "router.onionReseedSSLRequired";
/** @since 0.8.3 */
public static final String PROP_RESEED_URL = "i2p.reseedURL";
/** @since 1.7.0 */
public static final String PROP_ONION_RESEED_URL = "i2p.onionReseedURL";
/** all these @since 0.8.9 */
public static final String PROP_PROXY_USERNAME = "router.reseedProxy.username";
public static final String PROP_PROXY_PASSWORD = "router.reseedProxy.password";
@ -169,15 +185,23 @@ public class Reseeder {
reseed.start();
}
void requestOnionReseed() {
ReseedRunner reseedRunner = new ReseedRunner("127.0.0.1", 9050);
// set to daemon so it doesn't hang a shutdown
Thread reseed = new I2PAppThread(reseedRunner, "OnionReseed", true);
reseed.start();
}
/**
* Start a reseed from a single zip or su3 URL only.
* Explicitly specify proxy host and port. Use it for both http and https.
* Threaded, nonblocking.
*
* @throws IllegalArgumentException if it doesn't end with zip or su3
* @since 0.9.19
*/
void requestReseed(URI url) throws IllegalArgumentException {
ReseedRunner reseedRunner = new ReseedRunner(url);
void requestReseed(URI url, String proxyHost, int proxyPort) {
ReseedRunner reseedRunner = new ReseedRunner(url, proxyHost, proxyPort);
// set to daemon so it doesn't hang a shutdown
Thread reseed = new I2PAppThread(reseedRunner, "Reseed", true);
reseed.start();
@ -210,7 +234,8 @@ public class Reseeder {
isSU3 = false;
else
throw new IOException("Not a zip or su3 file");
tmp = new File(_context.getTempDir(), "manualreseeds-" + _context.random().nextInt() + (isSU3 ? ".su3" : ".zip"));
tmp = new File(_context.getTempDir(),
"manualreseeds-" + _context.random().nextInt() + (isSU3 ? ".su3" : ".zip"));
out = new BufferedOutputStream(new SecureFileOutputStream(tmp));
out.write(magic);
DataHelper.copy(in, out);
@ -233,8 +258,15 @@ public class Reseeder {
_context.router().eventLog().addEvent(EventLog.RESEED, fetched + " from file");
return fetched;
} finally {
try { in.close(); } catch (IOException ioe) {}
if (out != null) try { out.close(); } catch (IOException ioe) {}
try {
in.close();
} catch (IOException ioe) {
}
if (out != null)
try {
out.close();
} catch (IOException ioe) {
}
if (tmp != null)
tmp.delete();
}
@ -271,17 +303,32 @@ public class Reseeder {
* Start a reseed from the default URL list
*/
public ReseedRunner() {
this(null);
this(null, null, -1);
}
/**
* Start a reseed from this URL only, or null for trying one or more from the default list.
* Create a ReseedRunner which specifies a proxy host and port.
* If the proxy is Tor(9050), use it for both HTTP and HTTPS,
* if not, defer to the `getProxyType()`
*
* @param proxyHost hostname or IP address of proxy
* @param proxyPort port of proxy
*
* @since 0.9.53
*/
public ReseedRunner(String proxyHost, int proxyPort) throws IllegalArgumentException {
this(null, proxyHost, proxyPort);
}
/**
* Start a reseed from this URL only, or null for trying one or more from the
* default list.
*
* @param url if non-null, must be a zip or su3 URL, NOT a directory
* @throws IllegalArgumentException if it doesn't end with zip or su3
* @since 0.9.19
*/
public ReseedRunner(URI url) throws IllegalArgumentException {
public ReseedRunner(URI url, String proxyHost, int proxyPort) throws IllegalArgumentException {
if (url != null) {
String lc = url.getPath().toLowerCase(Locale.US);
if (!(lc.endsWith(".zip") || lc.endsWith(".su3")))
@ -289,33 +336,62 @@ public class Reseeder {
}
_url = url;
_bandwidths = new ArrayList<Long>(4);
if (_url != null && _url.getHost().endsWith(".onion") || proxyPort == 9050) {
_sproxyType = SSLEepGet.ProxyType.SOCKS5;
_shouldProxyHTTP = true;
_shouldProxySSL = true;
if (proxyHost != null && !proxyHost.equals("") && proxyPort > 0) {
_proxyHost = proxyHost;
_proxyPort = proxyPort;
} else {
_proxyHost = "127.0.0.1";
_proxyPort = 9050;
}
_sproxyHost = _proxyHost;
_sproxyPort = _proxyPort;
} else {
_sproxyType = getProxyType();
_shouldProxyHTTP = _sproxyType != SSLEepGet.ProxyType.NONE;
if (proxyPort == 9050) {
_shouldProxySSL = true;
}else{
_shouldProxySSL = _context.getBooleanProperty(PROP_SPROXY_ENABLE);
}
if (proxyHost != null && !proxyHost.equals("") && proxyPort > 0) {
_proxyHost = proxyHost;
_proxyPort = proxyPort;
_sproxyHost = _proxyHost;
_sproxyPort = _proxyPort;
} else if (_sproxyType == SSLEepGet.ProxyType.INTERNAL) {
_proxyHost = "localhost";
_proxyPort = _context.portMapper().getPort(PortMapper.SVC_HTTP_PROXY, 4444);
_sproxyHost = "localhost";
_sproxyPort = _context.portMapper().getPort(PortMapper.SVC_HTTP_PROXY, 4444);
} else if (_sproxyType != SSLEepGet.ProxyType.NONE) {
if (_context.getBooleanProperty(PROP_PROXY_ENABLE)) {
_proxyHost = _context.getProperty(PROP_PROXY_HOST);
_proxyPort = _context.getProperty(PROP_PROXY_PORT, -1);
} else {
_proxyHost = null;
_proxyPort = -1;
}
_shouldProxyHTTP = _proxyHost != null && _proxyHost.length() > 0 && _proxyPort > 0;
boolean shouldProxySSL = _context.getBooleanProperty(PROP_SPROXY_ENABLE);
SSLEepGet.ProxyType sproxyType;
if (shouldProxySSL) {
sproxyType = getProxyType();
if (sproxyType == SSLEepGet.ProxyType.INTERNAL) {
_sproxyHost = "localhost";
_sproxyPort = _context.portMapper().getPort(PortMapper.SVC_HTTP_PROXY, 4444);
} else {
if (_shouldProxySSL){
_sproxyHost = _context.getProperty(PROP_SPROXY_HOST);
_sproxyPort = _context.getProperty(PROP_SPROXY_PORT, -1);
}
}else{
sproxyType = SSLEepGet.ProxyType.NONE;
_sproxyHost = null;
_sproxyPort = -1;
}
_shouldProxySSL = shouldProxySSL && _sproxyHost != null && _sproxyHost.length() > 0 && _sproxyPort > 0;
_sproxyType = _shouldProxySSL ? sproxyType : SSLEepGet.ProxyType.NONE;
} else {
_proxyHost = null;
_proxyPort = -1;
_sproxyHost = null;
_sproxyPort = -1;
}
} else {
_proxyHost = null;
_proxyPort = -1;
_sproxyHost = null;
_sproxyPort = -1;
}
}
}
/*
@ -352,8 +428,15 @@ public class Reseeder {
throw new IllegalArgumentException("Must end with .zip or .su3");
}
} else {
if (_checker.onionReseedsConfigured()){
total = reseedOnion(false);
if (total == 0) {
total = reseed(false);
}
} else {
total = reseed(false);
}
}
if (total >= 20) {
String s = ngettext("Reseed successful, fetched {0} router info",
"Reseed successful, fetched {0} router infos", total);
@ -379,7 +462,8 @@ public class Reseeder {
System.out.println("Consider enabling a proxy for https on the reseed configuration page");
} else {
if (_proxyHost != null && _proxyPort > 0)
System.out.println("Check HTTP proxy setting - host: " + _proxyHost + " port: " + _proxyPort);
System.out
.println("Check HTTP proxy setting - host: " + _proxyHost + " port: " + _proxyPort);
else
System.out.println("Consider enabling an HTTP proxy on the reseed configuration page");
}
@ -387,7 +471,8 @@ public class Reseeder {
String old = _checker.getError();
_checker.setError(_t("Reseed failed.") + ' ' +
_t("See {0} for help.",
"<a target=\"_top\" href=\"/configreseed\">" + _t("reseed configuration page") + "</a>") +
"<a target=\"_top\" href=\"/configreseed\">" + _t("reseed configuration page") + "</a>")
+
"<br>" + old);
_checker.setStatus("");
}
@ -415,7 +500,8 @@ public class Reseeder {
}
// EepGet status listeners
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt,
int numRetries, Exception cause) {
// Since readURL() runs an EepGet with 0 retries,
// we can report errors with attemptFailed() instead of transferFailed().
// It has the benefit of providing cause of failure, which helps resolve issues.
@ -427,9 +513,16 @@ public class Reseeder {
_checker.setError(DataHelper.escapeHTML(cause.getMessage()));
}
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {}
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred,
long bytesRemaining, String url) {
}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url,
String outputFile, boolean notModified) {
}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
}
/**
* Use the Date header as a backup time source
@ -455,7 +548,8 @@ public class Reseeder {
_log.warn("Reseed adjusting clock by " +
DataHelper.formatDuration(Math.abs(offset)));
} else {
// No peers or NTP yet, this is probably better than the peer average will be for a while
// No peers or NTP yet, this is probably better than the peer average will be
// for a while
// default stratum - 1, so the peer average is a worse stratum
_context.clock().setNow(now, RouterClock.DEFAULT_STRATUM - 1);
_log.logAlways(Log.WARN, "NTP failure, Reseed adjusting clock by " +
@ -479,7 +573,8 @@ public class Reseeder {
* the routerInfo-*.dat files from the specified URL (or the default) and
* save them into this router's netDb dir.
*
* - If list specified in the properties, use it randomly, without regard to http/https
* - If list specified in the properties, use it randomly, without regard to
* http/https
* - If SSL not disabled, use the https randomly then
* the http randomly
* - Otherwise just the http randomly.
@ -488,12 +583,36 @@ public class Reseeder {
* @return count of routerinfos successfully fetched, or -1 if no valid URLs
*/
private int reseed(boolean echoStatus) {
List<URI> URLList = new ArrayList<URI>();
String URLs = _context.getProperty(PROP_RESEED_URL);
boolean defaulted = URLs == null;
boolean SSLDisable = _context.getBooleanProperty(PROP_SSL_DISABLE);
boolean SSLRequired = _context.getBooleanPropertyDefaultTrue(PROP_SSL_REQUIRED);
return reseed(echoStatus, PROP_RESEED_URL, SSLDisable, SSLRequired);
}
/**
* Onion Reseed has been requested, so lets go ahead and do it. Fetch all of
* the routerInfo-*.dat files from the specified onion URLs from the special
* onion-only list
* (or the default) and save them into this router's netDb dir.
*
* - SSL is enabled but not required for .onion URLs in this list.
* - set onionReseedSSLRequired=true to require SSL for onion reseed
*
* @param echoStatus apparently always false
* @return count of routerinfos successfully fetched, or -1 if no valid URLs
*/
private int reseedOnion(boolean echoStatus) {
boolean SSLDisable = _context.getBooleanProperty(PROP_SSL_DISABLE);
boolean SSLRequired = _context.getBooleanProperty(PROP_ONION_SSL_REQUIRED);
String urls = _context.getProperty(PROP_ONION_RESEED_URL);
if (urls == null)
return 0;
return reseed(echoStatus, PROP_ONION_RESEED_URL, SSLDisable, SSLRequired);
}
private int reseed(boolean echoStatus, String urlProp, boolean SSLRequired, boolean SSLDisable) {
List<URI> URLList = new ArrayList<URI>();
String URLs = _context.getProperty(urlProp);
boolean defaulted = URLs == null;
if (defaulted) {
if (SSLDisable)
URLs = DEFAULT_SEED_URL;
@ -506,7 +625,8 @@ public class Reseeder {
u = u + '/';
try {
URLList.add(new URI(u));
} catch (URISyntaxException mue) {}
} catch (URISyntaxException mue) {
}
}
Collections.shuffle(URLList, _context.random());
if (!SSLDisable && !SSLRequired) {
@ -519,7 +639,8 @@ public class Reseeder {
u = u + '/';
try {
URLList2.add(new URI(u));
} catch (URISyntaxException mue) {}
} catch (URISyntaxException mue) {
}
}
Collections.shuffle(URLList2, _context.random());
URLList.addAll(URLList2);
@ -538,11 +659,13 @@ public class Reseeder {
if (u.startsWith("https")) {
try {
SSLList.add(new URI(u));
} catch (URISyntaxException mue) {}
} catch (URISyntaxException mue) {
}
} else {
try {
nonSSLList.add(new URI(u));
} catch (URISyntaxException mue) {}
} catch (URISyntaxException mue) {
}
}
}
// shuffle lists
@ -560,7 +683,8 @@ public class Reseeder {
try {
URLList.remove(new URI("https://download.xxlspeed.com/"));
URLList.remove(new URI("https://netdb.i2p2.no/"));
} catch (URISyntaxException mue) {}
} catch (URISyntaxException mue) {
}
}
if (URLList.isEmpty()) {
System.out.println("No valid reseed URLs");
@ -593,7 +717,8 @@ public class Reseeder {
if (ENABLE_SU3) {
try {
dl = reseedSU3(new URI(url.toString() + SU3_FILENAME + query), echoStatus);
} catch (URISyntaxException mue) {}
} catch (URISyntaxException mue) {
}
}
if (ENABLE_NON_SU3) {
if (dl <= 0)
@ -698,11 +823,12 @@ public class Reseeder {
int fetched = 0;
int errors = 0;
// 200 max from one URL
for (Iterator<String> iter = urlList.iterator();
iter.hasNext() && fetched < 200 && System.currentTimeMillis() < timeLimit; ) {
for (Iterator<String> iter = urlList.iterator(); iter.hasNext() && fetched < 200
&& System.currentTimeMillis() < timeLimit;) {
try {
_checker.setStatus(
_t("Reseeding: fetching router info from seed URL ({0} successful, {1} errors).", fetched, errors));
_t("Reseeding: fetching router info from seed URL ({0} successful, {1} errors).",
fetched, errors));
if (!fetchSeed(seedURL.toString(), iter.next()))
continue;
@ -822,11 +948,11 @@ public class Reseeder {
}
_checker.setStatus(
_t("Reseeding: fetching router info from seed URL ({0} successful, {1} errors).", fetched, errors));
System.err.println("Reseed got " + fetched + " router infos " + getDisplayString(seedURL) + " with " + errors + " errors");
System.err.println("Reseed got " + fetched + " router infos " + getDisplayString(seedURL) + " with "
+ errors + " errors");
return fetched;
}
/**
* @return 2 ints: number successful and number of errors
* @since 0.9.19 pulled from reseedSU3
@ -852,7 +978,8 @@ public class Reseeder {
if (ver < _context.clock().now() - MAX_FILE_AGE)
throw new IOException("su3 file too old");
}
} catch (NumberFormatException nfe) {}
} catch (NumberFormatException nfe) {
}
int[] stats = extractZip(zip);
fetched = stats[0];
@ -942,9 +1069,11 @@ public class Reseeder {
/**
* Always throws an exception if something fails.
* We do NOT validate the received data here - that is done in PersistentDataStore
* We do NOT validate the received data here - that is done in
* PersistentDataStore
*
* @param peer The Base64 hash, may include % encoding. It is decoded and validated here.
* @param peer The Base64 hash, may include % encoding. It is decoded and
* validated here.
* @return true on success, false if skipped
*/
private boolean fetchSeed(String seedURL, String peer) throws IOException, URISyntaxException {
@ -962,7 +1091,8 @@ public class Reseeder {
if (ourHash != null && DataHelper.eq(hash, ourHash.getData()))
return false;
URI url = new URI(seedURL + (seedURL.endsWith("/") ? "" : "/") + ROUTERINFO_PREFIX + peer + ROUTERINFO_SUFFIX);
URI url = new URI(
seedURL + (seedURL.endsWith("/") ? "" : "/") + ROUTERINFO_PREFIX + peer + ROUTERINFO_SUFFIX);
byte data[] = readURL(url);
if (data == null || data.length <= 0)
@ -1001,7 +1131,8 @@ public class Reseeder {
get.addAuthorization(user, pass);
}
} else {
// Do a (probably) non-proxied eepget into our ByteArrayOutputStream with 0 retries
// Do a (probably) non-proxied eepget into our ByteArrayOutputStream with 0
// retries
get = new EepGet(_context, _shouldProxyHTTP, _proxyHost, _proxyPort, 0, 0, MAX_RESEED_RESPONSE_SIZE,
null, baos, url.toString(), false, null, null);
if (_shouldProxyHTTP && _context.getBooleanProperty(PROP_PROXY_AUTH_ENABLE)) {
@ -1106,8 +1237,10 @@ public class Reseeder {
_log.info("Saved RI (" + data.length + " bytes) to " + file);
} finally {
try {
if (fos != null) fos.close();
} catch (IOException ioe) {}
if (fos != null)
fos.close();
} catch (IOException ioe) {
}
}
return true;
}
@ -1217,14 +1350,15 @@ public class Reseeder {
}
/******
public static void main(String args[]) {
if ( (args != null) && (args.length == 1) && (!Boolean.parseBoolean(args[0])) ) {
System.out.println("Not reseeding, as requested");
return; // not reseeding on request
}
System.out.println("Reseeding");
Reseeder reseedHandler = new Reseeder();
reseedHandler.requestReseed();
}
* public static void main(String args[]) {
* if ( (args != null) && (args.length == 1) && (!Boolean.parseBoolean(args[0]))
* ) {
* System.out.println("Not reseeding, as requested");
* return; // not reseeding on request
* }
* System.out.println("Reseeding");
* Reseeder reseedHandler = new Reseeder();
* reseedHandler.requestReseed();
* }
******/
}