package net.i2p.router.tunnel;
import net.i2p.data.Hash;
import net.i2p.data.router.RouterInfo;
import net.i2p.data.i2np.TunnelDataMessage;
import net.i2p.router.JobImpl;
import net.i2p.router.OutNetMessage;
import net.i2p.router.RouterContext;
import net.i2p.util.Log;
/**
* We are the outbound gateway - we created this outbound tunnel.
* Receive the outbound message after it has been preprocessed and encrypted,
* then forward it on to the first hop in the tunnel.
*
* Not used for zero-hop OBGWs.
*/
class OutboundReceiver implements TunnelGateway.Receiver {
private final RouterContext _context;
private final Log _log;
private final TunnelCreatorConfig _config;
private RouterInfo _nextHopCache;
private final int _priority;
private static final long MAX_LOOKUP_TIME = 15*1000;
private static final int PRIORITY = OutNetMessage.PRIORITY_MY_DATA;
public OutboundReceiver(RouterContext ctx, TunnelCreatorConfig cfg) {
_context = ctx;
_log = ctx.logManager().getLog(OutboundReceiver.class);
_config = cfg;
_nextHopCache = _context.netDb().lookupRouterInfoLocally(_config.getPeer(1));
_priority = PRIORITY + cfg.getPriority();
// all createRateStat() in TunnelDispatcher
}
public long receiveEncrypted(byte encrypted[]) {
TunnelDataMessage msg = new TunnelDataMessage(_context);
msg.setData(encrypted);
msg.setTunnelId(_config.getConfig(0).getSendTunnel());
if (_log.shouldLog(Log.DEBUG))
_log.debug("received encrypted, sending out " + _config + ": " + msg);
RouterInfo ri = _nextHopCache;
if (ri == null)
ri = _context.netDb().lookupRouterInfoLocally(_config.getPeer(1));
if (ri != null) {
_nextHopCache = ri;
send(msg, ri);
return msg.getUniqueId();
} else {
// It should be rare to forget the router info for a peer in our own tunnel.
if (_log.shouldLog(Log.WARN))
_log.warn("lookup of " + _config.getPeer(1)
+ " required for " + msg);
_context.netDb().lookupRouterInfo(_config.getPeer(1), new SendJob(_context, msg),
new FailedJob(_context), MAX_LOOKUP_TIME);
return -1;
}
}
/**
* The next hop
* @return non-null
* @since 0.9.3
*/
public Hash getSendTo() {
return _config.getPeer(1);
}
private void send(TunnelDataMessage msg, RouterInfo ri) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("forwarding encrypted data out " + _config + ": " + msg.getUniqueId());
OutNetMessage m = new OutNetMessage(_context, msg, msg.getMessageExpiration(), _priority, ri);
_context.outNetMessagePool().add(m);
_config.incrementProcessedMessages();
}
private class SendJob extends JobImpl {
private final TunnelDataMessage _msg;
public SendJob(RouterContext ctx, TunnelDataMessage msg) {
super(ctx);
_msg = msg;
}
public String getName() { return "OBGW send after lookup"; }
public void runJob() {
RouterInfo ri = _context.netDb().lookupRouterInfoLocally(_config.getPeer(1));
if (_log.shouldLog(Log.DEBUG))
_log.debug("lookup of " + _config.getPeer(1)
+ " successful? " + (ri != null));
int stat;
if (ri != null) {
_nextHopCache = ri;
send(_msg, ri);
stat = 1;
} else {
stat = 0;
}
_context.statManager().addRateData("tunnel.outboundLookupSuccess", stat);
}
}
private class FailedJob extends JobImpl {
public FailedJob(RouterContext ctx) {
super(ctx);
}
public String getName() { return "OBGW lookup fail"; }
public void runJob() {
if (_log.shouldLog(Log.WARN))
_log.warn("lookup of " + _config.getPeer(1)
+ " failed for " + _config);
_context.statManager().addRateData("tunnel.outboundLookupSuccess", 0);
}
}
}