Compare commits

...

452 Commits

Author SHA1 Message Date
20ea680ff0 * 2005-10-14 0.6.1.3 released
2005-10-14  jrandom
    * Added a key explaining peers.jsp a bit (thanks tethra!)
2005-10-14 13:48:04 +00:00
cabb607211 2005-10-13 dust
* Bundled dust's Sucker for pulling RSS/Atom content into SML, which can
      then be injected into Syndie with the Syndie CLI.
    * Bundled ROME and JDOM (BSD and Apache licensed, respectively) for
      RSS/Atom parsing.
2005-10-13  jrandom
    * SSU retransmission choke bugfix (== != !=)
    * Include initial transmissions in the retransmission choke, so that
      if we are already retransmitting a message, we won't send anything
      to that peer other than that message (or ACKs, if necessary)
2005-10-14 02:15:39 +00:00
00a4761b5e 2005-10-13 jrandom
* SSU retransmission choke bugfix (== != !=)
    * Include initial transmissions in the retransmission choke, so that
      if we are already retransmitting a message, we won't send anything
      to that peer other than that message (or ACKs, if necessary)
2005-10-13 09:18:35 +00:00
3516701272 2005-10-12 jrandom
* Choke SSU retransmissions to a peer while there is already a
      retransmission in flight to them.  This currently lets other initial
      transmissions through, since packet loss is often sporadic, but maybe
      this should block initial transmissions as well?
    * Display the retransmission bytes stat on peers.jsp (thanks bar!)
    * Filter QUIT messages in the I2PTunnelIRCClient proxy
2005-10-13 03:54:54 +00:00
c4d785667a 2005-10-11 jrandom
* Piggyback the SSU partial ACKs with data packets.  This is backwards
      compatible.
    * Syndie RSS renderer bugfix, plus now include the full entry instead of
      just the blurb before the cut.
2005-10-11 21:05:14 +00:00
123e0ba589 2005-10-11 jrandom
* Piggyback the SSU explicit ACKs with data packets (partial ACKs aren't
      yet piggybacked).  This is backwards compatible.
    * SML parser cleanup in Syndie
2005-10-11 07:07:39 +00:00
197237aa32 2005-10-10 dust
* Implemented a new I2PTunnelIRCClient which locally filters inbound and
      outbound IRC commands for anonymity and security purposes, removing all
      CTCP messages except ACTION, as well as stripping the hostname from the
      USER message (while leaving the nick and 'full name').  The IRC proxy
      doesn't use this by default, but you can enable it by creating a new
      "IRC proxy" tunnel on the web interface, or by changing the tunnel type
      to "ircclient" in i2ptunnel.config.
2005-10-10  jrandom
    * I2PTunnel http client config cleanup and stats
    * Minor SSU congestion tweaks and stats
    * Reduced netDb exploration period
2005-10-10 23:05:18 +00:00
f30dc2b480 2005-10-10 dust
* Implemented a new I2PTunnelIRCClient which locally filters inbound and
      outbound IRC commands for anonymity and security purposes, removing all
      CTCP messages except ACTION, as well as stripping the hostname from the
      USER message (while leaving the nick and 'full name').  The IRC proxy
      doesn't use this by default, but you can enable it by creating a new
      "IRC proxy" tunnel on the web interface, or by changing the tunnel type
      to "ircclient" in i2ptunnel.config.
2005-10-10  jrandom
    * I2PTunnel http client config cleanup and stats
    * Minor SSU congestion tweaks and stats
    * Reduced netDb exploration period
2005-10-10 22:58:18 +00:00
d4ff34eacb * Treat petname names as being case insensitive. 2005-10-09 22:56:02 +00:00
978769a05d * Finished syndie address auto-import. If syndie.importAddresses=true in syndie.config, then new addresses will automatically be imported to the router's petname db. If importaddresses=true in a user's config file, then new addresses will automatically be imported to that users pername db, when they're logged in. 2005-10-09 20:16:30 +00:00
993c70f600 2005-10-09 jrandom
* Syndie CLI cleanup for simpler CLI posting.  Usage shown with
      java -jar lib/syndie.jar
    * Beginnings of the Syndie logging cleanup
    * Delete corrupt Syndie posts
2005-10-09 11:35:13 +00:00
5dfa9ad7f6 2005-10-09 jrandom
* Now that the streaming lib works reasonably, set the default inactivity
      event to send a 0 byte keepalive payload, rather than disconnecting the
      stream.  This should cut the irc netsplits and help out with other long
      lived streams.  The default timeout is now less than the old timeout as
      well, so the keepalive will be sent before earlier builds fire their
      fatal timeouts.
2005-10-09 05:46:57 +00:00
f282fe3854 * Fix probable NPE. 2005-10-09 04:01:29 +00:00
9297564555 * getName, getLocation -> getByName, getByLocation 2005-10-09 03:47:12 +00:00
e7ad516685 * Beautify PetNameDB API.
* Start of syndie auto address export.
2005-10-09 03:32:34 +00:00
ad574c8504 2005-10-08 jrandom
* Use the OS clock for stat timing, since it doesn't jump around (though
      still use the NTP'ed clock for display)
    * Added new DH stats
2005-10-08 22:05:46 +00:00
38617fe0a7 fixed link (thanks Jazzy) 2005-10-07 23:45:48 +00:00
cdee5b2c31 * 2005-10-07 0.6.1.2 released
2005-10-07  jrandom
    * Include the 1 second bandwidth usage on the console rather than the
      1 minute rate, as the 1 second value doesn't have the 1m/5m quantization
      issues.
2005-10-07 20:19:04 +00:00
7f6e65c76f 2005-10-07 jrandom
* Allow the I2PTunnelHTTPServer to send back the first few packets of an
      HTTP response quicker, and initialize the streaming lib's cwin more
      carefully.
    * Added a small web UI to the new Syndie scheduled updater.  If you log in
      as a user authorized to use the remote archive funtionality, you can
      request remote archives in your address book to be automatically pulled
      down by checking the "scheduled?" checkbox.
2005-10-07 10:22:59 +00:00
4dd628dbc8 2005-10-05 jrandom
* Allow the first few packets in the stream to fill in their IDs during
      handshake (thanks cervantes, Complication, et al!)  This should fix at
      least some of the intermittent HTTP POST issues.
2005-10-05 23:24:33 +00:00
3b5b48ad8a more cleanup (thanks bar) 2005-10-05 03:48:56 +00:00
c4cac3f3f1 we dont need no grammar 2005-10-05 01:45:21 +00:00
4a49e98c31 caps (thanks bar) 2005-10-05 01:11:25 +00:00
0c0e269e72 youspell (thanks bar) 2005-10-05 00:52:27 +00:00
70b6f97abe 2005-10-04 jrandom
* Syndie patch for single user remote archives (thanks nickless_head!)
    * Handle an invalid netDb store (thanks Complication!)
2005-10-04 23:43:05 +00:00
0013677b83 v2mail, not v2mail2 2005-10-04 23:34:19 +00:00
a98ceda64d postmans changes for i2pmail stuff 2005-10-04 23:33:15 +00:00
91ea1d0395 include two specific issues with freenet 2005-10-04 20:18:18 +00:00
4aa65c3bb3 2005-10-04 jrandom
* Further reduction in unnecessary streaming packets.
2005-10-04 07:36:25 +00:00
0a1f59940a 2005-10-03 jrandom
* Properly reject unroutable IP addresses *cough*
2005-10-04 02:05:52 +00:00
f540dc798b * Changed default update delay to twelve hours, and enforced a minimum
delay of one hour.
2005-10-04 00:27:34 +00:00
30f6f26a68 * Implemented a Syndie auto-updater. 2005-10-03 18:55:09 +00:00
ea3bf3ffc8 might as well commit this draft 2005-10-03 06:21:08 +00:00
831d5ac70c not used 2005-10-03 06:20:47 +00:00
1962867ad9 * Actually implement the bit that returns a 304 if archive.txt hasn't changed. 2005-10-03 02:54:34 +00:00
6019a03029 * 2005-10-01 0.6.1.1 released 2005-10-01 19:20:09 +00:00
df5736f571 * Add a notModified flag to Eepget and Eepget status listeners. 2005-10-01 00:57:32 +00:00
9a73c6defe * Support conditional get for remote archive imports. 2005-09-30 23:42:28 +00:00
9dfa87ba47 2005-09-30 jrandom
* Killed three more streaming lib bugs, one of which caused excess packets
      to be transmitted (dupacking dupacks), one that was the root of many of
      the old hung streams (shrinking highest received), and another that was
      releasing data too soon.
2005-09-30 23:12:57 +00:00
934a269753 2005-09-30 jrandom
* Only allow autodetection of our IP address if we haven't received an
      inbound connection in the last two minutes.
    * Increase the default max streaming resends to 8 from 5 (and down from
      the earlier 10)
2005-09-30 20:29:19 +00:00
1c0dfc242b * Fix history.txt formatting. 2005-09-30 17:51:32 +00:00
3bc3e5d47e * Export petnames from syndie to the router's petname db instead of userhosts.txt. 2005-09-30 07:32:46 +00:00
55869af2cc 2005-09-29 jrandom
* Support noreseed.i2p in addition to .i2pnoreseed for disabling automatic
      reseeding - useful on OSes that make it hard to create dot files.
      Thanks Complication (and anon)!
    * Fixed the installer version string (thanks Frontier!)
    * Added cleaner rejection of invalid IP addresses, shitlist those who send
      us invalid IP addresses, verify again that we are not sending invalid IP
      addresses, and log an error if it happens. (Thanks Complication, ptm,
      and adab!)
2005-09-30 07:17:56 +00:00
9f336dd05b Provide a store method on PetNameDB that takes no arguments, and writes the db back to where it was loaded from. 2005-09-30 05:28:31 +00:00
411ca5e6c3 Ignore case when checking network name. 2005-09-30 05:20:41 +00:00
c528e4db03 * 2005-09-29 0.6.1 released
2005-09-29  jrandom
    * Let syndie users modify their metadata.
    * Reseed the router on startup if there aren't enough peer references
      known locally.  This can be disabled by creating the file .i2pnoreseed
      in your home directory, and the existing detection and reseed handling
      on the web interface is unchanged.
2005-09-29 19:24:43 +00:00
848ead7683 * 2005-09-29 0.6.1 released
2005-09-29  jrandom
    * Let syndie users modify their metadata.
    * Reseed the router on startup if there aren't enough peer references
      known locally.  This can be disabled by creating the file .i2pnoreseed
      in your home directory, and the existing detection and reseed handling
      on the web interface is unchanged.
2005-09-29 19:19:22 +00:00
1b8419b9b5 added tracker-fr.i2p 2005-09-29 03:54:30 +00:00
900420719e 2005-09-28 jrandom
* Fix for at least some (all?) of the wrong stream errors in the streaming
      lib
2005-09-28 09:17:54 +00:00
ef7d1ba964 2005-09-27 jrandom
* Properly suggest filenames for attachments in Syndie (thanks all!)
    * Fixed the Syndie authorization scheme for single user vs. multiuser
2005-09-27 22:42:49 +00:00
ab1654c784 ; (1.181) added syncline.i2p, cerebrum.i2p, news.underscore.i2p,
;               onionforum.i2p, frostmirror.i2p, ptm.i2p, gloinsblog.i2p
;               underscore.i2p, mac7.i2p, wiht.i2p, jazzy.i2p, trwcln.i2p
2005-09-27 22:21:05 +00:00
24bad8e4bb 2005-09-26 jrandom
* I2PTunnel bugfix (thanks Complication!)
    * Increase the SSU cwin slower during congestion avoidance (at k/cwin^2
      instead of k/cwin)
    * Limit the number of inbound SSU sessions being built at once (using
      half of the i2np.udp.maxConcurrentEstablish config prop)
    * Don't shitlist on a message send failure alone (unless there aren't any
      common transports).
    * More careful bandwidth bursting
