Skip to content

Commit

Permalink
ext/sockets: linux AF_PACKET support.
Browse files Browse the repository at this point in the history
  • Loading branch information
devnexen committed Jan 11, 2025
1 parent 11937b3 commit d1090a5
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 18 deletions.
2 changes: 1 addition & 1 deletion ext/sockets/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -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.])

Expand Down
91 changes: 78 additions & 13 deletions ext/sockets/sockets.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -91,6 +97,19 @@ ZEND_DECLARE_MODULE_GLOBALS(sockets)
#define PF_INET AF_INET
#endif

#if defined(ETH_P_ALL)
#define PHP_ETH_PROTO_CHECK(protocol, family) \
do { \
/* We ll let EINVAL errno warning about miusage, too many protocols conflicts but AF_PACKET family works only with ETH_P_* constants */ \
if ((protocol >= ETH_P_LOOP && 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);
Expand Down Expand Up @@ -960,13 +979,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;
Expand All @@ -977,7 +999,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);
Expand All @@ -997,8 +1019,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;
Expand All @@ -1008,8 +1030,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;
Expand All @@ -1020,9 +1042,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();
}
}
Expand Down Expand Up @@ -1117,9 +1156,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();
}

Expand All @@ -1138,6 +1180,8 @@ PHP_FUNCTION(socket_create)
RETURN_THROWS();
}

PHP_ETH_PROTO_CHECK(protocol, protocol);

object_init_ex(return_value, socket_ce);
php_sock = Z_SOCKET_P(return_value);

Expand Down Expand Up @@ -1275,20 +1319,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();
}
Expand Down Expand Up @@ -1316,7 +1360,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;
Expand All @@ -1331,7 +1375,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;
Expand All @@ -1340,6 +1384,23 @@ 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");
Expand Down Expand Up @@ -2702,6 +2763,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);

Expand Down Expand Up @@ -2765,6 +2828,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);

Expand Down
33 changes: 31 additions & 2 deletions ext/sockets/sockets.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@
*/
const AF_DIVERT = UNKNOWN;
#endif
#ifdef AF_PACKET
/**
* @var int
* @cvalue AF_PACKET
*/
const AF_PACKET = UNKNOWN;
#endif
/**
* @var int
* @cvalue SOCK_STREAM
Expand Down Expand Up @@ -1978,6 +1985,28 @@
*/
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
Expand Down Expand Up @@ -2017,13 +2046,13 @@ function socket_read(Socket $socket, int $length, int $mode = PHP_BINARY_READ):
* @param string $address
* @param int $port
*/
function socket_getsockname(Socket $socket, &$address, &$port = null): bool {}
function socket_getsockname(Socket $socket, &$address, ?int &$objint = null): bool {}

/**
* @param string $address
* @param int $port
*/
function socket_getpeername(Socket $socket, &$address, &$port = null): bool {}
function socket_getpeername(Socket $socket, &$address, ?int &$objint = null): bool {}

function socket_create(int $domain, int $type, int $protocol): Socket|false {}

Expand Down
19 changes: 17 additions & 2 deletions ext/sockets/sockets_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions ext/sockets/tests/socket_afpacket.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
socket_getsockname from AF_PACKET socket
--EXTENSIONS--
sockets
--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);

// Connect to destination 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

0 comments on commit d1090a5

Please sign in to comment.