]> granicus.if.org Git - php/commitdiff
Refactoring: move stuff to new conversions.c
authorGustavo Lopes <glopes@nebm.ist.utl.pt>
Sun, 11 Nov 2012 23:40:38 +0000 (00:40 +0100)
committerGustavo Lopes <glopes@nebm.ist.utl.pt>
Sat, 2 Feb 2013 15:38:08 +0000 (16:38 +0100)
ext/sockets/config.m4
ext/sockets/conversions.c [new file with mode: 0644]
ext/sockets/conversions.h [new file with mode: 0644]
ext/sockets/sendrecvmsg.c
ext/sockets/sendrecvmsg.h

index 3b5b90714b7279b46caf7c09aae58c9b7a42acba..9c752496463f5c7db95567b66ed94ba5e9251663 100644 (file)
@@ -43,6 +43,6 @@ if test "$PHP_SOCKETS" != "no"; then
     AC_DEFINE(HAVE_SA_SS_FAMILY,1,[Whether you have sockaddr_storage.ss_family])
   fi
 
-  PHP_NEW_EXTENSION([sockets], [sockets.c multicast.c sockaddr_conv.c sendrecvmsg.c], [$ext_shared])
+  PHP_NEW_EXTENSION([sockets], [sockets.c multicast.c conversions.c sockaddr_conv.c sendrecvmsg.c], [$ext_shared])
   PHP_INSTALL_HEADERS([ext/sockets/], [php_sockets.h])
 fi
