Skip to content

Commit cb717cc

Browse files
committed
ext/sockets: linux AF_PACKET support.
1 parent 11937b3 commit cb717cc

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: 133 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,19 @@ ZEND_DECLARE_MODULE_GLOBALS(sockets)
9197
#define PF_INET AF_INET
9298
#endif
9399

100+
#if defined(ETH_P_ALL)
101+
#define PHP_ETH_PROTO_CHECK(protocol, family) \
102+
do { \
103+
/* We ll let EINVAL errno warning about miusage, too many protocols conflicts but AF_PACKET family works only with ETH_P_* constants */ \
104+
if ((protocol >= ETH_P_LOOP && protocol <= USHRT_MAX) && family == AF_PACKET) { \
105+
protocol = htons(protocol); \
106+
} \
107+
} \
108+
while (0)
109+
#else
110+
#define PHP_ETH_PROTO_CHECK(protocol, family) (0)
111+
#endif
112+
94113
static PHP_GINIT_FUNCTION(sockets);
95114
static PHP_GSHUTDOWN_FUNCTION(sockets);
96115
static PHP_MINIT_FUNCTION(sockets);
@@ -960,13 +979,16 @@ PHP_FUNCTION(socket_read)
960979
/* {{{ 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. */
961980
PHP_FUNCTION(socket_getsockname)
962981
{
963-
zval *arg1, *addr, *port = NULL;
982+
zval *arg1, *addr, *objint = NULL;
964983
php_sockaddr_storage sa_storage = {0};
965984
php_socket *php_sock;
966985
struct sockaddr *sa;
967986
struct sockaddr_in *sin;
968987
#ifdef HAVE_IPV6
969988
struct sockaddr_in6 *sin6;
989+
#endif
990+
#ifdef AF_PACKET
991+
struct sockaddr_ll *sll;
970992
#endif
971993
char addrbuf[INET6_ADDRSTRLEN];
972994
struct sockaddr_un *s_un;
@@ -977,7 +999,7 @@ PHP_FUNCTION(socket_getsockname)
977999
Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
9781000
Z_PARAM_ZVAL(addr)
9791001
Z_PARAM_OPTIONAL
980-
Z_PARAM_ZVAL(port)
1002+
Z_PARAM_ZVAL(objint)
9811003
ZEND_PARSE_PARAMETERS_END();
9821004

9831005
php_sock = Z_SOCKET_P(arg1);
@@ -997,8 +1019,8 @@ PHP_FUNCTION(socket_getsockname)
9971019
inet_ntop(AF_INET6, &sin6->sin6_addr, addrbuf, sizeof(addrbuf));
9981020
ZEND_TRY_ASSIGN_REF_STRING(addr, addrbuf);
9991021

1000-
if (port != NULL) {
1001-
ZEND_TRY_ASSIGN_REF_LONG(port, htons(sin6->sin6_port));
1022+
if (objint != NULL) {
1023+
ZEND_TRY_ASSIGN_REF_LONG(objint, htons(sin6->sin6_port));
10021024
}
10031025
RETURN_TRUE;
10041026
break;
@@ -1008,8 +1030,8 @@ PHP_FUNCTION(socket_getsockname)
10081030
addr_string = inet_ntop(AF_INET, &sin->sin_addr, addrbuf, sizeof(addrbuf));
10091031
ZEND_TRY_ASSIGN_REF_STRING(addr, addr_string);
10101032

1011-
if (port != NULL) {
1012-
ZEND_TRY_ASSIGN_REF_LONG(port, htons(sin->sin_port));
1033+
if (objint != NULL) {
1034+
ZEND_TRY_ASSIGN_REF_LONG(objint, htons(sin->sin_port));
10131035
}
10141036
RETURN_TRUE;
10151037
break;
@@ -1020,9 +1042,26 @@ PHP_FUNCTION(socket_getsockname)
10201042
ZEND_TRY_ASSIGN_REF_STRING(addr, s_un->sun_path);
10211043
RETURN_TRUE;
10221044
break;
1045+
#ifdef AF_PACKET
1046+
case AF_PACKET:
1047+
sll = (struct sockaddr_ll *) sa;
1048+
char ifrname[IFNAMSIZ];
1049+
1050+
if (UNEXPECTED(!if_indextoname(sll->sll_ifindex, ifrname))) {
1051+
zend_throw_error(NULL, "invalid interface index");
1052+
RETURN_THROWS();
1053+
}
1054+
1055+
ZEND_TRY_ASSIGN_REF_STRING(addr, ifrname);
1056+
if (objint != NULL) {
1057+
ZEND_TRY_ASSIGN_REF_LONG(objint, sll->sll_ifindex);
1058+
}
1059+
RETURN_TRUE;
1060+
break;
1061+
#endif
10231062

10241063
default:
1025-
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
1064+
zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET, or AF_INET6");
10261065
RETURN_THROWS();
10271066
}
10281067
}
@@ -1117,9 +1156,12 @@ PHP_FUNCTION(socket_create)
11171156
if (domain != AF_UNIX
11181157
#ifdef HAVE_IPV6
11191158
&& domain != AF_INET6
1159+
#endif
1160+
#ifdef AF_PACKET
1161+
&& domain != AF_PACKET
11201162
#endif
11211163
&& domain != AF_INET) {
1122-
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET6, or AF_INET");
1164+
zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET6, or AF_INET");
11231165
RETURN_THROWS();
11241166
}
11251167

@@ -1138,6 +1180,8 @@ PHP_FUNCTION(socket_create)
11381180
RETURN_THROWS();
11391181
}
11401182

