/* * Released into the public domain * with no warranty of any kind, either expressed or implied. */ package net.i2p.router.client; import java.util.Locale; import net.i2p.data.Base32; import net.i2p.data.Destination; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; import net.i2p.data.i2cp.DestReplyMessage; import net.i2p.data.i2cp.HostReplyMessage; import net.i2p.data.i2cp.I2CPMessage; import net.i2p.data.i2cp.I2CPMessageException; import net.i2p.data.i2cp.SessionId; import net.i2p.router.JobImpl; import net.i2p.router.RouterContext; /** * Look up the lease of a hash, to convert it to a Destination for the client. * Or, since 0.9.11, lookup a host name in the naming service. */ class LookupDestJob extends JobImpl { private final ClientConnectionRunner _runner; private final long _reqID; private final long _timeout; private final Hash _hash; private final String _name; private final SessionId _sessID; private final Hash _fromLocalDest; private static final long DEFAULT_TIMEOUT = 15*1000; public LookupDestJob(RouterContext context, ClientConnectionRunner runner, Hash h, Hash fromLocalDest) { this(context, runner, -1, DEFAULT_TIMEOUT, null, h, null, fromLocalDest); } /** * One of h or name non-null. * * For hash or b32 name, the dest will be returned if the LS can be found, * even if the dest uses unsupported crypto. * * @param reqID must be >= 0 if name != null * @param sessID must non-null if reqID >= 0 * @param fromLocalDest use these tunnels for the lookup, or null for exploratory * @since 0.9.11 */ public LookupDestJob(RouterContext context, ClientConnectionRunner runner, long reqID, long timeout, SessionId sessID, Hash h, String name, Hash fromLocalDest) { super(context); if ((h == null && name == null) || (h != null && name != null) || (reqID >= 0 && sessID == null) || (reqID < 0 && name != null)) throw new IllegalArgumentException(); _runner = runner; _reqID = reqID; _timeout = timeout; _sessID = sessID; _fromLocalDest = fromLocalDest; if (name != null && name.length() == 60) { // convert a b32 lookup to a hash lookup String nlc = name.toLowerCase(Locale.US); if (nlc.endsWith(".b32.i2p")) { byte[] b = Base32.decode(nlc.substring(0, 52)); if (b != null && b.length == Hash.HASH_LENGTH) { h = Hash.create(b); name = null; } } } _hash = h; _name = name; } public String getName() { return _name != null ? "HostName Lookup for Client" : "LeaseSet Lookup for Client"; } public void runJob() { if (_name != null) { // inline, ignore timeout Destination d = getContext().namingService().lookup(_name); if (d != null) returnDest(d); else returnFail(); } else { DoneJob done = new DoneJob(getContext()); getContext().netDb().lookupDestination(_hash, done, _timeout, _fromLocalDest); } } private class DoneJob extends JobImpl { public DoneJob(RouterContext enclosingContext) { super(enclosingContext); } public String getName() { return "LeaseSet Lookup Reply to Client"; } public void runJob() { Destination dest = getContext().netDb().lookupDestinationLocally(_hash); if (dest != null) returnDest(dest); else returnFail(); } } private void returnDest(Destination d) { I2CPMessage msg; if (_reqID >= 0) msg = new HostReplyMessage(_sessID, d, _reqID); else msg = new DestReplyMessage(d); try { _runner.doSend(msg); } catch (I2CPMessageException ime) {} } /** * Return the failed hash so the client can correlate replies with requests * @since 0.8.3 */ private void returnFail() { I2CPMessage msg; if (_reqID >= 0) msg = new HostReplyMessage(_sessID, HostReplyMessage.RESULT_FAILURE, _reqID); else msg = new DestReplyMessage(_hash); try { _runner.doSend(msg); } catch (I2CPMessageException ime) {} } }