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