1 /* 2 * Created on Feb 19, 2005 3 * Created by Alon Rohter 4 * Copyright (C) Azureus Software, Inc, All Rights Reserved. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 2 9 * of the License, or (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 * 18 */ 19 20 package com.aelitis.azureus.core.peermanager.messaging.azureus; 21 22 import java.util.HashMap; 23 import java.util.Map; 24 25 import org.gudy.azureus2.core3.util.DirectByteBuffer; 26 import org.gudy.azureus2.core3.util.DirectByteBufferPool; 27 import org.gudy.azureus2.core3.util.RandomUtils; 28 29 import com.aelitis.azureus.core.networkmanager.RawMessage; 30 import com.aelitis.azureus.core.networkmanager.impl.RawMessageImpl; 31 import com.aelitis.azureus.core.peermanager.messaging.Message; 32 import com.aelitis.azureus.core.peermanager.messaging.MessageException; 33 import com.aelitis.azureus.core.peermanager.messaging.MessageManager; 34 import com.aelitis.azureus.core.peermanager.messaging.bittorrent.*; 35 36 37 38 39 /** 40 * Factory for handling AZ message creation. 41 * NOTE: wire format: [total message length] + [id length] + [id bytes] + [version byte] + [payload bytes] 42 */ 43 public class AZMessageFactory { 44 public static final byte MESSAGE_VERSION_INITIAL = BTMessageFactory.MESSAGE_VERSION_INITIAL; 45 public static final byte MESSAGE_VERSION_SUPPORTS_PADDING = BTMessageFactory.MESSAGE_VERSION_SUPPORTS_PADDING; 46 47 public static final int AZ_HANDSHAKE_PAD_MAX = 64; 48 public static final int SMALL_PAD_MAX = 8; 49 public static final int BIG_PAD_MAX = 20; 50 51 private static final byte bss = DirectByteBuffer.SS_MSG; 52 53 54 55 private static final Map<String,LegacyData> legacy_data = new HashMap<String,LegacyData>(); 56 static { legacy_data.put( BTMessage.ID_BT_CHOKE, new LegacyData( RawMessage.PRIORITY_HIGH, true, new Message[]{new BTUnchoke((byte)0)}))57 legacy_data.put( BTMessage.ID_BT_CHOKE, new LegacyData( RawMessage.PRIORITY_HIGH, true, new Message[]{new BTUnchoke((byte)0)})); // with support for fast extension we don't cancel outstanding piece data, new BTPiece(-1, -1, null,(byte)0 )} ) ); legacy_data.put( BTMessage.ID_BT_UNCHOKE, new LegacyData( RawMessage.PRIORITY_NORMAL, true, new Message[]{new BTChoke((byte)0)} ) )58 legacy_data.put( BTMessage.ID_BT_UNCHOKE, new LegacyData( RawMessage.PRIORITY_NORMAL, true, new Message[]{new BTChoke((byte)0)} ) ); legacy_data.put( BTMessage.ID_BT_INTERESTED, new LegacyData( RawMessage.PRIORITY_HIGH, true, new Message[]{new BTUninterested((byte)0)} ) )59 legacy_data.put( BTMessage.ID_BT_INTERESTED, new LegacyData( RawMessage.PRIORITY_HIGH, true, new Message[]{new BTUninterested((byte)0)} ) ); legacy_data.put( BTMessage.ID_BT_UNINTERESTED, new LegacyData( RawMessage.PRIORITY_NORMAL, false, new Message[]{new BTInterested((byte)0)} ) )60 legacy_data.put( BTMessage.ID_BT_UNINTERESTED, new LegacyData( RawMessage.PRIORITY_NORMAL, false, new Message[]{new BTInterested((byte)0)} ) ); legacy_data.put( BTMessage.ID_BT_HAVE, new LegacyData( RawMessage.PRIORITY_LOW, false, null ) )61 legacy_data.put( BTMessage.ID_BT_HAVE, new LegacyData( RawMessage.PRIORITY_LOW, false, null ) ); legacy_data.put( BTMessage.ID_BT_BITFIELD, new LegacyData( RawMessage.PRIORITY_HIGH, true, null ) )62 legacy_data.put( BTMessage.ID_BT_BITFIELD, new LegacyData( RawMessage.PRIORITY_HIGH, true, null ) ); legacy_data.put( BTMessage.ID_BT_HAVE_ALL, new LegacyData( RawMessage.PRIORITY_HIGH, true, null ) )63 legacy_data.put( BTMessage.ID_BT_HAVE_ALL, new LegacyData( RawMessage.PRIORITY_HIGH, true, null ) ); legacy_data.put( BTMessage.ID_BT_HAVE_NONE, new LegacyData( RawMessage.PRIORITY_HIGH, true, null ) )64 legacy_data.put( BTMessage.ID_BT_HAVE_NONE, new LegacyData( RawMessage.PRIORITY_HIGH, true, null ) ); legacy_data.put( BTMessage.ID_BT_REQUEST, new LegacyData( RawMessage.PRIORITY_NORMAL, true, null ) )65 legacy_data.put( BTMessage.ID_BT_REQUEST, new LegacyData( RawMessage.PRIORITY_NORMAL, true, null ) ); legacy_data.put( BTMessage.ID_BT_REJECT_REQUEST, new LegacyData( RawMessage.PRIORITY_NORMAL, true, null ) )66 legacy_data.put( BTMessage.ID_BT_REJECT_REQUEST, new LegacyData( RawMessage.PRIORITY_NORMAL, true, null ) ); legacy_data.put( BTMessage.ID_BT_PIECE, new LegacyData( RawMessage.PRIORITY_LOW, false, null ) )67 legacy_data.put( BTMessage.ID_BT_PIECE, new LegacyData( RawMessage.PRIORITY_LOW, false, null ) ); legacy_data.put( BTMessage.ID_BT_CANCEL, new LegacyData( RawMessage.PRIORITY_HIGH, true, null ) )68 legacy_data.put( BTMessage.ID_BT_CANCEL, new LegacyData( RawMessage.PRIORITY_HIGH, true, null ) ); legacy_data.put( BTMessage.ID_BT_HANDSHAKE, new LegacyData( RawMessage.PRIORITY_HIGH, true, null ) )69 legacy_data.put( BTMessage.ID_BT_HANDSHAKE, new LegacyData( RawMessage.PRIORITY_HIGH, true, null ) ); legacy_data.put( BTMessage.ID_BT_KEEP_ALIVE, new LegacyData( RawMessage.PRIORITY_LOW, false, null ) )70 legacy_data.put( BTMessage.ID_BT_KEEP_ALIVE, new LegacyData( RawMessage.PRIORITY_LOW, false, null ) ); legacy_data.put( BTMessage.ID_BT_DHT_PORT, new LegacyData( RawMessage.PRIORITY_LOW, false, null ) )71 legacy_data.put( BTMessage.ID_BT_DHT_PORT, new LegacyData( RawMessage.PRIORITY_LOW, false, null ) ); legacy_data.put( BTMessage.ID_BT_SUGGEST_PIECE, new LegacyData( RawMessage.PRIORITY_NORMAL, true, null ) )72 legacy_data.put( BTMessage.ID_BT_SUGGEST_PIECE, new LegacyData( RawMessage.PRIORITY_NORMAL, true, null ) ); legacy_data.put( BTMessage.ID_BT_ALLOWED_FAST, new LegacyData( RawMessage.PRIORITY_LOW, false, null ) )73 legacy_data.put( BTMessage.ID_BT_ALLOWED_FAST, new LegacyData( RawMessage.PRIORITY_LOW, false, null ) ); 74 } 75 76 77 78 /** 79 * Initialize the factory, i.e. register the messages with the message manager. 80 */ init()81 public static void init() { 82 try { 83 MessageManager.getSingleton().registerMessageType( new AZHandshake( new byte[20], null, null, "", "", 0, 0, 0, null, 0, new String[0], new byte[0], 0, MESSAGE_VERSION_SUPPORTS_PADDING,false ) ); 84 MessageManager.getSingleton().registerMessageType( new AZPeerExchange( new byte[20], null, null, MESSAGE_VERSION_SUPPORTS_PADDING )); 85 MessageManager.getSingleton().registerMessageType( new AZRequestHint( -1, -1, -1, -1, MESSAGE_VERSION_SUPPORTS_PADDING )); 86 MessageManager.getSingleton().registerMessageType( new AZHave( new int[0], MESSAGE_VERSION_SUPPORTS_PADDING )); 87 MessageManager.getSingleton().registerMessageType( new AZBadPiece( -1, MESSAGE_VERSION_SUPPORTS_PADDING )); 88 MessageManager.getSingleton().registerMessageType( new AZStatRequest( null, MESSAGE_VERSION_SUPPORTS_PADDING )); 89 MessageManager.getSingleton().registerMessageType( new AZStatReply( null, MESSAGE_VERSION_SUPPORTS_PADDING )); 90 MessageManager.getSingleton().registerMessageType( new AZMetaData( null, null, MESSAGE_VERSION_SUPPORTS_PADDING )); 91 92 /* 93 MessageManager.getSingleton().registerMessageType( new AZSessionSyn( new byte[20], -1, null) ); 94 MessageManager.getSingleton().registerMessageType( new AZSessionAck( new byte[20], -1, null) ); 95 MessageManager.getSingleton().registerMessageType( new AZSessionEnd( new byte[20], "" ) ); 96 MessageManager.getSingleton().registerMessageType( new AZSessionBitfield( -1, null ) ); 97 MessageManager.getSingleton().registerMessageType( new AZSessionCancel( -1, -1, -1, -1 ) ); 98 MessageManager.getSingleton().registerMessageType( new AZSessionHave( -1, new int[]{-1} ) ); 99 MessageManager.getSingleton().registerMessageType( new AZSessionPiece( -1, -1, -1, null ) ); 100 MessageManager.getSingleton().registerMessageType( new AZSessionRequest( -1, (byte)-1, -1, -1, -1 ) ); 101 */ 102 } 103 catch( MessageException me ) { me.printStackTrace(); } 104 } 105 106 107 /** 108 * Register a generic map payload type with the factory. 109 * @param type_id to register 110 * @throws MessageException on registration error 111 */ registerGenericMapPayloadMessageType( String type_id )112 public static void registerGenericMapPayloadMessageType( String type_id ) throws MessageException { 113 MessageManager.getSingleton().registerMessageType( new AZGenericMapPayload( type_id, null, MESSAGE_VERSION_INITIAL ) ); 114 } 115 116 117 118 /** 119 * Construct a new AZ message instance from the given message raw byte stream. 120 * @param stream_payload data 121 * @return decoded/deserialized AZ message 122 * @throws MessageException if message creation failed. 123 * NOTE: Does not auto-return to buffer pool the given direct buffer on thrown exception. 124 */ createAZMessage( DirectByteBuffer stream_payload )125 public static Message createAZMessage( DirectByteBuffer stream_payload ) throws MessageException { 126 int id_length = stream_payload.getInt( bss ); 127 128 if( id_length < 1 || id_length > 1024 || id_length > stream_payload.remaining( bss ) - 1 ) { 129 byte bt_id = stream_payload.get( (byte)0, 0 ); 130 throw new MessageException( "invalid AZ id length given: " +id_length+ ", stream_payload.remaining(): " +stream_payload.remaining( bss )+ ", BT id?=" +bt_id ); 131 } 132 133 byte[] id_bytes = new byte[ id_length ]; 134 135 stream_payload.get( bss, id_bytes ); 136 137 // if only the version came first we could save a lot of space by changing the id length + id.... 138 139 // in the meantime we overload the version byte to have a version number and flags 140 // flags = top 4 bits, version = bottom 4 bits 141 142 byte version_and_flags = stream_payload.get( bss ); 143 144 byte version = (byte)( version_and_flags & 0x0f ); 145 146 if ( version >= MESSAGE_VERSION_SUPPORTS_PADDING ){ 147 148 byte flags = (byte)(( version_and_flags >> 4 ) & 0x0f ); 149 150 if ( ( flags & 0x01 ) != 0 ){ 151 152 short padding_length = stream_payload.getShort( bss ); 153 154 byte[] padding = new byte[padding_length]; 155 156 stream_payload.get( bss, padding ); 157 } 158 } 159 160 return MessageManager.getSingleton().createMessage( id_bytes, stream_payload, version ); 161 } 162 163 164 165 166 /** 167 * Create the proper AZ raw message from the given base message. 168 * @param base_message to create from 169 * @return AZ raw message 170 */ createAZRawMessage( Message base_message, int padding_mode )171 public static RawMessage createAZRawMessage( Message base_message, int padding_mode ) { 172 byte[] id_bytes = base_message.getIDBytes(); 173 byte version = base_message.getVersion(); 174 175 DirectByteBuffer[] payload = base_message.getData(); 176 177 int payload_size = 0; 178 for( int i=0; i < payload.length; i++ ) { 179 payload_size += payload[i].remaining( bss ); 180 } 181 182 //create and fill header buffer 183 184 DirectByteBuffer header; 185 186 if ( version >= MESSAGE_VERSION_SUPPORTS_PADDING ){ 187 188 boolean enable_padding = padding_mode != AZMessageEncoder.PADDING_MODE_NONE; 189 190 short padding_length; 191 192 if ( enable_padding ){ 193 194 if ( padding_mode == AZMessageEncoder.PADDING_MODE_MINIMAL ){ 195 196 padding_length = (short)( RandomUtils.nextInt( SMALL_PAD_MAX )); 197 198 }else{ 199 200 padding_length = (short)( RandomUtils.nextInt( payload_size>256?SMALL_PAD_MAX:BIG_PAD_MAX )); 201 } 202 203 if ( padding_length == 0 ){ 204 205 enable_padding = false; 206 } 207 }else{ 208 209 padding_length = 0; 210 } 211 212 byte flags = enable_padding?(byte)0x01:(byte)0x00; 213 214 int header_size = 4 + 4 + id_bytes.length + 1 + (enable_padding?(2+padding_length):0); 215 216 header = DirectByteBufferPool.getBuffer( DirectByteBuffer.AL_MSG_AZ_HEADER, header_size ); 217 218 header.putInt( bss, header_size - 4 + payload_size ); 219 header.putInt( bss, id_bytes.length ); 220 header.put( bss, id_bytes ); 221 222 byte version_and_flags = (byte)( ( flags << 4 ) | version ); 223 224 header.put( bss, version_and_flags ); 225 226 if ( enable_padding ){ 227 228 byte[] padding = new byte[padding_length]; 229 230 header.putShort( bss, padding_length ); 231 header.put( bss, padding ); 232 } 233 }else{ 234 235 int header_size = 4 + 4 + id_bytes.length + 1; 236 237 header = DirectByteBufferPool.getBuffer( DirectByteBuffer.AL_MSG_AZ_HEADER, header_size ); 238 239 header.putInt( bss, header_size - 4 + payload_size ); 240 header.putInt( bss, id_bytes.length ); 241 header.put( bss, id_bytes ); 242 header.put( bss, version ); 243 } 244 245 header.flip( bss ); 246 247 DirectByteBuffer[] raw_buffs = new DirectByteBuffer[ payload.length + 1 ]; 248 raw_buffs[0] = header; 249 for( int i=0; i < payload.length; i++ ) { 250 raw_buffs[i+1] = payload[i]; 251 } 252 253 String message_id = base_message.getID(); 254 255 LegacyData ld = (LegacyData)legacy_data.get( message_id ); //determine if a legacy BT message 256 257 if( ld != null ) { //legacy message, use pre-configured values 258 return new RawMessageImpl( base_message, raw_buffs, ld.priority, ld.is_no_delay, ld.to_remove ); 259 } 260 261 // these should really be properties of the message... 262 263 int priority; 264 boolean no_delay = true; 265 266 if ( message_id == AZMessage.ID_AZ_HANDSHAKE ){ 267 268 // handshake needs to go out first - if not high then bitfield can get in front of it... 269 270 priority = RawMessage.PRIORITY_HIGH; 271 272 }else if ( message_id == AZMessage.ID_AZ_HAVE ){ 273 274 priority = RawMessage.PRIORITY_LOW; 275 no_delay = false; 276 277 }else{ 278 279 //standard message, ensure that protocol messages have wire priority over data payload messages 280 281 priority = base_message.getType() == Message.TYPE_DATA_PAYLOAD ? RawMessage.PRIORITY_LOW : RawMessage.PRIORITY_NORMAL; 282 } 283 284 return new RawMessageImpl( base_message, raw_buffs, priority, no_delay, null ); 285 } 286 287 288 289 290 291 292 protected static class LegacyData { 293 protected final int priority; 294 protected final boolean is_no_delay; 295 protected final Message[] to_remove; 296 LegacyData( int prio, boolean no_delay, Message[] remove )297 protected LegacyData( int prio, boolean no_delay, Message[] remove ) { 298 this.priority = prio; 299 this.is_no_delay = no_delay; 300 this.to_remove = remove; 301 } 302 } 303 304 } 305