Compare commits

...

97 Commits

Author SHA1 Message Date
a95a968fa8 * 2004-10-18 0.4.1.3 released
2004-10-18  jrandom
    * Allow sending messages with a section of a byte array.
    * Reduced stats published.
2004-10-18 19:07:59 +00:00
6c08941d8b updated curiosity.i2p 2004-10-18 18:23:13 +00:00
e13a5b3865 added blueheron.i2p 2004-10-17 21:39:06 +00:00
9011d5604a 2004-10-17 jrandom
* Don't b0rk on whitespace in the router address.
2004-10-17 21:03:05 +00:00
78aa4ca137 updated files.i2p 2004-10-17 08:45:16 +00:00
93111842df clarify history - we reduce the capacity calc, not the 'isFailing'.
* More aggressively reduce the capacity of peers if their tunnels are
      failing so that we move off them quicker.
2004-10-17 04:00:21 +00:00
88693f8adc 2004-10-16 jrandom
* More aggressively fail peers if their tunnels are failing so that we
      move off them quicker.
    * Simplify some data structure serialization for reuse in the streaming
      lib, as well as add support for signing and verifying partial byte
      arrays.
    * Logging updates
2004-10-17 03:58:08 +00:00
f904b012e9 initial impl for the new streaming lib (saying this isn't done should be obvious, but the
packet spec is at a save point)
2004-10-17 03:47:03 +00:00
cebe0a151f added utansans.i2p 2004-10-16 21:45:55 +00:00
8fffad0891 2004-10-16 jrandom
* Increased the default minimum tunnel test time to 5 seconds, since we
      still see the occational message processing time spike to 2 seconds.
    * Update the SimpleTimer to allow rescheduling a task thats already
      queued (useful for the new streaming lib).
2004-10-16 18:00:47 +00:00
fb1263dad7 2004-10-15 jrandom
* Replaced old minimum tunnel test timeout of 1s with a configurable
      value (router.config property "router.tunnelTestMinimum", with the
      default of 2s).
2004-10-15 17:39:18 +00:00
28c5d6c10d 2004-10-14 jrandom
* Tunnel rejection is no longer a sign of an overwhelmingly loaded
      peer, so don't use it as a key point of the IsFailing calculator.
      We still use it as a key point of the Capacity calculator, however.
2004-10-15 01:20:12 +00:00
e7a6f6836e added irc.orz.i2p 2004-10-14 22:35:11 +00:00
f8ffe016d1 2004-10-14 jrandom
* Allow for a configurable tunnel "growth factor", rather than trying
      to achieve a steady state.  This will let us grow gradually when
      the router is needed more, rather than blindly accepting the request
      or arbitrarily choking it at an averaged value.  Configure this with
      "router.tunnelGrowthFactor" in the router.config (default "1.5").
    * Adjust the tunnel test timeouts dynamically - rather than the old
      flat 30s (!!!) timeout, we set the timeout to 2x the average tunnel
      test time (the deviation factor can be adjusted by setting
      "router.tunnelTestDeviation" to "3.0" or whatever).  This should help
      find the 'good' tunnels.
    * Added some crazy debugging to try and track down an intermittent hang.
2004-10-14 20:02:45 +00:00
ec322f0966 added nano.i2p 2004-10-13 20:42:35 +00:00
0674709fc6 added ragnarok.i2p 2004-10-13 20:33:19 +00:00
d91ac7ef21 2004-10-13 jrandom
* Fix the probabalistic tunnel reject (we always accepted everything,
      since the docs on java.util.Random.nextDouble() are wrong..)
    * Fixed a race on startup (thanks Quadn!)
2004-10-13 19:40:47 +00:00
2f0c3c7baf added marcos.i2p 2004-10-13 16:29:27 +00:00
be68407707 duh (oops) 2004-10-12 21:50:17 +00:00
f799a25aeb 2004-10-12 jrandom
* Disable the probabalistic drop by default (enable via the router config
      property "tcp.dropProbabalistically=true")
    * Disable the actual watchdog shutdown by default, but keep track of more
      variables and log a lot more when it occurs (enable via the router
      config property "watchdog.haltOnHang=true")
    * Implement some tunnel participation smoothing by refusing requests
      probabalistically as our participating tunnel count exceeds the previous
      hour's, or when the 10 minute average tunnel test time exceeds the 60
      minute average tunnel test time.  The probabilities in both cases are
      oldAverage / #current, so if you're suddenly flooded with 200 tunnels
      and you had previously only participated in 50, you'll have a 25% chance
      of accepting a subsequent request.
2004-10-12 21:29:41 +00:00
8329d045f1 confirm removal 2004-10-11 00:23:26 +00:00
503b289240 * 2004-10-10 0.4.1.2 released 2004-10-10 19:33:08 +00:00
35e3bbb862 2004-10-10 cervantes
* Update the I2PTunnel HTTP proxy to strip out the i2paddresshelper from
      the request.
2004-10-10 14:57:15 +00:00
8dc261da79 2004-10-09 jrandom
* Added a watchdog timer to do some baseline liveliness checking to help
      debug some odd errors.
    * Added a pair of summary stats for bandwidth usage, allowing easy export
      with the other stats ("bw.sendBps" and "bw.receiveBps")
    * Trimmed another memory allocation on message reception.
2004-10-10 00:03:25 +00:00
65676f8988 2004-10-08 jrandom
* Revamp the AESInputStream so it doesn't allocate any temporary objects
      during its operation.
2004-10-08 22:53:03 +00:00
730da3aa27 2004-10-08 jrandom
* Don't kill the establisher threads during a soft restart.
    * Attempt to validate the peer's routerInfo earlier during handshaking.
    * Revamp the AESOutputStream so it doesn't allocate any temporary objects
      during its operation.
2004-10-08 18:38:48 +00:00
ff8674bca9 2004-10-07 jrandom
* Reimplement the I2NP reading with less temporary memory allocation.
      There is still significant GC churn, especially under load, but this
      should help.
    * Catch some oddball errors in the transport (message timeout while
      establishing).
2004-10-08 02:08:10 +00:00
c7cfef3b61 2004-10-07 jrandom
* Expire queued messages even when the writer is blocked.
    * Reimplement most of the I2NP writing with less temporary memory
      allocations (I2NP reading still gobbles memory).
2004-10-07 19:19:51 +00:00
32188b1cc0 expose some direct byte formatting methods
allow SHA256 to be run against a partial array
append to the stats.log instead of overwriting it
2004-10-07 16:48:46 +00:00
37479d8c0d logging 2004-10-07 16:45:11 +00:00
f5c7d6576d no need to double b0rk 2004-10-07 16:42:55 +00:00
38c422bbc0 2004-10-06 jrandom
* Implement an active queue management scheme on the TCP transports,
      dropping messages probabalistically as the queue fills up.  The
      estimated queue capacity is determined by the rate at which messages
      have been sent to the peer (averaged at 1, 5, and 60m periods).  As
      we exceed 1/2 of the estimated capacity, we drop messages throughout
      the queue probabalistically with regards to their size.  This is based
      on RFC 2309's RED, with the minimum threshold set to 1/2 the
      estimated connection capacity.  We may want to consider using a send
      rate and queue size measured across all connections, to deal with our
      own local bandwidth saturation, but we'll try the per-con metrics first.
2004-10-06 21:03:51 +00:00
39d4e5ea81 list the shutdown time w/ the clock fudge factor included 2004-10-06 16:47:36 +00:00
4191ad1cbf 2004-10-06 jrandom
* Enable explicit disabling of the systray entirely for windows machines
      with strange configurations: add -Dsystray.disable=true to the java
      command line.  (thanks mihi!)
2004-10-06 13:23:38 +00:00
29287da37c 2004-10-05 jrandom
* Allow peers on the same LAN to communicate with each other safely even
      when they cannot talk to each other through the external address.
2004-10-06 01:12:03 +00:00
98c780415b 2004-10-05 jrandom
* Display how much time is left before the graceful shutdown is complete.
    * Debug some improperly failed messages on timeout or disconnection.
2004-10-05 19:21:47 +00:00
756af9c699 oops 2004-10-05 18:21:44 +00:00
7f9076bb1d updated beyond.i2p (after verification) 2004-10-05 16:04:16 +00:00
2404f1ab9a added b.i2p 2004-10-05 15:57:08 +00:00
64bcfd09ec 2004-10-05 jrandom
* Don't go into a fast busy if an I2PTunnel 'server' is explicitly killed
      (thanks mule!)
    * Handle some more error conditions regarding abruptly closing sockets
      (thanks Jonva!)
2004-10-05 15:38:37 +00:00
6251d22c6e added tinyurl.i2p 2004-10-05 15:26:20 +00:00
de1b4937a1 2004-10-04 jrandom
* Update the shitlist to reject a peer for an exponentially increasing
      period of time (with an upper bounds of an hour).
    * Various minor stat and debugging fixes
2004-10-04 17:30:22 +00:00
d092dd79ba get rid of the really really frequent temporary object creation 2004-10-04 13:53:10 +00:00
a3ba968386 24h time 2004-10-04 01:17:01 +00:00
5ca2b97128 24h time 2004-10-04 01:00:38 +00:00
c9daad1cfd added detonate.i2p 2004-10-04 00:16:13 +00:00
0526d5b53a cli to splot the stat log 2004-10-03 23:53:16 +00:00
34163fb8e4 dont overwrite index.html anymore (0.4.1.2 wont) 2004-10-03 21:06:17 +00:00
98d2d661a8 2004-10-03 jrandom
* Add a new stat logging component to optionally dump the raw stats to
      disk as they are generated, rather than rely upon the summarized data.
      By default, this is off, but the router property "stat.logFilters" can
      be set to a comma delimited list of stats (e.g. "client.sendAckTime")
      which will be written to the file "stats.log" (or whatever the property
      "stat.logFile" is set to).  This can also log profile related stats,
      such as "dbResponseTime" or "tunnelTestResponseTime".
2004-10-03 20:48:43 +00:00
d9f0a0fd74 dont list an explicit webdefault.xml (use the default) 2004-10-03 14:04:21 +00:00
d20d043e0f 2004-10-02 jrandom
* Assure that we quickly fail messages bound for shitlisted peers.
    * Address a race on startup where the first peer contacted could hang the
      router (thanks Romster!)
    * Only whine about an intermittent inability to query the time server once
2004-10-02 19:05:24 +00:00
ce186e1872 2004-10-02 jrandom
* Command line utility to verify a peer's reachability - simply run
      net.i2p.router.transport.tcp.ConnectionHandler hostname port# and it
      will print out whether that peer is reachable or not (using a simple
      verification handshake).
2004-10-02 12:31:15 +00:00
a14da92e1d * 2004-10-01 0.4.1.1 released 2004-10-01 17:23:00 +00:00
2b54d850ea logging 2004-10-01 17:19:37 +00:00
a63c1b19fc 2004-10-01 jrandom
* Handle partial reseeds, caused by seeds going away before the download
      completes (thanks Sugadude!)
2004-10-01 14:35:49 +00:00
34f74cd6ef 2004-10-01 jrandom
* Explicitly refuse IPv6 addresses, since only some peers support
      them and we want fully reachable peers.
2004-10-01 11:49:02 +00:00
c0b8e62135 2004-10-01 jrandom
* Additional error handling for a variety of transport layer errors.
2004-10-01 09:39:14 +00:00
ea24166b8e added identiguy.i2p 2004-10-01 09:32:46 +00:00
178b229d66 *cough* (oops) 2004-09-30 16:44:46 +00:00
276493da65 * 2004-09-30 0.4.1 released (not backwards compatible)
2004-09-30  jrandom
    * Bundle the configuration necessary to run an eepsite out of the box
      with Jetty - simply edit ./eepsite/docroot/index.html and give people
      the key listed on the I2PTunnel configuration page, and its up.
    * Router console cleanup, and some (off by default) tunnels -
      smtp.postman.i2p (port 7659), pop.postman.i2p (port 7660), and
      irc.baffled.i2p (port 7661)
2004-09-30 15:58:54 +00:00
e85dadfef2 added jabber-2.i2p 2004-09-30 13:25:31 +00:00
1c70efb350 added eepsite info 2004-09-30 13:23:01 +00:00
6804a0c564 * always flush the console output (duh)
* deal with some degenerate situations with identities changing and auto IP detection
* some catch-alls for cleaning up the registry on degenerate tunnels
* lots of logging
2004-09-30 13:10:02 +00:00
6eb7ecc2d4 2004-09-30 jrandom
* Bundle the configuration necessary to run an eepsite out of the box
      with Jetty - simply edit ./eepsite/docroot/index.html and give people
      the key listed on the I2PTunnel configuration page, and its up.
plus minor bugfixes / refactoring / logging
2004-09-30 06:57:22 +00:00
f4956b06b6 be more careful on startup 2004-09-30 06:51:28 +00:00
9a2f7c2660 include the timeouts due to inability to find the leaseSet 2004-09-30 00:51:35 +00:00
b6017c558a * updated stats:
- sendsPerFailure: how many partial sends we make when they all fail
- timeoutCongestionInbound: describes how much faster than our average speed we were receiving data when each partial send timed out (in Bps)
- timeoutCongestionMessage: our send processing time when each partial send timed out (in ms)
- timeoutCongestionTunnel: our tunnel test time when each partial send timed out (in ms)
- participatingMessagesProcessedActive: # of messages more than the (most recent) average that a tunnel we were participating in transmitted (for tunnels with more than the average)
* updated to use Writer for rendering the console, so we can do partial writes (and hopefully help debug some kooky threading bugs on kaffe)
2004-09-29 22:54:38 +00:00
62ed6c6a58 * updated stats:
- sendsPerFailure: how many partial sends we make when they all fail
- timeoutCongestionInbound: describes how much faster than our average speed we were receiving data when each partial send timed out (in Bps)
- timeoutCongestionMessage: our send processing time when each partial send timed out (in ms)
- timeoutCongestionTunnel: our tunnel test time when each partial send timed out (in ms)
- participatingMessagesProcessedActive: # of messages more than the (most recent) average that a tunnel we were participating in transmitted (for tunnels with more than the average)
* updated to use Writer for rendering the console, so we can do partial writes (and hopefully help debug some kooky threading bugs on kaffe)
2004-09-29 22:49:19 +00:00
24966c812f javad0c 2004-09-29 20:12:26 +00:00
ea8dc2e0af *nix daemon scripts are gone 2004-09-29 19:38:15 +00:00
010b285e67 2004-09-29 jrandom
* Always wipe the Jetty work directory on startup, so that web updates
      are reflected immediately (Jetty does not honor the cache across
      multiple executions)
in addition, refactor various file ops out of the DataHelper into FileUtil
2004-09-29 19:34:02 +00:00
774231f347 * started reducing the temporary buffers created within various crypto methods , as we've
got some pretty heavy GC churn when under load.  rough estimate is we allocate 5-8x as
much data as we need, copying it all over the place before forwarding it (or processing it).
this should cut down a few of those copies, but not enough yet.  it'd be great to get that
down to 2x.
* lots of logging
2004-09-28 20:33:23 +00:00
ff1dfd8f25 let the algorithm handle writing the output to various places in the output array (so we can cut down on temporary memory allocation) 2004-09-28 16:38:24 +00:00
2741ac195d * protocol doc & impl cleanup
* more defensive programming
* more javadoc updates
2004-09-28 08:34:48 +00:00
cf780e296e bugfixes for autodetection/update of IP address 2004-09-27 18:05:28 +00:00
0361246db0 2004-09-27 jrandom
* Limit the number of connection tags saved to 10,000.  This is a huge
      limit, but consumes no more than 1MB of RAM.  For now, we drop them
      randomly after reaching that size, forcing those dropped peers to use
      a full DH negotiation.
    * HTML cleanup in the console.
2004-09-27 07:57:43 +00:00
63355ecd5b more tcp transport updates (getting closer to the old functionality)
* avoid bad peers
* shitlist appropriately
* include the bandwidth limiter
* add socket timeouts
* deal with *cough* closing connections
* javadocs
2004-09-26 18:11:39 +00:00
0f54ba59fb die phttp die 2004-09-26 15:32:24 +00:00
b67b243ebd the following isn't the end of the 0.4.1 updates, as there are still more things left to clean
up and debug in the new tcp transport, but it all works, and i dont like having big changes
sitting on my local machine (and there's no real need for branching atm)
2004-09-26  jrandom
    * Complete rewrite of the TCP transport with IP autodetection and
      low CPU overhead reconnections.  More concise connectivity errors
      are listed on the /oldconsole.jsp as well.  The IP autodetection works
      by listening to the first person who tells you what your IP address is
      when you have not defined one yourself and you have no other TCP
      connections.
    * Update to the I2NP message format to add transparent verification at
      the I2NP level (beyond standard TCP verification).
    * Remove a potential weakness in our AESEngine's safeEncrypt and safeDecrypt
      implementation (rather than verifying with E(H(key)), we now verify with
      E(H(iv))).
    * The above changes are NOT BACKWARDS COMPATIBLE.
    * Removed all of the old unused PHTTP code.
    * Refactor various methods and clean up some javadoc.
2004-09-26 15:16:44 +00:00
4c29c20613 javadoc fix 2004-09-26 14:50:49 +00:00
mpc
4c2619d948 minor changes 2004-09-24 21:08:00 +00:00
ea5662a4a2 another minor semantic jikes warning 2004-09-23 01:21:33 +00:00
7c1ce777a1 cleanup per jikes' whining (overloaded var names, val overflow, etc) 2004-09-23 01:13:22 +00:00
3bb85f2d61 add some logical exit values (useful for scripting).thanks xolo! 2004-09-23 01:05:40 +00:00
93e36b3113 added jake.i2p 2004-09-23 00:08:51 +00:00
932fb670e3 added anonymnet.i2p sasquotch.i2p orz.i2p microbleu.i2p www/smtp/pop3.postman.i2p 2004-09-22 23:58:37 +00:00
54dce61a95 2004-09-21 jrandom
* Have two tiers of hosts.txt files - the standard "hosts.txt" and
      the new "userhosts.txt".  Updates to I2P will only overwrite the former,
      but values stored in the later take precedence.  Both are queried on
      lookup.
2004-09-22 00:10:26 +00:00
e686c0e0a2 added curiosity.i2p 2004-09-17 19:47:58 +00:00
05acf32f39 2004-09-16 jrandom
* Refactor the TCP transport to deal with changing identities gracefully,
      and to prevent some wasted effort by keeping track of what host+port
      combinations we are connected to (rather than just the identities).  Also
      catch a few configuration errors earlier.
    * Removed no longer relevent methods from the Transport API that were
      exposing ideas that probably shouldn't be exposed.
    * Removed the 0.4.0.1 specific files from i2pupdate.zip (relating to script
      updates)
2004-09-16 23:55:12 +00:00
mpc
67064012c9 added freeciv.nightblade.i2p 2004-09-16 20:50:26 +00:00
10e93c3b1b rename (wtf was i thinking "Calculator"...) 2004-09-16 02:04:40 +00:00
5b2ec1cbb5 moved udp transport to 0.4.4 instead of 1.1 2004-09-14 19:52:46 +00:00
972f701c5c ganttproject.sf.net based plan 2004-09-14 02:19:33 +00:00
51285efbc3 2004-09-13 jrandom
* Update for the SDK reconnection to deal with overflow.
    * Web improvements (@ not # on the /logs.jsp [thanks ugha!] and fixed the
      rounding on lifetime bandwidth used [thanks gott!]).
2004-09-13 03:08:16 +00:00
e2635705f9 added xolotl.i2p 2004-09-11 01:25:24 +00:00
7762107543 added modulus.i2p 2004-09-10 04:11:15 +00:00
9123ad89c8 added links to www.i2p and dev.i2p (thanks pseudonym) 2004-09-09 04:10:25 +00:00
176 changed files with 8565 additions and 4520 deletions

View File

@ -199,10 +199,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
host = getHostName(destination);
if ( (host != null) && ("i2p".equals(host)) ) {
int pos2;
if ((pos2 = line.indexOf("?")) != -1) {
if ((pos2 = request.indexOf("?")) != -1) {
// Try to find an address helper in the fragments
String fragments = line.substring(pos2 + 1);
// and split the request into it's component parts for rebuilding later
String fragments = request.substring(pos2 + 1);
String uriPath = request.substring(0, pos2);
pos2 = fragments.indexOf(" ");
String protocolVersion = fragments.substring(pos2 + 1);
String urlEncoding = "";
fragments = fragments.substring(0, pos2);
fragments = fragments + "&";
String fragment;
@ -215,8 +219,17 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
if (pos2 >= 0) {
addressHelpers.put(destination,fragment.substring(pos2 + 1));
}
}
} else {
// append each fragment unless it's the address helper
if ("".equals(urlEncoding)) {
urlEncoding = "?" + fragment;
} else {
urlEncoding = urlEncoding + "&" + fragment;
}
}
}
// reconstruct the request minus the i2paddresshelper GET var
request = uriPath + urlEncoding + " " + protocolVersion;
}
String addressHelper = (String) addressHelpers.get(destination);

View File

@ -148,6 +148,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
I2PServerSocket i2pss = sockMgr.getServerSocket();
while (true) {
I2PSocket i2ps = i2pss.accept();
if (i2ps == null) throw new I2PException("I2PServerSocket closed");
I2PThread t = new I2PThread(new Handler(i2ps));
t.start();
}

View File

@ -8,6 +8,7 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Random;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
@ -375,10 +376,16 @@ public class TunnelController implements Logging {
buf.append("Full destination: ");
buf.append("<input type=\"text\" size=\"10\" onclick=\"this.select();\" ");
buf.append("value=\"").append(dest.toBase64()).append("\" />\n");
long val = new Random().nextLong();
if (val < 0) val = 0 - val;
buf.append("<br />You can <a href=\"http://temp").append(val);
buf.append(".i2p/?i2paddresshelper=").append(dest.toBase64()).append("\">view</a>");
buf.append(" it in a browser (only when you're using the eepProxy)\n");
buf.append("<br />If you are going to share this on IRC, you need to split it up:<br />\n");
String str = dest.toBase64();
buf.append(str.substring(0, str.length()/2)).append("<br />\n");
buf.append(str.substring(str.length()/2)).append("<br />\n");
buf.append("You can also post it to <a href=\"http://forum.i2p/viewforum.php?f=16\">Eepsite announcement forum</a><br />");
}
}
}

View File