2005-09-27 07:17:40 +00:00
f6d8200bc8 oops (thanks Complication!) 2005-09-27 00:56:49 +00:00
aef33548b3 2005-09-26 jrandom
* Reworded the SSU introductions config section (thanks duck!)
    * Force identity content encoding for I2PTunnel httpserver requests
      (thanks redzara!)
    * Further x-i2p-gzip bugfixes for the end of streams
    * Reduce the minimum bandwidth limits to 3KBps steady and burst (though
      I2P's performance at 3KBps is another issue)
    * Cleaned up some streaming lib structures
2005-09-26 23:45:52 +00:00
56ecdcce82 2005-09-25 jrandom
* Allow reseeding on the console if the netDb knows less than 30 peers,
      rather than less than 10 (without internet connectivity, we keep the
      last 15 router references)
    * Reenable the x-i2p-gzip HTTP processing by default, flushing the stream
      more aggressively.
    * Show the status that used to be called "ERR-Reject" as "OK (NAT)"
    * Reduced the default maximum number of streaming lib resends of a packet
      (10 retransmits is a bit much with a reasonable RTO)
2005-09-25 23:52:58 +00:00
b9b59ff95f 2005-09-25 Complication
* Better i2paddresshelper handling in the I2PTunnel httpclient, plus a new
      conflict resolution page if the i2paddresshelper parameter differs from
      an existing name to destination mapping.
2005-09-25  jrandom
    * Fix a long standing streaming lib bug (in the inactivity detection code)
    * Improved handling of initial streaming lib packet retransmissions to
      kill the "lost first packet" bug (where a page shows up with the first
      few KB missing)
    * Add support for initial window sizes greater than 1 - useful for
      eepsites to transmit e.g. 4 packets full of data along with the initial
      ACK, thereby cutting down on the rtt latency.  The congestion window
      size can and does still shrink down to 1 packet though.
    * Adjusted the streaming lib retransmission calculation algorithm to be
      more TCP-like.
2005-09-25 09:28:59 +00:00
aa9dd3e5c6 mention syndie bug stuff (good idea jnymo) 2005-09-24 04:08:42 +00:00
30bd659149 2005-09-21 redzara
* Use ISO-8859-1 for the susidns xml
2005-09-21 23:01:00 +00:00
3286ca49c8 2005-09-21 susi
* Bugfix in susidns for deleting entries
2005-09-21  jrandom
    * Add support for HTTP POST to EepGet
    * Use HTTP POST for syndie bulk fetches, since there's a lot of data to
      put in that URL.
2005-09-21 06:43:04 +00:00
7700d12178 damn thee, syntax 2005-09-20 03:43:57 +00:00
557b7e3f2e only build exe files on ant dist or ant installer 2005-09-20 03:38:14 +00:00
3e1e9146e1 don't build the exe files on x86_64 or osx 2005-09-20 03:17:06 +00:00
40d8d1aac1 * Made MetaNamingService the default naming service. 2005-09-19 00:56:47 +00:00
1457b8efba include the lib64 wrapper (thanks mule) 2005-09-19 00:36:59 +00:00
3821e80ac8 2005-09-18 jrandom
* Added support for pure 64bit linux with jbigi and the java service
      wrapper (no need for jcpuid if we're on os.arch=amd64).  Thanks mule
      et al for help testing!
    * UI cleanup in Syndie (thanks gloin and bar!)
2005-09-18 23:08:16 +00:00
d40bb459ea * Get the PetNameDB for the PetNameNamingService from the router context. 2005-09-18 22:36:10 +00:00
edf04f07c9 * Implemented a MetaNamingService. 2005-09-18 08:50:56 +00:00
2bdea23986 * Updated history.txt. 2005-09-18 05:41:46 +00:00
6be0c4b694 * Moved PetName and PetNameDB to core.
* Implement PetNameNamingService.
2005-09-18 05:28:51 +00:00
2a272f465c * 2005-09-17 0.6.0.6 released
2005-09-17  jrandom
    * Clean up syndie a bit more and bundle a default introductory post with
      both new installs and updates.
    * Typo fixes on the console (thanks bar!)
2005-09-18 01:29:58 +00:00
a8ecd32b45 2005-09-17 jrandom
* Updated the bandwidth limiter to use two tiers of bandwidth - our normal
      steady state rate, plus a new limit on how fast we transfer when
      bursting.  This is different from the old "burst as fast as possible
      until we're out of tokens" policy, and should help those with congested
      networks.  See /config.jsp to manage this rate.
    * Bugfixes in Syndie to handle missing cache files (no data was lost, the
      old posts just didn't show up).
    * Log properly in EepPost
2005-09-17 23:01:44 +00:00
20c42a175d 2005-09-17 jrandom
* Bugfixes in Syndie to handle missing cache files (no data was lost, the
      old posts just didn't show up).
    * Log properly in EepPost
2005-09-17 20:08:25 +00:00
d6c3ffde87 2005-09-17 jrandom
* Added the natively compiled jbigi and patched java service wrapper for
      OS X.  Thanks Bill Dorsey for letting me use your machine!
    * Don't build i2p.exe or i2pinstall.exe when run on OS X machines, as we
      don't bundle the binutils necessary (and there'd be a naming conflict
      if we did).
    * Added 'single user' functionality to syndie - if the single user
      checkbox on the admin page is checked, all users are allowed to control
      the instance and sync up with remote syndie nodes.
    * Temporarily disable the x-i2p-gzip in i2ptunnel until it is more closely
      debugged.
2005-09-17 07:31:48 +00:00
177e0ae6a3 2005-09-16 jrandom
* Reject unroutable IPs in SSU like we do for the TCP transport (unless
      you have i2np.udp.allowLocal=true defined - useful for private nets)
2005-09-16 21:24:42 +00:00
dab1b4d256 2005-09-16 jrandom
* Adjust I2PTunnelHTTPServer so it can be used for outproxy operators
      (just specify the spoofed host as an empty string), allowing them to
      honor x-i2p-gzip encoding.
    * Let windows users build the exes too (thanks bar and redzara!)
    * Allow I2PTunnel httpserver operators to disable gzip compression on
      individual tunnels with the i2ptunnel.gzip=false client option
      (good idea susi!)
2005-09-16 18:28:26 +00:00
3aba12631b use the logger, not stdout/stderr 2005-09-16 06:58:55 +00:00
cfee6430d4 xml 2005-09-16 04:54:48 +00:00
6b96df1cec runplain.sh, not startRouter.sh 2005-09-16 04:50:28 +00:00
deecfa5047 no message 2005-09-16 04:40:07 +00:00
6ca3f01038 launch4j 2005-09-16 04:34:59 +00:00
d89f589f2b 2005-09-16 jrandom
* Added the i2p.exe and i2pinstall.exe for windows users, using launch4j.
    * Added runplain.sh for *nix/osx users having problems using the java
      service wrapper (called from the install dir as: sh runplain.sh)
    * Bundle susidns and syndie, with links on the top nav
    * Have I2PTunnelHTTPClient and I2PTunnelHTTPServer use the x-i2p-gzip
      content-encoding (if offered), reducing the payload size before it
      reaches the streaming lib.  The existing compression is at the i2cp
      level, so we've been packetizing 4KB of uncompressed data and then
      compressing those messages, rather than compressing and then packetizing
      4KB of compressed data.  This should reduce the number of round trips
      to fetch web pages substantially.
    * Adjust the startup and timing of the addressbook so that susidns always
      has config to work off, and expose a method for susidns to tell it to
      reload its config and rerun.
2005-09-16 04:12:24 +00:00
8c1895e04f imported fixed susidns 2005-09-16 04:04:40 +00:00
c3d0132a98 test cvs again... 2005-09-15 05:57:28 +00:00
d955279d17 minor news (and test cvs...) 2005-09-15 05:56:10 +00:00
76266dce0d 2005-09-15 jrandom
* Error handling for failed intro packets (thanks red.hand!)
    * More carefully verify intro addresses
2005-09-15 05:39:31 +00:00
5694206b35 2005-09-13 jrandom
* More careful error handling with introductions (thanks dust!)
    * Fix the forceIntroducers checkbox on config.jsp (thanks Complication!)
    * Hide the shitlist on the summary so it doesn't confuse new users.
2005-09-13 23:02:35 +00:00
4293a18726 2005-09-12 comwiz
* Migrated the router tests to junit
2005-09-13 09:06:07 +00:00
9865af4174 2005-09-12 jrandom
* Removed guaranteed delivery mode entirely (so existing i2phex clients
      using it can get the benefits of mode=best_effort).  Guaranteed delivery
      is offered at the streaming lib level.
    * Improve the peer selection code for peer testing, as everyone now
      supports tests.
    * Give the watchdog its fangs - if it detects obscene job lag or if
      clients have been unable to get a leaseSet for more than 5 minutes,
      restart the router.  This was disabled a year ago due to spurious
      restarts, and can be disabled by "watchdog.haltOnHang=false", but the
      cause of the spurious restarts should be gone.
2005-09-13 03:32:29 +00:00
c8c109093d 2005-09-12 jrandom
* Bugfix for skewed store which could kill a UDP thread (causing complete
      comm failure and eventual OOM)
2005-09-13 01:12:43 +00:00
b5784d6025 2005-09-12 jrandom
* More aggressively publish updated routerInfo.
    * Expose the flag to force SSU introductions on the router console
    * Don't give people the option to disable SNTP time sync, at least not
      through the router console, because there is no reason to disable it.
      No, not even if your OS is "ntp synced", because chances are, its not.
2005-09-13 00:11:56 +00:00
31bdb8909a tino.i2p and fproxy.tino.i2p 2005-09-12 22:40:17 +00:00
ee921c22ae use the low level rates (thanks bar / complication) 2005-09-12 02:58:13 +00:00
172ffd0434 use the OS time, since it doesn't skew as much (especially on startup) 2005-09-11 04:37:15 +00:00
d9b4406c09 2005-09-10 jrandom
* Test the router's reachability earlier and more aggressively
    * Use the low level bandwidth limiter's rates for the router console, and
      if the router has net.i2p.router.transport.FIFOBandwidthLimiter=INFO in
      the logger config, keep track of the 1 second transfer rates as the stat
      'bw.sendBps1s' and 'bw.recvBps1s', allowing closer monitoring of burst
      behavior.
2005-09-11 03:22:51 +00:00
8ac0e85df4 updated to hq.postman.i2p 2005-09-11 01:40:15 +00:00
249ccd5e3c now that its all implemented... 2005-09-10 23:18:41 +00:00
727d76d43e deal with posts containing no tags by using the implicit tag "[none]" (thanks ardvark!) 2005-09-10 06:07:25 +00:00
44770b7c07 2005-09-09 jrandom
* Added preliminary support for NAT hole punching through SSU introducers
    * Honor peer test results from peers that we have an SSU session with if
      those sessions are idle for 3 minutes or more.
2005-09-10 04:30:36 +00:00
b5d571c75f 2005-09-09 cervantes
* New build due to change in build number :P (thanks ugha!)
2005-09-10 01:13:49 +00:00
da56d83716 First pass at a new naming system. Probably the last as well. So sad :). 2005-09-09 19:38:43 +00:00
f777e213ce search.i2p 2005-09-08 05:08:33 +00:00
79906f5a7d added search.i2p 2005-09-08 05:01:01 +00:00
54074e76b5 2005-09-07 BarkerJr
* HTML cleanup for the router console (thanks!)
2005-09-07  jrandom
    * Lay the foundation for 'client routers' - the ability for peers to opt
      out of participating in tunnels entirely due to firewall/NAT issues.
      Individual routers have control over where those peers are used in
      tunnels - in outbound or inbound, exploratory or client tunnels, or
      none at all.  The defaults with this build are to simply act as before -
      placing everyone as potential participants in any tunnel.
    * Another part of the foundation includes the option for netDb
      participants to refuse to answer queries regarding peers who are marked
      as unreachable, though this too is disabled by default (meaning the
      routerInfo is retrievable from the netDb).
2005-09-07 22:31:11 +00:00
c2ea8db683 Look for names in privatehosts.txt as well as userhosts.txt and hosts.txt. 2005-09-07 02:07:41 +00:00
744671a518 Adjusted wording on the bandwidth limiter controls to reflect new router defaults 2005-09-07 01:13:56 +00:00
7f5b127bbc fix0rz. 2005-09-06 20:45:21 +00:00
89eff0c628 tyop 2005-09-06 20:35:28 +00:00
177aeebb1c stuff 2005-09-06 20:15:55 +00:00
e0e6bde4a5 throw css around like mad (very minimal stylesheet in place) 2005-09-06 20:05:09 +00:00
e6b145716f allow publishing to a remote archive automatically when posting (optionally) with 0 additional clicks
allow transparently attaching any 'public' pet names in your addressbook to a blog post (with a checkbox)
2005-09-06 03:03:55 +00:00
f958342704 admin page - no more editing config props manually (w3wt) 2005-09-05 20:53:25 +00:00
5a1f738505 2005-09-05 jrandom
* Expose the HTTP headers to EepGet status listeners
    * Handle DSA key failures properly (if the signature is not invertable, it
      is obviously invalid)
2005-09-05 19:29:55 +00:00
8147cdf40c 2005-09-05 jrandom
* Expose the HTTP headers to EepGet status listeners
    * Handle DSA key failures properly (if the signature is not invertable, it
      is obviously invalid)
also, syndie now properly detects whether the remote archive can send a filtered export.zip
by examining the HTTP headers for X-Syndie-Export-Capable: true.  If the remote archive
does not set that header (and neither freesites, nor apache or anything other than the ArchiveServlet will),
it uses individual HTTP requests for individual blog posts and metadata fetches.
2005-09-05 19:27:08 +00:00
6afc64ac39 deal with locations that have : in them (aka http://glog.i2p/archive/archive.txt) 2005-09-05 17:09:19 +00:00
61b8e3598b added rss2.0 support via rss.jsp
rss.jsp can in turn receive all the filters that index.jsp can - e.g. ?blog=blah or ?selector=group://foo,
and by default returns the latest 10 values (overridden with ?wanted=15).  If you want it to pull
with a user's blog's preferences (filters, groups, etc), you can specify ?login=user&password=password
2005-09-05 05:33:33 +00:00
3bb445ff40 better filtering/ignoring
ui improvements (per isamoor's suggestions)
more petname integration
2005-09-05 01:26:19 +00:00
59a8037599 allow exporting eepsite destinations from the syndie database into userhosts.txt (so the eepproxy can get it) 2005-09-05 00:00:11 +00:00
09cb5fad59 allow you to bookmark syndie archives and later recall those bookmarks on the remote page 2005-09-04 22:43:22 +00:00
ee8e45ecf7 allow web based control of who gets to access remote repositories.
if the prop "syndie.remotePassword" is set, users can enter it while viewing their metadata
2005-09-04 21:51:17 +00:00
339868838d thanks BarkerJr :) 2005-09-04 20:26:42 +00:00
c5579fa349 (the filtered blogs may be out of order) 2005-09-04 19:33:00 +00:00
d4a859547c 2005-09-04 jrandom
* Don't persist peer profiles until we are shutting down, as the
      persistence process gobbles RAM and wall time.
    * Bugfix to allow you to check/uncheck the sharedClient setting on the
      I2PTunnel web interface.
    * Be more careful when expiring a failed tunnel message fragment so we
      don't drop the data while attempting to read it.
2005-09-04 19:15:49 +00:00
779aa240d2 added glog.i2p 2005-09-03 04:07:50 +00:00
9aaad00383 0.6.0.5 2005-09-02 19:10:05 +00:00
6422f7ef78 2005-09-02 jrandom
* Don't refuse to send a netDb store if the targetted peer has failed a
      bit (the value was an arbitrary amount).
    * Logging changes
2005-09-02 18:34:14 +00:00
3e51584b3c 0.6.0.4 2005-09-01 20:27:35 +00:00
4ff8a53084 2005-09-01 jrandom
* Don't send out a netDb store of a router if it is more than a few hours
      old, even if someone asked us for it.
2005-09-01 06:55:00 +00:00
ccb73437c4 2005-08-31 jrandom
* Don't publish leaseSets to the netDb if they will never be looked for -
      namely, if they are for destinations that only establish outbound
      streams.  I2PTunnel's 'client' and 'httpclient' proxies have been
      modified to tell the router that it doesn't need to publish their
      leaseSet (by setting the I2CP config option 'i2cp.dontPublishLeaseSet'
      to 'true').
    * Don't publish the top 10 peer rankings of each router in the netdb, as
      it isn't being watched right now.
2005-09-01 00:26:20 +00:00
b43114f61b 2005-08-31 jrandom
* Don't publish leaseSets to the netDb if they will never be looked for -
      namely, if they are for destinations that only establish outbound
      streams.  I2PTunnel's 'client' and 'httpclient' proxies have been
      modified to tell the router that it doesn't need to publish their
      leaseSet (by setting the I2CP config option 'i2cp.dontPublishLeaseSet'
      to 'true').
    * Don't publish the top 10 peer rankings of each router in the netdb, as
      it isn't being watched right now.
2005-09-01 00:20:16 +00:00
9bd87ab511 make it work with any host charset or content charset 2005-08-31 09:50:23 +00:00
b6ea55f7ef more error handling (thanks frosk) 2005-08-30 02:39:37 +00:00
5f18cec97d 2005-08-29 jrandom
* Added the new test Floodfill netDb
2005-08-30 02:04:17 +00:00
3ba921ec0e 2005-08-29 jrandom
* Added the new test Floodfill netDb
2005-08-30 01:59:11 +00:00
e313da254c 2005-08-27 jrandom
* Minor logging and optimization tweaks in the router and SDK
    * Use ISO-8859-1 in the XML files (thanks redzara!)
    * The consolePassword config property can now be used to bypass the router
      console's nonce checking, allowing CLI restarts
2005-08-27 22:46:22 +00:00
8660cf0d74 2005-08-27 jrandom
* Minor logging and optimization tweaks in the router and SDK
    * Use ISO-8859-1 in the XML files (thanks redzara!)
    * The consolePassword config property can now be used to bypass the router
      console's nonce checking, allowing CLI restarts
2005-08-27 22:15:35 +00:00
e0bfdff152 TZ asap 2005-08-25 21:08:13 +00:00
c27aed3603 fix up the entryId calc 2005-08-25 21:07:18 +00:00
cdc6002f0e no message 2005-08-25 21:01:15 +00:00
4cf3d9c1a2 HTTP file upload (rfc 1867) helper 2005-08-25 21:00:09 +00:00
0473e08e21 remote w0rks 2005-08-25 20:59:46 +00:00
346faa3de2 2005-08-24 jrandom
* Catch errors with corrupt tunnel messages more gracefully (no need to
      kill the thread and cause an OOM...)
    * Don't skip shitlisted peers for netDb store messages, as they aren't
      necessarily shitlisted by other people (though they probably are).
    * Adjust the netDb store per-peer timeout based on each particular peer's
      profile (timeout = 4x their average netDb store response time)
    * Don't republish leaseSets to *failed* peers - send them to peers who
      replied but just didn't know the value.
    * Set a 5 second timeout on the I2PTunnelHTTPServer reading the client's
      HTTP headers, rather than blocking indefinitely.  HTTP headers should be
      sent entirely within the first streaming packet anyway, so this won't be
      a problem.
    * Don't use the I2PTunnel*Server handler thread pool by default, as it may
      prevent any clients from accessing the server if the handlers get
      blocked by the streaming lib or other issues.
    * Don't overwrite a known status (OK/ERR-Reject/ERR-SymmetricNAT) with
      Unknown.
2005-08-24 22:55:25 +00:00
5ec6dca64d 2005-08-23 jrandom
* Removed the concept of "no bandwidth limit" - if none is specified, its
      16KBps in/out.
    * Include ack packets in the per-peer cwin throttle (they were part of the
      bandwidth limit though).
    * Tweak the SSU cwin operation to get more accurrate estimates under
      congestions.
    * SSU improvements to resend more efficiently.
    * Added a basic scheduler to eepget to fetch multiple files sequentially.
2005-08-23 22:43:51 +00:00
1a6b49cfb8 2005-08-23 jrandom
* Removed the concept of "no bandwidth limit" - if none is specified, its
      16KBps in/out.
    * Include ack packets in the per-peer cwin throttle (they were part of the
      bandwidth limit though).
    * Tweak the SSU cwin operation to get more accurrate estimates under
      congestions.
    * SSU improvements to resend more efficiently.
    * Added a basic scheduler to eepget to fetch multiple files sequentially.
2005-08-23 21:25:49 +00:00
c7b75df390 Added announcement about the new Irc2P server at irc.freshcoffee.i2p 2005-08-22 13:03:11 +00:00
f97c09291b 0.6.0.3 2005-08-21 19:21:50 +00:00
8f2a5b403c * 2005-08-21 0.6.0.3 released
2005-08-21  jrandom
    * If we already have an established SSU session with the Charlie helping
      test us, cancel the test with the status of "unknown".
2005-08-21 18:39:05 +00:00
ea41a90eae sanity checking 2005-08-21 18:37:57 +00:00
b1dd29e64d added syndie.i2p and syndiemedia.i2p 2005-08-21 18:33:58 +00:00
46e47c47ac ewps 2005-08-21 18:19:22 +00:00
b7bf431f0d [these are not the droids you are looking for] 2005-08-21 18:08:05 +00:00
7f432122d9 added irc.freshcoffee.i2p (new IRC server on the irc2p network) 2005-08-20 01:19:51 +00:00
e7be8c6097 Added references to the new irc2p server: irc.freshcoffee.i2p 2005-08-20 01:18:38 +00:00
adf56a16e1 2005-08-17 jrandom
* Revise the SSU peer testing protocol so that Bob verifies Charlie's
      viability before agreeing to Alice's request.  This doesn't work with
      older SSU peer test builds, but is backwards compatible (older nodes
      won't ask newer nodes to participate in tests, and newer nodes won't
      ask older nodes to either).
2005-08-17 20:16:27 +00:00
11204b8a2b 2005-08-17 jrandom
* Revise the SSU peer testing protocol so that Bob verifies Charlie's
      viability before agreeing to Alice's request.  This doesn't work with
      older SSU peer test builds, but is backwards compatible (older nodes
      won't ask newer nodes to participate in tests, and newer nodes won't
      ask older nodes to either).
2005-08-17 20:05:01 +00:00
cade27dceb added surrender.adab.i2p 2005-08-17 00:42:15 +00:00
5597d28e59 Removing references to irc.duck.i2p, adding references to irc.arcturus.i2p, and replacing current ircProxy default destination string with "irc.postman.i2p,irc.arcturus.i2p" 2005-08-16 09:35:58 +00:00
0502fec432 added terror.i2p 2005-08-15 18:44:04 +00:00
a6714fc2de Adding irc.arcturus.i2p, a new server for the soon-to-be Irc2P network 2005-08-14 15:52:12 +00:00
1219dadbd5 2005-08-12 jrandom
* Keep detailed stats on the peer testing, publishing the results in the
      netDb.
    * Don't overwrite the status with 'unknown' unless we haven't had a valid
      status in a while.
    * Make sure to avoid shitlisted peers for peer testing.
    * When we get an unknown result to a peer test, try again soon afterwards.
    * When a peer tells us that our address is different from what we expect,
      if we've done a recent peer test with a result of OK, fire off a peer
      test to make sure our IP/port is still valid.  If our test is old or the
      result was not OK, accept their suggestion, but queue up a peer test for
      later.
    * Don't try to do a netDb store to a shitlisted peer, and adjust the way
      we monitor netDb store progress (to clear up the high netDb.storePeers
      stat)
2005-08-12 23:54:46 +00:00
77b995f5ed 2005-08-10 jrandom
* Deployed the peer testing implementation to be run every few minutes on
      each router, as well as any time the user requests a test manually.  The
      tests do not reconfigure the ports at the moment, merely determine under
      what conditions the local router is reachable.  The status shown in the
      top left will be "ERR-SymmetricNAT" if the user's IP and port show up
      differently for different peers, "ERR-Reject" if the router cannot
      receive unsolicited packets or the peer helping test could not find a
      collaborator, "Unknown" if the test has not been run or the test
      participants were unreachable, or "OK" if the router can receive
      unsolicited connections and those connections use the same IP and port.
2005-08-10 23:55:40 +00:00
2f53b9ff68 0.6.0.2 2005-08-09 18:55:31 +00:00
d84d045849 deal with full windows without *cough* NPEs
(how many times can I cvs rtag -F before going crazy?)
2005-08-08 21:20:08 +00:00
d8e72dfe48 foo 2005-08-08 20:49:17 +00:00
88b9f7a74c "ERROR [eive on 8887] uter.transport.udp.UDPReceiver: Dropping inbound packet with 1 queued for 1912 packet handlers: Handlers: 3 handler 0 state: 2 handler 1 state: 2 handler 2 state: 2"
state = 2 means all three handlers are blocking on udpReceiver.receive())
this can legitimately happen if the bandwidth limiter or router throttle chokes the receive for >= 1s.
2005-08-08 20:42:13 +00:00
6a19501214 2005-08-08 jrandom
* Add a configurable throttle to the number of concurrent outbound SSU
      connection negotiations (via i2np.udp.maxConcurrentEstablish=4).  This
      may help those with slow connections to get integrated at the start.
    * Further fixlets to the streaming lib
2005-08-08 20:35:50 +00:00
ba30b56c5f 2005-08-07 Complication
* Display the average clock skew for both SSU and TCP connections
2005-08-07  jrandom
    * Fixed the long standing streaming lib bug where we could lose the first
      packet on retransmission.
    * Avoid an NPE when a message expires on the SSU queue.
    * Adjust the streaming lib's window growth factor with an additional
      Vegas-esque congestion detection algorithm.
    * Removed an unnecessary SSU session drop
    * Reduced the MTU (until we get a working PMTU lib)
    * Deferr tunnel acceptance until we know how to reach the next hop,
      rejecting it if we can't find them in time.
    * If our netDb store of our leaseSet fails, give it a few seconds before
      republishing.
2005-08-07 19:31:58 +00:00
a375e4b2ce added more postman services (w3wt) 2005-08-07 19:27:22 +00:00
44fd71e17f added i2p-bt.postman.i2p 2005-08-05 21:20:30 +00:00
b41c378de9 Removed reference and link to Invisiblechat/IIP from the router console greeting page (because IIP's dead, Jim... how many times does it need to be said?) and added irc.postman.i2p. 2005-08-05 19:20:52 +00:00
4ce6b308b3 * 2005-08-03 0.6.0.1 released
2005-08-03  jrandom
    * Backed out an inadvertant change to the netDb store redundancy factor.
    * Verify tunnel participant caching.
    * Logging cleanup
2005-08-03 18:58:12 +00:00
72c6e7d1c5 2005-08-01 duck
* Update IzPack to 3.7.2 (build 2005.04.22). This fixes bug #82.
2005-08-02 03:26:51 +00:00
7ca3f22e77 2005-08-01 duck
* Update IzPack to 3.7.2 (build 2005.04.22)
      This fixes bug #82
2005-08-02 03:25:51 +00:00
59790dafef 2005-08-01 duck
* Fix an addressbook NPE when a new hostname from the master addressbook
      didn't exist in the router addressbook.
    * Fix an addressbook bug which caused subscriptions not to be parsed at
      all. (Oops!)
2005-08-01 13:35:11 +00:00
7227cae6ef 2005-08-01 duck
* Fix an addressbook NPE when a new hostname from the master addressbook
      didn't exist in the router addressbook.
    * Fix an addressbook bug which caused subscriptions not to be parsed at
      all. (Oops!)
2005-08-01 13:34:10 +00:00
03bba51c1e * Fixed some issues with the merge logic that caused addressbooks to be written to disk even when unmodified.
* Fixed a bug that could result in a downloaded remote addressbook not being deleted, halting the update process.
2005-08-01 03:32:37 +00:00
0637050cbc No real reason for eepget to retry, addressbook will try again in an hour, and it makes updates take an absurdly long time. 2005-08-01 00:10:54 +00:00
7f58a68c5a Whoops! Forgot the new build file. I broke cvs! 2005-07-31 22:49:25 +00:00
8120b0397c Move addressbook off URL and on to EepGet. Should no longer leak dns lookups, but now only supports conditional GET with HTTP 1.1. If that's a big problem, it can be fixed in future. 2005-07-31 22:19:10 +00:00
fbe42b7dce Added HTTP 1.1 conditional GET support to EepGet. 2005-07-31 22:17:10 +00:00
def24e34ad 2005-07-31 jrandom
* Adjust the netDb search and store per peer timeouts to match the average
      measured per peer success times, rather than huge fixed values.
    * Optimized and reverified the netDb peer selection / retrieval process
      within the kbuckets.
    * Drop TCP connections that don't have any useful activity in 10 minutes.
    * If i2np.udp.fixedPort=true, never change the externally published port,
      even if we are autodetecting the IP address.
(also includes most of the new peer/NAT testing, but thats not used atm)
2005-07-31 21:35:26 +00:00
593253e6a3 update compilation target 2005-07-31 01:11:12 +00:00
56dd4cb8b5 * added luckypunk.i2p to hosts.txt 2005-07-30 02:27:12 +00:00
10c6f67500 oops 2005-07-28 20:33:27 +00:00
5c1f968afa no message 2005-07-27 20:16:44 +00:00
aaaf437d62 skip properly (DataHelper.read confusion) 2005-07-27 20:15:35 +00:00
a8a866b5f6 * 2005-07-27 0.6 released
2005-07-27  jrandom
    * Enabled SSU as the default top priority transport, adjusting the
      config.jsp page accordingly.
    * Add verification fields to the SSU and TCP connection negotiation (not
      compatible with previous builds)
    * Enable the backwards incompatible tunnel crypto change as documented in
      tunnel-alt.html (have each hop encrypt the received IV before using it,
      then encrypt it again before sending it on)
    * Disable the I2CP encryption, leaving in place the end to end garlic
      encryption (another backwards incompatible change)
    * Adjust the protocol versions on the TCP and SSU transports so that they
      won't talk to older routers.
    * Fix up the config stats handling again
    * Fix a rare off-by-one in the SSU fragmentation
    * Reduce some unnecessary netDb resending by inluding the peers queried
      successfully in the store redundancy count.
2005-07-27 19:03:43 +00:00
aeb8f02269 2005-07-22 jrandom
* Use the small thread pool for I2PTunnelHTTPServer (already used for
      I2PTunnelServer)
    * Minor memory churn reduction in I2CP
    * Small stats update
2005-07-23 00:15:56 +00:00
45767360ab 2005-07-21 jrandom
* Fix in the SDK for a bug which would manifest itself as misrouted
      streaming packets when a destination has many concurrent streaming
      connections (thanks duck!)
    * No more "Graceful shutdown in -18140121441141s"
2005-07-21 22:37:14 +00:00
3563aa2e4d 2005-07-20 jrandom
* Allow the user to specify an external port # for SSU even if the external
      host isn't specified (thanks duck!)
2005-07-20 19:24:47 +00:00
843d5b625a 2005-07-19 jrandom
* Further preparation for removing I2CP crypto
    * Added some validation to the DH key agreement (thanks $anon)
    * Validate tunnel data message expirations (though not really a problem,
      since tunnels expire)
    * Minor PRNG threading cleanup
2005-07-19 21:00:25 +00:00
0f8ede85ca 2005-07-15 cervantes
* Added workaround for an odd win32 bug in the stats configuration
	  console page which meant only the first checkbox selection was saved.

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

2005-06-22  Comwiz
    * Migrate the core tests to junit
2005-07-04 20:44:17 +00:00
440cf2c983 2005-03-23 Comwiz
* Phase 1 of the unit test bounty completed. (The router build script was modified not to build the router
 tests because of a broken dependancy on the core tests. This should be fixed in
 phase 3 of the unit test bounty.)
2005-06-23 02:11:04 +00:00
adeb09576a util/PooledRandomSource.java 2005-06-03 20:23:32 +00:00
fd52bcf8cd added archive.i2p, www.fr.i2p, romster.i2p, marshmallow.i2p, openforums.i2p 2005-05-26 04:51:24 +00:00
c2696bba00 2005-05-25 duck
* Fixed PRNG bug (bugzilla #107)
2005-05-25 21:32:38 +00:00
fef9d57483 removed duplicate manveru.i2p 2005-05-10 05:53:18 +00:00
c250692ef0 added bittorrent.i2p - new home for brittanytracker 2005-05-04 05:52:55 +00:00
2a6024e196 end of first round of ssu testing 2005-05-03 00:53:53 +00:00
835662b3c9 2005-05-01 jrandom
* Added a substantial optimization to the AES engine by caching the
      prepared session keys (duh).
2005-05-02 02:35:16 +00:00
6b5b880ab6 * replaced explicit NACKs and numACKs with ACK bitfields for high congestion links
* increased the maximum number of fragments allowed in a message from 31 to 127,
  reducing the maximum fragment size to 8KB and moving around some bits in the fragment
  info.  This is not backwards compatible.
* removed the old (hokey) congestion control description, replacing it with the TCP-esque
  algorithm implemented
note: the code for the ACK bitfields and fragment info changes have not yet been
implemented, so the old version of this document describes whats going on in the live net.
the new bitfields / fragment info should be deployed in the next day or so (hopefully :)
2005-05-01 20:08:08 +00:00
3de23d4206 2005-05-01 jrandom
* Cleaned up the peers page a bit more.
more udp stuff:
* add new config option: i2np.udp.alwaysPreferred=true to adjust the bidding
  so that UDP is picked first, even if a TCP connection exists
* fixed the initial clock skew problem (duh)
* reduced the MTU to 576 (largest nearly-universally-safe, and allows a
  tunnel message in 2 fragments)
* handle some races @ connection establishment (thanks duck!)
* if there are more ACKs than we can send in a packet, reschedule another
  ACK immediately
2005-05-01 17:21:48 +00:00
ea82f2a8cc oops (thanks newkid!) 2005-05-01 01:35:23 +00:00
b5ad7642bc 2005-04-30 jrandom
* Added a small new page to the web console (/peers.jsp) which contains
      the peer connection information.  This will be cleaned up a lot more
      before 0.6 is out, but its a start.
2005-05-01 00:48:15 +00:00
0fbe84e9f0 2005-04-30 jrandom
* Reduced some SimpleTimer churn
* add hooks for per-peer choking in the outbound message queue - if/when a
  peer reaches their cwin, no further messages will enter the 'active' pool
  until there are more bytes available.  other messages waiting (either later
  on in the same priority queue, or in the queues for other priorities) may
  take that slot.
* when we have a message acked, release the acked size to the congestion
  window (duh), rather than waiting for the second to expire and refill the
  capacity.
* send packets in a volley explicitly, waiting until we can allocate the full
  cwin size for that message
2005-04-30 23:26:18 +00:00
8063889d23 udp updates:
* more stats. including per-peer KBps (updated every second)
* improved blocking/timeout situations on the send queue
* added drop simulation hook
* provide logical RTO limits
2005-04-30 03:14:09 +00:00
6e1ac8e173 added elf.i2p, de-ebooks.i2p, i2pchan.i2p, longhorn.i2p 2005-04-29 22:26:12 +00:00
1b0bb5ea19 2005-04-29 jrandom
* Reduce the peer profile stat coallesce overhead by inlining it with the
      reorganize.
    * Limit each transport to at most one address (any transport that requires
      multiple entry points can include those alternatives in the address).
udp stuff:
* change the UDP transport's style from "udp" to "SSUv1"
* keep track of each peer's skew
* properly handle session reestablishment over an existing session, rather
  than requiring both sides to expire first
2005-04-29 06:24:12 +00:00
4ce51261f1 2005-04-28 jrandom
* More fixes for the I2PTunnel "other" interface handling (thanks nelgin!)
    * Add back the code to handle bids from multiple transports (though there
      is still only one transport enabled by default)
    * Adjust the router's queueing of outbound client messages when under
      heavy load by running the preparatory job in the client's I2CP handler
      thread, thereby blocking additional outbound messages when the router is
      hosed.
    * No need to validate or persist a netDb entry if we already have it
And for some udp stuff:
* only bid on what we know (duh)
* reduceed the queue size in the UDPSender itself, so that ACKs go
  through more quickly, leaving the payload messages to queue up in
  the outbound fragment scheduler
* rather than /= 2 on congestion, /= 2/3 (still AIMD, but less drastic)
* adjust the fragment selector so a wsiz throttle won't force extra
  volleys
* mark congestion when it occurs, not after the message has been
  ACKed
* when doing a round robin over the active messages, move on to the
  next after a full volley, not after each packet (causing less "fair"
  performance but better latency)
* reduced the lock contention in the inboundMessageFragments by
  moving the ack and complete queues to the ACKSender and
  MessageReceiver respectively (each of which have their own
  threads)
* prefer new and existing UDP sessions to new TCP sessions, but
  prefer existing TCP sessions to new UDP sessions
2005-04-28 21:54:27 +00:00
6e34d9b73e added amobius.i2p 2005-04-28 02:11:02 +00:00
6e01637400 added google.i2p 2005-04-27 21:30:53 +00:00
9a96798f9f added mrplod.i2p 2005-04-27 03:58:00 +00:00
c9db6f87d1 2005-04-25 smeghead
* Added button to router console for manual update checks.
    * Fixed bug in configupdate.jsp that caused the proxy port to be updated
      every time the form was submitted even if it hadn't changed.
2005-04-26 02:59:23 +00:00
567ce84e1e * randomized the shitlist duration (still with exponential backoff though)
* fail UDP sessions after two consecutive failed messages in different minutes
* honor UDP reconnections
2005-04-25 16:29:48 +00:00
cde7ac7e52 2005-04-24 jrandom
* Added a pool of PRNGs using a different synchronization technique,
      hopefully sufficient to work around IBM's PRNG bugs until we get our
      own Fortuna.
    * In the streaming lib, don't jack up the RTT on NACK, and have the window
      size bound the not-yet-ready messages to the peer, not the unacked
      message count (not sure yet whether this is worthwile).
    * Many additions to the messageHistory log.
    * Handle out of order tunnel fragment delivery (not an issue on the live
      net with TCP, but critical with UDP).
2005-04-24 18:44:59 +00:00
b2f0d17e94 2005-04-24 jrandom
* Added a pool of PRNGs using a different synchronization technique,
      hopefully sufficient to work around IBM's PRNG bugs until we get our
      own Fortuna.
    * In the streaming lib, don't jack up the RTT on NACK, and have the window
      size bound the not-yet-ready messages to the peer, not the unacked
      message count (not sure yet whether this is worthwile).
    * Many additions to the messageHistory log.
    * Handle out of order tunnel fragment delivery (not an issue on the live
      net with TCP, but critical with UDP).
and for udp stuff:
* implemented tcp-esque rto code in the udp transport
* make sure we don't ACK too many messages at once
* transmit fragments in a simple (nonrandom) order so that we can more easily
  adjust timeouts/etc.
* let the active outbound pool grow dynamically if there are outbound slots to
  spare
* use a simple decaying bloom filter at the UDP level to drop duplicate resent
  packets.
2005-04-24 18:42:02 +00:00
dae6be14b7 I removed those dumb platform specific makefiles. They weren't doing what they ought anyway. If there are platform specific issues, someone please tell me and I'll provide support for it here. Or patch it yourself.
And this is the big "Fix the Parser" patch.  It turns the sam_parse function in src/parse.c into something that actually works.  Generating the argument list from an incoming SAM thingy is a bit memory churn-y; perhaps when I have time I'll replace all those strdups with structures that simply track the (start,end) indices.
Oh and also I moved i2p-ping to the new system.  Which required 0 change in code.  All I did was fix the Makefile, and add shared library libtool support.  Anyway, so enjoy folks.  It's rare I'm this productive
- polecat
2005-04-23 03:28:40 +00:00
20cec857d2 signed with the latest 2005-04-21 16:26:46 +00:00
aum
739f694cfe Node shutdown now uses halt() 2005-04-21 03:10:16 +00:00
aum
84779002fb now builds a working Q console 2005-04-20 21:35:05 +00:00
df926fb60d * 2005-04-20 0.5.0.7 released 2005-04-20 20:14:17 +00:00
a2c7c5a516 2005-04-20 jrandom
* In the SDK, we don't actually need to block when we're sending a message
      as BestEffort (and these days, we're always sending BestEffort).
    * Pass out client messages in fewer (larger) steps.
    * Have the InNetMessagePool short circuit dispatch requests.
    * Have the message validator take into account expiration to cut down on
      false positives at high transfer rates.
    * Allow configuration of the probabalistic window size growth rate in the
      streaming lib's slow start and congestion avoidance phases, and default
      them to a more conservative value (2), rather than the previous value
      (1).
    * Reduce the ack delay in the streaming lib to 500ms
    * Honor choke requests in the streaming lib (only affects those getting
      insanely high transfer rates)
    * Let the user specify an interface besides 127.0.0.1 or 0.0.0.0 on the
      I2PTunnel client page (thanks maestro^!)
(plus minor udp tweaks)
2005-04-20 19:15:25 +00:00
aum
1861379d43 needed for QConsole 2005-04-20 18:54:39 +00:00
aum
408a344aae added QConsole 2005-04-20 18:53:08 +00:00
e9c1ed70d0 added sirup.i2p 2005-04-18 23:27:31 +00:00
916dcca2b0 * build with reference to the i2p.jar/mstreaming.jar/i2ptunnel.jar inline (building as necessary)
* removed unnecessary references to i2ptunnel (though i2ptunnelxmlobject still references i2ptunnelxmlwrapper)
2005-04-18 18:47:22 +00:00
aum
31e81bab17 fixed build failures 2005-04-18 18:12:32 +00:00
aum
6a5170c341 oops, forgot to add earlier 2005-04-18 18:05:50 +00:00
aum
42bff8093c removed obsolete ref to MiniHttpRequestHandlerBase, changed to MiniHttpRequestHandler 2005-04-18 18:04:12 +00:00
aum
d1df94f284 added needed html template files 2005-04-18 18:02:07 +00:00
aum
9cf1744291 restored images in binary mode 2005-04-18 17:24:33 +00:00
aum
f0545c8c9a removed images which were not checked in as binary 2005-04-18 17:22:27 +00:00
aum
ef9ed87d30 binary mode this time 2005-04-18 17:20:10 +00:00
aum
58ffd92a34 dammit, forgot binary mode 2005-04-18 17:19:25 +00:00
aum
418facc7e0 Added apps/q - the Q distributed file store framework, by aum 2005-04-18 17:03:21 +00:00
7f3c953e14 2005-04-17 sirup
* Added the possibility for i2ptunnel client and httpclient instances to
      have their own i2p session (and hence, destination and tunnels).  By
      default, tunnels are shared, but that can be changed on the web
      interface or with the sharedClient config option in i2ptunnel.config.
2005-04-17  jrandom
    * Marked the net.i2p.i2ptunnel.TunnelManager as deprecated.  Anyone use
      this?  If not, I want to drop it (lots of tiny details with lots of
      duplicated semantics).
2005-04-18 02:07:57 +00:00
addab1fa2a 2005-04-17 zzz
* Added new user-editable eepproxy error page templates.
2005-04-17  jrandom
    * Revamp the tunnel building throttles, fixing a situation where the
      rebuild may not recover, and defaulting it to unthrottled (users with
      slow CPUs may want to set "router.tunnel.shouldThrottle=true" in their
      advanced router config)
2005-04-17 23:23:20 +00:00
39343ce957 2005-04-16 jrandom
* Migrated to Bouncycastle's SHA256 and HMAC implementations for efficiency
2005-04-17 01:04:06 +00:00
7389cec78f 2005-04-16 jrandom
* Migrated to Bouncycastle's SHA256 and HMAC implementations for efficiency
(also lots of udp fixes)
2005-04-17 00:59:48 +00:00
9e5fe7d2b6 * fixed some stupid threading issues in the packet handler (duh)
* use the new raw i2np message format (the previous corruptions were due to above)
* add a new test component (UDPFlooder) which floods all peers at the rate desired
* packet munging fix for highly fragmented messages
* include basic slow start code
* fixed the UDP peer rate refilling
* cleaned up some nextSend scheduling
2005-04-16 15:18:09 +00:00
a7dfaee5ac added connelly.i2p 2005-04-13 02:29:59 +00:00
7beb92b1cc First pass of the UDP transport. No where near ready for use, but it does
the basics (negotiate a session and send I2NP messages back and forth).  Lots,
lots more left.
2005-04-12 16:48:43 +00:00
5b56d22da9 2005-04-12 jrandom
* Make sure we don't get cached updates (thanks smeghead!)
    * Clear out the callback for the TestJob after it passes (only affects the
      job timing accounting)
2005-04-12 15:22:11 +00:00
e6b343070a removed copy/paste error 2005-04-09 23:15:53 +00:00
8496b88518 2005-04-08 smeghead
* Added NativeBigInteger benchmark to scripts/i2pbench.sh.
2005-04-09 03:16:05 +00:00
aa542b7876 for implementation simplicity, include fragment size in the SessionConfirmed packets 2005-04-08 23:20:45 +00:00
3f7d46378b * specify exactly what gets in the DSA signatures for the connection establishment
* include a new signedOnTime so that we can prepare the packet at a different moment from
  when we encrypt & send it (also allowing us to reuse that signature on resends for the same
  establishment)
2005-04-08 14:21:26 +00:00
b36def1f72 2005-04-08 smeghead
* Security improvements to TrustedUpdate: signing and verification of the
      version string along with the data payload for signed update files
      (consequently the positions of the DSA signature and version string fields
      have been swapped in the spec for the update file's header); router will
      no longer perform a trusted update if the signed update's version is lower
      than or equal to the currently running router's version.
    * Added two new CLI commands to TrustedUpdate: showversion, verifyupdate.
    * Extended TrustedUpdate public API for use by third party applications.
2005-04-08 12:39:20 +00:00
5a6a3a5e8d oops, forgot to add new eepget script to build 2005-04-08 01:56:02 +00:00
c3bd26d9b4 added wspucktracker.i2p 2005-04-07 20:40:30 +00:00
aum
967e106ee7 fixed one last javadoc err 2005-04-07 04:36:06 +00:00
aum
7c73e59482 Fixed more javadoc errors 2005-04-07 04:26:55 +00:00
aum
03dfa913d1 Removed erroneous @author tag from methods 2005-04-07 04:05:13 +00:00
348e845793 *cough* thanks cervantes 2005-04-06 16:38:38 +00:00
80827c3aad * 2005-04-06 0.5.0.6 released 2005-04-06 15:43:25 +00:00
3b4cf0a024 added 55cancri.i2p 2005-04-06 15:14:00 +00:00
941252fd80 2005-04-05 jrandom
* Retry I2PTunnel startup if we are unable to build a socketManager for a
      client or httpclient tunnel.
    * Add some basic sanity checking on the I2CP settings (thanks duck!)
2005-04-05 22:24:32 +00:00
bc626ece2d 2005-04-05 jrandom
* After a successfull netDb search for a leaseSet, republish it to all of
      the peers we have tried so far who did not give us the key (up to 10),
      rather than the old K closest (which may include peers who had given us
      the key)
    * Don't wait 5 minutes to publish a leaseSet (duh!), and rather than
      republish it every 5 minutes, republish it every 3.  In addition, always
      republish as soon as the leaseSet changes (duh^2).
    * Minor fix for oddball startup race (thanks travis_bickle!)
    * Minor AES update to allow in-place decryption.
2005-04-05 16:06:14 +00:00
400feb3ba7 clarify crypto/hmac usage for simpler implementation 2005-04-05 15:28:54 +00:00
756a4e3995 added a section for congestion control describing what I hope to implement. what
/actually/ gets implemented will be documented further once its, er, implemented
2005-04-04 17:21:30 +00:00
aum
578301240e Added constructors to PrivateKey, PublicKey, SigningPrivateKey and
SigningPublicKey, which take a single String argument and construct
the object from the Base64 data in that string (where this data is
the product of a .toBase64() call on a prior instance).
2005-04-04 06:13:50 +00:00
aum
9b8f91c7f9 Added 'toPublic()' methods to PrivateKey and SigningPrivateKey, such
that these return PublicKey and SigningPublicKey objects, respectively.
2005-04-04 06:01:13 +00:00
c7c389d4fb added eepget wrapper script for *nix 2005-04-03 13:35:52 +00:00
68f7adfa0b *** keyword substitution change *** 2005-04-03 13:33:29 +00:00
c4ac5170c7 2005-04-03 jrandom
* EepGet fix for open-ended HTTP fetches (such as the news.xml
      feeding the NewsFetcher)
2005-04-03 12:50:11 +00:00
32e0c8ac71 updated status blurb 2005-04-03 07:22:28 +00:00
c9c1eae32f 2005-04-01 jrandom
* Allow editing I2PTunnel server instances with five digit ports
      (thanks nickless_head!)
    * More NewsFetcher debugging for reported weirdness
2005-04-01 13:29:26 +00:00
33366cc291 2005-04-01 jrandom
* Fix to check for missing news file (thanks smeghead!)
    * Added destination display CLI:
      java -cp lib/i2p.jar net.i2p.data.Destination privKeyFilename
    * Added destination display to the web interface (thanks pnspns)
    * Installed CIA backdoor
2005-04-01 11:28:06 +00:00
083ac1f125 n3wz0rz 2005-03-31 02:04:18 +00:00
80c6290b89 oh five oh five 2005-03-30 03:27:55 +00:00
6492ad165a Added tracker.fr.i2p 2005-03-30 03:21:18 +00:00
f0d1b1a40e added v2mail.i2p, complication.i2p 2005-03-30 01:49:49 +00:00
17f044e6cd if using numACKs, use a 2 byte value (to handle higher transfer rates) 2005-03-30 00:20:07 +00:00
63f3a9cd7b * 2005-03-29 0.5.0.5 released
2005-03-29  jrandom
    * Decreased the initial RTT estimate to 10s to allow more retries.
    * Increased the default netDb store replication factor from 2 to 6 to take
      into consideration tunnel failures.
    * Address some statistical anonymity attacks against the netDb that could
      be mounted by an active internal adversary by only answering lookups for
      leaseSets we received through an unsolicited store.
    * Don't throttle lookup responses (we throttle enough elsewhere)
    * Fix the NewsFetcher so that it doesn't incorrectly resume midway through
      the file (thanks nickster!)
    * Updated the I2PTunnel HTML (thanks postman!)
    * Added support to the I2PTunnel pages for the URL parameter "passphrase",
      which, if matched against the router.config "i2ptunnel.passphrase" value,
      skips the nonce check.  If the config prop doesn't exist or is blank, no
      passphrase is accepted.
    * Implemented HMAC-SHA256.
    * Enable the tunnel batching with a 500ms delay by default
    * Dropped compatability with 0.5.0.3 and earlier releases
2005-03-30 00:07:36 +00:00
b8ddbf13b4 added lazyguy.i2p 2005-03-28 02:41:19 +00:00
be9bdbfe0f * simplify the MAC construct with a single HMAC (the other setup was an oracle anyway)
* split out the encryption and MAC keys
2005-03-27 22:08:16 +00:00
bc74bf1402 added confessions.i2p, rsync.thetower.i2p, redzara.i2p, gaytorrents.i2p 2005-03-27 01:03:42 +00:00
5c2a57f95a minor cleanup 2005-03-26 09:22:17 +00:00
9cd8cc692e added replay prevention blurb, minor cleanup 2005-03-26 09:19:42 +00:00
ebac4df2d3 2005-03-26 jrandom
* Added some error handling and fairly safe to cache data to the streaming
      lib (good call Tom!)
2005-03-26 07:13:38 +00:00
0626f714c6 speling (thanks cervantes) 2005-03-26 06:23:57 +00:00
21842291e9 *cough* 2005-03-26 05:56:06 +00:00
d461c295f6 first draft of secure semireliable UDP protocol 2005-03-26 05:47:40 +00:00
85b3450525 2005-03-25 jrandom
* Fixed up building dependencies for the routerconsole on some more
      aggressive compilers (thanks polecat!)
2005-03-25 04:07:05 +00:00
aum
75d7c81b7c Oops, forgot the DataFormatException 2005-03-24 08:39:04 +00:00
aum
1433e20f73 Added Destination constructor which accepts/uses a base64 string arg 2005-03-24 08:37:17 +00:00
e614a2f726 * 2005-03-24 0.5.0.4 released 2005-03-24 07:29:27 +00:00
32be7f1fd8 grr 2005-03-24 04:58:28 +00:00
66e1d95a2a *cough* oops 2005-03-24 04:49:15 +00:00
ff03be217e 2005-03-23 jrandom
* Added more intelligent version checking in news.xml, in case we have a
      version newer than the one specified.
2005-03-24 03:18:15 +00:00
a52f8b89dc 2005-03-23 jrandom
* Added support for Transfer-Encoding: chunked to the EepGet, so that the
      cvsweb.cgi doesn't puke on us.
2005-03-24 02:38:10 +00:00
21c7c043b3 Fixed Bugzilla Bug #99 2005-03-24 01:54:23 +00:00
45e6608ad3 Added 'Unit test passed' log message and made test check that Bug #99 is fixed. 2005-03-24 01:50:19 +00:00
28978e3680 Fixed Bug #99: Data pending to be sent is still sent even if STREAM CLOSE is issued. 2005-03-24 01:49:00 +00:00
904f755c8c 2005-03-23 jrandom
* Implemented the news fetch / update policy code, as configurated on
      /configupdate.jsp.  Defaults are to grab the news every 24h (or if it
      doesn't exist yet, on startup).  No action is taken however, though if
      the news.xml specifies that a new release is available, an option to
      update will be shown on the router console.
    * New initialNews.xml delivered with new installs, and moved news.xml out
      of the i2pwww module and into the i2p module so that we can bundle it
      within each update.
2005-03-24 01:19:52 +00:00
a2c309ddd3 2005-03-23 jrandom
* New /configupdate.jsp page for controlling the update / notification
      process, as well as various minor related updates.  Note that not all
      options are exposed yet, and the update detection code isn't in place
      in this commit - it currently says there is always an update available.
    * New EepGet component for reliable downloading, with a CLI exposed in
      java -cp lib/i2p.jar net.i2p.util.EepGet url
    * Added a default signing key to the TrustedUpdate component to be used
      for verifying updates.  This signing key can be authenticated via
      gpg --verify i2p/core/java/src/net/i2p/crypto/TrustedUpdate.java
    * New public domain SHA1 implementation for the DSA code so that we can
      handle signing streams of arbitrary size without excess memory usage
      (thanks P.Verdy!)
    * Added some helpers to the TrustedUpdate to work off streams and to offer
      a minimal CLI:
          TrustedUpdate keygen pubKeyFile privKeyFile
          TrustedUpdate sign origFile signedFile privKeyFile
          TrustedUpdate verify signedFile
2005-03-23 21:13:03 +00:00
aum
677eeac8f7 changed existing 'decodeToString' to public 2005-03-23 06:30:31 +00:00
aum
b232cc0f24 D'oh, .decodeToString was already there, eliminated my vers 2005-03-23 06:26:23 +00:00
aum
18bbae1d1e changed 'String decode(String raw)' to 'String decodeToString(String raw)'
to eliminate name clash.
2005-03-23 06:24:25 +00:00
aum
08ee62b52c Added convenience methods:
- String encode(String raw)
 - String decode(String raw)
2005-03-23 06:21:16 +00:00
5b83aed719 * Added basic trusted update creation/verification 2005-03-22 17:08:01 +00:00
b5875ca07b 2005-03-21 jrandom
* Fixed the tunnel fragmentation handler to deal with multiple fragments
      in a single message properly (rather than release the buffer into the
      cache after processing the first one) (duh!)
    * Added the batching preprocessor which will bundle together multiple
      small messages inside a single tunnel message by delaying their delivery
      up to .5s, or whenever the pending data will fill a full message,
      whichever comes first.  This is disabled at the moment, since without the
      above bugfix widely deployed, lots and lots of messages would fail.
    * Within each tunnel pool, stick with a randomly selected peer for up to
      .5s before randomizing and selecting again, instead of randomizing the
      pool each time a tunnel is needed.
2005-03-22 02:00:10 +00:00
3f9bf28382 2005-03-21 jrandom
* Fixed the tunnel fragmentation handler to deal with multiple fragments
      in a single message properly (rather than release the buffer into the
      cache after processing the first one) (duh!)
    * Added the batching preprocessor which will bundle together multiple
      small messages inside a single tunnel message by delaying their delivery
      up to .5s, or whenever the pending data will fill a full message,
      whichever comes first.  This is disabled at the moment, since without the
      above bugfix widely deployed, lots and lots of messages would fail.
    * Within each tunnel pool, stick with a randomly selected peer for up to
      .5s before randomizing and selecting again, instead of randomizing the
      pool each time a tunnel is needed.
2005-03-22 01:38:21 +00:00
a2bd71c75b * 2005-03-18 0.5.0.3 released
2005-03-18  jrandom
    * Minor tweak to the timestamper to help reduce small skews
    * Adjust the stats published to include only the relevent ones
    * Only show the currently used speed calculation on the profile page
    * Allow the full max # resends to be sent, rather than piggybacking the
      RESET packet along side the final resend (duh)
    * Add irc.postman.i2p to the default list of IRC servers for new installs
    * Drop support for routers running 0.5 or 0.5.0.1 while maintaining
      backwards compatability for users running 0.5.0.2.
2005-03-18 22:34:51 +00:00
89509490c5 2005-03-18 jrandom
* Eepproxy Fix for corrupted HTTP headers (thanks nickster!)
    * Fixed case sensitivity issues on the HTTP headers (thanks duck!)
2005-03-18 08:48:00 +00:00
a997a46040 2005-03-17 jrandom
* Update the old speed calculator and associated profile data points to
      use a non-tiered moving average of the tunnel test time, avoiding the
      freshness issues of the old tiered speed stats.
    * Explicitly synchronize all of the methods on the PRNG, rather than just
      the feeder methods (sun and kaffe only need the feeder, but it seems ibm
      needs all of them synchronized).
    * Properly use the tunnel tests as part of the profile stats.
    * Don't flood the jobqueue with sequential persist profile tasks, but
      instead, inject a brief scheduling delay between them.
    * Reduce the TCP connection establishment timeout to 20s (which is still
      absurdly excessive)
    * Reduced the max resend delay to 30s so we can get some resends in when
      dealing with client apps that hang up early (e.g. wget)
    * Added more alternative socketManager factories (good call aum!)
2005-03-17 22:12:51 +00:00
538dd07e7b 2005-03-16 jrandom
* Adjust the old speed calculator to include end to end RTT data in its
      estimates, and use that as the primary speed calculator again.
    * Use the mean of the high capacity speeds to determine the fast
      threshold, rather than the median.  Perhaps we should use the mean of
      all active non-failing peers?
    * Updated the profile page to sort by tier, then alphabetically.
    * Added some alternative socketManager factories (good call aum!)
2005-03-17 05:29:55 +00:00
046778404e added arkan.i2p, search.i2p, floureszination.i2p, antipiratbyran.i2p
asylum.i2p, templar.i2p
2005-03-16 02:56:01 +00:00
766f83d653 added feedspace.i2p 2005-03-16 02:46:17 +00:00
b20aee6753 2005-03-14 jrandom
* New strict speed calculator that goes off the actual number of messages
      verifiably sent through the peer by way of tunnels.  Initially, this only
      contains the successful message count on inbound tunnels, but may be
      augmented later to include verified outbound messages, peers queried in
      the netDb, etc.  The speed calculation decays quickly, but should give
      a better differential than the previous stat (both values are shown on
      the /profiles.jsp page)
2005-03-15 03:47:14 +00:00
f9aa3aef18 added wiki.fr.i2p 2005-03-14 04:31:55 +00:00
d74aa6e53d (no, this doesnt fix things yet, but its a save point along the path)
2005-03-11  jrandom
    * Rather than the fixed resend timeout floor (10s), use 10s+RTT as the
      minimum (increased on resends as before, of course).
    * Always prod the clock update listeners, even if just to tell them that
      the time hasn't changed much.
    * Added support for explicit peer selection for individual tunnel pools,
      which will be useful in debugging but not recommended for use by normal
      end users.
    * More aggressively search for the next hop's routerInfo on tunnel join.
    * Give messages received via inbound tunnels that are bound to remote
      locations sufficient time (taking into account clock skew).
    * Give alternate direct send messages sufficient time (10s min, not 5s)
    * Always give the end to end data message the explicit timeout (though the
      old default was sufficient before)
    * No need to give end to end messages an insane expiration (+2m), as we
      are already handling skew on the receiving side.
    * Don't complain too loudly about expired TunnelCreateMessages (at least,
      not until after all those 0.5 and 0.5.0.1 users upgrade ;)
    * Properly keep the sendBps stat
    * When running the router with router.keepHistory=true, log more data to
      messageHistory.txt
    * Logging updates
    * Minor formatting updates
2005-03-11 22:23:36 +00:00
ea6fbc7835 added septu.i2p 2005-03-09 20:02:14 +00:00
536e604b8e 2005-03-07 jrandom
* Fix the HTTP response header filter to allow multiple headers with the
      same name (thanks duck and spotteri!)
2005-03-08 02:45:14 +00:00
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
991 changed files with 96717 additions and 18374 deletions

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path=""/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="../jetty/jettylib/javax.servlet.jar"/>
<classpathentry kind="output" path=""/>
</classpath>

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>addressbook</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

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

View File

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

View File

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

View File

@ -36,6 +36,7 @@ import java.io.File;
*/
public class Daemon {
public static final String VERSION = "2.0.3";
private static final Daemon _instance = new Daemon();
/**
* Update the router and published address books using remote data from the
@ -56,16 +57,16 @@ public class Daemon {
* @param log
* The log to write changes and conflicts to.
*/
public static void update(AddressBook master, AddressBook router,
public void update(AddressBook master, AddressBook router,
File published, SubscriptionList subscriptions, Log log) {
String routerLocation = router.getLocation();
master.merge(router);
router.merge(master, true, null);
Iterator iter = subscriptions.iterator();
while (iter.hasNext()) {
master.merge((AddressBook) iter.next(), log);
router.merge((AddressBook) iter.next(), false, log);
}
master.write(new File(routerLocation));
master.write(published);
router.write();
if (published != null)
router.write(published);
subscriptions.write();
}
@ -77,12 +78,14 @@ public class Daemon {
* @param home
* The directory containing addressbook's configuration files.
*/
public static void update(Map settings, String home) {
public 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 = new File(home, (String) settings
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"));
@ -95,14 +98,14 @@ public class Daemon {
AddressBook router = new AddressBook(routerFile);
List defaultSubs = new LinkedList();
defaultSubs.add("http://dev.i2p/i2p/hosts.txt");
defaultSubs.add("http://duck.i2p/hosts.txt");
defaultSubs.add("http://i2p/NF2RLVUxVulR3IqK0sGJR0dHQcGXAzwa6rEO4WAWYXOHw-DoZhKnlbf1nzHXwMEJoex5nFTyiNMqxJMWlY54cvU~UenZdkyQQeUSBZXyuSweflUXFqKN-y8xIoK2w9Ylq1k8IcrAFDsITyOzjUKoOPfVq34rKNDo7fYyis4kT5bAHy~2N1EVMs34pi2RFabATIOBk38Qhab57Umpa6yEoE~rbyR~suDRvD7gjBvBiIKFqhFueXsR2uSrPB-yzwAGofTXuklofK3DdKspciclTVzqbDjsk5UXfu2nTrC1agkhLyqlOfjhyqC~t1IXm-Vs2o7911k7KKLGjB4lmH508YJ7G9fLAUyjuB-wwwhejoWqvg7oWvqo4oIok8LG6ECR71C3dzCvIjY2QcrhoaazA9G4zcGMm6NKND-H4XY6tUWhpB~5GefB3YczOqMbHq4wi0O9MzBFrOJEOs3X4hwboKWANf7DT5PZKJZ5KorQPsYRSq0E3wSOsFCSsdVCKUGsAAAA/i2p/hosts.txt");
SubscriptionList subscriptions = new SubscriptionList(subscriptionFile,
etagsFile, lastModifiedFile, defaultSubs);
etagsFile, lastModifiedFile, defaultSubs, (String) settings
.get("proxy_host"), Integer.parseInt((String) settings.get("proxy_port")));
Log log = new Log(logFile);
Daemon.update(master, router, published, subscriptions, log);
update(master, router, published, subscriptions, log);
}
/**
@ -116,6 +119,10 @@ public class Daemon {
* others are ignored.
*/
public static void main(String[] args) {
_instance.run(args);
}
public void run(String[] args) {
String settingsLocation = "config.txt";
Map settings = new HashMap();
String home;
@ -128,9 +135,10 @@ public class Daemon {
Map defaultSettings = new HashMap();
defaultSettings.put("proxy_host", "localhost");
defaultSettings.put("proxy_port", "4444");
defaultSettings.put("master_addressbook", "myhosts.txt");
defaultSettings.put("router_addressbook", "../userhosts.txt");
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");
@ -148,24 +156,36 @@ public class Daemon {
File settingsFile = new File(homeFile, settingsLocation);
settings = ConfigParser.parse(settingsFile, defaultSettings);
// wait
try {
Thread.currentThread().sleep(5*60*1000);
} catch (InterruptedException ie) {}
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);
update(settings, home);
try {
Thread.sleep(delay * 60 * 60 * 1000);
synchronized (this) {
wait(delay * 60 * 60 * 1000);
}
} catch (InterruptedException exp) {
}
settings = ConfigParser.parse(settingsFile, defaultSettings);
}
}
/**
* Call this to get the addressbook to reread its config and
* refetch its subscriptions.
*/
public static void wakeup() {
synchronized (_instance) {
_instance.notifyAll();
}
}
}

View File

@ -44,10 +44,10 @@ public class DaemonThread extends Thread {
* @see java.lang.Runnable#run()
*/
public void run() {
try {
Thread.sleep(5 * 60 * 1000);
} catch (InterruptedException exp) {
}
//try {
// Thread.sleep(5 * 60 * 1000);
//} catch (InterruptedException exp) {
//}
Daemon.main(this.args);
}
}

View File

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

View File

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

View File

@ -111,12 +111,12 @@ public class Bogobot extends PircBot {
_botShutdownPassword = config.getProperty("botShutdownPassword", "take off eh");
_ircChannel = config.getProperty("ircChannel", "#i2p-chat");
_ircServer = config.getProperty("ircServer", "irc.duck.i2p");
_ircServer = config.getProperty("ircServer", "irc.postman.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");
_logFilePrefix = config.getProperty("logFilePrefix", "irc.postman.i2p.i2p-chat");
_logFileRotationInterval = config.getProperty("logFileRotationInterval", INTERVAL_DAILY);
_isRoundTripDelayEnabled = Boolean.valueOf(config.getProperty("isRoundTripDelayEnabled", "false")).booleanValue();

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

@ -31,6 +31,9 @@
</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 -->
@ -39,12 +42,13 @@
<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="-v9" />
<arg value="-p" />
<arg value="net.i2p.i2ptunnel.jsp" />
<arg value="-webinc" />
@ -52,10 +56,13 @@
<arg value="-webapp" />
<arg value="../jsp/" />
</java>
<javac destdir="../jsp/WEB-INF/classes/" srcdir="../jsp/WEB-INF/classes" includes="*.java">
<javac debug="true" deprecation="on" source="1.3" target="1.3"
destdir="../jsp/WEB-INF/classes/" srcdir="../jsp/WEB-INF/classes" includes="**/*.java">
<classpath>
<pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
<pathelement location="../../jetty/jettylib/commons-logging.jar" />
<pathelement location="../../jetty/jettylib/commons-el.jar" />
<pathelement location="build/i2ptunnel.jar" />
</classpath>
</javac>

View File

@ -0,0 +1,361 @@
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.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.zip.GZIPInputStream;
import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
import net.i2p.util.ByteCache;
import net.i2p.util.I2PThread;
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. Perhaps add transparent handling of the
* Content-encoding: x-i2p-gzip, adjusting the headers to say Content-encoding: identity?
* Content-encoding: gzip is trivial as well, but Transfer-encoding: chunked makes it
* more work than is worthwhile at the moment.
*
*/
class HTTPResponseOutputStream extends FilterOutputStream {
private I2PAppContext _context;
private Log _log;
private ByteCache _cache;
protected ByteArray _headerBuffer;
private boolean _headerWritten;
private byte _buf1[];
protected boolean _gzip;
private long _dataWritten;
private InternalGZIPInputStream _in;
private static final int CACHE_SIZE = 8*1024;
public HTTPResponseOutputStream(OutputStream raw) {
super(raw);
_context = I2PAppContext.getGlobalContext();
_context.statManager().createRateStat("i2ptunnel.httpCompressionRatio", "ratio of compressed size to decompressed size after transfer", "i2ptunnel", new long[] { 60*1000, 30*60*1000 });
_context.statManager().createRateStat("i2ptunnel.httpCompressed", "compressed size transferred", "i2ptunnel", new long[] { 60*1000, 30*60*1000 });
_context.statManager().createRateStat("i2ptunnel.httpExpanded", "size transferred after expansion", "i2ptunnel", new long[] { 60*1000, 30*60*1000 });
_log = _context.logManager().getLog(getClass());
_cache = ByteCache.getInstance(8, CACHE_SIZE);
_headerBuffer = _cache.acquire();
_headerWritten = false;
_gzip = false;
_dataWritten = 0;
_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);
_dataWritten += len;
//out.flush();
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);
_dataWritten += len-i-1;
//out.flush();
}
return;
}
}
}
/** grow (and free) the buffer as necessary */
private void ensureCapacity() {
if (_headerBuffer.getValid() + 1 >= _headerBuffer.getData().length) {
int newSize = (int)(_headerBuffer.getData().length * 1.5);
ByteArray newBuf = new ByteArray(new byte[newSize]);
System.arraycopy(_headerBuffer.getData(), 0, newBuf.getData(), 0, _headerBuffer.getValid());
newBuf.setValid(_headerBuffer.getValid());
newBuf.setOffset(0);
if (_headerBuffer.getData().length == CACHE_SIZE)
_cache.release(_headerBuffer);
_headerBuffer = newBuf;
}
}
/** are the headers finished? */
private boolean headerReceived() {
if (_headerBuffer.getValid() < 3) return false;
byte first = _headerBuffer.getData()[_headerBuffer.getValid()-3];
byte second = _headerBuffer.getData()[_headerBuffer.getValid()-2];
byte third = _headerBuffer.getData()[_headerBuffer.getValid()-1];
return (isNL(second) && isNL(third)) || // \n\n
(isNL(first) && isNL(third)); // \n\r\n
}
/**
* Tweak that first HTTP response line (HTTP 200 OK, etc)
*
*/
protected String filterResponseLine(String line) {
return line;
}
/** we ignore any potential \r, since we trim it on write anyway */
private static final byte NL = '\n';
private boolean isNL(byte b) { return (b == NL); }
/** ok, received, now munge & write it */
private void writeHeader() throws IOException {
String responseLine = null;
boolean connectionSent = false;
boolean proxyConnectionSent = false;
int lastEnd = -1;
for (int i = 0; i < _headerBuffer.getValid(); i++) {
if (isNL(_headerBuffer.getData()[i])) {
if (lastEnd == -1) {
responseLine = new String(_headerBuffer.getData(), 0, i+1); // includes NL
responseLine = filterResponseLine(responseLine);
responseLine = (responseLine.trim() + "\n");
out.write(responseLine.getBytes());
} else {
for (int j = lastEnd+1; j < i; j++) {
if (_headerBuffer.getData()[j] == ':') {
int keyLen = j-(lastEnd+1);
int valLen = i-(j+2);
if ( (keyLen <= 0) || (valLen <= 0) )
throw new IOException("Invalid header @ " + j);
String key = new String(_headerBuffer.getData(), lastEnd+1, keyLen);
String val = new String(_headerBuffer.getData(), j+2, valLen).trim();
if (_log.shouldLog(Log.INFO))
_log.info("Response header [" + key + "] = [" + val + "]");
if ("Connection".equalsIgnoreCase(key)) {
out.write("Connection: close\n".getBytes());
connectionSent = true;
} else if ("Proxy-Connection".equalsIgnoreCase(key)) {
out.write("Proxy-Connection: close\n".getBytes());
proxyConnectionSent = true;
} else if ( ("Content-encoding".equalsIgnoreCase(key)) && ("x-i2p-gzip".equalsIgnoreCase(val)) ) {
_gzip = true;
} else {
out.write((key.trim() + ": " + val.trim() + "\n").getBytes());
}
break;
}
}
}
lastEnd = i;
}
}
if (!connectionSent)
out.write("Connection: close\n".getBytes());
if (!proxyConnectionSent)
out.write("Proxy-Connection: close\n".getBytes());
finishHeaders();
boolean shouldCompress = shouldCompress();
if (_log.shouldLog(Log.INFO))
_log.info("After headers: gzip? " + _gzip + " compress? " + shouldCompress);
// done, shove off
if (_headerBuffer.getData().length == CACHE_SIZE)
_cache.release(_headerBuffer);
else
_headerBuffer = null;
if (shouldCompress) {
beginProcessing();
}
}
protected boolean shouldCompress() { return _gzip; }
protected void finishHeaders() throws IOException {
out.write("\n".getBytes()); // end of the headers
}
public void close() throws IOException {
out.close();
}
protected void beginProcessing() throws IOException {
//out.flush();
PipedInputStream pi = new PipedInputStream();
PipedOutputStream po = new PipedOutputStream(pi);
new I2PThread(new Pusher(pi, out), "HTTP decompresser").start();
out = po;
}
private class Pusher implements Runnable {
private InputStream _inRaw;
private OutputStream _out;
public Pusher(InputStream in, OutputStream out) {
_inRaw = in;
_out = out;
}
public void run() {
OutputStream to = null;
_in = null;
long start = System.currentTimeMillis();
long written = 0;
try {
_in = new InternalGZIPInputStream(_inRaw);
byte buf[] = new byte[8192];
int read = -1;
while ( (read = _in.read(buf)) != -1) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read " + read + " and writing it to the browser/streams");
_out.write(buf, 0, read);
_out.flush();
written += read;
}
if (_log.shouldLog(Log.INFO))
_log.info("Decompressed: " + written + ", " + _in.getTotalRead() + "/" + _in.getTotalExpanded());
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error decompressing: " + written + ", " + (_in != null ? _in.getTotalRead() + "/" + _in.getTotalExpanded() : ""), ioe);
} finally {
if (_log.shouldLog(Log.WARN) && (_in != null))
_log.warn("After decompression, written=" + written +
(_in != null ?
" read=" + _in.getTotalRead()
+ ", expanded=" + _in.getTotalExpanded() + ", remaining=" + _in.getRemaining()
+ ", finished=" + _in.getFinished()
: ""));
if (_out != null) try {
_out.close();
} catch (IOException ioe) {}
}
long end = System.currentTimeMillis();
double compressed = (_in != null ? _in.getTotalRead() : 0);
double expanded = (_in != null ? _in.getTotalExpanded() : 0);
double ratio = 0;
if (expanded > 0)
ratio = compressed/expanded;
_context.statManager().addRateData("i2ptunnel.httpCompressionRatio", (int)(100d*ratio), end-start);
_context.statManager().addRateData("i2ptunnel.httpCompressed", (long)compressed, end-start);
_context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded, end-start);
}
}
private class InternalGZIPInputStream extends GZIPInputStream {
public InternalGZIPInputStream(InputStream in) throws IOException {
super(in);
}
public long getTotalRead() { return super.inf.getTotalIn(); }
public long getTotalExpanded() { return super.inf.getTotalOut(); }
public long getRemaining() { return super.inf.getRemaining(); }
public boolean getFinished() { return super.inf.finished(); }
public String toString() {
return "Read: " + getTotalRead() + " expanded: " + getTotalExpanded() + " remaining: " + getRemaining() + " finished: " + getFinished();
}
}
public String toString() {
return super.toString() + ": " + _in;
}
public static void main(String args[]) {
String simple = "HTTP/1.1 200 OK\n" +
"foo: bar\n" +
"baz: bat\n" +
"\n" +
"hi ho, this is the body";
String filtered = "HTTP/1.1 200 OK\n" +
"Connection: keep-alive\n" +
"foo: bar\n" +
"baz: bat\n" +
"\n" +
"hi ho, this is the body";
String winfilter= "HTTP/1.1 200 OK\r\n" +
"Connection: keep-alive\r\n" +
"foo: bar\r\n" +
"baz: bat\r\n" +
"\r\n" +
"hi ho, this is the body";
String minimal = "HTTP/1.1 200 OK\n" +
"\n" +
"hi ho, this is the body";
String winmin = "HTTP/1.1 200 OK\r\n" +
"\r\n" +
"hi ho, this is the body";
String invalid1 = "HTTP/1.1 200 OK\n";
String invalid2 = "HTTP/1.1 200 OK";
String invalid3 = "HTTP 200 OK\r\n";
String invalid4 = "HTTP 200 OK\r";
String invalid5 = "HTTP/1.1 200 OK\r\n" +
"I am broken, and I smell\r\n" +
"\r\n";
String invalid6 = "HTTP/1.1 200 OK\r\n" +
":I am broken, and I smell\r\n" +
"\r\n";
String invalid7 = "HTTP/1.1 200 OK\n" +
"I am broken, and I smell:\n" +
":asdf\n" +
":\n" +
"\n";
String large = "HTTP/1.1 200 OK\n" +
"Last-modified: Tue, 25 Nov 2003 12:05:38 GMT\n" +
"Expires: Tue, 25 Nov 2003 12:05:38 GMT\n" +
"Content-length: 32\n" +
"\n" +
"hi ho, this is the body";
/* */
test("Simple", simple, true);
test("Filtered", filtered, true);
test("Filtered windows", winfilter, true);
test("Minimal", minimal, true);
test("Windows", winmin, true);
test("Large", large, true);
test("Invalid (short headers)", invalid1, true);
test("Invalid (no headers)", invalid2, true);
test("Invalid (windows with short headers)", invalid3, true);
test("Invalid (windows no headers)", invalid4, true);
test("Invalid (bad headers)", invalid5, true);
test("Invalid (bad headers2)", invalid6, false);
test("Invalid (bad headers3)", invalid7, false);
/* */
}
private static void test(String name, String orig, boolean shouldPass) {
System.out.println("====Testing: " + name + "\n" + orig + "\n------------");
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
HTTPResponseOutputStream resp = new HTTPResponseOutputStream(baos);
resp.write(orig.getBytes());
resp.flush();
String received = new String(baos.toByteArray());
System.out.println(received);
} catch (Exception e) {
if (shouldPass)
e.printStackTrace();
else
System.out.println("Properly fails with " + e.getMessage());
}
}
}

View File

@ -27,6 +27,7 @@
* not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
package net.i2p.i2ptunnel;
import java.io.BufferedReader;
@ -108,8 +109,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
_tunnelId = ++__tunnelId;
_log = _context.logManager().getLog(I2PTunnel.class);
_event = new EventDispatcherImpl();
_clientOptions = new Properties();
_clientOptions.putAll(System.getProperties());
Properties p = new Properties();
p.putAll(System.getProperties());
_clientOptions = p;
_sessions = new ArrayList(1);
addConnectionEventListener(lsnr);
@ -183,7 +185,8 @@ public class I2PTunnel implements Logging, EventDispatcher {
void addSession(I2PSession session) {
if (session == null) return;
synchronized (_sessions) {
_sessions.add(session);
if (!_sessions.contains(session))
_sessions.add(session);
}
}
void removeSession(I2PSession session) {
@ -287,8 +290,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
l.log("textserver <host> <port> <privkey>");
l.log("genkeys <privkeyfile> [<pubkeyfile>]");
l.log("gentextkeys");
l.log("client <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile>");
l.log("httpclient <port>");
l.log("client <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile> [<sharedClient>]");
l.log("ircclient <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile> [<sharedClient>]");
l.log("httpclient <port> [<sharedClient>] [<proxy>]");
l.log("lookup <name>");
l.log("quit");
l.log("close [forced] <jobnumber>|all");
@ -485,12 +489,16 @@ public class I2PTunnel implements Logging, EventDispatcher {
* Also sets the event "openClientResult" = "error" or "ok" (before setting the value to "ok" it also
* adds "Ready! Port #" to the logger as well). In addition, it will also set "clientLocalPort" =
* Integer port number if the client is listening
* sharedClient parameter is a String "true" or "false"
*
* @param args {portNumber, destinationBase64 or "file:filename"}
* @param args {portNumber, destinationBase64 or "file:filename"[, sharedClient]}
* @param l logger to receive events and output
*/
public void runClient(String args[], Logging l) {
if (args.length == 2) {
boolean isShared = true;
if (args.length == 3)
isShared = Boolean.valueOf(args[2].trim()).booleanValue();
if ( (args.length == 2) || (args.length == 3) ) {
int portNum = -1;
try {
portNum = Integer.parseInt(args[0]);
@ -501,6 +509,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
return;
}
I2PTunnelTask task;
ownDest = !isShared;
try {
task = new I2PTunnelClient(portNum, args[1], l, ownDest, (EventDispatcher) this, this);
addtask(task);
@ -511,11 +520,12 @@ public class I2PTunnel implements Logging, EventDispatcher {
notifyEvent("clientTaskId", new Integer(-1));
}
} else {
l.log("client <port> <pubkey>[,<pubkey>]|file:<pubkeyfile>");
l.log("client <port> <pubkey>[,<pubkey>]|file:<pubkeyfile>[ <sharedClient>]");
l.log(" creates a client that forwards port to the pubkey.\n"
+ " 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");
+ " randomlyl. sharedClient indicates if this client shares \n"
+ " with other clients (true of false)");
notifyEvent("clientTaskId", new Integer(-1));
}
}
@ -525,12 +535,13 @@ public class I2PTunnel implements Logging, EventDispatcher {
*
* Sets the event "httpclientTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error).
* Also sets "httpclientStatus" = "ok" or "error" after the client tunnel has started.
* parameter sharedClient is a String, either "true" or "false"
*
* @param args {portNumber and (optionally) proxy to be used for the WWW}
* @param args {portNumber[, sharedClient][, proxy to be used for the WWW]}
* @param l logger to receive events and output
*/
public void runHttpClient(String args[], Logging l) {
if (args.length >= 1 && args.length <= 2) {
if (args.length >= 1 && args.length <= 3) {
int port = -1;
try {
port = Integer.parseInt(args[0]);
@ -540,12 +551,32 @@ public class I2PTunnel implements Logging, EventDispatcher {
notifyEvent("httpclientTaskId", new Integer(-1));
return;
}
String proxy = "squid.i2p";
if (args.length == 2) {
proxy = args[1];
boolean isShared = true;
if (args.length > 1) {
if ("true".equalsIgnoreCase(args[1].trim())) {
isShared = true;
if (args.length == 3)
proxy = args[2];
} else if ("false".equalsIgnoreCase(args[1].trim())) {
_log.warn("args[1] == [" + args[1] + "] and rejected explicitly");
isShared = false;
if (args.length == 3)
proxy = args[2];
} else if (args.length == 3) {
isShared = false; // not "true"
proxy = args[2];
_log.warn("args[1] == [" + args[1] + "] but rejected");
} else {
// isShared not specified, default to true
isShared = true;
proxy = args[1];
}
}
I2PTunnelTask task;
ownDest = !isShared;
try {
task = new I2PTunnelHTTPClient(port, l, ownDest, proxy, (EventDispatcher) this, this);
addtask(task);
@ -556,8 +587,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
notifyEvent("httpclientTaskId", new Integer(-1));
}
} else {
l.log("httpclient <port> [<proxy>]");
l.log("httpclient <port> [<sharedClient>] [<proxy>]");
l.log(" creates a client that distributes HTTP requests.");
l.log(" <sharedClient> (optional) indicates if this client shares tunnels with other clients (true of false)");
l.log(" <proxy> (optional) indicates a proxy server to be used");
l.log(" when trying to access an address out of the .i2p domain");
l.log(" (the default proxy is squid.i2p).");
@ -565,6 +597,60 @@ public class I2PTunnel implements Logging, EventDispatcher {
}
}
/**
* Run an IRC client on the given port number
*
* Sets the event "ircclientTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error).
* Also sets "ircclientStatus" = "ok" or "error" after the client tunnel has started.
* parameter sharedClient is a String, either "true" or "false"
*
* @param args {portNumber,destinationBase64 or "file:filename" [, sharedClient]}
* @param l logger to receive events and output
*/
public void runIrcClient(String args[], Logging l) {
if (args.length >= 2 && args.length <= 3) {
int port = -1;
try {
port = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
notifyEvent("ircclientTaskId", new Integer(-1));
return;
}
boolean isShared = true;
if (args.length > 1) {
if ("true".equalsIgnoreCase(args[2].trim())) {
isShared = true;
} else if ("false".equalsIgnoreCase(args[1].trim())) {
_log.warn("args[1] == [" + args[1] + "] and rejected explicitly");
isShared = false;
} else {
// isShared not specified, default to true
isShared = true;
}
}
I2PTunnelTask task;
ownDest = !isShared;
try {
task = new I2PTunnelIRCClient(port, args[1],l, ownDest, (EventDispatcher) this, this);
addtask(task);
notifyEvent("ircclientTaskId", new Integer(task.getId()));
} catch (IllegalArgumentException iae) {
_log.error(getPrefix() + "Invalid I2PTunnel config to create an ircclient [" + host + ":"+ port + "]", iae);
l.log("Invalid I2PTunnel configuration [" + host + ":" + port + "]");
notifyEvent("ircclientTaskId", new Integer(-1));
}
} else {
l.log("ircclient <port> [<sharedClient>]");
l.log(" creates a client that filter IRC protocol.");
l.log(" <sharedClient> (optional) indicates if this client shares tunnels with other clients (true of false)");
notifyEvent("ircclientTaskId", new Integer(-1));
}
}
/**
* Run an SOCKS tunnel on the given port number
*
@ -1116,6 +1202,8 @@ public class I2PTunnel implements Logging, EventDispatcher {
}
private String getPrefix() { return '[' + _tunnelId + "]: "; }
public I2PAppContext getContext() { return _context; }
/**
* Call this whenever we lose touch with the router involuntarily (aka the router

View File

@ -12,15 +12,13 @@ import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import net.i2p.I2PException;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
@ -34,6 +32,7 @@ import net.i2p.util.Log;
public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runnable {
private static final Log _log = new Log(I2PTunnelClientBase.class);
protected I2PAppContext _context;
protected Logging l;
static final long DEFAULT_CONNECT_TIMEOUT = 60 * 1000;
@ -104,11 +103,27 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
this.l = l;
this.handlerName = handlerName + _clientId;
synchronized (sockLock) {
if (ownDest) {
sockMgr = buildSocketManager();
} else {
sockMgr = getSocketManager();
_context = tunnel.getContext();
_context.statManager().createRateStat("i2ptunnel.client.closeBacklog", "How many pending sockets remain when we close one due to backlog?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.client.closeNoBacklog", "How many pending sockets remain when it was removed prior to backlog timeout?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.client.manageTime", "How long it takes to accept a socket and fire it into an i2ptunnel runner (or queue it for the pool)?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.client.buildRunTime", "How long it takes to run a queued socket into an i2ptunnel runner?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
// no need to load the netDb with leaseSets for destinations that will never
// be looked up
tunnel.getClientOptions().setProperty("i2cp.dontPublishLeaseSet", "true");
while (sockMgr == null) {
synchronized (sockLock) {
if (ownDest) {
sockMgr = buildSocketManager();
} else {
sockMgr = getSocketManager();
}
}
if (sockMgr == null) {
_log.log(Log.CRIT, "Unable to create socket manager (our own? " + ownDest + ")");
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
}
if (sockMgr == null) {
@ -206,8 +221,24 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
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;
int portNum = 7654;
if (tunnel.port != null) {
try {
portNum = Integer.parseInt(tunnel.port);
} catch (NumberFormatException nfe) {
_log.log(Log.CRIT, "Invalid port specified [" + tunnel.port + "], reverting to " + portNum);
}
}
I2PSocketManager sockManager = null;
while (sockManager == null) {
sockManager = I2PSocketManagerFactory.createManager(tunnel.host, portNum, props);
if (sockManager == null) {
_log.log(Log.CRIT, "Unable to create socket manager");
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
}
sockManager.setName("Client");
return sockManager;
}
@ -339,7 +370,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
while (true) {
Socket s = ss.accept();
long before = System.currentTimeMillis();
manageConnection(s);
long total = System.currentTimeMillis() - before;
_context.statManager().addRateData("i2ptunnel.client.manageTime", total, total);
}
} catch (IOException ex) {
_log.error("Error listening for connections on " + localPort, ex);
@ -399,14 +433,20 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
private Socket _s;
public CloseEvent(Socket s) { _s = s; }
public void timeReached() {
int remaining = 0;
boolean stillWaiting = false;
synchronized (_waitingSockets) {
stillWaiting = _waitingSockets.remove(_s);
remaining = _waitingSockets.size();
}
if (stillWaiting) {
try { _s.close(); } catch (IOException ioe) {}
if (_log.shouldLog(Log.INFO))
if (_log.shouldLog(Log.INFO)) {
_context.statManager().addRateData("i2ptunnel.client.closeBacklog", remaining, 0);
_log.info("Closed a waiting socket because of backlog");
}
} else {
_context.statManager().addRateData("i2ptunnel.client.closeNoBacklog", remaining, 0);
}
}
}
@ -473,8 +513,12 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
}
} catch (InterruptedException ie) {}
if (s != null)
if (s != null) {
long before = System.currentTimeMillis();
clientConnectionRun(s);
long total = System.currentTimeMillis() - before;
_context.statManager().addRateData("i2ptunnel.client.buildRunTime", total, 0);
}
s = null;
}
}

View File

@ -22,9 +22,8 @@ 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.FileUtil;
import net.i2p.util.Log;
/**
@ -72,7 +71,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
"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>")
"Could not find the following Destination:<BR><BR><div>")
.getBytes();
private final static byte[] ERR_TIMEOUT =
@ -97,6 +96,22 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
"HTTP outproxy configured. Please configure an outproxy in I2PTunnel")
.getBytes();
private final static byte[] ERR_AHELPER_CONFLICT =
("HTTP/1.1 409 Conflict\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: Destination key conflict</H1>"+
"The addresshelper link you followed specifies a different destination key "+
"than a host entry in your host database. "+
"Someone could be trying to impersonate another eepsite, "+
"or people have given two eepsites identical names.<P/>"+
"You can resolve the conflict by considering which key you trust, "+
"and either discarding the addresshelper link, "+
"discarding the host entry from your host database, "+
"or naming one of them differently.<P/>")
.getBytes();
/** used to assign unique IDs to the threads / clients. no logic or functionality */
private static volatile long __clientId = 0;
@ -180,6 +195,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
return opts;
}
private static final boolean DEFAULT_GZIP = true;
private static long __requestId = 0;
protected void clientConnectionRun(Socket s) {
OutputStream out = null;
@ -192,13 +209,15 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(), "ISO-8859-1"));
String line, method = null, protocol = null, host = null, destination = null;
StringBuffer newRequest = new StringBuffer();
int ahelper = 0;
while ((line = br.readLine()) != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix(requestId) + "Line=[" + line + "]");
if (line.startsWith("Connection: ") ||
line.startsWith("Keep-Alive: ") ||
line.startsWith("Proxy-Connection: "))
String lowercaseLine = line.toLowerCase();
if (lowercaseLine.startsWith("connection: ") ||
lowercaseLine.startsWith("keep-alive: ") ||
lowercaseLine.startsWith("proxy-connection: "))
continue;
if (method == null) { // first line (GET /base64/realaddr)
@ -211,7 +230,18 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
String request = line.substring(pos + 1);
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;
@ -231,49 +261,102 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
// Quick hack for foo.bar.i2p
if (host.toLowerCase().endsWith(".i2p")) {
// Destination gets the host name
destination = host;
// Host becomes the destination key
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);
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 ahelperKey = null;
boolean ahelperConflict = false;
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);
String initialFragments = fragments;
fragments = fragments + "&";
String fragment;
while(fragments.length() > 0) {
pos2 = fragments.indexOf("&");
fragment = fragments.substring(0, pos2);
fragments = fragments.substring(pos2 + 1);
// Fragment looks like addresshelper key
if (fragment.startsWith("i2paddresshelper=")) {
pos2 = fragment.indexOf("=");
ahelperKey = fragment.substring(pos2 + 1);
// Key contains data, lets not ignore it
if (ahelperKey != null) {
// Host resolvable only with addresshelper
if ( (host == null) || ("i2p".equals(host)) )
{
// Cannot check, use addresshelper key
addressHelpers.put(destination,ahelperKey);
} else {
// Host resolvable from database, verify addresshelper key
// Silently bypass correct keys, otherwise alert
if (!host.equals(ahelperKey))
{
// Conflict: handle when URL reconstruction done
ahelperConflict = true;
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix(requestId) + "Addresshelper key conflict for site [" + destination + "], trusted key [" + host + "], specified key [" + ahelperKey + "].");
}
}
}
} else {
// Other fragments, just pass along
// Append each fragment to urlEncoding
if ("".equals(urlEncoding)) {
urlEncoding = "?" + fragment;
} else {
urlEncoding = urlEncoding + "&" + fragment;
}
}
}
// Reconstruct the request minus the i2paddresshelper GET var
request = uriPath + urlEncoding + " " + protocolVersion;
// Did addresshelper key conflict?
if (ahelperConflict)
{
String str;
byte[] header;
str = FileUtil.readTextFile("docs/ahelper-conflict-header.ht", 100, true);
if (str != null) header = str.getBytes();
else header = ERR_AHELPER_CONFLICT;
if (out != null) {
long alias = I2PAppContext.getGlobalContext().random().nextLong();
String trustedURL = protocol + uriPath + urlEncoding;
String conflictURL = protocol + alias + ".i2p/?" + initialFragments;
out.write(header);
out.write(("To visit the destination in your host database, click <a href=\"" + trustedURL + "\">here</a>. To visit the conflicting addresshelper link by temporarily giving it a random alias, click <a href=\"" + conflictURL + "\">here</a>.<P/>").getBytes());
out.write("</div><p><i>I2P HTTP Proxy Server<br>Generated on: ".getBytes());
out.write(new Date().toString().getBytes());
out.write("</i></body></html>\n".getBytes());
out.flush();
}
s.close();
return;
}
}
String addressHelper = (String) addressHelpers.get(destination);
if (addressHelper != null) {
destination = addressHelper;
host = getHostName(destination);
ahelper = 1;
}
line = method + " " + request.substring(pos);
} else if (host.indexOf(".") != -1) {
// The request must be forwarded to a WWW proxy
@ -326,29 +409,29 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
} else {
if (line.startsWith("Host: ") && !usingWWWProxy) {
if (lowercaseLine.startsWith("host: ") && !usingWWWProxy) {
line = "Host: " + host;
if (_log.shouldLog(Log.INFO))
_log.info(getPrefix(requestId) + "Setting host = " + host);
} else if (line.startsWith("User-Agent: ")) {
} else if (lowercaseLine.startsWith("user-agent: ")) {
// always stripped, added back at the end
line = null;
continue;
} else if (line.startsWith("Accept")) {
} else if (lowercaseLine.startsWith("accept")) {
// strip the accept-blah headers, as they vary dramatically from
// browser to browser
line = null;
continue;
} else if (line.startsWith("Referer: ")) {
} else if (lowercaseLine.startsWith("referer: ")) {
// Shouldn't we be more specific, like accepting in-site referers ?
//line = "Referer: i2p";
line = null;
continue; // completely strip the line
} else if (line.startsWith("Via: ")) {
} else if (lowercaseLine.startsWith("via: ")) {
//line = "Via: i2p";
line = null;
continue; // completely strip the line
} else if (line.startsWith("From: ")) {
} else if (lowercaseLine.startsWith("from: ")) {
//line = "From: i2p";
line = null;
continue; // completely strip the line
@ -356,6 +439,13 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
if (line.length() == 0) {
String ok = getTunnel().getContext().getProperty("i2ptunnel.gzip");
boolean gzip = DEFAULT_GZIP;
if (ok != null)
gzip = Boolean.valueOf(ok).booleanValue();
if (gzip)
newRequest.append("Accept-Encoding: x-i2p-gzip\r\n");
newRequest.append("User-Agent: MYOB/6.66 (AN/ON)\r\n");
newRequest.append("Connection: close\r\n\r\n");
break;
@ -393,21 +483,33 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
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);
String str;
byte[] header;
if (usingWWWProxy)
str = FileUtil.readTextFile("docs/dnfp-header.ht", 100, true);
else if(ahelper != 0)
str = FileUtil.readTextFile("docs/dnfb-header.ht", 100, true);
else
str = FileUtil.readTextFile("docs/dnf-header.ht", 100, true);
if (str != null)
header = str.getBytes();
else
header = ERR_DESTINATION_UNKNOWN;
writeErrorMessage(header, out, targetRequest, usingWWWProxy, destination);
s.close();
return;
}
String remoteID;
Properties opts = new Properties();
opts.setProperty("i2p.streaming.inactivityTimeout", ""+120*1000);
//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);
//opts.setProperty("i2p.streaming.inactivityTimeoutAction", ""+1);
I2PSocket i2ps = createI2PSocket(dest, getDefaultOptions(opts));
byte[] data = newRequest.toString().getBytes("ISO-8859-1");
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
I2PTunnelRunner runner = new I2PTunnelHTTPClientRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
} catch (SocketException ex) {
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
l.log(ex.getMessage());
@ -466,10 +568,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
if (out != null) {
out.write(errMessage);
if (targetRequest != null) {
out.write(targetRequest.getBytes());
int protopos = targetRequest.indexOf(" ");
String uri = targetRequest.substring(0, protopos);
out.write("<a href=\"http://".getBytes());
out.write(uri.getBytes());
out.write("\">http://".getBytes());
out.write(uri.getBytes());
out.write("</a>".getBytes());
if (usingWWWProxy) out.write(("<br>WWW proxy: " + wwwProxy).getBytes());
}
out.write("<p /><i>Generated on: ".getBytes());
out.write("</div><p><i>I2P HTTP Proxy Server<br>Generated on: ".getBytes());
out.write(new Date().toString().getBytes());
out.write("</i></body></html>\n".getBytes());
out.flush();
@ -483,7 +591,17 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
_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);
String str;
byte[] header;
if (usingWWWProxy)
str = FileUtil.readTextFile("docs/dnfp-header.ht", 100, true);
else
str = FileUtil.readTextFile("docs/dnf-header.ht", 100, true);
if (str != null)
header = str.getBytes();
else
header = ERR_DESTINATION_UNKNOWN;
writeErrorMessage(header, out, targetRequest, usingWWWProxy, wwwProxy);
} catch (IOException ioe) {
_log.warn(getPrefix(requestId) + "Error writing out the 'destination was unknown' " + "message", ioe);
}

View File

@ -0,0 +1,64 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.io.*;
import java.net.Socket;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import net.i2p.I2PAppContext;
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 {
private Log _log;
public I2PTunnelHTTPClientRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, List sockList, Runnable onTimeout) {
super(s, i2ps, slock, initialI2PData, sockList, onTimeout);
_log = I2PAppContext.getGlobalContext().logManager().getLog(I2PTunnelHTTPClientRunner.class);
}
protected OutputStream getSocketOut() throws IOException {
OutputStream raw = super.getSocketOut();
return new HTTPResponseOutputStream(raw);
}
protected void close(OutputStream out, InputStream in, OutputStream i2pout, InputStream i2pin, Socket s, I2PSocket i2ps, Thread t1, Thread t2) throws InterruptedException, IOException {
try {
i2pin.close();
i2pout.close();
} catch (IOException ioe) {
// ignore
if (_log.shouldLog(Log.DEBUG))
_log.debug("Unable to close the i2p socket output stream: " + i2pout, ioe);
}
try {
in.close();
out.close();
} catch (IOException ioe) {
// ignore
if (_log.shouldLog(Log.DEBUG))
_log.debug("Unable to close the browser output stream: " + out, ioe);
}
i2ps.close();
s.close();
t1.join(30*1000);
t2.join(30*1000);
}
}

View File

@ -3,21 +3,18 @@
*/
package net.i2p.i2ptunnel;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.Iterator;
import java.util.Properties;
import java.util.zip.GZIPOutputStream;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.data.DataHelper;
import net.i2p.util.EventDispatcher;
import net.i2p.util.I2PThread;
@ -26,7 +23,9 @@ 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.
* header with whatever this instance has been configured with, and
* if the browser set Accept-encoding: x-i2p-gzip, gzip the http
* message body and set Content-encoding: x-i2p-gzip.
*
*/
public class I2PTunnelHTTPServer extends I2PTunnelServer {
@ -37,87 +36,204 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
public I2PTunnelHTTPServer(InetAddress host, int port, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host, port, privData, l, notifyThis, tunnel);
_spoofHost = spoofHost;
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 });
}
public I2PTunnelHTTPServer(InetAddress host, int port, File privkey, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host, port, privkey, privkeyname, l, notifyThis, tunnel);
_spoofHost = spoofHost;
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 });
}
public I2PTunnelHTTPServer(InetAddress host, int port, InputStream privData, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host, port, privData, privkeyname, l, notifyThis, tunnel);
_spoofHost = spoofHost;
_spoofHost = spoofHost;
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 });
}
public void run() {
try {
I2PServerSocket i2pss = sockMgr.getServerSocket();
while (true) {
I2PSocket i2ps = i2pss.accept();
if (i2ps == null) throw new I2PException("I2PServerSocket closed");
I2PThread t = new I2PThread(new Handler(i2ps));
t.start();
}
} catch (I2PException ex) {
_log.error("Error while waiting for I2PConnections", ex);
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
}
/**
* Async handler to keep .accept() from blocking too long.
* todo: replace with a thread pool so we dont get overrun by threads if/when
* receiving a lot of connection requests concurrently.
* Called by the thread pool of I2PSocket handlers
*
*/
private class Handler implements Runnable {
private I2PSocket _handleSocket;
public Handler(I2PSocket socket) {
_handleSocket = socket;
}
public void run() {
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
long afterSocket = -1;
//local is fast, so synchronously. Does not need that many
//threads.
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();
protected void blockingHandle(I2PSocket socket) {
long afterAccept = getTunnel().getContext().clock().now();
long afterSocket = -1;
//local is fast, so synchronously. Does not need that many
//threads.
try {
// give them 5 seconds to send in the HTTP request
socket.setReadTimeout(5*1000);
InputStream in = socket.getInputStream();
StringBuffer command = new StringBuffer(128);
Properties headers = readHeaders(in, command);
headers.setProperty("Host", _spoofHost);
if ( (_spoofHost != null) && (_spoofHost.trim().length() > 0) )
headers.setProperty("Host", _spoofHost);
headers.setProperty("Connection", "close");
return formatHeaders(headers, command);
// we keep the enc sent by the browser before clobbering it, since it may have
// been x-i2p-gzip
String enc = headers.getProperty("Accept-encoding");
headers.setProperty("Accept-encoding", "identity;q=1, *;q=0");
String modifiedHeader = formatHeaders(headers, command);
//String modifiedHeader = getModifiedHeader(socket);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Modified header: [" + modifiedHeader + "]");
socket.setReadTimeout(readTimeout);
Socket s = new Socket(remoteHost, remotePort);
afterSocket = getTunnel().getContext().clock().now();
// instead of i2ptunnelrunner, use something that reads the HTTP
// request from the socket, modifies the headers, sends the request to the
// server, reads the response headers, rewriting to include Content-encoding: x-i2p-gzip
// if it was one of the Accept-encoding: values, and gzip the payload
Properties opts = getTunnel().getClientOptions();
boolean allowGZIP = true;
if (opts != null) {
String val = opts.getProperty("i2ptunnel.gzip");
if ( (val != null) && (!Boolean.valueOf(val).booleanValue()) )
allowGZIP = false;
}
if (_log.shouldLog(Log.INFO))
_log.info("HTTP server encoding header: " + enc);
if ( allowGZIP && (enc != null) && (enc.indexOf("x-i2p-gzip") >= 0) ) {
I2PThread req = new I2PThread(new CompressedRequestor(s, socket, modifiedHeader), "http compressor");
req.start();
} else {
new I2PTunnelRunner(s, socket, slock, null, modifiedHeader.getBytes(), null);
}
} catch (SocketException ex) {
try {
socket.close();
} catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error while closing the received i2p con", ex);
}
} catch (IOException ex) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error while receiving the new HTTP request", ex);
}
long afterHandle = getTunnel().getContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
getTunnel().getContext().statManager().addRateData("i2ptunnel.httpserver.blockingHandleTime", timeToHandle, 0);
if ( (timeToHandle > 1000) && (_log.shouldLog(Log.WARN)) )
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
}
private class CompressedRequestor implements Runnable {
private Socket _webserver;
private I2PSocket _browser;
private String _headers;
public CompressedRequestor(Socket webserver, I2PSocket browser, String headers) {
_webserver = webserver;
_browser = browser;
_headers = headers;
}
public void run() {
if (_log.shouldLog(Log.INFO))
_log.info("Compressed requestor running");
OutputStream serverout = null;
OutputStream browserout = null;
InputStream browserin = null;
InputStream serverin = null;
try {
serverout = _webserver.getOutputStream();
if (_log.shouldLog(Log.INFO))
_log.info("request headers: " + _headers);
serverout.write(_headers.getBytes());
browserin = _browser.getInputStream();
I2PThread sender = new I2PThread(new Sender(serverout, browserin, "server: browser to server"), "http compressed sender");
sender.start();
browserout = _browser.getOutputStream();
serverin = _webserver.getInputStream();
CompressedResponseOutputStream compressedOut = new CompressedResponseOutputStream(browserout);
Sender s = new Sender(compressedOut, serverin, "server: server to browser");
if (_log.shouldLog(Log.INFO))
_log.info("Before pumping the compressed response");
s.run(); // same thread
if (_log.shouldLog(Log.INFO))
_log.info("After pumping the compressed response: " + compressedOut.getTotalRead() + "/" + compressedOut.getTotalCompressed());
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn("error compressing", ioe);
} finally {
if (browserout != null) try { browserout.close(); } catch (IOException ioe) {}
if (serverout != null) try { serverout.close(); } catch (IOException ioe) {}
if (browserin != null) try { browserin.close(); } catch (IOException ioe) {}
if (serverin != null) try { serverin.close(); } catch (IOException ioe) {}
}
}
}
private class Sender implements Runnable {
private OutputStream _out;
private InputStream _in;
private String _name;
public Sender(OutputStream out, InputStream in, String name) {
_out = out;
_in = in;
_name = name;
}
public void run() {
if (_log.shouldLog(Log.INFO))
_log.info(_name + ": Begin sending");
try {
byte buf[] = new byte[16*1024];
int read = 0;
int total = 0;
while ( (read = _in.read(buf)) != -1) {
if (_log.shouldLog(Log.INFO))
_log.info(_name + ": read " + read + " and sending through the stream");
_out.write(buf, 0, read);
total += read;
}
if (_log.shouldLog(Log.INFO))
_log.info(_name + ": Done sending: " + total);
//_out.flush();
} catch (IOException ioe) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Error sending", ioe);
} finally {
if (_out != null) try { _out.close(); } catch (IOException ioe) {}
if (_in != null) try { _in.close(); } catch (IOException ioe) {}
}
}
}
private class CompressedResponseOutputStream extends HTTPResponseOutputStream {
private InternalGZIPOutputStream _gzipOut;
public CompressedResponseOutputStream(OutputStream o) {
super(o);
}
protected boolean shouldCompress() { return true; }
protected void finishHeaders() throws IOException {
if (_log.shouldLog(Log.INFO))
_log.info("Including x-i2p-gzip as the content encoding in the response");
out.write("Content-encoding: x-i2p-gzip\n".getBytes());
super.finishHeaders();
}
protected void beginProcessing() throws IOException {
if (_log.shouldLog(Log.INFO))
_log.info("Beginning compression processing");
//out.flush();
_gzipOut = new InternalGZIPOutputStream(out);
out = _gzipOut;
}
public long getTotalRead() { return _gzipOut.getTotalRead(); }
public long getTotalCompressed() { return _gzipOut.getTotalCompressed(); }
}
private class InternalGZIPOutputStream extends GZIPOutputStream {
public InternalGZIPOutputStream(OutputStream target) throws IOException {
super(target);
}
public long getTotalRead() { return super.def.getTotalIn(); }
public long getTotalCompressed() { return super.def.getTotalOut(); }
}
private String formatHeaders(Properties headers, StringBuffer command) {
StringBuffer buf = new StringBuffer(command.length() + headers.size() * 64);
buf.append(command.toString()).append('\n');
@ -152,6 +268,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
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); // ": "
if ("Accept-encoding".equalsIgnoreCase(name))
name = "Accept-encoding";
headers.setProperty(name, value);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read the header [" + name + "] = [" + value + "]");

