/* PeerConnectionIn - Handles incomming messages and hands them to PeerState. Copyright (C) 2003 Mark J. Wielaard This file is part of Snark. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.klomp.snark; import java.io.DataInputStream; import java.io.IOException; import net.i2p.I2PAppContext; import net.i2p.util.Log; class PeerConnectionIn implements Runnable { private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(PeerConnectionIn.class); private final Peer peer; private final DataInputStream din; // The max length of a complete message in bytes. // The biggest is the piece message, for which the length is the // request size (32K) plus 9. (we could also check if Storage.MAX_PIECES / 8 // in the bitfield message is bigger but it's currently 5000/8 = 625 so don't bother) private static final int MAX_MSG_SIZE = Math.max(PeerState.PARTSIZE + 9, MagnetState.CHUNK_SIZE + 100); // 100 for the ext msg dictionary private volatile Thread thread; private volatile boolean quit; long lastRcvd; public PeerConnectionIn(Peer peer, DataInputStream din) { this.peer = peer; this.din = din; lastRcvd = System.currentTimeMillis(); quit = false; } void disconnect() { if (quit == true) return; quit = true; Thread t = thread; if (t != null) t.interrupt(); if (din != null) { try { din.close(); } catch (IOException ioe) { //_log.warn("Error closing the stream from " + peer, ioe); } } } public void run() { thread = Thread.currentThread(); try { while (!quit) { final PeerState ps = peer.state; if (ps == null) break; // Common variables used for some messages. int piece; int begin; int len; // Wait till we hear something... int i = din.readInt(); lastRcvd = System.currentTimeMillis(); if (i < 0 || i > MAX_MSG_SIZE) throw new IOException("Unexpected length prefix: " + i); if (i == 0) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Received keepalive from " + peer); ps.keepAliveMessage(); continue; } byte b = din.readByte(); switch (b) { case Message.CHOKE: if (_log.shouldLog(Log.DEBUG)) _log.debug("Received choke from " + peer); ps.chokeMessage(true); break; case Message.UNCHOKE: if (_log.shouldLog(Log.DEBUG)) _log.debug("Received unchoke from " + peer); ps.chokeMessage(false); break; case Message.INTERESTED: if (_log.shouldLog(Log.DEBUG)) _log.debug("Received interested from " + peer); ps.interestedMessage(true); break; case Message.UNINTERESTED: if (_log.shouldLog(Log.DEBUG)) _log.debug("Received not interested from " + peer); ps.interestedMessage(false); break; case Message.HAVE: piece = din.readInt(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received havePiece(" + piece + ") from " + peer); ps.haveMessage(piece); break; case Message.BITFIELD: byte[] bitmap = new byte[i-1]; din.readFully(bitmap); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received bitmap from " + peer + ": size=" + (i-1) /* + ": " + ps.bitfield */ ); ps.bitfieldMessage(bitmap); break; case Message.REQUEST: piece = din.readInt(); begin = din.readInt(); len = din.readInt(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received request(" + piece + "," + begin + ") from " + peer); ps.requestMessage(piece, begin, len); break; case Message.PIECE: piece = din.readInt(); begin = din.readInt(); len = i-9; Request req = ps.getOutstandingRequest(piece, begin, len); if (req != null) { req.read(din); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received data(" + piece + "," + begin + ") from " + peer); ps.pieceMessage(req); } else { // XXX - Consume but throw away afterwards. int rcvd = din.skipBytes(len); if (rcvd != len) throw new IOException("EOF reading unwanted data"); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received UNWANTED data(" + piece + "," + begin + ") from " + peer); } break; case Message.CANCEL: piece = din.readInt(); begin = din.readInt(); len = din.readInt(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received cancel(" + piece + "," + begin + ") from " + peer); ps.cancelMessage(piece, begin, len); break; case Message.PORT: int port = din.readUnsignedShort(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received port message from " + peer); ps.portMessage(port); break; case Message.EXTENSION: int id = din.readUnsignedByte(); byte[] payload = new byte[i-2]; din.readFully(payload); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received extension message from " + peer); ps.extensionMessage(id, payload); break; // fast extensions below here case Message.SUGGEST: piece = din.readInt(); ps.suggestMessage(piece); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received suggest(" + piece + ") from " + peer); break; case Message.HAVE_ALL: ps.haveMessage(true); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received have_all from " + peer); break; case Message.HAVE_NONE: ps.haveMessage(false); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received have_none from " + peer); break; case Message.REJECT: piece = din.readInt(); begin = din.readInt(); len = din.readInt(); ps.rejectMessage(piece, begin, len); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received reject(" + piece + ',' + begin + ',' + len + ") from " + peer); break; case Message.ALLOWED_FAST: piece = din.readInt(); ps.allowedFastMessage(piece); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received allowed_fast(" + piece + ") from " + peer); break; default: byte[] bs = new byte[i-1]; din.readFully(bs); ps.unknownMessage(b, bs); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received unknown message from " + peer); } } } catch (IOException ioe) { // Ignore, probably the other side closed connection. if (_log.shouldLog(Log.INFO)) _log.info("IOError talking with " + peer, ioe); } catch (RuntimeException t) { _log.error("Error talking with " + peer, t); } finally { peer.disconnect(); } } }