From 4414b33abd087bba26cb2cbdc2bf05938d5a6690 Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Mon, 12 Nov 2012 00:40:38 +0100 Subject: [PATCH] Refactoring: move stuff to new conversions.c --- ext/sockets/config.m4 | 2 +- ext/sockets/conversions.c | 1477 +++++++++++++++++++++++++++++++++++++ ext/sockets/conversions.h | 78 ++ ext/sockets/sendrecvmsg.c | 1461 +----------------------------------- ext/sockets/sendrecvmsg.h | 25 + 5 files changed, 1583 insertions(+), 1460 deletions(-) create mode 100644 ext/sockets/conversions.c create mode 100644 ext/sockets/conversions.h diff --git a/ext/sockets/config.m4 b/ext/sockets/config.m4 index 3b5b90714b..9c75249646 100644 --- a/ext/sockets/config.m4 +++ b/ext/sockets/config.m4 @@ -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 index 0000000000..7ca9972ac0 --- /dev/null +++ b/ext/sockets/conversions.c @@ -0,0 +1,1477 @@ +#include "conversions.h" +#include "sockaddr_conv.h" +#include "conversions.h" +#include "sendrecvmsg.h" /* for ancillary registry */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 index 0000000000..70f31ba676 --- /dev/null +++ b/ext/sockets/conversions.h @@ -0,0 +1,78 @@ +#ifndef PHP_SOCK_CONVERSIONS_H +#define PHP_SOCK_CONVERSIONS_H 1 + +#include +#include +#include +#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 diff --git a/ext/sockets/sendrecvmsg.c b/ext/sockets/sendrecvmsg.c index 6b1a528c5b..6479bf90a8 100644 --- a/ext/sockets/sendrecvmsg.c +++ b/ext/sockets/sendrecvmsg.c @@ -18,18 +18,10 @@ #include #include "php_sockets.h" -#include "sockaddr_conv.h" #include "sendrecvmsg.h" -#include -#include -#include -#include +#include "conversions.h" #include -#include -#include -#include #include -#include #ifdef ZTS #include #endif @@ -47,1460 +39,11 @@ } \ } 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; diff --git a/ext/sockets/sendrecvmsg.h b/ext/sockets/sendrecvmsg.h index 82fb38b4b5..55dca3c1fb 100644 --- a/ext/sockets/sendrecvmsg.h +++ b/ext/sockets/sendrecvmsg.h @@ -1,5 +1,10 @@ +#ifndef PHP_SENDRECVMSG_H +#define PHP_SENDRECVMSG_H 1 + #include +#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 -- 2.40.0