Skip to content

Commit 66ea024

Browse files
committed
Support sticky IPV6_PKTINFO
1 parent b18bd89 commit 66ea024

File tree

4 files changed

+132
-2
lines changed

4 files changed

+132
-2
lines changed

Diff for: ext/sockets/sendrecvmsg.c

+88-2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ struct key_value {
7777
#define KEY_FILL_SOCKADDR "fill_sockaddr"
7878
#define KEY_RECVMSG_RET "recvmsg_ret"
7979
#define KEY_CMSG_LEN "cmsg_len"
80+
static const struct key_value empty_key_value_list[] = {{0}};
8081

8182

8283
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)
222223
}
223224
}
224225
}
226+
static void allocations_dispose(zend_llist **allocations)
227+
{
228+
zend_llist_destroy(*allocations);
229+
efree(*allocations);
230+
*allocations = NULL;
231+
}
225232

226233
static unsigned from_array_iterate(const zval *arr,
227234
void (*func)(zval **elem, unsigned i, void **args, ser_context *ctx),
@@ -1660,8 +1667,7 @@ PHP_FUNCTION(socket_recvmsg)
16601667

16611668
/* we don;t need msghdr anymore; free it */
16621669
msghdr = NULL;
1663-
zend_llist_destroy(allocations);
1664-
efree(allocations);
1670+
allocations_dispose(&allocations);
16651671

16661672
zval_dtor(zmsg);
16671673
if (!err.has_error) {
@@ -1723,6 +1729,86 @@ PHP_FUNCTION(socket_cmsg_space)
17231729
RETURN_LONG((long)CMSG_SPACE(entry->size + n * entry->var_el_size));
17241730
}
17251731

1732+
int php_do_setsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval **arg4)
1733+
{
1734+
struct err_s err = {0};
1735+
zend_llist *allocations = NULL;
1736+
void *opt_ptr;
1737+
socklen_t optlen;
1738+
int retval;
1739+
1740+
assert(level == IPPROTO_IPV6);
1741+
1742+
switch (optname) {
1743+
#ifdef IPV6_PKTINFO
1744+
case IPV6_PKTINFO:
1745+
opt_ptr = from_zval_run_conversions(*arg4, php_sock, from_zval_write_in6_pktinfo,
1746+
sizeof(struct in6_pktinfo), "in6_pktinfo", &allocations, &err);
1747+
if (err.has_error) {
1748+
err_msg_dispose(&err TSRMLS_CC);
1749+
return FAILURE;
1750+
}
1751+
1752+
optlen = sizeof(struct in6_pktinfo);
1753+
goto dosockopt;
1754+
#endif
1755+
}
1756+
1757+
/* we also support IPV6_TCLASS, but that can be handled by the default
1758+
* integer optval handling in the caller */
1759+
return 1;
1760+
1761+
dosockopt:
1762+
retval = setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen);
1763+
if (retval != 0) {
1764+
PHP_SOCKET_ERROR(php_sock, "unable to set socket option", errno);
1765+
}
1766+
allocations_dispose(&allocations);
1767+
1768+
return retval != 0 ? FAILURE : SUCCESS;
1769+
}
1770+
1771+
int php_do_getsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *result)
1772+
{
1773+
struct err_s err = {0};
1774+
void *buffer;
1775+
socklen_t size;
1776+
int res;
1777+
to_zval_read_field *reader;
1778+
1779+
assert(level == IPPROTO_IPV6);
1780+
1781+
switch (optname) {
1782+
#ifdef IPV6_PKTINFO
1783+
case IPV6_PKTINFO:
1784+
size = sizeof(struct in6_pktinfo);
1785+
reader = &to_zval_read_in6_pktinfo;
1786+
break;
1787+
#endif
1788+
default:
1789+
return 1;
1790+
}
1791+
1792+
buffer = ecalloc(1, size);
1793+
res = getsockopt(php_sock->bsd_socket, level, optname, buffer, &size);
1794+
if (res != 0) {
1795+
PHP_SOCKET_ERROR(php_sock, "unable to get socket option", errno);
1796+
} else {
1797+
zval *zv = to_zval_run_conversions(buffer, reader, "in6_pktinfo",
1798+
empty_key_value_list, &err);
1799+
if (err.has_error) {
1800+
err_msg_dispose(&err);
1801+
res = -1;
1802+
} else {
1803+
ZVAL_COPY_VALUE(result, zv);
1804+
efree(zv);
1805+
}
1806+
}
1807+
efree(buffer);
1808+
1809+
return res == 0 ? SUCCESS : FAILURE;
1810+
}
1811+
17261812
void php_socket_sendrecvmsg_init(INIT_FUNC_ARGS)
17271813
{
17281814
/* IPv6 ancillary data

Diff for: ext/sockets/sendrecvmsg.h

+3
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ PHP_FUNCTION(socket_cmsg_space);
66

77
void php_socket_sendrecvmsg_init(INIT_FUNC_ARGS);
88
void php_socket_sendrecvmsg_shutdown(SHUTDOWN_FUNC_ARGS);
9+
10+
int php_do_setsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval **arg4);
11+
int php_do_getsockopt_ipv6_rfc3542(php_socket *php_sock, int level, int optname, zval *result);

Diff for: ext/sockets/sockets.c

+10
Original file line numberDiff line numberDiff line change
@@ -1877,6 +1877,13 @@ PHP_FUNCTION(socket_get_option)
18771877
}
18781878
}
18791879
}
1880+
} else if (level == IPPROTO_IPV6) {
1881+
int ret = php_do_getsockopt_ipv6_rfc3542(php_sock, level, optname, return_value);
1882+
if (ret == SUCCESS) {
1883+
return;
1884+
} else if (ret == FAILURE) {
1885+
RETURN_FALSE;
1886+
} /* else continue */
18801887
}
18811888

