I2CP, SAM: Support lookup of ls2 options (proposal 167)

This commit is contained in:
zzz committed 2026-04-25 10:43:58 -04:00
1 parent 2f498e9a5d
commit 4f892d6331
18 files changed
+548 -101

No files matched your search

+42 -2
View File
@@ -423,7 +423,7 @@ public interface I2PSession {
public LookupResult lookupDest2(String name, long maxWait) throws I2PSessionException;
/**
* Lookup a Destination by hostname.
* Lookup a Destination by hash.
* Non-blocking.
* If the result is cached or there is an immediate failure,
* the result code will be something other than RESULT_DEFERRED, and the callback will NOT be called.
@@ -436,7 +436,7 @@ public interface I2PSession {
public LookupResult lookupDest(Hash h, long maxWait, LookupCallback callback) throws I2PSessionException;
/**
* Lookup a Destination by hash.
* Lookup a Destination by hostname.
* Non-blocking.
* If the result is cached or there is an immediate failure,
* the result code will be something other than RESULT_DEFERRED, and the callback will NOT be called.
@@ -448,6 +448,46 @@ public interface I2PSession {
*/
public LookupResult lookupDest(String name, long maxWait, LookupCallback callback) throws I2PSessionException;
/**
* Ask the router to lookup a Destination by hostname.
* Blocking. See above for details.
* Same as lookupDest() but with a failure code in the return value
*
* @param maxWait ms
* @param withOptions see proposal 167
* @return non-null
* @since 0.9.70
*/
public LookupResult lookupDest2(String name, long maxWait, boolean withOptions) throws I2PSessionException;
/**
* Lookup a Destination by hash.
* Non-blocking.
* If the result is cached or there is an immediate failure,
* the result code will be something other than RESULT_DEFERRED, and the callback will NOT be called.
*
* @param maxWait ms
* @param callback to return the result, non-null
* @param withOptions see proposal 167
* @return non-null. If result code is RESULT_DEFERRED, callback will be called later
* @since 0.9.70
*/
public LookupResult lookupDest(Hash h, long maxWait, LookupCallback callback, boolean withOptions) throws I2PSessionException;
/**
* Lookup a Destination by hostname.
* Non-blocking.
* If the result is cached or there is an immediate failure,
* the result code will be something other than RESULT_DEFERRED, and the callback will NOT be called.
*
* @param maxWait ms
* @param callback to return the result, non-null
* @param withOptions see proposal 167
* @return non-null. If result code is RESULT_DEFERRED, callback will be called later
* @since 0.9.70
*/
public LookupResult lookupDest(String name, long maxWait, LookupCallback callback, boolean withOptions) throws I2PSessionException;
/**
* Pass updated options to the router.
* Does not remove properties previously present but missing from this options parameter.
@@ -1,5 +1,7 @@
package net.i2p.client;
import java.util.Properties;
import net.i2p.data.Destination;
import net.i2p.data.i2cp.HostReplyMessage;
@@ -40,6 +42,18 @@ public interface LookupResult {
*/
public static final int RESULT_DECRYPTION_FAILURE = HostReplyMessage.RESULT_DECRYPTION_FAILURE;
/**
* See proposal 167
* @since 0.9.70
*/
public static final int RESULT_LEASESET_LOOKUP_FAILURE = HostReplyMessage.RESULT_LEASESET_LOOKUP_FAILURE;
/**
* See proposal 167
* @since 0.9.70
*/
public static final int RESULT_LOOKUP_TYPE_UNSUPPORTED = HostReplyMessage.RESULT_LOOKUP_TYPE_UNSUPPORTED;
/**
* For async calls only. Nonce will be non-zero and destination will be null.
* Callback will be called later with the final result and the same nonce.
@@ -58,6 +72,13 @@ public interface LookupResult {
*/
public Destination getDestination();
/**
* See proposal 167
* @return options from leaseset or null
* @since 0.9.70
*/
public Properties getOptions();
/**
* For async calls only. Nonce will be non-zero.
* Callback will be called later with the final result and the same nonce.
@@ -30,7 +30,7 @@ class HostReplyMessageHandler extends HandlerImpl {
Destination d = msg.getDestination();
long id = msg.getReqID();
if (d != null) {
session.destReceived(id, d);
session.destReceived(id, d, msg.getOptions());
} else {
session.destLookupFailed(id, msg.getResultCode());
}
@@ -206,6 +206,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
private volatile boolean _routerSupportsHostLookup;
private volatile boolean _routerSupportsLS2;
private volatile boolean _routerSupportsBlindingInfo;
private volatile boolean _routerSupportsLookupOptions;
protected static final int CACHE_MAX_SIZE = SystemVersion.isAndroid() ? 32 : 128;
/**
@@ -230,6 +231,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
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_BLINDINFO_VERSION = "0.9.43";
private static final String MIN_LOOKUP_OPTIONS_VERSION = "0.9.70";
/** @param routerVersion as rcvd in the SetDateMessage, may be null for very old routers */
void dateUpdated(String routerVersion) {
@@ -250,6 +252,9 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
_routerSupportsBlindingInfo = isrc ||
(routerVersion != null && routerVersion.length() > 0 &&
VersionComparator.comp(routerVersion, MIN_BLINDINFO_VERSION) >= 0);
_routerSupportsLookupOptions = isrc ||
(routerVersion != null && routerVersion.length() > 0 &&
VersionComparator.comp(routerVersion, MIN_LOOKUP_OPTIONS_VERSION) >= 0);
synchronized (_stateLock) {
if (_state == State.OPENING) {
changeState(State.GOTDATE);
@@ -327,6 +332,8 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
_routerSupportsHostLookup = isrc;
_routerSupportsSubsessions = isrc;
_routerSupportsLS2 = isrc;
_routerSupportsBlindingInfo = isrc;
_routerSupportsLookupOptions = isrc;
}
/**
@@ -1611,7 +1618,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
* @param d non-null
* @since 0.9.11
*/
void destReceived(long nonce, Destination d) {
void destReceived(long nonce, Destination d, Properties opts) {
// notify by nonce and hash
Hash h = d.calculateHash();
for (LookupWaiter w : _pendingLookups) {
@@ -1623,13 +1630,14 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
}
if (w.callback != null) {
// asynch
LkupResult result = new LkupResult(LookupResult.RESULT_SUCCESS, d, (int) w.nonce);
LkupResult result = new LkupResult(LookupResult.RESULT_SUCCESS, d, (int) w.nonce, opts);
w.callback.complete(result);
} else {
// synch
synchronized (w) {
w.destination = d;
w.code = LookupResult.RESULT_SUCCESS;
w.options = opts;
w.notifyAll();
}
}
@@ -1691,6 +1699,11 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
* @since 0.9.67
*/
public final LookupCallback callback;
/**
* the leaseset options, see proposal 167
* @since 0.9.70
*/
public Properties options;
public LookupWaiter(Hash h) {
this(h, -1);
@@ -1847,7 +1860,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
* @return null on failure
*/
public Destination lookupDest(String name, long maxWait) throws I2PSessionException {
LookupWaiter waiter = x_lookupDest(name, maxWait);
LookupWaiter waiter = x_lookupDest(name, maxWait, false);
if (waiter == null)
return null;
synchronized(waiter) {
@@ -1865,7 +1878,21 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
* @return non-null
*/
public LookupResult lookupDest2(String name, long maxWait) throws I2PSessionException {
LookupWaiter waiter = x_lookupDest(name, maxWait);
return lookupDest2(name, maxWait, false);
}
/**
* Ask the router to lookup a Destination by hostname.
* Blocking. See above for details.
* Same as lookupDest() but with a failure code in the return value
*
* @param maxWait ms
* @param withOptions see proposal 167
* @return non-null
* @since 0.9.70
*/
public LookupResult lookupDest2(String name, long maxWait, boolean withOptions) throws I2PSessionException {
LookupWaiter waiter = x_lookupDest(name, maxWait, withOptions);
if (waiter == null)
return LOOKUP_FAILURE;
synchronized(waiter) {
@@ -1873,6 +1900,8 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
Destination d = waiter.destination;
if (d == null && code == LookupResult.RESULT_SUCCESS)
code = LookupResult.RESULT_FAILURE;
if (withOptions && code == LookupResult.RESULT_SUCCESS)
return new LkupResult(code, d, 0, waiter.options);
return new LkupResult(code, d);
}
}
@@ -1884,7 +1913,12 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
* @since 0.9.11
* @return null on failure
*/
private LookupWaiter x_lookupDest(String name, long maxWait) throws I2PSessionException {
private LookupWaiter x_lookupDest(String name, long maxWait, boolean withOptions) throws I2PSessionException {
if (!_routerSupportsLookupOptions) {
withOptions = false;
if (_log.shouldWarn())
_log.warn("Lookup with options not supported by router");
}
if (name.length() == 0)
return null;
// Shortcut for b64
@@ -1898,10 +1932,13 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
// won't fit in Mapping
if (name.length() >= 256 && !_context.isRouterContext())
return null;
synchronized (_lookupCache) {
Destination rv = _lookupCache.get(name);
if (rv != null)
return new LookupWaiter(rv);
// if options are requested, we need to ask the router
if (!withOptions) {
synchronized (_lookupCache) {
Destination rv = _lookupCache.get(name);
if (rv != null)
return new LookupWaiter(rv);
}
}
if (isClosed()) {
if (_log.shouldLog(Log.INFO))
@@ -1910,8 +1947,48 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
}
if (!_routerSupportsHostLookup) {
// do them a favor and convert to Hash lookup
if (name.length() == 60 && name.toLowerCase(Locale.US).endsWith(".b32.i2p"))
return new LookupWaiter(lookupDest(Hash.create(Base32.decode(name.toLowerCase(Locale.US).substring(0, 52))), maxWait));
if (name.length() == 60 && name.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
Hash h = Hash.create(Base32.decode(name.toLowerCase(Locale.US).substring(0, 52)));
if (withOptions) {
// we only have an async call for hash-with-options
LkupCallback cb = new LkupCallback();
LookupResult res = lookupDest(h, maxWait, cb, true);
LookupWaiter rv = new LookupWaiter((Destination) null);
int code = res.getResultCode();
if (code == LookupResult.RESULT_SUCCESS) {
rv.destination = res.getDestination();
rv.options = res.getOptions();
rv.code = code;
return rv;
}
if (code != LookupResult.RESULT_DEFERRED) {
// immediate failure
rv.code = code;
return rv;
}
// blocking, sit and wait for it
synchronized(cb) {
try { cb.wait(maxWait); } catch (InterruptedException ie) {}
res = cb.getResult();
}
if (res == null) {
// timeout
rv.code = LookupResult.RESULT_FAILURE;
return rv;
}
code = res.getResultCode();
if (code == LookupResult.RESULT_SUCCESS) {
rv.destination = res.getDestination();
rv.options = res.getOptions();
rv.code = code;
return rv;
}
// failure
rv.code = code;
return rv;
}
return new LookupWaiter(lookupDest(h, maxWait));
}
// else unsupported
if (_log.shouldLog(Log.WARN))
_log.warn("Router does not support HostLookup for " + name);
@@ -1926,7 +2003,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
SessionId id = _sessionId;
if (id == null)
id = DUMMY_SESSION;
sendMessage_unchecked(new HostLookupMessage(id, name, nonce, maxWait));
sendMessage_unchecked(new HostLookupMessage(id, name, nonce, maxWait, withOptions));
try {
synchronized (waiter) {
waiter.wait(maxWait);
@@ -1941,7 +2018,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
}
/**
* Lookup a Destination by hostname.
* Lookup a Destination by hash.
* Non-blocking.
* If the result is cached or there is an immediate failure,
* the result code will be something other than RESULT_DEFERRED, and the callback will NOT be called.
@@ -1952,10 +2029,34 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
* @since 0.9.67
*/
public LookupResult lookupDest(Hash h, long maxWait, LookupCallback callback) throws I2PSessionException {
synchronized (_lookupCache) {
Destination rv = _lookupCache.get(h);
if (rv != null)
return new LkupResult(LookupResult.RESULT_SUCCESS, rv);
return lookupDest(h, maxWait, callback, false);
}
/**
* Lookup a Destination by hash.
* Non-blocking.
* If the result is cached or there is an immediate failure,
* the result code will be something other than RESULT_DEFERRED, and the callback will NOT be called.
*
* @param maxWait ms
* @param callback to return the result, non-null
* @param withOptions see proposal 167
* @return non-null. If result code is RESULT_DEFERRED, callback will be called later
* @since 0.9.70
*/
public LookupResult lookupDest(Hash h, long maxWait, LookupCallback callback, boolean withOptions) throws I2PSessionException {
if (!_routerSupportsLookupOptions) {
withOptions = false;
if (_log.shouldWarn())
_log.warn("Lookup with options not supported by router");
}
// if options are requested, we need to ask the router
if (!withOptions) {
synchronized (_lookupCache) {
Destination rv = _lookupCache.get(h);
if (rv != null)
return new LkupResult(LookupResult.RESULT_SUCCESS, rv);
}
}
synchronized (_stateLock) {
// not before GOTDATE
@@ -1977,7 +2078,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
if (maxWait > 60*1000)
maxWait = 60*1000;
try {
sendMessage_unchecked(new HostLookupMessage(id, h, nonce, maxWait));
sendMessage_unchecked(new HostLookupMessage(id, h, nonce, maxWait, withOptions));
} catch (I2PSessionException ise) {
_pendingLookups.remove(waiter);
throw ise;
@@ -1987,7 +2088,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
}
/**
* Lookup a Destination by hash.
* Lookup a Destination by hostname.
* Non-blocking.
* If the result is cached or there is an immediate failure,
* the result code will be something other than RESULT_DEFERRED, and the callback will NOT be called.
@@ -1998,6 +2099,27 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
* @since 0.9.67
*/
public LookupResult lookupDest(String name, long maxWait, LookupCallback callback) throws I2PSessionException {
return lookupDest(name, maxWait, callback, false);
}
/**
* Lookup a Destination by hostname.
* Non-blocking.
* If the result is cached or there is an immediate failure,
* the result code will be something other than RESULT_DEFERRED, and the callback will NOT be called.
*
* @param maxWait ms
* @param callback to return the result, non-null
* @param withOptions see proposal 167
* @return non-null. If result code is RESULT_DEFERRED, callback will be called later
* @since 0.9.70
*/
public LookupResult lookupDest(String name, long maxWait, LookupCallback callback, boolean withOptions) throws I2PSessionException {
if (!_routerSupportsLookupOptions) {
withOptions = false;
if (_log.shouldWarn())
_log.warn("Lookup with options not supported by router");
}
if (name.length() == 0)
return LOOKUP_FAILURE;
// Shortcut for b64
@@ -2012,10 +2134,13 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
// won't fit in Mapping
if (name.length() >= 256 && !_context.isRouterContext())
return LOOKUP_FAILURE;
synchronized (_lookupCache) {
Destination rv = _lookupCache.get(name);
if (rv != null)
return new LkupResult(LookupResult.RESULT_SUCCESS, rv);
// if options are requested, we need to ask the router
if (!withOptions) {
synchronized (_lookupCache) {
Destination rv = _lookupCache.get(name);
if (rv != null)
return new LkupResult(LookupResult.RESULT_SUCCESS, rv);
}
}
synchronized (_stateLock) {
// not before GOTDATE
@@ -2037,7 +2162,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
if (maxWait > 60*1000)
maxWait = 60*1000;
try {
sendMessage_unchecked(new HostLookupMessage(id, name, nonce, maxWait));
sendMessage_unchecked(new HostLookupMessage(id, name, nonce, maxWait, withOptions));
} catch (I2PSessionException ise) {
_pendingLookups.remove(waiter);
throw ise;
@@ -1,5 +1,7 @@
package net.i2p.client.impl;
import java.util.Properties;
import net.i2p.client.LookupResult;
import net.i2p.data.Destination;
@@ -13,9 +15,10 @@ public class LkupResult implements LookupResult {
private final int _code;
private final Destination _dest;
private final int _nonce;
private final Properties _opts;
LkupResult(int code, Destination dest) {
this(code, dest, 0);
this(code, dest, 0, null);
}
/**
@@ -24,7 +27,7 @@ public class LkupResult implements LookupResult {
* @since 0.9.67
*/
LkupResult(int nonce) {
this(RESULT_DEFERRED, null, nonce);
this(RESULT_DEFERRED, null, nonce, null);
}
/**
@@ -33,9 +36,19 @@ public class LkupResult implements LookupResult {
* @since 0.9.67
*/
LkupResult(int code, Destination dest, int nonce) {
this(code, dest, nonce, null);
}
/**
* See proposal 167
*
* @since 0.9.70
*/
LkupResult(int code, Destination dest, int nonce, Properties opts) {
_code = code;
_dest = dest;
_nonce = nonce;
_opts = opts;
}
/**
@@ -48,6 +61,13 @@ public class LkupResult implements LookupResult {
*/
public Destination getDestination() { return _dest; }
/**
* See proposal 167
* @return options from leaseset or null
* @since 0.9.70
*/
public Properties getOptions() { return _opts; }
/**
* For async calls only. Nonce will be non-zero.
* Callback will be called later with the final result and the same nonce.
@@ -257,8 +257,8 @@ class SubSession extends I2PSessionMuxedImpl {
* on reception of HostReplyMessage
* @param d non-null
*/
void destReceived(long nonce, Destination d) {
_primary.destReceived(nonce, d);
void destReceived(long nonce, Destination d, Properties opts) {
_primary.destReceived(nonce, d, opts);
}
/**
@@ -78,7 +78,7 @@ public class DummyNamingService extends NamingService {
try {
if (hostname.length() == BASE32_HASH_LENGTH + 8) {
// b32
d = LookupDest.lookupBase32Hash(_context, hostname.substring(0, BASE32_HASH_LENGTH));
d = LookupDest.lookupBase32Hash(_context, hostname.substring(0, BASE32_HASH_LENGTH), storedOptions);
} else {
// b33
d = LookupDest.lookupHostname(_context, hostname);
@@ -11,6 +11,8 @@ import net.i2p.client.I2PSessionException;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSimpleClient;
import net.i2p.client.LookupResult;
import net.i2p.client.impl.LkupCallback;
import net.i2p.data.Base32;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
@@ -38,12 +40,22 @@ public class LookupDest {
protected LookupDest(I2PAppContext context) {}
/** @param key 52 chars (do not include the .b32.i2p suffix) */
/**
* @param key 52 chars (do not include the .b32.i2p suffix)
*/
static Destination lookupBase32Hash(I2PAppContext ctx, String key) throws I2PSessionException {
return lookupBase32Hash(ctx, key, null);
}
/**
* @param key 52 chars (do not include the .b32.i2p suffix)
* @param lsopts out property or null
*/
static Destination lookupBase32Hash(I2PAppContext ctx, String key, Properties lsopts) throws I2PSessionException {
byte[] h = Base32.decode(key);
if (h == null)
return null;
return lookupHash(ctx, h);
return lookupHash(ctx, h, lsopts);
}
/* Might be useful but not in the context of urls due to upper/lower case */
@@ -56,8 +68,12 @@ public class LookupDest {
}
****/
/** @param h 32 byte hash */
private static Destination lookupHash(I2PAppContext ctx, byte[] h) throws I2PSessionException {
/**
* @param h 32 byte hash
* @param lsopts out property or null
* @since 0.9.70
*/
private static Destination lookupHash(I2PAppContext ctx, byte[] h, Properties lsopts) throws I2PSessionException {
Hash key = Hash.create(h);
Destination rv = null;
I2PClient client = new I2PSimpleClient();
@@ -66,7 +82,32 @@ public class LookupDest {
try {
session = client.createSession(null, opts);
session.connect();
rv = session.lookupDest(key, DEFAULT_TIMEOUT);
if (lsopts != null) {
// prop. 167
LkupCallback cb = new LkupCallback();
LookupResult res = session.lookupDest(key, 10*1000, cb, true);
int code = res.getResultCode();
if (code == LookupResult.RESULT_SUCCESS) {
rv = res.getDestination();
Properties ropts = res.getOptions();
if (ropts != null && !ropts.isEmpty())
lsopts.putAll(ropts);
} else if (code == LookupResult.RESULT_DEFERRED) {
// blocking, sit and wait for it
synchronized(cb) {
try { cb.wait(10*1000); } catch (InterruptedException ie) {}
res = cb.getResult();
}
if (res != null && res.getResultCode() == LookupResult.RESULT_SUCCESS) {
rv = res.getDestination();
Properties ropts = res.getOptions();
if (ropts != null && !ropts.isEmpty())
lsopts.putAll(ropts);
} // else timeout or failure
} // else immediate failure
} else {
rv = session.lookupDest(key, DEFAULT_TIMEOUT);
}
} finally {
if (session != null)
session.destroySession();
@@ -11,6 +11,7 @@ import java.io.InputStream;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.util.ByteArrayStream;
@@ -28,6 +29,7 @@ public class HostLookupMessage extends I2CPMessageImpl {
private int _lookupType;
private Hash _hash;
private String _host;
private Destination _dest;
private SessionId _sessionId;
public static final int LOOKUP_HASH = 0;
@@ -96,6 +98,28 @@ public class HostLookupMessage extends I2CPMessageImpl {
_lookupType = reqOpts ? LOOKUP_HOST_OPT : LOOKUP_HOST;
}
/**
* Always requests LS2 options
*
* @param reqID 0 to 2**32 - 1
* @param timeout ms 1 to 2**32 - 1
* @since 0.9.70
*/
public HostLookupMessage(SessionId id, Destination dest, long reqID, long timeout) {
if (id == null || dest == null)
throw new IllegalArgumentException();
if (reqID < 0 || reqID > MAX_INT)
throw new IllegalArgumentException();
if (timeout <= 0 || timeout > MAX_INT)
throw new IllegalArgumentException();
_sessionId = id;
_dest = dest;
_hash = dest.calculateHash();
_reqID = reqID;
_timeout = timeout;
_lookupType = LOOKUP_DEST_OPT;
}
public SessionId getSessionId() {
return _sessionId;
}
@@ -125,26 +149,34 @@ public class HostLookupMessage extends I2CPMessageImpl {
}
/**
* @return 0 (hash) or 1 (host)
* @return 0-4
*/
public int getLookupType() {
return _lookupType;
}
/**
* @return only valid if lookup type == 0
* @return only valid if lookup type == 0 or 2 or 4
*/
public Hash getHash() {
return _hash;
}
/**
* @return only valid if lookup type == 1
* @return only valid if lookup type == 1 or 3
*/
public String getHostname() {
return _host;
}
/**
* @return only valid if lookup type == 4
* @since 0.9.70
*/
public Destination getDestination() {
return _dest;
}
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
try {
_sessionId = new SessionId();
@@ -154,13 +186,24 @@ public class HostLookupMessage extends I2CPMessageImpl {
_lookupType = in.read();
if (_lookupType < 0)
throw new EOFException();
if (_lookupType == LOOKUP_HASH || _lookupType == LOOKUP_HASH_OPT) {
switch (_lookupType) {
case LOOKUP_HASH:
case LOOKUP_HASH_OPT:
_hash = Hash.create(in);
} else if (_lookupType == LOOKUP_HOST || _lookupType == LOOKUP_HOST_OPT) {
break;
case LOOKUP_HOST:
case LOOKUP_HOST_OPT:
_host = DataHelper.readString(in);
if (_host.length() == 0)
throw new I2CPMessageException("bad host");
} else {
break;
case LOOKUP_DEST_OPT:
_dest = Destination.create(in);
break;
default:
throw new I2CPMessageException("bad type");
}
} catch (DataFormatException dfe) {
@@ -170,15 +213,26 @@ public class HostLookupMessage extends I2CPMessageImpl {
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
int len;
if (_lookupType == LOOKUP_HASH || _lookupType == LOOKUP_HASH_OPT) {
switch (_lookupType) {
case LOOKUP_HASH:
case LOOKUP_HASH_OPT:
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 (_lookupType == LOOKUP_HOST || _lookupType == LOOKUP_HOST_OPT) {
break;
case LOOKUP_HOST:
case LOOKUP_HOST_OPT:
if (_host == null)
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
len = 12 + _host.length();
} else {
break;
case LOOKUP_DEST_OPT:
len = 11 + _dest.size();
break;
default:
throw new I2CPMessageException("bad type");
}
ByteArrayStream os = new ByteArrayStream(len);
@@ -187,10 +241,20 @@ public class HostLookupMessage extends I2CPMessageImpl {
DataHelper.writeLong(os, 4, _reqID);
DataHelper.writeLong(os, 4, _timeout);
os.write((byte) _lookupType);
if (_lookupType == LOOKUP_HASH) {
switch (_lookupType) {
case LOOKUP_HASH:
case LOOKUP_HASH_OPT:
_hash.writeBytes(os);
} else {
break;
case LOOKUP_HOST:
case LOOKUP_HOST_OPT:
DataHelper.writeString(os, _host);
break;
case LOOKUP_DEST_OPT:
_dest.writeBytes(os);
break;
}
} catch (DataFormatException dfe) {
throw new I2CPMessageException("bad data", dfe);
@@ -209,10 +273,22 @@ public class HostLookupMessage extends I2CPMessageImpl {
buf.append("\n\t").append(_sessionId);
buf.append("\n\tReqID: ").append(_reqID);
buf.append("\n\tTimeout: ").append(_timeout);
if (_lookupType == LOOKUP_HASH)
buf.append("\n\tType: ").append(_lookupType);
switch (_lookupType) {
case LOOKUP_HASH:
case LOOKUP_HASH_OPT:
buf.append("\n\tHash: ").append(_hash);
else if (_lookupType == LOOKUP_HOST)
break;
case LOOKUP_HOST:
case LOOKUP_HOST_OPT:
buf.append("\n\tHost: ").append(_host);
break;
case LOOKUP_DEST_OPT:
buf.append("\n\tDest: ").append(_dest);
break;
}
buf.append("]");
return buf.toString();
}
@@ -196,8 +196,11 @@ public class HostReplyMessage extends I2CPMessageImpl {
buf.append("\n\t").append(_sessionId);
buf.append("\n\tReqID: ").append(_reqID);
buf.append("\n\tResult: ").append(_code);
if (_code == RESULT_SUCCESS)
if (_code == RESULT_SUCCESS) {
buf.append("\n\tDestination: ").append(_dest);
if (_options != null && !_options.isEmpty())
buf.append("\n\tOptions: ").append(DataHelper.toString(_options));
}
buf.append("]");
return buf.toString();
}