diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java index c43651610..3dcb535cb 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java @@ -1202,22 +1202,23 @@ class PeerCoordinator implements PeerListener boolean skipped = false; for(Piece piece : wantedPieces) { if (piece.getId() == savedPiece) { - if (peer.isCompleted() && piece.getPeerCount() > 1) { + if (peer.isCompleted() && piece.getPeerCount() > 1 && + wantedPieces.size() > 2*END_GAME_THRESHOLD) { // Try to preserve rarest-first - // by not requesting a partial piece that non-seeders also have + // by not requesting a partial piece that at least two non-seeders also have // from a seeder - boolean nonSeeds = false; + int nonSeeds = 0; for (Peer pr : peers) { PeerState state = pr.state; if (state == null) continue; BitField bf = state.bitfield; if (bf == null) continue; if (bf.get(savedPiece) && !pr.isCompleted()) { - nonSeeds = true; - break; + if (++nonSeeds > 1) + break; } } - if (nonSeeds) { + if (nonSeeds > 1) { skipped = true; break; } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java index baad6df99..5d385024f 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java @@ -671,9 +671,9 @@ public class PluginStarter implements Runnable { for(ClientAppConfig app : apps) { // If the client is a running ClientApp that we want to stop, // bypass all the logic below. - ClientApp ca = ctx.routerAppManager().getClientApp(app.className, LoadClientAppsJob.parseArgs(app.args)); - if (ca != null && ca.getState() == ClientAppState.RUNNING) { - if (action.equals("stop")) { + if (action.equals("stop")) { + ClientApp ca = ctx.routerAppManager().getClientApp(app.className, LoadClientAppsJob.parseArgs(app.args)); + if (ca != null && ca.getState() == ClientAppState.RUNNING) { try { ca.shutdown(LoadClientAppsJob.parseArgs(app.stopargs)); } catch (Throwable t) { diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java b/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java index 6a5bf7fa3..043baba68 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java +++ b/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java @@ -391,7 +391,11 @@ class SAMv3Handler extends SAMv1Handler } else if (domain.equals("NAMING")) { canContinue = execNamingMessage(opcode, props); } else if (domain.equals("DATAGRAM")) { + // TODO not yet overridden, ID is ignored, most recent DATAGRAM session is used canContinue = execDatagramMessage(opcode, props); + } else if (domain.equals("RAW")) { + // TODO not yet overridden, ID is ignored, most recent RAW session is used + canContinue = execRawMessage(opcode, props); } else { if (_log.shouldLog(Log.DEBUG)) _log.debug("Unrecognized message domain: \"" @@ -694,10 +698,10 @@ class SAMv3Handler extends SAMv1Handler else { if (_log.shouldLog(Log.DEBUG)) - _log.debug ( "Unrecognized RAW message opcode: \"" + _log.debug ( "Unrecognized STREAM message opcode: \"" + opcode + "\"" ); try { - notifyStreamResult(true, "I2P_ERROR", "Unrecognized RAW message opcode: "+opcode ); + notifyStreamResult(true, "I2P_ERROR", "Unrecognized STREAM message opcode: "+opcode ); } catch (IOException e) {} return false; } @@ -716,9 +720,9 @@ class SAMv3Handler extends SAMv1Handler String dest = props.getProperty("DESTINATION"); if (dest == null) { - notifyStreamResult(verbose, "I2P_ERROR", "Destination not specified in RAW SEND message"); + notifyStreamResult(verbose, "I2P_ERROR", "Destination not specified in STREAM CONNECT message"); if (_log.shouldLog(Log.DEBUG)) - _log.debug("Destination not specified in RAW SEND message"); + _log.debug("Destination not specified in STREAM CONNECT message"); return false; } props.remove("DESTINATION"); diff --git a/apps/streaming/java/src/net/i2p/client/streaming/impl/PacketQueue.java b/apps/streaming/java/src/net/i2p/client/streaming/impl/PacketQueue.java index 1fdbb943f..102bfa6cc 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/impl/PacketQueue.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/impl/PacketQueue.java @@ -44,6 +44,7 @@ class PacketQueue implements SendMessageStatusListener { private static final int FINAL_TAGS_TO_SEND = 4; private static final int FINAL_TAG_THRESHOLD = 2; private static final long REMOVE_EXPIRED_TIME = 67*1000; + private static final boolean ENABLE_STATUS_LISTEN = false; public PacketQueue(I2PAppContext context, I2PSession session, ConnectionManager mgr) { _context = context; @@ -134,7 +135,7 @@ class PacketQueue implements SendMessageStatusListener { if (con != null) { if (con.isInbound()) options.setSendLeaseSet(false); - else + else if (ENABLE_STATUS_LISTEN) listenForStatus = true; } options.setTagsToSend(INITIAL_TAGS_TO_SEND); diff --git a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java index eee7e9fbc..12132726d 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java +++ b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java @@ -614,7 +614,7 @@ public class WebMail extends HttpServlet } catch (Exception e1) { showBody = false; - reason += _("Part ({0}) not shown, because of {1}", ident, e1.getClass().getName()) + br; + reason += _("Part ({0}) not shown, because of {1}", ident, e1.toString()) + br; } } if( html ) diff --git a/core/java/src/net/i2p/client/DomainSocketFactory.java b/core/java/src/net/i2p/client/DomainSocketFactory.java new file mode 100644 index 000000000..cf8908499 --- /dev/null +++ b/core/java/src/net/i2p/client/DomainSocketFactory.java @@ -0,0 +1,45 @@ +package net.i2p.client; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; + +import net.i2p.I2PAppContext; + +/** + * Bridge to Unix domain socket (or similar). + *
+ * This is a stub that does nothing. + * This class is replaced in the Android build. + * + * @author str4d + * @since 0.9.14 + */ +public class DomainSocketFactory { + public static String I2CP_SOCKET_ADDRESS = "net.i2p.client.i2cp"; + + /** + * @throws UnsupportedOperationException always + */ + public DomainSocketFactory(I2PAppContext context) { + throw new UnsupportedOperationException(); + } + + /** + * Override in Android. + * @throws IOException + * @throws UnsupportedOperationException always + */ + public Socket createSocket(String name) throws IOException { + throw new UnsupportedOperationException(); + } + + /** + * Override in Android. + * @throws IOException + * @throws UnsupportedOperationException always + */ + public ServerSocket createServerSocket(String name) throws IOException { + throw new UnsupportedOperationException(); + } +} diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java index 20aa43550..d14672647 100644 --- a/core/java/src/net/i2p/client/I2PSessionImpl.java +++ b/core/java/src/net/i2p/client/I2PSessionImpl.java @@ -52,6 +52,7 @@ import net.i2p.util.LHMCache; import net.i2p.util.Log; import net.i2p.util.OrderedProperties; import net.i2p.util.SimpleTimer2; +import net.i2p.util.SystemVersion; import net.i2p.util.VersionComparator; /** @@ -157,6 +158,12 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa protected static final String PROP_USER = "i2cp.username"; protected static final String PROP_PW = "i2cp.password"; + /** + * Use Unix domain socket (or similar) to connect to a router + * @since 0.9.14 + */ + protected static final String PROP_DOMAIN_SOCKET = "i2cp.domainSocket"; + private static final long VERIFY_USAGE_TIME = 60*1000; private static final long MAX_SEND_WAIT = 10*1000; @@ -279,6 +286,10 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa if (_context.isRouterContext()) // just for logging return "[internal connection]"; + else if (SystemVersion.isAndroid() && + Boolean.parseBoolean(_options.getProperty(PROP_DOMAIN_SOCKET))) + // just for logging + return "[Domain socket connection]"; return _options.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1"); } @@ -287,7 +298,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa * @since 0.9.7 was in loadConfig() */ private int getPort() { - if (_context.isRouterContext()) + if (_context.isRouterContext() || + (SystemVersion.isAndroid() && + Boolean.parseBoolean(_options.getProperty(PROP_DOMAIN_SOCKET)))) // just for logging return 0; String portNum = _options.getProperty(I2PClient.PROP_TCP_PORT, LISTEN_PORT + ""); @@ -447,7 +460,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa try { // protect w/ closeSocket() synchronized(_stateLock) { - // If we are in the router JVM, connect using the interal queue + // If we are in the router JVM, connect using the internal queue if (_context.isRouterContext()) { // _socket and _writer remain null InternalClientManager mgr = _context.internalClientManager(); @@ -457,7 +470,11 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa _queue = mgr.connect(); _reader = new QueuedI2CPMessageReader(_queue, this); } else { - if (Boolean.parseBoolean(_options.getProperty(PROP_ENABLE_SSL))) { + if (SystemVersion.isAndroid() && + Boolean.parseBoolean(_options.getProperty(PROP_DOMAIN_SOCKET))) { + final DomainSocketFactory fact = new DomainSocketFactory(_context); + _socket = fact.createSocket(DomainSocketFactory.I2CP_SOCKET_ADDRESS); + } else if (Boolean.parseBoolean(_options.getProperty(PROP_ENABLE_SSL))) { try { I2PSSLSocketFactory fact = new I2PSSLSocketFactory(_context, false, "certificates/i2cp"); _socket = fact.createSocket(_hostname, _portNum); diff --git a/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java b/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java index d22bafd2e..246af5403 100644 --- a/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java +++ b/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java @@ -19,6 +19,7 @@ import net.i2p.data.DataFormatException; import net.i2p.data.Destination; import net.i2p.data.Hash; import net.i2p.data.Signature; +import net.i2p.data.SigningPublicKey; import net.i2p.util.Log; /** @@ -34,16 +35,11 @@ public final class I2PDatagramDissector { private final DSAEngine dsaEng = DSAEngine.getInstance(); private final SHA256Generator hashGen = SHA256Generator.getInstance(); - private Hash rxHash; - + private byte[] rxHash; private Signature rxSign; - private Destination rxDest; - private final byte[] rxPayload = new byte[DGRAM_BUFSIZE]; - private int rxPayloadLen; - private boolean valid; /** @@ -56,6 +52,17 @@ public final class I2PDatagramDissector { * Load an I2P repliable datagram into the dissector. * Does NOT verify the signature. * + * Format is: + *The datagram format implemented here includes -the sender's {@link net.i2p.data.Destination}, the payload, and a hash of the -payload (signed by the sender's {@link net.i2p.data.SigningPrivateKey}).
+the sender's {@link net.i2p.data.Destination}, the payload, and a signature +(signed by the sender's {@link net.i2p.data.SigningPrivateKey}). +For DSA_SHA1 destinations, the signature is of the SHA-256 Hash of the payload. +For other destination types, the signature is of the payload itself. +