forked from I2P_Developers/i2p.i2p
Compare commits
25 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 |
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);
|
||||
|
@ -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,7 +40,8 @@ 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 */
|
||||
@ -53,15 +54,22 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
|
||||
private volatile long __forwarderId;
|
||||
|
||||
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock, byte[] initialData, List sockList) {
|
||||
this(s, i2ps, slock, initialData, sockList, null);
|
||||
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[] initialData, List sockList, Runnable onTimeout) {
|
||||
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();
|
||||
@ -111,15 +119,19 @@ public class I2PTunnelRunner extends I2PThread implements I2PSocket.SocketErrorL
|
||||
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");
|
||||
_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) {
|
||||
|
@ -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) ) {
|
||||
@ -297,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"); }
|
||||
@ -314,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);
|
||||
}
|
||||
@ -367,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) )
|
||||
@ -378,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 + "]";
|
||||
}
|
||||
@ -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)
|
||||
*
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -33,6 +33,7 @@ public class Connection {
|
||||
private long _lastSendId;
|
||||
private boolean _resetReceived;
|
||||
private boolean _resetSent;
|
||||
private long _resetSentOn;
|
||||
private boolean _connected;
|
||||
private boolean _hardDisconnected;
|
||||
private MessageInputStream _inputStream;
|
||||
@ -72,7 +73,7 @@ public class Connection {
|
||||
private long _lifetimeDupMessageReceived;
|
||||
|
||||
public static final long MAX_RESEND_DELAY = 60*1000;
|
||||
public static final long MIN_RESEND_DELAY = 40*1000;
|
||||
public static final long MIN_RESEND_DELAY = 30*1000;
|
||||
|
||||
/** wait up to 5 minutes after disconnection so we can ack/close packets */
|
||||
public static int DISCONNECT_TIMEOUT = 5*60*1000;
|
||||
@ -113,6 +114,7 @@ public class Connection {
|
||||
_ackSinceCongestion = true;
|
||||
_connectLock = new Object();
|
||||
_activeResends = 0;
|
||||
_resetSentOn = -1;
|
||||
_context.statManager().createRateStat("stream.con.windowSizeAtCongestion", "How large was our send window when we send a dup?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.chokeSizeBegin", "How many messages were outstanding when we started to choke?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
_context.statManager().createRateStat("stream.chokeSizeEnd", "How many messages were outstanding when we stopped being choked?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
|
||||
@ -146,20 +148,25 @@ public class Connection {
|
||||
synchronized (_outboundPackets) {
|
||||
if (!started)
|
||||
_context.statManager().addRateData("stream.chokeSizeBegin", _outboundPackets.size(), timeoutMs);
|
||||
if (!_connected)
|
||||
return false;
|
||||
started = true;
|
||||
if (_outboundPackets.size() >= _options.getWindowSize()) {
|
||||
if ( (_outboundPackets.size() >= _options.getWindowSize()) || (_activeResends > 0) ) {
|
||||
if (writeExpire > 0) {
|
||||
if (timeLeft <= 0) {
|
||||
_log.error("Outbound window is full of " + _outboundPackets.size()
|
||||
+ " with " + _activeResends + " active resends"
|
||||
+ " and we've waited too long (" + writeExpire + "ms)");
|
||||
return false;
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Outbound window is full (" + _outboundPackets.size() + "/" + _options.getWindowSize() + "), waiting " + timeLeft);
|
||||
_log.debug("Outbound window is full (" + _outboundPackets.size() + "/" + _options.getWindowSize() + "/"
|
||||
+ _activeResends + "), waiting " + timeLeft);
|
||||
try { _outboundPackets.wait(timeLeft); } catch (InterruptedException ie) {}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Outbound window is full (" + _outboundPackets.size() + "), waiting indefinitely");
|
||||
_log.debug("Outbound window is full (" + _outboundPackets.size() + "/" + _activeResends
|
||||
+ "), waiting indefinitely");
|
||||
try { _outboundPackets.wait(); } catch (InterruptedException ie) {}
|
||||
}
|
||||
} else {
|
||||
@ -180,6 +187,8 @@ public class Connection {
|
||||
*/
|
||||
void sendReset() {
|
||||
_resetSent = true;
|
||||
if (_resetSentOn <= 0)
|
||||
_resetSentOn = _context.clock().now();
|
||||
if ( (_remotePeer == null) || (_sendStreamId == null) ) return;
|
||||
PacketLocal reply = new PacketLocal(_context, _remotePeer);
|
||||
reply.setFlag(Packet.FLAG_RESET);
|
||||
@ -242,7 +251,7 @@ public class Connection {
|
||||
if (timeout > MAX_RESEND_DELAY)
|
||||
timeout = MAX_RESEND_DELAY;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Resend in " + timeout + " for " + packet);
|
||||
_log.debug("Resend in " + timeout + " for " + packet, new Exception("Sent by"));
|
||||
|
||||
SimpleTimer.getInstance().addEvent(new ResendPacketEvent(packet), timeout);
|
||||
}
|
||||
@ -383,6 +392,7 @@ public class Connection {
|
||||
public boolean getIsConnected() { return _connected; }
|
||||
public boolean getHardDisconnected() { return _hardDisconnected; }
|
||||
public boolean getResetSent() { return _resetSent; }
|
||||
public long getResetSentOn() { return _resetSentOn; }
|
||||
|
||||
void disconnect(boolean cleanDisconnect) {
|
||||
disconnect(cleanDisconnect, true);
|
||||
@ -688,6 +698,14 @@ public class Connection {
|
||||
default:
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Closing connection due to inactivity");
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
StringBuffer buf = new StringBuffer(128);
|
||||
buf.append("last sent was: ").append(_context.clock().now() - _lastSendTime);
|
||||
buf.append("ms ago, last received was: ").append(_context.clock().now()-_lastReceivedOn);
|
||||
buf.append("ms ago, inactivity timeout is: ").append(_options.getInactivityTimeout());
|
||||
_log.debug(buf.toString());
|
||||
}
|
||||
|
||||
disconnect(true);
|
||||
break;
|
||||
}
|
||||
@ -752,10 +770,8 @@ public class Connection {
|
||||
*/
|
||||
private class ResendPacketEvent implements SimpleTimer.TimedEvent {
|
||||
private PacketLocal _packet;
|
||||
private boolean _currentIsActiveResend;
|
||||
public ResendPacketEvent(PacketLocal packet) {
|
||||
_packet = packet;
|
||||
_currentIsActiveResend = false;
|
||||
packet.setResendPacketEvent(ResendPacketEvent.this);
|
||||
}
|
||||
|
||||
@ -763,7 +779,7 @@ public class Connection {
|
||||
if (_packet.getAckTime() > 0)
|
||||
return;
|
||||
|
||||
if (!_connected) {
|
||||
if (_resetSent || _resetReceived) {
|
||||
_packet.cancelled();
|
||||
return;
|
||||
}
|
||||
@ -771,12 +787,15 @@ public class Connection {
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Resend period reached for " + _packet);
|
||||
boolean resend = false;
|
||||
boolean isLowest = false;
|
||||
synchronized (_outboundPackets) {
|
||||
if (_packet.getSequenceNum() == _highestAckedThrough + 1)
|
||||
isLowest = true;
|
||||
if (_outboundPackets.containsKey(new Long(_packet.getSequenceNum())))
|
||||
resend = true;
|
||||
}
|
||||
if ( (resend) && (_packet.getAckTime() < 0) ) {
|
||||
if ( (_activeResends > 0) && (!_currentIsActiveResend) ) {
|
||||
if (!isLowest) {
|
||||
// we want to resend this packet, but there are already active
|
||||
// resends in the air and we dont want to make a bad situation
|
||||
// worse. wait another second
|
||||
@ -808,7 +827,6 @@ public class Connection {
|
||||
if (numSends == 2) {
|
||||
// first resend for this packet
|
||||
_activeResends++;
|
||||
_currentIsActiveResend = true;
|
||||
}
|
||||
|
||||
// in case things really suck, the other side may have lost thier
|
||||
@ -828,6 +846,17 @@ public class Connection {
|
||||
+ (_context.clock().now() - _packet.getCreatedOn()) + "ms)");
|
||||
_outboundQueue.enqueue(_packet);
|
||||
|
||||
_lastSendTime = _context.clock().now();
|
||||
|
||||
// acked during resending (... or somethin')
|
||||
if ( (_packet.getAckTime() > 0) && (_packet.getNumSends() > 1) ) {
|
||||
_activeResends--;
|
||||
synchronized (_outboundPackets) {
|
||||
_outboundPackets.notifyAll();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (numSends > _options.getMaxResends()) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Too many resends");
|
||||
|
@ -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);
|
||||
|
@ -56,7 +56,7 @@ public class ConnectionPacketHandler {
|
||||
long ready = con.getInputStream().getHighestReadyBockId();
|
||||
int available = con.getOptions().getInboundBufferSize() - con.getInputStream().getTotalReadySize();
|
||||
int allowedBlocks = available/con.getOptions().getMaxMessageSize();
|
||||
if (packet.getSequenceNum() > ready + allowedBlocks) {
|
||||
if ( (packet.getPayloadSize() > 0) && (packet.getSequenceNum() > ready + allowedBlocks) ) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Inbound buffer exceeded on connection " + con + " ("
|
||||
+ ready + "/"+ (ready+allowedBlocks) + "/" + available
|
||||
@ -106,14 +106,11 @@ public class ConnectionPacketHandler {
|
||||
con.incrementDupMessagesReceived(1);
|
||||
|
||||
// take note of congestion
|
||||
//con.getOptions().setResendDelay(con.getOptions().getResendDelay()*2);
|
||||
//con.getOptions().setWindowSize(con.getOptions().getWindowSize()/2);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("congestion.. dup " + packet);
|
||||
SimpleTimer.getInstance().addEvent(new AckDup(con), con.getOptions().getSendAckDelay());
|
||||
//con.incrementUnackedPacketsReceived();
|
||||
//con.setNextSendTime(_context.clock().now() + con.getOptions().getSendAckDelay());
|
||||
fastAck = true;
|
||||
//fastAck = true;
|
||||
} else {
|
||||
if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) {
|
||||
//con.incrementUnackedPacketsReceived();
|
||||
@ -128,7 +125,7 @@ public class ConnectionPacketHandler {
|
||||
fastAck = fastAck || ack(con, packet.getAckThrough(), packet.getNacks(), packet, isNew);
|
||||
con.eventOccurred();
|
||||
if (fastAck) {
|
||||
if (con.getLastSendTime() + 1000 < _context.clock().now()) {
|
||||
if (con.getLastSendTime() + 2000 < _context.clock().now()) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Fast ack for dup " + packet);
|
||||
con.ackImmediately();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 &&
|
||||
|
@ -34,6 +34,8 @@ class SchedulerHardDisconnected extends SchedulerImpl {
|
||||
public boolean accept(Connection con) {
|
||||
if (con == null) return false;
|
||||
long timeSinceClose = _context.clock().now() - con.getCloseSentOn();
|
||||
if (con.getResetSent())
|
||||
timeSinceClose = _context.clock().now() - con.getResetSentOn();
|
||||
boolean ok = (con.getHardDisconnected() || con.getResetSent()) &&
|
||||
(timeSinceClose < Connection.DISCONNECT_TIMEOUT);
|
||||
return ok;
|
||||
|
@ -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" />
|
||||
|
@ -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");
|
||||
|
103
history.txt
103
history.txt
@ -1,4 +1,105 @@
|
||||
$Id: history.txt,v 1.110 2004/12/16 05:21:24 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
|
||||
|
||||
|
17
hosts.txt
17
hosts.txt
@ -1,6 +1,13 @@
|
||||
; TC's hosts.txt guaranteed freshness
|
||||
; $Id: hosts.txt,v 1.96 2004/12/14 23:23:16 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
|
||||
@ -136,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
|
||||
@ -153,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
|
||||
@ -266,4 +271,10 @@ fedo.i2p=pklxM9~hpluoCoiPgzMBpTHXMpwEdCMMNEatVc4gnQDOXsKkW5FbUMQ9y0vj6EUs2vUZLkw
|
||||
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)
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
<info>
|
||||
<appname>i2p</appname>
|
||||
<appversion>0.4.2.4</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
|
||||
|
@ -42,9 +42,10 @@ tunnel.2.startOnLoad=false
|
||||
# local eepserver
|
||||
tunnel.3.name=eepsite
|
||||
tunnel.3.description=My eepsite
|
||||
tunnel.3.type=server
|
||||
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=127.0.0.1
|
||||
tunnel.3.i2cpPort=7654
|
||||
|
@ -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) {
|
||||
|
@ -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";
|
||||
|
@ -50,7 +50,7 @@ class RouterThrottleImpl implements RouterThrottle {
|
||||
|
||||
public boolean acceptNetworkMessage() {
|
||||
long lag = _context.jobQueue().getMaxLag();
|
||||
if (lag > JOB_LAG_LIMIT) {
|
||||
if ( (lag > JOB_LAG_LIMIT) && (_context.router().getUptime() > 60*1000) ) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Throttling network reader, as the job lag is " + lag);
|
||||
_context.statManager().addRateData("router.throttleNetworkCause", lag, lag);
|
||||
@ -110,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))
|
||||
@ -209,6 +194,14 @@ 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);
|
||||
|
@ -15,8 +15,8 @@ import net.i2p.CoreVersion;
|
||||
*
|
||||
*/
|
||||
public class RouterVersion {
|
||||
public final static String ID = "$Revision: 1.115 $ $Date: 2004/12/16 05:21:23 $";
|
||||
public final static String VERSION = "0.4.2.4";
|
||||
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);
|
||||
|
@ -115,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.
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,7 +51,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
|
||||
_manager.restart();
|
||||
}
|
||||
|
||||
public int countActivePeers() { return _manager.countActivePeers(); }
|
||||
public int countActivePeers() { return (_manager == null ? 0 : _manager.countActivePeers()); }
|
||||
|
||||
public List getBids(OutNetMessage msg) {
|
||||
return _manager.getBids(msg);
|
||||
|
@ -4,7 +4,9 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Date;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.i2np.I2NPMessage;
|
||||
import net.i2p.data.i2np.DeliveryStatusMessage;
|
||||
import net.i2p.router.OutNetMessage;
|
||||
@ -12,6 +14,7 @@ import net.i2p.router.Router;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
|
||||
/**
|
||||
* Push out I2NPMessages across the wire
|
||||
@ -24,6 +27,8 @@ class ConnectionRunner implements Runnable {
|
||||
private boolean _keepRunning;
|
||||
private byte _writeBuffer[];
|
||||
private long _lastTimeSend;
|
||||
private long _lastWriteEnd;
|
||||
private long _lastWriteBegin;
|
||||
|
||||
private static final long TIME_SEND_FREQUENCY = 60*1000;
|
||||
|
||||
@ -32,6 +37,8 @@ class ConnectionRunner implements Runnable {
|
||||
_log = ctx.logManager().getLog(ConnectionRunner.class);
|
||||
_con = con;
|
||||
_keepRunning = false;
|
||||
_lastWriteBegin = ctx.clock().now();
|
||||
_lastWriteEnd = _lastWriteBegin;
|
||||
}
|
||||
|
||||
public void startRunning() {
|
||||
@ -44,6 +51,9 @@ class ConnectionRunner implements Runnable {
|
||||
+ _con.getRemoteRouterIdentity().calculateHash().toBase64().substring(0,6);
|
||||
I2PThread t = new I2PThread(this, name);
|
||||
t.start();
|
||||
|
||||
long delay = TIME_SEND_FREQUENCY + _context.random().nextInt(60*1000);
|
||||
SimpleTimer.getInstance().addEvent(new KeepaliveEvent(), delay);
|
||||
}
|
||||
|
||||
public void stopRunning() {
|
||||
@ -54,7 +64,7 @@ class ConnectionRunner implements Runnable {
|
||||
while (_keepRunning && !_con.getIsClosed()) {
|
||||
OutNetMessage msg = _con.getNextMessage();
|
||||
if (msg == null) {
|
||||
if (_keepRunning)
|
||||
if (_keepRunning && !_con.getIsClosed())
|
||||
_log.error("next message is null but we should keep running?");
|
||||
_con.closeConnection();
|
||||
return;
|
||||
@ -98,23 +108,21 @@ class ConnectionRunner implements Runnable {
|
||||
|
||||
OutputStream out = _con.getOutputStream();
|
||||
boolean ok = false;
|
||||
long before = -1;
|
||||
long after = -1;
|
||||
try {
|
||||
synchronized (out) {
|
||||
before = _context.clock().now();
|
||||
_lastWriteBegin = _context.clock().now();
|
||||
out.write(buf, 0, written);
|
||||
if (sendTime) {
|
||||
out.write(buildTimeMessage().toByteArray());
|
||||
_lastTimeSend = _context.clock().now();
|
||||
}
|
||||
out.flush();
|
||||
after = _context.clock().now();
|
||||
_lastWriteEnd = _context.clock().now();
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Just sent message " + msg.getMessageId() + " to "
|
||||
+ msg.getTarget().getIdentity().getHash().toBase64().substring(0,6)
|
||||
+ " writeTime = " + (after-before) +"ms"
|
||||
+ " writeTime = " + (_lastWriteEnd - _lastWriteBegin) +"ms"
|
||||
+ " lifetime = " + msg.getLifetime() + "ms");
|
||||
|
||||
ok = true;
|
||||
@ -123,7 +131,7 @@ class ConnectionRunner implements Runnable {
|
||||
_log.warn("Error writing out the message", ioe);
|
||||
_con.closeConnection();
|
||||
}
|
||||
_con.sent(msg, ok, after - before);
|
||||
_con.sent(msg, ok, _lastWriteEnd - _lastWriteBegin);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -139,4 +147,54 @@ class ConnectionRunner implements Runnable {
|
||||
tm.setUniqueId(0);
|
||||
return tm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Every few minutes, send a (tiny) message to the peer if we haven't
|
||||
* spoken with them recently. This will help kill off any hung
|
||||
* connections (due to IP address changes, etc). If we don't get any
|
||||
* messages through in 5 minutes, kill the connection as well.
|
||||
*
|
||||
*/
|
||||
private class KeepaliveEvent implements SimpleTimer.TimedEvent {
|
||||
public void timeReached() {
|
||||
if (!_keepRunning) return;
|
||||
if (_con.getIsClosed()) return;
|
||||
long now = _context.clock().now();
|
||||
long timeSinceWrite = now - _lastWriteEnd;
|
||||
long timeSinceWriteBegin = now - _lastWriteBegin;
|
||||
if (timeSinceWrite > 5*TIME_SEND_FREQUENCY) {
|
||||
TCPTransport t = _con.getTransport();
|
||||
String msg = "Connection closed with "
|
||||
+ _con.getRemoteRouterIdentity().getHash().toBase64().substring(0,6)
|
||||
+ " due to " + DataHelper.formatDuration(timeSinceWrite)
|
||||
+ " of inactivity after "
|
||||
+ DataHelper.formatDuration(_con.getLifetime());
|
||||
if (_lastWriteBegin > _lastWriteEnd)
|
||||
msg = msg + " with a message being written for " +
|
||||
DataHelper.formatDuration(timeSinceWriteBegin);
|
||||
t.addConnectionErrorMessage(msg);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(msg);
|
||||
_con.closeConnection(false);
|
||||
return;
|
||||
}
|
||||
if (_lastTimeSend < _context.clock().now() - 2*TIME_SEND_FREQUENCY)
|
||||
enqueueTimeMessage();
|
||||
long delay = 2*TIME_SEND_FREQUENCY + _context.random().nextInt((int)TIME_SEND_FREQUENCY);
|
||||
SimpleTimer.getInstance().addEvent(KeepaliveEvent.this, delay);
|
||||
}
|
||||
}
|
||||
|
||||
private void enqueueTimeMessage() {
|
||||
OutNetMessage msg = new OutNetMessage(_context);
|
||||
RouterInfo ri = _context.netDb().lookupRouterInfoLocally(_con.getRemoteRouterIdentity().getHash());
|
||||
if (ri == null) return;
|
||||
msg.setTarget(ri);
|
||||
msg.setExpiration(_context.clock().now() + TIME_SEND_FREQUENCY);
|
||||
msg.setMessage(buildTimeMessage());
|
||||
msg.setPriority(100);
|
||||
_con.addMessage(msg);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Enqueueing time message to " + _con.getRemoteRouterIdentity().getHash().toBase64().substring(0,6));
|
||||
}
|
||||
}
|
||||
|
@ -170,4 +170,12 @@ public class TCPAddress {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean equals(RouterAddress addr) {
|
||||
if (addr == null) return false;
|
||||
Properties opts = addr.getOptions();
|
||||
if (opts == null) return false;
|
||||
return ( (_host.equals(opts.getProperty(PROP_HOST))) &&
|
||||
(Integer.toString(_port).equals(opts.getProperty(PROP_PORT))) );
|
||||
}
|
||||
}
|
||||
|
@ -86,6 +86,7 @@ public class TCPConnection {
|
||||
/** skew that the other peer has from our clock */
|
||||
public long getOffsetReceived() { return _offsetReceived; }
|
||||
public void setOffsetReceived(long ms) { _offsetReceived = ms; }
|
||||
public TCPTransport getTransport() { return _transport; }
|
||||
|
||||
/**
|
||||
* Actually start processing the messages on the connection (and reading
|
||||
@ -110,7 +111,8 @@ public class TCPConnection {
|
||||
* be called multiple times safely.
|
||||
*
|
||||
*/
|
||||
public synchronized void closeConnection() {
|
||||
public synchronized void closeConnection() { closeConnection(true); }
|
||||
public synchronized void closeConnection(boolean wasError) {
|
||||
if (_log.shouldLog(Log.INFO)) {
|
||||
if (_ident != null)
|
||||
_log.info("Connection between " + _ident.getHash().toBase64().substring(0,6)
|
||||
@ -139,10 +141,12 @@ public class TCPConnection {
|
||||
msg.timestamp("closeConnection");
|
||||
_transport.afterSend(msg, false, true, -1);
|
||||
}
|
||||
_context.profileManager().commErrorOccurred(_ident.getHash());
|
||||
_transport.addConnectionErrorMessage("Connection closed with "
|
||||
+ _ident.getHash().toBase64().substring(0,6)
|
||||
+ " after " + DataHelper.formatDuration(getLifetime()));
|
||||
if (wasError) {
|
||||
_context.profileManager().commErrorOccurred(_ident.getHash());
|
||||
_transport.addConnectionErrorMessage("Connection closed with "
|
||||
+ _ident.getHash().toBase64().substring(0,6)
|
||||
+ " after " + DataHelper.formatDuration(getLifetime()));
|
||||
}
|
||||
_transport.connectionClosed(this);
|
||||
}
|
||||
|
||||
@ -168,10 +172,16 @@ public class TCPConnection {
|
||||
msg.timestamp("TCPConnection.addMessage");
|
||||
List expired = null;
|
||||
int remaining = 0;
|
||||
long remainingSize = 0;
|
||||
synchronized (_pendingMessages) {
|
||||
_pendingMessages.add(msg);
|
||||
expired = locked_expireOld();
|
||||
locked_throttle();
|
||||
for (int i = 0; i < _pendingMessages.size(); i++) {
|
||||
OutNetMessage cur = (OutNetMessage)_pendingMessages.get(i);
|
||||
remaining++;
|
||||
remainingSize += cur.getMessageSize();
|
||||
}
|
||||
remaining = _pendingMessages.size();
|
||||
_pendingMessages.notifyAll();
|
||||
}
|
||||
@ -182,8 +192,8 @@ public class TCPConnection {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Message " + cur.getMessageId() + " expired on the queue to "
|
||||
+ _ident.getHash().toBase64().substring(0,6)
|
||||
+ " (queue size " + remaining + ") with lifetime "
|
||||
+ cur.getLifetime());
|
||||
+ " (queue size " + remaining + "/" + remainingSize + ") with lifetime "
|
||||
+ cur.getLifetime() + " and size " + cur.getMessageSize());
|
||||
sent(cur, false, 0);
|
||||
}
|
||||
}
|
||||
|
@ -132,8 +132,7 @@ public class TCPTransport extends TransportImpl {
|
||||
if (addr == null)
|
||||
return null;
|
||||
|
||||
TCPAddress tcpAddr = new TCPAddress(addr);
|
||||
if ( (_myAddress != null) && (tcpAddr.equals(_myAddress)) )
|
||||
if ( (_myAddress != null) && (_myAddress.equals(addr)) )
|
||||
return null; // dont talk to yourself
|
||||
|
||||
TransportBid bid = new TransportBid();
|
||||
|
@ -51,6 +51,8 @@ class ClientLeaseSetManagerJob extends JobImpl {
|
||||
_pool = pool;
|
||||
_currentLeaseSet = null;
|
||||
_lastCreated = -1;
|
||||
context.statManager().createRateStat("client.leaseSetExpired", "How long ago did our leaseSet expire?", "ClientMessages", new long[] { 60*60*1000l, 24*60*60*1000l });
|
||||
|
||||
}
|
||||
|
||||
public void forceRequestLease() {
|
||||
@ -106,6 +108,13 @@ class ClientLeaseSetManagerJob extends JobImpl {
|
||||
_log.warn("Insufficient safe inbound tunnels exist for the client (" + available
|
||||
+ " available, " + _pool.getClientSettings().getNumInboundTunnels()
|
||||
+ " required) - no leaseSet requested");
|
||||
if (_currentLeaseSet != null) {
|
||||
long howOld = getContext().clock().now() - _currentLeaseSet.getEarliestLeaseDate();
|
||||
if (howOld > 0) {
|
||||
// expired
|
||||
getContext().statManager().addRateData("client.leaseSetExpired", howOld, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
requeue(RECHECK_DELAY);
|
||||
}
|
||||
|
@ -68,6 +68,8 @@ class TunnelPool {
|
||||
_context.statManager().createRateStat("tunnel.outboundMessagesProcessed", "How many messages does an inbound tunnel process in its lifetime?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
_context.statManager().createRateStat("tunnel.participatingMessagesProcessed", "How many messages does an inbound tunnel process in its lifetime?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
_context.statManager().createRateStat("tunnel.participatingMessagesProcessedActive", "How many messages beyond the average were processed in a more-than-average tunnel's lifetime?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
_context.statManager().createRateStat("tunnel.participatingBytesProcessed", "How many bytes does an inbound tunnel process in its lifetime?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
_context.statManager().createRateStat("tunnel.participatingBytesProcessedActive", "How many bytes beyond the average were processed in a more-than-average tunnel's lifetime?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
|
||||
|
||||
_isLive = true;
|
||||
_persistenceHelper = new TunnelPoolPersistenceHelper(_context);
|
||||
@ -557,7 +559,7 @@ class TunnelPool {
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Tunnel " + id + " marked as not ready, since it /failed/", new Exception("Failed tunnel"));
|
||||
_log.warn("Tunnel " + id + " marked as not ready, since it /failed/: " + info.toString(), new Exception("Failed tunnel"));
|
||||
_context.messageHistory().tunnelFailed(info.getTunnelId());
|
||||
info.setIsReady(false);
|
||||
Hash us = _context.routerHash();
|
||||
@ -662,6 +664,11 @@ class TunnelPool {
|
||||
numMsgs-lastAvg,
|
||||
info.getSettings().getExpiration() -
|
||||
info.getSettings().getCreated());
|
||||
long numBytes = info.getBytesProcessed();
|
||||
lastAvg = (long)_context.statManager().getRate("tunnel.participatingBytesProcessed").getRate(10*60*1000l).getAverageValue();
|
||||
_context.statManager().addRateData("tunnel.participatingBytesProcessed", numBytes, numMsgs);
|
||||
if (numBytes > lastAvg)
|
||||
_context.statManager().addRateData("tunnel.participatingBytesProcessedActive", numBytes-lastAvg, numMsgs);
|
||||
break;
|
||||
case TunnelId.TYPE_UNSPECIFIED:
|
||||
default:
|
||||
|
Reference in New Issue
Block a user