2005-10-19 22:02:37 +00:00
|
|
|
package org.klomp.snark;
|
|
|
|
|
2012-11-05 17:20:07 +00:00
|
|
|
import java.io.ByteArrayOutputStream;
|
2008-07-16 13:42:54 +00:00
|
|
|
import java.io.File;
|
|
|
|
import java.io.IOException;
|
2015-11-07 22:38:05 +00:00
|
|
|
import java.net.URI;
|
|
|
|
import java.net.URISyntaxException;
|
2012-06-08 16:11:55 +00:00
|
|
|
import java.util.Collections;
|
2008-07-16 13:42:54 +00:00
|
|
|
import java.util.HashMap;
|
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;
|
2014-11-06 17:45:06 +00:00
|
|
|
|
2005-10-19 22:02:37 +00:00
|
|
|
import net.i2p.I2PAppContext;
|
|
|
|
import net.i2p.I2PException;
|
2015-08-31 13:19:29 +00:00
|
|
|
import net.i2p.client.I2PClient;
|
2005-12-16 23:18:56 +00:00
|
|
|
import net.i2p.client.I2PSession;
|
2010-12-26 20:36:44 +00:00
|
|
|
import net.i2p.client.I2PSessionException;
|
2005-10-19 22:02:37 +00:00
|
|
|
import net.i2p.client.streaming.I2PServerSocket;
|
|
|
|
import net.i2p.client.streaming.I2PSocket;
|
2009-08-26 22:15:32 +00:00
|
|
|
import net.i2p.client.streaming.I2PSocketEepGet;
|
2005-10-19 22:02:37 +00:00
|
|
|
import net.i2p.client.streaming.I2PSocketManager;
|
|
|
|
import net.i2p.client.streaming.I2PSocketManagerFactory;
|
2012-05-31 12:19:27 +00:00
|
|
|
import net.i2p.client.streaming.I2PSocketOptions;
|
2010-12-26 20:36:44 +00:00
|
|
|
import net.i2p.data.Base32;
|
2008-07-16 13:42:54 +00:00
|
|
|
import net.i2p.data.DataFormatException;
|
|
|
|
import net.i2p.data.Destination;
|
|
|
|
import net.i2p.data.Hash;
|
2010-04-12 19:07:53 +00:00
|
|
|
import net.i2p.util.ConcurrentHashSet;
|
2008-07-16 13:42:54 +00:00
|
|
|
import net.i2p.util.EepGet;
|
2009-02-10 02:34:48 +00:00
|
|
|
import net.i2p.util.FileUtil;
|
2005-10-19 22:02:37 +00:00
|
|
|
import net.i2p.util.Log;
|
2010-07-06 15:22:48 +00:00
|
|
|
import net.i2p.util.SecureDirectory;
|
2010-11-19 00:40:33 +00:00
|
|
|
import net.i2p.util.SecureFile;
|
2006-03-05 17:07:07 +00:00
|
|
|
import net.i2p.util.SimpleTimer;
|
2009-12-09 20:54:10 +00:00
|
|
|
import net.i2p.util.Translate;
|
2005-10-19 22:02:37 +00:00
|
|
|
|
2011-01-01 14:59:41 +00:00
|
|
|
import org.klomp.snark.dht.DHT;
|
2012-06-02 18:52:46 +00:00
|
|
|
import org.klomp.snark.dht.KRPC;
|
2010-12-20 00:05:03 +00:00
|
|
|
|
2005-10-19 22:02:37 +00:00
|
|
|
/**
|
|
|
|
* I2P specific helpers for I2PSnark
|
2008-11-16 17:11:53 +00:00
|
|
|
* We use this class as a sort of context for i2psnark
|
|
|
|
* so we can run multiple instances of single Snarks
|
|
|
|
* (but not multiple SnarkManagers, it is still static)
|
2005-10-19 22:02:37 +00:00
|
|
|
*/
|
|
|
|
public class I2PSnarkUtil {
|
2011-09-14 13:06:03 +00:00
|
|
|
private final I2PAppContext _context;
|
|
|
|
private final Log _log;
|
2013-01-23 22:44:52 +00:00
|
|
|
private final String _baseName;
|
2005-10-19 22:02:37 +00:00
|
|
|
|
|
|
|
private boolean _shouldProxy;
|
|
|
|
private String _proxyHost;
|
|
|
|
private int _proxyPort;
|
|
|
|
private String _i2cpHost;
|
|
|
|
private int _i2cpPort;
|
2011-09-14 13:06:03 +00:00
|
|
|
private final Map<String, String> _opts;
|
2012-06-18 21:07:34 +00:00
|
|
|
private volatile I2PSocketManager _manager;
|
2005-12-16 03:00:48 +00:00
|
|
|
private boolean _configured;
|
2012-06-18 21:07:34 +00:00
|
|
|
private volatile boolean _connecting;
|
2012-10-26 16:24:31 +00:00
|
|
|
private final Set<Hash> _banlist;
|
2007-03-14 04:06:27 +00:00
|
|
|
private int _maxUploaders;
|
2008-04-07 17:41:58 +00:00
|
|
|
private int _maxUpBW;
|
2008-11-18 02:18:23 +00:00
|
|
|
private int _maxConnections;
|
2011-09-14 13:06:03 +00:00
|
|
|
private final File _tmpDir;
|
2010-06-04 23:50:13 +00:00
|
|
|
private int _startupDelay;
|
2010-12-28 15:46:45 +00:00
|
|
|
private boolean _shouldUseOT;
|
2012-06-02 18:52:46 +00:00
|
|
|
private boolean _shouldUseDHT;
|
2011-09-14 13:06:03 +00:00
|
|
|
private boolean _areFilesPublic;
|
2012-06-08 16:11:55 +00:00
|
|
|
private List<String> _openTrackers;
|
2011-01-01 14:59:41 +00:00
|
|
|
private DHT _dht;
|
2017-03-20 16:41:04 +00:00
|
|
|
private long _startedTime;
|
2010-06-04 23:50:13 +00:00
|
|
|
|
2012-06-11 19:38:33 +00:00
|
|
|
private static final int EEPGET_CONNECT_TIMEOUT = 45*1000;
|
|
|
|
private static final int EEPGET_CONNECT_TIMEOUT_SHORT = 5*1000;
|
2010-06-04 23:50:13 +00:00
|
|
|
public static final int DEFAULT_STARTUP_DELAY = 3;
|
2008-11-15 23:52:40 +00:00
|
|
|
public static final boolean DEFAULT_USE_OPENTRACKERS = true;
|
2015-12-01 20:12:31 +00:00
|
|
|
public static final int MAX_CONNECTIONS = 24; // per torrent
|
2011-09-14 13:06:03 +00:00
|
|
|
public static final String PROP_MAX_BW = "i2cp.outboundBytesPerSecond";
|
2012-09-26 19:57:01 +00:00
|
|
|
public static final boolean DEFAULT_USE_DHT = true;
|
2013-12-15 15:16:54 +00:00
|
|
|
public static final String EEPGET_USER_AGENT = "I2PSnark";
|
2010-12-20 00:05:03 +00:00
|
|
|
|
2008-11-16 17:11:53 +00:00
|
|
|
public I2PSnarkUtil(I2PAppContext ctx) {
|
2013-01-23 22:44:52 +00:00
|
|
|
this(ctx, "i2psnark");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param baseName generally "i2psnark"
|
|
|
|
* @since Jetty 7
|
|
|
|
*/
|
|
|
|
public I2PSnarkUtil(I2PAppContext ctx, String baseName) {
|
2008-11-16 17:11:53 +00:00
|
|
|
_context = ctx;
|
2005-10-19 22:02:37 +00:00
|
|
|
_log = _context.logManager().getLog(Snark.class);
|
2013-01-23 22:44:52 +00:00
|
|
|
_baseName = baseName;
|
2013-11-21 12:43:45 +00:00
|
|
|
_opts = new HashMap<String, String>();
|
2010-05-10 14:13:55 +00:00
|
|
|
//setProxy("127.0.0.1", 4444);
|
2005-10-19 22:02:37 +00:00
|
|
|
setI2CPConfig("127.0.0.1", 7654, null);
|
2013-11-21 12:43:45 +00:00
|
|
|
_banlist = new ConcurrentHashSet<Hash>();
|
2007-03-14 04:06:27 +00:00
|
|
|
_maxUploaders = Snark.MAX_TOTAL_UPLOADERS;
|
2015-03-08 20:19:12 +00:00
|
|
|
_maxUpBW = SnarkManager.DEFAULT_MAX_UP_BW;
|
2008-11-18 02:18:23 +00:00
|
|
|
_maxConnections = MAX_CONNECTIONS;
|
2010-06-04 23:50:13 +00:00
|
|
|
_startupDelay = DEFAULT_STARTUP_DELAY;
|
2010-12-28 15:46:45 +00:00
|
|
|
_shouldUseOT = DEFAULT_USE_OPENTRACKERS;
|
2014-11-06 17:45:06 +00:00
|
|
|
_openTrackers = Collections.emptyList();
|
2012-06-02 18:52:46 +00:00
|
|
|
_shouldUseDHT = DEFAULT_USE_DHT;
|
2009-02-10 02:34:48 +00:00
|
|
|
// 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
|
2015-03-21 12:19:03 +00:00
|
|
|
_tmpDir = new SecureDirectory(ctx.getTempDir(), baseName + '-' + ctx.random().nextInt());
|
|
|
|
//FileUtil.rmdir(_tmpDir, false);
|
2009-02-10 02:34:48 +00:00
|
|
|
_tmpDir.mkdirs();
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Specify what HTTP proxy tracker requests should go through (specify a null
|
|
|
|
* host for no proxying)
|
|
|
|
*
|
|
|
|
*/
|
2010-05-10 14:13:55 +00:00
|
|
|
/*****
|
2005-10-19 22:02:37 +00:00
|
|
|
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
|
|
|
}
|
2010-05-10 14:13:55 +00:00
|
|
|
******/
|
2005-10-19 22:02:37 +00:00
|
|
|
|
2012-07-01 16:16:08 +00:00
|
|
|
/** @since 0.9.1 */
|
|
|
|
public I2PAppContext getContext() { return _context; }
|
|
|
|
|
2005-12-16 03:00:48 +00:00
|
|
|
public boolean configured() { return _configured; }
|
|
|
|
|
2015-10-17 16:49:37 +00:00
|
|
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
2005-12-16 03:00:48 +00:00
|
|
|
public void setI2CPConfig(String i2cpHost, int i2cpPort, Map opts) {
|
2008-11-18 02:18:23 +00:00
|
|
|
if (i2cpHost != null)
|
|
|
|
_i2cpHost = i2cpHost;
|
|
|
|
if (i2cpPort > 0)
|
|
|
|
_i2cpPort = i2cpPort;
|
2009-02-10 02:34:48 +00:00
|
|
|
// can't remove any options this way...
|
2005-10-19 22:02:37 +00:00
|
|
|
if (opts != null)
|
2005-12-16 03:00:48 +00:00
|
|
|
_opts.putAll(opts);
|
2011-09-14 13:06:03 +00:00
|
|
|
// this updates the session options and tells the router
|
|
|
|
setMaxUpBW(_maxUpBW);
|
2005-12-16 03:00:48 +00:00
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
2011-01-10 17:14:34 +00:00
|
|
|
/**
|
2013-06-01 16:57:50 +00:00
|
|
|
* This updates ALL the session options (not just the bw) and tells the router
|
2011-03-01 13:37:40 +00:00
|
|
|
* @param limit KBps
|
2011-01-10 17:14:34 +00:00
|
|
|
*/
|
2008-04-07 17:41:58 +00:00
|
|
|
public void setMaxUpBW(int limit) {
|
|
|
|
_maxUpBW = limit;
|
2011-01-10 17:14:34 +00:00
|
|
|
_opts.put(PROP_MAX_BW, Integer.toString(limit * (1024 * 6 / 5))); // add a little for overhead
|
2008-04-07 17:41:58 +00:00
|
|
|
_configured = true;
|
2011-01-10 17:14:34 +00:00
|
|
|
if (_manager != null) {
|
|
|
|
I2PSession sess = _manager.getSession();
|
|
|
|
if (sess != null) {
|
|
|
|
Properties newProps = new Properties();
|
|
|
|
newProps.putAll(_opts);
|
|
|
|
sess.updateOptions(newProps);
|
|
|
|
}
|
|
|
|
}
|
2008-04-07 17:41:58 +00:00
|
|
|
}
|
|
|
|
|
2008-11-18 02:18:23 +00:00
|
|
|
public void setMaxConnections(int limit) {
|
|
|
|
_maxConnections = limit;
|
|
|
|
_configured = true;
|
|
|
|
}
|
2010-06-04 23:50:13 +00:00
|
|
|
|
|
|
|
public void setStartupDelay(int minutes) {
|
|
|
|
_startupDelay = minutes;
|
|
|
|
_configured = true;
|
|
|
|
}
|
2008-11-18 02:18:23 +00:00
|
|
|
|
2005-12-16 03:00:48 +00:00
|
|
|
public String getI2CPHost() { return _i2cpHost; }
|
|
|
|
public int getI2CPPort() { return _i2cpPort; }
|
2010-05-10 14:13:55 +00:00
|
|
|
public Map<String, String> getI2CPOptions() { return _opts; }
|
2005-12-16 03:00:48 +00:00
|
|
|
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; }
|
2011-01-10 17:14:34 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return KBps
|
|
|
|
*/
|
2008-04-07 17:41:58 +00:00
|
|
|
public int getMaxUpBW() { return _maxUpBW; }
|
2008-11-18 02:18:23 +00:00
|
|
|
public int getMaxConnections() { return _maxConnections; }
|
2010-06-04 23:50:13 +00:00
|
|
|
public int getStartupDelay() { return _startupDelay; }
|
|
|
|
|
2011-09-14 13:06:03 +00:00
|
|
|
/** @since 0.8.9 */
|
|
|
|
public boolean getFilesPublic() { return _areFilesPublic; }
|
|
|
|
|
|
|
|
/** @since 0.8.9 */
|
|
|
|
public void setFilesPublic(boolean yes) { _areFilesPublic = yes; }
|
|
|
|
|
2012-05-19 13:27:02 +00:00
|
|
|
/** @since 0.9.1 */
|
|
|
|
public File getTempDir() { return _tmpDir; }
|
|
|
|
|
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) {
|
2012-06-18 21:07:34 +00:00
|
|
|
_connecting = true;
|
2010-10-30 15:30:14 +00:00
|
|
|
// try to find why reconnecting after stop
|
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug("Connecting to I2P", new Exception("I did it"));
|
2011-01-10 17:14:34 +00:00
|
|
|
Properties opts = _context.getProperties();
|
2005-12-16 03:00:48 +00:00
|
|
|
if (_opts != null) {
|
2013-11-28 11:10:57 +00:00
|
|
|
for (Map.Entry<String, String> entry : _opts.entrySet() )
|
|
|
|
opts.setProperty(entry.getKey(), entry.getValue());
|
2005-12-16 03:00:48 +00:00
|
|
|
}
|
|
|
|
if (opts.getProperty("inbound.nickname") == null)
|
2013-04-14 14:00:47 +00:00
|
|
|
opts.setProperty("inbound.nickname", _baseName.replace("i2psnark", "I2PSnark"));
|
2007-08-11 20:48:14 +00:00
|
|
|
if (opts.getProperty("outbound.nickname") == null)
|
2013-04-14 14:00:47 +00:00
|
|
|
opts.setProperty("outbound.nickname", _baseName.replace("i2psnark", "I2PSnark"));
|
2012-11-17 17:32:24 +00:00
|
|
|
if (opts.getProperty("outbound.priority") == null)
|
|
|
|
opts.setProperty("outbound.priority", "-10");
|
2010-10-15 13:48:36 +00:00
|
|
|
// Dont do this for now, it is set in I2PSocketEepGet for announces,
|
|
|
|
// we don't need fast handshake for peer connections.
|
|
|
|
//if (opts.getProperty("i2p.streaming.connectDelay") == null)
|
|
|
|
// opts.setProperty("i2p.streaming.connectDelay", "500");
|
2012-05-31 12:19:27 +00:00
|
|
|
if (opts.getProperty(I2PSocketOptions.PROP_CONNECT_TIMEOUT) == null)
|
|
|
|
opts.setProperty(I2PSocketOptions.PROP_CONNECT_TIMEOUT, "75000");
|
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");
|
2012-02-23 19:31:25 +00:00
|
|
|
if (opts.getProperty("i2p.streaming.maxConnsPerMinute") == null)
|
|
|
|
opts.setProperty("i2p.streaming.maxConnsPerMinute", "2");
|
|
|
|
if (opts.getProperty("i2p.streaming.maxTotalConnsPerMinute") == null)
|
2012-05-30 14:10:03 +00:00
|
|
|
opts.setProperty("i2p.streaming.maxTotalConnsPerMinute", "8");
|
2012-05-31 12:19:27 +00:00
|
|
|
if (opts.getProperty("i2p.streaming.maxConnsPerHour") == null)
|
|
|
|
opts.setProperty("i2p.streaming.maxConnsPerHour", "20");
|
2012-06-22 15:12:43 +00:00
|
|
|
if (opts.getProperty("i2p.streaming.enforceProtocol") == null)
|
|
|
|
opts.setProperty("i2p.streaming.enforceProtocol", "true");
|
2012-10-31 15:56:02 +00:00
|
|
|
if (opts.getProperty("i2p.streaming.disableRejectLogging") == null)
|
|
|
|
opts.setProperty("i2p.streaming.disableRejectLogging", "true");
|
2013-12-09 16:11:53 +00:00
|
|
|
if (opts.getProperty("i2p.streaming.answerPings") == null)
|
|
|
|
opts.setProperty("i2p.streaming.answerPings", "false");
|
2015-08-31 13:19:29 +00:00
|
|
|
if (opts.getProperty(I2PClient.PROP_SIGTYPE) == null)
|
|
|
|
opts.setProperty(I2PClient.PROP_SIGTYPE, "EdDSA_SHA512_Ed25519");
|
2005-12-16 03:00:48 +00:00
|
|
|
_manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);
|
2017-03-20 16:41:04 +00:00
|
|
|
if (_manager != null)
|
|
|
|
_startedTime = _context.clock().now();
|
2012-06-18 21:07:34 +00:00
|
|
|
_connecting = false;
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
2012-06-02 18:52:46 +00:00
|
|
|
if (_shouldUseDHT && _manager != null && _dht == null)
|
2013-01-23 22:44:52 +00:00
|
|
|
_dht = new KRPC(_context, _baseName, _manager.getSession());
|
2005-10-19 22:02:37 +00:00
|
|
|
return (_manager != null);
|
|
|
|
}
|
|
|
|
|
2010-12-20 00:05:03 +00:00
|
|
|
/**
|
|
|
|
* @return null if disabled or not started
|
|
|
|
* @since 0.8.4
|
|
|
|
*/
|
2011-01-01 14:59:41 +00:00
|
|
|
public DHT getDHT() { return _dht; }
|
2010-12-20 00:05:03 +00:00
|
|
|
|
2005-12-16 03:00:48 +00:00
|
|
|
public boolean connected() { return _manager != null; }
|
2010-12-20 00:05:03 +00:00
|
|
|
|
2012-06-18 21:07:34 +00:00
|
|
|
/** @since 0.9.1 */
|
|
|
|
public boolean isConnecting() { return _manager == null && _connecting; }
|
|
|
|
|
2012-06-11 12:04:40 +00:00
|
|
|
/**
|
|
|
|
* For FetchAndAdd
|
|
|
|
* @return null if not connected
|
|
|
|
* @since 0.9.1
|
|
|
|
*/
|
|
|
|
public I2PSocketManager getSocketManager() {
|
|
|
|
return _manager;
|
|
|
|
}
|
|
|
|
|
2005-12-16 03:00:48 +00:00
|
|
|
/**
|
|
|
|
* Destroy the destination itself
|
|
|
|
*/
|
2012-06-02 18:52:46 +00:00
|
|
|
public synchronized void disconnect() {
|
|
|
|
if (_dht != null) {
|
|
|
|
_dht.stop();
|
|
|
|
_dht = null;
|
|
|
|
}
|
2017-03-20 16:41:04 +00:00
|
|
|
_startedTime = 0;
|
2005-12-16 03:00:48 +00:00
|
|
|
I2PSocketManager mgr = _manager;
|
2010-10-24 16:56:43 +00:00
|
|
|
// FIXME this can cause race NPEs elsewhere
|
2005-12-16 03:00:48 +00:00
|
|
|
_manager = null;
|
2012-10-26 16:24:31 +00:00
|
|
|
_banlist.clear();
|
2012-06-18 18:06:47 +00:00
|
|
|
if (mgr != null) {
|
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug("Disconnecting from I2P", new Exception("I did it"));
|
2012-06-04 22:34:56 +00:00
|
|
|
mgr.destroySocketManager();
|
2012-06-18 18:06:47 +00:00
|
|
|
}
|
2009-02-10 02:34:48 +00:00
|
|
|
// this will delete a .torrent file d/l in progress so don't do that...
|
|
|
|
FileUtil.rmdir(_tmpDir, false);
|
|
|
|
// in case the user will d/l a .torrent file next...
|
|
|
|
_tmpDir.mkdirs();
|
2005-12-16 03:00:48 +00:00
|
|
|
}
|
|
|
|
|
2017-03-20 16:41:04 +00:00
|
|
|
/**
|
|
|
|
* When did we connect to the network?
|
|
|
|
* For RPC
|
|
|
|
* @return 0 if not connected
|
|
|
|
* @since 0.9.30
|
|
|
|
*/
|
|
|
|
public long getStartedTime() {
|
|
|
|
return _startedTime;
|
|
|
|
}
|
|
|
|
|
2005-10-19 22:02:37 +00:00
|
|
|
/** connect to the given destination */
|
|
|
|
I2PSocket connect(PeerID peer) throws IOException {
|
2010-10-24 16:56:43 +00:00
|
|
|
I2PSocketManager mgr = _manager;
|
|
|
|
if (mgr == null)
|
|
|
|
throw new IOException("No socket manager");
|
2010-07-09 16:32:31 +00:00
|
|
|
Destination addr = peer.getAddress();
|
|
|
|
if (addr == null)
|
|
|
|
throw new IOException("Null address");
|
2010-12-20 19:37:38 +00:00
|
|
|
if (addr.equals(getMyDestination()))
|
|
|
|
throw new IOException("Attempt to connect to myself");
|
2010-07-09 16:32:31 +00:00
|
|
|
Hash dest = addr.calculateHash();
|
2012-10-26 16:24:31 +00:00
|
|
|
if (_banlist.contains(dest))
|
|
|
|
throw new IOException("Not trying to contact " + dest.toBase64() + ", as they are banlisted");
|
2005-10-19 22:02:37 +00:00
|
|
|
try {
|
2013-10-24 20:52:37 +00:00
|
|
|
// TODO opts.setPort(xxx); connect(addr, opts)
|
|
|
|
// DHT moved above 6881 in 0.9.9
|
2010-07-09 16:32:31 +00:00
|
|
|
I2PSocket rv = _manager.connect(addr);
|
2010-04-12 19:07:53 +00:00
|
|
|
if (rv != null)
|
2012-10-26 16:24:31 +00:00
|
|
|
_banlist.remove(dest);
|
2006-03-05 17:07:07 +00:00
|
|
|
return rv;
|
2005-10-19 22:02:37 +00:00
|
|
|
} catch (I2PException ie) {
|
2012-10-26 16:24:31 +00:00
|
|
|
_banlist.add(dest);
|
2015-04-06 21:05:24 +00:00
|
|
|
_context.simpleTimer2().addEvent(new Unbanlist(dest), 10*60*1000);
|
2013-10-24 20:52:37 +00:00
|
|
|
IOException ioe = new IOException("Unable to reach the peer " + peer);
|
|
|
|
ioe.initCause(ie);
|
|
|
|
throw ioe;
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-26 16:24:31 +00:00
|
|
|
private class Unbanlist implements SimpleTimer.TimedEvent {
|
2006-03-05 17:07:07 +00:00
|
|
|
private Hash _dest;
|
2012-10-26 16:24:31 +00:00
|
|
|
public Unbanlist(Hash dest) { _dest = dest; }
|
|
|
|
public void timeReached() { _banlist.remove(_dest); }
|
2006-03-05 17:07:07 +00:00
|
|
|
}
|
|
|
|
|
2005-10-19 22:02:37 +00:00
|
|
|
/**
|
2012-06-11 19:38:33 +00:00
|
|
|
* Fetch the given URL, returning the file it is stored in, or null on error.
|
|
|
|
* No retries.
|
2005-10-19 22:02:37 +00:00
|
|
|
*/
|
2008-03-25 21:54:54 +00:00
|
|
|
public File get(String url) { return get(url, true, 0); }
|
2012-06-11 19:38:33 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param rewrite if true, convert http://KEY.i2p/foo/announce to http://i2p/KEY/foo/announce
|
|
|
|
*/
|
2008-03-25 21:54:54 +00:00
|
|
|
public File get(String url, boolean rewrite) { return get(url, rewrite, 0); }
|
2012-06-11 19:38:33 +00:00
|
|
|
|
|
|
|
/**
|
2017-01-27 03:03:34 +00:00
|
|
|
* @param retries if < 0, set timeout to a few seconds
|
2012-06-11 19:38:33 +00:00
|
|
|
*/
|
2008-03-19 00:20:15 +00:00
|
|
|
public File get(String url, int retries) { return get(url, true, retries); }
|
2012-06-11 19:38:33 +00:00
|
|
|
|
|
|
|
/**
|
2017-01-27 03:03:34 +00:00
|
|
|
* @param retries if < 0, set timeout to a few seconds
|
2012-06-11 19:38:33 +00:00
|
|
|
*/
|
2008-03-19 00:20:15 +00:00
|
|
|
public File get(String url, boolean rewrite, int retries) {
|
2010-07-09 17:40:59 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug("Fetching [" + url + "] proxy=" + _proxyHost + ":" + _proxyPort + ": " + _shouldProxy);
|
2005-10-19 22:02:37 +00:00
|
|
|
File out = null;
|
|
|
|
try {
|
2009-02-10 02:34:48 +00:00
|
|
|
// we could use the system tmp dir but deleteOnExit() doesn't seem to work on all platforms...
|
2010-11-19 00:40:33 +00:00
|
|
|
out = SecureFile.createTempFile("i2psnark", null, _tmpDir);
|
2005-10-19 22:02:37 +00:00
|
|
|
} catch (IOException ioe) {
|
2012-06-11 12:04:40 +00:00
|
|
|
_log.error("temp file error", ioe);
|
2008-10-26 18:18:34 +00:00
|
|
|
if (out != null)
|
|
|
|
out.delete();
|
2005-10-19 22:02:37 +00:00
|
|
|
return null;
|
|
|
|
}
|
2008-12-20 02:33:57 +00:00
|
|
|
out.deleteOnExit();
|
2005-12-16 03:00:48 +00:00
|
|
|
String fetchURL = url;
|
|
|
|
if (rewrite)
|
|
|
|
fetchURL = rewriteAnnounce(url);
|
|
|
|
//_log.debug("Rewritten url [" + fetchURL + "]");
|
2009-08-26 22:15:32 +00:00
|
|
|
//EepGet get = new EepGet(_context, _shouldProxy, _proxyHost, _proxyPort, retries, out.getAbsolutePath(), fetchURL);
|
|
|
|
// Use our tunnel for announces and .torrent fetches too! Make sure we're connected first...
|
2012-06-11 19:38:33 +00:00
|
|
|
int timeout;
|
|
|
|
if (retries < 0) {
|
|
|
|
if (!connected())
|
2009-08-26 22:15:32 +00:00
|
|
|
return null;
|
2012-06-11 19:38:33 +00:00
|
|
|
timeout = EEPGET_CONNECT_TIMEOUT_SHORT;
|
|
|
|
retries = 0;
|
|
|
|
} else {
|
|
|
|
timeout = EEPGET_CONNECT_TIMEOUT;
|
|
|
|
if (!connected()) {
|
|
|
|
if (!connect())
|
|
|
|
return null;
|
|
|
|
}
|
2009-08-26 22:15:32 +00:00
|
|
|
}
|
|
|
|
EepGet get = new I2PSocketEepGet(_context, _manager, retries, out.getAbsolutePath(), fetchURL);
|
2013-12-15 15:16:54 +00:00
|
|
|
get.addHeader("User-Agent", EEPGET_USER_AGENT);
|
2012-06-11 19:38:33 +00:00
|
|
|
if (get.fetch(timeout)) {
|
2010-07-09 17:40:59 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug("Fetch successful [" + url + "]: size=" + out.length());
|
2005-10-19 22:02:37 +00:00
|
|
|
return out;
|
|
|
|
} else {
|
2010-07-09 17:40:59 +00:00
|
|
|
if (_log.shouldLog(Log.WARN))
|
|
|
|
_log.warn("Fetch failed [" + url + "]");
|
2005-10-19 22:02:37 +00:00
|
|
|
out.delete();
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-05 17:20:07 +00:00
|
|
|
/**
|
|
|
|
* Fetch to memory
|
2017-01-27 03:03:34 +00:00
|
|
|
* @param retries if < 0, set timeout to a few seconds
|
2012-11-05 17:20:07 +00:00
|
|
|
* @param initialSize buffer size
|
|
|
|
* @param maxSize fails if greater
|
|
|
|
* @return null on error
|
|
|
|
* @since 0.9.4
|
|
|
|
*/
|
|
|
|
public byte[] get(String url, boolean rewrite, int retries, int initialSize, int maxSize) {
|
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug("Fetching [" + url + "] to memory");
|
|
|
|
String fetchURL = url;
|
|
|
|
if (rewrite)
|
|
|
|
fetchURL = rewriteAnnounce(url);
|
|
|
|
int timeout;
|
|
|
|
if (retries < 0) {
|
|
|
|
if (!connected())
|
|
|
|
return null;
|
|
|
|
timeout = EEPGET_CONNECT_TIMEOUT_SHORT;
|
|
|
|
retries = 0;
|
|
|
|
} else {
|
|
|
|
timeout = EEPGET_CONNECT_TIMEOUT;
|
|
|
|
if (!connected()) {
|
|
|
|
if (!connect())
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ByteArrayOutputStream out = new ByteArrayOutputStream(initialSize);
|
|
|
|
EepGet get = new I2PSocketEepGet(_context, _manager, retries, -1, maxSize, null, out, fetchURL);
|
2013-12-15 15:16:54 +00:00
|
|
|
get.addHeader("User-Agent", EEPGET_USER_AGENT);
|
2012-11-05 17:20:07 +00:00
|
|
|
if (get.fetch(timeout)) {
|
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug("Fetch successful [" + url + "]: size=" + out.size());
|
|
|
|
return out.toByteArray();
|
|
|
|
} else {
|
|
|
|
if (_log.shouldLog(Log.WARN))
|
|
|
|
_log.warn("Fetch failed [" + url + "]");
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2015-05-15 20:51:45 +00:00
|
|
|
public String getOurIPString() {
|
2010-12-20 19:37:38 +00:00
|
|
|
Destination dest = getMyDestination();
|
|
|
|
if (dest != null)
|
|
|
|
return dest.toBase64();
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return dest or null
|
|
|
|
* @since 0.8.4
|
|
|
|
*/
|
|
|
|
Destination getMyDestination() {
|
2008-04-07 17:41:58 +00:00
|
|
|
if (_manager == null)
|
2010-12-20 19:37:38 +00:00
|
|
|
return null;
|
2005-12-16 23:18:56 +00:00
|
|
|
I2PSession sess = _manager.getSession();
|
2010-12-20 19:37:38 +00:00
|
|
|
if (sess != null)
|
|
|
|
return sess.getMyDestination();
|
|
|
|
return null;
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
2008-11-16 17:11:53 +00:00
|
|
|
|
|
|
|
/** Base64 only - static (no naming service) */
|
|
|
|
static Destination getDestinationFromBase64(String ip) {
|
|
|
|
if (ip == null) return null;
|
|
|
|
if (ip.endsWith(".i2p")) {
|
|
|
|
if (ip.length() < 520)
|
|
|
|
return null;
|
|
|
|
try {
|
|
|
|
return new Destination(ip.substring(0, ip.length()-4)); // sans .i2p
|
|
|
|
} catch (DataFormatException dfe) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
return new Destination(ip);
|
|
|
|
} catch (DataFormatException dfe) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-26 20:36:44 +00:00
|
|
|
private static final int BASE32_HASH_LENGTH = 52; // 1 + Hash.HASH_LENGTH * 8 / 5
|
|
|
|
|
2008-11-16 17:11:53 +00:00
|
|
|
/** Base64 Hash or Hash.i2p or name.i2p using naming service */
|
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"
|
2010-12-26 20:36:44 +00:00
|
|
|
if (_manager != null && ip.length() == BASE32_HASH_LENGTH + 8 && ip.endsWith(".b32.i2p")) {
|
|
|
|
// Use existing I2PSession for b32 lookups if we have it
|
|
|
|
// This is much more efficient than using the naming service
|
|
|
|
I2PSession sess = _manager.getSession();
|
|
|
|
if (sess != null) {
|
|
|
|
byte[] b = Base32.decode(ip.substring(0, BASE32_HASH_LENGTH));
|
|
|
|
if (b != null) {
|
2012-06-05 01:03:39 +00:00
|
|
|
//Hash h = new Hash(b);
|
|
|
|
Hash h = Hash.create(b);
|
2010-12-26 20:36:44 +00:00
|
|
|
if (_log.shouldLog(Log.INFO))
|
|
|
|
_log.info("Using existing session for lookup of " + ip);
|
|
|
|
try {
|
2013-10-09 13:06:47 +00:00
|
|
|
return sess.lookupDest(h, 15*1000);
|
2010-12-26 20:36:44 +00:00
|
|
|
} catch (I2PSessionException ise) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_log.shouldLog(Log.INFO))
|
|
|
|
_log.info("Using naming service for lookup of " + ip);
|
|
|
|
return _context.namingService().lookup(ip);
|
2008-03-05 14:19:19 +00:00
|
|
|
}
|
2010-12-26 20:36:44 +00:00
|
|
|
if (_log.shouldLog(Log.INFO))
|
|
|
|
_log.info("Creating Destination for " + ip);
|
2008-03-05 14:19:19 +00:00
|
|
|
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 {
|
2010-12-26 20:36:44 +00:00
|
|
|
if (_log.shouldLog(Log.INFO))
|
|
|
|
_log.info("Creating Destination for " + ip);
|
2005-10-19 22:02:37 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2014-11-06 17:45:06 +00:00
|
|
|
/** @param ot non-null list of announce URLs */
|
2012-06-08 16:11:55 +00:00
|
|
|
public void setOpenTrackers(List<String> ot) {
|
|
|
|
_openTrackers = ot;
|
2008-11-15 23:52:40 +00:00
|
|
|
}
|
|
|
|
|
2014-11-06 17:45:06 +00:00
|
|
|
/** List of open tracker announce URLs to use as backups
|
2012-06-08 16:11:55 +00:00
|
|
|
* @return non-null, possibly unmodifiable, empty if disabled
|
2012-06-02 13:49:14 +00:00
|
|
|
*/
|
2010-12-28 15:46:45 +00:00
|
|
|
public List<String> getOpenTrackers() {
|
2008-11-15 23:52:40 +00:00
|
|
|
if (!shouldUseOpenTrackers())
|
2013-11-21 12:43:45 +00:00
|
|
|
return Collections.emptyList();
|
2012-06-08 16:11:55 +00:00
|
|
|
return _openTrackers;
|
2008-11-15 23:52:40 +00:00
|
|
|
}
|
2012-11-05 17:20:07 +00:00
|
|
|
|
|
|
|
/**
|
2014-11-06 17:45:06 +00:00
|
|
|
* Is this announce URL probably for an open tracker?
|
|
|
|
*
|
|
|
|
* @since 0.9.17
|
|
|
|
*/
|
|
|
|
public boolean isKnownOpenTracker(String url) {
|
|
|
|
try {
|
2015-11-07 22:38:05 +00:00
|
|
|
URI u = new URI(url);
|
2014-11-06 17:45:06 +00:00
|
|
|
String host = u.getHost();
|
|
|
|
return host != null && SnarkManager.KNOWN_OPENTRACKERS.contains(host);
|
2015-11-07 22:38:05 +00:00
|
|
|
} catch (URISyntaxException use) {
|
2014-11-06 17:45:06 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* List of open tracker announce URLs to use as backups even if disabled
|
2012-11-05 17:20:07 +00:00
|
|
|
* @return non-null
|
|
|
|
* @since 0.9.4
|
|
|
|
*/
|
|
|
|
public List<String> getBackupTrackers() {
|
|
|
|
return _openTrackers;
|
|
|
|
}
|
2008-11-15 23:52:40 +00:00
|
|
|
|
2010-12-28 15:46:45 +00:00
|
|
|
public void setUseOpenTrackers(boolean yes) {
|
|
|
|
_shouldUseOT = yes;
|
|
|
|
}
|
|
|
|
|
2008-11-15 23:52:40 +00:00
|
|
|
public boolean shouldUseOpenTrackers() {
|
2010-12-28 15:46:45 +00:00
|
|
|
return _shouldUseOT;
|
2008-11-15 23:52:40 +00:00
|
|
|
}
|
2012-06-02 18:52:46 +00:00
|
|
|
|
|
|
|
/** @since DHT */
|
|
|
|
public synchronized void setUseDHT(boolean yes) {
|
|
|
|
_shouldUseDHT = yes;
|
|
|
|
if (yes && _manager != null && _dht == null) {
|
2013-01-23 22:44:52 +00:00
|
|
|
_dht = new KRPC(_context, _baseName, _manager.getSession());
|
2012-06-02 18:52:46 +00:00
|
|
|
} else if (!yes && _dht != null) {
|
|
|
|
_dht.stop();
|
|
|
|
_dht = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @since DHT */
|
|
|
|
public boolean shouldUseDHT() {
|
|
|
|
return _shouldUseDHT;
|
|
|
|
}
|
2008-11-15 23:52:40 +00:00
|
|
|
|
2010-12-27 17:13:24 +00:00
|
|
|
/**
|
|
|
|
* Like DataHelper.toHexString but ensures no loss of leading zero bytes
|
|
|
|
* @since 0.8.4
|
|
|
|
*/
|
|
|
|
public static String toHex(byte[] b) {
|
|
|
|
StringBuilder buf = new StringBuilder(40);
|
|
|
|
for (int i = 0; i < b.length; i++) {
|
|
|
|
int bi = b[i] & 0xff;
|
|
|
|
if (bi < 16)
|
|
|
|
buf.append('0');
|
|
|
|
buf.append(Integer.toHexString(bi));
|
|
|
|
}
|
|
|
|
return buf.toString();
|
|
|
|
}
|
|
|
|
|
2009-12-10 02:12:18 +00:00
|
|
|
private static final String BUNDLE_NAME = "org.klomp.snark.web.messages";
|
2009-12-09 20:54:10 +00:00
|
|
|
|
|
|
|
/** lang in routerconsole.lang property, else current locale */
|
|
|
|
public String getString(String key) {
|
|
|
|
return Translate.getString(key, _context, BUNDLE_NAME);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* translate a string with a parameter
|
|
|
|
* This is a lot more expensive than getString(s, ctx), so use sparingly.
|
|
|
|
*
|
|
|
|
* @param s string to be translated containing {0}
|
|
|
|
* The {0} will be replaced by the parameter.
|
2017-01-27 03:03:34 +00:00
|
|
|
* Single quotes must be doubled, i.e. ' -> '' in the string.
|
2009-12-09 20:54:10 +00:00
|
|
|
* @param o parameter, not translated.
|
2015-11-16 19:32:00 +00:00
|
|
|
* To translate parameter also, use _t("foo {0} bar", _t("baz"))
|
2009-12-09 20:54:10 +00:00
|
|
|
* Do not double the single quotes in the parameter.
|
|
|
|
* Use autoboxing to call with ints, longs, floats, etc.
|
|
|
|
*/
|
|
|
|
public String getString(String s, Object o) {
|
|
|
|
return Translate.getString(s, o, _context, BUNDLE_NAME);
|
|
|
|
}
|
2009-12-10 18:08:50 +00:00
|
|
|
|
|
|
|
/** {0} and {1} */
|
|
|
|
public String getString(String s, Object o, Object o2) {
|
|
|
|
return Translate.getString(s, o, o2, _context, BUNDLE_NAME);
|
|
|
|
}
|
2010-05-27 00:38:32 +00:00
|
|
|
|
|
|
|
/** ngettext @since 0.7.14 */
|
|
|
|
public String getString(int n, String s, String p) {
|
|
|
|
return Translate.getString(n, s, p, _context, BUNDLE_NAME);
|
|
|
|
}
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|