diff --git a/ext/sockets/conversions.c b/ext/sockets/conversions.c
new file mode 100644 (file)
index 0000000..7ca9972
--- /dev/null
@@ -0,0 +1,1477 @@
+#include "conversions.h"
+#include "sockaddr_conv.h"
+#include "conversions.h"
+#include "sendrecvmsg.h" /* for ancillary registry */
+
+#include <Zend/zend_llist.h>
+#include <ext/standard/php_smart_str.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+
+#define MAX_USER_BUFF_SIZE ((size_t)(100*1024*1024))
+#define DEFAULT_BUFF_SIZE 8192
+
+struct _ser_context {
+       HashTable               params; /* stores pointers; has to be first */
+       struct err_s    err;
+       zend_llist              keys,
+       /* common part to res_context ends here */
+                                       allocations;
+       php_socket              *sock;
+};
+struct _res_context {
+       HashTable               params; /* stores pointers; has to be first */
+       struct err_s    err;
+       zend_llist              keys;
+};
+
+typedef struct {
+       /* zval info */
+       const char *name;
+       unsigned name_size;
+       int required;
+
+       /* structure info */
+       size_t field_offset; /* 0 to pass full structure, e.g. when more than
+                                                       one field is to be changed; in that case the
+                                                       callbacks need to know the name of the fields */
+
+       /* callbacks */
+       from_zval_write_field *from_zval;
+       to_zval_read_field *to_zval;
+} field_descriptor;
+
+#define KEY_FILL_SOCKADDR "fill_sockaddr"
+#define KEY_RECVMSG_RET "recvmsg_ret"
+#define KEY_CMSG_LEN   "cmsg_len"
+
+/* PARAMETERS */
+static int param_get_bool(void *ctx, const char *key, int def)
+{
+       int **elem;
+       if (zend_hash_find(ctx, key, strlen(key) + 1, (void**)&elem) == SUCCESS) {
+               return **elem;
+       } else {
+               return def;
+       }
+}
+
+/* MEMORY */
+static inline void *accounted_emalloc(size_t alloc_size, ser_context *ctx)
+{
+       void *ret = emalloc(alloc_size);
+       zend_llist_add_element(&ctx->allocations, &ret);
+       return ret;
+}
+static inline void *accounted_ecalloc(size_t nmemb, size_t alloc_size, ser_context *ctx)
+{
+       void *ret = ecalloc(nmemb, alloc_size);
+       zend_llist_add_element(&ctx->allocations, &ret);
+       return ret;
+}
+static inline void *accounted_safe_ecalloc(size_t nmemb, size_t alloc_size, size_t offset, ser_context *ctx)
+{
+       void *ret = safe_emalloc(nmemb, alloc_size, offset);
+       memset(ret, '\0', nmemb * alloc_size + offset);
+       zend_llist_add_element(&ctx->allocations, &ret);
+       return ret;
+}
+
+/* ERRORS */
+static void do_from_to_zval_err(struct err_s *err,
+                                                               zend_llist *keys,
+                                                               const char *what_conv,
+                                                               const char *fmt,
+                                                               va_list ap)
+{
+       smart_str                       path = {0};
+       const char                      **node;
+       char                            *user_msg;
+       int                                     user_msg_size;
+       zend_llist_position     pos;
+
+       if (err->has_error) {
+               return;
+       }
+
+       for (node = zend_llist_get_first_ex(keys, &pos);
+                       node != NULL;
+                       node = zend_llist_get_next_ex(keys, &pos)) {
+               smart_str_appends(&path, *node);
+               smart_str_appends(&path, " > ");
+       }
+
+       if (path.len > 3) {
+               path.len -= 3;
+       }
+       smart_str_0(&path);
+
+       user_msg_size = vspprintf(&user_msg, 0, fmt, ap);
+
+       err->has_error = 1;
+       err->level = E_WARNING;
+       spprintf(&err->msg, 0, "error converting %s data (path: %s): %.*s",
+                       what_conv,
+                       path.c && path.c != '\0' ? path.c : "unavailable",
+                       user_msg_size, user_msg);
+       err->should_free = 1;
+
+       efree(user_msg);
+       smart_str_free_ex(&path, 0);
+}
+__attribute__ ((format (printf, 2, 3)))
+static void do_from_zval_err(ser_context *ctx, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       do_from_to_zval_err(&ctx->err, &ctx->keys, "user", fmt, ap);
+       va_end(ap);
+}
+__attribute__ ((format (printf, 2, 3)))
+static void do_to_zval_err(res_context *ctx, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       do_from_to_zval_err(&ctx->err, &ctx->keys, "native", fmt, ap);
+       va_end(ap);
+}
+
+void err_msg_dispose(struct err_s *err TSRMLS_DC)
+{
+       if (err->msg != NULL) {
+               php_error_docref0(NULL TSRMLS_CC, err->level, "%s", err->msg);
+               if (err->should_free) {
+                       efree(err->msg);
+               }
+       }
+}
+void allocations_dispose(zend_llist **allocations)
+{
+       zend_llist_destroy(*allocations);
+       efree(*allocations);
+       *allocations = NULL;
+}
+
+static unsigned from_array_iterate(const zval *arr,
+                                                                  void (*func)(zval **elem, unsigned i, void **args, ser_context *ctx),
+                                                                  void **args,
+                                                                  ser_context *ctx)
+{
+       HashPosition    pos;
+       unsigned                i;
+       zval                    **elem;
+       char                    buf[sizeof("element #4294967295")];
+       char                    *bufp = buf;
+
+    for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(arr), &pos), i = 1;
+                       !ctx->err.has_error
+                       && zend_hash_get_current_data_ex(Z_ARRVAL_P(arr), (void **)&elem, &pos) == SUCCESS;
+                       zend_hash_move_forward_ex(Z_ARRVAL_P(arr), &pos), i++) {
+               if (snprintf(buf, sizeof(buf), "element #%u", i) >= sizeof(buf)) {
+                       memcpy(buf, "element", sizeof("element"));
+               }
+               zend_llist_add_element(&ctx->keys, &bufp);
+
+               func(elem, i, args, ctx);
+
+               zend_llist_remove_tail(&ctx->keys);
+    }
+
+    return i -1;
+}
+
+/* Generic Aggregated conversions */
+static void from_zval_write_aggregation(const zval *container,
+                                                                               char *structure,
+                                                                               const field_descriptor *descriptors,
+                                                                               ser_context *ctx)
+{
+       const field_descriptor  *descr;
+       zval                                    **elem;
+
+       if (Z_TYPE_P(container) != IS_ARRAY) {
+               do_from_zval_err(ctx, "%s", "expected an array here");
+       }
+
+       for (descr = descriptors; descr->name != NULL && !ctx->err.has_error; descr++) {
+               if (zend_hash_find(Z_ARRVAL_P(container),
+                               descr->name, descr->name_size, (void**)&elem) == SUCCESS) {
+
+                       if (descr->from_zval == NULL) {
+                               do_from_zval_err(ctx, "No information on how to convert value "
+                                               "of key '%s'", descr->name);
+                               break;
+                       }
+
+                       zend_llist_add_element(&ctx->keys, (void*)&descr->name);
+                       descr->from_zval(*elem, ((char*)structure) + descr->field_offset, ctx);
+                       zend_llist_remove_tail(&ctx->keys);
+
+               } else if (descr->required) {
+                       do_from_zval_err(ctx, "The key '%s' is required", descr->name);
+                       break;
+               }
+       }
+}
+static void to_zval_read_aggregation(const char *structure,
+                                                                        zval *zarr, /* initialized array */
+                                                                        const field_descriptor *descriptors,
+                                                                        res_context *ctx)
+{
+       const field_descriptor  *descr;
+
+       assert(Z_TYPE_P(zarr) == IS_ARRAY);
+       assert(Z_ARRVAL_P(zarr) != NULL);
+
+       for (descr = descriptors; descr->name != NULL && !ctx->err.has_error; descr++) {
+               zval *new_zv;
+
+               if (descr->to_zval == NULL) {
+                       do_to_zval_err(ctx, "No information on how to convert native "
+                                       "field into value for key '%s'", descr->name);
+                       break;
+               }
+
+               ALLOC_INIT_ZVAL(new_zv);
+               add_assoc_zval_ex(zarr, descr->name, descr->name_size, new_zv);
+
+               zend_llist_add_element(&ctx->keys, (void*)&descr->name);
+               descr->to_zval(structure + descr->field_offset, new_zv, ctx);
+               zend_llist_remove_tail(&ctx->keys);
+       }
+}
+
+/* CONVERSIONS for integers */
+static long from_zval_integer_common(const zval *arr_value, ser_context *ctx)
+{
+       long ret = 0;
+       zval lzval = zval_used_for_init;
+
+       if (Z_TYPE_P(arr_value) != IS_LONG) {
+               ZVAL_COPY_VALUE(&lzval, arr_value);
+               zval_copy_ctor(&lzval);
+               arr_value = &lzval;
+       }
+
+       switch (Z_TYPE_P(arr_value)) {
+       case IS_LONG:
+long_case:
+               ret = Z_LVAL_P(arr_value);
+               break;
+
+       /* if not long we're operating on lzval */
+       case IS_DOUBLE:
+double_case:
+               convert_to_long(&lzval);
+               goto long_case;
+
+       case IS_OBJECT:
+       case IS_STRING: {
+               long lval;
+               double dval;
+
+               convert_to_string(&lzval);
+
+               switch (is_numeric_string(Z_STRVAL(lzval), Z_STRLEN(lzval), &lval, &dval, 0)) {
+               case IS_DOUBLE:
+                       zval_dtor(&lzval);
+                       Z_TYPE(lzval) = IS_DOUBLE;
+                       Z_DVAL(lzval) = dval;
+                       goto double_case;
+
+               case IS_LONG:
+                       zval_dtor(&lzval);
+                       Z_TYPE(lzval) = IS_LONG;
+                       Z_DVAL(lzval) = lval;
+                       goto long_case;
+               }
+
+               /* if we get here, we don't have a numeric string */
+               do_from_zval_err(ctx, "expected an integer, but got a non numeric "
+                               "string (possibly from a converted object): '%s'", Z_STRVAL_P(arr_value));
+               break;
+       }
+
+       default:
+               do_from_zval_err(ctx, "%s", "expected an integer, either of a PHP "
+                               "integer type or of a convertible type");
+               break;
+       }
+
+       zval_dtor(&lzval);
+
+       return ret;
+}
+void from_zval_write_int(const zval *arr_value, char *field, ser_context *ctx)
+{
+       long lval;
+       int ival;
+
+       lval = from_zval_integer_common(arr_value, ctx);
+       if (ctx->err.has_error) {
+               return;
+       }
+
+       if (lval > INT_MAX || lval < INT_MIN) {
+               do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
+                               "for a native int");
+               return;
+       }
+
+       ival = (int)lval;
+       memcpy(field, &ival, sizeof(ival));
+}
+static void from_zval_write_unsigned(const zval *arr_value, char *field, ser_context *ctx)
+{
+       long lval;
+       unsigned ival;
+
+       lval = from_zval_integer_common(arr_value, ctx);
+       if (ctx->err.has_error) {
+               return;
+       }
+
+       if (sizeof(long) > sizeof(ival) && (lval < 0 || lval > UINT_MAX)) {
+               do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
+                               "for a native unsigned int");
+               return;
+       }
+
+       ival = (unsigned)lval;
+       memcpy(field, &ival, sizeof(ival));
+}
+static void from_zval_write_uint32(const zval *arr_value, char *field, ser_context *ctx)
+{
+       long lval;
+       uint32_t ival;
+
+       lval = from_zval_integer_common(arr_value, ctx);
+       if (ctx->err.has_error) {
+               return;
+       }
+
+       if (sizeof(long) > sizeof(uint32_t) && (lval < 0 || lval > 0xFFFFFFFF)) {
+               do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
+                               "for an unsigned 32-bit integer");
+               return;
+       }
+
+       ival = (uint32_t)lval;
+       memcpy(field, &ival, sizeof(ival));
+}
+static void from_zval_write_net_uint16(const zval *arr_value, char *field, ser_context *ctx)
+{
+       long lval;
+       uint16_t ival;
+
+       lval = from_zval_integer_common(arr_value, ctx);
+       if (ctx->err.has_error) {
+               return;
+       }
+
+       if (lval < 0 || lval > 0xFFFF) {
+               do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
+                               "for an unsigned 16-bit integer");
+               return;
+       }
+
+       ival = htons((uint16_t)lval);
+       memcpy(field, &ival, sizeof(ival));
+}
+static void from_zval_write_sa_family(const zval *arr_value, char *field, ser_context *ctx)
+{
+       long lval;
+       sa_family_t ival;
+
+       lval = from_zval_integer_common(arr_value, ctx);
+       if (ctx->err.has_error) {
+               return;
+       }
+
+       if (lval < 0 || lval > (sa_family_t)-1) { /* sa_family_t is unsigned */
+               do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
+                               "for a sa_family_t value");
+               return;
+       }
+
+       ival = (sa_family_t)lval;
+       memcpy(field, &ival, sizeof(ival));
+}
+static void from_zval_write_pid_t(const zval *arr_value, char *field, ser_context *ctx)
+{
+       long lval;
+       pid_t ival;
+
+       lval = from_zval_integer_common(arr_value, ctx);
+       if (ctx->err.has_error) {
+               return;
+       }
+
+       if (lval < 0 || (pid_t)lval != lval) { /* pid_t is signed */
+               do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
+                               "for a pid_t value");
+               return;
+       }
+
+       ival = (pid_t)lval;
+       memcpy(field, &ival, sizeof(ival));
+}
+static void from_zval_write_uid_t(const zval *arr_value, char *field, ser_context *ctx)
+{
+       long lval;
+       uid_t ival;
+
+       lval = from_zval_integer_common(arr_value, ctx);
+       if (ctx->err.has_error) {
+               return;
+       }
+
+       /* uid_t can be signed or unsigned (generally unsigned) */
+       if ((uid_t)-1 > (uid_t)0) {
+               if (sizeof(long) > sizeof(uid_t) && (lval < 0 || (uid_t)lval != lval)) {
+                       do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
+                                       "for a uid_t value");
+                       return;
+               }
+       } else {
+               if (sizeof(long) > sizeof(uid_t) && (uid_t)lval != lval) {
+                       do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
+                                       "for a uid_t value");
+                       return;
+               }
+       }
+
+       ival = (uid_t)lval;
+       memcpy(field, &ival, sizeof(ival));
+}
+
+void to_zval_read_int(const char *data, zval *zv, res_context *ctx)
+{
+       int ival;
+       memcpy(&ival, data, sizeof(ival));
+
+       ZVAL_LONG(zv, (long)ival);
+}
+static void to_zval_read_unsigned(const char *data, zval *zv, res_context *ctx)
+{
+       unsigned ival;
+       memcpy(&ival, data, sizeof(ival));
+
+       ZVAL_LONG(zv, (long)ival);
+}
+static void to_zval_read_net_uint16(const char *data, zval *zv, res_context *ctx)
+{
+       uint16_t ival;
+       memcpy(&ival, data, sizeof(ival));
+
+       ZVAL_LONG(zv, (long)ntohs(ival));
+}
+static void to_zval_read_uint32(const char *data, zval *zv, res_context *ctx)
+{
+       uint32_t ival;
+       memcpy(&ival, data, sizeof(ival));
+
+       ZVAL_LONG(zv, (long)ival);
+}
+static void to_zval_read_sa_family(const char *data, zval *zv, res_context *ctx)
+{
+       sa_family_t ival;
+       memcpy(&ival, data, sizeof(ival));
+
+       ZVAL_LONG(zv, (long)ival);
+}
+static void to_zval_read_pid_t(const char *data, zval *zv, res_context *ctx)
+{
+       pid_t ival;
+       memcpy(&ival, data, sizeof(ival));
+
+       ZVAL_LONG(zv, (long)ival);
+}
+static void to_zval_read_uid_t(const char *data, zval *zv, res_context *ctx)
+{
+       uid_t ival;
+       memcpy(&ival, data, sizeof(ival));
+
+       ZVAL_LONG(zv, (long)ival);
+}
+
+/* CONVERSIONS for sockaddr */
+static void from_zval_write_sin_addr(const zval *zaddr_str, char *inaddr, ser_context *ctx)
+{
+       int                                     res;
+       struct sockaddr_in      saddr = {0};
+       zval                            lzval = zval_used_for_init;
+       TSRMLS_FETCH();
+
+       if (Z_TYPE_P(zaddr_str) != IS_STRING) {
+               ZVAL_COPY_VALUE(&lzval, zaddr_str);
+               zval_copy_ctor(&lzval);
+               convert_to_string(&lzval);
+               zaddr_str = &lzval;
+       }
+
+       res = php_set_inet_addr(&saddr, Z_STRVAL_P(zaddr_str), ctx->sock TSRMLS_CC);
+       if (res) {
+               memcpy(inaddr, &saddr.sin_addr, sizeof saddr.sin_addr);
+       } else {
+               /* error already emitted, but let's emit another more relevant */
+               do_from_zval_err(ctx, "could not resolve address '%s' to get an AF_INET "
+                               "address", Z_STRVAL_P(zaddr_str));
+       }
+
+       zval_dtor(&lzval);
+}
+static void to_zval_read_sin_addr(const char *data, zval *zv, res_context *ctx)
+{
+       const struct in_addr *addr = (const struct in_addr *)data;
+       socklen_t size = INET_ADDRSTRLEN;
+
+       Z_TYPE_P(zv) = IS_STRING;
+       Z_STRVAL_P(zv) = ecalloc(1, size);
+       Z_STRLEN_P(zv) = 0;
+
+       if (inet_ntop(AF_INET, addr, Z_STRVAL_P(zv), size) == NULL) {
+               do_to_zval_err(ctx, "could not convert IPv4 address to string "
+                               "(errno %d)", errno);
+               return;
+       }
+
+       Z_STRLEN_P(zv) = strlen(Z_STRVAL_P(zv));
+}
+static const field_descriptor descriptors_sockaddr_in[] = {
+               {"family", sizeof("family"), 0, offsetof(struct sockaddr_in, sin_family), from_zval_write_sa_family, to_zval_read_sa_family},
+               {"addr", sizeof("addr"), 0, offsetof(struct sockaddr_in, sin_addr), from_zval_write_sin_addr, to_zval_read_sin_addr},
+               {"port", sizeof("port"), 0, offsetof(struct sockaddr_in, sin_port), from_zval_write_net_uint16, to_zval_read_net_uint16},
+               {0}
+};
+static void from_zval_write_sockaddr_in(const zval *container, char *sockaddr, ser_context *ctx)
+{
+       from_zval_write_aggregation(container, sockaddr, descriptors_sockaddr_in, ctx);
+}
+static void to_zval_read_sockaddr_in(const char *data, zval *zv, res_context *ctx)
+{
+       to_zval_read_aggregation(data, zv, descriptors_sockaddr_in, ctx);
+}
+static void from_zval_write_sin6_addr(const zval *zaddr_str, char *addr6, ser_context *ctx)
+{
+       int                                     res;
+       struct sockaddr_in6     saddr6 = {0};
+       zval                            lzval = zval_used_for_init;
+       TSRMLS_FETCH();
+
+       if (Z_TYPE_P(zaddr_str) != IS_STRING) {
+               ZVAL_COPY_VALUE(&lzval, zaddr_str);
+               zval_copy_ctor(&lzval);
+               convert_to_string(&lzval);
+               zaddr_str = &lzval;
+       }
+
+       res = php_set_inet6_addr(&saddr6,
+                       Z_STRVAL_P(zaddr_str), ctx->sock TSRMLS_CC);
+       if (res) {
+               memcpy(addr6, &saddr6.sin6_addr, sizeof saddr6.sin6_addr);
+       } else {
+               /* error already emitted, but let's emit another more relevant */
+               do_from_zval_err(ctx, "could not resolve address '%s' to get an AF_INET6 "
+                               "address", Z_STRVAL_P(zaddr_str));
+       }
+
+       zval_dtor(&lzval);
+}
+static void to_zval_read_sin6_addr(const char *data, zval *zv, res_context *ctx)
+{
+       const struct in6_addr *addr = (const struct in6_addr *)data;
+       socklen_t size = INET6_ADDRSTRLEN;
+
+       Z_TYPE_P(zv) = IS_STRING;
+       Z_STRVAL_P(zv) = ecalloc(1, size);
+       Z_STRLEN_P(zv) = 0;
+
+       if (inet_ntop(AF_INET6, addr, Z_STRVAL_P(zv), size) == NULL) {
+               do_to_zval_err(ctx, "could not convert IPv6 address to string "
+                               "(errno %d)", errno);
+               return;
+       }
+
+       Z_STRLEN_P(zv) = strlen(Z_STRVAL_P(zv));
+}
+static const field_descriptor descriptors_sockaddr_in6[] = {
+               {"family", sizeof("family"), 0, offsetof(struct sockaddr_in6, sin6_family), from_zval_write_sa_family, to_zval_read_sa_family},
+               {"addr", sizeof("addr"), 0, offsetof(struct sockaddr_in6, sin6_addr), from_zval_write_sin6_addr, to_zval_read_sin6_addr},
+               {"port", sizeof("port"), 0, offsetof(struct sockaddr_in6, sin6_port), from_zval_write_net_uint16, to_zval_read_net_uint16},
+               {"flowinfo", sizeof("flowinfo"), 0, offsetof(struct sockaddr_in6, sin6_flowinfo), from_zval_write_uint32, to_zval_read_uint32},
+               {"scope_id", sizeof("scope_id"), 0, offsetof(struct sockaddr_in6, sin6_scope_id), from_zval_write_uint32, to_zval_read_uint32},
+               {0}
+};
+static void from_zval_write_sockaddr_in6(const zval *container, char *sockaddr6, ser_context *ctx)
+{
+       from_zval_write_aggregation(container, sockaddr6, descriptors_sockaddr_in6, ctx);
+}
+static void to_zval_read_sockaddr_in6(const char *data, zval *zv, res_context *ctx)
+{
+       to_zval_read_aggregation(data, zv, descriptors_sockaddr_in6, ctx);
+}
+static void from_zval_write_sun_path(const zval *path, char *sockaddr_un_c, ser_context *ctx)
+{
+       zval                            lzval = zval_used_for_init;
+       struct sockaddr_un      *saddr = (struct sockaddr_un*)sockaddr_un_c;
+
+       if (Z_TYPE_P(path) != IS_STRING) {
+               ZVAL_COPY_VALUE(&lzval, path);
+               zval_copy_ctor(&lzval);
+               convert_to_string(&lzval);
+               path = &lzval;
+       }
+
+       if (Z_STRLEN_P(path) >= sizeof(saddr->sun_path)) {
+               do_from_zval_err(ctx, "the path is too long, the maximum permitted "
+                               "length is %ld", sizeof(saddr->sun_path) - 1);
+               return;
+       }
+
+       memcpy(&saddr->sun_path, Z_STRVAL_P(path), Z_STRLEN_P(path));
+       saddr->sun_path[Z_STRLEN_P(path)] = '\0';
+
+       zval_dtor(&lzval);
+}
+static void to_zval_read_sun_path(const char *data, zval *zv, res_context *ctx) {
+       struct sockaddr_un      *saddr = (struct sockaddr_un*)data;
+       char *nul_pos;
+
+       nul_pos = memchr(&saddr->sun_path, '\0', sizeof(saddr->sun_path));
+       if (nul_pos == NULL) {
+               do_to_zval_err(ctx, "could not find a NUL in the path");
+               return;
+       }
+
+       ZVAL_STRINGL(zv, saddr->sun_path, nul_pos - (char*)&saddr->sun_path, 1);
+}
+static const field_descriptor descriptors_sockaddr_un[] = {
+               {"family", sizeof("family"), 0, offsetof(struct sockaddr_un, sun_family), from_zval_write_sa_family, to_zval_read_sa_family},
+               {"path", sizeof("path"), 0, 0, from_zval_write_sun_path, to_zval_read_sun_path},
+               {0}
+};
+static void from_zval_write_sockaddr_un(const zval *container, char *sockaddr, ser_context *ctx)
+{
+       from_zval_write_aggregation(container, sockaddr, descriptors_sockaddr_un, ctx);
+}
+static void to_zval_read_sockaddr_un(const char *data, zval *zv, res_context *ctx)
+{
+       to_zval_read_aggregation(data, zv, descriptors_sockaddr_un, ctx);
+}
+static void from_zval_write_sockaddr_aux(const zval *container,
+                                                                                struct sockaddr **sockaddr_ptr,
+                                                                                socklen_t *sockaddr_len,
+                                                                                ser_context *ctx)
+{
+       int             family;
+       zval    **elem;
+       int             fill_sockaddr;
+
+       if (Z_TYPE_P(container) != IS_ARRAY) {
+               do_from_zval_err(ctx, "%s", "expected an array here");
+               return;
+       }
+
+       fill_sockaddr = param_get_bool(ctx, KEY_FILL_SOCKADDR, 1);
+
+       if (zend_hash_find(Z_ARRVAL_P(container), "family", sizeof("family"), (void**)&elem) == SUCCESS
+                       && Z_TYPE_PP(elem) != IS_NULL) {
+               const char *node = "family";
+               zend_llist_add_element(&ctx->keys, &node);
+               from_zval_write_int(*elem, (char*)&family, ctx);
+               zend_llist_remove_tail(&ctx->keys);
+       } else {
+               family = ctx->sock->type;
+       }
+
+       switch (family) {
+       case AF_INET:
+               /* though not all OSes support sockaddr_in used in IPv6 sockets */
+               if (ctx->sock->type != AF_INET && ctx->sock->type != AF_INET6) {
+                       do_from_zval_err(ctx, "the specified family (number %d) is not "
+                                       "supported on this socket", family);
+                       return;
+               }
+               *sockaddr_ptr = accounted_ecalloc(1, sizeof(struct sockaddr_in), ctx);
+               *sockaddr_len = sizeof(struct sockaddr_in);
+               if (fill_sockaddr) {
+                       from_zval_write_sockaddr_in(container, (char*)*sockaddr_ptr, ctx);
+                       (*sockaddr_ptr)->sa_family = AF_INET;
+               }
+               break;
+
+       case AF_INET6:
+               if (ctx->sock->type != AF_INET6) {
+                       do_from_zval_err(ctx, "the specified family (AF_INET6) is not "
+                                       "supported on this socket");
+                       return;
+               }
+               *sockaddr_ptr = accounted_ecalloc(1, sizeof(struct sockaddr_in6), ctx);
+               *sockaddr_len = sizeof(struct sockaddr_in6);
+               if (fill_sockaddr) {
+                       from_zval_write_sockaddr_in6(container, (char*)*sockaddr_ptr, ctx);
+                       (*sockaddr_ptr)->sa_family = AF_INET6;
+               }
+               break;
+
+       case AF_UNIX:
+               if (ctx->sock->type != AF_UNIX) {
+                       do_from_zval_err(ctx, "the specified family (AF_UNIX) is not "
+                                       "supported on this socket");
+                       return;
+               }
+               *sockaddr_ptr = accounted_ecalloc(1, sizeof(struct sockaddr_un), ctx);
+               *sockaddr_len = sizeof(struct sockaddr_un);
+               if (fill_sockaddr) {
+                       from_zval_write_sockaddr_un(container, (char*)*sockaddr_ptr, ctx);
+                       (*sockaddr_ptr)->sa_family = AF_UNIX;
+               }
+               break;
+
+       default:
+               do_from_zval_err(ctx, "%s", "the only families currently supported are "
+                               "AF_INET, AF_INET6 and AF_UNIX");
+               break;
+       }
+}
+static void to_zval_read_sockaddr_aux(const char *sockaddr_c, zval *zv, res_context *ctx)
+{
+       const struct sockaddr *saddr = (struct sockaddr *)sockaddr_c;
+
+       if (saddr->sa_family == 0) {
+               ZVAL_NULL(zv);
+               return;
+       }
+
+       array_init(zv);
+
+       switch (saddr->sa_family) {
+       case AF_INET:
+               to_zval_read_sockaddr_in(sockaddr_c, zv, ctx);
+               break;
+
+       case AF_INET6:
+               to_zval_read_sockaddr_in6(sockaddr_c, zv, ctx);
+               break;
+
+       case AF_UNIX:
+               to_zval_read_sockaddr_un(sockaddr_c, zv, ctx);
+               break;
+
+       default:
+               do_to_zval_err(ctx, "cannot read struct sockaddr with family %d; "
+                               "not supported",
+                               (int)saddr->sa_family);
+               break;
+       }
+}
+
+/* CONVERSIONS for cmsghdr */
+/*
+ * [ level => , type => , data => [],]
+ * struct cmsghdr {
+ *  socklen_t cmsg_len;    // data byte count, including header
+ *  int       cmsg_level;  // originating protocol
+ *  int       cmsg_type;   // protocol-specific type
+ *  // followed by unsigned char cmsg_data[];
+ * };
+ */
+static void from_zval_write_control(const zval                 *arr,
+                                                                       void                            **control_buf,
+                                                                       zend_llist_element      *alloc,
+                                                                       size_t                          *control_len,
+                                                                       size_t                          *offset,
+                                                                       ser_context                     *ctx)
+{
+       struct cmsghdr          *cmsghdr;
+       int                                     level,
+                                               type;
+       size_t                          data_len,
+                                               req_space,
+                                               space_left;
+       ancillary_reg_entry     *entry;
+
+       static const field_descriptor descriptor_level[] = {
+                       {"level", sizeof("level"), 0, 0, from_zval_write_int, 0},
+                       {0}
+       };
+       static const field_descriptor descriptor_type[] = {
+                       {"type", sizeof("type"), 0, 0, from_zval_write_int, 0},
+                       {0}
+       };
+       field_descriptor descriptor_data[] = {
+                       {"data", sizeof("data"), 0, 0, 0, 0},
+                       {0}
+       };
+
+       from_zval_write_aggregation(arr, (char *)&level, descriptor_level, ctx);
+       if (ctx->err.has_error) {
+               return;
+       }
+       from_zval_write_aggregation(arr, (char *)&type, descriptor_type, ctx);
+       if (ctx->err.has_error) {
+               return;
+       }
+
+       entry = get_ancillary_reg_entry(level, type);
+       if (entry == NULL) {
+               do_from_zval_err(ctx, "cmsghdr with level %d and type %d not supported",
+                               level, type);
+               return;
+       }
+
+       if (entry->calc_space) {
+               data_len = entry->calc_space(arr, ctx);
+               if (ctx->err.has_error) {
+                       return;
+               }
+       } else {
+               data_len = entry->size;
+       }
+       req_space = CMSG_SPACE(data_len);
+       space_left = *control_len - *offset;
+       assert(*control_len >= *offset);
+
+       if (space_left < req_space) {
+               *control_buf = safe_erealloc(*control_buf, 2, req_space, *control_len);
+               *control_len += 2 * req_space;
+               memcpy(&alloc->data, *control_buf, sizeof *control_buf);
+       }
+
+       cmsghdr = (struct cmsghdr*)(((char*)*control_buf) + *offset);
+       cmsghdr->cmsg_level     = level;
+       cmsghdr->cmsg_type      = type;
+       cmsghdr->cmsg_len       = CMSG_LEN(data_len);
+
+       descriptor_data[0].from_zval = entry->from_array;
+       from_zval_write_aggregation(arr, (char*)CMSG_DATA(cmsghdr), descriptor_data, ctx);
+
+       *offset += req_space;
+}
+static void from_zval_write_control_array(const zval *arr, char *msghdr_c, ser_context *ctx)
+{
+       HashPosition            pos;
+       char                            buf[sizeof("element #4294967295")];
+       char                            *bufp = buf;
+       zval                            **elem;
+       uint32_t                        i;
+       int                                     num_elems;
+       void                            *control_buf;
+       zend_llist_element      *alloc;
+       size_t                          control_len,
+                                               cur_offset;
+       struct msghdr           *msg = (struct msghdr*)msghdr_c;
+
+       if (Z_TYPE_P(arr) != IS_ARRAY) {
+               do_from_zval_err(ctx, "%s", "expected an array here");
+               return;
+       }
+
+       num_elems = zend_hash_num_elements(Z_ARRVAL_P(arr));
+       if (num_elems == 0) {
+               return;
+       }
+
+       /* estimate each message at 20 bytes */
+       control_buf     = accounted_safe_ecalloc(num_elems, CMSG_SPACE(20), 0, ctx);
+       alloc           = ctx->allocations.tail;
+       control_len = (size_t)num_elems * CMSG_SPACE(20);
+       cur_offset      = 0;
+
+    for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(arr), &pos), i = 0;
+                       !ctx->err.has_error
+                       && zend_hash_get_current_data_ex(Z_ARRVAL_P(arr), (void **)&elem, &pos) == SUCCESS;
+                       zend_hash_move_forward_ex(Z_ARRVAL_P(arr), &pos)) {
+
+               if (snprintf(buf, sizeof(buf), "element #%u", (unsigned)i++) >= sizeof(buf)) {
+                       memcpy(buf, "element", sizeof("element"));
+               }
+               zend_llist_add_element(&ctx->keys, &bufp);
+
+               from_zval_write_control(*elem, &control_buf, alloc, &control_len,
+                               &cur_offset, ctx);
+
+               zend_llist_remove_tail(&ctx->keys);
+    }
+
+    msg->msg_control = control_buf;
+    msg->msg_controllen = cur_offset; /* not control_len, which may be larger */
+}
+static void to_zval_read_cmsg_data(const char *cmsghdr_c, zval *zv, res_context *ctx)
+{
+       const struct cmsghdr    *cmsg = (const struct cmsghdr *)cmsghdr_c;
+       ancillary_reg_entry             *entry;
+       size_t                                  len,
+                                                       *len_p = &len;
+
+       entry = get_ancillary_reg_entry(cmsg->cmsg_level, cmsg->cmsg_type);
+       if (entry == NULL) {
+               do_to_zval_err(ctx, "cmsghdr with level %d and type %d not supported",
+                               cmsg->cmsg_level, cmsg->cmsg_type);
+               return;
+       }
+       if (CMSG_LEN(entry->size) > cmsg->cmsg_len) {
+               do_to_zval_err(ctx, "the cmsghdr structure is unexpectedly small; "
+                               "expected a length of at least %ld, but got %ld",
+                               (long)CMSG_LEN(entry->size), (long)cmsg->cmsg_len);
+               return;
+       }
+
+       len = (size_t)cmsg->cmsg_len; /* use another var because type of cmsg_len varies */
+       if (zend_hash_add(&ctx->params, KEY_CMSG_LEN, sizeof(KEY_CMSG_LEN),
+                       &len_p, sizeof(len_p), NULL) == FAILURE) {
+               do_to_zval_err(ctx, "%s", "could not set parameter " KEY_CMSG_LEN);
+               return;
+       }
+
+       entry->to_array((const char *)CMSG_DATA(cmsg), zv, ctx);
+
+       zend_hash_del(&ctx->params, KEY_CMSG_LEN, sizeof(KEY_CMSG_LEN));
+}
+static void to_zval_read_control(const char *cmsghdr_c, zval *zv, res_context *ctx)
+{
+       /* takes a cmsghdr, not a msghdr like from_zval_write_control */
+       static const field_descriptor descriptors[] = {
+                       {"level", sizeof("level"), 0, offsetof(struct cmsghdr, cmsg_level), 0, to_zval_read_int},
+                       {"type", sizeof("type"), 0, offsetof(struct cmsghdr, cmsg_type), 0, to_zval_read_int},
+                       {"data", sizeof("data"), 0, 0 /* cmsghdr passed */, 0, to_zval_read_cmsg_data},
+                       {0}
+       };
+
+       array_init_size(zv, 3);
+       to_zval_read_aggregation(cmsghdr_c, zv, descriptors, ctx);
+}
+static void to_zval_read_control_array(const char *msghdr_c, zval *zv, res_context *ctx)
+{
+       struct msghdr   *msg = (struct msghdr *)msghdr_c;
+       struct cmsghdr  *cmsg;
+       char                    buf[sizeof("element #4294967295")];
+       char                    *bufp = buf;
+       uint32_t                i = 1;
+
+       /*if (msg->msg_flags & MSG_CTRUNC) {
+               php_error_docref0(NULL, E_WARNING, "The MSG_CTRUNC flag is present; will not "
+                               "attempt to read control messages");
+               ZVAL_FALSE(zv);
+               return;
+       }*/
+
+       array_init(zv);
+
+       for (cmsg = CMSG_FIRSTHDR(msg);
+                       cmsg != NULL && !ctx->err.has_error;
+                       cmsg = CMSG_NXTHDR(msg,cmsg)) {
+               zval *elem;
+
+               ALLOC_INIT_ZVAL(elem);
+               add_next_index_zval(zv, elem);
+
+               if (snprintf(buf, sizeof(buf), "element #%u", (unsigned)i++) >= sizeof(buf)) {
+                       memcpy(buf, "element", sizeof("element"));
+               }
+               zend_llist_add_element(&ctx->keys, &bufp);
+
+               to_zval_read_control((const char *)cmsg, elem, ctx);
+
+               zend_llist_remove_tail(&ctx->keys);
+       }
+}
+
+/* CONVERSIONS for msghdr */
+static void from_zval_write_name(const zval *zname_arr, char *msghdr_c, ser_context *ctx)
+{
+       struct sockaddr *sockaddr;
+       socklen_t               sockaddr_len;
+       struct msghdr   *msghdr = (struct msghdr *)msghdr_c;
+
+       from_zval_write_sockaddr_aux(zname_arr, &sockaddr, &sockaddr_len, ctx);
+
+       msghdr->msg_name = sockaddr;
+       msghdr->msg_namelen = sockaddr_len;
+}
+static void to_zval_read_name(const char *sockaddr_p, zval *zv, res_context *ctx)
+{
+       void *name = (void*)*(void**)sockaddr_p;
+       if (name == NULL) {
+               ZVAL_NULL(zv);
+       } else {
+               to_zval_read_sockaddr_aux(name, zv, ctx);
+       }
+}
+static void from_zval_write_msghdr_buffer_size(const zval *elem, char *msghdr_c, ser_context *ctx)
+{
+       long lval;
+       struct msghdr *msghdr = (struct msghdr *)msghdr_c;
+
+       lval = from_zval_integer_common(elem, ctx);
+       if (ctx->err.has_error) {
+               return;
+       }
+
+       if (lval < 0 || lval > MAX_USER_BUFF_SIZE) {
+               do_from_zval_err(ctx, "the buffer size must be between 1 and %ld; "
+                               "given %ld", (long)MAX_USER_BUFF_SIZE, lval);
+               return;
+       }
+
+       msghdr->msg_iovlen = 1;
+       msghdr->msg_iov = accounted_emalloc(sizeof(*msghdr->msg_iov) * 1, ctx);
+       msghdr->msg_iov[0].iov_base = accounted_emalloc((size_t)lval, ctx);
+       msghdr->msg_iov[0].iov_len = (size_t)lval;
+}
+static void from_zval_write_iov_array_aux(zval **elem, unsigned i, void **args, ser_context *ctx)
+{
+       struct msghdr   *msg = args[0];
+       size_t                  len;
+
+       zval_add_ref(elem);
+       convert_to_string_ex(elem);
+
+       len = Z_STRLEN_PP(elem);
+       msg->msg_iov[i - 1].iov_base = accounted_emalloc(len, ctx);
+       msg->msg_iov[i - 1].iov_len = len;
+       memcpy(msg->msg_iov[i - 1].iov_base, Z_STRVAL_PP(elem), len);
+
+       zval_ptr_dtor(elem);
+}
+static void from_zval_write_iov_array(const zval *arr, char *msghdr_c, ser_context *ctx)
+{
+       int                             num_elem;
+       struct msghdr   *msg = (struct msghdr*)msghdr_c;
+
+       if (Z_TYPE_P(arr) != IS_ARRAY) {
+               do_from_zval_err(ctx, "%s", "expected an array here");
+               return;
+       }
+
+       num_elem = zend_hash_num_elements(Z_ARRVAL_P(arr));
+       if (num_elem == 0) {
+               return;
+       }
+
+       msg->msg_iov = accounted_safe_ecalloc(num_elem, sizeof *msg->msg_iov, 0, ctx);
+       msg->msg_iovlen = (size_t)num_elem;
+
+    from_array_iterate(arr, from_zval_write_iov_array_aux, (void**)&msg, ctx);
+}
+static void from_zval_write_controllen(const zval *elem, char *msghdr_c, ser_context *ctx)
+{
+       struct msghdr *msghdr = (struct msghdr *)msghdr_c;
+       uint32_t len;
+
+       /* controllen should be an unsigned with at least 32-bit. Let's assume
+        * this least common denominator
+        */
+       from_zval_write_uint32(elem, (char*)&len, ctx);
+       if (!ctx->err.has_error && len == 0) {
+               do_from_zval_err(ctx, "controllen cannot be 0");
+               return;
+       }
+       msghdr->msg_control = accounted_emalloc(len, ctx);
+       msghdr->msg_controllen = len;
+}
+void from_zval_write_msghdr_send(const zval *container, char *msghdr_c, ser_context *ctx)
+{
+       static const field_descriptor descriptors[] = {
+                       {"name", sizeof("name"), 0, 0, from_zval_write_name, 0},
+                       {"iov", sizeof("iov"), 0, 0, from_zval_write_iov_array, 0},
+                       {"control", sizeof("control"), 0, 0, from_zval_write_control_array, 0},
+                       {0}
+       };
+
+       from_zval_write_aggregation(container, msghdr_c, descriptors, ctx);
+}
+void from_zval_write_msghdr_recv(const zval *container, char *msghdr_c, ser_context *ctx)
+{
+       /* zval to struct msghdr, version for recvmsg(). It differs from the version
+        * for sendmsg() in that it:
+        *      - has a buffer_size instead of an iov array;
+        *      - has no control element; has a controllen element instead
+        * struct msghdr {
+        *    void *msg_name;
+        *    socklen_t msg_namelen;
+        *    struct iovec *msg_iov;
+        *    size_t msg_iovlen;
+        *    void *msg_control;
+        *    size_t msg_controllen; //can also be socklen_t
+        *    int msg_flags;
+        * };
+        */
+       static const field_descriptor descriptors[] = {
+                       {"name", sizeof("name"), 0, 0, from_zval_write_name, 0},
+                       {"buffer_size", sizeof("buffer_size"), 0, 0, from_zval_write_msghdr_buffer_size, 0},
+                       {"controllen", sizeof("controllen"), 1, 0, from_zval_write_controllen, 0},
+                       {0}
+       };
+       struct msghdr   *msghdr = (struct msghdr *)msghdr_c;
+       const int               falsev = 0,
+                                       *falsevp = &falsev;
+
+       if (zend_hash_add(&ctx->params, KEY_FILL_SOCKADDR, sizeof(KEY_FILL_SOCKADDR),
+                       (void*)&falsevp, sizeof(falsevp), NULL) == FAILURE) {
+               do_from_zval_err(ctx, "could not add fill_sockaddr; this is a bug");
+               return;
+       }
+
+       from_zval_write_aggregation(container, msghdr_c, descriptors, ctx);
+
+       zend_hash_del(&ctx->params, KEY_FILL_SOCKADDR, sizeof(KEY_FILL_SOCKADDR));
+       if (ctx->err.has_error) {
+               return;
+       }
+
+       if (msghdr->msg_iovlen == 0) {
+               msghdr->msg_iovlen = 1;
+               msghdr->msg_iov = accounted_emalloc(sizeof(*msghdr->msg_iov) * 1, ctx);
+               msghdr->msg_iov[0].iov_base = accounted_emalloc((size_t)DEFAULT_BUFF_SIZE, ctx);
+               msghdr->msg_iov[0].iov_len = (size_t)DEFAULT_BUFF_SIZE;
+       }
+}
+
+static void to_zval_read_iov(const char *msghdr_c, zval *zv, res_context *ctx)
+{
+       const struct msghdr     *msghdr = (const struct msghdr *)msghdr_c;
+       size_t                          iovlen = msghdr->msg_iovlen;
+       ssize_t                         **recvmsg_ret,
+                                               bytes_left;
+       uint                            i;
+
+       if (iovlen > UINT_MAX) {
+               do_to_zval_err(ctx, "unexpectedly large value for iov_len: %lu",
+                               (unsigned long)iovlen);
+       }
+       array_init_size(zv, (uint)iovlen);
+
+       if (zend_hash_find(&ctx->params, KEY_RECVMSG_RET, sizeof(KEY_RECVMSG_RET),
+                       (void**)&recvmsg_ret) == FAILURE) {
+               do_to_zval_err(ctx, "recvmsg_ret not found in params. This is a bug");
+               return;
+       }
+       bytes_left = **recvmsg_ret;
+
+       for (i = 0; bytes_left > 0 && i < (uint)iovlen; i++) {
+               zval    *elem;
+               size_t  len             = MIN(msghdr->msg_iov[i].iov_len, bytes_left);
+               char    *buf    = safe_emalloc(1, len, 1);
+
+               MAKE_STD_ZVAL(elem);
+               memcpy(buf, msghdr->msg_iov[i].iov_base, len);
+               buf[len] = '\0';
+
+               ZVAL_STRINGL(elem, buf, len, 0);
+               add_next_index_zval(zv, elem);
+               bytes_left -= len;
+       }
+}
+void to_zval_read_msghdr(const char *msghdr_c, zval *zv, res_context *ctx)
+{
+       static const field_descriptor descriptors[] = {
+                       {"name", sizeof("name"), 0, offsetof(struct msghdr, msg_name), 0, to_zval_read_name},
+                       {"control", sizeof("control"), 0, 0, 0, to_zval_read_control_array},
+                       {"iov", sizeof("iov"), 0, 0, 0, to_zval_read_iov},
+                       {"flags", sizeof("flags"), 0, offsetof(struct msghdr, msg_flags), 0, to_zval_read_int},
+                       {0}
+       };
+
+       array_init_size(zv, 4);
+
+       to_zval_read_aggregation(msghdr_c, zv, descriptors, ctx);
+}
+
+/* CONVERSIONS for if_index */
+static void from_zval_write_ifindex(const zval *zv, char *uinteger, ser_context *ctx)
+{
+       zval *va; unsigned *out;
+       unsigned ret;
+       zval                            lzval = zval_used_for_init;
+
+       if (Z_TYPE_P(zv) == IS_LONG) {
+               if (Z_LVAL_P(zv) < 0 || Z_LVAL_P(zv) > UINT_MAX) {
+                       do_from_zval_err(ctx, "the interface index cannot be negative or "
+                                       "larger than %u; given %ld", UINT_MAX, Z_LVAL_P(zv));
+               } else {
+                       ret = (unsigned)Z_LVAL_P(zv);
+               }
+       } else {
+#if HAVE_IF_NAMETOINDEX
+
+               if (Z_TYPE_P(zv) != IS_STRING) {
+                       ZVAL_COPY_VALUE(&lzval, zv);
+                       zval_copy_ctor(&lzval);
+                       convert_to_string(&lzval);
+                       zv = &lzval;
+               }
+
+               ret = if_nametoindex(Z_STRVAL_P(zv));
+               if (ret == 0) {
+                       do_from_zval_err(ctx, "no interface with name \"%s\" could be "
+                                       "found", Z_STRVAL_P(zv));
+               }
+#else
+               do_from_zval_err(ctx,
+                               "this platform does not support looking up an interface by "
+                               "name, an integer interface index must be supplied instead");
+#endif
+       }
+
+       if (!ctx->err.has_error) {
+               memcpy(uinteger, &ret, sizeof(ret));
+       }
+
+       zval_dtor(&lzval);
+}
+
+/* CONVERSIONS for struct in6_pktinfo */
+#ifdef IPV6_PKTINFO
+static const field_descriptor descriptors_in6_pktinfo[] = {
+               {"addr", sizeof("addr"), 1, offsetof(struct in6_pktinfo, ipi6_addr), from_zval_write_sin6_addr, to_zval_read_sin6_addr},
+               {"ifindex", sizeof("ifindex"), 1, offsetof(struct in6_pktinfo, ipi6_ifindex), from_zval_write_unsigned, to_zval_read_unsigned},
+               {0}
+};
+void from_zval_write_in6_pktinfo(const zval *container, char *in6_pktinfo_c, ser_context *ctx)
+{
+       from_zval_write_aggregation(container, in6_pktinfo_c, descriptors_in6_pktinfo, ctx);
+}
+void to_zval_read_in6_pktinfo(const char *data, zval *zv, res_context *ctx)
+{
+       array_init_size(zv, 2);
+
+       to_zval_read_aggregation(data, zv, descriptors_in6_pktinfo, ctx);
+}
+#endif
+
+/* CONVERSIONS for struct ucred */
+#ifdef SO_PASSCRED
+static const field_descriptor descriptors_ucred[] = {
+               {"pid", sizeof("pid"), 1, offsetof(struct ucred, pid), from_zval_write_pid_t, to_zval_read_pid_t},
+               {"uid", sizeof("uid"), 1, offsetof(struct ucred, uid), from_zval_write_uid_t, to_zval_read_uid_t},
+               /* assume the type gid_t is the same as uid_t: */
+               {"gid", sizeof("gid"), 1, offsetof(struct ucred, gid), from_zval_write_uid_t, to_zval_read_uid_t},
+               {0}
+};
+void from_zval_write_ucred(const zval *container, char *ucred_c, ser_context *ctx)
+{
+       from_zval_write_aggregation(container, ucred_c, descriptors_ucred, ctx);
+}
+void to_zval_read_ucred(const char *data, zval *zv, res_context *ctx)
+{
+       array_init_size(zv, 3);
+
+       to_zval_read_aggregation(data, zv, descriptors_ucred, ctx);
+}
+#endif
+
+/* CONVERSIONS for SCM_RIGHTS */
+#ifdef SCM_RIGHTS
+size_t calculate_scm_rights_space(const zval *arr, ser_context *ctx)
+{
+       int num_elems;
+
+       if (Z_TYPE_P(arr) != IS_ARRAY) {
+               do_from_zval_err(ctx, "%s", "expected an array here");
+               return (size_t)-1;
+       }
+
+       num_elems = zend_hash_num_elements(Z_ARRVAL_P(arr));
+       if (num_elems == 0) {
+               do_from_zval_err(ctx, "%s", "expected at least one element in this array");
+               return (size_t)-1;
+       }
+
+       return zend_hash_num_elements(Z_ARRVAL_P(arr)) * sizeof(int);
+}
+static void from_zval_write_fd_array_aux(zval **elem, unsigned i, void **args, ser_context *ctx)
+{
+       int *iarr = args[0];
+
+       if (Z_TYPE_PP(elem) == IS_RESOURCE) {
+               php_stream *stream;
+               php_socket *sock;
+
+               ZEND_FETCH_RESOURCE_NO_RETURN(sock, php_socket *, elem, -1,
+                               NULL, php_sockets_le_socket());
+               if (sock) {
+                       iarr[i] = sock->bsd_socket;
+                       return;
+               }
+
+               ZEND_FETCH_RESOURCE2_NO_RETURN(stream, php_stream *, elem, -1,
+                               NULL, php_file_le_stream(), php_file_le_pstream());
+               if (stream == NULL) {
+                       do_from_zval_err(ctx, "resource is not a stream or a socket");
+                       return;
+               }
+
+               if (php_stream_cast(stream, PHP_STREAM_AS_FD, (void **)&iarr[i],
+                               REPORT_ERRORS) == FAILURE) {
+                       do_from_zval_err(ctx, "cast stream to file descriptor failed");
+                       return;
+               }
+       } else {
+               do_from_zval_err(ctx, "expected a resource variable");
+       }
+}
+void from_zval_write_fd_array(const zval *arr, char *int_arr, ser_context *ctx)
+{
+       if (Z_TYPE_P(arr) != IS_ARRAY) {
+               do_from_zval_err(ctx, "%s", "expected an array here");
+               return;
+       }
+
+   from_array_iterate(arr, &from_zval_write_fd_array_aux, (void**)&int_arr, ctx);
+}
+void to_zval_read_fd_array(const char *data, zval *zv, res_context *ctx)
+{
+       size_t                  **cmsg_len;
+       int                             num_elems,
+                                       i;
+       struct cmsghdr  *dummy_cmsg = 0;
+       size_t                  data_offset;
+       TSRMLS_FETCH();
+
+       data_offset = (unsigned char *)CMSG_DATA(dummy_cmsg)
+                       - (unsigned char *)dummy_cmsg;
+
+       if (zend_hash_find(&ctx->params, KEY_CMSG_LEN, sizeof(KEY_CMSG_LEN),
+                       (void **)&cmsg_len) == FAILURE) {
+               do_to_zval_err(ctx, "could not get value of parameter " KEY_CMSG_LEN);
+               return;
+       }
+
+       if (**cmsg_len < data_offset) {
+               do_to_zval_err(ctx, "length of cmsg is smaller than its data member "
+                               "offset (%ld vs %ld)", (long)**cmsg_len, (long)data_offset);
+               return;
+       }
+       num_elems = (**cmsg_len - data_offset) / sizeof(int);
+
+       array_init_size(zv, num_elems);
+
+       for (i = 0; i < num_elems; i++) {
+               zval            *elem;
+               int                     fd;
+               struct stat     statbuf;
+
+               MAKE_STD_ZVAL(elem);
+
+               fd = *((int *)data + i);
+
+               /* determine whether we have a socket */
+               if (fstat(fd, &statbuf) == -1) {
+                       do_to_zval_err(ctx, "error creating resource for received file "
+                                       "descriptor %d: fstat() call failed with errno %d", fd, errno);
+                       efree(elem);
+                       return;
+               }
+               if (S_ISSOCK(statbuf.st_mode)) {
+                       php_socket *sock = socket_import_file_descriptor(fd);
+                       zend_register_resource(elem, sock, php_sockets_le_socket());
+               } else {
+                       php_stream *stream = php_stream_fopen_from_fd(fd, "rw", NULL);
+                       php_stream_to_zval(stream, elem);
+               }
+
+               add_next_index_zval(zv, elem);
+       }
+}
+#endif
+
+/* ENTRY POINT for conversions */
+static void free_from_zval_allocation(void *alloc_ptr_ptr)
+{
+       efree(*(void**)alloc_ptr_ptr);
+}
+void *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 /* out */,
+                                                               struct err_s                    *err /* in/out */)
+{
+       ser_context ctx = {{0}};
+       char *structure = NULL;
+
+       *allocations = NULL;
+
+       if (err->has_error) {
+               return NULL;
+       }
+
+       zend_hash_init(&ctx.params, 8, NULL, NULL, 0);
+       zend_llist_init(&ctx.keys, sizeof(const char *), NULL, 0);
+       zend_llist_init(&ctx.allocations, sizeof(void *), &free_from_zval_allocation, 0);
+       ctx.sock = sock;
+
+       structure = ecalloc(1, struct_size);
+
+       zend_llist_add_element(&ctx.keys, &top_name);
+       zend_llist_add_element(&ctx.allocations, &structure);
+
+       /* main call */
+       writer(container, structure, &ctx);
+
+       if (ctx.err.has_error) {
+               zend_llist_destroy(&ctx.allocations); /* deallocates structure as well */
+               structure = NULL;
+               *err = ctx.err;
+       } else {
+               *allocations = emalloc(sizeof **allocations);
+               **allocations = ctx.allocations;
+       }
+
+       zend_llist_destroy(&ctx.keys);
+       zend_hash_destroy(&ctx.params);
+
+       return structure;
+}
+zval *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)
+{
+       res_context                             ctx = {{0}, {0}};
+       const struct key_value  *kv;
+       zval                                    *zv = NULL;
+
+       if (err->has_error) {
+               return NULL;
+       }
+
+       ALLOC_INIT_ZVAL(zv);
+
+       zend_llist_init(&ctx.keys, sizeof(const char *), NULL, 0);
+       zend_llist_add_element(&ctx.keys, &top_name);
+
+       zend_hash_init(&ctx.params, 8, NULL, NULL, 0);
+       for (kv = key_value_pairs; kv->key != NULL; kv++) {
+               zend_hash_update(&ctx.params, kv->key, kv->key_size,
+                               (void*)&kv->value, sizeof(kv->value), NULL);
+       }
+
+       /* main call */
+       reader(structure, zv, &ctx);
+
+       if (ctx.err.has_error) {
+               zval_ptr_dtor(&zv);
+               zv = NULL;
+               *err = ctx.err;
+       }
+
+       zend_llist_destroy(&ctx.keys);
+       zend_hash_destroy(&ctx.params);
+
+       return zv;
+}
diff --git a/ext/sockets/conversions.h b/ext/sockets/conversions.h
new file mode 100644 (file)
index 0000000..70f31ba
--- /dev/null
@@ -0,0 +1,78 @@
+#ifndef PHP_SOCK_CONVERSIONS_H
+#define PHP_SOCK_CONVERSIONS_H 1
+
+#include <php.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include "php_sockets.h"
+
+/* TYPE DEFINITIONS */
+struct err_s {
+       int             has_error;
+       char    *msg;
+       int             level;
+       int             should_free;
+};
+
+struct key_value {
+       const char      *key;
+       unsigned        key_size;
+       void            *value;
+};
+
+/* the complete types of these two are not relevant to the outside */
+typedef struct _ser_context ser_context;
+typedef struct _res_context res_context;
+
+#define KEY_RECVMSG_RET "recvmsg_ret"
+
+typedef void (from_zval_write_field)(const zval *arr_value, char *field, ser_context *ctx);
+typedef void (to_zval_read_field)(const char *data, zval *zv, res_context *ctx);
+
+/* VARIABLE DECLARATIONS */
+extern const struct key_value empty_key_value_list[];
+
+/* AUX FUNCTIONS */
+void err_msg_dispose(struct err_s *err TSRMLS_DC);
+void allocations_dispose(zend_llist **allocations);
+
+/* CONVERSION FUNCTIONS */
+void from_zval_write_int(const zval *arr_value, char *field, ser_context *ctx);
+void to_zval_read_int(const char *data, zval *zv, res_context *ctx);
+
+#ifdef IPV6_PKTINFO
+void from_zval_write_in6_pktinfo(const zval *container, char *in6_pktinfo_c, ser_context *ctx);
+void to_zval_read_in6_pktinfo(const char *data, zval *zv, res_context *ctx);
+#endif
+
+#ifdef SO_PASSCRED
+void from_zval_write_ucred(const zval *container, char *ucred_c, ser_context *ctx);
+void to_zval_read_ucred(const char *data, zval *zv, res_context *ctx);
+#endif
+
+#ifdef SCM_RIGHTS
+size_t calculate_scm_rights_space(const zval *arr, ser_context *ctx);
+void from_zval_write_fd_array(const zval *arr, char *int_arr, ser_context *ctx);
+void to_zval_read_fd_array(const char *data, zval *zv, res_context *ctx);
+#endif
+
+void from_zval_write_msghdr_send(const zval *container, char *msghdr_c, ser_context *ctx);
+void from_zval_write_msghdr_recv(const zval *container, char *msghdr_c, ser_context *ctx);
+void to_zval_read_msghdr(const char *msghdr_c, zval *zv, res_context *ctx);
+
+/* ENTRY POINTS FOR CONVERSIONS */
+void *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 /* out */,
+                                                               struct err_s                    *err /* in/out */);
+
+zval *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);
+
+#endif
index 6b1a528c5b1dfbad3fda14331fdd472ac26789c2..6479bf90a850d1266961ec360df06c6030dd9469 100644 (file)
 
 #include <php.h>
 #include "php_sockets.h"
