merge of '4e478ff8494e308808d6df2c47e368ac3aeb61aa'
and '51ae655150adb03f61e4ead408e1e7d827661dc7'
This commit is contained in:
@ -755,7 +755,7 @@
|
|||||||
</target>
|
</target>
|
||||||
<target name="findbugs" depends="build2">
|
<target name="findbugs" depends="build2">
|
||||||
<echo message="Starting findbugs, this will take a while..." />
|
<echo message="Starting findbugs, this will take a while..." />
|
||||||
<exec executable="nice">
|
<exec executable="nice" failonerror="true">
|
||||||
<arg value="findbugs"/>
|
<arg value="findbugs"/>
|
||||||
<arg value="-textui"/>
|
<arg value="-textui"/>
|
||||||
<arg value="-projectName"/>
|
<arg value="-projectName"/>
|
||||||
|
@ -23,13 +23,18 @@ public final class SHA256Generator {
|
|||||||
return I2PAppContext.getGlobalContext().sha();
|
return I2PAppContext.getGlobalContext().sha();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Calculate the SHA-256 has of the source
|
/**
|
||||||
|
* Calculate the SHA-256 hash of the source and cache the result.
|
||||||
* @param source what to hash
|
* @param source what to hash
|
||||||
* @return hash of the source
|
* @return hash of the source
|
||||||
*/
|
*/
|
||||||
public final Hash calculateHash(byte[] source) {
|
public final Hash calculateHash(byte[] source) {
|
||||||
return calculateHash(source, 0, source.length);
|
return calculateHash(source, 0, source.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the hash and cache the result.
|
||||||
|
*/
|
||||||
public final Hash calculateHash(byte[] source, int start, int len) {
|
public final Hash calculateHash(byte[] source, int start, int len) {
|
||||||
Sha256Standalone digest = acquireGnu();
|
Sha256Standalone digest = acquireGnu();
|
||||||
digest.update(source, start, len);
|
digest.update(source, start, len);
|
||||||
@ -39,6 +44,10 @@ public final class SHA256Generator {
|
|||||||
return Hash.create(rv);
|
return Hash.create(rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this if you only need the data, not a Hash object.
|
||||||
|
* Does not cache.
|
||||||
|
*/
|
||||||
public final void calculateHash(byte[] source, int start, int len, byte out[], int outOffset) {
|
public final void calculateHash(byte[] source, int start, int len, byte out[], int outOffset) {
|
||||||
Sha256Standalone digest = acquireGnu();
|
Sha256Standalone digest = acquireGnu();
|
||||||
digest.update(source, start, len);
|
digest.update(source, start, len);
|
||||||
|
@ -31,6 +31,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -1040,25 +1041,45 @@ public class DataHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sort based on the Hash of the DataStructure
|
* Sort based on the Hash of the DataStructure.
|
||||||
* Warning - relatively slow.
|
* Warning - relatively slow.
|
||||||
* Only used by RouterInfo
|
* WARNING - this sort order must be consistent network-wide, so while the order is arbitrary,
|
||||||
* Why? Just because it has to be consistent so signing will work?
|
* it cannot be changed.
|
||||||
|
* Why? Just because it has to be consistent so signing will work.
|
||||||
* How to spec as returning the same type as the param?
|
* How to spec as returning the same type as the param?
|
||||||
|
* DEPRECATED - Only used by RouterInfo.
|
||||||
*/
|
*/
|
||||||
public static List<? extends DataStructure> sortStructures(Collection<? extends DataStructure> dataStructures) {
|
public static List<? extends DataStructure> sortStructures(Collection<? extends DataStructure> dataStructures) {
|
||||||
if (dataStructures == null) return Collections.EMPTY_LIST;
|
if (dataStructures == null) return Collections.EMPTY_LIST;
|
||||||
ArrayList<DataStructure> rv = new ArrayList(dataStructures.size());
|
|
||||||
TreeMap<String, DataStructure> tm = new TreeMap();
|
// This used to use Hash.toString(), which is insane, since a change to toString()
|
||||||
for (DataStructure struct : dataStructures) {
|
// would break the whole network. Now use Hash.toBase64().
|
||||||
tm.put(struct.calculateHash().toString(), struct);
|
// Note that the Base64 sort order is NOT the same as the raw byte sort order,
|
||||||
}
|
// despite what you may read elsewhere.
|
||||||
for (DataStructure struct : tm.values()) {
|
|
||||||
rv.add(struct);
|
//ArrayList<DataStructure> rv = new ArrayList(dataStructures.size());
|
||||||
}
|
//TreeMap<String, DataStructure> tm = new TreeMap();
|
||||||
|
//for (DataStructure struct : dataStructures) {
|
||||||
|
// tm.put(struct.calculateHash().toString(), struct);
|
||||||
|
//}
|
||||||
|
//for (DataStructure struct : tm.values()) {
|
||||||
|
// rv.add(struct);
|
||||||
|
//}
|
||||||
|
ArrayList<DataStructure> rv = new ArrayList(dataStructures);
|
||||||
|
Collections.sort(rv, new DataStructureComparator());
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See sortStructures() comments.
|
||||||
|
* @since 0.8.3
|
||||||
|
*/
|
||||||
|
private static class DataStructureComparator implements Comparator<DataStructure> {
|
||||||
|
public int compare(DataStructure l, DataStructure r) {
|
||||||
|
return l.calculateHash().toBase64().compareTo(r.calculateHash().toBase64());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOTE: formatDuration2() recommended in most cases for readability
|
* NOTE: formatDuration2() recommended in most cases for readability
|
||||||
*/
|
*/
|
||||||
|
@ -131,10 +131,13 @@ public class RouterAddress extends DataStructureImpl {
|
|||||||
&& DataHelper.eq(_transportStyle, addr.getTransportStyle());
|
&& DataHelper.eq(_transportStyle, addr.getTransportStyle());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** the style should be sufficient, for speed */
|
/**
|
||||||
|
* Just use style and hashCode for speed (expiration is always null).
|
||||||
|
* If we add multiple addresses of the same style, this may need to be changed.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return DataHelper.hashCode(_transportStyle);
|
return DataHelper.hashCode(_transportStyle) ^ _cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,6 +14,7 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -39,7 +40,8 @@ public class RouterInfo extends DatabaseEntry {
|
|||||||
private RouterIdentity _identity;
|
private RouterIdentity _identity;
|
||||||
private volatile long _published;
|
private volatile long _published;
|
||||||
private final Set<RouterAddress> _addresses;
|
private final Set<RouterAddress> _addresses;
|
||||||
private final Set<Hash> _peers;
|
/** may be null to save memory, no longer final */
|
||||||
|
private Set<Hash> _peers;
|
||||||
private /* FIXME final FIXME */ Properties _options;
|
private /* FIXME final FIXME */ Properties _options;
|
||||||
private volatile boolean _validated;
|
private volatile boolean _validated;
|
||||||
private volatile boolean _isValid;
|
private volatile boolean _isValid;
|
||||||
@ -47,6 +49,10 @@ public class RouterInfo extends DatabaseEntry {
|
|||||||
private volatile byte _byteified[];
|
private volatile byte _byteified[];
|
||||||
private volatile int _hashCode;
|
private volatile int _hashCode;
|
||||||
private volatile boolean _hashCodeInitialized;
|
private volatile boolean _hashCodeInitialized;
|
||||||
|
/** should we cache the byte and string versions _byteified ? **/
|
||||||
|
private boolean _shouldCache;
|
||||||
|
/** maybe we should check if we are floodfill? */
|
||||||
|
private static final boolean CACHE_ALL = Runtime.getRuntime().maxMemory() > 128*1024*1024l;
|
||||||
|
|
||||||
public static final String PROP_NETWORK_ID = "netId";
|
public static final String PROP_NETWORK_ID = "netId";
|
||||||
public static final String PROP_CAPABILITIES = "caps";
|
public static final String PROP_CAPABILITIES = "caps";
|
||||||
@ -58,7 +64,6 @@ public class RouterInfo extends DatabaseEntry {
|
|||||||
|
|
||||||
public RouterInfo() {
|
public RouterInfo() {
|
||||||
_addresses = new HashSet(2);
|
_addresses = new HashSet(2);
|
||||||
_peers = new HashSet(0);
|
|
||||||
_options = new OrderedProperties();
|
_options = new OrderedProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,6 +75,7 @@ public class RouterInfo extends DatabaseEntry {
|
|||||||
setPeers(old.getPeers());
|
setPeers(old.getPeers());
|
||||||
setOptions(old.getOptions());
|
setOptions(old.getOptions());
|
||||||
setSignature(old.getSignature());
|
setSignature(old.getSignature());
|
||||||
|
// copy over _byteified?
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getDate() {
|
public long getDate() {
|
||||||
@ -105,6 +111,11 @@ public class RouterInfo extends DatabaseEntry {
|
|||||||
public void setIdentity(RouterIdentity ident) {
|
public void setIdentity(RouterIdentity ident) {
|
||||||
_identity = ident;
|
_identity = ident;
|
||||||
resetCache();
|
resetCache();
|
||||||
|
// We only want to cache the bytes for our own RI, which is frequently written.
|
||||||
|
// To cache for all RIs doubles the RI memory usage.
|
||||||
|
// setIdentity() is only called when we are creating our own RI.
|
||||||
|
// Otherwise, the data is populated with readBytes().
|
||||||
|
_shouldCache = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -159,6 +170,8 @@ public class RouterInfo extends DatabaseEntry {
|
|||||||
* @deprecated Implemented here but unused elsewhere
|
* @deprecated Implemented here but unused elsewhere
|
||||||
*/
|
*/
|
||||||
public Set<Hash> getPeers() {
|
public Set<Hash> getPeers() {
|
||||||
|
if (_peers == null)
|
||||||
|
return Collections.EMPTY_SET;
|
||||||
return _peers;
|
return _peers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,9 +182,15 @@ public class RouterInfo extends DatabaseEntry {
|
|||||||
* @deprecated Implemented here but unused elsewhere
|
* @deprecated Implemented here but unused elsewhere
|
||||||
*/
|
*/
|
||||||
public void setPeers(Set<Hash> peers) {
|
public void setPeers(Set<Hash> peers) {
|
||||||
|
if (peers == null || peers.isEmpty()) {
|
||||||
|
_peers = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_peers == null)
|
||||||
|
_peers = new HashSet(2);
|
||||||
synchronized (_peers) {
|
synchronized (_peers) {
|
||||||
_peers.clear();
|
_peers.clear();
|
||||||
if (peers != null) _peers.addAll(peers);
|
_peers.addAll(peers);
|
||||||
}
|
}
|
||||||
resetCache();
|
resetCache();
|
||||||
}
|
}
|
||||||
@ -223,7 +242,6 @@ public class RouterInfo extends DatabaseEntry {
|
|||||||
if (_byteified != null) return _byteified;
|
if (_byteified != null) return _byteified;
|
||||||
if (_identity == null) throw new DataFormatException("Router identity isn't set? wtf!");
|
if (_identity == null) throw new DataFormatException("Router identity isn't set? wtf!");
|
||||||
if (_addresses == null) throw new DataFormatException("Router addressess isn't set? wtf!");
|
if (_addresses == null) throw new DataFormatException("Router addressess isn't set? wtf!");
|
||||||
if (_peers == null) throw new DataFormatException("Router peers isn't set? wtf!");
|
|
||||||
if (_options == null) throw new DataFormatException("Router options isn't set? wtf!");
|
if (_options == null) throw new DataFormatException("Router options isn't set? wtf!");
|
||||||
|
|
||||||
long before = Clock.getInstance().now();
|
long before = Clock.getInstance().now();
|
||||||
@ -239,6 +257,9 @@ public class RouterInfo extends DatabaseEntry {
|
|||||||
DataHelper.writeLong(out, 1, sz);
|
DataHelper.writeLong(out, 1, sz);
|
||||||
Collection<RouterAddress> addresses = _addresses;
|
Collection<RouterAddress> addresses = _addresses;
|
||||||
if (sz > 1)
|
if (sz > 1)
|
||||||
|
// WARNING this sort algorithm cannot be changed, as it must be consistent
|
||||||
|
// network-wide. The signature is not checked at readin time, but only
|
||||||
|
// later, and the addresses are stored in a Set, not a List.
|
||||||
addresses = (Collection<RouterAddress>) DataHelper.sortStructures(addresses);
|
addresses = (Collection<RouterAddress>) DataHelper.sortStructures(addresses);
|
||||||
for (RouterAddress addr : addresses) {
|
for (RouterAddress addr : addresses) {
|
||||||
addr.writeBytes(out);
|
addr.writeBytes(out);
|
||||||
@ -248,12 +269,14 @@ public class RouterInfo extends DatabaseEntry {
|
|||||||
// answer: they're always empty... they're a placeholder for one particular
|
// answer: they're always empty... they're a placeholder for one particular
|
||||||
// method of trusted links, which isn't implemented in the router
|
// method of trusted links, which isn't implemented in the router
|
||||||
// at the moment, and may not be later.
|
// at the moment, and may not be later.
|
||||||
// fixme to reduce objects - allow _peers == null
|
int psz = _peers == null ? 0 : _peers.size();
|
||||||
int psz = _peers.size();
|
|
||||||
DataHelper.writeLong(out, 1, psz);
|
DataHelper.writeLong(out, 1, psz);
|
||||||
if (psz > 0) {
|
if (psz > 0) {
|
||||||
Collection<Hash> peers = _peers;
|
Collection<Hash> peers = _peers;
|
||||||
if (psz > 1)
|
if (psz > 1)
|
||||||
|
// WARNING this sort algorithm cannot be changed, as it must be consistent
|
||||||
|
// network-wide. The signature is not checked at readin time, but only
|
||||||
|
// later, and the hashes are stored in a Set, not a List.
|
||||||
peers = (Collection<Hash>) DataHelper.sortStructures(peers);
|
peers = (Collection<Hash>) DataHelper.sortStructures(peers);
|
||||||
for (Hash peerHash : peers) {
|
for (Hash peerHash : peers) {
|
||||||
peerHash.writeBytes(out);
|
peerHash.writeBytes(out);
|
||||||
@ -266,6 +289,7 @@ public class RouterInfo extends DatabaseEntry {
|
|||||||
byte data[] = out.toByteArray();
|
byte data[] = out.toByteArray();
|
||||||
long after = Clock.getInstance().now();
|
long after = Clock.getInstance().now();
|
||||||
_log.debug("getBytes() took " + (after - before) + "ms");
|
_log.debug("getBytes() took " + (after - before) + "ms");
|
||||||
|
if (CACHE_ALL || _shouldCache)
|
||||||
_byteified = data;
|
_byteified = data;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@ -466,11 +490,16 @@ public class RouterInfo extends DatabaseEntry {
|
|||||||
_addresses.add(address);
|
_addresses.add(address);
|
||||||
}
|
}
|
||||||
int numPeers = (int) DataHelper.readLong(in, 1);
|
int numPeers = (int) DataHelper.readLong(in, 1);
|
||||||
|
if (numPeers == 0) {
|
||||||
|
_peers = null;
|
||||||
|
} else {
|
||||||
|
_peers = new HashSet(numPeers);
|
||||||
for (int i = 0; i < numPeers; i++) {
|
for (int i = 0; i < numPeers; i++) {
|
||||||
Hash peerIdentityHash = new Hash();
|
Hash peerIdentityHash = new Hash();
|
||||||
peerIdentityHash.readBytes(in);
|
peerIdentityHash.readBytes(in);
|
||||||
_peers.add(peerIdentityHash);
|
_peers.add(peerIdentityHash);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_options = DataHelper.readProperties(in);
|
_options = DataHelper.readProperties(in);
|
||||||
_signature = new Signature();
|
_signature = new Signature();
|
||||||
_signature.readBytes(in);
|
_signature.readBytes(in);
|
||||||
@ -504,7 +533,7 @@ public class RouterInfo extends DatabaseEntry {
|
|||||||
&& _published == info.getPublished()
|
&& _published == info.getPublished()
|
||||||
&& DataHelper.eq(_addresses, info.getAddresses())
|
&& DataHelper.eq(_addresses, info.getAddresses())
|
||||||
&& DataHelper.eq(_options, info.getOptions())
|
&& DataHelper.eq(_options, info.getOptions())
|
||||||
&& DataHelper.eq(_peers, info.getPeers());
|
&& DataHelper.eq(getPeers(), info.getPeers());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -530,7 +559,7 @@ public class RouterInfo extends DatabaseEntry {
|
|||||||
RouterAddress addr = (RouterAddress) iter.next();
|
RouterAddress addr = (RouterAddress) iter.next();
|
||||||
buf.append("\n\t\tAddress: ").append(addr);
|
buf.append("\n\t\tAddress: ").append(addr);
|
||||||
}
|
}
|
||||||
Set peers = _peers; // getPeers()
|
Set peers = getPeers();
|
||||||
buf.append("\n\tPeers: #: ").append(peers.size());
|
buf.append("\n\tPeers: #: ").append(peers.size());
|
||||||
for (Iterator iter = peers.iterator(); iter.hasNext();) {
|
for (Iterator iter = peers.iterator(); iter.hasNext();) {
|
||||||
Hash hash = (Hash) iter.next();
|
Hash hash = (Hash) iter.next();
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
2011-01-09 zzz
|
||||||
|
* DataHelper: Speed up and annotate sortStructures()
|
||||||
|
* Data Structures: More caching improvements, don't cache where we shouldn't
|
||||||
|
* NetDB: Don't rescan netDb directory unless changed,
|
||||||
|
to reduce Hash cache thrash (backport from test4)
|
||||||
|
* RouterInfo:
|
||||||
|
- Don't cache byteified data by default, to save ~1.5 MB
|
||||||
|
- Don't create empty peers Set, to save ~100KB
|
||||||
|
|
||||||
2011-01-07 zzz
|
2011-01-07 zzz
|
||||||
* Data Structures: More caching
|
* Data Structures: More caching
|
||||||
* i2psnark: Improve request tracking to reduce memory usage
|
* i2psnark: Improve request tracking to reduce memory usage
|
||||||
|
@ -20,6 +20,7 @@ import net.i2p.data.DataHelper;
|
|||||||
import net.i2p.data.DataStructureImpl;
|
import net.i2p.data.DataStructureImpl;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.util.SimpleByteCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the base message implementation.
|
* Defines the base message implementation.
|
||||||
@ -72,6 +73,7 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
|||||||
* Read the header, then read the rest into buffer, then call
|
* Read the header, then read the rest into buffer, then call
|
||||||
* readMessage in the implemented message type
|
* readMessage in the implemented message type
|
||||||
*
|
*
|
||||||
|
*<pre>
|
||||||
* Specifically:
|
* Specifically:
|
||||||
* 1 byte type (if caller didn't read already, as specified by the type param
|
* 1 byte type (if caller didn't read already, as specified by the type param
|
||||||
* 4 byte ID
|
* 4 byte ID
|
||||||
@ -79,9 +81,11 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
|||||||
* 2 byte size
|
* 2 byte size
|
||||||
* 1 byte checksum
|
* 1 byte checksum
|
||||||
* size bytes of payload (read by readMessage() in implementation)
|
* size bytes of payload (read by readMessage() in implementation)
|
||||||
|
*</pre>
|
||||||
*
|
*
|
||||||
* @param type the message type or -1 if we should read it here
|
* @param type the message type or -1 if we should read it here
|
||||||
* @param buffer temp buffer to use
|
* @param buffer temp buffer to use
|
||||||
|
* @return total length of the message
|
||||||
*/
|
*/
|
||||||
public int readBytes(InputStream in, int type, byte buffer[]) throws I2NPMessageException, IOException {
|
public int readBytes(InputStream in, int type, byte buffer[]) throws I2NPMessageException, IOException {
|
||||||
try {
|
try {
|
||||||
@ -110,9 +114,11 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
|||||||
cur += numRead;
|
cur += numRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
Hash calc = _context.sha().calculateHash(buffer, 0, size);
|
byte[] calc = SimpleByteCache.acquire(Hash.HASH_LENGTH);
|
||||||
|
_context.sha().calculateHash(buffer, 0, size, calc, 0);
|
||||||
//boolean eq = calc.equals(h);
|
//boolean eq = calc.equals(h);
|
||||||
boolean eq = DataHelper.eq(checksum, 0, calc.getData(), 0, CHECKSUM_LENGTH);
|
boolean eq = DataHelper.eq(checksum, 0, calc, 0, CHECKSUM_LENGTH);
|
||||||
|
SimpleByteCache.release(calc);
|
||||||
if (!eq)
|
if (!eq)
|
||||||
throw new I2NPMessageException("Hash does not match for " + getClass().getName());
|
throw new I2NPMessageException("Hash does not match for " + getClass().getName());
|
||||||
|
|
||||||
@ -123,11 +129,29 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
|||||||
//long time = _context.clock().now() - start;
|
//long time = _context.clock().now() - start;
|
||||||
//if (time > 50)
|
//if (time > 50)
|
||||||
// _context.statManager().addRateData("i2np.readTime", time, time);
|
// _context.statManager().addRateData("i2np.readTime", time, time);
|
||||||
return size + Hash.HASH_LENGTH + 1 + 4 + DataHelper.DATE_LENGTH;
|
return CHECKSUM_LENGTH + 1 + 2 + 4 + DataHelper.DATE_LENGTH + size;
|
||||||
} catch (DataFormatException dfe) {
|
} catch (DataFormatException dfe) {
|
||||||
throw new I2NPMessageException("Error reading the message header", dfe);
|
throw new I2NPMessageException("Error reading the message header", dfe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the header, then read the rest into buffer, then call
|
||||||
|
* readMessage in the implemented message type
|
||||||
|
*
|
||||||
|
*<pre>
|
||||||
|
* Specifically:
|
||||||
|
* 1 byte type (if caller didn't read already, as specified by the type param
|
||||||
|
* 4 byte ID
|
||||||
|
* 8 byte expiration
|
||||||
|
* 2 byte size
|
||||||
|
* 1 byte checksum
|
||||||
|
* size bytes of payload (read by readMessage() in implementation)
|
||||||
|
*</pre>
|
||||||
|
*
|
||||||
|
* @param type the message type or -1 if we should read it here
|
||||||
|
* @return total length of the message
|
||||||
|
*/
|
||||||
public int readBytes(byte data[], int type, int offset) throws I2NPMessageException, IOException {
|
public int readBytes(byte data[], int type, int offset) throws I2NPMessageException, IOException {
|
||||||
int cur = offset;
|
int cur = offset;
|
||||||
if (type < 0) {
|
if (type < 0) {
|
||||||
@ -153,9 +177,10 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
|||||||
+ " cur=" + cur
|
+ " cur=" + cur
|
||||||
+ " wanted=" + size + "]: " + getClass().getName());
|
+ " wanted=" + size + "]: " + getClass().getName());
|
||||||
|
|
||||||
Hash calc = _context.sha().calculateHash(data, cur, size);
|
byte[] calc = SimpleByteCache.acquire(Hash.HASH_LENGTH);
|
||||||
//boolean eq = calc.equals(h);
|
_context.sha().calculateHash(data, cur, size, calc, 0);
|
||||||
boolean eq = DataHelper.eq(hdata, 0, calc.getData(), 0, CHECKSUM_LENGTH);
|
boolean eq = DataHelper.eq(hdata, 0, calc, 0, CHECKSUM_LENGTH);
|
||||||
|
SimpleByteCache.release(calc);
|
||||||
if (!eq)
|
if (!eq)
|
||||||
throw new I2NPMessageException("Hash does not match for " + getClass().getName());
|
throw new I2NPMessageException("Hash does not match for " + getClass().getName());
|
||||||
|
|
||||||
@ -231,7 +256,8 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
|||||||
try {
|
try {
|
||||||
int writtenLen = writeMessageBody(buffer, prefixLen);
|
int writtenLen = writeMessageBody(buffer, prefixLen);
|
||||||
int payloadLen = writtenLen - prefixLen;
|
int payloadLen = writtenLen - prefixLen;
|
||||||
Hash h = _context.sha().calculateHash(buffer, prefixLen, payloadLen);
|
byte[] h = SimpleByteCache.acquire(Hash.HASH_LENGTH);
|
||||||
|
_context.sha().calculateHash(buffer, prefixLen, payloadLen, h, 0);
|
||||||
|
|
||||||
int off = 0;
|
int off = 0;
|
||||||
DataHelper.toLong(buffer, off, 1, getType());
|
DataHelper.toLong(buffer, off, 1, getType());
|
||||||
@ -242,7 +268,8 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
|||||||
off += DataHelper.DATE_LENGTH;
|
off += DataHelper.DATE_LENGTH;
|
||||||
DataHelper.toLong(buffer, off, 2, payloadLen);
|
DataHelper.toLong(buffer, off, 2, payloadLen);
|
||||||
off += 2;
|
off += 2;
|
||||||
System.arraycopy(h.getData(), 0, buffer, off, CHECKSUM_LENGTH);
|
System.arraycopy(h, 0, buffer, off, CHECKSUM_LENGTH);
|
||||||
|
SimpleByteCache.release(h);
|
||||||
|
|
||||||
//long time = _context.clock().now() - start;
|
//long time = _context.clock().now() - start;
|
||||||
//if (time > 50)
|
//if (time > 50)
|
||||||
|
@ -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 = 9;
|
public final static long BUILD = 10;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
@ -247,7 +247,11 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
|||||||
_enforceNetId = DEFAULT_ENFORCE_NETID;
|
_enforceNetId = DEFAULT_ENFORCE_NETID;
|
||||||
|
|
||||||
_kb = new KBucketSet(_context, ri.getIdentity().getHash());
|
_kb = new KBucketSet(_context, ri.getIdentity().getHash());
|
||||||
|
try {
|
||||||
_ds = new PersistentDataStore(_context, dbDir, this);
|
_ds = new PersistentDataStore(_context, dbDir, this);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new RuntimeException("Unable to initialize netdb storage", ioe);
|
||||||
|
}
|
||||||
//_ds = new TransientDataStore();
|
//_ds = new TransientDataStore();
|
||||||
// _exploreKeys = new HashSet(64);
|
// _exploreKeys = new HashSet(64);
|
||||||
_dbDir = dbDir;
|
_dbDir = dbDir;
|
||||||
|
@ -39,19 +39,22 @@ import net.i2p.util.SecureFileOutputStream;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class PersistentDataStore extends TransientDataStore {
|
class PersistentDataStore extends TransientDataStore {
|
||||||
private Log _log;
|
private final Log _log;
|
||||||
private String _dbDir;
|
private final File _dbDir;
|
||||||
private KademliaNetworkDatabaseFacade _facade;
|
private final KademliaNetworkDatabaseFacade _facade;
|
||||||
private Writer _writer;
|
private final Writer _writer;
|
||||||
private ReadJob _readJob;
|
private final ReadJob _readJob;
|
||||||
private boolean _initialized;
|
private boolean _initialized;
|
||||||
|
|
||||||
private final static int READ_DELAY = 60*1000;
|
private final static int READ_DELAY = 60*1000;
|
||||||
|
|
||||||
public PersistentDataStore(RouterContext ctx, String dbDir, KademliaNetworkDatabaseFacade facade) {
|
/**
|
||||||
|
* @param dbDir relative path
|
||||||
|
*/
|
||||||
|
public PersistentDataStore(RouterContext ctx, String dbDir, KademliaNetworkDatabaseFacade facade) throws IOException {
|
||||||
super(ctx);
|
super(ctx);
|
||||||
_log = ctx.logManager().getLog(PersistentDataStore.class);
|
_log = ctx.logManager().getLog(PersistentDataStore.class);
|
||||||
_dbDir = dbDir;
|
_dbDir = getDbDir(dbDir);
|
||||||
_facade = facade;
|
_facade = facade;
|
||||||
_readJob = new ReadJob();
|
_readJob = new ReadJob();
|
||||||
_context.jobQueue().addJob(_readJob);
|
_context.jobQueue().addJob(_readJob);
|
||||||
@ -79,7 +82,6 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
@Override
|
@Override
|
||||||
public void restart() {
|
public void restart() {
|
||||||
super.restart();
|
super.restart();
|
||||||
_dbDir = _facade.getDbDir();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -160,8 +162,7 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info("Removing key " + _key /* , getAddedBy() */);
|
_log.info("Removing key " + _key /* , getAddedBy() */);
|
||||||
try {
|
try {
|
||||||
File dbDir = getDbDir();
|
removeFile(_key, _dbDir);
|
||||||
removeFile(_key, dbDir);
|
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
_log.error("Error removing key " + _key, ioe);
|
_log.error("Error removing key " + _key, ioe);
|
||||||
}
|
}
|
||||||
@ -236,7 +237,10 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
|
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
|
// synch with the reader job
|
||||||
|
synchronized (_dbDir) {
|
||||||
write(key, data);
|
write(key, data);
|
||||||
|
}
|
||||||
data = null;
|
data = null;
|
||||||
}
|
}
|
||||||
key = null;
|
key = null;
|
||||||
@ -278,7 +282,6 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
File dbFile = null;
|
File dbFile = null;
|
||||||
try {
|
try {
|
||||||
String filename = null;
|
String filename = null;
|
||||||
File dbDir = getDbDir();
|
|
||||||
|
|
||||||
if (data instanceof LeaseSet)
|
if (data instanceof LeaseSet)
|
||||||
filename = getLeaseSetName(key);
|
filename = getLeaseSetName(key);
|
||||||
@ -287,7 +290,7 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
else
|
else
|
||||||
throw new IOException("We don't know how to write objects of type " + data.getClass().getName());
|
throw new IOException("We don't know how to write objects of type " + data.getClass().getName());
|
||||||
|
|
||||||
dbFile = new File(dbDir, filename);
|
dbFile = new File(_dbDir, filename);
|
||||||
long dataPublishDate = getPublishDate(data);
|
long dataPublishDate = getPublishDate(data);
|
||||||
if (dbFile.lastModified() < dataPublishDate) {
|
if (dbFile.lastModified() < dataPublishDate) {
|
||||||
// our filesystem is out of date, lets replace it
|
// our filesystem is out of date, lets replace it
|
||||||
@ -326,14 +329,26 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
/** This is only for manual reseeding? Why bother every 60 sec??? */
|
/** This is only for manual reseeding? Why bother every 60 sec??? */
|
||||||
private class ReadJob extends JobImpl {
|
private class ReadJob extends JobImpl {
|
||||||
private boolean _alreadyWarned;
|
private boolean _alreadyWarned;
|
||||||
|
private long _lastModified;
|
||||||
|
|
||||||
public ReadJob() {
|
public ReadJob() {
|
||||||
super(PersistentDataStore.this._context);
|
super(PersistentDataStore.this._context);
|
||||||
_alreadyWarned = false;
|
_alreadyWarned = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() { return "DB Read Job"; }
|
public String getName() { return "DB Read Job"; }
|
||||||
|
|
||||||
public void runJob() {
|
public void runJob() {
|
||||||
|
// check directory mod time to save a lot of object churn in scanning all the file names
|
||||||
|
long lastMod = _dbDir.lastModified();
|
||||||
|
if (lastMod > _lastModified) {
|
||||||
|
_lastModified = lastMod;
|
||||||
_log.info("Rereading new files");
|
_log.info("Rereading new files");
|
||||||
|
// synch with the writer job
|
||||||
|
synchronized (_dbDir) {
|
||||||
readFiles();
|
readFiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
requeue(READ_DELAY);
|
requeue(READ_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,9 +358,8 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
|
|
||||||
private void readFiles() {
|
private void readFiles() {
|
||||||
int routerCount = 0;
|
int routerCount = 0;
|
||||||
try {
|
|
||||||
File dbDir = getDbDir();
|
File routerInfoFiles[] = _dbDir.listFiles(RouterInfoFilter.getInstance());
|
||||||
File routerInfoFiles[] = dbDir.listFiles(RouterInfoFilter.getInstance());
|
|
||||||
if (routerInfoFiles != null) {
|
if (routerInfoFiles != null) {
|
||||||
routerCount += routerInfoFiles.length;
|
routerCount += routerInfoFiles.length;
|
||||||
if (routerInfoFiles.length > 5)
|
if (routerInfoFiles.length > 5)
|
||||||
@ -360,9 +374,6 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException ioe) {
|
|
||||||
_log.error("Error reading files in the db dir", ioe);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_alreadyWarned) {
|
if (!_alreadyWarned) {
|
||||||
ReseedChecker.checkReseed(_context, routerCount);
|
ReseedChecker.checkReseed(_context, routerCount);
|
||||||
@ -442,8 +453,8 @@ class PersistentDataStore extends TransientDataStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private File getDbDir() throws IOException {
|
private File getDbDir(String dbDir) throws IOException {
|
||||||
File f = new SecureDirectory(_context.getRouterDir(), _dbDir);
|
File f = new SecureDirectory(_context.getRouterDir(), dbDir);
|
||||||
if (!f.exists()) {
|
if (!f.exists()) {
|
||||||
boolean created = f.mkdirs();
|
boolean created = f.mkdirs();
|
||||||
if (!created)
|
if (!created)
|
||||||
|
@ -15,6 +15,7 @@ import net.i2p.data.i2np.I2NPMessageHandler;
|
|||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
import net.i2p.util.ByteCache;
|
import net.i2p.util.ByteCache;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.util.SimpleByteCache;
|
||||||
import net.i2p.util.SimpleTimer;
|
import net.i2p.util.SimpleTimer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -241,19 +242,21 @@ public class FragmentHandler {
|
|||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("endpoint IV: " + Base64.encode(preV, validLength - HopProcessor.IV_LENGTH, HopProcessor.IV_LENGTH));
|
_log.debug("endpoint IV: " + Base64.encode(preV, validLength - HopProcessor.IV_LENGTH, HopProcessor.IV_LENGTH));
|
||||||
|
|
||||||
Hash v = _context.sha().calculateHash(preV, 0, validLength);
|
byte[] v = SimpleByteCache.acquire(Hash.HASH_LENGTH);
|
||||||
|
_context.sha().calculateHash(preV, 0, validLength, v, 0);
|
||||||
_validateCache.release(ba);
|
_validateCache.release(ba);
|
||||||
|
|
||||||
boolean eq = DataHelper.eq(v.getData(), 0, preprocessed, offset + HopProcessor.IV_LENGTH, 4);
|
boolean eq = DataHelper.eq(v, 0, preprocessed, offset + HopProcessor.IV_LENGTH, 4);
|
||||||
if (!eq) {
|
if (!eq) {
|
||||||
if (_log.shouldLog(Log.WARN)) {
|
if (_log.shouldLog(Log.WARN)) {
|
||||||
_log.warn("Corrupt tunnel message - verification fails: " + Base64.encode(preprocessed, offset+HopProcessor.IV_LENGTH, 4)
|
_log.warn("Corrupt tunnel message - verification fails: " + Base64.encode(preprocessed, offset+HopProcessor.IV_LENGTH, 4)
|
||||||
+ " != " + Base64.encode(v.getData(), 0, 4));
|
+ " != " + Base64.encode(v, 0, 4));
|
||||||
_log.warn("No matching endpoint: # pad bytes: " + (paddingEnd-(HopProcessor.IV_LENGTH+4)-1)
|
_log.warn("No matching endpoint: # pad bytes: " + (paddingEnd-(HopProcessor.IV_LENGTH+4)-1)
|
||||||
+ " offset=" + offset + " length=" + length + " paddingEnd=" + paddingEnd + ' '
|
+ " offset=" + offset + " length=" + length + " paddingEnd=" + paddingEnd + ' '
|
||||||
+ Base64.encode(preprocessed, offset, length), new Exception("trace"));
|
+ Base64.encode(preprocessed, offset, length), new Exception("trace"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SimpleByteCache.release(v);
|
||||||
|
|
||||||
if (eq) {
|
if (eq) {
|
||||||
int excessPadding = paddingEnd - (HopProcessor.IV_LENGTH + 4 + 1);
|
int excessPadding = paddingEnd - (HopProcessor.IV_LENGTH + 4 + 1);
|
||||||
|
Reference in New Issue
Block a user