View File

@ -0,0 +1,389 @@
package net.i2p.i2ptunnel;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
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.DataHelper;
import net.i2p.data.Destination;
import net.i2p.util.EventDispatcher;
import net.i2p.util.Log;
public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable {
private static final Log _log = new Log(I2PTunnelIRCClient.class);
/** used to assign unique IDs to the threads / clients. no logic or functionality */
private static volatile long __clientId = 0;
/** 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;
/**
* @throws IllegalArgumentException if the I2PTunnel does not contain
* valid config to contact the router
*/
public I2PTunnelIRCClient(
int localPort,
String destinations,
Logging l,
boolean ownDest,
EventDispatcher notifyThis,
I2PTunnel tunnel) throws IllegalArgumentException {
super(localPort,
ownDest,
l,
notifyThis,
"IRCHandler " + (++__clientId), tunnel);
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 + "\"");
}
}
if (dests.size() <= 0) {
l.log("No target destinations found");
notifyEvent("openClientResult", "error");
return;
}
setName(getLocalPort() + " -> IRCClient");
startRunning();
notifyEvent("openIRCClientResult", "ok");
}
protected void clientConnectionRun(Socket s) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("got a connection.");
Destination dest = pickDestination();
I2PSocket i2ps = null;
try {
i2ps = createI2PSocket(dest);
i2ps.setReadTimeout(readTimeout);
Thread in = new Thread(new IrcInboundFilter(s,i2ps));
in.start();
Thread out = new Thread(new IrcOutboundFilter(s,i2ps));
out.start();
} catch (Exception ex) {
if (_log.shouldLog(Log.ERROR))
_log.error("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);
}
/*************************************************************************
*
*/
private class IrcInboundFilter implements Runnable {
private Socket local;
private I2PSocket remote;
IrcInboundFilter(Socket _local, I2PSocket _remote) {
local=_local;
remote=_remote;
}
public void run() {
InputStream input;
OutputStream output;
try {
input=remote.getInputStream();
output=local.getOutputStream();
} catch (IOException e) {
if (_log.shouldLog(Log.ERROR))
_log.error("IrcInboundFilter: no streams",e);
return;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("IrcInboundFilter: Running.");
while(true)
{
try {
String inmsg = DataHelper.readLine(input);
if(inmsg==null)
break;
if(inmsg.endsWith("\r"))
inmsg=inmsg.substring(0,inmsg.length()-1);
String outmsg = inboundFilter(inmsg);
if(outmsg!=null)
{
if(!inmsg.equals(outmsg)) {
if (_log.shouldLog(Log.WARN)) {
_log.warn("inbound FILTERED: "+outmsg);
_log.warn(" - inbound was: "+inmsg);
}
} else {
if (_log.shouldLog(Log.INFO))
_log.info("inbound: "+outmsg);
}
outmsg=outmsg+"\n";
output.write(outmsg.getBytes());
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("inbound BLOCKED: "+inmsg);
}
} catch (IOException e1) {
if (_log.shouldLog(Log.ERROR))
_log.error("IrcInboundFilter: disconnected",e1);
break;
}
}
try {
local.close();
} catch (IOException e) {
}
if(_log.shouldLog(Log.DEBUG))
_log.debug("IrcInboundFilter: Done.");
}
}
/*************************************************************************
*
*/
private class IrcOutboundFilter implements Runnable {
private Socket local;
private I2PSocket remote;
IrcOutboundFilter(Socket _local, I2PSocket _remote) {
local=_local;
remote=_remote;
}
public void run() {
InputStream input;
OutputStream output;
try {
input=local.getInputStream();
output=remote.getOutputStream();
} catch (IOException e) {
if (_log.shouldLog(Log.ERROR))
_log.error("IrcOutboundFilter: no streams",e);
return;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("IrcOutboundFilter: Running.");
while(true)
{
try {
String inmsg = DataHelper.readLine(input);
if(inmsg==null)
break;
if(inmsg.endsWith("\r"))
inmsg=inmsg.substring(0,inmsg.length()-1);
String outmsg = outboundFilter(inmsg);
if(outmsg!=null)
{
if(!inmsg.equals(outmsg)) {
if (_log.shouldLog(Log.WARN)) {
_log.warn("outbound FILTERED: "+outmsg);
_log.warn(" - outbound was: "+inmsg);
}
} else {
if (_log.shouldLog(Log.INFO))
_log.info("outbound: "+outmsg);
}
outmsg=outmsg+"\n";
output.write(outmsg.getBytes());
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("outbound BLOCKED: "+"\""+inmsg+"\"");
}
} catch (IOException e1) {
if (_log.shouldLog(Log.ERROR))
_log.error("IrcOutboundFilter: disconnected",e1);
break;
}
}
try {
remote.close();
} catch (IOException e) {
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("IrcOutboundFilter: Done.");
}
}
/*************************************************************************
*
*/
public static String inboundFilter(String s) {
String field[]=s.split(" ",4);
String command;
int idx=0;
final String[] allowedCommands =
{
"NOTICE",
"PING",
"PONG",
"MODE",
"JOIN",
"NICK",
"QUIT",
"PART",
"WALLOPS",
"ERROR"
};
if(field[0].charAt(0)==':')
idx++;
command = field[idx++];
idx++; //skip victim
// Allow numerical responses
try {
new Integer(command);
return s;
} catch(NumberFormatException nfe){}
// Allow all allowedCommands
for(int i=0;i<allowedCommands.length;i++) {
if(allowedCommands[i].equals(command))
return s;
}
// Allow PRIVMSG, but block CTCP.
if("PRIVMSG".equals(command))
{
String msg;
msg = field[idx++];
byte[] bytes = msg.getBytes();
if(bytes[1]==0x01)
{
// CTCP
msg=msg.substring(2);
if(msg.startsWith("ACTION ")) {
// /me says hello
return s;
}
return null; // Block all other ctcp
}
return s;
}
// Block the rest
return null;
}
public static String outboundFilter(String s) {
String field[]=s.split(" ",3);
String command;
final String[] allowedCommands =
{
"NOTICE",
"PONG",
"MODE",
"JOIN",
"NICK",
"WHO",
"WHOIS",
"LIST",
"NAMES",
"NICK",
// "QUIT", // replace with a filtered QUIT to hide client quit messages
"SILENCE",
"PART",
"OPER",
"PING",
"KICK",
"HELPME",
"RULES"
};
if(field[0].charAt(0)==':')
return null; // wtf
command = field[0].toUpperCase();
// Allow all allowedCommands
for(int i=0;i<allowedCommands.length;i++)
{
if(allowedCommands[i].equals(command))
return s;
}
// Allow PRIVMSG, but block CTCP (except ACTION).
if("PRIVMSG".equals(command))
{
String msg;
msg = field[2];
byte[] bytes = msg.getBytes();
if(bytes[1]==0x01)
{
// CTCP
msg=msg.substring(2);
if(msg.startsWith("ACTION ")) {
// /me says hello
return s;
}
return null; // Block all other ctcp
}
return s;
}
if("USER".equals(command)) {
int idx = field[2].lastIndexOf(":");
if(idx<0)
return "USER user hostname localhost :realname";
String realname = field[2].substring(idx+1);
String ret = "USER "+field[1]+" hostname localhost :"+realname;
return ret;
} else if ("QUIT".equals(command)) {
return "QUIT :leaving";
}
// Block the rest
return null;
}
}

View File

@ -3,7 +3,6 @@
*/
package net.i2p.i2ptunnel;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
@ -31,7 +30,7 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
* Sun's impl of BufferedOutputStream), but that is the streaming
* api's job...
*/
static int MAX_PACKET_SIZE = 1024 * 32;
static int MAX_PACKET_SIZE = 1024 * 4;
static final int NETWORK_BUFFER_SIZE = MAX_PACKET_SIZE;
@ -112,10 +111,13 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
return startedOn;
}
protected InputStream getSocketIn() throws IOException { return s.getInputStream(); }
protected OutputStream getSocketOut() throws IOException { return s.getOutputStream(); }
public void run() {
try {
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream(); // = new BufferedOutputStream(s.getOutputStream(), NETWORK_BUFFER_SIZE);
InputStream in = getSocketIn();
OutputStream out = getSocketOut(); // = new BufferedOutputStream(s.getOutputStream(), NETWORK_BUFFER_SIZE);
i2ps.setSocketErrorListener(this);
InputStream i2pin = i2ps.getInputStream();
OutputStream i2pout = i2ps.getOutputStream(); //new BufferedOutputStream(i2ps.getOutputStream(), MAX_PACKET_SIZE);
@ -151,11 +153,8 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
onTimeout.run();
}
// now one connection is dead - kill the other as well.
s.close();
i2ps.close();
t1.join(30*1000);
t2.join(30*1000);
// now one connection is dead - kill the other as well, after making sure we flush
close(out, in, i2pout, i2pin, s, i2ps, t1, t2);
} catch (InterruptedException ex) {
if (_log.shouldLog(Log.ERROR))
_log.error("Interrupted", ex);
@ -186,6 +185,27 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
}
}
protected void close(OutputStream out, InputStream in, OutputStream i2pout, InputStream i2pin, Socket s, I2PSocket i2ps, Thread t1, Thread t2) throws InterruptedException, IOException {
try {
out.flush();
} catch (IOException ioe) {
// ignore
}
try {
i2pout.flush();
} catch (IOException ioe) {
// ignore
}
in.close();
i2pin.close();
// ok, yeah, there's a race here in theory, if data comes in after flushing and before
// closing, but its better than before...
s.close();
i2ps.close();
t1.join(30*1000);
t2.join(30*1000);
}
public void errorOccurred() {
synchronized (finishLock) {
finished = true;
@ -216,7 +236,7 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
this.out = out;
_toI2P = toI2P;
direction = (toI2P ? "toI2P" : "fromI2P");
_cache = ByteCache.getInstance(256, NETWORK_BUFFER_SIZE);
_cache = ByteCache.getInstance(32, NETWORK_BUFFER_SIZE);
setName("StreamForwarder " + _runnerId + "." + (++__forwarderId));
start();
}
@ -281,6 +301,7 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
//else
// _log.warn("You may ignore this", ex);
} finally {
_cache.release(ba);
if (_log.shouldLog(Log.INFO)) {
_log.info(direction + ": done forwarding between "
+ from + " and " + to);
@ -302,7 +323,6 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
finishLock.notifyAll();
// the main thread will close sockets etc. now
}
_cache.release(ba);
}
}
}

