/* 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)); } zend_string_release(addr_str); } 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; zend_string *str = zend_string_alloc(size - 1, 0); memset(str->val, '\0', size); ZVAL_NEW_STR(zv, str); 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); } #endif /* HAVE_IPV6 */ static void from_zval_write_sun_path(const zval *path, char *sockaddr_un_c, ser_context *ctx) { zend_string *path_str; struct sockaddr_un *saddr = (struct sockaddr_un*)sockaddr_un_c; path_str = zval_get_string((zval *) path); /* code in this file relies on the path being nul terminated, even though * this is not required, at least on linux for abstract paths. It also * assumes that the path is not empty */ if (path_str->len == 0) { do_from_zval_err(ctx, "%s", "the path is cannot be empty"); return; } if (path_str->len >= 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, path_str->val, path_str->len); saddr->sun_path[path_str->len] = '\0'; zend_string_release(path_str); }
static void from_zval_write_uid_t(const zval *arr_value, char *field, ser_context *ctx) { zend_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(zend_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(zend_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)); }
/* 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 ((elem = zend_hash_str_find(Z_ARRVAL_P(container), descr->name, descr->name_size - 1)) != NULL) { 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; } } }
/* CONVERSIONS for integers */ static zend_long from_zval_integer_common(const zval *arr_value, ser_context *ctx) { zend_long ret = 0; zval lzval; ZVAL_NULL(&lzval); if (Z_TYPE_P(arr_value) != IS_LONG) { ZVAL_COPY(&lzval, (zval *)arr_value); 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: { zend_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); ZVAL_DOUBLE(&lzval, dval); goto double_case; case IS_LONG: zval_dtor(&lzval); ZVAL_LONG(&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_sin6_addr(const zval *zaddr_str, char *addr6, ser_context *ctx) { int res; struct sockaddr_in6 saddr6 = {0}; zend_string *addr_str; addr_str = zval_get_string((zval *) zaddr_str); res = php_set_inet6_addr(&saddr6, addr_str->val, ctx->sock); 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)); } zend_string_release(addr_str); }
/* 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}; zend_string *addr_str, *tmp_addr_str; addr_str = zval_get_tmp_string((zval *) zaddr_str, &tmp_addr_str); res = php_set_inet_addr(&saddr, ZSTR_VAL(addr_str), ctx->sock); 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", ZSTR_VAL(addr_str)); } zend_tmp_string_release(tmp_addr_str); }
static void from_zval_write_pid_t(const zval *arr_value, char *field, ser_context *ctx) { zend_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_sa_family(const zval *arr_value, char *field, ser_context *ctx) { zend_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_net_uint16(const zval *arr_value, char *field, ser_context *ctx) { zend_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)); }
void from_zval_write_int(const zval *arr_value, char *field, ser_context *ctx) { zend_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_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 ((elem = zend_hash_str_find(Z_ARRVAL_P(container), "family", sizeof("family") - 1)) != NULL && Z_TYPE_P(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; #if HAVE_IPV6 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; #endif /* HAVE_IPV6 */ 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); if (fill_sockaddr) { struct sockaddr_un *sock_un = (struct sockaddr_un*)*sockaddr_ptr; from_zval_write_sockaddr_un(container, (char*)*sockaddr_ptr, ctx); (*sockaddr_ptr)->sa_family = AF_UNIX; /* calculating length is more complicated here. Giving the size of * struct sockaddr_un here and relying on the nul termination of * sun_path does not work for paths in the abstract namespace. Note * that we always assume the path is not empty and nul terminated */ *sockaddr_len = offsetof(struct sockaddr_un, sun_path) + (sock_un->sun_path[0] == '\0' ? (1 + strlen(&sock_un->sun_path[1])) : strlen(sock_un->sun_path)); } else { *sockaddr_len = sizeof(struct sockaddr_un); } break; default: do_from_zval_err(ctx, "%s", "the only families currently supported are " "AF_INET, AF_INET6 and AF_UNIX"); break; }