1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Chris Schneider <cschneid@relog.ch> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include "php.h"
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #ifdef PHP_WIN32
28 #define O_RDONLY _O_RDONLY
29 #include "win32/param.h"
30 #else
31 #include <sys/param.h>
32 #endif
33 #include "ext/standard/head.h"
34 #include "php_string.h"
35 #include "pack.h"
36 #if HAVE_PWD_H
37 #ifdef PHP_WIN32
38 #include "win32/pwd.h"
39 #else
40 #include <pwd.h>
41 #endif
42 #endif
43 #include "fsock.h"
44 #if HAVE_NETINET_IN_H
45 #include <netinet/in.h>
46 #endif
47
48 #define INC_OUTPUTPOS(a,b) \
49 if ((a) < 0 || ((INT_MAX - outputpos)/((int)b)) < (a)) { \
50 efree(formatcodes); \
51 efree(formatargs); \
52 php_error_docref(NULL, E_WARNING, "Type %c: integer overflow in format string", code); \
53 RETURN_FALSE; \
54 } \
55 outputpos += (a)*(b);
56
57 /* Whether machine is little endian */
58 char machine_little_endian;
59
60 /* Mapping of byte from char (8bit) to long for machine endian */
61 static int byte_map[1];
62
63 /* Mappings of bytes from int (machine dependent) to int for machine endian */
64 static int int_map[sizeof(int)];
65
66 /* Mappings of bytes from shorts (16bit) for all endian environments */
67 static int machine_endian_short_map[2];
68 static int big_endian_short_map[2];
69 static int little_endian_short_map[2];
70
71 /* Mappings of bytes from longs (32bit) for all endian environments */
72 static int machine_endian_long_map[4];
73 static int big_endian_long_map[4];
74 static int little_endian_long_map[4];
75
76 #if SIZEOF_ZEND_LONG > 4
77 /* Mappings of bytes from quads (64bit) for all endian environments */
78 static int machine_endian_longlong_map[8];
79 static int big_endian_longlong_map[8];
80 static int little_endian_longlong_map[8];
81 #endif
82
83 /* {{{ php_pack
84 */
php_pack(zval * val,size_t size,int * map,char * output)85 static void php_pack(zval *val, size_t size, int *map, char *output)
86 {
87 size_t i;
88 char *v;
89
90 convert_to_long_ex(val);
91 v = (char *) &Z_LVAL_P(val);
92
93 for (i = 0; i < size; i++) {
94 *output++ = v[map[i]];
95 }
96 }
97 /* }}} */
98
99 /* {{{ php_pack_reverse_int32
100 */
php_pack_reverse_int32(uint32_t arg)101 static inline uint32_t php_pack_reverse_int32(uint32_t arg)
102 {
103 uint32_t result;
104 result = ((arg & 0xFF) << 24) | ((arg & 0xFF00) << 8) | ((arg >> 8) & 0xFF00) | ((arg >> 24) & 0xFF);
105
106 return result;
107 }
108 /* }}} */
109
110 /* {{{ php_pack
111 */
php_pack_reverse_int64(uint64_t arg)112 static inline uint64_t php_pack_reverse_int64(uint64_t arg)
113 {
114 union Swap64 {
115 uint64_t i;
116 uint32_t ul[2];
117 } tmp, result;
118 tmp.i = arg;
119 result.ul[0] = php_pack_reverse_int32(tmp.ul[1]);
120 result.ul[1] = php_pack_reverse_int32(tmp.ul[0]);
121
122 return result.i;
123 }
124 /* }}} */
125
126 /* {{{ php_pack_copy_float
127 */
php_pack_copy_float(int is_little_endian,void * dst,float f)128 static void php_pack_copy_float(int is_little_endian, void * dst, float f)
129 {
130 union Copy32 {
131 float f;
132 uint32_t i;
133 } m;
134 m.f = f;
135
136 #ifdef WORDS_BIGENDIAN
137 if (is_little_endian) {
138 m.i = php_pack_reverse_int32(m.i);
139 }
140 #else /* WORDS_BIGENDIAN */
141 if (!is_little_endian) {
142 m.i = php_pack_reverse_int32(m.i);
143 }
144 #endif /* WORDS_BIGENDIAN */
145
146 memcpy(dst, &m.f, sizeof(float));
147 }
148 /* }}} */
149
150 /* {{{ php_pack_copy_double
151 */
php_pack_copy_double(int is_little_endian,void * dst,double d)152 static void php_pack_copy_double(int is_little_endian, void * dst, double d)
153 {
154 union Copy64 {
155 double d;
156 uint64_t i;
157 } m;
158 m.d = d;
159
160 #ifdef WORDS_BIGENDIAN
161 if (is_little_endian) {
162 m.i = php_pack_reverse_int64(m.i);
163 }
164 #else /* WORDS_BIGENDIAN */
165 if (!is_little_endian) {
166 m.i = php_pack_reverse_int64(m.i);
167 }
168 #endif /* WORDS_BIGENDIAN */
169
170 memcpy(dst, &m.d, sizeof(double));
171 }
172 /* }}} */
173
174 /* {{{ php_pack_parse_float
175 */
php_pack_parse_float(int is_little_endian,void * src)176 static float php_pack_parse_float(int is_little_endian, void * src)
177 {
178 union Copy32 {
179 float f;
180 uint32_t i;
181 } m;
182 memcpy(&m.i, src, sizeof(float));
183
184 #ifdef WORDS_BIGENDIAN
185 if (is_little_endian) {
186 m.i = php_pack_reverse_int32(m.i);
187 }
188 #else /* WORDS_BIGENDIAN */
189 if (!is_little_endian) {
190 m.i = php_pack_reverse_int32(m.i);
191 }
192 #endif /* WORDS_BIGENDIAN */
193
194 return m.f;
195 }
196 /* }}} */
197
198 /* {{{ php_pack_parse_double
199 */
php_pack_parse_double(int is_little_endian,void * src)200 static double php_pack_parse_double(int is_little_endian, void * src)
201 {
202 union Copy64 {
203 double d;
204 uint64_t i;
205 } m;
206 memcpy(&m.i, src, sizeof(double));
207
208 #ifdef WORDS_BIGENDIAN
209 if (is_little_endian) {
210 m.i = php_pack_reverse_int64(m.i);
211 }
212 #else /* WORDS_BIGENDIAN */
213 if (!is_little_endian) {
214 m.i = php_pack_reverse_int64(m.i);
215 }
216 #endif /* WORDS_BIGENDIAN */
217
218 return m.d;
219 }
220 /* }}} */
221
222 /* pack() idea stolen from Perl (implemented formats behave the same as there except J and P)
223 * Implemented formats are Z, A, a, h, H, c, C, s, S, i, I, l, L, n, N, q, Q, J, P, f, d, x, X, @.
224 * Added g, G for little endian float and big endian float, added e, E for little endian double and big endian double.
225 */
226 /* {{{ proto string pack(string format, mixed arg1 [, mixed arg2 [, mixed ...]])
227 Takes one or more arguments and packs them into a binary string according to the format argument */
PHP_FUNCTION(pack)228 PHP_FUNCTION(pack)
229 {
230 zval *argv = NULL;
231 int num_args = 0;
232 size_t i;
233 int currentarg;
234 char *format;
235 size_t formatlen;
236 char *formatcodes;
237 int *formatargs;
238 size_t formatcount = 0;
239 int outputpos = 0, outputsize = 0;
240 zend_string *output;
241
242 ZEND_PARSE_PARAMETERS_START(1, -1)
243 Z_PARAM_STRING(format, formatlen)
244 Z_PARAM_VARIADIC('*', argv, num_args)
245 ZEND_PARSE_PARAMETERS_END();
246
247 /* We have a maximum of <formatlen> format codes to deal with */
248 formatcodes = safe_emalloc(formatlen, sizeof(*formatcodes), 0);
249 formatargs = safe_emalloc(formatlen, sizeof(*formatargs), 0);
250 currentarg = 0;
251
252 /* Preprocess format into formatcodes and formatargs */
253 for (i = 0; i < formatlen; formatcount++) {
254 char code = format[i++];
255 int arg = 1;
256
257 /* Handle format arguments if any */
258 if (i < formatlen) {
259 char c = format[i];
260
261 if (c == '*') {
262 arg = -1;
263 i++;
264 }
265 else if (c >= '0' && c <= '9') {
266 arg = atoi(&format[i]);
267
268 while (format[i] >= '0' && format[i] <= '9' && i < formatlen) {
269 i++;
270 }
271 }
272 }
273
274 /* Handle special arg '*' for all codes and check argv overflows */
275 switch ((int) code) {
276 /* Never uses any args */
277 case 'x':
278 case 'X':
279 case '@':
280 if (arg < 0) {
281 php_error_docref(NULL, E_WARNING, "Type %c: '*' ignored", code);
282 arg = 1;
283 }
284 break;
285
286 /* Always uses one arg */
287 case 'a':
288 case 'A':
289 case 'Z':
290 case 'h':
291 case 'H':
292 if (currentarg >= num_args) {
293 efree(formatcodes);
294 efree(formatargs);
295 php_error_docref(NULL, E_WARNING, "Type %c: not enough arguments", code);
296 RETURN_FALSE;
297 }
298
299 if (arg < 0) {
300 if (!try_convert_to_string(&argv[currentarg])) {
301 efree(formatcodes);
302 efree(formatargs);
303 return;
304 }
305
306 arg = Z_STRLEN(argv[currentarg]);
307 if (code == 'Z') {
308 /* add one because Z is always NUL-terminated:
309 * pack("Z*", "aa") === "aa\0"
310 * pack("Z2", "aa") === "a\0" */
311 arg++;
312 }
313 }
314
315 currentarg++;
316 break;
317
318 /* Use as many args as specified */
319 case 'q':
320 case 'Q':
321 case 'J':
322 case 'P':
323 #if SIZEOF_ZEND_LONG < 8
324 efree(formatcodes);
325 efree(formatargs);
326 php_error_docref(NULL, E_WARNING, "64-bit format codes are not available for 32-bit versions of PHP");
327 RETURN_FALSE;
328 #endif
329 case 'c':
330 case 'C':
331 case 's':
332 case 'S':
333 case 'i':
334 case 'I':
335 case 'l':
336 case 'L':
337 case 'n':
338 case 'N':
339 case 'v':
340 case 'V':
341 case 'f': /* float */
342 case 'g': /* little endian float */
343 case 'G': /* big endian float */
344 case 'd': /* double */
345 case 'e': /* little endian double */
346 case 'E': /* big endian double */
347 if (arg < 0) {
348 arg = num_args - currentarg;
349 }
350 if (currentarg > INT_MAX - arg) {
351 goto too_few_args;
352 }
353 currentarg += arg;
354
355 if (currentarg > num_args) {
356 too_few_args:
357 efree(formatcodes);
358 efree(formatargs);
359 php_error_docref(NULL, E_WARNING, "Type %c: too few arguments", code);
360 RETURN_FALSE;
361 }
362 break;
363
364 default:
365 efree(formatcodes);
366 efree(formatargs);
367 php_error_docref(NULL, E_WARNING, "Type %c: unknown format code", code);
368 RETURN_FALSE;
369 }
370
371 formatcodes[formatcount] = code;
372 formatargs[formatcount] = arg;
373 }
374
375 if (currentarg < num_args) {
376 php_error_docref(NULL, E_WARNING, "%d arguments unused", (num_args - currentarg));
377 }
378
379 /* Calculate output length and upper bound while processing*/
380 for (i = 0; i < formatcount; i++) {
381 int code = (int) formatcodes[i];
382 int arg = formatargs[i];
383
384 switch ((int) code) {
385 case 'h':
386 case 'H':
387 INC_OUTPUTPOS((arg + (arg % 2)) / 2,1) /* 4 bit per arg */
388 break;
389
390 case 'a':
391 case 'A':
392 case 'Z':
393 case 'c':
394 case 'C':
395 case 'x':
396 INC_OUTPUTPOS(arg,1) /* 8 bit per arg */
397 break;
398
399 case 's':
400 case 'S':
401 case 'n':
402 case 'v':
403 INC_OUTPUTPOS(arg,2) /* 16 bit per arg */
404 break;
405
406 case 'i':
407 case 'I':
408 INC_OUTPUTPOS(arg,sizeof(int))
409 break;
410
411 case 'l':
412 case 'L':
413 case 'N':
414 case 'V':
415 INC_OUTPUTPOS(arg,4) /* 32 bit per arg */
416 break;
417
418 #if SIZEOF_ZEND_LONG > 4
419 case 'q':
420 case 'Q':
421 case 'J':
422 case 'P':
423 INC_OUTPUTPOS(arg,8) /* 32 bit per arg */
424 break;
425 #endif
426
427 case 'f': /* float */
428 case 'g': /* little endian float */
429 case 'G': /* big endian float */
430 INC_OUTPUTPOS(arg,sizeof(float))
431 break;
432
433 case 'd': /* double */
434 case 'e': /* little endian double */
435 case 'E': /* big endian double */
436 INC_OUTPUTPOS(arg,sizeof(double))
437 break;
438
439 case 'X':
440 outputpos -= arg;
441
442 if (outputpos < 0) {
443 php_error_docref(NULL, E_WARNING, "Type %c: outside of string", code);
444 outputpos = 0;
445 }
446 break;
447
448 case '@':
449 outputpos = arg;
450 break;
451 }
452
453 if (outputsize < outputpos) {
454 outputsize = outputpos;
455 }
456 }
457
458 output = zend_string_alloc(outputsize, 0);
459 outputpos = 0;
460 currentarg = 0;
461
462 /* Do actual packing */
463 for (i = 0; i < formatcount; i++) {
464 int code = (int) formatcodes[i];
465 int arg = formatargs[i];
466
467 switch ((int) code) {
468 case 'a':
469 case 'A':
470 case 'Z': {
471 size_t arg_cp = (code != 'Z') ? arg : MAX(0, arg - 1);
472 zend_string *tmp_str;
473 zend_string *str = zval_get_tmp_string(&argv[currentarg++], &tmp_str);
474
475 memset(&ZSTR_VAL(output)[outputpos], (code == 'a' || code == 'Z') ? '\0' : ' ', arg);
476 memcpy(&ZSTR_VAL(output)[outputpos], ZSTR_VAL(str),
477 (ZSTR_LEN(str) < arg_cp) ? ZSTR_LEN(str) : arg_cp);
478
479 outputpos += arg;
480 zend_tmp_string_release(tmp_str);
481 break;
482 }
483
484 case 'h':
485 case 'H': {
486 int nibbleshift = (code == 'h') ? 0 : 4;
487 int first = 1;
488 zend_string *tmp_str;
489 zend_string *str = zval_get_tmp_string(&argv[currentarg++], &tmp_str);
490 char *v = ZSTR_VAL(str);
491
492 outputpos--;
493 if ((size_t)arg > ZSTR_LEN(str)) {
494 php_error_docref(NULL, E_WARNING, "Type %c: not enough characters in string", code);
495 arg = ZSTR_LEN(str);
496 }
497
498 while (arg-- > 0) {
499 char n = *v++;
500
501 if (n >= '0' && n <= '9') {
502 n -= '0';
503 } else if (n >= 'A' && n <= 'F') {
504 n -= ('A' - 10);
505 } else if (n >= 'a' && n <= 'f') {
506 n -= ('a' - 10);
507 } else {
508 php_error_docref(NULL, E_WARNING, "Type %c: illegal hex digit %c", code, n);
509 n = 0;
510 }
511
512 if (first--) {
513 ZSTR_VAL(output)[++outputpos] = 0;
514 } else {
515 first = 1;
516 }
517
518 ZSTR_VAL(output)[outputpos] |= (n << nibbleshift);
519 nibbleshift = (nibbleshift + 4) & 7;
520 }
521
522 outputpos++;
523 zend_tmp_string_release(tmp_str);
524 break;
525 }
526
527 case 'c':
528 case 'C':
529 while (arg-- > 0) {
530 php_pack(&argv[currentarg++], 1, byte_map, &ZSTR_VAL(output)[outputpos]);
531 outputpos++;
532 }
533 break;
534
535 case 's':
536 case 'S':
537 case 'n':
538 case 'v': {
539 int *map = machine_endian_short_map;
540
541 if (code == 'n') {
542 map = big_endian_short_map;
543 } else if (code == 'v') {
544 map = little_endian_short_map;
545 }
546
547 while (arg-- > 0) {
548 php_pack(&argv[currentarg++], 2, map, &ZSTR_VAL(output)[outputpos]);
549 outputpos += 2;
550 }
551 break;
552 }
553
554 case 'i':
555 case 'I':
556 while (arg-- > 0) {
557 php_pack(&argv[currentarg++], sizeof(int), int_map, &ZSTR_VAL(output)[outputpos]);
558 outputpos += sizeof(int);
559 }
560 break;
561
562 case 'l':
563 case 'L':
564 case 'N':
565 case 'V': {
566 int *map = machine_endian_long_map;
567
568 if (code == 'N') {
569 map = big_endian_long_map;
570 } else if (code == 'V') {
571 map = little_endian_long_map;
572 }
573
574 while (arg-- > 0) {
575 php_pack(&argv[currentarg++], 4, map, &ZSTR_VAL(output)[outputpos]);
576 outputpos += 4;
577 }
578 break;
579 }
580
581 #if SIZEOF_ZEND_LONG > 4
582 case 'q':
583 case 'Q':
584 case 'J':
585 case 'P': {
586 int *map = machine_endian_longlong_map;
587
588 if (code == 'J') {
589 map = big_endian_longlong_map;
590 } else if (code == 'P') {
591 map = little_endian_longlong_map;
592 }
593
594 while (arg-- > 0) {
595 php_pack(&argv[currentarg++], 8, map, &ZSTR_VAL(output)[outputpos]);
596 outputpos += 8;
597 }
598 break;
599 }
600 #endif
601
602 case 'f': {
603 while (arg-- > 0) {
604 float v = (float) zval_get_double(&argv[currentarg++]);
605 memcpy(&ZSTR_VAL(output)[outputpos], &v, sizeof(v));
606 outputpos += sizeof(v);
607 }
608 break;
609 }
610
611 case 'g': {
612 /* pack little endian float */
613 while (arg-- > 0) {
614 float v = (float) zval_get_double(&argv[currentarg++]);
615 php_pack_copy_float(1, &ZSTR_VAL(output)[outputpos], v);
616 outputpos += sizeof(v);
617 }
618
619 break;
620 }
621 case 'G': {
622 /* pack big endian float */
623 while (arg-- > 0) {
624 float v = (float) zval_get_double(&argv[currentarg++]);
625 php_pack_copy_float(0, &ZSTR_VAL(output)[outputpos], v);
626 outputpos += sizeof(v);
627 }
628 break;
629 }
630
631 case 'd': {
632 while (arg-- > 0) {
633 double v = (double) zval_get_double(&argv[currentarg++]);
634 memcpy(&ZSTR_VAL(output)[outputpos], &v, sizeof(v));
635 outputpos += sizeof(v);
636 }
637 break;
638 }
639
640 case 'e': {
641 /* pack little endian double */
642 while (arg-- > 0) {
643 double v = (double) zval_get_double(&argv[currentarg++]);
644 php_pack_copy_double(1, &ZSTR_VAL(output)[outputpos], v);
645 outputpos += sizeof(v);
646 }
647 break;
648 }
649
650 case 'E': {
651 /* pack big endian double */
652 while (arg-- > 0) {
653 double v = (double) zval_get_double(&argv[currentarg++]);
654 php_pack_copy_double(0, &ZSTR_VAL(output)[outputpos], v);
655 outputpos += sizeof(v);
656 }
657 break;
658 }
659
660 case 'x':
661 memset(&ZSTR_VAL(output)[outputpos], '\0', arg);
662 outputpos += arg;
663 break;
664
665 case 'X':
666 outputpos -= arg;
667
668 if (outputpos < 0) {
669 outputpos = 0;
670 }
671 break;
672
673 case '@':
674 if (arg > outputpos) {
675 memset(&ZSTR_VAL(output)[outputpos], '\0', arg - outputpos);
676 }
677 outputpos = arg;
678 break;
679 }
680 }
681
682 efree(formatcodes);
683 efree(formatargs);
684 ZSTR_VAL(output)[outputpos] = '\0';
685 ZSTR_LEN(output) = outputpos;
686 RETURN_NEW_STR(output);
687 }
688 /* }}} */
689
690 /* {{{ php_unpack
691 */
php_unpack(char * data,size_t size,int issigned,int * map)692 static zend_long php_unpack(char *data, size_t size, int issigned, int *map)
693 {
694 zend_long result;
695 char *cresult = (char *) &result;
696 size_t i;
697
698 result = issigned ? -1 : 0;
699
700 for (i = 0; i < size; i++) {
701 cresult[map[i]] = *data++;
702 }
703
704 return result;
705 }
706 /* }}} */
707
708 /* unpack() is based on Perl's unpack(), but is modified a bit from there.
709 * Rather than depending on error-prone ordered lists or syntactically
710 * unpleasant pass-by-reference, we return an object with named parameters
711 * (like *_fetch_object()). Syntax is "f[repeat]name/...", where "f" is the
712 * formatter char (like pack()), "[repeat]" is the optional repeater argument,
713 * and "name" is the name of the variable to use.
714 * Example: "c2chars/nints" will return an object with fields
715 * chars1, chars2, and ints.
716 * Numeric pack types will return numbers, a and A will return strings,
717 * f and d will return doubles.
718 * Implemented formats are Z, A, a, h, H, c, C, s, S, i, I, l, L, n, N, q, Q, J, P, f, d, x, X, @.
719 * Added g, G for little endian float and big endian float, added e, E for little endian double and big endian double.
720 */
721 /* {{{ proto array unpack(string format, string input)
722 Unpack binary string into named array elements according to format argument */
PHP_FUNCTION(unpack)723 PHP_FUNCTION(unpack)
724 {
725 char *format, *input;
726 zend_string *formatarg, *inputarg;
727 zend_long formatlen, inputpos, inputlen;
728 int i;
729 zend_long offset = 0;
730
731 ZEND_PARSE_PARAMETERS_START(2, 3)
732 Z_PARAM_STR(formatarg)
733 Z_PARAM_STR(inputarg)
734 Z_PARAM_OPTIONAL
735 Z_PARAM_LONG(offset)
736 ZEND_PARSE_PARAMETERS_END();
737
738 format = ZSTR_VAL(formatarg);
739 formatlen = ZSTR_LEN(formatarg);
740 input = ZSTR_VAL(inputarg);
741 inputlen = ZSTR_LEN(inputarg);
742 inputpos = 0;
743
744
745 if (offset < 0 || offset > inputlen) {
746 php_error_docref(NULL, E_WARNING, "Offset " ZEND_LONG_FMT " is out of input range" , offset);
747 RETURN_FALSE;
748 }
749 input += offset;
750 inputlen -= offset;
751
752 array_init(return_value);
753
754 while (formatlen-- > 0) {
755 char type = *(format++);
756 char c;
757 int arg = 1, argb;
758 char *name;
759 int namelen;
760 int size=0;
761
762 /* Handle format arguments if any */
763 if (formatlen > 0) {
764 c = *format;
765
766 if (c >= '0' && c <= '9') {
767 arg = atoi(format);
768
769 while (formatlen > 0 && *format >= '0' && *format <= '9') {
770 format++;
771 formatlen--;
772 }
773 } else if (c == '*') {
774 arg = -1;
775 format++;
776 formatlen--;
777 }
778 }
779
780 /* Get of new value in array */
781 name = format;
782 argb = arg;
783
784 while (formatlen > 0 && *format != '/') {
785 formatlen--;
786 format++;
787 }
788
789 namelen = format - name;
790
791 if (namelen > 200)
792 namelen = 200;
793
794 switch ((int) type) {
795 /* Never use any input */
796 case 'X':
797 size = -1;
798 if (arg < 0) {
799 php_error_docref(NULL, E_WARNING, "Type %c: '*' ignored", type);
800 arg = 1;
801 }
802 break;
803
804 case '@':
805 size = 0;
806 break;
807
808 case 'a':
809 case 'A':
810 case 'Z':
811 size = arg;
812 arg = 1;
813 break;
814
815 case 'h':
816 case 'H':
817 size = (arg > 0) ? (arg + (arg % 2)) / 2 : arg;
818 arg = 1;
819 break;
820
821 /* Use 1 byte of input */
822 case 'c':
823 case 'C':
824 case 'x':
825 size = 1;
826 break;
827
828 /* Use 2 bytes of input */
829 case 's':
830 case 'S':
831 case 'n':
832 case 'v':
833 size = 2;
834 break;
835
836 /* Use sizeof(int) bytes of input */
837 case 'i':
838 case 'I':
839 size = sizeof(int);
840 break;
841
842 /* Use 4 bytes of input */
843 case 'l':
844 case 'L':
845 case 'N':
846 case 'V':
847 size = 4;
848 break;
849
850 /* Use 8 bytes of input */
851 case 'q':
852 case 'Q':
853 case 'J':
854 case 'P':
855 #if SIZEOF_ZEND_LONG > 4
856 size = 8;
857 break;
858 #else
859 php_error_docref(NULL, E_WARNING, "64-bit format codes are not available for 32-bit versions of PHP");
860 zend_array_destroy(Z_ARR_P(return_value));
861 RETURN_FALSE;
862 #endif
863
864 /* Use sizeof(float) bytes of input */
865 case 'f':
866 case 'g':
867 case 'G':
868 size = sizeof(float);
869 break;
870
871 /* Use sizeof(double) bytes of input */
872 case 'd':
873 case 'e':
874 case 'E':
875 size = sizeof(double);
876 break;
877
878 default:
879 php_error_docref(NULL, E_WARNING, "Invalid format type %c", type);
880 zend_array_destroy(Z_ARR_P(return_value));
881 RETURN_FALSE;
882 break;
883 }
884
885 if (size != 0 && size != -1 && size < 0) {
886 php_error_docref(NULL, E_WARNING, "Type %c: integer overflow", type);
887 zend_array_destroy(Z_ARR_P(return_value));
888 RETURN_FALSE;
889 }
890
891 /* Do actual unpacking */
892 for (i = 0; i != arg; i++ ) {
893 /* Space for name + number, safe as namelen is ensured <= 200 */
894 char n[256];
895
896 if (arg != 1 || namelen == 0) {
897 /* Need to add element number to name */
898 snprintf(n, sizeof(n), "%.*s%d", namelen, name, i + 1);
899 } else {
900 /* Truncate name to next format code or end of string */
901 snprintf(n, sizeof(n), "%.*s", namelen, name);
902 }
903
904 if (size != 0 && size != -1 && INT_MAX - size + 1 < inputpos) {
905 php_error_docref(NULL, E_WARNING, "Type %c: integer overflow", type);
906 zend_array_destroy(Z_ARR_P(return_value));
907 RETURN_FALSE;
908 }
909
910 if ((inputpos + size) <= inputlen) {
911 switch ((int) type) {
912 case 'a': {
913 /* a will not strip any trailing whitespace or null padding */
914 zend_long len = inputlen - inputpos; /* Remaining string */
915
916 /* If size was given take minimum of len and size */
917 if ((size >= 0) && (len > size)) {
918 len = size;
919 }
920
921 size = len;
922
923 add_assoc_stringl(return_value, n, &input[inputpos], len);
924 break;
925 }
926 case 'A': {
927 /* A will strip any trailing whitespace */
928 char padn = '\0'; char pads = ' '; char padt = '\t'; char padc = '\r'; char padl = '\n';
929 zend_long len = inputlen - inputpos; /* Remaining string */
930
931 /* If size was given take minimum of len and size */
932 if ((size >= 0) && (len > size)) {
933 len = size;
934 }
935
936 size = len;
937
938 /* Remove trailing white space and nulls chars from unpacked data */
939 while (--len >= 0) {
940 if (input[inputpos + len] != padn
941 && input[inputpos + len] != pads
942 && input[inputpos + len] != padt
943 && input[inputpos + len] != padc
944 && input[inputpos + len] != padl
945 )
946 break;
947 }
948
949 add_assoc_stringl(return_value, n, &input[inputpos], len + 1);
950 break;
951 }
952 /* New option added for Z to remain in-line with the Perl implementation */
953 case 'Z': {
954 /* Z will strip everything after the first null character */
955 char pad = '\0';
956 zend_long s,
957 len = inputlen - inputpos; /* Remaining string */
958
959 /* If size was given take minimum of len and size */
960 if ((size >= 0) && (len > size)) {
961 len = size;
962 }
963
964 size = len;
965
966 /* Remove everything after the first null */
967 for (s=0 ; s < len ; s++) {
968 if (input[inputpos + s] == pad)
969 break;
970 }
971 len = s;
972
973 add_assoc_stringl(return_value, n, &input[inputpos], len);
974 break;
975 }
976
977
978 case 'h':
979 case 'H': {
980 zend_long len = (inputlen - inputpos) * 2; /* Remaining */
981 int nibbleshift = (type == 'h') ? 0 : 4;
982 int first = 1;
983 zend_string *buf;
984 zend_long ipos, opos;
985
986 /* If size was given take minimum of len and size */
987 if (size >= 0 && len > (size * 2)) {
988 len = size * 2;
989 }
990
991 if (len > 0 && argb > 0) {
992 len -= argb % 2;
993 }
994
995 buf = zend_string_alloc(len, 0);
996
997 for (ipos = opos = 0; opos < len; opos++) {
998 char cc = (input[inputpos + ipos] >> nibbleshift) & 0xf;
999
1000 if (cc < 10) {
1001 cc += '0';
1002 } else {
1003 cc += 'a' - 10;
1004 }
1005
1006 ZSTR_VAL(buf)[opos] = cc;
1007 nibbleshift = (nibbleshift + 4) & 7;
1008
1009 if (first-- == 0) {
1010 ipos++;
1011 first = 1;
1012 }
1013 }
1014
1015 ZSTR_VAL(buf)[len] = '\0';
1016 add_assoc_str(return_value, n, buf);
1017 break;
1018 }
1019
1020 case 'c':
1021 case 'C': {
1022 int issigned = (type == 'c') ? (input[inputpos] & 0x80) : 0;
1023 zend_long v = php_unpack(&input[inputpos], 1, issigned, byte_map);
1024 add_assoc_long(return_value, n, v);
1025 break;
1026 }
1027
1028 case 's':
1029 case 'S':
1030 case 'n':
1031 case 'v': {
1032 zend_long v;
1033 int issigned = 0;
1034 int *map = machine_endian_short_map;
1035
1036 if (type == 's') {
1037 issigned = input[inputpos + (machine_little_endian ? 1 : 0)] & 0x80;
1038 } else if (type == 'n') {
1039 map = big_endian_short_map;
1040 } else if (type == 'v') {
1041 map = little_endian_short_map;
1042 }
1043
1044 v = php_unpack(&input[inputpos], 2, issigned, map);
1045 add_assoc_long(return_value, n, v);
1046 break;
1047 }
1048
1049 case 'i':
1050 case 'I': {
1051 zend_long v;
1052 int issigned = 0;
1053
1054 if (type == 'i') {
1055 issigned = input[inputpos + (machine_little_endian ? (sizeof(int) - 1) : 0)] & 0x80;
1056 }
1057
1058 v = php_unpack(&input[inputpos], sizeof(int), issigned, int_map);
1059 add_assoc_long(return_value, n, v);
1060 break;
1061 }
1062
1063 case 'l':
1064 case 'L':
1065 case 'N':
1066 case 'V': {
1067 int issigned = 0;
1068 int *map = machine_endian_long_map;
1069 zend_long v = 0;
1070
1071 if (type == 'l' || type == 'L') {
1072 issigned = input[inputpos + (machine_little_endian ? 3 : 0)] & 0x80;
1073 } else if (type == 'N') {
1074 issigned = input[inputpos] & 0x80;
1075 map = big_endian_long_map;
1076 } else if (type == 'V') {
1077 issigned = input[inputpos + 3] & 0x80;
1078 map = little_endian_long_map;
1079 }
1080
1081 if (SIZEOF_ZEND_LONG > 4 && issigned) {
1082 v = ~INT_MAX;
1083 }
1084
1085 v |= php_unpack(&input[inputpos], 4, issigned, map);
1086 if (SIZEOF_ZEND_LONG > 4) {
1087 if (type == 'l') {
1088 v = (signed int) v;
1089 } else {
1090 v = (unsigned int) v;
1091 }
1092 }
1093 add_assoc_long(return_value, n, v);
1094 break;
1095 }
1096
1097 #if SIZEOF_ZEND_LONG > 4
1098 case 'q':
1099 case 'Q':
1100 case 'J':
1101 case 'P': {
1102 int issigned = 0;
1103 int *map = machine_endian_longlong_map;
1104 zend_long v = 0;
1105
1106 if (type == 'q' || type == 'Q') {
1107 issigned = input[inputpos + (machine_little_endian ? 7 : 0)] & 0x80;
1108 } else if (type == 'J') {
1109 issigned = input[inputpos] & 0x80;
1110 map = big_endian_longlong_map;
1111 } else if (type == 'P') {
1112 issigned = input[inputpos + 7] & 0x80;
1113 map = little_endian_longlong_map;
1114 }
1115
1116 v = php_unpack(&input[inputpos], 8, issigned, map);
1117
1118 if (type == 'q') {
1119 v = (zend_long) v;
1120 } else {
1121 v = (zend_ulong) v;
1122 }
1123
1124 add_assoc_long(return_value, n, v);
1125 break;
1126 }
1127 #endif
1128
1129 case 'f': /* float */
1130 case 'g': /* little endian float*/
1131 case 'G': /* big endian float*/
1132 {
1133 float v;
1134
1135 if (type == 'g') {
1136 v = php_pack_parse_float(1, &input[inputpos]);
1137 } else if (type == 'G') {
1138 v = php_pack_parse_float(0, &input[inputpos]);
1139 } else {
1140 memcpy(&v, &input[inputpos], sizeof(float));
1141 }
1142
1143 add_assoc_double(return_value, n, (double)v);
1144 break;
1145 }
1146
1147
1148 case 'd': /* double */
1149 case 'e': /* little endian float */
1150 case 'E': /* big endian float */
1151 {
1152 double v;
1153 if (type == 'e') {
1154 v = php_pack_parse_double(1, &input[inputpos]);
1155 } else if (type == 'E') {
1156 v = php_pack_parse_double(0, &input[inputpos]);
1157 } else {
1158 memcpy(&v, &input[inputpos], sizeof(double));
1159 }
1160 add_assoc_double(return_value, n, v);
1161 break;
1162 }
1163
1164 case 'x':
1165 /* Do nothing with input, just skip it */
1166 break;
1167
1168 case 'X':
1169 if (inputpos < size) {
1170 inputpos = -size;
1171 i = arg - 1; /* Break out of for loop */
1172
1173 if (arg >= 0) {
1174 php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
1175 }
1176 }
1177 break;
1178
1179 case '@':
1180 if (arg <= inputlen) {
1181 inputpos = arg;
1182 } else {
1183 php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
1184 }
1185
1186 i = arg - 1; /* Done, break out of for loop */
1187 break;
1188 }
1189
1190 inputpos += size;
1191 if (inputpos < 0) {
1192 if (size != -1) { /* only print warning if not working with * */
1193 php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
1194 }
1195 inputpos = 0;
1196 }
1197 } else if (arg < 0) {
1198 /* Reached end of input for '*' repeater */
1199 break;
1200 } else {
1201 php_error_docref(NULL, E_WARNING, "Type %c: not enough input, need %d, have " ZEND_LONG_FMT, type, size, inputlen - inputpos);
1202 zend_array_destroy(Z_ARR_P(return_value));
1203 RETURN_FALSE;
1204 }
1205 }
1206
1207 if (formatlen > 0) {
1208 formatlen--; /* Skip '/' separator, does no harm if inputlen == 0 */
1209 format++;
1210 }
1211 }
1212 }
1213 /* }}} */
1214
1215 /* {{{ PHP_MINIT_FUNCTION
1216 */
PHP_MINIT_FUNCTION(pack)1217 PHP_MINIT_FUNCTION(pack)
1218 {
1219 int machine_endian_check = 1;
1220 int i;
1221
1222 machine_little_endian = ((char *)&machine_endian_check)[0];
1223
1224 if (machine_little_endian) {
1225 /* Where to get lo to hi bytes from */
1226 byte_map[0] = 0;
1227
1228 for (i = 0; i < (int)sizeof(int); i++) {
1229 int_map[i] = i;
1230 }
1231
1232 machine_endian_short_map[0] = 0;
1233 machine_endian_short_map[1] = 1;
1234 big_endian_short_map[0] = 1;
1235 big_endian_short_map[1] = 0;
1236 little_endian_short_map[0] = 0;
1237 little_endian_short_map[1] = 1;
1238
1239 machine_endian_long_map[0] = 0;
1240 machine_endian_long_map[1] = 1;
1241 machine_endian_long_map[2] = 2;
1242 machine_endian_long_map[3] = 3;
1243 big_endian_long_map[0] = 3;
1244 big_endian_long_map[1] = 2;
1245 big_endian_long_map[2] = 1;
1246 big_endian_long_map[3] = 0;
1247 little_endian_long_map[0] = 0;
1248 little_endian_long_map[1] = 1;
1249 little_endian_long_map[2] = 2;
1250 little_endian_long_map[3] = 3;
1251
1252 #if SIZEOF_ZEND_LONG > 4
1253 machine_endian_longlong_map[0] = 0;
1254 machine_endian_longlong_map[1] = 1;
1255 machine_endian_longlong_map[2] = 2;
1256 machine_endian_longlong_map[3] = 3;
1257 machine_endian_longlong_map[4] = 4;
1258 machine_endian_longlong_map[5] = 5;
1259 machine_endian_longlong_map[6] = 6;
1260 machine_endian_longlong_map[7] = 7;
1261 big_endian_longlong_map[0] = 7;
1262 big_endian_longlong_map[1] = 6;
1263 big_endian_longlong_map[2] = 5;
1264 big_endian_longlong_map[3] = 4;
1265 big_endian_longlong_map[4] = 3;
1266 big_endian_longlong_map[5] = 2;
1267 big_endian_longlong_map[6] = 1;
1268 big_endian_longlong_map[7] = 0;
1269 little_endian_longlong_map[0] = 0;
1270 little_endian_longlong_map[1] = 1;
1271 little_endian_longlong_map[2] = 2;
1272 little_endian_longlong_map[3] = 3;
1273 little_endian_longlong_map[4] = 4;
1274 little_endian_longlong_map[5] = 5;
1275 little_endian_longlong_map[6] = 6;
1276 little_endian_longlong_map[7] = 7;
1277 #endif
1278 }
1279 else {
1280 zval val;
1281 int size = sizeof(Z_LVAL(val));
1282 Z_LVAL(val)=0; /*silence a warning*/
1283
1284 /* Where to get hi to lo bytes from */
1285 byte_map[0] = size - 1;
1286
1287 for (i = 0; i < (int)sizeof(int); i++) {
1288 int_map[i] = size - (sizeof(int) - i);
1289 }
1290
1291 machine_endian_short_map[0] = size - 2;
1292 machine_endian_short_map[1] = size - 1;
1293 big_endian_short_map[0] = size - 2;
1294 big_endian_short_map[1] = size - 1;
1295 little_endian_short_map[0] = size - 1;
1296 little_endian_short_map[1] = size - 2;
1297
1298 machine_endian_long_map[0] = size - 4;
1299 machine_endian_long_map[1] = size - 3;
1300 machine_endian_long_map[2] = size - 2;
1301 machine_endian_long_map[3] = size - 1;
1302 big_endian_long_map[0] = size - 4;
1303 big_endian_long_map[1] = size - 3;
1304 big_endian_long_map[2] = size - 2;
1305 big_endian_long_map[3] = size - 1;
1306 little_endian_long_map[0] = size - 1;
1307 little_endian_long_map[1] = size - 2;
1308 little_endian_long_map[2] = size - 3;
1309 little_endian_long_map[3] = size - 4;
1310
1311 #if SIZEOF_ZEND_LONG > 4
1312 machine_endian_longlong_map[0] = size - 8;
1313 machine_endian_longlong_map[1] = size - 7;
1314 machine_endian_longlong_map[2] = size - 6;
1315 machine_endian_longlong_map[3] = size - 5;
1316 machine_endian_longlong_map[4] = size - 4;
1317 machine_endian_longlong_map[5] = size - 3;
1318 machine_endian_longlong_map[6] = size - 2;
1319 machine_endian_longlong_map[7] = size - 1;
1320 big_endian_longlong_map[0] = size - 8;
1321 big_endian_longlong_map[1] = size - 7;
1322 big_endian_longlong_map[2] = size - 6;
1323 big_endian_longlong_map[3] = size - 5;
1324 big_endian_longlong_map[4] = size - 4;
1325 big_endian_longlong_map[5] = size - 3;
1326 big_endian_longlong_map[6] = size - 2;
1327 big_endian_longlong_map[7] = size - 1;
1328 little_endian_longlong_map[0] = size - 1;
1329 little_endian_longlong_map[1] = size - 2;
1330 little_endian_longlong_map[2] = size - 3;
1331 little_endian_longlong_map[3] = size - 4;
1332 little_endian_longlong_map[4] = size - 5;
1333 little_endian_longlong_map[5] = size - 6;
1334 little_endian_longlong_map[6] = size - 7;
1335 little_endian_longlong_map[7] = size - 8;
1336 #endif
1337 }
1338
1339 return SUCCESS;
1340 }
1341 /* }}} */
1342