forked from I2P_Developers/i2p.i2p
Reseed:
- Add form to manually reseed from zip or su3 URL (result status not yet working) - Add form to manually reseed from local zip or su3 file (not yet working, needs multipart/form-date moved from susimail) - Add form to create reseed zip file to share (working) - Backend support and refactoring in reseed code
This commit is contained in:
@ -1,10 +1,16 @@
|
|||||||
package net.i2p.router.web;
|
package net.i2p.router.web;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.router.networkdb.reseed.Reseeder;
|
import net.i2p.router.networkdb.reseed.Reseeder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,11 +31,72 @@ public class ConfigReseedHandler extends FormHandler {
|
|||||||
// skip the nonce checking in ReseedHandler
|
// skip the nonce checking in ReseedHandler
|
||||||
addFormNotice(_("Starting reseed process"));
|
addFormNotice(_("Starting reseed process"));
|
||||||
}
|
}
|
||||||
return;
|
} else if (_action.equals(_("Reseed from URL"))) {
|
||||||
}
|
String val = getJettyString("url");
|
||||||
if (_action.equals(_("Save changes"))) {
|
if (val != null)
|
||||||
|
val = val.trim();
|
||||||
|
if (val == null || val.length() == 0) {
|
||||||
|
addFormError(_("You must enter a URL"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
URL url;
|
||||||
|
try {
|
||||||
|
url = new URL(val);
|
||||||
|
} catch (MalformedURLException mue) {
|
||||||
|
addFormError(_("Bad URL {0}", val));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (!_context.netDb().reseedChecker().requestReseed(url)) {
|
||||||
|
addFormError(_("Reseeding is already in progress"));
|
||||||
|
} else {
|
||||||
|
// wait a while for completion but not forever
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException ie) {}
|
||||||
|
if (!_context.netDb().reseedChecker().inProgress()) {
|
||||||
|
String status = _context.netDb().reseedChecker().getStatus();
|
||||||
|
if (status.length() > 0)
|
||||||
|
addFormNotice(status);
|
||||||
|
else
|
||||||
|
addFormNotice(_("Ressed complete, check summary bar for status"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_context.netDb().reseedChecker().inProgress()) {
|
||||||
|
String status = _context.netDb().reseedChecker().getStatus();
|
||||||
|
if (status.length() > 0)
|
||||||
|
addFormNotice(status);
|
||||||
|
else
|
||||||
|
addFormNotice(_("Ressed in progress, check summary bar for status"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
addFormError(_("Bad URL {0}", val) + " - " + iae.getMessage());
|
||||||
|
}
|
||||||
|
} else if (_action.equals(_("Reseed from file"))) {
|
||||||
|
//////// FIXME multipart
|
||||||
|
String val = getJettyString("file");
|
||||||
|
if (val == null || val.length() == 0) {
|
||||||
|
addFormError(_("You must enter a file"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(DataHelper.getASCII(val));
|
||||||
|
try {
|
||||||
|
int count = _context.netDb().reseedChecker().requestReseed(bais);
|
||||||
|
if (count <= 0) {
|
||||||
|
addFormError(_("Reseed from file failed"));
|
||||||
|
} else {
|
||||||
|
addFormNotice(ngettext("Reseed successful, loaded {0} router info from file",
|
||||||
|
"Reseed successful, loaded {0} router infos from file",
|
||||||
|
count));
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
addFormError(_("Reseed from file failed") + " - " + ioe);
|
||||||
|
}
|
||||||
|
} else if (_action.equals(_("Save changes"))) {
|
||||||
saveChanges();
|
saveChanges();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
//addFormError(_("Unsupported") + ' ' + _action + '.');
|
//addFormError(_("Unsupported") + ' ' + _action + '.');
|
||||||
}
|
}
|
||||||
@ -84,4 +151,9 @@ public class ConfigReseedHandler extends FormHandler {
|
|||||||
else
|
else
|
||||||
addFormError(_("Error saving the configuration (applied but not saved) - please see the error logs"));
|
addFormError(_("Error saving the configuration (applied but not saved) - please see the error logs"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** translate (ngettext) @since 0.9.19 */
|
||||||
|
public String ngettext(String s, String p, int n) {
|
||||||
|
return Messages.getString(n, s, p, _context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,163 @@
|
|||||||
|
package net.i2p.router.web;
|
||||||
|
/*
|
||||||
|
* free (adj.): unencumbered; not under the control of others
|
||||||
|
* Written by jrandom in 2003 and released into the public domain
|
||||||
|
* with no warranty of any kind, either expressed or implied.
|
||||||
|
* It probably won't make your computer catch on fire, or eat
|
||||||
|
* your children, but it might. Use at your own risk.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
import net.i2p.data.DataFormatException;
|
||||||
|
import net.i2p.data.Hash;
|
||||||
|
import net.i2p.data.router.RouterAddress;
|
||||||
|
import net.i2p.data.router.RouterInfo;
|
||||||
|
import net.i2p.router.RouterContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy a random selection of 'count' router infos from configDir/netDb
|
||||||
|
* to 'toDir'. Skip your own router info, and old, hidden, unreachable, and
|
||||||
|
* introduced routers, and those from bad countries.
|
||||||
|
*
|
||||||
|
* Much easier than the one in installer/tools since we have a running router.
|
||||||
|
*
|
||||||
|
* Caller must delete file when done.
|
||||||
|
*
|
||||||
|
* @since 0.9.19 modified from BundleRouterInfos in installer/tools
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class ReseedBundler {
|
||||||
|
|
||||||
|
private final RouterContext _context;
|
||||||
|
private final static String ROUTERINFO_PREFIX = "routerInfo-";
|
||||||
|
private final static String ROUTERINFO_SUFFIX = ".dat";
|
||||||
|
|
||||||
|
public ReseedBundler(RouterContext ctx) {
|
||||||
|
_context = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a zip file with
|
||||||
|
* a random selection of 'count' router infos from configDir/netDb
|
||||||
|
* to 'toDir'. Skip your own router info, and old, hidden, unreachable, and
|
||||||
|
* introduced routers, and those from bad countries.
|
||||||
|
*
|
||||||
|
* The file will be in the temp directory. Caller must move or delete.
|
||||||
|
*/
|
||||||
|
public File createZip(int count) throws IOException {
|
||||||
|
Hash me = _context.routerHash();
|
||||||
|
int routerCount = 0;
|
||||||
|
int copied = 0;
|
||||||
|
long tooOld = System.currentTimeMillis() - 7*24*60*60*1000L;
|
||||||
|
List<RouterInfo> infos = new ArrayList<RouterInfo>(_context.netDb().getRouters());
|
||||||
|
// IP to router hash
|
||||||
|
Map<String, Hash> ipMap = new HashMap<String, Hash>(count);
|
||||||
|
List<RouterInfo> toWrite = new ArrayList<RouterInfo>(count);
|
||||||
|
Collections.shuffle(infos);
|
||||||
|
for (RouterInfo ri : infos) {
|
||||||
|
if (copied >= count)
|
||||||
|
break;
|
||||||
|
Hash key = ri.getIdentity().calculateHash();
|
||||||
|
if (key.equals(me)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ri.getPublished() < tooOld)
|
||||||
|
continue;
|
||||||
|
if (ri.getCapabilities().contains("U"))
|
||||||
|
continue;
|
||||||
|
if (ri.getCapabilities().contains("K"))
|
||||||
|
continue;
|
||||||
|
Collection<RouterAddress> addrs = ri.getAddresses();
|
||||||
|
if (addrs.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String name = getRouterInfoName(key);
|
||||||
|
boolean hasIntro = false;
|
||||||
|
boolean hasIPv4 = false;
|
||||||
|
boolean dupIP = false;
|
||||||
|
for (RouterAddress addr : addrs) {
|
||||||
|
if ("SSU".equals(addr.getTransportStyle()) && addr.getOption("ihost0") != null) {
|
||||||
|
hasIntro = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
String host = addr.getHost();
|
||||||
|
if (host != null && host.contains(".")) {
|
||||||
|
hasIPv4 = true;
|
||||||
|
Hash old = ipMap.put(host, key);
|
||||||
|
if (old != null && !old.equals(key)) {
|
||||||
|
dupIP = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dupIP)
|
||||||
|
continue;
|
||||||
|
if (hasIntro)
|
||||||
|
continue;
|
||||||
|
if (!hasIPv4)
|
||||||
|
continue;
|
||||||
|
if (_context.commSystem().isInBadCountry(ri))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
toWrite.add(ri);
|
||||||
|
copied++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toWrite.isEmpty())
|
||||||
|
throw new IOException("No router infos to include");
|
||||||
|
|
||||||
|
File rv = new File(_context.getTempDir(), "genreseed-" + _context.random().nextInt() + ".zip");
|
||||||
|
ZipOutputStream zip = null;
|
||||||
|
try {
|
||||||
|
zip = new ZipOutputStream(new FileOutputStream(rv) );
|
||||||
|
for (RouterInfo ri : toWrite) {
|
||||||
|
String name = getRouterInfoName(ri.getIdentity().calculateHash());
|
||||||
|
ZipEntry entry = new ZipEntry(name);
|
||||||
|
entry.setTime(ri.getPublished());
|
||||||
|
zip.putNextEntry(entry);
|
||||||
|
ri.writeBytes(zip);
|
||||||
|
}
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
rv.delete();
|
||||||
|
IOException ioe = new IOException(dfe.getMessage());
|
||||||
|
ioe.initCause(dfe);
|
||||||
|
throw ioe;
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
rv.delete();
|
||||||
|
throw ioe;
|
||||||
|
} finally {
|
||||||
|
if ( zip != null) {
|
||||||
|
try {
|
||||||
|
zip.finish();
|
||||||
|
zip.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
rv.delete();
|
||||||
|
throw ioe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copied/modded from PersistentDataStore
|
||||||
|
*/
|
||||||
|
private static String getRouterInfoName(Hash hash) {
|
||||||
|
String b64 = hash.toBase64();
|
||||||
|
return ROUTERINFO_PREFIX + b64 + ROUTERINFO_SUFFIX;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package net.i2p.router.web;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to create a i2preseed.zip file
|
||||||
|
* @since 0.9.19
|
||||||
|
*/
|
||||||
|
public class ReseedGenerator extends HelperBase {
|
||||||
|
|
||||||
|
public File createZip() throws IOException {
|
||||||
|
ReseedBundler rb = new ReseedBundler(_context);
|
||||||
|
return rb.createZip(100);
|
||||||
|
}
|
||||||
|
}
|
@ -19,8 +19,40 @@
|
|||||||
|
|
||||||
<jsp:useBean class="net.i2p.router.web.ConfigReseedHandler" id="formhandler" scope="request" />
|
<jsp:useBean class="net.i2p.router.web.ConfigReseedHandler" id="formhandler" scope="request" />
|
||||||
<%@include file="formhandler.jsi" %>
|
<%@include file="formhandler.jsi" %>
|
||||||
|
|
||||||
<div class="configure"><form action="" method="POST">
|
<div class="configure"><form action="" method="POST">
|
||||||
<input type="hidden" name="nonce" value="<%=pageNonce%>" >
|
<input type="hidden" name="nonce" value="<%=pageNonce%>" >
|
||||||
|
<h3><%=intl._("Manual Reseed from URL")%></h3>
|
||||||
|
<p><%=intl._("Enter zip or su3 URL")%> :
|
||||||
|
<input name="url" type="text" size="60" value="" />
|
||||||
|
</p>
|
||||||
|
<div class="formaction">
|
||||||
|
<input type="submit" name="action" class="download" value="<%=intl._("Reseed from URL")%>" />
|
||||||
|
</div></form></div>
|
||||||
|
|
||||||
|
<div class="configure">
|
||||||
|
<form action="" method="POST" enctype="multipart/form-data" >
|
||||||
|
<input type="hidden" name="nonce" value="<%=pageNonce%>" >
|
||||||
|
<h3><%=intl._("Manual Reseed from File")%></h3>
|
||||||
|
<p><%=intl._("Select zip or su3 file")%> :
|
||||||
|
<input name="file" type="file" value="" />
|
||||||
|
</p>
|
||||||
|
<div class="formaction">
|
||||||
|
<input type="submit" name="action" class="download" value="<%=intl._("Reseed from file")%>" />
|
||||||
|
</div></form></div>
|
||||||
|
|
||||||
|
<div class="configure">
|
||||||
|
<form action="/createreseed" method="GET">
|
||||||
|
<h3><%=intl._("Create Reseed File")%></h3>
|
||||||
|
<p><%=intl._("Create a new reseed zip file you may share for others to reseed manually.")%>
|
||||||
|
</p>
|
||||||
|
<div class="formaction">
|
||||||
|
<input type="submit" name="action" class="go" value="<%=intl._("Create reseed file")%>" />
|
||||||
|
</div></form></div>
|
||||||
|
|
||||||
|
<div class="configure">
|
||||||
|
<form action="" method="POST">
|
||||||
|
<input type="hidden" name="nonce" value="<%=pageNonce%>" >
|
||||||
<h3><%=intl._("Reseeding Configuration")%></h3>
|
<h3><%=intl._("Reseeding Configuration")%></h3>
|
||||||
<p><%=intl._("Reseeding is the bootstrapping process used to find other routers when you first install I2P, or when your router has too few router references remaining.")%>
|
<p><%=intl._("Reseeding is the bootstrapping process used to find other routers when you first install I2P, or when your router has too few router references remaining.")%>
|
||||||
<%=intl._("If reseeding has failed, you should first check your network connection.")%>
|
<%=intl._("If reseeding has failed, you should first check your network connection.")%>
|
||||||
@ -71,7 +103,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
</table></div>
|
</table></div>
|
||||||
<hr><div class="formaction">
|
<div class="formaction">
|
||||||
<input type="submit" class="cancel" name="foo" value="<%=intl._("Cancel")%>" />
|
<input type="submit" class="cancel" name="foo" value="<%=intl._("Cancel")%>" />
|
||||||
<input type="submit" name="action" class="download" value="<%=intl._("Save changes and reseed now")%>" />
|
<input type="submit" name="action" class="download" value="<%=intl._("Save changes and reseed now")%>" />
|
||||||
<input type="submit" name="action" class="accept" value="<%=intl._("Save changes")%>" />
|
<input type="submit" name="action" class="accept" value="<%=intl._("Save changes")%>" />
|
||||||
|
45
apps/routerconsole/jsp/createreseed.jsp
Normal file
45
apps/routerconsole/jsp/createreseed.jsp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<jsp:useBean class="net.i2p.router.web.ReseedGenerator" id="gen" scope="request" /><jsp:setProperty name="gen" property="contextId" value="<%=(String)session.getAttribute(\"i2p.contextId\")%>" /><%
|
||||||
|
/*
|
||||||
|
* USE CAUTION WHEN EDITING
|
||||||
|
* Trailing whitespace OR NEWLINE on the last line will cause
|
||||||
|
* IllegalStateExceptions !!!
|
||||||
|
*
|
||||||
|
* Do not tag this file for translation.
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
java.io.InputStream in = null;
|
||||||
|
java.io.File zip = null;
|
||||||
|
try {
|
||||||
|
zip = gen.createZip();
|
||||||
|
response.setContentLength((int) zip.length());
|
||||||
|
long lastmod = zip.lastModified();
|
||||||
|
if (lastmod > 0)
|
||||||
|
response.setDateHeader("Last-Modified", lastmod);
|
||||||
|
response.setDateHeader("Expires", 0);
|
||||||
|
response.addHeader("Cache-Control", "no-store, max-age=0, no-cache, must-revalidate");
|
||||||
|
response.addHeader("Pragma", "no-cache");
|
||||||
|
response.setContentType("application/zip; name=\"i2preseed.zip\"");
|
||||||
|
response.addHeader("Content-Disposition", "attachment; filename=\"i2preseed.zip\"");
|
||||||
|
byte buf[] = new byte[16*1024];
|
||||||
|
in = new java.io.FileInputStream(zip);
|
||||||
|
int read = 0;
|
||||||
|
java.io.OutputStream cout = response.getOutputStream();
|
||||||
|
while ( (read = in.read(buf)) != -1) {
|
||||||
|
cout.write(buf, 0, read);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (in != null)
|
||||||
|
try { in.close(); } catch (java.io.IOException ioe) {}
|
||||||
|
if (zip != null)
|
||||||
|
zip.delete();
|
||||||
|
}
|
||||||
|
} catch (java.io.IOException ioe) {
|
||||||
|
// prevent 'Committed' IllegalStateException from Jetty
|
||||||
|
if (!response.isCommitted()) {
|
||||||
|
response.sendError(403, ioe.toString());
|
||||||
|
} else {
|
||||||
|
// Jetty doesn't log this
|
||||||
|
throw ioe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
%>
|
11
history.txt
11
history.txt
@ -1,9 +1,20 @@
|
|||||||
|
2015-03-19 zzz
|
||||||
|
* Reseed:
|
||||||
|
- Add form to manually reseed from zip or su3 URL
|
||||||
|
- Add form to manually reseed from local zip or su3 file
|
||||||
|
- Add form to create reseed zip file to share
|
||||||
|
- Backend support and refactoring in reseed code
|
||||||
|
|
||||||
2015-03-18 zzz
|
2015-03-18 zzz
|
||||||
* NetDB:
|
* NetDB:
|
||||||
- Send exploratory lookups directly to the floodfill if
|
- Send exploratory lookups directly to the floodfill if
|
||||||
we are already connected to him
|
we are already connected to him
|
||||||
- Don't encrypt RI lookups when overloaded
|
- Don't encrypt RI lookups when overloaded
|
||||||
- Don't explore when overloaded
|
- Don't explore when overloaded
|
||||||
|
- Don't publish non-ff RI on exit if we are coming right back
|
||||||
|
* Router: Allow disabling the setting of some System properties, for embedded applications
|
||||||
|
* StatisticsManager: Publish dummy LS count if we just started
|
||||||
|
* Streaming: Reduce min RTO again
|
||||||
* Tunnels: Drop instead of reject requests on high job lag
|
* Tunnels: Drop instead of reject requests on high job lag
|
||||||
* UPnP: Update to cyberlink 3.0
|
* UPnP: Update to cyberlink 3.0
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
|||||||
/** deprecated */
|
/** deprecated */
|
||||||
public final static String ID = "Monotone";
|
public final static String ID = "Monotone";
|
||||||
public final static String VERSION = CoreVersion.VERSION;
|
public final static String VERSION = CoreVersion.VERSION;
|
||||||
public final static long BUILD = 6;
|
public final static long BUILD = 7;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package net.i2p.router.networkdb.reseed;
|
package net.i2p.router.networkdb.reseed;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
@ -107,6 +110,55 @@ public class ReseedChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 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.19
|
||||||
|
*/
|
||||||
|
public boolean requestReseed(URL url) throws IllegalArgumentException {
|
||||||
|
if (_inProgress.compareAndSet(false, true)) {
|
||||||
|
Reseeder reseeder = new Reseeder(_context, this);
|
||||||
|
try {
|
||||||
|
reseeder.requestReseed(url);
|
||||||
|
return true;
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
done();
|
||||||
|
throw iae;
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reseed from a zip or su3 input stream. Blocking.
|
||||||
|
*
|
||||||
|
* @return true if a reseed was started, false if already in progress
|
||||||
|
* @throws IOException if already in progress or on most other errors
|
||||||
|
* @since 0.9.19
|
||||||
|
*/
|
||||||
|
public int requestReseed(InputStream in) throws IOException {
|
||||||
|
// don't really need to check for in progress here
|
||||||
|
if (_inProgress.compareAndSet(false, true)) {
|
||||||
|
try {
|
||||||
|
Reseeder reseeder = new Reseeder(_context, this);
|
||||||
|
return reseeder.requestReseed(in);
|
||||||
|
} finally {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IOException("Reseed already in progress");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** .
|
||||||
* Is a reseed in progress?
|
* Is a reseed in progress?
|
||||||
*
|
*
|
||||||
* @since 0.9
|
* @since 0.9
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
package net.i2p.router.networkdb.reseed;
|
package net.i2p.router.networkdb.reseed;
|
||||||
|
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
@ -130,6 +133,11 @@ public class Reseeder {
|
|||||||
_checker = rc;
|
_checker = rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a reseed using the default reseed URLs.
|
||||||
|
* Supports su3 and directories.
|
||||||
|
* Threaded, nonblocking.
|
||||||
|
*/
|
||||||
void requestReseed() {
|
void requestReseed() {
|
||||||
ReseedRunner reseedRunner = new ReseedRunner();
|
ReseedRunner reseedRunner = new ReseedRunner();
|
||||||
// set to daemon so it doesn't hang a shutdown
|
// set to daemon so it doesn't hang a shutdown
|
||||||
@ -137,6 +145,77 @@ public class Reseeder {
|
|||||||
reseed.start();
|
reseed.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a reseed from a single zip or su3 URL only.
|
||||||
|
* Threaded, nonblocking.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if it doesn't end with zip or su3
|
||||||
|
* @since 0.9.19
|
||||||
|
*/
|
||||||
|
void requestReseed(URL url) throws IllegalArgumentException {
|
||||||
|
ReseedRunner reseedRunner = new ReseedRunner(url);
|
||||||
|
// set to daemon so it doesn't hang a shutdown
|
||||||
|
Thread reseed = new I2PAppThread(reseedRunner, "Reseed", true);
|
||||||
|
reseed.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a reseed from a zip or su3 input stream.
|
||||||
|
* Blocking, inline. Should be fast.
|
||||||
|
* This will close the stream.
|
||||||
|
*
|
||||||
|
* @return number of valid routerinfos imported
|
||||||
|
* @throws IOException on most errors
|
||||||
|
* @since 0.9.19
|
||||||
|
*/
|
||||||
|
int requestReseed(InputStream in) throws IOException {
|
||||||
|
byte[] su3Magic = DataHelper.getASCII(SU3File.MAGIC);
|
||||||
|
byte[] zipMagic = new byte[] { 0x1F, (byte) 0x8B, 0x08 };
|
||||||
|
int len = Math.max(su3Magic.length, zipMagic.length);
|
||||||
|
byte[] magic = new byte[len];
|
||||||
|
File tmp = null;
|
||||||
|
OutputStream out = null;
|
||||||
|
try {
|
||||||
|
DataHelper.read(in, magic);
|
||||||
|
boolean isSU3;
|
||||||
|
if (DataHelper.eq(magic, 0, su3Magic, 0, su3Magic.length))
|
||||||
|
isSU3 = true;
|
||||||
|
else if (DataHelper.eq(magic, 0, zipMagic, 0, zipMagic.length))
|
||||||
|
isSU3 = false;
|
||||||
|
else
|
||||||
|
throw new IOException("Not a zip or su3 file");
|
||||||
|
tmp = new File(_context.getTempDir(), "manualreseeds-" + _context.random().nextInt() + (isSU3 ? ".su3" : ".zip"));
|
||||||
|
out = new BufferedOutputStream(new SecureFileOutputStream(tmp));
|
||||||
|
out.write(magic);
|
||||||
|
byte buf[] = new byte[16*1024];
|
||||||
|
int read = 0;
|
||||||
|
while ( (read = in.read(buf)) != -1)
|
||||||
|
out.write(buf, 0, read);
|
||||||
|
out.close();
|
||||||
|
int[] stats;
|
||||||
|
ReseedRunner reseedRunner = new ReseedRunner();
|
||||||
|
// inline
|
||||||
|
if (isSU3)
|
||||||
|
stats = reseedRunner.extractSU3(tmp);
|
||||||
|
else
|
||||||
|
stats = reseedRunner.extractZip(tmp);
|
||||||
|
int fetched = stats[0];
|
||||||
|
int errors = stats[1];
|
||||||
|
if (fetched <= 0)
|
||||||
|
throw new IOException("No seeds extracted");
|
||||||
|
_checker.setStatus(
|
||||||
|
_("Reseeding: got router info from file ({0} successful, {1} errors).", fetched, errors));
|
||||||
|
System.err.println("Reseed got " + fetched + " router infos from file with " + errors + " errors");
|
||||||
|
_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) {}
|
||||||
|
if (tmp != null)
|
||||||
|
tmp.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class ReseedRunner implements Runnable, EepGet.StatusListener {
|
private class ReseedRunner implements Runnable, EepGet.StatusListener {
|
||||||
private boolean _isRunning;
|
private boolean _isRunning;
|
||||||
private String _proxyHost;
|
private String _proxyHost;
|
||||||
@ -147,8 +226,28 @@ public class Reseeder {
|
|||||||
/** bytes per sec for each su3 downloaded */
|
/** bytes per sec for each su3 downloaded */
|
||||||
private final List<Long> _bandwidths;
|
private final List<Long> _bandwidths;
|
||||||
private static final int MAX_DATE_SETS = 2;
|
private static final int MAX_DATE_SETS = 2;
|
||||||
|
private final URL _url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a reseed from the default URL list
|
||||||
|
*/
|
||||||
public ReseedRunner() {
|
public ReseedRunner() {
|
||||||
|
_url = null;
|
||||||
|
_bandwidths = new ArrayList<Long>(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(URL url) throws IllegalArgumentException {
|
||||||
|
String lc = url.getPath().toLowerCase(Locale.US);
|
||||||
|
if (!(lc.endsWith(".zip") || lc.endsWith(".su3")))
|
||||||
|
throw new IllegalArgumentException("Reseed URL must end with .zip or .su3");
|
||||||
|
_url = url;
|
||||||
_bandwidths = new ArrayList<Long>(4);
|
_bandwidths = new ArrayList<Long>(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +272,18 @@ public class Reseeder {
|
|||||||
_proxyPort = _context.getProperty(PROP_PROXY_PORT, -1);
|
_proxyPort = _context.getProperty(PROP_PROXY_PORT, -1);
|
||||||
}
|
}
|
||||||
System.out.println("Reseed start");
|
System.out.println("Reseed start");
|
||||||
int total = reseed(false);
|
int total;
|
||||||
|
if (_url != null) {
|
||||||
|
String lc = _url.getPath().toLowerCase(Locale.US);
|
||||||
|
if (lc.endsWith(".su3"))
|
||||||
|
total = reseedSU3(_url, false);
|
||||||
|
else if (lc.endsWith(".zip"))
|
||||||
|
total = reseedZip(_url, false);
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException("Must end with .zip or .su3");
|
||||||
|
} else {
|
||||||
|
total = reseed(false);
|
||||||
|
}
|
||||||
if (total >= 50) {
|
if (total >= 50) {
|
||||||
System.out.println("Reseed complete, " + total + " received");
|
System.out.println("Reseed complete, " + total + " received");
|
||||||
_checker.setError("");
|
_checker.setError("");
|
||||||
@ -319,6 +429,18 @@ public class Reseeder {
|
|||||||
Collections.shuffle(URLList2, _context.random());
|
Collections.shuffle(URLList2, _context.random());
|
||||||
URLList.addAll(URLList2);
|
URLList.addAll(URLList2);
|
||||||
}
|
}
|
||||||
|
return reseed(URLList, echoStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reseed has been requested, so lets go ahead and do it. Fetch all of
|
||||||
|
* the routerInfo-*.dat files from the specified URLs
|
||||||
|
* save them into this router's netDb dir.
|
||||||
|
*
|
||||||
|
* @param echoStatus apparently always false
|
||||||
|
* @return count of routerinfos successfully fetched
|
||||||
|
*/
|
||||||
|
private int reseed(List<URL> URLList, boolean echoStatus) {
|
||||||
int total = 0;
|
int total = 0;
|
||||||
for (int i = 0; i < URLList.size() && _isRunning; i++) {
|
for (int i = 0; i < URLList.size() && _isRunning; i++) {
|
||||||
URL url = URLList.get(i);
|
URL url = URLList.get(i);
|
||||||
@ -470,12 +592,38 @@ public class Reseeder {
|
|||||||
* @return count of routerinfos successfully fetched
|
* @return count of routerinfos successfully fetched
|
||||||
* @since 0.9.14
|
* @since 0.9.14
|
||||||
**/
|
**/
|
||||||
private int reseedSU3(URL seedURL, boolean echoStatus) {
|
public int reseedSU3(URL seedURL, boolean echoStatus) {
|
||||||
|
return reseedSU3OrZip(seedURL, true, echoStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a zip file containing routerInfo files
|
||||||
|
*
|
||||||
|
* We update the status here.
|
||||||
|
*
|
||||||
|
* @param seedURL the URL of the zip file
|
||||||
|
* @param echoStatus apparently always false
|
||||||
|
* @return count of routerinfos successfully fetched
|
||||||
|
* @since 0.9.19
|
||||||
|
**/
|
||||||
|
public int reseedZip(URL seedURL, boolean echoStatus) {
|
||||||
|
return reseedSU3OrZip(seedURL, false, echoStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch an su3 or zip file containing routerInfo files
|
||||||
|
*
|
||||||
|
* We update the status here.
|
||||||
|
*
|
||||||
|
* @param seedURL the URL of the SU3 or zip file
|
||||||
|
* @param echoStatus apparently always false
|
||||||
|
* @return count of routerinfos successfully fetched
|
||||||
|
* @since 0.9.19
|
||||||
|
**/
|
||||||
|
private int reseedSU3OrZip(URL seedURL, boolean isSU3, boolean echoStatus) {
|
||||||
int fetched = 0;
|
int fetched = 0;
|
||||||
int errors = 0;
|
int errors = 0;
|
||||||
File contentRaw = null;
|
File contentRaw = null;
|
||||||
File zip = null;
|
|
||||||
File tmpDir = null;
|
|
||||||
try {
|
try {
|
||||||
_checker.setStatus(_("Reseeding: fetching seed URL."));
|
_checker.setStatus(_("Reseeding: fetching seed URL."));
|
||||||
System.err.println("Reseeding from " + seedURL);
|
System.err.println("Reseeding from " + seedURL);
|
||||||
@ -497,6 +645,37 @@ public class Reseeder {
|
|||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Rcvd " + sz + " bytes in " + totalTime + " ms from " + seedURL);
|
_log.debug("Rcvd " + sz + " bytes in " + totalTime + " ms from " + seedURL);
|
||||||
}
|
}
|
||||||
|
int[] stats;
|
||||||
|
if (isSU3)
|
||||||
|
stats = extractSU3(contentRaw);
|
||||||
|
else
|
||||||
|
stats = extractZip(contentRaw);
|
||||||
|
fetched = stats[0];
|
||||||
|
errors = stats[1];
|
||||||
|
} catch (Throwable t) {
|
||||||
|
System.err.println("Error reseeding: " + t);
|
||||||
|
_log.error("Error reseeding", t);
|
||||||
|
errors++;
|
||||||
|
} finally {
|
||||||
|
if (contentRaw != null)
|
||||||
|
contentRaw.delete();
|
||||||
|
}
|
||||||
|
_checker.setStatus(
|
||||||
|
_("Reseeding: fetching router info from seed URL ({0} successful, {1} errors).", fetched, errors));
|
||||||
|
System.err.println("Reseed got " + fetched + " router infos from " + seedURL + " with " + errors + " errors");
|
||||||
|
return fetched;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 2 ints: number successful and number of errors
|
||||||
|
* @since 0.9.19 pulled from reseedSU3
|
||||||
|
*/
|
||||||
|
public int[] extractSU3(File contentRaw) throws IOException {
|
||||||
|
int fetched = 0;
|
||||||
|
int errors = 0;
|
||||||
|
File zip = null;
|
||||||
|
try {
|
||||||
SU3File su3 = new SU3File(_context, contentRaw);
|
SU3File su3 = new SU3File(_context, contentRaw);
|
||||||
zip = new File(_context.getTempDir(), "reseed-" + _context.random().nextInt() + ".zip");
|
zip = new File(_context.getTempDir(), "reseed-" + _context.random().nextInt() + ".zip");
|
||||||
su3.verifyAndMigrate(zip);
|
su3.verifyAndMigrate(zip);
|
||||||
@ -514,6 +693,35 @@ public class Reseeder {
|
|||||||
throw new IOException("su3 file too old");
|
throw new IOException("su3 file too old");
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException nfe) {}
|
} catch (NumberFormatException nfe) {}
|
||||||
|
|
||||||
|
int[] stats = extractZip(zip);
|
||||||
|
fetched = stats[0];
|
||||||
|
errors = stats[1];
|
||||||
|
} catch (Throwable t) {
|
||||||
|
System.err.println("Error reseeding: " + t);
|
||||||
|
_log.error("Error reseeding", t);
|
||||||
|
errors++;
|
||||||
|
} finally {
|
||||||
|
contentRaw.delete();
|
||||||
|
if (zip != null)
|
||||||
|
zip.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] rv = new int[2];
|
||||||
|
rv[0] = fetched;
|
||||||
|
rv[1] = errors;
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 2 ints: number successful and number of errors
|
||||||
|
* @since 0.9.19 pulled from reseedSU3
|
||||||
|
*/
|
||||||
|
public int[] extractZip(File zip) throws IOException {
|
||||||
|
int fetched = 0;
|
||||||
|
int errors = 0;
|
||||||
|
File tmpDir = null;
|
||||||
|
try {
|
||||||
tmpDir = new File(_context.getTempDir(), "reseeds-" + _context.random().nextInt());
|
tmpDir = new File(_context.getTempDir(), "reseeds-" + _context.random().nextInt());
|
||||||
if (!FileUtil.extractZip(zip, tmpDir))
|
if (!FileUtil.extractZip(zip, tmpDir))
|
||||||
throw new IOException("Bad zip file");
|
throw new IOException("Bad zip file");
|
||||||
@ -559,24 +767,17 @@ public class Reseeder {
|
|||||||
if (errors >= 5)
|
if (errors >= 5)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
|
||||||
System.err.println("Error reseeding: " + t);
|
|
||||||
_log.error("Error reseeding", t);
|
|
||||||
errors++;
|
|
||||||
} finally {
|
} finally {
|
||||||
if (contentRaw != null)
|
|
||||||
contentRaw.delete();
|
|
||||||
if (zip != null)
|
|
||||||
zip.delete();
|
|
||||||
if (tmpDir != null)
|
if (tmpDir != null)
|
||||||
FileUtil.rmdir(tmpDir, false);
|
FileUtil.rmdir(tmpDir, false);
|
||||||
}
|
}
|
||||||
_checker.setStatus(
|
|
||||||
_("Reseeding: fetching router info from seed URL ({0} successful, {1} errors).", fetched, errors));
|
|
||||||
System.err.println("Reseed got " + fetched + " router infos from " + seedURL + " with " + errors + " errors");
|
|
||||||
if (fetched > 0)
|
if (fetched > 0)
|
||||||
_context.netDb().rescan();
|
_context.netDb().rescan();
|
||||||
return fetched;
|
int[] rv = new int[2];
|
||||||
|
rv[0] = fetched;
|
||||||
|
rv[1] = errors;
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user