propagate from branch 'i2p.i2p' (head 9ca94e960929c6af5dea1085105278d2f33217f2)

to branch 'i2p.i2p.zzz.dhtsnark' (head 1f23a71b0fa5169c220f3f21dd705e1fcfbb1b5d)
This commit is contained in:
zzz
2012-06-19 23:31:53 +00:00
42 changed files with 873 additions and 266 deletions

View File

@ -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);

View File

@ -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...

View File

@ -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;

View File

@ -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
*/

View File

@ -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."));
}
}
}

View File

@ -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)

View File

@ -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);

View File

@ -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("&nbsp;");
} else if (_manager.util().connected()) {
if (isDegraded)
out.write("<a href=\"/i2psnark/?action=StopAll&amp;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&amp;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("=", "&#61;");
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 + "&amp;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 + "&amp;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 + "&amp;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 + "&amp;nonce=" + _nonce + "\"><img title=\"");

View File

@ -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 />

View File

@ -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

View File

@ -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() {

View File

@ -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());
}
}

View File

@ -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");

View File

@ -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")) {

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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);

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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
*/

View File

@ -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
*/

View File

@ -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();
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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() {

View File

@ -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;
}
/**

View File

@ -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;
}

View File

@ -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 {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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:

View File

@ -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 = "";

View File

@ -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 });