Skip to content

Commit c4c415d

Browse files
committed
ext/sockets: Enable AF_PACKET raw buffer support in socket_sendto/socket_recvfrom.
Take a new approach from PR #17926: instead of parsing ethernet/IP/TCP/UDP headers in C, expose the raw frame as a string to userland, letting users handle protocol decoding safely in PHP. This addresses the security concerns raised during review. Also rename opaque argument variables (arg1..arg6) to meaningful names in both functions and fix a bug in the commented-out sendto code that was using &sin instead of &sll.
1 parent b7c855f commit c4c415d

1 file changed

Lines changed: 54 additions & 50 deletions

File tree

ext/sockets/sockets.c

Lines changed: 54 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,52 +1497,52 @@ PHP_FUNCTION(socket_send)
14971497
/* {{{ Receives data from a socket, connected or not */
14981498
PHP_FUNCTION(socket_recvfrom)
14991499
{
1500-
zval *arg1, *arg2, *arg5, *arg6 = NULL;
1500+
zval *zsocket, *zdata, *zaddr, *zport = NULL;
15011501
php_socket *php_sock;
15021502
struct sockaddr_un s_un;
15031503
struct sockaddr_in sin;
15041504
#ifdef HAVE_IPV6
15051505
struct sockaddr_in6 sin6;
15061506
#endif
15071507
#ifdef AF_PACKET
1508-
//struct sockaddr_ll sll;
1508+
struct sockaddr_ll sll;
15091509
#endif
15101510
char addrbuf[INET6_ADDRSTRLEN];
15111511
socklen_t slen;
15121512
int retval;
1513-
zend_long arg3, arg4;
1513+
zend_long length, flags;
15141514
const char *address;
15151515
zend_string *recv_buf;
15161516

15171517
ZEND_PARSE_PARAMETERS_START(5, 6)
1518-
Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
1519-
Z_PARAM_ZVAL(arg2)
1520-
Z_PARAM_LONG(arg3)
1521-
Z_PARAM_LONG(arg4)
1522-
Z_PARAM_ZVAL(arg5)
1518+
Z_PARAM_OBJECT_OF_CLASS(zsocket, socket_ce)
1519+
Z_PARAM_ZVAL(zdata)
1520+
Z_PARAM_LONG(length)
1521+
Z_PARAM_LONG(flags)
1522+
Z_PARAM_ZVAL(zaddr)
15231523
Z_PARAM_OPTIONAL
1524-
Z_PARAM_ZVAL(arg6)
1524+
Z_PARAM_ZVAL(zport)
15251525
ZEND_PARSE_PARAMETERS_END();
15261526

1527-
php_sock = Z_SOCKET_P(arg1);
1527+
php_sock = Z_SOCKET_P(zsocket);
15281528
ENSURE_SOCKET_VALID(php_sock);
15291529

15301530
/* overflow check */
15311531
/* Shouldthrow ? */
15321532

1533-
if (arg3 <= 0 || arg3 > ZEND_LONG_MAX - 1) {
1533+
if (length <= 0 || length > ZEND_LONG_MAX - 1) {
15341534
RETURN_FALSE;
15351535
}
15361536

1537-
recv_buf = zend_string_alloc(arg3 + 1, 0);
1537+
recv_buf = zend_string_alloc(length + 1, 0);
15381538

15391539
switch (php_sock->type) {
15401540
case AF_UNIX:
15411541
slen = sizeof(s_un);
15421542
memset(&s_un, 0, slen);
15431543
s_un.sun_family = AF_UNIX;
15441544

1545-
retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), arg3, arg4, (struct sockaddr *)&s_un, (socklen_t *)&slen);
1545+
retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), length, flags, (struct sockaddr *)&s_un, (socklen_t *)&slen);
15461546

15471547
if (retval < 0) {
15481548
PHP_SOCKET_ERROR(php_sock, "Unable to recvfrom", errno);
@@ -1552,16 +1552,16 @@ PHP_FUNCTION(socket_recvfrom)
15521552
ZSTR_LEN(recv_buf) = retval;
15531553
ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0';
15541554

1555-
ZEND_TRY_ASSIGN_REF_NEW_STR(arg2, recv_buf);
1556-
ZEND_TRY_ASSIGN_REF_STRING(arg5, s_un.sun_path);
1555+
ZEND_TRY_ASSIGN_REF_NEW_STR(zdata, recv_buf);
1556+
ZEND_TRY_ASSIGN_REF_STRING(zaddr, s_un.sun_path);
15571557
break;
15581558

15591559
case AF_INET:
15601560
slen = sizeof(sin);
15611561
memset(&sin, 0, slen);
15621562
sin.sin_family = AF_INET;
15631563

1564-
if (arg6 == NULL) {
1564+
if (zport == NULL) {
15651565
zend_string_efree(recv_buf);
15661566
zend_throw_exception(
15671567
zend_ce_argument_count_error,
@@ -1570,7 +1570,7 @@ PHP_FUNCTION(socket_recvfrom)
15701570
RETURN_THROWS();
15711571
}
15721572

1573-
retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), arg3, arg4, (struct sockaddr *)&sin, (socklen_t *)&slen);
1573+
retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), length, flags, (struct sockaddr *)&sin, (socklen_t *)&slen);
15741574

