1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

ext/sockets: linux AF_PACKET support.

close GH-17440
This commit is contained in:
David Carlier
2025-01-10 21:49:22 +00:00
parent 9df1c930bf
commit 2ea386a516
7 changed files with 225 additions and 20 deletions

10
NEWS
View File

@@ -96,10 +96,6 @@ PHP NEWS
. Fixed bug #49169 (SoapServer calls wrong function, although "SOAP action"
header is correct). (nielsdos)
- Sodium:
. Fix overall theorical overflows on zend_string buffer allocations.
(David Carlier/nielsdos)
- Sockets:
. Added IPPROTO_ICMP/IPPROTO_ICMPV6 to create raw socket for ICMP usage.
(David Carlier)
@@ -124,6 +120,12 @@ PHP NEWS
. socket_set_option with the options MCAST_LEAVE_GROUP/MCAST_LEAVE_SOURCE_GROUP
will throw an exception if its value is not a valid array/object.
(David Carlier)
. socket_getsockname/socket_create/socket_bind handled AF_PACKET family socket.
(David Carlier)
- Sodium:
. Fix overall theorical overflows on zend_string buffer allocations.
(David Carlier/nielsdos)
- Standard:
. Fixed crypt() tests on musl when using --with-external-libcrypt

View File

@@ -164,6 +164,9 @@ PHP 8.5 UPGRADE NOTES
. socket_set_option with MCAST_LEAVE_GROUP/MCAST_LEAVE_SOURCE_GROUP
options will throw an exception if the value isn't a valid object
or array.
. socket_create/socket_bind can create AF_PACKET family sockets.
. socket_getsockname gets the interface index and its string
representation with AF_PACKET socket.
- Zlib:
. The "use_include_path" argument for the
@@ -241,6 +244,7 @@ PHP 8.5 UPGRADE NOTES
. TCP_REUSPORT_LB_NUMA_NODOM (FreeBSD only).
. TCP_REUSPORT_LB_NUMA_CURDOM (FreeBSD only).
. TCP_BBR_ALGORITHM (FreeBSD only).
. AF_PACKET (Linux only).
========================================
11. Changes to INI File Handling

View File

