1: | <?php |
2: | |
3: | |
4: | |
5: | |
6: | |
7: | |
8: | |
9: | |
10: | |
11: | |
12: | |
13: | |
14: | |
15: | |
16: | |
17: | |
18: | |
19: | namespace GameQ\Protocols; |
20: | |
21: | use GameQ\Exception\Protocol as Exception; |
22: | use GameQ\Helpers\Str; |
23: | use GameQ\Protocol; |
24: | use GameQ\Result; |
25: | |
26: | |
27: | |
28: | |
29: | |
30: | |
31: | |
32: | |
33: | |
34: | |
35: | |
36: | |
37: | |
38: | class Ventrilo extends Protocol |
39: | { |
40: | |
41: | |
42: | |
43: | |
44: | |
45: | |
46: | protected $packets = [ |
47: | self::PACKET_ALL => |
48: | "V\xc8\xf4\xf9`\xa2\x1e\xa5M\xfb\x03\xccQN\xa1\x10\x95\xaf\xb2g\x17g\x812\xfbW\xfd\x8e\xd2\x22r\x034z\xbb\x98", |
49: | ]; |
50: | |
51: | |
52: | |
53: | |
54: | |
55: | |
56: | protected $protocol = 'ventrilo'; |
57: | |
58: | |
59: | |
60: | |
61: | |
62: | |
63: | protected $name = 'ventrilo'; |
64: | |
65: | |
66: | |
67: | |
68: | |
69: | |
70: | protected $name_long = "Ventrilo"; |
71: | |
72: | |
73: | |
74: | |
75: | |
76: | |
77: | protected $join_link = "ventrilo://%s:%d/"; |
78: | |
79: | |
80: | |
81: | |
82: | |
83: | |
84: | protected $normalize = [ |
85: | |
86: | 'general' => [ |
87: | 'dedicated' => 'dedicated', |
88: | 'password' => 'auth', |
89: | 'hostname' => 'name', |
90: | 'numplayers' => 'clientcount', |
91: | 'maxplayers' => 'maxclients', |
92: | ], |
93: | |
94: | 'player' => [ |
95: | 'team' => 'cid', |
96: | 'name' => 'name', |
97: | ], |
98: | |
99: | 'team' => [ |
100: | 'id' => 'cid', |
101: | 'name' => 'name', |
102: | ], |
103: | ]; |
104: | |
105: | |
106: | |
107: | |
108: | |
109: | |
110: | private $head_encrypt_table = [ |
111: | 0x80, |
112: | 0xe5, |
113: | 0x0e, |
114: | 0x38, |
115: | 0xba, |
116: | 0x63, |
117: | 0x4c, |
118: | 0x99, |
119: | 0x88, |
120: | 0x63, |
121: | 0x4c, |
122: | 0xd6, |
123: | 0x54, |
124: | 0xb8, |
125: | 0x65, |
126: | 0x7e, |
127: | 0xbf, |
128: | 0x8a, |
129: | 0xf0, |
130: | 0x17, |
131: | 0x8a, |
132: | 0xaa, |
133: | 0x4d, |
134: | 0x0f, |
135: | 0xb7, |
136: | 0x23, |
137: | 0x27, |
138: | 0xf6, |
139: | 0xeb, |
140: | 0x12, |
141: | 0xf8, |
142: | 0xea, |
143: | 0x17, |
144: | 0xb7, |
145: | 0xcf, |
146: | 0x52, |
147: | 0x57, |
148: | 0xcb, |
149: | 0x51, |
150: | 0xcf, |
151: | 0x1b, |
152: | 0x14, |
153: | 0xfd, |
154: | 0x6f, |
155: | 0x84, |
156: | 0x38, |
157: | 0xb5, |
158: | 0x24, |
159: | 0x11, |
160: | 0xcf, |
161: | 0x7a, |
162: | 0x75, |
163: | 0x7a, |
164: | 0xbb, |
165: | 0x78, |
166: | 0x74, |
167: | 0xdc, |
168: | 0xbc, |
169: | 0x42, |
170: | 0xf0, |
171: | 0x17, |
172: | 0x3f, |
173: | 0x5e, |
174: | 0xeb, |
175: | 0x74, |
176: | 0x77, |
177: | 0x04, |
178: | 0x4e, |
179: | 0x8c, |
180: | 0xaf, |
181: | 0x23, |
182: | 0xdc, |
183: | 0x65, |
184: | 0xdf, |
185: | 0xa5, |
186: | 0x65, |
187: | 0xdd, |
188: | 0x7d, |
189: | 0xf4, |
190: | 0x3c, |
191: | 0x4c, |
192: | 0x95, |
193: | 0xbd, |
194: | 0xeb, |
195: | 0x65, |
196: | 0x1c, |
197: | 0xf4, |
198: | 0x24, |
199: | 0x5d, |
200: | 0x82, |
201: | 0x18, |
202: | 0xfb, |
203: | 0x50, |
204: | 0x86, |
205: | 0xb8, |
206: | 0x53, |
207: | 0xe0, |
208: | 0x4e, |
209: | 0x36, |
210: | 0x96, |
211: | 0x1f, |
212: | 0xb7, |
213: | 0xcb, |
214: | 0xaa, |
215: | 0xaf, |
216: | 0xea, |
217: | 0xcb, |
218: | 0x20, |
219: | 0x27, |
220: | 0x30, |
221: | 0x2a, |
222: | 0xae, |
223: | 0xb9, |
224: | 0x07, |
225: | 0x40, |
226: | 0xdf, |
227: | 0x12, |
228: | 0x75, |
229: | 0xc9, |
230: | 0x09, |
231: | 0x82, |
232: | 0x9c, |
233: | 0x30, |
234: | 0x80, |
235: | 0x5d, |
236: | 0x8f, |
237: | 0x0d, |
238: | 0x09, |
239: | 0xa1, |
240: | 0x64, |
241: | 0xec, |
242: | 0x91, |
243: | 0xd8, |
244: | 0x8a, |
245: | 0x50, |
246: | 0x1f, |
247: | 0x40, |
248: | 0x5d, |
249: | 0xf7, |
250: | 0x08, |
251: | 0x2a, |
252: | 0xf8, |
253: | 0x60, |
254: | 0x62, |
255: | 0xa0, |
256: | 0x4a, |
257: | 0x8b, |
258: | 0xba, |
259: | 0x4a, |
260: | 0x6d, |
261: | 0x00, |
262: | 0x0a, |
263: | 0x93, |
264: | 0x32, |
265: | 0x12, |
266: | 0xe5, |
267: | 0x07, |
268: | 0x01, |
269: | 0x65, |
270: | 0xf5, |
271: | 0xff, |
272: | 0xe0, |
273: | 0xae, |
274: | 0xa7, |
275: | 0x81, |
276: | 0xd1, |
277: | 0xba, |
278: | 0x25, |
279: | 0x62, |
280: | 0x61, |
281: | 0xb2, |
282: | 0x85, |
283: | 0xad, |
284: | 0x7e, |
285: | 0x9d, |
286: | 0x3f, |
287: | 0x49, |
288: | 0x89, |
289: | 0x26, |
290: | 0xe5, |
291: | 0xd5, |
292: | 0xac, |
293: | 0x9f, |
294: | 0x0e, |
295: | 0xd7, |
296: | 0x6e, |
297: | 0x47, |
298: | 0x94, |
299: | 0x16, |
300: | 0x84, |
301: | 0xc8, |
302: | 0xff, |
303: | 0x44, |
304: | 0xea, |
305: | 0x04, |
306: | 0x40, |
307: | 0xe0, |
308: | 0x33, |
309: | 0x11, |
310: | 0xa3, |
311: | 0x5b, |
312: | 0x1e, |
313: | 0x82, |
314: | 0xff, |
315: | 0x7a, |
316: | 0x69, |
317: | 0xe9, |
318: | 0x2f, |
319: | 0xfb, |
320: | 0xea, |
321: | 0x9a, |
322: | 0xc6, |
323: | 0x7b, |
324: | 0xdb, |
325: | 0xb1, |
326: | 0xff, |
327: | 0x97, |
328: | 0x76, |
329: | 0x56, |
330: | 0xf3, |
331: | 0x52, |
332: | 0xc2, |
333: | 0x3f, |
334: | 0x0f, |
335: | 0xb6, |
336: | 0xac, |
337: | 0x77, |
338: | 0xc4, |
339: | 0xbf, |
340: | 0x59, |
341: | 0x5e, |
342: | 0x80, |
343: | 0x74, |
344: | 0xbb, |
345: | 0xf2, |
346: | 0xde, |
347: | 0x57, |
348: | 0x62, |
349: | 0x4c, |
350: | 0x1a, |
351: | 0xff, |
352: | 0x95, |
353: | 0x6d, |
354: | 0xc7, |
355: | 0x04, |
356: | 0xa2, |
357: | 0x3b, |
358: | 0xc4, |
359: | 0x1b, |
360: | 0x72, |
361: | 0xc7, |
362: | 0x6c, |
363: | 0x82, |
364: | 0x60, |
365: | 0xd1, |
366: | 0x0d, |
367: | ]; |
368: | |
369: | |
370: | |
371: | |
372: | |
373: | |
374: | private $data_encrypt_table = [ |
375: | 0x82, |
376: | 0x8b, |
377: | 0x7f, |
378: | 0x68, |
379: | 0x90, |
380: | 0xe0, |
381: | 0x44, |
382: | 0x09, |
383: | 0x19, |
384: | 0x3b, |
385: | 0x8e, |
386: | 0x5f, |
387: | 0xc2, |
388: | 0x82, |
389: | 0x38, |
390: | 0x23, |
391: | 0x6d, |
392: | 0xdb, |
393: | 0x62, |
394: | 0x49, |
395: | 0x52, |
396: | 0x6e, |
397: | 0x21, |
398: | 0xdf, |
399: | 0x51, |
400: | 0x6c, |
401: | 0x76, |
402: | 0x37, |
403: | 0x86, |
404: | 0x50, |
405: | 0x7d, |
406: | 0x48, |
407: | 0x1f, |
408: | 0x65, |
409: | 0xe7, |
410: | 0x52, |
411: | 0x6a, |
412: | 0x88, |
413: | 0xaa, |
414: | 0xc1, |
415: | 0x32, |
416: | 0x2f, |
417: | 0xf7, |
418: | 0x54, |
419: | 0x4c, |
420: | 0xaa, |
421: | 0x6d, |
422: | 0x7e, |
423: | 0x6d, |
424: | 0xa9, |
425: | 0x8c, |
426: | 0x0d, |
427: | 0x3f, |
428: | 0xff, |
429: | 0x6c, |
430: | 0x09, |
431: | 0xb3, |
432: | 0xa5, |
433: | 0xaf, |
434: | 0xdf, |
435: | 0x98, |
436: | 0x02, |
437: | 0xb4, |
438: | 0xbe, |
439: | 0x6d, |
440: | 0x69, |
441: | 0x0d, |
442: | 0x42, |
443: | 0x73, |
444: | 0xe4, |
445: | 0x34, |
446: | 0x50, |
447: | 0x07, |
448: | 0x30, |
449: | 0x79, |
450: | 0x41, |
451: | 0x2f, |
452: | 0x08, |
453: | 0x3f, |
454: | 0x42, |
455: | 0x73, |
456: | 0xa7, |
457: | 0x68, |
458: | 0xfa, |
459: | 0xee, |
460: | 0x88, |
461: | 0x0e, |
462: | 0x6e, |
463: | 0xa4, |
464: | 0x70, |
465: | 0x74, |
466: | 0x22, |
467: | 0x16, |
468: | 0xae, |
469: | 0x3c, |
470: | 0x81, |
471: | 0x14, |
472: | 0xa1, |
473: | 0xda, |
474: | 0x7f, |
475: | 0xd3, |
476: | 0x7c, |
477: | 0x48, |
478: | 0x7d, |
479: | 0x3f, |
480: | 0x46, |
481: | 0xfb, |
482: | 0x6d, |
483: | 0x92, |
484: | 0x25, |
485: | 0x17, |
486: | 0x36, |
487: | 0x26, |
488: | 0xdb, |
489: | 0xdf, |
490: | 0x5a, |
491: | 0x87, |
492: | 0x91, |
493: | 0x6f, |
494: | 0xd6, |
495: | 0xcd, |
496: | 0xd4, |
497: | 0xad, |
498: | 0x4a, |
499: | 0x29, |
500: | 0xdd, |
501: | 0x7d, |
502: | 0x59, |
503: | 0xbd, |
504: | 0x15, |
505: | 0x34, |
506: | 0x53, |
507: | 0xb1, |
508: | 0xd8, |
509: | 0x50, |
510: | 0x11, |
511: | 0x83, |
512: | 0x79, |
513: | 0x66, |
514: | 0x21, |
515: | 0x9e, |
516: | 0x87, |
517: | 0x5b, |
518: | 0x24, |
519: | 0x2f, |
520: | 0x4f, |
521: | 0xd7, |
522: | 0x73, |
523: | 0x34, |
524: | 0xa2, |
525: | 0xf7, |
526: | 0x09, |
527: | 0xd5, |
528: | 0xd9, |
529: | 0x42, |
530: | 0x9d, |
531: | 0xf8, |
532: | 0x15, |
533: | 0xdf, |
534: | 0x0e, |
535: | 0x10, |
536: | 0xcc, |
537: | 0x05, |
538: | 0x04, |
539: | 0x35, |
540: | 0x81, |
541: | 0xb2, |
542: | 0xd5, |
543: | 0x7a, |
544: | 0xd2, |
545: | 0xa0, |
546: | 0xa5, |
547: | 0x7b, |
548: | 0xb8, |
549: | 0x75, |
550: | 0xd2, |
551: | 0x35, |
552: | 0x0b, |
553: | 0x39, |
554: | 0x8f, |
555: | 0x1b, |
556: | 0x44, |
557: | 0x0e, |
558: | 0xce, |
559: | 0x66, |
560: | 0x87, |
561: | 0x1b, |
562: | 0x64, |
563: | 0xac, |
564: | 0xe1, |
565: | 0xca, |
566: | 0x67, |
567: | 0xb4, |
568: | 0xce, |
569: | 0x33, |
570: | 0xdb, |
571: | 0x89, |
572: | 0xfe, |
573: | 0xd8, |
574: | 0x8e, |
575: | 0xcd, |
576: | 0x58, |
577: | 0x92, |
578: | 0x41, |
579: | 0x50, |
580: | 0x40, |
581: | 0xcb, |
582: | 0x08, |
583: | 0xe1, |
584: | 0x15, |
585: | 0xee, |
586: | 0xf4, |
587: | 0x64, |
588: | 0xfe, |
589: | 0x1c, |
590: | 0xee, |
591: | 0x25, |
592: | 0xe7, |
593: | 0x21, |
594: | 0xe6, |
595: | 0x6c, |
596: | 0xc6, |
597: | 0xa6, |
598: | 0x2e, |
599: | 0x52, |
600: | 0x23, |
601: | 0xa7, |
602: | 0x20, |
603: | 0xd2, |
604: | 0xd7, |
605: | 0x28, |
606: | 0x07, |
607: | 0x23, |
608: | 0x14, |
609: | 0x24, |
610: | 0x3d, |
611: | 0x45, |
612: | 0xa5, |
613: | 0xc7, |
614: | 0x90, |
615: | 0xdb, |
616: | 0x77, |
617: | 0xdd, |
618: | 0xea, |
619: | 0x38, |
620: | 0x59, |
621: | 0x89, |
622: | 0x32, |
623: | 0xbc, |
624: | 0x00, |
625: | 0x3a, |
626: | 0x6d, |
627: | 0x61, |
628: | 0x4e, |
629: | 0xdb, |
630: | 0x29, |
631: | ]; |
632: | |
633: | |
634: | |
635: | |
636: | |
637: | |
638: | |
639: | public function processResponse() |
640: | { |
641: | |
642: | $decrypted = $this->decryptPackets($this->packets_response); |
643: | |
644: | |
645: | $decrypted = preg_replace_callback( |
646: | '|%([0-9A-F]{2})|', |
647: | function ($matches) { |
648: | |
649: | return pack('H*', $matches[1]); |
650: | }, |
651: | $decrypted |
652: | ); |
653: | |
654: | |
655: | $lines = explode("\n", $decrypted); |
656: | |
657: | |
658: | $result = new Result(); |
659: | |
660: | |
661: | $result->add('dedicated', 1); |
662: | |
663: | |
664: | $channelFields = 5; |
665: | $playerFields = 7; |
666: | |
667: | |
668: | foreach ($lines as $line) { |
669: | |
670: | $line = trim($line); |
671: | |
672: | |
673: | if (strlen($line) == 0) { |
674: | continue; |
675: | } |
676: | |
677: | |
678: | |
679: | |
680: | |
681: | |
682: | |
683: | |
684: | |
685: | |
686: | |
687: | |
688: | |
689: | |
690: | |
691: | |
692: | if (($colon_pos = strpos($line, ":")) !== false && $colon_pos > 0) { |
693: | |
694: | list($key, $value) = explode(':', $line, 2); |
695: | |
696: | |
697: | $key = strtolower($key); |
698: | |
699: | |
700: | $value = trim($value); |
701: | |
702: | |
703: | switch ($key) { |
704: | case 'client': |
705: | $this->processPlayer($value, $playerFields, $result); |
706: | break; |
707: | |
708: | case 'channel': |
709: | $this->processChannel($value, $channelFields, $result); |
710: | break; |
711: | |
712: | |
713: | case 'channelfields': |
714: | $channelFields = count(explode(',', $value)); |
715: | break; |
716: | |
717: | |
718: | case 'clientfields': |
719: | $playerFields = count(explode(',', $value)); |
720: | break; |
721: | |
722: | |
723: | default: |
724: | $result->add($key, Str::isoToUtf8($value)); |
725: | break; |
726: | } |
727: | } |
728: | } |
729: | |
730: | unset($decrypted, $line, $lines, $colon_pos, $key, $value); |
731: | |
732: | return $result->fetch(); |
733: | } |
734: | |
735: | |
736: | |
737: | |
738: | |
739: | |
740: | |
741: | |
742: | |
743: | |
744: | |
745: | |
746: | protected function decryptPackets(array $packets = []) |
747: | { |
748: | |
749: | $decrypted = []; |
750: | |
751: | foreach ($packets as $packet) { |
752: | |
753: | $header = substr($packet, 0, 20); |
754: | |
755: | $header_items = []; |
756: | |
757: | $header_key = unpack("n1", $header); |
758: | |
759: | $key = array_shift($header_key); |
760: | |
761: | $chars = unpack("C*", substr($header, 2)); |
762: | |
763: | $a1 = $key & 0xFF; |
764: | $a2 = $key >> 8; |
765: | |
766: | if ($a1 == 0) { |
767: | throw new Exception(__METHOD__ . ": Header key is invalid"); |
768: | } |
769: | |
770: | $table = $this->head_encrypt_table; |
771: | |
772: | $characterCount = count($chars); |
773: | |
774: | $key = 0; |
775: | for ($index = 1; $index <= $characterCount; $index++) { |
776: | $chars[$index] -= ($table[$a2] + (($index - 1) % 5)) & 0xFF; |
777: | $a2 = ($a2 + $a1) & 0xFF; |
778: | if (($index % 2) == 0) { |
779: | $short_array = unpack("n1", pack("C2", $chars[$index - 1], $chars[$index])); |
780: | $header_items[$key] = $short_array[1]; |
781: | ++$key; |
782: | } |
783: | } |
784: | |
785: | $header_items = array_combine([ |
786: | 'zero', |
787: | 'cmd', |
788: | 'id', |
789: | 'totlen', |
790: | 'len', |
791: | 'totpck', |
792: | 'pck', |
793: | 'datakey', |
794: | 'crc', |
795: | ], $header_items); |
796: | |
797: | |
798: | if ($header_items['totpck'] != count($packets)) { |
799: | throw new Exception(__METHOD__ . ": Too few packets received"); |
800: | } |
801: | |
802: | |
803: | $table = $this->data_encrypt_table; |
804: | $a1 = $header_items['datakey'] & 0xFF; |
805: | $a2 = $header_items['datakey'] >> 8; |
806: | |
807: | if ($a1 == 0) { |
808: | throw new Exception(__METHOD__ . ": Data key is invalid"); |
809: | } |
810: | |
811: | $chars = unpack("C*", substr($packet, 20)); |
812: | $data = ""; |
813: | $characterCount = count($chars); |
814: | |
815: | for ($index = 1; $index <= $characterCount; $index++) { |
816: | $chars[$index] -= ($table[$a2] + (($index - 1) % 72)) & 0xFF; |
817: | $a2 = ($a2 + $a1) & 0xFF; |
818: | $data .= chr($chars[$index]); |
819: | } |
820: | |
821: | $decrypted[$header_items['pck']] = $data; |
822: | } |
823: | |
824: | |
825: | return implode('', $decrypted); |
826: | } |
827: | |
828: | |
829: | |
830: | |
831: | |
832: | |
833: | |
834: | |
835: | |
836: | protected function processChannel($data, $fieldCount, Result &$result) |
837: | { |
838: | |
839: | $items = explode(",", $data, $fieldCount); |
840: | |
841: | |
842: | foreach ($items as $item) { |
843: | |
844: | list($key, $value) = explode("=", $item, 2); |
845: | |
846: | $result->addTeam(strtolower($key), Str::isoToUtf8($value)); |
847: | } |
848: | } |
849: | |
850: | |
851: | |
852: | |
853: | |
854: | |
855: | |
856: | |
857: | |
858: | protected function processPlayer($data, $fieldCount, Result &$result) |
859: | { |
860: | |
861: | $items = explode(",", $data, $fieldCount); |
862: | |
863: | |
864: | foreach ($items as $item) { |
865: | |
866: | list($key, $value) = explode("=", $item, 2); |
867: | |
868: | $result->addPlayer(strtolower($key), Str::isoToUtf8($value)); |
869: | } |
870: | } |
871: | } |
872: | |