package net.i2p.router.message; /* * free (adj.): unencumbered; not under the control of others * Written by jrandom in 2003 and released into the public domain * with no warranty of any kind, either expressed or implied. * It probably won't make your computer catch on fire, or eat * your children, but it might. Use at your own risk. * */ import net.i2p.crypto.SessionKeyManager; import net.i2p.data.DataHelper; import net.i2p.data.Hash; import net.i2p.data.PrivateKey; import net.i2p.data.i2np.DeliveryInstructions; import net.i2p.data.i2np.GarlicClove; import net.i2p.data.i2np.GarlicMessage; import net.i2p.data.i2np.I2NPMessage; import net.i2p.router.LeaseSetKeys; import net.i2p.router.RouterContext; import net.i2p.util.Log; /** * Unencrypt a garlic message and pass off any valid cloves to the configured * receiver to dispatch as they choose. * */ public class GarlicMessageReceiver { private final RouterContext _context; private final Log _log; private final CloveReceiver _receiver; private final Hash _clientDestination; public interface CloveReceiver { public void handleClove(DeliveryInstructions instructions, I2NPMessage data); } /** * @param receiver non-null */ public GarlicMessageReceiver(RouterContext context, CloveReceiver receiver) { this(context, receiver, null); } /** * @param receiver non-null */ public GarlicMessageReceiver(RouterContext context, CloveReceiver receiver, Hash clientDestination) { _context = context; _log = context.logManager().getLog(GarlicMessageReceiver.class); _clientDestination = clientDestination; _receiver = receiver; //_log.error("New GMR dest = " + clientDestination); // all createRateStat in OCMOSJ.init() } public void receive(GarlicMessage message) { PrivateKey decryptionKey; SessionKeyManager skm; if (_clientDestination != null) { LeaseSetKeys keys = _context.keyManager().getKeys(_clientDestination); skm = _context.clientManager().getClientSessionKeyManager(_clientDestination); if (keys != null && skm != null) { decryptionKey = keys.getDecryptionKey(); } else { if (_log.shouldLog(Log.WARN)) _log.warn("Not trying to decrypt a garlic routed message to a disconnected client"); return; } } else { decryptionKey = _context.keyManager().getPrivateKey(); skm = _context.sessionKeyManager(); } CloveSet set = _context.garlicMessageParser().getGarlicCloves(message, decryptionKey, skm); if (set != null) { for (int i = 0; i < set.getCloveCount(); i++) { GarlicClove clove = set.getClove(i); handleClove(clove); } } else { if (_log.shouldLog(Log.WARN)) _log.warn("CloveMessageParser failed to decrypt the message [" + message.getUniqueId() + "]", new Exception("Decrypt garlic failed")); _context.statManager().addRateData("crypto.garlic.decryptFail", 1); _context.messageHistory().messageProcessingError(message.getUniqueId(), message.getClass().getName(), "Garlic could not be decrypted"); } } /** * Validate and pass off any valid cloves to the receiver * */ private void handleClove(GarlicClove clove) { if (!isValid(clove)) { //if (_log.shouldLog(Log.WARN)) // _log.warn("Invalid clove " + clove); return; } //if (_log.shouldLog(Log.DEBUG)) // _log.debug("valid clove " + clove); _receiver.handleClove(clove.getInstructions(), clove.getData()); } private boolean isValid(GarlicClove clove) { String invalidReason = _context.messageValidator().validateMessage(clove.getCloveId(), clove.getExpiration().getTime()); boolean rv = invalidReason == null; if (!rv) { String howLongAgo = DataHelper.formatDuration(_context.clock().now()-clove.getExpiration().getTime()); if (_log.shouldLog(Log.DEBUG)) _log.debug("Clove is NOT valid: id=" + clove.getCloveId() + " expiration " + howLongAgo + " ago", new Exception("Invalid within...")); else if (_log.shouldLog(Log.WARN)) _log.warn("Clove is NOT valid: id=" + clove.getCloveId() + " expiration " + howLongAgo + " ago: " + invalidReason + ": " + clove); _context.messageHistory().messageProcessingError(clove.getCloveId(), clove.getData().getClass().getSimpleName(), "Clove is not valid (expiration " + howLongAgo + " ago)"); } return rv; } }