-#include "sockaddr_conv.h"
 #include "sendrecvmsg.h"
-#include <arpa/inet.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
+#include "conversions.h"
 #include <limits.h>
-#include <stdarg.h>
-#include <netinet/in.h>
-#include <stddef.h>
 #include <Zend/zend_llist.h>
-#include <ext/standard/php_smart_str.h>
 #ifdef ZTS
 #include <TSRM/TSRM.h>
 #endif
                } \
        } while (0)
 
-struct err_s {
-       int             has_error;
-       char    *msg;
-       int             level;
-       int             should_free;
-};
-
-typedef struct {
-       HashTable               params; /* stores pointers; has to be first */
-       struct err_s    err;
-       zend_llist              keys,
-       /* common part to res_context ends here */
-                                       allocations;
-       php_socket              *sock;
-} ser_context;
-
-typedef struct {
-       HashTable               params; /* stores pointers; has to be first */
-       struct err_s    err;
-       zend_llist              keys;
-} res_context;
-
-struct key_value {
-       const char      *key;
-       unsigned        key_size;
-       void            *value;
-};
-#define KEY_FILL_SOCKADDR "fill_sockaddr"
-#define KEY_RECVMSG_RET "recvmsg_ret"
-#define KEY_CMSG_LEN   "cmsg_len"
-static const struct key_value empty_key_value_list[] = {{0}};
-
-
-typedef void (from_zval_write_field)(const zval *arr_value, char *field, ser_context *ctx);
-typedef void (to_zval_read_field)(const char *data, zval *zv, res_context *ctx);
-typedef size_t (calculate_req_space)(const zval *value, ser_context *ctx);
-
-typedef struct {
-       /* zval info */
-       const char *name;
-       unsigned name_size;
-       int required;
-
-       /* structure info */
-       size_t field_offset; /* 0 to pass full structure, e.g. when more than
-                                                       one field is to be changed; in that case the
-                                                       callbacks need to know the name of the fields */
-
-       /* callbacks */
-       from_zval_write_field *from_zval;
-       to_zval_read_field *to_zval;
-} field_descriptor;
-
-typedef struct {
-       int     cmsg_level;     /* originating protocol */
-       int     cmsg_type;      /* protocol-specific type */
-} anc_reg_key;
-
 static struct {
        int                     initialized;
        HashTable       ht;
 } ancillary_registry;
 
