From c3f91ff92765483c8e88dd2a039e2bbd377c7a9e Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 24 Sep 2023 02:57:26 -0400 Subject: [PATCH] Unified sockaddr->SDLNet_Address code, implemented SDLNet_GetLocalAddresses. Added an example app for SDLNet_GetLocalAddresses. This commit is not tested on, or even compiled for, Windows yet. --- CMakeLists.txt | 3 + SDL_net.c | 232 ++++++++++++++++++++++++++----------- examples/get-local-addrs.c | 40 +++++++ 3 files changed, 210 insertions(+), 65 deletions(-) create mode 100644 examples/get-local-addrs.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ae12bd..56f4da0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,3 +32,6 @@ target_link_libraries(simple-http-get PRIVATE SDL3_net::SDL3_net SDL3::SDL3) add_executable(resolve-hostnames examples/resolve-hostnames.c) target_link_libraries(resolve-hostnames PRIVATE SDL3_net::SDL3_net SDL3::SDL3) + +add_executable(get-local-addrs examples/get-local-addrs.c) +target_link_libraries(get-local-addrs PRIVATE SDL3_net::SDL3_net SDL3::SDL3) diff --git a/SDL_net.c b/SDL_net.c index eda3038..a5dd21e 100644 --- a/SDL_net.c +++ b/SDL_net.c @@ -5,6 +5,7 @@ #define _WIN32_WINNT 0x0600 /* we need APIs that didn't arrive until Windows Vista. */ #include #include +#include typedef SOCKET Socket; typedef int SockLen; typedef SOCKADDR_STORAGE AddressStorage; @@ -24,8 +25,6 @@ static int read(SOCKET s, char *buf, size_t count) { } return (int)count_received; } - -#define EAI_SYSTEM 0 #define poll WSAPoll #else #include @@ -38,6 +37,7 @@ static int read(SOCKET s, char *buf, size_t count) { #include #include #include +#include #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 typedef int Socket; @@ -126,7 +126,7 @@ static char *CreateSocketErrorString(int rc) FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, - err, + rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ msgbuf, SDL_arraysize(msgbuf), @@ -288,6 +288,48 @@ static void DestroyAddress(SDLNet_Address *addr) } } +static SDLNet_Address *CreateSDLNetAddrFromSockAddr(struct sockaddr *saddr, SockLen saddrlen) +{ + // !!! FIXME: this all seems inefficient in the name of keeping addresses generic. + char hostbuf[128]; + int gairc = getnameinfo(saddr, saddrlen, hostbuf, sizeof (hostbuf), NULL, 0, NI_NUMERICHOST); + if (gairc != 0) { + SetGetAddrInfoError("Failed to determine address", gairc); + return NULL; + } + + SDLNet_Address *addr = (SDLNet_Address *) SDL_calloc(1, sizeof (SDLNet_Address)); + if (!addr) { + SDL_OutOfMemory(); + return NULL; + } + SDL_AtomicSet(&addr->status, 1); + + struct addrinfo hints; + SDL_zero(hints); + hints.ai_family = saddr->sa_family; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = 0; + hints.ai_flags = AI_NUMERICHOST; + + gairc = getaddrinfo(hostbuf, NULL, &hints, &addr->ainfo); + if (gairc != 0) { + SDL_free(addr); + SetGetAddrInfoError("Failed to determine address", gairc); + return NULL; + } + + addr->human_readable = SDL_strdup(hostbuf); + if (!addr->human_readable) { + freeaddrinfo(addr->ainfo); + SDL_free(addr); + SDL_OutOfMemory(); + return NULL; + } + + return SDLNet_RefAddress(addr); +} + int SDLNet_Init(void) { char *origerrstr = NULL; @@ -528,8 +570,118 @@ void SDLNet_SimulateAddressResolutionLoss(int percent_loss) SDLNet_Address **SDLNet_GetLocalAddresses(int *num_addresses) { - // !!! FIXME: write me! - return NULL; + int count = 0; + SDLNet_Address **retval = NULL; + + *num_addresses = 0; + +#ifdef __WINDOWS__ + // !!! FIXME: maybe LoadLibrary(iphlpapi) on the first call, since most + // !!! FIXME: things won't ever use this. + + // MSDN docs say start with a 15K buffer, which usually works on the first + // try, instead of trying to query for size, allocate, and then retry, + // since this tends to be more expensive. + ULONG buflen = 15 * 1024; + IP_ADAPTER_ADDRESSES *addrs = NULL; + ULONG rc; + + do { + SDL_free(addrs); + addrs = (IP_ADAPTER_ADDRESSES *) SDL_malloc(buflen); + if (!addrs) { + SDL_OutOfMemory(); + return NULL; + } + + const ULONG flags = GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME; + rc = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, addrs, &buflen); + } while (rc == ERROR_BUFFER_OVERFLOW); + + if (rc != NO_ERROR) { + SetSocketError("GetAdaptersAddresses failed", rc); + SDL_free(addrs); + return NULL; + } + + for (IP_ADAPTER_ADDRESSES *i = addrs; i != NULL; i = i->Next) { + for (IP_ADAPTER_UNICAST_ADDRESS *j = i->FirstUnicastAddress; j != NULL; j = j->Next) { + count++; + } + } + + retval = (SDLNet_Address **) SDL_calloc(count + 1, sizeof (SDLNet_Address *)); + if (!retval) { + SDL_OutOfMemory(); + SDL_free(addrs); + return NULL; + } + + count = 0; + for (IP_ADAPTER_ADDRESSES *i = addrs; i != NULL; i = i->Next) { + for (IP_ADAPTER_UNICAST_ADDRESS *j = i->FirstUnicastAddress; j != NULL; j = j->Next) { + SDLNet_Address *addr = CreateSDLNetAddrFromSockAddr(j->Address.lpSockaddr, j->Address.iSockaddrLength); + if (addr) { + retval[count++] = addr; + } + } + } + + SDL_free(addrs); + +#else + struct ifaddrs *ifaddr; + if (getifaddrs(&ifaddr) == -1) { + SDL_SetError("getifaddrs failed: %s", strerror(errno)); + return NULL; // oh well. + } + + for (struct ifaddrs *i = ifaddr; i != NULL; i = i->ifa_next) { + if (i->ifa_name != NULL) { + count++; + } + } + + retval = (SDLNet_Address **) SDL_calloc(count + 1, sizeof (SDLNet_Address *)); + if (!retval) { + if (ifaddr) { + freeifaddrs(ifaddr); + } + SDL_OutOfMemory(); + return NULL; + } + + count = 0; + for (struct ifaddrs *i = ifaddr; i != NULL; i = i->ifa_next) { + if (i->ifa_name != NULL) { + SDLNet_Address *addr = NULL; + // !!! FIXME: getifaddrs doesn't return the sockaddr length, so we have to go with known protocols. :/ + if (i->ifa_addr->sa_family == AF_INET) { + addr = CreateSDLNetAddrFromSockAddr(i->ifa_addr, sizeof (struct sockaddr_in)); + } else if (i->ifa_addr->sa_family == AF_INET6) { + addr = CreateSDLNetAddrFromSockAddr(i->ifa_addr, sizeof (struct sockaddr_in6)); + } + + if (addr) { + retval[count++] = addr; + } + } + } + + if (ifaddr) { + freeifaddrs(ifaddr); + } +#endif + + *num_addresses = count; + + // try to shrink allocation. + void *ptr = SDL_realloc(retval, (count + 1) * sizeof (SDLNet_Address *)); + if (ptr) { + retval = (SDLNet_Address **) ptr; + } + + return retval; } void SDLNet_FreeLocalAddresses(SDLNet_Address **addresses) @@ -793,49 +945,22 @@ int SDLNet_AcceptClient(SDLNet_Server *server, SDLNet_StreamSocket **client_stre return SDL_SetError("Failed to make incoming socket non-blocking"); } - // !!! FIXME: this all seems inefficient in the name of keeping addresses generic. - char hostbuf[128]; char portbuf[16]; - const int rc = getnameinfo((struct sockaddr *) &from, fromlen, hostbuf, sizeof (hostbuf), portbuf, sizeof (portbuf), NI_NUMERICHOST | NI_NUMERICSERV); - if (rc != 0) { - CloseSocketHandle(handle); - return SetGetAddrInfoError("Failed to determine incoming connection's address", rc); - } - - SDLNet_Address *fromaddr = (SDLNet_Address *) SDL_calloc(1, sizeof (SDLNet_Address)); - if (!fromaddr) { - CloseSocketHandle(handle); - return SDL_OutOfMemory(); - } - SDL_AtomicSet(&fromaddr->status, 1); - - struct addrinfo hints; - SDL_zero(hints); - hints.ai_family = ((struct sockaddr *) &from)->sa_family; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = 0; - hints.ai_flags = AI_NUMERICHOST; - - const int gairc = getaddrinfo(hostbuf, NULL, &hints, &fromaddr->ainfo); + const int gairc = getnameinfo((struct sockaddr *) &from, fromlen, NULL, 0, portbuf, sizeof (portbuf), NI_NUMERICSERV); if (gairc != 0) { - SDL_free(fromaddr); CloseSocketHandle(handle); - return SetGetAddrInfoError("Failed to determine incoming connection's address: %s", rc); + return SetGetAddrInfoError("Failed to determine port number", gairc); } - fromaddr->human_readable = SDL_strdup(hostbuf); - if (!fromaddr->human_readable) { - freeaddrinfo(fromaddr->ainfo); - SDL_free(fromaddr); + SDLNet_Address *fromaddr = CreateSDLNetAddrFromSockAddr((struct sockaddr *) &from, fromlen); + if (!fromaddr) { CloseSocketHandle(handle); - return SDL_OutOfMemory(); + return -1; // error string was already set. } SDLNet_StreamSocket *sock = (SDLNet_StreamSocket *) SDL_calloc(1, sizeof (SDLNet_StreamSocket)); if (!sock) { - SDL_free(fromaddr->human_readable); - freeaddrinfo(fromaddr->ainfo); - SDL_free(fromaddr); + SDLNet_UnrefAddress(fromaddr); CloseSocketHandle(handle); return SDL_OutOfMemory(); } @@ -1261,7 +1386,6 @@ int SDLNet_ReceiveDatagram(SDLNet_DatagramSocket *sock, SDLNet_Datagram **dgram) return 0; } - // !!! FIXME: this all seems inefficient in the name of keeping addresses generic. char hostbuf[128]; char portbuf[16]; const int rc = getnameinfo((struct sockaddr *) &from, fromlen, hostbuf, sizeof (hostbuf), portbuf, sizeof (portbuf), NI_NUMERICHOST | NI_NUMERICSERV); @@ -1296,45 +1420,23 @@ int SDLNet_ReceiveDatagram(SDLNet_DatagramSocket *sock, SDLNet_Datagram **dgram) const SDL_bool create_fromaddr = (!fromaddr) ? SDL_TRUE : SDL_FALSE; if (create_fromaddr) { - fromaddr = (SDLNet_Address *) SDL_calloc(1, sizeof (SDLNet_Address)); + fromaddr = CreateSDLNetAddrFromSockAddr((struct sockaddr *) &from, fromlen); if (!fromaddr) { - return SDL_OutOfMemory(); - } - SDL_AtomicSet(&fromaddr->status, 1); - - struct addrinfo hints; - SDL_zero(hints); - hints.ai_family = ((struct sockaddr *) &from)->sa_family; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = 0; - hints.ai_flags = AI_NUMERICHOST; - - const int gairc = getaddrinfo(hostbuf, NULL, &hints, &fromaddr->ainfo); - if (gairc != 0) { - SDL_free(fromaddr); - return SetGetAddrInfoError("Failed to determine incoming packet's address", gairc); - } - - fromaddr->human_readable = SDL_strdup(hostbuf); - if (!fromaddr->human_readable) { - freeaddrinfo(fromaddr->ainfo); - SDL_free(fromaddr); - return SDL_OutOfMemory(); + return -1; // already set the error string. } } SDLNet_Datagram *dg = SDL_malloc(sizeof (SDLNet_Datagram) + br); if (!dg) { if (create_fromaddr) { - freeaddrinfo(fromaddr->ainfo); - SDL_free(fromaddr); + SDLNet_UnrefAddress(fromaddr); } return SDL_OutOfMemory(); } dg->buf = (Uint8 *) (dg+1); SDL_memcpy(dg->buf, sock->recv_buffer, br); - dg->addr = SDLNet_RefAddress(fromaddr); + dg->addr = create_fromaddr ? fromaddr : SDLNet_RefAddress(fromaddr); dg->port = (Uint16) SDL_atoi(portbuf); dg->buflen = br; diff --git a/examples/get-local-addrs.c b/examples/get-local-addrs.c new file mode 100644 index 0000000..d53ee13 --- /dev/null +++ b/examples/get-local-addrs.c @@ -0,0 +1,40 @@ +/* + * This is just for demonstration purposes! This doesn't + * do anything as complicated as, say, the `ifconfig` utility. + * + * All this to say: don't use this for anything serious! + */ + +#include +#include +#include +#include "SDL_net.h" + +int main(int argc, char **argv) +{ + SDLNet_Address **addrs = NULL; + int num_addrs = 0; + int i; + + if (SDLNet_Init() < 0) { + SDL_Log("SDLNet_Init() failed: %s", SDL_GetError()); + return 1; + } + + addrs = SDLNet_GetLocalAddresses(&num_addrs); + if (addrs == NULL) { + SDL_Log("Failed to determine local addresses: %s", SDL_GetError()); + SDLNet_Quit(); + return 1; + } + + SDL_Log("We saw %d local addresses:", num_addrs); + for (i = 0; i < num_addrs; i++) { + SDL_Log(" - %s", SDLNet_GetAddressString(addrs[i])); + } + + SDLNet_FreeLocalAddresses(addrs); + SDLNet_Quit(); + + return 0; +}