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 import std.exception;
9 
10 import crypto.aes;
11 import crypto.tea;
12 import crypto.rsa;
13 
14 import buffer.utils;
15 
16 enum CryptType
17 {
18     NONE            = 0,
19     XTEA            = 1,
20     AES             = 2,
21     RSA             = 3,
22     RSA_XTEA_MIXIN  = 4
23 }
24 
25 template TypeID(Type)
26 {
27          static if (is(Unqual!Type == byte))
28         const ubyte TypeID = 0x01;
29     else static if (is(Unqual!Type == ubyte))
30         const ubyte TypeID = 0x02;
31     else static if (is(Unqual!Type == short))
32         const ubyte TypeID = 0x03;
33     else static if (is(Unqual!Type == ushort))
34         const ubyte TypeID = 0x04;
35     else static if (is(Unqual!Type == int))
36         const ubyte TypeID = 0x05;
37     else static if (is(Unqual!Type == uint))
38         const ubyte TypeID = 0x06;
39     else static if (is(Unqual!Type == long))
40         const ubyte TypeID = 0x07;
41     else static if (is(Unqual!Type == ulong))
42         const ubyte TypeID = 0x08;
43     else static if (is(Unqual!Type == float))
44         const ubyte TypeID = 0x20;
45     else static if (is(Unqual!Type == double))
46         const ubyte TypeID = 0x21;
47     else static if (is(Unqual!Type == real))
48         const ubyte TypeID = 0x22;
49     else static if (is(Unqual!Type == bool))
50         const ubyte TypeID = 0x30;
51     else static if (is(Unqual!Type == char))
52         const ubyte TypeID = 0x40;
53     else static if (is(Unqual!Type == string))
54         const ubyte TypeID = 0x41;
55     else
56         static assert(0, "Data types that are not supported: " ~ typeid(Type));
57 }
58 
59 package:
60 
61 class Packet
62 {
63     static ubyte[] build(ushort magic, CryptType crypt, string key, Nullable!RSAKeyInfo rsaKey, string name, string method, Variant[] params)
64     {
65         assert(name.length <= 255, "Paramter name cannot be greater than 255 characters.");
66         assert(method.length <= 255, "Paramter method cannot be greater than 255 characters.");
67         assert(params.length > 0, "Parameter params must be provided.");
68 
69         ubyte[] tlv;
70         BufferBuilder bb = new BufferBuilder(&tlv);
71 
72         foreach (v; params)
73         {
74             if (v.type == typeid(byte))
75             {
76                 bb.put!byte(v.get!byte, true, false, 0);
77             }
78             else if (v.type == typeid(ubyte))
79             {
80                 bb.put!ubyte(v.get!ubyte, true, false, 0);
81             }
82             else if (v.type == typeid(short))
83             {
84                 bb.put!short(v.get!short, true, false, 0);
85             }
86             else if (v.type == typeid(ushort))
87             {
88                 bb.put!ushort(v.get!ushort, true, false, 0);
89             }
90             else if (v.type == typeid(int))
91             {
92                 bb.put!int(v.get!int, true, false, 0);
93             }
94             else if (v.type == typeid(uint))
95             {
96                 bb.put!uint(v.get!uint, true, false, 0);
97             }
98             else if (v.type == typeid(long))
99             {
100                 bb.put!long(v.get!long, true, false, 0);
101             }
102             else if (v.type == typeid(ulong))
103             {
104                 bb.put!ulong(v.get!ulong, true, false, 0);
105             }
106             else if (v.type == typeid(float))
107             {
108                 bb.put!float(v.get!float, true, false, 0);
109             }
110             else if (v.type == typeid(double))
111             {
112                 bb.put!double(v.get!double, true, false, 0);
113             }
114             else if (v.type == typeid(real))
115             {
116                 bb.put!real(v.get!real, true, false, 0);
117             }
118             else if (v.type == typeid(bool))
119             {
120                 bb.put!bool(v.get!bool, true, false, 0);
121             }
122             else if (v.type == typeid(char))
123             {
124                 bb.put!char(v.get!char, true, false, 0);
125             }
126             else if (v.type == typeid(string))
127             {
128                 bb.put!string(v.get!string, true, true, 4);
129             }
130             else
131             {
132                 assert(0, "Data types id that are not supported: " ~ v.type.toString);
133             }
134         }
135 
136         final switch (crypt)
137         {
138         case CryptType.NONE:
139             break;
140         case CryptType.XTEA:
141             tlv = Xtea.encrypt(tlv, key);
142             break;
143         case CryptType.AES:
144             tlv = AESUtils.encrypt!AES128(tlv, key);
145             break;
146         case CryptType.RSA:
147             tlv = RSA.encrypt(rsaKey, tlv);
148             break;
149         case CryptType.RSA_XTEA_MIXIN:
150             tlv = RSA.encrypt(rsaKey, tlv, true);
151         }
152 
153         ubyte[] buffer;
154         bb = new BufferBuilder(&buffer);
155         bb.put!ushort(magic, false, false, 0);
156         bb.put!int(0, false, false, 0);		// length, seize a seat.
157         bb.put!string(name, false, true, 2);
158         bb.put!string(method, false, true, 2);
159         buffer ~= tlv;
160         buffer.write!int(cast(int)(buffer.length - 2 - 4 + 2), 2);
161         buffer ~= strToByte_hex(MD5(buffer)[0 .. 4]);
162 
163         return buffer;
164     }
165 
166     static size_t parseInfo(ubyte[] buffer, out string name, out string method)
167     {
168         enforce(buffer != null && buffer.length >= 10, "Incorrect buffer length.");
169 
170         ushort len1 = buffer.peek!ushort(6);
171         if (len1 > 0)
172         {
173             name = cast(string) buffer[8 .. 8 + len1];
174         }
175 
176         ushort len2 = buffer.peek!ushort(8 + len1);
177         if (len2 > 0)
178         {
179             method = cast(string) buffer[10 + len1 .. 10 + len1 + len2];
180         }
181 
182         return 10 + len1 + len2;
183     }
184 
185     static Variant[] parse(ubyte[] buffer, ushort magic, CryptType crypt, string key, Nullable!RSAKeyInfo rsaKey, out string name, out string method)
186     {
187         enforce(buffer != null && buffer.length >= 10, "Incorrect buffer length.");
188 
189         ushort t_magic;
190         int t_len;
191         t_magic = buffer.peek!ushort(0);
192         t_len = buffer.peek!int(2);
193 
194         if ((t_magic != magic) || (t_len > cast(int)buffer.length - 6))
195         {
196             return null;
197         }
198 
199         buffer = buffer[0 .. t_len + 6];
200         if (strToByte_hex(MD5(buffer[0 .. $ - 2])[0 .. 4]) != buffer[$ - 2 .. $])
201         {
202             return null;
203         }
204 
205         size_t tlv_pos = parseInfo(buffer, name, method);
206         buffer = buffer[tlv_pos .. $ - 2];
207 
208         final switch (crypt)
209         {
210         case CryptType.NONE:
211             break;
212         case CryptType.XTEA:
213             buffer = Xtea.decrypt(buffer, key);
214             break;
215         case CryptType.AES:
216             buffer = AESUtils.decrypt!AES128(buffer, key);
217             break;
218         case CryptType.RSA:
219             buffer = RSA.decrypt(rsaKey, buffer);
220             break;
221         case CryptType.RSA_XTEA_MIXIN:
222             buffer = RSA.decrypt(rsaKey, buffer, true);
223             break;
224         }
225 
226         ubyte typeId;
227         int pos;
228         Variant[] ret;
229 
230         void get(T)()
231         {
232             ret ~= Variant(buffer.peek!T(pos));
233             pos += T.sizeof;
234         }
235 
236         while (pos < buffer.length)
237         {
238             typeId = buffer[pos];
239             pos++;
240 
241             if (typeId == TypeID!byte)
242             {
243                 get!byte;
244             }
245             else if (typeId == TypeID!ubyte)
246             {
247                 get!ubyte;
248             }
249             else if (typeId == TypeID!short)
250             {
251                 get!short;
252             }
253             else if (typeId == TypeID!ushort)
254             {
255                 get!ushort;
256             }
257             else if (typeId == TypeID!int)
258             {
259                 get!int;
260             }
261             else if (typeId == TypeID!uint)
262             {
263                 get!uint;
264             }
265             else if (typeId == TypeID!long)
266             {
267                 get!long;
268             }
269             else if (typeId == TypeID!ulong)
270             {
271                 get!ulong;
272             }
273             else if (typeId == TypeID!float)
274             {
275                 get!float;
276             }
277             else if (typeId == TypeID!double)
278             {
279                 get!double;
280             }
281             else if (typeId == TypeID!real)
282             {
283                 //get!real;
284                 ret ~= Variant(ubyteToReal(buffer[pos .. pos + real.sizeof]));
285                 pos += real.sizeof;
286             }
287             else if (typeId == TypeID!bool)
288             {
289                 get!bool;
290             }
291             else if (typeId == TypeID!char)
292             {
293                 get!char;
294             }
295             else if (typeId == TypeID!string)
296             {
297                 int temp = buffer.peek!int(pos);
298                 pos += 4;
299                 ret ~= Variant(cast(string) buffer[pos .. pos + temp]);
300                 pos += temp;
301             }
302             else
303             {
304                 assert(0, "Data types id that are not supported: " ~ typeId.to!string);
305             }
306         }
307 
308         return ret;
309     }
310 }
311 
312 class BufferBuilder
313 {
314     public ubyte[]* buffer;
315 
316     this(ubyte[]* buffer)
317     {
318         this.buffer = buffer;
319     }
320 
321     size_t put(T)(T value, bool isWriteTypeInfo, bool isWriteLengthInfo, int lengthBytes)
322     {
323         assert(lengthBytes == 0 || lengthBytes == 2 || lengthBytes == 4);
324 
325         ubyte[] buf_data;
326         size_t len;
327 
328         if (isWriteTypeInfo)
329         {
330             *buffer ~= TypeID!T;
331         }
332 
333         static if (is(Unqual!T == string))
334         {
335             buf_data = cast(ubyte[])value;
336             len = buf_data.length;
337         }
338         else static if (is(Unqual!T == real))
339         {
340             buf_data = realToUByte(value);
341             len = real.sizeof;
342         }
343         else
344         {
345             buf_data = new ubyte[T.sizeof];
346             buf_data.write!T(value, 0);
347             len = T.sizeof;
348         }
349 
350         if (isWriteLengthInfo && lengthBytes > 0)
351         {
352             ubyte[] buf_len = new ubyte[lengthBytes];
353             if (lengthBytes == 2)
354                 buf_len.write!ushort(cast(ushort)len, 0);
355             else
356                 buf_len.write!int(cast(int)len, 0);
357 
358             *buffer ~= buf_len;
359         }
360 
361         *buffer ~= buf_data;
362 
363         return len;
364     }
365 }