1 /* 2 * Created on Apr 30, 2004 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.net.InetAddress; 23 import java.util.*; 24 25 import org.gudy.azureus2.core3.util.ByteFormatter; 26 import org.gudy.azureus2.core3.util.Constants; 27 import org.gudy.azureus2.core3.util.Debug; 28 import org.gudy.azureus2.core3.util.DirectByteBuffer; 29 import org.gudy.azureus2.core3.util.HashWrapper; 30 import org.gudy.azureus2.core3.util.RandomUtils; 31 32 import com.aelitis.azureus.core.peermanager.messaging.Message; 33 import com.aelitis.azureus.core.peermanager.messaging.MessageException; 34 import com.aelitis.azureus.core.peermanager.messaging.MessagingUtil; 35 36 37 38 39 /** 40 * AZ handshake message. 41 */ 42 public class AZHandshake implements AZMessage { 43 44 public static final int HANDSHAKE_TYPE_PLAIN = 0; 45 public static final int HANDSHAKE_TYPE_CRYPTO = 1; 46 47 private static final byte bss = DirectByteBuffer.SS_MSG; 48 49 private final byte version; 50 private DirectByteBuffer buffer = null; 51 private String description = null; 52 53 private final byte[] identity; 54 private final HashWrapper sessionID; 55 private final HashWrapper reconnectID; 56 private final String client; 57 private final String client_version; 58 private final String[] avail_ids; 59 private final byte[] avail_versions; 60 private int tcp_port; 61 private int udp_port; 62 private int udp_non_data_port; 63 private final int handshake_type; 64 private final boolean uploadOnly; 65 private final InetAddress ipv6; 66 private final int md_size; 67 68 AZHandshake( byte[] peer_identity, HashWrapper sessionID, HashWrapper reconnectID, String _client, String version, int tcp_listen_port, int udp_listen_port, int udp_non_data_listen_port, InetAddress ipv6addr, int md_size, String[] avail_msg_ids, byte[] avail_msg_versions, int _handshake_type, byte _version, boolean uploadOnly)69 public AZHandshake( byte[] peer_identity, 70 HashWrapper sessionID, 71 HashWrapper reconnectID, 72 String _client, 73 String version, 74 int tcp_listen_port, 75 int udp_listen_port, 76 int udp_non_data_listen_port, 77 InetAddress ipv6addr, 78 int md_size, 79 String[] avail_msg_ids, 80 byte[] avail_msg_versions, 81 int _handshake_type, 82 byte _version, 83 boolean uploadOnly) { 84 85 this.identity = peer_identity; 86 this.sessionID = sessionID; 87 this.reconnectID = reconnectID; 88 this.client = _client; 89 this.client_version = version; 90 this.avail_ids = avail_msg_ids; 91 this.avail_versions = avail_msg_versions; 92 this.tcp_port = tcp_listen_port; 93 this.udp_port = udp_listen_port; 94 this.udp_non_data_port = udp_non_data_listen_port; 95 this.handshake_type = _handshake_type; 96 this.version = _version; 97 this.uploadOnly = uploadOnly; 98 this.ipv6 = ipv6addr; 99 this.md_size = md_size; 100 101 //verify given port info is ok 102 if( tcp_port < 0 || tcp_port > 65535 ) { 103 Debug.out( "given TCP listen port is invalid: " +tcp_port ); 104 tcp_port = 0; 105 } 106 107 if( udp_port < 0 || udp_port > 65535 ) { 108 Debug.out( "given UDP listen port is invalid: " +udp_port ); 109 udp_port = 0; 110 } 111 112 if( udp_non_data_port < 0 || udp_non_data_port > 65535 ) { 113 Debug.out( "given UDP non-data listen port is invalid: " +udp_non_data_port ); 114 udp_non_data_port = 0; 115 } 116 } 117 118 119 getIdentity()120 public byte[] getIdentity() { return identity; } getRemoteSessionID()121 public HashWrapper getRemoteSessionID() { return sessionID; } getReconnectSessionID()122 public HashWrapper getReconnectSessionID() { return reconnectID; } isUploadOnly()123 public boolean isUploadOnly() {return uploadOnly;} 124 125 getClient()126 public String getClient() { return client; } 127 getClientVersion()128 public String getClientVersion() { return client_version; } 129 getMessageIDs()130 public String[] getMessageIDs() { return avail_ids; } 131 getMessageVersions()132 public byte[] getMessageVersions() { return avail_versions; } 133 getTCPListenPort()134 public int getTCPListenPort() { return tcp_port; } getUDPListenPort()135 public int getUDPListenPort() { return udp_port; } getUDPNonDataListenPort()136 public int getUDPNonDataListenPort() { return udp_non_data_port; } getIPv6()137 public InetAddress getIPv6() { return ipv6; } getMetadataSize()138 public int getMetadataSize(){ return md_size; } getHandshakeType()139 public int getHandshakeType() { return handshake_type; } 140 141 getID()142 public String getID() { return AZMessage.ID_AZ_HANDSHAKE; } getIDBytes()143 public byte[] getIDBytes() { return AZMessage.ID_AZ_HANDSHAKE_BYTES; } 144 getFeatureID()145 public String getFeatureID() { return AZMessage.AZ_FEATURE_ID; } 146 getFeatureSubID()147 public int getFeatureSubID() { return AZMessage.SUBID_AZ_HANDSHAKE; } 148 149 getType()150 public int getType() { return Message.TYPE_PROTOCOL_PAYLOAD; } 151 getVersion()152 public byte getVersion() { return version; }; 153 getDescription()154 public String getDescription() { 155 if( description == null ) { 156 String msgs_desc = ""; 157 for( int i=0; i < avail_ids.length; i++ ) { 158 String id = avail_ids[ i ]; 159 byte ver = avail_versions[ i ]; 160 if( id.equals( getID() ) ) continue; //skip ourself 161 msgs_desc += "[" +id+ ":" +ver+ "]"; 162 } 163 description = getID()+ " from [" +ByteFormatter.nicePrint( identity, true )+ ", " + 164 client+ " " +client_version+ ", TCP/UDP ports " +tcp_port+ "/" +udp_port+ "/" + udp_non_data_port + 165 ", handshake " + (getHandshakeType() == HANDSHAKE_TYPE_PLAIN ? "plain" : "crypto") + 166 ", upload_only = " + (isUploadOnly() ? "1" : "0") + 167 (ipv6 != null ? ", ipv6 = "+ipv6.getHostAddress() : "") + 168 ", md_size=" + md_size + 169 (sessionID != null ? ", sessionID: "+sessionID.toBase32String() : "") + 170 (reconnectID != null ? ", reconnect request: "+reconnectID.toBase32String() : "") + 171 "] supports " +msgs_desc; 172 } 173 174 return description; 175 } 176 getData()177 public DirectByteBuffer[] getData() { 178 if (buffer == null) 179 { 180 Map payload_map = new HashMap(); 181 //client info 182 payload_map.put("identity", identity); 183 if (sessionID != null) 184 payload_map.put("session", sessionID.getBytes()); 185 if (reconnectID != null) 186 payload_map.put("reconn", reconnectID.getBytes()); 187 payload_map.put("client", client); 188 payload_map.put("version", client_version); 189 payload_map.put("tcp_port", new Long(tcp_port)); 190 payload_map.put("udp_port", new Long(udp_port)); 191 payload_map.put("udp2_port", new Long(udp_non_data_port)); 192 payload_map.put("handshake_type", new Long(handshake_type)); 193 payload_map.put("upload_only", new Long(uploadOnly ? 1L : 0L)); 194 if(ipv6 != null) 195 payload_map.put("ipv6", ipv6.getAddress()); 196 if ( md_size > 0 ){ 197 payload_map.put("mds", new Long(md_size)); 198 } 199 //available message list 200 List message_list = new ArrayList(); 201 for (int i = 0; i < avail_ids.length; i++) 202 { 203 String id = avail_ids[i]; 204 byte ver = avail_versions[i]; 205 if (id.equals(getID())) 206 continue; //skip ourself 207 Map msg = new HashMap(); 208 msg.put("id", id); 209 msg.put("ver", new byte[] { ver }); 210 message_list.add(msg); 211 } 212 213 payload_map.put("messages", message_list); 214 215 // random padding if crypto 216 if (handshake_type == AZHandshake.HANDSHAKE_TYPE_CRYPTO) 217 payload_map.put("pad", new byte[RandomUtils.nextInt( AZMessageFactory.AZ_HANDSHAKE_PAD_MAX)]); 218 219 buffer = MessagingUtil.convertPayloadToBencodedByteStream(payload_map, DirectByteBuffer.AL_MSG_AZ_HAND); 220 if (buffer.remaining(bss) > 1200) 221 System.out.println("Generated AZHandshake size = " + buffer.remaining(bss) + " bytes"); 222 } 223 224 return new DirectByteBuffer[] { buffer }; 225 } 226 227 deserialize( DirectByteBuffer data, byte version )228 public Message deserialize( DirectByteBuffer data, byte version ) throws MessageException { 229 Map root = MessagingUtil.convertBencodedByteStreamToPayload( data, 100, getID() ); 230 231 byte[] id = (byte[])root.get( "identity" ); 232 if( id == null ) throw new MessageException( "id == null" ); 233 if( id.length != 20 ) throw new MessageException( "id.length != 20: " +id.length ); 234 235 byte[] session = (byte[])root.get("session"); 236 byte[] reconnect = (byte[])root.get("reconn"); 237 238 byte[] raw_name = (byte[])root.get( "client" ); 239 if( raw_name == null ) throw new MessageException( "raw_name == null" ); 240 String name = new String( raw_name ); 241 242 byte[] raw_ver = (byte[])root.get( "version" ); 243 if( raw_ver == null ) throw new MessageException( "raw_ver == null" ); 244 String client_version = new String( raw_ver ); 245 246 Long tcp_lport = (Long)root.get( "tcp_port" ); 247 if( tcp_lport == null ) { //old handshake 248 tcp_lport = new Long( 0 ); 249 } 250 251 Long udp_lport = (Long)root.get( "udp_port" ); 252 if( udp_lport == null ) { //old handshake 253 udp_lport = new Long( 0 ); 254 } 255 256 Long udp2_lport = (Long)root.get( "udp2_port" ); 257 if( udp2_lport == null ) { //old handshake 258 udp2_lport = udp_lport; 259 } 260 261 Long h_type = (Long)root.get( "handshake_type" ); 262 if( h_type == null ) { //only 2307+ send type 263 h_type = new Long( HANDSHAKE_TYPE_PLAIN ); 264 } 265 266 InetAddress ipv6 = null; 267 if(root.get("ipv6") instanceof byte[]) 268 { 269 try 270 { 271 InetAddress.getByAddress((byte[]) root.get("ipv6")); 272 } catch (Exception e) 273 { 274 275 } 276 } 277 int md_size = 0; 278 Long mds = (Long)root.get( "mds" ); 279 if ( mds != null ){ 280 md_size = mds.intValue(); 281 } 282 List raw_msgs = (List) root.get("messages"); 283 if (raw_msgs == null) throw new MessageException("raw_msgs == null"); 284 285 String[] ids = new String[raw_msgs.size()]; 286 byte[] vers = new byte[raw_msgs.size()]; 287 288 int pos = 0; 289 290 for (Iterator i = raw_msgs.iterator(); i.hasNext();) { 291 Map msg = (Map) i.next(); 292 293 byte[] mid = (byte[]) msg.get("id"); 294 if (mid == null) throw new MessageException("mid == null"); 295 ids[pos] = new String(mid); 296 297 byte[] ver = (byte[]) msg.get("ver"); 298 if (ver == null) throw new MessageException("ver == null"); 299 300 if (ver.length != 1) throw new MessageException("ver.length != 1"); 301 vers[pos] = ver[0]; 302 303 pos++; 304 } 305 306 Long ulOnly = (Long)root.get("upload_only"); 307 boolean uploadOnly = ulOnly != null && ulOnly.longValue() > 0L ? true : false; 308 309 if ( name.equals( Constants.AZUREUS_PROTOCOL_NAME_PRE_4813 )){ 310 name = Constants.AZUREUS_PROTOCOL_NAME; 311 } 312 return new AZHandshake( id, session == null ? null : new HashWrapper(session),reconnect == null ? null : new HashWrapper(reconnect), name, client_version, tcp_lport.intValue(), udp_lport.intValue(), udp2_lport.intValue(), ipv6, md_size, ids, vers, h_type.intValue(), version , uploadOnly); 313 } 314 315 destroy()316 public void destroy() { 317 if( buffer != null ) buffer.returnToPool(); 318 } 319 } 320