Compare commits

...

98 Commits

Author SHA1 Message Date
jrandom
d70c5df5a0 0.3.1.3 (not backwards compatible, yadda yadda yadda) 2004-05-20 11:32:32 +00:00
jrandom
b2799d198c add (commented out) router.shutdownPassword 2004-05-20 11:27:49 +00:00
jrandom
f2fa2038b1 * made dbStore use a pessimistic algorithm - requiring confirmation of a store, rather than optimistically considering all store messages successful (NOT BACKWARDS COMPATIBLE)
* when allocating tunnels for a client, make sure it has a good amount of time left in it (using default values, this means at least 7.5 minutes)
* allow overriding the profile organizer's thresholds so as to enforce a minimum number of fast and reliable peers, allowing a base level of tunnel diversification.  this is done through the "profileOrganizer.minFastPeers" router.config / context property (default minimum = 4 fast and reliable peers)
* don't be so harsh with the isFailing calculator regarding db lookup responses, since we've decreased the timeout.  however, include "participated in a failed tunnel" as part of the criteria
* more logging than god
* for dropped messages, if it is a DeliveryStatusMessage its not an error, its just lag / congestion (keep the average delay as the new stat "inNetPool.droppedDeliveryStatusDelay")
2004-05-20 11:06:25 +00:00
jrandom
bfd59e64ea refactored the cleanup job
logging
2004-05-20 10:53:31 +00:00
jrandom
60e05e270a cleaned up slice processing
reduced max queued messages per connection to 10 (additional ones are immediately marked as failed)
update the I2P_FLAG byte to '*' making this NOT BACKWARDS COMPATIBLE
formatting
2004-05-20 10:51:22 +00:00
jrandom
242b9a6af9 fix the reread short circuiting algorithm 2004-05-20 10:44:31 +00:00
jrandom
e7e8ad9bdc add the socketErrorListener interface (sorry duck) 2004-05-19 22:30:52 +00:00
jrandom
0e5d164a8a support shutting down the router from the web console:
specify a "router.shutdownPassword" value in the router.config (or in the environment [ala -D]),
  then specify that password on the shutdown form in the web console and hit submit.  after 30 seconds, it'll kill the router (and unless you're running the sim, it'll kill the JVM too, including clientApp.* started tunnels / etc)
if we had some sort of ACL for accessing the web console, we could avoid the password field altogether, but we dont, so we cant.
2004-05-19 22:00:32 +00:00
jrandom
7293a8d3c0 more logging than your mom 2004-05-19 15:24:25 +00:00
jrandom
097a4647a8 handle i2ptunnel server connection .accept()s asynchronously so we don't refuse lots of requests, causing intermittent "failures"
use the new async error listening interface of the ministreaming lib
truckloads of logging
2004-05-19 15:20:55 +00:00
jrandom
0942a7f3ff truckloads of logging
new async interface for error notification (e.g. you can get notified of an error prior to it throwing the IOException).
This async is useful since the IOException can be delayed for up to a minute while waiting for the close packet to be delivered.
The alternative is to fire off a new thread to do the closing, and we may want to go there later, but i'm not sure.
2004-05-19 15:14:30 +00:00
brianr
2df4370477 Some changes to make the SAM module never block if called on a socket
which select() says is safe to read/write or called in any case on a socket
which is O_NONBLOCK

