/* * BitTorrentHandler.java * * Created on Feb 3, 2010, 11:51:24 PM * * Description: A channel handler for a bit torrent bitfield message. * * Copyright (C) Feb 3, 2010 reed. * * 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 3 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.texai.torrent; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; import net.jcip.annotations.NotThreadSafe; import org.apache.log4j.Logger; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; import org.texai.network.netty.handler.AbstractBitTorrentHandler; import org.texai.torrent.message.BitTorrentBitFieldMessage; import org.texai.torrent.message.BitTorrentCancelMessage; import org.texai.torrent.message.BitTorrentChokeMessage; import org.texai.torrent.message.BitTorrentHandshakeMessage; import org.texai.torrent.message.BitTorrentHaveMessage; import org.texai.torrent.message.BitTorrentInterestedMessage; import org.texai.torrent.message.BitTorrentKeepAliveMessage; import org.texai.torrent.message.BitTorrentMessage; import org.texai.torrent.message.BitTorrentNotInterestedMessage; import org.texai.torrent.message.BitTorrentPieceMessage; import org.texai.torrent.message.BitTorrentRequestMessage; import org.texai.torrent.message.BitTorrentUnchokeMessage; import org.texai.util.ByteUtils; import org.texai.util.TexaiException; /** A channel handler for a bit torrent bitfield message. * * @author reed */ @NotThreadSafe public final class BitTorrentHandler extends AbstractBitTorrentHandler { /** the logger */ private static final Logger LOGGER = Logger.getLogger(BitTorrentHandler.class); /** the channel dictionary, channel --> peer coordinator */ private final Map<Channel, PeerCoordinator> channelDictionary = new HashMap<>(); /** the SSL torrent */ private final SSLTorrent sslTorrent; /** Constructs a new BitTorrentHandler instance. * * @param sslTorrent the SSL torrent */ public BitTorrentHandler(final SSLTorrent sslTorrent) { //Preconditions assert sslTorrent != null : "sslTorrent must not be null"; this.sslTorrent = sslTorrent; } /** Receives a message object from a remote peer. * * @param channelHandlerContext the channel handler context * @param messageEvent the message event */ @Override public void messageReceived( final ChannelHandlerContext channelHandlerContext, final MessageEvent messageEvent) { //Preconditions assert channelHandlerContext != null : "channelHandlerContext must not be null"; assert messageEvent != null : "messageEvent must not be null"; assert messageEvent.getMessage() instanceof BitTorrentMessage; final BitTorrentMessage bitTorrentMessage = (BitTorrentMessage) messageEvent.getMessage(); LOGGER.info("server received messageEvent: " + messageEvent); final Channel channel = channelHandlerContext.getChannel(); assert channel != null; if (bitTorrentMessage instanceof BitTorrentHandshakeMessage) { // handshake final BitTorrentHandshakeMessage bitTorrentHandshakeMessage = (BitTorrentHandshakeMessage) bitTorrentMessage; LOGGER.info("handling client's handshake: " + bitTorrentHandshakeMessage); final PeerCoordinator peerCoordinator = getPeerCoordinator(bitTorrentHandshakeMessage.getInfoHash(), channel); if (peerCoordinator != null) { channelDictionary.put(channel, peerCoordinator); final Peer peer = peerCoordinator.getPeer(bitTorrentMessage.getPeerIdBytes()); if (peer == null) { final InetSocketAddress inetSocketAddress = (InetSocketAddress) channel.getRemoteAddress(); peerCoordinator.addPeerThatContactedUs( inetSocketAddress.getAddress(), inetSocketAddress.getPort(), channel, bitTorrentHandshakeMessage); } else { peer.receiveHandshake(bitTorrentHandshakeMessage); } } return; } final PeerCoordinator peerCoordinator = channelDictionary.get(channelHandlerContext.getChannel()); if (peerCoordinator == null) { LOGGER.info("no peer coordinator to handle channel, disconnecting from peer"); channel.close(); } @SuppressWarnings("null") final Peer peer = peerCoordinator.getPeer(bitTorrentMessage.getPeerIdBytes()); if (peer == null) { final InetSocketAddress localInetSocketAddress = (InetSocketAddress) channel.getLocalAddress(); final InetSocketAddress remoteInetSocketAddress = (InetSocketAddress) channel.getRemoteAddress(); try { //final InetSocketAddress inetSocketAddress = (InetSocketAddress) channel.getRemoteAddress(); LOGGER.info(localInetSocketAddress.getHostName() + ":" + localInetSocketAddress.getPort() + " no active handshake, disconnecting from peer: " + remoteInetSocketAddress.getAddress() + ":" + remoteInetSocketAddress.getPort() + " id: " + new String(bitTorrentMessage.getPeerIdBytes(), "US-ASCII")); } catch (UnsupportedEncodingException ex) { throw new TexaiException(ex); } channel.close(); return; } if (bitTorrentMessage instanceof BitTorrentBitFieldMessage) { // bitfield final BitTorrentBitFieldMessage bitTorrentBitfieldMessage = (BitTorrentBitFieldMessage) bitTorrentMessage; LOGGER.info("handling bitfield: " + bitTorrentBitfieldMessage); peer.bitfieldMessageFromPeer(bitTorrentBitfieldMessage); } else if (bitTorrentMessage instanceof BitTorrentCancelMessage) { // cancel final BitTorrentCancelMessage bitTorrentCancelMessage = (BitTorrentCancelMessage) bitTorrentMessage; LOGGER.info("handling cancel: " + bitTorrentCancelMessage); peer.cancelMessageFromPeer(bitTorrentCancelMessage); } else if (bitTorrentMessage instanceof BitTorrentChokeMessage) { // choke final BitTorrentChokeMessage bitTorrentChokeMessage = (BitTorrentChokeMessage) bitTorrentMessage; LOGGER.info("handling choke: " + bitTorrentChokeMessage); peer.chokeMessageFromPeer(); } else if (bitTorrentMessage instanceof BitTorrentHaveMessage) { // have final BitTorrentHaveMessage bitTorrentHaveMessage = (BitTorrentHaveMessage) bitTorrentMessage; LOGGER.info("handling have: " + bitTorrentHaveMessage); peer.haveMessageFromPeer(bitTorrentHaveMessage); } else if (bitTorrentMessage instanceof BitTorrentInterestedMessage) { // interested final BitTorrentInterestedMessage bitTorrentInterestedMessage = (BitTorrentInterestedMessage) bitTorrentMessage; LOGGER.info("handling interested: " + bitTorrentInterestedMessage); peer.interestedMessageFromPeer(); } else if (bitTorrentMessage instanceof BitTorrentKeepAliveMessage) { // keep-alive final BitTorrentKeepAliveMessage bitTorrentKeepAliveMessage = (BitTorrentKeepAliveMessage) bitTorrentMessage; LOGGER.info("handling keep-alive: " + bitTorrentKeepAliveMessage); peer.keepAliveMessageFromPeer(); } else if (bitTorrentMessage instanceof BitTorrentNotInterestedMessage) { // not-interested final BitTorrentNotInterestedMessage bitTorrentNotInterestedMessage = (BitTorrentNotInterestedMessage) bitTorrentMessage; LOGGER.info("handling not-interested: " + bitTorrentNotInterestedMessage); peer.notInterestedMessageFromPeer(); } else if (bitTorrentMessage instanceof BitTorrentPieceMessage) { // piece final BitTorrentPieceMessage bitTorrentPieceMessage = (BitTorrentPieceMessage) bitTorrentMessage; LOGGER.info("handling piece: " + bitTorrentPieceMessage); peer.pieceMessageFromPeer(bitTorrentPieceMessage); } else if (bitTorrentMessage instanceof BitTorrentRequestMessage) { // request final BitTorrentRequestMessage bitTorrentRequestMessage = (BitTorrentRequestMessage) bitTorrentMessage; LOGGER.info("handling request: " + bitTorrentRequestMessage); peer.requestMessageFromPeer(bitTorrentRequestMessage); } else if (bitTorrentMessage instanceof BitTorrentUnchokeMessage) { // unchoke final BitTorrentUnchokeMessage bitTorrentUnchokeMessage = (BitTorrentUnchokeMessage) bitTorrentMessage; LOGGER.info("handling unchoke: " + bitTorrentUnchokeMessage); peer.unChokeMessageFromPeer(); } else { assert false; } } /** Gets the peer coordinator that handles the given info hash, that identifies the torrent. Closes the * peer connection if no peer coordinator is found. * * @param infoHash the given info hash, that identifies the torrent * @param channel the communication channel between us and the peer * @return the peer coordinator that handles the given info hash, or null if not found */ private PeerCoordinator getPeerCoordinator( final byte[] infoHash, final Channel channel) { //Preconditions assert infoHash != null : "infoHash must not be null"; assert channel != null : "channel must not be null"; final PeerCoordinator peerCoordinator = sslTorrent.getPeerCoordinator(infoHash); if (peerCoordinator == null) { LOGGER.info("no peer coordinator to handle info hash: " + ByteUtils.toHex(infoHash) + ", disconnecting from peer"); channel.close(); } return peerCoordinator; } /** Handles a caught exception. * * @param channelHandlerContext the channel handler event * @param exceptionEvent the exception event */ @Override @SuppressWarnings("ThrowableResultIgnored") public void exceptionCaught( final ChannelHandlerContext channelHandlerContext, final ExceptionEvent exceptionEvent) { //Preconditions assert channelHandlerContext != null : "channelHandlerContext must not be null"; assert exceptionEvent != null : "exceptionEvent must not be null"; LOGGER.error("exceptionEvent: " + exceptionEvent); throw new TexaiException(exceptionEvent.getCause()); } }