root/ext/sockets/conversions.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. param_get_bool
  2. accounted_emalloc
  3. accounted_ecalloc
  4. accounted_safe_ecalloc
  5. do_from_to_zval_err
  6. ZEND_ATTRIBUTE_FORMAT
  7. ZEND_ATTRIBUTE_FORMAT
  8. err_msg_dispose
  9. allocations_dispose
  10. from_array_iterate
  11. from_zval_write_aggregation
  12. to_zval_read_aggregation
  13. from_zval_integer_common
  14. from_zval_write_int
  15. from_zval_write_uint32
  16. from_zval_write_net_uint16
  17. from_zval_write_sa_family
  18. from_zval_write_pid_t
  19. from_zval_write_uid_t
  20. to_zval_read_int
  21. to_zval_read_unsigned
  22. to_zval_read_net_uint16
  23. to_zval_read_uint32
  24. to_zval_read_sa_family
  25. to_zval_read_pid_t
  26. to_zval_read_uid_t
  27. from_zval_write_sin_addr
  28. to_zval_read_sin_addr
  29. from_zval_write_sockaddr_in
  30. to_zval_read_sockaddr_in
  31. from_zval_write_sin6_addr
  32. to_zval_read_sin6_addr
  33. from_zval_write_sockaddr_in6
  34. to_zval_read_sockaddr_in6
  35. from_zval_write_sun_path
  36. to_zval_read_sun_path
  37. from_zval_write_sockaddr_un
  38. to_zval_read_sockaddr_un
  39. from_zval_write_sockaddr_aux
  40. to_zval_read_sockaddr_aux
  41. from_zval_write_control
  42. from_zval_write_control_array
  43. to_zval_read_cmsg_data
  44. to_zval_read_control
  45. to_zval_read_control_array
  46. from_zval_write_name
  47. to_zval_read_name
  48. from_zval_write_msghdr_buffer_size
  49. from_zval_write_iov_array_aux
  50. from_zval_write_iov_array
  51. from_zval_write_controllen
  52. from_zval_write_msghdr_send
  53. from_zval_write_msghdr_recv
  54. to_zval_read_iov
  55. to_zval_read_msghdr
  56. from_zval_write_ifindex
  57. from_zval_write_in6_pktinfo
  58. to_zval_read_in6_pktinfo
  59. from_zval_write_ucred
  60. to_zval_read_ucred
  61. calculate_scm_rights_space
  62. from_zval_write_fd_array_aux
  63. from_zval_write_fd_array
  64. to_zval_read_fd_array
  65. free_from_zval_allocation
  66. from_zval_run_conversions
  67. to_zval_run_conversions

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

/* [<][>][^][v][top][bottom][index][help] */