Compare commits

...

52 Commits

Author SHA1 Message Date
daf32a24bc * 2005-01-06 0.4.2.6 released
2005-01-06  jrandom
    * Added a startup message to the addressbook, printing its version number
      to stdout (which is sent to wrapper.config) when it loads.
    * Updated the addressbook to reread the config file periodically
    * Added orion.i2p to the list of eepsites on the default homepage
2005-01-06 20:59:13 +00:00
34ecfd9857 added j.i2p 2005-01-06 11:56:35 +00:00
4838564460 2005-01-05 jrandom
* Handle unexpected network read errors more carefully (thanks parg!)
    * Added more methods to partially compare (DataHelper) and display
      arrays (Base64.encode).
    * Exposed the AES encryptBlock/decryptBlock on the context.aes()
    * Be more generous on the throttle when just starting up the router
    * Fix a missing scheduled event in the streaming lib (caused after reset)
    * Add a new DisconnectListener on the I2PSocketManager to allow
      notification of session destruction.
    * Make sure our own router identity is valid, and if it isn't, build a new
      one and restart the router.  Alternately, you can run the Router with
      the single command line argument "rebuild" and it will do the same.
2005-01-06 00:17:53 +00:00
3dd2f67ff3 added bl.i2p 2005-01-05 23:16:35 +00:00
eco
0ccec3dde0 Added log entry for bt1.eco.i2p and jap.eco.i2p removal. Fix typo in numbering of previous log entry. 2005-01-04 12:29:12 +00:00
eco
ad77879caa removed jap.eco.i2p and bt1.eco.i2p (obsolete) 2005-01-04 12:22:38 +00:00
27999983cc added chat.i2p 2005-01-03 02:36:32 +00:00
48b039940d added phonebooth.i2p 2005-01-01 05:18:01 +00:00
84dc7d9d82 2004-12-31 ragnarok
* Integrated latest addressbook changes (2.0.3) which include support for
      deploying as a .war file with no existing addressbook configuration.
    * Updated main build process to bundle the addressbook.war in the
      i2pinstall.jar and i2pupdate.zip.
2005-01-01 00:57:01 +00:00
70d6332bad 2004-12-31 jrandom
* Speling fxi (thanks digum!)
    * Bugfix for the I2PTunnel web interface so that it now properly launches
      newly added tunnels that are defined to be run on startup (thanks ugha!)
2004-12-31 17:18:05 +00:00
aec0b0c86a 2004-12-30 jrandom
* Revised the I2PTunnel client and httpclient connection establishment
      throttles.  There is now a pool of threads that build the I2PSocket
      connections with a default size of 5, configurable via the I2PTunnel
      client option 'i2ptunnel.numConnectionBuilders' (if set to 0, it will
      not throttle the number of concurrent builders, but will launch a thread
      per socket during establishment).  In addition, sockets accepted but
      not yet allocated to one of the connection builders will be destroyed
      after 30 seconds, configurable via 'i2ptunnel.maxWaitTime' (if set to
      0, it will wait indefinitely).
2004-12-30 22:51:16 +00:00
099f6a88c2 2004-12-29 jrandom
* Imported Ragnarok's addressbook source (2.0.2) which is built but not
      deployed in the i2pinstall.jar/i2pupdate.zip (yet).
    * Don't treat connection inactivity closure as a connection error.
2004-12-29 22:16:42 +00:00
00a5d42d3d forgot to import these... 2004-12-29 20:51:43 +00:00
28f4a2cb67 imported ragnarok's MIT licensed addressbook-2.0.2 2004-12-29 20:28:20 +00:00
1ac18ba10e 2004-12-29 jrandom
* Add in a new keepalive event on each TCP connection, proactively sending
      a (tiny) time message every minute or two, as well as killing the
      connection if no message has been fully sent within 5 minutes or so.
      This should help deal with hung connections from IP address changes.
2004-12-29 20:06:43 +00:00
1503ee2dfa 2004-12-28 jrandom
* Cleaned up the resending and choking algorithm in the streaming lib.
    * Removed the read timeout override for I2PTunnel's httpclient, allowing
      it to use the default for the streaming lib.
    * Revised ack triggers in the streaming lib.
    * Logging.
2004-12-29 15:53:28 +00:00
484b528d4f * 2004-12-21 0.4.2.5 released
2004-12-21  jrandom
    * Track a new stat for expired client leases (client.leaseSetExpired).
