forked from I2P_Developers/i2p.i2p
* NetDB:
- Don't reread RI if netdb date is recent - Prevent LS/RI overwrites - Disallow hash mismatches in RI files - Reseed won't fetch our own RI - Reseed won't overwrite recent RIs
This commit is contained in:
@ -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 = 18;
|
||||
public final static long BUILD = 19;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
@ -684,7 +684,9 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
||||
// if it hasn't changed, no need to do anything
|
||||
return rv;
|
||||
}
|
||||
} catch (ClassCastException cce) {}
|
||||
} catch (ClassCastException cce) {
|
||||
throw new IllegalArgumentException("Attempt to replace RI with " + leaseSet);
|
||||
}
|
||||
|
||||
String err = validate(key, leaseSet);
|
||||
if (err != null)
|
||||
@ -811,7 +813,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
||||
return store(key, routerInfo, true);
|
||||
}
|
||||
|
||||
public RouterInfo store(Hash key, RouterInfo routerInfo, boolean persist) throws IllegalArgumentException {
|
||||
RouterInfo store(Hash key, RouterInfo routerInfo, boolean persist) throws IllegalArgumentException {
|
||||
if (!_initialized) return null;
|
||||
|
||||
RouterInfo rv = null;
|
||||
@ -821,7 +823,9 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
||||
// no need to validate
|
||||
return rv;
|
||||
}
|
||||
} catch (ClassCastException cce) {}
|
||||
} catch (ClassCastException cce) {
|
||||
throw new IllegalArgumentException("Attempt to replace LS with " + routerInfo);
|
||||
}
|
||||
|
||||
String err = validate(key, routerInfo);
|
||||
if (err != null)
|
||||
|
@ -392,12 +392,17 @@ class PersistentDataStore extends TransientDataStore {
|
||||
private class ReadRouterJob extends JobImpl {
|
||||
private final File _routerFile;
|
||||
private final Hash _key;
|
||||
private long _knownDate;
|
||||
|
||||
/**
|
||||
* @param key must match the RI hash in the file
|
||||
*/
|
||||
public ReadRouterJob(File routerFile, Hash key) {
|
||||
super(PersistentDataStore.this._context);
|
||||
_routerFile = routerFile;
|
||||
_key = key;
|
||||
}
|
||||
|
||||
public String getName() { return "Read RouterInfo"; }
|
||||
|
||||
private boolean shouldRead() {
|
||||
@ -405,19 +410,21 @@ class PersistentDataStore extends TransientDataStore {
|
||||
DatabaseEntry data = get(_key, false);
|
||||
if (data == null) return true;
|
||||
if (data.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) {
|
||||
long knownDate = ((RouterInfo)data).getPublished();
|
||||
_knownDate = ((RouterInfo)data).getPublished();
|
||||
long fileDate = _routerFile.lastModified();
|
||||
if (fileDate > knownDate)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
// don't overwrite recent netdb RIs with reseed data
|
||||
return fileDate > _knownDate + (60*60*1000);
|
||||
} else {
|
||||
// wtf
|
||||
return true;
|
||||
// wtf - prevent injection from reseeding
|
||||
_log.error("Prevented LS overwrite by RI " + _key + " from " + _routerFile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void runJob() {
|
||||
if (!shouldRead()) return;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Reading " + _routerFile);
|
||||
try {
|
||||
InputStream fis = null;
|
||||
boolean corrupt = false;
|
||||
@ -432,6 +439,15 @@ class PersistentDataStore extends TransientDataStore {
|
||||
_log.error("The router "
|
||||
+ ri.getIdentity().calculateHash().toBase64()
|
||||
+ " is from a different network");
|
||||
} else if (!ri.getIdentity().calculateHash().equals(_key)) {
|
||||
// prevent injection from reseeding
|
||||
// this is checked in KNDF.validate() but catch it sooner and log as error.
|
||||
corrupt = true;
|
||||
_log.error(ri.getIdentity().calculateHash() + " does not match " + _key + " from " + _routerFile);
|
||||
} else if (ri.getPublished() <= _knownDate) {
|
||||
// Don't store but don't delete
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Skipping since netdb newer than " + _routerFile);
|
||||
} else {
|
||||
try {
|
||||
// persist = false so we don't write what we just read
|
||||
@ -441,7 +457,7 @@ class PersistentDataStore extends TransientDataStore {
|
||||
// so add it here.
|
||||
getContext().profileManager().heardAbout(ri.getIdentity().getHash(), ri.getPublished());
|
||||
} catch (IllegalArgumentException iae) {
|
||||
_log.info("Refused locally loaded routerInfo - deleting");
|
||||
_log.info("Refused locally loaded routerInfo - deleting", iae);
|
||||
corrupt = true;
|
||||
}
|
||||
}
|
||||
|
@ -334,6 +334,8 @@ public class Reseeder {
|
||||
// This isn't really URLs, but Base64 hashes
|
||||
// but they may include % encoding
|
||||
Set<String> urls = new HashSet(1024);
|
||||
Hash ourHash = _context.routerHash();
|
||||
String ourB64 = ourHash != null ? ourHash.toBase64() : null;
|
||||
int cur = 0;
|
||||
int total = 0;
|
||||
while (total++ < 1000) {
|
||||
@ -352,7 +354,13 @@ public class Reseeder {
|
||||
continue;
|
||||
}
|
||||
String name = content.substring(start + ("href=\"" + ROUTERINFO_PREFIX).length(), end);
|
||||
urls.add(name);
|
||||
// never load our own RI
|
||||
if (ourB64 == null || !name.contains(ourB64)) {
|
||||
urls.add(name);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Skipping our own RI");
|
||||
}
|
||||
cur = end + 1;
|
||||
}
|
||||
if (total <= 0) {
|
||||
@ -372,7 +380,8 @@ public class Reseeder {
|
||||
System.setProperty(PROP_STATUS,
|
||||
_("Reseeding: fetching router info from seed URL ({0} successful, {1} errors).", fetched, errors));
|
||||
|
||||
fetchSeed(seedURL, iter.next());
|
||||
if (!fetchSeed(seedURL, iter.next()))
|
||||
continue;
|
||||
fetched++;
|
||||
if (echoStatus) {
|
||||
System.out.print(".");
|
||||
@ -408,8 +417,9 @@ public class Reseeder {
|
||||
* 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.
|
||||
* @return true on success, false if skipped
|
||||
*/
|
||||
private void fetchSeed(String seedURL, String peer) throws IOException, URISyntaxException {
|
||||
private boolean fetchSeed(String seedURL, String peer) throws IOException, URISyntaxException {
|
||||
// Use URI to do % decoding of the B64 hash (some servers escape ~ and =)
|
||||
// Also do basic hash validation. This prevents stuff like
|
||||
// .. or / in the file name
|
||||
@ -420,13 +430,16 @@ public class Reseeder {
|
||||
byte[] hash = Base64.decode(b64);
|
||||
if (hash == null || hash.length != Hash.HASH_LENGTH)
|
||||
throw new IOException("bad hash " + peer);
|
||||
Hash ourHash = _context.routerHash();
|
||||
if (ourHash != null && DataHelper.eq(hash, ourHash.getData()))
|
||||
return false;
|
||||
|
||||
URL url = new URL(seedURL + (seedURL.endsWith("/") ? "" : "/") + ROUTERINFO_PREFIX + peer + ROUTERINFO_SUFFIX);
|
||||
|
||||
byte data[] = readURL(url);
|
||||
if (data == null || data.length <= 0)
|
||||
throw new IOException("Failed fetch of " + url);
|
||||
writeSeed(b64, data);
|
||||
return writeSeed(b64, data);
|
||||
}
|
||||
|
||||
/** @return null on error */
|
||||
@ -468,16 +481,24 @@ public class Reseeder {
|
||||
|
||||
/**
|
||||
* @param name valid Base64 hash
|
||||
* @return true on success, false if skipped
|
||||
*/
|
||||
private void writeSeed(String name, byte data[]) throws IOException {
|
||||
private boolean writeSeed(String name, byte data[]) throws IOException {
|
||||
String dirName = "netDb"; // _context.getProperty("router.networkDatabase.dbDir", "netDb");
|
||||
File netDbDir = new SecureDirectory(_context.getRouterDir(), dirName);
|
||||
if (!netDbDir.exists()) {
|
||||
boolean ok = netDbDir.mkdirs();
|
||||
netDbDir.mkdirs();
|
||||
}
|
||||
File file = new File(netDbDir, ROUTERINFO_PREFIX + name + ROUTERINFO_SUFFIX);
|
||||
// don't overwrite recent file
|
||||
// TODO: even better would be to compare to last-mod date from eepget
|
||||
if (file.exists() && file.lastModified() > _context.clock().now() - 60*60*1000) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Skipping RI, ours is recent: " + file);
|
||||
return false;
|
||||
}
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
File file = new File(netDbDir, ROUTERINFO_PREFIX + name + ROUTERINFO_SUFFIX);
|
||||
fos = new SecureFileOutputStream(file);
|
||||
fos.write(data);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
@ -487,6 +508,7 @@ public class Reseeder {
|
||||
if (fos != null) fos.close();
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user