Significant work is still required.
2004-05-19 01:26:02 +00:00
jrandom
7243963106 removed the insane explicit GC 2004-05-18 18:39:43 +00:00
jrandom
9f17654052 tuned to avoid expensive biginteger operation (well, not "expensive", but a lot more expensive than a few shifts)
when we need to debug, essentially assert the validity of the new tuned op, losing the performance benefit by duplicating the effort (to verify)
2004-05-18 18:33:37 +00:00
jrandom
1a65d7061d added morph.i2p 2004-05-17 05:46:06 +00:00
shendaras
292363eb65 imports (sorry, includes alphabetizing, wee)
(shendaras)
2004-05-17 03:38:53 +00:00
jrandom
07e79ce61a * do a db store after a successful db search (healing the netDb)
* timeout each peer in a db search after 10 seconds, not 30
* logging
2004-05-17 00:59:29 +00:00
brianr
1cf7dac82b test 2004-05-16 21:44:23 +00:00
brianr
6003b2902f Preliminary checkin of (mostly useless) Net::SAM perl module.
Net::SAM::StreamSession is currently unimplemented.
Net::Sam::[Datagram|Raw]Session block when they shouldn't and are buggy.
2004-05-16 21:35:46 +00:00
jrandom
ff0023a889 big ol' memory, cpu usage, and shutdown handling update. main changes include:
* rather than have all jobs created hooked into the clock for offset updates, have the jobQueue stay hooked up and update any active jobs accordingly (killing a memory leak of a JobTiming objects - one per job)
* dont go totally insane during shutdown and log like mad (though the clientApp things still log like mad, since they don't know the router is going down)
* adjust memory buffer sizes based on real world values so we don't have to expand/contract a lot
* dont display things that are completely useless (who cares what the first 32 bytes of a public key are?)
* reduce temporary object creation
* use more efficient collections at times
* on shutdown, log some state information (ready/timed jobs, pending messages, etc)
* explicit GC every 10 jobs.  yeah, not efficient, but just for now we'll keep 'er in there
* only reread the router config file if it changes (duh)
2004-05-16 04:54:50 +00:00
jrandom
8c6bf5a1cc added nickster.i2p 2004-05-16 04:42:17 +00:00
jrandom
61c97ab940 0.3.1.2 (backwards compatible, etc) 2004-05-13 23:49:08 +00:00
jrandom
b0a1b3b5ca added some harvest options
dont use javaw, since its a bitch to kill multiple jvms (yeah, this leaves a dos box.  we'll deal until we've got the shutdown admin control)
2004-05-13 23:32:57 +00:00
jrandom
4c7af01edc allow dynamic update to the reliability threshold factor (e.g. rather than the top 2/3rds being considered "reliable", allow that to be the top 1/3, or 1/2, etc)
the router.config var "profileOrganizer.reliabilityThresholdFactor=0.75" (or environment property -DprofileOrganizer.reliabilityThresholdFactor=0.5) etc
2004-05-13 23:24:09 +00:00
jrandom
0d431213cd include the previous period in the measurements (since they're discrete, not rolling)
also include the other elements as necessary by default
2004-05-13 07:14:54 +00:00
jrandom
ad9dd9a2e2 Lots of updates. I'm not calling this 0.3.1.2, still need to
"burn it it" some more, but its looking good.
* test all tunnels we manage every period or two. later we'll want to include some randomization to help fight traffic analysis, but that falls into the i2p 3.0 tunnel chaff / mixing / etc)
* test inbound tunnels correctly (use an outbound tunnel, not direct)
* only give the tunnels 30 seconds to succeed
* mark the tunnel as tested for both the inbound and outbound side and adjust the profiles for all participants accordingly
* keep track of the 'last test time' on a tunnel
* new tunnel test response time profile stat, as well as overall router stat (published in the netDb as "tunnel.testSuccessTime")
* rewrite of the speed calculator - the value it generates now is essentially "how many round trip messages can this router pass in a minute".
  it also allows a few runtime configurable options:
  = speedCalculator.eventThreshold:
    we use the smallest measurement period that has at least this many events in it (10m, 60m, 24h, lifetime)
  = speedCalculator.useInstantaneousRates:
    when we use the estimated round trip time, do we use instantaneous or period averages?
  = speedCalculator.useTunnelTestOnly:
    do we only use the tunnel test time (no db response or tunnel create time, or even estimated round trip times)?
* fix the reliability calculator to use the 10 minute tunnel create successes, not the (almost always 0) 1 minute rate.
* persist the tunnel create response time and send success time correctly (duh)
* add a new main() to PeerProfile - PeerProfile [filename]* will calculate the values of the peer profiles specified.  useful for tweaking the calculator, and/or the configurable options.  ala:
     java -DspeedCalculator.useInstantaneousRates peerProfiles/profile-*.dat
2004-05-13 04:32:26 +00:00
jrandom
c7895ed905 oh, you mean we're supposed to be at least a /little/ resiliant? 2004-05-13 03:54:33 +00:00
jrandom
57d7979d51 removed obsolete code
minor reorganization to help track down whats intermittently b0rking my kaffe instance
logging
2004-05-12 07:55:25 +00:00
jrandom
61f6871cd1 logging and a catastrophic try/catch (no situations have called for this yet, but its worth testing for) 2004-05-12 07:47:22 +00:00
jrandom
406048f7b9 ugly tests to see if the minimal RAW side of SAM works (it does, w00t) 2004-05-11 03:00:53 +00:00
jrandom
6dd5b0fe45 basic datagram tests (that work now :) 2004-05-11 02:44:16 +00:00
jrandom
fd4bc5e3cf keystream fixes 2004-05-11 02:43:52 +00:00
jrandom
3bab2d8957 only append the client's config properties to the SESSION commands (since some of the rest get confused with unknown tags...) 2004-05-11 02:07:27 +00:00
jrandom
af2f5cd2e1 kaffe shits a brick if you want the socket's address after .close() (grumble) 2004-05-11 01:55:22 +00:00
jrandom
d4bb32da82 fix per http://twiki.ntp.org/bin/view/Support/JavaSntpClientDev (thanks duck!)
this may have caused some clock skew problems on the deployed 0.3.1.1, so we'll get this deployed asap
2004-05-11 01:45:18 +00:00
jrandom
08aca6ca61 while (true) { m00; } 2004-05-09 07:23:43 +00:00
jrandom
697b3c6772 SAM .net lib work in progress - dm and firerabbit 2004-05-09 07:16:04 +00:00
jrandom
878525ced8 handle corrupt files more gracefully 2004-05-09 04:14:30 +00:00
shendaras
418531736b imports
Did you miss me?
(shendaras)
2004-05-09 01:31:12 +00:00
jrandom
6c175440c6 added mush.zeit.i2p 2004-05-08 06:41:01 +00:00
jrandom
723a2f2008 include timestamper in installer 2004-05-07 19:01:47 +00:00
jrandom
ea9b9fbf17 0.3.1.1 (tastes like chicken)
backwards compatible but some config changes necessary
2004-05-07 17:52:49 +00:00
jrandom
303e257841 synchronization reduction and keep track of the 'last' job for each runner (to help debug something i see once a week on kaffe) 2004-05-07 17:51:28 +00:00
jrandom
07b6a8ba92 if we lose our I2CP connection to the router, die hard and fast.
(only relevent for people whose socket manager / i2ptunnel / etc are located remote from the router)
2004-05-07 07:01:26 +00:00
jrandom
e216e18368 update the clock more liberally (since we've got the new ntp code) 2004-05-07 04:34:03 +00:00
jrandom
e57c5b4bc2 log to CRIT for jbigi load failure 2004-05-07 04:23:30 +00:00
jrandom
f772d6ddeb /me reboots brain, understands, and thanks mihi 2004-05-07 04:19:43 +00:00
jrandom
cd37c301d9 if (log.shouldLog())
yeah, aspect oriented programming sure would be nice.
2004-05-07 04:07:14 +00:00
jrandom
f5fa26639e minor html cleanup 2004-05-07 03:45:48 +00:00
jrandom
45ec73c115 include a unique request id in the client runner thread name
minor cleanup
2004-05-07 03:33:23 +00:00
jrandom
13952ebd8b include the expiration and messageId in the toString (shown on msg drop due to "unknown tunnel") 2004-05-07 03:31:33 +00:00
jrandom
2df0007a10 logging 2004-05-07 03:29:06 +00:00
jrandom
89bc5db3e1 increase the bundle probability to yet another arbitrary value
add the jobId to log messages to simplify tracing individual parallel sends
logging cleanup
2004-05-07 03:28:22 +00:00
mihi
4021deec7f poke jrandom's eyes into the semantic of an "else" clause
(you may remove both comments when you understood it)

[mihi]
2004-05-07 03:10:57 +00:00
mihi
a3977f37f7 javadoc, no functional changes 2004-05-07 03:06:41 +00:00
jrandom
766c12242e logging, javadoc 2004-05-07 01:45:12 +00:00
jrandom
a82b951aff made private things that don't need to be public
remove semantic inconsistency wrt getRemoteId(false) - it shouldn't ever timeout, since it always returns immediately
javadoc (though i wish i understood the close/close2/sendClose more clearly so i could javadoc that process)
2004-05-07 01:32:48 +00:00
jrandom
997a94eecc removed PHTTP lines since they were only used for time sync
added more info wrt NTP entry
added filename for the SAM bridge
2004-05-06 07:48:45 +00:00
jrandom
635535aac2 implement keyfile persistence (storing name=privKeyDataBase64\n for each name)
filename specified on the command line: SAMBridge [keyfile [listenHost] listenPortNum [ name=val]*]
2004-05-06 07:35:44 +00:00
jrandom
25314fd91a make sure we mark the send as *failed* if we need to reconnect 2004-05-06 04:18:28 +00:00
jrandom
9a06a5758d check shouldBundle only when its ready to be checked (duh) 2004-05-06 01:02:50 +00:00
jrandom
e5a2a9644f *cough* [d'oh] 2004-05-05 23:01:36 +00:00
jrandom
e0e7211852 lets default the read timeout to 5 minutes for clients (that hanging irc disconnect / not disconnected thing) 2004-05-05 22:57:43 +00:00
jrandom
cdaeb4d176 track and publish two new stats:
* netDb.failedPeers (how many peers didn't reply to a lookup in time)
* netDb.searchCount (how many searches we send out in a 3 hour period)
probabalistically include the leaseSet of the sender in the garlic sent
to a peer if the client requests it to be included (aka if they want
replies).  By default, this is enabled (disable by setting the I2CP
prop "shouldBundleReplyInfo=false").  Also, by default the probability is
30% (w00 h00, arbitrary values!), which can be overridden via the I2CP
prop "bundleReplyInfoProbability=80" (or =10, or =100, etc).  The tradeoff
here is quicker replies in exchange for bandwidth (the dbStore).
Yeah, it'd be nice if there were something keeping track of which leaseSet
each client sent to each peer so that it could explicitly include it only
if it were necessary, but for now that's probably overkill.
2004-05-05 22:46:10 +00:00
jrandom
07aa2e280d strip the Connection, Keep-Alive, and Proxy-Connection headers, and always inject Connection: close
(this is the cause of the intermittent "view $page through squid, try to view eepsite, end up requesting through squid" bug)
2004-05-05 07:29:48 +00:00
jrandom
6c4bc67ff3 simplistic streaming test (w00t, the streams worked - no mods necessary. go human, its your birthday, go human, its your birthday) 2004-05-05 04:43:05 +00:00
jrandom
d9f0cc27ef formatting 2004-05-05 03:37:26 +00:00
jrandom
59aec9d289 expose the read timeout for the client and put the default read timeout back to -1 for the server (override via command line, etc) 2004-05-05 03:36:18 +00:00
jrandom
451f4c503d fixed typo on timestamper, keep NetMonitor off by default 2004-05-05 01:32:08 +00:00
duck
cd82089d4d upgrade deprecated argument
fix ze german
(duck)
2004-05-04 17:17:10 +00:00
jrandom
3db8b63cde by default, set the readTimeout to 3 minutes, NOT infinity. Overridable as before (setting the timeout to -1)
add a unique id to the server thread
2004-05-04 08:16:41 +00:00
jrandom
6edf5d1e4f add a unique id to the thread names 2004-05-04 08:15:18 +00:00
jrandom
a23fa6fadd allow multiple concurrent connections to be created
added a unique ID to more threads
2004-05-04 08:14:19 +00:00
jrandom
51eb77e409 logging 2004-05-04 08:13:01 +00:00
jrandom
691326cea8 make sure we kill the threads that failed to ACK, rather than leave them sitting there, waiting forever
logging
2004-05-04 08:09:28 +00:00
jrandom
3cac1238ed handle reclose, logging, more clear notification 2004-05-04 05:53:11 +00:00
jrandom
b04512a4f6 add unique IDs to the threads for easier tracing 2004-05-04 04:46:04 +00:00
jrandom
3a4d0549aa add accept timeouts (default is that if the server doesnt .accept() in 5s, refuse the con)
add unique IDs to the various threads for logging / tracing purposes
2004-05-04 04:44:05 +00:00
jrandom
d7467f5dc3 disconnect isn't an error 2004-05-04 01:58:37 +00:00
jrandom
141902b86d parseParams throws exception on bad formatting, and its perfectly valid to have params with 0 values (e.g. DEST GENERATE\n) 2004-05-04 01:35:09 +00:00
jrandom
5aa680fc93 simple test of whether DEST GENERATE works 2004-05-04 01:32:39 +00:00
jrandom
a790117f5a test the naming commnads (fetching ME, a known host, and an unknown host) 2004-05-04 01:11:44 +00:00
jrandom
2a5a52c810 added xilog.i2p 2004-05-04 00:43:09 +00:00
jrandom
2156f4c2f3 * more verbose errors (include MESSAGE data on the I2P_ERROR reply, not just in the log)
* don't create excess I2PAppContexts (if any old context will do, use the global)
keep track of keys per spec (when DESTINATION=blah, create (or reuse) the destination private
keys).  we still need to persist this data though.
* the DESTINATION in the SESSION STATUS is now the same as the one sent in the
SESSION CREATE, /not/ the base64 of the private key, per spec
* enum is a reserved word in 1.5, so s/enum/names/ for future compatability
* logging
2004-05-03 11:34:38 +00:00
jrandom
2585460286 initial tests for HELLO and create session (style=stream). covers the basics, but doesn't cover a single normal scenario yet 2004-05-03 11:16:59 +00:00
jrandom
1b4af66986 flag as closed /after/ we send the disconnect message *cough* 2004-05-03 11:13:44 +00:00
jrandom
0324bac044 added lucky.i2p, removed lp.i2p 2004-05-03 07:04:12 +00:00
jrandom
2bfbe1ca27 logging (toss a unique ID onto the handler / inactivity threads) 2004-05-03 03:36:38 +00:00
jrandom
60584228d9 refactored packet handling into type specific methods
removed nested synchronization (which had been causing undetected deadlocks)
made sync blocks smaller, though this may have opened holes related to
resent ACK/SYN/CLOSE packets that are delivered in a race.  I'm not as
fluent in the ministreaming lib code as i should be (yet), but duck's thread
dumps were showing hundreds of threads waiting on a lock that'll never get
released (since the only way to release it would be to receive another
packet, and no more packets can be received until the lock is released, etc)
also, I2PSession is threadsafe - i can see no reason to synchronize on it
(and it was being synchronized on only part of the time?)
also, refactored the charset encoding stuff and minor log tweaking
i've been testing this for the last hour or so, on eepsites and squid (large
and small files), as well as irc, and there haven't been any glitches.  but
it needs more testing before it can be released, obviously.
2004-05-03 03:34:25 +00:00
jrandom
44e34f7b11 trim the request line (StringTokenizer w/ " " as a delim doesn't include \n as a delim, etc) 2004-05-02 07:50:20 +00:00
jrandom
7912050647 allow overriding the I2CP port/host 2004-05-02 07:49:22 +00:00
jrandom
2231abd407 default the DIRECTION to BOTH for streams 2004-05-02 07:07:25 +00:00
jrandom
8d17ba4d66 added sungo.i2p 2004-05-02 06:59:38 +00:00
jrandom
8244bdb440 include the timestamper (and fire the client on startup, using a pair of arbitrary NTP hosts - to be configured later) 2004-05-02 05:11:06 +00:00
jrandom
bc3b7ffd86 start the admin listener ASAP (right after reading the config)
fire the LoadClientAppsJob right after the admin listener is booted, which now includes support for the onBoot property (which causes the client to run immediately, instead of waiting 2+ minutes)
(yeah, it'd suck if all routers started up, tried to connect to people, got shitlisted, then 2 minutes later got the right NTP time, 'eh?)
2004-05-02 05:02:10 +00:00
jrandom
e22cb62493 handle /setTime?blah&now=yyyyMMdd_HH:mm:ss.SSSS (updating the router's clock)
yes, we'll want to filter the access to the admin manager ;)
2004-05-02 04:46:52 +00:00
jrandom
e923aa1f72 add the timestamper 2004-05-02 04:29:18 +00:00
jrandom
68a21f1fbb NTP client, GPLed 2004-05-02 04:18:53 +00:00
285 changed files with 6573 additions and 2631 deletions

View File

@@ -4,7 +4,6 @@ import net.i2p.data.Destination;
import net.i2p.util.Clock;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
import net.i2p.util.Clock;
/**
* Responsible for actually conducting the tests, coordinating the storing of the

View File

@@ -2,11 +2,11 @@ package net.i2p.heartbeat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import net.i2p.stat.Rate;

View File

@@ -10,7 +10,6 @@ import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
class HeartbeatMonitorGUI extends JFrame {
private HeartbeatMonitor _monitor;

View File

@@ -1,6 +1,5 @@
package net.i2p.heartbeat.gui;
import java.awt.Color;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

View File

@@ -1,26 +1,23 @@
package net.i2p.heartbeat.gui;
import java.awt.Color;
import java.awt.Font;
import java.util.List;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.XYItemRenderer;
import org.jfree.chart.renderer.XYLineAndShapeRenderer;
import org.jfree.data.XYSeries;
import org.jfree.data.XYSeriesCollection;
import net.i2p.heartbeat.PeerData;
import net.i2p.util.Log;
import org.jfree.data.XYSeries;
import org.jfree.data.XYSeriesCollection;
import org.jfree.data.MovingAverage;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.renderer.XYLineAndShapeRenderer;
import org.jfree.chart.renderer.XYItemRenderer;
import org.jfree.chart.renderer.XYDotRenderer;
import java.util.List;
import javax.swing.JPanel;
import java.awt.Font;
import java.awt.Color;
class JFreeChartAdapter {
private final static Log _log = new Log(JFreeChartAdapter.class);
private final static Color WHITE = new Color(255, 255, 255);

View File

@@ -1,21 +1,14 @@
package net.i2p.heartbeat.gui;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;
import javax.swing.JLabel;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import net.i2p.heartbeat.PeerDataWriter;
import net.i2p.util.Log;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import org.jfree.chart.ChartPanel;
import net.i2p.util.Log;
/**
* Render the graph and legend
*

View File

@@ -11,8 +11,8 @@ import java.util.Set;
import java.util.TreeMap;
import net.i2p.data.Destination;
import net.i2p.util.Log;
import net.i2p.heartbeat.ClientConfig;
import net.i2p.util.Log;
/**
* Configure how we want to render a particular clientConfig in the GUI

View File

@@ -7,8 +7,8 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketException;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.naming.NamingService;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketOptions;
@@ -19,7 +19,6 @@ import net.i2p.httptunnel.SocketManagerProducer;
import net.i2p.httptunnel.filter.Filter;
import net.i2p.httptunnel.filter.NullFilter;
import net.i2p.util.Log;
import net.i2p.I2PAppContext;
/**
* Handler for browsing Eepsites.

View File

@@ -3,7 +3,7 @@ package net.i2p.httptunnel.handler;
import java.io.IOException;
import java.io.OutputStream;
import net.i2p.client.naming.NamingService;
import net.i2p.I2PAppContext;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.data.Destination;
import net.i2p.httptunnel.HTTPListener;
@@ -12,7 +12,6 @@ import net.i2p.httptunnel.SocketManagerProducer;
import net.i2p.httptunnel.filter.Filter;
import net.i2p.httptunnel.filter.NullFilter;
import net.i2p.util.Log;
import net.i2p.I2PAppContext;
/**
* Handler for proxying "normal" HTTP requests.

View File

@@ -67,6 +67,8 @@ public class I2PTunnel implements Logging, EventDispatcher {
private Log _log;
private EventDispatcherImpl _event;
private I2PAppContext _context;
private static long __tunnelId = 0;
private long _tunnelId;
public static final int PACKET_DELAY = 100;
@@ -98,7 +100,8 @@ public class I2PTunnel implements Logging, EventDispatcher {
}
public I2PTunnel(String[] args, ConnectionEventListener lsnr) {
_context = new I2PAppContext();
_context = I2PAppContext.getGlobalContext(); // new I2PAppContext();
_tunnelId = ++__tunnelId;
_log = _context.logManager().getLog(I2PTunnel.class);
_event = new EventDispatcherImpl();
addConnectionEventListener(lsnr);
@@ -114,7 +117,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
checkRunByE = false;
} else if (args[i].equals("-nogui")) {
gui = false;
_log.warn("The `-nogui' option of I2PTunnel is deprecated.\n"
_log.warn(getPrefix() + "The `-nogui' option of I2PTunnel is deprecated.\n"
+ "Use `-cli', `-nocli' (aka `-wait') or `-die' instead.");
} else if (args[i].equals("-cli")) {
gui = false;
@@ -280,7 +283,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
serverHost = InetAddress.getByName(args[0]);
} catch (UnknownHostException uhe) {
l.log("unknown host");
_log.error("Error resolving " + args[0], uhe);
_log.error(getPrefix() + "Error resolving " + args[0], uhe);
notifyEvent("serverTaskId", new Integer(-1));
return;
}
@@ -289,7 +292,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
portNum = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error("Port specified is not valid: " + args[1], nfe);
_log.error(getPrefix() + "Port specified is not valid: " + args[1], nfe);
notifyEvent("serverTaskId", new Integer(-1));
return;
}
@@ -297,7 +300,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
privKeyFile = new File(args[2]);
if (!privKeyFile.canRead()) {
l.log("private key file does not exist");
_log.error("Private key file does not exist or is not readable: " + args[2]);
_log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[2]);
notifyEvent("serverTaskId", new Integer(-1));
return;
}
@@ -333,7 +336,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
serverHost = InetAddress.getByName(args[0]);
} catch (UnknownHostException uhe) {
l.log("unknown host");
_log.error("Error resolving " + args[0], uhe);
_log.error(getPrefix() + "Error resolving " + args[0], uhe);
notifyEvent("serverTaskId", new Integer(-1));
return;
}
@@ -342,7 +345,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
portNum = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error("Port specified is not valid: " + args[1], nfe);
_log.error(getPrefix() + "Port specified is not valid: " + args[1], nfe);
notifyEvent("serverTaskId", new Integer(-1));
return;
}
@@ -378,7 +381,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
port = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error("Port specified is not valid: " + args[0], nfe);
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
notifyEvent("clientTaskId", new Integer(-1));
return;
}
@@ -410,7 +413,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
port = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error("Port specified is not valid: " + args[0], nfe);
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
notifyEvent("httpclientTaskId", new Integer(-1));
return;
}
@@ -451,7 +454,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
port = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error("Port specified is not valid: " + args[0], nfe);
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
notifyEvent("sockstunnelTaskId", new Integer(-1));
return;
}
@@ -565,7 +568,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
pubdest = new FileOutputStream(args[1]);
} catch (IOException ioe) {
l.log("Error opening output stream");
_log.error("Error generating keys to out", ioe);
_log.error(getPrefix() + "Error generating keys to out", ioe);
notifyEvent("genkeysResult", "error");
return;
}
@@ -588,7 +591,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
} catch (IOException ioe) {
l.log("Error generating keys - " + ioe.getMessage());
notifyEvent("genkeysResult", "error");
_log.error("Error generating keys", ioe);
_log.error(getPrefix() + "Error generating keys", ioe);
}
}
@@ -722,7 +725,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
notifyEvent("runResult", "ok");
} catch (IOException ioe) {
l.log("IO error running the file");
_log.error("Error running the file", ioe);
_log.error(getPrefix() + "Error running the file", ioe);
notifyEvent("runResult", "error");
}
} else {
@@ -796,12 +799,12 @@ public class I2PTunnel implements Logging, EventDispatcher {
private boolean closetask(int num, boolean forced, Logging l) {
boolean closed = false;
_log.debug("closetask(): looking for task " + num);
_log.debug(getPrefix() + "closetask(): looking for task " + num);
synchronized (tasks) {
for (Iterator it = tasks.iterator(); it.hasNext();) {
I2PTunnelTask t = (I2PTunnelTask) it.next();
int id = t.getId();
_log.debug("closetask(): parsing task " + id + " (" + t.toString() + ")");
_log.debug(getPrefix() + "closetask(): parsing task " + id + " (" + t.toString() + ")");
if (id == num) {
closed = closetask(t, forced, l);
break;
@@ -836,7 +839,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
for (Iterator it = tasks.iterator(); it.hasNext();) {
I2PTunnelTask t = (I2PTunnelTask) it.next();
if (!t.isOpen()) {
_log.debug("Purging inactive tunnel: [" + t.getId() + "] " + t.toString());
_log.debug(getPrefix() + "Purging inactive tunnel: [" + t.getId() + "] " + t.toString());
it.remove();
}
}
@@ -849,7 +852,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
*/
public void log(String s) {
System.out.println(s);
_log.info("Display: " + s);
_log.info(getPrefix() + "Display: " + s);
}
/**
@@ -982,6 +985,8 @@ public class I2PTunnel implements Logging, EventDispatcher {
listeners.remove(lsnr);
}
}
private String getPrefix() { return '[' + _tunnelId + "]: "; }
/**
* Call this whenever we lose touch with the router involuntarily (aka the router
@@ -989,7 +994,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
*
*/
void routerDisconnected() {
_log.error("Router disconnected - firing notification events");
_log.error(getPrefix() + "Router disconnected - firing notification events");
synchronized (listeners) {
for (Iterator iter = listeners.iterator(); iter.hasNext();) {
ConnectionEventListener lsnr = (ConnectionEventListener) iter.next();

View File

@@ -5,7 +5,6 @@ package net.i2p.i2ptunnel;
import java.net.Socket;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
@@ -17,6 +16,8 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
private static final Log _log = new Log(I2PTunnelClient.class);
protected Destination dest;
private static final long DEFAULT_READ_TIMEOUT = 5*60*1000; // -1
protected long readTimeout = DEFAULT_READ_TIMEOUT;
public I2PTunnelClient(int localPort, String destination, Logging l, boolean ownDest, EventDispatcher notifyThis) {
super(localPort, ownDest, l, notifyThis, "SynSender");
@@ -45,9 +46,13 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
notifyEvent("openClientResult", "ok");
}
public void setReadTimeout(long ms) { readTimeout = ms; }
public long getReadTimeout() { return readTimeout; }
protected void clientConnectionRun(Socket s) {
try {
I2PSocket i2ps = createI2PSocket(dest);
i2ps.setReadTimeout(readTimeout);
new I2PTunnelRunner(s, i2ps, sockLock, null);
} catch (Exception ex) {
_log.info("Error connecting", ex);

View File

@@ -33,6 +33,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
private static final long DEFAULT_CONNECT_TIMEOUT = 60 * 1000;
private static volatile long __clientId = 0;
protected long _clientId;
protected Object sockLock = new Object(); // Guards sockMgr and mySockets
private I2PSocketManager sockMgr;
private List mySockets = new ArrayList();
@@ -60,9 +62,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
public I2PTunnelClientBase(int localPort, boolean ownDest, Logging l, EventDispatcher notifyThis, String handlerName) {
super(localPort + " (uninitialized)", notifyThis);
_clientId = ++__clientId;
this.localPort = localPort;
this.l = l;
this.handlerName = handlerName;
this.handlerName = handlerName + _clientId;
synchronized (sockLock) {
if (ownDest) {
@@ -75,7 +78,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
l.log("I2P session created");
Thread t = new I2PThread(this);
t.setName("Client");
t.setName("Client " + _clientId);
listenerReady = false;
t.start();
open = true;
@@ -179,8 +182,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
public I2PSocket createI2PSocket(Destination dest, I2PSocketOptions opt) throws I2PException, ConnectException, NoRouteToHostException, InterruptedIOException {
I2PSocket i2ps;
i2ps = sockMgr.connect(dest, opt);
synchronized (sockLock) {
i2ps = sockMgr.connect(dest, opt);
mySockets.add(i2ps);
}
@@ -272,12 +275,14 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
}
}
private static volatile long __runnerId = 0;
public class ClientConnectionRunner extends I2PThread {
private Socket s;
public ClientConnectionRunner(Socket s, String name) {
this.s = s;
setName(name);
setName(name + '.' + (++__runnerId));
start();
}

View File

@@ -20,6 +20,25 @@ import net.i2p.util.EventDispatcher;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Act as a mini HTTP proxy, handling various different types of requests,
* forwarding them through I2P appropriately, and displaying the reply. Supported
* request formats are: <pre>
* $method http://$site[$port]/$path $protocolVersion
* or
* $method $path $protocolVersion\nHost: $site
* or
* $method http://i2p/$site/$path $protocolVersion
* or
* $method /$site/$path $protocolVersion
* </pre>
*
* If the $site resolves with the I2P naming service, then it is directed towards
* that eepsite, otherwise it is directed towards this client's outproxy (typically
* "squid.i2p"). Only HTTP is supported (no HTTPS, ftp, mailto, etc). Both GET
* and POST have been tested, though other $methods should work.
*
*/
public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable {
private static final Log _log = new Log(I2PTunnelHTTPClient.class);
@@ -43,7 +62,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
"That Desitination was not found. Perhaps you pasted in the wrong "+
"BASE64 I2P Destination or the link you are following is bad. "+
"The host (or the WWW proxy, if you're using one) could also be "+
"temporarily offline. "+
"temporarily offline. You may want to <b>retry</b>. "+
"Could not find the following Destination:<BR><BR>")
.getBytes();
@@ -53,19 +72,17 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
"Cache-control: no-cache\r\n\r\n"+
"<html><body><H1>I2P ERROR: TIMEOUT</H1>"+
"That Desitination was reachable, but timed out getting a "+
"response. This may be a temporary error, so you should simply "+
"response. This is likely a temporary error, so you should simply "+
"try to refresh, though if the problem persists, the remote "+
"destination may have issues. Could not get a response from "+
"the following Destination:<BR><BR>")
.getBytes();
// public I2PTunnelHTTPClient(int localPort, Logging l,
// boolean ownDest, String wwwProxy) {
// this(localPort, l, ownDest, wwwProxy, (EventDispatcher)null);
// }
/** used to assign unique IDs to the threads / clients. no logic or functionality */
private static volatile long __clientId = 0;
public I2PTunnelHTTPClient(int localPort, Logging l, boolean ownDest, String wwwProxy, EventDispatcher notifyThis) {
super(localPort, ownDest, l, notifyThis, "HTTPHandler");
super(localPort, ownDest, l, notifyThis, "HTTPHandler " + (++__clientId));
if (waitEventValue("openBaseClientResult").equals("error")) {
notifyEvent("openHTTPClientResult", "error");
@@ -81,6 +98,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
notifyEvent("openHTTPClientResult", "ok");
}
private String getPrefix() { return "Client[" + _clientId + "]: "; }
protected void clientConnectionRun(Socket s) {
OutputStream out = null;
String targetRequest = null;
@@ -92,7 +111,18 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
String line, method = null, protocol = null, host = null, destination = null;
StringBuffer newRequest = new StringBuffer();
while ((line = br.readLine()) != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Line=[" + line + "]");
if (line.startsWith("Connection: ") ||
line.startsWith("Keep-Alive: ") ||
line.startsWith("Proxy-Connection: "))
continue;
if (method == null) { // first line (GET /base64/realaddr)
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Method is null for [" + line + "]");
int pos = line.indexOf(" ");
if (pos == -1) break;
method = line.substring(0, pos);
@@ -126,6 +156,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
// The request must be forwarded to a WWW proxy
destination = wwwProxy;
usingWWWProxy = true;
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Host doesnt end with .i2p and it contains a period [" + host + "]: wwwProxy!");
} else {
request = request.substring(pos + 1);
pos = request.indexOf("/");
@@ -135,29 +167,40 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
boolean isValid = usingWWWProxy || isSupportedAddress(host, protocol);
if (!isValid) {
if (_log.shouldLog(Log.INFO)) _log.info("notValid(" + host + ")");
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "notValid(" + host + ")");
method = null;
destination = null;
break;
} else if (!usingWWWProxy) {
if (_log.shouldLog(Log.INFO)) _log.info("host=getHostName(" + destination + ")");
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "host=getHostName(" + destination + ")");
host = getHostName(destination); // hide original host
}
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("METHOD:" + method + ":");
_log.debug("PROTOC:" + protocol + ":");
_log.debug("HOST :" + host + ":");
_log.debug("DEST :" + destination + ":");
_log.debug(getPrefix() + "METHOD:" + method + ":");
_log.debug(getPrefix() + "PROTOC:" + protocol + ":");
_log.debug(getPrefix() + "HOST :" + host + ":");
_log.debug(getPrefix() + "DEST :" + destination + ":");
}
} else {
if (line.startsWith("Host: ") && !usingWWWProxy) {
line = "Host: " + host;
if (_log.shouldLog(Log.INFO))
_log.info(getPrefix() + "Setting host = " + host);
}
} else if (line.startsWith("Host: ") && !usingWWWProxy) {
line = "Host: " + host;
if (_log.shouldLog(Log.INFO)) _log.info("Setting host = " + host);
}
newRequest.append(line).append("\r\n"); // HTTP spec
if (line.length() == 0) break;
if (line.length() == 0) {
newRequest.append("Connection: close\r\n\r\n");
break;
} else {
newRequest.append(line).append("\r\n"); // HTTP spec
}
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "NewRequest header: [" + newRequest.toString() + "]");
while (br.ready()) { // empty the buffer (POST requests)
int i = br.read();
if (i != -1) {
@@ -176,6 +219,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
s.close();
return;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Destination: " + destination);
Destination dest = I2PTunnel.destFromName(destination);
if (dest == null) {
l.log("Could not resolve " + destination + ".");
@@ -191,19 +238,19 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
timeoutThread.start();
} catch (SocketException ex) {
if (timeoutThread != null) timeoutThread.disable();
_log.info("Error trying to connect", ex);
_log.info(getPrefix() + "Error trying to connect", ex);
l.log(ex.getMessage());
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, wwwProxy);
closeSocket(s);
} catch (IOException ex) {
if (timeoutThread != null) timeoutThread.disable();
_log.info("Error trying to connect", ex);
_log.info(getPrefix() + "Error trying to connect", ex);
l.log(ex.getMessage());
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, wwwProxy);
closeSocket(s);
} catch (I2PException ex) {
if (timeoutThread != null) timeoutThread.disable();
_log.info("Error trying to connect", ex);
_log.info("getPrefix() + Error trying to connect", ex);
l.log(ex.getMessage());
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, wwwProxy);
closeSocket(s);
@@ -211,6 +258,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
private static final long INACTIVITY_TIMEOUT = 120 * 1000;
private static volatile long __timeoutId = 0;
private class InactivityTimeoutThread extends I2PThread {
@@ -230,7 +278,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
_targetRequest = targetRequest;
_useWWWProxy = useWWWProxy;
_disabled = false;
setName("InactivityThread");
long timeoutId = ++__timeoutId;
setName("InactivityThread " + getPrefix() + timeoutId);
}
public void disable() {
@@ -243,15 +292,15 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
public void run() {
while (!_disabled) {
if (_runner.isFinished()) {
if (_log.shouldLog(Log.INFO)) _log.info("HTTP client request completed prior to timeout");
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "HTTP client request completed prior to timeout");
return;
}
if (_runner.getLastActivityOn() < Clock.getInstance().now() - INACTIVITY_TIMEOUT) {
if (_runner.getStartedOn() < Clock.getInstance().now() - INACTIVITY_TIMEOUT) {
if (_log.shouldLog(Log.WARN))
_log.warn("HTTP client request timed out (lastActivity: "
_log.warn(getPrefix() + "HTTP client request timed out (lastActivity: "
+ new Date(_runner.getLastActivityOn()) + ", startedOn: "
+ new Date(_runner.getLastActivityOn()) + ")");
+ new Date(_runner.getStartedOn()) + ")");
timeout();
return;
} else {
@@ -270,7 +319,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
private void timeout() {
_log.info("Inactivity timeout reached");
_log.info(getPrefix() + "Inactivity timeout reached");
l.log("Inactivity timeout reached");
if (_out != null) {
try {
@@ -280,10 +329,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
writeErrorMessage(ERR_TIMEOUT, _out, _targetRequest, _useWWWProxy, wwwProxy);
}
} catch (IOException ioe) {
_log.warn("Error writing out the 'timeout' message", ioe);
_log.warn(getPrefix() + "Error writing out the 'timeout' message", ioe);
}
} else {
_log.warn("Client disconnected before we could say we timed out");
_log.warn(getPrefix() + "Client disconnected before we could say we timed out");
}
closeSocket(s);
}
@@ -314,16 +363,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
}
private static void handleHTTPClientException(Exception ex, OutputStream out, String targetRequest,
private void handleHTTPClientException(Exception ex, OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy) {
if (out != null) {
try {
writeErrorMessage(ERR_DESTINATION_UNKNOWN, out, targetRequest, usingWWWProxy, wwwProxy);
} catch (IOException ioe) {
_log.warn("Error writing out the 'destination was unknown' " + "message", ioe);
_log.warn(getPrefix() + "Error writing out the 'destination was unknown' " + "message", ioe);
}
} else {
_log.warn("Client disconnected before we could say that destination " + "was unknown", ex);
_log.warn(getPrefix() + "Client disconnected before we could say that destination " + "was unknown", ex);
}
}

View File

@@ -18,9 +18,11 @@ import net.i2p.util.Clock;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
public class I2PTunnelRunner extends I2PThread {
public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorListener {
private final static Log _log = new Log(I2PTunnelRunner.class);
private static volatile long __runnerId;
private long _runnerId;
/**
* max bytes streamed in a packet - smaller ones might be filled
* up to this size. Larger ones are not split (at least not on
@@ -49,9 +51,11 @@ public class I2PTunnelRunner extends I2PThread {
this.slock = slock;
this.initialData = initialData;
lastActivityOn = -1;
startedOn = -1;
_log.info("I2PTunnelRunner started");
setName("I2PTunnelRunner");
startedOn = Clock.getInstance().now();
if (_log.shouldLog(Log.INFO))
_log.info("I2PTunnelRunner started");
_runnerId = ++__runnerId;
setName("I2PTunnelRunner " + _runnerId);
start();
}
@@ -87,10 +91,10 @@ public class I2PTunnelRunner extends I2PThread {
}
public void run() {
startedOn = Clock.getInstance().now();
try {
InputStream in = s.getInputStream();
OutputStream out = new BufferedOutputStream(s.getOutputStream(), NETWORK_BUFFER_SIZE);
i2ps.setSocketErrorListener(this);
InputStream i2pin = i2ps.getInputStream();
OutputStream i2pout = new BufferedOutputStream(i2ps.getOutputStream(), MAX_PACKET_SIZE);
if (initialData != null) {
@@ -118,6 +122,8 @@ public class I2PTunnelRunner extends I2PThread {
} catch (IOException ex) {
ex.printStackTrace();
_log.debug("Error forwarding", ex);
} catch (Exception e) {
_log.error("Internal error", e);
} finally {
try {
if (s != null) s.close();
@@ -129,6 +135,15 @@ public class I2PTunnelRunner extends I2PThread {
}
}
public void errorOccurred() {
synchronized (finishLock) {
finished = true;
finishLock.notifyAll();
}
}
private volatile long __forwarderId = 0;
private class StreamForwarder extends I2PThread {
InputStream in;
@@ -137,7 +152,7 @@ public class I2PTunnelRunner extends I2PThread {
private StreamForwarder(InputStream in, OutputStream out) {
this.in = in;
this.out = out;
setName("StreamForwarder");
setName("StreamForwarder " + _runnerId + "." + (++__forwarderId));
start();
}
@@ -182,7 +197,8 @@ public class I2PTunnelRunner extends I2PThread {
out.close();
in.close();
} catch (IOException ex) {
_log.error("Error closing streams", ex);
if (_log.shouldLog(Log.WARN))
_log.warn("Error closing streams", ex);
}
synchronized (finishLock) {
finished = true;

View File

@@ -15,6 +15,7 @@ import java.util.Iterator;
import java.util.Properties;
import net.i2p.I2PException;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.streaming.I2PServerSocket;
@@ -40,7 +41,9 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
private Logging l;
private long readTimeout = -1;
private static final long DEFAULT_READ_TIMEOUT = -1; // 3*60*1000;
/** default timeout to 3 minutes - override if desired */
private long readTimeout = DEFAULT_READ_TIMEOUT;
public I2PTunnelServer(InetAddress host, int port, String privData, Logging l, EventDispatcher notifyThis) {
super(host + ":" + port + " <- " + privData, notifyThis);
@@ -81,13 +84,16 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
open = true;
}
private static volatile long __serverId = 0;
/**
* Start running the I2PTunnelServer.
*
*/
public void startRunning() {
Thread t = new I2PThread(this);
t.setName("Server");
t.setName("Server " + (++__serverId));
t.start();
}
@@ -139,15 +145,8 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
I2PServerSocket i2pss = sockMgr.getServerSocket();
while (true) {
I2PSocket i2ps = i2pss.accept();
//local is fast, so synchronously. Does not need that many
//threads.
try {
i2ps.setReadTimeout(readTimeout);
Socket s = new Socket(remoteHost, remotePort);
new I2PTunnelRunner(s, i2ps, slock, null);
} catch (SocketException ex) {
i2ps.close();
}
I2PThread t = new I2PThread(new Handler(i2ps));
t.start();
}
} catch (I2PException ex) {
_log.error("Error while waiting for I2PConnections", ex);
@@ -155,5 +154,43 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
_log.error("Error while waiting for I2PConnections", ex);
}
}
/**
* Async handler to keep .accept() from blocking too long.
* todo: replace with a thread pool so we dont get overrun by threads if/when
* receiving a lot of connection requests concurrently.
*
*/
private class Handler implements Runnable {
private I2PSocket _handleSocket;
public Handler(I2PSocket socket) {
_handleSocket = socket;
}
public void run() {
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
long afterSocket = -1;
//local is fast, so synchronously. Does not need that many
//threads.
try {
_handleSocket.setReadTimeout(readTimeout);
Socket s = new Socket(remoteHost, remotePort);
afterSocket = I2PAppContext.getGlobalContext().clock().now();
new I2PTunnelRunner(s, _handleSocket, slock, null);
} catch (SocketException ex) {
try {
_handleSocket.close();
} catch (IOException ioe) {
_log.error("Error while closing the received i2p con", ex);
}
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
long afterHandle = I2PAppContext.getGlobalContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
if (timeToHandle > 1000)
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: " + (afterSocket-afterAccept) + "]");
}
}
}

View File

@@ -6,7 +6,7 @@ public class ByteCollector {
int size;
public ByteCollector() {
contents = new byte[80];
contents = new byte[1024];
size = 0;
}

View File

@@ -1,74 +1,150 @@
package net.i2p.client.streaming;
import java.net.ConnectException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.util.Clock;
import net.i2p.util.Log;
/**
* Initial stub implementation for the server socket
* Server socket implementation, allowing multiple threads to accept I2PSockets
* and pull from a queue populated by various threads (each of whom have their own
* timeout)
*
*/
class I2PServerSocketImpl implements I2PServerSocket {
private final static Log _log = new Log(I2PServerSocketImpl.class);
private I2PSocketManager mgr;
private I2PSocket cached = null; // buffer one socket here
private boolean closing = false; // Are we being closed?
private Object acceptLock = new Object();
/** list of sockets waiting for the client to accept them */
private List pendingSockets = Collections.synchronizedList(new ArrayList(4));
/** have we been closed */
private volatile boolean closing = false;
/** lock on this when accepting a pending socket, and wait on it for notification of acceptance */
private Object socketAcceptedLock = new Object();
/** lock on this when adding a new socket to the pending list, and wait on it accordingly */
private Object socketAddedLock = new Object();
public I2PServerSocketImpl(I2PSocketManager mgr) {
this.mgr = mgr;
}
public synchronized I2PSocket accept() throws I2PException, ConnectException {
I2PSocket ret;
synchronized (acceptLock) {
while ((cached == null) && !closing) {
myWait();
}
if (closing) {
throw new ConnectException("I2PServerSocket closed");
}
ret = cached;
cached = null;
acceptLock.notifyAll();
}
_log.debug("TIMING: handed out accept result " + ret.hashCode());
/**
* Waits for the next socket connecting. If a remote user tried to make a
* connection and the local application wasn't .accept()ing new connections,
* they should get refused (if .accept() doesnt occur in some small period -
* currently 5 seconds)
*
* @return a connected I2PSocket
*
* @throws I2PException if there is a problem with reading a new socket
* from the data available (aka the I2PSession closed, etc)
* @throws ConnectException if the I2PServerSocket is closed
*/
public I2PSocket accept() throws I2PException, ConnectException {
if (_log.shouldLog(Log.DEBUG))
_log.debug("accept() called, pending: " + pendingSockets.size());
I2PSocket ret = null;
while ( (ret == null) && (!closing) ){
while (pendingSockets.size() <= 0) {
if (closing) throw new ConnectException("I2PServerSocket closed");
try {
synchronized(socketAddedLock) {
socketAddedLock.wait();
}
} catch (InterruptedException ie) {}
}
synchronized (pendingSockets) {
if (pendingSockets.size() > 0) {
ret = (I2PSocket)pendingSockets.remove(0);
}
}
if (ret != null) {
synchronized (socketAcceptedLock) {
socketAcceptedLock.notifyAll();
}
}
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("TIMING: handed out accept result " + ret.hashCode());
return ret;
}
public boolean getNewSocket(I2PSocket s) {
synchronized (acceptLock) {
while (cached != null) {
myWait();
}
cached = s;
acceptLock.notifyAll();
}
/**
* Make the socket available and wait until the client app accepts it, or until
* the given timeout elapses. This doesn't have any limits on the queue size -
* perhaps it should add some choking (e.g. after 5 waiting for accept, refuse)
*
* @param timeoutMs how long to wait until accept
* @return true if the socket was accepted, false if the timeout expired
* or the socket was closed
*/
public boolean addWaitForAccept(I2PSocket s, long timeoutMs) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("addWaitForAccept [new socket arrived [" + s.toString() + "], pending: " + pendingSockets.size());
if (closing) {
if (_log.shouldLog(Log.WARN))
_log.warn("Already closing the socket");
return false;
}
Clock clock = I2PAppContext.getGlobalContext().clock();
long start = clock.now();
long end = start + timeoutMs;
pendingSockets.add(s);
synchronized (socketAddedLock) {
socketAddedLock.notifyAll();
}
// keep looping until the socket has been grabbed by the accept()
// (or the expiration passes, or the socket is closed)
while (pendingSockets.contains(s)) {
long now = clock.now();
if (now >= end) {
if (_log.shouldLog(Log.INFO))
_log.info("Expired while waiting for accept (time elapsed =" + (now - start) + "ms) for socket " + s.toString());
pendingSockets.remove(s);
return false;
}
if (closing) {
if (_log.shouldLog(Log.WARN))
_log.warn("Server socket closed while waiting for accept");
pendingSockets.remove(s);
return false;
}
long remaining = end - now;
try {
synchronized (socketAcceptedLock) {
socketAcceptedLock.wait(remaining);
}
} catch (InterruptedException ie) {}
}
long now = clock.now();
if (_log.shouldLog(Log.DEBUG))
_log.info("Socket accepted after " + (now-start) + "ms for socket " + s.toString());
return true;
}
public void close() throws I2PException {
synchronized (acceptLock) {
closing = true;
acceptLock.notifyAll();
}
}
public I2PSocketManager getManager() {
return mgr;
}
private void myWait() {
try {
acceptLock.wait();
} catch (InterruptedException ex) {}
public void close() {
closing = true;
// let anyone .accept()ing know to fsck off
synchronized (socketAddedLock) {
socketAddedLock.notifyAll();
}
// let anyone addWaitForAccept()ing know to fsck off
synchronized (socketAcceptedLock) {
socketAcceptedLock.notifyAll();
}
}
public I2PSocketManager getManager() { return mgr; }
}

View File

@@ -50,4 +50,21 @@ public interface I2PSocket {
* Closes the socket if not closed yet
*/
public void close() throws IOException;
public void setSocketErrorListener(SocketErrorListener lsnr);
/**
* Allow notification of underlying errors communicating across I2P without
* waiting for any sort of cleanup process. For example, if some data could
* not be sent, this listener is notified immediately, and while the input/output
* streams are notified through IOExceptions, they are told only after the
* TCP-like stream is closed (which may be a minute later, if the close message
* times out as well). This is not fired on normal close() activity.
*
*/
public interface SocketErrorListener {
/**
* An error occurred communicating with the peer.
*/
void errorOccurred();
}
}

View File

@@ -29,14 +29,44 @@ class I2PSocketImpl implements I2PSocket {
private Object remoteIDWaiter = new Object();
private I2PInputStream in;
private I2POutputStream out;
private SocketErrorListener _socketErrorListener;
private boolean outgoing;
private long _socketId;
private static long __socketId = 0;
private long _bytesRead = 0;
private long _bytesWritten = 0;
private Object flagLock = new Object();
private boolean closed = false, sendClose = true, closed2 = false;
/**
* Whether the I2P socket has already been closed.
*/
private boolean closed = false;
/**
* Whether to send out a close packet when the socket is
* closed. (If the socket is closed because of an incoming close
* packet, we need not send one.)
*/
private boolean sendClose = true;
/**
* Whether the I2P socket has already been closed and all data
* (from I2P to the app, dunno whether to call this incoming or
* outgoing) has been processed.
*/
private boolean closed2 = false;
/**
* @param peer who this socket is (or should be) connected to
* @param mgr how we talk to the network
* @param outgoing did we initiate the connection (true) or did we receive it (false)?
* @param localID what is our half of the socket ID?
*/
public I2PSocketImpl(Destination peer, I2PSocketManager mgr, boolean outgoing, String localID) {
this.outgoing = outgoing;
manager = mgr;
remote = peer;
_socketId = ++__socketId;
local = mgr.getSession().getMyDestination();
in = new I2PInputStream();
I2PInputStream pin = new I2PInputStream();
@@ -45,10 +75,17 @@ class I2PSocketImpl implements I2PSocket {
this.localID = localID;
}
/**
* Our half of the socket's unique ID
*
*/
public String getLocalID() {
return localID;
}
/**
* We've received the other side's half of the socket's unique ID
*/
public void setRemoteID(String id) {
synchronized (remoteIDWaiter) {
remoteID = id;
@@ -56,10 +93,32 @@ class I2PSocketImpl implements I2PSocket {
}
}
public String getRemoteID(boolean wait) throws InterruptedIOException {
return getRemoteID(wait, -1);
/**
* Retrieve the other side's half of the socket's unique ID, or null if it
* isn't known yet
*
* @param wait if true, we should wait until we receive it from the peer, otherwise
* return what we know immediately (which may be null)
*/
public String getRemoteID(boolean wait) {
try {
return getRemoteID(wait, -1);
} catch (InterruptedIOException iie) {
_log.error("wtf, we said we didn't want it to time out! you smell", iie);
return null;
}
}
/**
* Retrieve the other side's half of the socket's unique ID, or null if it isn't
* known yet and we were instructed not to wait
*
* @param wait should we wait for the peer to send us their half of the ID, or
* just return immediately?
* @param maxWait if we're going to wait, after how long should we timeout and fail?
* (if this value is < 0, we wait indefinitely)
* @throws InterruptedIOException when the max waiting period has been exceeded
*/
public String getRemoteID(boolean wait, long maxWait) throws InterruptedIOException {
long dieAfter = System.currentTimeMillis() + maxWait;
synchronized (remoteIDWaiter) {
@@ -75,18 +134,32 @@ class I2PSocketImpl implements I2PSocket {
if ((maxWait >= 0) && (System.currentTimeMillis() >= dieAfter))
throw new InterruptedIOException("Timed out waiting for remote ID");
_log.debug("TIMING: RemoteID set to " + I2PSocketManager.getReadableForm(remoteID) + " for "
+ this.hashCode());
if (_log.shouldLog(Log.DEBUG))
_log.debug("TIMING: RemoteID set to "
+ I2PSocketManager.getReadableForm(remoteID) + " for "
+ this.hashCode());
}
return remoteID;
}
}
public String getRemoteID() throws InterruptedIOException {
/**
* Retrieve the other side's half of the socket's unique ID, or null if it
* isn't known yet. This does not wait
*
*/
public String getRemoteID() {
return getRemoteID(false);
}
/**
* The other side has given us some data, so inject it into our socket's
* inputStream
*
* @param data the data to inject into our local inputStream
*/
public void queueData(byte[] data) {
_bytesRead += data.length;
in.queueData(data);
}
@@ -121,7 +194,8 @@ class I2PSocketImpl implements I2PSocket {
}
/**
* Closes the socket if not closed yet
* Closes the socket if not closed yet (from the Application
* side).
*/
public void close() throws IOException {
synchronized (flagLock) {
@@ -132,7 +206,10 @@ class I2PSocketImpl implements I2PSocket {
in.notifyClosed();
}
public void internalClose() {
/**
* Close the socket from the I2P side, e. g. by a close packet.
*/
protected void internalClose() {
synchronized (flagLock) {
closed = true;
closed2 = true;
@@ -143,9 +220,17 @@ class I2PSocketImpl implements I2PSocket {
}
private byte getMask(int add) {
return (byte) ((outgoing ? (byte) 0xA0 : (byte) 0x50) + (byte) add);
if (outgoing)
return (byte)(I2PSocketManager.DATA_IN + (byte)add);
else
return (byte)(I2PSocketManager.DATA_OUT + (byte)add);
}
/**
* 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 in.getReadTimeout();
}
@@ -154,8 +239,19 @@ class I2PSocketImpl implements I2PSocket {
in.setReadTimeout(ms);
}
public void setSocketErrorListener(SocketErrorListener lsnr) {
_socketErrorListener = lsnr;
}
void errorOccurred() {
if (_socketErrorListener != null)
_socketErrorListener.errorOccurred();
}
private String getPrefix() { return "[" + _socketId + "]: "; }
//--------------------------------------------------
public class I2PInputStream extends InputStream {
private class I2PInputStream extends InputStream {
private ByteCollector bc = new ByteCollector();
@@ -178,7 +274,8 @@ class I2PSocketImpl implements I2PSocket {
}
public synchronized int read(byte[] b, int off, int len) throws IOException {
_log.debug("Read called: " + this.hashCode());
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Read called: " + this.hashCode());
if (len == 0) return 0;
long dieAfter = System.currentTimeMillis() + readTimeout;
byte[] read = bc.startToByteArray(len);
@@ -187,7 +284,8 @@ class I2PSocketImpl implements I2PSocket {
while (read.length == 0) {
synchronized (flagLock) {
if (closed) {
_log.debug("Closed is set, so closing stream: " + this.hashCode());
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Closed is set after reading " + _bytesRead + " and writing " + _bytesWritten + ", so closing stream: " + hashCode());
return -1;
}
}
@@ -201,7 +299,7 @@ class I2PSocketImpl implements I2PSocket {
if ((readTimeout >= 0)
&& (System.currentTimeMillis() >= dieAfter)) {
throw new InterruptedIOException("Timeout reading from I2PSocket (" + readTimeout + " msecs)");
throw new InterruptedIOException(getPrefix() + "Timeout reading from I2PSocket (" + readTimeout + " msecs)");
}
read = bc.startToByteArray(len);
@@ -210,12 +308,13 @@ class I2PSocketImpl implements I2PSocket {
System.arraycopy(read, 0, b, off, read.length);
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Read from I2PInputStream " + this.hashCode() + " returned " + read.length + " bytes");
_log.debug(getPrefix() + "Read from I2PInputStream " + hashCode() + " returned "
+ read.length + " bytes");
}
//if (_log.shouldLog(Log.DEBUG)) {
// _log.debug("Read from I2PInputStream " + this.hashCode()
// + " returned "+read.length+" bytes:\n"
// + HexDump.dump(read));
// + " returned "+read.length+" bytes:\n"
// + HexDump.dump(read));
//}
return read.length;
}
@@ -229,18 +328,24 @@ class I2PSocketImpl implements I2PSocket {
}
public synchronized void queueData(byte[] data, int off, int len) {
_log.debug("Insert " + len + " bytes into queue: " + this.hashCode());
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Insert " + len + " bytes into queue: " + hashCode());
bc.append(data, off, len);
notifyAll();
}
public synchronized void notifyClosed() {
notifyAll();
I2PInputStream.this.notifyAll();
}
public void close() throws IOException {
super.close();
notifyClosed();
}
}
public class I2POutputStream extends OutputStream {
private class I2POutputStream extends OutputStream {
public I2PInputStream sendTo;
@@ -253,6 +358,7 @@ class I2PSocketImpl implements I2PSocket {
}
public void write(byte[] b, int off, int len) throws IOException {
_bytesWritten += len;
sendTo.queueData(b, off, len);
}
@@ -261,96 +367,114 @@ class I2PSocketImpl implements I2PSocket {
}
}
public class I2PSocketRunner extends I2PThread {
private static volatile long __runnerId = 0;
private class I2PSocketRunner extends I2PThread {
public InputStream in;
public I2PSocketRunner(InputStream in) {
_log.debug("Runner's input stream is: " + in.hashCode());
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Runner's input stream is: " + in.hashCode());
this.in = in;
setName("SocketRunner from " + I2PSocketImpl.this.remote.calculateHash().toBase64().substring(0, 4));
String peer = I2PSocketImpl.this.remote.calculateHash().toBase64();
setName("SocketRunner " + (++__runnerId) + "/" + _socketId + " " + peer.substring(0, 4));
start();
}
/**
* Pump some more data
*
* @return true if we should keep on handling, false otherwise
*/
private boolean handleNextPacket(ByteCollector bc, byte buffer[])
throws IOException, I2PSessionException {
int len = in.read(buffer);
int bcsize = bc.getCurrentSize();
if (len != -1) {
bc.append(buffer, len);
} else if (bcsize == 0) {
// nothing left in the buffer, and read(..) got EOF (-1).
// the bart the
return false;
}
if ((bcsize < MAX_PACKET_SIZE) && (in.available() == 0)) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Runner Point d: " + hashCode());
try {
Thread.sleep(PACKET_DELAY);
} catch (InterruptedException e) {
_log.warn("wtf", e);
}
}
if ((bcsize >= MAX_PACKET_SIZE) || (in.available() == 0)) {
byte[] data = bc.startToByteArray(MAX_PACKET_SIZE);
if (data.length > 0) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Message size is: " + data.length);
boolean sent = sendBlock(data);
if (!sent) {
_log.error(getPrefix() + "Error sending message to peer. Killing socket runner");
errorOccurred();
return false;
}
}
}
return true;
}
public void run() {
byte[] buffer = new byte[MAX_PACKET_SIZE];
ByteCollector bc = new ByteCollector();
boolean sent = true;
boolean keepHandling = true;
int packetsHandled = 0;
try {
int len, bcsize;
// try {
while (true) {
len = in.read(buffer);
bcsize = bc.getCurrentSize();
if (len != -1) {
bc.append(buffer, len);
} else if (bcsize == 0) {
break;
}
if ((bcsize < MAX_PACKET_SIZE) && (in.available() == 0)) {
_log.debug("Runner Point d: " + this.hashCode());
try {
Thread.sleep(PACKET_DELAY);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if ((bcsize >= MAX_PACKET_SIZE) || (in.available() == 0)) {
byte[] data = bc.startToByteArray(MAX_PACKET_SIZE);
if (data.length > 0) {
_log.debug("Message size is: " + data.length);
sent = sendBlock(data);
if (!sent) {
_log.error("Error sending message to peer. Killing socket runner");
break;
}
}
}
// try {
while (keepHandling) {
keepHandling = handleNextPacket(bc, buffer);
packetsHandled++;
}
if ((bc.getCurrentSize() > 0) && sent) {
_log.error("A SCARY MONSTER HAS EATEN SOME DATA! " + "(input stream: " + in.hashCode() + "; "
if ((bc.getCurrentSize() > 0) && (packetsHandled > 1)) {
_log.error(getPrefix() + "A SCARY MONSTER HAS EATEN SOME DATA! " + "(input stream: "
+ in.hashCode() + "; "
+ "queue size: " + bc.getCurrentSize() + ")");
}
synchronized (flagLock) {
closed2 = true;
}
// } catch (IOException ex) {
// if (_log.shouldLog(Log.INFO))
// _log.info("Error reading and writing", ex);
// }
boolean sc;
synchronized (flagLock) {
sc = sendClose;
} // FIXME: Race here?
if (sc) {
_log.info("Sending close packet: " + outgoing);
byte[] packet = I2PSocketManager.makePacket((byte) (getMask(0x02)), remoteID, new byte[0]);
synchronized (manager.getSession()) {
sent = manager.getSession().sendMessage(remote, packet);
}
if (_log.shouldLog(Log.INFO))
_log.info(getPrefix() + "Sending close packet: (we started? " + outgoing + ") after reading " + _bytesRead + " and writing " + _bytesWritten);
byte[] packet = I2PSocketManager.makePacket(getMask(0x02), remoteID, new byte[0]);
boolean sent = manager.getSession().sendMessage(remote, packet);
if (!sent) {
_log.error("Error sending close packet to peer");
_log.error(getPrefix() + "Error sending close packet to peer");
errorOccurred();
}
}
manager.removeSocket(I2PSocketImpl.this);
} catch (InterruptedIOException ex) {
_log.error("BUG! read() operations should not timeout!", ex);
_log.error(getPrefix() + "BUG! read() operations should not timeout!", ex);
} catch (IOException ex) {
// WHOEVER removes this event on inconsistent
// state before fixing the inconsistent state (a
// reference on the socket in the socket manager
// etc.) will get hanged by me personally -- mihi
_log.error("Error running - **INCONSISTENT STATE!!!**", ex);
_log.error(getPrefix() + "Error running - **INCONSISTENT STATE!!!**", ex);
} catch (I2PException ex) {
_log.error("Error running - **INCONSISTENT STATE!!!**", ex);
_log.error(getPrefix() + "Error running - **INCONSISTENT STATE!!!**", ex);
}
}
private boolean sendBlock(byte data[]) throws I2PSessionException {
_log.debug("TIMING: Block to send for " + I2PSocketImpl.this.hashCode());
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "TIMING: Block to send for " + I2PSocketImpl.this.hashCode());
if (remoteID == null) {
_log.error("NULL REMOTEID");
_log.error(getPrefix() + "NULL REMOTEID");
return false;
}
byte[] packet = I2PSocketManager.makePacket(getMask(0x00), remoteID, data);
@@ -358,10 +482,10 @@ class I2PSocketImpl implements I2PSocket {
synchronized (flagLock) {
if (closed2) return false;
}
synchronized (manager.getSession()) {
sent = manager.getSession().sendMessage(remote, packet);
}
sent = manager.getSession().sendMessage(remote, packet);
return sent;
}
}
public String toString() { return "" + hashCode(); }
}

View File

@@ -4,7 +4,6 @@
*/
package net.i2p.client.streaming;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
@@ -21,6 +20,7 @@ import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.I2PSessionListener;
import net.i2p.data.Base64;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.Log;
@@ -40,11 +40,27 @@ public class I2PSocketManager implements I2PSessionListener {
private HashMap _outSockets;
private HashMap _inSockets;
private I2PSocketOptions _defaultOptions;
private long _acceptTimeout;
public static final short ACK = 0x51;
public static final short CLOSE_OUT = 0x52;
public static final short DATA_OUT = 0x50;
public static final short SYN = 0xA1;
public static final short CLOSE_IN = 0xA2;
public static final short DATA_IN = 0xA0;
public static final short CHAFF = 0xFF;
/**
* How long to wait for the client app to accept() before sending back CLOSE?
* This includes the time waiting in the queue. Currently set to 5 seconds.
*/
private static final long ACCEPT_TIMEOUT_DEFAULT = 5*1000;
public I2PSocketManager() {
_session = null;
_inSockets = new HashMap(16);
_outSockets = new HashMap(16);
_acceptTimeout = ACCEPT_TIMEOUT_DEFAULT;
}
public I2PSession getSession() {
@@ -55,15 +71,25 @@ public class I2PSocketManager implements I2PSessionListener {
_session = session;
if (session != null) session.setSessionListener(this);
}
/**
* How long should we wait for the client to .accept() a socket before
* sending back a NACK/Close?
*
* @param ms milliseconds to wait, maximum
*/
public void setAcceptTimeout(long ms) { _acceptTimeout = ms; }
public long getAcceptTimeout() { return _acceptTimeout; }
public void disconnected(I2PSession session) {
_log.error("Disconnected from the session");
_log.info("Disconnected from the session");
destroySocketManager();
}
public void errorOccurred(I2PSession session, String message, Throwable error) {
_log.error("Error occurred: [" + message + "]", error);
}
public void messageAvailable(I2PSession session, int msgId, long size) {
try {
I2PSocketImpl s;
@@ -77,157 +103,278 @@ public class I2PSocketManager implements I2PSessionListener {
return;
}
int type = msg[0] & 0xff;
String id = new String(new byte[] { msg[1], msg[2], msg[3]}, "ISO-8859-1");
String id = toString(new byte[] { msg[1], msg[2], msg[3]});
byte[] payload = new byte[msg.length - 4];
System.arraycopy(msg, 4, payload, 0, payload.length);
_log.debug("Message read: type = [" + Integer.toHexString(type) + "] id = [" + getReadableForm(id)
+ "] payload length: " + payload.length + "]");
synchronized (lock) {
switch (type) {
case 0x51:
// ACK outgoing
s = (I2PSocketImpl) _outSockets.get(id);
if (s == null) {
_log.warn("No socket responsible for ACK packet");
return;
}
if (payload.length == 3 && s.getRemoteID(false) == null) {
String newID = new String(payload, "ISO-8859-1");
s.setRemoteID(newID);
return;
} else {
if (payload.length != 3)
_log.warn("Ack packet had " + payload.length + " bytes");
else
_log.warn("Remote ID already exists? " + s.getRemoteID());
return;
}
case 0x52:
// disconnect outgoing
_log.debug("*Disconnect outgoing!");
try {
s = (I2PSocketImpl) _outSockets.get(id);
if (s != null) {
if (payload.length > 0) {
_log.debug("Disconnect packet had "
+ payload.length + " bytes");
}
if (s.getRemoteID(false) == null) {
s.setRemoteID(null); // Just to wake up socket
return;
}
s.internalClose();
_outSockets.remove(id);
}
return;
} catch (Exception t) {
_log.error("Ignoring error on disconnect", t);
}
case 0x50:
// packet send outgoing
_log.debug("*Packet send outgoing [" + payload.length + "]");
s = (I2PSocketImpl) _outSockets.get(id);
if (s != null) {
s.queueData(payload);
return;
} else {
_log.error("Null socket with data available");
throw new IllegalStateException("Null socket with data available");
}
case 0xA1:
// SYN incoming
_log.debug("*Syn!");
String newLocalID = makeID(_inSockets);
Destination d = new Destination();
d.readBytes(new ByteArrayInputStream(payload));
if (_serverSocket == null) {
// The app did not instantiate an I2PServerSocket
byte[] packet = makePacket((byte) 0x52, id, newLocalID.getBytes("ISO-8859-1"));
boolean replySentOk = false;
synchronized (_session) {
replySentOk = _session.sendMessage(d, packet);
}
if (!replySentOk) {
_log.error("Error sending close to " + d.calculateHash().toBase64()
+ " in response to a new con message", new Exception("Failed creation"));
}
return;
}
s = new I2PSocketImpl(d, this, false, newLocalID);
s.setRemoteID(id);
if (_serverSocket.getNewSocket(s)) {
_inSockets.put(newLocalID, s);
byte[] packet = makePacket((byte) 0x51, id, newLocalID.getBytes("ISO-8859-1"));
boolean replySentOk = false;
synchronized (_session) {
replySentOk = _session.sendMessage(d, packet);
}
if (!replySentOk) {
_log.error("Error sending reply to " + d.calculateHash().toBase64()
+ " in response to a new con message", new Exception("Failed creation"));
s.internalClose();
}
} else {
byte[] packet = (" " + id).getBytes("ISO-8859-1");
packet[0] = 0x52;
boolean nackSent = session.sendMessage(d, packet);
if (!nackSent) {
_log.error("Error sending NACK for session creation");
}
s.internalClose();
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Message read: type = [" + Integer.toHexString(type)
+ "] id = [" + getReadableForm(id)
+ "] payload length: [" + payload.length + "]");
switch (type) {
case ACK:
ackAvailable(id, payload);
return;
case 0xA2:
// disconnect incoming
_log.debug("*Disconnect incoming!");
try {
s = (I2PSocketImpl) _inSockets.get(id);
if (payload.length == 0 && s != null) {
s.internalClose();
_inSockets.remove(id);
return;
} else {
if (payload.length > 0) _log.warn("Disconnect packet had " + payload.length + " bytes");
return;
}
} catch (Exception t) {
_log.error("Ignoring error on disconnect", t);
return;
}
case 0xA0:
// packet send incoming
_log.debug("*Packet send incoming [" + payload.length + "]");
s = (I2PSocketImpl) _inSockets.get(id);
if (s != null) {
s.queueData(payload);
return;
} else {
_log.error("Null socket with data available");
throw new IllegalStateException("Null socket with data available");
}
case 0xFF:
case CLOSE_OUT:
disconnectAvailable(id, payload);
return;
case DATA_OUT:
sendOutgoingAvailable(id, payload);
return;
case SYN:
synIncomingAvailable(id, payload, session);
return;
case CLOSE_IN:
disconnectIncoming(id, payload);
return;
case DATA_IN:
sendIncoming(id, payload);
case CHAFF:
// ignore
return;
}
_log.error("\n\n=============== Unknown packet! " + "============" + "\nType: " + (int) type
+ "\nID: " + getReadableForm(id) + "\nBase64'ed Data: " + Base64.encode(payload)
+ "\n\n\n");
if (id != null) {
_inSockets.remove(id);
_outSockets.remove(id);
}
default:
handleUnknown(type, id, payload);
return;
}
} catch (I2PException ise) {
_log.error("Error processing", ise);
} catch (IOException ioe) {
_log.error("Error processing", ioe);
} catch (IllegalStateException ise) {
_log.debug("Error processing", ise);
}
}
/**
* We've received an ACK packet (hopefully, in response to a SYN that we
* recently sent out). Notify the associated I2PSocket that we now have
* the remote stream ID (which should get things going, since the handshake
* is complete).
*
*/
private void ackAvailable(String id, byte payload[]) {
I2PSocketImpl s = null;
synchronized (lock) {
s = (I2PSocketImpl) _outSockets.get(id);
}
if (s == null) {
_log.warn("No socket responsible for ACK packet");
return;
}
String remoteId = null;
remoteId = s.getRemoteID(false);
if ( (payload.length == 3) && (remoteId == null) ) {
String newID = toString(payload);
s.setRemoteID(newID);
return;
} else {
// (payload.length != 3 || getRemoteId != null)
if (_log.shouldLog(Log.WARN)) {
if (payload.length != 3)
_log.warn("Ack packet had " + payload.length + " bytes");
else
_log.warn("Remote ID already exists? " + remoteId);
}
return;
}
}
/**
* We received a disconnect packet, telling us to tear down the specified
* stream.
*/
private void disconnectAvailable(String id, byte payload[]) {
I2PSocketImpl s = null;
synchronized (lock) {
s = (I2PSocketImpl) _outSockets.get(id);
}
_log.debug("*Disconnect outgoing for socket " + s);
try {
if (s != null) {
if (payload.length > 0) {
_log.debug("Disconnect packet had "
+ payload.length + " bytes");
}
if (s.getRemoteID(false) == null) {
s.setRemoteID(null); // Just to wake up socket
return;
}
s.internalClose();
synchronized (lock) {
_outSockets.remove(id);
}
}
return;
} catch (Exception t) {
_log.error("Ignoring error on disconnect for socket " + s, t);
}
}
/**
* We've received data on a stream we created - toss the data onto
* the socket for handling.
*
* @throws IllegalStateException if the socket isn't open or isn't known
*/
private void sendOutgoingAvailable(String id, byte payload[]) throws IllegalStateException {
I2PSocketImpl s = null;
synchronized (lock) {
s = (I2PSocketImpl) _outSockets.get(id);
}
// packet send outgoing
if (_log.shouldLog(Log.DEBUG))
_log.debug("*Packet send outgoing [" + payload.length + "] for socket " + s);
if (s != null) {
s.queueData(payload);
return;
} else {
_log.error("Null socket with data available");
throw new IllegalStateException("Null socket with data available");
}
}
/**
* We've received a SYN packet (a request for a new stream). If the client has
* said they want incoming sockets (by retrieving the serverSocket), the stream
* will be ACKed, but if they have not, they'll be NACKed)
*
* @throws DataFormatException if the destination in the SYN was invalid
* @throws I2PSessionException if there was an I2P error sending the ACK or NACK
*/
private void synIncomingAvailable(String id, byte payload[], I2PSession session)
throws DataFormatException, I2PSessionException {
Destination d = new Destination();
d.fromByteArray(payload);
I2PSocketImpl s = null;
boolean acceptConnections = (_serverSocket != null);
String newLocalID = null;
synchronized (lock) {
newLocalID = makeID(_inSockets);
if (acceptConnections) {
s = new I2PSocketImpl(d, this, false, newLocalID);
s.setRemoteID(id);
}
}
_log.debug("*Syn! for socket " + s);
if (!acceptConnections) {
// The app did not instantiate an I2PServerSocket
byte[] packet = makePacket((byte) CLOSE_OUT, id, toBytes(newLocalID));
boolean replySentOk = false;
synchronized (_session) {
replySentOk = _session.sendMessage(d, packet);
}
if (!replySentOk) {
_log.error("Error sending close to " + d.calculateHash().toBase64()
+ " in response to a new con message",
new Exception("Failed creation"));
}
return;
}
if (_serverSocket.addWaitForAccept(s, _acceptTimeout)) {
_inSockets.put(newLocalID, s);
byte[] packet = makePacket((byte) ACK, id, toBytes(newLocalID));
boolean replySentOk = false;
replySentOk = _session.sendMessage(d, packet);
if (!replySentOk) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error sending reply to " + d.calculateHash().toBase64()
+ " in response to a new con message for socket " + s,
new Exception("Failed creation"));
s.internalClose();
}
} else {
// timed out or serverSocket closed
byte[] packet = toBytes(" " + id);
packet[0] = CLOSE_OUT;
boolean nackSent = session.sendMessage(d, packet);
if (!nackSent) {
_log.warn("Error sending NACK for session creation for socket " + s);
}
s.internalClose();
}
return;
}
/**
* We've received a disconnect for a socket we didn't initiate, so kill
* the socket.
*
*/
private void disconnectIncoming(String id, byte payload[]) {
I2PSocketImpl s = null;
synchronized (lock) {
s = (I2PSocketImpl) _inSockets.get(id);
if (payload.length == 0 && s != null) {
_inSockets.remove(id);
}
}
_log.debug("*Disconnect incoming for socket " + s);
try {
if (payload.length == 0 && s != null) {
s.internalClose();
return;
} else {
if ( (payload.length > 0) && (_log.shouldLog(Log.ERROR)) )
_log.error("Disconnect packet had " + payload.length + " bytes");
if (s != null)
s.internalClose();
return;
}
} catch (Exception t) {
_log.error("Ignoring error on disconnect", t);
return;
}
}
/**
* We've received data on a stream we received - toss the data onto
* the socket for handling.
*
* @throws IllegalStateException if the socket isn't open or isn't known
*/
private void sendIncoming(String id, byte payload[]) {
I2PSocketImpl s = null;
synchronized (lock) {
s = (I2PSocketImpl) _inSockets.get(id);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("*Packet send incoming [" + payload.length + "] for socket " + s);
if (s != null) {
s.queueData(payload);
return;
} else {
_log.info("Null socket with data available");
throw new IllegalStateException("Null socket with data available");
}
}
/**
* Unknown packet. moo.
*
*/
private void handleUnknown(int type, String id, byte payload[]) {
_log.error("\n\n=============== Unknown packet! " + "============"
+ "\nType: " + (int) type
+ "\nID: " + getReadableForm(id)
+ "\nBase64'ed Data: " + Base64.encode(payload)
+ "\n\n\n");
if (id != null) {
synchronized (lock) {
_inSockets.remove(id);
_outSockets.remove(id);
}
}
}
public void reportAbuse(I2PSession session, int severity) {
_log.error("Abuse reported [" + severity + "]");
}
@@ -258,59 +405,77 @@ public class I2PSocketManager implements I2PSessionListener {
* @throws InterruptedIOException if the connection timeouts
* @throws I2PException if there is some other I2P-related problem
*/
public I2PSocket connect(Destination peer, I2PSocketOptions options) throws I2PException, ConnectException, NoRouteToHostException, InterruptedIOException {
public I2PSocket connect(Destination peer, I2PSocketOptions options)
throws I2PException, ConnectException,
NoRouteToHostException, InterruptedIOException {
String localID, lcID;
I2PSocketImpl s;
synchronized (lock) {
localID = makeID(_outSockets);
lcID = getReadableForm(localID);
s = new I2PSocketImpl(peer, this, true, localID);
_outSockets.put(s.getLocalID(), s);
_outSockets.put(localID, s);
}
try {
ByteArrayOutputStream pubkey = new ByteArrayOutputStream();
_session.getMyDestination().writeBytes(pubkey);
String remoteID;
byte[] packet = makePacket((byte) 0xA1, localID, pubkey.toByteArray());
byte[] packet = makePacket((byte) SYN, localID, pubkey.toByteArray());
boolean sent = false;
synchronized (_session) {
sent = _session.sendMessage(peer, packet);
}
sent = _session.sendMessage(peer, packet);
if (!sent) {
_log.info("Unable to send & receive ack for SYN packet");
_log.info("Unable to send & receive ack for SYN packet for socket " + s);
synchronized (lock) {
_outSockets.remove(s.getLocalID());
}
throw new I2PException("Error sending through I2P network");
}
remoteID = s.getRemoteID(true, options.getConnectTimeout());
if (remoteID == null) { throw new ConnectException("Connection refused by peer"); }
if ("".equals(remoteID)) { throw new NoRouteToHostException("Unable to reach peer"); }
_log.debug("TIMING: s given out for remoteID " + getReadableForm(remoteID));
if (remoteID == null)
throw new ConnectException("Connection refused by peer for socket " + s);
if ("".equals(remoteID))
throw new NoRouteToHostException("Unable to reach peer for socket " + s);
if (_log.shouldLog(Log.DEBUG))
_log.debug("TIMING: s given out for remoteID "
+ getReadableForm(remoteID) + " for socket " + s);
return s;
} catch (InterruptedIOException ioe) {
_log.error("Timeout waiting for ack from syn for id " + getReadableForm(lcID), ioe);
if (_log.shouldLog(Log.ERROR))
_log.error("Timeout waiting for ack from syn for id "
+ getReadableForm(lcID) + " for socket " + s, ioe);
synchronized (lock) {
_outSockets.remove(s.getLocalID());
}
s.internalClose();
throw new InterruptedIOException("Timeout waiting for ack");
} catch (ConnectException ex) {
s.internalClose();
throw ex;
} catch (NoRouteToHostException ex) {
s.internalClose();
throw ex;
} catch (IOException ex) {
_log.error("Error sending syn on id " + getReadableForm(lcID), ex);
if (_log.shouldLog(Log.ERROR))
_log.error("Error sending syn on id " + getReadableForm(lcID) + " for socket " + s, ex);
synchronized (lock) {
_outSockets.remove(s.getLocalID());
}
s.internalClose();
throw new I2PException("Unhandled IOException occurred");
} catch (I2PException ex) {
_log.info("Error sending syn on id " + getReadableForm(lcID), ex);
if (_log.shouldLog(Log.INFO))
_log.info("Error sending syn on id " + getReadableForm(lcID) + " for socket " + s, ex);
synchronized (lock) {
_outSockets.remove(s.getLocalID());
}
s.internalClose();
throw ex;
} catch (Exception e) {
s.internalClose();
_log.error("Unhandled error connecting", e);
throw new ConnectException("Unhandled error connecting: " + e.getMessage());
}
}
@@ -324,7 +489,8 @@ public class I2PSocketManager implements I2PSessionListener {
* @throws InterruptedIOException if the connection timeouts
* @throws I2PException if there is some other I2P-related problem
*/
public I2PSocket connect(Destination peer) throws I2PException, ConnectException, NoRouteToHostException, InterruptedIOException {
public I2PSocket connect(Destination peer) throws I2PException, ConnectException,
NoRouteToHostException, InterruptedIOException {
return connect(peer, null);
}
@@ -334,14 +500,9 @@ public class I2PSocketManager implements I2PSessionListener {
*
*/
public void destroySocketManager() {
try {
if (_serverSocket != null) {
_serverSocket.close();
_serverSocket = null;
}
} catch (I2PException ex) {
_log.error("Error closing I2PServerSocket", ex);
if (_serverSocket != null) {
_serverSocket.close();
_serverSocket = null;
}
synchronized (lock) {
@@ -353,8 +514,9 @@ public class I2PSocketManager implements I2PSessionListener {
while (iter.hasNext()) {
id = (String)iter.next();
sock = (I2PSocketImpl)_inSockets.get(id);
_log.debug("Closing inSocket \""
+ getReadableForm(sock.getLocalID()) + "\"");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Closing inSocket \""
+ getReadableForm(sock.getLocalID()) + "\"");
sock.internalClose();
}
@@ -362,8 +524,9 @@ public class I2PSocketManager implements I2PSessionListener {
while (iter.hasNext()) {
id = (String)iter.next();
sock = (I2PSocketImpl)_outSockets.get(id);
_log.debug("Closing outSocket \""
+ getReadableForm(sock.getLocalID()) + "\"");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Closing outSocket \""
+ getReadableForm(sock.getLocalID()) + "\"");
sock.internalClose();
}
}
@@ -406,7 +569,7 @@ public class I2PSocketManager implements I2PSessionListener {
*/
public boolean ping(Destination peer, long timeoutMs) {
try {
return _session.sendMessage(peer, new byte[] { (byte) 0xFF});
return _session.sendMessage(peer, new byte[] { (byte) CHAFF});
} catch (I2PException ex) {
_log.error("I2PException:", ex);
return false;
@@ -415,8 +578,8 @@ public class I2PSocketManager implements I2PSessionListener {
public void removeSocket(I2PSocketImpl sock) {
synchronized (lock) {
_log.debug("Removing socket \""
+ getReadableForm(sock.getLocalID()) + "\"");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Removing socket \"" + getReadableForm(sock.getLocalID()) + "\" [" + sock + "]");
_inSockets.remove(sock.getLocalID());
_outSockets.remove(sock.getLocalID());
lock.notify();
@@ -424,14 +587,9 @@ public class I2PSocketManager implements I2PSessionListener {
}
public static String getReadableForm(String id) {
try {
if (id == null) return "(null)";
if (id.length() != 3) return "Bogus";
return Base64.encode(id.getBytes("ISO-8859-1"));
} catch (UnsupportedEncodingException ex) {
ex.printStackTrace();
return null;
}
if (id == null) return "(null)";
if (id.length() != 3) return "Bogus";
return Base64.encode(toBytes(id));
}
/**
@@ -439,22 +597,17 @@ public class I2PSocketManager implements I2PSessionListener {
*
* @param uniqueIn map of already known local IDs so we don't collide. WARNING - NOT THREADSAFE!
*/
public static String makeID(HashMap uniqueIn) {
private static String makeID(HashMap uniqueIn) {
String newID;
try {
do {
int id = (int) (Math.random() * 16777215 + 1);
byte[] nid = new byte[3];
nid[0] = (byte) (id / 65536);
nid[1] = (byte) ((id / 256) % 256);
nid[2] = (byte) (id % 256);
newID = new String(nid, "ISO-8859-1");
} while (uniqueIn.get(newID) != null);
return newID;
} catch (UnsupportedEncodingException ex) {
ex.printStackTrace();
return null;
}
do {
int id = (int) (Math.random() * 16777215 + 1);
byte[] nid = new byte[3];
nid[0] = (byte) (id / 65536);
nid[1] = (byte) ((id / 256) % 256);
nid[2] = (byte) (id % 256);
newID = toString(nid);
} while (uniqueIn.get(newID) != null);
return newID;
}
/**
@@ -462,17 +615,28 @@ public class I2PSocketManager implements I2PSessionListener {
* the given payload
*/
public static byte[] makePacket(byte type, String id, byte[] payload) {
byte[] packet = new byte[payload.length + 4];
packet[0] = type;
byte[] temp = toBytes(id);
if (temp.length != 3) throw new RuntimeException("Incorrect ID length: " + temp.length);
System.arraycopy(temp, 0, packet, 1, 3);
System.arraycopy(payload, 0, packet, 4, payload.length);
return packet;
}
private static final String toString(byte data[]) {
try {
byte[] packet = new byte[payload.length + 4];
packet[0] = type;
byte[] temp = id.getBytes("ISO-8859-1");
if (temp.length != 3) throw new RuntimeException("Incorrect ID length: " + temp.length);
System.arraycopy(temp, 0, packet, 1, 3);
System.arraycopy(payload, 0, packet, 4, payload.length);
return packet;
} catch (UnsupportedEncodingException ex) {
if (_log.shouldLog(Log.ERROR)) _log.error("Error building the packet", ex);
return new byte[0];
return new String(data, "ISO-8859-1");
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("WTF! iso-8859-1 isn't supported?");
}
}
private static final byte[] toBytes(String str) {
try {
return str.getBytes("ISO-8859-1");
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("WTF! iso-8859-1 isn't supported?");
}
}
}

View File

@@ -2,7 +2,6 @@ package net.i2p.client.streaming;
/**
* Define the configuration for streaming and verifying data on the socket.
* No options available...
*
*/
public class I2PSocketOptions {

View File

@@ -1,17 +1,18 @@
package net.i2p.netmonitor;
import net.i2p.data.RouterInfo;
import net.i2p.util.Log;
import net.i2p.util.Clock;
import java.util.Properties;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Locale;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.data.RouterInfo;
import net.i2p.util.Clock;
import net.i2p.util.Log;
/**
* Pull out important data from the published routerInfo and stash it away

View File

@@ -1,18 +1,18 @@
package net.i2p.netmonitor;
import net.i2p.util.Log;
import net.i2p.util.I2PThread;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Map;
import java.util.HashMap;
import java.io.IOException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Main driver for the app that harvests data about the performance of the network,

View File

@@ -1,19 +1,17 @@
package net.i2p.netmonitor;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.io.IOException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.data.DataFormatException;
import net.i2p.data.RouterInfo;
import net.i2p.util.Clock;
import net.i2p.util.Log;
/**
* Active process that drives the monitoring by periodically rading the

View File

@@ -1,16 +1,16 @@
package net.i2p.netmonitor;
import net.i2p.util.Clock;
import net.i2p.util.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Set;
import java.util.TreeMap;
import net.i2p.util.Clock;
import net.i2p.util.Log;
/**
* coordinate the data points summarizing the performance of a particular peer

View File

@@ -1,20 +1,17 @@
package net.i2p.netmonitor;
import net.i2p.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.TreeSet;
import java.util.Set;
import java.util.Locale;
import java.util.Date;
import java.util.StringTokenizer;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import net.i2p.util.Log;
/**
* Load up the peer summary
@@ -33,57 +30,61 @@ class PeerSummaryReader {
PeerSummary summary = null;
String curDescription = null;
List curArgs = null;
while ((line = reader.readLine()) != null) {
if (line.startsWith("peer\t")) {
String name = line.substring("peer\t".length()).trim();
summary = monitor.getSummary(name);
if (summary == null)
summary = new PeerSummary(name);
} else if (line.startsWith("## ")) {
curDescription = line.substring("## ".length()).trim();
curArgs = new ArrayList(4);
} else if (line.startsWith("# param ")) {
int start = line.indexOf(':');
String arg = line.substring(start+1).trim();
curArgs.add(arg);
} else {
StringTokenizer tok = new StringTokenizer(line);
String name = tok.nextToken();
try {
long when = getTime(tok.nextToken());
boolean isDouble = false;
List argVals = new ArrayList(curArgs.size());
while (tok.hasMoreTokens()) {
String val = (String)tok.nextToken();
if (val.indexOf('.') >= 0) {
argVals.add(new Double(val));
isDouble = true;
} else {
argVals.add(new Long(val));
try {
while ((line = reader.readLine()) != null) {
if (line.startsWith("peer\t")) {
String name = line.substring("peer\t".length()).trim();
summary = monitor.getSummary(name);
if (summary == null)
summary = new PeerSummary(name);
} else if (line.startsWith("## ")) {
curDescription = line.substring("## ".length()).trim();
curArgs = new ArrayList(4);
} else if (line.startsWith("# param ")) {
int start = line.indexOf(':');
String arg = line.substring(start+1).trim();
curArgs.add(arg);
} else {
StringTokenizer tok = new StringTokenizer(line);
String name = tok.nextToken();
try {
long when = getTime(tok.nextToken());
boolean isDouble = false;
List argVals = new ArrayList(curArgs.size());
while (tok.hasMoreTokens()) {
String val = (String)tok.nextToken();
if (val.indexOf('.') >= 0) {
argVals.add(new Double(val));
isDouble = true;
} else {
argVals.add(new Long(val));
}
}
String valDescriptions[] = new String[curArgs.size()];
for (int i = 0; i < curArgs.size(); i++)
valDescriptions[i] = (String)curArgs.get(i);
if (isDouble) {
double values[] = new double[argVals.size()];
for (int i = 0; i < argVals.size(); i++)
values[i] = ((Double)argVals.get(i)).doubleValue();
summary.addData(name, curDescription, valDescriptions, when, values);
} else {
long values[] = new long[argVals.size()];
for (int i = 0; i < argVals.size(); i++)
values[i] = ((Long)argVals.get(i)).longValue();
summary.addData(name, curDescription, valDescriptions, when, values);
}
} catch (ParseException pe) {
_log.error("Error parsing the data line [" + line + "]", pe);
} catch (NumberFormatException nfe) {
_log.error("Error parsing the data line [" + line + "]", nfe);
}
String valDescriptions[] = new String[curArgs.size()];
for (int i = 0; i < curArgs.size(); i++)
valDescriptions[i] = (String)curArgs.get(i);
if (isDouble) {
double values[] = new double[argVals.size()];
for (int i = 0; i < argVals.size(); i++)
values[i] = ((Double)argVals.get(i)).doubleValue();
summary.addData(name, curDescription, valDescriptions, when, values);
} else {
long values[] = new long[argVals.size()];
for (int i = 0; i < argVals.size(); i++)
values[i] = ((Long)argVals.get(i)).longValue();
summary.addData(name, curDescription, valDescriptions, when, values);
}
} catch (ParseException pe) {
_log.error("Error parsing the data line [" + line + "]", pe);
} catch (NumberFormatException nfe) {
_log.error("Error parsing the data line [" + line + "]", nfe);
}
}
}
} catch (Exception e) {
_log.error("Error handling the data", e);
throw new IOException("Error parsing the data");
}
if (summary == null)
return;
summary.coallesceData(monitor.getSummaryDurationHours() * 60*60*1000);

View File

@@ -1,15 +1,15 @@
package net.i2p.netmonitor;
import net.i2p.util.Log;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import java.util.Set;
import java.util.Locale;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.TreeSet;
import net.i2p.util.Log;
/**
* Dump various peer summaries to disk (so external apps (or good ol' vi) can

View File

@@ -1,14 +1,15 @@
package net.i2p.netmonitor;
import net.i2p.util.Log;
import java.io.IOException;
import java.io.File;
import java.io.FileInputStream;
import java.util.List;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import net.i2p.util.Log;
/**
* Load up the StatGroups from the location specified to configure the data harvester.
* The stat groups are formatted in a simple properties file style, e.g.: <pre>

View File

@@ -1,28 +1,24 @@
package net.i2p.netmonitor.gui;
import net.i2p.netmonitor.PeerSummary;
import net.i2p.netmonitor.PeerStat;
import net.i2p.util.Log;
import org.jfree.data.XYSeries;
import org.jfree.data.XYSeriesCollection;
import org.jfree.data.MovingAverage;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.renderer.XYLineAndShapeRenderer;
import org.jfree.chart.renderer.XYItemRenderer;
import org.jfree.chart.renderer.XYDotRenderer;
import java.awt.Color;
import java.awt.Font;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Iterator;
import javax.swing.JPanel;
import java.awt.Font;
import java.awt.Color;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.XYItemRenderer;
import org.jfree.chart.renderer.XYLineAndShapeRenderer;
import org.jfree.data.XYSeries;
import org.jfree.data.XYSeriesCollection;
import net.i2p.netmonitor.PeerStat;
import net.i2p.util.Log;
class JFreeChartAdapter {
private final static Log _log = new Log(JFreeChartAdapter.class);

View File

@@ -1,20 +1,15 @@
package net.i2p.netmonitor.gui;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;
import javax.swing.JLabel;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import net.i2p.util.Log;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import org.jfree.chart.ChartPanel;
import net.i2p.util.Log;
/**
* Render the graph and legend
*

View File

@@ -1,14 +1,13 @@
package net.i2p.netmonitor.gui;
import net.i2p.netmonitor.NetMonitor;
import net.i2p.netmonitor.PeerSummary;
import net.i2p.netmonitor.PeerStat;
import net.i2p.util.Log;
import net.i2p.util.I2PThread;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import net.i2p.netmonitor.NetMonitor;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Coordinate the visualization of the network monitor. <p />

View File

@@ -5,7 +5,6 @@ import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import java.awt.Dimension;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;

View File

@@ -1,9 +1,5 @@
package net.i2p.netmonitor.gui;
import java.awt.Color;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.swing.JPanel;
import javax.swing.JTextArea;

View File

@@ -1,20 +1,15 @@
package net.i2p.netmonitor.gui;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import net.i2p.data.Destination;
import net.i2p.util.Log;
import net.i2p.netmonitor.PeerSummary;
import net.i2p.util.Log;
/**
* Configure how we want to render a particular peerSummary in the GUI

View File

@@ -5,25 +5,21 @@ import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.TreeMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import net.i2p.util.Log;
import net.i2p.netmonitor.PeerStat;
import net.i2p.netmonitor.PeerSummary;
import net.i2p.util.Log;
class PeerPlotConfigPane extends JPanel implements PeerPlotConfig.UpdateListener {
private final static Log _log = new Log(PeerPlotConfigPane.class);

View File

@@ -0,0 +1,58 @@
using System.Reflection;
using System.Runtime.CompilerServices;
//
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
//
[assembly: AssemblyTitle("")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
//
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.*")]
//
// In order to sign your assembly you must specify a key to use. Refer to the
// Microsoft .NET Framework documentation for more information on assembly signing.
//
// Use the attributes below to control which key is used for signing.
//
// Notes:
// (*) If no key is specified, the assembly is not signed.
// (*) KeyName refers to a key that has been installed in the Crypto Service
// Provider (CSP) on your machine. KeyFile refers to a file which contains
// a key.
// (*) If the KeyFile and the KeyName values are both specified, the
// following processing occurs:
// (1) If the KeyName can be found in the CSP, that key is used.
// (2) If the KeyName does not exist and the KeyFile does exist, the key
// in the KeyFile is installed into the CSP and used.
// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
// When specifying the KeyFile, the location of the KeyFile should be
// relative to the project output directory which is
// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
// located in the project directory, you would specify the AssemblyKeyFile
// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
// documentation for more information on this.
//
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]

View File

@@ -0,0 +1,50 @@
using System;
using System.Net;
using System.Threading;
using System.Text;
using System.Collections;
namespace SAM.NET
{
class SAMTester
{
[STAThread]
static void Main(string[] args)
{
new SAMTester();
}
public SAMTester ()
{
SAMConnection connection1 = new SAMConnection(IPAddress.Parse("127.0.0.1"),7656);
SAMSession session1 = new SAMSession(connection1,SAM.NET.SamSocketType.Stream,"alice");
SAMConnection connection2 = new SAMConnection(IPAddress.Parse("127.0.0.1"),7656);
SAMSession session2 = new SAMSession(connection2,SAM.NET.SamSocketType.Stream,"bob");
SAMStream stream1 = new SAMStream(connection1,session1,233);
stream1.Connect(session2.getKey());
//Wait till we are connected to destination
while (!stream1.isConnected)
Thread.Sleep(1000);
//Send some bytes
stream1.Write(Encoding.ASCII.GetBytes(DateTime.Now.ToLongTimeString() + "Hi!!!!!!"));
//Wait till a stream magically appears on the other side
while (session2.getStreams().Count == 0) Thread.Sleep(1000);
Thread.Sleep(1000);
foreach (SAMStream stream in session2.getStreams().Values)
{
Console.WriteLine("Text received on " + stream.getID() + " at " + DateTime.Now.ToLongTimeString());
Console.WriteLine(Encoding.ASCII.GetString(stream.ReadToEnd()));
stream.Close();
}
stream1.Close();
connection1.Close();
connection2.Close();
}
}
}

View File

@@ -0,0 +1,58 @@
using System.Reflection;
using System.Runtime.CompilerServices;
//
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
//
[assembly: AssemblyTitle("")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
//
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.*")]
//
// In order to sign your assembly you must specify a key to use. Refer to the
// Microsoft .NET Framework documentation for more information on assembly signing.
//
// Use the attributes below to control which key is used for signing.
//
// Notes:
// (*) If no key is specified, the assembly is not signed.
// (*) KeyName refers to a key that has been installed in the Crypto Service
// Provider (CSP) on your machine. KeyFile refers to a file which contains
// a key.
// (*) If the KeyFile and the KeyName values are both specified, the
// following processing occurs:
// (1) If the KeyName can be found in the CSP, that key is used.
// (2) If the KeyName does not exist and the KeyFile does exist, the key
// in the KeyFile is installed into the CSP and used.
// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
// When specifying the KeyFile, the location of the KeyFile should be
// relative to the project output directory which is
// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
// located in the project directory, you would specify the AssemblyKeyFile
// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
// documentation for more information on this.
//
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]

View File

@@ -0,0 +1,271 @@
using System;
using System.Net.Sockets;
using System.Text;
using System.Net;
using System.IO;
using System.Collections;
using System.Threading;
namespace SAM.NET
{
public enum SamSocketType
{
Stream,
Datagram,
Raw
}
public class SAMConnection
{
private const string propertyMinVersion = "1.0";
private const string propertyMaxVersion = "1.0";
private Socket _sock;
private NetworkStream _sockStream;
private StreamReader _sockStreamIn;
private StreamWriter _sockStreamOut;
public SAMConnection(IPAddress routerIP, int port)
{
_sock = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
IPEndPoint rEP = new IPEndPoint(routerIP,port);
_sock.Connect(rEP);
_sockStream = new NetworkStream(_sock);
_sockStreamIn = new StreamReader(_sockStream);
_sockStreamOut = new StreamWriter(_sockStream);
try
{
sendVersion(propertyMinVersion,propertyMinVersion);
}
catch (Exception e)
{
_sock.Close();
throw (new Exception("No SAM for you :("));
}
}
private void sendVersion(string min, string max)
{
_sockStreamOut.WriteLine("HELLO VERSION MIN=" + propertyMinVersion + " MAX=" + propertyMaxVersion);
_sockStreamOut.Flush();
Hashtable response = SAMUtil.parseKeyValues(_sockStreamIn.ReadLine(),2);
if (response["RESULT"].ToString() != "OK") throw (new Exception("Version mismatch"));
}
public StreamWriter getOutputStream()
{
return _sockStreamOut;
}
public StreamReader getInputStream()
{
return _sockStreamIn;
}
public NetworkStream getStream()
{
return _sockStream;
}
public void Close()
{
_sock.Close();
}
}
/*
* Creating a SAMSession object will automatically:
* 1) create a sesion on SAM
* 1) query for the base64key
* 2) start a listening thread to catch all stream commands
*/
public class SAMSession
{
private Hashtable _streams;
private string _sessionKey;
public SAMSession (SAMConnection connection, SamSocketType type, string destination)
{
_streams = new Hashtable();
StreamWriter writer = connection.getOutputStream();
StreamReader reader = connection.getInputStream();
writer.WriteLine("SESSION CREATE STYLE=STREAM DESTINATION=" + destination);
writer.Flush();
Hashtable response = SAMUtil.parseKeyValues(reader.ReadLine(),2);
if (response["RESULT"].ToString() != "OK")
{
throw (new Exception(response["MESSAGE"].ToString()));
}
else
{
writer.WriteLine("NAMING LOOKUP NAME=ME");
writer.Flush();
response = SAMUtil.parseKeyValues(reader.ReadLine(),2);
_sessionKey = response["VALUE"].ToString();
SAMSessionListener listener = new SAMSessionListener(connection,this,_streams);
new Thread(new ThreadStart(listener.startListening)).Start();
}
}
public void addStream(SAMStream stream)
{
_streams.Add(stream.getID(),stream);
}
public string getKey()
{
return _sessionKey;
}
public Hashtable getStreams()
{
return _streams;
}
}
public class SAMSessionListener
{
private Hashtable _streams;
private SAMConnection _connection;
private SAMSession _session;
private bool stayAlive = true;
public SAMSessionListener(SAMConnection connection,SAMSession session, Hashtable streams)
{
_streams = streams;
_connection = connection;
_session = session;
}
public void startListening()
{
StreamReader reader = _connection.getInputStream();
while (stayAlive)
{
string response = reader.ReadLine();
if (response.StartsWith("STREAM STATUS"))
{
Hashtable values = SAMUtil.parseKeyValues(response,2);
SAMStream theStream = (SAMStream)_streams[int.Parse(values["ID"].ToString())];
if (theStream != null) theStream.ReceivedStatus(values);
}
if (response.StartsWith("STREAM CONNECTED"))
{
Hashtable values = SAMUtil.parseKeyValues(response,2);
SAMStream theStream = (SAMStream)_streams[int.Parse(values["ID"].ToString())];
if (theStream != null) theStream.isConnected = true;
}
if (response.StartsWith("STREAM RECEIVED"))
{
Hashtable values = SAMUtil.parseKeyValues(response,2);
int streamID = int.Parse(values["ID"].ToString());
SAMStream theStream = (SAMStream)_streams[streamID];
if (theStream == null) new SAMStream(_connection,_session,streamID);
theStream = (SAMStream)_streams[streamID];
theStream.ReceivedData(int.Parse(values["SIZE"].ToString()));
}
if (response.StartsWith("STREAM CLOSE"))
{
Hashtable values = SAMUtil.parseKeyValues(response,2);
SAMStream theStream = (SAMStream)_streams[int.Parse(values["ID"].ToString())];
if (theStream != null) theStream.isConnected = false;
}
}
}
}
public class SAMStream
{
private int _ID;
private byte[] _data;
private int _position=0;
private int _size=0;
private SAMSession _session;
private SAMConnection _connection;
public bool isConnected=false;
public SAMStream (SAMConnection connection,SAMSession session, int ID)
{
_data = new byte[100000]; //FIXME: change to non-static structure for storing stream data
_ID = ID;
_connection = connection;
_session = session;
_session.addStream(this);
}
public void Connect(string destination)
{
StreamWriter writer = _connection.getOutputStream();
writer.WriteLine("STREAM CONNECT ID=" + _ID.ToString() + " DESTINATION=" + destination);
writer.Flush();
}
public void ReceivedData(int size) //FIXME: WTF is going on when reading the payload here? All zeros and way too many of them.
{
NetworkStream stream = _connection.getStream();
int bytesRead = stream.Read(_data,_size,size);
_size = _size + bytes;
}
public void ReceivedStatus(Hashtable response)
{
if (response["RESULT"].ToString() != "OK")
{
throw (new Exception(response["RESULT"].ToString()));
}
else
{
isConnected = true;
}
}
public int getID() {return _ID;}
public bool DataAvailable()
{
return _position != _size;
}
public void Write(byte[] buf)
{
NetworkStream stream = _connection.getStream();
int sent = 0;
while (sent < buf.Length)
{
int toSend = Math.Min(buf.Length - sent,32768);
string header = "STREAM SEND ID=" + _ID.ToString() + " SIZE=" + toSend.ToString() + "\n";
byte[] headerbytes = Encoding.ASCII.GetBytes(header);
stream.Write(headerbytes,0,headerbytes.Length);
stream.Write(buf,sent,toSend);
sent = sent + toSend;
}
}
public byte[] ReadToEnd()
{
byte[] ret = new byte[_size - _position];
Array.Copy(_data,_position,ret,0,_size - _position);
_position = _size;
return ret;
}
public void Close()
{
StreamWriter writer = _connection.getOutputStream();
writer.WriteLine("STREAM CLOSE " + _ID.ToString());
writer.Flush();
}
}
public class SAMUtil
{
public static Hashtable parseKeyValues(string str, int startingWord)
{
Hashtable hash = new Hashtable();
string strTruncated = string.Join(" ",str.Split(' '),startingWord,str.Split(' ').Length - startingWord);
string[] sets = strTruncated.Split('=',' ');
for (int i=0; i<sets.Length; i=i+2)
{
hash.Add(sets[i],sets[i+1]);
}
return hash;
}
}
}

View File

@@ -8,12 +8,23 @@ package net.i2p.sam;
*
*/
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
@@ -23,15 +34,25 @@ import net.i2p.util.Log;
* @author human
*/
public class SAMBridge implements Runnable {
private final static Log _log = new Log(SAMBridge.class);
private ServerSocket serverSocket;
private Properties i2cpProps;
/**
* filename in which the name to private key mapping should
* be stored (and loaded from)
*/
private String persistFilename;
/**
* app designated destination name to the base64 of the I2P formatted
* destination keys (Destination+PrivateKey+SigningPrivateKey)
*/
private Map nameToPrivKeys = Collections.synchronizedMap(new HashMap(8));
private boolean acceptConnections = true;
private final static int SAM_LISTENPORT = 7656;
private static final int SAM_LISTENPORT = 7656;
public static final String DEFAULT_SAM_KEYFILE = "sam.keys";
private SAMBridge() {}
/**
@@ -40,8 +61,11 @@ public class SAMBridge implements Runnable {
* @param listenHost hostname to listen for SAM connections on ("0.0.0.0" for all)
* @param listenPort port number to listen for SAM connections on
* @param i2cpProps set of I2CP properties for finding and communicating with the router
* @param persistFile location to store/load named keys to/from
*/
public SAMBridge(String listenHost, int listenPort, Properties i2cpProps) {
public SAMBridge(String listenHost, int listenPort, Properties i2cpProps, String persistFile) {
persistFilename = persistFile;
loadKeys();
try {
if ( (listenHost != null) && !("0.0.0.0".equals(listenHost)) ) {
serverSocket = new ServerSocket(listenPort, 0, InetAddress.getByName(listenHost));
@@ -63,6 +87,97 @@ public class SAMBridge implements Runnable {
this.i2cpProps = i2cpProps;
}
/**
* Retrieve the destination associated with the given name
*
* @return null if the name does not exist, or if it is improperly formatted
*/
public Destination getDestination(String name) {
String val = (String)nameToPrivKeys.get(name);
if (val == null) return null;
try {
Destination d = new Destination();
d.fromBase64(val);
return d;
} catch (DataFormatException dfe) {
_log.error("Error retrieving the destination from " + name, dfe);
nameToPrivKeys.remove(name);
return null;
}
}
/**
* Retrieve the I2P private keystream for the given name, formatted
* as a base64 string (Destination+PrivateKey+SessionPrivateKey, as I2CP
* stores it).
*
* @return null if the name does not exist, else the stream
*/
public String getKeystream(String name) {
String val = (String)nameToPrivKeys.get(name);
if (val == null) return null;
return val;
}
/**
* Specify that the given keystream should be used for the given name
*
*/
public void addKeystream(String name, String stream) {
nameToPrivKeys.put(name, stream);
storeKeys();
}
/**
* Load up the keys from the persistFilename
*
*/
private synchronized void loadKeys() {
Map keys = new HashMap(16);
FileInputStream in = null;
try {
in = new FileInputStream(persistFilename);
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line = null;
while ( (line = reader.readLine()) != null) {
int eq = line.indexOf('=');
String name = line.substring(0, eq);
String privKeys = line.substring(eq+1);
keys.put(name, privKeys);
}
} catch (FileNotFoundException fnfe) {
_log.warn("Key file does not exist at " + persistFilename);
} catch (IOException ioe) {
_log.error("Unable to read the keys from " + persistFilename, ioe);
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
nameToPrivKeys = Collections.synchronizedMap(keys);
}
/**
* Store the current keys to disk in the location specified on creation
*
*/
private synchronized void storeKeys() {
FileOutputStream out = null;
try {
out = new FileOutputStream(persistFilename);
for (Iterator iter = nameToPrivKeys.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
String privKeys = (String)nameToPrivKeys.get(name);
out.write(name.getBytes());
out.write('=');
out.write(privKeys.getBytes());
out.write('\n');
}
} catch (IOException ioe) {
_log.error("Error writing out the SAM keys to " + persistFilename, ioe);
} finally {
if (out != null) try { out.close(); } catch (IOException ioe) {}
}
}
/**
* Usage:
* <pre>SAMBridge [[listenHost ]listenPort[ name=val]*]</pre>
@@ -72,15 +187,17 @@ public class SAMBridge implements Runnable {
* depth, etc.
*/
public static void main(String args[]) {
String keyfile = DEFAULT_SAM_KEYFILE;
int port = SAM_LISTENPORT;
String host = "0.0.0.0";
Properties opts = null;
if (args.length > 0) {
int portIndex = 0;
keyfile = args[0];
int portIndex = 1;
try {
port = Integer.parseInt(args[portIndex]);
} catch (NumberFormatException nfe) {
host = args[0];
host = args[1];
portIndex++;
try {
port = Integer.parseInt(args[portIndex]);
@@ -91,7 +208,7 @@ public class SAMBridge implements Runnable {
}
opts = parseOptions(args, portIndex+1);
}
SAMBridge bridge = new SAMBridge(host, port, opts);
SAMBridge bridge = new SAMBridge(host, port, opts, keyfile);
I2PThread t = new I2PThread(bridge, "SAMListener");
t.start();
}
@@ -114,7 +231,8 @@ public class SAMBridge implements Runnable {
}
private static void usage() {
System.err.println("Usage: SAMBridge [listenHost listenPortNum[ name=val]*]");
System.err.println("Usage: SAMBridge [keyfile [listenHost] listenPortNum[ name=val]*]");
System.err.println(" keyfile: location to persist private keys (default sam.keys)");
System.err.println(" listenHost: interface to listen on (0.0.0.0 for all interfaces)");
System.err.println(" listenPort: port to listen for SAM connections on (default 7656)");
System.err.println(" name=val: options to pass when connecting via I2CP, such as ");
@@ -140,10 +258,18 @@ public class SAMBridge implements Runnable {
} catch (IOException e) {}
continue;
}
handler.setBridge(this);
handler.startHandling();
} catch (SAMException e) {
if (_log.shouldLog(Log.ERROR))
_log.error("SAM error: " + e.getMessage(), e);
try {
String reply = "HELLO REPLY RESULT=I2P_ERROR MESSAGE=\"" + e.getMessage() + "\"\n";
s.getOutputStream().write(reply.getBytes("ISO-8859-1"));
} catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error("SAM Error sending error reply", ioe);
}
s.close();
}
}

View File

@@ -8,16 +8,14 @@ package net.i2p.sam;
*
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import net.i2p.client.I2PSessionException;
import net.i2p.client.datagram.I2PDatagramDissector;
import net.i2p.client.datagram.I2PDatagramMaker;
import net.i2p.client.datagram.I2PInvalidDatagramException;
import net.i2p.client.I2PSessionException;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.Log;
@@ -30,6 +28,7 @@ import net.i2p.util.Log;
public class SAMDatagramSession extends SAMMessageSession {
private final static Log _log = new Log(SAMDatagramSession.class);
public static int DGRAM_SIZE_MAX = 31*1024;
private SAMDatagramReceiver recv = null;
@@ -43,7 +42,8 @@ public class SAMDatagramSession extends SAMMessageSession {
* @param recv Object that will receive incoming data
*/
public SAMDatagramSession(String dest, Properties props,
SAMDatagramReceiver recv) throws IOException, DataFormatException, I2PSessionException {
SAMDatagramReceiver recv) throws IOException,
DataFormatException, I2PSessionException {
super(dest, props);
this.recv = recv;
@@ -58,7 +58,8 @@ public class SAMDatagramSession extends SAMMessageSession {
* @param recv Object that will receive incoming data
*/
public SAMDatagramSession(InputStream destStream, Properties props,
SAMDatagramReceiver recv) throws IOException, DataFormatException, I2PSessionException {
SAMDatagramReceiver recv) throws IOException,
DataFormatException, I2PSessionException {
super(destStream, props);
this.recv = recv;
@@ -73,6 +74,9 @@ public class SAMDatagramSession extends SAMMessageSession {
* @return True if the data was sent, false otherwise
*/
public boolean sendBytes(String dest, byte[] data) throws DataFormatException {
if (data.length > DGRAM_SIZE_MAX)
throw new DataFormatException("Datagram size exceeded (" + data.length + ")");
byte[] dgram = dgramMaker.makeI2PDatagram(data);
return sendBytesThroughMessageSession(dest, dgram);

View File

@@ -12,7 +12,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Properties;
import net.i2p.util.I2PThread;
@@ -30,6 +29,7 @@ public abstract class SAMHandler implements Runnable {
private final static Log _log = new Log(SAMHandler.class);
protected I2PThread thread = null;
protected SAMBridge bridge = null;
private Object socketWLock = new Object(); // Guards writings on socket
private Socket socket = null;
@@ -71,6 +71,8 @@ public abstract class SAMHandler implements Runnable {
thread.start();
}
public void setBridge(SAMBridge bridge) { this.bridge = bridge; }
/**
* Actually handle the SAM protocol.
*
@@ -124,7 +126,9 @@ public abstract class SAMHandler implements Runnable {
*
*/
protected final void closeClientSocket() throws IOException {
socket.close();
if (socket != null)
socket.close();
socket = null;
}
/**

View File

@@ -9,8 +9,8 @@ package net.i2p.sam;
*/
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
@@ -32,8 +32,8 @@ public class SAMHandlerFactory {
*
* @param s Socket attached to SAM client
* @param i2cpProps config options for our i2cp connection
*
* @return A SAM protocol handler
* @throws SAMException if the connection handshake (HELLO message) was malformed
* @return A SAM protocol handler, or null if the client closed before the handshake
*/
public static SAMHandler createSAMHandler(Socket s, Properties i2cpProps) throws SAMException {
BufferedReader br;
@@ -66,8 +66,8 @@ public class SAMHandlerFactory {
{
String opcode;
if (!(opcode = tok.nextToken()).equals("VERSION")) {
throw new SAMException("Unrecognized HELLO message opcode: \""
+ opcode + "\"");
throw new SAMException("Unrecognized HELLO message opcode: '"
+ opcode + "'");
}
}
@@ -88,22 +88,8 @@ public class SAMHandlerFactory {
}
String ver = chooseBestVersion(minVer, maxVer);
if (ver == null) {
// Let's answer negatively
try {
OutputStream out = s.getOutputStream();
out.write("HELLO REPLY RESULT=NOVERSION\n".getBytes("ISO-8859-1"));
return null;
} catch (UnsupportedEncodingException e) {
_log.error("Caught UnsupportedEncodingException ("
+ e.getMessage() + ")");
throw new SAMException("Character encoding error: "
+ e.getMessage());
} catch (IOException e) {
throw new SAMException("Error reading from socket: "
+ e.getMessage());
}
}
if (ver == null)
throw new SAMException("No version specified");
// Let's answer positively
try {
@@ -135,8 +121,8 @@ public class SAMHandlerFactory {
throw new SAMException("BUG! (in handler instantiation)");
}
} catch (IOException e) {
_log.error("IOException caught during SAM handler instantiation");
return null;
_log.error("Error creating the v1 handler", e);
throw new SAMException("IOException caught during SAM handler instantiation");
}
return handler;
}

View File

@@ -9,9 +9,8 @@ package net.i2p.sam;
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import net.i2p.client.I2PClient;

View File

@@ -8,10 +8,8 @@ package net.i2p.sam;
*
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import net.i2p.client.I2PSessionException;
@@ -26,6 +24,7 @@ import net.i2p.util.Log;
public class SAMRawSession extends SAMMessageSession {
private final static Log _log = new Log(SAMRawSession.class);
public static final int RAW_SIZE_MAX = 32*1024;
private SAMRawReceiver recv = null;
/**
@@ -64,6 +63,8 @@ public class SAMRawSession extends SAMMessageSession {
* @return True if the data was sent, false otherwise
*/
public boolean sendBytes(String dest, byte[] data) throws DataFormatException {
if (data.length > RAW_SIZE_MAX)
throw new DataFormatException("Data size limit exceeded (" + data.length + ")");
return sendBytesThroughMessageSession(dest, data);
}

View File

@@ -9,18 +9,19 @@ package net.i2p.sam;
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.util.HashMap;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import net.i2p.I2PException;
import net.i2p.client.I2PClient;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
@@ -29,8 +30,6 @@ import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.Base64;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.I2PException;
import net.i2p.util.HexDump;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
@@ -100,11 +99,23 @@ public class SAMStreamSession {
allprops.putAll(System.getProperties());
allprops.putAll(props);
// FIXME: we should setup I2CP host and port, too
String i2cpHost = allprops.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
int i2cpPort = 7654;
String port = allprops.getProperty(I2PClient.PROP_TCP_PORT, "7654");
try {
i2cpPort = Integer.parseInt(port);
} catch (NumberFormatException nfe) {
throw new SAMException("Invalid I2CP port specified [" + port + "]");
}
// streams MUST be mode=guaranteed (though i think the socket manager
// enforces this anyway...
allprops.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
_log.debug("Creating I2PSocketManager...");
socketMgr = I2PSocketManagerFactory.createManager(destStream,
"127.0.0.1",
7654, allprops);
i2cpHost,
i2cpPort,
allprops);
if (socketMgr == null) {
throw new SAMException("Error creating I2PSocketManager");
}

View File

@@ -8,22 +8,20 @@ package net.i2p.sam;
*
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.naming.NamingService;
import net.i2p.data.Base64;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.Log;
import net.i2p.I2PAppContext;
/**
* Miscellaneous utility methods used by SAM protocol handlers.
@@ -33,7 +31,6 @@ import net.i2p.I2PAppContext;
public class SAMUtils {
private final static Log _log = new Log(SAMUtils.class);
private static I2PAppContext _context = new I2PAppContext();
/**
* Generate a random destination key
@@ -86,7 +83,7 @@ public class SAMUtils {
* @return the Destination for the specified hostname, or null if not found
*/
public static Destination lookupHost(String name, OutputStream pubKey) {
NamingService ns = _context.namingService();
NamingService ns = I2PAppContext.getGlobalContext().namingService();
Destination dest = ns.lookup(name);
if ((pubKey != null) && (dest != null)) {
@@ -109,9 +106,10 @@ public class SAMUtils {
*
* @param tok A StringTokenizer pointing to the SAM parameters
*
* @return Properties with the parsed SAM params, or null if none is found
* @throws SAMException if the data was formatted incorrectly
* @return Properties with the parsed SAM params
*/
public static Properties parseParams(StringTokenizer tok) {
public static Properties parseParams(StringTokenizer tok) throws SAMException {
int pos, nprops = 0, ntoks = tok.countTokens();
String token, param, value;
Properties props = new Properties();
@@ -122,7 +120,7 @@ public class SAMUtils {
pos = token.indexOf("=");
if (pos == -1) {
_log.debug("Error in params format");
return null;
throw new SAMException("Bad formatting for param [" + token + "]");
}
param = token.substring(0, pos);
value = token.substring(pos + 1);
@@ -135,22 +133,18 @@ public class SAMUtils {
_log.debug("Parsed properties: " + dumpProperties(props));
}
if (nprops != 0) {
return props;
} else {
return null;
}
return props;
}
/* Dump a Properties object in an human-readable form */
private static String dumpProperties(Properties props) {
Enumeration enum = props.propertyNames();
Enumeration names = props.propertyNames();
String msg = "";
String key, val;
boolean firstIter = true;
while (enum.hasMoreElements()) {
key = (String)enum.nextElement();
while (names.hasMoreElements()) {
key = (String)names.nextElement();
val = props.getProperty(key);
if (!firstIter) {

View File

@@ -8,27 +8,24 @@ package net.i2p.sam;
*
*/
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.InputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.util.Enumeration;
import java.net.Socket;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PException;
import net.i2p.client.I2PSessionException;
import net.i2p.data.Base64;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.I2PException;
import net.i2p.util.Log;
/**
@@ -108,7 +105,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
break;
}
msg = buf.toString("ISO-8859-1");
msg = buf.toString("ISO-8859-1").trim();
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("New message received: " + msg);
}
@@ -127,8 +124,6 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
+ "\"; opcode: \"" + opcode + "\")");
}
props = SAMUtils.parseParams(tok);
if (i2cpProps != null)
props.putAll(i2cpProps); // make sure we've got the i2cp settings
if (domain.equals("STREAM")) {
canContinue = execStreamMessage(opcode, props);
@@ -137,6 +132,8 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
} else if (domain.equals("RAW")) {
canContinue = execRawMessage(opcode, props);
} else if (domain.equals("SESSION")) {
if (i2cpProps != null)
props.putAll(i2cpProps); // make sure we've got the i2cp settings
canContinue = execSessionMessage(opcode, props);
} else if (domain.equals("DEST")) {
canContinue = execDestMessage(opcode, props);
@@ -154,10 +151,10 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
}
} catch (UnsupportedEncodingException e) {
_log.error("Caught UnsupportedEncodingException ("
+ e.getMessage() + ")");
+ e.getMessage() + ")", e);
} catch (IOException e) {
_log.debug("Caught IOException ("
+ e.getMessage() + ")");
+ e.getMessage() + ")", e);
} catch (Exception e) {
_log.error("Unexpected exception", e);
} finally {
@@ -189,76 +186,91 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
if ((rawSession != null) || (datagramSession != null)
|| (streamSession != null)) {
_log.debug("Trying to create a session, but one still exists");
return false;
return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Session already exists\"\n");
}
if (props == null) {
_log.debug("No parameters specified in SESSION CREATE message");
return false;
return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"No parameters for SESSION CREATE\"\n");
}
dest = props.getProperty("DESTINATION");
if (dest == null) {
_log.debug("SESSION DESTINATION parameter not specified");
return false;
return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"DESTINATION not specified\"\n");
}
props.remove("DESTINATION");
String destKeystream = null;
if (dest.equals("TRANSIENT")) {
_log.debug("TRANSIENT destination requested");
ByteArrayOutputStream priv = new ByteArrayOutputStream();
ByteArrayOutputStream priv = new ByteArrayOutputStream(640);
SAMUtils.genRandomKey(priv, null);
dest = Base64.encode(priv.toByteArray());
destKeystream = Base64.encode(priv.toByteArray());
} else {
destKeystream = bridge.getKeystream(dest);
if (destKeystream == null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Custom destination specified [" + dest + "] but it isnt know, creating a new one");
ByteArrayOutputStream baos = new ByteArrayOutputStream(640);
SAMUtils.genRandomKey(baos, null);
destKeystream = Base64.encode(baos.toByteArray());
bridge.addKeystream(dest, destKeystream);
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Custom destination specified [" + dest + "] and it is already known");
}
}
String style = props.getProperty("STYLE");
if (style == null) {
_log.debug("SESSION STYLE parameter not specified");
return false;
return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"No SESSION STYLE specified\"\n");
}
props.remove("STYLE");
if (style.equals("RAW")) {
rawSession = new SAMRawSession(dest, props, this);
rawSession = new SAMRawSession(destKeystream, props, this);
} else if (style.equals("DATAGRAM")) {
datagramSession = new SAMDatagramSession(dest, props,this);
datagramSession = new SAMDatagramSession(destKeystream, props,this);
} else if (style.equals("STREAM")) {
String dir = props.getProperty("DIRECTION");
if (dir == null) {
_log.debug("No DIRECTION parameter in STREAM session");
return false;
_log.debug("No DIRECTION parameter in STREAM session, defaulting to BOTH");
dir = "BOTH";
}
if (!dir.equals("CREATE") && !dir.equals("RECEIVE")
&& !dir.equals("BOTH")) {
_log.debug("Unknow DIRECTION parameter value: " + dir);
return false;
_log.debug("Unknow DIRECTION parameter value: [" + dir + "]");
return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unknown DIRECTION parameter\"\n");
}
props.remove("DIRECTION");
streamSession = new SAMStreamSession(dest, dir,props,this);
streamSession = new SAMStreamSession(destKeystream, dir,props,this);
} else {
_log.debug("Unrecognized SESSION STYLE: \"" + style +"\"");
return false;
return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized SESSION STYLE\"\n");
}
return writeString("SESSION STATUS RESULT=OK DESTINATION="
+ dest + "\n");
} else {
_log.debug("Unrecognized SESSION message opcode: \""
+ opcode + "\"");
return false;
return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized opcode\"\n");
}
} catch (DataFormatException e) {
_log.debug("Invalid destination specified");
return writeString("SESSION STATUS RESULT=INVALID_KEY DESTINATION=" + dest + "\n");
return writeString("SESSION STATUS RESULT=INVALID_KEY DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n");
} catch (I2PSessionException e) {
_log.debug("I2P error when instantiating session", e);
return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + "\n");
return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n");
} catch (SAMException e) {
_log.error("Unexpected SAM error", e);
return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + "\n");
return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n");
} catch (IOException e) {
_log.error("Unexpected IOException", e);
return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + "\n");
return writeString("SESSION STATUS RESULT=I2P_ERROR DESTINATION=" + dest + " MESSAGE=\"" + e.getMessage() + "\"\n");
}
}
@@ -266,7 +278,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
private boolean execDestMessage(String opcode, Properties props) {
if (opcode.equals("GENERATE")) {
if (props != null) {
if (props.size() > 0) {
_log.debug("Properties specified in DEST GENERATE message");
return false;
}
@@ -483,159 +495,171 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
}
if (opcode.equals("SEND")) {
if (props == null) {
_log.debug("No parameters specified in STREAM SEND message");
return false;
}
int id;
{
String strid = props.getProperty("ID");
if (strid == null) {
_log.debug("ID not specified in STREAM SEND message");
return false;
}
try {
id = Integer.parseInt(strid);
} catch (NumberFormatException e) {
_log.debug("Invalid STREAM SEND ID specified: " + strid);
return false;
}
}
int size;
{
String strsize = props.getProperty("SIZE");
if (strsize == null) {
_log.debug("Size not specified in STREAM SEND message");
return false;
}
try {
size = Integer.parseInt(strsize);
} catch (NumberFormatException e) {
_log.debug("Invalid STREAM SEND size specified: "+strsize);
return false;
}
if (!checkSize(size)) {
_log.debug("Specified size (" + size
+ ") is out of protocol limits");
return false;
}
}
try {
DataInputStream in = new DataInputStream(getClientSocketInputStream());
byte[] data = new byte[size];
in.readFully(data);
if (!streamSession.sendBytes(id, data)) {
_log.error("STREAM SEND failed");
return false;
}
return true;
} catch (EOFException e) {
_log.debug("Too few bytes with RAW SEND message (expected: "
+ size);
return false;
} catch (IOException e) {
_log.debug("Caught IOException while parsing RAW SEND message",
e);
return false;
}
return execStreamSend(props);
} else if (opcode.equals("CONNECT")) {
if (props == null) {
_log.debug("No parameters specified in STREAM CONNECT message");
return false;
}
int id;
{
String strid = props.getProperty("ID");
if (strid == null) {
_log.debug("ID not specified in STREAM SEND message");
return false;
}
try {
id = Integer.parseInt(strid);
} catch (NumberFormatException e) {
_log.debug("Invalid STREAM CONNECT ID specified: " +strid);
return false;
}
if (id < 1) {
_log.debug("Invalid STREAM CONNECT ID specified: " +strid);
return false;
}
props.remove("ID");
}
String dest = props.getProperty("DESTINATION");
if (dest == null) {
_log.debug("Destination not specified in RAW SEND message");
return false;
}
props.remove("DESTINATION");
try {
if (!streamSession.connect(id, dest, props)) {
_log.debug("STREAM connection failed");
return false;
}
return writeString("STREAM STATUS RESULT=OK ID=" + id + "\n");
} catch (DataFormatException e) {
_log.debug("Invalid destination in STREAM CONNECT message");
return writeString("STREAM STATUS RESULT=INVALID_KEY ID="
+ id + "\n");
} catch (SAMInvalidDirectionException e) {
_log.debug("STREAM CONNECT failed: " + e.getMessage());
return writeString("STREAM STATUS RESULT=INVALID_DIRECTION ID="
+ id + "\n");
} catch (ConnectException e) {
_log.debug("STREAM CONNECT failed: " + e.getMessage());
return writeString("STREAM STATUS RESULT=CONNECTION_REFUSED ID="
+ id + "\n");
} catch (NoRouteToHostException e) {
_log.debug("STREAM CONNECT failed: " + e.getMessage());
return writeString("STREAM STATUS RESULT=CANT_REACH_PEER ID="
+ id + "\n");
} catch (InterruptedIOException e) {
_log.debug("STREAM CONNECT failed: " + e.getMessage());
return writeString("STREAM STATUS RESULT=TIMEOUT ID="
+ id + "\n");
} catch (I2PException e) {
_log.debug("STREAM CONNECT failed: " + e.getMessage());
return writeString("STREAM STATUS RESULT=I2P_ERROR ID="
+ id + "\n");
}
return execStreamConnect(props);
} else if (opcode.equals("CLOSE")) {
if (props == null) {
_log.debug("No parameters specified in STREAM CLOSE message");
return false;
}
int id;
{
String strid = props.getProperty("ID");
if (strid == null) {
_log.debug("ID not specified in STREAM CLOSE message");
return false;
}
try {
id = Integer.parseInt(strid);
} catch (NumberFormatException e) {
_log.debug("Invalid STREAM CLOSE ID specified: " +strid);
return false;
}
}
return streamSession.closeConnection(id);
return execStreamClose(props);
} else {
_log.debug("Unrecognized RAW message opcode: \""
+ opcode + "\"");
return false;
}
}
private boolean execStreamSend(Properties props) {
if (props == null) {
_log.debug("No parameters specified in STREAM SEND message");
return false;
}
int id;
{
String strid = props.getProperty("ID");
if (strid == null) {
_log.debug("ID not specified in STREAM SEND message");
return false;
}
try {
id = Integer.parseInt(strid);
} catch (NumberFormatException e) {
_log.debug("Invalid STREAM SEND ID specified: " + strid);
return false;
}
}
int size;
{
String strsize = props.getProperty("SIZE");
if (strsize == null) {
_log.debug("Size not specified in STREAM SEND message");
return false;
}
try {
size = Integer.parseInt(strsize);
} catch (NumberFormatException e) {
_log.debug("Invalid STREAM SEND size specified: "+strsize);
return false;
}
if (!checkSize(size)) {
_log.debug("Specified size (" + size
+ ") is out of protocol limits");
return false;
}
}
try {
DataInputStream in = new DataInputStream(getClientSocketInputStream());
byte[] data = new byte[size];
in.readFully(data);
if (!streamSession.sendBytes(id, data)) {
_log.error("STREAM SEND failed");
return false;
}
return true;
} catch (EOFException e) {
_log.debug("Too few bytes with RAW SEND message (expected: "
+ size);
return false;
} catch (IOException e) {
_log.debug("Caught IOException while parsing RAW SEND message",
e);
return false;
}
}
private boolean execStreamConnect(Properties props) {
if (props == null) {
_log.debug("No parameters specified in STREAM CONNECT message");
return false;
}
int id;
{
String strid = props.getProperty("ID");
if (strid == null) {
_log.debug("ID not specified in STREAM SEND message");
return false;
}
try {
id = Integer.parseInt(strid);
} catch (NumberFormatException e) {
_log.debug("Invalid STREAM CONNECT ID specified: " +strid);
return false;
}
if (id < 1) {
_log.debug("Invalid STREAM CONNECT ID specified: " +strid);
return false;
}
props.remove("ID");
}
String dest = props.getProperty("DESTINATION");
if (dest == null) {
_log.debug("Destination not specified in RAW SEND message");
return false;
}
props.remove("DESTINATION");
try {
if (!streamSession.connect(id, dest, props)) {
_log.debug("STREAM connection failed");
return false;
}
return writeString("STREAM STATUS RESULT=OK ID=" + id + "\n");
} catch (DataFormatException e) {
_log.debug("Invalid destination in STREAM CONNECT message");
return writeString("STREAM STATUS RESULT=INVALID_KEY ID="
+ id + "\n");
} catch (SAMInvalidDirectionException e) {
_log.debug("STREAM CONNECT failed: " + e.getMessage());
return writeString("STREAM STATUS RESULT=INVALID_DIRECTION ID="
+ id + "\n");
} catch (ConnectException e) {
_log.debug("STREAM CONNECT failed: " + e.getMessage());
return writeString("STREAM STATUS RESULT=CONNECTION_REFUSED ID="
+ id + "\n");
} catch (NoRouteToHostException e) {
_log.debug("STREAM CONNECT failed: " + e.getMessage());
return writeString("STREAM STATUS RESULT=CANT_REACH_PEER ID="
+ id + "\n");
} catch (InterruptedIOException e) {
_log.debug("STREAM CONNECT failed: " + e.getMessage());
return writeString("STREAM STATUS RESULT=TIMEOUT ID="
+ id + "\n");
} catch (I2PException e) {
_log.debug("STREAM CONNECT failed: " + e.getMessage());
return writeString("STREAM STATUS RESULT=I2P_ERROR ID="
+ id + "\n");
}
}
private boolean execStreamClose(Properties props) {
if (props == null) {
_log.debug("No parameters specified in STREAM CLOSE message");
return false;
}
int id;
{
String strid = props.getProperty("ID");
if (strid == null) {
_log.debug("ID not specified in STREAM CLOSE message");
return false;
}
try {
id = Integer.parseInt(strid);
} catch (NumberFormatException e) {
_log.debug("Invalid STREAM CLOSE ID specified: " +strid);
return false;
}
}
return streamSession.closeConnection(id);
}
/* Check whether a size is inside the limits allowed by this protocol */
private boolean checkSize(int size) {

View File

@@ -0,0 +1,78 @@
package net.i2p.sam;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import net.i2p.util.Log;
public class TestCreateSessionDatagram {
private static Log _log = new Log(TestCreateSessionDatagram.class);
private static void runTest(String samHost, int samPort, String conOptions) {
testTransient(samHost, samPort, conOptions);
testNewDest(samHost, samPort, conOptions);
testOldDest(samHost, samPort, conOptions);
}
private static void testTransient(String host, int port, String conOptions) {
testDest(host, port, conOptions, "TRANSIENT");
_log.debug("\n\nTest of transient complete\n\n\n");
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
private static void testNewDest(String host, int port, String conOptions) {
String destName = "Alice" + Math.random();
testDest(host, port, conOptions, destName);
}
private static void testOldDest(String host, int port, String conOptions) {
String destName = "Alice" + Math.random();
testDest(host, port, conOptions, destName);
_log.debug("\n\nTest of initial contact for " + destName + " complete, waiting 90 seconds");
try { Thread.sleep(90*1000); } catch (InterruptedException ie) {}
_log.debug("now testing subsequent contact\n\n\n");
testDest(host, port, conOptions, destName);
_log.debug("\n\nTest of subsequent contact complete\n\n");
}
private static void testDest(String host, int port, String conOptions, String destName) {
_log.info("\n\nTesting creating a new destination (should come back with 'SESSION STATUS RESULT=OK DESTINATION=someName)\n\n\n");
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=DATAGRAM DESTINATION=" + destName + " " + conOptions + "\n";
out.write(req.getBytes());
line = reader.readLine();
_log.info("Response to creating the session with destination " + destName + ": " + line);
_log.debug("The above should contain SESSION STATUS RESULT=OK\n\n\n");
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
s.close();
} catch (Exception e) {
_log.error("Error testing for valid version", e);
}
}
public static void main(String args[]) {
// "i2cp.tcp.host=www.i2p.net i2cp.tcp.port=7765";
// "i2cp.tcp.host=localhost i2cp.tcp.port=7654 tunnels.inboundDepth=0";
String conOptions = "i2cp.tcp.host=localhost i2cp.tcp.port=7654 tunnels.inboundDepth=0"; // "i2cp.tcp.host=dev.i2p.net i2cp.tcp.port=7002 tunnels.inboundDepth=0";
if (args.length > 0) {
conOptions = "";
for (int i = 0; i < args.length; i++)
conOptions = conOptions + " " + args[i];
}
try {
TestUtil.startupBridge(6000);
runTest("localhost", 6000, conOptions);
} catch (Throwable t) {
_log.error("Error running test", t);
}
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
System.exit(0);
}
}

View File

@@ -0,0 +1,78 @@
package net.i2p.sam;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import net.i2p.util.Log;
public class TestCreateSessionRaw {
private static Log _log = new Log(TestCreateSessionRaw.class);
private static void runTest(String samHost, int samPort, String conOptions) {
testTransient(samHost, samPort, conOptions);
testNewDest(samHost, samPort, conOptions);
testOldDest(samHost, samPort, conOptions);
}
private static void testTransient(String host, int port, String conOptions) {
testDest(host, port, conOptions, "TRANSIENT");
_log.debug("\n\nTest of transient complete\n\n\n");
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
private static void testNewDest(String host, int port, String conOptions) {
String destName = "Alice" + Math.random();
testDest(host, port, conOptions, destName);
}
private static void testOldDest(String host, int port, String conOptions) {
String destName = "Alice" + Math.random();
testDest(host, port, conOptions, destName);
_log.debug("\n\nTest of initial contact for " + destName + " complete, waiting 90 seconds");
try { Thread.sleep(90*1000); } catch (InterruptedException ie) {}
_log.debug("now testing subsequent contact\n\n\n");
testDest(host, port, conOptions, destName);
_log.debug("\n\nTest of subsequent contact complete\n\n");
}
private static void testDest(String host, int port, String conOptions, String destName) {
_log.info("\n\nTesting creating a new destination (should come back with 'SESSION STATUS RESULT=OK DESTINATION=someName)\n\n\n");
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=RAW DESTINATION=" + destName + " " + conOptions + "\n";
out.write(req.getBytes());
line = reader.readLine();
_log.info("Response to creating the session with destination " + destName + ": " + line);
_log.debug("The above should contain SESSION STATUS RESULT=OK\n\n\n");
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
s.close();
} catch (Exception e) {
_log.error("Error testing for valid version", e);
}
}
public static void main(String args[]) {
// "i2cp.tcp.host=www.i2p.net i2cp.tcp.port=7765";
// "i2cp.tcp.host=localhost i2cp.tcp.port=7654 tunnels.inboundDepth=0";
String conOptions = "i2cp.tcp.host=dev.i2p.net i2cp.tcp.port=7002 tunnels.inboundDepth=0";
if (args.length > 0) {
conOptions = "";
for (int i = 0; i < args.length; i++)
conOptions = conOptions + " " + args[i];
}
try {
TestUtil.startupBridge(6000);
runTest("localhost", 6000, conOptions);
} catch (Throwable t) {
_log.error("Error running test", t);
}
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
System.exit(0);
}
}

View File

@@ -0,0 +1,78 @@
package net.i2p.sam;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import net.i2p.util.Log;
public class TestCreateSessionStream {
private static Log _log = new Log(TestCreateSessionStream.class);
private static void runTest(String samHost, int samPort, String conOptions) {
testTransient(samHost, samPort, conOptions);
testNewDest(samHost, samPort, conOptions);
testOldDest(samHost, samPort, conOptions);
}
private static void testTransient(String host, int port, String conOptions) {
testDest(host, port, conOptions, "TRANSIENT");
_log.debug("\n\nTest of transient complete\n\n\n");
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
private static void testNewDest(String host, int port, String conOptions) {
String destName = "Alice" + Math.random();
testDest(host, port, conOptions, destName);
}
private static void testOldDest(String host, int port, String conOptions) {
String destName = "Alice" + Math.random();
testDest(host, port, conOptions, destName);
_log.debug("\n\nTest of initial contact for " + destName + " complete, waiting 90 seconds");
try { Thread.sleep(90*1000); } catch (InterruptedException ie) {}
_log.debug("now testing subsequent contact\n\n\n");
testDest(host, port, conOptions, destName);
_log.debug("\n\nTest of subsequent contact complete\n\n");
}
private static void testDest(String host, int port, String conOptions, String destName) {
_log.info("\n\nTesting creating a new destination (should come back with 'SESSION STATUS RESULT=OK DESTINATION=someName)\n\n\n");
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=STREAM DESTINATION=" + destName + " " + conOptions + "\n";
out.write(req.getBytes());
line = reader.readLine();
_log.info("Response to creating the session with destination " + destName + ": " + line);
_log.debug("The above should contain SESSION STATUS RESULT=OK\n\n\n");
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
s.close();
} catch (Exception e) {
_log.error("Error testing for valid version", e);
}
}
public static void main(String args[]) {
// "i2cp.tcp.host=www.i2p.net i2cp.tcp.port=7765";
// "i2cp.tcp.host=localhost i2cp.tcp.port=7654 tunnels.inboundDepth=0";
String conOptions = "i2cp.tcp.host=localhost i2cp.tcp.port=7654 tunnels.inboundDepth=0";
if (args.length > 0) {
conOptions = "";
for (int i = 0; i < args.length; i++)
conOptions = conOptions + " " + args[i];
}
try {
TestUtil.startupBridge(6000);
runTest("localhost", 6000, conOptions);
} catch (Throwable t) {
_log.error("Error running test", t);
}
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
System.exit(0);
}
}

View File

@@ -0,0 +1,106 @@
package net.i2p.sam;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.StringTokenizer;
import java.util.Properties;
import net.i2p.util.Log;
import net.i2p.sam.SAMUtils;
public class TestDatagramTransfer {
private static Log _log = new Log(TestCreateSessionDatagram.class);
private static void runTest(String samHost, int samPort, String conOptions) {
testTransfer(samHost, samPort, conOptions);
}
private static void testTransfer(String host, int port, String conOptions) {
String destName = "TRANSIENT";
_log.info("\n\nTesting creating a new destination (should come back with 'SESSION STATUS RESULT=OK DESTINATION=someName)\n\n\n");
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=DATAGRAM DESTINATION=" + destName + " " + conOptions + "\n";
out.write(req.getBytes());
line = reader.readLine();
_log.info("Response to creating the session with destination " + destName + ": " + line);
_log.debug("The above should contain SESSION STATUS RESULT=OK\n\n\n");
String lookup = "NAMING LOOKUP NAME=ME\n";
out.write(lookup.getBytes());
line = reader.readLine();
_log.info("Response from the lookup for ME: " + line);
_log.debug("The above should be a NAMING REPLY");
StringTokenizer tok = new StringTokenizer(line);
String maj = tok.nextToken();
String min = tok.nextToken();
Properties props = SAMUtils.parseParams(tok);
String value = props.getProperty("VALUE");
if (value == null) {
_log.error("No value for ME found! [" + line + "]");
return;
} else {
_log.info("Alice is located at " + value);
}
String send = "DATAGRAM SEND DESTINATION=" + value + " SIZE=3\nYo!";
out.write(send.getBytes());
line = reader.readLine();
tok = new StringTokenizer(line);
maj = tok.nextToken();
min = tok.nextToken();
props = SAMUtils.parseParams(tok);
String size = props.getProperty("SIZE");
String from = props.getProperty("DESTINATION");
if ( (value == null) || (size == null) ||
(!from.equals(value)) || (!size.equals("3")) ) {
_log.error("Reply of the datagram is incorrect: [" + line + "]");
return;
}
char buf[] = new char[3];
int read = reader.read(buf);
if (read != 3) {
_log.error("Unable to read the full datagram");
return;
}
if (new String(buf).equals("Yo!")) {
_log.info("Received payload successfully");
} else {
_log.error("Payload is incorrect! [" + new String(buf) + "]");
}
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
s.close();
} catch (Exception e) {
_log.error("Error testing for valid version", e);
}
}
public static void main(String args[]) {
// "i2cp.tcp.host=www.i2p.net i2cp.tcp.port=7765";
// "i2cp.tcp.host=localhost i2cp.tcp.port=7654 tunnels.inboundDepth=0";
String conOptions = "i2cp.tcp.host=localhost i2cp.tcp.port=7654 tunnels.inboundDepth=0"; // "i2cp.tcp.host=dev.i2p.net i2cp.tcp.port=7002 tunnels.inboundDepth=0";
if (args.length > 0) {
conOptions = "";
for (int i = 0; i < args.length; i++)
conOptions = conOptions + " " + args[i];
}
try {
TestUtil.startupBridge(6000);
runTest("localhost", 6000, conOptions);
} catch (Throwable t) {
_log.error("Error running test", t);
}
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
System.exit(0);
}
}

View File

@@ -0,0 +1,62 @@
package net.i2p.sam;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import net.i2p.util.Log;
public class TestDest {
private static Log _log = new Log(TestDest.class);
private static void runTest(String samHost, int samPort, String conOptions) {
test(samHost, samPort, conOptions);
}
private static void test(String host, int port, String conOptions) {
_log.info("\n\nTesting a DEST generate (should come back with 'DEST REPLY PUB=val PRIV=val')\n\n\n");
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=STREAM DESTINATION=testNaming " + conOptions + "\n";
out.write(req.getBytes());
line = reader.readLine();
_log.debug("Response to creating the session with destination testNaming: " + line);
_log.debug("The above should contain SESSION STATUS RESULT=OK\n\n\n");
String lookup = "DEST GENERATE\n";
out.write(lookup.getBytes());
line = reader.readLine();
_log.info("Response from the dest generate: " + line);
_log.debug("The abouve should be a DEST REPLY");
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
s.close();
} catch (Exception e) {
_log.error("Error testing for valid version", e);
}
}
public static void main(String args[]) {
// "i2cp.tcp.host=www.i2p.net i2cp.tcp.port=7765 tunnels.inboundDepth=0";
// "i2cp.tcp.host=localhost i2cp.tcp.port=7654 tunnels.inboundDepth=0";
String conOptions = "i2cp.tcp.host=www.i2p.net i2cp.tcp.port=7765 tunnels.inboundDepth=0"; // "i2cp.tcp.host=localhost i2cp.tcp.port=7654 tunnels.inboundDepth=0";
if (args.length > 0) {
conOptions = "";
for (int i = 0; i < args.length; i++)
conOptions = conOptions + " " + args[i];
}
try {
TestUtil.startupBridge(6000);
runTest("localhost", 6000, conOptions);
} catch (Throwable t) {
_log.error("Error running test", t);
}
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
System.exit(0);
}
}

View File

@@ -0,0 +1,76 @@
package net.i2p.sam;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import net.i2p.util.Log;
public class TestHello {
private static Log _log = new Log(TestHello.class);
private static void runTest(String samHost, int samPort) {
testValidVersion(samHost, samPort);
testInvalidVersion(samHost, samPort);
testCorruptLine(samHost, samPort);
}
private static void testValidVersion(String host, int port) {
_log.info("\n\nTesting valid version (should come back with an OK)\n\n\n");
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.info("line read for valid version: " + line);
s.close();
} catch (Exception e) {
_log.error("Error testing for valid version", e);
}
}
private static void testInvalidVersion(String host, int port) {
_log.info("\n\nTesting invalid version (should come back with an error)\n\n\n");
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=9.0 MAX=8.3\n".getBytes());
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.info("line read for invalid version: " + line);
s.close();
} catch (Exception e) {
_log.error("Error testing for valid version", e);
}
}
private static void testCorruptLine(String host, int port) {
_log.info("\n\nTesting corrupt line (should come back with an error)\n\n\n");
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO h0 h0 h0\n".getBytes());
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.info("line read for valid version: " + line);
s.close();
} catch (Exception e) {
_log.error("Error testing for valid version", e);
}
}
public static void main(String args[]) {
try {
TestUtil.startupBridge(6000);
runTest("localhost", 6000);
} catch (Throwable t) {
_log.error("Error running test", t);
}
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
System.exit(0);
}
}

View File

@@ -0,0 +1,82 @@
package net.i2p.sam;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import net.i2p.util.Log;
public class TestNaming {
private static Log _log = new Log(TestNaming.class);
private static void runTest(String samHost, int samPort, String conOptions) {
testMe(samHost, samPort, conOptions);
testDuck(samHost, samPort, conOptions);
testUnknown(samHost, samPort, conOptions);
}
private static void testMe(String host, int port, String conOptions) {
testName(host, port, conOptions, "ME");
_log.debug("\n\nTest of ME complete\n\n\n");
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
private static void testDuck(String host, int port, String conOptions) {
testName(host, port, conOptions, "duck.i2p");
_log.debug("\n\nTest of duck complete\n\n\n");
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
private static void testUnknown(String host, int port, String conOptions) {
testName(host, port, conOptions, "www.odci.gov");
_log.debug("\n\nTest of unknown host complete\n\n\n");
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
private static void testName(String host, int port, String conOptions, String name) {
_log.info("\n\nTesting a name lookup (should come back with 'NAMING REPLY RESULT=OK VALUE=someName)\n\n\n");
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=STREAM DESTINATION=testNaming " + conOptions + "\n";
out.write(req.getBytes());
line = reader.readLine();
_log.debug("Response to creating the session with destination testNaming: " + line);
_log.debug("The above should contain SESSION STATUS RESULT=OK\n\n\n");
String lookup = "NAMING LOOKUP NAME=" + name + "\n";
out.write(lookup.getBytes());
line = reader.readLine();
_log.info("Response from the lookup for [" + name +"]: " + line);
_log.debug("The abouve should be a NAMING REPLY");
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
s.close();
} catch (Exception e) {
_log.error("Error testing for valid version", e);
}
}
public static void main(String args[]) {
// "i2cp.tcp.host=www.i2p.net i2cp.tcp.port=7765";
// "i2cp.tcp.host=localhost i2cp.tcp.port=7654 tunnels.inboundDepth=0";
String conOptions = "i2cp.tcp.host=localhost i2cp.tcp.port=7654 tunnels.inboundDepth=0";
if (args.length > 0) {
conOptions = "";
for (int i = 0; i < args.length; i++)
conOptions = conOptions + " " + args[i];
}
try {
TestUtil.startupBridge(6000);
runTest("localhost", 6000, conOptions);
} catch (Throwable t) {
_log.error("Error running test", t);
}
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
System.exit(0);
}
}

View File

@@ -0,0 +1,109 @@
package net.i2p.sam;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.StringTokenizer;
import java.util.Properties;
import net.i2p.util.Log;
import net.i2p.sam.SAMUtils;
public class TestRawTransfer {
private static Log _log = new Log(TestCreateSessionDatagram.class);
private static void runTest(String samHost, int samPort, String conOptions) {
testTransfer(samHost, samPort, conOptions);
}
private static void testTransfer(String host, int port, String conOptions) {
String destName = "TRANSIENT";
_log.info("\n\nTesting creating a new destination (should come back with 'SESSION STATUS RESULT=OK DESTINATION=someName)\n\n\n");
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=RAW DESTINATION=" + destName + " " + conOptions + "\n";
out.write(req.getBytes());
line = reader.readLine();
_log.info("Response to creating the session with destination " + destName + ": " + line);
_log.debug("The above should contain SESSION STATUS RESULT=OK\n\n\n");
String lookup = "NAMING LOOKUP NAME=ME\n";
out.write(lookup.getBytes());
line = reader.readLine();
_log.info("Response from the lookup for ME: " + line);
_log.debug("The above should be a NAMING REPLY");
StringTokenizer tok = new StringTokenizer(line);
String maj = tok.nextToken();
String min = tok.nextToken();
Properties props = SAMUtils.parseParams(tok);
String value = props.getProperty("VALUE");
if (value == null) {
_log.error("No value for ME found! [" + line + "]");
return;
} else {
_log.info("Alice is located at " + value);
}
String send = "RAW SEND DESTINATION=" + value + " SIZE=3\nYo!";
out.write(send.getBytes());
line = reader.readLine();
try {
tok = new StringTokenizer(line);
maj = tok.nextToken();
min = tok.nextToken();
props = SAMUtils.parseParams(tok);
} catch (Exception e) {
_log.error("Error parsing response line: [" + line + "]", e);
return;
}
String size = props.getProperty("SIZE");
if ( (size == null) || (!size.equals("3")) ) {
_log.error("Reply of the datagram is incorrect: [" + line + "]");
return;
}
char buf[] = new char[3];
int read = reader.read(buf);
if (read != 3) {
_log.error("Unable to read the full datagram");
return;
}
if (new String(buf).equals("Yo!")) {
_log.info("Rec8eived payload successfully");
} else {
_log.error("Payload is incorrect! [" + new String(buf) + "]");
}
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
s.close();
} catch (Exception e) {
_log.error("Error testing for valid version", e);
}
}
public static void main(String args[]) {
// "i2cp.tcp.host=www.i2p.net i2cp.tcp.port=7765";
// "i2cp.tcp.host=localhost i2cp.tcp.port=7654 tunnels.inboundDepth=0";
String conOptions = "i2cp.tcp.host=dev.i2p.net i2cp.tcp.port=7002 tunnels.inboundDepth=0";
if (args.length > 0) {
conOptions = "";
for (int i = 0; i < args.length; i++)
conOptions = conOptions + " " + args[i];
}
try {
TestUtil.startupBridge(6000);
runTest("localhost", 6000, conOptions);
} catch (Throwable t) {
_log.error("Error running test", t);
}
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
System.exit(0);
}
}

View File

@@ -0,0 +1,223 @@
package net.i2p.sam;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Map;
import java.util.Collections;
import java.util.HashMap;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.util.Log;
import net.i2p.util.I2PThread;
/**
* <ol>
* <li>start up SAM</li>
* <li>Alice connects as 'Alice', gets her destination, stashes it away, and
* listens for any streams, echoing back whatever she receives.</li>
* <li>Bob connects as 'Bob', establishes a stream to the destination Alice
* stashed away, sends a few bundles of data, and closes the stream.</li>
* <li>Alice and Bob disconnect from SAM</li>
* <li>SAM bridge taken down</li>
* </ol>
*/
public class TestStreamTransfer {
private static Log _log = new Log(TestStreamTransfer.class);
private static String _alice = null;
private static boolean _dead = false;
private static void runTest(String samHost, int samPort, String conOptions) {
startAlice(samHost, samPort, conOptions);
testBob(samHost, samPort, conOptions);
}
private static void startAlice(String host, int port, String conOptions) {
_log.info("\n\nStarting up Alice");
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=STREAM DESTINATION=Alice " + conOptions + "\n";
out.write(req.getBytes());
line = reader.readLine();
_log.info("Response to creating the session with destination Alice: " + line);
req = "NAMING LOOKUP NAME=ME\n";
out.write(req.getBytes());
line = reader.readLine();
StringTokenizer tok = new StringTokenizer(line);
String maj = tok.nextToken();
String min = tok.nextToken();
Properties props = SAMUtils.parseParams(tok);
String value = props.getProperty("VALUE");
if (value == null) {
_log.error("No value for ME found! [" + line + "]");
return;
} else {
_log.info("Alice is located at " + value);
}
_alice = value;
I2PThread aliceThread = new I2PThread(new AliceRunner(reader, out, s));
aliceThread.setName("Alice");
aliceThread.start();
} catch (Exception e) {
_log.error("Error testing for valid version", e);
}
}
private static class AliceRunner implements Runnable {
private BufferedReader _reader;
private OutputStream _out;
private Socket _s;
/** ID (string) to base64 destination */
private Map _streams;
public AliceRunner(BufferedReader reader, OutputStream out, Socket s) {
_reader = reader;
_out = out;
_s = s;
_streams = Collections.synchronizedMap(new HashMap(4));
}
public void run() {
while (!_dead) {
try {
doRun();
} catch (Exception e) {
_log.error("Error running alice", e);
try { _reader.close(); } catch (IOException ioe) {}
try { _out.close(); } catch (IOException ioe) {}
try { _s.close(); } catch (IOException ioe) {}
_streams.clear();
}
}
}
private void doRun() throws IOException, SAMException {
String line = _reader.readLine();
StringTokenizer tok = new StringTokenizer(line);
String maj = tok.nextToken();
String min = tok.nextToken();
Properties props = SAMUtils.parseParams(tok);
if ( ("STREAM".equals(maj)) && ("CONNECTED".equals(min)) ) {
String dest = props.getProperty("DESTINATION");
String id = props.getProperty("ID");
if ( (dest == null) || (id == null) ) {
_log.error("Invalid STREAM CONNECTED line: [" + line + "]");
return;
}
dest = dest.trim();
id = id.trim();
_streams.put(id, dest);
} else if ( ("STREAM".equals(maj)) && ("CLOSED".equals(min)) ) {
String id = props.getProperty("ID");
if (id == null) {
_log.error("Invalid STREAM CLOSED line: [" + line + "]");
return;
}
_streams.remove(id);
} else if ( ("STREAM".equals(maj)) && ("RECEIVED".equals(min)) ) {
String id = props.getProperty("ID");
String size = props.getProperty("SIZE");
if ( (id == null) || (size == null) ) {
_log.error("Invalid STREAM RECEIVED line: [" + line + "]");
return;
}
id = id.trim();
size = size.trim();
int payloadSize = -1;
try {
payloadSize = Integer.parseInt(size);
} catch (NumberFormatException nfe) {
_log.error("Invalid SIZE in message [" + size + "]");
return;
}
// i know, its bytes, but this test uses chars
char payload[] = new char[payloadSize];
int read = _reader.read(payload);
if (read != payloadSize) {
_log.error("Incorrect size read - expected " + payloadSize + " got " + read);
return;
}
_log.info("Received from the stream " + id + ": [" + new String(payload) + "]");
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
// now echo it back
String reply = "STREAM SEND ID=" + id +
" SIZE=" + payloadSize +
"\n" + payload;
_out.write(reply.getBytes());
_out.flush();
} else {
_log.error("Received unsupported type [" + maj + "/"+ min + "]");
return;
}
}
}
private static void testBob(String host, int port, String conOptions) {
_log.info("\n\nTesting Bob\n\n\n");
try {
Socket s = new Socket(host, port);
OutputStream out = s.getOutputStream();
out.write("HELLO VERSION MIN=1.0 MAX=1.0\n".getBytes());
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = reader.readLine();
_log.debug("line read for valid version: " + line);
String req = "SESSION CREATE STYLE=STREAM DESTINATION=Bob " + conOptions + "\n";
out.write(req.getBytes());
line = reader.readLine();
_log.info("Response to creating the session with destination Bob: " + line);
req = "STREAM CONNECT ID=42 DESTINATION=" + _alice + "\n";
out.write(req.getBytes());
line = reader.readLine();
_log.info("Response to the stream connect from Bob to Alice: " + line);
StringTokenizer tok = new StringTokenizer(line);
String maj = tok.nextToken();
String min = tok.nextToken();
Properties props = SAMUtils.parseParams(tok);
String result = props.getProperty("RESULT");
if (!("OK".equals(result))) {
_log.error("Unable to connect!");
_dead = true;
return;
}
try { Thread.sleep(5*1000) ; } catch (InterruptedException ie) {}
req = "STREAM SEND ID=42 SIZE=10\nBlahBlah!!";
out.write(req.getBytes());
try { Thread.sleep(20*1000); } catch (InterruptedException ie) {}
req = "STREAM CLOSE ID=42\n";
out.write(req.getBytes());
try { Thread.sleep(3*1000); } catch (InterruptedException ie) {}
_dead = true;
s.close();
} catch (Exception e) {
_log.error("Error testing for valid version", e);
}
}
public static void main(String args[]) {
// "i2cp.tcp.host=www.i2p.net i2cp.tcp.port=7765 tunnels.inboundDepth=0";
// "i2cp.tcp.host=localhost i2cp.tcp.port=7654 tunnels.inboundDepth=0";
String conOptions = "i2cp.tcp.host=www.i2p.net i2cp.tcp.port=7765 tunnels.inboundDepth=0";
if (args.length > 0) {
conOptions = "";
for (int i = 0; i < args.length; i++)
conOptions = conOptions + " " + args[i];
}
try {
TestUtil.startupBridge(6000);
runTest("localhost", 6000, conOptions);
} catch (Throwable t) {
_log.error("Error running test", t);
}
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
System.exit(0);
}
}

View File

@@ -0,0 +1,9 @@
package net.i2p.sam;
public class TestUtil {
public static void startupBridge(int listenPort) {
// Usage: SAMBridge [listenHost listenPortNum[ name=val]*]
SAMBridge.main(new String[] { "0.0.0.0", listenPort+"" });
}
}

321
apps/sam/perl/Net/SAM.pm Normal file
View File

@@ -0,0 +1,321 @@
#!/usr/bin/perl
## Copyright 2004 Brian Ristuccia. This program is Free Software;
## You can redistribute it and/or modify it under the same terms as
## Perl itself.
package Net::SAM;
@ISA = ( "IO::Socket::INET" );
use strict;
use POSIX;
use Switch;
use IO::Socket;
use IO::Select;
#use Net::SAM::StreamSession;
#use Net::SAM::DatagramSession;
#use Net::SAM::RawSession;
sub new {
my ($class) = shift;
my $type = ref($class) || $class;
my $self = $type->SUPER::new("127.0.0.1:7656");
${*$self}->{incomingraw} = [];
# Connect us to the local SAM proxy.
# my $samsock = IO::Socket::INET->new('127.0.0.1:7657');
#$self->{samsock}=$samsock;
# Say hello, read response.
$self->SUPER::send("HELLO VERSION MIN=1.0 MAX=1.0\n");
while (! ${*$self}->{greeted}) {
$self->readprocess();
}
print "Created SAM object\n";
return $self;
}
sub lookup {
my $self = shift;
my $name= shift;
$self->SUPER::send("NAMING LOOKUP NAME=$name\n");
undef ${*$self}->{RESULT};
while (! ${*$self}->{RESULT}) {
$self->readprocess();
}
if ( ${*$self}->{RESULT} == "OK" ) {
return ${*$self}->{VALUE};
} else {
return undef;
}
}
#sub createsession {
# my ($self) = shift;
# my ($sesstype) = shift;
# print $self->{samsock} "SESSION CREATE STYLE=$SESSTYPE DESTINATION=$DEST, DIRECTION=
#}
#sub waitfor {
# my ($self) = shift;
# my ($prefix) = shift;
# my ($response) = <$samsock>;#
# if $response =~
#}
sub readprocesswrite {
my $self = shift;
$self->readprocess();
$self->dowrite();
}
sub doread {
my $self = shift;
my $rv;
my $data;
$rv = $self->recv($data, $POSIX::BUFSIZE, 0);
if ( defined($rv) && ( length($data) >= 1 ) ) {
# We received some data. Put it in our buffer.
${*$self}->{inbuffer} += $data;
} else {
# No data. Either we're on a non-blocking socket, or there
# was an error or EOF
if ( $!{EAGAIN} ) {
return 1;
} else {
# I suppose caller can look at $! for details
return undef;
}
}
}
sub dowrite {
my $self = shift;
my $rv;
my $data;
$rv = $self->send(${*$self}->{outbuffer}, 0);
if ( ! defined($rv) ) {
warn "SAM::dowrite - Couldn't write for no apparent reason.\n";
return undef;
}
if ( $rv == length(${*$self}->{outbuffer}) || $!{EWOULDBLOCK} ) {
substr(${*$self}->{outbuffer},0, $rv) = ''; # Remove from buffer
# Nuke buffer if empty
delete ${*$self}->{outbuffer} unless length(${*$self}->{outbuffer});
} else {
# Socket closed on us or something?
return undef;
}
}
sub messages {
my $self = shift;
return @{ ${*$self}->{messages} };
}
sub queuemessage {
my $self = shift;
my $message = shift;
push @{ ${*$self}->{messages} } , $message;
}
sub unqueuemessage {
my $self = shift;
return unshift(@{ ${*$self}->{messages} } );
}
sub readprocess {
my $self = shift;
$self->doread();
$self->process();
}
sub process {
my $self = shift;
my %tvhash;
my $payload;
# Before we can read any new messages, if an existing message has payload
# we must read it in. Otherwise we'll create garbage messages containing
# the payload of previous messages.
if ( ${*$self}->{payloadrequired} >= 1 ) {
if ( length( ${*$self}->{inbuffer} ) >= ${*$self}->{payloadrequired} ) {
# Scarf payload from inbuffer into $payload
$payload = substr(${*$self}->{inbuffer}, 0,
${*$self}->{payloadrequired});
# Nuke payload from inbuffer
substr(${*$self}->{inbuffer}, 0,
${*$self}->{payloadrequired} ) = '';
# Put message with payload into spool
push @{ ${*$self}->{messages} } ,
${*$self}->{messagerequiringpayload}.$payload;
# Delete the saved message requiring payload
delete ${*$self}->{messagerequiringpayload};
} else {
# Insufficient payload in inbuffer. Try again later.
return 1;
}
}
if ( ${*$self}->{inbuffer} =~ s/(.*\n)// ) {
%tvhash = $self->_hashtv($1); # Returns a tag/value hash
if ( $tvhash{SIZE} ) {
# We've got a message with payload on our hands. :(
${*$self}->{payloadrequired} = $tvhash{SIZE};
${*$self}->{messagerequiringpayload} = $1;
return 1; # Could call ourself here, but we'll get called again.
} else {
push @{ ${*$self}->{messages} } , $1;
}
}
return 1;
}
# sub junk {
# print "readprocess: " . $self->connected() . "\n";
# # May block if the SAM bridge gets hosed
# my $response = <$self>;
# print "readprocess: $!" . $self->connected() . "\n";
# chomp $response;
# my ($primative, $more, $extra) = split (' ', $response, 3);
# $primative = uc($primative);
# print "readprocess: " . $self->connected() . " -- $primative -- $more -- $extra\n";
# switch ($primative) {
# case "HELLO" {
# if ($more !~ m/REPLY/ ) { die ("Bogus HELLO response") }
# if ($extra =~ m/NOVERSION/ ) {
# die("SAM Bridge Doesn't support my version") ;
# }
# $self->_hashtv($extra);
# ${*$self}->{greeted} = 1;
# };
# case "SESSION" {
# if ( $more !~ m/STATUS/ ) {
# die("Bogus SESSION response");
# }
# $self->_hashtv($extra);
# }
# case "STREAM" {};
# case "DATAGRAM" {
# if ( $more !~ m/RECEIVE/ ) {
# die("Bogus DATAGRAM response.");
# }
# $self->_hashtv($extra);
# push @{ ${*$self}->{incomingdatagram } },
# [ ${*$self}->{DESTINATION},
# $self->_readblock(${*$self}->{SIZE}) ];
# };
# case "RAW" {
# if ( $more !~ m/RECEIVE/ ) {
# die("Bogus RAW response.");
# }
# $self->_hashtv($extra);
# push @{ $self->{incomingraw} }, $self->_readblock($self->{SIZE});
# };
# case "NAMING" {
# if ( $more !~ m/REPLY/ ) {
# die("Bogus NAMING response");
# }
# $self->_hashtv($extra);
# };
# case "DEST" {};
# }
# return 1;
# }
sub getfh {
# Return the FH of the SAM socket so apps can select() or poll() on it
my $self = shift;
return $self->{samsock};
}
sub _readblock {
my $self = shift;
my $size = shift;
my $chunk;
my $payload;
while ( $size > 1 ) {
# XXX: May block. No error checking.
print "readblock: $size\n";
$size -= $self->SUPER::recv($chunk, $size);
$payload .= $chunk;
}
return $payload;
}
sub _hashtv {
my $self = shift;
my $tvstring = shift;
my $tvhash;
while ( $tvstring =~ m/(\S+)=(\S+)/sg ) {
$tvhash->{$1}=$2;
print "hashtv: $1=$2\n"
}
return $tvhash;
}
sub DESTROY {
# Do nothing yet.
}
#sub StreamSession {
# my $self = shift;
# return Net::SAM::StreamSession->new($self);
#}
#sub DatagramSession {
# return Net::SAM::DatagramSession->new($self);
#}
#sub RawSession {
# return Net::SAM::RawSession->new($self);
#}
1;

View File

@@ -0,0 +1,48 @@
#!/usr/bin/perl
package Net::SAM::DatagramSession;
use Net::SAM;
@ISA = ("Net::SAM");
sub new {
my ($class) = shift;
my ($dest , $direction, $options) = shift;
my $self = $class->SUPER::new(@_);
$self->SUPER::send("SESSION CREATE STYLE=DATAGRAM DESTINATION=$dest DIRECTION=$direction $options\n");
undef ${*$self}->{RESULT};
while ( ! ${*$self}->{RESULT} ) {
$self->readprocess() || return undef;
}
if ( ${*$self}->{RESULT} == "OK" ) {
return $self;
} else {
return undef; # sorry.
}
}
sub send {
my $self = shift;
my $destination = shift;
my $message = shift;
my $size = length($message);
$self->SUPER::send("DATAGRAM SEND DESTINATION=$destination SIZE=$size\n$message");
}
sub receive {
my $self = shift;
# Shift one off the fifo array. Returns undef if none wait.
return shift @{ $self->{incomingdatagram} };
}
1;

View File

@@ -0,0 +1,45 @@
#!/usr/bin/perl
package Net::SAM::RawSession;
use Net::SAM;
@ISA = ("Net::SAM");
sub new {
my ($class) = shift;
my ($dest , $direction, $options) = shift;
my $self = $class->SUPER::new(@_);
$self->send("SESSION CREATE STYLE=RAW DESTINATION=$dest DIRECTION=$direction $options\n");
undef $self->{result};
while ( ! $self->{RESULT} ) {
$self->readprocess();
}
if ( $self->{RESULT} == "OK" ) {
return $self;
} else {
return 0; # sorry.
}
}
sub send {
my $self = shift;
my $destination = shift;
my $message = shift;
my $samsock = $self->{samsock};
my $size = length($message);
print $samsock "RAW SEND DESTINATION=$destination SIZE=$size\n$message";
}
sub receive {
my $self = shift;
# Shift one off the fifo array. Returns undef if none wait.
return shift @{ $self->{incomingraw} };
}
1;

View File

@@ -0,0 +1,3 @@
#!/usr/bin/perl
1;

18
apps/sam/perl/samcat.pl Normal file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/perl
use Net::SAM::RawSession;
use Net::SAM::DatagramSession;
$sam=Net::SAM::DatagramSession->new($ARGV[0], "BOTH", "tunnels.depthInbound=0");
print "Connected? " . $sam->connected() . "\n";
$me = $sam->lookup("ME");
print "Sending to $me.\n";
$sam->send($me,"fooquux");
$sam->readprocess();
($source, $message) = @{ $sam->receive() };
print "$source -- $message";

41
apps/time/java/build.xml Normal file
View File

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

View File

@@ -0,0 +1,144 @@
package net.i2p.time;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* NtpClient - an NTP client for Java. This program connects to an NTP server
* and prints the response to the console.
*
* The local clock offset calculation is implemented according to the SNTP
* algorithm specified in RFC 2030.
*
* Note that on windows platforms, the curent time-of-day timestamp is limited
* to an resolution of 10ms and adversely affects the accuracy of the results.
*
*
* This code is copyright (c) Adam Buckley 2004
*
* 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. A HTML version of the GNU General Public License can be
* seen at http://www.gnu.org/licenses/gpl.html
*
* 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.
*
* @author Adam Buckley
* (minor refactoring by jrandom)
*/
public class NtpClient {
/** difference between the unix epoch and jan 1 1900 (NTP uses that) */
private final static double SECONDS_1900_TO_EPOCH = 2208988800.0;
private final static int NTP_PORT = 123;
/**
* Query the ntp servers, returning the current time from first one we find
*
* @return milliseconds since january 1, 1970 (UTC)
* @throws IllegalArgumentException if none of the servers are reachable
*/
public static long currentTime(String serverNames[]) {
if (serverNames == null)
throw new IllegalArgumentException("No NTP servers specified");
for (int i = 0; i < serverNames.length; i++) {
long now = currentTime(serverNames[i]);
if (now > 0)
return now;
}
throw new IllegalArgumentException("No reachable NTP servers specified");
}
/**
* Query the given NTP server, returning the current internet time
*
* @return milliseconds since january 1, 1970 (UTC), or -1 on error
*/
public static long currentTime(String serverName) {
try {
// Send request
DatagramSocket socket = new DatagramSocket();
InetAddress address = InetAddress.getByName(serverName);
byte[] buf = new NtpMessage().toByteArray();
DatagramPacket packet = new DatagramPacket(buf, buf.length, address, NTP_PORT);
// Set the transmit timestamp *just* before sending the packet
// ToDo: Does this actually improve performance or not?
NtpMessage.encodeTimestamp(packet.getData(), 40,
(System.currentTimeMillis()/1000.0)
+ SECONDS_1900_TO_EPOCH);
socket.send(packet);
// Get response
packet = new DatagramPacket(buf, buf.length);
socket.setSoTimeout(10*1000);
try {
socket.receive(packet);
} catch (InterruptedIOException iie) {
socket.close();
return -1;
}
// Immediately record the incoming timestamp
double destinationTimestamp = (System.currentTimeMillis()/1000.0) + SECONDS_1900_TO_EPOCH;
// Process response
NtpMessage msg = new NtpMessage(packet.getData());
double roundTripDelay = (destinationTimestamp-msg.originateTimestamp) -
(msg.receiveTimestamp-msg.transmitTimestamp);
double localClockOffset = ((msg.receiveTimestamp - msg.originateTimestamp) +
(msg.transmitTimestamp - destinationTimestamp)) / 2;
socket.close();
//System.out.println("host: " + serverName + " rtt: " + roundTripDelay + " offset: " + localClockOffset + " seconds");
return (long)(System.currentTimeMillis() + localClockOffset*1000);
} catch (IOException ioe) {
ioe.printStackTrace();
return -1;
}
}
public static void main(String[] args) throws IOException {
// Process command-line args
if(args.length <= 0) {
printUsage();
return;
// args = new String[] { "ntp1.sth.netnod.se", "ntp2.sth.netnod.se" };
}
long now = currentTime(args);
System.out.println("Current time: " + new java.util.Date(now));
}
/**
* Prints usage
*/
static void printUsage() {
System.out.println(
"NtpClient - an NTP client for Java.\n" +
"\n" +
"This program connects to an NTP server and prints the current time to the console.\n" +
"\n" +
"\n" +
"Usage: java NtpClient server[ server]*\n" +
"\n" +
"\n" +
"This program is copyright (c) Adam Buckley 2004 and distributed under the terms\n" +
"of the GNU General Public License. This program is distributed in the hope\n" +
"that it will be useful, but WITHOUT ANY WARRANTY; without even the implied\n" +
"warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" +
"General Public License available at http://www.gnu.org/licenses/gpl.html for\n" +
"more details.");
}
}

View File

@@ -0,0 +1,451 @@
package net.i2p.time;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* This class represents a NTP message, as specified in RFC 2030. The message
* format is compatible with all versions of NTP and SNTP.
*
* This class does not support the optional authentication protocol, and
* ignores the key ID and message digest fields.
*
* For convenience, this class exposes message values as native Java types, not
* the NTP-specified data formats. For example, timestamps are
* stored as doubles (as opposed to the NTP unsigned 64-bit fixed point
* format).
*
* However, the contructor NtpMessage(byte[]) and the method toByteArray()
* allow the import and export of the raw NTP message format.
*
*
* Usage example
*
* // Send message
* DatagramSocket socket = new DatagramSocket();
* InetAddress address = InetAddress.getByName("ntp.cais.rnp.br");
* byte[] buf = new NtpMessage().toByteArray();
* DatagramPacket packet = new DatagramPacket(buf, buf.length, address, 123);
* socket.send(packet);
*
* // Get response
* socket.receive(packet);
* System.out.println(msg.toString());
*
*
* This code is copyright (c) Adam Buckley 2004
*
* 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. A HTML version of the GNU General Public License can be
* seen at http://www.gnu.org/licenses/gpl.html
*
* 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.
*
*
* Comments for member variables are taken from RFC2030 by David Mills,
* University of Delaware.
*
* Number format conversion code in NtpMessage(byte[] array) and toByteArray()
* inspired by http://www.pps.jussieu.fr/~jch/enseignement/reseaux/
* NTPMessage.java which is copyright (c) 2003 by Juliusz Chroboczek
*
* @author Adam Buckley
*/
public class NtpMessage {
/**
* This is a two-bit code warning of an impending leap second to be
* inserted/deleted in the last minute of the current day. It's values
* may be as follows:
*
* Value Meaning
* ----- -------
* 0 no warning
* 1 last minute has 61 seconds
* 2 last minute has 59 seconds)
* 3 alarm condition (clock not synchronized)
*/
public byte leapIndicator = 0;
/**
* This value indicates the NTP/SNTP version number. The version number
* is 3 for Version 3 (IPv4 only) and 4 for Version 4 (IPv4, IPv6 and OSI).
* If necessary to distinguish between IPv4, IPv6 and OSI, the
* encapsulating context must be inspected.
*/
public byte version = 3;
/**
* This value indicates the mode, with values defined as follows:
*
* Mode Meaning
* ---- -------
* 0 reserved
* 1 symmetric active
* 2 symmetric passive
* 3 client
* 4 server
* 5 broadcast
* 6 reserved for NTP control message
* 7 reserved for private use
*
* In unicast and anycast modes, the client sets this field to 3 (client)
* in the request and the server sets it to 4 (server) in the reply. In
* multicast mode, the server sets this field to 5 (broadcast).
*/
public byte mode = 0;
/**
* This value indicates the stratum level of the local clock, with values
* defined as follows:
*
* Stratum Meaning
* ----------------------------------------------
* 0 unspecified or unavailable
* 1 primary reference (e.g., radio clock)
* 2-15 secondary reference (via NTP or SNTP)
* 16-255 reserved
*/
public short stratum = 0;
/**
* This value indicates the maximum interval between successive messages,
* in seconds to the nearest power of two. The values that can appear in
* this field presently range from 4 (16 s) to 14 (16284 s); however, most
* applications use only the sub-range 6 (64 s) to 10 (1024 s).
*/
public byte pollInterval = 0;
/**
* This value indicates the precision of the local clock, in seconds to
* the nearest power of two. The values that normally appear in this field
* range from -6 for mains-frequency clocks to -20 for microsecond clocks
* found in some workstations.
*/
public byte precision = 0;
/**
* This value indicates the total roundtrip delay to the primary reference
* source, in seconds. Note that this variable can take on both positive
* and negative values, depending on the relative time and frequency
* offsets. The values that normally appear in this field range from
* negative values of a few milliseconds to positive values of several
* hundred milliseconds.
*/
public double rootDelay = 0;
/**
* This value indicates the nominal error relative to the primary reference
* source, in seconds. The values that normally appear in this field
* range from 0 to several hundred milliseconds.
*/
public double rootDispersion = 0;
/**
* This is a 4-byte array identifying the particular reference source.
* In the case of NTP Version 3 or Version 4 stratum-0 (unspecified) or
* stratum-1 (primary) servers, this is a four-character ASCII string, left
* justified and zero padded to 32 bits. In NTP Version 3 secondary
* servers, this is the 32-bit IPv4 address of the reference source. In NTP
* Version 4 secondary servers, this is the low order 32 bits of the latest
* transmit timestamp of the reference source. NTP primary (stratum 1)
* servers should set this field to a code identifying the external
* reference source according to the following list. If the external
* reference is one of those listed, the associated code should be used.
* Codes for sources not listed can be contrived as appropriate.
*
* Code External Reference Source
* ---- -------------------------
* LOCL uncalibrated local clock used as a primary reference for
* a subnet without external means of synchronization
* PPS atomic clock or other pulse-per-second source
* individually calibrated to national standards
* ACTS NIST dialup modem service
* USNO USNO modem service
* PTB PTB (Germany) modem service
* TDF Allouis (France) Radio 164 kHz
* DCF Mainflingen (Germany) Radio 77.5 kHz
* MSF Rugby (UK) Radio 60 kHz
* WWV Ft. Collins (US) Radio 2.5, 5, 10, 15, 20 MHz
* WWVB Boulder (US) Radio 60 kHz
* WWVH Kaui Hawaii (US) Radio 2.5, 5, 10, 15 MHz
* CHU Ottawa (Canada) Radio 3330, 7335, 14670 kHz
* LORC LORAN-C radionavigation system
* OMEG OMEGA radionavigation system
* GPS Global Positioning Service
* GOES Geostationary Orbit Environment Satellite
*/
public byte[] referenceIdentifier = {0, 0, 0, 0};
/**
* This is the time at which the local clock was last set or corrected, in
* seconds since 00:00 1-Jan-1900.
*/
public double referenceTimestamp = 0;
/**
* This is the time at which the request departed the client for the
* server, in seconds since 00:00 1-Jan-1900.
*/
public double originateTimestamp = 0;
/**
* This is the time at which the request arrived at the server, in seconds
* since 00:00 1-Jan-1900.
*/
public double receiveTimestamp = 0;
/**
* This is the time at which the reply departed the server for the client,
* in seconds since 00:00 1-Jan-1900.
*/
public double transmitTimestamp = 0;
/**
* Constructs a new NtpMessage from an array of bytes.
*/
public NtpMessage(byte[] array) {
// See the packet format diagram in RFC 2030 for details
leapIndicator = (byte) ((array[0] >> 6) & 0x3);
version = (byte) ((array[0] >> 3) & 0x7);
mode = (byte) (array[0] & 0x7);
stratum = unsignedByteToShort(array[1]);
pollInterval = array[2];
precision = array[3];
rootDelay = (array[4] * 256.0) +
unsignedByteToShort(array[5]) +
(unsignedByteToShort(array[6]) / 256.0) +
(unsignedByteToShort(array[7]) / 65536.0);
rootDispersion = (unsignedByteToShort(array[8]) * 256.0) +
unsignedByteToShort(array[9]) +
(unsignedByteToShort(array[10]) / 256.0) +
(unsignedByteToShort(array[11]) / 65536.0);
referenceIdentifier[0] = array[12];
referenceIdentifier[1] = array[13];
referenceIdentifier[2] = array[14];
referenceIdentifier[3] = array[15];
referenceTimestamp = decodeTimestamp(array, 16);
originateTimestamp = decodeTimestamp(array, 24);
receiveTimestamp = decodeTimestamp(array, 32);
transmitTimestamp = decodeTimestamp(array, 40);
}
/**
* Constructs a new NtpMessage in client -> server mode, and sets the
* transmit timestamp to the current time.
*/
public NtpMessage() {
// Note that all the other member variables are already set with
// appropriate default values.
this.mode = 3;
this.transmitTimestamp = (System.currentTimeMillis()/1000.0) + 2208988800.0;
}
/**
* This method constructs the data bytes of a raw NTP packet.
*/
public byte[] toByteArray() {
// All bytes are automatically set to 0
byte[] p = new byte[48];
p[0] = (byte) (leapIndicator << 6 | version << 3 | mode);
p[1] = (byte) stratum;
p[2] = (byte) pollInterval;
p[3] = (byte) precision;
// root delay is a signed 16.16-bit FP, in Java an int is 32-bits
int l = (int) (rootDelay * 65536.0);
p[4] = (byte) ((l >> 24) & 0xFF);
p[5] = (byte) ((l >> 16) & 0xFF);
p[6] = (byte) ((l >> 8) & 0xFF);
p[7] = (byte) (l & 0xFF);
// root dispersion is an unsigned 16.16-bit FP, in Java there are no
// unsigned primitive types, so we use a long which is 64-bits
long ul = (long) (rootDispersion * 65536.0);
p[8] = (byte) ((ul >> 24) & 0xFF);
p[9] = (byte) ((ul >> 16) & 0xFF);
p[10] = (byte) ((ul >> 8) & 0xFF);
p[11] = (byte) (ul & 0xFF);
p[12] = referenceIdentifier[0];
p[13] = referenceIdentifier[1];
p[14] = referenceIdentifier[2];
p[15] = referenceIdentifier[3];
encodeTimestamp(p, 16, referenceTimestamp);
encodeTimestamp(p, 24, originateTimestamp);
encodeTimestamp(p, 32, receiveTimestamp);
encodeTimestamp(p, 40, transmitTimestamp);
return p;
}
/**
* Returns a string representation of a NtpMessage
*/
public String toString() {
String precisionStr = new DecimalFormat("0.#E0").format(Math.pow(2, precision));
return "Leap indicator: " + leapIndicator + "\n" +
"Version: " + version + "\n" +
"Mode: " + mode + "\n" +
"Stratum: " + stratum + "\n" +
"Poll: " + pollInterval + "\n" +
"Precision: " + precision + " (" + precisionStr + " seconds)\n" +
"Root delay: " + new DecimalFormat("0.00").format(rootDelay*1000) + " ms\n" +
"Root dispersion: " + new DecimalFormat("0.00").format(rootDispersion*1000) + " ms\n" +
"Reference identifier: " + referenceIdentifierToString(referenceIdentifier, stratum, version) + "\n" +
"Reference timestamp: " + timestampToString(referenceTimestamp) + "\n" +
"Originate timestamp: " + timestampToString(originateTimestamp) + "\n" +
"Receive timestamp: " + timestampToString(receiveTimestamp) + "\n" +
"Transmit timestamp: " + timestampToString(transmitTimestamp);
}
/**
* Converts an unsigned byte to a short. By default, Java assumes that
* a byte is signed.
*/
public static short unsignedByteToShort(byte b) {
if((b & 0x80)==0x80)
return (short) (128 + (b & 0x7f));
else
return (short) b;
}
/**
* Will read 8 bytes of a message beginning at <code>pointer</code>
* and return it as a double, according to the NTP 64-bit timestamp
* format.
*/
public static double decodeTimestamp(byte[] array, int pointer) {
double r = 0.0;
for(int i=0; i<8; i++) {
r += unsignedByteToShort(array[pointer+i]) * Math.pow(2, (3-i)*8);
}
return r;
}
/**
* Encodes a timestamp in the specified position in the message
*/
public static void encodeTimestamp(byte[] array, int pointer, double timestamp) {
// Converts a double into a 64-bit fixed point
for(int i=0; i<8; i++) {
// 2^24, 2^16, 2^8, .. 2^-32
double base = Math.pow(2, (3-i)*8);
// Capture byte value
array[pointer+i] = (byte) (timestamp / base);
// Subtract captured value from remaining total
timestamp = timestamp - (double) (unsignedByteToShort(array[pointer+i]) * base);
}
// From RFC 2030: It is advisable to fill the non-significant
// low order bits of the timestamp with a random, unbiased
// bitstring, both to avoid systematic roundoff errors and as
// a means of loop detection and replay detection.
array[7+pointer] = (byte) (Math.random()*255.0);
}
/**
* Returns a timestamp (number of seconds since 00:00 1-Jan-1900) as a
* formatted date/time string.
*/
public static String timestampToString(double timestamp) {
if(timestamp==0) return "0";
// timestamp is relative to 1900, utc is used by Java and is relative
// to 1970
double utc = timestamp - (2208988800.0);
// milliseconds
long ms = (long) (utc * 1000.0);
// date/time
String date = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss").format(new Date(ms));
// fraction
double fraction = timestamp - ((long) timestamp);
String fractionSting = new DecimalFormat(".000000").format(fraction);
return date + fractionSting;
}
/**
* Returns a string representation of a reference identifier according
* to the rules set out in RFC 2030.
*/
public static String referenceIdentifierToString(byte[] ref, short stratum, byte version) {
// From the RFC 2030:
// In the case of NTP Version 3 or Version 4 stratum-0 (unspecified)
// or stratum-1 (primary) servers, this is a four-character ASCII
// string, left justified and zero padded to 32 bits.
if(stratum==0 || stratum==1) {
return new String(ref);
}
// In NTP Version 3 secondary servers, this is the 32-bit IPv4
// address of the reference source.
else if(version==3) {
return unsignedByteToShort(ref[0]) + "." +
unsignedByteToShort(ref[1]) + "." +
unsignedByteToShort(ref[2]) + "." +
unsignedByteToShort(ref[3]);
}
// In NTP Version 4 secondary servers, this is the low order 32 bits
// of the latest transmit timestamp of the reference source.
else if(version==4) {
return "" + ((unsignedByteToShort(ref[0]) / 256.0) +
(unsignedByteToShort(ref[1]) / 65536.0) +
(unsignedByteToShort(ref[2]) / 16777216.0) +
(unsignedByteToShort(ref[3]) / 4294967296.0));
}
return "";
}
}

View File

@@ -0,0 +1,109 @@
package net.i2p.time;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Periodically query a series of NTP servers and post the offset
* to a given URL. It tries the NTP servers in order, contacting them
* using UDP port 123, and sends the current date to the URL specified
* (specifically, URL+"&now=" + yyyyMMdd_HH:mm:ss.SSS in the UK locale).
* It does this every 5 minutes, forever.
*
* Usage: <pre>
* Timestamper URL ntpServer1[ ntpServer2]*
* </pre>
*/
public class Timestamper implements Runnable {
private static Log _log = new Log(Timestamper.class);
private String _targetURL;
private String _serverList[];
private int DELAY_MS = 5*60*1000;
public Timestamper(String url, String serverNames[]) {
if (_log.shouldLog(Log.INFO))
_log.info("Creating new timestamper pointing at " + url);
_targetURL = url;
_serverList = serverNames;
}
public void startTimestamper() {
if (_log.shouldLog(Log.INFO))
_log.info("Starting timestamper pointing at " + _targetURL);
I2PThread t = new I2PThread(this, "Timestamper");
t.setPriority(I2PThread.MIN_PRIORITY);
t.start();
}
public void run() {
if (_log.shouldLog(Log.INFO))
_log.info("Starting up timestamper");
try {
while (true) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Querying servers " + _serverList);
long now = NtpClient.currentTime(_serverList);
if (now < 0) {
_log.error("Unable to contact any of the NTP servers - network disconnect?");
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Stamp time");
stampTime(now);
}
try { Thread.sleep(DELAY_MS); } catch (InterruptedException ie) {}
}
} catch (Throwable t) {
_log.log(Log.CRIT, "Timestamper died!", t);
}
}
/**
* Send an HTTP request to a given URL specifying the current time
*/
private void stampTime(long now) {
try {
String toRequest = _targetURL + "&now=" + getNow(now);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Stamping [" + toRequest + "]");
URL url = new URL(toRequest);
Object o = url.getContent();
// ignore the content
} catch (MalformedURLException mue) {
_log.error("Invalid URL", mue);
} catch (IOException ioe) {
_log.error("Error stamping the time", ioe);
}
}
private SimpleDateFormat _fmt = new SimpleDateFormat("yyyyMMdd_HH:mm:ss.SSS", Locale.UK);
private String getNow(long now) {
synchronized (_fmt) {
return _fmt.format(new Date(now));
}
}
public static void main(String args[]) {
if ( (args == null) || (args.length < 2) ) {
usage();
return;
//args = new String[] { "http://dev.i2p.net:80/somePath?pass=password", "ntp1.sth.netnod.se", "ntp2.sth.netnod.se" };
}
String servers[] = new String[args.length-1];
System.arraycopy(args, 1, servers, 0, servers.length);
Timestamper ts = new Timestamper(args[0], servers);
ts.startTimestamper();
}
private static void usage() {
System.err.println("Usage: Timestamper URL ntpServer[ ntpServer]*");
_log.error("Usage: Timestamper URL ntpServer[ ntpServer]*");
}
}

View File

@@ -23,6 +23,7 @@
<ant dir="apps/sam/java/" target="jar" />
<ant dir="apps/heartbeat/java/" target="jar" />
<ant dir="apps/netmonitor/java/" target="jar" />
<ant dir="apps/time/java/" target="jar" />
<ant dir="installer/java/" target="jar" />
</target>
<target name="compile" />
@@ -36,6 +37,7 @@
<copy file="apps/sam/java/build/sam.jar" todir="build/" />
<copy file="apps/heartbeat/java/build/heartbeat.jar" todir="build/" />
<copy file="apps/netmonitor/java/build/netmonitor.jar" todir="build/" />
<copy file="apps/time/java/build/timestamper.jar" todir="build/" />
<copy file="installer/java/build/install.jar" todir="build/" />
<copy file="installer/java/build/guiinstall.jar" todir="build/" />
<copy file="installer/java/build/fetchseeds.jar" todir="build/" />
@@ -64,6 +66,7 @@
<ant dir="apps/sam/java/" target="distclean" />
<ant dir="apps/heartbeat/java/" target="distclean" />
<ant dir="apps/netmonitor/java/" target="distclean" />
<ant dir="apps/time/java/" target="distclean" />
<ant dir="installer/java/" target="distclean" />
<delete>
<fileset dir="." includes="**/*.class" />

View File

@@ -14,8 +14,8 @@ package net.i2p;
*
*/
public class CoreVersion {
public final static String ID = "$Revision: 1.3 $ $Date: 2004/04/20 04:18:54 $";
public final static String VERSION = "0.3.1";
public final static String ID = "$Revision: 1.6 $ $Date: 2004/05/13 18:49:08 $";
public final static String VERSION = "0.3.1.3";
public static void main(String args[]) {
System.out.println("I2P Core version: " + VERSION);

View File

@@ -1,26 +1,26 @@
package net.i2p;
import net.i2p.stat.StatManager;
import net.i2p.crypto.SessionKeyManager;
import net.i2p.crypto.PersistentSessionKeyManager;
import net.i2p.crypto.ElGamalAESEngine;
import net.i2p.crypto.ElGamalEngine;
import net.i2p.crypto.DummyElGamalEngine;
import net.i2p.crypto.SHA256Generator;
import net.i2p.crypto.HMACSHA256Generator;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import net.i2p.client.naming.NamingService;
import net.i2p.crypto.AESEngine;
import net.i2p.crypto.CryptixAESEngine;
import net.i2p.crypto.DSAEngine;
import net.i2p.client.naming.NamingService;
import net.i2p.util.LogManager;
import net.i2p.util.Clock;
import net.i2p.util.RandomSource;
import net.i2p.data.RoutingKeyGenerator;
import net.i2p.crypto.DummyElGamalEngine;
import net.i2p.crypto.ElGamalAESEngine;
import net.i2p.crypto.ElGamalEngine;
import net.i2p.crypto.HMACSHA256Generator;
import net.i2p.crypto.KeyGenerator;
import java.util.Properties;
import java.util.HashSet;
import java.util.Set;
import net.i2p.crypto.PersistentSessionKeyManager;
import net.i2p.crypto.SHA256Generator;
import net.i2p.crypto.SessionKeyManager;
import net.i2p.data.RoutingKeyGenerator;
import net.i2p.stat.StatManager;
import net.i2p.util.Clock;
import net.i2p.util.LogManager;
import net.i2p.util.RandomSource;
/**
* <p>Provide a base scope for accessing singletons that I2P exposes. Rather than

View File

@@ -24,14 +24,13 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.Clock;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
import net.i2p.util.LogManager;
import net.i2p.I2PAppContext;
/**
* ATalk - anonymous talk, demonstrating a trivial I2P usage scenario.

View File

@@ -9,9 +9,9 @@ package net.i2p.client;
*
*/
import net.i2p.I2PAppContext;
import net.i2p.data.i2cp.DisconnectMessage;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.I2PAppContext;
/**
* Handle I2CP disconnect messages from the router

View File

@@ -9,8 +9,8 @@ package net.i2p.client;
*
*/
import net.i2p.util.Log;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
/**
* Base class for handling I2CP messages

View File

@@ -12,7 +12,7 @@ package net.i2p.client;
import java.util.HashSet;
import java.util.Set;
import net.i2p.crypto.ElGamalAESEngine;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.data.LeaseSet;
@@ -32,7 +32,6 @@ import net.i2p.data.i2cp.SendMessageMessage;
import net.i2p.data.i2cp.SessionConfig;
import net.i2p.util.Log;
import net.i2p.util.RandomSource;
import net.i2p.I2PAppContext;
/**
* Produce the various messages the session needs to send to the router.

View File

@@ -14,6 +14,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.crypto.KeyGenerator;
import net.i2p.data.Certificate;
@@ -22,7 +23,6 @@ import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SigningPublicKey;
import net.i2p.I2PAppContext;
/**
* Base client implementation

View File

@@ -12,6 +12,7 @@ package net.i2p.client;
import java.util.HashMap;
import java.util.Map;
import net.i2p.I2PAppContext;
import net.i2p.data.i2cp.DisconnectMessage;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.data.i2cp.MessageStatusMessage;
@@ -19,7 +20,6 @@ import net.i2p.data.i2cp.RequestLeaseSetMessage;
import net.i2p.data.i2cp.SessionStatusMessage;
import net.i2p.data.i2cp.SetDateMessage;
import net.i2p.util.Log;
import net.i2p.I2PAppContext;
/**
* Contains a map of message handlers that a session will want to use

View File

@@ -23,6 +23,7 @@ import java.util.Map;
import java.util.Properties;
import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.data.LeaseSet;
@@ -36,10 +37,8 @@ import net.i2p.data.i2cp.I2CPMessageException;
import net.i2p.data.i2cp.I2CPMessageReader;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.data.i2cp.SessionId;
import net.i2p.util.Clock;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
import net.i2p.I2PAppContext;
/**
* Implementation of an I2P session running over TCP. This class is NOT thread safe -
@@ -148,7 +147,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
_portNum = Integer.parseInt(portNum);
} catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Invalid port number specified, defaulting to "
_log.warn(getPrefix() + "Invalid port number specified, defaulting to "
+ TestServer.LISTEN_PORT, nfe);
_portNum = TestServer.LISTEN_PORT;
}
@@ -173,7 +172,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping line.* property: " + key);
} else if ((key.length() > 255) || (val.length() > 255)) {
if (_log.shouldLog(Log.WARN))
_log.warn("Not passing on property ["
_log.warn(getPrefix() + "Not passing on property ["
+ key
+ "] in the session configuration as the value is too long (max = 255): "
+ val);
@@ -223,7 +222,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
_closed = false;
long startConnect = _context.clock().now();
try {
if (_log.shouldLog(Log.DEBUG)) _log.debug("connect begin to " + _hostname + ":" + _portNum);
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "connect begin to " + _hostname + ":" + _portNum);
_socket = new Socket(_hostname, _portNum);
_out = _socket.getOutputStream();
synchronized (_out) {
@@ -231,12 +230,12 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
}
InputStream in = _socket.getInputStream();
_reader = new I2CPMessageReader(in, this);
if (_log.shouldLog(Log.DEBUG)) _log.debug("before startReading");
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "before startReading");
_reader.startReading();
if (_log.shouldLog(Log.DEBUG)) _log.debug("Before getDate");
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Before getDate");
sendMessage(new GetDateMessage());
if (_log.shouldLog(Log.DEBUG)) _log.debug("After getDate / begin waiting for a response");
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "After getDate / begin waiting for a response");
while (!_dateReceived) {
try {
synchronized (_dateReceivedLock) {
@@ -245,11 +244,11 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
} catch (InterruptedException ie) {
}
}
if (_log.shouldLog(Log.DEBUG)) _log.debug("After received a SetDate response");
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "After received a SetDate response");
if (_log.shouldLog(Log.DEBUG)) _log.debug("Before producer.connect()");
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Before producer.connect()");
_producer.connect(this);
if (_log.shouldLog(Log.DEBUG)) _log.debug("After producer.connect()");
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "After producer.connect()");
// wait until we have created a lease set
while (_leaseSet == null) {
@@ -262,15 +261,15 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
}
long connected = _context.clock().now();
if (_log.shouldLog(Log.INFO))
_log.info("Lease set created with inbound tunnels after "
+ (connected - startConnect)
+ "ms - ready to participate in the network!");
_log.info(getPrefix() + "Lease set created with inbound tunnels after "
+ (connected - startConnect)
+ "ms - ready to participate in the network!");
} catch (UnknownHostException uhe) {
_closed = true;
throw new I2PSessionException("Invalid session configuration", uhe);
throw new I2PSessionException(getPrefix() + "Invalid session configuration", uhe);
} catch (IOException ioe) {
_closed = true;
throw new I2PSessionException("Problem connecting to " + _hostname + " on port " + _portNum, ioe);
throw new I2PSessionException(getPrefix() + "Problem connecting to " + _hostname + " on port " + _portNum, ioe);
}
}
@@ -289,7 +288,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* Report abuse with regards to the given messageId
*/
public void reportAbuse(int msgId, int severity) throws I2PSessionException {
if (isClosed()) throw new I2PSessionException("Already closed");
if (isClosed()) throw new I2PSessionException(getPrefix() + "Already closed");
_producer.reportAbuse(this, msgId, severity);
}
@@ -318,6 +317,8 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
return tags;
}
private static volatile long __notifierId = 0;
/**
* Recieve a payload message and let the app know its available
*/
@@ -327,7 +328,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
byte data[] = msg.getPayload().getUnencryptedData();
if ((data == null) || (data.length <= 0)) {
if (_log.shouldLog(Log.ERROR))
_log.error("addNewMessage of a message with no unencrypted data",
_log.error(getPrefix() + "addNewMessage of a message with no unencrypted data",
new Exception("Empty message"));
} else {
final long size = data.length;
@@ -337,9 +338,12 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
_sessionListener.messageAvailable(I2PSessionImpl.this, id, size);
}
});
notifier.setName("Notifier [" + _sessionId + "/" + id + "]");
long nid = ++__notifierId;
notifier.setName("Notifier " + nid);
notifier.setDaemon(true);
notifier.start();
if (_log.shouldLog(Log.INFO))
_log.info(getPrefix() + "Notifier " + nid + " is for session " + _sessionId + ", message " + id + "]");
}
}
@@ -351,11 +355,11 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
I2CPMessageHandler handler = _handlerMap.getHandler(message.getType());
if (handler == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("Unknown message or unhandleable message received: type = "
_log.warn(getPrefix() + "Unknown message or unhandleable message received: type = "
+ message.getType());
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Message received of type " + message.getType()
_log.debug(getPrefix() + "Message received of type " + message.getType()
+ " to be handled by " + handler);
handler.handleMessage(message, this);
}
@@ -420,11 +424,11 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
message.writeMessage(_out);
_out.flush();
}
if (_log.shouldLog(Log.DEBUG)) _log.debug("Message written out and flushed");
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Message written out and flushed");
} catch (I2CPMessageException ime) {
throw new I2PSessionException("Error writing out the message", ime);
throw new I2PSessionException(getPrefix() + "Error writing out the message", ime);
} catch (IOException ioe) {
throw new I2PSessionException("Error writing out the message", ioe);
throw new I2PSessionException(getPrefix() + "Error writing out the message", ioe);
}
}
@@ -432,7 +436,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* Pass off the error to the listener
*/
void propogateError(String msg, Throwable error) {
if (_log.shouldLog(Log.ERROR)) _log.error("Error occurred: " + msg, error);
if (_log.shouldLog(Log.ERROR)) _log.error(getPrefix() + "Error occurred: " + msg, error);
if (_sessionListener != null) _sessionListener.errorOccurred(this, msg, error);
}
@@ -446,8 +450,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
public void destroySession(boolean sendDisconnect) {
if (_closed) return;
if (_log.shouldLog(Log.DEBUG)) _log.debug("Destroy the session", new Exception("DestroySession()"));
_closed = true;
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Destroy the session", new Exception("DestroySession()"));
if (sendDisconnect) {
try {
_producer.disconnect(this);
@@ -455,6 +458,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
propogateError("Error destroying the session", ipe);
}
}
_closed = true;
closeSocket();
if (_sessionListener != null) _sessionListener.disconnected(this);
}
@@ -464,7 +468,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
*
*/
private void closeSocket() {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Closing the socket", new Exception("closeSocket"));
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Closing the socket", new Exception("closeSocket"));
_closed = true;
if (_reader != null) _reader.stopReading();
_reader = null;
@@ -484,24 +488,24 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* Recieve notification that the I2CP connection was disconnected
*/
public void disconnected(I2CPMessageReader reader) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Disconnected", new Exception("Disconnected"));
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Disconnected", new Exception("Disconnected"));
disconnect();
}
protected void disconnect() {
if (_closed) return;
if (_log.shouldLog(Log.DEBUG)) _log.debug("Disconnect() called", new Exception("Disconnect"));
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Disconnect() called", new Exception("Disconnect"));
if (shouldReconnect()) {
if (reconnect()) {
if (_log.shouldLog(Log.INFO)) _log.info("I2CP reconnection successful");
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "I2CP reconnection successful");
return;
} else {
if (_log.shouldLog(Log.ERROR)) _log.error("I2CP reconnection failed");
if (_log.shouldLog(Log.ERROR)) _log.error(getPrefix() + "I2CP reconnection failed");
}
}
if (_log.shouldLog(Log.ERROR))
_log.error("Disconned from the router, and not trying to reconnect further. I hope you're not hoping anything else will happen");
_log.error(getPrefix() + "Disconned from the router, and not trying to reconnect further. I hope you're not hoping anything else will happen");
if (_sessionListener != null) _sessionListener.disconnected(this);
_closed = true;
@@ -521,19 +525,21 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
_totalReconnectAttempts++;
} else {
if (_log.shouldLog(Log.CRIT))
_log.log(Log.CRIT, "Max number of reconnects exceeded ["
_log.log(Log.CRIT, getPrefix() + "Max number of reconnects exceeded ["
+ _totalReconnectAttempts + "], we give up!");
return false;
}
if (_log.shouldLog(Log.INFO)) _log.info("Reconnecting...");
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "Reconnecting...");
for (int i = 0; i < MAX_RECONNECT_ATTEMPTS; i++) {
try {
connect();
return true;
} catch (I2PSessionException ise) {
if (_log.shouldLog(Log.ERROR)) _log.error("Error reconnecting on attempt " + i, ise);
if (_log.shouldLog(Log.ERROR)) _log.error(getPrefix() + "Error reconnecting on attempt " + i, ise);
}
}
return false;
}
protected String getPrefix() { return "[" + (_sessionId == null ? -1 : _sessionId.getSessionId()) + "]: "; }
}

View File

@@ -15,18 +15,14 @@ import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import net.i2p.crypto.KeyGenerator;
import net.i2p.crypto.SessionKeyManager;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.util.RandomSource;
import net.i2p.I2PAppContext;
/**
* Thread safe implementation of an I2P session running over TCP.
@@ -65,6 +61,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
}
public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException {
return sendMessage(dest, payload, new SessionKey(), new HashSet(64));
}
@@ -106,19 +103,19 @@ class I2PSessionImpl2 extends I2PSessionImpl {
} else if (_context.sessionKeyManager().getAvailableTimeLeft(dest.getPublicKey(), key) < 30 * 1000) {
// if we have > 10 tags, but they expire in under 30 seconds, we want more
sentTags = createNewTags(50);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Tags are almost expired, adding 50 new ones");
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Tags are almost expired, adding 50 new ones");
}
SessionKey newKey = null;
if (false) // rekey
newKey = _context.keyGenerator().generateSessionKey();
long nonce = (long)_context.random().nextInt(Integer.MAX_VALUE);
MessageState state = new MessageState(nonce);
MessageState state = new MessageState(nonce, getPrefix());
state.setKey(key);
state.setTags(sentTags);
state.setNewKey(newKey);
state.setTo(dest);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Setting key = " + key);
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Setting key = " + key);
if (keyUsed != null) {
if (newKey != null)
@@ -136,7 +133,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
_sendingStates.add(state);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Adding sending state " + state.getMessageId() + " / "
_log.debug(getPrefix() + "Adding sending state " + state.getMessageId() + " / "
+ state.getNonce());
_producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey);
state.waitFor(MessageStatusMessage.STATUS_SEND_ACCEPTED,
@@ -146,18 +143,18 @@ class I2PSessionImpl2 extends I2PSessionImpl {
}
boolean found = state.received(MessageStatusMessage.STATUS_SEND_ACCEPTED);
if (_log.shouldLog(Log.DEBUG))
_log.debug("After waitFor sending state " + state.getMessageId().getMessageId()
_log.debug(getPrefix() + "After waitFor sending state " + state.getMessageId().getMessageId()
+ " / " + state.getNonce() + " found = " + found);
if (found) {
if (_log.shouldLog(Log.INFO))
_log.info("Message sent after " + state.getElapsed() + "ms with "
_log.info(getPrefix() + "Message sent after " + state.getElapsed() + "ms with "
+ payload.length + " bytes");
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Message send failed after " + state.getElapsed() + "ms with "
_log.info(getPrefix() + "Message send failed after " + state.getElapsed() + "ms with "
+ payload.length + " bytes");
if (_log.shouldLog(Log.ERROR))
_log.error("Never received *accepted* from the router! dropping and reconnecting");
_log.error(getPrefix() + "Never received *accepted* from the router! dropping and reconnecting");
disconnect();
return false;
}
@@ -175,19 +172,19 @@ class I2PSessionImpl2 extends I2PSessionImpl {
} else if (_context.sessionKeyManager().getAvailableTimeLeft(dest.getPublicKey(), key) < 30 * 1000) {
// if we have > 10 tags, but they expire in under 30 seconds, we want more
sentTags = createNewTags(50);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Tags are almost expired, adding 50 new ones");
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Tags are almost expired, adding 50 new ones");
}
SessionKey newKey = null;
if (false) // rekey
newKey = _context.keyGenerator().generateSessionKey();
long nonce = (long)_context.random().nextInt(Integer.MAX_VALUE);
MessageState state = new MessageState(nonce);
MessageState state = new MessageState(nonce, getPrefix());
state.setKey(key);
state.setTags(sentTags);
state.setNewKey(newKey);
state.setTo(dest);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Setting key = " + key);
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Setting key = " + key);
if (keyUsed != null) {
if (newKey != null)
@@ -205,7 +202,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
_sendingStates.add(state);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Adding sending state " + state.getMessageId() + " / "
_log.debug(getPrefix() + "Adding sending state " + state.getMessageId() + " / "
+ state.getNonce());
_producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey);
if (isGuaranteed())
@@ -222,28 +219,28 @@ class I2PSessionImpl2 extends I2PSessionImpl {
if ((!accepted) || (state.getMessageId() == null)) {
if (_log.shouldLog(Log.ERROR))
_log.error("State with nonce " + state.getNonce()
_log.error(getPrefix() + "State with nonce " + state.getNonce()
+ " was not accepted? (no messageId!! found=" + found
+ " msgId=" + state.getMessageId() + ")",
new Exception("Race on accept/success status messages?"));
new Exception("Race on accept/success status messages, or reconnected?"));
nackTags(state);
//if (_log.shouldLog(Log.CRIT))
// _log.log(Log.CRIT, "Disconnecting/reconnecting because we never were accepted!");
//disconnect();
//return false;
return false;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("After waitFor sending state " + state.getMessageId().getMessageId()
_log.debug(getPrefix() + "After waitFor sending state " + state.getMessageId().getMessageId()
+ " / " + state.getNonce() + " found = " + found);
if (found) {
if (_log.shouldLog(Log.INFO))
_log.info("Message sent after " + state.getElapsed() + "ms with "
_log.info(getPrefix() + "Message sent after " + state.getElapsed() + "ms with "
+ payload.length + " bytes");
ackTags(state);
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Message send failed after " + state.getElapsed() + "ms with "
_log.info(getPrefix() + "Message send failed after " + state.getElapsed() + "ms with "
+ payload.length + " bytes");
nackTags(state);
}
@@ -252,7 +249,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
private void ackTags(MessageState state) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("ack tags for msgId " + state.getMessageId() + " / "
_log.debug(getPrefix() + "ack tags for msgId " + state.getMessageId() + " / "
+ state.getNonce() + " key = " + state.getKey() + ", tags = "
+ state.getTags());
if ((state.getTags() != null) && (state.getTags().size() > 0)) {
@@ -265,26 +262,26 @@ class I2PSessionImpl2 extends I2PSessionImpl {
private void nackTags(MessageState state) {
if (_log.shouldLog(Log.INFO))
_log.info("nack tags for msgId " + state.getMessageId() + " / " + state.getNonce()
_log.info(getPrefix() + "nack tags for msgId " + state.getMessageId() + " / " + state.getNonce()
+ " key = " + state.getKey());
_context.sessionKeyManager().failTags(state.getTo().getPublicKey());
}
public void receiveStatus(int msgId, long nonce, int status) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received status " + status + " for msgId " + msgId + " / " + nonce);
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Received status " + status + " for msgId " + msgId + " / " + nonce);
MessageState state = null;
synchronized (_sendingStates) {
for (Iterator iter = _sendingStates.iterator(); iter.hasNext();) {
state = (MessageState) iter.next();
if (_log.shouldLog(Log.DEBUG)) _log.debug("State " + state.getMessageId() + " / " + state.getNonce());
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "State " + state.getMessageId() + " / " + state.getNonce());
if (state.getNonce() == nonce) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Found a matching state");
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Found a matching state");
break;
} else if ((state.getMessageId() != null) && (state.getMessageId().getMessageId() == msgId)) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Found a matching state by msgId");
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Found a matching state by msgId");
break;
} else {
if (_log.shouldLog(Log.DEBUG)) _log.debug("State does not match");
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "State does not match");
state = null;
}
}
@@ -299,7 +296,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
state.receive(status);
} else {
if (_log.shouldLog(Log.INFO))
_log.info("No matching state for messageId " + msgId + " / " + nonce
_log.info(getPrefix() + "No matching state for messageId " + msgId + " / " + nonce
+ " w/ status = " + status);
}
}
@@ -322,7 +319,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
MessageState state = (MessageState) iter.next();
state.cancel();
}
if (_log.shouldLog(Log.INFO)) _log.info("Disconnecting " + _sendingStates.size() + " states");
if (_log.shouldLog(Log.INFO)) _log.info(getPrefix() + "Disconnecting " + _sendingStates.size() + " states");
_sendingStates.clear();
}
}

View File

@@ -9,14 +9,13 @@ package net.i2p.client;
*
*/
import net.i2p.crypto.ElGamalAESEngine;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.data.Payload;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.data.i2cp.ReceiveMessageEndMessage;
import net.i2p.I2PAppContext;
/**
* Handle I2CP MessagePayloadMessages from the router delivering the contents

View File

@@ -20,6 +20,7 @@ import net.i2p.util.Log;
class MessageState {
private final static Log _log = new Log(MessageState.class);
private long _nonce;
private String _prefix;
private MessageId _id;
private Set _receivedStatus;
private SessionKey _key;
@@ -30,8 +31,9 @@ class MessageState {
private long _created;
private Object _lock = new Object();
public MessageState(long nonce) {
public MessageState(long nonce, String prefix) {
_nonce = nonce;
_prefix = prefix;
_id = null;
_receivedStatus = new HashSet();
_cancelled = false;
@@ -64,7 +66,8 @@ class MessageState {
}
public void setKey(SessionKey key) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Setting key [" + _key + "] to [" + key + "]");
if (_log.shouldLog(Log.DEBUG))
_log.debug(_prefix + "Setting key [" + _key + "] to [" + key + "]");
_key = key;
}
@@ -105,11 +108,13 @@ class MessageState {
if (_cancelled) return;
long timeToWait = expiration - Clock.getInstance().now();
if (timeToWait <= 0) {
if (_log.shouldLog(Log.WARN)) _log.warn("Expired waiting for the status [" + status + "]");
if (_log.shouldLog(Log.WARN))
_log.warn(_prefix + "Expired waiting for the status [" + status + "]");
return;
}
if (isSuccess(status) || isFailure(status)) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received a confirm (one way or the other)");
if (_log.shouldLog(Log.DEBUG))
_log.debug(_prefix + "Received a confirm (one way or the other)");
return;
}
if (timeToWait > 5000) {
@@ -133,21 +138,22 @@ class MessageState {
boolean rv = false;
if (_log.shouldLog(Log.DEBUG)) _log.debug("isSuccess(" + wantedStatus + "): " + received);
if (_log.shouldLog(Log.DEBUG))
_log.debug(_prefix + "isSuccess(" + wantedStatus + "): " + received);
for (Iterator iter = received.iterator(); iter.hasNext();) {
Integer val = (Integer) iter.next();
int recv = val.intValue();
switch (recv) {
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
if (_log.shouldLog(Log.WARN))
_log.warn("Received best effort failure after " + getElapsed() + " from "
+ this.toString());
_log.warn(_prefix + "Received best effort failure after " + getElapsed() + " from "
+ toString());
rv = false;
break;
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
if (_log.shouldLog(Log.WARN))
_log.warn("Received guaranteed failure after " + getElapsed() + " from "
+ this.toString());
_log.warn(_prefix + "Received guaranteed failure after " + getElapsed() + " from "
+ toString());
rv = false;
break;
case MessageStatusMessage.STATUS_SEND_ACCEPTED:
@@ -155,35 +161,36 @@ class MessageState {
return true; // if we're only looking for accepted, take it directly (don't let any GUARANTEED_* override it)
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Got accepted, but we're waiting for more from "
+ this.toString());
_log.debug(_prefix + "Got accepted, but we're waiting for more from "
+ toString());
continue;
// ignore accepted, as we want something better
}
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS:
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received best effort success after " + getElapsed()
+ " from " + this.toString());
_log.debug(_prefix + "Received best effort success after " + getElapsed()
+ " from " + toString());
if (wantedStatus == recv) {
rv = true;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Not guaranteed success, but best effort after "
+ getElapsed() + " will do... from " + this.toString());
_log.debug(_prefix + "Not guaranteed success, but best effort after "
+ getElapsed() + " will do... from " + toString());
rv = true;
}
break;
case MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS:
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received guaranteed success after " + getElapsed() + " from "
+ this.toString());
_log.debug(_prefix + "Received guaranteed success after " + getElapsed() + " from "
+ toString());
// even if we're waiting for best effort success, guaranteed is good enough
rv = true;
break;
case -1:
continue;
default:
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received something else [" + recv + "]...");
if (_log.shouldLog(Log.DEBUG))
_log.debug(_prefix + "Received something else [" + recv + "]...");
}
}
return rv;
@@ -197,21 +204,22 @@ class MessageState {
}
boolean rv = false;
if (_log.shouldLog(Log.DEBUG)) _log.debug("isFailure(" + wantedStatus + "): " + received);
if (_log.shouldLog(Log.DEBUG))
_log.debug(_prefix + "isFailure(" + wantedStatus + "): " + received);
for (Iterator iter = received.iterator(); iter.hasNext();) {
Integer val = (Integer) iter.next();
int recv = val.intValue();
switch (recv) {
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
if (_log.shouldLog(Log.DEBUG))
_log.warn("Received best effort failure after " + getElapsed() + " from "
+ this.toString());
_log.warn(_prefix + "Received best effort failure after " + getElapsed() + " from "
+ toString());
rv = true;
break;
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
if (_log.shouldLog(Log.DEBUG))
_log.warn("Received guaranteed failure after " + getElapsed() + " from "
+ this.toString());
_log.warn(_prefix + "Received guaranteed failure after " + getElapsed() + " from "
+ toString());
rv = true;
break;
case MessageStatusMessage.STATUS_SEND_ACCEPTED:
@@ -219,36 +227,37 @@ class MessageState {
rv = false;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Got accepted, but we're waiting for more from "
+ this.toString());
_log.debug(_prefix + "Got accepted, but we're waiting for more from "
+ toString());
continue;
// ignore accepted, as we want something better
}
break;
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS:
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received best effort success after " + getElapsed()
+ " from " + this.toString());
_log.debug(_prefix + "Received best effort success after " + getElapsed()
+ " from " + toString());
if (wantedStatus == recv) {
rv = false;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Not guaranteed success, but best effort after "
+ getElapsed() + " will do... from " + this.toString());
_log.debug(_prefix + "Not guaranteed success, but best effort after "
+ getElapsed() + " will do... from " + toString());
rv = false;
}
break;
case MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS:
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received guaranteed success after " + getElapsed() + " from "
+ this.toString());
_log.debug(_prefix + "Received guaranteed success after " + getElapsed() + " from "
+ toString());
// even if we're waiting for best effort success, guaranteed is good enough
rv = false;
break;
case -1:
continue;
default:
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received something else [" + recv + "]...");
if (_log.shouldLog(Log.DEBUG))
_log.debug(_prefix + "Received something else [" + recv + "]...");
}
}
return rv;

View File

@@ -9,10 +9,10 @@ package net.i2p.client;
*
*/
import net.i2p.I2PAppContext;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.data.i2cp.ReceiveMessageBeginMessage;
import net.i2p.I2PAppContext;
/**
* Handle I2CP MessageStatusMessages from the router. This currently only takes

View File

@@ -12,6 +12,7 @@ package net.i2p.client;
import java.util.HashMap;
import java.util.Map;
import net.i2p.I2PAppContext;
import net.i2p.crypto.KeyGenerator;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
@@ -25,7 +26,6 @@ import net.i2p.data.SigningPublicKey;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.RequestLeaseSetMessage;
import net.i2p.util.Log;
import net.i2p.I2PAppContext;
/**
* Handle I2CP RequestLeaseSetMessage from the router by granting all leases

View File

@@ -9,9 +9,9 @@ package net.i2p.client;
*
*/
import net.i2p.I2PAppContext;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.SessionStatusMessage;
import net.i2p.I2PAppContext;
/**
* Handle I2CP SessionStatusMessagese from the router, updating the session as

View File

@@ -9,10 +9,10 @@ package net.i2p.client;
*
*/
import net.i2p.I2PAppContext;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.SetDateMessage;
import net.i2p.util.Clock;
import net.i2p.I2PAppContext;
/**
* Handle I2CP time messages from the router

View File

@@ -9,7 +9,6 @@ package net.i2p.client.datagram;
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import net.i2p.crypto.DSAEngine;
@@ -17,7 +16,6 @@ import net.i2p.crypto.SHA256Generator;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.data.Signature;
import net.i2p.util.HexDump;
import net.i2p.util.Log;
/**

View File

@@ -7,8 +7,8 @@
*/
package net.i2p.client.naming;
import net.i2p.data.Destination;
import net.i2p.I2PAppContext;
import net.i2p.data.Destination;
/**
* A Dummy naming service that can only handle base64 destinations.

View File

@@ -12,9 +12,9 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.data.Destination;
import net.i2p.util.Log;
import net.i2p.I2PAppContext;
/**
* A naming service based on the "hosts.txt" file.

View File

@@ -7,12 +7,12 @@
*/
package net.i2p.client.naming;
import java.lang.reflect.Constructor;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.Log;
import net.i2p.I2PAppContext;
import java.lang.reflect.Constructor;
/**
* Naming services create a subclass of this class.

View File

@@ -13,13 +13,13 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.SessionKey;
import net.i2p.util.Log;
import net.i2p.util.RandomSource;
import net.i2p.I2PAppContext;
/**
* Dummy wrapper for AES cipher operation.

View File

@@ -17,6 +17,7 @@ import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
@@ -24,7 +25,6 @@ import net.i2p.data.SessionKey;
import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.util.RandomSource;
import net.i2p.I2PAppContext;
/**
* This reads an underlying stream as written by AESOutputStream - AES256 encrypted

View File

@@ -15,10 +15,10 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import net.i2p.data.SessionKey;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.SessionKey;
import net.i2p.util.Log;
/**
* This writes everything as CBC with PKCS#5 padding, but each block is padded

View File

@@ -11,9 +11,9 @@ package net.i2p.crypto;
import java.security.InvalidKeyException;
import net.i2p.I2PAppContext;
import net.i2p.data.SessionKey;
import net.i2p.util.Log;
import net.i2p.I2PAppContext;
/**
* Wrapper for AES cypher operation using Cryptix's Rijndael implementation. Implements

View File

@@ -13,6 +13,7 @@ import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
import net.i2p.data.SessionKey;
import net.i2p.util.Clock;
@@ -20,7 +21,6 @@ import net.i2p.util.I2PThread;
import net.i2p.util.Log;
import net.i2p.util.NativeBigInteger;
import net.i2p.util.RandomSource;
import net.i2p.I2PAppContext;
/**
* Generate a new session key through a diffie hellman exchange. This uses the

View File

@@ -31,15 +31,13 @@ package net.i2p.crypto;
import java.math.BigInteger;
import net.i2p.I2PAppContext;
import net.i2p.data.Hash;
import net.i2p.data.Signature;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SigningPublicKey;
import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.util.NativeBigInteger;
import net.i2p.util.RandomSource;
import net.i2p.I2PAppContext;
public class DSAEngine {
private Log _log;

View File

@@ -12,12 +12,12 @@ package net.i2p.crypto;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey;
import net.i2p.util.Log;
import net.i2p.I2PAppContext;
/**
* Fake ElG E and D, useful for when performance isn't being tested

View File

@@ -18,6 +18,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
@@ -25,11 +26,7 @@ import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.stat.StatManager;
import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.util.RandomSource;
import net.i2p.I2PAppContext;
/**
* Handles the actual ElGamal+AES encryption and decryption scenarios using the

View File

@@ -33,17 +33,16 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import net.i2p.I2PAppContext;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey;
import net.i2p.stat.StatManager;
import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.util.NativeBigInteger;
import net.i2p.util.RandomSource;
import net.i2p.I2PAppContext;
/**
* Wrapper for ElGamal encryption/signature schemes.

View File

@@ -1,9 +1,9 @@
package net.i2p.crypto;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.SessionKey;
import net.i2p.I2PAppContext;
/**
* Calculate the HMAC-SHA256 of a key+message. Currently FAKE - returns a stupid

View File

@@ -11,6 +11,7 @@ package net.i2p.crypto;
import java.math.BigInteger;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey;
@@ -22,7 +23,6 @@ import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.util.NativeBigInteger;
import net.i2p.util.RandomSource;
import net.i2p.I2PAppContext;
/** Define a way of generating asymetrical key pairs as well as symetrical keys
* @author jrandom

View File

@@ -21,13 +21,13 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.PublicKey;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.util.Log;
import net.i2p.I2PAppContext;
/**
* Expose the functionality to allow people to write out and read in the

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