View File

@ -11,6 +11,7 @@ import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.ConnectException;
import java.util.Iterator;
import java.util.Properties;
@ -39,6 +40,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
protected InetAddress remoteHost;
protected int remotePort;
private boolean _usePool;
private Logging l;
@ -46,15 +48,27 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
/** default timeout to 3 minutes - override if desired */
protected long readTimeout = DEFAULT_READ_TIMEOUT;
private static final boolean DEFAULT_USE_POOL = false;
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));
String usePool = tunnel.getClientOptions().getProperty("i2ptunnel.usePool");
if (usePool != null)
_usePool = "true".equalsIgnoreCase(usePool);
else
_usePool = DEFAULT_USE_POOL;
init(host, port, bais, privData, l);
}
public I2PTunnelServer(InetAddress host, int port, File privkey, String privkeyname, Logging l,
EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host + ":" + port + " <- " + privkeyname, notifyThis, tunnel);
String usePool = tunnel.getClientOptions().getProperty("i2ptunnel.usePool");
if (usePool != null)
_usePool = "true".equalsIgnoreCase(usePool);
else
_usePool = DEFAULT_USE_POOL;
try {
init(host, port, new FileInputStream(privkey), privkeyname, l);
} catch (IOException ioe) {
@ -65,6 +79,11 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
public I2PTunnelServer(InetAddress host, int port, InputStream privData, String privkeyname, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host + ":" + port + " <- " + privkeyname, notifyThis, tunnel);
String usePool = tunnel.getClientOptions().getProperty("i2ptunnel.usePool");
if (usePool != null)
_usePool = "true".equalsIgnoreCase(usePool);
else
_usePool = DEFAULT_USE_POOL;
init(host, port, privData, privkeyname, l);
}
@ -75,10 +94,25 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
I2PClient client = I2PClientFactory.createClient();
Properties props = new Properties();
props.putAll(getTunnel().getClientOptions());
synchronized (slock) {
sockMgr = I2PSocketManagerFactory.createManager(privData, getTunnel().host, Integer.parseInt(getTunnel().port),
props);
int portNum = 7654;
if (getTunnel().port != null) {
try {
portNum = Integer.parseInt(getTunnel().port);
} catch (NumberFormatException nfe) {
_log.log(Log.CRIT, "Invalid port specified [" + getTunnel().port + "], reverting to " + portNum);
}
}
while (sockMgr == null) {
synchronized (slock) {
sockMgr = I2PSocketManagerFactory.createManager(privData, getTunnel().host, portNum,
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());
@ -144,58 +178,103 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
}
}
private static final String PROP_HANDLER_COUNT = "i2ptunnel.blockingHandlerCount";
private static final int DEFAULT_HANDLER_COUNT = 10;
protected int getHandlerCount() {
int rv = DEFAULT_HANDLER_COUNT;
String cnt = getTunnel().getClientOptions().getProperty(PROP_HANDLER_COUNT);
if (cnt != null) {
try {
rv = Integer.parseInt(cnt);
if (rv <= 0)
rv = DEFAULT_HANDLER_COUNT;
} catch (NumberFormatException nfe) {
rv = DEFAULT_HANDLER_COUNT;
}
}
return rv;
}
public void run() {
try {
if (shouldUsePool()) {
I2PServerSocket i2pss = sockMgr.getServerSocket();
int handlers = getHandlerCount();
for (int i = 0; i < handlers; i++) {
I2PThread handler = new I2PThread(new Handler(i2pss), "Handle Server " + i);
handler.start();
}
} else {
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();
try {
final I2PSocket i2ps = i2pss.accept();
if (i2ps == null) throw new I2PException("I2PServerSocket closed");
new I2PThread(new Runnable() { public void run() { blockingHandle(i2ps); } }).start();
} catch (I2PException ipe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error accepting - KILLING THE TUNNEL SERVER", ipe);
return;
} catch (ConnectException ce) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error accepting", ce);
// not killing the server..
}
}
} catch (I2PException ex) {
_log.error("Error while waiting for I2PConnections", ex);
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
}
public boolean shouldUsePool() { return _usePool; }
/**
* Async handler to keep .accept() from blocking too long.
* todo: replace with a thread pool so we dont get overrun by threads if/when
* receiving a lot of connection requests concurrently.
* minor thread pool to pull off the accept() concurrently. there are still lots
* (and lots) of wasted threads within the I2PTunnelRunner, but its a start
*
*/
private class Handler implements Runnable {
private I2PSocket _handleSocket;
public Handler(I2PSocket socket) {
_handleSocket = socket;
private I2PServerSocket _serverSocket;
public Handler(I2PServerSocket serverSocket) {
_serverSocket = serverSocket;
}
public void run() {
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
long afterSocket = -1;
//local is fast, so synchronously. Does not need that many
//threads.
try {
_handleSocket.setReadTimeout(readTimeout);
Socket s = new Socket(remoteHost, remotePort);
afterSocket = I2PAppContext.getGlobalContext().clock().now();
new I2PTunnelRunner(s, _handleSocket, slock, null, null);
} catch (SocketException ex) {
while (open) {
try {
_handleSocket.close();
} catch (IOException ioe) {
_log.error("Error while closing the received i2p con", ex);
blockingHandle(_serverSocket.accept());
} catch (I2PException ex) {
_log.error("Error while waiting for I2PConnections", ex);
return;
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
return;
}
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
long afterHandle = I2PAppContext.getGlobalContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
if (timeToHandle > 1000)
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
}
}
protected void blockingHandle(I2PSocket socket) {
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
long afterSocket = -1;
//local is fast, so synchronously. Does not need that many
//threads.
try {
socket.setReadTimeout(readTimeout);
Socket s = new Socket(remoteHost, remotePort);
afterSocket = I2PAppContext.getGlobalContext().clock().now();
new I2PTunnelRunner(s, socket, slock, null, null);
} catch (SocketException ex) {
try {
socket.close();
} catch (IOException ioe) {
_log.error("Error while closing the received i2p con", ex);
}
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
long afterHandle = I2PAppContext.getGlobalContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
if (timeToHandle > 1000)
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
}
}

View File

@ -102,6 +102,7 @@ public class TunnelController implements Logging {
public void startTunnelBackground() {
if (_running) return;
_starting = true;
new I2PThread(new Runnable() { public void run() { startTunnel(); } }).start();
}
@ -134,6 +135,8 @@ public class TunnelController implements Logging {
}
if ("httpclient".equals(type)) {
startHttpClient();
}else if("ircclient".equals(type)) {
startIrcClient();
} else if ("client".equals(type)) {
startClient();
} else if ("server".equals(type)) {
@ -152,10 +155,23 @@ public class TunnelController implements Logging {
setListenOn();
String listenPort = getListenPort();
String proxyList = getProxyList();
String sharedClient = getSharedClient();
if (proxyList == null)
_tunnel.runHttpClient(new String[] { listenPort }, this);
_tunnel.runHttpClient(new String[] { listenPort, sharedClient }, this);
else
_tunnel.runHttpClient(new String[] { listenPort, proxyList }, this);
_tunnel.runHttpClient(new String[] { listenPort, sharedClient, proxyList }, this);
acquire();
_running = true;
}
private void startIrcClient() {
setI2CPOptions();
setSessionOptions();
setListenOn();
String listenPort = getListenPort();
String dest = getTargetDestination();
String sharedClient = getSharedClient();
_tunnel.runIrcClient(new String[] { listenPort, dest, sharedClient }, this);
acquire();
_running = true;
}
@ -198,7 +214,8 @@ public class TunnelController implements Logging {
setListenOn();
String listenPort = getListenPort();
String dest = getTargetDestination();
_tunnel.runClient(new String[] { listenPort, dest }, this);
String sharedClient = getSharedClient();
_tunnel.runClient(new String[] { listenPort, dest, sharedClient }, this);
acquire();
_running = true;
}
@ -257,8 +274,16 @@ public class TunnelController implements Logging {
if ("localhost".equals(_tunnel.host))
_tunnel.host = "127.0.0.1";
String port = getI2CPPort();
if ( (port != null) && (port.length() > 0) )
_tunnel.port = port;
if ( (port != null) && (port.length() > 0) ) {
try {
int portNum = Integer.parseInt(port);
_tunnel.port = String.valueOf(portNum);
} catch (NumberFormatException nfe) {
_tunnel.port = "7654";
}
} else {
_tunnel.port = "7654";
}
}
public void stopTunnel() {
@ -322,7 +347,20 @@ public class TunnelController implements Logging {
public String getListenPort() { return _config.getProperty("listenPort"); }
public String getTargetDestination() { return _config.getProperty("targetDestination"); }
public String getProxyList() { return _config.getProperty("proxyList"); }
public String getSharedClient() { return _config.getProperty("sharedClient", "true"); }
public boolean getStartOnLoad() { return "true".equalsIgnoreCase(_config.getProperty("startOnLoad", "true")); }
public String getMyDestination() {
if (_tunnel != null) {
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)
return dest.toBase64();
}
}
return null;
}
public boolean getIsRunning() { return _running; }
public boolean getIsStarting() { return _starting; }

View File

@ -1,12 +1,8 @@
package net.i2p.i2ptunnel;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@ -180,7 +176,7 @@ public class TunnelControllerGroup {
List msgs = new ArrayList();
for (int i = 0; i < _controllers.size(); i++) {
TunnelController controller = (TunnelController)_controllers.get(i);
controller.startTunnel();
controller.startTunnelBackground();
msgs.addAll(controller.clearMessages());
}

View File

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

View File

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

View File

@ -1,382 +0,0 @@
package net.i2p.i2ptunnel;
import java.io.File;
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");
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");
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\" />");
}
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\" />");
}
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("\" />");
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");
}
/**
* 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;
int connectDelay = 0;
Properties opts = getOptions(controller);
if (opts != null) {
String depth = opts.getProperty("tunnels.depthInbound");
if (depth != null) {
try {
tunnelDepth = Integer.parseInt(depth);
} catch (NumberFormatException nfe) {
tunnelDepth = 2;
}
}
String num = opts.getProperty("tunnels.numInbound");
if (num != null) {
try {
numTunnels = Integer.parseInt(num);
} catch (NumberFormatException nfe) {
numTunnels = 2;
}
}
String delay = opts.getProperty("i2p.streaming.connectDelay");
if (delay != null) {
try {
connectDelay = Integer.parseInt(delay);
} catch (NumberFormatException nfe) {
connectDelay = 0;
}
}
}
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>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>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 ("tunnels.depthInbound".equals(key)) continue;
if ("tunnels.numInbound".equals(key)) continue;
if ("i2p.streaming.connectDelay".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

@ -1,404 +0,0 @@
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 boolean _startOnLoad;
private boolean _privKeyGenerate;
private boolean _removeConfirmed;
public WebEditPageHelper() {
_action = null;
_type = null;
_id = null;
_removeConfirmed = false;
_log = I2PAppContext.getGlobalContext().logManager().getLog(WebEditPageHelper.class);
}
/**
* 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;
}
/**
* 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 "";
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.tunnels.numInbound", _tunnelCount);
if (_tunnelDepth != null)
cOpt.setProperty("option.tunnels.depthInbound", _tunnelDepth);
if (_connectDelay)
cOpt.setProperty("option.i2p.streaming.connectDelay", "1000");
else
cOpt.setProperty("option.i2p.streaming.connectDelay", "0");
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 ("tunnels.numInbound".equals(key)) continue;
if ("tunnels.depthInbound".equals(key)) continue;
if ("i2p.streaming.connectDelay".equals(key)) continue;
config.setProperty("option." + key, val);
}
}
config.setProperty("startOnLoad", _startOnLoad + "");
if (_tunnelCount != null)
config.setProperty("option.tunnels.numInbound", _tunnelCount);
if (_tunnelDepth != null)
config.setProperty("option.tunnels.depthInbound", _tunnelDepth);
if (_connectDelay)
config.setProperty("option.i2p.streaming.connectDelay", "1000");
else
config.setProperty("option.i2p.streaming.connectDelay", "0");
}
/**
* 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

@ -1,183 +0,0 @@
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 Log _log;
private String _action;
private int _controllerNum;
public WebStatusPageHelper() {
_action = null;
_controllerNum = -1;
_log = I2PAppContext.getGlobalContext().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 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";
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));
buf.append("</li>\n");
}
buf.append("</ul>");
return buf.toString();
}
private void getSummary(StringBuffer buf, int num, TunnelController controller) {
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).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).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();
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.startTunnel();
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

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

View File

@ -0,0 +1,227 @@
package net.i2p.i2ptunnel.web;
/*
* 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.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.i2ptunnel.TunnelController;
import net.i2p.i2ptunnel.TunnelControllerGroup;
import net.i2p.util.Log;
/**
* Ugly little accessor for the edit page
*/
public class EditBean extends IndexBean {
public EditBean() { super(); }
public static boolean staticIsClient(int tunnel) {
TunnelControllerGroup group = TunnelControllerGroup.getInstance();
List controllers = group.getControllers();
if (controllers.size() > tunnel) {
TunnelController cur = (TunnelController)controllers.get(tunnel);
if (cur == null) return false;
return ( ("client".equals(cur.getType())) ||
("httpclient".equals(cur.getType()))||
("ircclient".equals(cur.getType())));
} else {
return false;
}
}
public String getTargetHost(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getTargetHost();
else
return "";
}
public String getTargetPort(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getTargetPort();
else
return "";
}
public String getSpoofedHost(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getSpoofedHost();
else
return "";
}
public String getPrivateKeyFile(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getPrivKeyFile();
else
return "";
}
public boolean startAutomatically(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getStartOnLoad();
else
return false;
}
public boolean isSharedClient(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return "true".equalsIgnoreCase(tun.getSharedClient());
else
return true;
}
public boolean shouldDelay(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null) {
Properties opts = getOptions(tun);
if (opts != null) {
String delay = opts.getProperty("i2p.streaming.connectDelay");
if ( (delay == null) || ("0".equals(delay)) )
return false;
else
return true;
} else {
return false;
}
} else {
return false;
}
}
public boolean isInteractive(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null) {
Properties opts = getOptions(tun);
if (opts != null) {
String wsiz = opts.getProperty("i2p.streaming.maxWindowSize");
if ( (wsiz == null) || (!"1".equals(wsiz)) )
return false;
else
return true;
} else {
return false;
}
} else {
return false;
}
}
public int getTunnelDepth(int tunnel, int defaultLength) {
TunnelController tun = getController(tunnel);
if (tun != null) {
Properties opts = getOptions(tun);
if (opts != null) {
String len = opts.getProperty("inbound.length");
if (len == null) return defaultLength;
try {
return Integer.parseInt(len);
} catch (NumberFormatException nfe) {
return defaultLength;
}
} else {
return defaultLength;
}
} else {
return defaultLength;
}
}
public int getTunnelCount(int tunnel, int defaultCount) {
TunnelController tun = getController(tunnel);
if (tun != null) {
Properties opts = getOptions(tun);
if (opts != null) {
String len = opts.getProperty("inbound.quantity");
if (len == null) return defaultCount;
try {
return Integer.parseInt(len);
} catch (NumberFormatException nfe) {
return defaultCount;
}
} else {
return defaultCount;
}
} else {
return defaultCount;
}
}
public String getI2CPHost(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getI2CPHost();
else
return "localhost";
}
public String getI2CPPort(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getI2CPPort();
else
return "7654";
}
public String getCustomOptions(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null) {
Properties opts = getOptions(tun);
if (opts == null) return "";
StringBuffer buf = new StringBuffer(64);
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++;
}
return buf.toString();
} else {
return "";
}
}
/**
* 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,676 @@
package net.i2p.i2ptunnel.web;
/*
* 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.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.i2ptunnel.TunnelController;
import net.i2p.i2ptunnel.TunnelControllerGroup;
import net.i2p.util.Log;
/**
* Simple accessor for exposing tunnel info, but also an ugly form handler
*
*/
public class IndexBean {
protected I2PAppContext _context;
protected Log _log;
protected TunnelControllerGroup _group;
private String _action;
private int _tunnel;
private long _prevNonce;
private long _curNonce;
private long _nextNonce;
private String _passphrase;
private String _type;
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 _sharedClient;
private boolean _privKeyGenerate;
private boolean _removeConfirmed;
public static final int RUNNING = 1;
public static final int STARTING = 2;
public static final int NOT_RUNNING = 3;
public static final String PROP_TUNNEL_PASSPHRASE = "i2ptunnel.passphrase";
static final String PROP_NONCE = IndexBean.class.getName() + ".nonce";
static final String CLIENT_NICKNAME = "shared clients";
public IndexBean() {
_context = I2PAppContext.getGlobalContext();
_log = _context.logManager().getLog(IndexBean.class);
_group = TunnelControllerGroup.getInstance();
_action = null;
_tunnel = -1;
_curNonce = -1;
_prevNonce = -1;
try {
String nonce = System.getProperty(PROP_NONCE);
if (nonce != null)
_prevNonce = Long.parseLong(nonce);
} catch (NumberFormatException nfe) {}
_nextNonce = _context.random().nextLong();
System.setProperty(PROP_NONCE, Long.toString(_nextNonce));
}
public long getNextNonce() { return _nextNonce; }
public void setNonce(String nonce) {
if ( (nonce == null) || (nonce.trim().length() <= 0) ) return;
try {
_curNonce = Long.parseLong(nonce);
} catch (NumberFormatException nfe) {
_curNonce = -1;
}
}
public void setPassphrase(String phrase) {
_passphrase = phrase;
}
public void setAction(String action) {
if ( (action == null) || (action.trim().length() <= 0) ) return;
_action = action;
}
public void setTunnel(String tunnel) {
if ( (tunnel == null) || (tunnel.trim().length() <= 0) ) return;
try {
_tunnel = Integer.parseInt(tunnel);
} catch (NumberFormatException nfe) {
_tunnel = -1;
}
}
private boolean validPassphrase(String proposed) {
if (proposed == null) return false;
String pass = _context.getProperty(PROP_TUNNEL_PASSPHRASE);
if ( (pass != null) && (pass.trim().length() > 0) )
return pass.trim().equals(proposed.trim());
else
return false;
}
private String processAction() {
if ( (_action == null) || (_action.trim().length() <= 0) )
return "";
if ( (_prevNonce != _curNonce) && (!validPassphrase(_passphrase)) )
return "Invalid nonce, are you being spoofed?";
if ("Stop all tunnels".equals(_action))
return stopAll();
else if ("Start all tunnels".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 if ("Save changes".equals(_action))
return saveChanges();
else if ("Delete this proxy".equals(_action))
return deleteTunnel();
else
return "Action " + _action + " unknown";
}
private String stopAll() {
if (_group == null) return "";
List msgs = _group.stopAllControllers();
return getMessages(msgs);
}
private String startAll() {
if (_group == null) return "";
List msgs = _group.startAllControllers();
return getMessages(msgs);
}
private String restartAll() {
if (_group == null) return "";
List msgs = _group.restartAllControllers();
return getMessages(msgs);
}
private String reloadConfig() {
if (_group == null) return "";
_group.reloadControllers();
return "Config reloaded";
}
private String start() {
if (_tunnel < 0) return "Invalid tunnel";
List controllers = _group.getControllers();
if (_tunnel >= controllers.size()) return "Invalid tunnel";
TunnelController controller = (TunnelController)controllers.get(_tunnel);
controller.startTunnelBackground();
return "";
}
private String stop() {
if (_tunnel < 0) return "Invalid tunnel";
List controllers = _group.getControllers();
if (_tunnel >= controllers.size()) return "Invalid tunnel";
TunnelController controller = (TunnelController)controllers.get(_tunnel);
controller.stopTunnel();
return "";
}
private String saveChanges() {
TunnelController cur = getController(_tunnel);
Properties config = getConfig();
if (config == null)
return "Invalid params";
if (cur == null) {
// creating new
cur = new TunnelController(config, "", true);
_group.addController(cur);
if (cur.getStartOnLoad())
cur.startTunnelBackground();
} else {
cur.setConfig(config, "");
}
if ("ircclient".equals(cur.getType()) ||
"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 = _group.getControllers();
for (int i = 0; i < controllers.size(); i++) {
TunnelController c = (TunnelController)controllers.get(i);
if (c == cur) continue;
//only change when they really are declared of beeing a sharedClient
if (("httpclient".equals(c.getType()) ||
"ircclient".equals(c.getType())||
"client".equals(c.getType())
) && "true".equalsIgnoreCase(c.getSharedClient())) {
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);
}
cOpt.setProperty("option.inbound.nickname", CLIENT_NICKNAME);
cOpt.setProperty("option.outbound.nickname", CLIENT_NICKNAME);
c.setConfig(cOpt, "");
}
}
}
List msgs = doSave();
msgs.add(0, "Changes saved");
return getMessages(msgs);
}
private List doSave() {
_group.saveConfig();
return _group.clearAllMessages();
}
private String deleteTunnel() {
if (!_removeConfirmed)
return "Please confirm removal";
TunnelController cur = getController(_tunnel);
if (cur == null)
return "Invalid tunnel number";
List msgs = _group.removeController(cur);
msgs.addAll(doSave());
return getMessages(msgs);
}
/**
* Executes any action requested (start/stop/etc) and dump out the
* messages.
*
*/
public String getMessages() {
if (_group == null)
return "";
StringBuffer buf = new StringBuffer(512);
if (_action != null) {
try {
buf.append(processAction()).append("\n");
} catch (Exception e) {
_log.log(Log.CRIT, "Error processing " + _action, e);
}
}
getMessages(_group.clearAllMessages(), buf);
return buf.toString();
}
////
// The remaining methods are simple bean props for the jsp to query
////
public int getTunnelCount() {
if (_group == null) return 0;
return _group.getControllers().size();
}
public boolean isClient(int tunnelNum) {
TunnelController cur = getController(tunnelNum);
if (cur == null) return false;
return ( ("client".equals(cur.getType())) ||
("httpclient".equals(cur.getType())) ||
("ircclient".equals(cur.getType())));
}
public String getTunnelName(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getName();
else
return "";
}
public String getClientPort(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getListenPort();
else
return "";
}
public String getTunnelType(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return getTypeName(tun.getType());
else
return "";
}
public String getTypeName(String internalType) {
if ("client".equals(internalType)) return "Client proxy";
else if ("httpclient".equals(internalType)) return "HTTP proxy";
else if ("ircclient".equals(internalType)) return "IRC proxy";
else if ("server".equals(internalType)) return "Server";
else if ("httpserver".equals(internalType)) return "HTTP server";
else return internalType;
}
public String getInternalType(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getType();
else
return "";
}
public String getClientInterface(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getListenOnInterface();
else
return "";
}
public int getTunnelStatus(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun == null) return NOT_RUNNING;
if (tun.getIsRunning()) return RUNNING;
else if (tun.getIsStarting()) return STARTING;
else return NOT_RUNNING;
}
public String getTunnelDescription(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getDescription();
else
return "";
}
public String getSharedClient(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getSharedClient();
else
return "";
}
public String getClientDestination(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun == null) return "";
if ("client".equals(tun.getType())||"ircclient".equals(tun.getType())) return tun.getTargetDestination();
else return tun.getProxyList();
}
public String getServerTarget(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)
return tun.getTargetHost() + ':' + tun.getTargetPort();
else
return "";
}
public String getDestinationBase64(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null) {
String rv = tun.getMyDestination();
if (rv != null)
return rv;
else
return "";
} else {
return "";
}
}
///
/// bean props for form submission
///
/**
* What type of tunnel (httpclient, ircclient, client, or server). This is
* required when adding a new tunnel.
*
*/
public void setType(String type) {
_type = (type != null ? type.trim() : null);
}
String getType() { return _type; }
/** 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/ircclient listen on */
public void setPort(String port) {
_port = (port != null ? port.trim() : null);
}
/**
* what interface should this client/httpclient/ircclient 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/ircclient 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 (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 setShared(String moo) {
_sharedClient=true;
}
public void setShared(boolean val) {
_sharedClient=val;
}
public void setConnectDelay(String moo) {
_connectDelay = true;
}
public void setProfile(String profile) {
_profile = profile;
}
/**
* 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);
config.setProperty("option.inbound.nickname", CLIENT_NICKNAME);
config.setProperty("option.outbound.nickname", CLIENT_NICKNAME);
if (_name != null && !_sharedClient) {
config.setProperty("option.inbound.nickname", _name);
config.setProperty("option.outbound.nickname", _name);
}
config.setProperty("sharedClient", _sharedClient + "");
}else if ("ircclient".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);
config.setProperty("option.inbound.nickname", CLIENT_NICKNAME);
config.setProperty("option.outbound.nickname", CLIENT_NICKNAME);
if (_name != null && !_sharedClient) {
config.setProperty("option.inbound.nickname", _name);
config.setProperty("option.outbound.nickname", _name);
}
config.setProperty("sharedClient", _sharedClient + "");
} 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);
config.setProperty("option.inbound.nickname", CLIENT_NICKNAME);
config.setProperty("option.outbound.nickname", CLIENT_NICKNAME);
if (_name != null && !_sharedClient) {
config.setProperty("option.inbound.nickname", _name);
config.setProperty("option.outbound.nickname", _name);
}
config.setProperty("sharedClient", _sharedClient + "");
} 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) && (_i2cpPort.trim().length() > 0) )
config.setProperty("i2cpPort", _i2cpPort);
else
config.setProperty("i2cpPort", "7654");
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) {
if ( ((!"client".equals(_type)) && (!"httpclient".equals(_type))&& (!"ircclient".equals(_type))) || (!_sharedClient) ) {
config.setProperty("option.inbound.nickname", _name);
config.setProperty("option.outbound.nickname", _name);
} else {
config.setProperty("option.inbound.nickname", CLIENT_NICKNAME);
config.setProperty("option.outbound.nickname", CLIENT_NICKNAME);
}
}
if ("interactive".equals(_profile))
config.setProperty("option.i2p.streaming.maxWindowSize", "1");
else
config.remove("option.i2p.streaming.maxWindowSize");
}
///
///
///
protected TunnelController getController(int tunnel) {
if (tunnel < 0) return null;
if (_group == null) return null;
List controllers = _group.getControllers();
if (controllers.size() > tunnel)
return (TunnelController)controllers.get(tunnel);
else
return null;
}
private String getMessages(List msgs) {
StringBuffer buf = new StringBuffer(128);
getMessages(msgs, buf);
return buf.toString();
}
private void getMessages(List msgs, StringBuffer buf) {
if (msgs == null) return;
for (int i = 0; i < msgs.size(); i++) {
buf.append((String)msgs.get(i)).append("\n");
}
}
}

View File

@ -1,16 +1,26 @@
<%@page contentType="text/html" %>
<%@page contentType="text/html" import="net.i2p.i2ptunnel.web.EditBean" %>
<!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>
<% String tun = request.getParameter("tunnel");
if (tun != null) {
try {
int curTunnel = Integer.parseInt(tun);
if (EditBean.staticIsClient(curTunnel)) {
%><jsp:include page="editClient.jsp" /><%
} else {
%><jsp:include page="editServer.jsp" /><%
}
} catch (NumberFormatException nfe) {
%>Invalid tunnel parameter<%
}
} else {
String type = request.getParameter("type");
int curTunnel = -1;
if ("client".equals(type) || "httpclient".equals(type) || "ircclient".equals(type)) {
%><jsp:include page="editClient.jsp" /><%
} else if ("server".equals(type) || "httpserver".equals(type)) {
%><jsp:include page="editServer.jsp" /><%
} else {
%>Invalid tunnel type<%
}
}
%>

View File

@ -0,0 +1,293 @@
<%@page contentType="text/html" %>
<jsp:useBean class="net.i2p.i2ptunnel.web.EditBean" id="editBean" scope="request" />
<% String tun = request.getParameter("tunnel");
int curTunnel = -1;
if (tun != null) {
try {
curTunnel = Integer.parseInt(tun);
} catch (NumberFormatException nfe) {
curTunnel = -1;
}
}
%>
<html>
<head>
<title>I2PTunnel Webmanager</title>
</head>
<body>
<form action="index.jsp">
<input type="hidden" name="tunnel" value="<%=request.getParameter("tunnel")%>" />
<input type="hidden" name="nonce" value="<%=editBean.getNextNonce()%>" />
<table width="80%" align="center" border="0" cellpadding="1" cellspacing="1">
<tr>
<td style="background-color:#000">
<div style="background-color:#ffffed">
<table width="100%" align="center" border="0" cellpadding="4" cellspacing="1">
<tr>
<td colspan="2" align="center">
<% if (curTunnel >= 0) { %>
<b>Edit proxy settings</b>
<% } else { %>
<b>New proxy settings</b>
<% } %>
</td>
</tr>
<tr>
<td><b>Name: </b>
</td>
<td>
<input type="text" size="30" maxlength="50" name="name" value="<%=editBean.getTunnelName(curTunnel)%>" />
</td>
</tr>
<tr>
<td><b>Type: </b>
<td><%
if (curTunnel >= 0) {
%><%=editBean.getTunnelType(curTunnel)%>
<input type="hidden" name="type" value="<%=editBean.getInternalType(curTunnel)%>" />
<%
} else {
%><%=editBean.getTypeName(request.getParameter("type"))%>
<input type="hidden" name="type" value="<%=request.getParameter("type")%>" />
<%
}
%></td>
</tr>
<tr>
<td><b>Description: </b>
</td>
<td>
<input type="text" size="60" maxlength="80" name="description" value="<%=editBean.getTunnelDescription(curTunnel)%>" />
</td>
</tr>
<tr>
<td><b>Start automatically?:</b>
</td>
<td>
<% if (editBean.startAutomatically(curTunnel)) { %>
<input value="1" type="checkbox" name="startOnLoad" checked="true" />
<% } else { %>
<input value="1" type="checkbox" name="startOnLoad" />
<% } %>
<i>(Check the Box for 'YES')</i>
</td>
</tr>
<tr>
<td> <b>Listening Port:</b>
</td>
<td>
<input type="text" size="6" maxlength="5" name="port" value="<%=editBean.getClientPort(curTunnel)%>" />
</td>
</tr>
<tr>
<td><b> Accessable by:</b>
</td>
<td>
<select name="reachableBy">
<% String clientInterface = editBean.getClientInterface(curTunnel); %>
<% if (("127.0.0.1".equals(clientInterface)) || (clientInterface == null) || (clientInterface.trim().length() <= 0)) { %>
<option value="127.0.0.1" selected="true">Locally (127.0.0.1)</option>
<option value="0.0.0.0">Everyone (0.0.0.0)</option>
<option value="other">LAN Hosts (Please specify your LAN address)</option>
</select>
&nbsp;&nbsp;
<b>others:</b>
<input type="text" name="reachableByOther" size="20" />
<% } else if ("0.0.0.0".equals(clientInterface)) { %>
<option value="127.0.0.1">Locally (127.0.0.1)</option>
<option value="0.0.0.0" selected="true">Everyone (0.0.0.0)</option>
<option value="other">LAN Hosts (Please specify your LAN address)</option>
</select>
&nbsp;&nbsp;
<b>others:</b>
<input type="text" name="reachableByOther" size="20" />
<% } else { %>
<option value="127.0.0.1">Locally (127.0.0.1)</option>
<option value="0.0.0.0">Everyone (0.0.0.0)</option>
<option value="other" selected="true">LAN Hosts (Please specify your LAN address)</option>
</select>
&nbsp;&nbsp;
<b>others:</b>
<input type="text" name="reachableByOther" size="20" value="<%=clientInterface%>" />
<% } %>
</td>
</tr>
<tr>
<% if ("httpclient".equals(editBean.getInternalType(curTunnel))) { %>
<td><b>Outproxies:</b>
<% } else { %>
<td><b>Target:</b>
<% } %>
</td>
<td>
<% if ("httpclient".equals(editBean.getInternalType(curTunnel))) { %>
<input type="text" name="proxyList" value="<%=editBean.getClientDestination(curTunnel)%>" />
<% } else { %>
<input type="text" name="targetDestination" value="<%=editBean.getClientDestination(curTunnel)%>" />
<% } %>
<i>(name or destination)</i>
</td>
</tr>
<tr>
<td>
<b>Delayed connect?</b>
</td>
<td>
<% if (editBean.shouldDelay(curTunnel)) { %>
<input type="checkbox" value="1000" name="connectDelay" checked="true" />
<% } else { %>
<input type="checkbox" value="1000" name="connectDelay" />
<% } %>
<i>(for request/response connections)</i>
</td>
</tr>
<tr>
<td><b>Profile:</b>
</td>
<td>
<select name="profile">
<% if (editBean.isInteractive(curTunnel)) { %>
<option value="interactive" selected="true">interactive connection </option>
<option value="bulk">bulk connection (downloads/websites/BT) </option>
<% } else { %>
<option value="interactive">interactive connection </option>
<option value="bulk" selected="true">bulk connection (downloads/websites/BT) </option>
<% } %>
</select>
</td>
</tr>
<tr>
<td>
<b>Shared Client</b>
</td>
<td>
<% if (editBean.isSharedClient(curTunnel)) { %>
<input type="checkbox" value="true" name="shared" checked="true" />
<% } else { %>
<input type="checkbox" value="true" name="shared" />
<% } %>
<i>(Share tunnels with other clients and irc/httpclients? Change requires restart of client proxy)</i>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<b><hr size="1">
Advanced networking options<br />
<span style="color:#dd0000;">(NOTE: when this client proxy is configured to share tunnels, then these options are for all the shared proxy clients!)</span></b>
</td>
</tr>
<tr>
<td>
<b>Tunnel depth:</b>
</td>
<td><select name="tunnelDepth">
<% int tunnelDepth = editBean.getTunnelDepth(curTunnel, 2);
switch (tunnelDepth) {
case 0: %>
<option value="0" selected="true">0 hop tunnel (low anonymity, low latency)</option>
<option value="1" >1 hop tunnel (medium anonymity, medium latency)</option>
<option value="2" >2 hop tunnel (high anonymity, high latency)</option>
<% break;
case 1: %>
<option value="0" >0 hop tunnel (low anonymity, low latency)</option>
<option value="1" selected="true">1 hop tunnel (medium anonymity, medium latency)</option>
<option value="2" >2 hop tunnel (high anonymity, high latency)</option>
<% break;
case 2: %>
<option value="0" >0 hop tunnel (low anonymity, low latency)</option>
<option value="1" >1 hop tunnel (medium anonymity, medium latency)</option>
<option value="2" selected="true">2 hop tunnel (high anonymity, high latency)</option>
<% break;
default: %>
<option value="0" >0 hop tunnel (low anonymity, low latency)</option>
<option value="1" >1 hop tunnel (medium anonymity, medium latency)</option>
<option value="2" >2 hop tunnel (high anonymity, high latency)</option>
<option value="<%=tunnelDepth%>" ><%=tunnelDepth%> hop tunnel</option>
<% } %>
</select>
</td>
</tr>
<tr>
<td><b>Tunnel count:</b>
</td>
<td>
<select name="tunnelCount">
<% int tunnelCount = editBean.getTunnelCount(curTunnel, 2);
switch (tunnelCount) {
case 1: %>
<option value="1" selected="true" >1 inbound tunnel (low bandwidth usage, less reliability)</option>
<option value="2" >2 inbound tunnels (standard bandwidth usage, standard reliability)</option>
<option value="3" >3 inbound tunnels (higher bandwidth usage, higher reliability)</option>
<% break;
case 2: %>
<option value="1" >1 inbound tunnel (low bandwidth usage, less reliability)</option>
<option value="2" selected="true" >2 inbound tunnels (standard bandwidth usage, standard reliability)</option>
<option value="3" >3 inbound tunnels (higher bandwidth usage, higher reliability)</option>
<% break;
case 3: %>
<option value="1" >1 inbound tunnel (low bandwidth usage, less reliability)</option>
<option value="2" >2 inbound tunnels (standard bandwidth usage, standard reliability)</option>
<option value="3" selected="true" >3 inbound tunnels (higher bandwidth usage, higher reliability)</option>
<% break;
default: %>
<option value="1" >1 inbound tunnel (low bandwidth usage, less reliability)</option>
<option value="2" >2 inbound tunnels (standard bandwidth usage, standard reliability)</option>
<option value="3" >3 inbound tunnels (higher bandwidth usage, higher reliability)</option>
<option value="<%=tunnelDepth%>" ><%=tunnelDepth%> inbound tunnels</option>
<% } %>
</select>
</td>
<tr>
<td><b>I2CP host:</b>
</td>
<td>
<input type="text" name="clientHost" size="20" value="<%=editBean.getI2CPHost(curTunnel)%>" />
</td>
</tr>
<tr>
<td><b>I2CP port:</b>
</td>
<td>
<input type="text" name="clientPort" size="20" value="<%=editBean.getI2CPPort(curTunnel)%>" /><br />
</td>
</tr>
<tr>
<td><b>Custom options:</b>
</td>
<td>
<input type="text" name="customOptions" size="60" value="<%=editBean.getCustomOptions(curTunnel)%>" />
</td>
</tr>
<tr>
<td colspan="2">
<hr size="1">
</td>
</tr>
<tr>
<td>
<b>Save:</b>
</td>
<td>
<input type="submit" name="action" value="Save changes" />
</td>
</tr>
<tr>
<td><b>Delete?</b>
</td>
<td>
<input type="submit" name="action" value="Delete this proxy" /> &nbsp;&nbsp;
<b><span style="color:#dd0000;">confirm delete:</span></b>
<input type="checkbox" value="true" name="removeConfirm" />
</td>
</tr>
</table>
</td>
</tr>
</table>
</form>
</body>
</html>

View File

@ -0,0 +1,233 @@
<%@page contentType="text/html" %>
<jsp:useBean class="net.i2p.i2ptunnel.web.EditBean" id="editBean" scope="request" />
<% String tun = request.getParameter("tunnel");
int curTunnel = -1;
if (tun != null) {
try {
curTunnel = Integer.parseInt(tun);
} catch (NumberFormatException nfe) {
curTunnel = -1;
}
}
%>
<html>
<head>
<title>I2PTunnel Webmanager</title>
</head>
<body>
<form action="index.jsp">
<input type="hidden" name="tunnel" value="<%=request.getParameter("tunnel")%>" />
<input type="hidden" name="nonce" value="<%=editBean.getNextNonce()%>" />
<table width="80%" align="center" border="0" cellpadding="1" cellspacing="1">
<tr>
<td style="background-color:#000">
<div style="background-color:#ffffed">
<table width="100%" align="center" border="0" cellpadding="4" cellspacing="1">
<tr>
<td colspan="2" align="center">
<% if (curTunnel >= 0) { %>
<b>Edit server settings</b>
<% } else { %>
<b>New server settings</b>
<% } %>
</td>
</tr>
<tr>
<td><b>Name: </b>
</td>
<td>
<input type="text" size="30" maxlength="50" name="name" value="<%=editBean.getTunnelName(curTunnel)%>" />
</td>
</tr>
<tr>
<td><b>Type: </b>
<td><%
if (curTunnel >= 0) {
%><%=editBean.getTunnelType(curTunnel)%>
<input type="hidden" name="type" value="<%=editBean.getInternalType(curTunnel)%>" />
<%
} else {
%><%=editBean.getTypeName(request.getParameter("type"))%>
<input type="hidden" name="type" value="<%=request.getParameter("type")%>" />
<%
}
%></td>
</tr>
<tr>
<td><b>Description: </b>
</td>
<td>
<input type="text" size="60" maxlength="80" name="description" value="<%=editBean.getTunnelDescription(curTunnel)%>" />
</td>
</tr>
<tr>
<td><b>Start automatically?:</b>
</td>
<td>
<% if (editBean.startAutomatically(curTunnel)) { %>
<input value="1" type="checkbox" name="startOnLoad" checked="true" />
<% } else { %>
<input value="1" type="checkbox" name="startOnLoad" />
<% } %>
<i>(Check the Box for 'YES')</i>
</td>
</tr>
<tr>
<td> <b>Target:</b>
</td>
<td>
Host: <input type="text" size="20" name="targetHost" value="<%=editBean.getTargetHost(curTunnel)%>" />
Port: <input type="text" size="6" maxlength="5" name="targetPort" value="<%=editBean.getTargetPort(curTunnel)%>" />
</td>
</tr>
<% String curType = editBean.getInternalType(curTunnel);
if ( (curType == null) || (curType.trim().length() <= 0) )
curType = request.getParameter("type");
if ("httpserver".equals(curType)) { %>
<tr>
<td><b>Website name:</b></td>
<td><input type="text" size="20" name="spoofedHost" value="<%=editBean.getSpoofedHost(curTunnel)%>" />
</td></tr>
<% } %>
<tr>
<td><b>Private key file:</b>
</td>
<td><input type="text" size="30" name="privKeyFile" value="<%=editBean.getPrivateKeyFile(curTunnel)%>" /></td>
</tr>
<tr>
<td><b>Profile:</b>
</td>
<td>
<select name="profile">
<% if (editBean.isInteractive(curTunnel)) { %>
<option value="interactive" selected="true">interactive connection </option>
<option value="bulk">bulk connection (downloads/websites/BT) </option>
<% } else { %>
<option value="interactive">interactive connection </option>
<option value="bulk" selected="true">bulk connection (downloads/websites/BT) </option>
<% } %>
</select>
</td>
</tr>
<tr>
<td valign="top" align="left"><b>Local destination:</b><br /><i>(if known)</i></td>
<td valign="top" align="left"><input type="text" size="60" value="<%=editBean.getDestinationBase64(curTunnel)%>" /></td>
</tr>
<tr>
<td colspan="2" align="center">
<b><hr size="1">
Advanced networking options<br />
</b>
</td>
</tr>
<tr>
<td>
<b>Tunnel depth:</b>
</td>
<td><select name="tunnelDepth">
<% int tunnelDepth = editBean.getTunnelDepth(curTunnel, 2);
switch (tunnelDepth) {
case 0: %>
<option value="0" selected="true">0 hop tunnel (low anonymity, low latency)</option>
<option value="1" >1 hop tunnel (medium anonymity, medium latency)</option>
<option value="2" >2 hop tunnel (high anonymity, high latency)</option>
<% break;
case 1: %>
<option value="0" >0 hop tunnel (low anonymity, low latency)</option>
<option value="1" selected="true">1 hop tunnel (medium anonymity, medium latency)</option>
<option value="2" >2 hop tunnel (high anonymity, high latency)</option>
<% break;
case 2: %>
<option value="0" >0 hop tunnel (low anonymity, low latency)</option>
<option value="1" >1 hop tunnel (medium anonymity, medium latency)</option>
<option value="2" selected="true">2 hop tunnel (high anonymity, high latency)</option>
<% break;
default: %>
<option value="0" >0 hop tunnel (low anonymity, low latency)</option>
<option value="1" >1 hop tunnel (medium anonymity, medium latency)</option>
<option value="2" >2 hop tunnel (high anonymity, high latency)</option>
<option value="<%=tunnelDepth%>" ><%=tunnelDepth%> hop tunnel</option>
<% } %>
</select>
</td>
</tr>
<tr>
<td><b>Tunnel count:</b>
</td>
<td>
<select name="tunnelCount">
<% int tunnelCount = editBean.getTunnelCount(curTunnel, 2);
switch (tunnelCount) {
case 1: %>
<option value="1" selected="true" >1 inbound tunnel (low bandwidth usage, less reliability)</option>
<option value="2" >2 inbound tunnels (standard bandwidth usage, standard reliability)</option>
<option value="3" >3 inbound tunnels (higher bandwidth usage, higher reliability)</option>
<% break;
case 2: %>
<option value="1" >1 inbound tunnel (low bandwidth usage, less reliability)</option>
<option value="2" selected="true" >2 inbound tunnels (standard bandwidth usage, standard reliability)</option>
<option value="3" >3 inbound tunnels (higher bandwidth usage, higher reliability)</option>
<% break;
case 3: %>
<option value="1" >1 inbound tunnel (low bandwidth usage, less reliability)</option>
<option value="2" >2 inbound tunnels (standard bandwidth usage, standard reliability)</option>
<option value="3" selected="true" >3 inbound tunnels (higher bandwidth usage, higher reliability)</option>
<% break;
default: %>
<option value="1" >1 inbound tunnel (low bandwidth usage, less reliability)</option>
<option value="2" >2 inbound tunnels (standard bandwidth usage, standard reliability)</option>
<option value="3" >3 inbound tunnels (higher bandwidth usage, higher reliability)</option>
<option value="<%=tunnelDepth%>" ><%=tunnelDepth%> inbound tunnels</option>
<% } %>
</select>
</td>
<tr>
<td><b>I2CP host:</b>
</td>
<td>
<input type="text" name="clientHost" size="20" value="<%=editBean.getI2CPHost(curTunnel)%>" />
</td>
</tr>
<tr>
<td><b>I2CP port:</b>
</td>
<td>
<input type="text" name="clientPort" size="20" value="<%=editBean.getI2CPPort(curTunnel)%>" /><br />
</td>
</tr>
<tr>
<td><b>Custom options:</b>
</td>
<td>
<input type="text" name="customOptions" size="60" value="<%=editBean.getCustomOptions(curTunnel)%>" />
</td>
</tr>
<tr>
<td colspan="2">
<hr size="1">
</td>
</tr>
<tr>
<td>
<b>Save:</b>
</td>
<td>
<input type="submit" name="action" value="Save changes" />
</td>
</tr>
<tr>
<td><b>Delete?</b>
</td>
<td>
<input type="submit" name="action" value="Delete this proxy" /> &nbsp;&nbsp;
<b><span style="color:#dd0000;">confirm delete:</span></b>
<input type="checkbox" value="true" name="removeConfirm" />
</td>
</tr>
</table>
</td>
</tr>
</table>
</form>
</body>
</html>

View File

@ -1,33 +1,185 @@
<%@page contentType="text/html" %>
<%@page contentType="text/html" import="net.i2p.i2ptunnel.web.IndexBean" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<jsp:useBean class="net.i2p.i2ptunnel.web.IndexBean" id="indexBean" scope="request" />
<jsp:setProperty name="indexBean" property="*" />
<html><head>
<title>I2PTunnel status</title>
</head><body>
<html>
<head>
<title>I2PTunnel Webmanager</title>
</head>
<body style="font-family: Verdana, Tahoma, Helvetica, sans-serif;font-size:12px;">
<table width="90%" align="center" border="0" cellpadding="1" cellspacing="1">
<tr>
<td style="background-color:#000">
<div style="background-color:#ffffed">
<table width="100%" align="center" border="0" cellpadding="4" cellspacing="1">
<tr>
<td nowrap="true"><b>New Messages: </b><br />
<a href="index.jsp">refresh</a>
</td>
<td>
<textarea rows="3" cols="60" readonly="true"><jsp:getProperty name="indexBean" property="messages" /></textarea>
</td>
</tr>
</table>
</td>
</tr>
</table>
<br />
<table width="90%" align="center" border="0" cellpadding="1" cellspacing="1">
<tr>
<td style="background-color:#000">
<div style="background-color:#ffffed">
<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" />
<hr />
<table width="100%" align="center" border="0" cellpadding="4" cellspacing="1">
<tr>
<td colspan="7" align="center" valign="middle" style="font-size:14px;">
<b>Your Client Tunnels:</b><br />
<hr size="1" />
</td>
</tr>
<tr>
<td width="15%"><b>Name:</b></td>
<td><b>Port:</b></td>
<td><b>Type:</b></td>
<td><b>Interface:</b></td>
<td><b>Status:</b></td>
</tr>
<% for (int curClient = 0; curClient < indexBean.getTunnelCount(); curClient++) {
if (!indexBean.isClient(curClient)) continue; %>
<tr>
<td valign="top" align="left">
<b><a href="edit.jsp?tunnel=<%=curClient%>"><%=indexBean.getTunnelName(curClient) %></a></b></td>
<td valign="top" align="left" nowrap="true"><%=indexBean.getClientPort(curClient) %></td>
<td valign="top" align="left" nowrap="true"><%=indexBean.getTunnelType(curClient) %></td>
<td valign="top" align="left" nowrap="true"><%=indexBean.getClientInterface(curClient) %></td>
<td valign="top" align="left" nowrap="true"><%
switch (indexBean.getTunnelStatus(curClient)) {
case IndexBean.STARTING:
%><b><span style="color:#339933">Starting...</span></b>
<a href="index.jsp?nonce=<%=indexBean.getNextNonce()%>&action=stop&tunnel=<%=curClient%>">[STOP]</a><%
break;
case IndexBean.RUNNING:
%><b><span style="color:#00dd00">Running</span></b>
<a href="index.jsp?nonce=<%=indexBean.getNextNonce()%>&action=stop&tunnel=<%=curClient%>">[STOP]</a><%
break;
case IndexBean.NOT_RUNNING:
%><b><span style="color:#dd0000">Not Running</span></b>
<a href="index.jsp?nonce=<%=indexBean.getNextNonce()%>&action=start&tunnel=<%=curClient%>">[START]</a><%
break;
}
%></td>
</tr>
<tr><td align="right" valign="top">Destination:</td>
<td colspan="4"><input align="left" size="40" valign="top" style="overflow: hidden" readonly="true" value="<%=indexBean.getClientDestination(curClient) %>" /></td></tr>
<tr>
<td valign="top" align="right">Description:</td>
<td valign="top" align="left" colspan="4"><%=indexBean.getTunnelDescription(curClient) %></td>
</tr>
<% } %>
</table>
</td>
</tr>
</table>
<br />
<table width="90%" align="center" border="0" cellpadding="1" cellspacing="1">
<tr>
<td style="background-color:#000">
<div style="background-color:#ffffed">
<table width="100%" align="center" border="0" cellpadding="4" cellspacing="1">
<tr>
<td colspan="5" align="center" valign="middle" style="font-size:14px;">
<b>Your Server Tunnels:</b><br />
<hr size="1" />
</td>
</tr>
<tr>
<td width="15%"><b>Name: </b>
</td>
<td>
<b>Points at:</b>
</td>
<td>
<b>Status:</b>
</td>
</tr>
<% for (int curServer = 0; curServer < indexBean.getTunnelCount(); curServer++) {
if (indexBean.isClient(curServer)) continue; %>
<tr>
<td valign="top">
<b><a href="edit.jsp?tunnel=<%=curServer%>"><%=indexBean.getTunnelName(curServer)%></a></b>
</td>
<td valign="top"><%=indexBean.getServerTarget(curServer)%></td>
<td valign="top" nowrap="true"><%
switch (indexBean.getTunnelStatus(curServer)) {
case IndexBean.RUNNING:
%><b><span style="color:#00dd00">Running</span></b>
<a href="index.jsp?nonce=<%=indexBean.getNextNonce()%>&action=stop&tunnel=<%=curServer%>">[STOP]</a><%
if ("httpserver".equals(indexBean.getInternalType(curServer))) {
%> (<a href="http://<%=(new java.util.Random()).nextLong()%>.i2p/?i2paddresshelper=<%=indexBean.getDestinationBase64(curServer)%>">preview</a>)<%
}
break;
case IndexBean.NOT_RUNNING:
%><b><span style="color:#dd0000">Not Running</span></b>
<a href="index.jsp?nonce=<%=indexBean.getNextNonce()%>&action=start&tunnel=<%=curServer%>">[START]</a><%
break;
case IndexBean.STARTING:
%>
<b><span style="color:#339933">Starting...</span></b>
<a href="index.jsp?nonce=<%=indexBean.getNextNonce()%>&action=stop&tunnel=<%=curServer%>">[STOP]</a><%
break;
}
%>
</td>
</tr>
<tr><td valign="top" align="right">Description:</td>
<td valign="top" align="left" colspan="2"><%=indexBean.getTunnelDescription(curServer)%></td></tr>
<% } %>
</table>
</td>
</tr>
</table>
<br />
<table width="90%" align="center" border="0" cellpadding="1" cellspacing="1">
<tr>
<td style="background-color:#000">
<div style="background-color:#ffffed">
<table width="100%" align="center" border="0" cellpadding="4" cellspacing="1">
<tr>
<td colspan="2" align="center" valign="middle">
<b>Operations Menu - Please chose from below!</b><br /><br />
</td>
</tr>
<tr>
<form action="index.jsp" method="GET">
<input type="submit" name="action" value="Stop all" />
<input type="submit" name="action" value="Start all" />
<input type="submit" name="action" value="Restart all" />
<input type="submit" name="action" value="Reload config" />
<td >
<input type="hidden" name="nonce" value="<%=indexBean.getNextNonce()%>" />
<input type="submit" name="action" value="Stop all tunnels" />
<input type="submit" name="action" value="Start all tunnels" />
<input type="submit" name="action" value="Restart all" />
<input type="submit" name="action" value="Reload config" />
</td>
</form>
<form action="edit.jsp">
<td >
<b>Add new:</b>
<select name="type">
<select name="type">
<option value="httpclient">HTTP proxy</option>
<option value="ircclient">IRC 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" />
<option value="httpserver">HTTP server tunnel</option>
</select> <input type="submit" value="Create" />
</td>
</form>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
@ -14,4 +14,4 @@
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
</web-app>

BIN
apps/jdom/jdom.jar Normal file

Binary file not shown.

1
apps/jdom/readme.txt Normal file
View File

@ -0,0 +1 @@
This is JDOM 1.0 from http://jdom.org/, released under an Apache style license

View File

@ -3,18 +3,38 @@
<target name="all" depends="build" />
<target name="fetchJettylib" >
<available property="jetty.available" file="jettylib" />
<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 4.2.21 " />
<echo message="distribution (http://jetty.mortbay.org/) which we have copied to our website since" />
<echo message="theirs doesn't have direct HTTP access to the libs. These are not " />
<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://dev.i2p.net/jettylib.tar.bz2" verbose="true" dest="jettylib.tar.bz2" />
<untar src="jettylib.tar.bz2" compression="bzip2" dest="." />
<delete file="jettylib.tar.bz2" />
<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" />

View File

@ -62,6 +62,8 @@ public interface I2PSocket {
*/
public void close() throws IOException;
public boolean isClosed();
public void setSocketErrorListener(SocketErrorListener lsnr);
/**
* Allow notification of underlying errors communicating across I2P without

View File

@ -32,7 +32,7 @@ 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;
@ -233,6 +233,8 @@ class I2PSocketImpl implements I2PSocket {
in.notifyClosed();
}
public boolean isClosed() { return _closedOn > 0; }
/**
* Close the socket from the I2P side (by a close packet)
*/
@ -284,7 +286,7 @@ class I2PSocketImpl implements I2PSocket {
in.setReadTimeout(ms);
}
public void setSocketErrorListener(SocketErrorListener lsnr) {
public void setSocketErrorListener(I2PSocket.SocketErrorListener lsnr) {
_socketErrorListener = lsnr;
}

View File

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

View File

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

View File

@ -119,7 +119,7 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener {
_listeners.clear();
}
for (int i = 0; i < listeners.size(); i++) {
DisconnectListener lsnr = (DisconnectListener)listeners.get(i);
I2PSocketManager.DisconnectListener lsnr = (I2PSocketManager.DisconnectListener)listeners.get(i);
lsnr.sessionDisconnected();
}
}
@ -720,12 +720,12 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener {
public String getName() { return _name; }
public void setName(String name) { _name = name; }
public void addDisconnectListener(DisconnectListener lsnr) {
public void addDisconnectListener(I2PSocketManager.DisconnectListener lsnr) {
synchronized (_listeners) {
_listeners.add(lsnr);
}
}
public void removeDisconnectListener(DisconnectListener lsnr) {
public void removeDisconnectListener(I2PSocketManager.DisconnectListener lsnr) {
synchronized (_listeners) {
_listeners.remove(lsnr);
}

View File

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

View File

@ -1,6 +1,5 @@
package net.i2p.client.streaming;
import java.util.Iterator;
import java.util.Properties;
/**
@ -35,6 +34,18 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
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);

View File

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

View File

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

View File

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

View File

@ -1,24 +1,12 @@
package net.i2p.client.streaming;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Map;
import java.util.Collections;
import java.util.HashMap;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PClientFactory;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.util.Log;
import net.i2p.util.I2PThread;
@ -161,7 +149,8 @@ public class TestSwarm {
public void run() {
_started = _context.clock().now();
_context.statManager().addRateData("swarm." + _connectionId + ".started", 1, 0);
byte data[] = new byte[32*1024];
byte data[] = new byte[4*1024];
_context.random().nextBytes(data);
long value = 0;
long lastSend = _context.clock().now();
if (_socket == null) {
@ -179,15 +168,19 @@ public class TestSwarm {
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) {}
if (shouldSend()) {
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) {}
} else {
try { Thread.sleep(5000); } catch (InterruptedException ie) {}
}
}
} catch (Exception e) {
_log.error("Error sending", e);
@ -200,13 +193,13 @@ public class TestSwarm {
long now = lastRead;
try {
InputStream in = _socket.getInputStream();
byte buf[] = new byte[32*1024];
byte buf[] = new byte[8*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));
//_log.debug("Receiving " + _connectionId + " with " + read + " after " + (now-lastRead));
lastRead = now;
}
} catch (Exception e) {
@ -215,4 +208,8 @@ public class TestSwarm {
}
}
}
private boolean shouldSend() {
return Boolean.valueOf(_context.getProperty("shouldSend", "false")).booleanValue();
}
}

236
apps/pants/build.xml Normal file
View File

@ -0,0 +1,236 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Ports + Ant = Pants, a simple Ant-based package manager
free (adj.): unencumbered; not under the control of others
Written by smeghead 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.
-->
<project basedir="." default="help" name="pants-interface">
<!-- .......................... Global Properties .......................... -->
<!-- ........................... Internal Tasks ............................ -->
<target name="-fetchCvs" unless="cvs.source.available" if="using.cvs">
<cvs compressionlevel="${cvs.compression.level}"
date="${cvs.date}"
dest="./distfiles/cvs-src/${pbuild}"
failonerror="true"
package="${cvs.package}"
passfile="${cvs.passfile}"
port="${cvs.port}"
cvsRoot="${cvs.root}"
cvsRsh="${cvs.rsh}"
tag="${cvs.tag}"
/>
</target>
<target name="-fetchPackage" unless="using.cvs">
<get src="${package.url}"
verbose="true"
dest="./distfiles"
/>
</target>
<target name="-init">
<!--
TODO: Create dist/ and working/ folders for each pbuild subdir in case
they've been wiped.
-->
<loadproperties srcfile="world" />
<taskdef name="mergetypedproperties"
classname="net.i2p.pants.MergeTypedPropertiesTask"
classpath="./lib/pants.jar"
/>
<mergetypedproperties input="./pbuilds/${pbuild}/pbuild.properties"
output="./pbuilds/${pbuild}/merged-properties.temp"
booleanList="version.latest.find.regex.canonicaleq, version.latest.find.regex.caseinsensitive, version.latest.find.regex.comments, version.latest.find.regex.dotall, version.latest.find.regex.multiline, version.latest.find.regex.unicodecase, version.latest.find.regex.unixlines"
stringList="cvs.compression.level, cvs.date, cvs.package, cvs.passfile, cvs.port, cvs.root, cvs.rsh, cvs.tag, package.url, version.latest, version.latest.find.url, version.latest.find.regex"
/>
<loadproperties srcfile="./pbuilds/${pbuild}/merged-properties.temp" />
<delete file="./pbuilds/${pbuild}/merged-properties.temp" />
<!--
If '-Dpbuild={name}' isn't specified, the 'build', 'fetch', 'update'
and 'version' commands should default to 'world' behavior.
-->
<antcall target="-setWorld" />
<condition property="using.cvs">
<or>
<equals arg1="CVS" arg2="${version.using.${pbuild}}" />
<equals arg1="cvs" arg2="${version.using.${pbuild}}" />
</or>
</condition>
<!--
If 'version.recommended' isn't defined in pbuild.properties, default
to latest available version.
-->
</target>
<target name="-setWorld" unless="pbuild">
<property name="pbuild" value="world" />
</target>
<target name="-unpackTarBz2">
<untar src="${pbuild.package}"
compression="bzip2"
dest="./${pbuild}/working"
/>
</target>
<target name="-unpackTarGzip">
<untar src="${pbuild.package}"
compression="gzip"
dest="./${pbuild}/working"
/>
</target>
<target name="-unpackZip">
<unzip src="${pbuild.package}" dest="./${pbuild}/working" />
</target>
<target name="-updateCvs" if="using.cvs">
<cvs command="update -d"
compressionlevel="${compression.level}"
date="${cvs.date}"
dest="./distfiles/cvs-src"
failonerror="true"
package="${cvs.package}"
passfile="${cvs.passfile}"
port="${cvs.port}"
cvsRoot="${cvs.root}"
cvsRsh="${cvs.rsh}"
tag="${cvs.tag}"
/>
</target>
<target name="-updateConfirm" if="confirm.update" unless="no.prompts">
<input validargs="y,Y,n,N"
defaultvalue="n"
addproperty="confirm.update.answer">
You currently have the recommended version installed. A newer
version will be installed if you continue and this may break some
applications which depend on this package. Are you sure you want
to update? [y/N]
</input>
<condition property="abort.update">
<or>
<equals arg1="n" arg2="${confirm.update.answer}" />
<equals arg1="N" arg2="${confirm.update.answer}" />
</or>
</condition>
<fail if="abort.update">Update aborted.</fail>
</target>
<target name="-versionLatest">
<get src="${version.latest.find.url}"
dest="version.latest.in.temp"
verbose="true"
/>
<taskdef name="match"
classname="net.i2p.pants.MatchTask"
classpath="./lib/pants.jar"
/>
<match input="version.latest.in.temp"
output="version.latest.parsed.temp"
regex="${version.latest.find.regex}"
canonicaleq="${version.latest.find.regex.canonicaleq}"
caseinsensitive="${version.latest.find.regex.caseinsensitive}"
comments="${version.latest.find.regex.comments}"
dotall="${version.latest.find.regex.dotall}"
multiline="${version.latest.find.regex.multiline}"
unicodecase="${version.latest.find.regex.unicodecase}"
unixlines="${version.latest.find.regex.unixlines}"
/>
<loadproperties srcFile="version.latest.parsed.temp" />
<delete file="version.latest.in.temp" />
<delete file="version.latest.parsed.temp" />
<property name="version.latest" value="${group.1}" />
</target>
<target name="-versionRecommended">
<property name="version.recommended" value="x" />
</target>
<target name="-versionUsing">
<property name="version.using" value="x" />
</target>
<!-- .......................... Public Interface ........................... -->
<target name="build" depends="-init,fetch"
description="Build a pbuild and its dependencies">
<ant antfile="pbuild.xml" dir="./pbuilds/${pbuild}" target="clean" />
<ant antfile="pbuild.xml" dir="./pbuilds/${pbuild}" target="build" />
<!--
Perform a 'clean' on the target first (but not 'distclean')
-->
</target>
<target name="fetch" depends="-init"
description="Get package only">
<antcall target="-fetchPackage" />
<antcall target="-fetchCvs" />
<copydir dest="./pbuilds/${pbuild}/working"
src="./distfiles/cvs-src/${pbuild}"
/>
</target>
<target name="help"
description="Display usage synopsis">
<echo>
Pants usage:
ant [--usejikes] [-Dpbuild={name}] [-Dpbuild.version={version}]
[-D{property}={value}] [-Dno.prompts=true] build | fetch |
help | install | uninstall | update | version
build Build a pbuild and its dependencies
fetch Get package only
help Display usage synopsis
install Fetch, build and install a pbuild
uninstall Uninstall a pbuild
update Update pbuild(s) to the latest version(s)
version Display pbuild version information
</echo>
</target>
<!--
Install recommended version by default unless 'version' property is set.
Do not install if package is already installed.
-->
<target name="install" depends="-init, build"
description="Install a pbuild">
<ant antfile="pbuild.xml" dir="./pbuilds/${pbuild}" target="dist" />
<ant antfile="pbuild.xml" dir="./pbuilds/${pbuild}"
target="distclean"
/>
</target>
<target name="uninstall" depends="-init"
description="Uninstall a pbuild" />
<target name="update" depends="-init"
description="Update pbuild(s) to the latest version(s)">
<condition property="${confirm.update}">
<equals arg1="${version.using}" arg2="${version.recommended}" />
</condition>
<antcall target="-updateConfirm" />
</target>
<target name="version"
depends="-init, -versionRecommended, -versionUsing, -versionLatest"
description="Display pbuild version information">
<echo message="Latest version: ${version.recommended}" />
<echo message="Latest version: ${version.using}" />
<echo message="Latest version: ${version.latest}" />
</target>
</project>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="build" name="build-pants">
<target name="build"
description="Build the source">
<mkdir dir="./java/build"/>
<javac srcdir="./java/src" source="1.3" target="1.3" deprecation="on" destdir="./java/build" />
</target>
<target name="clean"
description="Remove all object files">
<delete dir="./java/build" />
<delete dir="./java/jartemp" />
</target>
<target name="dist" depends="build, jar"
description="Create the jar and copy it to ../lib">
<copy todir="../lib" file="./java/build/pants.jar" />
</target>
<target name="distclean" depends="clean"
description="Remove the jar and all object files" >
<delete file="../lib/pants.jar" />
</target>
<target name="jar">
<delete dir="./java/jartemp" />
<mkdir dir="./java/jartemp" />
<copy todir="./java/jartemp">
<fileset dir="./java/build" includes="**/*.class" />
</copy>
<jar basedir="./java/jartemp" jarfile="./java/build/pants.jar">
<manifest>
<section name="net.i2p.pants">
<attribute name="Implementation-Title" value="Pants" />
<attribute name="Implementation-Version" value="0.0.1" />
<attribute name="Implementation-Vendor" value="I2P" />
<attribute name="Implementation-Vendor-Id" value="I2P" />
<attribute name="Implementation-URL" value="http://www.i2p.net" />
</section>
</manifest>
</jar>
<delete dir="./java/jartemp" />
</target>
</project>

View File

@ -0,0 +1,212 @@
/*
* Ports + Ant = Pants, a simple Ant-based package manager
*
* free (adj.): unencumbered; not under the control of others
*
* Written by smeghead 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.
*/
package net.i2p.pants;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
/**
* <p>Custom Ant task for matching the contents of a file against a given
* regular expression and writing any matching groups to a file in
* <code>java.util.Properties</code> format.
* </p>
* <p>Each key in the properties file is named after the number corresponding to
* its matching group and its value is the contents of the matching group.
* </p>
* <p>Regular expressions passed to this task must conform to the specification
* used by Sun's <code>java.util.regex</code> package and thus are mostly
* compatible with Perl 5 regular expressions.
* </p>
* <p>When calling the <code>match</code> task, the attributes
* <code>input</code>, <code>output</code>, and <code>regex</code> are required.
* </p>
* <p>Optional boolean attributes may be used to toggle various modes for the
* regular expression engine (all are set to <code>false</code> by default):
* </p>
* <table>
* <tr><td><code>canonicaleq</code></td><td>Enable canonical equivalence</td></tr>
* <tr><td><code>caseinsensitive</code></td><td>Enable case-insensitive matching</td></tr>
* <tr><td><code>comments</code></td><td>Permit whitespace and comments in pattern</td></tr>
* <tr><td><code>dotall</code></td><td>Enable dotall mode</td></tr>
* <tr><td><code>multiline</code></td><td>Enable multi-line mode</td></tr>
* <tr><td><code>unicodecase</code></td><td>Enable Unicode-aware case folding</td></tr>
* <tr><td><code>unixlines</code></td><td>Enable Unix lines mode</td></tr>
* </table>
* <p>There is one additional optional boolean attribute,
* <code>failOnNoMatch</code>. If this attribute is <code>true</code> it causes
* the <code>match</code> task to throw a
* <code>org.apache.tools.ant.BuildException</code> and fail if no matches for
* the regular expression are found. The default value is <code>false</code>,
* meaning a failed match will simply result in a warning message to
* <code>STDERR</code> and an empty (0 byte) <code>output</code> file being
* created.
* </p>
* <p>
* <h4>Example</h4>
* </p>
* <p>Contents of input file <code>letter.txt</code>:
* <pre>
* Dear Alice,
*
* How's about you and me gettin' together for some anonymous foo action?
*
* Kisses,
* Bob
* </pre>
* </p>
* <p>Ant <code>match</code> task and a <code>taskdef</code> defining it:
* <pre>
* &lt;taskdef name="match" classname="net.i2p.pants.MatchTask" classpath="../../lib/pants.jar" /&gt;
* &lt;match input="letter.txt"
* output="matches.txt"
* regex="about (\S*?) and (\S*?) .+anonymous (\S*?)"
* /&gt;
* </pre>
* </p>
* <p>Contents of properties file <code>matches.txt</code> written by this task:
* <pre>
* group.0=about you and me gettin' together for some anonymous foo
* group.1=you
* group.2=me
* group.3=foo
* </pre>
* </p>
* <p>These values can be loaded from <code>matches.txt</code> into Ant
* properties like so:
* <pre>
* &lt;loadproperties srcFile="matches.txt" /&gt;
* </pre>
* </p>
*
* @author smeghead
*/
public class MatchTask extends Task {
private boolean _failOnNoMatch;
private String _inputFile;
private String _outputFile;
private String _regex;
private int _regexFlags;
public void execute() throws BuildException {
int charRead = 0;
FileReader fileReader = null;
FileWriter fileWriter = null;
Matcher matcher = null;
Pattern pattern = null;
PrintWriter printWriter = null;
StringBuffer text = new StringBuffer();
if (_inputFile == null)
throw new BuildException("Error: 'match' task requires 'input' attribute");
if (_outputFile == null)
throw new BuildException("Error: 'match' task requires 'output' attribute");
if (_regex == null)
throw new BuildException("Error: 'match' task requires 'regex' attribute");
pattern = Pattern.compile(_regex, _regexFlags);
try {
fileReader = new FileReader(_inputFile);
while ((charRead = fileReader.read()) != -1)
text.append((char) charRead);
fileReader.close();
matcher = pattern.matcher(text);
if (matcher.find()) {
printWriter = new PrintWriter(new FileWriter(_outputFile));
for (int i = 0; i <= matcher.groupCount(); i++)
printWriter.println("group." + Integer.toString(i) + "=" + matcher.group(i));
printWriter.flush();
printWriter.close();
} else {
if (_failOnNoMatch) {
throw new BuildException("Error: No matches found in " + _inputFile);
} else {
System.err.println("Warning: No matches found in " + _inputFile);
// Create 0 byte output file.
fileWriter = new FileWriter(_outputFile);
fileWriter.close();
}
}
} catch (FileNotFoundException fnfe) {
throw new BuildException("File " + _inputFile + " not found", fnfe);
} catch (IOException ioe) {
throw new BuildException(ioe);
}
}
public void setCanonicalEq(boolean enableCanonicalEq) {
if (enableCanonicalEq)
_regexFlags |= Pattern.CANON_EQ;
}
public void setCaseInsensitive(boolean enableCaseInsensitive) {
if (enableCaseInsensitive)
_regexFlags |= Pattern.CASE_INSENSITIVE;
}
public void setComments(boolean enableComments) {
if (enableComments)
_regexFlags |= Pattern.COMMENTS;
}
public void setDotall(boolean enableDotall) {
if (enableDotall)
_regexFlags |= Pattern.DOTALL;
}
public void setFailOnNoMatch(boolean failOnNoMatch) {
_failOnNoMatch = failOnNoMatch;
}
public void setInput(String inputFile) {
_inputFile = inputFile;
}
public void setMultiLine(boolean enableMultiLine) {
if (enableMultiLine)
_regexFlags |= Pattern.MULTILINE;
}
public void setOutput(String outputFile) {
_outputFile = outputFile;
}
public void setRegex(String regex) {
_regex = regex;
}
public void setUnicodeCase(boolean enableUnicodeCase) {
if (enableUnicodeCase)
_regexFlags |= Pattern.UNICODE_CASE;
}
public void setUnixLines(boolean enableUnixLines) {
if (enableUnixLines)
_regexFlags |= Pattern.UNIX_LINES;
}
}

View File

@ -0,0 +1,164 @@
/*
* Ports + Ant = Pants, a simple Ant-based package manager
*
* free (adj.): unencumbered; not under the control of others
*
* Written by smeghead 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.
*/
package net.i2p.pants;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Properties;
import java.util.StringTokenizer;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
/**
* <p>Custom Ant task for loading properties from a
* <code>java.util.Properties</code> file then merging them with lists of
* expected properties. When an expected property is found in the properties
* file it is set to the value given for it in the file. If an expected property
* from a list isn't found in the properties file its value will be set to "" or
* "false", depending on the property's data type.
* </p>
* <p>A property's data type is determined by membership in one of two lists
* which can be passed into an instance of this class: a string-typed list and a
* boolean-typed list. Values for string-typed properties may be any valid
* string accepted by <code>java.util.Properties</code>, and values for
* boolean-typed properties must be either "false" or "true".
* </p>
* <p>Lists holding more than one property must be comma-delimited.
* </p>
* <p>The output of this class is a temporary <code>java.util.Properties</code>
* file which is suitable for reading by the standard Ant
* <code>loadproperties</code> task.
* </p>
* <p>Note that if any properties in the given lists have already been defined
* before the <code>mergetypedproperties</code> task is called, their values
* cannot be changed since Ant properties are immutable.
* </p>
* <h4>Example</h4>
* </p>
* <p>Contents of a properties file <code>my.properties</code>:
* <pre>
* some.property.exists=true
* hasValue=false
* some.property=this is a value
* property0=bork bork
* propertyX=this property wasn't passed in a list
* </pre>
* </p>
* <p>Ant <code>mergetypedproperties</code> task and a <code>taskdef</code>
* defining it:
* <pre>
* &lt;taskdef name="mergetypedproperties" classname="net.i2p.pants.MergeTypedPropertiesTask" classpath="../../lib/pants.jar" /&gt;
* &lt;mergetypedproperties input="my.properties"
* output="merged-properties.temp"
* booleanList="some.property.exists,is.valid,hasValue"
* stringList="some.property,another.property,property0"
* /&gt;
* </pre>
* </p>
* <p>Contents of properties file <code>merged-properties.temp</code> written by this task:
* <pre>
* some.property.exists=true
* is.valid=false
* hasValue=false
* some.property=this is a value
* another.property=
* property0=bork bork
* propertyX=this property wasn't passed in a list
* </pre>
* </p>
* <p>If you don't want this task's output to include properties which weren't
* in the lists of expected properties, you can set the attribute
* <code>onlyExpected</code> to <code>true</code>. In the example, this would
* result in the file <code>merged-properties.temp</code> containing only the
* following properties:
* <pre>
* some.property.exists=true
* is.valid=false
* hasValue=false
* some.property=this is a value
* another.property=
* property0=bork bork
* </pre>
* </p>
*
* @author smeghead
*/
public class MergeTypedPropertiesTask extends Task {
private String _booleanList = "";
private String _inputFile;
private boolean _onlyExpected;
private String _outputFile;
private Properties _propertiesIn = new Properties();
private Properties _propertiesOut = new Properties();
private String _stringList = "";
public void execute() throws BuildException {
StringTokenizer strtokBoolean = new StringTokenizer(_booleanList, ",");
StringTokenizer strtokString = new StringTokenizer(_stringList, ",");
String property = "";
if (_inputFile == null)
throw new BuildException("Error: 'mergetypedproperties' task requires 'input' attribute");
if (_outputFile == null)
throw new BuildException("Error: 'mergetypedproperties' task requires 'output' attribute");
// Add some type-checking on the list elements
try {
_propertiesIn.load(new FileInputStream(_inputFile));
while (strtokBoolean.hasMoreTokens())
_propertiesOut.setProperty(strtokBoolean.nextToken().trim(), "false");
while (strtokString.hasMoreTokens())
_propertiesOut.setProperty(strtokString.nextToken().trim(), "");
for (Enumeration enum = _propertiesIn.elements(); enum.hasMoreElements(); ) {
property = (String) enum.nextElement();
if (_onlyExpected && !_propertiesOut.containsKey(property))
continue;
else
_propertiesOut.setProperty(property, _propertiesIn.getProperty(property));
}
_propertiesOut.store(new FileOutputStream(_inputFile), "This is a temporary file. It is safe to delete it.");
} catch (IOException ioe) {
throw new BuildException(ioe);
}
}
public void setBooleanList(String booleanList) {
_booleanList = booleanList;
}
public void setInput(String inputFile) {
_inputFile = inputFile;
}
public void setOnlyExpected(boolean onlyExpected) {
_onlyExpected = onlyExpected;
}
public void setOutput(String outputFile) {
_outputFile = outputFile;
}
public void setStringList(String stringList) {
_stringList = stringList;
}
}

View File

@ -0,0 +1,116 @@
What is Pants?
--------------
Pants is an Apache Ant-based package manager for the management of 3rd party
dependencies in Java development projects. It's loosely modeled after
FreeBSD's Ports and Gentoo Linux's Portage, with two major differences:
* Pants isn't intended for system-wide package management. It's tailored for
per-project 3rd party package management. You will typically have one
Pants repository per project and each repository will be located somewhere
under your project's root directory. If you're familiar with Ports or
Portage, a Pants repository is roughly analogous to /usr/ports or
/usr/portage.
* Pants is extremely portable. It goes anywhere Apache Ant goes.
Pants takes a modular approach to the standard Ant buildfile, breaking it
into 3 files for functionality and convenience:
1. The Pants public interface, pants/build.xml, provides a single consistent
way to access and manipulate dependency packages and relieves some of the
developer's burden by providing implementations for some frequently-used
and complex Ant operations.
2. pbuild.xml is a specially-structured and slimmed-down Ant buildfile in
which you implement custom handling for a package your project depends
on. This is known as the "pbuild" and is roughly analogous to a FreeBSD
port or a Gentoo ebuild. A fairly explanatory template for pbuilds,
pbuild.template.xml, is provided.
3. pbuild.properties contains those properties for a specific pbuild which
are most likely to change over time. It uses the java.util.Properties
format which is more human-friendly for hand-editing than Ant/XML. A
fairly explanatory template, pbuild.template.properties, is provided.
There is one more file that completes the Pants system: the metadata file
pants/world is a database for keeping track of all packages managed by Pants
for your project.
Pants automatically handles versioning for your project's dependency
packages and keeps track of their recommended versions, currently used
versions, and latest available versions. This makes it extremely simple for
project developers to switch back and forth between different versions of a
dependency, and makes it just as easy to update a dependency. You can even
update all your project's Pants-managed packages with a single command.
Pbuilds are designed to automatically handle the downloading, building,
repackaging and deployment of source archives, binary archives, and CVS
sources, all in a manner that's completely transparent to the project
developer. Pbuilds currently support tar + gzip, tar + bzip2, and zip
archives.
Because it is based on Ant, Pants integrates very well with Ant buildfiles
and will fit easily into your project's Ant build framework. However, its
interface is simple enough to be called just as easily by more traditional
build systems such as GNU Make.
Why Should I Use Pants?
-----------------------
There are many applications for Pants, but a few use cases should best serve
to illustrate its usefulness:
1. You have a project that you ship with several 3rd party libraries but the
versions you're using are stale. With a single command, Pants can
automatically discover the latest release versions for all of these, then
download, build, and place the fresh libraries where your project's main
build system expects them to be at build time.
2. You want to test multiple versions of a 3rd party library against your
project. Pants only requires you to issue a single command to switch
library versions, so can spend more time testing and less time hunting
packages down, unpackaging them, symlinking, etc.
3. Pants is public domain. You can ship it with your project if you need to
without having to worry about petty intellectual property or licensing
issues.
Minimum Requirements
--------------------
* Apache Ant 1.6.2 or higher is recommended
* Any Java runtime and operating system that will run Ant
Installation
------------
Not finished yet.
Why the Silly Name?
-------------------
Ports + Ant = Pants. Any other explanation is purely a product of your
twisted imagination.
Miscellaneous Pocket Fluff
--------------------------
Author: smeghead <smeghead@i2pmail.org> <smeghead@mail.i2p>
License: No license necessary. This work is released into the public domain.
Price: Free! But if you really appreciate Pants, or you're just a sicko,
please send me a picture of your worst or most unusual pair of
pants so I can add it to the Whirling Hall of Pants on pants.i2p,
the official Pants eepsite (that's an anonymous website on I2P--see
http://www.i2p.net for more information).
$Id$

View File

@ -0,0 +1,110 @@
# The properties defined in this file can be overridden on the command line by
# passing them in as parameters like so:
#
# ant -Dpbuild=myapp -Dversion.recommended=2.0.5 install
#
# *** DO NOT DEFINE A PROPERTY BUT LEAVE ITS VALUE BLANK. PANTS WILL BREAK! ***
# Recommended Package Version
#
# Set this property's value to the package version you want Pants to use for the
# pbuild by default. The version string specified must match the version
# substring from the package's filename if the filename contains a version
# number.
#
# Comment out this property to force use of the latest available version.
#
# If the pbuild is CVS-based rather than package-based, this property must be
# set to 'CVS'.
#
# Example:
#
# version.recommended=2.0.4
# Latest Package Version
#
# There are currently two ways to inform Pants of the latest version number for
# your package.
#
# Method 1: Manually modify the property 'version.latest' to reflect the latest
# version number.
#
# Method 2: Provide a URL for a page on the package's website and a regular
# expression with which to parse it in order to extract the version
# number of the latest available package. For this you must define the
# properties 'version.latest.find.url', 'version.latest.find.regex',
# and any regular expression engine mode flags needed. The pattern
# defined must have exactly one capturing group to encapsulate the
# version string, otherwise the operation will fail.
#
# You may use both methods, in which case the version number specified by Method
# 1 will be used as the fallback value if Method 2 for some reason is
# unsuccessful.
#
# If neither method is enabled here or they fail to return a valid value to
# Pants, the 'ant update' operation for this pbuild may exit ungracefully unless
# the pbuild is CVS-based (none of the version.latest.* properties are used by
# CVS-based pbuilds).
#
# The following is a list of boolean properties for optional mode flags used by
# the regular expression engine. Set a value of "true" for any you wish to use.
#
# version.latest.find.regex.canonicaleq - Enable canonical equivalence
# version.latest.find.regex.caseinsensitive - Enable case-insensitive matching
# version.latest.find.regex.comments - Permit whitespace and comments
# version.latest.find.regex.dotall - Enable dotall mode
# version.latest.find.regex.multiline - Enable multi-line mode
# version.latest.find.regex.unicodecase - Enable Unicode-aware case folding
# version.latest.find.regex.unixlines - Enable Unix lines mode
#
# Examples:
#
# version.latest=5.1.2
# version.latest.find.url=http://sourceforge.net/projects/jetty/
# version.latest.find.regex=Stable.+?Jetty-(.+?)</A>
# Package URL
#
# Specify the URL pointing to the pbuild's package from here. The token
# '${pbuild.version}' if used will automatically be expanded to the appropriate
# version string.
#
# The package URL property is not used by CVS-based pbuilds.
#
# Examples:
#
# package.url=ftp://borkbork.se/bork-${pbuild.version}.tar.bz2
# package.url=http://bork.borkbork.se/bork-${pbuild.version}-src.tar.gz
# CVS Repository
#
# The values expected for CVS properties here are the same as those expected by
# their corresponding Apache Ant 'Cvs' task attributes. For details see:
#
# http://ant.apache.org/manual/CoreTasks/cvs.html
#
# Not all of the 'Cvs' task's attributes have corresponding Pants properties.
# The following is a list of all valid CVS properties for Pants (and their
# default values if applicable):
#
# cvs.compression.level
# cvs.date
# cvs.package
# cvs.passfile=~/.cvspass
# cvs.port=2401
# cvs.root
# cvs.rsh
# cvs.tag
#
# Of these, only the 'cvs.root' property is required for CVS-based pbuilds.
#
# Examples:
#
# cvs.root=:pserver:anoncvs@borkbork.se:/cvsroot/bork
# cvs.rsh=ssh
# cvs.package=borkbork

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This is a template for Pants pbuilds. Pbuilds use standard Apache Ant syntax.
For each target in the Public Interface section you must provide either an
implementation or a stub. You may also add your own custom tasks and
properties to this file. Be careful that none of your custom properties'
names clash with the properties defined in pants/build.xml.
-->
<project basedir="." default="build" name="name-of-pbuild-here">
<!-- ....................... Begin Public Interface ........................ -->
<!--
When this target is called, the pbuild's sources and/or binaries have
already been extracted/copied by Pants into the pbuild's working/
subdirectory. This target must prepare those sources and/or binaries in
the working/ subdirectory into deployable form, for example by building
all necessary classes and jar files.
This target must not create or modify any files outside the pbuild's
working/ subdirectory. (An automatic sandboxing mechanism should be added
to Pants at some point.) It is however acceptable for a task called by
'builddep' to modify files outside of this pbuild's working/ directory.
-->
<target name="build" depends="builddep" />
<!--
Use this to call targets from other pbuilds, Ant buildfiles, Makefiles,
etc. which perform tasks this pbuild's 'build' target depends on. If other
pbuilds are called here, they must be called through the Pants interface
or else it may leave Pants in an inconsistent state.
Most pbuilds probably won't need to implement this target.
-->
<target name="builddep" />
<!--
This target must undo the actions performed by the 'build' target.
-->
<target name="clean" depends="depclean" />
<!--
If the 'builddep' target is implemented, this target must be implemented
to undo its actions.
-->
<target name="depclean" />
<!--
This target must copy all deployable files generated by the 'build' target
into the pbuild's dist/ subdirectory (for use by other pbuilds or Ant
processes) or to their final deployment locations outside the pants/
directory hierarchy. Note that the latter may require the user to gain
superuser/admin privileges.
-->
<target name="dist" depends="build" />
<!--
This target must remove all files from the pbuild's dist/ subdirectory
and final deployment locations, reversing the actions of the 'dist'
target. Note that removal of files from their final deployment locations
may require the user to gain superuser/admin privileges.
-->
<target name="distclean" depends="clean" />
<!-- ........................ End Public Interface ......................... -->
</project>

View File

@ -0,0 +1,112 @@
# The properties defined in this file can be overridden on the command line by
# passing them in as parameters like so:
#
# ant -Dpbuild=myapp -Dversion.recommended=2.0.5 install
#
# *** DO NOT DEFINE A PROPERTY BUT LEAVE ITS VALUE BLANK. PANTS WILL BREAK! ***
# Recommended Package Version
#
# Set this property's value to the package version you want Pants to use for the
# pbuild by default. The version string specified must match the version
# substring from the package's filename if the filename contains a version
# number.
#
# Comment out this property to force use of the latest available version.
#
# If the pbuild is CVS-based rather than package-based, this property must be
# set to 'CVS'.
#
# Example:
#
# version.recommended=2.0.4
version.recommended=CVS
# Latest Package Version
#
# There are currently two ways to inform Pants of the latest version number for
# your package.
#
# Method 1: Manually modify the property 'version.latest' to reflect the latest
# version number.
#
# Method 2: Provide a URL for a page on the package's website and a regular
# expression with which to parse it in order to extract the version
# number of the latest available package. For this you must define the
# properties 'version.latest.find.url', 'version.latest.find.regex',
# and any regular expression engine mode flags needed. The pattern
# defined must have exactly one capturing group to encapsulate the
# version string, otherwise the operation will fail.
#
# You may use both methods, in which case the version number specified by Method
# 1 will be used as the fallback value if Method 2 for some reason is
# unsuccessful.
#
# If neither method is enabled here or they fail to return a valid value to
# Pants, the 'ant update' operation for this pbuild may exit ungracefully unless
# the pbuild is CVS-based (none of the version.latest.* properties are used by
# CVS-based pbuilds).
#
# The following is a list of boolean properties for optional mode flags used by
# the regular expression engine. Set a value of "true" for any you wish to use.
#
# version.latest.find.regex.canonicaleq - Enable canonical equivalence
# version.latest.find.regex.caseinsensitive - Enable case-insensitive matching
# version.latest.find.regex.comments - Permit whitespace and comments
# version.latest.find.regex.dotall - Enable dotall mode
# version.latest.find.regex.multiline - Enable multi-line mode
# version.latest.find.regex.unicodecase - Enable Unicode-aware case folding
# version.latest.find.regex.unixlines - Enable Unix lines mode
#
# Examples:
#
# version.latest=5.1.2
# version.latest.find.url=http://sourceforge.net/projects/jetty/
# version.latest.find.regex=Stable.+?Jetty-(.+?)</A>
# Package URL
#
# Specify the URL pointing to the pbuild's package from here. The token
# '${pbuild.version}' if used will automatically be expanded to the appropriate
# version string.
#
# The package URL property is not used by CVS-based pbuilds.
#
# Examples:
#
# package.url=ftp://borkbork.se/bork-${pbuild.version}.tar.bz2
# package.url=http://bork.borkbork.se/bork-${pbuild.version}-src.tar.gz
# CVS Repository
#
# The values expected for CVS properties here are the same as those expected by
# their corresponding Apache Ant 'Cvs' task attributes. For details see:
#
# http://ant.apache.org/manual/CoreTasks/cvs.html
#
# Not all of the 'Cvs' task's attributes have corresponding Pants properties.
# The following is a list of all valid CVS properties for Pants (and their
# default values if applicable):
#
# cvs.compression.level
# cvs.date
# cvs.package
# cvs.passfile=~/.cvspass
# cvs.port=2401
# cvs.root
# cvs.rsh
# cvs.tag
#
# Of these, only the 'cvs.root' property is required for CVS-based pbuilds.
#
# Examples:
#
# cvs.root=:pserver:anoncvs@borkbork.se:/cvsroot/bork
# cvs.rsh=ssh
# cvs.package=borkbork
cvs.root=:ext:anoncvs@savannah.gnu.org:/cvsroot/gnu-crypto
cvs.rsh=ssh
cvs.package=gnu-crypto

View File

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="build" name="fortuna-pbuild">
<property name="gnucrypt.base.dir" value="./working/gnu-crypto" />
<property name="gnucrypt.etc.dir" value="${gnucrypt.base.dir}/etc" />
<property name="gnucrypt.lib.dir" value="${gnucrypt.base.dir}/lib" />
<property name="gnucrypt.object.dir" value="${gnucrypt.base.dir}/classes" />
<property name="gnucrypt.base.crypto.object.dir" value="${gnucrypt.object.dir}/gnu/crypto" />
<property name="gnucrypt.cipher.object.dir" value="${gnucrypt.base.crypto.object.dir}/cipher" />
<property name="gnucrypt.hash.object.dir" value="${gnucrypt.base.crypto.object.dir}/hash" />
<property name="gnucrypt.prng.object.dir" value="${gnucrypt.base.crypto.object.dir}/prng" />
<patternset id="fortuna.files">
<include name="${gnucrypt.base.crypto.object.dir}/Registry.class" />
<include name="${gnucrypt.prng.object.dir}/Fortuna*.class" />
<include name="${gnucrypt.prng.object.dir}/BasePRNG.class" />
<include name="${gnucrypt.prng.object.dir}/RandomEventListener.class" />
<include name="${gnucrypt.prng.object.dir}/IRandom.class" />
<include name="${gnucrypt.cipher.object.dir}/CipherFactory.class" />
<include name="${gnucrypt.cipher.object.dir}/IBlockCipher.class" />
<include name="${gnucrypt.hash.object.dir}/HashFactory.class" />
<include name="${gnucrypt.hash.object.dir}/IMessageDigest.class" />
</patternset>
<!--
Add this when Fortuna tests are added to GNU Crypto, else write some
-->
<target name="-test" />
<!-- ....................... Begin Public Interface ........................ -->
<!--
When this target is called, the pbuild's sources and/or binaries have
already been extracted/copied by Pants into the pbuild's working/
subdirectory. This target must prepare those sources and/or binaries in
the working/ subdirectory into deployable form, for example by building
all necessary classes and jar files.
This target must not create or modify any files outside the pbuild's
working/ subdirectory. (An automatic sandboxing mechanism should be added
to Pants at some point.) It is however acceptable for a task called by
'builddep' to modify files outside of this pbuild's working/ directory.
-->
<target name="build" depends="builddep">
<delete dir="./working/build" />
<delete dir="./working/jartemp" />
<mkdir dir="./working/build" />
<mkdir dir="./working/jartemp/${gnucrypt.object.dir}" />
<copy todir="./working/jartemp">
<fileset dir=".">
<patternset refid="fortuna.files" />
</fileset>
</copy>
<jar basedir="./working/jartemp/${gnucrypt.object.dir}" jarfile="./working/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="./working/jartemp" />
</target>
<!--
Use this to call targets from other pbuilds, Ant buildfiles, Makefiles,
etc. which perform tasks this pbuild's 'build' target depends on. If other
pbuilds are called here, they must be called through the Pants interface
or else it may leave Pants in an inconsistent state.
Most pbuilds probably won't need to implement this target.
-->
<target name="builddep">
<ant dir="${gnucrypt.base.dir}" target="jar" />
</target>
<!--
This target must undo the actions performed by the 'build' target.
-->
<target name="clean" depends="depclean">
<delete dir="./working/jartemp" />
</target>
<!--
If the 'builddep' target is implemented, this target must be implemented
to undo its actions.
-->
<target name="depclean">
<!--
Annoyingly the GNU Crypto distclean task called here doesn't clean
*all* derived files from java/gnu-crypto/lib like it should (because
a couple of lines are commented out).....
-->
<ant dir="${gnucrypt.base.dir}" target="distclean" />
<!--
.....and so we mop up the rest ourselves.
-->
<delete dir="${gnucrypt.lib.dir}" />
</target>
<!--
This target must copy all deployable files generated by the 'build' target
into the pbuild's dist/ subdirectory (for use by other pbuilds or Ant
processes) or to their final deployment locations outside the pants/
directory hierarchy. Note that the latter may require the user to gain
superuser/admin privileges.
-->
<target name="dist" depends="build">
<copy todir="./dist/fortuna.jar" file="./working/build/fortuna.jar" />
</target>
<!--
This target must remove all files from the pbuild's dist/ subdirectory
and final deployment locations, reversing the actions of the 'dist'
target. Note that removal of files from their final deployment locations
may require the user to gain superuser/admin privileges.
-->
<target name="distclean" depends="clean">
<delete file="./dist/fortuna.jar" />
</target>
<!-- ........................ End Public Interface ......................... -->
</project>

View File

@ -0,0 +1,112 @@
# The properties defined in this file can be overridden on the command line by
# passing them in as parameters like so:
#
# ant -Dpbuild=myapp -Dversion.recommended=2.0.5 install
#
# *** DO NOT DEFINE A PROPERTY BUT LEAVE ITS VALUE BLANK. PANTS WILL BREAK! ***
# Recommended Package Version
#
# Set this property's value to the package version you want Pants to use for the
# pbuild by default. The version string specified must match the version
# substring from the package's filename if the filename contains a version
# number.
#
# Comment out this property to force use of the latest available version.
#
# If the pbuild is CVS-based rather than package-based, this property must be
# set to 'CVS'.
#
# Example:
#
# version.recommended=2.0.4
version.recommended=5.1.2
# Latest Package Version
#
# There are currently two ways to inform Pants of the latest version number for
# your package.
#
# Method 1: Manually modify the property 'version.latest' to reflect the latest
# version number.
#
# Method 2: Provide a URL for a page on the package's website and a regular
# expression with which to parse it in order to extract the version
# number of the latest available package. For this you must define the
# properties 'version.latest.find.url', 'version.latest.find.regex',
# and any regular expression engine mode flags needed. The pattern
# defined must have exactly one capturing group to encapsulate the
# version string, otherwise the operation will fail.
#
# You may use both methods, in which case the version number specified by Method
# 1 will be used as the fallback value if Method 2 for some reason is
# unsuccessful.
#
# If neither method is enabled here or they fail to return a valid value to
# Pants, the 'ant update' operation for this pbuild may exit ungracefully unless
# the pbuild is CVS-based (none of the version.latest.* properties are used by
# CVS-based pbuilds).
#
# The following is a list of boolean properties for optional mode flags used by
# the regular expression engine. Set a value of "true" for any you wish to use.
#
# version.latest.find.regex.canonicaleq - Enable canonical equivalence
# version.latest.find.regex.caseinsensitive - Enable case-insensitive matching
# version.latest.find.regex.comments - Permit whitespace and comments
# version.latest.find.regex.dotall - Enable dotall mode
# version.latest.find.regex.multiline - Enable multi-line mode
# version.latest.find.regex.unicodecase - Enable Unicode-aware case folding
# version.latest.find.regex.unixlines - Enable Unix lines mode
#
# Examples:
#
# version.latest=5.1.2
# version.latest.find.url=http://sourceforge.net/projects/jetty/
# version.latest.find.regex=Stable.+?Jetty-(.+?)</A>
version.latest=5.1.2
version.latest.find.url=http://sourceforge.net/projects/jetty/
version.latest.find.regex=Stable.+?Jetty-(.+?)</A>
# Package URL
#
# Specify the URL pointing to the pbuild's package from here. The token
# '${pbuild.version}' if used will automatically be expanded to the appropriate
# version string.
#
# The package URL property is not used by CVS-based pbuilds.
#
# Examples:
#
# package.url=ftp://borkbork.se/bork-${pbuild.version}.tar.bz2
# package.url=http://bork.borkbork.se/bork-${pbuild.version}-src.tar.gz
package.url=http://mesh.dl.sourceforge.net/sourceforge/jetty/jetty-${pbuild.version}.zip
# CVS Repository
#
# The values expected for CVS properties here are the same as those expected by
# their corresponding Apache Ant 'Cvs' task attributes. For details see:
#
# http://ant.apache.org/manual/CoreTasks/cvs.html
#
# Not all of the 'Cvs' task's attributes have corresponding Pants properties.
# The following is a list of all valid CVS properties for Pants (and their
# default values if applicable):
#
# cvs.compression.level
# cvs.date
# cvs.package
# cvs.passfile=~/.cvspass
# cvs.port=2401
# cvs.root
# cvs.rsh
# cvs.tag
#
# Of these, only the 'cvs.root' property is required for CVS-based pbuilds.
#
# Examples:
#
# cvs.root=:pserver:anoncvs@borkbork.se:/cvsroot/bork
# cvs.rsh=ssh
# cvs.package=borkbork

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="all" name="jetty">
<!-- make this generic, place variables in properties file -->
<target name="all" depends="build"
description="Run the build target" />
<target name="assignProperties" if="group.0">
<property name="latest.jetty.version" value="${group.1}" />
<available property="jetty.package.available" file="jetty-${latest.jetty.version}.zip" />
<available property="jetty.package.unpacked.available" file="jettypkg/jetty-${latest.jetty.version}" />
<echo message="Properties assigned" />
</target>
<target name="build" depends="init, unpackJettyPackage" if="latest.jetty.version"
description="Download latest Jetty package and copy needed libs to jettylib/">
<property name="unpack.dir" value="jettypkg/jetty-${latest.jetty.version}" />
<copy todir="jettylib" overwrite="true" file="${unpack.dir}/ext/ant.jar" />
<copy todir="jettylib" overwrite="true" file="${unpack.dir}/ext/jasper-compiler.jar" />
<copy todir="jettylib" overwrite="true" file="${unpack.dir}/ext/jasper-runtime.jar" />
<copy todir="jettylib" overwrite="true" file="${unpack.dir}/ext/xercesImpl.jar" />
<copy todir="jettylib" overwrite="true" file="${unpack.dir}/ext/xml-apis.jar" />
<copy todir="jettylib" overwrite="true" file="${unpack.dir}/extra/lib/org.mortbay.jetty-jdk1.2.jar" />
<copy todir="jettylib" overwrite="true" file="${unpack.dir}/lib/javax.servlet.jar" />
<copy todir="jettylib" overwrite="true" file="${unpack.dir}/lib/org.mortbay.jetty.jar" />
<copy todir="jettylib" overwrite="true">
<fileset dir="${unpack.dir}/ext" includes="xmlParserAPIs*.jar" />
</copy>
</target>
<target name="builddep"
description="Build the custom helper Ant task for this buildfile">
<mkdir dir="java/build"/>
<javac srcdir="./java/src" source="1.3" target="1.3" deprecation="on" destdir="./java/build" />
</target>
<target name="clean"
description="Remove temp files and zip only; jettypkg/ requires manual deletion">
<echo message="Not actually deleting the Jetty package directory since it's so large" />
<delete>
<fileset dir="." includes="*.zip jettytemp.html parsed.temp" />
</delete>
</target>
<target name="cleandep"
description="Remove custom helper Ant task">
<delete dir="java/build" />
</target>
<target name="compile" />
<target name="distclean" depends="clean"
description="Remove temp files, zip and jettylib/ contents" >
<delete>
<fileset dir="jettylib" includes="*.jar"/>
</delete>
</target>
<target name="fetchJettyPackage" if="latest.jetty.version" unless="jetty.package.available">
<echo message="The Jetty libs are not necessary for using I2P, but are used by some" />
<echo message="applications on top of I2P such as the routerconsole." />
<get src="http://mesh.dl.sourceforge.net/sourceforge/jetty/jetty-${latest.jetty.version}.zip" verbose="true" dest="jetty-${latest.jetty.version}.zip" />
</target>
<target name="init" depends="builddep">
<echo message="Checking SourceForge for latest Jetty version....." />
<get src="http://sourceforge.net/projects/jetty/" dest="jettytemp.html" verbose="true" />
<taskdef name="match" classname="net.i2p.pants.MatchTask" classpath="../../lib/pants.jar" />
<match input="jettytemp.html"
output="parsed.temp"
regex="Stable.+?Jetty-(.+?)&lt;/A&gt;"
/>
<loadproperties srcFile="parsed.temp" />
<antcall target="assignProperties" />
</target>
<target name="jar" />
<target name="showlatest" depends="init"
description="Display latest version number for Jetty">
<echo message="Latest Jetty version: ${latest.jetty.version}" />
</target>
<target name="unpackJettyPackage" depends="fetchJettyPackage" if="latest.jetty.version" unless="jetty.package.unpacked.available">
<mkdir dir="jettypkg" />
<unzip src="jetty-${latest.jetty.version}.zip" dest="jettypkg" />
</target>
</project>

2
apps/pants/world Normal file
View File

@ -0,0 +1,2 @@
version.using.fortuna=CVS
version.using.jetty=5.1.2

BIN
apps/q/doc/client.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

26
apps/q/doc/diagrams.html Normal file
View File

@ -0,0 +1,26 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Q System Diagrams</title>
</head>
<body>
<h1>Q Diagrams</h1>
Informal system diagrams of Q network, hubs and clients.
<center>
<hr>
<img src="overall.jpg">
<hr>
<img src="client.jpg">
<hr>
<img src="hub.jpg">
</center>
<hr>
<address><a href="mailto:aum@mail.i2p">aum</a></address>
<!-- Created: Sat Apr 16 17:24:02 NZST 2005 -->
<!-- hhmts start -->
Last modified: Mon Apr 18 14:06:02 NZST 2005
<!-- hhmts end -->
</body>
</html>

BIN
apps/q/doc/hub.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

80
apps/q/doc/index.html Normal file
View File

@ -0,0 +1,80 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Quartermaster - I2P Distributed File Store</title>
</head>
<body>
<center>
<h1>Quartermaster<br>an I2P Distributed File Store</h1>
<h3>STATUS<h3>
<i>Whole new (incompatible) version currently in development;
ETA for release approx 4-7 days;
view screenshots <a href="screenshots.html">here</a>
</i>
<br>
<hr>
<small>
<a href="manual/index.html">User Manual</a> |
<a href="spec/index.html">Protocol Spec</a> |
<a href="metadata.html">Metadata Spec</a> |
<a href="diagrams.html">Q Pr0n (diagrams)</a> |
<a href="api/index.html">API Spec</a> |
<a href="qnoderefs.txt">qnoderefs.txt</a> |
Full Download |
Updated jar
</small>
</center>
<hr>
<h2>Intro</h2>
Quartermaster, or Q for short, is a distributed file storage framework for I2P.
<h2>Features</h2>
<ul>
<li>Now features 'QSites' - the Q equivalent of Freenet freesites,
static websites which are retrievable even if author is offline</li>
<li>Easy web interface - interact with Q (and view/insert QSites)
from your web browser</li>
<li>Maximum expectations of content retrievability</li>
<li>Content security akin to Freenet CHK and SSK keys</li>
<li>Powerful, flexible search engine</li>
<li>Comfortably accommodates both permanent and transient
nodes without significant network disruption (for instance,
no flooding of the I2P network with futile
calls to offline nodes)</li>
<li>Rapid query resolution, due to distributed catalogue
mirroring which eliminates all in-network query traffic</li>
<li>Modular, extensible architecture</li>
<li>Simple interfaces for 3rd-party app developers</li>
<li>Is custom-designed and built around I2P, so no duplication of
I2P's encryption/anonymity features</li>
<li>Simple XML-RPC interface for all inter-node communication, makes it easy to
implement user-level clients in any language; also allows alternative
implementations of core server and/or client nodes.</li>
</ul>
<hr>
<h2>Status</h2>
Q is presently under development, and a test release is expected soon.
<hr>
<h2>Architecture</h2>
Refer to the <a href="spec/index.html">Protocol Specification</a> for more information.
<hr>
<!-- Created: Sat Mar 26 11:09:12 NZST 2005 -->
<!-- hhmts start -->
Last modified: Mon Apr 18 18:55:19 NZST 2005
<!-- hhmts end -->
</body>
</html>

View File

@ -0,0 +1,805 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Q User/Programmer Manual</title>
<style type="text/css">
<!--
td { vertical-align: top; }
code { font-family: courier, monospace; font-weight: bold }
-->
</style>
</head>
<body style="font-family: arial, helvetica, sans-serif">
<center>
<h1>Q User/Programmer Manual</h1>
<i>A brief but hopefully easy guide to installing and using the Q distributed file
store within the I2P network</i>
<br><br>
<i>(Return to <a href="../index.html">Q Homepage</a>)</i>
<br>
<br>
<small>
<a href="#intro">Introduction</a> |
<a href="#checklist">Checklist</a> |
<a href="#serverorclient">Server?orClient?</a> |
<a href="#walkthrough">Walkthrough</a> |
<a href="#server">Server Nodes</a> |
<a href="#qmgr">About QMgr</a> |
<a href="#contact">Contact us</a>
</small>
</center>
<a name="intro"/>
<hr>
<h2>1. Introduction</h2>
<blockquote>
Q is a distributed Peer2Peer file storage/retrieval network that aims to deliver optimal
performance by respecting the properties of the I2P network.<br>
<br>
This manual serves as a 'walkthrough' guide, to take you through the steps from initial
download, to everyday usage. It also provides information for the benefit of higher-level
client application authors.
</blockquote>
<a name="checklist"/>
<hr>
<h2>2. Preliminary Checklist</h2>
<blockquote>
OK, we assume here that you've already cracked the tarball, and are looking at
the distribution files.<br>
<br>
In order to get Q set up and running, you'll need:
<ol>
<li>An I2P router installed, set up and (permanently or transiently) running</li>
<li>Your system shell set up with at the environment variables:
<ul>
<li><b>CLASSPATH</b> - this should include:
<ul>
<li>The regular I2P jar files and 3rd party support jar files (eg <b>i2p.jar</b>,
<b>i2ptunnel.jar</b>, <b>streaming.jar</b>,
<b>mstreaming.jar</b>, <b>jbigi.jar</b>)</li>
<li>Apache's XML-RPC support jarfile - included in this Q distro as
<b>xmlrpc.jar</b></li>
<li>Aum's jarfile <b>aum.jar</b>, which includes Q and all needed support code</li>
</ul>
</li>
<li><b>PATH</b> - your execution search path <b><i>must</i></b> include the directory
in which your main java VM execution program (<b>java</b>, or on windows systems,
<b>java.exe</b>) resides.<br>
<b>NOTE</b> - if <b>java[.exe]</b> is not on your <b>PATH</b>, then Q <i>will
not run</i>.</li>
</ul>
</ol>
</blockquote>
<a name="serverorclient"/>
<hr>
<h2>3. Q Server or Q Client?</h2>
<blockquote>
Nearly everyone will want to run a <b>Q Client Node</b>.<br>
<Br>
It is only client nodes which provide users with full access to the Q network.<br>
<br>
However, if you have a (near-) permanently running I2P Router, and you're a kind and
generous soul, you might <i>also</i> be willing to run a <b>Q Server Node</b> in addition
to your <b>Q Client Node</b>.<br>
<br>
If you do choose to run a server node, you'll be expected to keep it running as near as
possible to 24/7. While transience of client nodes - frequent entering and leaving the
Q network - causes little or no disruption, transience of server nodes can significantly
impair Q's usability for everyone, particularly if this transience occurs frequently amongst
more than the smallest percentage of the server node pool.<br>
<br>
Until you're feeling well "settled in" with Q, your best approach is to just run a
client node for now, and add a server node later when you feel ready.<br>
</blockquote>
<a name="walkthrough"/>
<hr>
<h2>4. Q Walkthrough</h2>
<h3>4.1. Introduction</h3>
<blockquote>
This chapter discusses the deployment and usage of a Q Client Node, and will take you
through the steps of:
<ol>
<li>Double-checking that you've met the installation requirements</li>
<li>Launching a Q Client Node</li>
<li>Verifying that your Q Client Node is running</li>
<li>If your node fails to launch, figuring out why</li>
<li>Importing one or more noderefs into your node</li>
<li>Observing that your node is discovering other nodes on the network</li>
<li>Observing that your node is discovering content on the network</li>
<li>Searching for items of content that match chosen criteria</li>
<li>Retrieving stuff from the network</li>
<li>Inserting stuff to the network</li>
<li>Shutting down your client node</li>
</ol>
Setup and running of Q Server Nodes will be discussed in a later chapter.
</blockquote>
<hr>
<h3>4.2. Verify Your Q Installation Is Correct</h3>
<blockquote>
Ensure that all the needed I2P jarfiles, as well as <b>xmlrpc.jar</b> and
Q's very own <b>aum.jar</b> are correctly listed in your <b>CLASSPATH</b> environment
varaible, and your main java launcher is correctly listed in your <b>PATH</b> environment
variable.<br>
<br>
Typically, you will likely copy the jarfiles <b>aum.jar</b> and <b>xmlrpc.jar</b>
into the <b>lib/</b> subdirectory of your I2P router installation, along with all
the other I2P jar files. Wherever you choose to put these files, make sure they're
correctly listed in your <b>CLASSPATH</b>.
<br>
Also, you'll want to add execute permission to your <b>qmgr</b> (or <b>qmgr.bat</b>)
wrapper script, and copy it into one of the directories listed in your <b>PATH</b>
environment variable.<br>
</blockquote>
<hr>
<h3>4.3. Get Familiar With qmgr</h3>
<blockquote>
<b>qmgr</b> (or <b>qmgr.bat</b>) is a convenience wrapper script to save your
sore fingers from needless typing. It's just a wrapper which passes arguments
to the java command <b><code>java&nbsp;net.i2p.aum.q.QMgr</code></b><br>
<br>
You can verify you've set up qmgr correctly with the command:
<blockquote><code><pre>
qmgr help</pre></code></blockquote>
This displays a brief summary of qmgr commands. On the other hand, the command:
<blockquote><code><pre>
qmgr help verbose</pre></code></blockquote>
floods your terminal window with a detailed explanation of all the qmgr commands
and their arguments.<br>
</blockquote>
<hr>
<h3>4.4. Running A Q Client Node For The First Time</h3>
<blockquote>
Provided you've successfully completed the preliminaries, you can launch your
Q Client Node with the command:
<blockquote><code><pre>
qmgr start</pre></code></blockquote>
All going well, you should have a Q Client Node now running in background.
</blockquote>
<hr>
<h3>4.5. Verify that your Q Client Node is actually Running</h3>
<blockquote>
After typed the <b>qmgr start</b> command, you will see little or no
evidence that Q is actually running.<br>
<br>
You can test if the node is actually up by typing the command:
<blockquote><code><pre>
qmgr status</pre></code></blockquote>
If your Q Client Node is running, this <b>status</b> command should produce
something like:
<blockquote><code><pre>
Pinging node at '/home/myusername/.quartermaster_client'...
Node Ping:
status=ok
numPeers=0
dest=-3LQaE215uIYwl-DsirnzXGQBI31EZQj9u~xx45E823WqjN5i2Umi37GPFTWc8KyislDjF37J7jy5newLUp-qrDpY7BZum3bRyTXo3Udl8a3sUjuu4qR5oBEWFfoghQiqDGYDQyJV9Rtz7DEGaKHGlhtoGsAYRXGXEa8a43T2llqZx2fqaXs~836g8t6sLZjryA5A9fpq98nE5lT0hcTalPieFpluJVairZREXpUiAUmGHG7wAIjF6iszXLEHSZ8Qc622Xgwy0d1yrPojL2yhZ64o05aueYcr~xNCiFxYoHyEJO3XYmkx~q-W-mzS3nn6pRevRda74MnX1~3fFDZ0u~OG6cLZoFkWgnxrwrWGFUUVMR87Yz251xMCKJAX6zErcoGjGFpqGZsWxl4~yq7yfkjPnq3GuTxp2cB75bRAOZRIAieqBOVJDEodFYW5amCinu4AxYE7G1ezz4ghqHFe~0yaAdO74Q1XoUny138YT6P33oNOOlISO1cAAAA
uptime=4952
load=0.0
id=6LVZ9-~GgJJ52WUF1fLHt3UnH50TnXSoPQXy7WZ4GA=
numLocalItems=47
numRemoteItems=2173</pre></code></blockquote>
If you see something like this, then smile, because Q is now up on your system.<br>
<br>
If the node launch failed, you might see something like:
<blockquote><code><pre>
Pinging node at '/home/myusername/.quartermaster_client'...
java.io.IOException: Connection refused
at org.apache.xmlrpc.XmlRpcClient$Worker.execute(Unknown Source)
at org.apache.xmlrpc.XmlRpcClient.execute(Unknown Source)
at net.i2p.aum.q.QMgr.doStatus(QMgr.java:310)
at net.i2p.aum.q.QMgr.execute(QMgr.java:813)
at net.i2p.aum.q.QMgr.main(QMgr.java:869)
Failed to ping node</pre></code></blockquote>
This indicates that your Q client node has either crashed, or failed to launch in the
first place.<br>
<br>
If you're having trouble like this, you might like to try running your Q client node
in foreground, instead of spawning it off into background.<br>
<br>
The command to run a Q client node in foreground is:
<blockquote><code><pre>
qmgr foreground</pre></code></blockquote>
You should see some meaningless startup messages, and no return to your shell prompt.<br>
</blockquote>
<hr>
<h3>4.6. Diversion - Q Storage Directories</h3>
<blockquote>
By default, when you run a Q Client Node, it creates a datastore directory tree
at <b>~/.quartermaster_client</b>. (Windows users note - you'll find this directory
wherever your user home directory is - this depends on what version of Windows
you have installed).<br>
<br>
Within this directory tree, you should see a file called <b>node.log</b>, which
will contain various debug log messages, and can help you to rectify any problems
with your Q installation. If you hit a wall and can't rectify the problems
yourself, you should send this file to the Q author (aum).<br>
<br>
It's possible to run your Q node from another directory, by passing that directory
as a <b>-dir &lt;path&gt;</b> argument to the
<b>qmgr</b> <b>start</b>, <b>foreground</b> and <b>stop</b>
commands. See <b>qmgr help verbose</b> for more information.
</blockquote>
<hr>
<h3>4.7. Importing a Noderef</h3>
<blockquote>
Note from the prior <b>qmgr status</b> command the line:
<blockquote><code><pre>
numPeers=0</pre></code></blockquote>
This means that your Q client node is running standalone, and doesn't have any contact
with any Q network. As such, your node is effectively useless. We need to hook up
your node with other nodes in the Q network.<br>
<br>
Q doesn't ship with any means for new client nodes to automatically connect to any Q
server nodes. This is deliberate.<br>
<br>
In all likelihood, there will be one 'main' Q network running within I2P, largely
based around the author's own Q server node, and most people will likely want to
use this Q network. But the author doesn't want to stop other people running their
own private Q networks, for whatever purpose has meaning for them.
<blockquote><i><small>
<hr>
This is especially relevant for Q as opposed to Freenet. With Freenet, there's
no way for a user to know of the existence of any item of content without
first being given its 'key'. However, since Q works with published catalogs,
any user can know everything that's available on a Q network, which might
not be desirable to those wishing to share content in a private situation.<br>
<Br>
The Q author anticipates, and warmly supports, people running their own
private Q networks within I2P, in addition to accessing the mainstream
'official' Q network.<br>
<br>
The way Q is designed and implemented, there is no way for anyone, including
Q's author, to know of the existence of anyone else's private Q network.
It is beyond the author's control, (and thus arguably the author's
legal responsibility), what private Q networks people set up, and what
kind of content is trafficked on these networks. This claim of plausible
deniability on the part of Q's author parallels that of a hardware retailer
denying responsibility for what people do with tools that they purchase.
<hr>
</small>
</i></blockquote>
Ok, getting back on topic - your brand new virgin Q client node is useless and lonely,
and desperately needs some Q server nodes to talk to. So let's hook up your node to
the mainstream Q network.<br>
<br>
You'll need to get one or more 'noderefs' for Q server nodes.<br>
<br>
There's nothing fancy about a Q noderef. It's just a regular I2P 'destination', with
which your Q Client Node can connect with a Q Server Node.<br>
<br>
A 'semi-official' list of noderefs for the mainstream Q network can be downloaded
from the url: <a href="http://aum.i2p/q/qnoderefs.txt">http://aum.i2p/q/qnoderefs.txt</a>.<br>
<br>
Download this file, save it as (say) <b>qnoderefs.txt</b>. (Alternatively, if you're
wanting to subscribe into a private Q network, then get a noderef for at least one
of that network's server nodes from someone on that network who trusts you).<br>
<br>
Import these noderefs into your Q client node via the command:
<blockquote><code><pre>
qmgr addref qnoderefs.txt</pre></code></blockquote>
If all goes well, you should see no output from this command, or (possibly) a brief
line or two suggesting success.<br>
<br>
Your client node is now subscribed into the Q network of your choice. Verify this
with the command:
<blockquote><code><pre>
qmgr status</pre></code></blockquote>
In the output from that command, you should see the <b>numPeers=</b> line showing at least
1 peer.<br>
<br>
If there is more than one Q Server Node on the Q network you've just subscribed to,
then your local node should sooner or later discover all these server nodes, and
the <b>numPeers</b> value should increase over time.<br>
<br>
<blockquote>
<hr>
While Q is in its early development and testing stages, the author may abdicate
the mainstream Q network, and publish nodrefs for a whole new mainstream Q network.
This will especially happen if the author makes any substantial changes to the
inter-node protocol, and/or releases incompatible new versions of Q client/server
nodes. Remember that
<a href="http://aum.i2p/q/qnoderefs.txt">http://aum.i2p/q/qnoderefs.txt</a> will
serve as the authoritative source for noderefs for the mainstream Q network within
the mainstream I2P network.
<hr>
</blockquote>
When your client node gets its noderefs to a Q network, it will periodically,
from then on, retrieve differential peer list and catalog updates from servers
it knows about.<br>
<br>
Even if you only feed your client just one ref for a single server node, it will
in time discover all other operating server nodes on that Q network, and will
build up a full local catalog of everything that's available on that Q network.<br>
<br>
Provided that your client is running ok, and has been fed with at least one
ref for a live Q network that contains content, then over time, successive:
<blockquote><code><pre>
qmgr status</pre></code></blockquote>
commands should report increasing values in the fields:
<ul>
<li><b>numPeers</b> - number of peers this client node knows about</li>
<li><b>numLocalItems</b> - number of locally stored content items, ie items
which you have either inserted to, or retrieved from, your client node</li>
<li><b>numRemoteItems</b> - number of unique data items which are available
on remote server nodes in the Q network, and which can be retrieved through
your local client node.</li>
</ul>
<blockquote>
<hr>
<h4>4.7.1. One Big Warning</h4>
If you are participating in more than one distinct Q network, then <b>do not</b>
insert noderefs for different networks into the same running instance of a
local Q client, unless you don't plan on inserting content via that client.<br>
<Br>
For instance, let's say you are participating in two different Q networks:
<ul>
<li>The 'mainstream' Q netowrk</li>
<li>A secret Q network - "My friends' teen angst diaries"</li>
</ul>
If you get a noderef for both these networks, and insert both of these into the
same running Q client node, then this local client node will be transparently
connected to both networks.<br>
<br>
If you only ever plan on retrieving content, and never inserting content, this
won't be a problem, except that you won't be able to tell which content
resides on the mainstream Q network, and which resides in the secret Q network.<br>
<Br>
The big problem arises from inserting content. Whenever you insert data through this
'contaminated'
Q client node, this node picks 3 different servers to which upload a copy of this
data. You won't have any control over whether the data gets inserted to the mainstream
Q network, or your secret Q network. You might insert something sensitive, intending it
to go only into the secret Q network, where in fact it also ends up in the mainstream
network, with consequences you might not want.
</blockquote>
</blockquote>
<hr>
<h3>4.8. Content Data and Metadata</h3>
<blockquote>
Whenever content gets stored on Q, it is actually stored as two separate items:
<ul>
<li>The <b>raw data</b> - whether a text file, or the raw bytes of image files,
audio files etc</li>
<li>The <b>metadata</b>, which contains human-readable and machine-readable
descriptions of the data</li>
</ul>
Metadata consists of a set of <b>category=value</b> pairs.<br>
<br>
Confused yet? Don't worry, I'm confused as well. Let's illustrate this with an
example of metadata for an MP3 audio recording:
<ul>
<li>title=Fight_Last_Thursday.mp3</li>
<li>type=audio</li>
<li>mimetype=audio/mpeg</li>
<li>abstract=upcoming single recorded in our garage last April</li>
<li>keywords=grunge,country,indie</li>
<li>artist=Ring of Fire</li>
<li>size=4379443</li>
<li>contact=ring-of-fire@mail.i2p</li>
<li>key=blah37blah24-yada23hfhyada</li>
</ul>
All metadata categories are optional. In fact, you can insert content with no metadata
at all.<br>
<br>
If you fail to provide metadata when inserting an item, a blank set of metadata will
be created with at least the following categories:
<ul>
<li><b>key</b> - the derived key, under which the item will later be retrievable
by yourself and others</li>
<li><b>title</b> - if not provided at insert time, this will be set to the key</li>
<li><b>size</b> - size of the item's raw data, in bytes</li>
</ul>
Within Q, there is a convention to supply a minimal amount of metadata. While this
is not expected or enforced, including all these categories is most strongly
recommended. These core categories are:
<ul>
<li><b>title</b> - a meaningful title for the data item, consisting only of characters
which are legal in filenames on all platforms, and which ends with a file extension.</li>
<li><b>type</b> - one of a superset of eMule classifiers, such as:
<ul>
<li><b>text</b> - plain text</li>
<li><b>html</b> - HTML content</li>
<li><b>image</b> - content is in an image format, such as .png, .jpg, .gif etc</li>
<li><b>audio</b> - content is an audio sample, such as .ogg, .mp3, .wav etc</li>
<li><b>video</b> - due to the sheer size of video files, and Q's present design,
it's unlikely people will be inserting video content anytime soon (unless it's
very short)</li>
<li><b>archive</b> - packed file collections, such as .tar.gz, .zip, .rar etc</li>
<li><b>misc</b> - content does not fit into any of the above categories</li>
</ul>
</li>
<li><b>mimetype</b> - not as important as the <b>type</b> category, but providing
this category in your metadata is still strongly encouraged. Value for this category
should be one of the standard mimetypes, eg <b>text/html</b>, <b>audio/ogg</b> etc.</li>
<li><b>abstract</b> - a short description (<255 characters), intended for human reading</li>
<li><b>keywords</b> - a comma-separated list of keywords, intended for
machine-readability, should be all lowercase, no spaces</li>
</ul>
Note that you can supply extra metadata categories in addition to the above, and that
people searching for content can search on these extra categories if they know about
them.
</blockquote>
<hr>
<h3>4.9. Searching For Content</h3>
<blockquote>
As mentioned earlier - in constrast with Freenet, local Q nodes build up a complete
catalog of all available content on whatever Q network they are connected to.<br>
<br>
This is a design decision, based on the choice to eliminate query traffic.<br>
<br>
The author hopes that this will result in a distributed storage network with a
high retrievability guarantee, in contrast with freenet which offers no such
guarantee.<br>
<br>
With Freenet, you only ever know of the existence of something if someone tells
you about it.<br>
<br>
But with Q, your local client node builds up a global catalog of everything that's
available within the whole network.<br>
<br>
The QMgr client has a command for searching your Q client node:
<blockquote><code><pre>
qmgr search -m category1=pattern1 category2=pattern2 ...</pre></code></blockquote>
For example:
<blockquote><code><pre>
qmgr search -m type=audio artist=Mozart keywords=symphony</pre></code></blockquote>
or:
<blockquote><code><pre>
qmgr search -m type=text title="bible|biblical|(Nag Hammadi)" keywords="apocrypha|Magdalene"</pre></code></blockquote>
As implied in the latter example, search patterns are regular expressions. This example will
locate all text items, whose <b>title</b> metadata category contains one of <b>bible</b>, <b>biblical</b> or <b>Nag&nbsp;Hammadi</b>, <i>and</i> whose <b>keywords</b> category contains either
or both the words <b>apocrypha</b> or <b>Magdalene</b>.<br>
<br>
Please use the search function carefully, otherwise (if and when Q usage grows) you
could be inundated with thousands or even millions of entries.<br>
<br>
If a search turns up nothing, qmgr will simply exit. But if it turns up one or more items,
it will the items out one at a time, with the key first, then each metadata entry
on an indented line following.
</blockquote>
<hr>
<h3>4.10. Retrieving Content</h3>
<blockquote>
Now, we're actually going to retrieve something.<br>
<br>
Presumably, after following the previous section, you will have seen one or more search
results come up, with the 'keys' under which the items can be accessed.<br>
<br>
Now, choose one of the keys, preferably for a short text item. Try either of the following
commands:
<blockquote><code><pre>
qmgr get &lt;keystring&gt; something.txt</pre></code></blockquote>
<i>or</i>:
<blockquote><code><pre>
qmgr get &lt;keystring&gt; &gt; something.txt</pre></code></blockquote>
(both have the same effect - the first one explicitly writes to the named file, the second
one dumps the raw data to stdout, which we shell-redirect into the file.<br>
<br>
<b><i>Note - redirection of fetched data to a file via shell is not working at present. Use only
the first form till we fix the bug.</i></b>
</blockquote>
<hr>
<h3>4.11. Inserting Content</h3>
<blockquote>
Our last example in this walkthrough relates to inserting content.<br>
<br>
Firstly, create a small text file with 2-3 lines of text, and save it as (say)
myqinsert.txt.<br>
<br>
Now, think of some metadata to insert along with the file. Or, you can just use
the set:
<blockquote><code><pre>
type=text
keywords=test
abstract=My simple test of inserting into Q
title=myqinsert.txt</pre></code></blockquote>
Now, let's insert the file. Ensure your Q client node is running, then type:
<blockquote><code><pre>
qmgr put myqinsert.txt -m type=text keywords=test title="myqinsert.txt" \
abstract="My simple test of inserting into Q"</pre></code></blockquote>
If all went well, this command should produce half a line of gibberish, followed
immediately by your shell prompt, eg:
<blockquote><code><pre>
aRoFC~9MU~pM2C-uCTDBp5B7j79spFD8gUeu~BNkUf0=<b>$</b>
</pre></code></blockquote>
The '$' at the end is your shell prompt, and all the characters before it are the 'key'
which was derived from the content you just inserted.<br>
<br>
To avoid the hassle of copying/pasting the key, you could just add output redirection
to the above command, eg:
<blockquote><code><pre>
qmgr put myqinsert.txt -m type=text keywords=test title="myqinsert.txt" \
abstract="My simple test of inserting into Q" \
> myqinsert.key</pre></code></blockquote>
This will cause the generated key to be written safe and sound into the file
<b>myqinsert.key</b>.<br>
<br>
You can verify that this insert worked by a 'get' command, as in:
<blockquote><code><pre>
qmgr get `cat myqinsert.key` somefilename.ext</pre></code></blockquote>
(Note that this won't work on windows because the DOS shell is irredeemably brain-damaged. If
you're using Windows, you <b>will</b> have to cut/paste the key.
</blockquote>
<hr>
<h3>4.12. Shutting Down your Node</h3>
<blockquote>
If you've worked through to here, then congratulations! You've got your Q Client Node set up
and working, and ready to meet all your distributed file storage and retrieval needs.<br>
<br>
You can leave your client node running 24/7 if you want. In fact, we recommend you keep your
client node running as much of the time as possible, so that you get prompt catalog updates,
and can more quickly stay in touch with new content.<br>
<br>
However, if you need to shut down your node, the command for doing this is:
<blockquote><code><pre>
qmgr stop</pre></code></blockquote>
This command will take a while to complete (since the node has to wait for the I2P
java shutdown hooks to complete before it can rest in peace). But once your node is
shut down, you can start it up again at any time and pick up where you left off.
</blockquote>
<a name="server"/>
<hr>
<h2>5. Running a Q Server Node</h2>
<h3>5.1. Introduction</h3>
<blockquote>
This section describes the requirements for, and procedures involved with, running
a Q Server Node.<br>
<br>
We'll use a similar 'walkthrough' style to that which we used in the previous section
on client nodes.
</blockquote>
<hr>
<h3>5.2. Requirements and Choices</h3>
<blockquote>
Running a Q server is a generous thing to do, and helps substantially with making
Q work at its best for everyone. However, please do make sure you can meet some
basic requirements:
<ul>
<li>You are running a permanent (24/7) I2P Router, on a box with at least (say)
98% uptime.</li>
<li>You have a little bandwidth to spare, and don't mind the extra memory, disk and
CPU-usage footprint of running a fulltime Q server node</li>
<li>You have already been able to successfully run a Q client node.</li>
</ul>
Also, please decide whether you want your server node to contribute to the mainstream
Q network, or whether you want to create your own private Q network, or join someone
else's private network. Your contribution will be most appreciated, though, if you
can run a server within the mainstream Q network.
</blockquote>
<hr>
<h3>5.3. Starting Your Server Node</h3>
<blockquote>
Starting up a Q Server node is very similar to starting up a Q client node, except
that with the qmgr command line, you must put the keyword arg <b>server</b> before the
command word. So the command is:
<blockquote><code><pre>
qmgr server start</pre></code></blockquote>
Similar to Q client nodes, you can check the status of a running Q server node with
the command:
<blockquote><code><pre>
qmgr server status</pre></code></blockquote>
(Note that this command will take longer to complete than with client nodes, because
the communication passes through a multi-hop I2P tunnel, rather than just through
localhost TCP).<br>
<br>
If the status command succeeds, then you'll know your new Q Server Node is happily
running in background.
</blockquote>
<hr>
<h3>5.4. Joining A Q Network</h3>
<blockquote>
When a Q Server node starts up for the first time, it is in a private network
all by itself.<br>
<br>
If you want to link your server into an existing Q network, you'll have to add a
noderef for at least one other server on that network. The command to do this
is similar to that for subscribing a client node to a network:
<blockquote><code><pre>
qmgr server addref &lt;noderef-file&gt;</pre></code></blockquote>
where &lt;noderef-file&gt; is a file into which you've saved the noderef for
the network you want to join.
<blockquote>
<hr><i><small>
Recall from the section on client nodes that the authoritative noderefs
for the mainstream Q network can be downloaded from
<a href="http://aum.i2p/q/qnoderefs.txt">http://aum.i2p/q/qnoderefs.txt</a>.
</small></i><hr>
</blockquote>
After you've added the noderef, subsequent <b>qmgr server status</b> commands
should show <b>numPeers</b> having a value of at least 1 (and growing, as more
server nodes come online in the mainstream Q network.)
</blockquote>
<hr>
<h3>5.5. Private Networks - Exporting Your Server's Noderef</h3>
<blockquote>
If you're planning to start your own private Q network, and want to include other
server operators in this network, then you'll have to export your server's noderef
and make it available to the others you want to invite into your network.<br>
<br>
The command to export your Q Server noderef is:
<blockquote><code><pre>
qmgr server getref &lt;noderef-file&gt;</pre></code></blockquote>
This will extract the <i>I2P Destination</i> of your running server node, and
write it into &lt;noderef-file&gt;. You can then privately share this file with
others who you want to invite into your private network. Each recipient of
this file will do a <b>qmgr server addref &lt;noderef-file&gt;</b> command
to import your ref into their servers.<br>
<br>
Don't forget that if you're running, or participating in, a private Q network, then
you'll need to run a separate client for accessing this network, separate from any
mainstream Q network client you may already be running.<br>
<br>
To start this extra client, you'll have to choose a directory where you want this
client to reside, a port number you want your client to listen on locally for
user commands, and run the command:
<blockquote><code><pre>
qmgr -dir /path/to/my/new/client -port &lt;portnum&gt; start</pre></code></blockquote>
You need the <b>-port &lt;portnum&gt;</b> command, because otherwise it'll fail
to launch (if you already have a client node running off the mainstream Q network).<br>
<br>
This will create, and launch, a new instance of a Q client, accessing your private
Q network. Don't forget to import your server's noderef into this client. Also,
note that you'll have to use this same <b>-port &lt;portnum&gt;</b> argument when
doing any operation on this client instance, such as get, put, status, search.
</blockquote>
<a name="qmgr"/>
<hr>
<h2>6. About the qmgr Utility</h2>
qmgr (or, to people fluent in Java, <b>net.i2p.aum.q.QMgr</b>), is just one simple
Q client application, that happens to be bundled in with the Q distro.<br>
<br>
It is by no means the only, or even main facility for accessing the Q network. We
anticipate that folks will write all manner of client apps, including fancy GUI
apps.<br>
<br>
Anyway, qmgr does give you a rudimentary yet workable client for basic access
to the Q network. Until fancy apps get written, qmgr will have to do.<br>
<br>
Don't forget that qmgr has very detailed inbuilt help. Run:
<blockquote><code><pre>
qmgr help</pre></code></blockquote>
for a quick help summary, or:
<blockquote><code><pre>
qmgr help verbose</pre></code></blockquote>
for the 'War and Peace' treatise.<br>
<br>
<blockquote><hr>
One crucial concept to remember with qmgr is that client and server node instances
are uniquely identified by the directories at which they reside. If you are running
multiple server and/or client instances, you can specify an instance with the
<b>-dir &lt;dirpath&gt;</b> option - see the help for details.
<hr></blockquote>
<hr>
One last note - we strongly discourage any writing of client apps that spawn a qmgr
process, pass it arguments and parse its results. This is most definitely a path to
pain, since qmgr's shell interface is subject to radical change at any time without
notice.<br>
<br>
qmgr is for human usage, or at most, inclusion in init/at/cron scripts. Please respect
this.<br>
<br>
If you want to write higher-level clients, your best course of action is to use the
official client api library, which we anticipate will have versions available in
Java, Python, Perl and C++. If you want to write in another language, such as
OCaml, Scheme etc, then the existing api lib implementations should serve as an excellent
reference to support you in writing a native port for your own language.
<a name="contact"/>
<hr>
<h2>8. Contacting the Author</h2>
I am <b>aum</b>, and can be reached as <b>aum</b> on in-I2P IRC networks, and also
at the in-I2P email address of <b>aum@mail.i2p</b>.<br>
<br>
<hr>
<center>
Return to <a href="../index.html">Q Homepage</a><br>
<br>
<small>
<a href="#intro">Introduction</a> |
<a href="#checklist">Checklist</a> |
<a href="#serverorclient">Server?orClient?</a> |
<a href="#walkthrough">Walkthrough</a> |
<a href="#server">Server Nodes</a> |
<a href="#qmgr">About QMgr</a> |
<a href="#contact">Contact us</a>
</small>
</center>
<hr>
<!-- Created: Fri Apr 1 11:03:27 NZST 2005 -->
<!-- hhmts start -->
Last modified: Sun Apr 3 20:06:53 NZST 2005
<!-- hhmts end -->
</body>
</html>

23
apps/q/doc/manual/notes Normal file
View File

@ -0,0 +1,23 @@
rise on each hit:
dy = (1 - y) / kRise
fall after each time unit:
dy = y / kFall
fall after time dt:
dy = - y ** - (dt / kFall)
after the next hit:
y = y - y ** (- dt / kFall) + (1 - y) / kRise
first attempt at a load measurement algorithm:
- kFall is an arbitrary constant which dictates decay rate of load
in the absence of hits
- kRise is another constant which dictates rise of load with each hit
- dt is the time between each hit

372
apps/q/doc/metadata.html Normal file
View File

@ -0,0 +1,372 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Q Metadata Specification</title>
<style type="text/css">
<!--
td { vertical-align: top; }
code { font-family: courier, monospace; font-weight: bolder; font-size:smaller }
-->
</style>
</head>
<body>
<h1>Q Metadata Specification</h1>
<h2>1. Introduction</h2>
This document lists the standard metadata keys for Q data items,
discussing the rules of metadata insertion, processing and validation.<br>
<hr>
<h3>1.1. Definitions</h3>
To avoid confusions in terminology, this document will strictly abide the following definitions:
<br>
<br>
<table width=80% cellspacing=0 cellpadding=4 border=1 align=center>
<tr style="font-weight: bold">
<td>Term</td>
<td>Definition</td>
</tr>
<tr>
<td><code>key</code></td>
<td>A metadata category name, technically a <code>key</code> as the word is used with
Java <code>Hashtable</code> and Python <code>dict</code> objects.</td>
</tr>
<tr>
<td><code>uri</code></td>
<td>A Uniform Resource Indicator for an item of content stored within the Q network.<br>
Q URIs have the form: <code>Q:&lt;basename&gt;[,&lt;cryptoKey&gt;][&lt;path&gt;]</code>
<br>
<br>
Some examples:
<ul>
<li><code>Q:fhvnr3HFSK234khsf90sdh42fsh</code> (a plain hash uri, no cryptoKey)</li>
<li><code>Q:e54fhjeo39schr2kcy4osEH478D/files/johnny.mp3</code> (a secure space URI,
no cryptoKey)</li>
<li><code>Q:vhfh4se987WwfkhwWFEwkh3234S,47fhh2dkhseiyu</code> (a plain hash URI, with
a cryptoKey)</li>
</td>
</tr>
<tr>
<td><code>basename</code></td>
<td>The basic element of a Q uri. This will be a base64-encoded hash - refer below to
URI calculation procedures</td>
</tr>
<tr>
<td><code>cryptoKey</code></td>
<td>An optional session encryption key for the stored data, encoded as base64.
This affords some protection to server node operators, and gives them a level
of plausible deniability for whatever gets stored in their server's
datastore without their direct human awareness.</td>
</tr>
<tr>
<td><code>path</code></td>
<td>Whever an item of content is inserted in <code>secure space</code> mode, this path
serves as a pseudo-pathname, and is conceptually similar to the <code>path</code>
component in (for example) standard HTTP URLs
<code>http://&lt;domainname&gt;[:&lt;port&gt;][&lt;path&gt;]</code>, such as
<code>http://slashdot.org/faq/editorial.shtml</code> (whose <code>path</code>
is <code>/faq/editorial.shtml</code>).<br>
<br>
Paths, if not empty, should contain a leading slash ("/").
If an application specifies a non-empty <code>path</code> that doesn't begin with a
leading '/', a '/' will be automatically prepended by the receiving node.
</td>
</tr>
<tr>
<td><code>plain hash</code></td>
<td>A mode of inserting items, whereby the security of the resulting URI comes from
computing the URI from a hash of the item's data and metadata (and imposing a
mathematical barrier against spoofing content under a given URI). Corresponds to
Freenet's <code>CHK@</code> keys.</td>
</tr>
<tr>
<td><code>secure space</code></td>
<td>A mode of inserting items where the security of the URI is based not on a hash of the
item's data and metadata (as with <code>plain hash</code> mode),
but on the <code>privateKey</code> provided by the
application, and a content signature created from that private key.
Corresponds to Freenet's <code>SSK@</code> keys. Within a secure space, you
can insert any number of items under different pseudo-pathnames (as is the case
with Freenet SSK keys).
</li>
</table>
<br><br>
<hr>
<h3>2.1. Keys Inserted By Application Before sending <code>putItem</code> RPCs</h3>
As the heading suggests, this is a list of metadata keys which should be inserted by a
Q application prior to invoking a <code>putItem</code> RPC on the local Q client node.<br>
<br>
<table width=80% cellspacing=0 cellpadding=4 border=1 align=center>
<tr style="font-weight: bold">
<td>Key</td>
<td>Data Type</td>
<td>Description</td>
</tr>
<tr>
<td><code>title</code></td>
<td>String</td>
<td>Optional but strongly recommended. A free-text short description of the item,
should be less than 80 characters. The idea is that applications should
support a 'view' of catalogue data that shows item titles. (Prior Q convention of
titles expressed as valid filename syntax has been abandoned).
</td>
</tr>
<tr>
<td><code>path</code></td>
<td>String</td>
<td>Optional but strongly recommended.
A virtual 'pathname' for the item, which should be in valid *nix
absolute pathname syntax (beginning with '/', containing no '//', consisting
only of alphanumerics, '-', '_', '.' and '/'.<br>
<br>
In Q web interfaces, the <code>filename</code> component of this path will
serve as the recommended filename when downloading/saving the item.<br>
<br>
If the application also provides a
<code>privateKey</code> key, the path
is used in conjunction with the private key to generate <code>publicKey</code>
and <code>signature</code> keys (see below), and ultimately the final <code>uri</code>
under which the item can be retrieved by others.<br>
<br>
Refer also to <code>mimetype</code> below.
</td>
</tr>
<tr>
<td><code>encrypt</code></td>
<td>String</td>
<td>Optional. If this key is present, and has a value "1", "yes" or "true",
this indicates that the application wishes the data to be stored on servers in
encrypted form.<br>
<br>
If this key is present and set to a positive value, the Q node, on receiving the
<code>putItem</code> RPC, will:
<ol>
<li>Generate a random symmetric encryption key</li>
<li>Encrypt the item's data using this encryption key</li>
<li>Delete the <code>encrypt</code> key from the metadata</li>
<li>Enclose a base64 representation of this encryption key in the RPC response
it sends back to the application (embedded in the <code>uri</code></li>
</ol>
</td>
</tr>
<tr>
<td><code>type</code></td>
<td>String</td>
<td>Optional but strongly recommended. A standard ed2k specifier, one of <code>text html image
audio video archive other</code></td>
</tr>
<tr>
<td><code>mimetype</code></td>
<td>String</td>
<td>Optional but moderately recommended. Mimetype designation of data, eg <code>text/html</code>,
<code>image/jpeg</code> etc. If not specified, an attempt will be made to guess
a mometype from the value of the <code>path</code> key. If this attempt fails, then
this key will be set to <code>application/x-octet-stream</code> by the node receiving
the <code>putItem</code> RPC.</td>
</tr>
<tr>
<td><code>keywords</code></td>
<td>String</td>
<td>Optional but moderately recommended.
A set of keywords, under which the inserting app would like this item to be
discoverable. Keywords should be entirely lower case and comma-separated. Content
inserts should consider keywords carefully, and only use space characters inside
keywords when necessary (eg, for flagging a distinctive phrase containing very
common words).</td>
<tr>
<td><code>privateKey</code></td>
<td>String</td>
<td>Optional. A Base64-encoded signing private key, in cases where the application wishes
to insert an item in <code>signed space</code> mode. This can be accompanied by another key,
<code>path</code>, indicating a 'path' within the signed space. If 'path'
is not given, it will default to '/'.<br>
<br>
Either way, when a node receives a
<code>putItem</code> RPC containing a <code>privateKey</code> in its metadata,
it removes this key and replaces it with <code>publicKey</code> and
<code>signature</code>.
</td>
</tr>
<tr>
<td><code>path</code></td>
<td>String</td>
<td>Optional. The virtual pathname, within signed space, under which to store the item.
This gets ignored and deleted unless the application also provides a
<code>privateKey</code> as well. But if the private key is given, the path
is used in conjunction with the private key to generate <code>publicKey</code>
and <code>signature</code> keys (see below).<br>
<code>path</code> should be a 'unix-style pathname', ie, containing only slashes
as (pseudo) directory delimiters, and alphanumeric, '-', '_' and '.' characters,
and preferably ending in a meaningful file extension such as <code>.html</code>
</td>
</tr>
<tr>
<td><code>expiry</code></td>
<td>int</td>
<td>Unixtime at which the inserted item should expire. When this expiry time
is reached, the item won't necessarily be deleted straight away, but may
be deleted whenever a node's data store is full.<br>
<br>
If this is not provided, it will default to a given duration according to
the client node's configuration.<br>
<br>
If it is provided, by an application, then the client node will transparently
generate the required 'rent payment' before caching the data item and uploading
it to servers.
</td>
</tr>
</table>
<br><br>
<hr>
<h3>2.2. Keys Inserted By Node Upon Receipt Of <code>putItem</code> RPC</h3>
<table width=80% cellspacing=0 cellpadding=4 border=1 align=center>
<tr style="font-weight: bold">
<td>Key</td>
<td>Data Type</td>
<td>Description</td>
</tr>
<tr>
<td><code>size</code></td>
<td>Integer</td>
<td>Size of the data to be inserted, in bytes.</td>
</tr>
<tr>
<td><code>dataHash</code></td>
<td>String</td>
<td>base64-encoded SHA256 hash of data.</td>
</tr>
<tr>
<td><code>uri</code></td>
<td>String</td>
<td>This depends on whether the item is being inserted in <i>plain</i> or
<i>signed space</i> mode.<br>
<br>
If inserting in <i>plain</i> mode, then the uri is in the form
<code>Q:somebase64hash</code>, where the hash is computed according to
the <a href="#plainhash">plain hash calculation procedure</a>.<br>
<br>
If inserting in <i>signed space</i> mode, then the uri will be in the form
<code>Q:somebase64hash/path.ext</code>, where the hash is computed as per
the <a href="#signedhash">signed space hash calculation procedure</a>, and
the <code>/path.ext</code> is the verbatim value of the app-supplied
<code>path</code> key.
</td>
</tr>
<tr>
<td><code>publicKey</code></td>
<td>String</td>
<td>Base64-encoded signing public key. In cases where app provides
<code>privateKey</code>,
a node will derive the signing public key from the private key,
delete the private key from the metadata, and replace it with its corresponding
public key
key.</td>
</tr>
<tr>
<td><code>signature</code></td>
<td>String</td>
<td>Base64-encoded signature of <code>path+dataHash</code>, created using
the app-provided <code>privateKey</code>.</td>
</tr>
<tr>
<td><code>rent</code></td>
<td>String</td>
<td>A rent payment for the data's accommodation on the server.<br>
Intention is to support a variety of payment tokens. Initially, the
only acceptable form of payment will be a hashcash-like token,
in the form <code>hashcash:base64string</code>. The <code>hashcash:</code>
prefix indicates that this payment is in hashcash currency, in which case
the <code>base64String</code> should decode to a 16-byte string whose
SHA256 hash partially collides with <code>dataHash</code>.
The greater the number of bits in the collision,
the longer the data's accommodation will be 'paid up for'.<br>
<br>
If this key is already present, a Q node will verify the hashcash,
and adjust the <code>expiry</code> key value to the time the item's accommodation
is paid up till.<br>
<br>
If the key is not present:
<ul>
<li>A client node will generate a value for this key with enough collision bits
to pay the accommodation up till the given app-specified <code>expiry</code> date.</li>
<li>A server node will grant temporary free accommodation, and adjust the <code>expiry</code>
key to the end of the free accommodation period.</li>
</ul>
</td>
</tr>
</table>
<br><br>
<a name="plainhash"/>
<hr>
<h2>3. URI Determination Procedures</h2>
<h3>3.1. Plain Hash URI Calculation Procedure</h3>
When items are inserted in <code>plain</code> mode, the final URI is determined from
a hash of the data and metadata. Security of the item is based on the mathematical difficulty
of creating an arbitrary data+metadata set whose hash collides with the target URI.<br>
<br>
Specifically, the recipe for calculating plain hash URIs is:
<ol>
<li>If the key <code>size</code> is missing, set this to the size of the data,
in bytes</li>
<li>If the key <code>dataHash</code> is missing, set this to the base64-encoded
SHA256(data)</li>
<li>If the key <code>title</code> is missing, set this to the value of <code>dataHash</code></li>
<li>From the metadata, create a set of strings, each in the form <code>key=value</code>,
where each line contains a metadata <code>key</code> and its <code>value</code>, and
is terminated by an ASCII linefeed (\n, 0x10).</li>
<li>Ensure that key <code>uri</code> is omitted</li>
<li>Sort the strings into ascending ASCII sort order</li>
<li>Concatenate the strings together into one big string</li>
<li>Calculate the SHA256 hash of this string</li>
<li>Encode the hash into Base64</li>
<li>Prepend the string <code>Q:</code> to this</li>
</ol>
<a name="signedhash"/>
<hr>
<h3>3.2. Signed Space URI Calculation Procedure</h3>
This is much simpler than determining plain hash URI, since the security of the URI
is based not on hashes of data and metadata, but on the cryptographic <code>privateKey</code>
given by the application.<br>
<br>
Calculation recipe for Signed Space URIs is:
<ol>
<li>Calculate the SHA256 hash of the private key's binary data (not its base64 representation)</li>
<li>Encode this hash into base64, dropping any trailing '=' characters</li>
<li>Append to this the value of metadata item <code>path</code> (recall that <code>path</code>,
if not empty, must begin with a '/')</li>
<li>Prepend the string <code>Q:</code> to this</li>
</ol>
The resulting URI then is in the form <code>Q:pubkeyHash/path.ext</code>
<hr>
<!-- Created: Tue Apr 5 00:56:45 NZST 2005 -->
<!-- hhmts start -->
Last modified: Wed Apr 6 00:36:37 NZST 2005
<!-- hhmts end -->
</body>
</html>

BIN
apps/q/doc/overall.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

1
apps/q/doc/qnoderefs.txt Normal file
View File

@ -0,0 +1 @@
rxvXpHKfWGWsql4PJaHglAERSUYyrdKKAzK6jPHT4QXRf9jgcVd4mInq0j6H4inVOzT9dG4L6c9GrlQwe4ysUm5jSTyZemxiZpQDCAazsoRzNDv6gevA40J6uGl10JtVtOjqXW8Ej0JUKubz88g~ogPb1h4Xibc-RrtqrvsJebg5xYFkLlnr7DxDtiWzIMRSZ9Ri2P~eq0SwZzd81tvASPj5fb3nySHeABAuY8HrNu0gqRLjeayDpd3OK1ogrxf1lMvfutn5pnLrlVcvKHa~6rNWWGSulsuEYWtpUd4Itj9aKqIgF9ES7RF77Z73W1f6NRTHO48ZLyLLaKVLjDIsHQP-0mOevszcPjFWtheqRKvT2D28WEMpVC-mPtfw91BkdgBa3pwWhwG~7KIhvWhGs8bj2NOKkqrwYU7xhNVaHdDDkzv4gsweCutHNiiCF~4yL54WzCIfSKDjcHjQxxVkh2NKeaItzgw9E~mPAKNZD22X~2oAuuL9i~0lldEV1ddUAAAA

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
apps/q/doc/screenshot.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
apps/q/doc/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@ -0,0 +1,23 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Q Screenshots</title>
</head>
<body>
<h1>Q Screenshots</h1>
<ul>
<li><a href="screenshot-search.jpg">Search Screen</li>
<li><a href="screenshot-qsite.jpg">QSite Insertion Form</li>
<li><a href="screenshot-iewarn.jpg">Q Security Features</li>
</ul>
<hr>
<address><a href="mailto:aum@mail.i2p">aum</a></address>
<!-- Created: Sat Apr 16 17:24:02 NZST 2005 -->
<!-- hhmts start -->
Last modified: Mon Apr 18 14:06:02 NZST 2005
<!-- hhmts end -->
</body>
</html>

1460
apps/q/doc/spec/index.html Normal file

File diff suppressed because it is too large Load Diff

90
apps/q/java/build.xml Normal file
View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="all" name="aum">
<!-- Written to assume that classpath is rooted in the current directory. -->
<!-- So this should be OK if you make this script in the root of a filesystem. -->
<!-- If not, just change src.dir to be the root of your sources' package tree -->
<!-- and use e.g. View over a Filesystem to mount that subdirectory with all capabilities. -->
<!-- The idea is that both Ant and NetBeans have to know what the package root is -->
<!-- for the classes in your application. -->
<!-- Don't worry if you don't know the Ant syntax completely or need help on some tasks! -->
<!-- The standard Ant documentation can be downloaded from AutoUpdate and -->
<!-- and then you can access the Ant manual in the online help. -->
<target name="init">
<property location="build" name="classes.dir"/>
<property location="src" name="src.dir"/>
<property location="doc/q/api" name="javadoc.dir"/>
<property name="project.name" value="${ant.project.name}"/>
<property location="${project.name}.jar" name="jar"/>
<property location="q.war" name="war"/>
</target>
<target name="builddep">
<ant dir="../../i2ptunnel/java/" target="build" />
<!-- i2ptunnel builds ministreaming and core -->
</target>
<target depends="init,builddep" name="compile">
<!-- Both srcdir and destdir should be package roots. -->
<mkdir dir="${classes.dir}"/>
<javac debug="true"
deprecation="true"
destdir="${classes.dir}"
srcdir="${src.dir}"
classpath="xmlrpc.jar:../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar:../../i2ptunnel/java/build/i2ptunnel.jar" >
<!-- To add something to the classpath: -->
<!-- <classpath><pathelement location="${mylib}"/></classpath> -->
<!-- To exclude some files: -->
<!-- <exclude name="com/foo/SomeFile.java"/><exclude name="com/foo/somepackage/"/> -->
</javac>
</target>
<target depends="init,compile" name="jar">
<!-- To make a standalone app, insert into <jar>: -->
<!-- <manifest><attribute name="Main-Class" value="com.foo.Main"/></manifest> -->
<!-- <jar basedir="${classes.dir}" compress="true" jarfile="${jar}"> -->
<jar compress="true" jarfile="${jar}">
<!-- <jar basedir="." compress="true" jarfile="${jar}" includes="**/*.class,doc/**/*.html"> -->
<fileset dir="${classes.dir}"/>
<fileset dir="." includes="qresources/**"/>
<manifest>
<attribute name="Main-Class" value="net.i2p.aum.q.QMgr"/>
<attribute name="Class-Path" value="i2p.jar xmlrpc.jar mstreaming.jar streaming.jar jbigi.jar"/>
</manifest>
</jar>
</target>
<target depends="init,compile" name="war">
<!-- To make a standalone app, insert into <jar>: -->
<!-- <manifest><attribute name="Main-Class" value="com.foo.Main"/></manifest> -->
<war compress="true" jarfile="${war}" webxml="web.xml">
<!-- <fileset file="build/net/i2p/aum/q/QConsole.class"/> -->
<classes dir="build" includes="**/QConsole.class"/>
<classes dir="build" includes="**/HTML/**"/>
<!-- <fileset includes="**/HTML/*.class"/> -->
<lib file="xmlrpc.jar"/>
</war>
</target>
<target depends="init,jar,war" description="Build everything." name="all"/>
<target depends="init" description="Javadoc for my API." name="javadoc">
<mkdir dir="${javadoc.dir}"/>
<javadoc destdir="${javadoc.dir}" packagenames="*">
<sourcepath>
<pathelement location="${src.dir}"/>
</sourcepath>
<sourcepath>
<pathelement location="/java/xmlrpc-1.2-b1/src/java"/>
</sourcepath>
</javadoc>
</target>
<target depends="init" description="Clean all build products." name="clean">
<delete dir="${classes.dir}"/>
<delete dir="${javadoc.dir}"/>
<delete file="${jar}"/>
</target>
</project>

View File

@ -0,0 +1,10 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading">Item Not Found</td>
</tr>
<tr>
<td align=center>Failed to retrieve item:<br>
<code><tmpl_var 404_uri></code>
</td>
</tr>
</table>

View File

@ -0,0 +1,27 @@
<TABLE align="center" class="mainpaneitem">
<TR>
<TD class="formHeading">About Q</TD>
</tr>
<tr class="formBody">
<TD align=center>
<p>Version
<tmpl_if version>
<tmpl_var version></p>
<tmpl_else>
0.0.1
</tmpl_if>
<p>Designed and engineered by <a href="mailto:aum@mail.i2p">aum</a></p>
<p>Copyright &copy; 2005 by aum.
<br>Released under the
<br>GNU Lesser General Public License</p>
<p style="font-size: smaller; font-style: italic">
Many thanks to jrandom, smeghead and frosk<br>
for their patient and knowledgeable support<br>
in helping this python programmer<br>
get partly proficient in java.</p>
</TD>
</TR>
</TABLE>

View File

@ -0,0 +1,40 @@
<table align="center" class="mainpaneitem" width=80%>
<tr>
<td class="formHeading">Add Hub Noderef</td>
</tr>
<form action="/tools" method="POST" class="formBody">
<input type="hidden" name="cmd" value="addref">
<tr>
<td align="center">
<table width=70%>
<tr>
<td>
In order for your node to join a Q network, it must have a ref to at
least one of the running Q Hubs. You need to get a Q Hub noderef and
paste it here.
</td>
</tr>
<tr>
<td align=center>
<textarea name="noderef" cols=40 rows=6></textarea>
</td>
</tr>
<tr>
<td align="center">
<input type="submit" name="submit" value="Add Hub Noderef!">
</td>
</tr>
</table>
</td>
</tr>
</form>
</table>

View File

@ -0,0 +1,29 @@
<table align="center" class="mainpaneitem" width=80%>
<tr>
<td class="formHeading" colspan=2>Critical Anonymity Alert!</td>
</tr>
<tr>
<td>
<table cellspacing=0 cellpadding=5 align=center border=1>
<tr>
<td>
<p>You are attempting to view a QSite via the Internet Explorer web browser.
</p>
<p>We have blocked the connection to protect your anonymity.
</p>
<p>As a matter of policy, Q does not support QSite browsing via Microsoft
Internet Explorer (MSIE), because of that browser's abysmal track record with
regard to security. If we did allow you to browse Q via MSIE, it would
be easy for a malicious QSite author to embed hostile content which
undermines your computer's security and compromises your anonymity.
</p>
<p>If you want to surf I2P QSites, you'll need to use a more secure web
browser such as <a href="http://www.mozilla.org">Mozilla or Firefox</a>.
</td>
</tr>
</table>
</td>
</tr>
</table>

View File

@ -0,0 +1,52 @@
<table align="center" class="mainpaneitem" width=80%>
<tr>
<td class="formHeading" colspan=2>Critical Anonymity Alert!</td>
</tr>
<tr>
<td>
<table cellspacing=0 cellpadding=5 align=center border=1>
<tr>
<td>
<p>You are attempting to view a QSite via an unprotected link.
</p>
<p>We have blocked the connection to protect your anonymity. If we don't
do this, then a malicious QSite author can insert content into the QSite
which triggers oubound hits to arbitrary servers on the mainstream web,
which in turn can easily reveal a lot of personal information about you
and the QSite you are accessing.
</p>
<p>If you want to browse QSites with your web browser with greater safety,
you'll have to follow these simple steps:
</p>
<ol>
<li>Edit your I2P <code>hosts.txt</code> file and add the following entry
(all on one line):
<blockquote><code><textarea rows=5 cols=50>q.i2p=<tmpl_var dest></textarea></code><blockquote>
(and make sure you do NOT reveal this entry to anyone else)
</li>
<li>Configure one of your web browsers to use the proxy server:
<blockquote><code>localhost:4444</code></blockquote>
(or whatever address you have configured your I2P EEProxy to listen on,
if you've changed it)<br><br>
</li>
<li>Start up the browser you have just configured, and enter the web address:
<blockquote><code>http://q.i2p</code></blockquote>
</li>
</ol>
<p>Even if you do this, you still won't have a 100% guarantee of anonymity, since
a malicious QSite author can send code to your browser which exploits a vulnerability
in the browser to compromise your anonymity (eg, accessing third party cookies, executing
arbitrary code, accessing your local filesystem, uploading compromising information
about you to hostile I2P EEPsites etc). But you can relax (somewhat) in the knowledge
that such attacks are much more difficult, particularly if you use a decent web browser.
</p>
<p>Thank you for your co-operation. We wish you happy and safe browsing.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>

View File

@ -0,0 +1,9 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading">Q Downloads</td>
</tr>
<tr>
<td>Not implemented yet</td>
</tr>
</table>

View File

@ -0,0 +1,28 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading">Generate New Keypair</td>
</tr>
<tr>
<td align="center">
<table>
<tr>
<td>
Click the button to generate a new keypair for
future inserts of signed space keys.
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center">
<form action="/tools" method="POST" class="formBody">
<input type="hidden" name="cmd" value="genkeys">
<input type="submit" name="submit" value="Generate Keys!">
</form>
</td>
</tr>
</table>

View File

@ -0,0 +1,32 @@
<table align="center" class="mainpaneitem">
<tr>
<td colspan=2 class="formHeading">Your New Keys</td>
</tr>
<tr>
<td align="center" colspan=2>
<table>
<tr>
<td>
Please save these keys in a safe place,
preferably on an encrypted partition:
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>Public Key: </td>
<td>
<input readonly type="text" size=32 style="font-family: courier, monospace" value="<tmpl_var publickey>">
</td>
</tr>
<tr>
<td>Private Key: </td>
<td><input readonly type="text" size=32 style="font-family: courier, monospace" value="<tmpl_var privatekey>"></td>
</tr>
</table>

View File

@ -0,0 +1,15 @@
<table align="center" class="mainpaneitem" border=0>
<TR>
<TD class="formHeading" colspan="3">Retrieve Content</TD>
</tr>
<tr class="formBody">
<form action="/home" method="POST">
<input type="hidden" name="cmd" value="get">
<TD>URI:</TD>
<td><INPUT type="text" name="uri" value="Q:" size="60" maxlength="128"></td>
<td><INPUT type="submit" name="submit" value="Retrieve it"></td>
</form>
</TR>
</table>

View File

@ -0,0 +1,11 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading">Q Help</td>
</tr>
<tr>
<td>
Not written yet
</td>
</tr>
</table>

View File

@ -0,0 +1,34 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading" colspan=2>Current Background Jobs</td>
</tr>
<tr>
<td>
<tmpl_if items>
<table cellspacing=0 cellpadding=5 align=center border=1>
<tr>
<td><b>Secs From Now</b></td>
<td><b>Description</b></td>
</tr>
<tmpl_loop items>
<tr>
<td><tmpl_var future></td>
<td><tmpl_var description></td>
</tr>
</tmpl_loop>
</table>
<tmpl_else>
<tr><td colspan=2 align=center><i>(No current jobs)</i></td></tr>
</tmpl_if>
</td>
</tr>
</table>

View File

@ -0,0 +1,122 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<TITLE>Q Web Interface</TITLE>
<style type="text/css">
<!--
body { font-family: helvetica, arial, sans-serif; background-color: #000080 }
td { }
code { font-family: courier, monospace; font-weight: bolder; font-size:smaller }
.logocell {
border-color: #ffe000;
border-width: 1px;
border-top-style:none;
border-bottom-style:solid;
border-left-style:none;
border-right-style:none;
text-align: right;
font-weight: bold;
color: #f0f0ff;
}
.tabcell {
font-weight: bold;
background-color: #fffff0;
padding:5px;
border-color:#ffe000;
border-width: 1px;
border-top-style:solid;
border-bottom-style:none;
border-left-style:solid;
border-right-style:solid;
}
.tabcell_inactive {
background-color: #f0f0ff;
padding:5px;
border-color:#ffe000;
border-width: 1px;
border-top-style:solid;
border-bottom-style:solid;
border-left-style:solid;
border-right-style:solid;
}
.tablink {
font-size: smaller;
text-decoration: none;
}
.mainpane { border-color:#ffe000;
border-width: 1px;
border-top-style:none;
border-bottom-style:solid;
border-left-style:solid;
border-right-style:solid;
}
.mainpaneitem1 { border-style:solid; border-color:#0000ff; border-width: thin; }
.mainpaneitem { border-style:solid;
border-color:#0000ff;
border-width: thin;
background-color: #f0f0ff;
}
.formHeading { font-size:larger; font-weight: bolder; text-align:center; }
.btn1 { font-weight: bold;
}
input1 { background-color: #fffff0;
color: #000080;
}
-->
</style>
</head>
<body bgcolor="#ffffff">
<TABLE width="99%" height=99% align="center" cellspacing="0" cellpadding="0" border=0>
<TR>
<TD>
<table width="100%" cellspacing=0 cellpadding=0>
<tr>
<td>
<table align=right cellspacing=0 cellpadding=0 border=0>
<tr>
<tmpl_loop tabs>
<td class=<tmpl_if active>"tabcell"<tmpl_else>"tabcell_inactive"</tmpl_if>>
<a class="tablink" href="/<tmpl_var name>"><tmpl_var label></a>
</td>
</tmpl_loop>
<tmpl_if _ignore>
<TD class="tabcell_inactive">
<a class="tablink" href="/status">Status</a>
</TD>
<TD class="tabcell">
<a class="tablink" href="/settings">Settings</a>
</TD>
</tmpl_if>
</tr>
</table>
</td>
<TD width=100% class="logocell">
Q <tmpl_var nodeType> Node
</TD>
</TR>
</table>
</TD> </TR>
<tr height=99% bgcolor="#fffff0">
<TD class="mainpane">
<br>
<center>
<tmpl_loop items>
<br>
<tmpl_var item>
<br>
</tmpl_loop>
</center>
</TD>
</tr>
</TABLE>
</body>
</html>

View File

@ -0,0 +1,29 @@
<table align="center" class="mainpaneitem" width=80%>
<tr>
<td class="formHeading" colspan=2>Critical Anonymity Alert!</td>
</tr>
<tr>
<td>
<table cellspacing=0 cellpadding=5 align=center border=1>
<tr>
<td>
<p>You are attempting to view a QSite via the Internet Explorer web browser.
</p>
<p>We have blocked the connection to protect your anonymity.
</p>
<p>As a matter of policy, Q does not support QSite browsing via Microsoft
Internet Explorer (MSIE), because of that browser's abysmal track record with
regard to security. If we did allow you to browse Q via MSIE, it would
be easy for a malicious QSite author to embed hostile content which
undermines your computer's security and compromises your anonymity.
</p>
<p>If you want to surf I2P QSites, you'll need to use a more secure web
browser such as <a href="http://www.mozilla.org">Mozilla or Firefox</a>.
</td>
</tr>
</table>
</td>
</tr>
</table>

View File

@ -0,0 +1,10 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading">Error Inserting <tmpl_if is_site>QSite<tmpl_else>Item</tmpl_if></td>
</tr>
<tr>
<td align=center>Your insert failed because:<br>
<code><tmpl_var error></code>
</td>
</tr>
</table>

View File

@ -0,0 +1,139 @@
<TABLE align="center" class="mainpaneitem" width=90% >
<TR>
<TD class="formHeading" colspan="2">Insert Content</TD>
</tr>
<TR>
<TD colspan="2" align=center><a href="/insert?mode=site">Insert A QSite Instead</a></TD>
</tr>
<tr><td colspan=2><hr></td></tr>
<form method="POST" ENCTYPE="multipart/form-data" action="/insert" class="formBody">
<!-- <form method="POST" action="/insert" class="formBody"> -->
<input type=hidden name="cmd" value="put">
<tr>
<td valign=top>Type: </td>
<td style="font-size:smaller">
<input type="radio" name="type" value="text">Text
<input type="radio" name="type" value="html">HTML
<input type="radio" name="type" value="image">Image
<input type="radio" name="type" value="audio">Audio
<input type="radio" name="type" value="video">Video
<input type="radio" name="type" value="archive">Archive
<input type="radio" name="type" value="other" checked>Other
</td>
</tr>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Title: </TD>
<td>
<input type="text" name="title" size="60" maxlength="58">
<div style="font-style: italic; font-size: smaller">
(A short descriptive title for this item)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Path: </TD>
<td>
<input type="text" name="path" size="60" maxlength="128">
<div style="font-style: italic; font-size: smaller">
(If you're inserting in plain-hash mode, this should be a suggested
filename for people to save the item as when downloading it; If you're
inserting in signed-space mode, this can be a longer pathname)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Mimetype: </TD>
<td>
<input type="text" name="mimetype" size="40" maxlength="40">
<div style="font-style: italic; font-size: smaller">
(Leave blank unless you know exactly what this means)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Keywords: </TD>
<td>
<input type="text" name="keywords" size="60" maxlength="80">
<div style="font-style: italic; font-size: smaller">
(Comma-separated list of short descriptive keywords)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Abstract: </TD>
<td>
<textarea name="abstract" cols="50" rows="2"></textarea>
<div style="font-style: italic; font-size: smaller">
(A short descriptive summary for the item, preferably no
longer than 256 characters)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>File: </TD>
<td>
<INPUT type="file" name="data" size="40" maxlength="128">
<div style="font-style: italic; font-size: smaller">
(Leave this blank if you are entering the raw data in the text box below)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Private Key: </TD>
<td>
<INPUT type="text" name="privkey" size="30" maxlength="60">
<div style="font-size: smaller; font-style: italic">
(Only if inserting in signed space mode)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD colspan=2 align=center>
<INPUT class="btn" type="submit" name="submit" value="Insert it">
</TD>
</tr>
<tr><td colspan=2><hr></td></tr>
<tr>
<td valign=top>Raw Data:</td>
<td>
<div style="font-size: smaller; font-style:italic">
(You may enter the raw data here as text instead of selecting a
file above)
</div>
<textarea name="rawdata" cols=60 rows=16></textarea>
</td>
</tr>
</form>
</TABLE>

View File

@ -0,0 +1,10 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading"><tmpl_if is_site>QSite<tmpl_else>Item</tmpl_if> Inserted Successfully</td>
</tr>
<tr>
<td align=center>Item URI:<br>
<code><input type="text" size=60 readonly value="<tmpl_var uri>"></code>
</td>
</tr>
</table>

View File

@ -0,0 +1,101 @@
<table align="center" class="mainpaneitem" width=90% >
<TR>
<TD class="formHeading" colspan="2">Insert A QSite</TD>
</tr>
<TR>
<TD colspan="2" align=center><a href="/insert?mode=file">Insert A Single File Instead</a></TD>
</tr>
<tr><td colspan=2><hr></td></tr>
<form method="POST" action="/insert" class="formBody">
<!-- <form method="POST" action="/insert" class="formBody"> -->
<input type=hidden name="cmd" value="putsite">
<input type=hidden name="type" value="qsite">
<tr>
<TD valign=top>Name: </TD>
<td>
<input type="text" name="name" size="32" maxlength="50">
<div style="font-style: italic; font-size: smaller">
(A short name for the QSite - should contain only alphanumerics, '-', and '_';
should definitely <i>not</i> contain '/', ':', '\', ' ' etc)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Private Key: </TD>
<td>
<INPUT type="text" name="privkey" size="30" maxlength="60">
<div style="font-size: smaller; font-style: italic">
(Mandatory - if you don't have a signed-space keypair, get one by
clicking on <b>Tools</b>)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Directory: </TD>
<td>
<INPUT type="text" name="dir" size="60" maxlength="128">
<div style="font-size: smaller; font-style: italic">
(Absolute directory path where the QSite's files reside)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Title: </TD>
<td>
<input type="text" name="title" size="60" maxlength="58">
<div style="font-style: italic; font-size: smaller">
(A short descriptive title for this QSite)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Keywords: </TD>
<td>
<input type="text" name="keywords" size="60" maxlength="80">
<div style="font-style: italic; font-size: smaller">
(Comma-separated list of short descriptive keywords)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD valign=top>Abstract: </TD>
<td>
<textarea name="abstract" cols="50" rows="2"></textarea>
<div style="font-style: italic; font-size: smaller">
(A short descriptive summary for the QSite, preferably no
longer than 256 characters)
</div>
</td>
</TR>
<tr><td colspan=2><hr></td></tr>
<tr>
<TD colspan=2 align=center>
<INPUT class="btn" type="submit" name="submit" value="Insert it">
</TD>
</tr>
</form>
</table>

View File

@ -0,0 +1,76 @@
<TABLE align="center" class="mainpaneitem">
<TR>
<TD class="formHeading" colspan="3">Search For Content</TD>
</tr>
<form method="GET" action="/search" class="formBody">
<input type=hidden name=cmd value="search">
<tr>
<td valign=top>Type: </td>
<td style="font-size:smaller" colspan=2>
<input type="radio" name="type" value="any" checked>ANY
<input type="radio" name="type" value="qsite">QSite
<input type="radio" name="type" value="text">Text
<input type="radio" name="type" value="html">HTML
<input type="radio" name="type" value="image">Image
<br>
<input type="radio" name="type" value="audio">Audio
<input type="radio" name="type" value="video">Video
<input type="radio" name="type" value="archive">Archive
<input type="radio" name="type" value="other">Other
</td>
</tr>
<tr>
<td>Title: </td>
<td><input type="text" name="title" size="60" value="<tmpl_var title>" maxlength="128"></td>
<td><input type="checkbox" name="title_re" <tmpl_if title_re>checked</tmpl_if>>regexp</td>
</tr>
<tr>
<TD>Path: </TD>
<td><INPUT type="text" name="path" size="60" value="<tmpl_var title>" maxlength="128"></td>
<td><input type="checkbox" name="path_re" <tmpl_if path_re>checked</tmpl_if>>regexp</td>
</TR>
<tr>
<TD>Mimetype: </TD>
<td><INPUT type="text" name="mimetype" size="40" maxlength="40" value="<tmpl_var mimetype>"></td>
<td><input type="checkbox" name="mimetype_re" <tmpl_if mimetype_re>checked</tmpl_if>>regexp</td>
</TR>
<tr>
<TD>Keywords: </TD>
<td><INPUT type="text" name="keywords" size="60" maxlength="80" value="<tmpl_var keywords>"></td>
<td><input type="checkbox" name="keywords_re" <tmpl_if keywords_re>checked</tmpl_if>>regexp</td>
</TR>
<tr>
<TD>Summary: </TD>
<td><textarea name="summary" cols="50" rows=2><tmpl_var summary></textarea></td>
<td><input type="checkbox" name="summary_re" <tmpl_if summary_re>checked</tmpl_if>>regexp</td>
</TR>
<tr>
<td>
Search Mode:
</td>
<td>
<input type=radio name="searchmode" value="and" checked>AND
<input type=radio name="searchmode" value="or">OR
</td>
</tr>
<tr><td colspan=3><hr></td></tr>
<tr>
<TD colspan=3 align=center>
<INPUT class="btn" type="submit" name="submit" value="Search!">
</TD>
</tr>
</form>
</TABLE>

View File

@ -0,0 +1,27 @@
<table align="center" class="mainpaneitem" <tmpl_if results>width="95%"</tmpl_if>>
<tr>
<td class="formHeading" colspan=2>Search Results</td>
</tr>
<tr>
<td style="font-style: italic" align="center"><tmpl_var numresults> items found</td>
</tr>
<tr>
<td width="90%">
<table cellspacing=0 cellpadding=5 align=center border=0 width=95%>
<tmpl_loop results>
<tr>
<td>
<div style="font-size: larger"><a href="/<tmpl_var uri>"><tmpl_var title></a></div>
<tmpl_if abstract>
<div style="font-style: italic"><tmpl_var abstract></div>
</tmpl_if>
<div style="font-size: smaller; color: #008000;"><tmpl_var uri></div>
<div style="font-size:smaller;"><tmpl_var type> (<tmpl_var mimetype>) <tmpl_var size> bytes</div>
<div/>
</td>
</tr>
</tmpl_loop>
</table>
</td>
</tr>
</table>

View File

@ -0,0 +1,9 @@
<table align="center" class="mainpaneitem">
<tr>
<td class="formHeading">Q Settings</td>
</tr>
<tr>
<td>Not implemented yet</td>
</tr>
</table>

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