Upstream tarball 10013
[amule.git] / src / libs / ec / c# / ecProto.cs
blob9f0407b0927c673b3471773a05ea58cb6c360026
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2003-2008 Froenchenko Leonid ( lfroen@gmail.com / http://www.amule.org )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 using System;
27 using System.IO;
28 using System.Security.Cryptography;
29 using System.Collections.Generic;
30 using System.Text;
32 namespace amule.net
34 public enum EcTagTypes {
35 EC_TAGTYPE_UNKNOWN = 0,
36 EC_TAGTYPE_CUSTOM = 1,
37 EC_TAGTYPE_UINT8 = 2,
38 EC_TAGTYPE_UINT16 = 3,
39 EC_TAGTYPE_UINT32 = 4,
40 EC_TAGTYPE_UINT64 = 5,
41 EC_TAGTYPE_STRING = 6,
42 EC_TAGTYPE_DOUBLE = 7,
43 EC_TAGTYPE_IPV4 = 8,
44 EC_TAGTYPE_HASH16 = 9
47 public class ecProto {
49 public class ecTag {
50 protected int m_size;
51 protected EcTagTypes m_type;
52 protected ECTagNames m_name;
53 protected LinkedList<ecTag> m_subtags;
55 public ecTag(ECTagNames n, EcTagTypes t)
57 m_name = n;
58 m_type = t;
59 m_subtags = new LinkedList<ecTag>();
61 public ecTag(ECTagNames n, EcTagTypes t, LinkedList<ecTag> subtags)
63 m_name = n;
64 m_type = t;
65 m_subtags = subtags; ;
68 public ecTag()
70 m_subtags = new LinkedList<ecTag>();
73 public int SubtagCount()
75 return m_subtags.Count;
78 protected void WriteSubtags(BinaryWriter wr)
80 Int16 count16 = (Int16)m_subtags.Count;
81 if (count16 != 0) {
82 wr.Write(System.Net.IPAddress.HostToNetworkOrder(count16));
83 foreach (ecTag t in m_subtags)
85 t.Write(wr);
90 public ECTagNames Name()
92 return m_name;
95 public virtual void Write(BinaryWriter wr)
97 Int16 name16 = (Int16)(m_name);
98 name16 <<= 1;
99 byte type8 = (byte)m_type;
100 Int32 size32 = (Int32)Size();
101 if (m_subtags.Count != 0) {
102 name16 |= 1;
104 wr.Write(System.Net.IPAddress.HostToNetworkOrder(name16));
105 wr.Write(type8);
106 wr.Write(System.Net.IPAddress.HostToNetworkOrder(size32));
108 WriteSubtags(wr);
110 // here derived class will put actual data
114 protected int Size()
116 int total_size = m_size;
117 foreach (ecTag t in m_subtags) {
118 total_size += t.Size();
119 // name + type + size for each tag
120 total_size += (2 + 1 + 4);
121 if (t.HaveSubtags()) {
122 total_size += 2;
125 return total_size;
128 public LinkedList<ecTag>.Enumerator GetTagIterator()
130 return m_subtags.GetEnumerator();
133 public void AddSubtag(ecTag t)
135 m_subtags.AddLast(t);
138 bool HaveSubtags()
140 return (m_subtags.Count != 0);
143 public ecTag SubTag(ECTagNames name)
145 foreach (ecTag t in m_subtags) {
146 if (t.m_name == name) {
147 return t;
150 return null;
154 public class ecTagInt : ecTag {
155 protected Int64 m_val;
156 public ecTagInt(ECTagNames n, byte v)
157 : base(n, EcTagTypes.EC_TAGTYPE_UINT8)
159 m_val = v;
160 m_size = 1;
163 public ecTagInt(ECTagNames n, Int16 v)
164 : base(n, EcTagTypes.EC_TAGTYPE_UINT16)
166 m_val = v;
167 m_size = 2;
170 public ecTagInt(ECTagNames n, Int32 v)
171 : base(n, EcTagTypes.EC_TAGTYPE_UINT32)
173 m_val = v;
174 m_size = 4;
177 public ecTagInt(ECTagNames n, Int64 v)
178 : base(n, EcTagTypes.EC_TAGTYPE_UINT64)
180 m_val = v;
181 m_size = 8;
184 public ecTagInt(ECTagNames n, Int32 tag_size, BinaryReader br, LinkedList<ecTag> subtags)
185 : base(n, EcTagTypes.EC_TAGTYPE_UINT8, subtags)
187 m_size = tag_size;
188 UInt64 raw_val;
189 Int32 hi, lo, v32;
190 Int16 v16;
191 switch ( m_size ) {
192 case 8:
193 m_type = EcTagTypes.EC_TAGTYPE_UINT64;
194 lo = System.Net.IPAddress.NetworkToHostOrder(br.ReadInt32());
195 hi = System.Net.IPAddress.NetworkToHostOrder(br.ReadInt32());
196 raw_val = ((UInt64)hi) << 32 | (UInt32)lo;
197 break;
198 case 4:
199 m_type = EcTagTypes.EC_TAGTYPE_UINT32;
200 v32 = (System.Net.IPAddress.NetworkToHostOrder(br.ReadInt32()));
201 raw_val = (UInt32)v32;
202 break;
203 case 2:
204 m_type = EcTagTypes.EC_TAGTYPE_UINT16;
205 v16 = System.Net.IPAddress.NetworkToHostOrder(br.ReadInt16());
206 raw_val = (UInt16)v16;
207 break;
208 case 1:
209 m_type = EcTagTypes.EC_TAGTYPE_UINT8;
210 raw_val = (UInt64)br.ReadByte();
211 break;
212 default:
213 throw new Exception("Unexpected size of data in integer tag");
215 m_val = (Int64)raw_val;
216 if ( m_val < 0 ) {
217 throw new Exception("WTF - typecasting is broken?!");
221 public int ValueInt()
223 return (int)m_val;
226 public Int64 Value64()
228 return m_val;
231 public override void Write(BinaryWriter wr)
233 base.Write(wr);
235 switch ( m_size ) {
236 case 8:
237 Int32 val32 = (Int32)(m_val >> 32);
238 wr.Write(System.Net.IPAddress.HostToNetworkOrder(val32));
240 val32 = (Int32)(m_val & 0xffffffff);
241 wr.Write(System.Net.IPAddress.HostToNetworkOrder(val32));
242 break;
243 case 4:
244 val32 = (Int32)(m_val & 0xffffffff);
245 wr.Write(System.Net.IPAddress.HostToNetworkOrder(val32));
246 break;
247 case 2:
248 Int16 val16 = (Int16)(m_val & 0xffff);
249 wr.Write(System.Net.IPAddress.HostToNetworkOrder(val16));
250 break;
251 case 1:
252 wr.Write((byte)(m_val & 0xff));
253 break;
259 public class ecMD5 : IComparable<ecMD5>, IEquatable<ecMD5> {
260 Int64 m_lo, m_hi;
261 public ecMD5(Int64 lo, Int64 hi)
263 m_hi = hi;
264 m_lo = lo;
268 // actual byte order doesn't matter, but conversion must be consistant
270 public ecMD5(byte [] v)
272 m_lo = ((Int64)v[0] << 0) | ((Int64)v[1] << 8) | ((Int64)v[2] << 16) | ((Int64)v[3] << 24) |
273 ((Int64)v[4] << 32) | ((Int64)v[5] << 40) | ((Int64)v[6] << 48) | ((Int64)v[7] << 56);
274 m_hi = ((Int64)v[8] << 0) | ((Int64)v[9] << 8) | ((Int64)v[10] << 16) | ((Int64)v[11] << 24) |
275 ((Int64)v[12] << 32) | ((Int64)v[13] << 40) | ((Int64)v[14] << 48) | ((Int64)v[15] << 56);
278 public byte [] ByteValue()
280 byte[] v = {
281 (byte)(m_lo >> 0), (byte)(m_lo >> 8), (byte)(m_lo >> 16), (byte)(m_lo >> 24),
282 (byte)(m_lo >> 32), (byte)(m_lo >> 40), (byte)(m_lo >> 48), (byte)(m_lo >> 56),
283 (byte)(m_hi >> 0), (byte)(m_hi >> 8), (byte)(m_hi >> 16), (byte)(m_hi >> 24),
284 (byte)(m_hi >> 32), (byte)(m_hi >> 40), (byte)(m_hi >> 48), (byte)(m_hi >> 56),
286 return v;
289 public override int GetHashCode()
291 return (int)m_lo ^ (int)m_hi;
294 public bool Equals(ecMD5 i)
296 return (m_hi == i.m_hi) && (m_lo == i.m_lo);
299 public int CompareTo(ecMD5 i)
301 Int64 r = ((m_hi == i.m_hi) ? (m_lo - i.m_lo) : (m_hi - i.m_hi));
302 return r > 0 ? 1 : (r < 0 ? -1 : 0);
306 public class ecTagMD5 : ecTag {
307 byte[] m_val;
309 public ecTagMD5(ECTagNames n, ecMD5 value)
310 : base(n, EcTagTypes.EC_TAGTYPE_HASH16)
312 m_val = value.ByteValue();
313 m_size = 16;
316 public ecTagMD5(ECTagNames n, string s, bool string_is_hash)
317 : base(n, EcTagTypes.EC_TAGTYPE_HASH16)
319 if ( string_is_hash ) {
320 // in this case hash is passed as hex string
321 if ( s.Length != 16*2 ) {
322 throw new Exception("md5 hash of proto version have incorrect length");
324 //byte[] hash_str = s.ToCharArray();
325 for (int i = 0; i < 16; i++) {
326 string v = s.Substring(i * 2, 2);
329 m_val = new byte[16];
330 } else {
331 MD5CryptoServiceProvider p = new MD5CryptoServiceProvider();
332 byte[] bs = System.Text.Encoding.UTF8.GetBytes(s);
333 m_val = p.ComputeHash(bs);
335 m_size = 16;
338 public ecTagMD5(ECTagNames name, byte[] hash_data)
339 : base(name, EcTagTypes.EC_TAGTYPE_HASH16)
341 m_val = hash_data;
342 m_size = 16;
345 public ecTagMD5(ECTagNames name, BinaryReader br, LinkedList<ecTag> subtags)
346 : base(name, EcTagTypes.EC_TAGTYPE_HASH16, subtags)
348 m_size = 16;
349 m_val = br.ReadBytes(16);
352 public override void Write(BinaryWriter wr)
354 base.Write(wr);
355 wr.Write(m_val);
358 public ecMD5 ValueMD5()
360 return new ecMD5(m_val);
364 public class ecTagIPv4 : ecTag {
365 Int32 m_addr;
366 Int16 m_port;
367 public ecTagIPv4(ECTagNames name, BinaryReader br, LinkedList<ecTag> subtags)
368 : base(name, EcTagTypes.EC_TAGTYPE_IPV4, subtags)
370 m_size = 4+2;
371 m_addr = System.Net.IPAddress.NetworkToHostOrder(br.ReadInt32());
372 m_port = System.Net.IPAddress.NetworkToHostOrder(br.ReadInt16());
376 public class ecTagCustom : ecTag {
377 byte[] m_val;
378 public ecTagCustom(ECTagNames n, Int32 tag_size, BinaryReader br, LinkedList<ecTag> subtags)
379 : base(n, EcTagTypes.EC_TAGTYPE_CUSTOM, subtags)
381 m_val= br.ReadBytes(tag_size);
382 m_size = tag_size;
384 public byte [] Value()
386 return m_val;
391 public class ecTagString : ecTag {
392 byte[] m_val;
393 public ecTagString(ECTagNames n, string s)
394 : base(n, EcTagTypes.EC_TAGTYPE_STRING)
396 m_val = System.Text.Encoding.UTF8.GetBytes(s);
397 m_size = m_val.GetLength(0) + 1;
400 public ecTagString(ECTagNames n, Int32 tag_size, BinaryReader br, LinkedList<ecTag> subtags)
401 : base(n, EcTagTypes.EC_TAGTYPE_STRING, subtags)
403 byte[] buf = br.ReadBytes(tag_size-1);
404 // discard trailing '0'
405 br.ReadBytes(1);
407 m_size = tag_size;
408 m_val = buf;
411 public override void Write(BinaryWriter wr)
413 base.Write(wr);
414 wr.Write(m_val);
415 byte zero_byte = 0;
416 wr.Write(zero_byte);
418 public string StringValue()
420 Encoding u8 = Encoding.UTF8;
421 string s = u8.GetString(m_val);
422 return s;
426 public class ecPacket : ecTag {
427 // since I have no zlib here, effectively disable compression
428 const int MaxUncompressedPacket = 0x6666;
430 private ECOpCodes m_opcode;
431 protected Int32 m_flags;
434 // Parsing ctor
436 ecTag ReadTag(BinaryReader br)
438 ecTag t = null;
439 Int16 tag_name16 = System.Net.IPAddress.NetworkToHostOrder(br.ReadInt16());
440 bool have_subtags = ((tag_name16 & 1) != 0);
441 ECTagNames tag_name = (ECTagNames)(tag_name16 >> 1);
443 byte tag_type8 = br.ReadByte();
444 Int32 tag_size32 = System.Net.IPAddress.NetworkToHostOrder(br.ReadInt32());
445 LinkedList<ecTag> subtags = null;
446 if ( have_subtags ) {
447 subtags = ReadSubtags(br);
449 EcTagTypes tag_type = (EcTagTypes)tag_type8;
450 switch (tag_type) {
451 case EcTagTypes.EC_TAGTYPE_UNKNOWN:
452 break;
453 case EcTagTypes.EC_TAGTYPE_CUSTOM:
454 t = new ecTagCustom(tag_name, tag_size32, br, subtags);
455 break;
457 case EcTagTypes.EC_TAGTYPE_UINT8:
458 t = new ecTagInt(tag_name, 1, br, subtags);
459 break;
460 case EcTagTypes.EC_TAGTYPE_UINT16:
461 t = new ecTagInt(tag_name, 2, br, subtags);
462 break;
463 case EcTagTypes.EC_TAGTYPE_UINT32:
464 t = new ecTagInt(tag_name, 4, br, subtags);
465 break;
466 case EcTagTypes.EC_TAGTYPE_UINT64:
467 t = new ecTagInt(tag_name, 8, br, subtags);
468 break;
470 case EcTagTypes.EC_TAGTYPE_STRING:
471 t = new ecTagString(tag_name, tag_size32, br, subtags);
472 break;
473 case EcTagTypes.EC_TAGTYPE_DOUBLE:
474 break;
475 case EcTagTypes.EC_TAGTYPE_IPV4:
476 t = new ecTagIPv4(tag_name, br, subtags);
477 break;
478 case EcTagTypes.EC_TAGTYPE_HASH16:
479 t = new ecTagMD5(tag_name, br, subtags);
480 break;
481 default:
482 break;
484 if ( t == null ) {
485 throw new Exception("Unexpected tag type");
487 return t;
490 LinkedList<ecTag> ReadSubtags(BinaryReader br)
492 Int16 count16 = System.Net.IPAddress.NetworkToHostOrder(br.ReadInt16());
493 LinkedList<ecTag> taglist = new LinkedList<ecTag>();
494 for (int i = 0; i < count16;i++) {
495 ecTag st = ReadTag(br);
496 taglist.AddLast(st);
498 return taglist;
501 public ecPacket()
503 m_flags = 0x20;
506 public ecPacket(BinaryReader br)
508 m_flags = System.Net.IPAddress.NetworkToHostOrder(br.ReadInt32());
509 Int32 packet_size = System.Net.IPAddress.NetworkToHostOrder(br.ReadInt32());
510 m_opcode = (ECOpCodes)br.ReadByte();
512 Int16 tags_count = System.Net.IPAddress.NetworkToHostOrder(br.ReadInt16());
514 if ( tags_count != 0 ) {
515 for (int i = 0; i < tags_count; i++) {
516 ecTag t = ReadTag(br);
517 AddSubtag(t);
523 // Default ctor - for tx packets
524 public ecPacket(ECOpCodes cmd)
526 m_flags = 0x20;
527 m_opcode = cmd;
530 public ecPacket(ECOpCodes cmd, EC_DETAIL_LEVEL detail_level)
532 m_flags = 0x20;
533 m_opcode = cmd;
534 if ( detail_level != EC_DETAIL_LEVEL.EC_DETAIL_FULL ) {
535 AddSubtag(new ecTagInt(ECTagNames.EC_TAG_DETAIL_LEVEL, (Int64)detail_level));
540 // Size of data for TX, not of payload
542 public int PacketSize()
544 int packet_size = Size();
545 if ((m_flags & (UInt32)ECFlags.EC_FLAG_ACCEPTS) != 0) {
546 packet_size += 4;
548 // 1 (command) + 2 (tag count) + 4 (flags) + 4 (total size)
549 return packet_size + 1 + 2 + 4 + 4;
552 public ECOpCodes Opcode()
554 return m_opcode;
557 public override void Write(BinaryWriter wr)
559 // 1 (command) + 2 (tag count)
560 int packet_size = Size() + 1 + 2;
561 if ( packet_size > MaxUncompressedPacket ) {
562 m_flags |= (Int32)ECFlags.EC_FLAG_ZLIB;
565 if ((m_flags & (UInt32)ECFlags.EC_FLAG_ZLIB) != 0) {
566 throw new NotImplementedException("no zlib compression yet");
570 wr.Write(System.Net.IPAddress.HostToNetworkOrder((Int32)(m_flags)));
571 if ((m_flags & (UInt32)ECFlags.EC_FLAG_ACCEPTS) != 0) {
572 wr.Write(System.Net.IPAddress.HostToNetworkOrder((Int32)(m_flags)));
575 wr.Write(System.Net.IPAddress.HostToNetworkOrder((Int32)(packet_size)));
576 wr.Write((byte)m_opcode);
577 if ( m_subtags.Count != 0 ) {
578 WriteSubtags(wr);
579 } else {
580 wr.Write((Int16)(0));
587 // Specific - purpose tags
590 public class ecLoginPacket : ecPacket {
591 public ecLoginPacket(string client_name, string version, string pass)
592 : base(ECOpCodes.EC_OP_AUTH_REQ)
594 m_flags |= 0x20 | (Int32)ECFlags.EC_FLAG_ACCEPTS;
596 AddSubtag(new ecTagString(ECTagNames.EC_TAG_CLIENT_NAME, client_name));
597 AddSubtag(new ecTagString(ECTagNames.EC_TAG_CLIENT_VERSION, version));
598 AddSubtag(new ecTagInt(ECTagNames.EC_TAG_PROTOCOL_VERSION,
599 (Int64)ProtocolVersion.EC_CURRENT_PROTOCOL_VERSION));
601 AddSubtag(new ecTagMD5(ECTagNames.EC_TAG_PASSWD_HASH, pass, false));
603 // discussion is ongoing
604 //AddSubtag(new ecTagMD5(ECTagNames.EC_TAG_VERSION_ID, EC_VERSION_ID, true));
608 public class ecDownloadsInfoReq : ecPacket {
609 public ecDownloadsInfoReq() : base(ECOpCodes.EC_OP_GET_DLOAD_QUEUE)
615 // Class exists only for parsing purpose
617 public class ecConnStateTag {
618 ecTagInt m_tag;
619 Int32 m_tag_val;
620 public ecConnStateTag(ecTagInt tag)
622 m_tag = tag;
623 //m_tag_val = (Int32)tag.Value64();
624 m_tag_val = 0xfff;
627 //public static explicit operator ecConnStateTag(ecTagInt t)
629 // return new ecConnStateTag(t.ValueInt64());
632 public bool IsConnected()
634 return IsConnectedED2K() || IsConnectedKademlia();
637 public bool IsConnectedED2K()
639 return (m_tag_val & 0x01) != 0;
642 public bool IsConnectingED2K()
644 return (m_tag_val & 0x02) != 0;
647 public bool IsConnectedKademlia()
649 return (m_tag_val & 0x04) != 0;
652 public bool IsKadFirewalled()
654 return (m_tag_val & 0x08) != 0;
657 public bool IsKadRunning()
659 return (m_tag_val & 0x10) != 0;
662 public ecProto.ecTag Server()
664 return m_tag.SubTag(ECTagNames.EC_TAG_SERVER);