18821889
/* sol_socket options and general case */
@@ -1981,6 +1988,9 @@ PHP_FUNCTION(socket_set_option)
19811988
#if HAVE_IPV6
19821989
else if (level == IPPROTO_IPV6) {
19831990
int res = php_do_setsockopt_ipv6_mcast(php_sock, level, optname, arg4);
1991+
if (res == 1) {
1992+
res = php_do_setsockopt_ipv6_rfc3542(php_sock, level, optname, arg4);
1993+
}
19841994
HANDLE_SUBCALL(res);
19851995
}
19861996
#endif

Diff for: ext/sockets/tests/socket_set_option_in6_pktinfo.phpt

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
socket_set_option() with IPV6_PKTINFO
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('sockets')) {
6+
die('skip sockets extension not available.');
7+
}
8+
if (!defined('IPPROTO_IPV6')) {
9+
die('skip IPv6 not available.');
10+
}
11+
12+
--FILE--
13+
<?php
14+
15+
$s = socket_create(AF_INET6, SOCK_DGRAM, SOL_UDP) or die("err");
16+
var_dump(socket_set_option($s, IPPROTO_IPV6, IPV6_PKTINFO, []));
17+
var_dump(socket_set_option($s, IPPROTO_IPV6, IPV6_PKTINFO, [
18+
"addr" => '::1',
19+
"ifindex" => 0
20+
]));
21+
//Oddly, Linux does not support IPV6_PKTINFO in sockgetopt().
22+
//See do_ipv6_getsockopt() on the kernel sources
23+
//A work-around with is sort-of possible (with IPV6_2292PKTOPTIONS),
24+
//but not worth it
25+
//var_dump(socket_get_option($s, IPPROTO_IPV6, IPV6_PKTINFO));
26+
27+
--EXPECTF--
28+
Warning: socket_set_option(): error converting user data (path: in6_pktinfo): The key 'addr' is required in %s on line %d
29+
bool(false)
30+
bool(true)
31+

0 commit comments

Comments
 (0)