2005-10-19 22:02:37 +00:00
|
|
|
package org.klomp.snark;
|
|
|
|
|
2008-07-16 13:42:54 +00:00
|
|
|
import java.io.File;
|
|
|
|
import java.io.IOException;
|
2008-11-15 23:52:40 +00:00
|
|
|
import java.util.ArrayList;
|
2008-07-16 13:42:54 +00:00
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.Iterator;
|
2008-11-15 23:52:40 +00:00
|
|
|
import java.util.List;
|
2008-07-16 13:42:54 +00:00
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Properties;
|
|
|
|
import java.util.Set;
|
2008-11-15 23:52:40 +00:00
|
|
|
import java.util.StringTokenizer;
|
2008-07-16 13:42:54 +00:00
|
|
|
|
2005-10-19 22:02:37 +00:00
|
|
|
import net.i2p.I2PAppContext;
|
|
|
|
import net.i2p.I2PException;
|
2005-12-16 23:18:56 +00:00
|
|
|
import net.i2p.client.I2PSession;
|
2005-10-19 22:02:37 +00:00
|
|
|
import net.i2p.client.streaming.I2PServerSocket;
|
|
|
|
import net.i2p.client.streaming.I2PSocket;
|
|
|
|
import net.i2p.client.streaming.I2PSocketManager;
|
|
|
|
import net.i2p.client.streaming.I2PSocketManagerFactory;
|
2008-07-16 13:42:54 +00:00
|
|
|
import net.i2p.data.DataFormatException;
|
|
|
|
import net.i2p.data.Destination;
|
|
|
|
import net.i2p.data.Hash;
|
|
|
|
import net.i2p.util.EepGet;
|
2005-10-19 22:02:37 +00:00
|
|
|
import net.i2p.util.Log;
|
2006-03-05 17:07:07 +00:00
|
|
|
import net.i2p.util.SimpleTimer;
|
2005-10-19 22:02:37 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* I2P specific helpers for I2PSnark
|
|
|
|
*/
|
|
|
|
public class I2PSnarkUtil {
|
|
|
|
private I2PAppContext _context;
|
|
|
|
private Log _log;
|
|
|
|
private static I2PSnarkUtil _instance = new I2PSnarkUtil();
|
|
|
|
public static I2PSnarkUtil instance() { return _instance; }
|
|
|
|
|
|
|
|
private boolean _shouldProxy;
|
|
|
|
private String _proxyHost;
|
|
|
|
private int _proxyPort;
|
|
|
|
private String _i2cpHost;
|
|
|
|
private int _i2cpPort;
|
2005-12-16 03:00:48 +00:00
|
|
|
private Map _opts;
|
2005-10-19 22:02:37 +00:00
|
|
|
private I2PSocketManager _manager;
|
2005-12-16 03:00:48 +00:00
|
|
|
private boolean _configured;
|
2006-03-05 17:07:07 +00:00
|
|
|
private Set _shitlist;
|
2007-03-14 04:06:27 +00:00
|
|
|
private int _maxUploaders;
|
2008-04-07 17:41:58 +00:00
|
|
|
private int _maxUpBW;
|
2005-10-19 22:02:37 +00:00
|
|
|
|
2008-11-15 23:52:40 +00:00
|
|
|
public static final String PROP_USE_OPENTRACKERS = "i2psnark.useOpentrackers";
|
|
|
|
public static final boolean DEFAULT_USE_OPENTRACKERS = true;
|
|
|
|
public static final String PROP_OPENTRACKERS = "i2psnark.opentrackers";
|
|
|
|
public static final String DEFAULT_OPENTRACKERS = "http://tracker.welterde.i2p/a";
|
|
|
|
public static final int DEFAULT_MAX_UP_BW = 8; //KBps
|
|
|
|
|
2005-10-19 22:02:37 +00:00
|
|
|
private I2PSnarkUtil() {
|
|
|
|
_context = I2PAppContext.getGlobalContext();
|
|
|
|
_log = _context.logManager().getLog(Snark.class);
|
2005-12-16 03:00:48 +00:00
|
|
|
_opts = new HashMap();
|
2005-10-19 22:02:37 +00:00
|
|
|
setProxy("127.0.0.1", 4444);
|
|
|
|
setI2CPConfig("127.0.0.1", 7654, null);
|
2006-03-05 17:07:07 +00:00
|
|
|
_shitlist = new HashSet(64);
|
2005-12-16 03:00:48 +00:00
|
|
|
_configured = false;
|
2007-03-14 04:06:27 +00:00
|
|
|
_maxUploaders = Snark.MAX_TOTAL_UPLOADERS;
|
2008-11-15 23:52:40 +00:00
|
|
|
_maxUpBW = DEFAULT_MAX_UP_BW;
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Specify what HTTP proxy tracker requests should go through (specify a null
|
|
|
|
* host for no proxying)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public void setProxy(String host, int port) {
|
|
|
|
if ( (host != null) && (port > 0) ) {
|
|
|
|
_shouldProxy = true;
|
|
|
|
_proxyHost = host;
|
|
|
|
_proxyPort = port;
|
|
|
|
} else {
|
|
|
|
_shouldProxy = false;
|
|
|
|
_proxyHost = null;
|
|
|
|
_proxyPort = -1;
|
|
|
|
}
|
2005-12-16 03:00:48 +00:00
|
|
|
_configured = true;
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
|
2005-12-16 03:00:48 +00:00
|
|
|
public boolean configured() { return _configured; }
|
|
|
|
|
|
|
|
public void setI2CPConfig(String i2cpHost, int i2cpPort, Map opts) {
|
2005-10-19 22:02:37 +00:00
|
|
|
_i2cpHost = i2cpHost;
|
|
|
|
_i2cpPort = i2cpPort;
|
|
|
|
if (opts != null)
|
2005-12-16 03:00:48 +00:00
|
|
|
_opts.putAll(opts);
|
|
|
|
_configured = true;
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
|
2007-03-14 04:06:27 +00:00
|
|
|
public void setMaxUploaders(int limit) {
|
|
|
|
_maxUploaders = limit;
|
|
|
|
_configured = true;
|
|
|
|
}
|
|
|
|
|
2008-04-07 17:41:58 +00:00
|
|
|
public void setMaxUpBW(int limit) {
|
|
|
|
_maxUpBW = limit;
|
|
|
|
_configured = true;
|
|
|
|
}
|
|
|
|
|
2005-12-16 03:00:48 +00:00
|
|
|
public String getI2CPHost() { return _i2cpHost; }
|
|
|
|
public int getI2CPPort() { return _i2cpPort; }
|
|
|
|
public Map getI2CPOptions() { return _opts; }
|
|
|
|
public String getEepProxyHost() { return _proxyHost; }
|
|
|
|
public int getEepProxyPort() { return _proxyPort; }
|
|
|
|
public boolean getEepProxySet() { return _shouldProxy; }
|
2007-03-14 04:06:27 +00:00
|
|
|
public int getMaxUploaders() { return _maxUploaders; }
|
2008-04-07 17:41:58 +00:00
|
|
|
public int getMaxUpBW() { return _maxUpBW; }
|
2005-12-16 03:00:48 +00:00
|
|
|
|
2005-10-19 22:02:37 +00:00
|
|
|
/**
|
|
|
|
* Connect to the router, if we aren't already
|
|
|
|
*/
|
2008-04-12 21:34:25 +00:00
|
|
|
synchronized public boolean connect() {
|
2005-10-19 22:02:37 +00:00
|
|
|
if (_manager == null) {
|
2005-12-16 03:00:48 +00:00
|
|
|
Properties opts = new Properties();
|
|
|
|
if (_opts != null) {
|
|
|
|
for (Iterator iter = _opts.keySet().iterator(); iter.hasNext(); ) {
|
|
|
|
String key = (String)iter.next();
|
|
|
|
opts.setProperty(key, _opts.get(key).toString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (opts.getProperty("inbound.nickname") == null)
|
|
|
|
opts.setProperty("inbound.nickname", "I2PSnark");
|
2007-08-11 20:48:14 +00:00
|
|
|
if (opts.getProperty("outbound.nickname") == null)
|
|
|
|
opts.setProperty("outbound.nickname", "I2PSnark");
|
2005-12-21 12:04:54 +00:00
|
|
|
if (opts.getProperty("i2p.streaming.inactivityTimeout") == null)
|
2007-11-24 20:22:45 +00:00
|
|
|
opts.setProperty("i2p.streaming.inactivityTimeout", "240000");
|
2005-12-21 12:04:54 +00:00
|
|
|
if (opts.getProperty("i2p.streaming.inactivityAction") == null)
|
2007-11-24 20:22:45 +00:00
|
|
|
opts.setProperty("i2p.streaming.inactivityAction", "1"); // 1 == disconnect, 2 == ping
|
2007-03-07 05:11:45 +00:00
|
|
|
if (opts.getProperty("i2p.streaming.initialWindowSize") == null)
|
|
|
|
opts.setProperty("i2p.streaming.initialWindowSize", "1");
|
|
|
|
if (opts.getProperty("i2p.streaming.slowStartGrowthRateFactor") == null)
|
|
|
|
opts.setProperty("i2p.streaming.slowStartGrowthRateFactor", "1");
|
2006-02-26 21:30:56 +00:00
|
|
|
//if (opts.getProperty("i2p.streaming.writeTimeout") == null)
|
|
|
|
// opts.setProperty("i2p.streaming.writeTimeout", "90000");
|
2005-12-27 13:20:50 +00:00
|
|
|
//if (opts.getProperty("i2p.streaming.readTimeout") == null)
|
|
|
|
// opts.setProperty("i2p.streaming.readTimeout", "120000");
|
2005-12-16 03:00:48 +00:00
|
|
|
_manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
return (_manager != null);
|
|
|
|
}
|
|
|
|
|
2005-12-16 03:00:48 +00:00
|
|
|
public boolean connected() { return _manager != null; }
|
|
|
|
/**
|
|
|
|
* Destroy the destination itself
|
|
|
|
*/
|
|
|
|
public void disconnect() {
|
|
|
|
I2PSocketManager mgr = _manager;
|
|
|
|
_manager = null;
|
2006-03-05 17:07:07 +00:00
|
|
|
_shitlist.clear();
|
2005-12-16 03:00:48 +00:00
|
|
|
mgr.destroySocketManager();
|
|
|
|
}
|
|
|
|
|
2005-10-19 22:02:37 +00:00
|
|
|
/** connect to the given destination */
|
|
|
|
I2PSocket connect(PeerID peer) throws IOException {
|
2006-03-05 17:07:07 +00:00
|
|
|
Hash dest = peer.getAddress().calculateHash();
|
|
|
|
synchronized (_shitlist) {
|
|
|
|
if (_shitlist.contains(dest))
|
|
|
|
throw new IOException("Not trying to contact " + dest.toBase64() + ", as they are shitlisted");
|
|
|
|
}
|
2005-10-19 22:02:37 +00:00
|
|
|
try {
|
2006-03-05 17:07:07 +00:00
|
|
|
I2PSocket rv = _manager.connect(peer.getAddress());
|
|
|
|
if (rv != null) synchronized (_shitlist) { _shitlist.remove(dest); }
|
|
|
|
return rv;
|
2005-10-19 22:02:37 +00:00
|
|
|
} catch (I2PException ie) {
|
2006-03-05 17:07:07 +00:00
|
|
|
synchronized (_shitlist) {
|
|
|
|
_shitlist.add(dest);
|
|
|
|
}
|
|
|
|
SimpleTimer.getInstance().addEvent(new Unshitlist(dest), 10*60*1000);
|
2005-10-19 22:02:37 +00:00
|
|
|
throw new IOException("Unable to reach the peer " + peer + ": " + ie.getMessage());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-05 17:07:07 +00:00
|
|
|
private class Unshitlist implements SimpleTimer.TimedEvent {
|
|
|
|
private Hash _dest;
|
|
|
|
public Unshitlist(Hash dest) { _dest = dest; }
|
|
|
|
public void timeReached() { synchronized (_shitlist) { _shitlist.remove(_dest); } }
|
|
|
|
}
|
|
|
|
|
2005-10-19 22:02:37 +00:00
|
|
|
/**
|
|
|
|
* fetch the given URL, returning the file it is stored in, or null on error
|
|
|
|
*/
|
2008-03-25 21:54:54 +00:00
|
|
|
public File get(String url) { return get(url, true, 0); }
|
|
|
|
public File get(String url, boolean rewrite) { return get(url, rewrite, 0); }
|
2008-03-19 00:20:15 +00:00
|
|
|
public File get(String url, int retries) { return get(url, true, retries); }
|
|
|
|
public File get(String url, boolean rewrite, int retries) {
|
2005-12-13 09:38:51 +00:00
|
|
|
_log.debug("Fetching [" + url + "] proxy=" + _proxyHost + ":" + _proxyPort + ": " + _shouldProxy);
|
2005-10-19 22:02:37 +00:00
|
|
|
File out = null;
|
|
|
|
try {
|
2006-02-15 05:33:17 +00:00
|
|
|
out = File.createTempFile("i2psnark", "url", new File("."));
|
2005-10-19 22:02:37 +00:00
|
|
|
} catch (IOException ioe) {
|
|
|
|
ioe.printStackTrace();
|
2008-10-26 18:18:34 +00:00
|
|
|
if (out != null)
|
|
|
|
out.delete();
|
2005-10-19 22:02:37 +00:00
|
|
|
return null;
|
|
|
|
}
|
2005-12-16 03:00:48 +00:00
|
|
|
String fetchURL = url;
|
|
|
|
if (rewrite)
|
|
|
|
fetchURL = rewriteAnnounce(url);
|
|
|
|
//_log.debug("Rewritten url [" + fetchURL + "]");
|
2008-03-19 00:20:15 +00:00
|
|
|
EepGet get = new EepGet(_context, _shouldProxy, _proxyHost, _proxyPort, retries, out.getAbsolutePath(), fetchURL);
|
2005-10-19 22:02:37 +00:00
|
|
|
if (get.fetch()) {
|
2005-12-13 09:38:51 +00:00
|
|
|
_log.debug("Fetch successful [" + url + "]: size=" + out.length());
|
2005-10-19 22:02:37 +00:00
|
|
|
return out;
|
|
|
|
} else {
|
2005-12-13 09:38:51 +00:00
|
|
|
_log.warn("Fetch failed [" + url + "]");
|
2005-10-19 22:02:37 +00:00
|
|
|
out.delete();
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-12-16 03:00:48 +00:00
|
|
|
public I2PServerSocket getServerSocket() {
|
2006-01-26 04:28:15 +00:00
|
|
|
I2PSocketManager mgr = _manager;
|
|
|
|
if (mgr != null)
|
|
|
|
return mgr.getServerSocket();
|
|
|
|
else
|
|
|
|
return null;
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
String getOurIPString() {
|
2008-04-07 17:41:58 +00:00
|
|
|
if (_manager == null)
|
|
|
|
return "unknown";
|
2005-12-16 23:18:56 +00:00
|
|
|
I2PSession sess = _manager.getSession();
|
|
|
|
if (sess != null) {
|
|
|
|
Destination dest = sess.getMyDestination();
|
|
|
|
if (dest != null)
|
|
|
|
return dest.toBase64();
|
|
|
|
}
|
|
|
|
return "unknown";
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
Destination getDestination(String ip) {
|
|
|
|
if (ip == null) return null;
|
|
|
|
if (ip.endsWith(".i2p")) {
|
2008-03-05 14:19:19 +00:00
|
|
|
if (ip.length() < 520) { // key + ".i2p"
|
|
|
|
Destination dest = _context.namingService().lookup(ip);
|
|
|
|
if (dest != null)
|
|
|
|
return dest;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
return new Destination(ip.substring(0, ip.length()-4)); // sans .i2p
|
|
|
|
} catch (DataFormatException dfe) {
|
|
|
|
return null;
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
return new Destination(ip);
|
|
|
|
} catch (DataFormatException dfe) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-25 21:54:54 +00:00
|
|
|
public String lookup(String name) {
|
|
|
|
Destination dest = getDestination(name);
|
|
|
|
if (dest == null)
|
|
|
|
return null;
|
|
|
|
return dest.toBase64();
|
|
|
|
}
|
|
|
|
|
2005-10-19 22:02:37 +00:00
|
|
|
/**
|
2008-03-25 21:54:54 +00:00
|
|
|
* Given http://KEY.i2p/foo/announce turn it into http://i2p/KEY/foo/announce
|
|
|
|
* Given http://tracker.blah.i2p/foo/announce leave it alone
|
2005-10-19 22:02:37 +00:00
|
|
|
*/
|
|
|
|
String rewriteAnnounce(String origAnnounce) {
|
|
|
|
int destStart = "http://".length();
|
|
|
|
int destEnd = origAnnounce.indexOf(".i2p");
|
2008-03-25 21:54:54 +00:00
|
|
|
if (destEnd < destStart + 516)
|
|
|
|
return origAnnounce;
|
2005-10-19 22:02:37 +00:00
|
|
|
int pathStart = origAnnounce.indexOf('/', destEnd);
|
2005-12-13 09:38:51 +00:00
|
|
|
String rv = "http://i2p/" + origAnnounce.substring(destStart, destEnd) + origAnnounce.substring(pathStart);
|
2005-12-15 08:58:30 +00:00
|
|
|
//_log.debug("Rewriting [" + origAnnounce + "] as [" + rv + "]");
|
2005-12-13 09:38:51 +00:00
|
|
|
return rv;
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
|
2008-11-15 23:52:40 +00:00
|
|
|
public String getOpenTrackerString() {
|
|
|
|
String rv = (String) _opts.get(PROP_OPENTRACKERS);
|
|
|
|
if (rv == null)
|
|
|
|
return DEFAULT_OPENTRACKERS;
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** comma delimited list open trackers to use as backups */
|
|
|
|
/** sorted map of name to announceURL=baseURL */
|
|
|
|
public List getOpenTrackers() {
|
|
|
|
if (!shouldUseOpenTrackers())
|
|
|
|
return null;
|
|
|
|
List rv = new ArrayList(1);
|
|
|
|
String trackers = getOpenTrackerString();
|
|
|
|
StringTokenizer tok = new StringTokenizer(trackers, ", ");
|
|
|
|
while (tok.hasMoreTokens())
|
|
|
|
rv.add(tok.nextToken());
|
|
|
|
|
|
|
|
if (rv.size() <= 0)
|
|
|
|
return null;
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean shouldUseOpenTrackers() {
|
|
|
|
String rv = (String) _opts.get(PROP_USE_OPENTRACKERS);
|
|
|
|
if (rv == null)
|
|
|
|
return DEFAULT_USE_OPENTRACKERS;
|
|
|
|
return Boolean.valueOf(rv).booleanValue();
|
|
|
|
}
|
|
|
|
|
2005-10-19 22:02:37 +00:00
|
|
|
/** hook between snark's logger and an i2p log */
|
|
|
|
void debug(String msg, int snarkDebugLevel, Throwable t) {
|
2005-12-18 05:39:52 +00:00
|
|
|
if (t instanceof OutOfMemoryError) {
|
|
|
|
try { Thread.sleep(100); } catch (InterruptedException ie) {}
|
|
|
|
try {
|
|
|
|
t.printStackTrace();
|
|
|
|
} catch (Throwable tt) {}
|
|
|
|
try {
|
|
|
|
System.out.println("OOM thread: " + Thread.currentThread().getName());
|
|
|
|
} catch (Throwable tt) {}
|
|
|
|
}
|
2005-10-19 22:02:37 +00:00
|
|
|
switch (snarkDebugLevel) {
|
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
_log.error(msg, t);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
_log.warn(msg, t);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
case 4:
|
|
|
|
_log.info(msg, t);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
case 6:
|
|
|
|
default:
|
|
|
|
_log.debug(msg, t);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|