propagate from branch 'i2p.i2p' (head e2aa08a93036bcf0d846b8ff67e9cb74de3e4d0f)

to branch 'i2p.i2p.zzz.test2' (head b3d23ed369ba339b9a71dfeb205110458df9ec0d)
This commit is contained in:
zzz
2015-11-07 17:08:39 +00:00
676 changed files with 39415 additions and 29120 deletions

View File

@ -56,7 +56,7 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl
_isRunning = true;
_refillThread = new I2PThread(this, "PRNG");
_refillThread.setDaemon(true);
_refillThread.setPriority(Thread.MIN_PRIORITY+1);
_refillThread.setPriority(Thread.NORM_PRIORITY - 2);
_refillThread.start();
}

View File

@ -0,0 +1,31 @@
#**************************************************************************
#* MessagesBundle.properties -- English language error messages
#*
#* Copyright (c) 1998 by William King (wrking@eng.sun.com) and
#* Aaron M. Renn (arenn@urbanophile.com)
#*
#* This program is free software; you can redistribute it and/or modify
#* it under the terms of the GNU Library General Public License as published
#* by the Free Software Foundation; either version 2 of the License or
#* (at your option) any later version.
#*
#* This program is distributed in the hope that it will be useful, but
#* WITHOUT ANY WARRANTY; without even the implied warranty of
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#* GNU Library General Public License for more details.
#*
#* You should have received a copy of the GNU Library General Public License
#* along with this program; see the file COPYING.LIB. If not, write to
#* the Free Software Foundation Inc., 59 Temple Place - Suite 330,
#* Boston, MA 02111-1307 USA
#**************************************************************************/
getopt.ambigious={0}\: valitsin \u201d{1}\u201d ei ole yksiselitteinen
getopt.arguments1={0}\: valitsin ''--{1}'' ei salli argumenttia
getopt.arguments2={0}\: valitsin ''{1}{2}'' ei salli argumenttia
getopt.requires={0}\: valitsin ''{1}'' vaatii argumentin
getopt.unrecognized={0}\: tunnistamaton valitsin ''--{1}''
getopt.unrecognized2={0}\: tunnistamaton valitsin ''{1}{2}''
getopt.illegal={0}\: virheellinen valitsin -- {1}
getopt.invalid={0}\: virheellinen valitsin -- {1}
getopt.requires2={0}\: valitsin vaatii argumentin -- {1}
getopt.invalidValue=Virheellinen arvo {0} parametrille 'has_arg'

View File

