summaryrefslogtreecommitdiff
path: root/ext/sockets
diff options
context:
space:
mode:
authorGustavo Lopes <glopes@nebm.ist.utl.pt>2012-11-06 17:27:08 +0100
committerGustavo Lopes <glopes@nebm.ist.utl.pt>2013-02-02 16:38:08 +0100
commit66ea02458746a4853ff6190c6c75da5e95677911 (patch)
treef73eef992e43432b73d9b78e7b1e626d87983ce1 /ext/sockets
parentb18bd8904e41941db204ac6b2bf4cf43421e8838 (diff)
downloadphp-git-66ea02458746a4853ff6190c6c75da5e95677911.tar.gz
Support sticky IPV6_PKTINFO
Diffstat (limited to 'ext/sockets')
-rw-r--r--ext/sockets/sendrecvmsg.c90
-rw-r--r--ext/sockets/sendrecvmsg.h3
-rw-r--r--ext/sockets/sockets.c10
-rw-r--r--ext/sockets/tests/socket_set_option_in6_pktinfo.phpt31
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)
+