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