package net.i2p.client.streaming.impl; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import net.i2p.I2PAppContext; import net.i2p.client.I2PSession; import net.i2p.client.I2PSessionException; import net.i2p.client.I2PSessionMuxedListener; import net.i2p.client.streaming.I2PSocketManager; import net.i2p.client.streaming.I2PSocketManager.DisconnectListener; import net.i2p.util.Log; /** * Receive raw information from the I2PSession and turn it into * Packets, if we can. *<p> * I2PSession -> MessageHandler -> PacketHandler -> ConnectionPacketHandler -> MessageInputStream */ class MessageHandler implements I2PSessionMuxedListener { private final ConnectionManager _manager; private final I2PAppContext _context; private final Log _log; private final Set<I2PSocketManager.DisconnectListener> _listeners; public MessageHandler(I2PAppContext ctx, ConnectionManager mgr) { _manager = mgr; _context = ctx; _listeners = new CopyOnWriteArraySet<DisconnectListener>(); _log = ctx.logManager().getLog(MessageHandler.class); _context.statManager().createRateStat("stream.packetReceiveFailure", "When do we fail to decrypt or otherwise receive a packet sent to us?", "Stream", new long[] { 60*60*1000, 24*60*60*1000 }); } /** Instruct the client that the given session has received a message with * size # of bytes. * This shouldn't be called anymore since we are registering as a muxed listener. * @param session session to notify * @param msgId message number available * @param size size of the message */ public void messageAvailable(I2PSession session, int msgId, long size) { messageAvailable(session, msgId, size, I2PSession.PROTO_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED); } /** Instruct the client that the given session has received a message with * size # of bytes. * @param session session to notify * @param msgId message number available * @param size size of the message */ public void messageAvailable(I2PSession session, int msgId, long size, int proto, int fromPort, int toPort) { byte data[]; try { data = session.receiveMessage(msgId); } catch (I2PSessionException ise) { _context.statManager().addRateData("stream.packetReceiveFailure", 1); if (_log.shouldLog(Log.WARN)) _log.warn("Error receiving the message", ise); return; } if (data == null) { if (_log.shouldLog(Log.WARN)) _log.warn("Received null data on " + session + " proto: " + proto + " fromPort: " + fromPort + " toPort: " + toPort); return; } if (_log.shouldLog(Log.DEBUG)) _log.debug("Received " + data.length + " bytes on " + session + " (" + _manager + ')' + " proto: " + proto + " fromPort: " + fromPort + " toPort: " + toPort); Packet packet = new Packet(session); try { packet.readPacket(data, 0, data.length); packet.setRemotePort(fromPort); packet.setLocalPort(toPort); _manager.getPacketHandler().receivePacket(packet); } catch (IllegalArgumentException iae) { _context.statManager().addRateData("stream.packetReceiveFailure", 1); if (_log.shouldLog(Log.WARN)) _log.warn("Received an invalid packet", iae); } } /** Instruct the client that the session specified seems to be under attack * and that the client may wish to move its destination to another router. * @param session session to report abuse to * @param severity how bad the abuse is */ public void reportAbuse(I2PSession session, int severity) { if (_log.shouldLog(Log.ERROR)) _log.error("Abuse reported with severity " + severity); _manager.disconnectAllHard(); } /** * Notify the client that the session has been terminated * * @param session that has been terminated */ public void disconnected(I2PSession session) { if (_log.shouldLog(Log.WARN)) _log.warn("I2PSession disconnected"); _manager.disconnectAllHard(); // kill anybody waiting in accept() _manager.getConnectionHandler().setActive(false); for (I2PSocketManager.DisconnectListener lsnr : _listeners) { lsnr.sessionDisconnected(); } _listeners.clear(); } /** * Notify the client that some error occurred * * @param session of the client * @param message to send to the client about the error * @param error the actual error */ public void errorOccurred(I2PSession session, String message, Throwable error) { if (_log.shouldLog(Log.WARN)) _log.warn("error occurred: " + message + "- " + error.getMessage(), error); //_manager.disconnectAllHard(); } public void addDisconnectListener(I2PSocketManager.DisconnectListener lsnr) { _listeners.add(lsnr); } public void removeDisconnectListener(I2PSocketManager.DisconnectListener lsnr) { _listeners.remove(lsnr); } }