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