Compare commits

..

44 Commits

Author SHA1 Message Date
jrandom
e614a2f726 * 2005-03-24 0.5.0.4 released 2005-03-24 07:29:27 +00:00
jrandom
32be7f1fd8 grr 2005-03-24 04:58:28 +00:00
jrandom
66e1d95a2a *cough* oops 2005-03-24 04:49:15 +00:00
jrandom
ff03be217e 2005-03-23 jrandom
* Added more intelligent version checking in news.xml, in case we have a
      version newer than the one specified.
2005-03-24 03:18:15 +00:00
jrandom
a52f8b89dc 2005-03-23 jrandom
* Added support for Transfer-Encoding: chunked to the EepGet, so that the
      cvsweb.cgi doesn't puke on us.
2005-03-24 02:38:10 +00:00
connelly
21c7c043b3 Fixed Bugzilla Bug #99 2005-03-24 01:54:23 +00:00
connelly
45e6608ad3 Added 'Unit test passed' log message and made test check that Bug #99 is fixed. 2005-03-24 01:50:19 +00:00
connelly
28978e3680 Fixed Bug #99: Data pending to be sent is still sent even if STREAM CLOSE is issued. 2005-03-24 01:49:00 +00:00
jrandom
904f755c8c 2005-03-23 jrandom
* Implemented the news fetch / update policy code, as configurated on
      /configupdate.jsp.  Defaults are to grab the news every 24h (or if it
      doesn't exist yet, on startup).  No action is taken however, though if
      the news.xml specifies that a new release is available, an option to
      update will be shown on the router console.
    * New initialNews.xml delivered with new installs, and moved news.xml out
      of the i2pwww module and into the i2p module so that we can bundle it
      within each update.
2005-03-24 01:19:52 +00:00
jrandom
a2c309ddd3 2005-03-23 jrandom
* New /configupdate.jsp page for controlling the update / notification
      process, as well as various minor related updates.  Note that not all
      options are exposed yet, and the update detection code isn't in place
      in this commit - it currently says there is always an update available.
    * New EepGet component for reliable downloading, with a CLI exposed in
      java -cp lib/i2p.jar net.i2p.util.EepGet url
    * Added a default signing key to the TrustedUpdate component to be used
      for verifying updates.  This signing key can be authenticated via
      gpg --verify i2p/core/java/src/net/i2p/crypto/TrustedUpdate.java
    * New public domain SHA1 implementation for the DSA code so that we can
      handle signing streams of arbitrary size without excess memory usage
      (thanks P.Verdy!)
    * Added some helpers to the TrustedUpdate to work off streams and to offer
      a minimal CLI:
          TrustedUpdate keygen pubKeyFile privKeyFile
          TrustedUpdate sign origFile signedFile privKeyFile
          TrustedUpdate verify signedFile
2005-03-23 21:13:03 +00:00
aum
677eeac8f7 changed existing 'decodeToString' to public 2005-03-23 06:30:31 +00:00
aum
b232cc0f24 D'oh, .decodeToString was already there, eliminated my vers 2005-03-23 06:26:23 +00:00
aum
18bbae1d1e changed 'String decode(String raw)' to 'String decodeToString(String raw)'
to eliminate name clash.
2005-03-23 06:24:25 +00:00
aum
08ee62b52c Added convenience methods:
- String encode(String raw)
 - String decode(String raw)
2005-03-23 06:21:16 +00:00
smeghead
5b83aed719 * Added basic trusted update creation/verification 2005-03-22 17:08:01 +00:00
jrandom
b5875ca07b 2005-03-21 jrandom
* Fixed the tunnel fragmentation handler to deal with multiple fragments
      in a single message properly (rather than release the buffer into the
      cache after processing the first one) (duh!)
    * Added the batching preprocessor which will bundle together multiple
      small messages inside a single tunnel message by delaying their delivery
      up to .5s, or whenever the pending data will fill a full message,
      whichever comes first.  This is disabled at the moment, since without the
      above bugfix widely deployed, lots and lots of messages would fail.
    * Within each tunnel pool, stick with a randomly selected peer for up to
      .5s before randomizing and selecting again, instead of randomizing the
      pool each time a tunnel is needed.
2005-03-22 02:00:10 +00:00
jrandom
3f9bf28382 2005-03-21 jrandom
* Fixed the tunnel fragmentation handler to deal with multiple fragments
      in a single message properly (rather than release the buffer into the
      cache after processing the first one) (duh!)
    * Added the batching preprocessor which will bundle together multiple
      small messages inside a single tunnel message by delaying their delivery
      up to .5s, or whenever the pending data will fill a full message,
      whichever comes first.  This is disabled at the moment, since without the
      above bugfix widely deployed, lots and lots of messages would fail.
    * Within each tunnel pool, stick with a randomly selected peer for up to
      .5s before randomizing and selecting again, instead of randomizing the
      pool each time a tunnel is needed.
2005-03-22 01:38:21 +00:00
jrandom
a2bd71c75b * 2005-03-18 0.5.0.3 released
2005-03-18  jrandom
    * Minor tweak to the timestamper to help reduce small skews
    * Adjust the stats published to include only the relevent ones
    * Only show the currently used speed calculation on the profile page
    * Allow the full max # resends to be sent, rather than piggybacking the
      RESET packet along side the final resend (duh)
    * Add irc.postman.i2p to the default list of IRC servers for new installs
    * Drop support for routers running 0.5 or 0.5.0.1 while maintaining
      backwards compatability for users running 0.5.0.2.
2005-03-18 22:34:51 +00:00
jrandom
89509490c5 2005-03-18 jrandom
* Eepproxy Fix for corrupted HTTP headers (thanks nickster!)
    * Fixed case sensitivity issues on the HTTP headers (thanks duck!)
2005-03-18 08:48:00 +00:00
jrandom
a997a46040 2005-03-17 jrandom
* Update the old speed calculator and associated profile data points to
      use a non-tiered moving average of the tunnel test time, avoiding the
      freshness issues of the old tiered speed stats.
    * Explicitly synchronize all of the methods on the PRNG, rather than just
      the feeder methods (sun and kaffe only need the feeder, but it seems ibm
      needs all of them synchronized).
    * Properly use the tunnel tests as part of the profile stats.
    * Don't flood the jobqueue with sequential persist profile tasks, but
      instead, inject a brief scheduling delay between them.
    * Reduce the TCP connection establishment timeout to 20s (which is still
      absurdly excessive)
    * Reduced the max resend delay to 30s so we can get some resends in when
      dealing with client apps that hang up early (e.g. wget)
    * Added more alternative socketManager factories (good call aum!)
2005-03-17 22:12:51 +00:00
jrandom
538dd07e7b 2005-03-16 jrandom
* Adjust the old speed calculator to include end to end RTT data in its
      estimates, and use that as the primary speed calculator again.
    * Use the mean of the high capacity speeds to determine the fast
      threshold, rather than the median.  Perhaps we should use the mean of
      all active non-failing peers?
    * Updated the profile page to sort by tier, then alphabetically.
    * Added some alternative socketManager factories (good call aum!)
2005-03-17 05:29:55 +00:00
cervantes
046778404e added arkan.i2p, search.i2p, floureszination.i2p, antipiratbyran.i2p
asylum.i2p, templar.i2p
2005-03-16 02:56:01 +00:00
jrandom
766f83d653 added feedspace.i2p 2005-03-16 02:46:17 +00:00
jrandom
b20aee6753 2005-03-14 jrandom
* New strict speed calculator that goes off the actual number of messages
      verifiably sent through the peer by way of tunnels.  Initially, this only
      contains the successful message count on inbound tunnels, but may be
      augmented later to include verified outbound messages, peers queried in
      the netDb, etc.  The speed calculation decays quickly, but should give
      a better differential than the previous stat (both values are shown on
      the /profiles.jsp page)
2005-03-15 03:47:14 +00:00
cervantes
f9aa3aef18 added wiki.fr.i2p 2005-03-14 04:31:55 +00:00
jrandom
d74aa6e53d (no, this doesnt fix things yet, but its a save point along the path)
2005-03-11  jrandom
    * Rather than the fixed resend timeout floor (10s), use 10s+RTT as the
      minimum (increased on resends as before, of course).
    * Always prod the clock update listeners, even if just to tell them that
      the time hasn't changed much.
    * Added support for explicit peer selection for individual tunnel pools,
      which will be useful in debugging but not recommended for use by normal
      end users.
    * More aggressively search for the next hop's routerInfo on tunnel join.
    * Give messages received via inbound tunnels that are bound to remote
      locations sufficient time (taking into account clock skew).
    * Give alternate direct send messages sufficient time (10s min, not 5s)
    * Always give the end to end data message the explicit timeout (though the
      old default was sufficient before)
    * No need to give end to end messages an insane expiration (+2m), as we
      are already handling skew on the receiving side.
    * Don't complain too loudly about expired TunnelCreateMessages (at least,
      not until after all those 0.5 and 0.5.0.1 users upgrade ;)
    * Properly keep the sendBps stat
    * When running the router with router.keepHistory=true, log more data to
      messageHistory.txt
    * Logging updates
    * Minor formatting updates
2005-03-11 22:23:36 +00:00
cervantes
ea6fbc7835 added septu.i2p 2005-03-09 20:02:14 +00:00
jrandom
536e604b8e 2005-03-07 jrandom
* Fix the HTTP response header filter to allow multiple headers with the
      same name (thanks duck and spotteri!)
2005-03-08 02:45:14 +00:00
jrandom
49d6f5018f * Properly expand the HTTP response header buffer (thanks shendaras!) 2005-03-07 00:40:45 +00:00
jrandom
4a830e422a added music.i2p, rotten.i2p, wintermute.i2p, kaji2.i2p, aspnet.i2p, gaming.i2p, nntp.i2p 2005-03-07 00:38:19 +00:00
jrandom
df6c52fe75 * 2005-03-06 0.5.0.2 released
2005-03-06  jrandom
    * Allow the I2PTunnel web interface to select streaming lib options for
      individual client tunnels, rather than sharing them across all of them,
      as we do with the session options.  This way people can (and should) set
      the irc proxy to interactive and the eepproxy to bulk.
    * Added a startRouter.sh script to new installs which simply calls
      "sh i2prouter start".  This should make it clear how people should start
      I2P.
2005-03-07 00:07:27 +00:00
jrandom
01979c08b3 2005-03-04 jrandom
* Filter HTTP response headers in the eepproxy, forcing Connection: close
      so that broken (/malicious) webservers can't allow persistent
      connections.  All HTTP compliant browsers should now always close the
      socket.
    * Enabled the GZIPInputStream's cache (they were'nt cached before)
    * Make sure our first send is always a SYN (duh)
    * Workaround for some buggy compilers
2005-03-05 02:54:42 +00:00
duck
7928ef83cc added cowsay.i2p 2005-03-04 23:37:39 +00:00
jrandom
10afe0a060 2005-03-03 jrandom
* Loop while starting up the I2PTunnel instances, in case the I2CP
      listener isn't up yet (thanks detonate!)
    * Implement custom reusable GZIP streams to both reduce memory churn
      and prevent the exposure of data in the standard GZIP header (creation
      time, OS, etc).  This is RFC1952 compliant, and backwards compatible,
      though has only been tested within the confines of I2P's compression use
      (DataHelper.[de]compress).
    * Preemptively support the next protocol version, so that after the 0.5.0.2
      release, we'll be able to drop protocol=2 to get rid of 0.5 users.
2005-03-04 06:09:20 +00:00
jrandom
ef230cfa3d 2005-03-02 jrandom
* Fix one substantial OOM cause (session tag manager was only dropping
      tags once the critical limit was met, rather than honoring their
      expiration) (duh)
    * Lots of small memory fixes
    * Double the allowable concurrent outstanding tunnel build tasks (20)
2005-03-03 03:36:52 +00:00
smeghead
2d15a42137 big code cleanup to reduce number of compiler warnings 2005-03-01 23:25:15 +00:00
jrandom
57d6a2f645 2005-03-01 jrandom
* Really disable the streaming lib packet caching
    * Synchronized a message handling point in the SDK (even though its use is
      already essentially single threaded, its better to play it safe)
    * Don't add new RepublishLeaseSetJobs on failure, just requeue up the
      existing one (duh)
    * Throttle the number of concurrent pending tunnel builds across all
      pools, in addition to simply throttling the number of new requests per
      minute for each pool individually.  This should avoid the cascading
      failure when tunnel builds take too long, as no new builds will be
      created until the previous ones are handled.
    * Factored out and extended the DataHelper's unit tests for dealing with
      long and date formatting.
    * Explicitly specify the HTTP auth realm as "i2prouter", though this
      alone doesn't address the bug where jetty asks for authentication too
      much.  (thanks orion!)
    * Updated the StreamSinkServer to ignore all read bytes, rather than write
      them to the filesystem.
2005-03-01 17:50:52 +00:00
jrandom
469a0852d7 2005-02-27 jrandom
* Don't rerequest leaseSets if there are already pending requests
    * Reverted the insufficiently tested caching in the DSA/SHA1 impl, and
      temporary disabled the streaming lib packet caching.
    * Reduced the resend RTT penalty to 10s
2005-02-27 22:09:37 +00:00
jrandom
7983bb1490 1.3 here too 2005-02-27 00:13:00 +00:00
jrandom
2e7eac02ed 2005-02-26 jrandom
* Force 1.3-isms on the precompiled jsps too (thanks laberhost)
2005-02-27 00:03:42 +00:00
jrandom
238389fc7f 2005-02-26 jrandom
* Further streaming lib caching improvements
    * Reduce the minimum RTT (used to calculate retry timeouts), but also
      increase the RTT on resends.
    * Lower the default message size to 4KB from 16KB to further reduce the
      chance of failed fragmentation.
    * Extend tunnel rebuild throttling to include fallback rebuilds
    * If there are less than 20 routers known, don't drop the last 20 (to help
      avoid dropping all peers under catastrophic failures)
    * New stats for end to end messages - "client.leaseSetFoundLocally",
      "client.leaseSetFoundRemoteTime", and "client.leaseSetFailedRemoteTime"
2005-02-26 19:16:46 +00:00
jrandom
4cec9da0a6 2005-02-24 jrandom
* Throttle the number of tunnel rebuilds per minute, preventing CPU
      overload under catastrophic failures (thanks Tracker and cervantes!)
    * Block the router startup process until we've initialized the clock
2005-02-24 23:53:35 +00:00
jrandom
00f27d4400 2005-02-24 jrandom
* Cache temporary memory allocation in the DSA's SHA1 impl, and the packet
      data in the streaming lib.
    * Fixed a streaming lib bug where the connection initiator would fail the
      stream if the ACK to their SYN was lost.
2005-02-24 18:05:25 +00:00
jrandom
f61618e4a4 2005-02-23 jrandom
* Now that we don't get stale SAM sessions, it'd be nice if we didn't
      get stale tunnel pools, don't you think?
2005-02-23 21:44:30 +00:00
231 changed files with 6490 additions and 1234 deletions

View File

@@ -21,7 +21,8 @@
<target name="distclean" depends="clean" />
<target name="compile" depends="init">
<javac srcdir="${src}" destdir="${build}" classpath="${servlet}"/>
<javac debug="true" deprecation="on" source="1.3" target="1.3"
srcdir="${src}" destdir="${build}" classpath="${servlet}"/>
</target>
<target name="jar" depends="compile">

View File

@@ -56,7 +56,8 @@
<arg value="-webapp" />
<arg value="../jsp/" />
</java>
<javac destdir="../jsp/WEB-INF/classes/" srcdir="../jsp/WEB-INF/classes" includes="**/*.java">
<javac debug="true" deprecation="on" source="1.3" target="1.3"
destdir="../jsp/WEB-INF/classes/" srcdir="../jsp/WEB-INF/classes" includes="**/*.java">
<classpath>
<pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />

View File

@@ -0,0 +1,242 @@
package net.i2p.i2ptunnel;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2005 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.FilterOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import net.i2p.data.ByteArray;
import net.i2p.util.ByteCache;
import net.i2p.util.Log;
/**
* Simple stream for delivering an HTTP response to
* the client, trivially filtered to make sure "Connection: close"
* is always in the response.
*
*/
class HTTPResponseOutputStream extends FilterOutputStream {
private static final Log _log = new Log(HTTPResponseOutputStream.class);
private ByteCache _cache;
protected ByteArray _headerBuffer;
private boolean _headerWritten;
private byte _buf1[];
private static final int CACHE_SIZE = 8*1024;
public HTTPResponseOutputStream(OutputStream raw) {
super(raw);
_cache = ByteCache.getInstance(8, CACHE_SIZE);
_headerBuffer = _cache.acquire();
_headerWritten = false;
_buf1 = new byte[1];
}
public void write(int c) throws IOException {
_buf1[0] = (byte)c;
write(_buf1, 0, 1);
}
public void write(byte buf[]) throws IOException {
write(buf, 0, buf.length);
}
public void write(byte buf[], int off, int len) throws IOException {
if (_headerWritten) {
out.write(buf, off, len);
return;
}
for (int i = 0; i < len; i++) {
ensureCapacity();
_headerBuffer.getData()[_headerBuffer.getValid()] = buf[off+i];
_headerBuffer.setValid(_headerBuffer.getValid()+1);
if (headerReceived()) {
writeHeader();
_headerWritten = true;
if (i + 1 < len) // write out the remaining
out.write(buf, off+i+1, len-i-1);
return;
}
}
}
/** grow (and free) the buffer as necessary */
private void ensureCapacity() {
if (_headerBuffer.getValid() + 1 >= _headerBuffer.getData().length) {
int newSize = (int)(_headerBuffer.getData().length * 1.5);
ByteArray newBuf = new ByteArray(new byte[newSize]);
System.arraycopy(_headerBuffer.getData(), 0, newBuf.getData(), 0, _headerBuffer.getValid());
newBuf.setValid(_headerBuffer.getValid());
newBuf.setOffset(0);
if (_headerBuffer.getData().length == CACHE_SIZE)
_cache.release(_headerBuffer);
_headerBuffer = newBuf;
}
}
/** are the headers finished? */
private boolean headerReceived() {
if (_headerBuffer.getValid() < 3) return false;
byte first = _headerBuffer.getData()[_headerBuffer.getValid()-3];
byte second = _headerBuffer.getData()[_headerBuffer.getValid()-2];
byte third = _headerBuffer.getData()[_headerBuffer.getValid()-1];
return (isNL(second) && isNL(third)) || // \n\n
(isNL(first) && isNL(third)); // \n\r\n
}
/**
* Tweak that first HTTP response line (HTTP 200 OK, etc)
*
*/
protected String filterResponseLine(String line) {
return line;
}
/** we ignore any potential \r, since we trim it on write anyway */
private static final byte NL = '\n';
private boolean isNL(byte b) { return (b == NL); }
/** ok, received, now munge & write it */
private void writeHeader() throws IOException {
String responseLine = null;
boolean connectionSent = false;
boolean proxyConnectionSent = false;
int lastEnd = -1;
for (int i = 0; i < _headerBuffer.getValid(); i++) {
if (isNL(_headerBuffer.getData()[i])) {
if (lastEnd == -1) {
responseLine = new String(_headerBuffer.getData(), 0, i+1); // includes NL
responseLine = filterResponseLine(responseLine);
responseLine = (responseLine.trim() + "\n");
out.write(responseLine.getBytes());
} else {
for (int j = lastEnd+1; j < i; j++) {
if (_headerBuffer.getData()[j] == ':') {
int keyLen = j-(lastEnd+1);
int valLen = i-(j+2);
if ( (keyLen <= 0) || (valLen <= 0) )
throw new IOException("Invalid header @ " + j);
String key = new String(_headerBuffer.getData(), lastEnd+1, keyLen);
String val = new String(_headerBuffer.getData(), j+2, valLen);
if ("Connection".equalsIgnoreCase(key)) {
out.write("Connection: close\n".getBytes());
connectionSent = true;
} else if ("Proxy-Connection".equalsIgnoreCase(key)) {
out.write("Proxy-Connection: close\n".getBytes());
proxyConnectionSent = true;
} else {
out.write((key.trim() + ": " + val.trim() + "\n").getBytes());
}
break;
}
}
}
lastEnd = i;
}
}
if (!connectionSent)
out.write("Connection: close\n".getBytes());
if (!proxyConnectionSent)
out.write("Proxy-Connection: close\n".getBytes());
out.write("\n".getBytes()); // end of the headers
// done, shove off
if (_headerBuffer.getData().length == CACHE_SIZE)
_cache.release(_headerBuffer);
else
_headerBuffer = null;
}
public static void main(String args[]) {
String simple = "HTTP/1.1 200 OK\n" +
"foo: bar\n" +
"baz: bat\n" +
"\n" +
"hi ho, this is the body";
String filtered = "HTTP/1.1 200 OK\n" +
"Connection: keep-alive\n" +
"foo: bar\n" +
"baz: bat\n" +
"\n" +
"hi ho, this is the body";
String winfilter= "HTTP/1.1 200 OK\r\n" +
"Connection: keep-alive\r\n" +
"foo: bar\r\n" +
"baz: bat\r\n" +
"\r\n" +
"hi ho, this is the body";
String minimal = "HTTP/1.1 200 OK\n" +
"\n" +
"hi ho, this is the body";
String winmin = "HTTP/1.1 200 OK\r\n" +
"\r\n" +
"hi ho, this is the body";
String invalid1 = "HTTP/1.1 200 OK\n";
String invalid2 = "HTTP/1.1 200 OK";
String invalid3 = "HTTP 200 OK\r\n";
String invalid4 = "HTTP 200 OK\r";
String invalid5 = "HTTP/1.1 200 OK\r\n" +
"I am broken, and I smell\r\n" +
"\r\n";
String invalid6 = "HTTP/1.1 200 OK\r\n" +
":I am broken, and I smell\r\n" +
"\r\n";
String invalid7 = "HTTP/1.1 200 OK\n" +
"I am broken, and I smell:\n" +
":asdf\n" +
":\n" +
"\n";
String large = "HTTP/1.1 200 OK\n" +
"Last-modified: Tue, 25 Nov 2003 12:05:38 GMT\n" +
"Expires: Tue, 25 Nov 2003 12:05:38 GMT\n" +
"Content-length: 32\n" +
"\n" +
"hi ho, this is the body";
/* */
test("Simple", simple, true);
test("Filtered", filtered, true);
test("Filtered windows", winfilter, true);
test("Minimal", minimal, true);
test("Windows", winmin, true);
test("Large", large, true);
test("Invalid (short headers)", invalid1, true);
test("Invalid (no headers)", invalid2, true);
test("Invalid (windows with short headers)", invalid3, true);
test("Invalid (windows no headers)", invalid4, true);
test("Invalid (bad headers)", invalid5, true);
test("Invalid (bad headers2)", invalid6, false);
test("Invalid (bad headers3)", invalid7, false);
/* */
}
private static void test(String name, String orig, boolean shouldPass) {
System.out.println("====Testing: " + name + "\n" + orig + "\n------------");
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
HTTPResponseOutputStream resp = new HTTPResponseOutputStream(baos);
resp.write(orig.getBytes());
resp.flush();
String received = new String(baos.toByteArray());
System.out.println(received);
} catch (Exception e) {
if (shouldPass)
e.printStackTrace();
else
System.out.println("Properly fails with " + e.getMessage());
}
}
}

View File

