merge of '0738aeef8a1d4e9ca82dc5ba0077d83a57c47f81'
and '9625ea3e96d57df74bc62018bf64230a22c49ce0'
This commit is contained in:
@ -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>
|
||||
*/
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -43,7 +43,6 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade {
|
||||
|
||||
public ClientManagerFacadeImpl(RouterContext context) {
|
||||
_context = context;
|
||||
_manager = null;
|
||||
_log.debug("Client manager facade created");
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,6 @@ class LeaseRequestState {
|
||||
_onFailed = onFailed;
|
||||
_expiration = expiration;
|
||||
_requestedLeaseSet = requested;
|
||||
_successful = false;
|
||||
}
|
||||
|
||||
/** created lease set from client */
|
||||
|
@ -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 {
|
||||
|
@ -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"; }
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>();
|
||||
|
@ -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? */
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
}
|
||||
|
@ -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...
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -22,7 +22,7 @@ import net.i2p.util.Log;
|
||||
* message.
|
||||
*
|
||||
*/
|
||||
public class OutboundMessageFragments {
|
||||
class OutboundMessageFragments {
|
||||
private RouterContext _context;
|
||||
private Log _log;
|
||||
private UDPTransport _transport;
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
/**
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
*******/
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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; }
|
||||
|
@ -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())
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Reference in New Issue
Block a user