-typedef socklen_t (*ancillary_size)(void);
-
-typedef struct {
-       socklen_t size; /* size of native structure */
-       socklen_t var_el_size; /* size of repeatable component */
-       calculate_req_space *calc_space;
-       from_zval_write_field *from_array;
-       to_zval_read_field *to_array;
-} ancillary_reg_entry;
-
-/* PARAMETERS */
-static int param_get_bool(void *ctx, const char *key, int def)
-{
-       int **elem;
-       if (zend_hash_find(ctx, key, strlen(key) + 1, (void**)&elem) == SUCCESS) {
-               return **elem;
-       } else {
-               return def;
-       }
-}
-
-/* FORWARD DECLARATIONS */
-static ancillary_reg_entry *get_ancillary_reg_entry(int cmsg_level, int msg_type);
-
-static inline void *accounted_emalloc(size_t alloc_size, ser_context *ctx)
-{
-       void *ret = emalloc(alloc_size);
-       zend_llist_add_element(&ctx->allocations, &ret);
-       return ret;
-}
-
-static inline void *accounted_ecalloc(size_t nmemb, size_t alloc_size, ser_context *ctx)
-{
-       void *ret = ecalloc(nmemb, alloc_size);
-       zend_llist_add_element(&ctx->allocations, &ret);
-       return ret;
-}
-static inline void *accounted_safe_ecalloc(size_t nmemb, size_t alloc_size, size_t offset, ser_context *ctx)
-{
-       void *ret = safe_emalloc(nmemb, alloc_size, offset);
-       memset(ret, '\0', nmemb * alloc_size + offset);
-       zend_llist_add_element(&ctx->allocations, &ret);
-       return ret;
-}
-
-static void do_from_to_zval_err(struct err_s *err,
-                                                               zend_llist *keys,
-                                                               const char *what_conv,
-                                                               const char *fmt,
-                                                               va_list ap)
-{
-       smart_str                       path = {0};
-       const char                      **node;
-       char                            *user_msg;
-       int                                     user_msg_size;
-       zend_llist_position     pos;
-
-       if (err->has_error) {
-               return;
-       }
-
-       for (node = zend_llist_get_first_ex(keys, &pos);
-                       node != NULL;
-                       node = zend_llist_get_next_ex(keys, &pos)) {
-               smart_str_appends(&path, *node);
-               smart_str_appends(&path, " > ");
-       }
-
-       if (path.len > 3) {
-               path.len -= 3;
-       }
-       smart_str_0(&path);
-
-       user_msg_size = vspprintf(&user_msg, 0, fmt, ap);
-
-       err->has_error = 1;
-       err->level = E_WARNING;
-       spprintf(&err->msg, 0, "error converting %s data (path: %s): %.*s",
-                       what_conv,
-                       path.c && path.c != '\0' ? path.c : "unavailable",
-                       user_msg_size, user_msg);
-       err->should_free = 1;
-
-       efree(user_msg);
-       smart_str_free_ex(&path, 0);
-}
-__attribute__ ((format (printf, 2, 3)))
-static void do_from_zval_err(ser_context *ctx, const char *fmt, ...)
-{
-       va_list ap;
-
-       va_start(ap, fmt);
-       do_from_to_zval_err(&ctx->err, &ctx->keys, "user", fmt, ap);
-       va_end(ap);
-}
-__attribute__ ((format (printf, 2, 3)))
-static void do_to_zval_err(res_context *ctx, const char *fmt, ...)
-{
-       va_list ap;
-
-       va_start(ap, fmt);
-       do_from_to_zval_err(&ctx->err, &ctx->keys, "native", fmt, ap);
-       va_end(ap);
-}
-static void err_msg_dispose(struct err_s *err TSRMLS_DC)
-{
-       if (err->msg != NULL) {
-               php_error_docref0(NULL TSRMLS_CC, err->level, "%s", err->msg);
-               if (err->should_free) {
-                       efree(err->msg);
-               }
-       }
-}
-static void allocations_dispose(zend_llist **allocations)
-{
-       zend_llist_destroy(*allocations);
-       efree(*allocations);
-       *allocations = NULL;
-}
-
-static unsigned from_array_iterate(const zval *arr,
-                                                                  void (*func)(zval **elem, unsigned i, void **args, ser_context *ctx),
-                                                                  void **args,
-                                                                  ser_context *ctx)
-{
-       HashPosition    pos;
-       unsigned                i;
-       zval                    **elem;
-       char                    buf[sizeof("element #4294967295")];
-       char                    *bufp = buf;
-
-    for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(arr), &pos), i = 1;
-                       !ctx->err.has_error
-                       && zend_hash_get_current_data_ex(Z_ARRVAL_P(arr), (void **)&elem, &pos) == SUCCESS;
-                       zend_hash_move_forward_ex(Z_ARRVAL_P(arr), &pos), i++) {
-               if (snprintf(buf, sizeof(buf), "element #%u", i) >= sizeof(buf)) {
-                       memcpy(buf, "element", sizeof("element"));
-               }
-               zend_llist_add_element(&ctx->keys, &bufp);
-
-               func(elem, i, args, ctx);
-
-               zend_llist_remove_tail(&ctx->keys);
-    }
-
-    return i -1;
-}
-
-/* Generic Aggregated conversions */
-static void from_zval_write_aggregation(const zval *container,
-                                                                               char *structure,
-                                                                               const field_descriptor *descriptors,
-                                                                               ser_context *ctx)
-{
-       const field_descriptor  *descr;
-       zval                                    **elem;
-
-       if (Z_TYPE_P(container) != IS_ARRAY) {
-               do_from_zval_err(ctx, "%s", "expected an array here");
-       }
-
-       for (descr = descriptors; descr->name != NULL && !ctx->err.has_error; descr++) {
-               if (zend_hash_find(Z_ARRVAL_P(container),
-                               descr->name, descr->name_size, (void**)&elem) == SUCCESS) {
-
-                       if (descr->from_zval == NULL) {
-                               do_from_zval_err(ctx, "No information on how to convert value "
-                                               "of key '%s'", descr->name);
-                               break;
-                       }
-
-                       zend_llist_add_element(&ctx->keys, (void*)&descr->name);
-                       descr->from_zval(*elem, ((char*)structure) + descr->field_offset, ctx);
-                       zend_llist_remove_tail(&ctx->keys);
-
-               } else if (descr->required) {
-                       do_from_zval_err(ctx, "The key '%s' is required", descr->name);
-                       break;
-               }
-       }
-}
-static void to_zval_read_aggregation(const char *structure,
-                                                                        zval *zarr, /* initialized array */
-                                                                        const field_descriptor *descriptors,
-                                                                        res_context *ctx)
-{
-       const field_descriptor  *descr;
-
-       assert(Z_TYPE_P(zarr) == IS_ARRAY);
-       assert(Z_ARRVAL_P(zarr) != NULL);
-
-       for (descr = descriptors; descr->name != NULL && !ctx->err.has_error; descr++) {
-               zval *new_zv;
-
-               if (descr->to_zval == NULL) {
-                       do_to_zval_err(ctx, "No information on how to convert native "
-                                       "field into value for key '%s'", descr->name);
-                       break;
-               }
-
-               ALLOC_INIT_ZVAL(new_zv);
-               add_assoc_zval_ex(zarr, descr->name, descr->name_size, new_zv);
-
-               zend_llist_add_element(&ctx->keys, (void*)&descr->name);
-               descr->to_zval(structure + descr->field_offset, new_zv, ctx);
-               zend_llist_remove_tail(&ctx->keys);
-       }
-}
-
-/* CONVERSIONS for integers */
-static long from_zval_integer_common(const zval *arr_value, ser_context *ctx)
-{
-       long ret = 0;
-       zval lzval = zval_used_for_init;
-
-       if (Z_TYPE_P(arr_value) != IS_LONG) {
-               ZVAL_COPY_VALUE(&lzval, arr_value);
-               zval_copy_ctor(&lzval);
-               arr_value = &lzval;
-       }
-
-       switch (Z_TYPE_P(arr_value)) {
-       case IS_LONG:
-long_case:
-               ret = Z_LVAL_P(arr_value);
-               break;
-
-       /* if not long we're operating on lzval */
-       case IS_DOUBLE:
-double_case:
-               convert_to_long(&lzval);
-               goto long_case;
-
-       case IS_OBJECT:
-       case IS_STRING: {
-               long lval;
-               double dval;
-
-               convert_to_string(&lzval);
-
-               switch (is_numeric_string(Z_STRVAL(lzval), Z_STRLEN(lzval), &lval, &dval, 0)) {
-               case IS_DOUBLE:
-                       zval_dtor(&lzval);
-                       Z_TYPE(lzval) = IS_DOUBLE;
-                       Z_DVAL(lzval) = dval;
-                       goto double_case;
-
-               case IS_LONG:
-                       zval_dtor(&lzval);
-                       Z_TYPE(lzval) = IS_LONG;
-                       Z_DVAL(lzval) = lval;
-                       goto long_case;
-               }
-
-               /* if we get here, we don't have a numeric string */
-               do_from_zval_err(ctx, "expected an integer, but got a non numeric "
-                               "string (possibly from a converted object): '%s'", Z_STRVAL_P(arr_value));
-               break;
-       }
-
-       default:
-               do_from_zval_err(ctx, "%s", "expected an integer, either of a PHP "
-                               "integer type or of a convertible type");
-               break;
-       }
-
-       zval_dtor(&lzval);
-
-       return ret;
-}
-static void from_zval_write_int(const zval *arr_value, char *field, ser_context *ctx)
-{
-       long lval;
-       int ival;
-
-       lval = from_zval_integer_common(arr_value, ctx);
-       if (ctx->err.has_error) {
-               return;
-       }
-
-       if (lval > INT_MAX || lval < INT_MIN) {
-               do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
-                               "for a native int");
-               return;
-       }
-
-       ival = (int)lval;
-       memcpy(field, &ival, sizeof(ival));
-}
-static void from_zval_write_unsigned(const zval *arr_value, char *field, ser_context *ctx)
-{
-       long lval;
-       unsigned ival;
-
-       lval = from_zval_integer_common(arr_value, ctx);
-       if (ctx->err.has_error) {
-               return;
-       }
-
-       if (sizeof(long) > sizeof(ival) && (lval < 0 || lval > UINT_MAX)) {
-               do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
-                               "for a native unsigned int");
-               return;
-       }
-
-       ival = (unsigned)lval;
-       memcpy(field, &ival, sizeof(ival));
-}
-static void from_zval_write_uint32(const zval *arr_value, char *field, ser_context *ctx)
-{
-       long lval;
-       uint32_t ival;
-
-       lval = from_zval_integer_common(arr_value, ctx);
-       if (ctx->err.has_error) {
-               return;
-       }
-
-       if (sizeof(long) > sizeof(uint32_t) && (lval < 0 || lval > 0xFFFFFFFF)) {
-               do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
-                               "for an unsigned 32-bit integer");
-               return;
-       }
-
-       ival = (uint32_t)lval;
-       memcpy(field, &ival, sizeof(ival));
-}
-static void from_zval_write_net_uint16(const zval *arr_value, char *field, ser_context *ctx)
-{
-       long lval;
-       uint16_t ival;
-
-       lval = from_zval_integer_common(arr_value, ctx);
-       if (ctx->err.has_error) {
-               return;
-       }
-
-       if (lval < 0 || lval > 0xFFFF) {
-               do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
-                               "for an unsigned 16-bit integer");
-               return;
-       }
-
-       ival = htons((uint16_t)lval);
-       memcpy(field, &ival, sizeof(ival));
-}
-static void from_zval_write_sa_family(const zval *arr_value, char *field, ser_context *ctx)
-{
-       long lval;
-       sa_family_t ival;
-
-       lval = from_zval_integer_common(arr_value, ctx);
-       if (ctx->err.has_error) {
-               return;
-       }
-
-       if (lval < 0 || lval > (sa_family_t)-1) { /* sa_family_t is unsigned */
-               do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
-                               "for a sa_family_t value");
-               return;
-       }
-
-       ival = (sa_family_t)lval;
-       memcpy(field, &ival, sizeof(ival));
-}
-static void from_zval_write_pid_t(const zval *arr_value, char *field, ser_context *ctx)
-{
-       long lval;
-       pid_t ival;
-
-       lval = from_zval_integer_common(arr_value, ctx);
-       if (ctx->err.has_error) {
-               return;
-       }
-
-       if (lval < 0 || (pid_t)lval != lval) { /* pid_t is signed */
-               do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
-                               "for a pid_t value");
-               return;
-       }
-
-       ival = (pid_t)lval;
-       memcpy(field, &ival, sizeof(ival));
-}
-static void from_zval_write_uid_t(const zval *arr_value, char *field, ser_context *ctx)
-{
-       long lval;
-       uid_t ival;
-
-       lval = from_zval_integer_common(arr_value, ctx);
-       if (ctx->err.has_error) {
-               return;
-       }
-
-       /* uid_t can be signed or unsigned (generally unsigned) */
-       if ((uid_t)-1 > (uid_t)0) {
-               if (sizeof(long) > sizeof(uid_t) && (lval < 0 || (uid_t)lval != lval)) {
-                       do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
-                                       "for a uid_t value");
-                       return;
-               }
-       } else {
-               if (sizeof(long) > sizeof(uid_t) && (uid_t)lval != lval) {
-                       do_from_zval_err(ctx, "%s", "given PHP integer is out of bounds "
-                                       "for a uid_t value");
-                       return;
-               }
-       }
-
-       ival = (uid_t)lval;
-       memcpy(field, &ival, sizeof(ival));
-}
-
-static void to_zval_read_int(const char *data, zval *zv, res_context *ctx)
-{
-       int ival;
-       memcpy(&ival, data, sizeof(ival));
-
-       ZVAL_LONG(zv, (long)ival);
-}
-static void to_zval_read_unsigned(const char *data, zval *zv, res_context *ctx)
-{
-       unsigned ival;
-       memcpy(&ival, data, sizeof(ival));
-
-       ZVAL_LONG(zv, (long)ival);
-}
-static void to_zval_read_net_uint16(const char *data, zval *zv, res_context *ctx)
-{
-       uint16_t ival;
-       memcpy(&ival, data, sizeof(ival));
-
-       ZVAL_LONG(zv, (long)ntohs(ival));
-}
-static void to_zval_read_uint32(const char *data, zval *zv, res_context *ctx)
-{
-       uint32_t ival;
-       memcpy(&ival, data, sizeof(ival));
-
-       ZVAL_LONG(zv, (long)ival);
-}
-static void to_zval_read_sa_family(const char *data, zval *zv, res_context *ctx)
-{
-       sa_family_t ival;
-       memcpy(&ival, data, sizeof(ival));
-
-       ZVAL_LONG(zv, (long)ival);
-}
-static void to_zval_read_pid_t(const char *data, zval *zv, res_context *ctx)
-{
-       pid_t ival;
-       memcpy(&ival, data, sizeof(ival));
-
-       ZVAL_LONG(zv, (long)ival);
-}
-static void to_zval_read_uid_t(const char *data, zval *zv, res_context *ctx)
-{
-       uid_t ival;
-       memcpy(&ival, data, sizeof(ival));
-
-       ZVAL_LONG(zv, (long)ival);
-}
-
-/* CONVERSIONS for sockaddr */
-static void from_zval_write_sin_addr(const zval *zaddr_str, char *inaddr, ser_context *ctx)
-{
-       int                                     res;
-       struct sockaddr_in      saddr = {0};
-       zval                            lzval = zval_used_for_init;
-       TSRMLS_FETCH();
-
-       if (Z_TYPE_P(zaddr_str) != IS_STRING) {
-               ZVAL_COPY_VALUE(&lzval, zaddr_str);
-               zval_copy_ctor(&lzval);
-               convert_to_string(&lzval);
-               zaddr_str = &lzval;
-       }
-
-       res = php_set_inet_addr(&saddr, Z_STRVAL_P(zaddr_str), ctx->sock TSRMLS_CC);
-       if (res) {
-               memcpy(inaddr, &saddr.sin_addr, sizeof saddr.sin_addr);
-       } else {
-               /* error already emitted, but let's emit another more relevant */
-               do_from_zval_err(ctx, "could not resolve address '%s' to get an AF_INET "
-                               "address", Z_STRVAL_P(zaddr_str));
-       }
-
-       zval_dtor(&lzval);
-}
-static void to_zval_read_sin_addr(const char *data, zval *zv, res_context *ctx)
-{
-       const struct in_addr *addr = (const struct in_addr *)data;
-       socklen_t size = INET_ADDRSTRLEN;
-
-       Z_TYPE_P(zv) = IS_STRING;
-       Z_STRVAL_P(zv) = ecalloc(1, size);
-       Z_STRLEN_P(zv) = 0;
-
-       if (inet_ntop(AF_INET, addr, Z_STRVAL_P(zv), size) == NULL) {
-               do_to_zval_err(ctx, "could not convert IPv4 address to string "
-                               "(errno %d)", errno);
-               return;
-       }
-
-       Z_STRLEN_P(zv) = strlen(Z_STRVAL_P(zv));
-}
-static const field_descriptor descriptors_sockaddr_in[] = {
-               {"family", sizeof("family"), 0, offsetof(struct sockaddr_in, sin_family), from_zval_write_sa_family, to_zval_read_sa_family},
-               {"addr", sizeof("addr"), 0, offsetof(struct sockaddr_in, sin_addr), from_zval_write_sin_addr, to_zval_read_sin_addr},
-               {"port", sizeof("port"), 0, offsetof(struct sockaddr_in, sin_port), from_zval_write_net_uint16, to_zval_read_net_uint16},
-               {0}
-};
-static void from_zval_write_sockaddr_in(const zval *container, char *sockaddr, ser_context *ctx)
-{
-       from_zval_write_aggregation(container, sockaddr, descriptors_sockaddr_in, ctx);
-}
-static void to_zval_read_sockaddr_in(const char *data, zval *zv, res_context *ctx)
-{
-       to_zval_read_aggregation(data, zv, descriptors_sockaddr_in, ctx);
-}
-static void from_zval_write_sin6_addr(const zval *zaddr_str, char *addr6, ser_context *ctx)
-{
-       int                                     res;
-       struct sockaddr_in6     saddr6 = {0};
-       zval                            lzval = zval_used_for_init;
-       TSRMLS_FETCH();
-
-       if (Z_TYPE_P(zaddr_str) != IS_STRING) {
-               ZVAL_COPY_VALUE(&lzval, zaddr_str);
-               zval_copy_ctor(&lzval);
-               convert_to_string(&lzval);
-               zaddr_str = &lzval;
-       }
-
-       res = php_set_inet6_addr(&saddr6,
-                       Z_STRVAL_P(zaddr_str), ctx->sock TSRMLS_CC);
-       if (res) {
-               memcpy(addr6, &saddr6.sin6_addr, sizeof saddr6.sin6_addr);
-       } else {
-               /* error already emitted, but let's emit another more relevant */
-               do_from_zval_err(ctx, "could not resolve address '%s' to get an AF_INET6 "
-                               "address", Z_STRVAL_P(zaddr_str));
-       }
-
-       zval_dtor(&lzval);
-}
-static void to_zval_read_sin6_addr(const char *data, zval *zv, res_context *ctx)
-{
-       const struct in6_addr *addr = (const struct in6_addr *)data;
-       socklen_t size = INET6_ADDRSTRLEN;
-
-       Z_TYPE_P(zv) = IS_STRING;
-       Z_STRVAL_P(zv) = ecalloc(1, size);
-       Z_STRLEN_P(zv) = 0;
-
-       if (inet_ntop(AF_INET6, addr, Z_STRVAL_P(zv), size) == NULL) {
-               do_to_zval_err(ctx, "could not convert IPv6 address to string "
-                               "(errno %d)", errno);
-               return;
-       }
-
-       Z_STRLEN_P(zv) = strlen(Z_STRVAL_P(zv));
-}
-static const field_descriptor descriptors_sockaddr_in6[] = {
-               {"family", sizeof("family"), 0, offsetof(struct sockaddr_in6, sin6_family), from_zval_write_sa_family, to_zval_read_sa_family},
-               {"addr", sizeof("addr"), 0, offsetof(struct sockaddr_in6, sin6_addr), from_zval_write_sin6_addr, to_zval_read_sin6_addr},
-               {"port", sizeof("port"), 0, offsetof(struct sockaddr_in6, sin6_port), from_zval_write_net_uint16, to_zval_read_net_uint16},
-               {"flowinfo", sizeof("flowinfo"), 0, offsetof(struct sockaddr_in6, sin6_flowinfo), from_zval_write_uint32, to_zval_read_uint32},
-               {"scope_id", sizeof("scope_id"), 0, offsetof(struct sockaddr_in6, sin6_scope_id), from_zval_write_uint32, to_zval_read_uint32},
-               {0}
-};
-static void from_zval_write_sockaddr_in6(const zval *container, char *sockaddr6, ser_context *ctx)
-{
-       from_zval_write_aggregation(container, sockaddr6, descriptors_sockaddr_in6, ctx);
-}
-static void to_zval_read_sockaddr_in6(const char *data, zval *zv, res_context *ctx)
-{
-       to_zval_read_aggregation(data, zv, descriptors_sockaddr_in6, ctx);
-}
-static void from_zval_write_sun_path(const zval *path, char *sockaddr_un_c, ser_context *ctx)
-{
-       zval                            lzval = zval_used_for_init;
-       struct sockaddr_un      *saddr = (struct sockaddr_un*)sockaddr_un_c;
-
-       if (Z_TYPE_P(path) != IS_STRING) {
-               ZVAL_COPY_VALUE(&lzval, path);
-               zval_copy_ctor(&lzval);
-               convert_to_string(&lzval);
-               path = &lzval;
-       }
-
-       if (Z_STRLEN_P(path) >= sizeof(saddr->sun_path)) {
-               do_from_zval_err(ctx, "the path is too long, the maximum permitted "
-                               "length is %ld", sizeof(saddr->sun_path) - 1);
-               return;
-       }
-
-       memcpy(&saddr->sun_path, Z_STRVAL_P(path), Z_STRLEN_P(path));
-       saddr->sun_path[Z_STRLEN_P(path)] = '\0';
-
-       zval_dtor(&lzval);
-}
-static void to_zval_read_sun_path(const char *data, zval *zv, res_context *ctx) {
-       struct sockaddr_un      *saddr = (struct sockaddr_un*)data;
-       char *nul_pos;
-
-       nul_pos = memchr(&saddr->sun_path, '\0', sizeof(saddr->sun_path));
-       if (nul_pos == NULL) {
-               do_to_zval_err(ctx, "could not find a NUL in the path");
-               return;
-       }
-
-       ZVAL_STRINGL(zv, saddr->sun_path, nul_pos - (char*)&saddr->sun_path, 1);
-}
-static const field_descriptor descriptors_sockaddr_un[] = {
-               {"family", sizeof("family"), 0, offsetof(struct sockaddr_un, sun_family), from_zval_write_sa_family, to_zval_read_sa_family},
-               {"path", sizeof("path"), 0, 0, from_zval_write_sun_path, to_zval_read_sun_path},
-               {0}
-};
-static void from_zval_write_sockaddr_un(const zval *container, char *sockaddr, ser_context *ctx)
-{
-       from_zval_write_aggregation(container, sockaddr, descriptors_sockaddr_un, ctx);
-}
-static void to_zval_read_sockaddr_un(const char *data, zval *zv, res_context *ctx)
-{
-       to_zval_read_aggregation(data, zv, descriptors_sockaddr_un, ctx);
-}
-static void from_zval_write_sockaddr_aux(const zval *container,
-                                                                                struct sockaddr **sockaddr_ptr,
-                                                                                socklen_t *sockaddr_len,
-                                                                                ser_context *ctx)
-{
-       int             family;
-       zval    **elem;
-       int             fill_sockaddr;
-
-       if (Z_TYPE_P(container) != IS_ARRAY) {
-               do_from_zval_err(ctx, "%s", "expected an array here");
-               return;
-       }
-
-       fill_sockaddr = param_get_bool(ctx, KEY_FILL_SOCKADDR, 1);
-
-       if (zend_hash_find(Z_ARRVAL_P(container), "family", sizeof("family"), (void**)&elem) == SUCCESS
-                       && Z_TYPE_PP(elem) != IS_NULL) {
-               const char *node = "family";
-               zend_llist_add_element(&ctx->keys, &node);
-               from_zval_write_int(*elem, (char*)&family, ctx);
-               zend_llist_remove_tail(&ctx->keys);
-       } else {
-               family = ctx->sock->type;
-       }
-
-       switch (family) {
-       case AF_INET:
-               /* though not all OSes support sockaddr_in used in IPv6 sockets */
-               if (ctx->sock->type != AF_INET && ctx->sock->type != AF_INET6) {
-                       do_from_zval_err(ctx, "the specified family (number %d) is not "
-                                       "supported on this socket", family);
-                       return;
-               }
-               *sockaddr_ptr = accounted_ecalloc(1, sizeof(struct sockaddr_in), ctx);
-               *sockaddr_len = sizeof(struct sockaddr_in);
-               if (fill_sockaddr) {
-                       from_zval_write_sockaddr_in(container, (char*)*sockaddr_ptr, ctx);
-                       (*sockaddr_ptr)->sa_family = AF_INET;
-               }
-               break;
-
-       case AF_INET6:
-               if (ctx->sock->type != AF_INET6) {
-                       do_from_zval_err(ctx, "the specified family (AF_INET6) is not "
-                                       "supported on this socket");
-                       return;
-               }
-               *sockaddr_ptr = accounted_ecalloc(1, sizeof(struct sockaddr_in6), ctx);
-               *sockaddr_len = sizeof(struct sockaddr_in6);
-               if (fill_sockaddr) {
-                       from_zval_write_sockaddr_in6(container, (char*)*sockaddr_ptr, ctx);
-                       (*sockaddr_ptr)->sa_family = AF_INET6;
-               }
-               break;
-
-       case AF_UNIX:
-               if (ctx->sock->type != AF_UNIX) {
-                       do_from_zval_err(ctx, "the specified family (AF_UNIX) is not "
-                                       "supported on this socket");
-                       return;
-               }
-               *sockaddr_ptr = accounted_ecalloc(1, sizeof(struct sockaddr_un), ctx);
-               *sockaddr_len = sizeof(struct sockaddr_un);
-               if (fill_sockaddr) {
-                       from_zval_write_sockaddr_un(container, (char*)*sockaddr_ptr, ctx);
-                       (*sockaddr_ptr)->sa_family = AF_UNIX;
-               }
-               break;
-
-       default:
-               do_from_zval_err(ctx, "%s", "the only families currently supported are "
-                               "AF_INET, AF_INET6 and AF_UNIX");
-               break;
-       }
-}
-static void to_zval_read_sockaddr_aux(const char *sockaddr_c, zval *zv, res_context *ctx)
-{
-       const struct sockaddr *saddr = (struct sockaddr *)sockaddr_c;
-
-       if (saddr->sa_family == 0) {
-               ZVAL_NULL(zv);
-               return;
-       }
-
-       array_init(zv);
-
-       switch (saddr->sa_family) {
-       case AF_INET:
-               to_zval_read_sockaddr_in(sockaddr_c, zv, ctx);
-               break;
-
-       case AF_INET6:
-               to_zval_read_sockaddr_in6(sockaddr_c, zv, ctx);
-               break;
-
-       case AF_UNIX:
-               to_zval_read_sockaddr_un(sockaddr_c, zv, ctx);
-               break;
-
-       default:
-               do_to_zval_err(ctx, "cannot read struct sockaddr with family %d; "
-                               "not supported",
-                               (int)saddr->sa_family);
-               break;
-       }
-}
-
-/* CONVERSIONS for cmsghdr */
-/*
- * [ level => , type => , data => [],]
- * struct cmsghdr {
- *  socklen_t cmsg_len;    // data byte count, including header
- *  int       cmsg_level;  // originating protocol
- *  int       cmsg_type;   // protocol-specific type
- *  // followed by unsigned char cmsg_data[];
- * };
- */
-static void from_zval_write_control(const zval                 *arr,
-                                                                       void                            **control_buf,
-                                                                       zend_llist_element      *alloc,
-                                                                       size_t                          *control_len,
-                                                                       size_t                          *offset,
-                                                                       ser_context                     *ctx)
-{
-       struct cmsghdr          *cmsghdr;
-       int                                     level,
-                                               type;
-       size_t                          data_len,
-                                               req_space,
-                                               space_left;
-       ancillary_reg_entry     *entry;
-
-       static const field_descriptor descriptor_level[] = {
-                       {"level", sizeof("level"), 0, 0, from_zval_write_int, 0},
-                       {0}
-       };
-       static const field_descriptor descriptor_type[] = {
-                       {"type", sizeof("type"), 0, 0, from_zval_write_int, 0},
-                       {0}
-       };
-       field_descriptor descriptor_data[] = {
-                       {"data", sizeof("data"), 0, 0, 0, 0},
-                       {0}
-       };
-
-       from_zval_write_aggregation(arr, (char *)&level, descriptor_level, ctx);
-       if (ctx->err.has_error) {
-               return;
-       }
-       from_zval_write_aggregation(arr, (char *)&type, descriptor_type, ctx);
-       if (ctx->err.has_error) {
-               return;
-       }
-
-       entry = get_ancillary_reg_entry(level, type);
-       if (entry == NULL) {
-               do_from_zval_err(ctx, "cmsghdr with level %d and type %d not supported",
-                               level, type);
-               return;
-       }
-
-       if (entry->calc_space) {
-               data_len = entry->calc_space(arr, ctx);
-               if (ctx->err.has_error) {
-                       return;
-               }
-       } else {
-               data_len = entry->size;
-       }
-       req_space = CMSG_SPACE(data_len);
-       space_left = *control_len - *offset;
-       assert(*control_len >= *offset);
-
-       if (space_left < req_space) {
-               *control_buf = safe_erealloc(*control_buf, 2, req_space, *control_len);
-               *control_len += 2 * req_space;
-               memcpy(&alloc->data, *control_buf, sizeof *control_buf);
-       }
-
-       cmsghdr = (struct cmsghdr*)(((char*)*control_buf) + *offset);
-       cmsghdr->cmsg_level     = level;
-       cmsghdr->cmsg_type      = type;
-       cmsghdr->cmsg_len       = CMSG_LEN(data_len);
-
-       descriptor_data[0].from_zval = entry->from_array;
-       from_zval_write_aggregation(arr, (char*)CMSG_DATA(cmsghdr), descriptor_data, ctx);
-
-       *offset += req_space;
-}
-static void from_zval_write_control_array(const zval *arr, char *msghdr_c, ser_context *ctx)
-{
-       HashPosition            pos;
-       char                            buf[sizeof("element #4294967295")];
-       char                            *bufp = buf;
-       zval                            **elem;
-       uint32_t                        i;
-       int                                     num_elems;
-       void                            *control_buf;
-       zend_llist_element      *alloc;
-       size_t                          control_len,
-                                               cur_offset;
-       struct msghdr           *msg = (struct msghdr*)msghdr_c;
-
-       if (Z_TYPE_P(arr) != IS_ARRAY) {
-               do_from_zval_err(ctx, "%s", "expected an array here");
-               return;
-       }
-
-       num_elems = zend_hash_num_elements(Z_ARRVAL_P(arr));
-       if (num_elems == 0) {
-               return;
-       }
-
-       /* estimate each message at 20 bytes */
-       control_buf     = accounted_safe_ecalloc(num_elems, CMSG_SPACE(20), 0, ctx);
-       alloc           = ctx->allocations.tail;
-       control_len = (size_t)num_elems * CMSG_SPACE(20);
-       cur_offset      = 0;
-
-    for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(arr), &pos), i = 0;
-                       !ctx->err.has_error
-                       && zend_hash_get_current_data_ex(Z_ARRVAL_P(arr), (void **)&elem, &pos) == SUCCESS;
-                       zend_hash_move_forward_ex(Z_ARRVAL_P(arr), &pos)) {
-
-               if (snprintf(buf, sizeof(buf), "element #%u", (unsigned)i++) >= sizeof(buf)) {
-                       memcpy(buf, "element", sizeof("element"));
-               }
-               zend_llist_add_element(&ctx->keys, &bufp);
-
-               from_zval_write_control(*elem, &control_buf, alloc, &control_len,
-                               &cur_offset, ctx);
-
-               zend_llist_remove_tail(&ctx->keys);
-    }
-
-    msg->msg_control = control_buf;
-    msg->msg_controllen = cur_offset; /* not control_len, which may be larger */
-}
-static void to_zval_read_cmsg_data(const char *cmsghdr_c, zval *zv, res_context *ctx)
-{
-       const struct cmsghdr    *cmsg = (const struct cmsghdr *)cmsghdr_c;
-       ancillary_reg_entry             *entry;
-       size_t                                  len,
-                                                       *len_p = &len;
-
-       entry = get_ancillary_reg_entry(cmsg->cmsg_level, cmsg->cmsg_type);
-       if (entry == NULL) {
-               do_to_zval_err(ctx, "cmsghdr with level %d and type %d not supported",
-                               cmsg->cmsg_level, cmsg->cmsg_type);
-               return;
-       }
-       if (CMSG_LEN(entry->size) > cmsg->cmsg_len) {
-               do_to_zval_err(ctx, "the cmsghdr structure is unexpectedly small; "
-                               "expected a length of at least %ld, but got %ld",
-                               (long)CMSG_LEN(entry->size), (long)cmsg->cmsg_len);
-               return;
-       }
-
-       len = (size_t)cmsg->cmsg_len; /* use another var because type of cmsg_len varies */
-       if (zend_hash_add(&ctx->params, KEY_CMSG_LEN, sizeof(KEY_CMSG_LEN),
-                       &len_p, sizeof(len_p), NULL) == FAILURE) {
-               do_to_zval_err(ctx, "%s", "could not set parameter " KEY_CMSG_LEN);
-               return;
-       }
-
-       entry->to_array((const char *)CMSG_DATA(cmsg), zv, ctx);
-
-       zend_hash_del(&ctx->params, KEY_CMSG_LEN, sizeof(KEY_CMSG_LEN));
-}
-static void to_zval_read_control(const char *cmsghdr_c, zval *zv, res_context *ctx)
-{
-       /* takes a cmsghdr, not a msghdr like from_zval_write_control */
-       static const field_descriptor descriptors[] = {
-                       {"level", sizeof("level"), 0, offsetof(struct cmsghdr, cmsg_level), 0, to_zval_read_int},
-                       {"type", sizeof("type"), 0, offsetof(struct cmsghdr, cmsg_type), 0, to_zval_read_int},
-                       {"data", sizeof("data"), 0, 0 /* cmsghdr passed */, 0, to_zval_read_cmsg_data},
-                       {0}
-       };
-
-       array_init_size(zv, 3);
-       to_zval_read_aggregation(cmsghdr_c, zv, descriptors, ctx);
-}
-static void to_zval_read_control_array(const char *msghdr_c, zval *zv, res_context *ctx)
-{
-       struct msghdr   *msg = (struct msghdr *)msghdr_c;
-       struct cmsghdr  *cmsg;
-       char                    buf[sizeof("element #4294967295")];
-       char                    *bufp = buf;
-       uint32_t                i = 1;
-
-       /*if (msg->msg_flags & MSG_CTRUNC) {
-               php_error_docref0(NULL, E_WARNING, "The MSG_CTRUNC flag is present; will not "
-                               "attempt to read control messages");
-               ZVAL_FALSE(zv);
-               return;
-       }*/
-
-       array_init(zv);
-
-       for (cmsg = CMSG_FIRSTHDR(msg);
-                       cmsg != NULL && !ctx->err.has_error;
-                       cmsg = CMSG_NXTHDR(msg,cmsg)) {
-               zval *elem;
-
-               ALLOC_INIT_ZVAL(elem);
-               add_next_index_zval(zv, elem);
-
-               if (snprintf(buf, sizeof(buf), "element #%u", (unsigned)i++) >= sizeof(buf)) {
-                       memcpy(buf, "element", sizeof("element"));
-               }
-               zend_llist_add_element(&ctx->keys, &bufp);
-
-               to_zval_read_control((const char *)cmsg, elem, ctx);
-
-               zend_llist_remove_tail(&ctx->keys);
-       }
-}
-
-/* CONVERSIONS for msghdr */
-static void from_zval_write_name(const zval *zname_arr, char *msghdr_c, ser_context *ctx)
-{
-       struct sockaddr *sockaddr;
-       socklen_t               sockaddr_len;
-       struct msghdr   *msghdr = (struct msghdr *)msghdr_c;
-
-       from_zval_write_sockaddr_aux(zname_arr, &sockaddr, &sockaddr_len, ctx);
-
-       msghdr->msg_name = sockaddr;
-       msghdr->msg_namelen = sockaddr_len;
-}
-static void to_zval_read_name(const char *sockaddr_p, zval *zv, res_context *ctx)
-{
-       void *name = (void*)*(void**)sockaddr_p;
-       if (name == NULL) {
-               ZVAL_NULL(zv);
-       } else {
-               to_zval_read_sockaddr_aux(name, zv, ctx);
-       }
-}
-static void from_zval_write_msghdr_buffer_size(const zval *elem, char *msghdr_c, ser_context *ctx)
-{
-       long lval;
-       struct msghdr *msghdr = (struct msghdr *)msghdr_c;
-
-       lval = from_zval_integer_common(elem, ctx);
-       if (ctx->err.has_error) {
-               return;
-       }
-
-       if (lval < 0 || lval > MAX_USER_BUFF_SIZE) {
-               do_from_zval_err(ctx, "the buffer size must be between 1 and %ld; "
-                               "given %ld", (long)MAX_USER_BUFF_SIZE, lval);
-               return;
-       }
-
-       msghdr->msg_iovlen = 1;
-       msghdr->msg_iov = accounted_emalloc(sizeof(*msghdr->msg_iov) * 1, ctx);
-       msghdr->msg_iov[0].iov_base = accounted_emalloc((size_t)lval, ctx);
-       msghdr->msg_iov[0].iov_len = (size_t)lval;
-}
-static void from_zval_write_iov_array_aux(zval **elem, unsigned i, void **args, ser_context *ctx)
-{
-       struct msghdr   *msg = args[0];
-       size_t                  len;
-
-       zval_add_ref(elem);
-       convert_to_string_ex(elem);
-
-       len = Z_STRLEN_PP(elem);
-       msg->msg_iov[i - 1].iov_base = accounted_emalloc(len, ctx);
-       msg->msg_iov[i - 1].iov_len = len;
-       memcpy(msg->msg_iov[i - 1].iov_base, Z_STRVAL_PP(elem), len);
-
-       zval_ptr_dtor(elem);
-}
-static void from_zval_write_iov_array(const zval *arr, char *msghdr_c, ser_context *ctx)
-{
-       int                             num_elem;
-       struct msghdr   *msg = (struct msghdr*)msghdr_c;
-
-       if (Z_TYPE_P(arr) != IS_ARRAY) {
-               do_from_zval_err(ctx, "%s", "expected an array here");
-               return;
-       }
-
-       num_elem = zend_hash_num_elements(Z_ARRVAL_P(arr));
-       if (num_elem == 0) {
-               return;
-       }
-
-       msg->msg_iov = accounted_safe_ecalloc(num_elem, sizeof *msg->msg_iov, 0, ctx);
-       msg->msg_iovlen = (size_t)num_elem;
-
-    from_array_iterate(arr, from_zval_write_iov_array_aux, (void**)&msg, ctx);
-}
-static void from_zval_write_controllen(const zval *elem, char *msghdr_c, ser_context *ctx)
-{
-       struct msghdr *msghdr = (struct msghdr *)msghdr_c;
-       uint32_t len;
-
-       /* controllen should be an unsigned with at least 32-bit. Let's assume
-        * this least common denominator
-        */
-       from_zval_write_uint32(elem, (char*)&len, ctx);
-       if (!ctx->err.has_error && len == 0) {
-               do_from_zval_err(ctx, "controllen cannot be 0");
-               return;
-       }
-       msghdr->msg_control = accounted_emalloc(len, ctx);
-       msghdr->msg_controllen = len;
-}
-static void from_zval_write_msghdr_send(const zval *container, char *msghdr_c, ser_context *ctx)
-{
-       static const field_descriptor descriptors[] = {
-                       {"name", sizeof("name"), 0, 0, from_zval_write_name, 0},
-                       {"iov", sizeof("iov"), 0, 0, from_zval_write_iov_array, 0},
-                       {"control", sizeof("control"), 0, 0, from_zval_write_control_array, 0},
-                       {0}
-       };
-
-       from_zval_write_aggregation(container, msghdr_c, descriptors, ctx);
-}
-static void from_zval_write_msghdr_recv(const zval *container, char *msghdr_c, ser_context *ctx)
-{
-       /* zval to struct msghdr, version for recvmsg(). It differs from the version
-        * for sendmsg() in that it:
-        *      - has a buffer_size instead of an iov array;
-        *      - has no control element; has a controllen element instead
-        * struct msghdr {
-        *    void *msg_name;
-        *    socklen_t msg_namelen;
-        *    struct iovec *msg_iov;
-        *    size_t msg_iovlen;
-        *    void *msg_control;
-        *    size_t msg_controllen; //can also be socklen_t
-        *    int msg_flags;
-        * };
-        */
-       static const field_descriptor descriptors[] = {
-                       {"name", sizeof("name"), 0, 0, from_zval_write_name, 0},
-                       {"buffer_size", sizeof("buffer_size"), 0, 0, from_zval_write_msghdr_buffer_size, 0},
-                       {"controllen", sizeof("controllen"), 1, 0, from_zval_write_controllen, 0},
-                       {0}
-       };
-       struct msghdr   *msghdr = (struct msghdr *)msghdr_c;
-       const int               falsev = 0,
-                                       *falsevp = &falsev;
-
-       if (zend_hash_add(&ctx->params, KEY_FILL_SOCKADDR, sizeof(KEY_FILL_SOCKADDR),
-                       (void*)&falsevp, sizeof(falsevp), NULL) == FAILURE) {
-               do_from_zval_err(ctx, "could not add fill_sockaddr; this is a bug");
-               return;
-       }
-
-       from_zval_write_aggregation(container, msghdr_c, descriptors, ctx);
-
-       zend_hash_del(&ctx->params, KEY_FILL_SOCKADDR, sizeof(KEY_FILL_SOCKADDR));
-       if (ctx->err.has_error) {
-               return;
-       }
-
-       if (msghdr->msg_iovlen == 0) {
-               msghdr->msg_iovlen = 1;
-               msghdr->msg_iov = accounted_emalloc(sizeof(*msghdr->msg_iov) * 1, ctx);
-               msghdr->msg_iov[0].iov_base = accounted_emalloc((size_t)DEFAULT_BUFF_SIZE, ctx);
-               msghdr->msg_iov[0].iov_len = (size_t)DEFAULT_BUFF_SIZE;
-       }
-}
-
-static void to_zval_read_iov(const char *msghdr_c, zval *zv, res_context *ctx)
-{
-       const struct msghdr     *msghdr = (const struct msghdr *)msghdr_c;
-       size_t                          iovlen = msghdr->msg_iovlen;
-       ssize_t                         **recvmsg_ret,
-                                               bytes_left;
-       uint                            i;
-
-       if (iovlen > UINT_MAX) {
-               do_to_zval_err(ctx, "unexpectedly large value for iov_len: %lu",
-                               (unsigned long)iovlen);
-       }
-       array_init_size(zv, (uint)iovlen);
-
-       if (zend_hash_find(&ctx->params, KEY_RECVMSG_RET, sizeof(KEY_RECVMSG_RET),
-                       (void**)&recvmsg_ret) == FAILURE) {
-               do_to_zval_err(ctx, "recvmsg_ret not found in params. This is a bug");
-               return;
-       }
-       bytes_left = **recvmsg_ret;
-
-       for (i = 0; bytes_left > 0 && i < (uint)iovlen; i++) {
-               zval    *elem;
-               size_t  len             = MIN(msghdr->msg_iov[i].iov_len, bytes_left);
-               char    *buf    = safe_emalloc(1, len, 1);
-
-               MAKE_STD_ZVAL(elem);
-               memcpy(buf, msghdr->msg_iov[i].iov_base, len);
-               buf[len] = '\0';
-
-               ZVAL_STRINGL(elem, buf, len, 0);
-               add_next_index_zval(zv, elem);
-               bytes_left -= len;
-       }
-}
-static void to_zval_read_msghdr(const char *msghdr_c, zval *zv, res_context *ctx)
-{
-       static const field_descriptor descriptors[] = {
-                       {"name", sizeof("name"), 0, offsetof(struct msghdr, msg_name), 0, to_zval_read_name},
-                       {"control", sizeof("control"), 0, 0, 0, to_zval_read_control_array},
-                       {"iov", sizeof("iov"), 0, 0, 0, to_zval_read_iov},
-                       {"flags", sizeof("flags"), 0, offsetof(struct msghdr, msg_flags), 0, to_zval_read_int},
-                       {0}
-       };
-
-       array_init_size(zv, 4);
-
-       to_zval_read_aggregation(msghdr_c, zv, descriptors, ctx);
-}
-
-
-/* CONVERSIONS for struct in6_pktinfo */
-#ifdef IPV6_PKTINFO
-static const field_descriptor descriptors_in6_pktinfo[] = {
-               {"addr", sizeof("addr"), 1, offsetof(struct in6_pktinfo, ipi6_addr), from_zval_write_sin6_addr, to_zval_read_sin6_addr},
-               {"ifindex", sizeof("ifindex"), 1, offsetof(struct in6_pktinfo, ipi6_ifindex), from_zval_write_unsigned, to_zval_read_unsigned},
-               {0}
-};
-static void from_zval_write_in6_pktinfo(const zval *container, char *in6_pktinfo_c, ser_context *ctx)
-{
-       from_zval_write_aggregation(container, in6_pktinfo_c, descriptors_in6_pktinfo, ctx);
-}
-static void to_zval_read_in6_pktinfo(const char *data, zval *zv, res_context *ctx)
-{
-       array_init_size(zv, 2);
-
-       to_zval_read_aggregation(data, zv, descriptors_in6_pktinfo, ctx);
-}
-#endif
-
-/* CONVERSIONS for struct ucred */
-#ifdef SO_PASSCRED
-static const field_descriptor descriptors_ucred[] = {
-               {"pid", sizeof("pid"), 1, offsetof(struct ucred, pid), from_zval_write_pid_t, to_zval_read_pid_t},
-               {"uid", sizeof("uid"), 1, offsetof(struct ucred, uid), from_zval_write_uid_t, to_zval_read_uid_t},
-               /* assume the type gid_t is the same as uid_t: */
-               {"gid", sizeof("gid"), 1, offsetof(struct ucred, gid), from_zval_write_uid_t, to_zval_read_uid_t},
-               {0}
-};
-static void from_zval_write_ucred(const zval *container, char *ucred_c, ser_context *ctx)
-{
-       from_zval_write_aggregation(container, ucred_c, descriptors_ucred, ctx);
-}
-static void to_zval_read_ucred(const char *data, zval *zv, res_context *ctx)
-{
-       array_init_size(zv, 3);
-
-       to_zval_read_aggregation(data, zv, descriptors_ucred, ctx);
-}
-#endif
-
-/* CONVERSIONS for SCM_RIGHTS */
-#ifdef SCM_RIGHTS
-static size_t calculate_scm_rights_space(const zval *arr, ser_context *ctx)
-{
-       int num_elems;
-
-       if (Z_TYPE_P(arr) != IS_ARRAY) {
-               do_from_zval_err(ctx, "%s", "expected an array here");
-               return (size_t)-1;
-       }
-
-       num_elems = zend_hash_num_elements(Z_ARRVAL_P(arr));
-       if (num_elems == 0) {
-               do_from_zval_err(ctx, "%s", "expected at least one element in this array");
-               return (size_t)-1;
-       }
-
-       return zend_hash_num_elements(Z_ARRVAL_P(arr)) * sizeof(int);
-}
-static void from_zval_write_fd_array_aux(zval **elem, unsigned i, void **args, ser_context *ctx)
-{
-       int *iarr = args[0];
-
-       if (Z_TYPE_PP(elem) == IS_RESOURCE) {
-               php_stream *stream;
-               php_socket *sock;
-
-               ZEND_FETCH_RESOURCE_NO_RETURN(sock, php_socket *, elem, -1,
-                               NULL, php_sockets_le_socket());
-               if (sock) {
-                       iarr[i] = sock->bsd_socket;
-                       return;
-               }
-
-               ZEND_FETCH_RESOURCE2_NO_RETURN(stream, php_stream *, elem, -1,
-                               NULL, php_file_le_stream(), php_file_le_pstream());
-               if (stream == NULL) {
-                       do_from_zval_err(ctx, "resource is not a stream or a socket");
-                       return;
-               }
-
-               if (php_stream_cast(stream, PHP_STREAM_AS_FD, (void **)&iarr[i],
-                               REPORT_ERRORS) == FAILURE) {
-                       do_from_zval_err(ctx, "cast stream to file descriptor failed");
-                       return;
-               }
-       } else {
-               do_from_zval_err(ctx, "expected a resource variable");
-       }
-}
-static void from_zval_write_fd_array(const zval *arr, char *int_arr, ser_context *ctx)
-{
-       if (Z_TYPE_P(arr) != IS_ARRAY) {
-               do_from_zval_err(ctx, "%s", "expected an array here");
-               return;
-       }
-
-   from_array_iterate(arr, &from_zval_write_fd_array_aux, (void**)&int_arr, ctx);
-}
-static void to_zval_read_fd_array(const char *data, zval *zv, res_context *ctx)
-{
-       size_t                  **cmsg_len;
-       int                             num_elems,
-                                       i;
-       struct cmsghdr  *dummy_cmsg = 0;
-       size_t                  data_offset;
-       TSRMLS_FETCH();
-
-       data_offset = (unsigned char *)CMSG_DATA(dummy_cmsg)
-                       - (unsigned char *)dummy_cmsg;
-
-       if (zend_hash_find(&ctx->params, KEY_CMSG_LEN, sizeof(KEY_CMSG_LEN),
-                       (void **)&cmsg_len) == FAILURE) {
-               do_to_zval_err(ctx, "could not get value of parameter " KEY_CMSG_LEN);
-               return;
-       }
-
-       if (**cmsg_len < data_offset) {
-               do_to_zval_err(ctx, "length of cmsg is smaller than its data member "
-                               "offset (%ld vs %ld)", (long)**cmsg_len, (long)data_offset);
-               return;
-       }
-       num_elems = (**cmsg_len - data_offset) / sizeof(int);
-
-       array_init_size(zv, num_elems);
-
-       for (i = 0; i < num_elems; i++) {
-               zval            *elem;
-               int                     fd;
-               struct stat     statbuf;
-
-               MAKE_STD_ZVAL(elem);
-
-               fd = *((int *)data + i);
-
-               /* determine whether we have a socket */
-               if (fstat(fd, &statbuf) == -1) {
-                       do_to_zval_err(ctx, "error creating resource for received file "
-                                       "descriptor %d: fstat() call failed with errno %d", fd, errno);
-                       efree(elem);
-                       return;
-               }
-               if (S_ISSOCK(statbuf.st_mode)) {
-                       php_socket *sock = socket_import_file_descriptor(fd);
-                       zend_register_resource(elem, sock, php_sockets_le_socket());
-               } else {
-                       php_stream *stream = php_stream_fopen_from_fd(fd, "rw", NULL);
-                       php_stream_to_zval(stream, elem);
-               }
-
-               add_next_index_zval(zv, elem);
-       }
-}
-#endif
-
-/* ENTRY POINT for conversions */
-static void free_from_zval_allocation(void *alloc_ptr_ptr)
-{
-       efree(*(void**)alloc_ptr_ptr);
-}
-static void *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 /* out */,
-                                                                         struct err_s                  *err /* in/out */)
-{
-       ser_context ctx = {{0}};
-       char *structure = NULL;
-
-       *allocations = NULL;
-
-       if (err->has_error) {
-               return NULL;
-       }
-
-       zend_hash_init(&ctx.params, 8, NULL, NULL, 0);
-       zend_llist_init(&ctx.keys, sizeof(const char *), NULL, 0);
-       zend_llist_init(&ctx.allocations, sizeof(void *), &free_from_zval_allocation, 0);
-       ctx.sock = sock;
-
-       structure = ecalloc(1, struct_size);
-
-       zend_llist_add_element(&ctx.keys, &top_name);
-       zend_llist_add_element(&ctx.allocations, &structure);
-
-       /* main call */
-       writer(container, structure, &ctx);
-
-       if (ctx.err.has_error) {
-               zend_llist_destroy(&ctx.allocations); /* deallocates structure as well */
-               structure = NULL;
-               *err = ctx.err;
-       } else {
-               *allocations = emalloc(sizeof **allocations);
-               **allocations = ctx.allocations;
-       }
-
-       zend_llist_destroy(&ctx.keys);
-       zend_hash_destroy(&ctx.params);
-
-       return structure;
-}
-static zval *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)
-{
-       res_context                             ctx = {{0}, {0}};
-       const struct key_value  *kv;
-       zval                                    *zv = NULL;
-
-       if (err->has_error) {
-               return NULL;
-       }
-
-       ALLOC_INIT_ZVAL(zv);
-
-       zend_llist_init(&ctx.keys, sizeof(const char *), NULL, 0);
-       zend_llist_add_element(&ctx.keys, &top_name);
-
-       zend_hash_init(&ctx.params, 8, NULL, NULL, 0);
-       for (kv = key_value_pairs; kv->key != NULL; kv++) {
-               zend_hash_update(&ctx.params, kv->key, kv->key_size,
-                               (void*)&kv->value, sizeof(kv->value), NULL);
-       }
-
-       /* main call */
-       reader(structure, zv, &ctx);
-
-       if (ctx.err.has_error) {
-               zval_ptr_dtor(&zv);
-               zv = NULL;
-               *err = ctx.err;
-       }
-
-       zend_llist_destroy(&ctx.keys);
-       zend_hash_destroy(&ctx.params);
-
-       return zv;
-}
 
 #ifdef ZTS
 static MUTEX_T ancillary_mutex;
