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>
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+ # include <linux/ipv6.h>
62+ # endif
5763# if defined(HAVE_LINUX_SOCK_DIAG_H )
5864# include <linux/sock_diag.h>
5965# else
@@ -120,6 +126,9 @@ static PHP_RSHUTDOWN_FUNCTION(sockets);
120126
121127zend_class_entry * socket_ce ;
122128static zend_object_handlers socket_object_handlers ;
129+ #ifdef AF_PACKET
130+ zend_class_entry * socket_ethinfo_ce ;
131+ #endif
123132
124133static zend_object * socket_create_object (zend_class_entry * class_type ) {
125134 php_socket * intern = zend_object_alloc (sizeof (php_socket ), class_type );
@@ -482,6 +491,9 @@ static PHP_MINIT_FUNCTION(sockets)
482491 socket_object_handlers .get_gc = socket_get_gc ;
483492 socket_object_handlers .compare = zend_objects_not_comparable ;
484493
494+ #if defined(AF_PACKET )
495+ socket_ethinfo_ce = register_class_SocketEthernetInfo ();
496+ #endif
485497 address_info_ce = register_class_AddressInfo ();
486498 address_info_ce -> create_object = address_info_create_object ;
487499 address_info_ce -> default_object_handlers = & address_info_object_handlers ;
@@ -1388,7 +1400,7 @@ PHP_FUNCTION(socket_bind)
13881400 struct sockaddr_ll * sa = (struct sockaddr_ll * ) sock_type ;
13891401 socklen_t sa_len = sizeof (sa );
13901402
1391- if (getsockname (php_sock -> bsd_socket , sock_type , & sa_len ) < 0 ) {
1403+ if (getsockname (php_sock -> bsd_socket , ( struct sockaddr * ) sa , & sa_len ) < 0 ) {
13921404 zend_value_error ("invalid AF_PACKET socket" );
13931405 RETURN_THROWS ();
13941406 }
@@ -1503,7 +1515,9 @@ PHP_FUNCTION(socket_recvfrom)
15031515 struct sockaddr_in6 sin6 ;
15041516#endif
15051517#ifdef AF_PACKET
1506- //struct sockaddr_ll sll;
1518+ struct sockaddr_ll sll ;
1519+ int protoid ;
1520+ socklen_t protoidlen = sizeof (protoid );
15071521#endif
15081522 char addrbuf [INET6_ADDRSTRLEN ];
15091523 socklen_t slen ;
@@ -1532,6 +1546,15 @@ PHP_FUNCTION(socket_recvfrom)
15321546 RETURN_FALSE ;
15331547 }
15341548
1549+ #ifdef AF_PACKET
1550+ // ethernet header + payload
1551+ // possibly follow-up PR SOCK_DGRAM
1552+ if (php_sock -> type == AF_PACKET && arg3 < 60 ) {
1553+ zend_argument_value_error (3 , "must be at least 60 for AF_PACKET" );
1554+ RETURN_THROWS ();
1555+ }
1556+ #endif
1557+
15351558 recv_buf = zend_string_alloc (arg3 + 1 , 0 );
15361559
15371560 switch (php_sock -> type ) {
@@ -1610,14 +1633,19 @@ PHP_FUNCTION(socket_recvfrom)
16101633 break ;
16111634#endif
16121635#ifdef AF_PACKET
1613- /*
16141636 case AF_PACKET :
1615- // TODO expose and use proper ethernet frame type instead i.e. src mac, dst mac and payload to userland
1616- // ditto for socket_sendto
1637+ getsockopt (php_sock -> bsd_socket , SOL_SOCKET , SO_TYPE , (char * ) & protoid , & protoidlen );
1638+
1639+ // TODO: SOCK_DGRAM support
1640+ if (protoid != SOCK_RAW ) {
1641+ zend_argument_value_error (1 , "must be SOCK_RAW socket type" );
1642+ RETURN_THROWS ();
1643+ }
16171644 slen = sizeof (sll );
16181645 memset (& sll , 0 , sizeof (sll ));
16191646 sll .sll_family = AF_PACKET ;
16201647 char ifrname [IFNAMSIZ ];
1648+ zval zpayload ;
16211649
16221650 retval = recvfrom (php_sock -> bsd_socket , ZSTR_VAL (recv_buf ), arg3 , arg4 , (struct sockaddr * )& sll , (socklen_t * )& slen );
16231651
@@ -1626,20 +1654,93 @@ PHP_FUNCTION(socket_recvfrom)
16261654 zend_string_efree (recv_buf );
16271655 RETURN_FALSE ;
16281656 }
1629- ZSTR_LEN(recv_buf) = retval;
1630- ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0';
16311657
16321658 if (UNEXPECTED (!if_indextoname (sll .sll_ifindex , ifrname ))) {
16331659 PHP_SOCKET_ERROR (php_sock , "unable to get the interface name" , errno );
16341660 zend_string_efree (recv_buf );
16351661 RETURN_FALSE ;
16361662 }
16371663
1638- ZEND_TRY_ASSIGN_REF_NEW_STR(arg2, recv_buf);
1664+ struct ethhdr * e = (struct ethhdr * )ZSTR_VAL (recv_buf );
1665+ unsigned short protocol = ntohs (e -> h_proto );
1666+ unsigned char * payload ;
1667+
1668+ zval obj ;
1669+ object_init_ex (& obj , socket_ethinfo_ce );
1670+ array_init (& zpayload );
1671+
1672+ switch (protocol ) {
1673+ case ETH_P_IP : {
1674+ payload = ((unsigned char * )e + sizeof (struct ethhdr ));
1675+ struct iphdr * ip = (struct iphdr * )payload ;
1676+ unsigned char * ipdata = payload + (ip -> ihl * 4 );
1677+ struct in_addr s , d ;
1678+ s .s_addr = ip -> saddr ;
1679+ d .s_addr = ip -> daddr ;
1680+ add_assoc_string (& zpayload , "ipsrc" , inet_ntoa (s ));
1681+ add_assoc_string (& zpayload , "ipdst" , inet_ntoa (d ));
1682+
1683+ switch (ip -> protocol ) {
1684+ case IPPROTO_TCP : {
1685+ struct tcphdr * tcp = (struct tcphdr * )ipdata ;
1686+ add_assoc_long (& zpayload , "portsrc" , ntohs (tcp -> th_sport ));
1687+ add_assoc_long (& zpayload , "portdst" , ntohs (tcp -> th_dport ));
1688+ break ;
1689+ }
1690+ case IPPROTO_UDP : {
1691+ struct udphdr * udp = (struct udphdr * )ipdata ;
1692+ add_assoc_long (& zpayload , "portsrc" , ntohs (udp -> uh_sport ));
1693+ add_assoc_long (& zpayload , "portdst" , ntohs (udp -> uh_dport ));
1694+ break ;
1695+ }
1696+ default :
1697+ zend_string_efree (recv_buf );
1698+ zval_ptr_dtor (& zpayload );
1699+ zval_ptr_dtor (& obj );
1700+ zend_value_error ("unsupported ip header protocol" );
1701+ RETURN_THROWS ();
1702+ }
1703+ break ;
1704+ }
1705+ case ETH_P_IPV6 : {
1706+ payload = ((unsigned char * )e + sizeof (struct ethhdr ));
1707+ struct ipv6hdr * ip = (struct ipv6hdr * )payload ;
1708+ char s [INET6_ADDRSTRLEN ], d [INET6_ADDRSTRLEN ];
1709+ inet_ntop (AF_INET6 , & ip -> saddr , s , sizeof (s ));
1710+ inet_ntop (AF_INET6 , & ip -> daddr , d , sizeof (d ));
1711+ add_assoc_string (& zpayload , "ipsrc" , s );
1712+ add_assoc_string (& zpayload , "ipdst" , d );
1713+ break ;
1714+ }
1715+ case ETH_P_LOOP : {
1716+ struct ethhdr * innere = (struct ethhdr * )((unsigned char * )e + ETH_HLEN );
1717+ add_assoc_string (& zpayload , "macsrc" , ether_ntoa ((struct ether_addr * )innere -> h_source ));
1718+ add_assoc_string (& zpayload , "macdst" , ether_ntoa ((struct ether_addr * )innere -> h_dest ));
1719+ break ;
1720+ }
1721+ default :
1722+ zend_string_efree (recv_buf );
1723+ zval_ptr_dtor (& zpayload );
1724+ zval_ptr_dtor (& obj );
1725+ zend_value_error ("unsupported ethernet protocol" );
1726+ RETURN_THROWS ();
1727+ }
1728+
1729+ Z_DELREF (zpayload );
1730+ zend_string_efree (recv_buf );
1731+ zend_update_property (Z_OBJCE (obj ), Z_OBJ (obj ), ZEND_STRL ("socket" ), arg1 );
1732+ zend_update_property_string (Z_OBJCE (obj ), Z_OBJ (obj ), ZEND_STRL ("macsrc" ), ether_ntoa ((struct ether_addr * )e -> h_source ));
1733+ zend_update_property_string (Z_OBJCE (obj ), Z_OBJ (obj ), ZEND_STRL ("macdst" ), ether_ntoa ((struct ether_addr * )e -> h_dest ));
1734+ zend_update_property_long (Z_OBJCE (obj ), Z_OBJ (obj ), ZEND_STRL ("ethprotocol" ), protocol );
1735+ zend_update_property (Z_OBJCE (obj ), Z_OBJ (obj ), ZEND_STRL ("payload" ), & zpayload );
1736+
1737+ ZEND_TRY_ASSIGN_REF_VALUE (arg2 , & obj );
16391738 ZEND_TRY_ASSIGN_REF_STRING (arg5 , ifrname );
1640- ZEND_TRY_ASSIGN_REF_LONG(arg6, sll.sll_ifindex);
1739+
1740+ if (arg6 ) {
1741+ ZEND_TRY_ASSIGN_REF_LONG (arg6 , sll .sll_ifindex );
1742+ }
16411743 break ;
1642- */
16431744#endif
16441745 default :
16451746 zend_argument_value_error (1 , "must be one of AF_UNIX, AF_INET, or AF_INET6" );
@@ -1661,7 +1762,10 @@ PHP_FUNCTION(socket_sendto)
16611762 struct sockaddr_in6 sin6 ;
16621763#endif
16631764#ifdef AF_PACKET
1664- //struct sockaddr_ll sll;
1765+ struct sockaddr_ll sll ;
1766+ unsigned char halen ;
1767+ int protoid ;
1768+ socklen_t protoidlen = sizeof (protoid );
16651769#endif
16661770 int retval ;
16671771 size_t buf_len ;
@@ -1694,6 +1798,15 @@ PHP_FUNCTION(socket_sendto)
16941798 RETURN_THROWS ();
16951799 }
16961800
1801+ #ifdef AF_PACKET
1802+ // ether header + payload
1803+ // TODO dealing with SOCK_DGRAM
1804+ if (php_sock -> type == AF_PACKET && len < 60 ) {
1805+ zend_argument_value_error (3 , "must be at least 64 for AF_PACKET" );
1806+ RETURN_THROWS ();
1807+ }
1808+ #endif
1809+
16971810 switch (php_sock -> type ) {
16981811 case AF_UNIX :
16991812 memset (& s_un , 0 , sizeof (s_un ));
@@ -1738,23 +1851,33 @@ PHP_FUNCTION(socket_sendto)
17381851 break ;
17391852#endif
17401853#ifdef AF_PACKET
1741- /*
17421854 case AF_PACKET :
1855+ getsockopt (php_sock -> bsd_socket , SOL_SOCKET , SO_TYPE , (char * ) & protoid , & protoidlen );
1856+
1857+ // TODO: SOCK_DGRAM support
1858+ if (protoid != SOCK_RAW ) {
1859+ zend_argument_value_error (1 , "must be SOCK_RAW socket type" );
1860+ RETURN_THROWS ();
1861+ }
17431862 if (port_is_null ) {
17441863 zend_argument_value_error (6 , "cannot be null when the socket type is AF_PACKET" );
17451864 RETURN_THROWS ();
17461865 }
17471866
1867+ halen = ZSTR_LEN (addr ) > ETH_ALEN ? ETH_ALEN : (unsigned char )ZSTR_LEN (addr );
1868+
17481869 memset (& sll , 0 , sizeof (sll ));
1870+ memcpy (sll .sll_addr , addr , halen );
17491871 sll .sll_family = AF_PACKET ;
17501872 sll .sll_ifindex = port ;
1873+ sll .sll_halen = halen ;
17511874
1752- retval = sendto(php_sock->bsd_socket, buf, ((size_t)len > buf_len) ? buf_len : (size_t)len, flags, (struct sockaddr *) &sin, sizeof(sin));
1875+ // TODO allows to use more user friendly type to replace raw buffer usage
1876+ retval = sendto (php_sock -> bsd_socket , buf , ((size_t )len > buf_len ) ? buf_len : (size_t )len , flags , (struct sockaddr * ) & sll , sizeof (sll ));
17531877 break ;
1754- */
17551878#endif
17561879 default :
1757- zend_argument_value_error (1 , "must be one of AF_UNIX, AF_INET, or AF_INET6" );
1880+ zend_argument_value_error (1 , "must be one of AF_UNIX, AF_INET, AF_PACKET or AF_INET6" );
17581881 RETURN_THROWS ();
17591882 }
17601883
@@ -2880,8 +3003,6 @@ PHP_FUNCTION(socket_addrinfo_connect)
28803003
28813004 ai = Z_ADDRESS_INFO_P (arg1 );
28823005
2883- PHP_ETH_PROTO_CHECK (ai -> addrinfo .ai_protocol , ai -> addrinfo .ai_family );
2884-
28853006 object_init_ex (return_value , socket_ce );
28863007 php_sock = Z_SOCKET_P (return_value );
28873008
0 commit comments