1 #ifdef __sun
2 /* to enable 'new' ancillary data layout instead */
3 # define _XPG4_2
4 #endif
5 #include "sockaddr_conv.h"
6 #include "conversions.h"
7 #include "sendrecvmsg.h" /* for ancillary registry */
8 #ifdef PHP_WIN32
9 # include "windows_common.h"
10 #endif
11
12 #include <Zend/zend_llist.h>
13 #include <zend_smart_str.h>
14
15 #ifndef PHP_WIN32
16 # include <sys/types.h>
17 # include <sys/socket.h>
18 # include <arpa/inet.h>
19 # include <netinet/in.h>
20 # include <sys/un.h>
21 # include <sys/ioctl.h>
22 # include <net/if.h>
23 #else
24 # include <stdint.h>
25 #endif
26
27 #include <limits.h>
28 #include <stdarg.h>
29 #include <stddef.h>
30
31 #ifdef PHP_WIN32
32 typedef unsigned short sa_family_t;
33 # define msghdr _WSAMSG
34 /*
35 struct _WSAMSG {
36 LPSOCKADDR name; //void *msg_name
37 INT namelen; //socklen_t msg_namelen
38 LPWSABUF lpBuffers; //struct iovec *msg_iov
39 ULONG dwBufferCount; //size_t msg_iovlen
40 WSABUF Control; //void *msg_control, size_t msg_controllen
41 DWORD dwFlags; //int msg_flags
42 }
43 */
44 # define msg_name name
45 # define msg_namelen namelen
46 # define msg_iov lpBuffers
47 # define msg_iovlen dwBufferCount
48 # define msg_control Control.buf
49 # define msg_controllen Control.len
50 # define msg_flags dwFlags
51 # define iov_base buf
52 # define iov_len len
53
54 # ifdef CMSG_DATA
55 # undef CMSG_DATA
56 # endif
57 # define CMSG_DATA WSA_CMSG_DATA
58 #endif
59
60 #define MAX_USER_BUFF_SIZE ((size_t)(100*1024*1024))
61 #define DEFAULT_BUFF_SIZE 8192
62
63 struct _ser_context {
64 HashTable params; /* stores pointers; has to be first */
65 struct err_s err;
66 zend_llist keys,
67 /* common part to res_context ends here */
68 allocations;
69 php_socket *sock;
70 };
71 struct _res_context {
72 HashTable params; /* stores pointers; has to be first */
73 struct err_s err;
74 zend_llist keys;
75 };
76
77 typedef struct {
78 /* zval info */
79 const char *name;
80 unsigned name_size;
81 int required;
82
83 /* structure info */
84 size_t field_offset; /* 0 to pass full structure, e.g. when more than
85 one field is to be changed; in that case the
86 callbacks need to know the name of the fields */
87
88 /* callbacks */
89 from_zval_write_field *from_zval;
90 to_zval_read_field *to_zval;
91 } field_descriptor;
92
93 #define KEY_FILL_SOCKADDR "fill_sockaddr"
94 #define KEY_RECVMSG_RET "recvmsg_ret"
95 #define KEY_CMSG_LEN "cmsg_len"
96
97 const struct key_value empty_key_value_list[] = {{0}};
98
99 /* PARAMETERS */
param_get_bool(void * ctx,const char * key,int def)100 static int param_get_bool(void *ctx, const char *key, int def)
101 {
102 int *elem;
103 if ((elem = zend_hash_str_find_ptr(ctx, key, strlen(key))) != NULL) {
104 return *elem;
105 } else {
106 return def;
107 }
108 }
109
110 /* MEMORY */
accounted_emalloc(size_t alloc_size,ser_context * ctx)111 static inline void *accounted_emalloc(size_t alloc_size, ser_context *ctx)
112 {
113 void *ret = emalloc(alloc_size);
114 zend_llist_add_element(&ctx->allocations, &ret);
115 return ret;
116 }
accounted_ecalloc(size_t nmemb,size_t alloc_size,ser_context * ctx)117 static inline void *accounted_ecalloc(size_t nmemb, size_t alloc_size, ser_context *ctx)
118 {
119 void *ret = ecalloc(nmemb, alloc_size);
120 zend_llist_add_element(&ctx->allocations, &ret);
121 return ret;
122 }
accounted_safe_ecalloc(size_t nmemb,size_t alloc_size,size_t offset,ser_context * ctx)123 static inline void *accounted_safe_ecalloc(size_t nmemb, size_t alloc_size, size_t offset, ser_context *ctx)
124 {
125 void *ret = safe_emalloc(nmemb, alloc_size, offset);
126 memset(ret, '\0', nmemb * alloc_size + offset);
127 zend_llist_add_element(&ctx->allocations, &ret);
128 return ret;
129 }
130
131 /* ERRORS */
do_from_to_zval_err(struct err_s * err,zend_llist * keys,const char * what_conv,const char * fmt,va_list ap)132 static void do_from_to_zval_err(struct err_s *err,
133 zend_llist *keys,
134 const char *what_conv,
135 const char *fmt,
136 va_list ap)
137 {
138 smart_str path = {0};
139 const char **node;
140 char *user_msg;
141 int user_msg_size;
142 zend_llist_position pos;
143
144 if (err->has_error) {
145 return;
146 }
147
148 for (node = zend_llist_get_first_ex(keys, &pos);
149 node != NULL;
150 node = zend_llist_get_next_ex(keys, &pos)) {
151 smart_str_appends(&path, *node);
152 smart_str_appends(&path, " > ");
153 }
154
155 if (path.s && ZSTR_LEN(path.s) > 3) {
156 ZSTR_LEN(path.s) -= 3;
157 }
158 smart_str_0(&path);
159
160 user_msg_size = vspprintf(&user_msg, 0, fmt, ap);
161
162 err->has_error = 1;
163 err->level = E_WARNING;
164 spprintf(&err->msg, 0, "error converting %s data (path: %s): %.*s",
165 what_conv,
166 path.s && *ZSTR_VAL(path.s) != '\0' ? ZSTR_VAL(path.s) : "unavailable",
167 user_msg_size, user_msg);
168 err->should_free = 1;
169
170 efree(user_msg);
171 smart_str_free(&path);
172 }
173 ZEND_ATTRIBUTE_FORMAT(printf, 2 ,3)
do_from_zval_err(ser_context * ctx,const char * fmt,...)174 static void do_from_zval_err(ser_context *ctx, const char *fmt, ...)
175 {
176 va_list ap;
177
178 va_start(ap, fmt);
179 do_from_to_zval_err(&ctx->err, &ctx->keys, "user", fmt, ap);
180 va_end(ap);
181 }
182 ZEND_ATTRIBUTE_FORMAT(printf, 2 ,3)
do_to_zval_err(res_context * ctx,const char * fmt,...)183 static void do_to_zval_err(res_context *ctx, const char *fmt, ...)
184 {
185 va_list ap;
186
187 va_start(ap, fmt);
188 do_from_to_zval_err(&ctx->err, &ctx->keys, "native", fmt, ap);
189 va_end(ap);
190 }
191
err_msg_dispose(struct err_s * err)192 void err_msg_dispose(struct err_s *err)
193 {
194 if (err->msg != NULL) {
195 php_error_docref(NULL, err->level, "%s", err->msg);
196 if (err->should_free) {
197 efree(err->msg);
198 }
199 }
200 }
allocations_dispose(zend_llist ** allocations)201 void allocations_dispose(zend_llist **allocations)
202 {
203 zend_llist_destroy(*allocations);
204 efree(*allocations);
205 *allocations = NULL;
206 }
207
from_array_iterate(const zval * arr,void (* func)(zval * elem,unsigned i,void ** args,ser_context * ctx),void ** args,ser_context * ctx)208 static unsigned from_array_iterate(const zval *arr,
209 void (*func)(zval *elem, unsigned i, void **args, ser_context *ctx),
210 void **args,
211 ser_context *ctx)
212 {
213 unsigned i;
214 zval *elem;
215 char buf[sizeof("element #4294967295")];
216 char *bufp = buf;
217
218 /* Note i starts at 1, not 0! */
219 i = 1;
220 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), elem) {
221 if ((size_t)snprintf(buf, sizeof(buf), "element #%u", i) >= sizeof(buf)) {
222 memcpy(buf, "element", sizeof("element"));
223 }
224 zend_llist_add_element(&ctx->keys, &bufp);
225
226 func(elem, i, args, ctx);
227
228 zend_llist_remove_tail(&ctx->keys);
229 if (ctx->err.has_error) {
230 break;
231 }
232 i++;
233 } ZEND_HASH_FOREACH_END();
234
235 return i -1;
236 }
237
238 /* Generic Aggregated conversions */
from_zval_write_aggregation(const zval * container,char * structure,const field_descriptor * descriptors,ser_context * ctx)239 static void from_zval_write_aggregation(const zval *container,
240 char *structure,
241 const field_descriptor *descriptors,
242 ser_context *ctx)
243 {
244 const field_descriptor *descr;
245 zval *elem;
246
247 if (Z_TYPE_P(container) != IS_ARRAY) {
248 do_from_zval_err(ctx, "%s", "expected an array here");
249 }
250
251 for (descr = descriptors; descr->name != NULL && !ctx->err.has_error; descr++) {
252 if ((elem = zend_hash_str_find(Z_ARRVAL_P(container),
253 descr->name, descr->name_size - 1)) != NULL) {
254
255 if (descr->from_zval == NULL) {
256 do_from_zval_err(ctx, "No information on how to convert value "
257 "of key '%s'", descr->name);
258 break;
259 }
260
261 zend_llist_add_element(&ctx->keys, (void*)&descr->name);
262 descr->from_zval(elem, ((char*)structure) + descr->field_offset, ctx);
263 zend_llist_remove_tail(&ctx->keys);
264
265 } else if (descr->required) {
266 do_from_zval_err(ctx, "The key '%s' is required", descr->name);
267 break;
268 }
269 }
270 }
to_zval_read_aggregation(const char * structure,zval * zarr,const field_descriptor * descriptors,res_context * ctx)271 static void to_zval_read_aggregation(const char *structure,
272 zval *zarr, /* initialized array */
273 const field_descriptor *descriptors,
274 res_context *ctx)
275 {
276 const field_descriptor *descr;
277
278 assert(Z_TYPE_P(zarr) == IS_ARRAY);
279 assert(Z_ARRVAL_P(zarr) != NULL);
280
281 for (descr = descriptors; descr->name != NULL && !ctx->err.has_error; descr++) {
282 zval *new_zv, tmp;
283
284 if (descr->to_zval == NULL) {
285 do_to_zval_err(ctx, "No information on how to convert native "
286 "field into value for key '%s'", descr->name);
287 break;
288 }
289
290 ZVAL_NULL(&tmp);
291 new_zv = zend_symtable_str_update(Z_ARRVAL_P(zarr), descr->name, descr->name_size - 1, &tmp);
292
293 zend_llist_add_element(&ctx->keys, (void*)&descr->name);
294 descr->to_zval(structure + descr->field_offset, new_zv, ctx);
295 zend_llist_remove_tail(&ctx->keys);
296 }
297 }
298
299 /* CONVERSIONS for integers */
from_zval_integer_common(const zval * arr_value,ser_context * ctx)300 static zend_long from_zval_integer_common(const zval *arr_value, ser_context *ctx)
301 {
302 zend_long ret = 0;
303 zval lzval;
304
305 ZVAL_NULL(&lzval);
306 if (Z_TYPE_P(arr_value) != IS_LONG) {
307 ZVAL_COPY(&lzval, (zval *)arr_value);
308 arr_value = &lzval;
309 }
310
311 switch (Z_TYPE_P(arr_value)) {
312 case IS_LONG:
313 long_case:
314 ret = Z_LVAL_P(arr_value);
315 break;
316
317 /* if not long we're operating on lzval */
318 case IS_DOUBLE:
319 double_case:
320 convert_to_long(&lzval);
321 goto long_case;
322
323 case IS_OBJECT:
324 case IS_STRING: {
325 zend_long lval;
326 double dval;
327
328 if (!try_convert_to_string(&lzval)) {
329 ctx->err.has_error = 1;
330 break;
331 }
332
333 switch (is_numeric_string(Z_STRVAL(lzval), Z_STRLEN(lzval), &lval, &dval, 0)) {
334 case IS_DOUBLE:
335 zval_ptr_dtor_str(&lzval);
336 ZVAL_DOUBLE(&lzval, dval);
337 goto double_case;
338
339 case IS_LONG:
340 zval_ptr_dtor_str(&lzval);
341 ZVAL_LONG(&lzval, lval);
342 goto long_case;
343 }
344
345 /* if we get here, we don't have a numeric string */
346 do_from_zval_err(ctx, "expected an integer, but got a non numeric "
347 "string (possibly from a converted object): '%s'", Z_STRVAL_P(arr_value));
348 break;
349 }
350
351 default:
352 do_from_zval_err(ctx, "%s", "expected an integer, either of a PHP "
353 "integer type or of a convertible type");
354 break;
355 }
356
357 zval_ptr_dtor(&lzval);
358
359 return ret;
360 }
from_zval_write_int(const zval * arr_value,char * field,ser_context * ctx)361 void from_zval_write_int(const zval *arr_value, char *field, ser_context *ctx)
362 {
363 zend_long lval;
364 int ival;
365
366 lval = from_zval_integer_common(arr_value, ctx);
367 if (ctx->err.has_error) {
368 return;
369 }
370
371 if (lval > INT_MAX || lval < INT_MIN) {
372 do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
373 "for a native int");
374 return;
375 }
376
377 ival = (int)lval;
378 memcpy(field, &ival, sizeof(ival));
379 }
from_zval_write_uint32(const zval * arr_value,char * field,ser_context * ctx)380 static void from_zval_write_uint32(const zval *arr_value, char *field, ser_context *ctx)
381 {
382 zend_long lval;
383 uint32_t ival;
384
385 lval = from_zval_integer_common(arr_value, ctx);
386 if (ctx->err.has_error) {
387 return;
388 }
389
390 if (sizeof(zend_long) > sizeof(uint32_t) && (lval < 0 || lval > 0xFFFFFFFF)) {
391 do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
392 "for an unsigned 32-bit integer");
393 return;
394 }
395
396 ival = (uint32_t)lval;
397 memcpy(field, &ival, sizeof(ival));
398 }
from_zval_write_net_uint16(const zval * arr_value,char * field,ser_context * ctx)399 static void from_zval_write_net_uint16(const zval *arr_value, char *field, ser_context *ctx)
400 {
401 zend_long lval;
402 uint16_t ival;
403
404 lval = from_zval_integer_common(arr_value, ctx);
405 if (ctx->err.has_error) {
406 return;
407 }
408
409 if (lval < 0 || lval > 0xFFFF) {
410 do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
411 "for an unsigned 16-bit integer");
412 return;
413 }
414
415 ival = htons((uint16_t)lval);
416 memcpy(field, &ival, sizeof(ival));
417 }
from_zval_write_sa_family(const zval * arr_value,char * field,ser_context * ctx)418 static void from_zval_write_sa_family(const zval *arr_value, char *field, ser_context *ctx)
419 {
420 zend_long lval;
421 sa_family_t ival;
422
423 lval = from_zval_integer_common(arr_value, ctx);
424 if (ctx->err.has_error) {
425 return;
426 }
427
428 if (lval < 0 || lval > (sa_family_t)-1) { /* sa_family_t is unsigned */
429 do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
430 "for a sa_family_t value");
431 return;
432 }
433
434 ival = (sa_family_t)lval;
435 memcpy(field, &ival, sizeof(ival));
436 }
437
438 #if defined(SO_PASSCRED) || defined(LOCAL_CREDS_PERSISTENT) || defined(LOCAL_CREDS)
from_zval_write_pid_t(const zval * arr_value,char * field,ser_context * ctx)439 static void from_zval_write_pid_t(const zval *arr_value, char *field, ser_context *ctx)
440 {
441 zend_long lval;
442 pid_t ival;
443
444 lval = from_zval_integer_common(arr_value, ctx);
445 if (ctx->err.has_error) {
446 return;
447 }
448
449 if (lval < 0 || (pid_t)lval != lval) { /* pid_t is signed */
450 do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
451 "for a pid_t value");
452 return;
453 }
454
455 ival = (pid_t)lval;
456 memcpy(field, &ival, sizeof(ival));
457 }
from_zval_write_uid_t(const zval * arr_value,char * field,ser_context * ctx)458 static void from_zval_write_uid_t(const zval *arr_value, char *field, ser_context *ctx)
459 {
460 zend_long lval;
461 uid_t ival;
462
463 lval = from_zval_integer_common(arr_value, ctx);
464 if (ctx->err.has_error) {
465 return;
466 }
467
468 /* uid_t can be signed or unsigned (generally unsigned) */
469 if ((uid_t)-1 > (uid_t)0) {
470 if (sizeof(zend_long) > sizeof(uid_t) && (lval < 0 || (uid_t)lval != lval)) {
471 do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
472 "for a uid_t value");
473 return;
474 }
475 } else {
476 if (sizeof(zend_long) > sizeof(uid_t) && (uid_t)lval != lval) {
477 do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
478 "for a uid_t value");
479 return;
480 }
481 }
482
483 ival = (uid_t)lval;
484 memcpy(field, &ival, sizeof(ival));
485 }
486 #endif
487
to_zval_read_int(const char * data,zval * zv,res_context * ctx)488 void to_zval_read_int(const char *data, zval *zv, res_context *ctx)
489 {
490 int ival;
491 memcpy(&ival, data, sizeof(ival));
492
493 ZVAL_LONG(zv, (zend_long)ival);
494 }
to_zval_read_net_uint16(const char * data,zval * zv,res_context * ctx)495 static void to_zval_read_net_uint16(const char *data, zval *zv, res_context *ctx)
496 {
497 uint16_t ival;
498 memcpy(&ival, data, sizeof(ival));
499
500 ZVAL_LONG(zv, (zend_long)ntohs(ival));
501 }
to_zval_read_sa_family(const char * data,zval * zv,res_context * ctx)502 static void to_zval_read_sa_family(const char *data, zval *zv, res_context *ctx)
503 {
504 sa_family_t ival;
505 memcpy(&ival, data, sizeof(ival));
506
507 ZVAL_LONG(zv, (zend_long)ival);
508 }
509 #if HAVE_IPV6
to_zval_read_unsigned(const char * data,zval * zv,res_context * ctx)510 static void to_zval_read_unsigned(const char *data, zval *zv, res_context *ctx)
511 {
512 unsigned ival;
513 memcpy(&ival, data, sizeof(ival));
514
515 ZVAL_LONG(zv, (zend_long)ival);
516 }
to_zval_read_uint32(const char * data,zval * zv,res_context * ctx)517 static void to_zval_read_uint32(const char *data, zval *zv, res_context *ctx)
518 {
519 uint32_t ival;
520 memcpy(&ival, data, sizeof(ival));
521
522 ZVAL_LONG(zv, (zend_long)ival);
523 }
524 #endif
525 #if defined(SO_PASSCRED) || defined(LOCAL_CREDS_PERSISTENT) || defined(LOCAL_CREDS)
to_zval_read_pid_t(const char * data,zval * zv,res_context * ctx)526 static void to_zval_read_pid_t(const char *data, zval *zv, res_context *ctx)
527 {
528 pid_t ival;
529 memcpy(&ival, data, sizeof(ival));
530
531 ZVAL_LONG(zv, (zend_long)ival);
532 }
to_zval_read_uid_t(const char * data,zval * zv,res_context * ctx)533 static void to_zval_read_uid_t(const char *data, zval *zv, res_context *ctx)
534 {
535 uid_t ival;
536 memcpy(&ival, data, sizeof(ival));
537
538 ZVAL_LONG(zv, (zend_long)ival);
539 }
540 #endif
541
542 /* CONVERSIONS for sockaddr */
from_zval_write_sin_addr(const zval * zaddr_str,char * inaddr,ser_context * ctx)543 static void from_zval_write_sin_addr(const zval *zaddr_str, char *inaddr, ser_context *ctx)
544 {
545 int res;
546 struct sockaddr_in saddr = {0};
547 zend_string *addr_str, *tmp_addr_str;
548
549 addr_str = zval_get_tmp_string((zval *) zaddr_str, &tmp_addr_str);
550 res = php_set_inet_addr(&saddr, ZSTR_VAL(addr_str), ctx->sock);
551 if (res) {
552 memcpy(inaddr, &saddr.sin_addr, sizeof saddr.sin_addr);
553 } else {
554 /* error already emitted, but let's emit another more relevant */
555 do_from_zval_err(ctx, "could not resolve address '%s' to get an AF_INET "
556 "address", ZSTR_VAL(addr_str));
557 }
558
559 zend_tmp_string_release(tmp_addr_str);
560 }
to_zval_read_sin_addr(const char * data,zval * zv,res_context * ctx)561 static void to_zval_read_sin_addr(const char *data, zval *zv, res_context *ctx)
562 {
563 const struct in_addr *addr = (const struct in_addr *)data;
564 socklen_t size = INET_ADDRSTRLEN;
565 zend_string *str = zend_string_alloc(size - 1, 0);
566 memset(ZSTR_VAL(str), '\0', size);
567
568 ZVAL_NEW_STR(zv, str);
569
570 if (inet_ntop(AF_INET, addr, Z_STRVAL_P(zv), size) == NULL) {
571 do_to_zval_err(ctx, "could not convert IPv4 address to string "
572 "(errno %d)", errno);
573 return;
574 }
575
576 Z_STRLEN_P(zv) = strlen(Z_STRVAL_P(zv));
577 }
578 static const field_descriptor descriptors_sockaddr_in[] = {
579 {"family", sizeof("family"), 0, offsetof(struct sockaddr_in, sin_family), from_zval_write_sa_family, to_zval_read_sa_family},
580 {"addr", sizeof("addr"), 0, offsetof(struct sockaddr_in, sin_addr), from_zval_write_sin_addr, to_zval_read_sin_addr},
581 {"port", sizeof("port"), 0, offsetof(struct sockaddr_in, sin_port), from_zval_write_net_uint16, to_zval_read_net_uint16},
582 {0}
583 };
from_zval_write_sockaddr_in(const zval * container,char * sockaddr,ser_context * ctx)584 static void from_zval_write_sockaddr_in(const zval *container, char *sockaddr, ser_context *ctx)
585 {
586 from_zval_write_aggregation(container, sockaddr, descriptors_sockaddr_in, ctx);
587 }
to_zval_read_sockaddr_in(const char * data,zval * zv,res_context * ctx)588 static void to_zval_read_sockaddr_in(const char *data, zval *zv, res_context *ctx)
589 {
590 to_zval_read_aggregation(data, zv, descriptors_sockaddr_in, ctx);
591 }
592 #if HAVE_IPV6
from_zval_write_sin6_addr(const zval * zaddr_str,char * addr6,ser_context * ctx)593 static void from_zval_write_sin6_addr(const zval *zaddr_str, char *addr6, ser_context *ctx)
594 {
595 int res;
596 struct sockaddr_in6 saddr6 = {0};
597 zend_string *addr_str, *tmp_addr_str;
598
599 addr_str = zval_get_tmp_string((zval *) zaddr_str, &tmp_addr_str);
600 res = php_set_inet6_addr(&saddr6, ZSTR_VAL(addr_str), ctx->sock);
601 if (res) {
602 memcpy(addr6, &saddr6.sin6_addr, sizeof saddr6.sin6_addr);
603 } else {
604 /* error already emitted, but let's emit another more relevant */
605 do_from_zval_err(ctx, "could not resolve address '%s' to get an AF_INET6 "
606 "address", Z_STRVAL_P(zaddr_str));
607 }
608
609 zend_tmp_string_release(tmp_addr_str);
610 }
to_zval_read_sin6_addr(const char * data,zval * zv,res_context * ctx)611 static void to_zval_read_sin6_addr(const char *data, zval *zv, res_context *ctx)
612 {
613 const struct in6_addr *addr = (const struct in6_addr *)data;
614 socklen_t size = INET6_ADDRSTRLEN;
615 zend_string *str = zend_string_alloc(size - 1, 0);
616
617 memset(ZSTR_VAL(str), '\0', size);
618
619 ZVAL_NEW_STR(zv, str);
620
621 if (inet_ntop(AF_INET6, addr, Z_STRVAL_P(zv), size) == NULL) {
622 do_to_zval_err(ctx, "could not convert IPv6 address to string "
623 "(errno %d)", errno);
624 return;
625 }
626
627 Z_STRLEN_P(zv) = strlen(Z_STRVAL_P(zv));
628 }
629 static const field_descriptor descriptors_sockaddr_in6[] = {
630 {"family", sizeof("family"), 0, offsetof(struct sockaddr_in6, sin6_family), from_zval_write_sa_family, to_zval_read_sa_family},
631 {"addr", sizeof("addr"), 0, offsetof(struct sockaddr_in6, sin6_addr), from_zval_write_sin6_addr, to_zval_read_sin6_addr},
632 {"port", sizeof("port"), 0, offsetof(struct sockaddr_in6, sin6_port), from_zval_write_net_uint16, to_zval_read_net_uint16},
633 {"flowinfo", sizeof("flowinfo"), 0, offsetof(struct sockaddr_in6, sin6_flowinfo), from_zval_write_uint32, to_zval_read_uint32},
634 {"scope_id", sizeof("scope_id"), 0, offsetof(struct sockaddr_in6, sin6_scope_id), from_zval_write_uint32, to_zval_read_uint32},
635 {0}
636 };
from_zval_write_sockaddr_in6(const zval * container,char * sockaddr6,ser_context * ctx)637 static void from_zval_write_sockaddr_in6(const zval *container, char *sockaddr6, ser_context *ctx)
638 {
639 from_zval_write_aggregation(container, sockaddr6, descriptors_sockaddr_in6, ctx);
640 }
to_zval_read_sockaddr_in6(const char * data,zval * zv,res_context * ctx)641 static void to_zval_read_sockaddr_in6(const char *data, zval *zv, res_context *ctx)
642 {
643 to_zval_read_aggregation(data, zv, descriptors_sockaddr_in6, ctx);
644 }
645 #endif /* HAVE_IPV6 */
from_zval_write_sun_path(const zval * path,char * sockaddr_un_c,ser_context * ctx)646 static void from_zval_write_sun_path(const zval *path, char *sockaddr_un_c, ser_context *ctx)
647 {
648 zend_string *path_str, *tmp_path_str;
649 struct sockaddr_un *saddr = (struct sockaddr_un*)sockaddr_un_c;
650
651 path_str = zval_get_tmp_string((zval *) path, &tmp_path_str);
652
653 /* code in this file relies on the path being nul terminated, even though
654 * this is not required, at least on linux for abstract paths. It also
655 * assumes that the path is not empty */
656 if (ZSTR_LEN(path_str) == 0) {
657 do_from_zval_err(ctx, "%s", "the path is cannot be empty");
658 zend_tmp_string_release(tmp_path_str);
659 return;
660 }
661 if (ZSTR_LEN(path_str) >= sizeof(saddr->sun_path)) {
662 do_from_zval_err(ctx, "the path is too long, the maximum permitted "
663 "length is %zd", sizeof(saddr->sun_path) - 1);
664 zend_tmp_string_release(tmp_path_str);
665 return;
666 }
667
668 memcpy(&saddr->sun_path, ZSTR_VAL(path_str), ZSTR_LEN(path_str));
669 saddr->sun_path[ZSTR_LEN(path_str)] = '\0';
670
671 zend_tmp_string_release(tmp_path_str);
672 }
to_zval_read_sun_path(const char * data,zval * zv,res_context * ctx)673 static void to_zval_read_sun_path(const char *data, zval *zv, res_context *ctx) {
674 struct sockaddr_un *saddr = (struct sockaddr_un*)data;
675 char *nul_pos;
676
677 nul_pos = memchr(&saddr->sun_path, '\0', sizeof(saddr->sun_path));
678 if (nul_pos == NULL) {
679 do_to_zval_err(ctx, "could not find a NUL in the path");
680 return;
681 }
682
683 ZVAL_STRINGL(zv, saddr->sun_path, nul_pos - (char*)&saddr->sun_path);
684 }
685 static const field_descriptor descriptors_sockaddr_un[] = {
686 {"family", sizeof("family"), 0, offsetof(struct sockaddr_un, sun_family), from_zval_write_sa_family, to_zval_read_sa_family},
687 {"path", sizeof("path"), 0, 0, from_zval_write_sun_path, to_zval_read_sun_path},
688 {0}
689 };
from_zval_write_sockaddr_un(const zval * container,char * sockaddr,ser_context * ctx)690 static void from_zval_write_sockaddr_un(const zval *container, char *sockaddr, ser_context *ctx)
691 {
692 from_zval_write_aggregation(container, sockaddr, descriptors_sockaddr_un, ctx);
693 }
to_zval_read_sockaddr_un(const char * data,zval * zv,res_context * ctx)694 static void to_zval_read_sockaddr_un(const char *data, zval *zv, res_context *ctx)
695 {
696 to_zval_read_aggregation(data, zv, descriptors_sockaddr_un, ctx);
697 }
from_zval_write_sockaddr_aux(const zval * container,struct sockaddr ** sockaddr_ptr,socklen_t * sockaddr_len,ser_context * ctx)698 static void from_zval_write_sockaddr_aux(const zval *container,
699 struct sockaddr **sockaddr_ptr,
700 socklen_t *sockaddr_len,
701 ser_context *ctx)
702 {
703 int family;
704 zval *elem;
705 int fill_sockaddr;
706
707 *sockaddr_ptr = NULL;
708 *sockaddr_len = 0;
709
710 if (Z_TYPE_P(container) != IS_ARRAY) {
711 do_from_zval_err(ctx, "%s", "expected an array here");
712 return;
713 }
714
715 fill_sockaddr = param_get_bool(ctx, KEY_FILL_SOCKADDR, 1);
716
717 if ((elem = zend_hash_str_find(Z_ARRVAL_P(container), "family", sizeof("family") - 1)) != NULL
718 && Z_TYPE_P(elem) != IS_NULL) {
719 const char *node = "family";
720 zend_llist_add_element(&ctx->keys, &node);
721 from_zval_write_int(elem, (char*)&family, ctx);
722 zend_llist_remove_tail(&ctx->keys);
723
724 if (UNEXPECTED(ctx->err.has_error)) {
725 return;
726 }
727 } else {
728 family = ctx->sock->type;
729 }
730
731 switch (family) {
732 case AF_INET:
733 /* though not all OSes support sockaddr_in used in IPv6 sockets */
734 if (ctx->sock->type != AF_INET && ctx->sock->type != AF_INET6) {
735 do_from_zval_err(ctx, "the specified family (number %d) is not "
736 "supported on this socket", family);
737 return;
738 }
739 *sockaddr_ptr = accounted_ecalloc(1, sizeof(struct sockaddr_in), ctx);
740 *sockaddr_len = sizeof(struct sockaddr_in);
741 if (fill_sockaddr) {
742 from_zval_write_sockaddr_in(container, (char*)*sockaddr_ptr, ctx);
743 (*sockaddr_ptr)->sa_family = AF_INET;
744 }
745 break;
746
747 #if HAVE_IPV6
748 case AF_INET6:
749 if (ctx->sock->type != AF_INET6) {
750 do_from_zval_err(ctx, "the specified family (AF_INET6) is not "
751 "supported on this socket");
752 return;
753 }
754 *sockaddr_ptr = accounted_ecalloc(1, sizeof(struct sockaddr_in6), ctx);
755 *sockaddr_len = sizeof(struct sockaddr_in6);
756 if (fill_sockaddr) {
757 from_zval_write_sockaddr_in6(container, (char*)*sockaddr_ptr, ctx);
758 (*sockaddr_ptr)->sa_family = AF_INET6;
759 }
760 break;
761 #endif /* HAVE_IPV6 */
762
763 case AF_UNIX:
764 if (ctx->sock->type != AF_UNIX) {
765 do_from_zval_err(ctx, "the specified family (AF_UNIX) is not "
766 "supported on this socket");
767 return;
768 }
769 *sockaddr_ptr = accounted_ecalloc(1, sizeof(struct sockaddr_un), ctx);
770 if (fill_sockaddr) {
771 struct sockaddr_un *sock_un = (struct sockaddr_un*)*sockaddr_ptr;
772
773 from_zval_write_sockaddr_un(container, (char*)*sockaddr_ptr, ctx);
774 (*sockaddr_ptr)->sa_family = AF_UNIX;
775
776 /* calculating length is more complicated here. Giving the size of
777 * struct sockaddr_un here and relying on the nul termination of
778 * sun_path does not work for paths in the abstract namespace. Note
779 * that we always assume the path is not empty and nul terminated */
780 *sockaddr_len = offsetof(struct sockaddr_un, sun_path) +
781 (sock_un->sun_path[0] == '\0'
782 ? (1 + strlen(&sock_un->sun_path[1]))
783 : strlen(sock_un->sun_path));
784 } else {
785 *sockaddr_len = sizeof(struct sockaddr_un);
786 }
787 break;
788
789 default:
790 do_from_zval_err(ctx, "%s", "the only families currently supported are "
791 "AF_INET, AF_INET6 and AF_UNIX");
792 break;
793 }
794 }
to_zval_read_sockaddr_aux(const char * sockaddr_c,zval * zv,res_context * ctx)795 static void to_zval_read_sockaddr_aux(const char *sockaddr_c, zval *zv, res_context *ctx)
796 {
797 const struct sockaddr *saddr = (struct sockaddr *)sockaddr_c;
798
799 if (saddr->sa_family == 0) {
800 ZVAL_NULL(zv);
801 return;
802 }
803
804 array_init(zv);
805
806 switch (saddr->sa_family) {
807 case AF_INET:
808 to_zval_read_sockaddr_in(sockaddr_c, zv, ctx);
809 break;
810
811 #if HAVE_IPV6
812 case AF_INET6:
813 to_zval_read_sockaddr_in6(sockaddr_c, zv, ctx);
814 break;
815 #endif /* HAVE_IPV6 */
816
817 case AF_UNIX:
818 to_zval_read_sockaddr_un(sockaddr_c, zv, ctx);
819 break;
820
821 default:
822 do_to_zval_err(ctx, "cannot read struct sockaddr with family %d; "
823 "not supported",
824 (int)saddr->sa_family);
825 break;
826 }
827 }
828
829 /* CONVERSIONS for cmsghdr */
830 /*
831 * [ level => , type => , data => [],]
832 * struct cmsghdr {
833 * socklen_t cmsg_len; // data byte count, including header
834 * int cmsg_level; // originating protocol
835 * int cmsg_type; // protocol-specific type
836 * // followed by unsigned char cmsg_data[];
837 * };
838 */
from_zval_write_control(const zval * arr,void ** control_buf,zend_llist_element * alloc,size_t * control_len,size_t * offset,ser_context * ctx)839 static void from_zval_write_control(const zval *arr,
840 void **control_buf,
841 zend_llist_element *alloc,
842 size_t *control_len,
843 size_t *offset,
844 ser_context *ctx)
845 {
846 struct cmsghdr *cmsghdr;
847 int level,
848 type;
849 size_t data_len,
850 req_space,
851 space_left;
852 ancillary_reg_entry *entry;
853
854 static const field_descriptor descriptor_level[] = {
855 {"level", sizeof("level"), 0, 0, from_zval_write_int, 0},
856 {0}
857 };
858 static const field_descriptor descriptor_type[] = {
859 {"type", sizeof("type"), 0, 0, from_zval_write_int, 0},
860 {0}
861 };
862 field_descriptor descriptor_data[] = {
863 {"data", sizeof("data"), 0, 0, 0, 0},
864 {0}
865 };
866
867 from_zval_write_aggregation(arr, (char *)&level, descriptor_level, ctx);
868 if (ctx->err.has_error) {
869 return;
870 }
871 from_zval_write_aggregation(arr, (char *)&type, descriptor_type, ctx);
872 if (ctx->err.has_error) {
873 return;
874 }
875
876 entry = get_ancillary_reg_entry(level, type);
877 if (entry == NULL) {
878 do_from_zval_err(ctx, "cmsghdr with level %d and type %d not supported",
879 level, type);
880 return;
881 }
882
883 if (entry->calc_space) {
884 zval *data_elem;
885 /* arr must be an array at this point */
886 if ((data_elem = zend_hash_str_find(Z_ARRVAL_P(arr), "data", sizeof("data") - 1)) == NULL) {
887 do_from_zval_err(ctx, "cmsghdr should have a 'data' element here");
888 return;
889 }
890 data_len = entry->calc_space(data_elem, ctx);
891 if (ctx->err.has_error) {
892 return;
893 }
894 } else {
895 data_len = entry->size;
896 }
897 req_space = CMSG_SPACE(data_len);
898 space_left = *control_len - *offset;
899 assert(*control_len >= *offset);
900
901 if (space_left < req_space) {
902 *control_buf = safe_erealloc(*control_buf, 2, req_space, *control_len);
903 *control_len += 2 * req_space;
904 memset((char *)*control_buf + *offset, '\0', *control_len - *offset);
905 memcpy(&alloc->data, control_buf, sizeof *control_buf);
906 }
907
908 cmsghdr = (struct cmsghdr*)(((char*)*control_buf) + *offset);
909 cmsghdr->cmsg_level = level;
910 cmsghdr->cmsg_type = type;
911 cmsghdr->cmsg_len = CMSG_LEN(data_len);
912
913 descriptor_data[0].from_zval = entry->from_array;
914 from_zval_write_aggregation(arr, (char*)CMSG_DATA(cmsghdr), descriptor_data, ctx);
915
916 *offset += req_space;
917 }
from_zval_write_control_array(const zval * arr,char * msghdr_c,ser_context * ctx)918 static void from_zval_write_control_array(const zval *arr, char *msghdr_c, ser_context *ctx)
919 {
920 char buf[sizeof("element #4294967295")];
921 char *bufp = buf;
922 zval *elem;
923 uint32_t i = 0;
924 int num_elems;
925 void *control_buf;
926 zend_llist_element *alloc;
927 size_t control_len,
928 cur_offset;
929 struct msghdr *msg = (struct msghdr*)msghdr_c;
930
931 if (Z_TYPE_P(arr) != IS_ARRAY) {
932 do_from_zval_err(ctx, "%s", "expected an array here");
933 return;
934 }
935
936 num_elems = zend_hash_num_elements(Z_ARRVAL_P(arr));
937 if (num_elems == 0) {
938 return;
939 }
940
941 /* estimate each message at 20 bytes */
942 control_buf = accounted_safe_ecalloc(num_elems, CMSG_SPACE(20), 0, ctx);
943 alloc = ctx->allocations.tail;
944 control_len = (size_t)num_elems * CMSG_SPACE(20);
945 cur_offset = 0;
946
947 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), elem) {
948 if (ctx->err.has_error) {
949 break;
950 }
951
952 if ((size_t)snprintf(buf, sizeof(buf), "element #%u", (unsigned)i++) >= sizeof(buf)) {
953 memcpy(buf, "element", sizeof("element"));
954 }
955 zend_llist_add_element(&ctx->keys, &bufp);
956
957 from_zval_write_control(elem, &control_buf, alloc, &control_len, &cur_offset, ctx);
958
959 zend_llist_remove_tail(&ctx->keys);
960 } ZEND_HASH_FOREACH_END();
961
962 msg->msg_control = control_buf;
963 msg->msg_controllen = cur_offset; /* not control_len, which may be larger */
964 }
to_zval_read_cmsg_data(const char * cmsghdr_c,zval * zv,res_context * ctx)965 static void to_zval_read_cmsg_data(const char *cmsghdr_c, zval *zv, res_context *ctx)
966 {
967 const struct cmsghdr *cmsg = (const struct cmsghdr *)cmsghdr_c;
968 ancillary_reg_entry *entry;
969 size_t len,
970 *len_p = &len;
971
972 entry = get_ancillary_reg_entry(cmsg->cmsg_level, cmsg->cmsg_type);
973 if (entry == NULL) {
974 do_to_zval_err(ctx, "cmsghdr with level %d and type %d not supported",
975 cmsg->cmsg_level, cmsg->cmsg_type);
976 return;
977 }
978 if (CMSG_LEN(entry->size) > cmsg->cmsg_len) {
979 do_to_zval_err(ctx, "the cmsghdr structure is unexpectedly small; "
980 "expected a length of at least " ZEND_LONG_FMT ", but got " ZEND_LONG_FMT,
981 (zend_long)CMSG_LEN(entry->size), (zend_long)cmsg->cmsg_len);
982 return;
983 }
984
985 len = (size_t)cmsg->cmsg_len; /* use another var because type of cmsg_len varies */
986
987 if (zend_hash_str_add_ptr(&ctx->params, KEY_CMSG_LEN, sizeof(KEY_CMSG_LEN) - 1, len_p) == NULL) {
988 do_to_zval_err(ctx, "%s", "could not set parameter " KEY_CMSG_LEN);
989 return;
990 }
991
992 entry->to_array((const char *)CMSG_DATA(cmsg), zv, ctx);
993
994 zend_hash_str_del(&ctx->params, KEY_CMSG_LEN, sizeof(KEY_CMSG_LEN) - 1);
995 }
to_zval_read_control(const char * cmsghdr_c,zval * zv,res_context * ctx)996 static void to_zval_read_control(const char *cmsghdr_c, zval *zv, res_context *ctx)
997 {
998 /* takes a cmsghdr, not a msghdr like from_zval_write_control */
999 static const field_descriptor descriptors[] = {
1000 {"level", sizeof("level"), 0, offsetof(struct cmsghdr, cmsg_level), 0, to_zval_read_int},
1001 {"type", sizeof("type"), 0, offsetof(struct cmsghdr, cmsg_type), 0, to_zval_read_int},
1002 {"data", sizeof("data"), 0, 0 /* cmsghdr passed */, 0, to_zval_read_cmsg_data},
1003 {0}
1004 };
1005
1006 array_init_size(zv, 3);
1007 to_zval_read_aggregation(cmsghdr_c, zv, descriptors, ctx);
1008 }
to_zval_read_control_array(const char * msghdr_c,zval * zv,res_context * ctx)1009 static void to_zval_read_control_array(const char *msghdr_c, zval *zv, res_context *ctx)
1010 {
1011 struct msghdr *msg = (struct msghdr *)msghdr_c;
1012 struct cmsghdr *cmsg;
1013 char buf[sizeof("element #4294967295")];
1014 char *bufp = buf;
1015 uint32_t i = 1;
1016
1017 array_init(zv);
1018
1019 for (cmsg = CMSG_FIRSTHDR(msg);
1020 cmsg != NULL && !ctx->err.has_error;
1021 cmsg = CMSG_NXTHDR(msg, cmsg)) {
1022 zval *elem, tmp;
1023
1024 ZVAL_NULL(&tmp);
1025 elem = zend_hash_next_index_insert(Z_ARRVAL_P(zv), &tmp);
1026
1027 if ((size_t)snprintf(buf, sizeof(buf), "element #%u", (unsigned)i++) >= sizeof(buf)) {
1028 memcpy(buf, "element", sizeof("element"));
1029 }
1030 zend_llist_add_element(&ctx->keys, &bufp);
1031
1032 to_zval_read_control((const char *)cmsg, elem, ctx);
1033
1034 zend_llist_remove_tail(&ctx->keys);
1035 }
1036 }
1037
1038 /* CONVERSIONS for msghdr */
from_zval_write_name(const zval * zname_arr,char * msghdr_c,ser_context * ctx)1039 static void from_zval_write_name(const zval *zname_arr, char *msghdr_c, ser_context *ctx)
1040 {
1041 struct sockaddr *sockaddr;
1042 socklen_t sockaddr_len;
1043 struct msghdr *msghdr = (struct msghdr *)msghdr_c;
1044
1045 from_zval_write_sockaddr_aux(zname_arr, &sockaddr, &sockaddr_len, ctx);
1046
1047 msghdr->msg_name = sockaddr;
1048 msghdr->msg_namelen = sockaddr_len;
1049 }
to_zval_read_name(const char * sockaddr_p,zval * zv,res_context * ctx)1050 static void to_zval_read_name(const char *sockaddr_p, zval *zv, res_context *ctx)
1051 {
1052 void *name = (void*)*(void**)sockaddr_p;
1053 if (name == NULL) {
1054 ZVAL_NULL(zv);
1055 } else {
1056 to_zval_read_sockaddr_aux(name, zv, ctx);
1057 }
1058 }
from_zval_write_msghdr_buffer_size(const zval * elem,char * msghdr_c,ser_context * ctx)1059 static void from_zval_write_msghdr_buffer_size(const zval *elem, char *msghdr_c, ser_context *ctx)
1060 {
1061 zend_long lval;
1062 struct msghdr *msghdr = (struct msghdr *)msghdr_c;
1063
1064 lval = from_zval_integer_common(elem, ctx);
1065 if (ctx->err.has_error) {
1066 return;
1067 }
1068
1069 if (lval < 0 || (zend_ulong)lval > MAX_USER_BUFF_SIZE) {
1070 do_from_zval_err(ctx, "the buffer size must be between 1 and " ZEND_LONG_FMT "; "
1071 "given " ZEND_LONG_FMT, (zend_long)MAX_USER_BUFF_SIZE, lval);
1072 return;
1073 }
1074
1075 msghdr->msg_iovlen = 1;
1076 msghdr->msg_iov = accounted_emalloc(sizeof(*msghdr->msg_iov) * 1, ctx);
1077 msghdr->msg_iov[0].iov_base = accounted_emalloc((size_t)lval, ctx);
1078 msghdr->msg_iov[0].iov_len = (size_t)lval;
1079 }
from_zval_write_iov_array_aux(zval * elem,unsigned i,void ** args,ser_context * ctx)1080 static void from_zval_write_iov_array_aux(zval *elem, unsigned i, void **args, ser_context *ctx)
1081 {
1082 struct msghdr *msg = args[0];
1083 zend_string *str, *tmp_str;
1084
1085 str = zval_get_tmp_string(elem, &tmp_str);
1086
1087 msg->msg_iov[i - 1].iov_base = accounted_emalloc(ZSTR_LEN(str), ctx);
1088 msg->msg_iov[i - 1].iov_len = ZSTR_LEN(str);
1089 memcpy(msg->msg_iov[i - 1].iov_base, ZSTR_VAL(str), ZSTR_LEN(str));
1090
1091 zend_tmp_string_release(tmp_str);
1092 }
from_zval_write_iov_array(const zval * arr,char * msghdr_c,ser_context * ctx)1093 static void from_zval_write_iov_array(const zval *arr, char *msghdr_c, ser_context *ctx)
1094 {
1095 int num_elem;
1096 struct msghdr *msg = (struct msghdr*)msghdr_c;
1097
1098 if (Z_TYPE_P(arr) != IS_ARRAY) {
1099 do_from_zval_err(ctx, "%s", "expected an array here");
1100 return;
1101 }
1102
1103 num_elem = zend_hash_num_elements(Z_ARRVAL_P(arr));
1104 if (num_elem == 0) {
1105 return;
1106 }
1107
1108 msg->msg_iov = accounted_safe_ecalloc(num_elem, sizeof *msg->msg_iov, 0, ctx);
1109 msg->msg_iovlen = (size_t)num_elem;
1110
1111 from_array_iterate(arr, from_zval_write_iov_array_aux, (void**)&msg, ctx);
1112 }
from_zval_write_controllen(const zval * elem,char * msghdr_c,ser_context * ctx)1113 static void from_zval_write_controllen(const zval *elem, char *msghdr_c, ser_context *ctx)
1114 {
1115 struct msghdr *msghdr = (struct msghdr *)msghdr_c;
1116 uint32_t len;
1117
1118 /* controllen should be an unsigned with at least 32-bit. Let's assume
1119 * this least common denominator
1120 */
1121 from_zval_write_uint32(elem, (char*)&len, ctx);
1122 if (ctx->err.has_error) {
1123 return;
1124 }
1125 if (len == 0) {
1126 do_from_zval_err(ctx, "controllen cannot be 0");
1127 return;
1128 }
1129 msghdr->msg_control = accounted_emalloc(len, ctx);
1130 msghdr->msg_controllen = len;
1131 }
from_zval_write_msghdr_send(const zval * container,char * msghdr_c,ser_context * ctx)1132 void from_zval_write_msghdr_send(const zval *container, char *msghdr_c, ser_context *ctx)
1133 {
1134 static const field_descriptor descriptors[] = {
1135 {"name", sizeof("name"), 0, 0, from_zval_write_name, 0},
1136 {"iov", sizeof("iov"), 0, 0, from_zval_write_iov_array, 0},
1137 {"control", sizeof("control"), 0, 0, from_zval_write_control_array, 0},
1138 {0}
1139 };
1140
1141 from_zval_write_aggregation(container, msghdr_c, descriptors, ctx);
1142 }
from_zval_write_msghdr_recv(const zval * container,char * msghdr_c,ser_context * ctx)1143 void from_zval_write_msghdr_recv(const zval *container, char *msghdr_c, ser_context *ctx)
1144 {
1145 /* zval to struct msghdr, version for recvmsg(). It differs from the version
1146 * for sendmsg() in that it:
1147 * - has a buffer_size instead of an iov array;
1148 * - has no control element; has a controllen element instead
1149 * struct msghdr {
1150 * void *msg_name;
1151 * socklen_t msg_namelen;
1152 * struct iovec *msg_iov;
1153 * size_t msg_iovlen;
1154 * void *msg_control;
1155 * size_t msg_controllen; //can also be socklen_t
1156 * int msg_flags;
1157 * };
1158 */
1159 static const field_descriptor descriptors[] = {
1160 {"name", sizeof("name"), 0, 0, from_zval_write_name, 0},
1161 {"buffer_size", sizeof("buffer_size"), 0, 0, from_zval_write_msghdr_buffer_size, 0},
1162 {"controllen", sizeof("controllen"), 1, 0, from_zval_write_controllen, 0},
1163 {0}
1164 };
1165 struct msghdr *msghdr = (struct msghdr *)msghdr_c;
1166 const int falsev = 0,
1167 *falsevp = &falsev;
1168
1169 if (zend_hash_str_add_ptr(&ctx->params, KEY_FILL_SOCKADDR, sizeof(KEY_FILL_SOCKADDR) - 1, (void *)falsevp) == NULL) {
1170 do_from_zval_err(ctx, "could not add fill_sockaddr; this is a bug");
1171 return;
1172 }
1173
1174 from_zval_write_aggregation(container, msghdr_c, descriptors, ctx);
1175
1176 zend_hash_str_del(&ctx->params, KEY_FILL_SOCKADDR, sizeof(KEY_FILL_SOCKADDR) - 1);
1177 if (ctx->err.has_error) {
1178 return;
1179 }
1180
1181 if (msghdr->msg_iovlen == 0) {
1182 msghdr->msg_iovlen = 1;
1183 msghdr->msg_iov = accounted_emalloc(sizeof(*msghdr->msg_iov) * 1, ctx);
1184 msghdr->msg_iov[0].iov_base = accounted_emalloc((size_t)DEFAULT_BUFF_SIZE, ctx);
1185 msghdr->msg_iov[0].iov_len = (size_t)DEFAULT_BUFF_SIZE;
1186 }
1187 }
1188
to_zval_read_iov(const char * msghdr_c,zval * zv,res_context * ctx)1189 static void to_zval_read_iov(const char *msghdr_c, zval *zv, res_context *ctx)
1190 {
1191 const struct msghdr *msghdr = (const struct msghdr *)msghdr_c;
1192 size_t iovlen = msghdr->msg_iovlen;
1193 ssize_t *recvmsg_ret,
1194 bytes_left;
1195 uint32_t i;
1196
1197 if (iovlen > UINT_MAX) {
1198 do_to_zval_err(ctx, "unexpectedly large value for iov_len: %lu",
1199 (unsigned long)iovlen);
1200 }
1201 array_init_size(zv, (uint32_t)iovlen);
1202
1203 if ((recvmsg_ret = zend_hash_str_find_ptr(&ctx->params, KEY_RECVMSG_RET, sizeof(KEY_RECVMSG_RET) - 1)) == NULL) {
1204 do_to_zval_err(ctx, "recvmsg_ret not found in params. This is a bug");
1205 return;
1206 }
1207 bytes_left = *recvmsg_ret;
1208
1209 for (i = 0; bytes_left > 0 && i < (uint32_t)iovlen; i++) {
1210 zval elem;
1211 size_t len = MIN(msghdr->msg_iov[i].iov_len, (size_t)bytes_left);
1212 zend_string *buf = zend_string_alloc(len, 0);
1213
1214 memcpy(ZSTR_VAL(buf), msghdr->msg_iov[i].iov_base, ZSTR_LEN(buf));
1215 ZSTR_VAL(buf)[ZSTR_LEN(buf)] = '\0';
1216
1217 ZVAL_NEW_STR(&elem, buf);
1218 add_next_index_zval(zv, &elem);
1219 bytes_left -= len;
1220 }
1221 }
to_zval_read_msghdr(const char * msghdr_c,zval * zv,res_context * ctx)1222 void to_zval_read_msghdr(const char *msghdr_c, zval *zv, res_context *ctx)
1223 {
1224 static const field_descriptor descriptors[] = {
1225 {"name", sizeof("name"), 0, offsetof(struct msghdr, msg_name), 0, to_zval_read_name},
1226 {"control", sizeof("control"), 0, 0, 0, to_zval_read_control_array},
1227 {"iov", sizeof("iov"), 0, 0, 0, to_zval_read_iov},
1228 {"flags", sizeof("flags"), 0, offsetof(struct msghdr, msg_flags), 0, to_zval_read_int},
1229 {0}
1230 };
1231
1232 array_init_size(zv, 4);
1233
1234 to_zval_read_aggregation(msghdr_c, zv, descriptors, ctx);
1235 }
1236
1237 #if defined(IPV6_PKTINFO) && HAVE_IPV6
1238 /* CONVERSIONS for if_index */
from_zval_write_ifindex(const zval * zv,char * uinteger,ser_context * ctx)1239 static void from_zval_write_ifindex(const zval *zv, char *uinteger, ser_context *ctx)
1240 {
1241 unsigned ret = 0;
1242
1243 if (Z_TYPE_P(zv) == IS_LONG) {
1244 if (Z_LVAL_P(zv) < 0 || (zend_ulong)Z_LVAL_P(zv) > UINT_MAX) { /* allow 0 (unspecified interface) */
1245 do_from_zval_err(ctx, "the interface index cannot be negative or "
1246 "larger than %u; given " ZEND_LONG_FMT, UINT_MAX, Z_LVAL_P(zv));
1247 } else {
1248 ret = (unsigned)Z_LVAL_P(zv);
1249 }
1250 } else {
1251 zend_string *str, *tmp_str;
1252
1253 str = zval_get_tmp_string((zval *) zv, &tmp_str);
1254
1255 #if HAVE_IF_NAMETOINDEX
1256 ret = if_nametoindex(ZSTR_VAL(str));
1257 if (ret == 0) {
1258 do_from_zval_err(ctx, "no interface with name \"%s\" could be found", ZSTR_VAL(str));
1259 }
1260 #elif defined(SIOCGIFINDEX)
1261 {
1262 struct ifreq ifr;
1263 if (strlcpy(ifr.ifr_name, ZSTR_VAL(str), sizeof(ifr.ifr_name))
1264 >= sizeof(ifr.ifr_name)) {
1265 do_from_zval_err(ctx, "the interface name \"%s\" is too large ", ZSTR_VAL(str));
1266 } else if (ioctl(ctx->sock->bsd_socket, SIOCGIFINDEX, &ifr) < 0) {
1267 if (errno == ENODEV) {
1268 do_from_zval_err(ctx, "no interface with name \"%s\" could be "
1269 "found", ZSTR_VAL(str));
1270 } else {
1271 do_from_zval_err(ctx, "error fetching interface index for "
1272 "interface with name \"%s\" (errno %d)",
1273 ZSTR_VAL(str), errno);
1274 }
1275 } else {
1276 ret = (unsigned)ifr.ifr_ifindex;
1277 }
1278 }
1279 #else
1280 do_from_zval_err(ctx,
1281 "this platform does not support looking up an interface by "
1282 "name, an integer interface index must be supplied instead");
1283 #endif
1284
1285 zend_tmp_string_release(tmp_str);
1286 }
1287
1288 if (!ctx->err.has_error) {
1289 memcpy(uinteger, &ret, sizeof(ret));
1290 }
1291 }
1292
1293 /* CONVERSIONS for struct in6_pktinfo */
1294 static const field_descriptor descriptors_in6_pktinfo[] = {
1295 {"addr", sizeof("addr"), 1, offsetof(struct in6_pktinfo, ipi6_addr), from_zval_write_sin6_addr, to_zval_read_sin6_addr},
1296 {"ifindex", sizeof("ifindex"), 1, offsetof(struct in6_pktinfo, ipi6_ifindex), from_zval_write_ifindex, to_zval_read_unsigned},
1297 {0}
1298 };
from_zval_write_in6_pktinfo(const zval * container,char * in6_pktinfo_c,ser_context * ctx)1299 void from_zval_write_in6_pktinfo(const zval *container, char *in6_pktinfo_c, ser_context *ctx)
1300 {
1301 from_zval_write_aggregation(container, in6_pktinfo_c, descriptors_in6_pktinfo, ctx);
1302 }
to_zval_read_in6_pktinfo(const char * data,zval * zv,res_context * ctx)1303 void to_zval_read_in6_pktinfo(const char *data, zval *zv, res_context *ctx)
1304 {
1305 array_init_size(zv, 2);
1306
1307 to_zval_read_aggregation(data, zv, descriptors_in6_pktinfo, ctx);
1308 }
1309 #endif
1310
1311 /* CONVERSIONS for struct ucred */
1312 #if defined(SO_PASSCRED) || defined(LOCAL_CREDS_PERSISTENT) || defined(LOCAL_CREDS)
1313 static const field_descriptor descriptors_ucred[] = {
1314 #if defined(LOCAL_CREDS_PERSISTENT)
1315 {"pid", sizeof("pid"), 1, offsetof(struct sockcred2, sc_pid), from_zval_write_pid_t, to_zval_read_pid_t},
1316 {"uid", sizeof("uid"), 1, offsetof(struct sockcred2, sc_euid), from_zval_write_uid_t, to_zval_read_uid_t},
1317 /* the type gid_t is the same as uid_t: */
1318 {"gid", sizeof("gid"), 1, offsetof(struct sockcred2, sc_egid), from_zval_write_uid_t, to_zval_read_uid_t},
1319 #elif defined(LOCAL_CREDS)
1320 {"pid", sizeof("pid"), 1, offsetof(struct sockcred, sc_pid), from_zval_write_pid_t, to_zval_read_pid_t},
1321 {"uid", sizeof("uid"), 1, offsetof(struct sockcred, sc_euid), from_zval_write_uid_t, to_zval_read_uid_t},
1322 /* the type gid_t is the same as uid_t: */
1323 {"gid", sizeof("gid"), 1, offsetof(struct sockcred, sc_egid), from_zval_write_uid_t, to_zval_read_uid_t},
1324 #elif defined(ANC_CREDS_CMSGCRED)
1325 {"pid", sizeof("pid"), 1, offsetof(struct cmsgcred, cmcred_pid), from_zval_write_pid_t, to_zval_read_pid_t},
1326 {"uid", sizeof("uid"), 1, offsetof(struct cmsgcred, cmcred_uid), from_zval_write_uid_t, to_zval_read_uid_t},
1327 /* assume the type gid_t is the same as uid_t: */
1328 {"gid", sizeof("gid"), 1, offsetof(struct cmsgcred, cmcred_gid), from_zval_write_uid_t, to_zval_read_uid_t},
1329 #elif defined(SO_PASSCRED)
1330 {"pid", sizeof("pid"), 1, offsetof(struct ucred, pid), from_zval_write_pid_t, to_zval_read_pid_t},
1331 {"uid", sizeof("uid"), 1, offsetof(struct ucred, uid), from_zval_write_uid_t, to_zval_read_uid_t},
1332 /* assume the type gid_t is the same as uid_t: */
1333 {"gid", sizeof("gid"), 1, offsetof(struct ucred, gid), from_zval_write_uid_t, to_zval_read_uid_t},
1334 #endif
1335 {0}
1336 };
from_zval_write_ucred(const zval * container,char * ucred_c,ser_context * ctx)1337 void from_zval_write_ucred(const zval *container, char *ucred_c, ser_context *ctx)
1338 {
1339 from_zval_write_aggregation(container, ucred_c, descriptors_ucred, ctx);
1340 }
to_zval_read_ucred(const char * data,zval * zv,res_context * ctx)1341 void to_zval_read_ucred(const char *data, zval *zv, res_context *ctx)
1342 {
1343 array_init_size(zv, 3);
1344
1345 to_zval_read_aggregation(data, zv, descriptors_ucred, ctx);
1346 }
1347 #endif
1348
1349 /* CONVERSIONS for SCM_RIGHTS */
1350 #ifdef SCM_RIGHTS
calculate_scm_rights_space(const zval * arr,ser_context * ctx)1351 size_t calculate_scm_rights_space(const zval *arr, ser_context *ctx)
1352 {
1353 int num_elems;
1354
1355 if (Z_TYPE_P(arr) != IS_ARRAY) {
1356 do_from_zval_err(ctx, "%s", "expected an array here");
1357 return (size_t)-1;
1358 }
1359
1360 num_elems = zend_hash_num_elements(Z_ARRVAL_P(arr));
1361 if (num_elems == 0) {
1362 do_from_zval_err(ctx, "%s", "expected at least one element in this array");
1363 return (size_t)-1;
1364 }
1365
1366 return zend_hash_num_elements(Z_ARRVAL_P(arr)) * sizeof(int);
1367 }
from_zval_write_fd_array_aux(zval * elem,unsigned i,void ** args,ser_context * ctx)1368 static void from_zval_write_fd_array_aux(zval *elem, unsigned i, void **args, ser_context *ctx)
1369 {
1370 int *iarr = args[0];
1371
1372 if (Z_TYPE_P(elem) == IS_OBJECT && Z_OBJCE_P(elem) == socket_ce) {
1373 php_socket *sock = Z_SOCKET_P(elem);
1374 if (IS_INVALID_SOCKET(sock)) {
1375 do_from_zval_err(ctx, "socket is already closed");
1376 return;
1377 }
1378
1379 iarr[i] = sock->bsd_socket;
1380 return;
1381 }
1382
1383 if (Z_TYPE_P(elem) == IS_RESOURCE) {
1384 php_stream *stream;
1385
1386 stream = (php_stream *)zend_fetch_resource2_ex(elem, NULL, php_file_le_stream(), php_file_le_pstream());
1387 if (stream == NULL) {
1388 do_from_zval_err(ctx, "resource is not a stream");
1389 return;
1390 }
1391
1392 if (php_stream_cast(stream, PHP_STREAM_AS_FD, (void **)&iarr[i - 1], REPORT_ERRORS) == FAILURE) {
1393 do_from_zval_err(ctx, "cast stream to file descriptor failed");
1394 return;
1395 }
1396 } else {
1397 do_from_zval_err(ctx, "expected a Socket object or a stream resource");
1398 }
1399 }
from_zval_write_fd_array(const zval * arr,char * int_arr,ser_context * ctx)1400 void from_zval_write_fd_array(const zval *arr, char *int_arr, ser_context *ctx)
1401 {
1402 if (Z_TYPE_P(arr) != IS_ARRAY) {
1403 do_from_zval_err(ctx, "%s", "expected an array here");
1404 return;
1405 }
1406
1407 from_array_iterate(arr, &from_zval_write_fd_array_aux, (void**)&int_arr, ctx);
1408 }
to_zval_read_fd_array(const char * data,zval * zv,res_context * ctx)1409 void to_zval_read_fd_array(const char *data, zval *zv, res_context *ctx)
1410 {
1411 size_t *cmsg_len;
1412 int num_elems,
1413 i;
1414 struct cmsghdr *dummy_cmsg = 0;
1415 size_t data_offset;
1416
1417 data_offset = (unsigned char *)CMSG_DATA(dummy_cmsg)
1418 - (unsigned char *)dummy_cmsg;
1419
1420 if ((cmsg_len = zend_hash_str_find_ptr(&ctx->params, KEY_CMSG_LEN, sizeof(KEY_CMSG_LEN) - 1)) == NULL) {
1421 do_to_zval_err(ctx, "could not get value of parameter " KEY_CMSG_LEN);
1422 return;
1423 }
1424
1425 if (*cmsg_len < data_offset) {
1426 do_to_zval_err(ctx, "length of cmsg is smaller than its data member "
1427 "offset (" ZEND_LONG_FMT " vs " ZEND_LONG_FMT ")", (zend_long)*cmsg_len, (zend_long)data_offset);
1428 return;
1429 }
1430 num_elems = (*cmsg_len - data_offset) / sizeof(int);
1431
1432 array_init_size(zv, num_elems);
1433
1434 for (i = 0; i < num_elems; i++) {
1435 zval elem;
1436 int fd;
1437 struct stat statbuf;
1438
1439 fd = *((int *)data + i);
1440
1441 /* determine whether we have a socket */
1442 if (fstat(fd, &statbuf) == -1) {
1443 do_to_zval_err(ctx, "error creating resource for received file "
1444 "descriptor %d: fstat() call failed with errno %d", fd, errno);
1445 return;
1446 }
1447 if (S_ISSOCK(statbuf.st_mode)) {
1448 object_init_ex(&elem, socket_ce);
1449 php_socket *sock = Z_SOCKET_P(&elem);
1450
1451 socket_import_file_descriptor(fd, sock);
1452 } else {
1453 php_stream *stream = php_stream_fopen_from_fd(fd, "rw", NULL);
1454 php_stream_to_zval(stream, &elem);
1455 }
1456
1457 add_next_index_zval(zv, &elem);
1458 }
1459 }
1460 #endif
1461
1462 /* ENTRY POINT for conversions */
free_from_zval_allocation(void * alloc_ptr_ptr)1463 static void free_from_zval_allocation(void *alloc_ptr_ptr)
1464 {
1465 efree(*(void**)alloc_ptr_ptr);
1466 }
from_zval_run_conversions(const zval * container,php_socket * sock,from_zval_write_field * writer,size_t struct_size,const char * top_name,zend_llist ** allocations,struct err_s * err)1467 void *from_zval_run_conversions(const zval *container,
1468 php_socket *sock,
1469 from_zval_write_field *writer,
1470 size_t struct_size,
1471 const char *top_name,
1472 zend_llist **allocations /* out */,
1473 struct err_s *err /* in/out */)
1474 {
1475 ser_context ctx;
1476 char *structure;
1477
1478 *allocations = NULL;
1479
1480 if (err->has_error) {
1481 return NULL;
1482 }
1483
1484 memset(&ctx, 0, sizeof(ctx));
1485 zend_hash_init(&ctx.params, 8, NULL, NULL, 0);
1486 zend_llist_init(&ctx.keys, sizeof(const char *), NULL, 0);
1487 zend_llist_init(&ctx.allocations, sizeof(void *), &free_from_zval_allocation, 0);
1488 ctx.sock = sock;
1489
1490 structure = ecalloc(1, struct_size);
1491
1492 zend_llist_add_element(&ctx.keys, &top_name);
1493 zend_llist_add_element(&ctx.allocations, &structure);
1494
1495 /* main call */
1496 writer(container, structure, &ctx);
1497
1498 if (ctx.err.has_error) {
1499 zend_llist_destroy(&ctx.allocations); /* deallocates structure as well */
1500 structure = NULL;
1501 *err = ctx.err;
1502 } else {
1503 *allocations = emalloc(sizeof **allocations);
1504 **allocations = ctx.allocations;
1505 }
1506
1507 zend_llist_destroy(&ctx.keys);
1508 zend_hash_destroy(&ctx.params);
1509
1510 return structure;
1511 }
to_zval_run_conversions(const char * structure,to_zval_read_field * reader,const char * top_name,const struct key_value * key_value_pairs,struct err_s * err,zval * zv)1512 zval *to_zval_run_conversions(const char *structure,
1513 to_zval_read_field *reader,
1514 const char *top_name,
1515 const struct key_value *key_value_pairs,
1516 struct err_s *err, zval *zv)
1517 {
1518 res_context ctx;
1519 const struct key_value *kv;
1520
1521 if (err->has_error) {
1522 return NULL;
1523 }
1524
1525 memset(&ctx, 0, sizeof(ctx));
1526 zend_llist_init(&ctx.keys, sizeof(const char *), NULL, 0);
1527 zend_llist_add_element(&ctx.keys, &top_name);
1528
1529 zend_hash_init(&ctx.params, 8, NULL, NULL, 0);
1530 for (kv = key_value_pairs; kv->key != NULL; kv++) {
1531 zend_hash_str_update_ptr(&ctx.params, kv->key, kv->key_size - 1, kv->value);
1532 }
1533
1534 ZVAL_NULL(zv);
1535 /* main call */
1536 reader(structure, zv, &ctx);
1537
1538 if (ctx.err.has_error) {
1539 zval_ptr_dtor(zv);
1540 ZVAL_UNDEF(zv);
1541 *err = ctx.err;
1542 }
1543
1544 zend_llist_destroy(&ctx.keys);
1545 zend_hash_destroy(&ctx.params);
1546
1547 return Z_ISUNDEF_P(zv)? NULL : zv;
1548 }
1549