forked from I2P_Developers/i2p.i2p
Compare commits
74 Commits
i2p_0_6_1_
...
0.6.1.30-2
Author | SHA1 | Date | |
---|---|---|---|
c69fda2298 | |||
cabb22331b | |||
5b3aca29a8 | |||
f35cbf59d8 | |||
a96119d09b | |||
2711294aee | |||
f838b1828b | |||
fbf6282c1a | |||
5195a5c1fc | |||
62b18b18b5 | |||
7c8f519b35 | |||
d6fb979616 | |||
f568d21969 | |||
7fe9d590f5 | |||
0a1240ebfd | |||
4e68f2a157 | |||
e9bd6907d1 | |||
b20495c39f | |||
0ecbc4c27b | |||
4d5b1d4c3f | |||
17b719f3f7 | |||
979a3e98d8 | |||
c6a1112f0a | |||
4ebcc95d9f | |||
7e59ce27fa | |||
22345a264e | |||
03739996da | |||
819a72d4f6 | |||
e480931e20 | |||
3f01d0a69e | |||
f67f47f0cd | |||
5ad6ee60eb | |||
5accba6cdc | |||
313e1704df | |||
cf4d2b17c9 | |||
9145eedc35 | |||
b772179077 | |||
9054a196ce | |||
d28a96ac7d | |||
9c73f80ac3 | |||
20c46cff04 | |||
f332513755 | |||
cb69a66498 | |||
1c66543938 | |||
53ab3c472e | |||
a4b221fa71 | |||
e3e1d0842d | |||
99b5bf9609 | |||
da10fe0df7 | |||
9fd5ba7b2d | |||
05b5df9d76 | |||
5c1dc79767 | |||
4acd2996c5 | |||
16fa6a89bc | |||
2a72e8574b | |||
d4a1bcf28f | |||
409b71def5 | |||
2dc5fbda02 | |||
71aaf03d09 | |||
30c99e630b | |||
445b39171a | |||
571c2d6047 | |||
42ff763933 | |||
727edc3ff9 | |||
82a4758a0a | |||
915914ebb3 | |||
c438b56378 | |||
307ccfb1b4 | |||
34e23259b4 | |||
036802d66a | |||
6a7dbc8e3a | |||
cf4a9ffc27 | |||
6ef4adf318 | |||
f84c9bf3b1 |
@ -17,6 +17,10 @@
|
||||
# Contains the addresses from your master address book
|
||||
# and your subscribed address books. (../userhosts.txt)
|
||||
#
|
||||
# private_addressbook The path to the private address book used by the router.
|
||||
# This is used only by the router and SusiDNS.
|
||||
# It is not published by addressbook. (../privatehosts.txt)
|
||||
#
|
||||
# published_addressbook The path to the copy of your address book made
|
||||
# available on i2p. (../eepsite/docroot/hosts.txt)
|
||||
#
|
||||
@ -35,6 +39,7 @@ proxy_host=localhost
|
||||
proxy_port=4444
|
||||
master_addressbook=myhosts.txt
|
||||
router_addressbook=../userhosts.txt
|
||||
private_addressbook=../privatehosts.txt
|
||||
published_addressbook=../eepsite/docroot/hosts.txt
|
||||
log=log.txt
|
||||
subscriptions=subscriptions.txt
|
||||
|
@ -66,6 +66,7 @@ public class AddressBook {
|
||||
* where key is a human readable name, and value is a base64 i2p
|
||||
* destination.
|
||||
*/
|
||||
/* unused
|
||||
public AddressBook(String url, String proxyHost, int proxyPort) {
|
||||
this.location = url;
|
||||
EepGet get = new EepGet(I2PAppContext.getGlobalContext(), true,
|
||||
@ -79,22 +80,26 @@ public class AddressBook {
|
||||
}
|
||||
new File("addressbook.tmp").delete();
|
||||
}
|
||||
|
||||
*/
|
||||
/**
|
||||
* 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.
|
||||
* Set a maximum size of the remote book to make it a little harder for a malicious book-sender.
|
||||
*
|
||||
* @param subscription
|
||||
* A Subscription instance pointing at a remote address book.
|
||||
*/
|
||||
static final long MAX_SUB_SIZE = 3 * 1024 * 1024l; //about 5,000 hosts
|
||||
public AddressBook(Subscription subscription, String proxyHost, int proxyPort) {
|
||||
this.location = subscription.getLocation();
|
||||
EepGet get = new EepGet(I2PAppContext.getGlobalContext(), true,
|
||||
proxyHost, proxyPort, 0, "addressbook.tmp",
|
||||
subscription.getLocation(), true, subscription.getEtag());
|
||||
get.fetch();
|
||||
subscription.setEtag(get.getETag());
|
||||
proxyHost, proxyPort, 0, -1l, MAX_SUB_SIZE, "addressbook.tmp", null,
|
||||
subscription.getLocation(), true, subscription.getEtag(), subscription.getLastModified(), null);
|
||||
if (get.fetch()) {
|
||||
subscription.setEtag(get.getETag());
|
||||
subscription.setLastModified(get.getLastModified());
|
||||
}
|
||||
try {
|
||||
this.addresses = ConfigParser.parse(new File("addressbook.tmp"));
|
||||
} catch (IOException exp) {
|
||||
@ -151,6 +156,27 @@ public class AddressBook {
|
||||
return this.addresses.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do basic validation of the hostname and dest
|
||||
* hostname was already converted to lower case by ConfigParser.parse()
|
||||
*/
|
||||
private static boolean valid(String host, String dest) {
|
||||
return
|
||||
host.endsWith(".i2p") &&
|
||||
host.length() > 4 &&
|
||||
host.length() <= 67 && // 63 + ".i2p"
|
||||
(! host.startsWith(".")) &&
|
||||
(! host.startsWith("-")) &&
|
||||
(! host.endsWith("-.i2p")) &&
|
||||
host.indexOf("..") < 0 &&
|
||||
host.replaceAll("[a-z0-9.-]", "").length() == 0 &&
|
||||
|
||||
dest.length() == 516 &&
|
||||
dest.endsWith("AAAA") &&
|
||||
dest.replaceAll("[a-zA-Z0-9~-]", "").length() == 0
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this AddressBook with AddressBook other, writing messages about new
|
||||
* addresses or conflicts to log. Addresses in AddressBook other that are
|
||||
@ -169,7 +195,7 @@ public class AddressBook {
|
||||
String otherKey = (String) otherIter.next();
|
||||
String otherValue = (String) other.addresses.get(otherKey);
|
||||
|
||||
if (otherKey.endsWith(".i2p") && otherValue.length() >= 516) {
|
||||
if (valid(otherKey, otherValue)) {
|
||||
if (this.addresses.containsKey(otherKey) && !overwrite) {
|
||||
if (!this.addresses.get(otherKey).equals(otherValue)
|
||||
&& log != null) {
|
||||
@ -184,7 +210,7 @@ public class AddressBook {
|
||||
this.modified = true;
|
||||
if (log != null) {
|
||||
log.append("New address " + otherKey
|
||||
+ " added to address book.");
|
||||
+ " added to address book. From: " + other.location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ public class ConfigParser {
|
||||
* 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.
|
||||
* The key is converted to lower case.
|
||||
*
|
||||
* @param input
|
||||
* A BufferedReader with lines in key=value format to parse into
|
||||
@ -77,7 +78,7 @@ public class ConfigParser {
|
||||
inputLine = ConfigParser.stripComments(inputLine);
|
||||
String[] splitLine = inputLine.split("=");
|
||||
if (splitLine.length == 2) {
|
||||
result.put(splitLine[0].trim(), splitLine[1].trim());
|
||||
result.put(splitLine[0].trim().toLowerCase(), splitLine[1].trim());
|
||||
}
|
||||
inputLine = input.readLine();
|
||||
}
|
||||
@ -301,4 +302,4 @@ public class ConfigParser {
|
||||
new FileWriter(file, false)));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ public class BitField
|
||||
|
||||
private final byte[] bitfield;
|
||||
private final int size;
|
||||
private int count;
|
||||
|
||||
/**
|
||||
* Creates a new BitField that represents <code>size</code> unset bits.
|
||||
@ -41,6 +42,7 @@ public class BitField
|
||||
this.size = size;
|
||||
int arraysize = ((size-1)/8)+1;
|
||||
bitfield = new byte[arraysize];
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,6 +62,11 @@ public class BitField
|
||||
// XXX - More correct would be to check that unused bits are
|
||||
// cleared or clear them explicitly ourselves.
|
||||
System.arraycopy(bitfield, 0, this.bitfield, 0, arraysize);
|
||||
|
||||
this.count = 0;
|
||||
for (int i = 0; i < size; i++)
|
||||
if (get(i))
|
||||
this.count++;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,7 +102,10 @@ public class BitField
|
||||
throw new IndexOutOfBoundsException(Integer.toString(bit));
|
||||
int index = bit/8;
|
||||
int mask = 128 >> (bit % 8);
|
||||
bitfield[index] |= mask;
|
||||
if ((bitfield[index] & mask) == 0) {
|
||||
count++;
|
||||
bitfield[index] |= mask;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -114,6 +124,22 @@ public class BitField
|
||||
return (bitfield[index] & mask) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of set bits.
|
||||
*/
|
||||
public int count()
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if all bits are set.
|
||||
*/
|
||||
public boolean complete()
|
||||
{
|
||||
return count >= size;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
// Not very efficient
|
||||
@ -129,4 +155,5 @@ public class BitField
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -101,10 +101,12 @@ public class I2PSnarkUtil {
|
||||
}
|
||||
if (opts.getProperty("inbound.nickname") == null)
|
||||
opts.setProperty("inbound.nickname", "I2PSnark");
|
||||
if (opts.getProperty("outbound.nickname") == null)
|
||||
opts.setProperty("outbound.nickname", "I2PSnark");
|
||||
if (opts.getProperty("i2p.streaming.inactivityTimeout") == null)
|
||||
opts.setProperty("i2p.streaming.inactivityTimeout", "90000");
|
||||
opts.setProperty("i2p.streaming.inactivityTimeout", "240000");
|
||||
if (opts.getProperty("i2p.streaming.inactivityAction") == null)
|
||||
opts.setProperty("i2p.streaming.inactivityAction", "2"); // 1 == disconnect, 2 == ping
|
||||
opts.setProperty("i2p.streaming.inactivityAction", "1"); // 1 == disconnect, 2 == ping
|
||||
if (opts.getProperty("i2p.streaming.initialWindowSize") == null)
|
||||
opts.setProperty("i2p.streaming.initialWindowSize", "1");
|
||||
if (opts.getProperty("i2p.streaming.slowStartGrowthRateFactor") == null)
|
||||
|
@ -54,8 +54,8 @@ public class Peer implements Comparable
|
||||
private boolean deregister = true;
|
||||
private static long __id;
|
||||
private long _id;
|
||||
final static long CHECK_PERIOD = 40*1000; // 40 seconds
|
||||
final static int RATE_DEPTH = 6; // make following arrays RATE_DEPTH long
|
||||
final static long CHECK_PERIOD = PeerCoordinator.CHECK_PERIOD; // 40 seconds
|
||||
final static int RATE_DEPTH = PeerCoordinator.RATE_DEPTH; // make following arrays RATE_DEPTH long
|
||||
private long uploaded_old[] = {-1,-1,-1,-1,-1,-1};
|
||||
private long downloaded_old[] = {-1,-1,-1,-1,-1,-1};
|
||||
|
||||
@ -461,10 +461,12 @@ public class Peer implements Comparable
|
||||
public long getInactiveTime() {
|
||||
PeerState s = state;
|
||||
if (s != null) {
|
||||
PeerConnectionIn in = s.in;
|
||||
PeerConnectionOut out = s.out;
|
||||
if (out != null)
|
||||
return System.currentTimeMillis() - out.lastSent;
|
||||
else
|
||||
if (in != null && out != null) {
|
||||
long now = System.currentTimeMillis();
|
||||
return Math.max(now - out.lastSent, now - in.lastRcvd);
|
||||
} else
|
||||
return -1; //"state, no out";
|
||||
} else {
|
||||
return -1; //"no state";
|
||||
@ -493,33 +495,24 @@ public class Peer implements Comparable
|
||||
|
||||
/**
|
||||
* Return how much the peer has
|
||||
* Quite inefficient - a byte lookup table or counter in Bitfield would be much better
|
||||
*/
|
||||
public int completed()
|
||||
{
|
||||
PeerState s = state;
|
||||
if (s == null || s.bitfield == null)
|
||||
return 0;
|
||||
int count = 0;
|
||||
for (int i = 0; i < s.bitfield.size(); i++)
|
||||
if (s.bitfield.get(i))
|
||||
count++;
|
||||
return count;
|
||||
return s.bitfield.count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if a peer is a seeder
|
||||
* Quite inefficient - a byte lookup table or counter in Bitfield would be much better
|
||||
*/
|
||||
public boolean isCompleted()
|
||||
{
|
||||
PeerState s = state;
|
||||
if (s == null || s.bitfield == null)
|
||||
return false;
|
||||
for (int i = 0; i < s.bitfield.size(); i++)
|
||||
if (!s.bitfield.get(i))
|
||||
return false;
|
||||
return true;
|
||||
return s.bitfield.complete();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -95,17 +95,14 @@ class PeerCheckerTask extends TimerTask
|
||||
peer.setRateHistory(upload, download);
|
||||
peer.resetCounters();
|
||||
|
||||
if (Snark.debug >= Snark.DEBUG)
|
||||
{
|
||||
Snark.debug(peer + ":", Snark.DEBUG);
|
||||
Snark.debug(" ul: " + upload/KILOPERSECOND
|
||||
+ " dl: " + download/KILOPERSECOND
|
||||
+ " i: " + peer.isInterested()
|
||||
+ " I: " + peer.isInteresting()
|
||||
+ " c: " + peer.isChoking()
|
||||
+ " C: " + peer.isChoked(),
|
||||
Snark.DEBUG);
|
||||
}
|
||||
Snark.debug(peer + ":", Snark.DEBUG);
|
||||
Snark.debug(" ul: " + upload/KILOPERSECOND
|
||||
+ " dl: " + download/KILOPERSECOND
|
||||
+ " i: " + peer.isInterested()
|
||||
+ " I: " + peer.isInteresting()
|
||||
+ " c: " + peer.isChoking()
|
||||
+ " C: " + peer.isChoked(),
|
||||
Snark.DEBUG);
|
||||
|
||||
// If we are at our max uploaders and we have lots of other
|
||||
// interested peers try to make some room.
|
||||
@ -118,9 +115,8 @@ class PeerCheckerTask extends TimerTask
|
||||
// Check if it still wants pieces from us.
|
||||
if (!peer.isInterested())
|
||||
{
|
||||
if (Snark.debug >= Snark.INFO)
|
||||
Snark.debug("Choke uninterested peer: " + peer,
|
||||
Snark.INFO);
|
||||
Snark.debug("Choke uninterested peer: " + peer,
|
||||
Snark.INFO);
|
||||
peer.setChoking(true);
|
||||
uploaders--;
|
||||
coordinator.uploaders--;
|
||||
@ -132,8 +128,7 @@ class PeerCheckerTask extends TimerTask
|
||||
else if (peer.isInteresting() && peer.isChoked())
|
||||
{
|
||||
// If they are choking us make someone else a downloader
|
||||
if (Snark.debug >= Snark.DEBUG)
|
||||
Snark.debug("Choke choking peer: " + peer, Snark.DEBUG);
|
||||
Snark.debug("Choke choking peer: " + peer, Snark.DEBUG);
|
||||
peer.setChoking(true);
|
||||
uploaders--;
|
||||
coordinator.uploaders--;
|
||||
@ -146,8 +141,7 @@ class PeerCheckerTask extends TimerTask
|
||||
else if (!peer.isInteresting() && !coordinator.completed())
|
||||
{
|
||||
// If they aren't interesting make someone else a downloader
|
||||
if (Snark.debug >= Snark.DEBUG)
|
||||
Snark.debug("Choke uninteresting peer: " + peer, Snark.DEBUG);
|
||||
Snark.debug("Choke uninteresting peer: " + peer, Snark.DEBUG);
|
||||
peer.setChoking(true);
|
||||
uploaders--;
|
||||
coordinator.uploaders--;
|
||||
@ -162,9 +156,8 @@ class PeerCheckerTask extends TimerTask
|
||||
&& download == 0)
|
||||
{
|
||||
// We are downloading but didn't receive anything...
|
||||
if (Snark.debug >= Snark.DEBUG)
|
||||
Snark.debug("Choke downloader that doesn't deliver:"
|
||||
+ peer, Snark.DEBUG);
|
||||
Snark.debug("Choke downloader that doesn't deliver:"
|
||||
+ peer, Snark.DEBUG);
|
||||
peer.setChoking(true);
|
||||
uploaders--;
|
||||
coordinator.uploaders--;
|
||||
@ -202,9 +195,8 @@ class PeerCheckerTask extends TimerTask
|
||||
|| uploaders > uploadLimit)
|
||||
&& worstDownloader != null)
|
||||
{
|
||||
if (Snark.debug >= Snark.DEBUG)
|
||||
Snark.debug("Choke worst downloader: " + worstDownloader,
|
||||
Snark.DEBUG);
|
||||
Snark.debug("Choke worst downloader: " + worstDownloader,
|
||||
Snark.DEBUG);
|
||||
|
||||
worstDownloader.setChoking(true);
|
||||
coordinator.uploaders--;
|
||||
|
@ -35,10 +35,13 @@ class PeerConnectionIn implements Runnable
|
||||
private Thread thread;
|
||||
private volatile boolean quit;
|
||||
|
||||
long lastRcvd;
|
||||
|
||||
public PeerConnectionIn(Peer peer, DataInputStream din)
|
||||
{
|
||||
this.peer = peer;
|
||||
this.din = din;
|
||||
lastRcvd = System.currentTimeMillis();
|
||||
quit = false;
|
||||
}
|
||||
|
||||
@ -76,6 +79,7 @@ class PeerConnectionIn implements Runnable
|
||||
// Wait till we hear something...
|
||||
// The length of a complete message in bytes.
|
||||
int i = din.readInt();
|
||||
lastRcvd = System.currentTimeMillis();
|
||||
if (i < 0)
|
||||
throw new IOException("Unexpected length prefix: " + i);
|
||||
|
||||
|
@ -237,6 +237,8 @@ public class PeerCoordinator implements PeerListener
|
||||
peer.disconnect();
|
||||
removePeerFromPieces(peer);
|
||||
}
|
||||
// delete any saved orphan partial piece
|
||||
savedRequest = null;
|
||||
}
|
||||
|
||||
public void connected(Peer peer)
|
||||
@ -251,8 +253,8 @@ public class PeerCoordinator implements PeerListener
|
||||
synchronized(peers)
|
||||
{
|
||||
Peer old = peerIDInList(peer.getPeerID(), peers);
|
||||
if ( (old != null) && (old.getInactiveTime() > 4*60*1000) ) {
|
||||
// idle for 4 minutes, kill the old con (64KB/4min = 273B/sec minimum for one block)
|
||||
if ( (old != null) && (old.getInactiveTime() > 8*60*1000) ) {
|
||||
// idle for 8 minutes, kill the old con (32KB/8min = 68B/sec minimum for one block)
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Remomving old peer: " + peer + ": " + old + ", inactive for " + old.getInactiveTime());
|
||||
peers.remove(old);
|
||||
@ -263,6 +265,7 @@ public class PeerCoordinator implements PeerListener
|
||||
{
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Already connected to: " + peer + ": " + old + ", inactive for " + old.getInactiveTime());
|
||||
// toDisconnect = peer to get out of synchronized(peers)
|
||||
peer.disconnect(false); // Don't deregister this connection/peer.
|
||||
}
|
||||
else
|
||||
@ -310,6 +313,9 @@ public class PeerCoordinator implements PeerListener
|
||||
synchronized(peers)
|
||||
{
|
||||
need_more = !peer.isConnected() && peers.size() < MAX_CONNECTIONS;
|
||||
// Check if we already have this peer before we build the connection
|
||||
Peer old = peerIDInList(peer.getPeerID(), peers);
|
||||
need_more = need_more && ((old == null) || (old.getInactiveTime() > 8*60*1000));
|
||||
}
|
||||
|
||||
if (need_more)
|
||||
@ -357,7 +363,6 @@ public class PeerCoordinator implements PeerListener
|
||||
while (it.hasNext())
|
||||
{
|
||||
Peer peer = (Peer)it.next();
|
||||
boolean remove = false;
|
||||
if (peer.isChoking() && peer.isInterested())
|
||||
{
|
||||
count++;
|
||||
@ -691,6 +696,8 @@ public class PeerCoordinator implements PeerListener
|
||||
private long savedRequestTime = 0;
|
||||
public void savePeerPartial(PeerState state)
|
||||
{
|
||||
if (halted)
|
||||
return;
|
||||
Request req = state.getPartialRequest();
|
||||
if (req == null)
|
||||
return;
|
||||
@ -791,7 +798,7 @@ public class PeerCoordinator implements PeerListener
|
||||
*/
|
||||
public void markUnrequested(Peer peer)
|
||||
{
|
||||
if (peer.state == null)
|
||||
if (halted || peer.state == null)
|
||||
return;
|
||||
int[] arr = peer.state.getRequestedPieces();
|
||||
for (int i = 0; arr[i] >= 0; i++)
|
||||
@ -804,8 +811,8 @@ public class PeerCoordinator implements PeerListener
|
||||
public int allowedUploaders()
|
||||
{
|
||||
if (Snark.overUploadLimit(uploaders)) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Over limit, uploaders was: " + uploaders);
|
||||
// if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Over limit, uploaders was: " + uploaders);
|
||||
return uploaders - 1;
|
||||
} else if (uploaders < MAX_UPLOADERS)
|
||||
return uploaders + 1;
|
||||
|
@ -63,7 +63,7 @@ public class Snark
|
||||
/**
|
||||
* What level of debug info to show.
|
||||
*/
|
||||
public static int debug = NOTICE;
|
||||
//public static int debug = NOTICE;
|
||||
|
||||
// Whether or not to ask the user for commands while sharing
|
||||
private static boolean command_interpreter = true;
|
||||
@ -468,6 +468,8 @@ public class Snark
|
||||
pc.halt();
|
||||
Storage st = storage;
|
||||
if (st != null) {
|
||||
if (storage.changed)
|
||||
SnarkManager.instance().saveTorrentStatus(storage.getMetaInfo(), storage.getBitField());
|
||||
try {
|
||||
storage.close();
|
||||
} catch (IOException ioe) {
|
||||
@ -503,6 +505,7 @@ public class Snark
|
||||
int i = 0;
|
||||
while (i < args.length)
|
||||
{
|
||||
/*
|
||||
if (args[i].equals("--debug"))
|
||||
{
|
||||
debug = INFO;
|
||||
@ -523,7 +526,7 @@ public class Snark
|
||||
catch (NumberFormatException nfe) { }
|
||||
}
|
||||
}
|
||||
else if (args[i].equals("--port"))
|
||||
else */ if (args[i].equals("--port"))
|
||||
{
|
||||
if (args.length - 1 < i + 1)
|
||||
usage("--port needs port number to listen on");
|
||||
@ -770,7 +773,7 @@ public class Snark
|
||||
totalUploaders += c.uploaders;
|
||||
}
|
||||
int limit = I2PSnarkUtil.instance().getMaxUploaders();
|
||||
Snark.debug("Total uploaders: " + totalUploaders + " Limit: " + limit, Snark.DEBUG);
|
||||
// Snark.debug("Total uploaders: " + totalUploaders + " Limit: " + limit, Snark.DEBUG);
|
||||
return totalUploaders > limit;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package org.klomp.snark;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
@ -30,6 +31,8 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
public static final String PROP_EEP_PORT = "i2psnark.eepPort";
|
||||
public static final String PROP_UPLOADERS_TOTAL = "i2psnark.uploaders.total";
|
||||
public static final String PROP_DIR = "i2psnark.dir";
|
||||
public static final String PROP_META_PREFIX = "i2psnark.zmeta.";
|
||||
public static final String PROP_META_BITFIELD_SUFFIX = ".bitfield";
|
||||
|
||||
public static final String PROP_AUTO_START = "i2snark.autoStart";
|
||||
public static final String DEFAULT_AUTO_START = "false";
|
||||
@ -271,7 +274,9 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
|
||||
public void saveConfig() {
|
||||
try {
|
||||
DataHelper.storeProps(_config, new File(_configFile));
|
||||
synchronized (_configFile) {
|
||||
DataHelper.storeProps(_config, new File(_configFile));
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
addMessage("Unable to save the config to '" + _configFile + "'");
|
||||
}
|
||||
@ -361,7 +366,97 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timestamp for a torrent from the config file
|
||||
*/
|
||||
public long getSavedTorrentTime(MetaInfo metainfo) {
|
||||
byte[] ih = metainfo.getInfoHash();
|
||||
String infohash = Base64.encode(ih);
|
||||
infohash = infohash.replace('=', '$');
|
||||
String time = _config.getProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX);
|
||||
if (time == null)
|
||||
return 0;
|
||||
int comma = time.indexOf(',');
|
||||
if (comma <= 0)
|
||||
return 0;
|
||||
time = time.substring(0, comma);
|
||||
try { return Long.parseLong(time); } catch (NumberFormatException nfe) {}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the saved bitfield for a torrent from the config file.
|
||||
* Convert "." to a full bitfield.
|
||||
*/
|
||||
public BitField getSavedTorrentBitField(MetaInfo metainfo) {
|
||||
byte[] ih = metainfo.getInfoHash();
|
||||
String infohash = Base64.encode(ih);
|
||||
infohash = infohash.replace('=', '$');
|
||||
String bf = _config.getProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX);
|
||||
if (bf == null)
|
||||
return null;
|
||||
int comma = bf.indexOf(',');
|
||||
if (comma <= 0)
|
||||
return null;
|
||||
bf = bf.substring(comma + 1).trim();
|
||||
int len = metainfo.getPieces();
|
||||
if (bf.equals(".")) {
|
||||
BitField bitfield = new BitField(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
bitfield.set(i);
|
||||
return bitfield;
|
||||
}
|
||||
byte[] bitfield = Base64.decode(bf);
|
||||
if (bitfield == null)
|
||||
return null;
|
||||
if (bitfield.length * 8 < len)
|
||||
return null;
|
||||
return new BitField(bitfield, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the completion status of a torrent and the current time in the config file
|
||||
* in the form "i2psnark.zmeta.$base64infohash=$time,$base64bitfield".
|
||||
* The config file property key is appended with the Base64 of the infohash,
|
||||
* with the '=' changed to '$' since a key can't contain '='.
|
||||
* The time is a standard long converted to string.
|
||||
* The status is either a bitfield converted to Base64 or "." for a completed
|
||||
* torrent to save space in the config file and in memory.
|
||||
*/
|
||||
public void saveTorrentStatus(MetaInfo metainfo, BitField bitfield) {
|
||||
byte[] ih = metainfo.getInfoHash();
|
||||
String infohash = Base64.encode(ih);
|
||||
infohash = infohash.replace('=', '$');
|
||||
String now = "" + System.currentTimeMillis();
|
||||
String bfs;
|
||||
if (bitfield.complete()) {
|
||||
bfs = ".";
|
||||
} else {
|
||||
byte[] bf = bitfield.getFieldBytes();
|
||||
bfs = Base64.encode(bf);
|
||||
}
|
||||
_config.setProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX, now + "," + bfs);
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the status of a torrent from the config file.
|
||||
* This may help the config file from growing too big.
|
||||
*/
|
||||
public void removeTorrentStatus(MetaInfo metainfo) {
|
||||
byte[] ih = metainfo.getInfoHash();
|
||||
String infohash = Base64.encode(ih);
|
||||
infohash = infohash.replace('=', '$');
|
||||
_config.remove(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX);
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
private String locked_validateTorrent(MetaInfo info) throws IOException {
|
||||
String announce = info.getAnnounce();
|
||||
// basic validation of url
|
||||
if ((!announce.startsWith("http://")) ||
|
||||
(announce.indexOf(".i2p/") < 0))
|
||||
return "Non-i2p tracker in " + info.getName() + ", deleting it";
|
||||
List files = info.getFiles();
|
||||
if ( (files != null) && (files.size() > MAX_FILES_PER_TORRENT) ) {
|
||||
return "Too many files in " + info.getName() + " (" + files.size() + "), deleting it";
|
||||
@ -426,6 +521,8 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
if (torrent != null) {
|
||||
File torrentFile = new File(filename);
|
||||
torrentFile.delete();
|
||||
if (torrent.storage != null)
|
||||
removeTorrentStatus(torrent.storage.getMetaInfo());
|
||||
addMessage("Torrent removed: '" + torrentFile.getName() + "'");
|
||||
}
|
||||
}
|
||||
@ -496,25 +593,29 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
}
|
||||
|
||||
private static final String DEFAULT_TRACKERS[] = {
|
||||
"Postman's tracker", "http://YRgrgTLGnbTq2aZOZDJQ~o6Uk5k6TK-OZtx0St9pb0G-5EGYURZioxqYG8AQt~LgyyI~NCj6aYWpPO-150RcEvsfgXLR~CxkkZcVpgt6pns8SRc3Bi-QSAkXpJtloapRGcQfzTtwllokbdC-aMGpeDOjYLd8b5V9Im8wdCHYy7LRFxhEtGb~RL55DA8aYOgEXcTpr6RPPywbV~Qf3q5UK55el6Kex-6VCxreUnPEe4hmTAbqZNR7Fm0hpCiHKGoToRcygafpFqDw5frLXToYiqs9d4liyVB-BcOb0ihORbo0nS3CLmAwZGvdAP8BZ7cIYE3Z9IU9D1G8JCMxWarfKX1pix~6pIA-sp1gKlL1HhYhPMxwyxvuSqx34o3BqU7vdTYwWiLpGM~zU1~j9rHL7x60pVuYaXcFQDR4-QVy26b6Pt6BlAZoFmHhPcAuWfu-SFhjyZYsqzmEmHeYdAwa~HojSbofg0TMUgESRXMw6YThK1KXWeeJVeztGTz25sL8AAAA.i2p/announce.php"
|
||||
, "eBook Tracker", "http://E71FRom6PZNEqTN2Lr8P-sr23b7HJVC32KoGnVQjaX6zJiXwhJy2HsXob36Qmj81TYFZdewFZa9mSJ533UZgGyQkXo2ahctg82JKYZfDe5uDxAn1E9YPjxZCWJaFJh0S~UwSs~9AZ7UcauSJIoNtpxrtbmRNVFLqnkEDdLZi26TeucfOmiFmIWnVblLniWv3tG1boE9Abd-6j3FmYVrRucYuepAILYt6katmVNOk6sXmno1Eynrp~~MBuFq0Ko6~jsc2E2CRVYXDhGHEMdt-j6JUz5D7S2RIVzDRqQyAZLKJ7OdQDmI31przzmne1vOqqqLC~1xUumZVIvF~yOeJUGNjJ1Vx0J8i2BQIusn1pQJ6UCB~ZtZZLQtEb8EPVCfpeRi2ri1M5CyOuxN0V5ekmPHrYIBNevuTCRC26NP7ZS5VDgx1~NaC3A-CzJAE6f1QXi0wMI9aywNG5KGzOPifcsih8eyGyytvgLtrZtV7ykzYpPCS-rDfITncpn5hliPUAAAA.i2p/pub/bt/announce.php"
|
||||
, "Gaytorrents Tracker", "http://uxPWHbK1OIj9HxquaXuhMiIvi21iK0~ZiG9d8G0840ZXIg0r6CbiV71xlsqmdnU6wm0T2LySriM0doW2gUigo-5BNkUquHwOjLROiETnB3ZR0Ml4IGa6QBPn1aAq2d9~g1r1nVjLE~pcFnXB~cNNS7kIhX1d6nLgYVZf0C2cZopEow2iWVUggGGnAA9mHjE86zLEnTvAyhbAMTqDQJhEuLa0ZYSORqzJDMkQt90MV4YMjX1ICY6RfUSFmxEqu0yWTrkHsTtRw48l~dz9wpIgc0a0T9C~eeWvmBFTqlJPtQZwntpNeH~jF7nlYzB58olgV2HHFYpVYD87DYNzTnmNWxCJ5AfDorm6AIUCV2qaE7tZtI1h6fbmGpGlPyW~Kw5GXrRfJwNvr6ajwAVi~bPVnrBwDZezHkfW4slOO8FACPR28EQvaTu9nwhAbqESxV2hCTq6vQSGjuxHeOuzBOEvRWkLKOHWTC09t2DbJ94FSqETmZopTB1ukEmaxRWbKSIaAAAA.i2p/announce.php"
|
||||
, "NickyB Tracker", "http://9On6d3cZ27JjwYCtyJJbowe054d5tFnfMjv4PHsYs-EQn4Y4mk2zRixatvuAyXz2MmRfXG-NAUfhKr0KCxRNZbvHmlckYfT-WBzwwpiMAl0wDFY~Pl8cqXuhfikSG5WrqdPfDNNIBuuznS0dqaczf~OyVaoEOpvuP3qV6wKqbSSLpjOwwAaQPHjlRtNIW8-EtUZp-I0LT45HSoowp~6b7zYmpIyoATvIP~sT0g0MTrczWhbVTUZnEkZeLhOR0Duw1-IRXI2KHPbA24wLO9LdpKKUXed05RTz0QklW5ROgR6TYv7aXFufX8kC0-DaKvQ5JKG~h8lcoHvm1RCzNqVE-2aiZnO2xH08H-iCWoLNJE-Td2kT-Tsc~3QdQcnEUcL5BF-VT~QYRld2--9r0gfGl-yDrJZrlrihHGr5J7ImahelNn9PpkVp6eIyABRmJHf2iicrk3CtjeG1j9OgTSwaNmEpUpn4aN7Kx0zNLdH7z6uTgCGD9Kmh1MFYrsoNlTp4AAAA.i2p/bittorrent/announce.php"
|
||||
, "Orion's tracker", "http://gKik1lMlRmuroXVGTZ~7v4Vez3L3ZSpddrGZBrxVriosCQf7iHu6CIk8t15BKsj~P0JJpxrofeuxtm7SCUAJEr0AIYSYw8XOmp35UfcRPQWyb1LsxUkMT4WqxAT3s1ClIICWlBu5An~q-Mm0VFlrYLIPBWlUFnfPR7jZ9uP5ZMSzTKSMYUWao3ejiykr~mtEmyls6g-ZbgKZawa9II4zjOy-hdxHgP-eXMDseFsrym4Gpxvy~3Fv9TuiSqhpgm~UeTo5YBfxn6~TahKtE~~sdCiSydqmKBhxAQ7uT9lda7xt96SS09OYMsIWxLeQUWhns-C~FjJPp1D~IuTrUpAFcVEGVL-BRMmdWbfOJEcWPZ~CBCQSO~VkuN1ebvIOr9JBerFMZSxZtFl8JwcrjCIBxeKPBmfh~xYh16BJm1BBBmN1fp2DKmZ2jBNkAmnUbjQOqWvUcehrykWk5lZbE7bjJMDFH48v3SXwRuDBiHZmSbsTY6zhGY~GkMQHNGxPMMSIAAAA.i2p/bt/announce.php"
|
||||
"Postman", "http://YRgrgTLGnbTq2aZOZDJQ~o6Uk5k6TK-OZtx0St9pb0G-5EGYURZioxqYG8AQt~LgyyI~NCj6aYWpPO-150RcEvsfgXLR~CxkkZcVpgt6pns8SRc3Bi-QSAkXpJtloapRGcQfzTtwllokbdC-aMGpeDOjYLd8b5V9Im8wdCHYy7LRFxhEtGb~RL55DA8aYOgEXcTpr6RPPywbV~Qf3q5UK55el6Kex-6VCxreUnPEe4hmTAbqZNR7Fm0hpCiHKGoToRcygafpFqDw5frLXToYiqs9d4liyVB-BcOb0ihORbo0nS3CLmAwZGvdAP8BZ7cIYE3Z9IU9D1G8JCMxWarfKX1pix~6pIA-sp1gKlL1HhYhPMxwyxvuSqx34o3BqU7vdTYwWiLpGM~zU1~j9rHL7x60pVuYaXcFQDR4-QVy26b6Pt6BlAZoFmHhPcAuWfu-SFhjyZYsqzmEmHeYdAwa~HojSbofg0TMUgESRXMw6YThK1KXWeeJVeztGTz25sL8AAAA.i2p/announce.php=http://tracker.postman.i2p/"
|
||||
, "eBook", "http://E71FRom6PZNEqTN2Lr8P-sr23b7HJVC32KoGnVQjaX6zJiXwhJy2HsXob36Qmj81TYFZdewFZa9mSJ533UZgGyQkXo2ahctg82JKYZfDe5uDxAn1E9YPjxZCWJaFJh0S~UwSs~9AZ7UcauSJIoNtpxrtbmRNVFLqnkEDdLZi26TeucfOmiFmIWnVblLniWv3tG1boE9Abd-6j3FmYVrRucYuepAILYt6katmVNOk6sXmno1Eynrp~~MBuFq0Ko6~jsc2E2CRVYXDhGHEMdt-j6JUz5D7S2RIVzDRqQyAZLKJ7OdQDmI31przzmne1vOqqqLC~1xUumZVIvF~yOeJUGNjJ1Vx0J8i2BQIusn1pQJ6UCB~ZtZZLQtEb8EPVCfpeRi2ri1M5CyOuxN0V5ekmPHrYIBNevuTCRC26NP7ZS5VDgx1~NaC3A-CzJAE6f1QXi0wMI9aywNG5KGzOPifcsih8eyGyytvgLtrZtV7ykzYpPCS-rDfITncpn5hliPUAAAA.i2p/pub/bt/announce.php=http://de-ebook-archiv.i2p/pub/bt/"
|
||||
, "Gaytorrents", "http://uxPWHbK1OIj9HxquaXuhMiIvi21iK0~ZiG9d8G0840ZXIg0r6CbiV71xlsqmdnU6wm0T2LySriM0doW2gUigo-5BNkUquHwOjLROiETnB3ZR0Ml4IGa6QBPn1aAq2d9~g1r1nVjLE~pcFnXB~cNNS7kIhX1d6nLgYVZf0C2cZopEow2iWVUggGGnAA9mHjE86zLEnTvAyhbAMTqDQJhEuLa0ZYSORqzJDMkQt90MV4YMjX1ICY6RfUSFmxEqu0yWTrkHsTtRw48l~dz9wpIgc0a0T9C~eeWvmBFTqlJPtQZwntpNeH~jF7nlYzB58olgV2HHFYpVYD87DYNzTnmNWxCJ5AfDorm6AIUCV2qaE7tZtI1h6fbmGpGlPyW~Kw5GXrRfJwNvr6ajwAVi~bPVnrBwDZezHkfW4slOO8FACPR28EQvaTu9nwhAbqESxV2hCTq6vQSGjuxHeOuzBOEvRWkLKOHWTC09t2DbJ94FSqETmZopTB1ukEmaxRWbKSIaAAAA.i2p/announce.php=http://gaytorrents.i2p/"
|
||||
, "NickyB", "http://9On6d3cZ27JjwYCtyJJbowe054d5tFnfMjv4PHsYs-EQn4Y4mk2zRixatvuAyXz2MmRfXG-NAUfhKr0KCxRNZbvHmlckYfT-WBzwwpiMAl0wDFY~Pl8cqXuhfikSG5WrqdPfDNNIBuuznS0dqaczf~OyVaoEOpvuP3qV6wKqbSSLpjOwwAaQPHjlRtNIW8-EtUZp-I0LT45HSoowp~6b7zYmpIyoATvIP~sT0g0MTrczWhbVTUZnEkZeLhOR0Duw1-IRXI2KHPbA24wLO9LdpKKUXed05RTz0QklW5ROgR6TYv7aXFufX8kC0-DaKvQ5JKG~h8lcoHvm1RCzNqVE-2aiZnO2xH08H-iCWoLNJE-Td2kT-Tsc~3QdQcnEUcL5BF-VT~QYRld2--9r0gfGl-yDrJZrlrihHGr5J7ImahelNn9PpkVp6eIyABRmJHf2iicrk3CtjeG1j9OgTSwaNmEpUpn4aN7Kx0zNLdH7z6uTgCGD9Kmh1MFYrsoNlTp4AAAA.i2p/bittorrent/announce.php=http://nickyb.i2p/bittorrent/"
|
||||
, "Orion", "http://gKik1lMlRmuroXVGTZ~7v4Vez3L3ZSpddrGZBrxVriosCQf7iHu6CIk8t15BKsj~P0JJpxrofeuxtm7SCUAJEr0AIYSYw8XOmp35UfcRPQWyb1LsxUkMT4WqxAT3s1ClIICWlBu5An~q-Mm0VFlrYLIPBWlUFnfPR7jZ9uP5ZMSzTKSMYUWao3ejiykr~mtEmyls6g-ZbgKZawa9II4zjOy-hdxHgP-eXMDseFsrym4Gpxvy~3Fv9TuiSqhpgm~UeTo5YBfxn6~TahKtE~~sdCiSydqmKBhxAQ7uT9lda7xt96SS09OYMsIWxLeQUWhns-C~FjJPp1D~IuTrUpAFcVEGVL-BRMmdWbfOJEcWPZ~CBCQSO~VkuN1ebvIOr9JBerFMZSxZtFl8JwcrjCIBxeKPBmfh~xYh16BJm1BBBmN1fp2DKmZ2jBNkAmnUbjQOqWvUcehrykWk5lZbE7bjJMDFH48v3SXwRuDBiHZmSbsTY6zhGY~GkMQHNGxPMMSIAAAA.i2p/bt/announce.php=http://orion.i2p/bt/"
|
||||
// , "anonymity", "http://8EoJZIKrWgGuDrxA3nRJs1jsPfiGwmFWL91hBrf0HA7oKhEvAna4Ocx47VLUR9retVEYBAyWFK-eZTPcvhnz9XffBEiJQQ~kFSCqb1fV6IfPiV3HySqi9U5Caf6~hC46fRd~vYnxmaBLICT3N160cxBETqH3v2rdxdJpvYt8q4nMk9LUeVXq7zqCTFLLG5ig1uKgNzBGe58iNcsvTEYlnbYcE930ABmrzj8G1qQSgSwJ6wx3tUQNl1z~4wSOUMan~raZQD60lRK70GISjoX0-D0Po9WmPveN3ES3g72TIET3zc3WPdK2~lgmKGIs8GgNLES1cXTolvbPhdZK1gxddRMbJl6Y6IPFyQ9o4-6Rt3Lp-RMRWZ2TG7j2OMcNSiOmATUhKEFBDfv-~SODDyopGBmfeLw16F4NnYednvn4qP10dyMHcUASU6Zag4mfc2-WivrOqeWhD16fVAh8MoDpIIT~0r9XmwdaVFyLcjbXObabJczxCAW3fodQUnvuSkwzAAAA.i2p/anonymityTracker/announce.php=http://anonymityweb.i2p/anonymityTracker/"
|
||||
// , "The freak's tracker", "http://mHKva9x24E5Ygfey2llR1KyQHv5f8hhMpDMwJDg1U-hABpJ2NrQJd6azirdfaR0OKt4jDlmP2o4Qx0H598~AteyD~RJU~xcWYdcOE0dmJ2e9Y8-HY51ie0B1yD9FtIV72ZI-V3TzFDcs6nkdX9b81DwrAwwFzx0EfNvK1GLVWl59Ow85muoRTBA1q8SsZImxdyZ-TApTVlMYIQbdI4iQRwU9OmmtefrCe~ZOf4UBS9-KvNIqUL0XeBSqm0OU1jq-D10Ykg6KfqvuPnBYT1BYHFDQJXW5DdPKwcaQE4MtAdSGmj1epDoaEBUa9btQlFsM2l9Cyn1hzxqNWXELmx8dRlomQLlV4b586dRzW~fLlOPIGC13ntPXogvYvHVyEyptXkv890jC7DZNHyxZd5cyrKC36r9huKvhQAmNABT2Y~pOGwVrb~RpPwT0tBuPZ3lHYhBFYmD8y~AOhhNHKMLzea1rfwTvovBMByDdFps54gMN1mX4MbCGT4w70vIopS9yAAAA.i2p/bytemonsoon/announce.php"
|
||||
};
|
||||
|
||||
/** comma delimited list of name=announceURL for the trackers to be displayed */
|
||||
/** comma delimited list of name=announceURL=baseURL for the trackers to be displayed */
|
||||
public static final String PROP_TRACKERS = "i2psnark.trackers";
|
||||
/** unordered map of announceURL to name */
|
||||
private static Map trackerMap = null;
|
||||
/** sorted map of name to announceURL=baseURL */
|
||||
public Map getTrackers() {
|
||||
HashMap rv = new HashMap();
|
||||
if (trackerMap != null) // only do this once, can't be updated while running
|
||||
return trackerMap;
|
||||
Map rv = new TreeMap();
|
||||
String trackers = _config.getProperty(PROP_TRACKERS);
|
||||
if ( (trackers == null) || (trackers.trim().length() <= 0) )
|
||||
trackers = _context.getProperty(PROP_TRACKERS);
|
||||
if ( (trackers == null) || (trackers.trim().length() <= 0) ) {
|
||||
for (int i = 0; i < DEFAULT_TRACKERS.length; i += 2)
|
||||
rv.put(DEFAULT_TRACKERS[i+1], DEFAULT_TRACKERS[i]);
|
||||
rv.put(DEFAULT_TRACKERS[i], DEFAULT_TRACKERS[i+1]);
|
||||
} else {
|
||||
StringTokenizer tok = new StringTokenizer(trackers, ",");
|
||||
while (tok.hasMoreTokens()) {
|
||||
@ -525,11 +626,12 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
String name = pair.substring(0, split).trim();
|
||||
String url = pair.substring(split+1).trim();
|
||||
if ( (name.length() > 0) && (url.length() > 0) )
|
||||
rv.put(url, name);
|
||||
rv.put(name, url);
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
trackerMap = rv;
|
||||
return trackerMap;
|
||||
}
|
||||
|
||||
private static class TorrentFilenameFilter implements FilenameFilter {
|
||||
|
@ -45,6 +45,7 @@ public class Storage
|
||||
// XXX - Not always set correctly
|
||||
int piece_size;
|
||||
int pieces;
|
||||
boolean changed;
|
||||
|
||||
/** The default piece size. */
|
||||
private static int MIN_PIECE_SIZE = 256*1024;
|
||||
@ -116,7 +117,8 @@ public class Storage
|
||||
}
|
||||
|
||||
String name = baseFile.getName();
|
||||
if (files.size() == 1)
|
||||
if (files.size() == 1) // FIXME: ...and if base file not a directory or should this be the only check?
|
||||
// this makes a bad metainfo if the directory has only one file in it
|
||||
{
|
||||
files = null;
|
||||
lengthsList = null;
|
||||
@ -285,6 +287,10 @@ public class Storage
|
||||
public void check(String rootDir) throws IOException
|
||||
{
|
||||
File base = new File(rootDir, filterName(metainfo.getName()));
|
||||
// look for saved bitfield and timestamp in the config file
|
||||
long savedTime = SnarkManager.instance().getSavedTorrentTime(metainfo);
|
||||
BitField savedBitField = SnarkManager.instance().getSavedTorrentBitField(metainfo);
|
||||
boolean useSavedBitField = savedTime > 0 && savedBitField != null;
|
||||
|
||||
List files = metainfo.getFiles();
|
||||
if (files == null)
|
||||
@ -298,6 +304,11 @@ public class Storage
|
||||
rafs = new RandomAccessFile[1];
|
||||
names = new String[1];
|
||||
lengths[0] = metainfo.getTotalLength();
|
||||
if (useSavedBitField) {
|
||||
long lm = base.lastModified();
|
||||
if (lm <= 0 || lm > savedTime)
|
||||
useSavedBitField = false;
|
||||
}
|
||||
if (base.exists() && !base.canWrite()) // hope we can get away with this, if we are only seeding...
|
||||
rafs[0] = new RandomAccessFile(base, "r");
|
||||
else
|
||||
@ -322,6 +333,11 @@ public class Storage
|
||||
File f = createFileFromNames(base, (List)files.get(i));
|
||||
lengths[i] = ((Long)ls.get(i)).longValue();
|
||||
total += lengths[i];
|
||||
if (useSavedBitField) {
|
||||
long lm = base.lastModified();
|
||||
if (lm <= 0 || lm > savedTime)
|
||||
useSavedBitField = false;
|
||||
}
|
||||
if (f.exists() && !f.canWrite()) // see above re: only seeding
|
||||
rafs[i] = new RandomAccessFile(f, "r");
|
||||
else
|
||||
@ -335,7 +351,14 @@ public class Storage
|
||||
throw new IOException("File lengths do not add up "
|
||||
+ total + " != " + metalength);
|
||||
}
|
||||
checkCreateFiles();
|
||||
if (useSavedBitField) {
|
||||
bitfield = savedBitField;
|
||||
needed = metainfo.getPieces() - bitfield.count();
|
||||
Snark.debug("Found saved state and files unchanged, skipping check", Snark.NOTICE);
|
||||
} else {
|
||||
checkCreateFiles();
|
||||
SnarkManager.instance().saveTorrentStatus(metainfo, bitfield);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -354,7 +377,7 @@ public class Storage
|
||||
if (!base.exists())
|
||||
throw new IOException("Could not reopen file " + base);
|
||||
|
||||
if (!base.canWrite()) // hope we can get away with this, if we are only seeding...
|
||||
if (complete() || !base.canWrite()) // hope we can get away with this, if we are only seeding...
|
||||
rafs[0] = new RandomAccessFile(base, "r");
|
||||
else
|
||||
rafs[0] = new RandomAccessFile(base, "rw");
|
||||
@ -372,7 +395,7 @@ public class Storage
|
||||
File f = getFileFromNames(base, (List)files.get(i));
|
||||
if (!f.exists())
|
||||
throw new IOException("Could not reopen file " + f);
|
||||
if (!f.canWrite()) // see above re: only seeding
|
||||
if (complete() || !f.canWrite()) // see above re: only seeding
|
||||
rafs[i] = new RandomAccessFile(f, "r");
|
||||
else
|
||||
rafs[i] = new RandomAccessFile(f, "rw");
|
||||
@ -384,7 +407,7 @@ public class Storage
|
||||
/**
|
||||
* Removes 'suspicious' characters from the give file name.
|
||||
*/
|
||||
private String filterName(String name)
|
||||
private static String filterName(String name)
|
||||
{
|
||||
// XXX - Is this enough?
|
||||
return name.replace(File.separatorChar, '_');
|
||||
@ -416,7 +439,7 @@ public class Storage
|
||||
return f;
|
||||
}
|
||||
|
||||
private File getFileFromNames(File base, List names) throws IOException
|
||||
public static File getFileFromNames(File base, List names)
|
||||
{
|
||||
Iterator it = names.iterator();
|
||||
while (it.hasNext())
|
||||
@ -517,6 +540,7 @@ public class Storage
|
||||
// gobble gobble
|
||||
}
|
||||
}
|
||||
changed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -604,6 +628,7 @@ public class Storage
|
||||
}
|
||||
}
|
||||
|
||||
changed = true;
|
||||
if (complete) {
|
||||
// listener.storageCompleted(this);
|
||||
// do we also need to close all of the files and reopen
|
||||
@ -623,6 +648,8 @@ public class Storage
|
||||
listener.setWantedPieces(this);
|
||||
Snark.debug("WARNING: Not really done, missing " + needed
|
||||
+ " pieces", Snark.WARNING);
|
||||
} else {
|
||||
SnarkManager.instance().saveTorrentStatus(metainfo, bitfield);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@ public class TrackerClient extends I2PThread
|
||||
private static final String STARTED_EVENT = "started";
|
||||
private static final String COMPLETED_EVENT = "completed";
|
||||
private static final String STOPPED_EVENT = "stopped";
|
||||
private static final String NOT_REGISTERED = "torrent not registered"; //bytemonsoon
|
||||
|
||||
private final static int SLEEP = 5; // 5 minutes.
|
||||
private final static int DELAY_MIN = 2000; // 2 secs.
|
||||
@ -151,6 +152,10 @@ public class TrackerClient extends I2PThread
|
||||
("WARNING: Could not contact tracker at '"
|
||||
+ announce + "': " + ioe, Snark.WARNING);
|
||||
coordinator.trackerProblems = ioe.getMessage();
|
||||
if (coordinator.trackerProblems.toLowerCase().startsWith(NOT_REGISTERED)) {
|
||||
stop = true;
|
||||
coordinator.snark.stopTorrent();
|
||||
}
|
||||
}
|
||||
|
||||
if (stop)
|
||||
@ -254,6 +259,10 @@ public class TrackerClient extends I2PThread
|
||||
("WARNING: Could not contact tracker at '"
|
||||
+ announce + "': " + ioe, Snark.WARNING);
|
||||
coordinator.trackerProblems = ioe.getMessage();
|
||||
if (coordinator.trackerProblems.toLowerCase().startsWith(NOT_REGISTERED)) {
|
||||
stop = true;
|
||||
coordinator.snark.stopTorrent();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -291,13 +300,13 @@ public class TrackerClient extends I2PThread
|
||||
+ "&downloaded=" + downloaded
|
||||
+ "&left=" + left
|
||||
+ ((event != NO_EVENT) ? ("&event=" + event) : "");
|
||||
if (Snark.debug >= Snark.INFO)
|
||||
Snark.debug("Sending TrackerClient request: " + s, Snark.INFO);
|
||||
Snark.debug("Sending TrackerClient request: " + s, Snark.INFO);
|
||||
|
||||
File fetched = I2PSnarkUtil.instance().get(s);
|
||||
if (fetched == null) {
|
||||
throw new IOException("Error fetching " + s);
|
||||
}
|
||||
fetched.deleteOnExit();
|
||||
|
||||
InputStream in = null;
|
||||
try {
|
||||
@ -305,8 +314,7 @@ public class TrackerClient extends I2PThread
|
||||
|
||||
TrackerInfo info = new TrackerInfo(in, coordinator.getID(),
|
||||
coordinator.getMetaInfo());
|
||||
if (Snark.debug >= Snark.INFO)
|
||||
Snark.debug("TrackerClient response: " + info, Snark.INFO);
|
||||
Snark.debug("TrackerClient response: " + info, Snark.INFO);
|
||||
lastRequestTime = System.currentTimeMillis();
|
||||
|
||||
String failure = info.getFailureReason();
|
||||
@ -325,7 +333,7 @@ public class TrackerClient extends I2PThread
|
||||
* Very lazy byte[] to URL encoder. Just encodes everything, even
|
||||
* "normal" chars.
|
||||
*/
|
||||
static String urlencode(byte[] bs)
|
||||
public static String urlencode(byte[] bs)
|
||||
{
|
||||
StringBuffer sb = new StringBuffer(bs.length*3);
|
||||
for (int i = 0; i < bs.length; i++)
|
||||
|
@ -67,14 +67,23 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
out.write("<tr><td width=\"20%\" class=\"snarkTitle\" valign=\"top\" align=\"left\">");
|
||||
out.write("I2PSnark<br />\n");
|
||||
out.write("<table border=\"0\" width=\"100%\">\n");
|
||||
out.write("<tr><td><a href=\"" + req.getRequestURI() + peerString + "\" class=\"snarkRefresh\">Refresh</a><br />\n");
|
||||
out.write("<td><a href=\"http://forum.i2p/viewforum.php?f=21\" class=\"snarkRefresh\">Forum</a><br />\n");
|
||||
out.write("<tr><td><a href=\"http://de-ebook-archiv.i2p/pub/bt/\" class=\"snarkRefresh\">eBook</a><br />\n");
|
||||
out.write("<td><a href=\"http://gaytorrents.i2p/\" class=\"snarkRefresh\">GayTorrents</a><br />\n");
|
||||
out.write("<tr><td><a href=\"http://nickyb.i2p/bittorrent/\" class=\"snarkRefresh\">NickyB</a><br />\n");
|
||||
out.write("<td><a href=\"http://orion.i2p/bt/\" class=\"snarkRefresh\">Orion</a><br />\n");
|
||||
out.write("<tr><td><a href=\"http://tracker.postman.i2p/\" class=\"snarkRefresh\">Postman</a><br />\n");
|
||||
out.write("<td> \n");
|
||||
out.write("<tr><td><a href=\"" + req.getRequestURI() + peerString + "\" class=\"snarkRefresh\">Refresh</a>\n");
|
||||
out.write("<td><a href=\"http://forum.i2p/viewforum.php?f=21\" class=\"snarkRefresh\">Forum</a>\n");
|
||||
int count = 0;
|
||||
Map trackers = _manager.getTrackers();
|
||||
for (Iterator iter = trackers.keySet().iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
String baseURL = (String)trackers.get(name);
|
||||
int e = baseURL.indexOf('=');
|
||||
if (e < 0)
|
||||
continue;
|
||||
baseURL = baseURL.substring(e + 1);
|
||||
if (count++ % 2 == 0)
|
||||
out.write("<tr>");
|
||||
out.write("<td><a href=\"" + baseURL + "\" class=\"snarkRefresh\">" + name + "</a>\n");
|
||||
}
|
||||
if (count % 2 == 1)
|
||||
out.write("<td> \n");
|
||||
out.write("</table>\n");
|
||||
out.write("</td><td width=\"80%\" class=\"snarkMessages\" valign=\"top\" align=\"left\"><pre>");
|
||||
List msgs = _manager.getMessages();
|
||||
@ -239,23 +248,31 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
_manager.addMessage("Torrent file deleted: " + f.getAbsolutePath());
|
||||
List files = snark.meta.getFiles();
|
||||
String dataFile = snark.meta.getName();
|
||||
for (int i = 0; files != null && i < files.size(); i++) {
|
||||
// multifile torrents have the getFiles() return lists of lists of filenames, but
|
||||
// each of those lists just contain a single file afaict...
|
||||
File df = new File(_manager.getDataDir(), files.get(i).toString());
|
||||
boolean deleted = FileUtil.rmdir(df, false);
|
||||
if (deleted)
|
||||
_manager.addMessage("Data dir deleted: " + df.getAbsolutePath());
|
||||
else
|
||||
_manager.addMessage("Data dir could not be deleted: " + df.getAbsolutePath());
|
||||
}
|
||||
if (dataFile != null) {
|
||||
f = new File(_manager.getDataDir(), dataFile);
|
||||
boolean deleted = f.delete();
|
||||
if (deleted)
|
||||
f = new File(_manager.getDataDir(), dataFile);
|
||||
if (files == null) { // single file torrent
|
||||
if (f.delete())
|
||||
_manager.addMessage("Data file deleted: " + f.getAbsolutePath());
|
||||
else
|
||||
_manager.addMessage("Data file could not be deleted: " + f.getAbsolutePath());
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < files.size(); i++) { // pass 1 delete files
|
||||
// multifile torrents have the getFiles() return lists of lists of filenames, but
|
||||
// each of those lists just contain a single file afaict...
|
||||
File df = Storage.getFileFromNames(f, (List) files.get(i));
|
||||
if (df.delete())
|
||||
_manager.addMessage("Data file deleted: " + df.getAbsolutePath());
|
||||
else
|
||||
_manager.addMessage("Data file could not be deleted: " + df.getAbsolutePath());
|
||||
}
|
||||
for (int i = files.size() - 1; i >= 0; i--) { // pass 2 delete dirs - not foolproof,
|
||||
// we could sort and do a strict bottom-up
|
||||
File df = Storage.getFileFromNames(f, (List) files.get(i));
|
||||
df = df.getParentFile();
|
||||
if (df == null || !df.exists())
|
||||
continue;
|
||||
if(df.delete())
|
||||
_manager.addMessage("Data dir deleted: " + df.getAbsolutePath());
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -288,16 +305,19 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
try {
|
||||
Storage s = new Storage(baseFile, announceURL, null);
|
||||
s.create();
|
||||
s.close(); // close the files... maybe need a way to pass this Storage to addTorrent rather than starting over
|
||||
MetaInfo info = s.getMetaInfo();
|
||||
File torrentFile = new File(baseFile.getParent(), baseFile.getName() + ".torrent");
|
||||
if (torrentFile.exists())
|
||||
throw new IOException("Cannot overwrite an existing .torrent file: " + torrentFile.getPath());
|
||||
_manager.saveTorrentStatus(info, s.getBitField()); // so addTorrent won't recheck
|
||||
// DirMonitor could grab this first, maybe hold _snarks lock?
|
||||
FileOutputStream out = new FileOutputStream(torrentFile);
|
||||
out.write(info.getTorrentData());
|
||||
out.close();
|
||||
_manager.addMessage("Torrent created for " + baseFile.getName() + ": " + torrentFile.getAbsolutePath());
|
||||
// now fire it up, but don't automatically seed it
|
||||
_manager.addTorrent(torrentFile.getCanonicalPath(), false);
|
||||
_manager.addTorrent(torrentFile.getCanonicalPath(), true);
|
||||
_manager.addMessage("Many I2P trackers require you to register new torrents before seeding - please do so before starting " + baseFile.getName());
|
||||
} catch (IOException ioe) {
|
||||
_manager.addMessage("Error creating a torrent for " + baseFile.getAbsolutePath() + ": " + ioe.getMessage());
|
||||
@ -335,7 +355,7 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
}
|
||||
|
||||
private static final int MAX_DISPLAYED_FILENAME_LENGTH = 60;
|
||||
private static final int MAX_DISPLAYED_ERROR_LENGTH = 30;
|
||||
private static final int MAX_DISPLAYED_ERROR_LENGTH = 40;
|
||||
private void displaySnark(PrintWriter out, Snark snark, String uri, int row, long stats[], boolean showPeers) throws IOException {
|
||||
String filename = snark.torrent;
|
||||
File f = new File(filename);
|
||||
@ -388,15 +408,15 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
String statusString = "Unknown";
|
||||
if (err != null) {
|
||||
if (isRunning && curPeers > 0 && !showPeers)
|
||||
statusString = "TrackerErr (" +
|
||||
statusString = "<a title=\"" + err + "\">TrackerErr</a> (" +
|
||||
curPeers + "/" + knownPeers +
|
||||
" <a href=\"" + uri + "?p=" + Base64.encode(snark.meta.getInfoHash()) + "\">peers</a>)";
|
||||
else if (isRunning)
|
||||
statusString = "TrackerErr (" + curPeers + "/" + knownPeers + " peers)";
|
||||
statusString = "<a title=\"" + err + "\">TrackerErr (" + curPeers + "/" + knownPeers + " peers)";
|
||||
else {
|
||||
if (err.length() > MAX_DISPLAYED_ERROR_LENGTH)
|
||||
err = err.substring(0, MAX_DISPLAYED_ERROR_LENGTH) + "...";
|
||||
statusString = "TrackerErr (" + err + ")";
|
||||
statusString = "TrackerErr<br />(" + err + ")";
|
||||
}
|
||||
} else if (remaining <= 0) {
|
||||
if (isRunning && curPeers > 0 && !showPeers)
|
||||
@ -438,6 +458,25 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
out.write(filename);
|
||||
if (remaining == 0)
|
||||
out.write("</a>");
|
||||
// temporarily hardcoded for postman and anonymity, requires bytemonsoon patch for lookup by info_hash
|
||||
String announce = snark.meta.getAnnounce();
|
||||
if (announce.startsWith("http://YRgrgTLG") || announce.startsWith("http://8EoJZIKr")) {
|
||||
Map trackers = _manager.getTrackers();
|
||||
for (Iterator iter = trackers.keySet().iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
String baseURL = (String)trackers.get(name);
|
||||
if (!baseURL.startsWith(announce))
|
||||
continue;
|
||||
int e = baseURL.indexOf('=');
|
||||
if (e < 0)
|
||||
continue;
|
||||
baseURL = baseURL.substring(e + 1);
|
||||
out.write(" (<a href=\"" + baseURL + "details.php?dllist=1&filelist=1&info_hash=");
|
||||
out.write(TrackerClient.urlencode(snark.meta.getInfoHash()));
|
||||
out.write("\" title=\"" + name + " Tracker\">Details</a>)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
out.write("</td>\n\t");
|
||||
|
||||
out.write("<td valign=\"top\" align=\"right\" class=\"snarkTorrentETA " + rowClass + "\">");
|
||||
@ -500,7 +539,7 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
client = "Azureus";
|
||||
else
|
||||
client = "Unknown";
|
||||
out.write("<font size=-1>" + client + "</font> <tt>" + peer.toString().substring(5, 9) + "</tt>");
|
||||
out.write("<font size=-1>" + client + "</font> <tt>" + peer.toString().substring(5, 9) + "</tt>");
|
||||
out.write("</td>\n\t");
|
||||
out.write("<td class=\"snarkTorrentStatus " + rowClass + "\">");
|
||||
out.write("</td>\n\t");
|
||||
@ -519,20 +558,32 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
out.write("</td>\n\t");
|
||||
out.write("<td valign=\"top\" align=\"right\" class=\"snarkTorrentStatus " + rowClass + "\">");
|
||||
if (remaining > 0) {
|
||||
if (peer.isInteresting() && !peer.isChoked())
|
||||
if (peer.isInteresting() && !peer.isChoked()) {
|
||||
out.write("<font color=#008000>");
|
||||
else
|
||||
out.write("<font color=#a00000>");
|
||||
out.write("<font size=-1>" + formatSize(peer.getDownloadRate()) + "ps</font></font>");
|
||||
out.write("<font size=-1>" + formatSize(peer.getDownloadRate()) + "ps</font></font>");
|
||||
} else {
|
||||
out.write("<font color=#a00000><font size=-1><a title=\"");
|
||||
if (!peer.isInteresting())
|
||||
out.write("Uninteresting\">");
|
||||
else
|
||||
out.write("Choked\">");
|
||||
out.write(formatSize(peer.getDownloadRate()) + "ps</a></font></font>");
|
||||
}
|
||||
}
|
||||
out.write("</td>\n\t");
|
||||
out.write("<td valign=\"top\" align=\"right\" class=\"snarkTorrentStatus " + rowClass + "\">");
|
||||
if (pct != 100.0) {
|
||||
if (peer.isInterested() && !peer.isChoking())
|
||||
if (peer.isInterested() && !peer.isChoking()) {
|
||||
out.write("<font color=#008000>");
|
||||
else
|
||||
out.write("<font color=#a00000>");
|
||||
out.write("<font size=-1>" + formatSize(peer.getUploadRate()) + "ps</font></font>");
|
||||
out.write("<font size=-1>" + formatSize(peer.getUploadRate()) + "ps</font></font>");
|
||||
} else {
|
||||
out.write("<font color=#a00000><font size=-1><a title=\"");
|
||||
if (!peer.isInterested())
|
||||
out.write("Uninterested\">");
|
||||
else
|
||||
out.write("Choking\">");
|
||||
out.write(formatSize(peer.getUploadRate()) + "ps</a></font></font>");
|
||||
}
|
||||
}
|
||||
out.write("</td>\n\t");
|
||||
out.write("<td class=\"snarkTorrentStatus " + rowClass + "\">");
|
||||
@ -578,12 +629,14 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
+ "<input type=\"text\" name=\"baseFile\" size=\"20\" value=\"" + baseFile
|
||||
+ "\" title=\"File to seed (must be within the specified path)\" /><br />\n");
|
||||
out.write("Tracker: <select name=\"announceURL\"><option value=\"\">Select a tracker</option>\n");
|
||||
Map trackers = sort(_manager.getTrackers());
|
||||
Map trackers = _manager.getTrackers();
|
||||
for (Iterator iter = trackers.keySet().iterator(); iter.hasNext(); ) {
|
||||
String name = (String)iter.next();
|
||||
String announceURL = (String)trackers.get(name);
|
||||
// we inject whitespace in sort(...) to guarantee uniqueness, but we can strip it off here
|
||||
out.write("\t<option value=\"" + announceURL + "\">" + name.trim() + "</option>\n");
|
||||
int e = announceURL.indexOf('=');
|
||||
if (e > 0)
|
||||
announceURL = announceURL.substring(0, e);
|
||||
out.write("\t<option value=\"" + announceURL + "\">" + name + "</option>\n");
|
||||
}
|
||||
out.write("</select>\n");
|
||||
out.write("or <input type=\"text\" name=\"announceURLOther\" size=\"50\" value=\"http://\" " +
|
||||
@ -592,18 +645,6 @@ public class I2PSnarkServlet extends HttpServlet {
|
||||
out.write("</form>\n</span>\n");
|
||||
}
|
||||
|
||||
private Map sort(Map trackers) {
|
||||
TreeMap rv = new TreeMap();
|
||||
for (Iterator iter = trackers.keySet().iterator(); iter.hasNext(); ) {
|
||||
String url = (String)iter.next();
|
||||
String name = (String)trackers.get(url);
|
||||
while (rv.containsKey(name))
|
||||
name = name + " ";
|
||||
rv.put(name, url);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
private void writeConfigForm(PrintWriter out, HttpServletRequest req) throws IOException {
|
||||
String uri = req.getRequestURI();
|
||||
String dataDir = _manager.getDataDir().getAbsolutePath();
|
||||
|
@ -122,6 +122,25 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
"The I2P HTTP Proxy supports http:// requests ONLY. Other protocols such as https:// and ftp:// are not allowed.<BR>")
|
||||
.getBytes();
|
||||
|
||||
private final static byte[] ERR_LOCALHOST =
|
||||
("HTTP/1.1 403 Access Denied\r\n"+
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n"+
|
||||
"Cache-control: no-cache\r\n"+
|
||||
"\r\n"+
|
||||
"<html><body><H1>I2P ERROR: REQUEST DENIED</H1>"+
|
||||
"Your browser is misconfigured. Do not use the proxy to access the router console or other localhost destinations.<BR>")
|
||||
.getBytes();
|
||||
|
||||
private final static int MAX_POSTBYTES = 20*1024*1024; // arbitrary but huge - all in memory, no temp file
|
||||
private final static byte[] ERR_MAXPOST =
|
||||
("HTTP/1.1 503 Bad POST\r\n"+
|
||||
"Content-Type: text/html; charset=iso-8859-1\r\n"+
|
||||
"Cache-control: no-cache\r\n"+
|
||||
"\r\n"+
|
||||
"<html><body><H1>I2P ERROR: REQUEST DENIED</H1>"+
|
||||
"The maximum POST size is " + MAX_POSTBYTES + " bytes.<BR>")
|
||||
.getBytes();
|
||||
|
||||
/** used to assign unique IDs to the threads / clients. no logic or functionality */
|
||||
private static volatile long __clientId = 0;
|
||||
|
||||
@ -394,6 +413,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
usingWWWProxy = true;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix(requestId) + "Host doesnt end with .i2p and it contains a period [" + host + "]: wwwProxy!");
|
||||
} else if (host.toLowerCase().startsWith("localhost:")) {
|
||||
if (out != null) {
|
||||
out.write(ERR_LOCALHOST);
|
||||
out.write("<p /><i>Generated on: ".getBytes());
|
||||
out.write(new Date().toString().getBytes());
|
||||
out.write("</i></body></html>\n".getBytes());
|
||||
out.flush();
|
||||
}
|
||||
s.close();
|
||||
return;
|
||||
} else {
|
||||
request = request.substring(pos + 1);
|
||||
pos = request.indexOf("/");
|
||||
@ -484,12 +513,25 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix(requestId) + "NewRequest header: [" + newRequest.toString() + "]");
|
||||
|
||||
int postbytes = 0;
|
||||
while (br.ready()) { // empty the buffer (POST requests)
|
||||
int i = br.read();
|
||||
if (i != -1) {
|
||||
newRequest.append((char) i);
|
||||
if (++postbytes > MAX_POSTBYTES) {
|
||||
if (out != null) {
|
||||
out.write(ERR_MAXPOST);
|
||||
out.write("<p /><i>Generated on: ".getBytes());
|
||||
out.write(new Date().toString().getBytes());
|
||||
out.write("</i></body></html>\n".getBytes());
|
||||
out.flush();
|
||||
}
|
||||
s.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (method == null || destination == null) {
|
||||
l.log("No HTTP method found in the request.");
|
||||
if (out != null) {
|
||||
@ -559,6 +601,12 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
l.log(ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
} catch (OutOfMemoryError oom) { // mainly for huge POSTs
|
||||
IOException ex = new IOException("OOM (in POST?)");
|
||||
_log.info("getPrefix(requestId) + Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
closeSocket(s);
|
||||
}
|
||||
}
|
||||
|
||||
@ -597,6 +645,12 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
}
|
||||
|
||||
private static String jumpServers[] = {
|
||||
"http://i2host.i2p/cgi-bin/i2hostjump?",
|
||||
"http://orion.i2p/jump/",
|
||||
"http://stats.i2p/cgi-bin/jump.cgi?a=",
|
||||
"http://trevorreznik.i2p/cgi-bin/jump.php?hostname="
|
||||
};
|
||||
private static void writeErrorMessage(byte[] errMessage, OutputStream out, String targetRequest,
|
||||
boolean usingWWWProxy, String wwwProxy, boolean showAddrHelper) throws IOException {
|
||||
if (out != null) {
|
||||
@ -615,11 +669,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
out.write("</a>".getBytes());
|
||||
if (usingWWWProxy) out.write(("<br>WWW proxy: " + wwwProxy).getBytes());
|
||||
if (showAddrHelper) {
|
||||
out.write("<br><br>Click below to try an address helper link:<br><br><a href=\"http://orion.i2p/jump/".getBytes());
|
||||
out.write(uri.getBytes());
|
||||
out.write("\">http://orion.i2p/jump/".getBytes());
|
||||
out.write(uri.getBytes());
|
||||
out.write("</a>".getBytes());
|
||||
out.write("<br><br>Click a link below to look for an address helper by using a \"jump\" service:<br>".getBytes());
|
||||
for (int i = 0; i < jumpServers.length; i++) {
|
||||
out.write("<br><a href=\"".getBytes());
|
||||
out.write(jumpServers[i].getBytes());
|
||||
out.write(uri.getBytes());
|
||||
out.write("\">".getBytes());
|
||||
out.write(jumpServers[i].getBytes());
|
||||
out.write(uri.getBytes());
|
||||
out.write("</a>".getBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
out.write("</div><p><i>I2P HTTP Proxy Server<br>Generated on: ".getBytes());
|
||||
|
@ -272,7 +272,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
int idx=0;
|
||||
final String[] allowedCommands =
|
||||
{
|
||||
"NOTICE",
|
||||
// "NOTICE", // can contain CTCP
|
||||
//"PING",
|
||||
//"PONG",
|
||||
"MODE",
|
||||
@ -306,34 +306,36 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
} catch(NumberFormatException nfe){}
|
||||
|
||||
|
||||
if ("PING".equals(command))
|
||||
if ("PING".equalsIgnoreCase(command))
|
||||
return "PING 127.0.0.1"; // no way to know what the ircd to i2ptunnel server con is, so localhost works
|
||||
if ("PONG".equals(command)) {
|
||||
if ("PONG".equalsIgnoreCase(command)) {
|
||||
// Turn the received ":irc.freshcoffee.i2p PONG irc.freshcoffee.i2p :127.0.0.1"
|
||||
// into ":127.0.0.1 PONG 127.0.0.1 " so that the caller can append the client's extra parameter
|
||||
// though, does 127.0.0.1 work for irc clients connecting remotely? and for all of them? sure would
|
||||
// be great if irc clients actually followed the RFCs here, but i guess thats too much to ask.
|
||||
// If we haven't PINGed them, or the PING we sent isn't something we know how to filter, this
|
||||
// is blank.
|
||||
String pong = expectedPong.length() > 0 ? expectedPong.toString() : null;
|
||||
//
|
||||
// String pong = expectedPong.length() > 0 ? expectedPong.toString() : null;
|
||||
// If we aren't going to rewrite it, pass it through
|
||||
String pong = expectedPong.length() > 0 ? expectedPong.toString() : s;
|
||||
expectedPong.setLength(0);
|
||||
return pong;
|
||||
}
|
||||
|
||||
// Allow all allowedCommands
|
||||
for(int i=0;i<allowedCommands.length;i++) {
|
||||
if(allowedCommands[i].equals(command))
|
||||
if(allowedCommands[i].equalsIgnoreCase(command))
|
||||
return s;
|
||||
}
|
||||
|
||||
// Allow PRIVMSG, but block CTCP.
|
||||
if("PRIVMSG".equals(command))
|
||||
if("PRIVMSG".equalsIgnoreCase(command) || "NOTICE".equalsIgnoreCase(command))
|
||||
{
|
||||
String msg;
|
||||
msg = field[idx++];
|
||||
|
||||
byte[] bytes = msg.getBytes();
|
||||
if(bytes[1]==0x01)
|
||||
if(msg.indexOf(0x01) >= 0) // CTCP marker ^A can be anywhere, not just immediately after the ':'
|
||||
{
|
||||
// CTCP
|
||||
msg=msg.substring(2);
|
||||
@ -356,7 +358,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
String command;
|
||||
final String[] allowedCommands =
|
||||
{
|
||||
"NOTICE",
|
||||
// "NOTICE", // can contain CTCP
|
||||
"MODE",
|
||||
"JOIN",
|
||||
"NICK",
|
||||
@ -387,7 +389,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
|
||||
command = field[0].toUpperCase();
|
||||
|
||||
if ("PING".equals(command)) {
|
||||
if ("PING".equalsIgnoreCase(command)) {
|
||||
// Most clients just send a PING and are happy with any old PONG. Others,
|
||||
// like BitchX, actually expect certain behavior. It sends two different pings:
|
||||
// "PING :irc.freshcoffee.i2p" and "PING 1234567890 127.0.0.1" (where the IP is the proxy)
|
||||
@ -403,13 +405,15 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
expectedPong.setLength(0);
|
||||
if (field.length == 1) { // PING
|
||||
rv = "PING";
|
||||
expectedPong.append("PONG 127.0.0.1");
|
||||
// If we aren't rewriting the PING don't rewrite the PONG
|
||||
// expectedPong.append("PONG 127.0.0.1");
|
||||
} else if (field.length == 2) { // PING nonce
|
||||
rv = "PING " + field[1];
|
||||
expectedPong.append("PONG ").append(field[1]);
|
||||
// If we aren't rewriting the PING don't rewrite the PONG
|
||||
// expectedPong.append("PONG ").append(field[1]);
|
||||
} else if (field.length == 3) { // PING nonce serverLocation
|
||||
rv = "PING " + field[1];
|
||||
expectedPong.append("PONG ").append(field[1]);
|
||||
expectedPong.append("PONG ").append(field[2]).append(" :").append(field[1]); // PONG serverLocation nonce
|
||||
} else {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("IRC client sent a PING we don't understand, filtering it (\"" + s + "\")");
|
||||
@ -417,28 +421,27 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("sending ping " + rv + ", waiting for " + expectedPong + " orig was [" + s + "]");
|
||||
_log.warn("sending ping [" + rv + "], waiting for [" + expectedPong + "] orig was [" + s + "]");
|
||||
|
||||
return rv;
|
||||
}
|
||||
if ("PONG".equals(command))
|
||||
if ("PONG".equalsIgnoreCase(command))
|
||||
return "PONG 127.0.0.1"; // no way to know what the ircd to i2ptunnel server con is, so localhost works
|
||||
|
||||
// Allow all allowedCommands
|
||||
for(int i=0;i<allowedCommands.length;i++)
|
||||
{
|
||||
if(allowedCommands[i].equals(command))
|
||||
if(allowedCommands[i].equalsIgnoreCase(command))
|
||||
return s;
|
||||
}
|
||||
|
||||
// Allow PRIVMSG, but block CTCP (except ACTION).
|
||||
if("PRIVMSG".equals(command))
|
||||
if("PRIVMSG".equalsIgnoreCase(command) || "NOTICE".equalsIgnoreCase(command))
|
||||
{
|
||||
String msg;
|
||||
msg = field[2];
|
||||
|
||||
byte[] bytes = msg.getBytes();
|
||||
if(bytes[1]==0x01)
|
||||
if(msg.indexOf(0x01) >= 0) // CTCP marker ^A can be anywhere, not just immediately after the ':'
|
||||
{
|
||||
// CTCP
|
||||
msg=msg.substring(2);
|
||||
@ -451,14 +454,14 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
return s;
|
||||
}
|
||||
|
||||
if("USER".equals(command)) {
|
||||
if("USER".equalsIgnoreCase(command)) {
|
||||
int idx = field[2].lastIndexOf(":");
|
||||
if(idx<0)
|
||||
return "USER user hostname localhost :realname";
|
||||
String realname = field[2].substring(idx+1);
|
||||
String ret = "USER "+field[1]+" hostname localhost :"+realname;
|
||||
return ret;
|
||||
} else if ("QUIT".equals(command)) {
|
||||
} else if ("QUIT".equalsIgnoreCase(command)) {
|
||||
return "QUIT :leaving";
|
||||
}
|
||||
|
||||
|
@ -3,15 +3,19 @@
|
||||
|
||||
<target name="all" depends="build" />
|
||||
<target name="fetchJettylib" >
|
||||
<available property="jetty.available" file="jetty-5.1.12.zip" />
|
||||
<available property="jetty.zip.available" file="jetty-5.1.12.zip" type="file" />
|
||||
<available property="jetty.zip.extracted" file="jettylib" type="dir" />
|
||||
<ant target="doFetchJettylib" />
|
||||
<ant target="doExtractJettylib" />
|
||||
</target>
|
||||
<target name="doFetchJettylib" unless="jetty.available" >
|
||||
<target name="doFetchJettylib" unless="jetty.zip.available" >
|
||||
<echo message="The libraries contained within the fetched file are from Jetty's 5.1.12" />
|
||||
<echo message="distribution (http://jetty.mortbay.org/). These are not " />
|
||||
<echo message="necessary for using I2P, but are used by some applications on top of I2P," />
|
||||
<echo message="such as the routerconsole." />
|
||||
<get src="http://mesh.dl.sourceforge.net/sourceforge/jetty/jetty-5.1.12.zip" verbose="true" dest="jetty-5.1.12.zip" />
|
||||
</target>
|
||||
<target name="doExtractJettylib" unless="jetty.zip.extracted" >
|
||||
<ant target="doExtract" />
|
||||
</target>
|
||||
<target name="doExtract">
|
||||
|
@ -138,8 +138,6 @@ public class I2PSocketManagerFactory {
|
||||
I2PSession session = client.createSession(myPrivateKeyStream, opts);
|
||||
session.connect();
|
||||
I2PSocketManager sockMgr = createManager(session, opts, "manager");
|
||||
if (sockMgr != null)
|
||||
sockMgr.setDefaultOptions(sockMgr.buildOptions(opts));
|
||||
return sockMgr;
|
||||
} catch (I2PSessionException ise) {
|
||||
_log.error("Error creating session for socket manager", ise);
|
||||
@ -199,4 +197,4 @@ public class I2PSocketManagerFactory {
|
||||
}
|
||||
return i2cpPort;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,8 @@ public class ConfigNetHandler extends FormHandler {
|
||||
private String _ntcpPort;
|
||||
private String _tcpPort;
|
||||
private String _udpPort;
|
||||
private boolean _ntcpAutoIP;
|
||||
private boolean _ntcpAutoPort;
|
||||
private String _inboundRate;
|
||||
private String _inboundBurstRate;
|
||||
private String _inboundBurst;
|
||||
@ -70,6 +72,8 @@ public class ConfigNetHandler extends FormHandler {
|
||||
public void setDynamicKeys(String moo) { _dynamicKeys = true; }
|
||||
public void setUpdateratesonly(String moo) { _ratesOnly = true; }
|
||||
public void setEnableloadtesting(String moo) { _enableLoadTesting = true; }
|
||||
public void setNtcpAutoIP(String moo) { _ntcpAutoIP = true; }
|
||||
public void setNtcpAutoPort(String moo) { _ntcpAutoPort = true; }
|
||||
|
||||
public void setHostname(String hostname) {
|
||||
_hostname = (hostname != null ? hostname.trim() : null);
|
||||
@ -142,27 +146,49 @@ public class ConfigNetHandler extends FormHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if ( (_ntcpHostname != null) && (_ntcpHostname.length() > 0) && (_ntcpPort != null) && (_ntcpPort.length() > 0) ) {
|
||||
String oldHost = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_HOSTNAME);
|
||||
String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_PORT);
|
||||
if ( (oldHost == null) || (!oldHost.equalsIgnoreCase(_ntcpHostname)) ||
|
||||
(oldPort == null) || (!oldPort.equalsIgnoreCase(_ntcpPort)) ) {
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_HOSTNAME, _ntcpHostname);
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_PORT, _ntcpPort);
|
||||
addFormNotice("Updating inbound TCP settings from " + oldHost + ":" + oldPort
|
||||
+ " to " + _ntcpHostname + ":" + _ntcpPort);
|
||||
restartRequired = true;
|
||||
}
|
||||
} else {
|
||||
String oldHost = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_HOSTNAME);
|
||||
String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_PORT);
|
||||
if ( (oldHost != null) || (oldPort != null) ) {
|
||||
// Normalize some things to make the following code a little easier...
|
||||
String oldNHost = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_HOSTNAME);
|
||||
if (oldNHost == null) oldNHost = "";
|
||||
String oldNPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_PORT);
|
||||
if (oldNPort == null) oldNPort = "";
|
||||
String sAutoHost = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_AUTO_IP);
|
||||
String sAutoPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_AUTO_PORT);
|
||||
boolean oldAutoHost = "true".equalsIgnoreCase(sAutoHost);
|
||||
boolean oldAutoPort = "true".equalsIgnoreCase(sAutoPort);
|
||||
if (_ntcpHostname == null) _ntcpHostname = "";
|
||||
if (_ntcpPort == null) _ntcpPort = "";
|
||||
|
||||
if (oldAutoHost != _ntcpAutoIP || ! oldNHost.equalsIgnoreCase(_ntcpHostname)) {
|
||||
if (_ntcpAutoIP) {
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_AUTO_IP, "true");
|
||||
_context.router().removeConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_HOSTNAME);
|
||||
_context.router().removeConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_PORT);
|
||||
addFormNotice("Updating inbound TCP settings from " + oldHost + ":" + oldPort
|
||||
+ " so that we no longer receive inbound TCP connections");
|
||||
restartRequired = true;
|
||||
addFormNotice("Updating inbound TCP address to auto");
|
||||
} else if (_ntcpHostname.length() > 0) {
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_HOSTNAME, _ntcpHostname);
|
||||
_context.router().removeConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_AUTO_IP);
|
||||
addFormNotice("Updating inbound TCP address to " + _ntcpHostname);
|
||||
} else {
|
||||
_context.router().removeConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_HOSTNAME);
|
||||
_context.router().removeConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_AUTO_IP);
|
||||
addFormNotice("Disabling inbound TCP");
|
||||
}
|
||||
restartRequired = true;
|
||||
}
|
||||
if (oldAutoPort != _ntcpAutoPort || ! oldNPort.equals(_ntcpPort)) {
|
||||
if ( _ntcpAutoPort ) {
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_AUTO_PORT, "true");
|
||||
_context.router().removeConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_PORT);
|
||||
addFormNotice("Updating inbound TCP port to auto");
|
||||
} else if (_ntcpPort.length() > 0) {
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_PORT, _ntcpPort);
|
||||
_context.router().removeConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_AUTO_PORT);
|
||||
addFormNotice("Updating inbound TCP port to " + _ntcpPort);
|
||||
} else {
|
||||
_context.router().removeConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_PORT);
|
||||
_context.router().removeConfigSetting(ConfigNetHelper.PROP_I2NP_NTCP_AUTO_PORT);
|
||||
addFormNotice("Disabling inbound TCP");
|
||||
}
|
||||
restartRequired = true;
|
||||
}
|
||||
|
||||
if ( (_udpPort != null) && (_udpPort.length() > 0) ) {
|
||||
|
@ -50,6 +50,8 @@ public class ConfigNetHelper {
|
||||
}
|
||||
public final static String PROP_I2NP_NTCP_HOSTNAME = "i2np.ntcp.hostname";
|
||||
public final static String PROP_I2NP_NTCP_PORT = "i2np.ntcp.port";
|
||||
public final static String PROP_I2NP_NTCP_AUTO_PORT = "i2np.ntcp.autoip";
|
||||
public final static String PROP_I2NP_NTCP_AUTO_IP = "i2np.ntcp.autoport";
|
||||
public String getNtcphostname() {
|
||||
String hostname = _context.getProperty(PROP_I2NP_NTCP_HOSTNAME);
|
||||
if (hostname == null) return "";
|
||||
@ -69,6 +71,26 @@ public class ConfigNetHelper {
|
||||
return ua.toString();
|
||||
}
|
||||
|
||||
public String getUdpIP() {
|
||||
RouterAddress addr = _context.router().getRouterInfo().getTargetAddress("SSU");
|
||||
if (addr == null)
|
||||
return "unknown";
|
||||
UDPAddress ua = new UDPAddress(addr);
|
||||
if (ua.getHost() == null)
|
||||
return "unknown";
|
||||
return ua.getHost();
|
||||
}
|
||||
|
||||
public String getUdpPort() {
|
||||
RouterAddress addr = _context.router().getRouterInfo().getTargetAddress("SSU");
|
||||
if (addr == null)
|
||||
return "unknown";
|
||||
UDPAddress ua = new UDPAddress(addr);
|
||||
if (ua.getPort() <= 0)
|
||||
return "unknown";
|
||||
return "" + ua.getPort();
|
||||
}
|
||||
|
||||
public String getEnableTimeSyncChecked() {
|
||||
String disabled = _context.getProperty(Timestamper.PROP_DISABLED, "false");
|
||||
if ( (disabled != null) && ("true".equalsIgnoreCase(disabled)) )
|
||||
@ -93,6 +115,22 @@ public class ConfigNetHelper {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getTcpAutoPortChecked() {
|
||||
String enabled = _context.getProperty(PROP_I2NP_NTCP_AUTO_PORT, "false");
|
||||
if ( (enabled != null) && ("true".equalsIgnoreCase(enabled)) )
|
||||
return " checked ";
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getTcpAutoIPChecked() {
|
||||
String enabled = _context.getProperty(PROP_I2NP_NTCP_AUTO_IP, "false");
|
||||
if ( (enabled != null) && ("true".equalsIgnoreCase(enabled)) )
|
||||
return " checked ";
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getRequireIntroductionsChecked() {
|
||||
short status = _context.commSystem().getReachabilityStatus();
|
||||
switch (status) {
|
||||
@ -236,4 +274,23 @@ public class ConfigNetHelper {
|
||||
buf.append("</select>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public int getShareBandwidth() {
|
||||
String irate = _context.getProperty(PROP_INBOUND_KBPS);
|
||||
String orate = _context.getProperty(PROP_OUTBOUND_KBPS);
|
||||
String pctStr = _context.getProperty(PROP_SHARE_PERCENTAGE);
|
||||
if ( (irate != null) && (orate != null) && (pctStr != null)) {
|
||||
try {
|
||||
int irateKBps = Integer.parseInt(irate);
|
||||
int orateKBps = Integer.parseInt(orate);
|
||||
if (irateKBps < 0 || orateKBps < 0)
|
||||
return 0;
|
||||
int pct = Integer.parseInt(pctStr);
|
||||
return (int) (((float) pct) * Math.min(irateKBps, orateKBps) / 100);
|
||||
} catch (NumberFormatException nfe) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import net.i2p.stat.StatManager;
|
||||
public class ConfigStatsHandler extends FormHandler {
|
||||
private String _filename;
|
||||
private List _stats;
|
||||
private String _graphs;
|
||||
private boolean _explicitFilter;
|
||||
private String _explicitFilterValue;
|
||||
|
||||
@ -48,6 +49,25 @@ public class ConfigStatsHandler extends FormHandler {
|
||||
_log.debug("Updated stats: " + _stats);
|
||||
}
|
||||
|
||||
public void setGraphList(String stats[]) {
|
||||
if (stats != null) {
|
||||
String s = "";
|
||||
for (int i = 0; i < stats.length; i++) {
|
||||
String cur = stats[i].trim();
|
||||
if (cur.length() > 0) {
|
||||
if (s.length() > 0)
|
||||
s = s + ",";
|
||||
s = s + cur;
|
||||
}
|
||||
}
|
||||
_graphs = s;
|
||||
} else {
|
||||
_graphs = "";
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Updated graphs: " + _graphs);
|
||||
}
|
||||
|
||||
public void setExplicitFilter(String foo) { _explicitFilter = true; }
|
||||
public void setExplicitFilterValue(String filter) { _explicitFilterValue = filter; }
|
||||
|
||||
@ -88,11 +108,13 @@ public class ConfigStatsHandler extends FormHandler {
|
||||
}
|
||||
|
||||
_context.router().setConfigSetting(StatManager.PROP_STAT_FILTER, stats.toString());
|
||||
_context.router().setConfigSetting("stat.summaries", _graphs);
|
||||
boolean ok = _context.router().saveConfig();
|
||||
if (ok)
|
||||
addFormNotice("Stat filter and location updated successfully to: " + stats.toString());
|
||||
else
|
||||
addFormError("Failed to update the stat filter and location");
|
||||
addFormNotice("Graph list updated, may take up to 60s to be reflected here and on the <a href=\"graphs.jsp\">Graphs Page</a>");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import net.i2p.stat.Rate;
|
||||
import net.i2p.stat.RateStat;
|
||||
import net.i2p.stat.FrequencyStat;
|
||||
import net.i2p.router.RouterContext;
|
||||
@ -20,12 +21,15 @@ public class ConfigStatsHelper {
|
||||
/** list of names of stats which are remaining, ordered by nested groups */
|
||||
private List _stats;
|
||||
private String _currentStatName;
|
||||
private String _currentGraphName;
|
||||
private String _currentStatDescription;
|
||||
private String _currentGroup;
|
||||
/** true if the current stat is the first in the group */
|
||||
private boolean _currentIsFirstInGroup;
|
||||
/** true if the stat is being logged */
|
||||
private boolean _currentIsLogged;
|
||||
private boolean _currentIsGraphed;
|
||||
private boolean _currentCanBeGraphed;
|
||||
|
||||
/**
|
||||
* Configure this bean to query a particular router context
|
||||
@ -71,6 +75,7 @@ public class ConfigStatsHelper {
|
||||
public boolean hasMoreStats() {
|
||||
if (_stats.size() <= 0)
|
||||
return false;
|
||||
_currentIsGraphed = false;
|
||||
_currentStatName = (String)_stats.remove(0);
|
||||
RateStat rs = _context.statManager().getRate(_currentStatName);
|
||||
if (rs != null) {
|
||||
@ -82,6 +87,16 @@ public class ConfigStatsHelper {
|
||||
else
|
||||
_currentIsFirstInGroup = false;
|
||||
_currentGroup = rs.getGroupName();
|
||||
long period = rs.getPeriods()[0]; // should be the minimum
|
||||
if (period <= 10*60*1000) {
|
||||
Rate r = rs.getRate(period);
|
||||
_currentCanBeGraphed = r != null;
|
||||
if (_currentCanBeGraphed)
|
||||
_currentIsGraphed = r.getSummaryListener() != null;
|
||||
_currentGraphName = _currentStatName + "." + period;
|
||||
} else {
|
||||
_currentCanBeGraphed = false;
|
||||
}
|
||||
} else {
|
||||
FrequencyStat fs = _context.statManager().getFrequency(_currentStatName);
|
||||
if (fs != null) {
|
||||
@ -93,6 +108,7 @@ public class ConfigStatsHelper {
|
||||
else
|
||||
_currentIsFirstInGroup = false;
|
||||
_currentGroup = fs.getGroupName();
|
||||
_currentCanBeGraphed = false;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Stat does not exist?! [" + _currentStatName + "]");
|
||||
@ -119,7 +135,10 @@ public class ConfigStatsHelper {
|
||||
/** What group is the current stat in */
|
||||
public String getCurrentGroupName() { return _currentGroup; }
|
||||
public String getCurrentStatName() { return _currentStatName; }
|
||||
public String getCurrentGraphName() { return _currentGraphName; }
|
||||
public String getCurrentStatDescription() { return _currentStatDescription; }
|
||||
public boolean getCurrentIsLogged() { return _currentIsLogged; }
|
||||
public boolean getCurrentIsGraphed() { return _currentIsGraphed; }
|
||||
public boolean getCurrentCanBeGraphed() { return _currentCanBeGraphed; }
|
||||
public String getExplicitFilter() { return _filter; }
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package net.i2p.router.web;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.crypto.TrustedUpdate;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.web.ConfigServiceHandler.UpdateWrapperManagerTask;
|
||||
import net.i2p.util.Log;
|
||||
@ -20,20 +21,29 @@ public class ConfigUpdateHandler extends FormHandler {
|
||||
private String _trustedKeys;
|
||||
|
||||
public static final String PROP_NEWS_URL = "router.newsURL";
|
||||
public static final String DEFAULT_NEWS_URL = "http://dev.i2p.net/cgi-bin/cvsweb.cgi/i2p/news.xml?rev=HEAD";
|
||||
// public static final String DEFAULT_NEWS_URL = "http://dev.i2p.net/cgi-bin/cvsweb.cgi/i2p/news.xml?rev=HEAD";
|
||||
public static final String DEFAULT_NEWS_URL = "http://dev.i2p/cgi-bin/cvsweb.cgi/i2p/news.xml?rev=HEAD";
|
||||
public static final String PROP_REFRESH_FREQUENCY = "router.newsRefreshFrequency";
|
||||
public static final String DEFAULT_REFRESH_FREQUENCY = 12*60*60*1000 + "";
|
||||
public static final String PROP_UPDATE_URL = "router.updateURL";
|
||||
public static final String DEFAULT_UPDATE_URL = "http://dev.i2p.net/i2p/i2pupdate.sud";
|
||||
public static final String PROP_UPDATE_POLICY = "router.updatePolicy";
|
||||
public static final String DEFAULT_UPDATE_POLICY = "notify";
|
||||
public static final String PROP_SHOULD_PROXY = "router.updateThroughProxy";
|
||||
public static final String DEFAULT_SHOULD_PROXY = Boolean.FALSE.toString();
|
||||
public static final String DEFAULT_SHOULD_PROXY = Boolean.TRUE.toString();
|
||||
public static final String PROP_PROXY_HOST = "router.updateProxyHost";
|
||||
public static final String DEFAULT_PROXY_HOST = "localhost";
|
||||
public static final String PROP_PROXY_PORT = "router.updateProxyPort";
|
||||
public static final String DEFAULT_PROXY_PORT = "4444";
|
||||
|
||||
public static final String PROP_UPDATE_URL = "router.updateURL";
|
||||
// public static final String DEFAULT_UPDATE_URL = "http://dev.i2p.net/i2p/i2pupdate.sud";
|
||||
public static final String DEFAULT_UPDATE_URL =
|
||||
"http://amiga.i2p/i2p/i2pupdate.sud\r\n" +
|
||||
"http://stats.i2p/i2p/i2pupdate.sud\r\n" +
|
||||
"http://complication.i2p/i2p/i2pupdate.sud";
|
||||
|
||||
public static final String PROP_TRUSTED_KEYS = "router.trustedUpdateKeys";
|
||||
|
||||
|
||||
protected void processForm() {
|
||||
if ("Check for update now".equals(_action)) {
|
||||
NewsFetcher fetcher = NewsFetcher.getInstance(I2PAppContext.getGlobalContext());
|
||||
@ -52,14 +62,6 @@ public class ConfigUpdateHandler extends FormHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if ( (_updateURL != null) && (_updateURL.length() > 0) ) {
|
||||
String oldURL = _context.router().getConfigSetting(PROP_UPDATE_URL);
|
||||
if ( (oldURL == null) || (!_updateURL.equals(oldURL)) ) {
|
||||
_context.router().setConfigSetting(PROP_UPDATE_URL, _updateURL);
|
||||
addFormNotice("Updating update URL to " + _updateURL);
|
||||
}
|
||||
}
|
||||
|
||||
if ( (_proxyHost != null) && (_proxyHost.length() > 0) ) {
|
||||
String oldHost = _context.router().getConfigSetting(PROP_PROXY_HOST);
|
||||
if ( (oldHost == null) || (!_proxyHost.equals(oldHost)) ) {
|
||||
@ -98,8 +100,22 @@ public class ConfigUpdateHandler extends FormHandler {
|
||||
addFormNotice("Updating update policy to " + _updatePolicy);
|
||||
}
|
||||
}
|
||||
|
||||
// should save the keys...
|
||||
|
||||
if ( (_updateURL != null) && (_updateURL.length() > 0) ) {
|
||||
String oldURL = _context.router().getConfigSetting(PROP_UPDATE_URL);
|
||||
if ( (oldURL == null) || (!_updateURL.equals(oldURL)) ) {
|
||||
_context.router().setConfigSetting(PROP_UPDATE_URL, _updateURL);
|
||||
addFormNotice("Updating update URLs.");
|
||||
}
|
||||
}
|
||||
|
||||
if ( (_trustedKeys != null) && (_trustedKeys.length() > 0) ) {
|
||||
String oldKeys = new TrustedUpdate(_context).getTrustedKeysString();
|
||||
if ( (oldKeys == null) || (!_trustedKeys.equals(oldKeys)) ) {
|
||||
_context.router().setConfigSetting(PROP_TRUSTED_KEYS, _trustedKeys);
|
||||
addFormNotice("Updating trusted keys.");
|
||||
}
|
||||
}
|
||||
|
||||
_context.router().saveConfig();
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.util.List;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.crypto.TrustedUpdate;
|
||||
import net.i2p.router.RouterContext;
|
||||
@ -113,11 +112,6 @@ public class ConfigUpdateHelper {
|
||||
}
|
||||
|
||||
public String getTrustedKeys() {
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
TrustedUpdate up = new TrustedUpdate(_context);
|
||||
List keys = up.getTrustedKeys();
|
||||
for (int i = 0; i < keys.size(); i++)
|
||||
buf.append((String)keys.get(i)).append('\n');
|
||||
return buf.toString();
|
||||
return new TrustedUpdate(_context).getTrustedKeysString();
|
||||
}
|
||||
}
|
||||
|
@ -55,20 +55,37 @@ public class GraphHelper {
|
||||
|
||||
public String getImages() {
|
||||
try {
|
||||
_out.write("<img src=\"viewstat.jsp?stat=bw.combined"
|
||||
+ "&periodCount=" + _periodCount
|
||||
+ "&width=" + _width
|
||||
+ "&height=" + _height
|
||||
+ "\" title=\"Combined bandwidth graph\" />\n");
|
||||
|
||||
List listeners = StatSummarizer.instance().getListeners();
|
||||
TreeSet ordered = new TreeSet(new AlphaComparator());
|
||||
ordered.addAll(listeners);
|
||||
|
||||
// go to some trouble to see if we have the data for the combined bw graph
|
||||
boolean hasTx = false;
|
||||
boolean hasRx = false;
|
||||
for (Iterator iter = ordered.iterator(); iter.hasNext(); ) {
|
||||
SummaryListener lsnr = (SummaryListener)iter.next();
|
||||
String title = lsnr.getRate().getRateStat().getName();
|
||||
if (title.equals("bw.sendRate")) hasTx = true;
|
||||
else if (title.equals("bw.recvRate")) hasRx = true;
|
||||
}
|
||||
|
||||
if (hasTx && hasRx && !_showEvents)
|
||||
_out.write("<img width=\""
|
||||
+ (_width + 83) + "\" height=\"" + (_height + 92)
|
||||
+ "\" src=\"viewstat.jsp?stat=bw.combined"
|
||||
+ "&periodCount=" + _periodCount
|
||||
+ "&width=" + _width
|
||||
+ "&height=" + (_height - 14)
|
||||
+ "\" title=\"Combined bandwidth graph\" />\n");
|
||||
|
||||
for (Iterator iter = ordered.iterator(); iter.hasNext(); ) {
|
||||
SummaryListener lsnr = (SummaryListener)iter.next();
|
||||
Rate r = lsnr.getRate();
|
||||
String title = r.getRateStat().getName() + " for " + DataHelper.formatDuration(_periodCount * r.getPeriod());
|
||||
_out.write("<img src=\"viewstat.jsp?stat=" + r.getRateStat().getName()
|
||||
_out.write("<img width=\""
|
||||
+ (_width + 83) + "\" height=\"" + (_height + 92)
|
||||
+ "\" src=\"viewstat.jsp?stat="
|
||||
+ r.getRateStat().getName()
|
||||
+ "&showEvents=" + _showEvents
|
||||
+ "&period=" + r.getPeriod()
|
||||
+ "&periodCount=" + _periodCount
|
||||
@ -86,6 +103,7 @@ public class GraphHelper {
|
||||
}
|
||||
public String getForm() {
|
||||
try {
|
||||
_out.write("<p /><a href=\"configstats.jsp\">Select Stats to Graph</a><p />");
|
||||
_out.write("<form action=\"graphs.jsp\" method=\"GET\">");
|
||||
_out.write("Periods: <input size=\"3\" type=\"text\" name=\"periodCount\" value=\"" + _periodCount + "\" /><br />\n");
|
||||
_out.write("Plot averages: <input type=\"radio\" name=\"showEvents\" value=\"false\" " + (_showEvents ? "" : "checked=\"true\" ") + " /> ");
|
||||
@ -119,4 +137,4 @@ class AlphaComparator implements Comparator {
|
||||
String rName = r.getRate().getRateStat().getName() + "." + r.getRate().getPeriod();
|
||||
return lName.compareTo(rName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
||||
private Log _log;
|
||||
private boolean _updateAvailable;
|
||||
private long _lastFetch;
|
||||
private String _lastModified;
|
||||
private static NewsFetcher _instance;
|
||||
//public static final synchronized NewsFetcher getInstance() { return _instance; }
|
||||
public static final synchronized NewsFetcher getInstance(I2PAppContext ctx) {
|
||||
@ -105,11 +106,12 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
||||
proxyPort = Integer.parseInt(port);
|
||||
EepGet get = null;
|
||||
if (shouldProxy)
|
||||
get = new EepGet(_context, proxyHost, proxyPort, 10, TEMP_NEWS_FILE, newsURL);
|
||||
get = new EepGet(_context, true, proxyHost, proxyPort, 2, TEMP_NEWS_FILE, newsURL, true, null, _lastModified);
|
||||
else
|
||||
get = new EepGet(_context, 10, TEMP_NEWS_FILE, newsURL);
|
||||
get = new EepGet(_context, false, null, 0, 0, TEMP_NEWS_FILE, newsURL, true, null, _lastModified);
|
||||
get.addStatusListener(this);
|
||||
get.fetch();
|
||||
if (get.fetch())
|
||||
_lastModified = get.getLastModified();
|
||||
} catch (Throwable t) {
|
||||
_log.error("Error fetching the news", t);
|
||||
}
|
||||
@ -212,8 +214,8 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
||||
_log.error("Failed to copy the news file!");
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Transfer complete, but no file?");
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Transfer complete, but no file? - probably 304 Not Modified");
|
||||
}
|
||||
checkForUpdates();
|
||||
}
|
||||
@ -225,4 +227,5 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
|
||||
temp.delete();
|
||||
}
|
||||
public void headerReceived(String url, int attemptNum, String key, String val) {}
|
||||
public void attempting(String url) {}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package net.i2p.router.web;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import java.net.Socket;
|
||||
@ -13,18 +12,51 @@ import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.EepGet;
|
||||
|
||||
/**
|
||||
* Handler to deal with reseed requests. This reseed from the URL
|
||||
* http://dev.i2p.net/i2pdb2/ unless the java env property "i2p.reseedURL" is
|
||||
* http://dev.i2p.net/i2pdb2/ unless the I2P configuration property "i2p.reseedURL" is
|
||||
* set. It always writes to ./netDb/, so don't mess with that.
|
||||
*
|
||||
*/
|
||||
public class ReseedHandler {
|
||||
private static ReseedRunner _reseedRunner;
|
||||
private RouterContext _context;
|
||||
private Log _log;
|
||||
|
||||
// Reject unreasonably big files, because we download into a ByteArrayOutputStream.
|
||||
private static final long MAX_RESEED_RESPONSE_SIZE = 8 * 1024 * 1024;
|
||||
|
||||
private static final String DEFAULT_SEED_URL = "http://dev.i2p.net/i2pdb2/";
|
||||
|
||||
public ReseedHandler() {
|
||||
this(ContextHelper.getContext(null));
|
||||
}
|
||||
public ReseedHandler(RouterContext ctx) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(ReseedHandler.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure this bean to query a particular router context
|
||||
*
|
||||
* @param contextId begging few characters of the routerHash, or null to pick
|
||||
* the first one we come across.
|
||||
*/
|
||||
public void setContextId(String contextId) {
|
||||
try {
|
||||
_context = ContextHelper.getContext(contextId);
|
||||
_log = _context.logManager().getLog(ReseedHandler.class);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static ReseedRunner _reseedRunner = new ReseedRunner();
|
||||
|
||||
public void setReseedNonce(String nonce) {
|
||||
if (nonce == null) return;
|
||||
if (nonce.equals(System.getProperty("net.i2p.router.web.ReseedHandler.nonce")) ||
|
||||
@ -33,22 +65,28 @@ public class ReseedHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public static void requestReseed() {
|
||||
synchronized (_reseedRunner) {
|
||||
public void requestReseed() {
|
||||
synchronized (ReseedHandler.class) {
|
||||
if (_reseedRunner == null)
|
||||
_reseedRunner = new ReseedRunner();
|
||||
if (_reseedRunner.isRunning()) {
|
||||
return;
|
||||
} else {
|
||||
System.setProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "true");
|
||||
System.out.println("Reseeding");
|
||||
I2PThread reseed = new I2PThread(_reseedRunner, "Reseed");
|
||||
reseed.start();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ReseedRunner implements Runnable {
|
||||
public class ReseedRunner implements Runnable, EepGet.StatusListener {
|
||||
private boolean _isRunning;
|
||||
public ReseedRunner() { _isRunning = false; }
|
||||
|
||||
public ReseedRunner() {
|
||||
_isRunning = false;
|
||||
System.setProperty("net.i2p.router.web.ReseedHandler.statusMessage","Reseeding.");
|
||||
}
|
||||
public boolean isRunning() { return _isRunning; }
|
||||
public void run() {
|
||||
_isRunning = true;
|
||||
@ -57,141 +95,159 @@ public class ReseedHandler {
|
||||
System.setProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false");
|
||||
_isRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
static final String DEFAULT_SEED_URL = "http://dev.i2p.net/i2pdb2/";
|
||||
/**
|
||||
* Reseed has been requested, so lets go ahead and do it. Fetch all of
|
||||
* the routerInfo-*.dat files from the specified URL (or the default) and
|
||||
* save them into this router's netDb dir.
|
||||
*
|
||||
*/
|
||||
private static void reseed(boolean echoStatus) {
|
||||
String seedURL = I2PAppContext.getGlobalContext().getProperty("i2p.reseedURL", DEFAULT_SEED_URL);
|
||||
if ( (seedURL == null) || (seedURL.trim().length() <= 0) )
|
||||
seedURL = DEFAULT_SEED_URL;
|
||||
try {
|
||||
URL dir = new URL(seedURL);
|
||||
byte contentRaw[] = readURL(dir);
|
||||
if (contentRaw == null) return;
|
||||
String content = new String(contentRaw);
|
||||
Set urls = new HashSet();
|
||||
int cur = 0;
|
||||
while (true) {
|
||||
int start = content.indexOf("href=\"routerInfo-", cur);
|
||||
if (start < 0)
|
||||
break;
|
||||
|
||||
int end = content.indexOf(".dat\">", start);
|
||||
String name = content.substring(start+"href=\"routerInfo-".length(), end);
|
||||
urls.add(name);
|
||||
cur = end + 1;
|
||||
}
|
||||
// EepGet status listeners
|
||||
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {
|
||||
// Since readURL() runs an EepGet with 0 retries,
|
||||
// we can report errors with attemptFailed() instead of transferFailed().
|
||||
// It has the benefit of providing cause of failure, which helps resolve issues.
|
||||
if (_log.shouldLog(Log.ERROR)) _log.error("EepGet failed on " + url, cause);
|
||||
}
|
||||
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {}
|
||||
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {}
|
||||
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {}
|
||||
public void headerReceived(String url, int attemptNum, String key, String val) {}
|
||||
public void attempting(String url) {}
|
||||
// End of EepGet status listeners
|
||||
|
||||
int fetched = 0;
|
||||
int errors = 0;
|
||||
for (Iterator iter = urls.iterator(); iter.hasNext(); ) {
|
||||
try {
|
||||
fetchSeed(seedURL, (String)iter.next());
|
||||
fetched++;
|
||||
if (echoStatus) {
|
||||
System.out.print(".");
|
||||
if (fetched % 60 == 0)
|
||||
System.out.println();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
errors++;
|
||||
/**
|
||||
* Reseed has been requested, so lets go ahead and do it. Fetch all of
|
||||
* the routerInfo-*.dat files from the specified URL (or the default) and
|
||||
* save them into this router's netDb dir.
|
||||
*
|
||||
*/
|
||||
private static final String RESEED_TIPS =
|
||||
"Ensure that nothing blocks outbound HTTP, check <a href=logs.jsp>logs</a> " +
|
||||
"and if nothing helps, read FAQ about reseeding manually.";
|
||||
|
||||
private void reseed(boolean echoStatus) {
|
||||
|
||||
String seedURL = _context.getProperty("i2p.reseedURL", DEFAULT_SEED_URL);
|
||||
if ( (seedURL == null) || (seedURL.trim().length() <= 0) )
|
||||
seedURL = DEFAULT_SEED_URL;
|
||||
try {
|
||||
System.setProperty("net.i2p.router.web.ReseedHandler.errorMessage","");
|
||||
System.setProperty("net.i2p.router.web.ReseedHandler.statusMessage","Reseeding: fetching seed URL.");
|
||||
URL dir = new URL(seedURL);
|
||||
byte contentRaw[] = readURL(dir);
|
||||
if (contentRaw == null) {
|
||||
System.setProperty("net.i2p.router.web.ReseedHandler.errorMessage",
|
||||
"Last reseed failed fully (failed reading seed URL). " +
|
||||
RESEED_TIPS);
|
||||
// Logging deprecated here since attemptFailed() provides better info
|
||||
_log.debug("Failed reading seed URL: " + seedURL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (echoStatus) System.out.println();
|
||||
} catch (Throwable t) {
|
||||
I2PAppContext.getGlobalContext().logManager().getLog(ReseedHandler.class).error("Error reseeding", t);
|
||||
}
|
||||
}
|
||||
|
||||
private static void fetchSeed(String seedURL, String peer) throws Exception {
|
||||
URL url = new URL(seedURL + (seedURL.endsWith("/") ? "" : "/") + "routerInfo-" + peer + ".dat");
|
||||
String content = new String(contentRaw);
|
||||
Set urls = new HashSet();
|
||||
int cur = 0;
|
||||
while (true) {
|
||||
int start = content.indexOf("href=\"routerInfo-", cur);
|
||||
if (start < 0)
|
||||
break;
|
||||
|
||||
byte data[] = readURL(url);
|
||||
//System.out.println("read: " + (data != null ? data.length : -1));
|
||||
writeSeed(peer, data);
|
||||
}
|
||||
|
||||
private static byte[] readURL(URL url) throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024);
|
||||
String hostname = url.getHost();
|
||||
int port = url.getPort();
|
||||
if (port < 0)
|
||||
port = 80;
|
||||
Socket s = new Socket(hostname, port);
|
||||
OutputStream out = s.getOutputStream();
|
||||
InputStream in = s.getInputStream();
|
||||
String request = getRequest(url);
|
||||
//System.out.println("Sending to " + hostname +":"+ port + ": " + request);
|
||||
out.write(request.getBytes());
|
||||
out.flush();
|
||||
// skip the HTTP response headers
|
||||
// (if we were smart, we'd check for HTTP 200, content-length, etc)
|
||||
int consecutiveNL = 0;
|
||||
while (true) {
|
||||
int cur = in.read();
|
||||
switch (cur) {
|
||||
case -1:
|
||||
return null;
|
||||
case '\n':
|
||||
case '\r':
|
||||
consecutiveNL++;
|
||||
break;
|
||||
default:
|
||||
consecutiveNL = 0;
|
||||
int end = content.indexOf(".dat\">", start);
|
||||
String name = content.substring(start+"href=\"routerInfo-".length(), end);
|
||||
urls.add(name);
|
||||
cur = end + 1;
|
||||
}
|
||||
if (urls.size() <= 0) {
|
||||
_log.error("Read " + contentRaw.length + " bytes from seed " + seedURL + ", but found no routerInfo URLs.");
|
||||
System.setProperty("net.i2p.router.web.ReseedHandler.errorMessage",
|
||||
"Last reseed failed fully (no routerInfo URLs at seed URL). " +
|
||||
RESEED_TIPS);
|
||||
return;
|
||||
}
|
||||
|
||||
int fetched = 0;
|
||||
int errors = 0;
|
||||
for (Iterator iter = urls.iterator(); iter.hasNext(); ) {
|
||||
try {
|
||||
System.setProperty("net.i2p.router.web.ReseedHandler.statusMessage",
|
||||
"Reseeding: fetching router info from seed URL (" +
|
||||
fetched + " successful, " + errors + " errors, " + urls.size() + " total).");
|
||||
|
||||
fetchSeed(seedURL, (String)iter.next());
|
||||
fetched++;
|
||||
if (echoStatus) {
|
||||
System.out.print(".");
|
||||
if (fetched % 60 == 0)
|
||||
System.out.println();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
if (echoStatus) System.out.println();
|
||||
|
||||
int failPercent = 100 * errors / urls.size();
|
||||
|
||||
// Less than 10% of failures is considered success,
|
||||
// because some routerInfos will always fail.
|
||||
if ((failPercent >= 10) && (failPercent < 90)) {
|
||||
System.setProperty("net.i2p.router.web.ReseedHandler.errorMessage",
|
||||
"Last reseed failed partly (" + failPercent + "% of " + urls.size() + "). " +
|
||||
RESEED_TIPS);
|
||||
}
|
||||
if (failPercent >= 90) {
|
||||
System.setProperty("net.i2p.router.web.ReseedHandler.errorMessage",
|
||||
"Last reseed failed (" + failPercent + "% of " + urls.size() + "). " +
|
||||
RESEED_TIPS);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
System.setProperty("net.i2p.router.web.ReseedHandler.errorMessage",
|
||||
"Last reseed failed fully (exception caught). " +
|
||||
RESEED_TIPS);
|
||||
_log.error("Error reseeding", t);
|
||||
}
|
||||
if (consecutiveNL == 4)
|
||||
break;
|
||||
}
|
||||
// ok, past the headers, grab the goods
|
||||
byte buf[] = new byte[1024];
|
||||
while (true) {
|
||||
int read = in.read(buf);
|
||||
if (read < 0)
|
||||
break;
|
||||
baos.write(buf, 0, read);
|
||||
|
||||
/* Since we don't return a value, we should always throw an exception if something fails. */
|
||||
private void fetchSeed(String seedURL, String peer) throws Exception {
|
||||
Log log = I2PAppContext.getGlobalContext().logManager().getLog(ReseedHandler.class);
|
||||
URL url = new URL(seedURL + (seedURL.endsWith("/") ? "" : "/") + "routerInfo-" + peer + ".dat");
|
||||
|
||||
byte data[] = readURL(url);
|
||||
if (data == null) {
|
||||
// Logging deprecated here since attemptFailed() provides better info
|
||||
_log.debug("Failed fetching seed: " + url.toString());
|
||||
throw new Exception ("Failed fetching seed.");
|
||||
}
|
||||
//System.out.println("read: " + (data != null ? data.length : -1));
|
||||
writeSeed(peer, data);
|
||||
}
|
||||
in.close();
|
||||
s.close();
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
private static String getRequest(URL url) {
|
||||
StringBuffer buf = new StringBuffer(512);
|
||||
String path = url.getPath();
|
||||
if ("".equals(path))
|
||||
path = "/";
|
||||
buf.append("GET ").append(path).append(" HTTP/1.0\n");
|
||||
buf.append("Host: ").append(url.getHost());
|
||||
int port = url.getPort();
|
||||
if ( (port > 0) && (port != 80) )
|
||||
buf.append(":").append(port);
|
||||
buf.append("\nConnection: close\n\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static void writeSeed(String name, byte data[]) throws Exception {
|
||||
String dirName = "netDb"; // _context.getProperty("router.networkDatabase.dbDir", "netDb");
|
||||
File netDbDir = new File(dirName);
|
||||
if (!netDbDir.exists()) {
|
||||
boolean ok = netDbDir.mkdirs();
|
||||
|
||||
private byte[] readURL(URL url) throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024);
|
||||
|
||||
// Do a non-proxied eepget into our ByteArrayOutputStream with 0 retries
|
||||
EepGet get = new EepGet( I2PAppContext.getGlobalContext(), false, null, -1, 0, 0, MAX_RESEED_RESPONSE_SIZE,
|
||||
null, baos, url.toString(), false, null, null);
|
||||
get.addStatusListener(ReseedRunner.this);
|
||||
if (get.fetch()) return baos.toByteArray(); else return null;
|
||||
}
|
||||
FileOutputStream fos = new FileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat"));
|
||||
fos.write(data);
|
||||
fos.close();
|
||||
}
|
||||
|
||||
private void writeSeed(String name, byte data[]) throws Exception {
|
||||
String dirName = "netDb"; // _context.getProperty("router.networkDatabase.dbDir", "netDb");
|
||||
File netDbDir = new File(dirName);
|
||||
if (!netDbDir.exists()) {
|
||||
boolean ok = netDbDir.mkdirs();
|
||||
}
|
||||
FileOutputStream fos = new FileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat"));
|
||||
fos.write(data);
|
||||
fos.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
if ( (args != null) && (args.length == 1) && (!Boolean.valueOf(args[0]).booleanValue()) ) {
|
||||
System.out.println("Not reseeding, as requested");
|
||||
return; // not reseeding on request
|
||||
}
|
||||
System.out.println("Reseeding");
|
||||
reseed(true);
|
||||
ReseedHandler reseedHandler = new ReseedHandler();
|
||||
reseedHandler.requestReseed();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -88,7 +88,8 @@ public class RouterConsoleRunner {
|
||||
// get i2p started - they can reseed later in the web console)
|
||||
String names[] = (netDb.exists() ? netDb.list() : null);
|
||||
if ( (names == null) || (names.length < 15) ) {
|
||||
ReseedHandler.requestReseed();
|
||||
ReseedHandler reseedHandler = new ReseedHandler();
|
||||
reseedHandler.requestReseed();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,27 +45,27 @@ public class StatSummarizer implements Runnable {
|
||||
|
||||
private static final String DEFAULT_DATABASES = "bw.sendRate.60000" +
|
||||
",bw.recvRate.60000" +
|
||||
",tunnel.testSuccessTime.60000" +
|
||||
",udp.outboundActiveCount.60000" +
|
||||
",udp.receivePacketSize.60000" +
|
||||
",udp.receivePacketSkew.60000" +
|
||||
",udp.sendConfirmTime.60000" +
|
||||
",udp.sendPacketSize.60000" +
|
||||
",router.activePeers.60000" +
|
||||
",router.activeSendPeers.60000" +
|
||||
",tunnel.acceptLoad.60000" +
|
||||
",tunnel.dropLoadProactive.60000" +
|
||||
",tunnel.buildExploratorySuccess.60000" +
|
||||
",tunnel.buildExploratoryReject.60000" +
|
||||
",tunnel.buildExploratoryExpire.60000" +
|
||||
",client.sendAckTime.60000" +
|
||||
",client.dispatchNoACK.60000" +
|
||||
",ntcp.sendTime.60000" +
|
||||
",ntcp.transmitTime.60000" +
|
||||
",ntcp.sendBacklogTime.60000" +
|
||||
",ntcp.receiveTime.60000" +
|
||||
",transport.sendMessageFailureLifetime.60000" +
|
||||
",transport.sendProcessingTime.60000";
|
||||
// ",tunnel.testSuccessTime.60000" +
|
||||
// ",udp.outboundActiveCount.60000" +
|
||||
// ",udp.receivePacketSize.60000" +
|
||||
// ",udp.receivePacketSkew.60000" +
|
||||
// ",udp.sendConfirmTime.60000" +
|
||||
// ",udp.sendPacketSize.60000" +
|
||||
",router.activePeers.60000";
|
||||
// ",router.activeSendPeers.60000" +
|
||||
// ",tunnel.acceptLoad.60000" +
|
||||
// ",tunnel.dropLoadProactive.60000" +
|
||||
// ",tunnel.buildExploratorySuccess.60000" +
|
||||
// ",tunnel.buildExploratoryReject.60000" +
|
||||
// ",tunnel.buildExploratoryExpire.60000" +
|
||||
// ",client.sendAckTime.60000" +
|
||||
// ",client.dispatchNoACK.60000" +
|
||||
// ",ntcp.sendTime.60000" +
|
||||
// ",ntcp.transmitTime.60000" +
|
||||
// ",ntcp.sendBacklogTime.60000" +
|
||||
// ",ntcp.receiveTime.60000" +
|
||||
// ",transport.sendMessageFailureLifetime.60000" +
|
||||
// ",transport.sendProcessingTime.60000";
|
||||
|
||||
private String adjustDatabases(String oldSpecs) {
|
||||
String spec = _context.getProperty("stat.summaries", DEFAULT_DATABASES);
|
||||
@ -142,16 +142,17 @@ public class StatSummarizer implements Runnable {
|
||||
}
|
||||
|
||||
public boolean renderRatePng(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, boolean showCredit) throws IOException {
|
||||
long end = _context.clock().now();
|
||||
long end = _context.clock().now() - 60*1000;
|
||||
if (periodCount <= 0) periodCount = SummaryListener.PERIODS;
|
||||
if (periodCount > SummaryListener.PERIODS)
|
||||
periodCount = SummaryListener.PERIODS;
|
||||
long period = 60*1000;
|
||||
long start = end - period*periodCount;
|
||||
long begin = System.currentTimeMillis();
|
||||
//long begin = System.currentTimeMillis();
|
||||
try {
|
||||
RrdGraphDef def = new RrdGraphDef();
|
||||
def.setTimePeriod(start/1000, end/1000);
|
||||
def.setTimePeriod(start/1000, 0);
|
||||
def.setBaseValue(1024);
|
||||
String title = "Bandwidth usage";
|
||||
if (!hideTitle)
|
||||
def.setTitle(title);
|
||||
@ -159,15 +160,15 @@ public class StatSummarizer implements Runnable {
|
||||
String recvName = SummaryListener.createName(_context, "bw.recvRate.60000");
|
||||
def.datasource(sendName, sendName, sendName, "AVERAGE", "MEMORY");
|
||||
def.datasource(recvName, recvName, recvName, "AVERAGE", "MEMORY");
|
||||
def.area(sendName, Color.BLUE, "Outbound bytes/second");
|
||||
//def.line(sendName, Color.BLUE, "Outbound bytes/second", 3);
|
||||
//def.line(recvName, Color.RED, "Inbound bytes/second@r", 3);
|
||||
def.area(recvName, Color.RED, "Inbound bytes/second@r");
|
||||
def.area(sendName, Color.BLUE, "Outbound bytes/sec");
|
||||
//def.line(sendName, Color.BLUE, "Outbound bytes/sec", 3);
|
||||
def.line(recvName, Color.RED, "Inbound bytes/sec@r", 3);
|
||||
//def.area(recvName, Color.RED, "Inbound bytes/sec@r");
|
||||
if (!hideLegend) {
|
||||
def.gprint(sendName, "AVERAGE", "outbound average: @2@sbytes/second");
|
||||
def.gprint(sendName, "MAX", " max: @2@sbytes/second@r");
|
||||
def.gprint(recvName, "AVERAGE", "inbound average: @2bytes/second@s");
|
||||
def.gprint(recvName, "MAX", " max: @2@sbytes/second@r");
|
||||
def.gprint(sendName, "AVERAGE", "out average: @2@sbytes/sec");
|
||||
def.gprint(sendName, "MAX", " max: @2@sbytes/sec@r");
|
||||
def.gprint(recvName, "AVERAGE", "in average: @2@sbytes/sec");
|
||||
def.gprint(recvName, "MAX", " max: @2@sbytes/sec@r");
|
||||
}
|
||||
if (!showCredit)
|
||||
def.setShowSignature(false);
|
||||
@ -188,7 +189,7 @@ public class StatSummarizer implements Runnable {
|
||||
data = graph.getPNGBytes();
|
||||
else
|
||||
data = graph.getPNGBytes(width, height);
|
||||
long timeToPlot = System.currentTimeMillis() - begin;
|
||||
//long timeToPlot = System.currentTimeMillis() - begin;
|
||||
out.write(data);
|
||||
//File t = File.createTempFile("jrobinData", ".xml");
|
||||
//_listener.getData().dumpXml(new FileOutputStream(t));
|
||||
@ -201,6 +202,9 @@ public class StatSummarizer implements Runnable {
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error rendering", ioe);
|
||||
throw ioe;
|
||||
} catch (OutOfMemoryError oom) {
|
||||
_log.error("Error rendering", oom);
|
||||
throw new IOException("Error plotting: " + oom.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,7 +154,7 @@ class SummaryRenderer {
|
||||
*
|
||||
*/
|
||||
public static synchronized void render(I2PAppContext ctx, OutputStream out, String filename) throws IOException {
|
||||
long end = ctx.clock().now();
|
||||
long end = ctx.clock().now() - 60*1000;
|
||||
long start = end - 60*1000*SummaryListener.PERIODS;
|
||||
long begin = System.currentTimeMillis();
|
||||
try {
|
||||
@ -174,17 +174,26 @@ class SummaryRenderer {
|
||||
}
|
||||
public void render(OutputStream out) throws IOException { render(out, -1, -1, false, false, false, false, -1, true); }
|
||||
public void render(OutputStream out, int width, int height, boolean hideLegend, boolean hideGrid, boolean hideTitle, boolean showEvents, int periodCount, boolean showCredit) throws IOException {
|
||||
long end = _listener.now();
|
||||
long end = _listener.now() - 60*1000;
|
||||
if (periodCount <= 0) periodCount = SummaryListener.PERIODS;
|
||||
if (periodCount > SummaryListener.PERIODS)
|
||||
periodCount = SummaryListener.PERIODS;
|
||||
long start = end - _listener.getRate().getPeriod()*periodCount;
|
||||
long begin = System.currentTimeMillis();
|
||||
//long begin = System.currentTimeMillis();
|
||||
try {
|
||||
RrdGraphDef def = new RrdGraphDef();
|
||||
def.setTimePeriod(start/1000, end/1000);
|
||||
String title = _listener.getRate().getRateStat().getName() + " averaged for "
|
||||
+ DataHelper.formatDuration(_listener.getRate().getPeriod());
|
||||
def.setTimePeriod(start/1000, 0);
|
||||
String name = _listener.getRate().getRateStat().getName();
|
||||
// heuristic to set K=1024
|
||||
if ((name.startsWith("bw.") || name.indexOf("Size") >= 0 || name.indexOf("Bps") >= 0)
|
||||
&& !showEvents)
|
||||
def.setBaseValue(1024);
|
||||
String title = name;
|
||||
if (showEvents)
|
||||
title = title + " events in ";
|
||||
else
|
||||
title = title + " averaged for ";
|
||||
title = title + DataHelper.formatDuration(_listener.getRate().getPeriod());
|
||||
if (!hideTitle)
|
||||
def.setTitle(title);
|
||||
String path = _listener.getData().getPath();
|
||||
@ -203,8 +212,9 @@ class SummaryRenderer {
|
||||
def.datasource(plotName, path, plotName, "AVERAGE", "MEMORY");
|
||||
def.area(plotName, Color.BLUE, descr + "@r");
|
||||
if (!hideLegend) {
|
||||
def.gprint(plotName, "AVERAGE", "average: @2@s");
|
||||
def.gprint(plotName, "MAX", " max: @2@s@r");
|
||||
def.gprint(plotName, "AVERAGE", "avg: @2@s");
|
||||
def.gprint(plotName, "MAX", " max: @2@s");
|
||||
def.gprint(plotName, "LAST", " now: @2@s@r");
|
||||
}
|
||||
if (!showCredit)
|
||||
def.setShowSignature(false);
|
||||
@ -233,7 +243,7 @@ class SummaryRenderer {
|
||||
data = graph.getPNGBytes();
|
||||
else
|
||||
data = graph.getPNGBytes(width, height);
|
||||
long timeToPlot = System.currentTimeMillis() - begin;
|
||||
//long timeToPlot = System.currentTimeMillis() - begin;
|
||||
out.write(data);
|
||||
//File t = File.createTempFile("jrobinData", ".xml");
|
||||
//_listener.getData().dumpXml(new FileOutputStream(t));
|
||||
@ -245,6 +255,9 @@ class SummaryRenderer {
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error rendering", ioe);
|
||||
throw ioe;
|
||||
} catch (OutOfMemoryError oom) {
|
||||
_log.error("Error rendering", oom);
|
||||
throw new IOException("Error plotting: " + oom.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,9 @@ package net.i2p.router.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.TrustedUpdate;
|
||||
@ -102,7 +105,9 @@ public class UpdateHandler {
|
||||
private void update() {
|
||||
_startedOn = -1;
|
||||
_status = "<b>Updating</b><br />";
|
||||
String updateURL = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_URL, ConfigUpdateHandler.DEFAULT_UPDATE_URL);
|
||||
String updateURL = selectUpdateURL();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Selected update URL: " + updateURL);
|
||||
boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue();
|
||||
String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST);
|
||||
String port = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT);
|
||||
@ -162,17 +167,35 @@ public class UpdateHandler {
|
||||
}
|
||||
}
|
||||
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {
|
||||
_log.log(Log.CRIT, "Update did not download completely (" + bytesTransferred + " with "
|
||||
_log.log(Log.CRIT, "Update from " + url + " did not download completely (" + bytesTransferred + " with "
|
||||
+ bytesRemaining + " after " + currentAttempt + " tries)");
|
||||
|
||||
_status = "<b>Transfer failed</b><br />";
|
||||
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false");
|
||||
}
|
||||
public void headerReceived(String url, int attemptNum, String key, String val) {}
|
||||
public void attempting(String url) {}
|
||||
}
|
||||
|
||||
private void restart() {
|
||||
_context.router().addShutdownTask(new ConfigServiceHandler.UpdateWrapperManagerTask(Router.EXIT_GRACEFUL_RESTART));
|
||||
_context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART);
|
||||
}
|
||||
|
||||
private String selectUpdateURL() {
|
||||
String URLs = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_URL, ConfigUpdateHandler.DEFAULT_UPDATE_URL);
|
||||
StringTokenizer tok = new StringTokenizer(URLs, " ,\r\n");
|
||||
List URLList = new ArrayList();
|
||||
while (tok.hasMoreTokens())
|
||||
URLList.add(tok.nextToken().trim());
|
||||
int size = URLList.size();
|
||||
_log.log(Log.DEBUG, "Picking update source from " + size + " candidates.");
|
||||
if (size <= 0) {
|
||||
_log.log(Log.WARN, "Update list is empty - no update available");
|
||||
return null;
|
||||
}
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
|
||||
_log.log(Log.DEBUG, "Picked update source " + index + ".");
|
||||
return (String) URLList.get(index);
|
||||
}
|
||||
}
|
||||
|
@ -43,9 +43,21 @@
|
||||
A negative rate means a default limit of 16KBytes per second.</i><br />
|
||||
Bandwidth share percentage:
|
||||
<jsp:getProperty name="nethelper" property="sharePercentageBox" /><br />
|
||||
Sharing a higher percentage will improve your anonymity and help the network<br />
|
||||
<% int share = nethelper.getShareBandwidth();
|
||||
if (share < 12) {
|
||||
out.print("<b>NOTE</b>: You have configured I2P to share only " + share + "KBps. ");
|
||||
out.print("I2P requires at least 12KBps to enable sharing. ");
|
||||
out.print("Please enable sharing (participating in tunnels) by configuring more bandwidth. ");
|
||||
out.print("It improves your anonymity by creating cover traffic, and helps the network.<br />");
|
||||
} else {
|
||||
out.print("You have configured I2P to share " + share + "KBps. ");
|
||||
out.print("The higher the share bandwidth the more you improve your anonymity and help the network.<br />");
|
||||
}
|
||||
%>
|
||||
<p>
|
||||
<input type="submit" name="save" value="Save changes" /> <input type="reset" value="Cancel" /><br />
|
||||
<hr />
|
||||
<!--
|
||||
<b>Enable load testing: </b>
|
||||
<input type="checkbox" name="enableloadtesting" value="true" <jsp:getProperty name="nethelper" property="enableLoadTesting" /> />
|
||||
<p>If enabled, your router will periodically anonymously probe some of your peers
|
||||
@ -54,6 +66,7 @@
|
||||
load testing is fed into the profiles as well as the
|
||||
<a href="oldstats.jsp#test.rtt">test.rtt</a> and related stats.</p>
|
||||
<hr />
|
||||
-->
|
||||
<b>External UDP address:</b> <i><jsp:getProperty name="nethelper" property="udpAddress" /></i><br />
|
||||
<b>Require SSU introductions? </b>
|
||||
<input type="checkbox" name="requireIntroductions" value="true" <jsp:getProperty name="nethelper" property="requireIntroductionsChecked" /> /><br />
|
||||
@ -70,14 +83,21 @@
|
||||
Externally reachable hostname or IP address:
|
||||
<input name ="ntcphost" type="text" size="16" value="<jsp:getProperty name="nethelper" property="ntcphostname" />" />
|
||||
(dyndns and the like are fine)<br />
|
||||
OR use IP address detected by SSU
|
||||
(currently <jsp:getProperty name="nethelper" property="udpIP" />)?
|
||||
<input type="checkbox" name="ntcpAutoIP" value="true" <jsp:getProperty name="nethelper" property="tcpAutoIPChecked" /> /><br />
|
||||
<p>
|
||||
Externally reachable TCP port:
|
||||
<input name ="ntcpport" type="text" size="6" value="<jsp:getProperty name="nethelper" property="ntcpport" />" /><br />
|
||||
OR use the same port configured for SSU
|
||||
(currently <jsp:getProperty name="nethelper" property="udpPort" />)?
|
||||
<input type="checkbox" name="ntcpAutoPort" value="true" <jsp:getProperty name="nethelper" property="tcpAutoPortChecked" /> /><br />
|
||||
<p>You do <i>not</i> need to allow inbound TCP connections - outbound connections work with no
|
||||
configuration. However, if you want to receive inbound TCP connections, you <b>must</b> poke a hole
|
||||
in your NAT or firewall for unsolicited TCP connections. If you specify the wrong IP address or
|
||||
hostname, or do not properly configure your NAT or firewall, your network performance will degrade
|
||||
substantially. When in doubt, leave the hostname and port number blank.</p>
|
||||
<p><b>Note: changing this setting will terminate all of your connections and effectively
|
||||
<p><b>Note: changing any of these settings will terminate all of your connections and effectively
|
||||
restart your router.</b>
|
||||
<hr />
|
||||
<!--
|
||||
|
@ -78,22 +78,26 @@ function toggleAll(category)
|
||||
<table>
|
||||
<% while (statshelper.hasMoreStats()) {
|
||||
while (statshelper.groupRequired()) { %>
|
||||
<tr><td valign="top" align="left" colspan="2">
|
||||
<tr><td valign="top" align="left" colspan="3">
|
||||
<b><%=statshelper.getCurrentGroupName()%></b>
|
||||
(<a href="javascript: void(null);" onclick="toggleAll('<%=statshelper.getCurrentGroupName()%>')">toggle all</a>)
|
||||
</td></tr><%
|
||||
</td></tr><tr><td>Log</td><td>Graph</td><td></td></tr><%
|
||||
} // end iterating over required groups for the current stat %>
|
||||
<tr><td valign="top" align="left">
|
||||
<input id="<%=statshelper.getCurrentGroupName()%>" type="checkbox" name="statList" value="<%=statshelper.getCurrentStatName()%>" <%
|
||||
if (statshelper.getCurrentIsLogged()) { %>checked="true" <% } %>/></td>
|
||||
<td valign="top" align="left">
|
||||
<% if (statshelper.getCurrentCanBeGraphed()) { %>
|
||||
<input id="<%=statshelper.getCurrentGroupName()%>" type="checkbox" name="graphList" value="<%=statshelper.getCurrentGraphName()%>" <%
|
||||
if (statshelper.getCurrentIsGraphed()) { %>checked="true" <% } %>/><% } %></td>
|
||||
<td valign="top" align="left"><b><%=statshelper.getCurrentStatName()%>:</b><br />
|
||||
<%=statshelper.getCurrentStatDescription()%></td></tr><%
|
||||
} // end iterating over all stats %>
|
||||
<tr><td colspan="2"><hr /></td></tr>
|
||||
<tr><td colspan="3"><hr /></td></tr>
|
||||
<tr><td><input type="checkbox" name="explicitFilter" /></td>
|
||||
<td>Advanced filter:
|
||||
<td colspan="2">Advanced filter:
|
||||
<input type="text" name="explicitFilterValue" value="<%=statshelper.getExplicitFilter()%>" size="40" /></td></tr>
|
||||
<tr><td colspan="2"><hr /></td></tr>
|
||||
<tr><td colspan="3"><hr /></td></tr>
|
||||
<tr><td><input type="submit" name="shouldsave" value="Save changes" /> </td>
|
||||
<td><input type="reset" value="Cancel" /></td></tr>
|
||||
</form>
|
||||
@ -101,4 +105,4 @@ function toggleAll(category)
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
@ -32,17 +32,17 @@
|
||||
<input type="text" size="60" name="newsURL" value="<jsp:getProperty name="updatehelper" property="newsURL" />"><br />
|
||||
Refresh frequency:
|
||||
<jsp:getProperty name="updatehelper" property="refreshFrequencySelectBox" /><br />
|
||||
Update URL:
|
||||
<input type="text" size="60" name="updateURL" value="<jsp:getProperty name="updatehelper" property="updateURL" />"><br />
|
||||
Update policy:
|
||||
<jsp:getProperty name="updatehelper" property="updatePolicySelectBox" /><br />
|
||||
Update through the eepProxy?
|
||||
<p>Update through the eepProxy?
|
||||
<jsp:getProperty name="updatehelper" property="updateThroughProxy" /><br />
|
||||
eepProxy host: <input type="text" size="10" name="proxyHost" value="<jsp:getProperty name="updatehelper" property="proxyHost" />" /><br />
|
||||
eepProxy port: <input type="text" size="4" name="proxyPort" value="<jsp:getProperty name="updatehelper" property="proxyPort" />" /><br />
|
||||
<!-- prompt for the eepproxy -->
|
||||
Trusted keys:
|
||||
<textarea name="trustedKeys" disabled="true" cols="60" rows="2"><jsp:getProperty name="updatehelper" property="trustedKeys" /></textarea>
|
||||
eepProxy port: <input type="text" size="4" name="proxyPort" value="<jsp:getProperty name="updatehelper" property="proxyPort" />" /></p>
|
||||
<p>Update URLs:<br />
|
||||
<textarea name="updateURL" cols="90" rows="4"><jsp:getProperty name="updatehelper" property="updateURL" /></textarea></p>
|
||||
<p>Trusted keys:</br />
|
||||
<textarea name="trustedKeys" cols="90" rows="4"><jsp:getProperty name="updatehelper" property="trustedKeys" /></textarea></p>
|
||||
<br />
|
||||
<input type="submit" name="action" value="Save" />
|
||||
</form>
|
||||
</div>
|
||||
|
@ -5,6 +5,7 @@
|
||||
<html><head>
|
||||
<title>I2P Router Console - home</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
<link rel="shortcut icon" href="favicon.ico" />
|
||||
</head><body>
|
||||
<%
|
||||
if (System.getProperty("router.consoleNonce") == null) {
|
||||
|
@ -24,7 +24,7 @@
|
||||
<!-- Could not find docs/toolbar.html! -->
|
||||
<a href="susimail/susimail">Susimail</a> |
|
||||
<a href="susidns/index.jsp">SusiDNS</a> |
|
||||
<a href="syndie/">Syndie</a> |
|
||||
<!-- <a href="syndie/">Syndie</a> | -->
|
||||
<a href="i2psnark/">I2PSnark</a> |
|
||||
<a href="http://localhost:7658/">My Eepsite</a> <br>
|
||||
<a href="i2ptunnel/index.jsp">I2PTunnel</a> |
|
||||
|
@ -43,13 +43,16 @@
|
||||
<b>Failing:</b> <jsp:getProperty name="helper" property="failingPeers" /><br />
|
||||
<!-- <b>Shitlisted:</b> <jsp:getProperty name="helper" property="shitlistedPeers" /><br /> -->
|
||||
<b>Known:</b> <jsp:getProperty name="helper" property="allPeers" /><br /><%
|
||||
if (helper.getActivePeers() <= 0) {
|
||||
if (helper.getActivePeers() <= 0) {
|
||||
%><b><a href="config.jsp">check your NAT/firewall</a></b><br /><%
|
||||
}
|
||||
}
|
||||
// If showing the reseed link is allowed
|
||||
if (helper.allowReseed()) {
|
||||
if ("true".equals(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false"))) {
|
||||
out.print(" <i>reseeding</i>");
|
||||
// While reseed occurring, show status message instead
|
||||
out.print("<i>" + System.getProperty("net.i2p.router.web.ReseedHandler.statusMessage","") + "</i><br />");
|
||||
} else {
|
||||
// While no reseed occurring, show reseed link
|
||||
long nonce = new java.util.Random().nextLong();
|
||||
String prev = System.getProperty("net.i2p.router.web.ReseedHandler.nonce");
|
||||
if (prev != null) System.setProperty("net.i2p.router.web.ReseedHandler.noncePrev", prev);
|
||||
@ -59,7 +62,14 @@
|
||||
uri = uri + "&reseedNonce=" + nonce;
|
||||
else
|
||||
uri = uri + "?reseedNonce=" + nonce;
|
||||
out.print(" <a href=\"" + uri + "\">reseed</a>");
|
||||
out.print(" <a href=\"" + uri + "\">reseed</a><br />");
|
||||
}
|
||||
}
|
||||
// If a new reseed ain't running, and the last reseed had errors, show error message
|
||||
if ("false".equals(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false"))) {
|
||||
String reseedErrorMessage = System.getProperty("net.i2p.router.web.ReseedHandler.errorMessage","");
|
||||
if (reseedErrorMessage.length() > 0) {
|
||||
out.print("<i>" + reseedErrorMessage + "</i><br />");
|
||||
}
|
||||
}
|
||||
%><hr />
|
||||
|
@ -112,12 +112,15 @@ public class SAMHandlerFactory {
|
||||
case 1:
|
||||
handler = new SAMv1Handler(s, verMajor, verMinor, i2cpProps);
|
||||
break;
|
||||
case 2:
|
||||
handler = new SAMv2Handler(s, verMajor, verMinor, i2cpProps);
|
||||
break;
|
||||
default:
|
||||
_log.error("BUG! Trying to initialize the wrong SAM version!");
|
||||
throw new SAMException("BUG! (in handler instantiation)");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
_log.error("Error creating the v1 handler", e);
|
||||
_log.error("Error creating the handler for version "+verMajor, e);
|
||||
throw new SAMException("IOException caught during SAM handler instantiation");
|
||||
}
|
||||
return handler;
|
||||
@ -133,15 +136,16 @@ public class SAMHandlerFactory {
|
||||
|| (maxMajor == -1) || (maxMinor == -1)) {
|
||||
return null;
|
||||
}
|
||||
if (minMajor > maxMajor) {
|
||||
return null;
|
||||
} else if ((minMajor == maxMajor) && (minMinor > maxMinor)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((minMajor >= 1) && (minMinor >= 0)) {
|
||||
return "1.0";
|
||||
}
|
||||
if ((minMinor >= 10) || (maxMinor >= 10)) return null ;
|
||||
|
||||
float fminVer = (float) minMajor + (float) minMinor / 10 ;
|
||||
float fmaxVer = (float) maxMajor + (float) maxMinor / 10 ;
|
||||
|
||||
|
||||
if ( ( fminVer <= 2.0 ) && ( fmaxVer >= 2.0 ) ) return "2.0" ;
|
||||
|
||||
if ( ( fminVer <= 1.0 ) && ( fmaxVer >= 1.0 ) ) return "1.0" ;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -15,14 +15,31 @@ import net.i2p.data.Destination;
|
||||
/**
|
||||
* Interface for sending streaming data to a SAM client
|
||||
*/
|
||||
|
||||
public interface SAMStreamReceiver {
|
||||
/**
|
||||
* Sends the result of a stream send operation
|
||||
*/
|
||||
public void streamSendAnswer( int id, String result, String bufferState ) throws IOException;
|
||||
|
||||
/**
|
||||
* Notifies that the outwards buffer is free for writing
|
||||
*/
|
||||
public void notifyStreamSendBufferFree( int id ) throws IOException;
|
||||
|
||||
/**
|
||||
* Notify about a new incoming connection
|
||||
*
|
||||
* @param id New connection id
|
||||
*/
|
||||
public void notifyStreamConnection(int id, Destination dest) throws IOException;
|
||||
public void notifyStreamIncomingConnection ( int id, Destination dest ) throws IOException;
|
||||
|
||||
/**
|
||||
* Notify about a new outgoing connection
|
||||
*
|
||||
* @param id New connection id
|
||||
*/
|
||||
public void notifyStreamOutgoingConnection(int id, String result, String msg) throws IOException;
|
||||
|
||||
/**
|
||||
* Send a byte array to a SAM client.
|
||||
|
@ -47,13 +47,13 @@ public class SAMStreamSession {
|
||||
|
||||
private final static Log _log = new Log(SAMStreamSession.class);
|
||||
|
||||
private final static int SOCKET_HANDLER_BUF_SIZE = 32768;
|
||||
protected final static int SOCKET_HANDLER_BUF_SIZE = 32768;
|
||||
|
||||
private SAMStreamReceiver recv = null;
|
||||
protected SAMStreamReceiver recv = null;
|
||||
|
||||
private SAMStreamSessionServer server = null;
|
||||
|
||||
private I2PSocketManager socketMgr = null;
|
||||
protected I2PSocketManager socketMgr = null;
|
||||
|
||||
private Object handlersMapLock = new Object();
|
||||
/** stream id (Long) to SAMStreamSessionSocketReader */
|
||||
@ -65,13 +65,14 @@ public class SAMStreamSession {
|
||||
private int lastNegativeId = 0;
|
||||
|
||||
// Can we create outgoing connections?
|
||||
private boolean canCreate = false;
|
||||
protected boolean canCreate = false;
|
||||
|
||||
/**
|
||||
* should we flush every time we get a STREAM SEND, or leave that up to
|
||||
* the streaming lib to decide?
|
||||
*/
|
||||
private boolean forceFlush = false;
|
||||
protected boolean forceFlush = false;
|
||||
|
||||
public static String PROP_FORCE_FLUSH = "sam.forceFlush";
|
||||
public static String DEFAULT_FORCE_FLUSH = "false";
|
||||
|
||||
@ -189,7 +190,7 @@ public class SAMStreamSession {
|
||||
* @throws InterruptedIOException if the connection timeouts
|
||||
* @throws I2PException if there's another I2P-related error
|
||||
*/
|
||||
public boolean connect(int id, String dest, Properties props) throws I2PException, ConnectException, NoRouteToHostException, DataFormatException, InterruptedIOException, SAMInvalidDirectionException {
|
||||
public boolean connect ( int id, String dest, Properties props ) throws I2PException, ConnectException, NoRouteToHostException, DataFormatException, InterruptedIOException, SAMInvalidDirectionException, IOException {
|
||||
if (!canCreate) {
|
||||
_log.debug("Trying to create an outgoing connection using a receive-only session");
|
||||
throw new SAMInvalidDirectionException("Trying to create connections through a receive-only session");
|
||||
@ -208,10 +209,15 @@ public class SAMStreamSession {
|
||||
opts.setConnectTimeout(60 * 1000);
|
||||
|
||||
_log.debug("Connecting new I2PSocket...");
|
||||
|
||||
// blocking connection (SAMv1)
|
||||
|
||||
I2PSocket i2ps = socketMgr.connect(d, opts);
|
||||
|
||||
createSocketHandler(i2ps, id);
|
||||
|
||||
recv.notifyStreamOutgoingConnection ( id, "OK", null );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -277,7 +283,7 @@ public class SAMStreamSession {
|
||||
*
|
||||
* @return An id associated to the socket handler
|
||||
*/
|
||||
private int createSocketHandler(I2PSocket s, int id) {
|
||||
protected int createSocketHandler ( I2PSocket s, int id ) {
|
||||
SAMStreamSessionSocketReader reader = null;
|
||||
StreamSender sender = null;
|
||||
if (id == 0) {
|
||||
@ -285,8 +291,8 @@ public class SAMStreamSession {
|
||||
}
|
||||
|
||||
try {
|
||||
reader = new SAMStreamSessionSocketReader(s, id);
|
||||
sender = new StreamSender(s, id);
|
||||
reader = newSAMStreamSessionSocketReader(s, id);
|
||||
sender = newStreamSender(s, id);
|
||||
} catch (IOException e) {
|
||||
_log.error("IOException when creating SAM STREAM session socket handler", e);
|
||||
recv.stopStreamReceiving();
|
||||
@ -318,7 +324,7 @@ public class SAMStreamSession {
|
||||
*
|
||||
* @param id Handler id
|
||||
*/
|
||||
private SAMStreamSessionSocketReader getSocketReader(int id) {
|
||||
protected SAMStreamSessionSocketReader getSocketReader ( int id ) {
|
||||
synchronized (handlersMapLock) {
|
||||
return (SAMStreamSessionSocketReader)handlersMap.get(new Integer(id));
|
||||
}
|
||||
@ -334,7 +340,7 @@ public class SAMStreamSession {
|
||||
*
|
||||
* @param id Handler id
|
||||
*/
|
||||
private boolean checkSocketHandlerId(int id) {
|
||||
protected boolean checkSocketHandlerId ( int id ) {
|
||||
synchronized (handlersMapLock) {
|
||||
return (!(handlersMap.get(new Integer(id)) == null));
|
||||
}
|
||||
@ -345,7 +351,7 @@ public class SAMStreamSession {
|
||||
*
|
||||
* @param id Handler id to be removed
|
||||
*/
|
||||
private void removeSocketHandler(int id) {
|
||||
protected void removeSocketHandler ( int id ) {
|
||||
SAMStreamSessionSocketReader reader = null;
|
||||
StreamSender sender = null;
|
||||
|
||||
@ -446,7 +452,8 @@ public class SAMStreamSession {
|
||||
}
|
||||
|
||||
_log.debug("New connection id: " + id);
|
||||
recv.notifyStreamConnection(id, i2ps.getPeerDestination());
|
||||
|
||||
recv.notifyStreamIncomingConnection ( id, i2ps.getPeerDestination() );
|
||||
} catch (I2PException e) {
|
||||
_log.debug("Caught I2PException", e);
|
||||
break;
|
||||
@ -469,29 +476,62 @@ public class SAMStreamSession {
|
||||
|
||||
}
|
||||
|
||||
|
||||
boolean setReceiveLimit ( int id, long limit, boolean nolimit )
|
||||
{
|
||||
_log.debug ( "Protocol v1 does not support a receive limit for streams" );
|
||||
return false ;
|
||||
}
|
||||
|
||||
/**
|
||||
* SAM STREAM socket handler, running in its own thread. It forwards
|
||||
* SAM STREAM socket reader, running in its own thread. It forwards
|
||||
* forward data to/from an I2P socket.
|
||||
*
|
||||
* @author human
|
||||
*/
|
||||
public class SAMStreamSessionSocketReader implements Runnable {
|
||||
|
||||
private I2PSocket i2pSocket = null;
|
||||
protected I2PSocket i2pSocket = null;
|
||||
|
||||
private Object runningLock = new Object();
|
||||
private boolean stillRunning = true;
|
||||
protected Object runningLock = new Object();
|
||||
|
||||
protected boolean stillRunning = true;
|
||||
|
||||
protected int id;
|
||||
|
||||
private int id;
|
||||
|
||||
/**
|
||||
* Create a new SAM STREAM session socket reader
|
||||
*
|
||||
* @param s Socket to be handled
|
||||
* @param id Unique id assigned to the handler
|
||||
*/
|
||||
public SAMStreamSessionSocketReader(I2PSocket s, int id) throws IOException {
|
||||
_log.debug("Instantiating new SAM STREAM session socket handler");
|
||||
public SAMStreamSessionSocketReader ( I2PSocket s, int id ) throws IOException {}
|
||||
|
||||
/**
|
||||
* Stop a SAM STREAM session socket reader thread immediately.
|
||||
*/
|
||||
public void stopRunning() {}
|
||||
|
||||
public void run() {}
|
||||
|
||||
}
|
||||
|
||||
protected SAMStreamSessionSocketReader
|
||||
newSAMStreamSessionSocketReader ( I2PSocket s, int id ) throws IOException {
|
||||
return new SAMv1StreamSessionSocketReader ( s, id );
|
||||
}
|
||||
|
||||
public class SAMv1StreamSessionSocketReader extends SAMStreamSessionSocketReader {
|
||||
/**
|
||||
* Create a new SAM STREAM session socket reader
|
||||
*
|
||||
* @param s Socket to be handled
|
||||
* @param id Unique id assigned to the handler
|
||||
*/
|
||||
|
||||
public SAMv1StreamSessionSocketReader ( I2PSocket s, int id ) throws IOException {
|
||||
super(s, id);
|
||||
_log.debug("Instantiating new SAM STREAM session socket reader");
|
||||
|
||||
i2pSocket = s;
|
||||
this.id = id;
|
||||
@ -507,6 +547,7 @@ public class SAMStreamSession {
|
||||
if (stillRunning) {
|
||||
stillRunning = false;
|
||||
}
|
||||
runningLock.notifyAll() ;
|
||||
}
|
||||
}
|
||||
|
||||
@ -558,7 +599,40 @@ public class SAMStreamSession {
|
||||
* Lets us push data through the stream without blocking, (even after exceeding
|
||||
* the I2PSocket's buffer)
|
||||
*/
|
||||
private class StreamSender implements Runnable {
|
||||
protected class StreamSender implements Runnable {
|
||||
public StreamSender ( I2PSocket s, int id ) throws IOException {}
|
||||
|
||||
/**
|
||||
* Send bytes through the SAM STREAM session socket sender
|
||||
*
|
||||
* @param data Data to be sent
|
||||
*
|
||||
* @throws IOException if the client didnt provide enough data
|
||||
*/
|
||||
public void sendBytes ( InputStream in, int size ) throws IOException {}
|
||||
|
||||
|
||||
/**
|
||||
* Stop a SAM STREAM session socket sender thread immediately
|
||||
*
|
||||
*/
|
||||
public void stopRunning() {}
|
||||
|
||||
/**
|
||||
* Stop a SAM STREAM session socket sender gracefully: stop the
|
||||
* sender thread once all pending data has been sent.
|
||||
*/
|
||||
public void shutDownGracefully() {}
|
||||
|
||||
public void run() {}
|
||||
}
|
||||
|
||||
protected StreamSender newStreamSender ( I2PSocket s, int id ) throws IOException {
|
||||
return new v1StreamSender ( s, id ) ;
|
||||
}
|
||||
|
||||
protected class v1StreamSender extends StreamSender
|
||||
{
|
||||
private List _data;
|
||||
private int _id;
|
||||
private ByteCache _cache;
|
||||
@ -567,7 +641,8 @@ public class SAMStreamSession {
|
||||
private Object runningLock = new Object();
|
||||
private I2PSocket i2pSocket = null;
|
||||
|
||||
public StreamSender(I2PSocket s, int id) throws IOException {
|
||||
public v1StreamSender ( I2PSocket s, int id ) throws IOException {
|
||||
super ( s, id );
|
||||
_data = new ArrayList(1);
|
||||
_id = id;
|
||||
_cache = ByteCache.getInstance(4, 32*1024);
|
||||
|
@ -35,6 +35,8 @@ import net.i2p.util.Log;
|
||||
* @author human
|
||||
*/
|
||||
public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramReceiver, SAMStreamReceiver {
|
||||
protected int verMajorId = 1;
|
||||
protected int verMinorId = 0;
|
||||
|
||||
private final static Log _log = new Log(SAMv1Handler.class);
|
||||
|
||||
@ -42,7 +44,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
|
||||
private SAMRawSession rawSession = null;
|
||||
private SAMDatagramSession datagramSession = null;
|
||||
private SAMStreamSession streamSession = null;
|
||||
protected SAMStreamSession streamSession = null;
|
||||
|
||||
private long _id;
|
||||
private static volatile long __id = 0;
|
||||
@ -74,11 +76,15 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
_id = ++__id;
|
||||
_log.debug("SAM version 1 handler instantiated");
|
||||
|
||||
if ((this.verMajor != 1) || (this.verMinor != 0)) {
|
||||
if ( ! verifVersion() ) {
|
||||
throw new SAMException("BUG! Wrong protocol version!");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean verifVersion() {
|
||||
return ( verMajor == 1 && verMinor == 0 ) ;
|
||||
}
|
||||
|
||||
public void handle() {
|
||||
String msg = null;
|
||||
String domain = null;
|
||||
@ -248,7 +254,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
}
|
||||
props.remove("DIRECTION");
|
||||
|
||||
streamSession = new SAMStreamSession(destKeystream, dir,props,this);
|
||||
streamSession = newSAMStreamSession(destKeystream, dir,props);
|
||||
} else {
|
||||
_log.debug("Unrecognized SESSION STYLE: \"" + style +"\"");
|
||||
return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"Unrecognized SESSION STYLE\"\n");
|
||||
@ -275,6 +281,13 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SAMStreamSession newSAMStreamSession(String destKeystream, String direction, Properties props )
|
||||
throws IOException, DataFormatException, SAMException
|
||||
{
|
||||
return new SAMStreamSession(destKeystream, direction, props, this) ;
|
||||
}
|
||||
|
||||
/* Parse and execute a DEST message*/
|
||||
private boolean execDestMessage(String opcode, Properties props) {
|
||||
|
||||
@ -489,7 +502,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
}
|
||||
|
||||
/* Parse and execute a STREAM message */
|
||||
private boolean execStreamMessage(String opcode, Properties props) {
|
||||
protected boolean execStreamMessage(String opcode, Properties props) {
|
||||
if (streamSession == null) {
|
||||
_log.error("STREAM message received, but no STREAM session exists");
|
||||
return false;
|
||||
@ -508,7 +521,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
}
|
||||
}
|
||||
|
||||
private boolean execStreamSend(Properties props) {
|
||||
protected boolean execStreamSend(Properties props) {
|
||||
if (props == null) {
|
||||
_log.debug("No parameters specified in STREAM SEND message");
|
||||
return false;
|
||||
@ -570,7 +583,7 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
}
|
||||
}
|
||||
|
||||
private boolean execStreamConnect(Properties props) {
|
||||
protected boolean execStreamConnect(Properties props) {
|
||||
if (props == null) {
|
||||
_log.debug("No parameters specified in STREAM CONNECT message");
|
||||
return false;
|
||||
@ -604,39 +617,38 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
props.remove("DESTINATION");
|
||||
|
||||
try {
|
||||
if (!streamSession.connect(id, dest, props)) {
|
||||
_log.debug("STREAM connection failed");
|
||||
return false;
|
||||
try {
|
||||
if (!streamSession.connect(id, dest, props)) {
|
||||
_log.debug("STREAM connection failed");
|
||||
return false;
|
||||
}
|
||||
} catch (DataFormatException e) {
|
||||
_log.debug("Invalid destination in STREAM CONNECT message");
|
||||
notifyStreamOutgoingConnection ( id, "INVALID_KEY", null );
|
||||
} catch (SAMInvalidDirectionException e) {
|
||||
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
||||
notifyStreamOutgoingConnection ( id, "INVALID_DIRECTION", null );
|
||||
} catch (ConnectException e) {
|
||||
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
||||
notifyStreamOutgoingConnection ( id, "CONNECTION_REFUSED", null );
|
||||
} catch (NoRouteToHostException e) {
|
||||
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
||||
notifyStreamOutgoingConnection ( id, "CANT_REACH_PEER", null );
|
||||
} catch (InterruptedIOException e) {
|
||||
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
||||
notifyStreamOutgoingConnection ( id, "TIMEOUT", null );
|
||||
} catch (I2PException e) {
|
||||
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
||||
notifyStreamOutgoingConnection ( id, "I2P_ERROR", null );
|
||||
}
|
||||
return writeString("STREAM STATUS RESULT=OK ID=" + id + "\n");
|
||||
} catch (DataFormatException e) {
|
||||
_log.debug("Invalid destination in STREAM CONNECT message");
|
||||
return writeString("STREAM STATUS RESULT=INVALID_KEY ID="
|
||||
+ id + "\n");
|
||||
} catch (SAMInvalidDirectionException e) {
|
||||
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
||||
return writeString("STREAM STATUS RESULT=INVALID_DIRECTION ID="
|
||||
+ id + "\n");
|
||||
} catch (ConnectException e) {
|
||||
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
||||
return writeString("STREAM STATUS RESULT=CONNECTION_REFUSED ID="
|
||||
+ id + "\n");
|
||||
} catch (NoRouteToHostException e) {
|
||||
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
||||
return writeString("STREAM STATUS RESULT=CANT_REACH_PEER ID="
|
||||
+ id + "\n");
|
||||
} catch (InterruptedIOException e) {
|
||||
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
||||
return writeString("STREAM STATUS RESULT=TIMEOUT ID="
|
||||
+ id + "\n");
|
||||
} catch (I2PException e) {
|
||||
_log.debug("STREAM CONNECT failed: " + e.getMessage());
|
||||
return writeString("STREAM STATUS RESULT=I2P_ERROR ID="
|
||||
+ id + "\n");
|
||||
} catch (IOException e) {
|
||||
return false ;
|
||||
}
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
private boolean execStreamClose(Properties props) {
|
||||
protected boolean execStreamClose(Properties props) {
|
||||
if (props == null) {
|
||||
_log.debug("No parameters specified in STREAM CLOSE message");
|
||||
return false;
|
||||
@ -745,7 +757,41 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
}
|
||||
|
||||
// SAMStreamReceiver implementation
|
||||
public void notifyStreamConnection(int id, Destination d) throws IOException {
|
||||
|
||||
public void streamSendAnswer( int id, String result, String bufferState ) throws IOException
|
||||
{
|
||||
if ( streamSession == null )
|
||||
{
|
||||
_log.error ( "BUG! Want to answer to stream SEND, but session is null!" );
|
||||
throw new NullPointerException ( "BUG! STREAM session is null!" );
|
||||
}
|
||||
|
||||
if ( !writeString ( "STREAM SEND ID=" + id
|
||||
+ " RESULT=" + result
|
||||
+ " STATE=" + bufferState
|
||||
+ "\n" ) )
|
||||
{
|
||||
throw new IOException ( "Error notifying connection to SAM client" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void notifyStreamSendBufferFree( int id ) throws IOException
|
||||
{
|
||||
if ( streamSession == null )
|
||||
{
|
||||
_log.error ( "BUG! Stream outgoing buffer is free, but session is null!" );
|
||||
throw new NullPointerException ( "BUG! STREAM session is null!" );
|
||||
}
|
||||
|
||||
if ( !writeString ( "STREAM READY_TO_SEND ID=" + id + "\n" ) )
|
||||
{
|
||||
throw new IOException ( "Error notifying connection to SAM client" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void notifyStreamIncomingConnection(int id, Destination d) throws IOException {
|
||||
if (streamSession == null) {
|
||||
_log.error("BUG! Received stream connection, but session is null!");
|
||||
throw new NullPointerException("BUG! STREAM session is null!");
|
||||
@ -758,6 +804,28 @@ public class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatag
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyStreamOutgoingConnection ( int id, String result, String msg ) throws IOException
|
||||
{
|
||||
if ( streamSession == null )
|
||||
{
|
||||
_log.error ( "BUG! Received stream connection, but session is null!" );
|
||||
throw new NullPointerException ( "BUG! STREAM session is null!" );
|
||||
}
|
||||
|
||||
String msgString = "" ;
|
||||
|
||||
if ( msg != null ) msgString = " MESSAGE=\"" + msg + "\"";
|
||||
|
||||
if ( !writeString ( "STREAM STATUS RESULT="
|
||||
+ result
|
||||
+ " ID=" + id
|
||||
+ msgString
|
||||
+ "\n" ) )
|
||||
{
|
||||
throw new IOException ( "Error notifying connection to SAM client" );
|
||||
}
|
||||
}
|
||||
|
||||
public void receiveStreamBytes(int id, byte data[], int len) throws IOException {
|
||||
if (streamSession == null) {
|
||||
_log.error("Received stream bytes, but session is null!");
|
||||
|
196
apps/sam/java/src/net/i2p/sam/SAMv2Handler.java
Normal file
196
apps/sam/java/src/net/i2p/sam/SAMv2Handler.java
Normal file
@ -0,0 +1,196 @@
|
||||
package net.i2p.sam;
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
* Written by human in 2004 and released into the public domain
|
||||
* with no warranty of any kind, either expressed or implied.
|
||||
* It probably won't make your computer catch on fire, or eat
|
||||
* your children, but it might. Use at your own risk.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ConnectException;
|
||||
import java.net.NoRouteToHostException;
|
||||
import java.net.Socket;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Class able to handle a SAM version 2 client connection.
|
||||
*
|
||||
* @author mkvore
|
||||
*/
|
||||
|
||||
public class SAMv2Handler extends SAMv1Handler implements SAMRawReceiver, SAMDatagramReceiver, SAMStreamReceiver
|
||||
{
|
||||
|
||||
private final static Log _log = new Log ( SAMv2Handler.class );
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SAM version 2 handler. This constructor expects
|
||||
* that the SAM HELLO message has been still answered (and
|
||||
* stripped) from the socket input stream.
|
||||
*
|
||||
* @param s Socket attached to a SAM client
|
||||
* @param verMajor SAM major version to manage (should be 2)
|
||||
* @param verMinor SAM minor version to manage
|
||||
*/
|
||||
public SAMv2Handler ( Socket s, int verMajor, int verMinor ) throws SAMException, IOException
|
||||
{
|
||||
this ( s, verMajor, verMinor, new Properties() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SAM version 2 handler. This constructor expects
|
||||
* that the SAM HELLO message has been still answered (and
|
||||
* stripped) from the socket input stream.
|
||||
*
|
||||
* @param s Socket attached to a SAM client
|
||||
* @param verMajor SAM major version to manage (should be 2)
|
||||
* @param verMinor SAM minor version to manage
|
||||
* @param i2cpProps properties to configure the I2CP connection (host, port, etc)
|
||||
*/
|
||||
|
||||
public SAMv2Handler ( Socket s, int verMajor, int verMinor, Properties i2cpProps ) throws SAMException, IOException
|
||||
{
|
||||
super ( s, verMajor, verMinor, i2cpProps );
|
||||
}
|
||||
|
||||
public boolean verifVersion()
|
||||
{
|
||||
return (verMajor == 2 && verMinor == 0) ;
|
||||
}
|
||||
|
||||
SAMStreamSession newSAMStreamSession(String destKeystream, String direction, Properties props )
|
||||
throws IOException, DataFormatException, SAMException
|
||||
{
|
||||
return new SAMv2StreamSession(destKeystream, direction, props, this) ;
|
||||
}
|
||||
|
||||
|
||||
/* Parse and execute a STREAM message */
|
||||
protected boolean execStreamMessage ( String opcode, Properties props )
|
||||
{
|
||||
if ( streamSession == null )
|
||||
{
|
||||
_log.error ( "STREAM message received, but no STREAM session exists" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( opcode.equals ( "SEND" ) )
|
||||
{
|
||||
return execStreamSend ( props );
|
||||
}
|
||||
else if ( opcode.equals ( "CONNECT" ) )
|
||||
{
|
||||
return execStreamConnect ( props );
|
||||
}
|
||||
else if ( opcode.equals ( "CLOSE" ) )
|
||||
{
|
||||
return execStreamClose ( props );
|
||||
}
|
||||
else if ( opcode.equals ( "RECEIVE") )
|
||||
{
|
||||
return execStreamReceive( props );
|
||||
}
|
||||
else
|
||||
{
|
||||
_log.debug ( "Unrecognized RAW message opcode: \""
|
||||
+ opcode + "\"" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private boolean execStreamReceive ( Properties props )
|
||||
{
|
||||
if ( props == null )
|
||||
{
|
||||
_log.debug ( "No parameters specified in STREAM RECEIVE message" );
|
||||
return false;
|
||||
}
|
||||
|
||||
int id;
|
||||
|
||||
{
|
||||
String strid = props.getProperty ( "ID" );
|
||||
|
||||
if ( strid == null )
|
||||
{
|
||||
_log.debug ( "ID not specified in STREAM RECEIVE message" );
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
id = Integer.parseInt ( strid );
|
||||
}
|
||||
catch ( NumberFormatException e )
|
||||
{
|
||||
_log.debug ( "Invalid STREAM RECEIVE ID specified: " + strid );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
boolean nolimit = false;
|
||||
|
||||
long limit = 0;
|
||||
{
|
||||
String strsize = props.getProperty ( "LIMIT" );
|
||||
|
||||
if ( strsize == null )
|
||||
{
|
||||
_log.debug ( "Limit not specified in STREAM RECEIVE message" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( strsize.equals( "NONE" ) )
|
||||
{
|
||||
nolimit = true ;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
limit = Long.parseLong ( strsize );
|
||||
}
|
||||
catch ( NumberFormatException e )
|
||||
{
|
||||
_log.debug ( "Invalid STREAM RECEIVE size specified: " + strsize );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( limit < 0 )
|
||||
{
|
||||
_log.debug ( "Specified limit (" + limit
|
||||
+ ") is out of protocol limits" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
streamSession.setReceiveLimit ( id, limit, nolimit ) ;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
574
apps/sam/java/src/net/i2p/sam/SAMv2StreamSession.java
Normal file
574
apps/sam/java/src/net/i2p/sam/SAMv2StreamSession.java
Normal file
@ -0,0 +1,574 @@
|
||||
package net.i2p.sam;
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
* Written by human in 2004 and released into the public domain
|
||||
* with no warranty of any kind, either expressed or implied.
|
||||
* It probably won't make your computer catch on fire, or eat
|
||||
* your children, but it might. Use at your own risk.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ConnectException;
|
||||
import java.net.NoRouteToHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.client.streaming.I2PSocketManagerFactory;
|
||||
import net.i2p.client.streaming.I2PSocketOptions;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.ByteCache;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* SAMv2 STREAM session class.
|
||||
*
|
||||
* @author mkvore
|
||||
*/
|
||||
|
||||
public class SAMv2StreamSession extends SAMStreamSession
|
||||
{
|
||||
|
||||
private final static Log _log = new Log ( SAMv2StreamSession.class );
|
||||
|
||||
/**
|
||||
* Create a new SAM STREAM session.
|
||||
*
|
||||
* @param dest Base64-encoded destination (private key)
|
||||
* @param dir Session direction ("RECEIVE", "CREATE" or "BOTH")
|
||||
* @param props Properties to setup the I2P session
|
||||
* @param recv Object that will receive incoming data
|
||||
*/
|
||||
public SAMv2StreamSession ( String dest, String dir, Properties props,
|
||||
SAMStreamReceiver recv ) throws IOException, DataFormatException, SAMException
|
||||
{
|
||||
super ( dest, dir, props, recv );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SAM STREAM session.
|
||||
*
|
||||
* @param destStream Input stream containing the destination keys
|
||||
* @param dir Session direction ("RECEIVE", "CREATE" or "BOTH")
|
||||
* @param props Properties to setup the I2P session
|
||||
* @param recv Object that will receive incoming data
|
||||
*/
|
||||
public SAMv2StreamSession ( InputStream destStream, String dir,
|
||||
Properties props, SAMStreamReceiver recv ) throws IOException, DataFormatException, SAMException
|
||||
{
|
||||
super ( destStream, dir, props, recv );
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect the SAM STREAM session to the specified Destination
|
||||
*
|
||||
* @param id Unique id for the connection
|
||||
* @param dest Base64-encoded Destination to connect to
|
||||
* @param props Options to be used for connection
|
||||
*
|
||||
* @throws DataFormatException if the destination is not valid
|
||||
* @throws SAMInvalidDirectionException if trying to connect through a
|
||||
* receive-only session
|
||||
* @return true if the communication with the SAM client is ok
|
||||
*/
|
||||
|
||||
public boolean connect ( int id, String dest, Properties props )
|
||||
throws DataFormatException, SAMInvalidDirectionException
|
||||
{
|
||||
if ( !canCreate )
|
||||
{
|
||||
_log.debug ( "Trying to create an outgoing connection using a receive-only session" );
|
||||
throw new SAMInvalidDirectionException ( "Trying to create connections through a receive-only session" );
|
||||
}
|
||||
|
||||
if ( checkSocketHandlerId ( id ) )
|
||||
{
|
||||
_log.debug ( "The specified id (" + id + ") is already in use" );
|
||||
return false ;
|
||||
}
|
||||
|
||||
Destination d = new Destination();
|
||||
|
||||
d.fromBase64 ( dest );
|
||||
|
||||
I2PSocketOptions opts = socketMgr.buildOptions ( props );
|
||||
|
||||
if ( props.getProperty ( I2PSocketOptions.PROP_CONNECT_TIMEOUT ) == null )
|
||||
opts.setConnectTimeout ( 60 * 1000 );
|
||||
|
||||
_log.debug ( "Connecting new I2PSocket..." );
|
||||
|
||||
|
||||
// non-blocking connection (SAMv2)
|
||||
|
||||
StreamConnector connector ;
|
||||
|
||||
connector = new StreamConnector ( id, d, opts );
|
||||
|
||||
I2PThread connectThread = new I2PThread ( connector, "StreamConnector" + id ) ;
|
||||
|
||||
connectThread.start() ;
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* SAM STREAM socket connecter, running in its own thread.
|
||||
*
|
||||
* @author mkvore
|
||||
*/
|
||||
|
||||
public class StreamConnector implements Runnable
|
||||
{
|
||||
|
||||
private Object runningLock = new Object();
|
||||
private boolean stillRunning = true;
|
||||
|
||||
private int id;
|
||||
private Destination dest ;
|
||||
private I2PSocketOptions opts ;
|
||||
|
||||
/**
|
||||
* Create a new SAM STREAM session socket reader
|
||||
*
|
||||
* @param id Unique id assigned to the handler
|
||||
* @param dest Destination to reach
|
||||
* @param opts Socket options (I2PSocketOptions)
|
||||
*/
|
||||
|
||||
|
||||
public StreamConnector ( int id, Destination dest, I2PSocketOptions opts )// throws IOException
|
||||
{
|
||||
_log.debug ( "Instantiating new SAM STREAM connector" );
|
||||
|
||||
this.id = id ;
|
||||
this.opts = opts ;
|
||||
this.dest = dest ;
|
||||
}
|
||||
|
||||
|
||||
public void run()
|
||||
{
|
||||
_log.debug ( "run() called for socket connector " + id );
|
||||
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
I2PSocket i2ps = socketMgr.connect ( dest, opts );
|
||||
|
||||
createSocketHandler ( i2ps, id );
|
||||
|
||||
recv.notifyStreamOutgoingConnection ( id, "OK", null );
|
||||
}
|
||||
|
||||
catch ( DataFormatException e )
|
||||
{
|
||||
_log.debug ( "Invalid destination in STREAM CONNECT message" );
|
||||
recv.notifyStreamOutgoingConnection ( id, "INVALID_KEY", e.getMessage() );
|
||||
}
|
||||
catch ( ConnectException e )
|
||||
{
|
||||
_log.debug ( "STREAM CONNECT failed: " + e.getMessage() );
|
||||
recv.notifyStreamOutgoingConnection ( id, "CONNECTION_REFUSED", e.getMessage() );
|
||||
}
|
||||
catch ( NoRouteToHostException e )
|
||||
{
|
||||
_log.debug ( "STREAM CONNECT failed: " + e.getMessage() );
|
||||
recv.notifyStreamOutgoingConnection ( id, "CANT_REACH_PEER", e.getMessage() );
|
||||
}
|
||||
catch ( InterruptedIOException e )
|
||||
{
|
||||
_log.debug ( "STREAM CONNECT failed: " + e.getMessage() );
|
||||
recv.notifyStreamOutgoingConnection ( id, "TIMEOUT", e.getMessage() );
|
||||
}
|
||||
catch ( I2PException e )
|
||||
{
|
||||
_log.debug ( "STREAM CONNECT failed: " + e.getMessage() );
|
||||
recv.notifyStreamOutgoingConnection ( id, "I2P_ERROR", e.getMessage() );
|
||||
}
|
||||
}
|
||||
catch ( IOException e )
|
||||
{
|
||||
_log.debug ( "Error sending disconnection notice for handler "
|
||||
+ id, e );
|
||||
}
|
||||
|
||||
_log.debug ( "Shutting down SAM STREAM session connector " + id );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Lets us push data through the stream without blocking, (even after exceeding
|
||||
* the I2PSocket's buffer)
|
||||
*/
|
||||
|
||||
protected StreamSender newStreamSender ( I2PSocket s, int id ) throws IOException
|
||||
{
|
||||
return new v2StreamSender ( s, id ) ;
|
||||
}
|
||||
|
||||
protected SAMStreamSessionSocketReader
|
||||
newSAMStreamSessionSocketReader(I2PSocket s, int id ) throws IOException
|
||||
{
|
||||
return new SAMv2StreamSessionSocketReader(s,id);
|
||||
}
|
||||
|
||||
protected class v2StreamSender extends StreamSender
|
||||
|
||||
{
|
||||
private List _data;
|
||||
private int _dataSize;
|
||||
private int _id;
|
||||
private ByteCache _cache;
|
||||
private OutputStream _out = null;
|
||||
private boolean _stillRunning, _shuttingDownGracefully;
|
||||
private Object runningLock = new Object();
|
||||
private I2PSocket i2pSocket = null;
|
||||
|
||||
public v2StreamSender ( I2PSocket s, int id ) throws IOException
|
||||
{
|
||||
super ( s, id );
|
||||
_data = new ArrayList ( 1 );
|
||||
_dataSize = 0;
|
||||
_id = id;
|
||||
_cache = ByteCache.getInstance ( 10, 32 * 1024 );
|
||||
_out = s.getOutputStream();
|
||||
_stillRunning = true;
|
||||
_shuttingDownGracefully = false;
|
||||
i2pSocket = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send bytes through the SAM STREAM session socket sender
|
||||
*
|
||||
* @param data Data to be sent
|
||||
*
|
||||
* @throws IOException if the client didnt provide enough data
|
||||
*/
|
||||
public void sendBytes ( InputStream in, int size ) throws IOException
|
||||
{
|
||||
if ( _log.shouldLog ( Log.DEBUG ) )
|
||||
_log.debug ( "Handler " + _id + ": sending " + size + " bytes" );
|
||||
|
||||
ByteArray ba = _cache.acquire();
|
||||
|
||||
int read = DataHelper.read ( in, ba.getData(), 0, size );
|
||||
|
||||
if ( read != size )
|
||||
throw new IOException ( "Insufficient data from the SAM client (" + read + "/" + size + ")" );
|
||||
|
||||
ba.setValid ( read );
|
||||
|
||||
synchronized ( _data )
|
||||
{
|
||||
if ( _dataSize >= SOCKET_HANDLER_BUF_SIZE )
|
||||
{
|
||||
_cache.release ( ba, false );
|
||||
recv.streamSendAnswer ( _id, "FAILED", "BUFFER_FULL" ) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
_dataSize += size ;
|
||||
_data.add ( ba );
|
||||
_data.notifyAll();
|
||||
|
||||
if ( _dataSize >= SOCKET_HANDLER_BUF_SIZE )
|
||||
{
|
||||
recv.streamSendAnswer ( _id, "OK", "BUFFER_FULL" ) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
recv.streamSendAnswer ( _id, "OK", "READY" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a SAM STREAM session socket sender thread immediately
|
||||
*
|
||||
*/
|
||||
public void stopRunning()
|
||||
{
|
||||
_log.debug ( "stopRunning() invoked on socket sender " + _id );
|
||||
|
||||
synchronized ( runningLock )
|
||||
{
|
||||
if ( _stillRunning )
|
||||
{
|
||||
_stillRunning = false;
|
||||
|
||||
try
|
||||
{
|
||||
i2pSocket.close();
|
||||
}
|
||||
catch ( IOException e )
|
||||
{
|
||||
_log.debug ( "Caught IOException", e );
|
||||
}
|
||||
|
||||
synchronized ( _data )
|
||||
{
|
||||
_data.clear();
|
||||
_data.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a SAM STREAM session socket sender gracefully: stop the
|
||||
* sender thread once all pending data has been sent.
|
||||
*/
|
||||
public void shutDownGracefully()
|
||||
{
|
||||
_log.debug ( "shutDownGracefully() invoked on socket sender " + _id );
|
||||
_shuttingDownGracefully = true;
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
_log.debug ( "run() called for socket sender " + _id );
|
||||
ByteArray data = null;
|
||||
|
||||
while ( _stillRunning )
|
||||
{
|
||||
data = null;
|
||||
|
||||
try
|
||||
{
|
||||
synchronized ( _data )
|
||||
{
|
||||
if ( _data.size() > 0 )
|
||||
{
|
||||
int formerSize = _dataSize ;
|
||||
data = ( ByteArray ) _data.remove ( 0 );
|
||||
_dataSize -= data.getValid();
|
||||
|
||||
if ( ( formerSize >= SOCKET_HANDLER_BUF_SIZE ) && ( _dataSize < SOCKET_HANDLER_BUF_SIZE ) )
|
||||
recv.notifyStreamSendBufferFree ( _id );
|
||||
}
|
||||
else if ( _shuttingDownGracefully )
|
||||
{
|
||||
/* No data left and shutting down gracefully?
|
||||
If so, stop the sender. */
|
||||
stopRunning();
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Wait for data. */
|
||||
_data.wait ( 5000 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( data != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
_out.write ( data.getData(), 0, data.getValid() );
|
||||
|
||||
if ( forceFlush )
|
||||
{
|
||||
// i dont like doing this, but it clears the buffer issues
|
||||
_out.flush();
|
||||
}
|
||||
}
|
||||
catch ( IOException ioe )
|
||||
{
|
||||
// ok, the stream failed, but the SAM client didn't
|
||||
|
||||
if ( _log.shouldLog ( Log.WARN ) )
|
||||
_log.warn ( "Stream failed", ioe );
|
||||
|
||||
removeSocketHandler ( _id );
|
||||
|
||||
stopRunning();
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
_cache.release ( data, false );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( InterruptedException ie ) {}
|
||||
catch ( IOException e ) {}}
|
||||
|
||||
synchronized ( _data )
|
||||
{
|
||||
_data.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Send bytes through a SAM STREAM session.
|
||||
*
|
||||
* @param data Bytes to be sent
|
||||
*
|
||||
* @return True if the data was queued for sending, false otherwise
|
||||
*/
|
||||
public boolean setReceiveLimit ( int id, long limit, boolean nolimit )
|
||||
{
|
||||
SAMStreamSessionSocketReader reader = getSocketReader ( id );
|
||||
|
||||
if ( reader == null )
|
||||
{
|
||||
if ( _log.shouldLog ( Log.WARN ) )
|
||||
_log.warn ( "Trying to set a limit to a nonexistent reader " + id );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
( (SAMv2StreamSessionSocketReader) reader).setLimit ( limit, nolimit );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* SAM STREAM socket reader, running in its own thread. It forwards
|
||||
* forward data to/from an I2P socket.
|
||||
*
|
||||
* @author human
|
||||
*/
|
||||
|
||||
|
||||
|
||||
public class SAMv2StreamSessionSocketReader extends SAMv1StreamSessionSocketReader
|
||||
{
|
||||
|
||||
protected boolean nolimit ;
|
||||
protected long limit ;
|
||||
protected long totalReceived ;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SAM STREAM session socket reader
|
||||
*
|
||||
* @param s Socket to be handled
|
||||
* @param id Unique id assigned to the handler
|
||||
*/
|
||||
public SAMv2StreamSessionSocketReader ( I2PSocket s, int id ) throws IOException
|
||||
{
|
||||
super ( s, id );
|
||||
nolimit = false ;
|
||||
limit = 0 ;
|
||||
totalReceived = 0 ;
|
||||
}
|
||||
|
||||
public void setLimit ( long limit, boolean nolimit )
|
||||
{
|
||||
synchronized (runningLock)
|
||||
{
|
||||
this.limit = limit ;
|
||||
this.nolimit = nolimit ;
|
||||
runningLock.notify() ;
|
||||
}
|
||||
_log.debug ( "new limit set for socket reader " + id + " : " + (nolimit ? "NOLIMIT" : limit + " bytes" ) );
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
_log.debug ( "run() called for socket reader " + id );
|
||||
|
||||
int read = -1;
|
||||
byte[] data = new byte[SOCKET_HANDLER_BUF_SIZE];
|
||||
|
||||
try
|
||||
{
|
||||
InputStream in = i2pSocket.getInputStream();
|
||||
|
||||
while ( stillRunning )
|
||||
{
|
||||
synchronized (runningLock)
|
||||
{
|
||||
while ( stillRunning && ( !nolimit && totalReceived >= limit) )
|
||||
{
|
||||
try{
|
||||
runningLock.wait() ;
|
||||
}
|
||||
catch (InterruptedException ie)
|
||||
{}
|
||||
}
|
||||
if ( !stillRunning )
|
||||
break ;
|
||||
}
|
||||
|
||||
read = in.read ( data );
|
||||
|
||||
if ( read == -1 )
|
||||
{
|
||||
_log.debug ( "Handler " + id + ": connection closed" );
|
||||
break;
|
||||
}
|
||||
|
||||
totalReceived += read ;
|
||||
|
||||
recv.receiveStreamBytes ( id, data, read );
|
||||
}
|
||||
}
|
||||
catch ( IOException e )
|
||||
{
|
||||
_log.debug ( "Caught IOException", e );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
i2pSocket.close();
|
||||
}
|
||||
catch ( IOException e )
|
||||
{
|
||||
_log.debug ( "Caught IOException", e );
|
||||
}
|
||||
|
||||
if ( stillRunning )
|
||||
{
|
||||
removeSocketHandler ( id );
|
||||
// FIXME: we need error reporting here!
|
||||
|
||||
try
|
||||
{
|
||||
recv.notifyStreamDisconnection ( id, "OK", null );
|
||||
}
|
||||
catch ( IOException e )
|
||||
{
|
||||
_log.debug ( "Error sending disconnection notice for handler "
|
||||
+ id, e );
|
||||
}
|
||||
}
|
||||
|
||||
_log.debug ( "Shutting down SAM STREAM session socket handler " + id );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -29,9 +29,15 @@ public class I2PSocketFull implements I2PSocket {
|
||||
if (c == null) return;
|
||||
if (c.getIsConnected()) {
|
||||
OutputStream out = c.getOutputStream();
|
||||
if (out != null)
|
||||
out.close();
|
||||
c.disconnect(true);
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ioe) {
|
||||
// ignore any write error, as we want to keep on and kill the
|
||||
// con (thanks Complication!)
|
||||
}
|
||||
}
|
||||
c.disconnect(true);
|
||||
} else {
|
||||
//throw new IOException("Not connected");
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ public class I2PSocketManagerFull implements I2PSocketManager {
|
||||
public long getAcceptTimeout() { return _acceptTimeout; }
|
||||
|
||||
public void setDefaultOptions(I2PSocketOptions options) {
|
||||
_defaultOptions = new ConnectionOptions(options);
|
||||
_defaultOptions = new ConnectionOptions((ConnectionOptions) options);
|
||||
}
|
||||
|
||||
public I2PSocketOptions getDefaultOptions() {
|
||||
|
@ -19,7 +19,7 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Revision: 1.1 $
|
||||
* $Revision: 1.2 $
|
||||
*/
|
||||
|
||||
package i2p.susi.dns;
|
||||
@ -76,6 +76,8 @@ public class AddressbookBean
|
||||
deletionMarks = new LinkedList();
|
||||
}
|
||||
private long configLastLoaded = 0;
|
||||
private static final String PRIVATE_BOOK = "private_addressbook";
|
||||
private static final String DEFAULT_PRIVATE_BOOK = "../privatehosts.txt";
|
||||
private void loadConfig()
|
||||
{
|
||||
long currentTime = System.currentTimeMillis();
|
||||
@ -86,6 +88,9 @@ public class AddressbookBean
|
||||
try {
|
||||
properties.clear();
|
||||
properties.load( new FileInputStream( ConfigBean.configFileName ) );
|
||||
// added in 0.5, for compatibility with 0.4 config.txt
|
||||
if( properties.getProperty(PRIVATE_BOOK) == null)
|
||||
properties.setProperty(PRIVATE_BOOK, DEFAULT_PRIVATE_BOOK);
|
||||
configLastLoaded = currentTime;
|
||||
}
|
||||
catch (Exception e) {
|
||||
@ -112,8 +117,9 @@ public class AddressbookBean
|
||||
public String getBook()
|
||||
{
|
||||
if( book == null || ( book.compareToIgnoreCase( "master" ) != 0 &&
|
||||
book.compareToIgnoreCase( "router" ) != 0 ) &&
|
||||
book.compareToIgnoreCase( "published" ) != 0 )
|
||||
book.compareToIgnoreCase( "router" ) != 0 &&
|
||||
book.compareToIgnoreCase( "private" ) != 0 &&
|
||||
book.compareToIgnoreCase( "published" ) != 0 ))
|
||||
book = "master";
|
||||
|
||||
return book;
|
||||
@ -163,7 +169,11 @@ public class AddressbookBean
|
||||
list.addLast( new AddressBean( name, destination ) );
|
||||
}
|
||||
// Format a message about filtered addressbook size, and the number of displayed entries
|
||||
message = "Filtered list contains " + list.size() + " entries";
|
||||
if( filter != null && filter.length() > 0 )
|
||||
message = "Filtered l";
|
||||
else
|
||||
message = "L";
|
||||
message += "ist contains " + list.size() + " entries";
|
||||
if (list.size() > 300) message += ", displaying the first 300."; else message += ".";
|
||||
|
||||
Object array[] = list.toArray();
|
||||
@ -251,6 +261,10 @@ public class AddressbookBean
|
||||
{
|
||||
return getBook().compareToIgnoreCase( "published" ) == 0;
|
||||
}
|
||||
public boolean isPrivate()
|
||||
{
|
||||
return getBook().compareToIgnoreCase( "private" ) == 0;
|
||||
}
|
||||
public void setFilter(String filter) {
|
||||
if( filter != null && ( filter.length() == 0 || filter.compareToIgnoreCase( "none" ) == 0 ) ) {
|
||||
filter = null;
|
||||
|
@ -19,14 +19,14 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Revision: 1.4 $
|
||||
* $Revision: 1.1 $
|
||||
*/
|
||||
|
||||
package i2p.susi.dns;
|
||||
|
||||
public class VersionBean {
|
||||
|
||||
private static String version = "0.4";
|
||||
private static String version = "0.5";
|
||||
private static String url = "http://susi.i2p/?i2paddresshelper=T2DU1KAz3meB0B53U8Y06-I7vHR7XmC0qXAJfLW6b-1L1FVKoySRZz4xazHAwyv2xtRpvKrv6ukLm1tThEW0zQWtZPtX8G6KkzMibD8t7IS~4yw-9VkBtUydyYfsX08AK3v~-egSW8HCXTdyIJVtrETJb337VDUHW-7D4L1JLbwSH4if2ooks6yFTrljK5aVMi-16dZOVvmoyJc3jBqSdK6kraO4gW5-vHTmbLwL498p9nug1KOg1DqgN2GeU5X1QlVrlpFb~IIfdP~O8NT7u-LAjW3jSJsMbLDHMSYTIhC7xmJIiBoi-qk8p6TLynAmvJ7HRvbx4N1EB-uJHyD16wsZkkHyEOfmXbj0ZqLyKEGb3thPwCz-M9v~c2Qt3WbwjXJAtHpjlHkdJ4Fg91cX2oak~JoapnPf6Syw8hko5syf6VVoCYLnrrYyM8oGl8mLclHkj~VCidQNqMSM74IhrHfK6HmRikqtZBexb5M6wfMTTqBvaHURdD21GOpFKYBUAAAA";
|
||||
|
||||
public String getVersion() {
|
||||
|
@ -20,7 +20,7 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Revision: 1.2 $
|
||||
* $Revision: 1.3 $
|
||||
*/
|
||||
%>
|
||||
<%@ page contentType="text/html"%>
|
||||
@ -48,9 +48,11 @@
|
||||
<p>addressbooks
|
||||
<a href="addressbook.jsp?book=master">master</a> |
|
||||
<a href="addressbook.jsp?book=router">router</a> |
|
||||
<a href="addressbook.jsp?book=published">published</a> *
|
||||
<a href="addressbook.jsp?book=published">published</a> |
|
||||
<a href="addressbook.jsp?book=private">private</a> *
|
||||
<a href="subscriptions.jsp">subscriptions</a> *
|
||||
<a href="config.jsp">configuration</a>
|
||||
<a href="config.jsp">configuration</a> *
|
||||
<a href="index.jsp">overview</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -92,7 +94,8 @@
|
||||
<a href="addressbook.jsp?filter=0-9">0-9</a>
|
||||
<a href="addressbook.jsp?filter=none">all</a></p>
|
||||
<c:if test="${book.hasFilter}">
|
||||
<p>Current filter: ${book.filter}</p>
|
||||
<p>Current filter: ${book.filter}
|
||||
(<a href="addressbook.jsp?filter=none">clear filter</a>)</p>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
@ -117,7 +120,7 @@
|
||||
<table class="book" cellspacing="0" cellpadding="5">
|
||||
<tr class="head">
|
||||
|
||||
<c:if test="${book.master || book.router || book.published}">
|
||||
<c:if test="${book.master || book.router || book.published || book.private}">
|
||||
<th> </th>
|
||||
</c:if>
|
||||
|
||||
@ -127,7 +130,7 @@
|
||||
<!-- limit iterator to 300, or "Form too large" may result on submit -->
|
||||
<c:forEach items="${book.entries}" var="addr" begin="0" end="299">
|
||||
<tr class="list${book.trClass}">
|
||||
<c:if test="${book.master || book.router || book.published}">
|
||||
<c:if test="${book.master || book.router || book.published || book.private}">
|
||||
<td class="checkbox"><input type="checkbox" name="checked" value="${addr.name}" alt="Mark for deletion"></td>
|
||||
</c:if>
|
||||
<td class="names"><a href="http://${addr.name}/">${addr.name}</a> -
|
||||
@ -139,7 +142,7 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<c:if test="${book.master || book.router || book.published}">
|
||||
<c:if test="${book.master || book.router || book.published || book.private}">
|
||||
<div id="buttons">
|
||||
<p class="buttons"><input type="image" name="action" value="delete" src="images/delete.png" alt="Delete checked" />
|
||||
</p>
|
||||
|
@ -20,7 +20,7 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Revision: 1.14 $
|
||||
* $Revision: 1.1 $
|
||||
*/
|
||||
%>
|
||||
<%@ page contentType="text/html" %>
|
||||
@ -43,9 +43,11 @@
|
||||
addressbooks
|
||||
<a href="addressbook.jsp?book=master">master</a> |
|
||||
<a href="addressbook.jsp?book=router">router</a> |
|
||||
<a href="addressbook.jsp?book=published">published</a> *
|
||||
<a href="addressbook.jsp?book=published">published</a> |
|
||||
<a href="addressbook.jsp?book=private">private</a> *
|
||||
<a href="subscriptions.jsp">subscriptions</a> *
|
||||
<a href="config.jsp">configuration</a>
|
||||
configuration *
|
||||
<a href="index.jsp">overview</a>
|
||||
</p>
|
||||
</div>
|
||||
<div id="headline">
|
||||
@ -65,22 +67,22 @@ addressbooks
|
||||
<div id="help">
|
||||
<h3>Hints</h3>
|
||||
<ol>
|
||||
<li>All file or directory paths here are relative to the addressbooks working directory, which normally
|
||||
<li>All file or directory paths here are relative to the addressbook's working directory, which normally
|
||||
is located at $I2P/addressbook/.</li>
|
||||
<li>If you want to manually add lines to an addressbook, add them to the master addressbook. The router
|
||||
<li>If you want to manually add lines to an addressbook, add them to the private or master addressbooks. The router
|
||||
addressbook and the published addressbook are overwritten by the addressbook application.</li>
|
||||
<li><b>Important:</b>When you publish your addressbook, <b>ALL</b> destinations appear there, even those
|
||||
from your master addressbook. Unfortunately the master addressbook points to your userhosts.txt, which was
|
||||
used for private destinations before. So if you want to keep the destinations in your userhosts.txt secret,
|
||||
please change the master addressbook to a different file before turning on addressbook publishing.</li>
|
||||
<li><b>Important:</b>When you publish your addressbook, <b>ALL</b> destinations from the master and router addressbooks appear there.
|
||||
Use the private addressbook for private destinations, these are not published.
|
||||
</li>
|
||||
</ol>
|
||||
<h3>Options</h3>
|
||||
<ul>
|
||||
<li><b>subscriptions</b> - file containing the list of subscriptions URLs (no need to change)</li>
|
||||
<li><b>update_delay</b> - update interval in hours (no need to change)</li>
|
||||
<li><b>published_addressbook</b> - your public hosts.txt file (choose a path within your webserver document root)</li>
|
||||
<li><b>router_addressbook</b> - your hosts.txt (no need to change)</li>
|
||||
<li><b>master_addressbook</b> - your personal addressbook, it gets never overwritten by the addressbook</li>
|
||||
<li><b>router_addressbook</b> - your hosts.txt (don't change)</li>
|
||||
<li><b>master_addressbook</b> - your personal addressbook, it never gets overwritten by the addressbook (don't change)</li>
|
||||
<li><b>private_addressbook</b> - your private addressbook, it is never published (defaults to ../privatehosts.txt, don't change)</li>
|
||||
<li><b>proxy_port</b> - http port for your eepProxy (no need to change)</li>
|
||||
<li><b>proxy_host</b> - hostname for your eepProxy (no need to change)</li>
|
||||
<li><b>should_publish</b> - true/false whether to write the published addressbook</li>
|
||||
@ -93,4 +95,4 @@ please change the master addressbook to a different file before turning on addre
|
||||
<p class="footer">susidns v${version.version} © <a href="${version.url}">susi</a> 2005 </p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
@ -20,7 +20,7 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Revision: 1.1 $
|
||||
* $Revision: 1.2 $
|
||||
*/
|
||||
%>
|
||||
<%@ page contentType="text/html"%>
|
||||
@ -42,9 +42,11 @@
|
||||
<p>addressbooks
|
||||
<a href="addressbook.jsp?book=master">master</a> |
|
||||
<a href="addressbook.jsp?book=router">router</a> |
|
||||
<a href="addressbook.jsp?book=published">published</a> *
|
||||
<a href="addressbook.jsp?book=published">published</a> |
|
||||
<a href="addressbook.jsp?book=private">private</a> *
|
||||
<a href="subscriptions.jsp">subscriptions</a> *
|
||||
<a href="config.jsp">configuration</a>
|
||||
<a href="config.jsp">configuration</a> *
|
||||
overview
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -52,23 +54,25 @@
|
||||
<h3>Huh? what addressbook?</h3>
|
||||
<p>
|
||||
The addressbook application is part of your i2p installation. It regularly updates your hosts.txt file
|
||||
from distributed sources. It keeps your hosts.txt up to date, so it automatically contains all new
|
||||
eepsites announced on <a href="http://orion.i2p">orion</a>
|
||||
or in the <a href="http://forum.i2p/viewforum.php?f=16">forum</a>.
|
||||
from distributed sources. It keeps your hosts.txt up to date, so it can automatically add
|
||||
eepsites announced on other sites if you subscribe to those sites' addressbooks.
|
||||
</p>
|
||||
<p>
|
||||
(To speak the truth: In its default configuration the addressbook does not poll
|
||||
orion, but dev.i2p only. Subscribing to <a href="http://orion.i2p">orion</a> is an easy task,
|
||||
just add <a href="http://orion.i2p/hosts.txt">http://orion.i2p/hosts.txt</a> to your <a href="subscriptions.jsp">subscriptions</a> file.)
|
||||
additional sites, but dev.i2p only. Subscribing to additional sites is an easy task,
|
||||
just add them to your <a href="subscriptions.jsp">subscriptions</a> file.)
|
||||
</p>
|
||||
<p>If you have questions about naming in i2p, there is an excellent <a href="http://forum.i2p.net/viewtopic.php?t=134">introduction</a>
|
||||
from duck in the forum.</p>
|
||||
<h3>How does the addressbook work?</h3>
|
||||
<p>The addressbook application regularly (normally once per hour) polls your subscriptions and merges their content
|
||||
into your so called router addressbook (normally your plain hosts.txt). Then it merges your so called master addressbook (normally
|
||||
your userhosts.txt) into the router addressbook as well. If configured the router addressbook is now written to the so published addressbook,
|
||||
which is a publicly available copy of your hosts.txt somewhere in your eepsites document root. (Yes, this means that, with activated publication,
|
||||
your once private keys from userhosts.txt now are publicly available for everybody.)
|
||||
into your so-called router addressbook (normally your plain hosts.txt). Then it merges your so-called master addressbook (normally
|
||||
your userhosts.txt) into the router addressbook as well. If configured, the router addressbook is now written to the published addressbook,
|
||||
which is a publicly available copy of your hosts.txt somewhere in your eepsite's document root.
|
||||
</p><p>
|
||||
The router also uses a private addressbook (privatehosts.txt, not shown in the picture), which is not merged or published.
|
||||
Hosts in the private addressbook can be accessed by you but their addresses are never distributed to others.
|
||||
The private addressbook can also be used for aliases of hosts in your other addressbooks.
|
||||
</p>
|
||||
<p><img src="images/how.png" border="0" alt="addressbook working scheme"/></p>
|
||||
</div>
|
||||
|
@ -20,7 +20,7 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Revision: 1.7 $
|
||||
* $Revision: 1.2 $
|
||||
*/
|
||||
%>
|
||||
<%@ page contentType="text/html"%>
|
||||
@ -42,9 +42,11 @@
|
||||
<p>addressbooks
|
||||
<a href="addressbook.jsp?book=master">master</a> |
|
||||
<a href="addressbook.jsp?book=router">router</a> |
|
||||
<a href="addressbook.jsp?book=published">published</a> *
|
||||
<a href="subscriptions.jsp">subscriptions</a> *
|
||||
<a href="config.jsp">configuration</a>
|
||||
<a href="addressbook.jsp?book=published">published</a> |
|
||||
<a href="addressbook.jsp?book=private">private</a> *
|
||||
subscriptions *
|
||||
<a href="config.jsp">configuration</a> *
|
||||
<a href="index.jsp">overview</a>
|
||||
</p>
|
||||
</div>
|
||||
<div id="headline">
|
||||
@ -66,9 +68,8 @@
|
||||
<p class="help">
|
||||
The subscription file contains a list of (i2p) URLs. The addressbook application
|
||||
regularly (once per hour) checks this list for new eepsites. Those URLs simply contain the published hosts.txt
|
||||
file of other people. Default subscription is the hosts.txt from dev.i2p. The most
|
||||
popular collaboration site for eepsite is orion.i2p. So its a good idea to add http://orion.i2p/hosts.txt
|
||||
as a 2nd subscription.
|
||||
file of other people. The default subscription is the hosts.txt from dev.i2p, which is updated infrequently.
|
||||
So it is a good idea to add additional subscriptions to sites that have the latest addresses.
|
||||
</p>
|
||||
</div>
|
||||
<div id="footer">
|
||||
|
14
build.xml
14
build.xml
@ -30,7 +30,7 @@
|
||||
<ant dir="apps/addressbook/" target="war" />
|
||||
<ant dir="apps/susimail/" target="war" />
|
||||
<ant dir="apps/susidns/src" target="all" />
|
||||
<ant dir="apps/syndie/java/" target="jar" />
|
||||
<!-- <ant dir="apps/syndie/java/" target="jar" /> -->
|
||||
<ant dir="apps/i2psnark/java/" target="standalone" />
|
||||
</target>
|
||||
<target name="buildrouter">
|
||||
@ -103,9 +103,11 @@
|
||||
<copy file="apps/addressbook/dist/addressbook.war" todir="build/" />
|
||||
<copy file="apps/susimail/susimail.war" todir="build/" />
|
||||
<copy file="apps/susidns/src/susidns.war" todir="build/" />
|
||||
<!--
|
||||
<copy file="apps/syndie/syndie.war" todir="build/" />
|
||||
<copy file="apps/syndie/java/build/syndie.jar" todir="build/" />
|
||||
<copy file="apps/syndie/java/build/sucker.jar" todir="build/" />
|
||||
-->
|
||||
<copy file="apps/i2psnark/i2psnark.war" todir="build/" />
|
||||
<copy file="apps/i2psnark/java/build/i2psnark.jar" todir="build/" />
|
||||
<copy file="apps/jdom/jdom.jar" todir="build/" />
|
||||
@ -195,7 +197,7 @@
|
||||
<copy file="build/routerconsole.jar" todir="pkg-temp/lib/" />
|
||||
<copy file="build/sam.jar" todir="pkg-temp/lib/" />
|
||||
<copy file="build/systray.jar" todir="pkg-temp/lib" />
|
||||
<copy file="build/sucker.jar" todir="pkg-temp/lib" />
|
||||
<!-- <copy file="build/sucker.jar" todir="pkg-temp/lib" /> -->
|
||||
<copy file="build/i2psnark.jar" todir="pkg-temp/lib/" />
|
||||
<copy file="i2p.exe" todir="pkg-temp/" failonerror="false" />
|
||||
<copy file="installer/resources/runplain.sh" todir="pkg-temp/" />
|
||||
@ -209,7 +211,7 @@
|
||||
<copy file="build/addressbook.war" todir="pkg-temp/webapps/" />
|
||||
<copy file="build/susimail.war" todir="pkg-temp/webapps/" />
|
||||
<copy file="build/susidns.war" todir="pkg-temp/webapps/" />
|
||||
<copy file="build/syndie.war" todir="pkg-temp/webapps/" />
|
||||
<!-- <copy file="build/syndie.war" todir="pkg-temp/webapps/" /> -->
|
||||
<copy file="build/i2psnark.war" todir="pkg-temp/webapps/" />
|
||||
<copy file="apps/i2psnark/java/build/launch-i2psnark.jar" todir="pkg-temp/" />
|
||||
<copy file="apps/i2psnark/jetty-i2psnark.xml" todir="pkg-temp/" />
|
||||
@ -279,11 +281,13 @@
|
||||
<copy file="installer/resources/eepsite_index.html" tofile="pkg-temp/eepsite/docroot/index.html" />
|
||||
<copy file="installer/resources/favicon.ico" tofile="pkg-temp/eepsite/docroot/favicon.ico" />
|
||||
<copy file="installer/resources/jetty.xml" tofile="pkg-temp/eepsite/jetty.xml" />
|
||||
<!--
|
||||
<mkdir dir="pkg-temp/syndie" />
|
||||
<mkdir dir="pkg-temp/syndie/archive" />
|
||||
<mkdir dir="pkg-temp/syndie/archive/ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=" />
|
||||
<copy file="installer/resources/blogMeta.snm" tofile="pkg-temp/syndie/archive/ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=/meta.snm" />
|
||||
<copy file="installer/resources/blogPost.snd" tofile="pkg-temp/syndie/archive/ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=/1132012800001.snd" />
|
||||
-->
|
||||
</target>
|
||||
<target name="tarball" depends="preppkg">
|
||||
<tar compression="bzip2" destfile="i2p.tar.bz2">
|
||||
@ -320,7 +324,7 @@
|
||||
<copy file="build/addressbook.war" todir="pkg-temp/webapps/" />
|
||||
<copy file="build/susimail.war" todir="pkg-temp/webapps/" />
|
||||
<copy file="build/susidns.war" todir="pkg-temp/webapps/" />
|
||||
<copy file="build/syndie.war" todir="pkg-temp/webapps/" />
|
||||
<!-- <copy file="build/syndie.war" todir="pkg-temp/webapps/" /> -->
|
||||
<copy file="build/i2psnark.war" todir="pkg-temp/webapps/" />
|
||||
<!-- <copy file="apps/i2psnark/java/build/launch-i2psnark.jar" todir="pkg-temp/" /> -->
|
||||
<copy file="apps/i2psnark/jetty-i2psnark.xml" todir="pkg-temp/" />
|
||||
@ -351,11 +355,13 @@
|
||||
<mkdir dir="pkg-temp/eepsite" />
|
||||
<mkdir dir="pkg-temp/eepsite/webapps" />
|
||||
<mkdir dir="pkg-temp/eepsite/cgi-bin" />
|
||||
<!--
|
||||
<mkdir dir="pkg-temp/syndie" />
|
||||
<mkdir dir="pkg-temp/syndie/archive" />
|
||||
<mkdir dir="pkg-temp/syndie/archive/ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=" />
|
||||
<copy file="installer/resources/blogMeta.snm" tofile="pkg-temp/syndie/archive/ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=/meta.snm" />
|
||||
<copy file="installer/resources/blogPost.snd" tofile="pkg-temp/syndie/archive/ovpBy2mpO1CQ7deYhQ1cDGAwI6pQzLbWOm1Sdd0W06c=/1132012800001.snd" />
|
||||
-->
|
||||
</target>
|
||||
<target name="prepjupdate" depends="prepupdate, buildWEB">
|
||||
<copy file="build/ant.jar" todir="pkg-temp/lib/" />
|
||||
|
@ -14,8 +14,8 @@ package net.i2p;
|
||||
*
|
||||
*/
|
||||
public class CoreVersion {
|
||||
public final static String ID = "$Revision: 1.70 $ $Date: 2007-02-15 18:25:05 $";
|
||||
public final static String VERSION = "0.6.1.28";
|
||||
public final static String ID = "$Revision: 1.72 $ $Date: 2007-08-23 19:33:31 $";
|
||||
public final static String VERSION = "0.6.1.30";
|
||||
|
||||
public static void main(String args[]) {
|
||||
System.out.println("I2P Core version: " + VERSION);
|
||||
|
@ -59,6 +59,25 @@ iD8DBQFCZ38IWYfZ3rPnHH0RAgOHAJ4wNgmfO2AkL8IXiGnPtWrTlXcVogCfQ79z
|
||||
jP69nPbh4KLGhF+SD0+0bW4=
|
||||
=npPe
|
||||
-----END PGP SIGNATURE-----
|
||||
*/
|
||||
/*
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA1
|
||||
|
||||
*/
|
||||
/* zzz's key */
|
||||
private static final String DEFAULT_TRUSTED_KEY2 =
|
||||
"lT54eq3SH0TWWwQ1wgH6XPelIno7wH7UfiZOpQg-ZuxdNhc4UjjrohKdK" +
|
||||
"Zqfswt1ANPnmOlMewLGBESl7kJB9c5sByz~IOlNyz5BMLRC~R~ZC9QI4W" +
|
||||
"XwUBYW8BhYO2mkvtdOrcy690lDkwzdf5xLxlCBpQlTaLYzQVjVWBcvbCA=";
|
||||
/*
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
Version: GnuPG v1.4.6 (GNU/Linux)
|
||||
|
||||
iD8DBQFHdupcQVV2uqduC+0RAocuAKCR4ILLuz3RB8QT7zkadmS2LmFuMwCgweqG
|
||||
lFm5Fqx/iW5+k0QaQZ3W9mY=
|
||||
=V3i7
|
||||
-----END PGP SIGNATURE-----
|
||||
*/
|
||||
private static final String VALID_VERSION_CHARS = "0123456789.";
|
||||
private static final int VERSION_BYTES = 16;
|
||||
@ -92,14 +111,17 @@ jP69nPbh4KLGhF+SD0+0bW4=
|
||||
String propertyTrustedKeys = context.getProperty(PROP_TRUSTED_KEYS);
|
||||
|
||||
if ( (propertyTrustedKeys != null) && (propertyTrustedKeys.length() > 0) ) {
|
||||
StringTokenizer propertyTrustedKeysTokens = new StringTokenizer(propertyTrustedKeys, ",");
|
||||
StringTokenizer propertyTrustedKeysTokens = new StringTokenizer(propertyTrustedKeys, " ,\r\n");
|
||||
|
||||
while (propertyTrustedKeysTokens.hasMoreTokens())
|
||||
_trustedKeys.add(propertyTrustedKeysTokens.nextToken().trim());
|
||||
|
||||
} else {
|
||||
_trustedKeys.add(DEFAULT_TRUSTED_KEY);
|
||||
_trustedKeys.add(DEFAULT_TRUSTED_KEY2);
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("TrustedUpdate created, trusting " + _trustedKeys.size() + " keys.");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -180,6 +202,7 @@ jP69nPbh4KLGhF+SD0+0bW4=
|
||||
private static final void genKeysCLI(String publicKeyFile, String privateKeyFile) {
|
||||
FileOutputStream fileOutputStream = null;
|
||||
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
try {
|
||||
Object signingKeypair[] = _context.keyGenerator().generateSigningKeypair();
|
||||
SigningPublicKey signingPublicKey = (SigningPublicKey) signingKeypair[0];
|
||||
@ -273,7 +296,26 @@ jP69nPbh4KLGhF+SD0+0bW4=
|
||||
public ArrayList getTrustedKeys() {
|
||||
return _trustedKeys;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches the trusted keys for the current instance.
|
||||
*
|
||||
* @return A <code>String</code> containing the trusted keys,
|
||||
* delimited by CR LF line breaks.
|
||||
*/
|
||||
public String getTrustedKeysString() {
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
for (int i = 0; i < _trustedKeys.size(); i++) {
|
||||
// If something already buffered, first add line break.
|
||||
if (buf.length() > 0) buf.append("\r\n");
|
||||
buf.append((String) _trustedKeys.get(i));
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads the version string from a signed update file.
|
||||
*
|
||||
|
@ -236,6 +236,10 @@ public class DataHelper {
|
||||
if (split <= 0) continue;
|
||||
String key = line.substring(0, split);
|
||||
String val = line.substring(split+1);
|
||||
// Unescape line breaks after loading.
|
||||
// Remember: "\" needs escaping both for regex and string.
|
||||
val = val.replaceAll("\\\\r","\r");
|
||||
val = val.replaceAll("\\\\n","\n");
|
||||
if ( (key.length() > 0) && (val.length() > 0) )
|
||||
if (forceLowerCase)
|
||||
props.setProperty(key.toLowerCase(), val);
|
||||
|
@ -184,6 +184,7 @@ public class Rate {
|
||||
private static final int SLACK = 2000;
|
||||
public void coalesce() {
|
||||
long now = now();
|
||||
double correctedTotalValue; // for summaryListener which divides by rounded EventCount
|
||||
synchronized (_lock) {
|
||||
long measuredPeriod = now - _lastCoalesceDate;
|
||||
if (measuredPeriod < _period - SLACK) {
|
||||
@ -198,9 +199,14 @@ public class Rate {
|
||||
// how much were we off by? (so that we can sample down the measured values)
|
||||
double periodFactor = measuredPeriod / (double)_period;
|
||||
_lastTotalValue = _currentTotalValue / periodFactor;
|
||||
_lastEventCount = (long) ( (_currentEventCount + periodFactor - 1) / periodFactor);
|
||||
_lastEventCount = (long) (0.499999 + (_currentEventCount / periodFactor));
|
||||
_lastTotalEventTime = (long) (_currentTotalEventTime / periodFactor);
|
||||
_lastCoalesceDate = now;
|
||||
if (_currentEventCount == 0)
|
||||
correctedTotalValue = 0;
|
||||
else
|
||||
correctedTotalValue = _currentTotalValue *
|
||||
(_lastEventCount / (double) _currentEventCount);
|
||||
|
||||
if (_lastTotalValue > _extremeTotalValue) {
|
||||
_extremeTotalValue = _lastTotalValue;
|
||||
@ -213,7 +219,7 @@ public class Rate {
|
||||
_currentTotalEventTime = 0;
|
||||
}
|
||||
if (_summaryListener != null)
|
||||
_summaryListener.add(_lastTotalValue, _lastEventCount, _lastTotalEventTime, _period);
|
||||
_summaryListener.add(correctedTotalValue, _lastEventCount, _lastTotalEventTime, _period);
|
||||
}
|
||||
|
||||
public void setSummaryListener(RateSummaryListener listener) { _summaryListener = listener; }
|
||||
|
@ -18,6 +18,7 @@ import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.SocketTimeout;
|
||||
|
||||
/**
|
||||
* EepGet [-p localhost:4444]
|
||||
@ -33,8 +34,14 @@ public class EepGet {
|
||||
private String _proxyHost;
|
||||
private int _proxyPort;
|
||||
private int _numRetries;
|
||||
private long _minSize; // minimum and maximum acceptable response size, -1 signifies unlimited,
|
||||
private long _maxSize; // applied both against whole responses and chunks
|
||||
private String _outputFile;
|
||||
private OutputStream _outputStream;
|
||||
/** url we were asked to fetch */
|
||||
private String _url;
|
||||
/** the URL we actually fetch from (may differ from the _url in case of redirect) */
|
||||
private String _actualURL;
|
||||
private String _postData;
|
||||
private boolean _allowCaching;
|
||||
private List _listeners;
|
||||
@ -49,9 +56,18 @@ public class EepGet {
|
||||
private long _bytesRemaining;
|
||||
private int _currentAttempt;
|
||||
private String _etag;
|
||||
private String _lastModified;
|
||||
private boolean _encodingChunked;
|
||||
private boolean _notModified;
|
||||
private String _contentType;
|
||||
private boolean _transferFailed;
|
||||
private boolean _headersRead;
|
||||
private boolean _aborted;
|
||||
private long _fetchHeaderTimeout;
|
||||
private long _fetchEndTime;
|
||||
private long _fetchInactivityTimeout;
|
||||
private int _redirects;
|
||||
private String _redirectLocation;
|
||||
|
||||
public EepGet(I2PAppContext ctx, String proxyHost, int proxyPort, int numRetries, String outputFile, String url) {
|
||||
this(ctx, true, proxyHost, proxyPort, numRetries, outputFile, url);
|
||||
@ -69,27 +85,44 @@ public class EepGet {
|
||||
this(ctx, shouldProxy, proxyHost, proxyPort, numRetries, outputFile, url, true, null);
|
||||
}
|
||||
public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, String outputFile, String url, String postData) {
|
||||
this(ctx, shouldProxy, proxyHost, proxyPort, numRetries, outputFile, url, true, null, postData);
|
||||
this(ctx, shouldProxy, proxyHost, proxyPort, numRetries, -1, -1, outputFile, null, url, true, null, postData);
|
||||
}
|
||||
public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, String outputFile, String url, boolean allowCaching, String etag) {
|
||||
this(ctx, shouldProxy, proxyHost, proxyPort, numRetries, outputFile, url, allowCaching, etag, null);
|
||||
this(ctx, shouldProxy, proxyHost, proxyPort, numRetries, -1, -1, outputFile, null, url, allowCaching, etag, null);
|
||||
}
|
||||
public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, String outputFile, String url, boolean allowCaching, String etag, String postData) {
|
||||
public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, String outputFile, String url, boolean allowCaching, String etag, String lastModified) {
|
||||
this(ctx, shouldProxy, proxyHost, proxyPort, numRetries, -1, -1, outputFile, null, url, allowCaching, etag, lastModified, null);
|
||||
}
|
||||
public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, long minSize, long maxSize, String outputFile, OutputStream outputStream, String url, boolean allowCaching, String etag, String postData) {
|
||||
this(ctx, shouldProxy, proxyHost, proxyPort, numRetries, minSize, maxSize, outputFile, outputStream, url, allowCaching, etag, null, postData);
|
||||
}
|
||||
public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, long minSize, long maxSize,
|
||||
String outputFile, OutputStream outputStream, String url, boolean allowCaching,
|
||||
String etag, String lastModified, String postData) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(EepGet.class);
|
||||
_shouldProxy = shouldProxy;
|
||||
_shouldProxy = (proxyHost != null) && (proxyHost.length() > 0) && (proxyPort > 0) && shouldProxy;
|
||||
_proxyHost = proxyHost;
|
||||
_proxyPort = proxyPort;
|
||||
_numRetries = numRetries;
|
||||
_outputFile = outputFile;
|
||||
_minSize = minSize;
|
||||
_maxSize = maxSize;
|
||||
_outputFile = outputFile; // if outputFile is set, outputStream must be null
|
||||
_outputStream = outputStream; // if both are set, outputStream overrides outputFile
|
||||
_url = url;
|
||||
_actualURL = url;
|
||||
_postData = postData;
|
||||
_alreadyTransferred = 0;
|
||||
_bytesTransferred = 0;
|
||||
_bytesRemaining = -1;
|
||||
_currentAttempt = 0;
|
||||
_transferFailed = false;
|
||||
_headersRead = false;
|
||||
_aborted = false;
|
||||
_fetchHeaderTimeout = 45*1000;
|
||||
_listeners = new ArrayList(1);
|
||||
_etag = etag;
|
||||
_lastModified = lastModified;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -187,6 +220,7 @@ public class EepGet {
|
||||
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause);
|
||||
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt);
|
||||
public void headerReceived(String url, int currentAttempt, String key, String val);
|
||||
public void attempting(String url);
|
||||
}
|
||||
private class CLIStatusListener implements StatusListener {
|
||||
private int _markSize;
|
||||
@ -301,6 +335,7 @@ public class EepGet {
|
||||
buf.append("KBps");
|
||||
System.out.println(buf.toString());
|
||||
}
|
||||
public void attempting(String url) {}
|
||||
public void headerReceived(String url, int currentAttempt, String key, String val) {}
|
||||
}
|
||||
|
||||
@ -313,19 +348,52 @@ public class EepGet {
|
||||
* Blocking fetch, returning true if the URL was retrieved, false if all retries failed
|
||||
*
|
||||
*/
|
||||
public boolean fetch() {
|
||||
public boolean fetch() { return fetch(_fetchHeaderTimeout); }
|
||||
/**
|
||||
* Blocking fetch, timing out individual attempts if the HTTP response headers
|
||||
* don't come back in the time given. If the timeout is zero or less, this will
|
||||
* wait indefinitely.
|
||||
*/
|
||||
public boolean fetch(long fetchHeaderTimeout) {
|
||||
return fetch(fetchHeaderTimeout, -1, -1);
|
||||
}
|
||||
public boolean fetch(long fetchHeaderTimeout, long totalTimeout, long inactivityTimeout) {
|
||||
_fetchHeaderTimeout = fetchHeaderTimeout;
|
||||
_fetchEndTime = (totalTimeout > 0 ? System.currentTimeMillis() + totalTimeout : -1);
|
||||
_fetchInactivityTimeout = inactivityTimeout;
|
||||
_keepFetching = true;
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Fetching (proxied? " + _shouldProxy + ") url=" + _url);
|
||||
_log.debug("Fetching (proxied? " + _shouldProxy + ") url=" + _actualURL);
|
||||
while (_keepFetching) {
|
||||
SocketTimeout timeout = null;
|
||||
if (_fetchHeaderTimeout > 0)
|
||||
timeout = new SocketTimeout(_fetchHeaderTimeout);
|
||||
final SocketTimeout stimeout = timeout; // ugly
|
||||
timeout.setTimeoutCommand(new Runnable() {
|
||||
public void run() {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("timeout reached on " + _url + ": " + stimeout);
|
||||
_aborted = true;
|
||||
}
|
||||
});
|
||||
timeout.setTotalTimeoutPeriod(_fetchEndTime);
|
||||
try {
|
||||
sendRequest();
|
||||
doFetch();
|
||||
return true;
|
||||
for (int i = 0; i < _listeners.size(); i++)
|
||||
((StatusListener)_listeners.get(i)).attempting(_url);
|
||||
sendRequest(timeout);
|
||||
timeout.resetTimer();
|
||||
doFetch(timeout);
|
||||
timeout.cancel();
|
||||
if (!_transferFailed)
|
||||
return true;
|
||||
break;
|
||||
} catch (IOException ioe) {
|
||||
timeout.cancel();
|
||||
for (int i = 0; i < _listeners.size(); i++)
|
||||
((StatusListener)_listeners.get(i)).attemptFailed(_url, _bytesTransferred, _bytesRemaining, _currentAttempt, _numRetries, ioe);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("ERR: doFetch failed " + ioe);
|
||||
} finally {
|
||||
if (_out != null) {
|
||||
try {
|
||||
@ -344,39 +412,122 @@ public class EepGet {
|
||||
_currentAttempt++;
|
||||
if (_currentAttempt > _numRetries)
|
||||
break;
|
||||
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
|
||||
try {
|
||||
long delay = _context.random().nextInt(60*1000);
|
||||
Thread.sleep(5*1000+delay);
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _listeners.size(); i++)
|
||||
((StatusListener)_listeners.get(i)).transferFailed(_url, _bytesTransferred, _bytesRemaining, _currentAttempt);
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("All attempts failed for " + _url);
|
||||
return false;
|
||||
}
|
||||
|
||||
/** return true if the URL was completely retrieved */
|
||||
private void doFetch() throws IOException {
|
||||
readHeaders();
|
||||
private void doFetch(SocketTimeout timeout) throws IOException {
|
||||
_headersRead = false;
|
||||
_aborted = false;
|
||||
try {
|
||||
readHeaders();
|
||||
} finally {
|
||||
_headersRead = true;
|
||||
}
|
||||
if (_aborted)
|
||||
throw new IOException("Timed out reading the HTTP headers");
|
||||
|
||||
timeout.resetTimer();
|
||||
if (_fetchInactivityTimeout > 0)
|
||||
timeout.setInactivityTimeout(_fetchInactivityTimeout);
|
||||
else
|
||||
timeout.setInactivityTimeout(60*1000);
|
||||
|
||||
if (_redirectLocation != null) {
|
||||
try {
|
||||
URL oldURL = new URL(_actualURL);
|
||||
String query = oldURL.getQuery();
|
||||
if (query == null) query = "";
|
||||
if (_redirectLocation.startsWith("http://")) {
|
||||
if ( (_redirectLocation.indexOf('?') < 0) && (query.length() > 0) )
|
||||
_actualURL = _redirectLocation + "?" + query;
|
||||
else
|
||||
_actualURL = _redirectLocation;
|
||||
} else {
|
||||
URL url = new URL(_actualURL);
|
||||
if (_redirectLocation.startsWith("/"))
|
||||
_actualURL = "http://" + url.getHost() + ":" + url.getPort() + _redirectLocation;
|
||||
else
|
||||
_actualURL = "http://" + url.getHost() + ":" + url.getPort() + "/" + _redirectLocation;
|
||||
if ( (_actualURL.indexOf('?') < 0) && (query.length() > 0) )
|
||||
_actualURL = _actualURL + "?" + query;
|
||||
else
|
||||
_actualURL = _actualURL;
|
||||
}
|
||||
} catch (MalformedURLException mue) {
|
||||
throw new IOException("Redirected from an invalid URL");
|
||||
}
|
||||
_redirects++;
|
||||
if (_redirects > 5)
|
||||
throw new IOException("Too many redirects: to " + _redirectLocation);
|
||||
if (_log.shouldLog(Log.INFO)) _log.info("Redirecting to " + _redirectLocation);
|
||||
sendRequest(timeout);
|
||||
doFetch(timeout);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Headers read completely, reading " + _bytesRemaining);
|
||||
|
||||
boolean strictSize = (_bytesRemaining >= 0);
|
||||
|
||||
|
||||
// If minimum or maximum size defined, ensure they aren't exceeded
|
||||
if ((_minSize > 0) && (_bytesRemaining < _minSize))
|
||||
throw new IOException("HTTP response size " + _bytesRemaining + " violates minimum of " + _minSize + " bytes");
|
||||
if ((_maxSize > -1) && (_bytesRemaining > _maxSize))
|
||||
throw new IOException("HTTP response size " + _bytesRemaining + " violates maximum of " + _maxSize + " bytes");
|
||||
|
||||
int remaining = (int)_bytesRemaining;
|
||||
byte buf[] = new byte[1024];
|
||||
while (_keepFetching && ( (remaining > 0) || !strictSize )) {
|
||||
while (_keepFetching && ( (remaining > 0) || !strictSize ) && !_aborted) {
|
||||
int toRead = buf.length;
|
||||
if (strictSize && toRead > remaining)
|
||||
toRead = remaining;
|
||||
int read = _proxyIn.read(buf, 0, toRead);
|
||||
if (read == -1)
|
||||
break;
|
||||
timeout.resetTimer();
|
||||
_out.write(buf, 0, read);
|
||||
_bytesTransferred += read;
|
||||
// This seems necessary to properly resume a partial download into a stream,
|
||||
// as nothing else increments _alreadyTransferred, and there's no file length to check.
|
||||
// Hopefully this won't break compatibility with existing status listeners
|
||||
// (cause them to behave weird, or show weird numbers).
|
||||
_alreadyTransferred += read;
|
||||
if ((_maxSize > -1) && (_alreadyTransferred > _maxSize)) // could transfer a little over maxSize
|
||||
throw new IOException("Bytes transferred " + _alreadyTransferred + " violates maximum of " + _maxSize + " bytes");
|
||||
remaining -= read;
|
||||
if (remaining==0 && _encodingChunked) {
|
||||
if(_proxyIn.read()=='\r' && _proxyIn.read()=='\n') {
|
||||
remaining = (int) readChunkLength();
|
||||
int char1 = _proxyIn.read();
|
||||
if (char1 == '\r') {
|
||||
int char2 = _proxyIn.read();
|
||||
if (char2 == '\n') {
|
||||
remaining = (int) readChunkLength();
|
||||
} else {
|
||||
_out.write(char1);
|
||||
_out.write(char2);
|
||||
_bytesTransferred += 2;
|
||||
remaining -= 2;
|
||||
read += 2;
|
||||
}
|
||||
} else {
|
||||
_out.write(char1);
|
||||
_bytesTransferred++;
|
||||
remaining--;
|
||||
read++;
|
||||
}
|
||||
}
|
||||
timeout.resetTimer();
|
||||
if (read > 0)
|
||||
for (int i = 0; i < _listeners.size(); i++)
|
||||
((StatusListener)_listeners.get(i)).bytesTransferred(
|
||||
@ -386,15 +537,27 @@ public class EepGet {
|
||||
_encodingChunked?-1:_bytesRemaining,
|
||||
_url);
|
||||
}
|
||||
|
||||
|
||||
if (_out != null)
|
||||
_out.close();
|
||||
_out = null;
|
||||
|
||||
if (_aborted)
|
||||
throw new IOException("Timed out reading the HTTP data");
|
||||
|
||||
timeout.cancel();
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Done transferring " + _bytesTransferred);
|
||||
_log.debug("Done transferring " + _bytesTransferred + " (ok? " + !_transferFailed + ")");
|
||||
|
||||
if ( (_bytesRemaining == -1) || (remaining == 0) ){
|
||||
|
||||
if (_transferFailed) {
|
||||
// 404, etc - transferFailed is called after all attempts fail, by fetch() above
|
||||
for (int i = 0; i < _listeners.size(); i++)
|
||||
((StatusListener)_listeners.get(i)).attemptFailed(_url, _bytesTransferred, _bytesRemaining, _currentAttempt, _numRetries, new Exception("Attempt failed"));
|
||||
} else if ((_minSize > 0) && (_alreadyTransferred < _minSize)) {
|
||||
throw new IOException("Bytes transferred " + _alreadyTransferred + " violates minimum of " + _minSize + " bytes");
|
||||
} else if ( (_bytesRemaining == -1) || (remaining == 0) ) {
|
||||
for (int i = 0; i < _listeners.size(); i++)
|
||||
((StatusListener)_listeners.get(i)).transferComplete(
|
||||
_alreadyTransferred,
|
||||
@ -415,30 +578,57 @@ public class EepGet {
|
||||
boolean read = DataHelper.readLine(_proxyIn, buf);
|
||||
if (!read) throw new IOException("Unable to read the first line");
|
||||
int responseCode = handleStatus(buf.toString());
|
||||
|
||||
boolean redirect = false;
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("rc: " + responseCode + " for " + _actualURL);
|
||||
boolean rcOk = false;
|
||||
switch (responseCode) {
|
||||
case 200: // full
|
||||
_out = new FileOutputStream(_outputFile, false);
|
||||
if (_outputStream != null)
|
||||
_out = _outputStream;
|
||||
else
|
||||
_out = new FileOutputStream(_outputFile, false);
|
||||
_alreadyTransferred = 0;
|
||||
rcOk = true;
|
||||
break;
|
||||
case 206: // partial
|
||||
_out = new FileOutputStream(_outputFile, true);
|
||||
if (_outputStream != null)
|
||||
_out = _outputStream;
|
||||
else
|
||||
_out = new FileOutputStream(_outputFile, true);
|
||||
rcOk = true;
|
||||
break;
|
||||
case 301: // various redirections
|
||||
case 302:
|
||||
case 303:
|
||||
case 307:
|
||||
_alreadyTransferred = 0;
|
||||
rcOk = true;
|
||||
redirect = true;
|
||||
break;
|
||||
case 304: // not modified
|
||||
_bytesRemaining = 0;
|
||||
_keepFetching = false;
|
||||
_notModified = true;
|
||||
return;
|
||||
return;
|
||||
case 404: // not found
|
||||
_keepFetching = false;
|
||||
_transferFailed = true;
|
||||
return;
|
||||
case 416: // completed (or range out of reach)
|
||||
_bytesRemaining = 0;
|
||||
_keepFetching = false;
|
||||
return;
|
||||
default:
|
||||
rcOk = false;
|
||||
_transferFailed = true;
|
||||
}
|
||||
|
||||
// clear out the arguments, as we use the same variables for return values
|
||||
_etag = null;
|
||||
_lastModified = null;
|
||||
|
||||
buf.setLength(0);
|
||||
byte lookahead[] = new byte[3];
|
||||
while (true) {
|
||||
@ -471,6 +661,7 @@ public class EepGet {
|
||||
if (_encodingChunked) {
|
||||
_bytesRemaining = readChunkLength();
|
||||
}
|
||||
if (!redirect) _redirectLocation = null;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -560,11 +751,15 @@ public class EepGet {
|
||||
}
|
||||
} else if (key.equalsIgnoreCase("ETag")) {
|
||||
_etag = val.trim();
|
||||
} else if (key.equalsIgnoreCase("Last-Modified")) {
|
||||
_lastModified = val.trim();
|
||||
} else if (key.equalsIgnoreCase("Transfer-encoding")) {
|
||||
if (val.indexOf("chunked") != -1)
|
||||
_encodingChunked = true;
|
||||
} else if (key.equalsIgnoreCase("Content-Type")) {
|
||||
_contentType=val;
|
||||
} else if (key.equalsIgnoreCase("Location")) {
|
||||
_redirectLocation=val.trim();
|
||||
} else {
|
||||
// ignore the rest
|
||||
}
|
||||
@ -587,31 +782,48 @@ public class EepGet {
|
||||
private static final byte NL = '\n';
|
||||
private boolean isNL(byte b) { return (b == NL); }
|
||||
|
||||
private void sendRequest() throws IOException {
|
||||
File outFile = new File(_outputFile);
|
||||
if (outFile.exists())
|
||||
_alreadyTransferred = outFile.length();
|
||||
private void sendRequest(SocketTimeout timeout) throws IOException {
|
||||
if (_outputStream != null) {
|
||||
// We are reading into a stream supplied by a caller,
|
||||
// for which we cannot easily determine how much we've written.
|
||||
// Assume that _alreadyTransferred holds the right value
|
||||
// (we should never be restarted to work on an old stream).
|
||||
} else {
|
||||
File outFile = new File(_outputFile);
|
||||
if (outFile.exists())
|
||||
_alreadyTransferred = outFile.length();
|
||||
}
|
||||
|
||||
String req = getRequest();
|
||||
|
||||
if (_proxyIn != null) try { _proxyIn.close(); } catch (IOException ioe) {}
|
||||
if (_proxyOut != null) try { _proxyOut.close(); } catch (IOException ioe) {}
|
||||
if (_proxy != null) try { _proxy.close(); } catch (IOException ioe) {}
|
||||
|
||||
if (_shouldProxy) {
|
||||
_proxy = new Socket(_proxyHost, _proxyPort);
|
||||
} else {
|
||||
try {
|
||||
URL url = new URL(_url);
|
||||
String host = url.getHost();
|
||||
int port = url.getPort();
|
||||
if (port == -1)
|
||||
port = 80;
|
||||
_proxy = new Socket(host, port);
|
||||
URL url = new URL(_actualURL);
|
||||
if ("http".equals(url.getProtocol())) {
|
||||
String host = url.getHost();
|
||||
int port = url.getPort();
|
||||
if (port == -1)
|
||||
port = 80;
|
||||
_proxy = new Socket(host, port);
|
||||
} else {
|
||||
throw new IOException("URL is not supported:" + _actualURL);
|
||||
}
|
||||
} catch (MalformedURLException mue) {
|
||||
throw new IOException("Request URL is invalid");
|
||||
}
|
||||
}
|
||||
_proxyIn = _proxy.getInputStream();
|
||||
_proxyOut = _proxy.getOutputStream();
|
||||
|
||||
_proxyOut.write(req.toString().getBytes());
|
||||
|
||||
timeout.setSocket(_proxy);
|
||||
|
||||
_proxyOut.write(DataHelper.getUTF8(req.toString()));
|
||||
_proxyOut.flush();
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@ -623,12 +835,24 @@ public class EepGet {
|
||||
boolean post = false;
|
||||
if ( (_postData != null) && (_postData.length() > 0) )
|
||||
post = true;
|
||||
URL url = new URL(_actualURL);
|
||||
String proto = url.getProtocol();
|
||||
String host = url.getHost();
|
||||
int port = url.getPort();
|
||||
String path = url.getPath();
|
||||
String query = url.getQuery();
|
||||
if (query != null)
|
||||
path = path + "?" + query;
|
||||
if (!path.startsWith("/"))
|
||||
path = "/" + path;
|
||||
if ( (port == 80) || (port == 443) || (port <= 0) ) path = proto + "://" + host + path;
|
||||
else path = proto + "://" + host + ":" + port + path;
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Requesting " + path);
|
||||
if (post) {
|
||||
buf.append("POST ").append(_url).append(" HTTP/1.1\r\n");
|
||||
buf.append("POST ").append(_actualURL).append(" HTTP/1.1\r\n");
|
||||
} else {
|
||||
buf.append("GET ").append(_url).append(" HTTP/1.1\r\n");
|
||||
buf.append("GET ").append(_actualURL).append(" HTTP/1.1\r\n");
|
||||
}
|
||||
URL url = new URL(_url);
|
||||
buf.append("Host: ").append(url.getHost()).append("\r\n");
|
||||
if (_alreadyTransferred > 0) {
|
||||
buf.append("Range: bytes=");
|
||||
@ -641,11 +865,16 @@ public class EepGet {
|
||||
buf.append("Cache-control: no-cache\r\n");
|
||||
buf.append("Pragma: no-cache\r\n");
|
||||
}
|
||||
if (_etag != null) {
|
||||
if ((_etag != null) && (_alreadyTransferred <= 0)) {
|
||||
buf.append("If-None-Match: ");
|
||||
buf.append(_etag);
|
||||
buf.append("\r\n");
|
||||
}
|
||||
if ((_lastModified != null) && (_alreadyTransferred <= 0)) {
|
||||
buf.append("If-Modified-Since: ");
|
||||
buf.append(_lastModified);
|
||||
buf.append("\r\n");
|
||||
}
|
||||
if (post)
|
||||
buf.append("Content-length: ").append(_postData.length()).append("\r\n");
|
||||
buf.append("Connection: close\r\n\r\n");
|
||||
@ -660,6 +889,10 @@ public class EepGet {
|
||||
return _etag;
|
||||
}
|
||||
|
||||
public String getLastModified() {
|
||||
return _lastModified;
|
||||
}
|
||||
|
||||
public boolean getNotModified() {
|
||||
return _notModified;
|
||||
}
|
||||
|
@ -36,7 +36,8 @@ public class EepGetScheduler implements EepGet.StatusListener {
|
||||
public void fetch(boolean shouldBlock) {
|
||||
//Checking for a valid index is done in fetchNext, so we don't have to worry about it.
|
||||
if (shouldBlock) {
|
||||
fetchNext();
|
||||
while (_curURL < _urls.size())
|
||||
fetchNext();
|
||||
} else {
|
||||
fetch();
|
||||
}
|
||||
@ -77,6 +78,7 @@ public class EepGetScheduler implements EepGet.StatusListener {
|
||||
_listener.transferFailed(url, bytesTransferred, bytesRemaining, currentAttempt);
|
||||
fetchNext();
|
||||
}
|
||||
public void attempting(String url) { _listener.attempting(url); }
|
||||
|
||||
public void headerReceived(String url, int attemptNum, String key, String val) {}
|
||||
}
|
||||
|
71
core/java/src/net/i2p/util/SocketTimeout.java
Normal file
71
core/java/src/net/i2p/util/SocketTimeout.java
Normal file
@ -0,0 +1,71 @@
|
||||
package net.i2p.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public class SocketTimeout implements SimpleTimer.TimedEvent {
|
||||
private Socket _targetSocket;
|
||||
private long _startTime;
|
||||
private long _inactivityDelay;
|
||||
private long _lastActivity;
|
||||
private long _totalTimeoutTime;
|
||||
private boolean _cancelled;
|
||||
private Runnable _command;
|
||||
public SocketTimeout(long delay) { this(null, delay); }
|
||||
public SocketTimeout(Socket socket, long delay) {
|
||||
_inactivityDelay = delay;
|
||||
_targetSocket = socket;
|
||||
_cancelled = false;
|
||||
_lastActivity = _startTime = System.currentTimeMillis();
|
||||
_totalTimeoutTime = -1;
|
||||
SimpleTimer.getInstance().addEvent(SocketTimeout.this, delay);
|
||||
}
|
||||
public void timeReached() {
|
||||
if (_cancelled) return;
|
||||
|
||||
if ( ( (_totalTimeoutTime > 0) && (_totalTimeoutTime <= System.currentTimeMillis()) ) ||
|
||||
(_inactivityDelay + _lastActivity <= System.currentTimeMillis()) ) {
|
||||
if (_targetSocket != null) {
|
||||
try {
|
||||
if (!_targetSocket.isClosed())
|
||||
_targetSocket.close();
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
if (_command != null) _command.run();
|
||||
} else {
|
||||
SimpleTimer.getInstance().addEvent(SocketTimeout.this, _inactivityDelay);
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
_cancelled = true;
|
||||
SimpleTimer.getInstance().removeEvent(SocketTimeout.this);
|
||||
}
|
||||
public void setSocket(Socket s) { _targetSocket = s; }
|
||||
public void resetTimer() { _lastActivity = System.currentTimeMillis(); }
|
||||
public void setInactivityTimeout(long timeout) { _inactivityDelay = timeout; }
|
||||
public void setTotalTimeoutPeriod(long timeoutPeriod) {
|
||||
if (timeoutPeriod > 0)
|
||||
_totalTimeoutTime = _startTime + timeoutPeriod;
|
||||
else
|
||||
_totalTimeoutTime = -1;
|
||||
}
|
||||
public void setTimeoutCommand(Runnable job) { _command = job; }
|
||||
|
||||
private static final SimpleDateFormat _fmt = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss.SSS");
|
||||
private static String ts(long when) { synchronized (_fmt) { return _fmt.format(new Date(when)); } }
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("started on ");
|
||||
buf.append(ts(_startTime));
|
||||
buf.append("idle for ");
|
||||
buf.append(System.currentTimeMillis() - _lastActivity);
|
||||
buf.append("ms ");
|
||||
if (_totalTimeoutTime > 0)
|
||||
buf.append("total timeout at ").append(ts(_totalTimeoutTime));
|
||||
buf.append("cancelled? ").append(_cancelled);
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
262
history.txt
262
history.txt
@ -1,4 +1,264 @@
|
||||
$Id: history.txt,v 1.561 2007-03-17 16:18:12 jrandom Exp $
|
||||
$Id: history.txt,v 1.613 2008-01-08 21:15:45 zzz Exp $
|
||||
|
||||
2008-01-08 zzz
|
||||
* addressbook: Limit size of subscribed hosts.txt,
|
||||
don't save old etag or last-modified data
|
||||
* EepGet: Add some logging,
|
||||
enforce size limits even when size not in returned header,
|
||||
don't return old etag or last-modified data,
|
||||
don't call transferFailed listener more than once
|
||||
* Sign my update signing key
|
||||
* NewsFetcher: add last-modified support, reduce number of retries
|
||||
* Error pages: add icon and logo,
|
||||
clarify 'destination not found' and 'proxy not found' pages
|
||||
|
||||
|
||||
2008-01-07 zzz
|
||||
* profiles.jsp formatting cleanup
|
||||
* NTCP: Reduce max idle time from 60m to 20m
|
||||
* NTCP: Fix idle time on connections with zero messages,
|
||||
correctly drop these connections
|
||||
|
||||
2008-01-03 zzz
|
||||
* addressbook: Do basic validation of hostnames and destkeys
|
||||
* susidns: Add support for the private addressbook,
|
||||
update the text and links somewhat
|
||||
|
||||
2008-01-02 zzz
|
||||
* Add stats.i2p to the jump list
|
||||
* Impose 20MB limit on POSTs and catch OOMs in POST
|
||||
* eepsite_index.html: add stats.i2p services
|
||||
* addressbook: log source of new keys; disallow dests > 516 bytes
|
||||
* addressbook: convert hostnames to lower case to prevent duplicates
|
||||
* susidns: generalize references to orion
|
||||
|
||||
2007-12-29 zzz
|
||||
* Tweak IRC inbound PONG filtering to fix xchat/BitchX lagometers
|
||||
* Allow commas in router.trustedUpdateKeys and router.updateURL again
|
||||
* Change default news host from dev.i2p.net to dev.i2p
|
||||
* Change jetty timeout from 30 to 60 sec (thanks sponge!)
|
||||
|
||||
2007-12-28 zzz
|
||||
* Add zzz's update signing key
|
||||
|
||||
2007-12-26 Complication
|
||||
* Improve reseed handler (less repetitive code,
|
||||
avoid reporting errors when less than 10% of fetches fail)
|
||||
|
||||
2007-12-26 Complication
|
||||
* Escape both CR, LF and CR LF line breaks in Router.saveConfig()
|
||||
and unescape them in DataHelper.loadProps() to support
|
||||
saving and loading config properties with line breaks
|
||||
* Change the update URLs textbox into a textarea like keys have,
|
||||
so different URLs go on different lines
|
||||
* Modify TrustedUpdate to provide a method which supplies a key list
|
||||
delimited with CR LF line breaks
|
||||
* Modify DEFAULT_UPDATE_URL to supply a default URL list
|
||||
delimited with CR LF line breaks
|
||||
* Modify selectUpdateURL() to handle URL lists
|
||||
delimited by any kind of line breaks
|
||||
* Start saving trusted update keys
|
||||
* Improve formatting on configupdate.jsp
|
||||
|
||||
2007-12-22 zzz
|
||||
* Add support for multiple update URLs
|
||||
* Change default for update to use i2p proxy,
|
||||
add several URLs as defaults
|
||||
* Enable trusted key form on configupdate.jsp
|
||||
* Clarify the 'destination not found' error page
|
||||
|
||||
2007-12-16 zzz
|
||||
* i2psnark: remove anonymitytracker from default list
|
||||
|
||||
2007-12-10 zzz
|
||||
* Fix NPE in CLI TrustedUpdate keygen
|
||||
|
||||
2007-12-02 Complication
|
||||
* Commit SAM v2 patch from mkvore (thank you!)
|
||||
* Minor reformatting to preserve consistent whitespace
|
||||
in old SAM classes (new classes unaltered)
|
||||
|
||||
2007-12-01 Complication
|
||||
* Separate the checks "does Jetty .zip file need downloading"
|
||||
and "does Jetty .zip file need extracting" in the Jetty buildfile.
|
||||
First download (unless already done), then extract (unless done).
|
||||
|
||||
2007-11-26 zzz
|
||||
* i2psnark: add timeout for receive inactivity
|
||||
|
||||
2007-11-24 zzz
|
||||
* i2psnark: increase streaming lib write timeout to 240 sec and change
|
||||
timeout action from "ping" to "disconect", as the fix in .30 to
|
||||
honor options on outbound connections led to hung outbound connections
|
||||
(bitfield never transmitted, connection never dropped)
|
||||
|
||||
2007-11-06 jrandom
|
||||
* add i2host.i2p to the jump list
|
||||
|
||||
2007-10-11 zzz
|
||||
* IRC Proxy: Fix several possible anonymity holes:
|
||||
- Block CTCP in NOTICE messages
|
||||
- Block CTCP anywhere in PRIVMSG and NOTICE, not just at first character
|
||||
- Check for lower case commands
|
||||
(Thanks sponge!)
|
||||
|
||||
2007-10-07 jrandom
|
||||
* back out the NTCP backlog pushback, as it could be used to mount an
|
||||
active anonymity attack.
|
||||
|
||||
* 2007-10-07 0.6.1.30 released
|
||||
|
||||
2007-10-07 Complication
|
||||
* Fix an issue in EepGet whereby sending of "etag" and "lastModified" headers
|
||||
broke retrying.
|
||||
|
||||
2007-09-27 zzz
|
||||
* Implement pushback of NTCP transport backlog to the outbound tunnel selection code
|
||||
* Clean up the NTCP and UDP tables on peers.jsp to be consistent,
|
||||
fix some of the sorting
|
||||
|
||||
2007-09-22 zzz
|
||||
* Send messages for the same destination out the same outbound
|
||||
tunnel to reduce out-of-order delivery.
|
||||
|
||||
2007-09-19 zzz
|
||||
* i2psnark: Fix broken multifile torrent Delete;
|
||||
cleanup Storage resources in AddTorrent;
|
||||
don't autostart torrent after Create
|
||||
|
||||
2007-09-18 zzz
|
||||
* eepsite_index.html: Add links to trevorreznik address book
|
||||
* streaming lib: Fix SocketManagerFactory to honor options on outbound connections
|
||||
* streaming lib: Fix setDefaultOptions() when called with a ConnectionOptions parameter
|
||||
* i2psnark: Don't make outbound connections to already-connected peers
|
||||
* i2psnark: Debug logging cleanup
|
||||
|
||||
2007-09-14 zzz
|
||||
* eepget: Increase header timeout to 45s
|
||||
* HTTP proxy: Return a better error message for localhost requests
|
||||
* tunnels: Fix PooledTunnelCreatorConfig memory leak
|
||||
|
||||
2007-09-09 zzz
|
||||
* eepget: Add support for Last-Modified and If-Modified-Since
|
||||
* addressbook: Finish incomplete support for Last-Modified
|
||||
|
||||
2007-09-08 zzz
|
||||
* eepget: Copy over SocketTimeout.java file from syndie
|
||||
|
||||
2007-09-07 jrandom
|
||||
* eepget: Merge timeout support from syndie
|
||||
|
||||
* 2007-08-23 0.6.1.29 released
|
||||
|
||||
2007-08-12 zzz
|
||||
* readme.html - Add inproxy.tino.i2p, replace search.i2p with eepsites.i2p,
|
||||
tweak the eepsite and troubleshooting sections
|
||||
|
||||
2007-08-11 zzz
|
||||
* Add stats for individual tunnel rates (nice when graphed)
|
||||
* i2psnark: Fix outbound tunnel nickname
|
||||
|
||||
2007-08-05 Complication
|
||||
* Update the sharing calculator on config.jsp
|
||||
and explain the trade-off even more thoroughly.
|
||||
|
||||
2007-08-04 Complication
|
||||
* Lower the threshold between the K and L bandwidth class,
|
||||
so that K is now < 12 KB/s, instead of <= 16 KB/s.
|
||||
Hopefully this lets people with 128 kbit/s (16 KB/s) upload lines
|
||||
participate in routing, if they keep the default share percentage.
|
||||
|
||||
2007-07-16 zzz
|
||||
* i2psnark: Add tooltip info for choked/uninterested
|
||||
|
||||
2007-07-16 zzz
|
||||
* Make selection of graphed data configurable via configstats.jsp,
|
||||
remove most of the default graphs to save some memory
|
||||
|
||||
2007-07-15 zzz
|
||||
* Add current values to graph legends
|
||||
* Fix up previous Rate fix to check for divide by zero
|
||||
|
||||
2007-07-14 Complication
|
||||
* Take the post-download routerInfo size check back out of ReseedHandler,
|
||||
since it wasn't helpful, and a lower limit caused false warnings.
|
||||
* Give EepGet ability to enforce a min/max HTTP response size.
|
||||
* Enforce a maximum response size of 8 MB when ReseedHandler
|
||||
downloads into a ByteArrayOutputStream.
|
||||
* Refactor ReseedHandler/ReseedRunner from static to ordinary classes,
|
||||
change invocation from RouterConsoleRunner accordingly.
|
||||
* Add an EepGet status listener to ReseedHandler to log causes of reseed failure,
|
||||
provide status reports to indicate the progress of reseeding.
|
||||
* Enable icon for default eepsite, and the index page
|
||||
of the router console (more later).
|
||||
|
||||
2007-07-14 zzz
|
||||
* Clean up graphs.jsp - set K=1024 where appropriate,
|
||||
output image sizes in html, catch ooms, other minor tweaks
|
||||
* Fix current event count truncation which fixes graphs with low
|
||||
60-sec event counts displaying high values
|
||||
(bw.* and router.* graphs for example were 1.5x too high)
|
||||
Affects all "events per period" (non-lifetime) counts.
|
||||
|
||||
2007-07-09 zzz
|
||||
* i2psnark: give a better error message for a non-i2p torrent
|
||||
|
||||
2007-07-07 zzz
|
||||
* Add auto-detect IP/Port to NTCP. When enabled on config.jsp,
|
||||
SSU will notify/restart NTCP when the external address changes.
|
||||
Now you can enable inbound TCP without a static IP or dyndns service.
|
||||
|
||||
2007-07-04 zzz
|
||||
* Display calculated share bandwidth and remove load testing
|
||||
on config.jsp
|
||||
|
||||
2007-07-01 zzz
|
||||
* Replace broken option i2np.udp.alwaysPreferred with
|
||||
i2np.udp.preferred and adjust UDP bids; possible settings are
|
||||
"false" (default), "true", and "always".
|
||||
Default setting results in same behavior as before
|
||||
(NTCP is preferred unless it isn't established and UDP is established).
|
||||
Use to compare NTCP and UDP transports.
|
||||
|
||||
2007-06-27 jrandom
|
||||
* fix for a streaming lib bug that could leave a thread waiting
|
||||
indefinitely (thanks Complication!)
|
||||
|
||||
2007-06-16 Complication
|
||||
* First pass on EepGet and ReseedHandler improvements,
|
||||
please avoid use on routers which matter!
|
||||
* Give EepGet ability of downloading into an OutputStream,
|
||||
such as the ByteArrayOutputStream of ReseedHandler.
|
||||
* Detect failure to reseed better, report it persistently
|
||||
and more verbosely, provide a link to logs
|
||||
and suggest manual reseed.
|
||||
|
||||
2007-05-06 Complication
|
||||
* Fix the build.xml file, so the preppkg build target won't try copying files
|
||||
which became deprecated with the old Syndie (thanks for alerting, itsu!)
|
||||
|
||||
2007-03-31 zzz
|
||||
* Add trevorreznik jump server to the http proxy error page
|
||||
* Add anonymity to the trackers supporting details links in i2psnark
|
||||
|
||||
2007-03-24 zzz
|
||||
* Remove Syndie from build targets and navbar
|
||||
|
||||
2007-03-22 zzz
|
||||
* i2psnark tracker handling tweaks:
|
||||
- Add link to tracker details page (Postman only for now, requires bytemonsoon patch)
|
||||
- Add Base URL to tracker list configuration
|
||||
- Web page links built from tracker list Base URLs
|
||||
- Only build and sort tracker list once
|
||||
- Add anonymityWeb tracker to default list
|
||||
- Add tooltip info for TrackerErrs
|
||||
- Stop torrent if not registered with tracker
|
||||
- Mark temp files as delete on exit
|
||||
|
||||
2007-03-18 zzz
|
||||
* i2psnark: Cleanup some handling of saved partial pieces
|
||||
* i2psnark: Put bit counting in Bitfield.java for efficiency
|
||||
* i2psnark: Save torrent completion state in i2psnark.config
|
||||
|
||||
* 2007-03-17 0.6.1.28 released
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
; TC's hosts.txt guaranteed freshness
|
||||
; $Id: hosts.txt,v 1.169 2006-12-16 17:31:07 jrandom Exp $
|
||||
; $Id: hosts.txt,v 1.172 2007-11-06 06:26:01 jrandom Exp $
|
||||
; changelog:
|
||||
; (1.193) added i2host.i2p (was i2phost.i2p)
|
||||
; (1.192) added trevorreznik.i2p
|
||||
; (1.191) added trac.i2p
|
||||
; (1.190) added archive.syndie.i2p
|
||||
; (1.189) added mtn.i2p
|
||||
@ -504,4 +506,6 @@ ninja.i2p=XhyN8MgL6DwWE98g2DJlDLHy~02g58IF14LTb472cmSLRj~cHv7wyi1mxy21vtf12oPWRq
|
||||
mtn.i2p=RqXC4xbFK6t3g2wk9SO4RjY7mj1c0DmtMra5c1Md8t-DcNPSjQFmqT97pcZ5IR1JDKqyCO7RI~aATTTMPQexoEeqK9-6Poeh2RA1C81FzcA9sHvjLeg3eB1Cju-sE-IDeFntEvCC4w7wWnpmLzCfdXK7OjSK1wYc6OkqPOLVDEy-N-4UUPlZFWWUghpjBGXGayXz6JRKtoMIwhFQaiKdRvAs32ozM9RM0NWzrCaRLZBIQ6Gg1Ys1wF0-oBJgC4T4CN6SxJNaz9Dfw4GNtPyD6lq3S1osY1ccflm3itvUt3JC1J9ypoXzylBE5MuS-LTgbgbMdMFty07AoWB~EY8TwW8EQQO07GSzB7hm53u0iCEH44GexhKHtQP-hYbIr3mklo89BvfWIRGMTwUkAzYojzC-vOyIh16LWrQQhGbLvByKQSdWk9nInm3GEfqRVtSpuqd4m6iHzrBDZ3fvKjbuywot3hDNHitOHOedmBNA8neCzLkod8b0Z4xx~qRIEObxAAAA
|
||||
archive.syndie.i2p=iXX0DadZTJQpPr1to0OmQ4xokHgx1HYd5ec7~zIjQ80W~p4kRCYJmEzibH2Kn59Gi04SAXeA2O9i3bNqfGCQjsbz7UcjPGrW6-UrckXVXW67Moxp7QWY6i-aKuVYM3bqYxUL2mWvcDzJ8D0ecMpvasxhxwXpdFn2J6CGboMxeGV8R3hwwlNYbYoKgHr74qEJaIZpm1FrRWvNHV5cMv363iWnPy72XspQefk79-VOjPsxfummosU7gqlxl5teyiGKNzMs3G6iJyfVHO8IlKtdn~P~ET9p7zWlTPgV8NTyCVB-Wn5S3JMkMGOFZR7wSlxSwGFpTFQKc7mxVTtLZ5nWcV2OhvOIxRZ31RvGJZyVs562RC5aMfyqcM5IHQiZVlmkhzJKIy9VDw8tKayQtRM-xeN5k6Qr7iMmYIRORwuAODkYApoMD9a0eJ6ZYOSgBMOCSvYcwfT8axRY~GabiHm0QC82mo-nDgrUypGKtOPMI9MIqMTsb8Yl-UGWn6twBAIzAAAA
|
||||
trac.i2p=OBnF9NtkEsPij2F-lp3bWDVrJsPQQPdq6adlpq0N4BY1XRjtDBZl~EpDdk7roq49~ptKAQG2cNUeBEKIIrdlZhJio5pMwUl6YinizzkNTFfZipB5OKoB7PBulxkw-N9mKMhS1btd9ajcV8tiP3xiv7VSlgiDwbdKg1fmkvNrVrJnzkN3-ey2kebYnbh7jjU2gPFUl~CwSEkIi6AK9EfqmFR-DUVohyygqAY~fi4EMeTVXGUqftXSNFYUwpRJgFrWRPTurtZnJK5403q67oEk0eWrPIZ8ytJWSBfffAXL3ts~0O1FZeKXUccsAl33j70~lklSolNVLJ40y-6X5ZLWajmX0ONU3j0qI5A~7fgNgsg-vKypPDuzl8ug-D~BmhqdAf0sRYmziDVwTgU~WRB6IzhhXFR6CbwrGXdgOGg2qNT1eOnMwGo3SMMJ7kK88VC5LdYg2dyiyjZATuvT92QdZglrVQIeBqAehcFjOBuycC1ED3AOak8D9Xplj7V6hN-HAAAA
|
||||
trevorreznik.i2p=FxoG5dx6WuF8gu0Fc4ItB61hd8dBZ0L6FjkvoA-kQrC0DcY5YUqKBUmrcxzKhKo4nLyg0jRV-RSNUuWsali44dmpHQ6wuFjCGTr5zMk0NoVUpBsBNtHLEQWxROnuQFlQAq6x~eVdU5bAvnNFd-6RXDJ86Br9bf5YG4PHmZPon7ld3t1kAvPaZepITKlIq5iXy3va-pzAMFEIhDtLMzFjGhLWMg9m-8tkx4577WvzUWbSAUdxKkxwjecIzbTDTQsZld~t5MkaMmn5UpNX6Gh3iDUPWHXOjn4I8MIELiSaeRdpHDgua3mo~p8oO0QvWBgn~jQ8yr6nz7vMAdoRtOwPlUqHCCM1BsPVcN9a92rPpTFBk0BXikjJJrkDDIaFjJnKmRAsEUoa-eHaPV~RS0QPrfuzlp0XjBzQcAQjNIw6hO4xqJujSs8SdObMpQAqCR5Sc5NzOvTr5eAzCIz0gmQnKrA1RJVOebKDERM~qQ6rFrdVDkjLEJd6SyjUr-TdIrayAAAA
|
||||
i2host.i2p=FkX89~FOWc2HjTeVerud7hzZCtQNDKqEvMeJiDEnSzneA44hhDEDSbjiDuEWOCPwvgCwrzMg9o3apHXosNMnRVTYlYIJWb1~fgRnzkHIJnz2yrojLcTElTLMgQSzHdV04ock3oPHjw8r2j73hnTiq5u3HhexT0D-IK02S0GUd9cPcP2YnWlNMnGuA-J6s7VHUd5mUhBO3LeOcOusBUOjsbt79bwBZYsnnVV~JRSVzHPd8kYbRMEjDuyaIJHEr2pkwwtrk~BqLeZ5UCbbVob1-733FtOfQXjKTpFPK~IlHDM7ItgXt77U5KZHkZ54WQYJNCE6HlE7LhFB79cqjgvxXTfvyjoqZVJ1lq1yFS8GtLcnurJLkN5BleCEDJEK3D7kCymCpngW9wlKjakEc85ZculU65neqIpqIk6noir6e1vseeUZKxBM3k6AYkLoFYSh98PpENjTHUdaNTl1kQA2Mmi9zobGS0gn0~RWHFord19TC5ReBtOFWkseksHp-5-nAAAA
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<i2p.news date="$Date: 2007-02-15 18:25:04 $">
|
||||
<i2p.release version="0.6.1.28" date="2007/02/15" minVersion="0.6"
|
||||
<i2p.news date="$Date: 2007-08-23 19:33:29 $">
|
||||
<i2p.release version="0.6.1.30" date="2007/02/15" minVersion="0.6"
|
||||
anonurl="http://i2p/NF2RLVUxVulR3IqK0sGJR0dHQcGXAzwa6rEO4WAWYXOHw-DoZhKnlbf1nzHXwMEJoex5nFTyiNMqxJMWlY54cvU~UenZdkyQQeUSBZXyuSweflUXFqKN-y8xIoK2w9Ylq1k8IcrAFDsITyOzjUKoOPfVq34rKNDo7fYyis4kT5bAHy~2N1EVMs34pi2RFabATIOBk38Qhab57Umpa6yEoE~rbyR~suDRvD7gjBvBiIKFqhFueXsR2uSrPB-yzwAGofTXuklofK3DdKspciclTVzqbDjsk5UXfu2nTrC1agkhLyqlOfjhyqC~t1IXm-Vs2o7911k7KKLGjB4lmH508YJ7G9fLAUyjuB-wwwhejoWqvg7oWvqo4oIok8LG6ECR71C3dzCvIjY2QcrhoaazA9G4zcGMm6NKND-H4XY6tUWhpB~5GefB3YczOqMbHq4wi0O9MzBFrOJEOs3X4hwboKWANf7DT5PZKJZ5KorQPsYRSq0E3wSOsFCSsdVCKUGsAAAA/i2p/i2pupdate.sud"
|
||||
publicurl="http://dev.i2p.net/i2p/i2pupdate.sud"
|
||||
anonannouncement="http://i2p/NF2RLVUxVulR3IqK0sGJR0dHQcGXAzwa6rEO4WAWYXOHw-DoZhKnlbf1nzHXwMEJoex5nFTyiNMqxJMWlY54cvU~UenZdkyQQeUSBZXyuSweflUXFqKN-y8xIoK2w9Ylq1k8IcrAFDsITyOzjUKoOPfVq34rKNDo7fYyis4kT5bAHy~2N1EVMs34pi2RFabATIOBk38Qhab57Umpa6yEoE~rbyR~suDRvD7gjBvBiIKFqhFueXsR2uSrPB-yzwAGofTXuklofK3DdKspciclTVzqbDjsk5UXfu2nTrC1agkhLyqlOfjhyqC~t1IXm-Vs2o7911k7KKLGjB4lmH508YJ7G9fLAUyjuB-wwwhejoWqvg7oWvqo4oIok8LG6ECR71C3dzCvIjY2QcrhoaazA9G4zcGMm6NKND-H4XY6tUWhpB~5GefB3YczOqMbHq4wi0O9MzBFrOJEOs3X4hwboKWANf7DT5PZKJZ5KorQPsYRSq0E3wSOsFCSsdVCKUGsAAAA/pipermail/i2p/2005-September/000878.html"
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
<info>
|
||||
<appname>i2p</appname>
|
||||
<appversion>0.6.1.28</appversion>
|
||||
<appversion>0.6.1.30</appversion>
|
||||
<authors>
|
||||
<author name="I2P" email="support@i2p.net"/>
|
||||
</authors>
|
||||
|
@ -6,6 +6,7 @@ Proxy-Connection: close
|
||||
|
||||
<html><head>
|
||||
<title>Destination key conflict</title>
|
||||
<link rel="shortcut icon" href="http://localhost:7657/favicon.ico" />
|
||||
<style type='text/css'>
|
||||
div.warning {
|
||||
margin: 0em 1em 1em 224px;
|
||||
@ -31,7 +32,7 @@ div.logo {
|
||||
</head>
|
||||
<body>
|
||||
<div class=logo>
|
||||
<a href="http://localhost:7657/index.jsp">Router Console</a><br />
|
||||
<a href="http://localhost:7657/index.jsp" title="Router Console"><img src="http://localhost:7657/i2plogo.png" alt="Router Console" width="187" height="35" border="0"/></a><br />
|
||||
[<a href="http://localhost:7657/config.jsp">configuration</a> | <a href="http://localhost:7657/help.jsp">help</a>]
|
||||
</div>
|
||||
<div class=warning id=warning>
|
||||
|
@ -6,6 +6,7 @@ Proxy-Connection: close
|
||||
|
||||
<html><head>
|
||||
<title>Eepsite not reachable</title>
|
||||
<link rel="shortcut icon" href="http://localhost:7657/favicon.ico" />
|
||||
<style type='text/css'>
|
||||
div.warning {
|
||||
margin: 0em 1em 1em 224px;
|
||||
@ -31,13 +32,13 @@ div.logo {
|
||||
</head>
|
||||
<body>
|
||||
<div class=logo>
|
||||
<a href="http://localhost:7657/index.jsp">Router Console</a><br />
|
||||
<a href="http://localhost:7657/index.jsp" title="Router Console"><img src="http://localhost:7657/i2plogo.png" alt="Router Console" width="187" height="35" border="0"/></a><br />
|
||||
[<a href="http://localhost:7657/config.jsp">configuration</a> | <a href="http://localhost:7657/help.jsp">help</a>]
|
||||
</div>
|
||||
<div class=warning id=warning>
|
||||
The eepsite was not reachable. Perhaps
|
||||
the link you are following is
|
||||
bad. The eepsite could also
|
||||
be temporarily offline. You may want to
|
||||
The eepsite was not reachable.
|
||||
The eepsite is temporarily offline, there is network congestion,
|
||||
or your router is not yet well-integrated with peers.
|
||||
You may want to
|
||||
<a href="javascript: window.location.reload()">retry</a>.
|
||||
<BR><BR>Could not find the following destination:<BR><BR>
|
||||
|
@ -6,6 +6,7 @@ Proxy-Connection: close
|
||||
|
||||
<html><head>
|
||||
<title>Invalid eepsite destination</title>
|
||||
<link rel="shortcut icon" href="http://localhost:7657/favicon.ico" />
|
||||
<style type='text/css'>
|
||||
div.warning {
|
||||
margin: 0em 1em 1em 224px;
|
||||
@ -31,7 +32,7 @@ div.logo {
|
||||
</head>
|
||||
<body>
|
||||
<div class=logo>
|
||||
<a href="http://localhost:7657/index.jsp">Router Console</a><br />
|
||||
<a href="http://localhost:7657/index.jsp" title="Router Console"><img src="http://localhost:7657/i2plogo.png" alt="Router Console" width="187" height="35" border="0"/></a><br />
|
||||
[<a href="http://localhost:7657/config.jsp">configuration</a> | <a href="http://localhost:7657/help.jsp">help</a>]
|
||||
</div>
|
||||
<div class=warning id=warning>
|
||||
|
@ -6,6 +6,7 @@ Proxy-Connection: close
|
||||
|
||||
<html><head>
|
||||
<title>Eepsite unknown</title>
|
||||
<link rel="shortcut icon" href="http://localhost:7657/favicon.ico" />
|
||||
<style type='text/css'>
|
||||
div.warning {
|
||||
margin: 0em 1em 1em 224px;
|
||||
@ -31,7 +32,7 @@ div.logo {
|
||||
</head>
|
||||
<body>
|
||||
<div class=logo>
|
||||
<a href="http://localhost:7657/index.jsp">Router Console</a><br />
|
||||
<a href="http://localhost:7657/index.jsp" title="Router Console"><img src="http://localhost:7657/i2plogo.png" alt="Router Console" width="187" height="35" border="0"/></a><br />
|
||||
[<a href="http://localhost:7657/config.jsp">configuration</a> | <a href="http://localhost:7657/help.jsp">help</a>]
|
||||
</div>
|
||||
<div class=warning id=warning>
|
||||
|
@ -6,6 +6,7 @@ Proxy-Connection: close
|
||||
|
||||
<html><head>
|
||||
<title>Outproxy Not Found</title>
|
||||
<link rel="shortcut icon" href="http://localhost:7657/favicon.ico" />
|
||||
<style type='text/css'>
|
||||
div.warning {
|
||||
margin: 0em 1em 1em 224px;
|
||||
@ -32,13 +33,14 @@ div.logo {
|
||||
</head>
|
||||
<body>
|
||||
<div class=logo>
|
||||
<a href="http://localhost:7657/index.jsp">Router Console</a><br />
|
||||
<a href="http://localhost:7657/index.jsp" title="Router Console"><img src="http://localhost:7657/i2plogo.png" alt="Router Console" width="187" height="35" border="0"/></a><br />
|
||||
[<a href="http://localhost:7657/config.jsp">configuration</a> | <a href="http://localhost:7657/help.jsp">help</a>]
|
||||
</div>
|
||||
<div class=warning id=warning>
|
||||
The WWW Outproxy was not found.
|
||||
It could
|
||||
be temporarily offline. You may want to
|
||||
It is offline, there is network congestion,
|
||||
or your router is not yet well-integrated with peers.
|
||||
You may want to
|
||||
<a href="javascript: parent.window.location.reload()">retry</a>
|
||||
as this will randomly reselect an outproxy from the pool you have defined
|
||||
<a href="http://localhost:7657/i2ptunnel/index.jsp">here</a>
|
||||
|
@ -1,6 +1,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Welcome to your eepsite</title>
|
||||
<link rel="shortcut icon" href="favicon.ico" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>Welcome to your eepsite</h1>
|
||||
@ -13,16 +14,15 @@
|
||||
<a href="http://localhost:7657/i2ptunnel/edit.jsp?tunnel=3">configuration page</a>).
|
||||
The instructions below detail how to assign a name like "mysite.i2p" to your key and
|
||||
start up your eepsite.</p>
|
||||
<p>You can also reach your eepsite locally through
|
||||
<a href="http://localhost:7658/">http://localhost:7658/</a>. If you
|
||||
want to change the port number, edit the file ./eepsite/jetty.xml and
|
||||
update the I2PTunnel configuration accordingly.</p>
|
||||
<p>You can reach your eepsite locally through
|
||||
<a href="http://localhost:7658/">http://localhost:7658/</a>.
|
||||
</p>
|
||||
|
||||
<h2>Step-by-step instructions for starting your new eepsite and announcing it to the I2P community</h2>
|
||||
Your eepsite is stopped by default.
|
||||
After you start it, it will be difficult for other people to find because it
|
||||
doesn't have a name and they don't have your really long Base64 key.
|
||||
You could just tell people that really long key but thankfully i2p has an address book
|
||||
You could just tell people that really long key, but thankfully i2p has an address book
|
||||
and several easy ways to tell people about your eepsite. Here's detailed instructions.
|
||||
<ul>
|
||||
<li>Pick a name for your eepsite (<i>something</i>.i2p). Use all lower-case.
|
||||
@ -51,35 +51,49 @@
|
||||
<li>Before you tell the world about your new eepsite, you should add some content.
|
||||
Go to i2p/eepsite/docroot and copy this document (index.html) to help.html so you can refer to it later.
|
||||
Now edit index.html and add content, pictures, and whatever you would like to share.
|
||||
<li>Now it's time to add your eepsite to I2P's most popular address book.
|
||||
To do that, you must to tell <a href="http://orion.i2p/">orion.i2p</a>
|
||||
what your eepsite name and key are. Orion has a web interface where you can add a key to the address book.
|
||||
The key entry form is <a href="http://orion.i2p/list/">here</a>.
|
||||
<li>Now it's time to add your eepsite to an I2P address book hosted by a site
|
||||
such as <a href="http://stats.i2p/">stats.i2p</a>.
|
||||
That is, you must enter
|
||||
your eepsite name and key into a web interface on one or more of these sites.
|
||||
Here is <a href="http://stats.i2p/i2p/addkey.html">the key entry form at stats.i2p</a>,
|
||||
here is <a href="http://orion.i2p/list/">the key entry form at orion.i2p</a> and
|
||||
here is <a href="http://trevorreznik.i2p/host-database/">the key entry form at trevorreznik.i2p</a>.
|
||||
Again, your key is the entire "Local destination" key on the
|
||||
<a href="http://localhost:7657/i2ptunnel/edit.jsp?tunnel=3">eepsite i2ptunnel configuration page</a>.
|
||||
Be sure you get the whole thing, ending with "AAAA".
|
||||
Don't forget to click "add a key".
|
||||
Check to see if it reports the key was added.
|
||||
Since most routers periodically get address book updates from orion, within several hours others will be able to find your
|
||||
Since many routers periodically get address book updates from these sites, within several hours others will be able to find your
|
||||
website by simply typing <i>something</i>.i2p into their browser.
|
||||
<li>If you are in a hurry and can't wait a few hours, you can tell people to use orion's "jump" address helper redirection service.
|
||||
This will work within a few minutes of your entering the key to orion.i2p.
|
||||
Test it yourself first by entering http://orion.i2p/jump/<i>something</i>.i2p into your browser.
|
||||
<li>Speaking of address book updates, this would be a good time to add some more addressbooks
|
||||
to your own subscription list. Go to your <a href="http://localhost:7657/susidns/subscriptions.jsp">subscriptions configuration page</a>
|
||||
and add a couple of these -
|
||||
<a href="http://orion.i2p/hosts.txt">http://orion.i2p/hosts.txt</a>,
|
||||
<a href="http://tino.i2p/hosts.txt">http://tino.i2p/hosts.txt</a>,
|
||||
<a href="http://stats.i2p/newhosts.txt">http://stats.i2p/newhosts.txt</a>,
|
||||
<a href="http://trevorreznik.i2p/hosts.txt">http://trevorreznik.i2p/hosts.txt</a> and hit "Save".
|
||||
Now you will get updates too!
|
||||
<li>If you are in a hurry and can't wait a few hours, you can tell people to use a "jump" address helper redirection service.
|
||||
This will work within a few minutes of your entering the key to an address book.
|
||||
Test it yourself first by entering http://orion.i2p/jump/<i>something</i>.i2p
|
||||
or http://stats.i2p/cgi-bin/jump.cgi?a=<i>something</i>.i2p or
|
||||
or http://trevorreznik.i2p/cgi-bin/jump.php?hostname=<i>something</i>.i2p into your browser.
|
||||
Once it's working, then you can tell others to use it.
|
||||
<li>Some people check <a href="http://orion.i2p/list/">orion.i2p/list/</a> for new eepsites, so you may start getting
|
||||
<li>Some people check eepsite lists such as
|
||||
<a href="http://inproxy.tino.i2p/status.php">inproxy.tino.i2p/status.php</a> for new eepsites, so you may start getting
|
||||
a few visitors. But there are plenty of other ways to tell people. Here are a few ideas:
|
||||
<ul>
|
||||
<li>Post a message on the <a href="http://forum.i2p/viewforum.php?f=16">Eepsite announce forum</a>
|
||||
on <a href="http://forum.i2p/">forum.i2p</a>.
|
||||
<li>Tell people about it on the #i2p or #i2p-chat channels on IRC.
|
||||
<li>Put it in a new post on <a href="syndie.i2p.net">the new Syndie</a>.
|
||||
<li>Put it in a new post on <a href="http://syndie.i2p.net">the new Syndie</a>.
|
||||
<li>Put it in <a href="http://ugha.i2p/EepsiteIndex">Ugha's Eepsite Index Wiki</a>
|
||||
</ul>
|
||||
Note that some sites recommend pasting in that really long destination key.
|
||||
You can if you want - but
|
||||
if you have successfully posted your key on the <a href="http://orion.i2p/list/">orion page here</a>,
|
||||
tested it using http://orion.i2p/jump/<i>something</i>.i2p, and waited 24 hours for orion's
|
||||
address book to propagate to others, that shouldn't be necessary.
|
||||
if you have successfully posted your key at an add-key service,
|
||||
tested it using a jump service, and waited 24 hours for the
|
||||
address book update to propagate to others, that shouldn't be necessary.
|
||||
<li>If you have any questions try IRC #i2p or the
|
||||
<a href="http://forum.i2p/viewforum.php?f=10">technical problems section</a> on
|
||||
<a href="http://forum.i2p/">forum.i2p</a>.
|
||||
|
@ -25,7 +25,7 @@
|
||||
</Arg>
|
||||
<Set name="MinThreads">3</Set>
|
||||
<Set name="MaxThreads">10</Set>
|
||||
<Set name="MaxIdleTimeMs">30000</Set>
|
||||
<Set name="MaxIdleTimeMs">60000</Set>
|
||||
<Set name="LowResourcePersistTimeMs">1000</Set>
|
||||
<Set name="ConfidentialPort">8443</Set>
|
||||
<Set name="IntegralPort">8443</Set>
|
||||
|
19
news.xml
19
news.xml
@ -1,5 +1,5 @@
|
||||
<i2p.news date="$Date: 2007-03-15 03:21:35 $">
|
||||
<i2p.release version="0.6.1.28" date="2007/02/15" minVersion="0.6"
|
||||
<i2p.news date="$Date: 2007-10-07 22:09:35 $">
|
||||
<i2p.release version="0.6.1.30" date="2007/02/15" minVersion="0.6"
|
||||
anonurl="http://i2p/NF2RLVUxVulR3IqK0sGJR0dHQcGXAzwa6rEO4WAWYXOHw-DoZhKnlbf1nzHXwMEJoex5nFTyiNMqxJMWlY54cvU~UenZdkyQQeUSBZXyuSweflUXFqKN-y8xIoK2w9Ylq1k8IcrAFDsITyOzjUKoOPfVq34rKNDo7fYyis4kT5bAHy~2N1EVMs34pi2RFabATIOBk38Qhab57Umpa6yEoE~rbyR~suDRvD7gjBvBiIKFqhFueXsR2uSrPB-yzwAGofTXuklofK3DdKspciclTVzqbDjsk5UXfu2nTrC1agkhLyqlOfjhyqC~t1IXm-Vs2o7911k7KKLGjB4lmH508YJ7G9fLAUyjuB-wwwhejoWqvg7oWvqo4oIok8LG6ECR71C3dzCvIjY2QcrhoaazA9G4zcGMm6NKND-H4XY6tUWhpB~5GefB3YczOqMbHq4wi0O9MzBFrOJEOs3X4hwboKWANf7DT5PZKJZ5KorQPsYRSq0E3wSOsFCSsdVCKUGsAAAA/i2p/i2pupdate.sud"
|
||||
publicurl="http://dev.i2p.net/i2p/i2pupdate.sud"
|
||||
anonannouncement="http://i2p/NF2RLVUxVulR3IqK0sGJR0dHQcGXAzwa6rEO4WAWYXOHw-DoZhKnlbf1nzHXwMEJoex5nFTyiNMqxJMWlY54cvU~UenZdkyQQeUSBZXyuSweflUXFqKN-y8xIoK2w9Ylq1k8IcrAFDsITyOzjUKoOPfVq34rKNDo7fYyis4kT5bAHy~2N1EVMs34pi2RFabATIOBk38Qhab57Umpa6yEoE~rbyR~suDRvD7gjBvBiIKFqhFueXsR2uSrPB-yzwAGofTXuklofK3DdKspciclTVzqbDjsk5UXfu2nTrC1agkhLyqlOfjhyqC~t1IXm-Vs2o7911k7KKLGjB4lmH508YJ7G9fLAUyjuB-wwwhejoWqvg7oWvqo4oIok8LG6ECR71C3dzCvIjY2QcrhoaazA9G4zcGMm6NKND-H4XY6tUWhpB~5GefB3YczOqMbHq4wi0O9MzBFrOJEOs3X4hwboKWANf7DT5PZKJZ5KorQPsYRSq0E3wSOsFCSsdVCKUGsAAAA/pipermail/i2p/2005-September/000878.html"
|
||||
@ -10,16 +10,17 @@
|
||||
anonlogs="http://i2p/Nf3ab-ZFkmI-LyMt7GjgT-jfvZ3zKDl0L96pmGQXF1B82W2Bfjf0n7~288vafocjFLnQnVcmZd~-p0-Oolfo9aW2Rm-AhyqxnxyLlPBqGxsJBXjPhm1JBT4Ia8FB-VXt0BuY0fMKdAfWwN61-tj4zIcQWRxv3DFquwEf035K~Ra4SWOqiuJgTRJu7~o~DzHVljVgWIzwf8Z84cz0X33pv-mdG~~y0Bsc2qJVnYwjjR178YMcRSmNE0FVMcs6f17c6zqhMw-11qjKpY~EJfHYCx4lBWF37CD0obbWqTNUIbL~78vxqZRT3dgAgnLixog9nqTO-0Rh~NpVUZnoUi7fNR~awW5U3Cf7rU7nNEKKobLue78hjvRcWn7upHUF45QqTDuaM3yZa7OsjbcH-I909DOub2Q0Dno6vIwuA7yrysccN1sbnkwZbKlf4T6~iDdhaSLJd97QCyPOlbyUfYy9QLNExlRqKgNVJcMJRrIual~Lb1CLbnzt0uvobM57UpqSAAAA/meeting141"
|
||||
publiclogs="http://www.i2p.net/meeting141" />
|
||||
•
|
||||
2007-02-16: 0.6.1.27
|
||||
<a href="http://dev.i2p/pipermail/i2p/2007-February/001335.html">released</a>
|
||||
2007-10-07: 0.6.1.30
|
||||
<a href="http://dev.i2p/pipermail/i2p/2007-October/001356.html">released</a>
|
||||
with
|
||||
<a href="http://forum.i2p/viewtopic.php?t=2027">i2psnark</a>
|
||||
and floodfill fixes and improvements.
|
||||
streaming lib, eepget, and i2psnark improvements, and a memory leak fix.
|
||||
<br />
|
||||
<!--
|
||||
•
|
||||
2007-03-13:
|
||||
<a href="http://dev.i2p/pipermail/i2p/2007-March/001338.html">status notes</a>
|
||||
2007-04-10:
|
||||
<a href="http://dev.i2p/pipermail/i2p/2007-April/001343.html">status notes</a>
|
||||
and
|
||||
<a href="http://www.i2p/meeting202">meeting log</a>
|
||||
<a href="http://www.i2p/meeting206">meeting log</a>
|
||||
<br />
|
||||
-->
|
||||
</i2p.news>
|
||||
|
34
readme.html
34
readme.html
@ -1,5 +1,5 @@
|
||||
<p>If you've just started I2P, the Active: number on the left should start to
|
||||
grow over the next few minutes and you'll see some local "destinations" listed
|
||||
<p>If you've just started I2P, the Active: numbers on the left should start to
|
||||
grow over the next few minutes and you'll see a "shared clients" local destination listed
|
||||
on the left (if not, <a href="#trouble">see below</a>). Once those show up,
|
||||
you can:</p>
|
||||
<ul>
|
||||
@ -11,13 +11,14 @@ you can:</p>
|
||||
tell your browser to use the <b>HTTP proxy at localhost port 4444</b>, then
|
||||
browse to an eepsite -
|
||||
<ul>
|
||||
<li><a href="http://orion.i2p/">orion.i2p</a>: a site which tracks eepsite uptime and changes</li>
|
||||
<li><a href="http://inproxy.tino.i2p/status.php">inproxy.tino.i2p</a> and
|
||||
<a href="http://perv.i2p/stats.cgi">perv.i2p</a>: sites tracking active eepsites</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>
|
||||
<li><a href="http://search.i2p/">search.i2p</a>: an anonymously hosted search engine of eepsites</li>
|
||||
<li><a href="http://eepsites.i2p/">eepsites.i2p</a>: an anonymously hosted search engine of eepsites</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://dev.i2p/">dev.i2p</a>: a secure and anonymous connection to <a href="http://dev.i2p.net/">dev.i2p.net</a></li>
|
||||
<li>Freenet proxy: <a href="http://fproxy.tino.i2p">fproxy.tino.i2p</a></li>
|
||||
<li><a href="http://fproxy.tino.i2p">fproxy.tino.i2p</a>: Freenet proxy</li>
|
||||
</ul>
|
||||
There are many more eepsites - just follow the links from the ones you see,
|
||||
bookmark your favorites, and visit them often!</li>
|
||||
@ -27,8 +28,7 @@ you can:</p>
|
||||
through the I2P network.</li>
|
||||
<li><b>transfer files</b> - there is an integrated <a href="i2psnark/">port</a> of the
|
||||
<a href="http://www.klomp.org/snark/">Snark</a> <a href="http://www.bittorrent.com/">BitTorrent</a>
|
||||
client, which is compatible with the Python
|
||||
<a href="http://i2p-bt.postman.i2p/">mainline I2P-BT</a>.</li>
|
||||
client.</li>
|
||||
<li><b>use anonymous email</b> - postman has created a mail system compatible with normal mail
|
||||
clients (POP3 / SMTP) that allows email within I2P as well as mail from and to the normal
|
||||
internet! get your account at <a href="http://hq.postman.i2p/">hq.postman.i2p</a>.
|
||||
@ -44,18 +44,20 @@ you can:</p>
|
||||
<a href="http://localhost:7658/">http://localhost:7658/</a>. Simply place your files in
|
||||
the <code>eepsite/docroot/</code> directory (or place any standard JSP/Servlet <code>.war</code>
|
||||
files under <code>eepsite/webapps</code>, or standard CGI script under <code>eepsite/cgi-bin</code>)
|
||||
and they'll show up. After starting up an <a href="/i2ptunnel/">eepsite tunnel</a> pointing at it, your eepsite's
|
||||
<i>destination</i> (which uniquely and securely identifies it) will be visible.
|
||||
If you want other people to see your eepsite,
|
||||
you need to give them that really huge string. Just paste it into the
|
||||
<a href="http://forum.i2p/viewforum.php?f=16">Eepsite announce</a> forum, add it to
|
||||
ugha's <a href="http://ugha.i2p/I2pLinks">wiki</a>, <a href="http://orion.i2p/">orion's list</a>, or paste it in the #i2p or #i2p-chat channels on
|
||||
IRC (be sure to split it into two lines, as its too long for one).</p>
|
||||
and they'll show up. After starting up an <a href="/i2ptunnel/">eepsite tunnel</a> pointing at it, your eepsite
|
||||
will be visible to others.
|
||||
Detailed instructions for starting your eepsite are on
|
||||
<a href="http://localhost:7658/">your temporary eepsite page</a>.
|
||||
</p>
|
||||
|
||||
<h2><a name="trouble">Troubleshooting</a></h2>
|
||||
|
||||
<p>If, after 5 or 10 minutes, your Active: x/y count has less than 20 active
|
||||
peers, you may want to review the information on the
|
||||
<p>Be patient - I2P may be slow to start the first time as it searches for peers.
|
||||
If, after 30 minutes, your Active: connected/recent count has less than 10 connected
|
||||
peers, you should open port 8887 on your firewall for better connectivity.
|
||||
If you cannot see any eepsites at all (even <a href="http://www.i2p">www.i2p</a>),
|
||||
be sure your browser proxy is set to localhost port 4444.
|
||||
You may also want to review the information on the
|
||||
<a href="http://www.i2p.net/">I2P website</a>, post up messages to the
|
||||
<a href="http://forum.i2p.net/">I2P discussion forum</a>, or swing by #i2p or
|
||||
#i2p-chat on IRC at <a href="irc://irc.freenode.net/#i2p">irc.freenode.net</a>, irc.postman.i2p or irc.freshcoffee.i2p (they're linked together).</p>
|
||||
|
@ -14,6 +14,8 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterAddress;
|
||||
|
||||
/**
|
||||
* Manages the communication subsystem between peers, including connections,
|
||||
@ -51,7 +53,12 @@ public abstract class CommSystemFacade implements Service {
|
||||
*/
|
||||
public short getReachabilityStatus() { return STATUS_OK; }
|
||||
public void recheckReachability() {}
|
||||
public boolean isBacklogged(Hash dest) { return false; }
|
||||
|
||||
/**
|
||||
* Tell other transports our address changed
|
||||
*/
|
||||
public void notifyReplaceAddress(RouterAddress UDPAddr) {}
|
||||
/**
|
||||
* We are able to receive unsolicited connections
|
||||
*/
|
||||
|
@ -363,7 +363,7 @@ public class Router {
|
||||
|
||||
// publicize our ballpark capacity - this does not affect anything at
|
||||
// the moment
|
||||
public static final char CAPABILITY_BW16 = 'K';
|
||||
public static final char CAPABILITY_BW12 = 'K';
|
||||
public static final char CAPABILITY_BW32 = 'L';
|
||||
public static final char CAPABILITY_BW64 = 'M';
|
||||
public static final char CAPABILITY_BW128 = 'N';
|
||||
@ -382,8 +382,8 @@ public class Router {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Adding capabilities w/ bw limit @ " + bwLim, new Exception("caps"));
|
||||
|
||||
if (bwLim <= 16) {
|
||||
ri.addCapability(CAPABILITY_BW16);
|
||||
if (bwLim < 12) {
|
||||
ri.addCapability(CAPABILITY_BW12);
|
||||
} else if (bwLim <= 32) {
|
||||
ri.addCapability(CAPABILITY_BW32);
|
||||
} else if (bwLim <= 64) {
|
||||
@ -939,6 +939,10 @@ public class Router {
|
||||
for (Iterator iter = ordered.iterator() ; iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = _config.getProperty(key);
|
||||
// Escape line breaks before saving.
|
||||
// Remember: "\" needs escaping both for regex and string.
|
||||
val = val.replaceAll("\\r","\\\\r");
|
||||
val = val.replaceAll("\\n","\\\\n");
|
||||
buf.append(key).append('=').append(val).append('\n');
|
||||
}
|
||||
}
|
||||
|
@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
|
||||
*
|
||||
*/
|
||||
public class RouterVersion {
|
||||
public final static String ID = "$Revision: 1.497 $ $Date: 2007-03-15 13:26:06 $";
|
||||
public final static String VERSION = "0.6.1.28";
|
||||
public final static long BUILD = 0;
|
||||
public final static String ID = "$Revision: 1.548 $ $Date: 2008-01-08 21:15:43 $";
|
||||
public final static String VERSION = "0.6.1.30";
|
||||
public final static long BUILD = 20;
|
||||
public static void main(String args[]) {
|
||||
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
|
||||
System.out.println("Router ID: " + RouterVersion.ID);
|
||||
|
@ -41,6 +41,9 @@ public interface TunnelManagerFacade extends Service {
|
||||
*
|
||||
*/
|
||||
boolean isInUse(Hash peer);
|
||||
|
||||
/** Is a tunnel a valid member of the pool? */
|
||||
public boolean isValidTunnel(Hash client, TunnelInfo tunnel);
|
||||
|
||||
/** how many tunnels are we participating in? */
|
||||
public int getParticipatingCount();
|
||||
@ -85,6 +88,7 @@ class DummyTunnelManagerFacade implements TunnelManagerFacade {
|
||||
public TunnelInfo selectOutboundTunnel() { return null; }
|
||||
public TunnelInfo selectOutboundTunnel(Hash destination) { return null; }
|
||||
public boolean isInUse(Hash peer) { return false; }
|
||||
public boolean isValidTunnel(Hash client, TunnelInfo tunnel) { return false; }
|
||||
public int getParticipatingCount() { return 0; }
|
||||
public int getFreeTunnelCount() { return 0; }
|
||||
public int getOutboundTunnelCount() { return 0; }
|
||||
|
@ -2,6 +2,7 @@ package net.i2p.router.message;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@ -378,7 +379,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
||||
+ _lease.getTunnelId() + " on "
|
||||
+ _lease.getGateway().toBase64());
|
||||
|
||||
_outTunnel = selectOutboundTunnel();
|
||||
_outTunnel = selectOutboundTunnel(_to);
|
||||
if (_outTunnel != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": Sending tunnel message out " + _outTunnel.getSendTunnelId(0) + " to "
|
||||
@ -434,6 +435,45 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use the same outbound tunnel as we did for the same destination previously,
|
||||
* if possible, to keep the streaming lib happy
|
||||
*
|
||||
*/
|
||||
private static HashMap _tunnelCache = new HashMap();
|
||||
private static long _cleanTime = 0;
|
||||
private TunnelInfo selectOutboundTunnel(Destination to) {
|
||||
TunnelInfo tunnel;
|
||||
long now = getContext().clock().now();
|
||||
synchronized (_tunnelCache) {
|
||||
if (now - _cleanTime > 5*60*1000) { // clean out periodically
|
||||
List deleteList = new ArrayList();
|
||||
for (Iterator iter = _tunnelCache.keySet().iterator(); iter.hasNext(); ) {
|
||||
Destination dest = (Destination) iter.next();
|
||||
tunnel = (TunnelInfo) _tunnelCache.get(dest);
|
||||
if (!getContext().tunnelManager().isValidTunnel(_from.calculateHash(), tunnel))
|
||||
deleteList.add(dest);
|
||||
}
|
||||
for (Iterator iter = deleteList.iterator(); iter.hasNext(); ) {
|
||||
Destination dest = (Destination) iter.next();
|
||||
_tunnelCache.remove(dest);
|
||||
}
|
||||
_cleanTime = now;
|
||||
}
|
||||
tunnel = (TunnelInfo) _tunnelCache.get(to);
|
||||
if (tunnel != null) {
|
||||
if (getContext().tunnelManager().isValidTunnel(_from.calculateHash(), tunnel))
|
||||
return(tunnel);
|
||||
else
|
||||
_tunnelCache.remove(to);
|
||||
}
|
||||
tunnel = selectOutboundTunnel();
|
||||
if (tunnel != null)
|
||||
_tunnelCache.put(to, tunnel);
|
||||
}
|
||||
return tunnel;
|
||||
}
|
||||
/**
|
||||
* Pick an arbitrary outbound tunnel to send the message through, or null if
|
||||
* there aren't any around
|
||||
|
@ -91,12 +91,12 @@ class ProfileOrganizerRenderer {
|
||||
buf.append("<tr>");
|
||||
buf.append("<td><code>");
|
||||
if (prof.getIsFailing()) {
|
||||
buf.append("<font color=\"red\">--").append(peer.toBase64().substring(0,6)).append("</font>");
|
||||
buf.append("<font color=\"red\">-- ").append(peer.toBase64().substring(0,6)).append("</font>");
|
||||
} else {
|
||||
if (prof.getIsActive()) {
|
||||
buf.append("<font color=\"blue\">++").append(peer.toBase64().substring(0,6)).append("</font>");
|
||||
buf.append("<font color=\"blue\">++ ").append(peer.toBase64().substring(0,6)).append("</font>");
|
||||
} else {
|
||||
buf.append("__").append(peer.toBase64().substring(0,6));
|
||||
buf.append(" ").append(peer.toBase64().substring(0,6));
|
||||
}
|
||||
}
|
||||
buf.append("</code></td>");
|
||||
|
@ -19,6 +19,7 @@ import java.util.Set;
|
||||
import java.util.Vector;
|
||||
import java.util.Collections;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.RouterAddress;
|
||||
import net.i2p.router.CommSystemFacade;
|
||||
import net.i2p.router.OutNetMessage;
|
||||
@ -26,6 +27,7 @@ import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.transport.ntcp.NTCPAddress;
|
||||
import net.i2p.router.transport.ntcp.NTCPTransport;
|
||||
import net.i2p.router.transport.tcp.TCPTransport;
|
||||
import net.i2p.router.transport.udp.UDPAddress;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class CommSystemFacadeImpl extends CommSystemFacade {
|
||||
@ -119,6 +121,10 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
|
||||
GetBidsJob.getBids(_context, this, msg);
|
||||
}
|
||||
|
||||
public boolean isBacklogged(Hash dest) {
|
||||
return _manager.isBacklogged(dest);
|
||||
}
|
||||
|
||||
public List getMostRecentErrorMessages() {
|
||||
return _manager.getMostRecentErrorMessages();
|
||||
}
|
||||
@ -194,6 +200,8 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
|
||||
|
||||
public final static String PROP_I2NP_NTCP_HOSTNAME = "i2np.ntcp.hostname";
|
||||
public final static String PROP_I2NP_NTCP_PORT = "i2np.ntcp.port";
|
||||
public final static String PROP_I2NP_NTCP_AUTO_PORT = "i2np.ntcp.autoip";
|
||||
public final static String PROP_I2NP_NTCP_AUTO_IP = "i2np.ntcp.autoport";
|
||||
|
||||
public static RouterAddress createNTCPAddress(RouterContext ctx) {
|
||||
if (!TransportManager.enableNTCP(ctx)) return null;
|
||||
@ -235,4 +243,80 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
|
||||
//}
|
||||
return addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* UDP changed addresses, tell NTCP and restart
|
||||
*/
|
||||
public void notifyReplaceAddress(RouterAddress UDPAddr) {
|
||||
if (UDPAddr == null)
|
||||
return;
|
||||
NTCPTransport t = (NTCPTransport) _manager.getNTCPTransport();
|
||||
if (t == null)
|
||||
return;
|
||||
Properties UDPProps = UDPAddr.getOptions();
|
||||
if (UDPProps == null)
|
||||
return;
|
||||
Properties newProps;
|
||||
RouterAddress oldAddr = t.getCurrentAddress();
|
||||
//_log.warn("Changing NTCP Address? was " + oldAddr);
|
||||
RouterAddress newAddr = oldAddr;
|
||||
if (newAddr == null) {
|
||||
newAddr = new RouterAddress();
|
||||
newAddr.setCost(10);
|
||||
newAddr.setExpiration(null);
|
||||
newAddr.setTransportStyle(NTCPTransport.STYLE);
|
||||
newProps = new Properties();
|
||||
} else {
|
||||
newProps = newAddr.getOptions();
|
||||
if (newProps == null)
|
||||
newProps = new Properties();
|
||||
}
|
||||
|
||||
boolean changed = false;
|
||||
String oport = newProps.getProperty(NTCPAddress.PROP_PORT);
|
||||
String enabled = _context.getProperty(PROP_I2NP_NTCP_AUTO_PORT, "false");
|
||||
if ( (enabled != null) && ("true".equalsIgnoreCase(enabled)) ) {
|
||||
String nport = UDPProps.getProperty(UDPAddress.PROP_PORT);
|
||||
if (nport == null || nport.length() <= 0)
|
||||
return;
|
||||
if (oport == null || ! oport.equals(nport)) {
|
||||
newProps.setProperty(NTCPAddress.PROP_PORT, nport);
|
||||
changed = true;
|
||||
}
|
||||
} else if (oport == null || oport.length() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
String ohost = newProps.getProperty(NTCPAddress.PROP_HOST);
|
||||
enabled = _context.getProperty(PROP_I2NP_NTCP_AUTO_IP, "false");
|
||||
if ( (enabled != null) && ("true".equalsIgnoreCase(enabled)) ) {
|
||||
String nhost = UDPProps.getProperty(UDPAddress.PROP_HOST);
|
||||
if (nhost == null || nhost.length() <= 0)
|
||||
return;
|
||||
if (ohost == null || ! ohost.equalsIgnoreCase(nhost)) {
|
||||
newProps.setProperty(NTCPAddress.PROP_HOST, nhost);
|
||||
changed = true;
|
||||
}
|
||||
} else if (ohost == null || ohost.length() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
//_log.warn("No change to NTCP Address");
|
||||
return;
|
||||
}
|
||||
|
||||
// stopListening stops the pumper, readers, and writers, so required even if
|
||||
// oldAddr == null since startListening starts them all again
|
||||
_log.warn("Halting NTCP to change address");
|
||||
t.stopListening();
|
||||
newAddr.setOptions(newProps);
|
||||
// Give NTCP Pumper time to stop so we don't end up with two...
|
||||
// Need better way
|
||||
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
|
||||
t.restartListening(newAddr);
|
||||
_log.warn("Changed NTCP Address and started up, address is now " + newAddr);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ public interface Transport {
|
||||
public void renderStatusHTML(Writer out, String urlBase, int sortFlags) throws IOException;
|
||||
public short getReachabilityStatus();
|
||||
public void recheckReachability();
|
||||
public boolean isBacklogged(Hash dest);
|
||||
|
||||
public boolean isUnreachable(Hash peer);
|
||||
}
|
||||
|
@ -367,7 +367,10 @@ public abstract class TransportImpl implements Transport {
|
||||
* one.
|
||||
*/
|
||||
protected void replaceAddress(RouterAddress address) {
|
||||
// _log.error("Replacing address for " + getStyle() + " was " + _currentAddress + " now " + address);
|
||||
_currentAddress = address;
|
||||
if ("SSU".equals(getStyle()))
|
||||
_context.commSystem().notifyReplaceAddress(address);
|
||||
}
|
||||
|
||||
/** Who to notify on message availability */
|
||||
@ -379,6 +382,7 @@ public abstract class TransportImpl implements Transport {
|
||||
public RouterContext getContext() { return _context; }
|
||||
public short getReachabilityStatus() { return CommSystemFacade.STATUS_UNKNOWN; }
|
||||
public void recheckReachability() {}
|
||||
public boolean isBacklogged(Hash dest) { return false; }
|
||||
|
||||
private static final long UNREACHABLE_PERIOD = 5*60*1000;
|
||||
public boolean isUnreachable(Hash peer) {
|
||||
|
@ -124,6 +124,15 @@ public class TransportManager implements TransportEventListener {
|
||||
_transports.clear();
|
||||
}
|
||||
|
||||
public Transport getNTCPTransport() {
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
Transport t = (Transport)_transports.get(i);
|
||||
if("NTCP".equals(t.getStyle()))
|
||||
return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
int getTransportCount() { return _transports.size(); }
|
||||
|
||||
private boolean isSupported(Set addresses, Transport t) {
|
||||
@ -184,7 +193,14 @@ public class TransportManager implements TransportEventListener {
|
||||
((Transport)_transports.get(i)).recheckReachability();
|
||||
}
|
||||
|
||||
|
||||
public boolean isBacklogged(Hash dest) {
|
||||
for (int i = 0; i < _transports.size(); i++) {
|
||||
Transport t = (Transport)_transports.get(i);
|
||||
if (t.isBacklogged(dest))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Map getAddresses() {
|
||||
Map rv = new HashMap(_transports.size());
|
||||
|
@ -124,8 +124,8 @@ public class EventPumper implements Runnable {
|
||||
|
||||
int failsafeWrites = 0;
|
||||
int failsafeCloses = 0;
|
||||
|
||||
long expireIdleWriteTime = 60*60*1000l + _context.random().nextLong(60*60*1000l);
|
||||
// pointless if we do this every 2 seconds?
|
||||
long expireIdleWriteTime = 20*60*1000l; // + _context.random().nextLong(60*60*1000l);
|
||||
for (Iterator iter = all.iterator(); iter.hasNext(); ) {
|
||||
try {
|
||||
SelectionKey key = (SelectionKey)iter.next();
|
||||
@ -143,7 +143,7 @@ public class EventPumper implements Runnable {
|
||||
failsafeWrites++;
|
||||
}
|
||||
|
||||
if ( (con.getTimeSinceSend() > expireIdleWriteTime) && (con.getMessagesSent() > 0) ) {
|
||||
if ( con.getTimeSinceSend() > expireIdleWriteTime) {
|
||||
// we haven't sent anything in a really long time, so lets just close 'er up
|
||||
con.close();
|
||||
failsafeCloses++;
|
||||
|
@ -111,6 +111,8 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(getClass());
|
||||
_created = System.currentTimeMillis();
|
||||
_lastSendTime = _created;
|
||||
_lastReceiveTime = _created;
|
||||
_transport = transport;
|
||||
_chan = chan;
|
||||
_readBufs = new ArrayList(4);
|
||||
@ -204,8 +206,8 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
||||
return queued;
|
||||
}
|
||||
}
|
||||
public long getTimeSinceSend() { return _lastSendTime <= 0 ? 0 : System.currentTimeMillis()-_lastSendTime; }
|
||||
public long getTimeSinceReceive() { return _lastReceiveTime <= 0 ? 0 : System.currentTimeMillis()-_lastReceiveTime; }
|
||||
public long getTimeSinceSend() { return System.currentTimeMillis()-_lastSendTime; }
|
||||
public long getTimeSinceReceive() { return System.currentTimeMillis()-_lastReceiveTime; }
|
||||
public long getTimeSinceCreated() { return System.currentTimeMillis()-_created; }
|
||||
public int getConsecutiveBacklog() { return _consecutiveBacklog; }
|
||||
|
||||
@ -304,7 +306,7 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
|
||||
}
|
||||
return queueTime;
|
||||
}
|
||||
private boolean tooBacklogged() {
|
||||
public boolean tooBacklogged() {
|
||||
long queueTime = queueTime();
|
||||
if (queueTime <= 0) return false;
|
||||
int size = 0;
|
||||
|
@ -311,6 +311,13 @@ public class NTCPTransport extends TransportImpl {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBacklogged(Hash dest) {
|
||||
synchronized (_conLock) {
|
||||
NTCPConnection con = (NTCPConnection)_conByIdent.get(dest);
|
||||
return (con != null) && con.isEstablished() && con.tooBacklogged();
|
||||
}
|
||||
}
|
||||
|
||||
void removeCon(NTCPConnection con) {
|
||||
NTCPConnection removed = null;
|
||||
synchronized (_conLock) {
|
||||
@ -379,7 +386,21 @@ public class NTCPTransport extends TransportImpl {
|
||||
_writer.startWriting(NUM_CONCURRENT_WRITERS);
|
||||
|
||||
configureLocalAddress();
|
||||
return bindAddress();
|
||||
}
|
||||
|
||||
public RouterAddress restartListening(RouterAddress addr) {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Restarting ntcp transport listening");
|
||||
_pumper.startPumping();
|
||||
|
||||
_reader.startReading(NUM_CONCURRENT_READERS);
|
||||
_writer.startWriting(NUM_CONCURRENT_WRITERS);
|
||||
|
||||
_myAddress = new NTCPAddress(addr);
|
||||
return bindAddress();
|
||||
}
|
||||
|
||||
private RouterAddress bindAddress() {
|
||||
if (_myAddress != null) {
|
||||
try {
|
||||
ServerSocketChannel chan = ServerSocketChannel.open();
|
||||
@ -527,82 +548,85 @@ public class NTCPTransport extends TransportImpl {
|
||||
StringBuffer buf = new StringBuffer(512);
|
||||
buf.append("<b id=\"ntcpcon\">NTCP connections: ").append(peers.size()).append("</b><br />\n");
|
||||
buf.append("<table border=\"1\">\n");
|
||||
buf.append(" <tr><td><b>peer</b></td>");
|
||||
buf.append(" <tr><td><b><a href=\"#def.peer\">peer</a></b></td>");
|
||||
buf.append(" <td><b>dir</b></td>");
|
||||
buf.append(" <td><b>uptime</b></td>");
|
||||
buf.append(" <td><b>idle</b></td>");
|
||||
buf.append(" <td><b>sent</b></td>");
|
||||
buf.append(" <td><b>received</b></td>");
|
||||
buf.append(" <td><b>out/in</b></td>");
|
||||
buf.append(" <td align=\"right\"><b><a href=\"#def.idle\">idle</a></b></td>");
|
||||
buf.append(" <td align=\"right\"><b><a href=\"#def.rate\">in/out</a></b></td>");
|
||||
buf.append(" <td align=\"right\"><b><a href=\"#def.up\">up</a></b></td>");
|
||||
buf.append(" <td align=\"right\"><b><a href=\"#def.skew\">skew</a></b></td>");
|
||||
buf.append(" <td align=\"right\"><b><a href=\"#def.send\">send</a></b></td>");
|
||||
buf.append(" <td align=\"right\"><b><a href=\"#def.recv\">recv</a></b></td>");
|
||||
buf.append(" <td><b>out queue</b></td>");
|
||||
buf.append(" <td><b>backlogged?</b></td>");
|
||||
buf.append(" <td><b>reading?</b></td>");
|
||||
buf.append(" <td><b>skew</b></td>");
|
||||
buf.append(" </tr>\n");
|
||||
out.write(buf.toString());
|
||||
buf.setLength(0);
|
||||
for (Iterator iter = peers.iterator(); iter.hasNext(); ) {
|
||||
NTCPConnection con = (NTCPConnection)iter.next();
|
||||
buf.append("<tr><td>").append(con.getRemotePeer().calculateHash().toBase64().substring(0,8));
|
||||
buf.append("</td><td>");
|
||||
String name = con.getRemotePeer().calculateHash().toBase64().substring(0,6);
|
||||
buf.append("<tr><td><code><a href=\"netdb.jsp#").append(name).append("\">").append(name);
|
||||
buf.append("</code></td><td align=\"center\"><code>");
|
||||
if (con.getIsInbound())
|
||||
buf.append("in");
|
||||
else
|
||||
buf.append("out");
|
||||
buf.append("</td><td>").append(DataHelper.formatDuration(con.getUptime()));
|
||||
totalUptime += con.getUptime();
|
||||
buf.append("</td><td>").append(con.getTimeSinceSend()/1000);
|
||||
buf.append("s/").append(con.getTimeSinceReceive()/1000);
|
||||
buf.append("s</td><td>").append(con.getMessagesSent());
|
||||
totalSend += con.getMessagesSent();
|
||||
buf.append("</td><td>").append(con.getMessagesReceived());
|
||||
totalRecv += con.getMessagesReceived();
|
||||
buf.append("</td><td>");
|
||||
if (con.getTimeSinceSend() < 10*1000) {
|
||||
buf.append(formatRate(con.getSendRate()/1024));
|
||||
bpsSend += con.getSendRate();
|
||||
} else {
|
||||
buf.append(formatRate(0));
|
||||
}
|
||||
buf.append("/");
|
||||
buf.append("</code></td><td align=\"right\"><code>");
|
||||
buf.append(con.getTimeSinceReceive()/1000);
|
||||
buf.append("s/").append(con.getTimeSinceSend()/1000);
|
||||
buf.append("s</code></td><td align=\"right\"><code>");
|
||||
if (con.getTimeSinceReceive() < 10*1000) {
|
||||
buf.append(formatRate(con.getRecvRate()/1024));
|
||||
bpsRecv += con.getRecvRate();
|
||||
} else {
|
||||
buf.append(formatRate(0));
|
||||
}
|
||||
buf.append("/");
|
||||
if (con.getTimeSinceSend() < 10*1000) {
|
||||
buf.append(formatRate(con.getSendRate()/1024));
|
||||
bpsSend += con.getSendRate();
|
||||
} else {
|
||||
buf.append(formatRate(0));
|
||||
}
|
||||
buf.append("KBps");
|
||||
buf.append("</code></td><td align=\"right\"><code>").append(DataHelper.formatDuration(con.getUptime()));
|
||||
totalUptime += con.getUptime();
|
||||
offsetTotal = offsetTotal + con.getClockSkew();
|
||||
buf.append("</code></td><td align=\"right\"><code>").append(con.getClockSkew());
|
||||
buf.append("s</code></td><td align=\"right\"><code>").append(con.getMessagesSent());
|
||||
totalSend += con.getMessagesSent();
|
||||
buf.append("</code></td><td align=\"right\"><code>").append(con.getMessagesReceived());
|
||||
totalRecv += con.getMessagesReceived();
|
||||
long outQueue = con.getOutboundQueueSize();
|
||||
if (outQueue <= 0) {
|
||||
buf.append("</td><td>No messages");
|
||||
buf.append("</code></td><td align=\"right\"><code>No messages");
|
||||
} else {
|
||||
buf.append("</td><td>").append(outQueue).append(" message");
|
||||
buf.append("</code></td><td align=\"right\"><code>").append(outQueue).append(" message");
|
||||
if (outQueue > 1)
|
||||
buf.append("s");
|
||||
writingPeers++;
|
||||
}
|
||||
buf.append("</td><td>").append(con.getConsecutiveBacklog() > 0 ? "true" : "false");
|
||||
buf.append("</code></td><td align=\"center\"><code>").append(con.getConsecutiveBacklog() > 0 ? "true" : "false");
|
||||
long readTime = con.getReadTime();
|
||||
if (readTime <= 0) {
|
||||
buf.append("</td><td>No");
|
||||
buf.append("</code></td><td align=\"center\"><code>No");
|
||||
} else {
|
||||
buf.append("</td><td>For ").append(DataHelper.formatDuration(readTime));
|
||||
buf.append("</code></td><td><code>For ").append(DataHelper.formatDuration(readTime));
|
||||
readingPeers++;
|
||||
}
|
||||
offsetTotal = offsetTotal + con.getClockSkew();
|
||||
buf.append("</td><td>").append(con.getClockSkew());
|
||||
buf.append("s</td></tr>\n");
|
||||
buf.append("</code></td></tr>\n");
|
||||
out.write(buf.toString());
|
||||
buf.setLength(0);
|
||||
}
|
||||
|
||||
if (peers.size() > 0) {
|
||||
buf.append("<tr><td colspan=\"11\"><hr /></td></tr>\n");
|
||||
buf.append("<tr><td>").append(peers.size()).append(" peers</td><td> </td><td>").append(DataHelper.formatDuration(totalUptime/peers.size()));
|
||||
buf.append("</td><td> </td><td>").append(totalSend).append("</td><td>").append(totalRecv);
|
||||
buf.append("</td><td>").append(formatRate(bpsSend/1024)).append("/").append(formatRate(bpsRecv/1024)).append("KBps");
|
||||
buf.append("<tr><td>").append(peers.size()).append(" peers</td><td> </td><td> ");
|
||||
buf.append("</td><td align=\"right\">").append(formatRate(bpsRecv/1024)).append("/").append(formatRate(bpsSend/1024)).append("KBps");
|
||||
buf.append("</td><td align=\"right\">").append(DataHelper.formatDuration(totalUptime/peers.size()));
|
||||
buf.append("</td><td align=\"right\">").append(peers.size() > 0 ? DataHelper.formatDuration(offsetTotal*1000/peers.size()) : "0ms");
|
||||
buf.append("</td><td align=\"right\">").append(totalSend).append("</td><td align=\"right\">").append(totalRecv);
|
||||
buf.append("</td><td> </td><td> </td><td> ");
|
||||
buf.append("</td><td>").append(peers.size() > 0 ? DataHelper.formatDuration(offsetTotal*1000/peers.size()) : "0ms");
|
||||
buf.append("</td></tr>\n");
|
||||
}
|
||||
|
||||
@ -613,7 +637,7 @@ public class NTCPTransport extends TransportImpl {
|
||||
buf.setLength(0);
|
||||
}
|
||||
|
||||
private static NumberFormat _rateFmt = new DecimalFormat("#,#00.00");
|
||||
private static NumberFormat _rateFmt = new DecimalFormat("#,#0.00");
|
||||
private static String formatRate(float rate) {
|
||||
synchronized (_rateFmt) { return _rateFmt.format(rate); }
|
||||
}
|
||||
@ -647,7 +671,7 @@ public class NTCPTransport extends TransportImpl {
|
||||
}
|
||||
protected int compare(NTCPConnection l, NTCPConnection r) {
|
||||
// base64 retains binary ordering
|
||||
return DataHelper.compareTo(l.getRemotePeer().calculateHash().getData(), r.getRemotePeer().calculateHash().getData());
|
||||
return l.getRemotePeer().calculateHash().toBase64().compareTo(r.getRemotePeer().calculateHash().toBase64());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,9 +73,13 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
|
||||
/** shared fast bid for connected peers */
|
||||
private TransportBid _fastBid;
|
||||
/** shared slow bid for unconnected peers */
|
||||
private TransportBid _slowBid;
|
||||
/** shared slow bid for unconnected peers when we want to prefer UDP */
|
||||
private TransportBid _slowBid;
|
||||
/** shared slow bid for unconnected peers */
|
||||
private TransportBid _slowestBid;
|
||||
/** shared fast bid for unconnected peers when we want to prefer UDP */
|
||||
private TransportBid _fastPreferredBid;
|
||||
/** shared slow bid for unconnected peers when we want to always prefer UDP */
|
||||
private TransportBid _slowPreferredBid;
|
||||
|
||||
/** list of RemoteHostId for peers whose packets we want to drop outright */
|
||||
@ -92,14 +96,17 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
/** define this to explicitly set an external port */
|
||||
public static final String PROP_EXTERNAL_PORT = "i2np.udp.port";
|
||||
/**
|
||||
* If i2np.udp.alwaysPreferred is set, the UDP bids will always be under
|
||||
* If i2np.udp.preferred is set to "always", the UDP bids will always be under
|
||||
* the bid from the TCP transport - even if a TCP connection already
|
||||
* exists. If this is true (the default), it will always prefer UDP, otherwise
|
||||
* exists. If it is set to "true",
|
||||
* it will prefer UDP unless no UDP session exists and a TCP connection
|
||||
* already exists.
|
||||
* If it is set to "false" (the default),
|
||||
* it will prefer TCP unless no TCP session exists and a UDP connection
|
||||
* already exists.
|
||||
*/
|
||||
public static final String PROP_ALWAYS_PREFER_UDP = "i2np.udp.alwaysPreferred";
|
||||
private static final String DEFAULT_ALWAYS_PREFER_UDP = "true";
|
||||
public static final String PROP_PREFER_UDP = "i2np.udp.preferred";
|
||||
private static final String DEFAULT_PREFER_UDP = "false";
|
||||
|
||||
public static final String PROP_FIXED_PORT = "i2np.udp.fixedPort";
|
||||
private static final String DEFAULT_FIXED_PORT = "true";
|
||||
@ -139,8 +146,10 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
_activeThrottle = mq;
|
||||
|
||||
_fastBid = new SharedBid(50);
|
||||
_slowBid = new SharedBid(1000);
|
||||
_slowPreferredBid = new SharedBid(75);
|
||||
_slowBid = new SharedBid(65);
|
||||
_fastPreferredBid = new SharedBid(15);
|
||||
_slowPreferredBid = new SharedBid(20);
|
||||
_slowestBid = new SharedBid(1000);
|
||||
|
||||
_fragments = new OutboundMessageFragments(_context, this, _activeThrottle);
|
||||
_inboundFragments = new InboundMessageFragments(_context, _fragments, this);
|
||||
@ -843,7 +852,10 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
if (peer != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("bidding on a message to an established peer: " + peer);
|
||||
return _fastBid;
|
||||
if (preferUDP())
|
||||
return _fastPreferredBid;
|
||||
else
|
||||
return _fastBid;
|
||||
} else {
|
||||
if (null == toAddress.getTargetAddress(STYLE))
|
||||
return null;
|
||||
@ -852,14 +864,21 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
_log.debug("bidding on a message to an unestablished peer: " + to.toBase64());
|
||||
if (alwaysPreferUDP())
|
||||
return _slowPreferredBid;
|
||||
else
|
||||
else if (preferUDP())
|
||||
return _slowBid;
|
||||
else
|
||||
return _slowestBid;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean preferUDP() {
|
||||
String pref = _context.getProperty(PROP_PREFER_UDP, DEFAULT_PREFER_UDP);
|
||||
return (pref != null) && ! "false".equals(pref);
|
||||
}
|
||||
|
||||
private boolean alwaysPreferUDP() {
|
||||
String pref = _context.getProperty(PROP_ALWAYS_PREFER_UDP, DEFAULT_ALWAYS_PREFER_UDP);
|
||||
return (pref != null) && "true".equals(pref);
|
||||
String pref = _context.getProperty(PROP_PREFER_UDP, DEFAULT_PREFER_UDP);
|
||||
return (pref != null) && "always".equals(pref);
|
||||
}
|
||||
|
||||
private static final int MAX_IDLE_TIME = 5*60*1000;
|
||||
@ -1337,7 +1356,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
private static final IdleInComparator _instance = new IdleInComparator();
|
||||
public static final IdleInComparator instance() { return _instance; }
|
||||
protected int compare(PeerState l, PeerState r) {
|
||||
long rv = l.getLastReceiveTime() - r.getLastReceiveTime();
|
||||
long rv = r.getLastReceiveTime() - l.getLastReceiveTime();
|
||||
if (rv == 0) // fallback on alpha
|
||||
return super.compare(l, r);
|
||||
else
|
||||
@ -1348,7 +1367,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
private static final IdleOutComparator _instance = new IdleOutComparator();
|
||||
public static final IdleOutComparator instance() { return _instance; }
|
||||
protected int compare(PeerState l, PeerState r) {
|
||||
long rv = l.getLastSendTime() - r.getLastSendTime();
|
||||
long rv = r.getLastSendTime() - l.getLastSendTime();
|
||||
if (rv == 0) // fallback on alpha
|
||||
return super.compare(l, r);
|
||||
else
|
||||
@ -1381,7 +1400,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
private static final UptimeComparator _instance = new UptimeComparator();
|
||||
public static final UptimeComparator instance() { return _instance; }
|
||||
protected int compare(PeerState l, PeerState r) {
|
||||
long rv = l.getKeyEstablishedTime() - r.getKeyEstablishedTime();
|
||||
long rv = r.getKeyEstablishedTime() - l.getKeyEstablishedTime();
|
||||
if (rv == 0) // fallback on alpha
|
||||
return super.compare(l, r);
|
||||
else
|
||||
@ -1518,7 +1537,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
}
|
||||
protected int compare(PeerState l, PeerState r) {
|
||||
// base64 retains binary ordering
|
||||
return DataHelper.compareTo(l.getRemotePeer().getData(), r.getRemotePeer().getData());
|
||||
return l.getRemotePeer().toBase64().compareTo(r.getRemotePeer().toBase64());
|
||||
}
|
||||
}
|
||||
private static class InverseComparator implements Comparator {
|
||||
@ -1627,7 +1646,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
buf.append("<a href=\"netdb.jsp#");
|
||||
buf.append(name);
|
||||
buf.append("\">");
|
||||
buf.append(name).append("@");
|
||||
buf.append(name);
|
||||
/*
|
||||
buf.append("@");
|
||||
byte ip[] = peer.getRemoteIP();
|
||||
for (int j = 0; j < ip.length; j++) {
|
||||
int num = ip[j] & 0xFF;
|
||||
@ -1650,7 +1671,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
else if (port < 10000)
|
||||
buf.append("0");
|
||||
buf.append(port);
|
||||
buf.append("</a>");
|
||||
*/
|
||||
buf.append("</a> ");
|
||||
if (peer.getWeRelayToThemAs() > 0)
|
||||
buf.append(">");
|
||||
else
|
||||
@ -1683,7 +1705,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
if (idleIn < 0) idleIn = 0;
|
||||
if (idleOut < 0) idleOut = 0;
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append("<td valign=\"top\" align=\"right\" ><code>");
|
||||
buf.append(idleIn);
|
||||
buf.append("s/");
|
||||
buf.append(idleOut);
|
||||
@ -1692,9 +1714,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
int recvBps = (idleIn > 2 ? 0 : peer.getReceiveBps());
|
||||
int sendBps = (idleOut > 2 ? 0 : peer.getSendBps());
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append("<td valign=\"top\" align=\"right\" ><code>");
|
||||
buf.append(formatKBps(recvBps));
|
||||
buf.append("KBps/");
|
||||
buf.append("/");
|
||||
buf.append(formatKBps(sendBps));
|
||||
buf.append("KBps ");
|
||||
//buf.append(formatKBps(peer.getReceiveACKBps()));
|
||||
@ -1705,18 +1727,18 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
|
||||
long uptime = now - peer.getKeyEstablishedTime();
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append("<td valign=\"top\" align=\"right\" ><code>");
|
||||
buf.append(DataHelper.formatDuration(uptime));
|
||||
buf.append("</code></td>");
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append("<td valign=\"top\" align=\"right\" ><code>");
|
||||
buf.append(peer.getClockSkew());
|
||||
buf.append("s</code></td>");
|
||||
offsetTotal = offsetTotal + peer.getClockSkew();
|
||||
|
||||
long sendWindow = peer.getSendWindowBytes();
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append("<td valign=\"top\" align=\"right\" ><code>");
|
||||
buf.append(sendWindow/1024);
|
||||
buf.append("K");
|
||||
buf.append("/").append(peer.getConcurrentSends());
|
||||
@ -1724,26 +1746,26 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
buf.append("/").append(peer.getConsecutiveSendRejections());
|
||||
buf.append("</code></td>");
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append("<td valign=\"top\" align=\"right\" ><code>");
|
||||
buf.append(peer.getSlowStartThreshold()/1024);
|
||||
buf.append("K</code></td>");
|
||||
|
||||
int rtt = peer.getRTT();
|
||||
int rto = peer.getRTO();
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append("<td valign=\"top\" align=\"right\" ><code>");
|
||||
buf.append(rtt);
|
||||
buf.append("</code></td>");
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append("<td valign=\"top\" align=\"right\" ><code>");
|
||||
buf.append(peer.getRTTDeviation());
|
||||
buf.append("</code></td>");
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append("<td valign=\"top\" align=\"right\" ><code>");
|
||||
buf.append(rto);
|
||||
buf.append("</code></td>");
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append("<td valign=\"top\" align=\"right\" ><code>");
|
||||
buf.append(peer.getMTU()).append("/").append(peer.getReceiveMTU());
|
||||
|
||||
//.append('/');
|
||||
@ -1754,11 +1776,11 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
long sent = peer.getPacketsTransmitted();
|
||||
long recv = peer.getPacketsReceived();
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append("<td valign=\"top\" align=\"right\" ><code>");
|
||||
buf.append(sent);
|
||||
buf.append("</code></td>");
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append("<td valign=\"top\" align=\"right\" ><code>");
|
||||
buf.append(recv);
|
||||
buf.append("</code></td>");
|
||||
|
||||
@ -1770,14 +1792,14 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
long resent = peer.getPacketsRetransmitted();
|
||||
long dupRecv = peer.getPacketsReceivedDuplicate();
|
||||
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append("<td valign=\"top\" align=\"right\" ><code>");
|
||||
//buf.append(formatPct(sendLostPct));
|
||||
buf.append(resent); // + "/" + peer.getPacketsPeriodRetransmitted() + "/" + sent);
|
||||
//buf.append(peer.getPacketRetransmissionRate());
|
||||
buf.append("</code></td>");
|
||||
|
||||
double recvDupPct = (double)peer.getPacketsReceivedDuplicate()/(double)peer.getPacketsReceived();
|
||||
buf.append("<td valign=\"top\" ><code>");
|
||||
buf.append("<td valign=\"top\" align=\"right\" ><code>");
|
||||
buf.append(dupRecv); //formatPct(recvDupPct));
|
||||
buf.append("</code></td>");
|
||||
|
||||
@ -1803,22 +1825,22 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
|
||||
buf.append("<tr><td colspan=\"15\"><hr /></td></tr>\n");
|
||||
buf.append(" <tr><td colspan=\"2\"><b>Total</b></td>");
|
||||
buf.append(" <td>");
|
||||
buf.append(formatKBps(bpsIn)).append("KBps/").append(formatKBps(bpsOut));
|
||||
buf.append(" <td align=\"right\">");
|
||||
buf.append(formatKBps(bpsIn)).append("/").append(formatKBps(bpsOut));
|
||||
buf.append("KBps</td>");
|
||||
buf.append(" <td>").append(numPeers > 0 ? DataHelper.formatDuration(uptimeMsTotal/numPeers) : "0s");
|
||||
buf.append("</td><td>").append(numPeers > 0 ? DataHelper.formatDuration(offsetTotal*1000/numPeers) : "0ms").append("</td>\n");
|
||||
buf.append(" <td>");
|
||||
buf.append(" <td align=\"right\">").append(numPeers > 0 ? DataHelper.formatDuration(uptimeMsTotal/numPeers) : "0s");
|
||||
buf.append("</td><td align=\"right\">").append(numPeers > 0 ? DataHelper.formatDuration(offsetTotal*1000/numPeers) : "0ms").append("</td>\n");
|
||||
buf.append(" <td align=\"right\">");
|
||||
buf.append(numPeers > 0 ? cwinTotal/(numPeers*1024) + "K" : "0K");
|
||||
buf.append("</td><td> </td>\n");
|
||||
buf.append(" <td>");
|
||||
buf.append(" <td align=\"right\">");
|
||||
buf.append(numPeers > 0 ? rttTotal/numPeers : 0);
|
||||
buf.append("</td><td> </td><td>");
|
||||
buf.append("</td><td align=\"right\"> </td><td align=\"right\">");
|
||||
buf.append(numPeers > 0 ? rtoTotal/numPeers : 0);
|
||||
buf.append("</td>\n <td> </td><td>");
|
||||
buf.append(sendTotal).append("</td><td>").append(recvTotal).append("</td>\n");
|
||||
buf.append(" <td>").append(resentTotal);
|
||||
buf.append("</td><td>").append(dupRecvTotal).append("</td>\n");
|
||||
buf.append("</td>\n <td> </td><td align=\"right\">");
|
||||
buf.append(sendTotal).append("</td><td align=\"right\">").append(recvTotal).append("</td>\n");
|
||||
buf.append(" <td align=\"right\">").append(resentTotal);
|
||||
buf.append("</td><td align=\"right\">").append(dupRecvTotal).append("</td>\n");
|
||||
buf.append(" </tr>\n");
|
||||
buf.append("<tr><td colspan=\"15\" valign=\"top\" align=\"left\">");
|
||||
long bytesTransmitted = _context.bandwidthLimiter().getTotalAllocatedOutboundBytes();
|
||||
|
@ -14,8 +14,8 @@ import net.i2p.util.Log;
|
||||
public class PooledTunnelCreatorConfig extends TunnelCreatorConfig {
|
||||
private TunnelPool _pool;
|
||||
private TestJob _testJob;
|
||||
private Job _expireJob;
|
||||
private TunnelInfo _pairedTunnel;
|
||||
// private Job _expireJob;
|
||||
// private TunnelInfo _pairedTunnel;
|
||||
private boolean _live;
|
||||
|
||||
/** Creates a new instance of PooledTunnelCreatorConfig */
|
||||
@ -68,8 +68,9 @@ public class PooledTunnelCreatorConfig extends TunnelCreatorConfig {
|
||||
public TunnelPool getTunnelPool() { return _pool; }
|
||||
|
||||
public void setTestJob(TestJob job) { _testJob = job; }
|
||||
public void setExpireJob(Job job) { _expireJob = job; }
|
||||
public void setExpireJob(Job job) { /* _expireJob = job; */ }
|
||||
|
||||
public void setPairedTunnel(TunnelInfo tunnel) { _pairedTunnel = tunnel; }
|
||||
public TunnelInfo getPairedTunnel() { return _pairedTunnel; }
|
||||
// Fix memory leaks caused by references if you need to use pairedTunnel
|
||||
public void setPairedTunnel(TunnelInfo tunnel) { /* _pairedTunnel = tunnel; */}
|
||||
// public TunnelInfo getPairedTunnel() { return _pairedTunnel; }
|
||||
}
|
||||
|
@ -277,7 +277,7 @@ public abstract class TunnelPeerSelector {
|
||||
|
||||
private static char[] getExcludeCaps(RouterContext ctx) {
|
||||
String excludeCaps = ctx.getProperty("router.excludePeerCaps",
|
||||
String.valueOf(Router.CAPABILITY_BW16));
|
||||
String.valueOf(Router.CAPABILITY_BW12));
|
||||
Set peers = new HashSet();
|
||||
if (excludeCaps != null) {
|
||||
char excl[] = excludeCaps.toCharArray();
|
||||
|
@ -34,6 +34,9 @@ public class TunnelPool {
|
||||
private long _lastSelectionPeriod;
|
||||
private int _expireSkew;
|
||||
private long _started;
|
||||
private long _lastRateUpdate;
|
||||
private long _lastLifetimeProcessed;
|
||||
private String _rateName;
|
||||
|
||||
public TunnelPool(RouterContext ctx, TunnelPoolManager mgr, TunnelPoolSettings settings, TunnelPeerSelector sel) {
|
||||
_context = ctx;
|
||||
@ -48,12 +51,19 @@ public class TunnelPool {
|
||||
_lifetimeProcessed = 0;
|
||||
_expireSkew = _context.random().nextInt(90*1000);
|
||||
_started = System.currentTimeMillis();
|
||||
_lastRateUpdate = _started;
|
||||
_lastLifetimeProcessed = 0;
|
||||
_rateName = "tunnel.Bps." +
|
||||
(_settings.isExploratory() ? "exploratory" : _settings.getDestinationNickname()) +
|
||||
(_settings.isInbound() ? ".in" : ".out");
|
||||
refreshSettings();
|
||||
}
|
||||
|
||||
public void startup() {
|
||||
_alive = true;
|
||||
_started = System.currentTimeMillis();
|
||||
_lastRateUpdate = _started;
|
||||
_lastLifetimeProcessed = 0;
|
||||
_manager.getExecutor().repoll();
|
||||
if (_settings.isInbound() && (_settings.getDestination() != null) ) {
|
||||
// we just reconnected and didn't require any new tunnel builders.
|
||||
@ -66,6 +76,9 @@ public class TunnelPool {
|
||||
if (ls != null)
|
||||
_context.clientManager().requestLeaseSet(_settings.getDestination(), ls);
|
||||
}
|
||||
_context.statManager().createRateStat(_rateName,
|
||||
"Tunnel Bandwidth", "Tunnels",
|
||||
new long[] { 5*60*1000l });
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
@ -249,6 +262,7 @@ public class TunnelPool {
|
||||
_manager.getExecutor().repoll();
|
||||
|
||||
_lifetimeProcessed += info.getProcessedMessagesCount();
|
||||
updateRate();
|
||||
|
||||
long lifetimeConfirmed = info.getVerifiedBytesTransferred();
|
||||
long lifetime = 10*60*1000;
|
||||
@ -295,6 +309,7 @@ public class TunnelPool {
|
||||
_manager.tunnelFailed();
|
||||
|
||||
_lifetimeProcessed += cfg.getProcessedMessagesCount();
|
||||
updateRate();
|
||||
|
||||
if (_settings.isInbound() && (_settings.getDestination() != null) ) {
|
||||
if (ls != null) {
|
||||
@ -303,6 +318,17 @@ public class TunnelPool {
|
||||
}
|
||||
}
|
||||
|
||||
void updateRate() {
|
||||
long now = _context.clock().now();
|
||||
long et = now - _lastRateUpdate;
|
||||
if (et > 2*60*1000) {
|
||||
long bw = 1024 * (_lifetimeProcessed - _lastLifetimeProcessed) * 1000 / et; // Bps
|
||||
_context.statManager().addRateData(_rateName, bw, 0);
|
||||
_lastRateUpdate = now;
|
||||
_lastLifetimeProcessed = _lifetimeProcessed;
|
||||
}
|
||||
}
|
||||
|
||||
void refreshLeaseSet() {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(toString() + ": refreshing leaseSet on tunnel expiration (but prior to grace timeout)");
|
||||
|
@ -187,6 +187,19 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isValidTunnel(Hash client, TunnelInfo tunnel) {
|
||||
if (tunnel.getExpiration() < _context.clock().now())
|
||||
return false;
|
||||
TunnelPool pool;
|
||||
if (tunnel.isInbound())
|
||||
pool = (TunnelPool)_clientInboundPools.get(client);
|
||||
else
|
||||
pool = (TunnelPool)_clientOutboundPools.get(client);
|
||||
if (pool == null)
|
||||
return false;
|
||||
return pool.listTunnels().contains(tunnel);
|
||||
}
|
||||
|
||||
public TunnelPoolSettings getInboundSettings() { return _inboundExploratory.getSettings(); }
|
||||
public TunnelPoolSettings getOutboundSettings() { return _outboundExploratory.getSettings(); }
|
||||
public void setInboundSettings(TunnelPoolSettings settings) { _inboundExploratory.setSettings(settings); }
|
||||
@ -498,7 +511,7 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
String cap = getCapacity(peer);
|
||||
TunnelId id = (info.isInbound() ? info.getReceiveTunnelId(j) : info.getSendTunnelId(j));
|
||||
if (_context.routerHash().equals(peer))
|
||||
out.write("<td><i>" + peer.toBase64().substring(0,4) + (id == null ? "" : ":" + id) + "</i>" + cap + "</td>");
|
||||
out.write("<td>" + (id == null ? "" : "" + id) + "</td>");
|
||||
else
|
||||
out.write("<td>" + peer.toBase64().substring(0,4) + (id == null ? "" : ":" + id) + cap + "</td>");
|
||||
}
|
||||
@ -535,14 +548,14 @@ public class TunnelPoolManager implements TunnelManagerFacade {
|
||||
RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer);
|
||||
if (info != null) {
|
||||
String caps = info.getCapabilities();
|
||||
if (caps.indexOf(Router.CAPABILITY_BW16) >= 0) {
|
||||
return "[<16 ]";
|
||||
if (caps.indexOf(Router.CAPABILITY_BW12) >= 0) {
|
||||
return "[<12 ]";
|
||||
} else if (caps.indexOf(Router.CAPABILITY_BW32) >= 0) {
|
||||
return "[<32 ]";
|
||||
return "[<=32 ]";
|
||||
} else if (caps.indexOf(Router.CAPABILITY_BW64) >= 0) {
|
||||
return "[<64 ]";
|
||||
return "[<=64 ]";
|
||||
} else if (caps.indexOf(Router.CAPABILITY_BW128) >= 0) {
|
||||
return "<b>[<128]</b>";
|
||||
return "<b>[<=128]</b>";
|
||||
} else if (caps.indexOf(Router.CAPABILITY_BW256) >= 0) {
|
||||
return "<b>[>128]</b>";
|
||||
} else {
|
||||
|
Reference in New Issue
Block a user