Compare commits

...

65 Commits

Author SHA1 Message Date
4ce6b308b3 * 2005-08-03 0.6.0.1 released
2005-08-03  jrandom
    * Backed out an inadvertant change to the netDb store redundancy factor.
    * Verify tunnel participant caching.
    * Logging cleanup
2005-08-03 18:58:12 +00:00
72c6e7d1c5 2005-08-01 duck
* Update IzPack to 3.7.2 (build 2005.04.22). This fixes bug #82.
2005-08-02 03:26:51 +00:00
7ca3f22e77 2005-08-01 duck
* Update IzPack to 3.7.2 (build 2005.04.22)
      This fixes bug #82
2005-08-02 03:25:51 +00:00
59790dafef 2005-08-01 duck
* Fix an addressbook NPE when a new hostname from the master addressbook
      didn't exist in the router addressbook.
    * Fix an addressbook bug which caused subscriptions not to be parsed at
      all. (Oops!)
2005-08-01 13:35:11 +00:00
7227cae6ef 2005-08-01 duck
* Fix an addressbook NPE when a new hostname from the master addressbook
      didn't exist in the router addressbook.
    * Fix an addressbook bug which caused subscriptions not to be parsed at
      all. (Oops!)
2005-08-01 13:34:10 +00:00
03bba51c1e * Fixed some issues with the merge logic that caused addressbooks to be written to disk even when unmodified.
* Fixed a bug that could result in a downloaded remote addressbook not being deleted, halting the update process.
2005-08-01 03:32:37 +00:00
0637050cbc No real reason for eepget to retry, addressbook will try again in an hour, and it makes updates take an absurdly long time. 2005-08-01 00:10:54 +00:00
7f58a68c5a Whoops! Forgot the new build file. I broke cvs! 2005-07-31 22:49:25 +00:00
8120b0397c Move addressbook off URL and on to EepGet. Should no longer leak dns lookups, but now only supports conditional GET with HTTP 1.1. If that's a big problem, it can be fixed in future. 2005-07-31 22:19:10 +00:00
fbe42b7dce Added HTTP 1.1 conditional GET support to EepGet. 2005-07-31 22:17:10 +00:00
def24e34ad 2005-07-31 jrandom
* Adjust the netDb search and store per peer timeouts to match the average
      measured per peer success times, rather than huge fixed values.
    * Optimized and reverified the netDb peer selection / retrieval process
      within the kbuckets.
    * Drop TCP connections that don't have any useful activity in 10 minutes.
    * If i2np.udp.fixedPort=true, never change the externally published port,
      even if we are autodetecting the IP address.
(also includes most of the new peer/NAT testing, but thats not used atm)
2005-07-31 21:35:26 +00:00
593253e6a3 update compilation target 2005-07-31 01:11:12 +00:00
56dd4cb8b5 * added luckypunk.i2p to hosts.txt 2005-07-30 02:27:12 +00:00
10c6f67500 oops 2005-07-28 20:33:27 +00:00
5c1f968afa no message 2005-07-27 20:16:44 +00:00
aaaf437d62 skip properly (DataHelper.read confusion) 2005-07-27 20:15:35 +00:00
a8a866b5f6 * 2005-07-27 0.6 released
2005-07-27  jrandom
    * Enabled SSU as the default top priority transport, adjusting the
      config.jsp page accordingly.
    * Add verification fields to the SSU and TCP connection negotiation (not
      compatible with previous builds)
    * Enable the backwards incompatible tunnel crypto change as documented in
      tunnel-alt.html (have each hop encrypt the received IV before using it,
      then encrypt it again before sending it on)
    * Disable the I2CP encryption, leaving in place the end to end garlic
      encryption (another backwards incompatible change)
    * Adjust the protocol versions on the TCP and SSU transports so that they
      won't talk to older routers.
    * Fix up the config stats handling again
    * Fix a rare off-by-one in the SSU fragmentation
    * Reduce some unnecessary netDb resending by inluding the peers queried
      successfully in the store redundancy count.
2005-07-27 19:03:43 +00:00
aeb8f02269 2005-07-22 jrandom
* Use the small thread pool for I2PTunnelHTTPServer (already used for
      I2PTunnelServer)
    * Minor memory churn reduction in I2CP
    * Small stats update
2005-07-23 00:15:56 +00:00
45767360ab 2005-07-21 jrandom
* Fix in the SDK for a bug which would manifest itself as misrouted
      streaming packets when a destination has many concurrent streaming
      connections (thanks duck!)
    * No more "Graceful shutdown in -18140121441141s"
2005-07-21 22:37:14 +00:00
3563aa2e4d 2005-07-20 jrandom
* Allow the user to specify an external port # for SSU even if the external
      host isn't specified (thanks duck!)
2005-07-20 19:24:47 +00:00
843d5b625a 2005-07-19 jrandom
* Further preparation for removing I2CP crypto
    * Added some validation to the DH key agreement (thanks $anon)
    * Validate tunnel data message expirations (though not really a problem,
      since tunnels expire)
    * Minor PRNG threading cleanup
2005-07-19 21:00:25 +00:00
0f8ede85ca 2005-07-15 cervantes
* Added workaround for an odd win32 bug in the stats configuration
	  console page which meant only the first checkbox selection was saved.

2005-07-15  Romster
	* Added per group selection toggles in the stats configuration console
	  page.
2005-07-16 12:52:35 +00:00
9267d7cae2 more n3ws 2005-07-13 21:59:01 +00:00
dade5a981b 2005-07-13 jrandom
* Fixed a recently injected bug in the multitransport bidding which had
      allowed an essentially arbitrary choice of transports, rather than the
      properly ordered choice.
(getLatency() != getLatencyMs().  duh)
2005-07-13 20:07:31 +00:00
f873cba27e 2005-07-13 jrandom
* Fixed a long standing bug where we weren't properly comparing session
      tags but instead largely depending upon comparing their hashCode,
      causing intermittent decryption errors.
2005-07-13 18:20:43 +00:00
108dec53a5 * mixing a revert and some logging updates... (crosses fingers) 2005-07-12 22:30:13 +00:00
e9592ed400 2005-07-12 jrandom
* Add some data duplication to avoid a recently injected concurrency problem
      in the session tag manager (thanks redzara and romster).
2005-07-12 21:26:07 +00:00
4c230522a2 typo *ahem* 2005-07-12 03:56:42 +00:00
16bd19c6dc added bash.i2p, stats.i2p 2005-07-11 23:23:22 +00:00
b4b6d49d34 ssu testing 2005-07-11 23:16:41 +00:00
9d5f16a889 2005-07-11 jrandom
* Reduced the growth factor on the slow start and congestion avoidance for
      the streaming lib.
    * Adjusted some of the I2PTunnelServer threading to use a small pool of
      handlers, rather than launching off new threads which then immediately
      launch off an I2PTunnelRunner instance (which launches 3 more threads..)
    * Don't persist session keys / session tags (not worth it, for now)
    * Added some detection and handling code for duplicate session tags being
      delivered (root cause still not addressed)
    * Make the PRNG's buffer size configurable (via the config property
      "i2p.prng.totalBufferSizeKB=4096")
    * Disable SSU flooding by default (duh)
    * Updates to the StreamSink apps for better throttling tests.
2005-07-11 23:06:23 +00:00
51c492b842 no message 2005-07-09 23:02:19 +00:00
d3380228ac * you mean 3f != 0x3f? [duh]
* minor cleanups
2005-07-09 22:58:22 +00:00
ad47bf5da3 * moved the inbound partial messages to the PeerState itself, reducing lock contention in the InboundMessageFragments and transparently dropping failed messages when we drop old peer states 2005-07-07 22:27:44 +00:00
76e8631e31 included IV tagging info 2005-07-07 21:16:57 +00:00
f688b9112d 2005-07-05
* Use a buffered PRNG, pulling the PRNG data off a larger precalculated
      buffer, rather than the underlying PRNG's (likely small) one, which in
      turn reduces the frequency of recalcing.
    * More tuning to reduce temporary allocation churn
2005-07-05 22:08:56 +00:00
18d3f5d25d 2005-07-04 jrandom
* Within the tunnel, use xor(IV, msg[0:16]) as the flag to detect dups,
      rather than the IV by itself, preventing an attack that would let
      colluding internal adversaries tag a message to determine that they are
      in the same tunnel.  Thanks dvorak for the catch!
    * Drop long inactive profiles on startup and shutdown
    * /configstats.jsp: web interface to pick what stats to log
    * Deliver more session tags to account for wider window sizes
    * Cache some intermediate values in our HMACSHA256 and BC's HMAC
    * Track the client send rate (stream.sendBps and client.sendBpsRaw)
    * UrlLauncher: adjust the browser selection order
    * I2PAppContext: hooks for dummy HMACSHA256 and a weak PRNG
    * StreamSinkClient: add support for sending an unlimited amount of data
    * Migrate the tests out of the default build jars

2005-06-22  Comwiz
    * Migrate the core tests to junit
2005-07-04 20:44:17 +00:00
440cf2c983 2005-03-23 Comwiz
* Phase 1 of the unit test bounty completed. (The router build script was modified not to build the router
 tests because of a broken dependancy on the core tests. This should be fixed in
 phase 3 of the unit test bounty.)
