forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p' (head 9ca94e960929c6af5dea1085105278d2f33217f2)
to branch 'i2p.i2p.zzz.dhtsnark' (head 1f23a71b0fa5169c220f3f21dd705e1fcfbb1b5d)
This commit is contained in:
@ -325,7 +325,10 @@ abstract class ExtensionHandler {
|
||||
BDecoder dec = new BDecoder(is);
|
||||
BEValue bev = dec.bdecodeMap();
|
||||
Map<String, BEValue> map = bev.getMap();
|
||||
byte[] ids = map.get("added").getBytes();
|
||||
bev = map.get("added");
|
||||
if (bev == null)
|
||||
return;
|
||||
byte[] ids = bev.getBytes();
|
||||
if (ids.length < HASH_LENGTH)
|
||||
return;
|
||||
int len = Math.min(ids.length, (I2PSnarkUtil.MAX_CONNECTIONS - 1) * HASH_LENGTH);
|
||||
|
@ -55,8 +55,9 @@ public class I2PSnarkUtil {
|
||||
private String _i2cpHost;
|
||||
private int _i2cpPort;
|
||||
private final Map<String, String> _opts;
|
||||
private I2PSocketManager _manager;
|
||||
private volatile I2PSocketManager _manager;
|
||||
private boolean _configured;
|
||||
private volatile boolean _connecting;
|
||||
private final Set<Hash> _shitlist;
|
||||
private int _maxUploaders;
|
||||
private int _maxUpBW;
|
||||
@ -200,6 +201,7 @@ public class I2PSnarkUtil {
|
||||
*/
|
||||
synchronized public boolean connect() {
|
||||
if (_manager == null) {
|
||||
_connecting = true;
|
||||
// try to find why reconnecting after stop
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Connecting to I2P", new Exception("I did it"));
|
||||
@ -239,6 +241,7 @@ public class I2PSnarkUtil {
|
||||
if (opts.getProperty("i2p.streaming.maxConnsPerHour") == null)
|
||||
opts.setProperty("i2p.streaming.maxConnsPerHour", "20");
|
||||
_manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);
|
||||
_connecting = false;
|
||||
}
|
||||
// FIXME this only instantiates krpc once, left stuck with old manager
|
||||
if (_shouldUseDHT && _manager != null && _dht == null)
|
||||
@ -254,6 +257,9 @@ public class I2PSnarkUtil {
|
||||
|
||||
public boolean connected() { return _manager != null; }
|
||||
|
||||
/** @since 0.9.1 */
|
||||
public boolean isConnecting() { return _manager == null && _connecting; }
|
||||
|
||||
/**
|
||||
* For FetchAndAdd
|
||||
* @return null if not connected
|
||||
@ -275,8 +281,11 @@ public class I2PSnarkUtil {
|
||||
// FIXME this can cause race NPEs elsewhere
|
||||
_manager = null;
|
||||
_shitlist.clear();
|
||||
if (mgr != null)
|
||||
if (mgr != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Disconnecting from I2P", new Exception("I did it"));
|
||||
mgr.destroySocketManager();
|
||||
}
|
||||
// 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...
|
||||
|
@ -39,13 +39,13 @@ class PeerState implements DataLoader
|
||||
|
||||
// Interesting and choking describes whether we are interested in or
|
||||
// are choking the other side.
|
||||
boolean interesting = false;
|
||||
boolean choking = true;
|
||||
volatile boolean interesting;
|
||||
volatile boolean choking = true;
|
||||
|
||||
// Interested and choked describes whether the other side is
|
||||
// interested in us or choked us.
|
||||
boolean interested = false;
|
||||
boolean choked = true;
|
||||
volatile boolean interested;
|
||||
volatile boolean choked = true;
|
||||
|
||||
/** the pieces the peer has */
|
||||
BitField bitfield;
|
||||
|
@ -250,6 +250,7 @@ public class Snark
|
||||
private String rootDataDir = ".";
|
||||
private final CompleteListener completeListener;
|
||||
private boolean stopped;
|
||||
private boolean starting;
|
||||
private byte[] id;
|
||||
private byte[] infoHash;
|
||||
private String additionalTrackerURL;
|
||||
@ -509,9 +510,19 @@ public class Snark
|
||||
}
|
||||
|
||||
/**
|
||||
* Start up contacting peers and querying the tracker
|
||||
* Start up contacting peers and querying the tracker.
|
||||
* Blocks if tunnel is not yet open.
|
||||
*/
|
||||
public void startTorrent() {
|
||||
public synchronized void startTorrent() {
|
||||
starting = true;
|
||||
try {
|
||||
x_startTorrent();
|
||||
} finally {
|
||||
starting = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void x_startTorrent() {
|
||||
boolean ok = _util.connect();
|
||||
if (!ok) fatal("Unable to connect to I2P");
|
||||
if (coordinator == null) {
|
||||
@ -572,14 +583,24 @@ public class Snark
|
||||
debug("NOT starting TrackerClient???", NOTICE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop contacting the tracker and talking with peers
|
||||
*/
|
||||
public void stopTorrent() {
|
||||
stopTorrent(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop contacting the tracker and talking with peers
|
||||
* @param fast if true, limit the life of the unannounce threads
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public synchronized void stopTorrent(boolean fast) {
|
||||
stopped = true;
|
||||
TrackerClient tc = trackerclient;
|
||||
if (tc != null)
|
||||
tc.halt();
|
||||
tc.halt(fast);
|
||||
PeerCoordinator pc = coordinator;
|
||||
if (pc != null)
|
||||
pc.halt();
|
||||
@ -670,6 +691,22 @@ public class Snark
|
||||
return stopped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Startup in progress.
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public boolean isStarting() {
|
||||
return starting && stopped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set startup in progress.
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public void setStarting() {
|
||||
starting = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.8.4
|
||||
*/
|
||||
|
@ -34,6 +34,8 @@ import net.i2p.util.Log;
|
||||
import net.i2p.util.OrderedProperties;
|
||||
import net.i2p.util.SecureDirectory;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
|
||||
/**
|
||||
* Manage multiple snarks
|
||||
@ -59,6 +61,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
private ConnectionAcceptor _connectionAcceptor;
|
||||
private Thread _monitor;
|
||||
private volatile boolean _running;
|
||||
private volatile boolean _stopping;
|
||||
private final Map<String, Tracker> _trackerMap;
|
||||
|
||||
public static final String PROP_I2CP_HOST = "i2psnark.i2cpHost";
|
||||
@ -146,16 +149,26 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
_connectionAcceptor = new ConnectionAcceptor(_util);
|
||||
_monitor = new I2PAppThread(new DirMonitor(), "Snark DirMonitor", true);
|
||||
_monitor.start();
|
||||
_context.addShutdownTask(new SnarkManagerShutdown());
|
||||
// Not required, Jetty has a shutdown hook
|
||||
//_context.addShutdownTask(new SnarkManagerShutdown());
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the webapp at Jetty shutdown.
|
||||
* Stops all torrents. Does not close the tunnel, so the announces have a chance.
|
||||
* Fix this so an individual webaapp stop will close the tunnel.
|
||||
* Runs inline.
|
||||
*/
|
||||
public void stop() {
|
||||
_running = false;
|
||||
_monitor.interrupt();
|
||||
_connectionAcceptor.halt();
|
||||
(new SnarkManagerShutdown()).run();
|
||||
stopAllTorrents(true);
|
||||
}
|
||||
|
||||
/** @since 0.9.1 */
|
||||
public boolean isStopping() { return _stopping; }
|
||||
|
||||
/** hook to I2PSnarkUtil for the servlet */
|
||||
public I2PSnarkUtil util() { return _util; }
|
||||
|
||||
@ -871,7 +884,7 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
torrent.startTorrent();
|
||||
addMessage(_("Fetching {0}", name));
|
||||
boolean haveSavedPeers = false;
|
||||
if ((!util().connected()) && !haveSavedPeers) {
|
||||
if ((_util.connected()) && !haveSavedPeers) {
|
||||
addMessage(_("We have no saved peers and no other torrents are running. " +
|
||||
"Fetch of {0} will not succeed until you start another torrent.", name));
|
||||
}
|
||||
@ -1603,17 +1616,133 @@ public class SnarkManager implements Snark.CompleteListener {
|
||||
}
|
||||
}
|
||||
|
||||
private class SnarkManagerShutdown extends I2PAppThread {
|
||||
@Override
|
||||
public void run() {
|
||||
Set names = listTorrentFiles();
|
||||
int running = 0;
|
||||
for (Iterator iter = names.iterator(); iter.hasNext(); ) {
|
||||
Snark snark = getTorrent((String)iter.next());
|
||||
if (snark != null && !snark.isStopped()) {
|
||||
snark.stopTorrent();
|
||||
try { Thread.sleep(50); } catch (InterruptedException ie) {}
|
||||
/**
|
||||
* If not connected, thread it, otherwise inline
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public void startTorrent(byte[] infoHash) {
|
||||
for (Snark snark : _snarks.values()) {
|
||||
if (DataHelper.eq(infoHash, snark.getInfoHash())) {
|
||||
if (snark.isStarting() || !snark.isStopped()) {
|
||||
addMessage("Torrent already started");
|
||||
return;
|
||||
}
|
||||
boolean connected = _util.connected();
|
||||
if ((!connected) && !_util.isConnecting())
|
||||
addMessage(_("Opening the I2P tunnel"));
|
||||
addMessage(_("Starting up torrent {0}", snark.getBaseName()));
|
||||
if (connected) {
|
||||
snark.startTorrent();
|
||||
} else {
|
||||
// mark it for the UI
|
||||
snark.setStarting();
|
||||
(new I2PAppThread(new ThreadedStarter(snark), "TorrentStarter", true)).start();
|
||||
try { Thread.sleep(200); } catch (InterruptedException ie) {}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
addMessage("Torrent not found?");
|
||||
}
|
||||
|
||||
/**
|
||||
* If not connected, thread it, otherwise inline
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public void startAllTorrents() {
|
||||
if (_util.connected()) {
|
||||
startAll();
|
||||
} else {
|
||||
addMessage(_("Opening the I2P tunnel and starting all torrents."));
|
||||
for (Snark snark : _snarks.values()) {
|
||||
// mark it for the UI
|
||||
snark.setStarting();
|
||||
}
|
||||
(new I2PAppThread(new ThreadedStarter(null), "TorrentStarterAll", true)).start();
|
||||
try { Thread.sleep(200); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use null constructor param for all
|
||||
* @since 0.9.1
|
||||
*/
|
||||
private class ThreadedStarter implements Runnable {
|
||||
private final Snark snark;
|
||||
public ThreadedStarter(Snark s) { snark = s; }
|
||||
public void run() {
|
||||
if (snark != null) {
|
||||
if (snark.isStopped())
|
||||
snark.startTorrent();
|
||||
} else {
|
||||
startAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline
|
||||
* @since 0.9.1
|
||||
*/
|
||||
private void startAll() {
|
||||
for (Snark snark : _snarks.values()) {
|
||||
if (snark.isStopped())
|
||||
snark.startTorrent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all running torrents, and close the tunnel after a delay
|
||||
* to allow for announces.
|
||||
* If called at router shutdown via Jetty shutdown hook -> webapp destroy() -> stop(),
|
||||
* the tunnel won't actually be closed as the SimpleScheduler is already shutdown
|
||||
* or will be soon, so we delay a few seconds inline.
|
||||
* @param finalShutdown if true, sleep at the end if any torrents were running
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public void stopAllTorrents(boolean finalShutdown) {
|
||||
_stopping = true;
|
||||
if (finalShutdown && _log.shouldLog(Log.WARN))
|
||||
_log.warn("SnarkManager final shutdown");
|
||||
int count = 0;
|
||||
for (Snark snark : _snarks.values()) {
|
||||
if (!snark.isStopped()) {
|
||||
if (count == 0)
|
||||
addMessage(_("Stopping all torrents and closing the I2P tunnel."));
|
||||
count++;
|
||||
if (finalShutdown)
|
||||
snark.stopTorrent(true);
|
||||
else
|
||||
stopTorrent(snark, false);
|
||||
// Throttle since every unannounce is now threaded.
|
||||
// How to do this without creating a ton of threads?
|
||||
try { Thread.sleep(20); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
if (_util.connected()) {
|
||||
if (count > 0) {
|
||||
// Schedule this even for final shutdown, as there's a chance
|
||||
// that it's just this webapp that is stopping.
|
||||
SimpleScheduler.getInstance().addEvent(new Disconnector(), 60*1000);
|
||||
addMessage(_("Closing I2P tunnel after notifying trackers."));
|
||||
if (finalShutdown) {
|
||||
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
|
||||
}
|
||||
} else {
|
||||
_util.disconnect();
|
||||
_stopping = false;
|
||||
addMessage(_("I2P tunnel closed."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @since 0.9.1 */
|
||||
private class Disconnector implements SimpleTimer.TimedEvent {
|
||||
public void timeReached() {
|
||||
if (_util.connected()) {
|
||||
_util.disconnect();
|
||||
_stopping = false;
|
||||
addMessage(_("I2P tunnel closed."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import net.i2p.util.I2PAppThread;
|
||||
|
||||
/**
|
||||
* Makes sure everything ends correctly when shutting down.
|
||||
* @deprecated unused
|
||||
*/
|
||||
public class SnarkShutdown extends I2PAppThread
|
||||
{
|
||||
@ -61,7 +62,7 @@ public class SnarkShutdown extends I2PAppThread
|
||||
|
||||
//Snark.debug("Halting TrackerClient...", Snark.INFO);
|
||||
if (trackerclient != null)
|
||||
trackerclient.halt();
|
||||
trackerclient.halt(true);
|
||||
|
||||
//Snark.debug("Halting PeerCoordinator...", Snark.INFO);
|
||||
if (coordinator != null)
|
||||
|
@ -28,6 +28,7 @@ import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@ -97,6 +98,7 @@ public class TrackerClient implements Runnable {
|
||||
// these 2 used in loop()
|
||||
private volatile boolean runStarted;
|
||||
private volatile int consecutiveFails;
|
||||
private volatile boolean _fastUnannounce;
|
||||
|
||||
private final List<Tracker> trackers;
|
||||
|
||||
@ -134,6 +136,7 @@ public class TrackerClient implements Runnable {
|
||||
stop = false;
|
||||
consecutiveFails = 0;
|
||||
runStarted = false;
|
||||
_fastUnannounce = false;
|
||||
_thread = new I2PAppThread(this, _threadName + " #" + (++_runCount), true);
|
||||
_thread.start();
|
||||
started = true;
|
||||
@ -144,8 +147,9 @@ public class TrackerClient implements Runnable {
|
||||
|
||||
/**
|
||||
* Interrupts this Thread to stop it.
|
||||
* @param fast if true, limit the life of the unannounce threads
|
||||
*/
|
||||
public synchronized void halt() {
|
||||
public synchronized void halt(boolean fast) {
|
||||
boolean wasStopped = stop;
|
||||
if (wasStopped) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
@ -168,6 +172,7 @@ public class TrackerClient implements Runnable {
|
||||
_log.debug("Interrupting " + t.getName());
|
||||
t.interrupt();
|
||||
}
|
||||
_fastUnannounce = true;
|
||||
if (!wasStopped)
|
||||
unannounce();
|
||||
}
|
||||
@ -236,7 +241,7 @@ public class TrackerClient implements Runnable {
|
||||
* Do this one time only (not every time it is started).
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public void setup() {
|
||||
private void setup() {
|
||||
// Construct the list of trackers for this torrent,
|
||||
// starting with the primary one listed in the metainfo,
|
||||
// followed by the secondary open trackers
|
||||
@ -415,6 +420,9 @@ public class TrackerClient implements Runnable {
|
||||
tr.interval = LONG_SLEEP; // slow down
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_util.debug("Not announcing to " + tr.announce + " last announce was " +
|
||||
new Date(tr.lastRequestTime) + " interval is " + DataHelper.formatDuration(tr.interval), Snark.INFO);
|
||||
}
|
||||
if ((!tr.stop) && maxSeenPeers < tr.seenPeers)
|
||||
maxSeenPeers = tr.seenPeers;
|
||||
@ -439,6 +447,8 @@ public class TrackerClient implements Runnable {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_util.debug("Not getting PEX peers", Snark.INFO);
|
||||
}
|
||||
|
||||
// Get peers from DHT
|
||||
@ -476,6 +486,8 @@ public class TrackerClient implements Runnable {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_util.debug("Not getting DHT peers", Snark.INFO);
|
||||
}
|
||||
|
||||
|
||||
@ -534,7 +546,7 @@ public class TrackerClient implements Runnable {
|
||||
if (_util.connected() &&
|
||||
tr.started && (!tr.stop) && tr.trackerProblems == null) {
|
||||
try {
|
||||
(new I2PAppThread(new Unannouncer(tr), _threadName + " Unannounce " + (++i), true)).start();
|
||||
(new I2PAppThread(new Unannouncer(tr), _threadName + " U" + (++i), true)).start();
|
||||
} catch (OutOfMemoryError oom) {
|
||||
// probably ran out of threads, ignore
|
||||
tr.reset();
|
||||
@ -611,8 +623,9 @@ public class TrackerClient implements Runnable {
|
||||
_util.debug("Sending TrackerClient request: " + s, Snark.INFO);
|
||||
|
||||
tr.lastRequestTime = System.currentTimeMillis();
|
||||
// Don't wait for a response to stopped.
|
||||
File fetched = _util.get(s, true, event.equals(STOPPED_EVENT) ? -1 : 0);
|
||||
// Don't wait for a response to stopped when shutting down
|
||||
boolean fast = _fastUnannounce && event.equals(STOPPED_EVENT);
|
||||
File fetched = _util.get(s, true, fast ? -1 : 0);
|
||||
if (fetched == null) {
|
||||
throw new IOException("Error fetching " + s);
|
||||
}
|
||||
@ -671,7 +684,7 @@ public class TrackerClient implements Runnable {
|
||||
* @return true for i2p hosts only
|
||||
* @since 0.7.12
|
||||
*/
|
||||
static boolean isValidAnnounce(String ann) {
|
||||
public static boolean isValidAnnounce(String ann) {
|
||||
URL url;
|
||||
try {
|
||||
url = new URL(ann);
|
||||
|
@ -74,6 +74,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_log = _context.logManager().getLog(I2PSnarkServlet.class);
|
||||
_nonce = _context.random().nextLong();
|
||||
// FIXME instantiate new one every time
|
||||
_manager = SnarkManager.instance();
|
||||
String configFile = _context.getProperty(PROP_CONFIG_FILE);
|
||||
if ( (configFile == null) || (configFile.trim().length() <= 0) )
|
||||
@ -322,7 +323,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
final long stats[] = {0,0,0,0,0,0};
|
||||
String peerParam = req.getParameter("p");
|
||||
|
||||
List snarks = getSortedSnarks(req);
|
||||
List<Snark> snarks = getSortedSnarks(req);
|
||||
boolean isForm = _manager.util().connected() || !snarks.isEmpty();
|
||||
if (isForm) {
|
||||
out.write("<form action=\"_post\" method=\"POST\">\n");
|
||||
@ -407,7 +408,9 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
ua.startsWith("Dillo"));
|
||||
|
||||
boolean noThinsp = isDegraded || (ua != null && ua.startsWith("Opera"));
|
||||
if (_manager.util().connected()) {
|
||||
if (_manager.isStopping()) {
|
||||
out.write(" ");
|
||||
} else if (_manager.util().connected()) {
|
||||
if (isDegraded)
|
||||
out.write("<a href=\"/i2psnark/?action=StopAll&nonce=" + _nonce + "\"><img title=\"");
|
||||
else {
|
||||
@ -421,7 +424,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
out.write("\">");
|
||||
if (isDegraded)
|
||||
out.write("</a>");
|
||||
} else if (!snarks.isEmpty()) {
|
||||
} else if ((!_manager.util().isConnecting()) && !snarks.isEmpty()) {
|
||||
if (isDegraded)
|
||||
out.write("<a href=\"/i2psnark/?action=StartAll&nonce=" + _nonce + "\"><img title=\"");
|
||||
else
|
||||
@ -572,14 +575,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
if (torrent != null) {
|
||||
byte infoHash[] = Base64.decode(torrent);
|
||||
if ( (infoHash != null) && (infoHash.length == 20) ) { // valid sha1
|
||||
for (String name : _manager.listTorrentFiles()) {
|
||||
Snark snark = _manager.getTorrent(name);
|
||||
if ( (snark != null) && (DataHelper.eq(infoHash, snark.getInfoHash())) ) {
|
||||
snark.startTorrent();
|
||||
_manager.addMessage(_("Starting up torrent {0}", snark.getBaseName()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
_manager.startTorrent(infoHash);
|
||||
}
|
||||
}
|
||||
} else if (action.startsWith("Remove_")) {
|
||||
@ -644,10 +640,11 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
// multifile torrents have the getFiles() return lists of lists of filenames, but
|
||||
// each of those lists just contain a single file afaict...
|
||||
File df = Storage.getFileFromNames(f, files.get(i));
|
||||
if (df.delete())
|
||||
_manager.addMessage(_("Data file deleted: {0}", df.getAbsolutePath()));
|
||||
else
|
||||
if (df.delete()) {
|
||||
//_manager.addMessage(_("Data file deleted: {0}", df.getAbsolutePath()));
|
||||
} else {
|
||||
_manager.addMessage(_("Data file could not be deleted: {0}", df.getAbsolutePath()));
|
||||
}
|
||||
}
|
||||
// step 2 make Set of dirs with reverse sort
|
||||
Set<File> dirs = new TreeSet(Collections.reverseOrder());
|
||||
@ -659,16 +656,20 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
// step 3 delete dirs bottom-up
|
||||
for (File df : dirs) {
|
||||
if (df.delete()) {
|
||||
_manager.addMessage(_("Data dir deleted: {0}", df.getAbsolutePath()));
|
||||
} else if (_log.shouldLog(Log.WARN)) {
|
||||
_log.warn("Could not delete dir " + df);
|
||||
//_manager.addMessage(_("Data dir deleted: {0}", df.getAbsolutePath()));
|
||||
} else {
|
||||
_manager.addMessage(_("Directory could not be deleted: {0}", df.getAbsolutePath()));
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Could not delete dir " + df);
|
||||
}
|
||||
}
|
||||
// step 4 delete base
|
||||
if (f.delete()) {
|
||||
_manager.addMessage(_("Data dir deleted: {0}", f.getAbsolutePath()));
|
||||
} else if (_log.shouldLog(Log.WARN)) {
|
||||
_log.warn("Could not delete dir " + f);
|
||||
_manager.addMessage(_("Directory deleted: {0}", f.getAbsolutePath()));
|
||||
} else {
|
||||
_manager.addMessage(_("Directory could not be deleted: {0}", f.getAbsolutePath()));
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Could not delete dir " + f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -740,29 +741,9 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
_manager.addMessage(_("Error creating torrent - you must enter a file or directory"));
|
||||
}
|
||||
} else if ("StopAll".equals(action)) {
|
||||
_manager.addMessage(_("Stopping all torrents and closing the I2P tunnel."));
|
||||
List snarks = getSortedSnarks(req);
|
||||
for (int i = 0; i < snarks.size(); i++) {
|
||||
Snark snark = (Snark)snarks.get(i);
|
||||
if (!snark.isStopped()) {
|
||||
_manager.stopTorrent(snark, false);
|
||||
try { Thread.sleep(50); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
if (_manager.util().connected()) {
|
||||
// Give the stopped announces time to get out
|
||||
try { Thread.sleep(2000); } catch (InterruptedException ie) {}
|
||||
_manager.util().disconnect();
|
||||
_manager.addMessage(_("I2P tunnel closed."));
|
||||
}
|
||||
_manager.stopAllTorrents(false);
|
||||
} else if ("StartAll".equals(action)) {
|
||||
_manager.addMessage(_("Opening the I2P tunnel and starting all torrents."));
|
||||
List snarks = getSortedSnarks(req);
|
||||
for (int i = 0; i < snarks.size(); i++) {
|
||||
Snark snark = (Snark)snarks.get(i);
|
||||
if (snark.isStopped())
|
||||
snark.startTorrent();
|
||||
}
|
||||
_manager.startAllTorrents();
|
||||
} else if ("Clear".equals(action)) {
|
||||
_manager.clearMessages();
|
||||
} else {
|
||||
@ -826,7 +807,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
name = name.trim();
|
||||
hurl = hurl.trim();
|
||||
aurl = aurl.trim().replace("=", "=");
|
||||
if (name.length() > 0 && hurl.startsWith("http://") && aurl.startsWith("http://")) {
|
||||
if (name.length() > 0 && hurl.startsWith("http://") && TrackerClient.isValidAnnounce(aurl)) {
|
||||
Map<String, Tracker> trackers = _manager.getTrackerMap();
|
||||
trackers.put(name, new Tracker(name, aurl, hurl));
|
||||
_manager.saveTrackerMap();
|
||||
@ -998,6 +979,8 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "trackererror.png\" title=\"" + err + "\"></td><td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Tracker Error") +
|
||||
"<br>" + err;
|
||||
}
|
||||
} else if (snark.isStarting()) {
|
||||
statusString = "<img alt=\"\" border=\"0\" src=\"" + _imgPath + "stalled.png\" ></td><td class=\"snarkTorrentStatus " + rowClass + "\">" + _("Starting");
|
||||
} else if (remaining == 0 || needed == 0) { // < 0 means no meta size yet
|
||||
// partial complete or seeding
|
||||
if (isRunning) {
|
||||
@ -1150,6 +1133,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
if (showPeers)
|
||||
parameters = parameters + "&p=1";
|
||||
if (isRunning) {
|
||||
// Stop Button
|
||||
if (isDegraded)
|
||||
out.write("<a href=\"/i2psnark/?action=Stop_" + b64 + "&nonce=" + _nonce + "\"><img title=\"");
|
||||
else
|
||||
@ -1160,7 +1144,9 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
out.write("\">");
|
||||
if (isDegraded)
|
||||
out.write("</a>");
|
||||
} else {
|
||||
} else if (!snark.isStarting()) {
|
||||
if (!_manager.isStopping()) {
|
||||
// Start Button
|
||||
// This works in Opera but it's displayed a little differently, so use noThinsp here too so all 3 icons are consistent
|
||||
if (noThinsp)
|
||||
out.write("<a href=\"/i2psnark/?action=Start_" + b64 + "&nonce=" + _nonce + "\"><img title=\"");
|
||||
@ -1172,8 +1158,9 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
out.write("\">");
|
||||
if (isDegraded)
|
||||
out.write("</a>");
|
||||
|
||||
}
|
||||
if (isValid) {
|
||||
// Remove Button
|
||||
// Doesnt work with Opera so use noThinsp instead of isDegraded
|
||||
if (noThinsp)
|
||||
out.write("<a href=\"/i2psnark/?action=Remove_" + b64 + "&nonce=" + _nonce + "\"><img title=\"");
|
||||
@ -1193,6 +1180,7 @@ public class I2PSnarkServlet extends DefaultServlet {
|
||||
out.write("</a>");
|
||||
}
|
||||
|
||||
// Delete Button
|
||||
// Doesnt work with Opera so use noThinsp instead of isDegraded
|
||||
if (noThinsp)
|
||||
out.write("<a href=\"/i2psnark/?action=Delete_" + b64 + "&nonce=" + _nonce + "\"><img title=\"");
|
||||
|
@ -46,6 +46,7 @@ import java.util.ArrayList;
|
||||
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.StringTokenizer;
|
||||
@ -192,7 +193,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
}
|
||||
}
|
||||
|
||||
/** @return non-null */
|
||||
/** @return A copy, non-null */
|
||||
List<I2PSession> getSessions() {
|
||||
return new ArrayList(_sessions);
|
||||
}
|
||||
@ -207,6 +208,11 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
_sessions.remove(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic options used for clients and servers.
|
||||
* NOT a copy, Do NOT modify for per-connection options, make a copy.
|
||||
* @return NOT a copy, do NOT modify for per-connection options
|
||||
*/
|
||||
public Properties getClientOptions() { return _clientOptions; }
|
||||
|
||||
private void addtask(I2PTunnelTask tsk) {
|
||||
@ -326,10 +332,14 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
|
||||
/**
|
||||
* Configure the extra I2CP options to use in any subsequent I2CP sessions.
|
||||
* Generic options used for clients and servers
|
||||
* Usage: "clientoptions[ key=value]*" .
|
||||
*
|
||||
* Sets the event "clientoptions_onResult" = "ok" after completion.
|
||||
*
|
||||
* Deprecated To be made private, use setClientOptions().
|
||||
* This does NOT update a running TunnelTask.
|
||||
*
|
||||
* @param args each args[i] is a key=value pair to add to the options
|
||||
* @param l logger to receive events and output
|
||||
*/
|
||||
@ -347,6 +357,27 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
||||
notifyEvent("clientoptions_onResult", "ok");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic options used for clients and servers.
|
||||
* This DOES update a running TunnelTask, but NOT the session.
|
||||
* A more efficient runClientOptions().
|
||||
*
|
||||
* @param opts non-null
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public void setClientOptions(Properties opts) {
|
||||
for (Iterator iter = _clientOptions.keySet().iterator(); iter.hasNext();) {
|
||||
Object key = iter.next();
|
||||
if (!opts.containsKey(key))
|
||||
iter.remove();
|
||||
}
|
||||
_clientOptions.putAll(opts);
|
||||
for (I2PTunnelTask task : tasks) {
|
||||
task.optionsUpdated(this);
|
||||
}
|
||||
notifyEvent("clientoptions_onResult", "ok");
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the server pointing at the host and port specified using the private i2p
|
||||
* destination loaded from the specified file. <p />
|
||||
|
@ -471,8 +471,9 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
}
|
||||
|
||||
/**
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
* Create the default options (using the default timeout, etc).
|
||||
* Warning, this does not make a copy of I2PTunnel's client options,
|
||||
* it modifies them directly.
|
||||
*/
|
||||
protected I2PSocketOptions getDefaultOptions() {
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
@ -483,8 +484,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
}
|
||||
|
||||
/**
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
* Create the default options (using the default timeout, etc).
|
||||
* Warning, this does not make a copy of I2PTunnel's client options,
|
||||
* it modifies them directly.
|
||||
* Do not use overrides for per-socket options.
|
||||
*/
|
||||
protected I2PSocketOptions getDefaultOptions(Properties overrides) {
|
||||
Properties defaultOpts = getTunnel().getClientOptions();
|
||||
@ -495,6 +498,22 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
return opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the I2PSocketManager.
|
||||
*
|
||||
* @since 0.9.1
|
||||
*/
|
||||
@Override
|
||||
public void optionsUpdated(I2PTunnel tunnel) {
|
||||
if (getTunnel() != tunnel)
|
||||
return;
|
||||
I2PSocketManager sm = _ownDest ? sockMgr : socketManager;
|
||||
if (sm == null)
|
||||
return;
|
||||
Properties props = tunnel.getClientOptions();
|
||||
sm.setDefaultOptions(sockMgr.buildOptions(props));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new I2PSocket towards to the specified destination,
|
||||
* adding it to the list of connections actually managed by this
|
||||
|
@ -132,8 +132,9 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
|
||||
}
|
||||
|
||||
/**
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
* Create the default options (using the default timeout, etc).
|
||||
* Warning, this does not make a copy of I2PTunnel's client options,
|
||||
* it modifies them directly.
|
||||
*/
|
||||
@Override
|
||||
protected I2PSocketOptions getDefaultOptions() {
|
||||
|
@ -225,7 +225,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
}
|
||||
|
||||
/**
|
||||
* create the default options (using the default timeout, etc)
|
||||
* Create the default options (using the default timeout, etc).
|
||||
* Warning, this does not make a copy of I2PTunnel's client options,
|
||||
* it modifies them directly.
|
||||
* unused?
|
||||
*/
|
||||
@Override
|
||||
@ -244,8 +246,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
}
|
||||
|
||||
/**
|
||||
* create the default options (using the default timeout, etc)
|
||||
*
|
||||
* Create the default options (using the default timeout, etc).
|
||||
* Warning, this does not make a copy of I2PTunnel's client options,
|
||||
* it modifies them directly.
|
||||
* Do not use overrides for per-socket options.
|
||||
*/
|
||||
@Override
|
||||
protected I2PSocketOptions getDefaultOptions(Properties overrides) {
|
||||
@ -330,6 +334,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
String ahelperKey = null;
|
||||
String userAgent = null;
|
||||
String authorization = null;
|
||||
int remotePort = 0;
|
||||
while((line = reader.readLine(method)) != null) {
|
||||
line = line.trim();
|
||||
if(_log.shouldLog(Log.DEBUG)) {
|
||||
@ -486,10 +491,12 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
// Host becomes the destination's "{b32}.b32.i2p" string, or "i2p" on lookup failure
|
||||
host = getHostName(destination);
|
||||
|
||||
if(requestURI.getPort() >= 0) {
|
||||
// TODO support I2P ports someday
|
||||
//if (port >= 0)
|
||||
// host = host + ':' + port;
|
||||
int rPort = requestURI.getPort();
|
||||
if (rPort > 0) {
|
||||
// Save it to put in the I2PSocketOptions,
|
||||
remotePort = rPort;
|
||||
/********
|
||||
// but strip it from the URL
|
||||
if(_log.shouldLog(Log.WARN)) {
|
||||
_log.warn(getPrefix(requestId) + "Removing port from [" + request + "]");
|
||||
}
|
||||
@ -500,6 +507,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
method = null;
|
||||
break;
|
||||
}
|
||||
******/
|
||||
} else {
|
||||
remotePort = 80;
|
||||
}
|
||||
|
||||
String query = requestURI.getRawQuery();
|
||||
@ -959,12 +969,16 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
return;
|
||||
}
|
||||
|
||||
Properties opts = new Properties();
|
||||
//Properties opts = new Properties();
|
||||
//opts.setProperty("i2p.streaming.inactivityTimeout", ""+120*1000);
|
||||
// 1 == disconnect. see ConnectionOptions in the new streaming lib, which i
|
||||
// dont want to hard link to here
|
||||
//opts.setProperty("i2p.streaming.inactivityTimeoutAction", ""+1);
|
||||
I2PSocket i2ps = createI2PSocket(clientDest, getDefaultOptions(opts));
|
||||
//I2PSocketOptions sktOpts = getDefaultOptions(opts);
|
||||
I2PSocketOptions sktOpts = getDefaultOptions();
|
||||
if (remotePort > 0)
|
||||
sktOpts.setPort(remotePort);
|
||||
I2PSocket i2ps = createI2PSocket(clientDest, sktOpts);
|
||||
byte[] data = newRequest.toString().getBytes("ISO-8859-1");
|
||||
Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId);
|
||||
new I2PTunnelHTTPClientRunner(s, i2ps, sockLock, data, mySockets, onTimeout);
|
||||
@ -1174,8 +1188,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
}
|
||||
public static final String DEFAULT_JUMP_SERVERS =
|
||||
"http://i2host.i2p/cgi-bin/i2hostjump?," +
|
||||
"http://stats.i2p/cgi-bin/jump.cgi?a=," +
|
||||
"http://i2jump.i2p/";
|
||||
"http://stats.i2p/cgi-bin/jump.cgi?a=";
|
||||
//"http://i2jump.i2p/";
|
||||
|
||||
/**
|
||||
* @param jumpServers comma- or space-separated list, or null
|
||||
@ -1207,15 +1221,21 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
StringTokenizer tok = new StringTokenizer(jumpServers, ", ");
|
||||
while(tok.hasMoreTokens()) {
|
||||
String jurl = tok.nextToken();
|
||||
if(!jurl.startsWith("http://")) {
|
||||
String jumphost;
|
||||
try {
|
||||
URI jURI = new URI(jurl);
|
||||
String proto = jURI.getScheme();
|
||||
jumphost = jURI.getHost();
|
||||
if (proto == null || jumphost == null ||
|
||||
!proto.toLowerCase(Locale.US).equals("http"))
|
||||
continue;
|
||||
jumphost = jumphost.toLowerCase(Locale.US);
|
||||
if (!jumphost.endsWith(".i2p"))
|
||||
continue;
|
||||
} catch(URISyntaxException use) {
|
||||
continue;
|
||||
}
|
||||
// Skip jump servers we don't know
|
||||
String jumphost = jurl.substring(7); // "http://"
|
||||
jumphost = jumphost.substring(0, jumphost.indexOf('/'));
|
||||
if(!jumphost.endsWith(".i2p")) {
|
||||
continue;
|
||||
}
|
||||
if(!jumphost.endsWith(".b32.i2p")) {
|
||||
Destination dest = I2PAppContext.getGlobalContext().namingService().lookup(jumphost);
|
||||
if(dest == null) {
|
||||
@ -1227,8 +1247,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
out.write(jurl.getBytes());
|
||||
out.write(uri.getBytes());
|
||||
out.write("\">".getBytes());
|
||||
out.write(jurl.substring(7).getBytes());
|
||||
out.write(uri.getBytes());
|
||||
// Translators: parameter is a host name
|
||||
out.write(_("{0} jump service", jumphost).getBytes());
|
||||
out.write("</a>\n".getBytes());
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
}
|
||||
|
||||
private void setupI2PTunnelHTTPServer(String spoofHost) {
|
||||
_spoofHost = spoofHost;
|
||||
_spoofHost = (spoofHost != null && spoofHost.trim().length() > 0) ? spoofHost.trim() : null;
|
||||
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 });
|
||||
getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpNullWorkaround", "How often an http server works around a streaming lib or i2ptunnel bug", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000 });
|
||||
}
|
||||
@ -96,6 +96,9 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
*/
|
||||
@Override
|
||||
protected void blockingHandle(I2PSocket socket) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Incoming connection to '" + toString() + "' port " + socket.getLocalPort() +
|
||||
" from: " + socket.getPeerDestination().calculateHash() + " port " + socket.getPort());
|
||||
long afterAccept = getTunnel().getContext().clock().now();
|
||||
long afterSocket = -1;
|
||||
//local is fast, so synchronously. Does not need that many
|
||||
@ -115,8 +118,21 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
addEntry(headers, DEST32_HEADER, Base32.encode(socket.getPeerDestination().calculateHash().getData()) + ".b32.i2p");
|
||||
addEntry(headers, DEST64_HEADER, socket.getPeerDestination().toBase64());
|
||||
|
||||
if ( (_spoofHost != null) && (_spoofHost.trim().length() > 0) )
|
||||
setEntry(headers, "Host", _spoofHost);
|
||||
// Port-specific spoofhost
|
||||
Properties opts = getTunnel().getClientOptions();
|
||||
String spoofHost;
|
||||
int ourPort = socket.getLocalPort();
|
||||
if (ourPort != 80 && ourPort > 0 && ourPort <= 65535 && opts != null) {
|
||||
String portSpoof = opts.getProperty("spoofedHost." + ourPort);
|
||||
if (portSpoof != null)
|
||||
spoofHost = portSpoof.trim();
|
||||
else
|
||||
spoofHost = _spoofHost;
|
||||
} else {
|
||||
spoofHost = _spoofHost;
|
||||
}
|
||||
if (spoofHost != null)
|
||||
setEntry(headers, "Host", spoofHost);
|
||||
setEntry(headers, "Connection", "close");
|
||||
// we keep the enc sent by the browser before clobbering it, since it may have
|
||||
// been x-i2p-gzip
|
||||
@ -134,7 +150,6 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
// request from the socket, modifies the headers, sends the request to the
|
||||
// server, reads the response headers, rewriting to include Content-encoding: x-i2p-gzip
|
||||
// if it was one of the Accept-encoding: values, and gzip the payload
|
||||
Properties opts = getTunnel().getClientOptions();
|
||||
boolean allowGZIP = true;
|
||||
if (opts != null) {
|
||||
String val = opts.getProperty("i2ptunnel.gzip");
|
||||
|
@ -111,6 +111,9 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
|
||||
|
||||
@Override
|
||||
protected void blockingHandle(I2PSocket socket) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Incoming connection to '" + toString() + "' port " + socket.getLocalPort() +
|
||||
" from: " + socket.getPeerDestination().calculateHash() + " port " + socket.getPort());
|
||||
try {
|
||||
String modifiedRegistration;
|
||||
if(!this.method.equals("webirc")) {
|
||||
|
@ -295,6 +295,19 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the I2PSocketManager.
|
||||
*
|
||||
* @since 0.9.1
|
||||
*/
|
||||
@Override
|
||||
public void optionsUpdated(I2PTunnel tunnel) {
|
||||
if (getTunnel() != tunnel || sockMgr == null)
|
||||
return;
|
||||
Properties props = tunnel.getClientOptions();
|
||||
sockMgr.setDefaultOptions(sockMgr.buildOptions(props));
|
||||
}
|
||||
|
||||
protected int getHandlerCount() {
|
||||
int rv = DEFAULT_HANDLER_COUNT;
|
||||
String cnt = getTunnel().getClientOptions().getProperty(PROP_HANDLER_COUNT);
|
||||
@ -408,7 +421,8 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
|
||||
protected void blockingHandle(I2PSocket socket) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Incoming connection to '" + toString() + "' from: " + socket.getPeerDestination().calculateHash().toBase64());
|
||||
_log.info("Incoming connection to '" + toString() + "' port " + socket.getLocalPort() +
|
||||
" from: " + socket.getPeerDestination().calculateHash() + " port " + socket.getPort());
|
||||
long afterAccept = I2PAppContext.getGlobalContext().clock().now();
|
||||
long afterSocket = -1;
|
||||
//local is fast, so synchronously. Does not need that many
|
||||
|
@ -58,6 +58,15 @@ public abstract class I2PTunnelTask extends EventDispatcherImpl {
|
||||
|
||||
public abstract boolean close(boolean forced);
|
||||
|
||||
/**
|
||||
* Notify the task that I2PTunnel's options have been updated.
|
||||
* Extending classes should override and call I2PTunnel.getClientOptions(),
|
||||
* then update the I2PSocketManager.
|
||||
*
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public void optionsUpdated(I2PTunnel tunnel) {}
|
||||
|
||||
/**
|
||||
* For tasks that don't call I2PTunnel.addSession() directly
|
||||
* @since 0.8.13
|
||||
|
@ -4,8 +4,10 @@ import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
|
||||
@ -326,6 +328,7 @@ public class TunnelController implements Logging {
|
||||
_log.info("Releasing session " + s);
|
||||
TunnelControllerGroup.getInstance().release(this, s);
|
||||
}
|
||||
// _sessions.clear() ????
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("No sessions to release? for " + getName());
|
||||
@ -384,20 +387,27 @@ public class TunnelController implements Logging {
|
||||
}
|
||||
}
|
||||
|
||||
private void setSessionOptions() {
|
||||
List opts = new ArrayList();
|
||||
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = _config.getProperty(key);
|
||||
/**
|
||||
* These are the ones stored with a prefix of "option."
|
||||
*
|
||||
* @return keys with the "option." prefix stripped
|
||||
* @since 0.9.1 Much better than getClientOptions()
|
||||
*/
|
||||
public Properties getClientOptionProps() {
|
||||
Properties opts = new Properties();
|
||||
for (Map.Entry e : _config.entrySet()) {
|
||||
String key = (String) e.getKey();
|
||||
if (key.startsWith("option.")) {
|
||||
key = key.substring("option.".length());
|
||||
opts.add(key + "=" + val);
|
||||
String val = (String) e.getValue();
|
||||
opts.setProperty(key, val);
|
||||
}
|
||||
}
|
||||
String args[] = new String[opts.size()];
|
||||
for (int i = 0; i < opts.size(); i++)
|
||||
args[i] = (String)opts.get(i);
|
||||
_tunnel.runClientOptions(args, this);
|
||||
return opts;
|
||||
}
|
||||
|
||||
private void setSessionOptions() {
|
||||
_tunnel.setClientOptions(getClientOptionProps());
|
||||
}
|
||||
|
||||
private void setI2CPOptions() {
|
||||
@ -431,25 +441,37 @@ public class TunnelController implements Logging {
|
||||
startTunnel();
|
||||
}
|
||||
|
||||
/**
|
||||
* As of 0.9.1, updates the options on an existing session
|
||||
*/
|
||||
public void setConfig(Properties config, String prefix) {
|
||||
Properties props = new Properties();
|
||||
for (Iterator iter = config.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = config.getProperty(key);
|
||||
for (Map.Entry e : config.entrySet()) {
|
||||
String key = (String) e.getKey();
|
||||
if (key.startsWith(prefix)) {
|
||||
key = key.substring(prefix.length());
|
||||
String val = (String) e.getValue();
|
||||
props.setProperty(key, val);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Set prop [" + key + "] to [" + val + "]");
|
||||
}
|
||||
}
|
||||
_config = props;
|
||||
// tell i2ptunnel, who will tell the TunnelTask, who will tell the SocketManager
|
||||
setSessionOptions();
|
||||
if (_running && _sessions != null) {
|
||||
for (I2PSession s : _sessions) {
|
||||
// tell the router via the session
|
||||
if (!s.isClosed()) {
|
||||
s.updateOptions(_tunnel.getClientOptions());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Properties getConfig(String prefix) {
|
||||
Properties rv = new Properties();
|
||||
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = _config.getProperty(key);
|
||||
for (Map.Entry e : _config.entrySet()) {
|
||||
String key = (String) e.getKey();
|
||||
String val = (String) e.getValue();
|
||||
rv.setProperty(prefix + key, val);
|
||||
}
|
||||
return rv;
|
||||
@ -460,19 +482,27 @@ public class TunnelController implements Logging {
|
||||
public String getDescription() { return _config.getProperty("description"); }
|
||||
public String getI2CPHost() { return _config.getProperty("i2cpHost"); }
|
||||
public String getI2CPPort() { return _config.getProperty("i2cpPort"); }
|
||||
|
||||
/**
|
||||
* These are the ones with a prefix of "option."
|
||||
*
|
||||
* @return one big string of "key=val key=val ..."
|
||||
* @deprecated why would you want this? Use getClientOptionProps() instead
|
||||
*/
|
||||
public String getClientOptions() {
|
||||
StringBuilder opts = new StringBuilder(64);
|
||||
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = _config.getProperty(key);
|
||||
for (Map.Entry e : _config.entrySet()) {
|
||||
String key = (String) e.getKey();
|
||||
if (key.startsWith("option.")) {
|
||||
key = key.substring("option.".length());
|
||||
String val = (String) e.getValue();
|
||||
if (opts.length() > 0) opts.append(' ');
|
||||
opts.append(key).append('=').append(val);
|
||||
}
|
||||
}
|
||||
return opts.toString();
|
||||
}
|
||||
|
||||
public String getListenOnInterface() { return _config.getProperty("interface"); }
|
||||
public String getTargetHost() { return _config.getProperty("targetHost"); }
|
||||
public String getTargetPort() { return _config.getProperty("targetPort"); }
|
||||
@ -486,6 +516,7 @@ public class TunnelController implements Logging {
|
||||
/** default true */
|
||||
public boolean getStartOnLoad() { return Boolean.valueOf(_config.getProperty("startOnLoad", "true")).booleanValue(); }
|
||||
public boolean getPersistentClientKey() { return Boolean.valueOf(_config.getProperty("option.persistentClientKey")).booleanValue(); }
|
||||
|
||||
public String getMyDestination() {
|
||||
if (_tunnel != null) {
|
||||
List<I2PSession> sessions = _tunnel.getSessions();
|
||||
|
@ -15,10 +15,12 @@ import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketOptions;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.util.HexDump;
|
||||
import net.i2p.util.Log;
|
||||
@ -203,7 +205,10 @@ public class SOCKS4aServer extends SOCKSServer {
|
||||
// Let's not due a new Dest for every request, huh?
|
||||
//I2PSocketManager sm = I2PSocketManagerFactory.createManager();
|
||||
//destSock = sm.connect(I2PTunnel.destFromName(connHostName), null);
|
||||
destSock = t.createI2PSocket(I2PAppContext.getGlobalContext().namingService().lookup(connHostName));
|
||||
Properties overrides = new Properties();
|
||||
I2PSocketOptions sktOpts = t.buildOptions(overrides);
|
||||
sktOpts.setPort(connPort);
|
||||
destSock = t.createI2PSocket(I2PAppContext.getGlobalContext().namingService().lookup(connHostName), sktOpts);
|
||||
} else if ("localhost".equals(connHostName) || "127.0.0.1".equals(connHostName)) {
|
||||
String err = "No localhost accesses allowed through the Socks Proxy";
|
||||
_log.error(err);
|
||||
|
@ -366,7 +366,10 @@ public class SOCKS5Server extends SOCKSServer {
|
||||
} catch (IOException ioe) {}
|
||||
throw new SOCKSException("Host not found");
|
||||
}
|
||||
destSock = t.createI2PSocket(I2PAppContext.getGlobalContext().namingService().lookup(connHostName));
|
||||
Properties overrides = new Properties();
|
||||
I2PSocketOptions sktOpts = t.buildOptions(overrides);
|
||||
sktOpts.setPort(connPort);
|
||||
destSock = t.createI2PSocket(I2PAppContext.getGlobalContext().namingService().lookup(connHostName), sktOpts);
|
||||
} else if ("localhost".equals(connHostName) || "127.0.0.1".equals(connHostName)) {
|
||||
String err = "No localhost accesses allowed through the Socks Proxy";
|
||||
_log.error(err);
|
||||
|
@ -376,18 +376,6 @@ public class EditBean extends IndexBean {
|
||||
*/
|
||||
private static Properties getOptions(TunnelController controller) {
|
||||
if (controller == null) return null;
|
||||
String opts = controller.getClientOptions();
|
||||
StringTokenizer tok = new StringTokenizer(opts);
|
||||
Properties props = new Properties();
|
||||
while (tok.hasMoreTokens()) {
|
||||
String pair = tok.nextToken();
|
||||
int eq = pair.indexOf('=');
|
||||
if ( (eq <= 0) || (eq >= pair.length()) )
|
||||
continue;
|
||||
String key = pair.substring(0, eq);
|
||||
String val = pair.substring(eq+1);
|
||||
props.setProperty(key, val);
|
||||
}
|
||||
return props;
|
||||
return controller.getClientOptionProps();
|
||||
}
|
||||
}
|
||||
|
@ -497,8 +497,6 @@
|
||||
<div class="header"></div>
|
||||
<div class="footer">
|
||||
<div class="toolbox">
|
||||
<span class="comment"><%=intl._("NOTE: If tunnel is currently running, most changes will not take effect until tunnel is stopped and restarted.")%></span>
|
||||
<div class="separator"><hr /></div>
|
||||
<input type="hidden" value="true" name="removeConfirm" />
|
||||
<button id="controlCancel" class="control" type="submit" name="action" value="" title="Cancel"><%=intl._("Cancel")%></button>
|
||||
<button id="controlDelete" <%=(editBean.allowJS() ? "onclick=\"if (!confirm('Are you sure you want to delete?')) { return false; }\" " : "")%>accesskey="D" class="control" type="submit" name="action" value="Delete this proxy" title="Delete this Proxy"><%=intl._("Delete")%>(<span class="accessKey">D</span>)</button>
|
||||
|
@ -507,8 +507,6 @@
|
||||
<div class="header"></div>
|
||||
<div class="footer">
|
||||
<div class="toolbox">
|
||||
<span class="comment"><%=intl._("NOTE: If tunnel is currently running, most changes will not take effect until tunnel is stopped and restarted.")%></span>
|
||||
<div class="separator"><hr /></div>
|
||||
<input type="hidden" value="true" name="removeConfirm" />
|
||||
<button id="controlCancel" class="control" type="submit" name="action" value="" title="Cancel"><%=intl._("Cancel")%></button>
|
||||
<button id="controlDelete" <%=(editBean.allowJS() ? "onclick=\"if (!confirm('Are you sure you want to delete?')) { return false; }\" " : "")%>accesskey="D" class="control" type="submit" name="action" value="Delete this proxy" title="Delete this Proxy"><%=intl._("Delete")%>(<span class="accessKey">D</span>)</button>
|
||||
|
@ -7,11 +7,19 @@ import java.net.ConnectException;
|
||||
import java.nio.channels.SelectableChannel;
|
||||
|
||||
/**
|
||||
* As this does not (yet) extend ServerSocketChannel it cannot be returned by StandardServerSocket.getChannel(),
|
||||
* until we implement an I2P SocketAddress class.
|
||||
*
|
||||
* Warning, this interface and implementation is preliminary and subject to change without notice.
|
||||
*
|
||||
* @since 0.8.11
|
||||
*/
|
||||
public abstract class AcceptingChannel extends SelectableChannel {
|
||||
|
||||
abstract I2PSocket accept() throws I2PException, ConnectException;
|
||||
I2PSocketManager _socketManager;
|
||||
|
||||
protected final I2PSocketManager _socketManager;
|
||||
|
||||
AcceptingChannel(I2PSocketManager manager) {
|
||||
this._socketManager = manager;
|
||||
}
|
||||
|
@ -0,0 +1,107 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
* A SocketAddress (Destination + port) so we can have SocketChannels.
|
||||
* Ports are not widely used in I2P, in most cases the port will be zero.
|
||||
* See InetSocketAddress for javadocs.
|
||||
*
|
||||
* Warning, this interface and implementation is preliminary and subject to change without notice.
|
||||
*
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public class I2PSocketAddress extends SocketAddress {
|
||||
|
||||
private final int _port;
|
||||
private final Destination _dest;
|
||||
private final String _host;
|
||||
|
||||
// no constructor for port-only "wildcard" address
|
||||
|
||||
/**
|
||||
* Does not do a reverse lookup. Host will be null.
|
||||
*/
|
||||
public I2PSocketAddress(Destination dest, int port) {
|
||||
_port = port;
|
||||
_dest = dest;
|
||||
_host = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a naming service lookup to resolve the dest.
|
||||
* May take several seconds for b32.
|
||||
*/
|
||||
public I2PSocketAddress(String host, int port) {
|
||||
_port = port;
|
||||
_dest = I2PAppContext.getGlobalContext().namingService().lookup(host);
|
||||
_host = host;
|
||||
}
|
||||
|
||||
public static I2PSocketAddress createUnresolved(String host, int port) {
|
||||
return new I2PSocketAddress(port, host);
|
||||
}
|
||||
|
||||
/** unresolved */
|
||||
private I2PSocketAddress(int port, String host) {
|
||||
_port = port;
|
||||
_dest = null;
|
||||
_host = host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return _port;
|
||||
}
|
||||
|
||||
public Destination getAddress() {
|
||||
return _dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the host only if given in the constructor. Does not do a reverse lookup.
|
||||
*/
|
||||
public String getHostName() {
|
||||
return _host;
|
||||
}
|
||||
|
||||
public boolean isUnresolved() {
|
||||
return _dest == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
if (_dest != null)
|
||||
buf.append(_dest.calculateHash().toString());
|
||||
else
|
||||
buf.append(_host);
|
||||
buf.append(':');
|
||||
buf.append(_port);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || !(obj instanceof I2PSocketAddress))
|
||||
return false;
|
||||
I2PSocketAddress o = (I2PSocketAddress) obj;
|
||||
if (_port != o._port)
|
||||
return false;
|
||||
if (_dest != null)
|
||||
return _dest.equals(o._dest);
|
||||
if (o._dest != null)
|
||||
return false;
|
||||
if (_host != null)
|
||||
return _host.equals(o._host);
|
||||
return o._host == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return DataHelper.hashCode(_dest) ^ DataHelper.hashCode(_host) ^ _port;
|
||||
}
|
||||
}
|
@ -111,8 +111,8 @@ public class I2PSocketEepGet extends EepGet {
|
||||
if ("http".equals(url.getProtocol())) {
|
||||
String host = url.getHost();
|
||||
int port = url.getPort();
|
||||
if (port != -1)
|
||||
throw new IOException("Ports not supported in i2p: " + _actualURL);
|
||||
if (port <= 0 || port > 65535)
|
||||
port = 80;
|
||||
|
||||
// HTTP Proxy compatibility http://i2p/B64KEY/blah
|
||||
// Rewrite the url to strip out the /i2p/,
|
||||
@ -143,6 +143,7 @@ public class I2PSocketEepGet extends EepGet {
|
||||
// in the SYN packet, saving one RTT.
|
||||
props.setProperty(PROP_CONNECT_DELAY, CONNECT_DELAY);
|
||||
I2PSocketOptions opts = _socketManager.buildOptions(props);
|
||||
opts.setPort(port);
|
||||
_socket = _socketManager.connect(dest, opts);
|
||||
} else {
|
||||
throw new IOException("Unsupported protocol: " + _actualURL);
|
||||
|
@ -38,11 +38,32 @@ public interface I2PSocketManager {
|
||||
*/
|
||||
public void setAcceptTimeout(long ms);
|
||||
public long getAcceptTimeout();
|
||||
|
||||
/**
|
||||
* Update the options on a running socket manager.
|
||||
* Parameters in the I2PSocketOptions interface may be changed directly
|
||||
* with the setters; no need to use this method for those.
|
||||
* This does NOT update the underlying I2CP or tunnel options; use getSession().updateOptions() for that.
|
||||
* @param options as created from a call to buildOptions(properties), non-null
|
||||
*/
|
||||
public void setDefaultOptions(I2PSocketOptions options);
|
||||
|
||||
/**
|
||||
* Current options, not a copy, setters may be used to make changes.
|
||||
*/
|
||||
public I2PSocketOptions getDefaultOptions();
|
||||
|
||||
public I2PServerSocket getServerSocket();
|
||||
|
||||
/**
|
||||
* Create a copy of the current options, to be used in a setDefaultOptions() call.
|
||||
*/
|
||||
public I2PSocketOptions buildOptions();
|
||||
|
||||
/**
|
||||
* Create a modified copy of the current options, to be used in a setDefaultOptions() call.
|
||||
* @param opts The new options, may be null
|
||||
*/
|
||||
public I2PSocketOptions buildOptions(Properties opts);
|
||||
|
||||
/**
|
||||
@ -102,6 +123,10 @@ public interface I2PSocketManager {
|
||||
public String getName();
|
||||
public void setName(String name);
|
||||
|
||||
/**
|
||||
* Deprecated - Factory will initialize.
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
public void init(I2PAppContext context, I2PSession session, Properties opts, String name);
|
||||
|
||||
public void addDisconnectListener(DisconnectListener lsnr);
|
||||
|
@ -4,6 +4,7 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
|
||||
@ -161,39 +162,18 @@ public class I2PSocketManagerFactory {
|
||||
}
|
||||
|
||||
private static I2PSocketManager createManager(I2PSession session, Properties opts, String name) {
|
||||
//if (false) {
|
||||
//I2PSocketManagerImpl mgr = new I2PSocketManagerImpl();
|
||||
//mgr.setSession(session);
|
||||
//mgr.setDefaultOptions(new I2PSocketOptions());
|
||||
//return mgr;
|
||||
//} else {
|
||||
String classname = opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER);
|
||||
if (classname != null) {
|
||||
try {
|
||||
Class cls = Class.forName(classname);
|
||||
Object obj = cls.newInstance();
|
||||
if (obj instanceof I2PSocketManager) {
|
||||
I2PSocketManager mgr = (I2PSocketManager)obj;
|
||||
I2PAppContext context = I2PAppContext.getGlobalContext();
|
||||
mgr.init(context, session, opts, name);
|
||||
return mgr;
|
||||
} else {
|
||||
throw new IllegalStateException("Invalid manager class [" + classname + "]");
|
||||
}
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
_log.error("Error loading " + classname, cnfe);
|
||||
throw new IllegalStateException("Invalid manager class [" + classname + "] - not found");
|
||||
} catch (InstantiationException ie) {
|
||||
_log.error("Error loading " + classname, ie);
|
||||
throw new IllegalStateException("Invalid manager class [" + classname + "] - unable to instantiate");
|
||||
} catch (IllegalAccessException iae) {
|
||||
_log.error("Error loading " + classname, iae);
|
||||
throw new IllegalStateException("Invalid manager class [" + classname + "] - illegal access");
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("No manager class specified");
|
||||
}
|
||||
//}
|
||||
I2PAppContext context = I2PAppContext.getGlobalContext();
|
||||
String classname = opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER);
|
||||
try {
|
||||
Class cls = Class.forName(classname);
|
||||
Constructor<I2PSocketManager> con = (Constructor<I2PSocketManager>)
|
||||
cls.getConstructor(new Class[] {I2PAppContext.class, I2PSession.class, Properties.class, String.class});
|
||||
I2PSocketManager mgr = con.newInstance(new Object[] {context, session, opts, name});
|
||||
return mgr;
|
||||
} catch (Throwable t) {
|
||||
_log.log(Log.CRIT, "Error loading " + classname, t);
|
||||
throw new IllegalStateException(t);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -105,6 +105,9 @@ public interface I2PSocketOptions {
|
||||
|
||||
/**
|
||||
* The local port.
|
||||
* Zero (default) means you will receive traffic on all ports.
|
||||
* Nonzero means you will get traffic ONLY for that port, use with care,
|
||||
* as most applications do not specify a remote port.
|
||||
* @param port 0 - 65535
|
||||
* @since 0.8.9
|
||||
*/
|
||||
|
@ -200,6 +200,9 @@ class I2PSocketOptionsImpl implements I2PSocketOptions {
|
||||
|
||||
/**
|
||||
* The local port.
|
||||
* Zero (default) means you will receive traffic on all ports.
|
||||
* Nonzero means you will get traffic ONLY for that port, use with care,
|
||||
* as most applications do not specify a remote port.
|
||||
* @param port 0 - 65535
|
||||
* @since 0.8.9
|
||||
*/
|
||||
|
@ -13,16 +13,21 @@ import java.nio.channels.spi.AbstractSelectionKey;
|
||||
import java.nio.channels.spi.SelectorProvider;
|
||||
|
||||
/**
|
||||
* As this does not (yet) extend ServerSocketChannel it cannot be returned by StandardServerSocket.getChannel(),
|
||||
* until we implement an I2P SocketAddress class.
|
||||
*
|
||||
* Warning, this interface and implementation is preliminary and subject to change without notice.
|
||||
*
|
||||
* @since 0.8.11
|
||||
*/
|
||||
public class AcceptingChannelImpl extends AcceptingChannel {
|
||||
boolean _isRegistered = false;
|
||||
SelectionKey whichKey = null;
|
||||
SelectorProvider provider = null;
|
||||
Selector sel = null;
|
||||
Object lock = null;
|
||||
I2PSocket next = null;
|
||||
I2PServerSocket socket;
|
||||
class AcceptingChannelImpl extends AcceptingChannel {
|
||||
private boolean _isRegistered;
|
||||
private SelectionKey whichKey;
|
||||
private SelectorProvider provider;
|
||||
private Selector sel;
|
||||
private Object lock;
|
||||
private volatile I2PSocket next;
|
||||
private final I2PServerSocket socket;
|
||||
|
||||
I2PSocket accept() throws I2PException, ConnectException {
|
||||
I2PSocket sock;
|
||||
@ -31,9 +36,11 @@ public class AcceptingChannelImpl extends AcceptingChannel {
|
||||
} catch(SocketTimeoutException ex) {
|
||||
return null;
|
||||
}
|
||||
I2PSocket temp = next;
|
||||
next = sock;
|
||||
return temp;
|
||||
synchronized (this) {
|
||||
I2PSocket temp = next;
|
||||
next = sock;
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
|
||||
AcceptingChannelImpl(I2PSocketManager manager) {
|
||||
@ -96,7 +103,7 @@ public class AcceptingChannelImpl extends AcceptingChannel {
|
||||
|
||||
@Override
|
||||
public int readyOps() {
|
||||
if((operations & OP_ACCEPT) != 0)
|
||||
if((operations & OP_ACCEPT) != 0) {
|
||||
if(next != null) {
|
||||
return OP_ACCEPT;
|
||||
} else {
|
||||
@ -107,6 +114,7 @@ public class AcceptingChannelImpl extends AcceptingChannel {
|
||||
if(next != null)
|
||||
return OP_ACCEPT;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
@ -136,8 +144,9 @@ public class AcceptingChannelImpl extends AcceptingChannel {
|
||||
|
||||
@Override
|
||||
protected void implCloseChannel() throws IOException {
|
||||
if(next != null) {
|
||||
next.close();
|
||||
I2PSocket nxt = next;
|
||||
if(nxt != null) {
|
||||
nxt.close();
|
||||
}
|
||||
_socketManager.destroySocketManager();
|
||||
}
|
||||
|
@ -67,7 +67,10 @@ class ConnectionManager {
|
||||
// TODO change proto to PROTO_STREAMING someday.
|
||||
// Right now we get everything, and rely on Datagram to specify PROTO_UDP.
|
||||
// PacketQueue has sent PROTO_STREAMING since the beginning of mux support (0.7.1)
|
||||
_session.addMuxedSessionListener(_messageHandler, I2PSession.PROTO_ANY, I2PSession.PORT_ANY);
|
||||
// As of 0.9.1, new option to enforce streaming protocol, off by default
|
||||
// As of 0.9.1, listen on configured port (default 0 = all)
|
||||
int protocol = defaultOptions.getEnforceProtocol() ? I2PSession.PROTO_STREAMING : I2PSession.PROTO_ANY;
|
||||
_session.addMuxedSessionListener(_messageHandler, protocol, defaultOptions.getLocalPort());
|
||||
_outboundQueue = new PacketQueue(_context, _session, this);
|
||||
/** Socket timeout for accept() */
|
||||
_soTimeout = -1;
|
||||
|
@ -19,6 +19,7 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
private int _connectDelay;
|
||||
private boolean _fullySigned;
|
||||
private boolean _answerPings;
|
||||
private boolean _enforceProto;
|
||||
private volatile int _windowSize;
|
||||
private int _receiveWindow;
|
||||
private int _profile;
|
||||
@ -87,6 +88,8 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
public static final String PROP_MAX_TOTAL_CONNS_MIN = "i2p.streaming.maxTotalConnsPerMinute";
|
||||
public static final String PROP_MAX_TOTAL_CONNS_HOUR = "i2p.streaming.maxTotalConnsPerHour";
|
||||
public static final String PROP_MAX_TOTAL_CONNS_DAY = "i2p.streaming.maxTotalConnsPerDay";
|
||||
/** @since 0.9.1 */
|
||||
public static final String PROP_ENFORCE_PROTO = "i2p.streaming.enforceProtocol";
|
||||
|
||||
private static final int TREND_COUNT = 3;
|
||||
static final int INITIAL_WINDOW_SIZE = 6;
|
||||
@ -95,6 +98,11 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
public static final int DEFAULT_INITIAL_ACK_DELAY = 2*1000;
|
||||
static final int MIN_WINDOW_SIZE = 1;
|
||||
private static final boolean DEFAULT_ANSWER_PINGS = true;
|
||||
/**
|
||||
* If PROTO is enforced, we cannot communicate with destinations earlier than version 0.7.1.
|
||||
* @since 0.9.1
|
||||
*/
|
||||
private static final boolean DEFAULT_ENFORCE_PROTO = true;
|
||||
|
||||
// Syncronization fix, but doing it this way causes NPE...
|
||||
// FIXME private final int _trend[] = new int[TREND_COUNT]; FIXME
|
||||
@ -237,7 +245,33 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
*/
|
||||
public ConnectionOptions(ConnectionOptions opts) {
|
||||
super(opts);
|
||||
if (opts != null) {
|
||||
if (opts != null)
|
||||
update(opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update everything by copying over from opts
|
||||
* @param opts non-null
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public void updateAll(ConnectionOptions opts) {
|
||||
// user is unlikely to change these 6 between buildOptions() and setDefaultOptions(),
|
||||
// since they may be updated directly, but just in case...
|
||||
setConnectTimeout(opts.getConnectTimeout());
|
||||
setReadTimeout(opts.getReadTimeout());
|
||||
setWriteTimeout(opts.getWriteTimeout());
|
||||
setMaxBufferSize(opts.getMaxBufferSize());
|
||||
setLocalPort(opts.getLocalPort());
|
||||
setPort(opts.getPort());
|
||||
update(opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update everything (except super) by copying over from opts
|
||||
* @param opts non-null
|
||||
* @since 0.9.1
|
||||
*/
|
||||
private void update(ConnectionOptions opts) {
|
||||
setMaxWindowSize(opts.getMaxWindowSize());
|
||||
setConnectDelay(opts.getConnectDelay());
|
||||
setProfile(opts.getProfile());
|
||||
@ -258,6 +292,7 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
//setWriteTimeout(opts.getWriteTimeout());
|
||||
//setReadTimeout(opts.getReadTimeout());
|
||||
setAnswerPings(opts.getAnswerPings());
|
||||
setEnforceProtocol(opts.getEnforceProtocol());
|
||||
initLists(opts);
|
||||
_maxConnsPerMinute = opts.getMaxConnsPerMinute();
|
||||
_maxConnsPerHour = opts.getMaxConnsPerHour();
|
||||
@ -265,7 +300,6 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
_maxTotalConnsPerMinute = opts.getMaxTotalConnsPerMinute();
|
||||
_maxTotalConnsPerHour = opts.getMaxTotalConnsPerHour();
|
||||
_maxTotalConnsPerDay = opts.getMaxTotalConnsPerDay();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -292,6 +326,7 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
// overrides default in super()
|
||||
setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
|
||||
setAnswerPings(getBool(opts, PROP_ANSWER_PINGS, DEFAULT_ANSWER_PINGS));
|
||||
setEnforceProtocol(getBool(opts, PROP_ENFORCE_PROTO, DEFAULT_ENFORCE_PROTO));
|
||||
initLists(opts);
|
||||
_maxConnsPerMinute = getInt(opts, PROP_MAX_CONNS_MIN, 0);
|
||||
_maxConnsPerHour = getInt(opts, PROP_MAX_CONNS_HOUR, 0);
|
||||
@ -301,6 +336,9 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
_maxTotalConnsPerDay = getInt(opts, PROP_MAX_TOTAL_CONNS_DAY, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: NOT part of the interface
|
||||
*/
|
||||
@Override
|
||||
public void setProperties(Properties opts) {
|
||||
super.setProperties(opts);
|
||||
@ -343,6 +381,8 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT));
|
||||
if (opts.containsKey(PROP_ANSWER_PINGS))
|
||||
setAnswerPings(getBool(opts, PROP_ANSWER_PINGS, DEFAULT_ANSWER_PINGS));
|
||||
if (opts.containsKey(PROP_ENFORCE_PROTO))
|
||||
setEnforceProtocol(getBool(opts, PROP_ENFORCE_PROTO, DEFAULT_ENFORCE_PROTO));
|
||||
initLists(opts);
|
||||
if (opts.containsKey(PROP_MAX_CONNS_MIN))
|
||||
_maxConnsPerMinute = getInt(opts, PROP_MAX_CONNS_MIN, 0);
|
||||
@ -392,6 +432,19 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
public boolean getAnswerPings() { return _answerPings; }
|
||||
public void setAnswerPings(boolean yes) { _answerPings = yes; }
|
||||
|
||||
/**
|
||||
* Do we receive all traffic, or only traffic marked with I2PSession.PROTO_STREAMING (6) ?
|
||||
* Default false.
|
||||
* If PROTO is enforced, we cannot communicate with destinations earlier than version 0.7.1
|
||||
* (released March 2009), which is when streaming started sending the PROTO_STREAMING indication.
|
||||
* Set to true if you are running multiple protocols on a single Destination.
|
||||
*
|
||||
* @return if we do
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public boolean getEnforceProtocol() { return _enforceProto; }
|
||||
public void setEnforceProtocol(boolean yes) { _enforceProto = yes; }
|
||||
|
||||
/**
|
||||
* How many messages will we send before waiting for an ACK?
|
||||
*
|
||||
@ -611,30 +664,35 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
private void initLists(Properties opts) {
|
||||
_accessListEnabled = getBool(opts, PROP_ENABLE_ACCESS_LIST, false);
|
||||
_blackListEnabled = getBool(opts, PROP_ENABLE_BLACKLIST, false);
|
||||
// Don't think these would ever be accessed simultaneously,
|
||||
// but avoid concurrent modification just in case
|
||||
Set<Hash> accessList, blackList;
|
||||
if (_accessListEnabled)
|
||||
_accessList = new HashSet();
|
||||
accessList = new HashSet();
|
||||
else
|
||||
_accessList = Collections.EMPTY_SET;
|
||||
accessList = Collections.EMPTY_SET;
|
||||
if (_blackListEnabled)
|
||||
_blackList = new HashSet();
|
||||
blackList = new HashSet();
|
||||
else
|
||||
_blackList = Collections.EMPTY_SET;
|
||||
if (!(_accessListEnabled || _blackListEnabled))
|
||||
return;
|
||||
String hashes = opts.getProperty(PROP_ACCESS_LIST);
|
||||
if (hashes == null)
|
||||
return;
|
||||
StringTokenizer tok = new StringTokenizer(hashes, ", ");
|
||||
while (tok.hasMoreTokens()) {
|
||||
String hashstr = tok.nextToken();
|
||||
Hash h = ConvertToHash.getHash(hashstr);
|
||||
if (h == null)
|
||||
error("bad list hash: " + hashstr);
|
||||
else if (_blackListEnabled)
|
||||
_blackList.add(h);
|
||||
else
|
||||
_accessList.add(h);
|
||||
blackList = Collections.EMPTY_SET;
|
||||
if (_accessListEnabled || _blackListEnabled) {
|
||||
String hashes = opts.getProperty(PROP_ACCESS_LIST);
|
||||
if (hashes == null)
|
||||
return;
|
||||
StringTokenizer tok = new StringTokenizer(hashes, ", ");
|
||||
while (tok.hasMoreTokens()) {
|
||||
String hashstr = tok.nextToken();
|
||||
Hash h = ConvertToHash.getHash(hashstr);
|
||||
if (h == null)
|
||||
error("bad list hash: " + hashstr);
|
||||
else if (_blackListEnabled)
|
||||
blackList.add(h);
|
||||
else
|
||||
accessList.add(h);
|
||||
}
|
||||
}
|
||||
_accessList = accessList;
|
||||
_blackList = blackList;
|
||||
if (_accessListEnabled && _accessList.isEmpty())
|
||||
error("Connection access list enabled but no valid entries; no peers can connect");
|
||||
else if (_blackListEnabled && _blackList.isEmpty())
|
||||
@ -647,9 +705,10 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
log.error(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
/** doesn't include everything */
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
buf.append("conDelay=").append(_connectDelay);
|
||||
buf.append(" maxSize=").append(_maxMessageSize);
|
||||
buf.append(" rtt=").append(_rtt);
|
||||
@ -663,6 +722,14 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
buf.append(" inactivityTimeout=").append(_inactivityTimeout);
|
||||
buf.append(" inboundBuffer=").append(_inboundBufferSize);
|
||||
buf.append(" maxWindowSize=").append(_maxWindowSize);
|
||||
buf.append(" blacklistSize=").append(_blackList.size());
|
||||
buf.append(" whitelistSize=").append(_accessList.size());
|
||||
buf.append(" maxConns=").append(_maxConnsPerMinute).append('/')
|
||||
.append(_maxConnsPerHour).append('/')
|
||||
.append(_maxConnsPerDay);
|
||||
buf.append(" maxTotalConns=").append(_maxTotalConnsPerMinute).append('/')
|
||||
.append(_maxTotalConnsPerHour).append('/')
|
||||
.append(_maxTotalConnsPerDay);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import net.i2p.I2PException;
|
||||
*/
|
||||
class I2PServerSocketFull implements I2PServerSocket {
|
||||
private final I2PSocketManagerFull _socketManager;
|
||||
private volatile AcceptingChannel _channel;
|
||||
|
||||
public I2PServerSocketFull(I2PSocketManagerFull mgr) {
|
||||
_socketManager = mgr;
|
||||
@ -28,8 +29,10 @@ class I2PServerSocketFull implements I2PServerSocket {
|
||||
/**
|
||||
* @since 0.8.11
|
||||
*/
|
||||
public AcceptingChannel getChannel() {
|
||||
return new AcceptingChannelImpl(_socketManager);
|
||||
public synchronized AcceptingChannel getChannel() {
|
||||
if (_channel == null)
|
||||
_channel = new AcceptingChannelImpl(_socketManager);
|
||||
return _channel;
|
||||
}
|
||||
|
||||
public long getSoTimeout() {
|
||||
|
@ -16,6 +16,7 @@ class I2PSocketFull implements I2PSocket {
|
||||
private Connection _connection;
|
||||
private Destination _remotePeer;
|
||||
private Destination _localPeer;
|
||||
private volatile MessageChannel _channel;
|
||||
|
||||
public I2PSocketFull(Connection con) {
|
||||
_connection = con;
|
||||
@ -70,8 +71,10 @@ class I2PSocketFull implements I2PSocket {
|
||||
/**
|
||||
* @since 0.8.9
|
||||
*/
|
||||
public SelectableChannel getChannel() {
|
||||
return new MessageChannel(this);
|
||||
public synchronized SelectableChannel getChannel() {
|
||||
if (_channel == null)
|
||||
_channel = new MessageChannel(this);
|
||||
return _channel;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,17 +28,17 @@ import net.i2p.util.Log;
|
||||
* Direct instantiation by others is deprecated.
|
||||
*/
|
||||
public class I2PSocketManagerFull implements I2PSocketManager {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private I2PSession _session;
|
||||
private I2PServerSocketFull _serverSocket;
|
||||
private final I2PAppContext _context;
|
||||
private final Log _log;
|
||||
private final I2PSession _session;
|
||||
private final I2PServerSocketFull _serverSocket;
|
||||
private StandardServerSocket _realServerSocket;
|
||||
private ConnectionOptions _defaultOptions;
|
||||
private final ConnectionOptions _defaultOptions;
|
||||
private long _acceptTimeout;
|
||||
private String _name;
|
||||
private int _maxStreams;
|
||||
private static int __managerId = 0;
|
||||
private ConnectionManager _connectionManager;
|
||||
private final ConnectionManager _connectionManager;
|
||||
|
||||
/**
|
||||
* How long to wait for the client app to accept() before sending back CLOSE?
|
||||
@ -46,10 +46,28 @@ public class I2PSocketManagerFull implements I2PSocketManager {
|
||||
*/
|
||||
private static final long ACCEPT_TIMEOUT_DEFAULT = 5*1000;
|
||||
|
||||
/**
|
||||
* @deprecated use 4-arg constructor
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
public I2PSocketManagerFull() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/** how many streams will we allow at once? */
|
||||
public static final String PROP_MAX_STREAMS = "i2p.streaming.maxConcurrentStreams";
|
||||
|
||||
/**
|
||||
* @deprecated use 4-arg constructor
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
public void init(I2PAppContext context, I2PSession session, Properties opts, String name) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is what I2PSocketManagerFactory.createManager() returns.
|
||||
* Direct instantiation by others is deprecated.
|
||||
*
|
||||
* @param context
|
||||
* @param session
|
||||
@ -57,22 +75,6 @@ public class I2PSocketManagerFull implements I2PSocketManager {
|
||||
* @param name
|
||||
*/
|
||||
public I2PSocketManagerFull(I2PAppContext context, I2PSession session, Properties opts, String name) {
|
||||
this();
|
||||
init(context, session, opts, name);
|
||||
}
|
||||
|
||||
/** how many streams will we allow at once? */
|
||||
public static final String PROP_MAX_STREAMS = "i2p.streaming.maxConcurrentStreams";
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param context
|
||||
* @param session
|
||||
* @param opts
|
||||
* @param name
|
||||
*/
|
||||
public void init(I2PAppContext context, I2PSession session, Properties opts, String name) {
|
||||
_context = context;
|
||||
_session = session;
|
||||
_log = _context.logManager().getLog(I2PSocketManagerFull.class);
|
||||
@ -98,7 +100,15 @@ public class I2PSocketManagerFull implements I2PSocketManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of the current options, to be used in a setDefaultOptions() call.
|
||||
*/
|
||||
public I2PSocketOptions buildOptions() { return buildOptions(null); }
|
||||
|
||||
/**
|
||||
* Create a modified copy of the current options, to be used in a setDefaultOptions() call.
|
||||
* @param opts The new options, may be null
|
||||
*/
|
||||
public I2PSocketOptions buildOptions(Properties opts) {
|
||||
ConnectionOptions curOpts = new ConnectionOptions(_defaultOptions);
|
||||
curOpts.setProperties(opts);
|
||||
@ -159,10 +169,24 @@ public class I2PSocketManagerFull implements I2PSocketManager {
|
||||
public void setAcceptTimeout(long ms) { _acceptTimeout = ms; }
|
||||
public long getAcceptTimeout() { return _acceptTimeout; }
|
||||
|
||||
/**
|
||||
* Update the options on a running socket manager.
|
||||
* Parameters in the I2PSocketOptions interface may be changed directly
|
||||
* with the setters; no need to use this method for those.
|
||||
* This does NOT update the underlying I2CP or tunnel options; use getSession().updateOptions() for that.
|
||||
* @param options as created from a call to buildOptions(properties), non-null
|
||||
*/
|
||||
public void setDefaultOptions(I2PSocketOptions options) {
|
||||
_defaultOptions = new ConnectionOptions((ConnectionOptions) options);
|
||||
if (!(options instanceof ConnectionOptions))
|
||||
throw new IllegalArgumentException();
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Changing options from:\n " + _defaultOptions + "\nto:\n " + options);
|
||||
_defaultOptions.updateAll((ConnectionOptions) options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Current options, not a copy, setters may be used to make changes.
|
||||
*/
|
||||
public I2PSocketOptions getDefaultOptions() {
|
||||
return _defaultOptions;
|
||||
}
|
||||
|
@ -15,18 +15,23 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* As this does not (yet) extend SocketChannel it cannot be returned by StandardSocket.getChannel(),
|
||||
* until we implement an I2P SocketAddress class.
|
||||
*
|
||||
* Warning, this interface and implementation is preliminary and subject to change without notice.
|
||||
*
|
||||
* @since 0.8.9
|
||||
*/
|
||||
public class MessageChannel extends SelectableChannel implements ReadableByteChannel, WritableByteChannel {
|
||||
|
||||
final MessageInputStream in;
|
||||
final MessageOutputStream out;
|
||||
boolean _isRegistered = false;
|
||||
SelectionKey whichKey = null;
|
||||
SelectorProvider provider = null;
|
||||
Selector sel = null;
|
||||
Object lock = null;
|
||||
I2PSocket socket;
|
||||
private final MessageInputStream in;
|
||||
private final MessageOutputStream out;
|
||||
private boolean _isRegistered;
|
||||
private SelectionKey whichKey;
|
||||
private SelectorProvider provider;
|
||||
private Selector sel;
|
||||
private Object lock;
|
||||
private final I2PSocket socket;
|
||||
|
||||
MessageChannel(I2PSocket socket) {
|
||||
try {
|
||||
@ -145,10 +150,10 @@ public class MessageChannel extends SelectableChannel implements ReadableByteCha
|
||||
* returns 0, which happens when there's
|
||||
* no more data available.
|
||||
*/
|
||||
|
||||
public int read(ByteBuffer buf) throws IOException {
|
||||
int amount = 0;
|
||||
for (;;) {
|
||||
// TODO if buf.hasArray() ... getArray() ... getArrayOffset() ...
|
||||
byte[] lbuf = new byte[buf.remaining()];
|
||||
int samount = in.read(lbuf);
|
||||
if (samount <= 0) {
|
||||
@ -167,12 +172,12 @@ public class MessageChannel extends SelectableChannel implements ReadableByteCha
|
||||
* already set buffer size. Once it starts to fail
|
||||
* (wait timeout is 0) then put the bytes back and return.
|
||||
*/
|
||||
|
||||
public int write(ByteBuffer buf) throws IOException {
|
||||
int written = 0;
|
||||
for (;;) {
|
||||
if(buf.remaining()==0)
|
||||
return written;
|
||||
// TODO if buf.hasArray() ... getArray() ... getArrayOffset() ...
|
||||
byte[] lbuf = new byte[Math.min(buf.remaining(), 0x1000)];
|
||||
buf.get(lbuf);
|
||||
try {
|
||||
|
@ -72,10 +72,11 @@ class StandardServerSocket extends ServerSocket {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null always
|
||||
* @return null always, see AcceptingChannelImpl for more info
|
||||
*/
|
||||
@Override
|
||||
public ServerSocketChannel getChannel() {
|
||||
//return _socket.getChannel();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -65,10 +65,11 @@ class StandardSocket extends Socket {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null always
|
||||
* @return null always, see MessageChannel for more info
|
||||
*/
|
||||
@Override
|
||||
public SocketChannel getChannel() {
|
||||
//return _socket.getChannel();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,9 @@ public interface I2PSession {
|
||||
/** Send a new message to the given destination, containing the specified
|
||||
* payload, returning true if the router feels confident that the message
|
||||
* was delivered.
|
||||
*
|
||||
* WARNING: It is recommended that you use a method that specifies the protocol and ports.
|
||||
*
|
||||
* @param dest location to send the message
|
||||
* @param payload body of the message to be sent (unencrypted)
|
||||
* @return whether it was accepted by the router for delivery or not
|
||||
@ -149,6 +152,9 @@ public interface I2PSession {
|
||||
public void reportAbuse(int msgId, int severity) throws I2PSessionException;
|
||||
|
||||
/** Instruct the I2PSession where it should send event notifications
|
||||
*
|
||||
* WARNING: It is recommended that you use a method that specifies the protocol and ports.
|
||||
*
|
||||
* @param lsnr listener to retrieve events
|
||||
*/
|
||||
public void setSessionListener(I2PSessionListener lsnr);
|
||||
|
41
history.txt
41
history.txt
@ -1,3 +1,42 @@
|
||||
2012-06-18 zzz
|
||||
* i2psnark:
|
||||
- Improve torrent shutdown handling to maximize chance of
|
||||
announces getting to tracker
|
||||
- Thread task to open tunnel and improve UI feedback while open is pending
|
||||
- Clean up delete-torrent messages
|
||||
- Remove redundant shutdown hook
|
||||
- Avoid NPE in PEX message handling
|
||||
- Log tweaks
|
||||
|
||||
2012-06-15 zzz
|
||||
* I2PSocketEepGet: Use specified port
|
||||
* I2PTunnel:
|
||||
- Don't strip port from an I2P URL
|
||||
- More client options cleanups
|
||||
- Options changes now propagate to running
|
||||
socket managers and sessions, and through to the router
|
||||
- Better parsing of jump server URLs
|
||||
* NetDB: Only publish stats every so often, to improve
|
||||
anonymity while preserving the ability to monitor
|
||||
the network (effective next release)
|
||||
* SocketManager:
|
||||
- Simplify factory, use 4-arg constructor,
|
||||
make fields final, deprecate 0-arg constructor
|
||||
- Improve how options are updated
|
||||
- Javadocs
|
||||
|
||||
2012-06-13 zzz
|
||||
* I2PSocketEepGet: Set port to 80
|
||||
* I2PTunnel:
|
||||
- Pass port through HTTP client proxy
|
||||
- HTTP server proxy sets host header to
|
||||
the value of "spoofedhost.xx" option for port xx
|
||||
- Set client options more efficiently
|
||||
* i2psnark: Possible fix for piece-after-choke
|
||||
* Streaming:
|
||||
- Channel cleanups and comments
|
||||
- New I2PSocketAddress
|
||||
|
||||
2012-06-11 zzz
|
||||
* i2psnark:
|
||||
- Display torrent file downloads in torrent area
|
||||
@ -8,7 +47,7 @@
|
||||
- Reduce delay between peer adds for faster startup
|
||||
- Thread the announces and reduce timeout when stopping
|
||||
* NativeBigInteger: Workaround for Raspberry Pi to load the correct lib
|
||||
* Router: Don't let shutdown tasks hang the shutdown
|
||||
* Router: Don't let shutdown tasks hang the shutdown (tickets #564, #566)
|
||||
|
||||
2012-06-08 zzz
|
||||
* i2psnark:
|
||||
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Monotone";
|
||||
public final static String VERSION = CoreVersion.VERSION;
|
||||
public final static long BUILD = 13;
|
||||
public final static long BUILD = 16;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
@ -30,6 +30,8 @@ public class StatisticsManager implements Service {
|
||||
private final RouterContext _context;
|
||||
|
||||
public final static String PROP_PUBLISH_RANKINGS = "router.publishPeerRankings";
|
||||
/** enhance anonymity by only including build stats one out of this many times */
|
||||
private static final int RANDOM_INCLUDE_STATS = 4;
|
||||
|
||||
private final DecimalFormat _fmt;
|
||||
private final DecimalFormat _pct;
|
||||
@ -82,11 +84,13 @@ public class StatisticsManager implements Service {
|
||||
stats.setProperty("stat_identities", newlines+"");
|
||||
***/
|
||||
|
||||
if (_context.getBooleanPropertyDefaultTrue(PROP_PUBLISH_RANKINGS)) {
|
||||
if (_context.getBooleanPropertyDefaultTrue(PROP_PUBLISH_RANKINGS) &&
|
||||
(CoreVersion.VERSION.equals("0.9") || _context.random().nextInt(RANDOM_INCLUDE_STATS) == 0)) {
|
||||
long publishedUptime = _context.router().getUptime();
|
||||
// Don't publish these for first hour
|
||||
if (publishedUptime > 62*60*1000 && CoreVersion.VERSION.equals("0.8.13"))
|
||||
includeAverageThroughput(stats);
|
||||
// Disabled in 0.9
|
||||
//if (publishedUptime > 62*60*1000)
|
||||
// includeAverageThroughput(stats);
|
||||
//includeRate("router.invalidMessageTime", stats, new long[] { 10*60*1000 });
|
||||
//includeRate("router.duplicateMessageId", stats, new long[] { 24*60*60*1000 });
|
||||
//includeRate("tunnel.duplicateIV", stats, new long[] { 24*60*60*1000 });
|
||||
|
Reference in New Issue
Block a user