merge of '01c6aeb6ee46c7795e83553f40e16b5bdae08aac'

and '56ee4b01df5de86f0fdc98cdc4ed1197104584b5'
This commit is contained in:
z3d
2010-10-13 16:07:35 +00:00
156 changed files with 3726 additions and 2475 deletions

View File

@@ -34,7 +34,9 @@ import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicBoolean;
import net.i2p.I2PException;
import net.i2p.client.I2PClientFactory;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.util.Log;
// needed only for debugging.
// import java.util.logging.Level;
@@ -50,7 +52,7 @@ public class DoCMDS implements Runnable {
// FIX ME
// I need a better way to do versioning, but this will do for now.
public static final String BMAJ = "00", BMIN = "00", BREV = "0D", BEXT = "";
public static final String BMAJ = "00", BMIN = "00", BREV = "0E", BEXT = "";
public static final String BOBversion = BMAJ + "." + BMIN + "." + BREV + BEXT;
private Socket server;
private Properties props;
@@ -86,6 +88,7 @@ public class DoCMDS implements Runnable {
private static final String C_inhost = "inhost";
private static final String C_inport = "inport";
private static final String C_list = "list";
private static final String C_lookup = "lookup";
private static final String C_newkeys = "newkeys";
private static final String C_option = "option";
private static final String C_outhost = "outhost";
@@ -113,6 +116,7 @@ public class DoCMDS implements Runnable {
{C_inhost, C_inhost + " hostname | IP * Set the inbound hostname or IP."},
{C_inport, C_inport + " port_number * Set the inbound port number nickname listens on."},
{C_list, C_list + " * List all tunnels."},
{C_lookup, C_lookup + " * Lookup an i2p address."},
{C_newkeys, C_newkeys + " * Generate a new keypair for the current nickname."},
{C_option, C_option + " I2CPoption=something * Set an I2CP option. NOTE: Don't use any spaces."},
{C_outhost, C_outhost + " hostname | IP * Set the outbound hostname or IP."},
@@ -138,6 +142,7 @@ public class DoCMDS implements Runnable {
C_inhost + " " +
C_inport + " " +
C_list + " " +
C_lookup + " " +
C_newkeys + " " +
C_option + " " +
C_outhost + " " +
@@ -446,6 +451,25 @@ public class DoCMDS implements Runnable {
} else if (Command.equals(C_visit)) {
visitAllThreads();
out.println("OK ");
} else if (Command.equals(C_lookup)) {
Destination dest = null;
String reply = null;
if (Arg.endsWith(".i2p")) {
try {
try {
dest = I2PTunnel.destFromName(Arg);
} catch (DataFormatException ex) {
}
reply = dest.toBase64();
} catch (NullPointerException npe) {
// Could not find the destination!?
}
}
if (reply == null) {
out.println("ERROR Address Not found.");
} else {
out.println("OK " + reply);
}
} else if (Command.equals(C_getdest)) {
if (ns) {
if (dk) {

View File

@@ -25,9 +25,9 @@ import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Iterator;
@@ -35,6 +35,8 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import net.i2p.util.SecureFileOutputStream;
/**
* Utility class providing methods to parse and write files in config file
* format, and subscription file format.
@@ -277,7 +279,7 @@ public class ConfigParser {
*/
public static void write(Map map, File file) throws IOException {
ConfigParser
.write(map, new BufferedWriter(new FileWriter(file, false)));
.write(map, new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(file), "UTF-8")));
}
/**
@@ -316,7 +318,7 @@ public class ConfigParser {
public static void writeSubscriptions(List list, File file)
throws IOException {
ConfigParser.writeSubscriptions(list, new BufferedWriter(
new FileWriter(file, false)));
new OutputStreamWriter(new SecureFileOutputStream(file), "UTF-8")));
}
}

View File

@@ -29,6 +29,7 @@ import java.util.List;
import java.util.Map;
import net.i2p.I2PAppContext;
import net.i2p.util.SecureDirectory;
/**
* Main class of addressbook. Performs updates, and runs the main loop.
@@ -131,11 +132,11 @@ public class Daemon {
String settingsLocation = "config.txt";
File homeFile;
if (args.length > 0) {
homeFile = new File(args[0]);
homeFile = new SecureDirectory(args[0]);
if (!homeFile.isAbsolute())
homeFile = new File(I2PAppContext.getGlobalContext().getRouterDir(), args[0]);
homeFile = new SecureDirectory(I2PAppContext.getGlobalContext().getRouterDir(), args[0]);
} else {
homeFile = new File(System.getProperty("user.dir"));
homeFile = new SecureDirectory(System.getProperty("user.dir"));
}
Map defaultSettings = new HashMap();

View File

@@ -26,6 +26,7 @@ import net.i2p.util.ConcurrentHashSet;
import net.i2p.util.EepGet;
import net.i2p.util.FileUtil;
import net.i2p.util.Log;
import net.i2p.util.SecureDirectory;
import net.i2p.util.SimpleScheduler;
import net.i2p.util.SimpleTimer;
import net.i2p.util.Translate;
@@ -77,7 +78,7 @@ public class I2PSnarkUtil {
// This is used for both announce replies and .torrent file downloads,
// so it must be available even if not connected to I2CP.
// so much for multiple instances
_tmpDir = new File(ctx.getTempDir(), "i2psnark");
_tmpDir = new SecureDirectory(ctx.getTempDir(), "i2psnark");
FileUtil.rmdir(_tmpDir, false);
_tmpDir.mkdirs();
}
@@ -196,11 +197,14 @@ public class I2PSnarkUtil {
/** connect to the given destination */
I2PSocket connect(PeerID peer) throws IOException {
Hash dest = peer.getAddress().calculateHash();
Destination addr = peer.getAddress();
if (addr == null)
throw new IOException("Null address");
Hash dest = addr.calculateHash();
if (_shitlist.contains(dest))
throw new IOException("Not trying to contact " + dest.toBase64() + ", as they are shitlisted");
try {
I2PSocket rv = _manager.connect(peer.getAddress());
I2PSocket rv = _manager.connect(addr);
if (rv != null)
_shitlist.remove(dest);
return rv;
@@ -224,7 +228,8 @@ public class I2PSnarkUtil {
public File get(String url, boolean rewrite) { return get(url, rewrite, 0); }
public File get(String url, int retries) { return get(url, true, retries); }
public File get(String url, boolean rewrite, int retries) {
_log.debug("Fetching [" + url + "] proxy=" + _proxyHost + ":" + _proxyPort + ": " + _shouldProxy);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Fetching [" + url + "] proxy=" + _proxyHost + ":" + _proxyPort + ": " + _shouldProxy);
File out = null;
try {
// we could use the system tmp dir but deleteOnExit() doesn't seem to work on all platforms...
@@ -248,10 +253,12 @@ public class I2PSnarkUtil {
}
EepGet get = new I2PSocketEepGet(_context, _manager, retries, out.getAbsolutePath(), fetchURL);
if (get.fetch()) {
_log.debug("Fetch successful [" + url + "]: size=" + out.length());
if (_log.shouldLog(Log.DEBUG))
_log.debug("Fetch successful [" + url + "]: size=" + out.length());
return out;
} else {
_log.warn("Fetch failed [" + url + "]");
if (_log.shouldLog(Log.WARN))
_log.warn("Fetch failed [" + url + "]");
out.delete();
return null;
}

View File

@@ -109,7 +109,8 @@ public class MetaInfo
*/
public MetaInfo(Map m) throws InvalidBEncodingException
{
_log.debug("Creating a metaInfo: " + m, new Exception("source"));
if (_log.shouldLog(Log.DEBUG))
_log.debug("Creating a metaInfo: " + m, new Exception("source"));
BEValue val = (BEValue)m.get("announce");
if (val == null)
throw new InvalidBEncodingException("Missing announce string");
@@ -446,14 +447,16 @@ public class MetaInfo
else
buf.append(val.toString());
}
_log.debug(buf.toString());
if (_log.shouldLog(Log.DEBUG))
_log.debug(buf.toString());
byte[] infoBytes = BEncoder.bencode(info);
//_log.debug("info bencoded: [" + Base64.encode(infoBytes, true) + "]");
try
{
MessageDigest digest = MessageDigest.getInstance("SHA");
byte hash[] = digest.digest(infoBytes);
_log.debug("info hash: [" + net.i2p.data.Base64.encode(hash) + "]");
if (_log.shouldLog(Log.DEBUG))
_log.debug("info hash: [" + net.i2p.data.Base64.encode(hash) + "]");
return hash;
}
catch(NoSuchAlgorithmException nsa)

View File

@@ -92,7 +92,8 @@ public class Peer implements Comparable
byte[] id = handshake(in, out);
this.peerID = new PeerID(id, sock.getPeerDestination());
_id = ++__id;
_log.debug("Creating a new peer with " + peerID.getAddress().calculateHash().toBase64(), new Exception("creating " + _id));
if (_log.shouldLog(Log.DEBUG))
_log.debug("Creating a new peer with " + peerID.getAddress().calculateHash().toBase64(), new Exception("creating " + _id));
}
/**
@@ -129,7 +130,7 @@ public class Peer implements Comparable
@Override
public int hashCode()
{
return peerID.hashCode() ^ (2 << _id);
return peerID.hashCode() ^ (7777 * (int)_id);
}
/**
@@ -150,6 +151,7 @@ public class Peer implements Comparable
/**
* Compares the PeerIDs.
* @deprecated unused?
*/
public int compareTo(Object o)
{
@@ -181,14 +183,16 @@ public class Peer implements Comparable
if (state != null)
throw new IllegalStateException("Peer already started");
_log.debug("Running connection to " + peerID.getAddress().calculateHash().toBase64(), new Exception("connecting"));
if (_log.shouldLog(Log.DEBUG))
_log.debug("Running connection to " + peerID.getAddress().calculateHash().toBase64(), new Exception("connecting"));
try
{
// Do we need to handshake?
if (din == null)
{
sock = util.connect(peerID);
_log.debug("Connected to " + peerID + ": " + sock);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Connected to " + peerID + ": " + sock);
if ((sock == null) || (sock.isClosed())) {
throw new IOException("Unable to reach " + peerID);
}
@@ -207,14 +211,20 @@ public class Peer implements Comparable
// = new BufferedOutputStream(sock.getOutputStream());
byte [] id = handshake(in, out); //handshake(bis, bos);
byte [] expected_id = peerID.getID();
if (!Arrays.equals(expected_id, id))
throw new IOException("Unexpected peerID '"
if (expected_id == null) {
peerID.setID(id);
} else if (Arrays.equals(expected_id, id)) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Handshake got matching IDs with " + toString());
} else {
throw new IOException("Unexpected peerID '"
+ PeerID.idencode(id)
+ "' expected '"
+ PeerID.idencode(expected_id) + "'");
_log.debug("Handshake got matching IDs with " + toString());
}
} else {
_log.debug("Already have din [" + sock + "] with " + toString());
if (_log.shouldLog(Log.DEBUG))
_log.debug("Already have din [" + sock + "] with " + toString());
}
PeerConnectionIn in = new PeerConnectionIn(this, din);
@@ -229,7 +239,8 @@ public class Peer implements Comparable
state = s;
listener.connected(this);
_log.debug("Start running the reader with " + toString());
if (_log.shouldLog(Log.DEBUG))
_log.debug("Start running the reader with " + toString());
// Use this thread for running the incomming connection.
// The outgoing connection creates its own Thread.
out.startup();
@@ -279,7 +290,8 @@ public class Peer implements Comparable
dout.write(my_id);
dout.flush();
_log.debug("Wrote my shared hash and ID to " + toString());
if (_log.shouldLog(Log.DEBUG))
_log.debug("Wrote my shared hash and ID to " + toString());
// Handshake read - header
byte b = din.readByte();
@@ -306,7 +318,8 @@ public class Peer implements Comparable
// Handshake read - peer id
din.readFully(bs);
_log.debug("Read the remote side's hash and peerID fully from " + toString());
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read the remote side's hash and peerID fully from " + toString());
return bs;
}

View File

@@ -76,10 +76,12 @@ public class PeerAcceptor
// is this working right?
try {
peerInfoHash = readHash(in);
_log.info("infohash read from " + socket.getPeerDestination().calculateHash().toBase64()
+ ": " + Base64.encode(peerInfoHash));
if (_log.shouldLog(Log.INFO))
_log.info("infohash read from " + socket.getPeerDestination().calculateHash().toBase64()
+ ": " + Base64.encode(peerInfoHash));
} catch (IOException ioe) {
_log.info("Unable to read the infohash from " + socket.getPeerDestination().calculateHash().toBase64());
if (_log.shouldLog(Log.INFO))
_log.info("Unable to read the infohash from " + socket.getPeerDestination().calculateHash().toBase64());
throw ioe;
}
in = new SequenceInputStream(new ByteArrayInputStream(peerInfoHash), in);

View File

@@ -375,7 +375,8 @@ public class PeerCoordinator implements PeerListener
if (need_more)
{
_log.debug("Adding a peer " + peer.getPeerID().getAddress().calculateHash().toBase64() + " for " + metainfo.getName(), new Exception("add/run"));
if (_log.shouldLog(Log.DEBUG))
_log.debug("Adding a peer " + peer.getPeerID().toString() + " for " + metainfo.getName(), new Exception("add/run"));
// Run the peer with us as listener and the current bitfield.
final PeerListener listener = this;

View File

@@ -24,19 +24,33 @@ import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Map;
import net.i2p.I2PAppContext;
import net.i2p.data.Base32;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import org.klomp.snark.bencode.BDecoder;
import org.klomp.snark.bencode.BEValue;
import org.klomp.snark.bencode.InvalidBEncodingException;
/**
* Store the address information about a peer.
* Prior to 0.8.1, an instantiation required a peer ID, and full Destination address.
* Starting with 0.8.1, to support compact tracker responses,
* a PeerID can be instantiated with a Destination Hash alone.
* The full destination lookup is deferred until getAddress() is called,
* and the PeerID is not required.
* Equality is now determined solely by the dest hash.
*/
public class PeerID implements Comparable
{
private final byte[] id;
private final Destination address;
private byte[] id;
private Destination address;
private final int port;
private byte[] destHash;
/** whether we have tried to get the dest from the hash - only do once */
private boolean triedDestLookup;
private final int hash;
public PeerID(byte[] id, Destination address)
@@ -44,7 +58,7 @@ public class PeerID implements Comparable
this.id = id;
this.address = address;
this.port = 6881;
this.destHash = address.calculateHash().getData();
hash = calculateHash();
}
@@ -77,17 +91,49 @@ public class PeerID implements Comparable
throw new InvalidBEncodingException("Invalid destination [" + bevalue.getString() + "]");
port = 6881;
this.destHash = address.calculateHash().getData();
hash = calculateHash();
}
/**
* Creates a PeerID from a destHash
* @since 0.8.1
*/
public PeerID(byte[] dest_hash) throws InvalidBEncodingException
{
// id and address remain null
port = 6881;
if (dest_hash.length != 32)
throw new InvalidBEncodingException("bad hash length");
destHash = dest_hash;
hash = DataHelper.hashCode(dest_hash);
}
public byte[] getID()
{
return id;
}
public Destination getAddress()
/** for connecting out to peer based on desthash @since 0.8.1 */
public void setID(byte[] xid)
{
id = xid;
}
/**
* Get the destination.
* If this PeerId was instantiated with a destHash,
* and we have not yet done so, lookup the full destination, which may take
* up to 10 seconds.
* @return Dest or null if unknown
*/
public synchronized Destination getAddress()
{
if (address == null && destHash != null && !triedDestLookup) {
String b32 = Base32.encode(destHash) + ".b32.i2p";
address = I2PAppContext.getGlobalContext().namingService().lookup(b32);
triedDestLookup = true;
}
return address;
}
@@ -96,16 +142,19 @@ public class PeerID implements Comparable
return port;
}
/** @since 0.8.1 */
public byte[] getDestHash()
{
return destHash;
}
private int calculateHash()
{
int b = 0;
for (int i = 0; i < id.length; i++)
b ^= id[i];
return (b ^ address.hashCode()) ^ port;
return DataHelper.hashCode(destHash);
}
/**
* The hash code of a PeerID is the exclusive or of all id bytes.
* The hash code of a PeerID is the hashcode of the desthash
*/
@Override
public int hashCode()
@@ -115,18 +164,15 @@ public class PeerID implements Comparable
/**
* Returns true if and only if this peerID and the given peerID have
* the same 20 bytes as ID.
* the same destination hash
*/
public boolean sameID(PeerID pid)
{
boolean equal = true;
for (int i = 0; equal && i < id.length; i++)
equal = id[i] == pid.id[i];
return equal;
return DataHelper.eq(destHash, pid.getDestHash());
}
/**
* Two PeerIDs are equal when they have the same id, address and port.
* Two PeerIDs are equal when they have the same dest hash
*/
@Override
public boolean equals(Object o)
@@ -135,9 +181,7 @@ public class PeerID implements Comparable
{
PeerID pid = (PeerID)o;
return port == pid.port
&& address.equals(pid.address)
&& sameID(pid);
return sameID(pid);
}
else
return false;
@@ -145,6 +189,7 @@ public class PeerID implements Comparable
/**
* Compares port, address and id.
* @deprecated unused? and will NPE now that address can be null?
*/
public int compareTo(Object o)
{
@@ -176,6 +221,8 @@ public class PeerID implements Comparable
@Override
public String toString()
{
if (id == null || address == null)
return "unkn@" + Base64.encode(destHash).substring(0, 6);
int nonZero = 0;
for (int i = 0; i < id.length; i++) {
if (id[i] != 0) {

View File

@@ -437,7 +437,7 @@ public class Snark
try { storage.close(); } catch (IOException ioee) {
ioee.printStackTrace();
}
fatal("Could not create storage", ioe);
fatal("Could not check or create storage", ioe);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -357,6 +357,7 @@ public class TrackerClient extends I2PAppThread
+ "&uploaded=" + uploaded
+ "&downloaded=" + downloaded
+ "&left=" + left
+ "&compact"
+ ((! event.equals(NO_EVENT)) ? ("&event=" + event) : "");
if (left <= 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
s += "&numwant=0";

View File

@@ -24,7 +24,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -33,11 +32,17 @@ import org.klomp.snark.bencode.BDecoder;
import org.klomp.snark.bencode.BEValue;
import org.klomp.snark.bencode.InvalidBEncodingException;
/**
* The data structure for the tracker response.
* Handles both traditional and compact formats.
* Compact format 1 - a list of hashes - early format for testing
* Compact format 2 - One big string of concatenated hashes - official format
*/
public class TrackerInfo
{
private final String failure_reason;
private final int interval;
private final Set peers;
private final Set<Peer> peers;
private int complete;
private int incomplete;
@@ -73,10 +78,19 @@ public class TrackerInfo
interval = beInterval.getInt();
BEValue bePeers = (BEValue)m.get("peers");
if (bePeers == null)
if (bePeers == null) {
peers = Collections.EMPTY_SET;
else
peers = getPeers(bePeers.getList(), my_id, metainfo);
} else {
Set<Peer> p;
try {
// One big string (the official compact format)
p = getPeers(bePeers.getBytes(), my_id, metainfo);
} catch (InvalidBEncodingException ibe) {
// List of Dictionaries or List of Strings
p = getPeers(bePeers.getList(), my_id, metainfo);
}
peers = p;
}
BEValue bev = (BEValue)m.get("complete");
if (bev != null) try {
@@ -94,32 +108,68 @@ public class TrackerInfo
}
}
public static Set getPeers(InputStream in, byte[] my_id, MetaInfo metainfo)
/******
public static Set<Peer> getPeers(InputStream in, byte[] my_id, MetaInfo metainfo)
throws IOException
{
return getPeers(new BDecoder(in), my_id, metainfo);
}
public static Set getPeers(BDecoder be, byte[] my_id, MetaInfo metainfo)
public static Set<Peer> getPeers(BDecoder be, byte[] my_id, MetaInfo metainfo)
throws IOException
{
return getPeers(be.bdecodeList().getList(), my_id, metainfo);
}
******/
public static Set getPeers(List l, byte[] my_id, MetaInfo metainfo)
/** List of Dictionaries or List of Strings */
private static Set<Peer> getPeers(List<BEValue> l, byte[] my_id, MetaInfo metainfo)
throws IOException
{
Set peers = new HashSet(l.size());
Set<Peer> peers = new HashSet(l.size());
Iterator it = l.iterator();
while (it.hasNext())
{
for (BEValue bev : l) {
PeerID peerID;
try {
peerID = new PeerID(((BEValue)it.next()).getMap());
// Case 1 - non-compact - A list of dictionaries (maps)
peerID = new PeerID(bev.getMap());
} catch (InvalidBEncodingException ibe) {
// don't let one bad entry spoil the whole list
//Snark.debug("Discarding peer from list: " + ibe, Snark.ERROR);
try {
// Case 2 - compact - A list of 32-byte binary strings (hashes)
// This was just for testing and is not the official format
peerID = new PeerID(bev.getBytes());
} catch (InvalidBEncodingException ibe2) {
// don't let one bad entry spoil the whole list
//Snark.debug("Discarding peer from list: " + ibe, Snark.ERROR);
continue;
}
}
peers.add(new Peer(peerID, my_id, metainfo));
}
return peers;
}
private static final int HASH_LENGTH = 32;
/**
* One big string of concatenated 32-byte hashes
* @since 0.8.1
*/
private static Set<Peer> getPeers(byte[] l, byte[] my_id, MetaInfo metainfo)
throws IOException
{
int count = l.length / HASH_LENGTH;
Set<Peer> peers = new HashSet(count);
for (int i = 0; i < count; i++) {
PeerID peerID;
byte[] hash = new byte[HASH_LENGTH];
System.arraycopy(l, i * HASH_LENGTH, hash, 0, HASH_LENGTH);
try {
peerID = new PeerID(hash);
} catch (InvalidBEncodingException ibe) {
// won't happen
continue;
}
peers.add(new Peer(peerID, my_id, metainfo));
@@ -128,7 +178,7 @@ public class TrackerInfo
return peers;
}
public Set getPeers()
public Set<Peer> getPeers()
{
return peers;
}

View File

@@ -140,7 +140,7 @@ public class BEValue
* succeeds when the BEValue is actually a List, otherwise it will
* throw a InvalidBEncodingException.
*/
public List getList() throws InvalidBEncodingException
public List<BEValue> getList() throws InvalidBEncodingException
{
try
{
@@ -157,7 +157,7 @@ public class BEValue
* values. This operation only succeeds when the BEValue is actually
* a Map, otherwise it will throw a InvalidBEncodingException.
*/
public Map getMap() throws InvalidBEncodingException
public Map<BEValue, BEValue> getMap() throws InvalidBEncodingException
{
try
{

View File

@@ -8,6 +8,7 @@ import java.io.PrintWriter;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
@@ -821,10 +822,10 @@ public class I2PSnarkServlet extends Default {
out.write("</td>\n</tr>\n");
if(showPeers && isRunning && curPeers > 0) {
List peers = snark.coordinator.peerList();
Iterator it = peers.iterator();
while (it.hasNext()) {
Peer peer = (Peer)it.next();
List<Peer> peers = snark.coordinator.peerList();
if (!showDebug)
Collections.sort(peers, new PeerComparator());
for (Peer peer : peers) {
if (!peer.isConnected())
continue;
out.write("<tr class=\"" + rowClass + "\">");
@@ -908,6 +909,19 @@ public class I2PSnarkServlet extends Default {
}
}
/**
* Sort by completeness (seeds first), then by ID
* @since 0.8.1
*/
private static class PeerComparator implements Comparator<Peer> {
public int compare(Peer l, Peer r) {
int diff = r.completed() - l.completed(); // reverse
if (diff != 0)
return diff;
return l.toString().substring(5, 9).compareTo(r.toString().substring(5, 9));
}
}
private void writeAddForm(PrintWriter out, HttpServletRequest req) throws IOException {
String uri = req.getRequestURI();
String newURL = req.getParameter("newURL");

View File

@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: I2P i2psnark\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-07-01 04:52+0000\n"
"PO-Revision-Date: 2010-07-01 12:53+0800\n"
"POT-Creation-Date: 2010-10-04 02:45+0000\n"
"PO-Revision-Date: 2010-10-04 12:00+0800\n"
"Last-Translator: walking <walking@mail.i2p>\n"
"Language-Team: foo <foo@bar>\n"
"MIME-Version: 1.0\n"
@@ -224,7 +224,6 @@ msgid "Torrents"
msgstr "种子"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:187
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:193
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:837
msgid "I2PSnark"
msgstr ""
@@ -233,63 +232,65 @@ msgstr ""
msgid "Refresh page"
msgstr "刷新页面"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:193
msgid "<img border=\"0\" src=\"/themes/console/snark/images/arrow_refresh.png\"> I2PSnark"
msgstr ""
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:195
msgid "Forum"
msgstr "论坛"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:240
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1289
msgid "Status"
msgstr "状态"
msgid "<img border=\"0\" src=\"/themes/console/snark/images/status.png\" title=\"Torrent Status\">Status"
msgstr "<img border=\"0\" src=\"/themes/console/snark/images/status.png\" title=\"种子状态\">状态"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:246
msgid "Hide Peers"
msgstr "隐藏用户"
msgid "<img border=\"0\" src=\"/themes/console/snark/images/showpeers.png\" title=\"Toggle Peer Visibility\" alt=\"Hide Peers\">"
msgstr "<img border=\"0\" src=\"/themes/console/snark/images/showpeers.png\" title=\"隐藏节点\" alt=\"隐藏节点\">"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:249
msgid "Show Peers"
msgstr "显示用户"
msgid "<img border=\"0\" src=\"/themes/console/snark/images/hidepeers.png\" title=\"Toggle Peer Visibility\" alt=\"Show Peers\">"
msgstr "<img border=\"0\" src=\"/themes/console/snark/images/hidepeers.png\" title=\"显示节点\" alt=\"显示节点\">"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:254
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1271
msgid "Torrent"
msgstr "种子"
msgid "<img border=\"0\" src=\"/themes/console/snark/images/torrent.png\" title=\"Loaded Torrents\">Torrent"
msgstr "<img border=\"0\" src=\"/themes/console/snark/images/torrent.png\" title=\"载入的种子\">种子"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:256
msgid "ETA"
msgstr "预计剩余时间"
msgid "<img border=\"0\" src=\"/themes/console/snark/images/eta.png\" title=\"Estimated Download Time\">ETA"
msgstr "<img border=\"0\" src=\"/themes/console/snark/images/eta.png\" title=\"预计剩余时间\">预计剩余时间"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:258
msgid "Downloaded"
msgstr "下载"
msgid "<img border=\"0\" src=\"/themes/console/images/inbound.png\" title=\"Data Downloaded\">RX"
msgstr "<img border=\"0\" src=\"/themes/console/images/inbound.png\" title=\"下载数据量\">下载"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:260
msgid "Uploaded"
msgstr "上传"
msgid "<img border=\"0\" src=\"/themes/console/images/outbound.png\" title=\"Data Uploaded\">TX"
msgstr "<img border=\"0\" src=\"/themes/console/images/outbound.png\" title=\"上传数据量\">上传"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:262
msgid "Down Rate"
msgstr "下载速度"
msgid "<img border=\"0\" src=\"/themes/console/images/inbound.png\" title=\"Download Speed\">Rate"
msgstr "<img border=\"0\" src=\"/themes/console/images/inbound.png\" title=\"下载速度\">下载速度"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:264
msgid "Up Rate"
msgstr "上传速度"
msgid "<img border=\"0\" src=\"/themes/console/images/outbound.png\" title=\"Upload Speed\">Rate"
msgstr "<img border=\"0\" src=\"/themes/console/images/outbound.png\" title=\"上传速度\">上传速度"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:271
msgid "Stop all torrents and the I2P tunnel"
msgstr "停止全部种子及I2P隧道"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:273
msgid "Stop All"
msgstr "停止全部"
msgid "<img src=\"/themes/console/snark/images/stop_all.png\" title=\"Stop All Torrents\" alt=\"Stop All\">"
msgstr "<img src=\"/themes/console/snark/images/stop_all.png\" title=\"全部停止\" alt=\"全部停止\">"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:278
msgid "Start all torrents and the I2P tunnel"
msgstr "启动全部种子及I2P隧道"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:280
msgid "Start All"
msgstr "启动全部"
msgid "<img src=\"/themes/console/snark/images/start_all.png\" title=\"Start All Torrents\" alt=\"Start All\">"
msgstr "<img src=\"/themes/console/snark/images/start_all.png\" title=\"全部开始\" alt=\"全部开始\">"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:297
msgid "No torrents loaded."
@@ -317,13 +318,13 @@ msgid "Torrent file {0} does not exist"
msgstr "种子文件{0}不存在"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:346
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1476
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1481
#, java-format
msgid "Torrent already running: {0}"
msgstr "种子已启动:{0}"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:348
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1478
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1483
#, java-format
msgid "Torrent already in the queue: {0}"
msgstr "种子排队中:{0}"
@@ -450,7 +451,7 @@ msgid "Seeding"
msgstr "正做种"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:682
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1327
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1317
msgid "Complete"
msgstr "完成"
@@ -486,24 +487,24 @@ msgid "Tracker"
msgstr "Tracker服务器"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:751
msgid "Details"
msgstr "详情"
msgid "<img border=\"0\" src=\"/themes/console/snark/images/details.png\">"
msgstr ""
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:785
msgid "Stop the torrent"
msgstr "停止种子"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:787
msgid "Stop"
msgstr "停止"
msgid "<img src=\"/themes/console/snark/images/stop.png\" title=\"Stop Torrent\" alt=\"Stop\">"
msgstr "<img src=\"/themes/console/snark/images/stop.png\" title=\"停止下载\" alt=\"停止\">"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:793
msgid "Start the torrent"
msgstr "启动种子"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:795
msgid "Start"
msgstr "启动"
msgid "<img src=\"/themes/console/snark/images/start.png\" title=\"Start Torrent\" alt=\"Start\">"
msgstr "<img src=\"/themes/console/snark/images/start.png\" title=\"开始下载\" alt=\"开始\">"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:800
msgid "Remove the torrent from the active list, deleting the .torrent file"
@@ -518,8 +519,8 @@ msgid "Are you sure you want to delete the file \\''{0}.torrent\\'' (downloaded
msgstr "您确定要删除文件“{0}.torrent”(下载的数据文件不会被删除)?"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:807
msgid "Remove"
msgstr "移除"
msgid "<img src=\"/themes/console/snark/images/remove.png\" title=\"Remove Torrent\" alt=\"Remove\">"
msgstr "<img src=\"/themes/console/snark/images/remove.png\" title=\"删除种子\" alt=\"删除种子\">"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:811
msgid "Delete the .torrent file and the associated data file(s)"
@@ -534,8 +535,8 @@ msgid "Are you sure you want to delete the torrent \\''{0}\\'' and all downloade
msgstr "您确定要删除种子“{0}”(下载的数据文件会一并被删除)?"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:818
msgid "Delete"
msgstr "删除"
msgid "<img src=\"/themes/console/snark/images/delete.png\" title=\"Delete Torrent + Data\" alt=\"Delete\">"
msgstr "<img src=\"/themes/console/snark/images/delete.png\" title=\"删除种子 + 数据\" alt=\"删除种子 + 数据\">"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:861
msgid "Seed"
@@ -558,8 +559,8 @@ msgid "Choking (We are not allowing the peer to request pieces)"
msgstr "拒绝请求"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:927
msgid "Add Torrent"
msgstr "添加种子"
msgid "<img border=\"0\" src=\"/themes/console/snark/images/add.png\">Add Torrent"
msgstr "<img border=\"0\" src=\"/themes/console/snark/images/add.png\">添加种子"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:929
msgid "From URL"
@@ -571,16 +572,16 @@ msgstr "添加种子"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:937
#, java-format
msgid "Alternately, you can copy .torrent files to the directory {0}."
msgstr "或者您可以将.torrent文件复制到以下目录{0}."
msgid "You can also copy .torrent files to: <code>{0}"
msgstr "或者您可以将.torrent文件复制到<code>{0}."
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:939
msgid "Removing a .torrent file will cause the torrent to stop."
msgstr "删除种子文件将导致中止该下载任务。"
msgid "Removing a .torrent will cause it to stop."
msgstr "删除种子文件将导致该下载任务中止。"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:958
msgid "Create Torrent"
msgstr "创建种子"
msgid "<img border=\"0\" src=\"/themes/console/snark/images/create.png\">Create Torrent"
msgstr "<img border=\"0\" src=\"/themes/console/snark/images/create.png\">创建种子"
#. out.write("From file: <input type=\"file\" name=\"newFile\" size=\"50\" value=\"" + newFile + "\" /><br>\n");
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:961
@@ -596,8 +597,8 @@ msgid "Select a tracker"
msgstr "选择一个Tracker"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:982
msgid "or"
msgstr "或"
msgid "or&nbsp;"
msgstr "或&nbsp;"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:985
msgid "Specify custom tracker announce URL"
@@ -609,8 +610,8 @@ msgstr "创建种子"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1006
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1133
msgid "Configuration"
msgstr "设置"
msgid "<img border=\"0\" src=\"/themes/console/snark/images/config.png\">Configuration"
msgstr "<img border=\"0\" src=\"/themes/console/snark/images/config.png\">设置"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1009
msgid "Data directory"
@@ -709,53 +710,103 @@ msgid "1 tunnel"
msgid_plural "{0} tunnels"
msgstr[0] "{0}隧道"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1283
msgid "Up to higher level directory"
msgstr "上一层文件夹"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1271
msgid "Torrent"
msgstr "种子"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1288
msgid "File"
msgstr "文件"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1278
msgid "<img border=\"0\" src=\"/themes/console/snark/images/file.png\" title=\"File\" alt=\"File\">&nbsp;"
msgstr "<img border=\"0\" src=\"/themes/console/snark/images/file.png\" title=\"文件\" alt=\"文件\">&nbsp;"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1288
msgid "Size"
msgstr "大小"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1278
msgid "<img border=\"0\" src=\"/themes/console/snark/images/size.png\" title=\"FileSize\" alt=\"FileSize\">Size"
msgstr "<img border=\"0\" src=\"/themes/console/snark/images/size.png\" title=\"文件大小\" alt=\"文件大小\">大小"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1311
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1279
msgid "<img border=\"0\" src=\"/themes/console/snark/images/status.png\" title=\"Download Status\">Status"
msgstr "<img border=\"0\" src=\"/themes/console/snark/images/status.png\" title=\"下载状态\">状态"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1301
msgid "Directory"
msgstr "文件夹"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1316
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1306
msgid "Torrent not found?"
msgstr "种子未找到"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1324
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1314
msgid "File not found in torrent?"
msgstr "种子中没有发现文件?"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1330
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1320
msgid "complete"
msgstr "完成"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1331
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1321
msgid "bytes remaining"
msgstr "剩余字节数"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1456
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1373
msgid "Up to higher level directory"
msgstr "上一层文件夹"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1461
#, java-format
msgid "Torrent fetched from {0}"
msgstr "从{0}获取种子成功"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1484
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1489
#, java-format
msgid "Torrent at {0} was not valid"
msgstr "{0}的种子中有错误"
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1489
#: ../java/src/org/klomp/snark/web/I2PSnarkServlet.java:1494
#, java-format
msgid "Torrent was not retrieved from {0}"
msgstr "从{0}获得种子失败"
#~ msgid "Status"
#~ msgstr "状态"
#~ msgid "Hide Peers"
#~ msgstr "隐藏用户"
#~ msgid "Show Peers"
#~ msgstr "显示用户"
#~ msgid "ETA"
#~ msgstr "预计剩余时间"
#~ msgid "Downloaded"
#~ msgstr "已下载"
#~ msgid "Uploaded"
#~ msgstr "已上传"
#~ msgid "Down Rate"
#~ msgstr "下载速度"
#~ msgid "Up Rate"
#~ msgstr "上传速度"
#~ msgid "Stop All"
#~ msgstr "停止全部"
#~ msgid "Start All"
#~ msgstr "启动全部"
#~ msgid "Details"
#~ msgstr "详情"
#~ msgid "Stop"
#~ msgstr "停止"
#~ msgid "Start"
#~ msgstr "启动"
#~ msgid "Remove"
#~ msgstr "移除"
#~ msgid "Delete"
#~ msgstr "删除"
#~ msgid "Add Torrent"
#~ msgstr "添加种子"
#~ msgid "Create Torrent"
#~ msgstr "创建种子"
#~ msgid "or"
#~ msgstr "或"
#~ msgid "Configuration"
#~ msgstr "设置"
#~ msgid "File"
#~ msgstr "文件"
#~ msgid "Size"
#~ msgstr "大小"
#~ msgid "Cannot change the I2CP settings while torrents are active"
#~ msgstr "正在下载/上传无法更改I2CP设置"
#~ msgid "{0} torrents"

View File

@@ -22,4 +22,68 @@
30
</session-timeout>
</session-config>
<!-- mime types not in mime.properties in the jetty 5.1.15 source -->
<mime-mapping>
<extension>mkv</extension>
<mime-type>video/x-matroska</mime-type>
</mime-mapping>
<mime-mapping>
<extension>wmv</extension>
<mime-type>video/x-ms-wmv</mime-type>
</mime-mapping>
<mime-mapping>
<extension>flv</extension>
<mime-type>video/x-flv</mime-type>
</mime-mapping>
<mime-mapping>
<extension>mp4</extension>
<mime-type>video/mp4</mime-type>
</mime-mapping>
<mime-mapping>
<extension>rar</extension>
<mime-type>application/rar</mime-type>
</mime-mapping>
<mime-mapping>
<extension>7z</extension>
<mime-type>application/x-7z-compressed</mime-type>
</mime-mapping>
<mime-mapping>
<extension>iso</extension>
<mime-type>application/x-iso9660-image</mime-type>
</mime-mapping>
<mime-mapping>
<extension>ico</extension>
<mime-type>image/x-icon</mime-type>
</mime-mapping>
<mime-mapping>
<extension>exe</extension>
<mime-type>application/x-msdos-program</mime-type>
</mime-mapping>
<mime-mapping>
<extension>flac</extension>
<mime-type>audio/flac</mime-type>
</mime-mapping>
<mime-mapping>
<extension>m4a</extension>
<mime-type>audio/mpeg</mime-type>
</mime-mapping>
<mime-mapping>
<extension>wma</extension>
<mime-type>audio/x-ms-wma</mime-type>
</mime-mapping>
</web-app>

View File

@@ -81,7 +81,7 @@
<target name="war" depends="precompilejsp, bundle">
<war destfile="build/i2ptunnel.war" webxml="../jsp/web-out.xml"
basedir="../jsp/" excludes="web.xml, **/*.java, *.jsp">
basedir="../jsp/" excludes="web.xml, web-fragment.xml, web-out.xml, **/*.java, *.jsp">
</war>
</target>

View File

@@ -24,6 +24,9 @@ import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
/**
* This does the transparent gzip decompression on the client side.
* Extended in I2PTunnelHTTPServer to do the compression on the server side.
*
* Simple stream for delivering an HTTP response to
* the client, trivially filtered to make sure "Connection: close"
* is always in the response. Perhaps add transparent handling of the
@@ -33,29 +36,27 @@ import net.i2p.util.Log;
*
*/
class HTTPResponseOutputStream extends FilterOutputStream {
private I2PAppContext _context;
private Log _log;
private ByteCache _cache;
private final I2PAppContext _context;
private final Log _log;
protected ByteArray _headerBuffer;
private boolean _headerWritten;
private byte _buf1[];
private final byte _buf1[];
protected boolean _gzip;
private long _dataWritten;
private InternalGZIPInputStream _in;
private static final int CACHE_SIZE = 8*1024;
private static final ByteCache _cache = ByteCache.getInstance(8, CACHE_SIZE);
// OOM DOS prevention
private static final int MAX_HEADER_SIZE = 64*1024;
public HTTPResponseOutputStream(OutputStream raw) {
super(raw);
_context = I2PAppContext.getGlobalContext();
_context.statManager().createRateStat("i2ptunnel.httpCompressionRatio", "ratio of compressed size to decompressed size after transfer", "I2PTunnel", new long[] { 60*1000, 30*60*1000 });
_context.statManager().createRateStat("i2ptunnel.httpCompressed", "compressed size transferred", "I2PTunnel", new long[] { 60*1000, 30*60*1000 });
_context.statManager().createRateStat("i2ptunnel.httpExpanded", "size transferred after expansion", "I2PTunnel", new long[] { 60*1000, 30*60*1000 });
_context.statManager().createRateStat("i2ptunnel.httpCompressionRatio", "ratio of compressed size to decompressed size after transfer", "I2PTunnel", new long[] { 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.httpCompressed", "compressed size transferred", "I2PTunnel", new long[] { 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.httpExpanded", "size transferred after expansion", "I2PTunnel", new long[] { 60*60*1000 });
_log = _context.logManager().getLog(getClass());
_cache = ByteCache.getInstance(8, CACHE_SIZE);
_headerBuffer = _cache.acquire();
_headerWritten = false;
_gzip = false;
_dataWritten = 0;
_buf1 = new byte[1];
}
@@ -96,14 +97,20 @@ class HTTPResponseOutputStream extends FilterOutputStream {
}
}
/** grow (and free) the buffer as necessary */
private void ensureCapacity() {
/**
* grow (and free) the buffer as necessary
* @throws IOException if the headers are too big
*/
private void ensureCapacity() throws IOException {
if (_headerBuffer.getValid() >= MAX_HEADER_SIZE)
throw new IOException("Max header size exceeded: " + MAX_HEADER_SIZE);
if (_headerBuffer.getValid() + 1 >= _headerBuffer.getData().length) {
int newSize = (int)(_headerBuffer.getData().length * 1.5);
ByteArray newBuf = new ByteArray(new byte[newSize]);
System.arraycopy(_headerBuffer.getData(), 0, newBuf.getData(), 0, _headerBuffer.getValid());
newBuf.setValid(_headerBuffer.getValid());
newBuf.setOffset(0);
// if we changed the ByteArray size, don't put it back in the cache
if (_headerBuffer.getData().length == CACHE_SIZE)
_cache.release(_headerBuffer);
_headerBuffer = newBuf;
@@ -219,7 +226,7 @@ class HTTPResponseOutputStream extends FilterOutputStream {
//out.flush();
PipedInputStream pi = new PipedInputStream();
PipedOutputStream po = new PipedOutputStream(pi);
new I2PAppThread(new Pusher(pi, out), "HTTP decompresser").start();
new I2PAppThread(new Pusher(pi, out), "HTTP decompressor").start();
out = po;
}
@@ -231,13 +238,13 @@ class HTTPResponseOutputStream extends FilterOutputStream {
_out = out;
}
public void run() {
OutputStream to = null;
_in = null;
long start = System.currentTimeMillis();
long written = 0;
ByteArray ba = null;
try {
_in = new InternalGZIPInputStream(_inRaw);
byte buf[] = new byte[8192];
ba = _cache.acquire();
byte buf[] = ba.getData();
int read = -1;
while ( (read = _in.read(buf)) != -1) {
if (_log.shouldLog(Log.DEBUG))
@@ -251,6 +258,8 @@ class HTTPResponseOutputStream extends FilterOutputStream {
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error decompressing: " + written + ", " + (_in != null ? _in.getTotalRead() + "/" + _in.getTotalExpanded() : ""), ioe);
} catch (OutOfMemoryError oom) {
_log.error("OOM in HTTP Decompressor", oom);
} finally {
if (_log.shouldLog(Log.WARN) && (_in != null))
_log.warn("After decompression, written=" + written +
@@ -259,23 +268,26 @@ class HTTPResponseOutputStream extends FilterOutputStream {
+ ", expanded=" + _in.getTotalExpanded() + ", remaining=" + _in.getRemaining()
+ ", finished=" + _in.getFinished()
: ""));
if (ba != null)
_cache.release(ba);
if (_out != null) try {
_out.close();
} catch (IOException ioe) {}
}
long end = System.currentTimeMillis();
double compressed = (_in != null ? _in.getTotalRead() : 0);
double expanded = (_in != null ? _in.getTotalExpanded() : 0);
double ratio = 0;
if (expanded > 0)
ratio = compressed/expanded;
_context.statManager().addRateData("i2ptunnel.httpCompressionRatio", (int)(100d*ratio), end-start);
_context.statManager().addRateData("i2ptunnel.httpCompressed", (long)compressed, end-start);
_context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded, end-start);
if (compressed > 0 && expanded > 0) {
// only update the stats if we did something
double ratio = compressed/expanded;
_context.statManager().addRateData("i2ptunnel.httpCompressionRatio", (int)(100d*ratio), 0);
_context.statManager().addRateData("i2ptunnel.httpCompressed", (long)compressed, 0);
_context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded, 0);
}
}
}
/** just a wrapper to provide stats for debugging */
private static class InternalGZIPInputStream extends GZIPInputStream {
public InternalGZIPInputStream(InputStream in) throws IOException {
super(in);
@@ -294,6 +306,12 @@ class HTTPResponseOutputStream extends FilterOutputStream {
return 0;
}
}
/**
* From Inflater javadoc:
* Returns the total number of bytes remaining in the input buffer. This can be used to find out
* what bytes still remain in the input buffer after decompression has finished.
*/
public long getRemaining() {
try {
return super.inf.getRemaining();

View File

@@ -8,7 +8,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
@@ -110,7 +109,7 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
}
if (size == 1) // skip the rand in the most common case
return dests.get(0);
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
int index = _context.random().nextInt(size);
return dests.get(index);
}
}

View File

@@ -146,7 +146,7 @@ public class I2PTunnelConnectClient extends I2PTunnelClientBase implements Runna
int size = _proxyList.size();
if (size <= 0)
return null;
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
int index = _context.random().nextInt(size);
return _proxyList.get(index);
}
}

View File

@@ -205,7 +205,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
l.log("Proxy list is empty - no outproxy available");
return null;
}
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
int index = _context.random().nextInt(size);
String proxy = (String)proxyList.get(index);
return proxy;
}
@@ -626,6 +626,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
//line = "From: i2p";
line = null;
continue; // completely strip the line
} else if (lowercaseLine.startsWith("authorization: ntlm ")) {
// Block Windows NTLM after 401
line = null;
continue;
} else if (lowercaseLine.startsWith("proxy-authorization: ntlm ")) {
// Block Windows NTLM after 407
line = null;
continue;
}
}
@@ -808,7 +816,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
* @return non-null
*/
private byte[] getErrorPage(String base, byte[] backup) {
return getErrorPage(getTunnel().getContext(), base, backup);
return getErrorPage(_context, base, backup);
}
private static byte[] getErrorPage(I2PAppContext ctx, String base, byte[] backup) {

View File

@@ -290,6 +290,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
}
}
/** just a wrapper to provide stats for debugging */
private static class InternalGZIPOutputStream extends GZIPOutputStream {
public InternalGZIPOutputStream(OutputStream target) throws IOException {
super(target);

View File

@@ -9,7 +9,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import net.i2p.I2PAppContext;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
@@ -124,7 +123,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
}
if (size == 1) // skip the rand in the most common case
return dests.get(0);
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
int index = _context.random().nextInt(size);
return dests.get(index);
}
@@ -182,6 +181,8 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
}
outmsg=outmsg+"\r\n"; // rfc1459 sec. 2.3
output.write(outmsg.getBytes("ISO-8859-1"));
// probably doesn't do much but can't hurt
output.flush();
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("inbound BLOCKED: "+inmsg);
@@ -257,6 +258,8 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
}
outmsg=outmsg+"\r\n"; // rfc1459 sec. 2.3
output.write(outmsg.getBytes("ISO-8859-1"));
// save 250 ms in streaming
output.flush();
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("outbound BLOCKED: "+"\""+inmsg+"\"");

View File

@@ -129,7 +129,14 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
// do NOT flush here, it will block and then onTimeout.run() won't happen on fail.
// But if we don't flush, then we have to wait for the connectDelay timer to fire
// in i2p socket? To be researched and/or fixed.
//i2pout.flush();
//
// AS OF 0.8.1, MessageOutputStream.flush() is fixed to only wait for accept,
// not for "completion" (i.e. an ACK from the far end).
// So we now get a fast return from flush(), and can do it here to save 250 ms.
// To make sure we are under the initial window size and don't hang waiting for accept,
// only flush if it fits in one message.
if (initialI2PData.length <= 1730) // ConnectionOptions.DEFAULT_MAX_MESSAGE_SIZE
i2pout.flush();
}
}
if (initialSocketData != null) {

View File

@@ -18,6 +18,7 @@ import net.i2p.data.Base32;
import net.i2p.data.Destination;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.SecureFileOutputStream;
/**
* Coordinate the runtime operation and configuration of a tunnel.
@@ -89,7 +90,7 @@ public class TunnelController implements Logging {
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(keyFile);
fos = new SecureFileOutputStream(keyFile);
Destination dest = client.createDestination(fos);
String destStr = dest.toBase64();
log("Private key created and saved in " + keyFile.getAbsolutePath());

View File

@@ -20,6 +20,7 @@ import net.i2p.client.I2PSessionException;
import net.i2p.data.DataHelper;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.SecureFileOutputStream;
/**
* Coordinate a set of tunnels within the JVM, loading and storing their config
@@ -255,7 +256,7 @@ public class TunnelControllerGroup {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(cfgFile);
fos = new SecureFileOutputStream(cfgFile);
fos.write(buf.toString().getBytes("UTF-8"));
if (_log.shouldLog(Log.INFO))
_log.info("Config written to " + cfgFile.getPath());

View File

@@ -13,6 +13,11 @@ import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.data.Base64;
import net.i2p.data.Destination;
import net.i2p.data.PrivateKeyFile;
import net.i2p.data.Signature;
import net.i2p.data.SigningPrivateKey;
import net.i2p.i2ptunnel.TunnelController;
import net.i2p.i2ptunnel.TunnelControllerGroup;
@@ -68,6 +73,31 @@ public class EditBean extends IndexBean {
return "i2ptunnel" + tunnel + "-privKeys.dat";
}
public String getNameSignature(int tunnel) {
String spoof = getSpoofedHost(tunnel);
if (spoof.length() <= 0)
return "";
TunnelController tun = getController(tunnel);
if (tun == null)
return "";
String keyFile = tun.getPrivKeyFile();
if (keyFile != null && keyFile.trim().length() > 0) {
PrivateKeyFile pkf = new PrivateKeyFile(keyFile);
try {
Destination d = pkf.getDestination();
if (d == null)
return "";
SigningPrivateKey privKey = pkf.getSigningPrivKey();
if (privKey == null)
return "";
//System.err.println("Signing " + spoof + " with " + Base64.encode(privKey.getData()));
Signature sig = _context.dsa().sign(spoof.getBytes("UTF-8"), privKey);
return Base64.encode(sig.getData());
} catch (Exception e) {}
}
return "";
}
public boolean startAutomatically(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null)

View File

@@ -9,6 +9,7 @@ package net.i2p.i2ptunnel.web;
*/
import java.util.concurrent.ConcurrentHashMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -41,10 +42,10 @@ public class IndexBean {
protected TunnelControllerGroup _group;
private String _action;
private int _tunnel;
private long _prevNonce;
private long _prevNonce2;
private long _curNonce;
private long _nextNonce;
//private long _prevNonce;
//private long _prevNonce2;
private String _curNonce;
//private long _nextNonce;
private String _type;
private String _name;
@@ -85,10 +86,14 @@ public class IndexBean {
/** deprecated unimplemented, now using routerconsole realm */
//public static final String PROP_TUNNEL_PASSPHRASE = "i2ptunnel.passphrase";
public static final String PROP_TUNNEL_PASSPHRASE = "consolePassword";
static final String PROP_NONCE = IndexBean.class.getName() + ".nonce";
static final String PROP_NONCE_OLD = PROP_NONCE + '2';
//static final String PROP_NONCE = IndexBean.class.getName() + ".nonce";
//static final String PROP_NONCE_OLD = PROP_NONCE + '2';
/** 3 wasn't enough for some browsers. They are reloading the page for some reason - maybe HEAD? @since 0.8.1 */
private static final int MAX_NONCES = 5;
/** store nonces in a static FIFO instead of in System Properties @since 0.8.1 */
private static final List<String> _nonces = new ArrayList(MAX_NONCES + 1);
static final String CLIENT_NICKNAME = "shared clients";
public static final String PROP_THEME_NAME = "routerconsole.theme";
public static final String DEFAULT_THEME = "light";
public static final String PROP_CSS_DISABLED = "routerconsole.css.disabled";
@@ -98,34 +103,39 @@ public class IndexBean {
_context = I2PAppContext.getGlobalContext();
_log = _context.logManager().getLog(IndexBean.class);
_group = TunnelControllerGroup.getInstance();
_action = null;
_tunnel = -1;
_curNonce = -1;
_prevNonce = -1;
_prevNonce2 = -1;
try {
String nonce2 = System.getProperty(PROP_NONCE_OLD);
if (nonce2 != null)
_prevNonce2 = Long.parseLong(nonce2);
String nonce = System.getProperty(PROP_NONCE);
if (nonce != null) {
_prevNonce = Long.parseLong(nonce);
System.setProperty(PROP_NONCE_OLD, nonce);
}
} catch (NumberFormatException nfe) {}
_nextNonce = _context.random().nextLong();
System.setProperty(PROP_NONCE, Long.toString(_nextNonce));
_curNonce = "-1";
addNonce();
_booleanOptions = new ConcurrentHashSet(4);
_otherOptions = new ConcurrentHashMap(4);
}
public long getNextNonce() { return _nextNonce; }
public static String getNextNonce() {
synchronized (_nonces) {
return _nonces.get(0);
}
}
public void setNonce(String nonce) {
if ( (nonce == null) || (nonce.trim().length() <= 0) ) return;
try {
_curNonce = Long.parseLong(nonce);
} catch (NumberFormatException nfe) {
_curNonce = -1;
_curNonce = nonce;
}
/** add a random nonce to the head of the queue @since 0.8.1 */
private void addNonce() {
String nextNonce = Long.toString(_context.random().nextLong());
synchronized (_nonces) {
_nonces.add(0, nextNonce);
int sz = _nonces.size();
if (sz > MAX_NONCES)
_nonces.remove(sz - 1);
}
}
/** do we know this nonce? @since 0.8.1 */
private static boolean haveNonce(String nonce) {
synchronized (_nonces) {
return _nonces.contains(nonce);
}
}
@@ -155,7 +165,7 @@ public class IndexBean {
private String processAction() {
if ( (_action == null) || (_action.trim().length() <= 0) || ("Cancel".equals(_action)))
return "";
if ( (_prevNonce != _curNonce) && (_prevNonce2 != _curNonce) && (!validPassphrase()) )
if ( (!haveNonce(_curNonce)) && (!validPassphrase()) )
return "Invalid form submission, probably because you used the 'back' or 'reload' button on your browser. Please resubmit.";
if ("Stop all".equals(_action))
return stopAll();

View File

@@ -196,7 +196,16 @@
<a href="/susidns/addressbook.jsp?book=private&hostname=<%=editBean.getTunnelName(curTunnel)%>&destination=<%=editBean.getDestinationBase64(curTunnel)%>#add"><%=intl._("Add to local addressbook")%></a>
<% } %>
</div>
<% if (("httpserver".equals(tunnelType)) || ("httpbidirserver".equals(tunnelType))) {
%><div id="sigField" class="rowItem">
<label for="signature">
<%=intl._("Hostname Signature")%>
</label>
<input type="text" size="30" readonly="readonly" title="Use to prove that the website name is for this destination" value="<%=editBean.getNameSignature(curTunnel)%>" wrap="off" class="freetext" />
</div>
<% } %>
<div class="footer">
</div>
</div>

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: I2P i2ptunnel\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-07-01 04:52+0000\n"
"POT-Creation-Date: 2010-10-04 02:45+0000\n"
"PO-Revision-Date: 2010-05-29 10:57+0800\n"
"Last-Translator: walking <walking@mail.i2p>\n"
"Language-Team: foo <foo@bar>\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Chinese\n"
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:493
#: ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java:492
#, java-format
msgid ""
"To visit the destination in your host database, click <a href=\"{0}\">here</"
@@ -35,55 +35,55 @@ msgstr ""
"请点击下面的链接通过【跳转(Jump)】服务提供的【地址助手】链接跳转至域名对应的"
"主机:"
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:369
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:372
msgid "New Tunnel"
msgstr "新建隧道"
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:389
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:392
msgid "Standard client"
msgstr "标准客户端"
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:390
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:393
msgid "HTTP client"
msgstr "HTTP 客户端"
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:391
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:394
msgid "IRC client"
msgstr "IRC 客户端"
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:392
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:395
msgid "Standard server"
msgstr "标准服务器"
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:393
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:396
msgid "HTTP server"
msgstr "HTTP 服务器"
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:394
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:397
msgid "SOCKS 4/4a/5 proxy"
msgstr "SOCKS4/4A/5 代理"
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:395
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:398
msgid "SOCKS IRC proxy"
msgstr "SOCKS IRC 代理"
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:396
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:399
msgid "CONNECT/SSL/HTTPS proxy"
msgstr "CONNECT/SSL/HTTPS 代理"
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:397
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:400
msgid "IRC server"
msgstr "IRC 服务器"
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:398
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:401
msgid "Streamr client"
msgstr "Streamr 客户端"
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:399
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:402
msgid "Streamr server"
msgstr "Streamr 服务器"
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:400
#: ../java/src/net/i2p/i2ptunnel/web/IndexBean.java:403
msgid "HTTP bidir"
msgstr "双向http"

View File

@@ -130,16 +130,19 @@ public class I2PSocketManagerFactory {
if (!opts.containsKey(name))
opts.setProperty(name, System.getProperty(name));
}
boolean oldLib = DEFAULT_MANAGER.equals(opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER));
if (oldLib && false) {
//boolean oldLib = DEFAULT_MANAGER.equals(opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER));
//if (oldLib && false) {
// for the old streaming lib
opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
// opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
//opts.setProperty("tunnels.depthInbound", "0");
} else {
//} else {
// for new streaming lib:
opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT);
//opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT);
// as of 0.8.1 (I2CP default is BestEffort)
if (!opts.containsKey(I2PClient.PROP_RELIABILITY))
opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_NONE);
//p.setProperty("tunnels.depthInbound", "0");
}
//}
if (i2cpHost != null)
opts.setProperty(I2PClient.PROP_TCP_HOST, i2cpHost);

View File

@@ -113,7 +113,7 @@
<target name="war" depends="precompilejsp">
<!-- Don't include the css in the war, the main build.xml will copy it to docs/themes/console/ -->
<war destfile="build/routerconsole.war" webxml="../jsp/web-out.xml"
basedir="../jsp/" excludes="web.xml, *.css, **/*.java, *.jsp, *.jsi, web-fragment.xml">
basedir="../jsp/" excludes="web.xml, *.css, **/*.java, *.jsp, *.jsi, web-fragment.xml, web-out.xml">
</war>
</target>
<target name="precompilejsp" unless="precompilejsp.uptodate">

View File

@@ -17,6 +17,8 @@ public class ConfigLoggingHandler extends FormHandler {
private String _recordFormat;
private String _dateFormat;
private String _fileSize;
private String _newLogClass;
private String _newLogLevel = "WARN";
@Override
protected void processForm() {
@@ -26,7 +28,7 @@ public class ConfigLoggingHandler extends FormHandler {
// noop
}
}
public void setShouldsave(String moo) { _shouldSave = true; }
public void setLevels(String levels) {
@@ -47,6 +49,18 @@ public class ConfigLoggingHandler extends FormHandler {
public void setLogfilesize(String size) {
_fileSize = (size != null ? size.trim() : null);
}
/** @since 0.8.1 */
public void setNewlogclass(String s) {
if (s != null && s.length() > 0)
_newLogClass = s;
}
/** @since 0.8.1 */
public void setNewloglevel(String s) {
if (s != null)
_newLogLevel = s;
}
/**
* The user made changes to the config and wants to save them, so
@@ -56,14 +70,18 @@ public class ConfigLoggingHandler extends FormHandler {
private void saveChanges() {
boolean shouldSave = false;
if (_levels != null) {
if (_levels != null || _newLogClass != null) {
try {
Properties props = new Properties();
props.load(new ByteArrayInputStream(_levels.getBytes()));
if (_levels != null)
props.load(new ByteArrayInputStream(_levels.getBytes()));
if (_newLogClass != null)
props.setProperty(_newLogClass, _newLogLevel);
_context.logManager().setLimits(props);
shouldSave = true;
addFormNotice("Log limits updated");
addFormNotice(_("Log overrides updated"));
} catch (IOException ioe) {
// shouldn't ever happen (BAIS shouldnt cause an IOE)
_context.logManager().getLog(ConfigLoggingHandler.class).error("Error reading from the props?", ioe);
addFormError("Error updating the log limits - levels not valid");
}
@@ -83,7 +101,7 @@ public class ConfigLoggingHandler extends FormHandler {
}
}
if (_dateFormat != null) {
if (_dateFormat != null && !_dateFormat.equals(_context.logManager().getDateFormatPattern())) {
boolean valid = _context.logManager().setDateFormat(_dateFormat);
if (valid) {
shouldSave = true;
@@ -139,7 +157,7 @@ public class ConfigLoggingHandler extends FormHandler {
boolean saved = _context.logManager().saveConfig();
if (saved)
addFormNotice("Log configuration saved and applied successfully");
addFormNotice(_("Log configuration saved"));
else
addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs");
}

View File

@@ -1,9 +1,13 @@
package net.i2p.router.web;
import java.util.List;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
public class ConfigLoggingHelper extends HelperBase {
public ConfigLoggingHelper() {}
@@ -19,18 +23,13 @@ public class ConfigLoggingHelper extends HelperBase {
}
public String getMaxFileSize() {
int bytes = _context.logManager().getFileSize();
if (bytes == 0) return "1m";
if (bytes > 1024*1024*1024)
return (bytes/(1024*1024*1024)) + "g";
else if (bytes > 1024*1024)
return (bytes/(1024*1024)) + "m";
else
return (bytes/(1024)) + "k";
if (bytes <= 0) return "1.00 MB";
return DataHelper.formatSize2(bytes) + 'B';
}
public String getLogLevelTable() {
StringBuilder buf = new StringBuilder(32*1024);
Properties limits = _context.logManager().getLimits();
TreeSet sortedLogs = new TreeSet();
TreeSet<String> sortedLogs = new TreeSet();
for (Iterator iter = limits.keySet().iterator(); iter.hasNext(); ) {
String prefix = (String)iter.next();
sortedLogs.add(prefix);
@@ -46,6 +45,20 @@ public class ConfigLoggingHelper extends HelperBase {
buf.append("<i>" + _("Add additional logging statements above. Example: net.i2p.router.tunnel=WARN") + "</i><br>");
buf.append("<i>" + _("Or put entries in the logger.config file. Example: logger.record.net.i2p.router.tunnel=WARN") + "</i><br>");
buf.append("<i>" + _("Valid levels are DEBUG, INFO, WARN, ERROR, CRIT") + "</i>\n");
/****
// this is too big and ugly
if (limits.size() <= 0)
return "";
buf.append("<table>");
for (String prefix : sortedLogs) {
buf.append("<tr><td>").append(prefix).append("</td><td>");
String level = limits.getProperty(prefix);
buf.append(getLogLevelBox("level-" + prefix, level, true)).append("</td></tr>");
}
buf.append("</table>");
****/
return buf.toString();
}
@@ -53,18 +66,69 @@ public class ConfigLoggingHelper extends HelperBase {
public String getDefaultLogLevelBox() {
String cur = _context.logManager().getDefaultLimit();
return getLogLevelBox("defaultloglevel", cur, false);
}
private String getLogLevelBox(String name, String cur, boolean showRemove) {
StringBuilder buf = new StringBuilder(128);
buf.append("<select name=\"defaultloglevel\">\n");
buf.append("<select name=\"").append(name).append("\">\n");
for (int i = 0; i < levels.length; i++) {
String l = levels[i];
buf.append("<option value=\"").append(l).append("\" ");
buf.append("<option value=\"").append(l).append('\"');
if (l.equals(cur))
buf.append(" selected=\"true\" ");
buf.append(" selected=\"true\"");
buf.append('>').append(_(l)).append("</option>\n");
}
if (showRemove)
buf.append("<option value=\"remove\">").append(_("Remove")).append("</option>");
buf.append("</select>\n");
return buf.toString();
}
/**
* All the classes the log manager knows about, except ones that
* already have overrides
* @since 0.8.1
*/
public String getNewClassBox() {
List<Log> logs = _context.logManager().getLogs();
Set limits = _context.logManager().getLimits().keySet();
TreeSet<String> sortedLogs = new TreeSet();
for (Log log : logs) {
String name = log.getName();
if (!limits.contains(name))
sortedLogs.add(name);
// add higher classes of length 3 or more
int dots = 0;
int lastdot = -1;
int nextdot = 0;
while ((nextdot = name.indexOf('.', lastdot + 1)) > 0) {
if (++dots >= 3) {
String subst = name.substring(0, nextdot);
if (!limits.contains(subst))
sortedLogs.add(subst);
}
lastdot = nextdot;
}
}
StringBuilder buf = new StringBuilder(65536);
buf.append("<select name=\"newlogclass\">\n" +
"<option value=\"\" selected=\"true\">")
.append(_("Select a class to add"))
.append("</option>\n");
for (String l : sortedLogs) {
buf.append("<option value=\"").append(l).append("\">")
.append(l).append("</option>\n");
}
buf.append("</select>\n");
buf.append(getLogLevelBox("newloglevel", "WARN", false));
return buf.toString();
}
}

View File

@@ -44,13 +44,16 @@ public class LogsHelper extends HelperBase {
}
******/
private String formatMessages(List msgs) {
/** formats in reverse order */
private String formatMessages(List<String> msgs) {
if (msgs.isEmpty())
return "<p><i>" + _("No log messages") + "</i></p>";
boolean colorize = Boolean.valueOf(_context.getProperty("routerconsole.logs.color")).booleanValue();
StringBuilder buf = new StringBuilder(16*1024);
buf.append("<ul>");
buf.append("<code>\n");
for (int i = msgs.size(); i > 0; i--) {
String msg = (String)msgs.get(i - 1);
String msg = msgs.get(i - 1);
buf.append("<li>");
if (colorize) {
String color;

View File

@@ -19,6 +19,8 @@ import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
@@ -34,6 +36,7 @@ import net.i2p.router.TunnelPoolSettings;
import net.i2p.router.networkdb.kademlia.HashDistance; // debug
import net.i2p.util.HexDump; // debug
import net.i2p.util.ObjectCounter;
import net.i2p.util.OrderedProperties;
import net.i2p.util.VersionComparator;
public class NetDbRenderer {
@@ -371,9 +374,11 @@ public class NetDbRenderer {
int cost = addr.getCost();
if (!((style.equals("SSU") && cost == 5) || (style.equals("NTCP") && cost == 10)))
buf.append('[').append(_("cost")).append('=').append("" + cost).append("] ");
for (Iterator optIter = addr.getOptions().keySet().iterator(); optIter.hasNext(); ) {
String name = (String)optIter.next();
String val = addr.getOptions().getProperty(name);
Properties p = new OrderedProperties();
p.putAll(addr.getOptions());
for (Map.Entry e : p.entrySet()) {
String name = (String) e.getKey();
String val = (String) e.getValue();
buf.append('[').append(_(DataHelper.stripHTML(name))).append('=').append(DataHelper.stripHTML(val)).append("] ");
}
}

View File

@@ -563,7 +563,7 @@ public class PluginStarter implements Runnable {
/**
* http://jimlife.wordpress.com/2007/12/19/java-adding-new-classpath-at-runtime/
*/
public static void addPath(URL u) throws Exception {
private static void addPath(URL u) throws Exception {
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class urlClass = URLClassLoader.class;
Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class});

View File

@@ -16,6 +16,7 @@ import net.i2p.util.FileUtil;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.OrderedProperties;
import net.i2p.util.SecureDirectory;
import net.i2p.util.SimpleScheduler;
import net.i2p.util.SimpleTimer;
import net.i2p.util.VersionComparator;
@@ -150,7 +151,7 @@ public class PluginUpdateHandler extends UpdateHandler {
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
updateStatus("<b>" + _("Plugin downloaded") + "</b>");
File f = new File(_updateFile);
File appDir = new File(_context.getConfigDir(), PLUGIN_DIR);
File appDir = new SecureDirectory(_context.getConfigDir(), PLUGIN_DIR);
if ((!appDir.exists()) && (!appDir.mkdir())) {
f.delete();
statusDone("<b>" + _("Cannot create plugin directory {0}", appDir.getAbsolutePath()) + "</b>");
@@ -273,7 +274,7 @@ public class PluginUpdateHandler extends UpdateHandler {
return;
}
File destDir = new File(appDir, appName);
File destDir = new SecureDirectory(appDir, appName);
if (destDir.exists()) {
if (Boolean.valueOf(props.getProperty("install-only")).booleanValue()) {
to.delete();

View File

@@ -14,6 +14,7 @@ import net.i2p.data.DataHelper;
import net.i2p.router.RouterContext;
import net.i2p.util.FileUtil;
import net.i2p.util.I2PAppThread;
import net.i2p.util.SecureDirectory;
import org.mortbay.http.DigestAuthenticator;
import org.mortbay.http.HashUserRealm;
@@ -62,7 +63,7 @@ public class RouterConsoleRunner {
}
public void startConsole() {
File workDir = new File(I2PAppContext.getGlobalContext().getTempDir(), "jetty-work");
File workDir = new SecureDirectory(I2PAppContext.getGlobalContext().getTempDir(), "jetty-work");
boolean workDirRemoved = FileUtil.rmdir(workDir, false);
if (!workDirRemoved)
System.err.println("ERROR: Unable to remove Jetty temporary work directory");
@@ -115,7 +116,7 @@ public class RouterConsoleRunner {
}
_server.setRootWebApp(ROUTERCONSOLE);
WebApplicationContext wac = _server.addWebApplication("/", _webAppsDir + ROUTERCONSOLE + ".war");
File tmpdir = new File(workDir, ROUTERCONSOLE + "-" + _listenPort);
File tmpdir = new SecureDirectory(workDir, ROUTERCONSOLE + "-" + _listenPort);
tmpdir.mkdir();
wac.setTempDirectory(tmpdir);
baseHandler = new LocaleWebAppHandler(I2PAppContext.getGlobalContext());
@@ -130,7 +131,7 @@ public class RouterConsoleRunner {
String enabled = props.getProperty(PREFIX + appName + ENABLED);
if (! "false".equals(enabled)) {
String path = new File(dir, fileNames[i]).getCanonicalPath();
tmpdir = new File(workDir, appName + "-" + _listenPort);
tmpdir = new SecureDirectory(workDir, appName + "-" + _listenPort);
WebAppStarter.addWebApp(I2PAppContext.getGlobalContext(), _server, appName, path, tmpdir);
if (enabled == null) {

View File

@@ -10,6 +10,7 @@ import java.util.concurrent.ConcurrentHashMap;
import net.i2p.I2PAppContext;
import net.i2p.util.FileUtil;
import net.i2p.util.SecureDirectory;
import org.mortbay.http.HttpContext;
import org.mortbay.http.HttpListener;
@@ -41,7 +42,7 @@ public class WebAppStarter {
* @throws just about anything, caller would be wise to catch Throwable
*/
static void startWebApp(I2PAppContext ctx, Server server, String appName, String warPath) throws Exception {
File tmpdir = new File(ctx.getTempDir(), "jetty-work-" + appName + ctx.random().nextInt());
File tmpdir = new SecureDirectory(ctx.getTempDir(), "jetty-work-" + appName + ctx.random().nextInt());
WebApplicationContext wac = addWebApp(ctx, server, appName, warPath, tmpdir);
wac.start();
}
@@ -73,7 +74,7 @@ public class WebAppStarter {
warModTimes.put(warPath, new Long(newmod));
} else if (oldmod.longValue() < newmod) {
// copy war to temporary directory
File warTmpDir = new File(ctx.getTempDir(), "war-copy-" + appName + ctx.random().nextInt());
File warTmpDir = new SecureDirectory(ctx.getTempDir(), "war-copy-" + appName + ctx.random().nextInt());
warTmpDir.mkdir();
String tmpPath = (new File(warTmpDir, appName + ".war")).getAbsolutePath();
if (!FileUtil.copy(warPath, tmpPath, true))

View File

@@ -46,6 +46,8 @@
</i></td>
</tr><tr><td class="mediumtags" align="right"><b><%=intl._("Log level overrides")%>:</b></td>
<td><jsp:getProperty name="logginghelper" property="logLevelTable" /></td>
</tr><tr><td class="mediumtags" align="right"><b><%=intl._("New override")%>:</b></td>
<td><jsp:getProperty name="logginghelper" property="newClassBox" /></td>
</tr><tr><td colspan="2"><hr></td>
</tr><tr class="tablefooter"><td colspan="2"> <div class="formaction">
<input type="reset" value="<%=intl._("Cancel")%>" >

File diff suppressed because it is too large Load Diff

View File

@@ -148,16 +148,21 @@ public class Connection {
}
/**
* This doesn't "send a choke". Rather, it blocks if the outbound window is full,
* thus choking the sender that calls this.
*
* Block until there is an open outbound packet slot or the write timeout
* expires.
* PacketLocal is the only caller, generally with -1.
*
* @param timeoutMs PacketLocal is the only caller, often with -1??????
* @return true if the packet should be sent
* @param timeoutMs 0 or negative means wait forever, 5 minutes max
* @return true if the packet should be sent, false for a fatal error
* will return false after 5 minutes even if timeoutMs is <= 0.
*/
boolean packetSendChoke(long timeoutMs) {
// if (false) return true; // <--- what the fuck??
long start = _context.clock().now();
long writeExpire = start + timeoutMs;
long writeExpire = start + timeoutMs; // only used if timeoutMs > 0
boolean started = false;
while (true) {
long timeLeft = writeExpire - _context.clock().now();

View File

@@ -646,6 +646,7 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
return Boolean.valueOf(val).booleanValue();
}
/****
public static void main(String args[]) {
Properties p = new Properties();
@@ -656,4 +657,5 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
c = new ConnectionOptions(new I2PSocketOptionsImpl(p));
System.out.println("opts: " + c);
}
****/
}

View File

@@ -11,7 +11,7 @@ import java.util.Map;
import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
import net.i2p.util.ByteCache;
//import net.i2p.util.ByteCache;
import net.i2p.util.Log;
/**
@@ -20,8 +20,8 @@ import net.i2p.util.Log;
*
*/
public class MessageInputStream extends InputStream {
private I2PAppContext _context;
private Log _log;
private final I2PAppContext _context;
private final Log _log;
/**
* List of ByteArray objects of data ready to be read,
* with the first ByteArray at index 0, and the next
@@ -29,7 +29,7 @@ public class MessageInputStream extends InputStream {
* that array.
*
*/
private List _readyDataBlocks;
private final List<ByteArray> _readyDataBlocks;
private int _readyDataBlockIndex;
/** highest message ID used in the readyDataBlocks */
private volatile long _highestReadyBlockId;
@@ -40,7 +40,7 @@ public class MessageInputStream extends InputStream {
* out of order when there are lower IDs not yet
* received
*/
private Map _notYetReadyBlocks;
private final Map<Long, ByteArray> _notYetReadyBlocks;
/**
* if we have received a flag saying there won't be later messages, EOF
* after we have cleared what we have received.
@@ -51,9 +51,9 @@ public class MessageInputStream extends InputStream {
private int _readTimeout;
private IOException _streamError;
private long _readTotal;
private ByteCache _cache;
//private ByteCache _cache;
private byte[] _oneByte = new byte[1];
private final byte[] _oneByte = new byte[1];
private final Object _dataLock;
@@ -61,16 +61,12 @@ public class MessageInputStream extends InputStream {
_context = ctx;
_log = ctx.logManager().getLog(MessageInputStream.class);
_readyDataBlocks = new ArrayList(4);
_readyDataBlockIndex = 0;
_highestReadyBlockId = -1;
_highestBlockId = -1;
_readTimeout = -1;
_readTotal = 0;
_notYetReadyBlocks = new HashMap(4);
_dataLock = new Object();
_closeReceived = false;
_locallyClosed = false;
_cache = ByteCache.getInstance(128, Packet.MAX_PAYLOAD_SIZE);
//_cache = ByteCache.getInstance(128, Packet.MAX_PAYLOAD_SIZE);
}
/** What is the highest block ID we've completely received through?
@@ -140,10 +136,8 @@ public class MessageInputStream extends InputStream {
if (num <= 0) return null;
blocks = new long[num];
int i = 0;
for (Iterator iter = _notYetReadyBlocks.keySet().iterator(); iter.hasNext(); ) {
Long id = (Long)iter.next();
blocks[i] = id.longValue();
i++;
for (Long id : _notYetReadyBlocks.keySet()) {
blocks[i++] = id.longValue();
}
}
Arrays.sort(blocks);
@@ -178,16 +172,15 @@ public class MessageInputStream extends InputStream {
buf.append("Close received, ready bytes: ");
long available = 0;
for (int i = 0; i < _readyDataBlocks.size(); i++)
available += ((ByteArray)_readyDataBlocks.get(i)).getValid();
available += _readyDataBlocks.get(i).getValid();
available -= _readyDataBlockIndex;
buf.append(available);
buf.append(" blocks: ").append(_readyDataBlocks.size());
buf.append(" not ready blocks: ");
long notAvailable = 0;
for (Iterator iter = _notYetReadyBlocks.keySet().iterator(); iter.hasNext(); ) {
Long id = (Long)iter.next();
ByteArray ba = (ByteArray)_notYetReadyBlocks.get(id);
for (Long id : _notYetReadyBlocks.keySet()) {
ByteArray ba = _notYetReadyBlocks.get(id);
buf.append(id).append(" ");
if (ba != null)
@@ -237,7 +230,7 @@ public class MessageInputStream extends InputStream {
long cur = _highestReadyBlockId + 1;
// now pull in any previously pending blocks
while (_notYetReadyBlocks.containsKey(new Long(cur))) {
ByteArray ba = (ByteArray)_notYetReadyBlocks.remove(new Long(cur));
ByteArray ba = _notYetReadyBlocks.remove(new Long(cur));
if ( (ba != null) && (ba.getData() != null) && (ba.getValid() > 0) ) {
_readyDataBlocks.add(ba);
}
@@ -341,7 +334,7 @@ public class MessageInputStream extends InputStream {
return i;
} else {
// either was already ready, or we wait()ed and it arrived
ByteArray cur = (ByteArray)_readyDataBlocks.get(0);
ByteArray cur = _readyDataBlocks.get(0);
byte rv = cur.getData()[cur.getOffset()+_readyDataBlockIndex];
_readyDataBlockIndex++;
boolean removed = false;
@@ -378,7 +371,7 @@ public class MessageInputStream extends InputStream {
int numBytes = 0;
synchronized (_dataLock) {
for (int i = 0; i < _readyDataBlocks.size(); i++) {
ByteArray cur = (ByteArray)_readyDataBlocks.get(i);
ByteArray cur = _readyDataBlocks.get(i);
if (i == 0)
numBytes += cur.getValid() - _readyDataBlockIndex;
else
@@ -402,14 +395,13 @@ public class MessageInputStream extends InputStream {
if (_locallyClosed) return 0;
int numBytes = 0;
for (int i = 0; i < _readyDataBlocks.size(); i++) {
ByteArray cur = (ByteArray)_readyDataBlocks.get(i);
ByteArray cur = _readyDataBlocks.get(i);
if (i == 0)
numBytes += cur.getValid() - _readyDataBlockIndex;
else
numBytes += cur.getValid();
}
for (Iterator iter = _notYetReadyBlocks.values().iterator(); iter.hasNext(); ) {
ByteArray cur = (ByteArray)iter.next();
for (ByteArray cur : _notYetReadyBlocks.values()) {
numBytes += cur.getValid();
}
return numBytes;
@@ -421,7 +413,7 @@ public class MessageInputStream extends InputStream {
if (_locallyClosed) return 0;
int numBytes = 0;
for (int i = 0; i < _readyDataBlocks.size(); i++) {
ByteArray cur = (ByteArray)_readyDataBlocks.get(i);
ByteArray cur = _readyDataBlocks.get(i);
if (i == 0)
numBytes += cur.getValid() - _readyDataBlockIndex;
else
@@ -440,8 +432,7 @@ public class MessageInputStream extends InputStream {
// we don't need the data, but we do need to keep track of the messageIds
// received, so we can ACK accordingly
for (Iterator iter = _notYetReadyBlocks.values().iterator(); iter.hasNext(); ) {
ByteArray ba = (ByteArray)iter.next();
for (ByteArray ba : _notYetReadyBlocks.values()) {
ba.setData(null);
//_cache.release(ba);
}

View File

@@ -43,6 +43,10 @@ public class MessageOutputStream extends OutputStream {
private long _sendPeriodBytes;
private int _sendBps;
/**
* Since this is less than i2ptunnel's i2p.streaming.connectDelay default of 1000,
* we only wait 250 at the start. Guess that's ok, 1000 is too long anyway.
*/
private static final int DEFAULT_PASSIVE_FLUSH_DELAY = 250;
public MessageOutputStream(I2PAppContext ctx, DataReceiver receiver) {
@@ -273,8 +277,18 @@ public class MessageOutputStream extends OutputStream {
}
/**
* Flush the data already queued up, blocking until it has been
* delivered.
* Flush the data already queued up, blocking only if the outbound
* window is full.
*
* Prior to 0.8.1, this blocked until "delivered".
* "Delivered" meant "received an ACK from the far end",
* which is not the commom implementation of flush(), and really hurt the
* performance of i2psnark, which flush()ed frequently.
* Calling flush() would cause a complete window stall.
*
* As of 0.8.1, only wait for accept into the streaming output queue.
* This will speed up snark significantly, and allow us to flush()
* the initial data in I2PTunnelRunner, saving 250 ms.
*
* @throws IOException if the write fails
*/
@@ -283,6 +297,14 @@ public class MessageOutputStream extends OutputStream {
/* @throws InterruptedIOException if the write times out
* Documented here, but doesn't belong in the javadoc.
*/
flush(true);
}
/**
* @param wait_for_accept_only see discussion in close() code
* @@since 0.8.1
*/
private void flush(boolean wait_for_accept_only) throws IOException {
long begin = _context.clock().now();
WriteStatus ws = null;
if (_log.shouldLog(Log.INFO) && _valid > 0)
@@ -297,14 +319,28 @@ public class MessageOutputStream extends OutputStream {
throwAnyError();
return;
}
ws = _dataReceiver.writeData(_buf, 0, _valid);
_written += _valid;
_valid = 0;
locked_updateBufferSize();
_lastFlushed = _context.clock().now();
_dataLock.notifyAll();
// if valid == 0 return ??? - no, this could flush a CLOSE packet too.
// Yes, flush here, inside the data lock, and do all the waitForCompletion() stuff below
// (disabled)
if (!wait_for_accept_only) {
ws = _dataReceiver.writeData(_buf, 0, _valid);
_written += _valid;
_valid = 0;
locked_updateBufferSize();
_lastFlushed = _context.clock().now();
_dataLock.notifyAll();
}
}
// Skip all the waitForCompletion() stuff below, which is insanity, as of 0.8.1
// must do this outside the data lock
if (wait_for_accept_only) {
flushAvailable(_dataReceiver, true);
return;
}
// Wait a loooooong time, until we have the ACK
if (_log.shouldLog(Log.DEBUG))
_log.debug("before waiting " + _writeTimeout + "ms for completion of " + ws);
if (_closed &&
@@ -328,14 +364,28 @@ public class MessageOutputStream extends OutputStream {
throwAnyError();
}
/**
* This does a flush, and BLOCKS until
* the CLOSE packet is acked.
*/
@Override
public void close() throws IOException {
if (_closed) {
synchronized (_dataLock) { _dataLock.notifyAll(); }
return;
}
// setting _closed before flush() will force flush() to send a CLOSE packet
_closed = true;
flush();
// In 0.8.1 we rewrote flush() to only wait for accept into the window,
// not "completion" (i.e. ack from the far end).
// Unfortunately, that broke close(), at least in i2ptunnel HTTPClient.
// Symptom was premature close, i.e. incomplete pages and images.
// Possible cause - I2PTunnelRunner code? or the code here that follows flush()?
// It seems like we shouldn't have to wait for the far-end ACK for a close packet,
// should we? To be researched further.
// false -> wait for completion, not just accept.
flush(false);
_log.debug("Output stream closed after writing " + _written);
ByteArray ba = null;
synchronized (_dataLock) {
@@ -351,7 +401,11 @@ public class MessageOutputStream extends OutputStream {
_dataCache.release(ba);
}
}
/** nonblocking close */
/**
* nonblocking close -
* Use outside of this package is deprecated, should be made package local
*/
public void closeInternal() {
_closed = true;
if (_streamError == null)
@@ -412,6 +466,8 @@ public class MessageOutputStream extends OutputStream {
if (_log.shouldLog(Log.INFO) && _valid > 0)
_log.info("flushAvailable() valid = " + _valid);
synchronized (_dataLock) {
// if valid == 0 return ??? - no, this could flush a CLOSE packet too.
// _buf may be null, but the data receiver can handle that just fine,
// deciding whether or not to send a packet
ws = target.writeData(_buf, 0, _valid);
@@ -457,14 +513,21 @@ public class MessageOutputStream extends OutputStream {
/** Define a way to detect the status of a write */
public interface WriteStatus {
/** wait until the data written either fails or succeeds */
/**
* Wait until the data written either fails or succeeds.
* Success means an ACK FROM THE FAR END.
* @param maxWaitMs -1 = forever
*/
public void waitForCompletion(int maxWaitMs);
/**
* wait until the data written is accepted into the outbound pool,
* Wait until the data written is accepted into the outbound pool,
* (i.e. the outbound window is not full)
* which we throttle rather than accept arbitrary data and queue
* @param maxWaitMs -1 = forever ?
* @param maxWaitMs -1 = forever
*/
public void waitForAccept(int maxWaitMs);
/** the write was accepted. aka did the socket not close? */
public boolean writeAccepted();
/** did the write fail? */

View File

@@ -194,7 +194,8 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
}
/**
* @param maxWaitMs MessageOutputStream is the only caller, often with -1 ??????
* Blocks until outbound window is not full. See Connection.packetSendChoke().
* @param maxWaitMs MessageOutputStream is the only caller, generally with -1
*/
public void waitForAccept(int maxWaitMs) {
if (_connection == null)
@@ -220,6 +221,7 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
+ toString());
}
/** block until the packet is acked from the far end */
public void waitForCompletion(int maxWaitMs) {
long expiration = _context.clock().now()+maxWaitMs;
while (true) {

View File

@@ -72,7 +72,6 @@
<include name="images/*.png"/>
<include name="css.css"/>
<include name="index.html"/>
<include name="WEB-INF/web-out.xml"/>
<include name="WEB-INF/classes/${project}.properties"/>
</fileset>
</war>

View File

@@ -38,6 +38,7 @@ import java.util.Properties;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.util.SecureFileOutputStream;
public class AddressbookBean
{
@@ -330,7 +331,7 @@ public class AddressbookBean
{
String filename = properties.getProperty( getBook() + "_addressbook" );
FileOutputStream fos = new FileOutputStream( ConfigBean.addressbookPrefix + filename );
FileOutputStream fos = new SecureFileOutputStream( ConfigBean.addressbookPrefix + filename );
addressbook.store( fos, null );
try {
fos.close();

View File

@@ -27,13 +27,13 @@ package i2p.susi.dns;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import net.i2p.I2PAppContext;
import net.i2p.util.SecureFileOutputStream;
public class ConfigBean implements Serializable {
@@ -111,7 +111,7 @@ public class ConfigBean implements Serializable {
{
File file = new File( configFileName );
try {
PrintWriter out = new PrintWriter( new FileOutputStream( file ) );
PrintWriter out = new PrintWriter( new SecureFileOutputStream( file ) );
out.print( config );
out.flush();
out.close();

View File

@@ -28,12 +28,13 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Properties;
import net.i2p.util.SecureFileOutputStream;
public class SubscriptionsBean
{
private String action, fileName, content, serial, lastSerial;
@@ -113,7 +114,7 @@ public class SubscriptionsBean
{
File file = new File( getFileName() );
try {
PrintWriter out = new PrintWriter( new FileOutputStream( file ) );
PrintWriter out = new PrintWriter( new SecureFileOutputStream( file ) );
out.print( content );
out.flush();
out.close();

View File

@@ -31,6 +31,7 @@ import net.i2p.util.KeyRing;
import net.i2p.util.LogManager;
//import net.i2p.util.PooledRandomSource;
import net.i2p.util.RandomSource;
import net.i2p.util.SecureDirectory;
/**
* <p>Provide a base scope for accessing singletons that I2P exposes. Rather than
@@ -218,7 +219,7 @@ public class I2PAppContext {
// config defaults to base
s = getProperty("i2p.dir.config");
if (s != null) {
_configDir = new File(s);
_configDir = new SecureDirectory(s);
if (!_configDir.exists())
_configDir.mkdir();
} else {
@@ -227,7 +228,7 @@ public class I2PAppContext {
// router defaults to config
s = getProperty("i2p.dir.router");
if (s != null) {
_routerDir = new File(s);
_routerDir = new SecureDirectory(s);
if (!_routerDir.exists())
_routerDir.mkdir();
} else {
@@ -241,7 +242,7 @@ public class I2PAppContext {
// these all default to router
s = getProperty("i2p.dir.log");
if (s != null) {
_logDir = new File(s);
_logDir = new SecureDirectory(s);
if (!_logDir.exists())
_logDir.mkdir();
} else {
@@ -249,7 +250,7 @@ public class I2PAppContext {
}
s = getProperty("i2p.dir.app");
if (s != null) {
_appDir = new File(s);
_appDir = new SecureDirectory(s);
if (!_appDir.exists())
_appDir.mkdir();
} else {
@@ -345,14 +346,14 @@ public class I2PAppContext {
String d = getProperty("i2p.dir.temp", System.getProperty("java.io.tmpdir"));
// our random() probably isn't warmed up yet
String f = "i2p-" + Math.abs((new java.util.Random()).nextInt()) + ".tmp";
_tmpDir = new File(d, f);
_tmpDir = new SecureDirectory(d, f);
if (_tmpDir.exists()) {
// good or bad ?
} else if (_tmpDir.mkdir()) {
_tmpDir.deleteOnExit();
} else {
System.err.println("Could not create temp dir " + _tmpDir.getAbsolutePath());
_tmpDir = new File(_routerDir, "tmp");
_tmpDir = new SecureDirectory(_routerDir, "tmp");
_tmpDir.mkdir();
}
}

View File

@@ -18,9 +18,9 @@ import net.i2p.util.Log;
* @author jrandom
*/
abstract class HandlerImpl implements I2CPMessageHandler {
protected Log _log;
private int _type;
protected I2PAppContext _context;
protected final Log _log;
private final int _type;
protected final I2PAppContext _context;
public HandlerImpl(I2PAppContext context, int type) {
_context = context;

View File

@@ -34,6 +34,8 @@ public interface I2PClient {
public final static String PROP_RELIABILITY_BEST_EFFORT = "BestEffort";
/** Reliability value: guaranteed */
public final static String PROP_RELIABILITY_GUARANTEED = "Guaranteed";
/** @since 0.8.1 */
public final static String PROP_RELIABILITY_NONE = "none";
/** protocol flag that must be sent when opening the i2cp connection to the router */
public final static int PROTOCOL_BYTE = 0x2A;
@@ -64,4 +66,4 @@ public interface I2PClient {
* @return newly created destination
*/
public Destination createDestination(OutputStream destKeyStream, Certificate cert) throws I2PException, IOException;
}
}

View File

@@ -33,12 +33,14 @@ import net.i2p.util.Log;
class I2PSessionImpl2 extends I2PSessionImpl {
/** set of MessageState objects, representing all of the messages in the process of being sent */
private /* FIXME final FIXME */ Set _sendingStates;
private /* FIXME final FIXME */ Set<MessageState> _sendingStates;
/** max # seconds to wait for confirmation of the message send */
private final static long SEND_TIMEOUT = 60 * 1000; // 60 seconds to send
/** should we gzip each payload prior to sending it? */
private final static boolean SHOULD_COMPRESS = true;
private final static boolean SHOULD_DECOMPRESS = true;
/** Don't expect any MSMs from the router for outbound traffic @since 0.8.1 */
private boolean _noEffort;
/** for extension */
public I2PSessionImpl2() {}
@@ -53,6 +55,8 @@ class I2PSessionImpl2 extends I2PSessionImpl {
super(ctx, destKeyStream, options);
_log = ctx.logManager().getLog(I2PSessionImpl2.class);
_sendingStates = new HashSet(32);
// default is BestEffort
_noEffort = "none".equalsIgnoreCase(options.getProperty(I2PClient.PROP_RELIABILITY));
ctx.statManager().createRateStat("i2cp.sendBestEffortTotalTime", "how long to do the full sendBestEffort call?", "i2cp", new long[] { 10*60*1000 } );
//ctx.statManager().createRateStat("i2cp.sendBestEffortStage0", "first part of sendBestEffort?", "i2cp", new long[] { 10*60*1000 } );
@@ -60,15 +64,16 @@ class I2PSessionImpl2 extends I2PSessionImpl {
//ctx.statManager().createRateStat("i2cp.sendBestEffortStage2", "third part of sendBestEffort?", "i2cp", new long[] { 10*60*1000 } );
//ctx.statManager().createRateStat("i2cp.sendBestEffortStage3", "fourth part of sendBestEffort?", "i2cp", new long[] { 10*60*1000 } );
//ctx.statManager().createRateStat("i2cp.sendBestEffortStage4", "fifth part of sendBestEffort?", "i2cp", new long[] { 10*60*1000 } );
_context.statManager().createRateStat("i2cp.receiveStatusTime.0", "How long it took to get status=0 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
_context.statManager().createRateStat("i2cp.receiveStatusTime.1", "How long it took to get status=1 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
_context.statManager().createRateStat("i2cp.receiveStatusTime.2", "How long it took to get status=2 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
_context.statManager().createRateStat("i2cp.receiveStatusTime.3", "How long it took to get status=3 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
_context.statManager().createRateStat("i2cp.receiveStatusTime.4", "How long it took to get status=4 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
_context.statManager().createRateStat("i2cp.receiveStatusTime.5", "How long it took to get status=5 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
_context.statManager().createRateStat("i2cp.receiveStatusTime", "How long it took to get any status", "i2cp", new long[] { 60*1000, 10*60*1000 });
_context.statManager().createRateStat("i2cp.tx.msgCompressed", "compressed size transferred", "i2cp", new long[] { 60*1000, 30*60*1000 });
_context.statManager().createRateStat("i2cp.tx.msgExpanded", "size before compression", "i2cp", new long[] { 60*1000, 30*60*1000 });
//_context.statManager().createRateStat("i2cp.receiveStatusTime.0", "How long it took to get status=0 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
_context.statManager().createRateStat("i2cp.receiveStatusTime.1", "How long it took to get status=1 back", "i2cp", new long[] { 10*60*1000 });
// best effort codes unused
//_context.statManager().createRateStat("i2cp.receiveStatusTime.2", "How long it took to get status=2 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
//_context.statManager().createRateStat("i2cp.receiveStatusTime.3", "How long it took to get status=3 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
_context.statManager().createRateStat("i2cp.receiveStatusTime.4", "How long it took to get status=4 back", "i2cp", new long[] { 10*60*1000 });
_context.statManager().createRateStat("i2cp.receiveStatusTime.5", "How long it took to get status=5 back", "i2cp", new long[] { 10*60*1000 });
_context.statManager().createRateStat("i2cp.receiveStatusTime", "How long it took to get any status", "i2cp", new long[] { 10*60*1000 });
_context.statManager().createRateStat("i2cp.tx.msgCompressed", "compressed size transferred", "i2cp", new long[] { 30*60*1000 });
_context.statManager().createRateStat("i2cp.tx.msgExpanded", "size before compression", "i2cp", new long[] { 30*60*1000 });
}
protected long getTimeout() {
@@ -186,7 +191,10 @@ class I2PSessionImpl2 extends I2PSessionImpl {
}
_context.statManager().addRateData("i2cp.tx.msgCompressed", compressed, 0);
_context.statManager().addRateData("i2cp.tx.msgExpanded", size, 0);
return sendBestEffort(dest, payload, keyUsed, tagsSent, expires);
if (_noEffort)
return sendNoEffort(dest, payload, expires);
else
return sendBestEffort(dest, payload, keyUsed, tagsSent, expires);
}
/**
@@ -213,6 +221,9 @@ class I2PSessionImpl2 extends I2PSessionImpl {
private static final int NUM_TAGS = 50;
/**
* TODO - Don't need to save MessageState since actuallyWait is false...
* But for now just use sendNoEffort() instead.
*
* @param keyUsed unused - no end-to-end crypto
* @param tagsSent unused - no end-to-end crypto
*/
@@ -257,7 +268,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
+ "ms left, " + oldTags + " tags known and "
+ (tag == null ? "no tag" : " a valid tag"));
}
if (false) // rekey
newKey = _context.keyGenerator().generateSessionKey();
@@ -371,6 +382,37 @@ class I2PSessionImpl2 extends I2PSessionImpl {
return found;
}
/**
* Same as sendBestEffort(), except we do not expect any MessageStatusMessage responses -
* not for accepted, or success, or failure.
* So we don't create a MessageState and save it on the _sendingStates HashSet
*
* @return true always
* @since 0.8.1
*/
protected boolean sendNoEffort(Destination dest, byte payload[], long expires)
throws I2PSessionException {
// nonce always 0
_producer.sendMessage(this, dest, 0, payload, null, null, null, null, expires);
return true;
}
/**
* Only call this with nonzero status, i.e. for outbound messages
* whose MessageState may be queued on _sendingStates.
*
* Even when using sendBestEffort(), this is a waste, because the
* MessageState is removed from _sendingStates immediately and
* so the lookup here fails.
* And iterating through the HashSet instead of having a map
* is bad too.
*
* This is now pretty much avoided since streaming now sets
* i2cp.messageReliability = none, which forces sendNoEffort() instead of sendBestEffort(),
* so the router won't send us any MSM's for outbound traffic.
*
* @param status != 0
*/
@Override
public void receiveStatus(int msgId, long nonce, int status) {
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Received status " + status + " for msgId " + msgId + " / " + nonce);
@@ -413,12 +455,13 @@ class I2PSessionImpl2 extends I2PSessionImpl {
case 1:
_context.statManager().addRateData("i2cp.receiveStatusTime.1", lifetime, 0);
break;
case 2:
_context.statManager().addRateData("i2cp.receiveStatusTime.2", lifetime, 0);
break;
case 3:
_context.statManager().addRateData("i2cp.receiveStatusTime.3", lifetime, 0);
break;
// best effort codes unused
//case 2:
// _context.statManager().addRateData("i2cp.receiveStatusTime.2", lifetime, 0);
// break;
//case 3:
// _context.statManager().addRateData("i2cp.receiveStatusTime.3", lifetime, 0);
// break;
case 4:
_context.statManager().addRateData("i2cp.receiveStatusTime.4", lifetime, 0);
break;

View File

@@ -37,13 +37,7 @@ class MessageState {
_context = ctx;
_nonce = nonce;
_prefix = prefix + "[" + _stateId + "]: ";
_id = null;
_receivedStatus = new HashSet();
_cancelled = false;
_key = null;
_newKey = null;
_tags = null;
_to = null;
_created = ctx.clock().now();
//ctx.statManager().createRateStat("i2cp.checkStatusTime", "how long it takes to go through the states", "i2cp", new long[] { 60*1000 });
}

View File

@@ -18,13 +18,13 @@ import net.i2p.util.SimpleTimer;
*
* @author zzz
*/
public class SessionIdleTimer implements SimpleTimer.TimedEvent {
class SessionIdleTimer implements SimpleTimer.TimedEvent {
public static final long MINIMUM_TIME = 5*60*1000;
private static final long DEFAULT_REDUCE_TIME = 20*60*1000;
private static final long DEFAULT_CLOSE_TIME = 30*60*1000;
private final static Log _log = new Log(SessionIdleTimer.class);
private I2PAppContext _context;
private I2PSessionImpl _session;
private final I2PAppContext _context;
private final I2PSessionImpl _session;
private boolean _reduceEnabled;
private int _reduceQuantity;
private long _reduceTime;
@@ -36,7 +36,6 @@ public class SessionIdleTimer implements SimpleTimer.TimedEvent {
/**
* reduce, shutdown, or both must be true
*/
/* FIXME Exporting non-public type through public API FIXME */
public SessionIdleTimer(I2PAppContext context, I2PSessionImpl session, boolean reduce, boolean shutdown) {
_context = context;
_session = session;

View File

@@ -66,6 +66,10 @@ public class EepGetNamingService extends NamingService {
hostname = hostname.toLowerCase();
// If you want b32, chain with HostsTxtNamingService
if (hostname.length() == 60 && hostname.endsWith(".b32.i2p"))
return null;
// check the cache
Destination d = getCache(hostname);
if (d != null)

View File

@@ -66,6 +66,10 @@ public class ExecNamingService extends NamingService {
hostname = hostname.toLowerCase();
// If you want b32, chain with HostsTxtNamingService
if (hostname.length() == 60 && hostname.endsWith(".b32.i2p"))
return null;
// check the cache
Destination d = getCache(hostname);
if (d != null)

View File

@@ -27,6 +27,7 @@ class LookupDest {
protected LookupDest(I2PAppContext context) {}
/** @param key 52 chars (do not include the .b32.i2p suffix) */
static Destination lookupBase32Hash(I2PAppContext ctx, String key) {
byte[] h = Base32.decode(key);
if (h == null)
@@ -44,6 +45,7 @@ class LookupDest {
}
****/
/** @param h 32 byte hash */
static Destination lookupHash(I2PAppContext ctx, byte[] h) {
Hash key = new Hash(h);
Destination rv = null;

View File

@@ -32,7 +32,7 @@ public abstract class NamingService {
public static final String PROP_IMPL = "i2p.naming.impl";
private static final String DEFAULT_IMPL = "net.i2p.client.naming.HostsTxtNamingService";
protected static final int CACHE_MAX_SIZE = 8;
protected static final int CACHE_MAX_SIZE = 16;
/**
@@ -107,7 +107,7 @@ public abstract class NamingService {
* The service may override the age and/or size limit
*/
/** Don't know why a dest would ever change but keep this short anyway */
protected static final long CACHE_MAX_AGE = 60*1000;
protected static final long CACHE_MAX_AGE = 7*60*1000;
private class CacheEntry {
public Destination dest;
@@ -174,4 +174,11 @@ public abstract class NamingService {
return ce.dest;
}
}
/** @since 0.8.1 */
public void clearCache() {
synchronized (_cache) {
_cache.clear();
}
}
}

View File

@@ -1,7 +1,6 @@
package net.i2p.crypto;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Cache the objects used in CryptixRijndael_Algorithm.makeKey to reduce
@@ -11,7 +10,7 @@ import java.util.List;
*
*/
public final class CryptixAESKeyCache {
private final List _availableKeys;
private final LinkedBlockingQueue<KeyCacheEntry> _availableKeys;
private static final int KEYSIZE = 32; // 256bit AES
private static final int BLOCKSIZE = 16;
@@ -22,7 +21,7 @@ public final class CryptixAESKeyCache {
private static final int MAX_KEYS = 64;
public CryptixAESKeyCache() {
_availableKeys = new ArrayList(MAX_KEYS);
_availableKeys = new LinkedBlockingQueue(MAX_KEYS);
}
/**
@@ -30,10 +29,9 @@ public final class CryptixAESKeyCache {
*
*/
public final KeyCacheEntry acquireKey() {
synchronized (_availableKeys) {
if (!_availableKeys.isEmpty())
return (KeyCacheEntry)_availableKeys.remove(0);
}
KeyCacheEntry rv = _availableKeys.poll();
if (rv != null)
return rv;
return createNew();
}
@@ -42,10 +40,7 @@ public final class CryptixAESKeyCache {
*
*/
public final void releaseKey(KeyCacheEntry key) {
synchronized (_availableKeys) {
if (_availableKeys.size() < MAX_KEYS)
_availableKeys.add(key);
}
_availableKeys.offer(key);
}
public static final KeyCacheEntry createNew() {

View File

@@ -65,35 +65,17 @@ public class DHSessionKeyBuilder {
public final static String PROP_DH_PRECALC_MIN = "crypto.dh.precalc.min";
public final static String PROP_DH_PRECALC_MAX = "crypto.dh.precalc.max";
public final static String PROP_DH_PRECALC_DELAY = "crypto.dh.precalc.delay";
public final static String DEFAULT_DH_PRECALC_MIN = "5";
public final static String DEFAULT_DH_PRECALC_MAX = "50";
public final static String DEFAULT_DH_PRECALC_DELAY = "10000";
public final static int DEFAULT_DH_PRECALC_MIN = 5;
public final static int DEFAULT_DH_PRECALC_MAX = 50;
public final static int DEFAULT_DH_PRECALC_DELAY = 10000;
static {
I2PAppContext ctx = _context;
ctx.statManager().createRateStat("crypto.dhGeneratePublicTime", "How long it takes to create x and X", "Encryption", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
ctx.statManager().createRateStat("crypto.dhCalculateSessionTime", "How long it takes to create the session key", "Encryption", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
try {
int val = Integer.parseInt(ctx.getProperty(PROP_DH_PRECALC_MIN, DEFAULT_DH_PRECALC_MIN));
MIN_NUM_BUILDERS = val;
} catch (Throwable t) {
int val = Integer.parseInt(DEFAULT_DH_PRECALC_MIN);
MIN_NUM_BUILDERS = val;
}
try {
int val = Integer.parseInt(ctx.getProperty(PROP_DH_PRECALC_MAX, DEFAULT_DH_PRECALC_MAX));
MAX_NUM_BUILDERS = val;
} catch (Throwable t) {
int val = Integer.parseInt(DEFAULT_DH_PRECALC_MAX);
MAX_NUM_BUILDERS = val;
}
try {
int val = Integer.parseInt(ctx.getProperty(PROP_DH_PRECALC_DELAY, DEFAULT_DH_PRECALC_DELAY));
CALC_DELAY = val;
} catch (Throwable t) {
int val = Integer.parseInt(DEFAULT_DH_PRECALC_DELAY);
CALC_DELAY = val;
}
MIN_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MIN, DEFAULT_DH_PRECALC_MIN);
MAX_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MAX, DEFAULT_DH_PRECALC_MAX);
CALC_DELAY = ctx.getProperty(PROP_DH_PRECALC_DELAY, DEFAULT_DH_PRECALC_DELAY);
if (_log.shouldLog(Log.DEBUG))
_log.debug("DH Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: "

View File

@@ -41,6 +41,10 @@ import net.i2p.data.SigningPublicKey;
import net.i2p.util.Log;
import net.i2p.util.NativeBigInteger;
/**
* Params and rv's changed from Hash to SHA1Hash for version 0.8.1
* There shouldn't be any external users of those variants.
*/
public class DSAEngine {
private Log _log;
private I2PAppContext _context;
@@ -61,7 +65,9 @@ public class DSAEngine {
public boolean verifySignature(Signature signature, InputStream in, SigningPublicKey verifyingKey) {
return verifySignature(signature, calculateHash(in), verifyingKey);
}
public boolean verifySignature(Signature signature, Hash hash, SigningPublicKey verifyingKey) {
/** @param hash SHA-1 hash, NOT a SHA-256 hash */
public boolean verifySignature(Signature signature, SHA1Hash hash, SigningPublicKey verifyingKey) {
long start = _context.clock().now();
try {
@@ -111,17 +117,18 @@ public class DSAEngine {
}
public Signature sign(byte data[], int offset, int length, SigningPrivateKey signingKey) {
if ((signingKey == null) || (data == null) || (data.length <= 0)) return null;
Hash h = calculateHash(data, offset, length);
SHA1Hash h = calculateHash(data, offset, length);
return sign(h, signingKey);
}
public Signature sign(InputStream in, SigningPrivateKey signingKey) {
if ((signingKey == null) || (in == null) ) return null;
Hash h = calculateHash(in);
SHA1Hash h = calculateHash(in);
return sign(h, signingKey);
}
public Signature sign(Hash hash, SigningPrivateKey signingKey) {
/** @param hash SHA-1 hash, NOT a SHA-256 hash */
public Signature sign(SHA1Hash hash, SigningPrivateKey signingKey) {
if ((signingKey == null) || (hash == null)) return null;
long start = _context.clock().now();
@@ -186,7 +193,8 @@ public class DSAEngine {
return sig;
}
public Hash calculateHash(InputStream in) {
/** @return hash SHA-1 hash, NOT a SHA-256 hash */
public SHA1Hash calculateHash(InputStream in) {
SHA1 digest = new SHA1();
byte buf[] = new byte[64];
int read = 0;
@@ -199,14 +207,15 @@ public class DSAEngine {
_log.warn("Unable to hash the stream", ioe);
return null;
}
return new Hash(digest.engineDigest());
return new SHA1Hash(digest.engineDigest());
}
public static Hash calculateHash(byte[] source, int offset, int len) {
/** @return hash SHA-1 hash, NOT a SHA-256 hash */
public static SHA1Hash calculateHash(byte[] source, int offset, int len) {
SHA1 h = new SHA1();
h.engineUpdate(source, offset, len);
byte digested[] = h.digest();
return new Hash(digested);
return new SHA1Hash(digested);
}
public static void main(String args[]) {

View File

@@ -15,16 +15,16 @@ import org.bouncycastle.crypto.macs.I2PHMac;
* in {@link org.bouncycastle.crypto.macs.I2PHMac} and
* {@link org.bouncycastle.crypto.digests.MD5Digest}.
*
* deprecated unused
*/
public class HMAC256Generator extends HMACGenerator {
public HMAC256Generator(I2PAppContext context) { super(context); }
@Override
protected I2PHMac acquire() {
synchronized (_available) {
if (!_available.isEmpty())
return (I2PHMac)_available.remove(0);
}
I2PHMac rv = _available.poll();
if (rv != null)
return rv;
// the HMAC is hardcoded to use SHA256 digest size
// for backwards compatability. next time we have a backwards
// incompatible change, we should update this by removing ", 32"
@@ -43,6 +43,7 @@ public class HMAC256Generator extends HMACGenerator {
}
/******
public static void main(String args[]) {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
byte data[] = new byte[64];
@@ -51,4 +52,5 @@ public class HMAC256Generator extends HMACGenerator {
Hash mac = ctx.hmac256().calculate(key, data);
System.out.println(Base64.encode(mac.getData()));
}
******/
}

View File

@@ -1,8 +1,7 @@
package net.i2p.crypto;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
@@ -22,14 +21,14 @@ import org.bouncycastle.crypto.macs.I2PHMac;
public class HMACGenerator {
private I2PAppContext _context;
/** set of available HMAC instances for calculate */
protected final List _available;
protected final LinkedBlockingQueue<I2PHMac> _available;
/** set of available byte[] buffers for verify */
private final List _availableTmp;
private final LinkedBlockingQueue<byte[]> _availableTmp;
public HMACGenerator(I2PAppContext context) {
_context = context;
_available = new ArrayList(32);
_availableTmp = new ArrayList(32);
_available = new LinkedBlockingQueue(32);
_availableTmp = new LinkedBlockingQueue(32);
}
/**
@@ -88,39 +87,30 @@ public class HMACGenerator {
}
protected I2PHMac acquire() {
synchronized (_available) {
if (!_available.isEmpty())
return (I2PHMac)_available.remove(0);
}
I2PHMac rv = _available.poll();
if (rv != null)
return rv;
// the HMAC is hardcoded to use SHA256 digest size
// for backwards compatability. next time we have a backwards
// incompatible change, we should update this by removing ", 32"
return new I2PHMac(new MD5Digest(), 32);
}
private void release(Mac mac) {
synchronized (_available) {
if (_available.size() < 64)
_available.add(mac);
}
private void release(I2PHMac mac) {
_available.offer(mac);
}
// temp buffers for verify(..)
private byte[] acquireTmp() {
byte rv[] = null;
synchronized (_availableTmp) {
if (!_availableTmp.isEmpty())
rv = (byte[])_availableTmp.remove(0);
}
byte rv[] = _availableTmp.poll();
if (rv != null)
Arrays.fill(rv, (byte)0x0);
else
rv = new byte[Hash.HASH_LENGTH];
return rv;
}
private void releaseTmp(byte tmp[]) {
synchronized (_availableTmp) {
if (_availableTmp.size() < 64)
_availableTmp.add((Object)tmp);
}
_availableTmp.offer(tmp);
}
}

View File

@@ -63,7 +63,7 @@ public final class SHA1 extends MessageDigest implements Cloneable {
/**
* This implementation returns a fixed-size digest.
*/
private static final int HASH_LENGTH = 20; // bytes == 160 bits
static final int HASH_LENGTH = 20; // bytes == 160 bits
/**
* Private context for incomplete blocks and padding bytes.

View File

@@ -0,0 +1,81 @@
package net.i2p.crypto;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 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.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.DataStructureImpl;
/**
* Because DSAEngine was abusing Hash for 20-byte hashes
*
* @since 0.8.1
* @author zzz
*/
public class SHA1Hash extends DataStructureImpl {
private byte[] _data;
private int _cachedHashCode;
public final static int HASH_LENGTH = SHA1.HASH_LENGTH;
/** @throws IllegalArgumentException if data is not 20 bytes (null is ok) */
public SHA1Hash(byte data[]) {
setData(data);
}
public byte[] getData() {
return _data;
}
/** @throws IllegalArgumentException if data is not 20 bytes (null is ok) */
public void setData(byte[] data) {
// FIXME DSAEngine uses a SHA-1 "Hash" as parameters and return values!
if (data != null && data.length != HASH_LENGTH)
throw new IllegalArgumentException("Hash must be 20 bytes");
_data = data;
_cachedHashCode = calcHashCode();
}
/** @throws IOException always */
public void readBytes(InputStream in) throws DataFormatException, IOException {
throw new IOException("unimplemented");
}
/** @throws IOException always */
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
throw new IOException("unimplemented");
}
@Override
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof SHA1Hash)) return false;
return DataHelper.eq(_data, ((SHA1Hash) obj)._data);
}
/** a Hash is a hash, so just use the first 4 bytes for speed */
@Override
public int hashCode() {
return _cachedHashCode;
}
/** a Hash is a hash, so just use the first 4 bytes for speed */
private int calcHashCode() {
int rv = 0;
if (_data != null) {
for (int i = 0; i < 4; i++)
rv ^= (_data[i] << (i*8));
}
return rv;
}
}

View File

@@ -102,6 +102,25 @@ iD8DBQFHphOV+h38a3n8zjMRAll+AJ9KA6WiDJcTN4qfrslSemUMr+FBrwCeM8pF
D8usM7Dxp5yrDrCYZ5AIijc=
=SrXI
-----END PGP SIGNATURE-----
*/
/*
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
*/
/* HungryHobo's key */
private static final String DEFAULT_TRUSTED_KEY4 =
"l3G6um9nB9EDLkT9cUusz5fX-GxXSWE5zaj2~V8lUL~XsGuFf8gKqzJLK" +
"NkAw0CgDIDsLRHHuUaF7ZHo5Z7HG~9JJU9Il4G2jyNYtg5S8AzG0UxkEt" +
"-JeBEqIxv5GDn6OFKr~wTI0UafJbegEWokl-8m-GPWf0vW-yPMjL7y5MI=";
/*
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
iEYEARECAAYFAkysnNIACgkQHix7YXbc3BJVfwCeNGUHaWSqZUbWN9L8VyQLpwxI
JXQAnA28vDmMMMH/WPbC5ixmJeGGNUiR
=3oMC
-----END PGP SIGNATURE-----
*/
private static final int VERSION_BYTES = 16;
@@ -148,6 +167,7 @@ D8usM7Dxp5yrDrCYZ5AIijc=
addKey(DEFAULT_TRUSTED_KEY, "jrandom@mail.i2p");
addKey(DEFAULT_TRUSTED_KEY2, "zzz@mail.i2p");
//addKey(DEFAULT_TRUSTED_KEY3, "complication@mail.i2p");
addKey(DEFAULT_TRUSTED_KEY4, "HungryHobo@mail.i2p");
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("TrustedUpdate created, trusting " + _trustedKeys.size() + " keys.");

View File

@@ -10,24 +10,22 @@ package net.i2p.crypto;
*/
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import net.i2p.I2PAppContext;
import net.i2p.util.Clock;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
import net.i2p.util.NativeBigInteger;
import net.i2p.util.RandomSource;
/**
* Precalculate the Y and K for ElGamal encryption operations.
*
* This class precalcs a set of values on its own thread, using those transparently
* when a new instance is created. By default, the minimum threshold for creating
* new values for the pool is 5, and the max pool size is 10. Whenever the pool has
* new values for the pool is 20, and the max pool size is 50. Whenever the pool has
* less than the minimum, it fills it up again to the max. There is a delay after
* each precalculation so that the CPU isn't hosed during startup (defaulting to 10 seconds).
* each precalculation so that the CPU isn't hosed during startup.
* These three parameters are controlled by java environmental variables and
* can be adjusted via:
* -Dcrypto.yk.precalc.min=40 -Dcrypto.yk.precalc.max=100 -Dcrypto.yk.precalc.delay=60000
@@ -39,51 +37,36 @@ import net.i2p.util.RandomSource;
* @author jrandom
*/
class YKGenerator {
private final static Log _log = new Log(YKGenerator.class);
private static int MIN_NUM_BUILDERS = -1;
private static int MAX_NUM_BUILDERS = -1;
private static int CALC_DELAY = -1;
/* FIXME final type if you are to syncronize FIXME */
private static volatile List _values = new ArrayList(50); // list of BigInteger[] values (y and k)
private static Thread _precalcThread = null;
//private final static Log _log = new Log(YKGenerator.class);
private static final int MIN_NUM_BUILDERS;
private static final int MAX_NUM_BUILDERS;
private static final int CALC_DELAY;
private static final LinkedBlockingQueue<BigInteger[]> _values = new LinkedBlockingQueue(50); // list of BigInteger[] values (y and k)
private static final Thread _precalcThread;
private static final I2PAppContext ctx;
public final static String PROP_YK_PRECALC_MIN = "crypto.yk.precalc.min";
public final static String PROP_YK_PRECALC_MAX = "crypto.yk.precalc.max";
public final static String PROP_YK_PRECALC_DELAY = "crypto.yk.precalc.delay";
public final static String DEFAULT_YK_PRECALC_MIN = "10";
public final static String DEFAULT_YK_PRECALC_MAX = "30";
public final static String DEFAULT_YK_PRECALC_DELAY = "10000";
public final static int DEFAULT_YK_PRECALC_MIN = 20;
public final static int DEFAULT_YK_PRECALC_MAX = 50;
public final static int DEFAULT_YK_PRECALC_DELAY = 200;
/** check every 30 seconds whether we have less than the minimum */
private final static long CHECK_DELAY = 30 * 1000;
private static long CHECK_DELAY = 30 * 1000;
static {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
try {
int val = Integer.parseInt(ctx.getProperty(PROP_YK_PRECALC_MIN, DEFAULT_YK_PRECALC_MIN));
MIN_NUM_BUILDERS = val;
} catch (Throwable t) {
int val = Integer.parseInt(DEFAULT_YK_PRECALC_MIN);
MIN_NUM_BUILDERS = val;
}
try {
int val = Integer.parseInt(ctx.getProperty(PROP_YK_PRECALC_MAX, DEFAULT_YK_PRECALC_MAX));
MAX_NUM_BUILDERS = val;
} catch (Throwable t) {
int val = Integer.parseInt(DEFAULT_YK_PRECALC_MAX);
MAX_NUM_BUILDERS = val;
}
try {
int val = Integer.parseInt(ctx.getProperty(PROP_YK_PRECALC_DELAY, DEFAULT_YK_PRECALC_DELAY));
CALC_DELAY = val;
} catch (Throwable t) {
int val = Integer.parseInt(DEFAULT_YK_PRECALC_DELAY);
CALC_DELAY = val;
}
ctx = I2PAppContext.getGlobalContext();
MIN_NUM_BUILDERS = ctx.getProperty(PROP_YK_PRECALC_MIN, DEFAULT_YK_PRECALC_MIN);
MAX_NUM_BUILDERS = ctx.getProperty(PROP_YK_PRECALC_MAX, DEFAULT_YK_PRECALC_MAX);
CALC_DELAY = ctx.getProperty(PROP_YK_PRECALC_DELAY, DEFAULT_YK_PRECALC_DELAY);
if (_log.shouldLog(Log.DEBUG))
_log.debug("ElGamal YK Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: "
+ CALC_DELAY + ")");
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("ElGamal YK Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: "
// + CALC_DELAY + ")");
ctx.statManager().createRateStat("crypto.YKUsed", "Need a YK from the queue", "Encryption", new long[] { 60*60*1000 });
ctx.statManager().createRateStat("crypto.YKEmpty", "YK queue empty", "Encryption", new long[] { 60*60*1000 });
_precalcThread = new I2PThread(new YKPrecalcRunner(MIN_NUM_BUILDERS, MAX_NUM_BUILDERS));
_precalcThread.setName("YK Precalc");
@@ -93,45 +76,36 @@ class YKGenerator {
}
private static final int getSize() {
synchronized (_values) {
return _values.size();
}
return _values.size();
}
private static final int addValues(BigInteger yk[]) {
int sz = 0;
synchronized (_values) {
_values.add(yk);
sz = _values.size();
}
return sz;
/** @return true if successful, false if full */
private static final boolean addValues(BigInteger yk[]) {
return _values.offer(yk);
}
/** @return rv[0] = Y; rv[1] = K */
public static BigInteger[] getNextYK() {
if (true) {
synchronized (_values) {
if (!_values.isEmpty()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sufficient precalculated YK values - fetch the existing");
return (BigInteger[]) _values.remove(0);
}
}
}
if (_log.shouldLog(Log.INFO)) _log.info("Insufficient precalculated YK values - create a new one");
ctx.statManager().addRateData("crypto.YKUsed", 1, 0);
BigInteger[] rv = _values.poll();
if (rv != null)
return rv;
ctx.statManager().addRateData("crypto.YKEmpty", 1, 0);
return generateYK();
}
private final static BigInteger _two = new NativeBigInteger(1, new byte[] { 0x02});
/** @return rv[0] = Y; rv[1] = K */
private static final BigInteger[] generateYK() {
NativeBigInteger k = null;
BigInteger y = null;
long t0 = 0;
long t1 = 0;
//long t0 = 0;
//long t1 = 0;
while (k == null) {
t0 = Clock.getInstance().now();
k = new NativeBigInteger(KeyGenerator.PUBKEY_EXPONENT_SIZE, RandomSource.getInstance());
t1 = Clock.getInstance().now();
//t0 = Clock.getInstance().now();
k = new NativeBigInteger(KeyGenerator.PUBKEY_EXPONENT_SIZE, ctx.random());
//t1 = Clock.getInstance().now();
if (BigInteger.ZERO.compareTo(k) == 0) {
k = null;
continue;
@@ -139,39 +113,31 @@ class YKGenerator {
BigInteger kPlus2 = k.add(_two);
if (kPlus2.compareTo(CryptoConstants.elgp) > 0) k = null;
}
long t2 = Clock.getInstance().now();
//long t2 = Clock.getInstance().now();
y = CryptoConstants.elgg.modPow(k, CryptoConstants.elgp);
BigInteger yk[] = new BigInteger[2];
yk[0] = y;
yk[1] = k;
long diff = t2 - t0;
if (diff > 1000) {
if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to generate YK value for ElGamal (" + diff + "ms)");
}
//long diff = t2 - t0;
//if (diff > 1000) {
// if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to generate YK value for ElGamal (" + diff + "ms)");
//}
return yk;
}
public static void main(String args[]) {
RandomSource.getInstance().nextBoolean(); // warm it up
try {
Thread.sleep(20 * 1000);
} catch (InterruptedException ie) { // nop
}
_log.debug("\n\n\n\nBegin test\n");
System.out.println("\n\n\n\nBegin test\n");
long negTime = 0;
for (int i = 0; i < 5; i++) {
long startNeg = Clock.getInstance().now();
getNextYK();
long endNeg = Clock.getInstance().now();
negTime += endNeg - startNeg;
}
_log.debug("YK fetch time for 5 runs: " + negTime + " @ " + negTime / 5l + "ms each");
try {
Thread.sleep(30 * 1000);
} catch (InterruptedException ie) { // nop
}
System.out.println("YK fetch time for 5 runs: " + negTime + " @ " + negTime / 5l + "ms each");
}
private static class YKPrecalcRunner implements Runnable {
@@ -186,15 +152,21 @@ class YKGenerator {
public void run() {
while (true) {
int curSize = 0;
long start = Clock.getInstance().now();
//long start = Clock.getInstance().now();
int startSize = getSize();
// Adjust delay
if (startSize <= (_minSize / 2) && CHECK_DELAY > 1000)
CHECK_DELAY -= 1000;
else if (startSize > (_minSize * 2) && CHECK_DELAY < 60000)
CHECK_DELAY += 1000;
curSize = startSize;
while (curSize < _minSize) {
while (curSize < _maxSize) {
long begin = Clock.getInstance().now();
curSize = addValues(generateYK());
long end = Clock.getInstance().now();
if (_log.shouldLog(Log.DEBUG)) _log.debug("Precalculated YK value in " + (end - begin) + "ms");
if (curSize < _minSize) {
for (int i = curSize; i < _maxSize; i++) {
//long begin = Clock.getInstance().now();
if (!addValues(generateYK()))
break;
//long end = Clock.getInstance().now();
//if (_log.shouldLog(Log.DEBUG)) _log.debug("Precalculated YK value in " + (end - begin) + "ms");
// for some relief...
try {
Thread.sleep(CALC_DELAY);
@@ -202,14 +174,14 @@ class YKGenerator {
}
}
}
long end = Clock.getInstance().now();
int numCalc = curSize - startSize;
if (numCalc > 0) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Precalced " + numCalc + " to " + curSize + " in "
+ (end - start - CALC_DELAY * numCalc) + "ms (not counting "
+ (CALC_DELAY * numCalc) + "ms relief). now sleeping");
}
//long end = Clock.getInstance().now();
//int numCalc = curSize - startSize;
//if (numCalc > 0) {
// if (_log.shouldLog(Log.DEBUG))
// _log.debug("Precalced " + numCalc + " to " + curSize + " in "
// + (end - start - CALC_DELAY * numCalc) + "ms (not counting "
// + (CALC_DELAY * numCalc) + "ms relief). now sleeping");
//}
try {
Thread.sleep(CHECK_DELAY);
} catch (InterruptedException ie) { // nop
@@ -217,4 +189,4 @@ class YKGenerator {
}
}
}
}
}

View File

@@ -17,7 +17,6 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -42,6 +41,7 @@ import net.i2p.util.ByteCache;
import net.i2p.util.OrderedProperties;
import net.i2p.util.ReusableGZIPInputStream;
import net.i2p.util.ReusableGZIPOutputStream;
import net.i2p.util.SecureFileOutputStream;
/**
* Defines some simple IO routines for dealing with marshalling data structures
@@ -339,11 +339,12 @@ public class DataHelper {
/**
* Writes the props to the file, unsorted (unless props is an OrderedProperties)
* Note that this does not escape the \r or \n that are unescaped in loadProps() above.
* As of 0.8.1, file will be mode 600.
*/
public static void storeProps(Properties props, File file) throws IOException {
PrintWriter out = null;
try {
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8")));
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(file), "UTF-8")));
out.println("# NOTE: This I2P config file must use UTF-8 encoding");
for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();

View File

@@ -31,6 +31,7 @@ public class Hash extends DataStructureImpl {
public Hash() {
}
/** @throws IllegalArgumentException if data is not 32 bytes (null is ok) */
public Hash(byte data[]) {
setData(data);
}
@@ -39,7 +40,10 @@ public class Hash extends DataStructureImpl {
return _data;
}
/** @throws IllegalArgumentException if data is not 32 bytes (null is ok) */
public void setData(byte[] data) {
if (data != null && data.length != HASH_LENGTH)
throw new IllegalArgumentException("Hash must be 32 bytes");
_data = data;
_stringified = null;
_base64ed = null;

View File

@@ -14,8 +14,11 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import net.i2p.util.OrderedProperties;
/**
* Defines a method of communicating with a router
*
@@ -28,7 +31,7 @@ public class RouterAddress extends DataStructureImpl {
private Properties _options;
public RouterAddress() {
setCost(-1);
_cost = -1;
}
/**
@@ -134,18 +137,27 @@ public class RouterAddress extends DataStructureImpl {
return DataHelper.hashCode(_transportStyle);
}
/**
* This is used on peers.jsp so sort options so it looks better.
* We don't just use OrderedProperties for _options because DataHelper.writeProperties()
* sorts also.
*/
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
StringBuilder buf = new StringBuilder(128);
buf.append("[RouterAddress: ");
buf.append("\n\tTransportStyle: ").append(getTransportStyle());
buf.append("\n\tCost: ").append(getCost());
buf.append("\n\tExpiration: ").append(getExpiration());
buf.append("\n\tOptions: #: ").append(getOptions().size());
for (Iterator iter = getOptions().keySet().iterator(); iter.hasNext();) {
String key = (String) iter.next();
String val = getOptions().getProperty(key);
buf.append("\n\t\t[").append(key).append("] = [").append(val).append("]");
buf.append("\n\tTransportStyle: ").append(_transportStyle);
buf.append("\n\tCost: ").append(_cost);
buf.append("\n\tExpiration: ").append(_expiration);
if (_options != null) {
buf.append("\n\tOptions: #: ").append(_options.size());
Properties p = new OrderedProperties();
p.putAll(_options);
for (Map.Entry e : p.entrySet()) {
String key = (String) e.getKey();
String val = (String) e.getValue();
buf.append("\n\t\t[").append(key).append("] = [").append(val).append("]");
}
}
buf.append("]");
return buf.toString();

View File

@@ -49,16 +49,16 @@ public class AbuseReason extends DataStructureImpl {
@Override
public boolean equals(Object object) {
if ((object == null) || !(object instanceof AbuseReason)) return false;
return DataHelper.eq(getReason(), ((AbuseReason) object).getReason());
return DataHelper.eq(_reason, ((AbuseReason) object).getReason());
}
@Override
public int hashCode() {
return DataHelper.hashCode(getReason());
return DataHelper.hashCode(_reason);
}
@Override
public String toString() {
return "[AbuseReason: " + getReason() + "]";
return "[AbuseReason: " + _reason + "]";
}
}

View File

@@ -28,7 +28,7 @@ public class AbuseSeverity extends DataStructureImpl {
private int _severityId;
public AbuseSeverity() {
setSeverity(-1);
_severityId = -1;
}
public int getSeverity() {
@@ -56,11 +56,11 @@ public class AbuseSeverity extends DataStructureImpl {
@Override
public int hashCode() {
return getSeverity();
return _severityId;
}
@Override
public String toString() {
return "[AbuseSeverity: " + getSeverity() + "]";
return "[AbuseSeverity: " + _severityId + "]";
}
}

View File

@@ -27,11 +27,11 @@ public class CreateSessionMessage extends I2CPMessageImpl {
private SessionConfig _sessionConfig;
public CreateSessionMessage(SessionConfig config) {
setSessionConfig(config);
_sessionConfig = config;
}
public CreateSessionMessage() {
setSessionConfig(new SessionConfig());
_sessionConfig = new SessionConfig();
}
public SessionConfig getSessionConfig() {
@@ -75,7 +75,7 @@ public class CreateSessionMessage extends I2CPMessageImpl {
public boolean equals(Object object) {
if ((object != null) && (object instanceof CreateSessionMessage)) {
CreateSessionMessage msg = (CreateSessionMessage) object;
return DataHelper.eq(getSessionConfig(), msg.getSessionConfig());
return DataHelper.eq(_sessionConfig, msg.getSessionConfig());
}
return false;
@@ -85,7 +85,7 @@ public class CreateSessionMessage extends I2CPMessageImpl {
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("[CreateSessionMessage: ");
buf.append("\n\tConfig: ").append(getSessionConfig());
buf.append("\n\tConfig: ").append(_sessionConfig);
buf.append("]");
return buf.toString();
}

View File

@@ -69,7 +69,7 @@ public class DestroySessionMessage extends I2CPMessageImpl {
public boolean equals(Object object) {
if ((object != null) && (object instanceof DestroySessionMessage)) {
DestroySessionMessage msg = (DestroySessionMessage) object;
return DataHelper.eq(getSessionId(), msg.getSessionId());
return DataHelper.eq(_sessionId, msg.getSessionId());
}
return false;
@@ -86,7 +86,7 @@ public class DestroySessionMessage extends I2CPMessageImpl {
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("[DestroySessionMessage: ");
buf.append("\n\tSessionId: ").append(getSessionId());
buf.append("\n\tSessionId: ").append(_sessionId);
buf.append("]");
return buf.toString();
}

View File

@@ -41,7 +41,7 @@ public class I2CPMessageHandler {
try {
if (length < 0) throw new I2CPMessageException("Invalid message length specified");
int type = (int) DataHelper.readLong(in, 1);
I2CPMessage msg = createMessage(in, length, type);
I2CPMessage msg = createMessage(type);
msg.readMessage(in, length, type);
return msg;
} catch (DataFormatException dfe) {
@@ -53,8 +53,8 @@ public class I2CPMessageHandler {
* Yes, this is fairly ugly, but its the only place it ever happens.
*
*/
private static I2CPMessage createMessage(InputStream in, int length, int type) throws IOException,
I2CPMessageException {
private static I2CPMessage createMessage(int type) throws IOException,
I2CPMessageException {
switch (type) {
case CreateLeaseSetMessage.MESSAGE_TYPE:
return new CreateLeaseSetMessage();
@@ -101,6 +101,7 @@ public class I2CPMessageHandler {
}
}
/***
public static void main(String args[]) {
try {
I2CPMessage msg = readMessage(new FileInputStream(args[0]));
@@ -109,4 +110,5 @@ public class I2CPMessageHandler {
e.printStackTrace();
}
}
***/
}

View File

@@ -27,10 +27,10 @@ public class MessageId extends DataStructureImpl {
private long _messageId;
public MessageId() {
setMessageId(-1);
_messageId = -1;
}
public MessageId(long id) {
setMessageId(id);
_messageId = id;
}
public long getMessageId() {
@@ -58,11 +58,11 @@ public class MessageId extends DataStructureImpl {
@Override
public int hashCode() {
return (int)getMessageId();
return (int)_messageId;
}
@Override
public String toString() {
return "[MessageId: " + getMessageId() + "]";
return "[MessageId: " + _messageId + "]";
}
}

View File

@@ -29,8 +29,8 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
private Payload _payload;
public MessagePayloadMessage() {
setSessionId(-1);
setMessageId(-1);
_sessionId = -1;
_messageId = -1;
}
public long getSessionId() {
@@ -113,7 +113,7 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
MessagePayloadMessage msg = (MessagePayloadMessage) object;
return _sessionId == msg.getSessionId()
&& _messageId == msg.getMessageId()
&& DataHelper.eq(getPayload(), msg.getPayload());
&& DataHelper.eq(_payload, msg.getPayload());
}
return false;
@@ -123,9 +123,9 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("[MessagePayloadMessage: ");
buf.append("\n\tSessionId: ").append(getSessionId());
buf.append("\n\tMessageId: ").append(getMessageId());
buf.append("\n\tPayload: ").append(getPayload());
buf.append("\n\tSessionId: ").append(_sessionId);
buf.append("\n\tMessageId: ").append(_messageId);
buf.append("\n\tPayload: ").append(_payload);
buf.append("]");
return buf.toString();
}

View File

@@ -32,17 +32,19 @@ public class MessageStatusMessage extends I2CPMessageImpl {
public final static int STATUS_AVAILABLE = 0;
public final static int STATUS_SEND_ACCEPTED = 1;
/** unused */
public final static int STATUS_SEND_BEST_EFFORT_SUCCESS = 2;
/** unused */
public final static int STATUS_SEND_BEST_EFFORT_FAILURE = 3;
public final static int STATUS_SEND_GUARANTEED_SUCCESS = 4;
public final static int STATUS_SEND_GUARANTEED_FAILURE = 5;
public MessageStatusMessage() {
setSessionId(-1);
setStatus(-1);
setMessageId(-1);
setSize(-1);
setNonce(-1);
_sessionId = -1;
_status = -1;
_messageId = -1;
_size = -1;
_nonce = -1;
}
public long getSessionId() {
@@ -169,11 +171,11 @@ public class MessageStatusMessage extends I2CPMessageImpl {
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("[MessageStatusMessage: ");
buf.append("\n\tSessionId: ").append(getSessionId());
buf.append("\n\tNonce: ").append(getNonce());
buf.append("\n\tMessageId: ").append(getMessageId());
buf.append("\n\tStatus: ").append(getStatusString(getStatus()));
buf.append("\n\tSize: ").append(getSize());
buf.append("\n\tSessionId: ").append(_sessionId);
buf.append("\n\tNonce: ").append(_nonce);
buf.append("\n\tMessageId: ").append(_messageId);
buf.append("\n\tStatus: ").append(getStatusString(_status));
buf.append("\n\tSize: ").append(_size);
buf.append("]");
return buf.toString();
}

View File

@@ -28,8 +28,8 @@ public class ReceiveMessageBeginMessage extends I2CPMessageImpl {
private long _messageId;
public ReceiveMessageBeginMessage() {
setSessionId(-1);
setMessageId(-1);
_sessionId = -1;
_messageId = -1;
}
public long getSessionId() {
@@ -103,8 +103,8 @@ public class ReceiveMessageBeginMessage extends I2CPMessageImpl {
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("[ReceiveMessageBeginMessage: ");
buf.append("\n\tSessionId: ").append(getSessionId());
buf.append("\n\tMessageId: ").append(getMessageId());
buf.append("\n\tSessionId: ").append(_sessionId);
buf.append("\n\tMessageId: ").append(_messageId);
buf.append("]");
return buf.toString();
}

View File

@@ -27,8 +27,8 @@ public class ReceiveMessageEndMessage extends I2CPMessageImpl {
private long _messageId;
public ReceiveMessageEndMessage() {
setSessionId(-1);
setMessageId(-1);
_sessionId = -1;
_messageId = -1;
}
public long getSessionId() {
@@ -87,8 +87,8 @@ public class ReceiveMessageEndMessage extends I2CPMessageImpl {
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("[ReceiveMessageEndMessage: ");
buf.append("\n\tSessionId: ").append(getSessionId());
buf.append("\n\tMessageId: ").append(getMessageId());
buf.append("\n\tSessionId: ").append(_sessionId);
buf.append("\n\tMessageId: ").append(_messageId);
buf.append("]");
return buf.toString();
}

View File

@@ -81,8 +81,8 @@ public class ReconfigureSessionMessage extends I2CPMessageImpl {
public boolean equals(Object object) {
if ((object != null) && (object instanceof ReconfigureSessionMessage)) {
ReconfigureSessionMessage msg = (ReconfigureSessionMessage) object;
return DataHelper.eq(getSessionId(), msg.getSessionId())
&& DataHelper.eq(getSessionConfig(), msg.getSessionConfig());
return DataHelper.eq(_sessionId, msg.getSessionId())
&& DataHelper.eq(_sessionConfig, msg.getSessionConfig());
}
return false;
@@ -92,8 +92,8 @@ public class ReconfigureSessionMessage extends I2CPMessageImpl {
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("[ReconfigureSessionMessage: ");
buf.append("\n\tSessionId: ").append(getSessionId());
buf.append("\n\tSessionConfig: ").append(getSessionConfig());
buf.append("\n\tSessionId: ").append(_sessionId);
buf.append("\n\tSessionConfig: ").append(_sessionConfig);
buf.append("]");
return buf.toString();
}

View File

@@ -30,7 +30,7 @@ import net.i2p.data.TunnelId;
public class RequestLeaseSetMessage extends I2CPMessageImpl {
public final static int MESSAGE_TYPE = 21;
private SessionId _sessionId;
private List _endpoints;
private List<TunnelEndpoint> _endpoints;
private Date _end;
public RequestLeaseSetMessage() {
@@ -51,12 +51,12 @@ public class RequestLeaseSetMessage extends I2CPMessageImpl {
public Hash getRouter(int endpoint) {
if ((endpoint < 0) || (_endpoints.size() < endpoint)) return null;
return ((TunnelEndpoint) _endpoints.get(endpoint)).getRouter();
return _endpoints.get(endpoint).getRouter();
}
public TunnelId getTunnelId(int endpoint) {
if ((endpoint < 0) || (_endpoints.size() < endpoint)) return null;
return ((TunnelEndpoint) _endpoints.get(endpoint)).getTunnelId();
return _endpoints.get(endpoint).getTunnelId();
}
/** @deprecated unused - presumably he meant remove? */
@@ -159,8 +159,6 @@ public class RequestLeaseSetMessage extends I2CPMessageImpl {
private TunnelId _tunnelId;
public TunnelEndpoint() {
_router = null;
_tunnelId = null;
}
public TunnelEndpoint(Hash router, TunnelId id) {

View File

@@ -168,8 +168,8 @@ public class SessionConfig extends DataStructureImpl {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
_log.debug("PubKey size for destination: " + _destination.getPublicKey().getData().length);
_log.debug("SigningKey size for destination: " + _destination.getSigningPublicKey().getData().length);
//_log.debug("PubKey size for destination: " + _destination.getPublicKey().getData().length);
//_log.debug("SigningKey size for destination: " + _destination.getSigningPublicKey().getData().length);
_destination.writeBytes(out);
DataHelper.writeProperties(out, _options, true); // UTF-8
DataHelper.writeDate(out, _creationDate);

View File

@@ -26,7 +26,7 @@ public class SessionId extends DataStructureImpl {
private int _sessionId;
public SessionId() {
setSessionId(-1);
_sessionId = -1;
}
public int getSessionId() {
@@ -49,16 +49,16 @@ public class SessionId extends DataStructureImpl {
@Override
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof SessionId)) return false;
return getSessionId() == ((SessionId) obj).getSessionId();
return _sessionId == ((SessionId) obj).getSessionId();
}
@Override
public int hashCode() {
return getSessionId();
return _sessionId;
}
@Override
public String toString() {
return "[SessionId: " + getSessionId() + "]";
return "[SessionId: " + _sessionId + "]";
}
}

View File

@@ -28,7 +28,7 @@ public class SetDateMessage extends I2CPMessageImpl {
public SetDateMessage() {
super();
setDate(new Date(Clock.getInstance().now()));
_date = new Date(Clock.getInstance().now());
}
public Date getDate() {
@@ -70,7 +70,7 @@ public class SetDateMessage extends I2CPMessageImpl {
public boolean equals(Object object) {
if ((object != null) && (object instanceof SetDateMessage)) {
SetDateMessage msg = (SetDateMessage) object;
return DataHelper.eq(getDate(), msg.getDate());
return DataHelper.eq(_date, msg.getDate());
}
return false;
@@ -80,7 +80,7 @@ public class SetDateMessage extends I2CPMessageImpl {
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("[SetDateMessage");
buf.append("\n\tDate: ").append(getDate());
buf.append("\n\tDate: ").append(_date);
buf.append("]");
return buf.toString();
}

View File

@@ -55,11 +55,11 @@ public class EepGet {
protected long _bytesTransferred;
protected long _bytesRemaining;
protected int _currentAttempt;
private String _etag;
private String _lastModified;
protected String _etag;
protected String _lastModified;
protected boolean _encodingChunked;
protected boolean _notModified;
private String _contentType;
protected String _contentType;
protected boolean _transferFailed;
protected boolean _headersRead;
protected boolean _aborted;
@@ -537,6 +537,15 @@ public class EepGet {
if (_redirects > 5)
throw new IOException("Too many redirects: to " + _redirectLocation);
if (_log.shouldLog(Log.INFO)) _log.info("Redirecting to " + _redirectLocation);
// reset some important variables, we don't want to save the values from the redirect
_bytesRemaining = -1;
_redirectLocation = null;
_etag = null;
_lastModified = null;
_contentType = null;
_encodingChunked = false;
sendRequest(timeout);
doFetch(timeout);
return;

View File

@@ -116,6 +116,7 @@ public class EepHead extends EepGet {
else
timeout.setInactivityTimeout(60*1000);
// Should we even follow redirects for HEAD?
if (_redirectLocation != null) {
//try {
URL oldURL = new URL(_actualURL);
@@ -143,6 +144,15 @@ public class EepHead extends EepGet {
if (_redirects > 5)
throw new IOException("Too many redirects: to " + _redirectLocation);
if (_log.shouldLog(Log.INFO)) _log.info("Redirecting to " + _redirectLocation);
// reset some important variables, we don't want to save the values from the redirect
_bytesRemaining = -1;
_redirectLocation = null;
_etag = null;
_lastModified = null;
_contentType = null;
_encodingChunked = false;
sendRequest(timeout);
doFetch(timeout);
return;

View File

@@ -11,8 +11,8 @@ import net.i2p.I2PAppContext;
*/
public class LogConsoleBuffer {
private I2PAppContext _context;
private final List _buffer;
private final List _critBuffer;
private final List<String> _buffer;
private final List<String> _critBuffer;
public LogConsoleBuffer(I2PAppContext context) {
_context = context;
@@ -43,7 +43,7 @@ public class LogConsoleBuffer {
* in the logs)
*
*/
public List getMostRecentMessages() {
public List<String> getMostRecentMessages() {
synchronized (_buffer) {
return new ArrayList(_buffer);
}
@@ -54,9 +54,9 @@ public class LogConsoleBuffer {
* in the logs)
*
*/
public List getMostRecentCriticalMessages() {
public List<String> getMostRecentCriticalMessages() {
synchronized (_critBuffer) {
return new ArrayList(_critBuffer);
}
}
}
}

View File

@@ -156,7 +156,7 @@ public class LogManager {
return rv;
}
/** @deprecated unused */
/** now used by ConfigLogingHelper */
public List<Log> getLogs() {
return new ArrayList(_logs.values());
}
@@ -415,15 +415,21 @@ public class LogManager {
/**
* Determine how many bytes are in the given formatted string (5m, 60g, 100k, etc)
*
* Size may be k, m, or g; a trailing b is ignored. Upper-case is allowed.
* Spaces between the number and letter is are allowed.
* The number may be in floating point.
* 4096 min, 2 GB max (returns int)
*/
public int getFileSize(String size) {
int sz = -1;
public static int getFileSize(String size) {
try {
String v = size;
char mod = size.toUpperCase().charAt(size.length() - 1);
if (!Character.isDigit(mod)) v = size.substring(0, size.length() - 1);
int val = Integer.parseInt(v);
String v = size.trim().toUpperCase();
if (v.length() < 2)
return -1;
if (v.endsWith("B"))
v = v.substring(0, v.length() - 1);
char mod = v.charAt(v.length() - 1);
if (!Character.isDigit(mod)) v = v.substring(0, v.length() - 1);
double val = Double.parseDouble(v);
switch (mod) {
case 'K':
val *= 1024;
@@ -438,10 +444,11 @@ public class LogManager {
// blah, noop
break;
}
return val;
if (val < 4096 || val > Integer.MAX_VALUE)
return -1;
return (int) val;
} catch (Throwable t) {
System.err.println("Error parsing config for filesize: [" + size + "]");
t.printStackTrace();
return -1;
}
}

View File

@@ -11,7 +11,6 @@ package net.i2p.util;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
@@ -158,7 +157,8 @@ class LogWriter implements Runnable {
File parent = f.getParentFile();
if (parent != null) {
if (!parent.exists()) {
boolean ok = parent.mkdirs();
File sd = new SecureDirectory(parent.getAbsolutePath());
boolean ok = sd.mkdirs();
if (!ok) {
System.err.println("Unable to create the parent directory: " + parent.getAbsolutePath());
//System.exit(0);
@@ -171,7 +171,7 @@ class LogWriter implements Runnable {
}
closeFile();
try {
_currentOut = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f), "UTF-8"));
_currentOut = new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(f), "UTF-8"));
} catch (IOException ioe) {
System.err.println("Error rotating into [" + f.getAbsolutePath() + "]" + ioe);
}

View File

@@ -145,7 +145,7 @@ public class RandomSource extends SecureRandom implements EntropyHarvester {
File f = new File(I2PAppContext.getGlobalContext().getConfigDir(), SEEDFILE);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(f);
fos = new SecureFileOutputStream(f);
fos.write(buf);
} catch (IOException ioe) {
// ignore

View File

@@ -0,0 +1,74 @@
package net.i2p.util;
import java.io.File;
/**
* Same as File but sets the file mode after mkdir() so it can
* be read and written by the owner only (i.e. 700 on linux)
*
* @since 0.8.1
* @author zzz
*/
public class SecureDirectory extends File {
private static final boolean canSetPerms =
(new VersionComparator()).compare(System.getProperty("java.version"), "1.6") >= 0;
private static final boolean isNotWindows = !System.getProperty("os.name").startsWith("Win");
public SecureDirectory(String pathname) {
super(pathname);
}
public SecureDirectory(String parent, String child) {
super(parent, child);
}
public SecureDirectory(File parent, String child) {
super(parent, child);
}
/**
* Sets directory to mode 700 if the directory is created
*/
@Override
public boolean mkdir() {
boolean rv = super.mkdir();
if (rv)
setPerms();
return rv;
}
/**
* Sets directory to mode 700 if the directory is created
* Does NOT change the mode of other created directories
*/
@Override
public boolean mkdirs() {
boolean rv = super.mkdirs();
if (rv)
setPerms();
return rv;
}
/**
* Tries to set the permissions to 700,
* ignores errors
*/
private void setPerms() {
if (!canSetPerms)
return;
try {
setReadable(false, false);
setReadable(true, true);
setWritable(false, false);
setWritable(true, true);
if (isNotWindows) {
setExecutable(false, false);
setExecutable(true, true);
}
} catch (Throwable t) {
// NoSuchMethodException or NoSuchMethodError if we somehow got the
// version detection wrong or the JVM doesn't support it
}
}
}

View File

@@ -0,0 +1,72 @@
package net.i2p.util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/**
* Same as FileOutputStream but sets the file mode so it can only
* be read and written by the owner only (i.e. 600 on linux)
*
* @since 0.8.1
* @author zzz
*/
public class SecureFileOutputStream extends FileOutputStream {
private static final boolean canSetPerms =
(new VersionComparator()).compare(System.getProperty("java.version"), "1.6") >= 0;
/**
* Sets output file to mode 600
*/
public SecureFileOutputStream(String file) throws FileNotFoundException {
super(file);
setPerms(new File(file));
}
/**
* Sets output file to mode 600 only if append = false
* (otherwise it is presumed to be 600 already)
*/
public SecureFileOutputStream(String file, boolean append) throws FileNotFoundException {
super(file, append);
//if (!append)
setPerms(new File(file));
}
/**
* Sets output file to mode 600
*/
public SecureFileOutputStream(File file) throws FileNotFoundException {
super(file);
setPerms(file);
}
/**
* Sets output file to mode 600 only if append = false
* (otherwise it is presumed to be 600 already)
*/
public SecureFileOutputStream(File file, boolean append) throws FileNotFoundException {
super(file, append);
//if (!append)
setPerms(file);
}
/**
* Tries to set the permissions to 600,
* ignores errors
*/
private static void setPerms(File f) {
if (!canSetPerms)
return;
try {
f.setReadable(false, false);
f.setReadable(true, true);
f.setWritable(false, false);
f.setWritable(true, true);
} catch (Throwable t) {
// NoSuchMethodException or NoSuchMethodError if we somehow got the
// version detection wrong or the JVM doesn't support it
}
}
}

View File

@@ -1,3 +1,6 @@
2010-10-10 sponge
* Fulfill Request for name resolving in BOB
2010-09-15 dr|z3d
* I2PSnark: Overhaul UI, implement new theme.

View File

@@ -25,7 +25,7 @@ tunnel.0.startOnLoad=true
# irc
tunnel.1.name=IRC Proxy
tunnel.1.description=IRC proxy to access the anonymous IRC network
tunnel.1.description=IRC proxy to access anonymous IRC servers
tunnel.1.type=ircclient
tunnel.1.sharedClient=false
tunnel.1.interface=127.0.0.1

View File

@@ -1,3 +1,8 @@
To start I2P, run:
$INSTALL_PATH/i2prouter start
On non-x86, run:
$INSTALL_PATH/runplain.sh

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