Skip to content

Commit f5b384c

Browse files
committed
android: Use an AF_NETLINK socket for NET_GetLocalAddresses.
Android doesn't have getifaddrs() before API 24, but we still target 21. This code has been tested on a desktop Linux system, but presumably works on Android as well...I hope. :) Fixes #124.
1 parent 35425a9 commit f5b384c

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed

src/SDL_net.c

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
7683
typedef 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+
638676
NET_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

Comments
 (0)