Skip to content

Commit f27a478

Browse files
committed
ext/sockets: linux AF_PACKET support.
1 parent 9283abc commit f27a478

File tree

5 files changed

+218
-19
lines changed

5 files changed

+218
-19
lines changed

ext/sockets/config.m4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ PHP_ARG_ENABLE([sockets],
55

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

ext/sockets/sockets.c

Lines changed: 132 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@
6464
# else
6565
# undef SO_BPF_EXTENSIONS
6666
# endif
67+
# if defined(HAVE_LINUX_IF_PACKET_H)
68+
# include <linux/if_packet.h>
69+
# endif
70+
# if defined(HAVE_LINUX_IF_ETHER_H)
71+
# include <linux/if_ether.h>
72+
# endif
6773
#endif
6874

6975
#include <stddef.h>
@@ -91,6 +97,18 @@ ZEND_DECLARE_MODULE_GLOBALS(sockets)
9197
#define PF_INET AF_INET
9298
#endif
9399

100+
#if defined(AF_PACKET)
101+
#define PHP_ETH_PROTO_CHECK(protocol, family) \
102+
do { \
103+
/* We ll let EINVAL errno warning about miusage, too many protocols conflicts */ \
104+
if (protocol <= USHRT_MAX && family == AF_PACKET) { \
105+
protocol = htons(protocol); \
106+
} \
107+
} while (0)
108+
#else
109+
#define PHP_ETH_PROTO_CHECK(protocol, family) (0)
110+
#endif
111+
94112
static PHP_GINIT_FUNCTION(sockets);
95113
static PHP_GSHUTDOWN_FUNCTION(sockets);
96114
static PHP_MINIT_FUNCTION(sockets);
@@ -960,13 +978,16 @@ PHP_FUNCTION(socket_read)
960978
/* {{{ 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. */
961979
PHP_FUNCTION(socket_getsockname)
962980
{
963-
zval *arg1, *addr, *port = NULL;
981+
zval *arg1, *addr, *objint = NULL;
964982
php_sockaddr_storage sa_storage = {0};
965983
php_socket *php_sock;
966984
struct sockaddr *sa;
967985
struct sockaddr_in *sin;
968986
#ifdef HAVE_IPV6
969987
struct sockaddr_in6 *sin6;
988+
#endif
989+
#ifdef AF_PACKET
990+
struct sockaddr_ll *sll;
970991
#endif
971992
char addrbuf[INET6_ADDRSTRLEN];
972993
struct sockaddr_un *s_un;
@@ -977,7 +998,7 @@ PHP_FUNCTION(socket_getsockname)
977998
Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
978999
Z_PARAM_ZVAL(addr)
9791000
Z_PARAM_OPTIONAL
980-
Z_PARAM_ZVAL(port)
1001+
Z_PARAM_ZVAL(objint)
9811002
ZEND_PARSE_PARAMETERS_END();
9821003

9831004
php_sock = Z_SOCKET_P(arg1);
@@ -997,8 +1018,8 @@ PHP_FUNCTION(socket_getsockname)
9971018
inet_ntop(AF_INET6, &sin6->sin6_addr, addrbuf, sizeof(addrbuf));
9981019
ZEND_TRY_ASSIGN_REF_STRING(addr, addrbuf);
9991020

1000-
if (port != NULL) {
1001-
ZEND_TRY_ASSIGN_REF_LONG(port, htons(sin6->sin6_port));
1021+
if (objint != NULL) {
1022+
ZEND_TRY_ASSIGN_REF_LONG(objint, htons(sin6->sin6_port));
10021023
}
10031024
RETURN_TRUE;
10041025
break;
@@ -1008,8 +1029,8 @@ PHP_FUNCTION(socket_getsockname)
10081029
addr_string = inet_ntop(AF_INET, &sin->sin_addr, addrbuf, sizeof(addrbuf));
10091030
ZEND_TRY_ASSIGN_REF_STRING(addr, addr_string);
10101031

1011-
if (port != NULL) {
1012-
ZEND_TRY_ASSIGN_REF_LONG(port, htons(sin->sin_port));
1032+
if (objint != NULL) {
1033+
ZEND_TRY_ASSIGN_REF_LONG(objint, htons(sin->sin_port));
10131034
}
10141035
RETURN_TRUE;
10151036
break;
@@ -1020,9 +1041,26 @@ PHP_FUNCTION(socket_getsockname)
10201041
ZEND_TRY_ASSIGN_REF_STRING(addr, s_un->sun_path);
10211042
RETURN_TRUE;
10221043
break;
1044+
#ifdef AF_PACKET
1045+
case AF_PACKET:
1046+
sll = (struct sockaddr_ll *) sa;
1047+
char ifrname[IFNAMSIZ];
1048+
1049+
if (UNEXPECTED(!if_indextoname(sll->sll_ifindex, ifrname))) {
1050+
zend_throw_error(NULL, "invalid interface index");
1051+
RETURN_THROWS();
1052+
}
1053+
1054+
ZEND_TRY_ASSIGN_REF_STRING(addr, ifrname);
1055+
if (objint != NULL) {
1056+
ZEND_TRY_ASSIGN_REF_LONG(objint, sll->sll_ifindex);
1057+
}
1058+
RETURN_TRUE;
1059+
break;
1060+
#endif
10231061

10241062
default:
1025-
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
1063+
zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET, or AF_INET6");
10261064
RETURN_THROWS();
10271065
}
10281066
}
@@ -1117,9 +1155,12 @@ PHP_FUNCTION(socket_create)
11171155
if (domain != AF_UNIX
11181156
#ifdef HAVE_IPV6
11191157
&& domain != AF_INET6
1158+
#endif
1159+
#ifdef AF_PACKET
1160+
&& domain != AF_PACKET
11201161
#endif
11211162
&& domain != AF_INET) {
1122-
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET6, or AF_INET");
1163+
zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET6, or AF_INET");
11231164
RETURN_THROWS();
11241165
}
11251166

@@ -1138,6 +1179,8 @@ PHP_FUNCTION(socket_create)
11381179
RETURN_THROWS();
11391180
}
11401181

1182+
PHP_ETH_PROTO_CHECK(protocol, domain);
1183+
11411184
object_init_ex(return_value, socket_ce);
11421185
php_sock = Z_SOCKET_P(return_value);
11431186

@@ -1275,20 +1318,20 @@ PHP_FUNCTION(socket_bind)
12751318
php_socket *php_sock;
12761319
char *addr;
12771320
size_t addr_len;
1278-
zend_long port = 0;
1321+
zend_long objint = 0;
12791322
zend_long retval = 0;
12801323

12811324
ZEND_PARSE_PARAMETERS_START(2, 3)
12821325
Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
12831326
Z_PARAM_STRING(addr, addr_len)
12841327
Z_PARAM_OPTIONAL
1285-
Z_PARAM_LONG(port)
1328+
Z_PARAM_LONG(objint)
12861329
ZEND_PARSE_PARAMETERS_END();
12871330

12881331
php_sock = Z_SOCKET_P(arg1);
12891332
ENSURE_SOCKET_VALID(php_sock);
12901333

1291-
if (port < 0 || port > USHRT_MAX) {
1334+
if (objint < 0 || objint > USHRT_MAX) {
12921335
zend_argument_value_error(3, "must be between 0 and %u", USHRT_MAX);
12931336
RETURN_THROWS();
12941337
}
@@ -1316,7 +1359,7 @@ PHP_FUNCTION(socket_bind)
13161359
struct sockaddr_in *sa = (struct sockaddr_in *) sock_type;
13171360

13181361
sa->sin_family = AF_INET;
1319-
sa->sin_port = htons((unsigned short) port);
1362+
sa->sin_port = htons((unsigned short) objint);
13201363

13211364
if (! php_set_inet_addr(sa, addr, php_sock)) {
13221365
RETURN_FALSE;
@@ -1331,7 +1374,7 @@ PHP_FUNCTION(socket_bind)
13311374
struct sockaddr_in6 *sa = (struct sockaddr_in6 *) sock_type;
13321375

13331376
sa->sin6_family = AF_INET6;
1334-
sa->sin6_port = htons((unsigned short) port);
1377+
sa->sin6_port = htons((unsigned short) objint);
13351378

13361379
if (! php_set_inet6_addr(sa, addr, php_sock)) {
13371380
RETURN_FALSE;
@@ -1340,9 +1383,26 @@ PHP_FUNCTION(socket_bind)
13401383
retval = bind(php_sock->bsd_socket, (struct sockaddr *)sa, sizeof(struct sockaddr_in6));
13411384
break;
13421385
}
1386+
#endif
1387+
#ifdef AF_PACKET
1388+
case AF_PACKET:
1389+
{
1390+
struct sockaddr_ll *sa = (struct sockaddr_ll *) sock_type;
1391+
socklen_t sa_len = sizeof(sa);
1392+
1393+
if (getsockname(php_sock->bsd_socket, sock_type, &sa_len) < 0) {
1394+
zend_value_error("invalid AF_PACKET socket");
1395+
RETURN_THROWS();
1396+
}
1397+
1398+
sa->sll_ifindex = if_nametoindex(addr);
1399+
1400+
retval = bind(php_sock->bsd_socket, sock_type, sizeof(struct sockaddr_ll));
1401+
break;
1402+
}
13431403
#endif
13441404
default:
1345-
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
1405+
zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET, or AF_INET6");
13461406
RETURN_THROWS();
13471407
}
13481408

@@ -1443,6 +1503,9 @@ PHP_FUNCTION(socket_recvfrom)
14431503
struct sockaddr_in sin;
14441504
#ifdef HAVE_IPV6
14451505
struct sockaddr_in6 sin6;
1506+
#endif
1507+
#ifdef AF_PACKET
1508+
//struct sockaddr_ll sll;
14461509
#endif
14471510
char addrbuf[INET6_ADDRSTRLEN];
14481511
socklen_t slen;
@@ -1547,6 +1610,38 @@ PHP_FUNCTION(socket_recvfrom)
15471610
ZEND_TRY_ASSIGN_REF_STRING(arg5, addrbuf[0] ? addrbuf : "::");
15481611
ZEND_TRY_ASSIGN_REF_LONG(arg6, ntohs(sin6.sin6_port));
15491612
break;
1613+
#endif
1614+
#ifdef AF_PACKET
1615+
/*
1616+
case AF_PACKET:
1617+
// TODO expose and use proper ethernet frame type instead i.e. src mac, dst mac and payload to userland
1618+
// ditto for socket_sendto
1619+
slen = sizeof(sll);
1620+
memset(&sll, 0, sizeof(sll));
1621+
sll.sll_family = AF_PACKET;
1622+
char ifrname[IFNAMSIZ];
1623+
1624+
retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), arg3, arg4, (struct sockaddr *)&sll, (socklen_t *)&slen);
1625+
1626+
if (retval < 0) {
1627+
PHP_SOCKET_ERROR(php_sock, "unable to recvfrom", errno);
1628+
zend_string_efree(recv_buf);
1629+
RETURN_FALSE;
1630+
}
1631+
ZSTR_LEN(recv_buf) = retval;
1632+
ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0';
1633+
1634+
if (UNEXPECTED(!if_indextoname(sll.sll_ifindex, ifrname))) {
1635+
PHP_SOCKET_ERROR(php_sock, "unable to get the interface name", errno);
1636+
zend_string_efree(recv_buf);
1637+
RETURN_FALSE;
1638+
}
1639+
1640+
ZEND_TRY_ASSIGN_REF_NEW_STR(arg2, recv_buf);
1641+
ZEND_TRY_ASSIGN_REF_STRING(arg5, ifrname);
1642+
ZEND_TRY_ASSIGN_REF_LONG(arg6, sll.sll_ifindex);
1643+
break;
1644+
*/
15501645
#endif
15511646
default:
15521647
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
@@ -1566,6 +1661,9 @@ PHP_FUNCTION(socket_sendto)
15661661
struct sockaddr_in sin;
15671662
#ifdef HAVE_IPV6
15681663
struct sockaddr_in6 sin6;
1664+
#endif
1665+
#ifdef AF_PACKET
1666+
//struct sockaddr_ll sll;
15691667
#endif
15701668
int retval;
15711669
size_t buf_len, addr_len;
@@ -1639,6 +1737,22 @@ PHP_FUNCTION(socket_sendto)
16391737

16401738
retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin6, sizeof(sin6));
16411739
break;
1740+
#endif
1741+
#ifdef AF_PACKET
1742+
/*
1743+
case AF_PACKET:
1744+
if (port_is_null) {
1745+
zend_argument_value_error(6, "cannot be null when the socket type is AF_PACKET");
1746+
RETURN_THROWS();
1747+
}
1748+
1749+
memset(&sll, 0, sizeof(sll));
1750+
sll.sll_family = AF_PACKET;
1751+
sll.sll_ifindex = port;
1752+
1753+
retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin, sizeof(sin));
1754+
break;
1755+
*/
16421756
#endif
16431757
default:
16441758
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)
27022816

