1 module buffer.packet;
2 
3 import std.variant;
4 import std.bitmanip;
5 import std.traits;
6 import std.typecons;
7 import std.conv : to;
8 
9 import cryption.aes;
10 import cryption.tea.xtea;
11 import cryption.rsa;
12 
13 import buffer.utils;
14 
15 enum CryptType
16 {
17     NONE = 0,
18     XTEA = 1,
19     AES  = 2,
20     RSA  = 3
21 }
22 
23 template TypeID(Type)
24 {
25          static if (is(Unqual!Type == byte))
26         const ubyte TypeID = 0x01;
27     else static if (is(Unqual!Type == ubyte))
28         const ubyte TypeID = 0x02;
29     else static if (is(Unqual!Type == short))
30         const ubyte TypeID = 0x03;
31     else static if (is(Unqual!Type == ushort))
32         const ubyte TypeID = 0x04;
33     else static if (is(Unqual!Type == int))
34         const ubyte TypeID = 0x05;
35     else static if (is(Unqual!Type == uint))
36         const ubyte TypeID = 0x06;
37     else static if (is(Unqual!Type == long))
38         const ubyte TypeID = 0x07;
39     else static if (is(Unqual!Type == ulong))
40         const ubyte TypeID = 0x08;
41     else static if (is(Unqual!Type == float))
42         const ubyte TypeID = 0x20;
43     else static if (is(Unqual!Type == double))
44         const ubyte TypeID = 0x21;
45     else static if (is(Unqual!Type == bool))
46         const ubyte TypeID = 0x30;
47     else static if (is(Unqual!Type == char))
48         const ubyte TypeID = 0x40;
49     else static if (is(Unqual!Type == string))
50         const ubyte TypeID = 0x41;
51     else
52         static assert(0, "Data types that are not supported: " ~ typeid(Type));
53 }
54 
55 package class Packet
56 {
57     static ubyte[] build(ushort magic, CryptType crypt, string key, Nullable!RSAKeyInfo rsaKey, ushort messageId, string method, Variant[] params)
58     {
59         assert(params.length > 0, "Parameter params must be provided.");
60         assert(method.length <= 255, "Paramter method cannot be greater than 255 characters.");
61 
62         ubyte[] temp;
63         ubyte[] tlv;
64 
65         void put(T)(Variant v)
66         {
67             tlv ~= TypeID!T;
68             temp = new ubyte[T.sizeof];
69             temp.write!T(v.get!T, 0);
70             tlv ~= temp;
71         }
72 
73         foreach (v; params)
74         {
75             if (v.type == typeid(byte))
76             {
77                 put!byte(v);
78             }
79             else if (v.type == typeid(ubyte))
80             {
81                 put!ubyte(v);
82             }
83             else if (v.type == typeid(short))
84             {
85                 put!short(v);
86             }
87             else if (v.type == typeid(ushort))
88             {
89                 put!ushort(v);
90             }
91             else if (v.type == typeid(int))
92             {
93                 put!int(v);
94             }
95             else if (v.type == typeid(uint))
96             {
97                 put!uint(v);
98             }
99             else if (v.type == typeid(long))
100             {
101                 put!long(v);
102             }
103             else if (v.type == typeid(ulong))
104             {
105                 put!ulong(v);
106             }
107             else if (v.type == typeid(float))
108             {
109                 put!float(v);
110             }
111             else if (v.type == typeid(double))
112             {
113                 put!double(v);
114             }
115             else if (v.type == typeid(bool))
116             {
117                 put!bool(v);
118             }
119             else if (v.type == typeid(char))
120             {
121                 put!char(v);
122             }
123             else if (v.type == typeid(string))
124             {
125                 tlv ~= TypeID!string;
126                 string str = v.get!string;
127                 temp = new ubyte[4];
128                 temp.write!int(cast(int) str.length, 0);
129                 tlv ~= temp;
130                 temp = cast(ubyte[]) str;
131                 tlv ~= temp;
132             }
133             else
134             {
135                 assert(0, "Data types id that are not supported: " ~ v.type.toString);
136             }
137         }
138 
139         final switch (crypt)
140         {
141         case CryptType.NONE:
142             break;
143         case CryptType.XTEA:
144             tlv = Xtea.encrypt(tlv, key);
145             break;
146         case CryptType.AES:
147             tlv = AESUtils.encrypt!AES128(tlv, key);
148             break;
149         case CryptType.RSA:
150             tlv = RSA.encrypt(rsaKey, tlv);
151             break;
152         }
153 
154         ubyte[] method_buf = cast(ubyte[]) method;
155         ubyte[] buffer = new ubyte[10];
156 
157         buffer.write!ushort(magic, 0);
158         buffer.write!int(cast(int)(2 + 2 + method_buf.length + tlv.length + 2), 2);
159         buffer.write!ushort(messageId, 6);
160         buffer.write!ushort(cast(ushort) method_buf.length, 8);
161         if (method_buf.length > 0)
162             buffer ~= method_buf;
163         buffer ~= tlv;
164         buffer ~= strToByte_hex(MD5(buffer)[0 .. 4]);
165 
166         return buffer;
167     }
168 
169     static void parseInfo(ubyte[] buffer, out ushort messageId, out string method)
170     {
171         assert(buffer != null && buffer.length >= 12, "Incorrect buffer length.");
172 
173         messageId = buffer.peek!ushort(6);
174 
175         ushort t_method_len = buffer.peek!ushort(8);
176         if (t_method_len > 0)
177         {
178             method = cast(string) buffer[10 .. 10 + t_method_len];
179         }
180     }
181 
182     static Variant[] parse(ubyte[] buffer, ushort magic, CryptType crypt, string key, Nullable!RSAKeyInfo rsaKey, out ushort messageId, out string method)
183     {
184         assert(buffer != null && buffer.length >= 12, "Incorrect buffer length.");
185 
186         ushort t_magic, t_crc;
187         int t_len;
188         t_magic = buffer.peek!ushort(0);
189         t_len = buffer.peek!int(2);
190 
191         if ((t_magic != magic) || (t_len > buffer.length - 6))
192             return null;
193 
194         buffer = buffer[0 .. t_len + 6];
195         t_crc = buffer.peek!ushort(buffer.length - 2);
196         if (strToByte_hex(MD5(buffer[0 .. $ - 2])[0 .. 4]) != buffer[$ - 2 .. $])
197             return null;
198 
199         parseInfo(buffer, messageId, method);
200 
201         ushort t_method_len = buffer.peek!ushort(8);
202         buffer = buffer[10 + t_method_len .. $ - 2];
203 
204         final switch (crypt)
205         {
206         case CryptType.NONE:
207             break;
208         case CryptType.XTEA:
209             buffer = Xtea.decrypt(buffer, key);
210             break;
211         case CryptType.AES:
212             buffer = AESUtils.decrypt!AES128(buffer, key);
213             break;
214         case CryptType.RSA:
215             buffer = RSA.decrypt(rsaKey, buffer);
216             break;
217         }
218 
219         ubyte typeId;
220         int pos;
221         Variant[] ret;
222 
223         void get(T)()
224         {
225             ret ~= Variant(buffer.peek!T(pos));
226             pos += T.sizeof;
227         }
228 
229         while (pos < buffer.length)
230         {
231             typeId = buffer[pos];
232             pos++;
233 
234             if (typeId == TypeID!byte)
235             {
236                 get!byte;
237             }
238             else if (typeId == TypeID!ubyte)
239             {
240                 get!ubyte;
241             }
242             else if (typeId == TypeID!short)
243             {
244                 get!short;
245             }
246             else if (typeId == TypeID!ushort)
247             {
248                 get!ushort;
249             }
250             else if (typeId == TypeID!int)
251             {
252                 get!int;
253             }
254             else if (typeId == TypeID!uint)
255             {
256                 get!uint;
257             }
258             else if (typeId == TypeID!long)
259             {
260                 get!long;
261             }
262             else if (typeId == TypeID!ulong)
263             {
264                 get!ulong;
265             }
266             else if (typeId == TypeID!float)
267             {
268                 get!float;
269             }
270             else if (typeId == TypeID!double)
271             {
272                 get!double;
273             }
274             else if (typeId == TypeID!bool)
275             {
276                 get!bool;
277             }
278             else if (typeId == TypeID!char)
279             {
280                 get!char;
281             }
282             else if (typeId == TypeID!string)
283             {
284                 int temp = buffer.peek!int(pos);
285                 pos += 4;
286                 ret ~= Variant(cast(string) buffer[pos .. pos + temp]);
287                 pos += temp;
288             }
289             else
290             {
291                 assert(0, "Data types id that are not supported: " ~ typeId.to!string);
292             }
293         }
294 
295         return ret;
296     }
297 }