forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p.unittests' (head 53586f73fb813f519cdb6a1f7b1b40efec2e35dc)
to branch 'i2p.i2p' (head 628a2c591ca44095e2f93acd026046d4512cf692)
This commit is contained in:
@@ -18,7 +18,7 @@ public class CoreVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Monotone";
|
||||
|
||||
public final static String VERSION = "0.9.22";
|
||||
public final static String VERSION = "0.9.23";
|
||||
|
||||
/**
|
||||
* For Vuze.
|
||||
|
||||
@@ -84,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;
|
||||
@@ -532,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)
|
||||
@@ -940,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();
|
||||
|
||||
@@ -18,6 +18,7 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -98,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.
|
||||
@@ -106,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.
|
||||
@@ -116,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.
|
||||
@@ -133,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;
|
||||
|
||||
/**
|
||||
@@ -152,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;
|
||||
|
||||
/**
|
||||
@@ -171,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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -102,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");
|
||||
}
|
||||
|
||||
@@ -114,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();
|
||||
@@ -132,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);
|
||||
}
|
||||
|
||||
@@ -371,7 +371,7 @@ class I2CPMessageProducer {
|
||||
return;
|
||||
}
|
||||
msg.setSessionId(sid);
|
||||
session.sendMessage(msg);
|
||||
session.sendMessage_unchecked(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -209,8 +209,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
||||
VersionComparator.comp(routerVersion, MIN_SUBSESSION_VERSION) >= 0);
|
||||
synchronized (_stateLock) {
|
||||
if (_state == State.OPENING) {
|
||||
_state = State.GOTDATE;
|
||||
_stateLock.notifyAll();
|
||||
changeState(State.GOTDATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -635,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()");
|
||||
@@ -737,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);
|
||||
}
|
||||
|
||||
@@ -870,7 +862,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
||||
if ((duration > 100) && _log.shouldLog(Log.INFO))
|
||||
_log.info("Message availability notification for " + msgId.intValue() + " took "
|
||||
+ duration + " to " + _sessionListener);
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
_log.log(Log.CRIT, "Error notifying app of message availability", e);
|
||||
}
|
||||
} else {
|
||||
@@ -1034,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
|
||||
@@ -1041,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 {
|
||||
@@ -1055,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1441,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) {
|
||||
@@ -1533,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);
|
||||
@@ -1567,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);
|
||||
|
||||
@@ -29,6 +29,7 @@ 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;
|
||||
@@ -210,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");
|
||||
}
|
||||
@@ -253,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);
|
||||
}
|
||||
|
||||
@@ -261,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);
|
||||
}
|
||||
@@ -272,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,
|
||||
@@ -339,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);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ 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;
|
||||
|
||||
@@ -163,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);
|
||||
}
|
||||
@@ -173,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);
|
||||
}
|
||||
@@ -192,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);
|
||||
@@ -213,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);
|
||||
@@ -279,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))
|
||||
@@ -405,7 +399,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 {
|
||||
try {
|
||||
_demultiplexer.messageAvailable(I2PSessionMuxedImpl.this,
|
||||
msg.id, msg.size, msg.proto, msg.fromPort, msg.toPort);
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
_log.error("Error notifying app of message availability", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,11 +122,11 @@ public 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();
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ class SubSession extends I2PSessionMuxedImpl {
|
||||
public void connect() throws I2PSessionException {
|
||||
synchronized(_stateLock) {
|
||||
if (_state != State.OPEN) {
|
||||
_state = State.OPENING;
|
||||
changeState(State.OPENING);
|
||||
}
|
||||
}
|
||||
boolean success = false;
|
||||
@@ -121,7 +121,7 @@ class SubSession extends I2PSessionMuxedImpl {
|
||||
if (_state != State.OPEN) {
|
||||
Thread notifier = new I2PAppThread(_availabilityNotifier, "ClientNotifier " + getPrefix(), true);
|
||||
notifier.start();
|
||||
_state = State.OPEN;
|
||||
changeState(State.OPEN);
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
@@ -161,7 +161,20 @@ class SubSession extends I2PSessionMuxedImpl {
|
||||
message.getType() != CreateSessionMessage.MESSAGE_TYPE &&
|
||||
message.getType() != CreateLeaseSetMessage.MESSAGE_TYPE)
|
||||
throw new I2PSessionException("Already closed");
|
||||
_primary.sendMessage(message);
|
||||
_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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -77,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
|
||||
@@ -85,9 +87,11 @@ 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) {
|
||||
} catch (IOException ioe) {
|
||||
if (_file.exists())
|
||||
_log.error("Error loading hosts file " + _file, ioe);
|
||||
else if (_log.shouldLog(Log.WARN))
|
||||
@@ -119,7 +123,7 @@ public class SingleFileNamingService extends NamingService {
|
||||
return line.substring(0, split);
|
||||
}
|
||||
return null;
|
||||
} catch (Exception ioe) {
|
||||
} catch (IOException ioe) {
|
||||
if (_file.exists())
|
||||
_log.error("Error loading hosts file " + _file, ioe);
|
||||
else if (_log.shouldLog(Log.WARN))
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
package net.i2p.crypto;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Locale;
|
||||
@@ -77,12 +82,40 @@ public class CertUtil {
|
||||
* @return value or null if not found
|
||||
*/
|
||||
public static String getSubjectValue(X509Certificate cert, String type) {
|
||||
X500Principal p = cert.getSubjectX500Principal();
|
||||
return getValue(p, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value out of the issuer distinguished name.
|
||||
*
|
||||
* Warning - unsupported in Android (no javax.naming), returns null.
|
||||
*
|
||||
* @param type e.g. "CN"
|
||||
* @return value or null if not found
|
||||
* @since 0.9.24
|
||||
*/
|
||||
public static String getIssuerValue(X509Certificate cert, String type) {
|
||||
X500Principal p = cert.getIssuerX500Principal();
|
||||
return getValue(p, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value out of a X500Principal.
|
||||
*
|
||||
* Warning - unsupported in Android (no javax.naming), returns null.
|
||||
*
|
||||
* @param type e.g. "CN"
|
||||
* @return value or null if not found
|
||||
*/
|
||||
private static String getValue(X500Principal p, String type) {
|
||||
if (SystemVersion.isAndroid()) {
|
||||
error("Don't call this in Android", new UnsupportedOperationException("I did it"));
|
||||
return null;
|
||||
}
|
||||
if (p == null)
|
||||
return null;
|
||||
type = type.toUpperCase(Locale.US);
|
||||
X500Principal p = cert.getSubjectX500Principal();
|
||||
String subj = p.getName();
|
||||
try {
|
||||
LdapName name = new LdapName(subj);
|
||||
@@ -106,4 +139,40 @@ public class CertUtil {
|
||||
Log l = ctx.logManager().getLog(CertUtil.class);
|
||||
l.log(level, msg, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Java public key from a X.509 certificate file.
|
||||
* Throws if the certificate is invalid (e.g. expired).
|
||||
*
|
||||
* @return non-null, throws on all errors including certificate invalid
|
||||
* @since 0.9.24 moved from SU3File private method
|
||||
*/
|
||||
public static PublicKey loadKey(File kd) throws IOException, GeneralSecurityException {
|
||||
return loadCert(kd).getPublicKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the certificate from a X.509 certificate file.
|
||||
* Throws if the certificate is invalid (e.g. expired).
|
||||
*
|
||||
* @return non-null, throws on all errors including certificate invalid
|
||||
* @since 0.9.24 adapted from SU3File private method
|
||||
*/
|
||||
public static X509Certificate loadCert(File kd) throws IOException, GeneralSecurityException {
|
||||
InputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(kd);
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
X509Certificate cert = (X509Certificate)cf.generateCertificate(fis);
|
||||
cert.checkValidity();
|
||||
return cert;
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// java 1.8.0_40-b10, openSUSE
|
||||
// Exception in thread "main" java.lang.IllegalArgumentException: Input byte array has wrong 4-byte ending unit
|
||||
// at java.util.Base64$Decoder.decode0(Base64.java:704)
|
||||
throw new GeneralSecurityException("cert error", iae);
|
||||
} finally {
|
||||
try { if (fis != null) fis.close(); } catch (IOException foo) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,34 +38,12 @@ import net.i2p.util.SystemVersion;
|
||||
*/
|
||||
public class CryptixAESEngine extends AESEngine {
|
||||
private final static CryptixRijndael_Algorithm _algo = new CryptixRijndael_Algorithm();
|
||||
private final static boolean USE_FAKE_CRYPTO = false;
|
||||
// keys are now cached in the SessionKey objects
|
||||
//private CryptixAESKeyCache _cache;
|
||||
|
||||
/** 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?
|
||||
@@ -124,12 +102,6 @@ public class CryptixAESEngine extends AESEngine {
|
||||
if (length % 16 != 0)
|
||||
throw new IllegalArgumentException("Only lengths mod 16 are supported here");
|
||||
|
||||
if (USE_FAKE_CRYPTO) {
|
||||
_log.warn("AES Crypto disabled! Using trivial XOR");
|
||||
System.arraycopy(payload, payloadIndex, out, outIndex, length);
|
||||
return;
|
||||
}
|
||||
|
||||
if (USE_SYSTEM_AES && length >= MIN_SYSTEM_AES_LENGTH) {
|
||||
try {
|
||||
SecretKeySpec key = new SecretKeySpec(sessionKey.getData(), "AES");
|
||||
@@ -177,12 +149,6 @@ public class CryptixAESEngine extends AESEngine {
|
||||
throw new IllegalArgumentException("out is too small (out.length=" + out.length
|
||||
+ " outIndex=" + outIndex + " length=" + length);
|
||||
|
||||
if (USE_FAKE_CRYPTO) {
|
||||
_log.warn("AES Crypto disabled! Using trivial XOR");
|
||||
System.arraycopy(payload, payloadIndex, out, outIndex, length);
|
||||
return ;
|
||||
}
|
||||
|
||||
if (USE_SYSTEM_AES && length >= MIN_SYSTEM_AES_LENGTH) {
|
||||
try {
|
||||
SecretKeySpec key = new SecretKeySpec(sessionKey.getData(), "AES");
|
||||
|
||||
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");
|
||||
|
||||
@@ -257,7 +257,7 @@ public class DSAEngine {
|
||||
_log.warn("Took too long to verify the signature (" + diff + "ms)");
|
||||
}
|
||||
return ok;
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
_log.log(Log.CRIT, "Error verifying the signature", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ class DirKeyRing implements KeyRing {
|
||||
* and have a CN == keyName.
|
||||
*
|
||||
* CN check unsupported on Android.
|
||||
*
|
||||
* @return null if file doesn't exist, throws on all other errors
|
||||
*/
|
||||
public PublicKey getKey(String keyName, String scope, SigType type)
|
||||
throws GeneralSecurityException, IOException {
|
||||
@@ -47,26 +49,15 @@ class DirKeyRing implements KeyRing {
|
||||
File kd = new File(sd, fileName + ".crt");
|
||||
if (!kd.exists())
|
||||
return null;
|
||||
InputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(kd);
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
X509Certificate cert = (X509Certificate)cf.generateCertificate(fis);
|
||||
cert.checkValidity();
|
||||
if (!SystemVersion.isAndroid()) {
|
||||
// getSubjectValue() unsupported on Android.
|
||||
// Any cert problems will be caught in non-Android testing.
|
||||
String cn = CertUtil.getSubjectValue(cert, "CN");
|
||||
if (!keyName.equals(cn))
|
||||
throw new GeneralSecurityException("CN mismatch: " + cn);
|
||||
}
|
||||
return cert.getPublicKey();
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// java 1.8.0_40-b10, openSUSE
|
||||
throw new GeneralSecurityException("Bad cert", iae);
|
||||
} finally {
|
||||
try { if (fis != null) fis.close(); } catch (IOException foo) {}
|
||||
X509Certificate cert = CertUtil.loadCert(kd);
|
||||
if (!SystemVersion.isAndroid()) {
|
||||
// getSubjectValue() unsupported on Android.
|
||||
// Any cert problems will be caught in non-Android testing.
|
||||
String cn = CertUtil.getSubjectValue(cert, "CN");
|
||||
if (!keyName.equals(cn))
|
||||
throw new GeneralSecurityException("CN mismatch: " + cn);
|
||||
}
|
||||
return cert.getPublicKey();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.i2p.crypto;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.math.BigInteger;
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Provider;
|
||||
import java.security.Security;
|
||||
import java.security.spec.ECField;
|
||||
@@ -42,8 +43,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;
|
||||
@@ -278,7 +279,7 @@ class ECConstants {
|
||||
AlgorithmParameters ap;
|
||||
try {
|
||||
ap = AlgorithmParameters.getInstance("EC");
|
||||
} catch (Exception e) {
|
||||
} catch (GeneralSecurityException e) {
|
||||
if (BC_AVAILABLE) {
|
||||
log("Named curve " + name + " is not available, trying BC", e);
|
||||
ap = AlgorithmParameters.getInstance("EC", "BC");
|
||||
@@ -292,7 +293,7 @@ class ECConstants {
|
||||
ECParameterSpec rv = ap.getParameterSpec(ECParameterSpec.class);
|
||||
log("Named curve " + name + " loaded");
|
||||
return rv;
|
||||
} catch (Exception e) {
|
||||
} catch (GeneralSecurityException e) {
|
||||
log("Named curve " + name + " is not available", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -327,12 +327,12 @@ public class ElGamalAESEngine {
|
||||
//ByteArrayInputStream bais = new ByteArrayInputStream(decrypted);
|
||||
int cur = 0;
|
||||
long numTags = DataHelper.fromLong(decrypted, cur, 2);
|
||||
if ((numTags < 0) || (numTags > MAX_TAGS_RECEIVED)) throw new Exception("Invalid number of session tags");
|
||||
if ((numTags < 0) || (numTags > MAX_TAGS_RECEIVED)) throw new IllegalArgumentException("Invalid number of session tags");
|
||||
if (numTags > 0) tags = new ArrayList<SessionTag>((int)numTags);
|
||||
cur += 2;
|
||||
//_log.debug("# tags: " + numTags);
|
||||
if (numTags * SessionTag.BYTE_LENGTH > decrypted.length - 2) {
|
||||
throw new Exception("# tags: " + numTags + " is too many for " + (decrypted.length - 2));
|
||||
throw new IllegalArgumentException("# tags: " + numTags + " is too many for " + (decrypted.length - 2));
|
||||
}
|
||||
for (int i = 0; i < numTags; i++) {
|
||||
byte tag[] = new byte[SessionTag.BYTE_LENGTH];
|
||||
@@ -344,7 +344,7 @@ public class ElGamalAESEngine {
|
||||
cur += 4;
|
||||
//_log.debug("len: " + len);
|
||||
if ((len < 0) || (len > decrypted.length - cur - Hash.HASH_LENGTH - 1))
|
||||
throw new Exception("Invalid size of payload (" + len + ", remaining " + (decrypted.length-cur) +")");
|
||||
throw new IllegalArgumentException("Invalid size of payload (" + len + ", remaining " + (decrypted.length-cur) +")");
|
||||
//byte hashval[] = new byte[Hash.HASH_LENGTH];
|
||||
//System.arraycopy(decrypted, cur, hashval, 0, Hash.HASH_LENGTH);
|
||||
//readHash = new Hash();
|
||||
@@ -379,8 +379,8 @@ public class ElGamalAESEngine {
|
||||
return unencrData;
|
||||
}
|
||||
|
||||
throw new Exception("Hash does not match");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Hash does not match");
|
||||
} catch (RuntimeException e) {
|
||||
if (_log.shouldLog(Log.WARN)) _log.warn("Unable to decrypt AES block", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public enum EncType {
|
||||
return true;
|
||||
try {
|
||||
getParams();
|
||||
} catch (Exception e) {
|
||||
} catch (InvalidParameterSpecException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -56,11 +56,9 @@ import net.i2p.util.RandomSource;
|
||||
* @author jrandom
|
||||
*/
|
||||
public class KeyGenerator {
|
||||
//private final Log _log;
|
||||
private final I2PAppContext _context;
|
||||
|
||||
public KeyGenerator(I2PAppContext context) {
|
||||
//_log = context.logManager().getLog(KeyGenerator.class);
|
||||
_context = context;
|
||||
}
|
||||
|
||||
@@ -85,7 +83,6 @@ public class KeyGenerator {
|
||||
/**
|
||||
* PBE the passphrase with the salt.
|
||||
* Warning - SLOW
|
||||
* Deprecated - Used by Syndie only.
|
||||
*/
|
||||
public SessionKey generateSessionKey(byte salt[], byte passphrase[]) {
|
||||
byte salted[] = new byte[16+passphrase.length];
|
||||
@@ -122,6 +119,7 @@ public class KeyGenerator {
|
||||
/**
|
||||
* @deprecated use getElGamalExponentSize() which allows override in the properties
|
||||
*/
|
||||
@Deprecated
|
||||
public static final int PUBKEY_EXPONENT_SIZE = DEFAULT_USE_LONG_EXPONENT ?
|
||||
PUBKEY_EXPONENT_SIZE_FULL :
|
||||
PUBKEY_EXPONENT_SIZE_SHORT;
|
||||
@@ -228,7 +226,7 @@ public class KeyGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic signature type, supports DSA and ECDSA
|
||||
* Generic signature type, supports DSA, ECDSA, EdDSA
|
||||
* @since 0.9.9
|
||||
*/
|
||||
public SimpleDataStructure[] generateSigningKeys(SigType type) throws GeneralSecurityException {
|
||||
@@ -343,7 +341,7 @@ public class KeyGenerator {
|
||||
public static void main(String args[]) {
|
||||
try {
|
||||
main2(args);
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -381,7 +379,7 @@ public class KeyGenerator {
|
||||
try {
|
||||
System.out.println("Testing " + type);
|
||||
testSig(type, runs);
|
||||
} catch (Exception e) {
|
||||
} catch (GeneralSecurityException e) {
|
||||
System.out.println("error testing " + type);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateExpiredException;
|
||||
import java.security.cert.CertificateNotYetValidException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Locale;
|
||||
@@ -31,11 +31,46 @@ import net.i2p.util.SystemVersion;
|
||||
*/
|
||||
public class KeyStoreUtil {
|
||||
|
||||
public static boolean _blacklistLogged;
|
||||
|
||||
public static final String DEFAULT_KEYSTORE_PASSWORD = "changeit";
|
||||
private static final String DEFAULT_KEY_ALGORITHM = "RSA";
|
||||
private static final int DEFAULT_KEY_SIZE = 2048;
|
||||
private static final int DEFAULT_KEY_VALID_DAYS = 3652; // 10 years
|
||||
|
||||
/**
|
||||
* No reports of some of these in a Java keystore but just to be safe...
|
||||
* CNNIC ones are in Ubuntu keystore.
|
||||
*/
|
||||
private static final BigInteger[] BLACKLIST_SERIAL = new BigInteger[] {
|
||||
// CNNIC https://googleonlinesecurity.blogspot.com/2015/03/maintaining-digital-certificate-security.html
|
||||
new BigInteger("49:33:00:01".replace(":", ""), 16),
|
||||
// CNNIC EV root https://bugzilla.mozilla.org/show_bug.cgi?id=607208
|
||||
new BigInteger("48:9f:00:01".replace(":", ""), 16),
|
||||
// Superfish http://blog.erratasec.com/2015/02/extracting-superfish-certificate.html
|
||||
new BigInteger("d2:fc:13:87:a9:44:dc:e7".replace(":", ""), 16),
|
||||
// eDellRoot https://www.reddit.com/r/technology/comments/3twmfv/dell_ships_laptops_with_rogue_root_ca_exactly/
|
||||
new BigInteger("6b:c5:7b:95:18:93:aa:97:4b:62:4a:c0:88:fc:3b:b6".replace(":", ""), 16),
|
||||
// DSDTestProvider https://blog.hboeck.de/archives/876-Superfish-2.0-Dangerous-Certificate-on-Dell-Laptops-breaks-encrypted-HTTPS-Connections.html
|
||||
// serial number is actually negative; hex string as reported by certtool below
|
||||
//new BigInteger("a4:4c:38:47:f8:ee:71:80:43:4d:b1:80:b9:a7:e9:62".replace(":", ""), 16)
|
||||
new BigInteger("-5b:b3:c7:b8:07:11:8e:7f:bc:b2:4e:7f:46:58:16:9e".replace(":", ""), 16)
|
||||
};
|
||||
|
||||
/**
|
||||
* Corresponding issuer CN for the serial number.
|
||||
* Must be same number of entries as BLACKLIST_SERIAL.
|
||||
* See removeBlacklistedCerts() below for alternatives if we want
|
||||
* to blacklist a cert without an issuer CN.
|
||||
*/
|
||||
private static final String[] BLACKLIST_ISSUER_CN = new String[] {
|
||||
"CNNIC ROOT",
|
||||
"China Internet Network Information Center EV Certificates Root",
|
||||
"Superfish, Inc.",
|
||||
"eDellRoot",
|
||||
"DSDTestProvider"
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new KeyStore object, and load it from ksFile if it is
|
||||
* non-null and it exists.
|
||||
@@ -63,6 +98,8 @@ public class KeyStoreUtil {
|
||||
if (ksFile != null && !exists) {
|
||||
OutputStream fos = null;
|
||||
try {
|
||||
// must be initted
|
||||
ks.load(null, DEFAULT_KEYSTORE_PASSWORD.toCharArray());
|
||||
fos = new SecureFileOutputStream(ksFile);
|
||||
ks.store(fos, pwchars);
|
||||
} finally {
|
||||
@@ -98,7 +135,8 @@ public class KeyStoreUtil {
|
||||
try {
|
||||
ks.load(null, DEFAULT_KEYSTORE_PASSWORD.toCharArray());
|
||||
success = addCerts(new File(System.getProperty("java.home"), "etc/security/cacerts"), ks) > 0;
|
||||
} catch (Exception e) {}
|
||||
} catch (IOException e) {
|
||||
} catch (GeneralSecurityException e) {}
|
||||
} else {
|
||||
success = loadCerts(new File(System.getProperty("java.home"), "etc/security/cacerts.bks"), ks);
|
||||
}
|
||||
@@ -109,11 +147,14 @@ public class KeyStoreUtil {
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
if (success) {
|
||||
removeBlacklistedCerts(ks);
|
||||
} else {
|
||||
try {
|
||||
// must be initted
|
||||
ks.load(null, DEFAULT_KEYSTORE_PASSWORD.toCharArray());
|
||||
} catch (Exception e) {}
|
||||
} catch (IOException e) {
|
||||
} catch (GeneralSecurityException e) {}
|
||||
error("All key store loads failed, will only load local certificates", null);
|
||||
}
|
||||
return ks;
|
||||
@@ -140,13 +181,15 @@ public class KeyStoreUtil {
|
||||
try {
|
||||
// not clear if null is allowed for password
|
||||
ks.load(null, DEFAULT_KEYSTORE_PASSWORD.toCharArray());
|
||||
} catch (Exception foo) {}
|
||||
} catch (IOException foo) {
|
||||
} catch (GeneralSecurityException e) {}
|
||||
return false;
|
||||
} catch (IOException ioe) {
|
||||
error("KeyStore load error, no default keys: " + file.getAbsolutePath(), ioe);
|
||||
try {
|
||||
ks.load(null, DEFAULT_KEYSTORE_PASSWORD.toCharArray());
|
||||
} catch (Exception foo) {}
|
||||
} catch (IOException foo) {
|
||||
} catch (GeneralSecurityException e) {}
|
||||
return false;
|
||||
} finally {
|
||||
try { if (fis != null) fis.close(); } catch (IOException foo) {}
|
||||
@@ -167,11 +210,66 @@ public class KeyStoreUtil {
|
||||
for(Enumeration<String> e = ks.aliases(); e.hasMoreElements();) {
|
||||
String alias = e.nextElement();
|
||||
if (ks.isCertificateEntry(alias)) {
|
||||
info("Found cert " + alias);
|
||||
//info("Found cert " + alias);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
} catch (Exception foo) {}
|
||||
} catch (GeneralSecurityException e) {}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all blacklisted X509 Certs in a key store.
|
||||
* Match by serial number and issuer CN, which should uniquely identify a cert,
|
||||
* if the CN is present. Should be faster than fingerprints.
|
||||
*
|
||||
* @return number successfully removed
|
||||
* @since 0.9.24
|
||||
*/
|
||||
private static int removeBlacklistedCerts(KeyStore ks) {
|
||||
// This matches on the CN in the issuer,
|
||||
// and we can't do that on Android.
|
||||
// We could just match the whole string, and we will have to
|
||||
// if we want do it on Android or match a cert that has an issuer without a CN.
|
||||
// Or, most certs that don't have a CN have an OU, that could be a fallback.
|
||||
// Or do sha1hash(cert.getEncoded()) but that would be slower.
|
||||
if (SystemVersion.isAndroid())
|
||||
return 0;
|
||||
int count = 0;
|
||||
try {
|
||||
for(Enumeration<String> e = ks.aliases(); e.hasMoreElements();) {
|
||||
String alias = e.nextElement();
|
||||
if (ks.isCertificateEntry(alias)) {
|
||||
Certificate c = ks.getCertificate(alias);
|
||||
if (c != null && (c instanceof X509Certificate)) {
|
||||
X509Certificate xc = (X509Certificate) c;
|
||||
BigInteger serial = xc.getSerialNumber();
|
||||
for (int i = 0; i < BLACKLIST_SERIAL.length; i++) {
|
||||
// debug:
|
||||
//String xname = CertUtil.getIssuerValue(xc, "CN");
|
||||
//info("Found \"" + xname + "\" s/n: " + serial.toString(16));
|
||||
//if (xname == null)
|
||||
// info("name is null, full issuer: " + xc.getIssuerX500Principal().getName());
|
||||
if (BLACKLIST_SERIAL[i].equals(serial)) {
|
||||
String name = CertUtil.getIssuerValue(xc, "CN");
|
||||
if (BLACKLIST_ISSUER_CN[i].equals(name)) {
|
||||
ks.deleteEntry(alias);
|
||||
count++;
|
||||
if (!_blacklistLogged) {
|
||||
// should this be a logAlways?
|
||||
warn("Ignoring blacklisted certificate \"" + alias +
|
||||
"\" issued by: \"" + name +
|
||||
"\" s/n: " + serial.toString(16), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (GeneralSecurityException e) {}
|
||||
if (count > 0)
|
||||
_blacklistLogged = true;
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -198,7 +296,8 @@ public class KeyStoreUtil {
|
||||
String alias = f.getName().toLowerCase(Locale.US);
|
||||
if (alias.endsWith(".crt") || alias.endsWith(".pem") || alias.endsWith(".key") ||
|
||||
alias.endsWith(".der") || alias.endsWith(".key") || alias.endsWith(".p7b") ||
|
||||
alias.endsWith(".p7c") || alias.endsWith(".pfx") || alias.endsWith(".p12"))
|
||||
alias.endsWith(".p7c") || alias.endsWith(".pfx") || alias.endsWith(".p12") ||
|
||||
alias.endsWith(".cer"))
|
||||
alias = alias.substring(0, alias.length() - 4);
|
||||
boolean success = addCert(f, alias, ks);
|
||||
if (success)
|
||||
@@ -217,45 +316,32 @@ public class KeyStoreUtil {
|
||||
* @since 0.8.2, moved from SSLEepGet in 0.9.9
|
||||
*/
|
||||
public static boolean addCert(File file, String alias, KeyStore ks) {
|
||||
InputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(file);
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
X509Certificate cert = (X509Certificate)cf.generateCertificate(fis);
|
||||
X509Certificate cert = CertUtil.loadCert(file);
|
||||
info("Read X509 Certificate from " + file.getAbsolutePath() +
|
||||
" Issuer: " + cert.getIssuerX500Principal() +
|
||||
" Serial: " + cert.getSerialNumber().toString(16) +
|
||||
"; Valid From: " + cert.getNotBefore() +
|
||||
" To: " + cert.getNotAfter());
|
||||
try {
|
||||
cert.checkValidity();
|
||||
} catch (CertificateExpiredException cee) {
|
||||
String s = "Rejecting expired X509 Certificate: " + file.getAbsolutePath();
|
||||
// Android often has old system certs
|
||||
if (SystemVersion.isAndroid())
|
||||
warn(s, cee);
|
||||
else
|
||||
error(s, cee);
|
||||
return false;
|
||||
} catch (CertificateNotYetValidException cnyve) {
|
||||
error("Rejecting X509 Certificate not yet valid: " + file.getAbsolutePath(), cnyve);
|
||||
return false;
|
||||
}
|
||||
ks.setCertificateEntry(alias, cert);
|
||||
info("Now trusting X509 Certificate, Issuer: " + cert.getIssuerX500Principal());
|
||||
} catch (CertificateExpiredException cee) {
|
||||
String s = "Rejecting expired X509 Certificate: " + file.getAbsolutePath();
|
||||
// Android often has old system certs
|
||||
if (SystemVersion.isAndroid())
|
||||
warn(s, cee);
|
||||
else
|
||||
error(s, cee);
|
||||
return false;
|
||||
} catch (CertificateNotYetValidException cnyve) {
|
||||
error("Rejecting X509 Certificate not yet valid: " + file.getAbsolutePath(), cnyve);
|
||||
return false;
|
||||
} catch (GeneralSecurityException gse) {
|
||||
error("Error reading X509 Certificate: " + file.getAbsolutePath(), gse);
|
||||
return false;
|
||||
} catch (IOException ioe) {
|
||||
error("Error reading X509 Certificate: " + file.getAbsolutePath(), ioe);
|
||||
return false;
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// java 1.8.0_40-b10, openSUSE
|
||||
// Exception in thread "main" java.lang.IllegalArgumentException: Input byte array has wrong 4-byte ending unit
|
||||
// at java.util.Base64$Decoder.decode0(Base64.java:704)
|
||||
error("Error reading X509 Certificate: " + file.getAbsolutePath(), iae);
|
||||
return false;
|
||||
} finally {
|
||||
try { if (fis != null) fis.close(); } catch (IOException foo) {}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -316,7 +402,10 @@ public class KeyStoreUtil {
|
||||
error("Not overwriting key " + alias + ", already exists in " + ks, null);
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (IOException e) {
|
||||
error("Not overwriting key \"" + alias + "\", already exists in " + ks, e);
|
||||
return false;
|
||||
} catch (GeneralSecurityException e) {
|
||||
error("Not overwriting key \"" + alias + "\", already exists in " + ks, e);
|
||||
return false;
|
||||
}
|
||||
@@ -354,7 +443,10 @@ public class KeyStoreUtil {
|
||||
success = getPrivateKey(ks, ksPW, alias, keyPW) != null;
|
||||
if (!success)
|
||||
error("Key gen failed to get private key", null);
|
||||
} catch (Exception e) {
|
||||
} catch (IOException e) {
|
||||
error("Key gen failed to get private key", e);
|
||||
success = false;
|
||||
} catch (GeneralSecurityException e) {
|
||||
error("Key gen failed to get private key", e);
|
||||
success = false;
|
||||
}
|
||||
@@ -503,9 +595,9 @@ public class KeyStoreUtil {
|
||||
|
||||
/****
|
||||
public static void main(String[] args) {
|
||||
File ksf = (args.length > 0) ? new File(args[0]) : null;
|
||||
try {
|
||||
if (args.length > 0) {
|
||||
File ksf = new File(args[0]);
|
||||
if (ksf != null && !ksf.exists()) {
|
||||
createKeyStore(ksf, DEFAULT_KEYSTORE_PASSWORD);
|
||||
System.out.println("Created empty keystore " + ksf);
|
||||
} else {
|
||||
@@ -514,6 +606,17 @@ public class KeyStoreUtil {
|
||||
System.out.println("Loaded system keystore");
|
||||
int count = countCerts(ks);
|
||||
System.out.println("Found " + count + " certs");
|
||||
if (ksf != null && ksf.isDirectory()) {
|
||||
count = addCerts(ksf, ks);
|
||||
System.out.println("Found " + count + " certs in " + ksf);
|
||||
if (count > 0) {
|
||||
// rerun blacklist as a test
|
||||
_blacklistLogged = false;
|
||||
count = removeBlacklistedCerts(ks);
|
||||
if (count > 0)
|
||||
System.out.println("Found " + count + " blacklisted certs in " + ksf);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
System.out.println("FAIL");
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@ import java.security.GeneralSecurityException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
@@ -978,24 +976,12 @@ public class SU3File {
|
||||
* @since 0.9.15
|
||||
*/
|
||||
private static PublicKey loadKey(File kd) throws IOException {
|
||||
InputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(kd);
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
X509Certificate cert = (X509Certificate)cf.generateCertificate(fis);
|
||||
cert.checkValidity();
|
||||
return cert.getPublicKey();
|
||||
return CertUtil.loadKey(kd);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
IOException ioe = new IOException("cert error");
|
||||
ioe.initCause(gse);
|
||||
throw ioe;
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// java 1.8.0_40-b10, openSUSE
|
||||
IOException ioe = new IOException("cert error");
|
||||
ioe.initCause(iae);
|
||||
throw ioe;
|
||||
} finally {
|
||||
try { if (fis != null) fis.close(); } catch (IOException foo) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.i2p.crypto;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Signature;
|
||||
@@ -11,7 +12,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,11 +196,29 @@ 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) {
|
||||
} catch (GeneralSecurityException e) {
|
||||
return false;
|
||||
} catch (RuntimeException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -344,7 +344,11 @@ riCe6OlAEiNpcc6mMyIYYWFICbrDFTrDR3wXqwc/Jkcx6L5VVWoagpSzbo3yGhc=
|
||||
System.out.println("\r\nPrivate key written to: " + privateKeyFile);
|
||||
System.out.println("Public key written to: " + publicKeyFile);
|
||||
System.out.println("\r\nPublic key: " + signingPublicKey.toBase64() + "\r\n");
|
||||
} catch (Exception e) {
|
||||
} catch (IOException e) {
|
||||
System.err.println("Error writing keys:");
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} catch (DataFormatException e) {
|
||||
System.err.println("Error writing keys:");
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
@@ -758,7 +762,7 @@ riCe6OlAEiNpcc6mMyIYYWFICbrDFTrDR3wXqwc/Jkcx6L5VVWoagpSzbo3yGhc=
|
||||
bytesToSignInputStream = new SequenceInputStream(versionHeaderInputStream, fileInputStream);
|
||||
signature = _context.dsa().sign(bytesToSignInputStream, signingPrivateKey);
|
||||
|
||||
} catch (Exception e) {
|
||||
} catch (IOException e) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error signing", e);
|
||||
|
||||
|
||||
@@ -722,7 +722,7 @@ public class GroupElement implements Serializable {
|
||||
if (!this.repr.equals(ge.repr)) {
|
||||
try {
|
||||
ge = ge.toRep(this.repr);
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -215,7 +215,7 @@ public class Certificate extends DataStructureImpl {
|
||||
throw new DataFormatException("Cert is too small [" + source.length + " off=" + offset + "]");
|
||||
|
||||
int cur = offset;
|
||||
_type = (int)DataHelper.fromLong(source, cur, 1);
|
||||
_type = source[cur] & 0xff;
|
||||
cur++;
|
||||
int length = (int)DataHelper.fromLong(source, cur, 2);
|
||||
cur += 2;
|
||||
|
||||
@@ -37,6 +37,8 @@ import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
@@ -1615,11 +1617,11 @@ public class DataHelper {
|
||||
* NOTE: formatDuration2() recommended in most cases for readability
|
||||
*/
|
||||
public static String formatSize(long bytes) {
|
||||
double val = bytes;
|
||||
float val = bytes;
|
||||
int scale = 0;
|
||||
while (val >= 1024) {
|
||||
while (val >= 1024.0f) {
|
||||
scale++;
|
||||
val /= 1024;
|
||||
val /= 1024.0f;
|
||||
}
|
||||
|
||||
DecimalFormat fmt = new DecimalFormat("##0.00");
|
||||
@@ -1693,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;
|
||||
}
|
||||
@@ -1708,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;
|
||||
}
|
||||
@@ -1888,4 +1890,38 @@ public class DataHelper {
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as s.split(regex) but caches the compiled pattern for speed.
|
||||
* This saves about 10 microseconds (Bulldozer) on subsequent invocations.
|
||||
*
|
||||
* @param s non-null
|
||||
* @param regex non-null
|
||||
* @throws java.util.regex.PatternSyntaxException unchecked
|
||||
* @since 0.9.24
|
||||
*/
|
||||
public static String[] split(String s, String regex) {
|
||||
return split(s, regex, 0);
|
||||
}
|
||||
|
||||
private static final ConcurrentHashMap<String, Pattern> patterns = new ConcurrentHashMap<String, Pattern>();
|
||||
|
||||
/**
|
||||
* Same as s.split(regex, limit) but caches the compiled pattern for speed.
|
||||
* This saves about 10 microseconds (Bulldozer) on subsequent invocations.
|
||||
*
|
||||
* @param s non-null
|
||||
* @param regex non-null
|
||||
* @param limit result threshold
|
||||
* @throws java.util.regex.PatternSyntaxException unchecked
|
||||
* @since 0.9.24
|
||||
*/
|
||||
public static String[] split(String s, String regex, int limit) {
|
||||
Pattern p = patterns.get(regex);
|
||||
if (p == null) {
|
||||
p = Pattern.compile(regex);
|
||||
patterns.putIfAbsent(regex, p);
|
||||
}
|
||||
return p.split(s, limit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
@@ -174,7 +175,10 @@ public class PrivateKeyFile {
|
||||
pkf.write();
|
||||
verifySignature(pkf.getDestination());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (I2PException e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
@@ -358,7 +362,7 @@ public class PrivateKeyFile {
|
||||
HashCash hc;
|
||||
try {
|
||||
hc = HashCash.mintCash(resource, effort);
|
||||
} catch (Exception e) {
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
return null;
|
||||
}
|
||||
System.out.println("Generation took: " + DataHelper.formatDuration(System.currentTimeMillis() - begin));
|
||||
@@ -391,7 +395,9 @@ public class PrivateKeyFile {
|
||||
Destination d2;
|
||||
try {
|
||||
d2 = pkf2.getDestination();
|
||||
} catch (Exception e) {
|
||||
} catch (I2PException e) {
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
if (d2 == null)
|
||||
@@ -500,7 +506,7 @@ public class PrivateKeyFile {
|
||||
long low = Long.MAX_VALUE;
|
||||
try {
|
||||
low = HashCash.estimateTime(hashEffort);
|
||||
} catch (Exception e) {}
|
||||
} catch (NoSuchAlgorithmException e) {}
|
||||
// takes a lot longer than the estimate usually...
|
||||
// maybe because the resource string is much longer than used in the estimate?
|
||||
return "It is estimated that generating a HashCash Certificate with value " + hashEffort +
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ public class I2CPMessageReader {
|
||||
public void run() {
|
||||
try {
|
||||
run2();
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
_log.log(Log.CRIT, "Uncaught I2CP error", e);
|
||||
_listener.readError(I2CPMessageReader.this, e);
|
||||
cancelRunner();
|
||||
@@ -193,7 +193,7 @@ public class I2CPMessageReader {
|
||||
} catch (OutOfMemoryError oom) {
|
||||
// ooms seen here... maybe log and keep going?
|
||||
throw oom;
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
_log.log(Log.CRIT, "Unhandled error reading I2CP stream", e);
|
||||
_listener.disconnected(I2CPMessageReader.this);
|
||||
cancelRunner();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -145,7 +145,7 @@ public class BufferedStatLog implements StatLog {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("writing " + writeStart +"->"+ writeEnd);
|
||||
writeEvents(writeStart, writeEnd);
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
_log.error("error writing " + writeStart +"->"+ writeEnd, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,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;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ import java.net.InetSocketAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.net.URL;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
@@ -24,6 +25,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;
|
||||
|
||||
@@ -270,7 +273,7 @@ public class EepGet {
|
||||
break;
|
||||
} // switch
|
||||
} // while
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
error = true;
|
||||
}
|
||||
@@ -312,22 +315,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('=');
|
||||
URI nameURL = null;
|
||||
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 URI(url);
|
||||
} catch (URISyntaxException e) {
|
||||
System.err.println("Please enter a properly formed URL.");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
String path = nameURL.getRawPath(); // 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -690,24 +723,31 @@ public class EepGet {
|
||||
|
||||
if (_redirectLocation != null) {
|
||||
// we also are here after a 407
|
||||
//try {
|
||||
try {
|
||||
if (_redirectLocation.startsWith("http://")) {
|
||||
_actualURL = _redirectLocation;
|
||||
} else {
|
||||
// the Location: field has been required to be an absolute URI at least since
|
||||
// RFC 1945 (HTTP/1.0 1996), so it isn't clear what the point of this is.
|
||||
// This oddly adds a ":" even if no port, but that seems to work.
|
||||
URL url = new URL(_actualURL);
|
||||
if (_redirectLocation.startsWith("/"))
|
||||
_actualURL = "http://" + url.getHost() + ":" + url.getPort() + _redirectLocation;
|
||||
URI url = new URI(_actualURL);
|
||||
String host = url.getHost();
|
||||
if (host == null)
|
||||
throw new MalformedURLException("Redirected to invalid URL");
|
||||
int port = url.getPort();
|
||||
if (port < 0)
|
||||
port = 80;
|
||||
if (_redirectLocation.startsWith("/"))
|
||||
_actualURL = "http://" + host + ":" + port + _redirectLocation;
|
||||
else
|
||||
// this blows up completely on a redirect to https://, for example
|
||||
_actualURL = "http://" + url.getHost() + ":" + url.getPort() + "/" + _redirectLocation;
|
||||
_actualURL = "http://" + host+ ":" + port + "/" + _redirectLocation;
|
||||
}
|
||||
// an MUE is an IOE
|
||||
//} catch (MalformedURLException mue) {
|
||||
// throw new IOException("Redirected from an invalid URL");
|
||||
//}
|
||||
} catch (URISyntaxException use) {
|
||||
IOException ioe = new MalformedURLException("Redirected to invalid URL");
|
||||
ioe.initCause(use);
|
||||
throw ioe;
|
||||
}
|
||||
|
||||
AuthState as = _authState;
|
||||
if (_responseCode == 407) {
|
||||
@@ -1099,7 +1139,7 @@ public class EepGet {
|
||||
private int handleStatus(String line) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Status line: [" + line.trim() + "]");
|
||||
String[] toks = line.split(" ", 3);
|
||||
String[] toks = DataHelper.split(line, " ", 3);
|
||||
if (toks.length < 2) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("ERR: status "+ line);
|
||||
@@ -1194,10 +1234,12 @@ public class EepGet {
|
||||
if (_shouldProxy) {
|
||||
_proxy = InternalSocket.getSocket(_proxyHost, _proxyPort);
|
||||
} else {
|
||||
//try {
|
||||
URL url = new URL(_actualURL);
|
||||
if ("http".equals(url.getProtocol())) {
|
||||
try {
|
||||
URI url = new URI(_actualURL);
|
||||
if ("http".equals(url.getScheme())) {
|
||||
String host = url.getHost();
|
||||
if (host == null)
|
||||
throw new MalformedURLException("URL is not supported:" + _actualURL);
|
||||
String hostlc = host.toLowerCase(Locale.US);
|
||||
if (hostlc.endsWith(".i2p"))
|
||||
throw new UnknownHostException("I2P addresses must be proxied");
|
||||
@@ -1216,10 +1258,11 @@ public class EepGet {
|
||||
} else {
|
||||
throw new MalformedURLException("URL is not supported:" + _actualURL);
|
||||
}
|
||||
// an MUE is an IOE
|
||||
//} catch (MalformedURLException mue) {
|
||||
// throw new IOException("Request URL is invalid");
|
||||
//}
|
||||
} catch (URISyntaxException use) {
|
||||
IOException ioe = new MalformedURLException("Request URL is invalid");
|
||||
ioe.initCause(use);
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
_proxyIn = _proxy.getInputStream();
|
||||
if (!(_proxy instanceof InternalSocket))
|
||||
@@ -1241,13 +1284,20 @@ public class EepGet {
|
||||
boolean post = false;
|
||||
if ( (_postData != null) && (_postData.length() > 0) )
|
||||
post = true;
|
||||
URL url = new URL(_actualURL);
|
||||
URI url;
|
||||
try {
|
||||
url = new URI(_actualURL);
|
||||
} catch (URISyntaxException use) {
|
||||
IOException ioe = new MalformedURLException("Bad URL");
|
||||
ioe.initCause(use);
|
||||
throw ioe;
|
||||
}
|
||||
String host = url.getHost();
|
||||
if (host == null || host.length() <= 0)
|
||||
throw new MalformedURLException("Bad URL, no host");
|
||||
int port = url.getPort();
|
||||
String path = url.getPath();
|
||||
String query = url.getQuery();
|
||||
String path = url.getRawPath();
|
||||
String query = url.getRawQuery();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Requesting " + _actualURL);
|
||||
// RFC 2616 sec 5.1.2 - full URL if proxied, absolute path only if not proxied
|
||||
@@ -1470,7 +1520,7 @@ public class EepGet {
|
||||
String key = null;
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
switch (data[i]) {
|
||||
case '\"':
|
||||
case '"':
|
||||
if (isQuoted) {
|
||||
// keys never quoted
|
||||
if (key != null) {
|
||||
|
||||
@@ -6,7 +6,9 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import gnu.getopt.Getopt;
|
||||
|
||||
@@ -107,7 +109,7 @@ public class EepHead extends EepGet {
|
||||
break;
|
||||
} // switch
|
||||
} // while
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
error = true;
|
||||
}
|
||||
@@ -176,24 +178,31 @@ public class EepHead extends EepGet {
|
||||
|
||||
// Should we even follow redirects for HEAD?
|
||||
if (_redirectLocation != null) {
|
||||
//try {
|
||||
try {
|
||||
if (_redirectLocation.startsWith("http://")) {
|
||||
_actualURL = _redirectLocation;
|
||||
} else {
|
||||
// the Location: field has been required to be an absolute URI at least since
|
||||
// RFC 1945 (HTTP/1.0 1996), so it isn't clear what the point of this is.
|
||||
// This oddly adds a ":" even if no port, but that seems to work.
|
||||
URL url = new URL(_actualURL);
|
||||
if (_redirectLocation.startsWith("/"))
|
||||
_actualURL = "http://" + url.getHost() + ":" + url.getPort() + _redirectLocation;
|
||||
URI url = new URI(_actualURL);
|
||||
String host = url.getHost();
|
||||
if (host == null)
|
||||
throw new MalformedURLException("Redirected to invalid URL");
|
||||
int port = url.getPort();
|
||||
if (port < 0)
|
||||
port = 80;
|
||||
if (_redirectLocation.startsWith("/"))
|
||||
_actualURL = "http://" + host + ":" + port + _redirectLocation;
|
||||
else
|
||||
// this blows up completely on a redirect to https://, for example
|
||||
_actualURL = "http://" + url.getHost() + ":" + url.getPort() + "/" + _redirectLocation;
|
||||
_actualURL = "http://" + host+ ":" + port + "/" + _redirectLocation;
|
||||
}
|
||||
// an MUE is an IOE
|
||||
//} catch (MalformedURLException mue) {
|
||||
// throw new IOException("Redirected from an invalid URL");
|
||||
//}
|
||||
} catch (URISyntaxException use) {
|
||||
IOException ioe = new MalformedURLException("Redirected to invalid URL");
|
||||
ioe.initCause(use);
|
||||
throw ioe;
|
||||
}
|
||||
AuthState as = _authState;
|
||||
if (_responseCode == 407) {
|
||||
if (!_shouldProxy)
|
||||
@@ -252,11 +261,20 @@ public class EepHead extends EepGet {
|
||||
@Override
|
||||
protected String getRequest() throws IOException {
|
||||
StringBuilder buf = new StringBuilder(512);
|
||||
URL url = new URL(_actualURL);
|
||||
URI url;
|
||||
try {
|
||||
url = new URI(_actualURL);
|
||||
} catch (URISyntaxException use) {
|
||||
IOException ioe = new MalformedURLException("Bad URL");
|
||||
ioe.initCause(use);
|
||||
throw ioe;
|
||||
}
|
||||
String host = url.getHost();
|
||||
if (host == null)
|
||||
throw new MalformedURLException("Bad URL");
|
||||
int port = url.getPort();
|
||||
String path = url.getPath();
|
||||
String query = url.getQuery();
|
||||
String path = url.getRawPath();
|
||||
String query = url.getRawQuery();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Requesting " + _actualURL);
|
||||
// RFC 2616 sec 5.1.2 - full URL if proxied, absolute path only if not proxied
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -11,6 +11,7 @@ package net.i2p.util;
|
||||
|
||||
import gnu.crypto.prng.AsyncFortunaStandalone;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
@@ -266,7 +267,7 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste
|
||||
synchronized(_fortuna) {
|
||||
_fortuna.addRandomBytes(data, offset, len);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
// AIOOBE seen, root cause unknown, ticket #1576
|
||||
Log log = _context.logManager().getLog(FortunaRandomSource.class);
|
||||
log.warn("feedEntropy()", e);
|
||||
@@ -290,6 +291,6 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste
|
||||
rand.nextBytes(buf);
|
||||
System.out.write(buf);
|
||||
}
|
||||
} catch (Exception e) { e.printStackTrace(); }
|
||||
} catch (IOException e) { e.printStackTrace(); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -83,6 +83,7 @@ import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.KeyStoreUtil;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
|
||||
import org.apache.http.conn.util.PublicSuffixList;
|
||||
@@ -204,7 +205,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
|
||||
//
|
||||
}));
|
||||
|
||||
/**
|
||||
@@ -435,7 +444,7 @@ public class I2PSSLSocketFactory {
|
||||
try {
|
||||
if (line.charAt(0) == '#')
|
||||
continue;
|
||||
String[] s = line.split(",");
|
||||
String[] s = DataHelper.split(line, ",");
|
||||
String lc = s[0].toLowerCase(Locale.US);
|
||||
tlds.add(lc);
|
||||
i++;
|
||||
|
||||
@@ -14,76 +14,63 @@ import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
/**
|
||||
* In case its useful later...
|
||||
* (e.g. w/ native programatic thread dumping, etc)
|
||||
*
|
||||
* As of 0.9.21, I2PThreads are initialized to NORM_PRIORITY
|
||||
* (not the priority of the creating thread).
|
||||
* 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();
|
||||
setPriority(NORM_PRIORITY);
|
||||
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
|
||||
// _createdBy = new Exception("Created by");
|
||||
}
|
||||
|
||||
public I2PThread(String name) {
|
||||
super(name);
|
||||
setPriority(NORM_PRIORITY);
|
||||
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
|
||||
// _createdBy = new Exception("Created by");
|
||||
}
|
||||
|
||||
public I2PThread(Runnable r) {
|
||||
super(r);
|
||||
setPriority(NORM_PRIORITY);
|
||||
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
|
||||
// _createdBy = new Exception("Created by");
|
||||
}
|
||||
|
||||
public I2PThread(Runnable r, String name) {
|
||||
super(r, name);
|
||||
setPriority(NORM_PRIORITY);
|
||||
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
|
||||
// _createdBy = new Exception("Created by");
|
||||
}
|
||||
|
||||
public I2PThread(Runnable r, String name, boolean isDaemon) {
|
||||
super(r, name);
|
||||
setDaemon(isDaemon);
|
||||
setPriority(NORM_PRIORITY);
|
||||
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
|
||||
// _createdBy = new Exception("Created by");
|
||||
}
|
||||
|
||||
public I2PThread(ThreadGroup g, Runnable r) {
|
||||
super(g, r);
|
||||
setPriority(NORM_PRIORITY);
|
||||
//if ( (_log == null) || (_log.shouldLog(Log.DEBUG)) )
|
||||
// _createdBy = new Exception("Created by");
|
||||
}
|
||||
|
||||
/****
|
||||
private void log(int level, String msg) { log(level, msg, null); }
|
||||
|
||||
private void log(int level, String msg, Throwable t) {
|
||||
// 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.
|
||||
@@ -103,25 +90,18 @@ public class I2PThread extends Thread {
|
||||
System.out.println("Check ulimit -u, /etc/security/limits.conf, or /proc/sys/kernel/threads-max");
|
||||
}
|
||||
oom.printStackTrace();
|
||||
if (!(SystemVersion.isWindows() || SystemVersion.isAndroid()))
|
||||
throw new RuntimeException("Thread could not be started, " +
|
||||
"Check ulimit -u, /etc/security/limits.conf, or /proc/sys/kernel/threads-max", oom);
|
||||
throw new RuntimeException("Thread could not be started", oom);
|
||||
}
|
||||
}
|
||||
|
||||
@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 {
|
||||
@@ -129,18 +109,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -479,9 +478,7 @@ public class LogManager implements Flushable {
|
||||
if (!format.equals(""))
|
||||
fmt.applyPattern(format);
|
||||
// the router sets the JVM time zone to UTC but saves the original here so we can get it
|
||||
String systemTimeZone = _context.getProperty("i2p.systemTimeZone");
|
||||
if (systemTimeZone != null)
|
||||
fmt.setTimeZone(TimeZone.getTimeZone(systemTimeZone));
|
||||
fmt.setTimeZone(SystemVersion.getSystemTimeZone(_context));
|
||||
_dateFormatPattern = format;
|
||||
_dateFormat = fmt;
|
||||
return true;
|
||||
@@ -763,7 +760,7 @@ public class LogManager implements Flushable {
|
||||
|
||||
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();
|
||||
|
||||
@@ -75,7 +75,7 @@ abstract class LogWriterBase implements Runnable {
|
||||
if (_write && shouldReadConfig)
|
||||
rereadConfig();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
System.err.println("Error writing the log: " + e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import freenet.support.CPUInformation.UnknownCPUException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.CryptoConstants;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
* <p>BigInteger that takes advantage of the jbigi library for the modPow operation,
|
||||
@@ -734,7 +735,7 @@ public class NativeBigInteger extends BigInteger {
|
||||
in = new BufferedReader(new InputStreamReader(new FileInputStream("/proc/cpuinfo"), "ISO-8859-1"), 4096);
|
||||
String line = null;
|
||||
while ( (line = in.readLine()) != null) {
|
||||
String[] parts = line.split(":", 2);
|
||||
String[] parts = DataHelper.split(line, ":", 2);
|
||||
if (parts.length < 2)
|
||||
continue;
|
||||
String key = parts[0].trim().toLowerCase(Locale.US);
|
||||
|
||||
@@ -6,7 +6,8 @@ import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Locale;
|
||||
|
||||
import gnu.getopt.Getopt;
|
||||
@@ -106,7 +107,7 @@ public class PartialEepGet extends EepGet {
|
||||
break;
|
||||
} // switch
|
||||
} // while
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
error = true;
|
||||
}
|
||||
@@ -167,13 +168,20 @@ public class PartialEepGet extends EepGet {
|
||||
@Override
|
||||
protected String getRequest() throws IOException {
|
||||
StringBuilder buf = new StringBuilder(2048);
|
||||
URL url = new URL(_actualURL);
|
||||
URI url;
|
||||
try {
|
||||
url = new URI(_actualURL);
|
||||
} catch (URISyntaxException use) {
|
||||
IOException ioe = new MalformedURLException("Bad URL");
|
||||
ioe.initCause(use);
|
||||
throw ioe;
|
||||
}
|
||||
String host = url.getHost();
|
||||
if (host == null || host.length() <= 0)
|
||||
throw new MalformedURLException("Bad URL, no host");
|
||||
int port = url.getPort();
|
||||
String path = url.getPath();
|
||||
String query = url.getQuery();
|
||||
String path = url.getRawPath();
|
||||
String query = url.getRawQuery();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Requesting " + _actualURL);
|
||||
// RFC 2616 sec 5.1.2 - full URL if proxied, absolute path only if not proxied
|
||||
|
||||
@@ -99,6 +99,18 @@ public class PasswordManager {
|
||||
String shash = _context.getProperty(pfx + PROP_SHASH);
|
||||
if (shash == null)
|
||||
return false;
|
||||
return checkHash(shash, pw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check pw against b64 salt+hash, as generated by createHash()
|
||||
*
|
||||
* @param shash b64 string
|
||||
* @param pw plain text non-null, already trimmed
|
||||
* @return if pw verified
|
||||
* @since 0.9.24
|
||||
*/
|
||||
public boolean checkHash(String shash, String pw) {
|
||||
byte[] shashBytes = Base64.decode(shash);
|
||||
if (shashBytes == null || shashBytes.length != SHASH_LENGTH)
|
||||
return false;
|
||||
@@ -110,6 +122,23 @@ public class PasswordManager {
|
||||
return DataHelper.eq(hash, pwHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a salt+hash, to be saved and verified later by verifyHash().
|
||||
*
|
||||
* @param pw plain text non-null, already trimmed
|
||||
* @return salted+hash b64 string
|
||||
* @since 0.9.24
|
||||
*/
|
||||
public String createHash(String pw) {
|
||||
byte[] salt = new byte[SALT_LENGTH];
|
||||
_context.random().nextBytes(salt);
|
||||
byte[] pwHash = _context.keyGenerator().generateSessionKey(salt, DataHelper.getUTF8(pw)).getData();
|
||||
byte[] shashBytes = new byte[SHASH_LENGTH];
|
||||
System.arraycopy(salt, 0, shashBytes, 0, SALT_LENGTH);
|
||||
System.arraycopy(pwHash, 0, shashBytes, SALT_LENGTH, SessionKey.KEYSIZE_BYTES);
|
||||
return Base64.encode(shashBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Either plain or b64
|
||||
*
|
||||
|
||||
@@ -30,6 +30,10 @@ public class PortMapper {
|
||||
public static final String SVC_SMTP = "SMTP";
|
||||
public static final String SVC_POP = "POP3";
|
||||
public static final String SVC_SAM = "SAM";
|
||||
/** @since 0.9.24 */
|
||||
public static final String SVC_SAM_UDP = "SAM-UDP";
|
||||
/** @since 0.9.24 */
|
||||
public static final String SVC_SAM_SSL = "SAM-SSL";
|
||||
public static final String SVC_BOB = "BOB";
|
||||
/** not necessary, already in config? */
|
||||
public static final String SVC_I2CP = "I2CP";
|
||||
@@ -111,7 +115,7 @@ public class PortMapper {
|
||||
* @since 0.9.20
|
||||
*/
|
||||
public void renderStatusHTML(Writer out) throws IOException {
|
||||
List<String> services = new ArrayList(_dir.keySet());
|
||||
List<String> services = new ArrayList<String>(_dir.keySet());
|
||||
out.write("<h2>Port Mapper</h2><table><tr><th>Service<th>Host<th>Port\n");
|
||||
Collections.sort(services);
|
||||
for (String s : services) {
|
||||
|
||||
@@ -124,7 +124,7 @@ public class ResettableGZIPInputStream extends InflaterInputStream {
|
||||
public long getTotalRead() {
|
||||
try {
|
||||
return inf.getBytesRead();
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -136,7 +136,7 @@ public class ResettableGZIPInputStream extends InflaterInputStream {
|
||||
public long getTotalExpanded() {
|
||||
try {
|
||||
return inf.getBytesWritten();
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
// possible NPE in some implementations
|
||||
return 0;
|
||||
}
|
||||
@@ -149,7 +149,7 @@ public class ResettableGZIPInputStream extends InflaterInputStream {
|
||||
public long getRemaining() {
|
||||
try {
|
||||
return inf.getRemaining();
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
// possible NPE in some implementations
|
||||
return 0;
|
||||
}
|
||||
@@ -162,7 +162,7 @@ public class ResettableGZIPInputStream extends InflaterInputStream {
|
||||
public boolean getFinished() {
|
||||
try {
|
||||
return inf.finished();
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
// possible NPE in some implementations
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,8 @@ import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.cert.CertificateException;
|
||||
@@ -179,7 +180,7 @@ public class SSLEepGet extends EepGet {
|
||||
break;
|
||||
} // switch
|
||||
} // while
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
error = true;
|
||||
}
|
||||
@@ -369,7 +370,7 @@ public class SSLEepGet extends EepGet {
|
||||
System.out.println(" Valid To: " + cert.getNotAfter());
|
||||
try {
|
||||
cert.checkValidity();
|
||||
} catch (Exception e) {
|
||||
} catch (GeneralSecurityException e) {
|
||||
System.out.println(" WARNING: Certificate is not currently valid, it cannot be used");
|
||||
}
|
||||
CertUtil.saveCert(cert, new File(name));
|
||||
@@ -553,12 +554,14 @@ public class SSLEepGet extends EepGet {
|
||||
|
||||
String req = getRequest();
|
||||
|
||||
//try {
|
||||
URL url = new URL(_actualURL);
|
||||
String host = null;
|
||||
int port = 0;
|
||||
if ("https".equals(url.getProtocol())) {
|
||||
String host;
|
||||
int port;
|
||||
try {
|
||||
URI url = new URI(_actualURL);
|
||||
if ("https".equals(url.getScheme())) {
|
||||
host = url.getHost();
|
||||
if (host == null)
|
||||
throw new MalformedURLException("Bad URL");
|
||||
if (host.toLowerCase(Locale.US).endsWith(".i2p"))
|
||||
throw new MalformedURLException("I2P addresses unsupported");
|
||||
port = url.getPort();
|
||||
@@ -589,10 +592,11 @@ public class SSLEepGet extends EepGet {
|
||||
} else {
|
||||
throw new MalformedURLException("Only https supported: " + _actualURL);
|
||||
}
|
||||
// an MUE is an IOE
|
||||
//} catch (MalformedURLException mue) {
|
||||
// throw new IOException("Request URL is invalid");
|
||||
//}
|
||||
} catch (URISyntaxException use) {
|
||||
IOException ioe = new MalformedURLException("Redirected to invalid URL");
|
||||
ioe.initCause(use);
|
||||
throw ioe;
|
||||
}
|
||||
|
||||
_proxyIn = _proxy.getInputStream();
|
||||
_proxyOut = _proxy.getOutputStream();
|
||||
|
||||
@@ -51,7 +51,7 @@ public class ShellCommand {
|
||||
*
|
||||
* @author hypercubus
|
||||
*/
|
||||
private class CommandThread extends Thread {
|
||||
private class CommandThread extends I2PAppThread {
|
||||
private final boolean consumeOutput;
|
||||
private final Object shellCommand;
|
||||
private final Result result;
|
||||
@@ -84,7 +84,7 @@ public class ShellCommand {
|
||||
*
|
||||
* @author hypercubus
|
||||
*/
|
||||
private static class StreamConsumer extends Thread {
|
||||
private static class StreamConsumer extends I2PAppThread {
|
||||
private final BufferedReader bufferedReader;
|
||||
|
||||
public StreamConsumer(InputStream inputStream) {
|
||||
@@ -115,7 +115,7 @@ public class ShellCommand {
|
||||
*
|
||||
* @author hypercubus
|
||||
*/
|
||||
private static class StreamReader extends Thread {
|
||||
private static class StreamReader extends I2PAppThread {
|
||||
private final BufferedReader bufferedReader;
|
||||
|
||||
public StreamReader(InputStream inputStream) {
|
||||
@@ -149,7 +149,7 @@ public class ShellCommand {
|
||||
*
|
||||
* @author hypercubus
|
||||
*/
|
||||
private static class StreamWriter extends Thread {
|
||||
private static class StreamWriter extends I2PAppThread {
|
||||
private final BufferedWriter bufferedWriter;
|
||||
|
||||
public StreamWriter(OutputStream outputStream) {
|
||||
@@ -439,7 +439,7 @@ public class ShellCommand {
|
||||
System.out.println("ShellCommand waiting for \"" + name + '\"');
|
||||
try {
|
||||
process.waitFor();
|
||||
} catch (Exception e) {
|
||||
} catch (InterruptedException e) {
|
||||
if (DEBUG) {
|
||||
System.out.println("ShellCommand exception waiting for \"" + name + '\"');
|
||||
e.printStackTrace();
|
||||
@@ -457,7 +457,7 @@ public class ShellCommand {
|
||||
if (process.exitValue() > 0)
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (IOException e) {
|
||||
// probably IOException, file not found from exec()
|
||||
if (DEBUG) {
|
||||
System.out.println("ShellCommand execute exception for \"" + name + '\"');
|
||||
|
||||
@@ -118,11 +118,12 @@ public class SimpleTimer2 {
|
||||
// (new Exception("OWCH! DAMN! Wrong ThreadGroup `" + name +"', `" + rv.getName() + "'")).printStackTrace();
|
||||
// }
|
||||
rv.setDaemon(true);
|
||||
rv.setPriority(Thread.NORM_PRIORITY + 1);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
private ScheduledFuture schedule(TimedEvent t, long timeoutMs) {
|
||||
private ScheduledFuture<?> schedule(TimedEvent t, long timeoutMs) {
|
||||
return _executor.schedule(t, timeoutMs, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@@ -248,7 +249,7 @@ public class SimpleTimer2 {
|
||||
private final SimpleTimer2 _pool;
|
||||
private int _fuzz;
|
||||
protected static final int DEFAULT_FUZZ = 3;
|
||||
private ScheduledFuture _future; // _executor.remove() doesn't work so we have to use this
|
||||
private ScheduledFuture<?> _future; // _executor.remove() doesn't work so we have to use this
|
||||
// ... and I expect cancelling this way is more efficient
|
||||
|
||||
/** state of the current event. All access should be under lock. */
|
||||
@@ -294,7 +295,7 @@ public class SimpleTimer2 {
|
||||
if (timeoutMs <= 0) {
|
||||
// streaming timers do call with timeoutMs == 0
|
||||
if (timeoutMs < 0 && _log.shouldLog(Log.WARN))
|
||||
_log.warn("Timeout <= 0: " + this + " timeout = " + timeoutMs + " state: " + _state);
|
||||
_log.warn("Sched. timeout < 0: " + this + " timeout = " + timeoutMs + " state: " + _state);
|
||||
timeoutMs = 1; // otherwise we may execute before _future is updated, which is fine
|
||||
// except it triggers 'early execution' warning logging
|
||||
}
|
||||
@@ -336,6 +337,11 @@ public class SimpleTimer2 {
|
||||
* two timeouts, else use the later
|
||||
*/
|
||||
public synchronized void reschedule(long timeoutMs, boolean useEarliestTime) {
|
||||
if (timeoutMs <= 0) {
|
||||
if (timeoutMs < 0 && _log.shouldWarn())
|
||||
_log.warn("Resched. timeout < 0: " + this + " timeout = " + timeoutMs + " state: " + _state);
|
||||
timeoutMs = 1;
|
||||
}
|
||||
final long now = System.currentTimeMillis();
|
||||
long oldTimeout;
|
||||
boolean scheduled = _state == TimedEventState.SCHEDULED;
|
||||
@@ -348,6 +354,12 @@ public class SimpleTimer2 {
|
||||
if ((oldTimeout - _fuzz > timeoutMs && useEarliestTime) ||
|
||||
(oldTimeout + _fuzz < timeoutMs && !useEarliestTime)||
|
||||
(!scheduled)) {
|
||||
if (scheduled && oldTimeout <= 5) {
|
||||
// don't reschedule to avoid race
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("not rescheduling to " + timeoutMs + ", about to execute " + this + " in " + oldTimeout);
|
||||
return;
|
||||
}
|
||||
if (scheduled && (now + timeoutMs) < _nextRun) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Re-scheduling: " + this + " timeout = " + timeoutMs + " old timeout was " + oldTimeout + " state: " + _state);
|
||||
@@ -381,10 +393,14 @@ public class SimpleTimer2 {
|
||||
_cancelAfterRun = true;
|
||||
return true;
|
||||
case SCHEDULED:
|
||||
boolean cancelled = _future.cancel(false);
|
||||
// There's probably a race here, where it's cancelled after it's running
|
||||
// The result (if rescheduled) is a dup on the queue, see tickets 1694, 1705
|
||||
// Mitigated by close-to-execution check in reschedule()
|
||||
boolean cancelled = _future.cancel(true);
|
||||
if (cancelled)
|
||||
_state = TimedEventState.CANCELLED;
|
||||
else {} // log something as this could be serious, we remain RUNNING otherwise
|
||||
else
|
||||
_log.error("could not cancel " + this + " to run in " + (_nextRun - System.currentTimeMillis()), new Exception());
|
||||
return cancelled;
|
||||
}
|
||||
return false;
|
||||
@@ -406,6 +422,10 @@ public class SimpleTimer2 {
|
||||
long before = System.currentTimeMillis();
|
||||
long delay = 0;
|
||||
synchronized(this) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
_log.warn("I was interrupted in run, state "+_state+" event "+this);
|
||||
return;
|
||||
}
|
||||
if (_rescheduleAfterRun)
|
||||
throw new IllegalStateException(this + " rescheduleAfterRun cannot be true here");
|
||||
|
||||
@@ -415,13 +435,15 @@ public class SimpleTimer2 {
|
||||
case IDLE: // fall through
|
||||
case RUNNING:
|
||||
throw new IllegalStateException(this + " not possible to be in " + _state);
|
||||
case SCHEDULED: // proceed, switch to IDLE in case I need to reschedule
|
||||
_state = TimedEventState.IDLE;
|
||||
case SCHEDULED:
|
||||
// proceed, will switch to IDLE to reschedule
|
||||
}
|
||||
|
||||
// if I was rescheduled by the user, re-submit myself to the executor.
|
||||
int difference = (int)(_nextRun - before); // careful with long uptimes
|
||||
long difference = _nextRun - before; // careful with long uptimes
|
||||
if (difference > _fuzz) {
|
||||
// proceed, switch to IDLE to reschedule
|
||||
_state = TimedEventState.IDLE;
|
||||
schedule(difference);
|
||||
return;
|
||||
}
|
||||
@@ -436,10 +458,12 @@ public class SimpleTimer2 {
|
||||
else if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(_pool + " no _future " + this);
|
||||
// This can be an incorrect warning especially after a schedule(0)
|
||||
if (_log.shouldLog(Log.WARN) && delay > 100)
|
||||
_log.warn(_pool + " early execution " + delay + ": " + this);
|
||||
else if (_log.shouldLog(Log.WARN) && delay < -1000)
|
||||
_log.warn(" late execution " + (0 - delay) + ": " + this + _pool.debug());
|
||||
if (_log.shouldWarn()) {
|
||||
if (delay > 100)
|
||||
_log.warn(_pool + " early execution " + delay + ": " + this);
|
||||
else if (delay < -1000)
|
||||
_log.warn(" late execution " + (0 - delay) + ": " + this + _pool.debug());
|
||||
}
|
||||
try {
|
||||
timeReached();
|
||||
} catch (Throwable t) {
|
||||
|
||||
@@ -5,6 +5,9 @@ package net.i2p.util;
|
||||
*/
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Methods to find out what system we are running on
|
||||
@@ -18,6 +21,8 @@ public abstract class SystemVersion {
|
||||
private static final boolean _isArm = System.getProperty("os.arch").startsWith("arm");
|
||||
private static final boolean _isX86 = System.getProperty("os.arch").contains("86") ||
|
||||
System.getProperty("os.arch").equals("amd64");
|
||||
private static final boolean _isGentoo = System.getProperty("os.version").contains("gentoo") ||
|
||||
System.getProperty("os.version").contains("hardened"); // Funtoo
|
||||
private static final boolean _isAndroid;
|
||||
private static final boolean _isApache;
|
||||
private static final boolean _isGNU;
|
||||
@@ -27,6 +32,7 @@ public abstract class SystemVersion {
|
||||
private static final boolean _oneDotSix;
|
||||
private static final boolean _oneDotSeven;
|
||||
private static final boolean _oneDotEight;
|
||||
private static final boolean _oneDotNine;
|
||||
private static final int _androidSDK;
|
||||
|
||||
static {
|
||||
@@ -62,10 +68,12 @@ public abstract class SystemVersion {
|
||||
_oneDotSix = _androidSDK >= 9;
|
||||
_oneDotSeven = _androidSDK >= 19;
|
||||
_oneDotEight = false;
|
||||
_oneDotNine = false;
|
||||
} else {
|
||||
_oneDotSix = VersionComparator.comp(System.getProperty("java.version"), "1.6") >= 0;
|
||||
_oneDotSeven = _oneDotSix && VersionComparator.comp(System.getProperty("java.version"), "1.7") >= 0;
|
||||
_oneDotEight = _oneDotSeven && VersionComparator.comp(System.getProperty("java.version"), "1.8") >= 0;
|
||||
_oneDotNine = _oneDotEight && VersionComparator.comp(System.getProperty("java.version"), "1.9") >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +103,13 @@ public abstract class SystemVersion {
|
||||
return _isGNU;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.23
|
||||
*/
|
||||
public static boolean isGentoo() {
|
||||
return _isGentoo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.8
|
||||
*/
|
||||
@@ -139,6 +154,15 @@ public abstract class SystemVersion {
|
||||
return _oneDotEight;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return true if Java 1.9 or higher, false for Android.
|
||||
* @since 0.9.23
|
||||
*/
|
||||
public static boolean isJava9() {
|
||||
return _oneDotNine;
|
||||
}
|
||||
|
||||
/**
|
||||
* This isn't always correct.
|
||||
* http://stackoverflow.com/questions/807263/how-do-i-detect-which-kind-of-jre-is-installed-32bit-vs-64bit
|
||||
@@ -181,4 +205,59 @@ public abstract class SystemVersion {
|
||||
maxMemory = 96*1024*1024l;
|
||||
return maxMemory;
|
||||
}
|
||||
|
||||
/**
|
||||
* The system's time zone, which is probably different from the
|
||||
* JVM time zone, because Router changes the JVM default to GMT.
|
||||
* It saves the old default in the context properties where we can get it.
|
||||
* Use this to format a time in local time zone with DateFormat.setTimeZone().
|
||||
*
|
||||
* @return non-null
|
||||
* @since 0.9.24
|
||||
*/
|
||||
public static TimeZone getSystemTimeZone() {
|
||||
return getSystemTimeZone(I2PAppContext.getGlobalContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* The system's time zone, which is probably different from the
|
||||
* JVM time zone, because Router changes the JVM default to GMT.
|
||||
* It saves the old default in the context properties where we can get it.
|
||||
* Use this to format a time in local time zone with DateFormat.setTimeZone().
|
||||
*
|
||||
* @return non-null
|
||||
* @since 0.9.24
|
||||
*/
|
||||
public static TimeZone getSystemTimeZone(I2PAppContext ctx) {
|
||||
String systemTimeZone = ctx.getProperty("i2p.systemTimeZone");
|
||||
if (systemTimeZone != null)
|
||||
return TimeZone.getTimeZone(systemTimeZone);
|
||||
return TimeZone.getDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.24
|
||||
*/
|
||||
/****
|
||||
public static void main(String[] args) {
|
||||
System.out.println("64 bit : " + is64Bit());
|
||||
System.out.println("Java 6 : " + isJava6());
|
||||
System.out.println("Java 7 : " + isJava7());
|
||||
System.out.println("Java 8 : " + isJava8());
|
||||
System.out.println("Java 9 : " + isJava9());
|
||||
System.out.println("Android : " + isAndroid());
|
||||
if (isAndroid())
|
||||
System.out.println(" Version: " + getAndroidVersion());
|
||||
System.out.println("Apache : " + isApache());
|
||||
System.out.println("ARM : " + isARM());
|
||||
System.out.println("Mac : " + isMac());
|
||||
System.out.println("Gentoo : " + isGentoo());
|
||||
System.out.println("GNU : " + isGNU());
|
||||
System.out.println("Windows : " + isWindows());
|
||||
System.out.println("Wrapper : " + hasWrapper());
|
||||
System.out.println("x86 : " + isX86());
|
||||
System.out.println("Max mem : " + getMaxMemory());
|
||||
|
||||
}
|
||||
****/
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ public abstract class Translate {
|
||||
* The {0} will be replaced by the parameter.
|
||||
* Single quotes must be doubled, i.e. ' -> '' in the string.
|
||||
* @param o parameter, not translated.
|
||||
* To tranlslate parameter also, use _t("foo {0} bar", _t("baz"))
|
||||
* To translate parameter also, use _t("foo {0} bar", _t("baz"))
|
||||
* Do not double the single quotes in the parameter.
|
||||
* Use autoboxing to call with ints, longs, floats, etc.
|
||||
*/
|
||||
|
||||
@@ -147,7 +147,7 @@ public class BlockFile implements Closeable {
|
||||
bf.bfck(true);
|
||||
bf.close();
|
||||
raif.close();
|
||||
} catch (Exception e) {
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user