@@ -12,15 +12,12 @@ import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import net.i2p.I2PException;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
@@ -104,11 +101,17 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
this.l = l;
this.handlerName = handlerName + _clientId;
synchronized (sockLock) {
if (ownDest) {
sockMgr = buildSocketManager();
} else {
sockMgr = getSocketManager();
while (sockMgr == null) {
synchronized (sockLock) {
if (ownDest) {
sockMgr = buildSocketManager();
} else {
sockMgr = getSocketManager();
}
}
if (sockMgr == null) {
_log.log(Log.CRIT, "Unable to create socket manager");
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
}
if (sockMgr == null) {

View File

@@ -22,9 +22,7 @@ import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.Clock;
import net.i2p.util.EventDispatcher;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
@@ -196,9 +194,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix(requestId) + "Line=[" + line + "]");
if (line.startsWith("Connection: ") ||
line.startsWith("Keep-Alive: ") ||
line.startsWith("Proxy-Connection: "))
String lowercaseLine = line.toLowerCase();
if (lowercaseLine.startsWith("connection: ") ||
lowercaseLine.startsWith("keep-alive: ") ||
lowercaseLine.startsWith("proxy-connection: "))
continue;
if (method == null) { // first line (GET /base64/realaddr)
@@ -337,29 +336,29 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
} else {
if (line.startsWith("Host: ") && !usingWWWProxy) {
if (lowercaseLine.startsWith("host: ") && !usingWWWProxy) {
line = "Host: " + host;
if (_log.shouldLog(Log.INFO))
_log.info(getPrefix(requestId) + "Setting host = " + host);
} else if (line.startsWith("User-Agent: ")) {
} else if (lowercaseLine.startsWith("user-agent: ")) {
// always stripped, added back at the end
line = null;
continue;
} else if (line.startsWith("Accept")) {
} else if (lowercaseLine.startsWith("accept")) {
// strip the accept-blah headers, as they vary dramatically from
// browser to browser
line = null;
continue;
} else if (line.startsWith("Referer: ")) {
} else if (lowercaseLine.startsWith("referer: ")) {
// Shouldn't we be more specific, like accepting in-site referers ?
//line = "Referer: i2p";
line = null;
continue; // completely strip the line
} else if (line.startsWith("Via: ")) {
} else if (lowercaseLine.startsWith("via: ")) {
//line = "Via: i2p";
line = null;
continue; // completely strip the line
} else if (line.startsWith("From: ")) {
} else if (lowercaseLine.startsWith("from: ")) {
//line = "From: i2p";
line = null;
continue; // completely strip the line
@@ -418,7 +417,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
I2PSocket i2ps = createI2PSocket(dest, getDefaultOptions(opts));
byte[] data = newRequest.toString().getBytes("ISO-8859-1");
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
I2PTunnelRunner runner = new I2PTunnelHTTPClientRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
} catch (SocketException ex) {
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
l.log(ex.getMessage());

View File

@@ -0,0 +1,41 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.FilterOutputStream;
import java.net.Socket;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper;
import net.i2p.util.ByteCache;
import net.i2p.util.Log;
/**
* Override the response with a stream filtering the HTTP headers
* received. Specifically, this makes sure we get Connection: close,
* so the browser knows they really shouldn't try to use persistent
* connections. The HTTP server *should* already be setting this,
* since the HTTP headers sent by the browser specify Connection: close,
* and the server should echo it. However, both broken and malicious
* servers could ignore that, potentially confusing the user.
*
*/
public class I2PTunnelHTTPClientRunner extends I2PTunnelRunner {
public I2PTunnelHTTPClientRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, List sockList, Runnable onTimeout) {
super(s, i2ps, slock, initialI2PData, sockList, onTimeout);
}
protected OutputStream getSocketOut() throws IOException {
OutputStream raw = super.getSocketOut();
return new HTTPResponseOutputStream(raw);
}
}

View File

@@ -4,7 +4,6 @@
package net.i2p.i2ptunnel;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
@@ -17,7 +16,6 @@ import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.data.DataHelper;
import net.i2p.util.EventDispatcher;
import net.i2p.util.I2PThread;

View File

@@ -3,7 +3,6 @@
*/
package net.i2p.i2ptunnel;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
@@ -31,7 +30,7 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
* Sun's impl of BufferedOutputStream), but that is the streaming
* api's job...
*/
static int MAX_PACKET_SIZE = 1024 * 32;
static int MAX_PACKET_SIZE = 1024 * 4;
static final int NETWORK_BUFFER_SIZE = MAX_PACKET_SIZE;
@@ -112,10 +111,13 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
return startedOn;
}
protected InputStream getSocketIn() throws IOException { return s.getInputStream(); }
protected OutputStream getSocketOut() throws IOException { return s.getOutputStream(); }
public void run() {
try {
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream(); // = new BufferedOutputStream(s.getOutputStream(), NETWORK_BUFFER_SIZE);
InputStream in = getSocketIn();
OutputStream out = getSocketOut(); // = new BufferedOutputStream(s.getOutputStream(), NETWORK_BUFFER_SIZE);
i2ps.setSocketErrorListener(this);
InputStream i2pin = i2ps.getInputStream();
OutputStream i2pout = i2ps.getOutputStream(); //new BufferedOutputStream(i2ps.getOutputStream(), MAX_PACKET_SIZE);
@@ -216,7 +218,7 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
this.out = out;
_toI2P = toI2P;
direction = (toI2P ? "toI2P" : "fromI2P");
_cache = ByteCache.getInstance(16, NETWORK_BUFFER_SIZE);
_cache = ByteCache.getInstance(32, NETWORK_BUFFER_SIZE);
setName("StreamForwarder " + _runnerId + "." + (++__forwarderId));
start();
}
@@ -281,6 +283,7 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
//else
// _log.warn("You may ignore this", ex);
} finally {
_cache.release(ba);
if (_log.shouldLog(Log.INFO)) {
_log.info(direction + ": done forwarding between "
+ from + " and " + to);
@@ -302,7 +305,6 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
finishLock.notifyAll();
// the main thread will close sockets etc. now
}
_cache.release(ba);
}
}
}

View File

@@ -75,10 +75,16 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
I2PClient client = I2PClientFactory.createClient();
Properties props = new Properties();
props.putAll(getTunnel().getClientOptions());
synchronized (slock) {
sockMgr = I2PSocketManagerFactory.createManager(privData, getTunnel().host, Integer.parseInt(getTunnel().port),
props);
while (sockMgr == null) {
synchronized (slock) {
sockMgr = I2PSocketManagerFactory.createManager(privData, getTunnel().host, Integer.parseInt(getTunnel().port),
props);
}
if (sockMgr == null) {
_log.log(Log.CRIT, "Unable to create socket manager");
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
}
sockMgr.setName("Server");
getTunnel().addSession(sockMgr.getSession());

View File

@@ -1,12 +1,8 @@
package net.i2p.i2ptunnel;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;

View File

@@ -1,7 +1,5 @@
package net.i2p.i2ptunnel;
import java.io.File;
import java.util.Iterator;
import java.util.Properties;
import java.util.Random;
@@ -63,6 +61,8 @@ class WebEditPageFormGenerator {
buf.append("value=\"squid.i2p\" ");
buf.append("/><br />\n");
addStreamingOptions(buf, controller);
buf.append("<hr />Note: the following options are shared across all client tunnels and");
buf.append(" HTTP proxies<br />\n");
@@ -86,6 +86,8 @@ class WebEditPageFormGenerator {
buf.append("value=\"").append(controller.getTargetDestination()).append("\" ");
buf.append(" /> (either the hosts.txt name or the full base64 destination)<br />\n");
addStreamingOptions(buf, controller);
buf.append("<hr />Note: the following options are shared across all client tunnels and");
buf.append(" HTTP proxies<br />\n");
@@ -124,6 +126,8 @@ class WebEditPageFormGenerator {
buf.append("<input type=\"hidden\" name=\"privKeyGenerate\" value=\"true\" />");
}
addStreamingOptions(buf, controller);
addOptions(buf, controller);
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
@@ -166,6 +170,8 @@ class WebEditPageFormGenerator {
buf.append("<input type=\"hidden\" name=\"privKeyGenerate\" value=\"true\" />");
}
addStreamingOptions(buf, controller);
addOptions(buf, controller);
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
@@ -206,8 +212,9 @@ class WebEditPageFormGenerator {
buf.append(" checked=\"true\" />\n<br />\n");
else
buf.append(" />\n<br />\n");
}
/**
* Generate the fields asking for what port and interface the tunnel should
* listen on.
@@ -245,6 +252,46 @@ class WebEditPageFormGenerator {
buf.append("\"><br />\n");
}
private static void addStreamingOptions(StringBuffer buf, TunnelController controller) {
int connectDelay = 0;
int maxWindowSize = -1;
Properties opts = getOptions(controller);
if (opts != null) {
String delay = opts.getProperty("i2p.streaming.connectDelay");
if (delay != null) {
try {
connectDelay = Integer.parseInt(delay);
} catch (NumberFormatException nfe) {
connectDelay = 0;
}
}
String max = opts.getProperty("i2p.streaming.maxWindowSize");
if (max != null) {
try {
maxWindowSize = Integer.parseInt(max);
} catch (NumberFormatException nfe) {
maxWindowSize = -1;
}
}
}
buf.append("<b>Delay connection briefly? </b> ");
buf.append("<input type=\"checkbox\" name=\"connectDelay\" value=\"");
buf.append((connectDelay > 0 ? connectDelay : 1000)).append("\" ");
if (connectDelay > 0)
buf.append("checked=\"true\" ");
buf.append("/> (useful for brief request/response connections)<br />\n");
buf.append("<b>Communication profile:</b>");
buf.append("<select name=\"profile\">");
if (maxWindowSize <= 0)
buf.append("<option value=\"interactive\">Interactive</option><option value=\"bulk\" selected=\"true\">Bulk</option>");
else
buf.append("<option value=\"interactive\" selected=\"true\">Interactive</option><option value=\"bulk\">Bulk</option>");
buf.append("</select><br />\n");
}
/**
* Add fields for customizing the I2PSession options, including helpers for
* tunnel depth and count, as well as I2CP host and port.
@@ -255,8 +302,6 @@ class WebEditPageFormGenerator {
private static void addOptions(StringBuffer buf, TunnelController controller) {
int tunnelDepth = 2;
int numTunnels = 2;
int connectDelay = 0;
int maxWindowSize = -1;
Properties opts = getOptions(controller);
if (opts != null) {
String depth = opts.getProperty("inbound.length");
@@ -275,22 +320,6 @@ class WebEditPageFormGenerator {
numTunnels = 2;
}
}
String delay = opts.getProperty("i2p.streaming.connectDelay");
if (delay != null) {
try {
connectDelay = Integer.parseInt(delay);
} catch (NumberFormatException nfe) {
connectDelay = 0;
}
}
String max = opts.getProperty("i2p.streaming.maxWindowSize");
if (max != null) {
try {
maxWindowSize = Integer.parseInt(max);
} catch (NumberFormatException nfe) {
maxWindowSize = -1;
}
}
}
buf.append("<b>Tunnel depth:</b> ");
@@ -330,21 +359,6 @@ class WebEditPageFormGenerator {
}
buf.append("</select><br />\n");
buf.append("<b>Delay connection briefly? </b> ");
buf.append("<input type=\"checkbox\" name=\"connectDelay\" value=\"");
buf.append((connectDelay > 0 ? connectDelay : 1000)).append("\" ");
if (connectDelay > 0)
buf.append("checked=\"true\" ");
buf.append("/> (useful for brief request/response connections)<br />\n");
buf.append("<b>Communication profile:</b>");
buf.append("<select name=\"profile\">");
if (maxWindowSize <= 0)
buf.append("<option value=\"interactive\">Interactive</option><option value=\"bulk\" selected=\"true\">Bulk</option>");
else
buf.append("<option value=\"interactive\" selected=\"true\">Interactive</option><option value=\"bulk\">Bulk</option>");
buf.append("</select><br />\n");
buf.append("<b>I2CP host:</b> ");
buf.append("<input type=\"text\" name=\"clientHost\" size=\"20\" value=\"");
if ( (controller != null) && (controller.getI2CPHost() != null) )

View File

@@ -296,6 +296,10 @@ public class WebEditPageHelper {
cOpt.setProperty("option.inbound.length", _tunnelDepth);
cOpt.setProperty("option.outbound.length", _tunnelDepth);
}
// these are per-proxy settings, not per-session settings, and
// as such don't need to be shared. the values are propogated
// to the current tunnel's settings via cur.setConfig above
/*
if (_connectDelay)
cOpt.setProperty("option.i2p.streaming.connectDelay", "1000");
else
@@ -304,6 +308,7 @@ public class WebEditPageHelper {
cOpt.setProperty("option.i2p.streaming.maxWindowSize", "1");
else
cOpt.remove("option.i2p.streaming.maxWindowSize");
*/
if (_name != null) {
cOpt.setProperty("option.inbound.nickname", _name);
cOpt.setProperty("option.outbound.nickname", _name);

View File

@@ -14,7 +14,6 @@ import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.DataFormatException;
import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.util.Log;

View File

@@ -4,27 +4,16 @@
*/
package net.i2p.client.streaming;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.I2PSessionListener;
import net.i2p.data.Base64;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.Log;
/**

View File

@@ -36,18 +36,27 @@ public class I2PSocketManagerFactory {
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager() {
String i2cpHost = System.getProperty(I2PClient.PROP_TCP_HOST, "localhost");
int i2cpPort = 7654;
String i2cpPortStr = System.getProperty(I2PClient.PROP_TCP_PORT);
if (i2cpPortStr != null) {
try {
i2cpPort = Integer.parseInt(i2cpPortStr);
} catch (NumberFormatException nfe) {
// gobble gobble
}
}
return createManager(getHost(), getPort(), System.getProperties());
}
/**
* Create a socket manager using a brand new destination connected to the
* I2CP router on the local machine on the default port (7654).
*
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(Properties opts) {
return createManager(getHost(), getPort(), opts);
}
return createManager(i2cpHost, i2cpPort, System.getProperties());
/**
* Create a socket manager using a brand new destination connected to the
* I2CP router on the specified host and port
*
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(String host, int port) {
return createManager(host, port, System.getProperties());
}
/**
@@ -72,6 +81,26 @@ public class I2PSocketManagerFactory {
}
}
/**
* Create a socket manager using the destination loaded from the given private key
* stream and connected to the default I2CP host and port.
*
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(InputStream myPrivateKeyStream) {
return createManager(myPrivateKeyStream, getHost(), getPort(), System.getProperties());
}
/**
* Create a socket manager using the destination loaded from the given private key
* stream and connected to the default I2CP host and port.
*
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(InputStream myPrivateKeyStream, Properties opts) {
return createManager(myPrivateKeyStream, getHost(), getPort(), opts);
}
/**
* Create a socket manager using the destination loaded from the given private key
* stream and connected to the I2CP router on the specified machine on the given
@@ -154,4 +183,20 @@ public class I2PSocketManagerFactory {
}
}
private static String getHost() {
return System.getProperty(I2PClient.PROP_TCP_HOST, "localhost");
}
private static int getPort() {
int i2cpPort = 7654;
String i2cpPortStr = System.getProperty(I2PClient.PROP_TCP_PORT);
if (i2cpPortStr != null) {
try {
i2cpPort = Integer.parseInt(i2cpPortStr);
} catch (NumberFormatException nfe) {
// gobble gobble
}
}
return i2cpPort;
}
}

View File

@@ -1,7 +1,5 @@
package net.i2p.client.streaming;
import java.util.Properties;
/**
* Define the configuration for streaming and verifying data on the socket.
*

View File

@@ -1,6 +1,5 @@
package net.i2p.client.streaming;
import java.util.Iterator;
import java.util.Properties;
/**

View File

@@ -8,8 +8,6 @@ import java.io.OutputStream;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.util.Random;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.data.Destination;

View File

@@ -122,11 +122,12 @@ public class StreamSinkServer {
long written = 0;
int read = 0;
while ( (read = in.read(buf)) != -1) {
_fos.write(buf, 0, read);
//_fos.write(buf, 0, read);
written += read;
if (_log.shouldLog(Log.DEBUG))
_log.debug("read and wrote " + read);
}
_fos.write(("written: [" + written + "]\n").getBytes());
long lifetime = System.currentTimeMillis() - start;
_log.error("Got EOF from client socket [written=" + written + " lifetime=" + lifetime + "]");
} catch (IOException ioe) {
@@ -150,7 +151,7 @@ public class StreamSinkServer {
StreamSinkServer server = null;
switch (args.length) {
case 0:
server = new StreamSinkServer("dataDir", "server.key", "localhost", 10001);
server = new StreamSinkServer("dataDir", "server.key", "localhost", 7654);
break;
case 2:
server = new StreamSinkServer(args[0], args[1]);

View File

@@ -1,24 +1,12 @@
package net.i2p.client.streaming;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Map;
import java.util.Collections;
import java.util.HashMap;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PClientFactory;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.util.Log;
import net.i2p.util.I2PThread;

View File

@@ -74,7 +74,9 @@
<arg value="../jsp/" />
</java>
<javac destdir="../jsp/WEB-INF/classes/" srcdir="../jsp/WEB-INF/classes" includes="**/*.java">
<javac debug="true" deprecation="on" source="1.3" target="1.3"
destdir="../jsp/WEB-INF/classes/"
srcdir="../jsp/WEB-INF/classes" includes="**/*.java">
<classpath>
<pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />

View File

@@ -17,11 +17,6 @@ public class ConfigAdvancedHandler extends FormHandler {
private boolean _shouldSave;
private String _config;
public void ConfigNetHandler() {
_shouldSave = false;
_forceRestart = false;
}
protected void processForm() {
if (_shouldSave) {
saveChanges();

View File

@@ -1,11 +1,8 @@
package net.i2p.router.web;
import java.io.ByteArrayInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Properties;
import net.i2p.util.Log;
/**
* Handler to deal with form submissions from the logging config form and act
@@ -21,10 +18,6 @@ public class ConfigLoggingHandler extends FormHandler {
private String _dateFormat;
private String _fileSize;
public void ConfigNetHandler() {
_shouldSave = false;
}
protected void processForm() {
if (_shouldSave) {
saveChanges();

View File

@@ -1,14 +1,9 @@
package net.i2p.router.web;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.TreeSet;
import net.i2p.util.Log;
import net.i2p.router.RouterContext;
public class ConfigLoggingHelper {

View File

@@ -13,15 +13,10 @@ import java.net.URLConnection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.i2p.util.Log;
import net.i2p.time.Timestamper;
import net.i2p.router.RouterContext;
import net.i2p.router.ClientTunnelSettings;
/**
* Handler to deal with form submissions from the main config form and act
* upon the values.
@@ -41,13 +36,6 @@ public class ConfigNetHandler extends FormHandler {
private String _reseedFrom;
private String _sharePct;
public void ConfigNetHandler() {
_guessRequested = false;
_reseedRequested = false;
_saveRequested = false;
_timeSyncEnabled = false;
}
protected void processForm() {
if (_guessRequested) {
guessHostname();

View File

@@ -1,16 +1,7 @@
package net.i2p.router.web;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Iterator;
import java.util.TreeMap;
import net.i2p.time.Timestamper;
import net.i2p.util.Log;
import net.i2p.router.RouterContext;
import net.i2p.router.ClientTunnelSettings;
public class ConfigNetHelper {
private RouterContext _context;

View File

@@ -8,7 +8,6 @@ import java.util.Properties;
import java.util.TreeMap;
import net.i2p.data.DataHelper;
import net.i2p.router.ClientTunnelSettings;
import net.i2p.router.Router;
import net.i2p.apps.systray.SysTray;
import net.i2p.apps.systray.UrlLauncher;
@@ -20,9 +19,8 @@ import org.tanukisoftware.wrapper.WrapperManager;
*
*/
public class ConfigServiceHandler extends FormHandler {
public void ConfigNetHandler() {}
private class UpdateWrapperManagerTask implements Runnable {
public static class UpdateWrapperManagerTask implements Runnable {
private int _exitCode;
public UpdateWrapperManagerTask(int exitCode) {
_exitCode = exitCode;

View File

@@ -1,15 +1,8 @@
package net.i2p.router.web;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Iterator;
import java.util.Set;
import java.util.Properties;
import java.util.TreeMap;
import net.i2p.util.Log;
import net.i2p.data.Destination;
import net.i2p.router.RouterContext;
import net.i2p.router.TunnelPoolSettings;

View File

@@ -0,0 +1,103 @@
package net.i2p.router.web;
import net.i2p.data.DataHelper;
/**
*
*/
public class ConfigUpdateHandler extends FormHandler {
private String _newsURL;
private long _refreshFrequency;
private String _updateURL;
private String _updatePolicy;
private String _proxyHost;
private String _proxyPort;
private boolean _updateThroughProxy;
private String _trustedKeys;
public static final String PROP_NEWS_URL = "router.newsURL";
public static final String DEFAULT_NEWS_URL = "http://dev.i2p.net/cgi-bin/cvsweb.cgi/i2p/news.xml?rev=HEAD";
public static final String PROP_REFRESH_FREQUENCY = "router.newsRefreshFrequency";
public static final String DEFAULT_REFRESH_FREQUENCY = 12*60*60*1000 + "";
public static final String PROP_UPDATE_URL = "router.updateURL";
public static final String DEFAULT_UPDATE_URL = "http://dev.i2p.net/i2p/i2pupdate.sud";
public static final String PROP_UPDATE_POLICY = "router.updatePolicy";
public static final String DEFAULT_UPDATE_POLICY = "notify";
public static final String PROP_SHOULD_PROXY = "router.updateThroughProxy";
public static final String DEFAULT_SHOULD_PROXY = Boolean.FALSE.toString();
public static final String PROP_PROXY_HOST = "router.updateProxyHost";
public static final String DEFAULT_PROXY_HOST = "localhost";
public static final String PROP_PROXY_PORT = "router.updateProxyPort";
public static final String DEFAULT_PROXY_PORT = "4444";
protected void processForm() {
if ( (_newsURL != null) && (_newsURL.length() > 0) ) {
String oldURL = _context.router().getConfigSetting(PROP_NEWS_URL);
if ( (oldURL == null) || (!_newsURL.equals(oldURL)) ) {
_context.router().setConfigSetting(PROP_NEWS_URL, _newsURL);
addFormNotice("Updating news URL to " + _newsURL);
}
}
if ( (_updateURL != null) && (_updateURL.length() > 0) ) {
String oldURL = _context.router().getConfigSetting(PROP_UPDATE_URL);
if ( (oldURL == null) || (!_updateURL.equals(oldURL)) ) {
_context.router().setConfigSetting(PROP_UPDATE_URL, _updateURL);
addFormNotice("Updating update URL to " + _updateURL);
}
}
if ( (_proxyHost != null) && (_proxyHost.length() > 0) ) {
String oldHost = _context.router().getConfigSetting(PROP_PROXY_HOST);
if ( (oldHost == null) || (!_proxyHost.equals(oldHost)) ) {
_context.router().setConfigSetting(PROP_PROXY_HOST, _proxyHost);
addFormNotice("Updating proxy host to " + _proxyHost);
}
}
if ( (_proxyPort != null) && (_proxyPort.length() > 0) ) {
String oldPort = _context.router().getConfigSetting(PROP_PROXY_PORT);
if ( (oldPort == null) || (!_proxyHost.equals(oldPort)) ) {
_context.router().setConfigSetting(PROP_PROXY_PORT, _proxyPort);
addFormNotice("Updating proxy port to " + _proxyPort);
}
}
if (_updateThroughProxy) {
_context.router().setConfigSetting(PROP_SHOULD_PROXY, Boolean.TRUE.toString());
} else {
_context.router().setConfigSetting(PROP_SHOULD_PROXY, Boolean.FALSE.toString());
}
String oldFreqStr = _context.router().getConfigSetting(PROP_REFRESH_FREQUENCY);
long oldFreq = -1;
if (oldFreqStr != null)
try { oldFreq = Long.parseLong(oldFreqStr); } catch (NumberFormatException nfe) {}
if (_refreshFrequency != oldFreq) {
_context.router().setConfigSetting(PROP_REFRESH_FREQUENCY, ""+_refreshFrequency);
addFormNotice("Updating refresh frequency to " + DataHelper.formatDuration(_refreshFrequency));
}
if ( (_updatePolicy != null) && (_updatePolicy.length() > 0) ) {
String oldPolicy = _context.router().getConfigSetting(PROP_UPDATE_POLICY);
if ( (oldPolicy == null) || (!_updatePolicy.equals(oldPolicy)) ) {
_context.router().setConfigSetting(PROP_UPDATE_POLICY, _updatePolicy);
addFormNotice("Updating update policy to " + _updatePolicy);
}
}
// should save the keys...
_context.router().saveConfig();
}
public void setNewsURL(String url) { _newsURL = url; }
public void setRefreshFrequency(String freq) {
try { _refreshFrequency = Long.parseLong(freq); } catch (NumberFormatException nfe) {}
}
public void setUpdateURL(String url) { _updateURL = url; }
public void setUpdatePolicy(String policy) { _updatePolicy = policy; }
public void setTrustedKeys(String keys) { _trustedKeys = keys; }
public void setUpdateThroughProxy(String foo) { _updateThroughProxy = true; }
public void setProxyHost(String host) { _proxyHost = host; }
public void setProxyPort(String port) { _proxyPort = port; }
}

View File

@@ -0,0 +1,123 @@
package net.i2p.router.web;
import java.util.List;
import net.i2p.data.DataHelper;
import net.i2p.crypto.TrustedUpdate;
import net.i2p.router.RouterContext;
public class ConfigUpdateHelper {
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 ConfigUpdateHelper() {}
public boolean updateAvailable() {
return true;
}
public String getNewsURL() {
String url = _context.getProperty(ConfigUpdateHandler.PROP_NEWS_URL);
if (url != null)
return url;
else
return ConfigUpdateHandler.DEFAULT_NEWS_URL;
}
public String getUpdateURL() {
String url = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_URL);
if (url != null)
return url;
else
return ConfigUpdateHandler.DEFAULT_UPDATE_URL;
}
public String getProxyHost() {
String host = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST);
if (host != null)
return host;
else
return ConfigUpdateHandler.DEFAULT_PROXY_HOST;
}
public String getProxyPort() {
String port = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT);
if (port != null)
return port;
else
return ConfigUpdateHandler.DEFAULT_PROXY_PORT;
}
public String getUpdateThroughProxy() {
String proxy = _context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY);
if (Boolean.valueOf(proxy).booleanValue())
return "<input type=\"checkbox\" value=\"true\" name=\"updateThroughProxy\" checked=\"true\" >";
else
return "<input type=\"checkbox\" value=\"true\" name=\"updateThroughProxy\" >";
}
private static final long PERIODS[] = new long[] { 12*60*60*1000l, 24*60*60*1000l, 48*60*60*1000l, -1l };
public String getRefreshFrequencySelectBox() {
String freq = _context.getProperty(ConfigUpdateHandler.PROP_REFRESH_FREQUENCY);
if (freq == null) freq = ConfigUpdateHandler.DEFAULT_REFRESH_FREQUENCY;
long ms = -1;
try {
ms = Long.parseLong(freq);
} catch (NumberFormatException nfe) {}
StringBuffer buf = new StringBuffer(256);
buf.append("<select name=\"refreshFrequency\">");
for (int i = 0; i < PERIODS.length; i++) {
buf.append("<option value=\"").append(PERIODS[i]);
if (PERIODS[i] == ms)
buf.append("\" selected=\"true\"");
if (PERIODS[i] == -1)
buf.append("\">Never</option>\n");
else
buf.append("\">Every ").append(DataHelper.formatDuration(PERIODS[i])).append("</option>\n");
}
buf.append("</select>\n");
return buf.toString();
}
public String getUpdatePolicySelectBox() {
String policy = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_POLICY);
if (policy == null) policy = ConfigUpdateHandler.DEFAULT_UPDATE_POLICY;
StringBuffer buf = new StringBuffer(256);
buf.append("<select name=\"updatePolicy\">");
if ("notify".equals(policy))
buf.append("<option value=\"notify\" selected=\"true\">Notify only</option>");
else
buf.append("<option value=\"notify\">Notify only</option>");
if ("install".equals(policy))
buf.append("<option value=\"install\" selected=\"true\">Download and install</option>");
else
buf.append("<option value=\"install\">Download and install</option>");
buf.append("</select>\n");
return buf.toString();
}
public String getTrustedKeys() {
StringBuffer buf = new StringBuffer(1024);
TrustedUpdate up = new TrustedUpdate(_context);
List keys = up.getTrustedKeys();
for (int i = 0; i < keys.size(); i++)
buf.append((String)keys.get(i)).append('\n');
return buf.toString();
}
}

View File

@@ -1,9 +1,5 @@
package net.i2p.router.web;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import net.i2p.router.RouterContext;
import net.i2p.util.FileUtil;

View File

@@ -3,10 +3,7 @@ package net.i2p.router.web;
import java.util.List;
import java.util.ArrayList;
import net.i2p.util.Log;
import net.i2p.router.RouterContext;
import net.i2p.router.ClientTunnelSettings;
/**
* Simple form handler base class - does not depend on servlets or jsp,

View File

@@ -1,7 +1,5 @@
package net.i2p.router.web;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import net.i2p.router.RouterContext;

View File

@@ -0,0 +1,240 @@
package net.i2p.router.web;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.router.RouterContext;
import net.i2p.router.RouterVersion;
import net.i2p.util.EepGet;
import net.i2p.util.Log;
/**
* Task to periodically look for updates to the news.xml, and to keep
* track of whether that has an announcement for a new version.
*/
public class NewsFetcher implements Runnable, EepGet.StatusListener {
private I2PAppContext _context;
private Log _log;
private boolean _updateAvailable;
private long _lastFetch;
private static NewsFetcher _instance;
public static final NewsFetcher getInstance() { return _instance; }
private static final String NEWS_FILE = "docs/news.xml";
public NewsFetcher(I2PAppContext ctx) {
_context = ctx;
_log = ctx.logManager().getLog(NewsFetcher.class);
_instance = this;
File news = new File(NEWS_FILE);
if (news.exists())
_lastFetch = news.lastModified();
else
_lastFetch = 0;
}
public boolean updateAvailable() { return _updateAvailable; }
public void run() {
try { Thread.sleep(_context.random().nextLong(5*60*1000)); } catch (InterruptedException ie) {}
while (true) {
if (!_updateAvailable) checkForUpdates();
if (shouldFetchNews())
fetchNews();
try { Thread.sleep(10*60*1000); } catch (InterruptedException ie) {}
}
}
private boolean shouldInstall() {
String policy = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_POLICY);
return ("install".equals(policy));
}
private boolean shouldFetchNews() {
String freq = _context.getProperty(ConfigUpdateHandler.PROP_REFRESH_FREQUENCY);
if (freq == null)
freq = ConfigUpdateHandler.DEFAULT_REFRESH_FREQUENCY;
try {
long ms = Long.parseLong(freq);
if (ms <= 0)
return false;
if (_lastFetch + ms < _context.clock().now()) {
return true;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Last fetched " + DataHelper.formatDuration(_context.clock().now() - _lastFetch) + " ago");
return false;
}
} catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Invalid refresh frequency: " + freq);
return false;
}
}
private void fetchNews() {
String newsURL = _context.getProperty(ConfigUpdateHandler.PROP_NEWS_URL, ConfigUpdateHandler.DEFAULT_NEWS_URL);
boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue();
String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST);
String port = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT);
int proxyPort = -1;
try {
proxyPort = Integer.parseInt(port);
EepGet get = null;
if (shouldProxy)
get = new EepGet(_context, proxyHost, proxyPort, 10, NEWS_FILE, newsURL);
else
get = new EepGet(_context, 10, NEWS_FILE, newsURL);
get.addStatusListener(this);
get.fetch();
} catch (Throwable t) {
_log.error("Error fetching the news", t);
}
_lastFetch = _context.clock().now();
}
private static final String VERSION_STRING = "version=\"" + RouterVersion.VERSION + "\"";
private static final String VERSION_PREFIX = "version=\"";
private void checkForUpdates() {
File news = new File(NEWS_FILE);
if (!news.exists()) return;
FileInputStream in = null;
try {
in = new FileInputStream(news);
StringBuffer buf = new StringBuffer(128);
while (DataHelper.readLine(in, buf)) {
int index = buf.indexOf(VERSION_PREFIX);
if (index == -1) {
// skip
} else {
int end = buf.indexOf("\"", index + VERSION_PREFIX.length());
if (end > index) {
String ver = buf.substring(index+VERSION_PREFIX.length(), end);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Found version: [" + ver + "]");
if (needsUpdate(ver)) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Our version is out of date, update!");
break;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Our version is current");
return;
}
}
}
if (buf.indexOf(VERSION_STRING) != -1) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Our version found, no need to update: " + buf.toString());
return;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("No match in " + buf.toString());
}
buf.setLength(0);
}
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error checking the news for an update", ioe);
return;
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
// could not find version="0.5.0.1", so there must be an update ;)
if (_log.shouldLog(Log.DEBUG))
_log.debug("Our version was NOT found (" + RouterVersion.VERSION + "), update needed");
_updateAvailable = true;
if (shouldInstall()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Policy requests update, so we update");
UpdateHandler handler = null;
if (_context instanceof RouterContext) {
handler = new UpdateHandler((RouterContext)_context);
} else {
List contexts = RouterContext.listContexts();
if (contexts.size() > 0)
handler = new UpdateHandler((RouterContext)contexts.get(0));
else
_log.log(Log.CRIT, "No router context to update with?");
}
if (handler != null)
handler.update();
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Policy requests manual update, so we do nothing");
}
}
private boolean needsUpdate(String version) {
StringTokenizer newTok = new StringTokenizer(sanitize(version), ".");
StringTokenizer ourTok = new StringTokenizer(sanitize(RouterVersion.VERSION), ".");
while (newTok.hasMoreTokens() && ourTok.hasMoreTokens()) {
String newVer = newTok.nextToken();
String oldVer = ourTok.nextToken();
switch (compare(newVer, oldVer)) {
case -1: // newVer is smaller
return false;
case 0: // eq
break;
case 1: // newVer is larger
return true;
}
}
if (newTok.hasMoreTokens() && !ourTok.hasMoreTokens())
return true;
return false;
}
private static final String VALID = "0123456789.";
private static final String sanitize(String str) {
StringBuffer buf = new StringBuffer(str);
for (int i = 0; i < buf.length(); i++) {
if (VALID.indexOf(buf.charAt(i)) == -1) {
buf.deleteCharAt(i);
i--;
}
}
return buf.toString();
}
private static final int compare(String lhs, String rhs) {
try {
int left = Integer.parseInt(lhs);
int right = Integer.parseInt(rhs);
if (left < right)
return -1;
else if (left == right)
return 0;
else
return 1;
} catch (NumberFormatException nfe) {
return 0;
}
}
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
// ignore
}
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
// ignore
}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
if (_log.shouldLog(Log.INFO))
_log.info("News fetched from " + url);
checkForUpdates();
}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
if (_log.shouldLog(Log.ERROR))
_log.error("Failed to fetch the news from " + url);
}
}