2005-06-23 02:11:04 +00:00
adeb09576a util/PooledRandomSource.java 2005-06-03 20:23:32 +00:00
fd52bcf8cd added archive.i2p, www.fr.i2p, romster.i2p, marshmallow.i2p, openforums.i2p 2005-05-26 04:51:24 +00:00
c2696bba00 2005-05-25 duck
* Fixed PRNG bug (bugzilla #107)
2005-05-25 21:32:38 +00:00
fef9d57483 removed duplicate manveru.i2p 2005-05-10 05:53:18 +00:00
c250692ef0 added bittorrent.i2p - new home for brittanytracker 2005-05-04 05:52:55 +00:00
2a6024e196 end of first round of ssu testing 2005-05-03 00:53:53 +00:00
835662b3c9 2005-05-01 jrandom
* Added a substantial optimization to the AES engine by caching the
      prepared session keys (duh).
2005-05-02 02:35:16 +00:00
6b5b880ab6 * replaced explicit NACKs and numACKs with ACK bitfields for high congestion links
* increased the maximum number of fragments allowed in a message from 31 to 127,
  reducing the maximum fragment size to 8KB and moving around some bits in the fragment
  info.  This is not backwards compatible.
* removed the old (hokey) congestion control description, replacing it with the TCP-esque
  algorithm implemented
note: the code for the ACK bitfields and fragment info changes have not yet been
implemented, so the old version of this document describes whats going on in the live net.
the new bitfields / fragment info should be deployed in the next day or so (hopefully :)
2005-05-01 20:08:08 +00:00
3de23d4206 2005-05-01 jrandom
* Cleaned up the peers page a bit more.
more udp stuff:
* add new config option: i2np.udp.alwaysPreferred=true to adjust the bidding
  so that UDP is picked first, even if a TCP connection exists
* fixed the initial clock skew problem (duh)
* reduced the MTU to 576 (largest nearly-universally-safe, and allows a
  tunnel message in 2 fragments)
* handle some races @ connection establishment (thanks duck!)
* if there are more ACKs than we can send in a packet, reschedule another
  ACK immediately
2005-05-01 17:21:48 +00:00
ea82f2a8cc oops (thanks newkid!) 2005-05-01 01:35:23 +00:00
b5ad7642bc 2005-04-30 jrandom
* Added a small new page to the web console (/peers.jsp) which contains
      the peer connection information.  This will be cleaned up a lot more
      before 0.6 is out, but its a start.
2005-05-01 00:48:15 +00:00
0fbe84e9f0 2005-04-30 jrandom
* Reduced some SimpleTimer churn
* add hooks for per-peer choking in the outbound message queue - if/when a
  peer reaches their cwin, no further messages will enter the 'active' pool
  until there are more bytes available.  other messages waiting (either later
  on in the same priority queue, or in the queues for other priorities) may
  take that slot.
* when we have a message acked, release the acked size to the congestion
  window (duh), rather than waiting for the second to expire and refill the
  capacity.
* send packets in a volley explicitly, waiting until we can allocate the full
  cwin size for that message
2005-04-30 23:26:18 +00:00
8063889d23 udp updates:
* more stats. including per-peer KBps (updated every second)
* improved blocking/timeout situations on the send queue
* added drop simulation hook
* provide logical RTO limits
2005-04-30 03:14:09 +00:00
6e1ac8e173 added elf.i2p, de-ebooks.i2p, i2pchan.i2p, longhorn.i2p 2005-04-29 22:26:12 +00:00
1b0bb5ea19 2005-04-29 jrandom
* Reduce the peer profile stat coallesce overhead by inlining it with the
      reorganize.
    * Limit each transport to at most one address (any transport that requires
      multiple entry points can include those alternatives in the address).
udp stuff:
* change the UDP transport's style from "udp" to "SSUv1"
* keep track of each peer's skew
* properly handle session reestablishment over an existing session, rather
  than requiring both sides to expire first
2005-04-29 06:24:12 +00:00
4ce51261f1 2005-04-28 jrandom
* More fixes for the I2PTunnel "other" interface handling (thanks nelgin!)
    * Add back the code to handle bids from multiple transports (though there
      is still only one transport enabled by default)
    * Adjust the router's queueing of outbound client messages when under
      heavy load by running the preparatory job in the client's I2CP handler
      thread, thereby blocking additional outbound messages when the router is
      hosed.
    * No need to validate or persist a netDb entry if we already have it
And for some udp stuff:
* only bid on what we know (duh)
* reduceed the queue size in the UDPSender itself, so that ACKs go
  through more quickly, leaving the payload messages to queue up in
  the outbound fragment scheduler
* rather than /= 2 on congestion, /= 2/3 (still AIMD, but less drastic)
* adjust the fragment selector so a wsiz throttle won't force extra
  volleys
* mark congestion when it occurs, not after the message has been
  ACKed
* when doing a round robin over the active messages, move on to the
  next after a full volley, not after each packet (causing less "fair"
  performance but better latency)
* reduced the lock contention in the inboundMessageFragments by
  moving the ack and complete queues to the ACKSender and
  MessageReceiver respectively (each of which have their own
  threads)
* prefer new and existing UDP sessions to new TCP sessions, but
  prefer existing TCP sessions to new UDP sessions
2005-04-28 21:54:27 +00:00
6e34d9b73e added amobius.i2p 2005-04-28 02:11:02 +00:00
6e01637400 added google.i2p 2005-04-27 21:30:53 +00:00
9a96798f9f added mrplod.i2p 2005-04-27 03:58:00 +00:00
c9db6f87d1 2005-04-25 smeghead
* Added button to router console for manual update checks.
    * Fixed bug in configupdate.jsp that caused the proxy port to be updated
      every time the form was submitted even if it hadn't changed.
2005-04-26 02:59:23 +00:00
567ce84e1e * randomized the shitlist duration (still with exponential backoff though)
* fail UDP sessions after two consecutive failed messages in different minutes
* honor UDP reconnections
2005-04-25 16:29:48 +00:00
cde7ac7e52 2005-04-24 jrandom
* Added a pool of PRNGs using a different synchronization technique,
      hopefully sufficient to work around IBM's PRNG bugs until we get our
      own Fortuna.
    * In the streaming lib, don't jack up the RTT on NACK, and have the window
      size bound the not-yet-ready messages to the peer, not the unacked
      message count (not sure yet whether this is worthwile).
    * Many additions to the messageHistory log.
    * Handle out of order tunnel fragment delivery (not an issue on the live
      net with TCP, but critical with UDP).
2005-04-24 18:44:59 +00:00
b2f0d17e94 2005-04-24 jrandom
* Added a pool of PRNGs using a different synchronization technique,
      hopefully sufficient to work around IBM's PRNG bugs until we get our
      own Fortuna.
    * In the streaming lib, don't jack up the RTT on NACK, and have the window
      size bound the not-yet-ready messages to the peer, not the unacked
      message count (not sure yet whether this is worthwile).
    * Many additions to the messageHistory log.
    * Handle out of order tunnel fragment delivery (not an issue on the live
      net with TCP, but critical with UDP).
and for udp stuff:
* implemented tcp-esque rto code in the udp transport
* make sure we don't ACK too many messages at once
* transmit fragments in a simple (nonrandom) order so that we can more easily
  adjust timeouts/etc.
* let the active outbound pool grow dynamically if there are outbound slots to
  spare
* use a simple decaying bloom filter at the UDP level to drop duplicate resent
  packets.
2005-04-24 18:42:02 +00:00
dae6be14b7 I removed those dumb platform specific makefiles. They weren't doing what they ought anyway. If there are platform specific issues, someone please tell me and I'll provide support for it here. Or patch it yourself.
And this is the big "Fix the Parser" patch.  It turns the sam_parse function in src/parse.c into something that actually works.  Generating the argument list from an incoming SAM thingy is a bit memory churn-y; perhaps when I have time I'll replace all those strdups with structures that simply track the (start,end) indices.
Oh and also I moved i2p-ping to the new system.  Which required 0 change in code.  All I did was fix the Makefile, and add shared library libtool support.  Anyway, so enjoy folks.  It's rare I'm this productive
- polecat
2005-04-23 03:28:40 +00:00
20cec857d2 signed with the latest 2005-04-21 16:26:46 +00:00
aum
739f694cfe Node shutdown now uses halt() 2005-04-21 03:10:16 +00:00
aum
84779002fb now builds a working Q console 2005-04-20 21:35:05 +00:00
291 changed files with 11271 additions and 4990 deletions

View File

@ -6,8 +6,7 @@
<property name="dist" location="dist"/>
<property name="jar" value="addressbook.jar"/>
<property name="war" value="addressbook.war"/>
<property name="servlet" value="../jetty/jettylib/javax.servlet.jar"/>
<target name="init">
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>
@ -22,7 +21,12 @@
<target name="compile" depends="init">
<javac debug="true" deprecation="on" source="1.3" target="1.3"
srcdir="${src}" destdir="${build}" classpath="${servlet}"/>
srcdir="${src}" destdir="${build}">
<classpath>
<pathelement location="../../core/java/build/i2p.jar" />
<pathelement location="../jetty/jettylib/javax.servlet.jar" />
</classpath>
</javac>
</target>
<target name="jar" depends="compile">

View File

@ -24,11 +24,12 @@ package addressbook;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.net.URL;
import java.net.HttpURLConnection;
import java.io.File;
import java.io.IOException;
import net.i2p.I2PAppContext;
import net.i2p.util.EepGet;
/**
* An address book for storing human readable names mapped to base64 i2p
* destinations. AddressBooks can be created from local and remote files, merged
@ -65,14 +66,18 @@ public class AddressBook {
* where key is a human readable name, and value is a base64 i2p
* destination.
*/
public AddressBook(URL url) {
this.location = url.getHost();
public AddressBook(String url, String proxyHost, int proxyPort) {
this.location = url;
EepGet get = new EepGet(I2PAppContext.getGlobalContext(), true,
proxyHost, proxyPort, 0, "addressbook.tmp", url, true,
null);
get.fetch();
try {
this.addresses = ConfigParser.parse(url);
this.addresses = ConfigParser.parse(new File("addressbook.tmp"));
} catch (IOException exp) {
this.addresses = new HashMap();
}
new File("addressbook.tmp").delete();
}
/**
@ -83,43 +88,19 @@ public class AddressBook {
* @param subscription
* A Subscription instance pointing at a remote address book.
*/
public AddressBook(Subscription subscription) {
public AddressBook(Subscription subscription, String proxyHost, int proxyPort) {
this.location = subscription.getLocation();
try {
URL url = new URL(subscription.getLocation());
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
if (subscription.getEtag() != null) {
connection.addRequestProperty("If-None-Match", subscription
.getEtag());
}
if (subscription.getLastModified() != null) {
connection.addRequestProperty("If-Modified-Since", subscription
.getLastModified());
}
connection.connect();
if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
connection.disconnect();
this.addresses = new HashMap();
return;
}
if (connection.getHeaderField("ETag") != null) {
subscription.setEtag(connection.getHeaderField("ETag"));
}
if (connection.getHeaderField("Last-Modified") != null) {
subscription.setLastModified(connection
.getHeaderField("Last-Modified"));
}
} catch (IOException exp) {
}
try {
this.addresses = ConfigParser.parse(new URL(subscription
.getLocation()));
EepGet get = new EepGet(I2PAppContext.getGlobalContext(), true,
proxyHost, proxyPort, 0, "addressbook.tmp",
subscription.getLocation(), true, subscription.getEtag());
get.fetch();
subscription.setEtag(get.getETag());
try {
this.addresses = ConfigParser.parse(new File("addressbook.tmp"));
} catch (IOException exp) {
this.addresses = new HashMap();
}
new File("addressbook.tmp").delete();
}
/**
@ -181,7 +162,7 @@ public class AddressBook {
* @param log
* The log to write messages about new addresses or conflicts to.
*/
public void merge(AddressBook other, Log log) {
public void merge(AddressBook other, boolean overwrite, Log log) {
Iterator otherIter = other.addresses.keySet().iterator();
while (otherIter.hasNext()) {
@ -189,7 +170,7 @@ public class AddressBook {
String otherValue = (String) other.addresses.get(otherKey);
if (otherKey.endsWith(".i2p") && otherValue.length() >= 516) {
if (this.addresses.containsKey(otherKey)) {
if (this.addresses.containsKey(otherKey) && !overwrite) {
if (!this.addresses.get(otherKey).equals(otherValue)
&& log != null) {
log.append("Conflict for " + otherKey + " from "
@ -197,28 +178,19 @@ public class AddressBook {
+ ". Destination in remote address book is "
+ otherValue);
}
} else {
} else if (!this.addresses.containsKey(otherKey)
|| !this.addresses.get(otherKey).equals(otherValue)) {
this.addresses.put(otherKey, otherValue);
this.modified = true;
if (log != null) {
log.append("New address " + otherKey
+ " added to address book.");
+ " added to address book.");
}
}
}
}
}
/**
* Merge this AddressBook with other, without logging.
*
* @param other
* An AddressBook to merge with.
*/
public void merge(AddressBook other) {
this.merge(other, null);
}
/**
* Write the contents of this AddressBook out to the File file. If the file
* cannot be writen to, this method will silently fail.
@ -243,4 +215,4 @@ public class AddressBook {
public void write() {
this.write(new File(this.location));
}
}
}

View File

@ -27,7 +27,6 @@ import java.util.List;
import java.util.LinkedList;
import java.util.Iterator;
import java.io.*;
import java.net.URL;
/**
* Utility class providing methods to parse and write files in config file
@ -86,24 +85,6 @@ public class ConfigParser {
return result;
}
/**
* Return a Map using the contents of the file at url. See
* parseBufferedReader for details of the input format.
*
* @param url
* A url pointing to a file to parse.
* @return A Map containing the key, value pairs from url.
* @throws IOException
* if url cannot be read.
*/
public static Map parse(URL url) throws IOException {
InputStream urlStream;
urlStream = url.openConnection().getInputStream();
BufferedReader input = new BufferedReader(new InputStreamReader(
urlStream));
return ConfigParser.parse(input);
}
/**
* Return a Map using the contents of the File file. See parseBufferedReader
* for details of the input format.

View File

@ -58,15 +58,14 @@ public class Daemon {
*/
public static void update(AddressBook master, AddressBook router,
File published, SubscriptionList subscriptions, Log log) {
String routerLocation = router.getLocation();
master.merge(router);
router.merge(master, true, null);
Iterator iter = subscriptions.iterator();
while (iter.hasNext()) {
master.merge((AddressBook) iter.next(), log);
router.merge((AddressBook) iter.next(), false, log);
}
master.write(new File(routerLocation));
router.write();
if (published != null)
master.write(published);
router.write(published);
subscriptions.write();
}
@ -101,7 +100,8 @@ public class Daemon {
defaultSubs.add("http://i2p/NF2RLVUxVulR3IqK0sGJR0dHQcGXAzwa6rEO4WAWYXOHw-DoZhKnlbf1nzHXwMEJoex5nFTyiNMqxJMWlY54cvU~UenZdkyQQeUSBZXyuSweflUXFqKN-y8xIoK2w9Ylq1k8IcrAFDsITyOzjUKoOPfVq34rKNDo7fYyis4kT5bAHy~2N1EVMs34pi2RFabATIOBk38Qhab57Umpa6yEoE~rbyR~suDRvD7gjBvBiIKFqhFueXsR2uSrPB-yzwAGofTXuklofK3DdKspciclTVzqbDjsk5UXfu2nTrC1agkhLyqlOfjhyqC~t1IXm-Vs2o7911k7KKLGjB4lmH508YJ7G9fLAUyjuB-wwwhejoWqvg7oWvqo4oIok8LG6ECR71C3dzCvIjY2QcrhoaazA9G4zcGMm6NKND-H4XY6tUWhpB~5GefB3YczOqMbHq4wi0O9MzBFrOJEOs3X4hwboKWANf7DT5PZKJZ5KorQPsYRSq0E3wSOsFCSsdVCKUGsAAAA/i2p/hosts.txt");
SubscriptionList subscriptions = new SubscriptionList(subscriptionFile,
etagsFile, lastModifiedFile, defaultSubs);
etagsFile, lastModifiedFile, defaultSubs, (String) settings
.get("proxy_host"), Integer.parseInt((String) settings.get("proxy_port")));
Log log = new Log(logFile);
Daemon.update(master, router, published, subscriptions, log);
@ -154,11 +154,6 @@ public class Daemon {
while (true) {
settings = ConfigParser.parse(settingsFile, defaultSettings);
System.setProperty("proxySet", "true");
System.setProperty("http.proxyHost", (String) settings
.get("proxy_host"));
System.setProperty("http.proxyPort", (String) settings
.get("proxy_port"));
long delay = Long.parseLong((String) settings.get("update_delay"));
if (delay < 1) {
delay = 1;

View File

@ -33,6 +33,8 @@ import java.util.List;
public class SubscriptionIterator implements Iterator {
private Iterator subIterator;
private String proxyHost;
private int proxyPort;
/**
* Construct a SubscriptionIterator using the Subscriprions in List subscriptions.
@ -40,8 +42,10 @@ public class SubscriptionIterator implements Iterator {
* @param subscriptions
* List of Subscription objects that represent address books.
*/
public SubscriptionIterator(List subscriptions) {
public SubscriptionIterator(List subscriptions, String proxyHost, int proxyPort) {
this.subIterator = subscriptions.iterator();
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
}
@ -49,15 +53,15 @@ public class SubscriptionIterator implements Iterator {
* @see java.util.Iterator#hasNext()
*/
public boolean hasNext() {
return subIterator.hasNext();
return this.subIterator.hasNext();
}
/* (non-Javadoc)
* @see java.util.Iterator#next()
*/
public Object next() {
Subscription sub = (Subscription) subIterator.next();
return new AddressBook(sub);
Subscription sub = (Subscription) this.subIterator.next();
return new AddressBook(sub, this.proxyHost, this.proxyPort);
}
/* (non-Javadoc)

View File

@ -42,6 +42,10 @@ public class SubscriptionList {
private File etagsFile;
private File lastModifiedFile;
private String proxyHost;
private int proxyPort;
/**
* Construct a SubscriptionList using the urls from locationsFile and, if
@ -58,15 +62,18 @@ public class SubscriptionList {
* GET. The file is in the format "url=leastmodified".
*/
public SubscriptionList(File locationsFile, File etagsFile,
File lastModifiedFile, List defaultSubs) {
File lastModifiedFile, List defaultSubs, String proxyHost,
int proxyPort) {
this.subscriptions = new LinkedList();
this.etagsFile = etagsFile;
this.lastModifiedFile = lastModifiedFile;
List locations;
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
Map etags;
Map lastModified;
String location;
locations = ConfigParser.parseSubscriptions(locationsFile, defaultSubs);
List locations = ConfigParser.parseSubscriptions(locationsFile,
defaultSubs);
try {
etags = ConfigParser.parse(etagsFile);
} catch (IOException exp) {
@ -80,11 +87,9 @@ public class SubscriptionList {
Iterator iter = locations.iterator();
while (iter.hasNext()) {
location = (String) iter.next();
subscriptions.add(new Subscription(location, (String) etags
this.subscriptions.add(new Subscription(location, (String) etags
.get(location), (String) lastModified.get(location)));
}
iter = this.iterator();
}
/**
@ -94,7 +99,8 @@ public class SubscriptionList {
* @return A SubscriptionIterator.
*/
public SubscriptionIterator iterator() {
return new SubscriptionIterator(this.subscriptions);
return new SubscriptionIterator(this.subscriptions, this.proxyHost,
this.proxyPort);
}
/**

View File

@ -1146,6 +1146,8 @@ public class I2PTunnel implements Logging, EventDispatcher {
}
private String getPrefix() { return '[' + _tunnelId + "]: "; }
public I2PAppContext getContext() { return _context; }
/**
* Call this whenever we lose touch with the router involuntarily (aka the router

View File

@ -110,7 +110,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
}
}
if (sockMgr == null) {
_log.log(Log.CRIT, "Unable to create socket manager");
_log.log(Log.CRIT, "Unable to create socket manager (our own? " + ownDest + ")");
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
}

View File

@ -35,85 +35,66 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
public I2PTunnelHTTPServer(InetAddress host, int port, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host, port, privData, l, notifyThis, tunnel);
_spoofHost = spoofHost;
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 });
}
public I2PTunnelHTTPServer(InetAddress host, int port, File privkey, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host, port, privkey, privkeyname, l, notifyThis, tunnel);
_spoofHost = spoofHost;
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 });
}
public I2PTunnelHTTPServer(InetAddress host, int port, InputStream privData, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host, port, privData, privkeyname, l, notifyThis, tunnel);
_spoofHost = spoofHost;
_spoofHost = spoofHost;
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 });
}
public void run() {
try {
I2PServerSocket i2pss = sockMgr.getServerSocket();
while (true) {
I2PSocket i2ps = i2pss.accept();
if (i2ps == null) throw new I2PException("I2PServerSocket closed");
I2PThread t = new I2PThread(new Handler(i2ps));
t.start();
}
} catch (I2PException ex) {
_log.error("Error while waiting for I2PConnections", ex);
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
}
/**
* Async handler to keep .accept() from blocking too long.
* todo: replace with a thread pool so we dont get overrun by threads if/when
* receiving a lot of connection requests concurrently.
* Called by the thread pool of I2PSocket handlers
*
*/
private class Handler implements Runnable {
private I2PSocket _handleSocket;
public Handler(I2PSocket socket) {
_handleSocket = socket;
}
public void run() {
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
long afterSocket = -1;
//local is fast, so synchronously. Does not need that many
//threads.
protected void blockingHandle(I2PSocket socket) {
long afterAccept = getTunnel().getContext().clock().now();
long afterSocket = -1;
//local is fast, so synchronously. Does not need that many
//threads.
try {
socket.setReadTimeout(readTimeout);
String modifiedHeader = getModifiedHeader(socket);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Modified header: [" + modifiedHeader + "]");
Socket s = new Socket(remoteHost, remotePort);
afterSocket = getTunnel().getContext().clock().now();
new I2PTunnelRunner(s, socket, slock, null, modifiedHeader.getBytes(), null);
} catch (SocketException ex) {
try {
_handleSocket.setReadTimeout(readTimeout);
String modifiedHeader = getModifiedHeader();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Modified header: [" + modifiedHeader + "]");
Socket s = new Socket(remoteHost, remotePort);
afterSocket = I2PAppContext.getGlobalContext().clock().now();
new I2PTunnelRunner(s, _handleSocket, slock, null, modifiedHeader.getBytes(), null);
} catch (SocketException ex) {
try {
_handleSocket.close();
} catch (IOException ioe) {
socket.close();
} catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error while closing the received i2p con", ex);
}
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
long afterHandle = I2PAppContext.getGlobalContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
if (timeToHandle > 1000)
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: "
+ (afterSocket-afterAccept) + "]");
}
private String getModifiedHeader() throws IOException {
InputStream in = _handleSocket.getInputStream();
StringBuffer command = new StringBuffer(128);
Properties headers = readHeaders(in, command);
headers.setProperty("Host", _spoofHost);
headers.setProperty("Connection", "close");
return formatHeaders(headers, command);
} catch (IOException ex) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error while receiving the new HTTP request", ex);
}
long afterHandle = getTunnel().getContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
getTunnel().getContext().statManager().addRateData("i2ptunnel.httpserver.blockingHandleTime", timeToHandle, 0);
if ( (timeToHandle > 1000) && (_log.shouldLog(Log.WARN)) )
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
}
private String getModifiedHeader(I2PSocket handleSocket) throws IOException {
InputStream in = handleSocket.getInputStream();
StringBuffer command = new StringBuffer(128);
Properties headers = readHeaders(in, command);
headers.setProperty("Host", _spoofHost);
headers.setProperty("Connection", "close");
return formatHeaders(headers, command);
}
private String formatHeaders(Properties headers, StringBuffer command) {

View File

@ -159,58 +159,91 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
}
}
private static final String PROP_HANDLER_COUNT = "i2ptunnel.blockingHandlerCount";
private static final int DEFAULT_HANDLER_COUNT = 10;
protected int getHandlerCount() {
int rv = DEFAULT_HANDLER_COUNT;
String cnt = getTunnel().getClientOptions().getProperty(PROP_HANDLER_COUNT);
if (cnt != null) {
try {
rv = Integer.parseInt(cnt);
if (rv <= 0)
rv = DEFAULT_HANDLER_COUNT;
} catch (NumberFormatException nfe) {
rv = DEFAULT_HANDLER_COUNT;
}
}
return rv;
}
public void run() {
try {
I2PServerSocket i2pss = sockMgr.getServerSocket();
int handlers = getHandlerCount();
for (int i = 0; i < handlers; i++) {
I2PThread handler = new I2PThread(new Handler(i2pss), "Handle Server " + i);
handler.start();
}
/*
while (true) {
I2PSocket i2ps = i2pss.accept();
if (i2ps == null) throw new I2PException("I2PServerSocket closed");
I2PThread t = new I2PThread(new Handler(i2ps));
t.start();
}
} catch (I2PException ex) {
_log.error("Error while waiting for I2PConnections", ex);
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
*/
}
/**
* Async handler to keep .accept() from blocking too long.
* todo: replace with a thread pool so we dont get overrun by threads if/when
* receiving a lot of connection requests concurrently.
* minor thread pool to pull off the accept() concurrently. there are still lots
* (and lots) of wasted threads within the I2PTunnelRunner, but its a start
*
*/
private class Handler implements Runnable {
private I2PSocket _handleSocket;
public Handler(I2PSocket socket) {
_handleSocket = socket;
private I2PServerSocket _serverSocket;
public Handler(I2PServerSocket serverSocket) {
_serverSocket = serverSocket;
}
public void run() {
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
long afterSocket = -1;
//local is fast, so synchronously. Does not need that many
//threads.
try {
_handleSocket.setReadTimeout(readTimeout);
Socket s = new Socket(remoteHost, remotePort);
afterSocket = I2PAppContext.getGlobalContext().clock().now();
new I2PTunnelRunner(s, _handleSocket, slock, null, null);
} catch (SocketException ex) {
while (open) {
try {
_handleSocket.close();
} catch (IOException ioe) {
_log.error("Error while closing the received i2p con", ex);
blockingHandle(_serverSocket.accept());
} catch (I2PException ex) {
_log.error("Error while waiting for I2PConnections", ex);
return;
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
return;
}
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
long afterHandle = I2PAppContext.getGlobalContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
if (timeToHandle > 1000)
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
}
}
protected void blockingHandle(I2PSocket socket) {
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
long afterSocket = -1;
//local is fast, so synchronously. Does not need that many
//threads.
try {
socket.setReadTimeout(readTimeout);
Socket s = new Socket(remoteHost, remotePort);
afterSocket = I2PAppContext.getGlobalContext().clock().now();
new I2PTunnelRunner(s, socket, slock, null, null);
} catch (SocketException ex) {
try {
socket.close();
} catch (IOException ioe) {
_log.error("Error while closing the received i2p con", ex);
}
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
long afterHandle = I2PAppContext.getGlobalContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
if (timeToHandle > 1000)
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
}
}

View File

@ -1,445 +0,0 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.Clock;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Quick and dirty socket listener to control an I2PTunnel.
* Basically run this class as TunnelManager [listenHost] [listenPort] and
* then send it commands on that port. Commands are one shot deals -
* Send a command + newline, get a response plus newline, then get disconnected.
* <p />
* <b>Implemented commands:</b>
* <pre>
* -------------------------------------------------
* lookup &lt;name&gt;\n
* --
* &lt;base64 of the destination&gt;\n
* or
* &lt;error message, usually 'Unknown host'&gt;\n
*
* Lookup the public key of a named destination (i.e. listed in hosts.txt)
* -------------------------------------------------
* genkey\n
* --
* &lt;base64 of the destination&gt;\t&lt;base64 of private data&gt;\n
*
* Generates a new public and private key pair
* -------------------------------------------------
* convertprivate &lt;base64 of privkey&gt;
* --
* &lt;base64 of destination&gt;\n
* or
* &lt;error message&gt;\n
*
* Returns the destination (pubkey) of a given private key.
* -------------------------------------------------
* listen_on &lt;ip&gt;\n
* --
* ok\n
* or
* error\n
*
* Sets the ip address clients will listen on. By default this is the
* localhost (127.0.0.1)
* -------------------------------------------------
* openclient &lt;listenPort&gt; &lt;peer&gt;[ &lt;sharedClient&gt;]\n
* --
* ok [&lt;jobId&gt;]\n
* or
* ok &lt;listenPort&gt; [&lt;jobId&gt;]\n
* or
* error\n
*
* Open a tunnel on the given &lt;listenport&gt; to the destination specified
* by &lt;peer&gt;. If &lt;listenPort&gt; is 0 a free port is picked and returned in
* the reply message. Otherwise the short reply message is used.
* Peer can be the base64 of the destination, a file with the public key
* specified as 'file:&lt;filename&gt;' or the name of a destination listed in
* hosts.txt. The &lt;jobId&gt; returned together with "ok" and &lt;listenport&gt; can
* later be used as argument for the "close" command.
* &lt;sharedClient&gt; indicates if this httpclient shares tunnels with other
* clients or not (just use 'true' and 'false'
* -------------------------------------------------
* openhttpclient &lt;listenPort&gt; [&lt;sharedClient&gt;] [&lt;proxy&gt;]\n
* --
* ok [&lt;jobId&gt;]\n
* or
* ok &lt;listenPort&gt; [&lt;jobId&gt;]\n
* or
* error\n
*
* Open an HTTP proxy through the I2P on the given
* &lt;listenport&gt;. &lt;proxy&gt; (optional) specifies a
* destination to be used as an outbound proxy, to access normal WWW
* sites out of the .i2p domain. If &lt;listenPort&gt; is 0 a free
* port is picked and returned in the reply message. Otherwise the
* short reply message is used. &lt;proxy&gt; can be the base64 of the
* destination, a file with the public key specified as
* 'file:&lt;filename&gt;' or the name of a destination listed in
* hosts.txt. The &lt;jobId&gt; returned together with "ok" and
* &lt;listenport&gt; can later be used as argument for the "close"
* command.
* &lt;sharedClient&gt; indicates if this httpclient shares tunnels with other
* clients or not (just use 'true' and 'false'
* -------------------------------------------------
* opensockstunnel &lt;listenPort&gt;\n
* --
* ok [&lt;jobId&gt;]\n
* or
* ok &lt;listenPort&gt; [&lt;jobId&gt;]\n
* or
* error\n
*
* Open an SOCKS tunnel through the I2P on the given
* &lt;listenport&gt;. If &lt;listenPort&gt; is 0 a free port is
* picked and returned in the reply message. Otherwise the short
* reply message is used. The &lt;jobId&gt; returned together with
* "ok" and &lt;listenport&gt; can later be used as argument for the
* "close" command.
* -------------------------------------------------
* openserver &lt;serverHost&gt; &lt;serverPort&gt; &lt;serverKeys&gt;\n
* --
* ok [&lt;jobId&gt;]\n
* or
* error\n
*
* Starts receiving traffic for the destination specified by &lt;serverKeys&gt;
* and forwards it to the &lt;serverPort&gt; of &lt;serverHost&gt;.
* &lt;serverKeys&gt; is the base 64 encoded private key set of the local
* destination. The &lt;joId&gt; returned together with "ok" can later be used
* as argument for the "close" command.
* -------------------------------------------------
* close [forced] &lt;jobId&gt;\n
* or
* close [forced] all\n
* --
* ok\n
* or
* error\n
*
* Closes the job specified by &lt;jobId&gt; or all jobs. Use the list command
* for a list of running jobs.
* Normally a connection job is not closed when it still has an active
* connection. Use the optional 'forced' keyword to close connections
* regardless of their use.
* -------------------------------------------------
* list\n
* --
* Example output:
*
* [0] i2p.dnsalias.net/69.55.226.145:5555 &lt;- C:\i2pKeys\squidPriv
* [1] 8767 -&gt; HTTPClient
* [2] 7575 -&gt; file:C:\i2pKeys\squidPub
* [3] 5252 -&gt; sCcSANIO~f4AQtCNI1BvDp3ZBS~9Ag5O0k0Msm7XBWWz5eOnZWL3MQ-2rxlesucb9XnpASGhWzyYNBpWAfaIB3pux1J1xujQLOwscMIhm7T8BP76Ly5jx6BLZCYrrPj0BI0uV90XJyT~4UyQgUlC1jzFQdZ9HDgBPJDf1UI4-YjIwEHuJgdZynYlQ1oUFhgno~HhcDByXO~PDaO~1JDMDbBEfIh~v6MgmHp-Xchod1OfKFrxFrzHgcJbn7E8edTFjZA6JCi~DtFxFelQz1lSBd-QB1qJnA0g-pVL5qngNUojXJCXs4qWcQ7ICLpvIc-Fpfj-0F1gkVlGDSGkb1yLH3~8p4czYgR3W5D7OpwXzezz6clpV8kmbd~x2SotdWsXBPRhqpewO38coU4dJG3OEUbuYmdN~nJMfWbmlcM1lXzz2vBsys4sZzW6dV3hZnbvbfxNTqbdqOh-KXi1iAzXv7CVTun0ubw~CfeGpcAqutC5loRUq7Mq62ngOukyv8Z9AAAA
*
* Lists descriptions of all running jobs. The exact format of the
* description depends on the type of job.
* -------------------------------------------------
* </pre>
*
*
* @deprecated this isn't run by default, and no one seems to use it, and has
* lots of things to maintain. so, at some point this may dissapear
* unless someone pipes up ;)
*/
public class TunnelManager implements Runnable {
private final static Log _log = new Log(TunnelManager.class);
private I2PTunnel _tunnel;
private ServerSocket _socket;
private boolean _keepAccepting;
public TunnelManager(int listenPort) {
this(null, listenPort);
}
public TunnelManager(String listenHost, int listenPort) {
_tunnel = new I2PTunnel();
_keepAccepting = true;
try {
if (listenHost != null) {
_socket = new ServerSocket(listenPort, 0, InetAddress.getByName(listenHost));
_log.info("Listening for tunnel management clients on " + listenHost + ":" + listenPort);
} else {
_socket = new ServerSocket(listenPort);
_log.info("Listening for tunnel management clients on localhost:" + listenPort);
}
} catch (Exception e) {
_log.error("Error starting up tunnel management listener on " + listenPort, e);
}
}
public static void main(String args[]) {
int port = 7676;
String host = null;
if (args.length == 1) {
try {
port = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
_log.error("Usage: TunnelManager [host] [port]");
return;
}
} else if (args.length == 2) {
host = args[0];
try {
port = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
_log.error("Usage: TunnelManager [host] [port]");
return;
}
}
TunnelManager mgr = new TunnelManager(host, port);
Thread t = new I2PThread(mgr, "Listener");
t.start();
}
public void run() {
if (_socket == null) {
_log.error("Unable to start listening, since the socket was not bound. Already running?");
return;
}
_log.debug("Running");
try {
while (_keepAccepting) {
Socket socket = _socket.accept();
_log.debug("Client accepted");
if (socket != null) {
Thread t = new I2PThread(new TunnelManagerClientRunner(this, socket));
t.setName("TunnelManager Client");
t.setPriority(I2PThread.MIN_PRIORITY);
t.start();
}
}
} catch (IOException ioe) {
_log.error("Error accepting connections", ioe);
} catch (Exception e) {
_log.error("Other error?!", e);
} finally {
if (_socket != null) try {
_socket.close();
} catch (IOException ioe) {
}
}
try {
Thread.sleep(5000);
} catch (InterruptedException ie) {
}
}
public void error(String msg, OutputStream out) throws IOException {
out.write(msg.getBytes());
out.write('\n');
}
public void processQuit(OutputStream out) throws IOException {
out.write("Nice try".getBytes());
out.write('\n');
}
public void processList(OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
long startCommand = Clock.getInstance().now();
_tunnel.runCommand("list", buf);
Object obj = _tunnel.waitEventValue("listDone");
long endCommand = Clock.getInstance().now();
String str = buf.getBuffer();
_log.debug("ListDone complete after " + (endCommand - startCommand) + "ms: [" + str + "]");
out.write(str.getBytes());
out.write('\n');
buf.ignoreFurtherActions();
}
public void processListenOn(String ip, OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("listen_on " + ip, buf);
String status = (String) _tunnel.waitEventValue("listen_onResult");
out.write((status + "\n").getBytes());
buf.ignoreFurtherActions();
}
/**
* "lookup <name>" returns with the result in base64, else "Unknown host" [or something like that],
* then a newline.
*
*/
public void processLookup(String name, OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("lookup " + name, buf);
String rv = (String) _tunnel.waitEventValue("lookupResult");
out.write(rv.getBytes());
out.write('\n');
buf.ignoreFurtherActions();
}
public void processTestDestination(String destKey, OutputStream out) throws IOException {
try {
Destination d = new Destination();
d.fromBase64(destKey);
out.write("valid\n".getBytes());
} catch (DataFormatException dfe) {
out.write("invalid\n".getBytes());
}
out.flush();
}
public void processConvertPrivate(String priv, OutputStream out) throws IOException {
try {
Destination dest = new Destination();
dest.fromBase64(priv);
String str = dest.toBase64();
out.write(str.getBytes());
out.write('\n');
} catch (DataFormatException dfe) {
_log.error("Error converting private data", dfe);
out.write("Error converting private key\n".getBytes());
}
}
public void processClose(String which, boolean forced, OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand((forced ? "close forced " : "close ") + which, buf);
String str = (String) _tunnel.waitEventValue("closeResult");
out.write((str + "\n").getBytes());
buf.ignoreFurtherActions();
}
/**
* "genkey" returns with the base64 of the destination, followed by a tab, then the base64 of that
* destination's private keys, then a newline.
*
*/
public void processGenKey(OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("gentextkeys", buf);
String priv = (String) _tunnel.waitEventValue("privateKey");
String pub = (String) _tunnel.waitEventValue("publicDestination");
out.write((pub + "\t" + priv).getBytes());
out.write('\n');
buf.ignoreFurtherActions();
}
public void processOpenClient(int listenPort, String peer, String sharedClient, OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("client " + listenPort + " " + peer + " " + sharedClient, buf);
Integer taskId = (Integer) _tunnel.waitEventValue("clientTaskId");
if (taskId.intValue() < 0) {
out.write("error\n".getBytes());
buf.ignoreFurtherActions();
return;
}
String rv = (String) _tunnel.waitEventValue("openClientResult");
if (rv.equals("error")) {
out.write((rv + "\n").getBytes());
buf.ignoreFurtherActions();
return;
}
if (listenPort != 0) {
out.write((rv + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
return;
}
Integer port = (Integer) _tunnel.waitEventValue("clientLocalPort");
out.write((rv + " " + port.intValue() + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
}
public void processOpenHTTPClient(int listenPort, String sharedClient, String proxy, OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("httpclient " + listenPort + " " + sharedClient + " " + proxy, buf);
Integer taskId = (Integer) _tunnel.waitEventValue("httpclientTaskId");
if (taskId.intValue() < 0) {
out.write("error\n".getBytes());
buf.ignoreFurtherActions();
return;
}
String rv = (String) _tunnel.waitEventValue("openHTTPClientResult");
if (rv.equals("error")) {
out.write((rv + "\n").getBytes());
buf.ignoreFurtherActions();
return;
}
if (listenPort != 0) {
out.write((rv + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
return;
}
Integer port = (Integer) _tunnel.waitEventValue("clientLocalPort");
out.write((rv + " " + port.intValue() + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
}
public void processOpenSOCKSTunnel(int listenPort, OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("sockstunnel " + listenPort, buf);
Integer taskId = (Integer) _tunnel.waitEventValue("sockstunnelTaskId");
if (taskId.intValue() < 0) {
out.write("error\n".getBytes());
buf.ignoreFurtherActions();
return;
}
String rv = (String) _tunnel.waitEventValue("openSOCKSTunnelResult");
if (rv.equals("error")) {
out.write((rv + "\n").getBytes());
buf.ignoreFurtherActions();
return;
}
if (listenPort != 0) {
out.write((rv + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
return;
}
Integer port = (Integer) _tunnel.waitEventValue("clientLocalPort");
out.write((rv + " " + port.intValue() + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
}
public void processOpenServer(String serverHost, int serverPort, String privateKeys, OutputStream out)
throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("textserver " + serverHost + " " + serverPort + " " + privateKeys, buf);
Integer taskId = (Integer) _tunnel.waitEventValue("serverTaskId");
if (taskId.intValue() < 0) {
out.write("error\n".getBytes());
buf.ignoreFurtherActions();
return;
}
String rv = (String) _tunnel.waitEventValue("openServerResult");
if (rv.equals("error")) {
out.write((rv + "\n").getBytes());
buf.ignoreFurtherActions();
return;
}
out.write((rv + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
}
/**
* Frisbee.
*
*/
public void unknownCommand(String command, OutputStream out) throws IOException {
out.write("Unknown command: ".getBytes());
out.write(command.getBytes());
out.write("\n".getBytes());
}
}

View File

@ -1,203 +0,0 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.StringTokenizer;
import net.i2p.util.Log;
/**
* Runner thread that reads commands from the socket and fires off commands to
* the TunnelManager
*
*/
class TunnelManagerClientRunner implements Runnable {
private final static Log _log = new Log(TunnelManagerClientRunner.class);
private TunnelManager _mgr;
private Socket _clientSocket;
public TunnelManagerClientRunner(TunnelManager mgr, Socket socket) {
_clientSocket = socket;
_mgr = mgr;
}
public void run() {
_log.debug("Client running");
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(_clientSocket.getInputStream()));
OutputStream out = _clientSocket.getOutputStream();
String cmd = reader.readLine();
if (cmd != null) processCommand(cmd, out);
} catch (IOException ioe) {
_log.error("Error processing client commands", ioe);
} finally {
if (_clientSocket != null) try {
_clientSocket.close();
} catch (IOException ioe) {
}
}
_log.debug("Client closed");
}
/**
* Parse the command string and fire off the appropriate tunnelManager method,
* sending the results to the output stream
*/
private void processCommand(String command, OutputStream out) throws IOException {
_log.debug("Processing [" + command + "]");
StringTokenizer tok = new StringTokenizer(command);
if (!tok.hasMoreTokens()) {
_mgr.unknownCommand(command, out);
} else {
String cmd = tok.nextToken();
if ("quit".equalsIgnoreCase(cmd)) {
_mgr.processQuit(out);
} else if ("lookup".equalsIgnoreCase(cmd)) {
if (tok.hasMoreTokens())
_mgr.processLookup(tok.nextToken(), out);
else
_mgr.error("Usage: lookup <hostname>", out);
} else if ("testdestination".equalsIgnoreCase(cmd)) {
if (tok.hasMoreTokens())
_mgr.processTestDestination(tok.nextToken(), out);
else
_mgr.error("Usage: testdestination <publicDestination>", out);
} else if ("convertprivate".equalsIgnoreCase(cmd)) {
if (tok.hasMoreTokens())
_mgr.processConvertPrivate(tok.nextToken(), out);
else
_mgr.error("Usage: convertprivate <privateData>", out);
} else if ("close".equalsIgnoreCase(cmd)) {
if (tok.hasMoreTokens()) {
String closeArg;
if ((closeArg = tok.nextToken()).equals("forced")) {
if (tok.hasMoreTokens()) {
_mgr.processClose(tok.nextToken(), true, out);
} else {
_mgr.error("Usage: close [forced] <jobnumber>|all", out);
}
} else {
_mgr.processClose(closeArg, false, out);
}
} else {
_mgr.error("Usage: close [forced] <jobnumber>|all", out);
}
} else if ("genkey".equalsIgnoreCase(cmd)) {
_mgr.processGenKey(out);
} else if ("list".equalsIgnoreCase(cmd)) {
_mgr.processList(out);
} else if ("listen_on".equalsIgnoreCase(cmd)) {
if (tok.hasMoreTokens()) {
_mgr.processListenOn(tok.nextToken(), out);
} else {
_mgr.error("Usage: listen_on <ip>", out);
}
} else if ("openclient".equalsIgnoreCase(cmd)) {
int listenPort = 0;
String peer = null;
String sharedClient = null;
int numTokens = tok.countTokens();
if (numTokens < 2 || numTokens > 3) {
_mgr.error("Usage: openclient <listenPort> <peer> <sharedClient>", out);
return;
}
try {
listenPort = Integer.parseInt(tok.nextToken());
peer = tok.nextToken();
if (tok.hasMoreTokens())
sharedClient = tok.nextToken();
else
sharedClient = "true";
_mgr.processOpenClient(listenPort, peer, sharedClient, out);
} catch (NumberFormatException nfe) {
_mgr.error("Bad listen port", out);
return;
}
} else if ("openhttpclient".equalsIgnoreCase(cmd)) {
int listenPort = 0;
String proxy = "squid.i2p";
String sharedClient = "true";
int numTokens = tok.countTokens();
if (numTokens < 1 || numTokens > 3) {
_mgr.error("Usage: openhttpclient <listenPort> [<sharedClient>] [<proxy>]", out);
return;
}
try {
listenPort = Integer.parseInt(tok.nextToken());
if (tok.hasMoreTokens()) {
String val = tok.nextToken();
if (tok.hasMoreTokens()) {
sharedClient = val;
proxy = tok.nextToken();
} else {
if ( ("true".equals(val)) || ("false".equals(val)) ) {
sharedClient = val;
} else {
proxy = val;
}
}
}
_mgr.processOpenHTTPClient(listenPort, sharedClient, proxy, out);
} catch (NumberFormatException nfe) {
_mgr.error("Bad listen port", out);
return;
}
} else if ("opensockstunnel".equalsIgnoreCase(cmd)) {
int listenPort = 0;
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: opensockstunnel <listenPort>", out);
return;
}
try {
String portStr = tok.nextToken();
listenPort = Integer.parseInt(portStr);
} catch (NumberFormatException nfe) {
_mgr.error("Bad listen port", out);
return;
}
if (tok.hasMoreTokens()) {
_mgr.error("Usage: opensockstunnel <listenport>", out);
return;
}
_mgr.processOpenSOCKSTunnel(listenPort, out);
} else if ("openserver".equalsIgnoreCase(cmd)) {
int listenPort = 0;
String serverHost = null;
String serverKeys = null;
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: openserver <serverHost> <serverPort> <serverKeys>", out);
return;
}
serverHost = tok.nextToken();
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: openserver <serverHost> <serverPort> <serverKeys>", out);
return;
}
try {
String portStr = tok.nextToken();
listenPort = Integer.parseInt(portStr);
} catch (NumberFormatException nfe) {
_mgr.error("Bad listen port", out);
return;
}
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: openserver <serverHost> <serverPort> <serverKeys>", out);
return;
}
serverKeys = tok.nextToken();
_mgr.processOpenServer(serverHost, listenPort, serverKeys, out);
} else {
_mgr.unknownCommand(command, out);
}
}
}
}

View File

@ -93,7 +93,7 @@ if (curTunnel >= 0) {
</select>
&nbsp;&nbsp;
<b>others:</b>
<input type="text" name="reachablyByOther" size="20" />
<input type="text" name="reachableByOther" size="20" />
<% } else if ("0.0.0.0".equals(clientInterface)) { %>
<option value="127.0.0.1">Locally (127.0.0.1)</option>
<option value="0.0.0.0" selected="true">Everyone (0.0.0.0)</option>
@ -102,7 +102,7 @@ if (curTunnel >= 0) {
</select>
&nbsp;&nbsp;
<b>others:</b>
<input type="text" name="reachablyByOther" size="20" />
<input type="text" name="reachableByOther" size="20" />
<% } else { %>
<option value="127.0.0.1">Locally (127.0.0.1)</option>
<option value="0.0.0.0">Everyone (0.0.0.0)</option>

View File

@ -15,6 +15,7 @@ import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.data.Destination;
import net.i2p.data.DataFormatException;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
@ -74,61 +75,67 @@ public class StreamSinkClient {
} finally {
if (fis == null) try { fis.close(); } catch (IOException ioe) {}
}
System.out.println("Send " + _sendSize + "KB to " + peer.calculateHash().toBase64());
try {
I2PSocket sock = mgr.connect(peer);
byte buf[] = new byte[32*1024];
Random rand = new Random();
OutputStream out = sock.getOutputStream();
long beforeSending = System.currentTimeMillis();
for (int i = 0; i < _sendSize; i+= 32) {
rand.nextBytes(buf);
out.write(buf);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Send " + _sendSize + "KB to " + peer.calculateHash().toBase64());
while (true) {
try {
I2PSocket sock = mgr.connect(peer);
byte buf[] = new byte[Math.min(32*1024, _sendSize*1024)];
Random rand = new Random();
OutputStream out = sock.getOutputStream();
long beforeSending = System.currentTimeMillis();
for (int i = 0; (_sendSize < 0) || (i < _sendSize); i+= buf.length/1024) {
rand.nextBytes(buf);
out.write(buf);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Wrote " + ((1+i*buf.length)/1024) + "/" + _sendSize + "KB");
if (_writeDelay > 0) {
try { Thread.sleep(_writeDelay); } catch (InterruptedException ie) {}
}
}
sock.close();
long afterSending = System.currentTimeMillis();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Wrote " + (i+32) + "/" + _sendSize + "KB");
if (_writeDelay > 0) {
try { Thread.sleep(_writeDelay); } catch (InterruptedException ie) {}
}
}
sock.close();
long afterSending = System.currentTimeMillis();
System.out.println("Sent " + _sendSize + "KB in " + (afterSending-beforeSending) + "ms");
} catch (InterruptedIOException iie) {
_log.error("Timeout connecting to the peer", iie);
return;
} catch (NoRouteToHostException nrthe) {
_log.error("Unable to connect to the peer", nrthe);
return;
} catch (ConnectException ce) {
_log.error("Connection already dropped", ce);
return;
} catch (I2PException ie) {
_log.error("Error connecting to the peer", ie);
return;
} catch (IOException ioe) {
_log.error("IO error sending", ioe);
return;
_log.debug("Sent " + _sendSize + "KB in " + (afterSending-beforeSending) + "ms");
} catch (InterruptedIOException iie) {
_log.error("Timeout connecting to the peer", iie);
//return;
} catch (NoRouteToHostException nrthe) {
_log.error("Unable to connect to the peer", nrthe);
//return;
} catch (ConnectException ce) {
_log.error("Connection already dropped", ce);
//return;
} catch (I2PException ie) {
_log.error("Error connecting to the peer", ie);
return;
} catch (IOException ioe) {
_log.error("IO error sending", ioe);
return;
}
}
}
/**
* Fire up the client. <code>Usage: StreamSinkClient [i2cpHost i2cpPort] sendSizeKB writeDelayMs serverDestFile</code> <br />
* Fire up the client. <code>Usage: StreamSinkClient [i2cpHost i2cpPort] sendSizeKB writeDelayMs serverDestFile [concurrentSends]</code> <br />
* <ul>
* <li><b>sendSizeKB</b>: how many KB to send</li>
* <li><b>sendSizeKB</b>: how many KB to send, or -1 for unlimited</li>
* <li><b>writeDelayMs</b>: how long to wait between each .write (0 for no delay)</li>
* <li><b>serverDestFile</b>: file containing the StreamSinkServer's binary Destination</li>
* <li><b>concurrentSends</b>: how many concurrent threads should send to the server at once</li>
* </ul>
*/
public static void main(String args[]) {
StreamSinkClient client = null;
int sendSizeKB = -1;
int writeDelayMs = -1;
int concurrent = 1;
switch (args.length) {
case 3:
case 3: // fall through
case 4:
try {
sendSizeKB = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
@ -141,9 +148,13 @@ public class StreamSinkClient {
System.err.println("Write delay ms invalid [" + args[1] + "]");
return;
}
if (args.length == 4) {
try { concurrent = Integer.parseInt(args[3]); } catch (NumberFormatException nfe) {}
}
client = new StreamSinkClient(sendSizeKB, writeDelayMs, args[2]);
break;
case 5:
case 5: // fall through
case 6:
try {
int port = Integer.parseInt(args[1]);
sendSizeKB = Integer.parseInt(args[2]);
@ -152,11 +163,26 @@ public class StreamSinkClient {
} catch (NumberFormatException nfe) {
System.err.println("arg error");
}
if (args.length == 6) {
try { concurrent = Integer.parseInt(args[5]); } catch (NumberFormatException nfe) {}
}
break;
default:
System.out.println("Usage: StreamSinkClient [i2cpHost i2cpPort] sendSizeKB writeDelayMs serverDestFile");
System.out.println("Usage: StreamSinkClient [i2cpHost i2cpPort] sendSizeKB writeDelayMs serverDestFile [concurrentSends]");
}
if (client != null) {
for (int i = 0; i < concurrent; i++)
new I2PThread(new Runner(client), "Client " + i).start();
}
}
private static class Runner implements Runnable {
private StreamSinkClient _client;
public Runner(StreamSinkClient client) {
_client = client;
}
public void run() {
_client.runClient();
}
if (client != null)
client.runClient();
}
}

View File

@ -6,6 +6,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import net.i2p.I2PAppContext;
@ -26,6 +28,7 @@ public class StreamSinkServer {
private String _destFile;
private String _i2cpHost;
private int _i2cpPort;
private int _handlers;
/**
* Create but do not start the streaming server.
@ -34,13 +37,14 @@ public class StreamSinkServer {
* @param ourDestFile filename to write our binary destination to
*/
public StreamSinkServer(String sinkDir, String ourDestFile) {
this(sinkDir, ourDestFile, null, -1);
this(sinkDir, ourDestFile, null, -1, 3);
}
public StreamSinkServer(String sinkDir, String ourDestFile, String i2cpHost, int i2cpPort) {
public StreamSinkServer(String sinkDir, String ourDestFile, String i2cpHost, int i2cpPort, int handlers) {
_sinkDir = sinkDir;
_destFile = ourDestFile;
_i2cpHost = i2cpHost;
_i2cpPort = i2cpPort;
_handlers = handlers;
_log = I2PAppContext.getGlobalContext().logManager().getLog(StreamSinkServer.class);
}
@ -56,7 +60,8 @@ public class StreamSinkServer {
else
mgr = I2PSocketManagerFactory.createManager();
Destination dest = mgr.getSession().getMyDestination();
System.out.println("Listening for connections on: " + dest.calculateHash().toBase64());
if (_log.shouldLog(Log.INFO))
_log.info("Listening for connections on: " + dest.calculateHash().toBase64());
FileOutputStream fos = null;
try {
fos = new FileOutputStream(_destFile);
@ -72,24 +77,16 @@ public class StreamSinkServer {
}
I2PServerSocket sock = mgr.getServerSocket();
while (true) {
try {
I2PSocket curSock = sock.accept();
handle(curSock);
} catch (I2PException ie) {
_log.error("Error accepting connection", ie);
return;
} catch (ConnectException ce) {
_log.error("Connection already dropped", ce);
return;
}
}
startup(sock);
}
private void handle(I2PSocket socket) {
I2PThread t = new I2PThread(new ClientRunner(socket));
t.setName("Handle " + socket.getPeerDestination().calculateHash().toBase64().substring(0,4));
t.start();
public void startup(I2PServerSocket sock) {
for (int i = 0; i < _handlers; i++) {
I2PThread t = new I2PThread(new ClientRunner(sock));
t.setName("Handler " + i);
t.setDaemon(false);
t.start();
}
}
/**
@ -97,27 +94,44 @@ public class StreamSinkServer {
*
*/
private class ClientRunner implements Runnable {
private I2PSocket _sock;
private FileOutputStream _fos;
public ClientRunner(I2PSocket socket) {
_sock = socket;
private I2PServerSocket _socket;
public ClientRunner(I2PServerSocket socket) {
_socket = socket;
}
public void run() {
while (true) {
try {
I2PSocket socket = _socket.accept();
if (socket != null)
handle(socket);
} catch (I2PException ie) {
_log.error("Error accepting connection", ie);
return;
} catch (ConnectException ce) {
_log.error("Connection already dropped", ce);
return;
}
}
}
private void handle(I2PSocket sock) {
FileOutputStream fos = null;
try {
File sink = new File(_sinkDir);
if (!sink.exists())
sink.mkdirs();
File cur = File.createTempFile("clientSink", ".dat", sink);
_fos = new FileOutputStream(cur);
System.out.println("Writing to " + cur.getAbsolutePath());
fos = new FileOutputStream(cur);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Writing to " + cur.getAbsolutePath());
} catch (IOException ioe) {
_log.error("Error creating sink", ioe);
_fos = null;
return;
}
}
public void run() {
if (_fos == null) return;
long start = System.currentTimeMillis();
try {
InputStream in = _sock.getInputStream();
InputStream in = sock.getInputStream();
byte buf[] = new byte[4096];
long written = 0;
int read = 0;
@ -125,47 +139,55 @@ public class StreamSinkServer {
//_fos.write(buf, 0, read);
written += read;
if (_log.shouldLog(Log.DEBUG))
_log.debug("read and wrote " + read);
_log.debug("read and wrote " + read + " (" + written + ")");
}
_fos.write(("written: [" + written + "]\n").getBytes());
fos.write(("written: [" + written + "]\n").getBytes());
long lifetime = System.currentTimeMillis() - start;
_log.error("Got EOF from client socket [written=" + written + " lifetime=" + lifetime + "]");
_log.info("Got EOF from client socket [written=" + written + " lifetime=" + lifetime + "]");
} catch (IOException ioe) {
_log.error("Error writing the sink", ioe);
} finally {
if (_fos != null) try { _fos.close(); } catch (IOException ioe) {}
if (_sock != null) try { _sock.close(); } catch (IOException ioe) {}
_log.error("Client socket closed");
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
if (sock != null) try { sock.close(); } catch (IOException ioe) {}
_log.debug("Client socket closed");
}
}
}
/**
* Fire up the streaming server. <code>Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile</code><br />
* Fire up the streaming server. <code>Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile [numHandlers]</code><br />
* <ul>
* <li><b>sinkDir</b>: Directory to store received files in</li>
* <li><b>ourDestFile</b>: filename to write our binary destination to</li>
* <li><b>numHandlers</b>: how many concurrent connections to handle</li>
* </ul>
*/
public static void main(String args[]) {
StreamSinkServer server = null;
switch (args.length) {
case 0:
server = new StreamSinkServer("dataDir", "server.key", "localhost", 7654);
server = new StreamSinkServer("dataDir", "server.key", "localhost", 7654, 3);
break;
case 2:
server = new StreamSinkServer(args[0], args[1]);
break;
case 4:
case 5:
int handlers = 3;
if (args.length == 5) {
try {
handlers = Integer.parseInt(args[4]);
} catch (NumberFormatException nfe) {}
}
try {
int port = Integer.parseInt(args[1]);
server = new StreamSinkServer(args[2], args[3], args[0], port);
server = new StreamSinkServer(args[2], args[3], args[0], port, handlers);
} catch (NumberFormatException nfe) {
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile");
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile [handlers]");
}
break;
default:
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile");
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile [handlers]");
}
if (server != null)
server.runServer();

View File

@ -17,7 +17,7 @@
<property location="doc/q/api" name="javadoc.dir"/>
<property name="project.name" value="${ant.project.name}"/>
<property location="${project.name}.jar" name="jar"/>
<property location="${project.name}.war" name="war"/>
<property location="q.war" name="war"/>
</target>
<target name="builddep">
@ -59,9 +59,9 @@
<!-- To make a standalone app, insert into <jar>: -->
<!-- <manifest><attribute name="Main-Class" value="com.foo.Main"/></manifest> -->
<war compress="true" jarfile="${war}" webxml="web.xml">
<!-- <fileset dir="${classes.dir}" includes="**/QConsole.class"/> -->
<classes file="build/net/i2p/aum/q/QConsole.class"/>
<classes file="build/HTML/**"/>
<!-- <fileset file="build/net/i2p/aum/q/QConsole.class"/> -->
<classes dir="build" includes="**/QConsole.class"/>
<classes dir="build" includes="**/HTML/**"/>
<!-- <fileset includes="**/HTML/*.class"/> -->
<lib file="xmlrpc.jar"/>
</war>

View File

@ -157,6 +157,8 @@ public abstract class QNode extends Thread
*/
public String nodeType = "(base)";
public boolean isRunning;
// ----------------------------------------------------------
// CONSTRUCTORS
@ -580,6 +582,13 @@ public abstract class QNode extends Thread
System.out.println("scheduleStartupJobs: c<p="+updateCatalogFromPeers+", isClient="+isClient);
}
public void scheduleShutdown()
{
Hashtable job = new Hashtable();
job.put("cmd", "shutdown");
runAfter(1000, job, "shutdown");
}
public void schedulePeerUploadJob(QDataItem item)
{
String uri = (String)item.get("uri");
@ -790,6 +799,8 @@ public abstract class QNode extends Thread
{
log.info("Starting background tasks");
isRunning = true;
// mark our start time
nodeStartTime = new Date();
@ -833,7 +844,7 @@ public abstract class QNode extends Thread
// fetch items from the job queue, and launch
// threads to execute them
while (true)
while (isRunning)
{
// get a thread slot from the thread pool
try {

View File

@ -341,13 +341,15 @@ public class QServerMethods {
//System.out.println("shutdown: our privkey="+node.privKeyStr);
//System.out.println("shutdown: nodePrivKey="+nodePrivKey);
if (nodePrivKey.equals(node.privKeyStr)) {
res.put("status", "ok");
//node.scheduleShutdown();
// get a runtime
System.out.println("Node at "+node.dataDir+" shutting down");
//System.out.println("Node at "+node.dataDir+" shutting down");
Runtime r = Runtime.getRuntime();
// and terminate the vm
r.exit(0);
//r.halt(0);
//r.exit(0);
r.halt(0);
}
else {
res.put("status", "error");

View File

@ -62,6 +62,9 @@ class QWorkerThread extends Thread {
else if (cmd.equals("test")) {
doTest();
}
else if (cmd.equals("shutdown")) {
doShutdown();
}
else {
node.log.error("workerthread.run: unrecognised command '"+cmd+"'");
System.out.println("workerthread.run: unrecognised command '"+cmd+"'");
@ -90,6 +93,21 @@ class QWorkerThread extends Thread {
System.out.println("TESTJOB: msg='"+msg+"'");
}
public void doShutdown() throws Exception {
try {
new File(node.jobsDir + node.sep + jobTime).delete();
new File(node.jobsDir + node.sep + jobTime + ".desc").delete();
} catch (Exception e) {
e.printStackTrace();
}
SimpleFile f = new SimpleFile("/tmp/eeee", "rws");
f.write("xxx");
node.isRunning = false;
Runtime.getRuntime().halt(0);
}
public void doLocalPutItem() throws Exception {
Hashtable metadata = (Hashtable)job.get("metadata");
String path = (String)job.get("localDataFilePath");

View File

@ -28,7 +28,8 @@ public class ConfigNetHandler extends FormHandler {
private boolean _reseedRequested;
private boolean _saveRequested;
private boolean _timeSyncEnabled;
private String _port;
private String _tcpPort;
private String _udpPort;
private String _inboundRate;
private String _inboundBurst;
private String _outboundRate;
@ -56,8 +57,11 @@ public class ConfigNetHandler extends FormHandler {
public void setHostname(String hostname) {
_hostname = (hostname != null ? hostname.trim() : null);
}
public void setPort(String port) {
_port = (port != null ? port.trim() : null);
public void setTcpPort(String port) {
_tcpPort = (port != null ? port.trim() : null);
}
public void setUdpPort(String port) {
_udpPort = (port != null ? port.trim() : null);
}
public void setInboundrate(String rate) {
_inboundRate = (rate != null ? rate.trim() : null);
@ -207,14 +211,25 @@ public class ConfigNetHandler extends FormHandler {
restartRequired = true;
}
}
if ( (_port != null) && (_port.length() > 0) ) {
if ( (_tcpPort != null) && (_tcpPort.length() > 0) ) {
String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT);
if ( (oldPort == null) && (_port.equals("8887")) ) {
if ( (oldPort == null) && (_tcpPort.equals("8887")) ) {
// still on default.. noop
} else if ( (oldPort == null) || (!oldPort.equalsIgnoreCase(_port)) ) {
} else if ( (oldPort == null) || (!oldPort.equalsIgnoreCase(_tcpPort)) ) {
// its not the default OR it has changed
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT, _port);
addFormNotice("Updating TCP port from " + oldPort + " to " + _port);
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT, _tcpPort);
addFormNotice("Updating TCP port from " + oldPort + " to " + _tcpPort);
restartRequired = true;
}
}
if ( (_udpPort != null) && (_udpPort.length() > 0) ) {
String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_UDP_PORT);
if ( (oldPort == null) && (_udpPort.equals("8887")) ) {
// still on default.. noop
} else if ( (oldPort == null) || (!oldPort.equalsIgnoreCase(_udpPort)) ) {
// its not the default OR it has changed
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT, _udpPort);
addFormNotice("Updating UDP port from " + oldPort + " to " + _udpPort);
restartRequired = true;
}
}

View File

@ -24,11 +24,13 @@ public class ConfigNetHelper {
/** copied from various private TCP components */
public final static String PROP_I2NP_TCP_HOSTNAME = "i2np.tcp.hostname";
public final static String PROP_I2NP_TCP_PORT = "i2np.tcp.port";
public final static String PROP_I2NP_UDP_PORT = "i2np.udp.port";
public final static String PROP_I2NP_INTERNAL_UDP_PORT = "i2np.udp.internalPort";
public String getHostname() {
return _context.getProperty(PROP_I2NP_TCP_HOSTNAME);
}
public String getPort() {
public String getTcpPort() {
int port = 8887;
String val = _context.getProperty(PROP_I2NP_TCP_PORT);
if (val != null) {
@ -41,6 +43,21 @@ public class ConfigNetHelper {
return "" + port;
}
public String getUdpPort() {
int port = 8887;
String val = _context.getProperty(PROP_I2NP_UDP_PORT);
if (val == null)
val = _context.getProperty(PROP_I2NP_INTERNAL_UDP_PORT);
if (val != null) {
try {
port = Integer.parseInt(val);
} catch (NumberFormatException nfe) {
// ignore, use default from above
}
}
return "" + port;
}
public String getEnableTimeSyncChecked() {
String disabled = _context.getProperty(Timestamper.PROP_DISABLED, "false");
if ( (disabled != null) && ("true".equalsIgnoreCase(disabled)) )

View File

@ -0,0 +1,96 @@
package net.i2p.router.web;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import net.i2p.util.Log;
import net.i2p.stat.StatManager;
/**
* Handler to deal with form submissions from the stats config form and act
* upon the values.
*
*/
public class ConfigStatsHandler extends FormHandler {
private String _filename;
private List _stats;
private boolean _explicitFilter;
private String _explicitFilterValue;
public ConfigStatsHandler() {
super();
_stats = new ArrayList();
_explicitFilter = false;
}
protected void processForm() {
saveChanges();
}
public void setFilename(String filename) {
_filename = (filename != null ? filename.trim() : null);
}
public void setStatList(String stats[]) {
if (stats != null) {
for (int i = 0; i < stats.length; i++) {
String cur = stats[i].trim();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Stat: [" + cur + "]");
if ( (cur.length() > 0) && (!_stats.contains(cur)) )
_stats.add(cur);
}
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Updated stats: " + _stats);
}
public void setExplicitFilter(String foo) { _explicitFilter = true; }
public void setExplicitFilterValue(String filter) { _explicitFilterValue = filter; }
/**
* The user made changes to the config and wants to save them, so
* lets go ahead and do so.
*
*/
private void saveChanges() {
if (_filename == null)
_filename = StatManager.DEFAULT_STAT_FILE;
_context.router().setConfigSetting(StatManager.PROP_STAT_FILE, _filename);
if (_explicitFilter) {
_stats.clear();
if (_explicitFilterValue.indexOf(',') != -1) {
StringTokenizer tok = new StringTokenizer(_explicitFilterValue, ",");
while (tok.hasMoreTokens()) {
String cur = tok.nextToken().trim();
if ( (cur.length() > 0) && (!_stats.contains(cur)) )
_stats.add(cur);
}
} else {
String stat = _explicitFilterValue.trim();
if ( (stat.length() > 0) && (!_stats.contains(stat)) )
_stats.add(stat);
}
}
StringBuffer stats = new StringBuffer();
for (int i = 0; i < _stats.size(); i++) {
stats.append((String)_stats.get(i));
if (i + 1 < _stats.size())
stats.append(',');
}
_context.router().setConfigSetting(StatManager.PROP_STAT_FILTER, stats.toString());
boolean ok = _context.router().saveConfig();
if (ok)
addFormNotice("Stat filter and location updated successfully to: " + stats.toString());
else
addFormError("Failed to update the stat filter and location");
}
}

View File

@ -0,0 +1,125 @@
package net.i2p.router.web;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import net.i2p.stat.RateStat;
import net.i2p.stat.FrequencyStat;
import net.i2p.router.RouterContext;
import net.i2p.util.Log;
public class ConfigStatsHelper {
private RouterContext _context;
private Log _log;
private String _filter;
private Set _filters;
/** list of names of stats which are remaining, ordered by nested groups */
private List _stats;
private String _currentStatName;
private String _currentStatDescription;
private String _currentGroup;
/** true if the current stat is the first in the group */
private boolean _currentIsFirstInGroup;
/** true if the stat is being logged */
private boolean _currentIsLogged;
/**
* 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(ConfigStatsHelper.class);
} catch (Throwable t) {
t.printStackTrace();
}
_stats = new ArrayList();
Map groups = _context.statManager().getStatsByGroup();
for (Iterator iter = groups.values().iterator(); iter.hasNext(); ) {
Set stats = (Set)iter.next();
for (Iterator statIter = stats.iterator(); statIter.hasNext(); )
_stats.add(statIter.next());
}
_filter = _context.statManager().getStatFilter();
if (_filter == null)
_filter = "";
_filters = new HashSet();
StringTokenizer tok = new StringTokenizer(_filter, ",");
while (tok.hasMoreTokens())
_filters.add(tok.nextToken().trim());
}
public ConfigStatsHelper() {}
public String getFilename() { return _context.statManager().getStatFile(); }
/**
* move the cursor to the next known stat, returning true if a valid
* stat is available.
*
* @return true if a valid stat is available, otherwise false
*/
public boolean hasMoreStats() {
if (_stats.size() <= 0)
return false;
_currentStatName = (String)_stats.remove(0);
RateStat rs = _context.statManager().getRate(_currentStatName);
if (rs != null) {
_currentStatDescription = rs.getDescription();
if (_currentGroup == null)
_currentIsFirstInGroup = true;
else if (!rs.getGroupName().equals(_currentGroup))
_currentIsFirstInGroup = true;
else
_currentIsFirstInGroup = false;
_currentGroup = rs.getGroupName();
} else {
FrequencyStat fs = _context.statManager().getFrequency(_currentStatName);
if (fs != null) {
_currentStatDescription = fs.getDescription();
if (_currentGroup == null)
_currentIsFirstInGroup = true;
else if (!fs.getGroupName().equals(_currentGroup))
_currentIsFirstInGroup = true;
else
_currentIsFirstInGroup = false;
_currentGroup = fs.getGroupName();
} else {
if (_log.shouldLog(Log.ERROR))
_log.error("Stat does not exist?! [" + _currentStatName + "]");
return false;
}
}
if (_filters.contains("*") || _filters.contains(_currentStatName))
_currentIsLogged = true;
else
_currentIsLogged = false;
return true;
}
/** Is the current stat the first in the group? */
public boolean groupRequired() {
if (_currentIsFirstInGroup) {
_currentIsFirstInGroup = false;
return true;
} else {
return false;
}
}
/** What group is the current stat in */
public String getCurrentGroupName() { return _currentGroup; }
public String getCurrentStatName() { return _currentStatName; }
public String getCurrentStatDescription() { return _currentStatDescription; }
public boolean getCurrentIsLogged() { return _currentIsLogged; }
public String getExplicitFilter() { return _filter; }
}

View File

@ -1,6 +1,10 @@
package net.i2p.router.web;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.router.Router;
import net.i2p.router.web.ConfigServiceHandler.UpdateWrapperManagerTask;
import net.i2p.util.Log;
/**
*
@ -31,6 +35,15 @@ public class ConfigUpdateHandler extends FormHandler {
public static final String DEFAULT_PROXY_PORT = "4444";
protected void processForm() {
if ("Check for update now".equals(_action)) {
NewsFetcher fetcher = NewsFetcher.getInstance(I2PAppContext.getGlobalContext());
fetcher.fetchNews();
if (fetcher.updateAvailable())
addFormNotice("Update available, click link on left");
else
addFormNotice("No update available");
}
if ( (_newsURL != null) && (_newsURL.length() > 0) ) {
String oldURL = _context.router().getConfigSetting(PROP_NEWS_URL);
if ( (oldURL == null) || (!_newsURL.equals(oldURL)) ) {
@ -38,6 +51,7 @@ public class ConfigUpdateHandler extends FormHandler {
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)) ) {
@ -56,7 +70,7 @@ public class ConfigUpdateHandler extends FormHandler {
if ( (_proxyPort != null) && (_proxyPort.length() > 0) ) {
String oldPort = _context.router().getConfigSetting(PROP_PROXY_PORT);
if ( (oldPort == null) || (!_proxyHost.equals(oldPort)) ) {
if ( (oldPort == null) || (!_proxyPort.equals(oldPort)) ) {
_context.router().setConfigSetting(PROP_PROXY_PORT, _proxyPort);
addFormNotice("Updating proxy port to " + _proxyPort);
}

View File

@ -4,6 +4,7 @@ import java.util.List;
import java.util.ArrayList;
import net.i2p.router.RouterContext;
import net.i2p.util.Log;
/**
* Simple form handler base class - does not depend on servlets or jsp,
@ -16,6 +17,7 @@ import net.i2p.router.RouterContext;
*/
public class FormHandler {
protected RouterContext _context;
protected Log _log;
private String _nonce;
protected String _action;
private List _errors;
@ -41,6 +43,7 @@ public class FormHandler {
public void setContextId(String contextId) {
try {
_context = ContextHelper.getContext(contextId);
_log = _context.logManager().getLog(getClass());
} catch (Throwable t) {
t.printStackTrace();
}

View File

@ -91,7 +91,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
return false;
}
}
private void fetchNews() {
public 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);

View File

@ -25,8 +25,11 @@ public class NoticeHelper {
public String getSystemNotice() {
if (_context.router().gracefulShutdownInProgress()) {
return "Graceful shutdown in "
+ DataHelper.formatDuration(_context.router().getShutdownTimeRemaining());
long remaining = _context.router().getShutdownTimeRemaining();
if (remaining > 0)
return "Graceful shutdown in " + DataHelper.formatDuration(remaining);
else
return "Graceful shutdown imminent, please be patient as state is written to disk";
} else {
return "";
}

View File

@ -0,0 +1,37 @@
package net.i2p.router.web;
import java.io.IOException;
import java.io.Writer;
import net.i2p.router.RouterContext;
public class PeerHelper {
private RouterContext _context;
private Writer _out;
/**
* 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 PeerHelper() {}
public void setOut(Writer out) { _out = out; }
public String getPeerSummary() {
try {
_context.commSystem().renderStatusHTML(_out);
} catch (IOException ioe) {
ioe.printStackTrace();
}
return "";
}
}

View File

@ -28,13 +28,13 @@
<input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigNetHandler.nonce")%>" />
<input type="hidden" name="action" value="blah" />
TCP port:
<input name="port" type="text" size="4" value="<jsp:getProperty name="nethelper" property="port" />" /> <br />
UDP port: <i><jsp:getProperty name="nethelper" property="udpPort" /></i><br />
<!-- <input name="udpPort" type="text" size="5" value="<jsp:getProperty name="nethelper" property="udpPort" />" /><br /> -->
<b>You must poke a hole in your firewall or NAT (if applicable) to receive new inbound UDP packets on
this port from arbitrary peers (this requirement will be removed in i2p 0.6.1, but is necessary now)</b><br />
TCP port: <input name="tcpPort" type="text" size="5" value="<jsp:getProperty name="nethelper" property="tcpPort" />" /> <br />
<b>You must poke a hole in your firewall or NAT (if applicable) so that you can receive inbound TCP
connections on it.</b> Nothing will work if you don't. Sorry. We know how to make it so
this restriction won't be necessary, but its later on in the
<a href="http://www.i2p.net/roadmap">roadmap</a> and we only have so many coder-hours (but if you want
to help, please <a href="http://www.i2p.net/getinvolved">get involved!</a>)
connections on it (this requirement will be removed in i2p 0.6.1, but is necessary now)</b>
<hr />
<b>Bandwidth limiter</b><br />
@ -57,7 +57,7 @@
packets on port 123 to one of the pool.ntp.org machines (or some other SNTP server).</i>
<hr />
<input type="submit" name="save" value="Save changes" /> <input type="reset" value="Cancel" /><br />
<i>Changing the TCP port will force a 'soft restart' - dropping your connections and clients as
<i>Changing the TCP or UDP port will force a 'soft restart' - dropping your connections and clients as
if the router was stopped and restarted. <b>Please be patient</b> - it may take
a few seconds to complete.</i>
</form>
@ -73,6 +73,13 @@
"i2p.reseedURL=someURL" (e.g. java -Di2p.reseedURL=http://dev.i2p.net/i2pdb/ ...). You can
also do it manually by getting routerInfo-*.dat files from someone (a friend, someone on IRC,
whatever) and saving them to your netDb/ directory.</p>
<p>
With the SSU transport, the internal UDP port may be different from the external
UDP port (in case of a firewall/NAT) - the UDP port field above specifies the
external one and assumes they are the same, but if you want to set the internal
port to something else, you can add "i2np.udp.internalPort=1234" to the
<a href="configadvanced.jsp">advanced</a> config and restart the router.
</p>
</div>
</body>

View File

@ -8,5 +8,7 @@
%>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("configstats.jsp") != -1) {
%>Stats | <% } else { %><a href="configstats.jsp">Stats</a> | <% }
if (request.getRequestURI().indexOf("configadvanced.jsp") != -1) {
%>Advanced<% } else { %><a href="configadvanced.jsp">Advanced</a><% } %></h4>

View File

@ -0,0 +1,104 @@
<%@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 stats</title>
<link rel="stylesheet" href="default.css" type="text/css" />
<script type="text/javascript">
function init()
{
checkAll = false;
}
function toggleAll(category)
{
var inputs = document.getElementsByTagName("input");
for(index = 0; index < inputs.length; index++)
{
if(inputs[index].id == category)
{
if(inputs[index].checked == 0)
{
inputs[index].checked = 1;
}
else if(inputs[index].checked == 1)
{
inputs[index].checked = 0;
}
}
if(category == '*')
{
if (checkAll == false)
{
inputs[index].checked = 1;
}
else if (checkAll == true)
{
inputs[index].checked = 0;
}
}
}
if(category == '*')
{
if (checkAll == false)
{
checkAll = true;
}
else if (checkAll == true)
{
checkAll = false;
}
}
}
</script>
</head><body onLoad="init();">
<%@include file="nav.jsp" %>
<%@include file="summary.jsp" %>
<div class="main" id="main">
<%@include file="confignav.jsp" %>
<jsp:useBean class="net.i2p.router.web.ConfigStatsHandler" id="formhandler" scope="request" />
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="formhandler" property="*" />
<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.ConfigStatsHelper" id="statshelper" scope="request" />
<jsp:setProperty name="statshelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<form id="statsForm" name="statsForm" action="configstats.jsp" method="POST">
<% String prev = System.getProperty("net.i2p.router.web.ConfigStatsHandler.nonce");
if (prev != null) System.setProperty("net.i2p.router.web.ConfigStatsHandler.noncePrev", prev);
System.setProperty("net.i2p.router.web.ConfigStatsHandler.nonce", new java.util.Random().nextLong()+""); %>
<input type="hidden" name="action" value="foo" />
<input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigStatsHandler.nonce")%>" />
Stat file: <input type="text" name="filename" value="<%=statshelper.getFilename()%>" /><br />
Filter: (<a href="javascript: void(null);" onclick="toggleAll('*')">toggle all</a>)<br />
<table>
<% while (statshelper.hasMoreStats()) {
while (statshelper.groupRequired()) { %>
<tr><td valign="top" align="left" colspan="2">
<b><%=statshelper.getCurrentGroupName()%></b>
(<a href="javascript: void(null);" onclick="toggleAll('<%=statshelper.getCurrentGroupName()%>')">toggle all</a>)
</td></tr><%
} // end iterating over required groups for the current stat %>
<tr><td valign="top" align="left">
<input id="<%=statshelper.getCurrentGroupName()%>" type="checkbox" name="statList" value="<%=statshelper.getCurrentStatName()%>" <%
if (statshelper.getCurrentIsLogged()) { %>checked="true" <% } %>/></td>
<td valign="top" align="left"><b><%=statshelper.getCurrentStatName()%>:</b><br />
<%=statshelper.getCurrentStatDescription()%></td></tr><%
} // end iterating over all stats %>
<tr><td colspan="2"><hr /></td></tr>
<tr><td><input type="checkbox" name="explicitFilter" /></td>
<td>Advanced filter:
<input type="text" name="explicitFilterValue" value="<%=statshelper.getExplicitFilter()%>" size="40" /></td></tr>
<tr><td colspan="2"><hr /></td></tr>
<tr><td><input type="submit" name="shouldsave" value="Save changes" /> </td>
<td><input type="reset" value="Cancel" /></td></tr>
</form>
</table>
</div>
</body>
</html>

View File

@ -27,7 +27,7 @@
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" />
<input type="submit" name="action" value="Check for update now" /><br /><br />
News URL:
<input type="text" size="60" name="newsURL" value="<jsp:getProperty name="updatehelper" property="newsURL" />"><br />
Refresh frequency:
@ -36,10 +36,10 @@
<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?
Update through the eepProxy?
<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 />
eepProxy host: <input type="text" size="10" name="proxyHost" value="<jsp:getProperty name="updatehelper" property="proxyHost" />" /><br />
eepProxy 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>

View File

@ -0,0 +1,21 @@
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<title>I2P Router Console - peer connections</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">
<jsp:useBean class="net.i2p.router.web.PeerHelper" id="peerHelper" scope="request" />
<jsp:setProperty name="peerHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:setProperty name="peerHelper" property="out" value="<%=out%>" />
<jsp:getProperty name="peerHelper" property="peerSummary" />
</div>
</body>
</html>

View File

@ -33,7 +33,7 @@
}
%><hr />
<u><b>Peers</b></u><br />
<u><b><a href="peers.jsp">Peers</a></b></u><br />
<b>Active:</b> <jsp:getProperty name="helper" property="activePeers" />/<jsp:getProperty name="helper" property="activeProfiles" /><br />
<b>Fast:</b> <jsp:getProperty name="helper" property="fastPeers" /><br />
<b>High capacity:</b> <jsp:getProperty name="helper" property="highCapacityPeers" /><br />

64
apps/sam/c/Makefile Normal file
View File

@ -0,0 +1,64 @@
FLAGS+=-g
CFLAGS+=$(FLAGS)
LDFLAGS+=$(FLAGS)
OBJS:=obj/sam.lo obj/strl.lo obj/parse.lo obj/tinystring.lo
DEPS:=$(patsubst obj/%.lo, .deps/%.d, $(OBJS))
DESTDIR:=$(if $(DESTDIR),$(DESTDIR)/lib,/usr/lib)
MAKEFLAGS=-s -r
PERL=$(shell which perl 2>/dev/null)
ifneq ($(PERL),)
STATUS=$(PERL) ./status
else
STATUS=echo
endif
LIBTOOL_LOG=libtool.log
all:: cleanlog .deps/finish
cleanlog:
echo >$(LIBTOOL_LOG)
lib/libsam.so: obj/libsam.la
libtool --mode=install install $^ `pwd`/$@ >>$(LIBTOOL_LOG)
obj/libsam-static.la: $(OBJS)
$(STATUS) library '(static)'
libtool --mode=link gcc -static $(LDFLAGS) -o $@ $^ >>$(LIBTOOL_LOG)
obj/libsam.la: $(OBJS)
$(STATUS) library '(shared)'
libtool --mode=link gcc -rpath $(DESTDIR) $(LDFLAGS) -o $@ $^ >>$(LIBTOOL_LOG)
obj/%.lo: src/%.c
$(STATUS) compile $*
libtool --mode=compile gcc $(CFLAGS) -Iinc/ -c -o $@ $< >>$(LIBTOOL_LOG)
$(OBJS):|obj
obj:
$(STATUS) MKDIR $@
mkdir -p $@
.deps/%.d: src/%.c
$(STATUS) deps $*
gcc -Iinc/ -MM -MT obj/$*.o $< -o $@
-include $(DEPS)
DEPS+=.deps/finish
.deps/finish: lib/libsam.so
libtool --finish $(DESTDIR) >>$(LIBTOOL_LOG) && touch $@
$(DEPS):|.deps
.deps:
$(STATUS) MKDIR $@
mkdir -p $@
clean:
$(STATUS) clean
libtool --mode=clean rm -f obj/*.l* lib/*.l* lib/*.so* lib/*.a >>$(LIBTOOL_LOG)
rm -Rf .deps libtool.log
.PHONY: all cleanlog clean

View File

@ -1,25 +0,0 @@
#
# This Makefile contains instructions common to all platforms
#
#
# Build rules
#
all: clean depend libsam
depend:
$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
$(OBJDIR)/%.o: $(SRCDIR)/%.c
$(CC) $(CFLAGS) -o $@ -c $<
libsam: $(OBJS)
$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
#
# Cleanup rules
#
clean:
-$(RM) -f $(LIBDIR)/libsam.a $(OBJDIR)/*.o .depend

View File

@ -1,48 +0,0 @@
#
# This Makefile is compatible with GNU Make and should work on Cygwin
#
#
# Your operating system
#
OS = CYGWIN
#
# Directories
#
INCDIR = inc
LIBDIR = lib
OBJDIR = obj
SRCDIR = src
#
# Programs
#
AR = ar
CC = gcc
RM = rm
#
# Flags
#
CFLAGS = -g -O2 -pipe -std=c99 -Wall
CFLAGS += -DOS=$(OS)
CFLAGS += -I$(INCDIR)
#
# Object files
#
OBJS = $(OBJDIR)/sam.o \
$(OBJDIR)/snprintf.o \
$(OBJDIR)/strl.o
#
# Include the make instructions common to all platforms
#
include Makefile.common

View File

@ -1,46 +0,0 @@
#
# This Makefile is compatible with GNU Make and should work on FreeBSD
#
#
# Your operating system
#
OS = FREEBSD
#
# Directories
#
INCDIR = inc
LIBDIR = lib
OBJDIR = obj
SRCDIR = src
#
# Programs
#
AR = ar
CC = gcc
RM = rm
#
# Flags
#
CFLAGS = -g -O2 -pipe -std=c99 -Wall
CFLAGS += -DOS=$(OS)
CFLAGS += -I$(INCDIR)
#
# Object files
#
OBJS = $(OBJDIR)/sam.o
#
# Include the make instructions common to all platforms
#
include Makefile.common

View File

@ -1,47 +0,0 @@
#
# This Makefile is compatible with GNU Make and should work on Linux
#
#
# Your operating system
#
OS = LINUX
#
# Directories
#
INCDIR = inc
LIBDIR = lib
OBJDIR = obj
SRCDIR = src
#
# Programs
#
AR = ar
CC = gcc
RM = rm
#
# Flags
#
CFLAGS = -g -O2 -pipe -std=c99 -Wall
CFLAGS += -DOS=$(OS)
CFLAGS += -I$(INCDIR)
#
# Object files
#
OBJS = $(OBJDIR)/sam.o \
$(OBJDIR)/strl.o
#
# Include the make instructions common to all platforms
#
include Makefile.common

View File

@ -1,47 +0,0 @@
#
# This Makefile is compatible with GNU Make and should work on Windows (MingW)
#
#
# Your operating system
#
OS = MINGW
#
# Directories
#
INCDIR = inc
LIBDIR = lib
OBJDIR = obj
SRCDIR = src
#
# Programs
#
AR = C:\MinGW\bin\ar
CC = C:\MinGW\bin\gcc
RM = C:\MinGW\bin\rm
#
# Flags
#
CFLAGS = -g -O2 -pipe -std=c99 -Wall
CFLAGS += -DOS=$(OS)
CFLAGS += -I$(INCDIR)
#
# Object files
#
OBJS = $(OBJDIR)/sam.o \
$(OBJDIR)/strl.o
#
# Include the make instructions common to all platforms
#
include Makefile.common

View File

@ -1,39 +1,54 @@
#
# This Makefile is compatible with GNU Make and should work on POSIX systems
#
FLAGS+=-g
#
# Programs
#
CFLAGS = $(FLAGS) -pipe -std=c99 -Wall
CFLAGS += -I../../inc
LDFLAGS = $(FLAGS) -L../../lib -lsam
CC = gcc
INSTALL = install
RM = rm
OBJS:=i2p-ping.lo
DEPS:=$(patsubst obj/%.lo, .deps/%.d, $(OBJS))
DESTDIR:=$(if $(DESTDIR),$(DESTDIR)/lib,/usr/lib)
#
# Flags
#
MAKEFLAGS=-s -r
PERL=$(shell which perl 2>/dev/null)
ifneq ($(PERL),)
STATUS=$(PERL) ../../status
else
STATUS=echo
endif
CFLAGS = -g -O2 -pipe -std=c99 -Wall
CFLAGS += -I../../inc -L../../lib
LIBS = -lsam
LIBTOOL_LOG=libtool.log
#
# Build rules
#
all:: cleanlog i2p-ping
all: clean i2p-ping
cleanlog:
>$(LIBTOOL_LOG)
i2p-ping: i2p-ping.c
$(CC) $(CFLAGS) -o i2p-ping.o -c i2p-ping.c
$(CC) $(CFLAGS) -o i2p-ping i2p-ping.o $(LIBS)
i2p-ping: $(OBJS)
$(STATUS) link
libtool --mode=link gcc $(LDFLAGS) -o $@ $^ >>$(LIBTOOL_LOG)
install: i2p-ping
$(INSTALL) i2p-ping $(HOME)/bin
#
# Cleanup rules
#
%.lo: %.c
$(STATUS) compile $*
libtool --mode=compile gcc $(CFLAGS) -Iinc/ -c -o $@ $< >>$(LIBTOOL_LOG)
.deps/%.d: src/%.c
$(STATUS) deps $*
gcc -Iinc/ -MM -MT obj/$*.o $^ -o $@
clean:
-$(RM) -f i2p-ping *.o
$(STATUS) clean
rm -Rf .deps obj libtool.log
libtool --mode=clean rm -f i2p-ping i2p-ping.lo >>$(LIBTOOL_LOG)
$(OBJS):|obj
obj:
$(STATUS) MKDIR $@
mkdir -p $@
-include $(DEPS)
$(DEPS):|.deps
.deps:
$(STATUS) MKDIR $@
mkdir -p $@
.PHONY: all cleanlog clean

24
apps/sam/c/inc/parse.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef _PARSE_HEADER_FEEP
#define _PARSE_HEADER_FEEP
#include "tinystring.h"
typedef struct arg_s {
string_t name;
string_t value;
// int pos;
} arg_t;
typedef struct {
arg_t* arg;
int num;
} args_t;
args_t arg_parse(const char*);
void arg_done(args_t);
arg_t* arg_get(args_t,int);
arg_t* arg_find(args_t,string_t);
#define AG(a,b) arg_get(a,b)
#endif /* _PARSE_HEADER_FEEP */

View File

@ -121,9 +121,9 @@ bool sam_read_buffer(sam_sess_t *session);
const char *sam_strerror(samerr_t code);
/* SAM controls - callbacks */
void (*sam_diedback)(sam_sess_t *session);
void (*sam_logback)(char *str);
void (*sam_namingback)(sam_sess_t *session, char *name,
sam_pubkey_t pubkey, samerr_t result);
void (*sam_logback)(const char *str);
void (*sam_namingback)(sam_sess_t *session, const char *name,
sam_pubkey_t pubkey, samerr_t result, const char* message);
/* Stream commands */
void sam_stream_close(sam_sess_t *session, sam_sid_t stream_id);
@ -131,14 +131,15 @@ sam_sid_t sam_stream_connect(sam_sess_t *session, const sam_pubkey_t dest);
samerr_t sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
const void *data, size_t size);
/* Stream commands - callbacks */
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id,
samerr_t reason);
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id,
samerr_t reason, const char* message);
void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
sam_pubkey_t dest);
void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id,
sam_pubkey_t dest);
void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id,
void *data, size_t size);
void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
samerr_t result);
void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
samerr_t result, const char* message);
/* Stream send queue (experimental) */
void sam_sendq_add(sam_sess_t *session, sam_sid_t stream_id,

View File

@ -0,0 +1,48 @@
#ifndef TINYSTRING_HEADER
#define TINYSTRING_HEADER
#include <sys/types.h>
#ifndef bool
#define bool short int
#endif
struct string_s;
#define string_t struct string_s*
//Mysteeeerious *waggles mysteriously*
/*{
char* data;
long int size;
} *string_t;
*/
string_t string_create(const char*);
string_t string_ncreate(const char* cstr,long int length);
string_t string_wrap(const char*);
//Does not malloc, do NOT pass to string_free
string_t string_fmt(const char* fmt, ...);
string_t string_cat(string_t,string_t);
/* Source Dest */
void string_copy(string_t,string_t);
void string_copy_raw(string_t,void*,size_t);
const char* string_data(string_t);
long int string_size(string_t);
void string_free(string_t);
bool string_equal(string_t,string_t);
bool string_equali(string_t,string_t);
int string_cmp(string_t,string_t);
int string_cmpi(string_t,string_t);
#define _sw(a) string_wrap(a)
#define _scr(a,b,c) string_copy_raw(a,b,c)
#define string_is(a,b) (! strncmp(string_data(a),(b),string_size(a)))
#endif /* TINYSTRING_HEADER */

78
apps/sam/c/src/parse.c Normal file
View File

@ -0,0 +1,78 @@
#include "parse.h"
#include <assert.h>
#include <ctype.h>
#include <malloc.h>
#define _GNU_SOURCE
#include <string.h>
args_t arg_parse(const char* line_raw) {
args_t self;
int numargs = 0;
const char *end, *last;
/* First pass to count how many args... */
end = line_raw;
while(*end && isspace(*end)) ++end;
//Skip initial space...
for(;;) {
while(*end && !isspace(*end)) ++end;
//Go to end of argument
++numargs;
while(*end && isspace(*end)) ++end;
//Go to end of space after argument
if(!*end) break;
}
self.num = numargs; // One more # args than spaces.
self.arg = malloc(sizeof(arg_t)*numargs);
/* Second pass to assign args. (Lemee alone, is more efficient than a linked list!) */
last = line_raw;
numargs = 0; //Now numargs is which current arg.
end = line_raw;
while(*end && isspace(*end)) ++end;
//Skip initial space...
for(;;) {
arg_t* nextarg = self.arg + numargs;;
const char* isbinary;
while(*end && !isspace(*end)) ++end;
//Go to end of argument
isbinary = strchr(last,'='); //Is there a value?
//Make sure not to pass end in our search for =
if(isbinary && (isbinary < end)) {
nextarg->name = string_ncreate(last,isbinary-last);
nextarg->value = string_ncreate(isbinary+1,end-isbinary-1);
} else {
nextarg->name = string_ncreate(last,end-last);
nextarg->value = string_create(NULL);
}
++numargs;
while(*end && isspace(*end)) ++end;
//Go to end of space after argument
if(!*end) break;
last = end;
}
return self;
}
void arg_done(args_t self) {
free(self.arg);
self.arg = NULL;
self.num = 0;
}
arg_t* arg_get(args_t self, int index) {
if(index >= self.num) return NULL;
return self.arg + index;
}
arg_t* arg_find(args_t self,string_t testname) {
int index;
for(index=0;index<self.num;++index) {
if(string_equali(self.arg[index].name,testname)) {
return self.arg + index;
}
}
return NULL;
}

View File

@ -30,6 +30,10 @@
#include "sam.h"
#include "platform.h"
#include "parse.h"
#include "tinystring.h"
#include <assert.h>
static bool sam_hello(sam_sess_t *session);
static void sam_log(const char *format, ...);
@ -57,7 +61,7 @@ static ssize_t sam_write(sam_sess_t *session, const void *buf, size_t n);
*/
/* a peer closed the connection */
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id, samerr_t reason)
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id, samerr_t reason, const char* message)
= NULL;
/* a peer connected to us */
@ -76,15 +80,14 @@ void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest, void *data,
void (*sam_diedback)(sam_sess_t *session) = NULL;
/* logging callback */
void (*sam_logback)(char *str) = NULL;
void (*sam_logback)(const char *str) = NULL;
/* naming lookup reply - `pubkey' will be NULL if `result' isn't SAM_OK */
void (*sam_namingback)(sam_sess_t *session, char *name, sam_pubkey_t pubkey,
samerr_t result) = NULL;
void (*sam_namingback)(sam_sess_t *session, const char *name, sam_pubkey_t pubkey, samerr_t result, const char* message) = NULL;
/* our connection to a peer has completed */
void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
samerr_t result) = NULL;
samerr_t result, const char* message) = NULL;
/* a peer sent some raw data (`data' MUST be freed) */
void (*sam_rawback)(sam_sess_t *session, void *data, size_t size) = NULL;
@ -290,13 +293,13 @@ static void sam_log(const char *format, ...)
*/
void sam_naming_lookup(sam_sess_t *session, const char *name)
{
assert(session != NULL);
char cmd[SAM_CMD_LEN];
assert(session != NULL);
char cmd[SAM_CMD_LEN];
snprintf(cmd, sizeof cmd, "NAMING LOOKUP NAME=%s\n", name);
sam_write(session, cmd, strlen(cmd));
snprintf(cmd, sizeof cmd, "NAMING LOOKUP NAME=%s\n", name);
sam_write(session, cmd, strlen(cmd));
return;
return;
}
/*
@ -304,242 +307,193 @@ void sam_naming_lookup(sam_sess_t *session, const char *name)
*
* s - string of data that we read (read past tense)
*/
bool sam_parse_args(sam_sess_t *session, args_t args);
static void sam_parse(sam_sess_t *session, char *s)
{
assert(session != NULL);
#define SAM_DGRAM_RECEIVED_REPLY "DATAGRAM RECEIVED"
#define SAM_NAMING_REPLY "NAMING REPLY"
#define SAM_NAMING_REPLY_OK "NAMING REPLY RESULT=OK"
#define SAM_NAMING_REPLY_IK "NAMING REPLY RESULT=INVALID_KEY"
#define SAM_NAMING_REPLY_KNF "NAMING REPLY RESULT=KEY_NOT_FOUND"
#define SAM_RAW_RECEIVED_REPLY "RAW RECEIVED"
#define SAM_STREAM_CLOSED_REPLY "STREAM CLOSED"
#define SAM_STREAM_CONNECTED_REPLY "STREAM CONNECTED"
#define SAM_STREAM_RECEIVED_REPLY "STREAM RECEIVED"
#define SAM_STREAM_STATUS_REPLY "STREAM STATUS"
#define SAM_STREAM_STATUS_REPLY_OK "STREAM STATUS RESULT=OK"
#define SAM_STREAM_STATUS_REPLY_CRP "STREAM STATUS RESULT=CANT_REACH_PEER"
#define SAM_STREAM_STATUS_REPLY_I2E "STREAM STATUS RESULT=I2P_ERROR"
#define SAM_STREAM_STATUS_REPLY_IK "STREAM STATUS RESULT=INVALID_KEY"
#define SAM_STREAM_STATUS_REPLY_TO "STREAM STATUS RESULT=TIMEOUT"
//Wrapper for ease of memory management
args_t args;
assert(session != NULL);
args = arg_parse(s);
if(!sam_parse_args(session, args)) {
SAMLOG("Unknown SAM command received: %s", s);
}
arg_done(args);
}
/*
* TODO: add raw parsing
*/
long int strtol_checked(const char* str) {
static char* end = NULL;
long int ret = strtol(str,&end,10);
assert(str != end || "No number found at all!");
return ret;
}
if (strncmp(s, SAM_DGRAM_RECEIVED_REPLY,
strlen(SAM_DGRAM_RECEIVED_REPLY)) == 0) {
char *p;
sam_pubkey_t dest;
size_t size;
void *data;
p = strchr(s, '='); /* DESTINATION= */
assert(p != NULL);
p++;
strlcpy(dest, p, sizeof dest);
p = strchr(p, '='); /* SIZE= */
assert(p != NULL);
p++;
size = strtol(p, NULL, 10);
assert(size != 0);
data = malloc(size + 1); /* +1 for NUL termination, so when we are
receiving a string it will just work and it
won't be necessary to send NUL. When binary
data is sent, the extra NUL character will
just be ignored by the client program,
because it is not added to the size */
if (data == NULL) {
SAMLOGS("Out of memory");
abort();
}
if (sam_read2(session, data, size) != -1) {
p = data + size;
*p = '\0'; /* see above NUL note */
sam_dgramback(session, dest, data, size); /* `data' must be freed */
} else
free(data);
return;
} else if (strncmp(s, SAM_NAMING_REPLY, strlen(SAM_NAMING_REPLY)) == 0) {
char *p;
char *q;
char name[SAM_NAME_LEN];
p = strchr(s, '='); /* can't use strrchar because of option
MESSAGE= */
assert(p != NULL); /* RESULT= */
p++;
p = strchr(p, '='); /* NAME= */
assert(p != NULL);
p++;
if (strncmp(s, SAM_NAMING_REPLY_OK, strlen(SAM_NAMING_REPLY_OK)) == 0) {
sam_pubkey_t pubkey;
q = strchr(p, ' '); /* ' 'VAL.. */
assert(q != NULL);
*q = '\0';
q++;
q = strchr(q, '='); /* VALUE= */
assert(q != NULL);
q++;
strlcpy(name, p, sizeof name);
strlcpy(pubkey, q, sizeof pubkey);
sam_namingback(session, name, pubkey, SAM_OK);
} else if (strncmp(s, SAM_NAMING_REPLY_IK,
strlen(SAM_NAMING_REPLY_IK)) == 0) {
q = strchr(p, ' '); /* ' 'MES.. (optional) */
if (q != NULL)
*q = '\0';
strlcpy(name, p, sizeof name);
sam_namingback(session, name, NULL, SAM_INVALID_KEY);
} else if (strncmp(s, SAM_NAMING_REPLY_KNF,
strlen(SAM_NAMING_REPLY_KNF)) == 0) {
q = strchr(p, ' '); /* ' 'MES.. (optional) */
if (q != NULL)
*q = '\0';
strlcpy(name, p, sizeof name);
sam_namingback(session, name, NULL, SAM_KEY_NOT_FOUND);
} else {
q = strchr(p, ' '); /* ' 'MES.. (optional) */
if (q != NULL)
*q = '\0';
strlcpy(name, p, sizeof name);
sam_namingback(session, name, NULL, SAM_UNKNOWN);
}
return;
} else if (strncmp(s, SAM_STREAM_CLOSED_REPLY,
strlen(SAM_STREAM_CLOSED_REPLY)) == 0) {
char *p;
sam_sid_t stream_id;
p = strchr(s, '='); /* can't use strrchar because of option MESSAGE= */
assert(p != NULL); /* ID= */
p++;
stream_id = strtol(p, NULL, 10);
assert(stream_id != 0);
p = strchr(p, '='); /* RESULT= */
assert(p != NULL);
p++;
if (strncmp(p, "OK", strlen("OK")) == 0)
sam_closeback(session, stream_id, SAM_OK);
else if (strncmp(p, "CANT_REACH_PEER", strlen("CANT_REACH_PEER")) == 0)
sam_closeback(session, stream_id, SAM_CANT_REACH_PEER);
else if (strncmp(p, "I2P_ERROR", strlen("I2P_ERROR")) == 0)
sam_closeback(session, stream_id, SAM_I2P_ERROR);
else if (strncmp(p, "PEER_NOT_FOUND", strlen("PEER_NOT_FOUND")) == 0)
sam_closeback(session, stream_id, SAM_PEER_NOT_FOUND);
else if (strncmp(p, "TIMEOUT", strlen("TIMEOUT")) == 0)
sam_closeback(session, stream_id, SAM_TIMEOUT);
else
sam_closeback(session, stream_id, SAM_UNKNOWN);
return;
} else if (strncmp(s, SAM_STREAM_CONNECTED_REPLY,
strlen(SAM_STREAM_CONNECTED_REPLY)) == 0) {
char *p;
sam_sid_t stream_id;
sam_pubkey_t dest;
p = strrchr(s, '='); /* ID= */
assert(p != NULL);
*p = '\0';
p++;
stream_id = strtol(p, NULL, 10);
assert(stream_id != 0);
p = strstr(s, "N="); /* DESTINATION= */
p += 2;
strlcpy(dest, p, sizeof dest);
sam_connectback(session, stream_id, dest);
bool sam_parse_args(sam_sess_t *session, args_t args)
{
arg_t* arg; // The current argument being examined...
const char* message = NULL; // Almost EVERYTHING can have a message...
return;
if(args.num <= 0) return 0;
} else if (strncmp(s, SAM_STREAM_RECEIVED_REPLY,
strlen(SAM_STREAM_RECEIVED_REPLY)) == 0) {
char *p;
sam_sid_t stream_id;
#define ARG_IS(a,b) string_equal(AG(args,a)->name,string_wrap(b))
#define ARG_FIND(a) arg_find(args,_sw(a))
// Almost EVERYTHING can have a message...
arg = ARG_FIND("MESSAGE");
if(arg) {
message = string_data(arg->value);
}
if(ARG_IS(0,"DATAGRAM") &&
ARG_IS(1,"RECEIVED")) {
sam_pubkey_t dest;
size_t size;
void *data;
arg = ARG_FIND("DESTINATION");
assert(arg != NULL);
_scr(arg->value, dest, sizeof dest);
arg = ARG_FIND("SIZE");
assert(arg != NULL);
size = strtol_checked(string_data(arg->value));
data = malloc(size + 1);
/* +1 for NUL termination, so when we are
receiving a string it will just work and it
won't be necessary to send NUL. When binary
data is sent, the extra NUL character will
just be ignored by the client program,
because it is not added to the size */
if (data == NULL) {
SAMLOGS("Out of memory");
abort();
}
if (sam_read2(session, data, size) != -1) {
char* p = data + size;
*p = '\0'; /* see above NUL note */
sam_dgramback(session, dest, data, size); /* `data' must be freed */
} else
free(data);
} else if (ARG_IS(0,"NAMING") &&
ARG_IS(1, "REPLY")) {
if(NULL == (arg = ARG_FIND("RESULT"))) {
SAMLOGS("Naming reply with no result");
return 0;
}
if (string_is(arg->value,"OK")) {
sam_pubkey_t pubkey;
arg = ARG_FIND("VALUE");
assert(arg != NULL);
_scr(arg->value, pubkey, sizeof pubkey);
arg = ARG_FIND("NAME");
assert(arg != NULL);
sam_namingback(session, string_data(arg->value), pubkey, SAM_OK, message);
} else if(string_is(arg->value,"INVALID_KEY")) {
arg_t* namearg = ARG_FIND("NAME");
assert(namearg != NULL);
sam_namingback(session, string_data(namearg->value), NULL,
SAM_INVALID_KEY, message);
} else if(string_is(arg->value,"KEY_NOT_FOUND")) {
arg_t* namearg = ARG_FIND("NAME");
assert(namearg != NULL);
sam_namingback(session, string_data(namearg->value), NULL,
SAM_KEY_NOT_FOUND, message);
} else {
arg_t* namearg = ARG_FIND("NAME");
assert(namearg != NULL);
sam_namingback(session, string_data(namearg->value), NULL,
SAM_UNKNOWN, message);
}
} else if (ARG_IS(0,"STREAM")) {
sam_sid_t stream_id;
arg = ARG_FIND("ID");
assert(arg != 0);
stream_id = strtol_checked(string_data(arg->value));
if(ARG_IS(1,"CLOSED")) {
arg = ARG_FIND("RESULT");
assert(arg != NULL);
if (string_is(arg->value,"OK")) {
sam_closeback(session, stream_id, SAM_OK, message);
} else if (string_is(arg->value,"CANT_REACH_PEER")) {
sam_closeback(session, stream_id, SAM_CANT_REACH_PEER, message);
} else if (string_is(arg->value,"I2P_ERROR")) {
sam_closeback(session, stream_id, SAM_I2P_ERROR, message);
} else if (string_is(arg->value,"PEER_NOT_FOUND")) {
sam_closeback(session, stream_id, SAM_PEER_NOT_FOUND, message);
} else if (string_is(arg->value,"TIMEOUT")) {
sam_closeback(session, stream_id, SAM_TIMEOUT, message);
} else {
sam_closeback(session, stream_id, SAM_UNKNOWN, message);
}
} else if(ARG_IS(1,"CONNECTED")) {
sam_pubkey_t dest;
arg = ARG_FIND("DESTINATION");
assert(arg != NULL);
_scr(arg->value, dest, sizeof dest);
sam_connectback(session, stream_id, dest);
} else if(ARG_IS(1,"RECEIVED")) {
size_t size;
void *data;
p = strrchr(s, '='); /* SIZE= */
assert(p != NULL);
p++;
size = strtol(p, NULL, 10);
assert(size != 0);
p -= 6;
*p = '\0';
p = strrchr(s, '='); /* ID= */
assert(p != NULL);
p++;
stream_id = strtol(p, NULL, 10);
assert(stream_id != 0);
data = malloc(size + 1); /* +1 for NUL termination, so when we are
receiving a string it will just work and it
won't be necessary to send NUL. When binary
data is sent, the extra NUL character will
just be ignored by the client program,
because it is not added to the size */
arg = ARG_FIND("SIZE");
assert(arg != NULL);
size = strtol_checked(string_data(arg->value));
data = malloc(size + 1);
/* +1 for NUL termination, so when we are
receiving a string it will just work and it
won't be necessary to send NUL. When binary
data is sent, the extra NUL character will
just be ignored by the client program,
because it is not added to the size */
if (data == NULL) {
SAMLOGS("Out of memory");
abort();
}
if (sam_read2(session, data, size) != -1) {
p = data + size;
char* p = data + size;
*p = '\0'; /* see above NUL note */
sam_databack(session, stream_id, data, size);
/* ^^^ `data' must be freed ^^^*/
} else
free(data);
return;
} else if (strncmp(s, SAM_STREAM_STATUS_REPLY,
strlen(SAM_STREAM_STATUS_REPLY)) == 0) {
char *p;
sam_sid_t stream_id;
p = strchr(s, '='); /* can't use strrchar because of option MESSAGE= */
assert(p != NULL); /* RESULT= */
p++;
p = strchr(p, '='); /* ID= */
assert(p != NULL);
p++;
stream_id = strtol(p, NULL, 10);
assert(stream_id != 0);
if (strncmp(s, SAM_STREAM_STATUS_REPLY_OK,
strlen(SAM_STREAM_STATUS_REPLY_OK)) == 0)
sam_statusback(session, stream_id, SAM_OK);
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_CRP,
strlen(SAM_STREAM_STATUS_REPLY_CRP)) == 0)
sam_statusback(session, stream_id, SAM_CANT_REACH_PEER);
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_I2E,
strlen(SAM_STREAM_STATUS_REPLY_I2E)) == 0)
sam_statusback(session, stream_id, SAM_I2P_ERROR);
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_IK,
strlen(SAM_STREAM_STATUS_REPLY_IK)) == 0)
sam_statusback(session, stream_id, SAM_INVALID_KEY);
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_TO,
strlen(SAM_STREAM_STATUS_REPLY_TO)) == 0)
sam_statusback(session, stream_id, SAM_TIMEOUT);
else
sam_statusback(session, stream_id, SAM_UNKNOWN);
return;
} else
SAMLOG("Unknown SAM command received: %s", s);
return;
} else if(ARG_IS(1,"STATUS")) {
arg = ARG_FIND("RESULT");
assert(arg != NULL);
if (string_is(arg->value,"OK")) {
sam_statusback(session, stream_id, SAM_OK, message);
} else if (string_is(arg->value,"CANT_REACH_PEER")) {
sam_statusback(session, stream_id,
SAM_CANT_REACH_PEER, message);
} else if (string_is(arg->value,"I2P_ERROR")) {
sam_statusback(session, stream_id, SAM_I2P_ERROR, message);
} else if (string_is(arg->value,"INVALID_KEY")) {
sam_statusback(session, stream_id, SAM_INVALID_KEY, message);
} else if (string_is(arg->value,"TIMEOUT")) {
sam_statusback(session, stream_id, SAM_TIMEOUT, message);
} else {
sam_statusback(session, stream_id, SAM_UNKNOWN, message);
}
}
} else
return 0;
return -1;
}
#undef ARG_IS
#undef ARG_FIND
/*
* Sends data to a destination in a raw packet
*

128
apps/sam/c/src/tinystring.c Normal file
View File

@ -0,0 +1,128 @@
#include "tinystring.h"
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <malloc.h>
#define _GNU_SOURCE
#include <string.h>
#ifndef min
#define min(a,b) ((a) > (b) ? (b) : (a))
#endif
extern char *strndup(const char *s, size_t n);
struct string_s {
const char* data;
long int size;
bool _no_del; //SIGH...
};
string_t string_ncreate(const char* cstr,long int length) {
string_t self = malloc(sizeof(struct string_s));
self->size = length;
if(cstr) self->data = strndup(cstr,length);
else self->data = NULL;
self->_no_del = 0;
return self;
}
string_t string_create(const char* cstr) {
if(!cstr)
return string_ncreate(NULL, 0);
return string_ncreate(cstr, strlen(cstr));
}
string_t string_nwrap(const char* cstr, long int length) {
static struct string_s self;
self.size = length;
self.data = cstr;
self._no_del = 1;
return &self;
}
string_t string_wrap(const char* cstr) {
if(!cstr)
return string_nwrap(NULL, 0);
return string_nwrap(cstr, strlen(cstr));
}
string_t string_fmt(const char* fmt, ...) {
va_list args;
FILE* tmp = tmpfile();
string_t self = malloc(sizeof(struct string_s));
char* data;
va_start(args, fmt);
vfprintf(tmp, fmt, args);
va_end(args);
self->size = ftell(tmp);
rewind(tmp);
data = malloc(self->size);
fread(data, self->size, sizeof(char), tmp);
fclose(tmp);
self->data = data;
return self;
}
string_t string_cat(string_t head,string_t tail) {
//There are two ways to skin a cat...
string_t self = malloc(sizeof(struct string_s));
char* data;
self->size = head->size+tail->size;
data = malloc(self->size);
memcpy(data, head->data, head->size);
memcpy(data+head->size,tail->data,tail->size);
self->data = data;
return self;
}
/* Source Dest */
void string_copy(string_t src,string_t dest) {
dest->data = realloc((char*)dest->data,src->size);
memcpy((char*)dest->data,src->data,dest->size);
}
void string_copy_raw(string_t src, void* dest,size_t size) {
size = min(src->size,size);
memcpy(dest,src->data,size);
}
const char* string_data(string_t self) {
return self->data;
}
long int string_size(string_t self) {
return self->size;
}
void string_free(string_t self) {
if(!self->_no_del)
free((char*)self->data);
free(self);
}
#ifndef min
#define min(a,b) ((a) < (b) ? (a) : (b))
#endif
bool string_equal(string_t this,string_t that) {
return !memcmp(this->data,that->data,min(this->size,that->size));
}
bool string_equali(string_t this,string_t that) {
return !strncasecmp(this->data,that->data,min(this->size,that->size));
}
int string_cmp(string_t this,string_t that) {
return memcmp(this->data,that->data,min(this->size,that->size));
}
int string_cmpi(string_t this,string_t that) {
return strncasecmp(this->data,that->data,min(this->size,that->size));
}

4
apps/sam/c/status Normal file
View File

@ -0,0 +1,4 @@
#!/usr/bin/env perl
printf "%-8s ",uc(shift @ARGV);
print join(' ', @ARGV),"\n";

View File

@ -3,7 +3,7 @@
<target name="bin" description="Builds assemblies from source">
<mkdir dir="bin" />
<csc target="dll" output="bin/sam-sharp.dll">
<csc target="library" output="bin/sam-sharp.dll">
<sources>
<include name="src/**/*.cs" />
</sources>

View File

@ -65,14 +65,15 @@ public class Connection {
private Object _connectLock;
/** how many messages have been resent and not yet ACKed? */
private int _activeResends;
private ConEvent _connectionEvent;
private long _lifetimeBytesSent;
private long _lifetimeBytesReceived;
private long _lifetimeDupMessageSent;
private long _lifetimeDupMessageReceived;
public static final long MAX_RESEND_DELAY = 20*1000;
public static final long MIN_RESEND_DELAY = 10*1000;
public static final long MAX_RESEND_DELAY = 10*1000;
public static final long MIN_RESEND_DELAY = 3*1000;
/** wait up to 5 minutes after disconnection so we can ack/close packets */
public static int DISCONNECT_TIMEOUT = 5*60*1000;
@ -116,9 +117,12 @@ public class Connection {
_connectLock = new Object();
_activeResends = 0;
_resetSentOn = -1;
_connectionEvent = new ConEvent();
_context.statManager().createRateStat("stream.con.windowSizeAtCongestion", "How large was our send window when we send a dup?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("stream.chokeSizeBegin", "How many messages were outstanding when we started to choke?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("stream.chokeSizeEnd", "How many messages were outstanding when we stopped being choked?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
if (_log.shouldLog(Log.DEBUG))
_log.debug("New connection created with options: " + _options);
}
public long getNextOutboundPacketNum() {
@ -152,7 +156,8 @@ public class Connection {
if (!_connected)
return false;
started = true;
if ( (_outboundPackets.size() >= _options.getWindowSize()) || (_activeResends > 0) ) {
if ( (_outboundPackets.size() >= _options.getWindowSize()) || (_activeResends > 0) ||
(_lastSendId - _highestAckedThrough > _options.getWindowSize()) ) {
if (writeExpire > 0) {
if (timeLeft <= 0) {
_log.error("Outbound window is full of " + _outboundPackets.size()
@ -802,9 +807,29 @@ public class Connection {
buf.append(" close received");
buf.append(" acked packets ").append(getAckedPackets());
buf.append(" maxWin ").append(getOptions().getMaxWindowSize());
buf.append("]");
return buf.toString();
}
public SimpleTimer.TimedEvent getConnectionEvent() { return _connectionEvent; }
/**
* fired to reschedule event notification
*/
class ConEvent implements SimpleTimer.TimedEvent {
private Exception _addedBy;
public ConEvent() {
//_addedBy = new Exception("added by");
}
public void timeReached() {
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("firing event on " + _connection, _addedBy);
eventOccurred();
}
public String toString() { return "event on " + Connection.this.toString(); }
}
/**
* Coordinate the resends of a given packet
@ -864,14 +889,15 @@ public class Connection {
newWindowSize /= 2;
if (newWindowSize <= 0)
newWindowSize = 1;
if (_log.shouldLog(Log.WARN))
_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);
if (_log.shouldLog(Log.WARN))
_log.warn("Congestion resending packet " + _packet.getSequenceNum() + ": new windowSize " + newWindowSize
+ "/" + getOptions().getWindowSize() + ") for " + Connection.this.toString());
windowAdjusted();
}
}

View File

@ -68,6 +68,7 @@ public class ConnectionManager {
_context.statManager().createRateStat("stream.con.lifetimeRTT", "What is the final RTT when a stream closes?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("stream.con.lifetimeCongestionSeenAt", "When was the last congestion seen at when a stream closes?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("stream.con.lifetimeSendWindowSize", "What is the final send window size when a stream closes?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("stream.receiveActive", "How many streams are active when a new one is received (period being not yet dropped)", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
}
Connection getConnectionByInboundId(byte[] id) {
@ -109,7 +110,14 @@ public class ConnectionManager {
byte receiveId[] = new byte[4];
_context.random().nextBytes(receiveId);
boolean reject = false;
int active = 0;
int total = 0;
synchronized (_connectionLock) {
total = _connectionByInboundId.size();
for (Iterator iter = _connectionByInboundId.values().iterator(); iter.hasNext(); ) {
if ( ((Connection)iter.next()).getIsConnected() )
active++;
}
if (locked_tooManyStreams()) {
reject = true;
} else {
@ -121,12 +129,16 @@ public class ConnectionManager {
} else {
_connectionByInboundId.put(ba, oldCon);
// receiveId already taken, try another
// (need to realloc receiveId, as ba.getData() points to the old value)
receiveId = new byte[4];
_context.random().nextBytes(receiveId);
}
}
}
}
_context.statManager().addRateData("stream.receiveActive", active, total);
if (reject) {
if (_log.shouldLog(Log.WARN))
_log.warn("Refusing connection since we have exceeded our max of "
@ -227,6 +239,8 @@ public class ConnectionManager {
}
private boolean locked_tooManyStreams() {
if (_maxConcurrentStreams <= 0) return false;
if (_connectionByInboundId.size() < _maxConcurrentStreams) return false;
int active = 0;
for (Iterator iter = _connectionByInboundId.values().iterator(); iter.hasNext(); ) {
Connection con = (Connection)iter.next();
@ -238,8 +252,6 @@ public class ConnectionManager {
_log.info("More than 100 connections! " + active
+ " total: " + _connectionByInboundId.size());
if (_maxConcurrentStreams <= 0) return false;
if (_connectionByInboundId.size() < _maxConcurrentStreams) return false;
return (active >= _maxConcurrentStreams);
}

View File

@ -98,8 +98,8 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
setInactivityTimeout(getInt(opts, PROP_INACTIVITY_TIMEOUT, 5*60*1000));
setInactivityAction(getInt(opts, PROP_INACTIVITY_ACTION, INACTIVITY_ACTION_DISCONNECT));
setInboundBufferSize(getMaxMessageSize() * (Connection.MAX_WINDOW_SIZE + 2));
setCongestionAvoidanceGrowthRateFactor(getInt(opts, PROP_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR, 2));
setSlowStartGrowthRateFactor(getInt(opts, PROP_SLOW_START_GROWTH_RATE_FACTOR, 2));
setCongestionAvoidanceGrowthRateFactor(getInt(opts, PROP_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR, 1));
setSlowStartGrowthRateFactor(getInt(opts, PROP_SLOW_START_GROWTH_RATE_FACTOR, 1));
setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
setMaxWindowSize(getInt(opts, PROP_MAX_WINDOW_SIZE, Connection.MAX_WINDOW_SIZE));

View File

@ -33,7 +33,7 @@ public class ConnectionPacketHandler {
boolean ok = verifyPacket(packet, con);
if (!ok) {
if ( (!packet.isFlagSet(Packet.FLAG_RESET)) && (_log.shouldLog(Log.ERROR)) )
_log.error("Packet does NOT verify: " + packet);
_log.error("Packet does NOT verify: " + packet + " on " + con);
packet.releasePayload();
return;
}
@ -167,11 +167,14 @@ public class ConnectionPacketHandler {
// non-ack message payloads are queued in the MessageInputStream
packet.releasePayload();
}
//if (choke)
// con.fastRetransmit();
}
private boolean ack(Connection con, long ackThrough, long nacks[], Packet packet, boolean isNew, boolean choke) {
if ( (nacks != null) && (nacks.length > 0) )
con.getOptions().setRTT(con.getOptions().getRTT() + nacks.length*1000);
//if ( (nacks != null) && (nacks.length > 0) )
// con.getOptions().setRTT(con.getOptions().getRTT() + nacks.length*1000);
int numResends = 0;
List acked = con.ackPackets(ackThrough, nacks);
@ -224,15 +227,17 @@ public class ConnectionPacketHandler {
oldSize >>>= 1;
if (oldSize <= 0)
oldSize = 1;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Congestion occurred - new windowSize " + oldSize + " congestionSeenAt: "
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
+ ") for " + con);
// setRTT has its own ceiling
con.getOptions().setRTT(con.getOptions().getRTT() + 10*1000);
con.getOptions().setWindowSize(oldSize);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Congestion occurred - new windowSize " + oldSize + " / " + con.getOptions().getWindowSize() + " congestionSeenAt: "
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
+ ") for " + con);
congested = true;
}
@ -253,7 +258,7 @@ public class ConnectionPacketHandler {
newWindowSize += 1;
} else {
// slow start, but modified to take into account the fact
// that windows in the streaming lib are messages, not bytes,
// that windows in the streaming lib are messages, not bytes,
// so we only grow 1 every N times (where N = the slow start factor)
int shouldIncrement = _context.random().nextInt(con.getOptions().getSlowStartGrowthRateFactor());
if (shouldIncrement <= 0)
@ -263,13 +268,14 @@ public class ConnectionPacketHandler {
if (newWindowSize <= 0)
newWindowSize = 1;
if (_log.shouldLog(Log.DEBUG))
_log.debug("New window size " + newWindowSize + "/" + oldWindow + " congestionSeenAt: "
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
+ ") for " + con);
con.getOptions().setWindowSize(newWindowSize);
con.setCongestionWindowEnd(newWindowSize + lowest);
if (_log.shouldLog(Log.DEBUG))
_log.debug("New window size " + newWindowSize + "/" + oldWindow + "/" + con.getOptions().getWindowSize() + " congestionSeenAt: "
+ con.getLastCongestionSeenAt() + " (#resends: " + numResends
+ ") for " + con);
}
con.windowAdjusted();
@ -299,16 +305,16 @@ public class ConnectionPacketHandler {
if (packet.getSequenceNum() <= 2) {
return true;
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Packet without RST or SYN where we dont know stream ID: "
if (_log.shouldLog(Log.ERROR))
_log.error("Packet without RST or SYN where we dont know stream ID: "
+ packet);
return false;
}
}
} else {
if (!DataHelper.eq(con.getSendStreamId(), packet.getReceiveStreamId())) {
if (_log.shouldLog(Log.WARN))
_log.warn("Packet received with the wrong reply stream id: "
if (_log.shouldLog(Log.ERROR))
_log.error("Packet received with the wrong reply stream id: "
+ con + " / " + packet);
return false;
} else {
@ -325,8 +331,8 @@ public class ConnectionPacketHandler {
if (DataHelper.eq(con.getReceiveStreamId(), packet.getSendStreamId())) {
boolean ok = packet.verifySignature(_context, packet.getOptionalFrom(), null);
if (!ok) {
if (_log.shouldLog(Log.WARN))
_log.warn("Received unsigned / forged RST on " + con);
if (_log.shouldLog(Log.ERROR))
_log.error("Received unsigned / forged RST on " + con);
return;
} else {
if (_log.shouldLog(Log.DEBUG))

View File

@ -91,8 +91,8 @@ public class MessageHandler implements I2PSessionListener {
*
*/
public void errorOccurred(I2PSession session, String message, Throwable error) {
if (_log.shouldLog(Log.ERROR))
_log.error("error occurred: " + message + "- " + error.getMessage());
if (_log.shouldLog(Log.WARN))
_log.warn("error occurred: " + message + "- " + error.getMessage());
if (_log.shouldLog(Log.WARN))
_log.warn("cause", error);
//_manager.disconnectAllHard();

View File

@ -38,6 +38,10 @@ public class MessageOutputStream extends OutputStream {
* size
*/
private volatile int _nextBufferSize;
// rate calc helpers
private long _sendPeriodBeginTime;
private long _sendPeriodBytes;
private int _sendBps;
public MessageOutputStream(I2PAppContext ctx, DataReceiver receiver) {
this(ctx, receiver, Packet.MAX_PAYLOAD_SIZE);
@ -55,6 +59,10 @@ public class MessageOutputStream extends OutputStream {
_writeTimeout = -1;
_passiveFlushDelay = 500;
_nextBufferSize = -1;
_sendPeriodBeginTime = ctx.clock().now();
_sendPeriodBytes = 0;
_sendBps = 0;
_context.statManager().createRateStat("stream.sendBps", "How fast we pump data through the stream", "Stream", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
_flusher = new Flusher();
if (_log.shouldLog(Log.DEBUG))
_log.debug("MessageOutputStream created");
@ -137,6 +145,21 @@ public class MessageOutputStream extends OutputStream {
if ( (elapsed > 10*1000) && (_log.shouldLog(Log.DEBUG)) )
_log.debug("wtf, took " + elapsed + "ms to write to the stream?", new Exception("foo"));
throwAnyError();
updateBps(len);
}
private void updateBps(int len) {
long now = _context.clock().now();
int periods = (int)Math.floor((now - _sendPeriodBeginTime) / 1000d);
if (periods > 0) {
// first term decays on slow transmission
_sendBps = (int)(((float)0.9f*((float)_sendBps/(float)periods)) + ((float)0.1f*((float)_sendPeriodBytes/(float)periods)));
_sendPeriodBytes = len;
_sendPeriodBeginTime = now;
_context.statManager().addRateData("stream.sendBps", _sendBps, 0);
} else {
_sendPeriodBytes += len;
}
}
public void write(int b) throws IOException {

View File

@ -119,6 +119,19 @@ public class PacketHandler {
}
private void receiveKnownCon(Connection con, Packet packet) {
if (packet.isFlagSet(Packet.FLAG_ECHO)) {
if (packet.getSendStreamId() != null) {
receivePing(packet);
} else if (packet.getReceiveStreamId() != null) {
receivePong(packet);
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Echo packet received with no stream IDs: " + packet);
}
packet.releasePayload();
return;
}
// the packet is pointed at a stream ID we're receiving on
if (isValidMatch(con.getSendStreamId(), packet.getReceiveStreamId())) {
// the packet's receive stream ID also matches what we expect
@ -163,8 +176,19 @@ public class PacketHandler {
} 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);
if (_log.shouldLog(Log.ERROR)) {
Set cons = _manager.listConnections();
StringBuffer buf = new StringBuffer(512);
buf.append("Received a packet on the wrong stream: ");
buf.append(packet);
buf.append(" connection: ");
buf.append(con);
for (Iterator iter = cons.iterator(); iter.hasNext();) {
Connection cur = (Connection)iter.next();
buf.append(" ").append(cur);
}
_log.error(buf.toString(), new Exception("Wrong stream"));
}
}
packet.releasePayload();
}

View File

@ -17,21 +17,6 @@ abstract class SchedulerImpl implements TaskScheduler {
}
protected void reschedule(long msToWait, Connection con) {
SimpleTimer.getInstance().addEvent(new ConEvent(con), msToWait);
}
private class ConEvent implements SimpleTimer.TimedEvent {
private Connection _connection;
private Exception _addedBy;
public ConEvent(Connection con) {
_connection = con;
//_addedBy = new Exception("added by");
}
public void timeReached() {
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("firing event on " + _connection, _addedBy);
_connection.eventOccurred();
}
public String toString() { return "event on " + _connection; }
SimpleTimer.getInstance().addEvent(con.getConnectionEvent(), msToWait);
}
}

View File

@ -1,239 +1,239 @@
/*
* Created on Nov 9, 2004
*
* This file is part of susimail project, see http://susi.i2p/
*
* Copyright (C) 2004-2005 <susi23@mail.i2p>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* 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.1 $
*/
package i2p.susi.webmail;
import i2p.susi.util.Config;
import i2p.susi.util.ReadBuffer;
import i2p.susi.webmail.encoding.Encoding;
import i2p.susi.webmail.encoding.EncodingFactory;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Locale;
/**
* data structure to hold a single message, mostly used with folder view and sorting
*
* @author susi
*/
public class Mail {
public static final String DATEFORMAT = "date.format";
public static final String unknown = "unknown";
public int id, size;
public String sender, reply, subject, dateString,
formattedSender, formattedSubject, formattedDate,
shortSender, shortSubject, quotedDate, uidl;
public Date date;
public ReadBuffer header, body;
public MailPart part;
Object[] to, cc;
public String error;
public boolean markForDeletion;
public boolean deleted;
public Mail() {
id = 0;
size = 0;
formattedSender = unknown;
formattedSubject = unknown;
formattedDate = unknown;
shortSender = unknown;
shortSubject = unknown;
quotedDate = unknown;
error = "";
}
/**
*
* @param address
* @return
*/
public static boolean validateAddress( String address )
{
if( address == null || address.length() == 0 )
return false;
address = address.trim();
if( address.indexOf( "\n" ) != -1 ||
address.indexOf( "\r" ) != -1 )
return false;
String[] tokens = address.split( "[ \t]+" );
int addresses = 0;
for( int i = 0; i < tokens.length; i++ ) {
if( tokens[i].matches( "^[^@< \t]+@[^> \t]+$" ) ||
tokens[i].matches( "^<[^@< \t]+@[^> \t]+>$" ) )
addresses++;
}
return addresses == 1;
}
/**
* @param address
* @return
*/
public static String getAddress(String address )
{
String[] tokens = address.split( "[ \t]+" );
for( int i = 0; i < tokens.length; i++ ) {
if( tokens[i].matches( "^[^@< \t]+@[^> \t]+$" ) )
return "<" + tokens[i] + ">";
if( tokens[i].matches( "^<[^@< \t]+@[^> \t]+>$" ) )
return tokens[i];
}
return null;
}
public static boolean getRecipientsFromList( ArrayList recipients, String text, boolean ok )
{
if( text != null && text.length() > 0 ) {
String[] ccs = text.split( "," );
for( int i = 0; i < ccs.length; i++ ) {
String recipient = ccs[i].trim();
if( validateAddress( recipient ) ) {
String str = getAddress( recipient );
if( str != null && str.length() > 0 ) {
recipients.add( str );
}
else {
ok = false;
}
}
else {
ok = false;
}
}
}
return ok;
}
public static void appendRecipients( StringBuffer buf, ArrayList recipients, String prefix )
{
for( Iterator it = recipients.iterator(); it.hasNext(); ) {
buf.append( prefix );
prefix ="\t";
buf.append( (String)it.next() );
buf.append( "\r\n" );
}
}
public void parseHeaders()
{
DateFormat dateFormatter = new SimpleDateFormat( Config.getProperty( DATEFORMAT, "mm/dd/yyyy HH:mm:ss" ) );
DateFormat mailDateFormatter = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH );
error = "";
if( header != null ) {
boolean ok = true;
Encoding html = EncodingFactory.getEncoding( "HTML" );
if( html == null ) {
error += "HTML encoder not found.<br>";
ok = false;
}
Encoding hl = EncodingFactory.getEncoding( "HEADERLINE" );
if( hl == null ) {
error += "Header line encoder not found.<br>";
ok = false;
}
if( ok ) {
try {
ReadBuffer decoded = hl.decode( header );
BufferedReader reader = new BufferedReader( new InputStreamReader( new ByteArrayInputStream( decoded.content, decoded.offset, decoded.length ), "ISO-8859-1" ) );
String line;
while( ( line = reader.readLine() ) != null ) {
if( line.length() == 0 )
break;
if( line.startsWith( "From:" ) ) {
sender = line.substring( 5 ).trim();
formattedSender = getAddress( sender );
shortSender = formattedSender.trim();
if( shortSender.length() > 40 ) {
shortSender = shortSender.substring( 0, 37 ).trim() + "...";
}
shortSender = html.encode( shortSender );
}
else if( line.startsWith( "Date:" ) ) {
dateString = line.substring( 5 ).trim();
try {
date = mailDateFormatter.parse( dateString );
formattedDate = dateFormatter.format( date );
quotedDate = html.encode( dateString );
}
catch (ParseException e) {
date = null;
e.printStackTrace();
}
}
else if( line.startsWith( "Subject:" ) ) {
subject = line.substring( 8 ).trim();
formattedSubject = subject;
shortSubject = formattedSubject;
if( formattedSubject.length() > 60 )
shortSubject = formattedSubject.substring( 0, 57 ).trim() + "...";
shortSubject = html.encode( shortSubject );
}
else if( line.toLowerCase().startsWith( "Reply-To:" ) ) {
reply = Mail.getAddress( line.substring( 9 ).trim() );
}
else if( line.startsWith( "To:" ) ) {
ArrayList list = new ArrayList();
Mail.getRecipientsFromList( list, line.substring( 3 ).trim(), true );
to = list.toArray();
}
else if( line.startsWith( "Cc:" ) ) {
ArrayList list = new ArrayList();
Mail.getRecipientsFromList( list, line.substring( 3 ).trim(), true );
cc = list.toArray();
}
}
}
catch( Exception e ) {
error += "Error parsing mail header: " + e.getClass().getName() + "<br>";
}
}
}
}
}
/*
* Created on Nov 9, 2004
*
* This file is part of susimail project, see http://susi.i2p/
*
* Copyright (C) 2004-2005 <susi23@mail.i2p>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* 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.2 $
*/
package i2p.susi.webmail;
import i2p.susi.util.Config;
import i2p.susi.util.ReadBuffer;
import i2p.susi.webmail.encoding.Encoding;
import i2p.susi.webmail.encoding.EncodingFactory;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Locale;
/**
* data structure to hold a single message, mostly used with folder view and sorting
*
* @author susi
*/
public class Mail {
public static final String DATEFORMAT = "date.format";
public static final String unknown = "unknown";
public int id, size;
public String sender, reply, subject, dateString,
formattedSender, formattedSubject, formattedDate,
shortSender, shortSubject, quotedDate, uidl;
public Date date;
public ReadBuffer header, body;
public MailPart part;
Object[] to, cc;
public String error;
public boolean markForDeletion;
public boolean deleted;
public Mail() {
id = 0;
size = 0;
formattedSender = unknown;
formattedSubject = unknown;
formattedDate = unknown;
shortSender = unknown;
shortSubject = unknown;
quotedDate = unknown;
error = "";
}
/**
*
* @param address
* @return
*/
public static boolean validateAddress( String address )
{
if( address == null || address.length() == 0 )
return false;
address = address.trim();
if( address.indexOf( "\n" ) != -1 ||
address.indexOf( "\r" ) != -1 )
return false;
String[] tokens = address.split( "[ \t]+" );
int addresses = 0;
for( int i = 0; i < tokens.length; i++ ) {
if( tokens[i].matches( "^[^@< \t]+@[^> \t]+$" ) ||
tokens[i].matches( "^<[^@< \t]+@[^> \t]+>$" ) )
addresses++;
}
return addresses == 1;
}
/**
* @param address
* @return
*/
public static String getAddress(String address )
{
String[] tokens = address.split( "[ \t]+" );
for( int i = 0; i < tokens.length; i++ ) {
if( tokens[i].matches( "^[^@< \t]+@[^> \t]+$" ) )
return "<" + tokens[i] + ">";
if( tokens[i].matches( "^<[^@< \t]+@[^> \t]+>$" ) )
return tokens[i];
}
return null;
}
public static boolean getRecipientsFromList( ArrayList recipients, String text, boolean ok )
{
if( text != null && text.length() > 0 ) {
String[] ccs = text.split( "," );
for( int i = 0; i < ccs.length; i++ ) {
String recipient = ccs[i].trim();
if( validateAddress( recipient ) ) {
String str = getAddress( recipient );
if( str != null && str.length() > 0 ) {
recipients.add( str );
}
else {
ok = false;
}
}
else {
ok = false;
}
}
}
return ok;
}
public static void appendRecipients( StringBuffer buf, ArrayList recipients, String prefix )
{
for( Iterator it = recipients.iterator(); it.hasNext(); ) {
buf.append( prefix );
prefix ="\t";
buf.append( (String)it.next() );
buf.append( "\r\n" );
}
}
public void parseHeaders()
{
DateFormat dateFormatter = new SimpleDateFormat( Config.getProperty( DATEFORMAT, "mm/dd/yyyy HH:mm:ss" ) );
DateFormat mailDateFormatter = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH );
error = "";
if( header != null ) {
boolean ok = true;
Encoding html = EncodingFactory.getEncoding( "HTML" );
if( html == null ) {
error += "HTML encoder not found.<br>";
ok = false;
}
Encoding hl = EncodingFactory.getEncoding( "HEADERLINE" );
if( hl == null ) {
error += "Header line encoder not found.<br>";
ok = false;
}
if( ok ) {
try {
ReadBuffer decoded = hl.decode( header );
BufferedReader reader = new BufferedReader( new InputStreamReader( new ByteArrayInputStream( decoded.content, decoded.offset, decoded.length ), "ISO-8859-1" ) );
String line;
while( ( line = reader.readLine() ) != null ) {
if( line.length() == 0 )
break;
if( line.startsWith( "From:" ) ) {
sender = line.substring( 5 ).trim();
formattedSender = getAddress( sender );
shortSender = formattedSender.trim();
if( shortSender.length() > 40 ) {
shortSender = shortSender.substring( 0, 37 ).trim() + "...";
}
shortSender = html.encode( shortSender );
}
else if( line.startsWith( "Date:" ) ) {
dateString = line.substring( 5 ).trim();
try {
date = mailDateFormatter.parse( dateString );
formattedDate = dateFormatter.format( date );
quotedDate = html.encode( dateString );
}
catch (ParseException e) {
date = null;
e.printStackTrace();
}
}
else if( line.startsWith( "Subject:" ) ) {
subject = line.substring( 8 ).trim();
formattedSubject = subject;
shortSubject = formattedSubject;
if( formattedSubject.length() > 60 )
shortSubject = formattedSubject.substring( 0, 57 ).trim() + "...";
shortSubject = html.encode( shortSubject );
}
else if( line.toLowerCase().startsWith( "Reply-To:" ) ) {
reply = Mail.getAddress( line.substring( 9 ).trim() );
}
else if( line.startsWith( "To:" ) ) {
ArrayList list = new ArrayList();
Mail.getRecipientsFromList( list, line.substring( 3 ).trim(), true );
to = list.toArray();
}
else if( line.startsWith( "Cc:" ) ) {
ArrayList list = new ArrayList();
Mail.getRecipientsFromList( list, line.substring( 3 ).trim(), true );
cc = list.toArray();
}
}
}
catch( Exception e ) {
error += "Error parsing mail header: " + e.getClass().getName() + "<br>";
}
}
}
}
}

View File

@ -1,102 +1,102 @@
/*
* Created on Nov 23, 2004
*
* This file is part of susimail project, see http://susi.i2p/
*
* Copyright (C) 2004-2005 <susi23@mail.i2p>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* 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.1 $
*/
package i2p.susi.webmail;
import java.util.Hashtable;
import i2p.susi.webmail.pop3.POP3MailBox;
/**
* @author user
*/
public class MailCache {
public static final boolean FETCH_HEADER = true;
public static final boolean FETCH_ALL = false;
private POP3MailBox mailbox;
private String error;
private Hashtable mails;
private Object synchronizer;
MailCache( POP3MailBox mailbox ) {
this.mailbox = mailbox;
mails = new Hashtable();
synchronizer = new Object();
}
/**
* Fetch any needed data from pop3 server.
*
* @param id message id to get
* @param headerOnly fetch only header lines?
* @return
*/
public Mail getMail( String uidl, boolean headerOnly ) {
Mail mail = null, newMail = null;
if( mailbox != null ) {
/*
* synchronize update to hashtable
*/
synchronized( synchronizer ) {
mail = (Mail)mails.get( uidl );
if( mail == null ) {
newMail = new Mail();
mails.put( uidl, newMail );
}
}
if( mail == null ) {
mail = newMail;
mail.uidl = uidl;
mail.size = mailbox.getSize( uidl );
}
if( mail.size < 1024 )
headerOnly = false;
boolean parseHeaders = mail.header == null;
if( headerOnly ) {
if( mail.header == null )
mail.header = mailbox.getHeader( uidl );
}
else {
if( mail.body == null ) {
mail.body = mailbox.getBody( uidl );
if( mail.body != null ) {
mail.header = mail.body;
MailPart.parse( mail );
}
}
}
if( parseHeaders && mail.header != null )
mail.parseHeaders();
}
if( mail != null && mail.deleted )
mail = null;
return mail;
}
}
/*
* Created on Nov 23, 2004
*
* This file is part of susimail project, see http://susi.i2p/
*
* Copyright (C) 2004-2005 <susi23@mail.i2p>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* 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.2 $
*/
package i2p.susi.webmail;
import java.util.Hashtable;
import i2p.susi.webmail.pop3.POP3MailBox;
/**
* @author user
*/
public class MailCache {
public static final boolean FETCH_HEADER = true;
public static final boolean FETCH_ALL = false;
private POP3MailBox mailbox;
private String error;
private Hashtable mails;
private Object synchronizer;
MailCache( POP3MailBox mailbox ) {
this.mailbox = mailbox;
mails = new Hashtable();
synchronizer = new Object();
}
/**
* Fetch any needed data from pop3 server.
*
* @param id message id to get
* @param headerOnly fetch only header lines?
* @return
*/
public Mail getMail( String uidl, boolean headerOnly ) {
Mail mail = null, newMail = null;
if( mailbox != null ) {
/*
* synchronize update to hashtable
*/
synchronized( synchronizer ) {
mail = (Mail)mails.get( uidl );
if( mail == null ) {
newMail = new Mail();
mails.put( uidl, newMail );
}
}
if( mail == null ) {
mail = newMail;
mail.uidl = uidl;
mail.size = mailbox.getSize( uidl );
}
if( mail.size < 1024 )
headerOnly = false;
boolean parseHeaders = mail.header == null;
if( headerOnly ) {
if( mail.header == null )
mail.header = mailbox.getHeader( uidl );
}
else {
if( mail.body == null ) {
mail.body = mailbox.getBody( uidl );
if( mail.body != null ) {
mail.header = mail.body;
MailPart.parse( mail );
}
}
}
if( parseHeaders && mail.header != null )
mail.parseHeaders();
}
if( mail != null && mail.deleted )
mail = null;
return mail;
}
}

View File

@ -92,11 +92,7 @@ public class UrlLauncher {
} else {
if (_shellCommand.executeSilentAndWaitTimed("konqueror " + url, 5))
return true;
if (_shellCommand.executeSilentAndWaitTimed("galeon " + url, 5))
return true;
// fall through
}
if (_shellCommand.executeSilentAndWaitTimed("opera -newpage " + url, 5))
@ -111,11 +107,18 @@ public class UrlLauncher {
if (_shellCommand.executeSilentAndWaitTimed("netscape " + url, 5))
return true;
if (_shellCommand.executeSilentAndWaitTimed("konqueror " + url, 5))
return true;
if (_shellCommand.executeSilentAndWaitTimed("galeon " + url, 5))
return true;
if (_shellCommand.executeSilentAndWaitTimed("links " + url, 5))
return true;
if (_shellCommand.executeSilentAndWaitTimed("lynx " + url, 5))
return true;
}
return false;
}

View File

@ -76,9 +76,9 @@
windowtitle="I2P">
<sourcepath>
<pathelement location="core/java/src" />
<pathelement location="core/java/test" />
<!--<pathelement location="core/java/test" />-->
<pathelement location="router/java/src" />
<pathelement location="router/java/test" />
<!--<pathelement location="router/java/test" />-->
<pathelement location="apps/ministreaming/java/src" />
<pathelement location="apps/streaming/java/src" />
<pathelement location="apps/i2ptunnel/java/src" />
@ -89,6 +89,7 @@
<pathelement location="apps/jetty/jettylib/org.mortbay.jetty.jar" />
<pathelement location="apps/systray/java/lib/systray4j.jar" />
<pathelement location="installer/lib/wrapper/win32/wrapper.jar" />
<pathelement location="core/lib/junit.jar" />
</classpath>
</javadoc>
</target>
@ -245,7 +246,15 @@
<tarfileset dir="pkg-temp" includes="**/*" prefix="i2p" />
</tar>
</target>
<target name="updater" depends="distclean, build">
<target name="updater" depends="prepupdate">
<zip destfile="i2pupdate.zip" basedir="pkg-temp" />
</target>
<target name="updateTest" depends="prepupdate">
<ant dir="core/java/" target="jarTest" />
<copy file="core/java/build/i2ptest.jar" todir="pkg-temp/lib" />
<zip destfile="i2pupdate.zip" basedir="pkg-temp" />
</target>
<target name="prepupdate" depends="distclean, build">
<delete dir="pkg-temp" />
<copy file="build/i2p.jar" todir="pkg-temp/lib/" />
<copy file="build/i2ptunnel.jar" todir="pkg-temp/lib/" />
@ -285,7 +294,6 @@
<mkdir dir="pkg-temp/eepsite" />
<mkdir dir="pkg-temp/eepsite/webapps" />
<mkdir dir="pkg-temp/eepsite/cgi-bin" />
<zip destfile="i2pupdate.zip" basedir="pkg-temp" />
</target>
<taskdef name="izpack" classpath="${basedir}/installer/lib/izpack/standalone-compiler.jar" classname="com.izforge.izpack.ant.IzPackTask" />
<target name="installer" depends="preppkg">
@ -300,4 +308,23 @@
</jar>
<izpack input="${basedir}/installer/install.xml" output="${basedir}/install.jar" installerType="standard" basedir="${basedir}" />
</target>
<target name="test">
<ant dir="core/java/" target="test" />
</target>
<target name="junit.report">
<ant dir="core/java/" target="junit.report" />
</target>
<target name="clover.report">
<ant dir="core/java/" target="clover.report" />
</target>
<target name="test.report" depends="junit.report, clover.report"/>
<target name="useclover">
<taskdef resource="clovertasks"/>
<mkdir dir="reports/core/clover" />
<mkdir dir="reports/core/clover" />
<clover-setup initString="reports/core/clover/coverage.db"/>
</target>
<target name="fulltest">
<ant dir="core/java/" target="fulltest" />
</target>
</project>

View File

@ -8,16 +8,90 @@
<target name="compile">
<mkdir dir="./build" />
<mkdir dir="./build/obj" />
<javac srcdir="./src:./test" debug="true" source="1.3" target="1.3" deprecation="on" destdir="./build/obj" />
<javac srcdir="./src" debug="true" source="1.3" target="1.3" deprecation="on" destdir="./build/obj" />
</target>
<target name="compileTest">
<mkdir dir="./build" />
<mkdir dir="./build/obj" />
<javac srcdir="./src:./test" debug="true" source="1.3" target="1.3" deprecation="on" destdir="./build/obj" classpath="./lib/junit.jar" />
</target>
<target name="jar" depends="compile">
<jar destfile="./build/i2p.jar" basedir="./build/obj" includes="**/*.class" />
</target>
<target name="jarTest" depends="compileTest">
<jar destfile="./build/i2ptest.jar" basedir="./build/obj" includes="**/*.class" />
</target>
<target name="javadoc">
<mkdir dir="./build" />
<mkdir dir="./build/javadoc" />
<javadoc sourcepath="./src:./test" destdir="./build/javadoc" packagenames="*" use="true" splitindex="true" windowtitle="I2P SDK" />
</target>
<target name="test" depends="clean, compileTest">
<junit printsummary="on" fork="yes">
<classpath>
<pathelement path="${classpath}" />
<pathelement location="./build/obj" />
<pathelement location="./lib/junit.jar" />
<pathelement location="../../installer/lib/jbigi/jbigi.jar" />
<pathelement path="${ant.home}/lib/clover.jar"/>
</classpath>
<batchtest>
<fileset dir="./test/">
<include name="**/*Test.java" />
<exclude name="**/ElGamalAESEngineTest.java" />
<exclude name="**/StructureTest.java" />
</fileset>
</batchtest>
<formatter type="xml"/>
</junit>
<mkdir dir="../../reports/" />
<mkdir dir="../../reports/core/" />
<mkdir dir="../../reports/core/junit/" />
<delete>
<fileset dir="../../reports/core/junit">
<include name="TEST-*.xml"/>
</fileset>
</delete>
<copy todir="../../reports/core/junit">
<fileset dir=".">
<include name="TEST-*.xml"/>
</fileset>
</copy>
<delete>
<fileset dir=".">
<include name="TEST-*.xml"/>
</fileset>
</delete>
</target>
<target name="junit.report">
<junitreport todir="../../reports/core/junit">
<fileset dir="../../reports/core/junit">
<include name="TEST-*.xml"/>
</fileset>
<report format="frames" todir="../../reports/core/html/junit"/>
</junitreport>
</target>
<target name="clover.report">
<taskdef resource="clovertasks"/>
<mkdir dir="../../reports/" />
<mkdir dir="../../reports/core" />
<mkdir dir="../../reports/core/clover" />
<clover-setup initString="../../reports/core/clover/coverage.db"/>
<clover-report>
<current outfile="../../reports/core/html/clover">
<format type="html"/>
</current>
</clover-report>
</target>
<target name="test.report" depends="junit.report, clover.report"/>
<target name="useclover">
<taskdef resource="clovertasks"/>
<mkdir dir="../../reports/" />
<mkdir dir="../../reports/core/" />
<mkdir dir="../../reports/core/clover" />
<clover-setup initString="../../reports/core/clover/coverage.db"/>
</target>
<target name="fulltest" depends="useclover, test, test.report" />
<target name="clean">
<delete dir="./build" />
</target>

View File

@ -14,8 +14,8 @@ package net.i2p;
*
*/
public class CoreVersion {
public final static String ID = "$Revision: 1.34 $ $Date: 2005/04/06 10:43:26 $";
public final static String VERSION = "0.5.0.7";
public final static String ID = "$Revision: 1.36 $ $Date: 2005/07/27 14:04:49 $";
public final static String VERSION = "0.6.0.1";
public static void main(String args[]) {
System.out.println("I2P Core version: " + VERSION);

View File

@ -10,6 +10,8 @@ import net.i2p.crypto.CryptixAESEngine;
import net.i2p.crypto.DSAEngine;
import net.i2p.crypto.DummyDSAEngine;
import net.i2p.crypto.DummyElGamalEngine;
import net.i2p.crypto.DummyHMACSHA256Generator;
import net.i2p.crypto.DummyPooledRandomSource;
import net.i2p.crypto.ElGamalAESEngine;
import net.i2p.crypto.ElGamalEngine;
import net.i2p.crypto.HMACSHA256Generator;
@ -22,6 +24,7 @@ import net.i2p.stat.StatManager;
import net.i2p.util.Clock;
import net.i2p.util.LogManager;
import net.i2p.util.RandomSource;
import net.i2p.util.PooledRandomSource;
/**
* <p>Provide a base scope for accessing singletons that I2P exposes. Rather than
@ -327,8 +330,12 @@ public class I2PAppContext {
}
private void initializeHMAC() {
synchronized (this) {
if (_hmac == null)
_hmac= new HMACSHA256Generator(this);
if (_hmac == null) {
if ("true".equals(getProperty("i2p.fakeHMAC", "false")))
_hmac = new DummyHMACSHA256Generator(this);
else
_hmac= new HMACSHA256Generator(this);
}
_hmacInitialized = true;
}
}
@ -431,8 +438,12 @@ public class I2PAppContext {
}
private void initializeRandom() {
synchronized (this) {
if (_random == null)
_random = new RandomSource(this);
if (_random == null) {
if ("true".equals(getProperty("i2p.weakPRNG", "false")))
_random = new DummyPooledRandomSource(this);
else
_random = new PooledRandomSource(this);
}
_randomInitialized = true;
}
}

View File

@ -39,9 +39,14 @@ import net.i2p.util.Log;
class I2CPMessageProducer {
private final static Log _log = new Log(I2CPMessageProducer.class);
private I2PAppContext _context;
private int _sendBps;
private long _sendPeriodBytes;
private long _sendPeriodBeginTime;
public I2CPMessageProducer(I2PAppContext context) {
_context = context;
_sendBps = 0;
context.statManager().createRateStat("client.sendBpsRaw", "How fast we pump out I2CP data messages", "ClientMessages", new long[] { 60*1000, 5*60*1000, 10*60*1000, 60*60*1000 });
}
/**
@ -94,8 +99,30 @@ class I2CPMessageProducer {
Payload data = createPayload(dest, payload, tag, key, tags, newKey);
msg.setPayload(data);
session.sendMessage(msg);
updateBps(payload.length);
}
private void updateBps(int len) {
long now = _context.clock().now();
float period = ((float)now-_sendPeriodBeginTime)/1000f;
if (period >= 1f) {
// first term decays on slow transmission
_sendBps = (int)(((float)0.9f * (float)_sendBps) + ((float)0.1f*((float)_sendPeriodBytes)/period));
_sendPeriodBytes = len;
_sendPeriodBeginTime = now;
_context.statManager().addRateData("client.sendBpsRaw", _sendBps, 0);
} else {
_sendPeriodBytes += len;
}
}
/**
* Should we include the I2CP end to end crypto (which is in addition to any
* garlic crypto added by the router)
*
*/
static final boolean END_TO_END_CRYPTO = false;
/**
* Create a new signed payload and send it off to the destination
*
@ -106,6 +133,10 @@ class I2CPMessageProducer {
if (payload == null) throw new I2PSessionException("No payload specified");
Payload data = new Payload();
if (!END_TO_END_CRYPTO) {
data.setEncryptedData(payload);
return data;
}
// no padding at this level
// the garlic may pad, and the tunnels may pad, and the transports may pad
int size = payload.length;
@ -150,4 +181,4 @@ class I2CPMessageProducer {
msg.setSessionId(session.getSessionId());
session.sendMessage(msg);
}
}
}

View File

@ -29,20 +29,27 @@ import net.i2p.util.Log;
class I2PClientMessageHandlerMap {
private final static Log _log = new Log(I2PClientMessageHandlerMap.class);
/** map of message type id --> I2CPMessageHandler */
private Map _handlers;
private I2CPMessageHandler _handlers[];
public I2PClientMessageHandlerMap(I2PAppContext context) {
_handlers = new HashMap();
_handlers.put(new Integer(DisconnectMessage.MESSAGE_TYPE), new DisconnectMessageHandler(context));
_handlers.put(new Integer(SessionStatusMessage.MESSAGE_TYPE), new SessionStatusMessageHandler(context));
_handlers.put(new Integer(RequestLeaseSetMessage.MESSAGE_TYPE), new RequestLeaseSetMessageHandler(context));
_handlers.put(new Integer(MessagePayloadMessage.MESSAGE_TYPE), new MessagePayloadMessageHandler(context));
_handlers.put(new Integer(MessageStatusMessage.MESSAGE_TYPE), new MessageStatusMessageHandler(context));
_handlers.put(new Integer(SetDateMessage.MESSAGE_TYPE), new SetDateMessageHandler(context));
int highest = DisconnectMessage.MESSAGE_TYPE;
highest = Math.max(highest, SessionStatusMessage.MESSAGE_TYPE);
highest = Math.max(highest, RequestLeaseSetMessage.MESSAGE_TYPE);
highest = Math.max(highest, MessagePayloadMessage.MESSAGE_TYPE);
highest = Math.max(highest, MessageStatusMessage.MESSAGE_TYPE);
highest = Math.max(highest, SetDateMessage.MESSAGE_TYPE);
_handlers = new I2CPMessageHandler[highest+1];
_handlers[DisconnectMessage.MESSAGE_TYPE] = new DisconnectMessageHandler(context);
_handlers[SessionStatusMessage.MESSAGE_TYPE] = new SessionStatusMessageHandler(context);
_handlers[RequestLeaseSetMessage.MESSAGE_TYPE] = new RequestLeaseSetMessageHandler(context);
_handlers[MessagePayloadMessage.MESSAGE_TYPE] = new MessagePayloadMessageHandler(context);
_handlers[MessageStatusMessage.MESSAGE_TYPE] = new MessageStatusMessageHandler(context);
_handlers[SetDateMessage.MESSAGE_TYPE] = new SetDateMessageHandler(context);
}
public I2CPMessageHandler getHandler(int messageTypeId) {
I2CPMessageHandler handler = (I2CPMessageHandler) _handlers.get(new Integer(messageTypeId));
return handler;
if ( (messageTypeId < 0) || (messageTypeId >= _handlers.length) ) return null;
return _handlers[messageTypeId];
}
}

View File

@ -78,7 +78,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
/** class that generates new messages */
protected I2CPMessageProducer _producer;
/** map of integer --> MessagePayloadMessage */
/** map of Long --> MessagePayloadMessage */
private Map _availableMessages;
protected I2PClientMessageHandlerMap _handlerMap;
@ -295,7 +295,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
public byte[] receiveMessage(int msgId) throws I2PSessionException {
MessagePayloadMessage msg = null;
synchronized (_availableMessages) {
msg = (MessagePayloadMessage) _availableMessages.remove(new Integer(msgId));
msg = (MessagePayloadMessage) _availableMessages.remove(new Long(msgId));
}
if (msg == null) return null;
return msg.getPayload().getUnencryptedData();
@ -346,9 +346,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
*/
public void addNewMessage(MessagePayloadMessage msg) {
synchronized (_availableMessages) {
_availableMessages.put(new Integer(msg.getMessageId().getMessageId()), msg);
_availableMessages.put(new Long(msg.getMessageId()), msg);
}
int id = msg.getMessageId().getMessageId();
long id = msg.getMessageId();
byte data[] = msg.getPayload().getUnencryptedData();
if ((data == null) || (data.length <= 0)) {
if (_log.shouldLog(Log.CRIT))
@ -363,12 +363,12 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
SimpleTimer.getInstance().addEvent(new VerifyUsage(id), 30*1000);
}
private class VerifyUsage implements SimpleTimer.TimedEvent {
private int _msgId;
public VerifyUsage(int id) { _msgId = id; }
private long _msgId;
public VerifyUsage(long id) { _msgId = id; }
public void timeReached() {
MessagePayloadMessage removed = null;
synchronized (_availableMessages) {
removed = (MessagePayloadMessage)_availableMessages.remove(new Integer(_msgId));
removed = (MessagePayloadMessage)_availableMessages.remove(new Long(_msgId));
}
if (removed != null)
_log.log(Log.CRIT, "Message NOT removed! id=" + _msgId + ": " + removed);
@ -393,9 +393,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
}
}
public void available(int msgId, int size) {
public void available(long msgId, int size) {
synchronized (AvailabilityNotifier.this) {
_pendingIds.add(new Integer(msgId));
_pendingIds.add(new Long(msgId));
_pendingSizes.add(new Integer(size));
AvailabilityNotifier.this.notifyAll();
}
@ -403,7 +403,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
public void run() {
_alive = true;
while (_alive) {
Integer msgId = null;
Long msgId = null;
Integer size = null;
synchronized (AvailabilityNotifier.this) {
if (_pendingIds.size() <= 0) {
@ -413,7 +413,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
}
}
if (_pendingIds.size() > 0) {
msgId = (Integer)_pendingIds.remove(0);
msgId = (Long)_pendingIds.remove(0);
size = (Integer)_pendingSizes.remove(0);
}
}
@ -532,8 +532,8 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* Pass off the error to the listener
*/
void propogateError(String msg, Throwable error) {
if (_log.shouldLog(Log.ERROR))
_log.error(getPrefix() + "Error occurred: " + msg + " - " + error.getMessage());
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + "Error occurred: " + msg + " - " + error.getMessage());
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + " cause", error);

View File

@ -119,49 +119,57 @@ 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");
SessionKey key = null;
SessionKey newKey = null;
SessionTag tag = null;
Set sentTags = null;
int oldTags = _context.sessionKeyManager().getAvailableTags(dest.getPublicKey(), key);
long availTimeLeft = _context.sessionKeyManager().getAvailableTimeLeft(dest.getPublicKey(), key);
int oldTags = 0;
long begin = _context.clock().now();
if (I2CPMessageProducer.END_TO_END_CRYPTO) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("begin sendBestEffort");
key = _context.sessionKeyManager().getCurrentKey(dest.getPublicKey());
if (_log.shouldLog(Log.DEBUG)) _log.debug("key fetched");
if (key == null) key = _context.sessionKeyManager().createSession(dest.getPublicKey());
tag = _context.sessionKeyManager().consumeNextAvailableTag(dest.getPublicKey(), key);
if (_log.shouldLog(Log.DEBUG)) _log.debug("tag consumed");
sentTags = null;
oldTags = _context.sessionKeyManager().getAvailableTags(dest.getPublicKey(), key);
long availTimeLeft = _context.sessionKeyManager().getAvailableTimeLeft(dest.getPublicKey(), key);
if ( (tagsSent == null) || (tagsSent.size() <= 0) ) {
if (oldTags < 10) {
sentTags = createNewTags(NUM_TAGS);
if (_log.shouldLog(Log.DEBUG))
_log.debug("** sendBestEffort only had " + oldTags + " with " + availTimeLeft + ", adding " + NUM_TAGS);
} else if (availTimeLeft < 2 * 60 * 1000) {
// if we have > 10 tags, but they expire in under 2 minutes, we want more
sentTags = createNewTags(NUM_TAGS);
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Tags expiring in " + availTimeLeft + ", adding " + NUM_TAGS + " new ones");
//_log.error("** sendBestEffort available time left " + availTimeLeft);
if ( (tagsSent == null) || (tagsSent.size() <= 0) ) {
if (oldTags < NUM_TAGS) {
sentTags = createNewTags(NUM_TAGS);
if (_log.shouldLog(Log.DEBUG))
_log.debug("** sendBestEffort only had " + oldTags + " with " + availTimeLeft + ", adding " + NUM_TAGS + ": " + sentTags);
} else if (availTimeLeft < 2 * 60 * 1000) {
// if we have > 50 tags, but they expire in under 2 minutes, we want more
sentTags = createNewTags(NUM_TAGS);
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Tags expiring in " + availTimeLeft + ", adding " + NUM_TAGS + " new ones: " + sentTags);
//_log.error("** sendBestEffort available time left " + availTimeLeft);
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("sendBestEffort old tags: " + oldTags + " available time left: " + availTimeLeft);
}
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("sendBestEffort old tags: " + oldTags + " available time left: " + availTimeLeft);
_log.debug("sendBestEffort is sending " + tagsSent.size() + " with " + availTimeLeft
+ "ms left, " + oldTags + " tags known and "
+ (tag == null ? "no tag" : " a valid tag"));
}
if (false) // rekey
newKey = _context.keyGenerator().generateSessionKey();
if ( (tagsSent != null) && (tagsSent.size() > 0) ) {
if (sentTags == null)
sentTags = new HashSet();
sentTags.addAll(tagsSent);
}
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("sendBestEffort is sending " + tagsSent.size() + " with " + availTimeLeft
+ "ms left, " + oldTags + " tags known and "
+ (tag == null ? "no tag" : " a valid tag"));
// not using end to end crypto, so don't ever bundle any tags
}
SessionKey newKey = null;
if (false) // rekey
newKey = _context.keyGenerator().generateSessionKey();
if ( (tagsSent != null) && (tagsSent.size() > 0) ) {
if (sentTags == null)
sentTags = new HashSet();
sentTags.addAll(tagsSent);
}
if (_log.shouldLog(Log.DEBUG)) _log.debug("before creating nonce");
long nonce = _context.random().nextInt(Integer.MAX_VALUE);
@ -174,10 +182,14 @@ class I2PSessionImpl2 extends I2PSessionImpl {
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Setting key = " + key);
if (keyUsed != null) {
if (newKey != null)
keyUsed.setData(newKey.getData());
else
keyUsed.setData(key.getData());
if (I2CPMessageProducer.END_TO_END_CRYPTO) {
if (newKey != null)
keyUsed.setData(newKey.getData());
else
keyUsed.setData(key.getData());
} else {
keyUsed.setData(SessionKey.INVALID_KEY.getData());
}
}
if (tagsSent != null) {
if (sentTags != null) {

View File

@ -35,7 +35,7 @@ class MessagePayloadMessageHandler extends HandlerImpl {
_log.debug("Handle message " + message);
try {
MessagePayloadMessage msg = (MessagePayloadMessage) message;
MessageId id = msg.getMessageId();
long id = msg.getMessageId();
Payload payload = decryptPayload(msg, session);
session.addNewMessage(msg);
@ -55,6 +55,11 @@ class MessagePayloadMessageHandler extends HandlerImpl {
*/
private Payload decryptPayload(MessagePayloadMessage msg, I2PSessionImpl session) throws DataFormatException {
Payload payload = msg.getPayload();
if (!I2CPMessageProducer.END_TO_END_CRYPTO) {
payload.setUnencryptedData(payload.getEncryptedData());
return payload;
}
byte[] data = _context.elGamalAESEngine().decrypt(payload.getEncryptedData(), session.getDecryptionKey());
if (data == null) {
if (_log.shouldLog(Log.WARN))

View File

@ -46,7 +46,7 @@ class MessageStatusMessageHandler extends HandlerImpl {
}
return;
case MessageStatusMessage.STATUS_SEND_ACCEPTED:
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
session.receiveStatus((int)msg.getMessageId(), msg.getNonce(), msg.getStatus());
// noop
return;
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS:
@ -54,14 +54,14 @@ class MessageStatusMessageHandler extends HandlerImpl {
if (_log.shouldLog(Log.INFO))
_log.info("Message delivery succeeded for message " + msg.getMessageId());
//if (!skipStatus)
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
session.receiveStatus((int)msg.getMessageId(), msg.getNonce(), msg.getStatus());
return;
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
if (_log.shouldLog(Log.INFO))
_log.info("Message delivery FAILED for message " + msg.getMessageId());
//if (!skipStatus)
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
session.receiveStatus((int)msg.getMessageId(), msg.getNonce(), msg.getStatus());
return;
default:
if (_log.shouldLog(Log.ERROR))

View File

@ -159,7 +159,6 @@ public class AESEngine {
System.arraycopy(payload, inIndex, rv, outIndex, rv.length - outIndex);
}
public static void main(String args[]) {
I2PAppContext ctx = new I2PAppContext();
SessionKey key = ctx.keyGenerator().generateSessionKey();

View File

@ -123,15 +123,17 @@ public class CryptixAESEngine extends AESEngine {
}
public final void encryptBlock(byte payload[], int inIndex, SessionKey sessionKey, byte out[], int outIndex) {
CryptixAESKeyCache.KeyCacheEntry keyData = _cache.acquireKey();
try {
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16, keyData);
CryptixRijndael_Algorithm.blockEncrypt(payload, out, inIndex, outIndex, key, 16);
} catch (InvalidKeyException ike) {
_log.error("Invalid key", ike);
} finally {
_cache.releaseKey(keyData);
if (sessionKey.getPreparedKey() == null) {
try {
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16);
sessionKey.setPreparedKey(key);
} catch (InvalidKeyException ike) {
_log.log(Log.CRIT, "Invalid key", ike);
throw new IllegalArgumentException("wtf, invalid key? " + ike.getMessage());
}
}
CryptixRijndael_Algorithm.blockEncrypt(payload, out, inIndex, outIndex, sessionKey.getPreparedKey(), 16);
}
/** decrypt the data with the session key provided
@ -146,15 +148,17 @@ public class CryptixAESEngine extends AESEngine {
throw new IllegalArgumentException("bad block args [payload.len=" + payload.length
+ " inIndex=" + inIndex + " rv.len=" + rv.length
+ " outIndex="+outIndex);
CryptixAESKeyCache.KeyCacheEntry keyData = _cache.acquireKey();
try {
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16, keyData);
CryptixRijndael_Algorithm.blockDecrypt(payload, rv, inIndex, outIndex, key, 16);
} catch (InvalidKeyException ike) {
_log.error("Invalid key", ike);
} finally {
_cache.releaseKey(keyData);
if (sessionKey.getPreparedKey() == null) {
try {
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16);
sessionKey.setPreparedKey(key);
} catch (InvalidKeyException ike) {
_log.log(Log.CRIT, "Invalid key", ike);
throw new IllegalArgumentException("wtf, invalid key? " + ike.getMessage());
}
}
CryptixRijndael_Algorithm.blockDecrypt(payload, rv, inIndex, outIndex, sessionKey.getPreparedKey(), 16);
}
public static void main(String args[]) {

View File

@ -18,6 +18,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper;
import net.i2p.data.SessionKey;
@ -157,8 +158,14 @@ public class DHSessionKeyBuilder {
// read: Y
BigInteger Y = readBigI(in);
if (Y == null) return null;
builder.setPeerPublicValue(Y);
return builder;
try {
builder.setPeerPublicValue(Y);
return builder;
} catch (InvalidPublicParameterException ippe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Key exchange failed (hostile peer?)", ippe);
return null;
}
}
static BigInteger readBigI(InputStream in) throws IOException {
@ -175,7 +182,7 @@ public class DHSessionKeyBuilder {
System.arraycopy(Y, 0, Y2, 1, 256);
Y = Y2;
}
return new NativeBigInteger(Y);
return new NativeBigInteger(1, Y);
}
/**
@ -253,7 +260,10 @@ public class DHSessionKeyBuilder {
*
*/
public byte[] getMyPublicValueBytes() {
BigInteger bi = getMyPublicValue();
return toByteArray(getMyPublicValue());
}
private static final byte[] toByteArray(BigInteger bi) {
byte data[] = bi.toByteArray();
byte rv[] = new byte[256];
if (data.length == 257) // high byte has the sign bit
@ -269,10 +279,11 @@ public class DHSessionKeyBuilder {
* Specify the value given by the peer for use in the session key negotiation
*
*/
public void setPeerPublicValue(BigInteger peerVal) {
public void setPeerPublicValue(BigInteger peerVal) throws InvalidPublicParameterException {
validatePublic(peerVal);
_peerValue = peerVal;
}
public void setPeerPublicValue(byte val[]) {
public void setPeerPublicValue(byte val[]) throws InvalidPublicParameterException {
if (val.length != 256)
throw new IllegalArgumentException("Peer public value must be exactly 256 bytes");
@ -284,12 +295,16 @@ public class DHSessionKeyBuilder {
System.arraycopy(val, 0, val2, 1, 256);
val = val2;
}
_peerValue = new NativeBigInteger(val);
setPeerPublicValue(new NativeBigInteger(1, val));
//_peerValue = new NativeBigInteger(val);
}
public BigInteger getPeerPublicValue() {
return _peerValue;
}
public byte[] getPeerPublicValueBytes() {
return toByteArray(getPeerPublicValue());
}
/**
* Retrieve the session key, calculating it if necessary (and if possible).
@ -355,8 +370,58 @@ public class DHSessionKeyBuilder {
}
return key;
}
/**
* rfc2631:
* The following algorithm MAY be used to validate a received public key y.
*
* 1. Verify that y lies within the interval [2,p-1]. If it does not,
* the key is invalid.
* 2. Compute y^q mod p. If the result == 1, the key is valid.
* Otherwise the key is invalid.
*/
private static final void validatePublic(BigInteger publicValue) throws InvalidPublicParameterException {
int cmp = publicValue.compareTo(NativeBigInteger.ONE);
if (cmp <= 0)
throw new InvalidPublicParameterException("Public value is below two: " + publicValue.toString());
cmp = publicValue.compareTo(CryptoConstants.elgp);
if (cmp >= 0)
throw new InvalidPublicParameterException("Public value is above p-1: " + publicValue.toString());
// todo:
// whatever validation needs to be done to mirror the rfc's part 2 (we don't have a q, so can't do
// if (NativeBigInteger.ONE.compareTo(publicValue.modPow(q, CryptoConstants.elgp)) != 0)
// throw new InvalidPublicParameterException("Invalid public value with y^q mod p != 1");
//
}
/*
private static void testValidation() {
NativeBigInteger bi = new NativeBigInteger("-3416069082912684797963255430346582466254460710249795973742848334283491150671563023437888953432878859472362439146158925287289114133666004165938814597775594104058593692562989626922979416277152479694258099203456493995467386903611666213773085025718340335205240293383622352894862685806192183268523899615405287022135356656720938278415659792084974076416864813957028335830794117802560169423133816961503981757298122040391506600117301607823659479051969827845787626261515313227076880722069706394405554113103165334903531980102626092646197079218895216346725765704256096661045699444128316078549709132753443706200863682650825635513");
try {
validatePublic(bi);
System.err.println("valid?!");
} catch (InvalidPublicParameterException ippe) {
System.err.println("Ok, invalid. cool");
}
byte val[] = bi.toByteArray();
System.out.println("Len: " + val.length + " first is ok? " + ( (val[0] & 0x80) == 1)
+ "\n" + DataHelper.toString(val, 64));
NativeBigInteger bi2 = new NativeBigInteger(1, val);
try {
validatePublic(bi2);
System.out.println("valid");
} catch (InvalidPublicParameterException ippe) {
System.out.println("invalid");
}
}
*/
public static void main(String args[]) {
//if (true) { testValidation(); return; }
RandomSource.getInstance().nextBoolean(); // warm it up
try {
Thread.sleep(20 * 1000);
@ -365,36 +430,40 @@ public class DHSessionKeyBuilder {
I2PAppContext ctx = new I2PAppContext();
_log.debug("\n\n\n\nBegin test\n");
long negTime = 0;
for (int i = 0; i < 5; i++) {
long startNeg = Clock.getInstance().now();
DHSessionKeyBuilder builder1 = new DHSessionKeyBuilder();
DHSessionKeyBuilder builder2 = new DHSessionKeyBuilder();
BigInteger pub1 = builder1.getMyPublicValue();
builder2.setPeerPublicValue(pub1);
BigInteger pub2 = builder2.getMyPublicValue();
builder1.setPeerPublicValue(pub2);
SessionKey key1 = builder1.getSessionKey();
SessionKey key2 = builder2.getSessionKey();
long endNeg = Clock.getInstance().now();
negTime += endNeg - startNeg;
try {
for (int i = 0; i < 5; i++) {
long startNeg = Clock.getInstance().now();
DHSessionKeyBuilder builder1 = new DHSessionKeyBuilder();
DHSessionKeyBuilder builder2 = new DHSessionKeyBuilder();
BigInteger pub1 = builder1.getMyPublicValue();
builder2.setPeerPublicValue(pub1);
BigInteger pub2 = builder2.getMyPublicValue();
builder1.setPeerPublicValue(pub2);
SessionKey key1 = builder1.getSessionKey();
SessionKey key2 = builder2.getSessionKey();
long endNeg = Clock.getInstance().now();
negTime += endNeg - startNeg;
if (!key1.equals(key2))
_log.error("**ERROR: Keys do not match");
else
_log.debug("**Success: Keys match");
if (!key1.equals(key2))
_log.error("**ERROR: Keys do not match");
else
_log.debug("**Success: Keys match");
byte iv[] = new byte[16];
RandomSource.getInstance().nextBytes(iv);
String origVal = "1234567890123456"; // 16 bytes max using AESEngine
byte enc[] = new byte[16];
byte dec[] = new byte[16];
ctx.aes().encrypt(origVal.getBytes(), 0, enc, 0, key1, iv, 16);
ctx.aes().decrypt(enc, 0, dec, 0, key2, iv, 16);
String tranVal = new String(dec);
if (origVal.equals(tranVal))
_log.debug("**Success: D(E(val)) == val");
else
_log.error("**ERROR: D(E(val)) != val [val=(" + tranVal + "), origVal=(" + origVal + ")");
byte iv[] = new byte[16];
RandomSource.getInstance().nextBytes(iv);
String origVal = "1234567890123456"; // 16 bytes max using AESEngine
byte enc[] = new byte[16];
byte dec[] = new byte[16];
ctx.aes().encrypt(origVal.getBytes(), 0, enc, 0, key1, iv, 16);
ctx.aes().decrypt(enc, 0, dec, 0, key2, iv, 16);
String tranVal = new String(dec);
if (origVal.equals(tranVal))
_log.debug("**Success: D(E(val)) == val");
else
_log.error("**ERROR: D(E(val)) != val [val=(" + tranVal + "), origVal=(" + origVal + ")");
}
} catch (InvalidPublicParameterException ippe) {
_log.error("Invalid dh", ippe);
}
_log.debug("Negotiation time for 5 runs: " + negTime + " @ " + negTime / 5l + "ms each");
try {
@ -451,4 +520,13 @@ public class DHSessionKeyBuilder {
return builder;
}
}
public static class InvalidPublicParameterException extends I2PException {
public InvalidPublicParameterException() {
super();
}
public InvalidPublicParameterException(String msg) {
super(msg);
}
}
}

View File

@ -0,0 +1,52 @@
package net.i2p.crypto;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.SessionKey;
/**
* Calculate the HMAC-SHA256 of a key+message. All the good stuff occurs
* in {@link org.bouncycastle.crypto.macs.HMac} and
* {@link org.bouncycastle.crypto.digests.SHA256Digest}.
*
*/
public class DummyHMACSHA256Generator extends HMACSHA256Generator {
private I2PAppContext _context;
public DummyHMACSHA256Generator(I2PAppContext context) {
super(context);
_context = context;
}
public static HMACSHA256Generator getInstance() {
return I2PAppContext.getGlobalContext().hmac();
}
/**
* Calculate the HMAC of the data with the given key
*/
public Hash calculate(SessionKey key, byte data[]) {
if ((key == null) || (key.getData() == null) || (data == null))
throw new NullPointerException("Null arguments for HMAC");
return calculate(key, data, 0, data.length);
}
/**
* Calculate the HMAC of the data with the given key
*/
public Hash calculate(SessionKey key, byte data[], int offset, int length) {
if ((key == null) || (key.getData() == null) || (data == null))
throw new NullPointerException("Null arguments for HMAC");
byte rv[] = new byte[Hash.HASH_LENGTH];
System.arraycopy(key.getData(), 0, rv, 0, Hash.HASH_LENGTH);
if (Hash.HASH_LENGTH >= length)
DataHelper.xor(data, offset, rv, 0, rv, 0, length);
else
DataHelper.xor(data, offset, rv, 0, rv, 0, Hash.HASH_LENGTH);
return new Hash(rv);
}
}

View File

@ -0,0 +1,98 @@
package net.i2p.crypto;
import java.util.Random;
import net.i2p.I2PAppContext;
import net.i2p.util.PooledRandomSource;
import net.i2p.util.RandomSource;
import net.i2p.util.Log;
/**
*
*/
public class DummyPooledRandomSource extends PooledRandomSource {
public DummyPooledRandomSource(I2PAppContext context) {
super(context);
}
protected void initializePool(I2PAppContext context) {
_pool = new RandomSource[POOL_SIZE];
for (int i = 0; i < POOL_SIZE; i++) {
_pool[i] = new DummyRandomSource(context);
_pool[i].nextBoolean();
}
_nextPool = 0;
}
private class DummyRandomSource extends RandomSource {
private Random _prng;
public DummyRandomSource(I2PAppContext context) {
super(context);
// when we replace to have hooks for fortuna (etc), replace with
// a factory (or just a factory method)
_prng = new Random();
}
/**
* According to the java docs (http://java.sun.com/j2se/1.4.1/docs/api/java/util/Random.html#nextInt(int))
* nextInt(n) should return a number between 0 and n (including 0 and excluding n). However, their pseudocode,
* as well as sun's, kaffe's, and classpath's implementation INCLUDES NEGATIVE VALUES.
* WTF. Ok, so we're going to have it return between 0 and n (including 0, excluding n), since
* thats what it has been used for.
*
*/
public int nextInt(int n) {
if (n == 0) return 0;
int val = _prng.nextInt(n);
if (val < 0) val = 0 - val;
if (val >= n) val = val % n;
return val;
}
/**
* Like the modified nextInt, nextLong(n) returns a random number from 0 through n,
* including 0, excluding n.
*/
public long nextLong(long n) {
long v = _prng.nextLong();
if (v < 0) v = 0 - v;
if (v >= n) v = v % n;
return v;
}
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
public boolean nextBoolean() { return _prng.nextBoolean(); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
public void nextBytes(byte buf[]) { _prng.nextBytes(buf); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
public double nextDouble() { return _prng.nextDouble(); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
public float nextFloat() { return _prng.nextFloat(); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
public double nextGaussian() { return _prng.nextGaussian(); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
public int nextInt() { return _prng.nextInt(); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
public long nextLong() { return _prng.nextLong(); }
}
}

View File

@ -16,6 +16,7 @@ import java.util.List;
import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.data.Base64;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
@ -52,7 +53,7 @@ public class ElGamalAESEngine {
_context.statManager().createFrequencyStat("crypto.elGamalAES.decryptExistingSession",
"how frequently we decrypt with an existing ElGamal/AES+SessionTag session?",
"Encryption", new long[] { 60 * 1000l, 60 * 60 * 1000l, 24 * 60 * 60 * 1000l});
_context.statManager().createFrequencyStat("crypto.elGamalAES.decryptFail",
_context.statManager().createFrequencyStat("crypto.elGamalAES.decryptFailed",
"how frequently we fail to decrypt with ElGamal/AES+SessionTag?", "Encryption",
new long[] { 60 * 60 * 1000l, 24 * 60 * 60 * 1000l});
}
@ -81,21 +82,38 @@ public class ElGamalAESEngine {
SessionKey usedKey = new SessionKey();
Set foundTags = new HashSet();
byte decrypted[] = null;
boolean wasExisting = false;
if (key != null) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Key is known for tag " + st);
//if (_log.shouldLog(Log.DEBUG)) _log.debug("Key is known for tag " + st);
usedKey.setData(key.getData());
long id = _context.random().nextLong();
if (_log.shouldLog(Log.DEBUG))
_log.debug(id + ": Decrypting existing session encrypted with tag: " + st.toString() + ": key: " + key.toBase64() + ": " + data.length + " bytes: " + Base64.encode(data, 0, 64));
decrypted = decryptExistingSession(data, key, targetPrivateKey, foundTags, usedKey, foundKey);
if (decrypted != null)
if (decrypted != null) {
_context.statManager().updateFrequency("crypto.elGamalAES.decryptExistingSession");
else
if ( (foundTags.size() > 0) && (_log.shouldLog(Log.WARN)) )
_log.warn(id + ": ElG/AES decrypt success with " + st + ": found tags: " + foundTags);
wasExisting = true;
} else {
_context.statManager().updateFrequency("crypto.elGamalAES.decryptFailed");
if (_log.shouldLog(Log.WARN)) {
_log.warn(id + ": ElG decrypt fail: known tag [" + st + "], failed decrypt");
}
}
} else {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Key is NOT known for tag " + st);
decrypted = decryptNewSession(data, targetPrivateKey, foundTags, usedKey, foundKey);
if (decrypted != null)
if (decrypted != null) {
_context.statManager().updateFrequency("crypto.elGamalAES.decryptNewSession");
else
if ( (foundTags.size() > 0) && (_log.shouldLog(Log.WARN)) )
_log.warn("ElG decrypt success: found tags: " + foundTags);
} else {
_context.statManager().updateFrequency("crypto.elGamalAES.decryptFailed");
if (_log.shouldLog(Log.WARN))
_log.warn("ElG decrypt fail: unknown tag: " + st);
}
}
if ((key == null) && (decrypted == null)) {
@ -104,10 +122,12 @@ public class ElGamalAESEngine {
if (foundTags.size() > 0) {
if (foundKey.getData() != null) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Found key: " + foundKey);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Found key: " + foundKey.toBase64() + " tags: " + foundTags + " wasExisting? " + wasExisting);
_context.sessionKeyManager().tagsReceived(foundKey, foundTags);
} else {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Used key: " + usedKey);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Used key: " + usedKey.toBase64() + " tags: " + foundTags + " wasExisting? " + wasExisting);
_context.sessionKeyManager().tagsReceived(usedKey, foundTags);
}
}
@ -131,10 +151,10 @@ public class ElGamalAESEngine {
byte[] decryptNewSession(byte data[], PrivateKey targetPrivateKey, Set foundTags, SessionKey usedKey,
SessionKey foundKey) throws DataFormatException {
if (data == null) {
if (_log.shouldLog(Log.WARN)) _log.warn("Data is null, unable to decrypt new session");
//if (_log.shouldLog(Log.WARN)) _log.warn("Data is null, unable to decrypt new session");
return null;
} else if (data.length < 514) {
if (_log.shouldLog(Log.WARN)) _log.warn("Data length is too small (" + data.length + ")");
//if (_log.shouldLog(Log.WARN)) _log.warn("Data length is too small (" + data.length + ")");
return null;
}
byte elgEncr[] = new byte[514];
@ -145,8 +165,8 @@ public class ElGamalAESEngine {
}
byte elgDecr[] = _context.elGamalEngine().decrypt(elgEncr, targetPrivateKey);
if (elgDecr == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("decrypt returned null", new Exception("decrypt failed"));
//if (_log.shouldLog(Log.WARN))
// _log.warn("decrypt returned null", new Exception("decrypt failed"));
return null;
}
@ -172,9 +192,9 @@ public class ElGamalAESEngine {
byte aesDecr[] = decryptAESBlock(data, 514, data.length-514, usedKey, iv, null, foundTags, foundKey);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Decrypt with a NEW session successfull: # tags read = " + foundTags.size(),
new Exception("Decrypted by"));
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Decrypt with a NEW session successfull: # tags read = " + foundTags.size(),
// new Exception("Decrypted by"));
return aesDecr;
}
@ -211,14 +231,23 @@ public class ElGamalAESEngine {
byte decrypted[] = decryptAESBlock(data, 32, data.length-32, key, iv, preIV, foundTags, foundKey);
if (decrypted == null) {
// it begins with a valid session tag, but thats just a coincidence.
if (_log.shouldLog(Log.DEBUG))
_log.debug("Decrypt with a non session tag, but tags read: " + foundTags.size());
return decryptNewSession(data, targetPrivateKey, foundTags, usedKey, foundKey);
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Decrypt with a non session tag, but tags read: " + foundTags.size());
if (_log.shouldLog(Log.WARN))
_log.warn("Decrypting looks negative... existing key fails with existing tag, lets try as a new one");
byte rv[] = decryptNewSession(data, targetPrivateKey, foundTags, usedKey, foundKey);
if (_log.shouldLog(Log.WARN)) {
if (rv == null)
_log.warn("Decrypting failed with a known existing tag as either an existing message or a new session");
else
_log.warn("Decrypting suceeded as a new session, even though it used an existing tag!");
}
return rv;
}
// existing session decrypted successfully!
if (_log.shouldLog(Log.DEBUG))
_log.debug("Decrypt with an EXISTING session tag successfull, # tags read: " + foundTags.size(),
new Exception("Decrypted by"));
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Decrypt with an EXISTING session tag successfull, # tags read: " + foundTags.size(),
// new Exception("Decrypted by"));
return decrypted;
}
@ -261,7 +290,7 @@ public class ElGamalAESEngine {
long numTags = DataHelper.fromLong(decrypted, cur, 2);
cur += 2;
//_log.debug("# tags: " + numTags);
if ((numTags < 0) || (numTags > 65535)) throw new Exception("Invalid number of session tags");
if ((numTags < 0) || (numTags > 200)) throw new Exception("Invalid number of session tags");
if (numTags * SessionTag.BYTE_LENGTH > decrypted.length - 2) {
throw new Exception("# tags: " + numTags + " is too many for " + (decrypted.length - 2));
}
@ -333,7 +362,10 @@ public class ElGamalAESEngine {
if (_log.shouldLog(Log.INFO))
_log.info("Current tag is NOT null, encrypting as existing session", new Exception("encrypt existing"));
_context.statManager().updateFrequency("crypto.elGamalAES.encryptExistingSession");
return encryptExistingSession(data, target, key, tagsForDelivery, currentTag, newKey, paddedSize);
byte rv[] = encryptExistingSession(data, target, key, tagsForDelivery, currentTag, newKey, paddedSize);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Existing session encrypted with tag: " + currentTag.toString() + ": " + rv.length + " bytes and key: " + key.toBase64() + ": " + Base64.encode(rv, 0, 64));
return rv;
}
/**
@ -396,7 +428,7 @@ public class ElGamalAESEngine {
if (elgEncr.length < 514) {
byte elg[] = new byte[514];
int diff = elg.length - elgEncr.length;
if (_log.shouldLog(Log.DEBUG)) _log.debug("Difference in size: " + diff);
//if (_log.shouldLog(Log.DEBUG)) _log.debug("Difference in size: " + diff);
System.arraycopy(elgEncr, 0, elg, diff, elgEncr.length);
elgEncr = elg;
}
@ -415,8 +447,8 @@ public class ElGamalAESEngine {
System.arraycopy(aesEncr, 0, rv, elgEncr.length, aesEncr.length);
//_log.debug("Return length: " + rv.length);
long finish = _context.clock().now();
if (_log.shouldLog(Log.DEBUG))
_log.debug("after the elgEngine.encrypt took a total of " + (finish - after) + "ms");
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("after the elgEngine.encrypt took a total of " + (finish - after) + "ms");
return rv;
}
@ -493,7 +525,7 @@ public class ElGamalAESEngine {
System.arraycopy(tag.getData(), 0, aesData, cur, SessionTag.BYTE_LENGTH);
cur += SessionTag.BYTE_LENGTH;
}
//_log.debug("# tags created, registered, and written: " + tags.size());
//_log.debug("# tags created, registered, and written: " + tagsForDelivery.size());
DataHelper.toLong(aesData, cur, 4, data.length);
cur += 4;
//_log.debug("data length: " + data.length);
@ -519,8 +551,8 @@ public class ElGamalAESEngine {
System.arraycopy(padding, 0, aesData, cur, padding.length);
cur += padding.length;
//Hash h = _context.sha().calculateHash(aesUnencr);
//_log.debug("Hash of entire aes block before encryption: (len=" + aesUnencr.length + ")\n" + DataHelper.toString(h.getData(), 32));
//Hash h = _context.sha().calculateHash(data);
//_log.debug("Hash of entire aes block before encryption: (len=" + data.length + ")\n" + DataHelper.toString(h.getData(), 32));
_context.aes().encrypt(aesData, prefixBytes, aesData, prefixBytes, key, iv, aesData.length - prefixBytes);
//_log.debug("Encrypted length: " + aesEncr.length);
//return aesEncr;

View File

@ -9,20 +9,36 @@ import net.i2p.data.Hash;
import net.i2p.data.SessionKey;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.macs.HMac;
/**
* Calculate the HMAC-SHA256 of a key+message. All the good stuff occurs
* in {@link org.bouncycastle.crypto.macs.HMac} and
* {@link org.bouncycastle.crypto.digests.SHA256Digest}.
* {@link org.bouncycastle.crypto.digests.SHA256Digest}. Alternately, if
* the context property "i2p.HMACMD5" is set to true, then this whole HMAC
* generator will be transformed into HMACMD5, maintaining the same size and
* using {@link org.bouncycastle.crypto.digests.MD5Digest}.
*
*/
public class HMACSHA256Generator {
private I2PAppContext _context;
/** set of available HMAC instances for calculate */
private List _available;
/** set of available byte[] buffers for verify */
private List _availableTmp;
private boolean _useMD5;
public static final boolean DEFAULT_USE_MD5 = true;
public HMACSHA256Generator(I2PAppContext context) {
_context = context;
_available = new ArrayList(32);
_availableTmp = new ArrayList(32);
if ("true".equals(context.getProperty("i2p.HMACMD5", Boolean.toString(DEFAULT_USE_MD5).toLowerCase())))
_useMD5 = true;
else
_useMD5 = false;
}
public static HMACSHA256Generator getInstance() {
@ -35,12 +51,15 @@ public class HMACSHA256Generator {
public Hash calculate(SessionKey key, byte data[]) {
if ((key == null) || (key.getData() == null) || (data == null))
throw new NullPointerException("Null arguments for HMAC");
return calculate(key, data, 0, data.length);
byte rv[] = new byte[Hash.HASH_LENGTH];
calculate(key, data, 0, data.length, rv, 0);
return new Hash(rv);
}
/**
* Calculate the HMAC of the data with the given key
*/
/*
public Hash calculate(SessionKey key, byte data[], int offset, int length) {
if ((key == null) || (key.getData() == null) || (data == null))
throw new NullPointerException("Null arguments for HMAC");
@ -53,12 +72,60 @@ public class HMACSHA256Generator {
release(mac);
return new Hash(rv);
}
*/
/**
* Calculate the HMAC of the data with the given key
*/
public void calculate(SessionKey key, byte data[], int offset, int length, byte target[], int targetOffset) {
if ((key == null) || (key.getData() == null) || (data == null))
throw new NullPointerException("Null arguments for HMAC");
HMac mac = acquire();
mac.init(key.getData());
mac.update(data, offset, length);
//byte rv[] = new byte[Hash.HASH_LENGTH];
mac.doFinal(target, targetOffset);
release(mac);
//return new Hash(rv);
}
/**
* Verify the MAC inline, reducing some unnecessary memory churn.
*
* @param key session key to verify the MAC with
* @param curData MAC to verify
* @param curOffset index into curData to MAC
* @param curLength how much data in curData do we want to run the HMAC over
* @param origMAC what do we expect the MAC of curData to equal
* @param origMACOffset index into origMAC
* @param origMACLength how much of the MAC do we want to verify
*/
public boolean verify(SessionKey key, byte curData[], int curOffset, int curLength, byte origMAC[], int origMACOffset, int origMACLength) {
if ((key == null) || (key.getData() == null) || (curData == null))
throw new NullPointerException("Null arguments for HMAC");
HMac mac = acquire();
mac.init(key.getData());
mac.update(curData, curOffset, curLength);
byte rv[] = acquireTmp();
//byte rv[] = new byte[Hash.HASH_LENGTH];
mac.doFinal(rv, 0);
release(mac);
boolean eq = DataHelper.eq(rv, 0, origMAC, origMACOffset, origMACLength);
releaseTmp(rv);
return eq;
}
private HMac acquire() {
synchronized (_available) {
if (_available.size() > 0)
return (HMac)_available.remove(0);
}
if (_useMD5)
return new HMac(new MD5Digest());
else
return new HMac(new SHA256Digest());
}
private void release(HMac mac) {
@ -67,4 +134,24 @@ public class HMACSHA256Generator {
_available.add(mac);
}
}
// temp buffers for verify(..)
private byte[] acquireTmp() {
byte rv[] = null;
synchronized (_availableTmp) {
if (_availableTmp.size() > 0)
rv = (byte[])_availableTmp.remove(0);
}
if (rv != null)
Arrays.fill(rv, (byte)0x0);
else
rv = new byte[Hash.HASH_LENGTH];
return rv;
}
private void releaseTmp(byte tmp[]) {
synchronized (_availableTmp) {
if (_availableTmp.size() < 64)
_availableTmp.add((Object)tmp);
}
}
}

View File

@ -58,6 +58,8 @@ public class PersistentSessionKeyManager extends TransientSessionKeyManager {
*
*/
public void saveState(OutputStream out) throws IOException, DataFormatException {
if (true) return;
Set tagSets = getInboundTagSets();
Set sessions = getOutboundSessions();
if (_log.shouldLog(Log.INFO))

View File

@ -1,6 +1,8 @@
package net.i2p.crypto;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.data.Hash;
@ -12,7 +14,10 @@ import org.bouncycastle.crypto.digests.SHA256Digest;
*
*/
public final class SHA256Generator {
public SHA256Generator(I2PAppContext context) {}
private List _digests;
public SHA256Generator(I2PAppContext context) {
_digests = new ArrayList(32);
}
public static final SHA256Generator getInstance() {
return I2PAppContext.getGlobalContext().sha();
@ -27,11 +32,35 @@ public final class SHA256Generator {
}
public final Hash calculateHash(byte[] source, int start, int len) {
byte rv[] = new byte[Hash.HASH_LENGTH];
SHA256Digest digest = new SHA256Digest();
digest.update(source, start, len);
digest.doFinal(rv, 0);
calculateHash(source, start, len, rv, 0);
return new Hash(rv);
}
public final void calculateHash(byte[] source, int start, int len, byte out[], int outOffset) {
SHA256Digest digest = acquire();
digest.update(source, start, len);
digest.doFinal(out, outOffset);
release(digest);
}
private SHA256Digest acquire() {
SHA256Digest rv = null;
synchronized (_digests) {
if (_digests.size() > 0)
rv = (SHA256Digest)_digests.remove(0);
}
if (rv != null)
rv.reset();
else
rv = new SHA256Digest();
return rv;
}
private void release(SHA256Digest digest) {
synchronized (_digests) {
if (_digests.size() < 32) {
_digests.add(digest);
}
}
}
public static void main(String args[]) {
I2PAppContext ctx = I2PAppContext.getGlobalContext();

View File

@ -174,7 +174,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
if (sess.getCurrentKey().equals(key)) {
SessionTag nxt = sess.consumeNext();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Tag consumed: " + nxt);
_log.debug("Tag consumed: " + nxt + " with key: " + key.toBase64());
return nxt;
}
if (_log.shouldLog(Log.DEBUG))
@ -220,6 +220,11 @@ class TransientSessionKeyManager extends SessionKeyManager {
*
*/
public void tagsDelivered(PublicKey target, SessionKey key, Set sessionTags) {
if (_log.shouldLog(Log.DEBUG)) {
//_log.debug("Tags delivered to set " + set + " on session " + sess);
if (sessionTags.size() > 0)
_log.debug("Tags delivered: " + sessionTags.size() + " for key: " + key.toBase64() + ": " + sessionTags);
}
OutboundSession sess = getSession(target);
if (sess == null) {
createSession(target, key);
@ -228,11 +233,6 @@ class TransientSessionKeyManager extends SessionKeyManager {
sess.setCurrentKey(key);
TagSet set = new TagSet(sessionTags, key, _context.clock().now());
sess.addTags(set);
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Tags delivered to set " + set + " on session " + sess);
if (sessionTags.size() > 0)
_log.debug("Tags delivered: " + sessionTags.size() + " total = " + sess.availableTags());
}
}
/**
@ -252,13 +252,44 @@ class TransientSessionKeyManager extends SessionKeyManager {
public void tagsReceived(SessionKey key, Set sessionTags) {
int overage = 0;
TagSet tagSet = new TagSet(sessionTags, key, _context.clock().now());
TagSet old = null;
SessionTag dupTag = null;
for (Iterator iter = sessionTags.iterator(); iter.hasNext();) {
SessionTag tag = (SessionTag) iter.next();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receiving tag " + tag + " for key " + key);
_log.debug("Receiving tag " + tag + " for key " + key.toBase64() + " / " + key.toString() + ": tagSet: " + tagSet);
synchronized (_inboundTagSets) {
_inboundTagSets.put(tag, tagSet);
old = (TagSet)_inboundTagSets.put(tag, tagSet);
overage = _inboundTagSets.size() - MAX_INBOUND_SESSION_TAGS;
if (old != null) {
if (!old.getAssociatedKey().equals(tagSet.getAssociatedKey())) {
_inboundTagSets.remove(tag);
dupTag = tag;
break;
} else {
old = null; // ignore the dup
}
}
}
}
if (old != null) {
// drop both old and tagSet tags
synchronized (_inboundTagSets) {
for (Iterator iter = old.getTags().iterator(); iter.hasNext(); ) {
SessionTag tag = (SessionTag)iter.next();
_inboundTagSets.remove(tag);
}
for (Iterator iter = sessionTags.iterator(); iter.hasNext(); ) {
SessionTag tag = (SessionTag)iter.next();
_inboundTagSets.remove(tag);
}
}
if (_log.shouldLog(Log.WARN)) {
_log.warn("Multiple tags matching! tagSet: " + tagSet + " and old tagSet: " + old + " tag: " + dupTag + "/" + dupTag.toBase64());
_log.warn("Earlier tag set creation: " + old + ": key=" + old.getAssociatedKey().toBase64(), old.getCreatedBy());
_log.warn("Current tag set creation: " + tagSet + ": key=" + tagSet.getAssociatedKey().toBase64(), tagSet.getCreatedBy());
}
}
@ -267,6 +298,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
if ( (sessionTags.size() <= 0) && (_log.shouldLog(Log.DEBUG)) )
_log.debug("Received 0 tags for key " + key);
if (false) aggressiveExpire();
}
/**
@ -329,6 +361,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
*
*/
public SessionKey consumeTag(SessionTag tag) {
if (false) aggressiveExpire();
synchronized (_inboundTagSets) {
TagSet tagSet = (TagSet) _inboundTagSets.remove(tag);
if (tagSet == null) {
@ -340,7 +373,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
SessionKey key = tagSet.getAssociatedKey();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Consuming tag " + tag + " for sessionKey " + key);
_log.debug("Consuming tag " + tag.toString() + " for sessionKey " + key.toBase64() + " / " + key.toString() + " on tagSet: " + tagSet);
return key;
}
}
@ -378,19 +411,36 @@ class TransientSessionKeyManager extends SessionKeyManager {
int removed = 0;
int remaining = 0;
long now = _context.clock().now();
StringBuffer buf = null;
StringBuffer bufSummary = null;
if (_log.shouldLog(Log.DEBUG)) {
buf = new StringBuffer(128);
buf.append("Expiring inbound: ");
bufSummary = new StringBuffer(1024);
}
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) {
long age = now - ts.getDate();
if (age > SESSION_LIFETIME_MAX_MS) {
//if (ts.getDate() < now - SESSION_LIFETIME_MAX_MS) {
iter.remove();
removed++;
if (buf != null)
buf.append(tag.toString()).append(" @ age ").append(DataHelper.formatDuration(age));
} else if (false && (bufSummary != null) ) {
bufSummary.append("\nTagSet: " + ts.toString() + ", key: " + ts.getAssociatedKey().toBase64()+"/" + ts.getAssociatedKey().toString()
+ ": tag: " + tag.toString());
}
}
remaining = _inboundTagSets.size();
}
_context.statManager().addRateData("crypto.sessionTagsRemaining", remaining, 0);
if ( (buf != null) && (removed > 0) )
_log.debug(buf.toString());
if (bufSummary != null)
_log.debug("Cleaning up with remaining: " + bufSummary.toString());
//_log.warn("Expiring tags: [" + tagsToDrop + "]");
@ -606,6 +656,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
private Set _sessionTags;
private SessionKey _key;
private long _date;
private Exception _createdBy;
public TagSet(Set tags, SessionKey key, long date) {
if (key == null) throw new IllegalArgumentException("Missing key");
@ -613,6 +664,12 @@ class TransientSessionKeyManager extends SessionKeyManager {
_sessionTags = tags;
_key = key;
_date = date;
if (true) {
long now = I2PAppContext.getGlobalContext().clock().now();
_createdBy = new Exception("Created by: key=" + _key.toBase64() + " on "
+ new Date(now) + "/" + now
+ " via " + Thread.currentThread().getName());
}
}
/** when the tag set was created */
@ -652,6 +709,8 @@ class TransientSessionKeyManager extends SessionKeyManager {
_sessionTags.remove(first);
return first;
}
public Exception getCreatedBy() { return _createdBy; }
public int hashCode() {
long rv = 0;
@ -669,4 +728,4 @@ class TransientSessionKeyManager extends SessionKeyManager {
&& ts.getDate() == getDate();
}
}
}
}

View File

@ -55,12 +55,11 @@ Hash: SHA1
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (GNU/Linux)
iD8DBQFCQXoJGnFL2th344YRAtsIAKCUy/sOIsxahUnT2hKLXFL9lXsAmACfUHa5
CPah6TDXYJCWmR0n3oPtrvo=
=mD0t
iD8DBQFCZ38IWYfZ3rPnHH0RAgOHAJ4wNgmfO2AkL8IXiGnPtWrTlXcVogCfQ79z
jP69nPbh4KLGhF+SD0+0bW4=
=npPe
-----END PGP SIGNATURE-----
*/
private static final String VALID_VERSION_CHARS = "0123456789.";
private static final int VERSION_BYTES = 16;
private static final int HEADER_BYTES = Signature.SIGNATURE_BYTES + VERSION_BYTES;
@ -288,15 +287,20 @@ CPah6TDXYJCWmR0n3oPtrvo=
try {
fileInputStream = new FileInputStream(signedFile);
byte[] data = new byte[VERSION_BYTES];
int bytesRead = DataHelper.read(fileInputStream, data, Signature.SIGNATURE_BYTES, VERSION_BYTES);
if (bytesRead != VERSION_BYTES)
long skipped = fileInputStream.skip(Signature.SIGNATURE_BYTES);
if (skipped != Signature.SIGNATURE_BYTES)
return "";
byte[] data = new byte[VERSION_BYTES];
int bytesRead = DataHelper.read(fileInputStream, data);
if (bytesRead != VERSION_BYTES) {
return "";
}
for (int i = 0; i < VERSION_BYTES; i++)
if (data[i] == 0x00)
if (data[i] == 0x00) {
return new String(data, 0, i, "UTF-8");
}
return new String(data, "UTF-8");
} catch (UnsupportedEncodingException uee) {

View File

@ -84,6 +84,17 @@ public class Base64 {
(byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1',
(byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
(byte) '8', (byte) '9', (byte) '+', (byte) '/'};
private final static byte[] ALPHABET_ALT = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
(byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
(byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R',
(byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X',
(byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd',
(byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
(byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p',
(byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v',
(byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1',
(byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
(byte) '8', (byte) '9', (byte) '-', (byte) '~'};
/**
* Translates a Base64 value to either its 6-bit reconstruction value
@ -131,6 +142,7 @@ public class Base64 {
}
public static void main(String[] args) {
test();
if (args.length == 0) {
help();
return;
@ -312,6 +324,49 @@ public class Base64 {
} // end switch
} // end encode3to4
private static void encode3to4(byte[] source, int srcOffset, int numSigBytes, StringBuffer buf, byte alpha[]) {
// 1 2 3
// 01234567890123456789012345678901 Bit position
// --------000000001111111122222222 Array position from threeBytes
// --------| || || || | Six bit groups to index ALPHABET
// >>18 >>12 >> 6 >> 0 Right shift necessary
// 0x3f 0x3f 0x3f Additional AND
// Create buffer with zero-padding if there are only one or two
// significant bytes passed in the array.
// We have to shift left 24 in order to flush out the 1's that appear
// when Java treats a value as negative that is cast from a byte to an int.
int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
| (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
| (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
switch (numSigBytes) {
case 3:
buf.append((char)alpha[(inBuff >>> 18)]);
buf.append((char)alpha[(inBuff >>> 12) & 0x3f]);
buf.append((char)alpha[(inBuff >>> 6) & 0x3f]);
buf.append((char)alpha[(inBuff) & 0x3f]);
return;
case 2:
buf.append((char)alpha[(inBuff >>> 18)]);
buf.append((char)alpha[(inBuff >>> 12) & 0x3f]);
buf.append((char)alpha[(inBuff >>> 6) & 0x3f]);
buf.append((char)EQUALS_SIGN);
return;
case 1:
buf.append((char)alpha[(inBuff >>> 18)]);
buf.append((char)alpha[(inBuff >>> 12) & 0x3f]);
buf.append((char)EQUALS_SIGN);
buf.append((char)EQUALS_SIGN);
return;
default:
return;
} // end switch
} // end encode3to4
/**
* Encodes a byte array into Base64 notation.
* Equivalen to calling
@ -331,14 +386,12 @@ public class Base64 {
private static String safeEncode(byte[] source, int off, int len, boolean useStandardAlphabet) {
if (len + off > source.length)
throw new ArrayIndexOutOfBoundsException("Trying to encode too much! source.len=" + source.length + " off=" + off + " len=" + len);
String encoded = encodeBytes(source, off, len, false);
if (useStandardAlphabet) {
// noop
} else {
encoded = encoded.replace('/', '~');
encoded = encoded.replace('+', '-');
}
return encoded;
StringBuffer buf = new StringBuffer(len * 4 / 3);
if (useStandardAlphabet)
encodeBytes(source, off, len, false, buf, ALPHABET);
else
encodeBytes(source, off, len, false, buf, ALPHABET_ALT);
return buf.toString();
}
/**
@ -381,6 +434,12 @@ public class Base64 {
return encodeBytes(source, off, len, true);
} // end encodeBytes
private static String encodeBytes(byte[] source, int off, int len, boolean breakLines) {
StringBuffer buf = new StringBuffer( (len*4)/3 );
encodeBytes(source, off, len, breakLines, buf, ALPHABET);
return buf.toString();
}
/**
* Encodes a byte array into Base64 notation.
*
@ -390,32 +449,36 @@ public class Base64 {
* @param breakLines Break lines at 80 characters or less.
* @since 1.4
*/
private static String encodeBytes(byte[] source, int off, int len, boolean breakLines) {
private static void encodeBytes(byte[] source, int off, int len, boolean breakLines, StringBuffer out, byte alpha[]) {
int len43 = len * 4 / 3;
byte[] outBuff = new byte[(len43) // Main 4:3
+ ((len % 3) > 0 ? 4 : 0) // Account for padding
+ (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines
//byte[] outBuff = new byte[(len43) // Main 4:3
// + ((len % 3) > 0 ? 4 : 0) // Account for padding
// + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines
int d = 0;
int e = 0;
int len2 = len - 2;
int lineLength = 0;
for (; d < len2; d += 3, e += 4) {
encode3to4(source, d + off, 3, outBuff, e);
//encode3to4(source, d + off, 3, outBuff, e);
encode3to4(source, d + off, 3, out, alpha);
lineLength += 4;
if (breakLines && lineLength == MAX_LINE_LENGTH) {
outBuff[e + 4] = NEW_LINE;
//outBuff[e + 4] = NEW_LINE;
out.append('\n');
e++;
lineLength = 0;
} // end if: end of line
} // en dfor: each piece of array
if (d < len) {
encode3to4(source, d + off, len - d, outBuff, e);
//encode3to4(source, d + off, len - d, outBuff, e);
encode3to4(source, d + off, len - d, out, alpha);
e += 4;
} // end if: some padding needed
return new String(outBuff, 0, e);
//out.append(new String(outBuff, 0, e));
//return new String(outBuff, 0, e);
} // end encodeBytes
/**

View File

@ -10,6 +10,7 @@ package net.i2p.data;
*/
import java.io.Serializable;
import net.i2p.data.Base64;
/**
* Wrap up an array of bytes so that they can be compared and placed in hashes,
@ -26,8 +27,9 @@ public class ByteArray implements Serializable, Comparable {
}
public ByteArray(byte[] data) {
_offset = 0;
_data = data;
_valid = 0;
_valid = (data != null ? data.length : 0);
}
public ByteArray(byte[] data, int offset, int length) {
_data = data;
@ -56,19 +58,20 @@ public class ByteArray implements Serializable, Comparable {
public final boolean equals(Object o) {
if (o == null) return false;
if (o instanceof ByteArray) {
return compare(getData(), ((ByteArray) o).getData());
ByteArray ba = (ByteArray)o;
return compare(getData(), _offset, _valid, ba.getData(), ba.getOffset(), ba.getValid());
}
try {
byte val[] = (byte[]) o;
return compare(getData(), val);
return compare(getData(), _offset, _valid, val, 0, val.length);
} catch (Throwable t) {
return false;
}
}
private static final boolean compare(byte[] lhs, byte[] rhs) {
return DataHelper.eq(lhs, rhs);
private static final boolean compare(byte[] lhs, int loff, int llen, byte[] rhs, int roff, int rlen) {
return (llen == rlen) && DataHelper.eq(lhs, loff, rhs, roff, llen);
}
public final int compareTo(Object obj) {
@ -81,6 +84,10 @@ public class ByteArray implements Serializable, Comparable {
}
public final String toString() {
return DataHelper.toString(getData(), 32);
return super.toString() + "/" + DataHelper.toString(getData(), 32) + "." + _valid;
}
public final String toBase64() {
return Base64.encode(_data, _offset, _valid);
}
}

View File

@ -721,7 +721,7 @@ public class DataHelper {
public static int hashCode(byte b[]) {
int rv = 0;
if (b != null) {
for (int i = 0; i < b.length && i < 8; i++)
for (int i = 0; i < b.length && i < 32; i++)
rv += (b[i] << i);
}
return rv;

View File

@ -24,8 +24,10 @@ import net.i2p.util.Log;
public class SessionKey extends DataStructureImpl {
private final static Log _log = new Log(SessionKey.class);
private byte[] _data;
private Object _preparedKey;
public final static int KEYSIZE_BYTES = 32;
public static final SessionKey INVALID_KEY = new SessionKey(new byte[KEYSIZE_BYTES]);
public SessionKey() {
this(null);
@ -38,9 +40,23 @@ public class SessionKey extends DataStructureImpl {
return _data;
}
/**
* caveat: this method isn't synchronized with the preparedKey, so don't
* try to *change* the key data after already doing some
* encryption/decryption (or if you do change it, be sure this object isn't
* mid decrypt)
*/
public void setData(byte[] data) {
_data = data;
_preparedKey = null;
}
/**
* retrieve an internal representation of the session key, as known
* by the AES engine used. this can be reused safely
*/
public Object getPreparedKey() { return _preparedKey; }
public void setPreparedKey(Object obj) { _preparedKey = obj; }
public void readBytes(InputStream in) throws DataFormatException, IOException {
_data = new byte[KEYSIZE_BYTES];
@ -63,7 +79,8 @@ public class SessionKey extends DataStructureImpl {
return DataHelper.hashCode(_data);
}
public String toString() {
public String toString() {
if (true) return super.toString();
StringBuffer buf = new StringBuffer(64);
buf.append("[SessionKey: ");
if (_data == null) {

View File

@ -37,10 +37,12 @@ public class SessionTag extends ByteArray {
}
public void setData(byte val[]) throws IllegalArgumentException {
if (val == null) super.setData(null);
if (val == null)
throw new NullPointerException("SessionTags cannot be null");
if (val.length != BYTE_LENGTH)
throw new IllegalArgumentException("SessionTags must be " + BYTE_LENGTH + " bytes");
super.setData(val);
setValid(BYTE_LENGTH);
}
public void readBytes(InputStream in) throws DataFormatException, IOException {

View File

@ -61,6 +61,7 @@ public abstract class I2CPMessageImpl extends DataStructureImpl implements I2CPM
+ " class: " + getClass().getName() + ")");
if (length < 0) throw new IOException("Negative payload size");
/*
byte buf[] = new byte[length];
int read = DataHelper.read(in, buf);
if (read != length)
@ -69,6 +70,8 @@ public abstract class I2CPMessageImpl extends DataStructureImpl implements I2CPM
ByteArrayInputStream bis = new ByteArrayInputStream(buf);
doReadMessage(bis, length);
*/
doReadMessage(in, length);
}
/**

View File

@ -26,22 +26,25 @@ import net.i2p.util.Log;
*/
public class MessageId extends DataStructureImpl {
private final static Log _log = new Log(MessageId.class);
private int _messageId;
private long _messageId;
public MessageId() {
setMessageId(-1);
}
public MessageId(long id) {
setMessageId(id);
}
public int getMessageId() {
public long getMessageId() {
return _messageId;
}
public void setMessageId(int id) {
public void setMessageId(long id) {
_messageId = id;
}
public void readBytes(InputStream in) throws DataFormatException, IOException {
_messageId = (int) DataHelper.readLong(in, 4);
_messageId = DataHelper.readLong(in, 4);
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
@ -55,7 +58,7 @@ public class MessageId extends DataStructureImpl {
}
public int hashCode() {
return getMessageId();
return (int)getMessageId();
}
public String toString() {

View File

@ -27,29 +27,29 @@ import net.i2p.util.Log;
public class MessagePayloadMessage extends I2CPMessageImpl {
private final static Log _log = new Log(MessagePayloadMessage.class);
public final static int MESSAGE_TYPE = 31;
private SessionId _sessionId;
private MessageId _messageId;
private long _sessionId;
private long _messageId;
private Payload _payload;
public MessagePayloadMessage() {
setSessionId(null);
setMessageId(null);
setSessionId(-1);
setMessageId(-1);
setPayload(null);
}
public SessionId getSessionId() {
public long getSessionId() {
return _sessionId;
}
public void setSessionId(SessionId id) {
public void setSessionId(long id) {
_sessionId = id;
}
public MessageId getMessageId() {
public long getMessageId() {
return _messageId;
}
public void setMessageId(MessageId id) {
public void setMessageId(long id) {
_messageId = id;
}
@ -63,10 +63,8 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
try {
_sessionId = new SessionId();
_sessionId.readBytes(in);
_messageId = new MessageId();
_messageId.readBytes(in);
_sessionId = DataHelper.readLong(in, 2);
_messageId = DataHelper.readLong(in, 4);
_payload = new Payload();
_payload.readBytes(in);
} catch (DataFormatException dfe) {
@ -84,9 +82,9 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
*
*/
public void writeMessage(OutputStream out) throws I2CPMessageException, IOException {
if (_sessionId == null)
if (_sessionId <= 0)
throw new I2CPMessageException("Unable to write out the message, as the session ID has not been defined");
if (_messageId == null)
if (_messageId < 0)
throw new I2CPMessageException("Unable to write out the message, as the message ID has not been defined");
if (_payload == null)
throw new I2CPMessageException("Unable to write out the message, as the payload has not been defined");
@ -95,8 +93,8 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
try {
DataHelper.writeLong(out, 4, size);
DataHelper.writeLong(out, 1, getType());
DataHelper.writeLong(out, 2, _sessionId.getSessionId());
DataHelper.writeLong(out, 4, _messageId.getMessageId());
DataHelper.writeLong(out, 2, _sessionId);
DataHelper.writeLong(out, 4, _messageId);
DataHelper.writeLong(out, 4, _payload.getSize());
out.write(_payload.getEncryptedData());
} catch (DataFormatException dfe) {

View File

@ -12,6 +12,7 @@ package net.i2p.data.i2cp;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
@ -26,8 +27,8 @@ import net.i2p.util.Log;
public class MessageStatusMessage extends I2CPMessageImpl {
private final static Log _log = new Log(SessionStatusMessage.class);
public final static int MESSAGE_TYPE = 22;
private SessionId _sessionId;
private MessageId _messageId;
private long _sessionId;
private long _messageId;
private long _nonce;
private long _size;
private int _status;
@ -40,18 +41,18 @@ public class MessageStatusMessage extends I2CPMessageImpl {
public final static int STATUS_SEND_GUARANTEED_FAILURE = 5;
public MessageStatusMessage() {
setSessionId(null);
setSessionId(-1);
setStatus(-1);
setMessageId(null);
setMessageId(-1);
setSize(-1);
setNonce(-1);
}
public SessionId getSessionId() {
public long getSessionId() {
return _sessionId;
}
public void setSessionId(SessionId id) {
public void setSessionId(long id) {
_sessionId = id;
}
@ -63,11 +64,11 @@ public class MessageStatusMessage extends I2CPMessageImpl {
_status = status;
}
public MessageId getMessageId() {
public long getMessageId() {
return _messageId;
}
public void setMessageId(MessageId id) {
public void setMessageId(long id) {
_messageId = id;
}
@ -108,10 +109,8 @@ public class MessageStatusMessage extends I2CPMessageImpl {
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
try {
_sessionId = new SessionId();
_sessionId.readBytes(in);
_messageId = new MessageId();
_messageId.readBytes(in);
_sessionId = DataHelper.readLong(in, 2);
_messageId = DataHelper.readLong(in, 4);
_status = (int) DataHelper.readLong(in, 1);
_size = DataHelper.readLong(in, 4);
_nonce = DataHelper.readLong(in, 4);
@ -120,20 +119,32 @@ public class MessageStatusMessage extends I2CPMessageImpl {
}
}
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
if ((_sessionId == null) || (_messageId == null) || (_status < 0) || (_nonce <= 0))
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
ByteArrayOutputStream os = new ByteArrayOutputStream(64);
/**
* Override to reduce mem churn
*/
public void writeMessage(OutputStream out) throws I2CPMessageException, IOException {
int len = 2 + // sessionId
4 + // messageId
1 + // status
4 + // size
4; // nonce
try {
_sessionId.writeBytes(os);
_messageId.writeBytes(os);
DataHelper.writeLong(os, 1, _status);
DataHelper.writeLong(os, 4, _size);
DataHelper.writeLong(os, 4, _nonce);
DataHelper.writeLong(out, 4, len);
DataHelper.writeLong(out, 1, getType());
DataHelper.writeLong(out, 2, _sessionId);
DataHelper.writeLong(out, 4, _messageId);
DataHelper.writeLong(out, 1, _status);
DataHelper.writeLong(out, 4, _size);
DataHelper.writeLong(out, 4, _nonce);
} catch (DataFormatException dfe) {
throw new I2CPMessageException("Error writing out the message data", dfe);
throw new I2CPMessageException("Unable to write the message length or type", dfe);
}
return os.toByteArray();
}
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
throw new UnsupportedOperationException("This shouldn't be called... use writeMessage(out)");
}
public int getType() {

View File

@ -12,6 +12,7 @@ package net.i2p.data.i2cp;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
@ -26,50 +27,62 @@ import net.i2p.util.Log;
public class ReceiveMessageBeginMessage extends I2CPMessageImpl {
private final static Log _log = new Log(ReceiveMessageBeginMessage.class);
public final static int MESSAGE_TYPE = 6;
private SessionId _sessionId;
private MessageId _messageId;
private long _sessionId;
private long _messageId;
public ReceiveMessageBeginMessage() {
setSessionId(null);
setMessageId(null);
setSessionId(-1);
setMessageId(-1);
}
public SessionId getSessionId() {
public long getSessionId() {
return _sessionId;
}
public void setSessionId(SessionId id) {
public void setSessionId(long id) {
_sessionId = id;
}
public MessageId getMessageId() {
public long getMessageId() {
return _messageId;
}
public void setMessageId(MessageId id) {
public void setMessageId(long id) {
_messageId = id;
}
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
try {
_sessionId = new SessionId();
_sessionId.readBytes(in);
_messageId = new MessageId();
_messageId.readBytes(in);
_sessionId = DataHelper.readLong(in, 2);
_messageId = DataHelper.readLong(in, 4);
} catch (DataFormatException dfe) {
throw new I2CPMessageException("Unable to load the message data", dfe);
}
}
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");
byte rv[] = new byte[2+4];
DataHelper.toLong(rv, 0, 2, _sessionId.getSessionId());
DataHelper.toLong(rv, 2, 4, _messageId.getMessageId());
return rv;
throw new UnsupportedOperationException("This shouldn't be called... use writeMessage(out)");
}
/**
* Override to reduce mem churn
*/
public void writeMessage(OutputStream out) throws I2CPMessageException, IOException {
int len = 2 + // sessionId
4; // messageId
try {
DataHelper.writeLong(out, 4, len);
DataHelper.writeLong(out, 1, getType());
DataHelper.writeLong(out, 2, _sessionId);
DataHelper.writeLong(out, 4, _messageId);
} catch (DataFormatException dfe) {
throw new I2CPMessageException("Unable to write the message length or type", dfe);
}
}
public int getType() {
return MESSAGE_TYPE;
}

View File

@ -26,47 +26,45 @@ import net.i2p.util.Log;
public class ReceiveMessageEndMessage extends I2CPMessageImpl {
private final static Log _log = new Log(ReceiveMessageEndMessage.class);
public final static int MESSAGE_TYPE = 7;
private SessionId _sessionId;
private MessageId _messageId;
private long _sessionId;
private long _messageId;
public ReceiveMessageEndMessage() {
setSessionId(null);
setMessageId(null);
setSessionId(-1);
setMessageId(-1);
}
public SessionId getSessionId() {
public long getSessionId() {
return _sessionId;
}
public void setSessionId(SessionId id) {
public void setSessionId(long id) {
_sessionId = id;
}
public MessageId getMessageId() {
public long getMessageId() {
return _messageId;
}
public void setMessageId(MessageId id) {
public void setMessageId(long id) {
_messageId = id;
}
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
try {
_sessionId = new SessionId();
_sessionId.readBytes(in);
_messageId = new MessageId();
_messageId.readBytes(in);
_sessionId = DataHelper.readLong(in, 2);
_messageId = DataHelper.readLong(in, 4);
} catch (DataFormatException dfe) {
throw new I2CPMessageException("Unable to load the message data", dfe);
}
}
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
if ((_sessionId == null) || (_messageId == null))
if ((_sessionId < 0) || (_messageId < 0))
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
byte rv[] = new byte[2+4];
DataHelper.toLong(rv, 0, 2, _sessionId.getSessionId());
DataHelper.toLong(rv, 2, 4, _messageId.getMessageId());
DataHelper.toLong(rv, 0, 2, _sessionId);
DataHelper.toLong(rv, 2, 4, _messageId);
return rv;
}

View File

@ -129,7 +129,7 @@ public class RequestLeaseSetMessage extends I2CPMessageImpl {
RequestLeaseSetMessage msg = (RequestLeaseSetMessage) object;
if (getEndpoints() != msg.getEndpoints()) return false;
for (int i = 0; i < getEndpoints(); i++) {
if (!DataHelper.eq(getRouter(i), msg.getRouter(i)) || DataHelper.eq(getTunnelId(i), msg.getTunnelId(i)))
if (!DataHelper.eq(getRouter(i), msg.getRouter(i)) || !DataHelper.eq(getTunnelId(i), msg.getTunnelId(i)))
return false;
}
return DataHelper.eq(getSessionId(), msg.getSessionId()) && DataHelper.eq(getEndDate(), msg.getEndDate());

View File

@ -30,11 +30,15 @@ public class BufferedStatLog implements StatLog {
private BufferedWriter _out;
private String _outFile;
private static final int BUFFER_SIZE = 1024;
private static final boolean DISABLE_LOGGING = false;
public BufferedStatLog(I2PAppContext ctx) {
_context = ctx;
_log = ctx.logManager().getLog(BufferedStatLog.class);
_events = new StatEvent[1000];
for (int i = 0; i < 1000; i++)
_events = new StatEvent[BUFFER_SIZE];
if (DISABLE_LOGGING) return;
for (int i = 0; i < BUFFER_SIZE; i++)
_events[i] = new StatEvent();
_eventNext = 0;
_lastWrite = _events.length-1;
@ -46,6 +50,7 @@ public class BufferedStatLog implements StatLog {
}
public void addData(String scope, String stat, long value, long duration) {
if (DISABLE_LOGGING) return;
synchronized (_events) {
_events[_eventNext].init(scope, stat, value, duration);
_eventNext = (_eventNext + 1) % _events.length;
@ -71,9 +76,9 @@ public class BufferedStatLog implements StatLog {
return _statFilters.contains(stat) || _statFilters.contains("*");
}
}
private void updateFilters() {
String val = _context.getProperty("stat.logFilters");
String val = _context.getProperty(StatManager.PROP_STAT_FILTER);
if (val != null) {
if ( (_lastFilters != null) && (_lastFilters.equals(val)) ) {
// noop
@ -90,9 +95,9 @@ public class BufferedStatLog implements StatLog {
synchronized (_statFilters) { _statFilters.clear(); }
}
String filename = _context.getProperty("stat.logFile");
String filename = _context.getProperty(StatManager.PROP_STAT_FILE);
if (filename == null)
filename = "stats.log";
filename = StatManager.DEFAULT_STAT_FILE;
if ( (_outFile != null) && (_outFile.equals(filename)) ) {
// noop
} else {

View File

@ -29,6 +29,10 @@ public class StatManager {
private Map _rateStats;
private StatLog _statLog;
public static final String PROP_STAT_FILTER = "stat.logFilters";
public static final String PROP_STAT_FILE = "stat.logFile";
public static final String DEFAULT_STAT_FILE = "stats.log";
/**
* The stat manager should only be constructed and accessed through the
* application context. This constructor should only be used by the
@ -139,7 +143,7 @@ public class StatManager {
return _frequencyStats.containsKey(statName);
}
/** Group name (String) to a Set of stat names */
/** Group name (String) to a Set of stat names, ordered alphabetically */
public Map getStatsByGroup() {
Map groups = new TreeMap();
for (Iterator iter = _frequencyStats.values().iterator(); iter.hasNext();) {
@ -156,4 +160,7 @@ public class StatManager {
}
return groups;
}
public String getStatFilter() { return _context.getProperty(PROP_STAT_FILTER); }
public String getStatFile() { return _context.getProperty(PROP_STAT_FILE, DEFAULT_STAT_FILE); }
}

View File

@ -0,0 +1,228 @@
package net.i2p.util;
/*
* 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.security.SecureRandom;
import net.i2p.I2PAppContext;
import net.i2p.crypto.EntropyHarvester;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
/**
* Allocate data out of a large buffer of data, rather than the PRNG's
* (likely) small buffer to reduce the frequency of prng recalcs (though
* the recalcs are now more time consuming).
*
*/
public class BufferedRandomSource extends RandomSource {
private byte _buffer[];
private int _nextByte;
private int _nextBit;
private static volatile long _reseeds;
private static final int DEFAULT_BUFFER_SIZE = 256*1024;
public BufferedRandomSource(I2PAppContext context) {
this(context, DEFAULT_BUFFER_SIZE);
}
public BufferedRandomSource(I2PAppContext context, int bufferSize) {
super(context);
context.statManager().createRateStat("prng.reseedCount", "How many times the prng has been reseeded", "Encryption", new long[] { 60*1000, 10*60*1000, 60*60*1000 } );
_buffer = new byte[bufferSize];
refillBuffer();
// stagger reseeding
_nextByte = ((int)_reseeds-1) * 16 * 1024;
}
private final void refillBuffer() {
long before = System.currentTimeMillis();
doRefillBuffer();
long duration = System.currentTimeMillis() - before;
if ( (_reseeds % 1) == 0)
_context.statManager().addRateData("prng.reseedCount", _reseeds, duration);
}
private synchronized final void doRefillBuffer() {
super.nextBytes(_buffer);
_nextByte = 0;
_nextBit = 0;
_reseeds++;
}
private static final byte GOBBLE_MASK[] = { 0x0, // 0 bits
0x1, // 1 bit
0x3, // 2 bits
0x7, // 3 bits
0xF, // 4 bits
0x1F, // 5 bits
0x3F, // 6 bits
0x7F, // 7 bits
(byte)0xFF // 8 bits
};
private synchronized final long nextBits(int numBits) {
if (false) {
long rv = 0;
for (int curBit = 0; curBit < numBits; curBit++) {
if (_nextBit >= 8) {
_nextBit = 0;
_nextByte++;
}
if (_nextByte >= _buffer.length)
refillBuffer();
rv += (_buffer[_nextByte] << curBit);
_nextBit++;
/*
int avail = 8 - _nextBit;
// this is not correct! (or is it?)
rv += (_buffer[_nextByte] << 8 - avail);
_nextBit += avail;
numBits -= avail;
if (_nextBit >= 8) {
_nextBit = 0;
_nextByte++;
}
*/
}
return rv;
} else {
long rv = 0;
int curBit = 0;
while (curBit < numBits) {
if (_nextBit >= 8) {
_nextBit = 0;
_nextByte++;
}
if (_nextByte >= _buffer.length)
refillBuffer();
int gobbleBits = 8 - _nextBit;
int want = numBits - curBit;
if (gobbleBits > want)
gobbleBits = want;
curBit += gobbleBits;
int shift = 8 - _nextBit - gobbleBits;
int c = (_buffer[_nextByte] & (GOBBLE_MASK[gobbleBits] << shift));
rv += ((c >>> shift) << (curBit-gobbleBits));
_nextBit += gobbleBits;
}
return rv;
}
}
public synchronized final void nextBytes(byte buf[]) {
int outOffset = 0;
while (outOffset < buf.length) {
int availableBytes = _buffer.length - _nextByte - (_nextBit != 0 ? 1 : 0);
if (availableBytes <= 0)
refillBuffer();
int start = _buffer.length - availableBytes;
int writeSize = Math.min(buf.length - outOffset, availableBytes);
System.arraycopy(_buffer, start, buf, outOffset, writeSize);
outOffset += writeSize;
_nextByte += writeSize;
_nextBit = 0;
}
}
public final int nextInt(int n) {
if (n <= 0) return 0;
int val = ((int)nextBits(countBits(n))) % n;
if (val < 0)
return 0 - val;
else
return val;
}
public final int nextInt() { return nextInt(Integer.MAX_VALUE); }
/**
* Like the modified nextInt, nextLong(n) returns a random number from 0 through n,
* including 0, excluding n.
*/
public final long nextLong(long n) {
if (n <= 0) return 0;
long val = nextBits(countBits(n)) % n;
if (val < 0)
return 0 - val;
else
return val;
}
public final long nextLong() { return nextLong(Long.MAX_VALUE); }
static final int countBits(long val) {
int rv = 0;
while (val > Integer.MAX_VALUE) {
rv += 31;
val >>>= 31;
}
while (val > 0) {
rv++;
val >>= 1;
}
return rv;
}
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
public final boolean nextBoolean() {
return nextBits(1) != 0;
}
private static final double DOUBLE_DENOMENATOR = (double)(1L << 53);
/** defined per javadoc ( ((nextBits(26)<<27) + nextBits(27)) / (1 << 53)) */
public final double nextDouble() {
long top = (((long)nextBits(26) << 27) + nextBits(27));
return top / DOUBLE_DENOMENATOR;
}
private static final float FLOAT_DENOMENATOR = (float)(1 << 24);
/** defined per javadoc (nextBits(24) / ((float)(1 << 24)) ) */
public float nextFloat() {
long top = nextBits(24);
return top / FLOAT_DENOMENATOR;
}
public double nextGaussian() {
// bah, unbuffered
return super.nextGaussian();
}
public static void main(String args[]) {
for (int i = 0; i < 16; i++)
test();
}
private static void test() {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
byte data[] = new byte[16*1024];
for (int i = 0; i < data.length; i += 4) {
long l = ctx.random().nextLong();
if (l < 0) l = 0 - l;
DataHelper.toLong(data, i, 4, l);
}
byte compressed[] = DataHelper.compress(data);
System.out.println("Data: " + data.length + "/" + compressed.length + ": " + toString(data));
}
private static final String toString(byte data[]) {
StringBuffer buf = new StringBuffer(data.length * 9);
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < 8; j++) {
if ((data[i] & (1 << j)) != 0)
buf.append('1');
else
buf.append('0');
}
buf.append(' ');
}
return buf.toString();
}
}

View File

@ -85,11 +85,15 @@ public final class ByteCache {
*
*/
public final void release(ByteArray entry) {
release(entry, true);
}
public final void release(ByteArray entry, boolean shouldZero) {
if (_cache) {
if ( (entry == null) || (entry.getData() == null) )
return;
Arrays.fill(entry.getData(), (byte)0x0);
if (shouldZero)
Arrays.fill(entry.getData(), (byte)0x0);
synchronized (_available) {
if (_available.size() < _maxCached)
_available.add(entry);

View File

@ -59,6 +59,7 @@ public class Clock implements Timestamper.UpdateListener {
*
*/
public void setOffset(long offsetMs, boolean force) {
if (false) return;
long delta = offsetMs - _offset;
if (!force) {
if ((offsetMs > MAX_OFFSET) || (offsetMs < 0 - MAX_OFFSET)) {

View File

@ -28,6 +28,8 @@ public class DecayingBloomFilter {
private long _currentDuplicates;
private boolean _keepDecaying;
private DecayEvent _decayEvent;
private static final boolean ALWAYS_MISS = false;
/**
* Create a bloom filter that will decay its entries over time.
@ -41,8 +43,8 @@ public class DecayingBloomFilter {
_context = context;
_log = context.logManager().getLog(DecayingBloomFilter.class);
_entryBytes = entryBytes;
_current = new BloomSHA1(23, 11);
_previous = new BloomSHA1(23, 11);
_current = new BloomSHA1(23, 11); //new BloomSHA1(23, 11);
_previous = new BloomSHA1(23, 11); //new BloomSHA1(23, 11);
_durationMs = durationMs;
int numExtenders = (32+ (entryBytes-1))/entryBytes - 1;
if (numExtenders < 0)
@ -78,6 +80,7 @@ public class DecayingBloomFilter {
*
*/
public boolean add(byte entry[]) {
if (ALWAYS_MISS) return false;
if (entry == null)
throw new IllegalArgumentException("Null entry");
if (entry.length != _entryBytes)
@ -95,6 +98,7 @@ public class DecayingBloomFilter {
*
*/
public boolean add(long entry) {
if (ALWAYS_MISS) return false;
synchronized (this) {
if (_entryBytes <= 7)
entry &= _longToEntryMask;
@ -108,7 +112,30 @@ public class DecayingBloomFilter {
}
}
/**
* return true if the entry is already known. this does NOT add the
* entry however.
*
*/
public boolean isKnown(long entry) {
if (ALWAYS_MISS) return false;
synchronized (this) {
if (_entryBytes <= 7)
entry &= _longToEntryMask;
if (entry < 0) {
DataHelper.toLong(_longToEntry, 0, _entryBytes, 0-entry);
_longToEntry[0] |= (1 << 7);
} else {
DataHelper.toLong(_longToEntry, 0, _entryBytes, entry);
}
return locked_add(_longToEntry, false);
}
}
private boolean locked_add(byte entry[]) {
return locked_add(entry, true);
}
private boolean locked_add(byte entry[], boolean addIfNew) {
if (_extended != null) {
// extend the entry to 32 bytes
System.arraycopy(entry, 0, _extended, 0, entry.length);
@ -121,8 +148,10 @@ public class DecayingBloomFilter {
_currentDuplicates++;
return true;
} else {
_current.insert(_extended);
_previous.insert(_extended);
if (addIfNew) {
_current.insert(_extended);
_previous.insert(_extended);
}
return false;
}
} else {
@ -132,8 +161,10 @@ public class DecayingBloomFilter {
_currentDuplicates++;
return true;
} else {
_current.locked_insert(entry);
_previous.locked_insert(entry);
if (addIfNew) {
_current.locked_insert(entry);
_previous.locked_insert(entry);
}
return false;
}
}

View File

@ -54,18 +54,18 @@ public class EepGet {
this(ctx, true, proxyHost, proxyPort, numRetries, outputFile, url);
}
public EepGet(I2PAppContext ctx, String proxyHost, int proxyPort, int numRetries, String outputFile, String url, boolean allowCaching) {
this(ctx, true, proxyHost, proxyPort, numRetries, outputFile, url, allowCaching);
this(ctx, true, proxyHost, proxyPort, numRetries, outputFile, url, allowCaching, null);
}
public EepGet(I2PAppContext ctx, int numRetries, String outputFile, String url) {
this(ctx, false, null, -1, numRetries, outputFile, url);
}
public EepGet(I2PAppContext ctx, int numRetries, String outputFile, String url, boolean allowCaching) {
this(ctx, false, null, -1, numRetries, outputFile, url, allowCaching);
this(ctx, false, null, -1, numRetries, outputFile, url, allowCaching, null);
}
public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, String outputFile, String url) {
this(ctx, shouldProxy, proxyHost, proxyPort, numRetries, outputFile, url, true);
this(ctx, shouldProxy, proxyHost, proxyPort, numRetries, outputFile, url, true, null);
}
public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, String outputFile, String url, boolean allowCaching) {
public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, String outputFile, String url, boolean allowCaching, String etag) {
_context = ctx;
_log = ctx.logManager().getLog(EepGet.class);
_shouldProxy = shouldProxy;
@ -79,10 +79,11 @@ public class EepGet {
_bytesRemaining = -1;
_currentAttempt = 0;
_listeners = new ArrayList(1);
_etag = etag;
}
/**
* EepGet [-p localhost:4444] [-n #retries] [-o outputFile] [-m markSize lineLen] url
* EepGet [-p localhost:4444] [-n #retries] [-e etag] [-o outputFile] [-m markSize lineLen] url
*
*/
public static void main(String args[]) {
@ -91,6 +92,7 @@ public class EepGet {
int numRetries = 5;
int markSize = 1024;
int lineLen = 40;
String etag = null;
String saveAs = null;
String url = null;
try {
@ -103,6 +105,9 @@ public class EepGet {
} else if (args[i].equals("-n")) {
numRetries = Integer.parseInt(args[i+1]);
i++;
} else if (args[i].equals("-e")) {
etag = "\"" + args[i+1] + "\"";
i++;
} else if (args[i].equals("-o")) {
saveAs = args[i+1];
i++;
@ -127,7 +132,7 @@ public class EepGet {
if (saveAs == null)
saveAs = suggestName(url);
EepGet get = new EepGet(I2PAppContext.getGlobalContext(), proxyHost, proxyPort, numRetries, saveAs, url);
EepGet get = new EepGet(I2PAppContext.getGlobalContext(), true, proxyHost, proxyPort, numRetries, saveAs, url, true, etag);
get.addStatusListener(get.new CLIStatusListener(markSize, lineLen));
get.fetch();
}
@ -230,6 +235,7 @@ public class EepGet {
System.out.println("== Output saved to " + outputFile);
long timeToSend = _context.clock().now() - _startedOn;
System.out.println("== Transfer time: " + DataHelper.formatDuration(timeToSend));
System.out.println("== ETag: " + _etag);
StringBuffer buf = new StringBuffer(50);
buf.append("== Transfer rate: ");
double kbps = (1000.0d*(double)(_written)/((double)timeToSend*1024.0d));
@ -367,6 +373,10 @@ public class EepGet {
_out = new FileOutputStream(_outputFile, true);
rcOk = true;
break;
case 304: // not modified
_bytesRemaining = 0;
_keepFetching = false;
return;
case 416: // completed (or range out of reach)
_bytesRemaining = 0;
_keepFetching = false;
@ -564,9 +574,19 @@ public class EepGet {
buf.append("Cache-control: no-cache\n");
buf.append("Pragma: no-cache\n");
}
if (_etag != null) {
buf.append("If-None-Match: ");
buf.append(_etag);
buf.append("\n");
}
buf.append("Connection: close\n\n");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Request: [" + buf.toString() + "]");
return buf.toString();
}
public String getETag() {
return _etag;
}
}

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