Compare commits

...

16 Commits

Author SHA1 Message Date
jrandom
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
jrandom
34ecfd9857 added j.i2p 2005-01-06 11:56:35 +00:00
jrandom
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
jrandom
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
scintilla
27999983cc added chat.i2p 2005-01-03 02:36:32 +00:00
jrandom
48b039940d added phonebooth.i2p 2005-01-01 05:18:01 +00:00
jrandom
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
jrandom
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
jrandom
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
jrandom
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
jrandom
00a5d42d3d forgot to import these... 2004-12-29 20:51:43 +00:00
jrandom
28f4a2cb67 imported ragnarok's MIT licensed addressbook-2.0.2 2004-12-29 20:28:20 +00:00
jrandom
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
jrandom
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
54 changed files with 2004 additions and 104 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

@@ -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);

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;
/**
@@ -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)
*

View File

@@ -257,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, "");
}

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

@@ -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

@@ -33,6 +33,7 @@ public class Connection {
private long _lastSendId;
private boolean _resetReceived;
private boolean _resetSent;
private long _resetSentOn;
private boolean _connected;
private boolean _hardDisconnected;
private MessageInputStream _inputStream;
@@ -72,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;
@@ -113,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 });
@@ -146,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 {
@@ -180,6 +187,8 @@ public class Connection {
*/
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);
@@ -242,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);
}
@@ -383,6 +392,7 @@ public class Connection {
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);
@@ -688,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;
}
@@ -752,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);
}
@@ -763,7 +779,7 @@ public class Connection {
if (_packet.getAckTime() > 0)
return;
if (!_connected) {
if (_resetSent || _resetReceived) {
_packet.cancelled();
return;
}
@@ -771,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
@@ -808,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
@@ -828,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

@@ -114,9 +114,9 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
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;
}
@@ -165,7 +165,7 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
// packets sent, otherwise the other side could receive the CLOSE prematurely,
// since this ACK could arrive before the unacked payload message.
if (con.getOutputStream().getClosed() &&
( (size > 0) || (con.getUnackedPacketsSent() <= 0) ) ) {
( (size > 0) || (con.getUnackedPacketsSent() <= 0) || (packet.getSequenceNum() > 0) ) ) {
packet.setFlag(Packet.FLAG_CLOSE);
con.setCloseSentOn(_context.clock().now());
if (_log.shouldLog(Log.DEBUG))

View File

@@ -56,7 +56,7 @@ public class ConnectionPacketHandler {
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
@@ -106,14 +106,11 @@ public class ConnectionPacketHandler {
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());
fastAck = true;
//fastAck = true;
} else {
if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) {
//con.incrementUnackedPacketsReceived();
@@ -128,7 +125,7 @@ public class ConnectionPacketHandler {
fastAck = fastAck || ack(con, packet.getAckThrough(), packet.getNacks(), packet, isNew);
con.eventOccurred();
if (fastAck) {
if (con.getLastSendTime() + 1000 < _context.clock().now()) {
if (con.getLastSendTime() + 2000 < _context.clock().now()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Fast ack for dup " + packet);
con.ackImmediately();

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

@@ -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

@@ -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

@@ -34,6 +34,8 @@ class SchedulerHardDisconnected 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 ok = (con.getHardDisconnected() || con.getResetSent()) &&
(timeSinceClose < Connection.DISCONNECT_TIMEOUT);
return ok;

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

@@ -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

@@ -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;

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

@@ -1,4 +1,65 @@
$Id: history.txt,v 1.117 2004/12/21 11:32:50 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

View File

@@ -1,6 +1,11 @@
; TC's hosts.txt guaranteed freshness
; $Id: hosts.txt,v 1.98 2004/12/18 01:31:22 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
@@ -138,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
@@ -155,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
@@ -270,4 +273,8 @@ sciencebooks.i2p=Rrqf6EmeuqckygNrim-ZcZ97uEIL9Ykkr8cj1RSGbeih33~UUpjAp0x2fxrpbib
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

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

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

@@ -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

@@ -261,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
@@ -813,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

@@ -50,7 +50,7 @@ class RouterThrottleImpl implements RouterThrottle {
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);

View File

@@ -15,8 +15,8 @@ import net.i2p.CoreVersion;
*
*/
public class RouterVersion {
public final static String ID = "$Revision: 1.122 $ $Date: 2004/12/21 11:32:50 $";
public final static String VERSION = "0.4.2.5";
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

@@ -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

@@ -4,7 +4,9 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import net.i2p.data.DataHelper;
import net.i2p.data.DataFormatException;
import net.i2p.data.RouterInfo;
import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.DeliveryStatusMessage;
import net.i2p.router.OutNetMessage;
@@ -12,6 +14,7 @@ import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer;
/**
* Push out I2NPMessages across the wire
@@ -24,6 +27,8 @@ class ConnectionRunner implements Runnable {
private boolean _keepRunning;
private byte _writeBuffer[];
private long _lastTimeSend;
private long _lastWriteEnd;
private long _lastWriteBegin;
private static final long TIME_SEND_FREQUENCY = 60*1000;
@@ -32,6 +37,8 @@ class ConnectionRunner implements Runnable {
_log = ctx.logManager().getLog(ConnectionRunner.class);
_con = con;
_keepRunning = false;
_lastWriteBegin = ctx.clock().now();
_lastWriteEnd = _lastWriteBegin;
}
public void startRunning() {
@@ -44,6 +51,9 @@ class ConnectionRunner implements Runnable {
+ _con.getRemoteRouterIdentity().calculateHash().toBase64().substring(0,6);
I2PThread t = new I2PThread(this, name);
t.start();
long delay = TIME_SEND_FREQUENCY + _context.random().nextInt(60*1000);
SimpleTimer.getInstance().addEvent(new KeepaliveEvent(), delay);
}
public void stopRunning() {
@@ -54,7 +64,7 @@ class ConnectionRunner implements Runnable {
while (_keepRunning && !_con.getIsClosed()) {
OutNetMessage msg = _con.getNextMessage();
if (msg == null) {
if (_keepRunning)
if (_keepRunning && !_con.getIsClosed())
_log.error("next message is null but we should keep running?");
_con.closeConnection();
return;
@@ -98,23 +108,21 @@ class ConnectionRunner implements Runnable {
OutputStream out = _con.getOutputStream();
boolean ok = false;
long before = -1;
long after = -1;
try {
synchronized (out) {
before = _context.clock().now();
_lastWriteBegin = _context.clock().now();
out.write(buf, 0, written);
if (sendTime) {
out.write(buildTimeMessage().toByteArray());
_lastTimeSend = _context.clock().now();
}
out.flush();
after = _context.clock().now();
_lastWriteEnd = _context.clock().now();
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Just sent message " + msg.getMessageId() + " to "
+ msg.getTarget().getIdentity().getHash().toBase64().substring(0,6)
+ " writeTime = " + (after-before) +"ms"
+ " writeTime = " + (_lastWriteEnd - _lastWriteBegin) +"ms"
+ " lifetime = " + msg.getLifetime() + "ms");
ok = true;
@@ -123,7 +131,7 @@ class ConnectionRunner implements Runnable {
_log.warn("Error writing out the message", ioe);
_con.closeConnection();
}
_con.sent(msg, ok, after - before);
_con.sent(msg, ok, _lastWriteEnd - _lastWriteBegin);
}
/**
@@ -139,4 +147,54 @@ class ConnectionRunner implements Runnable {
tm.setUniqueId(0);
return tm;
}
/**
* Every few minutes, send a (tiny) message to the peer if we haven't
* spoken with them recently. This will help kill off any hung
* connections (due to IP address changes, etc). If we don't get any
* messages through in 5 minutes, kill the connection as well.
*
*/
private class KeepaliveEvent implements SimpleTimer.TimedEvent {
public void timeReached() {
if (!_keepRunning) return;
if (_con.getIsClosed()) return;
long now = _context.clock().now();
long timeSinceWrite = now - _lastWriteEnd;
long timeSinceWriteBegin = now - _lastWriteBegin;
if (timeSinceWrite > 5*TIME_SEND_FREQUENCY) {
TCPTransport t = _con.getTransport();
String msg = "Connection closed with "
+ _con.getRemoteRouterIdentity().getHash().toBase64().substring(0,6)
+ " due to " + DataHelper.formatDuration(timeSinceWrite)
+ " of inactivity after "
+ DataHelper.formatDuration(_con.getLifetime());
if (_lastWriteBegin > _lastWriteEnd)
msg = msg + " with a message being written for " +
DataHelper.formatDuration(timeSinceWriteBegin);
t.addConnectionErrorMessage(msg);
if (_log.shouldLog(Log.INFO))
_log.info(msg);
_con.closeConnection(false);
return;
}
if (_lastTimeSend < _context.clock().now() - 2*TIME_SEND_FREQUENCY)
enqueueTimeMessage();
long delay = 2*TIME_SEND_FREQUENCY + _context.random().nextInt((int)TIME_SEND_FREQUENCY);
SimpleTimer.getInstance().addEvent(KeepaliveEvent.this, delay);
}
}
private void enqueueTimeMessage() {
OutNetMessage msg = new OutNetMessage(_context);
RouterInfo ri = _context.netDb().lookupRouterInfoLocally(_con.getRemoteRouterIdentity().getHash());
if (ri == null) return;
msg.setTarget(ri);
msg.setExpiration(_context.clock().now() + TIME_SEND_FREQUENCY);
msg.setMessage(buildTimeMessage());
msg.setPriority(100);
_con.addMessage(msg);
if (_log.shouldLog(Log.INFO))
_log.info("Enqueueing time message to " + _con.getRemoteRouterIdentity().getHash().toBase64().substring(0,6));
}
}

View File

@@ -86,6 +86,7 @@ public class TCPConnection {
/** skew that the other peer has from our clock */
public long getOffsetReceived() { return _offsetReceived; }
public void setOffsetReceived(long ms) { _offsetReceived = ms; }
public TCPTransport getTransport() { return _transport; }
/**
* Actually start processing the messages on the connection (and reading
@@ -110,7 +111,8 @@ public class TCPConnection {
* be called multiple times safely.
*
*/
public synchronized void closeConnection() {
public synchronized void closeConnection() { closeConnection(true); }
public synchronized void closeConnection(boolean wasError) {
if (_log.shouldLog(Log.INFO)) {
if (_ident != null)
_log.info("Connection between " + _ident.getHash().toBase64().substring(0,6)
@@ -139,10 +141,12 @@ public class TCPConnection {
msg.timestamp("closeConnection");
_transport.afterSend(msg, false, true, -1);
}
_context.profileManager().commErrorOccurred(_ident.getHash());
_transport.addConnectionErrorMessage("Connection closed with "
+ _ident.getHash().toBase64().substring(0,6)
+ " after " + DataHelper.formatDuration(getLifetime()));
if (wasError) {
_context.profileManager().commErrorOccurred(_ident.getHash());
_transport.addConnectionErrorMessage("Connection closed with "
+ _ident.getHash().toBase64().substring(0,6)
+ " after " + DataHelper.formatDuration(getLifetime()));
}
_transport.connectionClosed(this);
}
@@ -168,10 +172,16 @@ public class TCPConnection {
msg.timestamp("TCPConnection.addMessage");
List expired = null;
int remaining = 0;
long remainingSize = 0;
synchronized (_pendingMessages) {
_pendingMessages.add(msg);
expired = locked_expireOld();
locked_throttle();
for (int i = 0; i < _pendingMessages.size(); i++) {
OutNetMessage cur = (OutNetMessage)_pendingMessages.get(i);
remaining++;
remainingSize += cur.getMessageSize();
}
remaining = _pendingMessages.size();
_pendingMessages.notifyAll();
}
@@ -182,8 +192,8 @@ public class TCPConnection {
if (_log.shouldLog(Log.WARN))
_log.warn("Message " + cur.getMessageId() + " expired on the queue to "
+ _ident.getHash().toBase64().substring(0,6)
+ " (queue size " + remaining + ") with lifetime "
+ cur.getLifetime());
+ " (queue size " + remaining + "/" + remainingSize + ") with lifetime "
+ cur.getLifetime() + " and size " + cur.getMessageSize());
sent(cur, false, 0);
}
}

View File

@@ -559,7 +559,7 @@ class TunnelPool {
}
if (_log.shouldLog(Log.WARN))
_log.warn("Tunnel " + id + " marked as not ready, since it /failed/", new Exception("Failed tunnel"));
_log.warn("Tunnel " + id + " marked as not ready, since it /failed/: " + info.toString(), new Exception("Failed tunnel"));
_context.messageHistory().tunnelFailed(info.getTunnelId());
info.setIsReady(false);
Hash us = _context.routerHash();