15751575
if (retval < 0) {
15761576
PHP_SOCKET_ERROR(php_sock, "Unable to recvfrom", errno);
@@ -1582,17 +1582,17 @@ PHP_FUNCTION(socket_recvfrom)
15821582

15831583
address = inet_ntop(AF_INET, &sin.sin_addr, addrbuf, sizeof(addrbuf));
15841584

1585-
ZEND_TRY_ASSIGN_REF_NEW_STR(arg2, recv_buf);
1586-
ZEND_TRY_ASSIGN_REF_STRING(arg5, address ? address : "0.0.0.0");
1587-
ZEND_TRY_ASSIGN_REF_LONG(arg6, ntohs(sin.sin_port));
1585+
ZEND_TRY_ASSIGN_REF_NEW_STR(zdata, recv_buf);
1586+
ZEND_TRY_ASSIGN_REF_STRING(zaddr, address ? address : "0.0.0.0");
1587+
ZEND_TRY_ASSIGN_REF_LONG(zport, ntohs(sin.sin_port));
15881588
break;
15891589
#ifdef HAVE_IPV6
15901590
case AF_INET6:
15911591
slen = sizeof(sin6);
15921592
memset(&sin6, 0, slen);
15931593
sin6.sin6_family = AF_INET6;
15941594

1595-
if (arg6 == NULL) {
1595+
if (zport == NULL) {
15961596
zend_string_efree(recv_buf);
15971597
zend_throw_exception(
15981598
zend_ce_argument_count_error,
@@ -1601,7 +1601,7 @@ PHP_FUNCTION(socket_recvfrom)
16011601
RETURN_THROWS();
16021602
}
16031603

1604-
retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), arg3, arg4, (struct sockaddr *)&sin6, (socklen_t *)&slen);
1604+
retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), length, flags, (struct sockaddr *)&sin6, (socklen_t *)&slen);
16051605

16061606
if (retval < 0) {
16071607
PHP_SOCKET_ERROR(php_sock, "unable to recvfrom", errno);
@@ -1613,22 +1613,20 @@ PHP_FUNCTION(socket_recvfrom)
16131613

16141614
inet_ntop(AF_INET6, &sin6.sin6_addr, addrbuf, sizeof(addrbuf));
16151615

1616-
ZEND_TRY_ASSIGN_REF_NEW_STR(arg2, recv_buf);
1617-
ZEND_TRY_ASSIGN_REF_STRING(arg5, addrbuf[0] ? addrbuf : "::");
1618-
ZEND_TRY_ASSIGN_REF_LONG(arg6, ntohs(sin6.sin6_port));
1616+
ZEND_TRY_ASSIGN_REF_NEW_STR(zdata, recv_buf);
1617+
ZEND_TRY_ASSIGN_REF_STRING(zaddr, addrbuf[0] ? addrbuf : "::");
1618+
ZEND_TRY_ASSIGN_REF_LONG(zport, ntohs(sin6.sin6_port));
16191619
break;
16201620
#endif
16211621
#ifdef AF_PACKET
1622-
/*
1623-
case AF_PACKET:
1624-
// TODO expose and use proper ethernet frame type instead i.e. src mac, dst mac and payload to userland
1625-
// ditto for socket_sendto
1622+
case AF_PACKET: {
1623+
char ifrname[IFNAMSIZ];
1624+
16261625
slen = sizeof(sll);
1627-
memset(&sll, 0, sizeof(sll));
1626+
memset(&sll, 0, slen);
16281627
sll.sll_family = AF_PACKET;
1629-
char ifrname[IFNAMSIZ];
16301628

1631-
retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), arg3, arg4, (struct sockaddr *)&sll, (socklen_t *)&slen);
1629+
retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), length, flags, (struct sockaddr *)&sll, (socklen_t *)&slen);
16321630

16331631
if (retval < 0) {
16341632
PHP_SOCKET_ERROR(php_sock, "unable to recvfrom", errno);
@@ -1644,14 +1642,17 @@ PHP_FUNCTION(socket_recvfrom)
16441642
RETURN_FALSE;
16451643
}
16461644

1647-
ZEND_TRY_ASSIGN_REF_NEW_STR(arg2, recv_buf);
1648-
ZEND_TRY_ASSIGN_REF_STRING(arg5, ifrname);
1649-
ZEND_TRY_ASSIGN_REF_LONG(arg6, sll.sll_ifindex);
1645+
ZEND_TRY_ASSIGN_REF_NEW_STR(zdata, recv_buf);
1646+
ZEND_TRY_ASSIGN_REF_STRING(zaddr, ifrname);
1647+
1648+
if (zport) {
1649+
ZEND_TRY_ASSIGN_REF_LONG(zport, sll.sll_ifindex);
1650+
}
16501651
break;
1651-
*/
1652+
}
16521653
#endif
16531654
default:
1654-
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
1655+
zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET, or AF_INET6");
16551656
RETURN_THROWS();
16561657
}
16571658

