Skip to content

Commit d1090a5

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

File tree

5 files changed

+163
-18
lines changed

5 files changed

+163
-18
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: 78 additions & 13 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,6 +1384,23 @@ 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:
13451406
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
@@ -2702,6 +2763,8 @@ PHP_FUNCTION(socket_addrinfo_bind)
27022763

27032764
ai = Z_ADDRESS_INFO_P(arg1);
27042765

2766+
PHP_ETH_PROTO_CHECK(ai->addrinfo.ai_protocol, ai->addrinfo.ai_family);
2767+
27052768
object_init_ex(return_value, socket_ce);
27062769
php_sock = Z_SOCKET_P(return_value);
27072770

@@ -2765,6 +2828,8 @@ PHP_FUNCTION(socket_addrinfo_connect)
27652828

27662829
ai = Z_ADDRESS_INFO_P(arg1);
27672830

2831+
PHP_ETH_PROTO_CHECK(ai->addrinfo.ai_protocol, ai->addrinfo.ai_family);
2832+
27682833
object_init_ex(return_value, socket_ce);
27692834
php_sock = Z_SOCKET_P(return_value);
27702835

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

ext/sockets/sockets_arginfo.h

Lines changed: 17 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
socket_getsockname from AF_PACKET socket
3+
--EXTENSIONS--
4+
sockets
5+
--SKIPIF--
6+
<?php
7+
8+
if (!defined("AF_PACKET")) {
9+
die('SKIP AF_PACKET not supported on this platform.');
10+
}
11+
if (!function_exists("posix_getuid") || posix_getuid() != 0) {
12+
die('SKIP AF_PACKET requires root permissions.');
13+
}
14+
?>
15+
--FILE--
16+
<?php
17+
$s_c = socket_create(AF_PACKET, SOCK_RAW, ETH_P_IP);
18+
$s_bind = socket_bind($s_c, 'lo');
19+
var_dump($s_bind);
20+
21+
// Connect to destination address
22+
$s_conn = socket_getsockname($s_c, $istr, $iindex);
23+
var_dump($s_conn);
24+
var_dump($istr);
25+
var_dump($iindex);
26+
27+
socket_getpeername($s_c, $istr2, $iindex2);
28+
socket_close($s_c);
29+
?>
30+
--EXPECTF--
31+
bool(true)
32+
bool(true)
33+
string(2) "lo"
34+
int(%i)
35+
36+
Warning: socket_getpeername(): unable to retrieve peer name [95]: Operation not supported in %s on line %d

0 commit comments

Comments
 (0)