2005-10-17 jrandom

* Allow an env prop to configure whether we want to use the backwards
      compatible (but not standards compliant) HMAC-MD5, or whether we want
      to use the not-backwards compatible (but standards compliant) one.  No
      one should touch this setting, unless your name is toad or jrandom ;)
    * Added some new dummy facades
    * Be more aggressive on loading up the router.config before building the
      router context
    * Added new hooks for apps to deal with previously undefined I2NP message
      types without having to modify any code.
    * Demo code for using a castrated router for SSU comm (SSUDemo.java)
This commit is contained in:
jrandom
2005-10-18 00:39:46 +00:00
committed by zzz
parent f9d3b157f0
commit 731e26e7d6
11 changed files with 406 additions and 28 deletions

View File

@ -31,6 +31,14 @@
<ant dir="apps/susidns/src" target="all" />
<ant dir="apps/syndie/java/" target="jar" />
</target>
<target name="buildrouter">
<ant dir="core/java/" target="distclean" />
<ant dir="router/java/" target="distclean" />
<ant dir="core/java/" target="jar" />
<ant dir="router/java/" target="jar" />
<copy file="core/java/build/i2p.jar" todir="build/" />
<copy file="router/java/build/router.jar" todir="build/" />
</target>
<target name="buildWEB">
<ant dir="apps/jetty" target="fetchJettylib" />
<ant dir="apps/routerconsole/java" target="build" />
@ -367,8 +375,8 @@
<copy file="installer/resources/blogMeta.snm" tofile="pkg-temp/syndie/archive/ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=/meta.snm" />
<copy file="installer/resources/blogPost.snd" tofile="pkg-temp/syndie/archive/ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=/1126915200003.snd" />
</target>
<taskdef name="izpack" classpath="${basedir}/installer/lib/izpack/standalone-compiler.jar" classname="com.izforge.izpack.ant.IzPackTask" />
<target name="installer" depends="preppkg">
<taskdef name="izpack" classpath="${basedir}/installer/lib/izpack/standalone-compiler.jar" classname="com.izforge.izpack.ant.IzPackTask" />
<jar destfile="./pkg-temp/lib/copy.jar" basedir="./core/java/build/obj" includes="net/i2p/util/*.class">
<manifest><attribute name="Main-Class" value="net.i2p.util.Copy" /></manifest>
</jar>

View File

@ -28,6 +28,7 @@ public class HMACSHA256Generator {
/** set of available byte[] buffers for verify */
private List _availableTmp;
private boolean _useMD5;
private int _macSize;
public static final boolean DEFAULT_USE_MD5 = true;
@ -39,6 +40,10 @@ public class HMACSHA256Generator {
_useMD5 = true;
else
_useMD5 = false;
if ("true".equals(context.getProperty("i2p.HMACBrokenSize", "true")))
_macSize = 32;
else
_macSize = (_useMD5 ? 16 : 32);
}
public static HMACSHA256Generator getInstance() {

View File

@ -1,4 +1,16 @@
$Id: history.txt,v 1.297 2005/10/14 11:02:38 jrandom Exp $
$Id: history.txt,v 1.298 2005/10/14 11:26:31 jrandom Exp $
2005-10-17 jrandom
* Allow an env prop to configure whether we want to use the backwards
compatible (but not standards compliant) HMAC-MD5, or whether we want
to use the not-backwards compatible (but standards compliant) one. No
one should touch this setting, unless your name is toad or jrandom ;)
* Added some new dummy facades
* Be more aggressive on loading up the router.config before building the
router context
* Added new hooks for apps to deal with previously undefined I2NP message
types without having to modify any code.
* Demo code for using a castrated router for SSU comm (SSUDemo.java)
2005-10-14 jrandom
* More explicit filter for linux/PPC building (thanks anon!)

View File

@ -12,6 +12,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
@ -36,6 +39,15 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
private static final boolean RAW_FULL_SIZE = false;
/** unsynchronized as its pretty much read only (except at startup) */
private static final Map _builders = new HashMap(8);
public static final void registerBuilder(Builder builder, int type) { _builders.put(new Integer(type), builder); }
/** interface for extending the types of messages handled */
public interface Builder {
/** instantiate a new I2NPMessage to be populated shortly */
public I2NPMessage build(I2PAppContext ctx);
}
public I2NPMessageImpl(I2PAppContext context) {
_context = context;
_log = context.logManager().getLog(I2NPMessageImpl.class);
@ -334,7 +346,11 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
case TunnelCreateStatusMessage.MESSAGE_TYPE:
return new TunnelCreateStatusMessage(context);
default:
return null;
Builder builder = (Builder)_builders.get(new Integer(type));
if (builder == null)
return null;
else
return builder.build(context);
}
}
}

