@@ -70,7 +70,14 @@ static int read(SOCKET s, char *buf, size_t count) {
7070#include <poll.h>
7171#include <unistd.h>
7272#include <fcntl.h>
73+
74+ #ifdef SDL_PLATFORM_ANDROID
75+ #include <linux/netlink.h>
76+ #include <linux/rtnetlink.h>
77+ #else
7378#include <ifaddrs.h>
79+ #endif
80+
7481#define INVALID_SOCKET -1
7582#define SOCKET_ERROR -1
7683typedef int Socket ;
@@ -635,6 +642,37 @@ void NET_SimulateAddressResolutionLoss(int percent_loss)
635642 SDL_SetAtomicInt (& resolver_percent_loss , SDL_clamp (percent_loss , 0 , 100 ));
636643}
637644
645+ #ifdef SDL_PLATFORM_ANDROID
646+ typedef struct EnumerateNetAddrTableData
647+ {
648+ NET_Address * * retval ;
649+ int count ;
650+ int real_count ;
651+ } EnumerateNetAddrTableData ;
652+
653+ static void SDLCALL EnumerateNetAddrTable (void * userdata , SDL_PropertiesID props , const char * name )
654+ {
655+ NET_Address * netaddr = SDL_GetPointerProperty (props , name , NULL );
656+ if (netaddr ) {
657+ NET_RefAddress (netaddr );
658+ EnumerateNetAddrTableData * data = (EnumerateNetAddrTableData * ) userdata ;
659+ SDL_assert (data -> real_count < data -> count );
660+ data -> retval [data -> real_count ++ ] = netaddr ;
661+ }
662+ }
663+
664+ static void SDLCALL CleanupNetAddrTable (void * userdata , void * value )
665+ {
666+ (void ) userdata ;
667+ NET_UnrefAddress ((NET_Address * ) value );
668+ }
669+
670+ static int SDLCALL CompareNetAddrQsort (const void * a , const void * b )
671+ {
672+ return NET_CompareAddresses (* (NET_Address * * ) a , * (NET_Address * * ) b );
673+ }
674+ #endif
675+
638676NET_Address * * NET_GetLocalAddresses (int * num_addresses )
639677{
640678 int count = 0 ;
@@ -699,6 +737,108 @@ NET_Address **NET_GetLocalAddresses(int *num_addresses)
699737
700738 SDL_free (addrs );
701739
740+ #elif defined(SDL_PLATFORM_ANDROID )
741+ // Android has getifaddrs() in Android 24, but we still target 21, so do it the hard way.
742+ const int sock = socket (AF_NETLINK , SOCK_DGRAM , NETLINK_ROUTE );
743+ if (sock < 0 ) {
744+ SetLastSocketError ("Failed to create AF_NETLINK socket" );
745+ return NULL ;
746+ }
747+
748+ fcntl (sock , F_SETFL , fcntl (sock , F_GETFL , 0 ) | O_NONBLOCK );
749+
750+ // Ask for the address information.
751+ typedef struct reqstruct
752+ {
753+ struct nlmsghdr header ;
754+ struct ifaddrmsg msg ;
755+ } reqstruct ;
756+
757+ reqstruct req ;
758+ SDL_zero (req );
759+ req .header .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP ;
760+ req .header .nlmsg_type = RTM_GETADDR ;
761+ req .header .nlmsg_len = NLMSG_LENGTH (sizeof (req .msg ));
762+ req .msg .ifa_family = AF_UNSPEC ;
763+
764+ if (send (sock , & req , req .header .nlmsg_len , 0 ) != req .header .nlmsg_len ) {
765+ SetLastSocketError ("Failed to send request to AF_NETLINK socket" );
766+ close (sock );
767+ return NULL ;
768+ }
769+
770+ // this can produce duplicate entries for various reasons; use an SDL_PropertiesID to keep a unique table.
771+ const SDL_PropertiesID addr_table = SDL_CreateProperties ();
772+ if (!addr_table ) {
773+ close (sock );
774+ return NULL ;
775+ }
776+
777+ Uint8 buffer [64 * 1024 ];
778+ ssize_t br ;
779+ while ((br = recv (sock , buffer , sizeof (buffer ), 0 )) > 0 ) {
780+ for (const struct nlmsghdr * header = (const struct nlmsghdr * ) buffer ; NLMSG_OK (header , (size_t ) br ); header = NLMSG_NEXT (header , br )) {
781+ if (header -> nlmsg_type == NLMSG_DONE ) {
782+ break ; // we got it all.
783+ } else if (header -> nlmsg_type == NLMSG_ERROR ) {
784+ // uhoh.
785+ NET_FreeLocalAddresses (retval );
786+ retval = NULL ;
787+ break ;
788+ } else if (header -> nlmsg_type == RTM_NEWADDR ) {
789+ const struct ifaddrmsg * msg = (const struct ifaddrmsg * ) (NLMSG_DATA (header ));
790+ size_t payload_len = IFA_PAYLOAD (header );
791+ for (const struct rtattr * attr = IFA_RTA (msg ); RTA_OK (attr , payload_len ); attr = RTA_NEXT (attr , payload_len )) {
792+ if ((attr -> rta_type != IFA_ADDRESS ) && (attr -> rta_type != IFA_LOCAL )) {
793+ continue ;
794+ }
795+
796+ // this gives us the raw bytes of an address, but not the actual sockaddr_* layout, so we have to go with known protocols. :/
797+ AddressStorage addrstorage ;
798+ SockLen addrlen = 0 ;
799+ SDL_zero (addrstorage );
800+ addrstorage .ss_family = msg -> ifa_family ;
801+ if (addrstorage .ss_family == AF_INET ) {
802+ struct sockaddr_in * sa = (struct sockaddr_in * ) & addrstorage ;
803+ SDL_memcpy (& sa -> sin_addr , RTA_DATA (attr ), RTA_PAYLOAD (attr ));
804+ addrlen = sizeof (* sa );
805+ } else if (addrstorage .ss_family == AF_INET6 ) {
806+ struct sockaddr_in6 * sa6 = (struct sockaddr_in6 * ) & addrstorage ;
807+ SDL_memcpy (& sa6 -> sin6_addr , RTA_DATA (attr ), RTA_PAYLOAD (attr ));
808+ addrlen = sizeof (* sa6 );
809+ } else {
810+ continue ; // unknown protocol family.
811+ }
812+
813+ NET_Address * netaddr = CreateSDLNetAddrFromSockAddr ((const struct sockaddr * ) & addrstorage , addrlen );
814+ if (!netaddr ) {
815+ continue ;
816+ }
817+
818+ const char * key = NET_GetAddressString (netaddr );
819+ if (!key ) {
820+ NET_UnrefAddress (netaddr );
821+ } else {
822+ SDL_SetPointerPropertyWithCleanup (addr_table , key , netaddr , CleanupNetAddrTable , NULL );
823+ count ++ ;
824+ }
825+ }
826+ }
827+ }
828+ }
829+
830+ close (sock );
831+
832+ retval = (NET_Address * * ) SDL_calloc (count + 1 , sizeof (NET_Address * ));
833+ if (retval ) {
834+ EnumerateNetAddrTableData data = { retval , count , 0 };
835+ SDL_EnumerateProperties (addr_table , EnumerateNetAddrTable , & data );
836+ count = data .real_count ;
837+ SDL_qsort (retval , count , sizeof (* retval ), CompareNetAddrQsort );
838+ }
839+
840+ SDL_DestroyProperties (addr_table ); // will unref all addresses still in the table, but we took a reference too.
841+
702842#else
703843 struct ifaddrs * ifaddr ;
704844 if (getifaddrs (& ifaddr ) == -1 ) {
0 commit comments