1183+
PHP_ETH_PROTO_CHECK(protocol, protocol);
1184+
11411185
object_init_ex(return_value, socket_ce);
11421186
php_sock = Z_SOCKET_P(return_value);
11431187

@@ -1275,20 +1319,20 @@ PHP_FUNCTION(socket_bind)
12751319
php_socket *php_sock;
12761320
char *addr;
12771321
size_t addr_len;
1278-
zend_long port = 0;
1322+
zend_long objint = 0;
12791323
zend_long retval = 0;
12801324

12811325
ZEND_PARSE_PARAMETERS_START(2, 3)
12821326
Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
12831327
Z_PARAM_STRING(addr, addr_len)
12841328
Z_PARAM_OPTIONAL
1285-
Z_PARAM_LONG(port)
1329+
Z_PARAM_LONG(objint)
12861330
ZEND_PARSE_PARAMETERS_END();
12871331

12881332
php_sock = Z_SOCKET_P(arg1);
12891333
ENSURE_SOCKET_VALID(php_sock);
12901334

1291-
if (port < 0 || port > USHRT_MAX) {
1335+
if (objint < 0 || objint > USHRT_MAX) {
12921336
zend_argument_value_error(3, "must be between 0 and %u", USHRT_MAX);
12931337
RETURN_THROWS();
12941338
}
@@ -1316,7 +1360,7 @@ PHP_FUNCTION(socket_bind)
13161360
struct sockaddr_in *sa = (struct sockaddr_in *) sock_type;
13171361

13181362
sa->sin_family = AF_INET;
1319-
sa->sin_port = htons((unsigned short) port);
1363+
sa->sin_port = htons((unsigned short) objint);
13201364

13211365
if (! php_set_inet_addr(sa, addr, php_sock)) {
13221366
RETURN_FALSE;
@@ -1331,7 +1375,7 @@ PHP_FUNCTION(socket_bind)
13311375
struct sockaddr_in6 *sa = (struct sockaddr_in6 *) sock_type;
13321376

13331377
sa->sin6_family = AF_INET6;
1334-
sa->sin6_port = htons((unsigned short) port);
1378+
sa->sin6_port = htons((unsigned short) objint);
13351379

13361380
if (! php_set_inet6_addr(sa, addr, php_sock)) {
13371381
RETURN_FALSE;
@@ -1340,9 +1384,26 @@ PHP_FUNCTION(socket_bind)
13401384
retval = bind(php_sock->bsd_socket, (struct sockaddr *)sa, sizeof(struct sockaddr_in6));
13411385
break;
13421386
}
1387+
#endif
1388+
#ifdef AF_PACKET
1389+
case AF_PACKET:
1390+
{
1391+
struct sockaddr_ll *sa = (struct sockaddr_ll *) sock_type;
1392+
socklen_t sa_len = sizeof(sa);
1393+
1394+
if (getsockname(php_sock->bsd_socket, sock_type, &sa_len) < 0) {
1395+
zend_value_error("invalid AF_PACKET socket");
1396+
RETURN_THROWS();
1397+
}
1398+
1399+
sa->sll_ifindex = if_nametoindex(addr);
1400+
1401+
retval = bind(php_sock->bsd_socket, sock_type, sizeof(struct sockaddr_ll));
1402+
break;
1403+
}
13431404
#endif
13441405
default:
1345-
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
1406+
zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET, or AF_INET6");
13461407
RETURN_THROWS();
13471408
}
13481409

@@ -1443,6 +1504,9 @@ PHP_FUNCTION(socket_recvfrom)
14431504
struct sockaddr_in sin;
14441505
#ifdef HAVE_IPV6
14451506
struct sockaddr_in6 sin6;
1507+
#endif
1508+
#ifdef AF_PACKET
1509+
//struct sockaddr_ll sll;
14461510
#endif
14471511
char addrbuf[INET6_ADDRSTRLEN];
14481512
socklen_t slen;
@@ -1547,6 +1611,38 @@ PHP_FUNCTION(socket_recvfrom)
15471611
ZEND_TRY_ASSIGN_REF_STRING(arg5, addrbuf[0] ? addrbuf : "::");
15481612
ZEND_TRY_ASSIGN_REF_LONG(arg6, ntohs(sin6.sin6_port));
15491613
break;
1614+
#endif
1615+
#ifdef AF_PACKET
1616+
/*
1617+
case AF_PACKET:
1618+
// TODO expose and use proper ethernet frame type instead i.e. src mac, dst mac and payload to userland
1619+
// ditto for socket_sendto
1620+
slen = sizeof(sll);
1621+
memset(&sll, 0, sizeof(sll));
1622+
sll.sll_family = AF_PACKET;
1623+
char ifrname[IFNAMSIZ];
1624+
1625+
retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), arg3, arg4, (struct sockaddr *)&sll, (socklen_t *)&slen);
1626+
1627+
if (retval < 0) {
1628+
PHP_SOCKET_ERROR(php_sock, "unable to recvfrom", errno);
1629+
zend_string_efree(recv_buf);
1630+
RETURN_FALSE;
1631+
}
1632+
ZSTR_LEN(recv_buf) = retval;
1633+
ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0';
1634+
1635+
if (UNEXPECTED(!if_indextoname(sll.sll_ifindex, ifrname))) {
1636+
PHP_SOCKET_ERROR(php_sock, "unable to get the interface name", errno);
1637+
zend_string_efree(recv_buf);
1638+
RETURN_FALSE;
1639+
}
1640+
1641+
ZEND_TRY_ASSIGN_REF_NEW_STR(arg2, recv_buf);
1642+
ZEND_TRY_ASSIGN_REF_STRING(arg5, ifrname);
1643+
ZEND_TRY_ASSIGN_REF_LONG(arg6, sll.sll_ifindex);
1644+
break;
1645+
*/
15501646
#endif
15511647
default:
15521648
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
@@ -1566,6 +1662,9 @@ PHP_FUNCTION(socket_sendto)
15661662
struct sockaddr_in sin;
15671663
#ifdef HAVE_IPV6
15681664
struct sockaddr_in6 sin6;
1665+
#endif
1666+
#ifdef AF_PACKET
1667+
//struct sockaddr_ll sll;
15691668
#endif
15701669
int retval;
15711670
size_t buf_len, addr_len;
@@ -1639,6 +1738,22 @@ PHP_FUNCTION(socket_sendto)
16391738

16401739
retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin6, sizeof(sin6));
16411740
break;
1741+
#endif
1742+
#ifdef AF_PACKET
1743+
/*
1744+
case AF_PACKET:
1745+
if (port_is_null) {
1746+
zend_argument_value_error(6, "cannot be null when the socket type is AF_PACKET");
1747+
RETURN_THROWS();
1748+
}
1749+
1750+
memset(&sll, 0, sizeof(sll));
1751+
sll.sll_family = AF_PACKET;
1752+
sll.sll_ifindex = port;
1753+
1754+
retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin, sizeof(sin));
1755+
break;
1756+
*/
16421757
#endif
16431758
default:
16441759
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
@@ -2702,6 +2817,8 @@ PHP_FUNCTION(socket_addrinfo_bind)
27022817

27032818
ai = Z_ADDRESS_INFO_P(arg1);
27042819

2820+
PHP_ETH_PROTO_CHECK(ai->addrinfo.ai_protocol, ai->addrinfo.ai_family);
2821+
27052822
object_init_ex(return_value, socket_ce);
27062823
php_sock = Z_SOCKET_P(return_value);
27072824

@@ -2765,6 +2882,8 @@ PHP_FUNCTION(socket_addrinfo_connect)
27652882

27662883
ai = Z_ADDRESS_INFO_P(arg1);
27672884

2885+
PHP_ETH_PROTO_CHECK(ai->addrinfo.ai_protocol, ai->addrinfo.ai_family);
2886+
27682887
object_init_ex(return_value, socket_ce);
27692888
php_sock = Z_SOCKET_P(return_value);
27702889

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)