- 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:
zzz
2015-03-19 23:17:18 +00:00
parent 8742a66f2f
commit 59348f8dbd
9 changed files with 614 additions and 22 deletions

View File

@ -1,10 +1,16 @@
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.HashMap;
import java.util.List;
import java.util.Map;
import net.i2p.data.DataHelper;
import net.i2p.router.networkdb.reseed.Reseeder;
/**
@ -25,11 +31,72 @@ public class ConfigReseedHandler extends FormHandler {
// skip the nonce checking in ReseedHandler
addFormNotice(_("Starting reseed process"));
}
return;
}
if (_action.equals(_("Save changes"))) {
} else if (_action.equals(_("Reseed from URL"))) {
String val = getJettyString("url");
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();
return;
}
//addFormError(_("Unsupported") + ' ' + _action + '.');
}
@ -84,4 +151,9 @@ public class ConfigReseedHandler extends FormHandler {
else
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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}