View File

@ -440,8 +440,8 @@ public class JobQueue {
timeToWait = 10;
else if (timeToWait > 10*1000)
timeToWait = 10*1000;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Waiting " + timeToWait + " before rechecking the timed queue");
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Waiting " + timeToWait + " before rechecking the timed queue");
try {
_jobLock.wait(timeToWait);
} catch (InterruptedException ie) {}

View File

@ -10,10 +10,7 @@ package net.i2p.router;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.*;
import net.i2p.data.Hash;
import net.i2p.data.LeaseSet;
@ -68,7 +65,7 @@ class DummyNetworkDatabaseFacade extends NetworkDatabaseFacade {
private RouterContext _context;
public DummyNetworkDatabaseFacade(RouterContext ctx) {
_routers = new HashMap();
_routers = Collections.synchronizedMap(new HashMap());
_context = ctx;
}
@ -93,11 +90,13 @@ class DummyNetworkDatabaseFacade extends NetworkDatabaseFacade {
public void publish(RouterInfo localRouterInfo) {}
public LeaseSet store(Hash key, LeaseSet leaseSet) { return leaseSet; }
public RouterInfo store(Hash key, RouterInfo routerInfo) {
_routers.put(key, routerInfo);
return routerInfo;
RouterInfo rv = (RouterInfo)_routers.put(key, routerInfo);
return rv;
}
public void unpublish(LeaseSet localLeaseSet) {}
public void fail(Hash dbEntry) {}
public void fail(Hash dbEntry) {
_routers.remove(dbEntry);
}
public Set findNearestRouters(Hash key, int maxNumRouters, Set peersToIgnore) { return new HashSet(_routers.values()); }

View File

@ -106,11 +106,30 @@ public class Router {
}
_config = new Properties();
_context = new RouterContext(this, envProps);
if (configFilename == null)
_configFilename = _context.getProperty(PROP_CONFIG_FILE, "router.config");
else
if (configFilename == null) {
if (envProps != null) {
_configFilename = envProps.getProperty(PROP_CONFIG_FILE);
}
if (_configFilename == null)
_configFilename = System.getProperty(PROP_CONFIG_FILE, "router.config");
} else {
_configFilename = configFilename;
}
readConfig();
if (envProps == null) {
envProps = _config;
} else {
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
String k = (String)iter.next();
String v = _config.getProperty(k);
envProps.setProperty(k, v);
}
}
_context = new RouterContext(this, envProps);
_routerInfo = null;
_higherVersionSeen = false;
_log = _context.logManager().getLog(Router.class);
@ -248,9 +267,12 @@ public class Router {
}
private static Properties getConfig(RouterContext ctx, String filename) {
Log log = ctx.logManager().getLog(Router.class);
if (log.shouldLog(Log.DEBUG))
log.debug("Config file: " + filename);
Log log = null;
if (ctx != null) {
log = ctx.logManager().getLog(Router.class);
if (log.shouldLog(Log.DEBUG))
log.debug("Config file: " + filename, new Exception("location"));
}
Properties props = new Properties();
FileInputStream fis = null;
try {
@ -260,10 +282,12 @@ public class Router {
// dont be a wanker
props.remove(PROP_SHUTDOWN_IN_PROGRESS);
} else {
log.warn("Configuration file " + filename + " does not exist");
if (log != null)
log.warn("Configuration file " + filename + " does not exist");
}
} catch (Exception ioe) {
log.error("Error loading the router configuration from " + filename, ioe);
if (log != null)
log.error("Error loading the router configuration from " + filename, ioe);
} finally {
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
}

View File

@ -90,7 +90,10 @@ public class RouterContext extends I2PAppContext {
}
private void initAll() {
_adminManager = new AdminManager(this);
_clientManagerFacade = new ClientManagerFacadeImpl(this);
if ("false".equals(getProperty("i2p.dummyClientFacade", "false")))
_clientManagerFacade = new ClientManagerFacadeImpl(this);
else
_clientManagerFacade = new DummyClientManagerFacade(this);
_clientMessagePool = new ClientMessagePool(this);
_jobQueue = new JobQueue(this);
_inNetMessagePool = new InNetMessagePool(this);
@ -98,17 +101,26 @@ public class RouterContext extends I2PAppContext {
_messageHistory = new MessageHistory(this);
_messageRegistry = new OutboundMessageRegistry(this);
_messageStateMonitor = new MessageStateMonitor(this);
_netDb = new FloodfillNetworkDatabaseFacade(this); // new KademliaNetworkDatabaseFacade(this);
if ("false".equals(getProperty("i2p.dummyNetDb", "false")))
_netDb = new FloodfillNetworkDatabaseFacade(this); // new KademliaNetworkDatabaseFacade(this);
else
_netDb = new DummyNetworkDatabaseFacade(this);
_keyManager = new KeyManager(this);
if ("false".equals(getProperty("i2p.vmCommSystem", "false")))
_commSystem = new CommSystemFacadeImpl(this);
else
_commSystem = new VMCommSystem(this);
_profileOrganizer = new ProfileOrganizer(this);
_peerManagerFacade = new PeerManagerFacadeImpl(this);
if ("false".equals(getProperty("i2p.dummyPeerManager", "false")))
_peerManagerFacade = new PeerManagerFacadeImpl(this);
else
_peerManagerFacade = new DummyPeerManagerFacade();
_profileManager = new ProfileManagerImpl(this);
_bandwidthLimiter = new FIFOBandwidthLimiter(this);
_tunnelManager = new TunnelPoolManager(this);
if ("false".equals(getProperty("i2p.dummyTunnelManager", "false")))
_tunnelManager = new TunnelPoolManager(this);
else
_tunnelManager = new DummyTunnelManagerFacade();
_tunnelDispatcher = new TunnelDispatcher(this);
_statPublisher = new StatisticsManager(this);
_shitlist = new Shitlist(this);

View File

@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
*
*/
public class RouterVersion {
public final static String ID = "$Revision: 1.269 $ $Date: 2005/10/13 21:15:40 $";
public final static String ID = "$Revision: 1.270 $ $Date: 2005/10/14 08:48:05 $";
public final static String VERSION = "0.6.1.3";
public final static long BUILD = 0;
public final static long BUILD = 1;
public static void main(String args[]) {
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
System.out.println("Router ID: " + RouterVersion.ID);

View File

@ -0,0 +1,271 @@
package net.i2p.router;
import java.util.*;
import java.io.*;
import net.i2p.I2PAppContext;
import net.i2p.data.*;
import net.i2p.data.i2np.*;
/**
* Demo of a stripped down router - no tunnels, no netDb, no i2cp, no peer profiling,
* just the SSU comm layer, crypto, and associated infrastructure, extended to handle
* a new type of message ("FooMessage").
*
*/
public class SSUDemo {
RouterContext _us;
public static void main(String args[]) {
SSUDemo demo = new SSUDemo();
demo.run();
}
public SSUDemo() {}
public void run() {
String cfgFile = "router.config";
Properties envProps = getEnv();
Router r = new Router(cfgFile, envProps);
r.runRouter();
_us = r.getContext();
setupHandlers();
// wait for it to warm up a bit
try { Thread.sleep(30*1000); } catch (InterruptedException ie) {}
// now write out our ident and info
RouterInfo myInfo = _us.router().getRouterInfo();
storeMyInfo(myInfo);
// look for any other peers written to the same directory, and send each
// a single Foo message (0x0123), unless they've already contacted us first.
// this call never returns
loadPeers();
}
private Properties getEnv() {
Properties envProps = System.getProperties();
// disable the TCP transport, as its deprecated
envProps.setProperty("i2np.tcp.disable", "true");
// we want SNTP synchronization for replay prevention
envProps.setProperty("time.disabled", "false");
// allow 127.0.0.1/10.0.0.1/etc (useful for testing). If this is false,
// peers who say they're on an invalid IP are shitlisted
envProps.setProperty("i2np.udp.allowLocal", "true");
// explicit IP+port. at least one router on the net has to have their IP+port
// set, since there has to be someone to detect one's IP off. most don't need
// to set these though
envProps.setProperty("i2np.udp.host", "127.0.0.1");
envProps.setProperty("i2np.udp.internalPort", "12000");
envProps.setProperty("i2np.udp.port", "12000");
// disable I2CP, the netDb, peer testing/profile persistence, and tunnel
// creation/management
envProps.setProperty("i2p.dummyClientFacade", "true");
envProps.setProperty("i2p.dummyNetDb", "true");
envProps.setProperty("i2p.dummyPeerManager", "true");
envProps.setProperty("i2p.dummyTunnelManager", "true");
// set to false if you want to use HMAC-SHA256-128 instead of HMAC-MD5-128 as
// the SSU MAC
envProps.setProperty("i2p.HMACMD5", "true");
// if you're using the HMAC MD5, by default it will use a 32 byte MAC field,
// which is a bug, as it doesn't generate the same values as a 16 byte MAC field.
// set this to false if you don't want the bug
envProps.setProperty("i2p.HMACBrokenSize", "false");
// no need to include any stats in the routerInfo we send to people on SSU
// session establishment
envProps.setProperty("router.publishPeerRankings", "false");
// write the logs to ./logs/log-router-*.txt (logger configured with the file
// ./logger.config, or another config file specified as
// -Dlogger.configLocation=blah)
envProps.setProperty("loggerFilenameOverride", "logs/log-router-@.txt");
return envProps;
}
private void setupHandlers() {
// netDb store is sent on connection establishment, which includes contact info
// for the peer. the DBStoreJobBuilder builds a new asynchronous Job to process
// each one received (storing it in our in-memory, passive netDb)
_us.inNetMessagePool().registerHandlerJobBuilder(DatabaseStoreMessage.MESSAGE_TYPE, new DBStoreJobBuilder());
// handle any Foo messages by displaying them on stdout
_us.inNetMessagePool().registerHandlerJobBuilder(FooMessage.MESSAGE_TYPE, new FooJobBuilder());
}
/** random place for storing router info files - written as $dir/base64(SHA256(info.getIdentity)) */
private File getInfoDir() { return new File("/tmp/ssuDemoInfo/"); }
private void storeMyInfo(RouterInfo info) {
File infoDir = getInfoDir();
if (!infoDir.exists())
infoDir.mkdirs();
FileOutputStream fos = null;
File infoFile = new File(infoDir, info.getIdentity().calculateHash().toBase64());
try {
fos = new FileOutputStream(infoFile);
info.writeBytes(fos);
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (DataFormatException dfe) {
dfe.printStackTrace();
} finally {
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
}
System.out.println("Our info stored at: " + infoFile.getAbsolutePath());
}
private void loadPeers() {
File infoDir = getInfoDir();
if (!infoDir.exists())
infoDir.mkdirs();
while (true) {
File peerFiles[] = infoDir.listFiles();
if ( (peerFiles != null) && (peerFiles.length > 0) ) {
for (int i = 0; i < peerFiles.length; i++) {
if (peerFiles[i].isFile() && !peerFiles[i].isHidden()) {
if (!_us.routerHash().toBase64().equals(peerFiles[i].getName())) {
System.out.println("Reading info: " + peerFiles[i].getAbsolutePath());
try {
FileInputStream in = new FileInputStream(peerFiles[i]);
RouterInfo ri = new RouterInfo();
ri.readBytes(in);
peerRead(ri);
} catch (IOException ioe) {
System.err.println("Error reading " + peerFiles[i].getAbsolutePath());
ioe.printStackTrace();
} catch (DataFormatException dfe) {
System.err.println("Corrupt " + peerFiles[i].getAbsolutePath());
dfe.printStackTrace();
}
}
}
}
}
try { Thread.sleep(30*1000); } catch (InterruptedException ie) {}
}
}
private void peerRead(RouterInfo ri) {
RouterInfo old = _us.netDb().store(ri.getIdentity().calculateHash(), ri);
if (old == null)
newPeerRead(ri);
}
private void newPeerRead(RouterInfo ri) {
OutNetMessage out = new OutNetMessage(_us);
// _us.clock() is an ntp synchronized clock. give up on sending this message
// if it doesn't get ACKed within the next 10 seconds
out.setExpiration(_us.clock().now() + 10*1000);
out.setPriority(100);
out.setTarget(ri);
FooMessage data = new FooMessage(_us, new byte[] { 0x0, 0x1, 0x2, 0x3 });
System.out.println("SEND: " + Base64.encode(data.getData()));
out.setMessage(data);
// job fired if we can't contact them, or if it takes too long to get an ACK
out.setOnFailedSendJob(null);
// job fired once the transport gets a full ACK of the message
out.setOnSendJob(new AfterACK());
// queue up the message, establishing a new SSU session if necessary, using
// their direct SSU address if they have one, or their indirect SSU addresses
// if they don't. If we cannot contact them, we will 'shitlist' their address,
// during which time we will not even attempt to send messages to them. We also
// drop their netDb info when we shitlist them, in case their info is no longer
// correct. Since the netDb is disabled for all meaningful purposes, the SSUDemo
// will be responsible for fetching such information.
_us.outNetMessagePool().add(out);
}
/** fired if and only if the FooMessage is ACKed before we time out */
private class AfterACK extends JobImpl {
public AfterACK() { super(_us); }
public void runJob() { System.out.println("Foo message sent completely"); }
public String getName() { return "After Foo message send"; }
}
////
// Foo and netDb store handling below
/**
* Deal with an Foo message received
*/
private class FooJobBuilder implements HandlerJobBuilder {
public FooJobBuilder() {
I2NPMessageImpl.registerBuilder(new FooBuilder(), FooMessage.MESSAGE_TYPE);
}
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
return new FooHandleJob(_us, receivedMessage, from, fromHash);
}
}
private class FooHandleJob extends JobImpl {
private I2NPMessage _msg;
public FooHandleJob(RouterContext ctx, I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
super(ctx);
_msg = receivedMessage;
}
public void runJob() {
// we know its a FooMessage, since thats the type of message that the handler
// is registered as
FooMessage m = (FooMessage)_msg;
System.out.println("RECV: " + Base64.encode(m.getData()));
}
public String getName() { return "Handle Foo message"; }
}
private class FooBuilder implements I2NPMessageImpl.Builder {
public I2NPMessage build(I2PAppContext ctx) { return new FooMessage(ctx, null); }
}
/**
* Just carry some data...
*/
class FooMessage extends I2NPMessageImpl {
private byte[] _data;
public static final int MESSAGE_TYPE = 17;
public FooMessage(I2PAppContext ctx, byte data[]) {
super(ctx);
_data = data;
}
/** pull the read data off */
public byte[] getData() { return _data; }
/** specify the payload to be sent */
public void setData(byte data[]) { _data = data; }
public int getType() { return MESSAGE_TYPE; }
protected int calculateWrittenLength() { return _data.length; }
public void readMessage(byte[] data, int offset, int dataSize, int type) throws I2NPMessageException, IOException {
_data = new byte[dataSize];
System.arraycopy(data, offset, _data, 0, dataSize);
}
protected int writeMessageBody(byte[] out, int curIndex) throws I2NPMessageException {
System.arraycopy(_data, 0, out, curIndex, _data.length);
return curIndex + _data.length;
}
}
////
// netDb store handling below
/**
* Handle any netDb stores from the peer - they send us their netDb as part of
* their SSU establishment (and we send them ours).
*/
private class DBStoreJobBuilder implements HandlerJobBuilder {
public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
return new HandleJob(_us, receivedMessage, from, fromHash);
}
}
private class HandleJob extends JobImpl {
private I2NPMessage _msg;
public HandleJob(RouterContext ctx, I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
super(ctx);
_msg = receivedMessage;
}
public void runJob() {
// we know its a DatabaseStoreMessage, since thats the type of message that the handler
// is registered as
DatabaseStoreMessage m = (DatabaseStoreMessage)_msg;
try {
_us.netDb().store(m.getKey(), m.getRouterInfo());
} catch (IllegalArgumentException iae) {
iae.printStackTrace();
}
}
public String getName() { return "Handle netDb store"; }
}
}

View File

@ -12,6 +12,9 @@ import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.TunnelId;
import java.io.IOException;
import java.io.Writer;
/**
* Build and maintain tunnels throughout the network.
*
@ -66,3 +69,31 @@ public interface TunnelManagerFacade extends Service {
public void setInboundSettings(Hash client, TunnelPoolSettings settings);
public void setOutboundSettings(Hash client, TunnelPoolSettings settings);
}
class DummyTunnelManagerFacade implements TunnelManagerFacade {
public TunnelInfo getTunnelInfo(TunnelId id) { return null; }
public TunnelInfo selectInboundTunnel() { return null; }
public TunnelInfo selectInboundTunnel(Hash destination) { return null; }
public TunnelInfo selectOutboundTunnel() { return null; }
public TunnelInfo selectOutboundTunnel(Hash destination) { return null; }
public boolean isInUse(Hash peer) { return false; }
public int getParticipatingCount() { return 0; }
public int getFreeTunnelCount() { return 0; }
public int getOutboundTunnelCount() { return 0; }
public long getLastParticipatingExpiration() { return -1; }
public void buildTunnels(Destination client, ClientTunnelSettings settings) {}
public TunnelPoolSettings getInboundSettings() { return null; }
public TunnelPoolSettings getOutboundSettings() { return null; }
public TunnelPoolSettings getInboundSettings(Hash client) { return null; }
public TunnelPoolSettings getOutboundSettings(Hash client) { return null; }
public void setInboundSettings(TunnelPoolSettings settings) {}
public void setOutboundSettings(TunnelPoolSettings settings) {}
public void setInboundSettings(Hash client, TunnelPoolSettings settings) {}
public void setOutboundSettings(Hash client, TunnelPoolSettings settings) {}
public void renderStatusHTML(Writer out) throws IOException {}
public void restart() {}
public void shutdown() {}
public void startup() {}
}