Skip to content

Commit 6ed9e9b

Browse files
committed
ext/sockets: follow-up on AF_PACKET support.
support from socket_recvfrom and exposing basic PHP userland type.
1 parent 6c096bf commit 6ed9e9b

File tree

5 files changed

+122
-9
lines changed

5 files changed

+122
-9
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 linux/if_packet.h linux/if_ether.h])
8+
AC_CHECK_HEADERS([sys/sockio.h linux/filter.h linux/if_packet.h linux/if_ether.h netinet/ether.h])
99
AC_DEFINE([HAVE_SOCKETS], [1],
1010
[Define to 1 if the PHP extension 'sockets' is available.])
1111

ext/sockets/php_sockets.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ typedef struct {
7777

7878
extern PHP_SOCKETS_API zend_class_entry *socket_ce;
7979

80+
#ifdef AF_PACKET
81+
extern PHP_SOCKETS_API zend_class_entry *socket_ethinfo_ce;
82+
#endif
83+
8084
static inline php_socket *socket_from_obj(zend_object *obj) {
8185
return (php_socket *)((char *)(obj) - XtOffsetOf(php_socket, std));
8286
}

ext/sockets/sockets.c

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
# include <netdb.h>
4242
# include <netinet/in.h>
4343
# include <netinet/tcp.h>
44+
# include <netinet/udp.h>
4445
# include <sys/un.h>
4546
# include <arpa/inet.h>
4647
# include <sys/time.h>
@@ -54,6 +55,10 @@
5455
# ifdef HAVE_IF_NAMETOINDEX
5556
# include <net/if.h>
5657
# endif
58+
# ifdef HAVE_NETINET_ETHER_H
59+
# include <netinet/ether.h>
60+
# include <netinet/ip.h>
61+
# endif
5762
# if defined(HAVE_LINUX_SOCK_DIAG_H)
5863
# include <linux/sock_diag.h>
5964
# else
@@ -120,6 +125,9 @@ static PHP_RSHUTDOWN_FUNCTION(sockets);
120125

121126
zend_class_entry *socket_ce;
122127
static zend_object_handlers socket_object_handlers;
128+
#ifdef AF_PACKET
129+
zend_class_entry *socket_ethinfo_ce;
130+
#endif
123131

124132
static zend_object *socket_create_object(zend_class_entry *class_type) {
125133
php_socket *intern = zend_object_alloc(sizeof(php_socket), class_type);
@@ -482,6 +490,9 @@ static PHP_MINIT_FUNCTION(sockets)
482490
socket_object_handlers.get_gc = socket_get_gc;
483491
socket_object_handlers.compare = zend_objects_not_comparable;
484492

493+
#if defined(AF_PACKET)
494+
socket_ethinfo_ce = register_class_SocketEthernetInfo();
495+
#endif
485496
address_info_ce = register_class_AddressInfo();
486497
address_info_ce->create_object = address_info_create_object;
487498
address_info_ce->default_object_handlers = &address_info_object_handlers;
@@ -1503,7 +1514,7 @@ PHP_FUNCTION(socket_recvfrom)
15031514
struct sockaddr_in6 sin6;
15041515
#endif
15051516
#ifdef AF_PACKET
1506-
//struct sockaddr_ll sll;
1517+
struct sockaddr_ll sll;
15071518
#endif
15081519
char addrbuf[INET6_ADDRSTRLEN];
15091520
socklen_t slen;
@@ -1532,6 +1543,12 @@ PHP_FUNCTION(socket_recvfrom)
15321543
RETURN_FALSE;
15331544
}
15341545

1546+
#ifdef AF_PACKET
1547+
if (php_sock->type == AF_PACKET && arg3 < 2048) {
1548+
RETURN_FALSE;
1549+
}
1550+
#endif
1551+
15351552
recv_buf = zend_string_alloc(arg3 + 1, 0);
15361553

15371554
switch (php_sock->type) {
@@ -1610,14 +1627,14 @@ PHP_FUNCTION(socket_recvfrom)
16101627
break;
16111628
#endif
16121629
#ifdef AF_PACKET
1613-
/*
16141630
case AF_PACKET:
16151631
// TODO expose and use proper ethernet frame type instead i.e. src mac, dst mac and payload to userland
16161632
// ditto for socket_sendto
16171633
slen = sizeof(sll);
16181634
memset(&sll, 0, sizeof(sll));
16191635
sll.sll_family = AF_PACKET;
16201636
char ifrname[IFNAMSIZ];
1637+
zval zpayload;
16211638

16221639
retval = recvfrom(php_sock->bsd_socket, ZSTR_VAL(recv_buf), arg3, arg4, (struct sockaddr *)&sll, (socklen_t *)&slen);
16231640

@@ -1626,20 +1643,61 @@ PHP_FUNCTION(socket_recvfrom)
16261643
zend_string_efree(recv_buf);
16271644
RETURN_FALSE;
16281645
}
1629-
ZSTR_LEN(recv_buf) = retval;
1630-
ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0';
16311646

16321647
if (UNEXPECTED(!if_indextoname(sll.sll_ifindex, ifrname))) {
16331648
PHP_SOCKET_ERROR(php_sock, "unable to get the interface name", errno);
16341649
zend_string_efree(recv_buf);
16351650
RETURN_FALSE;
16361651
}
16371652

1638-
ZEND_TRY_ASSIGN_REF_NEW_STR(arg2, recv_buf);
1653+
struct ethhdr *e = (struct ethhdr *)ZSTR_VAL(recv_buf);
1654+
unsigned short protocol = ntohs(e->h_proto);
1655+
unsigned char *payload = ((unsigned char *)e + sizeof(struct ethhdr));
1656+
1657+
object_init_ex(arg2, socket_ethinfo_ce);
1658+
zend_update_property_string(Z_OBJCE_P(arg2), Z_OBJ_P(arg2), ZEND_STRL("macsrc"), ether_ntoa((struct ether_addr *)e->h_source));
1659+
zend_update_property_string(Z_OBJCE_P(arg2), Z_OBJ_P(arg2), ZEND_STRL("macdst"), ether_ntoa((struct ether_addr *)e->h_dest));
1660+
array_init(&zpayload);
1661+
1662+
switch (protocol) {
1663+
case ETH_P_IP: {
1664+
struct iphdr *ip = (struct iphdr *)payload;
1665+
unsigned char *ipdata = payload + (ip->ihl * 4);
1666+
struct in_addr s, d;
1667+
s.s_addr = ip->saddr;
1668+
d.s_addr = ip->daddr;
1669+
add_assoc_string(&zpayload, "ipsrc", inet_ntoa(s));
1670+
add_assoc_string(&zpayload, "ipdst", inet_ntoa(d));
1671+
1672+
switch (ip->protocol) {
1673+
case IPPROTO_TCP: {
1674+
struct tcphdr *tcp = (struct tcphdr *)ipdata;
1675+
add_assoc_long(&zpayload, "portsrc", ntohs(tcp->th_sport));
1676+
add_assoc_long(&zpayload, "portdst", ntohs(tcp->th_dport));
1677+
break;
1678+
}
1679+
case IPPROTO_UDP: {
1680+
struct udphdr *udp = (struct udphdr *)ipdata;
1681+
add_assoc_long(&zpayload, "portsrc", ntohs(udp->uh_sport));
1682+
add_assoc_long(&zpayload, "portdst", ntohs(udp->uh_dport));
1683+
break;
1684+
}
1685+
default:
1686+
zend_value_error("unsupported ip header protocol");
1687+
RETURN_THROWS();
1688+
}
1689+
break;
1690+
}
1691+
default:
1692+
zend_value_error("unsupported ethernet protocol");
1693+
RETURN_THROWS();
1694+
}
1695+
1696+
zend_update_property(Z_OBJCE_P(arg2), Z_OBJ_P(arg2), ZEND_STRL("payload"), &zpayload);
1697+
16391698
ZEND_TRY_ASSIGN_REF_STRING(arg5, ifrname);
16401699
ZEND_TRY_ASSIGN_REF_LONG(arg6, sll.sll_ifindex);
16411700
break;
1642-
*/
16431701
#endif
16441702
default:
16451703
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");
@@ -1751,7 +1809,7 @@ PHP_FUNCTION(socket_sendto)
17511809
17521810
retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin, sizeof(sin));
17531811
break;
1754-
*/
1812+
*/
17551813
#endif
17561814
default:
17571815
zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6");

ext/sockets/sockets.stub.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2156,3 +2156,17 @@ function socket_wsaprotocol_info_import(string $info_id): Socket|false {}
21562156

21572157
function socket_wsaprotocol_info_release(string $info_id): bool {}
21582158
#endif
2159+
2160+
#ifdef AF_PACKET
2161+
final class SocketEthernetInfo
2162+
{
2163+
/** @readonly **/
2164+
public Socket $socket;
2165+
/** @readonly **/
2166+
public string $macsrc;
2167+
/** @readonly **/
2168+
public string $macdst;
2169+
/** @readonly **/
2170+
public array $payload;
2171+
}
2172+
#endif

ext/sockets/sockets_arginfo.h

Lines changed: 38 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)