merge of '0738aeef8a1d4e9ca82dc5ba0077d83a57c47f81'

and '9625ea3e96d57df74bc62018bf64230a22c49ce0'
This commit is contained in:
z3d
2010-10-13 16:07:33 +00:00
149 changed files with 2721 additions and 1741 deletions

View File

@ -9,7 +9,9 @@ import net.i2p.data.PublicKey;
import net.i2p.data.SessionKey;
/**
* Hold the tunnel request record, managing its encryption and decryption.
* Hold the tunnel request record, managing its ElGamal encryption and decryption.
* Iterative AES encryption/decryption is done elsewhere.
*
* Cleartext:
* <pre>
* bytes 0-3: tunnel ID to receive messages as
@ -26,6 +28,12 @@ import net.i2p.data.SessionKey;
* bytes 193-221: uninterpreted / random padding
* </pre>
*
* Encrypted:
* <pre>
* bytes 0-15: First 16 bytes of router hash
* bytes 16-527: ElGamal encrypted block (discarding zero bytes at elg[0] and elg[257])
* </pre>
*
*/
public class BuildRequestRecord {
private ByteArray _data;
@ -152,7 +160,7 @@ public class BuildRequestRecord {
/**
* Encrypt the record to the specified peer. The result is formatted as: <pre>
* bytes 0-15: SHA-256-128 of the current hop's identity (the toPeer parameter)
* bytes 0-15: truncated SHA-256 of the current hop's identity (the toPeer parameter)
* bytes 15-527: ElGamal-2048 encrypted block
* </pre>
*/

View File

@ -27,6 +27,8 @@ import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SigningPublicKey;
import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.util.SecureDirectory;
import net.i2p.util.SecureFileOutputStream;
/**
* Maintain all of the key pairs for the router.
@ -142,7 +144,7 @@ public class KeyManager {
}
public void runJob() {
String keyDir = getContext().getProperty(PROP_KEYDIR, DEFAULT_KEYDIR);
File dir = new File(getContext().getRouterDir(), keyDir);
File dir = new SecureDirectory(getContext().getRouterDir(), keyDir);
if (!dir.exists())
dir.mkdirs();
if (dir.exists() && dir.isDirectory() && dir.canRead() && dir.canWrite()) {
@ -219,7 +221,7 @@ public class KeyManager {
FileInputStream in = null;
try {
if (exists) {
out = new FileOutputStream(keyFile);
out = new SecureFileOutputStream(keyFile);
structure.writeBytes(out);
return structure;
} else {

View File

@ -46,6 +46,7 @@ import net.i2p.util.FileUtil;
import net.i2p.util.I2PAppThread;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
import net.i2p.util.SecureFileOutputStream;
import net.i2p.util.SimpleScheduler;
import net.i2p.util.SimpleTimer;
@ -305,6 +306,7 @@ public class Router {
public void setHigherVersionSeen(boolean seen) { _higherVersionSeen = seen; }
public long getWhenStarted() { return _started; }
/** wall clock uptime */
public long getUptime() {
if ( (_context == null) || (_context.clock() == null) ) return 1; // racing on startup
@ -1053,11 +1055,12 @@ public class Router {
* this does escape the \r or \n that are unescaped in DataHelper.loadProps().
* Note that the escaping of \r or \n was probably a mistake and should be taken out.
*
* FIXME Synchronize!!
*/
public boolean saveConfig() {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(_configFilename);
fos = new SecureFileOutputStream(_configFilename);
StringBuilder buf = new StringBuilder(8*1024);
buf.append("# NOTE: This I2P config file must use UTF-8 encoding\n");
synchronized (_config) {
@ -1541,7 +1544,7 @@ private static class PersistRouterInfoJob extends JobImpl {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(infoFile);
fos = new SecureFileOutputStream(infoFile);
info.writeBytes(fos);
} catch (DataFormatException dfe) {
_log.error("Error rebuilding the router information", dfe);

View File

@ -1,10 +1,11 @@
package net.i2p.router;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import net.i2p.util.SecureFileOutputStream;
/**
* This is the class called by the runplain.sh script on linux
* and the i2p.exe launcher on Windows.
@ -33,7 +34,7 @@ public class RouterLaunch {
}
System.setProperty(PROP_WRAPPER_LOG, logfile.getAbsolutePath());
try {
System.setOut(new PrintStream(new FileOutputStream(logfile, true)));
System.setOut(new PrintStream(new SecureFileOutputStream(logfile, true)));
} catch (IOException ioe) {
ioe.printStackTrace();
}

View File

@ -17,6 +17,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import net.i2p.client.I2PClient;
import net.i2p.crypto.SessionKeyManager;
import net.i2p.crypto.TransientSessionKeyManager;
import net.i2p.data.Destination;
@ -76,11 +77,13 @@ public class ClientConnectionRunner {
* This contains the last 10 MessageIds that have had their (non-ack) status
* delivered to the client (so that we can be sure only to update when necessary)
*/
private final List _alreadyProcessed;
private final List<MessageId> _alreadyProcessed;
private ClientWriterRunner _writer;
private Hash _destHashCache;
/** are we, uh, dead */
private boolean _dead;
/** For outbound traffic. true if i2cp.messageReliability = "none"; @since 0.8.1 */
private boolean _dontSendMSM;
/**
* Create a new runner against the given socket
@ -91,11 +94,9 @@ public class ClientConnectionRunner {
_log = _context.logManager().getLog(ClientConnectionRunner.class);
_manager = manager;
_socket = socket;
_config = null;
_messages = new ConcurrentHashMap();
_alreadyProcessed = new ArrayList();
_acceptedPending = new ConcurrentHashSet();
_dead = false;
}
private static volatile int __id = 0;
@ -189,6 +190,9 @@ public class ClientConnectionRunner {
if (_log.shouldLog(Log.DEBUG))
_log.debug("SessionEstablished called for destination " + _destHashCache.toBase64());
_config = config;
// This is the only option that is interpreted here, not at the tunnel manager
if (config.getOptions() != null)
_dontSendMSM = "none".equalsIgnoreCase(config.getOptions().getProperty(I2PClient.PROP_RELIABILITY));
// per-destination session key manager to prevent rather easy correlation
if (_sessionKeyManager == null)
_sessionKeyManager = new TransientSessionKeyManager(_context);
@ -197,10 +201,18 @@ public class ClientConnectionRunner {
_manager.destinationEstablished(this);
}
/**
* Send a notification to the client that their message (id specified) was
* delivered (or failed delivery)
* Note that this sends the Guaranteed status codes, even though we only support best effort.
* Doesn't do anything if i2cp.messageReliability = "none"
*/
void updateMessageDeliveryStatus(MessageId id, boolean delivered) {
if (_dead) return;
if (_dead || _dontSendMSM)
return;
_context.jobQueue().addJob(new MessageDeliveryStatusUpdate(id, delivered));
}
/**
* called after a new leaseSet is granted by the client, the NetworkDb has been
* updated. This takes care of all the LeaseRequestState stuff (including firing any jobs)
@ -254,7 +266,8 @@ public class ClientConnectionRunner {
long expiration = 0;
if (message instanceof SendMessageExpiresMessage)
expiration = ((SendMessageExpiresMessage) message).getExpiration().getTime();
_acceptedPending.add(id);
if (!_dontSendMSM)
_acceptedPending.add(id);
if (_log.shouldLog(Log.DEBUG))
_log.debug("** Receiving message [" + id.getMessageId() + "] with payload of size ["
@ -276,9 +289,11 @@ public class ClientConnectionRunner {
/**
* Send a notification to the client that their message (id specified) was accepted
* for delivery (but not necessarily delivered)
*
* Doesn't do anything if i2cp.messageReliability = "none"
*/
void ackSendMessage(MessageId id, long nonce) {
if (_dontSendMSM)
return;
SessionId sid = _sessionId;
if (sid == null) return;
if (_log.shouldLog(Log.DEBUG))
@ -517,12 +532,17 @@ public class ClientConnectionRunner {
}
public String getName() { return "Update Delivery Status"; }
/**
* Note that this sends the Guaranteed status codes, even though we only support best effort.
*/
public void runJob() {
if (_dead) return;
MessageStatusMessage msg = new MessageStatusMessage();
msg.setMessageId(_messageId.getMessageId());
msg.setSessionId(_sessionId.getSessionId());
// has to be >= 0, it is initialized to -1
msg.setNonce(2);
msg.setSize(0);
if (_success)

View File

@ -41,8 +41,6 @@ public class ClientListenerRunner implements Runnable {
_log = _context.logManager().getLog(ClientListenerRunner.class);
_manager = manager;
_port = port;
_running = false;
_listening = false;
String val = context.getProperty(BIND_ALL_INTERFACES);
_bindAllInterfaces = Boolean.valueOf(val).booleanValue();

View File

@ -43,7 +43,6 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade {
public ClientManagerFacadeImpl(RouterContext context) {
_context = context;
_manager = null;
_log.debug("Client manager facade created");
}

View File

@ -33,7 +33,6 @@ class LeaseRequestState {
_onFailed = onFailed;
_expiration = expiration;
_requestedLeaseSet = requested;
_successful = false;
}
/** created lease set from client */

View File

@ -47,9 +47,6 @@ class MessageReceivedJob extends JobImpl {
/**
* Deliver notification to the client that the given message is available.
* This is synchronous and returns true if the notification was sent safely,
* otherwise it returns false
*
*/
public void messageAvailable(MessageId id, long size) {
if (_log.shouldLog(Log.DEBUG))
@ -59,6 +56,7 @@ class MessageReceivedJob extends JobImpl {
msg.setMessageId(id.getMessageId());
msg.setSessionId(_runner.getSessionId().getSessionId());
msg.setSize(size);
// has to be >= 0, it is initialized to -1
msg.setNonce(1);
msg.setStatus(MessageStatusMessage.STATUS_AVAILABLE);
try {

View File

@ -44,9 +44,9 @@ class RequestLeaseSetJob extends JobImpl {
_onCreate = onCreate;
_onFail = onFail;
_requestState = state;
ctx.statManager().createRateStat("client.requestLeaseSetSuccess", "How frequently the router requests successfully a new leaseSet?", "ClientMessages", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
ctx.statManager().createRateStat("client.requestLeaseSetTimeout", "How frequently the router requests a new leaseSet but gets no reply?", "ClientMessages", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
ctx.statManager().createRateStat("client.requestLeaseSetDropped", "How frequently the router requests a new leaseSet but the client drops?", "ClientMessages", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
ctx.statManager().createRateStat("client.requestLeaseSetSuccess", "How frequently the router requests successfully a new leaseSet?", "ClientMessages", new long[] { 60*60*1000 });
ctx.statManager().createRateStat("client.requestLeaseSetTimeout", "How frequently the router requests a new leaseSet but gets no reply?", "ClientMessages", new long[] { 60*60*1000 });
ctx.statManager().createRateStat("client.requestLeaseSetDropped", "How frequently the router requests a new leaseSet but the client drops?", "ClientMessages", new long[] { 60*60*1000 });
}
public String getName() { return "Request Lease Set"; }

View File

@ -37,17 +37,9 @@ public class GarlicConfig {
private long _replyBlockExpiration;
public GarlicConfig() {
_recipient = null;
_recipientPublicKey = null;
_cert = null;
_id = -1;
_expiration = -1;
_cloveConfigs = new ArrayList();
_instructions = null;
_requestAck = false;
_replyThroughRouter = null;
_replyInstructions = null;
_replyBlockCertificate = null;
_replyBlockMessageId = -1;
_replyBlockExpiration = -1;
}

View File

@ -194,10 +194,12 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
public void shutdown() {
_initialized = false;
_kb = null;
// don't null out _kb, it can cause NPEs in concurrent operations
//_kb = null;
if (_ds != null)
_ds.stop();
_ds = null;
// don't null out _ds, it can cause NPEs in concurrent operations
//_ds = null;
_exploreKeys.clear(); // hope this doesn't cause an explosion, it shouldn't.
// _exploreKeys = null;
}
@ -750,6 +752,10 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
} else if (upLongEnough && !routerInfo.isCurrent(ROUTER_INFO_EXPIRATION_SHORT)) {
if (routerInfo.getAddresses().isEmpty())
return "Peer " + key.toBase64() + " published > 90m ago with no addresses";
// This should cover the introducers case below too
// And even better, catches the case where the router is unreachable but knows no introducers
if (routerInfo.getCapabilities().indexOf(Router.CAPABILITY_UNREACHABLE) >= 0)
return "Peer " + key.toBase64() + " published > 90m ago and thinks it is unreachable";
RouterAddress ra = routerInfo.getTargetAddress("SSU");
if (ra != null) {
// Introducers change often, introducee will ping introducer for 2 hours

View File

@ -29,6 +29,8 @@ import net.i2p.router.RouterContext;
import net.i2p.router.networkdb.reseed.ReseedChecker;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
import net.i2p.util.SecureDirectory;
import net.i2p.util.SecureFileOutputStream;
/**
* Write out keys to disk when we get them and periodically read ones we don't know
@ -288,7 +290,7 @@ class PersistentDataStore extends TransientDataStore {
long dataPublishDate = getPublishDate(data);
if (dbFile.lastModified() < dataPublishDate) {
// our filesystem is out of date, lets replace it
fos = new FileOutputStream(dbFile);
fos = new SecureFileOutputStream(dbFile);
try {
data.writeBytes(fos);
fos.close();
@ -440,7 +442,7 @@ class PersistentDataStore extends TransientDataStore {
private File getDbDir() throws IOException {
File f = new File(_context.getRouterDir(), _dbDir);
File f = new SecureDirectory(_context.getRouterDir(), _dbDir);
if (!f.exists()) {
boolean created = f.mkdirs();
if (!created)

View File

@ -17,6 +17,8 @@ import net.i2p.router.RouterContext;
import net.i2p.util.EepGet;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.SecureDirectory;
import net.i2p.util.SecureFileOutputStream;
import net.i2p.util.SSLEepGet;
import net.i2p.util.Translate;
@ -260,11 +262,11 @@ public class Reseeder {
private void writeSeed(String name, byte data[]) throws Exception {
String dirName = "netDb"; // _context.getProperty("router.networkDatabase.dbDir", "netDb");
File netDbDir = new File(_context.getRouterDir(), dirName);
File netDbDir = new SecureDirectory(_context.getRouterDir(), dirName);
if (!netDbDir.exists()) {
boolean ok = netDbDir.mkdirs();
}
FileOutputStream fos = new FileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat"));
FileOutputStream fos = new SecureFileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat"));
fos.write(data);
fos.close();
}

View File

@ -3,7 +3,6 @@ package net.i2p.router.peermanager;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStream;
@ -19,6 +18,8 @@ import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.router.RouterContext;
import net.i2p.util.Log;
import net.i2p.util.SecureDirectory;
import net.i2p.util.SecureFileOutputStream;
class ProfilePersistenceHelper {
private Log _log;
@ -61,7 +62,7 @@ class ProfilePersistenceHelper {
long before = _context.clock().now();
OutputStream fos = null;
try {
fos = new BufferedOutputStream(new GZIPOutputStream(new FileOutputStream(f)));
fos = new BufferedOutputStream(new GZIPOutputStream(new SecureFileOutputStream(f)));
writeProfile(profile, fos);
} catch (IOException ioe) {
_log.error("Error writing profile to " + f);
@ -310,7 +311,7 @@ class ProfilePersistenceHelper {
private File getProfileDir() {
if (_profileDir == null) {
String dir = _context.getProperty(PROP_PEER_PROFILE_DIR, DEFAULT_PEER_PROFILE_DIR);
_profileDir = new File(_context.getRouterDir(), dir);
_profileDir = new SecureDirectory(_context.getRouterDir(), dir);
}
return _profileDir;
}

View File

@ -11,6 +11,7 @@ import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.router.RouterContext;
import net.i2p.util.SecureFileOutputStream;
/**
@ -191,7 +192,7 @@ public class ClientAppConfig {
File cfgFile = configFile(ctx);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(cfgFile);
fos = new SecureFileOutputStream(cfgFile);
StringBuilder buf = new StringBuilder(2048);
for(int i = 0; i < apps.size(); i++) {
ClientAppConfig app = (ClientAppConfig) apps.get(i);

View File

@ -27,6 +27,7 @@ import net.i2p.router.JobImpl;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.util.Log;
import net.i2p.util.SecureFileOutputStream;
public class CreateRouterInfoJob extends JobImpl {
private static Log _log = new Log(CreateRouterInfoJob.class);
@ -80,12 +81,12 @@ public class CreateRouterInfoJob extends JobImpl {
String infoFilename = getContext().getProperty(Router.PROP_INFO_FILENAME, Router.PROP_INFO_FILENAME_DEFAULT);
File ifile = new File(getContext().getRouterDir(), infoFilename);
fos1 = new FileOutputStream(ifile);
fos1 = new SecureFileOutputStream(ifile);
info.writeBytes(fos1);
String keyFilename = getContext().getProperty(Router.PROP_KEYS_FILENAME, Router.PROP_KEYS_FILENAME_DEFAULT);
File kfile = new File(getContext().getRouterDir(), keyFilename);
fos2 = new FileOutputStream(kfile);
fos2 = new SecureFileOutputStream(kfile);
privkey.writeBytes(fos2);
signingPrivKey.writeBytes(fos2);
pubkey.writeBytes(fos2);

View File

@ -26,6 +26,7 @@ import net.i2p.router.JobImpl;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.util.Log;
import net.i2p.util.SecureFileOutputStream;
/**
* This used be called from StartAcceptingClientsJob but is now disabled.
@ -135,7 +136,7 @@ public class RebuildRouterInfoJob extends JobImpl {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(infoFile);
fos = new SecureFileOutputStream(infoFile);
info.writeBytes(fos);
} catch (DataFormatException dfe) {
_log.log(Log.CRIT, "Error rebuilding the router information", dfe);

View File

@ -11,6 +11,8 @@ import java.io.PrintWriter;
import java.util.Properties;
import net.i2p.data.DataHelper;
import net.i2p.util.SecureDirectory;
import net.i2p.util.SecureFileOutputStream;
/**
* Get a working directory for i2p.
@ -64,19 +66,19 @@ public class WorkingDir {
boolean isWindows = System.getProperty("os.name").startsWith("Win");
File dirf = null;
if (dir != null) {
dirf = new File(dir);
dirf = new SecureDirectory(dir);
} else {
String home = System.getProperty("user.home");
if (isWindows) {
String appdata = System.getenv("APPDATA");
if (appdata != null)
home = appdata;
dirf = new File(home, WORKING_DIR_DEFAULT_WINDOWS);
dirf = new SecureDirectory(home, WORKING_DIR_DEFAULT_WINDOWS);
} else {
if (DAEMON_USER.equals(System.getProperty("user.name")))
dirf = new File(home, WORKING_DIR_DEFAULT_DAEMON);
dirf = new SecureDirectory(home, WORKING_DIR_DEFAULT_DAEMON);
else
dirf = new File(home, WORKING_DIR_DEFAULT);
dirf = new SecureDirectory(home, WORKING_DIR_DEFAULT);
}
}
@ -143,7 +145,7 @@ public class WorkingDir {
// this one must be after MIGRATE_BASE
success &= migrateJettyXml(oldDirf, dirf);
success &= migrateClientsConfig(oldDirf, dirf);
success &= copy(new File(oldDirf, "docs/news.xml"), new File(dirf, "docs"));
success &= copy(new File(oldDirf, "docs/news.xml"), new SecureDirectory(dirf, "docs"));
// Report success or failure
if (success) {
@ -197,7 +199,7 @@ public class WorkingDir {
PrintWriter out = null;
try {
in = new FileInputStream(oldFile);
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(newFile), "UTF-8")));
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(newFile), "UTF-8")));
out.println("# Modified by I2P User dir migration script");
String s = null;
boolean isDaemon = DAEMON_USER.equals(System.getProperty("user.name"));
@ -240,7 +242,7 @@ public class WorkingDir {
PrintWriter out = null;
try {
in = new FileInputStream(oldFile);
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(newFile), "UTF-8")));
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(newFile), "UTF-8")));
String s = null;
while ((s = DataHelper.readLine(in)) != null) {
if (s.indexOf("./eepsite/") >= 0) {
@ -270,7 +272,7 @@ public class WorkingDir {
* @param targetDir the directory to copy to, will be created if it doesn't exist
* @return true for success OR if src does not exist
*/
public static final boolean copy(File src, File targetDir) {
private static boolean copy(File src, File targetDir) {
if (!src.exists())
return true;
if (!targetDir.exists()) {
@ -280,7 +282,8 @@ public class WorkingDir {
}
System.err.println("Created " + targetDir.getPath());
}
File targetFile = new File(targetDir, src.getName());
// SecureDirectory is a File so this works for non-directories too
File targetFile = new SecureDirectory(targetDir, src.getName());
if (!src.isDirectory())
return copyFile(src, targetFile);
File children[] = src.listFiles();
@ -305,10 +308,10 @@ public class WorkingDir {
/**
* @param src not a directory, must exist
* @param dst not a directory, will be overwritten if existing
* @param dst not a directory, will be overwritten if existing, will be mode 600
* @return true if it was copied successfully
*/
public static boolean copyFile(File src, File dst) {
private static boolean copyFile(File src, File dst) {
if (!src.exists()) return false;
boolean rv = true;
@ -317,7 +320,7 @@ public class WorkingDir {
FileOutputStream out = null;
try {
in = new FileInputStream(src);
out = new FileOutputStream(dst);
out = new SecureFileOutputStream(dst);
int read = 0;
while ( (read = in.read(buf)) != -1)

View File

@ -98,23 +98,26 @@ public class GeoIP {
public void run() {
if (_lock.getAndSet(true))
return;
// clear the negative cache every few runs, to prevent it from getting too big
if (((++_lookupRunCount) % CLEAR) == 0)
_notFound.clear();
Long[] search = _pendingSearch.toArray(new Long[_pendingSearch.size()]);
if (search.length <= 0)
return;
_pendingSearch.clear();
Arrays.sort(search);
String[] countries = readGeoIPFile(search);
for (int i = 0; i < countries.length; i++) {
if (countries[i] != null)
_IPToCountry.put(search[i], countries[i]);
else
_notFound.add(search[i]);
try {
// clear the negative cache every few runs, to prevent it from getting too big
if (((++_lookupRunCount) % CLEAR) == 0)
_notFound.clear();
Long[] search = _pendingSearch.toArray(new Long[_pendingSearch.size()]);
if (search.length <= 0)
return;
_pendingSearch.clear();
Arrays.sort(search);
String[] countries = readGeoIPFile(search);
for (int i = 0; i < countries.length; i++) {
if (countries[i] != null)
_IPToCountry.put(search[i], countries[i]);
else
_notFound.add(search[i]);
}
} finally {
_lock.set(false);
}
_lock.set(false);
}
}

View File

@ -21,6 +21,7 @@ import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.data.Hash;
import net.i2p.data.RouterAddress;
@ -36,7 +37,11 @@ import net.i2p.util.Translate;
public class TransportManager implements TransportEventListener {
private Log _log;
private List<Transport> _transports;
/**
* Converted from List to prevent concurrent modification exceptions.
* If we want more than one transport with the same style we will have to change this.
*/
private Map<String, Transport> _transports;
private RouterContext _context;
private UPnPManager _upnpManager;
@ -56,20 +61,20 @@ public class TransportManager implements TransportEventListener {
_context.statManager().createRateStat("transport.bidFailSelf", "Could not attempt to bid on message, as it targeted ourselves", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("transport.bidFailNoTransports", "Could not attempt to bid on message, as none of the transports could attempt it", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("transport.bidFailAllTransports", "Could not attempt to bid on message, as all of the transports had failed", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_transports = new ArrayList();
_transports = new ConcurrentHashMap(2);
if (Boolean.valueOf(_context.getProperty(PROP_ENABLE_UPNP, "true")).booleanValue())
_upnpManager = new UPnPManager(context, this);
}
public void addTransport(Transport transport) {
if (transport == null) return;
_transports.add(transport);
_transports.put(transport.getStyle(), transport);
transport.setListener(this);
}
public void removeTransport(Transport transport) {
if (transport == null) return;
_transports.remove(transport);
_transports.remove(transport.getStyle());
transport.setListener(null);
}
@ -140,11 +145,10 @@ public class TransportManager implements TransportEventListener {
_upnpManager.start();
configTransports();
_log.debug("Starting up the transport manager");
for (int i = 0; i < _transports.size(); i++) {
Transport t = _transports.get(i);
for (Transport t : _transports.values()) {
RouterAddress addr = t.startListening();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Transport " + i + " (" + t.getStyle() + ") started");
_log.debug("Transport " + t.getStyle() + " started");
}
// kick UPnP - Do this to get the ports opened even before UDP registers an address
transportAddressChanged();
@ -161,19 +165,14 @@ public class TransportManager implements TransportEventListener {
public void stopListening() {
if (_upnpManager != null)
_upnpManager.stop();
for (int i = 0; i < _transports.size(); i++) {
_transports.get(i).stopListening();
for (Transport t : _transports.values()) {
t.stopListening();
}
_transports.clear();
}
public Transport getTransport(String style) {
for (int i = 0; i < _transports.size(); i++) {
Transport t = _transports.get(i);
if(style.equals(t.getStyle()))
return t;
}
return null;
return _transports.get(style);
}
int getTransportCount() { return _transports.size(); }
@ -189,16 +188,16 @@ public class TransportManager implements TransportEventListener {
public int countActivePeers() {
int peers = 0;
for (int i = 0; i < _transports.size(); i++) {
peers += _transports.get(i).countActivePeers();
for (Transport t : _transports.values()) {
peers += t.countActivePeers();
}
return peers;
}
public int countActiveSendPeers() {
int peers = 0;
for (int i = 0; i < _transports.size(); i++) {
peers += _transports.get(i).countActiveSendPeers();
for (Transport t : _transports.values()) {
peers += t.countActiveSendPeers();
}
return peers;
}
@ -210,8 +209,8 @@ public class TransportManager implements TransportEventListener {
* @param pct percent of limit 0-100
*/
public boolean haveOutboundCapacity(int pct) {
for (int i = 0; i < _transports.size(); i++) {
if (_transports.get(i).haveCapacity(pct))
for (Transport t : _transports.values()) {
if (t.haveCapacity(pct))
return true;
}
return false;
@ -225,8 +224,8 @@ public class TransportManager implements TransportEventListener {
public boolean haveHighOutboundCapacity() {
if (_transports.isEmpty())
return false;
for (int i = 0; i < _transports.size(); i++) {
if (!_transports.get(i).haveCapacity(HIGH_CAPACITY_PCT))
for (Transport t : _transports.values()) {
if (!t.haveCapacity(HIGH_CAPACITY_PCT))
return false;
}
return true;
@ -239,8 +238,8 @@ public class TransportManager implements TransportEventListener {
* @param pct percent of limit 0-100
*/
public boolean haveInboundCapacity(int pct) {
for (int i = 0; i < _transports.size(); i++) {
if (_transports.get(i).getCurrentAddress() != null && _transports.get(i).haveCapacity(pct))
for (Transport t : _transports.values()) {
if (t.getCurrentAddress() != null && t.haveCapacity(pct))
return true;
}
return false;
@ -253,8 +252,8 @@ public class TransportManager implements TransportEventListener {
*/
public Vector getClockSkews() {
Vector skews = new Vector();
for (int i = 0; i < _transports.size(); i++) {
Vector tempSkews = _transports.get(i).getClockSkews();
for (Transport t : _transports.values()) {
Vector tempSkews = t.getClockSkews();
if ((tempSkews == null) || (tempSkews.isEmpty())) continue;
skews.addAll(tempSkews);
}
@ -266,7 +265,7 @@ public class TransportManager implements TransportEventListener {
/** @return the best status of any transport */
public short getReachabilityStatus() {
short rv = CommSystemFacade.STATUS_UNKNOWN;
for (Transport t : _transports) {
for (Transport t : _transports.values()) {
short s = t.getReachabilityStatus();
if (s < rv)
rv = s;
@ -275,13 +274,12 @@ public class TransportManager implements TransportEventListener {
}
public void recheckReachability() {
for (int i = 0; i < _transports.size(); i++)
_transports.get(i).recheckReachability();
for (Transport t : _transports.values())
t.recheckReachability();
}
public boolean isBacklogged(Hash dest) {
for (int i = 0; i < _transports.size(); i++) {
Transport t = _transports.get(i);
for (Transport t : _transports.values()) {
if (t.isBacklogged(dest))
return true;
}
@ -289,8 +287,7 @@ public class TransportManager implements TransportEventListener {
}
public boolean isEstablished(Hash dest) {
for (int i = 0; i < _transports.size(); i++) {
Transport t = _transports.get(i);
for (Transport t : _transports.values()) {
if (t.isEstablished(dest))
return true;
}
@ -303,8 +300,7 @@ public class TransportManager implements TransportEventListener {
* This is NOT reset if the peer contacts us.
*/
public boolean wasUnreachable(Hash dest) {
for (int i = 0; i < _transports.size(); i++) {
Transport t = _transports.get(i);
for (Transport t : _transports.values()) {
if (!t.wasUnreachable(dest))
return false;
}
@ -330,9 +326,9 @@ public class TransportManager implements TransportEventListener {
public Map<String, RouterAddress> getAddresses() {
Map<String, RouterAddress> rv = new HashMap(_transports.size());
// do this first since SSU may force a NTCP change
for (Transport t : _transports)
for (Transport t : _transports.values())
t.updateAddress();
for (Transport t : _transports) {
for (Transport t : _transports.values()) {
if (t.getCurrentAddress() != null)
rv.put(t.getStyle(), t.getCurrentAddress());
}
@ -345,7 +341,7 @@ public class TransportManager implements TransportEventListener {
*/
private Map<String, Integer> getPorts() {
Map<String, Integer> rv = new HashMap(_transports.size());
for (Transport t : _transports) {
for (Transport t : _transports.values()) {
int port = t.getRequestedPort();
if (t.getCurrentAddress() != null) {
Properties opts = t.getCurrentAddress().getOptions();
@ -386,8 +382,7 @@ public class TransportManager implements TransportEventListener {
List<TransportBid> rv = new ArrayList(_transports.size());
Set failedTransports = msg.getFailedTransports();
for (int i = 0; i < _transports.size(); i++) {
Transport t = _transports.get(i);
for (Transport t : _transports.values()) {
if (failedTransports.contains(t.getStyle())) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Skipping transport " + t.getStyle() + " as it already failed");
@ -415,8 +410,7 @@ public class TransportManager implements TransportEventListener {
Hash peer = msg.getTarget().getIdentity().calculateHash();
Set failedTransports = msg.getFailedTransports();
TransportBid rv = null;
for (int i = 0; i < _transports.size(); i++) {
Transport t = _transports.get(i);
for (Transport t : _transports.values()) {
if (t.isUnreachable(peer)) {
unreachableTransports++;
// this keeps GetBids() from shitlisting for "no common transports"
@ -482,8 +476,7 @@ public class TransportManager implements TransportEventListener {
public List getMostRecentErrorMessages() {
List rv = new ArrayList(16);
for (int i = 0; i < _transports.size(); i++) {
Transport t = _transports.get(i);
for (Transport t : _transports.values()) {
rv.addAll(t.getMostRecentErrorMessages());
}
return rv;
@ -491,8 +484,7 @@ public class TransportManager implements TransportEventListener {
public void renderStatusHTML(Writer out, String urlBase, int sortFlags) throws IOException {
TreeMap transports = new TreeMap();
for (int i = 0; i < _transports.size(); i++) {
Transport t = _transports.get(i);
for (Transport t : _transports.values()) {
transports.put(t.getStyle(), t);
}
for (Iterator iter = transports.values().iterator(); iter.hasNext(); ) {
@ -506,8 +498,7 @@ public class TransportManager implements TransportEventListener {
StringBuilder buf = new StringBuilder(4*1024);
buf.append("<h3>").append(_("Router Transport Addresses")).append("</h3><pre>\n");
for (int i = 0; i < _transports.size(); i++) {
Transport t = _transports.get(i);
for (Transport t : _transports.values()) {
if (t.getCurrentAddress() != null)
buf.append(t.getCurrentAddress());
else

View File

@ -581,6 +581,8 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
// If not in portsForwarded, it wasn't successful, try again
if(portsForwarded.contains(port)) {
// We have forwarded it, and it should be forwarded, cool.
// Big problem here, if firewall resets, we don't know it.
// Do we need to re-forward anyway? or poll the router?
} else {
// Needs forwarding
if(portsToForwardNow == null) portsToForwardNow = new HashSet<ForwardPort>();

View File

@ -4,7 +4,7 @@ package net.i2p.router.transport.udp;
* Generic means of SACK/NACK transmission for partially or fully
* received messages
*/
public interface ACKBitfield {
interface ACKBitfield {
/** what message is this partially ACKing? */
public long getMessageId();
/** how many fragments are covered in this bitfield? */

View File

@ -15,7 +15,7 @@ import net.i2p.util.Log;
* any outstanding ACKs.
*
*/
public class ACKSender implements Runnable {
class ACKSender implements Runnable {
private RouterContext _context;
private Log _log;
private UDPTransport _transport;

View File

@ -14,7 +14,7 @@ import net.i2p.router.OutNetMessage;
*
* @since 0.7.12
*/
public class DummyThrottle implements OutboundMessageFragments.ActiveThrottle {
class DummyThrottle implements OutboundMessageFragments.ActiveThrottle {
public DummyThrottle() {
}

View File

@ -33,7 +33,7 @@ import net.i2p.util.SimpleTimer;
* as well as to drop any failed establishment attempts.
*
*/
public class EstablishmentManager {
class EstablishmentManager {
private final RouterContext _context;
private final Log _log;
private final UDPTransport _transport;
@ -316,6 +316,40 @@ public class EstablishmentManager {
}
}
/**
* Got a SessionDestroy on an established conn
*/
void receiveSessionDestroy(RemoteHostId from, PeerState state) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive session destroy (EST) from: " + from);
_transport.dropPeer(state, false, "received destroy message");
}
/**
* Got a SessionDestroy during outbound establish
*/
void receiveSessionDestroy(RemoteHostId from, OutboundEstablishState state) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive session destroy (OB) from: " + from);
_outboundStates.remove(from);
Hash peer = state.getRemoteIdentity().calculateHash();
_transport.dropPeer(peer, false, "received destroy message");
}
/**
* Got a SessionDestroy - maybe after an inbound establish
*/
void receiveSessionDestroy(RemoteHostId from) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive session destroy (IB) from: " + from);
InboundEstablishState state = _inboundStates.remove(from);
if (state != null) {
Hash peer = state.getConfirmedIdentity().calculateHash();
if (peer != null)
_transport.dropPeer(peer, false, "received destroy message");
}
}
/**
* A data packet arrived on an outbound connection being established, which
* means its complete (yay!). This is a blocking call, more than I'd like...

View File

@ -20,7 +20,7 @@ import net.i2p.util.Log;
* we are Bob.
*
*/
public class InboundEstablishState {
class InboundEstablishState {
private final RouterContext _context;
private final Log _log;
// SessionRequest message
@ -218,6 +218,13 @@ public class InboundEstablishState {
if (_receivedIdentity == null)
_receivedIdentity = new byte[conf.readTotalFragmentNum()][];
int cur = conf.readCurrentFragmentNum();
if (cur >= _receivedIdentity.length) {
// avoid AIOOBE
// should do more than this, but what? disconnect?
fail();
packetReceived();
return;
}
if (_receivedIdentity[cur] == null) {
byte fragment[] = new byte[conf.readCurrentFragmentSize()];
conf.readFragmentData(fragment, 0);

View File

@ -17,7 +17,7 @@ import net.i2p.util.Log;
* basic line of defense here).
*
*/
public class InboundMessageFragments /*implements UDPTransport.PartialACKSource */{
class InboundMessageFragments /*implements UDPTransport.PartialACKSource */{
private RouterContext _context;
private Log _log;
/** list of message IDs recently received, so we can ignore in flight dups */

View File

@ -11,7 +11,7 @@ import net.i2p.util.Log;
* Hold the raw data fragments of an inbound message
*
*/
public class InboundMessageState {
class InboundMessageState {
private RouterContext _context;
private Log _log;
private long _messageId;

View File

@ -20,7 +20,7 @@ import net.i2p.util.Log;
/**
*
*/
public class IntroductionManager {
class IntroductionManager {
private RouterContext _context;
private Log _log;
private UDPTransport _transport;
@ -96,7 +96,10 @@ public class IntroductionManager {
int sz = peers.size();
start = start % sz;
int found = 0;
long inactivityCutoff = _context.clock().now() - (UDPTransport.EXPIRE_TIMEOUT / 2);
long inactivityCutoff = _context.clock().now() - (UDPTransport.EXPIRE_TIMEOUT / 2); // 15 min
// if not too many to choose from, be less picky
if (sz <= howMany + 2)
inactivityCutoff -= UDPTransport.EXPIRE_TIMEOUT / 4;
for (int i = 0; i < sz && found < howMany; i++) {
PeerState cur = peers.get((start + i) % sz);
RouterInfo ri = _context.netDb().lookupRouterInfoLocally(cur.getRemotePeer());
@ -119,7 +122,11 @@ public class IntroductionManager {
continue;
}
// Try to pick active peers...
if (cur.getLastReceiveTime() < inactivityCutoff || cur.getLastSendTime() < inactivityCutoff) {
// FIXME this is really strict and causes us to run out of introducers
// We have much less introducers than we used to have because routers don't offer
// if they are approaching max connections (see EstablishmentManager)
// FIXED, was ||, is this OK now?
if (cur.getLastReceiveTime() < inactivityCutoff && cur.getLastSendTime() < inactivityCutoff) {
if (_log.shouldLog(Log.INFO))
_log.info("Peer is idle too long: " + cur);
continue;
@ -135,6 +142,8 @@ public class IntroductionManager {
found++;
}
// FIXME failsafe if found == 0, relax inactivityCutoff and try again?
// Try to keep the connection up for two hours after we made anybody an introducer
long pingCutoff = _context.clock().now() - (2 * 60 * 60 * 1000);
inactivityCutoff = _context.clock().now() - (UDPTransport.EXPIRE_TIMEOUT / 4);
@ -156,7 +165,7 @@ public class IntroductionManager {
* Not as elaborate as pickInbound() above.
* Just a quick check to see how many volunteers we know,
* which the Transport uses to see if we need more.
* @return number of peers that have volunteerd to introduce us
* @return number of peers that have volunteered to introduce us
*/
int introducerCount() {
return _inbound.size();

View File

@ -5,7 +5,7 @@ import net.i2p.router.OutNetMessage;
/**
* Base queue for messages not yet packetized
*/
public interface MessageQueue {
interface MessageQueue {
/**
* Get the next message, blocking until one is found or the expiration
* reached.

View File

@ -19,7 +19,7 @@ import net.i2p.util.Log;
* parse 'em into I2NPMessages, and stick them on the
* {@link net.i2p.router.InNetMessagePool} by way of the {@link UDPTransport}.
*/
public class MessageReceiver {
class MessageReceiver {
private RouterContext _context;
private Log _log;
private UDPTransport _transport;

View File

@ -21,7 +21,7 @@ import net.i2p.util.Log;
* they are Bob.
*
*/
public class OutboundEstablishState {
class OutboundEstablishState {
private final RouterContext _context;
private final Log _log;
// SessionRequest message

View File

@ -22,7 +22,7 @@ import net.i2p.util.Log;
* message.
*
*/
public class OutboundMessageFragments {
class OutboundMessageFragments {
private RouterContext _context;
private Log _log;
private UDPTransport _transport;

View File

@ -15,7 +15,7 @@ import net.i2p.util.Log;
* Maintain the outbound fragmentation for resending
*
*/
public class OutboundMessageState {
class OutboundMessageState {
private I2PAppContext _context;
private Log _log;
/** may be null if we are part of the establishment */

View File

@ -12,7 +12,7 @@ import net.i2p.util.Log;
* WARNING - UNUSED since 0.6.1.11
*
*/
public class OutboundRefiller implements Runnable {
class OutboundRefiller implements Runnable {
private RouterContext _context;
private Log _log;
private OutboundMessageFragments _fragments;

View File

@ -4,7 +4,6 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import net.i2p.I2PAppContext;
@ -22,8 +21,80 @@ import net.i2p.util.Log;
* Big ol' class to do all our packet formatting. The UDPPackets generated are
* fully authenticated, encrypted, and configured for delivery to the peer.
*
* The following is from udp.html on the website:
<p>
All UDP datagrams begin with a 16 byte MAC (Message Authentication Code)
and a 16 byte IV (Initialization Vector
followed by a variable
size payload encrypted with the appropriate key. The MAC used is
HMAC-MD5, truncated to 16 bytes, while the key is a full 32 byte AES256
key. The specific construct of the MAC is the first 16 bytes from:</p>
<pre>
HMAC-MD5(payload || IV || (payloadLength ^ protocolVersion), macKey)
</pre>
<p>The protocol version is currently 0.</p>
<p>The payload itself is AES256/CBC encrypted with the IV and the
sessionKey, with replay prevention addressed within its body,
explained below. The payloadLength in the MAC is a 2 byte unsigned
integer in 2s complement.</p>
<p>The protocolVersion is a 2 byte unsigned integer in 2s complement,
and currently set to 0. Peers using a different protocol version will
not be able to communicate with this peer, though earlier versions not
using this flag are.</p>
<h2><a name="payload">Payload</a></h2>
<p>Within the AES encrypted payload, there is a minimal common structure
to the various messages - a one byte flag and a four byte sending
timestamp (*seconds* since the unix epoch). The flag byte contains
the following bitfields:</p>
<pre>
bits 0-3: payload type
bit 4: rekey?
bit 5: extended options included
bits 6-7: reserved
</pre>
<p>If the rekey flag is set, 64 bytes of keying material follow the
timestamp. If the extended options flag is set, a one byte option
size value is appended to, followed by that many extended option
bytes, which are currently uninterpreted.</p>
<p>When rekeying, the first 32 bytes of the keying material is fed
into a SHA256 to produce the new MAC key, and the next 32 bytes are
fed into a SHA256 to produce the new session key, though the keys are
not immediately used. The other side should also reply with the
rekey flag set and that same keying material. Once both sides have
sent and received those values, the new keys should be used and the
previous keys discarded. It may be useful to keep the old keys
around briefly, to address packet loss and reordering.</p>
<p>NOTE: Rekeying is currently unimplemented.</p>
<pre>
Header: 37+ bytes
+----+----+----+----+----+----+----+----+
| MAC |
| |
+----+----+----+----+----+----+----+----+
| IV |
| |
+----+----+----+----+----+----+----+----+
|flag| time | (optionally |
+----+----+----+----+----+ |
| this may have 64 byte keying material |
| and/or a one+N byte extended options) |
+---------------------------------------|
</pre>
*
*
*/
public class PacketBuilder {
class PacketBuilder {
private I2PAppContext _context;
private Log _log;
private UDPTransport _transport;
@ -62,10 +133,16 @@ public class PacketBuilder {
_context.statManager().createRateStat("udp.packetAuthTimeSlow", "How long it takes to encrypt and MAC a packet for sending (when its slow)", "udp", UDPTransport.RATES);
}
/****
public UDPPacket buildPacket(OutboundMessageState state, int fragment, PeerState peer) {
return buildPacket(state, fragment, peer, null, null);
}
****/
/**
* This builds a data packet (PAYLOAD_TYPE_DATA).
* See the methods below for the other message types.
*
* @param ackIdsRemaining list of messageIds (Long) that should be acked by this packet.
* The list itself is passed by reference, and if a messageId is
* transmitted and the sender does not want the ID to be included
@ -78,7 +155,9 @@ public class PacketBuilder {
* included, it should be removed from the list.
*/
public UDPPacket buildPacket(OutboundMessageState state, int fragment, PeerState peer, List<Long> ackIdsRemaining, List<ACKBitfield> partialACKsRemaining) {
UDPPacket packet = UDPPacket.acquire(_context, false);
UDPPacket packet = buildPacketHeader((byte)(UDPPacket.PAYLOAD_TYPE_DATA << 4));
byte data[] = packet.getPacket().getData();
int off = HEADER_SIZE;
StringBuilder msg = null;
boolean acksIncluded = false;
@ -90,19 +169,6 @@ public class PacketBuilder {
msg.append("*");
}
byte data[] = packet.getPacket().getData();
Arrays.fill(data, 0, data.length, (byte)0x0);
int start = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
int off = start;
// header
data[off] |= (UDPPacket.PAYLOAD_TYPE_DATA << 4);
// todo: add support for rekeying and extended options
off++;
long now = _context.clock().now() / 1000;
DataHelper.toLong(data, off, 4, now);
off += 4;
// ok, now for the body...
// just always ask for an ACK for now...
@ -231,8 +297,10 @@ public class PacketBuilder {
return packet;
}
// We use this for keepalive purposes.
// It doesn't generate a reply, but that's ok.
/**
* We use this for keepalive purposes.
* It doesn't generate a reply, but that's ok.
*/
public UDPPacket buildPing(PeerState peer) {
return buildACK(peer, Collections.EMPTY_LIST);
}
@ -242,11 +310,14 @@ public class PacketBuilder {
/**
* Build the ack packet. The list need not be sorted into full and partial;
* this method will put all fulls before the partials in the outgoing packet.
* An ack packet is just a data packet with no data.
*
* @param ackBitfields list of ACKBitfield instances to either fully or partially ACK
*/
public UDPPacket buildACK(PeerState peer, List<ACKBitfield> ackBitfields) {
UDPPacket packet = UDPPacket.acquire(_context, false);
UDPPacket packet = buildPacketHeader((byte)(UDPPacket.PAYLOAD_TYPE_DATA << 4));
byte data[] = packet.getPacket().getData();
int off = HEADER_SIZE;
StringBuilder msg = null;
if (_log.shouldLog(Log.DEBUG)) {
@ -254,18 +325,6 @@ public class PacketBuilder {
msg.append("building ACK packet to ").append(peer.getRemotePeer().toBase64().substring(0,6));
}
byte data[] = packet.getPacket().getData();
Arrays.fill(data, 0, data.length, (byte)0x0);
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
// header
data[off] |= (UDPPacket.PAYLOAD_TYPE_DATA << 4);
// todo: add support for rekeying and extended options
off++;
long now = _context.clock().now() / 1000;
DataHelper.toLong(data, off, 4, now);
off += 4;
int fullACKCount = 0;
int partialACKCount = 0;
for (int i = 0; i < ackBitfields.size(); i++) {
@ -352,7 +411,9 @@ public class PacketBuilder {
* @return ready to send packet, or null if there was a problem
*/
public UDPPacket buildSessionCreatedPacket(InboundEstablishState state, int externalPort, SessionKey ourIntroKey) {
UDPPacket packet = UDPPacket.acquire(_context, false);
UDPPacket packet = buildPacketHeader(SESSION_CREATED_FLAG_BYTE);
byte data[] = packet.getPacket().getData();
int off = HEADER_SIZE;
InetAddress to = null;
try {
@ -366,17 +427,6 @@ public class PacketBuilder {
state.prepareSessionCreated();
byte data[] = packet.getPacket().getData();
Arrays.fill(data, 0, data.length, (byte)0x0);
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
// header
data[off] = SESSION_CREATED_FLAG_BYTE;
off++;
long now = _context.clock().now() / 1000;
DataHelper.toLong(data, off, 4, now);
off += 4;
byte sentIP[] = state.getSentIP();
if ( (sentIP == null) || (sentIP.length <= 0) || ( (_transport != null) && (!_transport.isValid(sentIP)) ) ) {
if (_log.shouldLog(Log.ERROR))
@ -455,7 +505,10 @@ public class PacketBuilder {
* @return ready to send packet, or null if there was a problem
*/
public UDPPacket buildSessionRequestPacket(OutboundEstablishState state) {
UDPPacket packet = UDPPacket.acquire(_context, false);
UDPPacket packet = buildPacketHeader(SESSION_REQUEST_FLAG_BYTE);
byte data[] = packet.getPacket().getData();
int off = HEADER_SIZE;
byte toIP[] = state.getSentIP();
if ( (_transport !=null) && (!_transport.isValid(toIP)) ) {
packet.release();
@ -470,19 +523,8 @@ public class PacketBuilder {
packet.release();
return null;
}
byte data[] = packet.getPacket().getData();
Arrays.fill(data, 0, data.length, (byte)0x0);
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
// header
data[off] = SESSION_REQUEST_FLAG_BYTE;
off++;
long now = _context.clock().now() / 1000;
DataHelper.toLong(data, off, 4, now);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending request with time = " + new Date(now*1000));
off += 4;
_log.debug("Sending request");
// now for the body
System.arraycopy(state.getSentX(), 0, data, off, state.getSentX().length);
@ -542,7 +584,10 @@ public class PacketBuilder {
* @return ready to send packets, or null if there was a problem
*/
public UDPPacket buildSessionConfirmedPacket(OutboundEstablishState state, int fragmentNum, int numFragments, byte identity[]) {
UDPPacket packet = UDPPacket.acquire(_context, false);
UDPPacket packet = buildPacketHeader(SESSION_CONFIRMED_FLAG_BYTE);
byte data[] = packet.getPacket().getData();
int off = HEADER_SIZE;
InetAddress to = null;
try {
to = InetAddress.getByAddress(state.getSentIP());
@ -553,17 +598,6 @@ public class PacketBuilder {
return null;
}
byte data[] = packet.getPacket().getData();
Arrays.fill(data, 0, data.length, (byte)0x0);
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
// header
data[off] = SESSION_CONFIRMED_FLAG_BYTE;
off++;
long now = _context.clock().now() / 1000;
DataHelper.toLong(data, off, 4, now);
off += 4;
// now for the body
data[off] |= fragmentNum << 4;
data[off] |= (numFragments & 0xF);
@ -620,6 +654,36 @@ public class PacketBuilder {
}
/**
* Build a destroy packet, which contains a header but no body.
*
* @since 0.8.1
*/
public UDPPacket buildSessionDestroyPacket(PeerState peer) {
UDPPacket packet = buildPacketHeader((byte)(UDPPacket.PAYLOAD_TYPE_SESSION_DESTROY << 4));
byte data[] = packet.getPacket().getData();
int off = HEADER_SIZE;
StringBuilder msg = null;
if (_log.shouldLog(Log.DEBUG)) {
msg = new StringBuilder(128);
msg.append("building session destroy packet to ").append(peer.getRemotePeer().toBase64().substring(0,6));
}
// no body in this message
if (msg != null)
_log.debug(msg.toString());
// pad up so we're on the encryption boundary
if ( (off % 16) != 0)
off += 16 - (off % 16);
packet.getPacket().setLength(off);
authenticate(packet, peer.getCurrentCipherKey(), peer.getCurrentMACKey());
setTo(packet, peer.getRemoteIPAddress(), peer.getRemotePort());
return packet;
}
/**
* full flag info for a peerTest message. this can be fixed,
* since we never rekey on test, and don't need any extended options
@ -636,19 +700,11 @@ public class PacketBuilder {
return buildPeerTestFromAlice(toIP, toPort, toIntroKey, toIntroKey, nonce, aliceIntroKey);
}
public UDPPacket buildPeerTestFromAlice(InetAddress toIP, int toPort, SessionKey toCipherKey, SessionKey toMACKey, long nonce, SessionKey aliceIntroKey) {
UDPPacket packet = UDPPacket.acquire(_context, false);
UDPPacket packet = buildPacketHeader(PEER_TEST_FLAG_BYTE);
byte data[] = packet.getPacket().getData();
Arrays.fill(data, 0, data.length, (byte)0x0);
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
// header
data[off] = PEER_TEST_FLAG_BYTE;
off++;
long now = _context.clock().now() / 1000;
DataHelper.toLong(data, off, 4, now);
int off = HEADER_SIZE;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending peer test " + nonce + " to Bob with time = " + new Date(now*1000));
off += 4;
_log.debug("Sending peer test " + nonce + " to Bob");
// now for the body
DataHelper.toLong(data, off, 4, nonce);
@ -678,19 +734,11 @@ public class PacketBuilder {
* @return ready to send packet, or null if there was a problem
*/
public UDPPacket buildPeerTestToAlice(InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, SessionKey charlieIntroKey, long nonce) {
UDPPacket packet = UDPPacket.acquire(_context, false);
UDPPacket packet = buildPacketHeader(PEER_TEST_FLAG_BYTE);
byte data[] = packet.getPacket().getData();
Arrays.fill(data, 0, data.length, (byte)0x0);
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
// header
data[off] = PEER_TEST_FLAG_BYTE;
off++;
long now = _context.clock().now() / 1000;
DataHelper.toLong(data, off, 4, now);
int off = HEADER_SIZE;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending peer test " + nonce + " to Alice with time = " + new Date(now*1000));
off += 4;
_log.debug("Sending peer test " + nonce + " to Alice");
// now for the body
DataHelper.toLong(data, off, 4, nonce);
@ -725,19 +773,11 @@ public class PacketBuilder {
public UDPPacket buildPeerTestToCharlie(InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, long nonce,
InetAddress charlieIP, int charliePort,
SessionKey charlieCipherKey, SessionKey charlieMACKey) {
UDPPacket packet = UDPPacket.acquire(_context, false);
UDPPacket packet = buildPacketHeader(PEER_TEST_FLAG_BYTE);
byte data[] = packet.getPacket().getData();
Arrays.fill(data, 0, data.length, (byte)0x0);
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
// header
data[off] = PEER_TEST_FLAG_BYTE;
off++;
long now = _context.clock().now() / 1000;
DataHelper.toLong(data, off, 4, now);
int off = HEADER_SIZE;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending peer test " + nonce + " to Charlie with time = " + new Date(now*1000));
off += 4;
_log.debug("Sending peer test " + nonce + " to Charlie");
// now for the body
DataHelper.toLong(data, off, 4, nonce);
@ -770,19 +810,11 @@ public class PacketBuilder {
* @return ready to send packet, or null if there was a problem
*/
public UDPPacket buildPeerTestToBob(InetAddress bobIP, int bobPort, InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, long nonce, SessionKey bobCipherKey, SessionKey bobMACKey) {
UDPPacket packet = UDPPacket.acquire(_context, false);
UDPPacket packet = buildPacketHeader(PEER_TEST_FLAG_BYTE);
byte data[] = packet.getPacket().getData();
Arrays.fill(data, 0, data.length, (byte)0x0);
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
// header
data[off] = PEER_TEST_FLAG_BYTE;
off++;
long now = _context.clock().now() / 1000;
DataHelper.toLong(data, off, 4, now);
int off = HEADER_SIZE;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending peer test " + nonce + " to Bob with time = " + new Date(now*1000));
off += 4;
_log.debug("Sending peer test " + nonce + " to Bob");
// now for the body
DataHelper.toLong(data, off, 4, nonce);
@ -846,22 +878,15 @@ public class PacketBuilder {
}
public UDPPacket buildRelayRequest(InetAddress introHost, int introPort, byte introKey[], long introTag, SessionKey ourIntroKey, long introNonce, boolean encrypt) {
UDPPacket packet = UDPPacket.acquire(_context, false);
UDPPacket packet = buildPacketHeader(PEER_RELAY_REQUEST_FLAG_BYTE);
byte data[] = packet.getPacket().getData();
Arrays.fill(data, 0, data.length, (byte)0x0);
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
int off = HEADER_SIZE;
byte ourIP[] = getOurExplicitIP();
int ourPort = getOurExplicitPort();
// header
data[off] = PEER_RELAY_REQUEST_FLAG_BYTE;
off++;
long now = _context.clock().now() / 1000;
DataHelper.toLong(data, off, 4, now);
if (_log.shouldLog(Log.INFO))
_log.info("Sending intro relay request to " + introHost + ":" + introPort); // + " regarding " + state.getRemoteIdentity().calculateHash().toBase64());
off += 4;
// now for the body
DataHelper.toLong(data, off, 4, introTag);
@ -915,19 +940,11 @@ public class PacketBuilder {
private static final byte PEER_RELAY_INTRO_FLAG_BYTE = (UDPPacket.PAYLOAD_TYPE_RELAY_INTRO << 4);
UDPPacket buildRelayIntro(RemoteHostId alice, PeerState charlie, UDPPacketReader.RelayRequestReader request) {
UDPPacket packet = UDPPacket.acquire(_context, false);
UDPPacket packet = buildPacketHeader(PEER_RELAY_INTRO_FLAG_BYTE);
byte data[] = packet.getPacket().getData();
Arrays.fill(data, 0, data.length, (byte)0x0);
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
// header
data[off] = PEER_RELAY_INTRO_FLAG_BYTE;
off++;
long now = _context.clock().now() / 1000;
DataHelper.toLong(data, off, 4, now);
int off = HEADER_SIZE;
if (_log.shouldLog(Log.INFO))
_log.info("Sending intro to " + charlie + " for " + alice);
off += 4;
// now for the body
byte ip[] = alice.getIP();
@ -972,17 +989,9 @@ public class PacketBuilder {
return null;
}
UDPPacket packet = UDPPacket.acquire(_context, false);
UDPPacket packet = buildPacketHeader(PEER_RELAY_RESPONSE_FLAG_BYTE);
byte data[] = packet.getPacket().getData();
Arrays.fill(data, 0, data.length, (byte)0x0);
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
// header
data[off] = PEER_RELAY_RESPONSE_FLAG_BYTE;
off++;
long now = _context.clock().now() / 1000;
DataHelper.toLong(data, off, 4, now);
off += 4;
int off = HEADER_SIZE;
if (_log.shouldLog(Log.INFO))
_log.info("Sending relay response to " + alice + " for " + charlie + " with alice's intro key " + aliceIntroKey.toBase64());
@ -1019,11 +1028,13 @@ public class PacketBuilder {
return packet;
}
/**
* Sends an empty unauthenticated packet for hole punching
*/
public UDPPacket buildHolePunch(UDPPacketReader reader) {
UDPPacket packet = UDPPacket.acquire(_context, false);
byte data[] = packet.getPacket().getData();
Arrays.fill(data, 0, data.length, (byte)0x0);
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
int ipSize = reader.getRelayIntroReader().readIPSize();
byte ip[] = new byte[ipSize];
@ -1052,7 +1063,34 @@ public class PacketBuilder {
return packet;
}
private void setTo(UDPPacket packet, InetAddress ip, int port) {
/** if no extended options or rekey data, which we don't support */
private static final int HEADER_SIZE = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE + 1 + 4;
/**
* Create a new packet and add the flag byte and the time stamp.
* Caller should add data starting at HEADER_SIZE.
* At this point, adding support for extended options and rekeying is unlikely,
* but if we do, we'll have to change this.
*
* @param flagByte contains type and flags
* @since 0.8.1
*/
private UDPPacket buildPacketHeader(byte flagByte) {
UDPPacket packet = UDPPacket.acquire(_context, false);
byte data[] = packet.getPacket().getData();
Arrays.fill(data, 0, data.length, (byte)0x0);
int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
// header
data[off] = flagByte;
off++;
long now = _context.clock().now() / 1000;
DataHelper.toLong(data, off, 4, now);
// todo: add support for rekeying and extended options
return packet;
}
private static void setTo(UDPPacket packet, InetAddress ip, int port) {
packet.getPacket().setAddress(ip);
packet.getPacket().setPort(port);
}

View File

@ -19,7 +19,7 @@ import net.i2p.util.Log;
* receiver's queue and pushing them as necessary.
*
*/
public class PacketHandler {
class PacketHandler {
private RouterContext _context;
private Log _log;
private UDPTransport _transport;
@ -186,6 +186,10 @@ public class PacketHandler {
}
//}
/**
* Initial handling, called for every packet
* Find the state and call the correct receivePacket() variant
*/
private void handlePacket(UDPPacketReader reader, UDPPacket packet) {
if (packet == null) return;
@ -229,6 +233,10 @@ public class PacketHandler {
}
}
/**
* Established conn
* Decrypt and validate the packet then call handlePacket()
*/
private void receivePacket(UDPPacketReader reader, UDPPacket packet, PeerState state) {
_state = 17;
boolean isValid = packet.validate(state.getCurrentMACKey());
@ -280,6 +288,12 @@ public class PacketHandler {
_state = 26;
}
/**
* New conn or failed validation
* Decrypt and validate the packet then call handlePacket()
*
* @param peerType OUTBOUND_FALLBACK, INBOUND_FALLBACK, or NEW_PEER
*/
private void receivePacket(UDPPacketReader reader, UDPPacket packet, short peerType) {
_state = 27;
boolean isValid = packet.validate(_transport.getIntroKey());
@ -314,7 +328,11 @@ public class PacketHandler {
private void receivePacket(UDPPacketReader reader, UDPPacket packet, InboundEstablishState state) {
receivePacket(reader, packet, state, true);
}
/**
* Inbound establishing conn
* Decrypt and validate the packet then call handlePacket()
*
* @param allowFallback if it isn't valid for this establishment state, try as a non-establishment packet
*/
private void receivePacket(UDPPacketReader reader, UDPPacket packet, InboundEstablishState state, boolean allowFallback) {
@ -355,6 +373,10 @@ public class PacketHandler {
}
}
/**
* Outbound establishing conn
* Decrypt and validate the packet then call handlePacket()
*/
private void receivePacket(UDPPacketReader reader, UDPPacket packet, OutboundEstablishState state) {
_state = 35;
if ( (state != null) && (_log.shouldLog(Log.DEBUG)) ) {
@ -406,6 +428,10 @@ public class PacketHandler {
/**
* Parse out the interesting bits and honor what it says
*
* @param state non-null if fully established
* @param outState non-null if outbound establishing in process
* @param inState unused always null
*/
private void handlePacket(UDPPacketReader reader, UDPPacket packet, PeerState state, OutboundEstablishState outState, InboundEstablishState inState) {
_state = 43;
@ -525,6 +551,15 @@ public class PacketHandler {
_establisher.receiveRelayResponse(from, reader);
_context.statManager().addRateData("udp.receivePacketSize.relayResponse", packet.getPacket().getLength(), packet.getLifetime());
break;
case UDPPacket.PAYLOAD_TYPE_SESSION_DESTROY:
_state = 53;
if (outState != null)
_establisher.receiveSessionDestroy(from, outState);
else if (state != null)
_establisher.receiveSessionDestroy(from, state);
else
_establisher.receiveSessionDestroy(from);
break;
default:
_state = 52;
if (_log.shouldLog(Log.WARN))

View File

@ -9,7 +9,7 @@ import net.i2p.util.Log;
* pool and toss 'em onto the outbound packet queue
*
*/
public class PacketPusher implements Runnable {
class PacketPusher implements Runnable {
// private RouterContext _context;
private Log _log;
private OutboundMessageFragments _fragments;

View File

@ -23,7 +23,7 @@ import net.i2p.util.ConcurrentHashSet;
* Contain all of the state about a UDP connection to a peer.
*
*/
public class PeerState {
class PeerState {
private RouterContext _context;
private Log _log;
/**

View File

@ -20,7 +20,7 @@ import net.i2p.util.Log;
* See comments in DQAT.java and mtn history ca. 2006-02-19
*
*/
public class TimedWeightedPriorityMessageQueue implements MessageQueue, OutboundMessageFragments.ActiveThrottle {
class TimedWeightedPriorityMessageQueue implements MessageQueue, OutboundMessageFragments.ActiveThrottle {
private RouterContext _context;
private Log _log;
/** FIFO queue of messages in a particular priority */

View File

@ -11,7 +11,7 @@ import net.i2p.util.Log;
* Coordinate the low level datagram socket, managing the UDPSender and
* UDPReceiver
*/
public class UDPEndpoint {
class UDPEndpoint {
private RouterContext _context;
private Log _log;
private int _listenPort;

View File

@ -16,7 +16,7 @@ import net.i2p.util.Log;
* of object instances to allow rapid reuse.
*
*/
public class UDPPacket {
class UDPPacket {
private I2PAppContext _context;
private static Log _log;
private volatile DatagramPacket _packet;
@ -55,6 +55,7 @@ public class UDPPacket {
public static final int IV_SIZE = 16;
public static final int MAC_SIZE = 16;
/** Message types, 4 bits max */
public static final int PAYLOAD_TYPE_SESSION_REQUEST = 0;
public static final int PAYLOAD_TYPE_SESSION_CREATED = 1;
public static final int PAYLOAD_TYPE_SESSION_CONFIRMED = 2;
@ -63,13 +64,17 @@ public class UDPPacket {
public static final int PAYLOAD_TYPE_RELAY_INTRO = 5;
public static final int PAYLOAD_TYPE_DATA = 6;
public static final int PAYLOAD_TYPE_TEST = 7;
/** @since 0.8.1 */
public static final int PAYLOAD_TYPE_SESSION_DESTROY = 8;
// various flag fields for use in the data packets
public static final byte DATA_FLAG_EXPLICIT_ACK = (byte)(1 << 7);
public static final byte DATA_FLAG_ACK_BITFIELDS = (1 << 6);
// unused
public static final byte DATA_FLAG_ECN = (1 << 4);
public static final byte DATA_FLAG_WANT_ACKS = (1 << 3);
public static final byte DATA_FLAG_WANT_REPLY = (1 << 2);
// unused
public static final byte DATA_FLAG_EXTENDED = (1 << 1);
public static final byte BITFIELD_CONTINUATION = (byte)(1 << 7);

View File

@ -15,7 +15,7 @@ import net.i2p.util.Log;
* elements, grab the appropriate subreader.
*
*/
public class UDPPacketReader {
class UDPPacketReader {
private I2PAppContext _context;
private Log _log;
private byte _message[];
@ -125,6 +125,8 @@ public class UDPPacketReader {
return "Relay request packet";
case UDPPacket.PAYLOAD_TYPE_RELAY_RESPONSE:
return "Relay response packet";
case UDPPacket.PAYLOAD_TYPE_SESSION_DESTROY:
return "Session destroyed packet";
default:
return "Other packet type...";
}
@ -135,6 +137,8 @@ public class UDPPacketReader {
buf.append(Base64.encode(_message, _payloadBeginOffset, _payloadLength));
}
/* ------- Begin Reader Classes ------- */
/** Help read the SessionRequest payload */
public class SessionRequestReader {
public static final int X_LENGTH = 256;
@ -755,7 +759,9 @@ public class UDPPacketReader {
}
}
/* ------- End Reader Classes ------- */
/******
public static void main(String args[]) {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
try {
@ -781,4 +787,5 @@ public class UDPPacketReader {
}
}
*******/
}

View File

@ -19,7 +19,7 @@ import net.i2p.util.SimpleTimer;
* from the queue ASAP by a {@link PacketHandler}
*
*/
public class UDPReceiver {
class UDPReceiver {
private RouterContext _context;
private Log _log;
private DatagramSocket _socket;

View File

@ -15,7 +15,7 @@ import net.i2p.util.Log;
* Lowest level packet sender, pushes anything on its queue ASAP.
*
*/
public class UDPSender {
class UDPSender {
private RouterContext _context;
private Log _log;
private DatagramSocket _socket;

View File

@ -838,7 +838,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
if (state != null)
dropPeer(state, shouldShitlist, why);
}
private void dropPeer(PeerState peer, boolean shouldShitlist, String why) {
void dropPeer(PeerState peer, boolean shouldShitlist, String why) {
if (_log.shouldLog(Log.WARN)) {
long now = _context.clock().now();
StringBuilder buf = new StringBuilder(4096);
@ -945,7 +946,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
// try to shift 'em around every 10 minutes or so
if (_introducersSelectedOn < _context.clock().now() - 10*60*1000) {
if (_log.shouldLog(Log.WARN))
_log.warn("Our introducers are valid, but thy havent changed in a while, so lets rechoose");
_log.warn("Our introducers are valid, but havent changed in a while, so lets rechoose");
return true;
} else {
if (_log.shouldLog(Log.INFO))
@ -954,7 +955,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
}
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Our introducers are not valid (" +valid + ")");
_log.info("Need more introducers (have " +valid + " need " + PUBLIC_RELAY_COUNT + ')');
return true;
}
} else {
@ -1017,7 +1018,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
/** minimum active peers to maintain IP detection, etc. */
private static final int MIN_PEERS = 3;
/** minimum peers volunteering to be introducers if we need that */
private static final int MIN_INTRODUCER_POOL = 4;
private static final int MIN_INTRODUCER_POOL = 5;
public TransportBid bid(RouterInfo toAddress, long dataSize) {
if (dataSize > OutboundMessageState.MAX_MSG_SIZE) {
@ -1233,7 +1234,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_introducersSelectedOn = _context.clock().now();
introducersIncluded = true;
} else {
// FIXME
// maybe we should fail to publish an address at all in this case?
// YES that would be better
if (_log.shouldLog(Log.WARN))
_log.warn("Need introducers but we don't know any");
}
@ -1331,13 +1334,13 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
switch (status) {
case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
case CommSystemFacade.STATUS_DIFFERENT:
if (_log.shouldLog(Log.INFO))
_log.info("Require introducers, because our status is " + status);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Require introducers, because our status is " + status);
return true;
default:
if (!allowDirectUDP()) {
if (_log.shouldLog(Log.INFO))
_log.info("Require introducers, because we do not allow direct UDP connections");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Require introducers, because we do not allow direct UDP connections");
return true;
}
return false;

View File

@ -24,7 +24,7 @@ public class BuildMessageProcessor {
public BuildMessageProcessor(I2PAppContext ctx) {
_filter = new DecayingHashSet(ctx, 60*1000, 32, "TunnelBMP");
ctx.statManager().createRateStat("tunnel.buildRequestDup", "How frequently we get dup build request messages", "Tunnels", new long[] { 60*1000, 10*60*1000 });
ctx.statManager().createRateStat("tunnel.buildRequestDup", "How frequently we get dup build request messages", "Tunnels", new long[] { 60*60*1000 });
}
/**
* Decrypt the record targetting us, encrypting all of the other records with the included

View File

@ -15,17 +15,19 @@ import net.i2p.util.Log;
*
*/
public class InboundEndpointProcessor {
private RouterContext _context;
private Log _log;
private TunnelCreatorConfig _config;
private IVValidator _validator;
private final RouterContext _context;
private final Log _log;
private final TunnelCreatorConfig _config;
private final IVValidator _validator;
static final boolean USE_ENCRYPTION = HopProcessor.USE_ENCRYPTION;
private static final ByteCache _cache = ByteCache.getInstance(128, HopProcessor.IV_LENGTH);
/** @deprecated unused */
public InboundEndpointProcessor(RouterContext ctx, TunnelCreatorConfig cfg) {
this(ctx, cfg, DummyValidator.getInstance());
}
public InboundEndpointProcessor(RouterContext ctx, TunnelCreatorConfig cfg, IVValidator validator) {
_context = ctx;
_log = ctx.logManager().getLog(InboundEndpointProcessor.class);
@ -84,6 +86,9 @@ public class InboundEndpointProcessor {
return true;
}
/**
* Iteratively undo the crypto that the various layers in the tunnel added.
*/
private void decrypt(RouterContext ctx, TunnelCreatorConfig cfg, byte iv[], byte orig[], int offset, int length) {
Log log = ctx.logManager().getLog(OutboundGatewayProcessor.class);
ByteArray ba = _cache.acquire();

View File

@ -14,9 +14,9 @@ import net.i2p.util.Log;
*
*/
public class OutboundGatewayProcessor {
private I2PAppContext _context;
private Log _log;
private TunnelCreatorConfig _config;
private final I2PAppContext _context;
private final Log _log;
private final TunnelCreatorConfig _config;
static final boolean USE_ENCRYPTION = HopProcessor.USE_ENCRYPTION;
private static final ByteCache _cache = ByteCache.getInstance(128, HopProcessor.IV_LENGTH);
@ -54,10 +54,8 @@ public class OutboundGatewayProcessor {
}
/**
* Undo the crypto that the various layers in the tunnel added. This is used
* by both the outbound gateway (preemptively undoing the crypto peers will add)
* and by the inbound endpoint.
*
* Iteratively undo the crypto that the various layers in the tunnel added. This is used
* by the outbound gateway (preemptively undoing the crypto peers will add).
*/
private void decrypt(I2PAppContext ctx, TunnelCreatorConfig cfg, byte iv[], byte orig[], int offset, int length) {
Log log = ctx.logManager().getLog(OutboundGatewayProcessor.class);
@ -73,6 +71,11 @@ public class OutboundGatewayProcessor {
_cache.release(ba);
}
/**
* Undo the crypto for a single hop. This is used
* by both the outbound gateway (preemptively undoing the crypto peers will add)
* and by the inbound endpoint.
*/
static void decrypt(I2PAppContext ctx, byte iv[], byte orig[], int offset, int length, byte cur[], HopConfig config) {
// update the IV for the previous (next?) hop
ctx.aes().decryptBlock(orig, offset, config.getIVKey(), orig, offset);

View File

@ -60,7 +60,6 @@ public class TunnelGateway {
_preprocessor = preprocessor;
_sender = sender;
_receiver = receiver;
_messagesSent = 0;
_flushFrequency = 500;
_delayedFlush = new DelayedFlush();
_lastFlush = _context.clock().now();
@ -128,8 +127,8 @@ public class TunnelGateway {
FlushTimer.getInstance().addEvent(_delayedFlush, delayAmount);
}
_context.statManager().addRateData("tunnel.lockedGatewayAdd", afterAdded-beforeLock, remaining);
long complete = System.currentTimeMillis();
if (_log.shouldLog(Log.DEBUG))
if (_log.shouldLog(Log.DEBUG)) {
long complete = System.currentTimeMillis();
_log.debug("Time to add the message " + msg.getUniqueId() + ": " + (complete-startAdd)
+ " delayed? " + delayedFlush + " remaining: " + remaining
+ " prepare: " + (beforeLock-startAdd)
@ -137,6 +136,7 @@ public class TunnelGateway {
+ " preprocess: " + (afterPreprocess-afterAdded)
+ " expire: " + (afterExpire-afterPreprocess)
+ " queue flush: " + (complete-afterExpire));
}
}
public int getMessagesSent() { return _messagesSent; }
@ -202,10 +202,7 @@ public class TunnelGateway {
_messageId = message.getUniqueId();
_expiration = message.getMessageExpiration();
_remaining = message.toByteArray();
_offset = 0;
_fragmentNumber = 0;
_created = now;
_messageIds = null;
}
/** may be null */
public Hash getToRouter() { return _toRouter; }

View File

@ -5,6 +5,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.i2p.data.Hash;
import net.i2p.router.RouterContext;
import net.i2p.router.TunnelPoolSettings;
@ -14,7 +15,7 @@ import net.i2p.router.TunnelPoolSettings;
*
*/
class ClientPeerSelector extends TunnelPeerSelector {
public List selectPeers(RouterContext ctx, TunnelPoolSettings settings) {
public List<Hash> selectPeers(RouterContext ctx, TunnelPoolSettings settings) {
int length = getLength(ctx, settings);
if (length < 0)
return null;
@ -31,7 +32,7 @@ class ClientPeerSelector extends TunnelPeerSelector {
ctx.profileOrganizer().selectFastPeers(length, exclude, matches, settings.getIPRestriction());
matches.remove(ctx.routerHash());
ArrayList rv = new ArrayList(matches);
ArrayList<Hash> rv = new ArrayList(matches);
if (rv.size() > 1)
orderPeers(rv, settings.getRandomKey());
if (settings.isInbound())

View File

@ -5,6 +5,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.i2p.data.Hash;
import net.i2p.router.RouterContext;
import net.i2p.router.TunnelPoolSettings;
import net.i2p.stat.Rate;
@ -17,7 +18,7 @@ import net.i2p.util.Log;
*
*/
class ExploratoryPeerSelector extends TunnelPeerSelector {
public List selectPeers(RouterContext ctx, TunnelPoolSettings settings) {
public List<Hash> selectPeers(RouterContext ctx, TunnelPoolSettings settings) {
Log l = ctx.logManager().getLog(getClass());
int length = getLength(ctx, settings);
if (length < 0) {
@ -33,7 +34,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
return rv;
}
Set exclude = getExclude(ctx, settings.isInbound(), settings.isExploratory());
Set<Hash> exclude = getExclude(ctx, settings.isInbound(), settings.isExploratory());
exclude.add(ctx.routerHash());
// Don't use ff peers for exploratory tunnels to lessen exposure to netDb searches and stores
// Hmm if they don't get explored they don't get a speed/capacity rating
@ -56,7 +57,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
l.debug("profileOrganizer.selectNotFailing(" + length + ") found " + matches);
matches.remove(ctx.routerHash());
ArrayList rv = new ArrayList(matches);
ArrayList<Hash> rv = new ArrayList(matches);
if (rv.size() > 1)
orderPeers(rv, settings.getRandomKey());
if (settings.isInbound())
@ -67,7 +68,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
}
private static final int MIN_NONFAILING_PCT = 25;
private boolean shouldPickHighCap(RouterContext ctx) {
private static boolean shouldPickHighCap(RouterContext ctx) {
if (Boolean.valueOf(ctx.getProperty("router.exploreHighCapacity", "false")).booleanValue())
return true;
// no need to explore too wildly at first
@ -86,9 +87,9 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
failPct = 100 - MIN_NONFAILING_PCT;
} else {
failPct = getExploratoryFailPercentage(ctx);
Log l = ctx.logManager().getLog(getClass());
if (l.shouldLog(Log.DEBUG))
l.debug("Normalized Fail pct: " + failPct);
//Log l = ctx.logManager().getLog(getClass());
//if (l.shouldLog(Log.DEBUG))
// l.debug("Normalized Fail pct: " + failPct);
// always try a little, this helps keep the failPct stat accurate too
if (failPct > 100 - MIN_NONFAILING_PCT)
failPct = 100 - MIN_NONFAILING_PCT;
@ -96,21 +97,23 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
return (failPct >= ctx.random().nextInt(100));
}
// We should really use the difference between the exploratory fail rate
// and the high capacity fail rate - but we don't have a stat for high cap,
// so use the fast (== client) fail rate, it should be close
// if the expl. and client tunnel lengths aren't too different.
// So calculate the difference between the exploratory fail rate
// and the client fail rate, normalized to 100:
// 100 * ((Efail - Cfail) / (100 - Cfail))
// Even this isn't the "true" rate for the NonFailingPeers pool, since we
// are often building exploratory tunnels using the HighCapacity pool.
private int getExploratoryFailPercentage(RouterContext ctx) {
/**
* We should really use the difference between the exploratory fail rate
* and the high capacity fail rate - but we don't have a stat for high cap,
* so use the fast (== client) fail rate, it should be close
* if the expl. and client tunnel lengths aren't too different.
* So calculate the difference between the exploratory fail rate
* and the client fail rate, normalized to 100:
* 100 * ((Efail - Cfail) / (100 - Cfail))
* Even this isn't the "true" rate for the NonFailingPeers pool, since we
* are often building exploratory tunnels using the HighCapacity pool.
*/
private static int getExploratoryFailPercentage(RouterContext ctx) {
int c = getFailPercentage(ctx, "Client");
int e = getFailPercentage(ctx, "Exploratory");
Log l = ctx.logManager().getLog(getClass());
if (l.shouldLog(Log.DEBUG))
l.debug("Client, Expl. Fail pct: " + c + ", " + e);
//Log l = ctx.logManager().getLog(getClass());
//if (l.shouldLog(Log.DEBUG))
// l.debug("Client, Expl. Fail pct: " + c + ", " + e);
if (e <= c || e <= 25) // doing very well (unlikely)
return 0;
if (c >= 90) // doing very badly
@ -118,7 +121,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
return (100 * (e-c)) / (100-c);
}
private int getFailPercentage(RouterContext ctx, String t) {
private static int getFailPercentage(RouterContext ctx, String t) {
String pfx = "tunnel.build" + t;
int timeout = getEvents(ctx, pfx + "Expire", 10*60*1000);
int reject = getEvents(ctx, pfx + "Reject", 10*60*1000);
@ -129,8 +132,8 @@ class ExploratoryPeerSelector extends TunnelPeerSelector {
return (int)(100 * pct);
}
// Use current + last to get more recent and smoother data
private int getEvents(RouterContext ctx, String stat, long period) {
/** Use current + last to get more recent and smoother data */
private static int getEvents(RouterContext ctx, String stat, long period) {
RateStat rs = ctx.statManager().getRate(stat);
if (rs == null)
return 0;

View File

@ -12,7 +12,7 @@ import net.i2p.util.Log;
/**
*
*/
public class PooledTunnelCreatorConfig extends TunnelCreatorConfig {
class PooledTunnelCreatorConfig extends TunnelCreatorConfig {
private TunnelPool _pool;
private TestJob _testJob;
// private Job _expireJob;

View File

@ -21,10 +21,13 @@ import net.i2p.router.TunnelPoolSettings;
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
import net.i2p.router.networkdb.kademlia.HashDistance;
import net.i2p.util.Log;
import net.i2p.util.VersionComparator;
/**
* Coordinate the selection of peers to go into a tunnel for one particular
* pool.
*
* Todo: there's nothing non-static in here
*/
public abstract class TunnelPeerSelector {
/**
@ -36,7 +39,7 @@ public abstract class TunnelPeerSelector {
* to build through, and the settings reject 0 hop tunnels, this will
* return null.
*/
public abstract List selectPeers(RouterContext ctx, TunnelPoolSettings settings);
public abstract List<Hash> selectPeers(RouterContext ctx, TunnelPoolSettings settings);
protected int getLength(RouterContext ctx, TunnelPoolSettings settings) {
int length = settings.getLength();
@ -79,6 +82,11 @@ public abstract class TunnelPeerSelector {
return length;
}
/**
* For debugging, also possibly for restricted routes?
* Needs analysis and testing
* @return should always be false
*/
protected boolean shouldSelectExplicit(TunnelPoolSettings settings) {
if (settings.isExploratory()) return false;
Properties opts = settings.getUnknownOptions();
@ -92,7 +100,12 @@ public abstract class TunnelPeerSelector {
return false;
}
protected List selectExplicit(RouterContext ctx, TunnelPoolSettings settings, int length) {
/**
* For debugging, also possibly for restricted routes?
* Needs analysis and testing
* @return should always be false
*/
protected List<Hash> selectExplicit(RouterContext ctx, TunnelPoolSettings settings, int length) {
String peers = null;
Properties opts = settings.getUnknownOptions();
if (opts != null)
@ -102,7 +115,7 @@ public abstract class TunnelPeerSelector {
peers = I2PAppContext.getGlobalContext().getProperty("explicitPeers");
Log log = ctx.logManager().getLog(ClientPeerSelector.class);
List rv = new ArrayList();
List<Hash> rv = new ArrayList();
StringTokenizer tok = new StringTokenizer(peers, ",");
while (tok.hasMoreTokens()) {
String peerStr = tok.nextToken();
@ -156,7 +169,7 @@ public abstract class TunnelPeerSelector {
/**
* Pick peers that we want to avoid
*/
public Set getExclude(RouterContext ctx, boolean isInbound, boolean isExploratory) {
public Set<Hash> getExclude(RouterContext ctx, boolean isInbound, boolean isExploratory) {
// we may want to update this to skip 'hidden' or 'unreachable' peers, but that
// isn't safe, since they may publish one set of routerInfo to us and another to
// other peers. the defaults for filterUnreachable has always been to return false,
@ -175,11 +188,12 @@ public abstract class TunnelPeerSelector {
//
// Defaults changed to true for inbound only in filterUnreachable below.
Set peers = new HashSet(1);
Set<Hash> peers = new HashSet(1);
peers.addAll(ctx.profileOrganizer().selectPeersRecentlyRejecting());
peers.addAll(ctx.tunnelManager().selectPeersInTooManyTunnels());
// if (false && filterUnreachable(ctx, isInbound, isExploratory)) {
if (filterUnreachable(ctx, isInbound, isExploratory)) {
// NOTE: filterUnreachable returns true for inbound, false for outbound
// This is the only use for getPeersByCapability? And the whole set of datastructures in PeerManager?
List<Hash> caps = ctx.peerManager().getPeersByCapability(Router.CAPABILITY_UNREACHABLE);
if (caps != null)
@ -189,6 +203,7 @@ public abstract class TunnelPeerSelector {
peers.addAll(caps);
}
if (filterSlow(ctx, isInbound, isExploratory)) {
// NOTE: filterSlow always returns true
Log log = ctx.logManager().getLog(TunnelPeerSelector.class);
char excl[] = getExcludeCaps(ctx);
if (excl != null) {
@ -301,6 +316,7 @@ public abstract class TunnelPeerSelector {
return peers;
}
/** warning, this is also called by ProfileOrganizer.isSelectable() */
public static boolean shouldExclude(RouterContext ctx, RouterInfo peer) {
Log log = ctx.logManager().getLog(TunnelPeerSelector.class);
return shouldExclude(ctx, log, peer, getExcludeCaps(ctx));
@ -318,6 +334,10 @@ public abstract class TunnelPeerSelector {
}
private static final long DONT_EXCLUDE_PERIOD = 15*60*1000;
/** 0.7.8 and earlier had major message corruption bugs */
private static final String MIN_VERSION = "0.7.9";
private static final VersionComparator _versionComparator = new VersionComparator();
private static boolean shouldExclude(RouterContext ctx, Log log, RouterInfo peer, char excl[]) {
String cap = peer.getCapabilities();
if (cap == null) {
@ -340,6 +360,13 @@ public abstract class TunnelPeerSelector {
// otherwise, it contains flags we aren't trying to focus on,
// so don't exclude it based on published capacity
// minimum version check
String v = peer.getOption("router.version");
if (v == null || _versionComparator.compare(v, MIN_VERSION) < 0)
return true;
// uptime is always spoofed to 90m, so just remove all this
/******
String val = peer.getOption("stat_uptime");
if (val != null) {
long uptimeMs = 0;
@ -390,6 +417,8 @@ public abstract class TunnelPeerSelector {
// not publishing an uptime, so exclude it
return true;
}
******/
return false;
}
private static final String PROP_OUTBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE = "router.outboundExploratoryExcludeUnreachable";
@ -403,6 +432,10 @@ public abstract class TunnelPeerSelector {
private static final String DEFAULT_INBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE = "true";
private static final String DEFAULT_INBOUND_CLIENT_EXCLUDE_UNREACHABLE = "true";
/**
* do we want to skip peers who haven't been up for long?
* @return true for inbound, false for outbound, unless configured otherwise
*/
protected boolean filterUnreachable(RouterContext ctx, boolean isInbound, boolean isExploratory) {
boolean def = false;
String val = null;
@ -429,6 +462,10 @@ public abstract class TunnelPeerSelector {
private static final String PROP_INBOUND_EXPLORATORY_EXCLUDE_SLOW = "router.inboundExploratoryExcludeSlow";
private static final String PROP_INBOUND_CLIENT_EXCLUDE_SLOW = "router.inboundClientExcludeSlow";
/**
* do we want to skip peers that are slow?
* @return true unless configured otherwise
*/
protected boolean filterSlow(RouterContext ctx, boolean isInbound, boolean isExploratory) {
boolean def = true;
String val = null;
@ -454,7 +491,10 @@ public abstract class TunnelPeerSelector {
private static final String PROP_INBOUND_EXPLORATORY_EXCLUDE_UPTIME = "router.inboundExploratoryExcludeUptime";
private static final String PROP_INBOUND_CLIENT_EXCLUDE_UPTIME = "router.inboundClientExcludeUptime";
/** do we want to skip peers who haven't been up for long? */
/**
* do we want to skip peers who haven't been up for long?
* @return true unless configured otherwise
*/
protected boolean filterUptime(RouterContext ctx, boolean isInbound, boolean isExploratory) {
boolean def = true;
String val = null;