forked from I2P_Developers/i2p.i2p
I2CP: New Blinding Info message (proposal 123)
client- and router-side support new session lookupDest2() method return new b33 failure codes from lookup show b33 alternates in tools stub out support in HTTP client
This commit is contained in:
@ -24,12 +24,15 @@ import net.i2p.app.ClientApp;
|
|||||||
import net.i2p.app.ClientAppManager;
|
import net.i2p.app.ClientAppManager;
|
||||||
import net.i2p.app.Outproxy;
|
import net.i2p.app.Outproxy;
|
||||||
import net.i2p.client.I2PSession;
|
import net.i2p.client.I2PSession;
|
||||||
|
import net.i2p.client.LookupResult;
|
||||||
import net.i2p.client.streaming.I2PSocket;
|
import net.i2p.client.streaming.I2PSocket;
|
||||||
import net.i2p.client.streaming.I2PSocketManager;
|
import net.i2p.client.streaming.I2PSocketManager;
|
||||||
import net.i2p.client.streaming.I2PSocketOptions;
|
import net.i2p.client.streaming.I2PSocketOptions;
|
||||||
|
import net.i2p.crypto.Blinding;
|
||||||
import net.i2p.crypto.SHA256Generator;
|
import net.i2p.crypto.SHA256Generator;
|
||||||
import net.i2p.data.Base32;
|
import net.i2p.data.Base32;
|
||||||
import net.i2p.data.Base64;
|
import net.i2p.data.Base64;
|
||||||
|
import net.i2p.data.BlindData;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
@ -1231,7 +1234,25 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
} else if (len >= 64) {
|
} else if (len >= 64) {
|
||||||
if (_log.shouldInfo())
|
if (_log.shouldInfo())
|
||||||
_log.info("lookup b33 in-session " + destination);
|
_log.info("lookup b33 in-session " + destination);
|
||||||
clientDest = sess.lookupDest(destination, 20*1000);
|
try {
|
||||||
|
BlindData bd = Blinding.decode(_context, destination);
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Resolved b33 " + bd);
|
||||||
|
// TESTING
|
||||||
|
//sess.sendBlindingInfo(bd, 24*60*60*1000);
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Unable to resolve b33 " + destination, iae);
|
||||||
|
// TODO new error page
|
||||||
|
}
|
||||||
|
LookupResult lresult = sess.lookupDest2(destination, 20*1000);
|
||||||
|
clientDest = lresult.getDestination();
|
||||||
|
int code = lresult.getResultCode();
|
||||||
|
if (code != 0) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Unable to resolve b33 " + destination + " error code " + code);
|
||||||
|
// TODO new form
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// 61-63 chars, this won't work
|
// 61-63 chars, this won't work
|
||||||
clientDest = _context.namingService().lookup(destination);
|
clientDest = _context.namingService().lookup(destination);
|
||||||
|
@ -14,6 +14,7 @@ import java.util.List;
|
|||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.i2p.data.BlindData;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.data.PrivateKey;
|
import net.i2p.data.PrivateKey;
|
||||||
@ -409,6 +410,17 @@ public interface I2PSession {
|
|||||||
*/
|
*/
|
||||||
public Destination lookupDest(String name, long maxWait) throws I2PSessionException;
|
public Destination lookupDest(String name, long maxWait) throws I2PSessionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask the router to lookup a Destination by host name.
|
||||||
|
* Blocking. See above for details.
|
||||||
|
* Same as lookupDest() but with a failure code in the return value
|
||||||
|
*
|
||||||
|
* @param maxWait ms
|
||||||
|
* @since 0.9.43
|
||||||
|
* @return non-null
|
||||||
|
*/
|
||||||
|
public LookupResult lookupDest2(String name, long maxWait) throws I2PSessionException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pass updated options to the router.
|
* Pass updated options to the router.
|
||||||
* Does not remove properties previously present but missing from this options parameter.
|
* Does not remove properties previously present but missing from this options parameter.
|
||||||
@ -425,6 +437,13 @@ public interface I2PSession {
|
|||||||
*/
|
*/
|
||||||
public int[] bandwidthLimits() throws I2PSessionException;
|
public int[] bandwidthLimits() throws I2PSessionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param expiration ms from now, 0 means forever
|
||||||
|
* @since 0.9.43
|
||||||
|
*/
|
||||||
|
public void sendBlindingInfo(BlindData bd, int expiration) throws I2PSessionException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen on specified protocol and port.
|
* Listen on specified protocol and port.
|
||||||
*
|
*
|
||||||
|
53
core/java/src/net/i2p/client/LookupResult.java
Normal file
53
core/java/src/net/i2p/client/LookupResult.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package net.i2p.client;
|
||||||
|
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.i2cp.HostReplyMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The return value of I2PSession.lookupDest2()
|
||||||
|
*
|
||||||
|
* @since 0.9.43
|
||||||
|
*/
|
||||||
|
public interface LookupResult {
|
||||||
|
|
||||||
|
/** getDestination() will be non-null */
|
||||||
|
public static final int RESULT_SUCCESS = HostReplyMessage.RESULT_SUCCESS;
|
||||||
|
|
||||||
|
/** general failure, probably a local hostname lookup failure, or a b32 lookup timeout */
|
||||||
|
public static final int RESULT_FAILURE = HostReplyMessage.RESULT_FAILURE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* b33 requires a lookup password but the router does not have it cached;
|
||||||
|
* please supply in a blinding info message
|
||||||
|
*/
|
||||||
|
public static final int RESULT_SECRET_REQUIRED = HostReplyMessage.RESULT_SECRET_REQUIRED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* b33 requires per-client auth private key but the router does not have it cached;
|
||||||
|
* please supply in a blinding info message
|
||||||
|
*/
|
||||||
|
public static final int RESULT_KEY_REQUIRED = HostReplyMessage.RESULT_KEY_REQUIRED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* b33 requires a lookup password and per-client auth private key but the router does not have them cached;
|
||||||
|
* please supply in a blinding info message
|
||||||
|
*/
|
||||||
|
public static final int RESULT_SECRET_AND_KEY_REQUIRED = HostReplyMessage.RESULT_SECRET_AND_KEY_REQUIRED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* b33 requires per-client auth private key, the router has a key, but decryption failed;
|
||||||
|
* please supply a new key in a blinding info message
|
||||||
|
*/
|
||||||
|
public static final int RESULT_DECRYPTION_FAILURE = HostReplyMessage.RESULT_DECRYPTION_FAILURE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return zero for success, nonzero for failure
|
||||||
|
*/
|
||||||
|
public int getResultCode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Destination on success, null on failure
|
||||||
|
*/
|
||||||
|
public Destination getDestination();
|
||||||
|
|
||||||
|
}
|
@ -32,7 +32,7 @@ class HostReplyMessageHandler extends HandlerImpl {
|
|||||||
if (d != null) {
|
if (d != null) {
|
||||||
session.destReceived(id, d);
|
session.destReceived(id, d);
|
||||||
} else {
|
} else {
|
||||||
session.destLookupFailed(id);
|
session.destLookupFailed(id, msg.getResultCode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,9 +38,11 @@ import net.i2p.client.I2PClient;
|
|||||||
import net.i2p.client.I2PSession;
|
import net.i2p.client.I2PSession;
|
||||||
import net.i2p.client.I2PSessionException;
|
import net.i2p.client.I2PSessionException;
|
||||||
import net.i2p.client.I2PSessionListener;
|
import net.i2p.client.I2PSessionListener;
|
||||||
|
import net.i2p.client.LookupResult;
|
||||||
import net.i2p.crypto.EncType;
|
import net.i2p.crypto.EncType;
|
||||||
import net.i2p.crypto.SigType;
|
import net.i2p.crypto.SigType;
|
||||||
import net.i2p.data.Base32;
|
import net.i2p.data.Base32;
|
||||||
|
import net.i2p.data.BlindData;
|
||||||
import net.i2p.data.DataFormatException;
|
import net.i2p.data.DataFormatException;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
@ -50,6 +52,7 @@ import net.i2p.data.PrivateKey;
|
|||||||
import net.i2p.data.Signature;
|
import net.i2p.data.Signature;
|
||||||
import net.i2p.data.SigningPrivateKey;
|
import net.i2p.data.SigningPrivateKey;
|
||||||
import net.i2p.data.SigningPublicKey;
|
import net.i2p.data.SigningPublicKey;
|
||||||
|
import net.i2p.data.i2cp.BlindingInfoMessage;
|
||||||
import net.i2p.data.i2cp.DestLookupMessage;
|
import net.i2p.data.i2cp.DestLookupMessage;
|
||||||
import net.i2p.data.i2cp.DestReplyMessage;
|
import net.i2p.data.i2cp.DestReplyMessage;
|
||||||
import net.i2p.data.i2cp.GetBandwidthLimitsMessage;
|
import net.i2p.data.i2cp.GetBandwidthLimitsMessage;
|
||||||
@ -198,6 +201,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
private volatile boolean _routerSupportsFastReceive;
|
private volatile boolean _routerSupportsFastReceive;
|
||||||
private volatile boolean _routerSupportsHostLookup;
|
private volatile boolean _routerSupportsHostLookup;
|
||||||
private volatile boolean _routerSupportsLS2;
|
private volatile boolean _routerSupportsLS2;
|
||||||
|
private volatile boolean _routerSupportsBlindingInfo;
|
||||||
|
|
||||||
protected static final int CACHE_MAX_SIZE = SystemVersion.isAndroid() ? 32 : 128;
|
protected static final int CACHE_MAX_SIZE = SystemVersion.isAndroid() ? 32 : 128;
|
||||||
/**
|
/**
|
||||||
@ -206,7 +210,8 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
*/
|
*/
|
||||||
private static final Map<Object, Destination> _lookupCache = new LHMCache<Object, Destination>(CACHE_MAX_SIZE);
|
private static final Map<Object, Destination> _lookupCache = new LHMCache<Object, Destination>(CACHE_MAX_SIZE);
|
||||||
private static final String MIN_HOST_LOOKUP_VERSION = "0.9.11";
|
private static final String MIN_HOST_LOOKUP_VERSION = "0.9.11";
|
||||||
private static final boolean TEST_LOOKUP = false;
|
// todo change to false for release
|
||||||
|
private static final boolean TEST_BLINDINFO = true;
|
||||||
|
|
||||||
/** SSL interface (only) @since 0.8.3 */
|
/** SSL interface (only) @since 0.8.3 */
|
||||||
protected static final String PROP_ENABLE_SSL = "i2cp.SSL";
|
protected static final String PROP_ENABLE_SSL = "i2cp.SSL";
|
||||||
@ -225,6 +230,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
|
|
||||||
private static final String MIN_FAST_VERSION = "0.9.4";
|
private static final String MIN_FAST_VERSION = "0.9.4";
|
||||||
private static final String MIN_LS2_VERSION = "0.9.38";
|
private static final String MIN_LS2_VERSION = "0.9.38";
|
||||||
|
private static final String MIN_BLINDINFO_VERSION = "0.9.43";
|
||||||
|
|
||||||
/** @param routerVersion as rcvd in the SetDateMessage, may be null for very old routers */
|
/** @param routerVersion as rcvd in the SetDateMessage, may be null for very old routers */
|
||||||
void dateUpdated(String routerVersion) {
|
void dateUpdated(String routerVersion) {
|
||||||
@ -233,7 +239,6 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
(routerVersion != null && routerVersion.length() > 0 &&
|
(routerVersion != null && routerVersion.length() > 0 &&
|
||||||
VersionComparator.comp(routerVersion, MIN_FAST_VERSION) >= 0);
|
VersionComparator.comp(routerVersion, MIN_FAST_VERSION) >= 0);
|
||||||
_routerSupportsHostLookup = isrc ||
|
_routerSupportsHostLookup = isrc ||
|
||||||
TEST_LOOKUP ||
|
|
||||||
(routerVersion != null && routerVersion.length() > 0 &&
|
(routerVersion != null && routerVersion.length() > 0 &&
|
||||||
VersionComparator.comp(routerVersion, MIN_HOST_LOOKUP_VERSION) >= 0);
|
VersionComparator.comp(routerVersion, MIN_HOST_LOOKUP_VERSION) >= 0);
|
||||||
_routerSupportsSubsessions = isrc ||
|
_routerSupportsSubsessions = isrc ||
|
||||||
@ -242,6 +247,10 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
_routerSupportsLS2 = isrc ||
|
_routerSupportsLS2 = isrc ||
|
||||||
(routerVersion != null && routerVersion.length() > 0 &&
|
(routerVersion != null && routerVersion.length() > 0 &&
|
||||||
VersionComparator.comp(routerVersion, MIN_LS2_VERSION) >= 0);
|
VersionComparator.comp(routerVersion, MIN_LS2_VERSION) >= 0);
|
||||||
|
_routerSupportsBlindingInfo = isrc ||
|
||||||
|
TEST_BLINDINFO ||
|
||||||
|
(routerVersion != null && routerVersion.length() > 0 &&
|
||||||
|
VersionComparator.comp(routerVersion, MIN_BLINDINFO_VERSION) >= 0);
|
||||||
synchronized (_stateLock) {
|
synchronized (_stateLock) {
|
||||||
if (_state == State.OPENING) {
|
if (_state == State.OPENING) {
|
||||||
changeState(State.GOTDATE);
|
changeState(State.GOTDATE);
|
||||||
@ -252,6 +261,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
public static final int LISTEN_PORT = I2PClient.DEFAULT_LISTEN_PORT;
|
public static final int LISTEN_PORT = I2PClient.DEFAULT_LISTEN_PORT;
|
||||||
|
|
||||||
private static final int BUF_SIZE = 32*1024;
|
private static final int BUF_SIZE = 32*1024;
|
||||||
|
private static final SessionId DUMMY_SESSION = new SessionId(65535);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* for extension by SimpleSession (no dest)
|
* for extension by SimpleSession (no dest)
|
||||||
@ -1500,6 +1510,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
if (h.equals(w.hash)) {
|
if (h.equals(w.hash)) {
|
||||||
synchronized (w) {
|
synchronized (w) {
|
||||||
w.destination = d;
|
w.destination = d;
|
||||||
|
w.code = LookupResult.RESULT_SUCCESS;
|
||||||
w.notifyAll();
|
w.notifyAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1515,6 +1526,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
for (LookupWaiter w : _pendingLookups) {
|
for (LookupWaiter w : _pendingLookups) {
|
||||||
if (h.equals(w.hash)) {
|
if (h.equals(w.hash)) {
|
||||||
synchronized (w) {
|
synchronized (w) {
|
||||||
|
w.code = LookupResult.RESULT_FAILURE;
|
||||||
w.notifyAll();
|
w.notifyAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1539,6 +1551,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
}
|
}
|
||||||
synchronized (w) {
|
synchronized (w) {
|
||||||
w.destination = d;
|
w.destination = d;
|
||||||
|
w.code = LookupResult.RESULT_SUCCESS;
|
||||||
w.notifyAll();
|
w.notifyAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1550,10 +1563,11 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
* on reception of HostReplyMessage
|
* on reception of HostReplyMessage
|
||||||
* @since 0.9.11
|
* @since 0.9.11
|
||||||
*/
|
*/
|
||||||
void destLookupFailed(long nonce) {
|
void destLookupFailed(long nonce, int code) {
|
||||||
for (LookupWaiter w : _pendingLookups) {
|
for (LookupWaiter w : _pendingLookups) {
|
||||||
if (nonce == w.nonce) {
|
if (nonce == w.nonce) {
|
||||||
synchronized (w) {
|
synchronized (w) {
|
||||||
|
w.code = code;
|
||||||
w.notifyAll();
|
w.notifyAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1581,6 +1595,11 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
public final long nonce;
|
public final long nonce;
|
||||||
/** the reply; synch on this */
|
/** the reply; synch on this */
|
||||||
public Destination destination;
|
public Destination destination;
|
||||||
|
/**
|
||||||
|
* the return code; sync on this
|
||||||
|
* @since 0.9.43
|
||||||
|
*/
|
||||||
|
public int code;
|
||||||
|
|
||||||
public LookupWaiter(Hash h) {
|
public LookupWaiter(Hash h) {
|
||||||
this(h, -1);
|
this(h, -1);
|
||||||
@ -1599,6 +1618,16 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
this.nonce = nonce;
|
this.nonce = nonce;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Dummy, completed
|
||||||
|
* @since 0.9.43
|
||||||
|
*/
|
||||||
|
public LookupWaiter(Destination d) {
|
||||||
|
hash = null;
|
||||||
|
name = null;
|
||||||
|
nonce = 0;
|
||||||
|
destination = d;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 0.9.20 */
|
/** @since 0.9.20 */
|
||||||
@ -1657,7 +1686,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
_log.info("Sending HostLookup for " + h);
|
_log.info("Sending HostLookup for " + h);
|
||||||
SessionId id = _sessionId;
|
SessionId id = _sessionId;
|
||||||
if (id == null)
|
if (id == null)
|
||||||
id = new SessionId(65535);
|
id = DUMMY_SESSION;
|
||||||
sendMessage_unchecked(new HostLookupMessage(id, h, nonce, maxWait));
|
sendMessage_unchecked(new HostLookupMessage(id, h, nonce, maxWait));
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
@ -1708,12 +1737,50 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
* @return null on failure
|
* @return null on failure
|
||||||
*/
|
*/
|
||||||
public Destination lookupDest(String name, long maxWait) throws I2PSessionException {
|
public Destination lookupDest(String name, long maxWait) throws I2PSessionException {
|
||||||
|
LookupWaiter waiter = x_lookupDest(name, maxWait);
|
||||||
|
if (waiter == null)
|
||||||
|
return null;
|
||||||
|
synchronized(waiter) {
|
||||||
|
return waiter.destination;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask the router to lookup a Destination by host name.
|
||||||
|
* Blocking. See above for details.
|
||||||
|
* Same as lookupDest() but with a failure code in the return value
|
||||||
|
*
|
||||||
|
* @param maxWait ms
|
||||||
|
* @since 0.9.43
|
||||||
|
* @return non-null
|
||||||
|
*/
|
||||||
|
public LookupResult lookupDest2(String name, long maxWait) throws I2PSessionException {
|
||||||
|
LookupWaiter waiter = x_lookupDest(name, maxWait);
|
||||||
|
if (waiter == null)
|
||||||
|
return new LkupResult(LookupResult.RESULT_FAILURE, null);
|
||||||
|
synchronized(waiter) {
|
||||||
|
int code = waiter.code;
|
||||||
|
Destination d = waiter.destination;
|
||||||
|
if (d == null && code == LookupResult.RESULT_SUCCESS)
|
||||||
|
code = LookupResult.RESULT_FAILURE;
|
||||||
|
return new LkupResult(code, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask the router to lookup a Destination by host name.
|
||||||
|
* Blocking. See above for details.
|
||||||
|
* @param maxWait ms
|
||||||
|
* @since 0.9.11
|
||||||
|
* @return null on failure
|
||||||
|
*/
|
||||||
|
private LookupWaiter x_lookupDest(String name, long maxWait) throws I2PSessionException {
|
||||||
if (name.length() == 0)
|
if (name.length() == 0)
|
||||||
return null;
|
return null;
|
||||||
// Shortcut for b64
|
// Shortcut for b64
|
||||||
if (name.length() >= 516) {
|
if (name.length() >= 516) {
|
||||||
try {
|
try {
|
||||||
return new Destination(name);
|
return new LookupWaiter(new Destination(name));
|
||||||
} catch (DataFormatException dfe) {
|
} catch (DataFormatException dfe) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -1724,7 +1791,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
synchronized (_lookupCache) {
|
synchronized (_lookupCache) {
|
||||||
Destination rv = _lookupCache.get(name);
|
Destination rv = _lookupCache.get(name);
|
||||||
if (rv != null)
|
if (rv != null)
|
||||||
return rv;
|
return new LookupWaiter(rv);
|
||||||
}
|
}
|
||||||
if (isClosed()) {
|
if (isClosed()) {
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
@ -1734,7 +1801,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
if (!_routerSupportsHostLookup) {
|
if (!_routerSupportsHostLookup) {
|
||||||
// do them a favor and convert to Hash lookup
|
// do them a favor and convert to Hash lookup
|
||||||
if (name.length() == 60 && name.toLowerCase(Locale.US).endsWith(".b32.i2p"))
|
if (name.length() == 60 && name.toLowerCase(Locale.US).endsWith(".b32.i2p"))
|
||||||
return lookupDest(Hash.create(Base32.decode(name.toLowerCase(Locale.US).substring(0, 52))), maxWait);
|
return new LookupWaiter(lookupDest(Hash.create(Base32.decode(name.toLowerCase(Locale.US).substring(0, 52))), maxWait));
|
||||||
// else unsupported
|
// else unsupported
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Router does not support HostLookup for " + name);
|
_log.warn("Router does not support HostLookup for " + name);
|
||||||
@ -1743,18 +1810,17 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
int nonce = _lookupID.incrementAndGet() & 0x7fffffff;
|
int nonce = _lookupID.incrementAndGet() & 0x7fffffff;
|
||||||
LookupWaiter waiter = new LookupWaiter(name, nonce);
|
LookupWaiter waiter = new LookupWaiter(name, nonce);
|
||||||
_pendingLookups.offer(waiter);
|
_pendingLookups.offer(waiter);
|
||||||
Destination rv = null;
|
|
||||||
try {
|
try {
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info("Sending HostLookup for " + name);
|
_log.info("Sending HostLookup for " + name);
|
||||||
SessionId id = _sessionId;
|
SessionId id = _sessionId;
|
||||||
if (id == null)
|
if (id == null)
|
||||||
id = new SessionId(65535);
|
id = DUMMY_SESSION;
|
||||||
sendMessage_unchecked(new HostLookupMessage(id, name, nonce, maxWait));
|
sendMessage_unchecked(new HostLookupMessage(id, name, nonce, maxWait));
|
||||||
try {
|
try {
|
||||||
synchronized (waiter) {
|
synchronized (waiter) {
|
||||||
waiter.wait(maxWait);
|
waiter.wait(maxWait);
|
||||||
rv = waiter.destination;
|
return waiter;
|
||||||
}
|
}
|
||||||
} catch (InterruptedException ie) {
|
} catch (InterruptedException ie) {
|
||||||
throw new I2PSessionException("Interrupted", ie);
|
throw new I2PSessionException("Interrupted", ie);
|
||||||
@ -1762,7 +1828,6 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
} finally {
|
} finally {
|
||||||
_pendingLookups.remove(waiter);
|
_pendingLookups.remove(waiter);
|
||||||
}
|
}
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1793,6 +1858,22 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
|||||||
return _bwLimits;
|
return _bwLimits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param expiration ms from now, 0 means forever
|
||||||
|
* @since 0.9.43
|
||||||
|
*/
|
||||||
|
public void sendBlindingInfo(BlindData bd, int expiration) throws I2PSessionException {
|
||||||
|
if (!_routerSupportsBlindingInfo)
|
||||||
|
throw new I2PSessionException("Router does not support BlindingInfo");
|
||||||
|
if (_log.shouldInfo())
|
||||||
|
_log.info("Sending BlindingInfo");
|
||||||
|
SessionId id = _sessionId;
|
||||||
|
if (id == null)
|
||||||
|
id = DUMMY_SESSION;
|
||||||
|
sendMessage_unchecked(new BlindingInfoMessage(bd, id, expiration));
|
||||||
|
}
|
||||||
|
|
||||||
protected void updateActivity() {
|
protected void updateActivity() {
|
||||||
_lastActivity = _context.clock().now();
|
_lastActivity = _context.clock().now();
|
||||||
if (_isReduced) {
|
if (_isReduced) {
|
||||||
|
31
core/java/src/net/i2p/client/impl/LkupResult.java
Normal file
31
core/java/src/net/i2p/client/impl/LkupResult.java
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package net.i2p.client.impl;
|
||||||
|
|
||||||
|
import net.i2p.client.LookupResult;
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The return value of I2PSession.lookupDest2()
|
||||||
|
*
|
||||||
|
* @since 0.9.43
|
||||||
|
*/
|
||||||
|
public class LkupResult implements LookupResult {
|
||||||
|
|
||||||
|
private final int _code;
|
||||||
|
private final Destination _dest;
|
||||||
|
|
||||||
|
LkupResult(int code, Destination dest) {
|
||||||
|
_code = code;
|
||||||
|
_dest = dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return zero for success, nonzero for failure
|
||||||
|
*/
|
||||||
|
public int getResultCode() { return _code; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Destination on success, null on failure
|
||||||
|
*/
|
||||||
|
public Destination getDestination() { return _dest; }
|
||||||
|
|
||||||
|
}
|
@ -263,8 +263,8 @@ class SubSession extends I2PSessionMuxedImpl {
|
|||||||
* on reception of HostReplyMessage
|
* on reception of HostReplyMessage
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
void destLookupFailed(long nonce) {
|
void destLookupFailed(long nonce, int code) {
|
||||||
_primary.destLookupFailed(nonce);
|
_primary.destLookupFailed(nonce, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -199,6 +199,7 @@ public final class Blinding {
|
|||||||
* See proposal 149.
|
* See proposal 149.
|
||||||
*
|
*
|
||||||
* @param address ending with ".b32.i2p"
|
* @param address ending with ".b32.i2p"
|
||||||
|
* @return BlindData structure, use getUnblindedPubKey() for the result
|
||||||
* @throws IllegalArgumentException on bad inputs or unsupported SigTypes
|
* @throws IllegalArgumentException on bad inputs or unsupported SigTypes
|
||||||
* @since 0.9.40
|
* @since 0.9.40
|
||||||
*/
|
*/
|
||||||
|
@ -278,6 +278,7 @@ public class BlindData {
|
|||||||
buf.append("[BlindData: ");
|
buf.append("[BlindData: ");
|
||||||
buf.append("\n\tSigningPublicKey: ").append(_clearSPK);
|
buf.append("\n\tSigningPublicKey: ").append(_clearSPK);
|
||||||
buf.append("\n\tAlpha : ").append(_alpha);
|
buf.append("\n\tAlpha : ").append(_alpha);
|
||||||
|
buf.append("\n\tAlpha valid for : ").append((new Date(_date)).toString());
|
||||||
buf.append("\n\tBlindedPublicKey: ").append(_blindSPK);
|
buf.append("\n\tBlindedPublicKey: ").append(_blindSPK);
|
||||||
buf.append("\n\tBlinded Hash : ").append(_blindHash);
|
buf.append("\n\tBlinded Hash : ").append(_blindHash);
|
||||||
if (_secret != null)
|
if (_secret != null)
|
||||||
@ -298,7 +299,12 @@ public class BlindData {
|
|||||||
else
|
else
|
||||||
buf.append("\n\tDestination : unknown");
|
buf.append("\n\tDestination : unknown");
|
||||||
buf.append("\n\tB32 : ").append(toBase32());
|
buf.append("\n\tB32 : ").append(toBase32());
|
||||||
buf.append("\n\tCreated : ").append((new Date(_date)).toString());
|
if (!_authRequired)
|
||||||
|
buf.append("\n\t + auth : ").append(Blinding.encode(_clearSPK, _secretRequired, true));
|
||||||
|
if (!_secretRequired)
|
||||||
|
buf.append("\n\t + secret : ").append(Blinding.encode(_clearSPK, true, _authRequired));
|
||||||
|
if (!(_authRequired || _secretRequired))
|
||||||
|
buf.append("\n\t + auth,secret : ").append(Blinding.encode(_clearSPK, true, true));
|
||||||
buf.append(']');
|
buf.append(']');
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
@ -845,9 +845,9 @@ public class PrivateKeyFile {
|
|||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder s = new StringBuilder(128);
|
StringBuilder s = new StringBuilder(128);
|
||||||
s.append("Dest: ");
|
s.append("Destination: ");
|
||||||
s.append(this.dest != null ? this.dest.toBase64() : "null");
|
s.append(this.dest != null ? this.dest.toBase64() : "null");
|
||||||
s.append("\nB32: ");
|
s.append("\nB32 : ");
|
||||||
s.append(this.dest != null ? this.dest.toBase32() : "null");
|
s.append(this.dest != null ? this.dest.toBase32() : "null");
|
||||||
if (dest != null) {
|
if (dest != null) {
|
||||||
SigningPublicKey spk = dest.getSigningPublicKey();
|
SigningPublicKey spk = dest.getSigningPublicKey();
|
||||||
@ -856,6 +856,9 @@ public class PrivateKeyFile {
|
|||||||
type == SigType.RedDSA_SHA512_Ed25519) {
|
type == SigType.RedDSA_SHA512_Ed25519) {
|
||||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||||
s.append("\nBlinded B32: ").append(Blinding.encode(spk));
|
s.append("\nBlinded B32: ").append(Blinding.encode(spk));
|
||||||
|
s.append("\n + auth key: ").append(Blinding.encode(spk, false, true));
|
||||||
|
s.append("\n + password: ").append(Blinding.encode(spk, true, false));
|
||||||
|
s.append("\n + auth/pw : ").append(Blinding.encode(spk, true, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.append("\nContains: ");
|
s.append("\nContains: ");
|
||||||
|
370
core/java/src/net/i2p/data/i2cp/BlindingInfoMessage.java
Normal file
370
core/java/src/net/i2p/data/i2cp/BlindingInfoMessage.java
Normal file
@ -0,0 +1,370 @@
|
|||||||
|
package net.i2p.data.i2cp;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.crypto.EncType;
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
|
import net.i2p.data.BlindData;
|
||||||
|
import net.i2p.data.DataFormatException;
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.Hash;
|
||||||
|
import net.i2p.data.PrivateKey;
|
||||||
|
import net.i2p.data.SigningPublicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advise the router that the endpoint is blinded.
|
||||||
|
* Client to router. There is no reply.
|
||||||
|
* Preliminary - subject to change -
|
||||||
|
* See proposal 123.
|
||||||
|
*
|
||||||
|
* @see BlindData
|
||||||
|
* @see net.i2p.crypto.Blinding
|
||||||
|
*
|
||||||
|
* @since 0.9.43; do not send to routers older than 0.9.43.
|
||||||
|
*/
|
||||||
|
public class BlindingInfoMessage extends I2CPMessageImpl {
|
||||||
|
public final static int MESSAGE_TYPE = 42;
|
||||||
|
|
||||||
|
private SessionId _sessionId;
|
||||||
|
private int _endpointType;
|
||||||
|
private int _authType;
|
||||||
|
private SigType _blindType;
|
||||||
|
private long _expiration;
|
||||||
|
private Hash _hash;
|
||||||
|
private String _host;
|
||||||
|
private Destination _dest;
|
||||||
|
private SigningPublicKey _pubkey;
|
||||||
|
private PrivateKey _privkey;
|
||||||
|
private String _secret;
|
||||||
|
private BlindData _blindData;
|
||||||
|
|
||||||
|
private static final int FLAG_AUTH = 0x0f;
|
||||||
|
private static final int FLAG_SECRET = 0x10;
|
||||||
|
|
||||||
|
public static final int TYPE_HASH = 0;
|
||||||
|
public static final int TYPE_HOST = 1;
|
||||||
|
public static final int TYPE_DEST = 2;
|
||||||
|
public static final int TYPE_KEY = 3;
|
||||||
|
|
||||||
|
public BlindingInfoMessage() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param expiration ms from now or 0 for forever
|
||||||
|
*/
|
||||||
|
public BlindingInfoMessage(BlindData bd,
|
||||||
|
SessionId id, int expiration) {
|
||||||
|
this(id, expiration, bd.getAuthType(), bd.getBlindedSigType(), bd.getAuthPrivKey(), bd.getSecret());
|
||||||
|
Destination dest = bd.getDestination();
|
||||||
|
if (dest != null) {
|
||||||
|
_dest = dest;
|
||||||
|
_hash = dest.calculateHash();
|
||||||
|
_pubkey = dest.getSigningPublicKey();
|
||||||
|
_endpointType = TYPE_DEST;
|
||||||
|
} else if (bd.getUnblindedPubKey() != null) {
|
||||||
|
_pubkey = bd.getUnblindedPubKey();
|
||||||
|
_endpointType = TYPE_KEY;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
_blindData = bd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param authType 0 (none), 1 (DH), 3 (PSK)
|
||||||
|
* @param expiration ms from now or 0 for forever
|
||||||
|
* @param privKey null for auth none, non-null for DH/PSK
|
||||||
|
* @param secret may be null
|
||||||
|
*/
|
||||||
|
public BlindingInfoMessage(Hash h,
|
||||||
|
SessionId id, int expiration,
|
||||||
|
int authType, SigType blindType,
|
||||||
|
PrivateKey privKey, String secret) {
|
||||||
|
this(id, expiration, authType, blindType, privKey, secret);
|
||||||
|
if (h == null || h.getData() == null)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
_hash = h;
|
||||||
|
_endpointType = TYPE_HASH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param h hostname
|
||||||
|
* @param authType 0 (none), 1 (DH), 3 (PSK)
|
||||||
|
* @param expiration ms from now or 0 for forever
|
||||||
|
* @param privKey null for auth none, non-null for DH/PSK
|
||||||
|
* @param secret may be null
|
||||||
|
*/
|
||||||
|
public BlindingInfoMessage(String h,
|
||||||
|
SessionId id, int expiration,
|
||||||
|
int authType, SigType blindType,
|
||||||
|
PrivateKey privKey, String secret) {
|
||||||
|
this(id, expiration, authType, blindType, privKey, secret);
|
||||||
|
if (h == null)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
_host = h;
|
||||||
|
_endpointType = TYPE_HOST;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param authType 0 (none), 1 (DH), 3 (PSK)
|
||||||
|
* @param expiration ms from now or 0 for forever
|
||||||
|
* @param privKey null for auth none, non-null for DH/PSK
|
||||||
|
* @param secret may be null
|
||||||
|
*/
|
||||||
|
public BlindingInfoMessage(Destination d,
|
||||||
|
SessionId id, int expiration,
|
||||||
|
int authType, SigType blindType,
|
||||||
|
PrivateKey privKey, String secret) {
|
||||||
|
this(id, expiration, authType, blindType, privKey, secret);
|
||||||
|
if (d == null || d.getSigningPublicKey() == null)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
_dest = d;
|
||||||
|
_hash = d.calculateHash();
|
||||||
|
_pubkey = d.getSigningPublicKey();
|
||||||
|
_endpointType = TYPE_DEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param authType 0 (none), 1 (DH), 3 (PSK)
|
||||||
|
* @param expiration ms from now or 0 for forever
|
||||||
|
* @param privKey null for auth none, non-null for DH/PSK
|
||||||
|
* @param secret may be null
|
||||||
|
*/
|
||||||
|
public BlindingInfoMessage(SigningPublicKey s,
|
||||||
|
SessionId id, int expiration,
|
||||||
|
int authType, SigType blindType,
|
||||||
|
PrivateKey privKey, String secret) {
|
||||||
|
this(id, expiration, authType, blindType, privKey, secret);
|
||||||
|
if (s == null || s.getData() == null)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
_pubkey = s;
|
||||||
|
_endpointType = TYPE_KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlindingInfoMessage(SessionId id, int expiration, int authType, SigType blindType,
|
||||||
|
PrivateKey privKey, String secret) {
|
||||||
|
if (id == null || blindType == null)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
if (authType != BlindData.AUTH_NONE && authType != BlindData.AUTH_DH &&
|
||||||
|
authType != BlindData.AUTH_PSK)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
if (authType == BlindData.AUTH_NONE && privKey != null)
|
||||||
|
throw new IllegalArgumentException("no key required");
|
||||||
|
if (authType != BlindData.AUTH_NONE && privKey == null)
|
||||||
|
throw new IllegalArgumentException("key required");
|
||||||
|
_sessionId = id;
|
||||||
|
_authType = authType;
|
||||||
|
_blindType = blindType;
|
||||||
|
if (expiration > 0)
|
||||||
|
_expiration = expiration + I2PAppContext.getGlobalContext().clock().now();
|
||||||
|
_privkey = privKey;
|
||||||
|
_secret = secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionId getSessionId() {
|
||||||
|
return _sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the SessionId for this message.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public SessionId sessionId() {
|
||||||
|
return _sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ms 1 to 2**32 - 1
|
||||||
|
*/
|
||||||
|
public long getTimeout() {
|
||||||
|
return _expiration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 0 (none), 1 (DH), 3 (PSK)
|
||||||
|
*/
|
||||||
|
public int getAuthType() {
|
||||||
|
return _authType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 0 (hash) or 1 (host) or 2 (dest) or 3 (key)
|
||||||
|
*/
|
||||||
|
public int getEndpointType() {
|
||||||
|
return _endpointType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return only valid if endpoint type == 0 or 2
|
||||||
|
*/
|
||||||
|
public Hash getHash() {
|
||||||
|
return _hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return only valid if endpoint type == 1
|
||||||
|
*/
|
||||||
|
public String getHostname() {
|
||||||
|
return _host;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return only valid if endpoint type == 2
|
||||||
|
*/
|
||||||
|
public String getDestination() {
|
||||||
|
return _host;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return only valid if endpoint type == 2 or 3
|
||||||
|
*/
|
||||||
|
public SigningPublicKey getSigningPublicKey() {
|
||||||
|
return _pubkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return private key or null
|
||||||
|
*/
|
||||||
|
public PrivateKey getPrivateKey() {
|
||||||
|
return _privkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return secret or null
|
||||||
|
*/
|
||||||
|
public String getSecret() {
|
||||||
|
return _secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return blind data or null if not enough info
|
||||||
|
*/
|
||||||
|
public BlindData getBlindData() {
|
||||||
|
if (_blindData != null)
|
||||||
|
return _blindData;
|
||||||
|
if (_endpointType == TYPE_DEST)
|
||||||
|
_blindData = new BlindData(I2PAppContext.getGlobalContext(), _dest, _blindType, _secret, _authType, _privkey);
|
||||||
|
else if (_endpointType == TYPE_KEY)
|
||||||
|
_blindData = new BlindData(I2PAppContext.getGlobalContext(), _pubkey, _blindType, _secret, _authType, _privkey);
|
||||||
|
return _blindData;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
|
||||||
|
try {
|
||||||
|
_sessionId = new SessionId();
|
||||||
|
_sessionId.readBytes(in);
|
||||||
|
int flags = in.read();
|
||||||
|
_authType = flags & FLAG_AUTH;
|
||||||
|
boolean hasSecret = (flags & FLAG_SECRET) != 0;
|
||||||
|
_endpointType = in.read();
|
||||||
|
int bt = (int) DataHelper.readLong(in, 2);
|
||||||
|
_blindType = SigType.getByCode(bt);
|
||||||
|
if (_blindType == null)
|
||||||
|
throw new I2CPMessageException("unsupported sig type " + bt);
|
||||||
|
_expiration = DataHelper.readLong(in, 4);
|
||||||
|
if (_endpointType == TYPE_HASH) {
|
||||||
|
_hash = Hash.create(in);
|
||||||
|
} else if (_endpointType == TYPE_HOST) {
|
||||||
|
_host = DataHelper.readString(in);
|
||||||
|
if (_host.length() == 0)
|
||||||
|
throw new I2CPMessageException("bad host");
|
||||||
|
} else if (_endpointType == TYPE_DEST) {
|
||||||
|
_dest = Destination.create(in);
|
||||||
|
} else if (_endpointType == TYPE_KEY) {
|
||||||
|
int st = (int) DataHelper.readLong(in, 2);
|
||||||
|
SigType sigt = SigType.getByCode(st);
|
||||||
|
if (sigt == null)
|
||||||
|
throw new I2CPMessageException("unsupported sig type " + st);
|
||||||
|
int len = sigt.getPubkeyLen();
|
||||||
|
byte[] key = new byte[len];
|
||||||
|
DataHelper.read(in, key);
|
||||||
|
_pubkey = new SigningPublicKey(sigt, key);
|
||||||
|
} else {
|
||||||
|
throw new I2CPMessageException("bad type");
|
||||||
|
}
|
||||||
|
if (_authType == BlindData.AUTH_DH || _authType == BlindData.AUTH_PSK) {
|
||||||
|
byte[] key = new byte[32];
|
||||||
|
DataHelper.read(in, key);
|
||||||
|
_privkey = new PrivateKey(EncType.ECIES_X25519, key);
|
||||||
|
} else if (_authType != BlindData.AUTH_NONE) {
|
||||||
|
throw new I2CPMessageException("bad auth type " + _authType);
|
||||||
|
}
|
||||||
|
if (hasSecret)
|
||||||
|
_secret = DataHelper.readString(in);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
throw new I2CPMessageException("bad data", dfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
|
||||||
|
int len;
|
||||||
|
if (_endpointType == TYPE_HASH) {
|
||||||
|
if (_hash == null)
|
||||||
|
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
|
||||||
|
len = 11 + Hash.HASH_LENGTH;
|
||||||
|
} else if (_endpointType == TYPE_HOST) {
|
||||||
|
if (_host == null)
|
||||||
|
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
|
||||||
|
len = 12 + _host.length();
|
||||||
|
} else {
|
||||||
|
throw new I2CPMessageException("bad type");
|
||||||
|
}
|
||||||
|
ByteArrayOutputStream os = new ByteArrayOutputStream(len);
|
||||||
|
try {
|
||||||
|
_sessionId.writeBytes(os);
|
||||||
|
byte flags = (byte) _authType;
|
||||||
|
if (_secret != null)
|
||||||
|
flags |= FLAG_SECRET;
|
||||||
|
os.write(flags);
|
||||||
|
os.write((byte) _endpointType);
|
||||||
|
DataHelper.writeLong(os, 2, _blindType.getCode());
|
||||||
|
DataHelper.writeLong(os, 4, _expiration);
|
||||||
|
if (_endpointType == TYPE_HASH) {
|
||||||
|
_hash.writeBytes(os);
|
||||||
|
} else if (_endpointType == TYPE_HOST) {
|
||||||
|
DataHelper.writeString(os, _host);
|
||||||
|
} else if (_endpointType == TYPE_DEST) {
|
||||||
|
_dest.writeBytes(os);
|
||||||
|
} else {
|
||||||
|
DataHelper.writeLong(os, 2, _privkey.getType().getCode());
|
||||||
|
os.write(_privkey.getData());
|
||||||
|
DataHelper.writeString(os, _host);
|
||||||
|
}
|
||||||
|
if (_secret != null)
|
||||||
|
DataHelper.writeString(os, _secret);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
throw new I2CPMessageException("bad data", dfe);
|
||||||
|
}
|
||||||
|
return os.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return MESSAGE_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
buf.append("[BlindingInfoMessage: ");
|
||||||
|
buf.append("\n\tSession: ").append(_sessionId);
|
||||||
|
buf.append("\n\tTimeout: ").append(_expiration);
|
||||||
|
if (_endpointType == TYPE_HASH)
|
||||||
|
buf.append("\n\tHash: ").append(_hash.toBase32());
|
||||||
|
else if (_endpointType == TYPE_HOST)
|
||||||
|
buf.append("\n\tHost: ").append(_host);
|
||||||
|
else if (_endpointType == TYPE_DEST)
|
||||||
|
buf.append("\n\tDest: ").append(_dest);
|
||||||
|
else if (_endpointType == TYPE_KEY)
|
||||||
|
buf.append("\n\tKey: ").append(_pubkey);
|
||||||
|
if (_privkey != null)
|
||||||
|
buf.append("\n\tPriv Key: ").append(_privkey);
|
||||||
|
if (_secret != null)
|
||||||
|
buf.append("\n\tSecret: ").append(_secret);
|
||||||
|
buf.append("]");
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,6 @@
|
|||||||
|
2019-09-10 zzz
|
||||||
|
* I2CP: New Blinding Info message (proposal 123)
|
||||||
|
|
||||||
2019-09-08 zzz
|
2019-09-08 zzz
|
||||||
* Transport:
|
* Transport:
|
||||||
- Don't automatically transition from firewalled
|
- Don't automatically transition from firewalled
|
||||||
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
|||||||
/** deprecated */
|
/** deprecated */
|
||||||
public final static String ID = "Monotone";
|
public final static String ID = "Monotone";
|
||||||
public final static String VERSION = CoreVersion.VERSION;
|
public final static String VERSION = CoreVersion.VERSION;
|
||||||
public final static long BUILD = 5;
|
public final static long BUILD = 6;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
@ -15,6 +15,7 @@ import net.i2p.CoreVersion;
|
|||||||
import net.i2p.crypto.EncType;
|
import net.i2p.crypto.EncType;
|
||||||
import net.i2p.crypto.SigType;
|
import net.i2p.crypto.SigType;
|
||||||
import net.i2p.data.Base64;
|
import net.i2p.data.Base64;
|
||||||
|
import net.i2p.data.BlindData;
|
||||||
import net.i2p.data.DatabaseEntry;
|
import net.i2p.data.DatabaseEntry;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
@ -25,7 +26,9 @@ import net.i2p.data.LeaseSet2;
|
|||||||
import net.i2p.data.Payload;
|
import net.i2p.data.Payload;
|
||||||
import net.i2p.data.PrivateKey;
|
import net.i2p.data.PrivateKey;
|
||||||
import net.i2p.data.PublicKey;
|
import net.i2p.data.PublicKey;
|
||||||
|
import net.i2p.data.SigningPublicKey;
|
||||||
import net.i2p.data.i2cp.BandwidthLimitsMessage;
|
import net.i2p.data.i2cp.BandwidthLimitsMessage;
|
||||||
|
import net.i2p.data.i2cp.BlindingInfoMessage;
|
||||||
import net.i2p.data.i2cp.CreateLeaseSetMessage;
|
import net.i2p.data.i2cp.CreateLeaseSetMessage;
|
||||||
import net.i2p.data.i2cp.CreateLeaseSet2Message;
|
import net.i2p.data.i2cp.CreateLeaseSet2Message;
|
||||||
import net.i2p.data.i2cp.CreateSessionMessage;
|
import net.i2p.data.i2cp.CreateSessionMessage;
|
||||||
@ -152,6 +155,9 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
|
|||||||
case GetBandwidthLimitsMessage.MESSAGE_TYPE:
|
case GetBandwidthLimitsMessage.MESSAGE_TYPE:
|
||||||
handleGetBWLimits((GetBandwidthLimitsMessage)message);
|
handleGetBWLimits((GetBandwidthLimitsMessage)message);
|
||||||
break;
|
break;
|
||||||
|
case BlindingInfoMessage.MESSAGE_TYPE:
|
||||||
|
handleBlindingInfo((BlindingInfoMessage)message);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (_log.shouldLog(Log.ERROR))
|
if (_log.shouldLog(Log.ERROR))
|
||||||
_log.error("Unhandled I2CP type received: " + message.getType());
|
_log.error("Unhandled I2CP type received: " + message.getType());
|
||||||
@ -774,6 +780,8 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
|
|||||||
* Divide router limit by 1.75 for overhead.
|
* Divide router limit by 1.75 for overhead.
|
||||||
* This could someday give a different answer to each client.
|
* This could someday give a different answer to each client.
|
||||||
* But it's not enforced anywhere.
|
* But it's not enforced anywhere.
|
||||||
|
*
|
||||||
|
* protected for unit test override
|
||||||
*/
|
*/
|
||||||
protected void handleGetBWLimits(GetBandwidthLimitsMessage message) {
|
protected void handleGetBWLimits(GetBandwidthLimitsMessage message) {
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
@ -789,4 +797,41 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @since 0.9.43
|
||||||
|
*/
|
||||||
|
private void handleBlindingInfo(BlindingInfoMessage message) {
|
||||||
|
if (_log.shouldInfo())
|
||||||
|
_log.info("Got Blinding info");
|
||||||
|
BlindData bd = message.getBlindData();
|
||||||
|
SigningPublicKey spk = bd.getUnblindedPubKey();
|
||||||
|
if (spk == null || bd == null) {
|
||||||
|
// hash or hostname lookup? don't support for now
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Unsupported BlindingInfo type: " + message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BlindData obd = _context.netDb().getBlindData(spk);
|
||||||
|
if (obd == null) {
|
||||||
|
_context.netDb().setBlindData(bd);
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("New: " + bd);
|
||||||
|
} else {
|
||||||
|
// update if changed
|
||||||
|
PrivateKey okey = obd.getAuthPrivKey();
|
||||||
|
PrivateKey nkey = bd.getAuthPrivKey();
|
||||||
|
String osec = obd.getSecret();
|
||||||
|
String nsec = bd.getSecret();
|
||||||
|
if ((nkey != null && !nkey.equals(okey)) ||
|
||||||
|
(nsec != null && !nsec.equals(osec))) {
|
||||||
|
_context.netDb().setBlindData(bd);
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Updated: " + bd);
|
||||||
|
} else {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("No change: " + obd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,6 +93,7 @@ class LookupDestJob extends JobImpl {
|
|||||||
SigningPublicKey spk = bd.getUnblindedPubKey();
|
SigningPublicKey spk = bd.getUnblindedPubKey();
|
||||||
BlindData bd2 = getContext().netDb().getBlindData(spk);
|
BlindData bd2 = getContext().netDb().getBlindData(spk);
|
||||||
if (bd2 != null) {
|
if (bd2 != null) {
|
||||||
|
// BlindData from database may have privkey or secret
|
||||||
bd = bd2;
|
bd = bd2;
|
||||||
} else {
|
} else {
|
||||||
getContext().netDb().setBlindData(bd);
|
getContext().netDb().setBlindData(bd);
|
||||||
@ -132,6 +133,20 @@ class LookupDestJob extends JobImpl {
|
|||||||
returnDest(d);
|
returnDest(d);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
boolean fail1 = _blindData.getAuthRequired() && _blindData.getAuthPrivKey() == null;
|
||||||
|
boolean fail2 = _blindData.getSecretRequired() && _blindData.getSecret() == null;
|
||||||
|
if (fail1 || fail2) {
|
||||||
|
int code;
|
||||||
|
if (fail1 && fail2)
|
||||||
|
code = HostReplyMessage.RESULT_SECRET_AND_KEY_REQUIRED;
|
||||||
|
else if (fail1)
|
||||||
|
code = HostReplyMessage.RESULT_KEY_REQUIRED;
|
||||||
|
else
|
||||||
|
code = HostReplyMessage.RESULT_SECRET_REQUIRED;
|
||||||
|
if (_log.shouldDebug())
|
||||||
|
_log.debug("Failed b33 lookup " + _name + " with code " + code);
|
||||||
|
returnFail(code);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (_name != null) {
|
if (_name != null) {
|
||||||
// inline, ignore timeout
|
// inline, ignore timeout
|
||||||
@ -151,7 +166,7 @@ class LookupDestJob extends JobImpl {
|
|||||||
getContext().netDb().lookupDestination(_hash, done, _timeout, _fromLocalDest);
|
getContext().netDb().lookupDestination(_hash, done, _timeout, _fromLocalDest);
|
||||||
} else {
|
} else {
|
||||||
// blinding decode fail
|
// blinding decode fail
|
||||||
returnFail();
|
returnFail(HostReplyMessage.RESULT_DECRYPTION_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,13 +213,22 @@ class LookupDestJob extends JobImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the failed hash so the client can correlate replies with requests
|
* Return the request ID or failed hash so the client can correlate replies with requests
|
||||||
* @since 0.8.3
|
* @since 0.8.3
|
||||||
*/
|
*/
|
||||||
private void returnFail() {
|
private void returnFail() {
|
||||||
|
returnFail(HostReplyMessage.RESULT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the request ID or failed hash so the client can correlate replies with requests
|
||||||
|
* @param code failure code, greater than zero, only used for HostReplyMessage
|
||||||
|
* @since 0.9.43
|
||||||
|
*/
|
||||||
|
private void returnFail(int code) {
|
||||||
I2CPMessage msg;
|
I2CPMessage msg;
|
||||||
if (_reqID >= 0)
|
if (_reqID >= 0)
|
||||||
msg = new HostReplyMessage(_sessID, HostReplyMessage.RESULT_FAILURE, _reqID);
|
msg = new HostReplyMessage(_sessID, code, _reqID);
|
||||||
else if (_hash != null)
|
else if (_hash != null)
|
||||||
msg = new DestReplyMessage(_hash);
|
msg = new DestReplyMessage(_hash);
|
||||||
else
|
else
|
||||||
|
Reference in New Issue
Block a user