@@ -5,7 +5,7 @@ PHP_ARG_ENABLE([sockets],
if test "$PHP_SOCKETS" != "no"; then
AC_CHECK_FUNCS([hstrerror if_nametoindex if_indextoname sockatmark])
AC_CHECK_HEADERS([sys/sockio.h linux/filter.h])
AC_CHECK_HEADERS([sys/sockio.h linux/filter.h linux/if_packet.h linux/if_ether.h])
AC_DEFINE([HAVE_SOCKETS], [1],
[Define to 1 if the PHP extension 'sockets' is available.])

View File

@@ -64,6 +64,12 @@
# else
# undef SO_BPF_EXTENSIONS
# endif
# if defined(HAVE_LINUX_IF_PACKET_H)
# include <linux/if_packet.h>
# endif
# if defined(HAVE_LINUX_IF_ETHER_H)
# include <linux/if_ether.h>
# endif
#endif
#include <stddef.h>
@@ -91,6 +97,18 @@ ZEND_DECLARE_MODULE_GLOBALS(sockets)
#define PF_INET AF_INET
#endif
#if defined(AF_PACKET)
#define PHP_ETH_PROTO_CHECK(protocol, family) \
do { \
/* We ll let EINVAL errno warning about miusage, too many protocols conflicts */ \
if (protocol <= USHRT_MAX && family == AF_PACKET) { \
protocol = htons(protocol); \
} \
} while (0)
#else
#define PHP_ETH_PROTO_CHECK(protocol, family) (0)
#endif
static PHP_GINIT_FUNCTION(sockets);
static PHP_GSHUTDOWN_FUNCTION(sockets);
static PHP_MINIT_FUNCTION(sockets);
@@ -960,13 +978,16 @@ PHP_FUNCTION(socket_read)
/* {{{ Queries the remote side of the given socket which may either result in host/port or in a UNIX filesystem path, dependent on its type. */
PHP_FUNCTION(socket_getsockname)
{
zval *arg1, *addr, *port = NULL;
zval *arg1, *addr, *objint = NULL;
php_sockaddr_storage sa_storage = {0};
php_socket *php_sock;
struct sockaddr *sa;
struct sockaddr_in *sin;
#ifdef HAVE_IPV6
struct sockaddr_in6 *sin6;
#endif
#ifdef AF_PACKET
struct sockaddr_ll *sll;
#endif
char addrbuf[INET6_ADDRSTRLEN];
struct sockaddr_un *s_un;
@@ -977,7 +998,7 @@ PHP_FUNCTION(socket_getsockname)
Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
Z_PARAM_ZVAL(addr)
Z_PARAM_OPTIONAL
Z_PARAM_ZVAL(port)
Z_PARAM_ZVAL(objint)
ZEND_PARSE_PARAMETERS_END();
php_sock = Z_SOCKET_P(arg1);
@@ -997,8 +1018,8 @@ PHP_FUNCTION(socket_getsockname)
inet_ntop(AF_INET6, &sin6->sin6_addr, addrbuf, sizeof(addrbuf));
ZEND_TRY_ASSIGN_REF_STRING(addr, addrbuf);
if (port != NULL) {
ZEND_TRY_ASSIGN_REF_LONG(port, htons(sin6->sin6_port));
if (objint != NULL) {
ZEND_TRY_ASSIGN_REF_LONG(objint, htons(sin6->sin6_port));
}
RETURN_TRUE;
break;
@@ -1008,8 +1029,8 @@ PHP_FUNCTION(socket_getsockname)
addr_string = inet_ntop(AF_INET, &sin->sin_addr, addrbuf, sizeof(addrbuf));
ZEND_TRY_ASSIGN_REF_STRING(addr, addr_string);
if (port != NULL) {
ZEND_TRY_ASSIGN_REF_LONG(port, htons(sin->sin_port));
if (objint != NULL) {
ZEND_TRY_ASSIGN_REF_LONG(objint, htons(sin->sin_port));
}
RETURN_TRUE;
break;
@@ -1020,9 +1041,26 @@ PHP_FUNCTION(socket_getsockname)
ZEND_TRY_ASSIGN_REF_STRING(addr, s_un->sun_path);
RETURN_TRUE;
break;
#ifdef AF_PACKET
case AF_PACKET:
sll = (struct sockaddr_ll *) sa;
char ifrname[IFNAMSIZ];
if (UNEXPECTED(!if_indextoname(sll->sll_ifindex, ifrname))) {
zend_throw_error(NULL, "invalid interface index");
RETURN_THROWS();
}
ZEND_TRY_ASSIGN_REF_STRING(addr, ifrname);
if (objint != NULL) {
ZEND_TRY_ASSIGN_REF_LONG(objint, sll->sll_ifindex);
}
RETURN_TRUE;
break;
#endif
default:
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET, or AF_INET6");
RETURN_THROWS();
}
}
@@ -1117,9 +1155,12 @@ PHP_FUNCTION(socket_create)
if (domain != AF_UNIX
#ifdef HAVE_IPV6
&& domain != AF_INET6
#endif
#ifdef AF_PACKET
&& domain != AF_PACKET
#endif
&& domain != AF_INET) {
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET6, or AF_INET");
zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET6, or AF_INET");
RETURN_THROWS();
}
@@ -1138,6 +1179,8 @@ PHP_FUNCTION(socket_create)
RETURN_THROWS();
}
PHP_ETH_PROTO_CHECK(protocol, domain);
object_init_ex(return_value, socket_ce);
php_sock = Z_SOCKET_P(return_value);
@@ -1275,20 +1318,20 @@ PHP_FUNCTION(socket_bind)
php_socket *php_sock;
char *addr;
size_t addr_len;
zend_long port = 0;
zend_long objint = 0;
zend_long retval = 0;
ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
Z_PARAM_STRING(addr, addr_len)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(port)
Z_PARAM_LONG(objint)
ZEND_PARSE_PARAMETERS_END();
php_sock = Z_SOCKET_P(arg1);
ENSURE_SOCKET_VALID(php_sock);
if (port < 0 || port > USHRT_MAX) {
if (objint < 0 || objint > USHRT_MAX) {
zend_argument_value_error(3, "must be between 0 and %u", USHRT_MAX);
RETURN_THROWS();
}
@@ -1316,7 +1359,7 @@ PHP_FUNCTION(socket_bind)
struct sockaddr_in *sa = (struct sockaddr_in *) sock_type;
sa->sin_family = AF_INET;
sa->sin_port = htons((unsigned short) port);
sa->sin_port = htons((unsigned short) objint);
if (! php_set_inet_addr(sa, addr, php_sock)) {
RETURN_FALSE;
@@ -1331,7 +1374,7 @@ PHP_FUNCTION(socket_bind)
struct sockaddr_in6 *sa = (struct sockaddr_in6 *) sock_type;
sa->sin6_family = AF_INET6;
sa->sin6_port = htons((unsigned short) port);
sa->sin6_port = htons((unsigned short) objint);
if (! php_set_inet6_addr(sa, addr, php_sock)) {
RETURN_FALSE;
@@ -1340,9 +1383,26 @@ PHP_FUNCTION(socket_bind)
retval = bind(php_sock->bsd_socket, (struct sockaddr *)sa, sizeof(struct sockaddr_in6));
break;
}
#endif
#ifdef AF_PACKET
case AF_PACKET:
{
struct sockaddr_ll *sa = (struct sockaddr_ll *) sock_type;
socklen_t sa_len = sizeof(sa);
if (getsockname(php_sock->bsd_socket, sock_type, &sa_len) < 0) {
zend_value_error("invalid AF_PACKET socket");
RETURN_THROWS();
}
sa->sll_ifindex = if_nametoindex(addr);
retval = bind(php_sock->bsd_socket, sock_type, sizeof(struct sockaddr_ll));
break;
}
#endif
default:
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET, or AF_INET6");
RETURN_THROWS();
}
@@ -1443,6 +1503,9 @@ PHP_FUNCTION(socket_recvfrom)
struct sockaddr_in sin;
#ifdef HAVE_IPV6
struct sockaddr_in6 sin6;
#endif
#ifdef AF_PACKET
//struct sockaddr_ll sll;
#endif
char addrbuf[INET6_ADDRSTRLEN];
socklen_t slen;
@@ -1547,6 +1610,38 @@ PHP_FUNCTION(socket_recvfrom)
ZEND_TRY_ASSIGN_REF_STRING(arg5, addrbuf[0] ? addrbuf : "::");
ZEND_TRY_ASSIGN_REF_LONG(arg6, ntohs(sin6.sin6_port));
break;
#endif
#ifdef AF_PACKET
/*
case AF_PACKET:
// TODO expose and use proper ethernet frame type instead i.e. src mac, dst mac and payload to userland
// ditto for socket_sendto
slen = sizeof(sll);
memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
char ifrname[IFNAMSIZ];
retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), arg3, arg4, (struct sockaddr *)&sll, (socklen_t *)&slen);
if (retval < 0) {
PHP_SOCKET_ERROR(php_sock, "unable to recvfrom", errno);
zend_string_efree(recv_buf);
RETURN_FALSE;
}
ZSTR_LEN(recv_buf) = retval;
ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0';
if (UNEXPECTED(!if_indextoname(sll.sll_ifindex, ifrname))) {
PHP_SOCKET_ERROR(php_sock, "unable to get the interface name", errno);
zend_string_efree(recv_buf);
RETURN_FALSE;
}
ZEND_TRY_ASSIGN_REF_NEW_STR(arg2, recv_buf);
ZEND_TRY_ASSIGN_REF_STRING(arg5, ifrname);
ZEND_TRY_ASSIGN_REF_LONG(arg6, sll.sll_ifindex);
break;
*/
#endif
default:
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
@@ -1566,6 +1661,9 @@ PHP_FUNCTION(socket_sendto)
struct sockaddr_in sin;
#ifdef HAVE_IPV6
struct sockaddr_in6 sin6;
#endif
#ifdef AF_PACKET
//struct sockaddr_ll sll;
#endif
int retval;
size_t buf_len, addr_len;
@@ -1639,6 +1737,22 @@ PHP_FUNCTION(socket_sendto)
retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin6, sizeof(sin6));
break;
#endif
#ifdef AF_PACKET
/*
case AF_PACKET:
if (port_is_null) {
zend_argument_value_error(6, "cannot be null when the socket type is AF_PACKET");
RETURN_THROWS();
}
memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = port;
retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin, sizeof(sin));
break;
*/
#endif
default:
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
@@ -2702,6 +2816,8 @@ PHP_FUNCTION(socket_addrinfo_bind)
ai = Z_ADDRESS_INFO_P(arg1);
PHP_ETH_PROTO_CHECK(ai->addrinfo.ai_protocol, ai->addrinfo.ai_family);
object_init_ex(return_value, socket_ce);
php_sock = Z_SOCKET_P(return_value);
@@ -2765,6 +2881,8 @@ PHP_FUNCTION(socket_addrinfo_connect)
ai = Z_ADDRESS_INFO_P(arg1);
PHP_ETH_PROTO_CHECK(ai->addrinfo.ai_protocol, ai->addrinfo.ai_family);
object_init_ex(return_value, socket_ce);
php_sock = Z_SOCKET_P(return_value);

View File

@@ -26,6 +26,13 @@ const AF_INET6 = UNKNOWN;
*/
const AF_DIVERT = UNKNOWN;
#endif
#ifdef AF_PACKET
/**
* @var int
* @cvalue AF_PACKET
*/
const AF_PACKET = UNKNOWN;
#endif
/**
* @var int
* @cvalue SOCK_STREAM
@@ -1978,6 +1985,28 @@ const UDPLITE_SEND_CSCOV = UNKNOWN;
*/
const UDPLITE_RECV_CSCOV = UNKNOWN;
#endif
#if defined(ETH_P_ALL)
/**
* @var int
* @cvalue ETH_P_IP
*/
const ETH_P_IP = UNKNOWN;
/**
* @var int
* @cvalue ETH_P_IPV6
*/
const ETH_P_IPV6 = UNKNOWN;
/**
* @var int
* @cvalue ETH_P_LOOP
*/
const ETH_P_LOOP = UNKNOWN;
/**
* @var int
* @cvalue ETH_P_ALL
*/
const ETH_P_ALL = UNKNOWN;
#endif
/**
* @strict-properties

View File

@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 2c5f4685d5dab42426d4fe0553dd17cb9935a572 */
* Stub hash: 0be24cb2f268ab3d43121637ae451d8da4b50410 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_socket_select, 0, 4, MAY_BE_LONG|MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(1, read, IS_ARRAY, 1)
@@ -319,6 +319,9 @@ static void register_sockets_symbols(int module_number)
#endif
#if defined(AF_DIVERT)
REGISTER_LONG_CONSTANT("AF_DIVERT", AF_DIVERT, CONST_PERSISTENT);
#endif
#if defined(AF_PACKET)
REGISTER_LONG_CONSTANT("AF_PACKET", AF_PACKET, CONST_PERSISTENT);
#endif
REGISTER_LONG_CONSTANT("SOCK_STREAM", SOCK_STREAM, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SOCK_DGRAM", SOCK_DGRAM, CONST_PERSISTENT);
@@ -1087,6 +1090,18 @@ static void register_sockets_symbols(int module_number)
#if defined(UDPLITE_RECV_CSCOV)
REGISTER_LONG_CONSTANT("UDPLITE_RECV_CSCOV", UDPLITE_RECV_CSCOV, CONST_PERSISTENT);
#endif
#if defined(ETH_P_ALL)
REGISTER_LONG_CONSTANT("ETH_P_IP", ETH_P_IP, CONST_PERSISTENT);
#endif
#if defined(ETH_P_ALL)
REGISTER_LONG_CONSTANT("ETH_P_IPV6", ETH_P_IPV6, CONST_PERSISTENT);
#endif
#if defined(ETH_P_ALL)
REGISTER_LONG_CONSTANT("ETH_P_LOOP", ETH_P_LOOP, CONST_PERSISTENT);
#endif
#if defined(ETH_P_ALL)
REGISTER_LONG_CONSTANT("ETH_P_ALL", ETH_P_ALL, CONST_PERSISTENT);
#endif
}
static zend_class_entry *register_class_Socket(void)

View File

@@ -0,0 +1,37 @@
--TEST--
socket_getsockname from AF_PACKET socket
--EXTENSIONS--
sockets
posix
--SKIPIF--
<?php
if (!defined("AF_PACKET")) {
die('SKIP AF_PACKET not supported on this platform.');
}
if (!function_exists("posix_getuid") || posix_getuid() != 0) {
die('SKIP AF_PACKET requires root permissions.');
}
?>
--FILE--
<?php
$s_c = socket_create(AF_PACKET, SOCK_RAW, ETH_P_IP);
$s_bind = socket_bind($s_c, 'lo');
var_dump($s_bind);
// sock_getsockname in this context gets the interface rather than the address.
$s_conn = socket_getsockname($s_c, $istr, $iindex);
var_dump($s_conn);
var_dump($istr);
var_dump($iindex);
socket_getpeername($s_c, $istr2, $iindex2);
socket_close($s_c);
?>
--EXPECTF--
bool(true)
bool(true)
string(2) "lo"
int(%i)
Warning: socket_getpeername(): unable to retrieve peer name [95]: Operation not supported in %s on line %d