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:
13
history.txt
13
history.txt
@@ -1,7 +1,20 @@
|
|||||||
|
2012-03-20 zzz
|
||||||
|
* i2psnark: Message area tweaks and clear link
|
||||||
|
* 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
|
||||||
|
* Router: Make runRouter() public
|
||||||
|
|
||||||
2012-03-19 sponge
|
2012-03-19 sponge
|
||||||
* Plugins: Less confusing message, fix CNFE by catch and ignore on delete.
|
* Plugins: Less confusing message, fix CNFE by catch and ignore on delete.
|
||||||
Order and reverse order plugin names for start/stop all cases.
|
Order and reverse order plugin names for start/stop all cases.
|
||||||
|
|
||||||
|
2012-03-18 zzz
|
||||||
|
* Jetty: Fix check alias override
|
||||||
|
|
||||||
2012-03-17 zzz
|
2012-03-17 zzz
|
||||||
* BuildHandler: Implement restart and shutdown to stop the thread
|
* BuildHandler: Implement restart and shutdown to stop the thread
|
||||||
* Jetty: Don't extract wars
|
* Jetty: Don't extract wars
|
||||||
|
@@ -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 = 18;
|
public final static long BUILD = 19;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
@@ -684,7 +684,9 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
|||||||
// if it hasn't changed, no need to do anything
|
// if it hasn't changed, no need to do anything
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
} catch (ClassCastException cce) {}
|
} catch (ClassCastException cce) {
|
||||||
|
throw new IllegalArgumentException("Attempt to replace RI with " + leaseSet);
|
||||||
|
}
|
||||||
|
|
||||||
String err = validate(key, leaseSet);
|
String err = validate(key, leaseSet);
|
||||||
if (err != null)
|
if (err != null)
|
||||||
@@ -811,7 +813,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
|||||||
return store(key, routerInfo, true);
|
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;
|
if (!_initialized) return null;
|
||||||
|
|
||||||
RouterInfo rv = null;
|
RouterInfo rv = null;
|
||||||
@@ -821,7 +823,9 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
|||||||
// no need to validate
|
// no need to validate
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
} catch (ClassCastException cce) {}
|
} catch (ClassCastException cce) {
|
||||||
|
throw new IllegalArgumentException("Attempt to replace LS with " + routerInfo);
|
||||||
|
}
|
||||||
|
|
||||||
String err = validate(key, routerInfo);
|
String err = validate(key, routerInfo);
|
||||||
if (err != null)
|
if (err != null)
|
||||||
|
@@ -392,12 +392,17 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
private class ReadRouterJob extends JobImpl {
|
private class ReadRouterJob extends JobImpl {
|
||||||
private final File _routerFile;
|
private final File _routerFile;
|
||||||
private final Hash _key;
|
private final Hash _key;
|
||||||
|
private long _knownDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key must match the RI hash in the file
|
||||||
|
*/
|
||||||
public ReadRouterJob(File routerFile, Hash key) {
|
public ReadRouterJob(File routerFile, Hash key) {
|
||||||
super(PersistentDataStore.this._context);
|
super(PersistentDataStore.this._context);
|
||||||
_routerFile = routerFile;
|
_routerFile = routerFile;
|
||||||
_key = key;
|
_key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() { return "Read RouterInfo"; }
|
public String getName() { return "Read RouterInfo"; }
|
||||||
|
|
||||||
private boolean shouldRead() {
|
private boolean shouldRead() {
|
||||||
@@ -405,19 +410,21 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
DatabaseEntry data = get(_key, false);
|
DatabaseEntry data = get(_key, false);
|
||||||
if (data == null) return true;
|
if (data == null) return true;
|
||||||
if (data.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) {
|
if (data.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) {
|
||||||
long knownDate = ((RouterInfo)data).getPublished();
|
_knownDate = ((RouterInfo)data).getPublished();
|
||||||
long fileDate = _routerFile.lastModified();
|
long fileDate = _routerFile.lastModified();
|
||||||
if (fileDate > knownDate)
|
// don't overwrite recent netdb RIs with reseed data
|
||||||
return true;
|
return fileDate > _knownDate + (60*60*1000);
|
||||||
else
|
|
||||||
return false;
|
|
||||||
} else {
|
} else {
|
||||||
// wtf
|
// wtf - prevent injection from reseeding
|
||||||
return true;
|
_log.error("Prevented LS overwrite by RI " + _key + " from " + _routerFile);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void runJob() {
|
public void runJob() {
|
||||||
if (!shouldRead()) return;
|
if (!shouldRead()) return;
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Reading " + _routerFile);
|
||||||
try {
|
try {
|
||||||
InputStream fis = null;
|
InputStream fis = null;
|
||||||
boolean corrupt = false;
|
boolean corrupt = false;
|
||||||
@@ -432,6 +439,15 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
_log.error("The router "
|
_log.error("The router "
|
||||||
+ ri.getIdentity().calculateHash().toBase64()
|
+ ri.getIdentity().calculateHash().toBase64()
|
||||||
+ " is from a different network");
|
+ " 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 {
|
} else {
|
||||||
try {
|
try {
|
||||||
// persist = false so we don't write what we just read
|
// persist = false so we don't write what we just read
|
||||||
@@ -441,7 +457,7 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
// so add it here.
|
// so add it here.
|
||||||
getContext().profileManager().heardAbout(ri.getIdentity().getHash(), ri.getPublished());
|
getContext().profileManager().heardAbout(ri.getIdentity().getHash(), ri.getPublished());
|
||||||
} catch (IllegalArgumentException iae) {
|
} catch (IllegalArgumentException iae) {
|
||||||
_log.info("Refused locally loaded routerInfo - deleting");
|
_log.info("Refused locally loaded routerInfo - deleting", iae);
|
||||||
corrupt = true;
|
corrupt = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -334,6 +334,8 @@ public class Reseeder {
|
|||||||
// This isn't really URLs, but Base64 hashes
|
// This isn't really URLs, but Base64 hashes
|
||||||
// but they may include % encoding
|
// but they may include % encoding
|
||||||
Set<String> urls = new HashSet(1024);
|
Set<String> urls = new HashSet(1024);
|
||||||
|
Hash ourHash = _context.routerHash();
|
||||||
|
String ourB64 = ourHash != null ? ourHash.toBase64() : null;
|
||||||
int cur = 0;
|
int cur = 0;
|
||||||
int total = 0;
|
int total = 0;
|
||||||
while (total++ < 1000) {
|
while (total++ < 1000) {
|
||||||
@@ -352,7 +354,13 @@ public class Reseeder {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String name = content.substring(start + ("href=\"" + ROUTERINFO_PREFIX).length(), end);
|
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;
|
cur = end + 1;
|
||||||
}
|
}
|
||||||
if (total <= 0) {
|
if (total <= 0) {
|
||||||
@@ -372,7 +380,8 @@ public class Reseeder {
|
|||||||
System.setProperty(PROP_STATUS,
|
System.setProperty(PROP_STATUS,
|
||||||
_("Reseeding: fetching router info from seed URL ({0} successful, {1} errors).", fetched, errors));
|
_("Reseeding: fetching router info from seed URL ({0} successful, {1} errors).", fetched, errors));
|
||||||
|
|
||||||
fetchSeed(seedURL, iter.next());
|
if (!fetchSeed(seedURL, iter.next()))
|
||||||
|
continue;
|
||||||
fetched++;
|
fetched++;
|
||||||
if (echoStatus) {
|
if (echoStatus) {
|
||||||
System.out.print(".");
|
System.out.print(".");
|
||||||
@@ -408,8 +417,9 @@ public class Reseeder {
|
|||||||
* 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 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 =)
|
// Use URI to do % decoding of the B64 hash (some servers escape ~ and =)
|
||||||
// Also do basic hash validation. This prevents stuff like
|
// Also do basic hash validation. This prevents stuff like
|
||||||
// .. or / in the file name
|
// .. or / in the file name
|
||||||
@@ -420,13 +430,16 @@ public class Reseeder {
|
|||||||
byte[] hash = Base64.decode(b64);
|
byte[] hash = Base64.decode(b64);
|
||||||
if (hash == null || hash.length != Hash.HASH_LENGTH)
|
if (hash == null || hash.length != Hash.HASH_LENGTH)
|
||||||
throw new IOException("bad hash " + peer);
|
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);
|
URL url = new URL(seedURL + (seedURL.endsWith("/") ? "" : "/") + ROUTERINFO_PREFIX + peer + ROUTERINFO_SUFFIX);
|
||||||
|
|
||||||
byte data[] = readURL(url);
|
byte data[] = readURL(url);
|
||||||
if (data == null || data.length <= 0)
|
if (data == null || data.length <= 0)
|
||||||
throw new IOException("Failed fetch of " + url);
|
throw new IOException("Failed fetch of " + url);
|
||||||
writeSeed(b64, data);
|
return writeSeed(b64, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return null on error */
|
/** @return null on error */
|
||||||
@@ -468,16 +481,24 @@ public class Reseeder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param name valid Base64 hash
|
* @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");
|
String dirName = "netDb"; // _context.getProperty("router.networkDatabase.dbDir", "netDb");
|
||||||
File netDbDir = new SecureDirectory(_context.getRouterDir(), dirName);
|
File netDbDir = new SecureDirectory(_context.getRouterDir(), dirName);
|
||||||
if (!netDbDir.exists()) {
|
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;
|
FileOutputStream fos = null;
|
||||||
try {
|
try {
|
||||||
File file = new File(netDbDir, ROUTERINFO_PREFIX + name + ROUTERINFO_SUFFIX);
|
|
||||||
fos = new SecureFileOutputStream(file);
|
fos = new SecureFileOutputStream(file);
|
||||||
fos.write(data);
|
fos.write(data);
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
@@ -487,6 +508,7 @@ public class Reseeder {
|
|||||||
if (fos != null) fos.close();
|
if (fos != null) fos.close();
|
||||||
} catch (IOException ioe) {}
|
} catch (IOException ioe) {}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user