Compare commits

...

1125 Commits

Author SHA1 Message Date
49d6f5018f * Properly expand the HTTP response header buffer (thanks shendaras!) 2005-03-07 00:40:45 +00:00
4a830e422a added music.i2p, rotten.i2p, wintermute.i2p, kaji2.i2p, aspnet.i2p, gaming.i2p, nntp.i2p 2005-03-07 00:38:19 +00:00
df6c52fe75 * 2005-03-06 0.5.0.2 released
2005-03-06  jrandom
    * Allow the I2PTunnel web interface to select streaming lib options for
      individual client tunnels, rather than sharing them across all of them,
      as we do with the session options.  This way people can (and should) set
      the irc proxy to interactive and the eepproxy to bulk.
    * Added a startRouter.sh script to new installs which simply calls
      "sh i2prouter start".  This should make it clear how people should start
      I2P.
2005-03-07 00:07:27 +00:00
01979c08b3 2005-03-04 jrandom
* Filter HTTP response headers in the eepproxy, forcing Connection: close
      so that broken (/malicious) webservers can't allow persistent
      connections.  All HTTP compliant browsers should now always close the
      socket.
    * Enabled the GZIPInputStream's cache (they were'nt cached before)
    * Make sure our first send is always a SYN (duh)
    * Workaround for some buggy compilers
2005-03-05 02:54:42 +00:00
7928ef83cc added cowsay.i2p 2005-03-04 23:37:39 +00:00
10afe0a060 2005-03-03 jrandom
* Loop while starting up the I2PTunnel instances, in case the I2CP
      listener isn't up yet (thanks detonate!)
    * Implement custom reusable GZIP streams to both reduce memory churn
      and prevent the exposure of data in the standard GZIP header (creation
      time, OS, etc).  This is RFC1952 compliant, and backwards compatible,
      though has only been tested within the confines of I2P's compression use
      (DataHelper.[de]compress).
    * Preemptively support the next protocol version, so that after the 0.5.0.2
      release, we'll be able to drop protocol=2 to get rid of 0.5 users.
2005-03-04 06:09:20 +00:00
ef230cfa3d 2005-03-02 jrandom
* Fix one substantial OOM cause (session tag manager was only dropping
      tags once the critical limit was met, rather than honoring their
      expiration) (duh)
    * Lots of small memory fixes
    * Double the allowable concurrent outstanding tunnel build tasks (20)
2005-03-03 03:36:52 +00:00
2d15a42137 big code cleanup to reduce number of compiler warnings 2005-03-01 23:25:15 +00:00
57d6a2f645 2005-03-01 jrandom
* Really disable the streaming lib packet caching
    * Synchronized a message handling point in the SDK (even though its use is
      already essentially single threaded, its better to play it safe)
    * Don't add new RepublishLeaseSetJobs on failure, just requeue up the
      existing one (duh)
    * Throttle the number of concurrent pending tunnel builds across all
      pools, in addition to simply throttling the number of new requests per
      minute for each pool individually.  This should avoid the cascading
      failure when tunnel builds take too long, as no new builds will be
      created until the previous ones are handled.
    * Factored out and extended the DataHelper's unit tests for dealing with
      long and date formatting.
    * Explicitly specify the HTTP auth realm as "i2prouter", though this
      alone doesn't address the bug where jetty asks for authentication too
      much.  (thanks orion!)
    * Updated the StreamSinkServer to ignore all read bytes, rather than write
      them to the filesystem.
2005-03-01 17:50:52 +00:00
469a0852d7 2005-02-27 jrandom
* Don't rerequest leaseSets if there are already pending requests
    * Reverted the insufficiently tested caching in the DSA/SHA1 impl, and
      temporary disabled the streaming lib packet caching.
    * Reduced the resend RTT penalty to 10s
2005-02-27 22:09:37 +00:00
7983bb1490 1.3 here too 2005-02-27 00:13:00 +00:00
2e7eac02ed 2005-02-26 jrandom
* Force 1.3-isms on the precompiled jsps too (thanks laberhost)
2005-02-27 00:03:42 +00:00
238389fc7f 2005-02-26 jrandom
* Further streaming lib caching improvements
    * Reduce the minimum RTT (used to calculate retry timeouts), but also
      increase the RTT on resends.
    * Lower the default message size to 4KB from 16KB to further reduce the
      chance of failed fragmentation.
    * Extend tunnel rebuild throttling to include fallback rebuilds
    * If there are less than 20 routers known, don't drop the last 20 (to help
      avoid dropping all peers under catastrophic failures)
    * New stats for end to end messages - "client.leaseSetFoundLocally",
      "client.leaseSetFoundRemoteTime", and "client.leaseSetFailedRemoteTime"
2005-02-26 19:16:46 +00:00
4cec9da0a6 2005-02-24 jrandom
* Throttle the number of tunnel rebuilds per minute, preventing CPU
      overload under catastrophic failures (thanks Tracker and cervantes!)
    * Block the router startup process until we've initialized the clock
2005-02-24 23:53:35 +00:00
00f27d4400 2005-02-24 jrandom
* Cache temporary memory allocation in the DSA's SHA1 impl, and the packet
      data in the streaming lib.
    * Fixed a streaming lib bug where the connection initiator would fail the
      stream if the ACK to their SYN was lost.
2005-02-24 18:05:25 +00:00
f61618e4a4 2005-02-23 jrandom
* Now that we don't get stale SAM sessions, it'd be nice if we didn't
      get stale tunnel pools, don't you think?
2005-02-23 21:44:30 +00:00
265d5e306e * 2005-02-23 0.5.0.1 released 2005-02-23 05:00:52 +00:00
10ed058c2e 2005-02-22 jrandom
* Reworked the tunnel (re)building process to remove the tokens and
      provide cleaner controls on the tunnels built.
    * Fixed situations where the timestamper wanted to test more servers than
      were provided (thanks Tracker!)
    * Get rid of the dead SAM sessions by using the streaming lib's callbacks
      (thanks Tracker!)
2005-02-23 04:20:28 +00:00
8a21f0efec 2005-02-22 jrandom
* Temporary workaround for the I2CP disconnect bug (have the streaminglib
      try to automatically reconnect on accept()/connect(..)).
    * Loop check for expired lease republishing (just in case)
2005-02-22 23:13:00 +00:00
b8291ac5a4 2005-02-22 jrandom
* Temporary workaround for the I2CP disconnect bug (have the streaminglib
      try to automatically reconnect on accept()/connect(..)).
    * Loop check for expired lease republishing (just in case)
2005-02-22 22:58:21 +00:00
c17433cb93 2005-02-22 jrandom
* Adjusted (and fixed...) the timestamper change detection
    * Deal with a rare reordering bug at the beginning of a stream (so we
      don't drop it unnecessarily)
    * Cleaned up some dropped message handling in the router
    * Reduced job queue churn when dealing with a large number of tunnels by
      sharing an expiration job
    * Keep a separate list of the most recent CRIT messages (shown on the
      logs.jsp).  This way they don't get buried among any other messages.
    * For clarity, display the tunnel variance config as "Randomization" on
      the web console.
    * If lease republishing fails (boo! hiss!) try it again
    * Actually fix the negative jobLag in the right place (this time)
    * Allow reseeding when there are less than 10 known peer references
    * Lots of logging updates.
2005-02-22 07:07:29 +00:00
35fe7f8203 2005-02-20 jrandom
* Allow the streaming lib resend frequency to drop down to 20s as the
      minimum, so that up to 2 retries can get sent on an http request.
    * Add further limits to failsafe tunnels.
    * Keep exploratory and client tunnel testing and building stats separate.
    * Only use the 60s period for throttling tunnel requests due to transient
      network overload.
    * Rebuild tunnels earlier (1-3m before expiration, by default)
    * Cache the next hop's routerInfo for participating tunnels so that the
      tunnel participation doesn't depend on the netDb.
    * Fixed a long standing bug in the streaming lib where we wouldn't always
      unchoke messages when the window size grows.
    * Make sure the window size never reaches 0 (duh)
2005-02-21 19:08:01 +00:00
21f13dba43 2005-02-20 jrandom
* Allow the streaming lib resend frequency to drop down to 20s as the
      minimum, so that up to 2 retries can get sent on an http request.
    * Add further limits to failsafe tunnels.
    * Keep exploratory and client tunnel testing and building stats separate.
    * Only use the 60s period for throttling tunnel requests due to transient
      network overload.
    * Rebuild tunnels earlier (1-3m before expiration, by default)
    * Cache the next hop's routerInfo for participating tunnels so that the
      tunnel participation doesn't depend on the netDb.
    * Fixed a long standing bug in the streaming lib where we wouldn't always
      unchoke messages when the window size grows.
    * Make sure the window size never reaches 0 (duh)
2005-02-21 18:02:14 +00:00
0db239a3fe added irc.postman.i2p 2005-02-21 03:13:40 +00:00
4745d61f9b added subrosa.i2p 2005-02-21 02:55:12 +00:00
b9a4c3ba52 *cough* 2005-02-20 11:09:05 +00:00
cbf6a70a1a 2005-02-20 jrandom
* Only build failsafe tunnels if we need them
    * Properly implement the selectNotFailingPeers so that we get a random
      selection of peers, rather than using the strictOrdering (thanks dm!)
    * Don't include too many "don't tell me about" peer references in the
      lookup message - only send the 10 peer references closest to the target.
2005-02-20 09:12:43 +00:00
7d4e093b58 2005-02-19 jrandom
* Only build new extra tunnels on failure if we don't have enough
    * Fix a fencepost in the tunnel building so that e.g. a variance of
      2 means +/- 2, not +/- 1 (thanks dm!)
    * Avoid an NPE on client disconnect
    * Never select a shitlisted peer to participate in a tunnel
    * Have netDb store messages timeout after 10s, not the full 60s (duh)
    * Keep session tags around for a little longer, just in case (grr)
    * Cleaned up some closing event issues on the streaming lib
    * Stop bundling the jetty 5.1.2 and updated wrapper.config in the update
      so that 0.4.* users will need to do a clean install, but we don't need
      to shove an additional 2MB in each update to those already on 0.5.
    * Imported the susimail css (oops, thanks susi!)
2005-02-19 23:20:56 +00:00
d27feabcb3 clear the old precompiled .java files (thanks duck!) 2005-02-18 16:56:46 +00:00
0d9efa17de bah, fuck it. we can deal with a little shitlisting 2005-02-18 16:04:51 +00:00
b125b04c8d 0.5 released 2005-02-18 15:58:20 +00:00
0539f1d794 updated for the new props 2005-02-18 15:35:02 +00:00
a4b6709f02 added moxonom.i2p 2005-02-18 15:27:46 +00:00
f2db143a6f Refuse to load 0.4 routerInfo (to help weed out the old ones) 2005-02-18 15:25:25 +00:00
b615f54d41 *cough* 2005-02-18 08:28:56 +00:00
db2328e03e * actually reseed properly
* hide the susimail deprecation warnings
* dont push hosts.txt in the update (people can subscribe if they want to)
2005-02-18 08:12:40 +00:00
e2071935ad added sex0r.i2p flock.i2p cneal.i2p www.nntp.i2p wallsgetbombed.i2p
thedarkside.i2p legion.i2p manveru.i2p books.manveru.i2p bt.i2p
2005-02-18 06:23:29 +00:00
1c40ff773f fproxy.i2p and www1.squid.i2p are back (yay!) 2005-02-18 00:52:26 +00:00
37a3645663 Default subscriptions shouldn't rely on a pre-existing hosts.txt. 2005-02-18 00:50:18 +00:00
eb8accd1e0 damn those copyright laws 2005-02-17 23:59:52 +00:00
3af97894b4 tyop 2005-02-17 23:45:50 +00:00
15a0dcf4d8 (not yet tagging this 0.5, but I don't think there's anytihng left)
2005-02-17  jrandom
    * If the clock is adjusted during a job run, don't act as if the job took
      negative time.
2005-02-17 22:57:53 +00:00
aa3a44c42a 2005-02-17 jrandom
* Included the GPL'ed susimail 0.13 by default (thanks susi23!)
2005-02-17 20:55:07 +00:00
40f4b47b87 initial vanilla import of susimail 0.13 (no build script yet) 2005-02-17 20:08:53 +00:00
dca09d96b3 logging 2005-02-17 19:49:16 +00:00
dd10747460 2005-02-17 jrandom
* Fixed the braindead tunnel testing logic
    * If a large number of tunnels are failing (within the last 5-10 minutes)
      and the current tunnel pool's configuration allows it, randomly build a
      zero hop tunnel to replace failed tunnels.
    * Enable postman's POP3 and SMTP tunnels by default
2005-02-17 17:59:27 +00:00
77176162af 2005-02-16 jrandom
* Added some error handling when the number of session tags exceeds the
      realistic capacity, dropping a random chunk of received tag sets and
      conducting some minor analysis of the remaining ones.  This is a part
      of a pretty serious error condition, and logs as CRIT (if/when people
      see "TOO MANY SESSION TAGS!", please let me know the full log line it
      puts in the wrapper.log or /logs.jsp)
    * Update the addressbook to only write to the published hosts location
      if the addressbook's config contains "should_publish=true" (by default,
      it contains "should_publish=false")
2005-02-17 04:08:34 +00:00
8b9ee4dfd7 updated to reflect what was implemented 2005-02-17 00:48:18 +00:00
6e8e77b9ec 0.5 merging 2005-02-16 22:43:00 +00:00
7ef9ce8cc6 0.5 merging 2005-02-16 22:37:24 +00:00
9646ac2911 continuing 0.5 merges 2005-02-16 22:35:12 +00:00
566a713baa 2005-02-16 jrandom
* (Merged the 0.5-pre branch back into CVS HEAD)
    * Replaced the old tunnel routing crypto with the one specified in
      router/doc/tunnel-alt.html, including updates to the web console to view
      and tweak it.
    * Provide the means for routers to reject tunnel requests with a wider
      range of responses:
        probabalistic rejection, due to approaching overload
        transient rejection, due to temporary overload
        bandwidth rejection, due to persistent bandwidth overload
        critical rejection, due to general router fault (or imminent shutdown)
      The different responses are factored into the profiles accordingly.
    * Replaced the old I2CP tunnel related options (tunnels.depthInbound, etc)
      with a series of new properties, relevent to the new tunnel routing code:
        inbound.nickname (used on the console)
        inbound.quantity (# of tunnels to use in any leaseSets)
        inbound.backupQuantity (# of tunnels to keep in the ready)
        inbound.length (# of remote peers in the tunnel)
        inbound.lengthVariance (if > 0, permute the length by adding a random #
                                up to the variance.  if < 0, permute the length
                                by adding or subtracting a random # up to the
                                variance)
        outbound.* (same as the inbound, except for the, uh, outbound tunnels
                    in that client's pool)
      There are other options, and more will be added later, but the above are
      the most relevent ones.
    * Replaced Jetty 4.2.21 with Jetty 5.1.2
    * Compress all profile data on disk.
    * Adjust the reseeding functionality to work even when the JVM's http proxy
      is set.
    * Enable a poor-man's interactive-flow in the streaming lib by choking the
      max window size.
    * Reduced the default streaming lib max message size to 16KB (though still
      configurable by the user), also doubling the default maximum window
      size.
    * Replaced the RouterIdentity in a Lease with its SHA256 hash.
    * Reduced the overall I2NP message checksum from a full 32 byte SHA256 to
      the first byte of the SHA256.
    * Added a new "netId" flag to let routers drop references to other routers
      who we won't be able to talk to.
    * Extended the timestamper to get a second (or third) opinion whenever it
      wants to actually adjust the clock offset.
    * Replaced that kludge of a timestamp I2NP message with a full blown
      DateMessage.
    * Substantial memory optimizations within the router and the SDK to reduce
      GC churn.  Client apps and the streaming libs have not been tuned,
      however.
    * More bugfixes thank you can shake a stick at.

2005-02-13  jrandom
    * Updated jbigi source to handle 64bit CPUs.  The bundled jbigi.jar still
      only contains 32bit versions, so build your own, placing libjbigi.so in
      your install dir if necessary.  (thanks mule!)
    * Added support for libjbigi-$os-athlon64 to NativeBigInteger and CPUID
      (thanks spaetz!)
2005-02-16 22:23:47 +00:00
36f7e98e90 added riaa.i2p 2005-02-16 14:03:22 +00:00
4da755816a added mpaa.i2p 2005-02-15 03:08:45 +00:00
3ef0258faf file TrivialRouterPreprocessor.java was initially added on branch i2p_0_5_pre_branch. 2005-02-14 22:15:20 +00:00
293ceaee93 2005-02-10 smeghead
* Initial check-in of Pants, a new utility to help us manage our 3rd-party
      dependencies (Fortuna, Jetty, Java Service Wrapper, etc.). Some parts of
      Pants are still non-functional at this time so don't mess with it yet
      unless you want to potentially mangle your working copy of CVS.
2005-02-11 02:44:47 +00:00
7b58d0fa0f Allow an unneeded newline in the SAM client protocol without disconnecting. 2005-02-09 19:28:29 +00:00
bd68c1e056 file configtunnels.jsp was initially added on branch i2p_0_5_pre_branch. 2005-02-09 18:15:53 +00:00
200162d973 file ConfigTunnelsHelper.java was initially added on branch i2p_0_5_pre_branch. 2005-02-09 18:15:52 +00:00
4b37a53f1c file TunnelHelper.java was initially added on branch i2p_0_5_pre_branch. 2005-02-09 13:29:02 +00:00
2d41de7ae0 Restore original method of filtering names with non .i2p tlds 2005-02-09 02:21:43 +00:00
bc5bc62c18 file CachingByteArrayOutputStream.java was initially added on branch i2p_0_5_pre_branch. 2005-02-08 22:22:15 +00:00
a0d680024e added pants.i2p 2005-02-08 21:11:24 +00:00
45013feea7 file DateMessage.java was initially added on branch i2p_0_5_pre_branch. 2005-02-08 13:28:51 +00:00
2abbe992dd file BloomFilterIVValidator.java was initially added on branch i2p_0_5_pre_branch. 2005-02-08 12:23:29 +00:00
d7081b3eeb file KeySelector.java was initially added on branch i2p_0_5_pre_branch. 2005-02-07 20:41:46 +00:00
a2f5289bd9 file DecayingBloomFilter.java was initially added on branch i2p_0_5_pre_branch. 2005-02-07 20:41:45 +00:00
b366a4b942 2005-02-07 jrandom
* Fixed a race in the streaming lib's delayed flush algorithm (thanks anon!)
2005-02-07 10:04:23 +00:00
27e92653fe 2005-02-06 Sugadude
* Added a filter to the addressbook to remove entries that dont end in ".i2p"
(thanks Sugadude!)
2005-02-06 22:14:46 +00:00
80120b7b7d added entropy feeding interface, and hooked it up to the end of the DH exchange (source=DH) as well as the end of the ElGamal/AES decrypt (source=ElG/AES). the default RandomSource ignores this data 2005-02-06 08:38:07 +00:00
af8a618826 added irc.carambar.i2p 2005-02-04 17:49:10 +00:00
af0e554562 file PooledTunnelCreatorConfig.java was initially added on branch i2p_0_5_pre_branch. 2005-02-04 07:31:42 +00:00
382cbb18db 2005-02-03 smeghead
* Added Ant buildfile in apps/fortuna for creating a custom Fortuna PRNG jar
      library from GNU Crypto's CVS HEAD sources.
2005-02-03 13:39:46 +00:00
252b523155 file TunnelPoolManager.java was initially added on branch i2p_0_5_pre_branch. 2005-02-01 13:37:30 +00:00
4303b3b716 file TunnelPoolSettings.java was initially added on branch i2p_0_5_pre_branch. 2005-02-01 13:37:29 +00:00
87715dc21a file DummyValidator.java was initially added on branch i2p_0_5_pre_branch. 2005-02-01 13:37:28 +00:00
8552494fc1 file TunnelGatewayMessage.java was initially added on branch i2p_0_5_pre_branch. 2005-02-01 13:37:25 +00:00
1c2290b613 added general.i2p 2005-01-28 22:30:47 +00:00
5f6060b801 2005-01-26 smeghead
* i2pProxy.pac, i2pbench.sh, and i2ptest.sh are now shipped with the dist
      packages and installed to $i2pinstalldir/scripts.
    * Added command line params to i2ptest.sh and i2pbench.sh: --gij to run them
      using gij + libgcj, and --sourcedir to run them from the source tree
      instead of the installation directory.
    * Fixed unreachable for() statement clause in the KBucketImpl class that was
      causing gcj to toss a compilation warning (jrandom++).
2005-01-27 04:48:41 +00:00
b39958604d added smeghead.i2p 2005-01-27 01:35:24 +00:00
22ca1491bc 2005-01-26 smeghead
* Added a couple of scripts, i2ptest.sh and i2pbench.sh, to manage the core
      tests and benchmarks.
    * Routerconsole now builds under gcj 3.4.3.
    * Corrected divide by zero error in TunnelId class under gcj (jrandom++).
2005-01-27 00:21:10 +00:00
690d7e30cf added nntp.fr.i2p (w00t) 2005-01-26 22:07:53 +00:00
4fac2f1094 2005-01-25 smeghead
* Tweaked some classes to enable gcj 3.4.3 to compile the router and
      supporting apps (except for the routerconsole which is still being
      investigated).
2005-01-26 06:29:17 +00:00
eb0935d577 added deadgod.i2p 2005-01-26 04:32:00 +00:00
425fedf55b outbound tunnels passing tests, now to start hacking on the tie-in 2005-01-25 21:42:25 +00:00
a33de09ae6 * implemented fragmentation
* added more inbound tests
* made the tunnel preprocessing header more clear and included better fragmentation support
(still left: tests for outbound tunnel processing, structures and jobs to integrate with the router,
remove that full SHA256 from each and every I2NPMessage or put a smaller one at the
transport layer, and all the rest of the tunnel pooling/building stuff)
2005-01-25 05:46:22 +00:00
5018e56103 oops, moving README and sam-sharp.build out of the source directory 2005-01-24 23:43:37 +00:00
de2c975ac2 2005-01-24 smeghead
* C#-ification of sam-sharp: interface greatly simplified using delegates
      and events; SamBaseEventHandler provides basic implementation and helper
      methods but is now optional.
    * NAnt buildfile and README added for sam-sharp.
2005-01-24 22:42:05 +00:00
d86e2c0f59 2005-01-23 smeghead
* Port the java SAM client library to mono/C# and released into the
      public domain.  The 0.1 version of this port is available in CVS as
      i2p/apps/sam/csharp/src/I2P.SAM.Client.  The other nonfunctional C#
      library has been removed.
2005-01-23 08:22:11 +00:00
14023163b3 added manveru.i2p 2005-01-23 05:09:34 +00:00
d85dc8213e 2005-01-21 Jhor
* Updated jbigi build scripts for OSX.
2005-01-21  jrandom
    * Added support for OSX to the NativeBigInteger code so that it will look
      in the classpath for libjbigi-osx-none.jnilib.  At the moment, that file
      is not bundled with the shipped jbigi.jar yet though.
2005-01-22 01:53:02 +00:00
f6a34055ac removed the tunnel.html-style tunnel encryption and implemented the new tunnel-alt.html style
still much to be done beyond this, but this stuff turned out quite trivial (w00t)
2005-01-21 07:54:56 +00:00
3beb0d9c12 added fr.i2p 2005-01-20 11:53:06 +00:00
60968fe6f1 added imhotep.i2p 2005-01-20 08:57:16 +00:00
517c3101c7 added jrandom.dev.i2p 2005-01-20 02:51:31 +00:00
998f03ba68 killed the loops and the PRNGs by having the tunnel participants themselves specify what
tunnel ID they listen on and make sure the previous peer doesn't change over time.  The
worst that a hostile peer could do is create a multiplicative work factor - they send N
messages, causing N*#hops in the loop of bandwidth usage.  This is identical to the hostile
peer simply building a pair of tunnels and sending N messages through them.
also added some discussion about the tradeoffs and variations wrt fixed size tunnel messages.
2005-01-19 23:13:10 +00:00
f3b0e0cfc7 we want to use E on the preIV, not HMAC - must be invertible (duh, thanks Connelly)
adjusted preIV size accordingly, and definitely use a delivered layerIVKey
2005-01-19 06:24:25 +00:00
a65e6c888c 2005-01-18 jrandom
* Increased the max # session tags maintained and decreased slightly the
      period over which they are gathered.
2005-01-19 00:08:13 +00:00
cd939d3379 speling mistaces 2005-01-18 16:21:12 +00:00
29e5aeff5c include the preIV in the verification hash 2005-01-18 16:01:55 +00:00
0e5cf81fca updates with new alternative crypto, including Connelly's suggestions for the IV 2005-01-18 15:55:17 +00:00
61f217c610 2005-01-17 jrandom
* Added meaningful support for adjusting the preferred message size in the
      streaming lib by setting the i2p.streaming.maxMessageSize=32768 (or
      whatever).  The other side will mimic a reduction (but never an increase).
    * Always make sure to use distinct ConnectionOption objects for each
      connection (duh)
    * Reduced the default ACK delay to 500ms on in the streaming lib
    * Only shrink the streaming window once per window
    * Don't bundle a new jetty.xml with updates
    * Catch another local routerInfo corruption issue on startup.
2005-01-17 08:15:00 +00:00
ccb1f491c7 use the first 16 bytes of the SHA256 for the columns & verification block, rather than all 32 bytes.
(AES won't let us go smaller.  oh well)
2005-01-16 06:07:06 +00:00
49fdac9b4e added ttp.i2p 2005-01-16 04:45:36 +00:00
6b6a9490f6 include blurb explaining tunnelIDs and replay prevention (thanks Connelly!) 2005-01-16 00:08:14 +00:00
2c783e9876 2005-01-15 cervantes
* Added support to the eepproxy for URLs such as
      http://localhost:4444/eepproxy/foo.i2p/bar/baz or even
      http://localhost:4444/eepproxy/foo.i2p/?i2paddresshelper=base64
2005-01-15 23:16:12 +00:00
ecd971c0e5 2005-01-15 jrandom
* Caught a series of (previously unhandled) errors caused by requeueing
      messages that had timed out on the TCP transport (thanks mae^!)
    * Reduce the barrier to dropping session tags on streaming lib resends -
      every fourth send should drop the tags, forcing ElGamal encryption.  This
      will help speed up the recovery after a disconnect, rather than the drop
      every fifth send.
2005-01-15 21:03:14 +00:00
c48875a6fb cbc, nimwit 2005-01-15 06:43:35 +00:00
a245ccb8b7 added freenet.eco.i2p, tracker.i2p, photo.i2p 2005-01-15 05:52:09 +00:00
75a18debcb forgot to update the processing xor 2005-01-15 03:53:13 +00:00
1a15d3bb55 filled in the tunnel building alternatives, throttling techniques, and mixing (meta)details 2005-01-15 00:06:40 +00:00
ffdcae47e3 add some whitening to the IV as it goes down the path 2005-01-14 22:43:43 +00:00
34a2bc8590 added hopekiller.i2p, microsoft.i2p, jhor.i2p, badtoys.i2p 2005-01-13 19:36:03 +00:00
8ae4d00ccb added mindspore.i2p 2005-01-13 19:31:06 +00:00
9ed6d5e7fb added irc.ircbnc.i2p (connect to it as an irc client, pass 'testpass' (may be changed/removed), and outproxy to irc servers) 2005-01-13 19:23:06 +00:00
c9243b241c added dvdr-core.i2p 2005-01-13 17:11:17 +00:00
9c364a64e3 more arm waiving wrt the tunnel building 2005-01-13 00:57:36 +00:00
b34306205c lets just get some visual versioning clues 2005-01-12 19:22:40 +00:00
77f778dbf9 Updated the crypto so that peer0 is the gateway (meaning max hop length is 8, not 9).
This prevents the first peer after the gateway from looking at the encrypted data received
and seeing "hey, none of the checksum blocks match the payload, they must be the gateway".
2005-01-12 19:09:00 +00:00
23fa4e4161 Made userhosts.txt the default master addressbook, and hosts.txt the default router addressbook (mostly just testing if this will commit properly) 2005-01-12 04:26:33 +00:00
5b6fd0b829 dont wannit 2005-01-12 03:49:05 +00:00
8fa8d7739f work in progress, but i want it in cvs so i dont lose it again 2005-01-09 23:01:34 +00:00
dc552c7a29 html fix (just to clarify that K[i] isn't actually *transmitted*) 2005-01-07 23:15:38 +00:00
cf84f453d3 Initial implementation of the new tunnel encryption code. Still much more work to be
done (e.g. *what* gets encrypted, modifying the tunnelCreate messages, the tunnel
building process, and the new tunnel pooling).  I seem to have lost much of the typed
up docs describing this too, so I'll be hitting that next.
2005-01-07 22:55:30 +00:00
daf32a24bc * 2005-01-06 0.4.2.6 released
2005-01-06  jrandom
    * Added a startup message to the addressbook, printing its version number
      to stdout (which is sent to wrapper.config) when it loads.
    * Updated the addressbook to reread the config file periodically
    * Added orion.i2p to the list of eepsites on the default homepage
2005-01-06 20:59:13 +00:00
34ecfd9857 added j.i2p 2005-01-06 11:56:35 +00:00
4838564460 2005-01-05 jrandom
* Handle unexpected network read errors more carefully (thanks parg!)
    * Added more methods to partially compare (DataHelper) and display
      arrays (Base64.encode).
    * Exposed the AES encryptBlock/decryptBlock on the context.aes()
    * Be more generous on the throttle when just starting up the router
    * Fix a missing scheduled event in the streaming lib (caused after reset)
    * Add a new DisconnectListener on the I2PSocketManager to allow
      notification of session destruction.
    * Make sure our own router identity is valid, and if it isn't, build a new
      one and restart the router.  Alternately, you can run the Router with
      the single command line argument "rebuild" and it will do the same.
2005-01-06 00:17:53 +00:00
3dd2f67ff3 added bl.i2p 2005-01-05 23:16:35 +00:00
eco
0ccec3dde0 Added log entry for bt1.eco.i2p and jap.eco.i2p removal. Fix typo in numbering of previous log entry. 2005-01-04 12:29:12 +00:00
eco
ad77879caa removed jap.eco.i2p and bt1.eco.i2p (obsolete) 2005-01-04 12:22:38 +00:00
27999983cc added chat.i2p 2005-01-03 02:36:32 +00:00
48b039940d added phonebooth.i2p 2005-01-01 05:18:01 +00:00
84dc7d9d82 2004-12-31 ragnarok
* Integrated latest addressbook changes (2.0.3) which include support for
      deploying as a .war file with no existing addressbook configuration.
    * Updated main build process to bundle the addressbook.war in the
      i2pinstall.jar and i2pupdate.zip.
2005-01-01 00:57:01 +00:00
70d6332bad 2004-12-31 jrandom
* Speling fxi (thanks digum!)
    * Bugfix for the I2PTunnel web interface so that it now properly launches
      newly added tunnels that are defined to be run on startup (thanks ugha!)
2004-12-31 17:18:05 +00:00
aec0b0c86a 2004-12-30 jrandom
* Revised the I2PTunnel client and httpclient connection establishment
      throttles.  There is now a pool of threads that build the I2PSocket
      connections with a default size of 5, configurable via the I2PTunnel
      client option 'i2ptunnel.numConnectionBuilders' (if set to 0, it will
      not throttle the number of concurrent builders, but will launch a thread
      per socket during establishment).  In addition, sockets accepted but
      not yet allocated to one of the connection builders will be destroyed
      after 30 seconds, configurable via 'i2ptunnel.maxWaitTime' (if set to
      0, it will wait indefinitely).
2004-12-30 22:51:16 +00:00
099f6a88c2 2004-12-29 jrandom
* Imported Ragnarok's addressbook source (2.0.2) which is built but not
      deployed in the i2pinstall.jar/i2pupdate.zip (yet).
    * Don't treat connection inactivity closure as a connection error.
2004-12-29 22:16:42 +00:00
00a5d42d3d forgot to import these... 2004-12-29 20:51:43 +00:00
28f4a2cb67 imported ragnarok's MIT licensed addressbook-2.0.2 2004-12-29 20:28:20 +00:00
1ac18ba10e 2004-12-29 jrandom
* Add in a new keepalive event on each TCP connection, proactively sending
      a (tiny) time message every minute or two, as well as killing the
      connection if no message has been fully sent within 5 minutes or so.
      This should help deal with hung connections from IP address changes.
2004-12-29 20:06:43 +00:00
1503ee2dfa 2004-12-28 jrandom
* Cleaned up the resending and choking algorithm in the streaming lib.
    * Removed the read timeout override for I2PTunnel's httpclient, allowing
      it to use the default for the streaming lib.
    * Revised ack triggers in the streaming lib.
    * Logging.
2004-12-29 15:53:28 +00:00
484b528d4f * 2004-12-21 0.4.2.5 released
2004-12-21  jrandom
    * Track a new stat for expired client leases (client.leaseSetExpired).
2004-12-21 18:23:03 +00:00
758293dc02 2004-12-21 jrandom
* Cleaned up the postinstall/startup scripts a bit more to handle winME,
      and added windows info to the headless docs. (thanks ardvark!)
    * Fixed a harmless (yet NPE inspiring) race during the final shutdown of
      a stream (thanks frosk!)
    * Add a pair of new stats for monitoring tunnel participation -
      tunnel.participatingBytesProcessed (total # bytes transferred) and
      tunnel.participatingBytesProcessedActive (total # bytes transferred for
      tunnels whose byte count exceed the 10m average).  This should help
      further monitor congestion issues.
    * Made the NamingService factory property public (thanks susi!)
2004-12-21 16:32:49 +00:00
6cb316b33e 2004-12-20 jrandom
* No longer do a blocking DNS lookup within the jobqueue (thanks mule!)
    * Set a 60s dns cache TTL, instead of 0s.  Most users who used to use
      dyndns/etc now just use IP autodetection, so the old "we need ttl=0"
      reasoning is gone.
2004-12-20 05:14:56 +00:00
1d31831e7d added up.i2p 2004-12-20 02:40:45 +00:00
ee32b07995 2004-12-19 jrandom
* Fix for a race on startup wrt the new stats (thanks susi!)
2004-12-19 18:55:09 +00:00
81f04ca692 2004-12-19 jrandom
* Added three new stats - router.activePeers, router.fastPeers, and
      router.highCapacityPeers, updated every minute
2004-12-19 16:27:10 +00:00
1756997608 2004-12-19 jrandom
* Added a new i2ptunnel type: 'httpserver', allowing you to specify what
      hostname should be sent to the webserver.  By default, new installs will
      have an httpserver pointing at their jetty instance with the spoofed
      name 'mysite.i2p' (editable on the /i2ptunnel/edit.jsp page).
2004-12-19 11:04:56 +00:00
ec11ea4ca7 * Convert native jcpuid code from C++ to C. This should alleviate build
problems experienced by some users.
2004-12-19 06:25:27 +00:00
a1ebf85e1b added dm.i2p 2004-12-18 06:31:22 +00:00
4b2a734cda * 2004-12-18 0.4.2.4 released 2004-12-18 04:07:13 +00:00
97ae8f78a0 added piespy.i2p 2004-12-18 03:11:03 +00:00
834665c3ba 2004-12-16 jrandom
* Catch another oddball case for a reset connection in the streaming lib.
    * Add a dumpprofile.jsp page, called with ?peer=base64OfPeerHash, which
      dumps the current state of that peer's profile.  Instead of the full
      base64, you can pass in however many characters you have and it will
      return the first match found.
2004-12-16 10:32:26 +00:00
d969dd2d8d 2004-12-16 jrandom
* Catch another oddball case for a reset connection in the streaming lib.
    * Add a dumpprofile.jsp page, called with ?peer=base64OfPeerHash, which
      dumps the current state of that peer's profile.  Instead of the full
      base64, you can pass in however many characters you have and it will
      return the first match found.
2004-12-16 10:21:23 +00:00
3cb727561c uugly stat dumper. call via /dumpstats.jsp?peer=routerIdentHash 2004-12-16 09:45:31 +00:00
cbc89376d3 2004-12-16 jrandom
* Remove the randomized factor in the tunnel rejection by bandwidth -
      we now accept the request if we've allocated less than our limit
      and reject it if we've allocated more.
    * Stick to the standard capacity scale on tunnel rejection, even for
      the 10m period.
    * Build the time message at the very last possible moment
2004-12-16 05:42:03 +00:00
66aa29e3d4 2004-12-15 jrandom
* Handle hard disconnects more gracefully within the streaming lib, and
      log unmonitored events more aggressively.
    * If we drop a peer after connection due to clock skew, log it to the
      /logs.jsp#connectionlogs with relevent info.  In addition, toss it in
      the stat 'tcp.disconnectAfterSkew'.
    * Fixed the formatting in the skew display
    * Added an ERROR message that is fired once after we run out of
      routerInfo files (thanks susi!)
    * Set the connect timeout equal to the streaming lib's disconnect timeout
      if not already specified (the I2PTunnel httpclient already enforces a
      60s connect timeout)
    * Fix for another connection startup problem in the streaming lib.
    * Fix for a stupid error in the probabalistic drop (rand <= P, not > P)
    * Adjust the capacity calculations so that tunnel failures alone in the
      last 10m will not trigger a 0 capacity rank.
2004-12-16 02:45:55 +00:00
5c72aca5ee added sciencebooks.i2p 2004-12-15 04:23:16 +00:00
8824815d6d 2004-12-14 jrandom
* Periodically send a message along all I2NP connections with the router's
      current time, allowing the receiving peer to determine that the clock
      has skewed too much, and hence, disconnect.  For backwards compatability
      reasons, this is being kludged into a DeliveryStatusMessage (ewww).  The
      next time we have a backwards compatability break, we can put in a proper
      message setup for it.
2004-12-14 16:42:35 +00:00
ad72e5cbdf 2004-12-14 jrandom
* Reenable the probabalistic drop on the TCP queues to deal with good old
      fashioned bandwidth limiting.  However, by default the probability is
      rigged to reserve 0% of the queue free - meaning we just aggressively
      fail messages in the queue if we're transferring too slowly.  That
      reservation factor can be increased with 'tcp.queueFreeFactor=0.25'
      (or whatever) and the drop code can be disabled with the parameter
      'tcp.dropProbabalistically=false'.
    * Still penalize a peer on tunnel failure, but don't immediately drop
      their capacity to 0.
    * More aggressively ACK duplicates
    * Randomize the timestamper period
    * Display the clock skew on the connection logs when a peer sends it.
    * Allow the timestamper to fix skews of up to 10 minutes
    * Logging
2004-12-14 12:14:41 +00:00
b2f183fc17 2004-12-14 jrandom
* Reenable the probabalistic drop on the TCP queues to deal with good old
      fashioned bandwidth limiting.  However, by default the probability is
      rigged to reserve 0% of the queue free - meaning we just aggressively
      fail messages in the queue if we're transferring too slowly.  That
      reservation factor can be increased with 'tcp.queueFreeFactor=0.25'
      (or whatever) and the drop code can be disabled with the parameter
      'tcp.dropProbabalistically=false'.
    * Still penalize a peer on tunnel failure, but don't immediately drop
      their capacity to 0.
    * More aggressively ACK duplicates
    * Randomize the timestamper period
    * Display the clock skew on the connection logs when a peer sends it.
    * Allow the timestamper to fix skews of up to 10 minutes
    * Logging
2004-12-14 11:54:39 +00:00
9e16bc203a 2004-12-13 jrandom
* Added some error checking on the new client send job (thanks duck!)
    * Implemented tunnel rejection based on bandwidth usage (rejecting tunnels
      proportional to the bytes allocated in existing tunnels vs the bytes
      allowed through the bandwidth limiter).
    * Enable a new configuration parameter for triggering a tunnel rebuild
      (tunnel.maxTunnelFailures), where that is the max allowed test failures
      before killing the tunnel (default 0).
    * Gather more data that we rank capacity by (now we monitor and balance the
      data from 10m/30m/60m/1d instead of just 10m/60m/1d).
    * Fix a truncation/type conversion problem on the long term capacity
      values (we were ignoring the daily stats outright)
2004-12-13 13:45:52 +00:00
83c6eac017 added forum.fr.i2p, fedo.i2p, and pastebin.i2p 2004-12-13 08:48:29 +00:00
d5b277a536 test to verify that a closed socket is propogated to the client 2004-12-13 01:18:24 +00:00
77ce6c33e3 2004-12-11 jrandom
* Fix the missing HTTP timeout, which was caused by the deferred syn used
      by default.  This, in turn, meant the I2PSocket creation doesn't fail
      on .connect, but is unable to transfer any data in any direction.  We now
      detect that condition for the I2PTunnelHTTPClient and throw up the right
      error page.
    * Logging
2004-12-11 09:26:23 +00:00
60f8d349cf 2004-12-11 jrandom
* Use a simpler and less memory intensive job for processing outbound
      client messages when the session is in mode=bestEffort.  We can
      immediately discard the data as soon as its sent the first time,
      rather than wait for an ack, since we will never internally resend.
    * Reduce some synchronization to avoid a rare deadlock
    * Replaced 'localhost' with 127.0.0.1 in the i2ptunnel config, and special
      case it within the tunnel controller.
    * Script cleanup for building jbigi/jcpuid
    * Logging
2004-12-11 07:05:12 +00:00
f539c3df70 added frosk.i2p 2004-12-11 05:08:14 +00:00
fe1cf1758c added theland.i2p 2004-12-11 04:16:10 +00:00
8c71c26487 added dox.i2p 2004-12-11 01:22:03 +00:00
2ce39d1fd4 added amiga.i2p 2004-12-10 22:37:29 +00:00
88a994b712 cleaned up paths 2004-12-10 13:12:07 +00:00
24c8cc1a0c reference the new gmp 4.1.4 2004-12-10 10:22:17 +00:00
caf684394c crypto unit test to allow cross-architecture testing of jbigi and the crypto code 2004-12-10 08:48:23 +00:00
4b74510450 added source instructions (thanks bens\!) 2004-12-10 00:34:53 +00:00
mpc
0ddcfc423e *** empty log message *** 2004-12-09 14:05:22 +00:00
b4ac56e204 added frooze.i2p 2004-12-09 08:27:45 +00:00
3b19ac3942 use the standard I2CP port (7654) not jrandom's local I2CP port (duh) 2004-12-09 00:29:29 +00:00
af52cad4ea * 2004-12-08 0.4.2.3 released 2004-12-08 21:08:10 +00:00
d88396c1e2 2004-12-08 jrandom
* Revised the buffering when reading from the SAM client and writing
      to the stream.  Also added a thread (sigh) so we don't block the
      SAM client from giving us more messages for abnormally long periods
      of time.
    * Display the router version in the logs on startup (oft requested)
    * Fix a race during the closing of a messageOutputStream
2004-12-08 17:16:16 +00:00
4c5f7b9451 aliased gott.i2p as jrandom.i2p (ed. note: no, i am not gott) 2004-12-08 07:45:09 +00:00
e601cedbb8 2004-12-06 jrandom
* Don't do a 'passive flush' while there are already outbound messages
      unacked.
    * Show the reseed link if up to 10 peers profiles are active (thanks
      dburton!)
2004-12-07 01:35:53 +00:00
fa12dc867f 2004-12-06 jrandom
* Don't do a 'passive flush' while there are already outbound messages
      unacked.
    * Show the reseed link if up to 10 peers profiles are active (thanks
      dburton!)
2004-12-07 01:09:16 +00:00
acfb6c4578 Added sonax.i2p 2004-12-06 22:13:41 +00:00
e52d637092 2004-12-06 jrandom
* Don't propogate streaming connection failures out to the SAM bridge as
      fatal errors.
    * Dont barf on repeated I2CP closure.
2004-12-06 05:03:57 +00:00
2fba055696 2004-12-05 jrandom
* Explicitly use "127.0.0.1" to bind the I2CP listener, not the JVM's
      getLocalhost call
2004-12-06 02:08:02 +00:00
88bb176f3b 2004-12-05 jrandom
* Default the I2CP listener to localhost only, unless overridden by
      i2cp.tcp.bindAllInterfaces=true (thanks dm!)
    * More SAM fixes for things recently broken (whee)
2004-12-06 00:54:07 +00:00
499eeb275b added 1.fcp.freenet.i2p and copied fcp.i2p to 2.fcp.freenet.i2p 2004-12-05 22:18:57 +00:00
61a8d679bb 2004-12-05 jrandom
* Fix the recently broken SAM bridge (duh)
    * Add a new pair of SAM apps - net.i2p.sam.client.SAMStreamSink and
      net.i2p.sam.client.SAMStreamSend, mirroring the streaming lib's
      StreamSink and StreamSend apps for transferring files.
    * Make the passive flush timer fire more frequently.
2004-12-05 15:32:32 +00:00
2bbde91625 2004-12-05 jrandom
* Fixed some links in the console (thanks ugha!) and the javadoc
      (thanks dinoman!)
    * Fix the stream's passive flush timer (oh, its supposed to work?)
2004-12-05 10:22:57 +00:00
9ce098ee06 added asciiwhite.i2p 2004-12-05 08:47:02 +00:00
927ae57d24 added fcp.i2p 2004-12-05 03:16:30 +00:00
mpc
d65c2d3539 added installation instructions 2004-12-05 01:57:12 +00:00
2d9d8f32dc 2004-12-03 jrandom
* Toss in a small pool of threads (3) to execute the events queued up with
      the SimpleTimer, as we do currently see the occational event
      notification spiking up to a second or so.
    * Implement a SAM client API in java, useful for event based streaming (or
      for testing the SAM bridge)
    * Added support to shut down the SAM bridge on OOM (useful if the SAM
      bridge is being run outside of the router).
    * Include the SAM test code in the sam.jar
    * Remove an irrelevent warning message from SAM, which was caused by
      perfectly normal operation due to a session being closed.
    * Removed some unnecessary synchronization in the streaming lib's
      PacketQueue
    * More quickly clean up the memory used by the streaming lib by
      immediately killing each packet's resend job as soon as it is ACKed (or
      cancelled), so that there are no longer any valid pointers to the
      (potentially 32KB) packet.
    * Fixed the timestamps dumped to stdout when debugging the PacketHandler.
    * Drop packets that would expand our inbound window beyond our maximum
      buffer size (default 32 messages)
    * Always read the ACK/NACK data from the verified packets received, even
      if we are going to drop them
    * Always adjust the window when there are messages ACKed, though do not
      change its size except as before.
    * Streamlined some synchronization in the router's I2CP handling
    * Streamlined some memory allocation in the SAM bridge
    * Default the streaming lib to disconnect on inactivity, rather than send
      an empty message.
2004-12-04 23:43:07 +00:00
1a30cd5f4a 2004-12-03 jrandom
* Toss in a small pool of threads (3) to execute the events queued up with
      the SimpleTimer, as we do currently see the occational event
      notification spiking up to a second or so.
    * Implement a SAM client API in java, useful for event based streaming (or
      for testing the SAM bridge)
    * Added support to shut down the SAM bridge on OOM (useful if the SAM
      bridge is being run outside of the router).
    * Include the SAM test code in the sam.jar
    * Remove an irrelevent warning message from SAM, which was caused by
      perfectly normal operation due to a session being closed.
    * Removed some unnecessary synchronization in the streaming lib's
      PacketQueue
    * More quickly clean up the memory used by the streaming lib by
      immediately killing each packet's resend job as soon as it is ACKed (or
      cancelled), so that there are no longer any valid pointers to the
      (potentially 32KB) packet.
    * Fixed the timestamps dumped to stdout when debugging the PacketHandler.
    * Drop packets that would expand our inbound window beyond our maximum
      buffer size (default 32 messages)
    * Always read the ACK/NACK data from the verified packets received, even
      if we are going to drop them
    * Always adjust the window when there are messages ACKed, though do not
      change its size except as before.
    * Streamlined some synchronization in the router's I2CP handling
    * Streamlined some memory allocation in the SAM bridge
    * Default the streaming lib to disconnect on inactivity, rather than send
      an empty message.
this still doesnt get the BT to where it needs to be, or fix the timeout problem,
but i dont like having so many commits outstanding and these updates are sound
2004-12-04 23:40:50 +00:00
f54687f398 added greenflog.i2p 2004-12-03 20:54:01 +00:00
mpc
9f4b4c5de1 Still trying to get this to compile under VS.NET 2004-12-03 05:59:25 +00:00
mpc
33bfa94229 Added session to naming callback 2004-12-02 23:05:30 +00:00
mpc
61e5f190a6 Updated examples 2004-12-02 22:54:22 +00:00
mpc
a4946272d0 get rid of stdint.h stuff because it confuses the microsoft compiler 2004-12-02 22:16:27 +00:00
8abd99d134 2004-12-01 jrandom
* Fix for a race in the streaming lib as caused by some odd SAM activity
2004-12-02 03:20:03 +00:00
97e8ab7c5b * 2004-12-01 0.4.2.2 released
2004-12-01  jrandom
    * Fixed a stupid typo that inadvertantly allowed persistent HTTP
      connections to work (thanks duck!)
    * Make sure we override the inactivity timeout too
2004-12-02 00:35:17 +00:00
cb930a7ab5 * 2004-12-01 0.4.2.2 released
2004-12-01  jrandom
    * Fixed a stupid typo that inadvertantly allowed persistent HTTP
      connections to work (thanks duck!)
    * Make sure we override the inactivity timeout too
2004-12-02 00:27:27 +00:00
610f1f7dd4 * 2004-12-01 0.4.2.1 released
2004-12-01  jrandom
    * Strip out any of the Accept-* HTTP header lines, and always make sure to
      include the forged User-agent header.
    * Adjust the default read timeout on the eepproxy to 60s, unless
      overridden.
    * Minor tweak on stream shutdown.
2004-12-01 22:31:55 +00:00
516d0b4db8 2004-11-30 jrandom
* Render the burst rate fields on /config.jsp properly (thanks ugha!)
    * Build in a simple timeout to flush data queued into the I2PSocket but
      not yet flushed.
    * Don't explicitly flush after each SAM stream write, but leave it up to
      the [nonblocking] passive flush.
    * Don't whine about 10-99 connection events occurring in a second
    * Don't wait for completion of packets that will not be ACKed (duh)
    * Adjust the congestion window, even if the packet was resent (duh)
    * Make sure to wake up any blocking read()'s when the MessageInputStream
      is close()ed (duh)
    * Never wait more than the disconnect timeout for a write to complete
2004-11-30 23:41:51 +00:00
df61ae5c6f duh. thanks clayboy :) 2004-11-30 00:10:20 +00:00
9f6584b55e 2004-11-29 jrandom
* Minor fixes to avoid unnecessary errors on shutdown (thanks susi!)
2004-11-29 23:24:49 +00:00
e4b41f5bb0 2004-11-29 jrandom
* Reduced contention for local client delivery
    * Drop the new code that munges the wrapper.config.  Instead, updates that
      need to change it will include their own wrapper.config in the
      i2pupdate.zip, overwriting the existing file.  If the file
      "wrapper.config.updated" is included, it is deleted at first opportunity
      and the router shut down, displaying a notice that the router must be
      started again cleanly to allow the changes to the wrapper.config to take
      effect.
    * Properly stop accept()ing I2PSocket connections if we close down the
      session (duh).
    * Make sure we cancel any outstanding Packets in flight when a connection
      is terminated (thanks susi!)
    * Split up the I2PTunnel closing a little further.
2004-11-29 22:27:39 +00:00
8d0cea93e9 2004-11-29 jrandom
* Reduced contention for local client delivery
    * Drop the new code that munges the wrapper.config.  Instead, updates that
      need to change it will include their own wrapper.config in the
      i2pupdate.zip, overwriting the existing file.  If the file
      "wrapper.config.updated" is included, it is deleted at first opportunity
      and the router shut down, displaying a notice that the router must be
      started again cleanly to allow the changes to the wrapper.config to take
      effect.
    * Properly stop accept()ing I2PSocket connections if we close down the
      session (duh).
    * Make sure we cancel any outstanding Packets in flight when a connection
      is terminated (thanks susi!)
    * Split up the I2PTunnel closing a little further.
2004-11-29 21:57:14 +00:00
d294d07919 added bdl.i2p 2004-11-29 20:55:38 +00:00
153eea2bd5 you mean i'm supposed to *test* it? 2004-11-29 03:35:39 +00:00
571e3c5c13 2004-11-28 jrandom
* Accept IP address detection changes with a 2-out-of-3 minimum.
    * As long as the router is up, keep retrying to bind the I2CP listener.
    * Decrease the java service wrapper ping frequency to once every 10
      minutes, rather than once every 5 seconds.
2004-11-29 02:09:27 +00:00
a2d268f3d6 2004-11-28 jrandom
* Accept IP address detection changes with a 2-out-of-3 minimum.
    * As long as the router is up, keep retrying to bind the I2CP listener.
    * Decrease the java service wrapper ping frequency to once every 10
      minutes, rather than once every 5 seconds.
2004-11-29 01:58:38 +00:00
02d456d7a0 added bacardi.i2p and guttersnipe.i2p 2004-11-28 22:47:01 +00:00
mpc
b3626ad86f should've tested it first 2004-11-28 05:11:39 +00:00
mpc
9b6eab451f partial raw handling 2004-11-28 05:10:29 +00:00
72be9b5f04 2004-11-27 jrandom
* Some cleanup and bugfixes for the IP address detection code where we
      only consider connections that have actually sent and received messages
      recently as active, rather than the mere presence of a TCP socket as
      activity.
2004-11-27 21:02:06 +00:00
35e94a7f65 added evil.i2p 2004-11-27 06:56:29 +00:00
8e02586cc9 2004-11-27 jrandom
* Removed the I2PTunnel inactivity timeout thread, since the new streaming
      lib can do that (without an additional per-connection thread).
    * Close the I2PTunnel forwarder threads more aggressively
2004-11-27 05:17:06 +00:00
0b5a640896 2004-11-27 jrandom
* Fix for a fast loop caused by a race in the new streaming library (thanks
      DrWoo, frontier, pwk_, and thetower!)
    * Minor updates to the SimpleTimer and Connection to help track down a
      high CPU usage problem (dumping debug info to stdout/wrapper.log if too
      many events/tasks fire in a second)
    * Minor fixes for races on client disconnects (causing NPEs)
2004-11-27 03:54:17 +00:00
64b5089909 * 2004-11-26 0.4.2 released
2004-11-26  jrandom
    * Enable the new streaming lib as the default.  That means, for any
      substantial definition, it is NOT BACKWARDS COMPATIBLE.
2004-11-26 15:20:14 +00:00
e0e09bfa45 (please wait until the release announcement before updating)
* 2004-11-26  0.4.2 released
2004-11-26  jrandom
    * Enable the new streaming lib as the default.  That means, for any
      substantial definition, it is NOT BACKWARDS COMPATIBLE.
2004-11-26 15:15:16 +00:00
8c7f9f2c65 added bdsm.i2p 2004-11-26 02:12:16 +00:00
aff5cea949 2004-11-25 jrandom
* Revised the installer to include start menu and desktop shortcuts for
      windows platforms, including pretty icons (thanks DrWoo!)
    * Allow clients specified in clients.config to have an explicit startup
      delay.
    * Update the default install to launch a browser pointing at the console
      whenever I2P starts up, rather than only the first time it starts up
      (configurable on /configservice.jsp, or in clients.config)
    * Bugfix to the clock skew checking code to monitor the delta between
      offsets, not the offset itself (duh)
    * Router console html update
    * New (and uuuuugly) code to verify that the wrapper.config contains
      the necessary classpath entries on update.  If it has to update the
      wrapper.config, it will stop the JVM and service completely, since the
      java service wrapper doesn't reread the wrapper.config on JVM restart -
      requiring the user to manually restart the service after an update.
    * Increase the TCP connection timeout to 30s (which is obscenely long)
2004-11-25 22:17:37 +00:00
8bd99f699f 2004-11-25 jrandom
* Revised the installer to include start menu and desktop shortcuts for
      windows platforms, including pretty icons (thanks DrWoo!)
    * Allow clients specified in clients.config to have an explicit startup
      delay.
    * Update the default install to launch a browser pointing at the console
      whenever I2P starts up, rather than only the first time it starts up
      (configurable on /configservice.jsp, or in clients.config)
    * Bugfix to the clock skew checking code to monitor the delta between
      offsets, not the offset itself (duh)
    * Router console html update
    * New (and uuuuugly) code to verify that the wrapper.config contains
      the necessary classpath entries on update.  If it has to update the
      wrapper.config, it will stop the JVM and service completely, since the
      java service wrapper doesn't reread the wrapper.config on JVM restart -
      requiring the user to manually restart the service after an update.
    * Increase the TCP connection timeout to 30s (which is obscenely long)
------------------------------------------------
2004-11-25 21:57:19 +00:00
b0513fff8a javadoc 2004-11-25 21:49:29 +00:00
f10db9d91a added eschaton.i2p 2004-11-23 03:58:46 +00:00
9b5fb17068 added blog.curiosity.i2p 2004-11-23 02:46:07 +00:00
608d713dca 2004-11-22 jrandom
* Update to the SAM bridge to reduce some unnecessary memory allocation.
    * New stat to keep track of slow jobs (ones that take more than a second
      to excute).  This is published in the netDb as jobQueue.jobRunSlow
2004-11-23 01:12:34 +00:00
6d5fc8ca21 2004-11-21 jrandom
* Update the I2PTunnel web interface to include an option for the new
      streaming lib (which is ignored until the 0.4.2 release).
    * Revised the I2PTunnel web interface to keep the I2CP options of client
      and httpclient tunnels in sync, as they all share the same I2CP session.
2004-11-22 17:57:16 +00:00
8c3145b70f 2004-11-21 jrandom
* Only allow small clock skews after the first 10 minutes of operation
      (to prevent later network lag bouncing us way off course - yes, we
      really need an NTP impl to balance out the network burps...)
    * Revamp the I2PTunnel web interface startup process so that everything
      is shown immediately, so that different pieces hanging don't hang
      the rest, and other minor bugfixes.
    * Take note of SAM startup error (in case you're already running a SAM
      bridge...)
    * Increase the bandwidth limiter burst values available to 10-60s (or
      whatever is placed in /configadvanced.jsp, of course)
2004-11-21 23:23:42 +00:00
12a6f3e938 2004-11-21 jrandom
* Only allow small clock skews after the first 10 minutes of operation
      (to prevent later network lag bouncing us way off course - yes, we
      really need an NTP impl to balance out the network burps...)
    * Revamp the I2PTunnel web interface startup process so that everything
      is shown immediately, so that different pieces hanging don't hang
      the rest, and other minor bugfixes.
    * Take note of SAM startup error (in case you're already running a SAM
      bridge...)
    * Increase the bandwidth limiter burst values available to 10-60s (or
      whatever is placed in /configadvanced.jsp, of course)
2004-11-21 22:31:33 +00:00
2c59435762 2004-11-21 jrandom
* Allow end of line comments in the hosts.txt and other config files,
      using '#' to begin the comments (thanks susi!)
    * Add support to I2PTunnel's 'client' feature for picking between multiple
      target destinations (e.g. 'client 6668 irc.duck.i2p,irc.baffled.i2p')
    * Add a quick link on the left hand nav to reseed if there aren't enough
      known peers, as well as link to the config page if there are no active
      peers.  Revised config page accordingly.
2004-11-21 19:42:57 +00:00
7336bf5c55 oops, forgot to commit this one. 2004-11-21 06:13:30 +00:00
2b21b97277 * make sure we send the close message even if the I2PSocket is closed directly (rather than the outputStream)
* only fail the session tags as a last resort for the last resend, rather than on every resend after the 1st
2004-11-21 04:11:35 +00:00
603bc99a2f 2004-11-21 jrandom
* Destroy ElGamal/AES+SessionTag keys after 15 minutes of inactivity
      rather that every 15 minutes, and increase the warning period in which
      we refresh tags from 30s to 2 minutes.
    * Bugfix for a rare problem closing an I2PTunnel stream where we'd fail
      to close the I2PSocket (leaving it to timeout).
2004-11-21 04:08:13 +00:00
426ede1c99 * handle con b0rkage more gracefully
* close the session on socket manager destroy (the old lib does, and SAM wants it to)
2004-11-20 15:52:08 +00:00
21506c1d1b handle poorly formatted packets more gracefully (in case someone uses the wrong streaming lib *cough*) 2004-11-20 02:40:57 +00:00
0b48b18e7e 2004-11-19 jrandom
* Off-by-one fix to the tunnel pool management code, along side some
      explicit initialization.  This can affect clients whose lengths are
      shorter than the router's default (thanks duck!)
2004-11-19 23:04:27 +00:00
mpc
c8f6d9c7a1 improved logging 2004-11-19 02:17:20 +00:00
ed8eced9dd * stats
if you want to get this data and you're running outside the router, just toss on a
"-Dstat.logFilters=* -Dstat.logFile=proxy.stats" to the java command line.  the resulting
file can be parsed w/ java -cp lib/i2p.jar net.i2p.stat.StatLogSplitter proxy.stats, then fed
into gnuplot or whatever
2004-11-19 00:00:05 +00:00
4a029b7853 * if we timeout connecting or otherwise need to cancel tags that we've sent, go one step
further and cancel all of the tags we're using for that peer so that we can react to their
  potential restart / tag loss quicker.
* use the minimum resend delay as the base to be exponentiated if our RTT is too low
  (so we resend less)
* dont be such a wuss when flushing a closed stream
2004-11-18 19:42:11 +00:00
6bd9e58ece * fix a reordering bug that can trim the end of a stream under heavy lag (thanks duck!)
* fix a pair of races on router crash
2004-11-18 13:47:27 +00:00
cd075fc8a6 2004-11-17 jrandom
* Fix to propogate i2psocket options into the SAM bridge correctly (thanks
      Ragnarok!)
2004-11-17 19:42:53 +00:00
e733427920 2004-11-17 jrandom
* Minor logging update.
2004-11-17 18:34:25 +00:00
107da0ae22 i suppose i should commit this, 'eh? (thanks mule) 2004-11-17 03:43:04 +00:00
mpc
3629d7a32c *** empty log message *** 2004-11-17 03:42:00 +00:00
71e1152cde if we've already sent our close packet but we still want to send something, send an ack packet 2004-11-17 00:57:33 +00:00
d01ab7fd23 *cough* (lets not have everyone think they're the resend with a packet in the air...) 2004-11-16 22:47:16 +00:00
f46d0a720c * fix up the propogation of client options to the streaming lib
* add new back-off logic to reduce payload resends during transient
  lag - only let one packet be resent at a time, even if the window size
  allows it (and the packet timers request it).  this should make
  congestion less painful, and reduce the overall number of messages
  resent (as the SACKs for the one packet actively resent should clarify
  what made it through)
2004-11-16 22:15:16 +00:00
d943b4993a 2004-11-16 jrandom
* Clean up the propogation of i2psocket options so that various streaming
      libs can honor them more precisely
2004-11-16 22:11:11 +00:00
4a4f57d6ac 2004-11-16 jrandom
* Minor logging update
(toss net.i2p.router.JobQueueRunner=WARN in /configlogging.jsp to see wtf is hanging your router)
2004-11-16 13:45:40 +00:00
085da16268 run multiple connections 2004-11-15 14:40:08 +00:00
306f6b0037 various tweaks to make sure we release appropriate references ASAP 2004-11-15 14:37:56 +00:00
3780d290fa 2004-11-14 jrandom
* Fix a long standing leak in I2PTunnel (hanging on to i2psocket objects)
    * Fix a leak injected into the SimpleTimer
    * Fix a race condition in the tunnel message handling
2004-11-15 14:35:16 +00:00
ad7dc66f90 2004-11-13 jrandom
* Added throttles on how many I2PTunnel client connections we open at once
    * Replaced some buffered streams in I2PTunnel with unbuffered streams, as
      the streaming library used should take care of any buffering.
    * Added a cache for some objects used in I2PTunnel, especially useful when
      there are many short lived connections.
    * Trimmed the SimpleTimer's processing a bit
2004-11-13 09:59:37 +00:00
258244fed8 * ack packets with a payload, even if they have ID=0 (duh)
* properly implement the connection timeout
* make sure we clear the outbound packets on close
* don't b0rk on repeated close() calls
2004-11-13 09:49:31 +00:00
5f7982540f 2004-11-13 jrandom
* Added throttles on how many I2PTunnel client connections we open at once
    * Replaced some buffered streams in I2PTunnel with unbuffered streams, as
      the streaming library used should take care of any buffering.
    * Added a cache for some objects used in I2PTunnel, especially useful when
      there are many short lived connections.
    * Trimmed the SimpleTimer's processing a bit
2004-11-13 09:43:35 +00:00
b1c0de4b77 (oops forgot to commit before passing out) 2004-11-12 06:07:33 +00:00
45b3fecfff allow throttles on the number of streams participated in, as well as how a timeout + queue for overflow 2004-11-11 21:19:59 +00:00
9774ded4dd added slacker.i2p 2004-11-11 10:00:32 +00:00
7ec027854e update connection options specification
tweak around with the connection delay and timeout activity to test delayed vs. immediate syn
2004-11-11 09:41:25 +00:00
b457001b42 timeout gracefully even if the socket is stopped in odd places 2004-11-11 09:37:46 +00:00
6fc6866eb4 *cough* 2004-11-10 13:28:39 +00:00
299e5528bc * deal with nondeferred connections (block the mgr.connect(..) until either success or failure)
* allow loading the connection options from the env (or another Properties specified)
2004-11-10 12:55:06 +00:00
881524a5e4 2004-11-10 jrandom
* Allow loading the (mini)streaming connection options from the
      environment.
    * More defensive programming in the DSA implementation.
2004-11-10 12:33:01 +00:00
ffc405138d added pdforge.i2p and ses.i2p 2004-11-10 09:43:45 +00:00
f6ff74af16 oops, properly choke the congestion detection for resend 2004-11-09 13:33:11 +00:00
73a12d47de * you mean we should implement congestion *avoidance* too?
* ack properly on duplicates
* set the message input stream buffer large enough to fit the max window (duh)
2004-11-09 13:26:10 +00:00
83165df7e5 * delay the ack of a syn
* make sure we ack duplicate messages received (if we aren't already doing so)
* implement a choke on the local buffer, in case we receive data faster than its
  removed from the i2psocket's MessageInputStream (handle via packet drop and
  explicit congestion notification)
2004-11-09 11:00:04 +00:00
30074be5a5 logging 2004-11-09 05:54:39 +00:00
16715aa309 * synchronize around the buffer used in the packet queue (duh)
* dont increment # unacked packets artificially
* dont try to push data after closing
* cleanup the packet serialization
* logging
2004-11-08 21:40:25 +00:00
53f3802a81 dont keepalive if we're in the closing process (duh) 2004-11-08 16:49:23 +00:00
07626b5cc2 two new tests - inactivity (seeing how we stay alive) and timeout (contacting someone who doesnt exist) 2004-11-08 15:27:41 +00:00
9ea603caf2 * hang around for 5m (er, 2.5msl, i suppose) after connection closure no matter what, so we
can respond apropriately
* optional inactivty timer with three possible results:
  disconnect, send a (blank) message (to be ACKed), or do nothing.
2004-11-08 15:05:13 +00:00
18ab9b80d2 testStaggered - read what we can while writing randomly 2004-11-08 05:48:36 +00:00
71c1cb4e12 * min resend delay = 20s
* rework the messageInputStream to implement read(byte[], off, len), and fix some fencepost
  bugs in the byte retrieval
2004-11-08 05:42:57 +00:00
0c049f39d9 2004-11-08 jrandom
* Remove spurious flush calls from I2PTunnel, and work with the
      I2PSocket's output stream directly (as the various implementations
      do their own buffering).
    * Another pass at a long standing JobQueue bug - dramatically simplify
      the job management synchronization since we dont need to deal with
      high contention (unlike last year when we had dozens of queue runners
      going at once).
    * Logging
2004-11-08 05:40:20 +00:00
096b807c37 2004-11-08 jrandom
* Make the SAM bridge more resiliant to bad handshakes (thanks duck!)
2004-11-08 03:18:01 +00:00
mpc
9018af4765 Leave it up to the client application whether to poll or not 2004-11-07 05:06:36 +00:00
mpc
323f28e306 Now it works?? 2004-11-07 04:14:53 +00:00
mpc
e9dbd00f42 *** empty log message *** 2004-11-07 04:12:36 +00:00
98b5252a2d 0.4.1.4 2004-11-07 03:00:56 +00:00
8abf42023a removed fproxy2, added postman 2004-11-07 02:49:40 +00:00
mpc
b792238f3f added my ping everyone script 2004-11-07 02:45:40 +00:00
2486e5e75f on some resends, drop our session tags for the peer and revert to ElGamal, since
they may have restarted or otherwise lost the tags delivered to them.
2004-11-07 02:36:42 +00:00
5f113f1610 2004-11-06 jrandom
* Expose a drop down on the /configclients.jsp to enter the outbound
      tunnel depth.
    * Improved *hosts.txt loading
    * Explicitly override the JVM's timezone settings to use GMT so that
      any client applications which use timezones won't leak sensitive
      data (thanks gott!)
    * Bundle sam.jar in the update (thanks duck!)
2004-11-07 02:25:13 +00:00
592e9dc3ff * limit the resend delay to a max of 60s between resends
* reduce the max # resends to 5
* if the session.sendMessage fails, we're fucked, so kill the socket
2004-11-06 08:02:02 +00:00
314316cee0 2004-11-06 jrandom
* Fix for a long standing synchronization bug in the SDK that in rare
      instances can add a few seconds of lag.
2004-11-06 07:59:54 +00:00
9cf663063e added orion and protokol 2004-11-06 02:36:12 +00:00
9ea9210a4b 2004-11-05 jrandom
* Bugfixes and unit tests for the SAM bridge to handle quoted message
      parameters, verify proper operation after multiple session lifetimes,
      as well as some synchronization problems.
    * New properties method on the DataHelper class.
    * Address a race on fast disconnecting clients
2004-11-05 11:59:07 +00:00
2bf1a94608 added blog.polecat.i2p 2004-11-05 11:22:56 +00:00
7a0236ad29 2004-11-05 jrandom
* Bugfixes and unit tests for the SAM bridge to handle quoted message
      parameters, verify proper operation after multiple session lifetimes,
      as well as some synchronization problems.
    * New properties method on the DataHelper class.
    * Address a race on fast disconnecting clients
2004-11-05 10:53:40 +00:00
4341a0c198 added ciaran.i2p 2004-11-05 10:35:58 +00:00
8071612c12 added polecat.i2p 2004-11-05 09:35:49 +00:00
ea9cc3da04 added susi.i2p 2004-11-05 08:39:07 +00:00
0df588fffb blackdown workarounds 2004-11-04 11:57:26 +00:00
a622311dbc make the updater clean things (thanks nickster\!) 2004-11-04 08:31:11 +00:00
1c95ac2470 Dont need no authentication 2004-11-03 01:34:22 +00:00
6ef22166f9 2004-11-02 jrandom
* Fix for a long standing synchronization bug in the JobQueue (and added
      some kooky flags to make sure it stays dead)
    * Update the ministreaming lib to force mode=guaranteed if the default
      lib is used, and mode=best_effort for all other libs.
2004-11-02 11:57:07 +00:00
1107e50108 2004-11-02 jrandom
* Fixed up the configuration overrides for the streaming socket lib
      integration so that it properly honors env settings.
    * More memory usage streamlining (last major revamp for now, i promise)
2004-11-02 08:27:55 +00:00
c19355a7b2 2004-11-01 jrandom
* Increase the tunnel test timeout rapidly if our tunnels are failing.
    * Honor message expirations for some tunnel jobs that were prematurely
      expired.
    * Streamline memory usage with temporary object caches and more efficient
      serialization for SHA256 calculation, logging, and both I2CP and I2NP
      message handling.
    * Fix some situations where we forward messages too eagerly.  For a
      request at the tunnel endpoint, if the tunnel is inbound and the target
      is remote, honor the message by tunnel routing the data rather than
      sending it directly to the requested location.
2004-11-01 13:31:29 +00:00
65d415fade javadoc fixes 2004-10-30 23:58:50 +00:00
b37313d3f9 a chunk of streaming lib updates (cwin calc & timed win, pings influencing rtt, etc) 2004-10-30 23:46:01 +00:00
58fcbad20a (mmMMmm profiling)
2004-10-30  jrandom
    * Cache the temporary objects used in the AES encryption/decryption
      process so that AES doesn't require any memory allocation to process
      data.
    * Dramatically reduce memory usage within various crypto implementations
      by avoiding unnecessary (though simplifying) buffers.
    * If we specify some tags to be sent in an I2CP message explicitly, use
      only those, not those plus a new set (otherwise we aren't sure on ACK
      which set was delivered)
    * Allow configuration for the partial send timeout (how long before
      resending a message down a different tunnel in a lease).  This can be
      updated with the "router.clientPartialSendTimeout" router config prop.
    * Logging
2004-10-30 23:43:59 +00:00
b571f331ec removed duplicate beyond.i2p, added edge.i2p 2004-10-30 18:56:07 +00:00
mpc
2547d4b3e7 *** empty log message *** 2004-10-30 12:41:18 +00:00
892786bf0c 2004-10-29 jrandom
* Strip the Referer, Via, and From headers completely, rather than
      inserting a bogus value ("i2p").  This should help with the use of
      SnipSnap and Geeklog (thanks nickster and DrWoo!)
2004-10-30 02:40:52 +00:00
0c51f2b583 2004-10-27 jrandom
* Fix a strange race condition on i2cp client disconnect.
    * win98 startup fixes (thanks tester-1 and ardvark!)
    * include build scripts for the new streaming lib (which is NOT ready
      for use yet, but you can hack around with it)
(enjoy, duck)
2004-10-28 02:11:52 +00:00
d5607ca195 * updated output stream test to match new API
* new paired stream server and client helpers
2004-10-28 02:05:51 +00:00
48cdf17a4f * revamped locking to block on flush and close until all of the
packets through that point have been ACKed, throwing an
  InterruptedIOException if there was a writeTimeout or an IOException
  if the con failed
* revamped the ack/nack field settings to ack as much as possible
* handle some strange timeout/resend errors on connection
* pass 1/2rtt as the packet 'optional delay' field, and use that to
  schedule the ack time (the 'last' messages in a window set the
  optional delay to 0, asking for immediate ack of all received)
* increase the optional delay to 2 bytes (#ms to delay)
* inject random failures and delays if configured to do so in
  PacketHandler.choke
* fix up the window size adjustment (increment on ack, /= 2 on resend)
* use the highest RTT in the new RTT calculation so that we fit more
  in (via SACK)
* fix up the SACK handling (duh)
* revise the resend time calculation
2004-10-28 02:03:38 +00:00
669a8fae15 iterate through the dict values
(thanks dinoman)
2004-10-27 04:07:00 +00:00
d592936873 * mark the input stream as closed after receiving the packet's data
* properly close the source file in StreamSinkSend
* always adjust the rtt on ack, not just for packets with 1 send
* handle dup SYN gracefully
* revamp the default connection options
* logging
2004-10-25 20:04:07 +00:00
87898dd2f1 added shiftfox.i2p 2004-10-25 06:15:47 +00:00
15c227f568 * sliding windows w/ additive increase / multiplicitive decrease
* immediately send an ack on receiving a duplicate payload message
  (unless we've sent one within the last RTT)
* only adjust the RTT when there have been no resends
* added some (disabled) throttles - randomly injecting delays on
  received packets, as well as randomly dropping them
* logging
2004-10-25 03:22:29 +00:00
8de41acfe1 * if we send a blank ACK message (that will not in turn be ACKed) and it
has session tags within it, send an additional ping to the peer,
  bundling those tags a second time, ACKing those tags on the pong.
* handle packets transferred during a race after the receiver ACKs the
  connection but before the establisher receives the ACK.
* notify the messageInputStream reader on close (duh)
* new stream sink test, shoving lots and lots of data down a stream
  with the existing StreamSinkServer and StreamSinkClient apps
* logging
2004-10-24 23:23:35 +00:00
9680effb9f 2004-10-24 jrandom
* Allow explicit inclusion of session tags in the SDK, enabling the
      resending of tags bundled with messages that would not otherwise
      be ACKed.
    * Don't force mode=guaranteed for end to end delivery - if mode=bestEffort
      no DeliveryStatusMessage will be bundled (and as such, client apps using
      it will need to do their own session tag ack/nack).
    * Handle client errors when notifying them of message availability.
    * New StreamSinkSend which sends a file to a destination and disconnects.
    * Update the I2PSocketManagerFactory to build the specific
      I2PSocketManager instance based on the "i2p.streaming.manager" property,
      containing the class name of the I2PSocketManager implementation to instantiate.
2004-10-24 23:00:44 +00:00
40df846e3f logging 2004-10-24 04:59:42 +00:00
eee94fbf84 * deal with writes > the packet size limit
* deal with window size > 1, especially before receiving the first ACK
* disable congestion control for the moment (aka unlimited window size)
2004-10-24 04:56:26 +00:00
813679ba25 2004-10-23 jrandom
* Minor ministreaming lib refactoring to simplify integration of the full
      streaming lib.
    * Minor bugfixes to data structure serialization.
2004-10-24 01:42:34 +00:00
2b9e16c9c9 very basic tests pass (ping, open then pause then close, open then echo back and forth a few times then close) 2004-10-24 00:59:29 +00:00
f9bb7f7cff added underground.i2p 2004-10-22 05:05:28 +00:00
41e9569094 renamed newsbytetest to newsbyte 2004-10-21 23:30:21 +00:00
336ee07191 added newsbytetest.i2p 2004-10-20 03:35:52 +00:00
81e0a145f1 foil evil typo plot 2004-10-18 23:37:49 +00:00
a95a968fa8 * 2004-10-18 0.4.1.3 released
2004-10-18  jrandom
    * Allow sending messages with a section of a byte array.
    * Reduced stats published.
2004-10-18 19:07:59 +00:00
6c08941d8b updated curiosity.i2p 2004-10-18 18:23:13 +00:00
e13a5b3865 added blueheron.i2p 2004-10-17 21:39:06 +00:00
9011d5604a 2004-10-17 jrandom
* Don't b0rk on whitespace in the router address.
2004-10-17 21:03:05 +00:00
78aa4ca137 updated files.i2p 2004-10-17 08:45:16 +00:00
93111842df clarify history - we reduce the capacity calc, not the 'isFailing'.
* More aggressively reduce the capacity of peers if their tunnels are
      failing so that we move off them quicker.
2004-10-17 04:00:21 +00:00
88693f8adc 2004-10-16 jrandom
* More aggressively fail peers if their tunnels are failing so that we
      move off them quicker.
    * Simplify some data structure serialization for reuse in the streaming
      lib, as well as add support for signing and verifying partial byte
      arrays.
    * Logging updates
2004-10-17 03:58:08 +00:00
f904b012e9 initial impl for the new streaming lib (saying this isn't done should be obvious, but the
packet spec is at a save point)
2004-10-17 03:47:03 +00:00
cebe0a151f added utansans.i2p 2004-10-16 21:45:55 +00:00
8fffad0891 2004-10-16 jrandom
* Increased the default minimum tunnel test time to 5 seconds, since we
      still see the occational message processing time spike to 2 seconds.
    * Update the SimpleTimer to allow rescheduling a task thats already
      queued (useful for the new streaming lib).
2004-10-16 18:00:47 +00:00
fb1263dad7 2004-10-15 jrandom
* Replaced old minimum tunnel test timeout of 1s with a configurable
      value (router.config property "router.tunnelTestMinimum", with the
      default of 2s).
2004-10-15 17:39:18 +00:00
28c5d6c10d 2004-10-14 jrandom
* Tunnel rejection is no longer a sign of an overwhelmingly loaded
      peer, so don't use it as a key point of the IsFailing calculator.
      We still use it as a key point of the Capacity calculator, however.
2004-10-15 01:20:12 +00:00
e7a6f6836e added irc.orz.i2p 2004-10-14 22:35:11 +00:00
f8ffe016d1 2004-10-14 jrandom
* Allow for a configurable tunnel "growth factor", rather than trying
      to achieve a steady state.  This will let us grow gradually when
      the router is needed more, rather than blindly accepting the request
      or arbitrarily choking it at an averaged value.  Configure this with
      "router.tunnelGrowthFactor" in the router.config (default "1.5").
    * Adjust the tunnel test timeouts dynamically - rather than the old
      flat 30s (!!!) timeout, we set the timeout to 2x the average tunnel
      test time (the deviation factor can be adjusted by setting
      "router.tunnelTestDeviation" to "3.0" or whatever).  This should help
      find the 'good' tunnels.
    * Added some crazy debugging to try and track down an intermittent hang.
2004-10-14 20:02:45 +00:00
ec322f0966 added nano.i2p 2004-10-13 20:42:35 +00:00
0674709fc6 added ragnarok.i2p 2004-10-13 20:33:19 +00:00
d91ac7ef21 2004-10-13 jrandom
* Fix the probabalistic tunnel reject (we always accepted everything,
      since the docs on java.util.Random.nextDouble() are wrong..)
    * Fixed a race on startup (thanks Quadn!)
2004-10-13 19:40:47 +00:00
2f0c3c7baf added marcos.i2p 2004-10-13 16:29:27 +00:00
be68407707 duh (oops) 2004-10-12 21:50:17 +00:00
f799a25aeb 2004-10-12 jrandom
* Disable the probabalistic drop by default (enable via the router config
      property "tcp.dropProbabalistically=true")
    * Disable the actual watchdog shutdown by default, but keep track of more
      variables and log a lot more when it occurs (enable via the router
      config property "watchdog.haltOnHang=true")
    * Implement some tunnel participation smoothing by refusing requests
      probabalistically as our participating tunnel count exceeds the previous
      hour's, or when the 10 minute average tunnel test time exceeds the 60
      minute average tunnel test time.  The probabilities in both cases are
      oldAverage / #current, so if you're suddenly flooded with 200 tunnels
      and you had previously only participated in 50, you'll have a 25% chance
      of accepting a subsequent request.
2004-10-12 21:29:41 +00:00
8329d045f1 confirm removal 2004-10-11 00:23:26 +00:00
503b289240 * 2004-10-10 0.4.1.2 released 2004-10-10 19:33:08 +00:00
35e3bbb862 2004-10-10 cervantes
* Update the I2PTunnel HTTP proxy to strip out the i2paddresshelper from
      the request.
2004-10-10 14:57:15 +00:00
8dc261da79 2004-10-09 jrandom
* Added a watchdog timer to do some baseline liveliness checking to help
      debug some odd errors.
    * Added a pair of summary stats for bandwidth usage, allowing easy export
      with the other stats ("bw.sendBps" and "bw.receiveBps")
    * Trimmed another memory allocation on message reception.
2004-10-10 00:03:25 +00:00
65676f8988 2004-10-08 jrandom
* Revamp the AESInputStream so it doesn't allocate any temporary objects
      during its operation.
2004-10-08 22:53:03 +00:00
730da3aa27 2004-10-08 jrandom
* Don't kill the establisher threads during a soft restart.
    * Attempt to validate the peer's routerInfo earlier during handshaking.
    * Revamp the AESOutputStream so it doesn't allocate any temporary objects
      during its operation.
2004-10-08 18:38:48 +00:00
ff8674bca9 2004-10-07 jrandom
* Reimplement the I2NP reading with less temporary memory allocation.
      There is still significant GC churn, especially under load, but this
      should help.
    * Catch some oddball errors in the transport (message timeout while
      establishing).
2004-10-08 02:08:10 +00:00
c7cfef3b61 2004-10-07 jrandom
* Expire queued messages even when the writer is blocked.
    * Reimplement most of the I2NP writing with less temporary memory
      allocations (I2NP reading still gobbles memory).
2004-10-07 19:19:51 +00:00
32188b1cc0 expose some direct byte formatting methods
allow SHA256 to be run against a partial array
append to the stats.log instead of overwriting it
2004-10-07 16:48:46 +00:00
37479d8c0d logging 2004-10-07 16:45:11 +00:00
f5c7d6576d no need to double b0rk 2004-10-07 16:42:55 +00:00
38c422bbc0 2004-10-06 jrandom
* Implement an active queue management scheme on the TCP transports,
      dropping messages probabalistically as the queue fills up.  The
      estimated queue capacity is determined by the rate at which messages
      have been sent to the peer (averaged at 1, 5, and 60m periods).  As
      we exceed 1/2 of the estimated capacity, we drop messages throughout
      the queue probabalistically with regards to their size.  This is based
      on RFC 2309's RED, with the minimum threshold set to 1/2 the
      estimated connection capacity.  We may want to consider using a send
      rate and queue size measured across all connections, to deal with our
      own local bandwidth saturation, but we'll try the per-con metrics first.
2004-10-06 21:03:51 +00:00
39d4e5ea81 list the shutdown time w/ the clock fudge factor included 2004-10-06 16:47:36 +00:00
4191ad1cbf 2004-10-06 jrandom
* Enable explicit disabling of the systray entirely for windows machines
      with strange configurations: add -Dsystray.disable=true to the java
      command line.  (thanks mihi!)
2004-10-06 13:23:38 +00:00
29287da37c 2004-10-05 jrandom
* Allow peers on the same LAN to communicate with each other safely even
      when they cannot talk to each other through the external address.
2004-10-06 01:12:03 +00:00
98c780415b 2004-10-05 jrandom
* Display how much time is left before the graceful shutdown is complete.
    * Debug some improperly failed messages on timeout or disconnection.
2004-10-05 19:21:47 +00:00
756af9c699 oops 2004-10-05 18:21:44 +00:00
7f9076bb1d updated beyond.i2p (after verification) 2004-10-05 16:04:16 +00:00
2404f1ab9a added b.i2p 2004-10-05 15:57:08 +00:00
64bcfd09ec 2004-10-05 jrandom
* Don't go into a fast busy if an I2PTunnel 'server' is explicitly killed
      (thanks mule!)
    * Handle some more error conditions regarding abruptly closing sockets
      (thanks Jonva!)
2004-10-05 15:38:37 +00:00
6251d22c6e added tinyurl.i2p 2004-10-05 15:26:20 +00:00
de1b4937a1 2004-10-04 jrandom
* Update the shitlist to reject a peer for an exponentially increasing
      period of time (with an upper bounds of an hour).
    * Various minor stat and debugging fixes
2004-10-04 17:30:22 +00:00
d092dd79ba get rid of the really really frequent temporary object creation 2004-10-04 13:53:10 +00:00
a3ba968386 24h time 2004-10-04 01:17:01 +00:00
5ca2b97128 24h time 2004-10-04 01:00:38 +00:00
c9daad1cfd added detonate.i2p 2004-10-04 00:16:13 +00:00
0526d5b53a cli to splot the stat log 2004-10-03 23:53:16 +00:00
34163fb8e4 dont overwrite index.html anymore (0.4.1.2 wont) 2004-10-03 21:06:17 +00:00
98d2d661a8 2004-10-03 jrandom
* Add a new stat logging component to optionally dump the raw stats to
      disk as they are generated, rather than rely upon the summarized data.
      By default, this is off, but the router property "stat.logFilters" can
      be set to a comma delimited list of stats (e.g. "client.sendAckTime")
      which will be written to the file "stats.log" (or whatever the property
      "stat.logFile" is set to).  This can also log profile related stats,
      such as "dbResponseTime" or "tunnelTestResponseTime".
2004-10-03 20:48:43 +00:00
d9f0a0fd74 dont list an explicit webdefault.xml (use the default) 2004-10-03 14:04:21 +00:00
d20d043e0f 2004-10-02 jrandom
* Assure that we quickly fail messages bound for shitlisted peers.
    * Address a race on startup where the first peer contacted could hang the
      router (thanks Romster!)
    * Only whine about an intermittent inability to query the time server once
2004-10-02 19:05:24 +00:00
ce186e1872 2004-10-02 jrandom
* Command line utility to verify a peer's reachability - simply run
      net.i2p.router.transport.tcp.ConnectionHandler hostname port# and it
      will print out whether that peer is reachable or not (using a simple
      verification handshake).
2004-10-02 12:31:15 +00:00
a14da92e1d * 2004-10-01 0.4.1.1 released 2004-10-01 17:23:00 +00:00
2b54d850ea logging 2004-10-01 17:19:37 +00:00
a63c1b19fc 2004-10-01 jrandom
* Handle partial reseeds, caused by seeds going away before the download
      completes (thanks Sugadude!)
2004-10-01 14:35:49 +00:00
34f74cd6ef 2004-10-01 jrandom
* Explicitly refuse IPv6 addresses, since only some peers support
      them and we want fully reachable peers.
2004-10-01 11:49:02 +00:00
c0b8e62135 2004-10-01 jrandom
* Additional error handling for a variety of transport layer errors.
2004-10-01 09:39:14 +00:00
ea24166b8e added identiguy.i2p 2004-10-01 09:32:46 +00:00
178b229d66 *cough* (oops) 2004-09-30 16:44:46 +00:00
276493da65 * 2004-09-30 0.4.1 released (not backwards compatible)
2004-09-30  jrandom
    * Bundle the configuration necessary to run an eepsite out of the box
      with Jetty - simply edit ./eepsite/docroot/index.html and give people
      the key listed on the I2PTunnel configuration page, and its up.
    * Router console cleanup, and some (off by default) tunnels -
      smtp.postman.i2p (port 7659), pop.postman.i2p (port 7660), and
      irc.baffled.i2p (port 7661)
2004-09-30 15:58:54 +00:00
e85dadfef2 added jabber-2.i2p 2004-09-30 13:25:31 +00:00
1c70efb350 added eepsite info 2004-09-30 13:23:01 +00:00
6804a0c564 * always flush the console output (duh)
* deal with some degenerate situations with identities changing and auto IP detection
* some catch-alls for cleaning up the registry on degenerate tunnels
* lots of logging
2004-09-30 13:10:02 +00:00
6eb7ecc2d4 2004-09-30 jrandom
* Bundle the configuration necessary to run an eepsite out of the box
      with Jetty - simply edit ./eepsite/docroot/index.html and give people
      the key listed on the I2PTunnel configuration page, and its up.
plus minor bugfixes / refactoring / logging
2004-09-30 06:57:22 +00:00
f4956b06b6 be more careful on startup 2004-09-30 06:51:28 +00:00
9a2f7c2660 include the timeouts due to inability to find the leaseSet 2004-09-30 00:51:35 +00:00
b6017c558a * updated stats:
- sendsPerFailure: how many partial sends we make when they all fail
- timeoutCongestionInbound: describes how much faster than our average speed we were receiving data when each partial send timed out (in Bps)
- timeoutCongestionMessage: our send processing time when each partial send timed out (in ms)
- timeoutCongestionTunnel: our tunnel test time when each partial send timed out (in ms)
- participatingMessagesProcessedActive: # of messages more than the (most recent) average that a tunnel we were participating in transmitted (for tunnels with more than the average)
* updated to use Writer for rendering the console, so we can do partial writes (and hopefully help debug some kooky threading bugs on kaffe)
2004-09-29 22:54:38 +00:00
62ed6c6a58 * updated stats:
- sendsPerFailure: how many partial sends we make when they all fail
- timeoutCongestionInbound: describes how much faster than our average speed we were receiving data when each partial send timed out (in Bps)
- timeoutCongestionMessage: our send processing time when each partial send timed out (in ms)
- timeoutCongestionTunnel: our tunnel test time when each partial send timed out (in ms)
- participatingMessagesProcessedActive: # of messages more than the (most recent) average that a tunnel we were participating in transmitted (for tunnels with more than the average)
* updated to use Writer for rendering the console, so we can do partial writes (and hopefully help debug some kooky threading bugs on kaffe)
2004-09-29 22:49:19 +00:00
24966c812f javad0c 2004-09-29 20:12:26 +00:00
ea8dc2e0af *nix daemon scripts are gone 2004-09-29 19:38:15 +00:00
010b285e67 2004-09-29 jrandom
* Always wipe the Jetty work directory on startup, so that web updates
      are reflected immediately (Jetty does not honor the cache across
      multiple executions)
in addition, refactor various file ops out of the DataHelper into FileUtil
2004-09-29 19:34:02 +00:00
774231f347 * started reducing the temporary buffers created within various crypto methods , as we've
got some pretty heavy GC churn when under load.  rough estimate is we allocate 5-8x as
much data as we need, copying it all over the place before forwarding it (or processing it).
this should cut down a few of those copies, but not enough yet.  it'd be great to get that
down to 2x.
* lots of logging
2004-09-28 20:33:23 +00:00
ff1dfd8f25 let the algorithm handle writing the output to various places in the output array (so we can cut down on temporary memory allocation) 2004-09-28 16:38:24 +00:00
2741ac195d * protocol doc & impl cleanup
* more defensive programming
* more javadoc updates
2004-09-28 08:34:48 +00:00
cf780e296e bugfixes for autodetection/update of IP address 2004-09-27 18:05:28 +00:00
0361246db0 2004-09-27 jrandom
* Limit the number of connection tags saved to 10,000.  This is a huge
      limit, but consumes no more than 1MB of RAM.  For now, we drop them
      randomly after reaching that size, forcing those dropped peers to use
      a full DH negotiation.
    * HTML cleanup in the console.
2004-09-27 07:57:43 +00:00
63355ecd5b more tcp transport updates (getting closer to the old functionality)
* avoid bad peers
* shitlist appropriately
* include the bandwidth limiter
* add socket timeouts
* deal with *cough* closing connections
* javadocs
2004-09-26 18:11:39 +00:00
0f54ba59fb die phttp die 2004-09-26 15:32:24 +00:00
b67b243ebd the following isn't the end of the 0.4.1 updates, as there are still more things left to clean
up and debug in the new tcp transport, but it all works, and i dont like having big changes
sitting on my local machine (and there's no real need for branching atm)
2004-09-26  jrandom
    * Complete rewrite of the TCP transport with IP autodetection and
      low CPU overhead reconnections.  More concise connectivity errors
      are listed on the /oldconsole.jsp as well.  The IP autodetection works
      by listening to the first person who tells you what your IP address is
      when you have not defined one yourself and you have no other TCP
      connections.
    * Update to the I2NP message format to add transparent verification at
      the I2NP level (beyond standard TCP verification).
    * Remove a potential weakness in our AESEngine's safeEncrypt and safeDecrypt
      implementation (rather than verifying with E(H(key)), we now verify with
      E(H(iv))).
    * The above changes are NOT BACKWARDS COMPATIBLE.
    * Removed all of the old unused PHTTP code.
    * Refactor various methods and clean up some javadoc.
2004-09-26 15:16:44 +00:00
4c29c20613 javadoc fix 2004-09-26 14:50:49 +00:00
mpc
4c2619d948 minor changes 2004-09-24 21:08:00 +00:00
ea5662a4a2 another minor semantic jikes warning 2004-09-23 01:21:33 +00:00
7c1ce777a1 cleanup per jikes' whining (overloaded var names, val overflow, etc) 2004-09-23 01:13:22 +00:00
3bb85f2d61 add some logical exit values (useful for scripting).thanks xolo! 2004-09-23 01:05:40 +00:00
93e36b3113 added jake.i2p 2004-09-23 00:08:51 +00:00
932fb670e3 added anonymnet.i2p sasquotch.i2p orz.i2p microbleu.i2p www/smtp/pop3.postman.i2p 2004-09-22 23:58:37 +00:00
54dce61a95 2004-09-21 jrandom
* Have two tiers of hosts.txt files - the standard "hosts.txt" and
      the new "userhosts.txt".  Updates to I2P will only overwrite the former,
      but values stored in the later take precedence.  Both are queried on
      lookup.
2004-09-22 00:10:26 +00:00
e686c0e0a2 added curiosity.i2p 2004-09-17 19:47:58 +00:00
05acf32f39 2004-09-16 jrandom
* Refactor the TCP transport to deal with changing identities gracefully,
      and to prevent some wasted effort by keeping track of what host+port
      combinations we are connected to (rather than just the identities).  Also
      catch a few configuration errors earlier.
    * Removed no longer relevent methods from the Transport API that were
      exposing ideas that probably shouldn't be exposed.
    * Removed the 0.4.0.1 specific files from i2pupdate.zip (relating to script
      updates)
2004-09-16 23:55:12 +00:00
mpc
67064012c9 added freeciv.nightblade.i2p 2004-09-16 20:50:26 +00:00
10e93c3b1b rename (wtf was i thinking "Calculator"...) 2004-09-16 02:04:40 +00:00
5b2ec1cbb5 moved udp transport to 0.4.4 instead of 1.1 2004-09-14 19:52:46 +00:00
972f701c5c ganttproject.sf.net based plan 2004-09-14 02:19:33 +00:00
51285efbc3 2004-09-13 jrandom
* Update for the SDK reconnection to deal with overflow.
    * Web improvements (@ not # on the /logs.jsp [thanks ugha!] and fixed the
      rounding on lifetime bandwidth used [thanks gott!]).
2004-09-13 03:08:16 +00:00
e2635705f9 added xolotl.i2p 2004-09-11 01:25:24 +00:00
7762107543 added modulus.i2p 2004-09-10 04:11:15 +00:00
9123ad89c8 added links to www.i2p and dev.i2p (thanks pseudonym) 2004-09-09 04:10:25 +00:00
39f3d6cc80 (release in the next hour or so)
2004-09-08  jrandom
    * Updated the "Active:" peer count to display the # of connections as well
      as the number of recently active router identities.
    * Implement some basic updating code - on startup, if there is a file named
      "i2pupdate.zip" in the I2P installation directory, extract it, delete it,
      then restart.
    * Added an ugly little script to allow launching the router on win9x
      machines without a dos box (using javaw to run a .bat file).
    * Logging updates.
    * Updated VERSION constants to 0.4.0.1
2004-09-09 02:26:42 +00:00
af5665f67c revert the last 2 changes 2004-09-08 22:25:54 +00:00
c2175cc692 corrected submission date 2004-09-08 22:15:43 +00:00
1ef371a467 reversing accidental reversion 2004-09-08 22:08:24 +00:00
58461ff5bb * Bugfix: Running the installer as a non-privileged user on Red Hat (and
hopefully any other affected *nix systems) now properly discards non-
   essential directories after installation.
* Support for Win9x in the installer and postinstall.bat.
* Changed the name of the default installation directory on all platforms
   from "I2P" to "i2p" in the installer.
* Changed "wrapper.conf" to "wrapper.config" for naming consistency with the
   other configuration files.
2004-09-08 22:04:13 +00:00
665959da90 added eepdot.i2p 2004-09-08 07:46:21 +00:00
8e63974f94 removed datagram_test.i2p 2004-09-08 07:45:11 +00:00
eae86f54ba Updates by cervantes:
* Proxy recursion disabled by default (strict)
    * Password Authentication for session commands
    * Support for http://path?i2paddresshelper=BASE64
    * Support for http://i2p/BASE64/path syntax
2004-09-08 07:25:09 +00:00
30128a122d added socks1.tor.i2p 2004-09-08 00:00:28 +00:00
56e22a39ac added sugadude.i2p 2004-09-07 22:30:53 +00:00
6ceb330baa 2004-09-07 jrandom
* Make sure that peers placed in the 'fast' group are ones we both know
      how to reach and have been able to reach recently.  These peers may
      still be placed in the 'high capacity' group however (though that group
      is only queried if the 'fast' group is too small)
    * Include some updates to the ProgileOrganizer's CLI.
2004-09-07 22:13:11 +00:00
f30509c7ba if i only had a brain... (thanks duck) 2004-09-07 20:54:44 +00:00
9489136bd6 fix the build (thanks duck) 2004-09-07 20:39:42 +00:00
05cd3d736b 2004-09-07 jrandom
* Disable the timestamper by default for all applications except the router
      (enable via -Dtime.disabled=false)
    * Simplify the retrieval of the full destination with text based browsers.
    * Bundle the updated wrapper.config and hosts.txt in the i2pupdate.tar.bz2
2004-09-07 09:49:02 +00:00
29b17772e5 dup entry 2004-09-07 07:23:49 +00:00
6151d63eac 2004-09-07 jrandom
* Write the native libraries to the current directory when they are loaded
      from a resource, and load them from that file on subsequent runs (in
      turn, we no longer *cough* delete the running libraries...)
    * Added support for a graceful restart.
    * Added new pseudo-shutdown hook specific to the router, allowing
      applications to request tasks to be run when the router shuts down.  We
      use this for integration with the service manager, since otherwise a
      graceful shutdown would cause a timeout, followed by a forced hard
      shutdown.
    * Handle a bug in the SimpleTimer with requeued tasks.
    * Made the capacity calculator a bit more dynamic by not outright ignoring
      the otherwise valid capacity data for a period with a single rejected
      tunnel (except for the 10 minute period).  In addition, peers with an
      equal capacity are ordered by speed rather than by their hashes.
    * Cleaned up the SimpleTimer, addressing some threading and synchronization
      issues.
    * When an I2PTunnel client or httpclient is explicitly closed, destroy the
      associated session (unless there are other clients using it), and deal
      with a closed session when starting a new I2PTunnel instance.
    * Refactoring and logging.
2004-09-07 07:17:02 +00:00
e57aa68854 added files.i2p 2004-09-07 00:38:19 +00:00
73fa6d9bd0 added beyond.i2p 2004-09-06 06:04:15 +00:00
da3c4b87c1 (oops, forgot to up the build # on the last round) 2004-09-06 05:21:26 +00:00
0eedc1b128 2004-09-06 jrandom
* Address a race condition in the key management code that would manifest
      itself as a corrupt router identity.
    * Properly clear old transport addresses from being displayed on the old
      console after soft restarts.
    * Properly refuse to load the client applications more than once in the
      same JVM.
    * Added support for a graceful restart (a graceful shutdown followed by a
      full JVM restart - useful for restarting client apps).
    * More defensive programming, HTML cleanup, logging
    * wrapper.config cleanup of duplicate lines
2004-09-06 05:20:40 +00:00
db339d40de 2004-09-04 jrandom
* Added some basic guards to prevent multiple instances from running.
       Specifically, a file "router.ping" in the install directory which is
       written to once a minute - if that file exists and has been modified
       within the last minute, refuse to start up.  In turn, adjust the
       service wrapper to wait a minute before restarting a crashed JVM.
     * Create a "work" directory in the I2P install dir which Jetty will
       use for all of its temporary files.
     * Tell the browser not to cache most of the router console's pages.
2004-09-04 21:54:08 +00:00
f72aa7884d added gott.i2p 2004-09-04 10:25:31 +00:00
1434f1bb40 added linuxagent.i2p 2004-09-04 10:01:58 +00:00
6bc92b26a7 2004-09-04 jrandom
* Update the SDK to automatically reconnect indefinitely with an
      exponential delay on retries (capped at 5 minutes).
2004-09-04 05:41:42 +00:00
63937d0fba 2004-09-03 jrandom
* Updated default wrapper.config to deal with the hard restart option
    * Include the history.txt in the /help.jsp page
    * HTML updates (wrapper.log, and no more unix scripts)
    * Updated VERSION constants to 0.4
2004-09-03 19:46:07 +00:00
7b86edaf7f 2004-09-03 hypercubus
* Bugfix: Installer launches postinstall.bat on WinNT/2K properly.
    * Temporarily removed install_i2p_service_unix and
      uninstall_i2p_service_unix from distribution packages.
    * postinstall.bat/postinstall.sh cleans installation directory of all files
      not applicable to the host OS.
2004-09-03 16:52:27 +00:00
49d4e565c6 2004-09-03 oOo
* Added some filters to the HTTP request, replacing the User-Agent,
      Referrer, Via, and From headers, which helps until we have a more
      comprehensive filtering system.
2004-09-03  jrandom
    * Disabled the old listener on port 7655.
2004-09-03 07:22:24 +00:00
44c54ecc16 2004-09-02 jrandom
* Cleaned up the base build.xml, adding a new target ("updater") which
      builds the file i2pupdate.tar.bz2 which can be safely extracted over
      existing installs.
2004-09-02 21:26:03 +00:00
6e543f825d new history file and RouterVersion.BUILD (displayed on the web console and in the published stats)
(see soon-to-be-posted email to the list about this stuff)
2004-09-02 20:00:28 +00:00
591800a28a *cough* 2004-09-02 08:36:26 +00:00
3340d74e3f * added www.i2p, dev.i2p, and cvs.i2p (pointing at www.i2p.net:80, dev.i2p.net:80, and cvs.i2p.net:2401, respectively)
* default the i2ptunnel listen interface to 127.0.0.1, not 0.0.0.0
* add a client tunnel listening on port 2401 pointing at cvs.i2p (but *not* started by default)
2004-09-02 08:32:05 +00:00
446d863106 use the new .jsp filenames (i'm not quite ready to drop support for this old admin console... yet) 2004-09-02 07:20:45 +00:00
8b2d27a916 the client tunnel settings are "tunnels.depthInbound", not "tunnel.depthInbound" 2004-09-02 07:07:33 +00:00
9b31f2257d file was renamed 2004-09-02 06:54:37 +00:00
252ec98e24 default to publish rankings & stats (disable via router.publishPeerRankings=false) 2004-09-02 05:41:30 +00:00
77dde5711b more aggressively delete the temp lib file 2004-09-02 05:40:25 +00:00
94e891880b keeping in pace with the wrapper logs 2004-09-02 05:27:04 +00:00
c414b3fad2 if the wrapper will write a wrapper.log in the current directory anyway, might as well use it there. 2004-09-02 05:22:12 +00:00
c0b63ee7a8 bournification 2004-09-02 05:02:53 +00:00
6fbbfbaa43 bourne-ified (damn borne agains) 2004-09-02 04:47:02 +00:00
4d663e4500 antialiasing & cleanup (thanks $anonDesigner!) 2004-09-02 02:26:13 +00:00
88ba2436c9 headless install instructions 2004-09-02 02:20:03 +00:00
bfda22ad57 * persist and honor the configuration option "visible" (default=true)
* every 30s check to make sure the icon is [not] visible (useful for broken OSes that do stupid things when you start systray4j before logging in)
* go to "/index.jsp" rather than just "/" (avoids a silly redirect)
2004-09-02 01:47:18 +00:00
b888f17672 within the installation, move readme.html to docs/readme.html 2004-09-02 01:22:33 +00:00
8aa07e6f12 synchronized on (and .wait/notify against) the ShellCommand, not the Thread.
in general the old way worked often, but would sometimes cause a race, this should be a bit safer
2004-09-01 23:08:35 +00:00
79e973af65 cleaned up a bit (thanks $anonDesigner!) 2004-09-01 22:13:01 +00:00
83c6fd43e5 * corrected: unix service install scripts erroneously pointed to 'i2psvc' instead of 'i2prouter'
* pid file naming fixed once and for all, using 'i2p.pid' ;-) ('i2prouter.pid' did not work)
2004-09-01 16:46:27 +00:00
1323d89912 last revision inadvertently launched the router console every time the router was started 2004-09-01 11:43:33 +00:00
6b89996b0b the return of Ignatious Toopie
(freshcoffee.i2p's logo is definitely cool, but imho brings too much of a "java" connotation.  perhaps we can use it on the "prerequisites: get java" page?)
2004-09-01 08:31:00 +00:00
8a9a60410c more PIDs (one for the JVM, one for the service)
the i2prouter script has some kooky naming dependencies, and the stop doesnt seem to work right, but its got pid files now... :)
2004-09-01 08:02:19 +00:00
09e8678369 pid file (not sure why the wrapper doesnt create one by default) 2004-09-01 07:53:03 +00:00
2ff7efadc2 put logs in the console buffer even if we're not sending anything to the console (since this console buffer is shown as the /logs.jsp) 2004-09-01 07:17:24 +00:00
5f3fcd2f37 some url cleanup (thanks nicktastic!) 2004-09-01 07:00:07 +00:00
7dffae4620 workaround a bug in libtool (which Kaffe uses) that b0rks on native libraries that aren't named "typically" for the OS (libjbigi.so, etc)
(sun, however, works fine with randomly named libraries)
2004-09-01 06:53:00 +00:00
ce2e7305d4 if we're doing ugly jsp taglets to filter by OS, might as well do it for both systray and service 2004-09-01 03:47:04 +00:00
4783b09f03 fixed bug in IE launch 2004-09-01 02:25:06 +00:00
15c089ca9c now opens browser to http://localhost:7657/ in all cases 2004-09-01 00:33:59 +00:00
e93a2ddd93 now directly reading the default browser from the registry on Windows 2004-08-31 23:25:47 +00:00
57b9c40609 need the jettylib to build the war 2004-08-31 22:38:00 +00:00
caaadd63ec cleaned up a little for the future revs 2004-08-31 22:26:19 +00:00
ca1b707fab note about how uninstalling the service kills the router... 2004-08-31 21:28:41 +00:00
a0499451a5 let windows users install/remove the service through the /configservice.jsp page 2004-08-31 21:25:23 +00:00
bbdf1a0b30 removing some annoying spaces on blank lines 2004-08-31 21:12:15 +00:00
bcd5230854 added 'port' preference for specification of the router console's listening port 2004-08-31 21:08:00 +00:00
5ec3d2a587 systray now reads router console port number from systray.config 2004-08-31 21:07:06 +00:00
9cb2be6ec4 erg, this should have been in the last commit... (help stuff, license stuff) 2004-08-31 03:31:18 +00:00
4e90ca0b26 * revamped (rewrote, htmlized) readme
* wrote basic license stuff
2004-08-31 03:29:20 +00:00
8c4c72c8b5 * in the multirouter (sim), give each router a randomly distributed clock skew (within the acceptable period)
* in the multirouter (sim), disable the timestamper (so the clock skew 'sticks')
* logging
2004-08-30 22:28:15 +00:00
8690d4d7a9 allow explicit overriding any logical constraints on the clock skew (useful for simulating strange things) 2004-08-30 22:23:24 +00:00
d5d9c9b483 get thee gone, extraneous comment! 2004-08-30 12:49:25 +00:00
0de8129457 * installer no longer hangs on Windows waiting for the spawned shell process to return
* shell process spawned by the installer on Windows will not create a visible command window
2004-08-30 12:28:08 +00:00
13f70ad42c systray now launches router console in config.jsp upon first launch after installation 2004-08-30 11:47:28 +00:00
49d7b568df * removed some failsafe code that had been preventing any messages from being sent down alternate tunnels in a leaseSet [oops]
this may have unintended consequences, as we need to deal with messages received from skewed clocks, but I believe the two pathways
used here are safe (we leave the larger timeout thresholds intact for dealing with remotely generated message times)
2004-08-30 08:29:06 +00:00
93afcd5c0c * removed unwanted systray window on *nix
* removed 'Shutdown I2P router' from systray menu; preferred method of shutdown is now router console
2004-08-30 01:08:13 +00:00
07ef3582f7 clarify the nextInt/nextLong boundaries (thanks oOo) 2004-08-29 22:42:21 +00:00
53c7ff14df code reuse++ (digest authentication works, nice find oOo!) 2004-08-29 22:35:55 +00:00
6b993688fc poke the systray (so that on startup, it may show the icon and/or launch the browser) 2004-08-29 21:06:15 +00:00
b9e667e155 if a netDb refetch of a lease we were able to fetch is going slowly, short circuit it by reinjecting the old (dropped) one after 10 seconds so we can attempt a resend 2004-08-29 20:56:24 +00:00
3cd26781b0 'The I2P Project' is redundant - just 'I2P', and switched version # 2004-08-29 20:34:13 +00:00
2d20ac6f29 removed unintentional duplicate copy of 'i2psvc' from 'lib/' 2004-08-29 20:15:31 +00:00
944d467654 changed deprecated 'tail -1' to 'tail -n 1'; the Java Service Wrapper team will probably change it soon, but we need this for 0.4 2004-08-29 17:59:10 +00:00
f68271c3d7 set mode_paranoid=true 2004-08-29 01:47:13 +00:00
4eb5070753 clear another possible thread leak 2004-08-27 23:52:13 +00:00
f57adc9cc4 don't fail the tunnels used to send/receive a request on a lost reply, as the potential that they're to blame is only 1 out of 5.
(and if other people's tunnels suck, that leads us to kill all of our tunnels.  which is Bad)
2004-08-27 20:56:00 +00:00
3e0b7bfeff cleaned up peer selection so we don't have to repeatedly ask the profileOrganizer the same thing over and over
(instead, have the profileOrganizer check the netDb to see if the profiled peer is reachable)
cleaned up the threshold calculation a bit more
2004-08-27 19:18:24 +00:00
faa78c67d8 router console opens automatically upon first launch (i.e. if router.config is not found) 2004-08-27 12:44:43 +00:00
a5ed02eb1c fixed some foolishness w/ booleans (thanks oOo!) 2004-08-27 02:04:49 +00:00
mpc
bfe11110aa Trying to get around CVS stupidity 2004-08-27 01:52:30 +00:00
mpc
d8b1d2382d Junking this for now.. use Stasher instead 2004-08-27 01:41:48 +00:00
mpc
7881a13610 moved some files around 2004-08-27 01:29:18 +00:00
mpc
aaa328950e invalid style error 2004-08-26 21:42:56 +00:00
d8eb1a0a4f more path woes solved, scripts launch properly from installer (yay!) 2004-08-26 19:08:25 +00:00
e3379b31cb only base the thresholds on peers who are not in recovery 2004-08-26 18:46:25 +00:00
f36ce3d245 drop the links to install/uninstall the service from this page. not worth the effort atm (they can just run the shell scripts) 2004-08-26 18:39:30 +00:00
c73f3385c0 don't ask the bandwidth limiter to authorize reading an EOF ;)
(this fixes the longstanding "-318 bytes read" bug)
2004-08-26 17:59:47 +00:00
2eb8b84bbd update to build.xml to reflect name change of 'postinstall' to 'postinstall.sh' 2004-08-26 17:25:05 +00:00
b31378ad1a local scripts are now made executable by postinstall.sh 2004-08-26 17:22:37 +00:00
53213bb553 fixed relative path problem so 'i2psvc' can be found 2004-08-26 14:48:10 +00:00
36b446c012 * never drop the threshold under the baseline (the peer selection algorithm can handle there being no fast peers, etc)
* revert some of the overly zealous peer distribution code - select randomly from the fast peers, not according to a strict LRU
(which was causing lots of queued up tunnel requests, as well as tunnel failures when they all failed)
need to think some more thunks about how to address this right now.  a few different algorithms available
to deal with different scenarios and #s of users, but nothing that by itself strikes me as 'ideal', yet.   perhaps its
time for another trip to the pub to see what inspiration can be found there ;)
2004-08-26 08:07:48 +00:00
ca70fc8dc8 load the body of the index.jsp from ./readme.html (so we can put in some intro crap w/ links and the user can change it later) 2004-08-26 08:00:16 +00:00
18ff889b56 crazy paranoia to deal with errors referencing bad dll/.so files for the service wrapper / systray / etc 2004-08-26 03:12:52 +00:00
fab3c0df3e take care of another scenario where a thread can leak 2004-08-26 03:08:19 +00:00
7e7f97d72a * add a new simplified version of java.util.Timer/TimedEvent
* removed all of the "temporary" threads used for adding timeouts to blocking socket operations:
 - use the ConnectionEstablisher's thread + a SimpleTimer.TimedEvent callback to timeout socket create
 - added a pool of socket handler threads (size=3 atm) for receiving any inbound sockets, which are
   pulled off a queue, after which a handshake occurs to verify the other side is I2NP (along side
   another SimpleTimer.TimedEvent callback in case that blocks)
this should get the last of the temporary threads (Jetty has its own thread pool for dealing with
HTTP requests, so we can ignore that thread created in the AdminRunner).  The only significant
reduction in threads left is to go with either NIO or UDP, but neither are happening in the immediate
future.
2004-08-25 20:17:46 +00:00
3a1fcf2865 oh, you mean we should actually stop waiting for something on a thread that has nothing left to do? what a concept!
(this should kill the leaking thread issue)
2004-08-25 19:49:07 +00:00
fd85416088 service wrapper is now fully integrated 2004-08-25 11:20:56 +00:00
18a6a9e965 arrggggh, 1.4-ism that kaffe supports (but sun-1.3 users havent complained about). (thanks frontier) 2004-08-25 06:47:23 +00:00
eed8d9c61b fixed dangerous comparison (== != .equals) [thanks mihi!] 2004-08-25 00:00:04 +00:00
mpc
cbe12adbe6 cervantes' web browser auto-proxy script 2004-08-24 20:58:31 +00:00
84f8931ddd oOo's timestamper fixes (wtf was i thinking with those web params? !thwap) 2004-08-24 19:59:54 +00:00
db135e502c renumber libs 2004-08-24 18:07:21 +00:00
6f205f8adf systray has its own singleton 2004-08-24 18:04:20 +00:00
e81c1df19f * helper to read the last few lines of a textfile
* use that to render the last few lines of the wrapper log on /logs.jsp (for the on demand stack trace)
* thread creation / finalization logging
* support a hard restart (stop immediately and restart the JVM) - useful for rerunning clients.config (etc)
* systray when not supported
2004-08-24 18:02:48 +00:00
71577c9b0e adding provisional support for i2p launching from installer 2004-08-24 13:01:42 +00:00
c88c245094 * moving wrapper scripts to installer/resources
* added wrapper files to the 'installer' target of the global build.xml
2004-08-24 12:25:46 +00:00
30ce04bc84 * added systray jars back to wrapper.conf
* moving wrapper.conf to installer/resources
2004-08-24 12:20:04 +00:00
205d8f7db2 initial commit of ServiceManager class; API is complete, so integration with routerconsole can proceed, but
return of error messages is not yet implemented, meaning returned exit values will all be null for now
2004-08-24 09:04:53 +00:00
d70c22d73f * systray now fully integrated into global ant build
* removed two unneeded test files i2psystray.bat, i2psystray.sh
2004-08-24 06:58:05 +00:00
920161bc07 * more code formatting
* forgot to make constructor private for singleton (d'oh!)
2004-08-24 03:32:17 +00:00
cdafefebd3 the shutdown detector is a daemon 2004-08-24 03:19:54 +00:00
f220300212 code cleanup 2004-08-24 02:37:01 +00:00
a2b86acc22 clear up a race where the timestamper might be fired when it shouldnt be 2004-08-24 00:46:31 +00:00
8e53028d78 * html cleanup
* classpath during javadoc
2004-08-23 21:55:57 +00:00
852dfa4abf removed timestamper 2004-08-23 21:50:27 +00:00
5d6845a58a added jbigi.jar, remoed systray jars 2004-08-23 21:49:39 +00:00
be846e69c5 * control the on_exit so we can shutdown gracefully (except OOMs, when we auto-restart)
* other minor wrapper details
2004-08-23 21:36:31 +00:00
eef8c06b39 new shutdown(exitCode) usage 2004-08-23 21:34:22 +00:00
54aa0fdb11 * add "dump threads"
* hook in to the service manager and let it know we're exiting gracefully (when appropriate)
* commented out but generally functional systray integration.  i cant get it to build sometimes though, something is b0rking up
2004-08-23 21:32:24 +00:00
3c62a5d2b4 * drop libsystray4j.so (for now)
* add some more targets (cleandep and distclean)
2004-08-23 21:28:25 +00:00
0fe70b660a expose as a singleton so we can .show() and .hide() 2004-08-23 21:27:23 +00:00
4f787ddb03 * moved non-source files out of the source tree
* added dependency on core to the build script (for ShellCommand)
2004-08-23 20:53:13 +00:00
be33752eb3 reformatting
(
remove
excess
newlines
)
(no logic changes)
2004-08-23 20:41:06 +00:00
a88dbbe5ba * added in the systray jars
* added loggerFilenameOverride
* removed --quiet parameter (it was bogus anyway ;)
2004-08-23 17:48:44 +00:00
9b4144b815 add support for using the '@' character in place of the rotation #, for situations where stupid config files treat '#' as the end of the line and beginning of a comment... 2004-08-23 17:47:46 +00:00
bce5b44275 standardized the spoof prevention:
- set the nonce and noncePrev for the handler when rendering the form
- include the current nonce in the hidden parameter "nonce"
- include an "action" parameter (so we know we want to execute something and hence, validate the nonce, rather than just display the page)
- if the nonce submitted doesnt match what is set in the nonce or noncePrev when validating, its invalid.  refuse to process
2004-08-23 17:11:38 +00:00
9f7320fa67 * new configservice.jsp page that shuts down the router (and has hooks for a few other things)
* new safer way of shutting down the router per discussions with oOo (dealing with a graceful
shutdown where the user updates their config before the shutdown is complete, etc)
* graceful shutdown implemented in the router - shutdownGracefully(), cancelGracefulShutdown(), shutdownInProgress()
2004-08-23 07:33:14 +00:00
e9310ee8dd updated lucky.i2p 2004-08-23 07:01:10 +00:00
be93db51f7 fixed a bug w/ mode=best_effort - we were returning false excessively 2004-08-23 05:57:24 +00:00
8e3e8ada32 * refactored and revamped the capacity threshold calculation to take
into account various skew situations and the capacity growth constant with
the intent of producing a higher quality threshold whenever possible
* increased the minimum # of fast peers from 4 to 8 (yay), which means we'll
try to have at least some peers to choose from
* added a new router config option - "router.maxParticipatingTunnels".  This is
useful for gracefully shutting down the router (aka set it to 0 and wait until
the router is no longer participating in tunnels, then shutdown).  You can
probably also come up with other situations where this is useful, but I don't
want to spoil all the fun ;)
2004-08-23 03:54:55 +00:00
4564a6e8fd added firerabbit.i2p 2004-08-23 02:21:30 +00:00
240190fa8f oOo's patch to get the console not looking toooo bad on IE, and clean up some ugliness on firefox/moz w/ larger fonts 2004-08-23 01:52:05 +00:00
4ca7c0d978 * moving ShellCommand class to the net.i2p.util package 2004-08-23 00:10:25 +00:00
190d0f9304 never ignore a (potential) tunnel failure, even though the tunnel may not have failed
(e.g. test outbound through A with a reply inbound through B.  if the message is lost, which tunnel failed?  both!)
2004-08-22 22:00:21 +00:00
b8d2a363fb added I2P website URL to header comments 2004-08-22 14:05:02 +00:00
2382785240 changed some filenames and updated credits in comment headers 2004-08-22 13:59:01 +00:00
476994595c adding the service wrapper install and uninstall scripts for *nix 2004-08-22 10:00:39 +00:00
287969f169 simple build script 2004-08-22 06:16:44 +00:00
ca93c52161 new CLI for creating signed NameReference files from a destination's private keystream
e.g. java -cp myi2p.jar;i2p.jar net.i2p.myi2p.address.CreateNameReferenceCLI eep.ref eepPriv.dat jrandom.i2p 1 eep
new CLI for importing / updating a signed NameReference into an address book
   e.g. java -cp myi2p.jar;i2p.jar net.i2p.myi2p.address.CreateEntryCLI addressbook.dat  C:\eep.ref jrandom.r00lz.i2p 0
logging / toString
2004-08-22 06:15:53 +00:00
b126b19e03 shut down the node on, er, shutdown 2004-08-22 06:12:10 +00:00
2c907060d4 forgot to add a SysTrayMenuListener for the new menu option 'Open router console' 2004-08-22 05:10:27 +00:00
e28c0d0b4a * removed systray support for KDE as it remains buggy; win32 is the only supported systray platform for now
* added an 'Open router console' option to the systray menu, for people who can't bring themselves to double-click the icon
2004-08-22 04:56:43 +00:00
918df735ed v0.0.0.0.0.1. it builds, and the core MyI2P framework is there, plus the skeleton for the MyI2P address book service
it doesn't do anything beyond compile and connect/disconnect, so no need to even bother looking at this.
but its a savepoint (iterative and incremental, 'eh?)
2004-08-22 03:44:00 +00:00
294936d137 added trailing '/' to router console url 2004-08-22 00:09:56 +00:00
115da03a23 * fixed another thread blocking problem
* made ConfigFile behave more appropriately with missing config files
* SysTray now behaves correctly when a file dialog has been canceled without any choices being made
2004-08-21 23:42:11 +00:00
cc085755aa oOo's patch to add support for temporary name<-->destination assignment (w00t!)
to use this, make a link going to http://some.name.i2p/some/path/blah?i2paddresshelper=base64Destination
e.g. http://whitehouse.gov.i2p/?i2paddresshelper=FpCkYW5pwVz36sSHoBuRT4ZvGif9QC9oQUWfz-wu4zEnJ1ewlAvinPuw3YnXUKmgLFZ0UY3wB7wqd0eQYiW2ZV1bwVhXvsyGV5ZehzxGaFu05IspCJjyaMIe90z5fyda4KgzyBwHKqwjGX57SMyn2cZhXbCKF6aNuipWxYOnL65uATDbw3jShEtL9v9299ohhGA3EcrYk3u86FgLmsOdi2GZruxy2RzioA-VKhaZl4RSJs6dFHPUYWgeLF3gT7Ciy-HbMZdDuiLTEX7mUlO0UZwnzT8mjUDeeYfyWtv9arwv-rAMeXxAVUYm7X2dDHN8TvmQCZ~LiQrnGmGReSIDKVT4u59xZX2Qg0GZf0fA5LRSW1zHLrlZWDJfNeESW57RlOkA9DDDOxwSVSw8LUQN-hPsoz~AgwA-vDklNWULqvp4lLifEJUlr5ZmnWrviLr6W6cHhdJBl89VzMThoknb5UibIvwTnu~tfA0rkyILXX07hdaoXF~prptuOhMtEcV2AAAA
this is better than the existing http://i2p/base64Destination/ since images at that page will now resolve
the name is resolved only if/when the hosts.txt doesn't have a matching entry (aka no spoofing duck.i2p),
but the name is *not* persisted to hosts.txt - if you restart your tunnel (or router, if they're in the same JVM), the address helper is lost
2004-08-21 22:39:27 +00:00
cb5e3efd8a don't force start the tunnels too early - only instantiate the tunnelControllerGroup when the CLI is run (aka after 2 minutes whem TunnelControllerGroup.main is called from clients.config) 2004-08-21 22:05:02 +00:00
274fd0b528 drop the package prefix from the search path - e.g. just load libjcpuid-x86-windows.so instead of freenet/support/CPUInformation/libjcpu-x86-windows.so 2004-08-21 18:05:36 +00:00
1431d1fecd toss the new precompiled & self detecting jbigi/jcpuid code into cvs and packaged in the new installer as lib/jbigi.jar 2004-08-21 18:03:45 +00:00
dc3d6bfc43 removing unused service wrapper libs 2004-08-21 11:13:57 +00:00
c9d4745a59 one less lib (duh) 2004-08-21 11:12:15 +00:00
921aef7f2c fixed a silly thread blocking problem 2004-08-21 11:01:12 +00:00
3c772f1974 backwards compatibility - first check for jbigi.dll / libjbigi.so, then do the new stuff ("jbigi" in a jar, followed by "net/i2p/util/libjbigi-linux-pentium4.so", etc) 2004-08-21 09:29:18 +00:00
bee9c7ee17 *cough* 2004-08-21 09:15:50 +00:00
75ca438f2f import Iakin's public domain mods plus some additional ones (support for the resource 'jbigi' anywhere in the classpath, java 1.3 support, docs) 2004-08-21 09:11:10 +00:00
9ea6eed22f added support for local build (saving as jbigi) 2004-08-21 09:04:24 +00:00
56f13c53ce bz2 2004-08-21 08:37:18 +00:00
fbc63c957a g++ 2.95.4 2004-08-21 08:21:24 +00:00
6052a9382b moved jbigi source into jbigi subdir (in parallel with other c native libs, like jcpuid) 2004-08-21 07:59:11 +00:00
f7f05cfc8b imported Iakin's public domain jcpuid library allowing the detection of CPU types from java
imported Iakin's modifications to jbigi to use the jcpuid library in detecting what jbigi implementation to load
imported and slightly updated Iakin's scripts to build jbigi for lots of architectures
(yay iakin!)
2004-08-21 07:56:53 +00:00
f4754d7481 * a missing systray.config file is now handled gracefully
* fixed javadoc warnings
2004-08-21 04:03:22 +00:00
7dc8d0cfec * systray now uses its own config file
* added config file handler class
* UrlHandler's browser launching behavior is now more sophisticated for Win32 using rundll
2004-08-21 01:43:35 +00:00
846c393168 giving systray client its own config file 2004-08-21 01:40:30 +00:00
78b7f228f5 * dropped httptunnel.jar and phttprelay.war from the dist (neither are used)
* automate the building of {lib,weblib,webapps}.tar.bz2 (even if i only run this one more time)
2004-08-20 19:58:04 +00:00
84e03f8b16 0.3.4.3, backwards compatible, to be released later today 2004-08-20 19:56:34 +00:00
288580aed7 be more accepting in what router references we receive (let them be up to 6h old before throwing a hissy fit) 2004-08-20 19:55:46 +00:00
de63bbcc86 adding clientApp parameter to pass the user's preferred browser into systray 2004-08-20 14:38:56 +00:00
80b8c284b4 * fixed a bug causing the systray to hang upon exit on Linux
* various nips and tucks all around
2004-08-20 14:36:03 +00:00
ffff6d701f added browser selection dialog 2004-08-20 12:56:40 +00:00
0b084ece08 * added ant build file
* systray is functional
2004-08-20 10:08:29 +00:00
28855d3fd1 * adding systray client to clients.config
* updating installer readme (the public domain 'license')
2004-08-20 03:11:08 +00:00
f7d356dc95 adding systray libs 2004-08-20 03:08:05 +00:00
104b332906 initial commit of systray code; not yet integrated 2004-08-20 03:07:27 +00:00
8b30852639 might as well link 'em to the netDb like the shitlist 2004-08-20 00:31:42 +00:00
bdaa14c257 formatting fix 2004-08-20 00:19:58 +00:00
0234fb62fb properly clean up the temp files (thanks oOo) 2004-08-19 23:46:56 +00:00
687ca781ab the DoS isn't CRIT and we log instances and publish the stat appropriately 2004-08-19 23:18:38 +00:00
3053c797e8 handle removal and addition (duh) 2004-08-19 22:02:28 +00:00
62d6709949 * include prepWEB as part of buildWEB (prepWEB checks to see if we've already downloaded it)
* include buildWEB as part of build (so everyone builds everything by default)
* have clean include pkgclean
* new i2ptunnel.config and clients.config for the pkg
2004-08-19 21:26:11 +00:00
410abaf92c handle the tunnels being instantiated prior to the CLI being inoked
(aka someone went to /i2ptunnel/ before the 2 minute delay passes)
2004-08-19 21:11:42 +00:00
5e07c478f5 a little tidying up 2004-08-19 20:27:35 +00:00
3eda53a97f ugly pages to display the old console and stats info, linked to off the top nav
also link to the i2ptunnel from the top nav (in a new window).
2004-08-19 20:26:19 +00:00
4e25382901 * load clientApp.* lines from clients.config by default, falling back on
reading the router's props only if that file doesn't exist.
* by default, only log CRIT messages to the screen (the rest are sent to
the log file).  this will be useful with the upcoming service controller
* refactor a common Properties helper to DataHelper.loadProps
2004-08-19 17:42:47 +00:00
fccb172e20 * Display both the hash and the full destination associated with each tunnel on
the /i2ptunnel/ page so they can easily be shared.
* By default, try to create any needed private key files (for server tunnels only)
* keep track of the I2PSession objects used by the I2PTunnel instances (only needed
for exposing the associated Destination)
2004-08-19 17:36:27 +00:00
5a761242f5 adding the wrapper binaries 2004-08-19 08:22:15 +00:00
aaaf1e14a5 removed the unnecessary launch parameter piping to /dev/null 2004-08-19 07:38:46 +00:00
3dcb9f6424 Added datagram_test.i2p 2004-08-19 05:59:27 +00:00
04621ff64a * service wrapper fully functioning under Linux and WinXP, should also be working for the other *nix platforms but this isn't tested yet
* renaming wrapper binaries to 'i2psvc' for *nix and 'I2Psvc.exe' for win32
2004-08-19 05:41:36 +00:00
5053808058 initial commit of service wrapper files
moving standalone-compiler.jar into the izpack subdir
2004-08-19 04:48:42 +00:00
9912c673bf * allow 2 failures in a tunnel before killing the tunnel. this is useful because
much of our tunnel failure detection code itself uses tunnels - send out tunnel 1
and get the reply through tunnel 2.  If it fails, which one "broke"?
* we now add a failure mark to both in all situations, including during tunnel
creation
* properly check the tunnel expiration 2-2.5 minutes prior to completion, rather
than the old 0.5-1.5 minutes.
2004-08-19 02:38:50 +00:00
4636f7be7b oops, we need to mark the peer we don't send to as failed (otherwise we wont send any more requests out, since there will be 'too many pending') 2004-08-18 20:44:57 +00:00
0ffc0a1959 Oops 2004-08-18 12:16:09 +00:00
e86032b129 Ver 0.9.1 2004-08-18 12:13:56 +00:00
87941a0975 Removed reference to stasher 2004-08-18 11:36:08 +00:00
3d6a40a683 don't do the netDb store of the peer's routerInfo until after we validate what they tell us (so we can shitlist them for the right reason) [thanks duck!] 2004-08-18 07:23:01 +00:00
9753470dcb handle a situation that would otherwise cause a search to block indefinitely rather than complete properly 2004-08-18 07:20:27 +00:00
a45e1b4781 made installer window resizable to compensate for some buggy *nix window managers 2004-08-18 04:47:09 +00:00
54f52d37ca * don't allow concurrent requests for the same key - add them to a list of deferred
requests which are notified on completion
* query peers who are sending us bad references, just don't follow their suggestions.
this is necessary since the peer may actually have the data (and other people may not be
getting shitty references from them)
2004-08-18 00:20:59 +00:00
6e295a7afb added the new GUI installer and associated build tasks 2004-08-17 20:43:11 +00:00
692cd7adae * reduced the period used to detect / avoid peers who send invalid data (60m instead of 120m)
* expose the reason for a dbStore rejection more cleanly
2004-08-17 20:37:47 +00:00
7794547d30 .cvsignore evidently didn't ignore itself 2004-08-17 19:40:24 +00:00
aum
35eaaee627 added -l flag for local-only put/get 2004-08-17 16:57:21 +00:00
aum
8029901ed7 fixed error (.i2pstasher) in help output 2004-08-17 10:09:56 +00:00
342c55043d accept w/ skew (duh) 2004-08-17 06:01:50 +00:00
3cf363667c added targets for .tar.bz2 package creation 2004-08-17 05:53:56 +00:00
2f8993995b enforce diversification of tunnel participants.
when picking peers to participate in a tunnel, we still select from the 'fast' tier,
except now we pick the ones that have least recently agreed to participate in a tunnel.
(they're already in the fast tier, so they're reliable [ish]).
the diversification has been pretty good so far, but i'm going to leave 'er running and monitor it overnight
2004-08-17 05:20:17 +00:00
8e9c541eba * new simple 'invalid reply rate' tracking the number of replies a peer sends that Suck.
(e.g. we can't find the referenced peer or the data they send back is corrupt/expired).
This is like the old invalidReplies, except its a rate that decays.
* if we receive more than 5 invalid replies from a peer in a 1-2 hour period,
stop verifying any subsequent replies, and also stop asking them for keys.
* cleaned up the store validation even further
2004-08-17 02:03:09 +00:00
7ed310ffd2 update ardvark.i2p (after checking ID) 2004-08-17 02:02:34 +00:00
bc1b020e95 put the timestamper in the background by default (so Base64 completes, thanks duck!) 2004-08-17 00:02:17 +00:00
5fdff16b1e removed shitlist ref 2004-08-16 22:38:10 +00:00
43e22a9028 javadoc fix (thanks oOo) 2004-08-16 22:17:47 +00:00
e102bf9eed lots of bitchin' oOo patches (woot, thanks oOo!), plus some cleanup
* apply oOo's patch for beautifying the new console w/ links to a shitlisted peer's netDb entry
* apply oOo's patch to clean up the peer shitlist count more aggressively
* apply oOo's patch to allow removing lines via /configadvanced.jsp
* apply oOo's patch to clean up the memory usage display
* apply oOo's patch to include log messages on /logs.jsp most recent first, rather than last
* get rid of the netDb key shitlist (its a bad idea, better solution coming soon)
2004-08-16 20:27:06 +00:00
aum
bf3ee5c158 added code.leo, to aid with editing the code via the
wonderful Leo structural literate editor
2004-08-16 16:19:32 +00:00
aum
2e99e3d9c5 added beginnings of splitfiles handling code
might not work yet for files > 28k
2004-08-16 16:18:41 +00:00
3d7029493a avoid a rare busy loop (thanks lucky) 2004-08-16 13:13:08 +00:00
a6ad2bbc5b added files.nickster.i2p 2004-08-16 04:25:48 +00:00
4dc17773c4 added brittanyworld.i2p 2004-08-15 23:06:52 +00:00
e5d66f46c6 deal with a race on close
more zealous bc synchronization
make sure we always close the streams explicitly
logging
2004-08-15 20:48:35 +00:00
d2fc24e792 deal with no proxy available
more carefully retrieve a proxy
logging (w/ unique requestId)
2004-08-15 20:44:14 +00:00
aum
ec52c81f46 Eliminated incorrect truncation of retrieved keys 2004-08-15 18:49:05 +00:00
aum
0eb0c4cc83 fixed getref bug 2004-08-15 18:33:37 +00:00
aum
b54e6bc933 *** empty log message *** 2004-08-15 17:58:57 +00:00
aum
83f891138d *** empty log message *** 2004-08-15 17:56:33 +00:00
aum
c621940b0f *** empty log message *** 2004-08-15 17:53:45 +00:00
aum
a27b0a0a1e *** empty log message *** 2004-08-15 17:45:01 +00:00
aum
23a52dbc9a first scripted commit 2004-08-15 16:57:02 +00:00
aum
f8a57c7885 Pulled another n bugs 2004-08-15 13:49:27 +00:00
a295d0ad1e cleanup the shitlist code
logging
2004-08-15 09:04:56 +00:00
190a2147cc Handle the netDb DoS problem at the root. The DoS was basically old peers telling us about expired
peers that we would crawl the entire netDb looking for (always failing, since there aren't any current
netDb entries for that peer that we would accept).
* keep a shitlist of keys we have recently searched for but were unable to find so we don't flood
* if our in-memory data store won't accept the data, its not helpful, so delete it on disk
* no need to do the preemptive refetching of a leaseSet, since we already garlic wrap it with payloads
* logging
2004-08-15 08:00:28 +00:00
49573b9e72 changed Windows-corrupted line endings back to UNIX 2004-08-15 07:17:13 +00:00
aum
e60b30ed44 Fixed n bugs, particularly relating to store 2004-08-15 07:02:36 +00:00
5c10ddf54c removing bogo.config, replaced bogobot.config 2004-08-15 05:17:12 +00:00
600ece819f config file implemented (thanks oOo)
NickServ capability (thanks oOo)
2004-08-15 05:14:49 +00:00
6bc7a3d8aa handle errors initializing, and deal with logFilePatterns that don't include a full path (e.g. log-#.txt instead of logs/log-#.txt) 2004-08-14 20:57:50 +00:00
aum
0af07e5352 *** empty log message *** 2004-08-14 18:11:42 +00:00
aum
8732f54c64 corrected invalid noderef aum.stasher 2004-08-14 17:51:50 +00:00
aum
437d5d76e9 added -f option for running stasher in foreground
reduced timeouts to 60secs
2004-08-14 17:43:46 +00:00
aum
7378be05d3 *** empty log message *** 2004-08-14 17:33:28 +00:00
aum
75febe4b75 Relocated from i2p/apps/sam/python
Stasher is a Kademlia-based distributed file store (aka 'DHT')
for I2P. Written in python, it can be accessed as:
 - low level python classes, or
 - via a client socket, with simple text-based protocol, or
 - via command-line client prog (called 'stasher', unsurprisingly)

Release status is pre-alpha

Developed by aum, August 2004
2004-08-14 17:23:07 +00:00
aum
f6d8d93a1b Removed all stasher-specifics, relocated them to
i2p/apps/stasher
2004-08-14 17:19:24 +00:00
aum
130310fddd Will suffice for doco for now 2004-08-14 13:07:04 +00:00
aum
8bd312046d First alpha release of stasher, a python-based Kademlia DHT implementation
specifically created for I2P.

New Files:
 - aum.stasher - a single noderef
 - stasher - wrapper script for starting/stopping/using stasher
 - stasher.py - similar wrapper script for 'doze users
 - src/bencode.py - requisite module
 - src/i2p/stasher.py - the stasher application classes - python module

Modified Files:
 - setup.py - added code to install stasher wrapper
 - src/i2p/__init__.py - added stasher to '__all__', allowing import
2004-08-14 12:58:12 +00:00
9cc96f45d0 * add a main() to TunnelControllerGroup which can be used as a clientApp.*
* new config property to have a tunnel start on load (default=true), so tunnels, er, start on load
* use i2ptunnel.config instead of i2ptunnel.cfg (for consistency)
* minor refactoring
2004-08-14 02:03:45 +00:00
c18fc1984d *cough* i knew there was a reason i left that test running... 2004-08-13 22:02:29 +00:00
3b651076d1 added stasher.i2p 2004-08-13 21:26:50 +00:00
352396bdc2 > Date: Fri, 13 Aug 2004 15:58:30 +1200 (NZST)
> Message-ID: <1776.202.37.75.101.1092369510.squirrel@202.37.75.101>
> From: adam@adambuckley.net
> To: jrandom@i2p.net
>
> [...]
>
> I hereby authorize my NtpClient.java and NtpMessage.java code to be
> redistributed under the BSD license for the purpose of integration with
> the I2P project, providing that I am credited as the original author of
> the code.
>
> [...]
w00t!  adam++
code migrated into core/java/src/net/i2p/time, integrated with Clock,
dropping that whole ugly pass-the-time-through-URL, and hence dropped
support for :7655/setTime.
New router.config properties to control the timestamper:
  time.sntpServerList=pool.ntp.org,pool.ntp.org,pool.ntp.org
  time.queryFrequencyMs=300000
  time.disabled=false
So, to disable, add time.disabled=true to your router.config.  It is
enabled by default.
Default router.config and startup scripts updated accordingly (since
timestamper.jar is now gone)
2004-08-13 21:15:22 +00:00
3c9b0273d4 only count locally generated lookup messages for detecting local DoS (duh) 2004-08-13 02:52:17 +00:00
5122f9989c track more info 2004-08-13 02:22:45 +00:00
8ebd22da96 hmm i thought i already committed this. well, anyway, cleaner stats wrt searching 2004-08-13 02:21:18 +00:00
c2d55013a6 0.3.4.2, backwards compatible, release pretty soon 2004-08-13 02:15:54 +00:00
25eda1378e * do DoS detection in constructor, so we get useful "why are we doing this"
stack traces (rather than "oh, we're doing it when... uh... writing to the socket")
* increase the throttle max, since we want to be able to send a few concurrent
2004-08-13 02:11:54 +00:00
dfac7bde9c * track searches more carefully
* detect situations where we may be inadvertantly flooding the netDb
and log them as CRIT with a stacktrace, as well as publish the count
of those events in the netDb
* detect potential netDb DoS situations by checking to see if we have
received more than 20 netDb lookups in 10 seconds, and if so,
probabalistically drop subsequent netDb messages (P=1-(10/numReceived)).
This is also published in the netDb.
2004-08-13 01:43:01 +00:00
348168d6c0 made the log less verbose for duck ;) 2004-08-13 01:27:55 +00:00
a1c772c8d8 changed quadn.i2p to library.i2p 2004-08-12 21:02:43 +00:00
f1ce1b5361 if we reach this point, bump up the expiration if necessary (otherwise the fast expire will occur with small clock skews) 2004-08-12 03:24:44 +00:00
ebdc7d70a1 shitlist appropriately (continued) 2004-08-12 03:23:48 +00:00
c5947c23bb include the shitlist summary 2004-08-12 03:22:27 +00:00
eeb1852d95 take note of the reason each peer is shitlisted and display that on the console (good idea oOo)
cleaned up the shitlisting process within the TCPTransport so that we don't shitlist twice (clobbering the detailed cause with a general "uh, couldn't contact 'em" cause)
2004-08-11 22:51:00 +00:00
2f28a635a9 integrated oOo's memory usage patch (the % shown is unfortunately % of peak, not % of max) [thanks oOo!]
cleaned up build script to make sure we always build fresh JSPs and xml [thanks oOo^2!]
2004-08-11 22:23:48 +00:00
d524c77560 *cough* (oops) 2004-08-11 08:23:56 +00:00
0025d94aa4 if the message has expired but hasn't exceeded the fudge factor, we still need to give it some time to be processed (we receive and validate it first, and if it reaches these jobs, we should let it continue)
logging
2004-08-11 07:10:37 +00:00
bb5ae2922d added freshcoffee.i2p 2004-08-11 07:00:23 +00:00
fbe9fe1ba8 new method for sanitizing content to be rendered on a web page (specifically, the routerInfo options can be hacked to contain evil html)
thanks for finding and suggesting the fix oOo!
2004-08-11 04:42:04 +00:00
mpc
007194d674 Check mallocs for NULL return 2004-08-11 04:39:17 +00:00
cdd74505d7 optional flag to allow using the standard base64 charset on decode/encode, rather than the (filename friendly) i2p charset 2004-08-11 00:59:11 +00:00
0aa023189d shitlist the old idents of peers who change idents
be sure to fire any onFail jobs if we aren't going to pass a message off (duh)
take clock skew into account when determining whether a message is expired (duh^2)
2004-08-11 00:57:36 +00:00
79aa10dfcb just a test 2004-08-10 21:22:45 +00:00
9ecfda0110 added basic HTTP authentication for accessing the router console (if a consolePassword is specified in the router.config)
unfortunately, this password setting is only read on router startup...
2004-08-10 19:51:11 +00:00
b89e26c460 cleaned up the validation of leaseSet/routerInfo elements being accepted so we validate only in one place (in the facade instead of both the facade and the dataStore)
don't accept entries created (too far) in the future
2004-08-10 16:55:54 +00:00
97e5952544 another oOo catch (not sure if this is ever run - it shouldn't be, but now it'll display the silly statement more clearly :) 2004-08-10 04:52:48 +00:00
8627328047 set the nextHopId whenever we know the nextHopInfo (duh) [thanks oOo!]
this should fix the deserialization errors from tunnels.dat
2004-08-10 04:11:58 +00:00
ec0c912c6f oOo's updates:
* properly describe expired leaseSets (e.g. "30s ago" instead of "in -30s")
* add a little table at the end of the netDb HTML summarizing the versions people claim to be running
2004-08-10 01:07:33 +00:00
mpc
953de3f1f2 Indentation 2004-08-09 10:56:14 +00:00
mpc
e1264de514 Magically fixed itself 2004-08-09 10:02:07 +00:00
5abd2b400c Updated wiki text sources 2004-08-09 10:01:04 +00:00
mpc
2c2a103676 Some more Makefile updates 2004-08-09 09:39:20 +00:00
44af799b66 reverting previous commit; realized it would hammer Mort Bay's servers 2004-08-08 23:07:07 +00:00
ec22ba3248 'dist' target broken, fixed by adding prepWEB as a depend before buildWEB 2004-08-08 22:42:56 +00:00
7fcc05c037 0.3.4.1, backwards compatible, release later today, blah blah blah 2004-08-08 19:29:53 +00:00
edf17d0a46 drop the arbitrary wait during creation (but not during startup)
only relevent to simulations, obviously
2004-08-08 19:26:44 +00:00
9cccd0bfc9 rather than flush any/all log messages 10 times a second, flush log messages once there are 100 of them or 10 seconds have passed, whichever comes first 2004-08-08 01:40:48 +00:00
e57c010e3d reduced context switches by removing the timeout val that was redundant 2004-08-08 01:39:00 +00:00
4dfcf1c1c8 updated boundary condition (thanks mule/oOo) 2004-08-08 01:35:45 +00:00
8d7786e97d bad jrandom, test first 2004-08-08 01:34:42 +00:00
2cb519cd06 updated implementation to match the RandomSource javadocs (inclusive of both 0 and n, unlike SecureRandom, which is inclusive of 0 and exclusive of n) 2004-08-07 19:14:13 +00:00
bc46ad4331 only block adding more *outbound* data, not handling data received from I2P. The data has already been received by the router and delivered to the streaming lib (and is sitting in RAM anyway...)
logging
2004-08-07 06:35:46 +00:00
be08e8f23b use the env defaults when no i2cp info is specified 2004-08-07 06:28:29 +00:00
f937809903 have a write() timeout after 60s instead of blocking forever by default (also used when injecting data into an input stream) 2004-08-06 22:24:56 +00:00
c0f32c942d more checking for timeout 2004-08-06 22:23:00 +00:00
39c5c830bb Added py2exe example 2004-08-06 14:41:05 +00:00
83c8953d1b Removed debug print statements 2004-08-06 13:43:46 +00:00
4b100a5a64 Replaced time.clock() -> time.time() (Bug on Unix) 2004-08-06 13:43:22 +00:00
b7e50e0b3a Replaced time.clock() -> time.time() (Bug on Unix) 2004-08-06 13:36:41 +00:00
mpc
6933052de7 Minor improvments to the build system 2004-08-06 01:27:53 +00:00
22d945f7b7 allow remote harvesting by specifying a URL to read routers from (e.g. --netDbURL http://dev.i2p.net/i2pdb/ ) 2004-08-05 20:35:52 +00:00
b81c5628ce fixed harvest of rankings (capacity vs reliability thing) 2004-08-05 20:07:10 +00:00
cdb4576bd7 per mule's patch, added support for a round robin across multiple outproxies
instead of the standard 'httpclient 4444' or 'httpclient 4444 squid.i2p', you can now specify a comma delimited list of outproxies:
'httpclient 4444 squid.i2p,www1.squid.i2p,www2.squid.i2p' and each individual http request goes through a randomly selected proxy
there are a few general issues with this, such as a lack of affinity (web applications that require a session to always come from a single IP address will break)
but it should work most of the time.
2004-08-05 18:17:33 +00:00
4859cd7dcf new job to aggressively fetch the most up to date routerInfo from peers by, er, asking them for it
disabled by default (enable through router.config 'netDb.shouldHarvest=true').
useful if you want to harvest the published router stats, since otherwise you could have data from a few hours back
2004-08-05 02:58:13 +00:00
3f70593ca8 precompile all of the JSPs so that people with JREs instead of JDKs can load this up 2004-08-05 02:24:00 +00:00
676288e6c0 added ems.i2p 2004-08-05 01:12:06 +00:00
1aa3e0cc5a Fixed bug in select 2004-08-04 14:35:45 +00:00
b0f8064d0d Add unit test 2004-08-04 14:35:12 +00:00
e5e85732d4 Oops, fixed one more bug 2004-08-03 09:16:53 +00:00
f97c1ef0d9 Fixed bug in recvfrom() 2004-08-03 08:51:28 +00:00
83cf815160 * add new and generally ugly components to allow web based control of tunnels
* build an i2ptunnel.war
2004-08-03 08:21:29 +00:00
fea62a529b * don't use static props on the I2PTunnel for I2CP / etc so that we can safely keep multiple instances alive
* propogate errors (if the I2CP host isnt reachable, the socket manager
won't be created) and handle appropriately
2004-08-03 08:18:10 +00:00
2cff5ae2bb added ooo.i2p 2004-08-02 21:09:21 +00:00
8aa29f5340 Update Python I2P library version 0.91 by sunshine 2004-08-02 15:23:37 +00:00
8051bfef1d remove generated doc 2004-08-02 15:22:21 +00:00
85bc79ab1b removed generated doc 2004-08-02 15:21:05 +00:00
97e5588184 Update Python I2P library version 0.91 by sunshine 2004-08-02 14:52:39 +00:00
e622fdc885 removed 2004-08-02 14:50:00 +00:00
4f81e1debe Update Python I2P version 0.91 by sunshine 2004-08-02 14:47:44 +00:00
9ccfd852d8 Updating I2P Python version 0.91 by sunshine 2004-08-02 14:44:24 +00:00
9df57a47d5 Updated Python I2P version 0.91 by sunshine 2004-08-02 14:34:06 +00:00
f2cadb7278 bugs.txt 2004-08-02 14:07:30 +00:00
3f6e7cb84c Updated Python I2P version 0.91 by sunshine 2004-08-02 14:06:30 +00:00
4ed4ce8240 Updated Python I2P version 0.91 by sunshine 2004-08-02 14:00:56 +00:00
4373956a3f Updated Python I2P version 0.91 by sunshine 2004-08-02 13:55:41 +00:00
36fb99a00d Updated Python-I2P version 0.91 by sunshine 2004-08-02 13:51:50 +00:00
8f5d325c4d Updated Python-I2P library 0.91 by sunshine 2004-08-02 13:50:21 +00:00
8d8b6da0bf updated to handle operation without a logger.config (reasonably) 2004-08-01 23:27:37 +00:00
3a61d260d7 cleanup wrt multiple app contexts 2004-08-01 23:24:47 +00:00
5bc433d1c8 fixed ^C^V errors 2004-08-01 23:21:35 +00:00
a0e4bbac6f updated defaults 2004-08-01 21:50:49 +00:00
d44d8cc53d penalize peers whose tunnels probably fail, but still dont kill those tunnels quite yet
logging
2004-08-01 20:41:33 +00:00
1305969247 requeues aren't absurd in a live net where connectivity sometimes fails (duh) 2004-08-01 20:39:17 +00:00
94becebafa increase the (essentially) arbitrary choke on message send times (which has caused some reliability problems under load) 2004-08-01 20:37:44 +00:00
8add433966 javadoc 2004-08-01 20:25:25 +00:00
c5b289fb1f javadoc fixes 2004-08-01 18:47:12 +00:00
f85ce180ed * added a way to control how large we let the buffers grow before we block,
or even whether to have the blocking action timeout and close the socket after
a certain delay
* refactored the I2PSocketOptions to be more actively used
* added a pair of ministreaming lib demo apps:
- StreamSinkServer listens to a destination and dumps any data it receives on a socket to a per-socket file
- StreamSinkClient sends a destination a specified number of random bytes, then disconnects
2004-08-01 18:34:02 +00:00
8101fa1c92 excluding Eclipse-specific project files 2004-08-01 06:50:14 +00:00
8a091e0205 fixed for ant 1.5 compat 2004-08-01 06:46:59 +00:00
edc3a54ad3 changed _botShutdownCommand to _botShutdownPassword for clarity 2004-08-01 06:42:31 +00:00
a3cd7d1068 Initial commit, bogobot version 0.3.1. 2004-08-01 06:26:51 +00:00
mpc
337441b8de Time for a new release 2004-08-01 06:25:56 +00:00
bd78a66bd4 updated build proc to conditionally fetch the jetty libs from http://dev.i2p.net/jettylib.tar.bz2
now the main ant 'dist' will build the web stuff as well (but ant 'build' wont)
2004-08-01 05:31:15 +00:00
96f9618081 expose more data and let the settings be overridden during runtime (and saved to disk) 2004-08-01 05:27:59 +00:00
mpc
cf7be2d601 Fixed stupid bug when pinging multiple hosts 2004-08-01 04:06:44 +00:00
aum
598732915e Added ref to HTTPServer I2PCGIHTTPServer and I2PSimpleHTTPServer to save users having to unnecessarily import i2p.I2PBaseHTTPServer 2004-08-01 03:21:11 +00:00
mpc
99c18396ab Some improvements to i2p-ping 2004-08-01 03:20:22 +00:00
mpc
6d5dd81066 added i2p-ping 2004-08-01 02:38:14 +00:00
7cd9451a22 fproxy2.i2p 2004-08-01 02:22:22 +00:00
mpc
29b5a7c5c2 Removed broken examples and added some more comments to warhammer 2004-08-01 00:30:25 +00:00
393a04165e honor some updates correctly 2004-07-31 23:48:44 +00:00
mpc
e97e834a5b Updated warhammer example, and fixed a bug on FreeBSD 2004-07-31 23:31:02 +00:00
bec685682b logging 2004-07-31 23:28:28 +00:00
34f119ca23 moved the router config reading code into the, er, router 2004-07-31 23:25:02 +00:00
09ed1b1f9e logging 2004-07-31 23:22:38 +00:00
fcb109f46d made the last of the config pages support dynamic updates (w3wt) 2004-07-31 23:19:23 +00:00
f30823e4ac removing my lost key 2004-07-31 22:00:07 +00:00
aum
c04885449d Replaced these files with:
- I2PSocketServer.py
 - I2PBaseHTTPServer.py
 - I2PSimpleHTTPServer.py
 - I2PCGIHTTPServer.py
difference being that these new modules are not hacks of the
original python server modules, rather, they subclass the
python server modules; this overcomes the Fear and Loathing
expressed by some regarding multiplicity of licenses, and
apart from that, is a better idea anyway. Only danger being if the
modules in later Python releases change substantially, these modules
could get broken.
2004-07-31 18:51:45 +00:00
aum
8c31e47eeb Fixed example to import correct modules 2004-07-31 18:46:59 +00:00
aum
d8ee5c180b Replaced hacked Python server modules with ones which
subclass the python modules.
2004-07-31 18:45:13 +00:00
823f4a26b3 patch was unnecessary. kept docstrings. 2004-07-31 10:45:47 +00:00
a05e8a446d update the default tunnel settings dynamically (adjusting the pool size accordingly) 2004-07-31 04:16:30 +00:00
21126f766c put the adminManager in the context so we can control it (and in turn, restart it) 2004-07-31 04:15:09 +00:00
8f46ead756 added the handler component to deal with arbitrary changes 2004-07-31 04:10:33 +00:00
75652fc2c4 config clients handler implemented 2004-07-31 03:34:00 +00:00
7cdc46f007 added thetower.i2p (he denies being thetower from TFEE fame) 2004-07-31 02:44:20 +00:00
ed9f9625ae added actual form handling to the main config.jsp page
dropped the old notice.jsp style result notification
display destination info on the left nav
2004-07-31 02:34:24 +00:00
mpc
7b60d3dab9 Add session to callbacks too 2004-07-31 02:31:39 +00:00
a6993fa489 now allow restarting within the same jvm (loading all the config options again, rebinding sockets, etc - it does NOT fire up all the clientApp tasks though - those aren't part of the router) 2004-07-31 02:21:46 +00:00
mpc
bc2774bde4 Added multi-session support 2004-07-31 01:36:51 +00:00
d10dc1e8d3 Doing something different this time -- but only to core
I marked all the empty statements with //nop
I removed unneccessary elses (ie, the if returns or throws)
I took out some casts (integral promotions/some didn't need to be there)
-- Love, shendaras
(2 in one day, w00t)
2004-07-31 01:04:34 +00:00
48556de92b added fproxy.i2p and mrflibble.i2p 2004-07-31 00:55:10 +00:00
7f6b477d2e Fixing up imports (I've been falling down on my job).
Note:  I didn't touch routerconsole since it isn't done (I believe)
-- Love, shendaras
2004-07-30 22:19:57 +00:00
11d8c67d12 added some client info (e.g. 2 clients w/ 2 1 hop tunnels each expiring in 8m) 2004-07-30 20:28:19 +00:00
fd2a4029e7 html cleanup for the new console 2004-07-30 20:27:13 +00:00
4467928845 pmd pointed out a few unnecessary things (well, a lot more than just this, but these are the ones i'm fixin ;) 2004-07-30 19:43:59 +00:00
aum
cc85a00bfd Added 'dontResolve' keyword to Socket.connect, which if set
to True, will prevent the hostname lookup. This gives
the caller the option of passing in a physical destination
base64, instead of being limited to known hosts.
2004-07-30 17:43:39 +00:00
aum
15d58ecdcd Noting that all files here are in public domain, except from the
files src/i2p/*Server.py, which are derived from the Python
standard library counterparts, and therefore inherit the Python license.
2004-07-30 12:44:23 +00:00
aum
08d93b9a78 added module docstring indicating changes 2004-07-30 12:40:41 +00:00
aum
a75a999e3b README file for adapted python server modules 2004-07-30 12:38:12 +00:00
aum
684ef709f5 Added python server modules to __all__ 2004-07-30 12:36:00 +00:00
aum
2e98dd09e7 These modules are taken verbatim from python 2.3 standard library,
and have been hacked to work with sunshine's I2P SAM Socket module.
2004-07-30 12:34:57 +00:00
aum
6635425bbc example_httpd.py - demo of I2P-ised Python server classes 2004-07-30 12:33:38 +00:00
5d4bdc5697 0.3.4 NOT BACKWARDS COMPATIBLE
(0.3.4 and not 0.3.3.1 since its got some major revamps)
to be released later today.  dont upgrade until the release announcement comes out
2004-07-29 21:37:18 +00:00
0fdb286005 added some comments wrt rate limiting and getting the proxies to be reachable remotely 2004-07-29 21:34:55 +00:00
59a8493aa7 fixed some URLs, and the irc proxy is loaded on startup 2004-07-29 21:29:02 +00:00
25378e894b less aggressive removal of peer references
logging
2004-07-29 20:36:44 +00:00
3b9fea20b6 added files.hypercubus.i2p 2004-07-29 20:07:54 +00:00
c6bb8f09ca avoid the race that could corrupt local transfers by using a single thread to receive notifications of message availability (and in turn fetch that data)
the old way fired off a new (very short lived) thread for each message received, and if two happened really really quickly, they'd both lock on the mutex and the order would be undefined
this avoids that.  thanks to oOo et al for pestering me and sending in logs :)
2004-07-29 20:02:12 +00:00
4a9bd84bf0 check the delivery order and call out an error if a pong comes back in the wrong order 2004-07-29 16:12:36 +00:00
c02522b0fe * track the message progress through the send process more carefully
* drop the outbound message as soon as it expires rather than transferring an expired message
* drop hard any outbound message that takes us over 5 seconds to process (if we have a 5s message processing time, we do no one any good)
* don't try to resend (only useful when dealing with multiple transports - aka insufficiently tested code)
* don't republish netDb messages as often
2004-07-29 05:37:10 +00:00
c2a71ef756 include stats on bytes wasted (overflow from the buckets) 2004-07-28 23:35:48 +00:00
e669110cf4 be sure to allow for clock skew 2004-07-28 23:34:42 +00:00
f4cf31c13d less aggressive passive publishing 2004-07-28 23:34:02 +00:00
7b23a5dcce keep track of wasted bytes (overflow from the bucket) 2004-07-28 23:32:51 +00:00
b2fda0c79d catch errors earlier 2004-07-28 23:29:21 +00:00
5af96f5ccb when we really need tunnels, always build them 2004-07-28 23:28:55 +00:00
ca445ac178 when we need tunnels, always build 2, not the exact quantity required (so that its a bit smoother) 2004-07-28 23:27:46 +00:00
mpc
16fb31b6eb doc update 2004-07-28 04:50:44 +00:00
mpc
a1ff325b7b Improve sendq to always send big packets for better network performance 2004-07-28 04:48:35 +00:00
5eaec4c841 only recurse one time 2004-07-28 03:51:38 +00:00
ffcc34c4f9 heh, if it expires, we probably don't want to forward it (duh) 2004-07-28 03:50:30 +00:00
2dbe33e769 * cleaned up the tunnelCreate reply timeout
* reduced the number of tags passed when garlic routing a tunnelCreate
* catch timeout on a tunnel message quicker
* give a tunnel message a new messageId per hop
* added some more infrastructure for per-hop tunnelId
2004-07-28 00:08:15 +00:00
60c7db0733 if I'm making this backwards incompatible, I might as well clean up the rest, 'eh?
* removed SourceRouteBlock & SourceRouteReplyMessage, as they're a redundant concept
that 1) takes up bandwidth 2) takes up CPU 3) smell funny.
now the TunnelCreateMessage includes a replyTag, replyKey, replyTunnel, and
replyGateway that they garlic encrypt their ACK/NACK through and with.

* tunnelCreateMessage doesn't need a seperate ACK - either we get a
TunnelCreateStatusMessage back or we don't.

* message structure mods for unique tunnel ID per hop (though currently all hops have
the same tunnel ID)
2004-07-27 22:04:02 +00:00
0ed95bbdf1 new helper to read/write 2004-07-27 21:45:56 +00:00
c901bcf9b7 javadoc warning fix 2004-07-27 17:41:40 +00:00
0ccf915a18 ewps 2004-07-27 17:39:52 +00:00
52b1c0a926 * netDb searchReply and lookup messages now contain H(peer), not the peer's full RouterInfo
(making a searchReply message ~100 bytes, down from ~30KB, and the lookup message ~64 bytes, down from ~10KB)
* when we get the netDb searchReply or lookup message referencing someone we don't know,
we fire off a lookup for them
* reduced some excessive padding
* dropped the DbSearchReplyMessageHandler, since it shouldn't be used (all search replies
should be handled by a MessageSelector built by the original search message)
* removed some oddball constructors from the SendMessageDirectJob and SendTunnelMessageJob (always must specify a timeout)
* refactored SendTunnelMessageJob main handler method into smaller logical methods
2004-07-27 17:34:36 +00:00
399865e6c8 increase the replenish frequency to occur every .1s
logging
2004-07-27 17:20:42 +00:00
54aeab1524 send the full RouterInfo in the STS validation, not just the RouterIdentity (and in turn, store that RouterInfo in the local netDb)
logging
2004-07-27 17:17:16 +00:00
91f83277e2 made incompatible with previous releases - the remaining commits before the next rev are NOT BACKWARDS COMPATIBLE
do NOT update until the next release
2004-07-27 17:15:55 +00:00
c937cb2f07 no need to test a peer that we already know is up 2004-07-27 06:34:30 +00:00
ebd150e473 we don't need to build a tunnel so often (just enough to keep things fresh)
cleaned up rebuild / verification process so that the select*TunnelIds will always return what is necessary
for the moment, don't automatically kill all tunnels of a peer who fails just once (they can recover)
logging
2004-07-27 06:19:44 +00:00
9218f7b82c deal with not having tunnels a bit earlier 2004-07-25 23:51:07 +00:00
edaf7aee5d * for the moment, remove the 'isFailing' check for peers who have failing tunnels
(we need a more sophisticated algorithm than the one in place for it to be effective)
* fix for the profileOrganizer to work safely in the sim
2004-07-25 23:46:55 +00:00
43c18d0f4d (techincally) reduced the minimum bandwidth rate to 1KBps, but NO ONE SHOULD SET IT THAT LOW. do not reduce your limits below 6KBps until More Stuff Gets Done.
logging
2004-07-25 23:43:13 +00:00
65d85f7479 the vast majority of messages on the live net are under 2KB 2004-07-25 23:40:08 +00:00
476e23db5b new stat monitoring the netDb search reply message sizes 2004-07-25 23:35:50 +00:00
abaa5d87f6 more efficient mem alloc & usage 2004-07-25 23:33:54 +00:00
ce3e7e623c handle disconnect while there are still requests pending 2004-07-24 17:54:49 +00:00
mpc
3fd35a9c18 *** empty log message *** 2004-07-24 03:31:24 +00:00
f170ae741e 0.3.3, backwards compatible, to be released Real Soon 2004-07-24 02:13:37 +00:00
03562b037d added (commented out) hooks for the 0.4 web arch 2004-07-24 02:11:22 +00:00
472312709a added ref for the 0.4 routerconsole stuff, but its not ready for use, so, er, dont use it 2004-07-24 02:08:21 +00:00
b68463249e first pass at the 0.4 architecture. not ready for use or integration yet, but is functional with some manual build/config work 2004-07-24 02:06:07 +00:00
740a2da702 more consistent html 2004-07-24 01:59:27 +00:00
85c8e56417 fixed a strange bug when the .wait delay is really accurate (too accurrate..). thanks ZeroCool for help debugging this! 2004-07-24 01:10:11 +00:00
481ef56e74 added www1.squid.i2p 2004-07-23 21:22:51 +00:00
008795770f allow the timestamper to be started up while disabled 2004-07-23 18:19:40 +00:00
834fb7e317 allow the timestamper to be controlled by env properties (and, in turn, safe to always run)
if/when the property "timestamper.enabled" is set, the timestamper will query the sntp server(s) and update the clock accordingly
if/when it is not set (or set to something other than "true"), it will pause with its standard delay before checking again
in addition, it has a guard to help running the timestamper multiple times in the same JVM
2004-07-23 17:43:45 +00:00
da4827f287 expose some data for the router console to query 2004-07-23 17:39:31 +00:00
9f4439583d expose some data points for the new console, and cleaned up some html
new piece of data exposed and maintained is a list of router contexts - shown as a singleton off RouterContext - allowing an app in the same JVM to find the routers (and chose between which one they want)
2004-07-23 17:36:29 +00:00
mpc
69981e4d78 *** empty log message *** 2004-07-23 03:08:20 +00:00
mpc
a857c6a88f *** empty log message *** 2004-07-23 00:10:59 +00:00
mpc
e8d19439f8 *** empty log message *** 2004-07-22 08:54:01 +00:00
56216250a7 Added doc sources (public domain) 2004-07-21 12:02:56 +00:00
bea331db26 Fixed typo (public domain) 2004-07-21 11:56:36 +00:00
bc4e833a47 Fix install path 2004-07-21 11:36:23 +00:00
mpc
83f399fffc hopefully i'll have time to work on this socket stuff tomorrow 2004-07-21 10:24:22 +00:00
5214436d18 initial import of Connelly's public domain I2P python lib 2004-07-21 07:42:29 +00:00
8603250d73 updated the readme to reference the current specs and implementations
removed the old out of date jython and python code
2004-07-21 06:25:44 +00:00
9a8a099701 javadoc fix 2004-07-20 21:43:42 +00:00
a5a0c8c837 moved minimal I2CP info to the I2PSession docs (since it is the one that implements it) 2004-07-20 21:31:57 +00:00
604bcd5874 initial impl 2004-07-20 21:28:28 +00:00
d29f9409bf include some basic I2CP info 2004-07-20 21:16:30 +00:00
b5a0f5910d first pass 2004-07-20 21:08:04 +00:00
ccb2600e67 when measuring capacity, consider data updated within the last hour as good, not just the last 5 minutes 2004-07-20 04:11:33 +00:00
f06e21ff5a null check (oops) 2004-07-20 04:10:33 +00:00
bb0817a2ec erg, expose the capacity calculator
(the last Router commit is a mod that ugha requested, but i think its ugly so its disabled atm)
2004-07-20 03:35:36 +00:00
6911f865ca expose the capacity calculator 2004-07-20 03:34:52 +00:00
fe28b2732c simple error condition check 2004-07-20 03:28:43 +00:00
e8e8c37496 * implement new 'capacity' concept, which replaces the old 'reliability'
one for peer selection and organization.  reliability is kept around
  for the moment and shown on the router console, but only to provide a
  comparison (it is not used in any way)
* new stat in the TunnelHistory: failRate
* coallesce TunnelHistory stats (duh!)
* new ProfileOrganizer CLI ("ProfileOrganizer[ filename]*"
* implement reasonable 'failure' logic - if they are actively rejecting
  tunnels or tunnels they've agreed to are failing, mark them as failing
* when choosing peers to test, exclude all fast ones
2004-07-20 03:27:34 +00:00
ef0f1ca1e7 include a lil more eye candy on the console (how active each tunnel is and last test time) 2004-07-20 02:57:55 +00:00
31ca34b954 rate.getAverageValue returns the average of the last fully completed period, but we want to include the current partial period as well 2004-07-20 02:53:41 +00:00
c4e6a2f0a8 if the log pattern/path referenced doesn't exist, create all necessary parent directories (killing the JVM if it fails, rather than silently gobble the log messages to /dev/null) 2004-07-19 17:18:49 +00:00
b56845e200 added quadn.i2p 2004-07-18 21:35:13 +00:00
d7a1fee781 closing a stream multiple times shouldn't kill the SAM session (thanks for the bug report Connelly) 2004-07-18 15:02:54 +00:00
mpc
b1f802c42d Add id tag to strl 2004-07-17 08:18:16 +00:00
mpc
5f022e6e1f minor code cleanup 2004-07-17 04:54:45 +00:00
mpc
392cbb817e cleaned up time class 2004-07-17 03:11:20 +00:00
130399a1e7 0.3.2.3 (coming soon to a hard drive near you) 2004-07-16 21:12:27 +00:00
37d5531737 logging, including replacing the scary monster with its true self (we had data queued up, but were unable to get an ACK on our last write) 2004-07-16 20:48:40 +00:00
f0b6cbaf89 logging 2004-07-16 19:14:39 +00:00
707b173e77 differentiate between an explicit tunnel rejection (due to overload, etc) and an implicit one (the request timed out, the tunnels delivering the request failed, etc)
also, within the implementation of the profile, only mark the explicit rejections as a rejection
2004-07-16 00:17:28 +00:00
4381bb5026 don't rip the peer's head off after multiple tunnel rejections - penalize them *once* for the instance (not once *per* instance) 2004-07-16 00:15:34 +00:00
5850ad1217 typo fix, thanks Connelly
(duck)
2004-07-15 16:02:53 +00:00
806d598a04 _context/getContext()
(missed one)
2004-07-15 05:23:18 +00:00
e737e5c950 * work around the disagreement between different versions of sun's compiler and JVM:
Some of them think that its ok for an inner class of a subclass to access protected data of the
outer class's parent when the parent is in another package.
Others do not.
Kaffe doesn't care (but thats because Kaffe doesn't do much for verification ;)
The JLS is aparently confusing, but it doesnt matter whether its a code or javac bug, we've got to change the code.
The simplest change would be to just make the JobImpl._context public, but I loath public data, so we make it private and add an accessor
(and change dozens of files)
whee
2004-07-15 05:12:37 +00:00
bbcde2f52b 0.3.2.2 (a lil installation testing and then i'll push) 2004-07-15 01:08:54 +00:00
f6ef77429c some boundary cases for the queue pumper's wait time 2004-07-15 01:04:13 +00:00
44491c1514 added nickster2.i2p and irc.nickster.i2p (pointing at iip) 2004-07-14 22:12:43 +00:00
71a6cf4ee6 * adjust the algorithm to deal with IO bound requests:
if more tokens become available while the first pending request is still blocked on
  read/write (aka after allocation and before next .waitForAllocation()), give the tokens
  to the next request
* refactor the satisfy{In,Out}boundRequests methods into smaller logical units
2004-07-14 21:07:57 +00:00
744ce6966f add a new throttle (and stats) based on send processing time
high send processing time and low job lag means the latency is coming from outside the jobQueue - aka bandwidth throttling
2004-07-14 20:01:40 +00:00
d25cec02c2 clean up sorting for peer reliability
increase penalties for tunnel rejection, and keep track of the 10 minute rate as well as 1 and 60
2004-07-14 19:56:38 +00:00
f02bf37fd3 stats and stats and stats
track the total allocated bytes correctly (even if we're throttled)
2004-07-14 19:54:04 +00:00
304b9d41d7 on kaffe i've periodically seen some hangs in the jobqueue, so lets try being a bit more conservative with the synchroniation, and include some debugging output in the router console to help track it down (if this doesnt fix it) 2004-07-13 20:19:28 +00:00
2d6af89f60 safer operation (for use in the sim where some things aren't always availble) 2004-07-13 20:17:15 +00:00
d6425973e2 include an objectId flag for use in the logging 2004-07-13 20:16:05 +00:00
d5ad56c4de use smaller writes to make it look more normal 2004-07-13 20:14:18 +00:00
4f1f2cc99e since people are using small buckets, the 10s replenish frequency is a really really bad idea (so default to 1s) 2004-07-13 05:49:16 +00:00
da439dd127 sanity checking for a kooky race condition 2004-07-12 21:33:32 +00:00
1375d01bdf new bandwidth allocation policy and usage to include support for partial allocations (and in turn, partial write(...)) while still keeping the FIFO ordering
this will give a much smoother traffic pattern, as instead of waiting 6 seconds to write a 32KB message under a 6KB rate, it'll write 6KB for each of the first 5 seconds, and 2KB the next
this also allows people to have small buckets (but again, bucket sizes smaller than the rate just don't make sense)
2004-07-12 21:09:05 +00:00
7b9db07f13 target=1.3 and source=1.3, not target=1.1 and source=1.3
(this is what caused the runtime errors on sun jvms but not on kaffe)
((aka i slacked and didn't test sufficiently.  off with my head))
this now builds and runs fine in sun 1.3-1.5 jvms, as well as kaffe
2004-07-12 16:39:22 +00:00
f2f26136c1 Minior cleanups -- removed commented out debugging code, wrote better
comments.
(ugha)
2004-07-12 05:12:22 +00:00
0f60ac5acf 0.3.2.1 (backwards compatible blah blah blah) 2004-07-11 18:57:01 +00:00
c28f19fe8a less painful and/or redundant penalties for failures 2004-07-11 18:50:23 +00:00
mpc
09a6dbc755 FreeBSD port 2004-07-11 13:22:37 +00:00
3bc0e0fc8a added source and target declarations for the javac commands so we can build with the 1.5^W5.0 JDK
(also added deprecation, since, well, we can :)
2004-07-11 04:16:59 +00:00
eb0e187a54 throttle tunnel participation based on whether we've had to throttle our network connection > some number of times in the last 10-20 minutes (rather than a simple "are we throttled *right now*?") 2004-07-10 22:28:34 +00:00
a788d30f34 added support for new 'clientoptions' command which alters the properties passed when creating subsequent I2CP connections
e.g.: -e "clientoptions tunnels.depthInbound=0" -e "httpclient 6666"
this updates so many files because they all need a reference to an I2PTunnel object on construction so they query tunnel.getClientOptions() instead of System.getProperties
2004-07-10 16:59:49 +00:00
591dfc961e give the reliability more positive influence so it doesn't go negative so easily
update the peerProfile's CLI to make the resulting stats easier to read
2004-07-10 04:15:51 +00:00
809e12b034 logging 2004-07-10 04:13:42 +00:00
1669d174e1 use mihi's template engine to set a random timestamper password so people dont need to think about that stuff
don't use the dyndns anymore for seeding (use dev.i2p.net/i2pdb)
2004-07-10 02:36:27 +00:00
3cfd28de43 add a new unit test for repeated fast reconnections 2004-07-10 01:58:05 +00:00
4888207eca if a client reconnects, we always want to get a new leaseSet ASAP (even if the pool hadn't been marked as stopped yet)
logging
2004-07-10 01:46:57 +00:00
294cb96107 if the job's startAfter is changed, tell the jobQueue to go through the timed jobs again in case the new time changes the scheduling 2004-07-10 01:44:27 +00:00
b648fa2b70 send the stats page out in chunks (more mem efficient, blah blah blah) 2004-07-10 01:39:54 +00:00
ab99122211 render status HTML in pieces (continued) 2004-07-09 05:33:19 +00:00
dd014fee88 send the router console out bit by bit rather than building it all up and sending it (thereby reducing its memory footprint dramatically) 2004-07-09 05:29:02 +00:00
c81f864de3 reduce the throttle threshold from 5s lag to 2s lag 2004-07-09 05:22:29 +00:00
90fe7dceec include the expiration in the error message if its dropped 2004-07-09 05:20:26 +00:00
3a568096f2 new throttling code which rejects tunnel create requests, networkDb lookup requests, and even tells the I2NP components to stop reading from the network (it doesnt affect writing to the network)
the simple RouterThrottleImpl bases its decision entirely on how congested the jobQueue is - if there are jobs that have been waiting 5+ seconds, reject everything and stop reading from the network
(each i2npMessageReader randomly waits .5-1s when throttled before rechecking it)
minor adjustments in the stats published - removing a few useless ones and adding the router.throttleNetworkCause (which is the average ms lag in the jobQueue when an I2NP reader is throttled)
2004-07-09 03:56:22 +00:00
94e694fc61 reduce the job pipeline to send a message by fetching the bids and adding the message to the connection queue synchronously
these had been broken out into seperate jobs before to reduce thread and lock contention, but that isn't as serious an issue anymore (in these cases) and the non-contention-related delays of these mini-jobs are trivial
2004-07-09 03:48:12 +00:00
bdfa6e4af5 dont penalize send failures (beyond what we already do for comm errors)
keep a rate for tunnel rejection, rather than a simple 'last' occurrance, and penalize the reliability with it
2004-07-09 03:45:11 +00:00
8e64ffb4f6 keep the relay message size rate data for the 10 minute period (so we can throttle on logical periods) 2004-07-09 03:43:07 +00:00
6c162643cb expose stat for throttling (# tunnels we're currently participating in) 2004-07-09 03:41:27 +00:00
ff7742bca3 expose some stats useful for throttling (# ready & waiting jobs and the max lag of those jobs) 2004-07-09 03:39:38 +00:00
9685884279 deal with null peer (used by the SubmitMessageHistoryJob to bw limit the history)
current 0.3.2 throws an NPE which causes the submitMessageHistory functionality to fail, which isn't really a loss since i send that data to /dev/null at the moment ;)
(but you'll want to router.keepHistory=false and router.submitHistory=false)
this'll go into the next rev, whenever it comes out
(thanks ugha!)
2004-07-07 22:23:25 +00:00
a8b0d7f999 pull out no longer relevent config parameters
start the irc proxy by default within the router
dont package up the startIrcProxy script
2004-07-07 18:06:46 +00:00
dd84233085 0.3.2 for release later today 2004-07-07 17:58:37 +00:00
fe3eac07f4 migrate the queue pumper thread to scheduled activity (instead of waking up every 500ms, check the job timings to see when we should next wake up. we also wake up whenever a new timed job is added, or the clock skew changes)
pull out some of the old unused vars/flags
2004-07-07 16:16:36 +00:00
0948686989 don't mark failing due to sendFailed (since that can be caused by a message that was about to expire anyway) 2004-07-07 16:11:46 +00:00
4c82970319 get rid of the whole slice concept
dont time out for too many messages (just time out individual ones)
however, if any of the messages that time out have been there for a minute, kill the con (since its hung)
kaffe workaround for fast closing sockets
2004-07-07 16:10:57 +00:00
fe2fede8ed removed maxWaitingJobs param 2004-07-06 18:26:04 +00:00
b23b1e5f1f instead of the maxQueuedMessages limit, use the rule 'if any of the messages time out on the queue, its going too slowly'
(this helps in situations where we've got a flash flood of small messages to send)
2004-07-06 18:24:59 +00:00
dca66c8de8 leave all threads at base priority (except the client runner, where we push at max)
don't consider a connection valid until it has been up for 30 seconds (so people who are simply establishing connections but whose nats are still messed up get the error)
when dealing with expired after accepted, dont drop unless it expired outside the fudge factor
increase the default maxWaitingJobs to 100, since we can get lots at once (and we dont gobble as much memory as we used to)
also, don't wake up the jobQueueRunner in getNext once a second, instead just let the threads updating the queue notify
2004-07-06 14:38:35 +00:00
49090014cc placeholder for overload detection 2004-07-06 14:30:52 +00:00
fa4f100705 new limiter, pull slow and not too useful tests (uncomment 'em to run 'em) 2004-07-04 04:35:16 +00:00
bbf68cd9a8 implemented the FIFO bandwidth limiter which, suprisingly, chokes read/write operations
if they would exceed the currently available # of tokens, letting through those operations
in the order they are called.  like the old trivial bandwidth limiter, this uses a token
bucket approach (keeping a pool of 'available' bytes, decrementing on their use and
periodically refilling it [up to a max limit, to prevent absurd bursts]).  on the other
hand, it doesn't have the starvation issues the old one had, which would continue to let
small operations go through (e.g. 8 byte write) and potentially block large operations
indefinitely (e.g. 32KB write).  However, this new version is, how shall I put it, context
switch heavy?  :)  We'll revise with a scheduling / queueing algorithm once we're away
from transports that require threads per connection
The two directions (input and output) are managed on their own queues, and if/when things
are backed up, you can see the details of what operations have been requested on the
router console.
Since we still need better router throttling code (to reject tunnels and back off more
accurately), I've included a minimum KBps on the limiter, currently set to 6KBps both
ways.  Once there is good throttling code, we can drop that to 1-2KBps, and maybe even
less after we do some bandwidth usage tuning.
There were also a few minor touch ups to handle message data being discarded earlier
than it had been before (since write/read operations can now take a long period of time
in the face of contention)
The five config properties for the bandwidth limiter are:
* i2np.bandwidth.inboundKBytesPerSecond
* i2np.bandwidth.outboundKBytesPerSecond
  (you can guess what those are)
* i2np.bandwidth.inboundBurstKBytes
* i2np.bandwidth.outboundBurstKBytes
  the burst KBytes specify how many bytes we'll let accumulate in the bucket, allowing
  us to burst after a period of inactivity.  excess tokens greater than this limit are
  discarded.
* i2np.bandwidth.replenishFrequencyMs
  this is an internal setting, used to specify how frequently to refil the buckets (min
  value of 1s, which is the default)
You may want to hold off on using these parameters though until the next release,
leaving it to the default of unlimited.  They are read periodically from the config file
however, so you can update them without restart / etc.  (if you want to have no limit on
the bandwidth, set the KBytesPerSecond to  a value <= 0)
2004-07-04 04:33:17 +00:00
mpc
bbc5f6588a *** empty log message *** 2004-07-04 01:53:35 +00:00
mpc
b3632a6a35 duh 2004-07-04 01:00:04 +00:00
mpc
3943c51bb4 Hmm 2004-07-04 00:57:14 +00:00
mpc
4c19ddde69 a couple locking changes 2004-07-03 22:03:49 +00:00
mpc
d8f0f1a1d3 Added test for Logger and debugged it 2004-07-03 21:53:10 +00:00
mpc
121c0d89f2 Fixed test 2004-07-03 20:58:42 +00:00
badfb9088e logging / debugging and formatting (no functional changes) 2004-07-03 19:42:34 +00:00
ff392fee14 properly fake-encrypt the data (this class is only used by the simulator or anything else w/ -Di2p.encryption=off) 2004-07-03 19:41:41 +00:00
mpc
a13693161a http://www.kirstenfan.com/galleries/magazines/fhm2002100sexy/002.jpg -- studies show charming women love computer programmers...... 2004-07-03 11:05:23 +00:00
4b8ac81669 minor refactoring, javadoc
dont add an arbitrary extra Router.CLOCK_FUDGE_FACTOR to the expiration
2004-07-02 18:57:42 +00:00
219a704ee0 bugger it, for consistency, always include the reply leaseSet with a message (later, when we want to optimize the bandwidth requirements, we can revisit) 2004-07-02 16:59:37 +00:00
3996cd1f08 make the client writer thread run at max priority, since it is very time sensitive and only executes for very brief periods 2004-07-02 16:53:49 +00:00
c636b0a0ec minor rewrite to make timing more precise (keeping a map of message add times, not just the 'last' add time) 2004-07-02 15:12:35 +00:00
aae9f671f0 added jdot.i2p (duck) 2004-07-02 13:31:09 +00:00
aec6e901ee And I thought I was dyslectic. (duck) 2004-07-02 13:18:00 +00:00
mpc
f49be25288 <Nightblade> hmm 2004-07-02 09:54:27 +00:00
mpc
f9a96126e1 I don't know how I missed this stuff before 2004-07-02 09:37:54 +00:00
8c9f58b939 Misc. stuff: javadoc, UNUSED...
shendaras
2004-07-02 02:54:04 +00:00
8a7e787f42 logging 2004-07-01 22:33:51 +00:00
148dcc084d factor out the clientWriterRunner and have it deal with multiple i2cp messages being enqueued really fast (at least, more efficiently, by pulling them all off at once and handling them in one pass) 2004-07-01 15:21:32 +00:00
15b1cbd762 synchronize the available() call, and made explicit some other synchronization 2004-07-01 15:11:34 +00:00
e9b7ca3697 dont accept outrageously long delays when building a tunnel (aka now each peer only gets the timeout to respond, instead of the full # peers * timeout to respond)
this will cause more dropped messages to show up, but in turn it will avoid slower peers (since they'll be marked down as rejecting the tunnel)
2004-07-01 15:08:18 +00:00
mpc
1b03e9a3ee Who was the idiot who came up with the idea that booleans should be numbers? 2004-07-01 09:30:52 +00:00
mpc
2b951e3f61 Change throws to asserts. If any of this stuff happens it means a code logic error or a retarded computer, so throwing it is just a waste of time. 2004-07-01 09:17:17 +00:00
mpc
f51e064cf6 *** empty log message *** 2004-07-01 02:23:59 +00:00
9640e93895 imports
shendaras
2004-06-30 13:21:15 +00:00
d5bd22040c Crappy fix for incorrect Total Bytes Sent/Total Bytes Received via BandwidthLimiter.... ah.. just read the FIXME there.
shendaras
2004-06-30 13:16:05 +00:00
dcdcb7521a dont kill the context, we may need it when tearing down the runner (e.g. to get the time) 2004-06-30 04:11:59 +00:00
4058c63884 dont be such a prude 2004-06-30 03:02:39 +00:00
dd34548cc6 publish the tunnel congestion stat 2004-06-29 22:32:31 +00:00
a6b5211fa7 congestion is only a warning, not an error 2004-06-29 22:30:14 +00:00
4e89b9c363 reliability threshold = median of active and nonfailing (inactive nonfailing can be a large number of 0 reliability peers) 2004-06-29 20:32:36 +00:00
f3e267d2d0 active peer testing - every minute, grab two reliable peers, throw a db store at them, and measure their response time
the db store sent is their own, and we use tunnels both ways, so they wont know who we are.  we also mark the
success/failure of the tunnels accordingly
2004-06-29 19:45:26 +00:00
2fd87dc1f1 when we select peers to test, lets use all of the reliable peers, not just well integrated peers 2004-06-29 19:41:30 +00:00
40b6b77cfa use the median reliability value of nonfailing peers for the reliabilty threshold, and simplify determining them for the speed and integration 2004-06-29 19:40:08 +00:00
d0c61dbf4d use the explicit max ID values (I2NPMessage.MAX_ID_VALUE and TunnelId.MAX_ID_VALUE)
logging
2004-06-29 19:32:46 +00:00
1cd5a3fcf7 include the "addedBy" if we're debugging the job, not if we're debugging JobImpl 2004-06-29 19:29:57 +00:00
af81cf2c50 explcitly define the max I2NP message ID value and validate against it 2004-06-29 19:28:40 +00:00
04373c5d1b just to be explicit about the max tunnel id 2004-06-29 19:25:23 +00:00
mpc
e9cdb0f78d new library 2004-06-29 02:55:53 +00:00
mpc
021b933ad3 new library 2004-06-28 23:55:07 +00:00
mpc
9fd067c9da Putting socks/threads/logger/misc in a separate library 2004-06-28 23:24:50 +00:00
d3f3f3bdf7 use the socketManager's new setName function (to let us log more easily) 2004-06-28 13:23:24 +00:00
72727dacd8 javadoc 2004-06-28 13:22:03 +00:00
caeb2bc4e3 the actual fix for the local eepsite problem (if getRemoteID was called *after* the remoteID was set, it would wait for 60s then fail. now we check for that)
synchronization cleanup (never get two locks)
logging
2004-06-28 13:21:18 +00:00
13974b601f added some stats (viewable on the router stat page when the i2ptunnel is run in the router's VM)
lots of logging
2004-06-28 13:18:18 +00:00
cbc6aea8b4 logging 2004-06-27 21:20:31 +00:00
290a2f7ccd (ok this is a little silly) 2004-06-27 21:08:19 +00:00
349e80f206 allow sending ASAP and remove any artificial delays 2004-06-27 20:53:32 +00:00
5c1e001a73 logging 2004-06-27 19:39:45 +00:00
mpc
f312318fab Finished winsock code cleanup 2004-06-27 13:07:06 +00:00
mpc
dc04b7cf09 Some winsock improvements 2004-06-27 09:12:05 +00:00
77a8a46d8e lets try to reduce creating new objects during finalization 2004-06-26 21:15:51 +00:00
95c7cd55c2 logging 2004-06-26 21:15:16 +00:00
5f0ef5e0e8 lets not crap on the secondary tunnel too (even though doing so isn't wrong)
this helps avoid catastrophic failures, at least a little, since a failure doesnt kill two sets of tunnels
2004-06-26 21:13:52 +00:00
1f26c603e0 for now, lets disable the tunnel pool persistance. this means that after a router crashes, tunnels it was participating in will fail even if the router comes back up before they expire.
disabling this saves us some IO contention (though this may only be relevent on my kaffe box... dunno)
2004-06-26 21:11:22 +00:00
7e2227ad42 lets keep track of how many messages die on our queue due to us being slow 2004-06-26 21:07:07 +00:00
9b4899da07 always use the cached host/port rather than grabbing the socket's InetAddress (in case it disconnects and throws NPEs)
use the NativeBigInteger as part of the session key negotiation (oops, forgot this one last time)
logging
2004-06-26 21:05:02 +00:00
mpc
83c88ac0c6 minor fixes 2004-06-26 02:26:37 +00:00
mpc
44623065b4 Threads (untested) 2004-06-26 02:16:54 +00:00
mpc
47c7c8177d Mutex code (untested) 2004-06-25 22:22:48 +00:00
mpc
bde7a5ff59 Mutex code (untested) 2004-06-25 22:19:18 +00:00
a8ad8644c8 0.3.1.5 (backwards compatible)
lots of bugfixes.  still no rate limiting, but, uh, lots of bugfixes
(release will be packaged and deployed later today)
2004-06-25 19:25:33 +00:00
4e91bb88a5 workaround an aggressively up-to-spec kaffe implementation (the spec says Socket.getInetAddress() is null if not connected,
but sun lets the getInetAddress() return a value if it had connected then disconnected, while kaffe buggers off and NPEs)
2004-06-25 19:21:11 +00:00
f60a90e2da added forum.i2p 2004-06-25 18:48:33 +00:00
784dc0f6a7 boot up quicker 2004-06-25 18:42:27 +00:00
e80e627fba more tests with the real TCP transport, not just the VM comm system (and for larger sims, dont keepHistory) 2004-06-25 18:41:50 +00:00
d5987c51c9 yet another deployment option - the user can define a jbigi.ref environmental variable to specify a file from which the name of the resource to be loaded should be found (default is "jbigi.cfg")
if that file exists, the NativeBigInteger will act as if jbigi.impl was set to the contents of that file.
For instance, a jbigi.cfg containing "win-p4" would have the NativeBigInteger search the classpath for the "win-p4" file and use it as a native library.
The jbigi.ref preempts the jbigi.impl property (only if the file exists and is not empty), but the external platform specific jbigi
preempts this (e.g. jbigi.dll or libjbigi.so), as does the jbigi.enable flag.
This option lets us have the admin console write to a file to choose which jbigi to use, rather than have to parse some shell script, etc
2004-06-25 18:32:17 +00:00
5ced441b17 dont fail the peer based on tunnel activity (it may not be their fault)
we *do* still penalize the peer based on tunnel failures, but thats in the reliability calculator, not this one.
2004-06-25 18:15:32 +00:00
57801202fd flush the protocol flag explicitly
make the tcp connection handler nonblocking by adding another (very short lived) thread - this prevents a peer connecting to us that is very slow (or unconnectable) from forcing other cons to timeout
completely ripped out the fscking bandwidth limiter until i get it more reliable
gave threads more explicit names (for the sim)
logging
2004-06-25 18:14:12 +00:00
a019399c3c reduce synchronization on static (instead use per context objects, for large sims) 2004-06-25 17:21:41 +00:00
e6f610a86c dont synchronize on statics, instead use a seperate format object per context (so large sims dont get bogged down on synchronization) 2004-06-25 17:20:08 +00:00
7ef528bbde add some minimal security to the admin console, requiring a passphrase to be entered when updating the clock offset
this works by a simple substring match of the URL - if the router.config contains the adminTimePassphrase=blah, the time update will only succeed if the URL contains "blah" in it
if the router.config does NOT contain an adminTimePassphrase, the time update WILL BE REFUSED.
aka to use the timestamper, you MUST set adminTimePassphrase AND update the clientApp.0.args= line to include the passphrase in the URL!
e.g.
 clientApp.0.args=http://localhost:7655/setTime?blah pool.ntp.org pool.ntp.org pool.ntp.org
2004-06-25 17:18:21 +00:00
a351a29bf3 if it expired waiting on the queue for processing, kill 'er 2004-06-25 17:12:01 +00:00
983d258bce logging 2004-06-25 17:09:55 +00:00
f6d38dd5e0 reduce SimpleDateFormat usage (implicit in Date.toString()) 2004-06-25 17:03:13 +00:00
d51245aada logging 2004-06-25 17:02:22 +00:00
mpc
56cf51f0f9 New configuration system 2004-06-25 01:31:02 +00:00
mpc
eb40fb9c5d typo 2004-06-24 11:51:24 +00:00
mpc
085da0cea7 Started work on a configuration options object 2004-06-24 11:47:01 +00:00
mpc
5539b19938 Added a new example program (which actually works) 2004-06-23 23:35:39 +00:00
94feb762ca keep detailed info for the sim 2004-06-23 19:55:52 +00:00
40b59d5a5a more valid display of bw usage (but not as fresh) 2004-06-23 19:54:12 +00:00
9ffd147470 handle writing the stats before the period has been reached 2004-06-23 19:53:20 +00:00
3fea4ad2ba we dont need to use this fudge in this fashion (its done on the receiving end) 2004-06-23 19:51:58 +00:00
1ab5536879 la la la
(yeah, this is what broke cvs HEAD, causing transmission failures, disconnects, encryption errors, etc.  oops)
2004-06-23 19:50:41 +00:00
9690a89a6d sliices are only too slow if there's something pending
logging mods
i really need to rewrite the tcp transport - the code is all functional, but the design sucks.
with the FIFO bandwidth limiter we could get away with a single 'send' thread rather than each TCPConnection having its own writer thread (but we'd still need the per-con reader thread, at least until nio is solid enough)
but maybe the rewrite can hold off until the AMOC implementation.  we'll see
2004-06-23 19:48:25 +00:00
mpc
8f895f4349 just starting this (backup) 2004-06-23 12:34:50 +00:00
mpc
980c0aa1d7 Added PRNG code 2004-06-23 11:56:53 +00:00
mpc
52fd6ca513 Get rid of Debian-specific Makefile -- gcc < 3.0 is obsolete 2004-06-22 22:49:59 +00:00
mpc
eb5dd2ff2e Now it compiles on Cygwin 2004-06-22 22:48:32 +00:00
7ca35452eb new target - buildclean (== distclean build)
useful for my ide so i dont have to run two targets sequentially (and 'dist' goes one step further to include javadoc, which i dont need)
2004-06-22 04:56:44 +00:00
dd781e256c new constructor 2004-06-22 04:53:22 +00:00
551a7ab82f benchmarking large messages doesnt make much sense when we can compress the payload really really well, now does it? 2004-06-22 04:52:06 +00:00
2901287d9e new command line flags to harvest from an explicit file list rather than using all files in a single directory
(this lets us specify lots of my.info references to make sure we harvest fresh data, rather than depending upon stat propogation)
usage: NetMonitor [configFilename] [--routers filename[,filename]*]
2004-06-22 04:50:43 +00:00
2b714967aa 14yyp error 2004-06-22 04:44:17 +00:00
e8734ef1e7 more logging for shutdown info 2004-06-22 04:42:27 +00:00
14b9f9509f * allow the client subsystem to tell the clientMessagePool that a message is definitely remote (since the client subsystem should know). this reduces the churn of the message pool asking all over again
* add a new ClientWriterRunner thread (1 per I2CP connection) so that a client application that hangs or otherwise doesn't read from its i2cp socket quickly doesn't hang the whole router (since we've previously used the jobQueue for pushing I2CP messages).  This may or may not clear the intermittent eepsite bug, but I'm not counting on it to (yet).
* update various points to deal with the client writer's operation (aka doSend won't throw IOException)
* logging
* lots and lots of metrics (yeah i know some of them vary based on the compiler)
2004-06-22 04:41:31 +00:00
b1f973d304 during initial router startup, we may try to publish "my.info" before the netDb/ dir is created, so lets make sure 2004-06-22 04:31:25 +00:00
2f17bfd71c minor refactoring. i hate how large that method is, but beyond the essential stuff, its pretty much just logging and benchmarking.
plus, yeah, this method still takes too long in some situations.  working on identifying why...
2004-06-22 04:29:28 +00:00
b6670ee23a lets see how fast this can theoretically go (leaving simulated delays to other components) 2004-06-22 04:26:56 +00:00
f1036df1f6 new debugging data point 2004-06-22 04:25:24 +00:00
5c3e815757 dummy DSA (for sim - dont run on live net) 2004-06-22 04:23:19 +00:00
55e780d885 logging and doc of a todo (wrt bestEffort) 2004-06-22 04:16:17 +00:00
mpc
d502df7d56 Make this compile straight out of CVS (you have to have libtomcrypt compiled in a libtomcrypt-0.96 directory in your home directory) 2004-06-21 08:21:17 +00:00
mpc
beb6cc8c0f the journey of a thousand miles begins with a single step :-) 2004-06-21 07:57:11 +00:00
mpc
c99db5e75c change preprocessor conditional to check for 1/0 instead of true/false 2004-06-21 07:50:33 +00:00
mpc
65cd70a85b LibSAM 2004-06-20 11:44:53 +00:00
5166eab5ee replaced double check locking (http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html) with the actual logic
- prepare the cache prior to use if you want to have the hash cache.
also fix the ejection policy to not clear the cache, but merely to remove sufficient values.
though maybe clearing the cache is the right thing to do so as to avoid ejection churn... hmm.
both of these fixes brought to you by the keen eyes of the one called mihi
2004-06-20 04:27:58 +00:00
232f6f158d removed (likely already ignored by the compiler) op. force of habit from dealing with stream.read() i suppose.
thanks mihi
2004-06-20 03:48:16 +00:00
2a07ceba62 a message send failure is no reason to drop the SAM session
for style=stream, tell the client the stream failed, and kill the virtual connection..
for raw and repliable datagrams, just carry on our merry way
2004-06-20 01:49:07 +00:00
3e4b8c7dd4 more stats 2004-06-20 01:45:25 +00:00
26138e213f new method - processingComplete(), which functions much just like OutNetMessage's discardData()
so drop the data when called, updating the MessageStateMonitor (and also telling the monitor on finalization, just cuz)
2004-06-20 01:40:12 +00:00
d82796e3ad note that we've successfully processed a message (and as such drop its payload) ASAP, and only use safely cached snippets of it afterwards 2004-06-20 01:37:01 +00:00
cdcb81c867 dont be so aggressive about waking up more jobs, since this just causes excess locking when we dont need it 2004-06-20 01:34:16 +00:00
5669e8f060 deal with discarded payloads and use the cached version 2004-06-20 01:31:23 +00:00
d84a40b4dc add some randomization to the startup time, so we're not too synchronous
also don't shut down so quickly, as the routers may dump some useful stats when they die a horrible death
2004-06-20 01:29:00 +00:00
591be43763 default to building more tunnels, because tunnels r k00l
(and fix the arg parsing)
2004-06-20 01:26:59 +00:00
97d0686354 new method: discardData() to be called as soon as we dont need the payload of a message anymore (but may still need the associated jobs/etc)
check in with the MessageStateMonitor, and cache some key attributes from the message (type, unique id, size, etc)
2004-06-20 01:21:24 +00:00
e2da05b197 more accurrate (but less lively) bandwidth rate calculation (since we dont necessarily calculate exactly on the edge of a measurement period, we use the data from the last full period)
logging on OOM
2004-06-20 01:18:31 +00:00
4f0052043d /me waves to our new friend, the MessageStateMonitor, which keeps track of how many messages we're dealing with in memory (and whether they've been processed & discarded yet) 2004-06-20 01:15:01 +00:00
cfc1d1a2db publish some new stats, including the bandwidth usage (as always, routers can chose not to publish these stats) 2004-06-20 01:12:14 +00:00
9957e6ef17 keep track of how many messages are processed in the tunnel 2004-06-20 01:09:04 +00:00
6a02c8383c the data is hopefully discarded by now, so dont try to get at it 2004-06-20 01:02:03 +00:00
bc0a4ee68d discard the data ASAP, and make sure we access only the safely cached snippets of it as necessary 2004-06-20 00:59:33 +00:00
1ca615da77 InNetMessage gets a context 2004-06-20 00:57:02 +00:00
7da0cee29a turned BandwidthLimiter into an interface, removed some of its teeth, and cleaned up TrivialBandwidthLimiter 2004-06-20 00:56:04 +00:00
f25bccd19f add some stats for the simulator (data doesnt seem that interesting, so i havent moved them to the CommSystemImpl)
use the .discardData() functionality
2004-06-20 00:53:19 +00:00
4e5a2e012c update since bw limiter interface changed (but dont bother to use it anymore here)
i wonder if i should remove the phttp transport now or keep it around in case it gets revived...
2004-06-20 00:50:38 +00:00
c9ee2a92a3 dont buffer the reads, since we dont want that buffer to interfere with either the bandwidth limiting or the AES decryption
logging
2004-06-20 00:46:57 +00:00
95a7938328 reduce the max slice time (aka max time to pump out a message + some cleanup) to 60 seconds
close connections to peers who are so slow that they leave messages on the queue to expire
reduce the default max queue size per connection to 10 messages
(as always, this is a configurable param, via "i2np.tcp.maxQueuedMessages" in router.config)
2004-06-20 00:44:43 +00:00
baedcdb2c1 handle situations where people dont specify a client name for a client app 2004-06-20 00:40:16 +00:00
bc06b3671a whenever a tunnel completes, log how many messages we passed through it in the stats:
tunnel.inboundMessagesProcessed
tunnel.outboundMessagesProcessed
tunnel.participatingMessagesProcessed
(for the various tunnel types)
2004-06-20 00:35:52 +00:00
a9172811ca reduce the grace period from 5 to 2 minutes, which will cause us to test peers more often
also add some logging (log level == debug will display why we mark a peer as failing)
2004-06-20 00:31:20 +00:00
91b1fd6d07 InNetMessage now needs a reference to a context, so give it one 2004-06-20 00:26:05 +00:00
bab7b8b9ed discard the payload of a message ASAP (even though we may need to hang on to the message for a while, for its replyJob, etc)
take note of the fact that the tunnel had activity
minor logging and formatting updates
2004-06-20 00:24:06 +00:00
1b7fb96ca8 dont expose a method we dont need to expose 2004-06-20 00:19:38 +00:00
6d84b8c02f 1) use cachedXor to cut down on the, uh, xor-ing (which involves at least one new byte[32])
2) implement an optimized 'should contain' algorithm, rather than being a wuss and building + comparing a BigInteger of the xor.
3) more unit tests
this stuff is called a *lot*, since we need to pick what bucket things go in all the time.
2004-06-20 00:18:28 +00:00
3e3749f011 added some unit tests (adding the local key (delta == 0x00) and adding 1000 random keys, all making sure nothing b0rks) 2004-06-20 00:13:05 +00:00
52384fb3a5 persist the local router's info @ netDb/my.info in addition to under netDb/routerInfo-$hash.dat
this makes it easy for harvesting with simulations
2004-06-20 00:11:23 +00:00
e401670087 i like logging, dont you like logging? 2004-06-20 00:08:59 +00:00
ae0b4c59cc include the port # in the thread name, and logging 2004-06-20 00:07:37 +00:00
0a8dc8afcc logging, its whats for dinner 2004-06-20 00:06:33 +00:00
d59b94df66 logging, deal with times when a client doesnt have a destination yet 2004-06-20 00:05:30 +00:00
e28502454b include the port in the thread name (useful for the sim) 2004-06-20 00:03:45 +00:00
bbf73f0937 enforce some sanity checks on the payload size. see recent rant in DatabaseSearchReplyMessage commit for why
this is necessary.
2004-06-19 23:58:24 +00:00
592519c45c somehow some people are getting situations where the payload doesnt decompress. wtf?
do i need to wrap the Input/Output streams we use to pipe data over the net with a verification wrapper for the messages?
e.g. prefix the serialization of all I2NPMessages sent on the wire with the SHA256 of that serialization and verify on read?
Ho hum, dunno.  maybe its something else, but the ElG/AES+SessionTag already has integrity verification so the only thing I can
think of is a checksum error that got past TCP's checking and corrupted the AES stream.
2004-06-19 23:56:41 +00:00
cc904ba9dc use new SessionConfig constructor 2004-06-19 23:46:08 +00:00
1679ba6719 so this String.getBytes(), its inefficient? you don't say...
(yeah, i know, this optimization takes advantage of the fact that the data in question uses single byte charsets)
2004-06-19 23:41:55 +00:00
07fadd4a6c avoid string.getBytes like the plague 2004-06-19 23:40:03 +00:00
57e1ff39e0 new method: cachedXor which, suprisingly, determines the xor of a hash against another hash, caching up to a certain number of values
currently uses an essentially random ejection policy, but this saves a lot of temporarly memory churn, since we xor many hashes against a router/destination's key
2004-06-19 23:35:43 +00:00
51e259c198 avoiding the String.getBytes() since its a bitch on gc (measured for this situation) 2004-06-19 23:32:41 +00:00
de334b003d for safety, always create a session config with a destination 2004-06-19 23:30:57 +00:00
a61ff12390 more microoptimizations, whee! 2004-06-19 23:28:12 +00:00
f4697be159 just a simple catch all for OOM while handling an OOM (naw, we dont recurse too much (just a little)) 2004-06-19 23:25:47 +00:00
3835fe3960 give the shutdown hook thread a name 2004-06-19 23:13:09 +00:00
76c374ef06 more accurate memory usage to reduce gc churn 2004-06-19 23:11:42 +00:00
57d24bd948 read the logger.config when its been updated, even if we dont have any log messages to write out (duh)
also, a micro-optimization for charset handling identified through profiling
2004-06-19 23:02:59 +00:00
deff14dfd8 lets see if this fixes bug 66 (http://dev.i2p.net/bugzilla/show_bug.cgi?id=66) 2004-06-13 20:27:44 +00:00
0a479be370 include NAME=val in failed lookup replies (per spec - thanks nightblade)
fixes http://dev.i2p.net/bugzilla/show_bug.cgi?id=79
2004-06-13 20:19:16 +00:00
ba6a2e3fd2 unit test for the bandwidth limiting functionality (TrivialBandwidthLimiter, BandwidthLimiter, BandwidthLimited{In,Out}putStream) 2004-06-13 20:03:21 +00:00
c3a395a41e update the bandwidth limiter config properties 2004-06-13 19:59:44 +00:00
a3136a19e9 big ol' rewrite, requiring new config settings and, er, it works pretty well.
see router.config.template mods and the new unit tests.
this implementation can cause starvation -
  e.g. lots of 1KB writes will go through before a 32KB write if the queue is low and the bwlimiter only replenishes say, 16KBps
  another impl would enforce a FIFO through thread wait/notify, etc, but would have the related overhead.
i dont know whether starvation situations will be the norm or the exception.  i'm running this on a few routers so we'll see.
2004-06-13 19:56:57 +00:00
b631568003 deal with null routers 2004-06-13 19:48:23 +00:00
1d0c03eca4 use the router context's properties (which now include the config settings) 2004-06-13 19:47:44 +00:00
eb30525a26 deal with null routers (useful for testing) 2004-06-13 19:47:02 +00:00
3e66ea3f56 include the router's config in the property settings 2004-06-13 19:45:53 +00:00
9f1189e606 use a bandwidth limited stream instead of asking for the allocation of the entire buffer at once (since, uh, its not likely that the bandwidth limiter will ever have hundreds of KBytes available for use) 2004-06-13 19:39:42 +00:00
698927bed4 logging 2004-06-13 19:37:18 +00:00
8fd02ee8dd allow the stream to optionally pull from the output stream's bandwidth limit queue (useful in very strange situations) 2004-06-13 19:36:41 +00:00
f3154e8f5e buffer between the bandwidth limiter and the raw stream 2004-06-13 19:34:46 +00:00
878af163a9 handle null boolean value (legal, but not in this context), fixes bug reported by nickster 2004-06-13 19:32:58 +00:00
da8341d014 reduced buffer size and updated domain name reference (dev.i2p.net, not i2p.dyndns.net) 2004-06-13 19:31:22 +00:00
95c33e88ed handle decompress error by propogating the IOE (thanks nickster for bug report) 2004-06-13 19:30:31 +00:00
fed8369a5f big rewrite of how we load the native implementation:
- the old method is default: it looks for jbigi.dll / libjbigi.so in the current dir (and library path)
- otherwise, you can specify -Djbigi.impl=blah and if there is a file called "blah" in the classpath (including any jar file), it will load that file as a native implementation
there are lots more javadocs now as well, and the dependency upon Log was removed (so anyone else who wants to use this just needs NativeBigInteger.java and the jbigi implementation)
2004-06-12 20:08:32 +00:00
d85806e3d5 added ferret.i2p
(duck)
2004-06-09 20:06:17 +00:00
097660ce53 Round-robin DNS NTP pool makes sense as default
Discussed at http://www.i2p.net/node/view/226 (thanks Nightblade)
(duck)
2004-06-09 14:30:26 +00:00
b08cfbbb35 we now have a website & eepproxy is launched automatically
(duck)
2004-06-09 14:27:51 +00:00
97ee3c47a0 added anonynanny.i2p
(duck)
2004-06-01 16:57:09 +00:00
05918de6ab fix bug #75
the databuffer is reused so it might contain junk, therefore only use the
available amount of bytes
(duck)
2004-05-26 12:25:04 +00:00
8d7abd8298 added hypercubus.i2p 2004-05-24 12:53:41 +00:00
727f4c3bb5 Another rfc2068 fix.
ERR_REQUEST_DENIED now returns a 403 status code. Note that I'm assuming
this error occurs because the proxy is configured to not allow connection
to non-i2p destinations. If it's issued for other reasons we might want
to consider using 503 instead.
2004-05-24 07:28:24 +00:00
7372ad0cc4 A few changes to make proxy return the proper rfc2068 status codes
instead of 404 for everything.

ERR_DESTINATION_UNKNOWN now returns 503 instead of 404 with a slightly
more clear error message.

ERR_TIMEOUT now returns 504 gateway timeout.
2004-05-24 07:08:02 +00:00
ca6884dbca imports (sorry, includes alphabetizing, wee)
(shendaras)
2004-05-24 03:21:21 +00:00
866 changed files with 88537 additions and 27332 deletions

View File

@ -0,0 +1,43 @@
addressbook v2.0.2 - A simple name resolution mechanism for I2P
addressbook is a simple implementation of subscribable address books for I2P.
Addresses are stored in userhosts.txt and a second copy of the address book is
placed on your eepsite as hosts.txt.
subscriptions.txt contains a list of urls to check for new addresses.
Since the urls are checked in order, and conflicting addresses are not added,
addressbook.subscriptions can be considered to be ranked in order of trust.
The system created by addressbook is similar to the early days of DNS,
when everyone ran a local name server. The major difference is the lack of
authority. Name cannot be guaranteed to be globally unique, but in practise
they probably will be, for a variety of social reasons.
Requirements
************
i2p with a running http proxy
Installation and Usage
**********************
1. Unzip addressbook-%ver.zip into your i2p directory.
2. Restart your router.
The addressbook daemon will automatically run while the router is up.
Aside from the daemon itself, the other elements of the addressbook interface
are the config.txt, myhosts.txt, and subscriptions.txt files found in the addressbook
directory.
config.txt is the configuration file for addressbook.
myhosts.txt is the addressbook master address book. Addresses placed in this file
take precidence over those in the router address book and in remote address books.
If changes are made to this file, they will be reflected in the router address book
and published address book after the next update. Do not make changes directly to the
router address book, as they could be lost during an update.
subscriptions.txt is the subscription list for addressbook. Each entry is an absolute
url to a file in hosts.txt format. Since the list is checked in order, url's should be
listed in order of trust.

View File

@ -0,0 +1,47 @@
<?xml version="1.0"?>
<project name="addressbook" default="war" basedir=".">
<property name="src" value="java/src/addressbook"/>
<property name="build" value="build"/>
<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}"/>
</target>
<target name="clean">
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
<target name="distclean" depends="clean" />
<target name="compile" depends="init">
<javac debug="true" deprecation="on" source="1.3" target="1.3"
srcdir="${src}" destdir="${build}" classpath="${servlet}"/>
</target>
<target name="jar" depends="compile">
<jar basedir="${build}" destfile="${dist}/${jar}">
<manifest>
<attribute name="Main-Class" value="addressbook.Daemon"/>
</manifest>
</jar>
</target>
<target name="war" depends="compile">
<mkdir dir="${dist}/tmp"/>
<mkdir dir="${dist}/tmp/WEB-INF"/>
<mkdir dir="${dist}/tmp/WEB-INF/classes"/>
<copy todir="${dist}/tmp/WEB-INF/classes">
<fileset dir="${build}"/>
</copy>
<war basedir="${dist}/tmp" webxml="web.xml" destfile="${dist}/${war}"/>
<delete dir="${dist}/tmp"/>
</target>
</project>

View File

@ -0,0 +1,43 @@
# This is the configuration file for addressbook.
#
# Options
# *******
# All paths are realitive to i2p/addressbook. Default value for
# each option is given in parentheses.
#
# proxy_host The hostname of your I2P http proxy.
# (localhost)
#
# proxy_port The port of your I2P http proxy. (4444)
#
# master_addressbook The path to your master address book, used for local
# changes only. (myhosts.txt)
#
# router_addressbook The path to the address book used by the router.
# Contains the addresses from your master address book
# and your subscribed address books. (../userhosts.txt)
#
# published_addressbook The path to the copy of your address book made
# available on i2p. (../eepsite/docroot/hosts.txt)
#
# log The path to your addressbook log. (log.txt)
#
# subscriptions The path to your subscription file. (subscriptions.txt)
#
# etags The path to the etags header storage file. (etags)
#
# last_modified The path to the last-modified header storage file.
# (last_modified)
#
# update_delay The time (in hours) between each update. (1)
proxy_host=localhost
proxy_port=4444
master_addressbook=myhosts.txt
router_addressbook=../userhosts.txt
published_addressbook=../eepsite/docroot/hosts.txt
log=log.txt
subscriptions=subscriptions.txt
etags=etags
last_modified=last_modified
update_delay=1

View File

@ -0,0 +1,246 @@
/*
* Copyright (c) 2004 Ragnarok
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
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;
/**
* An address book for storing human readable names mapped to base64 i2p
* destinations. AddressBooks can be created from local and remote files, merged
* together, and written out to local files.
*
* @author Ragnarok
*
*/
public class AddressBook {
private String location;
private Map addresses;
private boolean modified;
/**
* Construct an AddressBook from the contents of the Map addresses.
*
* @param addresses
* A Map containing human readable addresses as keys, mapped to
* base64 i2p destinations.
*/
public AddressBook(Map addresses) {
this.addresses = addresses;
}
/**
* Construct an AddressBook from the contents of the file at url. If the
* remote file cannot be read, construct an empty AddressBook
*
* @param url
* A URL pointing at a file with lines in the format "key=value",
* where key is a human readable name, and value is a base64 i2p
* destination.
*/
public AddressBook(URL url) {
this.location = url.getHost();
try {
this.addresses = ConfigParser.parse(url);
} catch (IOException exp) {
this.addresses = new HashMap();
}
}
/**
* Construct an AddressBook from the Subscription subscription. If the
* address book at subscription has not changed since the last time it was
* read or cannot be read, return an empty AddressBook.
*
* @param subscription
* A Subscription instance pointing at a remote address book.
*/
public AddressBook(Subscription subscription) {
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()));
} catch (IOException exp) {
this.addresses = new HashMap();
}
}
/**
* Construct an AddressBook from the contents of the file at file. If the
* file cannot be read, construct an empty AddressBook
*
* @param file
* A File pointing at a file with lines in the format
* "key=value", where key is a human readable name, and value is
* a base64 i2p destination.
*/
public AddressBook(File file) {
this.location = file.toString();
try {
this.addresses = ConfigParser.parse(file);
} catch (IOException exp) {
this.addresses = new HashMap();
}
}
/**
* Return a Map containing the addresses in the AddressBook.
*
* @return A Map containing the addresses in the AddressBook, where the key
* is a human readable name, and the value is a base64 i2p
* destination.
*/
public Map getAddresses() {
return this.addresses;
}
/**
* Return the location of the file this AddressBook was constructed from.
*
* @return A String representing either an abstract path, or a url,
* depending on how the instance was constructed.
*/
public String getLocation() {
return this.location;
}
/**
* Return a string representation of the contents of the AddressBook.
*
* @return A String representing the contents of the AddressBook.
*/
public String toString() {
return this.addresses.toString();
}
/**
* Merge this AddressBook with AddressBook other, writing messages about new
* addresses or conflicts to log. Addresses in AddressBook other that are
* not in this AddressBook are added to this AddressBook. In case of a
* conflict, addresses in this AddressBook take precedence
*
* @param other
* An AddressBook to merge with.
* @param log
* The log to write messages about new addresses or conflicts to.
*/
public void merge(AddressBook other, Log log) {
Iterator otherIter = other.addresses.keySet().iterator();
while (otherIter.hasNext()) {
String otherKey = (String) otherIter.next();
String otherValue = (String) other.addresses.get(otherKey);
if (otherKey.endsWith(".i2p") && otherValue.length() >= 516) {
if (this.addresses.containsKey(otherKey)) {
if (!this.addresses.get(otherKey).equals(otherValue)
&& log != null) {
log.append("Conflict for " + otherKey + " from "
+ other.location
+ ". Destination in remote address book is "
+ otherValue);
}
} else {
this.addresses.put(otherKey, otherValue);
this.modified = true;
if (log != null) {
log.append("New address " + otherKey
+ " 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.
*
* @param file
* The file to write the contents of this AddressBook too.
*/
public void write(File file) {
if (this.modified) {
try {
ConfigParser.write(this.addresses, file);
} catch (IOException exp) {
}
}
}
/**
* Write this AddressBook out to the file it was read from. Requires that
* AddressBook was constructed from a file on the local filesystem. If the
* file cannot be writen to, this method will silently fail.
*/
public void write() {
this.write(new File(this.location));
}
}

View File

@ -0,0 +1,323 @@
/*
* Copyright (c) 2004 Ragnarok
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package addressbook;
import java.util.HashMap;
import java.util.Map;
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
* format, and subscription file format.
*
* @author Ragnarok
*/
public class ConfigParser {
/**
* Strip the comments from a String. Lines that begin with '#' and ';' are
* considered comments, as well as any part of a line after a '#'.
*
* @param inputLine
* A String to strip comments from.
* @return A String without comments, but otherwise identical to inputLine.
*/
public static String stripComments(String inputLine) {
if (inputLine.startsWith(";")) {
return "";
}
if (inputLine.split("#").length > 0) {
return inputLine.split("#")[0];
} else {
return "";
}
}
/**
* Return a Map using the contents of BufferedReader input. input must have
* a single key, value pair on each line, in the format: key=value. Lines
* starting with '#' or ';' are considered comments, and ignored. Lines that
* are obviously not in the format key=value are also ignored.
*
* @param input
* A BufferedReader with lines in key=value format to parse into
* a Map.
* @return A Map containing the key, value pairs from input.
* @throws IOException
* if the BufferedReader cannot be read.
*
*/
public static Map parse(BufferedReader input) throws IOException {
Map result = new HashMap();
String inputLine;
inputLine = input.readLine();
while (inputLine != null) {
inputLine = ConfigParser.stripComments(inputLine);
String[] splitLine = inputLine.split("=");
if (splitLine.length == 2) {
result.put(splitLine[0].trim(), splitLine[1].trim());
}
inputLine = input.readLine();
}
input.close();
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.
*
* @param file
* A File to parse.
* @return A Map containing the key, value pairs from file.
* @throws IOException
* if file cannot be read.
*/
public static Map parse(File file) throws IOException {
FileInputStream fileStream = new FileInputStream(file);
BufferedReader input = new BufferedReader(new InputStreamReader(
fileStream));
return ConfigParser.parse(input);
}
/**
* Return a Map using the contents of the String string. See
* parseBufferedReader for details of the input format.
*
* @param string
* A String to parse.
* @return A Map containing the key, value pairs from string.
* @throws IOException
* if file cannot be read.
*/
public static Map parse(String string) throws IOException {
StringReader stringReader = new StringReader(string);
BufferedReader input = new BufferedReader(stringReader);
return ConfigParser.parse(input);
}
/**
* Return a Map using the contents of the File file. If file cannot be read,
* use map instead, and write the result to where file should have been.
*
* @param file
* A File to attempt to parse.
* @param map
* A Map to use as the default, if file fails.
* @return A Map containing the key, value pairs from file, or if file
* cannot be read, map.
*/
public static Map parse(File file, Map map) {
Map result = new HashMap();
try {
result = ConfigParser.parse(file);
} catch (IOException exp) {
result = map;
try {
ConfigParser.write(result, file);
} catch (IOException exp2) {
}
}
return result;
}
/**
* Return a List where each element is a line from the BufferedReader input.
*
* @param input
* A BufferedReader to parse.
* @return A List consisting of one element for each line in input.
* @throws IOException
* if input cannot be read.
*/
public static List parseSubscriptions(BufferedReader input)
throws IOException {
List result = new LinkedList();
String inputLine = input.readLine();
while (inputLine != null) {
inputLine = ConfigParser.stripComments(inputLine).trim();
if (inputLine.length() > 0) {
result.add(inputLine);
}
inputLine = input.readLine();
}
input.close();
return result;
}
/**
* Return a List where each element is a line from the File file.
*
* @param file
* A File to parse.
* @return A List consisting of one element for each line in file.
* @throws IOException
* if file cannot be read.
*/
public static List parseSubscriptions(File file) throws IOException {
FileInputStream fileStream = new FileInputStream(file);
BufferedReader input = new BufferedReader(new InputStreamReader(
fileStream));
return ConfigParser.parseSubscriptions(input);
}
/**
* Return a List where each element is a line from the String string.
*
* @param string
* A String to parse.
* @return A List consisting of one element for each line in string.
* @throws IOException
* if string cannot be read.
*/
public static List parseSubscriptions(String string) throws IOException {
StringReader stringReader = new StringReader(string);
BufferedReader input = new BufferedReader(stringReader);
return ConfigParser.parseSubscriptions(input);
}
/**
* Return a List using the contents of the File file. If file cannot be
* read, use list instead, and write the result to where file should have
* been.
*
* @param file
* A File to attempt to parse.
* @param string
* A List to use as the default, if file fails.
* @return A List consisting of one element for each line in file, or if
* file cannot be read, list.
*/
public static List parseSubscriptions(File file, List list) {
List result = new LinkedList();
try {
result = ConfigParser.parseSubscriptions(file);
} catch (IOException exp) {
result = list;
try {
ConfigParser.writeSubscriptions(result, file);
} catch (IOException exp2) {
}
}
return result;
}
/**
* Write contents of Map map to BufferedWriter output. Output is written
* with one key, value pair on each line, in the format: key=value.
*
* @param map
* A Map to write to output.
* @param output
* A BufferedWriter to write the Map to.
* @throws IOException
* if the BufferedWriter cannot be written to.
*/
public static void write(Map map, BufferedWriter output) throws IOException {
Iterator keyIter = map.keySet().iterator();
while (keyIter.hasNext()) {
String key = (String) keyIter.next();
output.write(key + "=" + (String) map.get(key));
output.newLine();
}
output.close();
}
/**
* Write contents of Map map to the File file. Output is written
* with one key, value pair on each line, in the format: key=value.
*
* @param map
* A Map to write to file.
* @param file
* A File to write the Map to.
* @throws IOException
* if file cannot be written to.
*/
public static void write(Map map, File file) throws IOException {
ConfigParser
.write(map, new BufferedWriter(new FileWriter(file, false)));
}
/**
* Write contents of List list to BufferedReader output. Output is written
* with each element of list on a new line.
*
* @param list
* A List to write to file.
* @param output
* A BufferedReader to write list to.
* @throws IOException
* if output cannot be written to.
*/
public static void writeSubscriptions(List list, BufferedWriter output)
throws IOException {
Iterator iter = list.iterator();
while (iter.hasNext()) {
output.write((String) iter.next());
output.newLine();
}
output.close();
}
/**
* Write contents of List list to File file. Output is written with each
* element of list on a new line.
*
* @param list
* A List to write to file.
* @param file
* A File to write list to.
* @throws IOException
* if output cannot be written to.
*/
public static void writeSubscriptions(List list, File file)
throws IOException {
ConfigParser.writeSubscriptions(list, new BufferedWriter(
new FileWriter(file, false)));
}
}

View File

@ -0,0 +1,174 @@
/*
* Copyright (c) 2004 Ragnarok
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package addressbook;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.LinkedList;
import java.io.File;
/**
* Main class of addressbook. Performs updates, and runs the main loop.
*
* @author Ragnarok
*
*/
public class Daemon {
public static final String VERSION = "2.0.3";
/**
* Update the router and published address books using remote data from the
* subscribed address books listed in subscriptions.
*
* @param master
* The master AddressBook. This address book is never
* overwritten, so it is safe for the user to write to.
* @param router
* The router AddressBook. This is the address book read by
* client applications.
* @param published
* The published AddressBook. This address book is published on
* the user's eepsite so that others may subscribe to it.
* @param subscriptions
* A SubscriptionList listing the remote address books to update
* from.
* @param log
* The log to write changes and conflicts to.
*/
public static void update(AddressBook master, AddressBook router,
File published, SubscriptionList subscriptions, Log log) {
String routerLocation = router.getLocation();
master.merge(router);
Iterator iter = subscriptions.iterator();
while (iter.hasNext()) {
master.merge((AddressBook) iter.next(), log);
}
master.write(new File(routerLocation));
if (published != null)
master.write(published);
subscriptions.write();
}
/**
* Run an update, using the Map settings to provide the parameters.
*
* @param settings
* A Map containg the parameters needed by update.
* @param home
* The directory containing addressbook's configuration files.
*/
public static void update(Map settings, String home) {
File masterFile = new File(home, (String) settings
.get("master_addressbook"));
File routerFile = new File(home, (String) settings
.get("router_addressbook"));
File published = null;
if ("true".equals(settings.get("should_publish")))
published = new File(home, (String) settings
.get("published_addressbook"));
File subscriptionFile = new File(home, (String) settings
.get("subscriptions"));
File logFile = new File(home, (String) settings.get("log"));
File etagsFile = new File(home, (String) settings.get("etags"));
File lastModifiedFile = new File(home, (String) settings
.get("last_modified"));
AddressBook master = new AddressBook(masterFile);
AddressBook router = new AddressBook(routerFile);
List defaultSubs = new LinkedList();
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);
Log log = new Log(logFile);
Daemon.update(master, router, published, subscriptions, log);
}
/**
* Load the settings, set the proxy, then enter into the main loop. The main
* loop performs an immediate update, and then an update every number of
* hours, as configured in the settings file.
*
* @param args
* Command line arguments. If there are any arguments provided,
* the first is taken as addressbook's home directory, and the
* others are ignored.
*/
public static void main(String[] args) {
String settingsLocation = "config.txt";
Map settings = new HashMap();
String home;
if (args.length > 0) {
home = args[0];
} else {
home = ".";
}
Map defaultSettings = new HashMap();
defaultSettings.put("proxy_host", "localhost");
defaultSettings.put("proxy_port", "4444");
defaultSettings.put("master_addressbook", "../userhosts.txt");
defaultSettings.put("router_addressbook", "../hosts.txt");
defaultSettings.put("published_addressbook", "../eepsite/docroot/hosts.txt");
defaultSettings.put("should_publish", "false");
defaultSettings.put("log", "log.txt");
defaultSettings.put("subscriptions", "subscriptions.txt");
defaultSettings.put("etags", "etags");
defaultSettings.put("last_modified", "last_modified");
defaultSettings.put("update_delay", "1");
File homeFile = new File(home);
if (!homeFile.exists()) {
boolean created = homeFile.mkdirs();
if (created)
System.out.println("INFO: Addressbook directory " + homeFile.getName() + " created");
else
System.out.println("ERROR: Addressbook directory " + homeFile.getName() + " could not be created");
}
File settingsFile = new File(homeFile, settingsLocation);
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;
}
Daemon.update(settings, home);
try {
Thread.sleep(delay * 60 * 60 * 1000);
} catch (InterruptedException exp) {
}
}
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2004 Ragnarok
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package addressbook;
/**
* A thread that waits five minutes, then runs the addressbook daemon.
*
* @author Ragnarok
*
*/
public class DaemonThread extends Thread {
private String[] args;
/**
* Construct a DaemonThread with the command line arguments args.
* @param args
* A String array to pass to Daemon.main().
*/
public DaemonThread(String[] args) {
this.args = args;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() {
try {
Thread.sleep(5 * 60 * 1000);
} catch (InterruptedException exp) {
}
Daemon.main(this.args);
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2004 Ragnarok
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package addressbook;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;
/**
* A simple log with automatic time stamping.
*
* @author Ragnarok
*
*/
public class Log {
private File file;
/**
* Construct a Log instance that writes to the File file.
*
* @param file
* A File for the log to write to.
*/
public Log(File file) {
this.file = file;
}
/**
* Write entry to a new line in the log, with appropriate time stamp.
*
* @param entry
* A String containing a message to append to the log.
*/
public void append(String entry) {
try {
BufferedWriter bw = new BufferedWriter(new FileWriter(this.file,
true));
String timestamp = new Date().toString();
bw.write(timestamp + " -- " + entry);
bw.newLine();
bw.close();
} catch (IOException exp) {
}
}
/**
* Return the File that the Log is writing to.
*
* @return The File that the log is writing to.
*/
public File getFile() {
return this.file;
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2004 Ragnarok
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package addressbook;
import javax.servlet.GenericServlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
/**
* A wrapper for addressbook to allow it to be started as a web application.
*
* @author Ragnarok
*
*/
public class Servlet extends GenericServlet {
/* (non-Javadoc)
* @see javax.servlet.Servlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
*/
public void service(ServletRequest request, ServletResponse response) {
}
/* (non-Javadoc)
* @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
*/
public void init(ServletConfig config) {
try {
super.init(config);
} catch (ServletException exp) {
}
String[] args = new String[1];
args[0] = config.getInitParameter("home");
DaemonThread thread = new DaemonThread(args);
thread.setDaemon(true);
thread.start();
System.out.println("INFO: Starting Addressbook " + Daemon.VERSION);
System.out.println("INFO: config root under " + args[0]);
}
}

View File

@ -0,0 +1,105 @@
/*
* Copyright (c) 2004 Ragnarok
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package addressbook;
/**
* A subscription to a remote address book.
*
* @author Ragnarok
*
*/
public class Subscription {
private String location;
private String etag;
private String lastModified;
/**
* Construct a Subscription pointing to the address book at location, that
* was last read at the time represented by etag and lastModified.
*
* @param location
* A String representing a url to a remote address book.
* @param etag
* The etag header that we recieved the last time we read this
* subscription.
* @param lastModified
* the last-modified header we recieved the last time we read
* this subscription.
*/
public Subscription(String location, String etag, String lastModified) {
this.location = location;
this.etag = etag;
this.lastModified = lastModified;
}
/**
* Return the location this Subscription points at.
*
* @return A String representing a url to a remote address book.
*/
public String getLocation() {
return this.location;
}
/**
* Return the etag header that we recieved the last time we read this
* subscription.
*
* @return A String containing the etag header.
*/
public String getEtag() {
return this.etag;
}
/**
* Set the etag header.
*
* @param etag
* A String containing the etag header.
*/
public void setEtag(String etag) {
this.etag = etag;
}
/**
* Return the last-modified header that we recieved the last time we read
* this subscription.
*
* @return A String containing the last-modified header.
*/
public String getLastModified() {
return this.lastModified;
}
/**
* Set the last-modified header.
*
* @param lastModified
* A String containing the last-modified header.
*/
public void setLastModified(String lastModified) {
this.lastModified = lastModified;
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2004 Ragnarok
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package addressbook;
import java.util.Iterator;
import java.util.List;
/**
* An iterator over the subscriptions in a SubscriptionList. Note that this iterator
* returns AddressBook objects, and not Subscription objects.
*
* @author Ragnarok
*/
public class SubscriptionIterator implements Iterator {
private Iterator subIterator;
/**
* Construct a SubscriptionIterator using the Subscriprions in List subscriptions.
*
* @param subscriptions
* List of Subscription objects that represent address books.
*/
public SubscriptionIterator(List subscriptions) {
this.subIterator = subscriptions.iterator();
}
/* (non-Javadoc)
* @see java.util.Iterator#hasNext()
*/
public boolean hasNext() {
return subIterator.hasNext();
}
/* (non-Javadoc)
* @see java.util.Iterator#next()
*/
public Object next() {
Subscription sub = (Subscription) subIterator.next();
return new AddressBook(sub);
}
/* (non-Javadoc)
* @see java.util.Iterator#remove()
*/
public void remove() {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright (c) 2004 Ragnarok
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package addressbook;
import java.util.List;
import java.util.LinkedList;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.io.File;
import java.io.IOException;
/**
* A list of Subscriptions loaded from a file.
*
* @author Ragnarok
*
*/
public class SubscriptionList {
private List subscriptions;
private File etagsFile;
private File lastModifiedFile;
/**
* Construct a SubscriptionList using the urls from locationsFile and, if
* available, the etags and last-modified headers loaded from etagsFile and
* lastModifiedFile.
*
* @param locationsFile
* A file containing one url on each line.
* @param etagsFile
* A file containg the etag headers used for conditional GET. The
* file is in the format "url=etag".
* @param lastModifiedFile
* A file containg the last-modified headers used for conditional
* GET. The file is in the format "url=leastmodified".
*/
public SubscriptionList(File locationsFile, File etagsFile,
File lastModifiedFile, List defaultSubs) {
this.subscriptions = new LinkedList();
this.etagsFile = etagsFile;
this.lastModifiedFile = lastModifiedFile;
List locations;
Map etags;
Map lastModified;
String location;
locations = ConfigParser.parseSubscriptions(locationsFile, defaultSubs);
try {
etags = ConfigParser.parse(etagsFile);
} catch (IOException exp) {
etags = new HashMap();
}
try {
lastModified = ConfigParser.parse(lastModifiedFile);
} catch (IOException exp) {
lastModified = new HashMap();
}
Iterator iter = locations.iterator();
while (iter.hasNext()) {
location = (String) iter.next();
subscriptions.add(new Subscription(location, (String) etags
.get(location), (String) lastModified.get(location)));
}
iter = this.iterator();
}
/**
* Return an iterator over the AddressBooks represented by the Subscriptions
* in this SubscriptionList.
*
* @return A SubscriptionIterator.
*/
public SubscriptionIterator iterator() {
return new SubscriptionIterator(this.subscriptions);
}
/**
* Write the etag and last-modified headers for each Subscription to files.
*/
public void write() {
Iterator iter = this.subscriptions.iterator();
Subscription sub;
Map etags = new HashMap();
Map lastModified = new HashMap();
while (iter.hasNext()) {
sub = (Subscription) iter.next();
if (sub.getEtag() != null) {
etags.put(sub.getLocation(), sub.getEtag());
}
if (sub.getLastModified() != null) {
lastModified.put(sub.getLocation(), sub.getLastModified());
}
}
try {
ConfigParser.write(etags, this.etagsFile);
ConfigParser.write(lastModified, this.lastModifiedFile);
} catch (IOException exp) {
}
}
}

View File

@ -0,0 +1,10 @@
# addressbook master address book. Addresses placed in this file take precidence
# over those in the router address book and in remote address books. If changes
# are made to this file, they will be reflected in the router address book and
# published address book after the next update.
#
# Do not make changes directly to the router address book, as they could be lost
# during an update.
#
# This file takes addresses in the hosts.txt format, i.e.
# example.i2p=somereallylongbase64thingAAAA

View File

@ -0,0 +1,7 @@
# Subscription list for addressbook
#
# Each entry is an absolute url to a file in hosts.txt format.
# Since the list is checked in order, url's should be listed in order of trust.
#
http://dev.i2p/i2p/hosts.txt
http://duck.i2p/hosts.txt

16
apps/addressbook/web.xml Normal file
View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app>
<servlet>
<servlet-name>addressbook</servlet-name>
<servlet-class>addressbook.Servlet</servlet-class>
<init-param>
<param-name>home</param-name>
<param-value>./addressbook</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
</web-app>

349
apps/bogobot/Bogobot.java Normal file
View File

@ -0,0 +1,349 @@
/*
* bogobot - A simple join/part stats logger bot for I2P IRC.
*
* Bogobot.java
* 2004 The I2P Project
* http://www.i2p.net
* This code is public domain.
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Properties;
import org.apache.log4j.DailyRollingFileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.jibble.pircbot.IrcException;
import org.jibble.pircbot.NickAlreadyInUseException;
import org.jibble.pircbot.PircBot;
import org.jibble.pircbot.User;
/**
* TODO 0.5 Add multi-server capability.
*
* @author hypercubus, oOo
* @version 0.4
*/
public class Bogobot extends PircBot {
private static final String INTERVAL_DAILY = "daily";
private static final String INTERVAL_MONTHLY = "monthly";
private static final String INTERVAL_WEEKLY = "weekly";
private boolean _isIntentionalDisconnect = false;
private long _lastUserlistCommandTimestamp = 0;
private Logger _logger = Logger.getLogger(Bogobot.class);
private int _currentAutoRoundTripTag = 0;
private long _lastAutoRoundTripSentTime = 0;
private Timer _tickTimer;
private String _configFile;
private String _botPrimaryNick;
private String _botSecondaryNick;
private String _botNickservPassword;
private String _botUsername;
private String _ownerPrimaryNick;
private String _ownerSecondaryNick;
private String _botShutdownPassword;
private String _ircChannel;
private String _ircServer;
private int _ircServerPort;
private boolean _isLoggerEnabled;
private String _loggedHostnamePattern;
private boolean _isUserlistCommandEnabled;
private String _logFilePrefix;
private String _logFileRotationInterval;
private long _commandAntiFloodInterval;
private String _userlistCommandTrigger;
private boolean _isRoundTripDelayEnabled;
private int _roundTripDelayPeriod;
class BogobotTickTask extends TimerTask {
private Bogobot _caller;
public BogobotTickTask(Bogobot caller) {
_caller = caller;
}
public void run() {
_caller.onTick();
}
}
private void loadConfigFile(String configFileName) {
_configFile = configFileName;
Properties config = new Properties();
FileInputStream fis = null;
try {
fis = new FileInputStream(configFileName);
config.load(fis);
} catch (IOException ioe) {
System.err.println("Error loading configuration file");
System.exit(2);
} finally {
if (fis != null) try {
fis.close();
} catch (IOException ioe) { // nop
}
}
_botPrimaryNick = config.getProperty("botPrimaryNick", "somebot");
_botSecondaryNick = config.getProperty("botSecondaryNick", "somebot_");
_botNickservPassword = config.getProperty("botNickservPassword", "");
_botUsername = config.getProperty("botUsername", "somebot");
_ownerPrimaryNick = config.getProperty("ownerPrimaryNick", "somenick");
_ownerSecondaryNick = config.getProperty("ownerSecondaryNick", "somenick_");
_botShutdownPassword = config.getProperty("botShutdownPassword", "take off eh");
_ircChannel = config.getProperty("ircChannel", "#i2p-chat");
_ircServer = config.getProperty("ircServer", "irc.duck.i2p");
_ircServerPort = Integer.parseInt(config.getProperty("ircServerPort", "6668"));
_isLoggerEnabled = Boolean.valueOf(config.getProperty("isLoggerEnabled", "true")).booleanValue();
_loggedHostnamePattern = config.getProperty("loggedHostnamePattern", "");
_logFilePrefix = config.getProperty("logFilePrefix", "irc.duck.i2p.i2p-chat");
_logFileRotationInterval = config.getProperty("logFileRotationInterval", INTERVAL_DAILY);
_isRoundTripDelayEnabled = Boolean.valueOf(config.getProperty("isRoundTripDelayEnabled", "false")).booleanValue();
_roundTripDelayPeriod = Integer.parseInt(config.getProperty("roundTripDelayPeriod", "300"));
_isUserlistCommandEnabled = Boolean.valueOf(config.getProperty("isUserlistCommandEnabled", "true")).booleanValue();
_userlistCommandTrigger = config.getProperty("userlistCommandTrigger", "!who");
_commandAntiFloodInterval = Long.parseLong(config.getProperty("commandAntiFloodInterval", "60"));
}
public Bogobot(String configFileName) {
loadConfigFile(configFileName);
this.setName(_botPrimaryNick);
this.setLogin(_botUsername);
_tickTimer = new Timer();
_tickTimer.scheduleAtFixedRate(new BogobotTickTask(this), 1000, 10 * 1000);
}
public static void main(String[] args) {
Bogobot bogobot;
if (args.length > 1) {
System.err.println("Too many arguments, the only allowed parameter is configuration file name");
System.exit(3);
}
if (args.length == 1) {
bogobot = new Bogobot(args[0]);
} else {
bogobot = new Bogobot("bogobot.config");
}
bogobot.setVerbose(true);
if (bogobot._isLoggerEnabled)
bogobot.initLogger();
bogobot.connectToServer();
}
protected void onTick() {
// Tick about once every ten seconds
if (this.isConnected() && _isRoundTripDelayEnabled) {
if( ( (System.currentTimeMillis() - _lastAutoRoundTripSentTime) >= (_roundTripDelayPeriod * 1000) ) && (this.getOutgoingQueueSize() == 0) ) {
// Connected, sending queue is empty and last RoundTrip is more then 5 minutes old -> Send a new one
_currentAutoRoundTripTag ++;
_lastAutoRoundTripSentTime = System.currentTimeMillis();
sendNotice(this.getNick(),"ROUNDTRIP " + _currentAutoRoundTripTag);
}
}
}
protected void onDisconnect() {
if (_isIntentionalDisconnect)
System.exit(0);
if (_isLoggerEnabled)
_logger.info(System.currentTimeMillis() + " quits *** " + this.getName() + " *** (Lost connection)");
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
// No worries.
}
connectToServer();
}
protected void onJoin(String channel, String sender, String login, String hostname) {
if (_isLoggerEnabled) {
if (sender.equals(this.getName())) {
_logger.info(System.currentTimeMillis() + " joins *** " + _botPrimaryNick + " ***");
} else {
String prependedHostname = "@" + hostname;
if (prependedHostname.endsWith(_loggedHostnamePattern)) {
_logger.info(System.currentTimeMillis() + " joins " + sender);
}
}
}
}
protected void onMessage(String channel, String sender, String login, String hostname, String message) {
message = message.replaceFirst("<.+?> ", "");
if (_isUserlistCommandEnabled && message.equals(_userlistCommandTrigger)) {
if (System.currentTimeMillis() - _lastUserlistCommandTimestamp < _commandAntiFloodInterval * 1000)
return;
Object[] users = getUsers(_ircChannel);
String output = "Userlist for " + _ircChannel + ": ";
for (int i = 0; i < users.length; i++)
output += "[" + ((User) users[i]).getNick() + "] ";
sendMessage(_ircChannel, output);
_lastUserlistCommandTimestamp = System.currentTimeMillis();
}
}
protected void onPart(String channel, String sender, String login, String hostname) {
if (_isLoggerEnabled) {
if (sender.equals(this.getName())) {
_logger.info(System.currentTimeMillis() + " parts *** " + _botPrimaryNick + " ***");
} else {
String prependedHostname = "@" + hostname;
if (prependedHostname.endsWith(_loggedHostnamePattern)) {
_logger.info(System.currentTimeMillis() + " parts " + sender);
}
}
}
}
protected void onPrivateMessage(String sender, String login, String hostname, String message) {
/*
* Nobody else except the bot's owner can shut it down, unless of
* course the owner's nick isn't registered and someone's spoofing it.
*/
if ((sender.equals(_ownerPrimaryNick) || sender.equals(_ownerSecondaryNick)) && message.equals(_botShutdownPassword)) {
if (_isLoggerEnabled)
_logger.info(System.currentTimeMillis() + " quits *** " + this.getName() + " ***");
_isIntentionalDisconnect = true;
disconnect();
}
}
protected void onQuit(String sourceNick, String sourceLogin, String sourceHostname, String reason) {
String prependedHostname = "@" + sourceHostname;
if (sourceNick.equals(_botPrimaryNick))
changeNick(_botPrimaryNick);
if (_isLoggerEnabled) {
if (prependedHostname.endsWith(_loggedHostnamePattern)) {
_logger.info(System.currentTimeMillis() + " quits " + sourceNick + " " + reason);
}
}
}
private void connectToServer() {
int loginAttempts = 0;
while (true) {
try {
connect(_ircServer, _ircServerPort);
break;
} catch (NickAlreadyInUseException e) {
if (loginAttempts == 1) {
System.out.println("Sorry, the primary and secondary bot nicks are already taken. Exiting.");
System.exit(1);
}
loginAttempts++;
try {
Thread.sleep(5000);
} catch (InterruptedException e1) {
// Hmph.
}
if (getName().equals(_botPrimaryNick))
setName(_botSecondaryNick);
else
setName(_botPrimaryNick);
continue;
} catch (IOException e) {
System.out.println("Error during login: ");
e.printStackTrace();
System.exit(1);
} catch (IrcException e) {
System.out.println("Error during login: ");
e.printStackTrace();
System.exit(1);
}
}
joinChannel(_ircChannel);
}
protected void onNotice(String sourceNick, String sourceLogin, String sourceHostname, String target, String notice) {
if (sourceNick.equals("NickServ") && (notice.indexOf("/msg NickServ IDENTIFY") >= 0) && (_botNickservPassword != "")) {
sendRawLineViaQueue("NICKSERV IDENTIFY " + _botNickservPassword);
}
if (sourceNick.equals(getNick()) && notice.equals( "ROUNDTRIP " + _currentAutoRoundTripTag)) {
int delay = (int)((System.currentTimeMillis() - _lastAutoRoundTripSentTime) / 100);
// sendMessage(_ircChannel, "Round-trip delay = " + (delay / 10.0f) + " seconds");
if (_isLoggerEnabled)
_logger.info(System.currentTimeMillis() + " roundtrip " + delay);
}
}
private void initLogger() {
String logFilePath = "logs" + File.separator + _logFilePrefix;
DailyRollingFileAppender rollingFileAppender = null;
if (!(new File("logs").exists()))
(new File("logs")).mkdirs();
try {
if (_logFileRotationInterval.equals("monthly"))
rollingFileAppender = new DailyRollingFileAppender(new PatternLayout("%m%n"), logFilePath, "'.'yyyy-MM'.log'");
else if (_logFileRotationInterval.equals("weekly"))
rollingFileAppender = new DailyRollingFileAppender(new PatternLayout("%m%n"), logFilePath, "'.'yyyy-ww'.log'");
else
rollingFileAppender = new DailyRollingFileAppender(new PatternLayout("%m%n"), logFilePath, "'.'yyyy-MM-dd'.log'");
rollingFileAppender.setThreshold(Level.INFO);
_logger.addAppender(rollingFileAppender);
} catch (IOException ex) {
System.out.println("Error: Couldn't create or open an existing log file. Exiting.");
System.exit(1);
}
}
}

View File

@ -0,0 +1,353 @@
/*
* bogoparser - A simple logfile analyzer for bogobot.
*
* Bogoparser.java
* 2004 The I2P Project
* http://www.i2p.net
* This code is public domain.
*/
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author hypercubus
* @version 0.4
*/
public class Bogoparser {
private static void displayUsageAndExit() {
System.out.println("\r\nUsage:\r\n\r\n java Bogoparser [--by-duration] <logfile>\r\n");
System.exit(1);
}
public static void main(String[] args) {
Bogoparser bogoparser;
if (args.length < 1 || args.length > 2)
displayUsageAndExit();
if (args.length == 2) {
if (!args[0].equals("--by-duration"))
displayUsageAndExit();
bogoparser = new Bogoparser(args[1], true);
}
if (args.length == 1)
bogoparser = new Bogoparser(args[0], false);
}
private Bogoparser(String logfile, boolean sortByDuration) {
ArrayList sortedSessions;
if (sortByDuration) {
sortedSessions = sortSessionsByDuration(calculateSessionDurations(sortSessionsByTime(readLogfile(logfile))));
formatAndOutputByDuration(sortedSessions);
} else {
sortedSessions = calculateSessionDurations(sortSessionsByQuitReason(sortSessionsByNick(sortSessionsByTime(readLogfile(logfile)))));
formatAndOutput(sortedSessions);
}
}
private ArrayList calculateSessionDurations(ArrayList sortedSessionsByQuitReasonOrDuration) {
ArrayList calculatedSessionDurations = new ArrayList();
for (int i = 0; i+1 < sortedSessionsByQuitReasonOrDuration.size(); i += 2) {
String joinsEntry = (String) sortedSessionsByQuitReasonOrDuration.get(i);
String[] joinsEntryFields = joinsEntry.split(" ");
String quitsEntry = (String) sortedSessionsByQuitReasonOrDuration.get(i+1);
Pattern p = Pattern.compile("^([^ ]+) [^ ]+ ([^ ]+) (.*)$");
Matcher m = p.matcher(quitsEntry);
if (m.matches()) {
String currentJoinTime = joinsEntryFields[0];
String currentNick = m.group(2);
String currentQuitReason = m.group(3);
String currentQuitTime = m.group(1);
long joinsTimeInMilliseconds;
long quitsTimeInMilliseconds;
long sessionLengthInMilliseconds;
joinsTimeInMilliseconds = Long.parseLong(currentJoinTime);
quitsTimeInMilliseconds = Long.parseLong(currentQuitTime);
sessionLengthInMilliseconds = quitsTimeInMilliseconds - joinsTimeInMilliseconds;
String hours = "" + sessionLengthInMilliseconds/1000/60/60;
String minutes = "" + (sessionLengthInMilliseconds/1000/60)%60;
if (hours.length() < 2)
hours = "0" + hours;
if (hours.length() < 3)
hours = "0" + hours;
if (minutes.length() < 2)
minutes = "0" + minutes;
int columnPadding = 19-currentNick.length();
String columnPaddingString = " ";
for (int j = 0; j < columnPadding; j++)
columnPaddingString = columnPaddingString + " ";
calculatedSessionDurations.add(sessionLengthInMilliseconds + " " + currentNick + columnPaddingString + " online " + hours + " hours " + minutes + " minutes " + currentQuitReason);
} else {
System.out.println("\r\nError: Unexpected entry in logfile: " + quitsEntry);
System.exit(1);
}
}
return calculatedSessionDurations;
}
private void formatAndOutput(ArrayList sortedSessions) {
String quitReason = null;
for (int i = 0; i < sortedSessions.size(); i++) {
String entry = (String) sortedSessions.get(i);
Pattern p = Pattern.compile("^[\\d]+ ([^ ]+ +online [\\d]+ hours [\\d]+ minutes) (.*)$");
Matcher m = p.matcher(entry);
if (m.matches()) {
if (quitReason == null) {
quitReason = m.group(2);
System.out.println("\r\nQUIT: " + ((m.group(2).equals("")) ? "No Reason Given" : quitReason) + "\r\n");
}
String tempQuitReason = m.group(2);
String tempSession = m.group(1);
if (tempQuitReason.equals(quitReason)) {
System.out.println(" " + tempSession);
} else {
quitReason = null;
i -= 1;
continue;
}
} else {
System.out.println("\r\nError: Unexpected entry in logfile: " + entry);
System.exit(1);
}
}
System.out.println("\r\n");
}
private void formatAndOutputByDuration(ArrayList sortedSessions) {
System.out.println("\r\n");
for (int i = 0; i < sortedSessions.size(); i++) {
String[] columns = ((String) sortedSessions.get(i)).split(" ", 2);
System.out.println(columns[1]);
}
System.out.println("\r\n");
}
private ArrayList readLogfile(String logfile) {
ArrayList log = new ArrayList();
try {
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(logfile)));
for (String line; (line = in.readLine()) != null; )
log.add(line);
in.close();
} catch (FileNotFoundException e) {
System.out.println("\r\nError: Can't find logfile '" + logfile + "'.\r\n");
System.exit(1);
} catch (IOException e) {
System.out.println("\r\nError: Can't read logfile '" + logfile + "'.\r\n");
System.exit(1);
}
return log;
}
/*
* Performs an odd-even transposition sort.
*/
private ArrayList sortSessionsByDuration(ArrayList calculatedSessionDurations) {
for (int i = 0; i < calculatedSessionDurations.size()/2; i++) {
for (int j = 0; j+1 < calculatedSessionDurations.size(); j += 2) {
String[] currentDurationString = ((String) calculatedSessionDurations.get(j)).split(" ", 2);
long currentDuration = Long.parseLong(currentDurationString[0]);
String[] nextDurationString = ((String) calculatedSessionDurations.get(j+1)).split(" ", 2);
long nextDuration = Long.parseLong(nextDurationString[0]);
if (currentDuration > nextDuration) {
calculatedSessionDurations.add(j, calculatedSessionDurations.get(j+1));
calculatedSessionDurations.remove(j+2);
}
}
for (int j = 1; j+1 < calculatedSessionDurations.size(); j += 2) {
String[] currentDurationString = ((String) calculatedSessionDurations.get(j)).split(" ", 2);
long currentDuration = Long.parseLong(currentDurationString[0]);
String[] nextDurationString = ((String) calculatedSessionDurations.get(j+1)).split(" ", 2);
long nextDuration = Long.parseLong(nextDurationString[0]);
if (currentDuration > nextDuration) {
calculatedSessionDurations.add(j, calculatedSessionDurations.get(j+1));
calculatedSessionDurations.remove(j+2);
}
}
}
return calculatedSessionDurations;
}
private ArrayList sortSessionsByNick(ArrayList sortedSessionsByTime) {
ArrayList sortedSessionsByNick = new ArrayList();
while (sortedSessionsByTime.size() != 0) {
String entry = (String) sortedSessionsByTime.get(0);
String[] entryFields = entry.split(" ");
String currentNick = entryFields[2];
sortedSessionsByNick.add(entry);
sortedSessionsByNick.add(sortedSessionsByTime.get(1));
sortedSessionsByTime.remove(0);
sortedSessionsByTime.remove(0);
for (int i = 0; i+1 < sortedSessionsByTime.size(); i += 2) {
String nextEntry = (String) sortedSessionsByTime.get(i);
String[] nextEntryFields = nextEntry.split(" ");
if (nextEntryFields[2].equals(currentNick)) {
sortedSessionsByNick.add(nextEntry);
sortedSessionsByNick.add(sortedSessionsByTime.get(i+1));
sortedSessionsByTime.remove(i);
sortedSessionsByTime.remove(i);
i -= 2;
}
}
}
return sortedSessionsByNick;
}
private ArrayList sortSessionsByQuitReason(ArrayList sortedSessionsByNick) {
ArrayList sortedSessionsByQuitReason = new ArrayList();
while (sortedSessionsByNick.size() != 0) {
String entry = (String) sortedSessionsByNick.get(1);
Pattern p = Pattern.compile("^[^ ]+ [^ ]+ [^ ]+ (.*)$");
Matcher m = p.matcher(entry);
if (m.matches()) {
String currentQuitReason = m.group(1);
sortedSessionsByQuitReason.add(sortedSessionsByNick.get(0));
sortedSessionsByQuitReason.add(entry);
sortedSessionsByNick.remove(0);
sortedSessionsByNick.remove(0);
for (int i = 0; i+1 < sortedSessionsByNick.size(); i += 2) {
String nextEntry = (String) sortedSessionsByNick.get(i+1);
Pattern p2 = Pattern.compile("^[^ ]+ [^ ]+ [^ ]+ (.*)$");
Matcher m2 = p2.matcher(nextEntry);
if (m2.matches()) {
String nextQuitReason = m2.group(1);
if (nextQuitReason.equals(currentQuitReason)) {
sortedSessionsByQuitReason.add(sortedSessionsByNick.get(i));
sortedSessionsByQuitReason.add(nextEntry);
sortedSessionsByNick.remove(i);
sortedSessionsByNick.remove(i);
i -= 2;
}
} else {
System.out.println("\r\nError: Unexpected entry in logfile: " + nextEntry);
System.exit(1);
}
}
} else {
System.out.println("\r\nError: Unexpected entry in logfile: " + entry);
System.exit(1);
}
}
return sortedSessionsByQuitReason;
}
/**
* Sessions terminated with "parts" messages instead of "quits" are filtered
* out.
*/
private ArrayList sortSessionsByTime(ArrayList log) {
ArrayList sortedSessionsByTime = new ArrayList();
mainLoop:
while (log.size() > 0) {
String entry = (String) log.get(0);
String[] entryFields = entry.split(" ");
if (entryFields[1].equals("quits") && !entryFields[1].equals("joins")) {
/*
* Discard entry. The specified log either doesn't contain
* the corresponding "joins" time for this quit entry or the
* entry is a "parts" or unknown message, and in both cases
* the entry's data is useless.
*/
log.remove(0);
continue;
}
for (int i = 1; i < log.size(); i++) { // Find corresponding "quits" entry.
String tempEntry = (String) log.get(i);
String[] tempEntryFields = tempEntry.split(" ");
if (tempEntryFields[2].equals(entryFields[2])) { // Check if the nick fields for the two entries match.
if (!tempEntryFields[1].equals("quits")) {
if (tempEntryFields[1].equals("joins")) { // Don't discard a subsequent "joins" entry.
log.remove(0);
continue mainLoop;
}
log.remove(i);
continue;
}
sortedSessionsByTime.add(entry);
sortedSessionsByTime.add(tempEntry);
log.remove(i);
break;
}
}
/*
* Discard "joins" entry. The specified log doesn't contain the
* corresponding "quits" time for this entry so the entry's
* data is useless.
*/
log.remove(0);
}
return sortedSessionsByTime;
}
}

View File

@ -0,0 +1,48 @@
/*
* ============================================================================
* The Apache Software License, Version 1.1
* ============================================================================
*
* Copyright (C) 1999 The Apache Software Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The end-user documentation included with the redistribution, if any, must
* include the following acknowledgment: "This product includes software
* developed by the Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself, if
* and wherever such third-party acknowledgments normally appear.
*
* 4. The names "log4j" and "Apache Software Foundation" must not be used to
* endorse or promote products derived from this software without prior
* written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache", nor may
* "Apache" appear in their name, without prior written permission of the
* Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
* DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* on behalf of the Apache Software Foundation. For more information on the
* Apache Software Foundation, please see <http://www.apache.org/>.
*
*/

View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

1
apps/bogobot/bogobot.bat Normal file
View File

@ -0,0 +1 @@
java -cp .;log4j-1.2.8.jar;pircbot.jar Bogobot

101
apps/bogobot/bogobot.config Normal file
View File

@ -0,0 +1,101 @@
#####
# Bogobot user configuration
#####
###
# The bot's nick and backup nick. You will probably want to register these with
# the IRC server's NickServ.(a NickServ interface is forthcoming).
#
botPrimaryNick=somebot
botSecondaryNick=somebot_
###
# The bot's password required by Nickserv service's identify command.
# You have to register the nickname yourself first, the bot will not.
#
botNickservPassword=
###
# The bot's username. Appears in the whois replies
#
botUsername=somebot
#####
# The bot owner's nick and backup nick. One of these must match the owner's
# currently-used nick or else remote shutdown will not be possible. You will
# probably want to register these with the IRC server's NickServ.
#
ownerPrimaryNick=somenick
ownerSecondaryNick=somenick_
###
# The bot will disconnect and shut down when sent this password via private
# message (aka query) from either of the owner nicks specified above. DO NOT USE
# THIS DEFAULT VALUE!
#
botShutdownPassword=take off eh
###
# The server, channel, and port the bot will connect to.
#
ircChannel=#i2p-chat
ircServer=irc.duck.i2p
ircServerPort=6668
###
# Set to "true" to enable logging, else "false" (but don't use quotation marks).
#
isLoggerEnabled=true
###
# Restrict logging of joins and parts on the user hostname.
# Leave empty to log all of them
# Prepend with a @ for a perfect match
# Otherwise, specify the required end of the user hostname
#
loggedHostnamePattern=@free.duck.i2p
###
# The prefix to be used for the filenames of logs.
#
logFilePrefix=irc.duck.i2p.i2p-chat
###
# How often the logs should be rotated. Either "daily", "weekly", or "monthly"
# (but don't use quotation marks).
#
logFileRotationInterval=daily
###
# Set to "true" to enable the regular round-trip delay computation,
# else "false" (but don't use quotation marks).
#
isRoundTripDelayEnabled=false
###
# How often should the round-trip delay be recorded.
# (in seconds)
#
roundTripDelayPeriod=300
###
# Set to "true" to enable the userlist command, else "false" (but don't use
# quotation marks).
#
isUserlistCommandEnabled=true
###
# The userlist trigger command to listen for. It is a good idea to prefix
# triggers with some non-alphanumeric character in order to avoid accidental
# trigger use during normal channel conversation. In most cases you will
# probably want to choose a unique trigger here that no other bots in the
# channel will respond to.
#
userlistCommandTrigger=!who
###
# The number of seconds to rest after replying to a userlist command issued by
# a user in the channel. The bot will ignore subsequent userlist commands during
# this period. This helps prevent flooding.
#
commandAntiFloodInterval=60

2
apps/bogobot/bogobot.sh Normal file
View File

@ -0,0 +1,2 @@
#!/bin/sh
java -cp .:log4j-1.2.8.jar:pircbot.jar Bogobot

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- ********************************************************** -->
<!-- bogobot - A simple join/part stats logger bot for I2P IRC. -->
<!-- -->
<!-- build-eclipse.xml -->
<!-- 2004 The I2P Project -->
<!-- http://www.i2p.net -->
<!-- This code is public domain. -->
<!-- -->
<!-- authors: hypercubus, oOo -->
<!-- version 0.4 -->
<!-- ********************************************************** -->
<project basedir="." default="dist" name="Bogobot">
<!-- init:
Create distribution directory if missing and initialize time stamp for
archive naming -->
<target name="init">
<mkdir dir="dist" />
<tstamp>
<format pattern="yyyy-MM-dd" property="DSTAMP" />
</tstamp>
</target>
<!-- dist.bin:
Create the binary distribution archive -->
<target depends="init" description="Create the binary distribution archive" name="dist.bin">
<zip destfile="dist/Bogobot_${DSTAMP}.zip">
<zipfileset dir="${basedir}" includes="bogobot.bat bogobot.config Bogobot.class bogobot.sh Bogoparser.class LICENSE_log4j.txt LICENSE_pircbot.txt log4j-1.2.8.jar pircbot.jar" />
</zip>
</target>
<!-- dist.source:
Create the source distribution archive -->
<target depends="init" description="Create the source distribution archive" name="dist.source">
<zip destfile="dist/Bogobot_source_${DSTAMP}.zip">
<zipfileset dir="${basedir}" includes="bogobot.bat bogobot.config Bogobot.java bogobot.sh Bogoparser.java build.xml build_eclipse.xml LICENSE_log4j.txt LICENSE_pircbot.txt log4j-1.2.8.jar pircbot.jar" />
</zip>
</target>
<!-- dist:
Create both the binary and source distribution archives -->
<target depends="dist.bin,dist.source" description="Create both the binary and source distribution archives" name="dist">
<echo message="Successfully created binary and source distribution archives in directory &apos;dist&apos;." />
</target>
<!-- clean:
Delete all class files and temporary directories -->
<target description="Delete all class files and temporary directories" name="clean">
<delete>
<fileset dir="${basedir}" includes="**/*.class" />
</delete>
<echo message="Clean successful." />
</target>
</project>

64
apps/bogobot/build.xml Normal file
View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- ********************************************************** -->
<!-- bogobot - A simple join/part stats logger bot for I2P IRC. -->
<!-- -->
<!-- build.xml -->
<!-- 2004 The I2P Project -->
<!-- http://www.i2p.net -->
<!-- This code is public domain. -->
<!-- -->
<!-- authors: hypercubus, oOo -->
<!-- version 0.4 -->
<!-- ********************************************************** -->
<project basedir="." default="compile" name="Bogobot">
<!-- init:
Create distribution directory if missing and initialize time stamp for
archive naming -->
<target name="init">
<mkdir dir="dist" />
<tstamp>
<format pattern="yyyy-MM-dd" property="DSTAMP" />
</tstamp>
</target>
<!-- compile:
Compile source code -->
<target depends="init" description="Compile source code" name="compile">
<javac classpath="${basedir};log4j-1.2.8.jar;pircbot.jar" source="1.4" srcdir="." />
</target>
<!-- dist.bin:
Create the binary distribution archive -->
<target depends="init,compile" description="Create the binary distribution archive" name="dist.bin">
<zip destfile="dist/Bogobot_${DSTAMP}.zip">
<zipfileset dir="${basedir}" includes="bogobot.bat bogobot.config Bogobot.class Bogobot$BogobotTickTask.class bogobot.sh Bogoparser.class LICENSE_log4j.txt LICENSE_pircbot.txt log4j-1.2.8.jar pircbot.jar" />
</zip>
</target>
<!-- dist.source:
Create the source distribution archive -->
<target depends="init" description="Create the source distribution archive" name="dist.source">
<zip destfile="dist/Bogobot_source_${DSTAMP}.zip">
<zipfileset dir="${basedir}" includes="bogobot.bat bogobot.config Bogobot.java bogobot.sh Bogoparser.java build.xml build_eclipse.xml LICENSE_log4j.txt LICENSE_pircbot.txt log4j-1.2.8.jar pircbot.jar" />
</zip>
</target>
<!-- dist:
Create both the binary and source distribution archives -->
<target depends="dist.bin,dist.source" description="Create both the binary and source distribution archives" name="dist">
<echo message="Successfully created binary and source distribution archives in directory &apos;dist&apos;." />
</target>
<!-- clean:
Delete all class files and temporary directories -->
<target description="Delete all class files and temporary directories" name="clean">
<delete>
<fileset dir="${basedir}" includes="**/*.class" />
</delete>
<echo message="Clean successful." />
</target>
</project>

Binary file not shown.

BIN
apps/bogobot/pircbot.jar Normal file

Binary file not shown.

106
apps/fortuna/build.xml Normal file
View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="all" name="fortuna">
<property name="cvs.base.dir" value="java/gnu-crypto" />
<property name="cvs.etc.dir" value="${cvs.base.dir}/etc" />
<property name="cvs.lib.dir" value="${cvs.base.dir}/lib" />
<property name="cvs.object.dir" value="${cvs.base.dir}/classes" />
<property name="cvs.base.crypto.object.dir" value="${cvs.object.dir}/gnu/crypto" />
<property name="cvs.cipher.object.dir" value="${cvs.base.crypto.object.dir}/cipher" />
<property name="cvs.hash.object.dir" value="${cvs.base.crypto.object.dir}/hash" />
<property name="cvs.prng.object.dir" value="${cvs.base.crypto.object.dir}/prng" />
<patternset id="fortuna.files">
<include name="${cvs.base.crypto.object.dir}/Registry.class"/>
<include name="${cvs.prng.object.dir}/Fortuna*.class"/>
<include name="${cvs.prng.object.dir}/BasePRNG.class"/>
<include name="${cvs.prng.object.dir}/RandomEventListener.class"/>
<include name="${cvs.prng.object.dir}/IRandom.class"/>
<include name="${cvs.cipher.object.dir}/CipherFactory.class"/>
<include name="${cvs.cipher.object.dir}/IBlockCipher.class"/>
<include name="${cvs.hash.object.dir}/HashFactory.class"/>
<include name="${cvs.hash.object.dir}/IMessageDigest.class"/>
</patternset>
<target name="all" depends="build,jar"
description="Create and test the custom Fortuna library" />
<target name="build" depends="-init,checkout"
description="Build the source and tests">
<ant dir="${cvs.base.dir}" target="jar" />
</target>
<target name="builddep" />
<target name="checkout" depends="-init" unless="cvs.source.available"
description="Check out GNU Crypto sources from CVS HEAD">
<cvs cvsRoot=":ext:anoncvs@savannah.gnu.org:/cvsroot/gnu-crypto"
cvsRsh="ssh"
dest="java"
package="gnu-crypto" />
</target>
<target name="clean"
description="Remove generated tests and object files">
<ant dir="${cvs.base.dir}" target="clean" />
</target>
<target name="cleandep" />
<target name="compile" />
<target name="distclean" depends="clean"
description="Remove all generated files">
<delete dir="build" />
<delete dir="jartemp" />
<!--
Annoyingly the GNU Crypto distclean task called here doesn't clean
*all* derived files from java/gnu-crypto/lib like it should.....
-->
<ant dir="${cvs.base.dir}" target="distclean" />
<!--
.....and so we mop up the rest ourselves.
-->
<delete dir="${cvs.lib.dir}" />
</target>
<target name="-init">
<available property="cvs.source.available" file="${cvs.base.dir}" />
</target>
<target name="jar" depends="build"
description="Create the custom Fortuna jar library">
<delete dir="build" />
<delete dir="jartemp" />
<mkdir dir="build" />
<mkdir dir="jartemp/${cvs.object.dir}" />
<copy todir="jartemp">
<fileset dir=".">
<patternset refid="fortuna.files" />
</fileset>
</copy>
<jar basedir="jartemp/${cvs.object.dir}" jarfile="build/fortuna.jar">
<manifest>
<section name="fortuna">
<attribute name="Implementation-Title" value="I2P Custom GNU Crypto Fortuna Library" />
<attribute name="Implementation-Version" value="CVS HEAD" />
<attribute name="Implementation-Vendor" value="Free Software Foundation" />
<attribute name="Implementation-Vendor-Id" value="FSF" />
<attribute name="Implementation-URL" value="http://www.gnu.org/software/gnu-crypto" />
</section>
</manifest>
</jar>
<delete dir="jartemp" />
</target>
<target name="test" depends="jar"
description="Perform crypto tests on custom Fortuna jar library" />
<!--
Add this when Fortuna tests are added to GNU Crypto, else write some
-->
<target name="update" depends="checkout"
description="Update GNU Crypto sources to latest CVS HEAD">
<cvs command="update -d" cvsRsh="ssh" dest="java/gnu-crypto" />
</target>
</project>

View File

@ -9,12 +9,12 @@
<target name="compile">
<mkdir dir="./build" />
<mkdir dir="./build/obj" />
<javac srcdir="./src" debug="true" destdir="./build/obj" includes="**/*.java" excludes="net/i2p/heartbeat/gui/**" classpath="../../../core/java/build/i2p.jar" />
<javac srcdir="./src" debug="true" deprecation="on" source="1.3" target="1.3" destdir="./build/obj" includes="**/*.java" excludes="net/i2p/heartbeat/gui/**" classpath="../../../core/java/build/i2p.jar" />
</target>
<target name="compileGUI">
<mkdir dir="./build" />
<mkdir dir="./build/obj" />
<javac debug="true" destdir="./build/obj">
<javac debug="true" source="1.3" target="1.3" deprecation="on" destdir="./build/obj">
<src path="src/" />
<classpath path="../../../core/java/build/i2p.jar" />
<classpath path="../../jfreechart/jfreechart-0.9.17/lib/jcommon-0.9.2.jar" />

View File

@ -358,7 +358,7 @@ public class ClientConfig {
int sendFreq = getInt(sendFrequencyVal);
int sendSize = getInt(sendSizeVal);
if ((duration <= 0) || (statFreq <= 0) || (sendFreq <= 0) || (sendSize <= 0)) {
if ((duration <= 0) || (statFreq <= 0) || (sendFreq < 0) || (sendSize <= 0)) {
if (_log.shouldLog(Log.WARN)) {
_log.warn("Invalid client config: duration [" + statDurationVal + "] stat frequency ["
+ statFrequencyVal + "] send frequency [" + sendFrequencyVal + "] send size ["
@ -424,7 +424,7 @@ public class ClientConfig {
* @return true if it was stored correctly, false if there were errors
*/
public boolean store(Properties clientConfig, int peerNum) {
if ((_peer == null) || (_sendFrequency <= 0) || (_sendSize <= 0) || (_statDuration <= 0)
if ((_peer == null) || (_sendFrequency < 0) || (_sendSize <= 0) || (_statDuration <= 0)
|| (_statFrequency <= 0) || (_statFile == null)) { return false; }
String comment = _comment;

View File

@ -90,7 +90,7 @@ class ClientEngine {
/** our actual heartbeat pumper - this drives the test */
private class ClientRunner implements Runnable {
/* (non-Javadoc)
/**
* @see java.lang.Runnable#run()
*/
public void run() {
@ -120,9 +120,12 @@ class ClientEngine {
_data.cleanup();
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
long timeToWait = nextSend - Clock.getInstance().now();
if (timeToWait > 0) {
try {
Thread.sleep(timeToWait);
} catch (InterruptedException ie) {
}
}
}
}

View File

@ -157,7 +157,7 @@ public class Heartbeat {
}
/** disconnect from the network */
private void disconnect() {
private void disconnect() { /* UNUSED */
_adapter.disconnect();
}

View File

@ -6,10 +6,10 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
@ -46,7 +46,7 @@ class I2PAdapter {
/** how do we talk to the router */
private I2PSession _session;
/** object that receives our i2cp notifications from the session and tells us */
private I2PListener _i2pListener;
private I2PListener _i2pListener; /* UNUSED */
/**
* This config property tells us where the private destination data for our
@ -219,7 +219,8 @@ class I2PAdapter {
DataHelper.writeDate(baos, new Date(now));
int padding = size - baos.size();
byte paddingData[] = new byte[padding];
Arrays.fill(paddingData, (byte) 0x2A);
I2PAppContext.getGlobalContext().random().nextBytes(paddingData);
//Arrays.fill(paddingData, (byte) 0x2A);
DataHelper.writeLong(baos, 2, padding);
baos.write(paddingData);
boolean sent = _session.sendMessage(peer, baos.toByteArray());
@ -577,14 +578,14 @@ class I2PAdapter {
* @see net.i2p.client.I2PSessionListener#errorOccurred(net.i2p.client.I2PSession, java.lang.String, java.lang.Throwable)
*/
public void errorOccurred(I2PSession session, String message, Throwable error) {
if (_log.shouldLog(Log.ERROR)) _log.error("Error occurred", error);
if (_log.shouldLog(Log.ERROR)) _log.error("Error occurred: " + message, error);
}
/* (non-Javadoc)
* @see net.i2p.client.I2PSessionListener#reportAbuse(net.i2p.client.I2PSession, int)
*/
public void reportAbuse(I2PSession session, int severity) {
if (_log.shouldLog(Log.ERROR)) _log.error("Abuse reported");
if (_log.shouldLog(Log.ERROR)) _log.error("Abuse reported with severity " + String.valueOf(severity));
}
/* (non-Javadoc)
@ -600,4 +601,4 @@ class I2PAdapter {
}
}
}
}
}

View File

@ -27,7 +27,7 @@ public class PeerData {
/** date sent (Long) to EventDataPoint containing the datapoints sent in the current period */
private Map _dataPoints;
/** date sent (Long) to EventDataPoint containing pings that haven't yet timed out or been ponged */
private Map _pendingPings;
private TreeMap _pendingPings;
private long _sessionStart;
private long _lifetimeSent;
private long _lifetimeReceived;
@ -103,6 +103,11 @@ public class PeerData {
* @return when the test began
*/
public long getSessionStart() { return _sessionStart; }
/**
* sets when the test began
* @param when when it began
*/
public void setSessionStart(long when) { _sessionStart = when; }
/**
@ -203,14 +208,32 @@ public class PeerData {
public void pongReceived(long dateSent, long pongSent) {
long now = Clock.getInstance().now();
synchronized (_updateLock) {
EventDataPoint data = (EventDataPoint) _pendingPings.remove(new Long(dateSent));
if (_pendingPings.size() <= 0) {
_log.warn("Pong received (sent at " + dateSent + ", " + (now-dateSent)
+ "ms ago, pong delay " + (pongSent-dateSent) + "ms, pong receive delay "
+ (now-pongSent) + "ms)");
return;
}
Long first = (Long)_pendingPings.firstKey();
EventDataPoint data = (EventDataPoint)_pendingPings.remove(new Long(dateSent));
if (data != null) {
data.setPongReceived(now);
data.setPongSent(pongSent);
data.setWasPonged(true);
locked_addDataPoint(data);
if (dateSent != first.longValue()) {
_log.error("Out of order delivery: received " + dateSent
+ " but the first pending is " + first.longValue()
+ " (delta " + (dateSent - first.longValue()) + ")");
} else {
_log.info("In order delivery for " + dateSent + " in ping "
+ _peer.getComment());
}
} else {
_log.warn("Pong received, but no matching ping? ping sent at = " + dateSent);
return;
}
}
_sendRate.addData(pongSent - dateSent, 0);
@ -251,9 +274,9 @@ public class PeerData {
_lostRate.addData(numTimedOut, 0);
_receiveRate.coallesceStats();
_sendRate.coallesceStats();
_lostRate.coallesceStats();
_receiveRate.coalesceStats();
_sendRate.coalesceStats();
_lostRate.coalesceStats();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Peer data cleaned up " + numTimedOut + " timed out pings and removed " + numDropped
@ -332,6 +355,11 @@ public class PeerData {
* @return the time the ping was sent
*/
public long getPingSent() { return _pingSent; }
/**
* sets when we sent this ping
* @param when when we sent the ping
*/
public void setPingSent(long when) { _pingSent = when; }
/**
@ -381,4 +409,4 @@ public class PeerData {
_wasPonged = pong;
}
}
}
}

View File

@ -114,7 +114,7 @@ public class PeerDataWriter {
return buf.toString();
}
private static final SimpleDateFormat _fmt = new SimpleDateFormat("yyyyMMdd.HH:mm:ss.SSS", Locale.UK);
private final SimpleDateFormat _fmt = new SimpleDateFormat("yyyyMMdd.HH:mm:ss.SSS", Locale.UK);
/**
* Converts a time (long) to text
@ -127,7 +127,7 @@ public class PeerDataWriter {
}
}
private static final DecimalFormat _numFmt = new DecimalFormat("#0", new DecimalFormatSymbols(Locale.UK));
private final DecimalFormat _numFmt = new DecimalFormat("#0", new DecimalFormatSymbols(Locale.UK));
/**
* Converts a number (double) to text

View File

@ -21,7 +21,7 @@ class HeartbeatControlPane extends JPanel {
private HeartbeatMonitorGUI _gui;
private JTabbedPane _configPane;
private final static Color WHITE = new Color(255, 255, 255);
private final static Color LIGHT_BLUE = new Color(180, 180, 255);
private final static Color LIGHT_BLUE = new Color(180, 180, 255); /* UNUSED */
private final static Color BLACK = new Color(0, 0, 0);
private Color _background = WHITE;
private Color _foreground = BLACK;

View File

@ -88,7 +88,9 @@ public class HeartbeatMonitor implements PeerPlotStateFetcher.FetchStateReceptor
_gui.stateUpdated();
}
/** store the config defining what peer tests we are monitoring (and how to render) */
/**
* store the config defining what peer tests we are monitoring (and how to render)
*/
void storeConfig() {}
/**
@ -103,6 +105,10 @@ public class HeartbeatMonitor implements PeerPlotStateFetcher.FetchStateReceptor
new HeartbeatMonitor().runMonitor();
}
/**
* Called when the config is updated
* @param config the updated config
*/
public void configUpdated(PeerPlotConfig config) {
_log.debug("Config updated, revamping the gui");
_gui.stateUpdated();

View File

@ -19,7 +19,7 @@ import net.i2p.heartbeat.PeerData;
import net.i2p.util.Log;
class JFreeChartAdapter {
private final static Log _log = new Log(JFreeChartAdapter.class);
private final static Log _log = new Log(JFreeChartAdapter.class); /* UNUSED */
private final static Color WHITE = new Color(255, 255, 255);
ChartPanel createPanel(HeartbeatMonitorState state) {
@ -45,7 +45,7 @@ class JFreeChartAdapter {
updateLines(plot, state);
}
private long getFirst(HeartbeatMonitorState state) {
private long getFirst(HeartbeatMonitorState state) { /* UNUSED */
long first = -1;
for (int i = 0; i < state.getTestCount(); i++) {
List dataPoints = state.getTest(i).getCurrentData().getDataPoints();
@ -116,6 +116,7 @@ class JFreeChartAdapter {
}
/**
* @param col the collection of xy series to add to
* @param config preferences for how to display this test
* @param lineName minimal name of the test (e.g. "jxHa.32KB.60s")
* @param data List of PeerData.EventDataPoint describing all of the events in the test
@ -144,6 +145,7 @@ class JFreeChartAdapter {
/**
* Add a data series for each average that we're configured to render
*
* @param col the collection of xy series to add to
* @param config preferences for how to display this test
* @param lineName minimal name of the test (e.g. "jxHa.32KB.60s")
* @param data List of PeerData.EventDataPoint describing all of the events in the test

View File

@ -14,14 +14,21 @@ import net.i2p.util.Log;
*
*/
class JFreeChartHeartbeatPlotPane extends HeartbeatPlotPane {
private final static Log _log = new Log(JFreeChartHeartbeatPlotPane.class);
private final static Log _log = new Log(JFreeChartHeartbeatPlotPane.class); /* UNUSED */
private ChartPanel _panel;
private JFreeChartAdapter _adapter;
/**
* Creates a JFreeChart plot pane for the given gui
* @param gui the heartbeat monitor gui
*/
public JFreeChartHeartbeatPlotPane(HeartbeatMonitorGUI gui) {
super(gui);
}
/**
* Called when the state is updated
*/
public void stateUpdated() {
if (_panel == null) {
remove(0); // remove the dummy

View File

@ -18,7 +18,7 @@ import net.i2p.util.Log;
* Configure how we want to render a particular clientConfig in the GUI
*/
class PeerPlotConfig {
private final static Log _log = new Log(PeerPlotConfig.class);
private final static Log _log = new Log(PeerPlotConfig.class); /* UNUSED */
/** where can we find the current state/data (either as a filename or a URL)? */
private String _location;
/** what test are we defining the plot data for? */
@ -170,8 +170,8 @@ class PeerPlotConfig {
Destination peer = getClientConfig().getPeer();
if (peer == null)
return "????";
else
return peer.calculateHash().toBase64().substring(0, 4);
return peer.calculateHash().toBase64().substring(0, 4);
}
/**
@ -196,8 +196,8 @@ class PeerPlotConfig {
int bytes = getClientConfig().getSendSize();
if (bytes < 1024)
return bytes + "b";
else
return bytes/1024 + "kb";
return bytes/1024 + "kb";
}
/**

View File

@ -243,6 +243,7 @@ class PeerPlotConfigPane extends JPanel implements PeerPlotConfig.UpdateListener
_minutes = minutes;
_button = button;
}
/* (non-Javadoc)
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/

View File

@ -326,7 +326,7 @@ class PeerPlotStateFetcher {
}
}
private void fakeRun() {
private void fakeRun() { /* UNUSED */
try {
Destination peer = new Destination();
Destination us = new Destination();

View File

@ -94,8 +94,8 @@ class StaticPeerData extends PeerData {
Integer i = (Integer)_averageSendTimes.get(new Integer(period));
if (i == null)
return -1;
else
return i.doubleValue();
return i.doubleValue();
}
@ -109,8 +109,8 @@ class StaticPeerData extends PeerData {
Integer i = (Integer)_averageReceiveTimes.get(new Integer(period));
if (i == null)
return -1;
else
return i.doubleValue();
return i.doubleValue();
}
/**
@ -123,8 +123,8 @@ class StaticPeerData extends PeerData {
Integer i = (Integer)_lostMessages.get(new Integer(period));
if (i == null)
return -1;
else
return i.doubleValue();
return i.doubleValue();
}
/* (non-Javadoc)

View File

@ -11,7 +11,7 @@
<mkdir dir="./build/obj" />
<javac
srcdir="./src"
debug="true"
debug="true" deprecation="on" source="1.3" target="1.3"
destdir="./build/obj"
classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" />
</target>

View File

@ -57,14 +57,14 @@ public class HTTPListener extends Thread {
* @return Whether this is the first proxy use, no doubt.
*/
public boolean firstProxyUse() {
// FIXME: check a config option here
if (true) return false;
if (true) return false; // FIXME: check a config option here
if (proxyUsed) {
return false;
} else {
proxyUsed = true;
return true;
}
proxyUsed = true;
return true;
}
/**

View File

@ -12,7 +12,7 @@ import net.i2p.util.Log;
*/
public class ErrorHandler {
private static final Log _log = new Log(ErrorHandler.class);
private static final Log _log = new Log(ErrorHandler.class); /* UNUSED */
/* package private */ErrorHandler() {

View File

@ -13,7 +13,7 @@ import net.i2p.util.Log;
*/
public class LocalHandler {
private static final Log _log = new Log(LocalHandler.class);
private static final Log _log = new Log(LocalHandler.class); /* UNUSED */
/* package private */LocalHandler() {
}

View File

@ -18,7 +18,7 @@ import net.i2p.util.Log;
*/
public class ProxyHandler extends EepHandler {
private static final Log _log = new Log(ErrorHandler.class);
private static final Log _log = new Log(ErrorHandler.class); /* UNUSED */
private static I2PAppContext _context = new I2PAppContext();
/* package private */ProxyHandler(ErrorHandler eh) {

View File

@ -12,7 +12,7 @@ import net.i2p.util.Log;
*/
public class RootHandler {
private static final Log _log = new Log(RootHandler.class);
private static final Log _log = new Log(RootHandler.class); /* UNUSED */
private RootHandler() {
errorHandler = new ErrorHandler();
@ -60,26 +60,24 @@ public class RootHandler {
url = url.substring(7);
pos = url.indexOf("/");
String host;
String rest;
if (pos == -1) {
errorHandler.handle(req, httpl, out, "No host end in URL");
return;
}
host = url.substring(0, pos);
url = url.substring(pos);
if ("i2p".equals(host) || "i2p.i2p".equals(host)) {
// normal request; go on below...
} else if (host.endsWith(".i2p")) {
// "old" service request, send a redirect...
out.write(("HTTP/1.1 302 Moved\r\nLocation: " + "http://i2p.i2p/" + host + url + "\r\n\r\n").getBytes("ISO-8859-1"));
return;
} else {
host = url.substring(0, pos);
url = url.substring(pos);
if ("i2p".equals(host) || "i2p.i2p".equals(host)) {
// normal request; go on below...
} else if (host.endsWith(".i2p")) {
// "old" service request, send a redirect...
out
.write(("HTTP/1.1 302 Moved\r\nLocation: " + "http://i2p.i2p/" + host + url + "\r\n\r\n")
.getBytes("ISO-8859-1"));
return;
} else {
// this is for proxying to the real web
proxyHandler.handle(req, httpl, out /*, true */);
return;
}
// this is for proxying to the real web
proxyHandler.handle(req, httpl, out /*, true */);
return;
}
}
if (url.equals("/")) { // main page

View File

@ -4,6 +4,7 @@
<target name="build" depends="builddep, jar" />
<target name="builddep">
<ant dir="../../ministreaming/java/" target="build" />
<ant dir="../../jetty/" target="build" />
<!-- ministreaming will build core -->
</target>
<target name="compile">
@ -11,17 +12,65 @@
<mkdir dir="./build/obj" />
<javac
srcdir="./src"
debug="true"
debug="true" deprecation="on" source="1.3" target="1.3"
destdir="./build/obj"
classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" />
</target>
<target name="jar" depends="compile">
<target name="jar" depends="builddep, compile">
<jar destfile="./build/i2ptunnel.jar" basedir="./build/obj" includes="**/*.class">
<manifest>
<attribute name="Main-Class" value="net.i2p.i2ptunnel.I2PTunnel" />
<attribute name="Class-Path" value="i2p.jar mstreaming.jar" />
</manifest>
</jar>
<ant target="war" />
</target>
<target name="war" depends="precompilejsp">
<war destfile="build/i2ptunnel.war" webxml="../jsp/web-out.xml"
basedir="../jsp/" excludes="web.xml, *.java, *.jsp">
</war>
</target>
<target name="precompilejsp">
<delete dir="../jsp/WEB-INF/" />
<delete file="../jsp/web-fragment.xml" />
<delete file="../jsp/web-out.xml" />
<mkdir dir="../jsp/WEB-INF/" />
<mkdir dir="../jsp/WEB-INF/classes" />
<!-- there are various jspc ant tasks, but they all seem a bit flakey -->
<java classname="org.apache.jasper.JspC" fork="true" >
<classpath>
<pathelement location="../../jetty/jettylib/jasper-compiler.jar" />
<pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
<pathelement location="../../jetty/jettylib/commons-logging.jar" />
<pathelement location="../../jetty/jettylib/commons-el.jar" />
<pathelement location="../../jetty/jettylib/ant.jar" />
<pathelement location="build/i2ptunnel.jar" />
</classpath>
<arg value="-d" />
<arg value="../jsp/WEB-INF/classes" />
<arg value="-p" />
<arg value="net.i2p.i2ptunnel.jsp" />
<arg value="-webinc" />
<arg value="../jsp/web-fragment.xml" />
<arg value="-webapp" />
<arg value="../jsp/" />
</java>
<javac debug="true" deprecation="on" source="1.3" target="1.3"
destdir="../jsp/WEB-INF/classes/" srcdir="../jsp/WEB-INF/classes" includes="**/*.java">
<classpath>
<pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
<pathelement location="../../jetty/jettylib/commons-logging.jar" />
<pathelement location="../../jetty/jettylib/commons-el.jar" />
<pathelement location="build/i2ptunnel.jar" />
</classpath>
</javac>
<copy file="../jsp/web.xml" tofile="../jsp/web-out.xml" />
<loadfile property="jspc.web.fragment" srcfile="../jsp/web-fragment.xml" />
<replace file="../jsp/web-out.xml">
<replacefilter token="&lt;!-- precompiled servlets --&gt;" value="${jspc.web.fragment}" />
</replace>
</target>
<target name="javadoc">
<mkdir dir="./build" />

View File

@ -17,6 +17,9 @@ class BufferLogger implements Logging {
private ByteArrayOutputStream _baos;
private boolean _ignore;
/**
* Constructs a buffered logger.
*/
public BufferLogger() {
_baos = new ByteArrayOutputStream(512);
_ignore = false;
@ -24,11 +27,15 @@ class BufferLogger implements Logging {
private final static String EMPTY = "";
/**
* Retrieves the buffer
* @return the buffer
*/
public String getBuffer() {
if (_ignore)
return EMPTY;
else
return new String(_baos.toByteArray());
return new String(_baos.toByteArray());
}
/**

View File

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

View File

@ -46,6 +46,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
@ -53,6 +54,7 @@ import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.I2PSession;
import net.i2p.client.naming.NamingService;
import net.i2p.data.Base64;
import net.i2p.data.DataFormatException;
@ -69,16 +71,18 @@ public class I2PTunnel implements Logging, EventDispatcher {
private I2PAppContext _context;
private static long __tunnelId = 0;
private long _tunnelId;
private Properties _clientOptions;
private List _sessions;
public static final int PACKET_DELAY = 100;
public static boolean ownDest = false;
public boolean ownDest = false;
public static String port = System.getProperty(I2PClient.PROP_TCP_PORT, "7654");
public static String host = System.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
public static String listenHost = host;
public String port = System.getProperty(I2PClient.PROP_TCP_PORT, "7654");
public String host = System.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
public String listenHost = host;
public static long readTimeout = -1;
public long readTimeout = -1;
private static final String nocli_args[] = { "-nocli", "-die"};
@ -104,6 +108,10 @@ public class I2PTunnel implements Logging, EventDispatcher {
_tunnelId = ++__tunnelId;
_log = _context.logManager().getLog(I2PTunnel.class);
_event = new EventDispatcherImpl();
_clientOptions = new Properties();
_clientOptions.putAll(System.getProperties());
_sessions = new ArrayList(1);
addConnectionEventListener(lsnr);
boolean gui = true;
boolean checkRunByE = true;
@ -167,6 +175,27 @@ public class I2PTunnel implements Logging, EventDispatcher {
}
}
List getSessions() {
synchronized (_sessions) {
return new ArrayList(_sessions);
}
}
void addSession(I2PSession session) {
if (session == null) return;
synchronized (_sessions) {
if (!_sessions.contains(session))
_sessions.add(session);
}
}
void removeSession(I2PSession session) {
if (session == null) return;
synchronized (_sessions) {
_sessions.remove(session);
}
}
public Properties getClientOptions() { return _clientOptions; }
private void addtask(I2PTunnelTask tsk) {
tsk.setTunnel(this);
if (tsk.isOpen()) {
@ -197,8 +226,12 @@ public class I2PTunnel implements Logging, EventDispatcher {
if ("help".equals(cmdname)) {
runHelp(l);
} else if ("clientoptions".equals(cmdname)) {
runClientOptions(args, l);
} else if ("server".equals(cmdname)) {
runServer(args, l);
} else if ("httpserver".equals(cmdname)) {
runHttpServer(args, l);
} else if ("textserver".equals(cmdname)) {
runTextServer(args, l);
} else if ("client".equals(cmdname)) {
@ -251,10 +284,11 @@ public class I2PTunnel implements Logging, EventDispatcher {
l.log("owndest yes|no");
l.log("ping <args>");
l.log("server <host> <port> <privkeyfile>");
l.log("httpserver <host> <port> <spoofedhost> <privkeyfile>");
l.log("textserver <host> <port> <privkey>");
l.log("genkeys <privkeyfile> [<pubkeyfile>]");
l.log("gentextkeys");
l.log("client <port> <pubkey>|file:<pubkeyfile>");
l.log("client <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile>");
l.log("httpclient <port>");
l.log("lookup <name>");
l.log("quit");
@ -262,6 +296,29 @@ public class I2PTunnel implements Logging, EventDispatcher {
l.log("list");
l.log("run <commandfile>");
}
/**
* Configure the extra I2CP options to use in any subsequent I2CP sessions.
* Usage: "clientoptions[ key=value]*" .
*
* Sets the event "clientoptions_onResult" = "ok" after completion.
*
* @param args each args[i] is a key=value pair to add to the options
* @param l logger to receive events and output
*/
public void runClientOptions(String args[], Logging l) {
_clientOptions.clear();
if (args != null) {
for (int i = 0; i < args.length; i++) {
int index = args[i].indexOf('=');
if (index <= 0) continue;
String key = args[i].substring(0, index);
String val = args[i].substring(index+1);
_clientOptions.setProperty(key, val);
}
}
notifyEvent("clientoptions_onResult", "ok");
}
/**
* Run the server pointing at the host and port specified using the private i2p
@ -304,7 +361,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
notifyEvent("serverTaskId", new Integer(-1));
return;
}
I2PTunnelServer serv = new I2PTunnelServer(serverHost, portNum, privKeyFile, args[2], l, (EventDispatcher) this);
I2PTunnelServer serv = new I2PTunnelServer(serverHost, portNum, privKeyFile, args[2], l, (EventDispatcher) this, this);
serv.setReadTimeout(readTimeout);
serv.startRunning();
addtask(serv);
@ -317,6 +374,65 @@ public class I2PTunnel implements Logging, EventDispatcher {
}
}
/**
* Run the HTTP server pointing at the host and port specified using the private i2p
* destination loaded from the specified file, replacing the HTTP headers
* so that the Host: specified is the one spoofed. <p />
*
* Sets the event "serverTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error)
* Also sets the event "openServerResult" = "ok" or "error" (displaying "Ready!" on the logger after
* 'ok'). So, success = serverTaskId != -1 and openServerResult = ok.
*
* @param args {hostname, portNumber, spoofedHost, privKeyFilename}
* @param l logger to receive events and output
*/
public void runHttpServer(String args[], Logging l) {
if (args.length == 4) {
InetAddress serverHost = null;
int portNum = -1;
File privKeyFile = null;
try {
serverHost = InetAddress.getByName(args[0]);
} catch (UnknownHostException uhe) {
l.log("unknown host");
_log.error(getPrefix() + "Error resolving " + args[0], uhe);
notifyEvent("serverTaskId", new Integer(-1));
return;
}
try {
portNum = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error(getPrefix() + "Port specified is not valid: " + args[1], nfe);
notifyEvent("serverTaskId", new Integer(-1));
return;
}
String spoofedHost = args[2];
privKeyFile = new File(args[3]);
if (!privKeyFile.canRead()) {
l.log("private key file does not exist");
_log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[3]);
notifyEvent("serverTaskId", new Integer(-1));
return;
}
I2PTunnelHTTPServer serv = new I2PTunnelHTTPServer(serverHost, portNum, privKeyFile, args[3], spoofedHost, l, (EventDispatcher) this, this);
serv.setReadTimeout(readTimeout);
serv.startRunning();
addtask(serv);
notifyEvent("serverTaskId", new Integer(serv.getId()));
return;
} else {
l.log("httpserver <host> <port> <spoofedhost> <privkeyfile>");
l.log(" creates an HTTP server that sends all incoming data\n"
+ " of its destination to host:port., filtering the HTTP\n"
+ " headers so it looks like the request is to the spoofed host.");
notifyEvent("serverTaskId", new Integer(-1));
}
}
/**
* Run the server pointing at the host and port specified using the private i2p
* destination loaded from the given base64 stream. <p />
@ -350,7 +466,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
return;
}
I2PTunnelServer serv = new I2PTunnelServer(serverHost, portNum, args[2], l, (EventDispatcher) this);
I2PTunnelServer serv = new I2PTunnelServer(serverHost, portNum, args[2], l, (EventDispatcher) this, this);
serv.setReadTimeout(readTimeout);
serv.startRunning();
addtask(serv);
@ -376,9 +492,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
*/
public void runClient(String args[], Logging l) {
if (args.length == 2) {
int port = -1;
int portNum = -1;
try {
port = Integer.parseInt(args[0]);
portNum = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
@ -386,13 +502,21 @@ public class I2PTunnel implements Logging, EventDispatcher {
return;
}
I2PTunnelTask task;
task = new I2PTunnelClient(port, args[1], l, ownDest, (EventDispatcher) this);
addtask(task);
notifyEvent("clientTaskId", new Integer(task.getId()));
try {
task = new I2PTunnelClient(portNum, args[1], l, ownDest, (EventDispatcher) this, this);
addtask(task);
notifyEvent("clientTaskId", new Integer(task.getId()));
} catch (IllegalArgumentException iae) {
_log.error(getPrefix() + "Invalid I2PTunnel config to create a client [" + host + ":"+ port + "]", iae);
l.log("Invalid I2PTunnel configuration [" + host + ":" + port + "]");
notifyEvent("clientTaskId", new Integer(-1));
}
} else {
l.log("client <port> <pubkey>|file:<pubkeyfile>");
l.log("client <port> <pubkey>[,<pubkey>]|file:<pubkeyfile>");
l.log(" creates a client that forwards port to the pubkey.\n"
+ " use 0 as port to get a free port assigned.");
+ " use 0 as port to get a free port assigned. If you specify\n"
+ " a comma delimited list of pubkeys, it will rotate among them\n"
+ " randomlyl");
notifyEvent("clientTaskId", new Integer(-1));
}
}
@ -423,9 +547,15 @@ public class I2PTunnel implements Logging, EventDispatcher {
proxy = args[1];
}
I2PTunnelTask task;
task = new I2PTunnelHTTPClient(port, l, ownDest, proxy, (EventDispatcher) this);
addtask(task);
notifyEvent("httpclientTaskId", new Integer(task.getId()));
try {
task = new I2PTunnelHTTPClient(port, l, ownDest, proxy, (EventDispatcher) this, this);
addtask(task);
notifyEvent("httpclientTaskId", new Integer(task.getId()));
} catch (IllegalArgumentException iae) {
_log.error(getPrefix() + "Invalid I2PTunnel config to create an httpclient [" + host + ":"+ port + "]", iae);
l.log("Invalid I2PTunnel configuration [" + host + ":" + port + "]");
notifyEvent("httpclientTaskId", new Integer(-1));
}
} else {
l.log("httpclient <port> [<proxy>]");
l.log(" creates a client that distributes HTTP requests.");
@ -460,7 +590,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
}
I2PTunnelTask task;
task = new I2PSOCKSTunnel(port, l, ownDest, (EventDispatcher) this);
task = new I2PSOCKSTunnel(port, l, ownDest, (EventDispatcher) this, this);
addtask(task);
notifyEvent("sockstunnelTaskId", new Integer(task.getId()));
} else {
@ -779,7 +909,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
if (allargs.length() != 0) {
I2PTunnelTask task;
// pings always use the main destination
task = new I2Ping(allargs, l, false, (EventDispatcher) this);
task = new I2Ping(allargs, l, false, (EventDispatcher) this, this);
addtask(task);
notifyEvent("pingTaskId", new Integer(task.getId()));
} else {

View File

@ -4,7 +4,11 @@
package net.i2p.i2ptunnel;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
@ -15,31 +19,48 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
private static final Log _log = new Log(I2PTunnelClient.class);
protected Destination dest;
/** list of Destination objects that we point at */
protected List dests;
private static final long DEFAULT_READ_TIMEOUT = 5*60*1000; // -1
protected long readTimeout = DEFAULT_READ_TIMEOUT;
public I2PTunnelClient(int localPort, String destination, Logging l, boolean ownDest, EventDispatcher notifyThis) {
super(localPort, ownDest, l, notifyThis, "SynSender");
/**
* @param destinations comma delimited list of peers we target
* @throws IllegalArgumentException if the I2PTunnel does not contain
* valid config to contact the router
*/
public I2PTunnelClient(int localPort, String destinations, Logging l,
boolean ownDest, EventDispatcher notifyThis,
I2PTunnel tunnel) throws IllegalArgumentException {
super(localPort, ownDest, l, notifyThis, "SynSender", tunnel);
if (waitEventValue("openBaseClientResult").equals("error")) {
notifyEvent("openClientResult", "error");
return;
}
try {
dest = I2PTunnel.destFromName(destination);
if (dest == null) {
l.log("Could not resolve " + destination + ".");
return;
StringTokenizer tok = new StringTokenizer(destinations, ",");
dests = new ArrayList(1);
while (tok.hasMoreTokens()) {
String destination = tok.nextToken();
try {
Destination dest = I2PTunnel.destFromName(destination);
if (dest == null)
l.log("Could not resolve " + destination);
else
dests.add(dest);
} catch (DataFormatException dfe) {
l.log("Bad format parsing \"" + destination + "\"");
}
} catch (DataFormatException e) {
l.log("Bad format in destination \"" + destination + "\".");
}
if (dests.size() <= 0) {
l.log("No target destinations found");
notifyEvent("openClientResult", "error");
return;
}
setName(getLocalPort() + " -> " + destination);
setName(getLocalPort() + " -> " + destinations);
startRunning();
@ -50,14 +71,34 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
public long getReadTimeout() { return readTimeout; }
protected void clientConnectionRun(Socket s) {
Destination dest = pickDestination();
I2PSocket i2ps = null;
try {
I2PSocket i2ps = createI2PSocket(dest);
i2ps = createI2PSocket(dest);
i2ps.setReadTimeout(readTimeout);
new I2PTunnelRunner(s, i2ps, sockLock, null);
new I2PTunnelRunner(s, i2ps, sockLock, null, mySockets);
} catch (Exception ex) {
_log.info("Error connecting", ex);
l.log(ex.getMessage());
closeSocket(s);
if (i2ps != null) {
synchronized (sockLock) {
mySockets.remove(sockLock);
}
}
}
}
private final Destination pickDestination() {
int size = dests.size();
if (size <= 0) {
if (_log.shouldLog(Log.ERROR))
_log.error("No client targets?!");
return null;
}
if (size == 1) // skip the rand in the most common case
return (Destination)dests.get(0);
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
return (Destination)dests.get(index);
}
}

View File

@ -17,6 +17,7 @@ import java.util.List;
import java.util.Properties;
import net.i2p.I2PException;
import net.i2p.client.I2PSession;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
@ -24,6 +25,7 @@ import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.Destination;
import net.i2p.util.EventDispatcher;
import net.i2p.util.I2PThread;
import net.i2p.util.SimpleTimer;
import net.i2p.util.Log;
public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runnable {
@ -31,13 +33,13 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
private static final Log _log = new Log(I2PTunnelClientBase.class);
protected Logging l;
private static final long DEFAULT_CONNECT_TIMEOUT = 60 * 1000;
static final long DEFAULT_CONNECT_TIMEOUT = 60 * 1000;
private static volatile long __clientId = 0;
protected long _clientId;
protected Object sockLock = new Object(); // Guards sockMgr and mySockets
private I2PSocketManager sockMgr;
private List mySockets = new ArrayList();
protected I2PSocketManager sockMgr;
protected List mySockets = new ArrayList();
protected Destination dest = null;
private int localPort;
@ -55,35 +57,78 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
private String handlerName;
private Object conLock = new Object();
/** List of Socket for those accept()ed but not yet started up */
private List _waitingSockets;
/** How many connections will we allow to be in the process of being built at once? */
private int _numConnectionBuilders;
/** How long will we allow sockets to sit in the _waitingSockets map before killing them? */
private int _maxWaitTime;
/**
* How many concurrent connections this I2PTunnel instance will allow to be
* in the process of connecting (or if less than 1, there is no limit)?
*/
public static final String PROP_NUM_CONNECTION_BUILDERS = "i2ptunnel.numConnectionBuilders";
/**
* How long will we let a socket wait after being accept()ed without getting
* pumped through a connection builder (in milliseconds). If this time is
* reached, the socket is unceremoniously closed and discarded. If the max
* wait time is less than 1, there is no limit.
*
*/
public static final String PROP_MAX_WAIT_TIME = "i2ptunnel.maxWaitTime";
private static final int DEFAULT_NUM_CONNECTION_BUILDERS = 5;
private static final int DEFAULT_MAX_WAIT_TIME = 30*1000;
//public I2PTunnelClientBase(int localPort, boolean ownDest,
// Logging l) {
// I2PTunnelClientBase(localPort, ownDest, l, (EventDispatcher)null);
//}
public I2PTunnelClientBase(int localPort, boolean ownDest, Logging l, EventDispatcher notifyThis, String handlerName) {
super(localPort + " (uninitialized)", notifyThis);
/**
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
* badly that we cant create a socketManager
*/
public I2PTunnelClientBase(int localPort, boolean ownDest, Logging l,
EventDispatcher notifyThis, String handlerName,
I2PTunnel tunnel) throws IllegalArgumentException{
super(localPort + " (uninitialized)", notifyThis, tunnel);
_clientId = ++__clientId;
this.localPort = localPort;
this.l = l;
this.handlerName = handlerName + _clientId;
synchronized (sockLock) {
if (ownDest) {
sockMgr = buildSocketManager();
} else {
sockMgr = getSocketManager();
while (sockMgr == null) {
synchronized (sockLock) {
if (ownDest) {
sockMgr = buildSocketManager();
} else {
sockMgr = getSocketManager();
}
}
if (sockMgr == null) {
_log.log(Log.CRIT, "Unable to create socket manager");
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
}
if (sockMgr == null) throw new NullPointerException();
if (sockMgr == null) {
l.log("Invalid I2CP configuration");
throw new IllegalArgumentException("Socket manager could not be created");
}
l.log("I2P session created");
getTunnel().addSession(sockMgr.getSession());
Thread t = new I2PThread(this);
t.setName("Client " + _clientId);
listenerReady = false;
t.start();
open = true;
synchronized (this) {
while (!listenerReady) {
while (!listenerReady && open) {
try {
wait();
} catch (InterruptedException e) {
@ -92,28 +137,82 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
}
}
configurePool(tunnel);
if (open && listenerReady) {
l.log("Ready! Port " + getLocalPort());
notifyEvent("openBaseClientResult", "ok");
} else {
l.log("Error!");
l.log("Error listening - please see the logs!");
notifyEvent("openBaseClientResult", "error");
}
}
/**
* build and configure the pool handling accept()ed but not yet
* established connections
*
*/
private void configurePool(I2PTunnel tunnel) {
_waitingSockets = new ArrayList(4);
Properties opts = tunnel.getClientOptions();
String maxWait = opts.getProperty(PROP_MAX_WAIT_TIME, DEFAULT_MAX_WAIT_TIME+"");
try {
_maxWaitTime = Integer.parseInt(maxWait);
} catch (NumberFormatException nfe) {
_maxWaitTime = DEFAULT_MAX_WAIT_TIME;
}
String numBuild = opts.getProperty(PROP_NUM_CONNECTION_BUILDERS, DEFAULT_NUM_CONNECTION_BUILDERS+"");
try {
_numConnectionBuilders = Integer.parseInt(numBuild);
} catch (NumberFormatException nfe) {
_numConnectionBuilders = DEFAULT_NUM_CONNECTION_BUILDERS;
}
for (int i = 0; i < _numConnectionBuilders; i++) {
String name = "ClientBuilder" + _clientId + '.' + i;
I2PThread b = new I2PThread(new TunnelConnectionBuilder(), name);
b.setDaemon(true);
b.start();
}
}
private static I2PSocketManager socketManager;
protected static synchronized I2PSocketManager getSocketManager() {
if (socketManager == null) {
socketManager = buildSocketManager();
protected synchronized I2PSocketManager getSocketManager() {
return getSocketManager(getTunnel());
}
protected static synchronized I2PSocketManager getSocketManager(I2PTunnel tunnel) {
if (socketManager != null) {
I2PSession s = socketManager.getSession();
if ( (s == null) || (s.isClosed()) ) {
_log.info("Building a new socket manager since the old one closed [s=" + s + "]");
socketManager = buildSocketManager(tunnel);
} else {
_log.info("Not building a new socket manager since the old one is open [s=" + s + "]");
}
} else {
_log.info("Building a new socket manager since there is no other one");
socketManager = buildSocketManager(tunnel);
}
return socketManager;
}
protected static I2PSocketManager buildSocketManager() {
protected I2PSocketManager buildSocketManager() {
return buildSocketManager(getTunnel());
}
protected static I2PSocketManager buildSocketManager(I2PTunnel tunnel) {
Properties props = new Properties();
props.putAll(System.getProperties());
return I2PSocketManagerFactory.createManager(I2PTunnel.host, Integer.parseInt(I2PTunnel.port), props);
if (tunnel == null)
props.putAll(System.getProperties());
else
props.putAll(tunnel.getClientOptions());
I2PSocketManager sockManager = I2PSocketManagerFactory.createManager(tunnel.host, Integer.parseInt(tunnel.port), props);
if (sockManager == null) return null;
sockManager.setName("Client");
return sockManager;
}
public final int getLocalPort() {
@ -122,9 +221,9 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
protected final InetAddress getListenHost(Logging l) {
try {
return InetAddress.getByName(I2PTunnel.listenHost);
return InetAddress.getByName(getTunnel().listenHost);
} catch (UnknownHostException uhe) {
l.log("Could not find listen host to bind to [" + I2PTunnel.host + "]");
l.log("Could not find listen host to bind to [" + getTunnel().host + "]");
_log.error("Error finding host to bind", uhe);
notifyEvent("openBaseClientResult", "error");
return null;
@ -147,9 +246,24 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
* create the default options (using the default timeout, etc)
*
*/
private I2PSocketOptions getDefaultOptions() {
I2PSocketOptions opts = new I2PSocketOptions();
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
protected I2PSocketOptions getDefaultOptions() {
Properties defaultOpts = getTunnel().getClientOptions();
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
return opts;
}
/**
* create the default options (using the default timeout, etc)
*
*/
protected I2PSocketOptions getDefaultOptions(Properties overrides) {
Properties defaultOpts = getTunnel().getClientOptions();
defaultOpts.putAll(overrides);
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
return opts;
}
@ -193,7 +307,14 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
public final void run() {
try {
InetAddress addr = getListenHost(l);
if (addr == null) return;
if (addr == null) {
open = false;
synchronized (this) {
notifyAll();
}
synchronized (_waitingSockets) { _waitingSockets.notifyAll(); }
return;
}
ss = new ServerSocket(localPort, 0, addr);
// If a free port was requested, find out what we got
@ -201,7 +322,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
localPort = ss.getLocalPort();
}
notifyEvent("clientLocalPort", new Integer(ss.getLocalPort()));
l.log("Listening for clients on port " + localPort + " of " + I2PTunnel.listenHost);
l.log("Listening for clients on port " + localPort + " of " + getTunnel().listenHost);
// Notify constructor that port is ready
synchronized (this) {
@ -224,8 +345,18 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
manageConnection(s);
}
} catch (IOException ex) {
_log.error("Error listening for connections", ex);
_log.error("Error listening for connections on " + localPort, ex);
notifyEvent("openBaseClientResult", "error");
synchronized (sockLock) {
mySockets.clear();
}
open = false;
synchronized (this) {
notifyAll();
}
}
synchronized (_waitingSockets) {
_waitingSockets.notifyAll();
}
}
@ -235,7 +366,52 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
* @param s Socket to take care of
*/
protected void manageConnection(Socket s) {
new ClientConnectionRunner(s, handlerName);
if (s == null) return;
if (_numConnectionBuilders <= 0) {
new I2PThread(new BlockingRunner(s), "Clinet run").start();
return;
}
if (_maxWaitTime > 0)
SimpleTimer.getInstance().addEvent(new CloseEvent(s), _maxWaitTime);
synchronized (_waitingSockets) {
_waitingSockets.add(s);
_waitingSockets.notifyAll();
}
}
/**
* Blocking runner, used during the connection establishment whenever we
* are not using the queued builders.
*
*/
private class BlockingRunner implements Runnable {
private Socket _s;
public BlockingRunner(Socket s) { _s = s; }
public void run() {
clientConnectionRun(_s);
}
}
/**
* Remove and close the socket from the waiting list, if it is still there.
*
*/
private class CloseEvent implements SimpleTimer.TimedEvent {
private Socket _s;
public CloseEvent(Socket s) { _s = s; }
public void timeReached() {
boolean stillWaiting = false;
synchronized (_waitingSockets) {
stillWaiting = _waitingSockets.remove(_s);
}
if (stillWaiting) {
try { _s.close(); } catch (IOException ioe) {}
if (_log.shouldLog(Log.INFO))
_log.info("Closed a waiting socket because of backlog");
}
}
}
public boolean close(boolean forced) {
@ -254,6 +430,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
}
return false;
}
I2PSession session = sockMgr.getSession();
if (session != null) {
getTunnel().removeSession(session);
}
l.log("Closing client " + toString());
try {
if (ss != null) ss.close();
@ -263,8 +443,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
}
l.log("Client closed.");
open = false;
return true;
}
synchronized (_waitingSockets) { _waitingSockets.notifyAll(); }
return true;
}
public static void closeSocket(Socket s) {
@ -274,20 +456,30 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
_log.error("Could not close socket", ex);
}
}
private static volatile long __runnerId = 0;
public class ClientConnectionRunner extends I2PThread {
private Socket s;
public ClientConnectionRunner(Socket s, String name) {
this.s = s;
setName(name + '.' + (++__runnerId));
start();
}
public void run() {
clientConnectionRun(s);
/**
* Pool runner pulling sockets off the waiting list and pushing them
* through clientConnectionRun. This dies when the I2PTunnel instance
* is closed.
*
*/
private class TunnelConnectionBuilder implements Runnable {
public void run() {
Socket s = null;
while (open) {
try {
synchronized (_waitingSockets) {
if (_waitingSockets.size() <= 0)
_waitingSockets.wait();
else
s = (Socket)_waitingSockets.remove(0);
}
} catch (InterruptedException ie) {}
if (s != null)
clientConnectionRun(s);
s = null;
}
}
}

View File

@ -9,15 +9,20 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;
import java.util.HashMap;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.Clock;
import net.i2p.util.EventDispatcher;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
@ -42,10 +47,12 @@ import net.i2p.util.Log;
public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable {
private static final Log _log = new Log(I2PTunnelHTTPClient.class);
private String wwwProxy;
private List proxyList;
private HashMap addressHelpers = new HashMap();
private final static byte[] ERR_REQUEST_DENIED =
("HTTP/1.1 404 Not Found\r\n"+
("HTTP/1.1 403 Access Denied\r\n"+
"Content-Type: text/html; charset=iso-8859-1\r\n"+
"Cache-control: no-cache\r\n"+
"\r\n"+
@ -54,57 +61,130 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
.getBytes();
private final static byte[] ERR_DESTINATION_UNKNOWN =
("HTTP/1.1 404 Not Found\r\n"+
("HTTP/1.1 503 Service Unavailable\r\n"+
"Content-Type: text/html; charset=iso-8859-1\r\n"+
"Cache-control: no-cache\r\n"+
"\r\n"+
"<html><body><H1>I2P ERROR: NOT FOUND</H1>"+
"That Desitination was not found. Perhaps you pasted in the wrong "+
"BASE64 I2P Destination or the link you are following is bad. "+
"The host (or the WWW proxy, if you're using one) could also be "+
"temporarily offline. You may want to <b>retry</b>. "+
"<html><body><H1>I2P ERROR: DESTINATION NOT FOUND</H1>"+
"That I2P Destination was not found. Perhaps you pasted in the "+
"wrong BASE64 I2P Destination or the link you are following is "+
"bad. The host (or the WWW proxy, if you're using one) could also "+
"be temporarily offline. You may want to <b>retry</b>. "+
"Could not find the following Destination:<BR><BR>")
.getBytes();
private final static byte[] ERR_TIMEOUT =
("HTTP/1.1 404 Not Found\r\n"+
("HTTP/1.1 504 Gateway Timeout\r\n"+
"Content-Type: text/html; charset=iso-8859-1\r\n"+
"Cache-control: no-cache\r\n\r\n"+
"<html><body><H1>I2P ERROR: TIMEOUT</H1>"+
"That Desitination was reachable, but timed out getting a "+
"That Destination was reachable, but timed out getting a "+
"response. This is likely a temporary error, so you should simply "+
"try to refresh, though if the problem persists, the remote "+
"destination may have issues. Could not get a response from "+
"the following Destination:<BR><BR>")
.getBytes();
private final static byte[] ERR_NO_OUTPROXY =
("HTTP/1.1 503 Service Unavailable\r\n"+
"Content-Type: text/html; charset=iso-8859-1\r\n"+
"Cache-control: no-cache\r\n"+
"\r\n"+
"<html><body><H1>I2P ERROR: No outproxy found</H1>"+
"Your request was for a site outside of I2P, but you have no "+
"HTTP outproxy configured. Please configure an outproxy in I2PTunnel")
.getBytes();
/** used to assign unique IDs to the threads / clients. no logic or functionality */
private static volatile long __clientId = 0;
public I2PTunnelHTTPClient(int localPort, Logging l, boolean ownDest, String wwwProxy, EventDispatcher notifyThis) {
super(localPort, ownDest, l, notifyThis, "HTTPHandler " + (++__clientId));
/**
* @throws IllegalArgumentException if the I2PTunnel does not contain
* valid config to contact the router
*/
public I2PTunnelHTTPClient(int localPort, Logging l, boolean ownDest,
String wwwProxy, EventDispatcher notifyThis,
I2PTunnel tunnel) throws IllegalArgumentException {
super(localPort, ownDest, l, notifyThis, "HTTPHandler " + (++__clientId), tunnel);
if (waitEventValue("openBaseClientResult").equals("error")) {
notifyEvent("openHTTPClientResult", "error");
return;
}
this.wwwProxy = wwwProxy;
proxyList = new ArrayList();
if (wwwProxy != null) {
StringTokenizer tok = new StringTokenizer(wwwProxy, ",");
while (tok.hasMoreTokens())
proxyList.add(tok.nextToken().trim());
}
setName(getLocalPort() + " -> HTTPClient [WWW outproxy: " + this.wwwProxy + "]");
setName(getLocalPort() + " -> HTTPClient [WWW outproxy list: " + wwwProxy + "]");
startRunning();
notifyEvent("openHTTPClientResult", "ok");
}
private String getPrefix() { return "Client[" + _clientId + "]: "; }
private String getPrefix(long requestId) { return "Client[" + _clientId + "/" + requestId + "]: "; }
private String selectProxy() {
synchronized (proxyList) {
int size = proxyList.size();
if (size <= 0) {
if (_log.shouldLog(Log.INFO))
_log.info("Proxy list is empty - no outproxy available");
l.log("Proxy list is emtpy - no outproxy available");
return null;
}
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
String proxy = (String)proxyList.get(index);
return proxy;
}
}
private static final int DEFAULT_READ_TIMEOUT = 60*1000;
/**
* create the default options (using the default timeout, etc)
*
*/
protected I2PSocketOptions getDefaultOptions() {
Properties defaultOpts = getTunnel().getClientOptions();
if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, ""+DEFAULT_READ_TIMEOUT);
//if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
// defaultOpts.setProperty("i2p.streaming.inactivityTimeout", ""+DEFAULT_READ_TIMEOUT);
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
return opts;
}
/**
* create the default options (using the default timeout, etc)
*
*/
protected I2PSocketOptions getDefaultOptions(Properties overrides) {
Properties defaultOpts = getTunnel().getClientOptions();
defaultOpts.putAll(overrides);
if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, ""+DEFAULT_READ_TIMEOUT);
if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
defaultOpts.setProperty("i2p.streaming.inactivityTimeout", ""+DEFAULT_READ_TIMEOUT);
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
return opts;
}
private static long __requestId = 0;
protected void clientConnectionRun(Socket s) {
OutputStream out = null;
String targetRequest = null;
boolean usingWWWProxy = false;
InactivityTimeoutThread timeoutThread = null;
String currentProxy = null;
long requestId = ++__requestId;
try {
out = s.getOutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(), "ISO-8859-1"));
@ -112,7 +192,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
StringBuffer newRequest = new StringBuffer();
while ((line = br.readLine()) != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Line=[" + line + "]");
_log.debug(getPrefix(requestId) + "Line=[" + line + "]");
if (line.startsWith("Connection: ") ||
line.startsWith("Keep-Alive: ") ||
@ -121,15 +201,26 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
if (method == null) { // first line (GET /base64/realaddr)
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Method is null for [" + line + "]");
_log.debug(getPrefix(requestId) + "Method is null for [" + line + "]");
int pos = line.indexOf(" ");
if (pos == -1) break;
method = line.substring(0, pos);
String request = line.substring(pos + 1);
if (request.startsWith("/") && System.getProperty("i2ptunnel.noproxy") != null) {
if (request.startsWith("/") && getTunnel().getClientOptions().getProperty("i2ptunnel.noproxy") != null) {
request = "http://i2p" + request;
} else if (request.startsWith("/eepproxy/")) {
// /eepproxy/foo.i2p/bar/baz.html HTTP/1.0
String subRequest = request.substring("/eepproxy/".length());
int protopos = subRequest.indexOf(" ");
String uri = subRequest.substring(0, protopos);
if (uri.indexOf("/") == -1) {
uri = uri + "/";
}
// "http://" + "foo.i2p/bar/baz.html" + " HTTP/1.0"
request = "http://" + uri + subRequest.substring(protopos);
}
pos = request.indexOf("//");
if (pos == -1) {
method = null;
@ -151,13 +242,73 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
if (host.toLowerCase().endsWith(".i2p")) {
destination = host;
host = getHostName(destination);
if ( (host != null) && ("i2p".equals(host)) ) {
int pos2;
if ((pos2 = request.indexOf("?")) != -1) {
// Try to find an address helper in the fragments
// and split the request into it's component parts for rebuilding later
String fragments = request.substring(pos2 + 1);
String uriPath = request.substring(0, pos2);
pos2 = fragments.indexOf(" ");
String protocolVersion = fragments.substring(pos2 + 1);
String urlEncoding = "";
fragments = fragments.substring(0, pos2);
fragments = fragments + "&";
String fragment;
while(fragments.length() > 0) {
pos2 = fragments.indexOf("&");
fragment = fragments.substring(0, pos2);
fragments = fragments.substring(pos2 + 1);
if (fragment.startsWith("i2paddresshelper")) {
pos2 = fragment.indexOf("=");
if (pos2 >= 0) {
addressHelpers.put(destination,fragment.substring(pos2 + 1));
}
} else {
// append each fragment unless it's the address helper
if ("".equals(urlEncoding)) {
urlEncoding = "?" + fragment;
} else {
urlEncoding = urlEncoding + "&" + fragment;
}
}
}
// reconstruct the request minus the i2paddresshelper GET var
request = uriPath + urlEncoding + " " + protocolVersion;
}
String addressHelper = (String) addressHelpers.get(destination);
if (addressHelper != null) {
destination = addressHelper;
host = getHostName(destination);
}
}
line = method + " " + request.substring(pos);
} else if (host.indexOf(".") != -1) {
// The request must be forwarded to a WWW proxy
destination = wwwProxy;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Before selecting outproxy for " + host);
currentProxy = selectProxy();
if (_log.shouldLog(Log.DEBUG))
_log.debug("After selecting outproxy for " + host + ": " + currentProxy);
if (currentProxy == null) {
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix(requestId) + "Host wants to be outproxied, but we dont have any!");
l.log("No HTTP outproxy found for the request.");
if (out != null) {
out.write(ERR_NO_OUTPROXY);
out.write("<p /><i>Generated on: ".getBytes());
out.write(new Date().toString().getBytes());
out.write("</i></body></html>\n".getBytes());
out.flush();
}
s.close();
return;
}
destination = currentProxy;
usingWWWProxy = true;
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Host doesnt end with .i2p and it contains a period [" + host + "]: wwwProxy!");
_log.debug(getPrefix(requestId) + "Host doesnt end with .i2p and it contains a period [" + host + "]: wwwProxy!");
} else {
request = request.substring(pos + 1);
pos = request.indexOf("/");
@ -167,31 +318,54 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
boolean isValid = usingWWWProxy || isSupportedAddress(host, protocol);
if (!isValid) {
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "notValid(" + host + ")");
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(requestId) + "notValid(" + host + ")");
method = null;
destination = null;
break;
} else if (!usingWWWProxy) {
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "host=getHostName(" + destination + ")");
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(requestId) + "host=getHostName(" + destination + ")");
host = getHostName(destination); // hide original host
}
if (_log.shouldLog(Log.DEBUG)) {
_log.debug(getPrefix() + "METHOD:" + method + ":");
_log.debug(getPrefix() + "PROTOC:" + protocol + ":");
_log.debug(getPrefix() + "HOST :" + host + ":");
_log.debug(getPrefix() + "DEST :" + destination + ":");
_log.debug(getPrefix(requestId) + "METHOD:" + method + ":");
_log.debug(getPrefix(requestId) + "PROTOC:" + protocol + ":");
_log.debug(getPrefix(requestId) + "HOST :" + host + ":");
_log.debug(getPrefix(requestId) + "DEST :" + destination + ":");
}
} else {
if (line.startsWith("Host: ") && !usingWWWProxy) {
line = "Host: " + host;
if (_log.shouldLog(Log.INFO))
_log.info(getPrefix() + "Setting host = " + host);
_log.info(getPrefix(requestId) + "Setting host = " + host);
} else if (line.startsWith("User-Agent: ")) {
// always stripped, added back at the end
line = null;
continue;
} else if (line.startsWith("Accept")) {
// strip the accept-blah headers, as they vary dramatically from
// browser to browser
line = null;
continue;
} else if (line.startsWith("Referer: ")) {
// Shouldn't we be more specific, like accepting in-site referers ?
//line = "Referer: i2p";
line = null;
continue; // completely strip the line
} else if (line.startsWith("Via: ")) {
//line = "Via: i2p";
line = null;
continue; // completely strip the line
} else if (line.startsWith("From: ")) {
//line = "From: i2p";
line = null;
continue; // completely strip the line
}
}
if (line.length() == 0) {
newRequest.append("User-Agent: MYOB/6.66 (AN/ON)\r\n");
newRequest.append("Connection: close\r\n\r\n");
break;
} else {
@ -199,7 +373,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "NewRequest header: [" + newRequest.toString() + "]");
_log.debug(getPrefix(requestId) + "NewRequest header: [" + newRequest.toString() + "]");
while (br.ready()) { // empty the buffer (POST requests)
int i = br.read();
@ -221,124 +395,48 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Destination: " + destination);
_log.debug(getPrefix(requestId) + "Destination: " + destination);
Destination dest = I2PTunnel.destFromName(destination);
if (dest == null) {
l.log("Could not resolve " + destination + ".");
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to resolve " + destination + " (proxy? " + usingWWWProxy + ", request: " + targetRequest);
writeErrorMessage(ERR_DESTINATION_UNKNOWN, out, targetRequest, usingWWWProxy, destination);
s.close();
return;
}
String remoteID;
I2PSocket i2ps = createI2PSocket(dest);
Properties opts = new Properties();
opts.setProperty("i2p.streaming.inactivityTimeout", ""+120*1000);
// 1 == disconnect. see ConnectionOptions in the new streaming lib, which i
// dont want to hard link to here
opts.setProperty("i2p.streaming.inactivityTimeoutAction", ""+1);
I2PSocket i2ps = createI2PSocket(dest, getDefaultOptions(opts));
byte[] data = newRequest.toString().getBytes("ISO-8859-1");
I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data);
timeoutThread = new InactivityTimeoutThread(runner, out, targetRequest, usingWWWProxy, s);
timeoutThread.start();
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
I2PTunnelRunner runner = new I2PTunnelHTTPClientRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
} catch (SocketException ex) {
if (timeoutThread != null) timeoutThread.disable();
_log.info(getPrefix() + "Error trying to connect", ex);
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
l.log(ex.getMessage());
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, wwwProxy);
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
closeSocket(s);
} catch (IOException ex) {
if (timeoutThread != null) timeoutThread.disable();
_log.info(getPrefix() + "Error trying to connect", ex);
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
l.log(ex.getMessage());
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, wwwProxy);
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
closeSocket(s);
} catch (I2PException ex) {
if (timeoutThread != null) timeoutThread.disable();
_log.info("getPrefix() + Error trying to connect", ex);
_log.info("getPrefix(requestId) + Error trying to connect", ex);
l.log(ex.getMessage());
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, wwwProxy);
closeSocket(s);
}
}
private static final long INACTIVITY_TIMEOUT = 120 * 1000;
private static volatile long __timeoutId = 0;
private class InactivityTimeoutThread extends I2PThread {
private Socket s;
private I2PTunnelRunner _runner;
private OutputStream _out;
private String _targetRequest;
private boolean _useWWWProxy;
private boolean _disabled;
private Object _disableLock = new Object();
public InactivityTimeoutThread(I2PTunnelRunner runner, OutputStream out, String targetRequest,
boolean useWWWProxy, Socket s) {
this.s = s;
_runner = runner;
_out = out;
_targetRequest = targetRequest;
_useWWWProxy = useWWWProxy;
_disabled = false;
long timeoutId = ++__timeoutId;
setName("InactivityThread " + getPrefix() + timeoutId);
}
public void disable() {
_disabled = true;
synchronized (_disableLock) {
_disableLock.notifyAll();
}
}
public void run() {
while (!_disabled) {
if (_runner.isFinished()) {
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "HTTP client request completed prior to timeout");
return;
}
if (_runner.getLastActivityOn() < Clock.getInstance().now() - INACTIVITY_TIMEOUT) {
if (_runner.getStartedOn() < Clock.getInstance().now() - INACTIVITY_TIMEOUT) {
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + "HTTP client request timed out (lastActivity: "
+ new Date(_runner.getLastActivityOn()) + ", startedOn: "
+ new Date(_runner.getStartedOn()) + ")");
timeout();
return;
} else {
// runner hasn't been going to long enough
}
} else {
// there has been activity in the period
}
synchronized (_disableLock) {
try {
_disableLock.wait(INACTIVITY_TIMEOUT);
} catch (InterruptedException ie) {
}
}
}
}
private void timeout() {
_log.info(getPrefix() + "Inactivity timeout reached");
l.log("Inactivity timeout reached");
if (_out != null) {
try {
if (_runner.getLastActivityOn() > 0) {
// some data has been sent, so don't 404 it
} else {
writeErrorMessage(ERR_TIMEOUT, _out, _targetRequest, _useWWWProxy, wwwProxy);
}
} catch (IOException ioe) {
_log.warn(getPrefix() + "Error writing out the 'timeout' message", ioe);
}
} else {
_log.warn(getPrefix() + "Client disconnected before we could say we timed out");
}
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
closeSocket(s);
}
}
private final static String getHostName(String host) {
if (host == null) return null;
try {
Destination dest = I2PTunnel.destFromName(host);
if (dest == null) return "i2p";
@ -348,6 +446,30 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
}
private class OnTimeout implements Runnable {
private Socket _socket;
private OutputStream _out;
private String _target;
private boolean _usingProxy;
private String _wwwProxy;
private long _requestId;
public OnTimeout(Socket s, OutputStream out, String target, boolean usingProxy, String wwwProxy, long id) {
_socket = s;
_out = out;
_target = target;
_usingProxy = usingProxy;
_wwwProxy = wwwProxy;
_requestId = id;
}
public void run() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Timeout occured requesting " + _target);
handleHTTPClientException(new RuntimeException("Timeout"), _out,
_target, _usingProxy, _wwwProxy, _requestId);
closeSocket(_socket);
}
}
private static void writeErrorMessage(byte[] errMessage, OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy) throws IOException {
if (out != null) {
@ -364,15 +486,18 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
private void handleHTTPClientException(Exception ex, OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy) {
boolean usingWWWProxy, String wwwProxy, long requestId) {
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix(requestId) + "Error sending to " + wwwProxy + " (proxy? " + usingWWWProxy + ", request: " + targetRequest, ex);
if (out != null) {
try {
writeErrorMessage(ERR_DESTINATION_UNKNOWN, out, targetRequest, usingWWWProxy, wwwProxy);
} catch (IOException ioe) {
_log.warn(getPrefix() + "Error writing out the 'destination was unknown' " + "message", ioe);
_log.warn(getPrefix(requestId) + "Error writing out the 'destination was unknown' " + "message", ioe);
}
} else {
_log.warn(getPrefix() + "Client disconnected before we could say that destination " + "was unknown", ex);
_log.warn(getPrefix(requestId) + "Client disconnected before we could say that destination " + "was unknown", ex);
}
}

View File

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

View File

@ -0,0 +1,160 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.Iterator;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.data.DataHelper;
import net.i2p.util.EventDispatcher;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Simple extension to the I2PTunnelServer that filters the HTTP
* headers sent from the client to the server, replacing the Host
* header with whatever this instance has been configured with.
*
*/
public class I2PTunnelHTTPServer extends I2PTunnelServer {
private final static Log _log = new Log(I2PTunnelHTTPServer.class);
/** what Host: should we seem to be to the webserver? */
private String _spoofHost;
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;
}
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;
}
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;
}
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.
*
*/
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.
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) {
_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);
}
}
private String formatHeaders(Properties headers, StringBuffer command) {
StringBuffer buf = new StringBuffer(command.length() + headers.size() * 64);
buf.append(command.toString()).append('\n');
for (Iterator iter = headers.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
String val = headers.getProperty(name);
buf.append(name).append(": ").append(val).append('\n');
}
buf.append('\n');
return buf.toString();
}
private Properties readHeaders(InputStream in, StringBuffer command) throws IOException {
Properties headers = new Properties();
StringBuffer buf = new StringBuffer(128);
boolean ok = DataHelper.readLine(in, command);
if (!ok) throw new IOException("EOF reached while reading the HTTP command [" + command.toString() + "]");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read the http command [" + command.toString() + "]");
while (true) {
buf.setLength(0);
ok = DataHelper.readLine(in, buf);
if (!ok) throw new IOException("EOF reached before the end of the headers [" + buf.toString() + "]");
if ( (buf.length() <= 1) && ( (buf.charAt(0) == '\n') || (buf.charAt(0) == '\r') ) ) {
// end of headers reached
return headers;
} else {
int split = buf.indexOf(": ");
if (split <= 0) throw new IOException("Invalid HTTP header, missing colon [" + buf.toString() + "]");
String name = buf.substring(0, split);
String value = buf.substring(split+2); // ": "
headers.setProperty(name, value);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read the header [" + name + "] = [" + value + "]");
}
}
}
}

View File

@ -3,7 +3,6 @@
*/
package net.i2p.i2ptunnel;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
@ -11,9 +10,11 @@ import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.HashMap;
import java.util.List;
import net.i2p.client.I2PSession;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.data.ByteArray;
import net.i2p.util.ByteCache;
import net.i2p.util.Clock;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
@ -29,7 +30,7 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
* Sun's impl of BufferedOutputStream), but that is the streaming
* api's job...
*/
static int MAX_PACKET_SIZE = 1024 * 32;
static int MAX_PACKET_SIZE = 1024 * 4;
static final int NETWORK_BUFFER_SIZE = MAX_PACKET_SIZE;
@ -38,23 +39,43 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
Object slock, finishLock = new Object();
boolean finished = false;
HashMap ostreams, sockets;
I2PSession session;
byte[] initialData;
byte[] initialI2PData;
byte[] initialSocketData;
/** when the last data was sent/received (or -1 if never) */
private long lastActivityOn;
/** when the runner started up */
private long startedOn;
private List sockList;
/** if we die before receiving any data, run this job */
private Runnable onTimeout;
private long totalSent;
private long totalReceived;
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialData) {
private volatile long __forwarderId;
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, List sockList) {
this(s, i2ps, slock, initialI2PData, null, sockList, null);
}
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, byte[] initialSocketData, List sockList) {
this(s, i2ps, slock, initialI2PData, initialSocketData, sockList, null);
}
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, List sockList, Runnable onTimeout) {
this(s, i2ps, slock, initialI2PData, null, sockList, onTimeout);
}
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, byte[] initialSocketData, List sockList, Runnable onTimeout) {
this.sockList = sockList;
this.s = s;
this.i2ps = i2ps;
this.slock = slock;
this.initialData = initialData;
this.initialI2PData = initialI2PData;
this.initialSocketData = initialSocketData;
this.onTimeout = onTimeout;
lastActivityOn = -1;
startedOn = Clock.getInstance().now();
if (_log.shouldLog(Log.INFO))
_log.info("I2PTunnelRunner started");
_runnerId = ++__runnerId;
__forwarderId = i2ps.hashCode();
setName("I2PTunnelRunner " + _runnerId);
start();
}
@ -90,51 +111,83 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
return startedOn;
}
protected InputStream getSocketIn() throws IOException { return s.getInputStream(); }
protected OutputStream getSocketOut() throws IOException { return s.getOutputStream(); }
public void run() {
try {
InputStream in = s.getInputStream();
OutputStream out = new BufferedOutputStream(s.getOutputStream(), NETWORK_BUFFER_SIZE);
InputStream in = getSocketIn();
OutputStream out = getSocketOut(); // = new BufferedOutputStream(s.getOutputStream(), NETWORK_BUFFER_SIZE);
i2ps.setSocketErrorListener(this);
InputStream i2pin = i2ps.getInputStream();
OutputStream i2pout = new BufferedOutputStream(i2ps.getOutputStream(), MAX_PACKET_SIZE);
if (initialData != null) {
OutputStream i2pout = i2ps.getOutputStream(); //new BufferedOutputStream(i2ps.getOutputStream(), MAX_PACKET_SIZE);
if (initialI2PData != null) {
synchronized (slock) {
i2pout.write(initialData);
i2pout.flush();
i2pout.write(initialI2PData);
//i2pout.flush();
}
}
Thread t1 = new StreamForwarder(in, i2pout);
Thread t2 = new StreamForwarder(i2pin, out);
if (initialSocketData != null) {
out.write(initialSocketData);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Initial data " + (initialI2PData != null ? initialI2PData.length : 0)
+ " written to I2P, " + (initialSocketData != null ? initialSocketData.length : 0)
+ " written to the socket, starting forwarders");
Thread t1 = new StreamForwarder(in, i2pout, true);
Thread t2 = new StreamForwarder(i2pin, out, false);
synchronized (finishLock) {
while (!finished) {
finishLock.wait();
}
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("At least one forwarder completed, closing and joining");
// this task is useful for the httpclient
if (onTimeout != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("runner has a timeout job, totalReceived = " + totalReceived
+ " totalSent = " + totalSent + " job = " + onTimeout);
if ( (totalSent <= 0) && (totalReceived <= 0) )
onTimeout.run();
}
// now one connection is dead - kill the other as well.
s.close();
s = null;
i2ps.close();
i2ps = null;
t1.join();
t2.join();
t1.join(30*1000);
t2.join(30*1000);
} catch (InterruptedException ex) {
_log.error("Interrupted", ex);
if (_log.shouldLog(Log.ERROR))
_log.error("Interrupted", ex);
} catch (IOException ex) {
ex.printStackTrace();
_log.debug("Error forwarding", ex);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Error forwarding", ex);
} catch (Exception e) {
_log.error("Internal error", e);
if (_log.shouldLog(Log.ERROR))
_log.error("Internal error", e);
} finally {
removeRef();
try {
if (s != null) s.close();
if (i2ps != null) i2ps.close();
if (s != null)
s.close();
} catch (IOException ex) {
ex.printStackTrace();
_log.error("Could not close socket", ex);
if (_log.shouldLog(Log.ERROR))
_log.error("Could not close java socket", ex);
}
if (i2ps != null) {
try {
i2ps.close();
} catch (IOException ex) {
if (_log.shouldLog(Log.ERROR))
_log.error("Could not close I2PSocket", ex);
}
i2ps.setSocketErrorListener(null);
}
}
}
public void errorOccurred() {
synchronized (finishLock) {
finished = true;
@ -142,63 +195,110 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
}
}
private volatile long __forwarderId = 0;
private void removeRef() {
if (sockList != null) {
synchronized (slock) {
boolean removed = sockList.remove(i2ps);
//System.out.println("Removal of i2psocket " + i2ps + " successful? "
// + removed + " remaining: " + sockList.size());
}
}
}
private class StreamForwarder extends I2PThread {
InputStream in;
OutputStream out;
String direction;
private boolean _toI2P;
private ByteCache _cache;
private StreamForwarder(InputStream in, OutputStream out) {
private StreamForwarder(InputStream in, OutputStream out, boolean toI2P) {
this.in = in;
this.out = out;
_toI2P = toI2P;
direction = (toI2P ? "toI2P" : "fromI2P");
_cache = ByteCache.getInstance(32, NETWORK_BUFFER_SIZE);
setName("StreamForwarder " + _runnerId + "." + (++__forwarderId));
start();
}
public void run() {
byte[] buffer = new byte[NETWORK_BUFFER_SIZE];
String from = i2ps.getThisDestination().calculateHash().toBase64().substring(0,6);
String to = i2ps.getPeerDestination().calculateHash().toBase64().substring(0,6);
if (_log.shouldLog(Log.DEBUG)) {
_log.debug(direction + ": Forwarding between "
+ from + " and " + to);
}
ByteArray ba = _cache.acquire();
byte[] buffer = ba.getData(); // new byte[NETWORK_BUFFER_SIZE];
try {
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
if (_toI2P)
totalSent += len;
else
totalReceived += len;
if (len > 0) updateActivity();
if (in.available() == 0) {
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Flushing after sending " + len + " bytes through");
if (_log.shouldLog(Log.DEBUG))
_log.debug(direction + ": " + len + " bytes flushed through to "
+ i2ps.getPeerDestination().calculateHash().toBase64().substring(0,6));
try {
Thread.sleep(I2PTunnel.PACKET_DELAY);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (in.available() == 0) {
out.flush(); // make sure the data get though
if (in.available() <= 0)
out.flush(); // make sure the data get though
}
}
//out.flush(); // close() flushes
} catch (SocketException ex) {
// this *will* occur when the other threads closes the socket
synchronized (finishLock) {
if (!finished) {
_log.debug("Socket closed - error reading and writing",
ex);
if (_log.shouldLog(Log.DEBUG))
_log.debug(direction + ": Socket closed - error reading and writing",
ex);
}
}
} catch (InterruptedIOException ex) {
_log.warn("Closing connection due to timeout (error: \""
+ ex.getMessage() + "\")");
if (_log.shouldLog(Log.WARN))
_log.warn(direction + ": Closing connection due to timeout (error: \""
+ ex.getMessage() + "\")");
} catch (IOException ex) {
if (!finished)
_log.error("Error forwarding", ex);
else
_log.warn("You may ignore this", ex);
if (!finished) {
if (_log.shouldLog(Log.WARN))
_log.warn(direction + ": Error forwarding", ex);
}
//else
// _log.warn("You may ignore this", ex);
} finally {
_cache.release(ba);
if (_log.shouldLog(Log.INFO)) {
_log.info(direction + ": done forwarding between "
+ from + " and " + to);
}
try {
out.close();
in.close();
} catch (IOException ex) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error closing streams", ex);
_log.warn(direction + ": Error closing input stream", ex);
}
try {
out.flush();
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn(direction + ": Error flushing to close", ioe);
}
synchronized (finishLock) {
finished = true;

View File

@ -14,8 +14,8 @@ import java.net.SocketException;
import java.util.Iterator;
import java.util.Properties;
import net.i2p.I2PException;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.streaming.I2PServerSocket;
@ -31,29 +31,30 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
private final static Log _log = new Log(I2PTunnelServer.class);
private I2PSocketManager sockMgr;
private I2PServerSocket i2pss;
protected I2PSocketManager sockMgr;
protected I2PServerSocket i2pss;
private Object lock = new Object(), slock = new Object();
private Object lock = new Object();
protected Object slock = new Object();
private InetAddress remoteHost;
private int remotePort;
protected InetAddress remoteHost;
protected int remotePort;
private Logging l;
private static final long DEFAULT_READ_TIMEOUT = -1; // 3*60*1000;
/** default timeout to 3 minutes - override if desired */
private long readTimeout = DEFAULT_READ_TIMEOUT;
protected long readTimeout = DEFAULT_READ_TIMEOUT;
public I2PTunnelServer(InetAddress host, int port, String privData, Logging l, EventDispatcher notifyThis) {
super(host + ":" + port + " <- " + privData, notifyThis);
public I2PTunnelServer(InetAddress host, int port, String privData, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host + ":" + port + " <- " + privData, notifyThis, tunnel);
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(privData));
init(host, port, bais, privData, l);
}
public I2PTunnelServer(InetAddress host, int port, File privkey, String privkeyname, Logging l,
EventDispatcher notifyThis) {
super(host + ":" + port + " <- " + privkeyname, notifyThis);
EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host + ":" + port + " <- " + privkeyname, notifyThis, tunnel);
try {
init(host, port, new FileInputStream(privkey), privkeyname, l);
} catch (IOException ioe) {
@ -62,8 +63,8 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
}
}
public I2PTunnelServer(InetAddress host, int port, InputStream privData, String privkeyname, Logging l, EventDispatcher notifyThis) {
super(host + ":" + port + " <- " + privkeyname, notifyThis);
public I2PTunnelServer(InetAddress host, int port, InputStream privData, String privkeyname, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host + ":" + port + " <- " + privkeyname, notifyThis, tunnel);
init(host, port, privData, privkeyname, l);
}
@ -73,12 +74,20 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
this.remotePort = port;
I2PClient client = I2PClientFactory.createClient();
Properties props = new Properties();
props.putAll(System.getProperties());
synchronized (slock) {
sockMgr = I2PSocketManagerFactory.createManager(privData, I2PTunnel.host, Integer.parseInt(I2PTunnel.port),
props);
props.putAll(getTunnel().getClientOptions());
while (sockMgr == null) {
synchronized (slock) {
sockMgr = I2PSocketManagerFactory.createManager(privData, getTunnel().host, Integer.parseInt(getTunnel().port),
props);
}
if (sockMgr == null) {
_log.log(Log.CRIT, "Unable to create socket manager");
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
}
sockMgr.setName("Server");
getTunnel().addSession(sockMgr.getSession());
l.log("Ready!");
notifyEvent("openServerResult", "ok");
open = true;
@ -129,6 +138,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
l.log("Shutting down server " + toString());
try {
if (i2pss != null) i2pss.close();
getTunnel().removeSession(sockMgr.getSession());
sockMgr.getSession().destroySession();
} catch (I2PException ex) {
_log.error("Error destroying the session", ex);
@ -145,6 +155,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
I2PServerSocket i2pss = sockMgr.getServerSocket();
while (true) {
I2PSocket i2ps = i2pss.accept();
if (i2ps == null) throw new I2PException("I2PServerSocket closed");
I2PThread t = new I2PThread(new Handler(i2ps));
t.start();
}
@ -175,7 +186,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
_handleSocket.setReadTimeout(readTimeout);
Socket s = new Socket(remoteHost, remotePort);
afterSocket = I2PAppContext.getGlobalContext().clock().now();
new I2PTunnelRunner(s, _handleSocket, slock, null);
new I2PTunnelRunner(s, _handleSocket, slock, null, null);
} catch (SocketException ex) {
try {
_handleSocket.close();

View File

@ -26,16 +26,19 @@ public abstract class I2PTunnelTask implements EventDispatcher {
// I2PTunnelTask(name, (EventDispatcher)null);
//}
protected I2PTunnelTask(String name, EventDispatcher notifyThis) {
protected I2PTunnelTask(String name, EventDispatcher notifyThis, I2PTunnel tunnel) {
attachEventDispatcher(notifyThis);
this.name = name;
this.id = -1;
this.tunnel = tunnel;
}
/** for apps that use multiple I2PTunnel instances */
public void setTunnel(I2PTunnel pTunnel) {
tunnel = pTunnel;
}
public I2PTunnel getTunnel() { return tunnel; }
public int getId() {
return this.id;
@ -61,6 +64,7 @@ public abstract class I2PTunnelTask implements EventDispatcher {
public void disconnected(I2PSession session) {
routerDisconnected();
getTunnel().removeSession(session);
}
public void errorOccurred(I2PSession session, String message, Throwable error) {

View File

@ -47,15 +47,15 @@ public class I2Ping extends I2PTunnelTask implements Runnable {
// I2Ping(cmd, l, (EventDispatcher)null);
//}
public I2Ping(String cmd, Logging l, boolean ownDest, EventDispatcher notifyThis) {
super("I2Ping [" + cmd + "]", notifyThis);
public I2Ping(String cmd, Logging l, boolean ownDest, EventDispatcher notifyThis, I2PTunnel tunnel) {
super("I2Ping [" + cmd + "]", notifyThis, tunnel);
this.l = l;
command = cmd;
synchronized (slock) {
if (ownDest) {
sockMgr = I2PTunnelClient.buildSocketManager();
sockMgr = I2PTunnelClient.buildSocketManager(tunnel);
} else {
sockMgr = I2PTunnelClient.getSocketManager();
sockMgr = I2PTunnelClient.getSocketManager(tunnel);
}
}
Thread t = new I2PThread(this);

View File

@ -0,0 +1,460 @@
package net.i2p.i2ptunnel;
import java.io.IOException;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Random;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.I2PSession;
import net.i2p.data.Destination;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Coordinate the runtime operation and configuration of a tunnel.
* These objects are bundled together under a TunnelControllerGroup where the
* entire group is stored / loaded from a single config file.
*
*/
public class TunnelController implements Logging {
private Log _log;
private Properties _config;
private I2PTunnel _tunnel;
private List _messages;
private List _sessions;
private boolean _running;
private boolean _starting;
/**
* Create a new controller for a tunnel out of the specific config options.
* The config may contain a large number of options - only ones that begin in
* the prefix should be used (and, in turn, that prefix should be stripped off
* before being interpreted by this controller)
*
* @param config original key=value mapping
* @param prefix beginning of key values that are relevent to this tunnel
*/
public TunnelController(Properties config, String prefix) {
this(config, prefix, true);
}
/**
*
* @param createKey for servers, whether we want to create a brand new destination
* with private keys at the location specified or not (does not
* overwrite existing ones)
*/
public TunnelController(Properties config, String prefix, boolean createKey) {
_tunnel = new I2PTunnel();
_log = I2PAppContext.getGlobalContext().logManager().getLog(TunnelController.class);
setConfig(config, prefix);
_messages = new ArrayList(4);
_running = false;
if (createKey && ("server".equals(getType()) || "httpserver".equals(getType())) )
createPrivateKey();
_starting = getStartOnLoad();
}
private void createPrivateKey() {
I2PClient client = I2PClientFactory.createClient();
String filename = getPrivKeyFile();
if ( (filename == null) || (filename.trim().length() <= 0) ) {
log("No filename specified for the private key");
return;
}
File keyFile = new File(getPrivKeyFile());
if (keyFile.exists()) {
log("Not overwriting existing private keys in " + keyFile.getAbsolutePath());
return;
} else {
File parent = keyFile.getParentFile();
if ( (parent != null) && (!parent.exists()) )
parent.mkdirs();
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(keyFile);
Destination dest = client.createDestination(fos);
String destStr = dest.toBase64();
log("Private key created and saved in " + keyFile.getAbsolutePath());
log("New destination: " + destStr);
} catch (I2PException ie) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error creating new destination", ie);
log("Error creating new destination: " + ie.getMessage());
} catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error creating writing the destination to " + keyFile.getAbsolutePath(), ioe);
log("Error writing the keys to " + keyFile.getAbsolutePath());
} finally {
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
}
}
public void startTunnelBackground() {
if (_running) return;
_starting = true;
new I2PThread(new Runnable() { public void run() { startTunnel(); } }).start();
}
/**
* Start up the tunnel (if it isn't already running)
*
*/
public void startTunnel() {
_starting = true;
try {
doStartTunnel();
} catch (Exception e) {
_log.error("Error starting up the tunnel", e);
log("Error starting up the tunnel - " + e.getMessage());
}
_starting = false;
}
private void doStartTunnel() {
if (_running) {
if (_log.shouldLog(Log.INFO))
_log.info("Already running");
log("Tunnel " + getName() + " is already running");
return;
}
String type = getType();
if ( (type == null) || (type.length() <= 0) ) {
if (_log.shouldLog(Log.WARN))
_log.warn("Cannot start the tunnel - no type specified");
return;
}
if ("httpclient".equals(type)) {
startHttpClient();
} else if ("client".equals(type)) {
startClient();
} else if ("server".equals(type)) {
startServer();
} else if ("httpserver".equals(type)) {
startHttpServer();
} else {
if (_log.shouldLog(Log.ERROR))
_log.error("Cannot start tunnel - unknown type [" + type + "]");
}
}
private void startHttpClient() {
setI2CPOptions();
setSessionOptions();
setListenOn();
String listenPort = getListenPort();
String proxyList = getProxyList();
if (proxyList == null)
_tunnel.runHttpClient(new String[] { listenPort }, this);
else
_tunnel.runHttpClient(new String[] { listenPort, proxyList }, this);
acquire();
_running = true;
}
/**
* Note the fact that we are using some sessions, so that they dont get
* closed by some other tunnels
*/
private void acquire() {
List sessions = _tunnel.getSessions();
if (sessions != null) {
for (int i = 0; i < sessions.size(); i++) {
I2PSession session = (I2PSession)sessions.get(i);
TunnelControllerGroup.getInstance().acquire(this, session);
}
_sessions = sessions;
} else {
_log.error("No sessions to acquire?");
}
}
/**
* Note the fact that we are no longer using some sessions, and if
* no other tunnels are using them, close them.
*/
private void release() {
if (_sessions != null) {
for (int i = 0; i < _sessions.size(); i++) {
I2PSession s = (I2PSession)_sessions.get(i);
TunnelControllerGroup.getInstance().release(this, s);
}
} else {
_log.error("No sessions to release?");
}
}
private void startClient() {
setI2CPOptions();
setSessionOptions();
setListenOn();
String listenPort = getListenPort();
String dest = getTargetDestination();
_tunnel.runClient(new String[] { listenPort, dest }, this);
acquire();
_running = true;
}
private void startServer() {
setI2CPOptions();
setSessionOptions();
String targetHost = getTargetHost();
String targetPort = getTargetPort();
String privKeyFile = getPrivKeyFile();
_tunnel.runServer(new String[] { targetHost, targetPort, privKeyFile }, this);
acquire();
_running = true;
}
private void startHttpServer() {
setI2CPOptions();
setSessionOptions();
String targetHost = getTargetHost();
String targetPort = getTargetPort();
String spoofedHost = getSpoofedHost();
String privKeyFile = getPrivKeyFile();
_tunnel.runHttpServer(new String[] { targetHost, targetPort, spoofedHost, privKeyFile }, this);
acquire();
_running = true;
}
private void setListenOn() {
String listenOn = getListenOnInterface();
if ( (listenOn != null) && (listenOn.length() > 0) ) {
_tunnel.runListenOn(new String[] { listenOn }, this);
}
}
private void setSessionOptions() {
List opts = new ArrayList();
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String val = _config.getProperty(key);
if (key.startsWith("option.")) {
key = key.substring("option.".length());
opts.add(key + "=" + val);
}
}
String args[] = new String[opts.size()];
for (int i = 0; i < opts.size(); i++)
args[i] = (String)opts.get(i);
_tunnel.runClientOptions(args, this);
}
private void setI2CPOptions() {
String host = getI2CPHost();
if ( (host != null) && (host.length() > 0) )
_tunnel.host = host;
// woohah, special casing for people with ipv6/etc
if ("localhost".equals(_tunnel.host))
_tunnel.host = "127.0.0.1";
String port = getI2CPPort();
if ( (port != null) && (port.length() > 0) )
_tunnel.port = port;
}
public void stopTunnel() {
_tunnel.runClose(new String[] { "forced", "all" }, this);
release();
_running = false;
}
public void restartTunnel() {
stopTunnel();
startTunnel();
}
public void setConfig(Properties config, String prefix) {
Properties props = new Properties();
for (Iterator iter = config.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String val = config.getProperty(key);
if (key.startsWith(prefix)) {
key = key.substring(prefix.length());
props.setProperty(key, val);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Set prop [" + key + "] to [" + val + "]");
}
}
_config = props;
}
public Properties getConfig(String prefix) {
Properties rv = new Properties();
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String val = _config.getProperty(key);
rv.setProperty(prefix + key, val);
}
return rv;
}
public String getType() { return _config.getProperty("type"); }
public String getName() { return _config.getProperty("name"); }
public String getDescription() { return _config.getProperty("description"); }
public String getI2CPHost() { return _config.getProperty("i2cpHost"); }
public String getI2CPPort() { return _config.getProperty("i2cpPort"); }
public String getClientOptions() {
StringBuffer opts = new StringBuffer(64);
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String val = _config.getProperty(key);
if (key.startsWith("option.")) {
key = key.substring("option.".length());
if (opts.length() > 0) opts.append(' ');
opts.append(key).append('=').append(val);
}
}
return opts.toString();
}
public String getListenOnInterface() { return _config.getProperty("interface"); }
public String getTargetHost() { return _config.getProperty("targetHost"); }
public String getTargetPort() { return _config.getProperty("targetPort"); }
public String getSpoofedHost() { return _config.getProperty("spoofedHost"); }
public String getPrivKeyFile() { return _config.getProperty("privKeyFile"); }
public String getListenPort() { return _config.getProperty("listenPort"); }
public String getTargetDestination() { return _config.getProperty("targetDestination"); }
public String getProxyList() { return _config.getProperty("proxyList"); }
public boolean getStartOnLoad() { return "true".equalsIgnoreCase(_config.getProperty("startOnLoad", "true")); }
public boolean getIsRunning() { return _running; }
public boolean getIsStarting() { return _starting; }
public void getSummary(StringBuffer buf) {
String type = getType();
if ("httpclient".equals(type))
getHttpClientSummary(buf);
else if ("client".equals(type))
getClientSummary(buf);
else if ("server".equals(type))
getServerSummary(buf);
else if ("httpserver".equals(type))
getHttpServerSummary(buf);
else
buf.append("Unknown type ").append(type);
}
private void getHttpClientSummary(StringBuffer buf) {
String description = getDescription();
if ( (description != null) && (description.trim().length() > 0) )
buf.append("<i>").append(description).append("</i><br />\n");
buf.append("HTTP proxy listening on port ").append(getListenPort());
String listenOn = getListenOnInterface();
if ("0.0.0.0".equals(listenOn))
buf.append(" (reachable by any machine)");
else if ("127.0.0.1".equals(listenOn))
buf.append(" (reachable locally only)");
else
buf.append(" (reachable at the ").append(listenOn).append(" interface)");
buf.append("<br />\n");
String proxies = getProxyList();
if ( (proxies == null) || (proxies.trim().length() <= 0) )
buf.append("Outproxy: default [squid.i2p]<br />\n");
else
buf.append("Outproxy: ").append(proxies).append("<br />\n");
getOptionSummary(buf);
}
private void getClientSummary(StringBuffer buf) {
String description = getDescription();
if ( (description != null) && (description.trim().length() > 0) )
buf.append("<i>").append(description).append("</i><br />\n");
buf.append("Client tunnel listening on port ").append(getListenPort());
buf.append(" pointing at ").append(getTargetDestination());
String listenOn = getListenOnInterface();
if ("0.0.0.0".equals(listenOn))
buf.append(" (reachable by any machine)");
else if ("127.0.0.1".equals(listenOn))
buf.append(" (reachable locally only)");
else
buf.append(" (reachable at the ").append(listenOn).append(" interface)");
buf.append("<br />\n");
getOptionSummary(buf);
}
private void getServerSummary(StringBuffer buf) {
String description = getDescription();
if ( (description != null) && (description.trim().length() > 0) )
buf.append("<i>").append(description).append("</i><br />\n");
buf.append("Server tunnel pointing at port ").append(getTargetPort());
buf.append(" on ").append(getTargetHost());
buf.append("<br />\n");
buf.append("Private destination loaded from ").append(getPrivKeyFile()).append("<br />\n");
getOptionSummary(buf);
}
private void getHttpServerSummary(StringBuffer buf) {
String description = getDescription();
if ( (description != null) && (description.trim().length() > 0) )
buf.append("<i>").append(description).append("</i><br />\n");
buf.append("Server tunnel pointing at port ").append(getTargetPort());
buf.append(" on ").append(getTargetHost());
buf.append(" for the site ").append(getSpoofedHost());
buf.append("<br />\n");
buf.append("Private destination loaded from ").append(getPrivKeyFile()).append("<br />\n");
getOptionSummary(buf);
}
private void getOptionSummary(StringBuffer buf) {
String opts = getClientOptions();
if ( (opts != null) && (opts.length() > 0) )
buf.append("Network options: ").append(opts).append("<br />\n");
if (_running) {
List sessions = _tunnel.getSessions();
for (int i = 0; i < sessions.size(); i++) {
I2PSession session = (I2PSession)sessions.get(i);
Destination dest = session.getMyDestination();
if (dest != null) {
buf.append("Destination hash: ").append(dest.calculateHash().toBase64()).append("<br />\n");
if ( ("server".equals(getType())) || ("httpserver".equals(getType())) ) {
buf.append("Full destination: ");
buf.append("<input type=\"text\" size=\"10\" onclick=\"this.select();\" ");
buf.append("value=\"").append(dest.toBase64()).append("\" />\n");
long val = new Random().nextLong();
if (val < 0) val = 0 - val;
buf.append("<br />You can <a href=\"http://temp").append(val);
buf.append(".i2p/?i2paddresshelper=").append(dest.toBase64()).append("\">view</a>");
buf.append(" it in a browser (only when you're using the eepProxy)\n");
buf.append("<br />If you are going to share this on IRC, you need to split it up:<br />\n");
String str = dest.toBase64();
buf.append(str.substring(0, str.length()/2)).append("<br />\n");
buf.append(str.substring(str.length()/2)).append("<br />\n");
buf.append("You can also post it to <a href=\"http://forum.i2p/viewforum.php?f=16\">Eepsite announcement forum</a><br />");
}
}
}
}
}
public void log(String s) {
synchronized (this) {
_messages.add(s);
while (_messages.size() > 10)
_messages.remove(0);
}
if (_log.shouldLog(Log.INFO))
_log.info(s);
}
/**
* Pull off any messages that the I2PTunnel has produced
*
* @return list of messages pulled off (each is a String, earliest first)
*/
public List clearMessages() {
List rv = null;
synchronized (this) {
rv = new ArrayList(_messages);
_messages.clear();
}
return rv;
}
}

View File

@ -0,0 +1,354 @@
package net.i2p.i2ptunnel;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.data.DataHelper;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Coordinate a set of tunnels within the JVM, loading and storing their config
* to disk, and building new ones as requested.
*
*/
public class TunnelControllerGroup {
private Log _log;
private static TunnelControllerGroup _instance;
static final String DEFAULT_CONFIG_FILE = "i2ptunnel.config";
private List _controllers;
private String _configFile = DEFAULT_CONFIG_FILE;
/**
* Map of I2PSession to a Set of TunnelController objects
* using the session (to prevent closing the session until
* no more tunnels are using it)
*
*/
private Map _sessions;
public static TunnelControllerGroup getInstance() {
synchronized (TunnelControllerGroup.class) {
if (_instance == null)
_instance = new TunnelControllerGroup(DEFAULT_CONFIG_FILE);
return _instance;
}
}
private TunnelControllerGroup(String configFile) {
_log = I2PAppContext.getGlobalContext().logManager().getLog(TunnelControllerGroup.class);
_controllers = Collections.synchronizedList(new ArrayList());
_configFile = configFile;
_sessions = new HashMap(4);
loadControllers(_configFile);
}
public static void main(String args[]) {
synchronized (TunnelControllerGroup.class) {
if (_instance != null) return; // already loaded through the web
if ( (args == null) || (args.length <= 0) ) {
_instance = new TunnelControllerGroup(DEFAULT_CONFIG_FILE);
} else if (args.length == 1) {
_instance = new TunnelControllerGroup(args[0]);
} else {
System.err.println("Usage: TunnelControllerGroup [filename]");
return;
}
}
}
/**
* Load up all of the tunnels configured in the given file (but do not start
* them)
*
*/
public void loadControllers(String configFile) {
Properties cfg = loadConfig(configFile);
if (cfg == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to load the config from " + configFile);
return;
}
int i = 0;
while (true) {
String type = cfg.getProperty("tunnel." + i + ".type");
if (type == null)
break;
TunnelController controller = new TunnelController(cfg, "tunnel." + i + ".");
_controllers.add(controller);
i++;
}
I2PThread startupThread = new I2PThread(new StartControllers(), "Startup tunnels");
startupThread.start();
if (_log.shouldLog(Log.INFO))
_log.info(i + " controllers loaded from " + configFile);
}
private class StartControllers implements Runnable {
public void run() {
for (int i = 0; i < _controllers.size(); i++) {
TunnelController controller = (TunnelController)_controllers.get(i);
if (controller.getStartOnLoad())
controller.startTunnel();
}
}
}
public void reloadControllers() {
unloadControllers();
loadControllers(_configFile);
}
/**
* Stop and remove reference to all known tunnels (but dont delete any config
* file or do other silly things)
*
*/
public void unloadControllers() {
stopAllControllers();
_controllers.clear();
if (_log.shouldLog(Log.INFO))
_log.info("All controllers stopped and unloaded");
}
/**
* Add the given tunnel to the set of known controllers (but dont add it to
* a config file or start it or anything)
*
*/
public void addController(TunnelController controller) { _controllers.add(controller); }
/**
* Stop and remove the given tunnel
*
* @return list of messages from the controller as it is stopped
*/
public List removeController(TunnelController controller) {
if (controller == null) return new ArrayList();
controller.stopTunnel();
List msgs = controller.clearMessages();
_controllers.remove(controller);
msgs.add("Tunnel " + controller.getName() + " removed");
return msgs;
}
/**
* Stop all tunnels
*
* @return list of messages the tunnels generate when stopped
*/
public List stopAllControllers() {
List msgs = new ArrayList();
for (int i = 0; i < _controllers.size(); i++) {
TunnelController controller = (TunnelController)_controllers.get(i);
controller.stopTunnel();
msgs.addAll(controller.clearMessages());
}
if (_log.shouldLog(Log.INFO))
_log.info(_controllers.size() + " controllers stopped");
return msgs;
}
/**
* Start all tunnels
*
* @return list of messages the tunnels generate when started
*/
public List startAllControllers() {
List msgs = new ArrayList();
for (int i = 0; i < _controllers.size(); i++) {
TunnelController controller = (TunnelController)_controllers.get(i);
controller.startTunnelBackground();
msgs.addAll(controller.clearMessages());
}
if (_log.shouldLog(Log.INFO))
_log.info(_controllers.size() + " controllers started");
return msgs;
}
/**
* Restart all tunnels
*
* @return list of messages the tunnels generate when restarted
*/
public List restartAllControllers() {
List msgs = new ArrayList();
for (int i = 0; i < _controllers.size(); i++) {
TunnelController controller = (TunnelController)_controllers.get(i);
controller.restartTunnel();
msgs.addAll(controller.clearMessages());
}
if (_log.shouldLog(Log.INFO))
_log.info(_controllers.size() + " controllers restarted");
return msgs;
}
/**
* Fetch all outstanding messages from any of the known tunnels
*
* @return list of messages the tunnels have generated
*/
public List clearAllMessages() {
List msgs = new ArrayList();
for (int i = 0; i < _controllers.size(); i++) {
TunnelController controller = (TunnelController)_controllers.get(i);
msgs.addAll(controller.clearMessages());
}
return msgs;
}
/**
* Save the configuration of all known tunnels to the default config
* file
*
*/
public void saveConfig() {
saveConfig(_configFile);
}
/**
* Save the configuration of all known tunnels to the given file
*
*/
public void saveConfig(String configFile) {
_configFile = configFile;
File cfgFile = new File(configFile);
File parent = cfgFile.getParentFile();
if ( (parent != null) && (!parent.exists()) )
parent.mkdirs();
TreeMap map = new TreeMap();
for (int i = 0; i < _controllers.size(); i++) {
TunnelController controller = (TunnelController)_controllers.get(i);
Properties cur = controller.getConfig("tunnel." + i + ".");
map.putAll(cur);
}
StringBuffer buf = new StringBuffer(1024);
for (Iterator iter = map.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String val = (String)map.get(key);
buf.append(key).append('=').append(val).append('\n');
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(cfgFile);
fos.write(buf.toString().getBytes());
if (_log.shouldLog(Log.INFO))
_log.info("Config written to " + cfgFile.getPath());
} catch (IOException ioe) {
_log.error("Error writing out the config");
} finally {
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
}
}
/**
* Load up the config data from the file
*
* @return properties loaded or null if there was an error
*/
private Properties loadConfig(String configFile) {
File cfgFile = new File(configFile);
if (!cfgFile.exists()) {
if (_log.shouldLog(Log.ERROR))
_log.error("Unable to load the controllers from " + configFile);
return null;
}
Properties props = new Properties();
try {
DataHelper.loadProps(props, cfgFile);
return props;
} catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error reading the controllers from " + configFile, ioe);
return null;
}
}
/**
* Retrieve a list of tunnels known
*
* @return list of TunnelController objects
*/
public List getControllers() { return _controllers; }
/**
* Note the fact that the controller is using the session so that
* it isn't destroyed prematurely.
*
*/
void acquire(TunnelController controller, I2PSession session) {
synchronized (_sessions) {
Set owners = (Set)_sessions.get(session);
if (owners == null) {
owners = new HashSet(1);
_sessions.put(session, owners);
}
owners.add(controller);
}
if (_log.shouldLog(Log.INFO))
_log.info("Acquiring session " + session + " for " + controller);
}
/**
* Note the fact that the controller is no longer using the session, and if
* no other controllers are using it, destroy the session.
*
*/
void release(TunnelController controller, I2PSession session) {
boolean shouldClose = false;
synchronized (_sessions) {
Set owners = (Set)_sessions.get(session);
if (owners != null) {
owners.remove(controller);
if (owners.size() <= 0) {
if (_log.shouldLog(Log.INFO))
_log.info("After releasing session " + session + " by " + controller + ", no more owners remain");
shouldClose = true;
_sessions.remove(session);
} else {
if (_log.shouldLog(Log.INFO))
_log.info("After releasing session " + session + " by " + controller + ", " + owners.size() + " owners remain");
shouldClose = false;
}
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("After releasing session " + session + " by " + controller + ", no owners were even known?!");
shouldClose = true;
}
}
if (shouldClose) {
try {
session.destroySession();
if (_log.shouldLog(Log.INFO))
_log.info("Session destroyed: " + session);
} catch (I2PSessionException ise) {
_log.error("Error closing the client session", ise);
}
}
}
}

View File

@ -0,0 +1,421 @@
package net.i2p.i2ptunnel;
import java.util.Iterator;
import java.util.Properties;
import java.util.Random;
import java.util.StringTokenizer;
/**
* Uuuugly code to generate the edit/add forms for the various
* I2PTunnel types (httpclient/client/server)
*
*/
class WebEditPageFormGenerator {
private static final String SELECT_TYPE_FORM =
"<form action=\"edit.jsp\"> Type of tunnel: <select name=\"type\">" +
"<option value=\"httpclient\">HTTP proxy</option>" +
"<option value=\"client\">Client tunnel</option>" +
"<option value=\"server\">Server tunnel</option>" +
"<option value=\"httpserver\">HTTP server tunnel</option>" +
"</select> <input type=\"submit\" value=\"GO\" />" +
"</form>\n";
/**
* Retrieve the form requested
*
*/
public static String getForm(WebEditPageHelper helper) {
TunnelController controller = helper.getTunnelController();
if ( (helper.getType() == null) && (controller == null) )
return SELECT_TYPE_FORM;
String id = helper.getNum();
String type = helper.getType();
if (controller != null)
type = controller.getType();
if ("httpclient".equals(type))
return getEditHttpClientForm(controller, id);
else if ("client".equals(type))
return getEditClientForm(controller, id);
else if ("server".equals(type))
return getEditServerForm(controller, id);
else if ("httpserver".equals(type))
return getEditHttpServerForm(controller, id);
else
return "WTF, unknown type [" + type + "]";
}
private static String getEditHttpClientForm(TunnelController controller, String id) {
StringBuffer buf = new StringBuffer(1024);
addGeneral(buf, controller, id);
buf.append("<b>Type:</b> <i>HTTP proxy</i><input type=\"hidden\" name=\"type\" value=\"httpclient\" /><br />\n");
addListeningOn(buf, controller, 4444);
buf.append("<b>Outproxies:</b> <input type=\"text\" name=\"proxyList\" size=\"20\" ");
if ( (controller != null) && (controller.getProxyList() != null) )
buf.append("value=\"").append(controller.getProxyList()).append("\" ");
else
buf.append("value=\"squid.i2p\" ");
buf.append("/><br />\n");
addStreamingOptions(buf, controller);
buf.append("<hr />Note: the following options are shared across all client tunnels and");
buf.append(" HTTP proxies<br />\n");
addOptions(buf, controller);
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
buf.append(" <i>confirm removal:</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
buf.append("</form>\n");
return buf.toString();
}
private static String getEditClientForm(TunnelController controller, String id) {
StringBuffer buf = new StringBuffer(1024);
addGeneral(buf, controller, id);
buf.append("<b>Type:</b> <i>Client tunnel</i><input type=\"hidden\" name=\"type\" value=\"client\" /><br />\n");
addListeningOn(buf, controller, 2025 + new Random().nextInt(1000)); // 2025 since nextInt can be negative
buf.append("<b>Target:</b> <input type=\"text\" size=\"40\" name=\"targetDestination\" ");
if ( (controller != null) && (controller.getTargetDestination() != null) )
buf.append("value=\"").append(controller.getTargetDestination()).append("\" ");
buf.append(" /> (either the hosts.txt name or the full base64 destination)<br />\n");
addStreamingOptions(buf, controller);
buf.append("<hr />Note: the following options are shared across all client tunnels and");
buf.append(" HTTP proxies<br />\n");
addOptions(buf, controller);
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\"><br />\n");
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
buf.append(" <i>confirm removal:</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
buf.append("</form>\n");
return buf.toString();
}
private static String getEditServerForm(TunnelController controller, String id) {
StringBuffer buf = new StringBuffer(1024);
addGeneral(buf, controller, id);
buf.append("<b>Type:</b> <i>Server tunnel</i><input type=\"hidden\" name=\"type\" value=\"server\" /><br />\n");
buf.append("<b>Target host:</b> <input type=\"text\" size=\"40\" name=\"targetHost\" ");
if ( (controller != null) && (controller.getTargetHost() != null) )
buf.append("value=\"").append(controller.getTargetHost()).append("\" ");
else
buf.append("value=\"127.0.0.1\" ");
buf.append(" /><br />\n");
buf.append("<b>Target port:</b> <input type=\"text\" size=\"4\" name=\"targetPort\" ");
if ( (controller != null) && (controller.getTargetPort() != null) )
buf.append("value=\"").append(controller.getTargetPort()).append("\" ");
else
buf.append("value=\"80\" ");
buf.append(" /><br />\n");
buf.append("<b>Private key file:</b> <input type=\"text\" name=\"privKeyFile\" value=\"");
if ( (controller != null) && (controller.getPrivKeyFile() != null) ) {
buf.append(controller.getPrivKeyFile()).append("\" /><br />");
} else {
buf.append("myServer.privKey\" /><br />");
buf.append("<input type=\"hidden\" name=\"privKeyGenerate\" value=\"true\" />");
}
addStreamingOptions(buf, controller);
addOptions(buf, controller);
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
buf.append(" <i>confirm removal:</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
buf.append("</form>\n");
return buf.toString();
}
private static String getEditHttpServerForm(TunnelController controller, String id) {
StringBuffer buf = new StringBuffer(1024);
addGeneral(buf, controller, id);
buf.append("<b>Type:</b> <i>HTTP server tunnel</i><input type=\"hidden\" name=\"type\" value=\"httpserver\" /><br />\n");
buf.append("<b>Target host:</b> <input type=\"text\" size=\"40\" name=\"targetHost\" ");
if ( (controller != null) && (controller.getTargetHost() != null) )
buf.append("value=\"").append(controller.getTargetHost()).append("\" ");
else
buf.append("value=\"127.0.0.1\" ");
buf.append(" /><br />\n");
buf.append("<b>Target port:</b> <input type=\"text\" size=\"4\" name=\"targetPort\" ");
if ( (controller != null) && (controller.getTargetPort() != null) )
buf.append("value=\"").append(controller.getTargetPort()).append("\" ");
else
buf.append("value=\"80\" ");
buf.append(" /><br />\n");
buf.append("<b>Website hostname:</b> <input type=\"text\" size=\"16\" name=\"spoofedHost\" ");
if ( (controller != null) && (controller.getSpoofedHost() != null) )
buf.append("value=\"").append(controller.getSpoofedHost()).append("\" ");
else
buf.append("value=\"mysite.i2p\" ");
buf.append(" /><br />\n");
buf.append("<b>Private key file:</b> <input type=\"text\" name=\"privKeyFile\" value=\"");
if ( (controller != null) && (controller.getPrivKeyFile() != null) ) {
buf.append(controller.getPrivKeyFile()).append("\" /><br />");
} else {
buf.append("myServer.privKey\" /><br />");
buf.append("<input type=\"hidden\" name=\"privKeyGenerate\" value=\"true\" />");
}
addStreamingOptions(buf, controller);
addOptions(buf, controller);
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
buf.append(" <i>confirm removal:</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
buf.append("</form>\n");
return buf.toString();
}
/**
* Start off the form and add some common fields (name, num, description)
*
* @param buf where to shove the form
* @param controller tunnel in question, or null if we're creating a new tunnel
* @param id index into the current list of tunnelControllerGroup.getControllers() list
* (or null if we are generating an 'add' form)
*/
private static void addGeneral(StringBuffer buf, TunnelController controller, String id) {
buf.append("<form action=\"edit.jsp\">");
if (id != null)
buf.append("<input type=\"hidden\" name=\"num\" value=\"").append(id).append("\" />");
long nonce = new Random().nextLong();
System.setProperty(WebEditPageHelper.class.getName() + ".nonce", nonce+"");
buf.append("<input type=\"hidden\" name=\"nonce\" value=\"").append(nonce).append("\" />");
buf.append("<b>Name:</b> <input type=\"text\" name=\"name\" size=\"20\" ");
if ( (controller != null) && (controller.getName() != null) )
buf.append("value=\"").append(controller.getName()).append("\" ");
buf.append("/><br />\n");
buf.append("<b>Description:</b> <input type=\"text\" name=\"description\" size=\"60\" ");
if ( (controller != null) && (controller.getDescription() != null) )
buf.append("value=\"").append(controller.getDescription()).append("\" ");
buf.append("/><br />\n");
buf.append("<b>Start automatically?</b> \n");
buf.append("<input type=\"checkbox\" name=\"startOnLoad\" value=\"true\" ");
if ( (controller != null) && (controller.getStartOnLoad()) )
buf.append(" checked=\"true\" />\n<br />\n");
else
buf.append(" />\n<br />\n");
}
/**
* Generate the fields asking for what port and interface the tunnel should
* listen on.
*
* @param buf where to shove the form
* @param controller tunnel in question, or null if we're creating a new tunnel
* @param defaultPort if we are creating a new tunnel, default the form to the given port
*/
private static void addListeningOn(StringBuffer buf, TunnelController controller, int defaultPort) {
buf.append("<b>Listening on port:</b> <input type=\"text\" name=\"port\" size=\"20\" ");
if ( (controller != null) && (controller.getListenPort() != null) )
buf.append("value=\"").append(controller.getListenPort()).append("\" ");
else
buf.append("value=\"").append(defaultPort).append("\" ");
buf.append("/><br />\n");
String selectedOn = null;
if ( (controller != null) && (controller.getListenOnInterface() != null) )
selectedOn = controller.getListenOnInterface();
buf.append("<b>Reachable by:</b> ");
buf.append("<select name=\"reachableBy\">");
buf.append("<option value=\"127.0.0.1\" ");
if ( (selectedOn != null) && ("127.0.0.1".equals(selectedOn)) )
buf.append("selected=\"true\" ");
buf.append(">Locally (127.0.0.1)</option>\n");
buf.append("<option value=\"0.0.0.0\" ");
if ( (selectedOn != null) && ("0.0.0.0".equals(selectedOn)) )
buf.append("selected=\"true\" ");
buf.append(">Everyone (0.0.0.0)</option>\n");
buf.append("</select> ");
buf.append("Other: <input type=\"text\" name=\"reachableByOther\" value=\"");
if ( (selectedOn != null) && (!"127.0.0.1".equals(selectedOn)) && (!"0.0.0.0".equals(selectedOn)) )
buf.append(selectedOn);
buf.append("\"><br />\n");
}
private static void addStreamingOptions(StringBuffer buf, TunnelController controller) {
int connectDelay = 0;
int maxWindowSize = -1;
Properties opts = getOptions(controller);
if (opts != null) {
String delay = opts.getProperty("i2p.streaming.connectDelay");
if (delay != null) {
try {
connectDelay = Integer.parseInt(delay);
} catch (NumberFormatException nfe) {
connectDelay = 0;
}
}
String max = opts.getProperty("i2p.streaming.maxWindowSize");
if (max != null) {
try {
maxWindowSize = Integer.parseInt(max);
} catch (NumberFormatException nfe) {
maxWindowSize = -1;
}
}
}
buf.append("<b>Delay connection briefly? </b> ");
buf.append("<input type=\"checkbox\" name=\"connectDelay\" value=\"");
buf.append((connectDelay > 0 ? connectDelay : 1000)).append("\" ");
if (connectDelay > 0)
buf.append("checked=\"true\" ");
buf.append("/> (useful for brief request/response connections)<br />\n");
buf.append("<b>Communication profile:</b>");
buf.append("<select name=\"profile\">");
if (maxWindowSize <= 0)
buf.append("<option value=\"interactive\">Interactive</option><option value=\"bulk\" selected=\"true\">Bulk</option>");
else
buf.append("<option value=\"interactive\" selected=\"true\">Interactive</option><option value=\"bulk\">Bulk</option>");
buf.append("</select><br />\n");
}
/**
* Add fields for customizing the I2PSession options, including helpers for
* tunnel depth and count, as well as I2CP host and port.
*
* @param buf where to shove the form
* @param controller tunnel in question, or null if we're creating a new tunnel
*/
private static void addOptions(StringBuffer buf, TunnelController controller) {
int tunnelDepth = 2;
int numTunnels = 2;
Properties opts = getOptions(controller);
if (opts != null) {
String depth = opts.getProperty("inbound.length");
if (depth != null) {
try {
tunnelDepth = Integer.parseInt(depth);
} catch (NumberFormatException nfe) {
tunnelDepth = 2;
}
}
String num = opts.getProperty("inbound.quantity");
if (num != null) {
try {
numTunnels = Integer.parseInt(num);
} catch (NumberFormatException nfe) {
numTunnels = 2;
}
}
}
buf.append("<b>Tunnel depth:</b> ");
buf.append("<select name=\"tunnelDepth\">");
buf.append("<option value=\"0\" ");
if (tunnelDepth == 0) buf.append(" selected=\"true\" ");
buf.append(">0 hop tunnel (low anonymity, low latency)</option>");
buf.append("<option value=\"1\" ");
if (tunnelDepth == 1) buf.append(" selected=\"true\" ");
buf.append(">1 hop tunnel (medium anonymity, medium latency)</option>");
buf.append("<option value=\"2\" ");
if (tunnelDepth == 2) buf.append(" selected=\"true\" ");
buf.append(">2 hop tunnel (high anonymity, high latency)</option>");
if (tunnelDepth > 2) {
buf.append("<option value=\"").append(tunnelDepth).append("\" selected=\"true\" >");
buf.append(tunnelDepth);
buf.append(" hop tunnel (custom)</option>");
}
buf.append("</select><br />\n");
buf.append("<b>Tunnel count:</b> ");
buf.append("<select name=\"tunnelCount\">");
buf.append("<option value=\"1\" ");
if (numTunnels == 1) buf.append(" selected=\"true\" ");
buf.append(">1 inbound tunnel (low bandwidth usage, less reliability)</option>");
buf.append("<option value=\"2\" ");
if (numTunnels == 2) buf.append(" selected=\"true\" ");
buf.append(">2 inbound tunnels (standard bandwidth usage, standard reliability)</option>");
buf.append("<option value=\"3\" ");
if (numTunnels == 3) buf.append(" selected=\"true\" ");
buf.append(">3 inbound tunnels (higher bandwidth usage, higher reliability)</option>");
if (numTunnels > 3) {
buf.append("<option value=\"").append(numTunnels).append("\" selected=\"true\" >");
buf.append(numTunnels);
buf.append(" inbound tunnels (custom)</option>");
}
buf.append("</select><br />\n");
buf.append("<b>I2CP host:</b> ");
buf.append("<input type=\"text\" name=\"clientHost\" size=\"20\" value=\"");
if ( (controller != null) && (controller.getI2CPHost() != null) )
buf.append(controller.getI2CPHost());
else
buf.append("127.0.0.1");
buf.append("\" /><br />\n");
buf.append("<b>I2CP port:</b> ");
buf.append("<input type=\"text\" name=\"clientPort\" size=\"20\" value=\"");
if ( (controller != null) && (controller.getI2CPPort() != null) )
buf.append(controller.getI2CPPort());
else
buf.append("7654");
buf.append("\" /><br />\n");
buf.append("<b>Other custom options:</b> \n");
buf.append("<input type=\"text\" name=\"customOptions\" size=\"60\" value=\"");
if (opts != null) {
int i = 0;
for (Iterator iter = opts.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String val = opts.getProperty(key);
if ("inbound.length".equals(key)) continue;
if ("outbound.length".equals(key)) continue;
if ("inbound.quantity".equals(key)) continue;
if ("outbound.quantity".equals(key)) continue;
if ("inbound.nickname".equals(key)) continue;
if ("outbound.nickname".equals(key)) continue;
if ("i2p.streaming.connectDelay".equals(key)) continue;
if ("i2p.streaming.maxWindowSize".equals(key)) continue;
if (i != 0) buf.append(' ');
buf.append(key).append('=').append(val);
i++;
}
}
buf.append("\" /><br />\n");
}
/**
* Retrieve the client options from the tunnel
*
* @return map of name=val to be used as I2P session options
*/
private static Properties getOptions(TunnelController controller) {
if (controller == null) return null;
String opts = controller.getClientOptions();
StringTokenizer tok = new StringTokenizer(opts);
Properties props = new Properties();
while (tok.hasMoreTokens()) {
String pair = tok.nextToken();
int eq = pair.indexOf('=');
if ( (eq <= 0) || (eq >= pair.length()) )
continue;
String key = pair.substring(0, eq);
String val = pair.substring(eq+1);
props.setProperty(key, val);
}
return props;
}
}

View File

@ -0,0 +1,453 @@
package net.i2p.i2ptunnel;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
/**
* UUUUuuuuuugly glue code to handle bean interaction from the web, process
* that data, and spit out the results (or the form requested). The basic
* usage is to set any of the fields with data then query the bean via
* getActionResults() which triggers the request processing (taking all the
* provided data, doing what needs to be done) and returns the results of those
* activites. Then a subsequent call to getEditForm() generates the HTML form
* to either edit the currently selected tunnel (if specified) or add a new one.
* This functionality is delegated to the WebEditPageFormGenerator.
*
*/
public class WebEditPageHelper {
private Log _log;
private String _action;
private String _type;
private String _id;
private String _name;
private String _description;
private String _i2cpHost;
private String _i2cpPort;
private String _tunnelDepth;
private String _tunnelCount;
private boolean _connectDelay;
private String _customOptions;
private String _proxyList;
private String _port;
private String _reachableBy;
private String _reachableByOther;
private String _targetDestination;
private String _targetHost;
private String _targetPort;
private String _spoofedHost;
private String _privKeyFile;
private String _profile;
private boolean _startOnLoad;
private boolean _privKeyGenerate;
private boolean _removeConfirmed;
private long _nonce;
public WebEditPageHelper() {
_action = null;
_type = null;
_id = null;
_removeConfirmed = false;
_log = I2PAppContext.getGlobalContext().logManager().getLog(WebEditPageHelper.class);
}
public void setNonce(String nonce) {
if (nonce != null) {
try {
_nonce = Long.parseLong(nonce);
} catch (NumberFormatException nfe) {}
}
}
/**
* Used for form submit - either "Save" or Remove"
*/
public void setAction(String action) {
_action = (action != null ? action.trim() : null);
}
/**
* What type of tunnel (httpclient, client, or server). This is
* required when adding a new tunnel.
*
*/
public void setType(String type) {
_type = (type != null ? type.trim() : null);
}
/**
* Which particular tunnel should be edited (index into the current
* TunnelControllerGroup's getControllers() list). This is required
* when editing a tunnel, but not when adding a new one.
*
*/
public void setNum(String id) {
_id = (id != null ? id.trim() : null);
}
String getType() { return _type; }
String getNum() { return _id; }
/** Short name of the tunnel */
public void setName(String name) {
_name = (name != null ? name.trim() : null);
}
/** one line description */
public void setDescription(String description) {
_description = (description != null ? description.trim() : null);
}
/** I2CP host the router is on */
public void setClientHost(String host) {
_i2cpHost = (host != null ? host.trim() : null);
}
/** I2CP port the router is on */
public void setClientPort(String port) {
_i2cpPort = (port != null ? port.trim() : null);
}
/** how many hops to use for inbound tunnels */
public void setTunnelDepth(String tunnelDepth) {
_tunnelDepth = (tunnelDepth != null ? tunnelDepth.trim() : null);
}
/** how many parallel inbound tunnels to use */
public void setTunnelCount(String tunnelCount) {
_tunnelCount = (tunnelCount != null ? tunnelCount.trim() : null);
}
/** what I2P session overrides should be used */
public void setCustomOptions(String customOptions) {
_customOptions = (customOptions != null ? customOptions.trim() : null);
}
/** what HTTP outproxies should be used (httpclient specific) */
public void setProxyList(String proxyList) {
_proxyList = (proxyList != null ? proxyList.trim() : null);
}
/** what port should this client/httpclient listen on */
public void setPort(String port) {
_port = (port != null ? port.trim() : null);
}
/**
* what interface should this client/httpclient listen on (unless
* overridden by the setReachableByOther() field)
*/
public void setReachableBy(String reachableBy) {
_reachableBy = (reachableBy != null ? reachableBy.trim() : null);
}
/**
* If specified, defines the exact IP interface to listen for requests
* on (in the case of client/httpclient tunnels)
*/
public void setReachableByOther(String reachableByOther) {
_reachableByOther = (reachableByOther != null ? reachableByOther.trim() : null);
}
/** What peer does this client tunnel point at */
public void setTargetDestination(String dest) {
_targetDestination = (dest != null ? dest.trim() : null);
}
/** What host does this server tunnel point at */
public void setTargetHost(String host) {
_targetHost = (host != null ? host.trim() : null);
}
/** What port does this server tunnel point at */
public void setTargetPort(String port) {
_targetPort = (port != null ? port.trim() : null);
}
/** What host does this http server tunnel spoof */
public void setSpoofedHost(String host) {
_spoofedHost = (host != null ? host.trim() : null);
}
/** What filename is this server tunnel's private keys stored in */
public void setPrivKeyFile(String file) {
_privKeyFile = (file != null ? file.trim() : null);
}
/**
* If called with any value, we want to generate a new destination
* for this server tunnel. This won't cause any existing private keys
* to be overwritten, however.
*/
public void setPrivKeyGenerate(String moo) {
_privKeyGenerate = true;
}
/**
* If called with any value (and the form submitted with action=Remove),
* we really do want to stop and remove the tunnel.
*/
public void setRemoveConfirm(String moo) {
_removeConfirmed = true;
}
/**
* If called with any value, we want this tunnel to start whenever it is
* loaded (aka right now and whenever the router is started up)
*/
public void setStartOnLoad(String moo) {
_startOnLoad = true;
}
public void setConnectDelay(String moo) {
_connectDelay = true;
}
public void setProfile(String profile) {
_profile = profile;
}
/**
* Process the form and display any resulting messages
*
*/
public String getActionResults() {
try {
return processAction();
} catch (Throwable t) {
_log.log(Log.CRIT, "Internal error processing request", t);
return "Internal error - " + t.getMessage();
}
}
/**
* Generate an HTML form to edit / create a tunnel according to the
* specified fields
*/
public String getEditForm() {
try {
return WebEditPageFormGenerator.getForm(this);
} catch (Throwable t) {
_log.log(Log.CRIT, "Internal error retrieving edit form", t);
return "Internal error - " + t.getMessage();
}
}
/**
* Retrieve the tunnel pointed to by the current id
*
*/
TunnelController getTunnelController() {
if (_id == null) return null;
int id = -1;
try {
id = Integer.parseInt(_id);
List controllers = TunnelControllerGroup.getInstance().getControllers();
if ( (id < 0) || (id >= controllers.size()) )
return null;
else
return (TunnelController)controllers.get(id);
} catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Invalid tunnel id [" + _id + "]", nfe);
return null;
}
}
private String processAction() {
if ( (_action == null) || (_action.trim().length() <= 0) )
return "";
String expected = System.getProperty(getClass().getName() + ".nonce");
if ( (expected == null) || (!expected.equals(Long.toString(_nonce))) )
return "<b>Invalid nonce, are you being spoofed?</b>";
if ("Save".equals(_action))
return save();
else if ("Remove".equals(_action))
return remove();
else
return "Action <i>" + _action + "</i> unknown";
}
private String remove() {
if (!_removeConfirmed)
return "Please confirm removal";
TunnelController cur = getTunnelController();
if (cur == null)
return "Invalid tunnel number";
List msgs = TunnelControllerGroup.getInstance().removeController(cur);
msgs.addAll(doSave());
return getMessages(msgs);
}
private String save() {
if (_type == null)
return "<b>Invalid form submission (no type?)</b>";
Properties config = getConfig();
if (config == null)
return "<b>Invalid params</b>";
TunnelController cur = getTunnelController();
if (cur == null) {
// creating new
cur = new TunnelController(config, "", _privKeyGenerate);
TunnelControllerGroup.getInstance().addController(cur);
if (cur.getStartOnLoad())
cur.startTunnelBackground();
} else {
cur.setConfig(config, "");
}
if ("httpclient".equals(cur.getType()) || "client".equals(cur.getType())) {
// all clients use the same I2CP session, and as such, use the same
// I2CP options
List controllers = TunnelControllerGroup.getInstance().getControllers();
for (int i = 0; i < controllers.size(); i++) {
TunnelController c = (TunnelController)controllers.get(i);
if (c == cur) continue;
if ("httpclient".equals(c.getType()) || "client".equals(c.getType())) {
Properties cOpt = c.getConfig("");
if (_tunnelCount != null) {
cOpt.setProperty("option.inbound.quantity", _tunnelCount);
cOpt.setProperty("option.outbound.quantity", _tunnelCount);
}
if (_tunnelDepth != null) {
cOpt.setProperty("option.inbound.length", _tunnelDepth);
cOpt.setProperty("option.outbound.length", _tunnelDepth);
}
// these are per-proxy settings, not per-session settings, and
// as such don't need to be shared. the values are propogated
// to the current tunnel's settings via cur.setConfig above
/*
if (_connectDelay)
cOpt.setProperty("option.i2p.streaming.connectDelay", "1000");
else
cOpt.setProperty("option.i2p.streaming.connectDelay", "0");
if ("interactive".equals(_profile))
cOpt.setProperty("option.i2p.streaming.maxWindowSize", "1");
else
cOpt.remove("option.i2p.streaming.maxWindowSize");
*/
if (_name != null) {
cOpt.setProperty("option.inbound.nickname", _name);
cOpt.setProperty("option.outbound.nickname", _name);
}
c.setConfig(cOpt, "");
}
}
}
return getMessages(doSave());
}
private List doSave() {
TunnelControllerGroup.getInstance().saveConfig();
return TunnelControllerGroup.getInstance().clearAllMessages();
}
/**
* Based on all provided data, create a set of configuration parameters
* suitable for use in a TunnelController. This will replace (not add to)
* any existing parameters, so this should return a comprehensive mapping.
*
*/
private Properties getConfig() {
Properties config = new Properties();
updateConfigGeneric(config);
if ("httpclient".equals(_type)) {
if (_port != null)
config.setProperty("listenPort", _port);
if (_reachableByOther != null)
config.setProperty("interface", _reachableByOther);
else
config.setProperty("interface", _reachableBy);
if (_proxyList != null)
config.setProperty("proxyList", _proxyList);
} else if ("client".equals(_type)) {
if (_port != null)
config.setProperty("listenPort", _port);
if (_reachableByOther != null)
config.setProperty("interface", _reachableByOther);
else
config.setProperty("interface", _reachableBy);
if (_targetDestination != null)
config.setProperty("targetDestination", _targetDestination);
} else if ("server".equals(_type)) {
if (_targetHost != null)
config.setProperty("targetHost", _targetHost);
if (_targetPort != null)
config.setProperty("targetPort", _targetPort);
if (_privKeyFile != null)
config.setProperty("privKeyFile", _privKeyFile);
} else if ("httpserver".equals(_type)) {
if (_targetHost != null)
config.setProperty("targetHost", _targetHost);
if (_targetPort != null)
config.setProperty("targetPort", _targetPort);
if (_privKeyFile != null)
config.setProperty("privKeyFile", _privKeyFile);
if (_spoofedHost != null)
config.setProperty("spoofedHost", _spoofedHost);
} else {
return null;
}
return config;
}
private void updateConfigGeneric(Properties config) {
config.setProperty("type", _type);
if (_name != null)
config.setProperty("name", _name);
if (_description != null)
config.setProperty("description", _description);
if (_i2cpHost != null)
config.setProperty("i2cpHost", _i2cpHost);
if (_i2cpPort != null)
config.setProperty("i2cpPort", _i2cpPort);
if (_customOptions != null) {
StringTokenizer tok = new StringTokenizer(_customOptions);
while (tok.hasMoreTokens()) {
String pair = tok.nextToken();
int eq = pair.indexOf('=');
if ( (eq <= 0) || (eq >= pair.length()) )
continue;
String key = pair.substring(0, eq);
String val = pair.substring(eq+1);
if ("inbound.length".equals(key)) continue;
if ("outbound.length".equals(key)) continue;
if ("inbound.quantity".equals(key)) continue;
if ("outbound.quantity".equals(key)) continue;
if ("inbound.nickname".equals(key)) continue;
if ("outbound.nickname".equals(key)) continue;
if ("i2p.streaming.connectDelay".equals(key)) continue;
if ("i2p.streaming.maxWindowSize".equals(key)) continue;
config.setProperty("option." + key, val);
}
}
config.setProperty("startOnLoad", _startOnLoad + "");
if (_tunnelCount != null) {
config.setProperty("option.inbound.quantity", _tunnelCount);
config.setProperty("option.outbound.quantity", _tunnelCount);
}
if (_tunnelDepth != null) {
config.setProperty("option.inbound.length", _tunnelDepth);
config.setProperty("option.outbound.length", _tunnelDepth);
}
if (_connectDelay)
config.setProperty("option.i2p.streaming.connectDelay", "1000");
else
config.setProperty("option.i2p.streaming.connectDelay", "0");
if (_name != null) {
config.setProperty("option.inbound.nickname", _name);
config.setProperty("option.outbound.nickname", _name);
}
if ("interactive".equals(_profile))
config.setProperty("option.i2p.streaming.maxWindowSize", "1");
else
config.remove("option.i2p.streaming.maxWindowSize");
}
/**
* Pretty print the messages provided
*
*/
private String getMessages(List msgs) {
if (msgs == null) return "";
int num = msgs.size();
switch (num) {
case 0: return "";
case 1: return (String)msgs.get(0);
default:
StringBuffer buf = new StringBuffer(512);
buf.append("<ul>");
for (int i = 0; i < num; i++)
buf.append("<li>").append((String)msgs.get(i)).append("</li>\n");
buf.append("</ul>\n");
return buf.toString();
}
}
}

View File

@ -0,0 +1,213 @@
package net.i2p.i2ptunnel;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
/**
* Ugly hack to let the web interface access the list of known tunnels and
* control their operation. Any data submitted by setting properties are
* acted upon by calling getActionResults() (which returns any messages
* generated). In addition, the getSummaryList() generates the html for
* summarizing all of the tunnels known, including both their status and the
* links to edit, stop, or start them.
*
*/
public class WebStatusPageHelper {
private I2PAppContext _context;
private Log _log;
private String _action;
private int _controllerNum;
private long _nonce;
public WebStatusPageHelper() {
_context = I2PAppContext.getGlobalContext();
_action = null;
_controllerNum = -1;
_log = _context.logManager().getLog(WebStatusPageHelper.class);
}
public void setAction(String action) {
_action = action;
}
public void setNum(String num) {
if (num != null) {
try {
_controllerNum = Integer.parseInt(num);
} catch (NumberFormatException nfe) {
_controllerNum = -1;
}
}
}
public void setNonce(long nonce) { _nonce = nonce; }
public void setNonce(String nonce) {
if (nonce != null) {
try {
_nonce = Long.parseLong(nonce);
} catch (NumberFormatException nfe) {}
}
}
public String getActionResults() {
try {
return processAction();
} catch (Throwable t) {
_log.log(Log.CRIT, "Internal error processing web status", t);
return "Internal error processing request - " + t.getMessage();
}
}
public String getSummaryList() {
TunnelControllerGroup group = TunnelControllerGroup.getInstance();
if (group == null)
return "<b>I2PTunnel instances not yet started - please be patient</b>\n";
long nonce = _context.random().nextLong();
StringBuffer buf = new StringBuffer(4*1024);
buf.append("<ul>");
List tunnels = group.getControllers();
for (int i = 0; i < tunnels.size(); i++) {
buf.append("<li>\n");
getSummary(buf, i, (TunnelController)tunnels.get(i), nonce);
buf.append("</li>\n");
}
buf.append("</ul>");
buf.append("<hr /><form action=\"index.jsp\" method=\"GET\">\n");
buf.append("<input type=\"hidden\" name=\"nonce\" value=\"").append(nonce).append("\" />\n");
buf.append("<input type=\"submit\" name=\"action\" value=\"Stop all\" />\n");
buf.append("<input type=\"submit\" name=\"action\" value=\"Start all\" />\n");
buf.append("<input type=\"submit\" name=\"action\" value=\"Restart all\" />\n");
buf.append("<input type=\"submit\" name=\"action\" value=\"Reload config\" />\n");
buf.append("</form>\n");
System.setProperty(getClass().getName() + ".nonce", nonce+"");
return buf.toString();
}
private void getSummary(StringBuffer buf, int num, TunnelController controller, long nonce) {
buf.append("<b>").append(controller.getName()).append("</b>: ");
if (controller.getIsRunning()) {
buf.append("<i>running</i> ");
buf.append("<a href=\"index.jsp?num=").append(num);
buf.append("&nonce=").append(nonce);
buf.append("&action=stop\">stop</a> ");
} else if (controller.getIsStarting()) {
buf.append("<i>startup in progress (please be patient)</i>");
} else {
buf.append("<i>not running</i> ");
buf.append("<a href=\"index.jsp?num=").append(num);
buf.append("&nonce=").append(nonce);
buf.append("&action=start\">start</a> ");
}
buf.append("<a href=\"edit.jsp?num=").append(num).append("\">edit</a> ");
buf.append("<br />\n");
controller.getSummary(buf);
}
private String processAction() {
if ( (_action == null) || (_action.trim().length() <= 0) )
return getMessages();
String expected = System.getProperty(getClass().getName() + ".nonce");
if ( (expected == null) || (!expected.equals(Long.toString(_nonce))) )
return "<b>Invalid nonce, are you being spoofed?</b>";
if ("Stop all".equals(_action))
return stopAll();
else if ("Start all".equals(_action))
return startAll();
else if ("Restart all".equals(_action))
return restartAll();
else if ("Reload config".equals(_action))
return reloadConfig();
else if ("stop".equals(_action))
return stop();
else if ("start".equals(_action))
return start();
else
return "Action <i>" + _action + "</i> unknown";
}
private String stopAll() {
TunnelControllerGroup group = TunnelControllerGroup.getInstance();
if (group == null)
return "<b>I2PTunnel instances not yet started - please be patient</b>\n";
List msgs = group.stopAllControllers();
return getMessages(msgs);
}
private String startAll() {
TunnelControllerGroup group = TunnelControllerGroup.getInstance();
if (group == null)
return "<b>I2PTunnel instances not yet started - please be patient</b>\n";
List msgs = group.startAllControllers();
return getMessages(msgs);
}
private String restartAll() {
TunnelControllerGroup group = TunnelControllerGroup.getInstance();
if (group == null)
return "<b>I2PTunnel instances not yet started - please be patient</b>\n";
List msgs = group.restartAllControllers();
return getMessages(msgs);
}
private String reloadConfig() {
TunnelControllerGroup group = TunnelControllerGroup.getInstance();
if (group == null)
return "<b>I2PTunnel instances not yet started - please be patient</b>\n";
group.reloadControllers();
return "Config reloaded";
}
private String start() {
TunnelControllerGroup group = TunnelControllerGroup.getInstance();
if (group == null)
return "<b>I2PTunnel instances not yet started - please be patient</b>\n";
if (_controllerNum < 0) return "Invalid tunnel";
List controllers = group.getControllers();
if (_controllerNum >= controllers.size()) return "Invalid tunnel";
TunnelController controller = (TunnelController)controllers.get(_controllerNum);
controller.startTunnelBackground();
return getMessages(controller.clearMessages());
}
private String stop() {
TunnelControllerGroup group = TunnelControllerGroup.getInstance();
if (group == null)
return "<b>I2PTunnel instances not yet started - please be patient</b>\n";
if (_controllerNum < 0) return "Invalid tunnel";
List controllers = group.getControllers();
if (_controllerNum >= controllers.size()) return "Invalid tunnel";
TunnelController controller = (TunnelController)controllers.get(_controllerNum);
controller.stopTunnel();
return getMessages(controller.clearMessages());
}
private String getMessages() {
TunnelControllerGroup group = TunnelControllerGroup.getInstance();
if (group == null)
return "";
return getMessages(group.clearAllMessages());
}
private String getMessages(List msgs) {
if (msgs == null) return "";
int num = msgs.size();
switch (num) {
case 0: return "";
case 1: return (String)msgs.get(0);
default:
StringBuffer buf = new StringBuffer(512);
buf.append("<ul>");
for (int i = 0; i < num; i++)
buf.append("<li>").append((String)msgs.get(i)).append("</li>\n");
buf.append("</ul>\n");
return buf.toString();
}
}
}

View File

@ -10,6 +10,7 @@ import java.net.Socket;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.data.Destination;
import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.i2ptunnel.I2PTunnelClientBase;
import net.i2p.i2ptunnel.I2PTunnelRunner;
import net.i2p.i2ptunnel.Logging;
@ -26,8 +27,8 @@ public class I2PSOCKSTunnel extends I2PTunnelClientBase {
// I2PSOCKSTunnel(localPort, l, ownDest, (EventDispatcher)null);
//}
public I2PSOCKSTunnel(int localPort, Logging l, boolean ownDest, EventDispatcher notifyThis) {
super(localPort, ownDest, l, notifyThis, "SOCKSHandler");
public I2PSOCKSTunnel(int localPort, Logging l, boolean ownDest, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(localPort, ownDest, l, notifyThis, "SOCKSHandler", tunnel);
if (waitEventValue("openBaseClientResult").equals("error")) {
notifyEvent("openSOCKSTunnelResult", "error");
@ -46,7 +47,7 @@ public class I2PSOCKSTunnel extends I2PTunnelClientBase {
SOCKSServer serv = SOCKSServerFactory.createSOCKSServer(s);
Socket clientSock = serv.getClientSocket();
I2PSocket destSock = serv.getDestinationI2PSocket();
new I2PTunnelRunner(clientSock, destSock, sockLock, null);
new I2PTunnelRunner(clientSock, destSock, sockLock, null, mySockets);
} catch (SOCKSException e) {
_log.error("Error from SOCKS connection: " + e.getMessage());
closeSocket(s);

View File

@ -14,7 +14,6 @@ import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.DataFormatException;
import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.util.Log;
@ -81,7 +80,7 @@ public abstract class SOCKSServer {
if (connHostName.toLowerCase().endsWith(".i2p")) {
_log.debug("connecting to " + connHostName + "...");
I2PSocketManager sm = I2PSocketManagerFactory.createManager();
destSock = sm.connect(I2PTunnel.destFromName(connHostName), new I2PSocketOptions());
destSock = sm.connect(I2PTunnel.destFromName(connHostName), null);
confirmConnection();
_log.debug("connection confirmed - exchanging data...");
} else {

View File

@ -0,0 +1,16 @@
<%@page contentType="text/html" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<title>I2PTunnel edit</title>
</head><body>
<a href="index.jsp">Back</a>
<jsp:useBean class="net.i2p.i2ptunnel.WebEditPageHelper" id="helper" scope="request" />
<jsp:setProperty name="helper" property="*" />
<b><jsp:getProperty name="helper" property="actionResults" /></b>
<jsp:getProperty name="helper" property="editForm" />
</body>
</html>

View File

@ -0,0 +1,2 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><title>I2P Router Console</title></head>
<body><meta http-equiv="refresh" content="0;url=index.jsp" /><a href="index.jsp">Enter</a></body></html>

View File

@ -0,0 +1,26 @@
<%@page contentType="text/html" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<title>I2PTunnel status</title>
</head><body>
<jsp:useBean class="net.i2p.i2ptunnel.WebStatusPageHelper" id="helper" scope="request" />
<jsp:setProperty name="helper" property="*" />
<h2>Messages since last page load:</h2>
<b><jsp:getProperty name="helper" property="actionResults" /></b>
<jsp:getProperty name="helper" property="summaryList" />
<form action="edit.jsp">
<b>Add new:</b>
<select name="type">
<option value="httpclient">HTTP proxy</option>
<option value="client">Client tunnel</option>
<option value="server">Server tunnel</option>
<option value="httpserver">HTTP server tunnel</option>
</select> <input type="submit" value="GO" />
</form>
</body>
</html>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app>
<!-- precompiled servlets -->
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

48
apps/jetty/build.xml Normal file
View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="all" name="jetty">
<target name="all" depends="build" />
<target name="fetchJettylib" >
<available property="jetty.available" file="jetty-5.1.2.zip" />
<ant target="doFetchJettylib" />
</target>
<target name="doFetchJettylib" unless="jetty.available" >
<echo message="The libraries contained within the fetched file are from Jetty's 5.1.2" />
<echo message="distribution (http://jetty.mortbay.org/). These are not " />
<echo message="necessary for using I2P, but are used by some applications on top of I2P," />
<echo message="such as the routerconsole." />
<get src="http://mesh.dl.sourceforge.net/sourceforge/jetty/jetty-5.1.2.zip" verbose="true" dest="jetty-5.1.2.zip" />
<ant target="doExtract" />
</target>
<target name="doExtract">
<unzip src="jetty-5.1.2.zip" dest="." />
<mkdir dir="jettylib" />
<copy todir="jettylib">
<fileset dir="jetty-5.1.2/lib">
<include name="*.jar" />
</fileset>
</copy>
<copy todir="jettylib">
<fileset dir="jetty-5.1.2/ext">
<include name="ant.jar" />
<include name="commons-el.jar" />
<include name="commons-logging.jar" />
<include name="jasper-compiler.jar" />
<include name="jasper-runtime.jar" />
<include name="javax.servlet.jar" />
<include name="org.mortbay.jetty.jar" />
<include name="xercesImpl.jar" />
</fileset>
</copy>
<delete dir="jetty-5.1.2" />
</target>
<target name="build" depends="fetchJettylib" />
<target name="builddep" />
<target name="compile" />
<target name="jar" />
<target name="clean" />
<target name="cleandep" depends="clean" />
<target name="distclean" depends="clean">
<echo message="Not actually deleting the jetty libs (since they're so large)" />
</target>
</project>

View File

@ -8,7 +8,7 @@
<target name="compile">
<mkdir dir="./build" />
<mkdir dir="./build/obj" />
<javac srcdir="./src" debug="true" destdir="./build/obj" classpath="../../../core/java/build/i2p.jar" />
<javac srcdir="./src" debug="true" deprecation="on" source="1.3" target="1.3" destdir="./build/obj" classpath="../../../core/java/build/i2p.jar" />
</target>
<target name="jar" depends="compile">
<jar destfile="./build/mstreaming.jar" basedir="./build/obj" includes="**/*.class" />

View File

@ -1,31 +1,71 @@
package net.i2p.client.streaming;
/** Like a StringBuffer, but for bytes */
public class ByteCollector {
/**
* Like a StringBuffer, but for bytes. This class is not internally synchronized,
* so care should be taken when using in a multithreaded environment.
*
*/
class ByteCollector {
byte[] contents;
int size;
private static final int INITIAL_CAPACITY = 1024;
private static final int SHORT_CAPACITY = 80;
/**
* New collector with the default initial capacity
*
*/
public ByteCollector() {
contents = new byte[1024];
this(INITIAL_CAPACITY);
}
/**
* New collector with an initial capacity as specified
*
*/
public ByteCollector(int capacity) {
contents = new byte[capacity];
size = 0;
}
/**
* New collector containing the specified bytes
*
*/
public ByteCollector(byte[] b) {
this();
append(b);
}
/**
* New collector with the specified byte
*
*/
public ByteCollector(byte b) {
this();
append(b);
}
/**
* Add a new byte to the collector (extending the buffer if necessary)
*
* @param b byte to add
* @return this object
*/
public ByteCollector append(byte b) {
ensureCapacity(size + 1);
contents[size++] = b;
return this;
}
/**
* Add new bytes to the collector (extending the buffer if necessary)
*
* @param b bytes to add
* @return this object
*/
public ByteCollector append(byte[] b) {
ensureCapacity(size + b.length);
System.arraycopy(b, 0, contents, size, b.length);
@ -33,10 +73,25 @@ public class ByteCollector {
return this;
}
/**
* Add new bytes to the collector (extending the buffer if necessary)
*
* @param b byte array to add from
* @param len number of bytes in the array to add
* @return this object
*/
public ByteCollector append(byte[] b, int len) {
return append(b, 0, len);
}
/**
* Add new bytes to the collector (extending the buffer if necessary)
*
* @param b byte array to add from
* @param off offset into the array to begin adding from
* @param len number of bytes in the array to add
* @return this object
*/
public ByteCollector append(byte[] b, int off, int len) {
ensureCapacity(size + len);
System.arraycopy(b, off, contents, size, len);
@ -44,17 +99,36 @@ public class ByteCollector {
return this;
}
/**
* Add the contents of the byte collector to the current collector (extending the buffer if necessary)
*
* @param bc collector to copy
* @return this object
*/
public ByteCollector append(ByteCollector bc) {
// optimieren?
return append(bc.toByteArray());
}
/**
* Copy the contents of the collector into a new array and return it
*
* @return new array containing a copy of the current collector's data
*/
public byte[] toByteArray() {
byte[] result = new byte[size];
System.arraycopy(contents, 0, result, 0, size);
return result;
}
/**
* Pull off the first $maxlen bytes from the collector, shifting the remaining
* bytes into the beginning of the collector's array.
*
* @param maxlen max number of bytes we want to pull from the collector (we will get
* less if the collector doesnt have that many bytes yet)
* @return copy of the bytes pulled from the collector
*/
public byte[] startToByteArray(int maxlen) {
if (size < maxlen) {
byte[] res = toByteArray();
@ -69,10 +143,22 @@ public class ByteCollector {
}
}
/**
* How many bytes are available for retrieval?
*
* @return number of bytes
*/
public int getCurrentSize() {
return size;
}
/**
* Make sure we have sufficient storage space.
*
* @param cap minimum number of bytes that the buffer should contain
* @return true if the the collector was expanded to meet the minimum,
* false if it was already large enough
*/
public boolean ensureCapacity(int cap) {
if (contents.length < cap) {
int l = contents.length;
@ -87,20 +173,46 @@ public class ByteCollector {
return false;
}
/**
* Does the collector have meaningful data or is it empty?
*
* @return true if it has no data
*/
public boolean isEmpty() {
return size == 0;
}
/**
* Search through the collector for the first occurrence of the sequence of
* bytes contained within the specified collector
*
* @param bc bytes that will be searched for
* @return index into the current collector, or -1 if it isn't found
*/
public int indexOf(ByteCollector bc) {
// optimieren?
return indexOf(bc.toByteArray());
}
/**
* Search through the collector for the first occurrence of the specified
* byte
*
* @param b byte that will be searched for
* @return index into the current collector, or -1 if it isn't found
*/
public int indexOf(byte b) {
// optimieren?
return indexOf(new byte[] { b});
}
/**
* Search through the collector for the first occurrence of the sequence of
* bytes
*
* @param ba bytes that will be searched for
* @return index into the current collector, or -1 if it isn't found
*/
public int indexOf(byte[] ba) {
loop: for (int i = 0; i < size - ba.length + 1; i++) {
for (int j = 0; j < ba.length; j++) {
@ -111,15 +223,28 @@ public class ByteCollector {
return -1;
}
/**
* Empty the collector. This does not affect its capacity.
*
*/
public void clear() {
size = 0;
}
/**
* Empty the collector and reduce its capacity.
*
*/
public void clearAndShorten() {
size = 0;
contents = new byte[80];
contents = new byte[SHORT_CAPACITY];
}
/**
* Render the bytes as a string
*
* @return the, uh, string
*/
public String toString() {
return new String(toByteArray());
}
@ -132,6 +257,12 @@ public class ByteCollector {
return h;
}
/**
* Compare the collectors.
*
* @return true if and only if both are the same size and the
* byte arrays they contain are equal.
*/
public boolean equals(Object o) {
if (o instanceof ByteCollector) {
ByteCollector by = (ByteCollector) o;
@ -145,7 +276,13 @@ public class ByteCollector {
}
}
public byte removeFirst() {
/**
* Remove the first byte from the collector, shifting its contents accordingly.
*
* @return byte removed
* @throws IllegalArgumentException if the collector is empty
*/
public byte removeFirst() throws IllegalArgumentException {
byte bb = contents[0];
if (size == 0) throw new IllegalArgumentException("ByteCollector is empty");
if (size > 1)

View File

@ -32,8 +32,18 @@ public interface I2PSocket {
*/
public OutputStream getOutputStream() throws IOException;
/**
* Retrieve this socket's configuration
*/
public I2PSocketOptions getOptions();
/**
* Configure the socket
*/
public void setOptions(I2PSocketOptions options);
/**
* How long we will wait blocked on a read() operation.
* How long we will wait blocked on a read() operation. This is simply a
* helper to query the I2PSocketOptions
*
* @return milliseconds to wait, or -1 if we will wait indefinitely
*/
@ -41,7 +51,8 @@ public interface I2PSocket {
/**
* Define how long we will wait blocked on a read() operation (-1 will make
* the socket wait forever).
* the socket wait forever). This is simply a helper to adjust the
* I2PSocketOptions
*
*/
public void setReadTimeout(long ms);

View File

@ -5,12 +5,15 @@ import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PSessionException;
import net.i2p.data.Destination;
import net.i2p.util.Clock;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Initial stub implementation for the socket
*
@ -21,7 +24,7 @@ class I2PSocketImpl implements I2PSocket {
public static final int MAX_PACKET_SIZE = 1024 * 32;
public static final int PACKET_DELAY = 100;
private I2PSocketManager manager;
private I2PSocketManagerImpl manager;
private Destination local;
private Destination remote;
private String localID;
@ -29,12 +32,16 @@ class I2PSocketImpl implements I2PSocket {
private Object remoteIDWaiter = new Object();
private I2PInputStream in;
private I2POutputStream out;
private SocketErrorListener _socketErrorListener;
private I2PSocket.SocketErrorListener _socketErrorListener;
private boolean outgoing;
private long _socketId;
private static long __socketId = 0;
private long _bytesRead = 0;
private long _bytesWritten = 0;
private long _createdOn;
private long _closedOn;
private long _remoteIdSetTime;
private I2PSocketOptions _options;
private Object flagLock = new Object();
/**
@ -62,17 +69,23 @@ class I2PSocketImpl implements I2PSocket {
* @param outgoing did we initiate the connection (true) or did we receive it (false)?
* @param localID what is our half of the socket ID?
*/
public I2PSocketImpl(Destination peer, I2PSocketManager mgr, boolean outgoing, String localID) {
public I2PSocketImpl(Destination peer, I2PSocketManagerImpl mgr, boolean outgoing, String localID) {
this.outgoing = outgoing;
manager = mgr;
remote = peer;
_socketId = ++__socketId;
local = mgr.getSession().getMyDestination();
in = new I2PInputStream();
I2PInputStream pin = new I2PInputStream();
String us = mgr.getSession().getMyDestination().calculateHash().toBase64().substring(0,4);
String name = us + (outgoing ? "->" : "<-") + peer.calculateHash().toBase64().substring(0,4);
in = new I2PInputStream(name + " in");
I2PInputStream pin = new I2PInputStream(name + " out");
out = new I2POutputStream(pin);
new I2PSocketRunner(pin);
this.localID = localID;
_createdOn = I2PAppContext.getGlobalContext().clock().now();
_remoteIdSetTime = -1;
_closedOn = -1;
_options = mgr.getDefaultOptions();
}
/**
@ -89,6 +102,7 @@ class I2PSocketImpl implements I2PSocket {
public void setRemoteID(String id) {
synchronized (remoteIDWaiter) {
remoteID = id;
_remoteIdSetTime = System.currentTimeMillis();
remoteIDWaiter.notifyAll();
}
}
@ -123,20 +137,27 @@ class I2PSocketImpl implements I2PSocket {
long dieAfter = System.currentTimeMillis() + maxWait;
synchronized (remoteIDWaiter) {
if (wait) {
try {
if (maxWait >= 0)
remoteIDWaiter.wait(maxWait);
else
remoteIDWaiter.wait();
} catch (InterruptedException ex) {
if (remoteID == null) {
try {
if (maxWait >= 0)
remoteIDWaiter.wait(maxWait);
else
remoteIDWaiter.wait();
} catch (InterruptedException ex) {
}
}
if ((maxWait >= 0) && (System.currentTimeMillis() >= dieAfter))
throw new InterruptedIOException("Timed out waiting for remote ID");
long now = System.currentTimeMillis();
if ((maxWait >= 0) && (now >= dieAfter)) {
long waitedExcess = now - dieAfter;
throw new InterruptedIOException("Timed out waiting for remote ID (waited " + waitedExcess
+ "ms too long [" + maxWait + "ms, remId " + remoteID
+ ", remId set " + (now-_remoteIdSetTime) + "ms ago])");
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("TIMING: RemoteID set to "
+ I2PSocketManager.getReadableForm(remoteID) + " for "
+ I2PSocketManagerImpl.getReadableForm(remoteID) + " for "
+ this.hashCode());
}
return remoteID;
@ -160,7 +181,11 @@ class I2PSocketImpl implements I2PSocket {
*/
public void queueData(byte[] data) {
_bytesRead += data.length;
in.queueData(data);
try {
in.queueData(data, false);
} catch (IOException ioe) {
_log.log(Log.CRIT, "wtf, we said DONT block, how can we timeout?", ioe);
}
}
/**
@ -199,21 +224,24 @@ class I2PSocketImpl implements I2PSocket {
*/
public void close() throws IOException {
synchronized (flagLock) {
_log.debug("Closing connection");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Closing connection");
closed = true;
_closedOn = I2PAppContext.getGlobalContext().clock().now();
}
out.close();
in.notifyClosed();
}
/**
* Close the socket from the I2P side, e. g. by a close packet.
* Close the socket from the I2P side (by a close packet)
*/
protected void internalClose() {
synchronized (flagLock) {
closed = true;
closed2 = true;
sendClose = false;
_closedOn = I2PAppContext.getGlobalContext().clock().now();
}
out.close();
in.notifyClosed();
@ -221,25 +249,42 @@ class I2PSocketImpl implements I2PSocket {
private byte getMask(int add) {
if (outgoing)
return (byte)(I2PSocketManager.DATA_IN + (byte)add);
return (byte)(I2PSocketManagerImpl.DATA_IN + (byte)add);
else
return (byte)(I2PSocketManager.DATA_OUT + (byte)add);
return (byte)(I2PSocketManagerImpl.DATA_OUT + (byte)add);
}
public void setOptions(I2PSocketOptions options) {
_options = options;
in.setReadTimeout(options.getReadTimeout());
}
public I2PSocketOptions getOptions() {
return _options;
}
/**
* How long we will wait blocked on a read() operation. This is simply a
* helper to query the I2PSocketOptions
*
* @return milliseconds to wait, or -1 if we will wait indefinitely
*/
public long getReadTimeout() {
return _options.getReadTimeout();
}
/**
* What is the longest we'll block on the input stream while waiting
* for more data? If this value is exceeded, the read() throws
* InterruptedIOException
* Define how long we will wait blocked on a read() operation (-1 will make
* the socket wait forever). This is simply a helper to adjust the
* I2PSocketOptions
*
*/
public long getReadTimeout() {
return in.getReadTimeout();
}
public void setReadTimeout(long ms) {
_options.setReadTimeout(ms);
in.setReadTimeout(ms);
}
public void setSocketErrorListener(SocketErrorListener lsnr) {
public void setSocketErrorListener(I2PSocket.SocketErrorListener lsnr) {
_socketErrorListener = lsnr;
}
@ -248,19 +293,34 @@ class I2PSocketImpl implements I2PSocket {
_socketErrorListener.errorOccurred();
}
public long getBytesSent() { return _bytesWritten; }
public long getBytesReceived() { return _bytesRead; }
public long getCreatedOn() { return _createdOn; }
public long getClosedOn() { return _closedOn; }
private String getPrefix() { return "[" + _socketId + "]: "; }
//--------------------------------------------------
private class I2PInputStream extends InputStream {
private String streamName;
private ByteCollector bc = new ByteCollector();
private boolean inStreamClosed = false;
private long readTimeout = -1;
public I2PInputStream(String name) {
streamName = name;
}
public long getReadTimeout() {
return readTimeout;
}
private String getStreamPrefix() {
return getPrefix() + streamName + ": ";
}
public void setReadTimeout(long ms) {
readTimeout = ms;
}
@ -273,42 +333,58 @@ class I2PSocketImpl implements I2PSocket {
throw new RuntimeException("Incorrect read() result");
}
public synchronized int read(byte[] b, int off, int len) throws IOException {
public int read(byte[] b, int off, int len) throws IOException {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Read called: " + this.hashCode());
_log.debug(getStreamPrefix() + "Read called for " + len + " bytes (avail="
+ bc.getCurrentSize() + "): " + this.hashCode());
if (len == 0) return 0;
long dieAfter = System.currentTimeMillis() + readTimeout;
byte[] read = bc.startToByteArray(len);
byte[] read = null;
synchronized (bc) {
read = bc.startToByteArray(len);
bc.notifyAll();
}
boolean timedOut = false;
while (read.length == 0) {
while ( (read.length == 0) && (!inStreamClosed) ) {
synchronized (flagLock) {
if (closed) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Closed is set after reading " + _bytesRead + " and writing " + _bytesWritten + ", so closing stream: " + hashCode());
_log.debug(getStreamPrefix() + "Closed is set after reading "
+ _bytesRead + " and writing " + _bytesWritten
+ ", so closing stream: " + hashCode());
return -1;
}
}
try {
if (readTimeout >= 0) {
wait(readTimeout);
} else {
wait();
synchronized (I2PSocketImpl.I2PInputStream.this) {
if (readTimeout >= 0) {
wait(readTimeout);
} else {
wait();
}
}
} catch (InterruptedException ex) {}
if ((readTimeout >= 0)
&& (System.currentTimeMillis() >= dieAfter)) {
throw new InterruptedIOException(getPrefix() + "Timeout reading from I2PSocket (" + readTimeout + " msecs)");
throw new InterruptedIOException(getStreamPrefix() + "Timeout reading from I2PSocket ("
+ readTimeout + " msecs)");
}
read = bc.startToByteArray(len);
synchronized (bc) {
read = bc.startToByteArray(len);
bc.notifyAll();
}
}
if (read.length > len) throw new RuntimeException("BUG");
if ( (inStreamClosed) && ( (read == null) || (read.length <= 0) ) )
return -1;
System.arraycopy(read, 0, b, off, read.length);
if (_log.shouldLog(Log.DEBUG)) {
_log.debug(getPrefix() + "Read from I2PInputStream " + hashCode() + " returned "
_log.debug(getStreamPrefix() + "Read from I2PInputStream " + hashCode() + " returned "
+ read.length + " bytes");
}
//if (_log.shouldLog(Log.DEBUG)) {
@ -320,27 +396,88 @@ class I2PSocketImpl implements I2PSocket {
}
public int available() {
return bc.getCurrentSize();
synchronized (bc) {
return bc.getCurrentSize();
}
}
public void queueData(byte[] data) {
queueData(data, 0, data.length);
/**
* Add the data to the queue
*
* @param allowBlock if true, we will block if the buffer and the socket options
* say so, otherwise we simply take the data regardless.
* @throws InterruptedIOException if the queue's buffer is full, the socket has
* a write timeout, and that timeout is exceeded
* @throws IOException if the connection was closed while queueing up the data
*/
void queueData(byte[] data, boolean allowBlock) throws InterruptedIOException, IOException {
queueData(data, 0, data.length, allowBlock);
}
public synchronized void queueData(byte[] data, int off, int len) {
/**
* Add the data to the queue
*
* @param allowBlock if true, we will block if the buffer and the socket options
* say so, otherwise we simply take the data regardless.
* @throws InterruptedIOException if the queue's buffer is full, the socket has
* a write timeout, and that timeout is exceeded
* @throws IOException if the connection was closed while queueing up the data
*/
public void queueData(byte[] data, int off, int len, boolean allowBlock) throws InterruptedIOException, IOException {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Insert " + len + " bytes into queue: " + hashCode());
bc.append(data, off, len);
notifyAll();
_log.debug(getStreamPrefix() + "Insert " + len + " bytes into queue: " + hashCode());
Clock clock = I2PAppContext.getGlobalContext().clock();
long endAfter = clock.now() + _options.getWriteTimeout();
synchronized (bc) {
if (allowBlock) {
if (_options.getMaxBufferSize() > 0) {
while (bc.getCurrentSize() > _options.getMaxBufferSize()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getStreamPrefix() + "Buffer size exceeded: pending "
+ bc.getCurrentSize() + " limit " + _options.getMaxBufferSize());
if (_options.getWriteTimeout() > 0) {
long timeLeft = endAfter - clock.now();
if (timeLeft <= 0) {
long waited = _options.getWriteTimeout() - timeLeft;
throw new InterruptedIOException(getStreamPrefix() + "Waited too long ("
+ waited + "ms) to write "
+ len + " with a buffer at " + bc.getCurrentSize());
}
}
if (inStreamClosed)
throw new IOException(getStreamPrefix() + "Stream closed while writing");
if (_closedOn > 0)
throw new IOException(getStreamPrefix() + "I2PSocket closed while writing");
try {
bc.wait(1000);
} catch (InterruptedException ie) {}
}
}
}
bc.append(data, off, len);
}
synchronized (I2PInputStream.this) {
I2PInputStream.this.notifyAll();
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getStreamPrefix() + "After insert " + len + " bytes into queue: " + hashCode());
}
public synchronized void notifyClosed() {
I2PInputStream.this.notifyAll();
public void notifyClosed() {
synchronized (I2PInputStream.this) {
I2PInputStream.this.notifyAll();
}
}
public void close() throws IOException {
super.close();
notifyClosed();
synchronized (bc) {
inStreamClosed = true;
bc.notifyAll();
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getStreamPrefix() + "After close");
}
}
@ -359,7 +496,7 @@ class I2PSocketImpl implements I2PSocket {
public void write(byte[] b, int off, int len) throws IOException {
_bytesWritten += len;
sendTo.queueData(b, off, len);
sendTo.queueData(b, off, len, true);
}
public void close() {
@ -388,10 +525,21 @@ class I2PSocketImpl implements I2PSocket {
*/
private boolean handleNextPacket(ByteCollector bc, byte buffer[])
throws IOException, I2PSessionException {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + ":" + Thread.currentThread().getName() + "handleNextPacket");
int len = in.read(buffer);
int bcsize = bc.getCurrentSize();
int bcsize = 0;
synchronized (bc) {
bcsize = bc.getCurrentSize();
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + ":" + Thread.currentThread().getName() + "handleNextPacket len=" + len + " bcsize=" + bcsize);
if (len != -1) {
bc.append(buffer, len);
synchronized (bc) {
bc.append(buffer, len);
}
} else if (bcsize == 0) {
// nothing left in the buffer, and read(..) got EOF (-1).
// the bart the
@ -399,7 +547,7 @@ class I2PSocketImpl implements I2PSocket {
}
if ((bcsize < MAX_PACKET_SIZE) && (in.available() == 0)) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Runner Point d: " + hashCode());
_log.debug(getPrefix() + ":" + Thread.currentThread().getName() + "Runner Point d: " + hashCode());
try {
Thread.sleep(PACKET_DELAY);
@ -408,15 +556,22 @@ class I2PSocketImpl implements I2PSocket {
}
}
if ((bcsize >= MAX_PACKET_SIZE) || (in.available() == 0)) {
byte[] data = bc.startToByteArray(MAX_PACKET_SIZE);
byte data[] = null;
synchronized (bc) {
data = bc.startToByteArray(MAX_PACKET_SIZE);
}
if (data.length > 0) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Message size is: " + data.length);
_log.debug(getPrefix() + ":" + Thread.currentThread().getName() + "Message size is: " + data.length);
boolean sent = sendBlock(data);
if (!sent) {
_log.error(getPrefix() + "Error sending message to peer. Killing socket runner");
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + ":" + Thread.currentThread().getName() + "Error sending message to peer. Killing socket runner");
errorOccurred();
return false;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + ":" + Thread.currentThread().getName() + "Message sent to peer");
}
}
}
@ -433,11 +588,19 @@ class I2PSocketImpl implements I2PSocket {
while (keepHandling) {
keepHandling = handleNextPacket(bc, buffer);
packetsHandled++;
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + ":" + Thread.currentThread().getName()
+ "Packets handled: " + packetsHandled);
}
if (_log.shouldLog(Log.INFO))
_log.info(getPrefix() + ":" + Thread.currentThread().getName()
+ "After handling packets, we're done. Packets handled: " + packetsHandled);
if ((bc.getCurrentSize() > 0) && (packetsHandled > 1)) {
_log.error(getPrefix() + "A SCARY MONSTER HAS EATEN SOME DATA! " + "(input stream: "
+ in.hashCode() + "; "
+ "queue size: " + bc.getCurrentSize() + ")");
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + "We lost some data queued up due to a network send error (input stream: "
+ in.hashCode() + "; "
+ "queue size: " + bc.getCurrentSize() + ")");
}
synchronized (flagLock) {
closed2 = true;
@ -448,15 +611,20 @@ class I2PSocketImpl implements I2PSocket {
} // FIXME: Race here?
if (sc) {
if (_log.shouldLog(Log.INFO))
_log.info(getPrefix() + "Sending close packet: (we started? " + outgoing + ") after reading " + _bytesRead + " and writing " + _bytesWritten);
byte[] packet = I2PSocketManager.makePacket(getMask(0x02), remoteID, new byte[0]);
_log.info(getPrefix() + ":" + Thread.currentThread().getName()
+ "Sending close packet: (we started? " + outgoing
+ ") after reading " + _bytesRead + " and writing " + _bytesWritten);
byte[] packet = I2PSocketManagerImpl.makePacket(getMask(0x02), remoteID, new byte[0]);
boolean sent = manager.getSession().sendMessage(remote, packet);
if (!sent) {
_log.error(getPrefix() + "Error sending close packet to peer");
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + ":" + Thread.currentThread().getName()
+ "Error sending close packet to peer");
errorOccurred();
}
}
manager.removeSocket(I2PSocketImpl.this);
internalClose();
} catch (InterruptedIOException ex) {
_log.error(getPrefix() + "BUG! read() operations should not timeout!", ex);
} catch (IOException ex) {
@ -477,7 +645,7 @@ class I2PSocketImpl implements I2PSocket {
_log.error(getPrefix() + "NULL REMOTEID");
return false;
}
byte[] packet = I2PSocketManager.makePacket(getMask(0x00), remoteID, data);
byte[] packet = I2PSocketManagerImpl.makePacket(getMask(0x00), remoteID, data);
boolean sent;
synchronized (flagLock) {
if (closed2) return false;

View File

@ -4,25 +4,17 @@
*/
package net.i2p.client.streaming;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.I2PSessionListener;
import net.i2p.data.Base64;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.Log;
/**
* Centralize the coordination and multiplexing of the local client's streaming.
@ -32,45 +24,8 @@ import net.i2p.util.Log;
* or receive any messages with its .receiveMessage
*
*/
public class I2PSocketManager implements I2PSessionListener {
private final static Log _log = new Log(I2PSocketManager.class);
private I2PSession _session;
private I2PServerSocketImpl _serverSocket = null;
private Object lock = new Object(); // for locking socket lists
private HashMap _outSockets;
private HashMap _inSockets;
private I2PSocketOptions _defaultOptions;
private long _acceptTimeout;
public static final short ACK = 0x51;
public static final short CLOSE_OUT = 0x52;
public static final short DATA_OUT = 0x50;
public static final short SYN = 0xA1;
public static final short CLOSE_IN = 0xA2;
public static final short DATA_IN = 0xA0;
public static final short CHAFF = 0xFF;
/**
* How long to wait for the client app to accept() before sending back CLOSE?
* This includes the time waiting in the queue. Currently set to 5 seconds.
*/
private static final long ACCEPT_TIMEOUT_DEFAULT = 5*1000;
public I2PSocketManager() {
_session = null;
_inSockets = new HashMap(16);
_outSockets = new HashMap(16);
_acceptTimeout = ACCEPT_TIMEOUT_DEFAULT;
}
public I2PSession getSession() {
return _session;
}
public void setSession(I2PSession session) {
_session = session;
if (session != null) session.setSessionListener(this);
}
public interface I2PSocketManager {
public I2PSession getSession();
/**
* How long should we wait for the client to .accept() a socket before
@ -78,321 +33,14 @@ public class I2PSocketManager implements I2PSessionListener {
*
* @param ms milliseconds to wait, maximum
*/
public void setAcceptTimeout(long ms) { _acceptTimeout = ms; }
public long getAcceptTimeout() { return _acceptTimeout; }
public void disconnected(I2PSession session) {
_log.info("Disconnected from the session");
destroySocketManager();
}
public void errorOccurred(I2PSession session, String message, Throwable error) {
_log.error("Error occurred: [" + message + "]", error);
}
public void setAcceptTimeout(long ms);
public long getAcceptTimeout();
public void setDefaultOptions(I2PSocketOptions options);
public I2PSocketOptions getDefaultOptions();
public I2PServerSocket getServerSocket();
public void messageAvailable(I2PSession session, int msgId, long size) {
try {
I2PSocketImpl s;
byte msg[] = session.receiveMessage(msgId);
if (msg.length == 1 && msg[0] == -1) {
_log.debug("Ping received");
return;
}
if (msg.length < 4) {
_log.error("==== packet too short ====");
return;
}
int type = msg[0] & 0xff;
String id = toString(new byte[] { msg[1], msg[2], msg[3]});
byte[] payload = new byte[msg.length - 4];
System.arraycopy(msg, 4, payload, 0, payload.length);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Message read: type = [" + Integer.toHexString(type)
+ "] id = [" + getReadableForm(id)
+ "] payload length: [" + payload.length + "]");
switch (type) {
case ACK:
ackAvailable(id, payload);
return;
case CLOSE_OUT:
disconnectAvailable(id, payload);
return;
case DATA_OUT:
sendOutgoingAvailable(id, payload);
return;
case SYN:
synIncomingAvailable(id, payload, session);
return;
case CLOSE_IN:
disconnectIncoming(id, payload);
return;
case DATA_IN:
sendIncoming(id, payload);
case CHAFF:
// ignore
return;
default:
handleUnknown(type, id, payload);
return;
}
} catch (I2PException ise) {
_log.error("Error processing", ise);
} catch (IllegalStateException ise) {
_log.debug("Error processing", ise);
}
}
/**
* We've received an ACK packet (hopefully, in response to a SYN that we
* recently sent out). Notify the associated I2PSocket that we now have
* the remote stream ID (which should get things going, since the handshake
* is complete).
*
*/
private void ackAvailable(String id, byte payload[]) {
I2PSocketImpl s = null;
synchronized (lock) {
s = (I2PSocketImpl) _outSockets.get(id);
}
if (s == null) {
_log.warn("No socket responsible for ACK packet");
return;
}
String remoteId = null;
remoteId = s.getRemoteID(false);
if ( (payload.length == 3) && (remoteId == null) ) {
String newID = toString(payload);
s.setRemoteID(newID);
return;
} else {
// (payload.length != 3 || getRemoteId != null)
if (_log.shouldLog(Log.WARN)) {
if (payload.length != 3)
_log.warn("Ack packet had " + payload.length + " bytes");
else
_log.warn("Remote ID already exists? " + remoteId);
}
return;
}
}
/**
* We received a disconnect packet, telling us to tear down the specified
* stream.
*/
private void disconnectAvailable(String id, byte payload[]) {
I2PSocketImpl s = null;
synchronized (lock) {
s = (I2PSocketImpl) _outSockets.get(id);
}
_log.debug("*Disconnect outgoing for socket " + s);
try {
if (s != null) {
if (payload.length > 0) {
_log.debug("Disconnect packet had "
+ payload.length + " bytes");
}
if (s.getRemoteID(false) == null) {
s.setRemoteID(null); // Just to wake up socket
return;
}
s.internalClose();
synchronized (lock) {
_outSockets.remove(id);
}
}
return;
} catch (Exception t) {
_log.error("Ignoring error on disconnect for socket " + s, t);
}
}
/**
* We've received data on a stream we created - toss the data onto
* the socket for handling.
*
* @throws IllegalStateException if the socket isn't open or isn't known
*/
private void sendOutgoingAvailable(String id, byte payload[]) throws IllegalStateException {
I2PSocketImpl s = null;
synchronized (lock) {
s = (I2PSocketImpl) _outSockets.get(id);
}
// packet send outgoing
if (_log.shouldLog(Log.DEBUG))
_log.debug("*Packet send outgoing [" + payload.length + "] for socket " + s);
if (s != null) {
s.queueData(payload);
return;
} else {
_log.error("Null socket with data available");
throw new IllegalStateException("Null socket with data available");
}
}
/**
* We've received a SYN packet (a request for a new stream). If the client has
* said they want incoming sockets (by retrieving the serverSocket), the stream
* will be ACKed, but if they have not, they'll be NACKed)
*
* @throws DataFormatException if the destination in the SYN was invalid
* @throws I2PSessionException if there was an I2P error sending the ACK or NACK
*/
private void synIncomingAvailable(String id, byte payload[], I2PSession session)
throws DataFormatException, I2PSessionException {
Destination d = new Destination();
d.fromByteArray(payload);
I2PSocketImpl s = null;
boolean acceptConnections = (_serverSocket != null);
String newLocalID = null;
synchronized (lock) {
newLocalID = makeID(_inSockets);
if (acceptConnections) {
s = new I2PSocketImpl(d, this, false, newLocalID);
s.setRemoteID(id);
}
}
_log.debug("*Syn! for socket " + s);
if (!acceptConnections) {
// The app did not instantiate an I2PServerSocket
byte[] packet = makePacket((byte) CLOSE_OUT, id, toBytes(newLocalID));
boolean replySentOk = false;
synchronized (_session) {
replySentOk = _session.sendMessage(d, packet);
}
if (!replySentOk) {
_log.error("Error sending close to " + d.calculateHash().toBase64()
+ " in response to a new con message",
new Exception("Failed creation"));
}
return;
}
if (_serverSocket.addWaitForAccept(s, _acceptTimeout)) {
_inSockets.put(newLocalID, s);
byte[] packet = makePacket((byte) ACK, id, toBytes(newLocalID));
boolean replySentOk = false;
replySentOk = _session.sendMessage(d, packet);
if (!replySentOk) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error sending reply to " + d.calculateHash().toBase64()
+ " in response to a new con message for socket " + s,
new Exception("Failed creation"));
s.internalClose();
}
} else {
// timed out or serverSocket closed
byte[] packet = toBytes(" " + id);
packet[0] = CLOSE_OUT;
boolean nackSent = session.sendMessage(d, packet);
if (!nackSent) {
_log.warn("Error sending NACK for session creation for socket " + s);
}
s.internalClose();
}
return;
}
/**
* We've received a disconnect for a socket we didn't initiate, so kill
* the socket.
*
*/
private void disconnectIncoming(String id, byte payload[]) {
I2PSocketImpl s = null;
synchronized (lock) {
s = (I2PSocketImpl) _inSockets.get(id);
if (payload.length == 0 && s != null) {
_inSockets.remove(id);
}
}
_log.debug("*Disconnect incoming for socket " + s);
try {
if (payload.length == 0 && s != null) {
s.internalClose();
return;
} else {
if ( (payload.length > 0) && (_log.shouldLog(Log.ERROR)) )
_log.error("Disconnect packet had " + payload.length + " bytes");
if (s != null)
s.internalClose();
return;
}
} catch (Exception t) {
_log.error("Ignoring error on disconnect", t);
return;
}
}
/**
* We've received data on a stream we received - toss the data onto
* the socket for handling.
*
* @throws IllegalStateException if the socket isn't open or isn't known
*/
private void sendIncoming(String id, byte payload[]) {
I2PSocketImpl s = null;
synchronized (lock) {
s = (I2PSocketImpl) _inSockets.get(id);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("*Packet send incoming [" + payload.length + "] for socket " + s);
if (s != null) {
s.queueData(payload);
return;
} else {
_log.info("Null socket with data available");
throw new IllegalStateException("Null socket with data available");
}
}
/**
* Unknown packet. moo.
*
*/
private void handleUnknown(int type, String id, byte payload[]) {
_log.error("\n\n=============== Unknown packet! " + "============"
+ "\nType: " + (int) type
+ "\nID: " + getReadableForm(id)
+ "\nBase64'ed Data: " + Base64.encode(payload)
+ "\n\n\n");
if (id != null) {
synchronized (lock) {
_inSockets.remove(id);
_outSockets.remove(id);
}
}
}
public void reportAbuse(I2PSession session, int severity) {
_log.error("Abuse reported [" + severity + "]");
}
public void setDefaultOptions(I2PSocketOptions options) {
_defaultOptions = options;
}
public I2PSocketOptions getDefaultOptions() {
return _defaultOptions;
}
public I2PServerSocket getServerSocket() {
if (_serverSocket == null) {
_serverSocket = new I2PServerSocketImpl(this);
}
return _serverSocket;
}
public I2PSocketOptions buildOptions();
public I2PSocketOptions buildOptions(Properties opts);
/**
* Create a new connected socket (block until the socket is created)
@ -407,77 +55,7 @@ public class I2PSocketManager implements I2PSessionListener {
*/
public I2PSocket connect(Destination peer, I2PSocketOptions options)
throws I2PException, ConnectException,
NoRouteToHostException, InterruptedIOException {
String localID, lcID;
I2PSocketImpl s;
synchronized (lock) {
localID = makeID(_outSockets);
lcID = getReadableForm(localID);
s = new I2PSocketImpl(peer, this, true, localID);
_outSockets.put(localID, s);
}
try {
ByteArrayOutputStream pubkey = new ByteArrayOutputStream();
_session.getMyDestination().writeBytes(pubkey);
String remoteID;
byte[] packet = makePacket((byte) SYN, localID, pubkey.toByteArray());
boolean sent = false;
sent = _session.sendMessage(peer, packet);
if (!sent) {
_log.info("Unable to send & receive ack for SYN packet for socket " + s);
synchronized (lock) {
_outSockets.remove(s.getLocalID());
}
throw new I2PException("Error sending through I2P network");
}
remoteID = s.getRemoteID(true, options.getConnectTimeout());
if (remoteID == null)
throw new ConnectException("Connection refused by peer for socket " + s);
if ("".equals(remoteID))
throw new NoRouteToHostException("Unable to reach peer for socket " + s);
if (_log.shouldLog(Log.DEBUG))
_log.debug("TIMING: s given out for remoteID "
+ getReadableForm(remoteID) + " for socket " + s);
return s;
} catch (InterruptedIOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Timeout waiting for ack from syn for id "
+ getReadableForm(lcID) + " for socket " + s, ioe);
synchronized (lock) {
_outSockets.remove(s.getLocalID());
}
s.internalClose();
throw new InterruptedIOException("Timeout waiting for ack");
} catch (ConnectException ex) {
s.internalClose();
throw ex;
} catch (NoRouteToHostException ex) {
s.internalClose();
throw ex;
} catch (IOException ex) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error sending syn on id " + getReadableForm(lcID) + " for socket " + s, ex);
synchronized (lock) {
_outSockets.remove(s.getLocalID());
}
s.internalClose();
throw new I2PException("Unhandled IOException occurred");
} catch (I2PException ex) {
if (_log.shouldLog(Log.INFO))
_log.info("Error sending syn on id " + getReadableForm(lcID) + " for socket " + s, ex);
synchronized (lock) {
_outSockets.remove(s.getLocalID());
}
s.internalClose();
throw ex;
} catch (Exception e) {
s.internalClose();
_log.error("Unhandled error connecting", e);
throw new ConnectException("Unhandled error connecting: " + e.getMessage());
}
}
NoRouteToHostException, InterruptedIOException;
/**
* Create a new connected socket (block until the socket is created)
@ -490,153 +68,37 @@ public class I2PSocketManager implements I2PSessionListener {
* @throws I2PException if there is some other I2P-related problem
*/
public I2PSocket connect(Destination peer) throws I2PException, ConnectException,
NoRouteToHostException, InterruptedIOException {
return connect(peer, null);
}
NoRouteToHostException, InterruptedIOException;
/**
* Destroy the socket manager, freeing all the associated resources. This
* method will block untill all the managed sockets are closed.
*
*/
public void destroySocketManager() {
if (_serverSocket != null) {
_serverSocket.close();
_serverSocket = null;
}
synchronized (lock) {
Iterator iter;
String id = null;
I2PSocketImpl sock;
iter = _inSockets.keySet().iterator();
while (iter.hasNext()) {
id = (String)iter.next();
sock = (I2PSocketImpl)_inSockets.get(id);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Closing inSocket \""
+ getReadableForm(sock.getLocalID()) + "\"");
sock.internalClose();
}
iter = _outSockets.keySet().iterator();
while (iter.hasNext()) {
id = (String)iter.next();
sock = (I2PSocketImpl)_outSockets.get(id);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Closing outSocket \""
+ getReadableForm(sock.getLocalID()) + "\"");
sock.internalClose();
}
}
_log.debug("Waiting for all open sockets to really close...");
synchronized (lock) {
while ((_inSockets.size() != 0) || (_outSockets.size() != 0)) {
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
try {
_log.debug("Destroying I2P session...");
_session.destroySession();
_log.debug("I2P session destroyed");
} catch (I2PSessionException e) {
_log.error("Error destroying I2P session", e);
}
}
public void destroySocketManager();
/**
* Retrieve a set of currently connected I2PSockets, either initiated locally or remotely.
*
*/
public Set listSockets() {
Set sockets = new HashSet(8);
synchronized (lock) {
sockets.addAll(_inSockets.values());
sockets.addAll(_outSockets.values());
}
return sockets;
}
public Set listSockets();
/**
* Ping the specified peer, returning true if they replied to the ping within
* the timeout specified, false otherwise. This call blocks.
*
*/
public boolean ping(Destination peer, long timeoutMs) {
try {
return _session.sendMessage(peer, new byte[] { (byte) CHAFF});
} catch (I2PException ex) {
_log.error("I2PException:", ex);
return false;
}
}
public boolean ping(Destination peer, long timeoutMs);
public void removeSocket(I2PSocketImpl sock) {
synchronized (lock) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Removing socket \"" + getReadableForm(sock.getLocalID()) + "\" [" + sock + "]");
_inSockets.remove(sock.getLocalID());
_outSockets.remove(sock.getLocalID());
lock.notify();
}
}
public String getName();
public void setName(String name);
public static String getReadableForm(String id) {
if (id == null) return "(null)";
if (id.length() != 3) return "Bogus";
return Base64.encode(toBytes(id));
}
/**
* Create a new part the connection ID that is locally unique
*
* @param uniqueIn map of already known local IDs so we don't collide. WARNING - NOT THREADSAFE!
*/
private static String makeID(HashMap uniqueIn) {
String newID;
do {
int id = (int) (Math.random() * 16777215 + 1);
byte[] nid = new byte[3];
nid[0] = (byte) (id / 65536);
nid[1] = (byte) ((id / 256) % 256);
nid[2] = (byte) (id % 256);
newID = toString(nid);
} while (uniqueIn.get(newID) != null);
return newID;
}
/**
* Create a new packet of the given type for the specified connection containing
* the given payload
*/
public static byte[] makePacket(byte type, String id, byte[] payload) {
byte[] packet = new byte[payload.length + 4];
packet[0] = type;
byte[] temp = toBytes(id);
if (temp.length != 3) throw new RuntimeException("Incorrect ID length: " + temp.length);
System.arraycopy(temp, 0, packet, 1, 3);
System.arraycopy(payload, 0, packet, 4, payload.length);
return packet;
}
public void init(I2PAppContext context, I2PSession session, Properties opts, String name);
private static final String toString(byte data[]) {
try {
return new String(data, "ISO-8859-1");
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("WTF! iso-8859-1 isn't supported?");
}
}
public void addDisconnectListener(DisconnectListener lsnr);
public void removeDisconnectListener(DisconnectListener lsnr);
private static final byte[] toBytes(String str) {
try {
return str.getBytes("ISO-8859-1");
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("WTF! iso-8859-1 isn't supported?");
}
public static interface DisconnectListener {
public void sessionDisconnected();
}
}

View File

@ -4,8 +4,10 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
@ -23,6 +25,10 @@ import net.i2p.util.Log;
public class I2PSocketManagerFactory {
private final static Log _log = new Log(I2PSocketManagerFactory.class);
public static final String PROP_MANAGER = "i2p.streaming.manager";
//public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.I2PSocketManagerImpl";
public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.I2PSocketManagerFull";
/**
* Create a socket manager using a brand new destination connected to the
* I2CP router on the local machine on the default port (7654).
@ -30,7 +36,18 @@ public class I2PSocketManagerFactory {
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager() {
return createManager("localhost", 7654, new Properties());
String i2cpHost = System.getProperty(I2PClient.PROP_TCP_HOST, "localhost");
int i2cpPort = 7654;
String i2cpPortStr = System.getProperty(I2PClient.PROP_TCP_PORT);
if (i2cpPortStr != null) {
try {
i2cpPort = Integer.parseInt(i2cpPortStr);
} catch (NumberFormatException nfe) {
// gobble gobble
}
}
return createManager(i2cpHost, i2cpPort, System.getProperties());
}
/**
@ -65,22 +82,76 @@ public class I2PSocketManagerFactory {
public static I2PSocketManager createManager(InputStream myPrivateKeyStream, String i2cpHost, int i2cpPort,
Properties opts) {
I2PClient client = I2PClientFactory.createClient();
opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
opts.setProperty(I2PClient.PROP_TCP_HOST, i2cpHost);
opts.setProperty(I2PClient.PROP_TCP_PORT, "" + i2cpPort);
if (opts == null)
opts = new Properties();
for (Iterator iter = System.getProperties().keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
if (!opts.containsKey(name))
opts.setProperty(name, System.getProperty(name));
}
boolean oldLib = DEFAULT_MANAGER.equals(opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER));
if (oldLib && false) {
// for the old streaming lib
opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
//opts.setProperty("tunnels.depthInbound", "0");
} else {
// for new streaming lib:
opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT);
//p.setProperty("tunnels.depthInbound", "0");
}
if (i2cpHost != null)
opts.setProperty(I2PClient.PROP_TCP_HOST, i2cpHost);
if (i2cpPort > 0)
opts.setProperty(I2PClient.PROP_TCP_PORT, "" + i2cpPort);
try {
I2PSession session = client.createSession(myPrivateKeyStream, opts);
session.connect();
return createManager(session);
I2PSocketManager sockMgr = createManager(session, opts, "manager");
if (sockMgr != null)
sockMgr.setDefaultOptions(sockMgr.buildOptions(opts));
return sockMgr;
} catch (I2PSessionException ise) {
_log.error("Error creating session for socket manager", ise);
return null;
}
}
private static I2PSocketManager createManager(I2PSession session) {
I2PSocketManager mgr = new I2PSocketManager();
mgr.setSession(session);
return mgr;
private static I2PSocketManager createManager(I2PSession session, Properties opts, String name) {
if (false) {
I2PSocketManagerImpl mgr = new I2PSocketManagerImpl();
mgr.setSession(session);
//mgr.setDefaultOptions(new I2PSocketOptions());
return mgr;
} else {
String classname = opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER);
if (classname != null) {
try {
Class cls = Class.forName(classname);
Object obj = cls.newInstance();
if (obj instanceof I2PSocketManager) {
I2PSocketManager mgr = (I2PSocketManager)obj;
I2PAppContext context = I2PAppContext.getGlobalContext();
mgr.init(context, session, opts, name);
return mgr;
} else {
throw new IllegalStateException("Invalid manager class [" + classname + "]");
}
} catch (ClassNotFoundException cnfe) {
_log.error("Error loading " + classname, cnfe);
throw new IllegalStateException("Invalid manager class [" + classname + "] - not found");
} catch (InstantiationException ie) {
_log.error("Error loading " + classname, ie);
throw new IllegalStateException("Invalid manager class [" + classname + "] - unable to instantiate");
} catch (IllegalAccessException iae) {
_log.error("Error loading " + classname, iae);
throw new IllegalStateException("Invalid manager class [" + classname + "] - illegal access");
}
} else {
throw new IllegalStateException("No manager class specified");
}
}
}
}

View File

@ -0,0 +1,787 @@
/*
* licensed under BSD license...
* (if you know the proper clause for that, add it ...)
*/
package net.i2p.client.streaming;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.I2PSessionListener;
import net.i2p.data.Base64;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.Log;
/**
* Centralize the coordination and multiplexing of the local client's streaming.
* There should be one I2PSocketManager for each I2PSession, and if an application
* is sending and receiving data through the streaming library using an
* I2PSocketManager, it should not attempt to call I2PSession's setSessionListener
* or receive any messages with its .receiveMessage
*
*/
class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener {
private I2PAppContext _context;
private Log _log;
private I2PSession _session;
private I2PServerSocketImpl _serverSocket = null;
private Object lock = new Object(); // for locking socket lists
private HashMap _outSockets;
private HashMap _inSockets;
private I2PSocketOptions _defaultOptions;
private long _acceptTimeout;
private String _name;
private List _listeners;
private static int __managerId = 0;
public static final short ACK = 0x51;
public static final short CLOSE_OUT = 0x52;
public static final short DATA_OUT = 0x50;
public static final short SYN = 0xA1;
public static final short CLOSE_IN = 0xA2;
public static final short DATA_IN = 0xA0;
public static final short CHAFF = 0xFF;
/**
* How long to wait for the client app to accept() before sending back CLOSE?
* This includes the time waiting in the queue. Currently set to 5 seconds.
*/
private static final long ACCEPT_TIMEOUT_DEFAULT = 5*1000;
public I2PSocketManagerImpl() {
this("SocketManager " + (++__managerId));
}
public I2PSocketManagerImpl(String name) {
init(I2PAppContext.getGlobalContext(), null, null, name);
}
public void init(I2PAppContext context, I2PSession session, Properties opts, String name) {
_name = name;
_context = context;
_log = _context.logManager().getLog(I2PSocketManager.class);
_inSockets = new HashMap(16);
_outSockets = new HashMap(16);
_acceptTimeout = ACCEPT_TIMEOUT_DEFAULT;
_listeners = new ArrayList(1);
setSession(session);
setDefaultOptions(buildOptions(opts));
_context.statManager().createRateStat("streaming.lifetime", "How long before the socket is closed?", "streaming", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("streaming.sent", "How many bytes are sent in the stream?", "streaming", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("streaming.received", "How many bytes are received in the stream?", "streaming", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("streaming.transferBalance", "How many streams send more than they receive (positive means more sent, negative means more received)?", "streaming", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("streaming.synNoAck", "How many times have we sent a SYN but not received an ACK?", "streaming", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("streaming.ackSendFailed", "How many times have we tried to send an ACK to a SYN and failed?", "streaming", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("streaming.nackSent", "How many times have we refused a SYN with a NACK?", "streaming", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("streaming.nackReceived", "How many times have we received a NACK to our SYN?", "streaming", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
}
public I2PSession getSession() {
return _session;
}
public void setSession(I2PSession session) {
_session = session;
if (session != null) session.setSessionListener(this);
}
/**
* How long should we wait for the client to .accept() a socket before
* sending back a NACK/Close?
*
* @param ms milliseconds to wait, maximum
*/
public void setAcceptTimeout(long ms) { _acceptTimeout = ms; }
public long getAcceptTimeout() { return _acceptTimeout; }
public void disconnected(I2PSession session) {
_log.info(getName() + ": Disconnected from the session");
destroySocketManager();
List listeners = null;
synchronized (_listeners) {
listeners = new ArrayList(_listeners);
_listeners.clear();
}
for (int i = 0; i < listeners.size(); i++) {
I2PSocketManager.DisconnectListener lsnr = (I2PSocketManager.DisconnectListener)listeners.get(i);
lsnr.sessionDisconnected();
}
}
public void errorOccurred(I2PSession session, String message, Throwable error) {
_log.error(getName() + ": Error occurred: [" + message + "]", error);
}
public void messageAvailable(I2PSession session, int msgId, long size) {
try {
I2PSocketImpl s;
byte msg[] = session.receiveMessage(msgId);
if (msg.length == 1 && msg[0] == -1) {
_log.debug(getName() + ": Ping received");
return;
}
if (msg.length < 4) {
_log.warn(getName() + ": ==== packet too short ====");
return;
}
int type = msg[0] & 0xff;
String id = toString(new byte[] { msg[1], msg[2], msg[3]});
byte[] payload = new byte[msg.length - 4];
System.arraycopy(msg, 4, payload, 0, payload.length);
if (_log.shouldLog(Log.DEBUG))
_log.debug(getName() + ": Message read: type = [" + Integer.toHexString(type)
+ "] id = [" + getReadableForm(id)
+ "] payload length: [" + payload.length + "]");
switch (type) {
case ACK:
ackAvailable(id, payload);
return;
case CLOSE_OUT:
disconnectAvailable(id, payload);
return;
case DATA_OUT:
sendOutgoingAvailable(id, payload);
return;
case SYN:
synIncomingAvailable(id, payload, session);
return;
case CLOSE_IN:
disconnectIncoming(id, payload);
return;
case DATA_IN:
sendIncoming(id, payload);
case CHAFF:
// ignore
return;
default:
handleUnknown(type, id, payload);
return;
}
} catch (I2PException ise) {
_log.warn(getName() + ": Error processing", ise);
} catch (IllegalStateException ise) {
_log.debug(getName() + ": Error processing", ise);
}
}
/**
* We've received an ACK packet (hopefully, in response to a SYN that we
* recently sent out). Notify the associated I2PSocket that we now have
* the remote stream ID (which should get things going, since the handshake
* is complete).
*
*/
private void ackAvailable(String id, byte payload[]) {
long begin = _context.clock().now();
I2PSocketImpl s = null;
synchronized (lock) {
s = (I2PSocketImpl) _outSockets.get(id);
}
if (s == null) {
_log.warn(getName() + ": No socket responsible for ACK packet for id " + getReadableForm(id));
return;
}
long socketRetrieved = _context.clock().now();
String remoteId = null;
remoteId = s.getRemoteID(false);
if ( (payload.length == 3) && (remoteId == null) ) {
String newID = toString(payload);
long beforeSetRemId = _context.clock().now();
s.setRemoteID(newID);
if (_log.shouldLog(Log.DEBUG)) {
_log.debug(getName() + ": ackAvailable - socket retrieval took "
+ (socketRetrieved-begin) + "ms, getRemoteId took "
+ (beforeSetRemId-socketRetrieved) + "ms, setRemoteId took "
+ (_context.clock().now()-beforeSetRemId) + "ms");
}
return;
} else {
// (payload.length != 3 || getRemoteId != null)
if (_log.shouldLog(Log.WARN)) {
if (payload.length != 3)
_log.warn(getName() + ": Ack packet had " + payload.length + " bytes");
else
_log.warn(getName() + ": Remote ID already exists? " + remoteId);
}
if (_log.shouldLog(Log.DEBUG)) {
_log.debug(getName() + ": invalid ack - socket retrieval took "
+ (socketRetrieved-begin) + "ms, overall took "
+ (_context.clock().now()-begin) + "ms");
}
return;
}
}
/**
* We received a disconnect packet, telling us to tear down the specified
* stream.
*/
private void disconnectAvailable(String id, byte payload[]) {
I2PSocketImpl s = null;
synchronized (lock) {
s = (I2PSocketImpl) _outSockets.get(id);
}
_log.debug(getName() + ": *Disconnect outgoing for socket " + s + " on id "
+ getReadableForm(id));
try {
if (s != null) {
if (payload.length > 0) {
_log.debug(getName() + ": Disconnect packet had "
+ payload.length + " bytes");
}
if (s.getRemoteID(false) == null) {
s.setRemoteID(null); // Just to wake up socket
return;
}
s.internalClose();
synchronized (lock) {
_outSockets.remove(id);
}
}
return;
} catch (Exception t) {
_log.warn(getName() + ": Ignoring error on disconnect for socket " + s, t);
}
}
/**
* We've received data on a stream we created - toss the data onto
* the socket for handling.
*
* @throws IllegalStateException if the socket isn't open or isn't known
*/
private void sendOutgoingAvailable(String id, byte payload[]) throws IllegalStateException {
I2PSocketImpl s = null;
synchronized (lock) {
s = (I2PSocketImpl) _outSockets.get(id);
}
// packet send outgoing
if (_log.shouldLog(Log.DEBUG))
_log.debug(getName() + ": *Packet send outgoing [" + payload.length + "] for socket "
+ s + " on id " + getReadableForm(id));
if (s != null) {
s.queueData(payload);
return;
} else {
if (_log.shouldLog(Log.WARN))
_log.warn(getName() + ": Null socket with data available");
throw new IllegalStateException("Null socket with data available");
}
}
/**
* We've received a SYN packet (a request for a new stream). If the client has
* said they want incoming sockets (by retrieving the serverSocket), the stream
* will be ACKed, but if they have not, they'll be NACKed)
*
* @throws DataFormatException if the destination in the SYN was invalid
* @throws I2PSessionException if there was an I2P error sending the ACK or NACK
*/
private void synIncomingAvailable(String id, byte payload[], I2PSession session)
throws DataFormatException, I2PSessionException {
Destination d = new Destination();
d.fromByteArray(payload);
I2PSocketImpl s = null;
boolean acceptConnections = (_serverSocket != null);
String newLocalID = null;
synchronized (lock) {
newLocalID = makeID(_inSockets);
if (acceptConnections) {
s = new I2PSocketImpl(d, this, false, newLocalID);
s.setRemoteID(id);
}
}
_log.debug(getName() + ": *Syn! for socket " + s + " on id " + getReadableForm(newLocalID)
+ " from " + d.calculateHash().toBase64().substring(0,6));
if (!acceptConnections) {
// The app did not instantiate an I2PServerSocket
byte[] packet = makePacket((byte) CLOSE_OUT, id, toBytes(newLocalID));
boolean replySentOk = false;
synchronized (_session) {
replySentOk = _session.sendMessage(d, packet);
}
if (!replySentOk) {
_log.warn(getName() + ": Error sending close to " + d.calculateHash().toBase64()
+ " in response to a new con message",
new Exception("Failed creation"));
}
_context.statManager().addRateData("streaming.nackSent", 1, 1);
return;
}
if (_serverSocket.addWaitForAccept(s, _acceptTimeout)) {
_inSockets.put(newLocalID, s);
byte[] packet = makePacket((byte) ACK, id, toBytes(newLocalID));
boolean replySentOk = false;
replySentOk = _session.sendMessage(d, packet);
if (!replySentOk) {
if (_log.shouldLog(Log.WARN))
_log.warn(getName() + ": Error sending reply to " + d.calculateHash().toBase64()
+ " in response to a new con message for socket " + s,
new Exception("Failed creation"));
s.internalClose();
_context.statManager().addRateData("streaming.ackSendFailed", 1, 1);
}
} else {
// timed out or serverSocket closed
byte[] packet = toBytes(" " + id);
packet[0] = CLOSE_OUT;
boolean nackSent = session.sendMessage(d, packet);
if (!nackSent) {
_log.warn(getName() + ": Error sending NACK for session creation for socket " + s);
}
s.internalClose();
_context.statManager().addRateData("streaming,nackSent", 1, 1);
}
return;
}
/**
* We've received a disconnect for a socket we didn't initiate, so kill
* the socket.
*
*/
private void disconnectIncoming(String id, byte payload[]) {
I2PSocketImpl s = null;
synchronized (lock) {
s = (I2PSocketImpl) _inSockets.get(id);
if (payload.length == 0 && s != null) {
_inSockets.remove(id);
}
}
_log.debug(getName() + ": *Disconnect incoming for socket " + s);
try {
if (payload.length == 0 && s != null) {
s.internalClose();
return;
} else {
if ( (payload.length > 0) && (_log.shouldLog(Log.ERROR)) )
_log.warn(getName() + ": Disconnect packet had " + payload.length + " bytes");
if (s != null)
s.internalClose();
return;
}
} catch (Exception t) {
_log.warn(getName() + ": Ignoring error on disconnect", t);
return;
}
}
/**
* We've received data on a stream we received - toss the data onto
* the socket for handling.
*
* @throws IllegalStateException if the socket isn't open or isn't known
*/
private void sendIncoming(String id, byte payload[]) throws IllegalStateException {
I2PSocketImpl s = null;
synchronized (lock) {
s = (I2PSocketImpl) _inSockets.get(id);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getName() + ": *Packet send incoming [" + payload.length + "] for socket " + s);
if (s != null) {
s.queueData(payload);
return;
} else {
_log.info(getName() + ": Null socket with data available");
throw new IllegalStateException("Null socket with data available");
}
}
/**
* Unknown packet. moo.
*
*/
private void handleUnknown(int type, String id, byte payload[]) {
_log.error(getName() + ": \n\n=============== Unknown packet! " + "============"
+ "\nType: " + (int) type
+ "\nID: " + getReadableForm(id)
+ "\nBase64'ed Data: " + Base64.encode(payload)
+ "\n\n\n");
if (id != null) {
synchronized (lock) {
_inSockets.remove(id);
_outSockets.remove(id);
}
}
}
public void reportAbuse(I2PSession session, int severity) {
_log.error(getName() + ": Abuse reported [" + severity + "]");
}
public void setDefaultOptions(I2PSocketOptions options) {
_defaultOptions = options;
}
public I2PSocketOptions getDefaultOptions() {
return _defaultOptions;
}
public I2PSocketOptions buildOptions() { return buildOptions(null); }
public I2PSocketOptions buildOptions(Properties opts) {
return new I2PSocketOptionsImpl(opts);
}
public I2PServerSocket getServerSocket() {
if (_serverSocket == null) {
_serverSocket = new I2PServerSocketImpl(this);
}
return _serverSocket;
}
/**
* Create a new connected socket (block until the socket is created)
*
* @param peer Destination to connect to
* @param options I2P socket options to be used for connecting
*
* @throws ConnectException if the peer refuses the connection
* @throws NoRouteToHostException if the peer is not found or not reachable
* @throws InterruptedIOException if the connection timeouts
* @throws I2PException if there is some other I2P-related problem
*/
public I2PSocket connect(Destination peer, I2PSocketOptions options)
throws I2PException, ConnectException,
NoRouteToHostException, InterruptedIOException {
String localID, lcID;
I2PSocketImpl s;
synchronized (lock) {
localID = makeID(_outSockets);
lcID = getReadableForm(localID);
s = new I2PSocketImpl(peer, this, true, localID);
_outSockets.put(localID, s);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getName() + ": connect(" + peer.calculateHash().toBase64().substring(0,6)
+ ", ...): localID = " + lcID);
try {
ByteArrayOutputStream pubkey = new ByteArrayOutputStream();
_session.getMyDestination().writeBytes(pubkey);
String remoteID;
byte[] packet = makePacket((byte) SYN, localID, pubkey.toByteArray());
boolean sent = false;
sent = _session.sendMessage(peer, packet);
if (!sent) {
_log.info(getName() + ": Unable to send & receive ack for SYN packet for socket "
+ s + " with localID = " + lcID);
synchronized (lock) {
_outSockets.remove(s.getLocalID());
}
_context.statManager().addRateData("streaming.synNoAck", 1, 1);
throw new I2PException("Error sending through I2P network");
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getName() + ": syn sent ok to "
+ peer.calculateHash().toBase64().substring(0,6)
+ " with localID = " + lcID);
}
if (options != null)
remoteID = s.getRemoteID(true, options.getConnectTimeout());
else
remoteID = s.getRemoteID(true, getDefaultOptions().getConnectTimeout());
if (_log.shouldLog(Log.DEBUG))
_log.debug(getName() + ": remoteID received from "
+ peer.calculateHash().toBase64().substring(0,6)
+ ": " + getReadableForm(remoteID)
+ " with localID = " + lcID);
if (remoteID == null) {
_context.statManager().addRateData("streaming.nackReceived", 1, 1);
throw new ConnectException("Connection refused by peer for socket " + s);
}
if ("".equals(remoteID)) {
_context.statManager().addRateData("streaming.synNoAck", 1, 1);
throw new NoRouteToHostException("Unable to reach peer for socket " + s);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getName() + ": TIMING: s given out for remoteID "
+ getReadableForm(remoteID) + " for socket " + s);
return s;
} catch (InterruptedIOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn(getName() + ": Timeout waiting for ack from syn for id "
+ lcID + " to " + peer.calculateHash().toBase64().substring(0,6)
+ " for socket " + s, ioe);
synchronized (lock) {
_outSockets.remove(s.getLocalID());
}
s.internalClose();
_context.statManager().addRateData("streaming.synNoAck", 1, 1);
throw new InterruptedIOException("Timeout waiting for ack");
} catch (ConnectException ex) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getName() + ": Connection error waiting for ack from syn for id "
+ lcID + " to " + peer.calculateHash().toBase64().substring(0,6)
+ " for socket " + s, ex);
s.internalClose();
throw ex;
} catch (NoRouteToHostException ex) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getName() + ": No route to host waiting for ack from syn for id "
+ lcID + " to " + peer.calculateHash().toBase64().substring(0,6)
+ " for socket " + s, ex);
s.internalClose();
throw ex;
} catch (IOException ex) {
if (_log.shouldLog(Log.WARN))
_log.warn(getName() + ": Error sending syn on id "
+ lcID + " to " + peer.calculateHash().toBase64().substring(0,6)
+ " for socket " + s, ex);
synchronized (lock) {
_outSockets.remove(s.getLocalID());
}
s.internalClose();
throw new I2PException("Unhandled IOException occurred");
} catch (I2PException ex) {
if (_log.shouldLog(Log.INFO))
_log.info(getName() + ": Error sending syn on id "
+ lcID + " to " + peer.calculateHash().toBase64().substring(0,6)
+ " for socket " + s, ex);
synchronized (lock) {
_outSockets.remove(s.getLocalID());
}
s.internalClose();
throw ex;
} catch (Exception e) {
s.internalClose();
_log.warn(getName() + ": Unhandled error connecting on "
+ lcID + " to " + peer.calculateHash().toBase64().substring(0,6)
+ " for socket " + s, e);
throw new ConnectException("Unhandled error connecting: " + e.getMessage());
}
}
/**
* Create a new connected socket (block until the socket is created)
*
* @param peer Destination to connect to
*
* @throws ConnectException if the peer refuses the connection
* @throws NoRouteToHostException if the peer is not found or not reachable
* @throws InterruptedIOException if the connection timeouts
* @throws I2PException if there is some other I2P-related problem
*/
public I2PSocket connect(Destination peer) throws I2PException, ConnectException,
NoRouteToHostException, InterruptedIOException {
return connect(peer, null);
}
/**
* Destroy the socket manager, freeing all the associated resources. This
* method will block untill all the managed sockets are closed.
*
*/
public void destroySocketManager() {
if (_serverSocket != null) {
_serverSocket.close();
_serverSocket = null;
}
synchronized (lock) {
Iterator iter;
String id = null;
I2PSocketImpl sock;
iter = _inSockets.keySet().iterator();
while (iter.hasNext()) {
id = (String)iter.next();
sock = (I2PSocketImpl)_inSockets.get(id);
if (_log.shouldLog(Log.DEBUG))
_log.debug(getName() + ": Closing inSocket \""
+ getReadableForm(sock.getLocalID()) + "\"");
sock.internalClose();
}
iter = _outSockets.keySet().iterator();
while (iter.hasNext()) {
id = (String)iter.next();
sock = (I2PSocketImpl)_outSockets.get(id);
if (_log.shouldLog(Log.DEBUG))
_log.debug(getName() + ": Closing outSocket \""
+ getReadableForm(sock.getLocalID()) + "\"");
sock.internalClose();
}
}
_log.debug(getName() + ": Waiting for all open sockets to really close...");
synchronized (lock) {
while ((_inSockets.size() != 0) || (_outSockets.size() != 0)) {
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
try {
_log.debug(getName() + ": Destroying I2P session...");
_session.destroySession();
_log.debug(getName() + ": I2P session destroyed");
} catch (I2PSessionException e) {
_log.warn(getName() + ": Error destroying I2P session", e);
}
}
/**
* Retrieve a set of currently connected I2PSockets, either initiated locally or remotely.
*
*/
public Set listSockets() {
Set sockets = new HashSet(8);
synchronized (lock) {
sockets.addAll(_inSockets.values());
sockets.addAll(_outSockets.values());
}
return sockets;
}
/**
* Ping the specified peer, returning true if they replied to the ping within
* the timeout specified, false otherwise. This call blocks.
*
*/
public boolean ping(Destination peer, long timeoutMs) {
try {
return _session.sendMessage(peer, new byte[] { (byte) CHAFF});
} catch (I2PException ex) {
_log.warn(getName() + ": I2PException:", ex);
return false;
}
}
public void removeSocket(I2PSocketImpl sock) {
String localId = sock.getLocalID();
boolean removed = false;
synchronized (lock) {
removed = (null != _inSockets.remove(localId));
removed = removed || (null != _outSockets.remove(localId));
lock.notify();
}
long now = _context.clock().now();
long lifetime = now - sock.getCreatedOn();
long timeSinceClose = now - sock.getClosedOn();
long sent = sock.getBytesSent();
long recv = sock.getBytesReceived();
if (_log.shouldLog(Log.DEBUG)) {
_log.debug(getName() + ": Removing socket \"" + getReadableForm(localId) + "\" [" + sock
+ ", send: " + sent + ", recv: " + recv
+ ", lifetime: " + lifetime + "ms, time since close: " + timeSinceClose
+ " removed? " + removed + ")]",
new Exception("removeSocket called"));
}
_context.statManager().addRateData("streaming.lifetime", lifetime, lifetime);
_context.statManager().addRateData("streaming.sent", sent, lifetime);
_context.statManager().addRateData("streaming.received", recv, lifetime);
if (sent > recv) {
_context.statManager().addRateData("streaming.transferBalance", 1, lifetime);
} else if (recv > sent) {
_context.statManager().addRateData("streaming.transferBalance", -1, lifetime);
} else {
// noop
}
}
public String getName() { return _name; }
public void setName(String name) { _name = name; }
public void addDisconnectListener(I2PSocketManager.DisconnectListener lsnr) {
synchronized (_listeners) {
_listeners.add(lsnr);
}
}
public void removeDisconnectListener(I2PSocketManager.DisconnectListener lsnr) {
synchronized (_listeners) {
_listeners.remove(lsnr);
}
}
public static String getReadableForm(String id) {
if (id == null) return "(null)";
if (id.length() != 3) return "Bogus";
return Base64.encode(toBytes(id));
}
/**
* Create a new part the connection ID that is locally unique
*
* @param uniqueIn map of already known local IDs so we don't collide. WARNING - NOT THREADSAFE!
*/
private static String makeID(HashMap uniqueIn) {
String newID;
do {
int id = (int) (Math.random() * 16777215 + 1);
byte[] nid = new byte[3];
nid[0] = (byte) (id / 65536);
nid[1] = (byte) ((id / 256) % 256);
nid[2] = (byte) (id % 256);
newID = toString(nid);
} while (uniqueIn.get(newID) != null);
return newID;
}
/**
* Create a new packet of the given type for the specified connection containing
* the given payload
*/
public static byte[] makePacket(byte type, String id, byte[] payload) {
byte[] packet = new byte[payload.length + 4];
packet[0] = type;
byte[] temp = toBytes(id);
if (temp.length != 3) throw new RuntimeException("Incorrect ID length: " + temp.length);
System.arraycopy(temp, 0, packet, 1, 3);
System.arraycopy(payload, 0, packet, 4, payload.length);
return packet;
}
private static final String toString(byte data[]) {
try {
return new String(data, "ISO-8859-1");
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("WTF! iso-8859-1 isn't supported?");
}
}
private static final byte[] toBytes(String str) {
try {
return str.getBytes("ISO-8859-1");
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("WTF! iso-8859-1 isn't supported?");
}
}
}

View File

@ -4,27 +4,71 @@ package net.i2p.client.streaming;
* Define the configuration for streaming and verifying data on the socket.
*
*/
public class I2PSocketOptions {
private long _connectTimeout;
public I2PSocketOptions() {
_connectTimeout = -1;
}
public interface I2PSocketOptions {
public static final String PROP_BUFFER_SIZE = "i2p.streaming.bufferSize";
public static final String PROP_CONNECT_TIMEOUT = "i2p.streaming.connectTimeout";
public static final String PROP_READ_TIMEOUT = "i2p.streaming.readTimeout";
public static final String PROP_WRITE_TIMEOUT = "i2p.streaming.writeTimeout";
/**
* How long we will wait for the ACK from a SYN, in milliseconds.
*
* @return milliseconds to wait, or -1 if we will wait indefinitely
*/
public long getConnectTimeout() {
return _connectTimeout;
}
public long getConnectTimeout();
/**
* Define how long we will wait for the ACK from a SYN, in milliseconds.
*
*/
public void setConnectTimeout(long ms) {
_connectTimeout = ms;
}
public void setConnectTimeout(long ms);
/**
* What is the longest we'll block on the input stream while waiting
* for more data. If this value is exceeded, the read() throws
* InterruptedIOException
*/
public long getReadTimeout();
/**
* What is the longest we'll block on the input stream while waiting
* for more data. If this value is exceeded, the read() throws
* InterruptedIOException
*/
public void setReadTimeout(long ms);
/**
* How much data will we accept that hasn't been written out yet. After
* this amount has been exceeded, subsequent .write calls will block until
* either some data is removed or the connection is closed. If this is
* less than or equal to zero, there is no limit (warning: can eat ram)
*
* @return buffer size limit, in bytes
*/
public int getMaxBufferSize();
/**
* How much data will we accept that hasn't been written out yet. After
* this amount has been exceeded, subsequent .write calls will block until
* either some data is removed or the connection is closed. If this is
* less than or equal to zero, there is no limit (warning: can eat ram)
*
*/
public void setMaxBufferSize(int numBytes);
/**
* What is the longest we'll block on the output stream while waiting
* for the data to flush. If this value is exceeded, the write() throws
* InterruptedIOException. If this is less than or equal to zero, there
* is no timeout.
*/
public long getWriteTimeout();
/**
* What is the longest we'll block on the output stream while waiting
* for the data to flush. If this value is exceeded, the write() throws
* InterruptedIOException. If this is less than or equal to zero, there
* is no timeout.
*/
public void setWriteTimeout(long ms);
}

View File

@ -0,0 +1,147 @@
package net.i2p.client.streaming;
import java.util.Properties;
/**
* Define the configuration for streaming and verifying data on the socket.
*
*/
class I2PSocketOptionsImpl implements I2PSocketOptions {
private long _connectTimeout;
private long _readTimeout;
private long _writeTimeout;
private int _maxBufferSize;
public static final int DEFAULT_BUFFER_SIZE = 1024*64;
public static final int DEFAULT_WRITE_TIMEOUT = 60*1000;
public static final int DEFAULT_CONNECT_TIMEOUT = 60*1000;
public I2PSocketOptionsImpl() {
this(System.getProperties());
}
public I2PSocketOptionsImpl(I2PSocketOptions opts) {
this(System.getProperties());
if (opts != null) {
_connectTimeout = opts.getConnectTimeout();
_readTimeout = opts.getReadTimeout();
_writeTimeout = opts.getWriteTimeout();
_maxBufferSize = opts.getMaxBufferSize();
}
}
public I2PSocketOptionsImpl(Properties opts) {
init(opts);
}
public void setProperties(Properties opts) {
if (opts == null) return;
if (opts.containsKey(PROP_BUFFER_SIZE))
_maxBufferSize = getInt(opts, PROP_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);
if (opts.containsKey(PROP_CONNECT_TIMEOUT))
_connectTimeout = getInt(opts, PROP_CONNECT_TIMEOUT, DEFAULT_CONNECT_TIMEOUT);
if (opts.containsKey(PROP_READ_TIMEOUT))
_readTimeout = getInt(opts, PROP_READ_TIMEOUT, -1);
if (opts.containsKey(PROP_WRITE_TIMEOUT))
_writeTimeout = getInt(opts, PROP_WRITE_TIMEOUT, DEFAULT_WRITE_TIMEOUT);
}
protected void init(Properties opts) {
_maxBufferSize = getInt(opts, PROP_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);
_connectTimeout = getInt(opts, PROP_CONNECT_TIMEOUT, DEFAULT_CONNECT_TIMEOUT);
_readTimeout = getInt(opts, PROP_READ_TIMEOUT, -1);
_writeTimeout = getInt(opts, PROP_WRITE_TIMEOUT, DEFAULT_WRITE_TIMEOUT);
}
protected int getInt(Properties opts, String name, int defaultVal) {
if (opts == null) return defaultVal;
String val = opts.getProperty(name);
if (val == null) {
return defaultVal;
} else {
try {
return Integer.parseInt(val);
} catch (NumberFormatException nfe) {
return defaultVal;
}
}
}
/**
* How long we will wait for the ACK from a SYN, in milliseconds.
*
* @return milliseconds to wait, or -1 if we will wait indefinitely
*/
public long getConnectTimeout() {
return _connectTimeout;
}
/**
* Define how long we will wait for the ACK from a SYN, in milliseconds.
*
*/
public void setConnectTimeout(long ms) {
_connectTimeout = ms;
}
/**
* What is the longest we'll block on the input stream while waiting
* for more data. If this value is exceeded, the read() throws
* InterruptedIOException
*/
public long getReadTimeout() {
return _readTimeout;
}
/**
* What is the longest we'll block on the input stream while waiting
* for more data. If this value is exceeded, the read() throws
* InterruptedIOException
*/
public void setReadTimeout(long ms) {
_readTimeout = ms;
}
/**
* How much data will we accept that hasn't been written out yet. After
* this amount has been exceeded, subsequent .write calls will block until
* either some data is removed or the connection is closed. If this is
* less than or equal to zero, there is no limit (warning: can eat ram)
*
* @return buffer size limit, in bytes
*/
public int getMaxBufferSize() {
return _maxBufferSize;
}
/**
* How much data will we accept that hasn't been written out yet. After
* this amount has been exceeded, subsequent .write calls will block until
* either some data is removed or the connection is closed. If this is
* less than or equal to zero, there is no limit (warning: can eat ram)
*
*/
public void setMaxBufferSize(int numBytes) {
_maxBufferSize = numBytes;
}
/**
* What is the longest we'll block on the output stream while waiting
* for the data to flush. If this value is exceeded, the write() throws
* InterruptedIOException. If this is less than or equal to zero, there
* is no timeout.
*/
public long getWriteTimeout() {
return _writeTimeout;
}
/**
* What is the longest we'll block on the output stream while waiting
* for the data to flush. If this value is exceeded, the write() throws
* InterruptedIOException. If this is less than or equal to zero, there
* is no timeout.
*/
public void setWriteTimeout(long ms) {
_writeTimeout = ms;
}
}

View File

@ -0,0 +1,162 @@
package net.i2p.client.streaming;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.util.Properties;
import java.util.Random;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.data.Destination;
import net.i2p.data.DataFormatException;
import net.i2p.util.Log;
/**
* Simple streaming lib test app that connects to a given destination and sends
* it a particular amount of random data, then disconnects. See the {@link #main}
*
*/
public class StreamSinkClient {
private Log _log;
private int _sendSize;
private int _writeDelay;
private String _peerDestFile;
private String _i2cpHost;
private int _i2cpPort;
/**
* Build the client but don't fire it up.
* @param sendSize how many KB to send
* @param writeDelayMs how long to wait between each .write (0 for no delay)
* @param serverDestFile file containing the StreamSinkServer's binary Destination
*/
public StreamSinkClient(int sendSize, int writeDelayMs, String serverDestFile) {
this(null, -1, sendSize, writeDelayMs, serverDestFile);
}
public StreamSinkClient(String i2cpHost, int i2cpPort, int sendSize, int writeDelayMs, String serverDestFile) {
_i2cpHost = i2cpHost;
_i2cpPort = i2cpPort;
_sendSize = sendSize;
_writeDelay = writeDelayMs;
_peerDestFile = serverDestFile;
_log = I2PAppContext.getGlobalContext().logManager().getLog(StreamSinkClient.class);
}
/**
* Actually connect and run the client - this call blocks until completion.
*
*/
public void runClient() {
I2PSocketManager mgr = null;
if (_i2cpHost != null)
mgr = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, new Properties());
else
mgr = I2PSocketManagerFactory.createManager();
Destination peer = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(_peerDestFile);
peer = new Destination();
peer.readBytes(fis);
} catch (IOException ioe) {
_log.error("Error finding the peer destination to contact in " + _peerDestFile, ioe);
return;
} catch (DataFormatException dfe) {
_log.error("Peer destination is not valid in " + _peerDestFile, dfe);
return;
} 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("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;
}
}
/**
* Fire up the client. <code>Usage: StreamSinkClient [i2cpHost i2cpPort] sendSizeKB writeDelayMs serverDestFile</code> <br />
* <ul>
* <li><b>sendSizeKB</b>: how many KB to send</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>
* </ul>
*/
public static void main(String args[]) {
StreamSinkClient client = null;
int sendSizeKB = -1;
int writeDelayMs = -1;
switch (args.length) {
case 3:
try {
sendSizeKB = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
System.err.println("Send size invalid [" + args[0] + "]");
return;
}
try {
writeDelayMs = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
System.err.println("Write delay ms invalid [" + args[1] + "]");
return;
}
client = new StreamSinkClient(sendSizeKB, writeDelayMs, args[2]);
break;
case 5:
try {
int port = Integer.parseInt(args[1]);
sendSizeKB = Integer.parseInt(args[2]);
writeDelayMs = Integer.parseInt(args[3]);
client = new StreamSinkClient(args[0], port, sendSizeKB, writeDelayMs, args[4]);
} catch (NumberFormatException nfe) {
System.err.println("arg error");
}
break;
default:
System.out.println("Usage: StreamSinkClient [i2cpHost i2cpPort] sendSizeKB writeDelayMs serverDestFile");
}
if (client != null)
client.runClient();
}
}

View File

@ -0,0 +1,130 @@
package net.i2p.client.streaming;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.data.Destination;
import net.i2p.data.DataFormatException;
import net.i2p.util.Log;
/**
* Simple streaming lib test app that connects to a given destination and sends
* the contents of a file, then disconnects. See the {@link #main}
*
*/
public class StreamSinkSend {
private Log _log;
private String _sendFile;
private int _writeDelay;
private String _peerDestFile;
/**
* Build the client but don't fire it up.
* @param filename file to send
* @param writeDelayMs how long to wait between each .write (0 for no delay)
* @param serverDestFile file containing the StreamSinkServer's binary Destination
*/
public StreamSinkSend(String filename, int writeDelayMs, String serverDestFile) {
_sendFile = filename;
_writeDelay = writeDelayMs;
_peerDestFile = serverDestFile;
_log = I2PAppContext.getGlobalContext().logManager().getLog(StreamSinkClient.class);
}
/**
* Actually connect and run the client - this call blocks until completion.
*
*/
public void runClient() {
I2PSocketManager mgr = I2PSocketManagerFactory.createManager();
Destination peer = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(_peerDestFile);
peer = new Destination();
peer.readBytes(fis);
} catch (IOException ioe) {
_log.error("Error finding the peer destination to contact in " + _peerDestFile, ioe);
return;
} catch (DataFormatException dfe) {
_log.error("Peer destination is not valid in " + _peerDestFile, dfe);
return;
} finally {
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
}
System.out.println("Send " + _sendFile + " to " + peer.calculateHash().toBase64());
try {
I2PSocket sock = mgr.connect(peer);
byte buf[] = new byte[32*1024];
OutputStream out = sock.getOutputStream();
long beforeSending = System.currentTimeMillis();
fis = new FileInputStream(_sendFile);
long size = 0;
while (true) {
int read = fis.read(buf);
if (read < 0)
break;
out.write(buf, 0, read);
size += read;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Wrote " + read);
if (_writeDelay > 0) {
try { Thread.sleep(_writeDelay); } catch (InterruptedException ie) {}
}
}
fis.close();
sock.close();
long afterSending = System.currentTimeMillis();
System.out.println("Sent " + (size / 1024) + "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 sendFile writeDelayMs serverDestFile</code> <br />
* <ul>
* <li><b>sendFile</b>: filename to send</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>
* </ul>
*/
public static void main(String args[]) {
if (args.length != 3) {
System.out.println("Usage: StreamSinkClient sendFile writeDelayMs serverDestFile");
} else {
int writeDelayMs = -1;
try {
writeDelayMs = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
System.err.println("Write delay ms invalid [" + args[1] + "]");
return;
}
StreamSinkSend client = new StreamSinkSend(args[0], writeDelayMs, args[2]);
client.runClient();
}
}
}

View File

@ -0,0 +1,173 @@
package net.i2p.client.streaming;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Listen to a destination, receiving any sockets and writing anything they
* send to a new file. See the {@link #main}
*
*/
public class StreamSinkServer {
private Log _log;
private String _sinkDir;
private String _destFile;
private String _i2cpHost;
private int _i2cpPort;
/**
* Create but do not start the streaming server.
*
* @param sinkDir Directory to store received files in
* @param ourDestFile filename to write our binary destination to
*/
public StreamSinkServer(String sinkDir, String ourDestFile) {
this(sinkDir, ourDestFile, null, -1);
}
public StreamSinkServer(String sinkDir, String ourDestFile, String i2cpHost, int i2cpPort) {
_sinkDir = sinkDir;
_destFile = ourDestFile;
_i2cpHost = i2cpHost;
_i2cpPort = i2cpPort;
_log = I2PAppContext.getGlobalContext().logManager().getLog(StreamSinkServer.class);
}
/**
* Actually fire up the server - this call blocks forever (or until the server
* socket closes)
*
*/
public void runServer() {
I2PSocketManager mgr = null;
if (_i2cpHost != null)
mgr = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, new Properties());
else
mgr = I2PSocketManagerFactory.createManager();
Destination dest = mgr.getSession().getMyDestination();
System.out.println("Listening for connections on: " + dest.calculateHash().toBase64());
FileOutputStream fos = null;
try {
fos = new FileOutputStream(_destFile);
dest.writeBytes(fos);
} catch (IOException ioe) {
_log.error("Error writing out our destination to " + _destFile, ioe);
return;
} catch (DataFormatException dfe) {
_log.error("Error formatting the destination", dfe);
return;
} finally {
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
}
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;
}
}
}
private void handle(I2PSocket socket) {
I2PThread t = new I2PThread(new ClientRunner(socket));
t.setName("Handle " + socket.getPeerDestination().calculateHash().toBase64().substring(0,4));
t.start();
}
/**
* Actually deal with a client - pull anything they send us and write it to a file.
*
*/
private class ClientRunner implements Runnable {
private I2PSocket _sock;
private FileOutputStream _fos;
public ClientRunner(I2PSocket socket) {
_sock = socket;
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());
} catch (IOException ioe) {
_log.error("Error creating sink", ioe);
_fos = null;
}
}
public void run() {
if (_fos == null) return;
long start = System.currentTimeMillis();
try {
InputStream in = _sock.getInputStream();
byte buf[] = new byte[4096];
long written = 0;
int read = 0;
while ( (read = in.read(buf)) != -1) {
//_fos.write(buf, 0, read);
written += read;
if (_log.shouldLog(Log.DEBUG))
_log.debug("read and wrote " + read);
}
_fos.write(("written: [" + written + "]\n").getBytes());
long lifetime = System.currentTimeMillis() - start;
_log.error("Got EOF from client socket [written=" + written + " lifetime=" + lifetime + "]");
} catch (IOException ioe) {
_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");
}
}
}
/**
* Fire up the streaming server. <code>Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile</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>
* </ul>
*/
public static void main(String args[]) {
StreamSinkServer server = null;
switch (args.length) {
case 0:
server = new StreamSinkServer("dataDir", "server.key", "localhost", 7654);
break;
case 2:
server = new StreamSinkServer(args[0], args[1]);
break;
case 4:
try {
int port = Integer.parseInt(args[1]);
server = new StreamSinkServer(args[2], args[3], args[0], port);
} catch (NumberFormatException nfe) {
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile");
}
break;
default:
System.out.println("Usage: StreamSinkServer [i2cpHost i2cpPort] sinkDir ourDestFile");
}
if (server != null)
server.runServer();
}
}

View File

@ -0,0 +1,206 @@
package net.i2p.client.streaming;
import java.io.InputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PClientFactory;
import net.i2p.data.Destination;
import net.i2p.util.Log;
import net.i2p.util.I2PThread;
/**
* Sit around on a destination, receiving lots of data and sending lots of
* data to whomever talks to us.
*
* Usage: TestSwarm myKeyFile [peerDestFile ]*
*
*/
public class TestSwarm {
private I2PAppContext _context;
private Log _log;
private String _destFile;
private String _peerDestFiles[];
private String _conOptions;
private I2PSocketManager _manager;
private boolean _dead;
public static void main(String args[]) {
if (args.length < 1) {
System.err.println("Usage: TestSwarm myDestFile [peerDestFile ]*");
return;
}
I2PAppContext ctx = new I2PAppContext();
String files[] = new String[args.length - 1];
System.arraycopy(args, 1, files, 0, files.length);
TestSwarm swarm = new TestSwarm(ctx, args[0], files);
swarm.startup();
}
public TestSwarm(I2PAppContext ctx, String destFile, String peerDestFiles[]) {
_context = ctx;
_log = ctx.logManager().getLog(TestSwarm.class);
_dead = false;
_destFile = destFile;
_peerDestFiles = peerDestFiles;
_conOptions = "";
}
public void startup() {
_log.debug("Starting up");
File keys = new File(_destFile);
if (!keys.exists()) {
try {
I2PClientFactory.createClient().createDestination(new FileOutputStream(keys));
} catch (Exception e) {
_log.error("Error creating a new destination on " + keys, e);
return;
}
}
try {
_manager = I2PSocketManagerFactory.createManager(new FileInputStream(_destFile), null, -1, null);
} catch (Exception e) {
_log.error("Error creatign the manager", e);
return;
}
I2PThread listener = new I2PThread(new Listener(), "Listener");
listener.start();
connectWithPeers();
}
private void connectWithPeers() {
if (_peerDestFiles != null) {
for (int i = 0; i < _peerDestFiles.length; i++) {
try {
FileInputStream fin = new FileInputStream(_peerDestFiles[i]);
Destination dest = new Destination();
dest.readBytes(fin);
I2PThread flooder = new I2PThread(new Flooder(dest), "Flooder+" + dest.calculateHash().toBase64().substring(0,4));
flooder.start();
} catch (Exception e) {
_log.error("Unable to read the peer from " + _peerDestFiles[i], e);
}
}
}
}
private class Listener implements Runnable {
public void run() {
try {
I2PServerSocket ss = _manager.getServerSocket();
I2PSocket s = null;
while ( (s = ss.accept()) != null) {
I2PThread flooder = new I2PThread(new Flooder(s), "Flooder-" + s.getPeerDestination().calculateHash().toBase64().substring(0,4));
flooder.start();
}
} catch (Exception e) {
_log.error("Error listening", e);
}
}
}
private static volatile long __conId = 0;
private class Flooder implements Runnable {
private Destination _remoteDestination;
private I2PSocket _socket;
private boolean _closed;
private long _started;
private long _totalSent;
private long _totalReceived;
private long _lastReceived;
private long _lastReceivedOn;
private long _connectionId;
public Flooder(Destination dest) {
_socket = null;
_remoteDestination = dest;
_connectionId = ++__conId;
_closed = false;
_lastReceived = -1;
_lastReceivedOn = _context.clock().now();
_context.statManager().createRateStat("swarm." + _connectionId + ".totalReceived", "Data size received", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
_context.statManager().createRateStat("swarm." + _connectionId + ".totalSent", "Data size sent", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
_context.statManager().createRateStat("swarm." + _connectionId + ".started", "When we start", "swarm", new long[] { 5*60*1000 });
_context.statManager().createRateStat("swarm." + _connectionId + ".lifetime", "How long we talk to a peer", "swarm", new long[] { 5*60*1000 });
}
public Flooder(I2PSocket socket) {
_socket = socket;
_remoteDestination = socket.getPeerDestination();
_connectionId = ++__conId;
_closed = false;
_lastReceived = -1;
_lastReceivedOn = _context.clock().now();
_context.statManager().createRateStat("swarm." + _connectionId + ".totalReceived", "Data size received", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
_context.statManager().createRateStat("swarm." + _connectionId + ".totalSent", "Data size sent", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
_context.statManager().createRateStat("swarm." + _connectionId + ".started", "When we start", "swarm", new long[] { 5*60*1000 });
_context.statManager().createRateStat("swarm." + _connectionId + ".lifetime", "How long we talk to a peer", "swarm", new long[] { 5*60*1000 });
}
public long getConnectionId() { return _connectionId; }
public Destination getDestination() { return _remoteDestination; }
public void run() {
_started = _context.clock().now();
_context.statManager().addRateData("swarm." + _connectionId + ".started", 1, 0);
byte data[] = new byte[32*1024];
long value = 0;
long lastSend = _context.clock().now();
if (_socket == null) {
try {
_socket = _manager.connect(_remoteDestination);
} catch (Exception e) {
_log.error("Error connecting to " + _remoteDestination.calculateHash().toBase64().substring(0,4));
return;
}
}
I2PThread floodListener = new I2PThread(new FloodListener(), "FloodListener" + _connectionId);
floodListener.start();
try {
OutputStream out = _socket.getOutputStream();
while (!_closed) {
out.write(data);
// out.flush();
_totalSent += data.length;
_context.statManager().addRateData("swarm." + _connectionId + ".totalSent", _totalSent, 0);
//try { Thread.sleep(100); } catch (InterruptedException ie) {}
long now = _context.clock().now();
_log.debug("Sending " + _connectionId + " after " + (now-lastSend));
lastSend = now;
try { Thread.sleep(20); } catch (InterruptedException ie) {}
}
} catch (Exception e) {
_log.error("Error sending", e);
}
}
private class FloodListener implements Runnable {
public void run() {
long lastRead = System.currentTimeMillis();
long now = lastRead;
try {
InputStream in = _socket.getInputStream();
byte buf[] = new byte[32*1024];
int read = 0;
while ( (read = in.read(buf)) != -1) {
now = System.currentTimeMillis();
_totalReceived += read;
_context.statManager().addRateData("swarm." + getConnectionId() + ".totalReceived", _totalReceived, 0);
_log.debug("Receiving " + _connectionId + " with " + read + " after " + (now-lastRead));
lastRead = now;
}
} catch (Exception e) {
_log.error("Error listening to the flood", e);
}
}
}
}
}

View File

@ -0,0 +1,24 @@
<html><body>
<p>Implements a TCP-like (reliable, authenticated, in order) set of sockets for
communicating over the IP-like (unreliable, unauthenticated, unordered) I2P
messages.</p>
<p>When an application wants to use streams, it must fetch an {@link
net.i2p.client.streaming.I2PSocketManager} from the {@link
net.i2p.client.streaming.I2PSocketManagerFactory}, which in turn builds its own
{@link net.i2p.client.I2PSession} internally. All communication over that
{@link net.i2p.client.I2PSession} is handled by the {@link
net.i2p.client.streaming.I2PSocketManager}, as it imposes its own formatting on
the raw messages sent and received. If an application wants to receive streams
from other clients on the network, it should access the blocking {@link
net.i2p.client.streaming.I2PServerSocket#accept} method, which will provide an
{@link net.i2p.client.streaming.I2PSocket} when a new one is available. If an
application wants to create a new stream to a peer, it should do so with the
appropriate {@link net.i2p.client.streaming.I2PSocketManager#connect} call.</p>
<p>There is a simple pair of demo applications available as well - {@link
net.i2p.client.streaming.StreamSinkServer} listens to a destination and dumps
the data from all sockets it accepts to individual files, while {@link
net.i2p.client.streaming.StreamSinkClient} connects to a particular destination
and sends a specific amount of random data then disconnects.</p>
</body></html>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="all" name="time">
<project basedir="." default="all" name="myi2p">
<target name="all" depends="clean, build" />
<target name="build" depends="builddep, jar" />
<target name="builddep">
@ -8,32 +8,30 @@
<target name="compile">
<mkdir dir="./build" />
<mkdir dir="./build/obj" />
<javac srcdir="./src" debug="true" destdir="./build/obj" includes="**/*.java" classpath="../../../core/java/build/i2p.jar" />
<javac
srcdir="./src"
debug="true" deprecation="on" source="1.3" target="1.3"
destdir="./build/obj"
classpath="../../../core/java/build/i2p.jar" />
</target>
<target name="jar" depends="compile">
<jar destfile="./build/timestamper.jar" basedir="./build/obj" includes="**/*.class">
<manifest>
<attribute name="Main-Class" value="net.i2p.time.Timestamper" />
<attribute name="Class-Path" value="i2p.jar timestamper.jar" />
</manifest>
</jar>
<jar destfile="./build/myi2p.jar" basedir="./build/obj" includes="**/*.class" />
</target>
<target name="javadoc">
<mkdir dir="./build" />
<mkdir dir="./build/javadoc" />
<javadoc
sourcepath="./src:../../../core/java/src:../../../core/java/test" destdir="./build/javadoc"
sourcepath="./src:../../../core/java/src" destdir="./build/javadoc"
packagenames="*"
use="true"
access="package"
splitindex="true"
windowtitle="I2P timestamper" />
windowtitle="MyI2P" />
</target>
<target name="clean">
<delete dir="./build" />
</target>
<target name="cleandep" depends="clean">
<ant dir="../../../core/java/" target="cleandep" />
<ant dir="../../../core/java/" target="distclean" />
</target>
<target name="distclean" depends="clean">
<ant dir="../../../core/java/" target="distclean" />

View File

@ -0,0 +1,116 @@
package net.i2p.myi2p;
import net.i2p.data.Destination;
/**
* Packages up a message for delivery. The raw format of the message within a
* repliable datagram is
* <code>MyI2P $maj.$min $service $type\n$payload</code>
* where <code>$maj.$min</code> is currently 1.0, $service is the type of MyI2P
* service, $type is the type of message within that service, and $payload is
* the data specific to that type.
*
*/
public class MyI2PMessage {
private Destination _peer;
private String _service;
private String _type;
private byte[] _payload;
private static final byte[] MESSAGE_PREFIX = "MyI2P 1.0 ".getBytes();
/**
* Build a new MyI2P message to be sent.
*
* @param to address to send the message
* @param service what MyI2P service is involved
* @param type type of message within that service is involved
* @param data payload of the message to deliver
*/
public MyI2PMessage(Destination to, String service, String type, byte data[]) {
_peer = to;
_service = service;
_type = type;
_payload = data;
}
/**
* Read in the MyI2P data from the given datagram info.
*
* @param from authenticated from address
* @param dgramPayload raw MyI2P formatted message
* @throws IllegalArgumentException if the message is not a valid MyI2P message
*/
public MyI2PMessage(Destination from, byte dgramPayload[]) throws IllegalArgumentException {
_peer = from;
int index = 0;
while (index < dgramPayload.length) {
if (index >= MESSAGE_PREFIX.length) break;
if (dgramPayload[index] != MESSAGE_PREFIX[index])
throw new IllegalArgumentException("Invalid payload (not a MyI2P message)");
index++;
}
// $service $type\n$payload
StringBuffer service = new StringBuffer(8);
while (index < dgramPayload.length) {
if (dgramPayload[index] == ' ') {
_service = service.toString();
index++;
break;
} else if (dgramPayload[index] == '\n') {
throw new IllegalArgumentException("Ran into newline while reading the service");
} else {
service.append((char)dgramPayload[index]);
index++;
}
}
StringBuffer type = new StringBuffer(8);
while (index < dgramPayload.length) {
if (dgramPayload[index] == '\n') {
_type = type.toString();
index++;
break;
} else {
service.append((char)dgramPayload[index]);
index++;
}
}
_payload = new byte[dgramPayload.length-index];
System.arraycopy(dgramPayload, index, _payload, 0, _payload.length);
}
/** who is this message from or who is it going to? */
public Destination getPeer() { return _peer; }
/** what MyI2P service is this bound for (addressBook, blog, etc)? */
public String getServiceType() { return _service; }
/** within that service, what type of message is this? */
public String getMessageType() { return _type; }
/** what is the raw data for the particular message? */
public byte[] getPayload() { return _payload; }
/**
* Retrieve the raw payload, suitable for wrapping in an I2PDatagramMaker
* and sending to another MyI2P node.
*
* @throws IllegalStateException if some data is missing
*/
public byte[] toRawPayload() throws IllegalStateException {
if (_service == null) throw new IllegalStateException("Service is null");
if (_type == null) throw new IllegalStateException("Type is null");
if (_payload == null) throw new IllegalStateException("Payload is null");
byte service[] = _service.getBytes();
byte type[] = _type.getBytes();
byte rv[] = new byte[MESSAGE_PREFIX.length + service.length + 1 + type.length + 1 + _payload.length];
System.arraycopy(MESSAGE_PREFIX, 0, rv, 0, MESSAGE_PREFIX.length);
System.arraycopy(service, 0, rv, MESSAGE_PREFIX.length, service.length);
rv[MESSAGE_PREFIX.length + service.length] = ' ';
System.arraycopy(type, 0, rv, MESSAGE_PREFIX.length + service.length + 1, type.length);
rv[MESSAGE_PREFIX.length + service.length + 1 + type.length] = '\n';
System.arraycopy(_payload, 0, rv, MESSAGE_PREFIX.length + service.length + 1 + type.length + 1, _payload.length);
return rv;
}
}

View File

@ -0,0 +1,266 @@
package net.i2p.myi2p;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
/**
* Main controller for a MyI2P node, coordinating all network communication and
* distributing messages to the appropriate services.
*
*/
public class Node {
private static List _nodes = new ArrayList(1);
/**
* Return a list of Node instances that are currently
* operating in the JVM
*/
private static List nodes() {
synchronized (_nodes) {
return new ArrayList(_nodes);
}
}
private I2PAppContext _context;
private Log _log;
private NodeAdapter _adapter;
/**
* contains configuration properties, such where our router is, what
* services to run, etc
*
*/
private Properties _config;
/** filename where _config is stored */
private String _configFile = DEFAULT_CONFIG_FILE;
/** mapping of service name (String) to Service for all services loaded */
private Map _services;
private static final String DEFAULT_CONFIG_FILE = "myi2p.config";
private static final String DEFAULT_KEY_FILE = "myi2p.keys";
private static final String PROP_KEY_FILE = "keyFile";
public Node(I2PAppContext context) {
_context = context;
_log = context.logManager().getLog(Node.class);
_config = new Properties();
_services = new HashMap(1);
if (_log.shouldLog(Log.CRIT))
_log.log(Log.CRIT, "Node created");
_adapter = new NodeAdapter(_context, this);
}
/**
* Main driver for the node. Usage: <code>Node [configFile]</code>
*
*/
public static void main(String args[]) {
String filename = DEFAULT_CONFIG_FILE;
if ( (args != null) && (args.length == 1) )
filename = args[0];
Node node = new Node(I2PAppContext.getGlobalContext());
node.setConfigFile(filename);
node.loadConfig();
node.startup();
while (true) {
//try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
//node.shutdown();
//if (true) return;
synchronized (node) {
try { node.wait(); } catch (InterruptedException ie) {}
}
}
}
public Properties getConfig() {
synchronized (_config) {
return new Properties(_config);
}
}
public void setConfig(Properties props) {
synchronized (_config) {
_config.clear();
_config.putAll(props);
}
}
public String getConfigFile() { return _configFile; }
public void setConfigFile(String filename) { _configFile = filename; }
/**
* Load up the config and all of the services
*
*/
public void loadConfig() {
FileInputStream fis = null;
try {
File cfgFile = new File(_configFile);
if (cfgFile.exists()) {
fis = new FileInputStream(cfgFile);
Properties props = new Properties();
props.load(fis);
setConfig(props);
if (_log.shouldLog(Log.INFO))
_log.info("Config loaded from " + _configFile);
} else {
if (_log.shouldLog(Log.ERROR))
_log.error("Config file " + _configFile + " does not exist, so we aren't going to do much");
}
} catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error reading config file " + _configFile, ioe);
} finally {
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
}
}
/**
* Boot up the node, connect to the router, and start all the services
*
*/
public void startup() {
boolean connected = connect();
if (connected) {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() { shutdown(); }
}));
loadServices();
startServices();
synchronized (_nodes) {
_nodes.add(this);
}
if (_log.shouldLog(Log.INFO))
_log.info("Node started");
} else {
if (_log.shouldLog(Log.ERROR))
_log.error("Unable to connect, startup didn't do much");
}
}
/**
* Drop any connections to the network and stop all services
*
*/
public void shutdown() {
disconnect();
stopServices();
synchronized (_nodes) {
_nodes.remove(this);
}
}
/**
* Send a message from the node to the peer as specified in the message
*
* @return true if it was sent
*/
public boolean sendMessage(MyI2PMessage msg) {
return _adapter.sendMessage(msg);
}
private void loadServices() {
Properties config = getConfig();
int i = 0;
while (true) {
String classname = config.getProperty("service."+i+".classname");
if ( (classname == null) || (classname.trim().length() <= 0) )
break;
boolean ok = loadService("service." + i + ".", config);
if (ok) i++;
}
if (_log.shouldLog(Log.INFO))
_log.info(i + " services loaded");
}
private boolean loadService(String prefix, Properties config) {
String classname = config.getProperty(prefix + "classname");
String type = config.getProperty(prefix + "type");
if (type == null) return false;
Properties opts = new Properties();
int i = 0;
while (true) {
String name = config.getProperty(prefix + "option." + i + ".name");
String value = config.getProperty(prefix + "option." + i + ".value");
if ( (name == null) || (name.trim().length() <= 0) || (value == null) || (value.trim().length() <= 0) )
break;
opts.setProperty(name.trim(), value.trim());
i++;
}
try {
Class cls = Class.forName(classname);
Object obj = cls.newInstance();
if (obj instanceof Service) {
Service service = (Service)obj;
service.setType(type);
service.setOptions(opts);
service.setNode(this);
service.setContext(_context);
synchronized (_services) {
_services.put(type, service);
}
if (_log.shouldLog(Log.INFO))
_log.info("Service " + type + " loaded");
return true;
} else {
if (_log.shouldLog(Log.ERROR))
_log.error("Error loading service " + type + ": not a service [" + classname + "]");
}
} catch (ClassNotFoundException cnfe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error loading service " + type + ": class " + classname + " is invalid", cnfe);
} catch (InstantiationException ie) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error instantiating service " + type + ": class " + classname + " could not be created", ie);
} catch (IllegalAccessException iae) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error creating service " + type + ": class " + classname + " could not be accessed", iae);
}
return false;
}
private boolean connect() {
Properties config = getConfig();
File keyFile = new File(config.getProperty(PROP_KEY_FILE, DEFAULT_KEY_FILE));
return _adapter.connect(config, keyFile);
}
private void disconnect() {
_adapter.disconnect();
}
private void startServices() {
for (Iterator iter = _services.values().iterator(); iter.hasNext(); ) {
Service service = (Service)iter.next();
service.startup();
}
}
private void stopServices() {
for (Iterator iter = _services.values().iterator(); iter.hasNext(); ) {
Service service = (Service)iter.next();
service.shutdown();
}
}
void handleMessage(MyI2PMessage msg) {
Service service = (Service)_services.get(msg.getServiceType());
if (service == null) {
if (_log.shouldLog(Log.ERROR))
_log.error("Message received for an unknown service ["
+ msg.getServiceType() + "] from "
+ msg.getPeer().calculateHash().toBase64());
} else {
service.receiveMessage(msg);
}
}
}

View File

@ -0,0 +1,178 @@
package net.i2p.myi2p;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionListener;
import net.i2p.client.I2PSessionException;
import net.i2p.client.datagram.I2PDatagramDissector;
import net.i2p.client.datagram.I2PDatagramMaker;
import net.i2p.client.datagram.I2PInvalidDatagramException;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.Log;
/**
* Bind the MyI2P node to the I2P network, handling messages, sessions,
* etc.
*
*/
public class NodeAdapter implements I2PSessionListener {
private I2PAppContext _context;
private Log _log;
private Node _node;
private I2PSession _session;
public NodeAdapter(I2PAppContext context, Node node) {
_node = node;
_context = context;
_log = context.logManager().getLog(NodeAdapter.class);
}
boolean sendMessage(MyI2PMessage msg) {
if (_session == null) {
if (_log.shouldLog(Log.ERROR))
_log.error("Cannot send the message, as we are not connected");
return false;
}
try {
I2PDatagramMaker builder = new I2PDatagramMaker(_session);
byte dgram[] = builder.makeI2PDatagram(msg.toRawPayload());
return _session.sendMessage(msg.getPeer(), dgram);
} catch (IllegalStateException ise) {
if (_log.shouldLog(Log.ERROR))
_log.error("MyI2PMessage was not valid", ise);
return false;
} catch (I2PSessionException ise) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error sending to the peer", ise);
return false;
}
}
/**
* Connect to the network using the current I2CP config and the private
* key file specified in the node config. If the file does not exist, a new
* destination will be created.
*
* @param config MyI2P node and I2CP configuration
* @param keyFile file to load the private keystream from (if it doesn't
* exist, a new one will be created and stored at that location)
*
* @return true if connection was successful, false otherwise
*/
boolean connect(Properties config, File keyFile) {
I2PClient client = I2PClientFactory.createClient();
if (!keyFile.exists()) {
File parent = keyFile.getParentFile();
if (parent != null) parent.mkdirs();
FileOutputStream fos = null;
try {
fos = new FileOutputStream(keyFile);
Destination dest = client.createDestination(fos);
if (_log.shouldLog(Log.INFO))
_log.info("New destination created ["
+ dest.calculateHash().toBase64()
+ "] with keys at " + keyFile);
} catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error writing new keystream to " + keyFile, ioe);
return false;
} catch (I2PException ie) {
if (_log.shouldLog(Log.ERROR))
_log.error("Internal error creating new destination", ie);
return false;
} finally {
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
}
}
FileInputStream fis = null;
try {
fis = new FileInputStream(keyFile);
_session = client.createSession(fis, config);
if (_session == null) {
_log.error("wtf, why did it create a null session?");
return false;
}
_session.setSessionListener(this);
_session.connect();
if (_log.shouldLog(Log.INFO))
_log.info("I2P session created");
return true;
} catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Unable to read the keystream from " + keyFile, ioe);
return false;
} catch (I2PSessionException ise) {
if (_log.shouldLog(Log.ERROR))
_log.error("Unable to connect to the router", ise);
return false;
} finally {
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
}
}
void disconnect() {
if (_session != null) {
try {
_session.destroySession();
} catch (I2PSessionException ise) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error destroying the session in shutdown", ise);
}
_session = null;
}
}
public void disconnected(I2PSession session) {
if (_log.shouldLog(Log.INFO))
_log.info("Session disconnected");
}
public void errorOccurred(I2PSession session, String message, Throwable error) {
if (_log.shouldLog(Log.ERROR))
_log.error("Session error occurred - " + message, error);
}
public void messageAvailable(I2PSession session, int msgId, long size) {
if (_log.shouldLog(Log.INFO))
_log.info("message available [" + msgId + "/"+ size + " bytes]");
try {
byte data[] = session.receiveMessage(msgId);
I2PDatagramDissector dissector = new I2PDatagramDissector();
dissector.loadI2PDatagram(data);
try {
MyI2PMessage msg = new MyI2PMessage(dissector.getSender(), dissector.getPayload());
_node.handleMessage(msg);
} catch (IllegalArgumentException iae) {
if (_log.shouldLog(Log.ERROR))
_log.error("Message is a valid datagram but invalid MyI2P message", iae);
}
} catch (I2PSessionException ise) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error retrieving message payload for message " + msgId, ise);
} catch (I2PInvalidDatagramException iide) {
if (_log.shouldLog(Log.ERROR))
_log.error("Message received was not a valid repliable datagram", iide);
} catch (DataFormatException dfe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Message received was a corrupt repliable datagram", dfe);
}
}
public void reportAbuse(I2PSession session, int severity) {
if (_log.shouldLog(Log.INFO))
_log.info("abuse occurred");
}
}

View File

@ -0,0 +1,35 @@
package net.i2p.myi2p;
import java.util.Properties;
import net.i2p.I2PAppContext;
/**
* Defines a service that can operate within a MyI2P node, responding to
* messages and performing whatever tasks are necessary.
*
*/
public interface Service {
/** what type of message will this service respond to? */
public String getType();
public void setType(String type);
/** what node is this service hooked into */
public Node getNode();
public void setNode(Node node);
/** what options specific to this node does the service have? */
public Properties getOptions();
public void setOptions(Properties opts);
/** give the service a scope */
public I2PAppContext getContext();
public void setContext(I2PAppContext context);
/** called when a message is received for the service */
public void receiveMessage(MyI2PMessage msg);
/** start the service up - the node is ready */
public void startup();
/** shut the service down - the node is going offline */
public void shutdown();
}

View File

@ -0,0 +1,36 @@
package net.i2p.myi2p;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.myi2p.Service;
import net.i2p.myi2p.Node;
import net.i2p.myi2p.MyI2PMessage;
/**
* Base service implementation
*
*/
public abstract class ServiceImpl implements Service {
private I2PAppContext _context;
private Node _node;
private Properties _options;
private String _serviceType;
public ServiceImpl() {
_context = null;
_node = null;
_options = null;
_serviceType = null;
}
// base inspectors / mutators
public Node getNode() { return _node; }
public void setNode(Node node) { _node = node; }
public I2PAppContext getContext() { return _context; }
public void setContext(I2PAppContext context) { _context = context; }
public Properties getOptions() { return _options; }
public void setOptions(Properties opts) { _options = opts; }
public String getType() { return _serviceType; }
public void setType(String type) { _serviceType = type; }
}

View File

@ -0,0 +1,126 @@
package net.i2p.myi2p.address;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.DataFormatException;
/**
* Main lookup component for maintaining references to other I2P destinations.
*
*/
public class AddressBook {
private I2PAppContext _context;
/** Name (String) to AddressBookEntry */
private Map _entries;
/**
* List of NameReference that has been received but whose preferred
* name conflicts with an existing entry.
*/
private List _conflictingReferences;
public AddressBook(I2PAppContext context) {
_context = context;
_entries = new HashMap(16);
_conflictingReferences = new ArrayList(0);
}
/** retrieve a list of entry names (strings) */
public Set getEntryNames() {
synchronized (_entries) {
return new HashSet(_entries.keySet());
}
}
public AddressBookEntry getEntry(String name) {
synchronized (_entries) {
return (AddressBookEntry)_entries.get(name);
}
}
public AddressBookEntry addEntry(AddressBookEntry entry) {
synchronized (_entries) {
return (AddressBookEntry)_entries.put(entry.getLocalName(), entry);
}
}
public void removeEntry(String name) {
synchronized (_entries) {
_entries.remove(name);
}
}
public int getConflictingReferenceCount() {
synchronized (_conflictingReferences) {
return _conflictingReferences.size();
}
}
public NameReference getConflictingReference(int index) {
synchronized (_conflictingReferences) {
return (NameReference)_conflictingReferences.get(index);
}
}
public void addConflictingReference(NameReference ref) {
synchronized (_conflictingReferences) {
_conflictingReferences.add(ref);
}
}
public void removeConflictingReference(int index) {
synchronized (_conflictingReferences) {
_conflictingReferences.remove(index);
}
}
public void read(InputStream in) throws IOException {
try {
int numEntries = (int)DataHelper.readLong(in, 2);
if (numEntries < 0) throw new IOException("Corrupt AddressBook - " + numEntries + " entries?");
for (int i = 0; i < numEntries; i++) {
AddressBookEntry entry = new AddressBookEntry(_context);
entry.read(in);
addEntry(entry);
}
int numConflicting = (int)DataHelper.readLong(in, 2);
if (numConflicting < 0) throw new IOException("Corrupt AddressBook - " + numConflicting + " conflicting?");
for (int i = 0; i < numConflicting; i++) {
NameReference ref = new NameReference(_context);
ref.read(in);
addConflictingReference(ref);
}
} catch (DataFormatException dfe) {
throw new IOException("Corrupt address book - " + dfe.getMessage());
}
}
public void write(OutputStream out) throws IOException {
try {
synchronized (_entries) {
DataHelper.writeLong(out, 2, _entries.size());
for (Iterator iter = _entries.values().iterator(); iter.hasNext(); ) {
AddressBookEntry entry = (AddressBookEntry)iter.next();
entry.write(out);
}
}
synchronized (_conflictingReferences) {
DataHelper.writeLong(out, 2, _conflictingReferences.size());
for (int i = 0; i < _conflictingReferences.size(); i++) {
NameReference ref = (NameReference)_conflictingReferences.get(i);
ref.write(out);
}
}
} catch (DataFormatException dfe) {
throw new IOException("Corrupt address book - " + dfe.getMessage());
}
}
public String toString() {
return "Entries: " + _entries.size() + " conflicting: " + _conflictingReferences.size();
}
}

View File

@ -0,0 +1,142 @@
package net.i2p.myi2p.address;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.DataFormatException;
/**
* Implements a local address book entry, pointing at a known secure
* NameReference as well as an optional Subscription.
*
*/
public class AddressBookEntry {
private I2PAppContext _context;
private String _localName;
private Properties _options;
private NameReference _reference;
private Subscription _subscription;
private long _addedOn;
public AddressBookEntry(I2PAppContext context) {
_context = context;
_localName = null;
_options = new Properties();
_reference = null;
_subscription = null;
_addedOn = context.clock().now();
}
/** Local (unique) name we use to reference the given destination */
public String getLocalName() { return _localName; }
public void setLocalName(String name) { _localName = name; }
public Properties getOptions() {
synchronized (_options) {
return new Properties(_options);
}
}
public void setOptions(Properties props) {
synchronized (_options) {
_options.clear();
if (props != null)
_options.putAll(props);
}
}
/** Secure name reference, provided by the destination */
public NameReference getNameReference() { return _reference; }
public void setNameReference(NameReference ref) { _reference = ref; }
/**
* If specified, the details of our subscription to the MyI2P address
* book at the referenced destination.
*
*/
public Subscription getSubscription() { return _subscription; }
public void setSubscription(Subscription sub) { _subscription = sub; }
/** When this entry was added */
public long getAddedOn() { return _addedOn; }
public void setAddedOn(long when) { _addedOn = when; }
/** load the data from the stream */
public void read(InputStream in) throws IOException {
try {
Boolean localNameDefined = DataHelper.readBoolean(in);
if ( (localNameDefined != null) && (localNameDefined.booleanValue()) )
_localName = DataHelper.readString(in);
else
_localName = null;
Date when = DataHelper.readDate(in);
if (when == null)
_addedOn = -1;
else
_addedOn = when.getTime();
Properties props = DataHelper.readProperties(in);
setOptions(props);
Boolean refDefined = DataHelper.readBoolean(in);
if ( (refDefined != null) && (refDefined.booleanValue()) ) {
_reference = new NameReference(_context);
_reference.read(in);
} else {
_reference = null;
}
Boolean subDefined = DataHelper.readBoolean(in);
if ( (subDefined != null) && (subDefined.booleanValue()) ) {
Subscription sub = new Subscription(_context);
sub.read(in);
_subscription = sub;
} else {
_subscription = null;
}
} catch (DataFormatException dfe) {
throw new IOException("Corrupt subscription: " + dfe.getMessage());
}
}
/** persist the data to the stream */
public void write(OutputStream out) throws IOException {
try {
if ( (_localName != null) && (_localName.trim().length() > 0) ) {
DataHelper.writeBoolean(out, Boolean.TRUE);
DataHelper.writeString(out, _localName);
} else {
DataHelper.writeBoolean(out, Boolean.FALSE);
}
DataHelper.writeDate(out, new Date(_addedOn));
synchronized (_options) {
DataHelper.writeProperties(out, _options);
}
if (_reference != null) {
DataHelper.writeBoolean(out, Boolean.TRUE);
_reference.write(out);
} else {
DataHelper.writeBoolean(out, Boolean.FALSE);
}
if (_subscription != null) {
DataHelper.writeBoolean(out, Boolean.TRUE);
_subscription.write(out);
} else {
DataHelper.writeBoolean(out, Boolean.FALSE);
}
} catch (DataFormatException dfe) {
throw new IOException("Corrupt subscription: " + dfe.getMessage());
}
}
}

View File

@ -0,0 +1,90 @@
package net.i2p.myi2p.address;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import net.i2p.data.DataHelper;
import net.i2p.data.DataFormatException;
import net.i2p.util.Log;
import net.i2p.myi2p.Service;
import net.i2p.myi2p.ServiceImpl;
import net.i2p.myi2p.Node;
import net.i2p.myi2p.MyI2PMessage;
/**
* Main service handler / coordinator for the MyI2P address book.
*
*/
public class AddressBookService extends ServiceImpl {
private Log _log;
private AddressBook _addressBook;
/** contains a mapping of event time (Long) to description (String) */
private Map _activityLog;
private String _addressBookFile;
private static String PROP_ADDRESSBOOK_FILE = "datafile";
private static String DEFAULT_ADDRESSBOOK_FILE = "addressbook.dat";
public static final String SERVICE_TYPE = "AddressBook";
public String getType() { return SERVICE_TYPE; }
public void startup() {
_log = getContext().logManager().getLog(AddressBookService.class);
_addressBookFile = getOptions().getProperty(PROP_ADDRESSBOOK_FILE, DEFAULT_ADDRESSBOOK_FILE);
File file = new File(_addressBookFile);
if (file.exists()) {
loadData(file);
} else {
_addressBook = new AddressBook(getContext());
_activityLog = new HashMap(16);
}
}
public void shutdown() {
File file = new File(_addressBookFile);
storeData(file);
}
public void receiveMessage(MyI2PMessage msg) {
_log.info("Received a " + msg.getMessageType() + " from "
+ msg.getPeer().calculateHash().toBase64()
+ new String(msg.getPayload()));
}
/** load everything from disk */
private void loadData(File dataFile) {
AddressBookServiceData data = new AddressBookServiceData(getContext());
data.load(dataFile);
if (data.getErrorMessage() != null) {
_log.warn(data.getErrorMessage(), data.getError());
_addressBook = new AddressBook(getContext());
_activityLog = new HashMap(16);
} else {
_addressBook = data.getAddressBook();
_activityLog = data.getActivityLog();
}
}
/** persist everything to disk */
private void storeData(File dataFile) {
AddressBookServiceData data = new AddressBookServiceData(getContext());
data.setActivityLog(_activityLog);
data.setAddressBook(_addressBook);
data.store(dataFile);
if (data.getErrorMessage() != null) {
_log.warn(data.getErrorMessage(), data.getError());
}
}
}

View File

@ -0,0 +1,104 @@
package net.i2p.myi2p.address;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
/**
* Component for loading and storing the service data to disk
*
*/
public class AddressBookServiceData {
private I2PAppContext _context;
private Log _log;
private AddressBook _addressBook;
private Map _activityLog;
private Exception _error;
private String _errorMessage;
public AddressBookServiceData(I2PAppContext context) {
_context = context;
_log = context.logManager().getLog(AddressBookServiceData.class);
_addressBook = null;
_activityLog = null;
_error = null;
_errorMessage = null;
}
public AddressBook getAddressBook() { return _addressBook; }
public void setAddressBook(AddressBook book) { _addressBook = book; }
public Map getActivityLog() { return _activityLog; }
public void setActivityLog(Map log) { _activityLog = log; }
public Exception getError() { return _error; }
public String getErrorMessage() { return _errorMessage; }
public void load(File from) {
FileInputStream fis = null;
try {
fis = new FileInputStream(from);
AddressBook addressBook = new AddressBook(_context);
addressBook.read(fis);
_addressBook = addressBook;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Address book: " + addressBook);
Properties props = DataHelper.readProperties(fis);
Map log = new HashMap(props.size());
for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String event = props.getProperty(key);
long when = 0;
try {
when = Long.parseLong(key);
while (log.containsKey(new Long(when)))
when++;
log.put(new Long(when), event);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Activity log: on " + new Date(when) + ": " + event);
} catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Corrupt activity log entry: when=" + key, nfe);
}
}
_activityLog = log;
} catch (Exception e) {
_error = e;
_errorMessage = "Error reading the address book from " + from;
}
}
public void store(File to) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(to);
_addressBook.write(fos);
Properties props = new Properties();
for (Iterator iter = _activityLog.keySet().iterator(); iter.hasNext(); ) {
Long when = (Long)iter.next();
String msg = (String)_activityLog.get(when);
props.setProperty(when.toString(), msg);
}
DataHelper.writeProperties(fos, props);
} catch (Exception e) {
_error = e;
_errorMessage = "Error writing the address book to " + to;
} finally {
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
}
}
}

View File

@ -0,0 +1,154 @@
package net.i2p.myi2p.address;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.data.Destination;
import net.i2p.util.Log;
/**
* CreateEntryCLI addressBookFile referenceFile localName subscriptionFrequencyHours [ key=value]*
*/
public class CreateEntryCLI {
private I2PAppContext _context;
private String _args[];
private String _addressBook;
private String _referenceFile;
private String _localName;
private int _subscriptionFrequencyHours;
private Properties _options;
public CreateEntryCLI(String args[]) {
_context = new I2PAppContext();
_args = args;
_options = new Properties();
}
public void execute() {
if (parseArgs())
doExecute();
else
System.err.println("Usage: CreateEntryCLI addressBookFile referenceFile localName subscriptionFrequencyHours[ key=value]*");
}
private boolean parseArgs() {
if ( (_args == null) || (_args.length < 3) )
return false;
_addressBook = _args[0];
_referenceFile = _args[1];
_localName = _args[2];
try {
_subscriptionFrequencyHours = Integer.parseInt(_args[3]);
} catch (NumberFormatException nfe) {
return false;
}
for (int i = 4; i < _args.length; i++) {
int eq = _args[i].indexOf('=');
if ( (eq <= 0) || (eq >= _args[i].length() - 1) )
continue;
String key = _args[i].substring(0,eq);
String val = _args[i].substring(eq+1);
_options.setProperty(key, val);
}
return true;
}
private void doExecute() {
AddressBookServiceData data = new AddressBookServiceData(_context);
File f = new File(_addressBook);
if (f.exists()) {
data.load(f);
if (data.getError() != null) {
if (data.getErrorMessage() != null)
System.err.println(data.getErrorMessage());
data.getError().printStackTrace();
return;
}
} else {
data.setAddressBook(new AddressBook(_context));
data.setActivityLog(new HashMap());
}
NameReference ref = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(_referenceFile);
ref = new NameReference(_context);
ref.read(fis);
} catch (Exception e) {
System.err.println("Name reference under " + _referenceFile + " is corrupt");
e.printStackTrace();
return;
} finally {
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
}
AddressBook book = data.getAddressBook();
Map activityLog = data.getActivityLog();
AddressBookEntry oldEntry = book.getEntry(_localName);
AddressBookEntry entry = new AddressBookEntry(_context);
entry.setLocalName(_localName);
entry.setNameReference(ref);
Subscription sub = new Subscription(_context);
sub.setQueryFrequencyMinutes(60*_subscriptionFrequencyHours);
entry.setSubscription(sub);
entry.setOptions(_options);
if (oldEntry == null) {
book.addEntry(entry);
System.out.println("New address book entry added for " + entry.getLocalName());
activityLog.put(new Long(_context.clock().now()), "New address book entry added for " + entry.getLocalName());
} else {
Destination oldDest = oldEntry.getNameReference().getDestination();
if (oldDest.equals(ref.getDestination())) {
if (ref.getSequenceNum() < oldEntry.getNameReference().getSequenceNum()) {
System.err.println("Not updating the address book - newer reference for " + entry.getLocalName() + " exists");
return;
} else {
// same or newer rev
if (null != entry.getSubscription()) {
if (null != oldEntry.getSubscription()) {
entry.getSubscription().setLastQueryAttempt(oldEntry.getSubscription().getLastQueryAttempt());
entry.getSubscription().setLastQuerySuccess(oldEntry.getSubscription().getLastQuerySuccess());
}
}
book.addEntry(entry);
System.err.println("Updating the options and subscription for an existing reference to " + entry.getLocalName());
activityLog.put(new Long(_context.clock().now()), "Updating options and subscription for " + entry.getLocalName());
}
} else {
book.addConflictingReference(ref);
System.out.println("Old entry exists for " + _localName + " - adding a conflicting reference");
System.out.println("Existing entry points to " + oldEntry.getNameReference().getDestination().calculateHash().toBase64());
System.out.println("New entry points to " + entry.getNameReference().getDestination().calculateHash().toBase64());
activityLog.put(new Long(_context.clock().now()), "Adding conflicting reference for " + entry.getLocalName());
}
}
data.setAddressBook(book);
data.setActivityLog(activityLog);
data.store(f);
if (data.getError() != null) {
if (data.getErrorMessage() != null)
System.err.println(data.getErrorMessage());
data.getError().printStackTrace();
return;
}
}
public static void main(String args[]) {
CreateEntryCLI cli = new CreateEntryCLI(args);
cli.execute();
}
}

View File

@ -0,0 +1,125 @@
package net.i2p.myi2p.address;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.data.Destination;
import net.i2p.data.PrivateKey;
import net.i2p.data.SigningPrivateKey;
import net.i2p.util.Log;
/**
* CreateNameReferenceCLI outputFile privateDestFile preferredName sequenceNum serviceType[ key=value]*
*/
public class CreateNameReferenceCLI {
private I2PAppContext _context;
private String _args[];
private String _outputFile;
private String _destFile;
private String _preferredName;
private long _sequenceNum;
private String _serviceType;
private Properties _options;
public CreateNameReferenceCLI(String[] args) {
_context = new I2PAppContext();
_args = args;
_options = new Properties();
}
public void execute() {
if (parseArgs())
doExecute();
else
System.err.println("Usage: CreateNameReferenceCLI outputFile privateDestFile preferredName sequenceNum serviceType[ key=value]*");
}
private boolean parseArgs() {
if ( (_args == null) || (_args.length < 4) )
return false;
_outputFile = _args[0];
_destFile = _args[1];
_preferredName = _args[2];
try {
_sequenceNum = Long.parseLong(_args[3]);
} catch (NumberFormatException nfe) {
return false;
}
_serviceType = _args[4];
for (int i = 5; i < _args.length; i++) {
int eq = _args[i].indexOf('=');
if ( (eq <= 0) || (eq >= _args[i].length() - 1) )
continue;
String key = _args[i].substring(0,eq);
String val = _args[i].substring(eq+1);
_options.setProperty(key, val);
}
return true;
}
private void doExecute() {
Destination dest = null;
SigningPrivateKey priv = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(_destFile);
dest = new Destination();
dest.readBytes(fis);
PrivateKey whocares = new PrivateKey();
whocares.readBytes(fis);
priv = new SigningPrivateKey();
priv.readBytes(fis);
} catch (Exception e) {
System.err.println("Destination private keys under " + _destFile + " are corrupt");
e.printStackTrace();
return;
} finally {
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
}
NameReference ref = new NameReference(_context);
ref.setDestination(dest);
ref.setPreferredName(_preferredName);
ref.setSequenceNum(_sequenceNum);
ref.setServiceType(_serviceType);
if (_options != null) {
for (Iterator iter = _options.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String val = _options.getProperty(key);
ref.setOption(key, val);
}
}
try {
ref.sign(priv);
} catch (IllegalStateException ise) {
System.err.println("Error signing the new reference");
ise.printStackTrace();
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(_outputFile);
ref.write(fos);
} catch (IOException ioe) {
System.err.println("Error writing out the new reference");
ioe.printStackTrace();
} finally {
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
}
System.out.println("Reference created at " + _outputFile);
}
public static void main(String args[]) {
CreateNameReferenceCLI cli = new CreateNameReferenceCLI(args);
cli.execute();
}
}

View File

@ -0,0 +1,198 @@
package net.i2p.myi2p.address;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.data.Signature;
import net.i2p.data.SigningPrivateKey;
import net.i2p.util.Log;
/**
* Define a verified and immutable reference to a particular I2P destination.
*
*/
public class NameReference {
private I2PAppContext _context;
private Log _log;
private Destination _destination;
private String _preferredName;
private String _serviceType;
private long _sequenceNum;
private Properties _options;
private Signature _signature;
public static final byte[] VERSION_PREFIX = "MyI2P_NameReference_1.0".getBytes();
public NameReference(I2PAppContext context) {
_context = context;
_log = context.logManager().getLog(NameReference.class);
_destination = null;
_preferredName = null;
_serviceType = null;
_sequenceNum = -1;
_options = new Properties();
_signature = null;
}
/** retrieve the destination this reference points at */
public Destination getDestination() { return _destination; }
public void setDestination(Destination dest) { _destination = dest; }
/** retrieve the name this destination would like to be called */
public String getPreferredName() { return _preferredName; }
public void setPreferredName(String name) { _preferredName = name; }
/** retrieve the type of service at this destination (eepsite, ircd, etc) */
public String getServiceType() { return _serviceType; }
public void setServiceType(String type) { _serviceType = type; }
/**
* data point to allow the reference to be updated. The reference with the
* larger sequence number should clobber an older reference.
*
*/
public long getSequenceNum() { return _sequenceNum; }
public void setSequenceNum(long num) { _sequenceNum = num; }
/** Get a list of option names (strings) */
public Set getOptionNames() { return new HashSet(_options.keySet()); }
/** Access any options published in the reference */
public String getOption(String name) { return (String)_options.getProperty(name); }
/**
* Specify a particular value for an option. If the value is null, the
* entry is removed. The name cannot have an '=' or newline, and the
* value cannot have a newline.
*
* @throws IllegalArgumentException if the name or value is illegal
*/
public void setOption(String name, String value) {
if (name == null) throw new IllegalArgumentException("Missing name");
if (name.indexOf('=') != -1)
throw new IllegalArgumentException("Name cannot have an = sign");
if (name.indexOf('\n') != -1)
throw new IllegalArgumentException("Name cannot have a newline");
if (value != null) {
if (value.indexOf('\n') != -1)
throw new IllegalArgumentException("Values do not allow newlines");
_options.setProperty(name, value);
} else {
_options.remove(name);
}
}
/** Access the DSA signature authenticating this reference */
public Signature getSignature() { return _signature; }
public void setSignature(Signature sig) { _signature = sig; }
/** Verify the DSA signature, returning true if it is valid, false otherwise */
public boolean validate() {
try {
byte raw[] = toSignableByteArray();
return _context.dsa().verifySignature(_signature, raw, _destination.getSigningPublicKey());
} catch (IllegalStateException ise) {
return false;
}
}
/**
* Sign the data
*
* @throws IllegalStateException if the data is invalid
*/
public void sign(SigningPrivateKey key) throws IllegalStateException {
byte signable[] = toSignableByteArray();
_signature = _context.dsa().sign(signable, key);
}
/**
* Retrieve the full serialized and signed version of this name reference
*
* @throws IllegalStateException if the signature or other data is invalid
*/
public void write(OutputStream out) throws IllegalStateException, IOException {
if (_signature == null) throw new IllegalStateException("No signature");
byte signable[] = toSignableByteArray();
out.write(signable);
try {
_signature.writeBytes(out);
} catch (DataFormatException dfe) {
throw new IllegalStateException("Signature was corrupt - " + dfe.getMessage());
}
}
/**
* Retrieve the signable (but not including the signature) name reference
*
* @throws IllegalStateException if the data is invalid
*/
private byte[] toSignableByteArray() throws IllegalStateException {
if (_sequenceNum < 0) throw new IllegalStateException("Sequence number is invalid");
if (_preferredName == null) throw new IllegalStateException("Preferred name is invalid");
if (_serviceType == null) throw new IllegalStateException("Service type is invalid");
if (_options == null) throw new IllegalStateException("Options not constructed");
if (_destination == null) throw new IllegalStateException("Destination not specified");
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
try {
baos.write(VERSION_PREFIX);
_destination.writeBytes(baos);
DataHelper.writeLong(baos, 4, _sequenceNum);
DataHelper.writeString(baos, _preferredName);
DataHelper.writeString(baos, _serviceType);
DataHelper.writeProperties(baos, _options); // sorts alphabetically
return baos.toByteArray();
} catch (DataFormatException dfe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Corrupted trying to write entry", dfe);
return null;
} catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error("IOError writing to memory?", ioe);
return null;
}
}
/**
* Read a full signed reference from the given stream
*
* @throws DataFormatException if the reference is corrupt
* @throws IOException if there is an error reading the stream
*/
public void read(InputStream in) throws DataFormatException, IOException {
byte versionBuf[] = new byte[VERSION_PREFIX.length];
int len = DataHelper.read(in, versionBuf);
if (len != versionBuf.length)
throw new IllegalArgumentException("Version length too short ("+ len + ")");
if (!DataHelper.eq(versionBuf, VERSION_PREFIX))
throw new IllegalArgumentException("Version mismatch (" + new String(versionBuf) + ")");
Destination dest = new Destination();
dest.readBytes(in);
long seq = DataHelper.readLong(in, 4);
String name = DataHelper.readString(in);
String type = DataHelper.readString(in);
Properties opts = DataHelper.readProperties(in);
Signature sig = new Signature();
sig.readBytes(in);
// ok, nothing b0rked
_destination = dest;
_sequenceNum = seq;
_preferredName = name;
_serviceType = type;
_options = opts;
_signature = sig;
}
}

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