@@ -1555,7 +98,7 @@ static void destroy_ancillary_registry(void)
                ancillary_registry.initialized = 0;
        }
 }
-static ancillary_reg_entry *get_ancillary_reg_entry(int cmsg_level, int msg_type)
+ancillary_reg_entry *get_ancillary_reg_entry(int cmsg_level, int msg_type)
 {
        anc_reg_key                     key = { cmsg_level, msg_type };
        ancillary_reg_entry     *entry;
index 82fb38b4b5f8b4266619c973dfd79c5f2eae55c0..55dca3c1fbc7b1bfff280c5288bcf40a0a48b037 100644 (file)
@@ -1,5 +1,10 @@
+#ifndef PHP_SENDRECVMSG_H
+#define PHP_SENDRECVMSG_H 1
+
 #include <php.h>
+#include "conversions.h"
 
+/* for sockets.c */
 PHP_FUNCTION(socket_sendmsg);
 PHP_FUNCTION(socket_recvmsg);
 PHP_FUNCTION(socket_cmsg_space);
@@ -9,3 +14,23 @@ void php_socket_sendrecvmsg_shutdown(SHUTDOWN_FUNC_ARGS);
 
 int php_do_setsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval **arg4);
 int php_do_getsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *result);
+
+/* for conversions.c */
+typedef struct {
+       int     cmsg_level;     /* originating protocol */
+       int     cmsg_type;      /* protocol-specific type */
+} anc_reg_key;
+
+typedef size_t (calculate_req_space)(const zval *value, ser_context *ctx);
+
+typedef struct {
+       socklen_t size; /* size of native structure */
+       socklen_t var_el_size; /* size of repeatable component */
+       calculate_req_space *calc_space;
+       from_zval_write_field *from_array;
+       to_zval_read_field *to_array;
+} ancillary_reg_entry;
+
+ancillary_reg_entry *get_ancillary_reg_entry(int cmsg_level, int msg_type);
+
+#endif