propagate from branch 'i2p.i2p.zzz.test4' (head 56ba5c9f8d0779f91259df05b7be0826fe08cd84)
to branch 'i2p.i2p' (head 0ba2cc80363f5c7086bce7a43f43a9b095ed2d9e)
This commit is contained in:
@ -168,6 +168,7 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl
|
||||
//System.out.println("Refilling " + (++refillCount) + " after " + diff + " for the PRNG took " + refillTime);
|
||||
}
|
||||
|
||||
/*****
|
||||
public static void main(String args[]) {
|
||||
try {
|
||||
AsyncFortunaStandalone rand = new AsyncFortunaStandalone(null); // Will cause NPEs above; fix this if you want to test! Sorry...
|
||||
@ -195,4 +196,5 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl
|
||||
} catch (Exception e) { e.printStackTrace(); }
|
||||
try { Thread.sleep(5*60*1000); } catch (InterruptedException ie) {}
|
||||
}
|
||||
*****/
|
||||
}
|
||||
|
@ -351,6 +351,7 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl
|
||||
}
|
||||
}
|
||||
|
||||
/*****
|
||||
public static void main(String args[]) {
|
||||
byte in[] = new byte[16];
|
||||
byte out[] = new byte[16];
|
||||
@ -379,7 +380,7 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl
|
||||
long after = System.currentTimeMillis();
|
||||
System.out.println("encrypting 4MB took " + (after-beforeAll));
|
||||
} catch (Exception e) { e.printStackTrace(); }
|
||||
/*
|
||||
****/ /*
|
||||
FortunaStandalone f = new FortunaStandalone();
|
||||
java.util.HashMap props = new java.util.HashMap();
|
||||
byte initSeed[] = new byte[1234];
|
||||
@ -394,5 +395,7 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl
|
||||
long time = System.currentTimeMillis() - before;
|
||||
System.out.println("512MB took " + time + ", or " + (8*64d)/((double)time/1000d) +"MBps");
|
||||
*/
|
||||
/*****
|
||||
}
|
||||
*****/
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package net.i2p;
|
||||
import java.io.File;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.client.naming.NamingService;
|
||||
@ -21,7 +22,9 @@ import net.i2p.crypto.KeyGenerator;
|
||||
import net.i2p.crypto.SHA256Generator;
|
||||
import net.i2p.crypto.SessionKeyManager;
|
||||
import net.i2p.crypto.TransientSessionKeyManager;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.RoutingKeyGenerator;
|
||||
import net.i2p.internal.InternalClientManager;
|
||||
import net.i2p.stat.StatManager;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
@ -363,10 +366,12 @@ public class I2PAppContext {
|
||||
if (_tmpDir == null) {
|
||||
String d = getProperty("i2p.dir.temp", System.getProperty("java.io.tmpdir"));
|
||||
// our random() probably isn't warmed up yet
|
||||
String f = "i2p-" + Math.abs((new java.util.Random()).nextInt()) + ".tmp";
|
||||
byte[] rand = new byte[6];
|
||||
(new Random()).nextBytes(rand);
|
||||
String f = "i2p-" + Base64.encode(rand) + ".tmp";
|
||||
_tmpDir = new SecureDirectory(d, f);
|
||||
if (_tmpDir.exists()) {
|
||||
// good or bad ?
|
||||
// good or bad ? loop and try again?
|
||||
} else if (_tmpDir.mkdir()) {
|
||||
_tmpDir.deleteOnExit();
|
||||
} else {
|
||||
@ -843,4 +848,13 @@ public class I2PAppContext {
|
||||
public boolean isRouterContext() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to connect to the router in the same JVM.
|
||||
* @return always null in I2PAppContext, the client manager if in RouterContext
|
||||
* @since 0.8.3
|
||||
*/
|
||||
public InternalClientManager internalClientManager() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
import net.i2p.data.i2cp.I2CPMessage;
|
||||
import net.i2p.data.i2cp.I2CPMessageImpl;
|
||||
import net.i2p.data.i2cp.I2CPMessageException;
|
||||
import net.i2p.internal.PoisonI2CPMessage;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
|
||||
/**
|
||||
@ -50,7 +51,7 @@ class ClientWriterRunner implements Runnable {
|
||||
public void stopWriting() {
|
||||
_messagesToWrite.clear();
|
||||
try {
|
||||
_messagesToWrite.put(new PoisonMessage());
|
||||
_messagesToWrite.put(new PoisonI2CPMessage());
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
|
||||
@ -62,7 +63,7 @@ class ClientWriterRunner implements Runnable {
|
||||
} catch (InterruptedException ie) {
|
||||
continue;
|
||||
}
|
||||
if (msg.getType() == PoisonMessage.MESSAGE_TYPE)
|
||||
if (msg.getType() == PoisonI2CPMessage.MESSAGE_TYPE)
|
||||
break;
|
||||
// only thread, we don't need synchronized
|
||||
try {
|
||||
@ -80,18 +81,4 @@ class ClientWriterRunner implements Runnable {
|
||||
}
|
||||
_messagesToWrite.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* End-of-stream msg used to stop the concurrent queue
|
||||
* See http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html
|
||||
*
|
||||
*/
|
||||
private static class PoisonMessage extends I2CPMessageImpl {
|
||||
public static final int MESSAGE_TYPE = 999999;
|
||||
public int getType() {
|
||||
return MESSAGE_TYPE;
|
||||
}
|
||||
public void doReadMessage(InputStream buf, int size) throws I2CPMessageException, IOException {}
|
||||
public byte[] doWriteMessage() throws I2CPMessageException, IOException { return null; }
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,9 @@ import net.i2p.data.i2cp.I2CPMessage;
|
||||
import net.i2p.data.i2cp.DestReplyMessage;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
|
||||
/**
|
||||
* Handle I2CP dest replies from the router
|
||||
*/
|
||||
@ -22,6 +25,12 @@ class DestReplyMessageHandler extends HandlerImpl {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Handle message " + message);
|
||||
DestReplyMessage msg = (DestReplyMessage) message;
|
||||
((I2PSimpleSession)session).destReceived(msg.getDestination());
|
||||
Destination d = msg.getDestination();
|
||||
if (d != null)
|
||||
session.destReceived(d);
|
||||
Hash h = msg.getHash();
|
||||
if (h != null)
|
||||
session.destLookupFailed(h);
|
||||
// else let it time out
|
||||
}
|
||||
}
|
||||
|
183
core/java/src/net/i2p/client/I2CPSSLSocketFactory.java
Normal file
183
core/java/src/net/i2p/client/I2CPSSLSocketFactory.java
Normal file
@ -0,0 +1,183 @@
|
||||
package net.i2p.client;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.Socket;
|
||||
import java.security.KeyStore;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateExpiredException;
|
||||
import java.security.cert.CertificateNotYetValidException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Loads trusted ASCII certs from ~/.i2p/certificates/ and $CWD/certificates/.
|
||||
* Keeps a single static SSLContext for the whole JVM.
|
||||
*
|
||||
* @author zzz
|
||||
* @since 0.8.3
|
||||
*/
|
||||
class I2CPSSLSocketFactory {
|
||||
|
||||
private static final Object _initLock = new Object();
|
||||
private static SSLSocketFactory _factory;
|
||||
private static Log _log;
|
||||
|
||||
private static final String CERT_DIR = "certificates";
|
||||
|
||||
/**
|
||||
* Initializes the static SSL Context if required, then returns a socket
|
||||
* to the host.
|
||||
*
|
||||
* @param ctx just for logging
|
||||
* @throws IOException on init error or usual socket errors
|
||||
*/
|
||||
public static Socket createSocket(I2PAppContext ctx, String host, int port) throws IOException {
|
||||
synchronized(_initLock) {
|
||||
if (_factory == null) {
|
||||
_log = ctx.logManager().getLog(I2CPSSLSocketFactory.class);
|
||||
initSSLContext(ctx);
|
||||
if (_factory == null)
|
||||
throw new IOException("Unable to create SSL Context for I2CP Client");
|
||||
_log.info("I2CP Client-side SSL Context initialized");
|
||||
}
|
||||
}
|
||||
return _factory.createSocket(host, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads certs from
|
||||
* the ~/.i2p/certificates/ and $CWD/certificates/ directories.
|
||||
*/
|
||||
private static void initSSLContext(I2PAppContext context) {
|
||||
KeyStore ks;
|
||||
try {
|
||||
ks = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
ks.load(null, "".toCharArray());
|
||||
} catch (GeneralSecurityException gse) {
|
||||
_log.error("Key Store init error", gse);
|
||||
return;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Key Store init error", ioe);
|
||||
return;
|
||||
}
|
||||
|
||||
File dir = new File(context.getConfigDir(), CERT_DIR);
|
||||
int adds = addCerts(dir, ks);
|
||||
int totalAdds = adds;
|
||||
if (adds > 0 && _log.shouldLog(Log.INFO))
|
||||
_log.info("Loaded " + adds + " trusted certificates from " + dir.getAbsolutePath());
|
||||
|
||||
File dir2 = new File(System.getProperty("user.dir"), CERT_DIR);
|
||||
if (!dir.getAbsolutePath().equals(dir2.getAbsolutePath())) {
|
||||
adds = addCerts(dir2, ks);
|
||||
totalAdds += adds;
|
||||
if (adds > 0 && _log.shouldLog(Log.INFO))
|
||||
_log.info("Loaded " + adds + " trusted certificates from " + dir.getAbsolutePath());
|
||||
}
|
||||
if (totalAdds > 0) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Loaded total of " + totalAdds + " new trusted certificates");
|
||||
} else {
|
||||
_log.error("No trusted certificates loaded (looked in " +
|
||||
dir.getAbsolutePath() + (dir.getAbsolutePath().equals(dir2.getAbsolutePath()) ? "" : (" and " + dir2.getAbsolutePath())) +
|
||||
", I2CP SSL client connections will fail. " +
|
||||
"Copy the file certificates/i2cp.local.crt from the router to the directory.");
|
||||
// don't continue, since we didn't load the system keystore, we have nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
SSLContext sslc = SSLContext.getInstance("TLS");
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
tmf.init(ks);
|
||||
sslc.init(null, tmf.getTrustManagers(), context.random());
|
||||
_factory = sslc.getSocketFactory();
|
||||
} catch (GeneralSecurityException gse) {
|
||||
_log.error("SSL context init error", gse);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all X509 Certs from a directory and add them to the
|
||||
* trusted set of certificates in the key store
|
||||
*
|
||||
* @return number successfully added
|
||||
*/
|
||||
private static int addCerts(File dir, KeyStore ks) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Looking for X509 Certificates in " + dir.getAbsolutePath());
|
||||
int added = 0;
|
||||
if (dir.exists() && dir.isDirectory()) {
|
||||
File[] files = dir.listFiles();
|
||||
if (files != null) {
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
File f = files[i];
|
||||
if (!f.isFile())
|
||||
continue;
|
||||
// use file name as alias
|
||||
String alias = f.getName().toLowerCase();
|
||||
boolean success = addCert(f, alias, ks);
|
||||
if (success)
|
||||
added++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an X509 Cert from a file and add it to the
|
||||
* trusted set of certificates in the key store
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
private static boolean addCert(File file, String alias, KeyStore ks) {
|
||||
InputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(file);
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
X509Certificate cert = (X509Certificate)cf.generateCertificate(fis);
|
||||
if (_log.shouldLog(Log.INFO)) {
|
||||
_log.info("Read X509 Certificate from " + file.getAbsolutePath() +
|
||||
" Issuer: " + cert.getIssuerX500Principal() +
|
||||
"; Valid From: " + cert.getNotBefore() +
|
||||
" To: " + cert.getNotAfter());
|
||||
}
|
||||
try {
|
||||
cert.checkValidity();
|
||||
} catch (CertificateExpiredException cee) {
|
||||
_log.error("Rejecting expired X509 Certificate: " + file.getAbsolutePath(), cee);
|
||||
return false;
|
||||
} catch (CertificateNotYetValidException cnyve) {
|
||||
_log.error("Rejecting X509 Certificate not yet valid: " + file.getAbsolutePath(), cnyve);
|
||||
return false;
|
||||
}
|
||||
ks.setCertificateEntry(alias, cert);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Now trusting X509 Certificate, Issuer: " + cert.getIssuerX500Principal());
|
||||
} catch (GeneralSecurityException gse) {
|
||||
_log.error("Error reading X509 Certificate: " + file.getAbsolutePath(), gse);
|
||||
return false;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error reading X509 Certificate: " + file.getAbsolutePath(), ioe);
|
||||
return false;
|
||||
} finally {
|
||||
try { if (fis != null) fis.close(); } catch (IOException foo) {}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -10,6 +10,8 @@ package net.i2p.client;
|
||||
*/
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.i2cp.BandwidthLimitsMessage;
|
||||
import net.i2p.data.i2cp.DestReplyMessage;
|
||||
import net.i2p.data.i2cp.DisconnectMessage;
|
||||
import net.i2p.data.i2cp.MessagePayloadMessage;
|
||||
import net.i2p.data.i2cp.MessageStatusMessage;
|
||||
@ -36,6 +38,8 @@ class I2PClientMessageHandlerMap {
|
||||
highest = Math.max(highest, MessagePayloadMessage.MESSAGE_TYPE);
|
||||
highest = Math.max(highest, MessageStatusMessage.MESSAGE_TYPE);
|
||||
highest = Math.max(highest, SetDateMessage.MESSAGE_TYPE);
|
||||
highest = Math.max(highest, DestReplyMessage.MESSAGE_TYPE);
|
||||
highest = Math.max(highest, BandwidthLimitsMessage.MESSAGE_TYPE);
|
||||
|
||||
_handlers = new I2CPMessageHandler[highest+1];
|
||||
_handlers[DisconnectMessage.MESSAGE_TYPE] = new DisconnectMessageHandler(context);
|
||||
@ -44,6 +48,8 @@ class I2PClientMessageHandlerMap {
|
||||
_handlers[MessagePayloadMessage.MESSAGE_TYPE] = new MessagePayloadMessageHandler(context);
|
||||
_handlers[MessageStatusMessage.MESSAGE_TYPE] = new MessageStatusMessageHandler(context);
|
||||
_handlers[SetDateMessage.MESSAGE_TYPE] = new SetDateMessageHandler(context);
|
||||
_handlers[DestReplyMessage.MESSAGE_TYPE] = new DestReplyMessageHandler(context);
|
||||
_handlers[BandwidthLimitsMessage.MESSAGE_TYPE] = new BWLimitsMessageHandler(context);
|
||||
}
|
||||
|
||||
public I2CPMessageHandler getHandler(int messageTypeId) {
|
||||
|
@ -138,13 +138,21 @@ public interface I2PSession {
|
||||
public SigningPrivateKey getPrivateKey();
|
||||
|
||||
/**
|
||||
* Lookup up a Hash
|
||||
*
|
||||
* Lookup a Destination by Hash.
|
||||
* Blocking. Waits a max of 10 seconds by default.
|
||||
*/
|
||||
public Destination lookupDest(Hash h) throws I2PSessionException;
|
||||
|
||||
/**
|
||||
* Get the current bandwidth limits
|
||||
* Blocking.
|
||||
* @param maxWait ms
|
||||
* @since 0.8.3
|
||||
* @return null on failure
|
||||
*/
|
||||
public Destination lookupDest(Hash h, long maxWait) throws I2PSessionException;
|
||||
|
||||
/**
|
||||
* Get the current bandwidth limits. Blocking.
|
||||
*/
|
||||
public int[] bandwidthLimits() throws I2PSessionException;
|
||||
|
||||
|
@ -15,7 +15,6 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@ -23,6 +22,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataFormatException;
|
||||
@ -33,14 +34,18 @@ import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.SessionTag;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.i2cp.DestLookupMessage;
|
||||
import net.i2p.data.i2cp.GetBandwidthLimitsMessage;
|
||||
import net.i2p.data.i2cp.GetDateMessage;
|
||||
import net.i2p.data.i2cp.I2CPMessage;
|
||||
import net.i2p.data.i2cp.I2CPMessageException;
|
||||
import net.i2p.data.i2cp.I2CPMessageReader;
|
||||
import net.i2p.data.i2cp.MessagePayloadMessage;
|
||||
import net.i2p.data.i2cp.SessionId;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.InternalSocket;
|
||||
import net.i2p.internal.I2CPMessageQueue;
|
||||
import net.i2p.internal.InternalClientManager;
|
||||
import net.i2p.internal.QueuedI2CPMessageReader;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
@ -66,9 +71,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
/** currently granted lease set, or null */
|
||||
private LeaseSet _leaseSet;
|
||||
|
||||
/** hostname of router */
|
||||
/** hostname of router - will be null if in RouterContext */
|
||||
protected String _hostname;
|
||||
/** port num to router */
|
||||
/** port num to router - will be 0 if in RouterContext */
|
||||
protected int _portNum;
|
||||
/** socket for comm */
|
||||
protected Socket _socket;
|
||||
@ -79,6 +84,13 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
/** where we pipe our messages */
|
||||
protected /* FIXME final FIXME */OutputStream _out;
|
||||
|
||||
/**
|
||||
* Used for internal connections to the router.
|
||||
* If this is set, _socket, _writer, and _out will be null.
|
||||
* @since 0.8.3
|
||||
*/
|
||||
protected I2CPMessageQueue _queue;
|
||||
|
||||
/** who we send events to */
|
||||
protected I2PSessionListener _sessionListener;
|
||||
|
||||
@ -86,6 +98,11 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
protected I2CPMessageProducer _producer;
|
||||
/** map of Long --> MessagePayloadMessage */
|
||||
protected Map<Long, MessagePayloadMessage> _availableMessages;
|
||||
|
||||
/** hashes of lookups we are waiting for */
|
||||
protected final LinkedBlockingQueue<LookupWaiter> _pendingLookups = new LinkedBlockingQueue();
|
||||
protected final Object _bwReceivedLock = new Object();
|
||||
protected int[] _bwLimits;
|
||||
|
||||
protected I2PClientMessageHandlerMap _handlerMap;
|
||||
|
||||
@ -122,6 +139,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
private long _lastActivity;
|
||||
private boolean _isReduced;
|
||||
|
||||
/** SSL interface (only) @since 0.8.3 */
|
||||
protected static final String PROP_ENABLE_SSL = "i2cp.SSL";
|
||||
|
||||
void dateUpdated() {
|
||||
_dateReceived = true;
|
||||
synchronized (_dateReceivedLock) {
|
||||
@ -172,19 +192,24 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
protected void loadConfig(Properties options) {
|
||||
_options = new Properties();
|
||||
_options.putAll(filter(options));
|
||||
_hostname = _options.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
|
||||
String portNum = _options.getProperty(I2PClient.PROP_TCP_PORT, LISTEN_PORT + "");
|
||||
try {
|
||||
_portNum = Integer.parseInt(portNum);
|
||||
} catch (NumberFormatException nfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix() + "Invalid port number specified, defaulting to "
|
||||
+ LISTEN_PORT, nfe);
|
||||
_portNum = LISTEN_PORT;
|
||||
if (_context.isRouterContext()) {
|
||||
// just for logging
|
||||
_hostname = "[internal connection]";
|
||||
} else {
|
||||
_hostname = _options.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
|
||||
String portNum = _options.getProperty(I2PClient.PROP_TCP_PORT, LISTEN_PORT + "");
|
||||
try {
|
||||
_portNum = Integer.parseInt(portNum);
|
||||
} catch (NumberFormatException nfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(getPrefix() + "Invalid port number specified, defaulting to "
|
||||
+ LISTEN_PORT, nfe);
|
||||
_portNum = LISTEN_PORT;
|
||||
}
|
||||
}
|
||||
|
||||
// auto-add auth if required, not set in the options, and we are in the same JVM
|
||||
if (_context.isRouterContext() &&
|
||||
// auto-add auth if required, not set in the options, and we are not in the same JVM
|
||||
if ((!_context.isRouterContext()) &&
|
||||
Boolean.valueOf(_context.getProperty("i2cp.auth")).booleanValue() &&
|
||||
((!options.containsKey("i2cp.username")) || (!options.containsKey("i2cp.password")))) {
|
||||
String configUser = _context.getProperty("i2cp.username");
|
||||
@ -272,10 +297,6 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
setOpening(true);
|
||||
_closed = false;
|
||||
_availabilityNotifier.stopNotifying();
|
||||
I2PThread notifier = new I2PThread(_availabilityNotifier);
|
||||
notifier.setName("Notifier " + _myDestination.calculateHash().toBase64().substring(0,4));
|
||||
notifier.setDaemon(true);
|
||||
notifier.start();
|
||||
|
||||
if ( (_options != null) &&
|
||||
(I2PClient.PROP_RELIABILITY_GUARANTEED.equals(_options.getProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT))) ) {
|
||||
@ -285,17 +306,32 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
|
||||
long startConnect = _context.clock().now();
|
||||
try {
|
||||
// If we are in the router JVM, connect using the interal pseudo-socket
|
||||
_socket = InternalSocket.getSocket(_hostname, _portNum);
|
||||
// _socket.setSoTimeout(1000000); // Uhmmm we could really-really use a real timeout, and handle it.
|
||||
_out = _socket.getOutputStream();
|
||||
synchronized (_out) {
|
||||
_out.write(I2PClient.PROTOCOL_BYTE);
|
||||
_out.flush();
|
||||
// If we are in the router JVM, connect using the interal queue
|
||||
if (_context.isRouterContext()) {
|
||||
// _socket, _out, and _writer remain null
|
||||
InternalClientManager mgr = _context.internalClientManager();
|
||||
if (mgr == null)
|
||||
throw new I2PSessionException("Router is not ready for connections");
|
||||
// the following may throw an I2PSessionException
|
||||
_queue = mgr.connect();
|
||||
_reader = new QueuedI2CPMessageReader(_queue, this);
|
||||
} else {
|
||||
if (Boolean.valueOf(_options.getProperty(PROP_ENABLE_SSL)).booleanValue())
|
||||
_socket = I2CPSSLSocketFactory.createSocket(_context, _hostname, _portNum);
|
||||
else
|
||||
_socket = new Socket(_hostname, _portNum);
|
||||
// _socket.setSoTimeout(1000000); // Uhmmm we could really-really use a real timeout, and handle it.
|
||||
_out = _socket.getOutputStream();
|
||||
synchronized (_out) {
|
||||
_out.write(I2PClient.PROTOCOL_BYTE);
|
||||
_out.flush();
|
||||
}
|
||||
_writer = new ClientWriterRunner(_out, this);
|
||||
InputStream in = _socket.getInputStream();
|
||||
_reader = new I2CPMessageReader(in, this);
|
||||
}
|
||||
_writer = new ClientWriterRunner(_out, this);
|
||||
InputStream in = _socket.getInputStream();
|
||||
_reader = new I2CPMessageReader(in, this);
|
||||
Thread notifier = new I2PAppThread(_availabilityNotifier, "ClientNotifier " + getPrefix(), true);
|
||||
notifier.start();
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "before startReading");
|
||||
_reader.startReading();
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Before getDate");
|
||||
@ -435,6 +471,10 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This notifies the client of payload messages.
|
||||
* Needs work.
|
||||
*/
|
||||
protected class AvailabilityNotifier implements Runnable {
|
||||
private List _pendingIds;
|
||||
private List _pendingSizes;
|
||||
@ -497,8 +537,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
}
|
||||
|
||||
/**
|
||||
* Recieve notification of some I2CP message and handle it if possible
|
||||
*
|
||||
* The I2CPMessageEventListener callback.
|
||||
* Recieve notification of some I2CP message and handle it if possible.
|
||||
* @param reader unused
|
||||
*/
|
||||
public void messageReceived(I2CPMessageReader reader, I2CPMessage message) {
|
||||
I2CPMessageHandler handler = _handlerMap.getHandler(message.getType());
|
||||
@ -515,7 +556,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
}
|
||||
|
||||
/**
|
||||
* Recieve notifiation of an error reading the I2CP stream
|
||||
* The I2CPMessageEventListener callback.
|
||||
* Recieve notifiation of an error reading the I2CP stream.
|
||||
* @param reader unused
|
||||
* @param error non-null
|
||||
*/
|
||||
public void readError(I2CPMessageReader reader, Exception error) {
|
||||
@ -567,9 +610,14 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
* @throws I2PSessionException if the message is malformed or there is an error writing it out
|
||||
*/
|
||||
void sendMessage(I2CPMessage message) throws I2PSessionException {
|
||||
if (isClosed() || _writer == null)
|
||||
if (isClosed())
|
||||
throw new I2PSessionException("Already closed");
|
||||
_writer.addMessage(message);
|
||||
else if (_queue != null)
|
||||
_queue.offer(message); // internal
|
||||
else if (_writer == null)
|
||||
throw new I2PSessionException("Already closed");
|
||||
else
|
||||
_writer.addMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -581,8 +629,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
// Only log as WARN if the router went away
|
||||
int level;
|
||||
String msgpfx;
|
||||
if ((error instanceof EOFException) ||
|
||||
(error.getMessage() != null && error.getMessage().startsWith("Pipe closed"))) {
|
||||
if (error instanceof EOFException) {
|
||||
level = Log.WARN;
|
||||
msgpfx = "Router closed connection: ";
|
||||
} else {
|
||||
@ -631,7 +678,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
_log.warn("Error destroying the session", ipe);
|
||||
}
|
||||
}
|
||||
_availabilityNotifier.stopNotifying();
|
||||
// SimpleSession does not initialize
|
||||
if (_availabilityNotifier != null)
|
||||
_availabilityNotifier.stopNotifying();
|
||||
_closed = true;
|
||||
_closing = false;
|
||||
closeSocket();
|
||||
@ -649,6 +698,10 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
_reader.stopReading();
|
||||
_reader = null;
|
||||
}
|
||||
if (_queue != null) {
|
||||
// internal
|
||||
_queue.close();
|
||||
}
|
||||
if (_writer != null) {
|
||||
_writer.stopWriting();
|
||||
_writer = null;
|
||||
@ -666,7 +719,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
}
|
||||
|
||||
/**
|
||||
* Recieve notification that the I2CP connection was disconnected
|
||||
* The I2CPMessageEventListener callback.
|
||||
* Recieve notification that the I2CP connection was disconnected.
|
||||
* @param reader unused
|
||||
*/
|
||||
public void disconnected(I2CPMessageReader reader) {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Disconnected", new Exception("Disconnected"));
|
||||
@ -733,21 +788,113 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
buf.append(s);
|
||||
else
|
||||
buf.append(getClass().getSimpleName());
|
||||
buf.append(" #");
|
||||
if (_sessionId != null)
|
||||
buf.append(_sessionId.getSessionId());
|
||||
else
|
||||
buf.append("n/a");
|
||||
buf.append(" #").append(_sessionId.getSessionId());
|
||||
buf.append("]: ");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public Destination lookupDest(Hash h) throws I2PSessionException {
|
||||
return null;
|
||||
/** called by the message handler */
|
||||
void destReceived(Destination d) {
|
||||
Hash h = d.calculateHash();
|
||||
for (LookupWaiter w : _pendingLookups) {
|
||||
if (w.hash.equals(h)) {
|
||||
w.destination = d;
|
||||
synchronized (w) {
|
||||
w.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** called by the message handler */
|
||||
void destLookupFailed(Hash h) {
|
||||
for (LookupWaiter w : _pendingLookups) {
|
||||
if (w.hash.equals(h)) {
|
||||
synchronized (w) {
|
||||
w.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** called by the message handler */
|
||||
void bwReceived(int[] i) {
|
||||
_bwLimits = i;
|
||||
synchronized (_bwReceivedLock) {
|
||||
_bwReceivedLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple object to wait for lookup replies
|
||||
* @since 0.8.3
|
||||
*/
|
||||
private static class LookupWaiter {
|
||||
/** the request */
|
||||
public final Hash hash;
|
||||
/** the reply */
|
||||
public Destination destination;
|
||||
|
||||
public LookupWaiter(Hash h) {
|
||||
this.hash = h;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocking. Waits a max of 10 seconds by default.
|
||||
* See lookupDest with maxWait parameter to change.
|
||||
* Implemented in 0.8.3 in I2PSessionImpl;
|
||||
* previously was available only in I2PSimpleSession.
|
||||
* Multiple outstanding lookups are now allowed.
|
||||
* @return null on failure
|
||||
*/
|
||||
public Destination lookupDest(Hash h) throws I2PSessionException {
|
||||
return lookupDest(h, 10*1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocking.
|
||||
* @param maxWait ms
|
||||
* @since 0.8.3
|
||||
* @return null on failure
|
||||
*/
|
||||
public Destination lookupDest(Hash h, long maxWait) throws I2PSessionException {
|
||||
if (_closed)
|
||||
return null;
|
||||
LookupWaiter waiter = new LookupWaiter(h);
|
||||
_pendingLookups.offer(waiter);
|
||||
try {
|
||||
sendMessage(new DestLookupMessage(h));
|
||||
try {
|
||||
synchronized (waiter) {
|
||||
waiter.wait(maxWait);
|
||||
}
|
||||
} catch (InterruptedException ie) {}
|
||||
} finally {
|
||||
_pendingLookups.remove(waiter);
|
||||
}
|
||||
return waiter.destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocking. Waits a max of 5 seconds.
|
||||
* But shouldn't take long.
|
||||
* Implemented in 0.8.3 in I2PSessionImpl;
|
||||
* previously was available only in I2PSimpleSession.
|
||||
* Multiple outstanding lookups are now allowed.
|
||||
* @return null on failure
|
||||
*/
|
||||
public int[] bandwidthLimits() throws I2PSessionException {
|
||||
return null;
|
||||
if (_closed)
|
||||
return null;
|
||||
sendMessage(new GetBandwidthLimitsMessage());
|
||||
try {
|
||||
synchronized (_bwReceivedLock) {
|
||||
_bwReceivedLock.wait(5*1000);
|
||||
}
|
||||
} catch (InterruptedException ie) {}
|
||||
return _bwLimits;
|
||||
}
|
||||
|
||||
protected void updateActivity() {
|
||||
|
@ -19,8 +19,10 @@ import net.i2p.data.i2cp.DestLookupMessage;
|
||||
import net.i2p.data.i2cp.DestReplyMessage;
|
||||
import net.i2p.data.i2cp.GetBandwidthLimitsMessage;
|
||||
import net.i2p.data.i2cp.I2CPMessageReader;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.InternalSocket;
|
||||
import net.i2p.internal.I2CPMessageQueue;
|
||||
import net.i2p.internal.InternalClientManager;
|
||||
import net.i2p.internal.QueuedI2CPMessageReader;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
|
||||
/**
|
||||
* Create a new session for doing naming and bandwidth queries only. Do not create a Destination.
|
||||
@ -31,12 +33,6 @@ import net.i2p.util.InternalSocket;
|
||||
* @author zzz
|
||||
*/
|
||||
class I2PSimpleSession extends I2PSessionImpl2 {
|
||||
private boolean _destReceived;
|
||||
private /* FIXME final FIXME */ Object _destReceivedLock;
|
||||
private Destination _destination;
|
||||
private boolean _bwReceived;
|
||||
private /* FIXME final FIXME */ Object _bwReceivedLock;
|
||||
private int[] _bwLimits;
|
||||
|
||||
/**
|
||||
* Create a new session for doing naming and bandwidth queries only. Do not create a destination.
|
||||
@ -44,12 +40,12 @@ class I2PSimpleSession extends I2PSessionImpl2 {
|
||||
* @throws I2PSessionException if there is a problem
|
||||
*/
|
||||
public I2PSimpleSession(I2PAppContext context, Properties options) throws I2PSessionException {
|
||||
// Warning, does not call super()
|
||||
_context = context;
|
||||
_log = context.logManager().getLog(I2PSimpleSession.class);
|
||||
_handlerMap = new SimpleMessageHandlerMap(context);
|
||||
_closed = true;
|
||||
_closing = false;
|
||||
_availabilityNotifier = new AvailabilityNotifier();
|
||||
if (options == null)
|
||||
options = System.getProperties();
|
||||
loadConfig(options);
|
||||
@ -65,23 +61,32 @@ class I2PSimpleSession extends I2PSessionImpl2 {
|
||||
@Override
|
||||
public void connect() throws I2PSessionException {
|
||||
_closed = false;
|
||||
_availabilityNotifier.stopNotifying();
|
||||
I2PThread notifier = new I2PThread(_availabilityNotifier);
|
||||
notifier.setName("Simple Notifier");
|
||||
notifier.setDaemon(true);
|
||||
notifier.start();
|
||||
|
||||
try {
|
||||
// If we are in the router JVM, connect using the interal pseudo-socket
|
||||
_socket = InternalSocket.getSocket(_hostname, _portNum);
|
||||
_out = _socket.getOutputStream();
|
||||
synchronized (_out) {
|
||||
_out.write(I2PClient.PROTOCOL_BYTE);
|
||||
_out.flush();
|
||||
// If we are in the router JVM, connect using the interal queue
|
||||
if (_context.isRouterContext()) {
|
||||
// _socket, _out, and _writer remain null
|
||||
InternalClientManager mgr = _context.internalClientManager();
|
||||
if (mgr == null)
|
||||
throw new I2PSessionException("Router is not ready for connections");
|
||||
// the following may throw an I2PSessionException
|
||||
_queue = mgr.connect();
|
||||
_reader = new QueuedI2CPMessageReader(_queue, this);
|
||||
} else {
|
||||
if (Boolean.valueOf(getOptions().getProperty(PROP_ENABLE_SSL)).booleanValue())
|
||||
_socket = I2CPSSLSocketFactory.createSocket(_context, _hostname, _portNum);
|
||||
else
|
||||
_socket = new Socket(_hostname, _portNum);
|
||||
_out = _socket.getOutputStream();
|
||||
synchronized (_out) {
|
||||
_out.write(I2PClient.PROTOCOL_BYTE);
|
||||
_out.flush();
|
||||
}
|
||||
_writer = new ClientWriterRunner(_out, this);
|
||||
InputStream in = _socket.getInputStream();
|
||||
_reader = new I2CPMessageReader(in, this);
|
||||
}
|
||||
_writer = new ClientWriterRunner(_out, this);
|
||||
InputStream in = _socket.getInputStream();
|
||||
_reader = new I2CPMessageReader(in, this);
|
||||
// we do not receive payload messages, so we do not need an AvailabilityNotifier
|
||||
_reader.startReading();
|
||||
|
||||
} catch (UnknownHostException uhe) {
|
||||
@ -93,57 +98,6 @@ class I2PSimpleSession extends I2PSessionImpl2 {
|
||||
}
|
||||
}
|
||||
|
||||
/** called by the message handler */
|
||||
void destReceived(Destination d) {
|
||||
_destReceived = true;
|
||||
_destination = d;
|
||||
synchronized (_destReceivedLock) {
|
||||
_destReceivedLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
void bwReceived(int[] i) {
|
||||
_bwReceived = true;
|
||||
_bwLimits = i;
|
||||
synchronized (_bwReceivedLock) {
|
||||
_bwReceivedLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Destination lookupDest(Hash h) throws I2PSessionException {
|
||||
if (_closed)
|
||||
return null;
|
||||
_destReceivedLock = new Object();
|
||||
sendMessage(new DestLookupMessage(h));
|
||||
for (int i = 0; i < 10 && !_destReceived; i++) {
|
||||
try {
|
||||
synchronized (_destReceivedLock) {
|
||||
_destReceivedLock.wait(1000);
|
||||
}
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
_destReceived = false;
|
||||
return _destination;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] bandwidthLimits() throws I2PSessionException {
|
||||
if (_closed)
|
||||
return null;
|
||||
_bwReceivedLock = new Object();
|
||||
sendMessage(new GetBandwidthLimitsMessage());
|
||||
for (int i = 0; i < 5 && !_bwReceived; i++) {
|
||||
try {
|
||||
synchronized (_bwReceivedLock) {
|
||||
_bwReceivedLock.wait(1000);
|
||||
}
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
_bwReceived = false;
|
||||
return _bwLimits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only map message handlers that we will use
|
||||
*/
|
||||
|
@ -22,6 +22,13 @@ import net.i2p.data.Hash;
|
||||
*
|
||||
* All calls are blocking and return null on failure.
|
||||
* Timeout is set to 10 seconds in I2PSimpleSession.
|
||||
*
|
||||
* As of 0.8.3, standard I2PSessions support lookups,
|
||||
* including multiple lookups in parallel, and overriding
|
||||
* the default timeout.
|
||||
* Using an existing I2PSession is much more efficient and
|
||||
* flexible than using this class.
|
||||
*
|
||||
*/
|
||||
class LookupDest {
|
||||
|
||||
|
@ -22,13 +22,14 @@ import net.i2p.util.RandomSource;
|
||||
* See CryptixAESEngine for the real thing.
|
||||
*/
|
||||
public class AESEngine {
|
||||
private Log _log;
|
||||
private I2PAppContext _context;
|
||||
protected final Log _log;
|
||||
protected final I2PAppContext _context;
|
||||
|
||||
public AESEngine(I2PAppContext ctx) {
|
||||
_context = ctx;
|
||||
_log = _context.logManager().getLog(AESEngine.class);
|
||||
if (getClass() == AESEngine.class)
|
||||
_log.warn("Warning: AES is disabled");
|
||||
_log = _context.logManager().getLog(getClass());
|
||||
if (getClass().equals(AESEngine.class))
|
||||
_log.logAlways(Log.WARN, "AES is disabled");
|
||||
}
|
||||
|
||||
/** Encrypt the payload with the session key
|
||||
@ -44,7 +45,10 @@ public class AESEngine {
|
||||
encrypt(payload, payloadIndex, out, outIndex, sessionKey, iv, 0, length);
|
||||
}
|
||||
|
||||
/** Encrypt the payload with the session key
|
||||
/**
|
||||
* Encrypt the payload with the session key.
|
||||
* This just copies payload to out, see extension for the real thing.
|
||||
*
|
||||
* @param payload data to be encrypted
|
||||
* @param payloadIndex index into the payload to start encrypting
|
||||
* @param out where to store the result
|
||||
@ -55,7 +59,7 @@ public class AESEngine {
|
||||
*/
|
||||
public void encrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int ivOffset, int length) {
|
||||
System.arraycopy(payload, payloadIndex, out, outIndex, length);
|
||||
_log.warn("Warning: AES is disabled");
|
||||
_log.logAlways(Log.WARN, "AES is disabled");
|
||||
}
|
||||
|
||||
public byte[] safeEncrypt(byte payload[], SessionKey sessionKey, byte iv[], int paddedSize) {
|
||||
@ -118,7 +122,6 @@ public class AESEngine {
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/** Decrypt the data with the session key
|
||||
* @param payload data to be decrypted
|
||||
* @param payloadIndex index into the payload to start decrypting
|
||||
@ -132,7 +135,10 @@ public class AESEngine {
|
||||
decrypt(payload, payloadIndex, out, outIndex, sessionKey, iv, 0, length);
|
||||
}
|
||||
|
||||
/** Decrypt the data with the session key
|
||||
/**
|
||||
* Decrypt the data with the session key.
|
||||
* This just copies payload to out, see extension for the real thing.
|
||||
*
|
||||
* @param payload data to be decrypted
|
||||
* @param payloadIndex index into the payload to start decrypting
|
||||
* @param out where to store the cleartext
|
||||
@ -143,18 +149,20 @@ public class AESEngine {
|
||||
*/
|
||||
public void decrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int ivOffset, int length) {
|
||||
System.arraycopy(payload, payloadIndex, out, outIndex, length);
|
||||
_log.warn("Warning: AES is disabled");
|
||||
_log.logAlways(Log.WARN, "AES is disabled");
|
||||
}
|
||||
|
||||
/**
|
||||
* Just copies payload to out
|
||||
* This just copies payload to out, see extension for the real thing.
|
||||
* @param sessionKey unused
|
||||
*/
|
||||
public void encryptBlock(byte payload[], int inIndex, SessionKey sessionKey, byte out[], int outIndex) {
|
||||
System.arraycopy(payload, inIndex, out, outIndex, out.length - outIndex);
|
||||
}
|
||||
|
||||
/** decrypt the data with the session key provided
|
||||
/**
|
||||
* This just copies payload to rv, see extension for the real thing.
|
||||
*
|
||||
* @param payload encrypted data
|
||||
* @param sessionKey private session key
|
||||
*/
|
||||
|
@ -27,18 +27,16 @@ import net.i2p.util.Log;
|
||||
* @author jrandom, thecrypto
|
||||
*/
|
||||
public class CryptixAESEngine extends AESEngine {
|
||||
private Log _log;
|
||||
private final static CryptixRijndael_Algorithm _algo = new CryptixRijndael_Algorithm();
|
||||
private final static boolean USE_FAKE_CRYPTO = false;
|
||||
private final static byte FAKE_KEY = 0x2A;
|
||||
private CryptixAESKeyCache _cache;
|
||||
// keys are now cached in the SessionKey objects
|
||||
//private CryptixAESKeyCache _cache;
|
||||
|
||||
private static final ByteCache _prevCache = ByteCache.getInstance(16, 16);
|
||||
|
||||
public CryptixAESEngine(I2PAppContext context) {
|
||||
super(context);
|
||||
_log = context.logManager().getLog(CryptixAESEngine.class);
|
||||
_cache = new CryptixAESKeyCache();
|
||||
//_cache = new CryptixAESKeyCache();
|
||||
}
|
||||
|
||||
/** @param length must be a multiple of 16 */
|
||||
|
@ -8,6 +8,8 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
* data referenced in it is needed (which often is only one or two lines
|
||||
* of code)
|
||||
*
|
||||
* Unused as a class, as the keys are cached in the SessionKey objects,
|
||||
* but the static methods are used in FortunaStandalone.
|
||||
*/
|
||||
public final class CryptixAESKeyCache {
|
||||
private final LinkedBlockingQueue<KeyCacheEntry> _availableKeys;
|
||||
@ -20,6 +22,9 @@ public final class CryptixAESKeyCache {
|
||||
|
||||
private static final int MAX_KEYS = 64;
|
||||
|
||||
/*
|
||||
* @deprecated unused, keys are now cached in the SessionKey objects
|
||||
*/
|
||||
public CryptixAESKeyCache() {
|
||||
_availableKeys = new LinkedBlockingQueue(MAX_KEYS);
|
||||
}
|
||||
@ -27,6 +32,7 @@ public final class CryptixAESKeyCache {
|
||||
/**
|
||||
* Get the next available structure, either from the cache or a brand new one
|
||||
*
|
||||
* @deprecated unused, keys are now cached in the SessionKey objects
|
||||
*/
|
||||
public final KeyCacheEntry acquireKey() {
|
||||
KeyCacheEntry rv = _availableKeys.poll();
|
||||
@ -38,6 +44,7 @@ public final class CryptixAESKeyCache {
|
||||
/**
|
||||
* Put this structure back onto the available cache for reuse
|
||||
*
|
||||
* @deprecated unused, keys are now cached in the SessionKey objects
|
||||
*/
|
||||
public final void releaseKey(KeyCacheEntry key) {
|
||||
_availableKeys.offer(key);
|
||||
|
@ -13,8 +13,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
@ -48,14 +47,15 @@ import net.i2p.util.RandomSource;
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DHSessionKeyBuilder {
|
||||
private static I2PAppContext _context = I2PAppContext.getGlobalContext();
|
||||
private final static Log _log = new Log(DHSessionKeyBuilder.class);
|
||||
private static int MIN_NUM_BUILDERS = -1;
|
||||
private static int MAX_NUM_BUILDERS = -1;
|
||||
private static int CALC_DELAY = -1;
|
||||
/* FIXME this should be final if you syncronize FIXME */
|
||||
private static volatile List _builders = new ArrayList(50);
|
||||
private static Thread _precalcThread = null;
|
||||
private static final I2PAppContext _context = I2PAppContext.getGlobalContext();
|
||||
private static final Log _log;
|
||||
private static final int MIN_NUM_BUILDERS;
|
||||
private static final int MAX_NUM_BUILDERS;
|
||||
private static final int CALC_DELAY;
|
||||
private static final LinkedBlockingQueue<DHSessionKeyBuilder> _builders;
|
||||
private static final Thread _precalcThread;
|
||||
|
||||
// the data of importance
|
||||
private BigInteger _myPrivateValue;
|
||||
private BigInteger _myPublicValue;
|
||||
private BigInteger _peerValue;
|
||||
@ -65,17 +65,31 @@ public class DHSessionKeyBuilder {
|
||||
public final static String PROP_DH_PRECALC_MIN = "crypto.dh.precalc.min";
|
||||
public final static String PROP_DH_PRECALC_MAX = "crypto.dh.precalc.max";
|
||||
public final static String PROP_DH_PRECALC_DELAY = "crypto.dh.precalc.delay";
|
||||
public final static int DEFAULT_DH_PRECALC_MIN = 5;
|
||||
public final static int DEFAULT_DH_PRECALC_MAX = 50;
|
||||
public final static int DEFAULT_DH_PRECALC_DELAY = 10000;
|
||||
public final static int DEFAULT_DH_PRECALC_MIN = 15;
|
||||
public final static int DEFAULT_DH_PRECALC_MAX = 40;
|
||||
public final static int DEFAULT_DH_PRECALC_DELAY = 200;
|
||||
|
||||
/** check every 30 seconds whether we have less than the minimum */
|
||||
private static long _checkDelay = 30 * 1000;
|
||||
|
||||
static {
|
||||
I2PAppContext ctx = _context;
|
||||
ctx.statManager().createRateStat("crypto.dhGeneratePublicTime", "How long it takes to create x and X", "Encryption", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
|
||||
ctx.statManager().createRateStat("crypto.dhCalculateSessionTime", "How long it takes to create the session key", "Encryption", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
|
||||
MIN_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MIN, DEFAULT_DH_PRECALC_MIN);
|
||||
MAX_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MAX, DEFAULT_DH_PRECALC_MAX);
|
||||
_log = ctx.logManager().getLog(DHSessionKeyBuilder.class);
|
||||
ctx.statManager().createRateStat("crypto.dhGeneratePublicTime", "How long it takes to create x and X", "Encryption", new long[] { 60*60*1000 });
|
||||
ctx.statManager().createRateStat("crypto.dhCalculateSessionTime", "How long it takes to create the session key", "Encryption", new long[] { 60*60*1000 });
|
||||
ctx.statManager().createRateStat("crypto.DHUsed", "Need a DH from the queue", "Encryption", new long[] { 60*60*1000 });
|
||||
ctx.statManager().createRateStat("crypto.DHEmpty", "DH queue empty", "Encryption", new long[] { 60*60*1000 });
|
||||
|
||||
// add to the defaults for every 128MB of RAM, up to 512MB
|
||||
long maxMemory = Runtime.getRuntime().maxMemory();
|
||||
int factor = Math.min(4, (int) (1 + (maxMemory / (128*1024*1024l))));
|
||||
int defaultMin = DEFAULT_DH_PRECALC_MIN * factor;
|
||||
int defaultMax = DEFAULT_DH_PRECALC_MAX * factor;
|
||||
MIN_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MIN, defaultMin);
|
||||
MAX_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MAX, defaultMax);
|
||||
|
||||
CALC_DELAY = ctx.getProperty(PROP_DH_PRECALC_DELAY, DEFAULT_DH_PRECALC_DELAY);
|
||||
_builders = new LinkedBlockingQueue(MAX_NUM_BUILDERS);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("DH Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: "
|
||||
@ -90,40 +104,33 @@ public class DHSessionKeyBuilder {
|
||||
|
||||
/**
|
||||
* Construct a new DH key builder
|
||||
*
|
||||
* or pulls a prebuilt one from the queue.
|
||||
*/
|
||||
public DHSessionKeyBuilder() {
|
||||
this(false);
|
||||
DHSessionKeyBuilder builder = null;
|
||||
synchronized (_builders) {
|
||||
if (!_builders.isEmpty()) {
|
||||
builder = (DHSessionKeyBuilder) _builders.remove(0);
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Removing a builder. # left = " + _builders.size());
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN)) _log.warn("NO MORE BUILDERS! creating one now");
|
||||
}
|
||||
}
|
||||
_context.statManager().addRateData("crypto.DHUsed", 1, 0);
|
||||
DHSessionKeyBuilder builder = _builders.poll();
|
||||
if (builder != null) {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Removing a builder. # left = " + _builders.size());
|
||||
_myPrivateValue = builder._myPrivateValue;
|
||||
_myPublicValue = builder._myPublicValue;
|
||||
_peerValue = builder._peerValue;
|
||||
_sessionKey = builder._sessionKey;
|
||||
// these two are still null after precalc
|
||||
//_peerValue = builder._peerValue;
|
||||
//_sessionKey = builder._sessionKey;
|
||||
_extraExchangedBytes = builder._extraExchangedBytes;
|
||||
} else {
|
||||
_myPrivateValue = null;
|
||||
_myPublicValue = null;
|
||||
_peerValue = null;
|
||||
_sessionKey = null;
|
||||
if (_log.shouldLog(Log.INFO)) _log.info("No more builders, creating one now");
|
||||
_context.statManager().addRateData("crypto.DHEmpty", 1, 0);
|
||||
// sets _myPrivateValue as a side effect
|
||||
_myPublicValue = generateMyValue();
|
||||
_extraExchangedBytes = new ByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
public DHSessionKeyBuilder(boolean usePool) {
|
||||
_myPrivateValue = null;
|
||||
_myPublicValue = null;
|
||||
_peerValue = null;
|
||||
_sessionKey = null;
|
||||
/**
|
||||
* Only for internal use
|
||||
* @parameter usePool unused, just to make it different from other constructor
|
||||
*/
|
||||
private DHSessionKeyBuilder(boolean usePool) {
|
||||
_extraExchangedBytes = new ByteArray();
|
||||
}
|
||||
|
||||
@ -189,18 +196,12 @@ public class DHSessionKeyBuilder {
|
||||
}
|
||||
|
||||
private static final int getSize() {
|
||||
synchronized (_builders) {
|
||||
return _builders.size();
|
||||
}
|
||||
}
|
||||
|
||||
private static final int addBuilder(DHSessionKeyBuilder builder) {
|
||||
int sz = 0;
|
||||
synchronized (_builders) {
|
||||
_builders.add(builder);
|
||||
sz = _builders.size();
|
||||
}
|
||||
return sz;
|
||||
/** @return true if successful, false if full */
|
||||
private static final boolean addBuilder(DHSessionKeyBuilder builder) {
|
||||
return _builders.offer(builder);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -210,7 +211,7 @@ public class DHSessionKeyBuilder {
|
||||
*/
|
||||
public BigInteger generateMyValue() {
|
||||
long start = System.currentTimeMillis();
|
||||
_myPrivateValue = new NativeBigInteger(KeyGenerator.PUBKEY_EXPONENT_SIZE, RandomSource.getInstance());
|
||||
_myPrivateValue = new NativeBigInteger(KeyGenerator.PUBKEY_EXPONENT_SIZE, _context.random());
|
||||
BigInteger myValue = CryptoConstants.elgg.modPow(_myPrivateValue, CryptoConstants.elgp);
|
||||
long end = System.currentTimeMillis();
|
||||
long diff = end - start;
|
||||
@ -314,6 +315,7 @@ public class DHSessionKeyBuilder {
|
||||
* If there aren't enough bytes (with all of them being consumed by the 32 byte key),
|
||||
* the SHA256 of the key itself is used.
|
||||
*
|
||||
* @return non-null (but rv.getData() may be null)
|
||||
*/
|
||||
public ByteArray getExtraBytes() {
|
||||
return _extraExchangedBytes;
|
||||
@ -406,6 +408,7 @@ public class DHSessionKeyBuilder {
|
||||
}
|
||||
*/
|
||||
|
||||
/******
|
||||
public static void main(String args[]) {
|
||||
//if (true) { testValidation(); return; }
|
||||
|
||||
@ -419,7 +422,7 @@ public class DHSessionKeyBuilder {
|
||||
long negTime = 0;
|
||||
try {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
long startNeg = Clock.getInstance().now();
|
||||
long startNeg = System.currentTimeMillis();
|
||||
DHSessionKeyBuilder builder1 = new DHSessionKeyBuilder();
|
||||
DHSessionKeyBuilder builder2 = new DHSessionKeyBuilder();
|
||||
BigInteger pub1 = builder1.getMyPublicValue();
|
||||
@ -428,7 +431,7 @@ public class DHSessionKeyBuilder {
|
||||
builder1.setPeerPublicValue(pub2);
|
||||
SessionKey key1 = builder1.getSessionKey();
|
||||
SessionKey key2 = builder2.getSessionKey();
|
||||
long endNeg = Clock.getInstance().now();
|
||||
long endNeg = System.currentTimeMillis();
|
||||
negTime += endNeg - startNeg;
|
||||
|
||||
if (!key1.equals(key2))
|
||||
@ -458,10 +461,11 @@ public class DHSessionKeyBuilder {
|
||||
} catch (InterruptedException ie) { // nop
|
||||
}
|
||||
}
|
||||
******/
|
||||
|
||||
private static class DHSessionKeyBuilderPrecalcRunner implements Runnable {
|
||||
private int _minSize;
|
||||
private int _maxSize;
|
||||
private final int _minSize;
|
||||
private final int _maxSize;
|
||||
|
||||
private DHSessionKeyBuilderPrecalcRunner(int minSize, int maxSize) {
|
||||
_minSize = minSize;
|
||||
@ -472,22 +476,28 @@ public class DHSessionKeyBuilder {
|
||||
while (true) {
|
||||
|
||||
int curSize = 0;
|
||||
long start = Clock.getInstance().now();
|
||||
long start = System.currentTimeMillis();
|
||||
int startSize = getSize();
|
||||
// Adjust delay
|
||||
if (startSize <= (_minSize * 2 / 3) && _checkDelay > 1000)
|
||||
_checkDelay -= 1000;
|
||||
else if (startSize > (_minSize * 3 / 2) && _checkDelay < 60*1000)
|
||||
_checkDelay += 1000;
|
||||
curSize = startSize;
|
||||
while (curSize < _minSize) {
|
||||
while (curSize < _maxSize) {
|
||||
if (curSize < _minSize) {
|
||||
for (int i = curSize; i < _maxSize; i++) {
|
||||
long curStart = System.currentTimeMillis();
|
||||
curSize = addBuilder(precalc(curSize));
|
||||
if (!addBuilder(precalc()))
|
||||
break;
|
||||
long curCalc = System.currentTimeMillis() - curStart;
|
||||
// for some relief...
|
||||
try {
|
||||
Thread.sleep(CALC_DELAY + curCalc * 10);
|
||||
Thread.sleep(CALC_DELAY + (curCalc * 3));
|
||||
} catch (InterruptedException ie) { // nop
|
||||
}
|
||||
}
|
||||
}
|
||||
long end = Clock.getInstance().now();
|
||||
long end = System.currentTimeMillis();
|
||||
int numCalc = curSize - startSize;
|
||||
if (numCalc > 0) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@ -496,16 +506,15 @@ public class DHSessionKeyBuilder {
|
||||
+ (CALC_DELAY * numCalc) + "ms relief). now sleeping");
|
||||
}
|
||||
try {
|
||||
Thread.sleep(30 * 1000);
|
||||
Thread.sleep(_checkDelay);
|
||||
} catch (InterruptedException ie) { // nop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DHSessionKeyBuilder precalc(int i) {
|
||||
private static DHSessionKeyBuilder precalc() {
|
||||
DHSessionKeyBuilder builder = new DHSessionKeyBuilder(false);
|
||||
builder.getMyPublicValue();
|
||||
//_log.debug("Precalc " + i + " complete");
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
@ -29,17 +29,17 @@ import net.i2p.util.Log;
|
||||
/**
|
||||
* Handles the actual ElGamal+AES encryption and decryption scenarios using the
|
||||
* supplied keys and data.
|
||||
*
|
||||
* No, this does not extend AESEngine or CryptixAESEngine.
|
||||
*/
|
||||
public class ElGamalAESEngine {
|
||||
private final static Log _log = new Log(ElGamalAESEngine.class);
|
||||
private final Log _log;
|
||||
private final static int MIN_ENCRYPTED_SIZE = 80; // smallest possible resulting size
|
||||
private I2PAppContext _context;
|
||||
|
||||
private ElGamalAESEngine() { // nop
|
||||
}
|
||||
private final I2PAppContext _context;
|
||||
|
||||
public ElGamalAESEngine(I2PAppContext ctx) {
|
||||
_context = ctx;
|
||||
_log = _context.logManager().getLog(ElGamalAESEngine.class);
|
||||
|
||||
_context.statManager().createFrequencyStat("crypto.elGamalAES.encryptNewSession",
|
||||
"how frequently we encrypt to a new ElGamal/AES+SessionTag session?",
|
||||
@ -627,6 +627,7 @@ public class ElGamalAESEngine {
|
||||
return numPadding;
|
||||
}
|
||||
|
||||
/****
|
||||
public static void main(String args[]) {
|
||||
I2PAppContext ctx = new I2PAppContext();
|
||||
ElGamalAESEngine e = new ElGamalAESEngine(ctx);
|
||||
@ -656,4 +657,5 @@ public class ElGamalAESEngine {
|
||||
}
|
||||
}
|
||||
}
|
||||
****/
|
||||
}
|
||||
|
@ -14,8 +14,7 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.DataStructureImpl;
|
||||
import net.i2p.data.SimpleDataStructure;
|
||||
|
||||
/**
|
||||
* Because DSAEngine was abusing Hash for 20-byte hashes
|
||||
@ -23,44 +22,31 @@ import net.i2p.data.DataStructureImpl;
|
||||
* @since 0.8.1
|
||||
* @author zzz
|
||||
*/
|
||||
public class SHA1Hash extends DataStructureImpl {
|
||||
private byte[] _data;
|
||||
public class SHA1Hash extends SimpleDataStructure {
|
||||
private int _cachedHashCode;
|
||||
|
||||
public final static int HASH_LENGTH = SHA1.HASH_LENGTH;
|
||||
|
||||
/** @throws IllegalArgumentException if data is not 20 bytes (null is ok) */
|
||||
public SHA1Hash(byte data[]) {
|
||||
setData(data);
|
||||
super(data);
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return _data;
|
||||
public int length() {
|
||||
return HASH_LENGTH;
|
||||
}
|
||||
|
||||
/** @throws IllegalArgumentException if data is not 20 bytes (null is ok) */
|
||||
@Override
|
||||
public void setData(byte[] data) {
|
||||
// FIXME DSAEngine uses a SHA-1 "Hash" as parameters and return values!
|
||||
if (data != null && data.length != HASH_LENGTH)
|
||||
throw new IllegalArgumentException("Hash must be 20 bytes");
|
||||
_data = data;
|
||||
_cachedHashCode = calcHashCode();
|
||||
}
|
||||
|
||||
/** @throws IOException always */
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
throw new IOException("unimplemented");
|
||||
}
|
||||
|
||||
/** @throws IOException always */
|
||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||
throw new IOException("unimplemented");
|
||||
super.setData(data);
|
||||
_cachedHashCode = super.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if ((obj == null) || !(obj instanceof SHA1Hash)) return false;
|
||||
return DataHelper.eq(_data, ((SHA1Hash) obj)._data);
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
super.readBytes(in);
|
||||
_cachedHashCode = super.hashCode();
|
||||
}
|
||||
|
||||
/** a Hash is a hash, so just use the first 4 bytes for speed */
|
||||
@ -68,14 +54,4 @@ public class SHA1Hash extends DataStructureImpl {
|
||||
public int hashCode() {
|
||||
return _cachedHashCode;
|
||||
}
|
||||
|
||||
/** a Hash is a hash, so just use the first 4 bytes for speed */
|
||||
private int calcHashCode() {
|
||||
int rv = 0;
|
||||
if (_data != null) {
|
||||
for (int i = 0; i < 4; i++)
|
||||
rv ^= (_data[i] << (i*8));
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ class YKGenerator {
|
||||
private static final int MIN_NUM_BUILDERS;
|
||||
private static final int MAX_NUM_BUILDERS;
|
||||
private static final int CALC_DELAY;
|
||||
private static final LinkedBlockingQueue<BigInteger[]> _values = new LinkedBlockingQueue(50); // list of BigInteger[] values (y and k)
|
||||
private static final LinkedBlockingQueue<BigInteger[]> _values;
|
||||
private static final Thread _precalcThread;
|
||||
private static final I2PAppContext ctx;
|
||||
|
||||
@ -53,13 +53,21 @@ class YKGenerator {
|
||||
public final static int DEFAULT_YK_PRECALC_DELAY = 200;
|
||||
|
||||
/** check every 30 seconds whether we have less than the minimum */
|
||||
private static long CHECK_DELAY = 30 * 1000;
|
||||
private static long _checkDelay = 30 * 1000;
|
||||
|
||||
static {
|
||||
ctx = I2PAppContext.getGlobalContext();
|
||||
MIN_NUM_BUILDERS = ctx.getProperty(PROP_YK_PRECALC_MIN, DEFAULT_YK_PRECALC_MIN);
|
||||
MAX_NUM_BUILDERS = ctx.getProperty(PROP_YK_PRECALC_MAX, DEFAULT_YK_PRECALC_MAX);
|
||||
|
||||
// add to the defaults for every 128MB of RAM, up to 1GB
|
||||
long maxMemory = Runtime.getRuntime().maxMemory();
|
||||
int factor = Math.min(8, (int) (1 + (maxMemory / (128*1024*1024l))));
|
||||
int defaultMin = DEFAULT_YK_PRECALC_MIN * factor;
|
||||
int defaultMax = DEFAULT_YK_PRECALC_MAX * factor;
|
||||
MIN_NUM_BUILDERS = ctx.getProperty(PROP_YK_PRECALC_MIN, defaultMin);
|
||||
MAX_NUM_BUILDERS = ctx.getProperty(PROP_YK_PRECALC_MAX, defaultMax);
|
||||
|
||||
CALC_DELAY = ctx.getProperty(PROP_YK_PRECALC_DELAY, DEFAULT_YK_PRECALC_DELAY);
|
||||
_values = new LinkedBlockingQueue(MAX_NUM_BUILDERS);
|
||||
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("ElGamal YK Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: "
|
||||
@ -137,12 +145,13 @@ class YKGenerator {
|
||||
long endNeg = Clock.getInstance().now();
|
||||
negTime += endNeg - startNeg;
|
||||
}
|
||||
// 173ms each on a 2008 netbook
|
||||
System.out.println("YK fetch time for 5 runs: " + negTime + " @ " + negTime / 5l + "ms each");
|
||||
}
|
||||
|
||||
private static class YKPrecalcRunner implements Runnable {
|
||||
private int _minSize;
|
||||
private int _maxSize;
|
||||
private final int _minSize;
|
||||
private final int _maxSize;
|
||||
|
||||
private YKPrecalcRunner(int minSize, int maxSize) {
|
||||
_minSize = minSize;
|
||||
@ -155,10 +164,10 @@ class YKGenerator {
|
||||
//long start = Clock.getInstance().now();
|
||||
int startSize = getSize();
|
||||
// Adjust delay
|
||||
if (startSize <= (_minSize / 2) && CHECK_DELAY > 1000)
|
||||
CHECK_DELAY -= 1000;
|
||||
else if (startSize > (_minSize * 2) && CHECK_DELAY < 60000)
|
||||
CHECK_DELAY += 1000;
|
||||
if (startSize <= (_minSize * 2 / 3) && _checkDelay > 1000)
|
||||
_checkDelay -= 1000;
|
||||
else if (startSize > (_minSize * 3 / 2) && _checkDelay < 60*1000)
|
||||
_checkDelay += 1000;
|
||||
curSize = startSize;
|
||||
if (curSize < _minSize) {
|
||||
for (int i = curSize; i < _maxSize; i++) {
|
||||
@ -183,7 +192,7 @@ class YKGenerator {
|
||||
// + (CALC_DELAY * numCalc) + "ms relief). now sleeping");
|
||||
//}
|
||||
try {
|
||||
Thread.sleep(CHECK_DELAY);
|
||||
Thread.sleep(_checkDelay);
|
||||
} catch (InterruptedException ie) { // nop
|
||||
}
|
||||
}
|
||||
|
@ -26,8 +26,10 @@ import java.io.OutputStream;
|
||||
* @author jrandom
|
||||
*/
|
||||
public class Certificate extends DataStructureImpl {
|
||||
private int _type;
|
||||
private byte[] _payload;
|
||||
public final static Certificate NULL_CERT = new NullCert();
|
||||
|
||||
protected int _type;
|
||||
protected byte[] _payload;
|
||||
|
||||
/** Specifies a null certificate type with no payload */
|
||||
public final static int CERTIFICATE_TYPE_NULL = 0;
|
||||
@ -41,6 +43,25 @@ public class Certificate extends DataStructureImpl {
|
||||
/** Contains multiple certs */
|
||||
public final static int CERTIFICATE_TYPE_MULTIPLE = 4;
|
||||
|
||||
/**
|
||||
* If null cert, return immutable static instance, else create new
|
||||
* @since 0.8.3
|
||||
*/
|
||||
public static Certificate create(InputStream in) throws DataFormatException, IOException {
|
||||
int type = (int) DataHelper.readLong(in, 1);
|
||||
int length = (int) DataHelper.readLong(in, 2);
|
||||
if (type == 0 && length == 0)
|
||||
return NULL_CERT;
|
||||
// from here down roughly the same as readBytes() below
|
||||
if (length == 0)
|
||||
return new Certificate(type, null);
|
||||
byte[] payload = new byte[length];
|
||||
int read = DataHelper.read(in, payload);
|
||||
if (read != length)
|
||||
throw new DataFormatException("Not enough bytes for the payload (read: " + read + " length: " + length + ')');
|
||||
return new Certificate(type, payload);
|
||||
}
|
||||
|
||||
public Certificate() {
|
||||
}
|
||||
|
||||
@ -90,8 +111,10 @@ public class Certificate extends DataStructureImpl {
|
||||
DataHelper.writeLong(out, 2, 0L);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return the written length (NOT the new offset)
|
||||
*/
|
||||
public int writeBytes(byte target[], int offset) {
|
||||
int cur = offset;
|
||||
DataHelper.toLong(target, cur, 1, _type);
|
||||
@ -140,10 +163,12 @@ public class Certificate extends DataStructureImpl {
|
||||
Certificate cert = (Certificate) object;
|
||||
return _type == cert.getCertificateType() && DataHelper.eq(_payload, cert.getPayload());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return _type + DataHelper.hashCode(_payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(64);
|
||||
@ -177,4 +202,67 @@ public class Certificate extends DataStructureImpl {
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* An immutable null certificate.
|
||||
* @since 0.8.3
|
||||
*/
|
||||
private static final class NullCert extends Certificate {
|
||||
private static final int NULL_LENGTH = 1 + 2;
|
||||
private static final byte[] NULL_DATA = new byte[NULL_LENGTH];
|
||||
|
||||
public NullCert() {
|
||||
// zero already
|
||||
//_type = CERTIFICATE_TYPE_NULL;
|
||||
}
|
||||
|
||||
/** @throws RuntimeException always */
|
||||
@Override
|
||||
public void setCertificateType(int type) {
|
||||
throw new RuntimeException("Data already set");
|
||||
}
|
||||
|
||||
/** @throws RuntimeException always */
|
||||
@Override
|
||||
public void setPayload(byte[] payload) {
|
||||
throw new RuntimeException("Data already set");
|
||||
}
|
||||
|
||||
/** @throws RuntimeException always */
|
||||
@Override
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
throw new RuntimeException("Data already set");
|
||||
}
|
||||
|
||||
/** Overridden for efficiency */
|
||||
@Override
|
||||
public void writeBytes(OutputStream out) throws IOException {
|
||||
out.write(NULL_DATA);
|
||||
}
|
||||
|
||||
/** Overridden for efficiency */
|
||||
@Override
|
||||
public int writeBytes(byte target[], int offset) {
|
||||
System.arraycopy(NULL_DATA, 0, target, offset, NULL_LENGTH);
|
||||
return NULL_LENGTH;
|
||||
}
|
||||
|
||||
/** @throws RuntimeException always */
|
||||
@Override
|
||||
public int readBytes(byte source[], int offset) throws DataFormatException {
|
||||
throw new RuntimeException("Data already set");
|
||||
}
|
||||
|
||||
/** Overridden for efficiency */
|
||||
@Override
|
||||
public int size() {
|
||||
return NULL_LENGTH;
|
||||
}
|
||||
|
||||
/** Overridden for efficiency */
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 99999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -914,9 +914,20 @@ public class DataHelper {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is different than InputStream.read(target), in that it
|
||||
* does repeated reads until the full data is received.
|
||||
*/
|
||||
public static int read(InputStream in, byte target[]) throws IOException {
|
||||
return read(in, target, 0, target.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is different than InputStream.read(target, offset, length), in that it
|
||||
* returns the new offset (== old offset + bytes read).
|
||||
* It also does repeated reads until the full data is received.
|
||||
* @return the new offset (== old offset + bytes read)
|
||||
*/
|
||||
public static int read(InputStream in, byte target[], int offset, int length) throws IOException {
|
||||
int cur = offset;
|
||||
while (cur < length) {
|
||||
|
@ -28,7 +28,10 @@ public class Destination extends KeysAndCert {
|
||||
fromBase64(s);
|
||||
}
|
||||
|
||||
/** deprecated, used only by Packet.java in streaming */
|
||||
/**
|
||||
* deprecated, used only by Packet.java in streaming
|
||||
* @return the written length (NOT the new offset)
|
||||
*/
|
||||
public int writeBytes(byte target[], int offset) {
|
||||
int cur = offset;
|
||||
System.arraycopy(_publicKey.getData(), 0, target, cur, PublicKey.KEYSIZE_BYTES);
|
||||
|
@ -65,8 +65,9 @@ public class KeysAndCert extends DataStructureImpl {
|
||||
_publicKey.readBytes(in);
|
||||
_signingKey = new SigningPublicKey();
|
||||
_signingKey.readBytes(in);
|
||||
_certificate = new Certificate();
|
||||
_certificate.readBytes(in);
|
||||
//_certificate = new Certificate();
|
||||
//_certificate.readBytes(in);
|
||||
_certificate = Certificate.create(in);
|
||||
__calculatedHash = null;
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,10 @@ public class Payload extends DataStructureImpl {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("wrote payload: " + _encryptedData.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the written length (NOT the new offset)
|
||||
*/
|
||||
public int writeBytes(byte target[], int offset) {
|
||||
if (_encryptedData == null) throw new IllegalStateException("Not yet encrypted. Please set the encrypted data");
|
||||
DataHelper.toLong(target, offset, 4, _encryptedData.length);
|
||||
|
@ -20,6 +20,12 @@ import net.i2p.crypto.SHA256Generator;
|
||||
*
|
||||
* Implemented in 0.8.2 and retrofitted over several of the classes in this package.
|
||||
*
|
||||
* As of 0.8.3, SDS objects may be cached. An SDS may be instantiated with null data,
|
||||
* and setData(null) is also OK. However,
|
||||
* once non-null data is set, the data reference is immutable;
|
||||
* subsequent attempts to set the data via setData(), readBytes(),
|
||||
* or fromBase64() will throw a RuntimeException.
|
||||
*
|
||||
* @since 0.8.2
|
||||
* @author zzz
|
||||
*/
|
||||
@ -57,14 +63,24 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
|
||||
* Sets the data.
|
||||
* @param data of correct length, or null
|
||||
* @throws IllegalArgumentException if data is not the legal number of bytes (but null is ok)
|
||||
* @throws RuntimeException if data already set.
|
||||
*/
|
||||
public void setData(byte[] data) {
|
||||
if (_data != null)
|
||||
throw new RuntimeException("Data already set");
|
||||
if (data != null && data.length != _length)
|
||||
throw new IllegalArgumentException("Bad data length");
|
||||
_data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data.
|
||||
* @param data of correct length, or null
|
||||
* @throws RuntimeException if data already set.
|
||||
*/
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
if (_data != null)
|
||||
throw new RuntimeException("Data already set");
|
||||
_data = new byte[_length];
|
||||
int read = read(in, _data);
|
||||
if (read != _length) throw new DataFormatException("Not enough bytes to read the data");
|
||||
@ -85,6 +101,7 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
|
||||
/**
|
||||
* Sets the data.
|
||||
* @throws DataFormatException if decoded data is not the legal number of bytes or on decoding error
|
||||
* @throws RuntimeException if data already set.
|
||||
*/
|
||||
@Override
|
||||
public void fromBase64(String data) throws DataFormatException {
|
||||
@ -162,5 +179,4 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
|
||||
if ((obj == null) || !(obj instanceof SimpleDataStructure)) return false;
|
||||
return DataHelper.eq(_data, ((SimpleDataStructure) obj)._data);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,14 +13,18 @@ import java.io.InputStream;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
|
||||
/**
|
||||
* Response to DestLookupMessage
|
||||
*
|
||||
* Response to DestLookupMessage.
|
||||
* As of 0.8.3, the response may include the hash from the request, indicating
|
||||
* a failure for a specific request.
|
||||
* Payload may be empty (failure), a Hash (failure), or a Destination.
|
||||
*/
|
||||
public class DestReplyMessage extends I2CPMessageImpl {
|
||||
public final static int MESSAGE_TYPE = 35;
|
||||
private Destination _dest;
|
||||
private Hash _hash;
|
||||
|
||||
public DestReplyMessage() {
|
||||
super();
|
||||
@ -30,23 +34,52 @@ public class DestReplyMessage extends I2CPMessageImpl {
|
||||
_dest = d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param h non-null with non-null data
|
||||
* @since 0.8.3
|
||||
*/
|
||||
public DestReplyMessage(Hash h) {
|
||||
_hash = h;
|
||||
}
|
||||
|
||||
public Destination getDestination() {
|
||||
return _dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.8.3
|
||||
*/
|
||||
public Hash getHash() {
|
||||
return _hash;
|
||||
}
|
||||
|
||||
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
|
||||
try {
|
||||
Destination d = new Destination();
|
||||
d.readBytes(in);
|
||||
_dest = d;
|
||||
} catch (DataFormatException dfe) {
|
||||
_dest = null; // null dest allowed
|
||||
if (size == 0) {
|
||||
_dest = null;
|
||||
_hash = null;
|
||||
} else {
|
||||
try {
|
||||
if (size == Hash.HASH_LENGTH) {
|
||||
Hash h = new Hash();
|
||||
h.readBytes(in);
|
||||
_hash = h;
|
||||
} else {
|
||||
Destination d = new Destination();
|
||||
d.readBytes(in);
|
||||
_dest = d;
|
||||
}
|
||||
} catch (DataFormatException dfe) {
|
||||
_dest = null;
|
||||
_hash = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
|
||||
if (_dest == null)
|
||||
if (_dest == null && _hash == null)
|
||||
return new byte[0]; // null response allowed
|
||||
if (_dest == null && _hash != null)
|
||||
return _hash.getData();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(_dest.size());
|
||||
try {
|
||||
_dest.writeBytes(os);
|
||||
@ -65,7 +98,8 @@ public class DestReplyMessage extends I2CPMessageImpl {
|
||||
public boolean equals(Object object) {
|
||||
if ((object != null) && (object instanceof DestReplyMessage)) {
|
||||
DestReplyMessage msg = (DestReplyMessage) object;
|
||||
return DataHelper.eq(getDestination(), msg.getDestination());
|
||||
return DataHelper.eq(getDestination(), msg.getDestination()) &&
|
||||
DataHelper.eq(getHash(), msg.getHash());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -75,6 +109,7 @@ public class DestReplyMessage extends I2CPMessageImpl {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[DestReplyMessage: ");
|
||||
buf.append("\n\tDestination: ").append(_dest);
|
||||
buf.append("\n\tHash: ").append(_hash);
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
|
@ -27,11 +27,11 @@ import net.i2p.util.Log;
|
||||
public class I2CPMessageReader {
|
||||
private final static Log _log = new Log(I2CPMessageReader.class);
|
||||
private InputStream _stream;
|
||||
private I2CPMessageEventListener _listener;
|
||||
private I2CPMessageReaderRunner _reader;
|
||||
private Thread _readerThread;
|
||||
protected I2CPMessageEventListener _listener;
|
||||
protected I2CPMessageReaderRunner _reader;
|
||||
protected Thread _readerThread;
|
||||
|
||||
private static volatile long __readerId = 0;
|
||||
protected static volatile long __readerId = 0;
|
||||
|
||||
public I2CPMessageReader(InputStream stream, I2CPMessageEventListener lsnr) {
|
||||
_stream = stream;
|
||||
@ -42,6 +42,14 @@ public class I2CPMessageReader {
|
||||
_readerThread.setName("I2CP Reader " + (++__readerId));
|
||||
}
|
||||
|
||||
/**
|
||||
* For internal extension only. No stream.
|
||||
* @since 0.8.3
|
||||
*/
|
||||
protected I2CPMessageReader(I2CPMessageEventListener lsnr) {
|
||||
setListener(lsnr);
|
||||
}
|
||||
|
||||
public void setListener(I2CPMessageEventListener lsnr) {
|
||||
_listener = lsnr;
|
||||
}
|
||||
@ -114,9 +122,9 @@ public class I2CPMessageReader {
|
||||
public void disconnected(I2CPMessageReader reader);
|
||||
}
|
||||
|
||||
private class I2CPMessageReaderRunner implements Runnable {
|
||||
private volatile boolean _doRun;
|
||||
private volatile boolean _stayAlive;
|
||||
protected class I2CPMessageReaderRunner implements Runnable {
|
||||
protected volatile boolean _doRun;
|
||||
protected volatile boolean _stayAlive;
|
||||
|
||||
public I2CPMessageReaderRunner() {
|
||||
_doRun = true;
|
||||
@ -175,7 +183,8 @@ public class I2CPMessageReader {
|
||||
cancelRunner();
|
||||
}
|
||||
}
|
||||
if (!_doRun) {
|
||||
// ??? unused
|
||||
if (_stayAlive && !_doRun) {
|
||||
// pause .5 secs when we're paused
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
|
51
core/java/src/net/i2p/internal/I2CPMessageQueue.java
Normal file
51
core/java/src/net/i2p/internal/I2CPMessageQueue.java
Normal file
@ -0,0 +1,51 @@
|
||||
package net.i2p.internal;
|
||||
|
||||
import net.i2p.data.i2cp.I2CPMessage;
|
||||
|
||||
/**
|
||||
* Contains the methods to talk to a router or client via I2CP,
|
||||
* when both are in the same JVM.
|
||||
* This interface contains methods to access two queues,
|
||||
* one for transmission and one for receiving.
|
||||
* The methods are identical to those in java.util.concurrent.BlockingQueue.
|
||||
*
|
||||
* Reading may be done in a thread using the QueuedI2CPMessageReader class.
|
||||
* Non-blocking writing may be done directly with offer().
|
||||
*
|
||||
* @author zzz
|
||||
* @since 0.8.3
|
||||
*/
|
||||
public abstract class I2CPMessageQueue {
|
||||
|
||||
/**
|
||||
* Send a message, nonblocking.
|
||||
* @return success (false if no space available)
|
||||
*/
|
||||
public abstract boolean offer(I2CPMessage msg);
|
||||
|
||||
/**
|
||||
* Receive a message, nonblocking.
|
||||
* Unused for now.
|
||||
* @return message or null if none available
|
||||
*/
|
||||
public abstract I2CPMessage poll();
|
||||
|
||||
/**
|
||||
* Send a message, blocking until space is available.
|
||||
* Unused for now.
|
||||
*/
|
||||
public abstract void put(I2CPMessage msg) throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Receive a message, blocking until one is available.
|
||||
* @return message
|
||||
*/
|
||||
public abstract I2CPMessage take() throws InterruptedException;
|
||||
|
||||
/**
|
||||
* == offer(new PoisonI2CPMessage());
|
||||
*/
|
||||
public void close() {
|
||||
offer(new PoisonI2CPMessage());
|
||||
}
|
||||
}
|
19
core/java/src/net/i2p/internal/InternalClientManager.java
Normal file
19
core/java/src/net/i2p/internal/InternalClientManager.java
Normal file
@ -0,0 +1,19 @@
|
||||
package net.i2p.internal;
|
||||
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.data.i2cp.I2CPMessage;
|
||||
|
||||
/**
|
||||
* A manager for the in-JVM I2CP message interface
|
||||
*
|
||||
* @author zzz
|
||||
* @since 0.8.3
|
||||
*/
|
||||
public interface InternalClientManager {
|
||||
|
||||
/**
|
||||
* Connect to the router, receiving a message queue to talk to the router with.
|
||||
* @throws I2PSessionException if the router isn't ready
|
||||
*/
|
||||
public I2CPMessageQueue connect() throws I2PSessionException;
|
||||
}
|
56
core/java/src/net/i2p/internal/PoisonI2CPMessage.java
Normal file
56
core/java/src/net/i2p/internal/PoisonI2CPMessage.java
Normal file
@ -0,0 +1,56 @@
|
||||
package net.i2p.internal;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.i2p.data.i2cp.I2CPMessageException;
|
||||
import net.i2p.data.i2cp.I2CPMessageImpl;
|
||||
|
||||
/**
|
||||
* For marking end-of-queues in a standard manner.
|
||||
* Don't actually send it.
|
||||
*
|
||||
* @author zzz
|
||||
* @since 0.8.3
|
||||
*/
|
||||
public class PoisonI2CPMessage extends I2CPMessageImpl {
|
||||
public final static int MESSAGE_TYPE = 999999;
|
||||
|
||||
public PoisonI2CPMessage() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated don't do this
|
||||
* @throws I2CPMessageException always
|
||||
*/
|
||||
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException {
|
||||
throw new I2CPMessageException("Don't do this");
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated don't do this
|
||||
* @throws I2CPMessageException always
|
||||
*/
|
||||
protected byte[] doWriteMessage() throws I2CPMessageException {
|
||||
throw new I2CPMessageException("Don't do this");
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return MESSAGE_TYPE;
|
||||
}
|
||||
|
||||
/* FIXME missing hashCode() method FIXME */
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if ((object != null) && (object instanceof PoisonI2CPMessage)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[PoisonMessage]";
|
||||
}
|
||||
}
|
62
core/java/src/net/i2p/internal/QueuedI2CPMessageReader.java
Normal file
62
core/java/src/net/i2p/internal/QueuedI2CPMessageReader.java
Normal file
@ -0,0 +1,62 @@
|
||||
package net.i2p.internal;
|
||||
|
||||
import net.i2p.data.i2cp.I2CPMessage;
|
||||
import net.i2p.data.i2cp.I2CPMessageReader;
|
||||
import net.i2p.util.I2PThread;
|
||||
|
||||
/**
|
||||
* Get messages off an In-JVM queue, zero-copy
|
||||
*
|
||||
* @author zzz
|
||||
* @since 0.8.3
|
||||
*/
|
||||
public class QueuedI2CPMessageReader extends I2CPMessageReader {
|
||||
private final I2CPMessageQueue in;
|
||||
|
||||
public QueuedI2CPMessageReader(I2CPMessageQueue in, I2CPMessageEventListener lsnr) {
|
||||
super(lsnr);
|
||||
this.in = in;
|
||||
_reader = new QueuedI2CPMessageReaderRunner();
|
||||
_readerThread = new I2PThread(_reader, "I2CP Internal Reader " + (++__readerId), true);
|
||||
}
|
||||
|
||||
protected class QueuedI2CPMessageReaderRunner extends I2CPMessageReaderRunner implements Runnable {
|
||||
|
||||
public QueuedI2CPMessageReaderRunner() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelRunner() {
|
||||
super.cancelRunner();
|
||||
_readerThread.interrupt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (_stayAlive) {
|
||||
while (_doRun) {
|
||||
// do read
|
||||
I2CPMessage msg = null;
|
||||
try {
|
||||
msg = in.take();
|
||||
if (msg.getType() == PoisonI2CPMessage.MESSAGE_TYPE)
|
||||
cancelRunner();
|
||||
else
|
||||
_listener.messageReceived(QueuedI2CPMessageReader.this, msg);
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
// ??? unused
|
||||
if (_stayAlive && !_doRun) {
|
||||
// pause .5 secs when we're paused
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException ie) {
|
||||
_listener.disconnected(QueuedI2CPMessageReader.this);
|
||||
cancelRunner();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
core/java/src/net/i2p/internal/package.html
Normal file
7
core/java/src/net/i2p/internal/package.html
Normal file
@ -0,0 +1,7 @@
|
||||
<html><body>
|
||||
<p>
|
||||
Interface and classes for a router and client
|
||||
within the same JVM to directly pass I2CP messages using Queues
|
||||
instead of serialized messages over socket streams.
|
||||
</p>
|
||||
</body></html>
|
@ -206,7 +206,7 @@ public class StatManager {
|
||||
* @return true if the stat should be ignored.
|
||||
*/
|
||||
public boolean ignoreStat(String statName) {
|
||||
if (_context.getBooleanPropertyDefaultTrue(PROP_STAT_FULL))
|
||||
if (_context.getBooleanProperty(PROP_STAT_FULL))
|
||||
return false;
|
||||
String required = _context.getProperty(PROP_STAT_REQUIRED, DEFAULT_STAT_REQUIRED);
|
||||
String req[] = required.split(",");
|
||||
|
126
core/java/src/net/i2p/util/Addresses.java
Normal file
126
core/java/src/net/i2p/util/Addresses.java
Normal file
@ -0,0 +1,126 @@
|
||||
package net.i2p.util;
|
||||
|
||||
/*
|
||||
* public domain
|
||||
*/
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
|
||||
/**
|
||||
* Get the local addresses
|
||||
*
|
||||
* @since 0.8.3 moved to core
|
||||
* @author zzz
|
||||
*/
|
||||
public abstract class Addresses {
|
||||
|
||||
/** @return the first non-local address it finds, or null */
|
||||
public static String getAnyAddress() {
|
||||
SortedSet<String> a = getAddresses();
|
||||
if (!a.isEmpty())
|
||||
return a.first();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a sorted set of all addresses, excluding
|
||||
* IPv6, local, broadcast, multicast, etc.
|
||||
*/
|
||||
public static SortedSet<String> getAddresses() {
|
||||
return getAddresses(false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a sorted set of all addresses, excluding
|
||||
* only link local and multicast
|
||||
* @since 0.8.3
|
||||
*/
|
||||
public static SortedSet<String> getAllAddresses() {
|
||||
return getAddresses(true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a sorted array of all addresses
|
||||
* @param whether to exclude IPV6 and local
|
||||
* @return an array of all addresses
|
||||
* @since 0.8.3
|
||||
*/
|
||||
public static SortedSet<String> getAddresses(boolean includeLocal, boolean includeIPv6) {
|
||||
boolean haveIPv4 = false;
|
||||
boolean haveIPv6 = false;
|
||||
SortedSet<String> rv = new TreeSet();
|
||||
try {
|
||||
InetAddress localhost = InetAddress.getLocalHost();
|
||||
InetAddress[] allMyIps = InetAddress.getAllByName(localhost.getCanonicalHostName());
|
||||
if (allMyIps != null) {
|
||||
for (int i = 0; i < allMyIps.length; i++) {
|
||||
if (allMyIps[i] instanceof Inet4Address)
|
||||
haveIPv4 = true;
|
||||
else
|
||||
haveIPv6 = true;
|
||||
if (shouldInclude(allMyIps[i], includeLocal, includeIPv6))
|
||||
rv.add(allMyIps[i].getHostAddress());
|
||||
}
|
||||
}
|
||||
} catch (UnknownHostException e) {}
|
||||
|
||||
try {
|
||||
for(Enumeration<NetworkInterface> ifcs = NetworkInterface.getNetworkInterfaces(); ifcs.hasMoreElements();) {
|
||||
NetworkInterface ifc = ifcs.nextElement();
|
||||
for(Enumeration<InetAddress> addrs = ifc.getInetAddresses(); addrs.hasMoreElements();) {
|
||||
InetAddress addr = addrs.nextElement();
|
||||
if (addr instanceof Inet4Address)
|
||||
haveIPv4 = true;
|
||||
else
|
||||
haveIPv6 = true;
|
||||
if (shouldInclude(addr, includeLocal, includeIPv6))
|
||||
rv.add(addr.getHostAddress());
|
||||
}
|
||||
}
|
||||
} catch (SocketException e) {}
|
||||
|
||||
if (includeLocal && haveIPv4)
|
||||
rv.add("0.0.0.0");
|
||||
if (includeLocal && includeIPv6 && haveIPv6)
|
||||
rv.add("0:0:0:0:0:0:0:0"); // we could do "::" but all the other ones are probably in long form
|
||||
return rv;
|
||||
}
|
||||
|
||||
private static boolean shouldInclude(InetAddress ia, boolean includeLocal, boolean includeIPv6) {
|
||||
return
|
||||
(!ia.isLinkLocalAddress()) &&
|
||||
(!ia.isMulticastAddress()) &&
|
||||
(includeLocal ||
|
||||
((!ia.isAnyLocalAddress()) &&
|
||||
(!ia.isLoopbackAddress()) &&
|
||||
(!ia.isSiteLocalAddress()))) &&
|
||||
// Hamachi 5/8 allocated to RIPE (30 November 2010)
|
||||
// Removed from TransportImpl.isPubliclyRoutable()
|
||||
// Check moved to here, for now, but will eventually need to
|
||||
// remove it from here also.
|
||||
(includeLocal ||
|
||||
(!ia.getHostAddress().startsWith("5."))) &&
|
||||
(includeIPv6 ||
|
||||
(ia instanceof Inet4Address));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.err.println("External Addresses:");
|
||||
Set<String> a = getAddresses(false, false);
|
||||
for (String s : a)
|
||||
System.err.println(s);
|
||||
System.err.println("All addresses:");
|
||||
a = getAddresses(true, true);
|
||||
for (String s : a)
|
||||
System.err.println(s);
|
||||
}
|
||||
}
|
@ -204,6 +204,7 @@ public class BufferedRandomSource extends RandomSource {
|
||||
return super.nextGaussian();
|
||||
}
|
||||
|
||||
/*****
|
||||
public static void main(String args[]) {
|
||||
for (int i = 0; i < 16; i++)
|
||||
test();
|
||||
@ -232,4 +233,5 @@ public class BufferedRandomSource extends RandomSource {
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
*****/
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
@ -16,13 +18,11 @@ import java.util.jar.JarOutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
// Pack200 import
|
||||
// you must also uncomment the correct line in unpack() below
|
||||
// For gcj, gij, etc., comment both out
|
||||
// Pack200 now loaded dynamically in unpack() below
|
||||
//
|
||||
// For Sun, OpenJDK, IcedTea, etc, use this
|
||||
import java.util.jar.Pack200;
|
||||
|
||||
//import java.util.jar.Pack200;
|
||||
//
|
||||
// For Apache Harmony or if you put its pack200.jar in your library directory use this
|
||||
//import org.apache.harmony.unpack200.Archive;
|
||||
|
||||
@ -231,37 +231,79 @@ public class FileUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* This won't work right if one of the two options in unpack() is commented out.
|
||||
* Public since 0.8.3
|
||||
* @since 0.8.1
|
||||
*/
|
||||
private static boolean isPack200Supported() {
|
||||
public static boolean isPack200Supported() {
|
||||
try {
|
||||
Class.forName("java.util.jar.Pack200", false, ClassLoader.getSystemClassLoader());
|
||||
return true;
|
||||
} catch (Exception e) {}
|
||||
try {
|
||||
Class.forName("org.apache.harmony.pack200.Archive", false, ClassLoader.getSystemClassLoader());
|
||||
Class.forName("org.apache.harmony.unpack200.Archive", false, ClassLoader.getSystemClassLoader());
|
||||
return true;
|
||||
} catch (Exception e) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean _failedOracle;
|
||||
private static boolean _failedApache;
|
||||
|
||||
/**
|
||||
* Unpack using either Oracle or Apache's unpack200 library,
|
||||
* with the classes discovered at runtime so neither is required at compile time.
|
||||
*
|
||||
* Caller must close streams
|
||||
* @throws IOException on unpack error or if neither library is available.
|
||||
* Will not throw ClassNotFoundException.
|
||||
* @throws org.apache.harmony.pack200.Pack200Exception which is not an IOException
|
||||
* @since 0.8.1
|
||||
*/
|
||||
private static void unpack(InputStream in, JarOutputStream out) throws Exception {
|
||||
// For Sun, OpenJDK, IcedTea, etc, use this
|
||||
Pack200.newUnpacker().unpack(in, out);
|
||||
//Pack200.newUnpacker().unpack(in, out);
|
||||
if (!_failedOracle) {
|
||||
try {
|
||||
Class p200 = Class.forName("java.util.jar.Pack200", true, ClassLoader.getSystemClassLoader());
|
||||
Method newUnpacker = p200.getMethod("newUnpacker", (Class[]) null);
|
||||
Object unpacker = newUnpacker.invoke(null,(Object[]) null);
|
||||
Method unpack = unpacker.getClass().getMethod("unpack", new Class[] {InputStream.class, JarOutputStream.class});
|
||||
// throws IOException
|
||||
unpack.invoke(unpacker, new Object[] {in, out});
|
||||
return;
|
||||
} catch (ClassNotFoundException e) {
|
||||
_failedOracle = true;
|
||||
//e.printStackTrace();
|
||||
} catch (NoSuchMethodException e) {
|
||||
_failedOracle = true;
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------
|
||||
// For Apache Harmony or if you put its pack200.jar in your library directory use this
|
||||
//(new Archive(in, out)).unpack();
|
||||
|
||||
if (!_failedApache) {
|
||||
try {
|
||||
Class p200 = Class.forName("org.apache.harmony.unpack200.Archive", true, ClassLoader.getSystemClassLoader());
|
||||
Constructor newUnpacker = p200.getConstructor(new Class[] {InputStream.class, JarOutputStream.class});
|
||||
Object unpacker = newUnpacker.newInstance(new Object[] {in, out});
|
||||
Method unpack = unpacker.getClass().getMethod("unpack", (Class[]) null);
|
||||
// throws IOException or Pack200Exception
|
||||
unpack.invoke(unpacker, (Object[]) null);
|
||||
return;
|
||||
} catch (ClassNotFoundException e) {
|
||||
_failedApache = true;
|
||||
//e.printStackTrace();
|
||||
} catch (NoSuchMethodException e) {
|
||||
_failedApache = true;
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------
|
||||
// For gcj, gij, etc., use this
|
||||
//throw new IOException("Pack200 not supported");
|
||||
throw new IOException("Unpack200 not supported");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -378,12 +420,13 @@ public class FileUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage: FileUtil (delete path | copy source dest)
|
||||
* Usage: FileUtil (delete path | copy source dest | unzip path.zip)
|
||||
*
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
if ( (args == null) || (args.length < 2) ) {
|
||||
testRmdir();
|
||||
System.err.println("Usage: delete path | copy source dest | unzip path.zip");
|
||||
//testRmdir();
|
||||
} else if ("delete".equals(args[0])) {
|
||||
boolean deleted = FileUtil.rmdir(args[1], false);
|
||||
if (!deleted)
|
||||
@ -407,6 +450,7 @@ public class FileUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/*****
|
||||
private static void testRmdir() {
|
||||
File t = new File("rmdirTest/test/subdir/blah");
|
||||
boolean created = t.mkdirs();
|
||||
@ -417,4 +461,5 @@ public class FileUtil {
|
||||
else
|
||||
System.out.println("PASS: rmdirTest deleted");
|
||||
}
|
||||
*****/
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ import net.i2p.crypto.EntropyHarvester;
|
||||
*
|
||||
*/
|
||||
public class FortunaRandomSource extends RandomSource implements EntropyHarvester {
|
||||
private AsyncFortunaStandalone _fortuna;
|
||||
private final AsyncFortunaStandalone _fortuna;
|
||||
private double _nextGaussian;
|
||||
private boolean _haveNextGaussian;
|
||||
|
||||
@ -210,6 +210,7 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste
|
||||
_fortuna.addRandomBytes(data, offset, len);
|
||||
}
|
||||
|
||||
/*****
|
||||
public static void main(String args[]) {
|
||||
try {
|
||||
RandomSource rand = I2PAppContext.getGlobalContext().random();
|
||||
@ -231,4 +232,5 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste
|
||||
System.out.println("Compressed size of 1MB: " + compressed.length);
|
||||
} catch (Exception e) { e.printStackTrace(); }
|
||||
}
|
||||
*****/
|
||||
}
|
||||
|
@ -61,8 +61,8 @@ public class I2PThread extends Thread {
|
||||
_createdBy = new Exception("Created by");
|
||||
}
|
||||
|
||||
private void log(int level, String msg) { log(level, msg, null); }
|
||||
private void log(int level, String msg, Throwable t) {
|
||||
private static void log(int level, String msg) { log(level, msg, null); }
|
||||
private static void log(int level, String msg, Throwable t) {
|
||||
// we cant assume log is created
|
||||
if (_log == null) _log = new Log(I2PThread.class);
|
||||
if (_log.shouldLog(level))
|
||||
@ -72,12 +72,12 @@ public class I2PThread extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
_name = Thread.currentThread().getName();
|
||||
log(Log.DEBUG, "New thread started: " + _name, _createdBy);
|
||||
log(Log.INFO, "New thread started" + (isDaemon() ? " (daemon): " : ": ") + _name, _createdBy);
|
||||
try {
|
||||
super.run();
|
||||
} catch (Throwable t) {
|
||||
try {
|
||||
log(Log.CRIT, "Killing thread " + getName(), t);
|
||||
log(Log.CRIT, "Thread terminated unexpectedly: " + getName(), t);
|
||||
} catch (Throwable woof) {
|
||||
System.err.println("Died within the OOM itself");
|
||||
t.printStackTrace();
|
||||
@ -85,12 +85,12 @@ public class I2PThread extends Thread {
|
||||
if (t instanceof OutOfMemoryError)
|
||||
fireOOM((OutOfMemoryError)t);
|
||||
}
|
||||
log(Log.DEBUG, "Thread finished gracefully: " + _name);
|
||||
log(Log.INFO, "Thread finished normally: " + _name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
log(Log.DEBUG, "Thread finalized: " + _name);
|
||||
//log(Log.DEBUG, "Thread finalized: " + _name);
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
|
@ -196,6 +196,7 @@ public class PooledRandomSource extends RandomSource {
|
||||
return prng.harvester();
|
||||
}
|
||||
|
||||
/*****
|
||||
public static void main(String args[]) {
|
||||
//PooledRandomSource prng = new PooledRandomSource(I2PAppContext.getGlobalContext());
|
||||
long start = System.currentTimeMillis();
|
||||
@ -214,4 +215,5 @@ public class PooledRandomSource extends RandomSource {
|
||||
System.out.println("Written to random.file: create took " + (created-start) + ", generate took " + (done-created));
|
||||
prng.saveSeed();
|
||||
}
|
||||
*****/
|
||||
}
|
||||
|
@ -25,18 +25,23 @@ import net.i2p.data.Base64;
|
||||
* @author jrandom
|
||||
*/
|
||||
public class RandomSource extends SecureRandom implements EntropyHarvester {
|
||||
private Log _log;
|
||||
private EntropyHarvester _entropyHarvester;
|
||||
protected I2PAppContext _context;
|
||||
private final EntropyHarvester _entropyHarvester;
|
||||
protected final I2PAppContext _context;
|
||||
|
||||
public RandomSource(I2PAppContext context) {
|
||||
super();
|
||||
_context = context;
|
||||
_log = context.logManager().getLog(RandomSource.class);
|
||||
// when we replace to have hooks for fortuna (etc), replace with
|
||||
// a factory (or just a factory method)
|
||||
_entropyHarvester = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton for whatever PRNG i2p uses.
|
||||
* Same as I2PAppContext.getGlobalContext().random();
|
||||
* use context.random() if you have a context already.
|
||||
* @return I2PAppContext.getGlobalContext().random()
|
||||
*/
|
||||
public static RandomSource getInstance() {
|
||||
return I2PAppContext.getGlobalContext().random();
|
||||
}
|
||||
@ -72,46 +77,30 @@ public class RandomSource extends SecureRandom implements EntropyHarvester {
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean nextBoolean() { return super.nextBoolean(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void nextBytes(byte buf[]) { super.nextBytes(buf); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
|
||||
@Override
|
||||
public double nextDouble() { return super.nextDouble(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
|
||||
@Override
|
||||
public float nextFloat() { return super.nextFloat(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
|
||||
@Override
|
||||
public double nextGaussian() { return super.nextGaussian(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
|
||||
@Override
|
||||
public int nextInt() { return super.nextInt(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
|
||||
@Override
|
||||
public long nextLong() { return super.nextLong(); }
|
||||
|
||||
*****/
|
||||
|
||||
/** */
|
||||
public EntropyHarvester harvester() { return _entropyHarvester; }
|
||||
|
||||
public void feedEntropy(String source, long data, int bitoffset, int bits) {
|
||||
|
@ -51,17 +51,18 @@ public class ShellCommand {
|
||||
*/
|
||||
private class CommandThread extends Thread {
|
||||
|
||||
final Object caller;
|
||||
boolean consumeOutput;
|
||||
String shellCommand;
|
||||
private final Object caller;
|
||||
private final boolean consumeOutput;
|
||||
private final Object shellCommand;
|
||||
|
||||
CommandThread(Object caller, String shellCommand, boolean consumeOutput) {
|
||||
/**
|
||||
* @param shellCommand either a String or a String[] (since 0.8.3)
|
||||
*/
|
||||
CommandThread(Object caller, Object shellCommand, boolean consumeOutput) {
|
||||
super("CommandThread");
|
||||
this.caller = caller;
|
||||
this.shellCommand = shellCommand;
|
||||
this.consumeOutput = consumeOutput;
|
||||
_commandSuccessful = false;
|
||||
_commandCompleted = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -200,6 +201,9 @@ public class ShellCommand {
|
||||
* {@link #getErrorStream()}, respectively. Input can be passed to the
|
||||
* <code>STDIN</code> of the shell process via {@link #getInputStream()}.
|
||||
*
|
||||
* Warning, no good way to quote or escape spaces in arguments with this method.
|
||||
* @deprecated unused
|
||||
*
|
||||
* @param shellCommand The command for the shell to execute.
|
||||
*/
|
||||
public void execute(String shellCommand) {
|
||||
@ -215,6 +219,9 @@ public class ShellCommand {
|
||||
* Input can be passed to the <code>STDIN</code> of the shell process via
|
||||
* {@link #getInputStream()}.
|
||||
*
|
||||
* Warning, no good way to quote or escape spaces in arguments with this method.
|
||||
* @deprecated unused
|
||||
*
|
||||
* @param shellCommand The command for the shell to execute.
|
||||
* @return <code>true</code> if the spawned shell process
|
||||
* returns an exit status of 0 (indicating success),
|
||||
@ -237,6 +244,9 @@ public class ShellCommand {
|
||||
* {@link #getErrorStream()}, respectively. Input can be passed to the
|
||||
* <code>STDIN</code> of the shell process via {@link #getInputStream()}.
|
||||
*
|
||||
* Warning, no good way to quote or escape spaces in arguments with this method.
|
||||
* @deprecated unused
|
||||
*
|
||||
* @param shellCommand The command for the shell to execute.
|
||||
* @param seconds The method will return <code>true</code> if this
|
||||
* number of seconds elapses without the process
|
||||
@ -276,6 +286,9 @@ public class ShellCommand {
|
||||
* without waiting for an exit status. Any output produced by the executed
|
||||
* command will not be displayed.
|
||||
*
|
||||
* Warning, no good way to quote or escape spaces in arguments with this method.
|
||||
* @deprecated unused
|
||||
*
|
||||
* @param shellCommand The command for the shell to execute.
|
||||
* @throws IOException
|
||||
*/
|
||||
@ -288,6 +301,8 @@ public class ShellCommand {
|
||||
* all of the command's resulting shell processes have completed. Any output
|
||||
* produced by the executed command will not be displayed.
|
||||
*
|
||||
* Warning, no good way to quote or escape spaces in arguments with this method.
|
||||
*
|
||||
* @param shellCommand The command for the shell to execute.
|
||||
* @return <code>true</code> if the spawned shell process
|
||||
* returns an exit status of 0 (indicating success),
|
||||
@ -307,7 +322,12 @@ public class ShellCommand {
|
||||
* specified number of seconds has elapsed first. Any output produced by the
|
||||
* executed command will not be displayed.
|
||||
*
|
||||
* @param shellCommand The command for the shell to execute.
|
||||
* Warning, no good way to quote or escape spaces in arguments when shellCommand is a String.
|
||||
* Use a String array for best results, especially on Windows.
|
||||
*
|
||||
* @param shellCommand The command for the shell to execute, as a String.
|
||||
* You can't quote arguments successfully.
|
||||
* See Runtime.exec(String) for more info.
|
||||
* @param seconds The method will return <code>true</code> if this
|
||||
* number of seconds elapses without the process
|
||||
* returning an exit status. A value of <code>0</code>
|
||||
@ -317,7 +337,33 @@ public class ShellCommand {
|
||||
* else <code>false</code>.
|
||||
*/
|
||||
public synchronized boolean executeSilentAndWaitTimed(String shellCommand, int seconds) {
|
||||
return executeSAWT(shellCommand, seconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes a command to the shell for execution. This method blocks until
|
||||
* all of the command's resulting shell processes have completed unless a
|
||||
* specified number of seconds has elapsed first. Any output produced by the
|
||||
* executed command will not be displayed.
|
||||
*
|
||||
* @param commandArray The command for the shell to execute,
|
||||
* as a String[].
|
||||
* See Runtime.exec(String[]) for more info.
|
||||
* @param seconds The method will return <code>true</code> if this
|
||||
* number of seconds elapses without the process
|
||||
* returning an exit status. A value of <code>0</code>
|
||||
* here disables waiting.
|
||||
* @return <code>true</code> if the spawned shell process
|
||||
* returns an exit status of 0 (indicating success),
|
||||
* else <code>false</code>.
|
||||
* @since 0.8.3
|
||||
*/
|
||||
public synchronized boolean executeSilentAndWaitTimed(String[] commandArray, int seconds) {
|
||||
return executeSAWT(commandArray, seconds);
|
||||
}
|
||||
|
||||
/** @since 0.8.3 */
|
||||
private boolean executeSAWT(Object shellCommand, int seconds) {
|
||||
_commandThread = new CommandThread(this, shellCommand, CONSUME_OUTPUT);
|
||||
_commandThread.start();
|
||||
try {
|
||||
@ -364,7 +410,10 @@ public class ShellCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
private boolean execute(String shellCommand, boolean consumeOutput, boolean waitForExitStatus) {
|
||||
/**
|
||||
* @param shellCommand either a String or a String[] (since 0.8.3) - quick hack
|
||||
*/
|
||||
private boolean execute(Object shellCommand, boolean consumeOutput, boolean waitForExitStatus) {
|
||||
|
||||
StreamConsumer processStderrConsumer;
|
||||
StreamConsumer processStdoutConsumer;
|
||||
@ -374,7 +423,13 @@ public class ShellCommand {
|
||||
StreamReader processStdoutReader;
|
||||
|
||||
try {
|
||||
_process = Runtime.getRuntime().exec(shellCommand, null);
|
||||
// easy way so we don't have to copy this whole method
|
||||
if (shellCommand instanceof String)
|
||||
_process = Runtime.getRuntime().exec((String)shellCommand);
|
||||
else if (shellCommand instanceof String[])
|
||||
_process = Runtime.getRuntime().exec((String[])shellCommand);
|
||||
else
|
||||
throw new ClassCastException("shell command must be a String or a String[]");
|
||||
if (consumeOutput) {
|
||||
processStderrConsumer = new StreamConsumer(_process.getErrorStream());
|
||||
processStderrConsumer.start();
|
||||
|
@ -28,12 +28,14 @@ import net.i2p.I2PAppContext;
|
||||
public class SimpleScheduler {
|
||||
private static final SimpleScheduler _instance = new SimpleScheduler();
|
||||
public static SimpleScheduler getInstance() { return _instance; }
|
||||
private static final int THREADS = 4;
|
||||
private static final int MIN_THREADS = 2;
|
||||
private static final int MAX_THREADS = 4;
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private ScheduledThreadPoolExecutor _executor;
|
||||
private String _name;
|
||||
private int _count;
|
||||
private final int _threads;
|
||||
|
||||
protected SimpleScheduler() { this("SimpleScheduler"); }
|
||||
protected SimpleScheduler(String name) {
|
||||
@ -41,7 +43,9 @@ public class SimpleScheduler {
|
||||
_log = _context.logManager().getLog(SimpleScheduler.class);
|
||||
_name = name;
|
||||
_count = 0;
|
||||
_executor = new ScheduledThreadPoolExecutor(THREADS, new CustomThreadFactory());
|
||||
long maxMemory = Runtime.getRuntime().maxMemory();
|
||||
_threads = (int) Math.max(MIN_THREADS, Math.min(MAX_THREADS, 1 + (maxMemory / (32*1024*1024))));
|
||||
_executor = new ScheduledThreadPoolExecutor(_threads, new CustomThreadFactory());
|
||||
_executor.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
@ -65,6 +69,13 @@ public class SimpleScheduler {
|
||||
re.schedule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue up the given event to be fired after timeoutMs and every
|
||||
* timeoutMs thereafter. The TimedEvent must not do its own rescheduling.
|
||||
* As all Exceptions are caught in run(), these will not prevent
|
||||
* subsequent executions (unlike SimpleTimer, where the TimedEvent does
|
||||
* its own rescheduling).
|
||||
*/
|
||||
public void addPeriodicEvent(SimpleTimer.TimedEvent event, long timeoutMs) {
|
||||
addPeriodicEvent(event, timeoutMs, timeoutMs);
|
||||
}
|
||||
@ -90,7 +101,7 @@ public class SimpleScheduler {
|
||||
private class CustomThreadFactory implements ThreadFactory {
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread rv = Executors.defaultThreadFactory().newThread(r);
|
||||
rv.setName(_name + ' ' + (++_count) + '/' + THREADS);
|
||||
rv.setName(_name + ' ' + (++_count) + '/' + _threads);
|
||||
// Uncomment this to test threadgrouping, but we should be all safe now that the constructor preallocates!
|
||||
// String name = rv.getThreadGroup().getName();
|
||||
// if(!name.equals("main")) {
|
||||
|
@ -18,14 +18,16 @@ import net.i2p.I2PAppContext;
|
||||
public class SimpleTimer {
|
||||
private static final SimpleTimer _instance = new SimpleTimer();
|
||||
public static SimpleTimer getInstance() { return _instance; }
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private final I2PAppContext _context;
|
||||
private final Log _log;
|
||||
/** event time (Long) to event (TimedEvent) mapping */
|
||||
private final TreeMap _events;
|
||||
/** event (TimedEvent) to event time (Long) mapping */
|
||||
private Map _eventTimes;
|
||||
private final List _readyEvents;
|
||||
private SimpleStore runn;
|
||||
private static final int MIN_THREADS = 2;
|
||||
private static final int MAX_THREADS = 4;
|
||||
|
||||
protected SimpleTimer() { this("SimpleTimer"); }
|
||||
protected SimpleTimer(String name) {
|
||||
@ -39,9 +41,11 @@ public class SimpleTimer {
|
||||
runner.setName(name);
|
||||
runner.setDaemon(true);
|
||||
runner.start();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
long maxMemory = Runtime.getRuntime().maxMemory();
|
||||
int threads = (int) Math.max(MIN_THREADS, Math.min(MAX_THREADS, 1 + (maxMemory / (32*1024*1024))));
|
||||
for (int i = 1; i <= threads ; i++) {
|
||||
I2PThread executor = new I2PThread(new Executor(_context, _log, _readyEvents, runn));
|
||||
executor.setName(name + "Executor " + i);
|
||||
executor.setName(name + "Executor " + i + '/' + threads);
|
||||
executor.setDaemon(true);
|
||||
executor.start();
|
||||
}
|
||||
|
@ -27,12 +27,14 @@ import net.i2p.I2PAppContext;
|
||||
public class SimpleTimer2 {
|
||||
private static final SimpleTimer2 _instance = new SimpleTimer2();
|
||||
public static SimpleTimer2 getInstance() { return _instance; }
|
||||
private static final int THREADS = 4;
|
||||
private static final int MIN_THREADS = 2;
|
||||
private static final int MAX_THREADS = 4;
|
||||
private I2PAppContext _context;
|
||||
private static Log _log; // static so TimedEvent can use it
|
||||
private ScheduledThreadPoolExecutor _executor;
|
||||
private String _name;
|
||||
private int _count;
|
||||
private final int _threads;
|
||||
|
||||
protected SimpleTimer2() { this("SimpleTimer2"); }
|
||||
protected SimpleTimer2(String name) {
|
||||
@ -40,7 +42,9 @@ public class SimpleTimer2 {
|
||||
_log = _context.logManager().getLog(SimpleTimer2.class);
|
||||
_name = name;
|
||||
_count = 0;
|
||||
_executor = new CustomScheduledThreadPoolExecutor(THREADS, new CustomThreadFactory());
|
||||
long maxMemory = Runtime.getRuntime().maxMemory();
|
||||
_threads = (int) Math.max(MIN_THREADS, Math.min(MAX_THREADS, 1 + (maxMemory / (32*1024*1024))));
|
||||
_executor = new CustomScheduledThreadPoolExecutor(_threads, new CustomThreadFactory());
|
||||
_executor.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
@ -67,7 +71,7 @@ public class SimpleTimer2 {
|
||||
private class CustomThreadFactory implements ThreadFactory {
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread rv = Executors.defaultThreadFactory().newThread(r);
|
||||
rv.setName(_name + ' ' + (++_count) + '/' + THREADS);
|
||||
rv.setName(_name + ' ' + (++_count) + '/' + _threads);
|
||||
// Uncomment this to test threadgrouping, but we should be all safe now that the constructor preallocates!
|
||||
// String name = rv.getThreadGroup().getName();
|
||||
// if(!name.equals("main")) {
|
||||
|
Reference in New Issue
Block a user