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