diff options
author | Gustavo Lopes <glopes@nebm.ist.utl.pt> | 2012-11-06 17:27:08 +0100 |
---|---|---|
committer | Gustavo Lopes <glopes@nebm.ist.utl.pt> | 2013-02-02 16:38:08 +0100 |
commit | 66ea02458746a4853ff6190c6c75da5e95677911 (patch) | |
tree | f73eef992e43432b73d9b78e7b1e626d87983ce1 /ext/sockets | |
parent | b18bd8904e41941db204ac6b2bf4cf43421e8838 (diff) | |
download | php-git-66ea02458746a4853ff6190c6c75da5e95677911.tar.gz |
Support sticky IPV6_PKTINFO
Diffstat (limited to 'ext/sockets')
-rw-r--r-- | ext/sockets/sendrecvmsg.c | 90 | ||||
-rw-r--r-- | ext/sockets/sendrecvmsg.h | 3 | ||||
-rw-r--r-- | ext/sockets/sockets.c | 10 | ||||
-rw-r--r-- | ext/sockets/tests/socket_set_option_in6_pktinfo.phpt | 31 |
4 files changed, 132 insertions, 2 deletions
diff --git a/ext/sockets/sendrecvmsg.c b/ext/sockets/sendrecvmsg.c index 201adbda43..6b1a528c5b 100644 --- a/ext/sockets/sendrecvmsg.c +++ b/ext/sockets/sendrecvmsg.c @@ -77,6 +77,7 @@ struct key_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); @@ -222,6 +223,12 @@ static void err_msg_dispose(struct err_s *err TSRMLS_DC) } } } +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), @@ -1660,8 +1667,7 @@ PHP_FUNCTION(socket_recvmsg) /* we don;t need msghdr anymore; free it */ msghdr = NULL; - zend_llist_destroy(allocations); - efree(allocations); + allocations_dispose(&allocations); zval_dtor(zmsg); if (!err.has_error) { @@ -1723,6 +1729,86 @@ PHP_FUNCTION(socket_cmsg_space) RETURN_LONG((long)CMSG_SPACE(entry->size + n * entry->var_el_size)); } +int php_do_setsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval **arg4) +{ + struct err_s err = {0}; + zend_llist *allocations = NULL; + void *opt_ptr; + socklen_t optlen; + int retval; + + assert(level == IPPROTO_IPV6); + + switch (optname) { +#ifdef IPV6_PKTINFO + case IPV6_PKTINFO: + opt_ptr = from_zval_run_conversions(*arg4, php_sock, from_zval_write_in6_pktinfo, + sizeof(struct in6_pktinfo), "in6_pktinfo", &allocations, &err); + if (err.has_error) { + err_msg_dispose(&err TSRMLS_CC); + return FAILURE; + } + + optlen = sizeof(struct in6_pktinfo); + goto dosockopt; +#endif + } + + /* we also support IPV6_TCLASS, but that can be handled by the default + * integer optval handling in the caller */ + return 1; + +dosockopt: + retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen); + if (retval != 0) { + PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno); + } + allocations_dispose(&allocations); + + return retval != 0 ? FAILURE : SUCCESS; +} + +int php_do_getsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *result) +{ + struct err_s err = {0}; + void *buffer; + socklen_t size; + int res; + to_zval_read_field *reader; + + assert(level == IPPROTO_IPV6); + + switch (optname) { +#ifdef IPV6_PKTINFO + case IPV6_PKTINFO: + size = sizeof(struct in6_pktinfo); + reader = &to_zval_read_in6_pktinfo; + break; +#endif + default: + return 1; + } + + buffer = ecalloc(1, size); + res = getsockopt(php_sock->bsd_socket, level, optname, buffer, &size); + if (res != 0) { + PHP_SOCKET_ERROR(php_sock, "unable to get socket option", errno); + } else { + zval *zv = to_zval_run_conversions(buffer, reader, "in6_pktinfo", + empty_key_value_list, &err); + if (err.has_error) { + err_msg_dispose(&err); + res = -1; + } else { + ZVAL_COPY_VALUE(result, zv); + efree(zv); + } + } + efree(buffer); + + return res == 0 ? SUCCESS : FAILURE; +} + void php_socket_sendrecvmsg_init(INIT_FUNC_ARGS) { /* IPv6 ancillary data diff --git a/ext/sockets/sendrecvmsg.h b/ext/sockets/sendrecvmsg.h index 929a6ad9cf..82fb38b4b5 100644 --- a/ext/sockets/sendrecvmsg.h +++ b/ext/sockets/sendrecvmsg.h @@ -6,3 +6,6 @@ PHP_FUNCTION(socket_cmsg_space); void php_socket_sendrecvmsg_init(INIT_FUNC_ARGS); 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); diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 1d86028691..9c57e2d98d 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -1877,6 +1877,13 @@ PHP_FUNCTION(socket_get_option) } } } + } else if (level == IPPROTO_IPV6) { + int ret = php_do_getsockopt_ipv6_rfc3542(php_sock, level, optname, return_value); + if (ret == SUCCESS) { + return; + } else if (ret == FAILURE) { + RETURN_FALSE; + } /* else continue */ } /* sol_socket options and general case */ @@ -1981,6 +1988,9 @@ PHP_FUNCTION(socket_set_option) #if HAVE_IPV6 else if (level == IPPROTO_IPV6) { int res = php_do_setsockopt_ipv6_mcast(php_sock, level, optname, arg4); + if (res == 1) { + res = php_do_setsockopt_ipv6_rfc3542(php_sock, level, optname, arg4); + } HANDLE_SUBCALL(res); } #endif diff --git a/ext/sockets/tests/socket_set_option_in6_pktinfo.phpt b/ext/sockets/tests/socket_set_option_in6_pktinfo.phpt new file mode 100644 index 0000000000..53320cad0c --- /dev/null +++ b/ext/sockets/tests/socket_set_option_in6_pktinfo.phpt @@ -0,0 +1,31 @@ +--TEST-- +socket_set_option() with IPV6_PKTINFO +--SKIPIF-- +<?php +if (!extension_loaded('sockets')) { +die('skip sockets extension not available.'); +} +if (!defined('IPPROTO_IPV6')) { +die('skip IPv6 not available.'); +} + +--FILE-- +<?php + +$s = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP) or die("err"); +var_dump(socket_set_option($s, IPPROTO_IPV6, IPV6_PKTINFO, [])); +var_dump(socket_set_option($s, IPPROTO_IPV6, IPV6_PKTINFO, [ + "addr" => '::1', + "ifindex" => 0 +])); +//Oddly, Linux does not support IPV6_PKTINFO in sockgetopt(). +//See do_ipv6_getsockopt() on the kernel sources +//A work-around with is sort-of possible (with IPV6_2292PKTOPTIONS), +//but not worth it +//var_dump(socket_get_option($s, IPPROTO_IPV6, IPV6_PKTINFO)); + +--EXPECTF-- +Warning: socket_set_option(): error converting user data (path: in6_pktinfo): The key 'addr' is required in %s on line %d +bool(false) +bool(true) + |