forked from I2P_Developers/i2p.i2p
Compare commits
52 Commits
i2p_0_4_2_
...
i2p_0_4_2_
Author | SHA1 | Date | |
---|---|---|---|
daf32a24bc | |||
34ecfd9857 | |||
4838564460 | |||
3dd2f67ff3 | |||
0ccec3dde0 | |||
ad77879caa | |||
27999983cc | |||
48b039940d | |||
84dc7d9d82 | |||
70d6332bad | |||
aec0b0c86a | |||
099f6a88c2 | |||
00a5d42d3d | |||
28f4a2cb67 | |||
1ac18ba10e | |||
1503ee2dfa | |||
484b528d4f | |||
758293dc02 | |||
6cb316b33e | |||
1d31831e7d | |||
ee32b07995 | |||
81f04ca692 | |||
1756997608 | |||
ec11ea4ca7 | |||
a1ebf85e1b | |||
4b2a734cda | |||
97ae8f78a0 | |||
834665c3ba | |||
d969dd2d8d | |||
3cb727561c | |||
cbc89376d3 | |||
66aa29e3d4 | |||
5c72aca5ee | |||
8824815d6d | |||
ad72e5cbdf | |||
b2f183fc17 | |||
9e16bc203a | |||
83c6eac017 | |||
d5b277a536 | |||
77ce6c33e3 | |||
60f8d349cf | |||
f539c3df70 | |||
fe1cf1758c | |||
8c71c26487 | |||
2ce39d1fd4 | |||
88a994b712 | |||
24c8cc1a0c | |||
caf684394c | |||
4b74510450 | |||
0ddcfc423e | |||
b4ac56e204 | |||
3b19ac3942 |
7
apps/addressbook/.classpath
Normal file
7
apps/addressbook/.classpath
Normal 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
17
apps/addressbook/.project
Normal 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>
|
43
apps/addressbook/README.txt
Normal file
43
apps/addressbook/README.txt
Normal 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.
|
46
apps/addressbook/build.xml
Normal file
46
apps/addressbook/build.xml
Normal 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>
|
43
apps/addressbook/config.txt
Normal file
43
apps/addressbook/config.txt
Normal 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
|
246
apps/addressbook/java/src/addressbook/AddressBook.java
Normal file
246
apps/addressbook/java/src/addressbook/AddressBook.java
Normal 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));
|
||||
}
|
||||
}
|
323
apps/addressbook/java/src/addressbook/ConfigParser.java
Normal file
323
apps/addressbook/java/src/addressbook/ConfigParser.java
Normal 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)));
|
||||
}
|
||||
|
||||
}
|
171
apps/addressbook/java/src/addressbook/Daemon.java
Normal file
171
apps/addressbook/java/src/addressbook/Daemon.java
Normal 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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
53
apps/addressbook/java/src/addressbook/DaemonThread.java
Normal file
53
apps/addressbook/java/src/addressbook/DaemonThread.java
Normal 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);
|
||||
}
|
||||
}
|
76
apps/addressbook/java/src/addressbook/Log.java
Normal file
76
apps/addressbook/java/src/addressbook/Log.java
Normal 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;
|
||||
}
|
||||
}
|
61
apps/addressbook/java/src/addressbook/Servlet.java
Normal file
61
apps/addressbook/java/src/addressbook/Servlet.java
Normal 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]);
|
||||
}
|
||||
|
||||
}
|
105
apps/addressbook/java/src/addressbook/Subscription.java
Normal file
105
apps/addressbook/java/src/addressbook/Subscription.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
123
apps/addressbook/java/src/addressbook/SubscriptionList.java
Normal file
123
apps/addressbook/java/src/addressbook/SubscriptionList.java
Normal 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) {
|
||||
}
|
||||
}
|
||||
}
|
10
apps/addressbook/myhosts.txt
Normal file
10
apps/addressbook/myhosts.txt
Normal 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
|
7
apps/addressbook/subscriptions.txt
Normal file
7
apps/addressbook/subscriptions.txt
Normal 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
16
apps/addressbook/web.xml
Normal 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>
|
@ -229,6 +229,8 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
runClientOptions(args, l);
|
||||
} else if ("server".equals(cmdname)) {
|
||||
runServer(args, l);
|
||||
} else if ("httpserver".equals(cmdname)) {
|
||||
runHttpServer(args, l);
|
||||
} else if ("textserver".equals(cmdname)) {
|
||||
runTextServer(args, l);
|
||||
} else if ("client".equals(cmdname)) {
|
||||
@ -281,6 +283,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
l.log("owndest yes|no");
|
||||
l.log("ping <args>");
|
||||
l.log("server <host> <port> <privkeyfile>");
|
||||
l.log("httpserver <host> <port> <spoofedhost> <privkeyfile>");
|
||||
l.log("textserver <host> <port> <privkey>");
|
||||
l.log("genkeys <privkeyfile> [<pubkeyfile>]");
|
||||
l.log("gentextkeys");
|
||||
@ -370,6 +373,65 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the HTTP server pointing at the host and port specified using the private i2p
|
||||
* destination loaded from the specified file, replacing the HTTP headers
|
||||
* so that the Host: specified is the one spoofed. <p />
|
||||
*
|
||||
* Sets the event "serverTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error)
|
||||
* Also sets the event "openServerResult" = "ok" or "error" (displaying "Ready!" on the logger after
|
||||
* 'ok'). So, success = serverTaskId != -1 and openServerResult = ok.
|
||||
*
|
||||
* @param args {hostname, portNumber, spoofedHost, privKeyFilename}
|
||||
* @param l logger to receive events and output
|
||||
*/
|
||||
public void runHttpServer(String args[], Logging l) {
|
||||
if (args.length == 4) {
|
||||
InetAddress serverHost = null;
|
||||
int portNum = -1;
|
||||
File privKeyFile = null;
|
||||
try {
|
||||
serverHost = InetAddress.getByName(args[0]);
|
||||
} catch (UnknownHostException uhe) {
|
||||
l.log("unknown host");
|
||||
_log.error(getPrefix() + "Error resolving " + args[0], uhe);
|
||||
notifyEvent("serverTaskId", new Integer(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
portNum = Integer.parseInt(args[1]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[1], nfe);
|
||||
notifyEvent("serverTaskId", new Integer(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
String spoofedHost = args[2];
|
||||
|
||||
privKeyFile = new File(args[3]);
|
||||
if (!privKeyFile.canRead()) {
|
||||
l.log("private key file does not exist");
|
||||
_log.error(getPrefix() + "Private key file does not exist or is not readable: " + args[3]);
|
||||
notifyEvent("serverTaskId", new Integer(-1));
|
||||
return;
|
||||
}
|
||||
I2PTunnelHTTPServer serv = new I2PTunnelHTTPServer(serverHost, portNum, privKeyFile, args[3], spoofedHost, l, (EventDispatcher) this, this);
|
||||
serv.setReadTimeout(readTimeout);
|
||||
serv.startRunning();
|
||||
addtask(serv);
|
||||
notifyEvent("serverTaskId", new Integer(serv.getId()));
|
||||
return;
|
||||
} else {
|
||||
l.log("httpserver <host> <port> <spoofedhost> <privkeyfile>");
|
||||
l.log(" creates an HTTP server that sends all incoming data\n"
|
||||
+ " of its destination to host:port., filtering the HTTP\n"
|
||||
+ " headers so it looks like the request is to the spoofed host.");
|
||||
notifyEvent("serverTaskId", new Integer(-1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the server pointing at the host and port specified using the private i2p
|
||||
* destination loaded from the given base64 stream. <p />
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +147,6 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
|
||||
private static final int DEFAULT_READ_TIMEOUT = 60*1000;
|
||||
|
||||
|
||||
/**
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
@ -156,8 +155,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
if (!defaultOpts.contains(I2PSocketOptions.PROP_READ_TIMEOUT))
|
||||
defaultOpts.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, ""+DEFAULT_READ_TIMEOUT);
|
||||
if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
|
||||
defaultOpts.setProperty("i2p.streaming.inactivityTimeout", ""+DEFAULT_READ_TIMEOUT);
|
||||
//if (!defaultOpts.contains("i2p.streaming.inactivityTimeout"))
|
||||
// defaultOpts.setProperty("i2p.streaming.inactivityTimeout", ""+DEFAULT_READ_TIMEOUT);
|
||||
I2PSocketOptions opts = sockMgr.buildOptions(defaultOpts);
|
||||
if (!defaultOpts.containsKey(I2PSocketOptions.PROP_CONNECT_TIMEOUT))
|
||||
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
|
||||
@ -407,7 +406,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
opts.setProperty("i2p.streaming.inactivityTimeoutAction", ""+1);
|
||||
I2PSocket i2ps = createI2PSocket(dest, getDefaultOptions(opts));
|
||||
byte[] data = newRequest.toString().getBytes("ISO-8859-1");
|
||||
I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data, mySockets);
|
||||
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
|
||||
} catch (SocketException ex) {
|
||||
_log.info(getPrefix(requestId) + "Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
@ -437,6 +437,30 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
}
|
||||
|
||||
private class OnTimeout implements Runnable {
|
||||
private Socket _socket;
|
||||
private OutputStream _out;
|
||||
private String _target;
|
||||
private boolean _usingProxy;
|
||||
private String _wwwProxy;
|
||||
private long _requestId;
|
||||
public OnTimeout(Socket s, OutputStream out, String target, boolean usingProxy, String wwwProxy, long id) {
|
||||
_socket = s;
|
||||
_out = out;
|
||||
_target = target;
|
||||
_usingProxy = usingProxy;
|
||||
_wwwProxy = wwwProxy;
|
||||
_requestId = id;
|
||||
}
|
||||
public void run() {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Timeout occured requesting " + _target);
|
||||
handleHTTPClientException(new RuntimeException("Timeout"), _out,
|
||||
_target, _usingProxy, _wwwProxy, _requestId);
|
||||
closeSocket(_socket);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeErrorMessage(byte[] errMessage, OutputStream out, String targetRequest,
|
||||
boolean usingWWWProxy, String wwwProxy) throws IOException {
|
||||
if (out != null) {
|
||||
|
@ -0,0 +1,162 @@
|
||||
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
|
||||
* (c) 2003 - 2004 mihi
|
||||
*/
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.EventDispatcher;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Simple extension to the I2PTunnelServer that filters the HTTP
|
||||
* headers sent from the client to the server, replacing the Host
|
||||
* header with whatever this instance has been configured with.
|
||||
*
|
||||
*/
|
||||
public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
private final static Log _log = new Log(I2PTunnelHTTPServer.class);
|
||||
/** what Host: should we seem to be to the webserver? */
|
||||
private String _spoofHost;
|
||||
|
||||
public I2PTunnelHTTPServer(InetAddress host, int port, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host, port, privData, l, notifyThis, tunnel);
|
||||
_spoofHost = spoofHost;
|
||||
}
|
||||
|
||||
public I2PTunnelHTTPServer(InetAddress host, int port, File privkey, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host, port, privkey, privkeyname, l, notifyThis, tunnel);
|
||||
_spoofHost = spoofHost;
|
||||
}
|
||||
|
||||
public I2PTunnelHTTPServer(InetAddress host, int port, InputStream privData, String privkeyname, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host, port, privData, privkeyname, l, notifyThis, tunnel);
|
||||
_spoofHost = spoofHost;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
I2PServerSocket i2pss = sockMgr.getServerSocket();
|
||||
while (true) {
|
||||
I2PSocket i2ps = i2pss.accept();
|
||||
if (i2ps == null) throw new I2PException("I2PServerSocket closed");
|
||||
I2PThread t = new I2PThread(new Handler(i2ps));
|
||||
t.start();
|
||||
}
|
||||
} catch (I2PException ex) {
|
||||
_log.error("Error while waiting for I2PConnections", ex);
|
||||
} catch (IOException ex) {
|
||||
_log.error("Error while waiting for I2PConnections", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Async handler to keep .accept() from blocking too long.
|
||||
* todo: replace with a thread pool so we dont get overrun by threads if/when
|
||||
* receiving a lot of connection requests concurrently.
|
||||
*
|
||||
*/
|
||||
private class Handler implements Runnable {
|
||||
private I2PSocket _handleSocket;
|
||||
public Handler(I2PSocket socket) {
|
||||
_handleSocket = socket;
|
||||
}
|
||||
public void run() {
|
||||
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
|
||||
long afterSocket = -1;
|
||||
|
||||
//local is fast, so synchronously. Does not need that many
|
||||
//threads.
|
||||
try {
|
||||
_handleSocket.setReadTimeout(readTimeout);
|
||||
String modifiedHeader = getModifiedHeader();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Modified header: [" + modifiedHeader + "]");
|
||||
|
||||
Socket s = new Socket(remoteHost, remotePort);
|
||||
afterSocket = I2PAppContext.getGlobalContext().clock().now();
|
||||
new I2PTunnelRunner(s, _handleSocket, slock, null, modifiedHeader.getBytes(), null);
|
||||
} catch (SocketException ex) {
|
||||
try {
|
||||
_handleSocket.close();
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error while closing the received i2p con", ex);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
_log.error("Error while waiting for I2PConnections", ex);
|
||||
}
|
||||
|
||||
long afterHandle = I2PAppContext.getGlobalContext().clock().now();
|
||||
long timeToHandle = afterHandle - afterAccept;
|
||||
if (timeToHandle > 1000)
|
||||
_log.warn("Took a while to handle the request [" + timeToHandle + ", socket create: "
|
||||
+ (afterSocket-afterAccept) + "]");
|
||||
}
|
||||
private String getModifiedHeader() throws IOException {
|
||||
InputStream in = _handleSocket.getInputStream();
|
||||
|
||||
StringBuffer command = new StringBuffer(128);
|
||||
Properties headers = readHeaders(in, command);
|
||||
headers.setProperty("Host", _spoofHost);
|
||||
headers.setProperty("Connection", "close");
|
||||
return formatHeaders(headers, command);
|
||||
}
|
||||
}
|
||||
|
||||
private String formatHeaders(Properties headers, StringBuffer command) {
|
||||
StringBuffer buf = new StringBuffer(command.length() + headers.size() * 64);
|
||||
buf.append(command.toString()).append('\n');
|
||||
for (Iterator iter = headers.keySet().iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
String val = headers.getProperty(name);
|
||||
buf.append(name).append(": ").append(val).append('\n');
|
||||
}
|
||||
buf.append('\n');
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private Properties readHeaders(InputStream in, StringBuffer command) throws IOException {
|
||||
Properties headers = new Properties();
|
||||
StringBuffer buf = new StringBuffer(128);
|
||||
|
||||
boolean ok = DataHelper.readLine(in, command);
|
||||
if (!ok) throw new IOException("EOF reached while reading the HTTP command [" + command.toString() + "]");
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Read the http command [" + command.toString() + "]");
|
||||
|
||||
while (true) {
|
||||
buf.setLength(0);
|
||||
ok = DataHelper.readLine(in, buf);
|
||||
if (!ok) throw new IOException("EOF reached before the end of the headers [" + buf.toString() + "]");
|
||||
if ( (buf.length() <= 1) && ( (buf.charAt(0) == '\n') || (buf.charAt(0) == '\r') ) ) {
|
||||
// end of headers reached
|
||||
return headers;
|
||||
} else {
|
||||
int split = buf.indexOf(": ");
|
||||
if (split <= 0) throw new IOException("Invalid HTTP header, missing colon [" + buf.toString() + "]");
|
||||
String name = buf.substring(0, split);
|
||||
String value = buf.substring(split+2); // ": "
|
||||
headers.setProperty(name, value);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Read the header [" + name + "] = [" + value + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,21 +40,37 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
Object slock, finishLock = new Object();
|
||||
boolean finished = false;
|
||||
HashMap ostreams, sockets;
|
||||
byte[] initialData;
|
||||
byte[] initialI2PData;
|
||||
byte[] initialSocketData;
|
||||
/** when the last data was sent/received (or -1 if never) */
|
||||
private long lastActivityOn;
|
||||
/** when the runner started up */
|
||||
private long startedOn;
|
||||
private List sockList;
|
||||
/** if we die before receiving any data, run this job */
|
||||
private Runnable onTimeout;
|
||||
private long totalSent;
|
||||
private long totalReceived;
|
||||
|
||||
private volatile long __forwarderId;
|
||||
|
||||
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialData, List sockList) {
|
||||
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, List sockList) {
|
||||
this(s, i2ps, slock, initialI2PData, null, sockList, null);
|
||||
}
|
||||
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, byte[] initialSocketData, List sockList) {
|
||||
this(s, i2ps, slock, initialI2PData, initialSocketData, sockList, null);
|
||||
}
|
||||
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, List sockList, Runnable onTimeout) {
|
||||
this(s, i2ps, slock, initialI2PData, null, sockList, onTimeout);
|
||||
}
|
||||
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialI2PData, byte[] initialSocketData, List sockList, Runnable onTimeout) {
|
||||
this.sockList = sockList;
|
||||
this.s = s;
|
||||
this.i2ps = i2ps;
|
||||
this.slock = slock;
|
||||
this.initialData = initialData;
|
||||
this.initialI2PData = initialI2PData;
|
||||
this.initialSocketData = initialSocketData;
|
||||
this.onTimeout = onTimeout;
|
||||
lastActivityOn = -1;
|
||||
startedOn = Clock.getInstance().now();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
@ -97,24 +113,27 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
}
|
||||
|
||||
public void run() {
|
||||
boolean closedCleanly = false;
|
||||
try {
|
||||
InputStream in = s.getInputStream();
|
||||
OutputStream out = s.getOutputStream(); // = new BufferedOutputStream(s.getOutputStream(), NETWORK_BUFFER_SIZE);
|
||||
i2ps.setSocketErrorListener(this);
|
||||
InputStream i2pin = i2ps.getInputStream();
|
||||
OutputStream i2pout = i2ps.getOutputStream(); //new BufferedOutputStream(i2ps.getOutputStream(), MAX_PACKET_SIZE);
|
||||
if (initialData != null) {
|
||||
if (initialI2PData != null) {
|
||||
synchronized (slock) {
|
||||
i2pout.write(initialData);
|
||||
i2pout.write(initialI2PData);
|
||||
//i2pout.flush();
|
||||
}
|
||||
}
|
||||
if (initialSocketData != null) {
|
||||
out.write(initialSocketData);
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Initial data " + (initialData != null ? initialData.length : 0)
|
||||
+ " written, starting forwarders");
|
||||
Thread t1 = new StreamForwarder(in, i2pout, "toI2P");
|
||||
Thread t2 = new StreamForwarder(i2pin, out, "fromI2P");
|
||||
_log.debug("Initial data " + (initialI2PData != null ? initialI2PData.length : 0)
|
||||
+ " written to I2P, " + (initialSocketData != null ? initialSocketData.length : 0)
|
||||
+ " written to the socket, starting forwarders");
|
||||
Thread t1 = new StreamForwarder(in, i2pout, true);
|
||||
Thread t2 = new StreamForwarder(i2pin, out, false);
|
||||
synchronized (finishLock) {
|
||||
while (!finished) {
|
||||
finishLock.wait();
|
||||
@ -122,12 +141,21 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("At least one forwarder completed, closing and joining");
|
||||
|
||||
// this task is useful for the httpclient
|
||||
if (onTimeout != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("runner has a timeout job, totalReceived = " + totalReceived
|
||||
+ " totalSent = " + totalSent + " job = " + onTimeout);
|
||||
if ( (totalSent <= 0) && (totalReceived <= 0) )
|
||||
onTimeout.run();
|
||||
}
|
||||
|
||||
// now one connection is dead - kill the other as well.
|
||||
s.close();
|
||||
i2ps.close();
|
||||
t1.join(30*1000);
|
||||
t2.join(30*1000);
|
||||
closedCleanly = true;
|
||||
} catch (InterruptedException ex) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Interrupted", ex);
|
||||
@ -140,21 +168,20 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
} finally {
|
||||
removeRef();
|
||||
try {
|
||||
if ( (s != null) && (!closedCleanly) )
|
||||
if (s != null)
|
||||
s.close();
|
||||
} catch (IOException ex) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Could not close java socket", ex);
|
||||
}
|
||||
try {
|
||||
if (i2ps != null) {
|
||||
if (!closedCleanly)
|
||||
i2ps.close();
|
||||
i2ps.setSocketErrorListener(null);
|
||||
if (i2ps != null) {
|
||||
try {
|
||||
i2ps.close();
|
||||
} catch (IOException ex) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Could not close I2PSocket", ex);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Could not close I2PSocket", ex);
|
||||
i2ps.setSocketErrorListener(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -181,12 +208,14 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
InputStream in;
|
||||
OutputStream out;
|
||||
String direction;
|
||||
private boolean _toI2P;
|
||||
private ByteCache _cache;
|
||||
|
||||
private StreamForwarder(InputStream in, OutputStream out, String dir) {
|
||||
private StreamForwarder(InputStream in, OutputStream out, boolean toI2P) {
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
direction = dir;
|
||||
_toI2P = toI2P;
|
||||
direction = (toI2P ? "toI2P" : "fromI2P");
|
||||
_cache = ByteCache.getInstance(256, NETWORK_BUFFER_SIZE);
|
||||
setName("StreamForwarder " + _runnerId + "." + (++__forwarderId));
|
||||
start();
|
||||
@ -207,6 +236,10 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
int len;
|
||||
while ((len = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, len);
|
||||
if (_toI2P)
|
||||
totalSent += len;
|
||||
else
|
||||
totalReceived += len;
|
||||
|
||||
if (len > 0) updateActivity();
|
||||
|
||||
@ -259,10 +292,10 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
_log.warn(direction + ": Error closing input stream", ex);
|
||||
}
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ex) {
|
||||
out.flush();
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(direction + ": Error closing output stream", ex);
|
||||
_log.warn(direction + ": Error flushing to close", ioe);
|
||||
}
|
||||
synchronized (finishLock) {
|
||||
finished = true;
|
||||
|
@ -31,19 +31,20 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
|
||||
private final static Log _log = new Log(I2PTunnelServer.class);
|
||||
|
||||
private I2PSocketManager sockMgr;
|
||||
private I2PServerSocket i2pss;
|
||||
protected I2PSocketManager sockMgr;
|
||||
protected I2PServerSocket i2pss;
|
||||
|
||||
private Object lock = new Object(), slock = new Object();
|
||||
private Object lock = new Object();
|
||||
protected Object slock = new Object();
|
||||
|
||||
private InetAddress remoteHost;
|
||||
private int remotePort;
|
||||
protected InetAddress remoteHost;
|
||||
protected int remotePort;
|
||||
|
||||
private Logging l;
|
||||
|
||||
private static final long DEFAULT_READ_TIMEOUT = -1; // 3*60*1000;
|
||||
/** default timeout to 3 minutes - override if desired */
|
||||
private long readTimeout = DEFAULT_READ_TIMEOUT;
|
||||
protected long readTimeout = DEFAULT_READ_TIMEOUT;
|
||||
|
||||
public I2PTunnelServer(InetAddress host, int port, String privData, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
super(host + ":" + port + " <- " + privData, notifyThis, tunnel);
|
||||
|
@ -16,6 +16,7 @@ import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@ -57,7 +58,7 @@ public class TunnelController implements Logging {
|
||||
setConfig(config, prefix);
|
||||
_messages = new ArrayList(4);
|
||||
_running = false;
|
||||
if (createKey && ("server".equals(getType())) )
|
||||
if (createKey && ("server".equals(getType()) || "httpserver".equals(getType())) )
|
||||
createPrivateKey();
|
||||
_starting = getStartOnLoad();
|
||||
}
|
||||
@ -99,6 +100,11 @@ public class TunnelController implements Logging {
|
||||
}
|
||||
}
|
||||
|
||||
public void startTunnelBackground() {
|
||||
if (_running) return;
|
||||
new I2PThread(new Runnable() { public void run() { startTunnel(); } }).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start up the tunnel (if it isn't already running)
|
||||
*
|
||||
@ -132,6 +138,8 @@ public class TunnelController implements Logging {
|
||||
startClient();
|
||||
} else if ("server".equals(type)) {
|
||||
startServer();
|
||||
} else if ("httpserver".equals(type)) {
|
||||
startHttpServer();
|
||||
} else {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Cannot start tunnel - unknown type [" + type + "]");
|
||||
@ -206,6 +214,18 @@ public class TunnelController implements Logging {
|
||||
_running = true;
|
||||
}
|
||||
|
||||
private void startHttpServer() {
|
||||
setI2CPOptions();
|
||||
setSessionOptions();
|
||||
String targetHost = getTargetHost();
|
||||
String targetPort = getTargetPort();
|
||||
String spoofedHost = getSpoofedHost();
|
||||
String privKeyFile = getPrivKeyFile();
|
||||
_tunnel.runHttpServer(new String[] { targetHost, targetPort, spoofedHost, privKeyFile }, this);
|
||||
acquire();
|
||||
_running = true;
|
||||
}
|
||||
|
||||
private void setListenOn() {
|
||||
String listenOn = getListenOnInterface();
|
||||
if ( (listenOn != null) && (listenOn.length() > 0) ) {
|
||||
@ -233,6 +253,9 @@ public class TunnelController implements Logging {
|
||||
String host = getI2CPHost();
|
||||
if ( (host != null) && (host.length() > 0) )
|
||||
_tunnel.host = host;
|
||||
// woohah, special casing for people with ipv6/etc
|
||||
if ("localhost".equals(_tunnel.host))
|
||||
_tunnel.host = "127.0.0.1";
|
||||
String port = getI2CPPort();
|
||||
if ( (port != null) && (port.length() > 0) )
|
||||
_tunnel.port = port;
|
||||
@ -294,6 +317,7 @@ public class TunnelController implements Logging {
|
||||
public String getListenOnInterface() { return _config.getProperty("interface"); }
|
||||
public String getTargetHost() { return _config.getProperty("targetHost"); }
|
||||
public String getTargetPort() { return _config.getProperty("targetPort"); }
|
||||
public String getSpoofedHost() { return _config.getProperty("spoofedHost"); }
|
||||
public String getPrivKeyFile() { return _config.getProperty("privKeyFile"); }
|
||||
public String getListenPort() { return _config.getProperty("listenPort"); }
|
||||
public String getTargetDestination() { return _config.getProperty("targetDestination"); }
|
||||
@ -311,6 +335,8 @@ public class TunnelController implements Logging {
|
||||
getClientSummary(buf);
|
||||
else if ("server".equals(type))
|
||||
getServerSummary(buf);
|
||||
else if ("httpserver".equals(type))
|
||||
getHttpServerSummary(buf);
|
||||
else
|
||||
buf.append("Unknown type ").append(type);
|
||||
}
|
||||
@ -364,6 +390,18 @@ public class TunnelController implements Logging {
|
||||
getOptionSummary(buf);
|
||||
}
|
||||
|
||||
private void getHttpServerSummary(StringBuffer buf) {
|
||||
String description = getDescription();
|
||||
if ( (description != null) && (description.trim().length() > 0) )
|
||||
buf.append("<i>").append(description).append("</i><br />\n");
|
||||
buf.append("Server tunnel pointing at port ").append(getTargetPort());
|
||||
buf.append(" on ").append(getTargetHost());
|
||||
buf.append(" for the site ").append(getSpoofedHost());
|
||||
buf.append("<br />\n");
|
||||
buf.append("Private destination loaded from ").append(getPrivKeyFile()).append("<br />\n");
|
||||
getOptionSummary(buf);
|
||||
}
|
||||
|
||||
private void getOptionSummary(StringBuffer buf) {
|
||||
String opts = getClientOptions();
|
||||
if ( (opts != null) && (opts.length() > 0) )
|
||||
@ -375,7 +413,7 @@ public class TunnelController implements Logging {
|
||||
Destination dest = session.getMyDestination();
|
||||
if (dest != null) {
|
||||
buf.append("Destination hash: ").append(dest.calculateHash().toBase64()).append("<br />\n");
|
||||
if ("server".equals(getType())) {
|
||||
if ( ("server".equals(getType())) || ("httpserver".equals(getType())) ) {
|
||||
buf.append("Full destination: ");
|
||||
buf.append("<input type=\"text\" size=\"10\" onclick=\"this.select();\" ");
|
||||
buf.append("value=\"").append(dest.toBase64()).append("\" />\n");
|
||||
|
@ -18,6 +18,7 @@ class WebEditPageFormGenerator {
|
||||
"<option value=\"httpclient\">HTTP proxy</option>" +
|
||||
"<option value=\"client\">Client tunnel</option>" +
|
||||
"<option value=\"server\">Server tunnel</option>" +
|
||||
"<option value=\"httpserver\">HTTP server tunnel</option>" +
|
||||
"</select> <input type=\"submit\" value=\"GO\" />" +
|
||||
"</form>\n";
|
||||
|
||||
@ -42,6 +43,8 @@ class WebEditPageFormGenerator {
|
||||
return getEditClientForm(controller, id);
|
||||
else if ("server".equals(type))
|
||||
return getEditServerForm(controller, id);
|
||||
else if ("httpserver".equals(type))
|
||||
return getEditHttpServerForm(controller, id);
|
||||
else
|
||||
return "WTF, unknown type [" + type + "]";
|
||||
}
|
||||
@ -103,7 +106,7 @@ class WebEditPageFormGenerator {
|
||||
if ( (controller != null) && (controller.getTargetHost() != null) )
|
||||
buf.append("value=\"").append(controller.getTargetHost()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"localhost\" ");
|
||||
buf.append("value=\"127.0.0.1\" ");
|
||||
buf.append(" /><br />\n");
|
||||
|
||||
buf.append("<b>Target port:</b> <input type=\"text\" size=\"4\" name=\"targetPort\" ");
|
||||
@ -129,6 +132,48 @@ class WebEditPageFormGenerator {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static String getEditHttpServerForm(TunnelController controller, String id) {
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
addGeneral(buf, controller, id);
|
||||
buf.append("<b>Type:</b> <i>HTTP server tunnel</i><input type=\"hidden\" name=\"type\" value=\"httpserver\" /><br />\n");
|
||||
|
||||
buf.append("<b>Target host:</b> <input type=\"text\" size=\"40\" name=\"targetHost\" ");
|
||||
if ( (controller != null) && (controller.getTargetHost() != null) )
|
||||
buf.append("value=\"").append(controller.getTargetHost()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"127.0.0.1\" ");
|
||||
buf.append(" /><br />\n");
|
||||
|
||||
buf.append("<b>Target port:</b> <input type=\"text\" size=\"4\" name=\"targetPort\" ");
|
||||
if ( (controller != null) && (controller.getTargetPort() != null) )
|
||||
buf.append("value=\"").append(controller.getTargetPort()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"80\" ");
|
||||
buf.append(" /><br />\n");
|
||||
|
||||
buf.append("<b>Website hostname:</b> <input type=\"text\" size=\"16\" name=\"spoofedHost\" ");
|
||||
if ( (controller != null) && (controller.getSpoofedHost() != null) )
|
||||
buf.append("value=\"").append(controller.getSpoofedHost()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"mysite.i2p\" ");
|
||||
buf.append(" /><br />\n");
|
||||
|
||||
buf.append("<b>Private key file:</b> <input type=\"text\" name=\"privKeyFile\" value=\"");
|
||||
if ( (controller != null) && (controller.getPrivKeyFile() != null) ) {
|
||||
buf.append(controller.getPrivKeyFile()).append("\" /><br />");
|
||||
} else {
|
||||
buf.append("myServer.privKey\" /><br />");
|
||||
buf.append("<input type=\"hidden\" name=\"privKeyGenerate\" value=\"true\" />");
|
||||
}
|
||||
|
||||
addOptions(buf, controller);
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
|
||||
buf.append(" <i>confirm removal:</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
|
||||
buf.append("</form>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start off the form and add some common fields (name, num, description)
|
||||
*
|
||||
@ -285,7 +330,7 @@ class WebEditPageFormGenerator {
|
||||
if ( (controller != null) && (controller.getI2CPHost() != null) )
|
||||
buf.append(controller.getI2CPHost());
|
||||
else
|
||||
buf.append("localhost");
|
||||
buf.append("127.0.0.1");
|
||||
buf.append("\" /><br />\n");
|
||||
buf.append("<b>I2CP port:</b> ");
|
||||
buf.append("<input type=\"text\" name=\"clientPort\" size=\"20\" value=\"");
|
||||
|
@ -38,6 +38,7 @@ public class WebEditPageHelper {
|
||||
private String _targetDestination;
|
||||
private String _targetHost;
|
||||
private String _targetPort;
|
||||
private String _spoofedHost;
|
||||
private String _privKeyFile;
|
||||
private boolean _startOnLoad;
|
||||
private boolean _privKeyGenerate;
|
||||
@ -139,6 +140,10 @@ public class WebEditPageHelper {
|
||||
public void setTargetPort(String port) {
|
||||
_targetPort = (port != null ? port.trim() : null);
|
||||
}
|
||||
/** What host does this http server tunnel spoof */
|
||||
public void setSpoofedHost(String host) {
|
||||
_spoofedHost = (host != null ? host.trim() : null);
|
||||
}
|
||||
/** What filename is this server tunnel's private keys stored in */
|
||||
public void setPrivKeyFile(String file) {
|
||||
_privKeyFile = (file != null ? file.trim() : null);
|
||||
@ -252,6 +257,8 @@ public class WebEditPageHelper {
|
||||
// creating new
|
||||
cur = new TunnelController(config, "", _privKeyGenerate);
|
||||
TunnelControllerGroup.getInstance().addController(cur);
|
||||
if (cur.getStartOnLoad())
|
||||
cur.startTunnelBackground();
|
||||
} else {
|
||||
cur.setConfig(config, "");
|
||||
}
|
||||
@ -320,6 +327,15 @@ public class WebEditPageHelper {
|
||||
config.setProperty("targetPort", _targetPort);
|
||||
if (_privKeyFile != null)
|
||||
config.setProperty("privKeyFile", _privKeyFile);
|
||||
} else if ("httpserver".equals(_type)) {
|
||||
if (_targetHost != null)
|
||||
config.setProperty("targetHost", _targetHost);
|
||||
if (_targetPort != null)
|
||||
config.setProperty("targetPort", _targetPort);
|
||||
if (_privKeyFile != null)
|
||||
config.setProperty("privKeyFile", _privKeyFile);
|
||||
if (_spoofedHost != null)
|
||||
config.setProperty("spoofedHost", _spoofedHost);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
<option value="httpclient">HTTP proxy</option>
|
||||
<option value="client">Client tunnel</option>
|
||||
<option value="server">Server tunnel</option>
|
||||
<option value="httpserver">HTTP server tunnel</option>
|
||||
</select> <input type="submit" value="GO" />
|
||||
</form>
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +102,8 @@ public class I2PSocketManagerFactory {
|
||||
|
||||
if (i2cpHost != null)
|
||||
opts.setProperty(I2PClient.PROP_TCP_HOST, i2cpHost);
|
||||
opts.setProperty(I2PClient.PROP_TCP_PORT, "" + i2cpPort);
|
||||
if (i2cpPort > 0)
|
||||
opts.setProperty(I2PClient.PROP_TCP_PORT, "" + i2cpPort);
|
||||
|
||||
try {
|
||||
I2PSession session = client.createSession(myPrivateKeyStream, opts);
|
||||
|
@ -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";
|
||||
|
@ -72,7 +72,7 @@ public class TestSwarm {
|
||||
}
|
||||
}
|
||||
try {
|
||||
_manager = I2PSocketManagerFactory.createManager(new FileInputStream(_destFile), "localhost", 10001, null);
|
||||
_manager = I2PSocketManagerFactory.createManager(new FileInputStream(_destFile), null, -1, null);
|
||||
} catch (Exception e) {
|
||||
_log.error("Error creatign the manager", e);
|
||||
return;
|
||||
|
@ -0,0 +1,42 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Writer;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.router.RouterContext;
|
||||
|
||||
/**
|
||||
* uuuugly. dump the peer profile data if given a peer.
|
||||
*
|
||||
*/
|
||||
public class StatHelper {
|
||||
private String _peer;
|
||||
private Writer _writer;
|
||||
|
||||
public void setPeer(String peer) { _peer = peer; }
|
||||
public void setWriter(Writer writer) { _writer = writer; }
|
||||
|
||||
public String getProfile() {
|
||||
RouterContext ctx = (RouterContext)net.i2p.router.RouterContext.listContexts().get(0);
|
||||
Set peers = ctx.profileOrganizer().selectAllPeers();
|
||||
for (Iterator iter = peers.iterator(); iter.hasNext(); ) {
|
||||
Hash peer = (Hash)iter.next();
|
||||
if (peer.toBase64().startsWith(_peer)) {
|
||||
try {
|
||||
WriterOutputStream wos = new WriterOutputStream(_writer);
|
||||
ctx.profileOrganizer().exportProfile(peer, wos);
|
||||
wos.flush();
|
||||
_writer.flush();
|
||||
return "";
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* Treat a writer as an output stream. Quick 'n dirty, none
|
||||
* of that "intarnasheeonaleyzayshun" stuff. So we can treat
|
||||
* the jsp's PrintWriter as an OutputStream
|
||||
*/
|
||||
public class WriterOutputStream extends OutputStream {
|
||||
private Writer _writer;
|
||||
|
||||
public WriterOutputStream(Writer writer) { _writer = writer; }
|
||||
public void write(int b) throws IOException { _writer.write(b); }
|
||||
}
|
5
apps/routerconsole/jsp/dumpprofile.jsp
Normal file
5
apps/routerconsole/jsp/dumpprofile.jsp
Normal file
@ -0,0 +1,5 @@
|
||||
<%@page contentType="text/plain"
|
||||
%><jsp:useBean id="helper" class="net.i2p.router.web.StatHelper"
|
||||
/><jsp:setProperty name="helper" property="peer" value="<%=request.getParameter("peer")%>"
|
||||
/><jsp:setProperty name="helper" property="writer" value="<%=out%>"
|
||||
/><jsp:getProperty name="helper" property="profile" />
|
@ -21,9 +21,9 @@ SRCDIR = src
|
||||
# Programs
|
||||
#
|
||||
|
||||
AR = C:\Dev-Cpp\bin\ar
|
||||
CC = C:\Dev-Cpp\bin\gcc
|
||||
RM = C:\Dev-Cpp\bin\rm
|
||||
AR = C:\MinGW\bin\ar
|
||||
CC = C:\MinGW\bin\gcc
|
||||
RM = C:\MinGW\bin\rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
|
@ -6,8 +6,8 @@
|
||||
# Programs
|
||||
#
|
||||
|
||||
CC = C:\Dev-Cpp\bin\gcc
|
||||
RM = C:\Dev-Cpp\bin\rm
|
||||
CC = C:\MinGW\bin\gcc
|
||||
RM = C:\MinGW\bin\rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
|
@ -87,6 +87,7 @@ typedef struct {
|
||||
int sock; /* the socket used for communications with SAM */
|
||||
bool connected; /* whether the socket is connected */
|
||||
sam_sid_t prev_id; /* the last stream id number we used */
|
||||
void *child; /* whatever you want it to be */
|
||||
} sam_sess_t; /* a SAM session */
|
||||
|
||||
typedef enum { /* see sam_strerror() for detailed descriptions of these */
|
||||
|
@ -937,6 +937,7 @@ sam_sess_t *sam_session_init(sam_sess_t *session)
|
||||
SAMLOGS("Out of memory");
|
||||
abort();
|
||||
}
|
||||
session->child = NULL;
|
||||
}
|
||||
session->connected = false;
|
||||
session->prev_id = 0;
|
||||
|
@ -547,7 +547,8 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
|
||||
try {
|
||||
if (!streamSession.sendBytes(id, getClientSocketInputStream(), size)) { // data)) {
|
||||
_log.error("STREAM SEND [" + size + "] failed");
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("STREAM SEND [" + size + "] failed");
|
||||
boolean rv = writeString("STREAM CLOSED RESULT=CANT_REACH_PEER ID=" + id + " MESSAGE=\"Send of " + size + " bytes failed\"\n");
|
||||
streamSession.closeConnection(id);
|
||||
return rv;
|
||||
@ -698,7 +699,8 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
try {
|
||||
closeClientSocket();
|
||||
} catch (IOException e) {
|
||||
_log.error("Error closing socket: " + e.getMessage());
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error closing socket", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -733,7 +735,8 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
try {
|
||||
closeClientSocket();
|
||||
} catch (IOException e) {
|
||||
_log.error("Error closing socket: " + e.getMessage());
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error closing socket", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -802,7 +805,8 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
try {
|
||||
closeClientSocket();
|
||||
} catch (IOException e) {
|
||||
_log.error("Error closing socket: " + e.getMessage());
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error closing socket", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,10 @@ public class Connection {
|
||||
private long _lastSendTime;
|
||||
private long _lastSendId;
|
||||
private boolean _resetReceived;
|
||||
private boolean _resetSent;
|
||||
private long _resetSentOn;
|
||||
private boolean _connected;
|
||||
private boolean _hardDisconnected;
|
||||
private MessageInputStream _inputStream;
|
||||
private MessageOutputStream _outputStream;
|
||||
private SchedulerChooser _chooser;
|
||||
@ -70,7 +73,7 @@ public class Connection {
|
||||
private long _lifetimeDupMessageReceived;
|
||||
|
||||
public static final long MAX_RESEND_DELAY = 60*1000;
|
||||
public static final long MIN_RESEND_DELAY = 40*1000;
|
||||
public static final long MIN_RESEND_DELAY = 30*1000;
|
||||
|
||||
/** wait up to 5 minutes after disconnection so we can ack/close packets */
|
||||
public static int DISCONNECT_TIMEOUT = 5*60*1000;
|
||||
@ -111,6 +114,7 @@ public class Connection {
|
||||
_ackSinceCongestion = true;
|
||||
_connectLock = new Object();
|
||||
_activeResends = 0;
|
||||
_resetSentOn = -1;
|
||||
_context.statManager().createRateStat("stream.con.windowSizeAtCongestion", "How large was our send window when we send a dup?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.chokeSizeBegin", "How many messages were outstanding when we started to choke?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.chokeSizeEnd", "How many messages were outstanding when we stopped being choked?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
@ -144,20 +148,25 @@ public class Connection {
|
||||
synchronized (_outboundPackets) {
|
||||
if (!started)
|
||||
_context.statManager().addRateData("stream.chokeSizeBegin", _outboundPackets.size(), timeoutMs);
|
||||
if (!_connected)
|
||||
return false;
|
||||
started = true;
|
||||
if (_outboundPackets.size() >= _options.getWindowSize()) {
|
||||
if ( (_outboundPackets.size() >= _options.getWindowSize()) || (_activeResends > 0) ) {
|
||||
if (writeExpire > 0) {
|
||||
if (timeLeft <= 0) {
|
||||
_log.error("Outbound window is full of " + _outboundPackets.size()
|
||||
+ " with " + _activeResends + " active resends"
|
||||
+ " and we've waited too long (" + writeExpire + "ms)");
|
||||
return false;
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Outbound window is full (" + _outboundPackets.size() + "/" + _options.getWindowSize() + "), waiting " + timeLeft);
|
||||
_log.debug("Outbound window is full (" + _outboundPackets.size() + "/" + _options.getWindowSize() + "/"
|
||||
+ _activeResends + "), waiting " + timeLeft);
|
||||
try { _outboundPackets.wait(timeLeft); } catch (InterruptedException ie) {}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Outbound window is full (" + _outboundPackets.size() + "), waiting indefinitely");
|
||||
_log.debug("Outbound window is full (" + _outboundPackets.size() + "/" + _activeResends
|
||||
+ "), waiting indefinitely");
|
||||
try { _outboundPackets.wait(); } catch (InterruptedException ie) {}
|
||||
}
|
||||
} else {
|
||||
@ -171,6 +180,25 @@ public class Connection {
|
||||
void ackImmediately() {
|
||||
_receiver.send(null, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* got a packet we shouldn't have, send 'em a reset
|
||||
*
|
||||
*/
|
||||
void sendReset() {
|
||||
_resetSent = true;
|
||||
if (_resetSentOn <= 0)
|
||||
_resetSentOn = _context.clock().now();
|
||||
if ( (_remotePeer == null) || (_sendStreamId == null) ) return;
|
||||
PacketLocal reply = new PacketLocal(_context, _remotePeer);
|
||||
reply.setFlag(Packet.FLAG_RESET);
|
||||
reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
|
||||
reply.setSendStreamId(_sendStreamId);
|
||||
reply.setReceiveStreamId(_receiveStreamId);
|
||||
reply.setOptionalFrom(_connectionManager.getSession().getMyDestination());
|
||||
// this just sends the packet - no retries or whatnot
|
||||
_outboundQueue.enqueue(reply);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush any data that we can
|
||||
@ -223,7 +251,7 @@ public class Connection {
|
||||
if (timeout > MAX_RESEND_DELAY)
|
||||
timeout = MAX_RESEND_DELAY;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Resend in " + timeout + " for " + packet);
|
||||
_log.debug("Resend in " + timeout + " for " + packet, new Exception("Sent by"));
|
||||
|
||||
SimpleTimer.getInstance().addEvent(new ResendPacketEvent(packet), timeout);
|
||||
}
|
||||
@ -304,8 +332,8 @@ public class Connection {
|
||||
_ackedPackets++;
|
||||
if (p.getNumSends() > 1) {
|
||||
_activeResends--;
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Active resend of " + p + " successful, # active left: " + _activeResends);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Active resend of " + p + " successful, # active left: " + _activeResends);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -362,6 +390,9 @@ public class Connection {
|
||||
public boolean getResetReceived() { return _resetReceived; }
|
||||
|
||||
public boolean getIsConnected() { return _connected; }
|
||||
public boolean getHardDisconnected() { return _hardDisconnected; }
|
||||
public boolean getResetSent() { return _resetSent; }
|
||||
public long getResetSentOn() { return _resetSentOn; }
|
||||
|
||||
void disconnect(boolean cleanDisconnect) {
|
||||
disconnect(cleanDisconnect, true);
|
||||
@ -371,6 +402,13 @@ public class Connection {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Disconnecting " + toString(), new Exception("discon"));
|
||||
|
||||
if (!cleanDisconnect) {
|
||||
_hardDisconnected = true;
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Hard disconnecting and sending a reset on " + toString(), new Exception("cause"));
|
||||
sendReset();
|
||||
}
|
||||
|
||||
if (cleanDisconnect && _connected) {
|
||||
// send close packets and schedule stuff...
|
||||
_outputStream.closeInternal();
|
||||
@ -660,6 +698,14 @@ public class Connection {
|
||||
default:
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Closing connection due to inactivity");
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
StringBuffer buf = new StringBuffer(128);
|
||||
buf.append("last sent was: ").append(_context.clock().now() - _lastSendTime);
|
||||
buf.append("ms ago, last received was: ").append(_context.clock().now()-_lastReceivedOn);
|
||||
buf.append("ms ago, inactivity timeout is: ").append(_options.getInactivityTimeout());
|
||||
_log.debug(buf.toString());
|
||||
}
|
||||
|
||||
disconnect(true);
|
||||
break;
|
||||
}
|
||||
@ -693,6 +739,9 @@ public class Connection {
|
||||
buf.append(" wsize: ").append(_options.getWindowSize());
|
||||
buf.append(" cwin: ").append(_congestionWindowEnd - _highestAckedThrough);
|
||||
buf.append(" rtt: ").append(_options.getRTT());
|
||||
// not synchronized to avoid some kooky races
|
||||
buf.append(" unacked outbound: ").append(_outboundPackets.size()).append(" ");
|
||||
/*
|
||||
buf.append(" unacked outbound: ");
|
||||
synchronized (_outboundPackets) {
|
||||
buf.append(_outboundPackets.size()).append(" [");
|
||||
@ -701,6 +750,7 @@ public class Connection {
|
||||
}
|
||||
buf.append("] ");
|
||||
}
|
||||
*/
|
||||
buf.append("unacked inbound? ").append(getUnackedPacketsReceived());
|
||||
if (_inputStream != null) {
|
||||
buf.append(" [high ");
|
||||
@ -720,10 +770,8 @@ public class Connection {
|
||||
*/
|
||||
private class ResendPacketEvent implements SimpleTimer.TimedEvent {
|
||||
private PacketLocal _packet;
|
||||
private boolean _currentIsActiveResend;
|
||||
public ResendPacketEvent(PacketLocal packet) {
|
||||
_packet = packet;
|
||||
_currentIsActiveResend = false;
|
||||
packet.setResendPacketEvent(ResendPacketEvent.this);
|
||||
}
|
||||
|
||||
@ -731,7 +779,7 @@ public class Connection {
|
||||
if (_packet.getAckTime() > 0)
|
||||
return;
|
||||
|
||||
if (!_connected) {
|
||||
if (_resetSent || _resetReceived) {
|
||||
_packet.cancelled();
|
||||
return;
|
||||
}
|
||||
@ -739,12 +787,15 @@ public class Connection {
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Resend period reached for " + _packet);
|
||||
boolean resend = false;
|
||||
boolean isLowest = false;
|
||||
synchronized (_outboundPackets) {
|
||||
if (_packet.getSequenceNum() == _highestAckedThrough + 1)
|
||||
isLowest = true;
|
||||
if (_outboundPackets.containsKey(new Long(_packet.getSequenceNum())))
|
||||
resend = true;
|
||||
}
|
||||
if ( (resend) && (_packet.getAckTime() < 0) ) {
|
||||
if ( (_activeResends > 0) && (!_currentIsActiveResend) ) {
|
||||
if (!isLowest) {
|
||||
// we want to resend this packet, but there are already active
|
||||
// resends in the air and we dont want to make a bad situation
|
||||
// worse. wait another second
|
||||
@ -776,7 +827,6 @@ public class Connection {
|
||||
if (numSends == 2) {
|
||||
// first resend for this packet
|
||||
_activeResends++;
|
||||
_currentIsActiveResend = true;
|
||||
}
|
||||
|
||||
// in case things really suck, the other side may have lost thier
|
||||
@ -796,6 +846,17 @@ public class Connection {
|
||||
+ (_context.clock().now() - _packet.getCreatedOn()) + "ms)");
|
||||
_outboundQueue.enqueue(_packet);
|
||||
|
||||
_lastSendTime = _context.clock().now();
|
||||
|
||||
// acked during resending (... or somethin')
|
||||
if ( (_packet.getAckTime() > 0) && (_packet.getNumSends() > 1) ) {
|
||||
_activeResends--;
|
||||
synchronized (_outboundPackets) {
|
||||
_outboundPackets.notifyAll();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (numSends > _options.getMaxResends()) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Too many resends");
|
||||
|
@ -17,17 +17,20 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private Connection _connection;
|
||||
private MessageOutputStream.WriteStatus _dummyStatus;
|
||||
private static final MessageOutputStream.WriteStatus _dummyStatus = new DummyStatus();
|
||||
|
||||
public ConnectionDataReceiver(I2PAppContext ctx, Connection con) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(ConnectionDataReceiver.class);
|
||||
_connection = con;
|
||||
_dummyStatus = new DummyStatus();
|
||||
}
|
||||
|
||||
public boolean writeInProcess() {
|
||||
return _connection.getUnackedPacketsSent() > 0;
|
||||
Connection con = _connection;
|
||||
if (con != null)
|
||||
return con.getUnackedPacketsSent() > 0;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,10 +45,12 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
|
||||
* delivery.
|
||||
*/
|
||||
public MessageOutputStream.WriteStatus writeData(byte[] buf, int off, int size) {
|
||||
Connection con = _connection;
|
||||
if (con == null) return _dummyStatus;
|
||||
boolean doSend = true;
|
||||
if ( (size <= 0) && (_connection.getLastSendId() >= 0) ) {
|
||||
if (_connection.getOutputStream().getClosed()) {
|
||||
if (_connection.getCloseSentOn() < 0) {
|
||||
if ( (size <= 0) && (con.getLastSendId() >= 0) ) {
|
||||
if (con.getOutputStream().getClosed()) {
|
||||
if (con.getCloseSentOn() < 0) {
|
||||
doSend = true;
|
||||
} else {
|
||||
// closed, no new data, and we've already sent a close packet
|
||||
@ -57,16 +62,18 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
if (_connection.getUnackedPacketsReceived() > 0)
|
||||
if (con.getUnackedPacketsReceived() > 0)
|
||||
doSend = true;
|
||||
|
||||
if (_log.shouldLog(Log.INFO) && !doSend)
|
||||
_log.info("writeData called: size="+size + " doSend=" + doSend
|
||||
+ " unackedReceived: " + _connection.getUnackedPacketsReceived()
|
||||
+ " con: " + _connection, new Exception("write called by"));
|
||||
+ " unackedReceived: " + con.getUnackedPacketsReceived()
|
||||
+ " con: " + con, new Exception("write called by"));
|
||||
|
||||
if (doSend) {
|
||||
PacketLocal packet = send(buf, off, size);
|
||||
if (packet == null) return _dummyStatus;
|
||||
|
||||
//dont wait for non-acks
|
||||
if ( (packet.getSequenceNum() > 0) || (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) )
|
||||
return packet;
|
||||
@ -85,7 +92,7 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
|
||||
* @param buf data to be sent - may be null
|
||||
* @param off offset into the buffer to start writing from
|
||||
* @param size how many bytes of the buffer to write (may be 0)
|
||||
* @return the packet sent
|
||||
* @return the packet sent, or null if the connection died
|
||||
*/
|
||||
public PacketLocal send(byte buf[], int off, int size) {
|
||||
return send(buf, off, size, false);
|
||||
@ -99,31 +106,33 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
|
||||
* @return the packet sent
|
||||
*/
|
||||
public PacketLocal send(byte buf[], int off, int size, boolean forceIncrement) {
|
||||
Connection con = _connection;
|
||||
if (con == null) return null;
|
||||
long before = System.currentTimeMillis();
|
||||
PacketLocal packet = buildPacket(buf, off, size, forceIncrement);
|
||||
PacketLocal packet = buildPacket(con, buf, off, size, forceIncrement);
|
||||
long built = System.currentTimeMillis();
|
||||
_connection.sendPacket(packet);
|
||||
con.sendPacket(packet);
|
||||
long sent = System.currentTimeMillis();
|
||||
|
||||
if ( (built-before > 1000) && (_log.shouldLog(Log.WARN)) )
|
||||
if ( (built-before > 5*1000) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn("wtf, took " + (built-before) + "ms to build a packet: " + packet);
|
||||
if ( (sent-built> 1000) && (_log.shouldLog(Log.WARN)) )
|
||||
if ( (sent-built> 5*1000) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn("wtf, took " + (sent-built) + "ms to send a packet: " + packet);
|
||||
return packet;
|
||||
}
|
||||
|
||||
private boolean isAckOnly(int size) {
|
||||
private boolean isAckOnly(Connection con, int size) {
|
||||
boolean ackOnly = ( (size <= 0) && // no data
|
||||
(_connection.getLastSendId() >= 0) && // not a SYN
|
||||
( (!_connection.getOutputStream().getClosed()) || // not a CLOSE
|
||||
(_connection.getOutputStream().getClosed() &&
|
||||
_connection.getCloseSentOn() > 0) )); // or it is a dup CLOSE
|
||||
(con.getLastSendId() >= 0) && // not a SYN
|
||||
( (!con.getOutputStream().getClosed()) || // not a CLOSE
|
||||
(con.getOutputStream().getClosed() &&
|
||||
con.getCloseSentOn() > 0) )); // or it is a dup CLOSE
|
||||
return ackOnly;
|
||||
}
|
||||
|
||||
private PacketLocal buildPacket(byte buf[], int off, int size, boolean forceIncrement) {
|
||||
boolean ackOnly = isAckOnly(size);
|
||||
PacketLocal packet = new PacketLocal(_context, _connection.getRemotePeer(), _connection);
|
||||
private PacketLocal buildPacket(Connection con, byte buf[], int off, int size, boolean forceIncrement) {
|
||||
boolean ackOnly = isAckOnly(con, size);
|
||||
PacketLocal packet = new PacketLocal(_context, con.getRemotePeer(), con);
|
||||
byte data[] = new byte[size];
|
||||
if (size > 0)
|
||||
System.arraycopy(buf, off, data, 0, size);
|
||||
@ -131,36 +140,36 @@ class ConnectionDataReceiver implements MessageOutputStream.DataReceiver {
|
||||
if (ackOnly && !forceIncrement)
|
||||
packet.setSequenceNum(0);
|
||||
else
|
||||
packet.setSequenceNum(_connection.getNextOutboundPacketNum());
|
||||
packet.setSendStreamId(_connection.getSendStreamId());
|
||||
packet.setReceiveStreamId(_connection.getReceiveStreamId());
|
||||
packet.setSequenceNum(con.getNextOutboundPacketNum());
|
||||
packet.setSendStreamId(con.getSendStreamId());
|
||||
packet.setReceiveStreamId(con.getReceiveStreamId());
|
||||
|
||||
_connection.getInputStream().updateAcks(packet);
|
||||
packet.setOptionalDelay(_connection.getOptions().getChoke());
|
||||
packet.setOptionalMaxSize(_connection.getOptions().getMaxMessageSize());
|
||||
packet.setResendDelay(_connection.getOptions().getResendDelay());
|
||||
con.getInputStream().updateAcks(packet);
|
||||
packet.setOptionalDelay(con.getOptions().getChoke());
|
||||
packet.setOptionalMaxSize(con.getOptions().getMaxMessageSize());
|
||||
packet.setResendDelay(con.getOptions().getResendDelay());
|
||||
|
||||
if (_connection.getOptions().getProfile() == ConnectionOptions.PROFILE_INTERACTIVE)
|
||||
if (con.getOptions().getProfile() == ConnectionOptions.PROFILE_INTERACTIVE)
|
||||
packet.setFlag(Packet.FLAG_PROFILE_INTERACTIVE, true);
|
||||
else
|
||||
packet.setFlag(Packet.FLAG_PROFILE_INTERACTIVE, false);
|
||||
|
||||
packet.setFlag(Packet.FLAG_SIGNATURE_REQUESTED, _connection.getOptions().getRequireFullySigned());
|
||||
packet.setFlag(Packet.FLAG_SIGNATURE_REQUESTED, con.getOptions().getRequireFullySigned());
|
||||
|
||||
if ( (!ackOnly) && (packet.getSequenceNum() <= 0) ) {
|
||||
packet.setFlag(Packet.FLAG_SYNCHRONIZE);
|
||||
packet.setOptionalFrom(_connection.getSession().getMyDestination());
|
||||
packet.setOptionalFrom(con.getSession().getMyDestination());
|
||||
}
|
||||
|
||||
// don't set the closed flag if this is a plain ACK and there are outstanding
|
||||
// packets sent, otherwise the other side could receive the CLOSE prematurely,
|
||||
// since this ACK could arrive before the unacked payload message.
|
||||
if (_connection.getOutputStream().getClosed() &&
|
||||
( (size > 0) || (_connection.getUnackedPacketsSent() <= 0) ) ) {
|
||||
if (con.getOutputStream().getClosed() &&
|
||||
( (size > 0) || (con.getUnackedPacketsSent() <= 0) || (packet.getSequenceNum() > 0) ) ) {
|
||||
packet.setFlag(Packet.FLAG_CLOSE);
|
||||
_connection.setCloseSentOn(_context.clock().now());
|
||||
con.setCloseSentOn(_context.clock().now());
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Closed is set for a new packet on " + _connection + ": " + packet);
|
||||
_log.debug("Closed is set for a new packet on " + con + ": " + packet);
|
||||
} else {
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Closed is not set for a new packet on " + _connection + ": " + packet);
|
||||
|
@ -366,7 +366,8 @@ public class ConnectionManager {
|
||||
if (removed) {
|
||||
if (_notifier != null)
|
||||
_notifier.pingComplete(false);
|
||||
_log.error("Ping failed");
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Ping failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,6 +89,8 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
setInactivityTimeout(getInt(opts, PROP_INACTIVITY_TIMEOUT, 5*60*1000));
|
||||
setInactivityAction(getInt(opts, PROP_INACTIVITY_ACTION, INACTIVITY_ACTION_DISCONNECT));
|
||||
setInboundBufferSize((getMaxMessageSize() + 2) * Connection.MAX_WINDOW_SIZE);
|
||||
|
||||
setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,6 +25,7 @@ public class ConnectionPacketHandler {
|
||||
_context.statManager().createRateStat("stream.con.receiveDuplicateSize", "Size of a duplicate message received on a connection", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.con.packetsAckedPerMessageReceived", "Size of a duplicate message received on a connection", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.sendsBeforeAck", "How many times a message was sent before it was ACKed?", "Stream", new long[] { 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.resetReceived", "How many messages had we sent successfully before receiving a RESET?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
}
|
||||
|
||||
/** distribute a packet to the connection specified */
|
||||
@ -35,12 +36,27 @@ public class ConnectionPacketHandler {
|
||||
_log.error("Packet does NOT verify: " + packet);
|
||||
return;
|
||||
}
|
||||
|
||||
if (con.getHardDisconnected()) {
|
||||
if ( (packet.getSequenceNum() > 0) || (packet.getPayloadSize() > 0) ||
|
||||
(packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) || (packet.isFlagSet(Packet.FLAG_CLOSE)) ) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Received a data packet after hard disconnect: " + packet + " on " + con);
|
||||
con.sendReset();
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Received a packet after hard disconnect, ignoring: " + packet + " on " + con);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
con.packetReceived();
|
||||
|
||||
long ready = con.getInputStream().getHighestReadyBockId();
|
||||
int available = con.getOptions().getInboundBufferSize() - con.getInputStream().getTotalReadySize();
|
||||
int allowedBlocks = available/con.getOptions().getMaxMessageSize();
|
||||
if (packet.getSequenceNum() > ready + allowedBlocks) {
|
||||
if ( (packet.getPayloadSize() > 0) && (packet.getSequenceNum() > ready + allowedBlocks) ) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Inbound buffer exceeded on connection " + con + " ("
|
||||
+ ready + "/"+ (ready+allowedBlocks) + "/" + available
|
||||
@ -65,6 +81,8 @@ public class ConnectionPacketHandler {
|
||||
if (packet.isFlagSet(Packet.FLAG_CLOSE) && packet.isFlagSet(Packet.FLAG_SIGNATURE_INCLUDED))
|
||||
con.closeReceived();
|
||||
|
||||
boolean fastAck = false;
|
||||
|
||||
if (isNew) {
|
||||
con.incrementUnackedPacketsReceived();
|
||||
con.incrementBytesReceived(packet.getPayloadSize());
|
||||
@ -82,18 +100,17 @@ public class ConnectionPacketHandler {
|
||||
_log.debug("Scheduling ack in " + delay + "ms for received packet " + packet);
|
||||
}
|
||||
} else {
|
||||
if ( (packet.getSequenceNum() > 0) || (packet.getPayloadSize() > 0) ) {
|
||||
if ( (packet.getSequenceNum() > 0) || (packet.getPayloadSize() > 0) ||
|
||||
(packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) ) {
|
||||
_context.statManager().addRateData("stream.con.receiveDuplicateSize", packet.getPayloadSize(), 0);
|
||||
con.incrementDupMessagesReceived(1);
|
||||
|
||||
// take note of congestion
|
||||
//con.getOptions().setResendDelay(con.getOptions().getResendDelay()*2);
|
||||
//con.getOptions().setWindowSize(con.getOptions().getWindowSize()/2);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("congestion.. dup " + packet);
|
||||
SimpleTimer.getInstance().addEvent(new AckDup(con), con.getOptions().getSendAckDelay());
|
||||
//con.incrementUnackedPacketsReceived();
|
||||
con.setNextSendTime(_context.clock().now() + con.getOptions().getSendAckDelay());
|
||||
//con.setNextSendTime(_context.clock().now() + con.getOptions().getSendAckDelay());
|
||||
//fastAck = true;
|
||||
} else {
|
||||
if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) {
|
||||
//con.incrementUnackedPacketsReceived();
|
||||
@ -105,10 +122,10 @@ public class ConnectionPacketHandler {
|
||||
}
|
||||
}
|
||||
|
||||
boolean fastAck = ack(con, packet.getAckThrough(), packet.getNacks(), packet, isNew);
|
||||
fastAck = fastAck || ack(con, packet.getAckThrough(), packet.getNacks(), packet, isNew);
|
||||
con.eventOccurred();
|
||||
if (fastAck) {
|
||||
if (con.getLastSendTime() + con.getOptions().getRTT() < _context.clock().now()) {
|
||||
if (con.getLastSendTime() + 2000 < _context.clock().now()) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Fast ack for dup " + packet);
|
||||
con.ackImmediately();
|
||||
@ -264,6 +281,8 @@ public class ConnectionPacketHandler {
|
||||
con.resetReceived();
|
||||
con.eventOccurred();
|
||||
|
||||
_context.statManager().addRateData("stream.resetReceived", con.getHighestAckedThrough(), con.getLifetime());
|
||||
|
||||
// no further processing
|
||||
return;
|
||||
}
|
||||
|
@ -32,6 +32,8 @@ public class I2PSocketFull implements I2PSocket {
|
||||
destroy();
|
||||
}
|
||||
|
||||
Connection getConnection() { return _connection; }
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return _connection.getInputStream();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -152,16 +152,28 @@ public class PacketHandler {
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Receive a syn packet with the wrong IDs: " + packet);
|
||||
_log.warn("Receive a syn packet with the wrong IDs, sending reset: " + packet);
|
||||
sendReset(packet);
|
||||
}
|
||||
} else {
|
||||
// someone is sending us a packet on the wrong stream
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Received a packet on the wrong stream: " + packet);
|
||||
_log.warn("Received a packet on the wrong stream: " + packet + " connection: " + con);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendReset(Packet packet) {
|
||||
PacketLocal reply = new PacketLocal(_context, packet.getOptionalFrom());
|
||||
reply.setFlag(Packet.FLAG_RESET);
|
||||
reply.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
|
||||
reply.setSendStreamId(packet.getReceiveStreamId());
|
||||
reply.setReceiveStreamId(packet.getSendStreamId());
|
||||
reply.setOptionalFrom(_manager.getSession().getMyDestination());
|
||||
// this just sends the packet - no retries or whatnot
|
||||
_manager.getPacketQueue().enqueue(reply);
|
||||
}
|
||||
|
||||
private void receiveUnknownCon(Packet packet, byte sendId[]) {
|
||||
if (packet.isFlagSet(Packet.FLAG_ECHO)) {
|
||||
if (packet.getSendStreamId() != null) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -38,6 +38,7 @@ class SchedulerChooser {
|
||||
|
||||
private List createSchedulers() {
|
||||
List rv = new ArrayList(8);
|
||||
rv.add(new SchedulerHardDisconnected(_context));
|
||||
rv.add(new SchedulerPreconnect(_context));
|
||||
rv.add(new SchedulerConnecting(_context));
|
||||
rv.add(new SchedulerReceived(_context));
|
||||
@ -54,8 +55,7 @@ class SchedulerChooser {
|
||||
}
|
||||
|
||||
public void eventOccurred(Connection con) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Event occurred on " + con, new Exception("source"));
|
||||
_log.log(Log.CRIT, "Yell at jrandom: Event occurred on " + con, new Exception("source"));
|
||||
}
|
||||
public boolean accept(Connection con) { return true; }
|
||||
};
|
||||
|
@ -37,7 +37,7 @@ class SchedulerConnecting extends SchedulerImpl {
|
||||
public boolean accept(Connection con) {
|
||||
if (con == null) return false;
|
||||
boolean notYetConnected = (con.getIsConnected()) &&
|
||||
(con.getSendStreamId() == null) &&
|
||||
//(con.getSendStreamId() == null) && // not null on recv
|
||||
(con.getLastSendId() >= 0) &&
|
||||
(con.getAckedPackets() <= 0) &&
|
||||
(!con.getResetReceived());
|
||||
@ -55,6 +55,7 @@ class SchedulerConnecting extends SchedulerImpl {
|
||||
_log.debug("waited too long: " + waited);
|
||||
return;
|
||||
} else {
|
||||
// should we be doing a con.sendAvailable here?
|
||||
if (con.getOptions().getConnectTimeout() > 0)
|
||||
reschedule(con.getOptions().getConnectTimeout(), con);
|
||||
}
|
||||
|
@ -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 &&
|
||||
|
@ -0,0 +1,47 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* <p>Scheduler used after we've locally done a hard disconnect,
|
||||
* but the final timeout hasn't passed.</p>
|
||||
*
|
||||
* <h2>Entry conditions:</h2><ul>
|
||||
* <li>Locally disconnected hard.</li>
|
||||
* <li>Less than the final timeout period has passed since the last ACK.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Events:</h2><ul>
|
||||
* <li>Packets received</li>
|
||||
* <li>RESET received</li>
|
||||
* <li>Message sending fails (error talking to the session)</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Next states:</h2>
|
||||
* <li>{@link SchedulerDead dead} - after the final timeout passes</li>
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
*/
|
||||
class SchedulerHardDisconnected extends SchedulerImpl {
|
||||
private Log _log;
|
||||
public SchedulerHardDisconnected(I2PAppContext ctx) {
|
||||
super(ctx);
|
||||
_log = ctx.logManager().getLog(SchedulerHardDisconnected.class);
|
||||
}
|
||||
|
||||
public boolean accept(Connection con) {
|
||||
if (con == null) return false;
|
||||
long timeSinceClose = _context.clock().now() - con.getCloseSentOn();
|
||||
if (con.getResetSent())
|
||||
timeSinceClose = _context.clock().now() - con.getResetSentOn();
|
||||
boolean ok = (con.getHardDisconnected() || con.getResetSent()) &&
|
||||
(timeSinceClose < Connection.DISCONNECT_TIMEOUT);
|
||||
return ok;
|
||||
}
|
||||
|
||||
public void eventOccurred(Connection con) {
|
||||
// noop. we do the timeout through the simpleTimer anyway
|
||||
}
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Have a client connect to a server, where the server waits 5
|
||||
* seconds and closes the socket and the client detect that
|
||||
* EOF.
|
||||
*
|
||||
*/
|
||||
public class ConnectCloseTest {
|
||||
private Log _log;
|
||||
private I2PSession _server;
|
||||
public void test() {
|
||||
try {
|
||||
I2PAppContext context = I2PAppContext.getGlobalContext();
|
||||
_log = context.logManager().getLog(ConnectCloseTest.class);
|
||||
_log.debug("creating server session");
|
||||
_server = createSession();
|
||||
_log.debug("running server");
|
||||
runServer(context, _server);
|
||||
_log.debug("running client");
|
||||
runClient(context, createSession());
|
||||
} catch (Exception e) {
|
||||
_log.error("error running", e);
|
||||
}
|
||||
try { Thread.sleep(10*60*1000); } catch (Exception e) {}
|
||||
}
|
||||
|
||||
private void runClient(I2PAppContext ctx, I2PSession session) {
|
||||
Thread t = new Thread(new ClientRunner(ctx, session));
|
||||
t.setName("client");
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
private void runServer(I2PAppContext ctx, I2PSession session) {
|
||||
Thread t = new Thread(new ServerRunner(ctx, session));
|
||||
t.setName("server");
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
private class ServerRunner implements Runnable {
|
||||
private I2PAppContext _context;
|
||||
private I2PSession _session;
|
||||
private Log _log;
|
||||
public ServerRunner(I2PAppContext ctx, I2PSession session) {
|
||||
_context = ctx;
|
||||
_session = session;
|
||||
_log = ctx.logManager().getLog(ServerRunner.class);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
Properties opts = new Properties();
|
||||
I2PSocketManager mgr = new I2PSocketManagerFull(_context, _session, opts, "client");
|
||||
_log.debug("* manager created");
|
||||
I2PServerSocket ssocket = mgr.getServerSocket();
|
||||
_log.debug("* server socket created");
|
||||
while (true) {
|
||||
I2PSocket socket = ssocket.accept();
|
||||
_log.debug("* socket accepted: " + socket);
|
||||
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
|
||||
socket.close();
|
||||
_log.debug("* socket closed: " + socket);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
_log.error("error running", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class ClientRunner implements Runnable {
|
||||
private I2PAppContext _context;
|
||||
private I2PSession _session;
|
||||
private Log _log;
|
||||
public ClientRunner(I2PAppContext ctx, I2PSession session) {
|
||||
_context = ctx;
|
||||
_session = session;
|
||||
_log = ctx.logManager().getLog(ClientRunner.class);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
Properties opts = new Properties();
|
||||
I2PSocketManager mgr = new I2PSocketManagerFull(_context, _session, opts, "client");
|
||||
_log.debug("* manager created");
|
||||
I2PSocket socket = mgr.connect(_server.getMyDestination());
|
||||
_log.debug("* socket created");
|
||||
InputStream in = socket.getInputStream();
|
||||
int c = in.read();
|
||||
if (c != -1)
|
||||
throw new RuntimeException("hrm, we got data? [" + c + "]");
|
||||
socket.close();
|
||||
_log.debug("* socket closed");
|
||||
mgr.destroySocketManager();
|
||||
mgr = null;
|
||||
socket = null;
|
||||
} catch (Exception e) {
|
||||
_log.error("error running", e);
|
||||
}
|
||||
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private I2PSession createSession() {
|
||||
try {
|
||||
I2PClient client = I2PClientFactory.createClient();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
|
||||
Destination dest = client.createDestination(baos);
|
||||
I2PSession sess = client.createSession(new ByteArrayInputStream(baos.toByteArray()), System.getProperties());
|
||||
sess.connect();
|
||||
return sess;
|
||||
} catch (Exception e) {
|
||||
_log.error("error running", e);
|
||||
throw new RuntimeException("b0rk b0rk b0rk");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
ConnectCloseTest ct = new ConnectCloseTest();
|
||||
ct.test();
|
||||
}
|
||||
}
|
@ -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" />
|
||||
|
@ -1,5 +1,5 @@
|
||||
Prior to building the jbigi library, you will need to fetch the GMP source
|
||||
from http://www.swox.com/gmp/, saving it to jbigi/gmp-4.1.3.tar.bz2 (it will
|
||||
from http://www.swox.com/gmp/, saving it to jbigi/gmp-4.1.4.tar.bz2 (it will
|
||||
be unpacked and built as necessary).
|
||||
|
||||
To build the native jbigi and jcpuid libraries for the current host CPU,
|
||||
|
@ -7,7 +7,6 @@ mkdir -p t/freenet/support/CPUInformation/
|
||||
cp jcpuid/lib/freenet/support/CPUInformation/*jcpuid* t/freenet/support/CPUInformation/
|
||||
|
||||
mkdir -p t/net/i2p/util/
|
||||
cp jbigi/lib/net/i2p/util/*jbigi* t/net/i2p/util/
|
||||
cp jbigi/lib/*jbigi* t/
|
||||
|
||||
(cd t ; jar cf ../jbigi.jar . ; cd ..)
|
||||
|
@ -13,7 +13,7 @@ FreeBSD*)
|
||||
esac
|
||||
|
||||
echo "Extracting GMP..."
|
||||
tar -xjf gmp-4.1.3.tar.bz2
|
||||
tar -xjf gmp-4.1.4.tar.bz2
|
||||
echo "Building..."
|
||||
mkdir bin
|
||||
mkdir lib
|
||||
@ -24,7 +24,7 @@ for x in none pentium pentiummmx pentium2 pentium3 pentium4 k6 k62 k63 athlon
|
||||
do
|
||||
mkdir bin/$x
|
||||
cd bin/$x
|
||||
../../gmp-4.1.3/configure --build=$x
|
||||
../../gmp-4.1.4/configure --build=$x
|
||||
make
|
||||
sh ../../build_jbigi.sh static
|
||||
case `uname -sr` in
|
||||
|
@ -1,41 +1,15 @@
|
||||
#/bin/sh
|
||||
|
||||
case `uname -sr` in
|
||||
MINGW*)
|
||||
echo "Building windows .dll's";;
|
||||
CYGWIN*)
|
||||
echo "Building windows .dll's";;
|
||||
Linux*)
|
||||
echo "Building linux .so's";;
|
||||
FreeBSD*)
|
||||
echo "Building freebsd .so's";;
|
||||
*)
|
||||
echo "Unsupported build environment"
|
||||
exit;;
|
||||
esac
|
||||
echo "Building the jbigi library with GMP"
|
||||
|
||||
echo "Extracting GMP..."
|
||||
tar -xjf gmp-4.1.3.tar.bz2
|
||||
tar -xjf gmp-4.1.4.tar.bz2
|
||||
echo "Building..."
|
||||
mkdir bin
|
||||
mkdir lib
|
||||
mkdir lib/net
|
||||
mkdir lib/net/i2p
|
||||
mkdir lib/net/i2p/util
|
||||
mkdir bin/local
|
||||
mkdir -p lib/
|
||||
mkdir -p bin/local
|
||||
cd bin/local
|
||||
../../gmp-4.1.3/configure
|
||||
../../gmp-4.1.4/configure
|
||||
make
|
||||
sh ../../build_jbigi.sh static
|
||||
case `uname -sr` in
|
||||
MINGW*)
|
||||
cp jbigi.dll ../../lib/jbigi;;
|
||||
CYGWIN*)
|
||||
cp jbigi.dll ../../lib/jbigi;;
|
||||
Linux*)
|
||||
cp libjbigi.so ../../lib/jbigi;;
|
||||
FreeBSD*)
|
||||
cp libjbigi.so ../../lib/jbigi;;
|
||||
esac
|
||||
cd ..
|
||||
cd ..
|
||||
cp *jbigi???* ../../lib/
|
||||
cd ../..
|
||||
|
@ -20,7 +20,7 @@ mkdir lib/freenet
|
||||
mkdir lib/freenet/support
|
||||
mkdir lib/freenet/support/CPUInformation
|
||||
|
||||
CPP="g++"
|
||||
CC="gcc"
|
||||
|
||||
case `uname -sr` in
|
||||
MINGW*)
|
||||
@ -43,7 +43,7 @@ esac
|
||||
|
||||
echo "Compiling C code..."
|
||||
rm -f $LIBFILE
|
||||
$CPP $LINKFLAGS $INCLUDES src/*.cpp -o $LIBFILE
|
||||
$CC $LINKFLAGS $INCLUDES src/*.c -o $LIBFILE
|
||||
strip $LIBFILE
|
||||
echo Built $LIBFILE
|
||||
|
||||
|
@ -5,8 +5,8 @@ JNIEXPORT jobject JNICALL Java_freenet_support_CPUInformation_CPUID_doCPUID
|
||||
(JNIEnv * env, jclass cls, jint iFunction)
|
||||
{
|
||||
int a,b,c,d;
|
||||
jclass clsResult = env->FindClass ("freenet/support/CPUInformation/CPUID$CPUIDResult");
|
||||
jmethodID constructor = env->GetMethodID(clsResult,"<init>","(IIII)V" );
|
||||
jclass clsResult = (*env)->FindClass(env, "freenet/support/CPUInformation/CPUID$CPUIDResult");
|
||||
jmethodID constructor = (*env)->GetMethodID(env, clsResult,"<init>","(IIII)V" );
|
||||
#ifdef _MSC_VER
|
||||
//Use MSVC assembler notation
|
||||
_asm
|
||||
@ -30,6 +30,6 @@ JNIEXPORT jobject JNICALL Java_freenet_support_CPUInformation_CPUID_doCPUID
|
||||
:"a"(iFunction)
|
||||
);
|
||||
#endif
|
||||
return env->NewObject(clsResult,constructor,a,b,c,d);
|
||||
return (*env)->NewObject(env, clsResult,constructor,a,b,c,d);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -22,7 +22,8 @@ public abstract class NamingService {
|
||||
private final static Log _log = new Log(NamingService.class);
|
||||
protected I2PAppContext _context;
|
||||
|
||||
private static final String PROP_IMPL = "i2p.naming.impl";
|
||||
/** what classname should be used as the naming service impl? */
|
||||
public static final String PROP_IMPL = "i2p.naming.impl";
|
||||
private static final String DEFAULT_IMPL = "net.i2p.client.naming.HostsTxtNamingService";
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -559,6 +559,16 @@ public class DataHelper {
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
public final static boolean eq(byte lhs[], int offsetLeft, byte rhs[], int offsetRight, int length) {
|
||||
if ( (lhs == null) || (rhs == null) ) return false;
|
||||
if (length <= 0) return true;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (lhs[offsetLeft + i] != rhs[offsetRight + i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public final static int compareTo(byte lhs[], byte rhs[]) {
|
||||
if ((rhs == null) && (lhs == null)) return 0;
|
||||
if (lhs == null) return -1;
|
||||
@ -680,7 +690,19 @@ public class DataHelper {
|
||||
*/
|
||||
public static String readLine(InputStream in) throws IOException {
|
||||
StringBuffer buf = new StringBuffer(128);
|
||||
|
||||
boolean ok = readLine(in, buf);
|
||||
if (ok)
|
||||
return buf.toString();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Read in a line, placing it into the buffer (excluding the newline).
|
||||
*
|
||||
* @return true if the line was read, false if eof was reached before a
|
||||
* newline was found
|
||||
*/
|
||||
public static boolean readLine(InputStream in, StringBuffer buf) throws IOException {
|
||||
int c = -1;
|
||||
while ( (c = in.read()) != -1) {
|
||||
if (c == '\n')
|
||||
@ -688,9 +710,9 @@ public class DataHelper {
|
||||
buf.append((char)c);
|
||||
}
|
||||
if (c == -1)
|
||||
return null;
|
||||
return false;
|
||||
else
|
||||
return buf.toString();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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");
|
||||
|
@ -124,7 +124,8 @@ public class Timestamper implements Runnable {
|
||||
alreadyBitched = true;
|
||||
}
|
||||
}
|
||||
try { Thread.sleep(_queryFrequency); } catch (InterruptedException ie) {}
|
||||
long sleepTime = _context.random().nextInt(_queryFrequency) + _queryFrequency;
|
||||
try { Thread.sleep(sleepTime); } catch (InterruptedException ie) {}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
_log.log(Log.CRIT, "Timestamper died!", t);
|
||||
|
@ -42,8 +42,8 @@ public class Clock implements Timestamper.UpdateListener {
|
||||
|
||||
/** if the clock is skewed by 3+ days, fuck 'em */
|
||||
public final static long MAX_OFFSET = 3 * 24 * 60 * 60 * 1000;
|
||||
/** after we've started up and shifted the clock, don't allow shifts of more than a minute */
|
||||
public final static long MAX_LIVE_OFFSET = 60 * 1000;
|
||||
/** after we've started up and shifted the clock, don't allow shifts of more than 10 minutes */
|
||||
public final static long MAX_LIVE_OFFSET = 10 * 60 * 1000;
|
||||
/** if the clock skewed changes by less than 1s, ignore the update (so we don't slide all over the place) */
|
||||
public final static long MIN_OFFSET_CHANGE = 10 * 1000;
|
||||
|
||||
|
206
core/java/test/net/i2p/crypto/ElGamalVerify.java
Normal file
206
core/java/test/net/i2p/crypto/ElGamalVerify.java
Normal file
@ -0,0 +1,206 @@
|
||||
package net.i2p.crypto;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.PublicKey;
|
||||
|
||||
/**
|
||||
* Unit test verifying the ElGamal encryption/decryption with some test
|
||||
* data. The keys generated & data stored were generated by jrandom on
|
||||
* a pentium4 w/ an optimized jbigi installed and verified with pure java.
|
||||
*
|
||||
*/
|
||||
public class ElGamalVerify {
|
||||
private I2PAppContext _context;
|
||||
|
||||
private static final String UNENCRYPTED[] = new String[] {
|
||||
"",
|
||||
"hello world",
|
||||
"1234567890123456789012345678901234567890123456789012345678901234567890" +
|
||||
"1234567890123456789012345678901234567890123456789012345678901234567890" +
|
||||
"1234567890123456789012345678901234567890123456789012345678901234567890" +
|
||||
"123456789012",
|
||||
"\0x00",
|
||||
"\0x00\0x00\0x00",
|
||||
"\0x00\0x01\0x02\0x00",
|
||||
};
|
||||
private static final String PUBLIC_KEY = new String(
|
||||
"pOvBUMrSUUeN5awynzbPbCAwe3MqWprhSpp3OR7pvdfm9PhWaNbPoKRLeEmDoUwyNDoHE0" +
|
||||
"E6mcZSG8qPQ8XUZFlczpilOl0MJBvsI9u9SMyi~bEqzSgzh9FNfS-NcGji3q2wI~Ux~q5B" +
|
||||
"KOjGlyMLgd1nxl5R5wIYL4uHKZNaYuArsRYmtV~MgMQPGvDtIbdGTV6aL6UbOYryzQSUMY" +
|
||||
"OuO3S~YoBjA6Nmi0SeJM3tyTxlI6U1EYjR6oQcI4SOFUW4L~8pfYWijcncCODAqpXVN6ZI" +
|
||||
"AJ3a6vjxGu56IDp4xCcKlOEHgdXvqmEC67dR5qf2btH6dtWoB3-Z6QPsS6tPTQ=="
|
||||
);
|
||||
private static final String PRIVATE_KEY = new String(
|
||||
"gMlIhURVXU8uPube20Xr8E1K11g-3qZxOj1riThHqt-rBx72MPq5ivT1rr28cE9mzOmsXi" +
|
||||
"bbsuBuQKYDvF7hGICRB3ROSPePYhcupV3j7XiXUIYjWNw9hvylHXK~nTT7jkpIBazBJZfr" +
|
||||
"LJPcDZTDB0YnCOHOL-KFn4N1R5B22g0iYRABN~O10AUjQmf1epklAXPqYlzmOYeJSfTPBI" +
|
||||
"E44nEccWJp0M0KynhKVbDI0v9VYm6sPFK7WrzRyWwHL~r735wiRkwywuMmKJtA7-PuJjcW" +
|
||||
"NLkJwx6WScH2msMzhzYPi8JSZJBl~PosX934l-L0T-KNV4jg1Ih6yoCnm1748A=="
|
||||
);
|
||||
private static final String ENCRYPTED[] = new String[] {
|
||||
"AMfISa8KvTpaC7KXZzSvC2axyiSk0xPexBAf29yU~IKq21DzaU19wQcGJg-ktpG4hjGSg7" +
|
||||
"u-mJ07b61yo-EGmVGZsv3nYuQYW-GjvsZQa9nm98VljlMtWrxu7TsRXw~SQlWQxMvthqJB" +
|
||||
"1A7Y7Qa~C7-UlRytkD-cpVdgUfM-esuMWmjGs6Vc33N5U-tce5Fywa-9y7PSn3ukBO8KGR" +
|
||||
"wm7T12~H2gvhgxrVeK2roOzsV7f5dGkvBQRZJ309Vg3j0kjaxWutgI3vli0pzDbSK9d5NR" +
|
||||
"-GUDtdOb6IIfLiOckBegcv6I-wlSXjYJe8mIoaK45Ok3rEpHwWKVKS2MeuI7AmsAWgkQmW" +
|
||||
"f8irmZaKc9X910VWSO5GYu6006hSc~r2TL3O7vwtW-Z9Oq~sAam9av1PPVJzAx8A4g~m~1" +
|
||||
"avtNnncwlChsGo6mZHXqz-QMdMJXXP57f4bx36ZomkvpM-ZLlFAn-a~42KQJAApo4LfEyk" +
|
||||
"7DPY2aTXL9ArOCNQIQB4f8QLyjvAvu6M3jzCoGo0wVX6oePfdiokGflriYOcD8rL4NbnCP" +
|
||||
"~MSnVzC8LKyRzQVN1tDYj8~njuFqekls6En8KFJ-qgtL4PiYxbnBQDUPoW6y61m-S9r9e9" +
|
||||
"y8qWd6~YtdAHAxVlw287~HEp9r7kqI-cjdo1337b7~5dm83KK45g5Nfw==",
|
||||
|
||||
"AIrd65mG1FJ~9J-DDSyhryVejJBSIjYOqV3GYmHDWgwLchTwq-bJS7dub3ENk9MZ-C6FIN" +
|
||||
"gjUFRaLBtfwJnySmNf8pIf1srmgdfqGV2h77ufG5Gs0jggKPmPV~7Z1kTcgsqpL8MyrfXr" +
|
||||
"Gi86X5ey-T0SZSFc0X1EhaE-47WlyWaGf-~xth6VOR~KG7clOxaOBpks-7WKZNQf7mpQRE" +
|
||||
"4IsPJyj5p1Rf-MeDbVKbK~52IfXSuUZQ8uZr34KMoy4chjn6e-jBhM4XuaQWhsM~a3Q-zE" +
|
||||
"pV-ea6t0bQTYfsbG9ch7pJuDPHM64o5mF9FS5-JGr7MOtfP7KDNHiYM2~-uC6BIAbiqBN8" +
|
||||
"WSLX1mrHVuhiM-hiJ7U4oq~HYB6N~U980sCIW0dgFBbhalzzQhJQSrC1DFDqGfL5-L25mj" +
|
||||
"ArP8dtvN0JY3LSnbcsm-pT9ttFHCPGomLfaAuP7ohknBoXK0j9e6~splg5sUA9TfLeBfqc" +
|
||||
"Lr0Sf8b3l~PvmrVkbVcaE8yUqSS6JFdt3pavjyyAQSmSlb2jVNKGPlrov5QLzlbH7G~AUv" +
|
||||
"IehsbGQX5ptRROtSojN~iYx3WQTOa-JLEC-AL7RbRu6B62p9I0pD0JgbUfCc4C4l9E9W~s" +
|
||||
"MuaJLAXxh0b2miF7C5bzZHxbt~MtZ7Ho5qpZMitXyoE3icb43B6Y1sbA==",
|
||||
|
||||
"ACjb0FkTIQbnEzCZlYXGxekznfJad5uW~F5Mbu~0wtsI1O2veqdr7Mb0N754xdIz7929Ti" +
|
||||
"1Kz-CxVEAkb3RBbVNcYHLfjy23oQ4BCioDKQaJcdkJqXa~Orm7Ta2tbkhM1Mx05MDrQaVF" +
|
||||
"gCVXtwTsPSLVK8VwScjPIFLXgQqqZ5osq~WhaMcYe2I2RCQLOx2VzaKbT21MMbtF70a-nK" +
|
||||
"WovkRUNfJEPeJosFwF2duAD0BHHrPiryK9BPDhyOiyN82ahOi2uim1Nt5yhlP3xo7cLV2p" +
|
||||
"6kTlR1BNC5pYjtsvetZf6wk-solNUrJWIzcuc18uRDNH5K90GTL6FXPMSulM~E4ATRQfhZ" +
|
||||
"fkW9xCrBIaIQM49ms2wONsp7fvI07b1r0rt7ZwCFOFit1HSAKl8UpsAYu-EsIO1qAK7vvO" +
|
||||
"UV~0OuBXkMZEyJT-uIVfbE~xrwPE0zPYE~parSVQgi~yNQBxukUM1smAM5xXVvJu8GjmE-" +
|
||||
"kJZw1cxaYLGsJjDHDk4HfEsyQVVPZ0V3bQvhB1tg5cCsTH~VNjts4taDTPWfDZmjtVaxxr" +
|
||||
"PRII4NEDKqEzg3JBevM~yft-RDfMc8RVlm-gCGANrRQORFii7uD3o9~y~4P2tLnO7Fy3m5" +
|
||||
"rdjRsOsWnCQZzw37mcBoT9rEZPrVpD8pjebJ1~HNc764xIpXDWVt8CbA==",
|
||||
|
||||
"AHDZBKiWeaIYQS9R1l70IlRnoplwKTkLP2dLlXmVh1gB33kx65uX8OMb3hdZEO0Bbzxkkx" +
|
||||
"quqlNn5w166nJO4nPbpEzVfgtY4ClUuv~W4H4CXBr0FcZM1COAkd6rtp6~lUp7cZ8FAkpH" +
|
||||
"spl95IxlFM-F1HwiPcbmTjRO1AwCal4sH8S5WmJCvBU6jH6pBPo~9B9vAtP7vX1EwsG2Jf" +
|
||||
"CQXkVkfvbWpSicbsWn77aECedS3HkIMrXrxojp7gAiPgQhX4NR387rcUPFsMHGeUraTUPZ" +
|
||||
"D7ctk5tpUuYYwRQc5cRKHa4zOq~AQyljx5w5~FByLda--6yCe7qDcILyTygudJ4AHRs1pJ" +
|
||||
"RU3uuRTHZx0XJQo~cPsoQ2piAOohITX9~yMCimCgv2EIhY3Z-mAgo8qQ4iMbItoE1cl93I" +
|
||||
"u2YV2n4wMq9laBx0shuKOJqO3rjRnszzCbqMuFAXfc3KgGDEaCpI7049s3i2yIcv4vT9uU" +
|
||||
"AlrM-dsrdw0JgJiFYl0JXh~TO0IyrcVcLpgZYgRhEvTAdkDNwTs-2GK4tzdPEd34os4a2c" +
|
||||
"DPL8joh3jhp~eGoRzrpcdRekxENdzheL4w3wD1fJ9W2-leil1FH6EPc3FSL6e~nqbw69gN" +
|
||||
"bsuXAMQ6CobukJdJEy37uKmEw4v6WPyfYMUUacchv1JoNfkHLpnAWifQ==",
|
||||
|
||||
"AGwvKAMJcPAliP-n7F0Rrj0JMRaFGjww~zvBjyzc~SPJrBF831cMqZFRmMHotgA7S5BrH2" +
|
||||
"6CL8okI2N-7as0F2l7OPx50dFEwSVSjqBjVV6SGRFC8oS-ii1FURMz2SCHSaj6kazAYq4s" +
|
||||
"DwyqR7vnUrOtPnZujHSU~a02jinyn-QOaHkxRiUp-Oo0jlZiU5xomXgLdkhtuz6725WUDj" +
|
||||
"3uVlMtIYfeKQsTdasujHe1oQhUmp58jfg5vgZ8g87cY8rn4p9DRwDBBuo6vi5on7T13sGx" +
|
||||
"tY9wz6HTpwzDhEqpNrj~h4JibElfi0Jo8ZllmNTO1ZCNpUQgASoTtyFLD5rk6cIAMK0R7A" +
|
||||
"7hjB0aelKM-V7AHkj-Fhrcm8xIgWhKaLn2wKbVNpAkllkiLALyfWJ9dhJ804RWQTMPE-GD" +
|
||||
"kBMIFOOJ9MhpEN533OBQDwUKcoxMjl0zOMNCLx8IdCE6cLtUDKJXLB0atnDpLkBer6FwXP" +
|
||||
"81EvKDYhtp1GsbiKvZDt8LSPJQnm2EdA3Pr9fpAisJ5Ocaxlfa6~uQCuqGA9nJ9n6w03u-" +
|
||||
"ZpSMhSh4zm2s1MqijmaJRc-QNKmN~u1hh3R2hwWNi7FoStMA87sutEBXMdFI8un7StHNSE" +
|
||||
"iCYwmmW2Nu3djkM-X8gGjSsdrphTU7uOXbwazmguobFGxI0JujYruM5Q==",
|
||||
|
||||
"ALFYtPSwEEW3eTO4hLw6PZNlBKoSIseQNBi034gq6FwYEZsJOAo-1VXcvMviKw2MCP9ZkH" +
|
||||
"lTNBfzc79ms2TU8kXxc7zwUc-l2HJLWh6dj2tIQLR8bbWM7U0iUx4XB1B-FEvdhbjz7dsu" +
|
||||
"6SBXVhxo2ulrk7Q7vX3kPrePhZZldcNZcS0t65DHYYwL~E~ROjQwOO4Cb~8FgiIUjb8CCN" +
|
||||
"w5zxJpBaEt7UvZffkVwj-EWTzFy3DIjWIRizxnsI~mUI-VspPE~xlmFX~TwPS9UbwJDpm8" +
|
||||
"-WzINFcehSzF3y9rzSMX-KbU8m4YZj07itZOiIbWgLeulTUB-UgwEkfJBG0xiSUAspZf2~" +
|
||||
"t~NthBlpcdrBLADXTJ7Jmkk4MIfysV~JpDB7IVg0v4WcUUwF3sYMmBCdPCwyYf0hTrl2Yb" +
|
||||
"L6kmm4u97WgQqf0TyzXtVZYwjct4LzZlyH591y6O6AQ4Fydqos9ABInzu-SbXq6S1Hi6vr" +
|
||||
"aNWU3mcy2myie32EEXtkX7P8eXWY35GCv9ThPEYHG5g1qKOk95ZCTYYwlpgeyaMKsnN3C~" +
|
||||
"x9TJA8K8T44v7vE6--Nw4Z4zjepwkIOht9iQsA6D6wRUQpeYX8bjIyYDPC7GUHq0WhXR6E" +
|
||||
"6Ojc9k8V5uh0SZ-rCQX6sccdk3JbyRhjGP4rSKr6MmvxVVsqBjcbpxsg=="
|
||||
};
|
||||
|
||||
public static void main(String args[]) {
|
||||
ElGamalVerify verify = new ElGamalVerify();
|
||||
verify.verifySelf();
|
||||
verify.verifyCompatability();
|
||||
if (args.length > 0)
|
||||
verify.generateEncrypted();
|
||||
}
|
||||
|
||||
public ElGamalVerify() {
|
||||
_context = new I2PAppContext();
|
||||
}
|
||||
|
||||
/** verify that we can decrypt what we encrypt */
|
||||
private void verifySelf() {
|
||||
try {
|
||||
Object keypair[] = _context.keyGenerator().generatePKIKeypair();
|
||||
PublicKey pub = (PublicKey)keypair[0];
|
||||
PrivateKey priv = (PrivateKey)keypair[1];
|
||||
|
||||
for (int i = 0; i < UNENCRYPTED.length; i++) {
|
||||
byte orig[] = UNENCRYPTED[i].getBytes();
|
||||
|
||||
byte encrypted[] = _context.elGamalEngine().encrypt(orig, pub);
|
||||
byte decrypted[] = _context.elGamalEngine().decrypt(encrypted, priv);
|
||||
|
||||
if (DataHelper.eq(decrypted, orig))
|
||||
log("OK : verifySelf[" + i + "] passed");
|
||||
else
|
||||
log("ERROR: verifySelf[" + i + "] failed");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log("ERROR: verifySelf blew up: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/** verify that we can decrypt what other people encrypt */
|
||||
private void verifyCompatability() {
|
||||
verifyDecrypt();
|
||||
}
|
||||
|
||||
private void verifyDecrypt() {
|
||||
try {
|
||||
PublicKey pub = new PublicKey();
|
||||
PrivateKey priv = new PrivateKey();
|
||||
pub.fromBase64(PUBLIC_KEY);
|
||||
priv.fromBase64(PRIVATE_KEY);
|
||||
|
||||
for (int i = 0; i < ENCRYPTED.length; i++) {
|
||||
byte enc[] = Base64.decode(ENCRYPTED[i]);
|
||||
byte decrypted[] = _context.elGamalEngine().decrypt(enc, priv);
|
||||
|
||||
if (DataHelper.eq(decrypted, UNENCRYPTED[i].getBytes()))
|
||||
log("OK : verifyDecrypt[" + i + "] passed");
|
||||
else
|
||||
log("ERROR: verifyDecrypt[" + i + "] failed");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log("ERROR: generateEncrypted blew up: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private void generateEncrypted() {
|
||||
try {
|
||||
Object keypair[] = _context.keyGenerator().generatePKIKeypair();
|
||||
PublicKey pub = (PublicKey)keypair[0];
|
||||
PrivateKey priv = (PrivateKey)keypair[1];
|
||||
|
||||
log("PUBLIC : " + pub.toBase64());
|
||||
log("PRIVATE: " + priv.toBase64());
|
||||
|
||||
for (int i = 0; i < UNENCRYPTED.length; i++) {
|
||||
byte orig[] = UNENCRYPTED[i].getBytes();
|
||||
|
||||
byte encrypted[] = _context.elGamalEngine().encrypt(orig, pub);
|
||||
|
||||
System.out.println("Encrypted [" + i + "]: ");
|
||||
String enc = Base64.encode(encrypted);
|
||||
for (int j = 0; j < enc.length(); j++) {
|
||||
int remaining = enc.length() - j*70;
|
||||
if (remaining > 0) {
|
||||
String cur = enc.substring(j * 70, remaining > 70 ? (j+1)*70 : enc.length());
|
||||
System.out.println(cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log("ERROR: generateEncrypted blew up: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void log(String msg) {
|
||||
System.out.println(msg);
|
||||
}
|
||||
}
|
193
history.txt
193
history.txt
@ -1,4 +1,195 @@
|
||||
$Id: history.txt,v 1.101 2004/12/08 12:16:17 jrandom Exp $
|
||||
$Id: history.txt,v 1.125 2005/01/05 19:17:53 jrandom Exp $
|
||||
|
||||
* 2005-01-06 0.4.2.6 released
|
||||
|
||||
2005-01-06 jrandom
|
||||
* Added a startup message to the addressbook, printing its version number
|
||||
to stdout (which is sent to wrapper.config) when it loads.
|
||||
* Updated the addressbook to reread the config file periodically
|
||||
* Added orion.i2p to the list of eepsites on the default homepage
|
||||
|
||||
2005-01-05 jrandom
|
||||
* Handle unexpected network read errors more carefully (thanks parg!)
|
||||
* Added more methods to partially compare (DataHelper) and display
|
||||
arrays (Base64.encode).
|
||||
* Exposed the AES encryptBlock/decryptBlock on the context.aes()
|
||||
* Be more generous on the throttle when just starting up the router
|
||||
* Fix a missing scheduled event in the streaming lib (caused after reset)
|
||||
* Add a new DisconnectListener on the I2PSocketManager to allow
|
||||
notification of session destruction.
|
||||
* Make sure our own router identity is valid, and if it isn't, build a new
|
||||
one and restart the router. Alternately, you can run the Router with
|
||||
the single command line argument "rebuild" and it will do the same.
|
||||
|
||||
2004-12-31 ragnarok
|
||||
* Integrated latest addressbook changes (2.0.3) which include support for
|
||||
deploying as a .war file with no existing addressbook configuration.
|
||||
* Updated main build process to bundle the addressbook.war in the
|
||||
i2pinstall.jar and i2pupdate.zip.
|
||||
|
||||
2004-12-31 jrandom
|
||||
* Speling fxi (thanks digum!)
|
||||
* Bugfix for the I2PTunnel web interface so that it now properly launches
|
||||
newly added tunnels that are defined to be run on startup (thanks ugha!)
|
||||
|
||||
2004-12-30 jrandom
|
||||
* Revised the I2PTunnel client and httpclient connection establishment
|
||||
throttles. There is now a pool of threads that build the I2PSocket
|
||||
connections with a default size of 5, configurable via the I2PTunnel
|
||||
client option 'i2ptunnel.numConnectionBuilders' (if set to 0, it will
|
||||
not throttle the number of concurrent builders, but will launch a thread
|
||||
per socket during establishment). In addition, sockets accepted but
|
||||
not yet allocated to one of the connection builders will be destroyed
|
||||
after 30 seconds, configurable via 'i2ptunnel.maxWaitTime' (if set to
|
||||
0, it will wait indefinitely).
|
||||
|
||||
2004-12-29 jrandom
|
||||
* Imported Ragnarok's addressbook source (2.0.2) which is built but not
|
||||
deployed in the i2pinstall.jar/i2pupdate.zip (yet).
|
||||
* Don't treat connection inactivity closure as a connection error.
|
||||
|
||||
2004-12-29 jrandom
|
||||
* Add in a new keepalive event on each TCP connection, proactively sending
|
||||
a (tiny) time message every minute or two, as well as killing the
|
||||
connection if no message has been fully sent within 5 minutes or so.
|
||||
This should help deal with hung connections from IP address changes.
|
||||
|
||||
2004-12-28 jrandom
|
||||
* Cleaned up the resending and choking algorithm in the streaming lib.
|
||||
* Removed the read timeout override for I2PTunnel's httpclient, allowing
|
||||
it to use the default for the streaming lib.
|
||||
* Revised ack triggers in the streaming lib.
|
||||
* Logging.
|
||||
|
||||
* 2004-12-21 0.4.2.5 released
|
||||
|
||||
2004-12-21 jrandom
|
||||
* Track a new stat for expired client leases (client.leaseSetExpired).
|
||||
|
||||
2004-12-21 jrandom
|
||||
* Cleaned up the postinstall/startup scripts a bit more to handle winME,
|
||||
and added windows info to the headless docs. (thanks ardvark!)
|
||||
* Fixed a harmless (yet NPE inspiring) race during the final shutdown of
|
||||
a stream (thanks frosk!)
|
||||
* Add a pair of new stats for monitoring tunnel participation -
|
||||
tunnel.participatingBytesProcessed (total # bytes transferred) and
|
||||
tunnel.participatingBytesProcessedActive (total # bytes transferred for
|
||||
tunnels whose byte count exceed the 10m average). This should help
|
||||
further monitor congestion issues.
|
||||
* Made the NamingService factory property public (thanks susi!)
|
||||
|
||||
2004-12-20 jrandom
|
||||
* No longer do a blocking DNS lookup within the jobqueue (thanks mule!)
|
||||
* Set a 60s dns cache TTL, instead of 0s. Most users who used to use
|
||||
dyndns/etc now just use IP autodetection, so the old "we need ttl=0"
|
||||
reasoning is gone.
|
||||
|
||||
2004-12-19 jrandom
|
||||
* Fix for a race on startup wrt the new stats (thanks susi!)
|
||||
|
||||
2004-12-19 jrandom
|
||||
* Added three new stats - router.activePeers, router.fastPeers, and
|
||||
router.highCapacityPeers, updated every minute
|
||||
|
||||
2004-12-19 jrandom
|
||||
* Added a new i2ptunnel type: 'httpserver', allowing you to specify what
|
||||
hostname should be sent to the webserver. By default, new installs will
|
||||
have an httpserver pointing at their jetty instance with the spoofed
|
||||
name 'mysite.i2p' (editable on the /i2ptunnel/edit.jsp page).
|
||||
|
||||
2004-12-19 scintilla
|
||||
* Convert native jcpuid code from C++ to C. This should alleviate build
|
||||
problems experienced by some users.
|
||||
|
||||
* 2004-12-18 0.4.2.4 released
|
||||
|
||||
2004-12-16 jrandom
|
||||
* Catch another oddball case for a reset connection in the streaming lib.
|
||||
* Add a dumpprofile.jsp page, called with ?peer=base64OfPeerHash, which
|
||||
dumps the current state of that peer's profile. Instead of the full
|
||||
base64, you can pass in however many characters you have and it will
|
||||
return the first match found.
|
||||
|
||||
2004-12-16 jrandom
|
||||
* Remove the randomized factor in the tunnel rejection by bandwidth -
|
||||
we now accept the request if we've allocated less than our limit
|
||||
and reject it if we've allocated more.
|
||||
* Stick to the standard capacity scale on tunnel rejection, even for
|
||||
the 10m period.
|
||||
* Build the time message at the very last possible moment
|
||||
|
||||
2004-12-15 jrandom
|
||||
* Handle hard disconnects more gracefully within the streaming lib, and
|
||||
log unmonitored events more aggressively.
|
||||
* If we drop a peer after connection due to clock skew, log it to the
|
||||
/logs.jsp#connectionlogs with relevent info. In addition, toss it in
|
||||
the stat 'tcp.disconnectAfterSkew'.
|
||||
* Fixed the formatting in the skew display
|
||||
* Added an ERROR message that is fired once after we run out of
|
||||
routerInfo files (thanks susi!)
|
||||
* Set the connect timeout equal to the streaming lib's disconnect timeout
|
||||
if not already specified (the I2PTunnel httpclient already enforces a
|
||||
60s connect timeout)
|
||||
* Fix for another connection startup problem in the streaming lib.
|
||||
* Fix for a stupid error in the probabalistic drop (rand <= P, not > P)
|
||||
* Adjust the capacity calculations so that tunnel failures alone in the
|
||||
last 10m will not trigger a 0 capacity rank.
|
||||
|
||||
2004-12-14 jrandom
|
||||
* Periodically send a message along all I2NP connections with the router's
|
||||
current time, allowing the receiving peer to determine that the clock
|
||||
has skewed too much, and hence, disconnect. For backwards compatability
|
||||
reasons, this is being kludged into a DeliveryStatusMessage (ewww). The
|
||||
next time we have a backwards compatability break, we can put in a proper
|
||||
message setup for it.
|
||||
|
||||
2004-12-14 jrandom
|
||||
* Reenable the probabalistic drop on the TCP queues to deal with good old
|
||||
fashioned bandwidth limiting. However, by default the probability is
|
||||
rigged to reserve 0% of the queue free - meaning we just aggressively
|
||||
fail messages in the queue if we're transferring too slowly. That
|
||||
reservation factor can be increased with 'tcp.queueFreeFactor=0.25'
|
||||
(or whatever) and the drop code can be disabled with the parameter
|
||||
'tcp.dropProbabalistically=false'.
|
||||
* Still penalize a peer on tunnel failure, but don't immediately drop
|
||||
their capacity to 0.
|
||||
* More aggressively ACK duplicates
|
||||
* Randomize the timestamper period
|
||||
* Display the clock skew on the connection logs when a peer sends it.
|
||||
* Allow the timestamper to fix skews of up to 10 minutes
|
||||
* Logging
|
||||
|
||||
2004-12-13 jrandom
|
||||
* Added some error checking on the new client send job (thanks duck!)
|
||||
* Implemented tunnel rejection based on bandwidth usage (rejecting tunnels
|
||||
proportional to the bytes allocated in existing tunnels vs the bytes
|
||||
allowed through the bandwidth limiter).
|
||||
* Enable a new configuration parameter for triggering a tunnel rebuild
|
||||
(tunnel.maxTunnelFailures), where that is the max allowed test failures
|
||||
before killing the tunnel (default 0).
|
||||
* Gather more data that we rank capacity by (now we monitor and balance the
|
||||
data from 10m/30m/60m/1d instead of just 10m/60m/1d).
|
||||
* Fix a truncation/type conversion problem on the long term capacity
|
||||
values (we were ignoring the daily stats outright)
|
||||
|
||||
2004-12-11 jrandom
|
||||
* Fix the missing HTTP timeout, which was caused by the deferred syn used
|
||||
by default. This, in turn, meant the I2PSocket creation doesn't fail
|
||||
on .connect, but is unable to transfer any data in any direction. We now
|
||||
detect that condition for the I2PTunnelHTTPClient and throw up the right
|
||||
error page.
|
||||
* Logging
|
||||
|
||||
2004-12-11 jrandom
|
||||
* Use a simpler and less memory intensive job for processing outbound
|
||||
client messages when the session is in mode=bestEffort. We can
|
||||
immediately discard the data as soon as its sent the first time,
|
||||
rather than wait for an ack, since we will never internally resend.
|
||||
* Reduce some synchronization to avoid a rare deadlock
|
||||
* Replaced 'localhost' with 127.0.0.1 in the i2ptunnel config, and special
|
||||
case it within the tunnel controller.
|
||||
* Script cleanup for building jbigi/jcpuid
|
||||
* Logging
|
||||
|
||||
* 2004-12-08 0.4.2.3 released
|
||||
|
||||
|
35
hosts.txt
35
hosts.txt
@ -1,6 +1,21 @@
|
||||
; TC's hosts.txt guaranteed freshness
|
||||
; $Id: hosts.txt,v 1.88 2004/12/06 17:13:41 jrandom Exp $
|
||||
; $Id: hosts.txt,v 1.104 2005/01/05 18:16:35 jrandom Exp $
|
||||
; changelog:
|
||||
; (1.126) added j.i2p
|
||||
; (1.125) added bl.i2p
|
||||
; (1.124) removed jap.eco.i2p and bt1.eco.i2p (obsolete)
|
||||
; (1.123) added chat.i2p
|
||||
; (1.122) added phonebooth.i2p
|
||||
; (1.121) added up.i2p
|
||||
; (1.120) added dm.i2p
|
||||
; (1.119) added piespy.i2p
|
||||
; (1.118) added sciencebooks.i2p
|
||||
; (1.117) added forum.fr.i2p, fedo.i2p, and pastebin.i2p
|
||||
; (1.116) added frosk.i2p
|
||||
; (1.115) added theland.i2p
|
||||
; (1.114) added dox.i2p
|
||||
; (1.113) added amiga.i2p
|
||||
; (1.112) added frooze.i2p
|
||||
; (1.111) aliased gott.i2p as jrandom.i2p (ed. note: no, i am not gott)
|
||||
; (1.110) added sonax.i2p
|
||||
; (1.109) added 1.fcp.freenet.i2p and copied fcp.i2p to 2.fcp.freenet.i2p
|
||||
@ -128,7 +143,6 @@ nntp.duck.i2p=5SIb-1I6SVwFlW~XlYTNJU1NCpbyHJ3co8KdrpDxBosnHtdsZzq2qzYzGDfvkKK3WR
|
||||
pgp.duck.i2p=kRm2KRa2EiWO~XQFYxSg6UM9lXYHZ93IB80j3ShFhJOOZ4AN05BrTGjMeT9nhn5n1LMEUhy9HJuN-Lhkd89Mbhufk7di6M40wqySns2g0T7XUCFUgQ8kl4~BoY8M9U0pHpM3RJcCw8WJEFGEk~fX8tgC4XQB43UIXfrdKTlNfKhxZODE4UdvlFfFdOYMgH53x8UgMZUprn~URTRNg1uwcaXr2luMwts6HnDt1bDd8elitsWViOJiw42yAFMqBnf-7mhiTCsoYg-nsOiq0Jirt58cBjAGUL5ujZo~vfXkLslDKjHOP32Y7HIi5ANEsbbRr~8QrVUojnVoJwFXs8BQBTevRpGLkCSLnKa31jNSt3msOnEP-n729Jabwj8o3pdRk9e-3~~y9gfj4bcmpSH9sOJoqmipXDiqOvv0tL9twERqse3tAwjE1NgXTvl5e2Zc~F0xJ-L2aG~6BX175ihjjEiYGWRYaoEisHMZdMtivsAK92dKl1JVEkuDF3W8KjL8AAAA
|
||||
scp.duck.i2p=AE2Ff7x-tJMI901UKEEXkcwb9~5KZKs--VBXEoZnnpC~mlgnqGsZr8MBvRvs6xAzP7xXLeL~cHQ~gvPFT60obEBitwH~JcKMahhJDGb0p23Z8B81QajXijypDpVfMDfFbMiqQGctXhnBidNKWe7HQEcJGZer-SCu3m8CiftcZ0t0g5Y67B2AtqLhza5xfepq6FQ-Tl6fpgS-UDcGUAqMpLUYfrBFB11oM09TRVsfNp~NIAvMdrvXiX8dTDUHJM4FRdpV2OsJiyDdgf5-W6s6ssxohviT5MdijUSYQw5sWj8~9Xkv~~aa11Dvty0E7K9IhXAeJlwBe3VuDizsAJGnFIU7PwZXV7-9-28Zgldarg1P-rh2QDvCqF1vjdyZe99BBcTiCqvE3zx2N-9eT~FeOURrgE~2rFBKVBZfuAASSORqiXIeAANKklCW2pGQrM3DkX8ybi93Weg~eBjwGQugO1FRZ-ISq7npRWruMiC7f~fWLqkmRUy9HahPNp8E41s9AAAA
|
||||
squid.i2p=8fiWZbRjOEzrj5n4jSqjN9UN54wTrsgEjqn7GRUQpLx1svf8lwckXPV5buP2VEYGo~83ftkIcDKyMLXkxSr8jqbb4yAEgPe2~w7OT~8LNvmVPz0xZhIO6fiw0WU4xD9x5PG1spYjWPnLFv7pynEvBpWFXaUlCacjWL2KkfViiGPXvveQqQIZs7VkxVD2HK-oT4yIjdqHpc7Y8nEV9xwds3-LX6to5p70jFe~kZJA2fjWHsSCm92TtPvoR3aTlL1VS3JUKpcH6BL5irsh-SKODEtDRCErPQI~j2SHzhcD6dMUsI7bm3AxivjFpSQHqyXLmLVdxECYsMET~nIHmuv8NYTHQQ0jM0XTQzwnQwEHjHRBd1~spR9uS2~LSnX4Pw~X1WTknJpPK1f4Cu1O44X4RYcLRCsxpEzytUBXA4BQizrbYgOJVGQa9-PNGxJeZsnNUZ3PxUi23Oh-c4jUaB0ZjyKTWJSpzj1GI6vc-gW-0ixGJ358TCSbKgqdBv~g~f7yAAAA
|
||||
jap.eco.i2p=R5rLvDIz8JAHgR7tf6iC8z7-ugctL23ipkNr~3SNCTxzdggCndToz~kmVACG5mGaoNf--KZsIKM2pbgSci45cJ1xAipx8CAaC3bbP3paDAY01hnxBPXcMN-Q1bisJPQHjkCphLRZqIh7gb1oXxiTtXU7erqkdAPXg6kvscFiblv5JxRg4B3Y5HHJ91UEb~Bc2oIsESQX3pCK~zmhWlReYROnoenaa9DDP1b6Na3SOAhgECR3PjoneMco1qDqlndb3C5wJ7X9YDGcZaIE~N6c0erNzWX-mGkEGARVLjyhtsOHMC72CROehAB3tsjwneKFojUpGQ16xcHGX4hEKFLNPztAbvnT7fg01dfcQb01YzIuBxbbjsP8nnoLJbEpjOaRrdIfk0cXm3aE6AQt7LoKYA-bW2sxgBxEAKsjcj4rQ57SDAi97FjnvzH2Xt363eHt0Jf0CypyEO9ajQYChEeMj6ISyVGe2a7wNJutq7aAx0ilbYB3Tiq0ExrbEsmXVvsOAAAA
|
||||
fillament.i2p=Pa50z7pU~ni5nWwUdaDZ5CJxG0fYjoarm9wlxnkgX~wHMX9RPgQAXz~r0Rr1Nadt2OA~dr9RMHswrMok0hutK3JZuFD707D7FjmWW2w979Ee9I3zxKyx9W5A2eE49PPT131NLa3uINXLXOYVA5frfDOmM75Dmvm533r8e2kloemJyj22HpvRiSXiQYgqYJGDMH3Hlnwk884eRkQu7P8DJL~hcuKpyY0FzLZtfxTNsdSavGjl7rKPMzJeP02-9TS5TkdHokZrstVM5Cn9ay1c8DQrMds7SPXJy13Ut34QRjb65JxRV0mrnY3teXewW6QFvFMXJCsf5C3i46t-9Fufy5D1H8cSd2Tx~Xl71MC5-1AJCcIS01Od23E9tFY3dU7IOSRhKC~FiAslyk3x-BnBSpKxbgl1w~LArBm5plNiCiUemJU88xYdn1UyukLer~yNrEHAWspckCRkXFwmUtPkaGNTvfwBSYns-skNHSd7MAUUoS-ewStBdmtnDgRkwSG9AAAA
|
||||
eco.i2p=KhRG6BGxVPh-BUDDfIgy0570cppTdighytcaGVR0HzQo46tgRMBp9Shlpax5FQX4nLHn6qHQbdFFpFbAwe7CiDhURCVF9-CxYmPurGadxlMPHMjz9O3jHX0CQiv2iULsk4XPrYXF3PqBc4t1J6vVyBVO7uTUhDi0gF6sN1Ro-1GLcWcsoR8Kx-hb~Z4WqGD0QAROOBPHnSRSb236qVBkhFvSkfigfBq3jFgEsttadYJA9ZLSUj1XrFFRBjz~xkRra8kJQSZl5dbfg-eZMlL49h61U6Uta5n1~tL6sarmnl9CaTl2Qo27SKB1OmMLeZEteA5G0-~LiOjN0nxaKpwrCjKIOyvwbQy2QqE-GEb9m8SN8nC2bwYK9fH15pTMHY8GvPYGcUukbF6RhefwzkEJLZ~PaAECrZYuLsn9KE5C35uRnlWJiuJlJ25hG7da5tFMyDB95efzq5IMxPeI0pMigRfuVfRSaGDpNos6JxjfEIX8umk3jIJUPhz1d8gP4QgrAAAA
|
||||
aum.i2p=09hSo56PTtkFLUEt1iUTO7zYTnO-B~ogsIsyyPWif6q1Iz4wz4JoBflAWZtedPmwGmH0nly4HYUS0gAADoUmBUnXwemmO6dxT-hPQkfEW-A7b3uEvYQWIN~kyFyg0Pa~FN6TaD9kGFttBN-GE4wxiHhXmWdzWNDVb0q5PVGnxMm6Jleik8xkd2Lgeexze8rIv8LCocAWx074USVkbCVQwoFi2P7EnjLq8odSz1cJAntbuCFeUZcjbslE3qmlcTFMCNCZXWKVzn7d5m4oszCQ83NidgekwxJ-S~iS6mBwIS0XKI--4iXiKXzzCFf0KtYfEWpvKCuqNJOcU8vQWAA2-i7~K28aLPzccDQn7acXWLKRTXF3tf0i6e-lSx-X6WTSWK-fuNitjAtKu~jqO10d~bCk7y~UPL-XwdH1XSTbk-Phhk7UoBTDiHY6zQHdD~iAzXER~9JXsJ4UoIrGFVabg7frzSt82CN7Ek5Li4AMz5gg3wq9H9HUa7xM5QfGIJpXAAAA
|
||||
@ -145,7 +159,6 @@ mesh.firerabbit.i2p=BLt3pciNQcVIU-IGnTfSsrputh~b6drZpc1vH8qeA745XoE~nMCGtw8S7HGY
|
||||
chess.fillament.i2p=8xvXLwcBYu2MxqMAoG6DIahvkNAwBQs43kdZTg9SlouzC3JSQ25RHvspbrxWoGIVAlB~XCxVmBB6EolYEHoSZv2YCziOj4UVxEbVP7nHkh32-7Uc5T7xlcjk8-rsmZzdgz9NhxKVn2Uhp3xtcdVAiyG4mpXisvK-7NgHY-mXPNvj6goaH58roeDUZ5otJN7nCFmcRAUpaUk-nlQs8YfgSCCEsWKOWhsVnAaHwtqtDlpdTo1YKooACMRSa-DcV5W75Il80JEWBD79qpSAeONGAOMMPT~VEMwNNg001VG-UZbvyspZdxHaETw2yd7N9l3-mwI-sYcveTTnNXLWO8IjdgDLdUIP5qrIW6WS9XZIHRKqT2kpwEw7xsEgCA~qSNiZWeai8n6Zs0rLmdyeZeafVEEW9vr6CKcLZ5W7i1SMDqCKnzSbZdd2Hvpb9aWFf5foEjLt33n8w2KSaCUe4zmN~xuQMq2yiB-vQ9~5fgPmphlMxo3ca5MTCfbhshw5137YAAAA
|
||||
kaji.i2p=i-nivH~36AgabgzvS06oKHR~MLoy6NA0oSv8JuNJDLZB8FXEDzIiyzWISmw9XJm8I7QqZ1yFH8~xe84STCHHvMMIQQrfBmOUODLWbKZor~~KhiHdNLtfVZC5BpnXkBCJkklj~fMYSpWa0C~pVRrZl75RoGtBjDVP9P8hioTv5B6RC86g2ypBH5r093rY0wnzxSL8-ZuV3F~H48VYbqro8cRlbMmjx2oEsSHkDpQyjCMVkIYKaCijkSArqZTG~zX6op6Ng9CJwdrkjKSsbzjV6MLnE4aNv-jm2WaGGD5pR24h7e3ImDOGAr17tXRtmNX5ZEQ1udQp8qIhd8UMUumrnm962r8KJWK~9WNzcVeqDrIxaaxC7vcQmXxoPeEW2efbH0yKhVZ7OFu~I9cAapSe~aNWp9UK4URSpuJvOedt0axp3ORaaM-a5U7noW3Ao-HB83qfFEPU-6uUu16HNiPqCFMJiA0qODTOwHiyyx4HKQvbhjujh4mmknSbsuapdgR1AAAA
|
||||
human.i2p=ji02vZzrp51aAsi~NZ8hwMLbr1rzMtdPUSiWAU94H89kO-~9Oc8Vucpf2vc6NOvStXpeTOqcRz-WhF01W8gj-YLP3WFskbjCcUwz0yF8dHonBeC4A5l4CjupAaztBSMbhu4vyN9FJkqZUFN01eZbQ9UqgXgLWMp4DtbUwf78y8VrzdAfmUOrVn6Iu89B~HUfOAKnpIlQXyGsQk1fnLw3PzDo2PVi8Q3C1Ntn0ybovD1xDKPrrHliTK4or2YujTcEOhSBLK4tQGvouN-tWqcVoF9O814yNGtze~uot62ACGJj9nvEU3J7QPgOl~fgBJ5Hvom0Qu-yPAGJuAZa29LSHnvRhih~z~6lWZYHREBYXQ58IzKktk90xJWcTwlwRRhyO-Sz3A5JYR3jM97h4SsoYBVrjK9TWnvGKj~fc8wYRDzt1oFVfubLlT-17LUzNc59H-2Vhxx8yaey8J~dqdWO0YdowqekxxlZf2~IVSGuLvIZYsr7~f--mLAxCgQBCjOjAAAA
|
||||
bt1.eco.i2p=SUHjD4QvbuY5VVTGTm49U8B~DtvTS2bwO1lREMNfJtZU6rxa4tgdqaIpUjRRrbZYLxZcgxjUYx4Bq7gcjriETIRaMy7lQtMx9zFk~XLE5baTmZeXc1~xuQCCTelnYv0yUswWSCZx7ll2a-Y6d88jvTydfxcCTNAYclT~gHnA0kU2kRHD5vgEteidiXFBVer2Ps68tnARUqURDKxTRyRnWtrxwQocmP5MJG29e4dIptcecxX8bKhgqgONzPAmYzAR7F694wEOXJZQO67ro0YQEuQmDXICBI8IwkbAt8qM~BG~JF1H26iWblWWs8mGJonwl34-CpVjMSXWVk~SC~60-xr97CsWSGAeZqrD8pLJyDGFsJ0jNFV0L6Q97ryUViakVwsHaAMlxZ3Hh7KAc6TUcJEGvuygRJ2DKeKA1wyuUDFc08m40HrRVwydn0hwzs4Qfb8hXfGHQ9-yauK6Gk-HvYFUL9qcIrOfrZGgq4xLVdgVh2wB1mIOTkcBMMti4941AAAA
|
||||
gernika.i2p=1D9ee5J04ZI7pvCwQ3hXQMpeMvbNw9cz3V2pioQ9LakwRQzfMEb9CVAeiFt-wE4HyTFWKeof5rz8F5vmIqFMaH~oMJSkCyNtwPfqiRAENeNeALHbY2plMPfqCfEFR4GkTqXnalAkJGqDjo55CokUfalEVTGMvNiv6i6dmNvwS8~0X56sXIaXgLKuGIK~UNOg9hT8A~uEGQWXwTKD3EDmECsJL40iYcT1UR9rffzuyxOvDhL12HbJ8bIlUGarzEscH-jolj5ShvZAbEyw-MnVzR9LiEqy7DaniKpPtC0oXRZuz7PpcQTqzN-zgQaLq8bHTx7NHIfTuA4P~hhz-STO4SjPot7h~Gbdglc193OmGlL5QwbfjXfdOIccBDh6~jtFaa28GxHrTMoi9GafjnllLfWpvynN1y5H7Jh7Uw3E7KDtBGVsDg9-btyvyQLP3kkqPfIAn7Oj6ePHr9u-TN9ZwDbWj~QBmXXutsE~lLu7aT7kv5Jx6PFmLEeWPib82UuGAAAA
|
||||
www.aum.i2p=8x3TYbh9aq6EVo8rSuPWBVSrwD~yS1al6z0RZYvRaQFXL9hFUUJYJNL2n3oR3tBg7Zr00MjqntIuBMd20LUKasnklTp4hDlDCE0KfeftYh~bFGykMRf0yTYEWaMHYpIRBY-IJEvSVlgHe8E4AWLMv-b6VKCDZ0~0AdUrsHQ0Qb4NQ-igBPZfU6c~UU0tUVUl1Efsuz1CdAp5pw5RdPviFtPH4tMvUca2t54Rwa-6v6QCqFZ7S2awyhAa73Zb9YlcqT4hP1JHF0wR0rL-OEoJV0gG47Co4Zr903SNW6cgKDj3lW1tIpzcVMoH3BE22SMEVjYyEHgAORdoYwaj19CUg1slDGmvUCoq4dPsnCIrvV7N0LeoUkZekt2pvr~yCH47ENV3oQYpFVLcMLN82tzI0ZFz5IyBHWGr22vlDlT1C-QVhAYQKN92XubDXSEgrhWv5IHPB0h~EgZi-rDcsJG2zb6ZqjtKFHp4CnNvTUxE1cCJh0aR1MDzM~o0iSMiMqh0AAAA
|
||||
madman2003.i2p=Uilou~5268x~UI5Cjg3XYKfBCeKVEMuD0g9Ea7~j2aNXbczbYXCdRQEetYMk0919Dyyj5Bfpiz5m4~xHMUoXs1aJedteFjvwwOHcXE1IuhORhILv8kKyW4zBYrK7LUnvn~xFsnl6~xaThgwxneXLGhKz5UOitIzDUJmxCAwfcARHgx9PIUuJ5LTninB3Luyev11TIqweOfe~X0dzVYSmemBgaw2T6dxCz7qz0mN5fJe18~qnTqJibKT~7teX~hQhRQolk38P3tLeBRq3wZGTRsJ6biGEydTbE-rqKrNvIMNYvUbhhRHEMdShAiUo4gPJwbyCRRScI~DbluYuy6iXXURnrCIEv2DKtfpHSS~JccPu25HKcpNBfCLzQye7L8hWgjAyaQoTi3idW2JJNEIBBBS4yfGk7wq9swlzBK2aanI-JzFcDrVDXd~fpN0JYN-phF3YJnKspJe2i83BW8J9b-y2JJVbTWBwU2mIEE~gXKggsCvqESfuKK5RhgFaS4dTAAAA
|
||||
@ -248,4 +261,20 @@ asciiwhite.i2p=kJJmuP~sE0SrQFX-nyV2cEFDffnqv3D4-vBNJX6wqEq7vbTjQzZVn3jtt5kYlv4Fp
|
||||
2.fcp.freenet.i2p=yY8ahms44MDj2cdI6o7Fc2ozz-jM4ACEp8t4YJiRsKgEYx6XNB7QrNK8jZQoKIQ8G6GMmKMYsgRGmbwWFt60au5HFNv56NWsB4Nd80G9iSRLiEHOHhaUSFnR~ZnSdzL-nbMqA8-vzteaqZ8HNnC2NNjJXsMyULFdkBsIKYS0jsS5hl7ddf0v69X7sCSp1CCy5H28rmZSEtKA4aYXGcBHJ8bXFomV5ovy~Mh2xZzWdeVmjdmJACG~ekDBcMkm7G5mlYoTN2aEBghxiHv~plGlHFqERB3lgISlomYIwYwPqxcmpxwbg9OSEo53t45P9~dat8XMvabzXiUdm0UgVnFsNBAIyNgyTEktw38~5v2XHhqpn8RSoMUeNOqp~k740v9fYH7xxHYCrfN7y3rKg~~GlWekbNmX5g0UvKjV-QWHIvrdOVZmII9qNTTwm3Uf2Tyi2aFll7TF9CvKUJCI0Mt1BpQEmPQdY~FgFXNd0p-~DvlRrwaI4jorbn4eUUEJqgzFAAAA
|
||||
sonax.i2p=s~FYxHeR1n0aN0kuilNtJU9IbXRX~jWI6vGz4TUf6oZyjZBYYmY9OpgDa3sr-8AERqsuG6EKA~UBn4KiQdkI-LU16DwEHvbgfZXAdwY73G6SkHOgwVAnT2SIOuh90v1QevYtxiGpA7IVm6icoz5V4I6V29hPlUNJxBFF~Qn2cY6zj0d6wBk6ulYc0vbyOwu8Lfc8T8bTnBLut3SZWgKHYLbxabcXJtcQpoN7k815N1MEUi0APD45o5TwPhvpHLt8fF10aNcMScpL01RR~tKHgC6jPX~HKM~Ld4l0z0lvLeq9197Qp6zHRvRD3YUQxyvgHi2ANkYprvJof1Abgs8TD1JWkZUAzlfqFPyjO~f3pJSDd5eM7E0Fk69ym1Y4Rltih~Y2N1IVtJPfo9Tx9a3RQWOJZ0MN12xfiZjyF92gDRgfKW5RDpwCoTVmP79u~WUr8lQ-qQbKhsMQQqMn8Uy8DeXvKLRpHaECtevjohL4lWBBR0ezXlBLCXKqaVXKcMTjAAAA
|
||||
jrandom.i2p=BxciUZkGQwi6Sj1Ucg5GGagDdwujlj8ClePa4~3d-1nnRJsBhTNJtvs-UuQYY77gPGNlpNk9dt02mxeS7f~pEC4E1KxJH9mhnf0OlIGB4hOOhDlXokAaKE2u1E-vVCDJlZCq32r~Ne53w-L2m5h5FAq5Bx7NXrTzWAEPgAlC8A2wASJWIF-EKOX5kfkkYoF6sKZqam5CxAAAMAUwmnbD--7wo8d2mz0C-c~oE7hzuHsg2J8yME5Zd~-FOZUxkoNCBJdfrVlVRn9~6685zqpaotL-SIqFK6~28ZCNMNCtnZcZZLeG2gZxq2pe8HtgzDrMMSx45vs3JLUp8Bap6~D9oFX0o7k2DQm-5R9D5NWhsJgol5Ny~8ABTXBpW~WQQOuKxE5xJauXM5rQia2yyXgldfOyGjxYnkrTGu6LaxlLmhyAva3ZbOO0mtLvrKLZebLKCYUxP7~TtNmSWEAzPKFYbjdZ~NjE0q4TanLFBaWotefPmy3IuAc8Mr7PbCvi4GmdAAAA
|
||||
frooze.i2p=kyjnpna6HHapU~tLQXRCHrX6Dpb2YCMEtFTloKF298480axGCZFY-WtpjW2Mo1ci5FlE7dkmEQ1PU0GfHZ6s0O~Hert5AcZpKh5nxRIMtS7uc~oErYCiX0yQp2P9D47B9VU5D2UmjYKWovqbrbKEAS1F0NDxXh65Ppnv-oBHVksLp7IrKLU3IOesdYtGcc9cSYJB6~mdbtqXxgHNNLTkyouSJKgSlJoe-tk1T3eYEsOJ6vzpeS6pNa6Px2dknejT~aVeNKLhjsXy706g22Mbl1-zCL87OE1bL2qWQbQN43qpLwCSv0LbRovO3r6iXRWWWrKhYj5nti~EQEWapJ2OL26~ElrpD9V9gbUVLZoxeYuu~-GKKwg6CvZYKtdf6YBYTZ0Ef4PUcv9faSqp7S4emcGOLQnSW9LezXhGhn0OPaAO~85nhdY~OfXDZ2Dg2tYYF-D70SzKycfHFLgKBQ9fkCxpOP0MHhhgGmKHabPvDHyQRFppcKLN6AYmUH1Lke-jAAAA
|
||||
amiga.i2p=-lqgoqA5KcJCuGilYz~cOBK3X7MI5VFBK7UwpSEwqbiQgSe340wABmE8V-tX7xH7rjc4Z2rGxqas5NKuOL3pCJozeKF4Z61d05nCemSXbLrqZvJUkGuZJfUz9lzeZ5OGO8L32o-I6DIGIUgFk6zRpiRW0lqO4lzV~fbiHacDhUXPLBM5gYjvZ319MLoVPp-YWG-STJFhjmAnzvj~8h-jJXHowM72YsWPo-B~oD1YtcAcXkpxVJOaLlqQq2yfjJv7YgIde3tqQ6drH7xmHdG~nF~dsu2dacqqt~HjDiUnPd12vJEhR18DDkVJVhDUbnUCcI83bt5RDdsMK8mSKRTie3h1sUam~1fLr8~gnEGTFp-yivGV1JkeleElR-rTJC7PsrlPCWzW2N4l1IMzRGOedJNAyqeXbkfjHtDBd58iZPugS5BZZ~u5MGqXgfu9TYFnLnTIGuMc3a2wSpe3miMvf2PKFnM~1nzStN0zEjFwhn4tZhOWA1TfTH5HMutqrxGxAAAA
|
||||
dox.i2p=ygf2mdGRnU12BNXXF-26OFhfPpAMigwasf93JEHVYEICVmz6Fk2BBKiGBWL4YNFUtcFo7y1iG3ms8BdmTBBkgCgEasuFZNzphEMgcNYi0eYAg1QLNgmbr5HEDpBNuXIV7fOpxYn6iij76oA09e5opPNdOLUk80kWrgrlJTel61n90uzEzz46fWIbFEw5K8xR-8aqLgC2sJDzGj8vEBY2NDKmCtjRK7aNBUayh~8vFfJyo5oN0gw8b5cOCrfSoxzqdpzI5CJ7070GjznWbWh~pueKWF4CpHn92ll3jvyd86KvUwdQh1qr1B-8fk6Oh~M9l5HRzqdUsSzp8SiG7WvLQoXrNdVX2Sjw22xOZDr7kOfEHZWYUdmrcYyqNLuIT2vgMFXdvCZnLhhGSaihkzuFqZv-34oGSqGDVlCanAPC4v6492IRRK7RiYnUo-JPhuWgpg9CdFTZtULa~drZbWNCZvAekn44D42wDx5V5vAJxuRoyir1s4gAnXHL7n9r7xdiAAAA
|
||||
theland.i2p=Lf52G95rF1kjlRu29NMpPQ46Ivd2Nz-sdIhvx5D4qU3Y3MPs50GNTjg81yJmUgbg95Q90Eyf~SHsgbzdlnjr-qQEZOXIgLQErq2UhUFGwUX6muqkCYYDIAZ-f7EI-yJiU-3~KDwWu77z-8XDjaJIf78tNV6kN3MvRwUSbBRLD7pAa3HtdhNnNyO-PPBJJVMxorQ-~AgEhmdgLj39WfgetETiRm4ew0NsN6IOHWn~~vh26FJ4awhLpJ0fCBD-fPve~jTbCovjsszKMBumTslvQS~p-dfjfQSTMRZAE3fs262NjdEwoVZP-k~~0HmzOmV9M-Oe8yez6ORDKm8sDyeDoQ~e21b77SiE2tNrgHeB3siITcZoTNyICX~ZSG~Qnb4k4kXdllVkqmXaf7k0pYE1yTKXM3sKphP~VJFMBgdSzZPprhp6RGjWibKy~94Ns8IyCA6c08810yodEdoeU53acQPp~wk0FP1HgcF3tVggk-wHg65cD09ncjOmly5Gc7uZAAAA
|
||||
frosk.i2p=Dez-OBQLhsI7Wq~O8YmYYJjl~IPvqUCZbLIFVMnyyqOCG9TEO~kGUVpWVD5nF0B5fE8kEWq8CuGKeYbVfqt0Vuz6Uvvo4gCdGnthohthbolIOZ9ZCci9MTNE6t29mJ2thHqc9Nw5TvYJGcvCVNi569~o5PDtYhZjVsOtrv8r3f9fUcUV-wKokD-y0fdF3Dzw4I7tJ6pfohj4avzDwwndAHXnNjB9mDgQsfjPF2KtvWLzXyAsCmfCoanV65tkc09ZX4x5QkekdVgTbD0BcHDP-o71MVufRsm4~ojRkbdSLw-VlbEcYFyeHTPBTpM68gTjueJlRwmvpXyP46w~Xnl9IEDfWu92CV9xjRt8qDxiyQ9jXR6dbWLptTF2UCznkBWvWph7P2wliTiawt~1bWGw2YvkfB6EaENYvDNY2cGn0GxpPhL4K19slEwJqX9f~EzLKYx4xaB5I8~W9syJAJSX8PsJbUlk6OhbbRoYOndd6OCRqHRUyh1w7dM~LvbY0W8hAAAA
|
||||
forum.fr.i2p=glvyM8eKBTYnlwEl3hVWDRBvfIfe-utzjGAlvDM-rwB4tb4CaM49Aeb5k0lxm7IaFjkp0u9t~1a2KIxwrU~Mj-JTJPKf0TrElVfOT6P0sVFOD3U1wllFwne5e7~0d8MeuylMrBe3dlz5KSPQ69stpDTbJgxcynoKiPMWcbmq~yQjnIvtDIpAPaB1JVYNo8mDhqMqg8GLrk9ttTHO90dYUpdI5lgxB6vR1nuPPwyFczOjVn8GvmT1DXGNoc6yAW0gCLuk7Hq-jNcNtXkNRC5TTdNiBLdLpsNMD3kKWDM04fODM5N~qp-nww-lEqfuZ3f1RT3gpjDwpMMAtKbd~VsRaCEINL5iBaAlZupW6fYTiuGUUdOZ739JK9GpQDsPXBOy~VSoM~cLXJCih12v-GDODDN0i8sSkqZ7CXDGg~jkg0ab2Y-Y2JnpYFLsgYziPtNRij1~rMCcC~-huiSfQio15gxczb~RRS-lpBQgyfaZ5JgIR2uVjWWe-uwKD-WquuIPAAAA
|
||||
fedo.i2p=pklxM9~hpluoCoiPgzMBpTHXMpwEdCMMNEatVc4gnQDOXsKkW5FbUMQ9y0vj6EUs2vUZLkwp3FP3-PuJEHMCLCTBdkVlE5JErwhZU6V3S5lOZ2jTrSuYAhcph~ML~ntjDzc-IZWgAax98ChzCpwxhEk4eP4B9vEhMG7njoxbJLfwAYu1qgq0A-Whm08rAhAcDwl8a-21Kqj8~iGq0XytMqyJU5XArq4aArWEwZFFLKJD9uR9bKeMaHIkJLNAyKIvfpgwRjYIMlIf6ekN3cpBU3B6c8NgDNPNqNBI6DQ1XGfKOEqX6d31mTOSzyjs-zgIJkt5zg-MH1pnMSVAI-vQbiAaPJc3Rjeqarv5Z89ZX~dghQFNQX1Wd7ezJy0WltaY6V7lLxd6QPTosDCDawT7ZoNhmLUNtFXI7oGS4Cm4ZTZAjZV-ZFMUXWUKFTZGyh4s2WbYqJq1Sackk8JmCvZ7JHazJ9ghnrQQ2bpzWeeGgtCPIcTnaUMPOZdsU96ehme7AAAA
|
||||
pastebin.i2p=mUDz9K6KmWe2zE4wj~YjqwD5f8pCjEbze-DuafzjtXOaj9SnRBrLNqgOTt063y9foZett484g9PFm~3ibqFZfUk3LsJi6YhZje-V~RZndBElRJ1cX~MBOG5wdHA2BYpBt7jX-N5J9ww7POtcPFDjyYlJLhRrY5FuRfxdsWJ4BOUHOwvbTq7IWvqHReayte6vKavpyvNcAotcFHAhOGpR6Ua3RncS~b6NA5k2CQIeS4mR6~iNCh0sx1gj4cWWWqXSa6pN19io82L2fcNEcxE-UETj4HkG5fTdBlB4I2cNAm1KnxQw9ntRf19p5EzYkOFYST5ra~RWmy0bWzJMBFlK9QcogVi97gmszuNPyfIpJIE1ssZ6BNHKMkPJ-fjxtSAseTBV-Fa51TIBwNyXC6VxMsixKIBX4Cg64oipoGpm~-7SdRgabHbB0vWwiV6RYEQ88oNcV2ycDqjyfQymT0T5S9b0aVd2Ey1ZWly-PR44~uC3mCctcMqfZVfNFs3NoY3ZAAAA
|
||||
sciencebooks.i2p=Rrqf6EmeuqckygNrim-ZcZ97uEIL9Ykkr8cj1RSGbeih33~UUpjAp0x2fxrpbibXwuGufWMNoaMkZH8FxOxZr1hvBeV4Mvjrn05kKWyPpBB1x7rAcVCCoD~bl6CUU2k4NtFrO9~BCCZdoIvMxXYWRy4nj18RVaIlOS3qE6~F04pU7Fy0sj7xtd02wgeyKkJ6pXYGfETMr6phVusHSSQfCumDBpnqEt7EcXTIsPz58y9Zim4u1wp6xj~OvGWFIP8fKIxpH~zf6o0qYytXrf6SJbIEvwITKGIXZTM5KxQ0LbEydSgPn4q5PttQlKg~7hxuGG5RYqP2IMg-f9z4-1SfYTbz~EixEq8gv0R30c60tqsyAaw9o37R3k9inkT5WylYwBKM0pzfcAVjejcoPYnFF1iithGl0AYkP~~isxCErCNwLs39NyE688C5amDIpXl7AgB~IvbwZfONbd2cwMOB2RFzTShYZlwcILB7EzgMvEt2l8GBkstP3CzNrjQ2gAL0AAAA
|
||||
piespy.i2p=jTCW8w04d~Sucb7OXFBZtq7tEOxeFBNm5T4uVZW3xNapThxk0Tie8OAIQMmz4lzDUaHoc97DN1s6k3rTprRrhbr7Idyls5I-r6jAQnrljqUveIMA-lbzXDYTgo8TqBR4~fpSL4RM5u4~sM4ZRKdwufnEjCSlYtTe8qmrofFx3cIuIKhfypsYcu-BEdrw7P~cRpmqJqBV~igulwfIxeABa8ygX~Uk~i68NELte55J70z~kijYGMAgUJVhtQxD~jZD5l3FrGkND9lVaam0lHseujNyy~ZB-ma5IKqJWKLEo19e224EVhIK4jcKYdnr7bE86mZiWg36GvWFhjJOkEvCJ3BWxaMjcDXyk-9vCY7MA403OCMGGHoKlzB2AKq1PEc1teMtoqMG~a42DTKevUbbiALtauxCe-BOxEtxpYTvvql-RVumrUh3hckh0wqAd1Gqr2uqRx4VaCF3X3duOxdoKV6Kgf~icfVIFq5HR7AhN63BGFhAeQLcudp3oA828a06AAAA
|
||||
dm.i2p=ZPCnBTv~~EZTvbq4D4Lscn4AL~VMwbmKSLEF4Aq8bzynQm~kGnkSLhrDpNaJyAvO0F9wX-7bxyu3KLmpE~0KZt-6-J2spfBqkRTOd39j1YjJ1HHXivWiI8yl4uoHXZun6UW~QdwKQPzAMBnl2W4rXpQekGxwh7HlnQT22FyI-ELtzh8FE8JJ6DxfYsDKd49QnsePM3r2dB6hWb0lPEf-DwSD8JtsRmXMmDfa16-yWt5Wa0K-xrWbKh~oQ8K1aXhHoUxsfJ~LQnklt~QGld9Cbk7q1hJKXFUSqCoLaADZEmvzuNlzE53ChK~W6IanEZ4WeNT7dMJaMtta2Ot5Ym9dXGygFp2XE5zGsOpq38XGNHHI3iKNCexIh0eTL3jwwkUQXl7UPWxrykRff8uRSgBq6s669lOzUbZfdmAbkTG6NvwU0RboQQ-OYs7APDn5ovXHI3zl0bRljZWBxNVswz34fQ~jkA5aHUcgV~sNgwjjz5um7K9V-QEVUBi68AhJbmvRAAAA
|
||||
up.i2p=D7MtDuWfl9XOkSDC~hCH7QBrnDhg2KdxFq2dyq0e2jJypmXPFSw0Hl6HOMdI3myFTtweKZObHaPzy4c3-hk9ZwBsqzT8XquTVoa~z7OAtoj0QQDdIGXaHpDvVWkj9wAilpJfypqThVho08F0cq9~yChBh2s02JoQersRQ4l9oMQaOSVS85wGudFYSeWUhRPJyb0FN4ic7KPRvFlOBnESCtQUxMa0HV28UDyc9kGUN8AmgaV2qi-LcdrxHsYwzbYKZVa6Tg3mHEqksuzH5AK~9KYHlzLz2QRDesL0jkf7WdVRlKSiqkrYQVSG7MAqHfHNhQbb9oIpqDARyAqUaPJyEVQRAphJ9qv-K54QW1T2Js5tF5XVJoZcU04d9aHyxaUKqWJwIMrRlfeKVGAPlE7pcUjQrTKe6-22-46CzKpHsG8j4kwSyvrwlpGdsbN7-q9tq3KH4b9hJiv5nsQ4lSBQ9Um6YCEmUdwffnfbbfVGPCmj9XcMSrnHfvCD2rf4-V9sAAAA
|
||||
phonebooth.i2p=akZY4njn7Vc3Xj6y14QsJXGTSJq5RK3~UvQGRwdBIZPnA2cDMiPfcitUCcL2G88NnpBt4sJZiwZWJRncv3~6q1QtQi1QZ1pICnOGErYbSLbWwSJ520BYOsxVxRptZlLNTeVGnS8MWFCXvyo1qVYncaV4DnJPIdo8D0z8EqPuHIuWPkIHkD69rIBsW7-um0c5C1xIyAVGXPA1Bu8Bkgf7-mjf1Tvu7ZvtJR5DRYx1hOhsz-oGVi~6Cjd1kASS2FfLYjfNkzIbE7uT2fBDpOSyO6i~bAijgbB628MIE1Vhp87Fq6MfwBBDc-IrWUbMip6rVy9snbk88vcC2dJonZhHcDYhr76m3uD4KVqDoAFc5HpO1cEilhdTnBataEgDUIE8IrClTGeqgy~aJDnjjDxXd0RXOQCQUL4A2rGCPL46NhbvdRC22bVF18NaggglpLuRCTUQJ7Y3wj9E-RxPpm4~yYrm2s7EPMfmo7IwS5ihvwLvMyiynFGivJrI1jADYPs2AAAA
|
||||
chat.i2p=E8iddJ-~CVMPdxN7FIgaQfFv8XywP6JLmx0pDq-dL4M1mSN1jRmrad9pyhbvLM1-o2X3ZVA4joXJetCv0-Gg39kTvIMolPM6pZ4fJgvNJTUXWaAhsbB7g0-oKD73J88GwNwJ377UgYKSOSHfTYJ1gvYu718FXR-5k48Tw~xmkJBLyJOh3GcK46nYm94ga0YpOGKQoy9Cdp3oafEAiPiJJpwb3LCBkg2Zq~snSQ~HJEg-desvHfhexwf5eRnrPzLZCiHJ8KRjaXN5sy4R7kfJkX4AKcCjNhcmis6kH2F2bH97RrnjFXnRPiWSXNWcuDSum84p1aKQSgHh1Vrbme4yJ5rPu-DxEvu-z9yo6o4XqkBKye7DwAb6dBq3sbdVDdJp7aqye22m6nZ3Cf0i6u-oasGuNGYUhWmML~p6MPjYwBiuMBL1A6VIJx-AVMHvGhm7xiAYrjR3U24YMSDo~foRJ9K7fIaEMgY0rsVcNAsc8owedNMa8FLBSk8AezHvB28-AAAA
|
||||
bl.i2p=-kRXee7rocbNNCDtgQBZ7i~z8WEg4jVvXgsLaqe2e6-DBR9st07Cqn1b6m9CXgIfL04XB-6Pw18LItova4s5U7U178PbPugOOeZPmwWmmDG11Ch92Pnpa6hHiJlQ3PTq1PLA-y1xgOyBtZ5UhQ6i8ErG5827l76hSs98lWdgoFdIoI7QR6~oPSxekLDs2XJZWPyShTAj7xOvRretKrObB06JsucZsDPXfvimyfkge71klvGnzMpLb9rTmMyg9R9r0Hc13rOGtj75HGtSEnTJFRXQlYCxr5~HeJXDMcXEftn2-nTGAzSdgOxLgXUCBGh-0Hf~nrek-gEiVl4DIO-HzJCBelpt1XSH24ou6Jp0BZIUaVCCUThHD0sdcCuCtLn8xKzz2LyMDmkb2nNfT6JIaFh6qdWZFxWyOoC9JORNWCPH~B6hAs4~Bo28NnFARJGsvz-B1JBMBzVFRuCghtU131hZ~Bk33kmKtfev8STOh~UKXqAFhR-6EEOu9PydUiknAAAA
|
||||
j.i2p=mxmGmXHJmKvXNkx9ucrGC31Uxt9XhFEtIq08ZbMm33nsWfYFrwM6x308TqC55f-wkvrA-8v33PJLuVxThK1GjELxUWO9c8pzbrgRbO2VH60coT1wOfD840w1xaMpxzRBVuavDv9WybOpwkpKkBRTHHEAwjAnUSLfWihLZCjDQd6BI2ZI0Y~dhtuPtJK-VE24CTf3XxuMYcTaiK2-Kj81D2TASWh~Z~rV0SGnpu4t4CQV-hA~CkvNW5mY7SwQqcYBrLLoxieL4rfpBn6G3TMyshws075uGDJKvQjklJpcjtTN9sEMB-Z1IKvAEHRP45-OsOFSw8iMweQ8x7~HN9F3uAdSnyE6KV8Fw9r-Utcb7gCx6RScw66pqC1jdYVNqLX07cI-GOYs96g8pnG6ixZruTkzCzD-cRqUCePkUvbF79XuZ3h6137k4WTqDAWS7iFnzXPdA5GQqbReHmozIuY3hyUZrbD2fRvXjWl6fG-43WpOTjkzRveYKjUW-RPnwIkcAAAA
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
$Id: install-headless.txt,v 1.2 2004/09/02 01:54:37 jrandom Exp $
|
||||
$Id: install-headless.txt,v 1.3 2004/09/29 14:38:15 jrandom Exp $
|
||||
Headless I2P installation instructions
|
||||
|
||||
1) tar xjf i2p.tar.bz2 (you've already done this)
|
||||
@ -10,7 +10,8 @@ If you're having trouble, swing by http://forum.i2p.net/, check the
|
||||
website at http://www.i2p.net/, or get on irc://irc.freenode.net/#i2p
|
||||
|
||||
To run I2P explicitly:
|
||||
sh i2prouter start
|
||||
(*nix): sh i2prouter start
|
||||
(win*): I2Psvc.exe -c wrapper.config
|
||||
|
||||
To stop the router (gracefully):
|
||||
lynx http://localhost:7657/configservice.jsp ("Shutdown gracefully")
|
||||
@ -26,4 +27,4 @@ Supported JVMs:
|
||||
Linux: Latest available from http://java.sun.com/ (1.3+ supported)
|
||||
FreeBSD: /usr/ports/java/linux-sun-jdk1.4
|
||||
various: http://www.kaffe.org/ using CVS HEAD as of Sept 1, 2004
|
||||
(or any subsequent releases)
|
||||
(or any subsequent releases)
|
||||
|
26
install.txt
Normal file
26
install.txt
Normal file
@ -0,0 +1,26 @@
|
||||
$Id$
|
||||
I2P source installation instructions
|
||||
|
||||
To build and install I2P from source, you must first build
|
||||
and package up the appropriate installer by running:
|
||||
|
||||
ant dist
|
||||
|
||||
This will produce a few key files:
|
||||
* i2p.tar.bz2: the headless installation
|
||||
* install.jar: the GUI installer
|
||||
* i2pupdate.zip: the update package
|
||||
|
||||
From there, you can follow the headless installation instructions
|
||||
with the headless installer, run the GUI installer, or deploy
|
||||
the update into an existing installation.
|
||||
|
||||
You will need to have ant installed from http://ant.apache.org/
|
||||
(1.5 or newer)
|
||||
|
||||
Supported JVMs:
|
||||
Windows: Latest available from http://java.sun.com/ (1.3+ supported)
|
||||
Linux: Latest available from http://java.sun.com/ (1.3+ supported)
|
||||
FreeBSD: /usr/ports/java/linux-sun-jdk1.4
|
||||
various: http://www.kaffe.org/ using CVS HEAD as of Sept 1, 2004
|
||||
(or any subsequent releases)
|
@ -4,7 +4,7 @@
|
||||
|
||||
<info>
|
||||
<appname>i2p</appname>
|
||||
<appversion>0.4.2.3</appversion>
|
||||
<appversion>0.4.2.6</appversion>
|
||||
<authors>
|
||||
<author name="I2P" email="support@i2p.net"/>
|
||||
</authors>
|
||||
|
@ -1,39 +1,14 @@
|
||||
@echo off
|
||||
setlocal
|
||||
set INSTALL_PATH="%1"
|
||||
|
||||
rem
|
||||
rem Java Service Wrapper general startup script
|
||||
rem
|
||||
|
||||
rem
|
||||
rem Resolve the real path of the Wrapper.exe
|
||||
rem For non NT systems, the _REALPATH and _WRAPPER_CONF values
|
||||
rem can be hard-coded below and the following test removed.
|
||||
rem
|
||||
if "%OS%"=="Windows_NT" goto nt
|
||||
echo This script only works with NT-based versions of Windows.
|
||||
goto :eof
|
||||
set _WRAPPER_EXE=%INSTALL_PATH%I2Psvc.exe
|
||||
set _WRAPPER_CONF="%INSTALL_PATH%wrapper.config"
|
||||
|
||||
:nt
|
||||
rem
|
||||
rem Find the application home.
|
||||
rem
|
||||
rem %~dp0 is location of current script under NT
|
||||
set _REALPATH=%~dp0
|
||||
set _WRAPPER_EXE=%_REALPATH%I2Psvc.exe
|
||||
|
||||
rem
|
||||
rem Find the wrapper.conf
|
||||
rem
|
||||
:conf
|
||||
set _WRAPPER_CONF="%~f1"
|
||||
if not %_WRAPPER_CONF%=="" goto startup
|
||||
set _WRAPPER_CONF="%_REALPATH%wrapper.config"
|
||||
|
||||
rem
|
||||
rem Start the Wrapper
|
||||
rem
|
||||
:startup
|
||||
"%_WRAPPER_EXE%" -c %_WRAPPER_CONF%
|
||||
if not errorlevel 1 goto :eof
|
||||
pause
|
||||
|
@ -5,7 +5,7 @@ tunnel.0.type=httpclient
|
||||
tunnel.0.interface=127.0.0.1
|
||||
tunnel.0.listenPort=4444
|
||||
tunnel.0.proxyList=squid.i2p,www1.squid.i2p
|
||||
tunnel.0.i2cpHost=localhost
|
||||
tunnel.0.i2cpHost=127.0.0.1
|
||||
tunnel.0.i2cpPort=7654
|
||||
tunnel.0.option.tunnels.depthInbound=2
|
||||
tunnel.0.option.tunnels.numInbound=2
|
||||
@ -19,7 +19,7 @@ tunnel.1.type=client
|
||||
tunnel.1.interface=127.0.0.1
|
||||
tunnel.1.listenPort=6668
|
||||
tunnel.1.targetDestination=irc.duck.i2p,irc.baffled.i2p
|
||||
tunnel.1.i2cpHost=localhost
|
||||
tunnel.1.i2cpHost=127.0.0.1
|
||||
tunnel.1.i2cpPort=7654
|
||||
tunnel.1.option.tunnels.depthInbound=2
|
||||
tunnel.1.option.tunnels.numInbound=2
|
||||
@ -33,7 +33,7 @@ tunnel.2.type=client
|
||||
tunnel.2.interface=127.0.0.1
|
||||
tunnel.2.listenPort=2401
|
||||
tunnel.2.targetDestination=cvs.i2p
|
||||
tunnel.2.i2cpHost=localhost
|
||||
tunnel.2.i2cpHost=127.0.0.1
|
||||
tunnel.2.i2cpPort=7654
|
||||
tunnel.2.option.tunnels.depthInbound=2
|
||||
tunnel.2.option.tunnels.numInbound=2
|
||||
@ -42,11 +42,12 @@ tunnel.2.startOnLoad=false
|
||||
# local eepserver
|
||||
tunnel.3.name=eepsite
|
||||
tunnel.3.description=My eepsite
|
||||
tunnel.3.type=server
|
||||
tunnel.3.targetHost=localhost
|
||||
tunnel.3.type=httpserver
|
||||
tunnel.3.targetHost=127.0.0.1
|
||||
tunnel.3.targetPort=7658
|
||||
tunnel.3.spoofedHost=mysite.i2p
|
||||
tunnel.3.privKeyFile=eepsite/eepPriv.dat
|
||||
tunnel.3.i2cpHost=localhost
|
||||
tunnel.3.i2cpHost=127.0.0.1
|
||||
tunnel.3.i2cpPort=7654
|
||||
tunnel.3.option.tunnels.depthInbound=2
|
||||
tunnel.3.option.tunnels.numInbound=2
|
||||
@ -54,7 +55,7 @@ tunnel.3.startOnLoad=true
|
||||
|
||||
# postman's SMTP server - see www.postman.i2p
|
||||
tunnel.4.description=smtp server
|
||||
tunnel.4.i2cpHost=localhost
|
||||
tunnel.4.i2cpHost=127.0.0.1
|
||||
tunnel.4.i2cpPort=7654
|
||||
tunnel.4.interface=127.0.0.1
|
||||
tunnel.4.listenPort=7659
|
||||
@ -69,7 +70,7 @@ tunnel.4.type=client
|
||||
# postman's POP3 server - see www.postman.i2p
|
||||
tunnel.5.name=pop3.postman.i2p
|
||||
tunnel.5.description=pop3 server
|
||||
tunnel.5.i2cpHost=localhost
|
||||
tunnel.5.i2cpHost=127.0.0.1
|
||||
tunnel.5.i2cpPort=7654
|
||||
tunnel.5.interface=127.0.0.1
|
||||
tunnel.5.listenPort=7660
|
||||
@ -78,4 +79,4 @@ tunnel.5.option.tunnels.numInbound=2
|
||||
tunnel.5.option.i2p.streaming.connectDelay=1000
|
||||
tunnel.5.startOnLoad=false
|
||||
tunnel.5.targetDestination=pop.postman.i2p
|
||||
tunnel.5.type=client
|
||||
tunnel.5.type=client
|
||||
|
@ -33,7 +33,7 @@ del /f /q "%INSTALL_PATH%postinstall.sh"
|
||||
:: del /f /q "%INSTALL_PATH%uninstall_i2p_service_unix"
|
||||
del /f /q "%INSTALL_PATH%icons\*.xpm"
|
||||
rmdir /q /s "%INSTALL_PATH%lib\wrapper"
|
||||
start /b /i /d"%INSTALL_PATH%" i2prouter.bat
|
||||
start /b /i /d"%INSTALL_PATH%" i2prouter.bat %INSTALL_PATH%
|
||||
|
||||
) else (
|
||||
|
||||
@ -47,6 +47,6 @@ del "%INSTALL_PATH%postinstall.sh"
|
||||
del "%INSTALL_PATH%uninstall_i2p_service_winnt.bat"
|
||||
del "%INSTALL_PATH%icons\*.xpm"
|
||||
deltree /Y "%INSTALL_PATH%lib\wrapper"
|
||||
start /M "%INSTALL_PATH%i2prouter.bat"
|
||||
start /M "%INSTALL_PATH%i2prouter.bat" %INSTALL_PATH%
|
||||
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -8,8 +8,12 @@ package net.i2p.router;
|
||||
*
|
||||
*/
|
||||
|
||||
//import net.i2p.router.message.ProcessOutboundClientMessageJob;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.client.I2PClient;
|
||||
|
||||
import net.i2p.router.message.OutboundClientMessageJob;
|
||||
import net.i2p.router.message.OutboundClientMessageOneShotJob;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@ -55,7 +59,22 @@ public class ClientMessagePool {
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Adding message for remote delivery");
|
||||
_context.jobQueue().addJob(new OutboundClientMessageJob(_context, msg));
|
||||
if (isGuaranteed(msg))
|
||||
_context.jobQueue().addJob(new OutboundClientMessageJob(_context, msg));
|
||||
else
|
||||
_context.jobQueue().addJob(new OutboundClientMessageOneShotJob(_context, msg));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isGuaranteed(ClientMessage msg) {
|
||||
Properties opts = null;
|
||||
if (msg.getSenderConfig() != null)
|
||||
opts = msg.getSenderConfig().getOptions();
|
||||
if (opts != null) {
|
||||
String val = opts.getProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT);
|
||||
return val.equals(I2PClient.PROP_RELIABILITY_GUARANTEED);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -75,9 +75,13 @@ public class Router {
|
||||
public final static String PROP_SHUTDOWN_IN_PROGRESS = "__shutdownInProgress";
|
||||
|
||||
static {
|
||||
// grumble about sun's java caching DNS entries *forever*
|
||||
System.setProperty("sun.net.inetaddr.ttl", "0");
|
||||
System.setProperty("networkaddress.cache.ttl", "0");
|
||||
// grumble about sun's java caching DNS entries *forever* by default
|
||||
// so lets just keep 'em for a minute
|
||||
System.setProperty("sun.net.inetaddr.ttl", "60");
|
||||
System.setProperty("networkaddress.cache.ttl", "60");
|
||||
// until we handle restricted routes and/or all peers support v6, try v4 first
|
||||
System.setProperty("java.net.preferIPv4Stack", "true");
|
||||
System.setProperty("http.agent", "I2P");
|
||||
// (no need for keepalive)
|
||||
System.setProperty("http.keepAlive", "false");
|
||||
System.setProperty("user.timezone", "GMT");
|
||||
@ -257,11 +261,49 @@ public class Router {
|
||||
}
|
||||
ri.sign(key);
|
||||
setRouterInfo(ri);
|
||||
_context.netDb().publish(ri);
|
||||
try {
|
||||
_context.netDb().publish(ri);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
_log.log(Log.CRIT, "Local router info is invalid? rebuilding a new identity", iae);
|
||||
rebuildNewIdentity();
|
||||
}
|
||||
} catch (DataFormatException dfe) {
|
||||
_log.log(Log.CRIT, "Internal error - unable to sign our own address?!", dfe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ugly list of files that we need to kill if we are building a new identity
|
||||
*
|
||||
*/
|
||||
private static final String _rebuildFiles[] = new String[] { "router.info",
|
||||
"router.keys",
|
||||
"connectionTag.keys",
|
||||
"keyBackup/privateEncryption.key",
|
||||
"keyBackup/privateSigning.key",
|
||||
"keyBackup/publicEncryption.key",
|
||||
"keyBackup/publicSigning.key",
|
||||
"sessionKeys.dat" };
|
||||
/**
|
||||
* Rebuild a new identity the hard way - delete all of our old identity
|
||||
* files, then reboot the router.
|
||||
*
|
||||
*/
|
||||
public void rebuildNewIdentity() {
|
||||
for (int i = 0; i < _rebuildFiles.length; i++) {
|
||||
File f = new File(_rebuildFiles[i]);
|
||||
if (f.exists()) {
|
||||
boolean removed = f.delete();
|
||||
if (removed)
|
||||
System.out.println("INFO: Removing old identity file: " + _rebuildFiles[i]);
|
||||
else
|
||||
System.out.println("ERROR: Could not remove old identity file: " + _rebuildFiles[i]);
|
||||
}
|
||||
}
|
||||
System.out.println("INFO: Restarting the router after removing any old identity files");
|
||||
// hard and ugly
|
||||
System.exit(EXIT_GRACEFUL_RESTART);
|
||||
}
|
||||
|
||||
/**
|
||||
* coalesce the stats framework every minute
|
||||
@ -272,6 +314,9 @@ public class Router {
|
||||
super(Router.this._context);
|
||||
Router.this._context.statManager().createRateStat("bw.receiveBps", "How fast we receive data", "Bandwidth", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
|
||||
Router.this._context.statManager().createRateStat("bw.sendBps", "How fast we send data", "Bandwidth", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
|
||||
Router.this._context.statManager().createRateStat("router.activePeers", "How many peers we are actively talking with", "Throttle", new long[] { 5*60*1000, 60*60*1000 });
|
||||
Router.this._context.statManager().createRateStat("router.highCapacityPeers", "How many high capacity peers we know", "Throttle", new long[] { 5*60*1000, 60*60*1000 });
|
||||
Router.this._context.statManager().createRateStat("router.fastPeers", "How many fast peers we know", "Throttle", new long[] { 5*60*1000, 60*60*1000 });
|
||||
}
|
||||
public String getName() { return "Coalesce stats"; }
|
||||
public void runJob() {
|
||||
@ -296,6 +341,15 @@ public class Router {
|
||||
Router.this._context.statManager().addRateData("bw.sendBps", (long)bps, 60*1000);
|
||||
}
|
||||
}
|
||||
|
||||
int active = Router.this._context.commSystem().countActivePeers();
|
||||
Router.this._context.statManager().addRateData("router.activePeers", active, 60*1000);
|
||||
|
||||
int fast = Router.this._context.profileOrganizer().countFastPeers();
|
||||
Router.this._context.statManager().addRateData("router.fastPeers", fast, 60*1000);
|
||||
|
||||
int highCap = Router.this._context.profileOrganizer().countHighCapacityPeers();
|
||||
Router.this._context.statManager().addRateData("router.highCapacityPeers", highCap, 60*1000);
|
||||
|
||||
requeue(60*1000);
|
||||
}
|
||||
@ -797,7 +851,11 @@ public class Router {
|
||||
installUpdates();
|
||||
verifyWrapperConfig();
|
||||
Router r = new Router();
|
||||
r.runRouter();
|
||||
if ( (args != null) && (args.length == 1) && ("rebuild".equals(args[0])) ) {
|
||||
r.rebuildNewIdentity();
|
||||
} else {
|
||||
r.runRouter();
|
||||
}
|
||||
}
|
||||
|
||||
private static final String UPDATE_FILE = "i2pupdate.zip";
|
||||
|
@ -30,6 +30,8 @@ class RouterThrottleImpl implements RouterThrottle {
|
||||
private static int THROTTLE_EVENT_LIMIT = 300;
|
||||
|
||||
private static final String PROP_MAX_TUNNELS = "router.maxParticipatingTunnels";
|
||||
private static final String PROP_DEFAULT_KBPS_THROTTLE = "router.defaultKBpsThrottle";
|
||||
private static final String PROP_BANDWIDTH_SHARE_PERCENTAGE = "router.sharePercentage";
|
||||
|
||||
public RouterThrottleImpl(RouterContext context) {
|
||||
_context = context;
|
||||
@ -43,11 +45,12 @@ class RouterThrottleImpl implements RouterThrottle {
|
||||
_context.statManager().createRateStat("router.throttleTunnelMaxExceeded", "How many tunnels we are participating in when we refuse one due to excees?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("router.throttleTunnelProbTooFast", "How many tunnels beyond the previous 1h average are we participating in when we throttle?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("router.throttleTunnelProbTestSlow", "How slow are our tunnel tests when our average exceeds the old average and we throttle?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
_context.statManager().createRateStat("router.throttleTunnelBandwidthExceeded", "How much bandwidth is allocated when we refuse due to bandwidth allocation?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
|
||||
}
|
||||
|
||||
public boolean acceptNetworkMessage() {
|
||||
long lag = _context.jobQueue().getMaxLag();
|
||||
if (lag > JOB_LAG_LIMIT) {
|
||||
if ( (lag > JOB_LAG_LIMIT) && (_context.router().getUptime() > 60*1000) ) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Throttling network reader, as the job lag is " + lag);
|
||||
_context.statManager().addRateData("router.throttleNetworkCause", lag, lag);
|
||||
@ -107,22 +110,7 @@ class RouterThrottleImpl implements RouterThrottle {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ok, we're not hosed, but can we handle the bandwidth requirements
|
||||
// of another tunnel?
|
||||
rs = _context.statManager().getRate("tunnel.participatingMessagesProcessed");
|
||||
r = null;
|
||||
if (rs != null)
|
||||
r = rs.getRate(10*60*1000);
|
||||
double msgsPerTunnel = (r != null ? r.getAverageValue() : 0);
|
||||
r = null;
|
||||
rs = _context.statManager().getRate("tunnel.relayMessageSize");
|
||||
if (rs != null)
|
||||
r = rs.getRate(10*60*1000);
|
||||
double bytesPerMsg = (r != null ? r.getAverageValue() : 0);
|
||||
double bytesPerTunnel = msgsPerTunnel * bytesPerMsg;
|
||||
|
||||
int numTunnels = _context.tunnelManager().getParticipatingCount();
|
||||
double bytesAllocated = (numTunnels + 1) * bytesPerTunnel;
|
||||
|
||||
if (_context.getProperty(Router.PROP_SHUTDOWN_IN_PROGRESS) != null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
@ -206,10 +194,21 @@ class RouterThrottleImpl implements RouterThrottle {
|
||||
// no default, ignore it
|
||||
}
|
||||
}
|
||||
|
||||
// ok, we're not hosed, but can we handle the bandwidth requirements
|
||||
// of another tunnel?
|
||||
rs = _context.statManager().getRate("tunnel.participatingBytesProcessed");
|
||||
r = null;
|
||||
if (rs != null)
|
||||
r = rs.getRate(10*60*1000);
|
||||
double bytesAllocated = r.getCurrentTotalValue();
|
||||
|
||||
if (!allowTunnel(bytesAllocated, numTunnels)) {
|
||||
_context.statManager().addRateData("router.throttleTunnelBandwidthExceeded", (long)bytesAllocated, 0);
|
||||
return false;
|
||||
}
|
||||
_context.statManager().addRateData("tunnel.bytesAllocatedAtAccept", (long)bytesAllocated, msg.getTunnelDurationSeconds()*1000);
|
||||
// todo: um, throttle (include bw usage of the netDb, our own tunnels, the clients,
|
||||
// and check to see that they are less than the bandwidth limits
|
||||
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Accepting a new tunnel request (now allocating " + bytesAllocated + " bytes across " + numTunnels
|
||||
@ -217,6 +216,99 @@ class RouterThrottleImpl implements RouterThrottle {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* with bytesAllocated already accounted for across the numTunnels existing
|
||||
* tunnels we have agreed to, can we handle another tunnel with our existing
|
||||
* bandwidth?
|
||||
*
|
||||
*/
|
||||
private boolean allowTunnel(double bytesAllocated, int numTunnels) {
|
||||
long bytesAllowed = getBytesAllowed();
|
||||
|
||||
bytesAllowed *= getSharePercentage();
|
||||
|
||||
double bytesPerTunnel = (numTunnels > 0 ? bytesAllocated / numTunnels : 0);
|
||||
double toAllocate = (numTunnels > 0 ? bytesPerTunnel * (numTunnels + 1) : 0);
|
||||
|
||||
double pctFull = toAllocate / bytesAllowed;
|
||||
|
||||
double allocatedKBps = toAllocate / (10 * 60 * 1024);
|
||||
|
||||
if (pctFull < 1.0) { // (_context.random().nextInt(100) > 100 * pctFull) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Probabalistically allowing the tunnel w/ " + pctFull + " of our " + bytesAllowed
|
||||
+ "bytes/" + allocatedKBps + "KBps allocated through " + numTunnels + " tunnels");
|
||||
return true;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Rejecting the tunnel w/ " + pctFull + " of our " + bytesAllowed
|
||||
+ "bytes allowed (" + toAllocate + "bytes / " + allocatedKBps
|
||||
+ "KBps) through " + numTunnels + " tunnels");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* What fraction of the bandwidth specified in our bandwidth limits should
|
||||
* we allow to be consumed by participating tunnels?
|
||||
*
|
||||
*/
|
||||
private double getSharePercentage() {
|
||||
String pct = _context.getProperty(PROP_BANDWIDTH_SHARE_PERCENTAGE, "0.8");
|
||||
if (pct != null) {
|
||||
try {
|
||||
return Double.parseDouble(pct);
|
||||
} catch (NumberFormatException nfe) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Unable to get the share percentage");
|
||||
}
|
||||
}
|
||||
return 0.8;
|
||||
}
|
||||
|
||||
/**
|
||||
* BytesPerSecond that we can pass along data
|
||||
*/
|
||||
private long getBytesAllowed() {
|
||||
String kbpsOutStr = _context.getProperty("i2np.bandwidth.outboundKBytesPerSecond");
|
||||
long kbpsOut = -1;
|
||||
if (kbpsOutStr != null) {
|
||||
try {
|
||||
kbpsOut = Integer.parseInt(kbpsOutStr);
|
||||
} catch (NumberFormatException nfe) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Unable to get the bytes allowed (outbound)");
|
||||
}
|
||||
}
|
||||
|
||||
String kbpsInStr = _context.getProperty("i2np.bandwidth.inboundKBytesPerSecond");
|
||||
long kbpsIn = -1;
|
||||
if (kbpsInStr != null) {
|
||||
try {
|
||||
kbpsIn = Integer.parseInt(kbpsInStr);
|
||||
} catch (NumberFormatException nfe) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Unable to get the bytes allowed (inbound)");
|
||||
}
|
||||
}
|
||||
|
||||
// whats our choke?
|
||||
long kbps = (kbpsOut > kbpsIn ? kbpsIn : kbpsOut);
|
||||
|
||||
if (kbps <= 0) {
|
||||
try {
|
||||
kbps = Integer.parseInt(_context.getProperty(PROP_DEFAULT_KBPS_THROTTLE, "64")); // absurd
|
||||
} catch (NumberFormatException nfe) {
|
||||
kbps = 64;
|
||||
}
|
||||
}
|
||||
|
||||
return kbps
|
||||
* 60 // per minute
|
||||
* 10 // per 10 minute period
|
||||
* 1024; // bytes;
|
||||
}
|
||||
|
||||
/** dont ever probabalistically throttle tunnels if we have less than this many */
|
||||
private int getMinThrottleTunnels() {
|
||||
try {
|
||||
|
@ -15,8 +15,8 @@ import net.i2p.CoreVersion;
|
||||
*
|
||||
*/
|
||||
public class RouterVersion {
|
||||
public final static String ID = "$Revision: 1.106 $ $Date: 2004/12/08 12:16:17 $";
|
||||
public final static String VERSION = "0.4.2.3";
|
||||
public final static String ID = "$Revision: 1.130 $ $Date: 2005/01/05 19:17:54 $";
|
||||
public final static String VERSION = "0.4.2.6";
|
||||
public final static long BUILD = 0;
|
||||
public static void main(String args[]) {
|
||||
System.out.println("I2P Router version: " + VERSION);
|
||||
|
@ -103,6 +103,7 @@ public class StatisticsManager implements Service {
|
||||
|
||||
includeThroughput(stats);
|
||||
includeRate("transport.sendProcessingTime", stats, new long[] { 60*60*1000 });
|
||||
includeRate("tcp.probabalisticDropQueueSize", stats, new long[] { 60*1000l, 60*60*1000l });
|
||||
//includeRate("tcp.queueSize", stats);
|
||||
//includeRate("jobQueue.jobLag", stats, new long[] { 60*1000, 60*60*1000 });
|
||||
//includeRate("jobQueue.jobRun", stats, new long[] { 60*1000, 60*60*1000 });
|
||||
@ -114,6 +115,8 @@ public class StatisticsManager implements Service {
|
||||
//includeRate("jobQueue.droppedJobs", stats, new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
//includeRate("inNetPool.dropped", stats, new long[] { 60*60*1000, 24*60*60*1000 });
|
||||
includeRate("tunnel.participatingTunnels", stats, new long[] { 5*60*1000, 60*60*1000 });
|
||||
includeRate("tunnel.participatingBytesProcessed", stats, new long[] { 10*60*1000 });
|
||||
includeRate("tunnel.participatingBytesProcessedActive", stats, new long[] { 10*60*1000 });
|
||||
includeRate("tunnel.testSuccessTime", stats, new long[] { 60*60*1000l, 24*60*60*1000l });
|
||||
//includeRate("tunnel.outboundMessagesProcessed", stats, new long[] { 10*60*1000, 60*60*1000 });
|
||||
//includeRate("tunnel.inboundMessagesProcessed", stats, new long[] { 10*60*1000, 60*60*1000 });
|
||||
|
@ -56,6 +56,7 @@ public class TunnelInfo extends DataStructureImpl {
|
||||
private boolean _wasEverReady;
|
||||
private int _messagesProcessed;
|
||||
private int _tunnelFailures;
|
||||
private long _bytesProcessed;
|
||||
|
||||
public TunnelInfo(I2PAppContext context) {
|
||||
_context = context;
|
||||
@ -79,6 +80,7 @@ public class TunnelInfo extends DataStructureImpl {
|
||||
_lastTested = -1;
|
||||
_messagesProcessed = 0;
|
||||
_tunnelFailures = 0;
|
||||
_bytesProcessed = 0;
|
||||
}
|
||||
|
||||
public TunnelId getTunnelId() { return _id; }
|
||||
@ -182,7 +184,12 @@ public class TunnelInfo extends DataStructureImpl {
|
||||
return _messagesProcessed;
|
||||
}
|
||||
/** we have just processed a message for this tunnel */
|
||||
public void messageProcessed() { _messagesProcessed++; }
|
||||
public void messageProcessed(int size) {
|
||||
_messagesProcessed++;
|
||||
_bytesProcessed += size;
|
||||
}
|
||||
/** how many bytes have been pumped through this tunnel in its lifetime? */
|
||||
public long getBytesProcessed() { return _bytesProcessed; }
|
||||
|
||||
/**
|
||||
* the tunnel was (potentially) unable to pass a message through.
|
||||
|
@ -83,6 +83,9 @@ class ClientWriterRunner implements Runnable {
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
I2CPMessage msg = (I2CPMessage)messages.get(i);
|
||||
Long when = (Long)messageTimes.get(i);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("["+_id+"] writeMessage before writing "
|
||||
+ msg.getClass().getName());
|
||||
_runner.writeMessage(msg);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("["+_id+"] writeMessage time since addMessage(): "
|
||||
|
@ -240,7 +240,7 @@ public class HandleTunnelMessageJob extends JobImpl {
|
||||
if (info == null)
|
||||
return;
|
||||
|
||||
info.messageProcessed();
|
||||
info.messageProcessed(_message.getMessageSize());
|
||||
|
||||
//if ( (_message.getVerificationStructure() == null) && (info.getSigningKey() != null) ) {
|
||||
if (_message.getVerificationStructure() == null) {
|
||||
|
@ -0,0 +1,490 @@
|
||||
package net.i2p.router.message;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import net.i2p.data.Certificate;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.Lease;
|
||||
import net.i2p.data.LeaseSet;
|
||||
import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.SessionTag;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.TunnelId;
|
||||
|
||||
import net.i2p.data.i2cp.MessageId;
|
||||
|
||||
import net.i2p.data.i2np.DataMessage;
|
||||
import net.i2p.data.i2np.DeliveryStatusMessage;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.data.i2np.GarlicMessage;
|
||||
import net.i2p.data.i2np.DeliveryInstructions;
|
||||
|
||||
import net.i2p.router.message.PayloadGarlicConfig;
|
||||
|
||||
import net.i2p.router.ClientMessage;
|
||||
import net.i2p.router.JobImpl;
|
||||
import net.i2p.router.ReplyJob;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.TunnelSelectionCriteria;
|
||||
import net.i2p.router.MessageSelector;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Send a client message out a random outbound tunnel and into a random inbound
|
||||
* tunnel on the target leaseSet. This also bundles the sender's leaseSet and
|
||||
* a DeliveryStatusMessage (for ACKing any sessionTags used in the garlic).
|
||||
*
|
||||
*/
|
||||
public class OutboundClientMessageOneShotJob extends JobImpl {
|
||||
private Log _log;
|
||||
private long _overallExpiration;
|
||||
private boolean _shouldBundle;
|
||||
private ClientMessage _clientMessage;
|
||||
private MessageId _clientMessageId;
|
||||
private int _clientMessageSize;
|
||||
private Destination _from;
|
||||
private Destination _to;
|
||||
/** target destination's leaseSet, if known */
|
||||
private LeaseSet _leaseSet;
|
||||
/** Actual lease the message is being routed through */
|
||||
private Lease _lease;
|
||||
private PayloadGarlicConfig _clove;
|
||||
private long _cloveId;
|
||||
private long _start;
|
||||
private boolean _finished;
|
||||
|
||||
/**
|
||||
* final timeout (in milliseconds) that the outbound message will fail in.
|
||||
* This can be overridden in the router.config or the client's session config
|
||||
* (the client's session config takes precedence)
|
||||
*/
|
||||
public final static String OVERALL_TIMEOUT_MS_PARAM = "clientMessageTimeout";
|
||||
private final static long OVERALL_TIMEOUT_MS_DEFAULT = 60*1000;
|
||||
|
||||
/** priority of messages, that might get honored some day... */
|
||||
private final static int SEND_PRIORITY = 500;
|
||||
|
||||
/**
|
||||
* If the client's config specifies shouldBundleReplyInfo=true, messages sent from
|
||||
* that client to any peers will probabalistically include the sending destination's
|
||||
* current LeaseSet (allowing the recipient to reply without having to do a full
|
||||
* netDb lookup). This should improve performance during the initial negotiations,
|
||||
* but is not necessary for communication that isn't bidirectional.
|
||||
*
|
||||
*/
|
||||
public static final String BUNDLE_REPLY_LEASESET = "shouldBundleReplyInfo";
|
||||
/**
|
||||
* Allow the override of the frequency of bundling the reply info in with a message.
|
||||
* The client app can specify bundleReplyInfoProbability=80 (for instance) and that
|
||||
* will cause the router to include the sender's leaseSet with 80% of the messages
|
||||
* sent to the peer.
|
||||
*
|
||||
*/
|
||||
public static final String BUNDLE_PROBABILITY = "bundleReplyInfoProbability";
|
||||
/**
|
||||
* How often do messages include the reply leaseSet (out of every 100 tries).
|
||||
* Including it each time is probably overkill, but who knows.
|
||||
*/
|
||||
private static final int BUNDLE_PROBABILITY_DEFAULT = 100;
|
||||
|
||||
/**
|
||||
* Send the sucker
|
||||
*/
|
||||
public OutboundClientMessageOneShotJob(RouterContext ctx, ClientMessage msg) {
|
||||
super(ctx);
|
||||
_log = ctx.logManager().getLog(OutboundClientMessageOneShotJob.class);
|
||||
|
||||
ctx.statManager().createFrequencyStat("client.sendMessageFailFrequency", "How often does a client fail to send a message?", "ClientMessages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
ctx.statManager().createRateStat("client.sendMessageSize", "How large are messages sent by the client?", "ClientMessages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
ctx.statManager().createRateStat("client.sendAckTime", "How long does it take to get an ACK back from a message?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
ctx.statManager().createRateStat("client.timeoutCongestionTunnel", "How lagged our tunnels are when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
ctx.statManager().createRateStat("client.timeoutCongestionMessage", "How fast we process messages locally when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
ctx.statManager().createRateStat("client.timeoutCongestionInbound", "How much faster we are receiving data than our average bps when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
|
||||
long timeoutMs = OVERALL_TIMEOUT_MS_DEFAULT;
|
||||
_clientMessage = msg;
|
||||
_clientMessageId = msg.getMessageId();
|
||||
_clientMessageSize = msg.getPayload().getSize();
|
||||
_from = msg.getFromDestination();
|
||||
_to = msg.getDestination();
|
||||
|
||||
String param = msg.getSenderConfig().getOptions().getProperty(OVERALL_TIMEOUT_MS_PARAM);
|
||||
if (param == null)
|
||||
param = ctx.router().getConfigSetting(OVERALL_TIMEOUT_MS_PARAM);
|
||||
if (param != null) {
|
||||
try {
|
||||
timeoutMs = Long.parseLong(param);
|
||||
} catch (NumberFormatException nfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Invalid client message timeout specified [" + param
|
||||
+ "], defaulting to " + OVERALL_TIMEOUT_MS_DEFAULT, nfe);
|
||||
timeoutMs = OVERALL_TIMEOUT_MS_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
_start = getContext().clock().now();
|
||||
_overallExpiration = timeoutMs + _start;
|
||||
_shouldBundle = getShouldBundle();
|
||||
_finished = false;
|
||||
}
|
||||
|
||||
public String getName() { return "Outbound client message"; }
|
||||
|
||||
public void runJob() {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": Send outbound client message job beginning");
|
||||
buildClove();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": Clove built");
|
||||
long timeoutMs = _overallExpiration - getContext().clock().now();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": Send outbound client message - sending off leaseSet lookup job");
|
||||
getContext().netDb().lookupLeaseSet(_to.calculateHash(), new SendJob(), new LookupLeaseSetFailedJob(), timeoutMs);
|
||||
}
|
||||
|
||||
private boolean getShouldBundle() {
|
||||
Properties opts = _clientMessage.getSenderConfig().getOptions();
|
||||
String wantBundle = opts.getProperty(BUNDLE_REPLY_LEASESET, "true");
|
||||
if ("true".equals(wantBundle)) {
|
||||
int probability = BUNDLE_PROBABILITY_DEFAULT;
|
||||
String str = opts.getProperty(BUNDLE_PROBABILITY);
|
||||
try {
|
||||
if (str != null)
|
||||
probability = Integer.parseInt(str);
|
||||
} catch (NumberFormatException nfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getJobId() + ": Bundle leaseSet probability overridden incorrectly ["
|
||||
+ str + "]", nfe);
|
||||
}
|
||||
if (probability >= getContext().random().nextInt(100))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** send a message to a random lease */
|
||||
private class SendJob extends JobImpl {
|
||||
public SendJob() {
|
||||
super(OutboundClientMessageOneShotJob.this.getContext());
|
||||
}
|
||||
public String getName() { return "Send outbound client message through the lease"; }
|
||||
public void runJob() {
|
||||
boolean ok = getNextLease();
|
||||
if (ok)
|
||||
send();
|
||||
else
|
||||
dieFatal();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fetch the next lease that we should try sending through, randomly chosen
|
||||
* from within the sorted leaseSet (sorted by # of failures through each
|
||||
* lease).
|
||||
*
|
||||
*/
|
||||
private boolean getNextLease() {
|
||||
_leaseSet = getContext().netDb().lookupLeaseSetLocally(_to.calculateHash());
|
||||
if (_leaseSet == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getJobId() + ": Lookup locally didn't find the leaseSet");
|
||||
return false;
|
||||
}
|
||||
long now = getContext().clock().now();
|
||||
|
||||
// get the possible leases
|
||||
List leases = new ArrayList(_leaseSet.getLeaseCount());
|
||||
for (int i = 0; i < _leaseSet.getLeaseCount(); i++) {
|
||||
Lease lease = _leaseSet.getLease(i);
|
||||
if (lease.isExpired(Router.CLOCK_FUDGE_FACTOR)) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getJobId() + ": getNextLease() - expired lease! - " + lease);
|
||||
continue;
|
||||
} else {
|
||||
leases.add(lease);
|
||||
}
|
||||
}
|
||||
|
||||
if (leases.size() <= 0) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(getJobId() + ": No leases found from: " + _leaseSet);
|
||||
return false;
|
||||
}
|
||||
|
||||
// randomize the ordering (so leases with equal # of failures per next
|
||||
// sort are randomly ordered)
|
||||
Collections.shuffle(leases);
|
||||
|
||||
// ordered by lease number of failures
|
||||
TreeMap orderedLeases = new TreeMap();
|
||||
for (Iterator iter = leases.iterator(); iter.hasNext(); ) {
|
||||
Lease lease = (Lease)iter.next();
|
||||
long id = lease.getNumFailure();
|
||||
while (orderedLeases.containsKey(new Long(id)))
|
||||
id++;
|
||||
orderedLeases.put(new Long(id), lease);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": ranking lease we havent sent it down as " + id);
|
||||
}
|
||||
|
||||
_lease = (Lease)orderedLeases.get(orderedLeases.firstKey());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* we couldn't even find the leaseSet, but try again (or die
|
||||
* if we've already tried too hard)
|
||||
*
|
||||
*/
|
||||
private class LookupLeaseSetFailedJob extends JobImpl {
|
||||
public LookupLeaseSetFailedJob() {
|
||||
super(OutboundClientMessageOneShotJob.this.getContext());
|
||||
}
|
||||
public String getName() { return "Lookup for outbound client message failed"; }
|
||||
public void runJob() {
|
||||
dieFatal();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the message to the specified tunnel by creating a new garlic message containing
|
||||
* the (already created) payload clove as well as a new delivery status message. This garlic
|
||||
* message is sent out one of our tunnels, destined for the lease (tunnel+router) specified, and the delivery
|
||||
* status message is targetting one of our free inbound tunnels as well. We use a new
|
||||
* reply selector to keep an eye out for that delivery status message's token
|
||||
*
|
||||
*/
|
||||
private void send() {
|
||||
if (_finished) return;
|
||||
long token = getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE);
|
||||
PublicKey key = _leaseSet.getEncryptionKey();
|
||||
SessionKey sessKey = new SessionKey();
|
||||
Set tags = new HashSet();
|
||||
LeaseSet replyLeaseSet = null;
|
||||
if (_shouldBundle) {
|
||||
replyLeaseSet = getContext().netDb().lookupLeaseSetLocally(_from.calculateHash());
|
||||
}
|
||||
|
||||
GarlicMessage msg = OutboundClientMessageJobHelper.createGarlicMessage(getContext(), token,
|
||||
_overallExpiration, key,
|
||||
_clove,
|
||||
_to,
|
||||
sessKey, tags,
|
||||
true, replyLeaseSet);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": send() - token expected " + token);
|
||||
|
||||
SendSuccessJob onReply = new SendSuccessJob(sessKey, tags);
|
||||
SendTimeoutJob onFail = new SendTimeoutJob();
|
||||
ReplySelector selector = new ReplySelector(token);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": Placing GarlicMessage into the new tunnel message bound for "
|
||||
+ _lease.getTunnelId() + " on "
|
||||
+ _lease.getRouterIdentity().getHash().toBase64());
|
||||
|
||||
TunnelId outTunnelId = selectOutboundTunnel();
|
||||
if (outTunnelId != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": Sending tunnel message out " + outTunnelId + " to "
|
||||
+ _lease.getTunnelId() + " on "
|
||||
+ _lease.getRouterIdentity().getHash().toBase64());
|
||||
SendTunnelMessageJob j = new SendTunnelMessageJob(getContext(), msg, outTunnelId,
|
||||
_lease.getRouterIdentity().getHash(),
|
||||
_lease.getTunnelId(), null, onReply,
|
||||
onFail, selector,
|
||||
_overallExpiration-getContext().clock().now(),
|
||||
SEND_PRIORITY);
|
||||
getContext().jobQueue().addJob(j);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error(getJobId() + ": Could not find any outbound tunnels to send the payload through... wtf?");
|
||||
dieFatal();
|
||||
}
|
||||
_clientMessage = null;
|
||||
_clove = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick an arbitrary outbound tunnel to send the message through, or null if
|
||||
* there aren't any around
|
||||
*
|
||||
*/
|
||||
private TunnelId selectOutboundTunnel() {
|
||||
TunnelSelectionCriteria crit = new TunnelSelectionCriteria();
|
||||
crit.setMaximumTunnelsRequired(1);
|
||||
crit.setMinimumTunnelsRequired(1);
|
||||
List tunnelIds = getContext().tunnelManager().selectOutboundTunnelIds(crit);
|
||||
if (tunnelIds.size() <= 0)
|
||||
return null;
|
||||
else
|
||||
return (TunnelId)tunnelIds.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* give up the ghost, this message just aint going through. tell the client to fuck off.
|
||||
*
|
||||
* this is safe to call multiple times (only tells the client once)
|
||||
*/
|
||||
private void dieFatal() {
|
||||
if (_finished) return;
|
||||
_finished = true;
|
||||
|
||||
long sendTime = getContext().clock().now() - _start;
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getJobId() + ": Failed to send the message " + _clientMessageId + " after "
|
||||
+ sendTime + "ms", new Exception("Message send failure"));
|
||||
|
||||
long messageDelay = getContext().throttle().getMessageDelay();
|
||||
long tunnelLag = getContext().throttle().getTunnelLag();
|
||||
long inboundDelta = (long)getContext().throttle().getInboundRateDelta();
|
||||
|
||||
getContext().statManager().addRateData("client.timeoutCongestionTunnel", tunnelLag, 1);
|
||||
getContext().statManager().addRateData("client.timeoutCongestionMessage", messageDelay, 1);
|
||||
getContext().statManager().addRateData("client.timeoutCongestionInbound", inboundDelta, 1);
|
||||
|
||||
getContext().messageHistory().sendPayloadMessage(_clientMessageId.getMessageId(), false, sendTime);
|
||||
getContext().clientManager().messageDeliveryStatusUpdate(_from, _clientMessageId, false);
|
||||
getContext().statManager().updateFrequency("client.sendMessageFailFrequency");
|
||||
_clientMessage = null;
|
||||
_clove = null;
|
||||
}
|
||||
|
||||
/** build the payload clove that will be used for all of the messages, placing the clove in the status structure */
|
||||
private void buildClove() {
|
||||
PayloadGarlicConfig clove = new PayloadGarlicConfig();
|
||||
|
||||
DeliveryInstructions instructions = new DeliveryInstructions();
|
||||
instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_DESTINATION);
|
||||
instructions.setDestination(_to.calculateHash());
|
||||
|
||||
instructions.setDelayRequested(false);
|
||||
instructions.setDelaySeconds(0);
|
||||
instructions.setEncrypted(false);
|
||||
|
||||
clove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
|
||||
clove.setDeliveryInstructions(instructions);
|
||||
clove.setExpiration(_overallExpiration);
|
||||
clove.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE));
|
||||
|
||||
DataMessage msg = new DataMessage(getContext());
|
||||
msg.setData(_clientMessage.getPayload().getEncryptedData());
|
||||
|
||||
clove.setPayload(msg);
|
||||
clove.setRecipientPublicKey(null);
|
||||
clove.setRequestAck(false);
|
||||
|
||||
_clove = clove;
|
||||
_cloveId = _clove.getId();
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": Built payload clove with id " + clove.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep an eye out for any of the delivery status message tokens that have been
|
||||
* sent down the various tunnels to deliver this message
|
||||
*
|
||||
*/
|
||||
private class ReplySelector implements MessageSelector {
|
||||
private long _pendingToken;
|
||||
public ReplySelector(long token) {
|
||||
_pendingToken = token;
|
||||
}
|
||||
|
||||
public boolean continueMatching() { return false; }
|
||||
public long getExpiration() { return _overallExpiration; }
|
||||
|
||||
public boolean isMatch(I2NPMessage inMsg) {
|
||||
if (inMsg.getType() == DeliveryStatusMessage.MESSAGE_TYPE) {
|
||||
return _pendingToken == ((DeliveryStatusMessage)inMsg).getMessageId();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after we get a confirmation that the message was delivered safely
|
||||
* (hoo-ray!)
|
||||
*
|
||||
*/
|
||||
private class SendSuccessJob extends JobImpl implements ReplyJob {
|
||||
private SessionKey _key;
|
||||
private Set _tags;
|
||||
|
||||
/**
|
||||
* Create a new success job that will be fired when the message encrypted with
|
||||
* the given session key and bearing the specified tags are confirmed delivered.
|
||||
*
|
||||
*/
|
||||
public SendSuccessJob(SessionKey key, Set tags) {
|
||||
super(OutboundClientMessageOneShotJob.this.getContext());
|
||||
_key = key;
|
||||
_tags = tags;
|
||||
}
|
||||
|
||||
public String getName() { return "Send client message successful to a lease"; }
|
||||
public void runJob() {
|
||||
if (_finished) return;
|
||||
_finished = true;
|
||||
long sendTime = getContext().clock().now() - _start;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(OutboundClientMessageOneShotJob.this.getJobId()
|
||||
+ ": SUCCESS! msg " + _clientMessageId
|
||||
+ " sent after " + sendTime + "ms");
|
||||
|
||||
if ( (_key != null) && (_tags != null) && (_tags.size() > 0) ) {
|
||||
if (_leaseSet != null)
|
||||
getContext().sessionKeyManager().tagsDelivered(_leaseSet.getEncryptionKey(),
|
||||
_key, _tags);
|
||||
}
|
||||
|
||||
long dataMsgId = _cloveId;
|
||||
getContext().messageHistory().sendPayloadMessage(dataMsgId, true, sendTime);
|
||||
getContext().clientManager().messageDeliveryStatusUpdate(_from, _clientMessageId, true);
|
||||
_lease.setNumSuccess(_lease.getNumSuccess()+1);
|
||||
|
||||
getContext().statManager().addRateData("client.sendAckTime", sendTime, 0);
|
||||
getContext().statManager().addRateData("client.sendMessageSize", _clientMessageSize, sendTime);
|
||||
}
|
||||
|
||||
public void setMessage(I2NPMessage msg) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired after the basic timeout for sending through the given tunnel has been reached.
|
||||
* We'll accept successes later, but won't expect them
|
||||
*
|
||||
*/
|
||||
private class SendTimeoutJob extends JobImpl {
|
||||
public SendTimeoutJob() {
|
||||
super(OutboundClientMessageOneShotJob.this.getContext());
|
||||
}
|
||||
|
||||
public String getName() { return "Send client message timed out through a lease"; }
|
||||
public void runJob() {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(OutboundClientMessageOneShotJob.this.getJobId()
|
||||
+ ": Soft timeout through the lease " + _lease);
|
||||
|
||||
_lease.setNumFailure(_lease.getNumFailure()+1);
|
||||
dieFatal();
|
||||
}
|
||||
}
|
||||
}
|
@ -121,7 +121,7 @@ public class SendTunnelMessageJob extends JobImpl {
|
||||
}
|
||||
}
|
||||
|
||||
info.messageProcessed();
|
||||
info.messageProcessed(_message.getMessageSize());
|
||||
|
||||
if (isEndpoint(info)) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -147,8 +147,10 @@ class PersistentDataStore extends TransientDataStore {
|
||||
}
|
||||
|
||||
private class ReadJob extends JobImpl {
|
||||
private boolean _alreadyWarned;
|
||||
public ReadJob() {
|
||||
super(PersistentDataStore.this._context);
|
||||
_alreadyWarned = false;
|
||||
}
|
||||
public String getName() { return "DB Read Job"; }
|
||||
public void runJob() {
|
||||
@ -158,6 +160,7 @@ class PersistentDataStore extends TransientDataStore {
|
||||
}
|
||||
|
||||
private void readFiles() {
|
||||
int routerCount = 0;
|
||||
try {
|
||||
File dbDir = getDbDir();
|
||||
File leaseSetFiles[] = dbDir.listFiles(LeaseSetFilter.getInstance());
|
||||
@ -170,6 +173,9 @@ class PersistentDataStore extends TransientDataStore {
|
||||
}
|
||||
File routerInfoFiles[] = dbDir.listFiles(RouterInfoFilter.getInstance());
|
||||
if (routerInfoFiles != null) {
|
||||
routerCount += routerInfoFiles.length;
|
||||
if (routerInfoFiles.length > 5)
|
||||
_alreadyWarned = false;
|
||||
for (int i = 0; i < routerInfoFiles.length; i++) {
|
||||
Hash key = getRouterInfoHash(routerInfoFiles[i].getName());
|
||||
if ( (key != null) && (!isKnown(key)) )
|
||||
@ -179,6 +185,11 @@ class PersistentDataStore extends TransientDataStore {
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error reading files in the db dir", ioe);
|
||||
}
|
||||
|
||||
if ( (routerCount <= 5) && (!_alreadyWarned) ) {
|
||||
_log.error("Very few routerInfo files remaining - please reseed");
|
||||
_alreadyWarned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,18 +45,25 @@ public class CapacityCalculator extends Calculator {
|
||||
private static long ESTIMATE_PERIOD = 60*60*1000;
|
||||
|
||||
public double calc(PeerProfile profile) {
|
||||
double capacity = 0;
|
||||
|
||||
RateStat acceptStat = profile.getTunnelCreateResponseTime();
|
||||
RateStat rejectStat = profile.getTunnelHistory().getRejectionRate();
|
||||
RateStat failedStat = profile.getTunnelHistory().getFailedRate();
|
||||
|
||||
capacity += estimatePartial(acceptStat, rejectStat, failedStat, 10*60*1000);
|
||||
capacity += estimatePartial(acceptStat, rejectStat, failedStat, 30*60*1000);
|
||||
capacity += estimatePartial(acceptStat, rejectStat, failedStat, 60*60*1000);
|
||||
capacity += estimatePartial(acceptStat, rejectStat, failedStat, 24*60*60*1000);
|
||||
double capacity10m = estimateCapacity(acceptStat, rejectStat, failedStat, 10*60*1000);
|
||||
double capacity30m = estimateCapacity(acceptStat, rejectStat, failedStat, 30*60*1000);
|
||||
double capacity60m = estimateCapacity(acceptStat, rejectStat, failedStat, 60*60*1000);
|
||||
double capacity1d = estimateCapacity(acceptStat, rejectStat, failedStat, 24*60*60*1000);
|
||||
|
||||
if (tooOld(profile))
|
||||
double capacity = capacity10m * periodWeight(10*60*1000) +
|
||||
capacity30m * periodWeight(30*60*1000) +
|
||||
capacity60m * periodWeight(60*60*1000) +
|
||||
capacity1d * periodWeight(24*60*60*1000);
|
||||
|
||||
// if we actively know they're bad, who cares if they used to be good?
|
||||
if (capacity10m <= 0)
|
||||
capacity = 0;
|
||||
|
||||
if (tooOld(profile))
|
||||
capacity = 1;
|
||||
|
||||
capacity += profile.getReliabilityBonus();
|
||||
@ -74,7 +81,7 @@ public class CapacityCalculator extends Calculator {
|
||||
return true;
|
||||
}
|
||||
|
||||
private double estimatePartial(RateStat acceptStat, RateStat rejectStat, RateStat failedStat, int period) {
|
||||
private double estimateCapacity(RateStat acceptStat, RateStat rejectStat, RateStat failedStat, int period) {
|
||||
Rate curAccepted = acceptStat.getRate(period);
|
||||
Rate curRejected = rejectStat.getRate(period);
|
||||
Rate curFailed = failedStat.getRate(period);
|
||||
@ -82,27 +89,29 @@ public class CapacityCalculator extends Calculator {
|
||||
long eventCount = 0;
|
||||
if (curAccepted != null)
|
||||
eventCount = curAccepted.getCurrentEventCount() + curAccepted.getLastEventCount();
|
||||
double stretch = ESTIMATE_PERIOD / period;
|
||||
double stretch = ((double)ESTIMATE_PERIOD) / period;
|
||||
double val = eventCount * stretch;
|
||||
long failed = 0;
|
||||
if (curFailed != null)
|
||||
failed = curFailed.getCurrentEventCount() + curFailed.getLastEventCount();
|
||||
if (failed > 0) {
|
||||
if ( (period == 10*60*1000) && (curFailed.getCurrentEventCount() > 0) )
|
||||
return 0.0d; // their tunnels have failed in the last 0-10 minutes
|
||||
else
|
||||
val -= failed * stretch;
|
||||
//if ( (period <= 10*60*1000) && (curFailed.getCurrentEventCount() > 0) )
|
||||
// return 0.0d; // their tunnels have failed in the last 0-10 minutes
|
||||
//else
|
||||
val -= failed * stretch;
|
||||
}
|
||||
|
||||
if ( (period == 10*60*1000) && (curRejected.getCurrentEventCount() + curRejected.getLastEventCount() > 0) )
|
||||
return 0.0d;
|
||||
else
|
||||
//if ( (period <= 10*60*1000) && (curRejected.getCurrentEventCount() + curRejected.getLastEventCount() > 0) ) {
|
||||
// //System.out.println("10m period has rejected " + (curRejected.getCurrentEventCount() + curRejected.getLastEventCount()) + " times");
|
||||
// return 0.0d;
|
||||
//} else
|
||||
val -= stretch * (curRejected.getCurrentEventCount() + curRejected.getLastEventCount());
|
||||
|
||||
val += GROWTH_FACTOR;
|
||||
|
||||
if (val >= 0) {
|
||||
return (val + GROWTH_FACTOR) * periodWeight(period);
|
||||
return val;
|
||||
} else {
|
||||
// failed too much, don't grow
|
||||
return 0.0d;
|
||||
}
|
||||
}
|
||||
|
@ -248,7 +248,7 @@ public class PeerProfile {
|
||||
if (_dbResponseTime == null)
|
||||
_dbResponseTime = new RateStat("dbResponseTime", "how long it takes to get a db response from the peer (in milliseconds)", group, new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000 } );
|
||||
if (_tunnelCreateResponseTime == null)
|
||||
_tunnelCreateResponseTime = new RateStat("tunnelCreateResponseTime", "how long it takes to get a tunnel create response from the peer (in milliseconds)", group, new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000 } );
|
||||
_tunnelCreateResponseTime = new RateStat("tunnelCreateResponseTime", "how long it takes to get a tunnel create response from the peer (in milliseconds)", group, new long[] { 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000 } );
|
||||
if (_tunnelTestResponseTime == null)
|
||||
_tunnelTestResponseTime = new RateStat("tunnelTestResponseTime", "how long it takes to successfully test a tunnel this peer participates in (in milliseconds)", group, new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000 } );
|
||||
if (_commError == null)
|
||||
|
@ -109,7 +109,8 @@ class ProfileOrganizerRenderer {
|
||||
buf.append("<td align=\"right\">").append(prof.getIsFailing()).append("</td>");
|
||||
//buf.append("<td><a href=\"/profile/").append(prof.getPeer().toBase64().substring(0, 32)).append("\">profile.txt</a> ");
|
||||
//buf.append(" <a href=\"#").append(prof.getPeer().toBase64().substring(0, 32)).append("\">netDb</a></td>");
|
||||
buf.append("<td><a href=\"netdb.jsp#").append(peer.toBase64().substring(0,6)).append("\">netDb</a></td>\n");
|
||||
buf.append("<td nowrap><a href=\"netdb.jsp#").append(peer.toBase64().substring(0,6)).append("\">netDb</a>");
|
||||
buf.append("/<a href=\"dumpprofile.jsp?peer=").append(peer.toBase64().substring(0,6)).append("\">profile</a></td>\n");
|
||||
buf.append("</tr>");
|
||||
}
|
||||
buf.append("</table>");
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user