27032817
ai = Z_ADDRESS_INFO_P(arg1);
27042818

2819+
PHP_ETH_PROTO_CHECK(ai->addrinfo.ai_protocol, ai->addrinfo.ai_family);
2820+
27052821
object_init_ex(return_value, socket_ce);
27062822
php_sock = Z_SOCKET_P(return_value);
27072823

@@ -2765,6 +2881,8 @@ PHP_FUNCTION(socket_addrinfo_connect)
27652881

27662882
ai = Z_ADDRESS_INFO_P(arg1);
27672883

2884+
PHP_ETH_PROTO_CHECK(ai->addrinfo.ai_protocol, ai->addrinfo.ai_family);
2885+
27682886
object_init_ex(return_value, socket_ce);
27692887
php_sock = Z_SOCKET_P(return_value);
27702888

ext/sockets/sockets.stub.php

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@
2626
*/
2727
const AF_DIVERT = UNKNOWN;
2828
#endif
29+
#ifdef AF_PACKET
30+
/**
31+
* @var int
32+
* @cvalue AF_PACKET
33+
*/
34+
const AF_PACKET = UNKNOWN;
35+
#endif
2936
/**
3037
* @var int
3138
* @cvalue SOCK_STREAM
@@ -1978,6 +1985,28 @@
19781985
*/
19791986
const UDPLITE_RECV_CSCOV = UNKNOWN;
19801987
#endif
1988+
#if defined(ETH_P_ALL)
1989+
/**
1990+
* @var int
1991+
* @cvalue ETH_P_IP
1992+
*/
1993+
const ETH_P_IP = UNKNOWN;
1994+
/**
1995+
* @var int
1996+
* @cvalue ETH_P_IPV6
1997+
*/
1998+
const ETH_P_IPV6 = UNKNOWN;
1999+
/**
2000+
* @var int
2001+
* @cvalue ETH_P_LOOP
2002+
*/
2003+
const ETH_P_LOOP = UNKNOWN;
2004+
/**
2005+
* @var int
2006+
* @cvalue ETH_P_ALL
2007+
*/
2008+
const ETH_P_ALL = UNKNOWN;
2009+
#endif
19812010

19822011
/**
19832012
* @strict-properties
@@ -2017,13 +2046,13 @@ function socket_read(Socket $socket, int $length, int $mode = PHP_BINARY_READ):
20172046
* @param string $address
20182047
* @param int $port
20192048
*/
2020-
function socket_getsockname(Socket $socket, &$address, &$port = null): bool {}
2049+
function socket_getsockname(Socket $socket, &$address, ?int &$objint = null): bool {}
20212050

20222051
/**
20232052
* @param string $address
20242053
* @param int $port
20252054
*/
2026-
function socket_getpeername(Socket $socket, &$address, &$port = null): bool {}
2055+
function socket_getpeername(Socket $socket, &$address, ?int &$objint = null): bool {}
20272056

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

0 commit comments

Comments
 (0)