@ -18,7 +18,7 @@ public class CoreVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = "0.9.19";
public final static String VERSION = "0.9.22";
/**
* For Vuze.

View File

@ -23,7 +23,6 @@ import net.i2p.data.Base64;
import net.i2p.data.RoutingKeyGenerator;
import net.i2p.internal.InternalClientManager;
import net.i2p.stat.StatManager;
import net.i2p.update.UpdateManager;
import net.i2p.util.Clock;
import net.i2p.util.ConcurrentHashSet;
import net.i2p.util.FileUtil;
@ -85,6 +84,7 @@ public class I2PAppContext {
private RandomSource _random;
private KeyGenerator _keyGenerator;
protected KeyRing _keyRing; // overridden in RouterContext
@SuppressWarnings("deprecation")
private SimpleScheduler _simpleScheduler;
private SimpleTimer _simpleTimer;
private SimpleTimer2 _simpleTimer2;
@ -533,7 +533,7 @@ public class I2PAppContext {
* @return set of Strings containing the names of defined system properties
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public Set<String> getPropertyNames() {
public Set<String> getPropertyNames() {
// clone to avoid ConcurrentModificationException
Set<String> names = new HashSet<String>((Set<String>) (Set) ((Properties) System.getProperties().clone()).keySet()); // TODO-Java6: s/keySet()/stringPropertyNames()/
if (_overrideProps != null)
@ -941,6 +941,7 @@ public class I2PAppContext {
* @since 0.9 to replace static instance in the class
* @deprecated in 0.9.20, use simpleTimer2()
*/
@SuppressWarnings("deprecation")
public SimpleScheduler simpleScheduler() {
if (!_simpleSchedulerInitialized)
initializeSimpleScheduler();

View File

@ -9,6 +9,8 @@ package net.i2p.client;
*
*/
import net.i2p.client.impl.I2PClientImpl;
/**
* Provide a means of hooking into an appropriate I2PClient implementation
*
@ -21,4 +23,4 @@ public class I2PClientFactory {
public static I2PClient createClient() {
return new I2PClientImpl();
}
}
}

View File

@ -9,6 +9,8 @@ package net.i2p.client;
*
*/
import java.io.InputStream;
import java.util.List;
import java.util.Properties;
import java.util.Set;
@ -16,12 +18,13 @@ import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.PrivateKey;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.data.SigningPrivateKey;
/**
* <p>Define the standard means of sending and receiving messages on the
* I2P network by using the I2CP (the client protocol). This is done over a
* bidirectional TCP socket and never sends any private keys.
* bidirectional TCP socket.
*
* End to end encryption in I2PSession was disabled in release 0.6.
*
@ -96,7 +99,7 @@ public interface I2PSession {
* objects that were sent along side the given keyUsed.
* @return success
*/
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException;
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set<SessionTag> tagsSent) throws I2PSessionException;
/**
* End-to-End Crypto is disabled, tags and keys are ignored.
@ -104,7 +107,7 @@ public interface I2PSession {
* @param tagsSent UNUSED, IGNORED.
* @return success
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent) throws I2PSessionException;
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent) throws I2PSessionException;
/**
* End-to-End Crypto is disabled, tags and keys are ignored.
@ -114,7 +117,7 @@ public interface I2PSession {
* @return success
* @since 0.7.1
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire) throws I2PSessionException;
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent, long expire) throws I2PSessionException;
/**
* See I2PSessionMuxedImpl for proto/port details.
@ -131,7 +134,7 @@ public interface I2PSession {
* @return success
* @since 0.7.1
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent,
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent,
int proto, int fromPort, int toPort) throws I2PSessionException;
/**
@ -150,7 +153,7 @@ public interface I2PSession {
* @return success
* @since 0.7.1
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire,
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent, long expire,
int proto, int fromPort, int toPort) throws I2PSessionException;
/**
@ -169,7 +172,7 @@ public interface I2PSession {
* @return success
* @since 0.8.4
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire,
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent, long expire,
int proto, int fromPort, int toPort, int flags) throws I2PSessionException;
/**
@ -247,6 +250,26 @@ public interface I2PSession {
*
*/
public void destroySession() throws I2PSessionException;
/**
* @return a new subsession, non-null
* @param privateKeyStream null for transient, if non-null must have same encryption keys as primary session
* and different signing keys
* @param opts subsession options if any, may be null
* @since 0.9.21
*/
public I2PSession addSubsession(InputStream privateKeyStream, Properties opts) throws I2PSessionException;
/**
* @since 0.9.21
*/
public void removeSubsession(I2PSession session);
/**
* @return a list of subsessions, non-null, does not include the primary session
* @since 0.9.21
*/
public List<I2PSession> getSubsessions();
/**
* Actually connect the session and start receiving/sending messages

View File

@ -12,6 +12,7 @@ import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.impl.I2PSimpleSession;
import net.i2p.crypto.SigType;
import net.i2p.data.Certificate;
import net.i2p.data.Destination;

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* Released into the public domain

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
import java.io.BufferedOutputStream;
import java.io.IOException;
@ -9,6 +9,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSessionException;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.I2CPMessageException;
import net.i2p.internal.PoisonI2CPMessage;

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* Released into the public domain

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* free (adj.): unencumbered; not under the control of others
@ -10,6 +10,7 @@ package net.i2p.client;
*/
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSessionException;
import net.i2p.data.i2cp.DisconnectMessage;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.util.I2PAppThread;

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* free (adj.): unencumbered; not under the control of others

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* Released into the public domain

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* free (adj.): unencumbered; not under the control of others

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* free (adj.): unencumbered; not under the control of others
@ -15,6 +15,8 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSessionException;
import net.i2p.client.SendMessageOptions;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.data.LeaseSet;
@ -34,6 +36,7 @@ import net.i2p.data.i2cp.ReportAbuseMessage;
import net.i2p.data.i2cp.SendMessageMessage;
import net.i2p.data.i2cp.SendMessageExpiresMessage;
import net.i2p.data.i2cp.SessionConfig;
import net.i2p.data.i2cp.SessionId;
import net.i2p.util.Log;
/**
@ -99,7 +102,7 @@ class I2CPMessageProducer {
if (_log.shouldLog(Log.DEBUG)) _log.debug("config signed");
msg.setSessionConfig(cfg);
if (_log.shouldLog(Log.DEBUG)) _log.debug("config loaded into message");
session.sendMessage(msg);
session.sendMessage_unchecked(msg);
if (_log.shouldLog(Log.DEBUG)) _log.debug("config message sent");
}
@ -111,7 +114,7 @@ class I2CPMessageProducer {
if (session.isClosed()) return;
DestroySessionMessage dmsg = new DestroySessionMessage();
dmsg.setSessionId(session.getSessionId());
session.sendMessage(dmsg);
session.sendMessage_unchecked(dmsg);
// use DisconnectMessage only if we fail and drop connection...
// todo: update the code to fire off DisconnectMessage on socket error
//DisconnectMessage msg = new DisconnectMessage();
@ -129,7 +132,7 @@ class I2CPMessageProducer {
* @param newKey unused - no end-to-end crypto
*/
public void sendMessage(I2PSessionImpl session, Destination dest, long nonce, byte[] payload, SessionTag tag,
SessionKey key, Set tags, SessionKey newKey, long expires) throws I2PSessionException {
SessionKey key, Set<SessionTag> tags, SessionKey newKey, long expires) throws I2PSessionException {
sendMessage(session, dest, nonce, payload, expires, 0);
}
@ -154,7 +157,12 @@ class I2CPMessageProducer {
} else
msg = new SendMessageMessage();
msg.setDestination(dest);
msg.setSessionId(session.getSessionId());
SessionId sid = session.getSessionId();
if (sid == null) {
_log.error(session.toString() + " send message w/o session", new Exception());
return;
}
msg.setSessionId(sid);
msg.setNonce(nonce);
Payload data = createPayload(dest, payload, null, null, null, null);
msg.setPayload(data);
@ -176,7 +184,12 @@ class I2CPMessageProducer {
return;
SendMessageMessage msg = new SendMessageExpiresMessage(options);
msg.setDestination(dest);
msg.setSessionId(session.getSessionId());
SessionId sid = session.getSessionId();
if (sid == null) {
_log.error(session.toString() + " send message w/o session", new Exception());
return;
}
msg.setSessionId(sid);
msg.setNonce(nonce);
Payload data = createPayload(dest, payload, null, null, null, null);
msg.setPayload(data);
@ -352,8 +365,13 @@ class I2CPMessageProducer {
msg.setLeaseSet(leaseSet);
msg.setPrivateKey(priv);
msg.setSigningPrivateKey(signingPriv);
msg.setSessionId(session.getSessionId());
session.sendMessage(msg);
SessionId sid = session.getSessionId();
if (sid == null) {
_log.error(session.toString() + " create LS w/o session", new Exception());
return;
}
msg.setSessionId(sid);
session.sendMessage_unchecked(msg);
}
/**
@ -381,7 +399,12 @@ class I2CPMessageProducer {
throw new I2PSessionException("Unable to sign the session config", dfe);
}
msg.setSessionConfig(cfg);
msg.setSessionId(session.getSessionId());
SessionId sid = session.getSessionId();
if (sid == null) {
_log.error(session.toString() + " update config w/o session", new Exception());
return;
}
msg.setSessionId(sid);
session.sendMessage(msg);
}
}

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* free (adj.): unencumbered; not under the control of others
@ -17,6 +17,9 @@ import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.crypto.KeyGenerator;
import net.i2p.crypto.SigType;
import net.i2p.data.Certificate;
@ -36,7 +39,7 @@ import net.i2p.util.RandomSource;
*
* @author jrandom
*/
class I2PClientImpl implements I2PClient {
public class I2PClientImpl implements I2PClient {
/**
* Create a destination with a DSA 1024/160 signature type and a null certificate.

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* free (adj.): unencumbered; not under the control of others

View File

@ -1,9 +1,13 @@
package net.i2p.client;
package net.i2p.client.impl;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.I2PSessionListener;
import net.i2p.client.I2PSessionMuxedListener;
import net.i2p.util.Log;
/*
@ -74,7 +78,9 @@ public class I2PSessionDemultiplexer implements I2PSessionMuxedListener {
* (Streaming lib)
*/
public void addListener(I2PSessionListener l, int proto, int port) {
_listeners.put(key(proto, port), new NoPortsListener(l));
I2PSessionListener old = _listeners.put(key(proto, port), new NoPortsListener(l));
if (old != null && _log.shouldLog(Log.WARN))
_log.warn("Listener " + l + " replaces " + old + " for proto: " + proto + " port: " + port);
}
/**
@ -82,7 +88,9 @@ public class I2PSessionDemultiplexer implements I2PSessionMuxedListener {
* UDP perhaps
*/
public void addMuxedListener(I2PSessionMuxedListener l, int proto, int port) {
_listeners.put(key(proto, port), l);
I2PSessionListener old = _listeners.put(key(proto, port), l);
if (old != null && _log.shouldLog(Log.WARN))
_log.warn("Listener " + l + " replaces " + old + " for proto: " + proto + " port: " + port);
}
public void removeListener(int proto, int port) {

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* free (adj.): unencumbered; not under the control of others
@ -23,11 +23,17 @@ import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import net.i2p.CoreVersion;
import net.i2p.I2PAppContext;
import net.i2p.client.DomainSocketFactory;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.I2PSessionListener;
import net.i2p.data.Base32;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
@ -36,13 +42,16 @@ import net.i2p.data.LeaseSet;
import net.i2p.data.PrivateKey;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.i2cp.DestLookupMessage;
import net.i2p.data.i2cp.DestReplyMessage;
import net.i2p.data.i2cp.GetBandwidthLimitsMessage;
import net.i2p.data.i2cp.GetDateMessage;
import net.i2p.data.i2cp.HostLookupMessage;
import net.i2p.data.i2cp.HostReplyMessage;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.I2CPMessageReader;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.data.i2cp.SessionId;
import net.i2p.data.i2cp.SessionStatusMessage;
import net.i2p.internal.I2CPMessageQueue;
import net.i2p.internal.InternalClientManager;
import net.i2p.internal.QueuedI2CPMessageReader;
@ -79,7 +88,16 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
/** this session's Id */
private SessionId _sessionId;
/** currently granted lease set, or null */
private volatile LeaseSet _leaseSet;
protected volatile LeaseSet _leaseSet;
// subsession stuff
// registered subsessions
private final List<SubSession> _subsessions;
// established subsessions
private final ConcurrentHashMap<SessionId, SubSession> _subsessionMap;
private final Object _subsessionLock = new Object();
private static final String MIN_SUBSESSION_VERSION = "0.9.21";
private volatile boolean _routerSupportsSubsessions;
/** hostname of router - will be null if in RouterContext */
protected final String _hostname;
@ -119,7 +137,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
protected final I2PAppContext _context;
/** monitor for waiting until a lease set has been granted */
private final Object _leaseSetWait = new Object();
protected final Object _leaseSetWait = new Object();
/**
* @since 0.9.8
@ -186,10 +204,12 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
TEST_LOOKUP ||
(routerVersion != null && routerVersion.length() > 0 &&
VersionComparator.comp(routerVersion, MIN_HOST_LOOKUP_VERSION) >= 0);
_routerSupportsSubsessions = _context.isRouterContext() ||
(routerVersion != null && routerVersion.length() > 0 &&
VersionComparator.comp(routerVersion, MIN_SUBSESSION_VERSION) >= 0);
synchronized (_stateLock) {
if (_state == State.OPENING) {
_state = State.GOTDATE;
_stateLock.notifyAll();
changeState(State.GOTDATE);
}
}
}
@ -203,18 +223,42 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
*/
protected I2PSessionImpl(I2PAppContext context, Properties options,
I2PClientMessageHandlerMap handlerMap) {
this(context, options, handlerMap, false);
this(context, options, handlerMap, null, false);
}
/*
* For extension by SubSession via I2PSessionMuxedImpl and I2PSessionImpl2
*
* @param destKeyStream stream containing the private key data,
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
* @param options set of options to configure the router with, if null will use System properties
* @since 0.9.21
*/
protected I2PSessionImpl(I2PSessionImpl primary, InputStream destKeyStream, Properties options) throws I2PSessionException {
this(primary.getContext(), options, primary.getHandlerMap(), primary.getProducer(), true);
_availabilityNotifier = new AvailabilityNotifier();
try {
readDestination(destKeyStream);
} catch (DataFormatException dfe) {
throw new I2PSessionException("Error reading the destination key stream", dfe);
} catch (IOException ioe) {
throw new I2PSessionException("Error reading the destination key stream", ioe);
}
}
/**
* Basic setup of finals
* @since 0.9.7
*/
private I2PSessionImpl(I2PAppContext context, Properties options,
I2PClientMessageHandlerMap handlerMap, boolean hasDest) {
I2PClientMessageHandlerMap handlerMap,
I2CPMessageProducer producer,
boolean hasDest) {
_context = context;
_handlerMap = handlerMap;
_log = context.logManager().getLog(getClass());
_subsessions = new CopyOnWriteArrayList<SubSession>();
_subsessionMap = new ConcurrentHashMap<SessionId, SubSession>(4);
if (options == null)
options = (Properties) System.getProperties().clone();
_options = loadConfig(options);
@ -222,7 +266,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
_portNum = getPort();
_fastReceive = Boolean.parseBoolean(_options.getProperty(I2PClient.PROP_FAST_RECEIVE));
if (hasDest) {
_producer = new I2CPMessageProducer(context);
_producer = producer;
_availableMessages = new ConcurrentHashMap<Long, MessagePayloadMessage>();
_myDestination = new Destination();
_privateKey = new PrivateKey();
@ -236,6 +280,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
}
_routerSupportsFastReceive = _context.isRouterContext();
_routerSupportsHostLookup = _context.isRouterContext();
_routerSupportsSubsessions = _context.isRouterContext();
}
/**
@ -247,10 +292,10 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
* @param destKeyStream stream containing the private key data,
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
* @param options set of options to configure the router with, if null will use System properties
* @throws I2PSessionException if there is a problem loading the private keys or
* @throws I2PSessionException if there is a problem loading the private keys
*/
public I2PSessionImpl(I2PAppContext context, InputStream destKeyStream, Properties options) throws I2PSessionException {
this(context, options, new I2PClientMessageHandlerMap(context), true);
this(context, options, new I2PClientMessageHandlerMap(context), new I2CPMessageProducer(context), true);
_availabilityNotifier = new AvailabilityNotifier();
try {
readDestination(destKeyStream);
@ -260,6 +305,69 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
throw new I2PSessionException("Error reading the destination key stream", ioe);
}
}
/**
* Router must be connected or was connected... for now.
*
* @return a new subsession, non-null
* @param privateKeyStream null for transient, if non-null must have same encryption keys as primary session
* and different signing keys
* @param opts subsession options if any, may be null
* @since 0.9.21
*/
public I2PSession addSubsession(InputStream privateKeyStream, Properties opts) throws I2PSessionException {
if (!_routerSupportsSubsessions)
throw new I2PSessionException("Router does not support subsessions");
SubSession sub;
synchronized(_subsessionLock) {
if (_subsessions.size() > _subsessionMap.size())
throw new I2PSessionException("Subsession request already pending");
sub = new SubSession(this, privateKeyStream, opts);
for (SubSession ss : _subsessions) {
if (ss.getDecryptionKey().equals(sub.getDecryptionKey()) &&
ss.getPrivateKey().equals(sub.getPrivateKey())) {
throw new I2PSessionException("Dup subsession");
}
}
_subsessions.add(sub);
}
synchronized (_stateLock) {
if (_state == State.OPEN) {
_producer.connect(sub);
} // else will be called in connect()
}
return sub;
}
/**
* @since 0.9.21
*/
public void removeSubsession(I2PSession session) {
if (!(session instanceof SubSession))
return;
synchronized(_subsessionLock) {
_subsessions.remove(session);
SessionId id = ((SubSession) session).getSessionId();
if (id != null)
_subsessionMap.remove(id);
/// tell the subsession
try {
// doesn't really throw
session.destroySession();
} catch (I2PSessionException ise) {}
}
}
/**
* @return a list of subsessions, non-null, does not include the primary session
* @since 0.9.21
*/
public List<I2PSession> getSubsessions() {
synchronized(_subsessionLock) {
return new ArrayList<I2PSession>(_subsessions);
}
}
/**
* Parse the config for anything we know about.
@ -400,6 +508,8 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
}
protected void changeState(State state) {
if (_log.shouldInfo())
_log.info(getPrefix() + "Change state to " + state);
synchronized (_stateLock) {
_state = state;
_stateLock.notifyAll();
@ -493,6 +603,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
try {
I2PSSLSocketFactory fact = new I2PSSLSocketFactory(_context, false, "certificates/i2cp");
_socket = fact.createSocket(_hostname, _portNum);
_socket.setKeepAlive(true);
} catch (GeneralSecurityException gse) {
IOException ioe = new IOException("SSL Fail");
ioe.initCause(gse);
@ -500,6 +611,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
}
} else {
_socket = new Socket(_hostname, _portNum);
_socket.setKeepAlive(true);
}
// _socket.setSoTimeout(1000000); // Uhmmm we could really-really use a real timeout, and handle it.
OutputStream out = _socket.getOutputStream();
@ -522,7 +634,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
auth.setProperty(PROP_USER, _options.getProperty(PROP_USER));
auth.setProperty(PROP_PW, _options.getProperty(PROP_PW));
}
sendMessage(new GetDateMessage(CoreVersion.VERSION, auth));
sendMessage_unchecked(new GetDateMessage(CoreVersion.VERSION, auth));
waitForDate();
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Before producer.connect()");
@ -551,6 +663,16 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
startIdleMonitor();
startVerifyUsage();
success = true;
// now send CreateSessionMessages for all subsessions, one at a time, must wait for each response
synchronized(_subsessionLock) {
for (SubSession ss : _subsessions) {
if (_log.shouldLog(Log.INFO))
_log.info(getPrefix() + "Connecting subsession " + ss);
_producer.connect(ss);
}
}
} catch (InterruptedException ie) {
throw new I2PSessionException("Interrupted", ie);
} catch (UnknownHostException uhe) {
@ -614,14 +736,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
* Report abuse with regards to the given messageId
*/
public void reportAbuse(int msgId, int severity) throws I2PSessionException {
synchronized (_stateLock) {
if (_state == State.CLOSED)
throw new I2PSessionException("Already closed");
if (_state == State.INIT)
throw new I2PSessionException("Not open, must call connect() first");
if (_state == State.OPENING) // not before GOTDATE
throw new I2PSessionException("Session not open yet");
}
verifyOpen();
_producer.reportAbuse(this, msgId, severity);
}
@ -761,19 +876,83 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
/**
* The I2CPMessageEventListener callback.
* Recieve notification of some I2CP message and handle it if possible.
*
* We route the message based on message type AND session ID.
*
* The following types never contain a session ID and are not routable to
* a subsession:
* BandwidthLimitsMessage, DestReplyMessage
*
* The following types may not contain a valid session ID
* even when intended for a subsession, so we must take special care:
* SessionStatusMessage
*
* @param reader unused
*/
public void messageReceived(I2CPMessageReader reader, I2CPMessage message) {
I2CPMessageHandler handler = _handlerMap.getHandler(message.getType());
if (handler == null) {
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + "Unknown message or unhandleable message received: type = "
+ message.getType());
int type = message.getType();
SessionId id = message.sessionId();
SessionId currId = _sessionId;
if (id == null || id.equals(currId) ||
(currId == null && id != null && type == SessionStatusMessage.MESSAGE_TYPE) ||
((id == null || id.getSessionId() == 65535) &&
(type == HostReplyMessage.MESSAGE_TYPE || type == DestReplyMessage.MESSAGE_TYPE))) {
// it's for us
I2CPMessageHandler handler = _handlerMap.getHandler(type);
if (handler != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Message received of type " + type
+ " to be handled by " + handler.getClass().getSimpleName());
handler.handleMessage(message, this);
} else {
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + "Unknown message or unhandleable message received: type = "
+ type);
}
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Message received of type " + message.getType()
+ " to be handled by " + handler.getClass().getSimpleName());
handler.handleMessage(message, this);
SubSession sub = _subsessionMap.get(id);
if (sub != null) {
// it's for a subsession
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "Message received of type " + type
+ " to be handled by " + sub);
sub.messageReceived(reader, message);
} else if (id != null && type == SessionStatusMessage.MESSAGE_TYPE) {
// look for a subsession without a session
synchronized (_subsessionLock) {
for (SubSession sess : _subsessions) {
if (sess.getSessionId() == null) {
sess.messageReceived(reader, message);
id = sess.getSessionId();
if (id != null) {
if (id.equals(_sessionId)) {
// shouldnt happen
sess.setSessionId(null);
if (_log.shouldLog(Log.WARN))
_log.warn("Dup or our session id " + id);
} else {
SubSession old = _subsessionMap.putIfAbsent(id, sess);
if (old != null) {
// shouldnt happen
sess.setSessionId(null);
if (_log.shouldLog(Log.WARN))
_log.warn("Dup session id " + id);
}
}
}
return;
}
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + "No session " + id + " to handle message: type = "
+ type);
}
}
} else {
// it's for nobody
if (_log.shouldLog(Log.WARN))
_log.warn(getPrefix() + "No session " + id + " to handle message: type = "
+ type);
}
}
}
@ -808,6 +987,18 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
*/
I2CPMessageProducer getProducer() { return _producer; }
/**
* For Subsessions
* @since 0.9.21
*/
I2PClientMessageHandlerMap getHandlerMap() { return _handlerMap; }
/**
* For Subsessions
* @since 0.9.21
*/
I2PAppContext getContext() { return _context; }
/**
* Retrieve the configuration options, filtered.
* All defaults passed in via constructor have been promoted to the primary map.
@ -835,6 +1026,39 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
}
}
/**
* Throws I2PSessionException if uninitialized, closed or closing.
* Blocks if opening.
*
* @since 0.9.23
*/
protected void verifyOpen() throws I2PSessionException {
synchronized (_stateLock) {
while (true) {
switch (_state) {
case INIT:
throw new I2PSessionException("Not open, must call connect() first");
case OPENING: // fall thru
case GOTDATE:
try {
_stateLock.wait(5*1000);
continue;
} catch (InterruptedException ie) {
throw new I2PSessionException("Interrupted", ie);
}
case OPEN:
return;
case CLOSING: // fall thru
case CLOSED:
throw new I2PSessionException("Already closed");
}
}
}
}
/**
* Deliver an I2CP message to the router
* As of 0.9.3, may block for several seconds if the write queue to the router is full
@ -842,12 +1066,19 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
* @throws I2PSessionException if the message is malformed or there is an error writing it out
*/
void sendMessage(I2CPMessage message) throws I2PSessionException {
synchronized (_stateLock) {
if (_state == State.CLOSED)
throw new I2PSessionException("Already closed");
if (_state == State.INIT)
throw new I2PSessionException("Not open, must call connect() first");
}
verifyOpen();
sendMessage_unchecked(message);
}
/**
* Deliver an I2CP message to the router.
* Does NOT check state. Call only from connect() or other methods that need to
* send messages when not in OPEN state.
*
* @throws I2PSessionException if the message is malformed or there is an error writing it out
* @since 0.9.23
*/
void sendMessage_unchecked(I2CPMessage message) throws I2PSessionException {
if (_queue != null) {
// internal
try {
@ -856,11 +1087,13 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
} catch (InterruptedException ie) {
throw new I2PSessionException("Interrupted", ie);
}
} else if (_writer == null) {
// race here
throw new I2PSessionException("Already closed or not open");
} else {
_writer.addMessage(message);
ClientWriterRunner writer = _writer;
if (writer == null) {
throw new I2PSessionException("Already closed or not open");
} else {
writer.addMessage(message);
}
}
}
@ -921,6 +1154,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
if (_availabilityNotifier != null)
_availabilityNotifier.stopNotifying();
closeSocket();
_subsessionMap.clear();
if (_sessionListener != null) _sessionListener.disconnected(this);
}
@ -935,6 +1169,13 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
locked_closeSocket();
changeState(State.CLOSED);
}
synchronized (_subsessionLock) {
for (SubSession sess : _subsessions) {
sess.changeState(State.CLOSED);
sess.setSessionId(null);
sess.setLeaseSet(null);
}
}
}
/**
@ -964,6 +1205,8 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
_socket = null; // so when propogateError calls closeSocket, it doesnt loop
}
}
setSessionId(null);
setLeaseSet(null);
}
/**
@ -1045,13 +1288,15 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
protected String getPrefix() {
StringBuilder buf = new StringBuilder();
buf.append('[');
buf.append(_state.toString()).append(' ');
String s = _options.getProperty("inbound.nickname");
if (s != null)
buf.append(s);
else
buf.append(getClass().getSimpleName());
if (_sessionId != null)
buf.append(" #").append(_sessionId.getSessionId());
SessionId id = _sessionId;
if (id != null)
buf.append(" #").append(id.getSessionId());
buf.append("]: ");
return buf.toString();
}
@ -1230,11 +1475,11 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
SessionId id = _sessionId;
if (id == null)
id = new SessionId(65535);
sendMessage(new HostLookupMessage(id, h, nonce, maxWait));
sendMessage_unchecked(new HostLookupMessage(id, h, nonce, maxWait));
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Sending DestLookup for " + h);
sendMessage(new DestLookupMessage(h));
sendMessage_unchecked(new DestLookupMessage(h));
}
try {
synchronized (waiter) {
@ -1322,7 +1567,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
SessionId id = _sessionId;
if (id == null)
id = new SessionId(65535);
sendMessage(new HostLookupMessage(id, name, nonce, maxWait));
sendMessage_unchecked(new HostLookupMessage(id, name, nonce, maxWait));
try {
synchronized (waiter) {
waiter.wait(maxWait);
@ -1356,7 +1601,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
return null;
}
}
sendMessage(new GetBandwidthLimitsMessage());
sendMessage_unchecked(new GetBandwidthLimitsMessage());
try {
synchronized (_bwReceivedLock) {
_bwReceivedLock.wait(5*1000);

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* free (adj.): unencumbered; not under the control of others
@ -20,9 +20,16 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PSessionException;
import net.i2p.client.I2PSessionListener;
import net.i2p.client.I2PSessionMuxedListener;
import net.i2p.client.SendMessageOptions;
import net.i2p.client.SendMessageStatusListener;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.util.Log;
@ -50,9 +57,9 @@ class I2PSessionImpl2 extends I2PSessionImpl {
private static final long REMOVE_EXPIRED_TIME = 63*1000;
/**
* for extension by SimpleSession (no dest)
*/
/**
* for extension by SimpleSession (no dest)
*/
protected I2PSessionImpl2(I2PAppContext context, Properties options,
I2PClientMessageHandlerMap handlerMap) {
super(context, options, handlerMap);
@ -61,15 +68,17 @@ class I2PSessionImpl2 extends I2PSessionImpl {
}
/**
* for extension by I2PSessionMuxedImpl
*
* Create a new session, reading the Destination, PrivateKey, and SigningPrivateKey
* from the destKeyStream, and using the specified options to connect to the router
*
* @param destKeyStream stream containing the private key data,
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
* @param options set of options to configure the router with, if null will use System properties
* @throws I2PSessionException if there is a problem loading the private keys or
* @throws I2PSessionException if there is a problem loading the private keys
*/
public I2PSessionImpl2(I2PAppContext ctx, InputStream destKeyStream, Properties options) throws I2PSessionException {
protected I2PSessionImpl2(I2PAppContext ctx, InputStream destKeyStream, Properties options) throws I2PSessionException {
super(ctx, destKeyStream, options);
_sendingStates = new ConcurrentHashMap<Long, MessageState>(32);
_sendMessageNonce = new AtomicLong();
@ -94,6 +103,26 @@ class I2PSessionImpl2 extends I2PSessionImpl {
_context.statManager().createRateStat("i2cp.tx.msgExpanded", "size before compression", "i2cp", new long[] { 30*60*1000 });
}
/*
* For extension by SubSession via I2PSessionMuxedImpl
*
* @param destKeyStream stream containing the private key data,
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
* @param options set of options to configure the router with, if null will use System properties
* @since 0.9.21
*/
protected I2PSessionImpl2(I2PSessionImpl primary, InputStream destKeyStream, Properties options) throws I2PSessionException {
super(primary, destKeyStream, options);
_sendingStates = new ConcurrentHashMap<Long, MessageState>(32);
_sendMessageNonce = new AtomicLong();
_noEffort = "none".equals(getOptions().getProperty(I2PClient.PROP_RELIABILITY, "").toLowerCase(Locale.US));
_context.statManager().createRateStat("i2cp.receiveStatusTime.1", "How long it took to get status=1 back", "i2cp", new long[] { 10*60*1000 });
_context.statManager().createRateStat("i2cp.receiveStatusTime.4", "How long it took to get status=4 back", "i2cp", new long[] { 10*60*1000 });
_context.statManager().createRateStat("i2cp.receiveStatusTime.5", "How long it took to get status=5 back", "i2cp", new long[] { 10*60*1000 });
_context.statManager().createRateStat("i2cp.tx.msgCompressed", "compressed size transferred", "i2cp", new long[] { 30*60*1000 });
_context.statManager().createRateStat("i2cp.tx.msgExpanded", "size before compression", "i2cp", new long[] { 30*60*1000 });
}
/**
* Fire up a periodic task to check for unclaimed messages
* @since 0.9.14
@ -182,17 +211,17 @@ class I2PSessionImpl2 extends I2PSessionImpl {
throw new UnsupportedOperationException("Use MuxedImpl");
}
/** @throws UnsupportedOperationException always, use MuxedImpl */
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent,
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent,
int proto, int fromport, int toport) throws I2PSessionException {
throw new UnsupportedOperationException("Use MuxedImpl");
}
/** @throws UnsupportedOperationException always, use MuxedImpl */
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire,
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent, long expire,
int proto, int fromport, int toport) throws I2PSessionException {
throw new UnsupportedOperationException("Use MuxedImpl");
}
/** @throws UnsupportedOperationException always, use MuxedImpl */
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire,
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent, long expire,
int proto, int fromport, int toport, int flags) throws I2PSessionException {
throw new UnsupportedOperationException("Use MuxedImpl");
}
@ -225,7 +254,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
* @param tagsSent unused - no end-to-end crypto
*/
@Override
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException {
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set<SessionTag> tagsSent) throws I2PSessionException {
return sendMessage(dest, payload, 0, payload.length, keyUsed, tagsSent, 0);
}
@ -233,7 +262,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
* @param keyUsed unused - no end-to-end crypto
* @param tagsSent unused - no end-to-end crypto
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent)
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent)
throws I2PSessionException {
return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0);
}
@ -244,17 +273,10 @@ class I2PSessionImpl2 extends I2PSessionImpl {
* @param keyUsed unused - no end-to-end crypto
* @param tagsSent unused - no end-to-end crypto
*/
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expires)
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent, long expires)
throws I2PSessionException {
if (_log.shouldLog(Log.DEBUG)) _log.debug("sending message");
synchronized (_stateLock) {
if (_state == State.CLOSED)
throw new I2PSessionException("Already closed");
if (_state == State.INIT)
throw new I2PSessionException("Not open, must call connect() first");
if (_state == State.OPENING || _state == State.GOTDATE) // not before GOTDATE or session
throw new I2PSessionException("Session not open yet");
}
verifyOpen();
updateActivity();
// Sadly there is no way to send something completely uncompressed in a backward-compatible way,
@ -311,7 +333,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
* @param keyUsed unused - no end-to-end crypto
* @param tagsSent unused - no end-to-end crypto
*/
protected boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent, long expires)
protected boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set<SessionTag> tagsSent, long expires)
throws I2PSessionException {
return sendBestEffort(dest, payload, expires, 0);
}

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* public domain
@ -8,12 +8,18 @@ import java.io.InputStream;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSessionException;
import net.i2p.client.I2PSessionListener;
import net.i2p.client.I2PSessionMuxedListener;
import net.i2p.client.SendMessageOptions;
import net.i2p.client.SendMessageStatusListener;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.util.Log;
@ -82,6 +88,24 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 {
// discards the one in super(), sorry about that... (no it wasn't started yet)
_availabilityNotifier = new MuxedAvailabilityNotifier();
}
/*
* For extension by SubSession
*
* @param destKeyStream stream containing the private key data,
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
* @param options set of options to configure the router with, if null will use System properties
* @since 0.9.21
*/
protected I2PSessionMuxedImpl(I2PSessionImpl primary, InputStream destKeyStream, Properties options) throws I2PSessionException {
super(primary, destKeyStream, options);
// also stored in _sessionListener but we keep it in _demultipexer
// as well so we don't have to keep casting
_demultiplexer = new I2PSessionDemultiplexer(primary.getContext());
super.setSessionListener(_demultiplexer);
// discards the one in super(), sorry about that... (no it wasn't started yet)
_availabilityNotifier = new MuxedAvailabilityNotifier();
}
/** listen on all protocols and ports */
@Override
@ -140,7 +164,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 {
*/
@Override
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size,
SessionKey keyUsed, Set tagsSent, long expires)
SessionKey keyUsed, Set<SessionTag> tagsSent, long expires)
throws I2PSessionException {
return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0, PROTO_UNSPECIFIED, PORT_UNSPECIFIED, PORT_UNSPECIFIED);
}
@ -150,7 +174,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 {
* @param tagsSent unused - no end-to-end crypto
*/
@Override
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent,
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set<SessionTag> tagsSent,
int proto, int fromport, int toport) throws I2PSessionException {
return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0, proto, fromport, toport);
}
@ -169,7 +193,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 {
*/
@Override
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size,
SessionKey keyUsed, Set tagsSent, long expires,
SessionKey keyUsed, Set<SessionTag> tagsSent, long expires,
int proto, int fromPort, int toPort)
throws I2PSessionException {
return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0, proto, fromPort, toPort, 0);
@ -190,7 +214,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 {
*/
@Override
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size,
SessionKey keyUsed, Set tagsSent, long expires,
SessionKey keyUsed, Set<SessionTag> tagsSent, long expires,
int proto, int fromPort, int toPort, int flags)
throws I2PSessionException {
payload = prepPayload(payload, offset, size, proto, fromPort, toPort);
@ -256,14 +280,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 {
* @since 0.9.14
*/
private byte[] prepPayload(byte[] payload, int offset, int size, int proto, int fromPort, int toPort) throws I2PSessionException {
synchronized (_stateLock) {
if (_state == State.CLOSED)
throw new I2PSessionException("Already closed");
if (_state == State.INIT)
throw new I2PSessionException("Not open, must call connect() first");
if (_state == State.OPENING || _state == State.GOTDATE) // not before GOTDATE or session
throw new I2PSessionException("Session not open yet");
}
verifyOpen();
updateActivity();
if (shouldCompress(size))
@ -315,9 +332,9 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 {
protected class MuxedAvailabilityNotifier extends AvailabilityNotifier {
private final LinkedBlockingQueue<MsgData> _msgs;
private volatile boolean _alive = false;
private volatile boolean _alive;
private static final int POISON_SIZE = -99999;
private final AtomicBoolean stopping = new AtomicBoolean(false);
private final AtomicBoolean stopping = new AtomicBoolean();
public MuxedAvailabilityNotifier() {
_msgs = new LinkedBlockingQueue<MsgData>();
@ -325,12 +342,12 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 {
@Override
public void stopNotifying() {
boolean again = true;
synchronized (stopping) {
if( !stopping.getAndSet(true)) {
if (_alive == true) {
_msgs.clear();
if (_alive) {
// System.out.println("I2PSessionMuxedImpl.stopNotifying()");
_msgs.clear();
boolean again = true;
while(again) {
try {
_msgs.put(new MsgData(0, POISON_SIZE, 0, 0, 0));
@ -340,8 +357,8 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 {
continue;
}
}
_alive = false;
}
_alive = false;
stopping.set(false);
}
// stopping.notifyAll();
@ -355,17 +372,24 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 {
try {
_msgs.put(new MsgData((int)(msgId & 0xffffffff), size, proto, fromPort, toPort));
} catch (InterruptedException ie) {}
if (!_alive && _log.shouldLog(Log.WARN))
_log.warn(getPrefix() + "message available but notifier not running");
}
@Override
public void run() {
MsgData msg;
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix() + "starting muxed availability notifier");
_msgs.clear();
_alive=true;
while (_alive) {
MsgData msg;
try {
msg = _msgs.take();
} catch (InterruptedException ie) {
_log.debug("I2PSessionMuxedImpl.run() InterruptedException " + String.valueOf(_msgs.size()) + " Messages, Alive " + _alive);
if (_log.shouldLog(Log.DEBUG))
_log.debug("I2PSessionMuxedImpl.run() InterruptedException " +
String.valueOf(_msgs.size()) + " Messages, Alive " + _alive);
continue;
}
if (msg.size == POISON_SIZE) {

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* Released into the public domain
@ -16,6 +16,8 @@ import java.util.Properties;
import net.i2p.CoreVersion;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PSessionException;
import net.i2p.data.i2cp.BandwidthLimitsMessage;
import net.i2p.data.i2cp.DestReplyMessage;
import net.i2p.data.i2cp.DisconnectMessage;
@ -37,7 +39,7 @@ import net.i2p.util.OrderedProperties;
*
* @author zzz
*/
class I2PSimpleSession extends I2PSessionImpl2 {
public class I2PSimpleSession extends I2PSessionImpl2 {
private static final int BUF_SIZE = 1024;
@ -89,6 +91,7 @@ class I2PSimpleSession extends I2PSessionImpl2 {
} else {
_socket = new Socket(_hostname, _portNum);
}
_socket.setKeepAlive(true);
OutputStream out = _socket.getOutputStream();
out.write(I2PClient.PROTOCOL_BYTE);
out.flush();
@ -119,11 +122,11 @@ class I2PSimpleSession extends I2PSessionImpl2 {
Properties auth = new OrderedProperties();
auth.setProperty(PROP_USER, opts.getProperty(PROP_USER));
auth.setProperty(PROP_PW, opts.getProperty(PROP_PW));
sendMessage(new GetDateMessage(CoreVersion.VERSION, auth));
sendMessage_unchecked(new GetDateMessage(CoreVersion.VERSION, auth));
} else {
// we must now send a GetDate even in SimpleSession, or we won't know
// what version we are talking with and cannot use HostLookup
sendMessage(new GetDateMessage(CoreVersion.VERSION));
sendMessage_unchecked(new GetDateMessage(CoreVersion.VERSION));
}
waitForDate();
}

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* free (adj.): unencumbered; not under the control of others
@ -10,6 +10,7 @@ package net.i2p.client;
*/
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSessionException;
import net.i2p.data.DataFormatException;
import net.i2p.data.Payload;
import net.i2p.data.i2cp.I2CPMessage;
@ -33,7 +34,7 @@ class MessagePayloadMessageHandler extends HandlerImpl {
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Handle message " + message);
_log.debug("Handle message " + message + " for session " + session);
try {
MessagePayloadMessage msg = (MessagePayloadMessage) message;
long id = msg.getMessageId();

View File

@ -1,8 +1,10 @@
package net.i2p.client;
package net.i2p.client.impl;
import java.util.concurrent.atomic.AtomicLong;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSession;
import net.i2p.client.SendMessageStatusListener;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.util.Log;

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* free (adj.): unencumbered; not under the control of others
@ -10,6 +10,7 @@ package net.i2p.client;
*/
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSessionException;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.data.i2cp.ReceiveMessageBeginMessage;

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* free (adj.): unencumbered; not under the control of others
@ -14,6 +14,7 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSessionException;
import net.i2p.crypto.KeyGenerator;
import net.i2p.crypto.SigType;
import net.i2p.data.DataFormatException;
@ -88,9 +89,8 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
String sspk = session.getOptions().getProperty("i2cp.leaseSetSigningPrivateKey");
PrivateKey privKey = null;
SigningPrivateKey signingPrivKey = null;
boolean useOldKeys;
if (spk != null && sspk != null) {
useOldKeys = true;
boolean useOldKeys = true;
int colon = sspk.indexOf(':');
SigType type = dest.getSigType();
if (colon > 0) {
@ -111,6 +111,7 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
signingPrivKey.fromBase64(sspk);
} catch (DataFormatException iae) {
useOldKeys = false;
signingPrivKey = null;
}
}
if (useOldKeys) {
@ -118,20 +119,36 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
privKey = new PrivateKey();
privKey.fromBase64(spk);
} catch (DataFormatException iae) {
useOldKeys = false;
privKey = null;
}
}
} else {
useOldKeys = false;
}
if (useOldKeys)
li = new LeaseInfo(privKey, signingPrivKey);
else
if (privKey == null && !_existingLeaseSets.isEmpty()) {
// look for keypair from another dest using same pubkey
PublicKey pk = dest.getPublicKey();
for (Map.Entry<Destination, LeaseInfo> e : _existingLeaseSets.entrySet()) {
if (pk.equals(e.getKey().getPublicKey())) {
privKey = e.getValue().getPrivateKey();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Creating new leaseInfo keys for " + dest + " with private key from " + e.getKey());
break;
}
}
}
if (privKey != null) {
if (signingPrivKey != null) {
li = new LeaseInfo(privKey, signingPrivKey);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Creating new leaseInfo keys for " + dest + " WITH configured private keys");
} else {
li = new LeaseInfo(privKey, dest);
}
} else {
li = new LeaseInfo(dest);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Creating new leaseInfo keys for " + dest + " without configured private keys");
}
_existingLeaseSets.put(dest, li);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Creating new leaseInfo keys for "
+ dest + " using configured private keys? " + useOldKeys);
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Caching the old leaseInfo keys for "
@ -178,6 +195,9 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
private final SigningPublicKey _signingPubKey;
private final SigningPrivateKey _signingPrivKey;
/**
* New keys
*/
public LeaseInfo(Destination dest) {
SimpleDataStructure encKeys[] = KeyGenerator.getInstance().generatePKIKeys();
// must be same type as the Destination's signing key
@ -194,6 +214,7 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
}
/**
* Existing keys
* @since 0.9.18
*/
public LeaseInfo(PrivateKey privKey, SigningPrivateKey signingPrivKey) {
@ -203,6 +224,23 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
_signingPrivKey = signingPrivKey;
}
/**
* Existing crypto key, new signing key
* @since 0.9.21
*/
public LeaseInfo(PrivateKey privKey, Destination dest) {
SimpleDataStructure signKeys[];
try {
signKeys = KeyGenerator.getInstance().generateSigningKeys(dest.getSigningPublicKey().getType());
} catch (GeneralSecurityException gse) {
throw new IllegalStateException(gse);
}
_pubKey = KeyGenerator.getPublicKey(privKey);
_privKey = privKey;
_signingPubKey = (SigningPublicKey) signKeys[0];
_signingPrivKey = (SigningPrivateKey) signKeys[1];
}
public PublicKey getPublicKey() {
return _pubKey;
}

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* free (adj.): unencumbered; not under the control of others

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* free (adj.): unencumbered; not under the control of others
@ -8,6 +8,7 @@ package net.i2p.client;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSessionException;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer;

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* free (adj.): unencumbered; not under the control of others
@ -10,6 +10,7 @@ package net.i2p.client;
*/
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSessionException;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.SessionStatusMessage;
import net.i2p.util.Log;

View File

@ -1,4 +1,4 @@
package net.i2p.client;
package net.i2p.client.impl;
/*
* free (adj.): unencumbered; not under the control of others

View File

@ -0,0 +1,347 @@
package net.i2p.client.impl;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.PrivateKey;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.i2cp.CreateLeaseSetMessage;
import net.i2p.data.i2cp.CreateSessionMessage;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.SessionId;
import net.i2p.util.I2PAppThread;
/**
* An additional session using another session's connection.
*
* A subsession uses the same connection to the router as the primary session,
* but has a different Destination. It uses the same tunnels as the primary
* but has its own leaseset. It must use the same encryption keys as the primary
* so that garlic encryption/decryption works.
*
* The message handler map and message producer are reused from primary.
*
* Does NOT reuse the session listener ????
*
* While the I2CP protocol, in theory, allows for fully independent sessions
* over the same I2CP connection, this is not currently supported by the router.
*
* @since 0.9.21
*/
class SubSession extends I2PSessionMuxedImpl {
private final I2PSessionMuxedImpl _primary;
/**
* @param primary must be a I2PSessionMuxedImpl
*/
public SubSession(I2PSession primary, InputStream destKeyStream, Properties options) throws I2PSessionException {
super((I2PSessionMuxedImpl)primary, destKeyStream, options);
_primary = (I2PSessionMuxedImpl) primary;
if (!getDecryptionKey().equals(_primary.getDecryptionKey()))
throw new I2PSessionException("encryption key mismatch");
if (getPrivateKey().equals(_primary.getPrivateKey()))
throw new I2PSessionException("signing key must differ");
// state management
}
/**
* Unsupported in a subsession.
* @throws UnsupportedOperationException always
*/
@Override
public I2PSession addSubsession(InputStream destKeyStream, Properties opts) throws I2PSessionException {
throw new UnsupportedOperationException();
}
/**
* Unsupported in a subsession.
* Does nothing.
*/
@Override
public void removeSubsession(I2PSession session) {}
/**
* Unsupported in a subsession.
* @return empty list always
*/
@Override
public List<I2PSession> getSubsessions() {
return Collections.emptyList();
}
/**
* Does nothing for now
*/
@Override
public void updateOptions(Properties options) {}
/**
* Connect to the router and establish a session. This call blocks until
* a session is granted.
*
* Should be threadsafe, other threads will block until complete.
* Disconnect / destroy from another thread may be called simultaneously and
* will (should?) interrupt the connect.
*
* @throws I2PSessionException if there is a configuration error or the router is
* not reachable
*/
@Override
public void connect() throws I2PSessionException {
synchronized(_stateLock) {
if (_state != State.OPEN) {
changeState(State.OPENING);
}
}
boolean success = false;
try {
_primary.connect();
// wait until we have created a lease set
int waitcount = 0;
while (_leaseSet == null) {
if (waitcount++ > 5*60) {
throw new IOException("No tunnels built after waiting 5 minutes. Your network connection may be down, or there is severe network congestion.");
}
synchronized (_leaseSetWait) {
// InterruptedException caught below
_leaseSetWait.wait(1000);
}
}
synchronized(_stateLock) {
if (_state != State.OPEN) {
Thread notifier = new I2PAppThread(_availabilityNotifier, "ClientNotifier " + getPrefix(), true);
notifier.start();
changeState(State.OPEN);
}
}
success = true;
} catch (InterruptedException ie) {
throw new I2PSessionException("Interrupted", ie);
} catch (IOException ioe) {
throw new I2PSessionException(getPrefix() + "Cannot connect to the router on " + _hostname + ':' + _portNum, ioe);
} finally {
if (!success) {
_availabilityNotifier.stopNotifying();
changeState(State.CLOSED);
}
}
}
/**
* Has the session been closed (or not yet connected)?
* False when open and during transitions.
*/
@Override
public boolean isClosed() {
return super.isClosed() || _primary.isClosed();
}
/**
* Deliver an I2CP message to the router
* May block for several seconds if the write queue to the router is full
*
* @throws I2PSessionException if the message is malformed or there is an error writing it out
*/
@Override
void sendMessage(I2CPMessage message) throws I2PSessionException {
// workaround for now, as primary will send out our CreateSession
// from his connect, while we are still closed.
// If we did it in connect() we wouldn't need this
if (isClosed() &&
message.getType() != CreateSessionMessage.MESSAGE_TYPE &&
message.getType() != CreateLeaseSetMessage.MESSAGE_TYPE)
throw new I2PSessionException("Already closed");
_primary.sendMessage_unchecked(message);
}
/**
* Deliver an I2CP message to the router.
* Does NOT check state. Call only from connect() or other methods that need to
* send messages when not in OPEN state.
*
* @throws I2PSessionException if the message is malformed or there is an error writing it out
* @since 0.9.23
*/
@Override
void sendMessage_unchecked(I2CPMessage message) throws I2PSessionException {
_primary.sendMessage_unchecked(message);
}
/**
* Pass off the error to the listener
* Misspelled, oh well.
* @param error non-null
*/
@Override
void propogateError(String msg, Throwable error) {
_primary.propogateError(msg, error);
if (_sessionListener != null) _sessionListener.errorOccurred(this, msg, error);
}
/**
* Tear down the session, and do NOT reconnect.
*
* Blocks if session has not been fully started.
*/
@Override
public void destroySession() {
_primary.destroySession();
if (_availabilityNotifier != null)
_availabilityNotifier.stopNotifying();
if (_sessionListener != null) _sessionListener.disconnected(this);
changeState(State.CLOSED);
}
/**
* Will interrupt a connect in progress.
*/
@Override
protected void disconnect() {
_primary.disconnect();
}
@Override
protected boolean reconnect() {
return _primary.reconnect();
}
/**
* Called by the message handler
* on reception of DestReplyMessage
*
* This will never happen, as the dest reply message does not contain a session ID.
*/
@Override
void destReceived(Destination d) {
_primary.destReceived(d);
}
/**
* Called by the message handler
* on reception of DestReplyMessage
*
* This will never happen, as the dest reply message does not contain a session ID.
*
* @param h non-null
*/
@Override
void destLookupFailed(Hash h) {
_primary.destLookupFailed(h);
}
/**
* Called by the message handler
* on reception of HostReplyMessage
* @param d non-null
*/
void destReceived(long nonce, Destination d) {
_primary.destReceived(nonce, d);
}
/**
* Called by the message handler
* on reception of HostReplyMessage
*/
@Override
void destLookupFailed(long nonce) {
_primary.destLookupFailed(nonce);
}
/**
* Called by the message handler.
* This will never happen, as the bw limits message does not contain a session ID.
*/
@Override
void bwReceived(int[] i) {
_primary.bwReceived(i);
}
/**
* 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
*/
@Override
public Destination lookupDest(Hash h) throws I2PSessionException {
return _primary.lookupDest(h);
}
/**
* Blocking.
* @param maxWait ms
* @return null on failure
*/
@Override
public Destination lookupDest(Hash h, long maxWait) throws I2PSessionException {
return _primary.lookupDest(h, maxWait);
}
/**
* Ask the router to lookup a Destination by host name.
* Blocking. Waits a max of 10 seconds by default.
*
* This only makes sense for a b32 hostname, OR outside router context.
* Inside router context, just query the naming service.
* Outside router context, this does NOT query the context naming service.
* Do that first if you expect a local addressbook.
*
* This will log a warning for non-b32 in router context.
*
* See interface for suggested implementation.
*
* Requires router side to be 0.9.11 or higher. If the router is older,
* this will return null immediately.
*/
@Override
public Destination lookupDest(String name) throws I2PSessionException {
return _primary.lookupDest(name);
}
/**
* Ask the router to lookup a Destination by host name.
* Blocking. See above for details.
* @param maxWait ms
* @return null on failure
*/
@Override
public Destination lookupDest(String name, long maxWait) throws I2PSessionException {
return _primary.lookupDest(name, maxWait);
}
/**
* This won't be called, as the reply does not contain a session ID, so
* it won't be routed back to us
*/
@Override
public int[] bandwidthLimits() throws I2PSessionException {
return _primary.bandwidthLimits();
}
@Override
protected void updateActivity() {
_primary.updateActivity();
}
@Override
public long lastActivity() {
return _primary.lastActivity();
}
@Override
public void setReduced() {
_primary.setReduced();
}
}

View File

@ -0,0 +1,11 @@
<html><body>
<p>Implements the base I2P SDK for developing applications that communicate
through I2P.</p>
<p>
These classes are for implementing the client side of I2CP
and are not to be used externally.
Subject to change and not part of the public API.
Moved from net.i2p.client in 0.9.21.
</p>
</body></html>

View File

@ -621,11 +621,33 @@ public class BlockfileNamingService extends DummyNamingService {
////////// Start NamingService API
/*
*
* Will strip a "www." prefix and retry if lookup fails
*
* @param hostname upper/lower case ok
* @param options If non-null and contains the key "list", lookup in
* that list only, otherwise all lists
*/
@Override
public Destination lookup(String hostname, Properties lookupOptions, Properties storedOptions) {
Destination rv = lookup2(hostname, lookupOptions, storedOptions);
if (rv == null) {
// if hostname starts with "www.", strip and try again
// but not for www.i2p
hostname = hostname.toLowerCase(Locale.US);
if (hostname.startsWith("www.") && hostname.length() > 7) {
hostname = hostname.substring(4);
rv = lookup2(hostname, lookupOptions, storedOptions);
}
}
return rv;
}
/*
* @param options If non-null and contains the key "list", lookup in
* that list only, otherwise all lists
*/
private Destination lookup2(String hostname, Properties lookupOptions, Properties storedOptions) {
String listname = null;
if (lookupOptions != null)
listname = lookupOptions.getProperty("list");

View File

@ -41,8 +41,8 @@ public class MetaNamingService extends DummyNamingService {
while (tok.hasMoreTokens()) {
try {
Class<?> cls = Class.forName(tok.nextToken());
Constructor<?> con = cls.getConstructor(new Class[] { I2PAppContext.class });
addNamingService((NamingService)con.newInstance(new Object[] { context }), false);
Constructor<?> con = cls.getConstructor(I2PAppContext.class);
addNamingService((NamingService)con.newInstance(), false);
} catch (Exception ex) {
}
}

View File

@ -536,8 +536,8 @@ public abstract class NamingService {
String impl = context.getProperty(PROP_IMPL, DEFAULT_IMPL);
try {
Class<?> cls = Class.forName(impl);
Constructor<?> con = cls.getConstructor(new Class[] { I2PAppContext.class });
instance = (NamingService)con.newInstance(new Object[] { context });
Constructor<?> con = cls.getConstructor(I2PAppContext.class);
instance = (NamingService)con.newInstance(context);
} catch (Exception ex) {
Log log = context.logManager().getLog(NamingService.class);
// Blockfile may throw RuntimeException but HostsTxt won't

View File

@ -28,6 +28,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.util.FileUtil;
import net.i2p.util.Log;
@ -76,6 +77,8 @@ public class SingleFileNamingService extends NamingService {
}
/**
* Will strip a "www." prefix and retry if lookup fails
*
* @param hostname case-sensitive; caller should convert to lower case
* @param lookupOptions ignored
* @param storedOptions ignored
@ -84,6 +87,8 @@ public class SingleFileNamingService extends NamingService {
public Destination lookup(String hostname, Properties lookupOptions, Properties storedOptions) {
try {
String key = getKey(hostname);
if (key == null && hostname.startsWith("www.") && hostname.length() > 7)
key = getKey(hostname.substring(4));
if (key != null)
return lookupBase64(key);
} catch (Exception ioe) {
@ -235,7 +240,7 @@ public class SingleFileNamingService extends NamingService {
// FIXME fails if previous last line didn't have a trailing \n
out.write(hostname.getBytes("UTF-8"));
out.write('=');
out.write(d.toBase64().getBytes());
out.write(DataHelper.getASCII(d.toBase64()));
out.write('\n');
out.close();
for (NamingServiceListener nsl : _listeners) {

View File

@ -1,6 +1,13 @@
<html><body>
<p>Implements the base I2P SDK for developing applications that communicate
through I2P.</p>
<p>
Interfaces and factories for
the base I2P SDK used to develop applications that communicate
through I2P.
</p>
<p>
Implementation moved to net.i2p.client.impl in 0.9.21.
</p>
<p>When a client application wants to communicate over I2P, the first thing it
needs to do is get a {@link net.i2p.client.I2PClient} from the

View File

@ -3,6 +3,7 @@ package net.i2p.crypto;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
@ -45,7 +46,7 @@ public class CertUtil {
// Get the encoded form which is suitable for exporting
byte[] buf = cert.getEncoded();
os = new SecureFileOutputStream(file);
wr = new PrintWriter(os);
wr = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
wr.println("-----BEGIN CERTIFICATE-----");
String b64 = Base64.encode(buf, true); // true = use standard alphabet
for (int i = 0; i < b64.length(); i += LINE_LENGTH) {

View File

@ -44,28 +44,7 @@ public class CryptixAESEngine extends AESEngine {
/** see test results below */
private static final int MIN_SYSTEM_AES_LENGTH = 704;
private static final boolean USE_SYSTEM_AES;
static {
boolean systemOK = false;
if (hasAESNI()) {
try {
systemOK = Cipher.getMaxAllowedKeyLength("AES") >= 256;
} catch (GeneralSecurityException gse) {
// a NoSuchAlgorithmException
} catch (NoSuchMethodError nsme) {
// JamVM, gij
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec key = new SecretKeySpec(new byte[32], "AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
systemOK = true;
} catch (GeneralSecurityException gse) {
}
}
}
USE_SYSTEM_AES = systemOK;
//System.out.println("Using system AES? " + systemOK);
}
private static final boolean USE_SYSTEM_AES = hasAESNI() && CryptoCheck.isUnlimited();
/**
* Do we have AES-NI support in the processor and JVM?

View File

@ -0,0 +1,47 @@
package net.i2p.crypto;
import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
/**
* Moved from CryptixAESEngine and net.i2p.router.tasks.CryptoChecker
*
* @since 0.9.23
*/
public class CryptoCheck {
private static final boolean _isUnlimited;
static {
boolean unlimited = false;
try {
unlimited = Cipher.getMaxAllowedKeyLength("AES") >= 256;
} catch (GeneralSecurityException gse) {
// a NoSuchAlgorithmException
} catch (NoSuchMethodError nsme) {
// JamVM, gij
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec key = new SecretKeySpec(new byte[32], "AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
unlimited = true;
} catch (GeneralSecurityException gse) {
}
}
_isUnlimited = unlimited;
}
private CryptoCheck() {}
/**
* Do we have unlimited crypto?
*/
public static boolean isUnlimited() {
return _isUnlimited;
}
public static void main(String args[]) {
System.out.println("Unlimited? " + isUnlimited());
}
}

View File

@ -92,8 +92,8 @@ public class CryptoConstants {
if (ECConstants.isBCAvailable()) {
try {
Class<?> cls = Class.forName("org.bouncycastle.jce.spec.ElGamalParameterSpec");
Constructor<?> con = cls.getConstructor(new Class[] {BigInteger.class, BigInteger.class});
spec = (AlgorithmParameterSpec)con.newInstance(new Object[] {elgp, elgg});
Constructor<?> con = cls.getConstructor(BigInteger.class, BigInteger.class);
spec = (AlgorithmParameterSpec)con.newInstance(elgp, elgg);
//System.out.println("BC ElG spec loaded");
} catch (Exception e) {
//System.out.println("BC ElG spec failed");

View File

@ -285,8 +285,8 @@ public class DSAEngine {
try {
return altSign(data, offset, length, signingKey);
} catch (GeneralSecurityException gse) {
if (_log.shouldLog(Log.WARN))
_log.warn(type + " Sign Fail", gse);
if (_log.shouldLog(Log.ERROR))
_log.error(type + " Sign Fail", gse);
return null;
}
}

View File

@ -42,8 +42,8 @@ class ECConstants {
if (Security.getProvider("BC") == null) {
try {
Class<?> cls = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
Constructor<?> con = cls.getConstructor(new Class[0]);
Provider bc = (Provider)con.newInstance(new Object[0]);
Constructor<?> con = cls.getConstructor();
Provider bc = (Provider)con.newInstance();
Security.addProvider(bc);
log("Added BC provider");
loaded = true;

View File

@ -417,6 +417,8 @@ public class KeyGenerator {
RandomSource.getInstance().nextBytes(src);
long start = System.nanoTime();
Signature sig = DSAEngine.getInstance().sign(src, privkey);
if (sig == null)
throw new GeneralSecurityException("signature generation failed");
long mid = System.nanoTime();
boolean ok = DSAEngine.getInstance().verifySignature(sig, src, pubkey);
long end = System.nanoTime();

View File

@ -501,6 +501,7 @@ public class KeyStoreUtil {
l.log(level, msg, t);
}
/****
public static void main(String[] args) {
try {
if (args.length > 0) {
@ -521,4 +522,5 @@ public class KeyStoreUtil {
e.printStackTrace();
}
}
****/
}

View File

@ -540,9 +540,10 @@ public class SU3File {
String ctype = null;
String ftype = null;
String kfile = null;
String kspass = KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD;
boolean error = false;
boolean shouldVerify = true;
Getopt g = new Getopt("SU3File", args, "t:c:f:k:x");
Getopt g = new Getopt("SU3File", args, "t:c:f:k:xp:");
int c;
while ((c = g.getopt()) != -1) {
switch (c) {
@ -566,6 +567,10 @@ public class SU3File {
shouldVerify = false;
break;
case 'p':
kspass = g.getOptarg();
break;
case '?':
case ':':
default:
@ -586,16 +591,16 @@ public class SU3File {
Properties props = new Properties();
props.setProperty("prng.bufferSize", "16384");
new I2PAppContext(props);
ok = signCLI(stype, ctype, ftype, a.get(0), a.get(1), a.get(2), a.get(3), a.get(4), "");
ok = signCLI(stype, ctype, ftype, a.get(0), a.get(1), a.get(2), a.get(3), a.get(4), "", kspass);
} else if ("bulksign".equals(cmd)) {
Properties props = new Properties();
props.setProperty("prng.bufferSize", "16384");
new I2PAppContext(props);
ok = bulkSignCLI(stype, ctype, a.get(0), a.get(1), a.get(2), a.get(3));
ok = bulkSignCLI(stype, ctype, a.get(0), a.get(1), a.get(2), a.get(3), kspass);
} else if ("verifysig".equals(cmd)) {
ok = verifySigCLI(a.get(0), kfile);
} else if ("keygen".equals(cmd)) {
ok = genKeysCLI(stype, a.get(0), a.get(1), a.get(2));
ok = genKeysCLI(stype, a.get(0), a.get(1), a.get(2), kspass);
} else if ("extract".equals(cmd)) {
ok = extractCLI(a.get(0), a.get(1), shouldVerify, kfile);
} else {
@ -611,12 +616,13 @@ public class SU3File {
}
private static final void showUsageCLI() {
System.err.println("Usage: SU3File keygen [-t type|code] publicKeyFile keystore.ks you@mail.i2p");
System.err.println(" SU3File sign [-t type|code] [-c type|code] [-f type|code] inputFile.zip signedFile.su3 keystore.ks version you@mail.i2p");
System.err.println(" SU3File bulksign [-t type|code] [-c type|code] directory keystore.ks version you@mail.i2p");
System.err.println(" SU3File showversion signedFile.su3");
System.err.println(" SU3File verifysig [-k file.crt] signedFile.su3 ## -k use this pubkey cert for verification");
System.err.println(" SU3File extract [-x] [-k file.crt] signedFile.su3 outFile ## -x don't check sig");
System.err.println("Usage: SU3File keygen [-t type|code] [-p keystorepw] publicKeyFile keystore.ks you@mail.i2p\n" +
" SU3File sign [-t type|code] [-c type|code] [-f type|code] [-p keystorepw] inputFile.zip signedFile.su3 keystore.ks version you@mail.i2p\n" +
" SU3File bulksign [-t type|code] [-c type|code] [-p keystorepw] directory keystore.ks version you@mail.i2p\n" +
" SU3File showversion signedFile.su3\n" +
" SU3File verifysig [-k file.crt] signedFile.su3 ## -k use this pubkey cert for verification\n" +
" SU3File extract [-x] [-k file.crt] signedFile.su3 outFile ## -x don't check sig");
System.err.println("Default keystore password: \"" + KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD + '"');
System.err.println(dumpTypes());
}
@ -714,7 +720,7 @@ public class SU3File {
* @since 0.9.9
*/
private static final boolean bulkSignCLI(String stype, String ctype, String dir,
String privateKeyFile, String version, String signerName) {
String privateKeyFile, String version, String signerName, String kspass) {
File d = new File(dir);
if (!d.isDirectory()) {
System.out.println("Directory does not exist: " + d);
@ -749,7 +755,8 @@ public class SU3File {
if (!inputFile.endsWith(".zip"))
continue;
String signedFile = inputFile.substring(0, inputFile.length() - 4) + ".su3";
boolean rv = signCLI(stype, ctype, null, inputFile, signedFile, privateKeyFile, version, signerName, keypw);
boolean rv = signCLI(stype, ctype, null, inputFile, signedFile,
privateKeyFile, version, signerName, keypw, kspass);
if (!rv)
return false;
success++;
@ -764,7 +771,7 @@ public class SU3File {
* @since 0.9.9
*/
private static final boolean signCLI(String stype, String ctype, String ftype, String inputFile, String signedFile,
String privateKeyFile, String version, String signerName, String keypw) {
String privateKeyFile, String version, String signerName, String keypw, String kspass) {
SigType type = stype == null ? SigType.getByCode(Integer.valueOf(DEFAULT_SIG_CODE)) : SigType.parseSigType(stype);
if (type == null) {
System.out.println("Signature type " + stype + " is not supported");
@ -799,7 +806,7 @@ public class SU3File {
System.out.println("Warning: File type " + ftype + " is undefined");
}
}
return signCLI(type, ct, ft, inputFile, signedFile, privateKeyFile, version, signerName, keypw);
return signCLI(type, ct, ft, inputFile, signedFile, privateKeyFile, version, signerName, keypw, kspass);
}
/**
@ -807,7 +814,7 @@ public class SU3File {
* @since 0.9.9
*/
private static final boolean signCLI(SigType type, ContentType ctype, int ftype, String inputFile, String signedFile,
String privateKeyFile, String version, String signerName, String keypw) {
String privateKeyFile, String version, String signerName, String keypw, String kspass) {
try {
while (keypw.length() < 6) {
System.out.print("Enter password for key \"" + signerName + "\": ");
@ -821,7 +828,7 @@ public class SU3File {
System.out.println("Key password must be at least 6 characters");
}
File pkfile = new File(privateKeyFile);
PrivateKey pk = KeyStoreUtil.getPrivateKey(pkfile,KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD, signerName, keypw);
PrivateKey pk = KeyStoreUtil.getPrivateKey(pkfile, kspass, signerName, keypw);
if (pk == null) {
System.out.println("Private key for " + signerName + " not found in keystore " + privateKeyFile);
return false;
@ -895,13 +902,14 @@ public class SU3File {
* @return success
* @since 0.9.9
*/
private static final boolean genKeysCLI(String stype, String publicKeyFile, String privateKeyFile, String alias) {
private static final boolean genKeysCLI(String stype, String publicKeyFile, String privateKeyFile,
String alias, String kspass) {
SigType type = stype == null ? SigType.getByCode(Integer.valueOf(DEFAULT_SIG_CODE)) : SigType.parseSigType(stype);
if (type == null) {
System.out.println("Signature type " + stype + " is not supported");
return false;
}
return genKeysCLI(type, publicKeyFile, privateKeyFile, alias);
return genKeysCLI(type, publicKeyFile, privateKeyFile, alias, kspass);
}
/**
@ -909,7 +917,8 @@ public class SU3File {
* @return success
* @since 0.9.9
*/
private static final boolean genKeysCLI(SigType type, String publicKeyFile, String privateKeyFile, String alias) {
private static final boolean genKeysCLI(SigType type, String publicKeyFile, String privateKeyFile,
String alias, String kspass) {
File pubFile = new File(publicKeyFile);
if (pubFile.exists()) {
System.out.println("Error: Not overwriting file " + publicKeyFile);
@ -947,7 +956,7 @@ public class SU3File {
if (keylen == 528)
keylen = 521;
}
boolean success = KeyStoreUtil.createKeys(ksFile, KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD, alias,
boolean success = KeyStoreUtil.createKeys(ksFile, kspass, alias,
alias, "I2P", 3652, type.getBaseAlgorithm().getName(),
keylen, keypw);
if (!success) {

View File

@ -11,7 +11,9 @@ import java.util.Map;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.data.Hash;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SimpleDataStructure;
import net.i2p.util.SystemVersion;
/**
* Defines the properties for various signature types
@ -193,8 +195,24 @@ public enum SigType {
return true;
try {
getParams();
if (getBaseAlgorithm() != SigAlgo.EdDSA)
Signature.getInstance(getAlgorithmName());
if (getBaseAlgorithm() != SigAlgo.EdDSA) {
Signature jsig = Signature.getInstance(getAlgorithmName());
if (getBaseAlgorithm() == SigAlgo.EC && SystemVersion.isGentoo() ) {
// Do a full keygen/sign test on Gentoo, because it lies. Keygen works but sigs fail.
// https://bugs.gentoo.org/show_bug.cgi?id=528338
// http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=2497
// http://zzz.i2p/topics/1931
// Be sure nothing in the code paths below calls isAvailable()
// get an I2P keypair
SimpleDataStructure[] keys = KeyGenerator.getInstance().generateSigningKeys(this);
SigningPrivateKey privKey = (SigningPrivateKey) keys[1];
// convert privkey back to Java key and sign
jsig.initSign(SigUtil.toJavaECKey(privKey));
// use the pubkey as random data
jsig.update(keys[0].getData());
jsig.sign();
}
}
getDigestInstance();
getHashInstance();
} catch (Exception e) {

View File

@ -91,7 +91,7 @@ class YKGenerator {
return;
_precalcThread = new I2PThread(new YKPrecalcRunner(MIN_NUM_BUILDERS, MAX_NUM_BUILDERS),
"YK Precalc", true);
_precalcThread.setPriority(Thread.MIN_PRIORITY);
_precalcThread.setPriority(Thread.NORM_PRIORITY - 2);
_isRunning = true;
_precalcThread.start();
}

View File

@ -15,12 +15,12 @@ import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
*/
public class EdDSAPrivateKey implements EdDSAKey, PrivateKey {
private static final long serialVersionUID = 23495873459878957L;
private transient final byte[] seed;
private transient final byte[] h;
private transient final byte[] a;
private transient final GroupElement A;
private transient final byte[] Abyte;
private transient final EdDSAParameterSpec edDsaSpec;
private final byte[] seed;
private final byte[] h;
private final byte[] a;
private final GroupElement A;
private final byte[] Abyte;
private final EdDSAParameterSpec edDsaSpec;
public EdDSAPrivateKey(EdDSAPrivateKeySpec spec) {
this.seed = spec.getSeed();

View File

@ -1,11 +1,15 @@
package net.i2p.crypto.eddsa.math;
import java.io.Serializable;
/**
*
* @since 0.9.15
*
*/
public abstract class FieldElement {
public abstract class FieldElement implements Serializable {
private static final long serialVersionUID = 1239527465875676L;
protected final Field f;
public FieldElement(Field f) {

View File

@ -101,8 +101,8 @@ public class Base32 {
}
private static byte[] read(InputStream in) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
byte buf[] = new byte[4096];
ByteArrayOutputStream baos = new ByteArrayOutputStream(64);
byte buf[] = new byte[64];
while (true) {
int read = in.read(buf);
if (read < 0) break;
@ -118,7 +118,7 @@ public class Base32 {
}
private static void decode(InputStream in, OutputStream out) throws IOException {
byte decoded[] = decode(new String(read(in)));
byte decoded[] = decode(DataHelper.getUTF8(read(in)));
if (decoded == null) {
System.out.println("FAIL");
return;
@ -199,7 +199,7 @@ public class Base32 {
byte[] b = decode(s);
if (b == null)
return null;
return new String(b);
return DataHelper.getUTF8(b);
}
/**

View File

@ -246,8 +246,8 @@ public class Base64 {
}
private static byte[] read(InputStream in) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
byte buf[] = new byte[4096];
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
byte buf[] = new byte[1024];
while (true) {
int read = in.read(buf);
if (read < 0) break;
@ -263,7 +263,7 @@ public class Base64 {
}
private static void decode(InputStream in, OutputStream out) throws IOException {
byte decoded[] = decode(new String(read(in)));
byte decoded[] = decode(DataHelper.getUTF8(read(in)));
if (decoded == null)
throw new IOException("Invalid base 64 string");
out.write(decoded);

View File

@ -16,7 +16,7 @@ import java.io.Serializable;
* maps, and the like.
*
*/
public class ByteArray implements Serializable, Comparable {
public class ByteArray implements Serializable, Comparable<ByteArray> {
private byte[] _data;
private int _valid;
private int _offset;
@ -85,9 +85,8 @@ public class ByteArray implements Serializable, Comparable {
return (llen == rlen) && DataHelper.eq(lhs, loff, rhs, roff, llen);
}
public final int compareTo(Object obj) {
if (obj.getClass() != getClass()) throw new ClassCastException("invalid object: " + obj);
return DataHelper.compareTo(_data, ((ByteArray)obj).getData());
public final int compareTo(ByteArray ba) {
return DataHelper.compareTo(_data, ba.getData());
}
@Override

View File

@ -47,21 +47,34 @@ public class Certificate extends DataStructureImpl {
public final static int CERTIFICATE_TYPE_KEY = 5;
/**
* If null cert, return immutable static instance, else create new
* @throws AIOOBE if not enough bytes, FIXME should throw DataFormatException
* If null, P256 key, or Ed25519 key cert, return immutable static instance, else create new
* @throws DataFormatException if not enough bytes
* @since 0.8.3
*/
public static Certificate create(byte[] data, int off) {
int type = data[off] & 0xff;
int length = (int) DataHelper.fromLong(data, off + 1, 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];
System.arraycopy(data, off + 3, payload, 0, length);
public static Certificate create(byte[] data, int off) throws DataFormatException {
int type;
byte[] payload;
int length;
try {
type = data[off] & 0xff;
length = (int) DataHelper.fromLong(data, off + 1, 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);
payload = new byte[length];
System.arraycopy(data, off + 3, payload, 0, length);
} catch (ArrayIndexOutOfBoundsException aioobe) {
throw new DataFormatException("not enough bytes", aioobe);
}
if (type == CERTIFICATE_TYPE_KEY) {
if (length == 4) {
if (Arrays.equals(payload, KeyCertificate.Ed25519_PAYLOAD))
return KeyCertificate.ELG_Ed25519_CERT;
if (Arrays.equals(payload, KeyCertificate.ECDSA256_PAYLOAD))
return KeyCertificate.ELG_ECDSA256_CERT;
}
try {
return new KeyCertificate(payload);
} catch (DataFormatException dfe) {
@ -72,7 +85,7 @@ public class Certificate extends DataStructureImpl {
}
/**
* If null cert, return immutable static instance, else create new
* If null, P256 key, or Ed25519 key cert, return immutable static instance, else create new
* @since 0.8.3
*/
public static Certificate create(InputStream in) throws DataFormatException, IOException {
@ -87,8 +100,15 @@ public class Certificate extends DataStructureImpl {
int read = DataHelper.read(in, payload);
if (read != length)
throw new DataFormatException("Not enough bytes for the payload (read: " + read + " length: " + length + ')');
if (type == CERTIFICATE_TYPE_KEY)
if (type == CERTIFICATE_TYPE_KEY) {
if (length == 4) {
if (Arrays.equals(payload, KeyCertificate.Ed25519_PAYLOAD))
return KeyCertificate.ELG_Ed25519_CERT;
if (Arrays.equals(payload, KeyCertificate.ECDSA256_PAYLOAD))
return KeyCertificate.ELG_ECDSA256_CERT;
}
return new KeyCertificate(payload);
}
return new Certificate(type, payload);
}
@ -262,7 +282,7 @@ public class Certificate extends DataStructureImpl {
} else {
buf.append(" payload size: ").append(_payload.length);
if (getCertificateType() == CERTIFICATE_TYPE_HASHCASH) {
buf.append(" Stamp: ").append(new String(_payload));
buf.append(" Stamp: ").append(DataHelper.getUTF8(_payload));
} else if (getCertificateType() == CERTIFICATE_TYPE_SIGNED && _payload.length == CERTIFICATE_LENGTH_SIGNED_WITH_HASH) {
buf.append(" Signed by hash: ").append(Base64.encode(_payload, Signature.SIGNATURE_BYTES, Hash.HASH_LENGTH));
} else {

View File

@ -1548,7 +1548,7 @@ public class DataHelper {
// years
t = ngettext("1 year", "{0} years", (int) (ms / (365L * 24 * 60 * 60 * 1000)));
} else {
return _("n/a");
return _t("n/a");
}
// Replace minus sign to work around
// bug in Chrome (and IE?), line breaks at the minus sign
@ -1595,7 +1595,7 @@ public class DataHelper {
// years
t = ngettext("1 year", "{0} years", (int) (ms / (365L * 24 * 60 * 60 * 1000)));
} else {
return _("n/a");
return _t("n/a");
}
if (ms < 0)
t = t.replace("-", "&minus;");
@ -1604,7 +1604,7 @@ public class DataHelper {
private static final String BUNDLE_NAME = "net.i2p.router.web.messages";
private static String _(String key) {
private static String _t(String key) {
return Translate.getString(key, I2PAppContext.getGlobalContext(), BUNDLE_NAME);
}
@ -1695,7 +1695,7 @@ public class DataHelper {
if (unescaped == null) return null;
String escaped = unescaped;
for (int i = 0; i < escapeChars.length; i++) {
escaped = escaped.replaceAll(escapeChars[i], escapeCodes[i]);
escaped = escaped.replace(escapeChars[i], escapeCodes[i]);
}
return escaped;
}
@ -1710,7 +1710,7 @@ public class DataHelper {
if (escaped == null) return null;
String unescaped = escaped;
for (int i = 0; i < escapeChars.length; i++) {
unescaped = unescaped.replaceAll(escapeCodes[i], escapeChars[i]);
unescaped = unescaped.replace(escapeCodes[i], escapeChars[i]);
}
return unescaped;
}
@ -1866,7 +1866,6 @@ public class DataHelper {
*
* @return null if orig is null
* @throws RuntimeException
* @deprecated unused
*/
public static String getUTF8(byte orig[], int offset, int len) {
if (orig == null) return null;

View File

@ -171,8 +171,12 @@ public abstract class DatabaseEntry extends DataStructureImpl {
throw new IllegalStateException();
byte[] bytes = getBytes();
if (bytes == null) throw new DataFormatException("Not enough data to sign");
if (key == null)
throw new DataFormatException("No signing key");
// now sign with the key
_signature = DSAEngine.getInstance().sign(bytes, key);
if (_signature == null)
throw new DataFormatException("Signature failed with " + key.getType() + " key");
}
/**

View File

@ -17,15 +17,41 @@ public class KeyCertificate extends Certificate {
public static final int HEADER_LENGTH = 4;
/** @since 0.9.22 pkg private for Certificate.create() */
static final byte[] Ed25519_PAYLOAD = new byte[] {
0, (byte) (SigType.EdDSA_SHA512_Ed25519.getCode()), 0, 0
};
/** @since 0.9.22 pkg private for Certificate.create() */
static final byte[] ECDSA256_PAYLOAD = new byte[] {
0, (byte) (SigType.ECDSA_SHA256_P256.getCode()), 0, 0
};
/**
* An immutable ElG/ECDSA-P256 certificate.
*/
public static final KeyCertificate ELG_ECDSA256_CERT;
/**
* An immutable ElG/Ed25519 certificate.
* @since 0.9.22
*/
public static final KeyCertificate ELG_Ed25519_CERT;
static {
KeyCertificate kc;
try {
kc = new ECDSA256Cert();
} catch (DataFormatException dfe) {
kc = null; // won't happen
throw new RuntimeException(dfe); // won't happen
}
ELG_ECDSA256_CERT = kc;
try {
kc = new Ed25519Cert();
} catch (DataFormatException dfe) {
throw new RuntimeException(dfe); // won't happen
}
ELG_Ed25519_CERT = kc;
}
/**
@ -122,7 +148,7 @@ public class KeyCertificate extends Certificate {
/**
* Signing Key extra data, if any, is first in the array.
* Crypto Key extra data, if any, is second in the array,
* at offset max(0, 128 - getSigType().getPubkeyLen()
* at offset max(0, getSigType().getPubkeyLen() - 128)
*
* @return null if unset or none
*/
@ -148,7 +174,7 @@ public class KeyCertificate extends Certificate {
SigType type = getSigType();
if (type == null)
throw new UnsupportedOperationException("unknown sig type");
int extra = 128 - type.getPubkeyLen();
int extra = Math.max(0, type.getPubkeyLen() - 128);
if (_payload.length == HEADER_LENGTH + extra)
return getExtraKeyData();
byte[] rv = new byte[extra];
@ -185,19 +211,17 @@ public class KeyCertificate extends Certificate {
/**
* An immutable ElG/ECDSA-256 certificate.
* @since 0.8.3
*/
private static final class ECDSA256Cert extends KeyCertificate {
private static final byte[] ECDSA256_DATA = new byte[] {
CERTIFICATE_TYPE_KEY, 0, HEADER_LENGTH, 0, (byte) (SigType.ECDSA_SHA256_P256.getCode()), 0, 0
};
private static final int ECDSA256_LENGTH = ECDSA256_DATA.length;
private static final byte[] ECDSA256_PAYLOAD = new byte[] {
0, (byte) (SigType.ECDSA_SHA256_P256.getCode()), 0, 0
};
private final int _hashcode;
public ECDSA256Cert() throws DataFormatException {
super(ECDSA256_PAYLOAD);
_hashcode = super.hashCode();
}
/** @throws RuntimeException always */
@ -246,7 +270,75 @@ public class KeyCertificate extends Certificate {
/** Overridden for efficiency */
@Override
public int hashCode() {
return 1234567;
return _hashcode;
}
}
/**
* An immutable ElG/Ed25519 certificate.
* @since 0.9.22
*/
private static final class Ed25519Cert extends KeyCertificate {
private static final byte[] ED_DATA = new byte[] { CERTIFICATE_TYPE_KEY,
0, HEADER_LENGTH,
0, (byte) SigType.EdDSA_SHA512_Ed25519.getCode(),
0, 0
};
private static final int ED_LENGTH = ED_DATA.length;
private final int _hashcode;
public Ed25519Cert() throws DataFormatException {
super(Ed25519_PAYLOAD);
_hashcode = super.hashCode();
}
/** @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(ED_DATA);
}
/** Overridden for efficiency */
@Override
public int writeBytes(byte target[], int offset) {
System.arraycopy(ED_DATA, 0, target, offset, ED_LENGTH);
return ED_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 ED_LENGTH;
}
/** Overridden for efficiency */
@Override
public int hashCode() {
return _hashcode;
}
}
}

View File

@ -418,10 +418,10 @@ public class LeaseSet extends DatabaseEntry {
encryp(key);
} catch (DataFormatException dfe) {
Log log = I2PAppContext.getGlobalContext().logManager().getLog(LeaseSet.class);
log.error("Error encrypting lease: " + _destination.calculateHash());
log.error("Error encrypting lease: " + _destination.calculateHash(), dfe);
} catch (IOException ioe) {
Log log = I2PAppContext.getGlobalContext().logManager().getLog(LeaseSet.class);
log.error("Error encrypting lease: " + _destination.calculateHash());
log.error("Error encrypting lease: " + _destination.calculateHash(), ioe);
}
}
@ -520,7 +520,11 @@ public class LeaseSet extends DatabaseEntry {
private synchronized boolean isEncrypted() {
if (_decrypted)
return true;
if (_checked || _destination == null)
// If the encryption key is not set yet, it can't have been encrypted yet.
// Router-side I2CP sets the destination (but not the encryption key)
// on an unsigned LS which is pending signature (and possibly encryption)
// by the client, and we don't want to attempt 'decryption' on it.
if (_checked || _encryptionKey == null || _destination == null)
return false;
SessionKey key = I2PAppContext.getGlobalContext().keyRing().get(_destination.calculateHash());
if (key != null) {
@ -529,10 +533,10 @@ public class LeaseSet extends DatabaseEntry {
_decrypted = true;
} catch (DataFormatException dfe) {
Log log = I2PAppContext.getGlobalContext().logManager().getLog(LeaseSet.class);
log.error("Error decrypting lease: " + _destination.calculateHash() + dfe);
log.error("Error decrypting lease: " + _destination.calculateHash(), dfe);
} catch (IOException ioe) {
Log log = I2PAppContext.getGlobalContext().logManager().getLog(LeaseSet.class);
log.error("Error decrypting lease: " + _destination.calculateHash() + ioe);
log.error("Error decrypting lease: " + _destination.calculateHash(), ioe);
}
}
_checked = true;

View File

@ -405,7 +405,10 @@ public class PrivateKeyFile {
System.arraycopy(this.dest.getPublicKey().getData(), 0, data, 0, PublicKey.KEYSIZE_BYTES);
System.arraycopy(this.dest.getSigningPublicKey().getData(), 0, data, PublicKey.KEYSIZE_BYTES, SigningPublicKey.KEYSIZE_BYTES);
byte[] payload = new byte[Hash.HASH_LENGTH + Signature.SIGNATURE_BYTES];
byte[] sig = DSAEngine.getInstance().sign(new ByteArrayInputStream(data), spk2).getData();
Signature sign = DSAEngine.getInstance().sign(new ByteArrayInputStream(data), spk2);
if (sign == null)
return null;
byte[] sig = sign.getData();
System.arraycopy(sig, 0, payload, 0, Signature.SIGNATURE_BYTES);
// Add dest2's Hash for reference
byte[] h2 = d2.calculateHash().getData();

View File

@ -46,7 +46,6 @@ import net.i2p.util.SystemVersion;
public class SDSCache<V extends SimpleDataStructure> {
//private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(SDSCache.class);
private static final Class[] conArg = new Class[] { byte[].class };
private static final double MIN_FACTOR = 0.20;
private static final double MAX_FACTOR = 5.0;
private static final double FACTOR;
@ -74,7 +73,7 @@ public class SDSCache<V extends SimpleDataStructure> {
_cache = new LHMCache<Integer, WeakReference<V>>(size);
_datalen = len;
try {
_rvCon = rvClass.getConstructor(conArg);
_rvCon = rvClass.getConstructor(byte[].class);
} catch (NoSuchMethodException e) {
throw new RuntimeException("SDSCache init error", e);
}

View File

@ -87,7 +87,7 @@ public class VerifiedDestination extends Destination {
* zeros and see if it meets our minimum effort.
*/
protected boolean verifyHashCashCert() {
String hcs = new String(_certificate.getPayload());
String hcs = DataHelper.getUTF8(_certificate.getPayload());
int end1 = 0;
for (int i = 0; i < 3; i++) {
end1 = 1 + hcs.indexOf(':', end1);

View File

@ -38,6 +38,16 @@ public class CreateLeaseSetMessage extends I2CPMessageImpl {
return _sessionId;
}
/**
* Return the SessionId for this message.
*
* @since 0.9.21
*/
@Override
public SessionId sessionId() {
return _sessionId;
}
public void setSessionId(SessionId id) {
_sessionId = id;
}

View File

@ -32,6 +32,16 @@ public class DestroySessionMessage extends I2CPMessageImpl {
return _sessionId;
}
/**
* Return the SessionId for this message.
*
* @since 0.9.21
*/
@Override
public SessionId sessionId() {
return _sessionId;
}
public void setSessionId(SessionId id) {
_sessionId = id;
}

View File

@ -76,6 +76,16 @@ public class HostLookupMessage extends I2CPMessageImpl {
return _sessionId;
}
/**
* Return the SessionId for this message.
*
* @since 0.9.21
*/
@Override
public SessionId sessionId() {
return _sessionId;
}
/**
* @return 0 to 2**32 - 1
*/

View File

@ -73,6 +73,16 @@ public class HostReplyMessage extends I2CPMessageImpl {
return _sessionId;
}
/**
* Return the SessionId for this message.
*
* @since 0.9.21
*/
@Override
public SessionId sessionId() {
return _sessionId;
}
/**
* @return 0 to 2**32 - 1
*/

View File

@ -60,9 +60,20 @@ public interface I2CPMessage extends DataStructure {
public void writeMessage(OutputStream out) throws I2CPMessageException, IOException;
/**
* Return the unique identifier for this type of APIMessage, as specified in the
* Return the unique identifier for this type of message, as specified in the
* network specification document under #ClientAccessLayerMessages
* @return unique identifier for this type of APIMessage
* @return unique identifier for this type of message
*/
public int getType();
}
/**
* Return the SessionId for this type of message.
* Most but not all message types include a SessionId.
* The ones that do already define getSessionId(), but some return a SessionId and
* some return a long, so we define a new method here.
*
* @return SessionId or null if this message type does not include a SessionId
* @since 0.9.21
*/
public SessionId sessionId();
}

View File

@ -12,7 +12,7 @@ package net.i2p.data.i2cp;
import net.i2p.I2PException;
/**
* Represent an error serializing or deserializing an APIMessage
* Represent an error serializing or deserializing a message
*
* @author jrandom
*/

View File

@ -127,4 +127,15 @@ public abstract class I2CPMessageImpl extends DataStructureImpl implements I2CPM
throw new DataFormatException("Error writing the message", ime);
}
}
/**
* Return the SessionId for this type of message.
* Most but not all message types include a SessionId.
* The ones that do already define getSessionId(), but some return a SessionId and
* some return a long, so we define a new method here.
*
* @return null always. Extending classes with a SessionId must override.
* @since 0.9.21
*/
public SessionId sessionId() { return null; }
}

View File

@ -158,6 +158,20 @@ public class I2CPMessageReader {
}
public void run() {
try {
run2();
} catch (Exception e) {
_log.log(Log.CRIT, "Uncaught I2CP error", e);
_listener.readError(I2CPMessageReader.this, e);
cancelRunner();
}
}
/**
* Called by run()
* @since 0.9.21
*/
protected void run2() {
while (_stayAlive) {
while (_doRun) {
// do read

View File

@ -37,6 +37,16 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
return _sessionId;
}
/**
* Return the SessionId for this message.
*
* @since 0.9.21
*/
@Override
public SessionId sessionId() {
return _sessionId >= 0 ? new SessionId(_sessionId) : null;
}
/** @param id 0-65535 */
public void setSessionId(long id) {
_sessionId = (int) id;

View File

@ -193,6 +193,16 @@ public class MessageStatusMessage extends I2CPMessageImpl {
return _sessionId;
}
/**
* Return the SessionId for this message.
*
* @since 0.9.21
*/
@Override
public SessionId sessionId() {
return _sessionId >= 0 ? new SessionId(_sessionId) : null;
}
/** @param id 0-65535 */
public void setSessionId(long id) {
_sessionId = (int) id;
@ -275,6 +285,12 @@ public class MessageStatusMessage extends I2CPMessageImpl {
return "GUARANTEED SUCCESS ";
case STATUS_SEND_SUCCESS_LOCAL:
return "LOCAL SUCCESS ";
case STATUS_SEND_BEST_EFFORT_FAILURE:
return "PROBABLE FAILURE ";
case STATUS_SEND_FAILURE_NO_TUNNELS:
return "NO LOCAL TUNNELS ";
case STATUS_SEND_FAILURE_NO_LEASESET:
return "LEASESET NOT FOUND ";
default:
return "SEND FAILURE CODE: " + status;
}

View File

@ -36,6 +36,16 @@ public class ReceiveMessageBeginMessage extends I2CPMessageImpl {
return _sessionId;
}
/**
* Return the SessionId for this message.
*
* @since 0.9.21
*/
@Override
public SessionId sessionId() {
return _sessionId >= 0 ? new SessionId(_sessionId) : null;
}
/** @param id 0-65535 */
public void setSessionId(long id) {
_sessionId = (int) id;

View File

@ -35,6 +35,16 @@ public class ReceiveMessageEndMessage extends I2CPMessageImpl {
return _sessionId;
}
/**
* Return the SessionId for this message.
*
* @since 0.9.21
*/
@Override
public SessionId sessionId() {
return _sessionId >= 0 ? new SessionId(_sessionId) : null;
}
/** @param id 0-65535 */
public void setSessionId(long id) {
_sessionId = (int) id;

View File

@ -33,6 +33,16 @@ public class ReconfigureSessionMessage extends I2CPMessageImpl {
return _sessionId;
}
/**
* Return the SessionId for this message.
*
* @since 0.9.21
*/
@Override
public SessionId sessionId() {
return _sessionId;
}
public void setSessionId(SessionId id) {
_sessionId = id;
}

View File

@ -35,6 +35,16 @@ public class ReportAbuseMessage extends I2CPMessageImpl {
return _sessionId;
}
/**
* Return the SessionId for this message.
*
* @since 0.9.21
*/
@Override
public SessionId sessionId() {
return _sessionId;
}
public void setSessionId(SessionId id) {
_sessionId = id;
}

View File

@ -45,6 +45,16 @@ public class RequestLeaseSetMessage extends I2CPMessageImpl {
return _sessionId;
}
/**
* Return the SessionId for this message.
*
* @since 0.9.21
*/
@Override
public SessionId sessionId() {
return _sessionId;
}
public void setSessionId(SessionId id) {
_sessionId = id;
}

View File

@ -55,6 +55,16 @@ public class RequestVariableLeaseSetMessage extends I2CPMessageImpl {
return _sessionId;
}
/**
* Return the SessionId for this message.
*
* @since 0.9.21
*/
@Override
public SessionId sessionId() {
return _sessionId;
}
public void setSessionId(SessionId id) {
_sessionId = id;
}

View File

@ -38,6 +38,16 @@ public class SendMessageMessage extends I2CPMessageImpl {
return _sessionId;
}
/**
* Return the SessionId for this message.
*
* @since 0.9.21
*/
@Override
public SessionId sessionId() {
return _sessionId;
}
public void setSessionId(SessionId id) {
_sessionId = id;
}

View File

@ -121,7 +121,11 @@ public class SessionConfig extends DataStructureImpl {
public void signSessionConfig(SigningPrivateKey signingKey) throws DataFormatException {
byte data[] = getBytes();
if (data == null) throw new DataFormatException("Unable to retrieve bytes for signing");
if (signingKey == null)
throw new DataFormatException("No signing key");
_signature = DSAEngine.getInstance().sign(data, signingKey);
if (_signature == null)
throw new DataFormatException("Signature failed with " + signingKey.getType() + " key");
}
/**

View File

@ -42,6 +42,16 @@ public class SessionStatusMessage extends I2CPMessageImpl {
return _sessionId;
}
/**
* Return the SessionId for this message.
*
* @since 0.9.21
*/
@Override
public SessionId sessionId() {
return _sessionId;
}
public void setSessionId(SessionId id) {
_sessionId = id;
}

View File

@ -1,5 +1,7 @@
package net.i2p.internal;
import java.io.Closeable;
import net.i2p.data.i2cp.I2CPMessage;
/**
@ -15,7 +17,7 @@ import net.i2p.data.i2cp.I2CPMessage;
* @author zzz
* @since 0.8.3
*/
public abstract class I2CPMessageQueue {
public abstract class I2CPMessageQueue implements Closeable {
/**
* Send a message, nonblocking.

View File

@ -42,7 +42,7 @@ public class QueuedI2CPMessageReader extends I2CPMessageReader {
* Pumps messages from the incoming message queue to the listener.
*/
@Override
public void run() {
protected void run2() {
while (_stayAlive) {
while (_doRun) {
// do read

View File

@ -628,6 +628,7 @@ public class KBucketSet<T extends SimpleDataStructure> {
* @param data size <= SDS length, else throws IAE
* Can be 1 bigger if top byte is zero
*/
@SuppressWarnings("unchecked")
private T makeKey(byte[] data) {
int len = _us.length();
int dlen = data.length;

View File

@ -2,8 +2,9 @@ package net.i2p.stat;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
@ -115,7 +116,7 @@ public class BufferedStatLog implements StatLog {
if (_out != null) try { _out.close(); } catch (IOException ioe) {}
_outFile = filename;
try {
_out = new BufferedWriter(new FileWriter(_outFile, true), 32*1024);
_out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(_outFile, true), "UTF-8"), 32*1024);
} catch (IOException ioe) { ioe.printStackTrace(); }
}
}

View File

@ -150,4 +150,16 @@ public class Frequency {
private final static long now() {
return System.currentTimeMillis();
}
/**
* Appends the data of this frequency to the specified StringBuilder
* @param dest to append data to
* @since 0.9.23
*/
synchronized void store(StringBuilder dest) {
dest.append("avgInterval:").append(_avgInterval).append(',');
dest.append("minAverageInterval").append(_minAverageInterval).append(',');
dest.append("lastEvent").append(_lastEvent).append(",");
dest.append("count").append(_count);
}
}

View File

@ -1,5 +1,10 @@
package net.i2p.stat;
import java.io.IOException;
import java.io.OutputStream;
import net.i2p.data.DataHelper;
/** coordinate an event frequency over various periods */
public class FrequencyStat {
/** unique name of the statistic */
@ -92,5 +97,34 @@ public class FrequencyStat {
if ((obj == null) || !(obj instanceof FrequencyStat)) return false;
return _statName.equals(((FrequencyStat)obj)._statName);
}
private final static String NL = System.getProperty("line.separator");
/**
* Serializes this FrequencyStat to the provided OutputStream
* @param out to write to
* @param prefix to prepend to the stat
* @throws IOException if something goes wrong
* @since 0.9.23
*/
public void store(OutputStream out, String prefix) throws IOException {
StringBuilder buf = new StringBuilder(1024);
buf.append(NL);
buf.append("################################################################################").append(NL);
buf.append("# Frequency: ").append(_groupName).append(": ").append(_statName).append(NL);
buf.append("# ").append(_description).append(NL);
buf.append("# ").append(NL).append(NL);
out.write(buf.toString().getBytes("UTF-8"));
buf.setLength(0);
for (Frequency r: _frequencies){
buf.append("#######").append(NL);
buf.append("# Period : ").append(DataHelper.formatDuration(r.getPeriod())).append(" for rate ")
.append(_groupName).append(" - ").append(_statName).append(NL);
buf.append(NL);
r.store(buf);
out.write(buf.toString().getBytes("UTF-8"));
buf.setLength(0);
}
}
}

View File

@ -14,19 +14,19 @@ import net.i2p.data.DataHelper;
*/
public class Rate {
//private final static Log _log = new Log(Rate.class);
private double _currentTotalValue;
private float _currentTotalValue;
// was long, save space
private int _currentEventCount;
private long _currentTotalEventTime;
private double _lastTotalValue;
private int _currentTotalEventTime;
private float _lastTotalValue;
// was long, save space
private int _lastEventCount;
private long _lastTotalEventTime;
private double _extremeTotalValue;
private int _lastTotalEventTime;
private float _extremeTotalValue;
// was long, save space
private int _extremeEventCount;
private long _extremeTotalEventTime;
private double _lifetimeTotalValue;
private int _extremeTotalEventTime;
private float _lifetimeTotalValue;
private long _lifetimeEventCount;
private long _lifetimeTotalEventTime;
private RateSummaryListener _summaryListener;
@ -227,10 +227,10 @@ public class Rate {
// ok ok, lets coalesce
// how much were we off by? (so that we can sample down the measured values)
double periodFactor = measuredPeriod / (double)_period;
float periodFactor = measuredPeriod / (float)_period;
_lastTotalValue = _currentTotalValue / periodFactor;
_lastEventCount = (int) (0.499999 + (_currentEventCount / periodFactor));
_lastTotalEventTime = (long) (_currentTotalEventTime / periodFactor);
_lastTotalEventTime = (int) (_currentTotalEventTime / periodFactor);
_lastCoalesceDate = now;
if (_currentEventCount == 0)
correctedTotalValue = 0;
@ -244,7 +244,7 @@ public class Rate {
_extremeTotalEventTime = _lastTotalEventTime;
}
_currentTotalValue = 0.0D;
_currentTotalValue = 0.0f;
_currentEventCount = 0;
_currentTotalEventTime = 0;
}
@ -505,16 +505,16 @@ public class Rate {
_period = PersistenceHelper.getInt(props, prefix, ".period");
_creationDate = PersistenceHelper.getLong(props, prefix, ".creationDate");
_lastCoalesceDate = PersistenceHelper.getLong(props, prefix, ".lastCoalesceDate");
_currentTotalValue = PersistenceHelper.getDouble(props, prefix, ".currentTotalValue");
_currentTotalValue = (float)PersistenceHelper.getDouble(props, prefix, ".currentTotalValue");
_currentEventCount = PersistenceHelper.getInt(props, prefix, ".currentEventCount");
_currentTotalEventTime = PersistenceHelper.getLong(props, prefix, ".currentTotalEventTime");
_lastTotalValue = PersistenceHelper.getDouble(props, prefix, ".lastTotalValue");
_currentTotalEventTime = (int)PersistenceHelper.getLong(props, prefix, ".currentTotalEventTime");
_lastTotalValue = (float)PersistenceHelper.getDouble(props, prefix, ".lastTotalValue");
_lastEventCount = PersistenceHelper.getInt(props, prefix, ".lastEventCount");
_lastTotalEventTime = PersistenceHelper.getLong(props, prefix, ".lastTotalEventTime");
_extremeTotalValue = PersistenceHelper.getDouble(props, prefix, ".extremeTotalValue");
_lastTotalEventTime = (int)PersistenceHelper.getLong(props, prefix, ".lastTotalEventTime");
_extremeTotalValue = (float)PersistenceHelper.getDouble(props, prefix, ".extremeTotalValue");
_extremeEventCount = PersistenceHelper.getInt(props, prefix, ".extremeEventCount");
_extremeTotalEventTime = PersistenceHelper.getLong(props, prefix, ".extremeTotalEventTime");
_lifetimeTotalValue = PersistenceHelper.getDouble(props, prefix, ".lifetimeTotalValue");
_extremeTotalEventTime = (int)PersistenceHelper.getLong(props, prefix, ".extremeTotalEventTime");
_lifetimeTotalValue = (float)PersistenceHelper.getDouble(props, prefix, ".lifetimeTotalValue");
_lifetimeEventCount = PersistenceHelper.getLong(props, prefix, ".lifetimeEventCount");
_lifetimeTotalEventTime = PersistenceHelper.getLong(props, prefix, ".lifetimeTotalEventTime");

View File

@ -184,7 +184,7 @@ public class RateStat {
buf.append("# Rate: ").append(_groupName).append(": ").append(_statName).append(NL);
buf.append("# ").append(_description).append(NL);
buf.append("# ").append(NL).append(NL);
out.write(buf.toString().getBytes());
out.write(buf.toString().getBytes("UTF-8"));
buf.setLength(0);
for (Rate r: _rates){
buf.append("#######").append(NL);
@ -193,7 +193,7 @@ public class RateStat {
buf.append(NL);
String curPrefix = prefix + "." + DataHelper.formatDuration(r.getPeriod());
r.store(curPrefix, buf);
out.write(buf.toString().getBytes());
out.write(buf.toString().getBytes("UTF-8"));
buf.setLength(0);
}
}

View File

@ -1,5 +1,7 @@
package net.i2p.stat;
import java.io.IOException;
import java.io.OutputStream;
import java.text.Collator;
import java.util.HashMap;
import java.util.HashSet;
@ -247,4 +249,18 @@ public class StatManager {
public boolean ignoreStat(String statName) {
return _context.isRouterContext() && !_context.getBooleanProperty(PROP_STAT_FULL);
}
/**
* Serializes all Frequencies and Rates to the provided OutputStream
* @param out to write to
* @param prefix to use when serializing
* @throws IOException if something goes wrong
* @since 0.9.23
*/
public void store(OutputStream out, String prefix) throws IOException {
for (FrequencyStat fs : _frequencyStats.values())
fs.store(out, prefix);
for (RateStat rs : _rateStats.values())
rs.store(out,prefix);
}
}

View File

@ -121,6 +121,114 @@ public interface UpdateManager {
* @return true if valid, false if corrupt
*/
public boolean notifyComplete(UpdateTask task, String actualVersion, File file);
/**
* Is an update available?
* Blocking.
* An available update may still have a constraint or lack sources.
* @param type the UpdateType of this request
* @return new version or null if nothing newer is available
* @since 0.9.21
*/
public String checkAvailable(UpdateType type);
/**
* Is an update available?
* Blocking.
* An available update may still have a constraint or lack sources.
* @param type the UpdateType of this request
* @param maxWait max time to block
* @return new version or null if nothing newer is available
* @since 0.9.21
*/
public String checkAvailable(UpdateType type, long maxWait);
/**
* Is an update available?
* Blocking.
* An available update may still have a constraint or lack sources.
* @param type the UpdateType of this request
* @param maxWait max time to block
* @param id id of this request
* @return new version or null if nothing newer is available
* @since 0.9.21
*/
public String checkAvailable(UpdateType type, String id, long maxWait);
/**
* Is a router update being downloaded?
* @return true iff router update is being downloaded
* @since 0.9.21
*/
public boolean isUpdateInProgress();
/**
* Is a router update being downloaded?
* @param type the UpdateType of this request
* @return true iff router update is being downloaded
* @since 0.9.21
*/
public boolean isUpdateInProgress(UpdateType type);
/**
* Is a router update being downloaded?
* @param type the UpdateType of this request
* @param id of this request
* @return true iff router update is being downloaded
* @since 0.9.21
*/
public boolean isUpdateInProgress(UpdateType type, String id);
/**
* Non-blocking. Does not check.
* Fails if check or update already in progress.
* If returns true, then call isUpdateInProgress() in a loop
* @param type the UpdateType of this request
* @return true if task started
* @since 0.9.21
*/
public boolean update(UpdateType type);
/**
* Non-blocking. Does not check.
* Fails if check or update already in progress.
* If returns true, then call isUpdateInProgress() in a loop
* @param type the UpdateType of this request
* @param id id of this request
* @return true if task started
* @since 0.9.21
*/
public boolean update(UpdateType type, String id);
/**
* Non-blocking. Does not check.
* Fails if check or update already in progress.
* If returns true, then call isUpdateInProgress() in a loop
* @param type the UpdateType of this request
* @param maxTime not honored by all Updaters
* @return true if task started
* @since 0.9.21
*/
public boolean update(UpdateType type, long maxTime);
/**
* Non-blocking. Does not check.
* Fails if check or update already in progress.
* If returns true, then call isUpdateInProgress() in a loop
* @param type the UpdateType of this request
* @param maxTime not honored by all Updaters
* @param id id of this request
* @return true if task started
* @since 0.9.21
*/
public boolean update(UpdateType type, String id, long maxTime);
/**
* The status on any update current or last finished.
* @return status or ""
* @since 0.9.21
*/
public String getStatus();
/**
* For debugging

View File

@ -15,6 +15,8 @@ import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.http.conn.util.InetAddressUtils;
import net.i2p.I2PAppContext;
/**
@ -229,10 +231,10 @@ public abstract class Addresses {
I2PAppContext ctx = I2PAppContext.getCurrentContext();
if (ctx != null && ctx.isRouterContext()) {
long maxMemory = SystemVersion.getMaxMemory();
long min = 128;
long min = 256;
long max = 4096;
// 512 nominal for 128 MB
size = (int) Math.max(min, Math.min(max, 1 + (maxMemory / (256*1024))));
// 1024 nominal for 128 MB
size = (int) Math.max(min, Math.min(max, 1 + (maxMemory / (128*1024))));
} else {
size = 32;
}
@ -260,12 +262,9 @@ public abstract class Addresses {
}
if (rv == null) {
try {
boolean isIPv4 = host.replaceAll("[0-9\\.]", "").length() == 0;
if (isIPv4 && host.replaceAll("[0-9]", "").length() != 3)
return null;
rv = InetAddress.getByName(host).getAddress();
if (isIPv4 ||
host.replaceAll("[0-9a-fA-F:]", "").length() == 0) {
if (InetAddressUtils.isIPv4Address(host) ||
InetAddressUtils.isIPv6Address(host)) {
synchronized (_IPAddress) {
_IPAddress.put(host, rv);
}

View File

@ -24,6 +24,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import gnu.getopt.Getopt;
@ -312,22 +314,52 @@ public class EepGet {
System.exit(1);
}
/**
* Parse URL for a viable filename.
*
* @param url a URL giving the location of an online resource
* @return a filename to save the resource as on local filesystem
*/
public static String suggestName(String url) {
int last = url.lastIndexOf('/');
if ((last < 0) || (url.lastIndexOf('#') > last))
last = url.lastIndexOf('#');
if ((last < 0) || (url.lastIndexOf('?') > last))
last = url.lastIndexOf('?');
if ((last < 0) || (url.lastIndexOf('=') > last))
last = url.lastIndexOf('=');
URL nameURL = null; // URL object
String name; // suggested name
String name = null;
if (last >= 0)
name = sanitize(url.substring(last+1));
if ( (name != null) && (name.length() > 0) )
return name;
else
return sanitize(url);
try {
nameURL = new URL(url);
} catch (MalformedURLException e) {
System.err.println("Please enter a properly formed URL.");
System.exit(1);
}
String path = nameURL.getPath(); // discard any URI queries
// if no file specified, eepget scrapes webpage - use domain as name
Pattern slashes = Pattern.compile("/+");
Matcher matcher = slashes.matcher(path);
// if empty path or just /'s - nameURL lets multiple /'s through
if (path.equals("") || matcher.matches()) {
name = sanitize(nameURL.getAuthority());
// if path specified
} else {
int last = path.lastIndexOf('/');
// if last / not at end of string, use following string as filename
if (last != path.length() - 1) {
name = sanitize(path.substring(last + 1));
// if there's a trailing / group look for previous / as trim point
} else {
int i = 1;
int slash;
while (true) {
slash = path.lastIndexOf('/', last - i);
if (slash != last - i) {
break;
}
i += 1;
}
name = sanitize(path.substring(slash + 1, path.length() - i));
}
}
return name;
}
@ -755,6 +787,8 @@ public class EepGet {
Thread pusher = null;
_decompressException = null;
if (_isGzippedResponse) {
if (_log.shouldInfo())
_log.info("Gzipped response, starting decompressor");
PipedInputStream pi = BigPipedInputStream.getInstance();
PipedOutputStream po = new PipedOutputStream(pi);
pusher = new I2PAppThread(new Gunzipper(pi, _out), "EepGet Decompressor");
@ -1096,7 +1130,7 @@ public class EepGet {
*/
private int handleStatus(String line) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Status line: [" + line + "]");
_log.debug("Status line: [" + line.trim() + "]");
String[] toks = line.split(" ", 3);
if (toks.length < 2) {
if (_log.shouldLog(Log.WARN))
@ -1160,17 +1194,13 @@ public class EepGet {
lookahead[1] = lookahead[2];
lookahead[2] = (byte)cur;
}
private static boolean isEndOfHeaders(byte lookahead[]) {
byte first = lookahead[0];
byte second = lookahead[1];
byte third = lookahead[2];
return (isNL(second) && isNL(third)) || // \n\n
(isNL(first) && isNL(third)); // \n\r\n
return lookahead[2] == NL &&
(lookahead[0] == NL || lookahead[1] == NL); // \n\n or \n\r\n
}
/** we ignore any potential \r, since we trim it on write anyway */
private static final byte NL = '\n';
private static boolean isNL(byte b) { return (b == NL); }
/**
* @param timeout may be null
@ -1315,7 +1345,8 @@ public class EepGet {
buf.append("Content-length: ").append(_postData.length()).append("\r\n");
// This will be replaced if we are going through I2PTunnelHTTPClient
buf.append("Accept-Encoding: ");
if ((!_shouldProxy) &&
// as of 0.9.23, the proxy passes the Accept-Encoding header through
if ( /* (!_shouldProxy) && */
// This is kindof a hack, but if we are downloading a gzip file
// we don't want to transparently gunzip it and save it as a .gz file.
(!path.endsWith(".gz")) && (!path.endsWith(".tgz")))

View File

@ -300,9 +300,9 @@ public class FileUtil {
if (!_failedOracle) {
try {
Class<?> p200 = Class.forName("java.util.jar.Pack200", true, ClassLoader.getSystemClassLoader());
Method newUnpacker = p200.getMethod("newUnpacker", (Class[]) null);
Method newUnpacker = p200.getMethod("newUnpacker");
Object unpacker = newUnpacker.invoke(null,(Object[]) null);
Method unpack = unpacker.getClass().getMethod("unpack", new Class[] {InputStream.class, JarOutputStream.class});
Method unpack = unpacker.getClass().getMethod("unpack", InputStream.class, JarOutputStream.class);
// throws IOException
unpack.invoke(unpacker, new Object[] {in, out});
return;
@ -321,9 +321,9 @@ public class FileUtil {
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);
Constructor<?> newUnpacker = p200.getConstructor(InputStream.class, JarOutputStream.class);
Object unpacker = newUnpacker.newInstance(in, out);
Method unpack = unpacker.getClass().getMethod("unpack");
// throws IOException or Pack200Exception
unpack.invoke(unpacker, (Object[]) null);
return;

View File

@ -262,8 +262,14 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste
/** reseed the fortuna */
@Override
public void feedEntropy(String source, byte[] data, int offset, int len) {
synchronized(_fortuna) {
_fortuna.addRandomBytes(data, offset, len);
try {
synchronized(_fortuna) {
_fortuna.addRandomBytes(data, offset, len);
}
} catch (Exception e) {
// AIOOBE seen, root cause unknown, ticket #1576
Log log = _context.logManager().getLog(FortunaRandomSource.class);
log.warn("feedEntropy()", e);
}
}

View File

@ -13,6 +13,8 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import net.i2p.data.DataHelper;
/**
* Hexdump class (well, it's actually a namespace with some functions,
* but let's stick with java terminology :-). These methods generate
@ -25,7 +27,7 @@ public class HexDump {
private static final int FORMAT_OFFSET_PADDING = 8;
private static final int FORMAT_BYTES_PER_ROW = 16;
private static final byte[] HEXCHARS = "0123456789abcdef".getBytes();
private static final byte[] HEXCHARS = DataHelper.getASCII("0123456789abcdef");
/**
* Dump a byte array in a String.
@ -37,11 +39,10 @@ public class HexDump {
try {
dump(data, 0, data.length, out);
return out.toString("ISO-8859-1");
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("no 8859?", e);
}
return out.toString();
}
/**
@ -56,11 +57,10 @@ public class HexDump {
try {
dump(data, off, len, out);
return out.toString("ISO-8859-1");
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("no 8859?", e);
}
return out.toString();
}
/**
@ -91,9 +91,10 @@ public class HexDump {
hexoff = Integer.toString(dumpoff, 16);
hexofflen = hexoff.length();
for (i = 0; i < FORMAT_OFFSET_PADDING - hexofflen; ++i) {
hexoff = "0" + hexoff;
out.write('0');
}
out.write((hexoff + " ").getBytes());
out.write(DataHelper.getASCII(hexoff));
out.write(' ');
// Bytes to be printed in the current line
nextbytes = (FORMAT_BYTES_PER_ROW < (end - dumpoff) ? FORMAT_BYTES_PER_ROW : (end - dumpoff));
@ -101,15 +102,15 @@ public class HexDump {
for (i = 0; i < FORMAT_BYTES_PER_ROW; ++i) {
// Put two spaces to separate 8-bytes blocks
if ((i % 8) == 0) {
out.write(" ".getBytes());
out.write(' ');
}
if (i >= nextbytes) {
out.write(" ".getBytes());
out.write(DataHelper.getASCII(" "));
} else {
val = data[dumpoff + i] & 0xff;
out.write(HEXCHARS[val >>> 4]);
out.write(HEXCHARS[val & 0xf]);
out.write(" ".getBytes());
out.write(' ');
}
}
@ -117,19 +118,32 @@ public class HexDump {
for (i = 0; i < FORMAT_BYTES_PER_ROW; ++i) {
if (i >= nextbytes) {
out.write(" ".getBytes());
out.write(' ');
} else {
val = data[i + dumpoff];
// Is it a printable character?
if ((val > 31) && (val < 127)) {
out.write(val);
} else {
out.write(".".getBytes());
out.write('.');
}
}
}
out.write("|\n".getBytes());
out.write('|');
out.write('\n');
}
}
}
/**
* @since 0.9.21
*/
/****
public static void main(String[] args) {
byte[] b = new byte[9993];
RandomSource.getInstance().nextBytes(b);
System.out.println(dump(b));
System.out.println(dump("test test test abcde xyz !!!".getBytes()));
}
****/
}

View File

@ -14,10 +14,13 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Like I2PThread but with per-thread OOM listeners,
* Like {@link I2PThread} but with per-thread OOM listeners,
* rather than a static router-wide listener list,
* so that an OOM in an app won't call the router listener
* to shutdown the whole router.
*
* This is preferred for application use.
* See {@link I2PThread} for features.
*/
public class I2PAppThread extends I2PThread {
@ -38,9 +41,17 @@ public class I2PAppThread extends I2PThread {
public I2PAppThread(Runnable r, String name) {
super(r, name);
}
public I2PAppThread(Runnable r, String name, boolean isDaemon) {
super(r, name, isDaemon);
}
/**
* @since 0.9.23
*/
public I2PAppThread(ThreadGroup group, Runnable r, String name) {
super(group, r, name);
}
@Override
protected void fireOOM(OutOfMemoryError oom) {

View File

@ -204,7 +204,15 @@ public class I2PSSLSocketFactory {
"TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
// following is disabled because it is weak
// see e.g. https://bugzilla.mozilla.org/show_bug.cgi?id=1107787
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA"
// ??? "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"
//
// NOTE:
// If you add anything here, please also add to installer/resources/eepsite/jetty-ssl.xml
//
}));
/**
@ -284,7 +292,7 @@ public class I2PSSLSocketFactory {
host.equals("localhost") ||
host.equals("127.0.0.1") ||
host.equals("::1") ||
host.equals("0:0:0:0:0:0:0::1")) {
host.equals("0:0:0:0:0:0:0:1")) {
if (log.shouldWarn())
log.warn("Skipping hostname validation for " + host);
return;
@ -301,7 +309,6 @@ public class I2PSSLSocketFactory {
// is not a viable option because the default HostnameVerifier expects to only be called
// in the case that there is a mismatch (and therefore always returns false) while some
// of the AsyncHttpClient providers (e.g. Netty, the default) call it on all connections.
// in the case that there is a mismatch (and therefore always returns false) while some
// To make matters worse, the check is not trivial (consider SAN and wildcard matching)
// and is implemented in sun.security.util.HostnameChecker (a Sun internal proprietary API).
// This leaves the developer in the position of either depending on an internal API or

View File

@ -14,68 +14,63 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* In case its useful later...
* (e.g. w/ native programatic thread dumping, etc)
*
* Preferred over {@link Thread} for all router uses.
* For applications, {@link I2PAppThread} is preferred.
* <p>
* Provides the following features:
* <ul>
* <li>Logging to wrapper log on unexpected termination in {@link #run()}.
* <li>Notification of OOM to registered listener (the router),
* which will cause logging to the wrapper log and a router restart
* <li>Catching and logging "OOM" caused by thread limit in {@link #start()}
* with distinct message, and does not call the OOM listener.
* <li>As of 0.9.21, initialization to NORM_PRIORITY
* (not the priority of the creating thread).
* </ul>
*/
public class I2PThread extends Thread {
/**
* Non-static to avoid refs to old context in Android.
* Probably should just remove all the logging though.
* Logging removed, too much trouble with extra contexts
*/
//private volatile Log _log;
private static final Set<OOMEventListener> _listeners = new CopyOnWriteArraySet<OOMEventListener>();
//private String _name;
//private Exception _createdBy;
public I2PThread() {
super();
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
// _createdBy = new Exception("Created by");
setPriority(NORM_PRIORITY);
}
public I2PThread(String name) {
super(name);
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
// _createdBy = new Exception("Created by");
setPriority(NORM_PRIORITY);
}
public I2PThread(Runnable r) {
super(r);
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
// _createdBy = new Exception("Created by");
setPriority(NORM_PRIORITY);
}
public I2PThread(Runnable r, String name) {
super(r, name);
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
// _createdBy = new Exception("Created by");
setPriority(NORM_PRIORITY);
}
public I2PThread(Runnable r, String name, boolean isDaemon) {
super(r, name);
setDaemon(isDaemon);
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
// _createdBy = new Exception("Created by");
setPriority(NORM_PRIORITY);
}
public I2PThread(ThreadGroup g, Runnable r) {
super(g, r);
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
// _createdBy = new Exception("Created by");
setPriority(NORM_PRIORITY);
}
/****
private void log(int level, String msg) { log(level, msg, null); }
private 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))
_log.log(level, msg, t);
/**
* @since 0.9.23
*/
public I2PThread(ThreadGroup group, Runnable r, String name) {
super(group, r, name);
setPriority(NORM_PRIORITY);
}
****/
/**
* Overridden to provide useful info to users on OOM, and to prevent
* shutting down the whole JVM for what is most likely not a heap issue.
@ -101,19 +96,9 @@ public class I2PThread extends Thread {
@Override
public void run() {
//_name = Thread.currentThread().getName();
//log(Log.INFO, "New thread started" + (isDaemon() ? " (daemon): " : ": ") + _name, _createdBy);
try {
super.run();
} catch (Throwable t) {
/****
try {
log(Log.CRIT, "Thread terminated unexpectedly: " + getName(), t);
} catch (Throwable woof) {
System.err.println("Died within the OOM itself");
t.printStackTrace();
}
****/
if (t instanceof OutOfMemoryError) {
fireOOM((OutOfMemoryError)t);
} else {
@ -121,18 +106,8 @@ public class I2PThread extends Thread {
t.printStackTrace();
}
}
// This creates a new I2PAppContext after it was deleted
// in Router.finalShutdown() via RouterContext.killGlobalContext()
//log(Log.INFO, "Thread finished normally: " + _name);
}
/****
protected void finalize() throws Throwable {
//log(Log.DEBUG, "Thread finalized: " + _name);
super.finalize();
}
****/
protected void fireOOM(OutOfMemoryError oom) {
for (OOMEventListener listener : _listeners)
listener.outOfMemory(oom);

View File

@ -68,7 +68,7 @@ public class InternalSocket extends Socket {
}
@Override
public void close() {
public synchronized void close() {
try {
if (_is != null) {
_is.close();
@ -84,7 +84,7 @@ public class InternalSocket extends Socket {
}
@Override
public boolean isClosed() {
public synchronized boolean isClosed() {
return _is == null || _os == null;
}

View File

@ -10,6 +10,7 @@ package net.i2p.util;
*/
import java.io.File;
import java.io.Flushable;
import java.io.IOException;
import java.text.DateFormat;
import java.text.DecimalFormat;
@ -37,7 +38,7 @@ import net.i2p.data.DataHelper;
* writes them where appropriate.
*
*/
public class LogManager {
public class LogManager implements Flushable {
public final static String CONFIG_LOCATION_PROP = "loggerConfigLocation";
public final static String FILENAME_OVERRIDE_PROP = "loggerFilenameOverride";
public final static String CONFIG_LOCATION_DEFAULT = "logger.config";
@ -762,7 +763,7 @@ public class LogManager {
private static final AtomicInteger __id = new AtomicInteger();
private class ShutdownHook extends Thread {
private class ShutdownHook extends I2PAppThread {
private final int _id;
public ShutdownHook() {
_id = __id.incrementAndGet();

Some files were not shown because too many files have changed in this diff Show More