/* * Created on Apr 30, 2004 * Created by Alon Rohter * Copyright (C) 2004, 2005, 2006 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.azureus; import java.util.*; import org.gudy.azureus2.core3.logging.LogEvent; import org.gudy.azureus2.core3.logging.LogIDs; import org.gudy.azureus2.core3.logging.Logger; import org.gudy.azureus2.core3.util.*; import com.aelitis.azureus.core.peermanager.messaging.*; import com.aelitis.azureus.core.peermanager.peerdb.*; /** * AZ peer exchange message. */ public class AZPeerExchange implements AZMessage, AZStylePeerExchange { private static final LogIDs LOGID = LogIDs.NET; private static final byte bss = DirectByteBuffer.SS_MSG; private DirectByteBuffer buffer = null; private String description = null; private final byte version; private final byte[] infohash; private final PeerItem[] peers_added; private final PeerItem[] peers_dropped; public AZPeerExchange( byte[] _infohash, PeerItem[] _peers_added, PeerItem[] _peers_dropped, byte version ) { this.infohash = _infohash; this.peers_added = _peers_added; this.peers_dropped = _peers_dropped; this.version = version; } private void insertPeers( String key_name, Map root_map, PeerItem[] peers ) { if( peers != null && peers.length > 0 ) { ArrayList raw_peers = new ArrayList(); byte[] handshake_types = new byte[ peers.length ]; byte[] udp_ports = new byte[peers.length*2]; // 2403 B55+ int num_valid_udp = 0; for( int i=0; i < peers.length; i++ ) { raw_peers.add( peers[i].getSerialization() ); handshake_types[i] = (byte)peers[i].getHandshakeType(); int udp_port = peers[i].getUDPPort(); if ( udp_port > 0 ){ num_valid_udp++; udp_ports[i*2] = (byte)(udp_port>>8); udp_ports[i*2+1] = (byte)udp_port; } } root_map.put( key_name, raw_peers ); root_map.put( key_name + "_HST", handshake_types ); if ( num_valid_udp > 0 ){ root_map.put( key_name + "_UDP", udp_ports ); } } } private PeerItem[] extractPeers( String key_name, Map root_map ) { PeerItem[] return_peers = null; ArrayList peers = new ArrayList(); List raw_peers = (List)root_map.get( key_name ); if( raw_peers != null ) { int peer_num = raw_peers.size(); byte[] handshake_types = (byte[])root_map.get( key_name + "_HST" ); byte[] udp_ports = (byte[])root_map.get( key_name + "_UDP" ); // 2403 B55+ int pos = 0; if ( handshake_types != null && handshake_types.length != peer_num ){ Logger.log(new LogEvent( LOGID, LogEvent.LT_WARNING,"PEX: invalid handshake types received: peers=" + peer_num + ",handshakes=" + handshake_types.length )); handshake_types = null; } if ( udp_ports != null && udp_ports.length != peer_num*2 ){ Logger.log(new LogEvent( LOGID, LogEvent.LT_WARNING,"PEX: invalid udp ports received: peers=" + peer_num + ",udp_ports=" + udp_ports.length )); udp_ports = null; } for( Iterator it = raw_peers.iterator(); it.hasNext(); ) { byte[] full_address = (byte[])it.next(); byte type = PeerItemFactory.HANDSHAKE_TYPE_PLAIN; if( handshake_types != null ) { //only 2307+ send types type = handshake_types[pos]; } int udp_port = 0; if ( udp_ports != null ){ udp_port = ((udp_ports[pos*2]<<8)&0xff00) + (udp_ports[pos*2+1]&0xff); } try{ PeerItem peer = PeerItemFactory.createPeerItem( full_address, PeerItemFactory.PEER_SOURCE_PEER_EXCHANGE, type, udp_port ); peers.add( peer ); }catch( Exception t ){ Logger.log(new LogEvent( LOGID, LogEvent.LT_WARNING,"PEX: invalid peer received" )); } pos++; } } if( !peers.isEmpty() ) { return_peers = new PeerItem[ peers.size() ]; peers.toArray( return_peers ); } return return_peers; } public byte[] getInfoHash() { return infohash; } public PeerItem[] getAddedPeers() { return peers_added; } public PeerItem[] getDroppedPeers() { return peers_dropped; } public String getID() { return AZMessage.ID_AZ_PEER_EXCHANGE; } public byte[] getIDBytes() { return AZMessage.ID_AZ_PEER_EXCHANGE_BYTES; } public String getFeatureID() { return AZMessage.AZ_FEATURE_ID; } public int getFeatureSubID() { return AZMessage.SUBID_AZ_PEER_EXCHANGE; } public int getType() { return Message.TYPE_PROTOCOL_PAYLOAD; } public byte getVersion() { return version; }; public String getDescription() { if( description == null ) { int add_count = peers_added == null ? 0 : peers_added.length; int drop_count = peers_dropped == null ? 0 : peers_dropped.length; description = getID()+ " for infohash " +ByteFormatter.nicePrint( infohash, true )+ " with " +add_count+ " added and " +drop_count+ " dropped peers"; } return description; } public DirectByteBuffer[] getData() { if( buffer == null ) { Map payload_map = new HashMap(); payload_map.put( "infohash", infohash ); insertPeers( "added", payload_map, peers_added ); insertPeers( "dropped", payload_map, peers_dropped ); buffer = MessagingUtil.convertPayloadToBencodedByteStream( payload_map, DirectByteBuffer.AL_MSG_AZ_PEX ); if( buffer.remaining( bss ) > 2000 ) System.out.println( "Generated AZPeerExchange size = " +buffer.remaining( bss )+ " bytes" ); } return new DirectByteBuffer[]{ buffer }; } public Message deserialize( DirectByteBuffer data, byte version ) throws MessageException { if( data.remaining( bss ) > 2000 ) System.out.println( "Received PEX msg byte size = " +data.remaining( bss ) ); Map root = MessagingUtil.convertBencodedByteStreamToPayload( data, 10, getID() ); byte[] hash = (byte[])root.get( "infohash" ); if( hash == null ) throw new MessageException( "hash == null" ); if( hash.length != 20 ) throw new MessageException( "hash.length != 20: " +hash.length ); PeerItem[] added = extractPeers( "added", root ); PeerItem[] dropped = extractPeers( "dropped", root ); if( added == null && dropped == null ) throw new MessageException( "[" +getID()+ "] received exchange message without any adds or drops" ); return new AZPeerExchange( hash, added, dropped, version ); } public void destroy() { if( buffer != null ) buffer.returnToPool(); } public int getMaxAllowedPeersPerVolley(boolean initial, boolean added) { return PeerExchangerItem.MAX_PEERS_PER_VOLLEY; } }