@@ -1662,15 +1663,15 @@ PHP_FUNCTION(socket_recvfrom)
16621663
/* {{{ Sends a message to a socket, whether it is connected or not */
16631664
PHP_FUNCTION(socket_sendto)
16641665
{
1665-
zval *arg1;
1666+
zval *zsocket;
16661667
php_socket *php_sock;
16671668
struct sockaddr_un s_un;
16681669
struct sockaddr_in sin;
16691670
#ifdef HAVE_IPV6
16701671
struct sockaddr_in6 sin6;
16711672
#endif
16721673
#ifdef AF_PACKET
1673-
//struct sockaddr_ll sll;
1674+
struct sockaddr_ll sll;
16741675
#endif
16751676
int retval;
16761677
size_t buf_len;
@@ -1680,7 +1681,7 @@ PHP_FUNCTION(socket_sendto)
16801681
zend_string *addr;
16811682

16821683
ZEND_PARSE_PARAMETERS_START(5, 6)
1683-
Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce)
1684+
Z_PARAM_OBJECT_OF_CLASS(zsocket, socket_ce)
16841685
Z_PARAM_STRING(buf, buf_len)
16851686
Z_PARAM_LONG(len)
16861687
Z_PARAM_LONG(flags)
@@ -1689,14 +1690,19 @@ PHP_FUNCTION(socket_sendto)
16891690
Z_PARAM_LONG_OR_NULL(port, port_is_null)
16901691
ZEND_PARSE_PARAMETERS_END();
16911692

1692-
php_sock = Z_SOCKET_P(arg1);
1693+
php_sock = Z_SOCKET_P(zsocket);
16931694
ENSURE_SOCKET_VALID(php_sock);
16941695

1695-
if (port < 0 || port > USHRT_MAX) {
1696-
zend_argument_value_error(6, "must be between 0 and %u", USHRT_MAX);
1697-
RETURN_THROWS();
1696+
#ifdef AF_PACKET
1697+
if (php_sock->type != AF_PACKET) {
1698+
#endif
1699+
if (port < 0 || port > USHRT_MAX) {
1700+
zend_argument_value_error(6, "must be between 0 and %u", USHRT_MAX);
1701+
RETURN_THROWS();
1702+
}
1703+
#ifdef AF_PACKET
16981704
}
1699-
1705+
#endif
17001706

17011707
if (len < 0) {
17021708
zend_argument_value_error(3, "must be greater than or equal to 0");
@@ -1753,7 +1759,6 @@ PHP_FUNCTION(socket_sendto)
17531759
break;
17541760
#endif
17551761
#ifdef AF_PACKET
1756-
/*
17571762
case AF_PACKET:
17581763
if (port_is_null) {
17591764
zend_argument_value_error(6, "cannot be null when the socket type is AF_PACKET");
@@ -1762,14 +1767,13 @@ PHP_FUNCTION(socket_sendto)
17621767

17631768
memset(&sll, 0, sizeof(sll));
17641769
sll.sll_family = AF_PACKET;
1765-
sll.sll_ifindex = port;
1770+
sll.sll_ifindex = (int)port;
17661771

1767-
retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin, sizeof(sin));
1772+
retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *)&sll, sizeof(sll));
17681773
break;
1769-
*/
17701774
#endif
17711775
default:
1772-
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
1776+
zend_argument_value_error(1, "must be one of AF_UNIX, AF_PACKET, AF_INET, or AF_INET6");
17731777
RETURN_THROWS();
17741778
}
17751779

0 commit comments

Comments
 (0)