View File

@@ -4,8 +4,6 @@ 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;
import net.i2p.router.admin.StatsGenerator;

View File

@@ -8,14 +8,12 @@ import java.io.OutputStream;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Handler to deal with reseed requests. This reseed from the URL

View File

@@ -4,19 +4,18 @@ import java.io.File;
import java.io.IOException;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.router.RouterContext;
import net.i2p.apps.systray.SysTray;
import net.i2p.util.FileUtil;
import net.i2p.util.I2PThread;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.WebApplicationContext;
import org.mortbay.http.DigestAuthenticator;
import org.mortbay.http.handler.SecurityHandler;
import org.mortbay.http.HashUserRealm;
import org.mortbay.http.HttpRequest;
import org.mortbay.http.SecurityConstraint;
import org.mortbay.http.Authenticator;
import org.mortbay.util.MultiException;
public class RouterConsoleRunner {
private Server _server;
@@ -73,12 +72,16 @@ public class RouterConsoleRunner {
} catch (Throwable t) {
t.printStackTrace();
}
I2PThread t = new I2PThread(new NewsFetcher(I2PAppContext.getGlobalContext()), "NewsFetcher");
t.setDaemon(true);
t.start();
}
private void initialize(WebApplicationContext context) {
String password = getPassword();
if (password != null) {
HashUserRealm realm = new HashUserRealm();
HashUserRealm realm = new HashUserRealm("i2prouter");
realm.put("admin", password);
realm.addUserToRole("admin", "routerAdmin");
context.setRealm(realm);

View File

@@ -3,7 +3,6 @@ package net.i2p.router.web;
import java.util.Iterator;
import java.util.Set;
import java.io.ByteArrayOutputStream;
import java.io.Writer;
import net.i2p.data.Hash;

View File

@@ -1,11 +1,7 @@
package net.i2p.router.web;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Locale;
@@ -88,7 +84,7 @@ public class SummaryHelper {
if (ms < 60 * 1000) {
return now + " (" + (ms / 1000) + "s)";
} else if (ms < 60 * 1000) {
} else if (ms < 60 * 60 * 1000) {
return now + " (" + (ms / (60 * 1000)) + "m)";
} else if (ms < 24 * 60 * 60 * 1000) {
return now + " (" + (ms / (60 * 60 * 1000)) + "h)";
@@ -100,7 +96,7 @@ public class SummaryHelper {
public boolean allowReseed() {
return (_context.netDb().getKnownRouters() < 10);
}
/**
* Retrieve amount of used memory.
*
@@ -471,4 +467,8 @@ public class SummaryHelper {
return _context.throttle().getTunnelLag() + "ms";
}
public boolean updateAvailable() {
return NewsFetcher.getInstance().updateAvailable();
}
}

View File

@@ -0,0 +1,168 @@
package net.i2p.router.web;
import java.io.File;
import java.text.DecimalFormat;
import net.i2p.crypto.TrustedUpdate;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.util.I2PThread;
import net.i2p.util.EepGet;
import net.i2p.util.Log;
/**
* Handle the request to update the router by firing off an EepGet call and
* displaying its status to anyone who asks. After the download completes,
* it is verified with the TrustedUpdate, and if it is authentic, the router
* is restarted.
*
*/
public class UpdateHandler {
private static UpdateRunner _updateRunner;
private RouterContext _context;
private Log _log;
private DecimalFormat _pct = new DecimalFormat("00.0%");
private static final String SIGNED_UPDATE_FILE = "i2pupdate.sud";
public UpdateHandler() {}
public UpdateHandler(RouterContext ctx) {
_context = ctx;
_log = ctx.logManager().getLog(UpdateHandler.class);
}
/**
* 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);
_log = _context.logManager().getLog(UpdateHandler.class);
} catch (Throwable t) {
t.printStackTrace();
}
}
public void setUpdateNonce(String nonce) {
if (nonce == null) return;
if (nonce.equals(System.getProperty("net.i2p.router.web.UpdateHandler.nonce")) ||
nonce.equals(System.getProperty("net.i2p.router.web.UpdateHandler.noncePrev"))) {
update();
}
}
public void update() {
synchronized (UpdateHandler.class) {
if (_updateRunner == null)
_updateRunner = new UpdateRunner();
if (_updateRunner.isRunning()) {
return;
} else {
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "true");
I2PThread update = new I2PThread(_updateRunner, "Update");
update.start();
}
}
}
public String getStatus() {
return _updateRunner.getStatus();
}
public class UpdateRunner implements Runnable, EepGet.StatusListener {
private boolean _isRunning;
private String _status;
private long _startedOn;
private long _written;
public UpdateRunner() {
_isRunning = false;
_status = "<b>Updating</b><br />";
}
public boolean isRunning() { return _isRunning; }
public String getStatus() { return _status; }
public void run() {
_isRunning = true;
update();
System.setProperty("net.i2p.router.web.ReseedHandler.updateInProgress", "false");
_isRunning = false;
}
private void update() {
_startedOn = -1;
_status = "<b>Updating</b><br />";
String updateURL = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_URL, ConfigUpdateHandler.DEFAULT_UPDATE_URL);
boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue();
String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST);
String port = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT);
int proxyPort = -1;
try {
proxyPort = Integer.parseInt(port);
} catch (NumberFormatException nfe) {
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false");
return;
}
try {
EepGet get = null;
if (shouldProxy)
get = new EepGet(_context, proxyHost, proxyPort, 10, SIGNED_UPDATE_FILE, updateURL);
else
get = new EepGet(_context, 10, SIGNED_UPDATE_FILE, updateURL);
get.addStatusListener(UpdateRunner.this);
_startedOn = _context.clock().now();
get.fetch();
} catch (Throwable t) {
_context.logManager().getLog(UpdateHandler.class).error("Error updating", t);
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false");
}
}
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Attempt failed on " + url, cause);
_written = 0;
// ignored
}
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
_written += currentWrite;
StringBuffer buf = new StringBuffer(64);
buf.append("<b>Updating</b> ");
double pct = ((double)alreadyTransferred + (double)_written) / ((double)alreadyTransferred + (double)bytesRemaining);
synchronized (_pct) {
buf.append(_pct.format(pct));
}
buf.append(":<br />\n").append(_written+alreadyTransferred);
buf.append(" transferred<br />");
_status = buf.toString();
}
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile) {
_status = "<b>Update downloaded</b><br />";
TrustedUpdate up = new TrustedUpdate(_context);
boolean ok = up.migrateVerified(SIGNED_UPDATE_FILE, "i2pupdate.zip");
File f = new File(SIGNED_UPDATE_FILE);
f.delete();
if (ok) {
_log.log(Log.CRIT, "Update was VERIFIED, restarting to install it");
_status = "<b>Update verified</b><br />Restarting<br />";
restart();
} else {
_log.log(Log.CRIT, "Update was INVALID - have you changed your keys?");
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false");
}
}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
_log.log(Log.CRIT, "Update did not download completely (" + bytesTransferred + " with "
+ bytesRemaining + " after " + currentAttempt + " tries)");
_status = "<b>Transfer failed</b><br />";
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false");
}
}
private void restart() {
_context.router().addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_GRACEFUL_RESTART));
_context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART);
}
}

View File

@@ -2,9 +2,11 @@
%>Network | <% } else { %><a href="config.jsp">Network</a> | <% }
if (request.getRequestURI().indexOf("configservice.jsp") != -1) {
%>Service | <% } else { %><a href="configservice.jsp">Service</a> | <% }
if (request.getRequestURI().indexOf("configupdate.jsp") != -1) {
%>Update | <% } else { %><a href="configupdate.jsp">Update</a> | <% }
if (request.getRequestURI().indexOf("configtunnels.jsp") != -1) {
%>Tunnels | <% } else { %><a href="configtunnels.jsp">Tunnels</a> | <% }
if (request.getRequestURI().indexOf("configlogging.jsp") != -1) {
%>Logging | <% } else { %><a href="configlogging.jsp">Logging</a> | <% }
if (request.getRequestURI().indexOf("configadvanced.jsp") != -1) {
%>Advanced | <% } else { %><a href="configadvanced.jsp">Advanced</a> | <% } %></h4>
%>Advanced<% } else { %><a href="configadvanced.jsp">Advanced</a><% } %></h4>

View File

@@ -0,0 +1,51 @@
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<title>I2P Router Console - config update</title>
<link rel="stylesheet" href="default.css" type="text/css" />
</head><body>
<%@include file="nav.jsp" %>
<%@include file="summary.jsp" %>
<div class="main" id="main">
<%@include file="confignav.jsp" %>
<jsp:useBean class="net.i2p.router.web.ConfigUpdateHandler" id="formhandler" scope="request" />
<jsp:setProperty name="formhandler" property="*" />
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<font color="red"><jsp:getProperty name="formhandler" property="errors" /></font>
<i><jsp:getProperty name="formhandler" property="notices" /></i>
<jsp:useBean class="net.i2p.router.web.ConfigUpdateHelper" id="updatehelper" scope="request" />
<jsp:setProperty name="updatehelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<form action="configupdate.jsp" method="POST">
<% String prev = System.getProperty("net.i2p.router.web.ConfigUpdateHandler.nonce");
if (prev != null) System.setProperty("net.i2p.router.web.ConfigUpdateHandler.noncePrev", prev);
System.setProperty("net.i2p.router.web.ConfigUpdateHandler.nonce", new java.util.Random().nextLong()+""); %>
<input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigUpdateHandler.nonce")%>" />
<input type="hidden" name="action" value="update" />
News URL:
<input type="text" size="60" name="newsURL" value="<jsp:getProperty name="updatehelper" property="newsURL" />"><br />
Refresh frequency:
<jsp:getProperty name="updatehelper" property="refreshFrequencySelectBox" /><br />
Update URL:
<input type="text" size="60" name="updateURL" value="<jsp:getProperty name="updatehelper" property="updateURL" />"><br />
Update policy:
<jsp:getProperty name="updatehelper" property="updatePolicySelectBox" /><br />
Update anonymously?
<jsp:getProperty name="updatehelper" property="updateThroughProxy" /><br />
Proxy host: <input type="text" size="10" name="proxyHost" value="<jsp:getProperty name="updatehelper" property="proxyHost" />" /><br />
Proxy port: <input type="text" size="4" name="proxyPort" value="<jsp:getProperty name="updatehelper" property="proxyPort" />" /><br />
<!-- prompt for the eepproxy -->
Trusted keys:
<textarea name="trustedKeys" disabled="true" cols="60" rows="2"><jsp:getProperty name="updatehelper" property="trustedKeys" /></textarea>
<input type="submit" value="Save" />
</form>
</div>
</body>
</html>

View File

@@ -60,3 +60,12 @@ div.main {
text-align: left;
color: inherit;
}
div.news {
margin: 0em 1em 1em 224px;
padding: .5em 1em;
background-color: #ffffc0;
border: medium solid #ffffd0;
text-align: left;
color: inherit;
}

View File

@@ -10,6 +10,13 @@
<%@include file="nav.jsp" %>
<%@include file="summary.jsp" %>
<div class="news" id="news">
<jsp:useBean class="net.i2p.router.web.ContentHelper" id="newshelper" scope="request" />
<jsp:setProperty name="newshelper" property="page" value="docs/news.xml" />
<jsp:setProperty name="newshelper" property="maxLines" value="300" />
<jsp:getProperty name="newshelper" property="content" />
</div>
<div class="main" id="main">
<jsp:useBean class="net.i2p.router.web.ContentHelper" id="contenthelper" scope="request" />
<jsp:setProperty name="contenthelper" property="page" value="docs/readme.html" />

View File

@@ -4,6 +4,9 @@
<jsp:useBean class="net.i2p.router.web.ReseedHandler" id="reseed" scope="request" />
<jsp:setProperty name="reseed" property="*" />
<jsp:useBean class="net.i2p.router.web.UpdateHandler" id="update" scope="request" />
<jsp:setProperty name="update" property="*" />
<jsp:setProperty name="update" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<div class="routersummary">
<u><b>General</b></u><br />
@@ -11,8 +14,24 @@
<b>Version:</b> <jsp:getProperty name="helper" property="version" /><br />
<b>Uptime:</b> <jsp:getProperty name="helper" property="uptime" /><br />
<b>Now:</b> <jsp:getProperty name="helper" property="time" /><br />
<b>Memory:</b> <jsp:getProperty name="helper" property="memory" /><br />
<hr />
<b>Memory:</b> <jsp:getProperty name="helper" property="memory" /><br /><%
if (helper.updateAvailable()) {
if ("true".equals(System.getProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false"))) {
out.print(update.getStatus());
} else {
long nonce = new java.util.Random().nextLong();
String prev = System.getProperty("net.i2p.router.web.UpdateHandler.nonce");
if (prev != null) System.setProperty("net.i2p.router.web.UpdateHandler.noncePrev", prev);
System.setProperty("net.i2p.router.web.UpdateHandler.nonce", nonce+"");
String uri = request.getRequestURI();
if (uri.indexOf('?') > 0)
uri = uri + "&updateNonce=" + nonce;
else
uri = uri + "?updateNonce=" + nonce;
out.print(" <a href=\"" + uri + "\">Update available</a>");
}
}
%><hr />
<u><b>Peers</b></u><br />
<b>Active:</b> <jsp:getProperty name="helper" property="activePeers" />/<jsp:getProperty name="helper" property="activeProfiles" /><br />

View File

@@ -0,0 +1,12 @@
<%@page contentType="text/html" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<title>I2P Router Console - verify update file signature</title>
</head>
<body>
<!-- net.i2p.crypto.TrustedUpdate.verify(request.getParameter("filename")) -->
</body>
</html>

View File

@@ -17,7 +17,6 @@ import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -221,7 +220,7 @@ public class SAMBridge implements Runnable {
SAMBridge bridge = new SAMBridge(host, port, opts, keyfile);
I2PThread t = new I2PThread(bridge, "SAMListener");
if (Boolean.valueOf(System.getProperty("sam.shutdownOnOOM", "false")).booleanValue()) {
t.addOOMEventListener(new I2PThread.OOMEventListener() {
I2PThread.addOOMEventListener(new I2PThread.OOMEventListener() {
public void outOfMemory(OutOfMemoryError err) {
err.printStackTrace();
System.err.println("OOMed, die die die");

View File

@@ -8,9 +8,7 @@ package net.i2p.sam;
*
*/
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;

View File

@@ -341,7 +341,7 @@ public class SAMStreamSession {
}
/**
* Remove and close a SAM STREAM session socket handler.
* Remove and gracefully close a SAM STREAM session socket handler.
*
* @param id Handler id to be removed
*/
@@ -357,12 +357,12 @@ public class SAMStreamSession {
if (reader != null)
reader.stopRunning();
if (sender != null)
sender.stopRunning();
_log.debug("Removed SAM STREAM session socket handler " + id);
sender.shutDownGracefully();
_log.debug("Removed SAM STREAM session socket handler (gracefully) " + id);
}
/**
* Remove and close all the socket handlers managed by this SAM
* Remove and hard close all the socket handlers managed by this SAM
* STREAM session.
*
*/
@@ -378,7 +378,7 @@ public class SAMStreamSession {
while (iter.hasNext()) {
id = (Integer)iter.next();
((SAMStreamSessionSocketReader)handlersMap.get(id)).stopRunning();
((StreamSender)sendersMap.get(id)).stopRunning();
((StreamSender)sendersMap.get(id)).shutDownGracefully();
}
handlersMap.clear();
sendersMap.clear();
@@ -498,25 +498,20 @@ public class SAMStreamSession {
}
/**
* Stop a SAM STREAM session socket reader
* Stop a SAM STREAM session socket reader thead immediately.
*
*/
public void stopRunning() {
_log.debug("stopRunning() invoked on socket handler " + id);
_log.debug("stopRunning() invoked on socket reader " + id);
synchronized (runningLock) {
if (stillRunning) {
stillRunning = false;
try {
i2pSocket.close();
} catch (IOException e) {
_log.debug("Caught IOException", e);
}
}
}
}
public void run() {
_log.debug("SAM STREAM session socket handler running");
_log.debug("run() called for socket reader " + id);
int read = -1;
byte[] data = new byte[SOCKET_HANDLER_BUF_SIZE];
@@ -568,7 +563,9 @@ public class SAMStreamSession {
private int _id;
private ByteCache _cache;
private OutputStream _out = null;
private boolean _stillRunning;
private boolean _stillRunning, _shuttingDownGracefully;
private Object runningLock = new Object();
private I2PSocket i2pSocket = null;
public StreamSender(I2PSocket s, int id) throws IOException {
_data = new ArrayList(1);
@@ -576,6 +573,8 @@ public class SAMStreamSession {
_cache = ByteCache.getInstance(4, 32*1024);
_out = s.getOutputStream();
_stillRunning = true;
_shuttingDownGracefully = false;
i2pSocket = s;
}
/**
@@ -602,28 +601,54 @@ public class SAMStreamSession {
}
/**
* Stop a SAM STREAM session socket sender
* Stop a SAM STREAM session socket sender thread immediately
*
*/
public void stopRunning() {
_log.debug("stopRunning() invoked on socket sender " + _id);
_stillRunning = false;
synchronized (_data) {
_data.clear();
_data.notifyAll();
synchronized (runningLock) {
if (_stillRunning) {
_stillRunning = false;
try {
i2pSocket.close();
} catch (IOException e) {
_log.debug("Caught IOException", e);
}
synchronized (_data) {
_data.clear();
_data.notifyAll();
}
}
}
}
/**
* Stop a SAM STREAM session socket sender gracefully: stop the
* sender thread once all pending data has been sent.
*/
public void shutDownGracefully() {
_log.debug("shutDownGracefully() invoked on socket sender " + _id);
_shuttingDownGracefully = true;
}
public void run() {
_log.debug("run() called for socket sender " + _id);
ByteArray data = null;
while (_stillRunning) {
data = null;
try {
synchronized (_data) {
if (_data.size() > 0)
if (_data.size() > 0) {
data = (ByteArray)_data.remove(0);
else
} else if (_shuttingDownGracefully) {
/* No data left and shutting down gracefully?
If so, stop the sender. */
stopRunning();
break;
} else {
/* Wait for data. */
_data.wait(5000);
}
}
if (data != null) {

View File

@@ -15,7 +15,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.net.Socket;

View File

@@ -2,7 +2,6 @@ package net.i2p.sam.client;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Properties;

View File

@@ -1,27 +1,19 @@
package net.i2p.sam.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Map;
import java.util.Collections;
import java.util.HashMap;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
import net.i2p.util.I2PThread;
import net.i2p.sam.client.SAMEventHandler;
import net.i2p.sam.client.SAMClientEventListenerImpl;
import net.i2p.sam.client.SAMReader;
/**

View File

@@ -1,27 +1,17 @@
package net.i2p.sam.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.FileOutputStream;
import java.io.File;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Map;
import java.util.Collections;
import java.util.HashMap;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
import net.i2p.util.I2PThread;
import net.i2p.sam.client.SAMEventHandler;
import net.i2p.sam.client.SAMClientEventListenerImpl;
import net.i2p.sam.client.SAMReader;
/**

View File

@@ -30,14 +30,32 @@ public class TestStreamTransfer {
private static Log _log = new Log(TestStreamTransfer.class);
private static String _alice = null;
private static boolean _dead = false;
private static Object _counterLock = new Object();
private static int _recvCounter = 0, _closeCounter = 0;
private static void runTest(String samHost, int samPort, String conOptions) {
int nTests = 20;
startAlice(samHost, samPort, conOptions);
for (int i = 0; i < 20; i++) {
/* Start up nTests different test threads. */
for (int i = 0; i < nTests; i++) {
testBob("bob" + i, samHost, samPort, conOptions);
if (i % 2 == 1)
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
/* Wait until the correct number of messages have been received
by Alices and the correct number of streams have been closed
by Bobs. */
while (true) {
synchronized (_counterLock) {
if (_recvCounter == nTests * 2 && _closeCounter == nTests) {
break;
}
}
try { Thread.sleep(1000); } catch (InterruptedException ie) {}
_log.info("Receive counter is: " + _recvCounter + " Close counter is: " + _closeCounter);
}
/* Return, assuming the test has passed. */
_log.info("Unit test passed.");
}
private static void startAlice(String host, int port, String conOptions) {
@@ -151,6 +169,9 @@ public class TestStreamTransfer {
return;
}
_log.info("\n== Received from the stream " + id + ": [" + new String(payload) + "]");
synchronized (_counterLock) {
_recvCounter++;
}
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
/*
// now echo it back
@@ -225,11 +246,15 @@ public class TestStreamTransfer {
_log.info("\n** Sending FooBarBaz!");
out.write(req.getBytes());
out.flush();
try { Thread.sleep(20*1000); } catch (InterruptedException ie) {}
/* Don't delay here, so we can test whether all data is
sent even if we do a STREAM CLOSE immediately. */
_log.info("Sending close");
req = "STREAM CLOSE ID=42\n";
out.write(req.getBytes());
out.flush();
synchronized (_counterLock) {
_closeCounter++;
}
try { Thread.sleep(30*1000); } catch (InterruptedException ie) {}
//_dead = true;
s.close();

View File

@@ -2,19 +2,15 @@ package net.i2p.client.streaming;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSession;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.SessionTag;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer;
@@ -75,8 +71,8 @@ public class Connection {
private long _lifetimeDupMessageSent;
private long _lifetimeDupMessageReceived;
public static final long MAX_RESEND_DELAY = 60*1000;
public static final long MIN_RESEND_DELAY = 20*1000;
public static final long MAX_RESEND_DELAY = 30*1000;
public static final long MIN_RESEND_DELAY = 10*1000;
/** wait up to 5 minutes after disconnection so we can ack/close packets */
public static int DISCONNECT_TIMEOUT = 5*60*1000;
@@ -188,7 +184,8 @@ public class Connection {
}
void ackImmediately() {
_receiver.send(null, 0, 0);
PacketLocal packet = _receiver.send(null, 0, 0);
//packet.releasePayload();
}
/**
@@ -261,7 +258,7 @@ public class Connection {
}
packet.setFlag(Packet.FLAG_DELAY_REQUESTED);
long timeout = (_options.getRTT() < MIN_RESEND_DELAY ? MIN_RESEND_DELAY : _options.getRTT());
long timeout = _options.getRTT() + MIN_RESEND_DELAY;
if (timeout > MAX_RESEND_DELAY)
timeout = MAX_RESEND_DELAY;
if (_log.shouldLog(Log.DEBUG))
@@ -587,6 +584,7 @@ public class Connection {
}
}
/** how many packets have we sent and the other side has ACKed? */
public long getAckedPackets() { return _ackedPackets; }
public long getCreatedOn() { return _createdOn; }
public long getCloseSentOn() { return _closeSentOn; }
@@ -602,6 +600,7 @@ public class Connection {
public void incrementUnackedPacketsReceived() { _unackedPacketsReceived++; }
public int getUnackedPacketsReceived() { return _unackedPacketsReceived; }
/** how many packets have we sent but not yet received an ACK for? */
public int getUnackedPacketsSent() {
synchronized (_outboundPackets) {
return _outboundPackets.size();
@@ -870,6 +869,8 @@ public class Connection {
_log.warn("Congestion resending packet " + _packet.getSequenceNum() + ": new windowSize " + newWindowSize
+ ") for " + Connection.this.toString());
// setRTT has its own ceiling
getOptions().setRTT(getOptions().getRTT() + 10*1000);
getOptions().setWindowSize(newWindowSize);
windowAdjusted();
}
@@ -891,13 +892,15 @@ public class Connection {
_context.sessionKeyManager().failTags(_remotePeer.getPublicKey());
}
if (_log.shouldLog(Log.WARN))
_log.warn("Resend packet " + _packet + " time " + numSends +
" activeResends: " + _activeResends +
" (wsize "
+ newWindowSize + " lifetime "
+ (_context.clock().now() - _packet.getCreatedOn()) + "ms)");
_outboundQueue.enqueue(_packet);
if (numSends - 1 <= _options.getMaxResends()) {
if (_log.shouldLog(Log.WARN))
_log.warn("Resend packet " + _packet + " time " + numSends +
" activeResends: " + _activeResends +
" (wsize "
+ newWindowSize + " lifetime "
+ (_context.clock().now() - _packet.getCreatedOn()) + "ms)");
_outboundQueue.enqueue(_packet);
}
_lastSendTime = _context.clock().now();
@@ -910,7 +913,7 @@ public class Connection {
return;
}
if (numSends > _options.getMaxResends()) {
if (numSends - 1 > _options.getMaxResends()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Too many resends");
_packet.cancelled();

View File

@@ -1,8 +1,7 @@
package net.i2p.client.streaming;
import java.io.InterruptedIOException;
import java.io.IOException;
import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
import net.i2p.util.Log;
/**
@@ -131,13 +130,19 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
}
private PacketLocal buildPacket(Connection con, byte buf[], int off, int size, boolean forceIncrement) {
if (size > Packet.MAX_PAYLOAD_SIZE) throw new IllegalArgumentException("size is too large (" + size + ")");
boolean ackOnly = isAckOnly(con, size);
boolean isFirst = (con.getAckedPackets() <= 0) && (con.getUnackedPacketsSent() <= 0);
PacketLocal packet = new PacketLocal(_context, con.getRemotePeer(), con);
byte data[] = new byte[size];
//ByteArray data = packet.acquirePayload();
ByteArray data = new ByteArray(new byte[size]);
if (size > 0)
System.arraycopy(buf, off, data, 0, size);
System.arraycopy(buf, off, data.getData(), 0, size);
data.setValid(size);
data.setOffset(0);
packet.setPayload(data);
if (ackOnly && !forceIncrement)
if ( (ackOnly && !forceIncrement) && (!isFirst) )
packet.setSequenceNum(0);
else
packet.setSequenceNum(con.getNextOutboundPacketNum());
@@ -155,7 +160,8 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
packet.setFlag(Packet.FLAG_SIGNATURE_REQUESTED, con.getOptions().getRequireFullySigned());
if ( (!ackOnly) && (packet.getSequenceNum() <= 0) ) {
//if ( (!ackOnly) && (packet.getSequenceNum() <= 0) ) {
if (isFirst) {
packet.setFlag(Packet.FLAG_SYNCHRONIZE);
packet.setOptionalFrom(con.getSession().getMyDestination());
packet.setOptionalMaxSize(con.getOptions().getMaxMessageSize());

View File

@@ -328,6 +328,7 @@ public class ConnectionManager {
}
_outboundQueue.enqueue(packet);
packet.releasePayload();
if (blocking) {
synchronized (req) {

View File

@@ -81,7 +81,7 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
super.init(opts);
setConnectDelay(getInt(opts, PROP_CONNECT_DELAY, -1));
setProfile(getInt(opts, PROP_PROFILE, PROFILE_BULK));
setMaxMessageSize(getInt(opts, PROP_MAX_MESSAGE_SIZE, 16*1024));
setMaxMessageSize(getInt(opts, PROP_MAX_MESSAGE_SIZE, 4*1024));
setRTT(getInt(opts, PROP_INITIAL_RTT, 30*1000));
setReceiveWindow(getInt(opts, PROP_INITIAL_RECEIVE_WINDOW, 1));
setResendDelay(getInt(opts, PROP_INITIAL_RESEND_DELAY, 1000));

View File

@@ -34,6 +34,7 @@ public class ConnectionPacketHandler {
if (!ok) {
if ( (!packet.isFlagSet(Packet.FLAG_RESET)) && (_log.shouldLog(Log.ERROR)) )
_log.error("Packet does NOT verify: " + packet);
packet.releasePayload();
return;
}
@@ -47,6 +48,7 @@ public class ConnectionPacketHandler {
if (_log.shouldLog(Log.WARN))
_log.warn("Received a packet after hard disconnect, ignoring: " + packet + " on " + con);
}
packet.releasePayload();
return;
}
@@ -72,6 +74,7 @@ public class ConnectionPacketHandler {
+ ": dropping " + packet);
ack(con, packet.getAckThrough(), packet.getNacks(), null, false);
con.getOptions().setChoke(5*1000);
packet.releasePayload();
return;
}
con.getOptions().setChoke(0);
@@ -91,6 +94,7 @@ public class ConnectionPacketHandler {
con.closeReceived();
boolean fastAck = false;
boolean ackOnly = false;
if (isNew) {
con.incrementUnackedPacketsReceived();
@@ -127,11 +131,19 @@ public class ConnectionPacketHandler {
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("ACK only packet received: " + packet);
ackOnly = true;
}
}
}
fastAck = fastAck || ack(con, packet.getAckThrough(), packet.getNacks(), packet, isNew);
if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE) &&
((packet.getSendStreamId() == null) ||
DataHelper.eq(packet.getSendStreamId(), Packet.STREAM_ID_UNKNOWN) ) ) {
// don't honor the ACK 0 in SYN packets received when the other side
// has obviously not seen our messages
} else {
fastAck = fastAck || ack(con, packet.getAckThrough(), packet.getNacks(), packet, isNew);
}
con.eventOccurred();
if (fastAck) {
if (con.getLastSendTime() + 2000 < _context.clock().now()) {
@@ -140,6 +152,11 @@ public class ConnectionPacketHandler {
con.ackImmediately();
}
}
if (ackOnly || !isNew) {
// non-ack message payloads are queued in the MessageInputStream
packet.releasePayload();
}
}
private boolean ack(Connection con, long ackThrough, long nacks[], Packet packet, boolean isNew) {
@@ -199,6 +216,8 @@ public class ConnectionPacketHandler {
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
+ ") for " + con);
// setRTT has its own ceiling
con.getOptions().setRTT(con.getOptions().getRTT() + 10*1000);
con.getOptions().setWindowSize(oldSize);
congested = true;

View File

@@ -1,6 +1,5 @@
package net.i2p.client.streaming;
import java.net.ConnectException;
import net.i2p.I2PException;
/**

View File

@@ -1,15 +1,8 @@
package net.i2p.client.streaming;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
@@ -17,7 +10,6 @@ import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.I2PSessionListener;
import net.i2p.data.Destination;
import net.i2p.util.Log;

View File

@@ -2,8 +2,6 @@ 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;
@@ -13,6 +11,7 @@ import java.util.Map;
import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
import net.i2p.util.ByteCache;
import net.i2p.util.Log;
/**
@@ -52,6 +51,7 @@ public class MessageInputStream extends InputStream {
private int _readTimeout;
private IOException _streamError;
private long _readTotal;
private ByteCache _cache;
private byte[] _oneByte = new byte[1];
@@ -70,6 +70,7 @@ public class MessageInputStream extends InputStream {
_dataLock = new Object();
_closeReceived = false;
_locallyClosed = false;
_cache = ByteCache.getInstance(128, Packet.MAX_PAYLOAD_SIZE);
}
/** What is the highest block ID we've completely received through? */
@@ -166,7 +167,7 @@ public class MessageInputStream extends InputStream {
buf.append("Close received, ready bytes: ");
long available = 0;
for (int i = 0; i < _readyDataBlocks.size(); i++)
available += ((ByteArray)_readyDataBlocks.get(i)).getData().length;
available += ((ByteArray)_readyDataBlocks.get(i)).getValid();
available -= _readyDataBlockIndex;
buf.append(available);
buf.append(" blocks: ").append(_readyDataBlocks.size());
@@ -178,8 +179,8 @@ public class MessageInputStream extends InputStream {
ByteArray ba = (ByteArray)_notYetReadyBlocks.get(id);
buf.append(id).append(" ");
if (ba.getData() != null)
notAvailable += ba.getData().length;
if (ba != null)
notAvailable += ba.getValid();
}
buf.append("not ready bytes: ").append(notAvailable);
@@ -198,10 +199,10 @@ public class MessageInputStream extends InputStream {
*
* @return true if this is a new packet, false if it is a dup
*/
public boolean messageReceived(long messageId, byte payload[]) {
public boolean messageReceived(long messageId, ByteArray payload) {
synchronized (_dataLock) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("received " + messageId + " with " + payload.length);
_log.debug("received " + messageId + " with " + payload.getValid());
if (messageId <= _highestReadyBlockId) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("ignoring dup message " + messageId);
@@ -212,17 +213,17 @@ public class MessageInputStream extends InputStream {
_highestBlockId = messageId;
if (_highestReadyBlockId + 1 == messageId) {
if (!_locallyClosed && payload.length > 0) {
if (!_locallyClosed && payload.getValid() > 0) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("accepting bytes as ready: " + payload.length);
_readyDataBlocks.add(new ByteArray(payload));
_log.debug("accepting bytes as ready: " + payload.getValid());
_readyDataBlocks.add(payload);
}
_highestReadyBlockId = messageId;
long cur = _highestReadyBlockId + 1;
// now pull in any previously pending blocks
while (_notYetReadyBlocks.containsKey(new Long(cur))) {
ByteArray ba = (ByteArray)_notYetReadyBlocks.remove(new Long(cur));
if ( (ba != null) && (ba.getData() != null) && (ba.getData().length > 0) ) {
if ( (ba != null) && (ba.getData() != null) && (ba.getValid() > 0) ) {
_readyDataBlocks.add(ba);
}
@@ -238,7 +239,7 @@ public class MessageInputStream extends InputStream {
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));
_notYetReadyBlocks.put(new Long(messageId), payload);
_dataLock.notifyAll();
}
}
@@ -324,21 +325,25 @@ public class MessageInputStream extends InputStream {
} else {
// either was already ready, or we wait()ed and it arrived
ByteArray cur = (ByteArray)_readyDataBlocks.get(0);
byte rv = cur.getData()[_readyDataBlockIndex];
byte rv = cur.getData()[cur.getOffset()+_readyDataBlockIndex];
_readyDataBlockIndex++;
if (cur.getData().length <= _readyDataBlockIndex) {
boolean removed = false;
if (cur.getValid() <= _readyDataBlockIndex) {
_readyDataBlockIndex = 0;
_readyDataBlocks.remove(0);
removed = true;
}
_readTotal++;
target[offset + i] = rv; // rv < 0 ? rv + 256 : rv
if ( (_readyDataBlockIndex <= 3) || (_readyDataBlockIndex >= cur.getData().length - 5) ) {
if ( (_readyDataBlockIndex <= 3) || (_readyDataBlockIndex >= cur.getValid() - 5) ) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("read(...," + offset+", " + length+ ")[" + i
+ "] after ready data: readyDataBlockIndex=" + _readyDataBlockIndex
+ " readyBlocks=" + _readyDataBlocks.size()
+ " readTotal=" + _readTotal);
}
//if (removed)
// _cache.release(cur);
}
} // for (int i = 0; i < length; i++) {
} // synchronized (_dataLock)
@@ -357,9 +362,9 @@ public class MessageInputStream extends InputStream {
for (int i = 0; i < _readyDataBlocks.size(); i++) {
ByteArray cur = (ByteArray)_readyDataBlocks.get(i);
if (i == 0)
numBytes += cur.getData().length - _readyDataBlockIndex;
numBytes += cur.getValid() - _readyDataBlockIndex;
else
numBytes += cur.getData().length;
numBytes += cur.getValid();
}
}
if (_log.shouldLog(Log.DEBUG))
@@ -380,13 +385,13 @@ public class MessageInputStream extends InputStream {
for (int i = 0; i < _readyDataBlocks.size(); i++) {
ByteArray cur = (ByteArray)_readyDataBlocks.get(i);
if (i == 0)
numBytes += cur.getData().length - _readyDataBlockIndex;
numBytes += cur.getValid() - _readyDataBlockIndex;
else
numBytes += cur.getData().length;
numBytes += cur.getValid();
}
for (Iterator iter = _notYetReadyBlocks.values().iterator(); iter.hasNext(); ) {
ByteArray cur = (ByteArray)iter.next();
numBytes += cur.getData().length;
numBytes += cur.getValid();
}
return numBytes;
}
@@ -399,9 +404,9 @@ public class MessageInputStream extends InputStream {
for (int i = 0; i < _readyDataBlocks.size(); i++) {
ByteArray cur = (ByteArray)_readyDataBlocks.get(i);
if (i == 0)
numBytes += cur.getData().length - _readyDataBlockIndex;
numBytes += cur.getValid() - _readyDataBlockIndex;
else
numBytes += cur.getData().length;
numBytes += cur.getValid();
}
return numBytes;
}
@@ -409,6 +414,8 @@ public class MessageInputStream extends InputStream {
public void close() {
synchronized (_dataLock) {
//while (_readyDataBlocks.size() > 0)
// _cache.release((ByteArray)_readyDataBlocks.remove(0));
_readyDataBlocks.clear();
// we don't need the data, but we do need to keep track of the messageIds
@@ -416,6 +423,7 @@ public class MessageInputStream extends InputStream {
for (Iterator iter = _notYetReadyBlocks.values().iterator(); iter.hasNext(); ) {
ByteArray ba = (ByteArray)iter.next();
ba.setData(null);
//_cache.release(ba);
}
_locallyClosed = true;
_dataLock.notifyAll();

View File

@@ -3,11 +3,13 @@ package net.i2p.client.streaming;
import java.util.Arrays;
import net.i2p.I2PAppContext;
import net.i2p.data.Base64;
import net.i2p.data.ByteArray;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.Signature;
import net.i2p.data.SigningPrivateKey;
import net.i2p.util.ByteCache;
/**
* Contain a single packet transferred as part of a streaming connection.
@@ -56,12 +58,13 @@ public class Packet {
private long _nacks[];
private int _resendDelay;
private int _flags;
private byte _payload[];
private ByteArray _payload;
// the next four are set only if the flags say so
private Signature _optionSignature;
private Destination _optionFrom;
private int _optionDelay;
private int _optionMaxSize;
private ByteCache _cache;
/**
* The receiveStreamId will be set to this when the packet doesn't know
@@ -135,6 +138,10 @@ public class Packet {
public static final int DEFAULT_MAX_SIZE = 32*1024;
private static final int MAX_DELAY_REQUEST = 65535;
public Packet() {
_cache = ByteCache.getInstance(128, MAX_PAYLOAD_SIZE);
}
/** what stream is this packet a part of? */
public byte[] getSendStreamId() {
@@ -200,14 +207,28 @@ public class Packet {
public static final int MAX_PAYLOAD_SIZE = 32*1024;
/** get the actual payload of the message. may be null */
public byte[] getPayload() { return _payload; }
public void setPayload(byte payload[]) {
public ByteArray getPayload() { return _payload; }
public void setPayload(ByteArray payload) {
//if ( (_payload != null) && (_payload != payload) )
// _cache.release(_payload);
_payload = payload;
if ( (payload != null) && (payload.length > MAX_PAYLOAD_SIZE) )
throw new IllegalArgumentException("Too large payload: " + payload.length);
if ( (payload != null) && (payload.getValid() > MAX_PAYLOAD_SIZE) )
throw new IllegalArgumentException("Too large payload: " + payload.getValid());
}
public int getPayloadSize() {
return (_payload == null ? 0 : _payload.length);
return (_payload == null ? 0 : _payload.getValid());
}
public void releasePayload() {
//if (_payload != null)
// _cache.release(_payload);
_payload = null;
}
public ByteArray acquirePayload() {
ByteArray old = _payload;
_payload = new ByteArray(new byte[Packet.MAX_PAYLOAD_SIZE]); //_cache.acquire();
//if (old != null)
// _cache.release(old);
return _payload;
}
/** is a particular flag set on this packet? */
@@ -340,12 +361,12 @@ public class Packet {
if (_payload != null) {
try {
System.arraycopy(_payload, 0, buffer, cur, _payload.length);
System.arraycopy(_payload.getData(), _payload.getOffset(), buffer, cur, _payload.getValid());
} catch (ArrayIndexOutOfBoundsException aioobe) {
System.err.println("payload.length: " + _payload.length + " buffer.length: " + buffer.length + " cur: " + cur);
System.err.println("payload.length: " + _payload.getValid() + " buffer.length: " + buffer.length + " cur: " + cur);
throw aioobe;
}
cur += _payload.length;
cur += _payload.getValid();
}
return cur - offset;
@@ -382,7 +403,7 @@ public class Packet {
size += 2; // option size
if (_payload != null) {
size += _payload.length;
size += _payload.getValid();
}
return size;
@@ -445,8 +466,10 @@ public class Packet {
throw new IllegalArgumentException("length: " + length + " offset: " + offset + " begin: " + payloadBegin);
// skip ahead to the payload
_payload = new byte[payloadSize];
System.arraycopy(buffer, payloadBegin, _payload, 0, payloadSize);
_payload = new ByteArray(new byte[payloadSize]); //_cache.acquire();
System.arraycopy(buffer, payloadBegin, _payload.getData(), 0, payloadSize);
_payload.setValid(payloadSize);
_payload.setOffset(0);
// ok now lets go back and deal with the options
if (isFlagSet(FLAG_DELAY_REQUESTED)) {
@@ -529,6 +552,11 @@ public class Packet {
}
public String toString() {
StringBuffer str = formatAsString();
return str.toString();
}
protected StringBuffer formatAsString() {
StringBuffer buf = new StringBuffer(64);
buf.append(toId(_sendStreamId));
//buf.append("<-->");
@@ -545,9 +573,9 @@ public class Packet {
buf.append(" ").append(_nacks[i]);
}
}
if ( (_payload != null) && (_payload.length > 0) )
buf.append(" data: ").append(_payload.length);
return buf.toString();
if ( (_payload != null) && (_payload.getValid() > 0) )
buf.append(" data: ").append(_payload.getValid());
return buf;
}
private static final String toId(byte id[]) {

View File

@@ -106,12 +106,16 @@ public class PacketHandler {
private static final SimpleDateFormat _fmt = new SimpleDateFormat("HH:mm:ss.SSS");
void displayPacket(Packet packet, String prefix, String suffix) {
String msg = null;
if (!_log.shouldLog(Log.DEBUG)) return;
StringBuffer buf = new StringBuffer(256);
synchronized (_fmt) {
msg = _fmt.format(new Date()) + ": " + prefix + " " + packet.toString() + (suffix != null ? " " + suffix : "");
buf.append(_fmt.format(new Date()));
}
if (_log.shouldLog(Log.DEBUG))
System.out.println(msg);
buf.append(": ").append(prefix).append(" ");
buf.append(packet.toString());
if (suffix != null)
buf.append(" ").append(suffix);
System.out.println(buf.toString());
}
private void receiveKnownCon(Connection con, Packet packet) {
@@ -155,12 +159,14 @@ public class PacketHandler {
if (_log.shouldLog(Log.WARN))
_log.warn("Receive a syn packet with the wrong IDs, sending reset: " + packet);
sendReset(packet);
packet.releasePayload();
} else {
if (!con.getResetSent()) {
// someone is sending us a packet on the wrong stream
if (_log.shouldLog(Log.WARN))
_log.warn("Received a packet on the wrong stream: " + packet + " connection: " + con);
}
packet.releasePayload();
}
}
}
@@ -187,6 +193,7 @@ public class PacketHandler {
if (_log.shouldLog(Log.WARN))
_log.warn("Echo packet received with no stream IDs: " + packet);
}
packet.releasePayload();
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Packet received on an unknown stream (and not an ECHO): " + packet);
@@ -221,6 +228,7 @@ public class PacketHandler {
+ buf.toString() + " sendId: "
+ (sendId != null ? Base64.encode(sendId) : " unknown"));
}
packet.releasePayload();
}
}
}

View File

@@ -5,6 +5,7 @@ import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.data.Destination;
import net.i2p.data.SessionKey;
import net.i2p.util.ByteCache;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer;
@@ -26,6 +27,7 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
private long _ackOn;
private long _cancelledOn;
private SimpleTimer.TimedEvent _resendEvent;
private ByteCache _cache = ByteCache.getInstance(128, MAX_PAYLOAD_SIZE);
public PacketLocal(I2PAppContext ctx, Destination to) {
this(ctx, to, null);
@@ -79,10 +81,11 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
_numSends++;
_lastSend = _context.clock().now();
}
public void ackReceived() {
public void ackReceived() {
synchronized (this) {
if (_ackOn <= 0)
_ackOn = _context.clock().now();
_ackOn = _context.clock().now();
releasePayload();
notifyAll();
}
SimpleTimer.getInstance().removeEvent(_resendEvent);
@@ -90,6 +93,7 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
public void cancelled() {
synchronized (this) {
_cancelledOn = _context.clock().now();
releasePayload();
notifyAll();
}
SimpleTimer.getInstance().removeEvent(_resendEvent);
@@ -110,16 +114,44 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
public void setResendPacketEvent(SimpleTimer.TimedEvent evt) { _resendEvent = evt; }
public String toString() {
String str = super.toString();
public StringBuffer formatAsString() {
StringBuffer buf = super.formatAsString();
Connection con = _connection;
if (con != null)
buf.append(" rtt ").append(con.getOptions().getRTT());
if ( (_tagsSent != null) && (_tagsSent.size() > 0) )
str = str + " with tags";
buf.append(" with tags");
if (_ackOn > 0)
return str + " ack after " + getAckTime() + (_numSends <= 1 ? "" : " sent " + _numSends + " times");
else
return str + (_numSends <= 1 ? "" : " sent " + _numSends + " times");
buf.append(" ack after ").append(getAckTime());
if (_numSends > 1)
buf.append(" sent ").append(_numSends).append(" times");
if (isFlagSet(Packet.FLAG_SYNCHRONIZE) ||
isFlagSet(Packet.FLAG_CLOSE) ||
isFlagSet(Packet.FLAG_RESET)) {
if (con != null) {
buf.append(" from ");
Destination local = con.getSession().getMyDestination();
if (local != null)
buf.append(local.calculateHash().toBase64().substring(0,4));
else
buf.append("unknown");
buf.append(" to ");
Destination remote = con.getRemotePeer();
if (remote != null)
buf.append(remote.calculateHash().toBase64().substring(0,4));
else
buf.append("unknown");
}
}
return buf;
}
public void waitForAccept(int maxWaitMs) {
@@ -131,10 +163,12 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
int window = _connection.getOptions().getWindowSize();
boolean accepted = _connection.packetSendChoke(maxWaitMs);
long after = _context.clock().now();
if (accepted)
if (accepted) {
_acceptedOn = after;
else
} else {
_acceptedOn = -1;
releasePayload();
}
int afterQueued = _connection.getUnackedPacketsSent();
if ( (after - before > 1000) && (_log.shouldLog(Log.DEBUG)) )
_log.debug("Took " + (after-before) + "ms to get "
@@ -149,11 +183,12 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
long expiration = _context.clock().now()+maxWaitMs;
while (true) {
long timeRemaining = expiration - _context.clock().now();
if ( (timeRemaining <= 0) && (maxWaitMs > 0) ) return;
if ( (timeRemaining <= 0) && (maxWaitMs > 0) ) break;
try {
synchronized (this) {
if (_ackOn > 0) return;
if (_cancelledOn > 0) return;
if (_ackOn > 0) break;
if (_cancelledOn > 0) break;
if (!_connection.getIsConnected()) break;
if (timeRemaining > 60*1000)
timeRemaining = 60*1000;
else if (timeRemaining <= 0)
@@ -162,6 +197,8 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
}
} catch (InterruptedException ie) {}
}
if (!writeSuccessful())
releasePayload();
}
public boolean writeAccepted() { return _acceptedOn > 0 && _cancelledOn <= 0; }

View File

@@ -1,6 +1,5 @@
package net.i2p.client.streaming;
import java.util.Arrays;
import java.util.Set;
import java.util.HashSet;
@@ -49,12 +48,16 @@ class PacketQueue {
tagsSent = new HashSet(0);
// cache this from before sendMessage
String conStr = (packet.getConnection() != null ? packet.getConnection().toString() : "");
String conStr = null;
if (_log.shouldLog(Log.DEBUG))
conStr = (packet.getConnection() != null ? packet.getConnection().toString() : "");
if (packet.getAckTime() > 0) {
_log.debug("Not resending " + packet);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Not resending " + packet);
return;
} else {
_log.debug("Sending... " + packet);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending... " + packet);
}
ByteArray ba = _cache.acquire();
@@ -125,6 +128,17 @@ class PacketQueue {
String suffix = (c != null ? "wsize " + c.getOptions().getWindowSize() : null);
_connectionManager.getPacketHandler().displayPacket(packet, "SEND", suffix);
}
if ( (packet.getSequenceNum() == 0) && (!packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) ) {
// ack only, so release it asap
packet.releasePayload();
} else if (packet.isFlagSet(Packet.FLAG_ECHO) && !packet.isFlagSet(Packet.FLAG_SIGNATURE_INCLUDED) ) {
// pong
packet.releasePayload();
} else if (packet.isFlagSet(Packet.FLAG_RESET)) {
// reset
packet.releasePayload();
}
}
}

View File

@@ -6,6 +6,7 @@ import java.util.ArrayList;
import java.util.Collections;
import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
@@ -29,7 +30,7 @@ public class MessageInputStreamTest {
for (int i = 0; i < orig.length / 1024; i++) {
byte msg[] = new byte[1024];
System.arraycopy(orig, i*1024, msg, 0, 1024);
in.messageReceived(i, msg);
in.messageReceived(i, new ByteArray(msg));
}
byte read[] = new byte[orig.length];
@@ -59,7 +60,7 @@ public class MessageInputStreamTest {
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);
in.messageReceived(cur.intValue(), new ByteArray(msg));
_log.debug("Injecting " + cur);
}
@@ -91,7 +92,7 @@ public class MessageInputStreamTest {
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);
in.messageReceived(cur.intValue(), new ByteArray(msg));
_log.debug("Injecting " + cur);
}
}
@@ -126,7 +127,7 @@ public class MessageInputStreamTest {
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);
in.messageReceived(cur.intValue(), new ByteArray(msg));
_log.debug("Injecting " + cur);
try {

View File

@@ -34,8 +34,10 @@
</target>
<target name="clean">
<delete>
<fileset dir="src/WEB-INF/classes/" includes="**/*.class, susimail.war" />
<fileset dir="src/WEB-INF/classes/" includes="**/*.class" />
</delete>
<delete dir="src/WEB-INF/classes/i2p"/>
<delete file="susimail.war"/>
</target>
<target name="cleandep" depends="clean" />
<target name="distclean" depends="clean" />

View File

@@ -19,11 +19,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Revision: 1.5 $
* $Revision: 1.1 $
*/
package i2p.susi.webmail;
import i2p.susi.debug.Debug;
import i2p.susi.util.Config;
import i2p.susi.util.ReadBuffer;
import i2p.susi.webmail.encoding.Encoding;

View File

@@ -19,13 +19,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Revision: 1.3 $
* $Revision: 1.1 $
*/
package i2p.susi.webmail;
import java.util.Hashtable;
import i2p.susi.debug.Debug;
import i2p.susi.webmail.pop3.POP3MailBox;
/**

View File

@@ -10,8 +10,6 @@
package net.i2p.apps.systray;
import java.awt.Frame;
import java.io.File;
import snoozesoft.systray4j.SysTrayMenu;
import snoozesoft.systray4j.SysTrayMenuEvent;
import snoozesoft.systray4j.SysTrayMenuIcon;

View File

@@ -193,6 +193,7 @@
<copy file="installer/resources/osid" todir="pkg-temp/" />
<copy file="installer/resources/postinstall.bat" todir="pkg-temp/" />
<copy file="installer/resources/postinstall.sh" todir="pkg-temp/" />
<copy file="installer/resources/startRouter.sh" todir="pkg-temp/" />
<copy file="installer/resources/systray.config" todir="pkg-temp/" />
<!-- <copy file="installer/resources/uninstall_i2p_service_unix" todir="pkg-temp/" /> -->
<copy file="installer/resources/uninstall_i2p_service_winnt.bat" todir="pkg-temp/" />
@@ -221,6 +222,7 @@
<copy file="core/perl/i2ptest.sh" todir="pkg-temp/scripts/" />
<mkdir dir="pkg-temp/docs" />
<copy file="readme.html" todir="pkg-temp/docs/" />
<copy file="initialNews.xml" tofile="pkg-temp/docs/news.xml" />
<copy file="installer/resources/startconsole.html" todir="pkg-temp/docs/" />
<copy file="installer/resources/start.ico" todir="pkg-temp/docs/" />
<copy file="installer/resources/console.ico" todir="pkg-temp/docs/" />
@@ -269,6 +271,8 @@
<copy file="build/addressbook.war" todir="pkg-temp/webapps/" />
<copy file="build/susimail.war" todir="pkg-temp/webapps/" />
<copy file="history.txt" todir="pkg-temp/" />
<mkdir dir="pkg-temp/docs/" />
<copy file="news.xml" todir="pkg-temp/docs/" />
<!-- the addressbook handles this for updates -->
<!-- <copy file="hosts.txt" todir="pkg-temp/" /> -->
<mkdir dir="pkg-temp/eepsite" />

View File

@@ -14,8 +14,8 @@ package net.i2p;
*
*/
public class CoreVersion {
public final static String ID = "$Revision: 1.28 $ $Date: 2005/02/17 17:57:53 $";
public final static String VERSION = "0.5.0.1";
public final static String ID = "$Revision: 1.31 $ $Date: 2005/03/18 17:34:53 $";
public final static String VERSION = "0.5.0.4";
public static void main(String args[]) {
System.out.println("I2P Core version: " + VERSION);

View File

@@ -91,7 +91,6 @@ public class I2PAppContext {
public static I2PAppContext getGlobalContext() {
synchronized (I2PAppContext.class) {
if (_globalAppContext == null) {
System.err.println("*** Building a seperate global context!");
_globalAppContext = new I2PAppContext(false, null);
}
}

View File

@@ -30,7 +30,6 @@ import net.i2p.data.i2cp.ReportAbuseMessage;
import net.i2p.data.i2cp.SendMessageMessage;
import net.i2p.data.i2cp.SessionConfig;
import net.i2p.util.Log;
import net.i2p.util.RandomSource;
/**
* Produce the various messages the session needs to send to the router.
@@ -53,13 +52,17 @@ class I2CPMessageProducer {
CreateSessionMessage msg = new CreateSessionMessage();
SessionConfig cfg = new SessionConfig(session.getMyDestination());
cfg.setOptions(session.getOptions());
if (_log.shouldLog(Log.DEBUG)) _log.debug("config created");
try {
cfg.signSessionConfig(session.getPrivateKey());
} catch (DataFormatException dfe) {
throw new I2PSessionException("Unable to sign the session config", dfe);
}
if (_log.shouldLog(Log.DEBUG)) _log.debug("config signed");
msg.setSessionConfig(cfg);
if (_log.shouldLog(Log.DEBUG)) _log.debug("config loaded into message");
session.sendMessage(msg);
if (_log.shouldLog(Log.DEBUG)) _log.debug("config message sent");
}
/**

View File

@@ -39,6 +39,7 @@ import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.data.i2cp.SessionId;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer;
/**
* Implementation of an I2P session running over TCP. This class is NOT thread safe -
@@ -78,7 +79,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
/** class that generates new messages */
protected I2CPMessageProducer _producer;
/** map of integer --> MessagePayloadMessage */
Map _availableMessages;
private Map _availableMessages;
protected I2PClientMessageHandlerMap _handlerMap;
@@ -290,7 +291,10 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
*
*/
public byte[] receiveMessage(int msgId) throws I2PSessionException {
MessagePayloadMessage msg = (MessagePayloadMessage) _availableMessages.remove(new Integer(msgId));
MessagePayloadMessage msg = null;
synchronized (_availableMessages) {
msg = (MessagePayloadMessage) _availableMessages.remove(new Integer(msgId));
}
if (msg == null) return null;
return msg.getPayload().getUnencryptedData();
}
@@ -339,12 +343,14 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* Recieve a payload message and let the app know its available
*/
public void addNewMessage(MessagePayloadMessage msg) {
_availableMessages.put(new Integer(msg.getMessageId().getMessageId()), msg);
synchronized (_availableMessages) {
_availableMessages.put(new Integer(msg.getMessageId().getMessageId()), msg);
}
int id = msg.getMessageId().getMessageId();
byte data[] = msg.getPayload().getUnencryptedData();
if ((data == null) || (data.length <= 0)) {
if (_log.shouldLog(Log.ERROR))
_log.error(getPrefix() + "addNewMessage of a message with no unencrypted data",
if (_log.shouldLog(Log.CRIT))
_log.log(Log.CRIT, getPrefix() + "addNewMessage of a message with no unencrypted data",
new Exception("Empty message"));
} else {
int size = data.length;
@@ -352,6 +358,20 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
if (_log.shouldLog(Log.INFO))
_log.info(getPrefix() + "Notified availability for session " + _sessionId + ", message " + id);
}
SimpleTimer.getInstance().addEvent(new VerifyUsage(id), 30*1000);
}
private class VerifyUsage implements SimpleTimer.TimedEvent {
private int _msgId;
public VerifyUsage(int id) { _msgId = id; }
public void timeReached() {
MessagePayloadMessage removed = null;
synchronized (_availableMessages) {
removed = (MessagePayloadMessage)_availableMessages.remove(new Integer(_msgId));
}
if (removed != null)
_log.log(Log.CRIT, "Message NOT removed! id=" + _msgId + ": " + removed);
}
}
private class AvailabilityNotifier implements Runnable {
@@ -402,6 +422,8 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
} catch (Exception e) {
_log.log(Log.CRIT, "Error notifying app of message availability", e);
}
} else {
_log.log(Log.CRIT, "Unable to notify an app that " + msgId + " of size " + size + " is available!");
}
}
}
@@ -482,12 +504,15 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
long beforeSync = _context.clock().now();
long inSync = 0;
if (_log.shouldLog(Log.DEBUG)) _log.debug("before sync to write");
try {
synchronized (_out) {
inSync = _context.clock().now();
if (_log.shouldLog(Log.DEBUG)) _log.debug("before writeMessage");
message.writeMessage(_out);
if (_log.shouldLog(Log.DEBUG)) _log.debug("after writeMessage");
_out.flush();
if (_log.shouldLog(Log.DEBUG)) _log.debug("after flush");
}
} catch (I2CPMessageException ime) {
throw new I2PSessionException(getPrefix() + "Error writing out the message", ime);

View File

@@ -73,10 +73,11 @@ class I2PSessionImpl2 extends I2PSessionImpl {
}
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent)
throws I2PSessionException {
if (_log.shouldLog(Log.DEBUG)) _log.debug("sending message");
if (isClosed()) throw new I2PSessionException("Already closed");
if (SHOULD_COMPRESS) payload = DataHelper.compress(payload, offset, size);
else throw new IllegalStateException("we need to update sendGuaranteed to support partial send");
if (_log.shouldLog(Log.DEBUG)) _log.debug("message compressed");
// 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
@@ -111,10 +112,12 @@ class I2PSessionImpl2 extends I2PSessionImpl {
private boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent)
throws I2PSessionException {
long begin = _context.clock().now();
if (_log.shouldLog(Log.DEBUG)) _log.debug("begin sendBestEffort");
SessionKey key = _context.sessionKeyManager().getCurrentKey(dest.getPublicKey());
if (_log.shouldLog(Log.DEBUG)) _log.debug("key fetched");
if (key == null) key = _context.sessionKeyManager().createSession(dest.getPublicKey());
SessionTag tag = _context.sessionKeyManager().consumeNextAvailableTag(dest.getPublicKey(), key);
if (_log.shouldLog(Log.DEBUG)) _log.debug("tag consumed");
Set sentTags = null;
int oldTags = _context.sessionKeyManager().getAvailableTags(dest.getPublicKey(), key);
long availTimeLeft = _context.sessionKeyManager().getAvailableTimeLeft(dest.getPublicKey(), key);
@@ -151,7 +154,10 @@ class I2PSessionImpl2 extends I2PSessionImpl {
sentTags.addAll(tagsSent);
}
if (_log.shouldLog(Log.DEBUG)) _log.debug("before creating nonce");
long nonce = _context.random().nextInt(Integer.MAX_VALUE);
if (_log.shouldLog(Log.DEBUG)) _log.debug("before sync state");
MessageState state = new MessageState(nonce, getPrefix());
state.setKey(key);
state.setTags(sentTags);
@@ -171,6 +177,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
}
}
if (_log.shouldLog(Log.DEBUG)) _log.debug("before sync state");
long beforeSendingSync = _context.clock().now();
long inSendingSync = 0;
synchronized (_sendingStates) {

View File

@@ -1,9 +1,7 @@
package net.i2p.client;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.i2p.data.Destination;

View File

@@ -41,7 +41,8 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
}
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
_log.debug("Handle message " + message);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Handle message " + message);
RequestLeaseSetMessage msg = (RequestLeaseSetMessage) message;
LeaseSet leaseSet = new LeaseSet();
for (int i = 0; i < msg.getEndpoints(); i++) {

View File

@@ -8,8 +8,6 @@
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;

View File

@@ -9,10 +9,7 @@ package net.i2p.crypto;
*
*/
import java.io.IOException;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.SessionKey;

View File

@@ -14,9 +14,6 @@ import java.io.ByteArrayOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;

View File

@@ -29,10 +29,13 @@ package net.i2p.crypto;
* POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.InputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.Signature;
import net.i2p.data.SigningPrivateKey;
@@ -55,6 +58,12 @@ public class DSAEngine {
return verifySignature(signature, signedData, 0, signedData.length, verifyingKey);
}
public boolean verifySignature(Signature signature, byte signedData[], int offset, int size, SigningPublicKey verifyingKey) {
return verifySignature(signature, calculateHash(signedData, offset, size), verifyingKey);
}
public boolean verifySignature(Signature signature, InputStream in, SigningPublicKey verifyingKey) {
return verifySignature(signature, calculateHash(in), verifyingKey);
}
public boolean verifySignature(Signature signature, Hash hash, SigningPublicKey verifyingKey) {
long start = _context.clock().now();
try {
@@ -72,7 +81,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, offset, size).getData();
byte data[] = hash.getData();
NativeBigInteger bi = new NativeBigInteger(1, data);
BigInteger u1 = bi.multiply(w).mod(CryptoConstants.dsaq);
BigInteger u2 = r.multiply(w).mod(CryptoConstants.dsaq);
@@ -99,6 +108,18 @@ public class DSAEngine {
}
public Signature sign(byte data[], int offset, int length, SigningPrivateKey signingKey) {
if ((signingKey == null) || (data == null) || (data.length <= 0)) return null;
Hash h = calculateHash(data, offset, length);
return sign(h, signingKey);
}
public Signature sign(InputStream in, SigningPrivateKey signingKey) {
if ((signingKey == null) || (in == null) ) return null;
Hash h = calculateHash(in);
return sign(h, signingKey);
}
public Signature sign(Hash hash, SigningPrivateKey signingKey) {
if ((signingKey == null) || (hash == null)) return null;
long start = _context.clock().now();
Signature sig = new Signature();
@@ -110,11 +131,8 @@ public class DSAEngine {
BigInteger r = CryptoConstants.dsag.modPow(k, CryptoConstants.dsap).mod(CryptoConstants.dsaq);
BigInteger kinv = k.modInverse(CryptoConstants.dsaq);
Hash h = calculateHash(data, offset, length);
if (h == null) return null;
BigInteger M = new NativeBigInteger(1, h.getData());
BigInteger M = new NativeBigInteger(1, hash.getData());
BigInteger x = new NativeBigInteger(1, signingKey.getData());
BigInteger s = (kinv.multiply(M.add(x.multiply(r)))).mod(CryptoConstants.dsaq);
@@ -157,141 +175,27 @@ public class DSAEngine {
return sig;
}
private int[] H0 = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0};
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 = len / 4 + padbytes / 4 + 3;
int[] M0 = new int[wordlength];
int wordcount = 0;
int x = 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 (len - (wordcount + 1) * 4 + 4) {
case 0:
M0[wordcount] |= 0x80000000;
break;
case 1:
M0[wordcount] = source[offset + x] << 24 >>> 24 << 24;
M0[wordcount] |= 0x00800000;
break;
case 2:
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[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;
}
M0[wordlength - 2] = (int) (length >>> 32);
M0[wordlength - 1] = (int) (length);
int[] H = new int[5];
for (x = 0; x < 5; x++) {
H[x] = H0[x];
}
int blocks = M0.length / 16;
int[] W = new int[80];
for (int bl = 0; bl < blocks; bl++) {
int a = H[0];
int b = H[1];
int c = H[2];
int d = H[3];
int e = H[4];
Arrays.fill(W, 0);
for (x = 0; x < 80; x++) {
if (x < 16) {
W[x] = M0[bl * 16 + x];
} else {
W[x] = ROTL(1, W[x - 3] ^ W[x - 8] ^ W[x - 14] ^ W[x - 16]);
}
public Hash calculateHash(InputStream in) {
SHA1 digest = new SHA1();
byte buf[] = new byte[64];
int read = 0;
try {
while ( (read = in.read(buf)) != -1) {
digest.engineUpdate(buf, 0, read);
}
for (x = 0; x < 80; x++) {
int T = add(ROTL(5, a), add(f(x, b, c, d), add(e, add(k(x), W[x]))));
e = d;
d = c;
c = ROTL(30, b);
b = a;
a = T;
}
H[0] = add(a, H[0]);
H[1] = add(b, H[1]);
H[2] = add(c, H[2]);
H[3] = add(d, H[3]);
H[4] = add(e, H[4]);
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to hash the stream", ioe);
return null;
}
byte[] hashbytes = new byte[20];
for (x = 0; x < 5; x++) {
hashbytes[x * 4] = (byte) (H[x] << 0 >>> 24);
hashbytes[x * 4 + 1] = (byte) (H[x] << 8 >>> 24);
hashbytes[x * 4 + 2] = (byte) (H[x] << 16 >>> 24);
hashbytes[x * 4 + 3] = (byte) (H[x] << 24 >>> 24);
}
Hash hash = new Hash();
hash.setData(hashbytes);
return hash;
return new Hash(digest.engineDigest());
}
private int k(int t) {
if (t > -1 && t < 20) {
return 0x5a827999;
} else if (t > 19 && t < 40) {
return 0x6ed9eba1;
} else if (t > 39 && t < 60) {
return 0x8f1bbcdc;
} else if (t > 59 && t < 80) { return 0xca62c1d6; }
return 0x00000000;
}
private int f(int t, int x, int y, int z) {
if (t > -1 && t < 20) {
return Ch(x, y, z);
} else if (t > 19 && t < 40) {
return Parity(x, y, z);
} else if (t > 39 && t < 60) {
return Maj(x, y, z);
} else if (t > 59 && t < 80) { return Parity(x, y, z); }
return 0x00000000;
}
private int Ch(int x, int y, int z) {
return (x & y) ^ (~x & z);
}
private int Parity(int x, int y, int z) {
return x ^ y ^ z;
}
private int Maj(int x, int y, int z) {
return (x & y) ^ (x & z) ^ (y & z);
}
private int ROTL(int n, int x) {
return (x << n) | (x >>> 32 - n);
}
private int add(int x, int y) {
return x + y;
public static Hash calculateHash(byte[] source, int offset, int len) {
SHA1 h = new SHA1();
h.engineUpdate(source, offset, len);
byte digested[] = h.digest();
return new Hash(digested);
}
}

View File

@@ -9,8 +9,6 @@ package net.i2p.crypto;
*
*/
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

View File

@@ -29,7 +29,6 @@ package net.i2p.crypto;
* POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import net.i2p.I2PAppContext;

View File

@@ -0,0 +1,697 @@
package net.i2p.crypto;
/* @(#)SHA1.java 1.11 2004-04-26
* This file was freely contributed to the LimeWire project and is covered
* by its existing GPL licence, but it may be used individually as a public
* domain implementation of a published algorithm (see below for references).
* It was also freely contributed to the Bitzi public domain sources.
* @author Philippe Verdy
*/
/* Sun may wish to change the following package name, if integrating this
* class in the Sun JCE Security Provider for Java 1.5 (code-named Tiger).
*
* You can include it in your own Security Provider by inserting
* this property in your Provider derived class:
* put("MessageDigest.SHA-1", "com.bitzi.util.SHA1");
*/
//package com.bitzi.util;
import java.security.*;
//--+---+1--+---+--2+---+---+3--+---+--4+---+---+5--+---+--6+---+---+7--+---+--
//34567890123456789012345678901234567890123456789012345678901234567890123456789
/**
* <p>The FIPS PUB 180-2 standard specifies four secure hash algorithms (SHA-1,
* SHA-256, SHA-384 and SHA-512) for computing a condensed representation of
* electronic data (message). When a message of any length < 2^^64 bits (for
* SHA-1 and SHA-256) or < 2^^128 bits (for SHA-384 and SHA-512) is input to
* an algorithm, the result is an output called a message digest. The message
* digests range in length from 160 to 512 bits, depending on the algorithm.
* Secure hash algorithms are typically used with other cryptographic
* algorithms, such as digital signature algorithms and keyed-hash message
* authentication codes, or in the generation of random numbers (bits).</p>
*
* <p>The four hash algorithms specified in this "SHS" standard are called
* secure because, for a given algorithm, it is computationally infeasible
* 1) to find a message that corresponds to a given message digest, or 2)
* to find two different messages that produce the same message digest. Any
* change to a message will, with a very high probability, result in a
* different message digest. This will result in a verification failure when
* the secure hash algorithm is used with a digital signature algorithm or a
* keyed-hash message authentication algorithm.</p>
*
* <p>A "SHS change notice" adds a SHA-224 algorithm for interoperability,
* which, like SHA-1 and SHA-256, operates on 512-bit blocks and 32-bit words,
* but truncates the final digest and uses distinct initialization values.</p>
*
* <p><b>References:</b></p>
* <ol>
* <li> NIST FIPS PUB 180-2, "Secure Hash Signature Standard (SHS) with
* change notice", National Institute of Standards and Technology (NIST),
* 2002 August 1, and U.S. Department of Commerce, August 26.<br>
* <a href="http://csrc.ncsl.nist.gov/CryptoToolkit/Hash.html">
* http://csrc.ncsl.nist.gov/CryptoToolkit/Hash.html</a>
* <li> NIST FIPS PUB 180-1, "Secure Hash Standard",
* U.S. Department of Commerce, May 1993.<br>
* <a href="http://www.itl.nist.gov/div897/pubs/fip180-1.htm">
* http://www.itl.nist.gov/div897/pubs/fip180-1.htm</a></li>
* <li> Bruce Schneier, "Section 18.7 Secure Hash Algorithm (SHA)",
* <cite>Applied Cryptography, 2nd edition</cite>, <br>
* John Wiley & Sons, 1996</li>
* </ol>
*/
public final class SHA1 extends MessageDigest implements Cloneable {
/**
* This implementation returns a fixed-size digest.
*/
private static final int HASH_LENGTH = 20; // bytes == 160 bits
/**
* Private context for incomplete blocks and padding bytes.
* INVARIANT: padding must be in 0..63.
* When the padding reaches 64, a new block is computed, and
* the 56 last bytes are kept in the padding history.
*/
private byte[] pad;
private int padding;
/**
* Private contextual byte count, sent in the next block,
* after the ending padding block.
*/
private long bytes;
/**
* Private context that contains the current digest key.
*/
private int hA, hB, hC, hD, hE;
/**
* Creates a SHA1 object with default initial state.
*/
public SHA1() {
super("SHA-1");
pad = new byte[64];
init();
}
/**
* Clones this object.
*/
public Object clone() throws CloneNotSupportedException {
SHA1 that = (SHA1)super.clone();
that.pad = (byte[])this.pad.clone();
return that;
}
/**
* Returns the digest length in bytes.
*
* Can be used to allocate your own output buffer when
* computing multiple digests.
*
* Overrides the protected abstract method of
* <code>java.security.MessageDigestSpi</code>.
* @return the digest length in bytes.
*/
public int engineGetDigestLength() {
return HASH_LENGTH;
}
/**
* Reset athen initialize the digest context.
*
* Overrides the protected abstract method of
* <code>java.security.MessageDigestSpi</code>.
*/
protected void engineReset() {
int i = 60;
do {
pad[i ] = (byte)0x00;
pad[i + 1] = (byte)0x00;
pad[i + 2] = (byte)0x00;
pad[i + 3] = (byte)0x00;
} while ((i -= 4) >= 0);
padding = 0;
bytes = 0;
init();
}
/**
* Initialize the digest context.
*/
protected void init() {
hA = 0x67452301;
hB = 0xefcdab89;
hC = 0x98badcfe;
hD = 0x10325476;
hE = 0xc3d2e1f0;
}
/**
* Updates the digest using the specified byte.
* Requires internal buffering, and may be slow.
*
* Overrides the protected abstract method of
* java.security.MessageDigestSpi.
* @param input the byte to use for the update.
*/
public void engineUpdate(byte input) {
bytes++;
if (padding < 63) {
pad[padding++] = input;
return;
}
pad[63] = input;
computeBlock(pad, 0);
padding = 0;
}
/**
* Updates the digest using the specified array of bytes,
* starting at the specified offset.
*
* Input length can be any size. May require internal buffering,
* if input blocks are not multiple of 64 bytes.
*
* Overrides the protected abstract method of
* java.security.MessageDigestSpi.
* @param input the array of bytes to use for the update.
* @param offset the offset to start from in the array of bytes.
* @param len the number of bytes to use, starting at offset.
*/
public void engineUpdate(byte[] input, int offset, int len) {
if (offset >= 0 && len >= 0 && offset + len <= input.length) {
bytes += len;
/* Terminate the previous block. */
int padlen = 64 - padding;
if (padding > 0 && len >= padlen) {
System.arraycopy(input, offset, pad, padding, padlen);
computeBlock(pad, 0);
padding = 0;
offset += padlen;
len -= padlen;
}
/* Loop on large sets of complete blocks. */
while (len >= 512) {
computeBlock(input, offset);
computeBlock(input, offset + 64);
computeBlock(input, offset + 128);
computeBlock(input, offset + 192);
computeBlock(input, offset + 256);
computeBlock(input, offset + 320);
computeBlock(input, offset + 384);
computeBlock(input, offset + 448);
offset += 512;
len -= 512;
}
/* Loop on remaining complete blocks. */
while (len >= 64) {
computeBlock(input, offset);
offset += 64;
len -= 64;
}
/* remaining bytes kept for next block. */
if (len > 0) {
System.arraycopy(input, offset, pad, padding, len);
padding += len;
}
return;
}
throw new ArrayIndexOutOfBoundsException(offset);
}
/**
* Completes the hash computation by performing final operations
* such as padding. Computes the final hash and returns the final
* value as a byte[20] array. Once engineDigest has been called,
* the engine will be automatically reset as specified in the
* JavaSecurity MessageDigest specification.
*
* For faster operations with multiple digests, allocate your own
* array and use engineDigest(byte[], int offset, int len).
*
* Overrides the protected abstract method of
* java.security.MessageDigestSpi.
* @return the length of the digest stored in the output buffer.
*/
public byte[] engineDigest() {
try {
final byte hashvalue[] = new byte[HASH_LENGTH];
engineDigest(hashvalue, 0, HASH_LENGTH);
return hashvalue;
} catch (DigestException e) {
return null;
}
}
/**
* Completes the hash computation by performing final operations
* such as padding. Once engineDigest has been called, the engine
* will be automatically reset (see engineReset).
*
* Overrides the protected abstract method of
* java.security.MessageDigestSpi.
* @param hashvalue the output buffer in which to store the digest.
* @param offset offset to start from in the output buffer
* @param len number of bytes within buf allotted for the digest.
* Both this default implementation and the SUN provider
* do not return partial digests. The presence of this
* parameter is solely for consistency in our API's.
* If the value of this parameter is less than the
* actual digest length, the method will throw a
* DigestException. This parameter is ignored if its
* value is greater than or equal to the actual digest
* length.
* @return the length of the digest stored in the output buffer.
*/
public int engineDigest(byte[] hashvalue, int offset, final int len)
throws DigestException {
if (len >= HASH_LENGTH) {
if (hashvalue.length - offset >= HASH_LENGTH) {
/* Flush the trailing bytes, adding padding bytes into last
* blocks. */
int i;
/* Add padding null bytes but replace the last 8 padding bytes
* by the little-endian 64-bit digested message bit-length. */
pad[i = padding] = (byte)0x80; /* required 1st padding byte */
/* Check if 8 bytes available in pad to store the total
* message size */
switch (i) { /* INVARIANT: i must be in [0..63] */
case 52: pad[53] = (byte)0x00; /* no break; falls thru */
case 53: pad[54] = (byte)0x00; /* no break; falls thru */
case 54: pad[55] = (byte)0x00; /* no break; falls thru */
case 55: break;
case 56: pad[57] = (byte)0x00; /* no break; falls thru */
case 57: pad[58] = (byte)0x00; /* no break; falls thru */
case 58: pad[59] = (byte)0x00; /* no break; falls thru */
case 59: pad[60] = (byte)0x00; /* no break; falls thru */
case 60: pad[61] = (byte)0x00; /* no break; falls thru */
case 61: pad[62] = (byte)0x00; /* no break; falls thru */
case 62: pad[63] = (byte)0x00; /* no break; falls thru */
case 63:
computeBlock(pad, 0);
/* Clear the 56 first bytes of pad[]. */
i = 52;
do {
pad[i ] = (byte)0x00;
pad[i + 1] = (byte)0x00;
pad[i + 2] = (byte)0x00;
pad[i + 3] = (byte)0x00;
} while ((i -= 4) >= 0);
break;
default:
/* Clear the rest of 56 first bytes of pad[]. */
switch (i & 3) {
case 3: i++;
break;
case 2: pad[(i += 2) - 1] = (byte)0x00;
break;
case 1: pad[(i += 3) - 2] = (byte)0x00;
pad[ i - 1] = (byte)0x00;
break;
case 0: pad[(i += 4) - 3] = (byte)0x00;
pad[ i - 2] = (byte)0x00;
pad[ i - 1] = (byte)0x00;
}
do {
pad[i ] = (byte)0x00;
pad[i + 1] = (byte)0x00;
pad[i + 2] = (byte)0x00;
pad[i + 3] = (byte)0x00;
} while ((i += 4) < 56);
}
/* Convert the message size from bytes to big-endian bits. */
pad[56] = (byte)((i = (int)(bytes >>> 29)) >> 24);
pad[57] = (byte)(i >>> 16);
pad[58] = (byte)(i >>> 8);
pad[59] = (byte)i;
pad[60] = (byte)((i = (int)bytes << 3) >> 24);
pad[61] = (byte)(i >>> 16);
pad[62] = (byte)(i >>> 8);
pad[63] = (byte)i;
computeBlock(pad, 0);
/* Return the computed digest in big-endian byte order. */
hashvalue[offset ] = (byte)((i = hA) >>> 24);
hashvalue[offset + 1] = (byte)(i >>> 16);
hashvalue[offset + 2] = (byte)(i >>> 8);
hashvalue[offset + 3] = (byte)i;
hashvalue[offset + 4] = (byte)((i = hB) >>> 24);
hashvalue[offset += 5] = (byte)(i >>> 16);
hashvalue[offset + 1] = (byte)(i >>> 8);
hashvalue[offset + 2] = (byte)i;
hashvalue[offset + 3] = (byte)((i = hC) >>> 24);
hashvalue[offset + 4] = (byte)(i >>> 16);
hashvalue[offset += 5] = (byte)(i >>> 8);
hashvalue[offset + 1] = (byte)i;
hashvalue[offset + 2] = (byte)((i = hD) >>> 24);
hashvalue[offset + 3] = (byte)(i >>> 16);
hashvalue[offset + 4] = (byte)(i >>> 8);
hashvalue[offset += 5] = (byte)i;
hashvalue[offset + 1] = (byte)((i = hE) >>> 24);
hashvalue[offset + 2] = (byte)(i >>> 16);
hashvalue[offset + 3] = (byte)(i >>> 8);
hashvalue[offset + 4] = (byte)i;
engineReset(); /* clear the evidence */
return HASH_LENGTH;
}
throw new DigestException(
"insufficient space in output buffer to store the digest");
}
throw new DigestException("partial digests not returned");
}
/**
* Updates the digest using the specified array of bytes,
* starting at the specified offset, but an implied length
* of exactly 64 bytes.
*
* Requires no internal buffering, but assumes a fixed input size,
* in which the required padding bytes may have been added.
*
* @param input the array of bytes to use for the update.
* @param offset the offset to start from in the array of bytes.
*/
private void computeBlock(final byte[] input, int offset) {
/* Local temporary work variables for intermediate digests. */
int a, b, c, d, e;
/* Cache the input block into the local working set of 32-bit
* values, in big-endian byte order. Be careful when
* widening bytes or integers due to sign extension! */
int i00, i01, i02, i03, i04, i05, i06, i07,
i08, i09, i10, i11, i12, i13, i14, i15;
/* Use hash schedule function Ch (rounds 0..19):
* Ch(x,y,z) = (x & y) ^ (~x & z) = (x & (y ^ z)) ^ z,
* and K00 = .... = K19 = 0x5a827999. */
/* First pass, on big endian input (rounds 0..15). */
e = hE
+ (((a = hA) << 5) | (a >>> 27)) + 0x5a827999 // K00
+ (((b = hB) & ((c = hC) ^ (d = hD))) ^ d) // Ch(b,c,d)
+ (i00 = input[offset ] << 24
| (input[offset + 1] & 0xff) << 16
| (input[offset + 2] & 0xff) << 8
| (input[offset + 3] & 0xff)); // W00
d += ((e << 5) | (e >>> 27)) + 0x5a827999 // K01
+ ((a & ((b = (b << 30) | (b >>> 2)) ^ c)) ^ c) // Ch(a,b,c)
+ (i01 = input[offset + 4] << 24
| (input[offset += 5] & 0xff) << 16
| (input[offset + 1] & 0xff) << 8
| (input[offset + 2] & 0xff)); // W01
c += ((d << 5) | (d >>> 27)) + 0x5a827999 // K02
+ ((e & ((a = (a << 30) | (a >>> 2)) ^ b)) ^ b) // Ch(e,a,b)
+ (i02 = input[offset + 3] << 24
| (input[offset + 4] & 0xff) << 16
| (input[offset += 5] & 0xff) << 8
| (input[offset + 1] & 0xff)); // W02
b += ((c << 5) | (c >>> 27)) + 0x5a827999 // K03
+ ((d & ((e = (e << 30) | (e >>> 2)) ^ a)) ^ a) // Ch(d,e,a)
+ (i03 = input[offset + 2] << 24
| (input[offset + 3] & 0xff) << 16
| (input[offset + 4] & 0xff) << 8
| (input[offset += 5] & 0xff)); // W03
a += ((b << 5) | (b >>> 27)) + 0x5a827999 // K04
+ ((c & ((d = (d << 30) | (d >>> 2)) ^ e)) ^ e) // Ch(c,d,e)
+ (i04 = input[offset + 1] << 24
| (input[offset + 2] & 0xff) << 16
| (input[offset + 3] & 0xff) << 8
| (input[offset + 4] & 0xff)); // W04
e += ((a << 5) | (a >>> 27)) + 0x5a827999 // K05
+ ((b & ((c = (c << 30) | (c >>> 2)) ^ d)) ^ d) // Ch(b,c,d)
+ (i05 = input[offset += 5] << 24
| (input[offset + 1] & 0xff) << 16
| (input[offset + 2] & 0xff) << 8
| (input[offset + 3] & 0xff)); // W05
d += ((e << 5) | (e >>> 27)) + 0x5a827999 // K06
+ ((a & ((b = (b << 30) | (b >>> 2)) ^ c)) ^ c) // Ch(a,b,c)
+ (i06 = input[offset + 4] << 24
| (input[offset += 5] & 0xff) << 16
| (input[offset + 1] & 0xff) << 8
| (input[offset + 2] & 0xff)); // W06
c += ((d << 5) | (d >>> 27)) + 0x5a827999 // K07
+ ((e & ((a = (a << 30) | (a >>> 2)) ^ b)) ^ b) // Ch(e,a,b)
+ (i07 = input[offset + 3] << 24
| (input[offset + 4] & 0xff) << 16
| (input[offset += 5] & 0xff) << 8
| (input[offset + 1] & 0xff)); // W07
b += ((c << 5) | (c >>> 27)) + 0x5a827999 // K08
+ ((d & ((e = (e << 30) | (e >>> 2)) ^ a)) ^ a) // Ch(d,e,a)
+ (i08 = input[offset + 2] << 24
| (input[offset + 3] & 0xff) << 16
| (input[offset + 4] & 0xff) << 8
| (input[offset += 5] & 0xff)); // W08
a += ((b << 5) | (b >>> 27)) + 0x5a827999 // K09
+ ((c & ((d = (d << 30) | (d >>> 2)) ^ e)) ^ e) // Ch(c,d,e)
+ (i09 = input[offset + 1] << 24
| (input[offset + 2] & 0xff) << 16
| (input[offset + 3] & 0xff) << 8
| (input[offset + 4] & 0xff)); // W09
e += ((a << 5) | (a >>> 27)) + 0x5a827999 // K10
+ ((b & ((c = (c << 30) | (c >>> 2)) ^ d)) ^ d) // Ch(b,c,d)
+ (i10 = input[offset += 5] << 24
| (input[offset + 1] & 0xff) << 16
| (input[offset + 2] & 0xff) << 8
| (input[offset + 3] & 0xff)); // W10
d += ((e << 5) | (e >>> 27)) + 0x5a827999 // K11
+ ((a & ((b = (b << 30) | (b >>> 2)) ^ c)) ^ c) // Ch(a,b,c)
+ (i11 = input[offset + 4] << 24
| (input[offset += 5] & 0xff) << 16
| (input[offset + 1] & 0xff) << 8
| (input[offset + 2] & 0xff)); // W11
c += ((d << 5) | (d >>> 27)) + 0x5a827999 // K12
+ ((e & ((a = (a << 30) | (a >>> 2)) ^ b)) ^ b) // Ch(e,a,b)
+ (i12 = input[offset + 3] << 24
| (input[offset + 4] & 0xff) << 16
| (input[offset += 5] & 0xff) << 8
| (input[offset + 1] & 0xff)); // W12
b += ((c << 5) | (c >>> 27)) + 0x5a827999 // K13
+ ((d & ((e = (e << 30) | (e >>> 2)) ^ a)) ^ a) // Ch(d,e,a)
+ (i13 = input[offset + 2] << 24
| (input[offset + 3] & 0xff) << 16
| (input[offset + 4] & 0xff) << 8
| (input[offset += 5] & 0xff)); // W13
a += ((b << 5) | (b >>> 27)) + 0x5a827999 // K14
+ ((c & ((d = (d << 30) | (d >>> 2)) ^ e)) ^ e) // Ch(c,d,e)
+ (i14 = input[offset + 1] << 24
| (input[offset + 2] & 0xff) << 16
| (input[offset + 3] & 0xff) << 8
| (input[offset + 4] & 0xff)); // W14
e += ((a << 5) | (a >>> 27)) + 0x5a827999 // K15
+ ((b & ((c = (c << 30) | (c >>> 2)) ^ d)) ^ d) // Ch(b,c,d)
+ (i15 = input[offset += 5] << 24
| (input[offset + 1] & 0xff) << 16
| (input[offset + 2] & 0xff) << 8
| (input[offset + 3] & 0xff)); // W15
/* Second pass, on scheduled input (rounds 16..31). */
d += ((e << 5) | (e >>> 27)) + 0x5a827999 // K16
+ ((a & ((b = (b << 30) | (b >>> 2)) ^ c)) ^ c) // Ch(a,b,c)
+ (i00 = ((i00 ^= i02 ^ i08 ^ i13) << 1) | (i00 >>> 31)); // W16
c += ((d << 5) | (d >>> 27)) + 0x5a827999 // K17
+ ((e & ((a = (a << 30) | (a >>> 2)) ^ b)) ^ b) // Ch(e,a,b)
+ (i01 = ((i01 ^= i03 ^ i09 ^ i14) << 1) | (i01 >>> 31)); // W17
b += ((c << 5) | (c >>> 27)) + 0x5a827999 // K18
+ ((d & ((e = (e << 30) | (e >>> 2)) ^ a)) ^ a) // Ch(d,e,a)
+ (i02 = ((i02 ^= i04 ^ i10 ^ i15) << 1) | (i02 >>> 31)); // W18
a += ((b << 5) | (b >>> 27)) + 0x5a827999 // K19
+ ((c & ((d = (d << 30) | (d >>> 2)) ^ e)) ^ e) // Ch(c,d,e)
+ (i03 = ((i03 ^= i05 ^ i11 ^ i00) << 1) | (i03 >>> 31)); // W19
/* Use hash schedule function Parity (rounds 20..39):
* Parity(x,y,z) = x ^ y ^ z,
* and K20 = .... = K39 = 0x6ed9eba1. */
e += ((a << 5) | (a >>> 27)) + 0x6ed9eba1 // K20
+ (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
+ (i04 = ((i04 ^= i06 ^ i12 ^ i01) << 1) | (i04 >>> 31)); // W20
d += ((e << 5) | (e >>> 27)) + 0x6ed9eba1 // K21
+ (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
+ (i05 = ((i05 ^= i07 ^ i13 ^ i02) << 1) | (i05 >>> 31)); // W21
c += ((d << 5) | (d >>> 27)) + 0x6ed9eba1 // K22
+ (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
+ (i06 = ((i06 ^= i08 ^ i14 ^ i03) << 1) | (i06 >>> 31)); // W22
b += ((c << 5) | (c >>> 27)) + 0x6ed9eba1 // K23
+ (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
+ (i07 = ((i07 ^= i09 ^ i15 ^ i04) << 1) | (i07 >>> 31)); // W23
a += ((b << 5) | (b >>> 27)) + 0x6ed9eba1 // K24
+ (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
+ (i08 = ((i08 ^= i10 ^ i00 ^ i05) << 1) | (i08 >>> 31)); // W24
e += ((a << 5) | (a >>> 27)) + 0x6ed9eba1 // K25
+ (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
+ (i09 = ((i09 ^= i11 ^ i01 ^ i06) << 1) | (i09 >>> 31)); // W25
d += ((e << 5) | (e >>> 27)) + 0x6ed9eba1 // K26
+ (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
+ (i10 = ((i10 ^= i12 ^ i02 ^ i07) << 1) | (i10 >>> 31)); // W26
c += ((d << 5) | (d >>> 27)) + 0x6ed9eba1 // K27
+ (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
+ (i11 = ((i11 ^= i13 ^ i03 ^ i08) << 1) | (i11 >>> 31)); // W27
b += ((c << 5) | (c >>> 27)) + 0x6ed9eba1 // K28
+ (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
+ (i12 = ((i12 ^= i14 ^ i04 ^ i09) << 1) | (i12 >>> 31)); // W28
a += ((b << 5) | (b >>> 27)) + 0x6ed9eba1 // K29
+ (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
+ (i13 = ((i13 ^= i15 ^ i05 ^ i10) << 1) | (i13 >>> 31)); // W29
e += ((a << 5) | (a >>> 27)) + 0x6ed9eba1 // K30
+ (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
+ (i14 = ((i14 ^= i00 ^ i06 ^ i11) << 1) | (i14 >>> 31)); // W30
d += ((e << 5) | (e >>> 27)) + 0x6ed9eba1 // K31
+ (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
+ (i15 = ((i15 ^= i01 ^ i07 ^ i12) << 1) | (i15 >>> 31)); // W31
/* Third pass, on scheduled input (rounds 32..47). */
c += ((d << 5) | (d >>> 27)) + 0x6ed9eba1 // K32
+ (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
+ (i00 = ((i00 ^= i02 ^ i08 ^ i13) << 1) | (i00 >>> 31)); // W32
b += ((c << 5) | (c >>> 27)) + 0x6ed9eba1 // K33
+ (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
+ (i01 = ((i01 ^= i03 ^ i09 ^ i14) << 1) | (i01 >>> 31)); // W33
a += ((b << 5) | (b >>> 27)) + 0x6ed9eba1 // K34
+ (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
+ (i02 = ((i02 ^= i04 ^ i10 ^ i15) << 1) | (i02 >>> 31)); // W34
e += ((a << 5) | (a >>> 27)) + 0x6ed9eba1 // K35
+ (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
+ (i03 = ((i03 ^= i05 ^ i11 ^ i00) << 1) | (i03 >>> 31)); // W35
d += ((e << 5) | (e >>> 27)) + 0x6ed9eba1 // K36
+ (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
+ (i04 = ((i04 ^= i06 ^ i12 ^ i01) << 1) | (i04 >>> 31)); // W36
c += ((d << 5) | (d >>> 27)) + 0x6ed9eba1 // K37
+ (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
+ (i05 = ((i05 ^= i07 ^ i13 ^ i02) << 1) | (i05 >>> 31)); // W37
b += ((c << 5) | (c >>> 27)) + 0x6ed9eba1 // K38
+ (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
+ (i06 = ((i06 ^= i08 ^ i14 ^ i03) << 1) | (i06 >>> 31)); // W38
a += ((b << 5) | (b >>> 27)) + 0x6ed9eba1 // K39
+ (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
+ (i07 = ((i07 ^= i09 ^ i15 ^ i04) << 1) | (i07 >>> 31)); // W39
/* Use hash schedule function Maj (rounds 40..59):
* Maj(x,y,z) = (x&y) ^ (x&z) ^ (y&z) = (x & y) | ((x | y) & z),
* and K40 = .... = K59 = 0x8f1bbcdc. */
e += ((a << 5) | (a >>> 27)) + 0x8f1bbcdc // K40
+ ((b & (c = (c << 30) | (c >>> 2))) | ((b | c) & d)) // Maj(b,c,d)
+ (i08 = ((i08 ^= i10 ^ i00 ^ i05) << 1) | (i08 >>> 31)); // W40
d += ((e << 5) | (e >>> 27)) + 0x8f1bbcdc // K41
+ ((a & (b = (b << 30) | (b >>> 2))) | ((a | b) & c)) // Maj(a,b,c)
+ (i09 = ((i09 ^= i11 ^ i01 ^ i06) << 1) | (i09 >>> 31)); // W41
c += ((d << 5) | (d >>> 27)) + 0x8f1bbcdc // K42
+ ((e & (a = (a << 30) | (a >>> 2))) | ((e | a) & b)) // Maj(e,a,b)
+ (i10 = ((i10 ^= i12 ^ i02 ^ i07) << 1) | (i10 >>> 31)); // W42
b += ((c << 5) | (c >>> 27)) + 0x8f1bbcdc // K43
+ ((d & (e = (e << 30) | (e >>> 2))) | ((d | e) & a)) // Maj(d,e,a)
+ (i11 = ((i11 ^= i13 ^ i03 ^ i08) << 1) | (i11 >>> 31)); // W43
a += ((b << 5) | (b >>> 27)) + 0x8f1bbcdc // K44
+ ((c & (d = (d << 30) | (d >>> 2))) | ((c | d) & e)) // Maj(c,d,e)
+ (i12 = ((i12 ^= i14 ^ i04 ^ i09) << 1) | (i12 >>> 31)); // W44
e += ((a << 5) | (a >>> 27)) + 0x8f1bbcdc // K45
+ ((b & (c = (c << 30) | (c >>> 2))) | ((b | c) & d)) // Maj(b,c,d)
+ (i13 = ((i13 ^= i15 ^ i05 ^ i10) << 1) | (i13 >>> 31)); // W45
d += ((e << 5) | (e >>> 27)) + 0x8f1bbcdc // K46
+ ((a & (b = (b << 30) | (b >>> 2))) | ((a | b) & c)) // Maj(a,b,c)
+ (i14 = ((i14 ^= i00 ^ i06 ^ i11) << 1) | (i14 >>> 31)); // W46
c += ((d << 5) | (d >>> 27)) + 0x8f1bbcdc // K47
+ ((e & (a = (a << 30) | (a >>> 2))) | ((e | a) & b)) // Maj(e,a,b)
+ (i15 = ((i15 ^= i01 ^ i07 ^ i12) << 1) | (i15 >>> 31)); // W47
/* Fourth pass, on scheduled input (rounds 48..63). */
b += ((c << 5) | (c >>> 27)) + 0x8f1bbcdc // K48
+ ((d & (e = (e << 30) | (e >>> 2))) | ((d | e) & a)) // Maj(d,e,a)
+ (i00 = ((i00 ^= i02 ^ i08 ^ i13) << 1) | (i00 >>> 31)); // W48
a += ((b << 5) | (b >>> 27)) + 0x8f1bbcdc // K49
+ ((c & (d = (d << 30) | (d >>> 2))) | ((c | d) & e)) // Maj(c,d,e)
+ (i01 = ((i01 ^= i03 ^ i09 ^ i14) << 1) | (i01 >>> 31)); // W49
e += ((a << 5) | (a >>> 27)) + 0x8f1bbcdc // K50
+ ((b & (c = (c << 30) | (c >>> 2))) | ((b | c) & d)) // Maj(b,c,d)
+ (i02 = ((i02 ^= i04 ^ i10 ^ i15) << 1) | (i02 >>> 31)); // W50
d += ((e << 5) | (e >>> 27)) + 0x8f1bbcdc // K51
+ ((a & (b = (b << 30) | (b >>> 2))) | ((a | b) & c)) // Maj(a,b,c)
+ (i03 = ((i03 ^= i05 ^ i11 ^ i00) << 1) | (i03 >>> 31)); // W51
c += ((d << 5) | (d >>> 27)) + 0x8f1bbcdc // K52
+ ((e & (a = (a << 30) | (a >>> 2))) | ((e | a) & b)) // Maj(e,a,b)
+ (i04 = ((i04 ^= i06 ^ i12 ^ i01) << 1) | (i04 >>> 31)); // W52
b += ((c << 5) | (c >>> 27)) + 0x8f1bbcdc // K53
+ ((d & (e = (e << 30) | (e >>> 2))) | ((d | e) & a)) // Maj(d,e,a)
+ (i05 = ((i05 ^= i07 ^ i13 ^ i02) << 1) | (i05 >>> 31)); // W53
a += ((b << 5) | (b >>> 27)) + 0x8f1bbcdc // K54
+ ((c & (d = (d << 30) | (d >>> 2))) | ((c | d) & e)) // Maj(c,d,e)
+ (i06 = ((i06 ^= i08 ^ i14 ^ i03) << 1) | (i06 >>> 31)); // W54
e += ((a << 5) | (a >>> 27)) + 0x8f1bbcdc // K55
+ ((b & (c = (c << 30) | (c >>> 2))) | ((b | c) & d)) // Maj(b,c,d)
+ (i07 = ((i07 ^= i09 ^ i15 ^ i04) << 1) | (i07 >>> 31)); // W55
d += ((e << 5) | (e >>> 27)) + 0x8f1bbcdc // K56
+ ((a & (b = (b << 30) | (b >>> 2))) | ((a | b) & c)) // Maj(a,b,c)
+ (i08 = ((i08 ^= i10 ^ i00 ^ i05) << 1) | (i08 >>> 31)); // W56
c += ((d << 5) | (d >>> 27)) + 0x8f1bbcdc // K57
+ ((e & (a = (a << 30) | (a >>> 2))) | ((e | a) & b)) // Maj(e,a,b)
+ (i09 = ((i09 ^= i11 ^ i01 ^ i06) << 1) | (i09 >>> 31)); // W57
b += ((c << 5) | (c >>> 27)) + 0x8f1bbcdc // K58
+ ((d & (e = (e << 30) | (e >>> 2))) | ((d | e) & a)) // Maj(d,e,a)
+ (i10 = ((i10 ^= i12 ^ i02 ^ i07) << 1) | (i10 >>> 31)); // W58
a += ((b << 5) | (b >>> 27)) + 0x8f1bbcdc // K59
+ ((c & (d = (d << 30) | (d >>> 2))) | ((c | d) & e)) // Maj(c,d,e)
+ (i11 = ((i11 ^= i13 ^ i03 ^ i08) << 1) | (i11 >>> 31)); // W59
/* Use hash schedule function Parity (rounds 60..79):
* Parity(x,y,z) = x ^ y ^ z,
* and K60 = .... = K79 = 0xca62c1d6. */
e += ((a << 5) | (a >>> 27)) + 0xca62c1d6 // K60
+ (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
+ (i12 = ((i12 ^= i14 ^ i04 ^ i09) << 1) | (i12 >>> 31)); // W60
d += ((e << 5) | (e >>> 27)) + 0xca62c1d6 // K61
+ (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
+ (i13 = ((i13 ^= i15 ^ i05 ^ i10) << 1) | (i13 >>> 31)); // W61
c += ((d << 5) | (d >>> 27)) + 0xca62c1d6 // K62
+ (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
+ (i14 = ((i14 ^= i00 ^ i06 ^ i11) << 1) | (i14 >>> 31)); // W62
b += ((c << 5) | (c >>> 27)) + 0xca62c1d6 // K63
+ (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
+ (i15 = ((i15 ^= i01 ^ i07 ^ i12) << 1) | (i15 >>> 31)); // W63
/* Fifth pass, on scheduled input (rounds 64..79). */
a += ((b << 5) | (b >>> 27)) + 0xca62c1d6 // K64
+ (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
+ (i00 = ((i00 ^= i02 ^ i08 ^ i13) << 1) | (i00 >>> 31)); // W64
e += ((a << 5) | (a >>> 27)) + 0xca62c1d6 // K65
+ (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
+ (i01 = ((i01 ^= i03 ^ i09 ^ i14) << 1) | (i01 >>> 31)); // W65
d += ((e << 5) | (e >>> 27)) + 0xca62c1d6 // K66
+ (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
+ (i02 = ((i02 ^= i04 ^ i10 ^ i15) << 1) | (i02 >>> 31)); // W66
c += ((d << 5) | (d >>> 27)) + 0xca62c1d6 // K67
+ (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
+ (i03 = ((i03 ^= i05 ^ i11 ^ i00) << 1) | (i03 >>> 31)); // W67
b += ((c << 5) | (c >>> 27)) + 0xca62c1d6 // K68
+ (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
+ (i04 = ((i04 ^= i06 ^ i12 ^ i01) << 1) | (i04 >>> 31)); // W68
a += ((b << 5) | (b >>> 27)) + 0xca62c1d6 // K69
+ (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
+ (i05 = ((i05 ^= i07 ^ i13 ^ i02) << 1) | (i05 >>> 31)); // W69
e += ((a << 5) | (a >>> 27)) + 0xca62c1d6 // K70
+ (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
+ (i06 = ((i06 ^= i08 ^ i14 ^ i03) << 1) | (i06 >>> 31)); // W70
d += ((e << 5) | (e >>> 27)) + 0xca62c1d6 // K71
+ (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
+ (i07 = ((i07 ^= i09 ^ i15 ^ i04) << 1) | (i07 >>> 31)); // W71
c += ((d << 5) | (d >>> 27)) + 0xca62c1d6 // K72
+ (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
+ (i08 = ((i08 ^= i10 ^ i00 ^ i05) << 1) | (i08 >>> 31)); // W72
b += ((c << 5) | (c >>> 27)) + 0xca62c1d6 // K73
+ (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
+ (i09 = ((i09 ^= i11 ^ i01 ^ i06) << 1) | (i09 >>> 31)); // W73
a += ((b << 5) | (b >>> 27)) + 0xca62c1d6 // K74
+ (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
+ (i10 = ((i10 ^= i12 ^ i02 ^ i07) << 1) | (i10 >>> 31)); // W74
e += ((a << 5) | (a >>> 27)) + 0xca62c1d6 // K75
+ (b ^ (c = (c << 30) | (c >>> 2)) ^ d) // Parity(b,c,d)
+ (i11 = ((i11 ^= i13 ^ i03 ^ i08) << 1) | (i11 >>> 31)); // W75
d += ((e << 5) | (e >>> 27)) + 0xca62c1d6 // K76
+ (a ^ (b = (b << 30) | (b >>> 2)) ^ c) // Parity(a,b,c)
+ (i12 = ((i12 ^= i14 ^ i04 ^ i09) << 1) | (i12 >>> 31)); // W76
c += ((d << 5) | (d >>> 27)) + 0xca62c1d6 // K77
+ (e ^ (a = (a << 30) | (a >>> 2)) ^ b) // Parity(e,a,b)
+ (i13 = ((i13 ^= i15 ^ i05 ^ i10) << 1) | (i13 >>> 31)); // W77
/* Terminate the last two rounds of fifth pass,
* feeding the final digest on the fly. */
hB +=
b += ((c << 5) | (c >>> 27)) + 0xca62c1d6 // K78
+ (d ^ (e = (e << 30) | (e >>> 2)) ^ a) // Parity(d,e,a)
+ (i14 = ((i14 ^= i00 ^ i06 ^ i11) << 1) | (i14 >>> 31)); // W78
hA +=
a += ((b << 5) | (b >>> 27)) + 0xca62c1d6 // K79
+ (c ^ (d = (d << 30) | (d >>> 2)) ^ e) // Parity(c,d,e)
+ (i15 = ((i15 ^= i01 ^ i07 ^ i12) << 1) | (i15 >>> 31)); // W79
hE += e;
hD += d;
hC += /* c= */ (c << 30) | (c >>> 2);
}
}

View File

@@ -0,0 +1,191 @@
package net.i2p.crypto;
/* @(#)SHA1Test.java 1.10 2004-04-24
* This file was freely contributed to the LimeWire project and is covered
* by its existing GPL licence, but it may be used individually as a public
* domain implementation of a published algorithm (see below for references).
* It was also freely contributed to the Bitzi public domain sources.
* @author Philippe Verdy
*/
/* Sun may wish to change the following package name, if integrating this
* class in the Sun JCE Security Provider for Java 1.5 (code-named Tiger).
*/
//package com.bitzi.util;
import java.security.*;
public class SHA1Test {
private static final SHA1 hash = new SHA1();
public static void main(String args[]) {
// http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf
System.out.println("****************************************");
System.out.println("* Basic FIPS PUB 180-1 test vectors... *");
System.out.println("****************************************");
tst(1, 1,
"abc",
"A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D");
tst(1, 2,
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
"84983E44 1C3BD26e BAAE4AA1 F95129E5 E54670F1");
tst(1, 3, /* one million bytes */
1000000, "a",
"34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F");
System.out.println();
// http://csrc.ncsl.nist.gov/cryptval/shs/SHAVS.pdf
System.out.println("********************************************************");
System.out.println("* SHSV Examples of the selected short messages test... *");
System.out.println("********************************************************");
tst(2, 2, new byte[] {/* 8 bits, i.e. 1 byte */
(byte)0x5e},
"5e6f80a3 4a9798ca fc6a5db9 6cc57ba4 c4db59c2");
tst(2, 4, new byte[] {/* 128 bits, i.e. 16 bytes */
(byte)0x9a,(byte)0x7d,(byte)0xfd,(byte)0xf1,(byte)0xec,(byte)0xea,(byte)0xd0,(byte)0x6e,
(byte)0xd6,(byte)0x46,(byte)0xaa,(byte)0x55,(byte)0xfe,(byte)0x75,(byte)0x71,(byte)0x46},
"82abff66 05dbe1c1 7def12a3 94fa22a8 2b544a35");
System.out.println();
System.out.println("*******************************************************");
System.out.println("* SHSV Examples of the selected long messages test... *");
System.out.println("*******************************************************");
tst(3, 2, new byte[] {/* 1304 bits, i.e. 163 bytes */
(byte)0xf7,(byte)0x8f,(byte)0x92,(byte)0x14,(byte)0x1b,(byte)0xcd,(byte)0x17,(byte)0x0a,
(byte)0xe8,(byte)0x9b,(byte)0x4f,(byte)0xba,(byte)0x15,(byte)0xa1,(byte)0xd5,(byte)0x9f,
(byte)0x3f,(byte)0xd8,(byte)0x4d,(byte)0x22,(byte)0x3c,(byte)0x92,(byte)0x51,(byte)0xbd,
(byte)0xac,(byte)0xbb,(byte)0xae,(byte)0x61,(byte)0xd0,(byte)0x5e,(byte)0xd1,(byte)0x15,
(byte)0xa0,(byte)0x6a,(byte)0x7c,(byte)0xe1,(byte)0x17,(byte)0xb7,(byte)0xbe,(byte)0xea,
(byte)0xd2,(byte)0x44,(byte)0x21,(byte)0xde,(byte)0xd9,(byte)0xc3,(byte)0x25,(byte)0x92,
(byte)0xbd,(byte)0x57,(byte)0xed,(byte)0xea,(byte)0xe3,(byte)0x9c,(byte)0x39,(byte)0xfa,
(byte)0x1f,(byte)0xe8,(byte)0x94,(byte)0x6a,(byte)0x84,(byte)0xd0,(byte)0xcf,(byte)0x1f,
(byte)0x7b,(byte)0xee,(byte)0xad,(byte)0x17,(byte)0x13,(byte)0xe2,(byte)0xe0,(byte)0x95,
(byte)0x98,(byte)0x97,(byte)0x34,(byte)0x7f,(byte)0x67,(byte)0xc8,(byte)0x0b,(byte)0x04,
(byte)0x00,(byte)0xc2,(byte)0x09,(byte)0x81,(byte)0x5d,(byte)0x6b,(byte)0x10,(byte)0xa6,
(byte)0x83,(byte)0x83,(byte)0x6f,(byte)0xd5,(byte)0x56,(byte)0x2a,(byte)0x56,(byte)0xca,
(byte)0xb1,(byte)0xa2,(byte)0x8e,(byte)0x81,(byte)0xb6,(byte)0x57,(byte)0x66,(byte)0x54,
(byte)0x63,(byte)0x1c,(byte)0xf1,(byte)0x65,(byte)0x66,(byte)0xb8,(byte)0x6e,(byte)0x3b,
(byte)0x33,(byte)0xa1,(byte)0x08,(byte)0xb0,(byte)0x53,(byte)0x07,(byte)0xc0,(byte)0x0a,
(byte)0xff,(byte)0x14,(byte)0xa7,(byte)0x68,(byte)0xed,(byte)0x73,(byte)0x50,(byte)0x60,
(byte)0x6a,(byte)0x0f,(byte)0x85,(byte)0xe6,(byte)0xa9,(byte)0x1d,(byte)0x39,(byte)0x6f,
(byte)0x5b,(byte)0x5c,(byte)0xbe,(byte)0x57,(byte)0x7f,(byte)0x9b,(byte)0x38,(byte)0x80,
(byte)0x7c,(byte)0x7d,(byte)0x52,(byte)0x3d,(byte)0x6d,(byte)0x79,(byte)0x2f,(byte)0x6e,
(byte)0xbc,(byte)0x24,(byte)0xa4,(byte)0xec,(byte)0xf2,(byte)0xb3,(byte)0xa4,(byte)0x27,
(byte)0xcd,(byte)0xbb,(byte)0xfb},
"cb0082c8 f197d260 991ba6a4 60e76e20 2bad27b3");
System.out.println();
// See also http://csrc.ncsl.nist.gov/cryptval/shs/sha1-vectors.zip
{
final int RETRIES = 10;
final int ITERATIONS = 2000;
final int BLOCKSIZE = 65536;
byte[] input = new byte[BLOCKSIZE];
for (int i = BLOCKSIZE; --i >= 0; )
input[i] = (byte)i;
long best = 0;
for (int i = 0; i < 1000; i++) // training for stable measure
System.currentTimeMillis();
for (int retry = 0; retry < RETRIES; retry++) {
long t0 = System.currentTimeMillis();
for (int i = ITERATIONS; --i >= 0; );
long t1 = System.currentTimeMillis();
for (int i = ITERATIONS; --i >= 0; )
hash.engineUpdate(input, 0, BLOCKSIZE);
long t2 = System.currentTimeMillis();
long time = (t2 - t1) - (t1 - t0);
if (retry == 0 || time < best)
best = time;
}
hash.engineReset();
double rate = 1000.0 * ITERATIONS * BLOCKSIZE / best;
System.out.println("Our rate = " +
(float)(rate * 8) + " bits/s = " +
(float)(rate / (1024 * 1024)) + " Megabytes/s");
// Java 1.5 beta-b32c, on Athlon XP 1800+:
// with java -client: 48.21 Megabytes/s.
// with java -server: 68.23 Megabytes/s.
try {
MessageDigest md = MessageDigest.getInstance("SHA");
for (int retry = 0; retry < RETRIES; retry++) {
long t0 = System.currentTimeMillis();
for (int i = ITERATIONS; --i >= 0; );
long t1 = System.currentTimeMillis();
for (int i = ITERATIONS; --i >= 0; )
md.update(input, 0, BLOCKSIZE);
long t2 = System.currentTimeMillis();
long time = (t2 - t1) - (t1 - t0);
if (retry == 0 || time < best)
best = time;
}
md.reset();
rate = 1000.0 * ITERATIONS * BLOCKSIZE / best;
System.out.println("JCE rate = " +
(float)(rate * 8) + " bits/s = " +
(float)(rate / (1024 * 1024)) + " Megabytes/s");
} catch (NoSuchAlgorithmException nsae) {
System.out.println("No SHA algorithm in local JCE Security Providers");
}
// Java 1.5 beta-b32c, on Athlon XP 1800+:
// with java -client: 23.20 Megabytes/s.
// with java -server: 45.72 Megabytes/s.
}
}
private static final boolean tst(final int set, final int vector,
final String source,
final String expect) {
byte[] input = new byte[source.length()];
for (int i = 0; i < input.length; i++)
input[i] = (byte)source.charAt(i);
return tst(set, vector, input, expect);
}
private static final boolean tst(final int set, final int vector,
final byte[] input,
final String expect) {
System.out.print("Set " + set + ", vector# " + vector + ": ");
hash.engineUpdate(input, 0, input.length);
return tstResult(expect);
}
private static final boolean tst(final int set, final int vector,
final int times, final String source,
final String expect) {
byte[] input = new byte[source.length()];
for (int i = 0; i < input.length; i++)
input[i] = (byte)source.charAt(i);
System.out.print("Set " + set + ", vector# " + vector + ": ");
for (int i = 0; i < times; i++)
hash.engineUpdate(input, 0, input.length);
return tstResult(expect);
}
private static final boolean tstResult(String expect) {
final String result = toHex(hash.engineDigest());
expect = expect.toUpperCase();
if (!expect.equals(result)) {
System.out.println("**************** WRONG ***************");
System.out.println(" expect: " + expect);
System.out.println(" result: " + result);
return false;
}
System.out.println("OK");
return true;
}
private static final String toHex(final byte[] bytes) {
StringBuffer buf = new StringBuffer(bytes.length * 2);
for (int i = 0; i < bytes.length; i++) {
if ((i & 3) == 0 && i != 0)
buf.append(' ');
buf.append(HEX.charAt((bytes[i] >> 4) & 0xF))
.append(HEX.charAt( bytes[i] & 0xF));
}
return buf.toString();
}
private static final String HEX = "0123456789ABCDEF";
}

View File

@@ -23,8 +23,8 @@ import net.i2p.data.DataHelper;
import net.i2p.data.PublicKey;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer;
/**
* Implement the session key management, but keep everything in memory (don't write
@@ -66,10 +66,23 @@ class TransientSessionKeyManager extends SessionKeyManager {
super(context);
_log = context.logManager().getLog(TransientSessionKeyManager.class);
_context = context;
_outboundSessions = new HashMap(64);
_inboundTagSets = new HashMap(1024);
_outboundSessions = new HashMap(1024);
_inboundTagSets = new HashMap(64*1024);
context.statManager().createRateStat("crypto.sessionTagsExpired", "How many tags/sessions are expired?", "Encryption", new long[] { 10*60*1000, 60*60*1000, 3*60*60*1000 });
context.statManager().createRateStat("crypto.sessionTagsRemaining", "How many tags/sessions are remaining after a cleanup?", "Encryption", new long[] { 10*60*1000, 60*60*1000, 3*60*60*1000 });
SimpleTimer.getInstance().addEvent(new CleanupEvent(), 60*1000);
}
private TransientSessionKeyManager() { this(null); }
private class CleanupEvent implements SimpleTimer.TimedEvent {
public void timeReached() {
long beforeExpire = _context.clock().now();
int expired = aggressiveExpire();
long expireTime = _context.clock().now() - beforeExpire;
_context.statManager().addRateData("crypto.sessionTagsExpired", expired, expireTime);
SimpleTimer.getInstance().addEvent(CleanupEvent.this, 60*1000);
}
}
/** TagSet */
protected Set getInboundTagSets() {
@@ -248,6 +261,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
overage = _inboundTagSets.size() - MAX_INBOUND_SESSION_TAGS;
}
}
if (overage > 0)
clearExcess(overage);
@@ -362,45 +376,32 @@ class TransientSessionKeyManager extends SessionKeyManager {
*/
public int aggressiveExpire() {
int removed = 0;
int remaining = 0;
long now = _context.clock().now();
Set tagsToDrop = null; // new HashSet(64);
synchronized (_inboundTagSets) {
for (Iterator iter = _inboundTagSets.keySet().iterator(); iter.hasNext();) {
SessionTag tag = (SessionTag) iter.next();
TagSet ts = (TagSet) _inboundTagSets.get(tag);
if (ts.getDate() < now - SESSION_LIFETIME_MAX_MS) {
if (tagsToDrop == null)
tagsToDrop = new HashSet(4);
tagsToDrop.add(tag);
iter.remove();
removed++;
}
}
if (tagsToDrop != null) {
removed += tagsToDrop.size();
for (Iterator iter = tagsToDrop.iterator(); iter.hasNext();)
_inboundTagSets.remove(iter.next());
}
remaining = _inboundTagSets.size();
}
_context.statManager().addRateData("crypto.sessionTagsRemaining", remaining, 0);
//_log.warn("Expiring tags: [" + tagsToDrop + "]");
synchronized (_outboundSessions) {
Set sessionsToDrop = null;
for (Iterator iter = _outboundSessions.keySet().iterator(); iter.hasNext();) {
PublicKey key = (PublicKey) iter.next();
OutboundSession sess = (OutboundSession) _outboundSessions.get(key);
removed += sess.expireTags();
if (sess.getTagSets().size() <= 0) {
if (sessionsToDrop == null)
sessionsToDrop = new HashSet(4);
sessionsToDrop.add(key);
}
}
if (sessionsToDrop != null) {
for (Iterator iter = sessionsToDrop.iterator(); iter.hasNext();) {
OutboundSession cur = (OutboundSession)_outboundSessions.remove(iter.next());
if ( (cur != null) && (_log.shouldLog(Log.WARN)) )
_log.warn("Removing session tags with " + cur.availableTags() + " available for "
+ (cur.getLastExpirationDate()-_context.clock().now())
+ "ms more", new Exception("Removed by"));
if (sess.availableTags() <= 0) {
iter.remove();
removed++;
}
}
}

View File

@@ -0,0 +1,380 @@
package net.i2p.crypto;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Signature;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SigningPublicKey;
import net.i2p.util.Log;
/**
* Handles DSA signing and verification of I2P update archives.
*
* @author smeghead
*/
public class TrustedUpdate {
/**
* default trusted key, generated by jrandom. This can be authenticated
* via gpg without modification (gpg --verify TrustedUpdate.java)
*
*/
/*
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
*/
private static final String DEFAULT_TRUSTED_KEY =
"W4kJbnv9KSVwbnapV7SaNW2kMIZKs~hwL0ro9pZXFo1xTwqz45nykCp1H" +
"M7sAKYDZay5z1HvYYOl9CNVz00xF03KPU9RUCVxhDZ1YXhZIskPKjUPUs" +
"CIpE~Z1C~N9KSEV6~2stDlBNH10VZ4T0X1TrcXwb3IBXliWo2y2GAx~Ow=";
/*
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (GNU/Linux)
iD8DBQFCQXoJGnFL2th344YRAtsIAKCUy/sOIsxahUnT2hKLXFL9lXsAmACfUHa5
CPah6TDXYJCWmR0n3oPtrvo=
=mD0t
-----END PGP SIGNATURE-----
*/
private ArrayList _trustedKeys;
private I2PAppContext _context;
private Log _log;
private static final int VERSION_BYTES = 16;
private static final int HEADER_BYTES = VERSION_BYTES + Signature.SIGNATURE_BYTES;
public static final String PROP_TRUSTED_KEYS = "router.trustedUpdateKeys";
public TrustedUpdate() {
this(I2PAppContext.getGlobalContext());
}
public TrustedUpdate(I2PAppContext ctx) {
_context = ctx;
_log = _context.logManager().getLog(TrustedUpdate.class);
_trustedKeys = new ArrayList(1);
String keys = ctx.getProperty(PROP_TRUSTED_KEYS);
if ( (keys != null) && (keys.length() > 0) ) {
StringTokenizer tok = new StringTokenizer(keys, ", ");
while (tok.hasMoreTokens())
_trustedKeys.add(tok.nextToken());
} else {
_trustedKeys.add(DEFAULT_TRUSTED_KEY);
}
}
public ArrayList getTrustedKeys() { return _trustedKeys; }
public static void main(String[] args) {
if (args.length <= 0) {
usage();
} else if ("keygen".equals(args[0])) {
genKeysCLI(args[1], args[2]);
} else if ("sign".equals(args[0])) {
signCLI(args[1], args[2], args[3], args[4]);
} else if ("verify".equals(args[0])) {
verifyCLI(args[1]);
} else {
usage();
}
}
private static final void usage() {
System.err.println("Usage: TrustedUpdate keygen publicKeyFile privateKeyFile");
System.err.println(" TrustedUpdate sign origFile signedFile privateKeyFile version");
System.err.println(" TrustedUpdate verify signedFile");
}
private static final void genKeysCLI(String publicKeyFile, String privateKeyFile) {
FileOutputStream out = null;
try {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
Object keys[] = ctx.keyGenerator().generateSigningKeypair();
SigningPublicKey pub = (SigningPublicKey)keys[0];
SigningPrivateKey priv = (SigningPrivateKey)keys[1];
out = new FileOutputStream(publicKeyFile);
pub.writeBytes(out);
out.close();
out = new FileOutputStream(privateKeyFile);
priv.writeBytes(out);
out.close();
out = null;
System.out.println("Private keys writen to " + privateKeyFile + " and public to " + publicKeyFile);
System.out.println("Public: " + pub.toBase64());
} catch (Exception e) {
e.printStackTrace();
System.err.println("Error writing out the keys");
} finally {
if (out != null) try { out.close(); } catch (IOException ioe) {}
}
}
private static final void signCLI(String origFile, String outFile, String privKeyFile, String version) {
TrustedUpdate up = new TrustedUpdate();
Signature sig = up.sign(origFile, outFile, privKeyFile, version);
if (sig != null)
System.out.println("Signed and written to " + outFile);
else
System.out.println("Error signing");
}
private static final void verifyCLI(String signedFile) {
TrustedUpdate up = new TrustedUpdate();
boolean ok = up.verify(signedFile);
if (ok)
System.out.println("Signature VALID");
else
System.out.println("Signature INVALID");
}
/**
* Reads the version string from a signed I2P update file.
*
* @param inputFile A signed I2P update file.
*
* @return The update version string read, or an empty string if no version
* string is present.
*/
public String getUpdateVersion(String inputFile) {
FileInputStream in = null;
try {
in = new FileInputStream(inputFile);
byte data[] = new byte[VERSION_BYTES];
int read = DataHelper.read(in, data);
if (read != VERSION_BYTES)
return null;
for (int i = 0; i < VERSION_BYTES; i++)
if (data[i] == 0x00)
return new String(data, 0, i, "UTF-8");
return new String(data, "UTF-8");
} catch (UnsupportedEncodingException uee) {
// If this ever gets called, you need a new JVM.
throw new RuntimeException("wtf, your JVM doesnt support utf-8? " + uee.getMessage());
} catch (IOException ioe) {
return "";
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
}
/**
* Uses the given private key to sign the given input file with DSA. The
* output will be a binary file where the first 16 bytes are the I2P
* update's version string encoded in UTF-8 (padded with trailing
* <code>0h</code> characters if necessary), the next 40 bytes are the
* resulting DSA signature, and the remaining bytes are the input file.
*
* @param inputFile The file to be signed.
* @param outputFile The signed file to write.
* @param privateKeyFile The name of the file containing the private key to
* sign <code>inputFile</code> with.
* @param updateVersion The version number of the I2P update. If this
* string is longer than 16 characters it will be
* truncated.
*
* @return An instance of {@link net.i2p.data.Signature}, or null if there was an error
*/
public Signature sign(String inputFile, String outputFile, String privateKeyFile, String updateVersion) {
SigningPrivateKey key = new SigningPrivateKey();
FileInputStream in = null;
try {
in = new FileInputStream(privateKeyFile);
key.readBytes(in);
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to load the signing key", ioe);
return null;
} catch (DataFormatException dfe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to load the signing key", dfe);
return null;
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
return sign(inputFile, outputFile, key, updateVersion);
}
public Signature sign(String inputFile, String outputFile, SigningPrivateKey privKey, String updateVersion) {
byte[] headerUpdateVersion = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00 };
byte[] updateVersionBytes = null;
if (updateVersion.length() > VERSION_BYTES)
updateVersion = updateVersion.substring(0, VERSION_BYTES);
try {
updateVersionBytes = updateVersion.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
// If this ever gets called, you need a new JVM.
throw new RuntimeException("wtf, your JVM doesnt support utf-8? " + e.getMessage());
}
System.arraycopy(updateVersionBytes, 0, headerUpdateVersion, 0, updateVersionBytes.length);
Signature signature = null;
FileInputStream in = null;
try {
in = new FileInputStream(inputFile);
signature = _context.dsa().sign(in, privKey);
} catch (Exception e) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error signing", e);
return null;
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
in = null;
}
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(outputFile);
fileOutputStream.write(headerUpdateVersion);
fileOutputStream.write(signature.getData());
in = new FileInputStream(inputFile);
byte buf[] = new byte[1024];
int read = 0;
while ( (read = in.read(buf)) != -1)
fileOutputStream.write(buf, 0, read);
fileOutputStream.close();
fileOutputStream = null;
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.log(Log.WARN, "Error writing signed I2P update file " + outputFile, ioe);
return null;
} finally {
if (fileOutputStream != null) try { fileOutputStream.close(); } catch (IOException ioe) {}
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
return signature;
}
/**
* Verifies the DSA signature of a signed I2P update.
*
* @param inputFile The signed update file to check.
*
* @return <code>true</code> if the file has a valid signature.
*/
public boolean verify(String inputFile) {
for (int i = 0; i < _trustedKeys.size(); i++) {
SigningPublicKey key = new SigningPublicKey();
try {
key.fromBase64((String)_trustedKeys.get(i));
boolean ok = verify(inputFile, key);
if (ok) return true;
} catch (DataFormatException dfe) {
_log.log(Log.CRIT, "Trusted key " + i + " is not valid");
}
}
if (_log.shouldLog(Log.WARN))
_log.warn("None of the keys match");
return false;
}
/**
* Verifies the DSA signature of a signed I2P update.
*
* @param inputFile The signed update file to check.
* @param key public key to verify against
*
* @return <code>true</code> if the file has a valid signature.
*/
public boolean verify(String inputFile, SigningPublicKey key) {
FileInputStream in = null;
try {
in = new FileInputStream(inputFile);
byte version[] = new byte[VERSION_BYTES];
Signature sig = new Signature();
if (VERSION_BYTES != DataHelper.read(in, version))
throw new IOException("Not enough data for the version bytes");
sig.readBytes(in);
return _context.dsa().verifySignature(sig, in, key);
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error reading " + inputFile + " to verify", ioe);
return false;
} catch (DataFormatException dfe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error reading the signature", dfe);
return false;
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
}
/**
* Verifies the DSA signature of a signed I2P update.
*
* @param inputFile The signed update file to check.
* @param publicKeyFile The public key to use for verification.
*
* @return <code>true</code> if the file has a valid signature.
*/
public boolean verify(String inputFile, String publicKeyFile) {
SigningPublicKey pub = new SigningPublicKey();
FileInputStream in = null;
try {
in = new FileInputStream(inputFile);
pub.readBytes(in);
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to load the signature", ioe);
return false;
} catch (DataFormatException dfe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to load the signature", dfe);
return false;
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
return verify(inputFile, pub);
}
/**
* Verify the signature on the signed inputFile, and if it is valid, migrate
* the raw data out of it and into the outputFile
*
* @return true if the signature was valid and the data moved, false otherwise.
*/
public boolean migrateVerified(String inputFile, String outputFile) {
boolean ok = verify(inputFile);
if (!ok) return false;
FileOutputStream out = null;
FileInputStream in = null;
try {
out = new FileOutputStream(outputFile);
in = new FileInputStream(inputFile);
long skipped = 0;
while (skipped < HEADER_BYTES) {
skipped += in.skip(HEADER_BYTES - skipped);
}
byte buf[] = new byte[1024];
int read = 0;
while ( (read = in.read(buf)) != -1)
out.write(buf, 0, read);
} catch (IOException ioe) {
return false;
} finally {
if (out != null) try { out.close(); } catch (IOException ioe) {}
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
return true;
}
}

View File

@@ -42,6 +42,10 @@ public class Base64 {
private final static Log _log = new Log(Base64.class);
/** added by aum */
public static String encode(String source) {
return encode(source.getBytes());
}
public static String encode(byte[] source) {
return encode(source, 0, (source != null ? source.length : 0));
}
@@ -564,7 +568,7 @@ public class Base64 {
* @return The data as a string
* @since 1.4
*/
private static String decodeToString(String s) {
public static String decodeToString(String s) {
return new String(decode(s));
} // end decodeToString

View File

@@ -10,7 +10,6 @@ package net.i2p.data;
*/
import java.io.BufferedReader;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -32,11 +31,12 @@ import java.util.List;
import java.util.Properties;
import java.util.TreeMap;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import net.i2p.util.ByteCache;
import net.i2p.util.CachingByteArrayOutputStream;
import net.i2p.util.OrderedProperties;
import net.i2p.util.ReusableGZIPInputStream;
import net.i2p.util.ReusableGZIPOutputStream;
/**
* Defines some simple IO routines for dealing with marshalling data structures
@@ -361,7 +361,7 @@ public class DataHelper {
*/
public static void writeLong(OutputStream rawStream, int numBytes, long value)
throws DataFormatException, IOException {
if (value < 0) throw new DataFormatException("Value is negative (" + value + ")");
for (int i = numBytes - 1; i >= 0; i--) {
byte cur = (byte)( (value >>> (i*8) ) & 0xFF);
rawStream.write(cur);
@@ -369,6 +369,7 @@ public class DataHelper {
}
public static byte[] toLong(int numBytes, long value) throws IllegalArgumentException {
if (value < 0) throw new IllegalArgumentException("Negative value not allowed");
byte val[] = new byte[numBytes];
toLong(val, 0, numBytes, value);
return val;
@@ -397,43 +398,6 @@ public class DataHelper {
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 + ")");
read = readLong(new ByteArrayInputStream(written), numBytes);
if (read != value)
throw new RuntimeException("testLong("+numBytes+","+value+") FAILED on readLong (" + 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
@@ -863,20 +827,21 @@ public class DataHelper {
return rv;
}
private static final int MAX_UNCOMPRESSED = 40*1024;
/** 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;
if (size >= MAX_UNCOMPRESSED)
throw new IllegalArgumentException("tell jrandom size=" + size);
ReusableGZIPOutputStream out = ReusableGZIPOutputStream.acquire();
try {
CachingByteArrayOutputStream baos = new CachingByteArrayOutputStream(16, 40*1024);
GZIPOutputStream out = new GZIPOutputStream(baos, size);
out.write(orig, offset, size);
out.finish();
out.flush();
byte rv[] = baos.toByteArray();
baos.releaseBuffer();
byte rv[] = out.getData();
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Compression of " + orig.length + " into " + rv.length + " (or " + 100.0d
// * (((double) orig.length) / ((double) rv.length)) + "% savings)");
@@ -884,31 +849,35 @@ public class DataHelper {
} catch (IOException ioe) {
//_log.error("Error compressing?!", ioe);
return null;
} finally {
ReusableGZIPOutputStream.release(out);
}
}
/** decompress the GZIP compressed data (returning null on error) */
public static byte[] decompress(byte orig[]) throws IOException {
return (orig != null ? decompress(orig, 0, orig.length) : null);
}
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, offset, length), length);
CachingByteArrayOutputStream baos = new CachingByteArrayOutputStream(16, 40*1024);
ByteCache cache = ByteCache.getInstance(10, 4*1024);
ByteArray ba = cache.acquire();
byte buf[] = ba.getData(); // new byte[4 * 1024];
ReusableGZIPInputStream in = ReusableGZIPInputStream.acquire();
in.initialize(new ByteArrayInputStream(orig, offset, length));
ByteCache cache = ByteCache.getInstance(8, MAX_UNCOMPRESSED);
ByteArray outBuf = cache.acquire();
int written = 0;
while (true) {
int read = in.read(buf);
if (read == -1) break;
baos.write(buf, 0, read);
int read = in.read(outBuf.getData(), written, MAX_UNCOMPRESSED-written);
if (read == -1)
break;
written += read;
}
byte rv[] = baos.toByteArray();
cache.release(ba);
baos.releaseBuffer();
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Decompression of " + orig.length + " into " + rv.length + " (or " + 100.0d
// * (((double) rv.length) / ((double) orig.length)) + "% savings)");
byte rv[] = new byte[written];
System.arraycopy(outBuf.getData(), 0, rv, 0, written);
cache.release(outBuf);
ReusableGZIPInputStream.release(in);
return rv;
}

View File

@@ -12,7 +12,6 @@ package net.i2p.data;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
/**
* Defines the class as a standard object with particular bit representation,
@@ -20,7 +19,7 @@ import java.io.Serializable;
*
* @author jrandom
*/
public interface DataStructure extends Serializable {
public interface DataStructure /* extends Serializable */ {
/**
* Load up the current object with data from the given stream. Data loaded
* this way must match the I2P data structure specification.

View File

@@ -298,6 +298,16 @@ public class LeaseSet extends DataStructureImpl {
}
_signature.writeBytes(out);
}
public int size() {
return PublicKey.KEYSIZE_BYTES //destination.pubKey
+ SigningPublicKey.KEYSIZE_BYTES // destination.signPubKey
+ 2 // destination.certificate
+ PublicKey.KEYSIZE_BYTES // encryptionKey
+ SigningPublicKey.KEYSIZE_BYTES // signingKey
+ 1
+ _leases.size() * (Hash.HASH_LENGTH + 4 + 8);
}
public boolean equals(Object object) {
if ((object == null) || !(object instanceof LeaseSet)) return false;

View File

@@ -73,14 +73,16 @@ public class Payload extends DataStructureImpl {
_encryptedData = new byte[size];
int read = read(in, _encryptedData);
if (read != size) throw new DataFormatException("Incorrect number of bytes read in the payload structure");
_log.debug("read payload: " + read + " bytes");
if (_log.shouldLog(Log.DEBUG))
_log.debug("read payload: " + read + " bytes");
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_encryptedData == null) throw new DataFormatException("Not yet encrypted. Please set the encrypted data");
DataHelper.writeLong(out, 4, _encryptedData.length);
out.write(_encryptedData);
_log.debug("wrote payload: " + _encryptedData.length);
if (_log.shouldLog(Log.DEBUG))
_log.debug("wrote payload: " + _encryptedData.length);
}
public int writeBytes(byte target[], int offset) {
if (_encryptedData == null) throw new IllegalStateException("Not yet encrypted. Please set the encrypted data");

View File

@@ -91,7 +91,11 @@ public class CreateLeaseSetMessage extends I2CPMessageImpl {
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
if ((_sessionId == null) || (_signingPrivateKey == null) || (_privateKey == null) || (_leaseSet == null))
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
ByteArrayOutputStream os = new ByteArrayOutputStream(512);
int size = 4 // sessionId
+ SigningPrivateKey.KEYSIZE_BYTES
+ PrivateKey.KEYSIZE_BYTES
+ _leaseSet.size();
ByteArrayOutputStream os = new ByteArrayOutputStream(size);
try {
_sessionId.writeBytes(os);
_signingPrivateKey.writeBytes(os);

View File

@@ -9,7 +9,6 @@ package net.i2p.data.i2cp;
*
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

View File

@@ -64,14 +64,10 @@ public class ReceiveMessageBeginMessage extends I2CPMessageImpl {
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
if ((_sessionId == null) || (_messageId == null))
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
ByteArrayOutputStream os = new ByteArrayOutputStream(64);
try {
_sessionId.writeBytes(os);
_messageId.writeBytes(os);
} catch (DataFormatException dfe) {
throw new I2CPMessageException("Error writing out the message data", dfe);
}
return os.toByteArray();
byte rv[] = new byte[2+4];
DataHelper.toLong(rv, 0, 2, _sessionId.getSessionId());
DataHelper.toLong(rv, 2, 4, _messageId.getMessageId());
return rv;
}
public int getType() {

View File

@@ -64,14 +64,10 @@ public class ReceiveMessageEndMessage extends I2CPMessageImpl {
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
if ((_sessionId == null) || (_messageId == null))
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
ByteArrayOutputStream os = new ByteArrayOutputStream(64);
try {
_sessionId.writeBytes(os);
_messageId.writeBytes(os);
} catch (DataFormatException dfe) {
throw new I2CPMessageException("Error writing out the message data", dfe);
}
return os.toByteArray();
byte rv[] = new byte[2+4];
DataHelper.toLong(rv, 0, 2, _sessionId.getSessionId());
DataHelper.toLong(rv, 2, 4, _messageId.getMessageId());
return rv;
}
public int getType() {

View File

@@ -9,7 +9,6 @@ package net.i2p.data.i2cp;
*
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

View File

@@ -101,6 +101,15 @@ public class Timestamper implements Runnable {
t.start();
}
public void waitForInitialization() {
try {
synchronized (this) {
if (!_initialized)
wait();
}
} catch (InterruptedException ie) {}
}
public void run() {
try { Thread.sleep(1000); } catch (InterruptedException ie) {}
_log = _context.logManager().getLog(Timestamper.class);
@@ -125,11 +134,14 @@ public class Timestamper implements Runnable {
try {
lastFailed = !queryTime(serverList);
} catch (IllegalArgumentException iae) {
if (!lastFailed)
_log.log(Log.CRIT, "Unable to reach any of the NTP servers - network disconnected?");
if ( (!lastFailed) && (_log.shouldLog(Log.ERROR)) )
_log.error("Unable to reach any of the NTP servers - network disconnected?");
lastFailed = true;
}
}
_initialized = true;
synchronized (this) { notifyAll(); }
long sleepTime = _context.random().nextInt(_queryFrequency) + _queryFrequency;
if (lastFailed)
sleepTime = 30*1000;
@@ -137,6 +149,7 @@ public class Timestamper implements Runnable {
}
} catch (Throwable t) {
_log.log(Log.CRIT, "Timestamper died!", t);
synchronized (this) { notifyAll(); }
}
}
@@ -145,21 +158,18 @@ public class Timestamper implements Runnable {
*/
private boolean queryTime(String serverList[]) throws IllegalArgumentException {
long found[] = new long[_concurringServers];
long localTime = -1;
long now = -1;
long expectedDelta = 0;
for (int i = 0; i < _concurringServers; i++) {
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
localTime = _context.clock().now();
now = NtpClient.currentTime(serverList);
long delta = now - localTime;
long delta = now - _context.clock().now();
found[i] = delta;
if (i == 0) {
if (Math.abs(delta) < MAX_VARIANCE) {
if (_log.shouldLog(Log.INFO))
_log.info("a single SNTP query was within the tolerance (" + delta + "ms)");
return true;
break;
} else {
// outside the tolerance, lets iterate across the concurring queries
expectedDelta = delta;

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