* SSLEepGet, Reseeder:

- Implement additional CA loading
      - Provide facility to reuse SSL state for speed
      - Provide facility to store previously untrusted certificates
      - Add SSL reseed hosts, prefer them by default
      - Reseed message cleanup
    * build.xml:
      - Add www.cacert.org cert to the installer and updater so
        SSL on a.netdb.i2p2.de and c.netdb.i2p2.de will work
      - Cleanup, fix distclean error in older ants.
This commit is contained in:
zzz
2010-11-21 20:37:49 +00:00
parent c8cad6ab79
commit 106af9967a
7 changed files with 654 additions and 194 deletions

View File

@ -29,6 +29,8 @@ import net.i2p.util.Translate;
* specified below unless the I2P configuration property "i2p.reseedURL" is
* set. It always writes to ./netDb/, so don't mess with that.
*
* This is somewhat complicated by trying to log to three places - the console,
* the router log, and the wrapper log.
*/
public class Reseeder {
private static ReseedRunner _reseedRunner;
@ -40,12 +42,25 @@ public class Reseeder {
private static final String DEFAULT_SEED_URL =
"http://a.netdb.i2p2.de/,http://b.netdb.i2p2.de/,http://c.netdb.i2p2.de/," +
"http://reseed.i2p-projekt.de/,http://i2pbote.net/netDb/,http://r31453.ovh.net/static_media/netDb/";
"http://reseed.i2p-projekt.de/,http://www.i2pbote.net/netDb/,http://r31453.ovh.net/static_media/netDb/";
/** @since 0.8.2 */
private static final String DEFAULT_SSL_SEED_URL =
"https://a.netdb.i2p2.de/,https://c.netdb.i2p2.de/," +
"https://www.i2pbote.net/netDb/";
private static final String PROP_INPROGRESS = "net.i2p.router.web.ReseedHandler.reseedInProgress";
/** the console shows this message while reseedInProgress == false */
private static final String PROP_ERROR = "net.i2p.router.web.ReseedHandler.errorMessage";
/** the console shows this message while reseedInProgress == true */
private static final String PROP_STATUS = "net.i2p.router.web.ReseedHandler.statusMessage";
public static final String PROP_PROXY_HOST = "router.reseedProxyHost";
public static final String PROP_PROXY_PORT = "router.reseedProxyPort";
/** @since 0.8.2 */
public static final String PROP_PROXY_ENABLE = "router.reseedProxyEnable";
/** @since 0.8.2 */
public static final String PROP_SSL_DISABLE = "router.reseedSSLDisable";
private static final String RESEED_TIPS =
_x("Ensure that nothing blocks outbound HTTP, check <a target=\"_top\" href=\"logs.jsp\">logs</a> " +
"and if nothing helps, read the <a target=\"_top\" href=\"http://www.i2p2.de/faq.html\">FAQ</a> about reseeding manually.");
@ -63,7 +78,6 @@ public class Reseeder {
if (_reseedRunner.isRunning()) {
return;
} else {
System.setProperty(PROP_INPROGRESS, "true");
// set to daemon so it doesn't hang a shutdown
Thread reseed = new I2PAppThread(_reseedRunner, "Reseed", true);
reseed.start();
@ -76,20 +90,46 @@ public class Reseeder {
private boolean _isRunning;
private String _proxyHost;
private int _proxyPort;
private SSLEepGet.SSLState _sslState;
public ReseedRunner() {
_isRunning = false;
System.clearProperty(PROP_ERROR);
System.setProperty(PROP_STATUS, _("Reseeding"));
System.setProperty(PROP_INPROGRESS, "true");
}
public boolean isRunning() { return _isRunning; }
/*
* Do it.
* We update PROP_ERROR here.
*/
public void run() {
_isRunning = true;
_proxyHost = _context.getProperty(PROP_PROXY_HOST);
_proxyPort = _context.getProperty(PROP_PROXY_PORT, -1);
_sslState = null; // start fresh
if (_context.getBooleanProperty(PROP_PROXY_ENABLE)) {
_proxyHost = _context.getProperty(PROP_PROXY_HOST);
_proxyPort = _context.getProperty(PROP_PROXY_PORT, -1);
}
System.out.println("Reseed start");
reseed(false);
System.out.println("Reseed complete");
int total = reseed(false);
if (total >= 50) {
System.out.println("Reseed complete, " + total + " received");
System.clearProperty(PROP_ERROR);
} else if (total > 0) {
System.out.println("Reseed complete, only " + total + " received");
System.setProperty(PROP_ERROR, ngettext("Reseed fetched only 1 router.",
"Reseed fetched only {0} routers.", total));
} else {
System.out.println("Reseed failed, check network connection");
System.out.println(
"Ensure that nothing blocks outbound HTTP, check the logs, " +
"and if nothing helps, read the FAQ about reseeding manually.");
System.setProperty(PROP_ERROR, _("Reseed failed.") + ' ' + _(RESEED_TIPS));
}
System.setProperty(PROP_INPROGRESS, "false");
System.clearProperty(PROP_STATUS);
_sslState = null; // don't hold ref
_isRunning = false;
}
@ -112,16 +152,56 @@ 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 SSL not disabled, use the https randomly then
* the http randomly
* - Otherwise just the http randomly.
*
* @param echoStatus apparently always false
* @return count of routerinfos successfully fetched
*/
private void reseed(boolean echoStatus) {
List URLList = new ArrayList();
String URLs = _context.getProperty("i2p.reseedURL", DEFAULT_SEED_URL);
private int reseed(boolean echoStatus) {
List<String> URLList = new ArrayList();
String URLs = _context.getProperty("i2p.reseedURL");
boolean defaulted = URLs == null;
boolean SSLDisable = _context.getBooleanProperty(PROP_SSL_DISABLE);
if (defaulted) {
if (SSLDisable)
URLs = DEFAULT_SEED_URL;
else
URLs = DEFAULT_SSL_SEED_URL;
}
StringTokenizer tok = new StringTokenizer(URLs, " ,");
while (tok.hasMoreTokens())
URLList.add(tok.nextToken().trim());
Collections.shuffle(URLList);
for (int i = 0; i < URLList.size() && _isRunning; i++)
reseedOne((String) URLList.get(i), echoStatus);
if (defaulted && !SSLDisable) {
// put the non-SSL at the end of the SSL
List<String> URLList2 = new ArrayList();
tok = new StringTokenizer(DEFAULT_SSL_SEED_URL, " ,");
while (tok.hasMoreTokens())
URLList2.add(tok.nextToken().trim());
Collections.shuffle(URLList2);
URLList.addAll(URLList2);
}
int total = 0;
for (int i = 0; i < URLList.size() && _isRunning; i++) {
String url = URLList.get(i);
int dl = reseedOne(url, echoStatus);
if (dl > 0) {
total += dl;
// remove alternate version if we haven't tried it yet
String alt;
if (url.startsWith("http://"))
alt = url.replace("http://", "https://");
else
alt = url.replace("https://", "http://");
int idx = URLList.indexOf(alt);
if (idx > i)
URLList.remove(i);
}
}
return total;
}
/**
@ -138,22 +218,23 @@ public class Reseeder {
*
* Jetty directory listings are not compatible, as they look like
* HREF="/full/path/to/routerInfo-...
*
* We update PROP_STATUS here.
*
* @param echoStatus apparently always false
* @return count of routerinfos successfully fetched
**/
private void reseedOne(String seedURL, boolean echoStatus) {
private int reseedOne(String seedURL, boolean echoStatus) {
try {
System.setProperty(PROP_ERROR, "");
System.setProperty(PROP_STATUS, _("Reseeding: fetching seed URL."));
System.err.println("Reseed from " + seedURL);
System.err.println("Reseeding from " + seedURL);
URL dir = new URL(seedURL);
byte contentRaw[] = readURL(dir);
if (contentRaw == null) {
System.setProperty(PROP_ERROR,
_("Last reseed failed fully (failed reading seed URL).") + ' ' +
_(RESEED_TIPS));
// Logging deprecated here since attemptFailed() provides better info
_log.debug("Failed reading seed URL: " + seedURL);
return;
_log.warn("Failed reading seed URL: " + seedURL);
System.err.println("Reseed got no router infos from " + seedURL);
return 0;
}
String content = new String(contentRaw);
Set<String> urls = new HashSet(1024);
@ -173,11 +254,9 @@ public class Reseeder {
cur = end + 1;
}
if (total <= 0) {
_log.error("Read " + contentRaw.length + " bytes from seed " + seedURL + ", but found no routerInfo URLs.");
System.setProperty(PROP_ERROR,
_("Last reseed failed fully (no routerInfo URLs at seed URL).") + ' ' +
_(RESEED_TIPS));
return;
_log.warn("Read " + contentRaw.length + " bytes from seed " + seedURL + ", but found no routerInfo URLs.");
System.err.println("Reseed got no router infos from " + seedURL);
return 0;
}
List<String> urlList = new ArrayList(urls);
@ -201,32 +280,18 @@ public class Reseeder {
errors++;
}
}
System.err.println("Reseed got " + fetched + " router infos from " + seedURL);
int failPercent = 100 * errors / total;
// Less than 10% of failures is considered success,
// because some routerInfos will always fail.
if ((failPercent >= 10) && (failPercent < 90)) {
System.setProperty(PROP_ERROR,
_("Last reseed failed partly ({0}% of {1}).", failPercent, total) + ' ' +
_(RESEED_TIPS));
}
if (failPercent >= 90) {
System.setProperty(PROP_ERROR,
_("Last reseed failed ({0}% of {1}).", failPercent, total) + ' ' +
_(RESEED_TIPS));
}
System.err.println("Reseed got " + fetched + " router infos from " + seedURL + " with " + errors + " errors");
if (fetched > 0)
_context.netDb().rescan();
// Don't go on to the next URL if we have enough
if (fetched >= 100)
_isRunning = false;
return fetched;
} catch (Throwable t) {
System.setProperty(PROP_ERROR,
_("Last reseed failed fully (exception caught).") + ' ' +
_(RESEED_TIPS));
_log.error("Error reseeding", t);
_log.warn("Error reseeding", t);
System.err.println("Reseed got no router infos from " + seedURL);
return 0;
}
}
@ -248,8 +313,17 @@ public class Reseeder {
ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024);
EepGet get;
if (url.toString().startsWith("https")) {
get = new SSLEepGet(I2PAppContext.getGlobalContext(), baos, url.toString());
boolean ssl = url.toString().startsWith("https");
if (ssl) {
SSLEepGet sslget;
if (_sslState == null) {
sslget = new SSLEepGet(I2PAppContext.getGlobalContext(), baos, url.toString());
// save state for next time
_sslState = sslget.getSSLState();
} else {
sslget = new SSLEepGet(I2PAppContext.getGlobalContext(), baos, url.toString(), _sslState);
}
get = sslget;
} else {
// Do a (probably) non-proxied eepget into our ByteArrayOutputStream with 0 retries
boolean shouldProxy = _proxyHost != null && _proxyHost.length() > 0 && _proxyPort > 0;
@ -257,7 +331,9 @@ public class Reseeder {
null, baos, url.toString(), false, null, null);
}
get.addStatusListener(ReseedRunner.this);
if (get.fetch()) return baos.toByteArray(); else return null;
if (get.fetch())
return baos.toByteArray();
return null;
}
private void writeSeed(String name, byte data[]) throws Exception {
@ -295,6 +371,11 @@ public class Reseeder {
return Translate.getString(s, o, o2, _context, BUNDLE_NAME);
}
/** translate */
private String ngettext(String s, String p, int n) {
return Translate.getString(n, s, p, _context, BUNDLE_NAME);
}
/******
public static void main(String args[]) {
if ( (args != null) && (args.length == 1) && (!Boolean.valueOf(args[0]).booleanValue()) ) {