2004-12-21 18:23:03 +00:00
758293dc02 2004-12-21 jrandom
* Cleaned up the postinstall/startup scripts a bit more to handle winME,
      and added windows info to the headless docs. (thanks ardvark!)
    * Fixed a harmless (yet NPE inspiring) race during the final shutdown of
      a stream (thanks frosk!)
    * Add a pair of new stats for monitoring tunnel participation -
      tunnel.participatingBytesProcessed (total # bytes transferred) and
      tunnel.participatingBytesProcessedActive (total # bytes transferred for
      tunnels whose byte count exceed the 10m average).  This should help
      further monitor congestion issues.
    * Made the NamingService factory property public (thanks susi!)
2004-12-21 16:32:49 +00:00
6cb316b33e 2004-12-20 jrandom
* No longer do a blocking DNS lookup within the jobqueue (thanks mule!)
    * Set a 60s dns cache TTL, instead of 0s.  Most users who used to use
      dyndns/etc now just use IP autodetection, so the old "we need ttl=0"
      reasoning is gone.
2004-12-20 05:14:56 +00:00
1d31831e7d added up.i2p 2004-12-20 02:40:45 +00:00
ee32b07995 2004-12-19 jrandom
* Fix for a race on startup wrt the new stats (thanks susi!)
2004-12-19 18:55:09 +00:00
81f04ca692 2004-12-19 jrandom
* Added three new stats - router.activePeers, router.fastPeers, and
      router.highCapacityPeers, updated every minute
2004-12-19 16:27:10 +00:00
1756997608 2004-12-19 jrandom
* Added a new i2ptunnel type: 'httpserver', allowing you to specify what
      hostname should be sent to the webserver.  By default, new installs will
      have an httpserver pointing at their jetty instance with the spoofed
      name 'mysite.i2p' (editable on the /i2ptunnel/edit.jsp page).
2004-12-19 11:04:56 +00:00
ec11ea4ca7 * Convert native jcpuid code from C++ to C. This should alleviate build
problems experienced by some users.
2004-12-19 06:25:27 +00:00
a1ebf85e1b added dm.i2p 2004-12-18 06:31:22 +00:00
4b2a734cda * 2004-12-18 0.4.2.4 released 2004-12-18 04:07:13 +00:00
97ae8f78a0 added piespy.i2p 2004-12-18 03:11:03 +00:00
834665c3ba 2004-12-16 jrandom
* Catch another oddball case for a reset connection in the streaming lib.
    * Add a dumpprofile.jsp page, called with ?peer=base64OfPeerHash, which
      dumps the current state of that peer's profile.  Instead of the full
      base64, you can pass in however many characters you have and it will
      return the first match found.
2004-12-16 10:32:26 +00:00
d969dd2d8d 2004-12-16 jrandom
* Catch another oddball case for a reset connection in the streaming lib.
    * Add a dumpprofile.jsp page, called with ?peer=base64OfPeerHash, which
      dumps the current state of that peer's profile.  Instead of the full
      base64, you can pass in however many characters you have and it will
      return the first match found.
2004-12-16 10:21:23 +00:00
3cb727561c uugly stat dumper. call via /dumpstats.jsp?peer=routerIdentHash 2004-12-16 09:45:31 +00:00
cbc89376d3 2004-12-16 jrandom
* Remove the randomized factor in the tunnel rejection by bandwidth -
      we now accept the request if we've allocated less than our limit
      and reject it if we've allocated more.
    * Stick to the standard capacity scale on tunnel rejection, even for
      the 10m period.
    * Build the time message at the very last possible moment
2004-12-16 05:42:03 +00:00
66aa29e3d4 2004-12-15 jrandom
* Handle hard disconnects more gracefully within the streaming lib, and
      log unmonitored events more aggressively.
    * If we drop a peer after connection due to clock skew, log it to the
      /logs.jsp#connectionlogs with relevent info.  In addition, toss it in
      the stat 'tcp.disconnectAfterSkew'.
    * Fixed the formatting in the skew display
    * Added an ERROR message that is fired once after we run out of
      routerInfo files (thanks susi!)
    * Set the connect timeout equal to the streaming lib's disconnect timeout
      if not already specified (the I2PTunnel httpclient already enforces a
      60s connect timeout)
    * Fix for another connection startup problem in the streaming lib.
    * Fix for a stupid error in the probabalistic drop (rand <= P, not > P)
    * Adjust the capacity calculations so that tunnel failures alone in the
      last 10m will not trigger a 0 capacity rank.
2004-12-16 02:45:55 +00:00
5c72aca5ee added sciencebooks.i2p 2004-12-15 04:23:16 +00:00
8824815d6d 2004-12-14 jrandom
* Periodically send a message along all I2NP connections with the router's
      current time, allowing the receiving peer to determine that the clock
      has skewed too much, and hence, disconnect.  For backwards compatability
      reasons, this is being kludged into a DeliveryStatusMessage (ewww).  The
      next time we have a backwards compatability break, we can put in a proper
      message setup for it.
2004-12-14 16:42:35 +00:00
ad72e5cbdf 2004-12-14 jrandom
* Reenable the probabalistic drop on the TCP queues to deal with good old
      fashioned bandwidth limiting.  However, by default the probability is
      rigged to reserve 0% of the queue free - meaning we just aggressively
      fail messages in the queue if we're transferring too slowly.  That
      reservation factor can be increased with 'tcp.queueFreeFactor=0.25'
      (or whatever) and the drop code can be disabled with the parameter
      'tcp.dropProbabalistically=false'.
    * Still penalize a peer on tunnel failure, but don't immediately drop
      their capacity to 0.
    * More aggressively ACK duplicates
    * Randomize the timestamper period
    * Display the clock skew on the connection logs when a peer sends it.
    * Allow the timestamper to fix skews of up to 10 minutes
    * Logging
2004-12-14 12:14:41 +00:00
b2f183fc17 2004-12-14 jrandom
* Reenable the probabalistic drop on the TCP queues to deal with good old
      fashioned bandwidth limiting.  However, by default the probability is
      rigged to reserve 0% of the queue free - meaning we just aggressively
      fail messages in the queue if we're transferring too slowly.  That
      reservation factor can be increased with 'tcp.queueFreeFactor=0.25'
      (or whatever) and the drop code can be disabled with the parameter
      'tcp.dropProbabalistically=false'.
    * Still penalize a peer on tunnel failure, but don't immediately drop
      their capacity to 0.
    * More aggressively ACK duplicates
    * Randomize the timestamper period
    * Display the clock skew on the connection logs when a peer sends it.
    * Allow the timestamper to fix skews of up to 10 minutes
    * Logging
2004-12-14 11:54:39 +00:00
9e16bc203a 2004-12-13 jrandom
* Added some error checking on the new client send job (thanks duck!)
    * Implemented tunnel rejection based on bandwidth usage (rejecting tunnels
      proportional to the bytes allocated in existing tunnels vs the bytes
      allowed through the bandwidth limiter).
    * Enable a new configuration parameter for triggering a tunnel rebuild
      (tunnel.maxTunnelFailures), where that is the max allowed test failures
      before killing the tunnel (default 0).
    * Gather more data that we rank capacity by (now we monitor and balance the
      data from 10m/30m/60m/1d instead of just 10m/60m/1d).
    * Fix a truncation/type conversion problem on the long term capacity
      values (we were ignoring the daily stats outright)
2004-12-13 13:45:52 +00:00
83c6eac017 added forum.fr.i2p, fedo.i2p, and pastebin.i2p 2004-12-13 08:48:29 +00:00
d5b277a536 test to verify that a closed socket is propogated to the client 2004-12-13 01:18:24 +00:00
77ce6c33e3 2004-12-11 jrandom
* Fix the missing HTTP timeout, which was caused by the deferred syn used
      by default.  This, in turn, meant the I2PSocket creation doesn't fail
      on .connect, but is unable to transfer any data in any direction.  We now
      detect that condition for the I2PTunnelHTTPClient and throw up the right
      error page.
    * Logging
2004-12-11 09:26:23 +00:00
60f8d349cf 2004-12-11 jrandom
* Use a simpler and less memory intensive job for processing outbound
      client messages when the session is in mode=bestEffort.  We can
      immediately discard the data as soon as its sent the first time,
      rather than wait for an ack, since we will never internally resend.
    * Reduce some synchronization to avoid a rare deadlock
    * Replaced 'localhost' with 127.0.0.1 in the i2ptunnel config, and special
      case it within the tunnel controller.
    * Script cleanup for building jbigi/jcpuid
    * Logging
2004-12-11 07:05:12 +00:00
f539c3df70 added frosk.i2p 2004-12-11 05:08:14 +00:00
fe1cf1758c added theland.i2p 2004-12-11 04:16:10 +00:00
8c71c26487 added dox.i2p 2004-12-11 01:22:03 +00:00
2ce39d1fd4 added amiga.i2p 2004-12-10 22:37:29 +00:00
88a994b712 cleaned up paths 2004-12-10 13:12:07 +00:00
24c8cc1a0c reference the new gmp 4.1.4 2004-12-10 10:22:17 +00:00
caf684394c crypto unit test to allow cross-architecture testing of jbigi and the crypto code 2004-12-10 08:48:23 +00:00
4b74510450 added source instructions (thanks bens\!) 2004-12-10 00:34:53 +00:00
mpc
0ddcfc423e *** empty log message *** 2004-12-09 14:05:22 +00:00
b4ac56e204 added frooze.i2p 2004-12-09 08:27:45 +00:00
3b19ac3942 use the standard I2CP port (7654) not jrandom's local I2CP port (duh) 2004-12-09 00:29:29 +00:00
113 changed files with 4101 additions and 350 deletions

View File

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

17
apps/addressbook/.project Normal file
View File

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

View File

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

View File

@ -0,0 +1,46 @@
<?xml version="1.0"?>
<project name="addressbook" default="war" basedir=".">
<property name="src" value="java/src/addressbook"/>
<property name="build" value="build"/>
<property name="dist" location="dist"/>
<property name="jar" value="addressbook.jar"/>
<property name="war" value="addressbook.war"/>
<property name="servlet" value="../jetty/jettylib/javax.servlet.jar"/>
<target name="init">
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>
</target>
<target name="clean">
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
<target name="distclean" depends="clean" />
<target name="compile" depends="init">
<javac srcdir="${src}" destdir="${build}" classpath="${servlet}"/>
</target>
<target name="jar" depends="compile">
<jar basedir="${build}" destfile="${dist}/${jar}">
<manifest>
<attribute name="Main-Class" value="addressbook.Daemon"/>
</manifest>
</jar>
</target>
<target name="war" depends="compile">
<mkdir dir="${dist}/tmp"/>
<mkdir dir="${dist}/tmp/WEB-INF"/>
<mkdir dir="${dist}/tmp/WEB-INF/classes"/>
<copy todir="${dist}/tmp/WEB-INF/classes">
<fileset dir="${build}"/>
</copy>
<war basedir="${dist}/tmp" webxml="web.xml" destfile="${dist}/${war}"/>
<delete dir="${dist}/tmp"/>
</target>
</project>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

@ -229,6 +229,8 @@ public class I2PTunnel implements Logging, EventDispatcher {
runClientOptions(args, l);
} else if ("server".equals(cmdname)) {
runServer(args, l);
} else if ("httpserver".equals(cmdname)) {
runHttpServer(args, l);
} else if ("textserver".equals(cmdname)) {
runTextServer(args, l);
} else if ("client".equals(cmdname)) {
@ -281,6 +283,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
l.log("owndest yes|no");
l.log("ping <args>");
l.log("server <host> <port> <privkeyfile>");
l.log("httpserver <host> <port> <spoofedhost> <privkeyfile>");
l.log("textserver <host> <port> <privkey>");
l.log("genkeys <privkeyfile> [<pubkeyfile>]");
l.log("gentextkeys");
@ -370,6 +373,65 @@ public class I2PTunnel implements Logging, EventDispatcher {
}
}
/**
* Run the HTTP server pointing at the host and port specified using the private i2p
* destination loaded from the specified file, replacing the HTTP headers
* so that the Host: specified is the one spoofed. <p />
*
* Sets the event "serverTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error)
* Also sets the event "openServerResult" = "ok" or "error" (displaying "Ready!" on the logger after
* 'ok'). So, success = serverTaskId != -1 and openServerResult = ok.
*
* @param args {hostname, portNumber, spoofedHost, privKeyFilename}
* @param l logger to receive events and output
*/
public void runHttpServer(String args[], Logging l) {
if (args.length == 4) {
InetAddress serverHost = null;
int portNum = -1;
File privKeyFile = null;
try {
serverHost = InetAddress.getByName(args[0]);
} catch (UnknownHostException uhe) {
l.log("unknown host");
_log.error(getPrefix() + "Error resolving " + args[0], uhe);
notifyEvent("serverTaskId", new Integer(-1));
return;
}
try {
portNum = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error(getPrefix() + "Port specified is not valid: " + args[1], nfe);
notifyEvent("serverTaskId", new Integer(-1));
return;
}
String spoofedHost = args[2];
privKeyFile = new File(args[3]);
if (!privKeyFile.canRead()) {
l.log("private key file does not exist");
_log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[3]);
notifyEvent("serverTaskId", new Integer(-1));
return;
}
I2PTunnelHTTPServer serv = new I2PTunnelHTTPServer(serverHost, portNum, privKeyFile, args[3], spoofedHost, l, (EventDispatcher) this, this);
serv.setReadTimeout(readTimeout);
serv.startRunning();
addtask(serv);
notifyEvent("serverTaskId", new Integer(serv.getId()));
return;
} else {
l.log("httpserver <host> <port> <spoofedhost> <privkeyfile>");
l.log(" creates an HTTP server that sends all incoming data\n"
+ " of its destination to host:port., filtering the HTTP\n"
+ " headers so it looks like the request is to the spoofed host.");
notifyEvent("serverTaskId", new Integer(-1));
}
}
/**
* Run the server pointing at the host and port specified using the private i2p
* destination loaded from the given base64 stream. <p />

View File

@ -12,8 +12,10 @@ import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import net.i2p.I2PException;
@ -26,6 +28,7 @@ import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.Destination;
import net.i2p.util.EventDispatcher;
import net.i2p.util.I2PThread;
import net.i2p.util.SimpleTimer;
import net.i2p.util.Log;
public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runnable {
@ -58,7 +61,30 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
private String handlerName;
private Object conLock = new Object();
private int pendingConnections = 0;
/** List of Socket for those accept()ed but not yet started up */
private List _waitingSockets;
/** How many connections will we allow to be in the process of being built at once? */
private int _numConnectionBuilders;
/** How long will we allow sockets to sit in the _waitingSockets map before killing them? */
private int _maxWaitTime;
/**
* How many concurrent connections this I2PTunnel instance will allow to be
* in the process of connecting (or if less than 1, there is no limit)?
*/
public static final String PROP_NUM_CONNECTION_BUILDERS = "i2ptunnel.numConnectionBuilders";
/**
* How long will we let a socket wait after being accept()ed without getting
* pumped through a connection builder (in milliseconds). If this time is
* reached, the socket is unceremoniously closed and discarded. If the max
* wait time is less than 1, there is no limit.
*
*/
public static final String PROP_MAX_WAIT_TIME = "i2ptunnel.maxWaitTime";
private static final int DEFAULT_NUM_CONNECTION_BUILDERS = 5;
private static final int DEFAULT_MAX_WAIT_TIME = 30*1000;
//public I2PTunnelClientBase(int localPort, boolean ownDest,
// Logging l) {
@ -108,6 +134,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
}
}
configurePool(tunnel);
if (open && listenerReady) {
l.log("Ready! Port " + getLocalPort());
notifyEvent("openBaseClientResult", "ok");
@ -116,6 +144,37 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
notifyEvent("openBaseClientResult", "error");
}
}
/**
* build and configure the pool handling accept()ed but not yet
* established connections
*
*/
private void configurePool(I2PTunnel tunnel) {
_waitingSockets = new ArrayList(4);
Properties opts = tunnel.getClientOptions();
String maxWait = opts.getProperty(PROP_MAX_WAIT_TIME, DEFAULT_MAX_WAIT_TIME+"");
try {
_maxWaitTime = Integer.parseInt(maxWait);
} catch (NumberFormatException nfe) {
_maxWaitTime = DEFAULT_MAX_WAIT_TIME;
}
String numBuild = opts.getProperty(PROP_NUM_CONNECTION_BUILDERS, DEFAULT_NUM_CONNECTION_BUILDERS+"");
try {
_numConnectionBuilders = Integer.parseInt(numBuild);
} catch (NumberFormatException nfe) {
_numConnectionBuilders = DEFAULT_NUM_CONNECTION_BUILDERS;
}
for (int i = 0; i < _numConnectionBuilders; i++) {
String name = "ClientBuilder" + _clientId + '.' + i;
I2PThread b = new I2PThread(new TunnelConnectionBuilder(), name);
b.setDaemon(true);
b.start();
}
}
private static I2PSocketManager socketManager;
@ -250,6 +309,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
synchronized (this) {
notifyAll();
}
synchronized (_waitingSockets) { _waitingSockets.notifyAll(); }
return;
}
ss = new ServerSocket(localPort, 0, addr);
@ -292,6 +352,9 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
notifyAll();
}
}
synchronized (_waitingSockets) {
_waitingSockets.notifyAll();
}
}
/**
@ -300,16 +363,52 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
* @param s Socket to take care of
*/
protected void manageConnection(Socket s) {
boolean useBlocking = false;
synchronized (conLock) {
pendingConnections++;
if (pendingConnections > 5)
useBlocking = true;
if (s == null) return;
if (_numConnectionBuilders <= 0) {
new I2PThread(new BlockingRunner(s), "Clinet run").start();
return;
}
if (_maxWaitTime > 0)
SimpleTimer.getInstance().addEvent(new CloseEvent(s), _maxWaitTime);
synchronized (_waitingSockets) {
_waitingSockets.add(s);
_waitingSockets.notifyAll();
}
}
/**
* Blocking runner, used during the connection establishment whenever we
* are not using the queued builders.
*
*/
private class BlockingRunner implements Runnable {
private Socket _s;
public BlockingRunner(Socket s) { _s = s; }
public void run() {
clientConnectionRun(_s);
}
}
/**
* Remove and close the socket from the waiting list, if it is still there.
*
*/
private class CloseEvent implements SimpleTimer.TimedEvent {
private Socket _s;
public CloseEvent(Socket s) { _s = s; }
public void timeReached() {
boolean stillWaiting = false;
synchronized (_waitingSockets) {
stillWaiting = _waitingSockets.remove(_s);
}
if (stillWaiting) {
try { _s.close(); } catch (IOException ioe) {}
if (_log.shouldLog(Log.INFO))
_log.info("Closed a waiting socket because of backlog");
}
}
if (useBlocking)
clientConnectionRun(s);
else
new ClientConnectionRunner(s, handlerName);
}
public boolean close(boolean forced) {
@ -341,8 +440,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
}
l.log("Client closed.");
open = false;
return true;
}
synchronized (_waitingSockets) { _waitingSockets.notifyAll(); }
return true;
}
public static void closeSocket(Socket s) {
@ -352,22 +453,29 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
_log.error("Could not close socket", ex);
}
}
private static volatile long __runnerId = 0;
public class ClientConnectionRunner extends I2PThread {
private Socket s;
public ClientConnectionRunner(Socket s, String name) {
this.s = s;
setName(name + '.' + (++__runnerId));
start();
}
public void run() {
clientConnectionRun(s);
synchronized (conLock) {
pendingConnections--;
/**
* Pool runner pulling sockets off the waiting list and pushing them
* through clientConnectionRun. This dies when the I2PTunnel instance
* is closed.
*
*/
private class TunnelConnectionBuilder implements Runnable {
public void run() {
Socket s = null;
while (open) {
try {
synchronized (_waitingSockets) {
if (_waitingSockets.size() <= 0)
_waitingSockets.wait();
else
s = (Socket)_waitingSockets.remove(0);
}
} catch (InterruptedException ie) {}
if (s != null)
clientConnectionRun(s);
s = null;
}
}
}

View File

@ -147,7 +147,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
private static final int DEFAULT_READ_TIMEOUT = 60*1000;
/**
* create the default options (using the default timeout, etc)
*
@ -156,8 +155,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
Properties defaultOpts = getTunnel().getClientOptions();
if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, ""+DEFAULT_READ_TIMEOUT);
if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
defaultOpts.setProperty("i2p.streaming.inactivityTimeout", ""+DEFAULT_READ_TIMEOUT);
//if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
// defaultOpts.setProperty("i2p.streaming.inactivityTimeout", ""+DEFAULT_READ_TIMEOUT);
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
@ -407,7 +406,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
opts.setProperty("i2p.streaming.inactivityTimeoutAction", ""+1);
I2PSocket i2ps = createI2PSocket(dest, getDefaultOptions(opts));
byte[] data = newRequest.toString().getBytes("ISO-8859-1");
I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data, mySockets);
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
} catch (SocketException ex) {
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
l.log(ex.getMessage());
@ -437,6 +437,30 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
}
}
private class OnTimeout implements Runnable {
private Socket _socket;
private OutputStream _out;
private String _target;
private boolean _usingProxy;
private String _wwwProxy;
private long _requestId;
public OnTimeout(Socket s, OutputStream out, String target, boolean usingProxy, String wwwProxy, long id) {
_socket = s;
_out = out;
_target = target;
_usingProxy = usingProxy;
_wwwProxy = wwwProxy;
_requestId = id;
}
public void run() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Timeout occured requesting " + _target);
handleHTTPClientException(new RuntimeException("Timeout"), _out,
_target, _usingProxy, _wwwProxy, _requestId);
closeSocket(_socket);
}
}
private static void writeErrorMessage(byte[] errMessage, OutputStream out, String targetRequest,
boolean usingWWWProxy, String wwwProxy) throws IOException {
if (out != null) {

View File

@ -0,0 +1,162 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.Iterator;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.data.DataHelper;
import net.i2p.util.EventDispatcher;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Simple extension to the I2PTunnelServer that filters the HTTP
* headers sent from the client to the server, replacing the Host
* header with whatever this instance has been configured with.
*
*/
public class I2PTunnelHTTPServer extends I2PTunnelServer {
private final static Log _log = new Log(I2PTunnelHTTPServer.class);
/** what Host: should we seem to be to the webserver? */
private String _spoofHost;
public I2PTunnelHTTPServer(InetAddress host, int port, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host, port, privData, l, notifyThis, tunnel);
_spoofHost = spoofHost;
}
public I2PTunnelHTTPServer(InetAddress host, int port, File privkey, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host, port, privkey, privkeyname, l, notifyThis, tunnel);
_spoofHost = spoofHost;
}
public I2PTunnelHTTPServer(InetAddress host, int port, InputStream privData, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host, port, privData, privkeyname, l, notifyThis, tunnel);
_spoofHost = spoofHost;
}
public void run() {
try {
I2PServerSocket i2pss = sockMgr.getServerSocket();
while (true) {
I2PSocket i2ps = i2pss.accept();
if (i2ps == null) throw new I2PException("I2PServerSocket closed");
I2PThread t = new I2PThread(new Handler(i2ps));
t.start();
}
} catch (I2PException ex) {
_log.error("Error while waiting for I2PConnections", ex);
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
}
/**
* Async handler to keep .accept() from blocking too long.
* todo: replace with a thread pool so we dont get overrun by threads if/when
* receiving a lot of connection requests concurrently.
*
*/
private class Handler implements Runnable {
private I2PSocket _handleSocket;
public Handler(I2PSocket socket) {
_handleSocket = socket;
}
public void run() {
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
long afterSocket = -1;
//local is fast, so synchronously. Does not need that many
//threads.
try {
_handleSocket.setReadTimeout(readTimeout);
String modifiedHeader = getModifiedHeader();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Modified header: [" + modifiedHeader + "]");
Socket s = new Socket(remoteHost, remotePort);
afterSocket = I2PAppContext.getGlobalContext().clock().now();
new I2PTunnelRunner(s, _handleSocket, slock, null, modifiedHeader.getBytes(), null);
} catch (SocketException ex) {
try {
_handleSocket.close();
} catch (IOException ioe) {
_log.error("Error while closing the received i2p con", ex);
}
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
long afterHandle = I2PAppContext.getGlobalContext().clock().now();
long timeToHandle = afterHandle - afterAccept;
if (timeToHandle > 1000)
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: "
+ (afterSocket-afterAccept) + "]");
}
private String getModifiedHeader() throws IOException {
InputStream in = _handleSocket.getInputStream();
StringBuffer command = new StringBuffer(128);
Properties headers = readHeaders(in, command);
headers.setProperty("Host", _spoofHost);
headers.setProperty("Connection", "close");
return formatHeaders(headers, command);
}
}
private String formatHeaders(Properties headers, StringBuffer command) {
StringBuffer buf = new StringBuffer(command.length() + headers.size() * 64);
buf.append(command.toString()).append('\n');
for (Iterator iter = headers.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
String val = headers.getProperty(name);
buf.append(name).append(": ").append(val).append('\n');
}
buf.append('\n');
return buf.toString();
}
private Properties readHeaders(InputStream in, StringBuffer command) throws IOException {
Properties headers = new Properties();
StringBuffer buf = new StringBuffer(128);
boolean ok = DataHelper.readLine(in, command);
if (!ok) throw new IOException("EOF reached while reading the HTTP command [" + command.toString() + "]");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read the http command [" + command.toString() + "]");
while (true) {
buf.setLength(0);
ok = DataHelper.readLine(in, buf);
if (!ok) throw new IOException("EOF reached before the end of the headers [" + buf.toString() + "]");
if ( (buf.length() <= 1) && ( (buf.charAt(0) == '\n') || (buf.charAt(0) == '\r') ) ) {
// end of headers reached
return headers;
} else {
int split = buf.indexOf(": ");
if (split <= 0) throw new IOException("Invalid HTTP header, missing colon [" + buf.toString() + "]");
String name = buf.substring(0, split);
String value = buf.substring(split+2); // ": "
headers.setProperty(name, value);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read the header [" + name + "] = [" + value + "]");
}
}
}
}

View File

@ -40,21 +40,37 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
Object slock, finishLock = new Object();
boolean finished = false;
HashMap ostreams, sockets;
byte[] initialData;
byte[] initialI2PData;
byte[] initialSocketData;
/** when the last data was sent/received (or -1 if never) */
private long lastActivityOn;
/** when the runner started up */
private long startedOn;
private List sockList;
/** if we die before receiving any data, run this job */
private Runnable onTimeout;
private long totalSent;
private long totalReceived;
private volatile long __forwarderId;
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialData, List sockList) {
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, List sockList) {
this(s, i2ps, slock, initialI2PData, null, sockList, null);
}
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, byte[] initialSocketData, List sockList) {
this(s, i2ps, slock, initialI2PData, initialSocketData, sockList, null);
}
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, List sockList, Runnable onTimeout) {
this(s, i2ps, slock, initialI2PData, null, sockList, onTimeout);
}
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, byte[] initialSocketData, List sockList, Runnable onTimeout) {
this.sockList = sockList;
this.s = s;
this.i2ps = i2ps;
this.slock = slock;
this.initialData = initialData;
this.initialI2PData = initialI2PData;
this.initialSocketData = initialSocketData;
this.onTimeout = onTimeout;
lastActivityOn = -1;
startedOn = Clock.getInstance().now();
if (_log.shouldLog(Log.INFO))
@ -97,24 +113,27 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
}
public void run() {
boolean closedCleanly = false;
try {
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream(); // = new BufferedOutputStream(s.getOutputStream(), NETWORK_BUFFER_SIZE);
i2ps.setSocketErrorListener(this);
InputStream i2pin = i2ps.getInputStream();
OutputStream i2pout = i2ps.getOutputStream(); //new BufferedOutputStream(i2ps.getOutputStream(), MAX_PACKET_SIZE);
if (initialData != null) {
if (initialI2PData != null) {
synchronized (slock) {
i2pout.write(initialData);
i2pout.write(initialI2PData);
//i2pout.flush();
}
}
if (initialSocketData != null) {
out.write(initialSocketData);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Initial data " + (initialData != null ? initialData.length : 0)
+ " written, starting forwarders");
Thread t1 = new StreamForwarder(in, i2pout, "toI2P");
Thread t2 = new StreamForwarder(i2pin, out, "fromI2P");
_log.debug("Initial data " + (initialI2PData != null ? initialI2PData.length : 0)
+ " written to I2P, " + (initialSocketData != null ? initialSocketData.length : 0)
+ " written to the socket, starting forwarders");
Thread t1 = new StreamForwarder(in, i2pout, true);
Thread t2 = new StreamForwarder(i2pin, out, false);
synchronized (finishLock) {
while (!finished) {
finishLock.wait();
@ -122,12 +141,21 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("At least one forwarder completed, closing and joining");
// this task is useful for the httpclient
if (onTimeout != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("runner has a timeout job, totalReceived = " + totalReceived
+ " totalSent = " + totalSent + " job = " + onTimeout);
if ( (totalSent <= 0) && (totalReceived <= 0) )
onTimeout.run();
}
// now one connection is dead - kill the other as well.
s.close();
i2ps.close();
t1.join(30*1000);
t2.join(30*1000);
closedCleanly = true;
} catch (InterruptedException ex) {
if (_log.shouldLog(Log.ERROR))
_log.error("Interrupted", ex);
@ -140,21 +168,20 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
} finally {
removeRef();
try {
if ( (s != null) && (!closedCleanly) )
if (s != null)
s.close();
} catch (IOException ex) {
if (_log.shouldLog(Log.ERROR))
_log.error("Could not close java socket", ex);
}
try {
if (i2ps != null) {
if (!closedCleanly)
i2ps.close();
i2ps.setSocketErrorListener(null);
if (i2ps != null) {
try {
i2ps.close();
} catch (IOException ex) {
if (_log.shouldLog(Log.ERROR))
_log.error("Could not close I2PSocket", ex);
}
} catch (IOException ex) {
if (_log.shouldLog(Log.ERROR))
_log.error("Could not close I2PSocket", ex);
i2ps.setSocketErrorListener(null);
}
}
}
@ -181,12 +208,14 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
InputStream in;
OutputStream out;
String direction;
private boolean _toI2P;
private ByteCache _cache;
private StreamForwarder(InputStream in, OutputStream out, String dir) {
private StreamForwarder(InputStream in, OutputStream out, boolean toI2P) {
this.in = in;
this.out = out;
direction = dir;
_toI2P = toI2P;
direction = (toI2P ? "toI2P" : "fromI2P");
_cache = ByteCache.getInstance(256, NETWORK_BUFFER_SIZE);
setName("StreamForwarder " + _runnerId + "." + (++__forwarderId));
start();
@ -207,6 +236,10 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
if (_toI2P)
totalSent += len;
else
totalReceived += len;
if (len > 0) updateActivity();
@ -259,10 +292,10 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
_log.warn(direction + ": Error closing input stream", ex);
}
try {
out.close();
} catch (IOException ex) {
out.flush();
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn(direction + ": Error closing output stream", ex);
_log.warn(direction + ": Error flushing to close", ioe);
}
synchronized (finishLock) {
finished = true;

View File

@ -31,19 +31,20 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
private final static Log _log = new Log(I2PTunnelServer.class);
private I2PSocketManager sockMgr;
private I2PServerSocket i2pss;
protected I2PSocketManager sockMgr;
protected I2PServerSocket i2pss;
private Object lock = new Object(), slock = new Object();
private Object lock = new Object();
protected Object slock = new Object();
private InetAddress remoteHost;
private int remotePort;
protected InetAddress remoteHost;
protected int remotePort;
private Logging l;
private static final long DEFAULT_READ_TIMEOUT = -1; // 3*60*1000;
/** default timeout to 3 minutes - override if desired */
private long readTimeout = DEFAULT_READ_TIMEOUT;
protected long readTimeout = DEFAULT_READ_TIMEOUT;
public I2PTunnelServer(InetAddress host, int port, String privData, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host + ":" + port + " <- " + privData, notifyThis, tunnel);

View File

@ -16,6 +16,7 @@ import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.I2PSession;
import net.i2p.data.Destination;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
@ -57,7 +58,7 @@ public class TunnelController implements Logging {
setConfig(config, prefix);
_messages = new ArrayList(4);
_running = false;
if (createKey && ("server".equals(getType())) )
if (createKey && ("server".equals(getType()) || "httpserver".equals(getType())) )
createPrivateKey();
_starting = getStartOnLoad();
}
@ -99,6 +100,11 @@ public class TunnelController implements Logging {
}
}
public void startTunnelBackground() {
if (_running) return;
new I2PThread(new Runnable() { public void run() { startTunnel(); } }).start();
}
/**
* Start up the tunnel (if it isn't already running)
*
@ -132,6 +138,8 @@ public class TunnelController implements Logging {
startClient();
} else if ("server".equals(type)) {
startServer();
} else if ("httpserver".equals(type)) {
startHttpServer();
} else {
if (_log.shouldLog(Log.ERROR))
_log.error("Cannot start tunnel - unknown type [" + type + "]");
@ -206,6 +214,18 @@ public class TunnelController implements Logging {
_running = true;
}
private void startHttpServer() {
setI2CPOptions();
setSessionOptions();
String targetHost = getTargetHost();
String targetPort = getTargetPort();
String spoofedHost = getSpoofedHost();
String privKeyFile = getPrivKeyFile();
_tunnel.runHttpServer(new String[] { targetHost, targetPort, spoofedHost, privKeyFile }, this);
acquire();
_running = true;
}
private void setListenOn() {
String listenOn = getListenOnInterface();
if ( (listenOn != null) && (listenOn.length() > 0) ) {
@ -233,6 +253,9 @@ public class TunnelController implements Logging {
String host = getI2CPHost();
if ( (host != null) && (host.length() > 0) )
_tunnel.host = host;
// woohah, special casing for people with ipv6/etc
if ("localhost".equals(_tunnel.host))
_tunnel.host = "127.0.0.1";
String port = getI2CPPort();
if ( (port != null) && (port.length() > 0) )
_tunnel.port = port;
@ -294,6 +317,7 @@ public class TunnelController implements Logging {
public String getListenOnInterface() { return _config.getProperty("interface"); }
public String getTargetHost() { return _config.getProperty("targetHost"); }
public String getTargetPort() { return _config.getProperty("targetPort"); }
public String getSpoofedHost() { return _config.getProperty("spoofedHost"); }
public String getPrivKeyFile() { return _config.getProperty("privKeyFile"); }
public String getListenPort() { return _config.getProperty("listenPort"); }
public String getTargetDestination() { return _config.getProperty("targetDestination"); }
@ -311,6 +335,8 @@ public class TunnelController implements Logging {
getClientSummary(buf);
else if ("server".equals(type))
getServerSummary(buf);
else if ("httpserver".equals(type))
getHttpServerSummary(buf);
else
buf.append("Unknown type ").append(type);
}
@ -364,6 +390,18 @@ public class TunnelController implements Logging {
getOptionSummary(buf);
}
private void getHttpServerSummary(StringBuffer buf) {
String description = getDescription();
if ( (description != null) && (description.trim().length() > 0) )
buf.append("<i>").append(description).append("</i><br />\n");
buf.append("Server tunnel pointing at port ").append(getTargetPort());
buf.append(" on ").append(getTargetHost());
buf.append(" for the site ").append(getSpoofedHost());
buf.append("<br />\n");
buf.append("Private destination loaded from ").append(getPrivKeyFile()).append("<br />\n");
getOptionSummary(buf);
}
private void getOptionSummary(StringBuffer buf) {
String opts = getClientOptions();
if ( (opts != null) && (opts.length() > 0) )
@ -375,7 +413,7 @@ public class TunnelController implements Logging {
Destination dest = session.getMyDestination();
if (dest != null) {
buf.append("Destination hash: ").append(dest.calculateHash().toBase64()).append("<br />\n");
if ("server".equals(getType())) {
if ( ("server".equals(getType())) || ("httpserver".equals(getType())) ) {
buf.append("Full destination: ");
buf.append("<input type=\"text\" size=\"10\" onclick=\"this.select();\" ");
buf.append("value=\"").append(dest.toBase64()).append("\" />\n");

View File

@ -18,6 +18,7 @@ class WebEditPageFormGenerator {
"<option value=\"httpclient\">HTTP proxy</option>" +
"<option value=\"client\">Client tunnel</option>" +
"<option value=\"server\">Server tunnel</option>" +
"<option value=\"httpserver\">HTTP server tunnel</option>" +
"</select> <input type=\"submit\" value=\"GO\" />" +
"</form>\n";
@ -42,6 +43,8 @@ class WebEditPageFormGenerator {
return getEditClientForm(controller, id);
else if ("server".equals(type))
return getEditServerForm(controller, id);
else if ("httpserver".equals(type))
return getEditHttpServerForm(controller, id);
else
return "WTF, unknown type [" + type + "]";
}
@ -103,7 +106,7 @@ class WebEditPageFormGenerator {
if ( (controller != null) && (controller.getTargetHost() != null) )
buf.append("value=\"").append(controller.getTargetHost()).append("\" ");
else
buf.append("value=\"localhost\" ");
buf.append("value=\"127.0.0.1\" ");
buf.append(" /><br />\n");
buf.append("<b>Target port:</b> <input type=\"text\" size=\"4\" name=\"targetPort\" ");
@ -129,6 +132,48 @@ class WebEditPageFormGenerator {
return buf.toString();
}
private static String getEditHttpServerForm(TunnelController controller, String id) {
StringBuffer buf = new StringBuffer(1024);
addGeneral(buf, controller, id);
buf.append("<b>Type:</b> <i>HTTP server tunnel</i><input type=\"hidden\" name=\"type\" value=\"httpserver\" /><br />\n");
buf.append("<b>Target host:</b> <input type=\"text\" size=\"40\" name=\"targetHost\" ");
if ( (controller != null) && (controller.getTargetHost() != null) )
buf.append("value=\"").append(controller.getTargetHost()).append("\" ");
else
buf.append("value=\"127.0.0.1\" ");
buf.append(" /><br />\n");
buf.append("<b>Target port:</b> <input type=\"text\" size=\"4\" name=\"targetPort\" ");
if ( (controller != null) && (controller.getTargetPort() != null) )
buf.append("value=\"").append(controller.getTargetPort()).append("\" ");
else
buf.append("value=\"80\" ");
buf.append(" /><br />\n");
buf.append("<b>Website hostname:</b> <input type=\"text\" size=\"16\" name=\"spoofedHost\" ");
if ( (controller != null) && (controller.getSpoofedHost() != null) )
buf.append("value=\"").append(controller.getSpoofedHost()).append("\" ");
else
buf.append("value=\"mysite.i2p\" ");
buf.append(" /><br />\n");
buf.append("<b>Private key file:</b> <input type=\"text\" name=\"privKeyFile\" value=\"");
if ( (controller != null) && (controller.getPrivKeyFile() != null) ) {
buf.append(controller.getPrivKeyFile()).append("\" /><br />");
} else {
buf.append("myServer.privKey\" /><br />");
buf.append("<input type=\"hidden\" name=\"privKeyGenerate\" value=\"true\" />");
}
addOptions(buf, controller);
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
buf.append(" <i>confirm removal:</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
buf.append("</form>\n");
return buf.toString();
}
/**
* Start off the form and add some common fields (name, num, description)
*
@ -285,7 +330,7 @@ class WebEditPageFormGenerator {
if ( (controller != null) && (controller.getI2CPHost() != null) )
buf.append(controller.getI2CPHost());
else
buf.append("localhost");
buf.append("127.0.0.1");
buf.append("\" /><br />\n");
buf.append("<b>I2CP port:</b> ");
buf.append("<input type=\"text\" name=\"clientPort\" size=\"20\" value=\"");

View File

@ -38,6 +38,7 @@ public class WebEditPageHelper {
private String _targetDestination;
private String _targetHost;
private String _targetPort;
private String _spoofedHost;
private String _privKeyFile;
private boolean _startOnLoad;
private boolean _privKeyGenerate;
@ -139,6 +140,10 @@ public class WebEditPageHelper {
public void setTargetPort(String port) {
_targetPort = (port != null ? port.trim() : null);
}
/** What host does this http server tunnel spoof */
public void setSpoofedHost(String host) {
_spoofedHost = (host != null ? host.trim() : null);
}
/** What filename is this server tunnel's private keys stored in */
public void setPrivKeyFile(String file) {
_privKeyFile = (file != null ? file.trim() : null);
@ -252,6 +257,8 @@ public class WebEditPageHelper {
// creating new
cur = new TunnelController(config, "", _privKeyGenerate);
TunnelControllerGroup.getInstance().addController(cur);
if (cur.getStartOnLoad())
cur.startTunnelBackground();
} else {
cur.setConfig(config, "");
}
@ -320,6 +327,15 @@ public class WebEditPageHelper {
config.setProperty("targetPort", _targetPort);
if (_privKeyFile != null)
config.setProperty("privKeyFile", _privKeyFile);
} else if ("httpserver".equals(_type)) {
if (_targetHost != null)
config.setProperty("targetHost", _targetHost);
if (_targetPort != null)
config.setProperty("targetPort", _targetPort);
if (_privKeyFile != null)
config.setProperty("privKeyFile", _privKeyFile);
if (_spoofedHost != null)
config.setProperty("spoofedHost", _spoofedHost);
} else {
return null;
}

View File

@ -25,6 +25,7 @@
<option value="httpclient">HTTP proxy</option>
<option value="client">Client tunnel</option>
<option value="server">Server tunnel</option>
<option value="httpserver">HTTP server tunnel</option>
</select> <input type="submit" value="GO" />
</form>

View File

@ -105,4 +105,11 @@ public interface I2PSocketManager {
public void setName(String name);
public void init(I2PAppContext context, I2PSession session, Properties opts, String name);
public void addDisconnectListener(DisconnectListener lsnr);
public void removeDisconnectListener(DisconnectListener lsnr);
public static interface DisconnectListener {
public void sessionDisconnected();
}
}

View File

@ -102,7 +102,8 @@ public class I2PSocketManagerFactory {
if (i2cpHost != null)
opts.setProperty(I2PClient.PROP_TCP_HOST, i2cpHost);
opts.setProperty(I2PClient.PROP_TCP_PORT, "" + i2cpPort);
if (i2cpPort > 0)
opts.setProperty(I2PClient.PROP_TCP_PORT, "" + i2cpPort);
try {
I2PSession session = client.createSession(myPrivateKeyStream, opts);

View File

@ -10,9 +10,11 @@ import java.io.InterruptedIOException;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
@ -46,6 +48,7 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener {
private I2PSocketOptions _defaultOptions;
private long _acceptTimeout;
private String _name;
private List _listeners;
private static int __managerId = 0;
public static final short ACK = 0x51;
@ -76,6 +79,7 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener {
_inSockets = new HashMap(16);
_outSockets = new HashMap(16);
_acceptTimeout = ACCEPT_TIMEOUT_DEFAULT;
_listeners = new ArrayList(1);
setSession(session);
setDefaultOptions(buildOptions(opts));
_context.statManager().createRateStat("streaming.lifetime", "How long before the socket is closed?", "streaming", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
@ -109,6 +113,15 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener {
public void disconnected(I2PSession session) {
_log.info(getName() + ": Disconnected from the session");
destroySocketManager();
List listeners = null;
synchronized (_listeners) {
listeners = new ArrayList(_listeners);
_listeners.clear();
}
for (int i = 0; i < listeners.size(); i++) {
DisconnectListener lsnr = (DisconnectListener)listeners.get(i);
lsnr.sessionDisconnected();
}
}
public void errorOccurred(I2PSession session, String message, Throwable error) {
@ -707,6 +720,17 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener {
public String getName() { return _name; }
public void setName(String name) { _name = name; }
public void addDisconnectListener(DisconnectListener lsnr) {
synchronized (_listeners) {
_listeners.add(lsnr);
}
}
public void removeDisconnectListener(DisconnectListener lsnr) {
synchronized (_listeners) {
_listeners.remove(lsnr);
}
}
public static String getReadableForm(String id) {
if (id == null) return "(null)";
if (id.length() != 3) return "Bogus";

View File

@ -72,7 +72,7 @@ public class TestSwarm {
}
}
try {
_manager = I2PSocketManagerFactory.createManager(new FileInputStream(_destFile), "localhost", 10001, null);
_manager = I2PSocketManagerFactory.createManager(new FileInputStream(_destFile), null, -1, null);
} catch (Exception e) {
_log.error("Error creatign the manager", e);
return;

View File

@ -0,0 +1,42 @@
package net.i2p.router.web;
import java.util.Iterator;
import java.util.Set;
import java.io.ByteArrayOutputStream;
import java.io.Writer;
import net.i2p.data.Hash;
import net.i2p.router.RouterContext;
/**
* uuuugly. dump the peer profile data if given a peer.
*
*/
public class StatHelper {
private String _peer;
private Writer _writer;
public void setPeer(String peer) { _peer = peer; }
public void setWriter(Writer writer) { _writer = writer; }
public String getProfile() {
RouterContext ctx = (RouterContext)net.i2p.router.RouterContext.listContexts().get(0);
Set peers = ctx.profileOrganizer().selectAllPeers();
for (Iterator iter = peers.iterator(); iter.hasNext(); ) {
Hash peer = (Hash)iter.next();
if (peer.toBase64().startsWith(_peer)) {
try {
WriterOutputStream wos = new WriterOutputStream(_writer);
ctx.profileOrganizer().exportProfile(peer, wos);
wos.flush();
_writer.flush();
return "";
} catch (Exception e) {
e.printStackTrace();
}
}
}
return "Unknown";
}
}

View File

@ -0,0 +1,17 @@
package net.i2p.router.web;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
/**
* Treat a writer as an output stream. Quick 'n dirty, none
* of that "intarnasheeonaleyzayshun" stuff. So we can treat
* the jsp's PrintWriter as an OutputStream
*/
public class WriterOutputStream extends OutputStream {
private Writer _writer;
public WriterOutputStream(Writer writer) { _writer = writer; }
public void write(int b) throws IOException { _writer.write(b); }
}

View File

@ -0,0 +1,5 @@
<%@page contentType="text/plain"
%><jsp:useBean id="helper" class="net.i2p.router.web.StatHelper"
/><jsp:setProperty name="helper" property="peer" value="<%=request.getParameter("peer")%>"
/><jsp:setProperty name="helper" property="writer" value="<%=out%>"
/><jsp:getProperty name="helper" property="profile" />

View File

@ -21,9 +21,9 @@ SRCDIR = src
# Programs
#
AR = C:\Dev-Cpp\bin\ar
CC = C:\Dev-Cpp\bin\gcc
RM = C:\Dev-Cpp\bin\rm
AR = C:\MinGW\bin\ar
CC = C:\MinGW\bin\gcc
RM = C:\MinGW\bin\rm
#
# Flags

View File

@ -6,8 +6,8 @@
# Programs
#
CC = C:\Dev-Cpp\bin\gcc
RM = C:\Dev-Cpp\bin\rm
CC = C:\MinGW\bin\gcc
RM = C:\MinGW\bin\rm
#
# Flags

View File

@ -87,6 +87,7 @@ typedef struct {
int sock; /* the socket used for communications with SAM */
bool connected; /* whether the socket is connected */
sam_sid_t prev_id; /* the last stream id number we used */
void *child; /* whatever you want it to be */
} sam_sess_t; /* a SAM session */
typedef enum { /* see sam_strerror() for detailed descriptions of these */

View File

@ -937,6 +937,7 @@ sam_sess_t *sam_session_init(sam_sess_t *session)
SAMLOGS("Out of memory");
abort();
}
session->child = NULL;
}
session->connected = false;
session->prev_id = 0;

View File

@ -547,7 +547,8 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
try {
if (!streamSession.sendBytes(id, getClientSocketInputStream(), size)) { // data)) {
_log.error("STREAM SEND [" + size + "] failed");
if (_log.shouldLog(Log.WARN))
_log.warn("STREAM SEND [" + size + "] failed");
boolean rv = writeString("STREAM CLOSED RESULT=CANT_REACH_PEER ID=" + id + " MESSAGE=\"Send of " + size + " bytes failed\"\n");
streamSession.closeConnection(id);
return rv;
@ -698,7 +699,8 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
try {
closeClientSocket();
} catch (IOException e) {
_log.error("Error closing socket: " + e.getMessage());
if (_log.shouldLog(Log.WARN))
_log.warn("Error closing socket", e);
}
}
@ -733,7 +735,8 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
try {
closeClientSocket();
} catch (IOException e) {
_log.error("Error closing socket: " + e.getMessage());
if (_log.shouldLog(Log.WARN))
_log.warn("Error closing socket", e);
}
}
@ -802,7 +805,8 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
try {
closeClientSocket();
} catch (IOException e) {
_log.error("Error closing socket: " + e.getMessage());
if (_log.shouldLog(Log.WARN))
_log.warn("Error closing socket", e);
}
}
}

View File

@ -32,7 +32,10 @@ public class Connection {
private long _lastSendTime;
private long _lastSendId;
private boolean _resetReceived;
private boolean _resetSent;
private long _resetSentOn;
private boolean _connected;
private boolean _hardDisconnected;
private MessageInputStream _inputStream;
private MessageOutputStream _outputStream;
private SchedulerChooser _chooser;
@ -70,7 +73,7 @@ public class Connection {
private long _lifetimeDupMessageReceived;
public static final long MAX_RESEND_DELAY = 60*1000;
public static final long MIN_RESEND_DELAY = 40*1000;
public static final long MIN_RESEND_DELAY = 30*1000;
/** wait up to 5 minutes after disconnection so we can ack/close packets */
public static int DISCONNECT_TIMEOUT = 5*60*1000;
@ -111,6 +114,7 @@ public class Connection {
_ackSinceCongestion = true;
_connectLock = new Object();
_activeResends = 0;
_resetSentOn = -1;
_context.statManager().createRateStat("stream.con.windowSizeAtCongestion", "How large was our send window when we send a dup?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("stream.chokeSizeBegin", "How many messages were outstanding when we started to choke?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("stream.chokeSizeEnd", "How many messages were outstanding when we stopped being choked?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
@ -144,20 +148,25 @@ public class Connection {
synchronized (_outboundPackets) {
if (!started)
_context.statManager().addRateData("stream.chokeSizeBegin", _outboundPackets.size(), timeoutMs);
if (!_connected)
return false;
started = true;
if (_outboundPackets.size() >= _options.getWindowSize()) {
if ( (_outboundPackets.size() >= _options.getWindowSize()) || (_activeResends > 0) ) {
if (writeExpire > 0) {
if (timeLeft <= 0) {
_log.error("Outbound window is full of " + _outboundPackets.size()
+ " with " + _activeResends + " active resends"
+ " and we've waited too long (" + writeExpire + "ms)");
return false;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Outbound window is full (" + _outboundPackets.size() + "/" + _options.getWindowSize() + "), waiting " + timeLeft);
_log.debug("Outbound window is full (" + _outboundPackets.size() + "/" + _options.getWindowSize() + "/"
+ _activeResends + "), waiting " + timeLeft);
try { _outboundPackets.wait(timeLeft); } catch (InterruptedException ie) {}
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Outbound window is full (" + _outboundPackets.size() + "), waiting indefinitely");
_log.debug("Outbound window is full (" + _outboundPackets.size() + "/" + _activeResends
+ "), waiting indefinitely");
try { _outboundPackets.wait(); } catch (InterruptedException ie) {}
}
} else {
@ -171,6 +180,25 @@ public class Connection {
void ackImmediately() {
_receiver.send(null, 0, 0);
}
/**
* got a packet we shouldn't have, send 'em a reset
*
*/
void sendReset() {
_resetSent = true;
if (_resetSentOn <= 0)
_resetSentOn = _context.clock().now();
if ( (_remotePeer == null) || (_sendStreamId == null) ) return;
PacketLocal reply = new PacketLocal(_context, _remotePeer);
reply.setFlag(Packet.FLAG_RESET);
reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
reply.setSendStreamId(_sendStreamId);
reply.setReceiveStreamId(_receiveStreamId);
reply.setOptionalFrom(_connectionManager.getSession().getMyDestination());
// this just sends the packet - no retries or whatnot
_outboundQueue.enqueue(reply);
}
/**
* Flush any data that we can
@ -223,7 +251,7 @@ public class Connection {
if (timeout > MAX_RESEND_DELAY)
timeout = MAX_RESEND_DELAY;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Resend in " + timeout + " for " + packet);
_log.debug("Resend in " + timeout + " for " + packet, new Exception("Sent by"));
SimpleTimer.getInstance().addEvent(new ResendPacketEvent(packet), timeout);
}
@ -304,8 +332,8 @@ public class Connection {
_ackedPackets++;
if (p.getNumSends() > 1) {
_activeResends--;
if (_log.shouldLog(Log.WARN))
_log.warn("Active resend of " + p + " successful, # active left: " + _activeResends);
if (_log.shouldLog(Log.INFO))
_log.info("Active resend of " + p + " successful, # active left: " + _activeResends);
}
}
}
@ -362,6 +390,9 @@ public class Connection {
public boolean getResetReceived() { return _resetReceived; }
public boolean getIsConnected() { return _connected; }
public boolean getHardDisconnected() { return _hardDisconnected; }
public boolean getResetSent() { return _resetSent; }
public long getResetSentOn() { return _resetSentOn; }
void disconnect(boolean cleanDisconnect) {
disconnect(cleanDisconnect, true);
@ -371,6 +402,13 @@ public class Connection {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Disconnecting " + toString(), new Exception("discon"));
if (!cleanDisconnect) {
_hardDisconnected = true;
if (_log.shouldLog(Log.WARN))
_log.warn("Hard disconnecting and sending a reset on " + toString(), new Exception("cause"));
sendReset();
}
if (cleanDisconnect && _connected) {
// send close packets and schedule stuff...
_outputStream.closeInternal();
@ -660,6 +698,14 @@ public class Connection {
default:
if (_log.shouldLog(Log.WARN))
_log.warn("Closing connection due to inactivity");
if (_log.shouldLog(Log.DEBUG)) {
StringBuffer buf = new StringBuffer(128);
buf.append("last sent was: ").append(_context.clock().now() - _lastSendTime);
buf.append("ms ago, last received was: ").append(_context.clock().now()-_lastReceivedOn);
buf.append("ms ago, inactivity timeout is: ").append(_options.getInactivityTimeout());
_log.debug(buf.toString());
}
disconnect(true);
break;
}
@ -693,6 +739,9 @@ public class Connection {
buf.append(" wsize: ").append(_options.getWindowSize());
buf.append(" cwin: ").append(_congestionWindowEnd - _highestAckedThrough);
buf.append(" rtt: ").append(_options.getRTT());
// not synchronized to avoid some kooky races
buf.append(" unacked outbound: ").append(_outboundPackets.size()).append(" ");
/*
buf.append(" unacked outbound: ");
synchronized (_outboundPackets) {
buf.append(_outboundPackets.size()).append(" [");
@ -701,6 +750,7 @@ public class Connection {
}
buf.append("] ");
}
*/
buf.append("unacked inbound? ").append(getUnackedPacketsReceived());
if (_inputStream != null) {
buf.append(" [high ");
@ -720,10 +770,8 @@ public class Connection {
*/
private class ResendPacketEvent implements SimpleTimer.TimedEvent {
private PacketLocal _packet;
private boolean _currentIsActiveResend;
public ResendPacketEvent(PacketLocal packet) {
_packet = packet;
_currentIsActiveResend = false;
packet.setResendPacketEvent(ResendPacketEvent.this);
}
@ -731,7 +779,7 @@ public class Connection {
if (_packet.getAckTime() > 0)
return;
if (!_connected) {
if (_resetSent || _resetReceived) {
_packet.cancelled();
return;
}
@ -739,12 +787,15 @@ public class Connection {
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Resend period reached for " + _packet);
boolean resend = false;
boolean isLowest = false;
synchronized (_outboundPackets) {
if (_packet.getSequenceNum() == _highestAckedThrough + 1)
isLowest = true;
if (_outboundPackets.containsKey(new Long(_packet.getSequenceNum())))
resend = true;
}
if ( (resend) && (_packet.getAckTime() < 0) ) {
if ( (_activeResends > 0) && (!_currentIsActiveResend) ) {
if (!isLowest) {
// we want to resend this packet, but there are already active
// resends in the air and we dont want to make a bad situation
// worse. wait another second
@ -776,7 +827,6 @@ public class Connection {
if (numSends == 2) {
// first resend for this packet
_activeResends++;
_currentIsActiveResend = true;
}
// in case things really suck, the other side may have lost thier
@ -796,6 +846,17 @@ public class Connection {
+ (_context.clock().now() - _packet.getCreatedOn()) + "ms)");
_outboundQueue.enqueue(_packet);
_lastSendTime = _context.clock().now();
// acked during resending (... or somethin')
if ( (_packet.getAckTime() > 0) && (_packet.getNumSends() > 1) ) {
_activeResends--;
synchronized (_outboundPackets) {
_outboundPackets.notifyAll();
}
return;
}
if (numSends > _options.getMaxResends()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Too many resends");

View File

@ -17,17 +17,20 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
private I2PAppContext _context;
private Log _log;
private Connection _connection;
private MessageOutputStream.WriteStatus _dummyStatus;
private static final MessageOutputStream.WriteStatus _dummyStatus = new DummyStatus();
public ConnectionDataReceiver(I2PAppContext ctx, Connection con) {
_context = ctx;
_log = ctx.logManager().getLog(ConnectionDataReceiver.class);
_connection = con;
_dummyStatus = new DummyStatus();
}
public boolean writeInProcess() {
return _connection.getUnackedPacketsSent() > 0;
Connection con = _connection;
if (con != null)
return con.getUnackedPacketsSent() > 0;
else
return false;
}
/**
@ -42,10 +45,12 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
* delivery.
*/
public MessageOutputStream.WriteStatus writeData(byte[] buf, int off, int size) {
Connection con = _connection;
if (con == null) return _dummyStatus;
boolean doSend = true;
if ( (size <= 0) && (_connection.getLastSendId() >= 0) ) {
if (_connection.getOutputStream().getClosed()) {
if (_connection.getCloseSentOn() < 0) {
if ( (size <= 0) && (con.getLastSendId() >= 0) ) {
if (con.getOutputStream().getClosed()) {
if (con.getCloseSentOn() < 0) {
doSend = true;
} else {
// closed, no new data, and we've already sent a close packet
@ -57,16 +62,18 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
}
}
if (_connection.getUnackedPacketsReceived() > 0)
if (con.getUnackedPacketsReceived() > 0)
doSend = true;
if (_log.shouldLog(Log.INFO) && !doSend)
_log.info("writeData called: size="+size + " doSend=" + doSend
+ " unackedReceived: " + _connection.getUnackedPacketsReceived()
+ " con: " + _connection, new Exception("write called by"));
+ " unackedReceived: " + con.getUnackedPacketsReceived()
+ " con: " + con, new Exception("write called by"));
if (doSend) {
PacketLocal packet = send(buf, off, size);
if (packet == null) return _dummyStatus;
//dont wait for non-acks
if ( (packet.getSequenceNum() > 0) || (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) )
return packet;
@ -85,7 +92,7 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
* @param buf data to be sent - may be null
* @param off offset into the buffer to start writing from
* @param size how many bytes of the buffer to write (may be 0)
* @return the packet sent
* @return the packet sent, or null if the connection died
*/
public PacketLocal send(byte buf[], int off, int size) {
return send(buf, off, size, false);
@ -99,31 +106,33 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
* @return the packet sent
*/
public PacketLocal send(byte buf[], int off, int size, boolean forceIncrement) {
Connection con = _connection;
if (con == null) return null;
long before = System.currentTimeMillis();
PacketLocal packet = buildPacket(buf, off, size, forceIncrement);
PacketLocal packet = buildPacket(con, buf, off, size, forceIncrement);
long built = System.currentTimeMillis();
_connection.sendPacket(packet);
con.sendPacket(packet);
long sent = System.currentTimeMillis();
if ( (built-before > 1000) && (_log.shouldLog(Log.WARN)) )
if ( (built-before > 5*1000) && (_log.shouldLog(Log.WARN)) )
_log.warn("wtf, took " + (built-before) + "ms to build a packet: " + packet);
if ( (sent-built> 1000) && (_log.shouldLog(Log.WARN)) )
if ( (sent-built> 5*1000) && (_log.shouldLog(Log.WARN)) )
_log.warn("wtf, took " + (sent-built) + "ms to send a packet: " + packet);
return packet;
}
private boolean isAckOnly(int size) {
private boolean isAckOnly(Connection con, int size) {
boolean ackOnly = ( (size <= 0) && // no data
(_connection.getLastSendId() >= 0) && // not a SYN
( (!_connection.getOutputStream().getClosed()) || // not a CLOSE
(_connection.getOutputStream().getClosed() &&
_connection.getCloseSentOn() > 0) )); // or it is a dup CLOSE
(con.getLastSendId() >= 0) && // not a SYN
( (!con.getOutputStream().getClosed()) || // not a CLOSE
(con.getOutputStream().getClosed() &&
con.getCloseSentOn() > 0) )); // or it is a dup CLOSE
return ackOnly;
}
private PacketLocal buildPacket(byte buf[], int off, int size, boolean forceIncrement) {
boolean ackOnly = isAckOnly(size);
PacketLocal packet = new PacketLocal(_context, _connection.getRemotePeer(), _connection);
private PacketLocal buildPacket(Connection con, byte buf[], int off, int size, boolean forceIncrement) {
boolean ackOnly = isAckOnly(con, size);
PacketLocal packet = new PacketLocal(_context, con.getRemotePeer(), con);
byte data[] = new byte[size];
if (size > 0)
System.arraycopy(buf, off, data, 0, size);
@ -131,36 +140,36 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
if (ackOnly && !forceIncrement)
packet.setSequenceNum(0);
else
packet.setSequenceNum(_connection.getNextOutboundPacketNum());
packet.setSendStreamId(_connection.getSendStreamId());
packet.setReceiveStreamId(_connection.getReceiveStreamId());
packet.setSequenceNum(con.getNextOutboundPacketNum());
packet.setSendStreamId(con.getSendStreamId());
packet.setReceiveStreamId(con.getReceiveStreamId());
_connection.getInputStream().updateAcks(packet);
packet.setOptionalDelay(_connection.getOptions().getChoke());
packet.setOptionalMaxSize(_connection.getOptions().getMaxMessageSize());
packet.setResendDelay(_connection.getOptions().getResendDelay());
con.getInputStream().updateAcks(packet);
packet.setOptionalDelay(con.getOptions().getChoke());
packet.setOptionalMaxSize(con.getOptions().getMaxMessageSize());
packet.setResendDelay(con.getOptions().getResendDelay());
if (_connection.getOptions().getProfile() == ConnectionOptions.PROFILE_INTERACTIVE)
if (con.getOptions().getProfile() == ConnectionOptions.PROFILE_INTERACTIVE)
packet.setFlag(Packet.FLAG_PROFILE_INTERACTIVE, true);
else
packet.setFlag(Packet.FLAG_PROFILE_INTERACTIVE, false);
packet.setFlag(Packet.FLAG_SIGNATURE_REQUESTED, _connection.getOptions().getRequireFullySigned());
packet.setFlag(Packet.FLAG_SIGNATURE_REQUESTED, con.getOptions().getRequireFullySigned());
if ( (!ackOnly) && (packet.getSequenceNum() <= 0) ) {
packet.setFlag(Packet.FLAG_SYNCHRONIZE);
packet.setOptionalFrom(_connection.getSession().getMyDestination());
packet.setOptionalFrom(con.getSession().getMyDestination());
}
// don't set the closed flag if this is a plain ACK and there are outstanding
// packets sent, otherwise the other side could receive the CLOSE prematurely,
// since this ACK could arrive before the unacked payload message.
if (_connection.getOutputStream().getClosed() &&
( (size > 0) || (_connection.getUnackedPacketsSent() <= 0) ) ) {
if (con.getOutputStream().getClosed() &&
( (size > 0) || (con.getUnackedPacketsSent() <= 0) || (packet.getSequenceNum() > 0) ) ) {
packet.setFlag(Packet.FLAG_CLOSE);
_connection.setCloseSentOn(_context.clock().now());
con.setCloseSentOn(_context.clock().now());
if (_log.shouldLog(Log.DEBUG))
_log.debug("Closed is set for a new packet on " + _connection + ": " + packet);
_log.debug("Closed is set for a new packet on " + con + ": " + packet);
} else {
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Closed is not set for a new packet on " + _connection + ": " + packet);

View File

@ -366,7 +366,8 @@ public class ConnectionManager {
if (removed) {
if (_notifier != null)
_notifier.pingComplete(false);
_log.error("Ping failed");
if (_log.shouldLog(Log.INFO))
_log.info("Ping failed");
}
}
}

View File

@ -89,6 +89,8 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
setInactivityTimeout(getInt(opts, PROP_INACTIVITY_TIMEOUT, 5*60*1000));
setInactivityAction(getInt(opts, PROP_INACTIVITY_ACTION, INACTIVITY_ACTION_DISCONNECT));
setInboundBufferSize((getMaxMessageSize() + 2) * Connection.MAX_WINDOW_SIZE);
setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
}
/**

View File

@ -25,6 +25,7 @@ public class ConnectionPacketHandler {
_context.statManager().createRateStat("stream.con.receiveDuplicateSize", "Size of a duplicate message received on a connection", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("stream.con.packetsAckedPerMessageReceived", "Size of a duplicate message received on a connection", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("stream.sendsBeforeAck", "How many times a message was sent before it was ACKed?", "Stream", new long[] { 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("stream.resetReceived", "How many messages had we sent successfully before receiving a RESET?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
}
/** distribute a packet to the connection specified */
@ -35,12 +36,27 @@ public class ConnectionPacketHandler {
_log.error("Packet does NOT verify: " + packet);
return;
}
if (con.getHardDisconnected()) {
if ( (packet.getSequenceNum() > 0) || (packet.getPayloadSize() > 0) ||
(packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) || (packet.isFlagSet(Packet.FLAG_CLOSE)) ) {
if (_log.shouldLog(Log.WARN))
_log.warn("Received a data packet after hard disconnect: " + packet + " on " + con);
con.sendReset();
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Received a packet after hard disconnect, ignoring: " + packet + " on " + con);
}
return;
}
con.packetReceived();
long ready = con.getInputStream().getHighestReadyBockId();
int available = con.getOptions().getInboundBufferSize() - con.getInputStream().getTotalReadySize();
int allowedBlocks = available/con.getOptions().getMaxMessageSize();
if (packet.getSequenceNum() > ready + allowedBlocks) {
if ( (packet.getPayloadSize() > 0) && (packet.getSequenceNum() > ready + allowedBlocks) ) {
if (_log.shouldLog(Log.WARN))
_log.warn("Inbound buffer exceeded on connection " + con + " ("
+ ready + "/"+ (ready+allowedBlocks) + "/" + available
@ -65,6 +81,8 @@ public class ConnectionPacketHandler {
if (packet.isFlagSet(Packet.FLAG_CLOSE) && packet.isFlagSet(Packet.FLAG_SIGNATURE_INCLUDED))
con.closeReceived();
boolean fastAck = false;
if (isNew) {
con.incrementUnackedPacketsReceived();
con.incrementBytesReceived(packet.getPayloadSize());
@ -82,18 +100,17 @@ public class ConnectionPacketHandler {
_log.debug("Scheduling ack in " + delay + "ms for received packet " + packet);
}
} else {
if ( (packet.getSequenceNum() > 0) || (packet.getPayloadSize() > 0) ) {
if ( (packet.getSequenceNum() > 0) || (packet.getPayloadSize() > 0) ||
(packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) ) {
_context.statManager().addRateData("stream.con.receiveDuplicateSize", packet.getPayloadSize(), 0);
con.incrementDupMessagesReceived(1);
// take note of congestion
//con.getOptions().setResendDelay(con.getOptions().getResendDelay()*2);
//con.getOptions().setWindowSize(con.getOptions().getWindowSize()/2);
if (_log.shouldLog(Log.WARN))
_log.warn("congestion.. dup " + packet);
SimpleTimer.getInstance().addEvent(new AckDup(con), con.getOptions().getSendAckDelay());
//con.incrementUnackedPacketsReceived();
con.setNextSendTime(_context.clock().now() + con.getOptions().getSendAckDelay());
//con.setNextSendTime(_context.clock().now() + con.getOptions().getSendAckDelay());
//fastAck = true;
} else {
if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) {
//con.incrementUnackedPacketsReceived();
@ -105,10 +122,10 @@ public class ConnectionPacketHandler {
}
}
boolean fastAck = ack(con, packet.getAckThrough(), packet.getNacks(), packet, isNew);
fastAck = fastAck || ack(con, packet.getAckThrough(), packet.getNacks(), packet, isNew);
con.eventOccurred();
if (fastAck) {
if (con.getLastSendTime() + con.getOptions().getRTT() < _context.clock().now()) {
if (con.getLastSendTime() + 2000 < _context.clock().now()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Fast ack for dup " + packet);
con.ackImmediately();
@ -264,6 +281,8 @@ public class ConnectionPacketHandler {
con.resetReceived();
con.eventOccurred();
_context.statManager().addRateData("stream.resetReceived", con.getHighestAckedThrough(), con.getLifetime());
// no further processing
return;
}

View File

@ -32,6 +32,8 @@ public class I2PSocketFull implements I2PSocket {
destroy();
}
Connection getConnection() { return _connection; }
public InputStream getInputStream() {
return _connection.getInputStream();
}

View File

@ -5,9 +5,11 @@ import java.io.InterruptedIOException;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
@ -225,4 +227,12 @@ public class I2PSocketManagerFull implements I2PSocketManager {
public String getName() { return _name; }
public void setName(String name) { _name = name; }
public void addDisconnectListener(DisconnectListener lsnr) {
_connectionManager.getMessageHandler().addDisconnectListener(lsnr);
}
public void removeDisconnectListener(DisconnectListener lsnr) {
_connectionManager.getMessageHandler().removeDisconnectListener(lsnr);
}
}

View File

@ -1,5 +1,8 @@
package net.i2p.client.streaming;
import java.util.ArrayList;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionListener;
@ -15,10 +18,12 @@ public class MessageHandler implements I2PSessionListener {
private ConnectionManager _manager;
private I2PAppContext _context;
private Log _log;
private List _listeners;
public MessageHandler(I2PAppContext ctx, ConnectionManager mgr) {
_manager = mgr;
_context = ctx;
_listeners = new ArrayList(1);
_log = ctx.logManager().getLog(MessageHandler.class);
_context.statManager().createRateStat("stream.packetReceiveFailure", "When do we fail to decrypt or otherwise receive a packet sent to us?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
}
@ -69,6 +74,16 @@ public class MessageHandler implements I2PSessionListener {
if (_log.shouldLog(Log.ERROR))
_log.error("I2PSession disconnected");
_manager.disconnectAllHard();
List listeners = null;
synchronized (_listeners) {
listeners = new ArrayList(_listeners);
_listeners.clear();
}
for (int i = 0; i < listeners.size(); i++) {
I2PSocketManager.DisconnectListener lsnr = (I2PSocketManager.DisconnectListener)listeners.get(i);
lsnr.sessionDisconnected();
}
}
/**
@ -80,4 +95,15 @@ public class MessageHandler implements I2PSessionListener {
_log.error("error occurred: " + message, error);
//_manager.disconnectAllHard();
}
public void addDisconnectListener(I2PSocketManager.DisconnectListener lsnr) {
synchronized (_listeners) {
_listeners.add(lsnr);
}
}
public void removeDisconnectListener(I2PSocketManager.DisconnectListener lsnr) {
synchronized (_listeners) {
_listeners.remove(lsnr);
}
}
}

View File

@ -152,16 +152,28 @@ public class PacketHandler {
}
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Receive a syn packet with the wrong IDs: " + packet);
_log.warn("Receive a syn packet with the wrong IDs, sending reset: " + packet);
sendReset(packet);
}
} else {
// someone is sending us a packet on the wrong stream
if (_log.shouldLog(Log.WARN))
_log.warn("Received a packet on the wrong stream: " + packet);
_log.warn("Received a packet on the wrong stream: " + packet + " connection: " + con);
}
}
}
private void sendReset(Packet packet) {
PacketLocal reply = new PacketLocal(_context, packet.getOptionalFrom());
reply.setFlag(Packet.FLAG_RESET);
reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
reply.setSendStreamId(packet.getReceiveStreamId());
reply.setReceiveStreamId(packet.getSendStreamId());
reply.setOptionalFrom(_manager.getSession().getMyDestination());
// this just sends the packet - no retries or whatnot
_manager.getPacketQueue().enqueue(reply);
}
private void receiveUnknownCon(Packet packet, byte sendId[]) {
if (packet.isFlagSet(Packet.FLAG_ECHO)) {
if (packet.getSendStreamId() != null) {

View File

@ -69,6 +69,8 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
public void prepare() {
if (_connection != null)
_connection.getInputStream().updateAcks(this);
if (_numSends > 0) // so we can debug to differentiate resends
setOptionalDelay(_numSends * 1000);
}
public long getCreatedOn() { return _createdOn; }
@ -110,10 +112,14 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
public String toString() {
String str = super.toString();
if ( (_tagsSent != null) && (_tagsSent.size() > 0) )
str = str + " with tags";
if (_ackOn > 0)
return str + " ack after " + getAckTime();
return str + " ack after " + getAckTime() + (_numSends <= 1 ? "" : " sent " + _numSends + " times");
else
return str;
return str + (_numSends <= 1 ? "" : " sent " + _numSends + " times");
}
public void waitForAccept(int maxWaitMs) {

View File

@ -74,6 +74,9 @@ class PacketQueue {
if ( (writeTime > 1000) && (_log.shouldLog(Log.WARN)) )
_log.warn("took " + writeTime + "ms to write the packet: " + packet);
// last chance to short circuit...
if (packet.getAckTime() > 0) return;
// this should not block!
begin = _context.clock().now();
sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent);

View File

@ -38,6 +38,7 @@ class SchedulerChooser {
private List createSchedulers() {
List rv = new ArrayList(8);
rv.add(new SchedulerHardDisconnected(_context));
rv.add(new SchedulerPreconnect(_context));
rv.add(new SchedulerConnecting(_context));
rv.add(new SchedulerReceived(_context));
@ -54,8 +55,7 @@ class SchedulerChooser {
}
public void eventOccurred(Connection con) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Event occurred on " + con, new Exception("source"));
_log.log(Log.CRIT, "Yell at jrandom: Event occurred on " + con, new Exception("source"));
}
public boolean accept(Connection con) { return true; }
};

View File

@ -37,7 +37,7 @@ class SchedulerConnecting extends SchedulerImpl {
public boolean accept(Connection con) {
if (con == null) return false;
boolean notYetConnected = (con.getIsConnected()) &&
(con.getSendStreamId() == null) &&
//(con.getSendStreamId() == null) && // not null on recv
(con.getLastSendId() >= 0) &&
(con.getAckedPackets() <= 0) &&
(!con.getResetReceived());
@ -55,6 +55,7 @@ class SchedulerConnecting extends SchedulerImpl {
_log.debug("waited too long: " + waited);
return;
} else {
// should we be doing a con.sendAvailable here?
if (con.getOptions().getConnectTimeout() > 0)
reschedule(con.getOptions().getConnectTimeout(), con);
}

View File

@ -33,10 +33,13 @@ class SchedulerDead extends SchedulerImpl {
public boolean accept(Connection con) {
if (con == null) return false;
long timeSinceClose = _context.clock().now() - con.getCloseSentOn();
if (con.getResetSent())
timeSinceClose = _context.clock().now() - con.getResetSentOn();
boolean nothingLeftToDo = (con.getCloseSentOn() > 0) &&
(con.getCloseReceivedOn() > 0) &&
(con.getUnackedPacketsReceived() <= 0) &&
(con.getUnackedPacketsSent() <= 0) &&
(con.getResetSent()) &&
(timeSinceClose >= Connection.DISCONNECT_TIMEOUT);
boolean timedOut = (con.getOptions().getConnectTimeout() < con.getLifetime()) &&
con.getSendStreamId() == null &&

View File

@ -0,0 +1,47 @@
package net.i2p.client.streaming;
import net.i2p.I2PAppContext;
import net.i2p.util.Log;
/**
* <p>Scheduler used after we've locally done a hard disconnect,
* but the final timeout hasn't passed.</p>
*
* <h2>Entry conditions:</h2><ul>
* <li>Locally disconnected hard.</li>
* <li>Less than the final timeout period has passed since the last ACK.</li>
* </ul>
*
* <h2>Events:</h2><ul>
* <li>Packets received</li>
* <li>RESET received</li>
* <li>Message sending fails (error talking to the session)</li>
* </ul>
*
* <h2>Next states:</h2>
* <li>{@link SchedulerDead dead} - after the final timeout passes</li>
* </ul>
*
*
*/
class SchedulerHardDisconnected extends SchedulerImpl {
private Log _log;
public SchedulerHardDisconnected(I2PAppContext ctx) {
super(ctx);
_log = ctx.logManager().getLog(SchedulerHardDisconnected.class);
}
public boolean accept(Connection con) {
if (con == null) return false;
long timeSinceClose = _context.clock().now() - con.getCloseSentOn();
if (con.getResetSent())
timeSinceClose = _context.clock().now() - con.getResetSentOn();
boolean ok = (con.getHardDisconnected() || con.getResetSent()) &&
(timeSinceClose < Connection.DISCONNECT_TIMEOUT);
return ok;
}
public void eventOccurred(Connection con) {
// noop. we do the timeout through the simpleTimer anyway
}
}

View File

@ -0,0 +1,138 @@
package net.i2p.client.streaming;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.I2PSession;
import net.i2p.data.Destination;
import net.i2p.util.Log;
/**
* Have a client connect to a server, where the server waits 5
* seconds and closes the socket and the client detect that
* EOF.
*
*/
public class ConnectCloseTest {
private Log _log;
private I2PSession _server;
public void test() {
try {
I2PAppContext context = I2PAppContext.getGlobalContext();
_log = context.logManager().getLog(ConnectCloseTest.class);
_log.debug("creating server session");
_server = createSession();
_log.debug("running server");
runServer(context, _server);
_log.debug("running client");
runClient(context, createSession());
} catch (Exception e) {
_log.error("error running", e);
}
try { Thread.sleep(10*60*1000); } catch (Exception e) {}
}
private void runClient(I2PAppContext ctx, I2PSession session) {
Thread t = new Thread(new ClientRunner(ctx, session));
t.setName("client");
t.setDaemon(true);
t.start();
}
private void runServer(I2PAppContext ctx, I2PSession session) {
Thread t = new Thread(new ServerRunner(ctx, session));
t.setName("server");
t.setDaemon(true);
t.start();
}
private class ServerRunner implements Runnable {
private I2PAppContext _context;
private I2PSession _session;
private Log _log;
public ServerRunner(I2PAppContext ctx, I2PSession session) {
_context = ctx;
_session = session;
_log = ctx.logManager().getLog(ServerRunner.class);
}
public void run() {
try {
Properties opts = new Properties();
I2PSocketManager mgr = new I2PSocketManagerFull(_context, _session, opts, "client");
_log.debug("* manager created");
I2PServerSocket ssocket = mgr.getServerSocket();
_log.debug("* server socket created");
while (true) {
I2PSocket socket = ssocket.accept();
_log.debug("* socket accepted: " + socket);
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
socket.close();
_log.debug("* socket closed: " + socket);
}
} catch (Exception e) {
_log.error("error running", e);
}
}
}
private class ClientRunner implements Runnable {
private I2PAppContext _context;
private I2PSession _session;
private Log _log;
public ClientRunner(I2PAppContext ctx, I2PSession session) {
_context = ctx;
_session = session;
_log = ctx.logManager().getLog(ClientRunner.class);
}
public void run() {
try {
Properties opts = new Properties();
I2PSocketManager mgr = new I2PSocketManagerFull(_context, _session, opts, "client");
_log.debug("* manager created");
I2PSocket socket = mgr.connect(_server.getMyDestination());
_log.debug("* socket created");
InputStream in = socket.getInputStream();
int c = in.read();
if (c != -1)
throw new RuntimeException("hrm, we got data? [" + c + "]");
socket.close();
_log.debug("* socket closed");
mgr.destroySocketManager();
mgr = null;
socket = null;
} catch (Exception e) {
_log.error("error running", e);
}
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
System.exit(0);
}
}
private I2PSession createSession() {
try {
I2PClient client = I2PClientFactory.createClient();
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
Destination dest = client.createDestination(baos);
I2PSession sess = client.createSession(new ByteArrayInputStream(baos.toByteArray()), System.getProperties());
sess.connect();
return sess;
} catch (Exception e) {
_log.error("error running", e);
throw new RuntimeException("b0rk b0rk b0rk");
}
}
public static void main(String args[]) {
ConnectCloseTest ct = new ConnectCloseTest();
ct.test();
}
}

View File

@ -26,6 +26,7 @@
<ant dir="apps/netmonitor/java/" target="jar" />
<ant dir="apps/systray/java/" target="jar" />
<ant dir="apps/routerconsole/java/" target="jar" />
<ant dir="apps/addressbook/" target="war" />
</target>
<target name="buildWEB">
<ant dir="apps/jetty" target="fetchJettylib" />
@ -55,6 +56,7 @@
<copy file="apps/netmonitor/java/build/netmonitor.jar" todir="build/" />
<copy file="apps/systray/java/build/systray.jar" todir="build/" />
<copy file="installer/lib/jbigi/jbigi.jar" todir="build" />
<copy file="apps/addressbook/dist/addressbook.war" todir="build/" />
</target>
<target name="javadoc">
<mkdir dir="./build" />
@ -102,6 +104,7 @@
<ant dir="apps/heartbeat/java/" target="distclean" />
<ant dir="apps/netmonitor/java/" target="distclean" />
<ant dir="apps/routerconsole/java/" target="distclean" />
<ant dir="apps/addressbook/" target="distclean" />
<ant dir="apps/systray/java/" target="distclean" />
<ant dir="installer/java/" target="distclean" />
<delete>
@ -178,6 +181,7 @@
<copy file="build/xml-apis.jar" todir="pkg-temp/lib/" />
<copy file="build/i2ptunnel.war" todir="pkg-temp/webapps/" />
<copy file="build/routerconsole.war" todir="pkg-temp/webapps/" />
<copy file="build/addressbook.war" todir="pkg-temp/webapps/" />
<copy file="installer/resources/clients.config" todir="pkg-temp/" />
<copy file="installer/resources/i2prouter" todir="pkg-temp/" />
<copy file="installer/resources/i2prouter.bat" todir="pkg-temp/" />
@ -239,6 +243,7 @@
<copy file="build/routerconsole.jar" todir="pkg-temp/lib/" />
<copy file="build/i2ptunnel.war" todir="pkg-temp/webapps/" />
<copy file="build/routerconsole.war" todir="pkg-temp/webapps/" />
<copy file="build/addressbook.war" todir="pkg-temp/webapps/" />
<copy file="history.txt" todir="pkg-temp/" />
<copy file="hosts.txt" todir="pkg-temp/" />
<mkdir dir="pkg-temp/eepsite" />

View File

@ -1,5 +1,5 @@
Prior to building the jbigi library, you will need to fetch the GMP source
from http://www.swox.com/gmp/, saving it to jbigi/gmp-4.1.3.tar.bz2 (it will
from http://www.swox.com/gmp/, saving it to jbigi/gmp-4.1.4.tar.bz2 (it will
be unpacked and built as necessary).
To build the native jbigi and jcpuid libraries for the current host CPU,

View File

@ -7,7 +7,6 @@ mkdir -p t/freenet/support/CPUInformation/
cp jcpuid/lib/freenet/support/CPUInformation/*jcpuid* t/freenet/support/CPUInformation/
mkdir -p t/net/i2p/util/
cp jbigi/lib/net/i2p/util/*jbigi* t/net/i2p/util/
cp jbigi/lib/*jbigi* t/
(cd t ; jar cf ../jbigi.jar . ; cd ..)

View File

@ -13,7 +13,7 @@ FreeBSD*)
esac
echo "Extracting GMP..."
tar -xjf gmp-4.1.3.tar.bz2
tar -xjf gmp-4.1.4.tar.bz2
echo "Building..."
mkdir bin
mkdir lib
@ -24,7 +24,7 @@ for x in none pentium pentiummmx pentium2 pentium3 pentium4 k6 k62 k63 athlon
do
mkdir bin/$x
cd bin/$x
../../gmp-4.1.3/configure --build=$x
../../gmp-4.1.4/configure --build=$x
make
sh ../../build_jbigi.sh static
case `uname -sr` in

View File

@ -1,41 +1,15 @@
#/bin/sh
case `uname -sr` in
MINGW*)
echo "Building windows .dll's";;
CYGWIN*)
echo "Building windows .dll's";;
Linux*)
echo "Building linux .so's";;
FreeBSD*)
echo "Building freebsd .so's";;
*)
echo "Unsupported build environment"
exit;;
esac
echo "Building the jbigi library with GMP"
echo "Extracting GMP..."
tar -xjf gmp-4.1.3.tar.bz2
tar -xjf gmp-4.1.4.tar.bz2
echo "Building..."
mkdir bin
mkdir lib
mkdir lib/net
mkdir lib/net/i2p
mkdir lib/net/i2p/util
mkdir bin/local
mkdir -p lib/
mkdir -p bin/local
cd bin/local
../../gmp-4.1.3/configure
../../gmp-4.1.4/configure
make
sh ../../build_jbigi.sh static
case `uname -sr` in
MINGW*)
cp jbigi.dll ../../lib/jbigi;;
CYGWIN*)
cp jbigi.dll ../../lib/jbigi;;
Linux*)
cp libjbigi.so ../../lib/jbigi;;
FreeBSD*)
cp libjbigi.so ../../lib/jbigi;;
esac
cd ..
cd ..
cp *jbigi???* ../../lib/
cd ../..

View File

@ -20,7 +20,7 @@ mkdir lib/freenet
mkdir lib/freenet/support
mkdir lib/freenet/support/CPUInformation
CPP="g++"
CC="gcc"
case `uname -sr` in
MINGW*)
@ -43,7 +43,7 @@ esac
echo "Compiling C code..."
rm -f $LIBFILE
$CPP $LINKFLAGS $INCLUDES src/*.cpp -o $LIBFILE
$CC $LINKFLAGS $INCLUDES src/*.c -o $LIBFILE
strip $LIBFILE
echo Built $LIBFILE

View File

@ -5,8 +5,8 @@ JNIEXPORT jobject JNICALL Java_freenet_support_CPUInformation_CPUID_doCPUID
(JNIEnv * env, jclass cls, jint iFunction)
{
int a,b,c,d;
jclass clsResult = env->FindClass ("freenet/support/CPUInformation/CPUID$CPUIDResult");
jmethodID constructor = env->GetMethodID(clsResult,"<init>","(IIII)V" );
jclass clsResult = (*env)->FindClass(env, "freenet/support/CPUInformation/CPUID$CPUIDResult");
jmethodID constructor = (*env)->GetMethodID(env, clsResult,"<init>","(IIII)V" );
#ifdef _MSC_VER
//Use MSVC assembler notation
_asm
@ -30,6 +30,6 @@ JNIEXPORT jobject JNICALL Java_freenet_support_CPUInformation_CPUID_doCPUID
:"a"(iFunction)
);
#endif
return env->NewObject(clsResult,constructor,a,b,c,d);
return (*env)->NewObject(env, clsResult,constructor,a,b,c,d);
}

View File

@ -105,6 +105,8 @@ class I2PSessionImpl2 extends I2PSessionImpl {
}
return compressed;
}
private static final int NUM_TAGS = 50;
private boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent)
throws I2PSessionException {
@ -119,14 +121,14 @@ class I2PSessionImpl2 extends I2PSessionImpl {
if ( (tagsSent == null) || (tagsSent.size() <= 0) ) {
if (oldTags < 10) {
sentTags = createNewTags(50);
sentTags = createNewTags(NUM_TAGS);
if (_log.shouldLog(Log.DEBUG))
_log.debug("** sendBestEffort only had " + oldTags + " with " + availTimeLeft + ", adding 50");
_log.debug("** sendBestEffort only had " + oldTags + " with " + availTimeLeft + ", adding " + NUM_TAGS);
} else if (availTimeLeft < 2 * 60 * 1000) {
// if we have > 10 tags, but they expire in under 2 minutes, we want more
sentTags = createNewTags(50);
sentTags = createNewTags(NUM_TAGS);
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Tags expiring in " + availTimeLeft + ", adding 50 new ones");
_log.debug(getPrefix() + "Tags expiring in " + availTimeLeft + ", adding " + NUM_TAGS + " new ones");
//_log.error("** sendBestEffort available time left " + availTimeLeft);
} else {
if (_log.shouldLog(Log.DEBUG))
@ -240,11 +242,11 @@ class I2PSessionImpl2 extends I2PSessionImpl {
SessionTag tag = _context.sessionKeyManager().consumeNextAvailableTag(dest.getPublicKey(), key);
Set sentTags = null;
if (_context.sessionKeyManager().getAvailableTags(dest.getPublicKey(), key) < 10) {
sentTags = createNewTags(50);
sentTags = createNewTags(NUM_TAGS);
} else if (_context.sessionKeyManager().getAvailableTimeLeft(dest.getPublicKey(), key) < 2 * 60 * 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(getPrefix() + "Tags are almost expired, adding 50 new ones");
sentTags = createNewTags(NUM_TAGS);
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Tags are almost expired, adding " + NUM_TAGS + " new ones");
}
SessionKey newKey = null;
if (false) // rekey

View File

@ -22,7 +22,8 @@ public abstract class NamingService {
private final static Log _log = new Log(NamingService.class);
protected I2PAppContext _context;
private static final String PROP_IMPL = "i2p.naming.impl";
/** what classname should be used as the naming service impl? */
public static final String PROP_IMPL = "i2p.naming.impl";
private static final String DEFAULT_IMPL = "net.i2p.client.naming.HostsTxtNamingService";

View File

@ -127,6 +127,21 @@ public class AESEngine {
_log.warn("Warning: AES is disabled");
}
public void encryptBlock(byte payload[], int inIndex, SessionKey sessionKey, byte out[], int outIndex) {
System.arraycopy(payload, inIndex, out, outIndex, out.length - outIndex);
}
/** decrypt the data with the session key provided
* @param payload encrypted data
* @param sessionKey private session key
* @return unencrypted data
*/
public void decryptBlock(byte payload[], int inIndex, SessionKey sessionKey, byte rv[], int outIndex) {
System.arraycopy(payload, inIndex, rv, outIndex, rv.length - outIndex);
}
public static void main(String args[]) {
I2PAppContext ctx = new I2PAppContext();
SessionKey key = ctx.keyGenerator().generateSessionKey();

View File

@ -161,7 +161,7 @@ public class AESInputStream extends FilterInputStream {
*
*/
private void refill() throws IOException {
if (!_eofFound) {
if ( (!_eofFound) && (_writesSinceDecrypt < BLOCK_SIZE) ) {
int read = in.read(_encryptedBuf, _writesSinceDecrypt, _encryptedBuf.length - _writesSinceDecrypt);
if (read == -1) {
_eofFound = true;

View File

@ -85,14 +85,14 @@ public class CryptixAESEngine extends AESEngine {
if (length % 16 != 0) numblock++;
decryptBlock(payload, payloadIndex, sessionKey, out, outIndex);
DataHelper.xor(out, outIndex, iv, 0, out, outIndex, 16);
DataHelper.xor(out, outIndex, iv, 0, out, outIndex, 16);
for (int x = 1; x < numblock; x++) {
decryptBlock(payload, payloadIndex + (x * 16), sessionKey, out, outIndex + (x * 16));
DataHelper.xor(out, outIndex + x * 16, payload, payloadIndex + (x - 1) * 16, out, outIndex + x * 16, 16);
}
}
final void encryptBlock(byte payload[], int inIndex, SessionKey sessionKey, byte out[], int outIndex) {
public final void encryptBlock(byte payload[], int inIndex, SessionKey sessionKey, byte out[], int outIndex) {
CryptixAESKeyCache.KeyCacheEntry keyData = _cache.acquireKey();
try {
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16, keyData);
@ -109,7 +109,7 @@ public class CryptixAESEngine extends AESEngine {
* @param sessionKey private session key
* @return unencrypted data
*/
final void decryptBlock(byte payload[], int inIndex, SessionKey sessionKey, byte rv[], int outIndex) {
public final void decryptBlock(byte payload[], int inIndex, SessionKey sessionKey, byte rv[], int outIndex) {
if ( (payload == null) || (rv == null) )
throw new IllegalArgumentException("null block args [payload=" + payload + " rv="+rv);
if (payload.length - inIndex > rv.length - outIndex)
@ -131,9 +131,11 @@ public class CryptixAESEngine extends AESEngine {
I2PAppContext ctx = new I2PAppContext();
try {
testEDBlock(ctx);
testEDBlock2(ctx);
testED(ctx);
testFake(ctx);
testNull(ctx);
testED2(ctx);
//testFake(ctx);
//testNull(ctx);
} catch (Exception e) {
e.printStackTrace();
}
@ -155,6 +157,21 @@ public class CryptixAESEngine extends AESEngine {
else
System.out.println("full D(E(orig)) == orig");
}
private static void testED2(I2PAppContext ctx) {
SessionKey key = ctx.keyGenerator().generateSessionKey();
byte iv[] = new byte[16];
byte orig[] = new byte[128];
byte data[] = new byte[128];
ctx.random().nextBytes(iv);
ctx.random().nextBytes(orig);
CryptixAESEngine aes = new CryptixAESEngine(ctx);
aes.encrypt(orig, 0, data, 0, key, iv, data.length);
aes.decrypt(data, 0, data, 0, key, iv, data.length);
if (!DataHelper.eq(data,orig))
throw new RuntimeException("full D(E(orig)) != orig");
else
System.out.println("full D(E(orig)) == orig");
}
private static void testFake(I2PAppContext ctx) {
SessionKey key = ctx.keyGenerator().generateSessionKey();
SessionKey wrongKey = ctx.keyGenerator().generateSessionKey();
@ -207,4 +224,19 @@ public class CryptixAESEngine extends AESEngine {
else
System.out.println("block D(E(orig)) == orig");
}
private static void testEDBlock2(I2PAppContext ctx) {
SessionKey key = ctx.keyGenerator().generateSessionKey();
byte iv[] = new byte[16];
byte orig[] = new byte[16];
byte data[] = new byte[16];
ctx.random().nextBytes(iv);
ctx.random().nextBytes(orig);
CryptixAESEngine aes = new CryptixAESEngine(ctx);
aes.encryptBlock(orig, 0, key, data, 0);
aes.decryptBlock(data, 0, key, data, 0);
if (!DataHelper.eq(data,orig))
throw new RuntimeException("block D(E(orig)) != orig");
else
System.out.println("block D(E(orig)) == orig");
}
}

View File

@ -43,10 +43,16 @@ public class Base64 {
private final static Log _log = new Log(Base64.class);
public static String encode(byte[] source) {
return encode(source, false);
return encode(source, 0, (source != null ? source.length : 0));
}
public static String encode(byte[] source, int off, int len) {
return encode(source, off, len, false);
}
public static String encode(byte[] source, boolean useStandardAlphabet) {
return safeEncode(source, useStandardAlphabet);
return encode(source, 0, (source != null ? source.length : 0), useStandardAlphabet);
}
public static String encode(byte[] source, int off, int len, boolean useStandardAlphabet) {
return safeEncode(source, off, len, useStandardAlphabet);
}
public static byte[] decode(String s) {
@ -318,8 +324,8 @@ public class Base64 {
* Same as encodeBytes, except uses a filesystem / URL friendly set of characters,
* replacing / with ~, and + with -
*/
private static String safeEncode(byte[] source, boolean useStandardAlphabet) {
String encoded = encodeBytes(source);
private static String safeEncode(byte[] source, int off, int len, boolean useStandardAlphabet) {
String encoded = encodeBytes(source, off, len, false);
if (useStandardAlphabet) {
// noop
} else {

View File

@ -559,6 +559,16 @@ public class DataHelper {
return lhs == rhs;
}
public final static boolean eq(byte lhs[], int offsetLeft, byte rhs[], int offsetRight, int length) {
if ( (lhs == null) || (rhs == null) ) return false;
if (length <= 0) return true;
for (int i = 0; i < length; i++) {
if (lhs[offsetLeft + i] != rhs[offsetRight + i])
return false;
}
return true;
}
public final static int compareTo(byte lhs[], byte rhs[]) {
if ((rhs == null) && (lhs == null)) return 0;
if (lhs == null) return -1;
@ -680,7 +690,19 @@ public class DataHelper {
*/
public static String readLine(InputStream in) throws IOException {
StringBuffer buf = new StringBuffer(128);
boolean ok = readLine(in, buf);
if (ok)
return buf.toString();
else
return null;
}
/**
* Read in a line, placing it into the buffer (excluding the newline).
*
* @return true if the line was read, false if eof was reached before a
* newline was found
*/
public static boolean readLine(InputStream in, StringBuffer buf) throws IOException {
int c = -1;
while ( (c = in.read()) != -1) {
if (c == '\n')
@ -688,9 +710,9 @@ public class DataHelper {
buf.append((char)c);
}
if (c == -1)
return null;
return false;
else
return buf.toString();
return true;
}

View File

@ -69,6 +69,7 @@ public class Payload extends DataStructureImpl {
public void readBytes(InputStream in) throws DataFormatException, IOException {
int size = (int) DataHelper.readLong(in, 4);
if (size < 0) throw new DataFormatException("payload size out of range (" + size + ")");
_encryptedData = new byte[size];
int read = read(in, _encryptedData);
if (read != size) throw new DataFormatException("Incorrect number of bytes read in the payload structure");

View File

@ -124,7 +124,8 @@ public class Timestamper implements Runnable {
alreadyBitched = true;
}
}
try { Thread.sleep(_queryFrequency); } catch (InterruptedException ie) {}
long sleepTime = _context.random().nextInt(_queryFrequency) + _queryFrequency;
try { Thread.sleep(sleepTime); } catch (InterruptedException ie) {}
}
} catch (Throwable t) {
_log.log(Log.CRIT, "Timestamper died!", t);

View File

@ -42,8 +42,8 @@ public class Clock implements Timestamper.UpdateListener {
/** if the clock is skewed by 3+ days, fuck 'em */
public final static long MAX_OFFSET = 3 * 24 * 60 * 60 * 1000;
/** after we've started up and shifted the clock, don't allow shifts of more than a minute */
public final static long MAX_LIVE_OFFSET = 60 * 1000;
/** after we've started up and shifted the clock, don't allow shifts of more than 10 minutes */
public final static long MAX_LIVE_OFFSET = 10 * 60 * 1000;
/** if the clock skewed changes by less than 1s, ignore the update (so we don't slide all over the place) */
public final static long MIN_OFFSET_CHANGE = 10 * 1000;

View File

@ -0,0 +1,206 @@
package net.i2p.crypto;
import net.i2p.I2PAppContext;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey;
/**
* Unit test verifying the ElGamal encryption/decryption with some test
* data. The keys generated & data stored were generated by jrandom on
* a pentium4 w/ an optimized jbigi installed and verified with pure java.
*
*/
public class ElGamalVerify {
private I2PAppContext _context;
private static final String UNENCRYPTED[] = new String[] {
"",
"hello world",
"1234567890123456789012345678901234567890123456789012345678901234567890" +
"1234567890123456789012345678901234567890123456789012345678901234567890" +
"1234567890123456789012345678901234567890123456789012345678901234567890" +
"123456789012",
"\0x00",
"\0x00\0x00\0x00",
"\0x00\0x01\0x02\0x00",
};
private static final String PUBLIC_KEY = new String(
"pOvBUMrSUUeN5awynzbPbCAwe3MqWprhSpp3OR7pvdfm9PhWaNbPoKRLeEmDoUwyNDoHE0" +
"E6mcZSG8qPQ8XUZFlczpilOl0MJBvsI9u9SMyi~bEqzSgzh9FNfS-NcGji3q2wI~Ux~q5B" +
"KOjGlyMLgd1nxl5R5wIYL4uHKZNaYuArsRYmtV~MgMQPGvDtIbdGTV6aL6UbOYryzQSUMY" +
"OuO3S~YoBjA6Nmi0SeJM3tyTxlI6U1EYjR6oQcI4SOFUW4L~8pfYWijcncCODAqpXVN6ZI" +
"AJ3a6vjxGu56IDp4xCcKlOEHgdXvqmEC67dR5qf2btH6dtWoB3-Z6QPsS6tPTQ=="
);
private static final String PRIVATE_KEY = new String(
"gMlIhURVXU8uPube20Xr8E1K11g-3qZxOj1riThHqt-rBx72MPq5ivT1rr28cE9mzOmsXi" +
"bbsuBuQKYDvF7hGICRB3ROSPePYhcupV3j7XiXUIYjWNw9hvylHXK~nTT7jkpIBazBJZfr" +
"LJPcDZTDB0YnCOHOL-KFn4N1R5B22g0iYRABN~O10AUjQmf1epklAXPqYlzmOYeJSfTPBI" +
"E44nEccWJp0M0KynhKVbDI0v9VYm6sPFK7WrzRyWwHL~r735wiRkwywuMmKJtA7-PuJjcW" +
"NLkJwx6WScH2msMzhzYPi8JSZJBl~PosX934l-L0T-KNV4jg1Ih6yoCnm1748A=="
);
private static final String ENCRYPTED[] = new String[] {
"AMfISa8KvTpaC7KXZzSvC2axyiSk0xPexBAf29yU~IKq21DzaU19wQcGJg-ktpG4hjGSg7" +
"u-mJ07b61yo-EGmVGZsv3nYuQYW-GjvsZQa9nm98VljlMtWrxu7TsRXw~SQlWQxMvthqJB" +
"1A7Y7Qa~C7-UlRytkD-cpVdgUfM-esuMWmjGs6Vc33N5U-tce5Fywa-9y7PSn3ukBO8KGR" +
"wm7T12~H2gvhgxrVeK2roOzsV7f5dGkvBQRZJ309Vg3j0kjaxWutgI3vli0pzDbSK9d5NR" +
"-GUDtdOb6IIfLiOckBegcv6I-wlSXjYJe8mIoaK45Ok3rEpHwWKVKS2MeuI7AmsAWgkQmW" +
"f8irmZaKc9X910VWSO5GYu6006hSc~r2TL3O7vwtW-Z9Oq~sAam9av1PPVJzAx8A4g~m~1" +
"avtNnncwlChsGo6mZHXqz-QMdMJXXP57f4bx36ZomkvpM-ZLlFAn-a~42KQJAApo4LfEyk" +
"7DPY2aTXL9ArOCNQIQB4f8QLyjvAvu6M3jzCoGo0wVX6oePfdiokGflriYOcD8rL4NbnCP" +
"~MSnVzC8LKyRzQVN1tDYj8~njuFqekls6En8KFJ-qgtL4PiYxbnBQDUPoW6y61m-S9r9e9" +
"y8qWd6~YtdAHAxVlw287~HEp9r7kqI-cjdo1337b7~5dm83KK45g5Nfw==",
"AIrd65mG1FJ~9J-DDSyhryVejJBSIjYOqV3GYmHDWgwLchTwq-bJS7dub3ENk9MZ-C6FIN" +
"gjUFRaLBtfwJnySmNf8pIf1srmgdfqGV2h77ufG5Gs0jggKPmPV~7Z1kTcgsqpL8MyrfXr" +
"Gi86X5ey-T0SZSFc0X1EhaE-47WlyWaGf-~xth6VOR~KG7clOxaOBpks-7WKZNQf7mpQRE" +
"4IsPJyj5p1Rf-MeDbVKbK~52IfXSuUZQ8uZr34KMoy4chjn6e-jBhM4XuaQWhsM~a3Q-zE" +
"pV-ea6t0bQTYfsbG9ch7pJuDPHM64o5mF9FS5-JGr7MOtfP7KDNHiYM2~-uC6BIAbiqBN8" +
"WSLX1mrHVuhiM-hiJ7U4oq~HYB6N~U980sCIW0dgFBbhalzzQhJQSrC1DFDqGfL5-L25mj" +
"ArP8dtvN0JY3LSnbcsm-pT9ttFHCPGomLfaAuP7ohknBoXK0j9e6~splg5sUA9TfLeBfqc" +
"Lr0Sf8b3l~PvmrVkbVcaE8yUqSS6JFdt3pavjyyAQSmSlb2jVNKGPlrov5QLzlbH7G~AUv" +
"IehsbGQX5ptRROtSojN~iYx3WQTOa-JLEC-AL7RbRu6B62p9I0pD0JgbUfCc4C4l9E9W~s" +
"MuaJLAXxh0b2miF7C5bzZHxbt~MtZ7Ho5qpZMitXyoE3icb43B6Y1sbA==",
"ACjb0FkTIQbnEzCZlYXGxekznfJad5uW~F5Mbu~0wtsI1O2veqdr7Mb0N754xdIz7929Ti" +
"1Kz-CxVEAkb3RBbVNcYHLfjy23oQ4BCioDKQaJcdkJqXa~Orm7Ta2tbkhM1Mx05MDrQaVF" +
"gCVXtwTsPSLVK8VwScjPIFLXgQqqZ5osq~WhaMcYe2I2RCQLOx2VzaKbT21MMbtF70a-nK" +
"WovkRUNfJEPeJosFwF2duAD0BHHrPiryK9BPDhyOiyN82ahOi2uim1Nt5yhlP3xo7cLV2p" +
"6kTlR1BNC5pYjtsvetZf6wk-solNUrJWIzcuc18uRDNH5K90GTL6FXPMSulM~E4ATRQfhZ" +
"fkW9xCrBIaIQM49ms2wONsp7fvI07b1r0rt7ZwCFOFit1HSAKl8UpsAYu-EsIO1qAK7vvO" +
"UV~0OuBXkMZEyJT-uIVfbE~xrwPE0zPYE~parSVQgi~yNQBxukUM1smAM5xXVvJu8GjmE-" +
"kJZw1cxaYLGsJjDHDk4HfEsyQVVPZ0V3bQvhB1tg5cCsTH~VNjts4taDTPWfDZmjtVaxxr" +
"PRII4NEDKqEzg3JBevM~yft-RDfMc8RVlm-gCGANrRQORFii7uD3o9~y~4P2tLnO7Fy3m5" +
"rdjRsOsWnCQZzw37mcBoT9rEZPrVpD8pjebJ1~HNc764xIpXDWVt8CbA==",
"AHDZBKiWeaIYQS9R1l70IlRnoplwKTkLP2dLlXmVh1gB33kx65uX8OMb3hdZEO0Bbzxkkx" +
"quqlNn5w166nJO4nPbpEzVfgtY4ClUuv~W4H4CXBr0FcZM1COAkd6rtp6~lUp7cZ8FAkpH" +
"spl95IxlFM-F1HwiPcbmTjRO1AwCal4sH8S5WmJCvBU6jH6pBPo~9B9vAtP7vX1EwsG2Jf" +
"CQXkVkfvbWpSicbsWn77aECedS3HkIMrXrxojp7gAiPgQhX4NR387rcUPFsMHGeUraTUPZ" +
"D7ctk5tpUuYYwRQc5cRKHa4zOq~AQyljx5w5~FByLda--6yCe7qDcILyTygudJ4AHRs1pJ" +
"RU3uuRTHZx0XJQo~cPsoQ2piAOohITX9~yMCimCgv2EIhY3Z-mAgo8qQ4iMbItoE1cl93I" +
"u2YV2n4wMq9laBx0shuKOJqO3rjRnszzCbqMuFAXfc3KgGDEaCpI7049s3i2yIcv4vT9uU" +
"AlrM-dsrdw0JgJiFYl0JXh~TO0IyrcVcLpgZYgRhEvTAdkDNwTs-2GK4tzdPEd34os4a2c" +
"DPL8joh3jhp~eGoRzrpcdRekxENdzheL4w3wD1fJ9W2-leil1FH6EPc3FSL6e~nqbw69gN" +
"bsuXAMQ6CobukJdJEy37uKmEw4v6WPyfYMUUacchv1JoNfkHLpnAWifQ==",
"AGwvKAMJcPAliP-n7F0Rrj0JMRaFGjww~zvBjyzc~SPJrBF831cMqZFRmMHotgA7S5BrH2" +
"6CL8okI2N-7as0F2l7OPx50dFEwSVSjqBjVV6SGRFC8oS-ii1FURMz2SCHSaj6kazAYq4s" +
"DwyqR7vnUrOtPnZujHSU~a02jinyn-QOaHkxRiUp-Oo0jlZiU5xomXgLdkhtuz6725WUDj" +
"3uVlMtIYfeKQsTdasujHe1oQhUmp58jfg5vgZ8g87cY8rn4p9DRwDBBuo6vi5on7T13sGx" +
"tY9wz6HTpwzDhEqpNrj~h4JibElfi0Jo8ZllmNTO1ZCNpUQgASoTtyFLD5rk6cIAMK0R7A" +
"7hjB0aelKM-V7AHkj-Fhrcm8xIgWhKaLn2wKbVNpAkllkiLALyfWJ9dhJ804RWQTMPE-GD" +
"kBMIFOOJ9MhpEN533OBQDwUKcoxMjl0zOMNCLx8IdCE6cLtUDKJXLB0atnDpLkBer6FwXP" +
"81EvKDYhtp1GsbiKvZDt8LSPJQnm2EdA3Pr9fpAisJ5Ocaxlfa6~uQCuqGA9nJ9n6w03u-" +
"ZpSMhSh4zm2s1MqijmaJRc-QNKmN~u1hh3R2hwWNi7FoStMA87sutEBXMdFI8un7StHNSE" +
"iCYwmmW2Nu3djkM-X8gGjSsdrphTU7uOXbwazmguobFGxI0JujYruM5Q==",
"ALFYtPSwEEW3eTO4hLw6PZNlBKoSIseQNBi034gq6FwYEZsJOAo-1VXcvMviKw2MCP9ZkH" +
"lTNBfzc79ms2TU8kXxc7zwUc-l2HJLWh6dj2tIQLR8bbWM7U0iUx4XB1B-FEvdhbjz7dsu" +
"6SBXVhxo2ulrk7Q7vX3kPrePhZZldcNZcS0t65DHYYwL~E~ROjQwOO4Cb~8FgiIUjb8CCN" +
"w5zxJpBaEt7UvZffkVwj-EWTzFy3DIjWIRizxnsI~mUI-VspPE~xlmFX~TwPS9UbwJDpm8" +
"-WzINFcehSzF3y9rzSMX-KbU8m4YZj07itZOiIbWgLeulTUB-UgwEkfJBG0xiSUAspZf2~" +
"t~NthBlpcdrBLADXTJ7Jmkk4MIfysV~JpDB7IVg0v4WcUUwF3sYMmBCdPCwyYf0hTrl2Yb" +
"L6kmm4u97WgQqf0TyzXtVZYwjct4LzZlyH591y6O6AQ4Fydqos9ABInzu-SbXq6S1Hi6vr" +
"aNWU3mcy2myie32EEXtkX7P8eXWY35GCv9ThPEYHG5g1qKOk95ZCTYYwlpgeyaMKsnN3C~" +
"x9TJA8K8T44v7vE6--Nw4Z4zjepwkIOht9iQsA6D6wRUQpeYX8bjIyYDPC7GUHq0WhXR6E" +
"6Ojc9k8V5uh0SZ-rCQX6sccdk3JbyRhjGP4rSKr6MmvxVVsqBjcbpxsg=="
};
public static void main(String args[]) {
ElGamalVerify verify = new ElGamalVerify();
verify.verifySelf();
verify.verifyCompatability();
if (args.length > 0)
verify.generateEncrypted();
}
public ElGamalVerify() {
_context = new I2PAppContext();
}
/** verify that we can decrypt what we encrypt */
private void verifySelf() {
try {
Object keypair[] = _context.keyGenerator().generatePKIKeypair();
PublicKey pub = (PublicKey)keypair[0];
PrivateKey priv = (PrivateKey)keypair[1];
for (int i = 0; i < UNENCRYPTED.length; i++) {
byte orig[] = UNENCRYPTED[i].getBytes();
byte encrypted[] = _context.elGamalEngine().encrypt(orig, pub);
byte decrypted[] = _context.elGamalEngine().decrypt(encrypted, priv);
if (DataHelper.eq(decrypted, orig))
log("OK : verifySelf[" + i + "] passed");
else
log("ERROR: verifySelf[" + i + "] failed");
}
} catch (Exception e) {
log("ERROR: verifySelf blew up: " + e.getMessage());
e.printStackTrace();
}
}
/** verify that we can decrypt what other people encrypt */
private void verifyCompatability() {
verifyDecrypt();
}
private void verifyDecrypt() {
try {
PublicKey pub = new PublicKey();
PrivateKey priv = new PrivateKey();
pub.fromBase64(PUBLIC_KEY);
priv.fromBase64(PRIVATE_KEY);
for (int i = 0; i < ENCRYPTED.length; i++) {
byte enc[] = Base64.decode(ENCRYPTED[i]);
byte decrypted[] = _context.elGamalEngine().decrypt(enc, priv);
if (DataHelper.eq(decrypted, UNENCRYPTED[i].getBytes()))
log("OK : verifyDecrypt[" + i + "] passed");
else
log("ERROR: verifyDecrypt[" + i + "] failed");
}
} catch (Exception e) {
log("ERROR: generateEncrypted blew up: " + e.getMessage());
e.printStackTrace();
}
}
private void generateEncrypted() {
try {
Object keypair[] = _context.keyGenerator().generatePKIKeypair();
PublicKey pub = (PublicKey)keypair[0];
PrivateKey priv = (PrivateKey)keypair[1];
log("PUBLIC : " + pub.toBase64());
log("PRIVATE: " + priv.toBase64());
for (int i = 0; i < UNENCRYPTED.length; i++) {
byte orig[] = UNENCRYPTED[i].getBytes();
byte encrypted[] = _context.elGamalEngine().encrypt(orig, pub);
System.out.println("Encrypted [" + i + "]: ");
String enc = Base64.encode(encrypted);
for (int j = 0; j < enc.length(); j++) {
int remaining = enc.length() - j*70;
if (remaining > 0) {
String cur = enc.substring(j * 70, remaining > 70 ? (j+1)*70 : enc.length());
System.out.println(cur);
}
}
}
} catch (Exception e) {
log("ERROR: generateEncrypted blew up: " + e.getMessage());
e.printStackTrace();
}
}
private void log(String msg) {
System.out.println(msg);
}
}

View File

@ -1,4 +1,195 @@
$Id: history.txt,v 1.101 2004/12/08 12:16:17 jrandom Exp $
$Id: history.txt,v 1.125 2005/01/05 19:17:53 jrandom Exp $
* 2005-01-06 0.4.2.6 released
2005-01-06 jrandom
* Added a startup message to the addressbook, printing its version number
to stdout (which is sent to wrapper.config) when it loads.
* Updated the addressbook to reread the config file periodically
* Added orion.i2p to the list of eepsites on the default homepage
2005-01-05 jrandom
* Handle unexpected network read errors more carefully (thanks parg!)
* Added more methods to partially compare (DataHelper) and display
arrays (Base64.encode).
* Exposed the AES encryptBlock/decryptBlock on the context.aes()
* Be more generous on the throttle when just starting up the router
* Fix a missing scheduled event in the streaming lib (caused after reset)
* Add a new DisconnectListener on the I2PSocketManager to allow
notification of session destruction.
* Make sure our own router identity is valid, and if it isn't, build a new
one and restart the router. Alternately, you can run the Router with
the single command line argument "rebuild" and it will do the same.
2004-12-31 ragnarok
* Integrated latest addressbook changes (2.0.3) which include support for
deploying as a .war file with no existing addressbook configuration.
* Updated main build process to bundle the addressbook.war in the
i2pinstall.jar and i2pupdate.zip.
2004-12-31 jrandom
* Speling fxi (thanks digum!)
* Bugfix for the I2PTunnel web interface so that it now properly launches
newly added tunnels that are defined to be run on startup (thanks ugha!)
2004-12-30 jrandom
* Revised the I2PTunnel client and httpclient connection establishment
throttles. There is now a pool of threads that build the I2PSocket
connections with a default size of 5, configurable via the I2PTunnel
client option 'i2ptunnel.numConnectionBuilders' (if set to 0, it will
not throttle the number of concurrent builders, but will launch a thread
per socket during establishment). In addition, sockets accepted but
not yet allocated to one of the connection builders will be destroyed
after 30 seconds, configurable via 'i2ptunnel.maxWaitTime' (if set to
0, it will wait indefinitely).
2004-12-29 jrandom
* Imported Ragnarok's addressbook source (2.0.2) which is built but not
deployed in the i2pinstall.jar/i2pupdate.zip (yet).
* Don't treat connection inactivity closure as a connection error.
2004-12-29 jrandom
* Add in a new keepalive event on each TCP connection, proactively sending
a (tiny) time message every minute or two, as well as killing the
connection if no message has been fully sent within 5 minutes or so.
This should help deal with hung connections from IP address changes.
2004-12-28 jrandom
* Cleaned up the resending and choking algorithm in the streaming lib.
* Removed the read timeout override for I2PTunnel's httpclient, allowing
it to use the default for the streaming lib.
* Revised ack triggers in the streaming lib.
* Logging.
* 2004-12-21 0.4.2.5 released
2004-12-21 jrandom
* Track a new stat for expired client leases (client.leaseSetExpired).
2004-12-21 jrandom
* Cleaned up the postinstall/startup scripts a bit more to handle winME,
and added windows info to the headless docs. (thanks ardvark!)
* Fixed a harmless (yet NPE inspiring) race during the final shutdown of
a stream (thanks frosk!)
* Add a pair of new stats for monitoring tunnel participation -
tunnel.participatingBytesProcessed (total # bytes transferred) and
tunnel.participatingBytesProcessedActive (total # bytes transferred for
tunnels whose byte count exceed the 10m average). This should help
further monitor congestion issues.
* Made the NamingService factory property public (thanks susi!)
2004-12-20 jrandom
* No longer do a blocking DNS lookup within the jobqueue (thanks mule!)
* Set a 60s dns cache TTL, instead of 0s. Most users who used to use
dyndns/etc now just use IP autodetection, so the old "we need ttl=0"
reasoning is gone.
2004-12-19 jrandom
* Fix for a race on startup wrt the new stats (thanks susi!)
2004-12-19 jrandom
* Added three new stats - router.activePeers, router.fastPeers, and
router.highCapacityPeers, updated every minute
2004-12-19 jrandom
* Added a new i2ptunnel type: 'httpserver', allowing you to specify what
hostname should be sent to the webserver. By default, new installs will
have an httpserver pointing at their jetty instance with the spoofed
name 'mysite.i2p' (editable on the /i2ptunnel/edit.jsp page).
2004-12-19 scintilla
* Convert native jcpuid code from C++ to C. This should alleviate build
problems experienced by some users.
* 2004-12-18 0.4.2.4 released
2004-12-16 jrandom
* Catch another oddball case for a reset connection in the streaming lib.
* Add a dumpprofile.jsp page, called with ?peer=base64OfPeerHash, which
dumps the current state of that peer's profile. Instead of the full
base64, you can pass in however many characters you have and it will
return the first match found.
2004-12-16 jrandom
* Remove the randomized factor in the tunnel rejection by bandwidth -
we now accept the request if we've allocated less than our limit
and reject it if we've allocated more.
* Stick to the standard capacity scale on tunnel rejection, even for
the 10m period.
* Build the time message at the very last possible moment
2004-12-15 jrandom
* Handle hard disconnects more gracefully within the streaming lib, and
log unmonitored events more aggressively.
* If we drop a peer after connection due to clock skew, log it to the
/logs.jsp#connectionlogs with relevent info. In addition, toss it in
the stat 'tcp.disconnectAfterSkew'.
* Fixed the formatting in the skew display
* Added an ERROR message that is fired once after we run out of
routerInfo files (thanks susi!)
* Set the connect timeout equal to the streaming lib's disconnect timeout
if not already specified (the I2PTunnel httpclient already enforces a
60s connect timeout)
* Fix for another connection startup problem in the streaming lib.
* Fix for a stupid error in the probabalistic drop (rand <= P, not > P)
* Adjust the capacity calculations so that tunnel failures alone in the
last 10m will not trigger a 0 capacity rank.
2004-12-14 jrandom
* Periodically send a message along all I2NP connections with the router's
current time, allowing the receiving peer to determine that the clock
has skewed too much, and hence, disconnect. For backwards compatability
reasons, this is being kludged into a DeliveryStatusMessage (ewww). The
next time we have a backwards compatability break, we can put in a proper
message setup for it.
2004-12-14 jrandom
* Reenable the probabalistic drop on the TCP queues to deal with good old
fashioned bandwidth limiting. However, by default the probability is
rigged to reserve 0% of the queue free - meaning we just aggressively
fail messages in the queue if we're transferring too slowly. That
reservation factor can be increased with 'tcp.queueFreeFactor=0.25'
(or whatever) and the drop code can be disabled with the parameter
'tcp.dropProbabalistically=false'.
* Still penalize a peer on tunnel failure, but don't immediately drop
their capacity to 0.
* More aggressively ACK duplicates
* Randomize the timestamper period
* Display the clock skew on the connection logs when a peer sends it.
* Allow the timestamper to fix skews of up to 10 minutes
* Logging
2004-12-13 jrandom
* Added some error checking on the new client send job (thanks duck!)
* Implemented tunnel rejection based on bandwidth usage (rejecting tunnels
proportional to the bytes allocated in existing tunnels vs the bytes
allowed through the bandwidth limiter).
* Enable a new configuration parameter for triggering a tunnel rebuild
(tunnel.maxTunnelFailures), where that is the max allowed test failures
before killing the tunnel (default 0).
* Gather more data that we rank capacity by (now we monitor and balance the
data from 10m/30m/60m/1d instead of just 10m/60m/1d).
* Fix a truncation/type conversion problem on the long term capacity
values (we were ignoring the daily stats outright)
2004-12-11 jrandom
* Fix the missing HTTP timeout, which was caused by the deferred syn used
by default. This, in turn, meant the I2PSocket creation doesn't fail
on .connect, but is unable to transfer any data in any direction. We now
detect that condition for the I2PTunnelHTTPClient and throw up the right
error page.
* Logging
2004-12-11 jrandom
* Use a simpler and less memory intensive job for processing outbound
client messages when the session is in mode=bestEffort. We can
immediately discard the data as soon as its sent the first time,
rather than wait for an ack, since we will never internally resend.
* Reduce some synchronization to avoid a rare deadlock
* Replaced 'localhost' with 127.0.0.1 in the i2ptunnel config, and special
case it within the tunnel controller.
* Script cleanup for building jbigi/jcpuid
* Logging
* 2004-12-08 0.4.2.3 released

View File

@ -1,6 +1,21 @@
; TC's hosts.txt guaranteed freshness
; $Id: hosts.txt,v 1.88 2004/12/06 17:13:41 jrandom Exp $
; $Id: hosts.txt,v 1.104 2005/01/05 18:16:35 jrandom Exp $
; changelog:
; (1.126) added j.i2p
; (1.125) added bl.i2p
; (1.124) removed jap.eco.i2p and bt1.eco.i2p (obsolete)
; (1.123) added chat.i2p
; (1.122) added phonebooth.i2p
; (1.121) added up.i2p
; (1.120) added dm.i2p
; (1.119) added piespy.i2p
; (1.118) added sciencebooks.i2p
; (1.117) added forum.fr.i2p, fedo.i2p, and pastebin.i2p
; (1.116) added frosk.i2p
; (1.115) added theland.i2p
; (1.114) added dox.i2p
; (1.113) added amiga.i2p
; (1.112) added frooze.i2p
; (1.111) aliased gott.i2p as jrandom.i2p (ed. note: no, i am not gott)
; (1.110) added sonax.i2p
; (1.109) added 1.fcp.freenet.i2p and copied fcp.i2p to 2.fcp.freenet.i2p
@ -128,7 +143,6 @@ nntp.duck.i2p=5SIb-1I6SVwFlW~XlYTNJU1NCpbyHJ3co8KdrpDxBosnHtdsZzq2qzYzGDfvkKK3WR
pgp.duck.i2p=kRm2KRa2EiWO~XQFYxSg6UM9lXYHZ93IB80j3ShFhJOOZ4AN05BrTGjMeT9nhn5n1LMEUhy9HJuN-Lhkd89Mbhufk7di6M40wqySns2g0T7XUCFUgQ8kl4~BoY8M9U0pHpM3RJcCw8WJEFGEk~fX8tgC4XQB43UIXfrdKTlNfKhxZODE4UdvlFfFdOYMgH53x8UgMZUprn~URTRNg1uwcaXr2luMwts6HnDt1bDd8elitsWViOJiw42yAFMqBnf-7mhiTCsoYg-nsOiq0Jirt58cBjAGUL5ujZo~vfXkLslDKjHOP32Y7HIi5ANEsbbRr~8QrVUojnVoJwFXs8BQBTevRpGLkCSLnKa31jNSt3msOnEP-n729Jabwj8o3pdRk9e-3~~y9gfj4bcmpSH9sOJoqmipXDiqOvv0tL9twERqse3tAwjE1NgXTvl5e2Zc~F0xJ-L2aG~6BX175ihjjEiYGWRYaoEisHMZdMtivsAK92dKl1JVEkuDF3W8KjL8AAAA
scp.duck.i2p=AE2Ff7x-tJMI901UKEEXkcwb9~5KZKs--VBXEoZnnpC~mlgnqGsZr8MBvRvs6xAzP7xXLeL~cHQ~gvPFT60obEBitwH~JcKMahhJDGb0p23Z8B81QajXijypDpVfMDfFbMiqQGctXhnBidNKWe7HQEcJGZer-SCu3m8CiftcZ0t0g5Y67B2AtqLhza5xfepq6FQ-Tl6fpgS-UDcGUAqMpLUYfrBFB11oM09TRVsfNp~NIAvMdrvXiX8dTDUHJM4FRdpV2OsJiyDdgf5-W6s6ssxohviT5MdijUSYQw5sWj8~9Xkv~~aa11Dvty0E7K9IhXAeJlwBe3VuDizsAJGnFIU7PwZXV7-9-28Zgldarg1P-rh2QDvCqF1vjdyZe99BBcTiCqvE3zx2N-9eT~FeOURrgE~2rFBKVBZfuAASSORqiXIeAANKklCW2pGQrM3DkX8ybi93Weg~eBjwGQugO1FRZ-ISq7npRWruMiC7f~fWLqkmRUy9HahPNp8E41s9AAAA
squid.i2p=8fiWZbRjOEzrj5n4jSqjN9UN54wTrsgEjqn7GRUQpLx1svf8lwckXPV5buP2VEYGo~83ftkIcDKyMLXkxSr8jqbb4yAEgPe2~w7OT~8LNvmVPz0xZhIO6fiw0WU4xD9x5PG1spYjWPnLFv7pynEvBpWFXaUlCacjWL2KkfViiGPXvveQqQIZs7VkxVD2HK-oT4yIjdqHpc7Y8nEV9xwds3-LX6to5p70jFe~kZJA2fjWHsSCm92TtPvoR3aTlL1VS3JUKpcH6BL5irsh-SKODEtDRCErPQI~j2SHzhcD6dMUsI7bm3AxivjFpSQHqyXLmLVdxECYsMET~nIHmuv8NYTHQQ0jM0XTQzwnQwEHjHRBd1~spR9uS2~LSnX4Pw~X1WTknJpPK1f4Cu1O44X4RYcLRCsxpEzytUBXA4BQizrbYgOJVGQa9-PNGxJeZsnNUZ3PxUi23Oh-c4jUaB0ZjyKTWJSpzj1GI6vc-gW-0ixGJ358TCSbKgqdBv~g~f7yAAAA
jap.eco.i2p=R5rLvDIz8JAHgR7tf6iC8z7-ugctL23ipkNr~3SNCTxzdggCndToz~kmVACG5mGaoNf--KZsIKM2pbgSci45cJ1xAipx8CAaC3bbP3paDAY01hnxBPXcMN-Q1bisJPQHjkCphLRZqIh7gb1oXxiTtXU7erqkdAPXg6kvscFiblv5JxRg4B3Y5HHJ91UEb~Bc2oIsESQX3pCK~zmhWlReYROnoenaa9DDP1b6Na3SOAhgECR3PjoneMco1qDqlndb3C5wJ7X9YDGcZaIE~N6c0erNzWX-mGkEGARVLjyhtsOHMC72CROehAB3tsjwneKFojUpGQ16xcHGX4hEKFLNPztAbvnT7fg01dfcQb01YzIuBxbbjsP8nnoLJbEpjOaRrdIfk0cXm3aE6AQt7LoKYA-bW2sxgBxEAKsjcj4rQ57SDAi97FjnvzH2Xt363eHt0Jf0CypyEO9ajQYChEeMj6ISyVGe2a7wNJutq7aAx0ilbYB3Tiq0ExrbEsmXVvsOAAAA
fillament.i2p=Pa50z7pU~ni5nWwUdaDZ5CJxG0fYjoarm9wlxnkgX~wHMX9RPgQAXz~r0Rr1Nadt2OA~dr9RMHswrMok0hutK3JZuFD707D7FjmWW2w979Ee9I3zxKyx9W5A2eE49PPT131NLa3uINXLXOYVA5frfDOmM75Dmvm533r8e2kloemJyj22HpvRiSXiQYgqYJGDMH3Hlnwk884eRkQu7P8DJL~hcuKpyY0FzLZtfxTNsdSavGjl7rKPMzJeP02-9TS5TkdHokZrstVM5Cn9ay1c8DQrMds7SPXJy13Ut34QRjb65JxRV0mrnY3teXewW6QFvFMXJCsf5C3i46t-9Fufy5D1H8cSd2Tx~Xl71MC5-1AJCcIS01Od23E9tFY3dU7IOSRhKC~FiAslyk3x-BnBSpKxbgl1w~LArBm5plNiCiUemJU88xYdn1UyukLer~yNrEHAWspckCRkXFwmUtPkaGNTvfwBSYns-skNHSd7MAUUoS-ewStBdmtnDgRkwSG9AAAA
eco.i2p=KhRG6BGxVPh-BUDDfIgy0570cppTdighytcaGVR0HzQo46tgRMBp9Shlpax5FQX4nLHn6qHQbdFFpFbAwe7CiDhURCVF9-CxYmPurGadxlMPHMjz9O3jHX0CQiv2iULsk4XPrYXF3PqBc4t1J6vVyBVO7uTUhDi0gF6sN1Ro-1GLcWcsoR8Kx-hb~Z4WqGD0QAROOBPHnSRSb236qVBkhFvSkfigfBq3jFgEsttadYJA9ZLSUj1XrFFRBjz~xkRra8kJQSZl5dbfg-eZMlL49h61U6Uta5n1~tL6sarmnl9CaTl2Qo27SKB1OmMLeZEteA5G0-~LiOjN0nxaKpwrCjKIOyvwbQy2QqE-GEb9m8SN8nC2bwYK9fH15pTMHY8GvPYGcUukbF6RhefwzkEJLZ~PaAECrZYuLsn9KE5C35uRnlWJiuJlJ25hG7da5tFMyDB95efzq5IMxPeI0pMigRfuVfRSaGDpNos6JxjfEIX8umk3jIJUPhz1d8gP4QgrAAAA
aum.i2p=09hSo56PTtkFLUEt1iUTO7zYTnO-B~ogsIsyyPWif6q1Iz4wz4JoBflAWZtedPmwGmH0nly4HYUS0gAADoUmBUnXwemmO6dxT-hPQkfEW-A7b3uEvYQWIN~kyFyg0Pa~FN6TaD9kGFttBN-GE4wxiHhXmWdzWNDVb0q5PVGnxMm6Jleik8xkd2Lgeexze8rIv8LCocAWx074USVkbCVQwoFi2P7EnjLq8odSz1cJAntbuCFeUZcjbslE3qmlcTFMCNCZXWKVzn7d5m4oszCQ83NidgekwxJ-S~iS6mBwIS0XKI--4iXiKXzzCFf0KtYfEWpvKCuqNJOcU8vQWAA2-i7~K28aLPzccDQn7acXWLKRTXF3tf0i6e-lSx-X6WTSWK-fuNitjAtKu~jqO10d~bCk7y~UPL-XwdH1XSTbk-Phhk7UoBTDiHY6zQHdD~iAzXER~9JXsJ4UoIrGFVabg7frzSt82CN7Ek5Li4AMz5gg3wq9H9HUa7xM5QfGIJpXAAAA
@ -145,7 +159,6 @@ mesh.firerabbit.i2p=BLt3pciNQcVIU-IGnTfSsrputh~b6drZpc1vH8qeA745XoE~nMCGtw8S7HGY
chess.fillament.i2p=8xvXLwcBYu2MxqMAoG6DIahvkNAwBQs43kdZTg9SlouzC3JSQ25RHvspbrxWoGIVAlB~XCxVmBB6EolYEHoSZv2YCziOj4UVxEbVP7nHkh32-7Uc5T7xlcjk8-rsmZzdgz9NhxKVn2Uhp3xtcdVAiyG4mpXisvK-7NgHY-mXPNvj6goaH58roeDUZ5otJN7nCFmcRAUpaUk-nlQs8YfgSCCEsWKOWhsVnAaHwtqtDlpdTo1YKooACMRSa-DcV5W75Il80JEWBD79qpSAeONGAOMMPT~VEMwNNg001VG-UZbvyspZdxHaETw2yd7N9l3-mwI-sYcveTTnNXLWO8IjdgDLdUIP5qrIW6WS9XZIHRKqT2kpwEw7xsEgCA~qSNiZWeai8n6Zs0rLmdyeZeafVEEW9vr6CKcLZ5W7i1SMDqCKnzSbZdd2Hvpb9aWFf5foEjLt33n8w2KSaCUe4zmN~xuQMq2yiB-vQ9~5fgPmphlMxo3ca5MTCfbhshw5137YAAAA
kaji.i2p=i-nivH~36AgabgzvS06oKHR~MLoy6NA0oSv8JuNJDLZB8FXEDzIiyzWISmw9XJm8I7QqZ1yFH8~xe84STCHHvMMIQQrfBmOUODLWbKZor~~KhiHdNLtfVZC5BpnXkBCJkklj~fMYSpWa0C~pVRrZl75RoGtBjDVP9P8hioTv5B6RC86g2ypBH5r093rY0wnzxSL8-ZuV3F~H48VYbqro8cRlbMmjx2oEsSHkDpQyjCMVkIYKaCijkSArqZTG~zX6op6Ng9CJwdrkjKSsbzjV6MLnE4aNv-jm2WaGGD5pR24h7e3ImDOGAr17tXRtmNX5ZEQ1udQp8qIhd8UMUumrnm962r8KJWK~9WNzcVeqDrIxaaxC7vcQmXxoPeEW2efbH0yKhVZ7OFu~I9cAapSe~aNWp9UK4URSpuJvOedt0axp3ORaaM-a5U7noW3Ao-HB83qfFEPU-6uUu16HNiPqCFMJiA0qODTOwHiyyx4HKQvbhjujh4mmknSbsuapdgR1AAAA
human.i2p=ji02vZzrp51aAsi~NZ8hwMLbr1rzMtdPUSiWAU94H89kO-~9Oc8Vucpf2vc6NOvStXpeTOqcRz-WhF01W8gj-YLP3WFskbjCcUwz0yF8dHonBeC4A5l4CjupAaztBSMbhu4vyN9FJkqZUFN01eZbQ9UqgXgLWMp4DtbUwf78y8VrzdAfmUOrVn6Iu89B~HUfOAKnpIlQXyGsQk1fnLw3PzDo2PVi8Q3C1Ntn0ybovD1xDKPrrHliTK4or2YujTcEOhSBLK4tQGvouN-tWqcVoF9O814yNGtze~uot62ACGJj9nvEU3J7QPgOl~fgBJ5Hvom0Qu-yPAGJuAZa29LSHnvRhih~z~6lWZYHREBYXQ58IzKktk90xJWcTwlwRRhyO-Sz3A5JYR3jM97h4SsoYBVrjK9TWnvGKj~fc8wYRDzt1oFVfubLlT-17LUzNc59H-2Vhxx8yaey8J~dqdWO0YdowqekxxlZf2~IVSGuLvIZYsr7~f--mLAxCgQBCjOjAAAA
bt1.eco.i2p=SUHjD4QvbuY5VVTGTm49U8B~DtvTS2bwO1lREMNfJtZU6rxa4tgdqaIpUjRRrbZYLxZcgxjUYx4Bq7gcjriETIRaMy7lQtMx9zFk~XLE5baTmZeXc1~xuQCCTelnYv0yUswWSCZx7ll2a-Y6d88jvTydfxcCTNAYclT~gHnA0kU2kRHD5vgEteidiXFBVer2Ps68tnARUqURDKxTRyRnWtrxwQocmP5MJG29e4dIptcecxX8bKhgqgONzPAmYzAR7F694wEOXJZQO67ro0YQEuQmDXICBI8IwkbAt8qM~BG~JF1H26iWblWWs8mGJonwl34-CpVjMSXWVk~SC~60-xr97CsWSGAeZqrD8pLJyDGFsJ0jNFV0L6Q97ryUViakVwsHaAMlxZ3Hh7KAc6TUcJEGvuygRJ2DKeKA1wyuUDFc08m40HrRVwydn0hwzs4Qfb8hXfGHQ9-yauK6Gk-HvYFUL9qcIrOfrZGgq4xLVdgVh2wB1mIOTkcBMMti4941AAAA
gernika.i2p=1D9ee5J04ZI7pvCwQ3hXQMpeMvbNw9cz3V2pioQ9LakwRQzfMEb9CVAeiFt-wE4HyTFWKeof5rz8F5vmIqFMaH~oMJSkCyNtwPfqiRAENeNeALHbY2plMPfqCfEFR4GkTqXnalAkJGqDjo55CokUfalEVTGMvNiv6i6dmNvwS8~0X56sXIaXgLKuGIK~UNOg9hT8A~uEGQWXwTKD3EDmECsJL40iYcT1UR9rffzuyxOvDhL12HbJ8bIlUGarzEscH-jolj5ShvZAbEyw-MnVzR9LiEqy7DaniKpPtC0oXRZuz7PpcQTqzN-zgQaLq8bHTx7NHIfTuA4P~hhz-STO4SjPot7h~Gbdglc193OmGlL5QwbfjXfdOIccBDh6~jtFaa28GxHrTMoi9GafjnllLfWpvynN1y5H7Jh7Uw3E7KDtBGVsDg9-btyvyQLP3kkqPfIAn7Oj6ePHr9u-TN9ZwDbWj~QBmXXutsE~lLu7aT7kv5Jx6PFmLEeWPib82UuGAAAA
www.aum.i2p=8x3TYbh9aq6EVo8rSuPWBVSrwD~yS1al6z0RZYvRaQFXL9hFUUJYJNL2n3oR3tBg7Zr00MjqntIuBMd20LUKasnklTp4hDlDCE0KfeftYh~bFGykMRf0yTYEWaMHYpIRBY-IJEvSVlgHe8E4AWLMv-b6VKCDZ0~0AdUrsHQ0Qb4NQ-igBPZfU6c~UU0tUVUl1Efsuz1CdAp5pw5RdPviFtPH4tMvUca2t54Rwa-6v6QCqFZ7S2awyhAa73Zb9YlcqT4hP1JHF0wR0rL-OEoJV0gG47Co4Zr903SNW6cgKDj3lW1tIpzcVMoH3BE22SMEVjYyEHgAORdoYwaj19CUg1slDGmvUCoq4dPsnCIrvV7N0LeoUkZekt2pvr~yCH47ENV3oQYpFVLcMLN82tzI0ZFz5IyBHWGr22vlDlT1C-QVhAYQKN92XubDXSEgrhWv5IHPB0h~EgZi-rDcsJG2zb6ZqjtKFHp4CnNvTUxE1cCJh0aR1MDzM~o0iSMiMqh0AAAA
madman2003.i2p=Uilou~5268x~UI5Cjg3XYKfBCeKVEMuD0g9Ea7~j2aNXbczbYXCdRQEetYMk0919Dyyj5Bfpiz5m4~xHMUoXs1aJedteFjvwwOHcXE1IuhORhILv8kKyW4zBYrK7LUnvn~xFsnl6~xaThgwxneXLGhKz5UOitIzDUJmxCAwfcARHgx9PIUuJ5LTninB3Luyev11TIqweOfe~X0dzVYSmemBgaw2T6dxCz7qz0mN5fJe18~qnTqJibKT~7teX~hQhRQolk38P3tLeBRq3wZGTRsJ6biGEydTbE-rqKrNvIMNYvUbhhRHEMdShAiUo4gPJwbyCRRScI~DbluYuy6iXXURnrCIEv2DKtfpHSS~JccPu25HKcpNBfCLzQye7L8hWgjAyaQoTi3idW2JJNEIBBBS4yfGk7wq9swlzBK2aanI-JzFcDrVDXd~fpN0JYN-phF3YJnKspJe2i83BW8J9b-y2JJVbTWBwU2mIEE~gXKggsCvqESfuKK5RhgFaS4dTAAAA
@ -248,4 +261,20 @@ asciiwhite.i2p=kJJmuP~sE0SrQFX-nyV2cEFDffnqv3D4-vBNJX6wqEq7vbTjQzZVn3jtt5kYlv4Fp
2.fcp.freenet.i2p=yY8ahms44MDj2cdI6o7Fc2ozz-jM4ACEp8t4YJiRsKgEYx6XNB7QrNK8jZQoKIQ8G6GMmKMYsgRGmbwWFt60au5HFNv56NWsB4Nd80G9iSRLiEHOHhaUSFnR~ZnSdzL-nbMqA8-vzteaqZ8HNnC2NNjJXsMyULFdkBsIKYS0jsS5hl7ddf0v69X7sCSp1CCy5H28rmZSEtKA4aYXGcBHJ8bXFomV5ovy~Mh2xZzWdeVmjdmJACG~ekDBcMkm7G5mlYoTN2aEBghxiHv~plGlHFqERB3lgISlomYIwYwPqxcmpxwbg9OSEo53t45P9~dat8XMvabzXiUdm0UgVnFsNBAIyNgyTEktw38~5v2XHhqpn8RSoMUeNOqp~k740v9fYH7xxHYCrfN7y3rKg~~GlWekbNmX5g0UvKjV-QWHIvrdOVZmII9qNTTwm3Uf2Tyi2aFll7TF9CvKUJCI0Mt1BpQEmPQdY~FgFXNd0p-~DvlRrwaI4jorbn4eUUEJqgzFAAAA
sonax.i2p=s~FYxHeR1n0aN0kuilNtJU9IbXRX~jWI6vGz4TUf6oZyjZBYYmY9OpgDa3sr-8AERqsuG6EKA~UBn4KiQdkI-LU16DwEHvbgfZXAdwY73G6SkHOgwVAnT2SIOuh90v1QevYtxiGpA7IVm6icoz5V4I6V29hPlUNJxBFF~Qn2cY6zj0d6wBk6ulYc0vbyOwu8Lfc8T8bTnBLut3SZWgKHYLbxabcXJtcQpoN7k815N1MEUi0APD45o5TwPhvpHLt8fF10aNcMScpL01RR~tKHgC6jPX~HKM~Ld4l0z0lvLeq9197Qp6zHRvRD3YUQxyvgHi2ANkYprvJof1Abgs8TD1JWkZUAzlfqFPyjO~f3pJSDd5eM7E0Fk69ym1Y4Rltih~Y2N1IVtJPfo9Tx9a3RQWOJZ0MN12xfiZjyF92gDRgfKW5RDpwCoTVmP79u~WUr8lQ-qQbKhsMQQqMn8Uy8DeXvKLRpHaECtevjohL4lWBBR0ezXlBLCXKqaVXKcMTjAAAA
jrandom.i2p=BxciUZkGQwi6Sj1Ucg5GGagDdwujlj8ClePa4~3d-1nnRJsBhTNJtvs-UuQYY77gPGNlpNk9dt02mxeS7f~pEC4E1KxJH9mhnf0OlIGB4hOOhDlXokAaKE2u1E-vVCDJlZCq32r~Ne53w-L2m5h5FAq5Bx7NXrTzWAEPgAlC8A2wASJWIF-EKOX5kfkkYoF6sKZqam5CxAAAMAUwmnbD--7wo8d2mz0C-c~oE7hzuHsg2J8yME5Zd~-FOZUxkoNCBJdfrVlVRn9~6685zqpaotL-SIqFK6~28ZCNMNCtnZcZZLeG2gZxq2pe8HtgzDrMMSx45vs3JLUp8Bap6~D9oFX0o7k2DQm-5R9D5NWhsJgol5Ny~8ABTXBpW~WQQOuKxE5xJauXM5rQia2yyXgldfOyGjxYnkrTGu6LaxlLmhyAva3ZbOO0mtLvrKLZebLKCYUxP7~TtNmSWEAzPKFYbjdZ~NjE0q4TanLFBaWotefPmy3IuAc8Mr7PbCvi4GmdAAAA
frooze.i2p=kyjnpna6HHapU~tLQXRCHrX6Dpb2YCMEtFTloKF298480axGCZFY-WtpjW2Mo1ci5FlE7dkmEQ1PU0GfHZ6s0O~Hert5AcZpKh5nxRIMtS7uc~oErYCiX0yQp2P9D47B9VU5D2UmjYKWovqbrbKEAS1F0NDxXh65Ppnv-oBHVksLp7IrKLU3IOesdYtGcc9cSYJB6~mdbtqXxgHNNLTkyouSJKgSlJoe-tk1T3eYEsOJ6vzpeS6pNa6Px2dknejT~aVeNKLhjsXy706g22Mbl1-zCL87OE1bL2qWQbQN43qpLwCSv0LbRovO3r6iXRWWWrKhYj5nti~EQEWapJ2OL26~ElrpD9V9gbUVLZoxeYuu~-GKKwg6CvZYKtdf6YBYTZ0Ef4PUcv9faSqp7S4emcGOLQnSW9LezXhGhn0OPaAO~85nhdY~OfXDZ2Dg2tYYF-D70SzKycfHFLgKBQ9fkCxpOP0MHhhgGmKHabPvDHyQRFppcKLN6AYmUH1Lke-jAAAA
amiga.i2p=-lqgoqA5KcJCuGilYz~cOBK3X7MI5VFBK7UwpSEwqbiQgSe340wABmE8V-tX7xH7rjc4Z2rGxqas5NKuOL3pCJozeKF4Z61d05nCemSXbLrqZvJUkGuZJfUz9lzeZ5OGO8L32o-I6DIGIUgFk6zRpiRW0lqO4lzV~fbiHacDhUXPLBM5gYjvZ319MLoVPp-YWG-STJFhjmAnzvj~8h-jJXHowM72YsWPo-B~oD1YtcAcXkpxVJOaLlqQq2yfjJv7YgIde3tqQ6drH7xmHdG~nF~dsu2dacqqt~HjDiUnPd12vJEhR18DDkVJVhDUbnUCcI83bt5RDdsMK8mSKRTie3h1sUam~1fLr8~gnEGTFp-yivGV1JkeleElR-rTJC7PsrlPCWzW2N4l1IMzRGOedJNAyqeXbkfjHtDBd58iZPugS5BZZ~u5MGqXgfu9TYFnLnTIGuMc3a2wSpe3miMvf2PKFnM~1nzStN0zEjFwhn4tZhOWA1TfTH5HMutqrxGxAAAA
dox.i2p=ygf2mdGRnU12BNXXF-26OFhfPpAMigwasf93JEHVYEICVmz6Fk2BBKiGBWL4YNFUtcFo7y1iG3ms8BdmTBBkgCgEasuFZNzphEMgcNYi0eYAg1QLNgmbr5HEDpBNuXIV7fOpxYn6iij76oA09e5opPNdOLUk80kWrgrlJTel61n90uzEzz46fWIbFEw5K8xR-8aqLgC2sJDzGj8vEBY2NDKmCtjRK7aNBUayh~8vFfJyo5oN0gw8b5cOCrfSoxzqdpzI5CJ7070GjznWbWh~pueKWF4CpHn92ll3jvyd86KvUwdQh1qr1B-8fk6Oh~M9l5HRzqdUsSzp8SiG7WvLQoXrNdVX2Sjw22xOZDr7kOfEHZWYUdmrcYyqNLuIT2vgMFXdvCZnLhhGSaihkzuFqZv-34oGSqGDVlCanAPC4v6492IRRK7RiYnUo-JPhuWgpg9CdFTZtULa~drZbWNCZvAekn44D42wDx5V5vAJxuRoyir1s4gAnXHL7n9r7xdiAAAA
theland.i2p=Lf52G95rF1kjlRu29NMpPQ46Ivd2Nz-sdIhvx5D4qU3Y3MPs50GNTjg81yJmUgbg95Q90Eyf~SHsgbzdlnjr-qQEZOXIgLQErq2UhUFGwUX6muqkCYYDIAZ-f7EI-yJiU-3~KDwWu77z-8XDjaJIf78tNV6kN3MvRwUSbBRLD7pAa3HtdhNnNyO-PPBJJVMxorQ-~AgEhmdgLj39WfgetETiRm4ew0NsN6IOHWn~~vh26FJ4awhLpJ0fCBD-fPve~jTbCovjsszKMBumTslvQS~p-dfjfQSTMRZAE3fs262NjdEwoVZP-k~~0HmzOmV9M-Oe8yez6ORDKm8sDyeDoQ~e21b77SiE2tNrgHeB3siITcZoTNyICX~ZSG~Qnb4k4kXdllVkqmXaf7k0pYE1yTKXM3sKphP~VJFMBgdSzZPprhp6RGjWibKy~94Ns8IyCA6c08810yodEdoeU53acQPp~wk0FP1HgcF3tVggk-wHg65cD09ncjOmly5Gc7uZAAAA
frosk.i2p=Dez-OBQLhsI7Wq~O8YmYYJjl~IPvqUCZbLIFVMnyyqOCG9TEO~kGUVpWVD5nF0B5fE8kEWq8CuGKeYbVfqt0Vuz6Uvvo4gCdGnthohthbolIOZ9ZCci9MTNE6t29mJ2thHqc9Nw5TvYJGcvCVNi569~o5PDtYhZjVsOtrv8r3f9fUcUV-wKokD-y0fdF3Dzw4I7tJ6pfohj4avzDwwndAHXnNjB9mDgQsfjPF2KtvWLzXyAsCmfCoanV65tkc09ZX4x5QkekdVgTbD0BcHDP-o71MVufRsm4~ojRkbdSLw-VlbEcYFyeHTPBTpM68gTjueJlRwmvpXyP46w~Xnl9IEDfWu92CV9xjRt8qDxiyQ9jXR6dbWLptTF2UCznkBWvWph7P2wliTiawt~1bWGw2YvkfB6EaENYvDNY2cGn0GxpPhL4K19slEwJqX9f~EzLKYx4xaB5I8~W9syJAJSX8PsJbUlk6OhbbRoYOndd6OCRqHRUyh1w7dM~LvbY0W8hAAAA
forum.fr.i2p=glvyM8eKBTYnlwEl3hVWDRBvfIfe-utzjGAlvDM-rwB4tb4CaM49Aeb5k0lxm7IaFjkp0u9t~1a2KIxwrU~Mj-JTJPKf0TrElVfOT6P0sVFOD3U1wllFwne5e7~0d8MeuylMrBe3dlz5KSPQ69stpDTbJgxcynoKiPMWcbmq~yQjnIvtDIpAPaB1JVYNo8mDhqMqg8GLrk9ttTHO90dYUpdI5lgxB6vR1nuPPwyFczOjVn8GvmT1DXGNoc6yAW0gCLuk7Hq-jNcNtXkNRC5TTdNiBLdLpsNMD3kKWDM04fODM5N~qp-nww-lEqfuZ3f1RT3gpjDwpMMAtKbd~VsRaCEINL5iBaAlZupW6fYTiuGUUdOZ739JK9GpQDsPXBOy~VSoM~cLXJCih12v-GDODDN0i8sSkqZ7CXDGg~jkg0ab2Y-Y2JnpYFLsgYziPtNRij1~rMCcC~-huiSfQio15gxczb~RRS-lpBQgyfaZ5JgIR2uVjWWe-uwKD-WquuIPAAAA
fedo.i2p=pklxM9~hpluoCoiPgzMBpTHXMpwEdCMMNEatVc4gnQDOXsKkW5FbUMQ9y0vj6EUs2vUZLkwp3FP3-PuJEHMCLCTBdkVlE5JErwhZU6V3S5lOZ2jTrSuYAhcph~ML~ntjDzc-IZWgAax98ChzCpwxhEk4eP4B9vEhMG7njoxbJLfwAYu1qgq0A-Whm08rAhAcDwl8a-21Kqj8~iGq0XytMqyJU5XArq4aArWEwZFFLKJD9uR9bKeMaHIkJLNAyKIvfpgwRjYIMlIf6ekN3cpBU3B6c8NgDNPNqNBI6DQ1XGfKOEqX6d31mTOSzyjs-zgIJkt5zg-MH1pnMSVAI-vQbiAaPJc3Rjeqarv5Z89ZX~dghQFNQX1Wd7ezJy0WltaY6V7lLxd6QPTosDCDawT7ZoNhmLUNtFXI7oGS4Cm4ZTZAjZV-ZFMUXWUKFTZGyh4s2WbYqJq1Sackk8JmCvZ7JHazJ9ghnrQQ2bpzWeeGgtCPIcTnaUMPOZdsU96ehme7AAAA
pastebin.i2p=mUDz9K6KmWe2zE4wj~YjqwD5f8pCjEbze-DuafzjtXOaj9SnRBrLNqgOTt063y9foZett484g9PFm~3ibqFZfUk3LsJi6YhZje-V~RZndBElRJ1cX~MBOG5wdHA2BYpBt7jX-N5J9ww7POtcPFDjyYlJLhRrY5FuRfxdsWJ4BOUHOwvbTq7IWvqHReayte6vKavpyvNcAotcFHAhOGpR6Ua3RncS~b6NA5k2CQIeS4mR6~iNCh0sx1gj4cWWWqXSa6pN19io82L2fcNEcxE-UETj4HkG5fTdBlB4I2cNAm1KnxQw9ntRf19p5EzYkOFYST5ra~RWmy0bWzJMBFlK9QcogVi97gmszuNPyfIpJIE1ssZ6BNHKMkPJ-fjxtSAseTBV-Fa51TIBwNyXC6VxMsixKIBX4Cg64oipoGpm~-7SdRgabHbB0vWwiV6RYEQ88oNcV2ycDqjyfQymT0T5S9b0aVd2Ey1ZWly-PR44~uC3mCctcMqfZVfNFs3NoY3ZAAAA
sciencebooks.i2p=Rrqf6EmeuqckygNrim-ZcZ97uEIL9Ykkr8cj1RSGbeih33~UUpjAp0x2fxrpbibXwuGufWMNoaMkZH8FxOxZr1hvBeV4Mvjrn05kKWyPpBB1x7rAcVCCoD~bl6CUU2k4NtFrO9~BCCZdoIvMxXYWRy4nj18RVaIlOS3qE6~F04pU7Fy0sj7xtd02wgeyKkJ6pXYGfETMr6phVusHSSQfCumDBpnqEt7EcXTIsPz58y9Zim4u1wp6xj~OvGWFIP8fKIxpH~zf6o0qYytXrf6SJbIEvwITKGIXZTM5KxQ0LbEydSgPn4q5PttQlKg~7hxuGG5RYqP2IMg-f9z4-1SfYTbz~EixEq8gv0R30c60tqsyAaw9o37R3k9inkT5WylYwBKM0pzfcAVjejcoPYnFF1iithGl0AYkP~~isxCErCNwLs39NyE688C5amDIpXl7AgB~IvbwZfONbd2cwMOB2RFzTShYZlwcILB7EzgMvEt2l8GBkstP3CzNrjQ2gAL0AAAA
piespy.i2p=jTCW8w04d~Sucb7OXFBZtq7tEOxeFBNm5T4uVZW3xNapThxk0Tie8OAIQMmz4lzDUaHoc97DN1s6k3rTprRrhbr7Idyls5I-r6jAQnrljqUveIMA-lbzXDYTgo8TqBR4~fpSL4RM5u4~sM4ZRKdwufnEjCSlYtTe8qmrofFx3cIuIKhfypsYcu-BEdrw7P~cRpmqJqBV~igulwfIxeABa8ygX~Uk~i68NELte55J70z~kijYGMAgUJVhtQxD~jZD5l3FrGkND9lVaam0lHseujNyy~ZB-ma5IKqJWKLEo19e224EVhIK4jcKYdnr7bE86mZiWg36GvWFhjJOkEvCJ3BWxaMjcDXyk-9vCY7MA403OCMGGHoKlzB2AKq1PEc1teMtoqMG~a42DTKevUbbiALtauxCe-BOxEtxpYTvvql-RVumrUh3hckh0wqAd1Gqr2uqRx4VaCF3X3duOxdoKV6Kgf~icfVIFq5HR7AhN63BGFhAeQLcudp3oA828a06AAAA
dm.i2p=ZPCnBTv~~EZTvbq4D4Lscn4AL~VMwbmKSLEF4Aq8bzynQm~kGnkSLhrDpNaJyAvO0F9wX-7bxyu3KLmpE~0KZt-6-J2spfBqkRTOd39j1YjJ1HHXivWiI8yl4uoHXZun6UW~QdwKQPzAMBnl2W4rXpQekGxwh7HlnQT22FyI-ELtzh8FE8JJ6DxfYsDKd49QnsePM3r2dB6hWb0lPEf-DwSD8JtsRmXMmDfa16-yWt5Wa0K-xrWbKh~oQ8K1aXhHoUxsfJ~LQnklt~QGld9Cbk7q1hJKXFUSqCoLaADZEmvzuNlzE53ChK~W6IanEZ4WeNT7dMJaMtta2Ot5Ym9dXGygFp2XE5zGsOpq38XGNHHI3iKNCexIh0eTL3jwwkUQXl7UPWxrykRff8uRSgBq6s669lOzUbZfdmAbkTG6NvwU0RboQQ-OYs7APDn5ovXHI3zl0bRljZWBxNVswz34fQ~jkA5aHUcgV~sNgwjjz5um7K9V-QEVUBi68AhJbmvRAAAA
up.i2p=D7MtDuWfl9XOkSDC~hCH7QBrnDhg2KdxFq2dyq0e2jJypmXPFSw0Hl6HOMdI3myFTtweKZObHaPzy4c3-hk9ZwBsqzT8XquTVoa~z7OAtoj0QQDdIGXaHpDvVWkj9wAilpJfypqThVho08F0cq9~yChBh2s02JoQersRQ4l9oMQaOSVS85wGudFYSeWUhRPJyb0FN4ic7KPRvFlOBnESCtQUxMa0HV28UDyc9kGUN8AmgaV2qi-LcdrxHsYwzbYKZVa6Tg3mHEqksuzH5AK~9KYHlzLz2QRDesL0jkf7WdVRlKSiqkrYQVSG7MAqHfHNhQbb9oIpqDARyAqUaPJyEVQRAphJ9qv-K54QW1T2Js5tF5XVJoZcU04d9aHyxaUKqWJwIMrRlfeKVGAPlE7pcUjQrTKe6-22-46CzKpHsG8j4kwSyvrwlpGdsbN7-q9tq3KH4b9hJiv5nsQ4lSBQ9Um6YCEmUdwffnfbbfVGPCmj9XcMSrnHfvCD2rf4-V9sAAAA
phonebooth.i2p=akZY4njn7Vc3Xj6y14QsJXGTSJq5RK3~UvQGRwdBIZPnA2cDMiPfcitUCcL2G88NnpBt4sJZiwZWJRncv3~6q1QtQi1QZ1pICnOGErYbSLbWwSJ520BYOsxVxRptZlLNTeVGnS8MWFCXvyo1qVYncaV4DnJPIdo8D0z8EqPuHIuWPkIHkD69rIBsW7-um0c5C1xIyAVGXPA1Bu8Bkgf7-mjf1Tvu7ZvtJR5DRYx1hOhsz-oGVi~6Cjd1kASS2FfLYjfNkzIbE7uT2fBDpOSyO6i~bAijgbB628MIE1Vhp87Fq6MfwBBDc-IrWUbMip6rVy9snbk88vcC2dJonZhHcDYhr76m3uD4KVqDoAFc5HpO1cEilhdTnBataEgDUIE8IrClTGeqgy~aJDnjjDxXd0RXOQCQUL4A2rGCPL46NhbvdRC22bVF18NaggglpLuRCTUQJ7Y3wj9E-RxPpm4~yYrm2s7EPMfmo7IwS5ihvwLvMyiynFGivJrI1jADYPs2AAAA
chat.i2p=E8iddJ-~CVMPdxN7FIgaQfFv8XywP6JLmx0pDq-dL4M1mSN1jRmrad9pyhbvLM1-o2X3ZVA4joXJetCv0-Gg39kTvIMolPM6pZ4fJgvNJTUXWaAhsbB7g0-oKD73J88GwNwJ377UgYKSOSHfTYJ1gvYu718FXR-5k48Tw~xmkJBLyJOh3GcK46nYm94ga0YpOGKQoy9Cdp3oafEAiPiJJpwb3LCBkg2Zq~snSQ~HJEg-desvHfhexwf5eRnrPzLZCiHJ8KRjaXN5sy4R7kfJkX4AKcCjNhcmis6kH2F2bH97RrnjFXnRPiWSXNWcuDSum84p1aKQSgHh1Vrbme4yJ5rPu-DxEvu-z9yo6o4XqkBKye7DwAb6dBq3sbdVDdJp7aqye22m6nZ3Cf0i6u-oasGuNGYUhWmML~p6MPjYwBiuMBL1A6VIJx-AVMHvGhm7xiAYrjR3U24YMSDo~foRJ9K7fIaEMgY0rsVcNAsc8owedNMa8FLBSk8AezHvB28-AAAA
bl.i2p=-kRXee7rocbNNCDtgQBZ7i~z8WEg4jVvXgsLaqe2e6-DBR9st07Cqn1b6m9CXgIfL04XB-6Pw18LItova4s5U7U178PbPugOOeZPmwWmmDG11Ch92Pnpa6hHiJlQ3PTq1PLA-y1xgOyBtZ5UhQ6i8ErG5827l76hSs98lWdgoFdIoI7QR6~oPSxekLDs2XJZWPyShTAj7xOvRretKrObB06JsucZsDPXfvimyfkge71klvGnzMpLb9rTmMyg9R9r0Hc13rOGtj75HGtSEnTJFRXQlYCxr5~HeJXDMcXEftn2-nTGAzSdgOxLgXUCBGh-0Hf~nrek-gEiVl4DIO-HzJCBelpt1XSH24ou6Jp0BZIUaVCCUThHD0sdcCuCtLn8xKzz2LyMDmkb2nNfT6JIaFh6qdWZFxWyOoC9JORNWCPH~B6hAs4~Bo28NnFARJGsvz-B1JBMBzVFRuCghtU131hZ~Bk33kmKtfev8STOh~UKXqAFhR-6EEOu9PydUiknAAAA
j.i2p=mxmGmXHJmKvXNkx9ucrGC31Uxt9XhFEtIq08ZbMm33nsWfYFrwM6x308TqC55f-wkvrA-8v33PJLuVxThK1GjELxUWO9c8pzbrgRbO2VH60coT1wOfD840w1xaMpxzRBVuavDv9WybOpwkpKkBRTHHEAwjAnUSLfWihLZCjDQd6BI2ZI0Y~dhtuPtJK-VE24CTf3XxuMYcTaiK2-Kj81D2TASWh~Z~rV0SGnpu4t4CQV-hA~CkvNW5mY7SwQqcYBrLLoxieL4rfpBn6G3TMyshws075uGDJKvQjklJpcjtTN9sEMB-Z1IKvAEHRP45-OsOFSw8iMweQ8x7~HN9F3uAdSnyE6KV8Fw9r-Utcb7gCx6RScw66pqC1jdYVNqLX07cI-GOYs96g8pnG6ixZruTkzCzD-cRqUCePkUvbF79XuZ3h6137k4WTqDAWS7iFnzXPdA5GQqbReHmozIuY3hyUZrbD2fRvXjWl6fG-43WpOTjkzRveYKjUW-RPnwIkcAAAA

View File

@ -1,4 +1,4 @@
$Id: install-headless.txt,v 1.2 2004/09/02 01:54:37 jrandom Exp $
$Id: install-headless.txt,v 1.3 2004/09/29 14:38:15 jrandom Exp $
Headless I2P installation instructions
1) tar xjf i2p.tar.bz2 (you've already done this)
@ -10,7 +10,8 @@ If you're having trouble, swing by http://forum.i2p.net/, check the
website at http://www.i2p.net/, or get on irc://irc.freenode.net/#i2p
To run I2P explicitly:
sh i2prouter start
(*nix): sh i2prouter start
(win*): I2Psvc.exe -c wrapper.config
To stop the router (gracefully):
lynx http://localhost:7657/configservice.jsp ("Shutdown gracefully")
@ -26,4 +27,4 @@ Supported JVMs:
Linux: Latest available from http://java.sun.com/ (1.3+ supported)
FreeBSD: /usr/ports/java/linux-sun-jdk1.4
various: http://www.kaffe.org/ using CVS HEAD as of Sept 1, 2004
(or any subsequent releases)
(or any subsequent releases)

26
install.txt Normal file
View File

@ -0,0 +1,26 @@
$Id$
I2P source installation instructions
To build and install I2P from source, you must first build
and package up the appropriate installer by running:
ant dist
This will produce a few key files:
* i2p.tar.bz2: the headless installation
* install.jar: the GUI installer
* i2pupdate.zip: the update package
From there, you can follow the headless installation instructions
with the headless installer, run the GUI installer, or deploy
the update into an existing installation.
You will need to have ant installed from http://ant.apache.org/
(1.5 or newer)
Supported JVMs:
Windows: Latest available from http://java.sun.com/ (1.3+ supported)
Linux: Latest available from http://java.sun.com/ (1.3+ supported)
FreeBSD: /usr/ports/java/linux-sun-jdk1.4
various: http://www.kaffe.org/ using CVS HEAD as of Sept 1, 2004
(or any subsequent releases)

View File

@ -4,7 +4,7 @@
<info>
<appname>i2p</appname>
<appversion>0.4.2.3</appversion>
<appversion>0.4.2.6</appversion>
<authors>
<author name="I2P" email="support@i2p.net"/>
</authors>

View File

@ -1,39 +1,14 @@
@echo off
setlocal
set INSTALL_PATH="%1"
rem
rem Java Service Wrapper general startup script
rem
rem
rem Resolve the real path of the Wrapper.exe
rem For non NT systems, the _REALPATH and _WRAPPER_CONF values
rem can be hard-coded below and the following test removed.
rem
if "%OS%"=="Windows_NT" goto nt
echo This script only works with NT-based versions of Windows.
goto :eof
set _WRAPPER_EXE=%INSTALL_PATH%I2Psvc.exe
set _WRAPPER_CONF="%INSTALL_PATH%wrapper.config"
:nt
rem
rem Find the application home.
rem
rem %~dp0 is location of current script under NT
set _REALPATH=%~dp0
set _WRAPPER_EXE=%_REALPATH%I2Psvc.exe
rem
rem Find the wrapper.conf
rem
:conf
set _WRAPPER_CONF="%~f1"
if not %_WRAPPER_CONF%=="" goto startup
set _WRAPPER_CONF="%_REALPATH%wrapper.config"
rem
rem Start the Wrapper
rem
:startup
"%_WRAPPER_EXE%" -c %_WRAPPER_CONF%
if not errorlevel 1 goto :eof
pause

View File

@ -5,7 +5,7 @@ tunnel.0.type=httpclient
tunnel.0.interface=127.0.0.1
tunnel.0.listenPort=4444
tunnel.0.proxyList=squid.i2p,www1.squid.i2p
tunnel.0.i2cpHost=localhost
tunnel.0.i2cpHost=127.0.0.1
tunnel.0.i2cpPort=7654
tunnel.0.option.tunnels.depthInbound=2
tunnel.0.option.tunnels.numInbound=2
@ -19,7 +19,7 @@ tunnel.1.type=client
tunnel.1.interface=127.0.0.1
tunnel.1.listenPort=6668
tunnel.1.targetDestination=irc.duck.i2p,irc.baffled.i2p
tunnel.1.i2cpHost=localhost
tunnel.1.i2cpHost=127.0.0.1
tunnel.1.i2cpPort=7654
tunnel.1.option.tunnels.depthInbound=2
tunnel.1.option.tunnels.numInbound=2
@ -33,7 +33,7 @@ tunnel.2.type=client
tunnel.2.interface=127.0.0.1
tunnel.2.listenPort=2401
tunnel.2.targetDestination=cvs.i2p
tunnel.2.i2cpHost=localhost
tunnel.2.i2cpHost=127.0.0.1
tunnel.2.i2cpPort=7654
tunnel.2.option.tunnels.depthInbound=2
tunnel.2.option.tunnels.numInbound=2
@ -42,11 +42,12 @@ tunnel.2.startOnLoad=false
# local eepserver
tunnel.3.name=eepsite
tunnel.3.description=My eepsite
tunnel.3.type=server
tunnel.3.targetHost=localhost
tunnel.3.type=httpserver
tunnel.3.targetHost=127.0.0.1
tunnel.3.targetPort=7658
tunnel.3.spoofedHost=mysite.i2p
tunnel.3.privKeyFile=eepsite/eepPriv.dat
tunnel.3.i2cpHost=localhost
tunnel.3.i2cpHost=127.0.0.1
tunnel.3.i2cpPort=7654
tunnel.3.option.tunnels.depthInbound=2
tunnel.3.option.tunnels.numInbound=2
@ -54,7 +55,7 @@ tunnel.3.startOnLoad=true
# postman's SMTP server - see www.postman.i2p
tunnel.4.description=smtp server
tunnel.4.i2cpHost=localhost
tunnel.4.i2cpHost=127.0.0.1
tunnel.4.i2cpPort=7654
tunnel.4.interface=127.0.0.1
tunnel.4.listenPort=7659
@ -69,7 +70,7 @@ tunnel.4.type=client
# postman's POP3 server - see www.postman.i2p
tunnel.5.name=pop3.postman.i2p
tunnel.5.description=pop3 server
tunnel.5.i2cpHost=localhost
tunnel.5.i2cpHost=127.0.0.1
tunnel.5.i2cpPort=7654
tunnel.5.interface=127.0.0.1
tunnel.5.listenPort=7660
@ -78,4 +79,4 @@ tunnel.5.option.tunnels.numInbound=2
tunnel.5.option.i2p.streaming.connectDelay=1000
tunnel.5.startOnLoad=false
tunnel.5.targetDestination=pop.postman.i2p
tunnel.5.type=client
tunnel.5.type=client

View File

@ -33,7 +33,7 @@ del /f /q "%INSTALL_PATH%postinstall.sh"
:: del /f /q "%INSTALL_PATH%uninstall_i2p_service_unix"
del /f /q "%INSTALL_PATH%icons\*.xpm"
rmdir /q /s "%INSTALL_PATH%lib\wrapper"
start /b /i /d"%INSTALL_PATH%" i2prouter.bat
start /b /i /d"%INSTALL_PATH%" i2prouter.bat %INSTALL_PATH%
) else (
@ -47,6 +47,6 @@ del "%INSTALL_PATH%postinstall.sh"
del "%INSTALL_PATH%uninstall_i2p_service_winnt.bat"
del "%INSTALL_PATH%icons\*.xpm"
deltree /Y "%INSTALL_PATH%lib\wrapper"
start /M "%INSTALL_PATH%i2prouter.bat"
start /M "%INSTALL_PATH%i2prouter.bat" %INSTALL_PATH%
)

View File

@ -16,6 +16,7 @@ listed (if not, <a href="#trouble">see below</a>). Once those are up, you can:<
<ul>
<li><a href="http://duck.i2p/">duck.i2p</a>: duck's eepsite, with links to other active sites</li>
<li><a href="http://ugha.i2p/">ugha.i2p</a>: ugha's eepsite, a wiki that anyone can edit, and lots of links</li>
<li><a href="http://orion.i2p/">orion.i2p</a>: a site which tracks eepsite uptime and changes</li>
<li><a href="http://files.i2p/">files.i2p</a>: a search engine that tries to keep track of things on I2P</li>
<li><a href="http://forum.i2p/">forum.i2p</a>: a secure and anonymous connection to <a href="http://forum.i2p.net/">forum.i2p.net</a></li>
<li><a href="http://www.i2p/">www.i2p</a>: a secure and anonymous connection to <a href="http://www.i2p.net/">www.i2p.net</a></li>
@ -54,7 +55,7 @@ IRC (be sure to split it into two lines, as its too long for one).</p>
<p>If the left hand side has a warning, telling you to check your NAT or firewall, please
see the <a href="/config.jsp">config page</a> and make sure that you can receive <b>inbound
TCP connections on port 8887</b> (or another port that you specify). Probelms forwarding
TCP connections on port 8887</b> (or another port that you specify). Problems forwarding
that port account for the vast majority of issues people run into. When it says
"Active: 72/85", the "72" means how many peers you are connected with now, and "85" means
how many you have spoken with recently - if that first number is 0, you can bet that there

View File

@ -138,6 +138,11 @@ public class I2NPMessageReader {
_log.warn("IO Error handling message", ioe);
_listener.disconnected(I2NPMessageReader.this);
cancelRunner();
} catch (Exception e) {
_log.log(Log.CRIT, "wtf, error reading", e);
_listener.readError(I2NPMessageReader.this, e);
_listener.disconnected(I2NPMessageReader.this);
cancelRunner();
}
}
if (!_doRun) {

View File

@ -8,8 +8,12 @@ package net.i2p.router;
*
*/
//import net.i2p.router.message.ProcessOutboundClientMessageJob;
import java.util.Properties;
import net.i2p.client.I2PClient;
import net.i2p.router.message.OutboundClientMessageJob;
import net.i2p.router.message.OutboundClientMessageOneShotJob;
import net.i2p.util.Log;
/**
@ -55,7 +59,22 @@ public class ClientMessagePool {
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Adding message for remote delivery");
_context.jobQueue().addJob(new OutboundClientMessageJob(_context, msg));
if (isGuaranteed(msg))
_context.jobQueue().addJob(new OutboundClientMessageJob(_context, msg));
else
_context.jobQueue().addJob(new OutboundClientMessageOneShotJob(_context, msg));
}
}
private boolean isGuaranteed(ClientMessage msg) {
Properties opts = null;
if (msg.getSenderConfig() != null)
opts = msg.getSenderConfig().getOptions();
if (opts != null) {
String val = opts.getProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT);
return val.equals(I2PClient.PROP_RELIABILITY_GUARANTEED);
} else {
return false;
}
}
}

View File

@ -50,7 +50,10 @@ public abstract class NetworkDatabaseFacade implements Service {
* @throws IllegalArgumentException if the data is not valid
*/
public abstract RouterInfo store(Hash key, RouterInfo routerInfo) throws IllegalArgumentException;
public abstract void publish(RouterInfo localRouterInfo);
/**
* @throws IllegalArgumentException if the local router is not valid
*/
public abstract void publish(RouterInfo localRouterInfo) throws IllegalArgumentException;
public abstract void publish(LeaseSet localLeaseSet);
public abstract void unpublish(LeaseSet localLeaseSet);
public abstract void fail(Hash dbEntry);

View File

@ -75,9 +75,13 @@ public class Router {
public final static String PROP_SHUTDOWN_IN_PROGRESS = "__shutdownInProgress";
static {
// grumble about sun's java caching DNS entries *forever*
System.setProperty("sun.net.inetaddr.ttl", "0");
System.setProperty("networkaddress.cache.ttl", "0");
// grumble about sun's java caching DNS entries *forever* by default
// so lets just keep 'em for a minute
System.setProperty("sun.net.inetaddr.ttl", "60");
System.setProperty("networkaddress.cache.ttl", "60");
// until we handle restricted routes and/or all peers support v6, try v4 first
System.setProperty("java.net.preferIPv4Stack", "true");
System.setProperty("http.agent", "I2P");
// (no need for keepalive)
System.setProperty("http.keepAlive", "false");
System.setProperty("user.timezone", "GMT");
@ -257,11 +261,49 @@ public class Router {
}
ri.sign(key);
setRouterInfo(ri);
_context.netDb().publish(ri);
try {
_context.netDb().publish(ri);
} catch (IllegalArgumentException iae) {
_log.log(Log.CRIT, "Local router info is invalid? rebuilding a new identity", iae);
rebuildNewIdentity();
}
} catch (DataFormatException dfe) {
_log.log(Log.CRIT, "Internal error - unable to sign our own address?!", dfe);
}
}
/**
* Ugly list of files that we need to kill if we are building a new identity
*
*/
private static final String _rebuildFiles[] = new String[] { "router.info",
"router.keys",
"connectionTag.keys",
"keyBackup/privateEncryption.key",
"keyBackup/privateSigning.key",
"keyBackup/publicEncryption.key",
"keyBackup/publicSigning.key",
"sessionKeys.dat" };
/**
* Rebuild a new identity the hard way - delete all of our old identity
* files, then reboot the router.
*
*/
public void rebuildNewIdentity() {
for (int i = 0; i < _rebuildFiles.length; i++) {
File f = new File(_rebuildFiles[i]);
if (f.exists()) {
boolean removed = f.delete();
if (removed)
System.out.println("INFO: Removing old identity file: " + _rebuildFiles[i]);
else
System.out.println("ERROR: Could not remove old identity file: " + _rebuildFiles[i]);
}
}
System.out.println("INFO: Restarting the router after removing any old identity files");
// hard and ugly
System.exit(EXIT_GRACEFUL_RESTART);
}
/**
* coalesce the stats framework every minute
@ -272,6 +314,9 @@ public class Router {
super(Router.this._context);
Router.this._context.statManager().createRateStat("bw.receiveBps", "How fast we receive data", "Bandwidth", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
Router.this._context.statManager().createRateStat("bw.sendBps", "How fast we send data", "Bandwidth", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
Router.this._context.statManager().createRateStat("router.activePeers", "How many peers we are actively talking with", "Throttle", new long[] { 5*60*1000, 60*60*1000 });
Router.this._context.statManager().createRateStat("router.highCapacityPeers", "How many high capacity peers we know", "Throttle", new long[] { 5*60*1000, 60*60*1000 });
Router.this._context.statManager().createRateStat("router.fastPeers", "How many fast peers we know", "Throttle", new long[] { 5*60*1000, 60*60*1000 });
}
public String getName() { return "Coalesce stats"; }
public void runJob() {
@ -296,6 +341,15 @@ public class Router {
Router.this._context.statManager().addRateData("bw.sendBps", (long)bps, 60*1000);
}
}
int active = Router.this._context.commSystem().countActivePeers();
Router.this._context.statManager().addRateData("router.activePeers", active, 60*1000);
int fast = Router.this._context.profileOrganizer().countFastPeers();
Router.this._context.statManager().addRateData("router.fastPeers", fast, 60*1000);
int highCap = Router.this._context.profileOrganizer().countHighCapacityPeers();
Router.this._context.statManager().addRateData("router.highCapacityPeers", highCap, 60*1000);
requeue(60*1000);
}
@ -797,7 +851,11 @@ public class Router {
installUpdates();
verifyWrapperConfig();
Router r = new Router();
r.runRouter();
if ( (args != null) && (args.length == 1) && ("rebuild".equals(args[0])) ) {
r.rebuildNewIdentity();
} else {
r.runRouter();
}
}
private static final String UPDATE_FILE = "i2pupdate.zip";

View File

@ -30,6 +30,8 @@ class RouterThrottleImpl implements RouterThrottle {
private static int THROTTLE_EVENT_LIMIT = 300;
private static final String PROP_MAX_TUNNELS = "router.maxParticipatingTunnels";
private static final String PROP_DEFAULT_KBPS_THROTTLE = "router.defaultKBpsThrottle";
private static final String PROP_BANDWIDTH_SHARE_PERCENTAGE = "router.sharePercentage";
public RouterThrottleImpl(RouterContext context) {
_context = context;
@ -43,11 +45,12 @@ class RouterThrottleImpl implements RouterThrottle {
_context.statManager().createRateStat("router.throttleTunnelMaxExceeded", "How many tunnels we are participating in when we refuse one due to excees?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("router.throttleTunnelProbTooFast", "How many tunnels beyond the previous 1h average are we participating in when we throttle?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("router.throttleTunnelProbTestSlow", "How slow are our tunnel tests when our average exceeds the old average and we throttle?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("router.throttleTunnelBandwidthExceeded", "How much bandwidth is allocated when we refuse due to bandwidth allocation?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
}
public boolean acceptNetworkMessage() {
long lag = _context.jobQueue().getMaxLag();
if (lag > JOB_LAG_LIMIT) {
if ( (lag > JOB_LAG_LIMIT) && (_context.router().getUptime() > 60*1000) ) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Throttling network reader, as the job lag is " + lag);
_context.statManager().addRateData("router.throttleNetworkCause", lag, lag);
@ -107,22 +110,7 @@ class RouterThrottleImpl implements RouterThrottle {
return false;
}
// ok, we're not hosed, but can we handle the bandwidth requirements
// of another tunnel?
rs = _context.statManager().getRate("tunnel.participatingMessagesProcessed");
r = null;
if (rs != null)
r = rs.getRate(10*60*1000);
double msgsPerTunnel = (r != null ? r.getAverageValue() : 0);
r = null;
rs = _context.statManager().getRate("tunnel.relayMessageSize");
if (rs != null)
r = rs.getRate(10*60*1000);
double bytesPerMsg = (r != null ? r.getAverageValue() : 0);
double bytesPerTunnel = msgsPerTunnel * bytesPerMsg;
int numTunnels = _context.tunnelManager().getParticipatingCount();
double bytesAllocated = (numTunnels + 1) * bytesPerTunnel;
if (_context.getProperty(Router.PROP_SHUTDOWN_IN_PROGRESS) != null) {
if (_log.shouldLog(Log.WARN))
@ -206,10 +194,21 @@ class RouterThrottleImpl implements RouterThrottle {
// no default, ignore it
}
}
// ok, we're not hosed, but can we handle the bandwidth requirements
// of another tunnel?
rs = _context.statManager().getRate("tunnel.participatingBytesProcessed");
r = null;
if (rs != null)
r = rs.getRate(10*60*1000);
double bytesAllocated = r.getCurrentTotalValue();
if (!allowTunnel(bytesAllocated, numTunnels)) {
_context.statManager().addRateData("router.throttleTunnelBandwidthExceeded", (long)bytesAllocated, 0);
return false;
}
_context.statManager().addRateData("tunnel.bytesAllocatedAtAccept", (long)bytesAllocated, msg.getTunnelDurationSeconds()*1000);
// todo: um, throttle (include bw usage of the netDb, our own tunnels, the clients,
// and check to see that they are less than the bandwidth limits
if (_log.shouldLog(Log.DEBUG))
_log.debug("Accepting a new tunnel request (now allocating " + bytesAllocated + " bytes across " + numTunnels
@ -217,6 +216,99 @@ class RouterThrottleImpl implements RouterThrottle {
return true;
}
/**
* with bytesAllocated already accounted for across the numTunnels existing
* tunnels we have agreed to, can we handle another tunnel with our existing
* bandwidth?
*
*/
private boolean allowTunnel(double bytesAllocated, int numTunnels) {
long bytesAllowed = getBytesAllowed();
bytesAllowed *= getSharePercentage();
double bytesPerTunnel = (numTunnels > 0 ? bytesAllocated / numTunnels : 0);
double toAllocate = (numTunnels > 0 ? bytesPerTunnel * (numTunnels + 1) : 0);
double pctFull = toAllocate / bytesAllowed;
double allocatedKBps = toAllocate / (10 * 60 * 1024);
if (pctFull < 1.0) { // (_context.random().nextInt(100) > 100 * pctFull) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Probabalistically allowing the tunnel w/ " + pctFull + " of our " + bytesAllowed
+ "bytes/" + allocatedKBps + "KBps allocated through " + numTunnels + " tunnels");
return true;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Rejecting the tunnel w/ " + pctFull + " of our " + bytesAllowed
+ "bytes allowed (" + toAllocate + "bytes / " + allocatedKBps
+ "KBps) through " + numTunnels + " tunnels");
return false;
}
}
/**
* What fraction of the bandwidth specified in our bandwidth limits should
* we allow to be consumed by participating tunnels?
*
*/
private double getSharePercentage() {
String pct = _context.getProperty(PROP_BANDWIDTH_SHARE_PERCENTAGE, "0.8");
if (pct != null) {
try {
return Double.parseDouble(pct);
} catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.INFO))
_log.info("Unable to get the share percentage");
}
}
return 0.8;
}
/**
* BytesPerSecond that we can pass along data
*/
private long getBytesAllowed() {
String kbpsOutStr = _context.getProperty("i2np.bandwidth.outboundKBytesPerSecond");
long kbpsOut = -1;
if (kbpsOutStr != null) {
try {
kbpsOut = Integer.parseInt(kbpsOutStr);
} catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.INFO))
_log.info("Unable to get the bytes allowed (outbound)");
}
}
String kbpsInStr = _context.getProperty("i2np.bandwidth.inboundKBytesPerSecond");
long kbpsIn = -1;
if (kbpsInStr != null) {
try {
kbpsIn = Integer.parseInt(kbpsInStr);
} catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.INFO))
_log.info("Unable to get the bytes allowed (inbound)");
}
}
// whats our choke?
long kbps = (kbpsOut > kbpsIn ? kbpsIn : kbpsOut);
if (kbps <= 0) {
try {
kbps = Integer.parseInt(_context.getProperty(PROP_DEFAULT_KBPS_THROTTLE, "64")); // absurd
} catch (NumberFormatException nfe) {
kbps = 64;
}
}
return kbps
* 60 // per minute
* 10 // per 10 minute period
* 1024; // bytes;
}
/** dont ever probabalistically throttle tunnels if we have less than this many */
private int getMinThrottleTunnels() {
try {

View File

@ -15,8 +15,8 @@ import net.i2p.CoreVersion;
*
*/
public class RouterVersion {
public final static String ID = "$Revision: 1.106 $ $Date: 2004/12/08 12:16:17 $";
public final static String VERSION = "0.4.2.3";
public final static String ID = "$Revision: 1.130 $ $Date: 2005/01/05 19:17:54 $";
public final static String VERSION = "0.4.2.6";
public final static long BUILD = 0;
public static void main(String args[]) {
System.out.println("I2P Router version: " + VERSION);

View File

@ -103,6 +103,7 @@ public class StatisticsManager implements Service {
includeThroughput(stats);
includeRate("transport.sendProcessingTime", stats, new long[] { 60*60*1000 });
includeRate("tcp.probabalisticDropQueueSize", stats, new long[] { 60*1000l, 60*60*1000l });
//includeRate("tcp.queueSize", stats);
//includeRate("jobQueue.jobLag", stats, new long[] { 60*1000, 60*60*1000 });
//includeRate("jobQueue.jobRun", stats, new long[] { 60*1000, 60*60*1000 });
@ -114,6 +115,8 @@ public class StatisticsManager implements Service {
//includeRate("jobQueue.droppedJobs", stats, new long[] { 60*60*1000, 24*60*60*1000 });
//includeRate("inNetPool.dropped", stats, new long[] { 60*60*1000, 24*60*60*1000 });
includeRate("tunnel.participatingTunnels", stats, new long[] { 5*60*1000, 60*60*1000 });
includeRate("tunnel.participatingBytesProcessed", stats, new long[] { 10*60*1000 });
includeRate("tunnel.participatingBytesProcessedActive", stats, new long[] { 10*60*1000 });
includeRate("tunnel.testSuccessTime", stats, new long[] { 60*60*1000l, 24*60*60*1000l });
//includeRate("tunnel.outboundMessagesProcessed", stats, new long[] { 10*60*1000, 60*60*1000 });
//includeRate("tunnel.inboundMessagesProcessed", stats, new long[] { 10*60*1000, 60*60*1000 });

View File

@ -56,6 +56,7 @@ public class TunnelInfo extends DataStructureImpl {
private boolean _wasEverReady;
private int _messagesProcessed;
private int _tunnelFailures;
private long _bytesProcessed;
public TunnelInfo(I2PAppContext context) {
_context = context;
@ -79,6 +80,7 @@ public class TunnelInfo extends DataStructureImpl {
_lastTested = -1;
_messagesProcessed = 0;
_tunnelFailures = 0;
_bytesProcessed = 0;
}
public TunnelId getTunnelId() { return _id; }
@ -182,7 +184,12 @@ public class TunnelInfo extends DataStructureImpl {
return _messagesProcessed;
}
/** we have just processed a message for this tunnel */
public void messageProcessed() { _messagesProcessed++; }
public void messageProcessed(int size) {
_messagesProcessed++;
_bytesProcessed += size;
}
/** how many bytes have been pumped through this tunnel in its lifetime? */
public long getBytesProcessed() { return _bytesProcessed; }
/**
* the tunnel was (potentially) unable to pass a message through.

View File

@ -83,6 +83,9 @@ class ClientWriterRunner implements Runnable {
for (int i = 0; i < messages.size(); i++) {
I2CPMessage msg = (I2CPMessage)messages.get(i);
Long when = (Long)messageTimes.get(i);
if (_log.shouldLog(Log.DEBUG))
_log.debug("["+_id+"] writeMessage before writing "
+ msg.getClass().getName());
_runner.writeMessage(msg);
if (_log.shouldLog(Log.DEBUG))
_log.debug("["+_id+"] writeMessage time since addMessage(): "

View File

@ -240,7 +240,7 @@ public class HandleTunnelMessageJob extends JobImpl {
if (info == null)
return;
info.messageProcessed();
info.messageProcessed(_message.getMessageSize());
//if ( (_message.getVerificationStructure() == null) && (info.getSigningKey() != null) ) {
if (_message.getVerificationStructure() == null) {

View File

@ -0,0 +1,490 @@
package net.i2p.router.message;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import net.i2p.data.Certificate;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.Lease;
import net.i2p.data.LeaseSet;
import net.i2p.data.PublicKey;
import net.i2p.data.SessionTag;
import net.i2p.data.SessionKey;
import net.i2p.data.TunnelId;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2np.DataMessage;
import net.i2p.data.i2np.DeliveryStatusMessage;
import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.GarlicMessage;
import net.i2p.data.i2np.DeliveryInstructions;
import net.i2p.router.message.PayloadGarlicConfig;
import net.i2p.router.ClientMessage;
import net.i2p.router.JobImpl;
import net.i2p.router.ReplyJob;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.router.TunnelSelectionCriteria;
import net.i2p.router.MessageSelector;
import net.i2p.util.Log;
/**
* Send a client message out a random outbound tunnel and into a random inbound
* tunnel on the target leaseSet. This also bundles the sender's leaseSet and
* a DeliveryStatusMessage (for ACKing any sessionTags used in the garlic).
*
*/
public class OutboundClientMessageOneShotJob extends JobImpl {
private Log _log;
private long _overallExpiration;
private boolean _shouldBundle;
private ClientMessage _clientMessage;
private MessageId _clientMessageId;
private int _clientMessageSize;
private Destination _from;
private Destination _to;
/** target destination's leaseSet, if known */
private LeaseSet _leaseSet;
/** Actual lease the message is being routed through */
private Lease _lease;
private PayloadGarlicConfig _clove;
private long _cloveId;
private long _start;
private boolean _finished;
/**
* final timeout (in milliseconds) that the outbound message will fail in.
* This can be overridden in the router.config or the client's session config
* (the client's session config takes precedence)
*/
public final static String OVERALL_TIMEOUT_MS_PARAM = "clientMessageTimeout";
private final static long OVERALL_TIMEOUT_MS_DEFAULT = 60*1000;
/** priority of messages, that might get honored some day... */
private final static int SEND_PRIORITY = 500;
/**
* If the client's config specifies shouldBundleReplyInfo=true, messages sent from
* that client to any peers will probabalistically include the sending destination's
* current LeaseSet (allowing the recipient to reply without having to do a full
* netDb lookup). This should improve performance during the initial negotiations,
* but is not necessary for communication that isn't bidirectional.
*
*/
public static final String BUNDLE_REPLY_LEASESET = "shouldBundleReplyInfo";
/**
* Allow the override of the frequency of bundling the reply info in with a message.
* The client app can specify bundleReplyInfoProbability=80 (for instance) and that
* will cause the router to include the sender's leaseSet with 80% of the messages
* sent to the peer.
*
*/
public static final String BUNDLE_PROBABILITY = "bundleReplyInfoProbability";
/**
* How often do messages include the reply leaseSet (out of every 100 tries).
* Including it each time is probably overkill, but who knows.
*/
private static final int BUNDLE_PROBABILITY_DEFAULT = 100;
/**
* Send the sucker
*/
public OutboundClientMessageOneShotJob(RouterContext ctx, ClientMessage msg) {
super(ctx);
_log = ctx.logManager().getLog(OutboundClientMessageOneShotJob.class);
ctx.statManager().createFrequencyStat("client.sendMessageFailFrequency", "How often does a client fail to send a message?", "ClientMessages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.sendMessageSize", "How large are messages sent by the client?", "ClientMessages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.sendAckTime", "How long does it take to get an ACK back from a message?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.timeoutCongestionTunnel", "How lagged our tunnels are when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.timeoutCongestionMessage", "How fast we process messages locally when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.timeoutCongestionInbound", "How much faster we are receiving data than our average bps when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
long timeoutMs = OVERALL_TIMEOUT_MS_DEFAULT;
_clientMessage = msg;
_clientMessageId = msg.getMessageId();
_clientMessageSize = msg.getPayload().getSize();
_from = msg.getFromDestination();
_to = msg.getDestination();
String param = msg.getSenderConfig().getOptions().getProperty(OVERALL_TIMEOUT_MS_PARAM);
if (param == null)
param = ctx.router().getConfigSetting(OVERALL_TIMEOUT_MS_PARAM);
if (param != null) {
try {
timeoutMs = Long.parseLong(param);
} catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Invalid client message timeout specified [" + param
+ "], defaulting to " + OVERALL_TIMEOUT_MS_DEFAULT, nfe);
timeoutMs = OVERALL_TIMEOUT_MS_DEFAULT;
}
}
_start = getContext().clock().now();
_overallExpiration = timeoutMs + _start;
_shouldBundle = getShouldBundle();
_finished = false;
}
public String getName() { return "Outbound client message"; }
public void runJob() {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getJobId() + ": Send outbound client message job beginning");
buildClove();
if (_log.shouldLog(Log.DEBUG))
_log.debug(getJobId() + ": Clove built");
long timeoutMs = _overallExpiration - getContext().clock().now();
if (_log.shouldLog(Log.DEBUG))
_log.debug(getJobId() + ": Send outbound client message - sending off leaseSet lookup job");
getContext().netDb().lookupLeaseSet(_to.calculateHash(), new SendJob(), new LookupLeaseSetFailedJob(), timeoutMs);
}
private boolean getShouldBundle() {
Properties opts = _clientMessage.getSenderConfig().getOptions();
String wantBundle = opts.getProperty(BUNDLE_REPLY_LEASESET, "true");
if ("true".equals(wantBundle)) {
int probability = BUNDLE_PROBABILITY_DEFAULT;
String str = opts.getProperty(BUNDLE_PROBABILITY);
try {
if (str != null)
probability = Integer.parseInt(str);
} catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.WARN))
_log.warn(getJobId() + ": Bundle leaseSet probability overridden incorrectly ["
+ str + "]", nfe);
}
if (probability >= getContext().random().nextInt(100))
return true;
else
return false;
} else {
return false;
}
}
/** send a message to a random lease */
private class SendJob extends JobImpl {
public SendJob() {
super(OutboundClientMessageOneShotJob.this.getContext());
}
public String getName() { return "Send outbound client message through the lease"; }
public void runJob() {
boolean ok = getNextLease();
if (ok)
send();
else
dieFatal();
}
}
/**
* fetch the next lease that we should try sending through, randomly chosen
* from within the sorted leaseSet (sorted by # of failures through each
* lease).
*
*/
private boolean getNextLease() {
_leaseSet = getContext().netDb().lookupLeaseSetLocally(_to.calculateHash());
if (_leaseSet == null) {
if (_log.shouldLog(Log.WARN))
_log.warn(getJobId() + ": Lookup locally didn't find the leaseSet");
return false;
}
long now = getContext().clock().now();
// get the possible leases
List leases = new ArrayList(_leaseSet.getLeaseCount());
for (int i = 0; i < _leaseSet.getLeaseCount(); i++) {
Lease lease = _leaseSet.getLease(i);
if (lease.isExpired(Router.CLOCK_FUDGE_FACTOR)) {
if (_log.shouldLog(Log.WARN))
_log.warn(getJobId() + ": getNextLease() - expired lease! - " + lease);
continue;
} else {
leases.add(lease);
}
}
if (leases.size() <= 0) {
if (_log.shouldLog(Log.INFO))
_log.info(getJobId() + ": No leases found from: " + _leaseSet);
return false;
}
// randomize the ordering (so leases with equal # of failures per next
// sort are randomly ordered)
Collections.shuffle(leases);
// ordered by lease number of failures
TreeMap orderedLeases = new TreeMap();
for (Iterator iter = leases.iterator(); iter.hasNext(); ) {
Lease lease = (Lease)iter.next();
long id = lease.getNumFailure();
while (orderedLeases.containsKey(new Long(id)))
id++;
orderedLeases.put(new Long(id), lease);
if (_log.shouldLog(Log.DEBUG))
_log.debug(getJobId() + ": ranking lease we havent sent it down as " + id);
}
_lease = (Lease)orderedLeases.get(orderedLeases.firstKey());
return true;
}
/**
* we couldn't even find the leaseSet, but try again (or die
* if we've already tried too hard)
*
*/
private class LookupLeaseSetFailedJob extends JobImpl {
public LookupLeaseSetFailedJob() {
super(OutboundClientMessageOneShotJob.this.getContext());
}
public String getName() { return "Lookup for outbound client message failed"; }
public void runJob() {
dieFatal();
}
}
/**
* Send the message to the specified tunnel by creating a new garlic message containing
* the (already created) payload clove as well as a new delivery status message. This garlic
* message is sent out one of our tunnels, destined for the lease (tunnel+router) specified, and the delivery
* status message is targetting one of our free inbound tunnels as well. We use a new
* reply selector to keep an eye out for that delivery status message's token
*
*/
private void send() {
if (_finished) return;
long token = getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE);
PublicKey key = _leaseSet.getEncryptionKey();
SessionKey sessKey = new SessionKey();
Set tags = new HashSet();
LeaseSet replyLeaseSet = null;
if (_shouldBundle) {
replyLeaseSet = getContext().netDb().lookupLeaseSetLocally(_from.calculateHash());
}
GarlicMessage msg = OutboundClientMessageJobHelper.createGarlicMessage(getContext(), token,
_overallExpiration, key,
_clove,
_to,
sessKey, tags,
true, replyLeaseSet);
if (_log.shouldLog(Log.DEBUG))
_log.debug(getJobId() + ": send() - token expected " + token);
SendSuccessJob onReply = new SendSuccessJob(sessKey, tags);
SendTimeoutJob onFail = new SendTimeoutJob();
ReplySelector selector = new ReplySelector(token);
if (_log.shouldLog(Log.DEBUG))
_log.debug(getJobId() + ": Placing GarlicMessage into the new tunnel message bound for "
+ _lease.getTunnelId() + " on "
+ _lease.getRouterIdentity().getHash().toBase64());
TunnelId outTunnelId = selectOutboundTunnel();
if (outTunnelId != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getJobId() + ": Sending tunnel message out " + outTunnelId + " to "
+ _lease.getTunnelId() + " on "
+ _lease.getRouterIdentity().getHash().toBase64());
SendTunnelMessageJob j = new SendTunnelMessageJob(getContext(), msg, outTunnelId,
_lease.getRouterIdentity().getHash(),
_lease.getTunnelId(), null, onReply,
onFail, selector,
_overallExpiration-getContext().clock().now(),
SEND_PRIORITY);
getContext().jobQueue().addJob(j);
} else {
if (_log.shouldLog(Log.ERROR))
_log.error(getJobId() + ": Could not find any outbound tunnels to send the payload through... wtf?");
dieFatal();
}
_clientMessage = null;
_clove = null;
}
/**
* Pick an arbitrary outbound tunnel to send the message through, or null if
* there aren't any around
*
*/
private TunnelId selectOutboundTunnel() {
TunnelSelectionCriteria crit = new TunnelSelectionCriteria();
crit.setMaximumTunnelsRequired(1);
crit.setMinimumTunnelsRequired(1);
List tunnelIds = getContext().tunnelManager().selectOutboundTunnelIds(crit);
if (tunnelIds.size() <= 0)
return null;
else
return (TunnelId)tunnelIds.get(0);
}
/**
* give up the ghost, this message just aint going through. tell the client to fuck off.
*
* this is safe to call multiple times (only tells the client once)
*/
private void dieFatal() {
if (_finished) return;
_finished = true;
long sendTime = getContext().clock().now() - _start;
if (_log.shouldLog(Log.WARN))
_log.warn(getJobId() + ": Failed to send the message " + _clientMessageId + " after "
+ sendTime + "ms", new Exception("Message send failure"));
long messageDelay = getContext().throttle().getMessageDelay();
long tunnelLag = getContext().throttle().getTunnelLag();
long inboundDelta = (long)getContext().throttle().getInboundRateDelta();
getContext().statManager().addRateData("client.timeoutCongestionTunnel", tunnelLag, 1);
getContext().statManager().addRateData("client.timeoutCongestionMessage", messageDelay, 1);
getContext().statManager().addRateData("client.timeoutCongestionInbound", inboundDelta, 1);
getContext().messageHistory().sendPayloadMessage(_clientMessageId.getMessageId(), false, sendTime);
getContext().clientManager().messageDeliveryStatusUpdate(_from, _clientMessageId, false);
getContext().statManager().updateFrequency("client.sendMessageFailFrequency");
_clientMessage = null;
_clove = null;
}
/** build the payload clove that will be used for all of the messages, placing the clove in the status structure */
private void buildClove() {
PayloadGarlicConfig clove = new PayloadGarlicConfig();
DeliveryInstructions instructions = new DeliveryInstructions();
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_DESTINATION);
instructions.setDestination(_to.calculateHash());
instructions.setDelayRequested(false);
instructions.setDelaySeconds(0);
instructions.setEncrypted(false);
clove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
clove.setDeliveryInstructions(instructions);
clove.setExpiration(_overallExpiration);
clove.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
DataMessage msg = new DataMessage(getContext());
msg.setData(_clientMessage.getPayload().getEncryptedData());
clove.setPayload(msg);
clove.setRecipientPublicKey(null);
clove.setRequestAck(false);
_clove = clove;
_cloveId = _clove.getId();
if (_log.shouldLog(Log.DEBUG))
_log.debug(getJobId() + ": Built payload clove with id " + clove.getId());
}
/**
* Keep an eye out for any of the delivery status message tokens that have been
* sent down the various tunnels to deliver this message
*
*/
private class ReplySelector implements MessageSelector {
private long _pendingToken;
public ReplySelector(long token) {
_pendingToken = token;
}
public boolean continueMatching() { return false; }
public long getExpiration() { return _overallExpiration; }
public boolean isMatch(I2NPMessage inMsg) {
if (inMsg.getType() == DeliveryStatusMessage.MESSAGE_TYPE) {
return _pendingToken == ((DeliveryStatusMessage)inMsg).getMessageId();
} else {
return false;
}
}
}
/**
* Called after we get a confirmation that the message was delivered safely
* (hoo-ray!)
*
*/
private class SendSuccessJob extends JobImpl implements ReplyJob {
private SessionKey _key;
private Set _tags;
/**
* Create a new success job that will be fired when the message encrypted with
* the given session key and bearing the specified tags are confirmed delivered.
*
*/
public SendSuccessJob(SessionKey key, Set tags) {
super(OutboundClientMessageOneShotJob.this.getContext());
_key = key;
_tags = tags;
}
public String getName() { return "Send client message successful to a lease"; }
public void runJob() {
if (_finished) return;
_finished = true;
long sendTime = getContext().clock().now() - _start;
if (_log.shouldLog(Log.INFO))
_log.info(OutboundClientMessageOneShotJob.this.getJobId()
+ ": SUCCESS! msg " + _clientMessageId
+ " sent after " + sendTime + "ms");
if ( (_key != null) && (_tags != null) && (_tags.size() > 0) ) {
if (_leaseSet != null)
getContext().sessionKeyManager().tagsDelivered(_leaseSet.getEncryptionKey(),
_key, _tags);
}
long dataMsgId = _cloveId;
getContext().messageHistory().sendPayloadMessage(dataMsgId, true, sendTime);
getContext().clientManager().messageDeliveryStatusUpdate(_from, _clientMessageId, true);
_lease.setNumSuccess(_lease.getNumSuccess()+1);
getContext().statManager().addRateData("client.sendAckTime", sendTime, 0);
getContext().statManager().addRateData("client.sendMessageSize", _clientMessageSize, sendTime);
}
public void setMessage(I2NPMessage msg) {}
}
/**
* Fired after the basic timeout for sending through the given tunnel has been reached.
* We'll accept successes later, but won't expect them
*
*/
private class SendTimeoutJob extends JobImpl {
public SendTimeoutJob() {
super(OutboundClientMessageOneShotJob.this.getContext());
}
public String getName() { return "Send client message timed out through a lease"; }
public void runJob() {
if (_log.shouldLog(Log.DEBUG))
_log.debug(OutboundClientMessageOneShotJob.this.getJobId()
+ ": Soft timeout through the lease " + _lease);
_lease.setNumFailure(_lease.getNumFailure()+1);
dieFatal();
}
}
}

View File

@ -121,7 +121,7 @@ public class SendTunnelMessageJob extends JobImpl {
}
}
info.messageProcessed();
info.messageProcessed(_message.getMessageSize());
if (isEndpoint(info)) {
if (_log.shouldLog(Log.INFO))

View File

@ -54,7 +54,12 @@ public class PublishLocalRouterInfoJob extends JobImpl {
_log.info("Newly updated routerInfo is published with " + stats.size()
+ "/" + ri.getOptions().size() + " options on "
+ new Date(ri.getPublished()));
getContext().netDb().publish(ri);
try {
getContext().netDb().publish(ri);
} catch (IllegalArgumentException iae) {
_log.log(Log.CRIT, "Error publishing our identity - corrupt?", iae);
getContext().router().rebuildNewIdentity();
}
} catch (DataFormatException dfe) {
_log.error("Error signing the updated local router info!", dfe);
}

View File

@ -487,18 +487,17 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
}
}
public void publish(RouterInfo localRouterInfo) {
/**
* @throws IllegalArgumentException if the local router info is invalid
*/
public void publish(RouterInfo localRouterInfo) throws IllegalArgumentException {
if (!_initialized) return;
Hash h = localRouterInfo.getIdentity().getHash();
try {
store(h, localRouterInfo);
synchronized (_explicitSendKeys) {
_explicitSendKeys.add(h);
}
writeMyInfo(localRouterInfo);
} catch (IllegalArgumentException iae) {
_log.error("Local routerInfo was invalid? "+ iae.getMessage(), iae);
store(h, localRouterInfo);
synchronized (_explicitSendKeys) {
_explicitSendKeys.add(h);
}
writeMyInfo(localRouterInfo);
}
/**

View File

@ -147,8 +147,10 @@ class PersistentDataStore extends TransientDataStore {
}
private class ReadJob extends JobImpl {
private boolean _alreadyWarned;
public ReadJob() {
super(PersistentDataStore.this._context);
_alreadyWarned = false;
}
public String getName() { return "DB Read Job"; }
public void runJob() {
@ -158,6 +160,7 @@ class PersistentDataStore extends TransientDataStore {
}
private void readFiles() {
int routerCount = 0;
try {
File dbDir = getDbDir();
File leaseSetFiles[] = dbDir.listFiles(LeaseSetFilter.getInstance());
@ -170,6 +173,9 @@ class PersistentDataStore extends TransientDataStore {
}
File routerInfoFiles[] = dbDir.listFiles(RouterInfoFilter.getInstance());
if (routerInfoFiles != null) {
routerCount += routerInfoFiles.length;
if (routerInfoFiles.length > 5)
_alreadyWarned = false;
for (int i = 0; i < routerInfoFiles.length; i++) {
Hash key = getRouterInfoHash(routerInfoFiles[i].getName());
if ( (key != null) && (!isKnown(key)) )
@ -179,6 +185,11 @@ class PersistentDataStore extends TransientDataStore {
} catch (IOException ioe) {
_log.error("Error reading files in the db dir", ioe);
}
if ( (routerCount <= 5) && (!_alreadyWarned) ) {
_log.error("Very few routerInfo files remaining - please reseed");
_alreadyWarned = true;
}
}
}

View File

@ -45,18 +45,25 @@ public class CapacityCalculator extends Calculator {
private static long ESTIMATE_PERIOD = 60*60*1000;
public double calc(PeerProfile profile) {
double capacity = 0;
RateStat acceptStat = profile.getTunnelCreateResponseTime();
RateStat rejectStat = profile.getTunnelHistory().getRejectionRate();
RateStat failedStat = profile.getTunnelHistory().getFailedRate();
capacity += estimatePartial(acceptStat, rejectStat, failedStat, 10*60*1000);
capacity += estimatePartial(acceptStat, rejectStat, failedStat, 30*60*1000);
capacity += estimatePartial(acceptStat, rejectStat, failedStat, 60*60*1000);
capacity += estimatePartial(acceptStat, rejectStat, failedStat, 24*60*60*1000);
double capacity10m = estimateCapacity(acceptStat, rejectStat, failedStat, 10*60*1000);
double capacity30m = estimateCapacity(acceptStat, rejectStat, failedStat, 30*60*1000);
double capacity60m = estimateCapacity(acceptStat, rejectStat, failedStat, 60*60*1000);
double capacity1d = estimateCapacity(acceptStat, rejectStat, failedStat, 24*60*60*1000);
if (tooOld(profile))
double capacity = capacity10m * periodWeight(10*60*1000) +
capacity30m * periodWeight(30*60*1000) +
capacity60m * periodWeight(60*60*1000) +
capacity1d * periodWeight(24*60*60*1000);
// if we actively know they're bad, who cares if they used to be good?
if (capacity10m <= 0)
capacity = 0;
if (tooOld(profile))
capacity = 1;
capacity += profile.getReliabilityBonus();
@ -74,7 +81,7 @@ public class CapacityCalculator extends Calculator {
return true;
}
private double estimatePartial(RateStat acceptStat, RateStat rejectStat, RateStat failedStat, int period) {
private double estimateCapacity(RateStat acceptStat, RateStat rejectStat, RateStat failedStat, int period) {
Rate curAccepted = acceptStat.getRate(period);
Rate curRejected = rejectStat.getRate(period);
Rate curFailed = failedStat.getRate(period);
@ -82,27 +89,29 @@ public class CapacityCalculator extends Calculator {
long eventCount = 0;
if (curAccepted != null)
eventCount = curAccepted.getCurrentEventCount() + curAccepted.getLastEventCount();
double stretch = ESTIMATE_PERIOD / period;
double stretch = ((double)ESTIMATE_PERIOD) / period;
double val = eventCount * stretch;
long failed = 0;
if (curFailed != null)
failed = curFailed.getCurrentEventCount() + curFailed.getLastEventCount();
if (failed > 0) {
if ( (period == 10*60*1000) && (curFailed.getCurrentEventCount() > 0) )
return 0.0d; // their tunnels have failed in the last 0-10 minutes
else
val -= failed * stretch;
//if ( (period <= 10*60*1000) && (curFailed.getCurrentEventCount() > 0) )
// return 0.0d; // their tunnels have failed in the last 0-10 minutes
//else
val -= failed * stretch;
}
if ( (period == 10*60*1000) && (curRejected.getCurrentEventCount() + curRejected.getLastEventCount() > 0) )
return 0.0d;
else
//if ( (period <= 10*60*1000) && (curRejected.getCurrentEventCount() + curRejected.getLastEventCount() > 0) ) {
// //System.out.println("10m period has rejected " + (curRejected.getCurrentEventCount() + curRejected.getLastEventCount()) + " times");
// return 0.0d;
//} else
val -= stretch * (curRejected.getCurrentEventCount() + curRejected.getLastEventCount());
val += GROWTH_FACTOR;
if (val >= 0) {
return (val + GROWTH_FACTOR) * periodWeight(period);
return val;
} else {
// failed too much, don't grow
return 0.0d;
}
}

View File

@ -248,7 +248,7 @@ public class PeerProfile {
if (_dbResponseTime == null)
_dbResponseTime = new RateStat("dbResponseTime", "how long it takes to get a db response from the peer (in milliseconds)", group, new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000 } );
if (_tunnelCreateResponseTime == null)
_tunnelCreateResponseTime = new RateStat("tunnelCreateResponseTime", "how long it takes to get a tunnel create response from the peer (in milliseconds)", group, new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000 } );
_tunnelCreateResponseTime = new RateStat("tunnelCreateResponseTime", "how long it takes to get a tunnel create response from the peer (in milliseconds)", group, new long[] { 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000 } );
if (_tunnelTestResponseTime == null)
_tunnelTestResponseTime = new RateStat("tunnelTestResponseTime", "how long it takes to successfully test a tunnel this peer participates in (in milliseconds)", group, new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000 } );
if (_commError == null)

View File

@ -109,7 +109,8 @@ class ProfileOrganizerRenderer {
buf.append("<td align=\"right\">").append(prof.getIsFailing()).append("</td>");
//buf.append("<td><a href=\"/profile/").append(prof.getPeer().toBase64().substring(0, 32)).append("\">profile.txt</a> ");
//buf.append(" <a href=\"#").append(prof.getPeer().toBase64().substring(0, 32)).append("\">netDb</a></td>");
buf.append("<td><a href=\"netdb.jsp#").append(peer.toBase64().substring(0,6)).append("\">netDb</a></td>\n");
buf.append("<td nowrap><a href=\"netdb.jsp#").append(peer.toBase64().substring(0,6)).append("\">netDb</a>");
buf.append("/<a href=\"dumpprofile.jsp?peer=").append(peer.toBase64().substring(0,6)).append("\">profile</a></td>\n");
buf.append("</tr>");
}
buf.append("</table>");

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