@ -8,7 +8,7 @@ import java.util.Random;
import java.util.StringTokenizer;
/**
* Uuuugly... generate the edit/add forms for the various
* Uuuugly code to generate the edit/add forms for the various
* I2PTunnel types (httpclient/client/server)
*
*/
@ -63,7 +63,7 @@ class WebEditPageFormGenerator {
addOptions(buf, controller);
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
buf.append(" <i>confirm</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
buf.append(" <i>confirm removal:</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
buf.append("</form>\n");
return buf.toString();
}
@ -83,7 +83,7 @@ class WebEditPageFormGenerator {
addOptions(buf, controller);
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\"><br />\n");
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
buf.append(" <i>confirm</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
buf.append(" <i>confirm removal:</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
buf.append("</form>\n");
return buf.toString();
}
@ -118,7 +118,7 @@ class WebEditPageFormGenerator {
addOptions(buf, controller);
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
buf.append(" <i>confirm</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
buf.append(" <i>confirm removal:</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
buf.append("</form>\n");
return buf.toString();
}

View File

@ -7,6 +7,7 @@
<jsp:useBean class="net.i2p.i2ptunnel.WebStatusPageHelper" id="helper" scope="request" />
<jsp:setProperty name="helper" property="*" />
<h2>Messages since last page load:</h2>
<b><jsp:getProperty name="helper" property="actionResults" /></b>
<jsp:getProperty name="helper" property="summaryList" />

View File

@ -234,7 +234,7 @@ class I2PSocketImpl implements I2PSocket {
}
/**
* Close the socket from the I2P side, e. g. by a close packet.
* Close the socket from the I2P side (by a close packet)
*/
protected void internalClose() {
synchronized (flagLock) {

View File

@ -118,7 +118,7 @@ public class I2PSocketManager implements I2PSessionListener {
return;
}
if (msg.length < 4) {
_log.error(getName() + ": ==== packet too short ====");
_log.warn(getName() + ": ==== packet too short ====");
return;
}
int type = msg[0] & 0xff;
@ -155,7 +155,7 @@ public class I2PSocketManager implements I2PSessionListener {
return;
}
} catch (I2PException ise) {
_log.error(getName() + ": Error processing", ise);
_log.warn(getName() + ": Error processing", ise);
} catch (IllegalStateException ise) {
_log.debug(getName() + ": Error processing", ise);
}
@ -176,7 +176,7 @@ public class I2PSocketManager implements I2PSessionListener {
}
if (s == null) {
_log.warn(getName() + ": No socket responsible for ACK packet");
_log.warn(getName() + ": No socket responsible for ACK packet for id " + getReadableForm(id));
return;
}
@ -223,7 +223,8 @@ public class I2PSocketManager implements I2PSessionListener {
s = (I2PSocketImpl) _outSockets.get(id);
}
_log.debug(getName() + ": *Disconnect outgoing for socket " + s);
_log.debug(getName() + ": *Disconnect outgoing for socket " + s + " on id "
+ getReadableForm(id));
try {
if (s != null) {
if (payload.length > 0) {
@ -241,7 +242,7 @@ public class I2PSocketManager implements I2PSessionListener {
}
return;
} catch (Exception t) {
_log.error(getName() + ": Ignoring error on disconnect for socket " + s, t);
_log.warn(getName() + ": Ignoring error on disconnect for socket " + s, t);
}
}
@ -259,7 +260,8 @@ public class I2PSocketManager implements I2PSessionListener {
// packet send outgoing
if (_log.shouldLog(Log.DEBUG))
_log.debug(getName() + ": *Packet send outgoing [" + payload.length + "] for socket " + s);
_log.debug(getName() + ": *Packet send outgoing [" + payload.length + "] for socket "
+ s + " on id " + getReadableForm(id));
if (s != null) {
s.queueData(payload);
return;
@ -293,7 +295,8 @@ public class I2PSocketManager implements I2PSessionListener {
s.setRemoteID(id);
}
}
_log.debug(getName() + ": *Syn! for socket " + s);
_log.debug(getName() + ": *Syn! for socket " + s + " on id " + getReadableForm(newLocalID)
+ " from " + d.calculateHash().toBase64().substring(0,6));
if (!acceptConnections) {
// The app did not instantiate an I2PServerSocket
@ -303,7 +306,7 @@ public class I2PSocketManager implements I2PSessionListener {
replySentOk = _session.sendMessage(d, packet);
}
if (!replySentOk) {
_log.error(getName() + ": Error sending close to " + d.calculateHash().toBase64()
_log.warn(getName() + ": Error sending close to " + d.calculateHash().toBase64()
+ " in response to a new con message",
new Exception("Failed creation"));
}
@ -360,13 +363,13 @@ public class I2PSocketManager implements I2PSessionListener {
return;
} else {
if ( (payload.length > 0) && (_log.shouldLog(Log.ERROR)) )
_log.error(getName() + ": Disconnect packet had " + payload.length + " bytes");
_log.warn(getName() + ": Disconnect packet had " + payload.length + " bytes");
if (s != null)
s.internalClose();
return;
}
} catch (Exception t) {
_log.error(getName() + ": Ignoring error on disconnect", t);
_log.warn(getName() + ": Ignoring error on disconnect", t);
return;
}
}
@ -454,6 +457,10 @@ public class I2PSocketManager implements I2PSessionListener {
s = new I2PSocketImpl(peer, this, true, localID);
_outSockets.put(localID, s);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getName() + ": connect(" + peer.calculateHash().toBase64().substring(0,6)
+ ", ...): localID = " + lcID);
try {
ByteArrayOutputStream pubkey = new ByteArrayOutputStream();
_session.getMyDestination().writeBytes(pubkey);
@ -462,18 +469,30 @@ public class I2PSocketManager implements I2PSessionListener {
boolean sent = false;
sent = _session.sendMessage(peer, packet);
if (!sent) {
_log.info(getName() + ": Unable to send & receive ack for SYN packet for socket " + s);
_log.info(getName() + ": Unable to send & receive ack for SYN packet for socket "
+ s + " with localID = " + lcID);
synchronized (lock) {
_outSockets.remove(s.getLocalID());
}
_context.statManager().addRateData("streaming.synNoAck", 1, 1);
throw new I2PException("Error sending through I2P network");
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getName() + ": syn sent ok to "
+ peer.calculateHash().toBase64().substring(0,6)
+ " with localID = " + lcID);
}
if (options != null)
remoteID = s.getRemoteID(true, options.getConnectTimeout());
else
remoteID = s.getRemoteID(true, getDefaultOptions().getConnectTimeout());
if (_log.shouldLog(Log.DEBUG))
_log.debug(getName() + ": remoteID received from "
+ peer.calculateHash().toBase64().substring(0,6)
+ ": " + getReadableForm(remoteID)
+ " with localID = " + lcID);
if (remoteID == null) {
_context.statManager().addRateData("streaming.nackReceived", 1, 1);
throw new ConnectException("Connection refused by peer for socket " + s);
@ -488,9 +507,10 @@ public class I2PSocketManager implements I2PSessionListener {
return s;
} catch (InterruptedIOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error(getName() + ": Timeout waiting for ack from syn for id "
+ getReadableForm(lcID) + " for socket " + s, ioe);
if (_log.shouldLog(Log.WARN))
_log.warn(getName() + ": Timeout waiting for ack from syn for id "
+ lcID + " to " + peer.calculateHash().toBase64().substring(0,6)
+ " for socket " + s, ioe);
synchronized (lock) {
_outSockets.remove(s.getLocalID());
}
@ -498,14 +518,24 @@ public class I2PSocketManager implements I2PSessionListener {
_context.statManager().addRateData("streaming.synNoAck", 1, 1);
throw new InterruptedIOException("Timeout waiting for ack");
} catch (ConnectException ex) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getName() + ": Connection error waiting for ack from syn for id "
+ lcID + " to " + peer.calculateHash().toBase64().substring(0,6)
+ " for socket " + s, ex);
s.internalClose();
throw ex;
} catch (NoRouteToHostException ex) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getName() + ": No route to host waiting for ack from syn for id "
+ lcID + " to " + peer.calculateHash().toBase64().substring(0,6)
+ " for socket " + s, ex);
s.internalClose();
throw ex;
} catch (IOException ex) {
if (_log.shouldLog(Log.ERROR))
_log.error(getName() + ": Error sending syn on id " + getReadableForm(lcID) + " for socket " + s, ex);
if (_log.shouldLog(Log.WARN))
_log.warn(getName() + ": Error sending syn on id "
+ lcID + " to " + peer.calculateHash().toBase64().substring(0,6)
+ " for socket " + s, ex);
synchronized (lock) {
_outSockets.remove(s.getLocalID());
}
@ -513,7 +543,9 @@ public class I2PSocketManager implements I2PSessionListener {
throw new I2PException("Unhandled IOException occurred");
} catch (I2PException ex) {
if (_log.shouldLog(Log.INFO))
_log.info(getName() + ": Error sending syn on id " + getReadableForm(lcID) + " for socket " + s, ex);
_log.info(getName() + ": Error sending syn on id "
+ lcID + " to " + peer.calculateHash().toBase64().substring(0,6)
+ " for socket " + s, ex);
synchronized (lock) {
_outSockets.remove(s.getLocalID());
}
@ -521,7 +553,9 @@ public class I2PSocketManager implements I2PSessionListener {
throw ex;
} catch (Exception e) {
s.internalClose();
_log.error(getName() + ": Unhandled error connecting", e);
_log.warn(getName() + ": Unhandled error connecting on "
+ lcID + " to " + peer.calculateHash().toBase64().substring(0,6)
+ " for socket " + s, e);
throw new ConnectException("Unhandled error connecting: " + e.getMessage());
}
}
@ -592,7 +626,7 @@ public class I2PSocketManager implements I2PSessionListener {
_session.destroySession();
_log.debug(getName() + ": I2P session destroyed");
} catch (I2PSessionException e) {
_log.error(getName() + ": Error destroying I2P session", e);
_log.warn(getName() + ": Error destroying I2P session", e);
}
}
@ -618,15 +652,17 @@ public class I2PSocketManager implements I2PSessionListener {
try {
return _session.sendMessage(peer, new byte[] { (byte) CHAFF});
} catch (I2PException ex) {
_log.error(getName() + ": I2PException:", ex);
_log.warn(getName() + ": I2PException:", ex);
return false;
}
}
public void removeSocket(I2PSocketImpl sock) {
String localId = sock.getLocalID();
boolean removed = false;
synchronized (lock) {
_inSockets.remove(sock.getLocalID());
_outSockets.remove(sock.getLocalID());
removed = (null != _inSockets.remove(localId));
removed = removed || (null != _outSockets.remove(localId));
lock.notify();
}
@ -637,9 +673,10 @@ public class I2PSocketManager implements I2PSessionListener {
long recv = sock.getBytesReceived();
if (_log.shouldLog(Log.DEBUG)) {
_log.debug(getName() + ": Removing socket \"" + getReadableForm(sock.getLocalID()) + "\" [" + sock
_log.debug(getName() + ": Removing socket \"" + getReadableForm(localId) + "\" [" + sock
+ ", send: " + sent + ", recv: " + recv
+ ", lifetime: " + lifetime + "ms, time since close: " + timeSinceClose + ")]",
+ ", lifetime: " + lifetime + "ms, time since close: " + timeSinceClose
+ " removed? " + removed + ")]",
new Exception("removeSocket called"));
}

View File

@ -145,11 +145,16 @@ public class ConfigNetHandler extends FormHandler {
}
int fetched = 0;
int errors = 0;
for (Iterator iter = urls.iterator(); iter.hasNext(); ) {
fetchSeed(seedURL, (String)iter.next());
fetched++;
try {
fetchSeed(seedURL, (String)iter.next());
fetched++;
} catch (Exception e) {
errors++;
}
}
addFormNotice("Reseeded with " + fetched + " peers");
addFormNotice("Reseeded with " + fetched + " peers (and " + errors + " failures)");
} catch (Throwable t) {
_context.logManager().getLog(ConfigNetHandler.class).error("Error reseeding", t);
addFormError("Error reseeding (RESEED_EXCEPTION)");

View File

@ -4,8 +4,8 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import net.i2p.data.DataHelper;
import net.i2p.router.RouterContext;
import net.i2p.util.FileUtil;
public class ContentHelper {
private String _page;
@ -40,14 +40,14 @@ public class ContentHelper {
}
}
public String getContent() {
String str = DataHelper.readTextFile(_page, _maxLines);
String str = FileUtil.readTextFile(_page, _maxLines);
if (str == null)
return "";
else
return str;
}
public String getTextContent() {
String str = DataHelper.readTextFile(_page, _maxLines);
String str = FileUtil.readTextFile(_page, _maxLines);
if (str == null)
return "";
else

View File

@ -4,8 +4,8 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import net.i2p.data.DataHelper;
import net.i2p.router.RouterContext;
import net.i2p.util.FileUtil;
public class LogsHelper {
private RouterContext _context;
@ -28,7 +28,7 @@ public class LogsHelper {
public String getLogs() {
List msgs = _context.logManager().getBuffer().getMostRecentMessages();
StringBuffer buf = new StringBuffer(16*1024);
buf.append("<h2>Most recent console messages:</h2><ul>");
buf.append("<ul>");
buf.append("<code>\n");
for (int i = msgs.size(); i > 0; i--) {
String msg = (String)msgs.get(i - 1);
@ -42,10 +42,26 @@ public class LogsHelper {
}
public String getServiceLogs() {
String str = DataHelper.readTextFile("wrapper.log", 500);
String str = FileUtil.readTextFile("wrapper.log", 500);
if (str == null)
return "";
else
return "<pre>" + str + "</pre>";
}
public String getConnectionLogs() {
List msgs = _context.commSystem().getMostRecentErrorMessages();
StringBuffer buf = new StringBuffer(16*1024);
buf.append("<ul>");
buf.append("<code>\n");
for (int i = msgs.size(); i > 0; i--) {
String msg = (String)msgs.get(i - 1);
buf.append("<li>");
buf.append(msg);
buf.append("</li>\n");
}
buf.append("</code></ul>\n");
return buf.toString();
}
}

View File

@ -2,11 +2,14 @@ package net.i2p.router.web;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import net.i2p.router.RouterContext;
public class NetDbHelper {
private RouterContext _context;
private Writer _out;
/**
* Configure this bean to query a particular router context
*
@ -23,13 +26,21 @@ public class NetDbHelper {
public NetDbHelper() {}
public void setWriter(Writer writer) { _out = writer; }
public String getNetDbSummary() {
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
try {
_context.netDb().renderStatusHTML(baos);
if (_out != null) {
_context.netDb().renderStatusHTML(_out);
return "";
} else {
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
_context.netDb().renderStatusHTML(new OutputStreamWriter(baos));
return new String(baos.toByteArray());
}
} catch (IOException ioe) {
ioe.printStackTrace();
return "";
}
return new String(baos.toByteArray());
}
}

View File

@ -0,0 +1,34 @@
package net.i2p.router.web;
import net.i2p.data.DataHelper;
import net.i2p.router.RouterContext;
/**
* Simple helper to query the appropriate router for data necessary to render
* any emergency notices
*/
public class NoticeHelper {
private RouterContext _context;
/**
* Configure this bean to query a particular router context
*
* @param contextId begging few characters of the routerHash, or null to pick
* the first one we come across.
*/
public void setContextId(String contextId) {
try {
_context = ContextHelper.getContext(contextId);
} catch (Throwable t) {
t.printStackTrace();
}
}
public String getSystemNotice() {
if (_context.router().gracefulShutdownInProgress()) {
return "Graceful shutdown in "
+ DataHelper.formatDuration(_context.router().getShutdownTimeRemaining());
} else {
return "";
}
}
}

View File

@ -2,6 +2,8 @@ package net.i2p.router.web;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.List;
import net.i2p.router.RouterContext;
@ -9,6 +11,7 @@ import net.i2p.router.admin.StatsGenerator;
public class OldConsoleHelper {
private RouterContext _context;
private Writer _out;
/**
* Configure this bean to query a particular router context
*
@ -25,11 +28,20 @@ public class OldConsoleHelper {
public OldConsoleHelper() {}
public void setWriter(Writer writer) {
_out = writer;
}
public String getConsole() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(128*1024);
_context.router().renderStatusHTML(baos);
return baos.toString();
if (_out != null) {
_context.router().renderStatusHTML(_out);
return "";
} else {
ByteArrayOutputStream baos = new ByteArrayOutputStream(128*1024);
_context.router().renderStatusHTML(new OutputStreamWriter(baos));
return baos.toString();
}
} catch (IOException ioe) {
return "<b>Error rending the console</b>";
}
@ -38,9 +50,14 @@ public class OldConsoleHelper {
public String getStats() {
StatsGenerator gen = new StatsGenerator(_context);
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
gen.generateStatsPage(baos);
return baos.toString();
if (_out != null) {
gen.generateStatsPage(_out);
return "";
} else {
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
gen.generateStatsPage(new OutputStreamWriter(baos));
return baos.toString();
}
} catch (IOException ioe) {
return "<b>Error rending the console</b>";
}

View File

@ -2,6 +2,7 @@ package net.i2p.router.web;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import net.i2p.router.RouterContext;
@ -26,7 +27,7 @@ public class ProfilesHelper {
public String getProfileSummary() {
ByteArrayOutputStream baos = new ByteArrayOutputStream(16*1024);
try {
_context.profileOrganizer().renderStatusHTML(baos);
_context.profileOrganizer().renderStatusHTML(new OutputStreamWriter(baos));
} catch (IOException ioe) {
ioe.printStackTrace();
}
@ -36,7 +37,7 @@ public class ProfilesHelper {
public String getShitlistSummary() {
ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024);
try {
_context.shitlist().renderStatusHTML(baos);
_context.shitlist().renderStatusHTML(new OutputStreamWriter(baos));
} catch (IOException ioe) {
ioe.printStackTrace();
}

View File

@ -1,10 +1,12 @@
package net.i2p.router.web;
import java.io.File;
import java.io.IOException;
import java.util.List;
import net.i2p.router.RouterContext;
import net.i2p.apps.systray.SysTray;
import net.i2p.util.FileUtil;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.WebApplicationContext;
@ -21,6 +23,10 @@ public class RouterConsoleRunner {
private String _listenHost = "127.0.0.1";
private String _webAppsDir = "./webapps/";
static {
System.setProperty("org.mortbay.http.Version.paranoid", "true");
}
public RouterConsoleRunner(String args[]) {
if (args.length == 3) {
_listenPort = args[0].trim();
@ -35,6 +41,14 @@ public class RouterConsoleRunner {
}
public void startConsole() {
File workDir = new File("work");
boolean workDirRemoved = FileUtil.rmdir(workDir, false);
if (!workDirRemoved)
System.err.println("ERROR: Unable to remove Jetty temporary work directory");
boolean workDirCreated = workDir.mkdirs();
if (!workDirCreated)
System.err.println("ERROR: Unable to create Jetty temporary work directory");
_server = new Server();
WebApplicationContext contexts[] = null;
try {

View File

@ -2,6 +2,7 @@ package net.i2p.router.web;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.text.DecimalFormat;
import net.i2p.data.DataHelper;
@ -297,26 +298,27 @@ public class SummaryHelper {
}
private static String getTransferred(long bytes) {
double val = bytes;
int scale = 0;
if (bytes > 1024*1024*1024) {
// gigs transferred
scale = 3;
bytes /= (1024*1024*1024);
val /= (double)(1024*1024*1024);
} else if (bytes > 1024*1024) {
// megs transferred
scale = 2;
bytes /= (1024*1024);
val /= (double)(1024*1024);
} else if (bytes > 1024) {
// kbytes transferred
scale = 1;
bytes /= 1024;
val /= (double)1024;
} else {
scale = 0;
}
DecimalFormat fmt = new DecimalFormat("##0.00");
String str = fmt.format(bytes);
String str = fmt.format(val);
switch (scale) {
case 1: return str + "KB";
case 2: return str + "MB";
@ -333,7 +335,9 @@ public class SummaryHelper {
public String getDestinations() {
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
try {
_context.clientManager().renderStatusHTML(baos);
OutputStreamWriter osw = new OutputStreamWriter(baos);
_context.clientManager().renderStatusHTML(osw);
osw.flush();
return new String(baos.toByteArray());
} catch (IOException ioe) {
_context.logManager().getLog(SummaryHelper.class).error("Error rendering client info", ioe);
@ -396,8 +400,7 @@ public class SummaryHelper {
if (_context == null)
return "0ms";
Rate delayRate = _context.statManager().getRate("transport.sendProcessingTime").getRate(60*1000);
return ((int)delayRate.getAverageValue()) + "ms";
return _context.throttle().getMessageDelay() + "ms";
}
/**
@ -409,7 +412,6 @@ public class SummaryHelper {
if (_context == null)
return "0ms";
Rate lagRate = _context.statManager().getRate("tunnel.testSuccessTime").getRate(10*60*1000);
return ((int)lagRate.getAverageValue()) + "ms";
return _context.throttle().getTunnelLag() + "ms";
}
}

View File

@ -35,8 +35,11 @@
<input name="port" type="text" size="4" value="<jsp:getProperty name="nethelper" property="port" />" /> <br />
<i>The hostname/IP address and TCP port must be reachable from the outside world. If
you are behind a firewall or NAT, this means you must poke a hole for this port. If
you are using DHCP and do not have a static IP address, you must use a service like
<a href="http://dyndns.org/">dyndns</a>. The "guess" functionality makes an HTTP request
you are using DHCP and do not have a static IP address, you should either use a service like
<a href="http://dyndns.org/">dyndns</a> or leave the hostname blank. If you leave it blank,
your router will autodetect the 'correct' IP address by asking a peer (and unconditionally
believing them if the address is routable and you don't have any established connections yet).
The "guess" functionality makes an HTTP request
to <a href="http://www.whatismyip.com/">www.whatismyip.com</a>.</i>
<hr />
<b>Enable internal time synchronization?</b> <input type="checkbox" <jsp:getProperty name="nethelper" property="enableTimeSyncChecked" /> name="enabletimesync" /><br />

View File

@ -29,7 +29,7 @@
<input type="hidden" name="action" value="blah" />
<b>Logging filename:</b>
<input type="text" name="logfilename" size="40" value="<jsp:getProperty name="logginghelper" property="logFilePattern" />" /><br />
<i>(the symbol '#' will be replaced during log rotation)</i><br />
<i>(the symbol '@' will be replaced during log rotation)</i><br />
<b>Log record format:</b>
<input type="text" name="logformat" size="20" value="<jsp:getProperty name="logginghelper" property="recordPattern" />" /><br />
<i>(use 'd' = date, 'c' = class, 't' = thread, 'p' = priority, 'm' = message)</i><br />

View File

@ -16,6 +16,9 @@
<h4>Router logs:</h4>
<jsp:getProperty name="logsHelper" property="logs" />
<hr />
<h4>Connection logs:</h4><a name="connectionlogs"> </a>
<jsp:getProperty name="logsHelper" property="connectionLogs" />
<hr />
<h4>Service logs:</h4><a name="servicelogs"> </a>
<jsp:getProperty name="logsHelper" property="serviceLogs" />
</div>

View File

@ -25,3 +25,7 @@
<jsp:setProperty name="navhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:getProperty name="navhelper" property="clientAppLinks" />
</h4>
<jsp:useBean class="net.i2p.router.web.NoticeHelper" id="noticehelper" scope="request" />
<jsp:setProperty name="noticehelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<b><jsp:getProperty name="noticehelper" property="systemNotice" /></b>

View File

@ -13,6 +13,7 @@
<div class="main" id="main">
<jsp:useBean class="net.i2p.router.web.NetDbHelper" id="netdbHelper" scope="request" />
<jsp:setProperty name="netdbHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="netdbHelper" property="writer" value="<%=out%>" />
<jsp:getProperty name="netdbHelper" property="netDbSummary" />
</div>

View File

@ -12,6 +12,7 @@
<jsp:useBean class="net.i2p.router.web.OldConsoleHelper" id="conhelper" scope="request" />
<jsp:setProperty name="conhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="conhelper" property="writer" value="<%=out%>" />
<div class="main" id="main">
<jsp:getProperty name="conhelper" property="console" />

View File

@ -12,6 +12,7 @@
<jsp:useBean class="net.i2p.router.web.OldConsoleHelper" id="oldhelper" scope="request" />
<jsp:setProperty name="oldhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="oldhelper" property="writer" value="<%=out%>" />
<div class="main" id="main">
<jsp:getProperty name="oldhelper" property="stats" />

View File

@ -28,6 +28,20 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* I2P-Ping won't compile on Windows because Windows lacks getopt()
*/
/*
* Exit values:
* 0: Received at least one response from one dest, or help
* message was successfully displayed
* 1: Received no responses from any dest
* 2: Naming lookup failed, or dest unspecified
* 3: SAM error
*/
#include <getopt.h> /* needed on Gentoo Linux */
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
@ -58,10 +72,11 @@ sam_sid_t laststream = 0;
bool mihi = false;
bool bell = false;
int main(int argc, char* argv[])
int main(int argc, char *argv[])
{
int ch;
int count = INT_MAX; /* number of times to ping */
int pongcount = -1;
char *samhost = "localhost";
uint16_t samport = 7656;
@ -88,7 +103,7 @@ int main(int argc, char* argv[])
quiet = true;
break;
case 'v': /* version */
puts("$Id: i2p-ping.c,v 1.2 2004/07/31 22:20:22 mpc Exp $");
puts("$Id: i2p-ping.c,v 1.4 2004/09/22 20:05:40 jrandom Exp $");
puts("Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>");
break;
case '?':
@ -101,7 +116,7 @@ int main(int argc, char* argv[])
argv += optind;
if (argc == 0) { /* they forgot to specify a ping target */
fprintf(stderr, "Ping who?\n");
return 1;
return 2;
}
/* Hook up the callback functions - required by LibSAM */
@ -120,9 +135,10 @@ int main(int argc, char* argv[])
if (rc != SAM_OK) {
fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc));
sam_session_free(&session);
return 1;
return 3;
}
pongcount = 0;
for (int j = 0; j < argc; j++) {
if (strlen(argv[j]) == 516) {
memcpy(dest, argv[j], SAM_PUBKEY_LEN);
@ -144,6 +160,8 @@ int main(int argc, char* argv[])
time_t finish = time(0);
laststream = 0;
if (laststatus == SAM_OK) {
pongcount++;
if (bell)
printf("\a"); /* putchar() doesn't work for some reason */
if (!mihi)
@ -164,7 +182,7 @@ int main(int argc, char* argv[])
sam_close(session);
sam_session_free(&session);
return 0;
return pongcount == 0 ? 1 : 0;
}
void usage()
@ -178,7 +196,7 @@ void usage()
*/
static void closeback(sam_sess_t *session, sam_sid_t stream_id, samerr_t reason)
{
fprintf(stderr, "Connection closed to stream %d: %s\n", stream_id,
fprintf(stderr, "Connection closed to stream %d: %s\n", (int)stream_id,
sam_strerror(reason));
}
@ -207,7 +225,7 @@ static void databack(sam_sess_t *session, sam_sid_t stream_id, void *data,
static void diedback(sam_sess_t *session)
{
fprintf(stderr, "Lost SAM connection!\n");
exit(1);
exit(3);
}
/*
@ -228,7 +246,7 @@ static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
{
if (result != SAM_OK) {
fprintf(stderr, "Naming lookup failed: %s\n", sam_strerror(result));
exit(1);
exit(2);
}
memcpy(dest, pubkey, SAM_PUBKEY_LEN);
gotdest = true;
@ -238,7 +256,7 @@ static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
* Our connection attempt returned a result
*/
static void statusback(sam_sess_t *session, sam_sid_t stream_id,
samerr_t result)
samerr_t result)
{
laststatus = result;
laststream = stream_id;

View File

@ -55,7 +55,7 @@ static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result);
bool gotdest = false;
sam_pubkey_t dest;
int main(int argc, char* argv[])
int main(int argc, char *argv[])
{
/*
* The target of our attack is specified on the command line
@ -142,7 +142,7 @@ int main(int argc, char* argv[])
* service programs don't need input ;)
*/
static void dgramback(sam_sess_t *session, sam_pubkey_t dest, void *data,
size_t size)
size_t size)
{
puts("Received a datagram (ignored)");
free(data);

View File

@ -0,0 +1,216 @@
package net.i2p.client.streaming;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.i2p.data.ByteArray;
/**
* Stream that can be given messages out of order
* yet present them in order.
*
*/
public class MessageInputStream extends InputStream {
/**
* List of ByteArray objects of data ready to be read,
* with the first ByteArray at index 0, and the next
* actual byte to be read at _readyDataBlockIndex of
* that array.
*
*/
private List _readyDataBlocks;
private int _readyDataBlockIndex;
/** highest message ID used in the readyDataBlocks */
private long _highestReadyBlockId;
/**
* Message ID (Long) to ByteArray for blocks received
* out of order when there are lower IDs not yet
* received
*/
private Map _notYetReadyBlocks;
/**
* if we have received a flag saying there won't be later messages, EOF
* after we have cleared what we have received.
*/
private boolean _closeReceived;
/** if we don't want any more data, ignore the data */
private boolean _locallyClosed;
private int _readTimeout;
private Object _dataLock;
public MessageInputStream() {
_readyDataBlocks = new ArrayList(4);
_readyDataBlockIndex = 0;
_highestReadyBlockId = -1;
_readTimeout = -1;
_notYetReadyBlocks = new HashMap(4);
_dataLock = new Object();
_closeReceived = false;
_locallyClosed = false;
}
/** What is the highest block ID we've completely received through? */
public long getHighestReadyBockId() {
synchronized (_dataLock) {
return _highestReadyBlockId;
}
}
/**
* Ascending list of block IDs greater than the highest
* ready block ID, or null if there aren't any.
*
*/
public long[] getOutOfOrderBlocks() {
long blocks[] = null;
synchronized (_dataLock) {
int num = _notYetReadyBlocks.size();
if (num <= 0) return null;
blocks = new long[num];
int i = 0;
for (Iterator iter = _notYetReadyBlocks.keySet().iterator(); iter.hasNext(); ) {
Long id = (Long)iter.next();
blocks[i] = id.longValue();
i++;
}
}
Arrays.sort(blocks);
return blocks;
}
/** how many blocks have we received that we still have holes before? */
public int getOutOfOrderBlockCount() {
synchronized (_dataLock) {
return _notYetReadyBlocks.size();
}
}
/**
* how long a read() call should block (if less than 0, block indefinitely,
* but if it is 0, do not block at all)
*/
public int getReadTimeout() { return _readTimeout; }
public void setReadTimeout(int timeout) { _readTimeout = timeout; }
/**
* A new message has arrived - toss it on the appropriate queue (moving
* previously pending messages to the ready queue if it fills the gap, etc)
*
*/
public void messageReceived(long messageId, byte payload[]) {
synchronized (_dataLock) {
if (messageId <= _highestReadyBlockId) return; // already received
if (_highestReadyBlockId + 1 == messageId) {
if (!_locallyClosed)
_readyDataBlocks.add(new ByteArray(payload));
_highestReadyBlockId = messageId;
// now pull in any previously pending blocks
while (_notYetReadyBlocks.containsKey(new Long(_highestReadyBlockId + 1))) {
_readyDataBlocks.add(_notYetReadyBlocks.get(new Long(_highestReadyBlockId + 1)));
_highestReadyBlockId++;
}
_dataLock.notifyAll();
} else {
if (_locallyClosed) // dont need the payload, just the msgId in order
_notYetReadyBlocks.put(new Long(messageId), new ByteArray(null));
else
_notYetReadyBlocks.put(new Long(messageId), new ByteArray(payload));
}
}
}
public int read() throws IOException {
if (_locallyClosed) throw new IOException("Already locally closed");
synchronized (_dataLock) {
if (_readyDataBlocks.size() <= 0) {
if ( (_notYetReadyBlocks.size() <= 0) && (_closeReceived) ) {
return -1;
} else {
if (_readTimeout < 0) {
try { _dataLock.wait(); } catch (InterruptedException ie) { }
} else if (_readTimeout > 0) {
try { _dataLock.wait(_readTimeout); } catch (InterruptedException ie) { }
} else { // readTimeout == 0
// noop, don't block
}
if (_readyDataBlocks.size() <= 0) {
throw new InterruptedIOException("Timeout reading");
}
}
}
// either was already ready, or we wait()ed and it arrived
ByteArray cur = (ByteArray)_readyDataBlocks.get(0);
byte rv = cur.getData()[_readyDataBlockIndex++];
if (cur.getData().length <= _readyDataBlockIndex) {
_readyDataBlockIndex = 0;
_readyDataBlocks.remove(0);
}
return (rv < 0 ? rv + 256 : rv);
}
}
public int available() throws IOException {
if (_locallyClosed) throw new IOException("Already closed, you wanker");
synchronized (_dataLock) {
if (_readyDataBlocks.size() <= 0)
return 0;
int numBytes = 0;
for (int i = 0; i < _readyDataBlocks.size(); i++) {
ByteArray cur = (ByteArray)_readyDataBlocks.get(i);
if (i == 0)
numBytes += cur.getData().length - _readyDataBlockIndex;
else
numBytes += cur.getData().length;
}
return numBytes;
}
}
/**
* How many bytes are queued up for reading (or sitting in the out-of-order
* buffer)?
*
*/
public int getTotalQueuedSize() {
synchronized (_dataLock) {
if (_locallyClosed) return 0;
int numBytes = 0;
for (int i = 0; i < _readyDataBlocks.size(); i++) {
ByteArray cur = (ByteArray)_readyDataBlocks.get(i);
if (i == 0)
numBytes += cur.getData().length - _readyDataBlockIndex;
else
numBytes += cur.getData().length;
}
for (Iterator iter = _notYetReadyBlocks.values().iterator(); iter.hasNext(); ) {
ByteArray cur = (ByteArray)iter.next();
numBytes += cur.getData().length;
}
return numBytes;
}
}
public void close() {
synchronized (_dataLock) {
_readyDataBlocks.clear();
// we don't need the data, but we do need to keep track of the messageIds
// received, so we can ACK accordingly
for (Iterator iter = _notYetReadyBlocks.values().iterator(); iter.hasNext(); ) {
ByteArray ba = (ByteArray)iter.next();
ba.setData(null);
}
_locallyClosed = true;
}
}
}

View File

@ -0,0 +1,95 @@
package net.i2p.client.streaming;
import java.io.IOException;
import java.io.OutputStream;
/**
*
*/
public class MessageOutputStream extends OutputStream {
private byte _buf[];
private int _valid;
private Object _dataLock;
private DataReceiver _dataReceiver;
private IOException _streamError;
public MessageOutputStream(DataReceiver receiver) {
this(receiver, 64*1024);
}
public MessageOutputStream(DataReceiver receiver, int bufSize) {
super();
_buf = new byte[bufSize];
_dataReceiver = receiver;
_dataLock = new Object();
}
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
public void write(byte b[], int off, int len) throws IOException {
synchronized (_dataLock) {
int remaining = len;
while (remaining > 0) {
if (_valid + remaining < _buf.length) {
// simply buffer the data, no flush
System.arraycopy(b, off, _buf, _valid, remaining);
remaining = 0;
} else {
// buffer whatever we can fit then flush,
// repeating until we've pushed all of the
// data through
int toWrite = _buf.length - _valid;
System.arraycopy(b, off, _buf, _valid, toWrite);
remaining -= toWrite;
_valid = _buf.length;
_dataReceiver.writeData(_buf, 0, _valid);
_valid = 0;
throwAnyError();
}
}
}
throwAnyError();
}
public void write(int b) throws IOException {
write(new byte[] { (byte)b }, 0, 1);
throwAnyError();
}
public void flush() throws IOException {
synchronized (_dataLock) {
_dataReceiver.writeData(_buf, 0, _valid);
_valid = 0;
}
throwAnyError();
}
private void throwAnyError() throws IOException {
if (_streamError != null) {
IOException ioe = _streamError;
_streamError = null;
throw ioe;
}
}
void streamErrorOccurred(IOException ioe) {
_streamError = ioe;
}
/**
* called whenever the engine wants to push more data to the
* peer
*
*/
void flushAvailable(DataReceiver target) {
synchronized (_dataLock) {
target.writeData(_buf, 0, _valid);
_valid = 0;
}
}
public interface DataReceiver {
public void writeData(byte buf[], int off, int size);
}
}

View File

@ -0,0 +1,389 @@
package net.i2p.client.streaming;
import java.util.Arrays;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.Signature;
import net.i2p.data.SigningPrivateKey;
/**
* Contain a single packet transferred as part of a streaming connection.
* The data format is as follows:<ul>
* <li>{@see #getSendStreamId sendStreamId} [4 byte value]</li>
* <li>{@see #getReceiveStreamId receiveStreamId} [4 byte value]</li>
* <li>{@see #getSequenceNum sequenceNum} [4 byte unsigned integer]</li>
* <li>{@see #getAckThrough ackThrough} [4 byte unsigned integer]</li>
* <li>number of NACKs [1 byte unsigned integer]</li>
* <li>that many {@see #getNacks NACKs}</li>
* <li>{@see #getResendDelay resendDelay} [1 byte integer]</li>
* <li>flags [2 byte value]</li>
* <li>option data size [2 byte integer]</li>
* <li>option data specified by those flags [0 or more bytes]</li>
* <li>payload [remaining packet size]</li>
* </ul>
*
* <p>The flags field above specifies some metadata about the packet, and in
* turn may require certain additional data to be included. The flags are
* as follows (with any data structures specified added to the options area
* in the given order):</p><ol>
* <li>{@see #FLAG_SYNCHRONIZE}: no option data</li>
* <li>{@see #FLAG_CLOSE}: no option data</li>
* <li>{@see #FLAG_RESET}: no option data</li>
* <li>{@see #FLAG_SIGNATURE_INCLUDED}: {@see net.i2p.data.Signature}</li>
* <li>{@see #FLAG_SIGNATURE_REQUESTED}: no option data</li>
* <li>{@see #FLAG_FROM_INCLUDED}: {@see net.i2p.data.Destination}</li>
* <li>{@see #FLAG_DELAY_REQUESTED}: 1 byte integer</li>
* <li>{@see #FLAG_MAX_PACKET_SIZE_INCLUDED}: 2 byte integer</li>
* <li>{@see #FLAG_PROFILE_INTERACTIVE}: no option data</li>
* </ol>
*
* <p>If the signature is included, it uses the Destination's DSA key
* to sign the entire header and payload with the space in the options
* for the signature being set to all zeroes.</p>
*
*/
public class Packet {
private byte _sendStreamId[];
private byte _receiveStreamId[];
private long _sequenceNum;
private long _ackThrough;
private long _nacks[];
private int _resendDelay;
private int _flags;
private byte _payload[];
// the next four are set only if the flags say so
private Signature _optionSignature;
private Destination _optionFrom;
private int _optionDelay;
private int _optionMaxSize;
/**
* The receiveStreamId will be set to this when the packet doesn't know
* what ID will be assigned by the remote peer (aka this is the initial
* synchronize packet)
*
*/
public static final byte RECEIVE_STREAM_ID_UNKNOWN[] = new byte[] { 0x00, 0x00, 0x00, 0x00 };
/**
* This packet is creating a new socket connection (if the receiveStreamId
* is RECEIVE_STREAM_ID_UNKNOWN) or it is acknowledging a request to
* create a connection and in turn is accepting the socket.
*
*/
public static final int FLAG_SYNCHRONIZE = (1 << 0);
/**
* The sender of this packet will not be sending any more payload data.
*/
public static final int FLAG_CLOSE = (1 << 1);
/**
* This packet is being sent to signify that the socket does not exist
* (or, if in response to an initial synchronize packet, that the
* connection was refused).
*
*/
public static final int FLAG_RESET = (1 << 2);
/**
* This packet contains a DSA signature from the packet's sender. This
* signature is within the packet options. All synchronize packets must
* have this flag set.
*
*/
public static final int FLAG_SIGNATURE_INCLUDED = (1 << 3);
/**
* This packet wants the recipient to include signatures on subsequent
* packets sent to the creator of this packet.
*/
public static final int FLAG_SIGNATURE_REQUESTED = (1 << 4);
/**
* This packet includes the full I2P destination of the packet's sender.
* The initial synchronize packet must have this flag set.
*/
public static final int FLAG_FROM_INCLUDED = (1 << 5);
/**
* This packet includes an explicit request for the recipient to delay
* sending any packets with data for a given amount of time.
*
*/
public static final int FLAG_DELAY_REQUESTED = (1 << 6);
/**
* This packet includes a request that the recipient not send any
* subsequent packets with payloads greater than a specific size.
* If not set and no prior value was delivered, the maximum value
* will be assumed (approximately 32KB).
*
*/
public static final int FLAG_MAX_PACKET_SIZE_INCLUDED = (1 << 7);
/**
* If set, this packet is travelling as part of an interactive flow,
* meaning it is more lag sensitive than throughput sensitive. aka
* send data ASAP rather than waiting around to send full packets.
*
*/
public static final int FLAG_PROFILE_INTERACTIVE = (1 << 8);
/** what stream is this packet a part of? */
public byte[] getSendStreamId() { return _sendStreamId; }
public void setSendStreamId(byte[] id) { _sendStreamId = id; }
/**
* what is the stream replies should be sent on? if the
* connection is still being built, this should be
* {@see #RECEIVE_STREAM_ID_UNKNOWN}.
*
*/
public byte[] getReceiveStreamId() { return _receiveStreamId; }
public void setReceiveStreamId(byte[] id) { _receiveStreamId = id; }
/** 0-indexed sequence number for this Packet in the sendStream */
public long getSequenceNum() { return _sequenceNum; }
public void setSequenceNum(long num) { _sequenceNum = num; }
/**
* what is the highest packet sequence number that received
* on the receiveStreamId? This field is ignored on the initial
* connection packet (where receiveStreamId is the unknown id).
*
*/
public long getAckThrough() { return _ackThrough; }
public void setAckThrough(long id) { _ackThrough = id; }
/**
* What packet sequence numbers below the getAckThrough() value
* have not been received? this may be null.
*
*/
public long[] getNacks() { return _nacks; }
public void setNacks(long nacks[]) { _nacks = nacks; }
/**
* How long is the creator of this packet going to wait before
* resending this packet (if it hasn't yet been ACKed). The
* value is seconds since the packet was created.
*
*/
public int getResendDelay() { return _resendDelay; }
public void setResendDelay(int numSeconds) { _resendDelay = numSeconds; }
/** get the actual payload of the message. may be null */
public byte[] getPayload() { return _payload; }
public void setPayload(byte payload[]) { _payload = payload; }
/** is a particular flag set on this packet? */
public boolean isFlagSet(int flag) { return 0 != (_flags & flag); }
public void setFlag(int flag) { _flags |= flag; }
/** the signature on the packet (only included if the flag for it is set) */
public Signature getOptionalSignature() { return _optionSignature; }
public void setOptionalSignature(Signature sig) { _optionSignature = sig; }
/** the sender of the packet (only included if the flag for it is set) */
public Destination getOptionalFrom() { return _optionFrom; }
public void setOptionalFrom(Destination from) { _optionFrom = from; }
/**
* How many milliseconds the sender of this packet wants the recipient
* to wait before sending any more data (only valid if the flag for it is
* set)
*/
public int getOptionalDelay() { return _optionDelay; }
public void setOptionalDelay(int delayMs) { _optionDelay = delayMs; }
/**
* What is the largest payload the sender of this packet wants to receive?
*
*/
public int getOptionalMaxSize() { return _optionMaxSize; }
public void setOptionalMaxSize(int numBytes) { _optionMaxSize = numBytes; }
/**
* Write the packet to the buffer (starting at the offset) and return
* the number of bytes written.
*
* @throws IllegalStateException if there is data missing or otherwise b0rked
*/
public int writePacket(byte buffer[], int offset) throws IllegalStateException {
return writePacket(buffer, offset, true);
}
/**
* @param includeSig if true, include the real signature, otherwise put zeroes
* in its place.
*/
private int writePacket(byte buffer[], int offset, boolean includeSig) throws IllegalStateException {
int cur = offset;
System.arraycopy(_sendStreamId, 0, buffer, cur, _sendStreamId.length);
cur += _sendStreamId.length;
System.arraycopy(_receiveStreamId, 0, buffer, cur, _receiveStreamId.length);
cur += _receiveStreamId.length;
DataHelper.toLong(buffer, cur, 4, _sequenceNum);
cur += 4;
DataHelper.toLong(buffer, cur, 4, _ackThrough);
cur += 4;
if (_nacks != null) {
DataHelper.toLong(buffer, cur, 1, _nacks.length);
cur++;
for (int i = 0; i < _nacks.length; i++) {
DataHelper.toLong(buffer, cur, 4, _nacks[i]);
cur += 4;
}
} else {
DataHelper.toLong(buffer, cur, 1, 0);
cur++;
}
DataHelper.toLong(buffer, cur, 1, _resendDelay);
cur++;
DataHelper.toLong(buffer, cur, 2, _flags);
cur += 2;
int optionSize = 0;
if (isFlagSet(FLAG_DELAY_REQUESTED))
optionSize += 1;
if (isFlagSet(FLAG_FROM_INCLUDED))
optionSize += _optionFrom.size();
if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED))
optionSize += 2;
if (isFlagSet(FLAG_SIGNATURE_INCLUDED))
optionSize += Signature.SIGNATURE_BYTES;
DataHelper.toLong(buffer, cur, 2, optionSize);
cur += 2;
if (isFlagSet(FLAG_DELAY_REQUESTED)) {
DataHelper.toLong(buffer, cur, 1, _optionDelay);
cur++;
}
if (isFlagSet(FLAG_FROM_INCLUDED)) {
cur += _optionFrom.writeBytes(buffer, cur);
}
if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED)) {
DataHelper.toLong(buffer, cur, 2, _optionMaxSize);
cur += 2;
}
if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
if (includeSig)
System.arraycopy(_optionSignature.getData(), 0, buffer, cur, Signature.SIGNATURE_BYTES);
else // we're signing (or validating)
Arrays.fill(buffer, cur, Signature.SIGNATURE_BYTES, (byte)0x0);
cur += Signature.SIGNATURE_BYTES;
}
if (_payload != null) {
System.arraycopy(_payload, 0, buffer, cur, _payload.length);
cur += _payload.length;
}
return cur - offset;
}
/**
* Read the packet from the buffer (starting at the offset) and return
* the number of bytes read.
*
* @param buffer packet buffer containing the data
* @param offset index into the buffer to start readign
* @param length how many bytes within the buffer past the offset are
* part of the packet?
*
* @throws IllegalArgumentException if the data is b0rked
*/
public void readPacket(byte buffer[], int offset, int length) throws IllegalArgumentException {
int cur = offset;
_sendStreamId = new byte[4];
System.arraycopy(buffer, cur, _sendStreamId, 0, 4);
cur += 4;
_receiveStreamId = new byte[4];
System.arraycopy(buffer, cur, _receiveStreamId, 0, 4);
cur += 4;
_sequenceNum = DataHelper.fromLong(buffer, cur, 4);
cur += 4;
_ackThrough = DataHelper.fromLong(buffer, cur, 4);
cur += 4;
int numNacks = (int)DataHelper.fromLong(buffer, cur, 1);
cur++;
if (numNacks > 0) {
_nacks = new long[numNacks];
for (int i = 0; i < numNacks; i++) {
_nacks[i] = DataHelper.fromLong(buffer, cur, 4);
cur += 4;
}
} else {
_nacks = null;
}
_resendDelay = (int)DataHelper.fromLong(buffer, cur, 1);
cur++;
_flags = (int)DataHelper.fromLong(buffer, cur, 2);
cur += 2;
int optionSize = (int)DataHelper.fromLong(buffer, cur, 2);
cur += 2;
int payloadBegin = cur + optionSize;
// skip ahead to the payload
_payload = new byte[offset + length - payloadBegin];
System.arraycopy(buffer, payloadBegin, _payload, 0, _payload.length);
// ok now lets go back and deal with the options
if (isFlagSet(FLAG_DELAY_REQUESTED)) {
_optionDelay = (int)DataHelper.fromLong(buffer, cur, 1);
cur++;
}
if (isFlagSet(FLAG_FROM_INCLUDED)) {
_optionFrom = new Destination();
cur += _optionFrom.readBytes(buffer, cur);
}
if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED)) {
_optionMaxSize = (int)DataHelper.fromLong(buffer, cur, 2);
cur += 2;
}
if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
Signature sig = new Signature();
byte buf[] = new byte[Signature.SIGNATURE_BYTES];
System.arraycopy(buffer, cur, buf, 0, Signature.SIGNATURE_BYTES);
sig.setData(buf);
cur += Signature.SIGNATURE_BYTES;
}
}
/**
* Determine whether the signature on the data is valid.
*
* @return true if the signature exists and validates against the data,
* false otherwise.
*/
public boolean verifySignature(I2PAppContext ctx, Destination from, byte buffer[]) {
if (!isFlagSet(FLAG_SIGNATURE_INCLUDED)) return false;
if (_optionSignature == null) return false;
int size = writePacket(buffer, 0, false);
return ctx.dsa().verifySignature(_optionSignature, buffer, 0, size, from.getSigningPublicKey());
}
/**
* Sign and write the packet to the buffer (starting at the offset) and return
* the number of bytes written.
*
* @throws IllegalStateException if there is data missing or otherwise b0rked
*/
public int writeSignedPacket(byte buffer[], int offset, I2PAppContext ctx, SigningPrivateKey key) throws IllegalStateException {
setFlag(FLAG_SIGNATURE_INCLUDED);
int size = writePacket(buffer, offset, false);
_optionSignature = ctx.dsa().sign(buffer, offset, size, key);
// jump into the signed data and inject the signature where we
// previously placed a bunch of zeroes
int signatureOffset = offset
+ 4 // sendStreamId
+ 4 // receiveStreamId
+ 4 // sequenceNum
+ 4 // ackThrough
+ (_nacks != null ? 4*_nacks.length + 1 : 1)
+ 1 // resendDelay
+ 2 // flags
+ 2 // optionSize
+ (isFlagSet(FLAG_DELAY_REQUESTED) ? 1 : 0)
+ (isFlagSet(FLAG_FROM_INCLUDED) ? _optionFrom.size() : 0)
+ (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED) ? 2 : 0);
System.arraycopy(_optionSignature.getData(), 0, buffer, signatureOffset, Signature.SIGNATURE_BYTES);
return size;
}
}

View File

@ -0,0 +1,90 @@
package net.i2p.client.streaming;
import java.io.InputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
/**
* Stress test the MessageInputStream
*/
public class MessageInputStreamTest {
private I2PAppContext _context;
private Log _log;
public MessageInputStreamTest() {
_context = I2PAppContext.getGlobalContext();
_log = _context.logManager().getLog(MessageInputStreamTest.class);
}
public void testInOrder() {
byte orig[] = new byte[32*1024];
_context.random().nextBytes(orig);
MessageInputStream in = new MessageInputStream();
for (int i = 0; i < 32; i++) {
byte msg[] = new byte[1024];
System.arraycopy(orig, i*1024, msg, 0, 1024);
in.messageReceived(i, msg);
}
byte read[] = new byte[32*1024];
try {
int howMany = DataHelper.read(in, read);
if (howMany != orig.length)
throw new RuntimeException("Failed test: not enough bytes read [" + howMany + "]");
if (!DataHelper.eq(orig, read))
throw new RuntimeException("Failed test: data read is not equal");
_log.info("Passed test: in order");
} catch (IOException ioe) {
throw new RuntimeException("IOError reading: " + ioe.getMessage());
}
}
public void testRandomOrder() {
byte orig[] = new byte[32*1024];
_context.random().nextBytes(orig);
MessageInputStream in = new MessageInputStream();
ArrayList order = new ArrayList(32);
for (int i = 0; i < 32; i++)
order.add(new Integer(i));
Collections.shuffle(order);
for (int i = 0; i < 32; i++) {
byte msg[] = new byte[1024];
Integer cur = (Integer)order.get(i);
System.arraycopy(orig, cur.intValue()*1024, msg, 0, 1024);
in.messageReceived(cur.intValue(), msg);
_log.debug("Injecting " + cur);
}
byte read[] = new byte[32*1024];
try {
int howMany = DataHelper.read(in, read);
if (howMany != orig.length)
throw new RuntimeException("Failed test: not enough bytes read [" + howMany + "]");
if (!DataHelper.eq(orig, read))
throw new RuntimeException("Failed test: data read is not equal");
_log.info("Passed test: random order");
} catch (IOException ioe) {
throw new RuntimeException("IOError reading: " + ioe.getMessage());
}
}
public static void main(String args[]) {
MessageInputStreamTest t = new MessageInputStreamTest();
try {
t.testInOrder();
t.testRandomOrder();
} catch (Exception e) {
e.printStackTrace();
}
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
}

View File

@ -49,7 +49,7 @@ public class SysTray implements SysTrayMenuListener {
if (!(new File("router.config")).exists())
openRouterConsole("http://localhost:" + _portString + "/index.jsp");
if (System.getProperty("os.name").startsWith("Windows"))
if ( (System.getProperty("os.name").startsWith("Windows")) && (!Boolean.getBoolean("systray.disable")) )
_instance = new SysTray();
}

View File

@ -6,7 +6,7 @@
<echo message=" dist: distclean then package everything up (installer, clean tarball, update tarball)" />
<echo message=" installer: build the GUI installer" />
<echo message=" tarball: tar the full install into i2p.tar.bz2 (extracts to build a new clean install)" />
<echo message=" updater: tar the built i2p specific files into an i2pupdate.tar.bz2 (extracts safely over existing installs)" />
<echo message=" updater: tar the built i2p specific files into an i2pupdate.zip (extracts safely over existing installs)" />
<echo message=" distclean: clean up all derived files" />
<echo message=" javadoc: generate javadoc for the entire project into ./build/javadoc" />
</target>
@ -57,7 +57,7 @@
<target name="javadoc">
<mkdir dir="./build" />
<mkdir dir="./build/javadoc" />
<javadoc
<javadoc access="package"
destdir="./build/javadoc"
packagenames="*"
use="true"
@ -143,7 +143,7 @@
<target name="pkgclean">
<delete dir="pkg-temp" />
<delete>
<fileset dir="." includes="i2p.tar.bz2 install.jar i2pupdate.tar.bz2" />
<fileset dir="." includes="i2p.tar.bz2 install.jar i2pupdate.zip" />
</delete>
</target>
<target name="preppkg" depends="build">
@ -207,8 +207,12 @@
<copy file="history.txt" todir="pkg-temp/" />
<mkdir dir="pkg-temp/docs" />
<copy file="readme.html" todir="pkg-temp/docs/" />
<mkdir dir="pkg-temp/work" />
<touch file="pkg-temp/work/ignore.this" />
<mkdir dir="pkg-temp/eepsite" />
<mkdir dir="pkg-temp/eepsite/webapps" />
<mkdir dir="pkg-temp/eepsite/logs" />
<mkdir dir="pkg-temp/eepsite/docroot" />
<copy file="installer/resources/eepsite_index.html" tofile="pkg-temp/eepsite/docroot/index.html" />
<copy file="installer/resources/jetty.xml" tofile="pkg-temp/eepsite/jetty.xml" />
</target>
<target name="tarball" depends="preppkg">
<tar compression="bzip2" destfile="i2p.tar.bz2">
@ -217,24 +221,18 @@
</target>
<target name="updater" depends="build">
<delete dir="pkg-temp" />
<copy file="build/heartbeat.jar" todir="pkg-temp/lib/" />
<copy file="build/i2p.jar" todir="pkg-temp/lib/" />
<copy file="build/i2ptunnel.jar" todir="pkg-temp/lib/" />
<copy file="build/mstreaming.jar" todir="pkg-temp/lib/" />
<copy file="build/router.jar" todir="pkg-temp/lib/" />
<copy file="build/routerconsole.jar" todir="pkg-temp/lib/" />
<copy file="build/sam.jar" todir="pkg-temp/lib/" />
<copy file="build/systray.jar" todir="pkg-temp/lib" />
<copy file="build/i2ptunnel.war" todir="pkg-temp/webapps/" />
<copy file="build/routerconsole.war" todir="pkg-temp/webapps/" />
<copy file="history.txt" todir="pkg-temp/" />
<copy file="hosts.txt" todir="pkg-temp/" />
<copy file="installer/resources/install_i2p_service_winnt.bat" todir="pkg-temp/" />
<copy file="installer/resources/uninstall_i2p_service_winnt.bat" todir="pkg-temp/" />
<copy file="installer/resources/i2prouter" todir="pkg-temp/" />
<copy file="installer/resources/i2prouter.bat" todir="pkg-temp/" />
<copy file="installer/resources/i2prouter_win9x.bat" todir="pkg-temp/" />
<copy file="installer/resources/wrapper.config" todir="pkg-temp/" />
<mkdir dir="pkg-temp/eepsite" />
<mkdir dir="pkg-temp/eepsite/webapps" />
<copy file="installer/resources/jetty.xml" tofile="pkg-temp/eepsite/jetty.xml" />
<zip destfile="i2pupdate.zip" basedir="pkg-temp" />
</target>
<taskdef name="izpack" classpath="${basedir}/installer/lib/izpack/standalone-compiler.jar" classname="com.izforge.izpack.ant.IzPackTask" />

View File

@ -14,8 +14,8 @@ package net.i2p;
*
*/
public class CoreVersion {
public final static String ID = "$Revision: 1.19 $ $Date: 2004/09/03 14:46:07 $";
public final static String VERSION = "0.4.0.1";
public final static String ID = "$Revision: 1.23 $ $Date: 2004/10/10 14:33:08 $";
public final static String VERSION = "0.4.1.3";
public static void main(String args[]) {
System.out.println("I2P Core version: " + VERSION);

View File

@ -284,7 +284,7 @@ public class I2PAppContext {
* matter. Though for the crazy people out there, we do expose a way to
* disable it.
*/
public AESEngine AESEngine() {
public AESEngine aes() {
if (!_AESEngineInitialized) initializeAESEngine();
return _AESEngine;
}

View File

@ -38,6 +38,7 @@ public interface I2PSession {
* @return whether it was accepted by the router for delivery or not
*/
public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException;
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size) throws I2PSessionException;
/**
* Like sendMessage above, except the key used and the tags sent are exposed to the
@ -66,8 +67,8 @@ public interface I2PSession {
* the contents of the set is ignored during the call, but afterwards it contains a set of SessionTag
* objects that were sent along side the given keyUsed.
*/
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent)
throws I2PSessionException;
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException;
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent) throws I2PSessionException;
/** Receive a message that the router has notified the client about, returning
* the payload.

View File

@ -580,7 +580,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
while (true) {
long delay = BASE_RECONNECT_DELAY << i;
i++;
if (delay > MAX_RECONNECT_DELAY)
if ( (delay > MAX_RECONNECT_DELAY) || (delay <= 0) )
delay = MAX_RECONNECT_DELAY;
try { Thread.sleep(delay); } catch (InterruptedException ie) {}

View File

@ -62,13 +62,21 @@ class I2PSessionImpl2 extends I2PSessionImpl {
}
public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException {
return sendMessage(dest, payload, new SessionKey(), new HashSet(64));
return sendMessage(dest, payload, 0, payload.length);
}
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size) throws I2PSessionException {
return sendMessage(dest, payload, offset, size, new SessionKey(), new HashSet(64));
}
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent)
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException {
return sendMessage(dest, payload, 0, payload.length, keyUsed, tagsSent);
}
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent)
throws I2PSessionException {
if (isClosed()) throw new I2PSessionException("Already closed");
if (SHOULD_COMPRESS) payload = DataHelper.compress(payload);
if (SHOULD_COMPRESS) payload = DataHelper.compress(payload, offset, size);
else throw new IllegalStateException("we need to update sendGuaranteed to support partial send");
// we always send as guaranteed (so we get the session keys/tags acked),
// but only block until the appropriate event has been reached (guaranteed
// success or accepted). we may want to break this out into a seperate

View File

@ -33,7 +33,6 @@ import net.i2p.util.Log;
* @author jrandom
*/
class RequestLeaseSetMessageHandler extends HandlerImpl {
private final static Log _log = new Log(RequestLeaseSetMessageHandler.class);
private Map _existingLeaseSets;
public RequestLeaseSetMessageHandler(I2PAppContext context) {

View File

@ -10,7 +10,10 @@ package net.i2p.client.naming;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.data.Destination;
@ -34,42 +37,58 @@ public class HostsTxtNamingService extends NamingService {
* If this system property is specified, the tunnel will read the
* given file for hostname=destKey values when resolving names
*/
public final static String PROP_HOSTS_FILE = "i2p.hostsfile";
public final static String PROP_HOSTS_FILE = "i2p.hostsfilelist";
/** default hosts.txt filename */
public final static String DEFAULT_HOSTS_FILE = "hosts.txt";
public final static String DEFAULT_HOSTS_FILE = "userhosts.txt,hosts.txt";
private final static Log _log = new Log(HostsTxtNamingService.class);
private List getFilenames() {
String list = _context.getProperty(PROP_HOSTS_FILE, DEFAULT_HOSTS_FILE);
StringTokenizer tok = new StringTokenizer(list, ",");
List rv = new ArrayList(tok.countTokens());
while (tok.hasMoreTokens())
rv.add(tok.nextToken());
return rv;
}
public Destination lookup(String hostname) {
// Try to look it up in hosts.txt
// Reload file each time to catch changes.
// (and it's easier :P
String hostsfile = _context.getProperty(PROP_HOSTS_FILE, DEFAULT_HOSTS_FILE);
Properties hosts = new Properties();
FileInputStream fis = null;
try {
File f = new File(hostsfile);
if (f.canRead()) {
fis = new FileInputStream(f);
hosts.load(fis);
} else {
_log.error("Hosts file " + hostsfile + " does not exist.");
}
} catch (Exception ioe) {
_log.error("Error loading hosts file " + hostsfile, ioe);
} finally {
if (fis != null) try {
fis.close();
} catch (IOException ioe) { // nop
// check the list each time, reloading the file on each
// lookup
List filenames = getFilenames();
for (int i = 0; i < filenames.size(); i++) {
String hostsfile = (String)filenames.get(i);
Properties hosts = new Properties();
FileInputStream fis = null;
try {
File f = new File(hostsfile);
if ( (f.exists()) && (f.canRead()) ) {
fis = new FileInputStream(f);
hosts.load(fis);
String key = hosts.getProperty(hostname);
if ( (key != null) && (key.trim().length() > 0) ) {
return lookupBase64(key);
}
} else {
_log.warn("Hosts file " + hostsfile + " does not exist.");
}
} catch (Exception ioe) {
_log.error("Error loading hosts file " + hostsfile, ioe);
} finally {
if (fis != null) try {
fis.close();
} catch (IOException ioe) { // nop
}
}
// not found, continue to the next file
}
String res = hosts.getProperty(hostname);
// If we can't find name in hosts, assume it's a key.
if ((res == null) || (res.trim().length() == 0)) {
res = hostname;
}
return lookupBase64(res);
// If we can't find name in any of the hosts files,
// assume it's a key.
return lookupBase64(hostname);
}
public String reverseLookup(Destination dest) {

View File

@ -37,28 +37,23 @@ public class AESEngine {
/** Encrypt the payload with the session key
* @param payload data to be encrypted
* @param payloadIndex index into the payload to start encrypting
* @param out where to store the result
* @param outIndex where in out to start writing
* @param sessionKey private esession key to encrypt to
* @param initializationVector IV for CBC
* @return encrypted data
* @param iv IV for CBC
* @param length how much data to encrypt
*/
public byte[] encrypt(byte payload[], SessionKey sessionKey, byte initializationVector[]) {
if ((initializationVector == null) || (payload == null) || (sessionKey == null)
|| (initializationVector.length != 16)) return null;
byte cyphertext[] = null;
if ((payload.length % 16) == 0)
cyphertext = new byte[payload.length];
else
cyphertext = new byte[payload.length + (16 - (payload.length % 16))];
System.arraycopy(payload, 0, cyphertext, 0, payload.length);
return cyphertext;
public void encrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int length) {
System.arraycopy(payload, payloadIndex, out, outIndex, length);
_log.warn("Warning: AES is disabled");
}
public byte[] safeEncrypt(byte payload[], SessionKey sessionKey, byte iv[], int paddedSize) {
if ((iv == null) || (payload == null) || (sessionKey == null) || (iv.length != 16)) return null;
ByteArrayOutputStream baos = new ByteArrayOutputStream(paddedSize + 64);
Hash h = _context.sha().calculateHash(sessionKey.getData());
Hash h = _context.sha().calculateHash(iv);
try {
h.writeBytes(baos);
DataHelper.writeLong(baos, 4, payload.length);
@ -72,19 +67,23 @@ public class AESEngine {
_log.error("Error writing data", dfe);
return null;
}
return encrypt(baos.toByteArray(), sessionKey, iv);
byte orig[] = baos.toByteArray();
byte rv[] = new byte[orig.length];
encrypt(orig, 0, rv, 0, sessionKey, iv, rv.length);
return rv;
}
public byte[] safeDecrypt(byte payload[], SessionKey sessionKey, byte iv[]) {
if ((iv == null) || (payload == null) || (sessionKey == null) || (iv.length != 16)) return null;
byte decr[] = decrypt(payload, sessionKey, iv);
byte decr[] = new byte[payload.length];
decrypt(payload, 0, decr, 0, sessionKey, iv, payload.length);
if (decr == null) {
_log.error("Error decrypting the data - payload " + payload.length + " decrypted to null");
return null;
}
ByteArrayInputStream bais = new ByteArrayInputStream(decr);
Hash h = _context.sha().calculateHash(sessionKey.getData());
Hash h = _context.sha().calculateHash(iv);
try {
Hash rh = new Hash();
rh.readBytes(bais);
@ -110,19 +109,19 @@ public class AESEngine {
}
}
/** decrypt the data with the session key provided
* @param cyphertext encrypted data
* @param sessionKey private session key
* @param initializationVector IV for CBC
* @return unencrypted data
*/
public byte[] decrypt(byte cyphertext[], SessionKey sessionKey, byte initializationVector[]) {
if ((initializationVector == null) || (cyphertext == null) || (sessionKey == null)
|| (initializationVector.length != 16)) return null;
byte payload[] = new byte[cyphertext.length];
/** Decrypt the data with the session key
* @param payload data to be decrypted
* @param payloadIndex index into the payload to start decrypting
* @param out where to store the cleartext
* @param outIndex where in out to start writing
* @param sessionKey private session key to decrypt to
* @param iv IV for CBC
* @param length how much data to decrypt
*/
public void decrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int length) {
System.arraycopy(payload, payloadIndex, out, outIndex, length);
_log.warn("Warning: AES is disabled");
return cyphertext;
}
public static void main(String args[]) {
@ -133,14 +132,16 @@ public class AESEngine {
byte sbuf[] = new byte[16];
RandomSource.getInstance().nextBytes(sbuf);
byte se[] = ctx.AESEngine().encrypt(sbuf, key, iv);
byte sd[] = ctx.AESEngine().decrypt(se, key, iv);
byte se[] = new byte[16];
ctx.aes().encrypt(sbuf, 0, se, 0, key, iv, sbuf.length);
byte sd[] = new byte[16];
ctx.aes().decrypt(se, 0, sd, 0, key, iv, se.length);
ctx.logManager().getLog(AESEngine.class).debug("Short test: " + DataHelper.eq(sd, sbuf));
byte lbuf[] = new byte[1024];
RandomSource.getInstance().nextBytes(sbuf);
byte le[] = ctx.AESEngine().safeEncrypt(lbuf, key, iv, 2048);
byte ld[] = ctx.AESEngine().safeDecrypt(le, key, iv);
byte le[] = ctx.aes().safeEncrypt(lbuf, key, iv, 2048);
byte ld[] = ctx.aes().safeDecrypt(le, key, iv);
ctx.logManager().getLog(AESEngine.class).debug("Long test: " + DataHelper.eq(ld, lbuf));
}
}

View File

@ -46,66 +46,81 @@ public class AESInputStream extends FilterInputStream {
private long _cumulativePrepared; // how many bytes decrypted and added to _readyBuf
private long _cumulativePaddingStripped; // how many bytes have been stripped
private ByteArrayOutputStream _encryptedBuf; // read from the stream but not yet decrypted
private List _readyBuf; // list of Bytes ready to be consumed, where index 0 is the first
/** read but not yet decrypted */
private byte _encryptedBuf[];
/** how many bytes have been added to the encryptedBuf since it was decrypted? */
private int _writesSinceDecrypt;
/** decrypted bytes ready for reading (first available == index of 0) */
private int _decryptedBuf[];
/** how many bytes are available for reading without decrypt? */
private int _decryptedSize;
private final static int BLOCK_SIZE = CryptixRijndael_Algorithm._BLOCK_SIZE;
private final static int READ_SIZE = BLOCK_SIZE;
private final static int DECRYPT_SIZE = BLOCK_SIZE - 1;
public AESInputStream(I2PAppContext context, InputStream source, SessionKey key, byte iv[]) {
public AESInputStream(I2PAppContext context, InputStream source, SessionKey key, byte[] iv) {
super(source);
_context = context;
_log = context.logManager().getLog(AESInputStream.class);
_key = key;
_lastBlock = new byte[BLOCK_SIZE];
System.arraycopy(iv, 0, _lastBlock, 0, BLOCK_SIZE);
_encryptedBuf = new ByteArrayOutputStream(BLOCK_SIZE);
_readyBuf = new ArrayList(1024);
_encryptedBuf = new byte[BLOCK_SIZE];
_writesSinceDecrypt = 0;
_decryptedBuf = new int[BLOCK_SIZE-1];
_decryptedSize = 0;
_cumulativePaddingStripped = 0;
_eofFound = false;
}
public int read() throws IOException {
while ((!_eofFound) && (_readyBuf.size() <= 0)) {
refill(READ_SIZE);
while ((!_eofFound) && (_decryptedSize <= 0)) {
refill();
}
Integer nval = getNext();
if (nval != null) {
return nval.intValue();
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("No byte available. eof? " + _eofFound);
if (_eofFound)
if (_decryptedSize > 0) {
int c = _decryptedBuf[0];
System.arraycopy(_decryptedBuf, 1, _decryptedBuf, 0, _decryptedBuf.length-1);
_decryptedSize--;
return c;
} else if (_eofFound) {
return -1;
throw new IOException("Not EOF, but none available? " + _readyBuf.size() + "/" + _encryptedBuf.size()
+ "/" + _cumulativeRead + "... impossible");
} else {
throw new IOException("Not EOF, but none available? " + _decryptedSize
+ "/" + _writesSinceDecrypt
+ "/" + _cumulativeRead + "... impossible");
}
}
public int read(byte dest[]) throws IOException {
for (int i = 0; i < dest.length; i++) {
int val = read();
if (val == -1) {
// no more to read... can they expect more?
if (_eofFound && (i == 0)) return -1;
return i;
}
dest[i] = (byte) val;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read the full buffer of size " + dest.length);
return dest.length;
return read(dest, 0, dest.length);
}
public int read(byte dest[], int off, int len) throws IOException {
byte buf[] = new byte[len];
int read = read(buf);
if (read == -1) return -1;
System.arraycopy(buf, 0, dest, off, read);
return read;
for (int i = 0; i < len; i++) {
int val = read();
if (val == -1) {
// no more to read... can they expect more?
if (_eofFound && (i == 0)) {
if (_log.shouldLog(Log.DEBUG))
_log.info("EOF? " + _eofFound
+ "\nread=" + i + " decryptedSize=" + _decryptedSize
+ " \nencryptedSize=" + _writesSinceDecrypt
+ " \ntotal=" + _cumulativeRead
+ " \npadding=" + _cumulativePaddingStripped
+ " \nprepared=" + _cumulativePrepared);
return -1;
} else {
if (i != len)
if (_log.shouldLog(Log.DEBUG))
_log.info("non-terminal eof: " + _eofFound + " i=" + i + " len=" + len);
}
return i;
}
dest[off+i] = (byte)val;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read the full buffer of size " + len);
return len;
}
public long skip(long numBytes) throws IOException {
@ -117,25 +132,15 @@ public class AESInputStream extends FilterInputStream {
}
public int available() throws IOException {
return _readyBuf.size();
return _decryptedSize;
}
public void close() throws IOException {
//_log.debug("We have " + _encryptedBuf.size() + " available to decrypt... doing so");
//decrypt();
//byte buf[] = new byte[_readyBuf.size()];
//for (int i = 0; i < buf.length; i++)
// buf[i] = ((Integer)_readyBuf.get(i)).byteValue();
//_log.debug("After decrypt: readyBuf.size: " + _readyBuf.size() + "\n val:\t" + Base64.encode(buf));
int ready = _readyBuf.size();
int encrypted = _readyBuf.size();
_readyBuf.clear();
_encryptedBuf.reset();
in.close();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Cumulative bytes read from source/decrypted/stripped: " + _cumulativeRead + "/"
+ _cumulativePrepared + "/" + _cumulativePaddingStripped + "] remaining [" + ready + " ready, "
+ encrypted + " still encrypted]");
+ _cumulativePrepared + "/" + _cumulativePaddingStripped + "] remaining [" + _decryptedSize + " ready, "
+ _writesSinceDecrypt + " still encrypted]");
}
public void mark(int readLimit) { // nop
@ -149,120 +154,66 @@ public class AESInputStream extends FilterInputStream {
return false;
}
/**
* Retrieve the next ready byte, or null if no bytes are ready. this does not refill or block
*
*/
private Integer getNext() {
if (_readyBuf.size() > 0) {
return (Integer) _readyBuf.remove(0);
}
return null;
}
/**
* Read at least one new byte from the underlying stream, and up to max new bytes,
* but not necessarily enough for a new decrypted block. This blocks until at least
* one new byte is read from the stream
*
*/
private void refill(int max) throws IOException {
private void refill() throws IOException {
if (!_eofFound) {
byte buf[] = new byte[max];
int read = in.read(buf);
int read = in.read(_encryptedBuf, _writesSinceDecrypt, _encryptedBuf.length - _writesSinceDecrypt);
if (read == -1) {
_eofFound = true;
} else if (read > 0) {
//_log.debug("Read from the source stream " + read + " bytes");
_cumulativeRead += read;
_encryptedBuf.write(buf, 0, read);
_writesSinceDecrypt += read;
}
}
if (false) return; // true to keep the data for decrypt/display on close
if (_encryptedBuf.size() > 0) {
if (_encryptedBuf.size() >= DECRYPT_SIZE) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("We have " + _encryptedBuf.size() + " available to decrypt... doing so");
decrypt();
if ( (_encryptedBuf.size() > 0) && (_log.shouldLog(Log.DEBUG)) )
_log.debug("Bytes left in the encrypted buffer after decrypt: " + _encryptedBuf.size());
} else {
if (_eofFound) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("EOF and not enough bytes to decrypt [size = " + _encryptedBuf.size()
+ " totalCumulative: " + _cumulativeRead + "/"+_cumulativePrepared +"]!");
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Not enough bytes to decrypt [size = " + _encryptedBuf.size()
+ " totalCumulative: " + _cumulativeRead + "/"+_cumulativePrepared +"]");
}
}
if (_writesSinceDecrypt == BLOCK_SIZE) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("We have " + _writesSinceDecrypt + " available to decrypt... doing so");
decryptBlock();
if ( (_writesSinceDecrypt > 0) && (_log.shouldLog(Log.DEBUG)) )
_log.debug("Bytes left in the encrypted buffer after decrypt: "
+ _writesSinceDecrypt);
}
}
/**
* Take (n*BLOCK_SIZE) bytes off the _encryptedBuf, decrypt them, and place
* them on _readyBuf
*
* Decrypt the
*/
private void decrypt() throws IOException {
byte encrypted[] = _encryptedBuf.toByteArray();
_encryptedBuf.reset();
if ((encrypted == null) || (encrypted.length <= 0))
private void decryptBlock() throws IOException {
if (_writesSinceDecrypt != BLOCK_SIZE)
throw new IOException("Error decrypting - no data to decrypt");
if (_decryptedSize != 0)
throw new IOException("wtf, decrypted size is not 0? " + _decryptedSize);
_context.aes().decrypt(_encryptedBuf, 0, _encryptedBuf, 0, _key, _lastBlock, BLOCK_SIZE);
DataHelper.xor(_encryptedBuf, 0, _lastBlock, 0, _encryptedBuf, 0, BLOCK_SIZE);
int payloadBytes = countBlockPayload(_encryptedBuf, 0);
int numBlocks = encrypted.length / BLOCK_SIZE;
if ((encrypted.length % BLOCK_SIZE) != 0) {
// it was flushed / handled off the BLOCK_SIZE segments, so put the excess
// back into the _encryptedBuf for later handling
int trailing = encrypted.length % BLOCK_SIZE;
_encryptedBuf.write(encrypted, encrypted.length - trailing, trailing);
byte nencrypted[] = new byte[encrypted.length - trailing];
System.arraycopy(encrypted, 0, nencrypted, 0, nencrypted.length);
encrypted = nencrypted;
if (_log.shouldLog(Log.WARN))
_log.warn("Decrypt got odd segment - " + trailing
+ " bytes pushed back for later decryption - corrupted or slow data stream perhaps?");
} else {
if (_log.shouldLog(Log.INFO))
_log.info(encrypted.length + " bytes makes up " + numBlocks + " blocks to decrypt normally");
for (int i = 0; i < payloadBytes; i++) {
int c = _encryptedBuf[i];
if (c <= 0)
c += 256;
_decryptedBuf[i] = c;
}
_decryptedSize = payloadBytes;
byte block[] = new byte[BLOCK_SIZE];
for (int i = 0; i < numBlocks; i++) {
System.arraycopy(encrypted, i * BLOCK_SIZE, block, 0, BLOCK_SIZE);
byte decrypted[] = _context.AESEngine().decrypt(block, _key, _lastBlock);
byte data[] = DataHelper.xor(decrypted, _lastBlock);
int cleaned[] = stripPadding(data);
for (int j = 0; j < cleaned.length; j++) {
if (cleaned[j] <= 0) {
cleaned[j] += 256;
//_log.error("(modified: " + cleaned[j] + ")");
}
_readyBuf.add(new Integer(cleaned[j]));
}
_cumulativePrepared += cleaned.length;
//_log.debug("Updating last block for inputStream");
System.arraycopy(decrypted, 0, _lastBlock, 0, BLOCK_SIZE);
}
_cumulativePaddingStripped += BLOCK_SIZE - payloadBytes;
_cumulativePrepared += payloadBytes;
int remaining = encrypted.length % BLOCK_SIZE;
if (remaining != 0) {
_encryptedBuf.write(encrypted, encrypted.length - remaining, remaining);
_log.debug("After pushing " + remaining
+ " bytes back onto the buffer, lets delay 1s our action so we don't fast busy until the net transfers data");
try {
Thread.sleep(1000);
} catch (InterruptedException ie) { // nop
}
} else {
//_log.debug("No remaining encrypted bytes beyond the block size");
}
System.arraycopy(_encryptedBuf, 0, _lastBlock, 0, BLOCK_SIZE);
_writesSinceDecrypt = 0;
}
/**
* How many non-padded bytes are there in the block starting at the given
* location.
*
* PKCS#5 specifies the padding for the block has the # of padding bytes
* located in the last byte of the block, and each of the padding bytes are
* equal to that value.
@ -275,47 +226,58 @@ public class AESInputStream extends FilterInputStream {
*
* We use 16 byte blocks in this AES implementation
*
* @throws IOException if the padding is invalid
*/
private int[] stripPadding(byte data[]) throws IOException {
int numPadBytes = data[data.length - 1];
if ((numPadBytes >= data.length) || (numPadBytes <= 0)) {
private int countBlockPayload(byte data[], int startIndex) throws IOException {
int numPadBytes = data[startIndex + BLOCK_SIZE - 1];
if ((numPadBytes >= BLOCK_SIZE) || (numPadBytes <= 0)) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("stripPadding from block " + DataHelper.toHexString(data) + " (" + data.length + "bytes): "
_log.debug("countBlockPayload on block index " + startIndex
+ numPadBytes + " is an invalid # of pad bytes");
throw new IOException("Invalid number of pad bytes (" + numPadBytes
+ ") for " + data.length + " bytes");
+ ") for " + startIndex + " index");
}
int rv[] = new int[data.length - numPadBytes];
// optional, but a really good idea: verify the padding
if (true) {
for (int i = data.length - numPadBytes; i < data.length; i++) {
if (data[i] != (byte) numPadBytes) {
for (int i = BLOCK_SIZE - numPadBytes; i < BLOCK_SIZE; i++) {
if (data[startIndex + i] != (byte) numPadBytes) {
throw new IOException("Incorrect padding on decryption: data[" + i
+ "] = " + data[i] + " not " + numPadBytes);
+ "] = " + data[startIndex + i] + " not " + numPadBytes);
}
}
}
for (int i = 0; i < rv.length; i++)
rv[i] = data[i];
_cumulativePaddingStripped += numPadBytes;
return rv;
return BLOCK_SIZE - numPadBytes;
}
int remainingBytes() {
return _encryptedBuf.size();
return _writesSinceDecrypt;
}
int readyBytes() {
return _readyBuf.size();
return _decryptedSize;
}
/**
* Test AESOutputStream/AESInputStream
*/
public static void main(String args[]) {
public static void main(String args[]) {
I2PAppContext ctx = new I2PAppContext();
try {
System.out.println("pwd=" + new java.io.File(".").getAbsolutePath());
System.out.println("Beginning");
runTest(ctx);
} catch (Throwable e) {
ctx.logManager().getLog(AESInputStream.class).error("Fail", e);
}
try { Thread.sleep(30*1000); } catch (InterruptedException ie) {}
System.out.println("Done");
}
private static void runTest(I2PAppContext ctx) {
Log log = ctx.logManager().getLog(AESInputStream.class);
log.setMinimumPriority(Log.DEBUG);
byte orig[] = new byte[1024 * 32];
RandomSource.getInstance().nextBytes(orig);
//byte orig[] = "you are my sunshine, my only sunshine".getBytes();
@ -351,10 +313,51 @@ public class AESInputStream extends FilterInputStream {
}
log.info("Done testing 0 byte data");
for (int i = 0; i <= 32768; i++) {
orig = new byte[i];
ctx.random().nextBytes(orig);
try {
log.info("Testing " + orig.length);
runTest(ctx, orig, key, iv);
} catch (RuntimeException re) {
log.error("Error testing " + orig.length);
throw re;
}
}
/*
orig = new byte[615280];
RandomSource.getInstance().nextBytes(orig);
for (int i = 0; i < 20; i++) {
runTest(ctx, orig, key, iv);
}
log.info("Done testing 615280 byte data");
*/
/*
for (int i = 0; i < 100; i++) {
orig = new byte[ctx.random().nextInt(1024*1024)];
ctx.random().nextBytes(orig);
try {
runTest(ctx, orig, key, iv);
} catch (RuntimeException re) {
log.error("Error testing " + orig.length);
throw re;
}
}
log.info("Done testing 100 random lengths");
*/
orig = new byte[32];
RandomSource.getInstance().nextBytes(orig);
runOffsetTest(ctx, orig, key, iv);
try {
runOffsetTest(ctx, orig, key, iv);
} catch (Exception e) {
log.info("Error running offset test", e);
}
log.info("Done testing offset test (it should have come back with a statement NOT EQUAL!)");
@ -377,30 +380,33 @@ public class AESInputStream extends FilterInputStream {
long endE = Clock.getInstance().now();
ByteArrayInputStream encryptedStream = new ByteArrayInputStream(encrypted);
AESInputStream in = new AESInputStream(ctx, encryptedStream, key, iv);
AESInputStream sin = new AESInputStream(ctx, encryptedStream, key, iv);
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
byte buf[] = new byte[1024 * 32];
int read = DataHelper.read(in, buf);
int read = DataHelper.read(sin, buf);
if (read > 0) baos.write(buf, 0, read);
in.close();
sin.close();
byte fin[] = baos.toByteArray();
long end = Clock.getInstance().now();
Hash origHash = SHA256Generator.getInstance().calculateHash(orig);
Hash newHash = SHA256Generator.getInstance().calculateHash(fin);
boolean eq = origHash.equals(newHash);
if (eq)
log.info("Equal hashes. hash: " + origHash);
else
log.error("NOT EQUAL! \norig: \t" + Base64.encode(orig) + "\nnew : \t" + Base64.encode(fin));
if (eq) {
//log.info("Equal hashes. hash: " + origHash);
} else {
throw new RuntimeException("NOT EQUAL! len=" + orig.length + " read=" + read
+ "\norig: \t" + Base64.encode(orig) + "\nnew : \t"
+ Base64.encode(fin));
}
boolean ok = DataHelper.eq(orig, fin);
log.debug("EQ data? " + ok + " origLen: " + orig.length + " fin.length: " + fin.length);
log.debug("Time to D(E(" + orig.length + ")): " + (end - start) + "ms");
log.debug("Time to E(" + orig.length + "): " + (endE - start) + "ms");
log.debug("Time to D(" + orig.length + "): " + (end - endE) + "ms");
} catch (Throwable t) {
log.error("ERROR transferring", t);
} catch (IOException ioe) {
log.error("ERROR transferring", ioe);
}
//try { Thread.sleep(5000); } catch (Throwable t) {}
}
@ -422,15 +428,15 @@ public class AESInputStream extends FilterInputStream {
System.arraycopy(encrypted, 0, encryptedSegment, 0, 40);
ByteArrayInputStream encryptedStream = new ByteArrayInputStream(encryptedSegment);
AESInputStream in = new AESInputStream(ctx, encryptedStream, key, iv);
AESInputStream sin = new AESInputStream(ctx, encryptedStream, key, iv);
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
byte buf[] = new byte[1024 * 32];
int read = DataHelper.read(in, buf);
int remaining = in.remainingBytes();
int readyBytes = in.readyBytes();
int read = DataHelper.read(sin, buf);
int remaining = sin.remainingBytes();
int readyBytes = sin.readyBytes();
log.info("Read: " + read);
if (read > 0) baos.write(buf, 0, read);
in.close();
sin.close();
byte fin[] = baos.toByteArray();
log.info("fin.length: " + fin.length + " remaining: " + remaining + " ready: " + readyBytes);
long end = Clock.getInstance().now();
@ -441,15 +447,16 @@ public class AESInputStream extends FilterInputStream {
if (eq)
log.info("Equal hashes. hash: " + origHash);
else
log.error("NOT EQUAL! \norig: \t" + Base64.encode(orig) + "\nnew : \t" + Base64.encode(fin));
throw new RuntimeException("NOT EQUAL! len=" + orig.length + "\norig: \t" + Base64.encode(orig) + "\nnew : \t" + Base64.encode(fin));
boolean ok = DataHelper.eq(orig, fin);
log.debug("EQ data? " + ok + " origLen: " + orig.length + " fin.length: " + fin.length);
log.debug("Time to D(E(" + orig.length + ")): " + (end - start) + "ms");
log.debug("Time to E(" + orig.length + "): " + (endE - start) + "ms");
log.debug("Time to D(" + orig.length + "): " + (end - endE) + "ms");
} catch (Throwable t) {
log.error("ERROR transferring", t);
} catch (RuntimeException re) {
throw re;
} catch (IOException ioe) {
log.error("ERROR transferring", ioe);
}
//try { Thread.sleep(5000); } catch (Throwable t) {}
}

View File

@ -34,7 +34,15 @@ public class AESOutputStream extends FilterOutputStream {
private I2PAppContext _context;
private SessionKey _key;
private byte[] _lastBlock;
private ByteArrayOutputStream _inBuf;
/**
* buffer containing the unwritten bytes. The first unwritten
* byte is _lastCommitted+1, and the last unwritten byte is _nextWrite-1
* (aka the next byte to be written on the array is _nextWrite)
*/
private byte[] _unencryptedBuf;
private byte _writeBlock[];
/** how many bytes have we been given since we flushed it to the stream? */
private int _writesSinceCommit;
private long _cumulativeProvided; // how many bytes provided to this stream
private long _cumulativeWritten; // how many bytes written to the underlying stream
private long _cumulativePadding; // how many bytes of padding written
@ -51,31 +59,32 @@ public class AESOutputStream extends FilterOutputStream {
_key = key;
_lastBlock = new byte[BLOCK_SIZE];
System.arraycopy(iv, 0, _lastBlock, 0, BLOCK_SIZE);
_inBuf = new ByteArrayOutputStream(MAX_BUF);
_unencryptedBuf = new byte[MAX_BUF];
_writeBlock = new byte[BLOCK_SIZE];
_writesSinceCommit = 0;
}
public void write(int val) throws IOException {
_cumulativeProvided++;
_inBuf.write(val);
if (_inBuf.size() > MAX_BUF) doFlush();
_unencryptedBuf[_writesSinceCommit++] = (byte)(val & 0xFF);
if (_writesSinceCommit == _unencryptedBuf.length)
doFlush();
}
public void write(byte src[]) throws IOException {
_cumulativeProvided += src.length;
_inBuf.write(src);
if (_inBuf.size() > MAX_BUF) doFlush();
write(src, 0, src.length);
}
public void write(byte src[], int off, int len) throws IOException {
_cumulativeProvided += len;
_inBuf.write(src, off, len);
if (_inBuf.size() > MAX_BUF) doFlush();
// i'm too lazy to unroll this into the partial writes (dealing with
// wrapping around the buffer size)
for (int i = 0; i < len; i++)
write(src[i+off]);
}
public void close() throws IOException {
flush();
out.close();
_inBuf.reset();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Cumulative bytes provided to this stream / written out / padded: "
+ _cumulativeProvided + "/" + _cumulativeWritten + "/" + _cumulativePadding);
@ -87,8 +96,10 @@ public class AESOutputStream extends FilterOutputStream {
}
private void doFlush() throws IOException {
writeEncrypted(_inBuf.toByteArray());
_inBuf.reset();
if (_log.shouldLog(Log.INFO))
_log.info("doFlush(): writesSinceCommit=" + _writesSinceCommit);
writeEncrypted();
_writesSinceCommit = 0;
}
/**
@ -101,41 +112,37 @@ public class AESOutputStream extends FilterOutputStream {
* times).
*
*/
private void writeEncrypted(byte src[]) throws IOException {
if ((src == null) || (src.length == 0)) return;
int numBlocks = src.length / (BLOCK_SIZE - 1);
private void writeEncrypted() throws IOException {
int numBlocks = _writesSinceCommit / (BLOCK_SIZE - 1);
byte block[] = new byte[BLOCK_SIZE];
block[BLOCK_SIZE - 1] = 0x01; // the padding byte for "full" blocks
if (_log.shouldLog(Log.INFO))
_log.info("writeE(): #=" + _writesSinceCommit + " blocks=" + numBlocks);
for (int i = 0; i < numBlocks; i++) {
System.arraycopy(src, i * 15, block, 0, 15);
byte data[] = DataHelper.xor(block, _lastBlock);
byte encrypted[] = _context.AESEngine().encrypt(data, _key, _lastBlock);
_cumulativeWritten += encrypted.length;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Padding block " + i + " of " + numBlocks + " with 1 byte. orig= "
+ DataHelper.toHexString(data) + " (size=" + data.length + ") encrypted= "
+ DataHelper.toHexString(encrypted) + " (size=" + encrypted.length + ")");
out.write(encrypted);
System.arraycopy(encrypted, encrypted.length - BLOCK_SIZE, _lastBlock, 0, BLOCK_SIZE);
DataHelper.xor(_unencryptedBuf, i * 15, _lastBlock, 0, _writeBlock, 0, 15);
// the padding byte for "full" blocks
_writeBlock[BLOCK_SIZE - 1] = (byte)(_lastBlock[BLOCK_SIZE - 1] ^ 0x01);
_context.aes().encrypt(_writeBlock, 0, _writeBlock, 0, _key, _lastBlock, BLOCK_SIZE);
out.write(_writeBlock);
System.arraycopy(_writeBlock, 0, _lastBlock, 0, BLOCK_SIZE);
_cumulativeWritten += BLOCK_SIZE;
_cumulativePadding++;
}
if (src.length % 15 != 0) {
if (_writesSinceCommit % 15 != 0) {
// we need to do non trivial padding
int remainingBytes = src.length - numBlocks * 15;
int remainingBytes = _writesSinceCommit - numBlocks * 15;
int paddingBytes = BLOCK_SIZE - remainingBytes;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Padding " + src.length + " with " + paddingBytes + " bytes in " + numBlocks + " blocks");
System.arraycopy(src, numBlocks * 15, block, 0, remainingBytes);
Arrays.fill(block, remainingBytes, BLOCK_SIZE, (byte) paddingBytes);
byte data[] = DataHelper.xor(block, _lastBlock);
byte encrypted[] = _context.AESEngine().encrypt(data, _key, _lastBlock);
out.write(encrypted);
System.arraycopy(encrypted, encrypted.length - BLOCK_SIZE, _lastBlock, 0, BLOCK_SIZE);
_log.debug("Padding " + _writesSinceCommit + " with " + paddingBytes + " bytes in " + (numBlocks+1) + " blocks");
System.arraycopy(_unencryptedBuf, numBlocks * 15, _writeBlock, 0, remainingBytes);
Arrays.fill(_writeBlock, remainingBytes, BLOCK_SIZE, (byte) paddingBytes);
DataHelper.xor(_writeBlock, 0, _lastBlock, 0, _writeBlock, 0, BLOCK_SIZE);
_context.aes().encrypt(_writeBlock, 0, _writeBlock, 0, _key, _lastBlock, BLOCK_SIZE);
out.write(_writeBlock);
System.arraycopy(_writeBlock, 0, _lastBlock, 0, BLOCK_SIZE);
_cumulativePadding += paddingBytes;
_cumulativeWritten += encrypted.length;
_cumulativeWritten += BLOCK_SIZE;
}
}
}

View File

@ -12,6 +12,7 @@ package net.i2p.crypto;
import java.security.InvalidKeyException;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.SessionKey;
import net.i2p.util.Log;
@ -33,104 +34,63 @@ public class CryptixAESEngine extends AESEngine {
super(context);
_log = context.logManager().getLog(CryptixAESEngine.class);
}
public byte[] encrypt(byte payload[], SessionKey sessionKey, byte initializationVector[]) {
if ((initializationVector == null) || (payload == null) || (payload.length <= 0) || (sessionKey == null)
|| (initializationVector.length != 16)) return null;
public void encrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int length) {
if ( (payload == null) || (out == null) || (sessionKey == null) || (iv == null) || (iv.length != 16) )
throw new NullPointerException("invalid args to aes");
if (payload.length < payloadIndex + length)
throw new IllegalArgumentException("Payload is too short");
if (out.length < outIndex + length)
throw new IllegalArgumentException("Output is too short");
if (length <= 0)
throw new IllegalArgumentException("Length is too small");
if (length % 16 != 0)
throw new IllegalArgumentException("Only lengths mod 16 are supported here");
if (USE_FAKE_CRYPTO) {
_log.warn("AES Crypto disabled! Using trivial XOR");
byte rv[] = new byte[payload.length];
for (int i = 0; i < rv.length; i++)
rv[i] = (byte) (payload[i] ^ FAKE_KEY);
return rv;
System.arraycopy(payload, payloadIndex, out, outIndex, length);
return;
}
int numblock = length / 16;
DataHelper.xor(iv, 0, payload, payloadIndex, out, outIndex, 16);
encryptBlock(out, outIndex, sessionKey, out, outIndex);
for (int x = 1; x < numblock; x++) {
DataHelper.xor(out, outIndex + (x-1) * 16, payload, payloadIndex + x * 16, out, outIndex + x * 16, 16);
encryptBlock(out, outIndex + x * 16, sessionKey, out, outIndex + x * 16);
}
}
public void decrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int length) {
if ((iv== null) || (payload == null) || (payload.length <= 0) || (sessionKey == null)
|| (iv.length != 16))
throw new IllegalArgumentException("bad setup");
if (USE_FAKE_CRYPTO) {
_log.warn("AES Crypto disabled! Using trivial XOR");
System.arraycopy(payload, payloadIndex, out, outIndex, length);
return ;
}
int numblock = payload.length / 16;
if (payload.length % 16 != 0) numblock++;
byte[][] plain = new byte[numblock][16];
for (int x = 0; x < numblock; x++) {
for (int y = 0; y < 16; y++) {
plain[x][y] = payload[x * 16 + y];
}
}
byte[][] cipher = new byte[numblock][16];
cipher[0] = encrypt(xor(initializationVector, plain[0]), sessionKey);
decryptBlock(payload, 0, sessionKey, out, 0);
DataHelper.xor(out, 0, iv, 0, out, 0, 16);
for (int x = 1; x < numblock; x++) {
cipher[x] = encrypt(xor(cipher[x - 1], plain[x]), sessionKey);
decryptBlock(payload, x * 16, sessionKey, out, x * 16);
DataHelper.xor(out, x * 16, payload, (x - 1) * 16, out, x * 16, 16);
}
byte[] ret = new byte[numblock * 16];
for (int x = 0; x < numblock; x++) {
for (int y = 0; y < 16; y++) {
ret[x * 16 + y] = cipher[x][y];
}
}
return ret;
}
public byte[] decrypt(byte payload[], SessionKey sessionKey, byte initializationVector[]) {
if ((initializationVector == null) || (payload == null) || (payload.length <= 0) || (sessionKey == null)
|| (initializationVector.length != 16)) return null;
if (USE_FAKE_CRYPTO) {
_log.warn("AES Crypto disabled! Using trivial XOR");
byte rv[] = new byte[payload.length];
for (int i = 0; i < rv.length; i++)
rv[i] = (byte) (payload[i] ^ FAKE_KEY);
return rv;
}
int numblock = payload.length / 16;
if (payload.length % 16 != 0) numblock++;
byte[][] cipher = new byte[numblock][16];
for (int x = 0; x < numblock; x++) {
for (int y = 0; y < 16; y++) {
cipher[x][y] = payload[x * 16 + y];
}
}
byte[][] plain = new byte[numblock][16];
plain[0] = xor(decrypt(cipher[0], sessionKey), initializationVector);
for (int x = 1; x < numblock; x++) {
plain[x] = xor(decrypt(cipher[x], sessionKey), cipher[x - 1]);
}
byte[] ret = new byte[numblock * 16];
for (int x = 0; x < numblock; x++) {
for (int y = 0; y < 16; y++) {
ret[x * 16 + y] = plain[x][y];
}
}
return ret;
}
final static byte[] xor(byte[] a, byte[] b) {
if ((a == null) || (b == null) || (a.length != b.length)) return null;
byte[] ret = new byte[a.length];
for (int x = 0; x < a.length; x++) {
ret[x] = (byte) (a[x] ^ b[x]);
}
return ret;
}
/** Encrypt the payload with the session key
* @param payload data to be encrypted
* @param sessionKey private esession key to encrypt to
* @return encrypted data
*/
final byte[] encrypt(byte payload[], SessionKey sessionKey) {
final void encryptBlock(byte payload[], int inIndex, SessionKey sessionKey, byte out[], int outIndex) {
try {
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16);
byte rv[] = new byte[payload.length];
CryptixRijndael_Algorithm.blockEncrypt(payload, rv, 0, key, 16);
return rv;
CryptixRijndael_Algorithm.blockEncrypt(payload, out, inIndex, outIndex, key, 16);
} catch (InvalidKeyException ike) {
_log.error("Invalid key", ike);
return null;
}
}
@ -139,15 +99,55 @@ public class CryptixAESEngine extends AESEngine {
* @param sessionKey private session key
* @return unencrypted data
*/
final byte[] decrypt(byte payload[], SessionKey sessionKey) {
final void decryptBlock(byte payload[], int inIndex, SessionKey sessionKey, byte rv[], int outIndex) {
try {
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16);
byte rv[] = new byte[payload.length];
CryptixRijndael_Algorithm.blockDecrypt(payload, rv, 0, key, 16);
return rv;
CryptixRijndael_Algorithm.blockDecrypt(payload, rv, inIndex, outIndex, key, 16);
} catch (InvalidKeyException ike) {
_log.error("Invalid key", ike);
return null;
}
}
public static void main(String args[]) {
I2PAppContext ctx = new I2PAppContext();
try {
testEDBlock(ctx);
testED(ctx);
} catch (Exception e) {
e.printStackTrace();
}
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
}
private static void testED(I2PAppContext ctx) {
SessionKey key = ctx.keyGenerator().generateSessionKey();
byte iv[] = new byte[16];
byte orig[] = new byte[128];
byte encrypted[] = new byte[128];
byte decrypted[] = new byte[128];
ctx.random().nextBytes(iv);
ctx.random().nextBytes(orig);
CryptixAESEngine aes = new CryptixAESEngine(ctx);
aes.encrypt(orig, 0, encrypted, 0, key, iv, orig.length);
aes.decrypt(encrypted, 0, decrypted, 0, key, iv, encrypted.length);
if (!DataHelper.eq(decrypted,orig))
throw new RuntimeException("full D(E(orig)) != orig");
else
System.out.println("full D(E(orig)) == orig");
}
private static void testEDBlock(I2PAppContext ctx) {
SessionKey key = ctx.keyGenerator().generateSessionKey();
byte iv[] = new byte[16];
byte orig[] = new byte[16];
byte encrypted[] = new byte[16];
byte decrypted[] = new byte[16];
ctx.random().nextBytes(iv);
ctx.random().nextBytes(orig);
CryptixAESEngine aes = new CryptixAESEngine(ctx);
aes.encryptBlock(orig, 0, key, encrypted, 0);
aes.decryptBlock(encrypted, 0, key, decrypted, 0);
if (!DataHelper.eq(decrypted,orig))
throw new RuntimeException("block D(E(orig)) != orig");
else
System.out.println("block D(E(orig)) == orig");
}
}

View File

@ -382,7 +382,7 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
* @param inOffset Index of in from which to start considering data.
* @param sessionKey The session key to use for encryption.
*/
public static final void blockEncrypt(byte[] in, byte[] result, int inOffset, Object sessionKey) {
public static final void blockEncrypt(byte[] in, byte[] result, int inOffset, int outOffset, Object sessionKey) {
if (_RDEBUG) trace(_IN, "blockEncrypt(" + in + ", " + inOffset + ", " + sessionKey + ")");
int[][] Ke = (int[][]) ((Object[]) sessionKey)[0]; // extract encryption round keys
int ROUNDS = Ke.length - 1;
@ -417,25 +417,25 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
// last round is special
Ker = Ke[ROUNDS];
int tt = Ker[0];
result[0] = (byte) (_S[(t0 >>> 24) & 0xFF] ^ (tt >>> 24));
result[1] = (byte) (_S[(t1 >>> 16) & 0xFF] ^ (tt >>> 16));
result[2] = (byte) (_S[(t2 >>> 8) & 0xFF] ^ (tt >>> 8));
result[3] = (byte) (_S[t3 & 0xFF] ^ tt);
result[outOffset++] = (byte) (_S[(t0 >>> 24) & 0xFF] ^ (tt >>> 24));
result[outOffset++] = (byte) (_S[(t1 >>> 16) & 0xFF] ^ (tt >>> 16));
result[outOffset++] = (byte) (_S[(t2 >>> 8) & 0xFF] ^ (tt >>> 8));
result[outOffset++] = (byte) (_S[t3 & 0xFF] ^ tt);
tt = Ker[1];
result[4] = (byte) (_S[(t1 >>> 24) & 0xFF] ^ (tt >>> 24));
result[5] = (byte) (_S[(t2 >>> 16) & 0xFF] ^ (tt >>> 16));
result[6] = (byte) (_S[(t3 >>> 8) & 0xFF] ^ (tt >>> 8));
result[7] = (byte) (_S[t0 & 0xFF] ^ tt);
result[outOffset++] = (byte) (_S[(t1 >>> 24) & 0xFF] ^ (tt >>> 24));
result[outOffset++] = (byte) (_S[(t2 >>> 16) & 0xFF] ^ (tt >>> 16));
result[outOffset++] = (byte) (_S[(t3 >>> 8) & 0xFF] ^ (tt >>> 8));
result[outOffset++] = (byte) (_S[t0 & 0xFF] ^ tt);
tt = Ker[2];
result[8] = (byte) (_S[(t2 >>> 24) & 0xFF] ^ (tt >>> 24));
result[9] = (byte) (_S[(t3 >>> 16) & 0xFF] ^ (tt >>> 16));
result[10] = (byte) (_S[(t0 >>> 8) & 0xFF] ^ (tt >>> 8));
result[11] = (byte) (_S[t1 & 0xFF] ^ tt);
result[outOffset++] = (byte) (_S[(t2 >>> 24) & 0xFF] ^ (tt >>> 24));
result[outOffset++] = (byte) (_S[(t3 >>> 16) & 0xFF] ^ (tt >>> 16));
result[outOffset++] = (byte) (_S[(t0 >>> 8) & 0xFF] ^ (tt >>> 8));
result[outOffset++] = (byte) (_S[t1 & 0xFF] ^ tt);
tt = Ker[3];
result[12] = (byte) (_S[(t3 >>> 24) & 0xFF] ^ (tt >>> 24));
result[13] = (byte) (_S[(t0 >>> 16) & 0xFF] ^ (tt >>> 16));
result[14] = (byte) (_S[(t1 >>> 8) & 0xFF] ^ (tt >>> 8));
result[15] = (byte) (_S[t2 & 0xFF] ^ tt);
result[outOffset++] = (byte) (_S[(t3 >>> 24) & 0xFF] ^ (tt >>> 24));
result[outOffset++] = (byte) (_S[(t0 >>> 16) & 0xFF] ^ (tt >>> 16));
result[outOffset++] = (byte) (_S[(t1 >>> 8) & 0xFF] ^ (tt >>> 8));
result[outOffset++] = (byte) (_S[t2 & 0xFF] ^ tt);
if (_RDEBUG && _debuglevel > 6) {
System.out.println("CT=" + toString(result));
System.out.println();
@ -452,7 +452,7 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
* @param inOffset Index of in from which to start considering data.
* @param sessionKey The session key to use for decryption.
*/
public static final void blockDecrypt(byte[] in, byte[] result, int inOffset, Object sessionKey) {
public static final void blockDecrypt(byte[] in, byte[] result, int inOffset, int outOffset, Object sessionKey) {
if (_RDEBUG) trace(_IN, "blockDecrypt(" + in + ", " + inOffset + ", " + sessionKey + ")");
int[][] Kd = (int[][]) ((Object[]) sessionKey)[1]; // extract decryption round keys
int ROUNDS = Kd.length - 1;
@ -487,25 +487,25 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
// last round is special
Kdr = Kd[ROUNDS];
int tt = Kdr[0];
result[0] = (byte) (_Si[(t0 >>> 24) & 0xFF] ^ (tt >>> 24));
result[1] = (byte) (_Si[(t3 >>> 16) & 0xFF] ^ (tt >>> 16));
result[2] = (byte) (_Si[(t2 >>> 8) & 0xFF] ^ (tt >>> 8));
result[3] = (byte) (_Si[t1 & 0xFF] ^ tt);
result[outOffset++] = (byte) (_Si[(t0 >>> 24) & 0xFF] ^ (tt >>> 24));
result[outOffset++] = (byte) (_Si[(t3 >>> 16) & 0xFF] ^ (tt >>> 16));
result[outOffset++] = (byte) (_Si[(t2 >>> 8) & 0xFF] ^ (tt >>> 8));
result[outOffset++] = (byte) (_Si[t1 & 0xFF] ^ tt);
tt = Kdr[1];
result[4] = (byte) (_Si[(t1 >>> 24) & 0xFF] ^ (tt >>> 24));
result[5] = (byte) (_Si[(t0 >>> 16) & 0xFF] ^ (tt >>> 16));
result[6] = (byte) (_Si[(t3 >>> 8) & 0xFF] ^ (tt >>> 8));
result[7] = (byte) (_Si[t2 & 0xFF] ^ tt);
result[outOffset++] = (byte) (_Si[(t1 >>> 24) & 0xFF] ^ (tt >>> 24));
result[outOffset++] = (byte) (_Si[(t0 >>> 16) & 0xFF] ^ (tt >>> 16));
result[outOffset++] = (byte) (_Si[(t3 >>> 8) & 0xFF] ^ (tt >>> 8));
result[outOffset++] = (byte) (_Si[t2 & 0xFF] ^ tt);
tt = Kdr[2];
result[8] = (byte) (_Si[(t2 >>> 24) & 0xFF] ^ (tt >>> 24));
result[9] = (byte) (_Si[(t1 >>> 16) & 0xFF] ^ (tt >>> 16));
result[10] = (byte) (_Si[(t0 >>> 8) & 0xFF] ^ (tt >>> 8));
result[11] = (byte) (_Si[t3 & 0xFF] ^ tt);
result[outOffset++] = (byte) (_Si[(t2 >>> 24) & 0xFF] ^ (tt >>> 24));
result[outOffset++] = (byte) (_Si[(t1 >>> 16) & 0xFF] ^ (tt >>> 16));
result[outOffset++] = (byte) (_Si[(t0 >>> 8) & 0xFF] ^ (tt >>> 8));
result[outOffset++] = (byte) (_Si[t3 & 0xFF] ^ tt);
tt = Kdr[3];
result[12] = (byte) (_Si[(t3 >>> 24) & 0xFF] ^ (tt >>> 24));
result[13] = (byte) (_Si[(t2 >>> 16) & 0xFF] ^ (tt >>> 16));
result[14] = (byte) (_Si[(t1 >>> 8) & 0xFF] ^ (tt >>> 8));
result[15] = (byte) (_Si[t0 & 0xFF] ^ tt);
result[outOffset++] = (byte) (_Si[(t3 >>> 24) & 0xFF] ^ (tt >>> 24));
result[outOffset++] = (byte) (_Si[(t2 >>> 16) & 0xFF] ^ (tt >>> 16));
result[outOffset++] = (byte) (_Si[(t1 >>> 8) & 0xFF] ^ (tt >>> 8));
result[outOffset++] = (byte) (_Si[t0 & 0xFF] ^ tt);
if (_RDEBUG && _debuglevel > 6) {
System.out.println("PT=" + toString(result));
System.out.println();
@ -618,9 +618,9 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
* @param sessionKey The session key to use for encryption.
* @param blockSize The block size in bytes of this Rijndael.
*/
public static final void blockEncrypt(byte[] in, byte[] result, int inOffset, Object sessionKey, int blockSize) {
public static final void blockEncrypt(byte[] in, byte[] result, int inOffset, int outOffset, Object sessionKey, int blockSize) {
if (blockSize == _BLOCK_SIZE) {
blockEncrypt(in, result, inOffset, sessionKey);
blockEncrypt(in, result, inOffset, outOffset, sessionKey);
return;
}
if (_RDEBUG) trace(_IN, "blockEncrypt(" + in + ", " + inOffset + ", " + sessionKey + ", " + blockSize + ")");
@ -636,7 +636,8 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
int[] a = new int[BC];
int[] t = new int[BC]; // temporary work array
int i;
int j = 0, tt;
int j = outOffset;
int tt;
for (i = 0; i < BC; i++)
// plaintext to ints + key
@ -673,9 +674,9 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
* @param sessionKey The session key to use for decryption.
* @param blockSize The block size in bytes of this Rijndael.
*/
public static final void blockDecrypt(byte[] in, byte[] result, int inOffset, Object sessionKey, int blockSize) {
public static final void blockDecrypt(byte[] in, byte[] result, int inOffset, int outOffset, Object sessionKey, int blockSize) {
if (blockSize == _BLOCK_SIZE) {
blockDecrypt(in, result, inOffset, sessionKey);
blockDecrypt(in, result, inOffset, outOffset, sessionKey);
return;
}
@ -692,7 +693,8 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
int[] a = new int[BC];
int[] t = new int[BC]; // temporary work array
int i;
int j = 0, tt;
int j = outOffset;
int tt;
for (i = 0; i < BC; i++)
// ciphertext to ints + key
@ -749,7 +751,7 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
System.out.println("PT=" + toString(pt));
}
byte[] ct = new byte[_BLOCK_SIZE];
blockEncrypt(pt, ct, 0, key, _BLOCK_SIZE);
blockEncrypt(pt, ct, 0, 0, key, _BLOCK_SIZE);
if (_RDEBUG && _debuglevel > 6) {
System.out.println("Intermediate Plaintext Values (Decryption)");
@ -757,7 +759,7 @@ public final class CryptixRijndael_Algorithm // implicit no-argument constructor
System.out.println("CT=" + toString(ct));
}
byte[] cpt = new byte[_BLOCK_SIZE];
blockDecrypt(ct, cpt, 0, key, _BLOCK_SIZE);
blockDecrypt(ct, cpt, 0, 0, key, _BLOCK_SIZE);
ok = areEqual(pt, cpt);
if (!ok) throw new RuntimeException("Symmetric operation failed");

View File

@ -13,8 +13,13 @@ import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper;
import net.i2p.data.SessionKey;
import net.i2p.util.Clock;
import net.i2p.util.I2PThread;
@ -135,7 +140,62 @@ public class DHSessionKeyBuilder {
_sessionKey = null;
_extraExchangedBytes = new ByteArray();
}
/**
* Conduct a DH exchange over the streams, returning the resulting data.
*
* @return exchanged data
* @throws IOException if there is an error (but does not close the streams
*/
public static DHSessionKeyBuilder exchangeKeys(InputStream in, OutputStream out) throws IOException {
DHSessionKeyBuilder builder = new DHSessionKeyBuilder();
// send: X
writeBigI(out, builder.getMyPublicValue());
// read: Y
BigInteger Y = readBigI(in);
if (Y == null) return null;
builder.setPeerPublicValue(Y);
return builder;
}
static BigInteger readBigI(InputStream in) throws IOException {
byte Y[] = new byte[256];
int read = DataHelper.read(in, Y);
if (read != 256) {
return null;
}
if (1 == (Y[0] & 0x80)) {
// high bit set, need to inject an additional byte to keep 2s complement
if (_log.shouldLog(Log.DEBUG))
_log.debug("High bit set");
byte Y2[] = new byte[257];
System.arraycopy(Y, 0, Y2, 1, 256);
Y = Y2;
}
return new NativeBigInteger(Y);
}
/**
* Write out the integer as a 256 byte value. This left pads with 0s so
* to keep in 2s complement, and if it is already 257 bytes (due to
* the sign bit) ignore that first byte.
*/
static void writeBigI(OutputStream out, BigInteger val) throws IOException {
byte x[] = val.toByteArray();
for (int i = x.length; i < 256; i++)
out.write(0);
if (x.length == 257)
out.write(x, 1, 256);
else if (x.length == 256)
out.write(x);
else if (x.length > 257)
throw new IllegalArgumentException("Value is too large! length="+x.length);
out.flush();
}
private static final int getSize() {
synchronized (_builders) {
return _builders.size();
@ -292,8 +352,10 @@ public class DHSessionKeyBuilder {
byte iv[] = new byte[16];
RandomSource.getInstance().nextBytes(iv);
String origVal = "1234567890123456"; // 16 bytes max using AESEngine
byte enc[] = ctx.AESEngine().encrypt(origVal.getBytes(), key1, iv);
byte dec[] = ctx.AESEngine().decrypt(enc, key2, iv);
byte enc[] = new byte[16];
byte dec[] = new byte[16];
ctx.aes().encrypt(origVal.getBytes(), 0, enc, 0, key1, iv, 16);
ctx.aes().decrypt(enc, 0, dec, 0, key2, iv, 16);
String tranVal = new String(dec);
if (origVal.equals(tranVal))
_log.debug("**Success: D(E(val)) == val");

View File

@ -50,8 +50,10 @@ public class DSAEngine {
public static DSAEngine getInstance() {
return I2PAppContext.getGlobalContext().dsa();
}
public boolean verifySignature(Signature signature, byte signedData[], SigningPublicKey verifyingKey) {
return verifySignature(signature, signedData, 0, signedData.length, verifyingKey);
}
public boolean verifySignature(Signature signature, byte signedData[], int offset, int size, SigningPublicKey verifyingKey) {
long start = _context.clock().now();
byte[] sigbytes = signature.getData();
@ -68,7 +70,7 @@ public class DSAEngine {
BigInteger r = new NativeBigInteger(1, rbytes);
BigInteger y = new NativeBigInteger(1, verifyingKey.getData());
BigInteger w = s.modInverse(CryptoConstants.dsaq);
byte data[] = calculateHash(signedData).getData();
byte data[] = calculateHash(signedData, offset, size).getData();
NativeBigInteger bi = new NativeBigInteger(1, data);
BigInteger u1 = bi.multiply(w).mod(CryptoConstants.dsaq);
BigInteger u2 = r.multiply(w).mod(CryptoConstants.dsaq);
@ -88,6 +90,9 @@ public class DSAEngine {
}
public Signature sign(byte data[], SigningPrivateKey signingKey) {
return sign(data, 0, data.length, signingKey);
}
public Signature sign(byte data[], int offset, int length, SigningPrivateKey signingKey) {
if ((signingKey == null) || (data == null) || (data.length <= 0)) return null;
long start = _context.clock().now();
@ -100,7 +105,7 @@ public class DSAEngine {
BigInteger r = CryptoConstants.dsag.modPow(k, CryptoConstants.dsap).mod(CryptoConstants.dsaq);
BigInteger kinv = k.modInverse(CryptoConstants.dsaq);
Hash h = calculateHash(data);
Hash h = calculateHash(data, offset, length);
if (h == null) return null;
@ -150,42 +155,42 @@ public class DSAEngine {
private int[] H0 = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0};
private Hash calculateHash(byte[] source) {
long length = source.length * 8;
private Hash calculateHash(byte[] source, int offset, int len) {
long length = len * 8;
int k = 448 - (int) ((length + 1) % 512);
if (k < 0) {
k += 512;
}
int padbytes = k / 8;
int wordlength = source.length / 4 + padbytes / 4 + 3;
int wordlength = len / 4 + padbytes / 4 + 3;
int[] M0 = new int[wordlength];
int wordcount = 0;
int x = 0;
for (x = 0; x < (source.length / 4) * 4; x += 4) {
M0[wordcount] = source[x] << 24 >>> 24 << 24;
M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16;
M0[wordcount] |= source[x + 2] << 24 >>> 24 << 8;
M0[wordcount] |= source[x + 3] << 24 >>> 24 << 0;
for (x = 0; x < (len / 4) * 4; x += 4) {
M0[wordcount] = source[offset + x] << 24 >>> 24 << 24;
M0[wordcount] |= source[offset + x + 1] << 24 >>> 24 << 16;
M0[wordcount] |= source[offset + x + 2] << 24 >>> 24 << 8;
M0[wordcount] |= source[offset + x + 3] << 24 >>> 24 << 0;
wordcount++;
}
switch (source.length - (wordcount + 1) * 4 + 4) {
switch (len - (wordcount + 1) * 4 + 4) {
case 0:
M0[wordcount] |= 0x80000000;
break;
case 1:
M0[wordcount] = source[x] << 24 >>> 24 << 24;
M0[wordcount] = source[offset + x] << 24 >>> 24 << 24;
M0[wordcount] |= 0x00800000;
break;
case 2:
M0[wordcount] = source[x] << 24 >>> 24 << 24;
M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16;
M0[wordcount] = source[offset + x] << 24 >>> 24 << 24;
M0[wordcount] |= source[offset + x + 1] << 24 >>> 24 << 16;
M0[wordcount] |= 0x00008000;
break;
case 3:
M0[wordcount] = source[x] << 24 >>> 24 << 24;
M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16;
M0[wordcount] |= source[x + 2] << 24 >>> 24 << 8;
M0[wordcount] = source[offset + x] << 24 >>> 24 << 24;
M0[wordcount] |= source[offset + x + 1] << 24 >>> 24 << 16;
M0[wordcount] |= source[offset + x + 2] << 24 >>> 24 << 8;
M0[wordcount] |= 0x00000080;
break;
}

View File

@ -248,7 +248,8 @@ public class ElGamalAESEngine {
SessionKey foundKey) throws DataFormatException {
//_log.debug("iv for decryption: " + DataHelper.toString(iv, 16));
//_log.debug("decrypting AES block. encr.length = " + (encrypted == null? -1 : encrypted.length) + " sentTag: " + DataHelper.toString(sentTag, 32));
byte decrypted[] = _context.AESEngine().decrypt(encrypted, key, iv);
byte decrypted[] = new byte[encrypted.length];
_context.aes().decrypt(encrypted, 0, decrypted, 0, key, iv, encrypted.length);
//Hash h = _context.sha().calculateHash(decrypted);
//_log.debug("Hash of entire aes block after decryption: \n" + DataHelper.toString(h.getData(), 32));
try {
@ -503,7 +504,8 @@ public class ElGamalAESEngine {
byte aesUnencr[] = aesSrc.toByteArray();
//Hash h = _context.sha().calculateHash(aesUnencr);
//_log.debug("Hash of entire aes block before encryption: (len=" + aesUnencr.length + ")\n" + DataHelper.toString(h.getData(), 32));
byte aesEncr[] = _context.AESEngine().encrypt(aesUnencr, key, iv);
byte aesEncr[] = new byte[aesUnencr.length];
_context.aes().encrypt(aesUnencr, 0, aesEncr, 0, key, iv, aesEncr.length);
//_log.debug("Encrypted length: " + aesEncr.length);
return aesEncr;
} catch (IOException ioe) {

View File

@ -62,40 +62,43 @@ public class SHA256Generator {
* @return hash of the source
*/
public Hash calculateHash(byte[] source) {
long length = source.length * 8;
return calculateHash(source, 0, source.length);
}
public Hash calculateHash(byte[] source, int start, int len) {
long length = len * 8;
int k = 448 - (int) ((length + 1) % 512);
if (k < 0) {
k += 512;
}
int padbytes = k / 8;
int wordlength = source.length / 4 + padbytes / 4 + 3;
int wordlength = len / 4 + padbytes / 4 + 3;
int[] M0 = new int[wordlength];
int wordcount = 0;
int x = 0;
for (x = 0; x < (source.length / 4) * 4; x += 4) {
M0[wordcount] = source[x] << 24 >>> 24 << 24;
M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16;
M0[wordcount] |= source[x + 2] << 24 >>> 24 << 8;
M0[wordcount] |= source[x + 3] << 24 >>> 24 << 0;
for (x = 0; x < (len / 4) * 4; x += 4) {
M0[wordcount] = source[x+start] << 24 >>> 24 << 24;
M0[wordcount] |= source[x+start + 1] << 24 >>> 24 << 16;
M0[wordcount] |= source[x+start + 2] << 24 >>> 24 << 8;
M0[wordcount] |= source[x+start + 3] << 24 >>> 24 << 0;
wordcount++;
}
switch (source.length - (wordcount + 1) * 4 + 4) {
switch (len - (wordcount + 1) * 4 + 4) {
case 0:
M0[wordcount] |= 0x80000000;
break;
case 1:
M0[wordcount] = source[x] << 24 >>> 24 << 24;
M0[wordcount] = source[x+start] << 24 >>> 24 << 24;
M0[wordcount] |= 0x00800000;
break;
case 2:
M0[wordcount] = source[x] << 24 >>> 24 << 24;
M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16;
M0[wordcount] = source[x+start] << 24 >>> 24 << 24;
M0[wordcount] |= source[x+start + 1] << 24 >>> 24 << 16;
M0[wordcount] |= 0x00008000;
break;
case 3:
M0[wordcount] = source[x] << 24 >>> 24 << 24;
M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16;
M0[wordcount] |= source[x + 2] << 24 >>> 24 << 8;
M0[wordcount] = source[x+start] << 24 >>> 24 << 24;
M0[wordcount] |= source[x+start + 1] << 24 >>> 24 << 16;
M0[wordcount] |= source[x+start + 2] << 24 >>> 24 << 8;
M0[wordcount] |= 0x00000080;
break;
}

View File

@ -86,6 +86,41 @@ public class Certificate extends DataStructureImpl {
DataHelper.writeLong(out, 2, 0L);
}
}
public int writeBytes(byte target[], int offset) {
int cur = offset;
DataHelper.toLong(target, cur, 1, _type);
cur++;
if (_payload != null) {
DataHelper.toLong(target, cur, 2, _payload.length);
cur += 2;
System.arraycopy(_payload, 0, target, cur, _payload.length);
cur += _payload.length;
} else {
DataHelper.toLong(target, cur, 2, 0);
cur += 2;
}
return cur - offset;
}
public int readBytes(byte source[], int offset) {
int cur = offset;
_type = (int)DataHelper.fromLong(source, cur, 1);
cur++;
int length = (int)DataHelper.fromLong(source, cur, 2);
cur += 2;
if (length > 0) {
_payload = new byte[length];
System.arraycopy(source, cur, _payload, 0, length);
cur += length;
}
return cur - offset;
}
public int size() {
return 1 + 2 + (_payload != null ? _payload.length : 0);
}
public boolean equals(Object object) {
if ((object == null) || !(object instanceof Certificate)) return false;

View File

@ -12,27 +12,23 @@ package net.i2p.data;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.TreeMap;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import net.i2p.util.OrderedProperties;
@ -99,24 +95,28 @@ public class DataHelper {
*/
public static void writeProperties(OutputStream rawStream, Properties props)
throws DataFormatException, IOException {
OrderedProperties p = new OrderedProperties();
if (props != null) p.putAll(props);
ByteArrayOutputStream baos = new ByteArrayOutputStream(32);
for (Iterator iter = p.keySet().iterator(); iter.hasNext();) {
String key = (String) iter.next();
String val = p.getProperty(key);
// now make sure they're in UTF-8
//key = new String(key.getBytes(), "UTF-8");
//val = new String(val.getBytes(), "UTF-8");
writeString(baos, key);
baos.write(_equalBytes);
writeString(baos, val);
baos.write(_semicolonBytes);
if (props != null) {
OrderedProperties p = new OrderedProperties();
p.putAll(props);
ByteArrayOutputStream baos = new ByteArrayOutputStream(32);
for (Iterator iter = p.keySet().iterator(); iter.hasNext();) {
String key = (String) iter.next();
String val = p.getProperty(key);
// now make sure they're in UTF-8
//key = new String(key.getBytes(), "UTF-8");
//val = new String(val.getBytes(), "UTF-8");
writeString(baos, key);
baos.write(_equalBytes);
writeString(baos, val);
baos.write(_semicolonBytes);
}
baos.close();
byte propBytes[] = baos.toByteArray();
writeLong(rawStream, 2, propBytes.length);
rawStream.write(propBytes);
} else {
writeLong(rawStream, 2, 0);
}
baos.close();
byte propBytes[] = baos.toByteArray();
writeLong(rawStream, 2, propBytes.length);
rawStream.write(propBytes);
}
/**
@ -261,7 +261,70 @@ public class DataHelper {
throw new DataFormatException("Invalid value (must be positive)", iae);
}
}
public static byte[] toLong(int numBytes, long value) throws IllegalArgumentException {
byte val[] = new byte[numBytes];
toLong(val, 0, numBytes, value);
return val;
}
public static void toLong(byte target[], int offset, int numBytes, long value) throws IllegalArgumentException {
if (numBytes <= 0) throw new IllegalArgumentException("Invalid number of bytes");
if (value < 0) throw new IllegalArgumentException("Negative value not allowed");
for (int i = 0; i < numBytes; i++)
target[offset+numBytes-i-1] = (byte)(value >>> (i*8));
}
public static long fromLong(byte src[], int offset, int numBytes) {
if ( (src == null) || (src.length == 0) )
return 0;
long rv = 0;
for (int i = 0; i < numBytes; i++) {
long cur = src[offset+i] & 0xFF;
if (cur < 0) cur = cur+256;
cur = (cur << (8*(numBytes-i-1)));
rv += cur;
}
if (rv < 0)
throw new IllegalArgumentException("wtf, fromLong got a negative? " + rv + ": offset="+ offset +" numBytes="+numBytes);
return rv;
}
public static void main(String args[]) {
for (int i = 0; i <= 0xFF; i++)
testLong(1, i);
System.out.println("Test 1byte passed");
for (long i = 0; i <= 0xFFFF; i++)
testLong(2, i);
System.out.println("Test 2byte passed");
for (long i = 0; i <= 0xFFFFFF; i++)
testLong(3, i);
System.out.println("Test 3byte passed");
for (long i = 0; i <= 0xFFFFFFFF; i++)
testLong(4, i);
System.out.println("Test 4byte passed");
for (long i = 0; i <= 0xFFFFFFFF; i++)
testLong(8, i);
System.out.println("Test 8byte passed");
}
private static void testLong(int numBytes, long value) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(numBytes);
writeLong(baos, numBytes, value);
byte written[] = baos.toByteArray();
byte extract[] = toLong(numBytes, value);
if (!eq(written, extract))
throw new RuntimeException("testLong("+numBytes+","+value+") FAILED");
long read = fromLong(extract, 0, extract.length);
if (read != value)
throw new RuntimeException("testLong("+numBytes+","+value+") FAILED on read (" + read + ")");
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
/** Read in a date from the stream as specified by the I2P data structure spec.
* A date is an 8 byte unsigned integer in network byte order specifying the number of
* milliseconds since midnight on January 1, 1970 in the GMT timezone. If the number is
@ -272,7 +335,7 @@ public class DataHelper {
* @return date read, or null
*/
public static Date readDate(InputStream in) throws DataFormatException, IOException {
long date = readLong(in, 8);
long date = readLong(in, DATE_LENGTH);
if (date == 0L) return null;
return new Date(date);
@ -287,10 +350,25 @@ public class DataHelper {
public static void writeDate(OutputStream out, Date date)
throws DataFormatException, IOException {
if (date == null)
writeLong(out, 8, 0L);
writeLong(out, DATE_LENGTH, 0L);
else
writeLong(out, 8, date.getTime());
writeLong(out, DATE_LENGTH, date.getTime());
}
public static byte[] toDate(Date date) throws IllegalArgumentException {
if (date == null)
return toLong(DATE_LENGTH, 0L);
else
return toLong(DATE_LENGTH, date.getTime());
}
public static Date fromDate(byte src[], int offset) throws IllegalArgumentException {
long when = fromLong(src, offset, DATE_LENGTH);
if (when <= 0)
return null;
else
return new Date(when);
}
public static final int DATE_LENGTH = 8;
/** Read in a string from the stream as specified by the I2P data structure spec.
* A string is 1 or more bytes where the first byte is the number of bytes (not characters!)
@ -364,12 +442,16 @@ public class DataHelper {
public static void writeBoolean(OutputStream out, Boolean bool)
throws DataFormatException, IOException {
if (bool == null)
writeLong(out, 1, 2);
writeLong(out, 1, BOOLEAN_UNKNOWN);
else if (Boolean.TRUE.equals(bool))
writeLong(out, 1, 1);
writeLong(out, 1, BOOLEAN_TRUE);
else
writeLong(out, 1, 0);
writeLong(out, 1, BOOLEAN_FALSE);
}
public static final byte BOOLEAN_TRUE = 0x1;
public static final byte BOOLEAN_FALSE = 0x0;
public static final byte BOOLEAN_UNKNOWN = 0x2;
//
// The following comparator helpers make it simpler to write consistently comparing
@ -463,12 +545,38 @@ public class DataHelper {
public final static byte[] xor(byte lhs[], byte rhs[]) {
if ((lhs == null) || (rhs == null) || (lhs.length != rhs.length)) return null;
byte rv[] = new byte[lhs.length];
byte diff[] = new byte[lhs.length];
for (int i = 0; i < lhs.length; i++)
diff[i] = (byte) (lhs[i] ^ rhs[i]);
xor(lhs, 0, rhs, 0, diff, 0, lhs.length);
return diff;
}
/**
* xor the lhs with the rhs, storing the result in out.
*
* @param lhs one of the source arrays
* @param startLeft starting index in the lhs array to begin the xor
* @param rhs the other source array
* @param startRight starting index in the rhs array to begin the xor
* @param out output array
* @param startOut starting index in the out array to store the result
* @param len how many bytes into the various arrays to xor
*/
public final static void xor(byte lhs[], int startLeft, byte rhs[], int startRight, byte out[], int startOut, int len) {
if ( (lhs == null) || (rhs == null) || (out == null) )
throw new NullPointerException("Invalid params to xor (" + lhs + ", " + rhs + ", " + out + ")");
if (lhs.length < startLeft + len)
throw new IllegalArgumentException("Left hand side is too short");
if (rhs.length < startRight + len)
throw new IllegalArgumentException("Right hand side is too short");
if (out.length < startOut + len)
throw new IllegalArgumentException("Result is too short");
for (int i = 0; i < len; i++)
out[startOut + i] = (byte) (lhs[startLeft + i] ^ rhs[startRight + i]);
}
//
// The following hashcode helpers make it simpler to write consistently hashing
// functions for objects based on their value, not JVM memory address
@ -574,11 +682,14 @@ public class DataHelper {
/** compress the data and return a new GZIP compressed array */
public static byte[] compress(byte orig[]) {
return compress(orig, 0, orig.length);
}
public static byte[] compress(byte orig[], int offset, int size) {
if ((orig == null) || (orig.length <= 0)) return orig;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(orig.length);
GZIPOutputStream out = new GZIPOutputStream(baos, orig.length);
out.write(orig);
ByteArrayOutputStream baos = new ByteArrayOutputStream(size);
GZIPOutputStream out = new GZIPOutputStream(baos, size);
out.write(orig, offset, size);
out.finish();
out.flush();
byte rv[] = baos.toByteArray();
@ -594,9 +705,12 @@ public class DataHelper {
/** decompress the GZIP compressed data (returning null on error) */
public static byte[] decompress(byte orig[]) throws IOException {
return decompress(orig, 0, orig.length);
}
public static byte[] decompress(byte orig[], int offset, int length) throws IOException {
if ((orig == null) || (orig.length <= 0)) return orig;
GZIPInputStream in = new GZIPInputStream(new ByteArrayInputStream(orig), orig.length);
ByteArrayOutputStream baos = new ByteArrayOutputStream(orig.length * 2);
GZIPInputStream in = new GZIPInputStream(new ByteArrayInputStream(orig, offset, length), length);
ByteArrayOutputStream baos = new ByteArrayOutputStream(length * 2);
byte buf[] = new byte[4 * 1024];
while (true) {
int read = in.read(buf);
@ -610,91 +724,4 @@ public class DataHelper {
return rv;
}
/**
* Read in the last few lines of a (newline delimited) textfile, or null if
* the file doesn't exist.
*
*/
public static String readTextFile(String filename, int maxNumLines) {
File f = new File(filename);
if (!f.exists()) return null;
FileInputStream fis = null;
try {
fis = new FileInputStream(f);
BufferedReader in = new BufferedReader(new InputStreamReader(fis));
List lines = new ArrayList(maxNumLines);
String line = null;
while ( (line = in.readLine()) != null) {
lines.add(line);
while (lines.size() > maxNumLines)
lines.remove(0);
}
StringBuffer buf = new StringBuffer(lines.size() * 80);
for (int i = 0; i < lines.size(); i++)
buf.append((String)lines.get(i)).append('\n');
return buf.toString();
} catch (IOException ioe) {
return null;
} finally {
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
}
}
public static boolean extractZip(File zipfile, File targetDir) {
try {
byte buf[] = new byte[16*1024];
ZipFile zip = new ZipFile(zipfile);
Enumeration entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = (ZipEntry)entries.nextElement();
if (entry.getName().indexOf("..") != -1) {
System.err.println("ERROR: Refusing to extract a zip entry with '..' in it [" + entry.getName() + "]");
return false;
}
File target = new File(targetDir, entry.getName());
File parent = target.getParentFile();
if ( (parent != null) && (!parent.exists()) ) {
boolean parentsOk = parent.mkdirs();
if (!parentsOk) {
System.err.println("ERROR: Unable to create the parent dir for " + entry.getName() + ": [" + parent.getAbsolutePath() + "]");
return false;
}
}
if (entry.isDirectory()) {
if (!target.exists()) {
boolean created = target.mkdirs();
if (!created) {
System.err.println("ERROR: Unable to create the directory [" + entry.getName() + "]");
return false;
} else {
System.err.println("INFO: Creating directory [" + entry.getName() + "]");
}
}
} else {
try {
InputStream in = zip.getInputStream(entry);
FileOutputStream fos = new FileOutputStream(target);
int read = 0;
while ( (read = in.read(buf)) != -1) {
fos.write(buf, 0, read);
}
fos.close();
in.close();
System.err.println("INFO: File [" + entry.getName() + "] extracted");
} catch (IOException ioe) {
System.err.println("ERROR: Error extracting the zip entry (" + entry.getName() + "]");
ioe.printStackTrace();
return false;
}
}
}
zip.close();
return true;
} catch (IOException ioe) {
System.err.println("ERROR: Unable to extract the zip file");
ioe.printStackTrace();
return false;
}
}
}

View File

@ -79,7 +79,41 @@ public class Destination extends DataStructureImpl {
_signingKey.writeBytes(out);
_certificate.writeBytes(out);
}
public int writeBytes(byte target[], int offset) {
int cur = offset;
System.arraycopy(_publicKey.getData(), 0, target, cur, PublicKey.KEYSIZE_BYTES);
cur += PublicKey.KEYSIZE_BYTES;
System.arraycopy(_signingKey.getData(), 0, target, cur, SigningPublicKey.KEYSIZE_BYTES);
cur += SigningPublicKey.KEYSIZE_BYTES;
cur += _certificate.writeBytes(target, cur);
return cur - offset;
}
public int readBytes(byte source[], int offset) {
int cur = offset;
_publicKey = new PublicKey();
byte buf[] = new byte[PublicKey.KEYSIZE_BYTES];
System.arraycopy(source, cur, buf, 0, PublicKey.KEYSIZE_BYTES);
_publicKey.setData(buf);
cur += PublicKey.KEYSIZE_BYTES;
_signingKey = new SigningPublicKey();
buf = new byte[SigningPublicKey.KEYSIZE_BYTES];
System.arraycopy(source, cur, buf, 0, SigningPublicKey.KEYSIZE_BYTES);
cur += SigningPublicKey.KEYSIZE_BYTES;
_certificate = new Certificate();
cur += _certificate.readBytes(buf, cur);
return cur - offset;
}
public int size() {
return PublicKey.KEYSIZE_BYTES + SigningPublicKey.KEYSIZE_BYTES + _certificate.size();
}
public boolean equals(Object object) {
if ((object == null) || !(object instanceof Destination)) return false;
Destination dst = (Destination) object;

View File

@ -322,6 +322,22 @@ public class RouterInfo extends DataStructureImpl {
return true;
}
/**
* Pull the first workable target address for the given transport
*
*/
public RouterAddress getTargetAddress(String transportStyle) {
synchronized (_addresses) {
for (Iterator iter = _addresses.iterator(); iter.hasNext(); ) {
RouterAddress addr = (RouterAddress)iter.next();
if (addr.getTransportStyle().equals(transportStyle))
return addr;
}
}
return null;
}
/**
* Actually validate the signature

View File

@ -28,7 +28,10 @@ public class SessionKey extends DataStructureImpl {
public final static int KEYSIZE_BYTES = 32;
public SessionKey() {
setData(null);
this(null);
}
public SessionKey(byte data[]) {
setData(data);
}
public byte[] getData() {

View File

@ -33,9 +33,8 @@ public class Signature extends DataStructureImpl {
FAKE_SIGNATURE[i] = 0x00;
}
public Signature() {
setData(null);
}
public Signature() { this(null); }
public Signature(byte data[]) { setData(data); }
public byte[] getData() {
return _data;

View File

@ -29,9 +29,8 @@ public class SigningPrivateKey extends DataStructureImpl {
public final static int KEYSIZE_BYTES = 20;
public SigningPrivateKey() {
setData(null);
}
public SigningPrivateKey() { this(null); }
public SigningPrivateKey(byte data[]) { setData(data); }
public byte[] getData() {
return _data;

View File

@ -29,9 +29,8 @@ public class SigningPublicKey extends DataStructureImpl {
public final static int KEYSIZE_BYTES = 128;
public SigningPublicKey() {
setData(null);
}
public SigningPublicKey() { this(null); }
public SigningPublicKey(byte data[]) { setData(data); }
public byte[] getData() {
return _data;

View File

@ -35,12 +35,25 @@ public class TunnelId extends DataStructureImpl {
public final static int TYPE_PARTICIPANT = 3;
public TunnelId() {
setTunnelId(-1);
setType(TYPE_UNSPECIFIED);
_tunnelId = -1;
_type = TYPE_UNSPECIFIED;
}
public TunnelId(long id) {
if (id <= 0) throw new IllegalArgumentException("wtf, tunnelId " + id);
_tunnelId = id;
_type = TYPE_UNSPECIFIED;
}
public TunnelId(long id, int type) {
if (id <= 0) throw new IllegalArgumentException("wtf, tunnelId " + id);
_tunnelId = id;
_type = type;
}
public long getTunnelId() { return _tunnelId; }
public void setTunnelId(long id) { _tunnelId = id; }
public void setTunnelId(long id) {
_tunnelId = id;
if (id <= 0) throw new IllegalArgumentException("wtf, tunnelId " + id);
}
/**
* is this tunnel inbound, outbound, or a participant (kept in memory only and used only for the router).s

View File

@ -0,0 +1,187 @@
package net.i2p.stat;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
*
*/
public class BufferedStatLog implements StatLog {
private I2PAppContext _context;
private Log _log;
private StatEvent _events[];
private int _eventNext;
private int _lastWrite;
/** flush stat events to disk after this many events (or 30s)*/
private int _flushFrequency;
private List _statFilters;
private String _lastFilters;
private BufferedWriter _out;
private String _outFile;
public BufferedStatLog(I2PAppContext ctx) {
_context = ctx;
_log = ctx.logManager().getLog(BufferedStatLog.class);
_events = new StatEvent[1000];
for (int i = 0; i < 1000; i++)
_events[i] = new StatEvent();
_eventNext = 0;
_lastWrite = _events.length-1;
_statFilters = new ArrayList(10);
_flushFrequency = 500;
I2PThread writer = new I2PThread(new StatLogWriter(), "StatLogWriter");
writer.setDaemon(true);
writer.start();
}
public void addData(String scope, String stat, long value, long duration) {
synchronized (_events) {
_events[_eventNext].init(scope, stat, value, duration);
_eventNext = (_eventNext + 1) % _events.length;
if (_eventNext == _lastWrite)
_lastWrite = (_lastWrite + 1) % _events.length; // drop an event
if (_log.shouldLog(Log.DEBUG))
_log.debug("AddData next=" + _eventNext + " lastWrite=" + _lastWrite);
if (_eventNext > _lastWrite) {
if (_eventNext - _lastWrite >= _flushFrequency)
_events.notifyAll();
} else {
if (_events.length - 1 - _lastWrite + _eventNext >= _flushFrequency)
_events.notifyAll();
}
}
}
private boolean shouldLog(String stat) {
synchronized (_statFilters) {
return _statFilters.contains(stat) || _statFilters.contains("*");
}
}
private void updateFilters() {
String val = _context.getProperty("stat.logFilters");
if (val != null) {
if ( (_lastFilters != null) && (_lastFilters.equals(val)) ) {
// noop
} else {
StringTokenizer tok = new StringTokenizer(val, ",");
synchronized (_statFilters) {
_statFilters.clear();
while (tok.hasMoreTokens())
_statFilters.add(tok.nextToken().trim());
}
}
_lastFilters = val;
} else {
synchronized (_statFilters) { _statFilters.clear(); }
}
String filename = _context.getProperty("stat.logFile");
if (filename == null)
filename = "stats.log";
if ( (_outFile != null) && (_outFile.equals(filename)) ) {
// noop
} else {
if (_out != null) try { _out.close(); } catch (IOException ioe) {}
_outFile = filename;
try {
_out = new BufferedWriter(new FileWriter(_outFile, true));
} catch (IOException ioe) { ioe.printStackTrace(); }
}
}
private class StatLogWriter implements Runnable {
private SimpleDateFormat _fmt = new SimpleDateFormat("yyyyMMdd HH:mm:ss.SSS");
public void run() {
int writeStart = -1;
int writeEnd = -1;
while (true) {
synchronized (_events) {
if (_eventNext > _lastWrite) {
if (_eventNext - _lastWrite < _flushFrequency)
try { _events.wait(30*1000); } catch (InterruptedException ie) {}
} else {
if (_events.length - 1 - _lastWrite + _eventNext < _flushFrequency)
try { _events.wait(30*1000); } catch (InterruptedException ie) {}
}
writeStart = (_lastWrite + 1) % _events.length;
writeEnd = _eventNext;
_lastWrite = (writeEnd == 0 ? _events.length-1 : writeEnd - 1);
}
if (writeStart != writeEnd) {
try {
if (_log.shouldLog(Log.DEBUG))
_log.debug("writing " + writeStart +"->"+ writeEnd);
writeEvents(writeStart, writeEnd);
} catch (Exception e) {
_log.error("error writing " + writeStart +"->"+ writeEnd, e);
}
}
}
}
private void writeEvents(int start, int end) {
try {
updateFilters();
int cur = start;
while (cur != end) {
if (shouldLog(_events[cur].getStat())) {
String when = null;
synchronized (_fmt) {
when = _fmt.format(new Date(_events[cur].getTime()));
}
_out.write(when);
_out.write(" ");
if (_events[cur].getScope() == null)
_out.write("noScope ");
else
_out.write(_events[cur].getScope() + " ");
_out.write(_events[cur].getStat()+" ");
_out.write(_events[cur].getValue()+" ");
_out.write(_events[cur].getDuration()+"\n");
}
cur = (cur + 1) % _events.length;
}
_out.flush();
} catch (IOException ioe) {
_log.error("Error writing out", ioe);
}
}
}
private class StatEvent {
private long _time;
private String _scope;
private String _stat;
private long _value;
private long _duration;
public long getTime() { return _time; }
public String getScope() { return _scope; }
public String getStat() { return _stat; }
public long getValue() { return _value; }
public long getDuration() { return _duration; }
public void init(String scope, String stat, long value, long duration) {
_scope = scope;
_stat = stat;
_value = value;
_duration = duration;
_time = _context.clock().now();
}
}
}

View File

@ -19,6 +19,8 @@ public class RateStat {
private String _description;
/** actual rate objects for this statistic */
private Rate _rates[];
/** component we tell about events as they occur */
private StatLog _statLog;
public RateStat(String name, String description, String group, long periods[]) {
_statName = name;
@ -28,11 +30,13 @@ public class RateStat {
for (int i = 0; i < periods.length; i++)
_rates[i] = new Rate(periods[i]);
}
public void setStatLog(StatLog sl) { _statLog = sl; }
/**
* update all of the rates for the various periods with the given value.
*/
public void addData(long value, long eventDuration) {
if (_statLog != null) _statLog.addData(_groupName, _statName, value, eventDuration);
for (int i = 0; i < _rates.length; i++)
_rates[i].addData(value, eventDuration);
}

View File

@ -0,0 +1,8 @@
package net.i2p.stat;
/**
* Component to be notified when a particular event occurs
*/
public interface StatLog {
public void addData(String scope, String stat, long value, long duration);
}

View File

@ -0,0 +1,76 @@
package net.i2p.stat;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Simple CLI to splot the stat logs into per-stat files containing
* #seconds since beginning and the value (ready for loading into your
* favorite plotting tool)
*/
public class StatLogSplitter {
private static final String DATE_FORMAT = "yyyyMMdd HH:mm:ss.SSS";
private static SimpleDateFormat _fmt = new SimpleDateFormat(DATE_FORMAT);
public static void main(String args[]) {
if (args.length != 1) {
System.err.println("Usage: StatLogSplitter filename");
return;
}
splitLog(args[0]);
}
private static void splitLog(String filename) {
Map outputFiles = new HashMap(4);
try {
BufferedReader in = new BufferedReader(new FileReader(filename));
String line;
long first = 0;
while ( (line = in.readLine()) != null) {
String date = line.substring(0, DATE_FORMAT.length()).trim();
int endGroup = line.indexOf(' ', DATE_FORMAT.length()+1);
int endStat = line.indexOf(' ', endGroup+1);
int endValue = line.indexOf(' ', endStat+1);
String group = line.substring(DATE_FORMAT.length()+1, endGroup).trim();
String stat = line.substring(endGroup, endStat).trim();
String value = line.substring(endStat, endValue).trim();
String duration = line.substring(endValue).trim();
//System.out.println(date + " " + group + " " + stat + " " + value + " " + duration);
try {
Date when = _fmt.parse(date);
if (first <= 0) first = when.getTime();
long val = Long.parseLong(value);
long time = Long.parseLong(duration);
if (!outputFiles.containsKey(stat)) {
outputFiles.put(stat, new FileWriter(stat + ".dat"));
System.out.println("Including data to " + stat + ".dat");
}
FileWriter out = (FileWriter)outputFiles.get(stat);
double s = (when.getTime()-first)/1000.0;
//long s = when.getTime();
out.write(s + " " + val + " [" + line + "]\n");
out.flush();
} catch (ParseException pe) {
continue;
} catch (NumberFormatException nfe){
continue;
}
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
for (Iterator iter = outputFiles.values().iterator(); iter.hasNext(); ) {
FileWriter out = (FileWriter)iter.next();
try { out.close(); } catch (IOException ioe) {}
}
}
}

View File

@ -27,6 +27,7 @@ public class StatManager {
private Map _frequencyStats;
/** stat name to RateStat */
private Map _rateStats;
private StatLog _statLog;
/**
* The stat manager should only be constructed and accessed through the
@ -39,6 +40,18 @@ public class StatManager {
_context = context;
_frequencyStats = Collections.synchronizedMap(new HashMap(128));
_rateStats = Collections.synchronizedMap(new HashMap(128));
_statLog = new BufferedStatLog(context);
}
public StatLog getStatLog() { return _statLog; }
public void setStatLog(StatLog log) {
_statLog = log;
synchronized (_rateStats) {
for (Iterator iter = _rateStats.values().iterator(); iter.hasNext(); ) {
RateStat rs = (RateStat)iter.next();
rs.setStatLog(log);
}
}
}
/**
@ -64,7 +77,9 @@ public class StatManager {
*/
public void createRateStat(String name, String description, String group, long periods[]) {
if (_rateStats.containsKey(name)) return;
_rateStats.put(name, new RateStat(name, description, group, periods));
RateStat rs = new RateStat(name, description, group, periods);
if (_statLog != null) rs.setStatLog(_statLog);
_rateStats.put(name, rs);
}
/** update the given frequency statistic, taking note that an event occurred (and recalculating all frequencies) */

View File

@ -115,7 +115,7 @@ public class NtpClient {
//System.out.println("host: " + serverName + " rtt: " + roundTripDelay + " offset: " + localClockOffset + " seconds");
return (long)(System.currentTimeMillis() + localClockOffset*1000);
} catch (IOException ioe) {
ioe.printStackTrace();
//ioe.printStackTrace();
return -1;
}
}

View File

@ -100,6 +100,7 @@ public class Timestamper implements Runnable {
if (_log.shouldLog(Log.INFO))
_log.info("Starting up timestamper");
boolean alreadyBitched = false;
try {
while (true) {
updateConfig();
@ -118,7 +119,9 @@ public class Timestamper implements Runnable {
_log.debug("Stamp time");
stampTime(now);
} catch (IllegalArgumentException iae) {
_log.log(Log.CRIT, "Unable to reach any of the NTP servers - network disconnected?");
if (!alreadyBitched)
_log.log(Log.CRIT, "Unable to reach any of the NTP servers - network disconnected?");
alreadyBitched = true;
}
}
try { Thread.sleep(_queryFrequency); } catch (InterruptedException ie) {}

View File

@ -0,0 +1,171 @@
package net.i2p.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* General helper methods for messing with files
*
*/
public class FileUtil {
/**
* Delete the path as well as any files or directories underneath it.
*
* @param path path to the directory being deleted
* @param failIfNotEmpty if true, do not delete anything if the directory
* is not empty (and return false)
* @return true if the path no longer exists (aka was removed),
* false if it remains
*/
public static final boolean rmdir(String path, boolean failIfNotEmpty) {
return rmdir(new File(path), failIfNotEmpty);
}
/**
* Delete the path as well as any files or directories underneath it.
*
* @param target the file or directory being deleted
* @param failIfNotEmpty if true, do not delete anything if the directory
* is not empty (and return false)
* @return true if the path no longer exists (aka was removed),
* false if it remains
*/
public static final boolean rmdir(File target, boolean failIfNotEmpty) {
if (!target.exists()) {
//System.out.println("info: target does not exist [" + target.getPath() + "]");
return true;
}
if (!target.isDirectory()) {
//System.out.println("info: target is not a directory [" + target.getPath() + "]");
return target.delete();
} else {
File children[] = target.listFiles();
if (children == null) {
//System.out.println("info: target null children [" + target.getPath() + "]");
return false;
}
if ( (failIfNotEmpty) && (children.length > 0) ) {
//System.out.println("info: target is not emtpy[" + target.getPath() + "]");
return false;
}
for (int i = 0; i < children.length; i++) {
if (!rmdir(children[i], failIfNotEmpty))
return false;
//System.out.println("info: target removed recursively [" + children[i].getPath() + "]");
}
return target.delete();
}
}
public static boolean extractZip(File zipfile, File targetDir) {
try {
byte buf[] = new byte[16*1024];
ZipFile zip = new ZipFile(zipfile);
Enumeration entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = (ZipEntry)entries.nextElement();
if (entry.getName().indexOf("..") != -1) {
System.err.println("ERROR: Refusing to extract a zip entry with '..' in it [" + entry.getName() + "]");
return false;
}
File target = new File(targetDir, entry.getName());
File parent = target.getParentFile();
if ( (parent != null) && (!parent.exists()) ) {
boolean parentsOk = parent.mkdirs();
if (!parentsOk) {
System.err.println("ERROR: Unable to create the parent dir for " + entry.getName() + ": [" + parent.getAbsolutePath() + "]");
return false;
}
}
if (entry.isDirectory()) {
if (!target.exists()) {
boolean created = target.mkdirs();
if (!created) {
System.err.println("ERROR: Unable to create the directory [" + entry.getName() + "]");
return false;
} else {
System.err.println("INFO: Creating directory [" + entry.getName() + "]");
}
}
} else {
try {
InputStream in = zip.getInputStream(entry);
FileOutputStream fos = new FileOutputStream(target);
int read = 0;
while ( (read = in.read(buf)) != -1) {
fos.write(buf, 0, read);
}
fos.close();
in.close();
System.err.println("INFO: File [" + entry.getName() + "] extracted");
} catch (IOException ioe) {
System.err.println("ERROR: Error extracting the zip entry (" + entry.getName() + "]");
ioe.printStackTrace();
return false;
}
}
}
zip.close();
return true;
} catch (IOException ioe) {
System.err.println("ERROR: Unable to extract the zip file");
ioe.printStackTrace();
return false;
}
}
/**
* Read in the last few lines of a (newline delimited) textfile, or null if
* the file doesn't exist.
*
*/
public static String readTextFile(String filename, int maxNumLines) {
File f = new File(filename);
if (!f.exists()) return null;
FileInputStream fis = null;
try {
fis = new FileInputStream(f);
BufferedReader in = new BufferedReader(new InputStreamReader(fis));
List lines = new ArrayList(maxNumLines);
String line = null;
while ( (line = in.readLine()) != null) {
lines.add(line);
while (lines.size() > maxNumLines)
lines.remove(0);
}
StringBuffer buf = new StringBuffer(lines.size() * 80);
for (int i = 0; i < lines.size(); i++)
buf.append((String)lines.get(i)).append('\n');
return buf.toString();
} catch (IOException ioe) {
return null;
} finally {
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
}
}
public static void main(String args[]) {
testRmdir();
}
private static void testRmdir() {
File t = new File("rmdirTest/test/subdir/blah");
boolean created = t.mkdirs();
if (!t.exists()) throw new RuntimeException("Unable to create test");
boolean deleted = FileUtil.rmdir("rmdirTest", false);
if (!deleted)
System.err.println("FAIL: unable to delete rmdirTest");
else
System.out.println("PASS: rmdirTest deleted");
}
}

View File

@ -246,8 +246,8 @@ public class LogManager {
File cfgFile = new File(_location);
if (!cfgFile.exists()) {
if (!_alreadyNoticedMissingConfig) {
if (_log.shouldLog(Log.ERROR))
_log.error("Log file " + _location + " does not exist");
if (_log.shouldLog(Log.WARN))
_log.warn("Log file " + _location + " does not exist");
System.err.println("Log file " + _location + " does not exist");
_alreadyNoticedMissingConfig = true;
}
@ -447,19 +447,18 @@ public class LogManager {
if (!Character.isDigit(mod)) v = size.substring(0, size.length() - 1);
int val = Integer.parseInt(v);
switch (mod) {
case 'K':
val *= 1024;
break;
case 'M':
val *= 1024 * 1024;
break;
case 'G':
val *= 1024 * 1024 * 1024;
break;
case 'T':
// because we can
val *= 1024 * 1024 * 1024 * 1024;
break;
case 'K':
val *= 1024;
break;
case 'M':
val *= 1024 * 1024;
break;
case 'G':
val *= 1024 * 1024 * 1024;
break;
default:
// blah, noop
break;
}
return val;
} catch (Throwable t) {

View File

@ -104,6 +104,8 @@ public class ShellCommand {
}
}
private final static int BUFFER_SIZE = 1024;
/**
* Reads data from a <code>java.io.InputStream</code> and writes it to
* <code>STDOUT</code>.
@ -112,8 +114,6 @@ public class ShellCommand {
*/
private class StreamReader extends Thread {
final int BUFFER_SIZE = 1024;
private BufferedReader bufferedReader;
private InputStreamReader inputStreamReader;

View File

@ -2,6 +2,7 @@ package net.i2p.util;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@ -18,10 +19,14 @@ public class SimpleTimer {
private static final SimpleTimer _instance = new SimpleTimer();
public static SimpleTimer getInstance() { return _instance; }
private Log _log;
/** event time (Long) to event (TimedEvent) mapping */
private Map _events;
/** event (TimedEvent) to event time (Long) mapping */
private Map _eventTimes;
private SimpleTimer() {
_events = new TreeMap();
_eventTimes = new HashMap();
I2PThread runner = new I2PThread(new SimpleTimerRunner());
runner.setName("SimpleTimer");
runner.setDaemon(true);
@ -35,9 +40,13 @@ public class SimpleTimer {
public void addEvent(TimedEvent event, long timeoutMs) {
long eventTime = System.currentTimeMillis() + timeoutMs;
synchronized (_events) {
// remove the old scheduled position, then reinsert it
if (_eventTimes.containsKey(event))
_events.remove(_eventTimes.get(event));
while (_events.containsKey(new Long(eventTime)))
eventTime++;
_events.put(new Long(eventTime), event);
_eventTimes.put(event, new Long(eventTime));
_events.notifyAll();
}
}
@ -87,6 +96,8 @@ public class SimpleTimer {
if (timesToRemove.size() > 0) {
for (int i = 0; i < timesToRemove.size(); i++)
_events.remove(timesToRemove.get(i));
for (int i = 0; i < eventsToFire.size(); i++)
_eventTimes.remove(eventsToFire.get(i));
} else {
if (nextEventDelay != -1)
_events.wait(nextEventDelay);

View File

@ -72,22 +72,28 @@ public class AES256Bench {
iv[x] = (byte)civ[x];
}
byte[] e = _context.AESEngine().encrypt(plain, key, iv);
byte[] d = _context.AESEngine().decrypt(e, key, iv);
byte[] e = new byte[plain.length];
_context.aes().encrypt(plain, 0, e, 0, key, iv, plain.length);
byte[] d = new byte[e.length];
_context.aes().decrypt(e, 0, d, 0, key, iv, d.length);
boolean same = true;
for (int x = 0; x < d.length; x++) {
if (plain[x] != d[x]) {
same = false;
throw new RuntimeException("Failed decrypt at " + x);
}
}
System.out.println("Standard test D(E(value)) == value? " + same);
if (!same) throw new RuntimeException("moo");
plain = "1234567890123456".getBytes();
e = _context.AESEngine().encrypt(plain, key, iv);
d = _context.AESEngine().decrypt(e, key, iv);
e = new byte[plain.length];
_context.aes().encrypt(plain, 0, e, 0, key, iv, plain.length);
d = new byte[e.length];
_context.aes().decrypt(e, 0, d, 0, key, iv, d.length);
same = DataHelper.eq(plain, d);
System.out.println("Different value test D(E(value)) == value? " + same);
if (!same) throw new RuntimeException("moo");
System.out.println();
System.out.println();
@ -104,9 +110,11 @@ public class AES256Bench {
message[i] = (byte)((i%26)+'a');
for (int x = 0; x < times; x++) {
long startencrypt = System.currentTimeMillis();
e = _context.AESEngine().encrypt(message, key, iv);
e = new byte[message.length];
d = new byte[e.length];
_context.aes().encrypt(message, 0, e, 0, key, iv, message.length);
long endencryptstartdecrypt = System.currentTimeMillis();
d = _context.AESEngine().decrypt(e, key, iv);
_context.aes().decrypt(e, 0, d, 0, key, iv, d.length);
long enddecrypt = System.currentTimeMillis();
System.out.print(".");
encrypttime += endencryptstartdecrypt - startencrypt;

View File

@ -145,8 +145,10 @@ class ElGamalAESEngineTest {
String msg = "Hello world01234012345678901234501234567890123450123456789012345";
h = SHA256Generator.getInstance().calculateHash(msg.getBytes());
_log.debug("Hash of entire aes block before encryption: \n" + DataHelper.toString(h.getData(), 32));
byte aesEncr[] = _context.AESEngine().encrypt(msg.getBytes(), sessionKey, iv);
byte aesDecr[] = _context.AESEngine().decrypt(aesEncr, sessionKey, iv);
byte aesEncr[] = new byte[msg.getBytes().length];
byte aesDecr[] = new byte[aesEncr.length];
_context.aes().encrypt(msg.getBytes(), 0, aesEncr, 0, sessionKey, iv, aesEncr.length);
_context.aes().decrypt(aesEncr, 0, aesDecr, 0, sessionKey, iv, aesEncr.length);
h = SHA256Generator.getInstance().calculateHash(aesDecr);
_log.debug("Hash of entire aes block after decryption: \n" + DataHelper.toString(h.getData(), 32));
if (msg.equals(new String(aesDecr))) {

View File

@ -1,4 +1,233 @@
$Id: history.txt,v 1.15 2004/09/08 17:15:43 hypercubus Exp $
$Id: history.txt,v 1.53 2004/10/17 16:03:06 jrandom Exp $
* 2004-10-18 0.4.1.3 released
2004-10-18 jrandom
* Allow sending messages with a section of a byte array.
* Reduced stats published.
2004-10-17 jrandom
* Don't b0rk on whitespace in the router address.
2004-10-16 jrandom
* More aggressively reduce the capacity of peers if their tunnels are
failing so that we move off them quicker.
* Simplify some data structure serialization for reuse in the streaming
lib, as well as add support for signing and verifying partial byte
arrays.
* Logging updates
2004-10-16 jrandom
* Increased the default minimum tunnel test time to 5 seconds, since we
still see the occational message processing time spike to 2 seconds.
* Update the SimpleTimer to allow rescheduling a task thats already
queued (useful for the new streaming lib).
2004-10-15 jrandom
* Replaced old minimum tunnel test timeout of 1s with a configurable
value (router.config property "router.tunnelTestMinimum", with the
default of 2s).
2004-10-14 jrandom
* Tunnel rejection is no longer a sign of an overwhelmingly loaded
peer, so don't use it as a key point of the IsFailing calculator.
We still use it as a key point of the Capacity calculator, however.
2004-10-14 jrandom
* Allow for a configurable tunnel "growth factor", rather than trying
to achieve a steady state. This will let us grow gradually when
the router is needed more, rather than blindly accepting the request
or arbitrarily choking it at an averaged value. Configure this with
"router.tunnelGrowthFactor" in the router.config (default "1.5").
* Adjust the tunnel test timeouts dynamically - rather than the old
flat 30s (!!!) timeout, we set the timeout to 2x the average tunnel
test time (the deviation factor can be adjusted by setting
"router.tunnelTestDeviation" to "3.0" or whatever). This should help
find the 'good' tunnels.
* Added some crazy debugging to try and track down an intermittent hang.
2004-10-13 jrandom
* Fix the probabalistic tunnel reject (we always accepted everything,
since the docs on java.util.Random.nextDouble() are wrong..)
* Fixed a race on startup (thanks Quadn!)
2004-10-12 jrandom
* Disable the probabalistic drop by default (enable via the router config
property "tcp.dropProbabalistically=true")
* Disable the actual watchdog shutdown by default, but keep track of more
variables and log a lot more when it occurs (enable via the router
config property "watchdog.haltOnHang=true")
* Implement some tunnel participation smoothing by refusing requests
probabalistically as our participating tunnel count exceeds the previous
hour's, or when the 10 minute average tunnel test time exceeds the 60
minute average tunnel test time. The probabilities in both cases are
oldAverage / #current, so if you're suddenly flooded with 200 tunnels
and you had previously only participated in 50, you'll have a 25% chance
of accepting a subsequent request.
* 2004-10-10 0.4.1.2 released
2004-10-10 cervantes
* Update the I2PTunnel HTTP proxy to strip out the i2paddresshelper from
the request.
2004-10-09 jrandom
* Added a watchdog timer to do some baseline liveliness checking to help
debug some odd errors.
* Added a pair of summary stats for bandwidth usage, allowing easy export
with the other stats ("bw.sendBps" and "bw.receiveBps")
* Trimmed another memory allocation on message reception.
2004-10-08 jrandom
* Revamp the AESInputStream so it doesn't allocate any temporary objects
during its operation.
2004-10-08 jrandom
* Don't kill the establisher threads during a soft restart.
* Attempt to validate the peer's routerInfo earlier during handshaking.
* Revamp the AESOutputStream so it doesn't allocate any temporary objects
during its operation.
2004-10-07 jrandom
* Reimplement the I2NP reading with less temporary memory allocation.
There is still significant GC churn, especially under load, but this
should help.
* Catch some oddball errors in the transport (message timeout while
establishing).
2004-10-07 jrandom
* Expire queued messages even when the writer is blocked.
* Reimplement most of the I2NP writing with less temporary memory
allocations (I2NP reading still gobbles memory).
2004-10-06 jrandom
* Implement an active queue management scheme on the TCP transports,
dropping messages probabalistically as the queue fills up. The
estimated queue capacity is determined by the rate at which messages
have been sent to the peer (averaged at 1, 5, and 60m periods). As
we exceed 1/2 of the estimated capacity, we drop messages throughout
the queue probabalistically with regards to their size. This is based
on RFC 2309's RED, with the minimum threshold set to 1/2 the
estimated connection capacity. We may want to consider using a send
rate and queue size measured across all connections, to deal with our
own local bandwidth saturation, but we'll try the per-con metrics first.
2004-10-06 jrandom
* Enable explicit disabling of the systray entirely for windows machines
with strange configurations: add -Dsystray.disable=true to the java
command line. (thanks mihi!)
2004-10-05 jrandom
* Allow peers on the same LAN to communicate with each other safely even
when they cannot talk to each other through the external address.
2004-10-05 jrandom
* Display how much time is left before the graceful shutdown is complete.
* Debug some improperly failed messages on timeout or disconnection.
2004-10-05 jrandom
* Don't go into a fast busy if an I2PTunnel 'server' is explicitly killed
(thanks mule!)
* Handle some more error conditions regarding abruptly closing sockets
(thanks Jonva!)
2004-10-04 jrandom
* Update the shitlist to reject a peer for an exponentially increasing
period of time (with an upper bounds of an hour).
* Various minor stat and debugging fixes
2004-10-03 jrandom
* Add a new stat logging component to optionally dump the raw stats to
disk as they are generated, rather than rely upon the summarized data.
By default, this is off, but the router property "stat.logFilters" can
be set to a comma delimited list of stats (e.g. "client.sendAckTime")
which will be written to the file "stats.log" (or whatever the property
"stat.logFile" is set to). This can also log profile related stats,
such as "dbResponseTime" or "tunnelTestResponseTime".
2004-10-02 jrandom
* Assure that we quickly fail messages bound for shitlisted peers.
* Address a race on startup where the first peer contacted could hang the
router (thanks Romster!)
* Only whine about an intermittent inability to query the time server once
2004-10-02 jrandom
* Command line utility to verify a peer's reachability - simply run
net.i2p.router.transport.tcp.ConnectionHandler hostname port# and it
will print out whether that peer is reachable or not (using a simple
verification handshake).
* 2004-10-01 0.4.1.1 released
2004-10-01 jrandom
* Handle partial reseeds, caused by seeds going away before the download
completes (thanks Sugadude!)
2004-10-01 jrandom
* Explicitly refuse IPv6 addresses, since only some peers support
them and we want fully reachable peers.
2004-10-01 jrandom
* Additional error handling for a variety of transport layer errors.
* 2004-09-30 0.4.1 released (not backwards compatible)
2004-09-30 jrandom
* Bundle the configuration necessary to run an eepsite out of the box
with Jetty - simply edit ./eepsite/docroot/index.html and give people
the key listed on the I2PTunnel configuration page, and its up.
* Router console cleanup, and some (off by default) tunnels -
smtp.postman.i2p (port 7659), pop.postman.i2p (port 7660), and
irc.baffled.i2p (port 7661)
2004-09-29 jrandom
* Always wipe the Jetty work directory on startup, so that web updates
are reflected immediately (Jetty does not honor the cache across
multiple executions)
2004-09-27 jrandom
* Limit the number of connection tags saved to 10,000. This is a huge
limit, but consumes no more than 1MB of RAM. For now, we drop them
randomly after reaching that size, forcing those dropped peers to use
a full DH negotiation.
* HTML cleanup in the console.
2004-09-26 jrandom
* Complete rewrite of the TCP transport with IP autodetection and
low CPU overhead reconnections. More concise connectivity errors
are listed on the /oldconsole.jsp as well. The IP autodetection works
by listening to the first person who tells you what your IP address is
when you have not defined one yourself and you have no other TCP
connections.
* Update to the I2NP message format to add transparent verification at
the I2NP level (beyond standard TCP verification).
* Remove a potential weakness in our AESEngine's safeEncrypt and safeDecrypt
implementation (rather than verifying with E(H(key)), we now verify with
E(H(iv))).
* The above changes are NOT BACKWARDS COMPATIBLE.
* Removed all of the old unused PHTTP code.
* Refactor various methods and clean up some javadoc.
2004-09-21 jrandom
* Have two tiers of hosts.txt files - the standard "hosts.txt" and
the new "userhosts.txt". Updates to I2P will only overwrite the former,
but values stored in the later take precedence. Both are queried on
lookup.
2004-09-16 jrandom
* Refactor the TCP transport to deal with changing identities gracefully,
and to prevent some wasted effort by keeping track of what host+port
combinations we are connected to (rather than just the identities). Also
catch a few configuration errors earlier.
* Removed no longer relevent methods from the Transport API that were
exposing ideas that probably shouldn't be exposed.
* Removed the 0.4.0.1 specific files from i2pupdate.zip (relating to script
updates)
2004-09-13 jrandom
* Update for the SDK reconnection to deal with overflow.
* Web improvements (@ not # on the /logs.jsp [thanks ugha!] and fixed the
rounding on lifetime bandwidth used [thanks gott!]).
* 2004-09-08 0.4.0.1 released

View File

@ -1,6 +1,26 @@
; TC's hosts.txt guaranteed freshness
; $Id: hosts.txt,v 1.42 2004/09/08 02:46:21 jrandom Exp $
; $Id: hosts.txt,v 1.64 2004/10/17 16:39:06 jrandom Exp $
; changelog:
; (1.87) updated curiosity.i2p (via signed email)
; (1.86) added blueheron.i2p
; (1.85) updated files.i2p (after verifying ident)
; (1.84) added utansans.i2p
; (1.83) added irc.orz.i2p
; (1.82) added nano.i2p
; (1.81) added ragnarok.i2p
; (1.80) added marcos.i2p
; (1.79) updated beyond.i2p's key
; (1.78) added b.i2p
; (1.77) added tinyurl.i2p
; (1.76) added detonate.i2p
; (1.75) added identiguy.i2p
; (1.74) added jabber-2.i2p
; (1.73) added jake.i2p
; (1.72) added anonymnet.i2p sasquotch.i2p orz.i2p microbleu.i2p {www,smtp,pop3}.postman.i2p
; (1.71) added curiosity.i2p
; (1.70) added freeciv.nightblade.i2p
; (1.69) added xolotl.i2p
; (1.68) added modulus.i2p
; (1.67) added eepdot.i2p
; (1.66) removed datagram_test.i2p
; (1.65) added socks1.tor.i2p
@ -150,8 +170,32 @@ cvs.i2p=dIvSbo35u0bc-5Vp0Wqs4YvarZS6l-y-URwOgH~NrRfQJ6mQmnfcmBJ1XPWswSHEGeRchK8l
linuxagent.i2p=OE-WAF6jzHUbUF7eytgiIaIKWh~oGf3c8~q-fUBbM2WntbXO7AhpU0QmZ~1lZIiQfzO-2Xyr0e-2VQBNexw73iOPLDII7mLv6xUnZmaKm-QPdvDEI9-ILPWgpMGJubAgA4ZmDgIUg2dSQ19Eq8lp5bwkuIUuU8Jd6JS1fvX-87G7PjcrE7vKM-Ty2gCnp7cWsadl8qYEkXLf5-CIr3LjGM-B-G~NoIqPf56df9ayHVguqT1f0c-d00qY80kUvb8DJ-X6VR9TNut6dKqRajI0earbkALP~wdoFggIIebqm1UXeI1tYROcrb155z4qu-4Nb~q4wzNAI9AVjNboIk50P2X0TrLNmjL9RWCrvHCt-r2ZV35Skpka4YmxATU59jSPw8jhXw1TgM-5DRswEtmOdxJMpkQU~iu~TJWmmcVvpkqyNMqXiqtOKqEgLTj8gOfNaITH0kdMVvXPKmYPTFimWu64T0VGBsqS~PQ50JxtteUP5FZv-dXluL8EbhtwQ7mLAAAA
gott.i2p=BxciUZkGQwi6Sj1Ucg5GGagDdwujlj8ClePa4~3d-1nnRJsBhTNJtvs-UuQYY77gPGNlpNk9dt02mxeS7f~pEC4E1KxJH9mhnf0OlIGB4hOOhDlXokAaKE2u1E-vVCDJlZCq32r~Ne53w-L2m5h5FAq5Bx7NXrTzWAEPgAlC8A2wASJWIF-EKOX5kfkkYoF6sKZqam5CxAAAMAUwmnbD--7wo8d2mz0C-c~oE7hzuHsg2J8yME5Zd~-FOZUxkoNCBJdfrVlVRn9~6685zqpaotL-SIqFK6~28ZCNMNCtnZcZZLeG2gZxq2pe8HtgzDrMMSx45vs3JLUp8Bap6~D9oFX0o7k2DQm-5R9D5NWhsJgol5Ny~8ABTXBpW~WQQOuKxE5xJauXM5rQia2yyXgldfOyGjxYnkrTGu6LaxlLmhyAva3ZbOO0mtLvrKLZebLKCYUxP7~TtNmSWEAzPKFYbjdZ~NjE0q4TanLFBaWotefPmy3IuAc8Mr7PbCvi4GmdAAAA
beyond.i2p=Gy9wxnDgcaNO8b-1gZTAn4GKWiMZHLSuOpXF9SSrVVO5Gx791OwZVNg9mMHvIkSekWuqtJwIRCArfXtaQNNe4ZJfl2bVNEKJonfXM9HLbNoJiSgmg-x75xYLXb04NDYhIbHJ4zSEpDFufjk1kI35XJo~32~qYynDMbYX9b3QtNfFfcBrOzH5iObTsMBBe5f2zEpjl1UWNGuKYa1tVHef65z6WCr16TUYSj5uDnUyTWWkwrgDZbTnRRlz--BQ9y4WiCgzQyMvRaNU-T6aPj6P9HlQIhV7efttpb2IRLpSKepIYG66ohBhI1n2G2dd18UHdR~PWNIy8x7AsCczlqnfh4lcJdiQqrNHQtpOI3XfM5gLM5sKLwrxbsQwB-6dnhonztTd1o9TWhTuA1FSlk3nRQ22E1~pQRh0sexgWjk1oa0Qr6kOojkY955aL6ZnD7RJx3fbeZ9JAy6s5uxYXuE1wkGQNUVEvi15jgVB8zk4GaB8ME6tdWVtcBTMjEqvulyEAAAA
files.i2p=1rI0OWp83VhVRyMiZkPmw~mEo-CVu548n4uJLsHJW4eNzQfQ~k8SSFGCMc0-YmOW8zvB1AmO1~hTy9qrT4-5rkVxvy7XTwK9-Mvb8N9fKvxr89yBitQvBwhsRKNScMBcV-qsauGd1F~wV~eb0BFjfZG2pyB~-AYuqZrqmoEf48Q~aXn5IKnIXkb8RmKbJNAws3opW3Tvbb-cHMhth3noc9QEpCJzzPZ6~hjjh0FTcPFiFAgaRIL81YcSfMTG7eTg2X20~5j4Em4GY6n8zHw6S5hbBuSMp~udmz11Bvc3QMcbfPMYhMxGMaqWUN8yN3kJwjk8griosz0o6XE~NcUvVXKZELW7jNmURd5e2a-zkSwjlOzoRi3OQhdeGypbUM0c-0-2OxOMJM~yw0bRingvRHT02Xr11nhyZRbysSMxFfxMY5doNKvJk~H7XhCaXCn0LtP7pd9JU9NPKzxIqbP8W0dZFP8C217yRmAHTfxbMmuYaQZenEkWmUVqlTyO-4NBAAAA
files.i2p=aEKZJq-9MfKzgKfxERC2Joj1ar-0c6dTrcLvwg4RVMI5uGCYfCSCRTtgFcwvEpVfTQ5RsGIxtPorEvbLjiY5TJJwtQj~ZWe3o2asm7Cwp-nmegXdAFyUSAKI33XUDvtVZjx2Erc~eGa39sIJrtJnfvapfRqmq1Xh9dcTMzEs9Aaz7cnMumTi6p5w-bBsQpQR3IBzsGkfmsUFaA4nwodz6ZhBs1Fqls7WrFLAlUz9hiKwtV-MrFy5RU5IH7elPkcN~5ReIDVBZ4axJZfMBOyszt3h6gmjyMiiaQ7keks8KDqFbQRZKUS8aRPCIzB90bzxbWZRO4gu0ApMeOd~tuIH71zYZ0UqdLOWo67iZhFz~njriskKH~1~lk--5Mk6487POUcY740ZC3FJl~6VXjf9Pv1n08Usf2Z3rK2i9QbwENsn9eH4T5bP5-JNDL8c3IZAHPo93W27gsNPAQkWrKC~Fvk9KK8jATpEBl2e5iZVUmwA07xLw~XJrFKQwaLS-KpPAAAA
sugadude.i2p=PIV8bZaIH4wRqzlzVLX1mDkELhLFdyLlKxcd2nUrJvd0YeTByls6LGFZ7wr0-9MT7cb3Ee23U1BApgm0~jT5bzFGIQOvx7AT8o0uMy~fcP1lhzo4lNwD5bHnf9OXe59FzORC3s79bwFmTtMCrEoYvQXCGherxwFfXywRPrJ2x0AjfjungiwX3vrndMdol1SDWTLdxTSARLPgLmswz47gGm4dOipjHBSGOm5bdgZBD6di8YGxHrB072QfXxBVSGzTgtQNnK4qAsZv1YbETk2CQ1nOKpCj0Zjkcnc1trTFOpUQKeOp6kiGCriAn8Ezbu1yuf2fiDAmPenFf8bDR7fNFR7kMDcKsbiht7fFi0wjPdE4Lx5H2PFyTuiw3~-osNsXFLtjvaeIV4P99vZRof1avADgp5k4pC8fEPKWfwXGPPFJjYEbWPE4VxmxcP93Iq-CMZfSd8fPeZNeBz8qL~n9IXJoXjO0G3nouQ7d1ZF4okVRO8FmZ91Umpy79v2I4sYFAAAA
socks1.tor.i2p=sl3oda7CJe3TN1JVoALIuoRAe--qYgDwo0A8PrUGaaWQ2xXiWYaTV7~FQOmv13JRCtbO-8rhOfGWnV-qia4CtRxH6nR6fytW~uJKRP4hbjhfvBuS1QdX2GwWlbgJdV8VT3OpJAe5iuKYCFnnSbVG2VZajtsoKJxHY748h1zhIh4YaMBGqoodQJrnvYWd4HJJ-ZauE-s-K2zWOTJ-EKZNp9UZr5DWDV3P5JTSruc7vyKiDVFra~-JT8mjnWBE9GKy1HiJKAe2UyKHBVjvJ9iL4bJ3HLH-z3oIP5KmW7s4czTq2fdO7yhssLMKxaGqhzsNViWv8FtvLJtHkTsprVQBeS2Eeqiw3iwcC4DGP4dUkj2Ex7DPqvxlA58Df0INgNz-kUPuURf0ZSK45v-s7F0f03BbwIu5eErLZhQckxs9tX6twSPXk2RaceOMA0-eKlbCh3FcpbFbfcimjOTcmpeUN7s-7C-XfM1vazIaIpTADwzZ9Ocs5TV~CU8TPyxZkx8FAAAA
eepdot.i2p=IXWmPK6689w4d2j-B-zQRdgG6md6UdvDZz9e3MnEWaoU7SiHk8Yrw4-2MSavhycFDkQNdpdJgYVhK0Ve8aF4TNf88KdI5SY4nl079CrjhTaNoQiUxOTJIUzTWGrDmNITfoV7PAjGWoMjw5dOcbK~PMpMTlOx9w8CtFEHU2cG4Gdcpu5NFpv6a5FWZNO-GRYrWfW1laZBviaE1Twwva-thbY~G5un9JFAoQZ88mS2JRKrxFuy1P1xCEEEiLvJfOcHVdL-0ebQV5q5Cwlyvrkki1se4URzlgF-FjQjTiaS0kbtHv6NkG-R19poUQu-ktMdMdDf2RLFBJPtyTvbyteyeXnI4LjGxSzQ8eH3txelIZKXwCRPYFNqutAmoQMqLf~NLDPtIq3QGp798sBbt3nooOKGEk3RVcJDwEzx~t3Qj6DWEA0xwSm56YYG84EYT2itIYGH8J4epFjGdV3zfWiME~QHLKfAwwkY-JdCiaDTk7RPfY1jkBw~SyI1jTYrX-PgAAAA
modulus.i2p=HL47CwJWiovjoHMTK~2tK6~sYEghJapUNkeqOdnkWFsf985N8eCpRUQtz7fFJZrHCTHjqeO5hxvcFye~Jjtf~id7pFlaotkpmbnx75x5HCiTIBLTUfbPurjhTXNK3A0JFxQ5lmYd8S5WqNykdaZGHjhfDSC~PUcfJA398S611e2D5se1LKAp0qFFhbNi69Unovj7HdXYJLiATDhewt7tY6mMa-p3BeTcvu26MhET4itYBhzp12AKiqWrEQM-4tpCmK5K7gxECHOTuMtZu7SEVc4CYAgBIsILtT0YA84441o8h0kpsKGDPFuszMWtz5XSIFlO7mwpu6PCmBx6FZbaZFSQ8CghxqhrWGnGIno-Lr9vKeTpD-AT7rqwuUrZHfd3yhGhOA7i0nM6Uh~qXbvFkzK83kyyxbX4InjAZ4g897LixiCc2qj6~eeh1c9oNcZ01-ho5U7lXUo5WrfG1-N7DeH2xkWGzFrjlZ3dXP8w~f7osxtth596Jz~cY8xNacMuAAAA
xolotl.i2p=byBX2FzIeKKRoUStOzidJ6jni4dkWBNK4x3khxgNo8NlJ8HqLl2MlgxlRsvhTHQy5L1GvTC9DYpnW0tE22Kl9QkPIGPikBLvwePT82gsRCxXOJ6CF8JiK0h9~giTOrRmguIo7d8DpDF3Sw7a9VVJf~9Dxs4IMCMPre36rPdmqkYSTZcfITfWCG1M1xnxQz9OuB6YKnnYcLvccSAcOTrWFEeiPHC0CsAo1EmJekmEGCK3lkNBRomfw05uhLqA4mFQgZbvHfSDhDv1eEUeQq8YBk4-vi4AsRjdmgLL4yduqoYnLgTYL6OvWuJ60EdxPkuZUzW8~5MGZfwKQLc4G0YmAjgLlQi1sdXT4HFFLNRvG2GO956Eay9Z3eLO2K7EvxY0C9MQ5n0LullF5NJD-V509h7kBPuItUC0haicVHXXwcXW-Lx36biz8apA9D24vMWvq4KLystsrtMetH1q1oyS4jA5BXWf~6FA~eCoZrX4lgnHJ6MHPCEmlUcQPrEvApIyAAAA
freeciv.nightblade.i2p=mDlAeW1BD7ictjta8G0YwebMBmATFIWVXrVF6gpm3A13NMi1LIaq5da~zuQBWD3BLYmblE-3-U4snRHttVJBr-e9jAvy7ZBskGqie30JbzPbTBzLJWqdpaKzahI-eiezoDgVyhOstCBbjFc3CW~ckiLfj4MVS2nCV7qgujSWHho8mspb8lS0uJJk0XQImYx4QWY7KFCz2CaHY8JX~m05~81hTdJLZUjhDVBXsjj0wW-lE8mDZckiCn7ZaRSASBhFHiZnB6aGdY32Ltad6SjH0LngOcVbR43DYSHODabD6igb90c00wruQRMFarQSBBAk31uWnjftggllcA0X77NILD0PKG8RGa2fRcqyyVveGgYWhsl8vInVI-q-xI1PWairamHkzd-du58cmxzkqYZQR~XX5tPR3lQ-avScr0HgxHkMuTZSxEh72BcQP-qw2m0mPDrE445EPw3ipk8pRQi6H8lS0kCfDi8DBEygnqoLIqu35wrpEICCyog1buvppZTfAAAA
curiosity.i2p=7tcdVioqcmDw4nHxleQBPS8-wO4IsYwmBQbYBeLWAXQsSenRUu8epki-wEMiDkHBf5-5RuOC26vy11f3taRZdxY8zbTWpgZW0L9ABS3vjTd0jkpPjuKhAWnQGr65W3YgKLxfoGO6jpPXOj8M1Bhb2aHSjHYRTpHorzzrU2KL3RDrJTrfkPh-3zNwYYHiZhcw0usSxoH4Kmn1AwjfJtKkhjRdw3e33QwZpvHyNzK6y5bvq1q5aTP0P-zm6xzKnOI0Kss6cm6Com92Oz3Rj~6wuZNCRvcUYelneMvgR~USjd~6-0R~Yrw~NkVW1fZYPFXX0u5IinGHMKvVzyiTclBYiAxFvkeiVcnkI5AJ4tV7I8sMnKPNW0o00zdjEgzVjdnFN4HXTtQTvHeFnfUmfxPusgtm3JaQ2WVo88-Sx9VA-JS6mcvG46v3QbXK4YHPdIgCYZ0Gq1Ds0CYblOks8hnKfObhnkpEgUYSsaUOAQXHlRLh8~D3MZibNamxCy31QotcAAAA
anonymnet.i2p=Iku15m2pHtAfphzLKbhptluZuJ2FSst-b6WaiP-DWLue579sGChPuZEVxnbriFouidM4hw2Kvud1AeAYm632kDZZzIBf5EyCEdAPwc9R6cfBqe2N4hcvAXyWH9jevGMNe~NMOnjNvl8rKm2bGcEprvo1FPXKA1Ene2ATPViBVg-2xQyuZs9ZcU8rSi9~PrtG05zepAwfAGM2kIhNdF4biECXT-SLSxkU9An3GtAZjNkRMdiMPSY94mZWEalFbscuL8mlhl4xFg7XsnA1TEn6KaMnBjuf3FV9XSlXZ7pUD87BrRZYFHA0ujw5FqGu~-qLuIkbUpyhYz2rIHxNNJHm9l3T8yvumCanwr39xvganMLAD4nw~i0PKqQ5tLx0WBo64-GMz8Rbhfkhhj4a09qeR6OmDMwiyHoBgv9NDM6TCYBbLFMjf6X3wbcap4oOTNXvO3RG5T7kF-1RJ2IH6V3SwL9JpdH51fTo2WgBL9cESCvGntRM9g~EFyGyMoBAcmilAAAA
sasquotch.i2p=6Hv24DvlxKauUfLHN4cPJd13cRyFq1wqaCb95r7fCYvhdFzluuZLDBYCM3w9GM3EVyAXS8uz0BCy9ZYncWXCsFQYejkTj~pTJEo5QjM1DszuUAGvDrqlm21e-ZwzEY817dIQCp9dQShfSnP41J~jVktE3ryMmtzAnCIZ~nMUejc9wManQG4muxuyUADdIiBIp-MXOAdXeEaaP0~5WPZhf0okm6uJd~fvO4h1Sy-CQuuJzWKYzDOvHFu-~9ONYQV8EdP4wovC15uHgX79YwAd8bpNTcfzJN9ypr9cQ0zGxYzDuK9LHaSD5MXQ8Hu2RgIrKUfb~osG4MQuYalBkUr3dpLzXLEokNIPk~BI~fI9AuG27wLI47fvOUB8Zbu1J9rSgz-J-~x8Tp4UFvRKDJ2N~5rj9mMdm9iO~U8DJvkOjjRraXhwqh3YcrnhHLPia10jvu72dAGi~S4bxiHuEcDn1JZIj64Jb6TC6xfhwEn-uoE~dqXdbr0MMoVcDsOP5dPjAAAA
orz.i2p=GALU66G3Deh71g1COXAWKQvwVB2ULbf6ALTvc-vNHfZ~jkLhGpxmXOlVOu17hfWhB~whwj-~Hn7dxLgbXO1WXb-xmvKKIgxUssDosoSbM2v~LgzJq~iIkeoN9VQe4tz2pMql1-f3kV8-AHZIILR7tY61TjDLQbZSVhT0S7FIcTQulbct8wjP-p4Dvp4XUUFXJDRqIpZB9beNALEO-SYpFuadDeavDY4iUhNcERDXlj38ginXceHm5SXaiUNTr~qXzNNuR0r5FsAgjHLuy3UgnxM2aL~j7PXBieNL~mLs~YcYRYvZVtG~SXKQjUqw5iUJYYutgqHsiT7IrQKZ~FgvIYJWaZBgF-84WsfHgCw6jECkiiLgUxh676iNqMSfTkp0KMgjt~ekU9aWpZdiaD5KV2-phiuVMdEVbXCGfLnwSqWIQIrSAAXvsjL8ew7sCbtBOriGKUzeL~rBBltL6gSVdxn92fVdSz2XWGExvaT856ahgOtD8RFqCQTAUm~63xuSAAAA
microbleu.i2p=V-3EgQik4jTtYyE4OXHBNl2kslSh-1g7SvtKvt-xzm32Q0zt07meIq2Brj1tkv~xll8t1gyfvy0Wn1n7Pcf4tlSEQ2KfJm6oHv-ZIovxewn420yl8cLgbB-dtoiY84fRxl8pS2GTHL~SOFOzussSSocKIBay71ODyhe4mJdtFA76LY6ycgsFIxK4U~Hb5YcIbenO3-94Oc7PXaRt4XmQ-7C9ZKAJXhLdlQGF3407gituMrhnppwQMSQP2ULJEBiLNtB7h1KasNn0Ao~HNf7bsJyCB29YGgIESIIAZxrBFNsjOpYg4WE3iWfzU-C0ohOCIvQhMXS0jPitke9R7Oor8TmJNagfZVnE2zg2oIPP8uKqf~kFTugeI3dUrlfdSYr-rvzEGzRMOFIT9GBEXnuNPnX3tUHD0LNewP-D3VvnT6Pu~0kc7s8gyjc5~gI3vj8gn8gQnnFnz6BTYfqLufF7UQnJtEKrCi1eAG8Dnbc6vaoA9qoAEXAtN5SFXiprqwBvAAAA
www.postman.i2p=r8Ti6CtRJtSNAhxaoJuHZnbhAhjrbHIzMAVsuCNmLA7NO78mHvqLMk0HjQXou5fpnE40rCz5mxg-Zfz6J6B0rpE-UiqiozIC1cM-H-XM09Ftu6Ta97BeQ5MdrjtzJlagwXNM1Cn8k35UAdJQR2CfHQfJv0BUGVmkU8-D42fLqAVuRU9WpmA44YNMXJMT7kSqiZxKPdnXbpfst2h6ERLqGbqgCEToaH6NOyA8Lu4FJbOfkzfhrVIWHCiqn1FaAUK2Ie1POhfo8blfhQ1NdgkxGnTeaD1MJolI1qatpBWpsuiPlfMy7rxzTmP2dwpi-QQEdISi8U2eag3jsKGopQXwyn7Rx4zPQd82e9LCpiXWg2HVco1QV4H9z2alSHVJkWU-jC7jzQkDe-Iws-ZfEhKENRDps9Ev93KU-5-fohZZ614Ia0iDUs9RxP3mOM-RTiers~2B7Iq5-6hNMEoIzPzGbMKrrLs1Bos6LoZmIwGRS5pvlAis7usHjmBCy2hwqvi8AAAA
smtp.postman.i2p=AUVlGWmbU0qNUBGvu1ls5OQcdZNjq7k1OPzqkhheGlc3HoQFi97-3BZevo4ThjL0WxHoBIMagAfXjAEm7EegzYqkfGtQ5TpEVAxg01eIGO-Lqq46MqbOFX~edpgi9StZIAPaXJps0MPtSfh0FsL-N5PTW2nqkITrMPxOuhyl8CvAdR~TxHSImMI5nNzCW39xkDrEBx5Mkn1s~xm2XdhyttG2z~g6~nec4X3rpV0sO1Fq2T1sgaKcHWUvJofzUV1y~bKnltJEB~KZpkkOxjz3SdcJzn8SPblyWAm3SUiAVY8ev2XwSaLl7-xe2bQtrnjbPfpHZ50yhGY-00wTcXEfSgt20DkibP7xg3cSAcVFla2IiGeJc~4WTCP3vkBipTnSFdNr3Ez2kCEa50kMiRfDkIk4S2E64~JBrgyNURXL4YNC~FV7qaK2gqHUJkhRiRQP2Hbq2lDygJGFjr2vIi0TBoZ0lWaX6cu8Jywhp-jZgrvun-F~n87iRvnNjkjuG6BYAAAA
pop.postman.i2p=W4ZehJpFv9HnM4qnWb6c305H1vWRoghgSz~UXVVl7AM6cNYPU~DHzI0ezxyYIcsywhDvRdSvN5QQSyb2t-waFRaoMxmub9Z68uvcNPjAyG1Hc-igVbcmiGKo1RwXXO1JTzaAFE8NB2mvZmpksPDks0ugcd3AzBrrkxsqt8KLYSXBc7hDM59YUGl0j59NUZoE3tJgZlQ~fYr4P4P9VetBgVQ5mJFpwF~RvM2frx9zQ2SonCSTNFR88MEUJ6EoZUTDG-iz6mUVv2qOanAD1ZfSmmMZpbtFLovqvUGv8JLxFhFdkNmt3AmIZzPjmvrAu2X~VnAhX80Wrvba8df9w0qtplH5LYBykmN~wndjcQxOH7~LbGeJ-8q4DHf-DSDPFyn6L4uCk35kCSGXjEf1f44DSHU338Wiagn9ut7twiFSf8NLXvKV2-uWmx~twDLTd9cEjCEBm7ZXMaqWTK1kOGj0nlybm~mgMW-~IRnb6alnR0GMxAE-6fnWS52PJtmWU0ToAAAA
jake.i2p=BzdF-Ib~jWRj9i1hypIasw67yv0YDEdCqloJr4Qq2Wvkt56slqzd0Qri6FQnWsGfTpmilvGI1H-7WAyU5KXc~nQ7F7KB~Im9cI1mIeMKeGK4Yzb6jV5SHctDZw2j1hqMpdjU76r45WiOgiAaNFin10SJ8IriNqce9VehnOupB53tpvoj1GnEIyxxaOsSDmxIakFZ8MsLW~s9wyxnlfvP-iOXLMpM6Achhcmxj-DWDcjwK-6rvLYoG6SY5vmpPNzhuyAa-8di8N3zBxL6WTf1Ox9UQrpXXZ1luYDUNxpFfPjzl9ZPpv4Ax6QzJvWn8B85GYs9ZkS1bpbtbbmQ1DTlOpwFMmidr0vaf68hNGvVY5yUgPZy28s8jvrz4AMEfAGlsozRkxr2JRwm3Oj3dqeW6ICKRwnThkjnXnGhQiIDV~bDfkgBzWvh4TyIYm5NBwAYFZdSx81Eno4sthfElFTeSDj0TjCKegNmAd6t4D82JMk8abws0MC-pIqaNLZRU1YVAAAA
jabber-2.i2p=62UDEWG936i-w144HMYX~1aiRnvIkRg2Rbjor3KXUPfKaPXx6A6R54nhLYVI0Zt3Y6bquRIlvZd1KKb6VnQ~GbPhDKvOIzrSP~G7IajZOdncG4QlsuJYwOVVOSVAkR55pxdwwVHXkoR4~4hlahYGvGAayvvDxxWojr-dWg-IIUnEv8hjjstUfphSy~Jx17de0G~8n0DSZ~YKu83Cnu9sSydoXqqczq3KChFDjfUYtohNiZA8lXfrfv5K3pEeTOCemE1rDflPGu~5fawk3G1xNetuZQCKdkFNSxtkRpj3Vm2ej1ApLUi92zog3ekBbxEwfiP0DfC4BvdwgAimVGH8MpXS4BijlDNNbCWak6Pdr24OqVwLJXzWfqb11WFH-mc2JYJCPhJCvESYs~iK3JOZ0QzbW9F-495oB6RD-9guK9bBQHC1lMsNVhaj3Mh8gohKghvy2MrTNbirHKDjyyiAj5RrT1OjWoZNBMvXPEZEtEowuzZptP-tTf19mhGfPFOjAAAA
identiguy.i2p=EhDXPsItHi7Dfx8~~0iHPzkjiK1568wqEifARTr-ngIznwVaBmeEnSHXfLkA-7F~Nrqw95yQLlZpja6N1DyQNtJXPx-Db3apXuKwsuTNSGgZme2kFHXPh7PCsfzARwxn4d2-Nx75V4BSCgQr0gRjDa~L~JmtxPRUpZX2NyBvD0w5MSaD~9t4RuCRQ2gVDpUUDcTh2jkm2bX-R9vRIHhCZLasHxDR7rJC9-38uMsQ6ywd4ulQQYIs0kB1sEofdXUypyqphQU9xIxz7azgzKJp~dHnJpJ6SpaH7UrDN-YfOkAvGFBbk9k-u3NGl571Y205dQF~l3XHvVKf6NS9~JvtVh0JibwQ2qLVPy3Y~TtViffvpunNGOrfnVrT5-1c3lWNHx8gTalY3R-vU5rDd8Bz3AhNM6KWktZq8LiHHhJUxIkebXZsid-f2g~QgRRywxG7ksIlKsCMEn076f03L-oWJIlrAg5MdQd-qlg1Bs1IhrR8YT8jNYrX1bGdlmPrJDpiAAAA
detonate.i2p=6swCa-wAcpBdiCnMdObQlQ15isZTpAV8rlArR54Q5dZkALb5iMCjt0hYMoz89R9ycmtxcTNkgdNX37JsuAjeBFk~nUI11EGSyu-T3AOJg0sxUkoESuxqyLajqDSx8WVziI95LM8zOg~Gv0hpObbziMzu8hzLML2fTWF681sBA6gFzVSnjK9zA3cbWX7f0zGo4crruXsjrmJqWfS2~fNt5RYwHth57u-rZ8a4vl58uyv1Gb1~FAlyjyGMcr7eaQq3UJdEgHOHtXZSoGXZSAerpFreeHqC6Mu~-~-T7iph3jlfNyJYxLOBD8QKMPW~hGW9l6ips5zcc0KFjX2PWH5y4nbttb1LNvI6zn-q1F5t-ITP7UIovWcINfSzy5YoiuT1K6ZSay9DQSeuldBt9ajXDCkJKxOiZlB9C0RAmd9WDyAIKt~HhdnlpA~yOUuthBzrAe7iXt7lUqY3Rwqiby1CgqPKY941ChVY57AwWyxG4clM-a2MB8GuBImfqhp2LDFWAAAA
tinyurl.i2p=mfjh9H8RPkh6vYTSpFoghxSnrOzwBeE8ri3mIKwQOak0v0382ZawEDavYKcjIGg6xqzC0KA8KcOu0h151HzHZgCq2Qbzb6VGGSyyXqtsPGxSIjifIveSkIqNZE~ThySOiWXJ9Qp1EmFe9zA2m6Ey~lZL~yD8ackLzB6VY32xQN7y8kvPSt39Boty0mPXSvS5faVAd0UkbuEHOCP~~Z-E~WB2L2-ZWUD~P3zrZK560fa9-aU8g6pCjIJ8Vqx19tbkvYA~IUPtgXsgmjX6hWLpxN1oHE18jsj1IcAu0TCfldWyOtqcGOeBo20BVLZnx7-AqS0TiZhoIj7nog3x3xoOH2w6JomdJ9AT~4Z08pz0HvymVud0PXKOU7yP~QBj1~Us05-k0F-W6SmU6906l5YfNoXACurwy0VtgTmv7oJyOWpPdhFkZ98PwPL16W4iYsvZx6lDxvCxViAkYLw-fAkmueKrn2ZBau6TlhksvZt-Fqyjs~iZ4yme1243d87IZNb5AAAA
b.i2p=b2Ol9hg3uAOK3mB7sK~ydOZQlZIcc0Cixh~M89i71VgzxsK6NYCvFuiW8YeDZ9p3e-cNQrGTxrV6405km0F~pfKKyu09E-~-zAM19SHFmVWbT-XLEpfo9krLQu7416VMRPsPfLISzRU2wAptW2N6CuQmOEp8telmilhUiEqYTC7GBzmo-XNIKs9XSs3-6wSLkZ0haRJtS7YDdmjrDE-UXhu55E7VhUnm7TAxSH4bdUtM5tDUU7Q8lq~t7y9qSu0EWwvMBnkR9-p8SlcVBr-Zy4aG10OUcvnA07TA~Y8OKtEzJZ1PvzuZVIKerAQXuLK~SMB-9Xni7v7pbGW6Lok6dG9KJjrLORTpURAFgC~mux096li0VcotR6eMfBwGH9zLWgBruknix3TCrmUoafzCP4vyuDrugN8-0k54HZemGb601~9PJQYwyP-V4b33LJdq1d-tKDvgFqR3mm-YrEgZbUQ22BSsI3Jx~tWc3hft8KHkfF3zV6Op4TB04XeQ-WupAAAA
beyond.i2p=WNtBKmOowfzhsCX7dWwddaKVZttpm6aSYDiXzExh2ABPS7Ts9WBZxkqWS8CdEHq6ZSOUGzT6kyQYRGmF9~uTZ~nkI-UG8Hqh6uibT0H6-NOR0k2Qjp5y49VB9VRHJ6I2Vxg1uo09edWYKUatMgl7GESInmhZPDF4NOAq1SD6TQpLfAvVxPWglISZWg2Br3K-u9cHqH3Q0fvyy-Cka9ALZiBkXlZ5vccy5B2KE2JXdI1fAn1TLx8dKuR11Xsd-aNjPofis1Ak-cTia9YjKYSiO4v0A7uguQwBZCmTwI-uXLzUeOZjg69NuIhFUi2hN3tuNvCqAg73ct0lAMnfZ1d3oT9BGW6Aqh4fMTXr8MPT0Qpq9te7KF7L5q-LJ-9JFnYVvjx0P-6YUe-mH89HwyL4nip-ET8jzgIRyYE-WNzc1Q5KOGcrQVZJl7Vv5lziCCMxYoSKqiThps1vv-lC1uEyPbMYANkJVJdx9bnTqlQ8r9rlmFFQEmBCojc7LeRZpslTAAAA
marcos.i2p=oN0aQlEygTHH1M2zLdkg1S8TcBaUIO4LQHC78R45ZImUCyOXVGzETk2AV6OD7LzLuzUOQSSfoHyVdctYMqq0buqdB6lzv1zKeNgofTHLvITEUJM5xemIz9vE91BIURjBpRVuonCGEQSBH~xqAsixv4OGtVyts5Nvle~vM~apZCBzEGEtWVikCyHSWL5MCBArbaEtYtCpUN-D2I628pb3JhbQppS3HCSa7b3tMFIIV-bqEjm1IIhvvcb5yyix78XKw~KAuE7KYvawXDOuJ0gtH6Hdox-UHMAWNP2~55oz-PPbchj3KvtL0yhI~6MJYKSpQEpoOsDpVDwmcnyU3V0NsEnab2Jjo0Cir7fmFIUjn9EWFjWk3yL5vd6qLxd2fjfO0Azqzb0uHp4wnP2sz25MPOpQMk-dNoX7DYI7LC9A3Ozu3yTisE62LVi8IRFB3p2O34DYN-flxR8ytn1B492x~0Ka80gd~HdiNo2rO4Qc04PILXe98NQj46kki8poho1DAAAA
ragnarok.i2p=mkRjUNekHYH3tpLDlt-qMSS5-SPPjLpvaIuCYOrNRM0ehuH93H74WuldWrDqzr77hLS3~JGD7qKVjaz9YDTDhK3dGV6K6xuXomJbShnYIQ0b-S0SZrhLXC8KIcp~DLQAW6rmoiIsyr99nZ1RPyVCOI~futZ9UnmZRsWbMu3z3dZfh7wGLeoAQAmnXW3IMxE7c0NFul6ojLGZkbK5Wrh1~gRESfB3TSF6TurQMaoL2Rf~ehVWFg6jtpDJ1UruwUWzyMapVqbe8Rftg9bXq6UlrBq61xuseNw20L1B-S0Gif2sC6DnhLT3k4MLnjVpB6OySQsuMoA67cnuLm8CZ~v9tH5~YlqU-hRH3yd-AFiMY4PkepI-Gjh--tr4NSNGsAlqM2XOf4z5XsdqQeT4GhWqXHjzb1nZcKmfumb69vOhWX9DuyVOq~wfY-RIuxlU08K0wV4IqmewK3TCIjIWTzpcxFwsZAGKx2QxMF8QXsQz1qTDSlzvX6hrnf1NXYXBPDVhAAAA
nano.i2p=oeXpaAMmVruU-7boMLZvnnLWvPnl-~WqLJKiJOigwiAs4c9umoCEo1gnN7IIr0XoloC4JmTZjsRfD-LE4kKKLKYMpw4hfGEQ74y~8fnf0Xe4gcG26l5O5RvKStZdK1pYzYioVfCgQk4a-RZfBQSd5l2mSrSHvpqzBmHrW9GZt56OymRELLHxfXn15AJXIRPdTtuiroXcKPMztZyvOsOWX47bkCcMfYTMJIrlYrlomh8uG4e45Kb8nX3uehy-kwPGFxMyJWMLs5ra7K~WzCzf6hbcdQ5vJVmYRj122WEjmtAl-gEKcK0XiLJMLF9y4aoIJGVHeA71a7mfKvSBEHjJ5BubC2IRoU5ZKxAkHtkOfoyDZ7ZxIB1r8~jAE2aPJzotQTN3l2gpcOFFcEapxXXWvFmIPjY0veorUGTjkXoGaLNl0-6JrH~w~5q2-bIt1mbWMySqX94YQmVTFYejArVS99NbdOIjb3VQoXKVXzjyu6Vr9BcfyYK9jMuqiztb-UfLAAAA
irc.orz.i2p=0oLxoY4VcI~e~OnFdHtYp9aghBcBLBK46BE-yffvaxsnsNgmf5xiRDORVJ0xjg6e8LZ-gTUXr5EHiJnJa5GEv3sris0PWEXW0QLV2WbkIOI3XNR8sa1WMAJYFX9wYSHNdGDuHj0Igcd9rxwaBufdcHeJ0WLvp1FXCMxEf9OTh52wL3ku5pPGlXaVp22RhJXqaKqGeIFh0A9t-gSF5cPB7D3jvoXPJlZtPFLkrsVQeDCpFC9TLaI2sebJjuEHaBhCLvjA8usG8WiZ0m-TVNwkg0XV~yy-rpOEGq-8CCLa1-7c6mc0gMmeolEFz9L9x6hXzw4TR0MXNMeu8nqeMhbvqws39iLiz1L2r6amzQrXjHReX1ibrEprNfLY~Ne1cgqQ5VsHpCr-UJv3D1QoFCJUwG7z0dJrgoi37aPRRjzUF-z67hi3z-ghKrY7d3m0YpTHUCwZ5o2uwUFM7xEvCXY4nQ8ndwEI0xJKfbBgWEmR8OxV3jbn9ZhSYSoygsbUQg55AAAA
utansans.i2p=87krkpFeYipyBHntsD2t-2A~pMW2jJG~3n2ZsvyOFeajYMAfr0s1G1EMxu~ZfywgFmWNir5pLlaXmAgd-IerlUnRF0FJrqqDlZVllYnAp-IZbGqg3PygDhSgYkuTh67l5wQEQTqwPMoz04F9O4w2GfzU-316xp634yWIYiFzeadHlnMVj-dsgM~namEo5dGmMWBpzk9iFkseTOtPJPC8e~TFrOhCPpPIbRPk6YkaVGJ0IyQ89bUmrRCH8ZiJuziY5i8S6YmGCuUIi5q9dLyu9~c5ucRQn13Ja9abWDXKgPF4MrErT4P5ATL-iQ~a00YF-YSXbSB~~0g3288QQwAN83oOKwSobL3vMXfmWyB7oA8zrjT8a4R4~tN3KXqbPTvkc-fZ-XDSaR57NssXLrz1DvJwhlQc2wcPAepFLX9c1IwgfF5coBwtZaq16gamvprRH9GCu4iWbjqhnQnaTiB~P1VqXRPGv5xI0JgcIgW0Q1H8vB9y2IQ~dW1gbGIiYdo9AAAA
blueheron.i2p=ZQkG-vHKcOcfg314mNZ9iNTP9Zl8W9H2gZzEPx9VesuIkeYRFQD1JbPvxWq5DBUfOzRb36c5pJeFuWWh35SViwmoJ~npcrqMWnfTtCq~JBYKCVYhpDZ-oo1Vg63GRwZ0hwaMv~l5jPWISdg~a5as-Cg69od~s86pdx9ci2GqSgINcyRMeBkWG2NEAUZSyhiMjSgW9lMVwA-5aXIiGlOaiKv4ldYZxwS9hpO0NLgWIIrQ7o2Nr5OL-Kh~FrShgSQlMSd0QwMFS0~QGKmcL7AmyIme3szLA9oN1bnKJ8ztRlTx3dzCI7nH9WXKmLWqvQck0PdRIT581rYw7qfU6uwN6UduS8enWnSXj5q47T6KzfWYBQu7AXzaatQGdMB6ct51Urfl56bRLT6Bst09Z2mvyjELlgQjGrVNoQlmRJ-e~y2kUmc-OYl0j~3g~EEiXnxmYXO3M6h54d60QcJgY7ccrWmnq7ayi~fBXXshHtZD~6w-cgQ4S5gBnpaEVG1Nt9xuAAAA

View File

@ -1,4 +1,4 @@
$Id: install-headless.txt,v 1.1 2004/09/01 21:20:03 jrandom Exp $
$Id: install-headless.txt,v 1.2 2004/09/02 01:54:37 jrandom Exp $
Headless I2P installation instructions
1) tar xjf i2p.tar.bz2 (you've already done this)
@ -9,9 +9,6 @@ Headless I2P installation instructions
If you're having trouble, swing by http://forum.i2p.net/, check the
website at http://www.i2p.net/, or get on irc://irc.freenode.net/#i2p
To have I2P run on startup as a daemon, after the above, run:
sh install_i2p_service_unix
To run I2P explicitly:
sh i2prouter start
@ -24,9 +21,6 @@ To stop the router (immediately):
To uninstall I2P:
rm -rf $i2pInstallDir
To remove the daemon setup, run:
sh uninstall_i2p_service_unix
Supported JVMs:
Windows: Latest available from http://java.sun.com/ (1.3+ supported)
Linux: Latest available from http://java.sun.com/ (1.3+ supported)

View File

@ -1 +1 @@
<?xml version="1.0" encoding="iso-8859-1" standalone="yes" ?>
<?xml version="1.0" encoding="iso-8859-1" standalone="yes" ?>

View File

@ -13,3 +13,8 @@ clientApp.1.args=sam.keys 127.0.0.1 7656 i2cp.tcp.host=localhost i2cp.tcp.port=7
clientApp.2.main=net.i2p.i2ptunnel.TunnelControllerGroup
clientApp.2.name=Tunnels
clientApp.2.args=i2ptunnel.config
# run our own eepsite with a seperate jetty instance
clientApp.3.main=org.mortbay.jetty.Server
clientApp.3.name=eepsite
clientApp.3.args=eepsite/jetty.xml

View File

@ -0,0 +1,24 @@
<html>
<head>
<title>Welcome to your eepsite</title>
</head>
<body>
<h1>Welcome to your eepsite</h1>
<p>This is your eepsite - simply edit the files under ./eepsite/docroot/
and they'll be reachable. The 'key' to your eepsite that you need to
give to other people is shown on the I2PTunnel configuration
<a href="http://localhost:7657/i2ptunnel/">configuration page</a>). </p>
<p>
If you have any standard java web applications (.war files) such as
<a href="http://wiki.blojsom.com/wiki/display/blojsom/About%2Bblojsom">blojsom</a>
or <a href="http://snipsnap.org/space/start">SnipSnap</a>, simply drop their .war
file into ./eepsite/webapps/ and they'll be reachable at
http://$yourEeepsite/warFileName/</p>
<p>You can also reach your eepsite locally through
<a href="http://localhost:7658/">http://localhost:7658/</a>. If you
want to change the port number, edit the file ./eepsite/jetty.xml and
update the I2PTunnel configuration accordingly.</p>
</body>
</html>

View File

@ -38,14 +38,53 @@ tunnel.2.option.tunnels.numInbound=2
tunnel.2.startOnLoad=false
# local eepserver
#tunnel.3.name=eepsite
#tunnel.3.description=My eepsite
#tunnel.3.type=server
#tunnel.3.targetHost=localhost
#tunnel.3.targetPort=8000
#tunnel.3.privKeyFile=eepPriv.dat
#tunnel.3.i2cpHost=localhost
#tunnel.3.i2cpPort=7654
#tunnel.3.option.tunnels.depthInbound=2
#tunnel.3.option.tunnels.numInbound=2
#tunnel.3.startOnLoad=true
tunnel.3.name=eepsite
tunnel.3.description=My eepsite
tunnel.3.type=server
tunnel.3.targetHost=localhost
tunnel.3.targetPort=7658
tunnel.3.privKeyFile=eepsite/eepPriv.dat
tunnel.3.i2cpHost=localhost
tunnel.3.i2cpPort=7654
tunnel.3.option.tunnels.depthInbound=2
tunnel.3.option.tunnels.numInbound=2
tunnel.3.startOnLoad=true
# postman's SMTP server - see www.postman.i2p
tunnel.4.description=smtp server
tunnel.4.i2cpHost=localhost
tunnel.4.i2cpPort=7654
tunnel.4.interface=127.0.0.1
tunnel.4.listenPort=7659
tunnel.4.name=smtp.postman.i2p
tunnel.4.option.tunnels.depthInbound=2
tunnel.4.option.tunnels.numInbound=2
tunnel.4.startOnLoad=false
tunnel.4.targetDestination=smtp.postman.i2p
tunnel.4.type=client
# postman's POP3 server - see www.postman.i2p
tunnel.5.name=pop3.postman.i2p
tunnel.5.description=pop3 server
tunnel.5.i2cpHost=localhost
tunnel.5.i2cpPort=7654
tunnel.5.interface=127.0.0.1
tunnel.5.listenPort=7660
tunnel.5.option.tunnels.depthInbound=2
tunnel.5.option.tunnels.numInbound=2
tunnel.5.startOnLoad=false
tunnel.5.targetDestination=pop.postman.i2p
tunnel.5.type=client
# another irc server, linked with irc.duck.i2p and IIP
tunnel.6.description=irc.baffled.i2p
tunnel.6.i2cpHost=localhost
tunnel.6.i2cpPort=7654
tunnel.6.interface=localhost
tunnel.6.listenPort=7661
tunnel.6.name=irc.baffled.i2p
tunnel.6.option.tunnels.depthInbound=2
tunnel.6.option.tunnels.numInbound=2
tunnel.6.startOnLoad=false
tunnel.6.targetDestination=irc.baffled.i2p
tunnel.6.type=client

View File

@ -0,0 +1,160 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure 1.2//EN" "http://jetty.mortbay.org/configure_1_2.dtd">
<!-- =============================================================== -->
<!-- Configure the Jetty Server -->
<!-- =============================================================== -->
<Configure class="org.mortbay.jetty.Server">
<!-- =============================================================== -->
<!-- Configure the Request Listeners -->
<!-- =============================================================== -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- Add and configure a HTTP listener to port 8080 -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<Call name="addListener">
<Arg>
<New class="org.mortbay.http.SocketListener">
<Arg>
<New class="org.mortbay.util.InetAddrPort">
<Set name="host">127.0.0.1</Set>
<Set name="port">7658</Set>
</New>
</Arg>
<Set name="MinThreads">3</Set>
<Set name="MaxThreads">10</Set>
<Set name="MaxIdleTimeMs">30000</Set>
<Set name="LowResourcePersistTimeMs">1000</Set>
<Set name="ConfidentialPort">8443</Set>
<Set name="IntegralPort">8443</Set>
<Set name="PoolName">main</Set>
</New>
</Arg>
</Call>
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- Add a HTTPS SSL listener on port 8443 -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- UNCOMMENT TO ACTIVATE
<Call name="addListener">
<Arg>
<New class="org.mortbay.http.SunJsseListener">
<Set name="Port">8443</Set>
<Set name="PoolName">main</Set>
<Set name="Keystore"><SystemProperty name="jetty.home" default="."/>/etc/demokeystore</Set>
<Set name="Password">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
<Set name="KeyPassword">OBF:1u2u1wml1z7s1z7a1wnl1u2g</Set>
<Set name="NonPersistentUserAgent">MSIE 5</Set>
</New>
</Arg>
</Call>
-->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- Add a AJP13 listener on port 8009 -->
<!-- This protocol can be used with mod_jk in apache, IIS etc. -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!--
<Call name="addListener">
<Arg>
<New class="org.mortbay.http.ajp.AJP13Listener">
<Set name="PoolName">ajp</Set>
<Set name="Port">8009</Set>
<Set name="MinThreads">3</Set>
<Set name="MaxThreads">20</Set>
<Set name="MaxIdleTimeMs">0</Set>
<Set name="confidentialPort">443</Set>
</New>
</Arg>
</Call>
-->
<!-- =============================================================== -->
<!-- Configure the Contexts -->
<!-- =============================================================== -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- Add a all web application within the webapps directory. -->
<!-- + No virtual host specified -->
<!-- + Look in the webapps directory relative to jetty.home or . -->
<!-- + Use the default webdefault.xml in jetty's install -->
<!-- + Upack the war file -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<Set name="rootWebApp">root</Set>
<Call name="addWebApplications">
<Arg></Arg>
<Arg>./eepsite/webapps/</Arg>
<Arg></Arg>
<Arg type="boolean">true</Arg>
</Call>
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- Add and configure a specific web application -->
<!-- + Set Unpack WAR files -->
<!-- + Set Default Descriptor. Resource, file or URL -->
<!-- + Set Virtual Hosts. A Null host or empty array means all hosts -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- UNCOMMENT TO ACTIVATE
<Call name="addWebApplication">
<Arg>/context</Arg>
<Arg>./webapps/root</Arg>
<Set name="extractWAR">false</Set>
<Set name="defaultsDescriptor">org/mortbay/jetty/servlet/webdefault.xml</Set>
<Set name="virtualHosts">
<Array type="java.lang.String">
<Item></Item>
<Item>127.0.0.1</Item>
<Item>localhost</Item>
<Item>www.acme.com</Item>
</Array>
</Set>
</Call>
-->
<Call name="addContext">
<Arg>
<New class="org.mortbay.http.HttpContext">
<Set name="contextPath">/</Set>
<Set name="resourceBase">./eepsite/docroot</Set>
<Call name="addHandler">
<Arg>
<New class="org.mortbay.http.handler.ResourceHandler">
<Set name="redirectWelcome">FALSE</Set>
</New>
</Arg>
</Call>
</New>
</Arg>
</Call>
<!-- =============================================================== -->
<!-- Configure the Request Log -->
<!-- =============================================================== -->
<Set name="RequestLog">
<New class="org.mortbay.http.NCSARequestLog">
<Arg>./eepsite/logs/yyyy_mm_dd.request.log</Arg>
<Set name="retainDays">90</Set>
<Set name="append">true</Set>
<Set name="extended">false</Set>
<Set name="buffered">false</Set>
<Set name="LogTimeZone">GMT</Set>
</New>
</Set>
<!-- =============================================================== -->
<!-- Configure the Other Server Options -->
<!-- =============================================================== -->
<Set name="requestsPerGC">2000</Set>
<Set name="statsOn">false</Set>
</Configure>

View File

@ -39,6 +39,7 @@ wrapper.java.library.path.2=lib
# Java Additional Parameters
wrapper.java.additional.1=-DloggerFilenameOverride=logs/log-router-@.txt
wrapper.java.additional.2=-Dorg.mortbay.http.Version.paranoid=true
# Initial Java Heap Size (in MB)
#wrapper.java.initmemory=4

135
projectplan.xml Normal file
View File

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="i2p" company="i2p" webLink="http://www.i2p.net/" view-date="1/9/2004" view-zoom="6" version="1.10">
<!-- --> <description> </description>
<tasks color="#8cb6ce">
<task id="0" name="TCP transport revamp" color="#8cb6ce" meeting="false" start="13/9/2004" duration="13" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="6" type="2"/>
<task id="1" name="Refactor transport.tcp.*" color="#8cb6ce" meeting="false" start="13/9/2004" duration="4" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="2" type="2"/>
</task>
<task id="2" name="Session tags" color="#8cb6ce" meeting="false" start="17/9/2004" duration="2" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="4" type="2"/>
</task>
<task id="4" name="Autodetect IP on connect" color="#8cb6ce" meeting="false" start="19/9/2004" duration="2" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="5" type="2"/>
</task>
<task id="5" name="Web UI updates to notify errors" color="#8cb6ce" meeting="false" start="21/9/2004" duration="2" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="12" type="2"/>
</task>
<task id="12" name="QA&#47;support" color="#8cb6ce" meeting="false" start="23/9/2004" duration="3" complete="0" fixed-start="false" priority="1" expand="true"/>
</task>
<task id="6" name="0.4.1" color="#8cb6ce" meeting="true" start="26/9/2004" duration="1" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="7" type="2"/>
<depend id="8" type="2"/>
</task>
<task id="7" name="Tunnel revamp" color="#8cb6ce" meeting="false" start="27/9/2004" duration="15" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="11" type="2"/>
<task id="8" name="Spec structures" color="#8cb6ce" meeting="false" start="27/9/2004" duration="2" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="10" type="2"/>
</task>
<task id="10" name="Tunnel length randomization and ordering" color="#8cb6ce" meeting="false" start="29/9/2004" duration="3" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="9" type="2"/>
</task>
<task id="9" name="Implement tunnel processing and building" color="#8cb6ce" meeting="false" start="2/10/2004" duration="5" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="13" type="2"/>
</task>
<task id="13" name="QA&#47;support" color="#8cb6ce" meeting="false" start="7/10/2004" duration="5" complete="0" fixed-start="false" priority="1" expand="true"/>
</task>
<task id="11" name="0.4.2" color="#8cb6ce" meeting="true" start="12/10/2004" duration="1" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="63" type="2"/>
</task>
<task id="62" name="Streaming lib" color="#8cb6ce" meeting="false" start="13/10/2004" duration="18" complete="0" fixed-start="false" priority="1" expand="true">
<task id="63" name="Packet spec" color="#8cb6ce" meeting="false" start="13/10/2004" duration="3" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="64" type="2"/>
</task>
<task id="64" name="Implementation" color="#8cb6ce" meeting="false" start="16/10/2004" duration="10" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="65" type="2"/>
</task>
<task id="65" name="QA&#47;support" color="#8cb6ce" meeting="false" start="26/10/2004" duration="5" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="66" type="2"/>
</task>
</task>
<task id="66" name="0.4.3" color="#8cb6ce" meeting="true" start="31/10/2004" duration="1" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="69" type="2"/>
</task>
<task id="68" name="UDP transport" color="#8cb6ce" meeting="false" start="31/10/2004" duration="19" complete="0" fixed-start="false" priority="1" expand="true">
<task id="69" name="Packet spec" color="#8cb6ce" meeting="false" start="31/10/2004" duration="3" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="70" type="2"/>
</task>
<task id="70" name="Implementation" color="#8cb6ce" meeting="false" start="2/11/2004" duration="10" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="71" type="2"/>
</task>
<task id="71" name="QA&#47;support" color="#8cb6ce" meeting="false" start="12/11/2004" duration="5" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="72" type="2"/>
<depend id="170" type="2"/>
</task>
</task>
<task id="170" name="0.4.4" color="#8cb6ce" meeting="true" start="17/11/2004" duration="1" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="122" type="2"/>
</task>
<task id="55" name="Cleanup" color="#8cb6ce" meeting="false" start="18/11/2004" duration="21" complete="0" fixed-start="false" priority="1" expand="true">
<task id="122" name="Code refactoring" color="#8cb6ce" meeting="false" start="18/11/2004" duration="7" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="56" type="2"/>
</task>
<task id="56" name="Site docs" color="#8cb6ce" meeting="false" start="25/11/2004" duration="7" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="57" type="2"/>
</task>
<task id="57" name="Code docs" color="#8cb6ce" meeting="false" start="2/12/2004" duration="7" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="54" type="2"/>
</task>
</task>
<task id="54" name="1.0" color="#8cb6ce" meeting="true" start="9/12/2004" duration="2" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="67" type="2"/>
</task>
<task id="67" name="Support" color="#8cb6ce" meeting="false" start="11/12/2004" duration="10" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="110" type="2"/>
</task>
<task id="109" name="Restricted routes" color="#8cb6ce" meeting="false" start="21/12/2004" duration="25" complete="0" fixed-start="false" priority="1" expand="true">
<task id="110" name="Basic restricted routes" color="#8cb6ce" meeting="false" start="21/12/2004" duration="10" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="111" type="2"/>
</task>
<task id="111" name="Trusted links" color="#8cb6ce" meeting="false" start="31/12/2004" duration="5" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="112" type="2"/>
</task>
<task id="112" name="QA&#47;support" color="#8cb6ce" meeting="false" start="5/1/2005" duration="10" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="113" type="2"/>
</task>
</task>
<task id="113" name="2.0" color="#8cb6ce" meeting="true" start="15/1/2005" duration="1" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="120" type="2"/>
</task>
<task id="120" name="Support" color="#8cb6ce" meeting="false" start="16/1/2005" duration="10" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="115" type="2"/>
</task>
<task id="114" name="Mixing" color="#8cb6ce" meeting="false" start="26/1/2005" duration="26" complete="0" fixed-start="false" priority="1" expand="true">
<task id="115" name="Tunnel throttles" color="#8cb6ce" meeting="false" start="26/1/2005" duration="3" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="116" type="2"/>
</task>
<task id="116" name="Tunnel pooling" color="#8cb6ce" meeting="false" start="29/1/2005" duration="5" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="117" type="2"/>
</task>
<task id="117" name="Tunnel chaffing" color="#8cb6ce" meeting="false" start="3/2/2005" duration="3" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="118" type="2"/>
</task>
<task id="118" name="Garlic delays" color="#8cb6ce" meeting="false" start="6/2/2005" duration="5" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="119" type="2"/>
</task>
<task id="119" name="QA&#47;support" color="#8cb6ce" meeting="false" start="11/2/2005" duration="10" complete="0" fixed-start="false" priority="1" expand="true">
<depend id="121" type="2"/>
</task>
</task>
<task id="121" name="3.0" color="#8cb6ce" meeting="true" start="21/2/2005" duration="1" complete="0" fixed-start="false" priority="1" expand="true"/>
</tasks>
<resources>
<resource id="0" name="jrandom" function="SoftwareDevelopment:2" contacts="" phone="" />
</resources>
<allocations>
</allocations>
<roles roleset-name="Default"/>
<roles roleset-name="SoftwareDevelopment"/>
</project>

View File

@ -43,9 +43,11 @@ to reach some of the following sites:</p>
<ul>
<li><a href="http://duck.i2p/">duck.i2p</a>: duck's eepsite, with links to other active sites</li>
<li><a href="http://ugha.i2p/">ugha.i2p</a>: ugha's eepsite, a wiki that anyone can edit, and lots of links</li>
<li><a href="http://forum.i2p/">forum.i2p</a>: an anonymous gateway to <a href="http://forum.i2p.net/">forum.i2p.net</a></li>
<li><a href="http://forum.i2p/">forum.i2p</a>: a secure and anonymous connection to <a href="http://forum.i2p.net/">forum.i2p.net</a></li>
<li><a href="http://www.i2p/">www.i2p</a>: a secure and anonymous connection to <a href="http://www.i2p.net/">www.i2p.net</a></li>
<li><a href="http://dev.i2p/">dev.i2p</a>: a secure and anonymous connection to <a href="http://dev.i2p.net/">dev.i2p.net</a></li>
<li><a href="http://fproxy.i2p/">fproxy.i2p</a> and <a href="http://fproxy2.i2p/">fproxy2.i2p</a>:
public gateways into two different freenet nodes</li>
a secure and anonymous connection to two different freenet nodes</li>
</ul>
<p>The IRC proxy is a gateway to duck's anonymously hosted IRC server
@ -60,4 +62,17 @@ the <a href="/i2ptunnel/" target="_blank">I2PTunnel configuration interface</a>
You can also go to that page if you want to add a new tunnel, such as if you want to run
your own eepsite.</p>
<h2>Want your own eepsite?</h2>
<p>In addition, we've configured some software to let you run your own eepsite - a
<a href="http://jetty.mortbay.org/">Jetty</a> instance listening on
<a href="http://localhost:7658/">http://localhost:7658/</a>. Simply place your files in
the <code>eepsite/docroot/</code> directory (or any standard JSP/Servlet <code>.war</code>
files under <code>eepsite/webapps</code>) and they'll show up. Your eepsite's
<i>destination</i> (which uniquely and securly identifies it) is shown on the I2PTunnel
<a href="/i2ptunnel/">configuration page</a> - if you want other people to see your eepsite,
you need to give them that really huge string. Just paste it into the
<a href="http://forum.i2p/viewforum.php?f=16">Eepsite announce</a> forum, add it to
ugha's <a href="http://ugha.i2p/I2pLinks">wiki</a>, or paste it in the #i2p or #i2p-chat channels on
IRC (be sure to split it into two lines, as its too long for one).</p>
<p><b>As a note, you can change this page by editing the file "docs/readme.html"</b></p>

View File

@ -39,30 +39,39 @@ public class DataMessage extends I2NPMessageImpl {
public int getSize() { return _data.length; }
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
try {
int size = (int)DataHelper.readLong(in, 4);
if ( (size <= 0) || (size > MAX_SIZE) )
throw new I2NPMessageException("wtf, size out of range? " + size);
_data = new byte[size];
int read = read(in, _data);
if (read != size)
throw new DataFormatException("Not enough bytes to read (read = " + read + ", expected = " + size + ")");
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Unable to load the message data", dfe);
}
int curIndex = offset;
long size = DataHelper.fromLong(data, curIndex, 4);
curIndex += 4;
if (size > 64*1024)
throw new I2NPMessageException("wtf, size=" + size);
_data = new byte[(int)size];
System.arraycopy(data, curIndex, _data, 0, (int)size);
}
protected byte[] writeMessage() throws I2NPMessageException, IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream((_data != null ? _data.length + 4 : 4));
try {
DataHelper.writeLong(os, 4, (_data != null ? _data.length : 0));
os.write(_data);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error writing out the message data", dfe);
/** calculate the message body's length (not including the header and footer */
protected int calculateWrittenLength() {
if (_data == null)
return 4;
else
return 4 + _data.length;
}
/** write the message body to the output array, starting at the given index */
protected int writeMessageBody(byte out[], int curIndex) {
if (_data == null) {
out[curIndex++] = 0x0;
out[curIndex++] = 0x0;
out[curIndex++] = 0x0;
out[curIndex++] = 0x0;
} else {
byte len[] = DataHelper.toLong(4, _data.length);
System.arraycopy(len, 0, out, curIndex, 4);
curIndex += 4;
System.arraycopy(_data, 0, out, curIndex, _data.length);
curIndex += _data.length;
}
return os.toByteArray();
return curIndex;
}
public int getType() { return MESSAGE_TYPE; }

View File

@ -124,63 +124,96 @@ public class DatabaseLookupMessage extends I2NPMessageImpl {
_dontIncludePeers = null;
}
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
try {
_key = new Hash();
_key.readBytes(in);
_fromHash = new Hash();
_fromHash.readBytes(in);
Boolean val = DataHelper.readBoolean(in);
if (val == null)
int curIndex = offset;
byte keyData[] = new byte[Hash.HASH_LENGTH];
System.arraycopy(data, curIndex, keyData, 0, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
_key = new Hash(keyData);
byte fromData[] = new byte[Hash.HASH_LENGTH];
System.arraycopy(data, curIndex, fromData, 0, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
_fromHash = new Hash(fromData);
boolean tunnelSpecified = false;
switch (data[curIndex]) {
case DataHelper.BOOLEAN_TRUE:
tunnelSpecified = true;
break;
case DataHelper.BOOLEAN_FALSE:
tunnelSpecified = false;
break;
default:
throw new I2NPMessageException("Tunnel must be explicitly specified (or not)");
boolean tunnelSpecified = val.booleanValue();
if (tunnelSpecified) {
_replyTunnel = new TunnelId();
_replyTunnel.readBytes(in);
}
int numPeers = (int)DataHelper.readLong(in, 2);
if ( (numPeers < 0) || (numPeers >= (1<<16) ) )
throw new DataFormatException("Invalid number of peers - " + numPeers);
Set peers = new HashSet(numPeers);
for (int i = 0; i < numPeers; i++) {
Hash peer = new Hash();
peer.readBytes(in);
peers.add(peer);
}
_dontIncludePeers = peers;
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Unable to load the message data", dfe);
}
curIndex++;
if (tunnelSpecified) {
_replyTunnel = new TunnelId(DataHelper.fromLong(data, curIndex, 4));
curIndex += 4;
}
int numPeers = (int)DataHelper.fromLong(data, curIndex, 2);
curIndex += 2;
if ( (numPeers < 0) || (numPeers >= (1<<16) ) )
throw new I2NPMessageException("Invalid number of peers - " + numPeers);
Set peers = new HashSet(numPeers);
for (int i = 0; i < numPeers; i++) {
byte peer[] = new byte[Hash.HASH_LENGTH];
System.arraycopy(data, curIndex, peer, 0, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
peers.add(new Hash(peer));
}
_dontIncludePeers = peers;
}
protected int calculateWrittenLength() {
int totalLength = 0;
totalLength += Hash.HASH_LENGTH*2; // key+fromHash
totalLength += 1; // hasTunnel?
if (_replyTunnel != null)
totalLength += 4;
totalLength += 2; // numPeers
if (_dontIncludePeers != null)
totalLength += Hash.HASH_LENGTH * _dontIncludePeers.size();
return totalLength;
}
protected byte[] writeMessage() throws I2NPMessageException, IOException {
protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException {
if (_key == null) throw new I2NPMessageException("Key being searched for not specified");
if (_fromHash == null) throw new I2NPMessageException("From address not specified");
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
try {
_key.writeBytes(os);
_fromHash.writeBytes(os);
if (_replyTunnel != null) {
DataHelper.writeBoolean(os, Boolean.TRUE);
_replyTunnel.writeBytes(os);
} else {
DataHelper.writeBoolean(os, Boolean.FALSE);
}
if ( (_dontIncludePeers == null) || (_dontIncludePeers.size() <= 0) ) {
DataHelper.writeLong(os, 2, 0);
} else {
DataHelper.writeLong(os, 2, _dontIncludePeers.size());
for (Iterator iter = _dontIncludePeers.iterator(); iter.hasNext(); ) {
Hash peer = (Hash)iter.next();
peer.writeBytes(os);
}
}
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error writing out the message data", dfe);
System.arraycopy(_key.getData(), 0, out, curIndex, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
System.arraycopy(_fromHash.getData(), 0, out, curIndex, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
if (_replyTunnel != null) {
out[curIndex++] = DataHelper.BOOLEAN_TRUE;
byte id[] = DataHelper.toLong(4, _replyTunnel.getTunnelId());
System.arraycopy(id, 0, out, curIndex, 4);
curIndex += 4;
} else {
out[curIndex++] = DataHelper.BOOLEAN_FALSE;
}
return os.toByteArray();
if ( (_dontIncludePeers == null) || (_dontIncludePeers.size() <= 0) ) {
out[curIndex++] = 0x0;
out[curIndex++] = 0x0;
} else {
byte len[] = DataHelper.toLong(2, _dontIncludePeers.size());
out[curIndex++] = len[0];
out[curIndex++] = len[1];
for (Iterator iter = _dontIncludePeers.iterator(); iter.hasNext(); ) {
Hash peer = (Hash)iter.next();
System.arraycopy(peer.getData(), 0, out, curIndex, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
}
}
return curIndex;
}
public int getType() { return MESSAGE_TYPE; }

View File

@ -57,56 +57,58 @@ public class DatabaseSearchReplyMessage extends I2NPMessageImpl {
public Hash getFromHash() { return _from; }
public void setFromHash(Hash from) { _from = from; }
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
try {
_key = new Hash();
_key.readBytes(in);
int num = (int)DataHelper.readLong(in, 1);
_peerHashes.clear();
for (int i = 0; i < num; i++) {
Hash peer = new Hash();
peer.readBytes(in);
addReply(peer);
}
_from = new Hash();
_from.readBytes(in);
_context.statManager().addRateData("netDb.searchReplyMessageReceive", num*32 + 64, 1);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Unable to load the message data", dfe);
int curIndex = offset;
byte keyData[] = new byte[Hash.HASH_LENGTH];
System.arraycopy(data, curIndex, keyData, 0, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
_key = new Hash(keyData);
int num = (int)DataHelper.fromLong(data, curIndex, 1);
curIndex++;
_peerHashes.clear();
for (int i = 0; i < num; i++) {
byte peer[] = new byte[Hash.HASH_LENGTH];
System.arraycopy(data, curIndex, peer, 0, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
addReply(new Hash(peer));
}
byte from[] = new byte[Hash.HASH_LENGTH];
System.arraycopy(data, curIndex, from, 0, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
_from = new Hash(from);
_context.statManager().addRateData("netDb.searchReplyMessageReceive", num*32 + 64, 1);
}
protected byte[] writeMessage() throws I2NPMessageException, IOException {
/** calculate the message body's length (not including the header and footer */
protected int calculateWrittenLength() {
return Hash.HASH_LENGTH + 1 + getNumReplies()*Hash.HASH_LENGTH + Hash.HASH_LENGTH;
}
/** write the message body to the output array, starting at the given index */
protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException {
if (_key == null)
throw new I2NPMessageException("Key in reply to not specified");
if (_peerHashes == null)
throw new I2NPMessageException("Peer replies are null");
if (_from == null)
throw new I2NPMessageException("No 'from' address specified!");
byte rv[] = null;
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
try {
_key.writeBytes(os);
DataHelper.writeLong(os, 1, _peerHashes.size());
for (int i = 0; i < getNumReplies(); i++) {
Hash peer = getReply(i);
peer.writeBytes(os);
}
_from.writeBytes(os);
rv = os.toByteArray();
_context.statManager().addRateData("netDb.searchReplyMessageSendSize", rv.length, 1);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error writing out the message data", dfe);
System.arraycopy(_key.getData(), 0, out, curIndex, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
byte len[] = DataHelper.toLong(1, _peerHashes.size());
out[curIndex++] = len[0];
for (int i = 0; i < getNumReplies(); i++) {
System.arraycopy(getReply(i).getData(), 0, out, curIndex, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
}
return rv;
System.arraycopy(_from.getData(), 0, out, curIndex, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
return curIndex;
}
public int getType() { return MESSAGE_TYPE; }

View File

@ -35,6 +35,8 @@ public class DatabaseStoreMessage extends I2NPMessageImpl {
private int _type;
private LeaseSet _leaseSet;
private RouterInfo _info;
private byte[] _leaseSetCache;
private byte[] _routerInfoCache;
private long _replyToken;
private TunnelId _replyTunnel;
private Hash _replyGateway;
@ -117,76 +119,109 @@ public class DatabaseStoreMessage extends I2NPMessageImpl {
public Hash getReplyGateway() { return _replyGateway; }
public void setReplyGateway(Hash peer) { _replyGateway = peer; }
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
try {
_key = new Hash();
_key.readBytes(in);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Hash read: " + _key.toBase64());
_type = (int)DataHelper.readLong(in, 1);
_replyToken = DataHelper.readLong(in, 4);
if (_replyToken > 0) {
_replyTunnel = new TunnelId();
_replyTunnel.readBytes(in);
_replyGateway = new Hash();
_replyGateway.readBytes(in);
} else {
_replyTunnel = null;
_replyGateway = null;
int curIndex = offset;
byte keyData[] = new byte[Hash.HASH_LENGTH];
System.arraycopy(data, curIndex, keyData, 0, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
_key = new Hash(keyData);
_type = (int)DataHelper.fromLong(data, curIndex, 1);
curIndex++;
_replyToken = DataHelper.fromLong(data, curIndex, 4);
curIndex += 4;
if (_replyToken > 0) {
_replyTunnel = new TunnelId(DataHelper.fromLong(data, curIndex, 4));
curIndex += 4;
byte gw[] = new byte[Hash.HASH_LENGTH];
System.arraycopy(data, curIndex, gw, 0, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
_replyGateway = new Hash(gw);
} else {
_replyTunnel = null;
_replyGateway = null;
}
if (_type == KEY_TYPE_LEASESET) {
_leaseSet = new LeaseSet();
try {
_leaseSet.readBytes(new ByteArrayInputStream(data, curIndex, data.length-curIndex));
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error reading the leaseSet", dfe);
}
if (_type == KEY_TYPE_LEASESET) {
_leaseSet = new LeaseSet();
_leaseSet.readBytes(in);
} else if (_type == KEY_TYPE_ROUTERINFO) {
_info = new RouterInfo();
int compressedSize = (int)DataHelper.readLong(in, 2);
byte compressed[] = new byte[compressedSize];
int read = DataHelper.read(in, compressed);
if (read != compressedSize)
throw new I2NPMessageException("Invalid compressed data size (expected "
+ compressedSize + " read " + read + ")");
ByteArrayInputStream bais = new ByteArrayInputStream(DataHelper.decompress(compressed));
_info.readBytes(bais);
} else {
throw new I2NPMessageException("Invalid type of key read from the structure - " + _type);
} else if (_type == KEY_TYPE_ROUTERINFO) {
_info = new RouterInfo();
int compressedSize = (int)DataHelper.fromLong(data, curIndex, 2);
curIndex += 2;
byte decompressed[] = DataHelper.decompress(data, curIndex, compressedSize);
try {
_info.readBytes(new ByteArrayInputStream(decompressed));
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error reading the routerInfo", dfe);
}
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Unable to load the message data", dfe);
} else {
throw new I2NPMessageException("Invalid type of key read from the structure - " + _type);
}
}
protected byte[] writeMessage() throws I2NPMessageException, IOException {
/** calculate the message body's length (not including the header and footer */
protected int calculateWrittenLength() {
int len = Hash.HASH_LENGTH + 1 + 4; // key+type+replyToken
if (_replyToken > 0)
len += 4 + Hash.HASH_LENGTH; // replyTunnel+replyGateway
if (_type == KEY_TYPE_LEASESET) {
_leaseSetCache = _leaseSet.toByteArray();
len += _leaseSetCache.length;
} else if (_type == KEY_TYPE_ROUTERINFO) {
byte uncompressed[] = _info.toByteArray();
byte compressed[] = DataHelper.compress(uncompressed);
_routerInfoCache = compressed;
len += compressed.length + 2;
}
return len;
}
/** write the message body to the output array, starting at the given index */
protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException {
if (_key == null) throw new I2NPMessageException("Invalid key");
if ( (_type != KEY_TYPE_LEASESET) && (_type != KEY_TYPE_ROUTERINFO) ) throw new I2NPMessageException("Invalid key type");
if ( (_type == KEY_TYPE_LEASESET) && (_leaseSet == null) ) throw new I2NPMessageException("Missing lease set");
if ( (_type == KEY_TYPE_ROUTERINFO) && (_info == null) ) throw new I2NPMessageException("Missing router info");
ByteArrayOutputStream os = new ByteArrayOutputStream(256);
try {
_key.writeBytes(os);
DataHelper.writeLong(os, 1, _type);
DataHelper.writeLong(os, 4, _replyToken);
if (_replyToken > 0) {
_replyTunnel.writeBytes(os);
_replyGateway.writeBytes(os);
} else {
// noop
}
if (_type == KEY_TYPE_LEASESET) {
_leaseSet.writeBytes(os);
} else if (_type == KEY_TYPE_ROUTERINFO) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024);
_info.writeBytes(baos);
byte uncompressed[] = baos.toByteArray();
byte compressed[] = DataHelper.compress(uncompressed);
DataHelper.writeLong(os, 2, compressed.length);
os.write(compressed);
}
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error writing out the message data", dfe);
System.arraycopy(_key.getData(), 0, out, curIndex, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
byte type[] = DataHelper.toLong(1, _type);
out[curIndex++] = type[0];
byte tok[] = DataHelper.toLong(4, _replyToken);
System.arraycopy(tok, 0, out, curIndex, 4);
curIndex += 4;
if (_replyToken > 0) {
byte id[] = DataHelper.toLong(4, _replyTunnel.getTunnelId());
System.arraycopy(id, 0, out, curIndex, 4);
curIndex += 4;
System.arraycopy(_replyGateway.getData(), 0, out, curIndex, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
}
return os.toByteArray();
if (_type == KEY_TYPE_LEASESET) {
// initialized in calculateWrittenLength
System.arraycopy(_leaseSetCache, 0, out, curIndex, _leaseSetCache.length);
curIndex += _leaseSetCache.length;
} else if (_type == KEY_TYPE_ROUTERINFO) {
byte len[] = DataHelper.toLong(2, _routerInfoCache.length);
out[curIndex++] = len[0];
out[curIndex++] = len[1];
System.arraycopy(_routerInfoCache, 0, out, curIndex, _routerInfoCache.length);
curIndex += _routerInfoCache.length;
}
return curIndex;
}
public int getType() { return MESSAGE_TYPE; }

View File

@ -42,27 +42,30 @@ public class DeliveryStatusMessage extends I2NPMessageImpl {
public Date getArrival() { return _arrival; }
public void setArrival(Date arrival) { _arrival = arrival; }
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
try {
_id = DataHelper.readLong(in, 4);
_arrival = DataHelper.readDate(in);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Unable to load the message data", dfe);
}
int curIndex = offset;
_id = DataHelper.fromLong(data, curIndex, 4);
curIndex += 4;
_arrival = DataHelper.fromDate(data, curIndex);
}
protected byte[] writeMessage() throws I2NPMessageException, IOException {
/** calculate the message body's length (not including the header and footer */
protected int calculateWrittenLength() {
return 4 + DataHelper.DATE_LENGTH; // id + arrival
}
/** write the message body to the output array, starting at the given index */
protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException {
if ( (_id < 0) || (_arrival == null) ) throw new I2NPMessageException("Not enough data to write out");
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
try {
DataHelper.writeLong(os, 4, _id);
DataHelper.writeDate(os, _arrival);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error writing out the message data", dfe);
}
return os.toByteArray();
byte id[] = DataHelper.toLong(4, _id);
System.arraycopy(id, 0, out, curIndex, 4);
curIndex += 4;
byte date[] = DataHelper.toDate(_arrival);
System.arraycopy(date, 0, out, curIndex, DataHelper.DATE_LENGTH);
curIndex += DataHelper.DATE_LENGTH;
return curIndex;
}
public int getType() { return MESSAGE_TYPE; }

View File

@ -98,7 +98,7 @@ public class GarlicClove extends DataStructureImpl {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Wrote instructions: " + _instructions);
_msg.writeBytes(out);
out.write(_msg.toByteArray());
DataHelper.writeLong(out, 4, _cloveId);
DataHelper.writeDate(out, _expiration);
if (_log.shouldLog(Log.DEBUG))

View File

@ -35,30 +35,29 @@ public class GarlicMessage extends I2NPMessageImpl {
public byte[] getData() { return _data; }
public void setData(byte[] data) { _data = data; }
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
try {
long len = DataHelper.readLong(in, 4);
_data = new byte[(int)len];
int read = read(in, _data);
if (read != len)
throw new I2NPMessageException("Incorrect size read [" + read + " read, expected " + len + "]");
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Unable to load the message data", dfe);
}
int curIndex = offset;
long len = DataHelper.fromLong(data, curIndex, 4);
curIndex += 4;
if ( (len <= 0) || (len > 64*1024) ) throw new I2NPMessageException("size="+len);
_data = new byte[(int)len];
System.arraycopy(data, curIndex, _data, 0, (int)len);
}
protected byte[] writeMessage() throws I2NPMessageException, IOException {
if ( (_data == null) || (_data.length <= 0) ) throw new I2NPMessageException("Not enough data to write out");
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
try {
DataHelper.writeLong(os, 4, _data.length);
os.write(_data);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error writing out the message data", dfe);
}
return os.toByteArray();
/** calculate the message body's length (not including the header and footer */
protected int calculateWrittenLength() {
return 4 + _data.length;
}
/** write the message body to the output array, starting at the given index */
protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException {
byte len[] = DataHelper.toLong(4, _data.length);
System.arraycopy(len, 0, out, curIndex, 4);
curIndex += 4;
System.arraycopy(_data, 0, out, curIndex, _data.length);
curIndex += _data.length;
return curIndex;
}
public int getType() { return MESSAGE_TYPE; }

View File

@ -28,11 +28,28 @@ public interface I2NPMessage extends DataStructure {
*
* @param in stream to read from
* @param type I2NP message type
* @param buffer scratch buffer to be used when reading and parsing
* @return size of the message read (including headers)
* @throws I2NPMessageException if the stream doesn't contain a valid message
* that this class can read.
* @throws IOException if there is a problem reading from the stream
*/
public void readBytes(InputStream in, int type) throws I2NPMessageException, IOException;
public int readBytes(InputStream in, int type, byte buffer[]) throws I2NPMessageException, IOException;
/**
* Read the body into the data structures, after the initial type byte and
* the uniqueId / expiration, using the current class's format as defined by
* the I2NP specification
*
* @param data data to read from
* @param offset where to start in the data array
* @param dataSize how long into the data to read
* @param type I2NP message type
* @throws I2NPMessageException if the stream doesn't contain a valid message
* that this class can read.
* @throws IOException if there is a problem reading from the stream
*/
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException;
/**
* Return the unique identifier for this type of I2NP message, as defined in
@ -50,4 +67,10 @@ public interface I2NPMessage extends DataStructure {
*
*/
public Date getMessageExpiration();
/** How large the message is, including any checksums */
public int getMessageSize();
/** write the message to the buffer, returning the number of bytes written */
public int toByteArray(byte buffer[]);
}

View File

@ -26,9 +26,13 @@ public class I2NPMessageHandler {
private I2PAppContext _context;
private long _lastReadBegin;
private long _lastReadEnd;
private int _lastSize;
private byte _messageBuffer[];
public I2NPMessageHandler(I2PAppContext context) {
_context = context;
_log = context.logManager().getLog(I2NPMessageHandler.class);
_messageBuffer = null;
_lastSize = -1;
}
/**
@ -39,25 +43,39 @@ public class I2NPMessageHandler {
* message - if it is an unknown type or has improper formatting, etc.
*/
public I2NPMessage readMessage(InputStream in) throws IOException, I2NPMessageException {
if (_messageBuffer == null) _messageBuffer = new byte[38*1024]; // more than necessary
try {
int type = (int)DataHelper.readLong(in, 1);
_lastReadBegin = System.currentTimeMillis();
I2NPMessage msg = createMessage(in, type);
msg.readBytes(in, type);
I2NPMessage msg = createMessage(type);
if (msg == null)
throw new I2NPMessageException("The type "+ type + " is an unknown I2NP message");
try {
_lastSize = msg.readBytes(in, type, _messageBuffer);
} catch (IOException ioe) {
throw ioe;
} catch (I2NPMessageException ime) {
throw ime;
} catch (Exception e) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error reading the stream", e);
throw new IOException("Unknown error reading the " + msg.getClass().getName()
+ ": " + e.getMessage());
}
_lastReadEnd = System.currentTimeMillis();
return msg;
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error reading the message", dfe);
}
}
public long getLastReadTime() { return _lastReadEnd - _lastReadBegin; }
public int getLastSize() { return _lastSize; }
/**
* Yes, this is fairly ugly, but its the only place it ever happens.
*
*/
private I2NPMessage createMessage(InputStream in, int type) throws IOException, I2NPMessageException {
private I2NPMessage createMessage(int type) throws I2NPMessageException {
switch (type) {
case DatabaseStoreMessage.MESSAGE_TYPE:
return new DatabaseStoreMessage(_context);
@ -78,7 +96,7 @@ public class I2NPMessageHandler {
case TunnelCreateStatusMessage.MESSAGE_TYPE:
return new TunnelCreateStatusMessage(_context);
default:
throw new I2NPMessageException("The type "+ type + " is an unknown I2NP message");
return null;
}
}

View File

@ -8,6 +8,7 @@ package net.i2p.data.i2np;
*
*/
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -17,6 +18,7 @@ import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.DataStructureImpl;
import net.i2p.data.Hash;
import net.i2p.util.Log;
/**
@ -29,6 +31,7 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
protected I2PAppContext _context;
private Date _expiration;
private long _uniqueId;
private byte _data[];
public final static long DEFAULT_EXPIRATION_MS = 1*60*1000; // 1 minute by default
@ -37,60 +40,63 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
_log = context.logManager().getLog(I2NPMessageImpl.class);
_expiration = new Date(_context.clock().now() + DEFAULT_EXPIRATION_MS);
_uniqueId = _context.random().nextLong(MAX_ID_VALUE);
_context.statManager().createRateStat("i2np.writeTime", "How long it takes to write an I2NP message", "I2NP", new long[] { 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("i2np.readTime", "How long it takes to read an I2NP message", "I2NP", new long[] { 10*60*1000, 60*60*1000 });
}
/**
* Write out the payload part of the message (not including the initial
* 1 byte type)
*
*/
protected abstract byte[] writeMessage() throws I2NPMessageException, IOException;
/**
* Read the body into the data structures, after the initial type byte and
* the uniqueId / expiration, using the current class's format as defined by
* the I2NP specification
*
* @param in stream to read from
* @param type I2NP message type
* @throws I2NPMessageException if the stream doesn't contain a valid message
* that this class can read.
* @throws IOException if there is a problem reading from the stream
*/
protected abstract void readMessage(InputStream in, int type) throws I2NPMessageException, IOException;
public void readBytes(InputStream in) throws DataFormatException, IOException {
try {
readBytes(in, -1);
readBytes(in, -1, new byte[1024]);
} catch (I2NPMessageException ime) {
throw new DataFormatException("Bad bytes", ime);
}
}
public void readBytes(InputStream in, int type) throws I2NPMessageException, IOException {
public int readBytes(InputStream in, int type, byte buffer[]) throws I2NPMessageException, IOException {
try {
if (type < 0)
type = (int)DataHelper.readLong(in, 1);
_uniqueId = DataHelper.readLong(in, 4);
_expiration = DataHelper.readDate(in);
int size = (int)DataHelper.readLong(in, 2);
Hash h = new Hash();
h.readBytes(in);
if (buffer.length < size) {
if (size > 64*1024) throw new I2NPMessageException("size=" + size);
buffer = new byte[size];
}
int cur = 0;
while (cur < size) {
int numRead = in.read(buffer, cur, size- cur);
if (numRead == -1) {
throw new I2NPMessageException("Payload is too short [" + numRead + ", wanted " + size + "]");
}
cur += numRead;
}
Hash calc = _context.sha().calculateHash(buffer, 0, size);
if (!calc.equals(h))
throw new I2NPMessageException("Hash does not match");
long start = _context.clock().now();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Reading bytes: type = " + type + " / uniqueId : " + _uniqueId + " / expiration : " + _expiration);
readMessage(buffer, 0, size, type);
long time = _context.clock().now() - start;
if (time > 50)
_context.statManager().addRateData("i2np.readTime", time, time);
return size + Hash.HASH_LENGTH + 1 + 4 + DataHelper.DATE_LENGTH;
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error reading the message header", dfe);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Reading bytes: type = " + type + " / uniqueId : " + _uniqueId + " / expiration : " + _expiration);
readMessage(in, type);
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
try {
DataHelper.writeLong(out, 1, getType());
DataHelper.writeLong(out, 4, _uniqueId);
DataHelper.writeDate(out, _expiration);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Writing bytes: type = " + getType() + " / uniqueId : " + _uniqueId + " / expiration : " + _expiration);
byte[] data = writeMessage();
out.write(data);
} catch (I2NPMessageException ime) {
throw new DataFormatException("Error writing out the I2NP message data", ime);
}
int size = getMessageSize();
if (size < 47) throw new DataFormatException("Unable to build the message");
byte buf[] = new byte[size];
int read = toByteArray(buf);
if (read < 0)
out.write(buf, 0, read);
}
/**
@ -105,4 +111,77 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
*/
public Date getMessageExpiration() { return _expiration; }
public void setMessageExpiration(Date exp) { _expiration = exp; }
public synchronized int getMessageSize() {
return calculateWrittenLength()+47; // 47 bytes in the header
}
public byte[] toByteArray() {
byte data[] = new byte[getMessageSize()];
int written = toByteArray(data);
if (written != data.length) {
_log.error("Error writing out " + data.length + " for " + getClass().getName());
return null;
}
return data;
}
public int toByteArray(byte buffer[]) {
long start = _context.clock().now();
byte prefix[][] = new byte[][] { DataHelper.toLong(1, getType()),
DataHelper.toLong(4, _uniqueId),
DataHelper.toDate(_expiration),
new byte[2],
new byte[Hash.HASH_LENGTH]};
byte suffix[][] = new byte[][] { };
try {
int writtenLen = toByteArray(buffer, prefix, suffix);
int prefixLen = 1+4+8+2+Hash.HASH_LENGTH;
int suffixLen = 0;
int payloadLen = writtenLen - prefixLen - suffixLen;
Hash h = _context.sha().calculateHash(buffer, prefixLen, payloadLen);
byte len[] = DataHelper.toLong(2, payloadLen);
buffer[1+4+8] = len[0];
buffer[1+4+8+1] = len[1];
for (int i = 0; i < Hash.HASH_LENGTH; i++)
System.arraycopy(h.getData(), 0, buffer, 1+4+8+2, Hash.HASH_LENGTH);
long time = _context.clock().now() - start;
if (time > 50)
_context.statManager().addRateData("i2np.writeTime", time, time);
return writtenLen;
} catch (I2NPMessageException ime) {
_context.logManager().getLog(getClass()).error("Error writing", ime);
throw new IllegalStateException("Unable to serialize the message: " + ime.getMessage());
}
}
/** calculate the message body's length (not including the header and footer */
protected abstract int calculateWrittenLength();
/**
* write the message body to the output array, starting at the given index.
* @return the index into the array after the last byte written
*/
protected abstract int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException;
protected int toByteArray(byte out[], byte[][] prefix, byte[][] suffix) throws I2NPMessageException {
int curIndex = 0;
for (int i = 0; i < prefix.length; i++) {
System.arraycopy(prefix[i], 0, out, curIndex, prefix[i].length);
curIndex += prefix[i].length;
}
curIndex = writeMessageBody(out, curIndex);
for (int i = 0; i < suffix.length; i++) {
System.arraycopy(suffix[i], 0, out, curIndex, suffix[i].length);
curIndex += suffix[i].length;
}
return curIndex;
}
}

View File

@ -81,7 +81,7 @@ public class I2NPMessageReader {
* reader
*
*/
public void messageReceived(I2NPMessageReader reader, I2NPMessage message, long msToRead);
public void messageReceived(I2NPMessageReader reader, I2NPMessage message, long msToRead, int bytesRead);
/**
* Notify the listener that an exception was thrown while reading from the given
* reader
@ -122,7 +122,8 @@ public class I2NPMessageReader {
I2NPMessage msg = _handler.readMessage(_stream);
if (msg != null) {
long msToRead = _handler.getLastReadTime();
_listener.messageReceived(I2NPMessageReader.this, msg, msToRead);
int bytesRead = _handler.getLastSize();
_listener.messageReceived(I2NPMessageReader.this, msg, msToRead, bytesRead);
}
} catch (I2NPMessageException ime) {
if (_log.shouldLog(Log.WARN))

View File

@ -28,7 +28,8 @@ public class TunnelConfigurationSessionKey extends DataStructureImpl {
private final static Log _log = new Log(TunnelConfigurationSessionKey.class);
private SessionKey _key;
public TunnelConfigurationSessionKey() { setKey(null); }
public TunnelConfigurationSessionKey() { this(null); }
public TunnelConfigurationSessionKey(SessionKey key) { setKey(key); }
public SessionKey getKey() { return _key; }
public void setKey(SessionKey key) { _key= key; }

View File

@ -19,6 +19,8 @@ import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SigningPublicKey;
import net.i2p.data.TunnelId;
import net.i2p.util.Log;
@ -52,6 +54,8 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
private TunnelId _replyTunnel;
private Hash _replyPeer;
private byte[] _certificateCache;
public static final int PARTICIPANT_TYPE_GATEWAY = 1;
public static final int PARTICIPANT_TYPE_ENDPOINT = 2;
public static final int PARTICIPANT_TYPE_OTHER = 3;
@ -124,91 +128,190 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
public void setReplyPeer(Hash peer) { _replyPeer = peer; }
public Hash getReplyPeer() { return _replyPeer; }
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
try {
_participantType = (int)DataHelper.readLong(in, 1);
if (_participantType != PARTICIPANT_TYPE_ENDPOINT) {
_nextRouter = new Hash();
_nextRouter.readBytes(in);
_nextTunnelId = new TunnelId();
_nextTunnelId.readBytes(in);
}
_tunnelId = new TunnelId();
_tunnelId.readBytes(in);
_tunnelDuration = DataHelper.readLong(in, 4);
_configKey = new TunnelConfigurationSessionKey();
_configKey.readBytes(in);
_maxPeakMessagesPerMin = DataHelper.readLong(in, 4);
_maxAvgMessagesPerMin = DataHelper.readLong(in, 4);
_maxPeakBytesPerMin = DataHelper.readLong(in, 4);
_maxAvgBytesPerMin = DataHelper.readLong(in, 4);
int curIndex = offset;
_participantType = (int)DataHelper.fromLong(data, curIndex, 1);
curIndex++;
if (_participantType != PARTICIPANT_TYPE_ENDPOINT) {
byte peer[] = new byte[Hash.HASH_LENGTH];
System.arraycopy(data, curIndex, peer, 0, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
_nextRouter = new Hash(peer);
int flags = (int)DataHelper.readLong(in, 1);
_includeDummyTraffic = flagsIncludeDummy(flags);
_reorderMessages = flagsReorder(flags);
_verificationPubKey = new TunnelSigningPublicKey();
_verificationPubKey.readBytes(in);
if (_participantType == PARTICIPANT_TYPE_GATEWAY) {
_verificationPrivKey = new TunnelSigningPrivateKey();
_verificationPrivKey.readBytes(in);
}
if ( (_participantType == PARTICIPANT_TYPE_ENDPOINT) || (_participantType == PARTICIPANT_TYPE_GATEWAY) ) {
_tunnelKey = new TunnelSessionKey();
_tunnelKey.readBytes(in);
}
_certificate = new Certificate();
_certificate.readBytes(in);
_replyTag = new SessionTag();
_replyTag.readBytes(in);
_replyKey = new SessionKey();
_replyKey.readBytes(in);
_replyTunnel = new TunnelId();
_replyTunnel.readBytes(in);
_replyPeer = new Hash();
_replyPeer.readBytes(in);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Unable to load the message data", dfe);
_nextTunnelId = new TunnelId(DataHelper.fromLong(data, curIndex, 4));
curIndex += 4;
}
_tunnelId = new TunnelId(DataHelper.fromLong(data, curIndex, 4));
curIndex += 4;
if (_tunnelId.getTunnelId() <= 0)
throw new I2NPMessageException("wtf, tunnelId == " + _tunnelId);
_tunnelDuration = DataHelper.fromLong(data, curIndex, 4);
curIndex += 4;
byte key[] = new byte[SessionKey.KEYSIZE_BYTES];
System.arraycopy(data, curIndex, key, 0, SessionKey.KEYSIZE_BYTES);
curIndex += SessionKey.KEYSIZE_BYTES;
_configKey = new TunnelConfigurationSessionKey(new SessionKey(key));
_maxPeakMessagesPerMin = DataHelper.fromLong(data, curIndex, 4);
curIndex += 4;
_maxAvgMessagesPerMin = DataHelper.fromLong(data, curIndex, 4);
curIndex += 4;
_maxPeakBytesPerMin = DataHelper.fromLong(data, curIndex, 4);
curIndex += 4;
_maxAvgBytesPerMin = DataHelper.fromLong(data, curIndex, 4);
curIndex += 4;
int flags = (int)DataHelper.fromLong(data, curIndex, 1);
curIndex++;
_includeDummyTraffic = flagsIncludeDummy(flags);
_reorderMessages = flagsReorder(flags);
key = new byte[SigningPublicKey.KEYSIZE_BYTES];
System.arraycopy(data, curIndex, key, 0, SigningPublicKey.KEYSIZE_BYTES);
curIndex += SigningPublicKey.KEYSIZE_BYTES;
_verificationPubKey = new TunnelSigningPublicKey(new SigningPublicKey(key));
if (_participantType == PARTICIPANT_TYPE_GATEWAY) {
key = new byte[SigningPrivateKey.KEYSIZE_BYTES];
System.arraycopy(data, curIndex, key, 0, SigningPrivateKey.KEYSIZE_BYTES);
curIndex += SigningPrivateKey.KEYSIZE_BYTES;
_verificationPrivKey = new TunnelSigningPrivateKey(new SigningPrivateKey(key));
}
if ( (_participantType == PARTICIPANT_TYPE_ENDPOINT) || (_participantType == PARTICIPANT_TYPE_GATEWAY) ) {
key = new byte[SessionKey.KEYSIZE_BYTES];
System.arraycopy(data, curIndex, key, 0, SessionKey.KEYSIZE_BYTES);
curIndex += SessionKey.KEYSIZE_BYTES;
_tunnelKey = new TunnelSessionKey(new SessionKey(key));
}
int certType = (int) DataHelper.fromLong(data, curIndex, 1);
curIndex++;
int certLength = (int) DataHelper.fromLong(data, curIndex, 2);
curIndex += 2;
if (certLength <= 0) {
_certificate = new Certificate(certType, null);
} else {
if (certLength > 16*1024) throw new I2NPMessageException("cert size " + certLength);
byte certPayload[] = new byte[certLength];
System.arraycopy(data, curIndex, certPayload, 0, certLength);
curIndex += certLength;
_certificate = new Certificate(certType, certPayload);
}
byte tag[] = new byte[SessionTag.BYTE_LENGTH];
System.arraycopy(data, curIndex, tag, 0, SessionTag.BYTE_LENGTH);
curIndex += SessionTag.BYTE_LENGTH;
_replyTag = new SessionTag(tag);
key = new byte[SessionKey.KEYSIZE_BYTES];
System.arraycopy(data, curIndex, key, 0, SessionKey.KEYSIZE_BYTES);
curIndex += SessionKey.KEYSIZE_BYTES;
_replyKey = new SessionKey(key);
_replyTunnel = new TunnelId(DataHelper.fromLong(data, curIndex, 4));
curIndex += 4;
byte peer[] = new byte[Hash.HASH_LENGTH];
System.arraycopy(data, curIndex, peer, 0, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
_replyPeer = new Hash(peer);
}
protected byte[] writeMessage() throws I2NPMessageException, IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
try {
DataHelper.writeLong(os, 1, _participantType);
if (_participantType != PARTICIPANT_TYPE_ENDPOINT) {
_nextRouter.writeBytes(os);
_nextTunnelId.writeBytes(os);
}
_tunnelId.writeBytes(os);
DataHelper.writeLong(os, 4, _tunnelDuration);
_configKey.writeBytes(os);
DataHelper.writeLong(os, 4, _maxPeakMessagesPerMin);
DataHelper.writeLong(os, 4, _maxAvgMessagesPerMin);
DataHelper.writeLong(os, 4, _maxPeakBytesPerMin);
DataHelper.writeLong(os, 4, _maxAvgBytesPerMin);
long flags = getFlags();
DataHelper.writeLong(os, 1, flags);
_verificationPubKey.writeBytes(os);
if (_participantType == PARTICIPANT_TYPE_GATEWAY) {
_verificationPrivKey.writeBytes(os);
}
if ( (_participantType == PARTICIPANT_TYPE_ENDPOINT) || (_participantType == PARTICIPANT_TYPE_GATEWAY) ) {
_tunnelKey.writeBytes(os);
}
_certificate.writeBytes(os);
_replyTag.writeBytes(os);
_replyKey.writeBytes(os);
_replyTunnel.writeBytes(os);
_replyPeer.writeBytes(os);
} catch (Throwable t) {
throw new I2NPMessageException("Error writing out the message data", t);
/** calculate the message body's length (not including the header and footer */
protected int calculateWrittenLength() {
int length = 0;
length += 1; // participantType
if (_participantType != PARTICIPANT_TYPE_ENDPOINT) {
length += Hash.HASH_LENGTH;
length += 4; // nextTunnelId
}
return os.toByteArray();
length += 4; // tunnelId
length += 4; // duration;
length += SessionKey.KEYSIZE_BYTES;
length += 4*4; // max limits
length += 1; // flags
length += SigningPublicKey.KEYSIZE_BYTES;
if (_participantType == PARTICIPANT_TYPE_GATEWAY)
length += SigningPrivateKey.KEYSIZE_BYTES;
if ( (_participantType == PARTICIPANT_TYPE_ENDPOINT)
|| (_participantType == PARTICIPANT_TYPE_GATEWAY) )
length += SessionKey.KEYSIZE_BYTES;
_certificateCache = _certificate.toByteArray();
length += _certificateCache.length;
length += SessionTag.BYTE_LENGTH;
length += SessionKey.KEYSIZE_BYTES;
length += 4; // replyTunnel
length += Hash.HASH_LENGTH; // replyPeer
return length;
}
/** write the message body to the output array, starting at the given index */
protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException {
byte type[] = DataHelper.toLong(1, _participantType);
out[curIndex++] = type[0];
if (_participantType != PARTICIPANT_TYPE_ENDPOINT) {
System.arraycopy(_nextRouter.getData(), 0, out, curIndex, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
byte id[] = DataHelper.toLong(4, _nextTunnelId.getTunnelId());
System.arraycopy(id, 0, out, curIndex, 4);
curIndex += 4;
}
byte id[] = DataHelper.toLong(4, _tunnelId.getTunnelId());
System.arraycopy(id, 0, out, curIndex, 4);
curIndex += 4;
byte duration[] = DataHelper.toLong(4, _tunnelDuration);
System.arraycopy(duration, 0, out, curIndex, 4);
curIndex += 4;
System.arraycopy(_configKey.getKey().getData(), 0, out, curIndex, SessionKey.KEYSIZE_BYTES);
curIndex += SessionKey.KEYSIZE_BYTES;
byte val[] = DataHelper.toLong(4, _maxPeakMessagesPerMin);
System.arraycopy(val, 0, out, curIndex, 4);
curIndex += 4;
val = DataHelper.toLong(4, _maxAvgMessagesPerMin);
System.arraycopy(val, 0, out, curIndex, 4);
curIndex += 4;
val = DataHelper.toLong(4, _maxPeakBytesPerMin);
System.arraycopy(val, 0, out, curIndex, 4);
curIndex += 4;
val = DataHelper.toLong(4, _maxAvgBytesPerMin);
System.arraycopy(val, 0, out, curIndex, 4);
curIndex += 4;
long flags = getFlags();
byte flag[] = DataHelper.toLong(1, flags);
out[curIndex++] = flag[0];
System.arraycopy(_verificationPubKey.getKey().getData(), 0, out, curIndex, SigningPublicKey.KEYSIZE_BYTES);
curIndex += SigningPublicKey.KEYSIZE_BYTES;
if (_participantType == PARTICIPANT_TYPE_GATEWAY) {
System.arraycopy(_verificationPrivKey.getKey().getData(), 0, out, curIndex, SigningPrivateKey.KEYSIZE_BYTES);
curIndex += SigningPrivateKey.KEYSIZE_BYTES;
}
if ( (_participantType == PARTICIPANT_TYPE_ENDPOINT) || (_participantType == PARTICIPANT_TYPE_GATEWAY) ) {
System.arraycopy(_tunnelKey.getKey().getData(), 0, out, curIndex, SessionKey.KEYSIZE_BYTES);
curIndex += SessionKey.KEYSIZE_BYTES;
}
System.arraycopy(_certificateCache, 0, out, curIndex, _certificateCache.length);
curIndex += _certificateCache.length;
System.arraycopy(_replyTag.getData(), 0, out, curIndex, SessionTag.BYTE_LENGTH);
curIndex += SessionTag.BYTE_LENGTH;
System.arraycopy(_replyKey.getData(), 0, out, curIndex, SessionKey.KEYSIZE_BYTES);
curIndex += SessionKey.KEYSIZE_BYTES;
id = DataHelper.toLong(4, _replyTunnel.getTunnelId());
System.arraycopy(id, 0, out, curIndex, 4);
curIndex += 4;
System.arraycopy(_replyPeer.getData(), 0, out, curIndex, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
return curIndex;
}
private boolean flagsIncludeDummy(long flags) {
@ -304,4 +407,5 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
buf.append("]");
return buf.toString();
}
}

View File

@ -46,7 +46,11 @@ public class TunnelCreateStatusMessage extends I2NPMessageImpl {
}
public TunnelId getTunnelId() { return _tunnelId; }
public void setTunnelId(TunnelId id) { _tunnelId = id; }
public void setTunnelId(TunnelId id) {
_tunnelId = id;
if ( (id != null) && (id.getTunnelId() <= 0) )
throw new IllegalArgumentException("wtf, tunnelId " + id);
}
public int getStatus() { return _status; }
public void setStatus(int status) { _status = status; }
@ -57,31 +61,42 @@ public class TunnelCreateStatusMessage extends I2NPMessageImpl {
public Hash getFromHash() { return _from; }
public void setFromHash(Hash from) { _from = from; }
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
try {
_tunnelId = new TunnelId();
_tunnelId.readBytes(in);
_status = (int)DataHelper.readLong(in, 1);
_from = new Hash();
_from.readBytes(in);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Unable to load the message data", dfe);
}
int curIndex = offset;
_tunnelId = new TunnelId(DataHelper.fromLong(data, curIndex, 4));
curIndex += 4;
if (_tunnelId.getTunnelId() <= 0)
throw new I2NPMessageException("wtf, negative tunnelId? " + _tunnelId);
_status = (int)DataHelper.fromLong(data, curIndex, 1);
curIndex++;
byte peer[] = new byte[Hash.HASH_LENGTH];
System.arraycopy(data, curIndex, peer, 0, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
_from = new Hash(peer);
}
protected byte[] writeMessage() throws I2NPMessageException, IOException {
if ( (_tunnelId == null) || (_from == null) ) throw new I2NPMessageException("Not enough data to write out");
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
try {
_tunnelId.writeBytes(os);
DataHelper.writeLong(os, 1, (_status < 0 ? 255 : _status));
_from.writeBytes(os);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error writing out the message data", dfe);
}
return os.toByteArray();
/** calculate the message body's length (not including the header and footer */
protected int calculateWrittenLength() {
return 4 + 1 + Hash.HASH_LENGTH; // id + status + from
}
/** write the message body to the output array, starting at the given index */
protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException {
if ( (_tunnelId == null) || (_from == null) ) throw new I2NPMessageException("Not enough data to write out");
if (_tunnelId.getTunnelId() < 0) throw new I2NPMessageException("Negative tunnelId!? " + _tunnelId);
byte id[] = DataHelper.toLong(4, _tunnelId.getTunnelId());
System.arraycopy(id, 0, out, curIndex, 4);
curIndex += 4;
byte status[] = DataHelper.toLong(1, _status);
out[curIndex++] = status[0];
System.arraycopy(_from.getData(), 0, out, curIndex, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
return curIndex;
}
public int getType() { return MESSAGE_TYPE; }

View File

@ -15,6 +15,8 @@ import java.io.InputStream;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.Signature;
import net.i2p.data.TunnelId;
import net.i2p.util.Log;
@ -44,10 +46,16 @@ public class TunnelMessage extends I2NPMessageImpl {
}
public TunnelId getTunnelId() { return _tunnelId; }
public void setTunnelId(TunnelId id) { _tunnelId = id; }
public void setTunnelId(TunnelId id) {
_tunnelId = id;
}
public byte[] getData() { return _data; }
public void setData(byte data[]) { _data = data; }
public void setData(byte data[]) {
_data = data;
if ( (data != null) && (_data.length <= 0) )
throw new IllegalArgumentException("Empty tunnel payload?");
}
public TunnelVerificationStructure getVerificationStructure() { return _verification; }
public void setVerificationStructure(TunnelVerificationStructure verification) { _verification = verification; }
@ -55,71 +63,93 @@ public class TunnelMessage extends I2NPMessageImpl {
public byte[] getEncryptedDeliveryInstructions() { return _encryptedInstructions; }
public void setEncryptedDeliveryInstructions(byte instructions[]) { _encryptedInstructions = instructions; }
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
public void readMessage(byte data[], int offset, int dataSize, int type) throws I2NPMessageException, IOException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
try {
_tunnelId = new TunnelId();
_tunnelId.readBytes(in);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read tunnel message for tunnel " + _tunnelId);
_size = DataHelper.readLong(in, 4);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read tunnel message size: " + _size);
if (_size < 0) throw new I2NPMessageException("Invalid size in the structure: " + _size);
_data = new byte[(int)_size];
int read = read(in, _data);
if (read != _size)
throw new I2NPMessageException("Incorrect number of bytes read (" + read + ", expected " + _size);
int includeVerification = (int)DataHelper.readLong(in, 1);
if (includeVerification == FLAG_INCLUDESTRUCTURE) {
_verification = new TunnelVerificationStructure();
_verification.readBytes(in);
int len = (int)DataHelper.readLong(in, 2);
_encryptedInstructions = new byte[len];
read = read(in, _encryptedInstructions);
if (read != len)
throw new I2NPMessageException("Incorrect number of bytes read for instructions (" + read + ", expected " + len + ")");
}
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Unable to load the message data", dfe);
int curIndex = offset;
_tunnelId = new TunnelId(DataHelper.fromLong(data, curIndex, 4));
curIndex += 4;
if (_tunnelId.getTunnelId() <= 0)
throw new I2NPMessageException("Invalid tunnel Id " + _tunnelId);
_size = DataHelper.fromLong(data, curIndex, 4);
curIndex += 4;
if (_size < 0) throw new I2NPMessageException("Invalid size in the structure: " + _size);
if (_size > 64*1024) throw new I2NPMessageException("Invalid size in the structure: " + _size);
_data = new byte[(int)_size];
System.arraycopy(data, curIndex, _data, 0, (int)_size);
curIndex += _size;
int includeVerification = (int)DataHelper.fromLong(data, curIndex, 1);
curIndex++;
if (includeVerification == FLAG_INCLUDESTRUCTURE) {
byte vHash[] = new byte[Hash.HASH_LENGTH];
System.arraycopy(data, curIndex, vHash, 0, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
byte vSig[] = new byte[Signature.SIGNATURE_BYTES];
System.arraycopy(data, curIndex, vSig, 0, Signature.SIGNATURE_BYTES);
curIndex += Signature.SIGNATURE_BYTES;
_verification = new TunnelVerificationStructure(new Hash(vHash), new Signature(vSig));
int len = (int)DataHelper.fromLong(data, curIndex, 2);
curIndex += 2;
if ( (len <= 0) || (len > 4*1024) ) throw new I2NPMessageException("wtf, size of instructions: " + len);
_encryptedInstructions = new byte[len];
System.arraycopy(data, curIndex, _encryptedInstructions, 0, len);
curIndex += len;
}
}
protected byte[] writeMessage() throws I2NPMessageException, IOException {
if ( (_tunnelId == null) || (_data == null) || (_data.length <= 0) )
throw new I2NPMessageException("Not enough data to write out");
ByteArrayOutputStream os = new ByteArrayOutputStream(4096);
try {
_tunnelId.writeBytes(os);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Writing tunnel message for tunnel " + _tunnelId);
DataHelper.writeLong(os, 4, _data.length);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Writing tunnel message length: " + _data.length);
os.write(_data);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Writing tunnel message data");
if ( (_verification == null) || (_encryptedInstructions == null) ) {
DataHelper.writeLong(os, 1, FLAG_DONT_INCLUDESTRUCTURE);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Writing DontIncludeStructure flag");
} else {
DataHelper.writeLong(os, 1, FLAG_INCLUDESTRUCTURE);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Writing IncludeStructure flag, then the verification structure, then the " +
"E(instr).length [" + _encryptedInstructions.length + "], then the E(instr)");
_verification.writeBytes(os);
DataHelper.writeLong(os, 2, _encryptedInstructions.length);
os.write(_encryptedInstructions);
}
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error writing out the message data", dfe);
/** calculate the message body's length (not including the header and footer */
protected int calculateWrittenLength() {
int length = 0;
length += 4; // tunnelId
length += 4; // data length
length += _data.length;
if ( (_verification == null) || (_encryptedInstructions == null) ) {
length += 1; // include verification?
} else {
length += 1; // include verification?
length += Hash.HASH_LENGTH + Signature.SIGNATURE_BYTES;
length += 2; // instructions length
length += _encryptedInstructions.length;
}
byte rv[] = os.toByteArray();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Overall data being written: " + rv.length);
return rv;
return length;
}
/** write the message body to the output array, starting at the given index */
protected int writeMessageBody(byte out[], int curIndex) throws I2NPMessageException {
if ( (_tunnelId == null) || (_data == null) )
throw new I2NPMessageException("Not enough data to write out (id=" + _tunnelId + " data=" + _data + ")");
if (_data.length <= 0)
throw new I2NPMessageException("Not enough data to write out (data.length=" + _data.length + ")");
byte id[] = DataHelper.toLong(4, _tunnelId.getTunnelId());
System.arraycopy(id, 0, out, curIndex, 4);
curIndex += 4;
byte len[] = DataHelper.toLong(4, _data.length);
System.arraycopy(len, 0, out, curIndex, 4);
curIndex += 4;
System.arraycopy(_data, 0, out, curIndex, _data.length);
curIndex += _data.length;
if ( (_verification == null) || (_encryptedInstructions == null) ) {
byte flag[] = DataHelper.toLong(1, FLAG_DONT_INCLUDESTRUCTURE);
out[curIndex++] = flag[0];
} else {
byte flag[] = DataHelper.toLong(1, FLAG_INCLUDESTRUCTURE);
out[curIndex++] = flag[0];
System.arraycopy(_verification.getMessageHash().getData(), 0, out, curIndex, Hash.HASH_LENGTH);
curIndex += Hash.HASH_LENGTH;
System.arraycopy(_verification.getAuthorizationSignature().getData(), 0, out, curIndex, Signature.SIGNATURE_BYTES);
curIndex += Signature.SIGNATURE_BYTES;
len = DataHelper.toLong(2, _encryptedInstructions.length);
System.arraycopy(len, 0, out, curIndex, 2);
curIndex += 2;
System.arraycopy(_encryptedInstructions, 0, out, curIndex, _encryptedInstructions.length);
curIndex += _encryptedInstructions.length;
}
return curIndex;
}
public int getType() { return MESSAGE_TYPE; }

View File

@ -28,7 +28,8 @@ public class TunnelSessionKey extends DataStructureImpl {
private final static Log _log = new Log(TunnelSessionKey.class);
private SessionKey _key;
public TunnelSessionKey() { setKey(null); }
public TunnelSessionKey() { this(null); }
public TunnelSessionKey(SessionKey key) { setKey(key); }
public SessionKey getKey() { return _key; }
public void setKey(SessionKey key) { _key= key; }

View File

@ -29,7 +29,8 @@ public class TunnelSigningPrivateKey extends DataStructureImpl {
private final static Log _log = new Log(EndPointPrivateKey.class);
private SigningPrivateKey _key;
public TunnelSigningPrivateKey() { setKey(null); }
public TunnelSigningPrivateKey() { this(null); }
public TunnelSigningPrivateKey(SigningPrivateKey key) { setKey(key); }
public SigningPrivateKey getKey() { return _key; }
public void setKey(SigningPrivateKey key) { _key= key; }

View File

@ -28,7 +28,8 @@ public class TunnelSigningPublicKey extends DataStructureImpl {
private final static Log _log = new Log(TunnelSigningPublicKey.class);
private SigningPublicKey _key;
public TunnelSigningPublicKey() { setKey(null); }
public TunnelSigningPublicKey() { this(null); }
public TunnelSigningPublicKey(SigningPublicKey key) { setKey(key); }
public SigningPublicKey getKey() { return _key; }
public void setKey(SigningPublicKey key) { _key= key; }

View File

@ -29,9 +29,10 @@ public class TunnelVerificationStructure extends DataStructureImpl {
private Hash _msgHash;
private Signature _authSignature;
public TunnelVerificationStructure() {
setMessageHash(null);
setAuthorizationSignature(null);
public TunnelVerificationStructure() { this(null, null); }
public TunnelVerificationStructure(Hash messageHash, Signature authSig) {
setMessageHash(messageHash);
setAuthorizationSignature(authSig);
}
public Hash getMessageHash() { return _msgHash; }

View File

@ -9,7 +9,7 @@ package net.i2p.router;
*/
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
@ -65,12 +65,14 @@ public abstract class ClientManagerFacade implements Service {
public abstract void messageReceived(ClientMessage msg);
public boolean verifyClientLiveliness() { return true; }
/**
* Return the client's current config, or null if not connected
*
*/
public abstract SessionConfig getClientSessionConfig(Destination dest);
public void renderStatusHTML(OutputStream out) throws IOException { }
public void renderStatusHTML(Writer out) throws IOException { }
}
class DummyClientManagerFacade extends ClientManagerFacade {

Some files were not shown because too many files have changed in this diff Show More