forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p' (head e2aa08a93036bcf0d846b8ff67e9cb74de3e4d0f)
to branch 'i2p.i2p.zzz.test2' (head b3d23ed369ba339b9a71dfeb205110458df9ec0d)
This commit is contained in:
@ -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();
|
||||
}
|
||||
|
||||
|
31
core/java/src/gnu/getopt/MessagesBundle_fi.properties
Normal file
31
core/java/src/gnu/getopt/MessagesBundle_fi.properties
Normal 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'
|
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package net.i2p.client;
|
||||
package net.i2p.client.impl;
|
||||
|
||||
/*
|
||||
* Released into the public domain
|
@ -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;
|
@ -1,4 +1,4 @@
|
||||
package net.i2p.client;
|
||||
package net.i2p.client.impl;
|
||||
|
||||
/*
|
||||
* Released into the public domain
|
@ -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;
|
@ -1,4 +1,4 @@
|
||||
package net.i2p.client;
|
||||
package net.i2p.client.impl;
|
||||
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
@ -1,4 +1,4 @@
|
||||
package net.i2p.client;
|
||||
package net.i2p.client.impl;
|
||||
|
||||
/*
|
||||
* Released into the public domain
|
@ -1,4 +1,4 @@
|
||||
package net.i2p.client;
|
||||
package net.i2p.client.impl;
|
||||
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
@ -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);
|
||||
}
|
||||
}
|
@ -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.
|
@ -1,4 +1,4 @@
|
||||
package net.i2p.client;
|
||||
package net.i2p.client.impl;
|
||||
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
@ -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) {
|
@ -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);
|
@ -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);
|
||||
}
|
@ -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) {
|
@ -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();
|
||||
}
|
@ -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();
|
@ -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;
|
@ -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;
|
@ -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;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package net.i2p.client;
|
||||
package net.i2p.client.impl;
|
||||
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
@ -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;
|
@ -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;
|
@ -1,4 +1,4 @@
|
||||
package net.i2p.client;
|
||||
package net.i2p.client.impl;
|
||||
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
347
core/java/src/net/i2p/client/impl/SubSession.java
Normal file
347
core/java/src/net/i2p/client/impl/SubSession.java
Normal 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();
|
||||
}
|
||||
}
|
11
core/java/src/net/i2p/client/impl/package.html
Normal file
11
core/java/src/net/i2p/client/impl/package.html
Normal 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>
|
@ -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");
|
||||
|
@ -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) {
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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?
|
||||
|
47
core/java/src/net/i2p/crypto/CryptoCheck.java
Normal file
47
core/java/src/net/i2p/crypto/CryptoCheck.java
Normal 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());
|
||||
}
|
||||
}
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
****/
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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("-", "−");
|
||||
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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; }
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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(); }
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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")))
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()));
|
||||
}
|
||||
****/
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
Reference in New Issue
Block a user