/* * Created on Feb 9, 2005 * Created by Alon Rohter * Copyright (C) 2004-2005 Aelitis, All Rights Reserved. * * 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 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * AELITIS, SAS au capital de 46,603.30 euros * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. * */ package com.aelitis.azureus.core.peermanager.messaging.bittorrent; import java.util.HashMap; import org.gudy.azureus2.core3.logging.*; import org.gudy.azureus2.core3.util.Debug; import org.gudy.azureus2.core3.util.DirectByteBuffer; import org.gudy.azureus2.core3.util.DirectByteBufferPool; import com.aelitis.azureus.core.networkmanager.RawMessage; import com.aelitis.azureus.core.networkmanager.impl.RawMessageImpl; import com.aelitis.azureus.core.peermanager.messaging.*; /** * */ public class BTMessageFactory { public static final byte MESSAGE_VERSION_INITIAL = 1; public static final byte MESSAGE_VERSION_SUPPORTS_PADDING = 2; // most of these messages are also used by AZ code private static final LogIDs LOGID = LogIDs.PEER; /** * Initialize the factory, i.e. register the messages with the message manager. */ public static void init() { try { MessageManager.getSingleton().registerMessageType( new BTBitfield( null, MESSAGE_VERSION_SUPPORTS_PADDING )); MessageManager.getSingleton().registerMessageType( new BTCancel( -1, -1, -1, MESSAGE_VERSION_SUPPORTS_PADDING )); MessageManager.getSingleton().registerMessageType( new BTChoke( MESSAGE_VERSION_SUPPORTS_PADDING )); MessageManager.getSingleton().registerMessageType( new BTHandshake( new byte[0], new byte[0], true, MESSAGE_VERSION_INITIAL )); MessageManager.getSingleton().registerMessageType( new BTHave( -1, MESSAGE_VERSION_SUPPORTS_PADDING )); MessageManager.getSingleton().registerMessageType( new BTInterested( MESSAGE_VERSION_SUPPORTS_PADDING )); MessageManager.getSingleton().registerMessageType( new BTKeepAlive(MESSAGE_VERSION_SUPPORTS_PADDING )); MessageManager.getSingleton().registerMessageType( new BTPiece( -1, -1, null, MESSAGE_VERSION_SUPPORTS_PADDING )); MessageManager.getSingleton().registerMessageType( new BTRequest( -1, -1 , -1, MESSAGE_VERSION_SUPPORTS_PADDING )); MessageManager.getSingleton().registerMessageType( new BTUnchoke( MESSAGE_VERSION_SUPPORTS_PADDING )); MessageManager.getSingleton().registerMessageType( new BTUninterested( MESSAGE_VERSION_SUPPORTS_PADDING )); MessageManager.getSingleton().registerMessageType( new BTSuggestPiece( -1, MESSAGE_VERSION_SUPPORTS_PADDING )); MessageManager.getSingleton().registerMessageType( new BTHaveAll( MESSAGE_VERSION_SUPPORTS_PADDING )); MessageManager.getSingleton().registerMessageType( new BTHaveNone( MESSAGE_VERSION_SUPPORTS_PADDING )); MessageManager.getSingleton().registerMessageType( new BTRejectRequest( -1, -1, -1, MESSAGE_VERSION_SUPPORTS_PADDING )); MessageManager.getSingleton().registerMessageType( new BTAllowedFast( -1, MESSAGE_VERSION_SUPPORTS_PADDING )); MessageManager.getSingleton().registerMessageType( new BTLTMessage( null, MESSAGE_VERSION_SUPPORTS_PADDING )); MessageManager.getSingleton().registerMessageType( new BTDHTPort(-1)); } catch( MessageException me ) { me.printStackTrace(); } } private static final String[] id_to_name = new String[21]; private static final HashMap legacy_data = new HashMap(); static { legacy_data.put( BTMessage.ID_BT_CHOKE, new LegacyData( RawMessage.PRIORITY_HIGH, true, new Message[]{new BTUnchoke((byte)0), new BTPiece( -1, -1, null,(byte)0 )}, (byte)0 ) ); id_to_name[0] = BTMessage.ID_BT_CHOKE; legacy_data.put( BTMessage.ID_BT_UNCHOKE, new LegacyData( RawMessage.PRIORITY_NORMAL, true, new Message[]{new BTChoke((byte)0)}, (byte)1 ) ); id_to_name[1] = BTMessage.ID_BT_UNCHOKE; legacy_data.put( BTMessage.ID_BT_INTERESTED, new LegacyData( RawMessage.PRIORITY_HIGH, true, new Message[]{new BTUninterested((byte)0)}, (byte)2 ) ); id_to_name[2] = BTMessage.ID_BT_INTERESTED; legacy_data.put( BTMessage.ID_BT_UNINTERESTED, new LegacyData( RawMessage.PRIORITY_NORMAL, false, new Message[]{new BTInterested((byte)0)}, (byte)3 ) ); id_to_name[3] = BTMessage.ID_BT_UNINTERESTED; legacy_data.put( BTMessage.ID_BT_HAVE, new LegacyData( RawMessage.PRIORITY_LOW, false, null, (byte)4 ) ); id_to_name[4] = BTMessage.ID_BT_HAVE; legacy_data.put( BTMessage.ID_BT_BITFIELD, new LegacyData( RawMessage.PRIORITY_HIGH, true, null, (byte)5 ) ); id_to_name[5] = BTMessage.ID_BT_BITFIELD; legacy_data.put( BTMessage.ID_BT_REQUEST, new LegacyData( RawMessage.PRIORITY_NORMAL, true, null, (byte)6 ) ); id_to_name[6] = BTMessage.ID_BT_REQUEST; legacy_data.put( BTMessage.ID_BT_PIECE, new LegacyData( RawMessage.PRIORITY_LOW, false, null, (byte)7 ) ); id_to_name[7] = BTMessage.ID_BT_PIECE; legacy_data.put( BTMessage.ID_BT_CANCEL, new LegacyData( RawMessage.PRIORITY_HIGH, true, null, (byte)8 ) ); id_to_name[8] = BTMessage.ID_BT_CANCEL; legacy_data.put( BTMessage.ID_BT_DHT_PORT, new LegacyData( RawMessage.PRIORITY_LOW, true, null, (byte)9 ) ); id_to_name[9] = BTMessage.ID_BT_DHT_PORT; legacy_data.put( BTMessage.ID_BT_SUGGEST_PIECE, new LegacyData( RawMessage.PRIORITY_NORMAL, true, null, (byte)13 ) ); id_to_name[13] = BTMessage.ID_BT_SUGGEST_PIECE; legacy_data.put( BTMessage.ID_BT_HAVE_ALL, new LegacyData( RawMessage.PRIORITY_HIGH, true, null, (byte)14 ) ); id_to_name[14] = BTMessage.ID_BT_HAVE_ALL; legacy_data.put( BTMessage.ID_BT_HAVE_NONE, new LegacyData( RawMessage.PRIORITY_HIGH, true, null, (byte)15 ) ); id_to_name[15] = BTMessage.ID_BT_HAVE_NONE; legacy_data.put( BTMessage.ID_BT_REJECT_REQUEST, new LegacyData( RawMessage.PRIORITY_NORMAL, true, null, (byte)16 ) ); id_to_name[16] = BTMessage.ID_BT_REJECT_REQUEST; legacy_data.put( BTMessage.ID_BT_ALLOWED_FAST, new LegacyData( RawMessage.PRIORITY_LOW, false, null, (byte)17 ) ); id_to_name[17] = BTMessage.ID_BT_ALLOWED_FAST; legacy_data.put( BTMessage.ID_BT_LT_EXT_MESSAGE, new LegacyData( RawMessage.PRIORITY_HIGH, true, null, (byte)20 ) ); id_to_name[20] = BTMessage.ID_BT_LT_EXT_MESSAGE; } /** * Construct a new BT message instance from the given message raw byte stream. * @param stream_payload data * @return decoded/deserialized BT message * @throws MessageException if message creation failed * NOTE: Does not auto-return given direct buffer on thrown exception. */ public static Message createBTMessage( DirectByteBuffer stream_payload) throws MessageException { byte id = stream_payload.get( DirectByteBuffer.SS_MSG ); switch( id ) { case 0: return MessageManager.getSingleton().createMessage( BTMessage.ID_BT_CHOKE_BYTES, stream_payload, (byte)1 ); case 1: return MessageManager.getSingleton().createMessage( BTMessage.ID_BT_UNCHOKE_BYTES, stream_payload, (byte)1 ); case 2: return MessageManager.getSingleton().createMessage( BTMessage.ID_BT_INTERESTED_BYTES, stream_payload, (byte)1 ); case 3: return MessageManager.getSingleton().createMessage( BTMessage.ID_BT_UNINTERESTED_BYTES, stream_payload, (byte)1 ); case 4: return MessageManager.getSingleton().createMessage( BTMessage.ID_BT_HAVE_BYTES, stream_payload, (byte)1 ); case 5: return MessageManager.getSingleton().createMessage( BTMessage.ID_BT_BITFIELD_BYTES, stream_payload, (byte)1 ); case 6: return MessageManager.getSingleton().createMessage( BTMessage.ID_BT_REQUEST_BYTES, stream_payload, (byte)1 ); case 7: return MessageManager.getSingleton().createMessage( BTMessage.ID_BT_PIECE_BYTES, stream_payload, (byte)1 ); case 8: return MessageManager.getSingleton().createMessage( BTMessage.ID_BT_CANCEL_BYTES, stream_payload, (byte)1 ); case 9: return MessageManager.getSingleton().createMessage( BTMessage.ID_BT_DHT_PORT_BYTES, stream_payload, (byte)1 ); case 13: return MessageManager.getSingleton().createMessage( BTMessage.ID_BT_SUGGEST_PIECE_BYTES, stream_payload, (byte)1 ); case 14: return MessageManager.getSingleton().createMessage( BTMessage.ID_BT_HAVE_ALL_BYTES, stream_payload, (byte)1 ); case 15: return MessageManager.getSingleton().createMessage( BTMessage.ID_BT_HAVE_NONE_BYTES, stream_payload, (byte)1 ); case 16: return MessageManager.getSingleton().createMessage( BTMessage.ID_BT_REJECT_REQUEST_BYTES, stream_payload, (byte)1 ); case 17: return MessageManager.getSingleton().createMessage( BTMessage.ID_BT_ALLOWED_FAST_BYTES, stream_payload, (byte)1 ); case 20: //Clients seeing our handshake reserved bit will send us the old 'extended' messaging hello message accidentally. //Instead of throwing an exception and dropping the peer connection, we'll just fake it as a keep-alive :) if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING, "Old extended messaging hello received (or malformed LT extension message), " + "ignoring and faking as keep-alive.")); return MessageManager.getSingleton().createMessage( BTMessage.ID_BT_KEEP_ALIVE_BYTES, null, (byte)1 ); default: { System.out.println( "Unknown BT message id [" +id+ "]" ); throw new MessageException( "Unknown BT message id [" +id+ "]" ); } } } public static int getMessageType( DirectByteBuffer stream_payload ) { byte id = stream_payload.get( DirectByteBuffer.SS_MSG, 0 ); if( id == 84 ) return Message.TYPE_PROTOCOL_PAYLOAD; //handshake message byte in position 4 if ( id >= 0 && id < id_to_name.length ){ String name = id_to_name[ id ]; if ( name != null ){ Message message = MessageManager.getSingleton().lookupMessage( name ); if ( message != null ){ return( message.getType()); } } } // invalid, return whatever return Message.TYPE_PROTOCOL_PAYLOAD; } /** * Create the proper BT raw message from the given base message. * @param base_message to create from * @return BT raw message */ public static RawMessage createBTRawMessage( Message base_message ) { if( base_message instanceof RawMessage ) { //used for handshake and keep-alive messages return (RawMessage)base_message; } LegacyData ld = (LegacyData)legacy_data.get( base_message.getID() ); if( ld == null ) { Debug.out( "legacy message type id not found for [" +base_message.getID()+ "]" ); return null; //message id type not found } DirectByteBuffer[] payload = base_message.getData(); int payload_size = 0; for( int i=0; i < payload.length; i++ ) { payload_size += payload[i].remaining( DirectByteBuffer.SS_MSG ); } DirectByteBuffer header = DirectByteBufferPool.getBuffer( DirectByteBuffer.AL_MSG_BT_HEADER, 5 ); header.putInt( DirectByteBuffer.SS_MSG, 1 + payload_size ); header.put( DirectByteBuffer.SS_MSG, ld.bt_id ); header.flip( DirectByteBuffer.SS_MSG ); DirectByteBuffer[] raw_buffs = new DirectByteBuffer[ payload.length + 1 ]; raw_buffs[0] = header; for( int i=0; i < payload.length; i++ ) { raw_buffs[i+1] = payload[i]; } return new RawMessageImpl( base_message, raw_buffs, ld.priority, ld.is_no_delay, ld.to_remove ); } protected static class LegacyData { protected final int priority; protected final boolean is_no_delay; protected final Message[] to_remove; protected final byte bt_id; protected LegacyData( int prio, boolean no_delay, Message[] remove, byte btid ) { this.priority = prio; this.is_no_delay = no_delay; this.to_remove = remove; this.bt_id = btid; } } }