From 26f620dc0ecda169cae31b40f12abe41b4cfd8c7 Mon Sep 17 00:00:00 2001 From: fincs Date: Sun, 27 Oct 2019 23:45:00 +0100 Subject: [PATCH] Split up socket.c + work in progress changes: - DNS stuff moved to runtime/resolver.c & .h - Address manipulation functions moved to runtime/util/inet_addr.c - Removed sfdnsres/nifm handling in runtime/devices/socket.c - Renamed socketGetLastBsdResult to socketGetLastResult - Renamed socketGetLastSfdnsresResult to resolverGetLastResult - gethostid now inits/deinits nifm as needed - sfdnsres related configuration gone from SocketInitConfig - Fixed bug in addrinfo deserialization - getaddrinfo rewritten, hints can now be NULL - TODO: add back a way to control service discovery/cancel handle --- nx/Makefile | 2 +- nx/include/switch.h | 4 +- nx/include/switch/runtime/devices/socket.h | 9 +- nx/include/switch/runtime/resolver.h | 5 + nx/include/switch/services/bsd.h | 2 +- nx/include/switch/services/sfdnsres.h | 2 +- nx/source/runtime/devices/socket.c | 1007 +------------------- nx/source/runtime/resolver.c | 577 +++++++++++ nx/source/runtime/util/inet_addr.c | 404 ++++++++ 9 files changed, 1004 insertions(+), 1008 deletions(-) create mode 100644 nx/include/switch/runtime/resolver.h create mode 100644 nx/source/runtime/resolver.c create mode 100644 nx/source/runtime/util/inet_addr.c diff --git a/nx/Makefile b/nx/Makefile index b552c627..f2d1e4fd 100644 --- a/nx/Makefile +++ b/nx/Makefile @@ -24,7 +24,7 @@ VERSION := $(LIBNX_MAJOR).$(LIBNX_MINOR).$(LIBNX_PATCH) #--------------------------------------------------------------------------------- TARGET := nx #BUILD := build -SOURCES := source/arm source/kernel source/sf source/services source/crypto source/nvidia source/nvidia/ioctl source/display source/audio source/applets source/runtime source/runtime/devices source/runtime/util/utf +SOURCES := source/arm source/kernel source/sf source/services source/crypto source/nvidia source/nvidia/ioctl source/display source/audio source/applets source/runtime source/runtime/devices source/runtime/util source/runtime/util/utf DATA := data INCLUDES := include external/bsd/include diff --git a/nx/include/switch.h b/nx/include/switch.h index 2d8fb7c8..2a6506ae 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -67,7 +67,8 @@ extern "C" { #include "switch/services/clkrst.h" #include "switch/services/psm.h" #include "switch/services/spsm.h" -//#include "switch/services/bsd.h" Use switch/runtime/devices/socket.h instead +//#include "switch/services/bsd.h" Use instead +//#include "switch/services/sfdnsres.h" Use instead #include "switch/services/fatal.h" #include "switch/services/time.h" #include "switch/services/usb.h" @@ -130,6 +131,7 @@ extern "C" { #include "switch/runtime/env.h" #include "switch/runtime/hosversion.h" #include "switch/runtime/nxlink.h" +#include "switch/runtime/resolver.h" #include "switch/runtime/util/utf.h" diff --git a/nx/include/switch/runtime/devices/socket.h b/nx/include/switch/runtime/devices/socket.h index 1f5cd82a..40f34073 100644 --- a/nx/include/switch/runtime/devices/socket.h +++ b/nx/include/switch/runtime/devices/socket.h @@ -24,11 +24,6 @@ typedef struct { u32 num_bsd_sessions; ///< Number of BSD service sessions (typically 3). BsdServiceType bsd_service_type; ///< BSD service type (typically \ref BsdServiceType_User). - - size_t serialized_out_addrinfos_max_size; ///< For getaddrinfo. - size_t serialized_out_hostent_max_size; ///< For gethostbyname/gethostbyaddr. - bool bypass_nsd; ///< For name gethostbyname/getaddrinfo: bypass the Name Server Daemon. - int dns_timeout; ///< For DNS requests: timeout or 0. } SocketInitConfig; /// Fetch the default configuration for the socket driver. @@ -36,9 +31,7 @@ const SocketInitConfig *socketGetDefaultInitConfig(void); /// Initalize the socket driver. Result socketInitialize(const SocketInitConfig *config); /// Fetch the last bsd:u/s Switch result code (thread-local). -Result socketGetLastBsdResult(void); -/// Fetch the last sfdnsres Switch result code (thread-local). -Result socketGetLastSfdnsresResult(void); +Result socketGetLastResult(void); /// Deinitialize the socket driver. void socketExit(void); diff --git a/nx/include/switch/runtime/resolver.h b/nx/include/switch/runtime/resolver.h new file mode 100644 index 00000000..4179bb63 --- /dev/null +++ b/nx/include/switch/runtime/resolver.h @@ -0,0 +1,5 @@ +#pragma once +#include "../types.h" + +/// Fetch the last resolver Switch result code (thread-local). +Result resolverGetLastResult(void); diff --git a/nx/include/switch/services/bsd.h b/nx/include/switch/services/bsd.h index 127482ab..e254ddd5 100644 --- a/nx/include/switch/services/bsd.h +++ b/nx/include/switch/services/bsd.h @@ -1,6 +1,6 @@ /** * @file bsd.h - * @brief BSD sockets (bsd:u/s) service IPC wrapper. Please use socket.c instead. + * @brief BSD sockets (bsd:u/s) service IPC wrapper. Please use the standard interface instead. * @author plutoo * @author TuxSH * @copyright libnx Authors diff --git a/nx/include/switch/services/sfdnsres.h b/nx/include/switch/services/sfdnsres.h index b23ceba2..62750e9b 100644 --- a/nx/include/switch/services/sfdnsres.h +++ b/nx/include/switch/services/sfdnsres.h @@ -1,6 +1,6 @@ /** * @file sfdnsres.h - * @brief Domain name resolution service IPC wrapper. Please use socket.c instead. + * @brief Domain name resolution service IPC wrapper. Please use the standard interface instead. * @author TuxSH * @author fincs * @copyright libnx Authors diff --git a/nx/source/runtime/devices/socket.c b/nx/source/runtime/devices/socket.c index 6d1da291..d0861bea 100644 --- a/nx/source/runtime/devices/socket.c +++ b/nx/source/runtime/devices/socket.c @@ -1,50 +1,26 @@ -#include -#include -#include -#include +#include #include -#include +#include +#include #include #include -#include -#include #include -#include -#include -#include +#include #include +#include +#include +#include +#include -#include -#include -#include - -#include "runtime/devices/socket.h" -#include "services/bsd.h" -#include "services/sfdnsres.h" -#include "services/nifm.h" #include "result.h" - -/// Configuration structure for sfdnsres. -typedef struct { - size_t serialized_out_addrinfos_max_size; ///< For getaddrinfo. - size_t serialized_out_hostent_max_size; ///< For gethostbyname/gethostbyaddr. - bool bypass_nsd; ///< For name gethostbyname/getaddrinfo: bypass the Name Server Daemon. - int timeout; ///< For DNS requests: timeout or 0. -} SfdnsresConfig; +#include "services/bsd.h" +#include "runtime/devices/socket.h" __attribute__((weak)) size_t __nx_pollfd_sb_max_fds = 64; int _convert_errno(int bsdErrno); -__thread int h_errno; - -static SfdnsresConfig g_sfdnsresConfig; -static __thread Result g_sfdnsresResult; - -const struct in6_addr in6addr_any = {0}; -const struct in6_addr in6addr_loopback = {.__u6_addr32 = {0, 0, 0, __builtin_bswap32(1)}}; - static int _socketOpen(struct _reent *r, void *fdptr, const char *path, int flags, int mode); static int _socketClose(struct _reent *r, void *fdptr); static ssize_t _socketWrite(struct _reent *r, void *fdptr, const char *buf, size_t count); @@ -94,11 +70,6 @@ static const SocketInitConfig g_defaultSocketInitConfig = { .num_bsd_sessions = 3, .bsd_service_type = BsdServiceType_User, - - .serialized_out_addrinfos_max_size = 0x1000, - .serialized_out_hostent_max_size = 0x200, - .bypass_nsd = false, - .dns_timeout = 0, }; const SocketInitConfig *socketGetDefaultInitConfig(void) { @@ -132,20 +103,10 @@ Result socketInitialize(const SocketInitConfig *config) { if (!bsd_service_type) bsd_service_type = g_defaultSocketInitConfig.bsd_service_type; - SfdnsresConfig sfdnsresConfig = { - .serialized_out_addrinfos_max_size = config->serialized_out_addrinfos_max_size, - .serialized_out_hostent_max_size = config->serialized_out_hostent_max_size, - .bypass_nsd = config->bypass_nsd, - .timeout = config->dns_timeout, - }; - int dev = FindDevice("soc:"); if(dev != -1) return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized); - ret = nifmInitialize(); - if(R_FAILED(ret)) return ret; - ret = bsdInitialize(&bcfg, num_bsd_sessions, bsd_service_type); if(R_SUCCEEDED(ret)) dev = AddDevice(&g_socketDevoptab); @@ -161,9 +122,6 @@ Result socketInitialize(const SocketInitConfig *config) { else { g_bsdResult = 0; g_bsdErrno = 0; - - g_sfdnsresConfig = sfdnsresConfig; - g_sfdnsresResult = 0; } return ret; @@ -172,17 +130,12 @@ Result socketInitialize(const SocketInitConfig *config) { void socketExit(void) { RemoveDevice("soc:"); bsdExit(); - nifmExit(); } -Result socketGetLastBsdResult(void) { +Result socketGetLastResult(void) { return g_bsdResult; } -Result socketGetLastSfdnsresResult(void) { - return g_sfdnsresResult; -} - /***********************************************************************************************************************/ static int _socketGetFd(int fd) { @@ -752,941 +705,3 @@ int recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, s errno = ENOSYS; return -1; } - -/***********************************************************************************************************************/ - -// Adapted from libctru -static int _socketInetAtonDetail(int *outBase, size_t *outNumBytes, const char *cp, struct in_addr *inp) { - int base; - u32 val; - int c; - char bytes[4]; - size_t num_bytes = 0; - - c = *cp; - for(;;) { - if(!isdigit(c)) return 0; - - val = 0; - base = 10; - if(c == '0') { - c = *++cp; - if(c == 'x' || c == 'X') { - base = 16; - c = *++cp; - } - else base = 8; - } - - for(;;) { - if(isdigit(c)) { - if(base == 8 && c >= '8') return 0; - val *= base; - val += c - '0'; - c = *++cp; - } - else if(base == 16 && isxdigit(c)) { - val *= base; - val += c + 10 - (islower(c) ? 'a' : 'A'); - c = *++cp; - } - else break; - } - - if(c == '.') { - if(num_bytes > 3) return 0; - if(val > 0xFF) return 0; - bytes[num_bytes++] = val; - c = *++cp; - } - else break; - } - - if(c != 0) { - *outNumBytes = num_bytes; - *outBase = base; - return 0; - } - - switch(num_bytes) { - case 0: - break; - - case 1: - if(val > 0xFFFFFF) return 0; - val |= bytes[0] << 24; - break; - - case 2: - if(val > 0xFFFF) return 0; - val |= bytes[0] << 24; - val |= bytes[1] << 16; - break; - - case 3: - if(val > 0xFF) return 0; - val |= bytes[0] << 24; - val |= bytes[1] << 16; - val |= bytes[2] << 8; - break; - } - - if(inp) - inp->s_addr = htonl(val); - - *outNumBytes = num_bytes; - *outBase = base; - - return 1; -} - -// Adapted from libctru -static const char *inet_ntop4(const void *src, char *dst, socklen_t size) { - const u8 *ip = src; - - char *p; - size_t i; - unsigned int n; - - if(size < INET_ADDRSTRLEN) { - errno = ENOSPC; - return NULL; - } - - for(p = dst, i = 0; i < 4; ++i) { - if(i > 0) *p++ = '.'; - - n = ip[i]; - if(n >= 100) { - *p++ = n/100 + '0'; - n %= 100; - } - if(n >= 10 || ip[i] >= 100) { - *p++ = n/10 + '0'; - n %= 10; - } - *p++ = n + '0'; - } - *p = 0; - - return dst; -} - -static int inet_pton4(const char *src, void *dst) { - int base; - size_t numBytes; - - int ret = _socketInetAtonDetail(&base, &numBytes, src, (struct in_addr *)dst); - return (ret == 1 && base == 10 && numBytes == 3) ? 1 : 0; -} - -/* Copyright (c) 1996 by Internet Software Consortium. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS - * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE - * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL - * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR - * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS - * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS - * SOFTWARE. - */ - -#define INADDRSZ 4 -#define IN6ADDRSZ 16 -#define INT16SZ 2 -/* const char * - * inet_ntop6(src, dst, size) - * convert IPv6 binary address into presentation (printable) format - * author: - * Paul Vixie, 1996. - */ -static const char * -inet_ntop6(src, dst, size) - const u_char *src; - char *dst; - size_t size; -{ - /* - * Note that int32_t and int16_t need only be "at least" large enough - * to contain a value of the specified size. On some systems, like - * Crays, there is no such thing as an integer variable with 16 bits. - * Keep this in mind if you think this function should have been coded - * to use pointer overlays. All the world's not a VAX. - */ - char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; - struct { int base, len; } best = {0}, cur = {0}; - u_int words[IN6ADDRSZ / INT16SZ]; - int i; - - /* - * Preprocess: - * Copy the input (bytewise) array into a wordwise array. - * Find the longest run of 0x00's in src[] for :: shorthanding. - */ - memset(words, 0, sizeof words); - for (i = 0; i < IN6ADDRSZ; i++) - words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); - best.base = -1; - cur.base = -1; - for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { - if (words[i] == 0) { - if (cur.base == -1) - cur.base = i, cur.len = 1; - else - cur.len++; - } else { - if (cur.base != -1) { - if (best.base == -1 || cur.len > best.len) - best = cur; - cur.base = -1; - } - } - } - if (cur.base != -1) { - if (best.base == -1 || cur.len > best.len) - best = cur; - } - if (best.base != -1 && best.len < 2) - best.base = -1; - - /* - * Format the result. - */ - tp = tmp; - for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { - /* Are we inside the best run of 0x00's? */ - if (best.base != -1 && i >= best.base && - i < (best.base + best.len)) { - if (i == best.base) - *tp++ = ':'; - continue; - } - /* Are we following an initial run of 0x00s or any real hex? */ - if (i != 0) - *tp++ = ':'; - /* Is this address an encapsulated IPv4? */ - if (i == 6 && best.base == 0 && - (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { - if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) - return (NULL); - tp += strlen(tp); - break; - } - //TuxSH: - //sprintf(tp, "%x", words[i]); - { - char hexbuf[8]; - char *e = hexbuf + 7; - u_int word = words[i]; - while(word > 0) { - static const char digits[] = "0123456789abcdef"; - *e-- = digits[word & 0xF]; - word >>= 4; - } - - memcpy(tp, e + 1, hexbuf + 8 - (e + 1)); - } - } - /* Was it a trailing run of 0x00's? */ - if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) - *tp++ = ':'; - *tp++ = '\0'; - - /* - * Check for overflow, copy, and we're done. - */ - if ((tp - tmp) > size) { - errno = ENOSPC; - return (NULL); - } - strcpy(dst, tmp); - return (dst); -} - -/* int - * inet_pton6(src, dst) - * convert presentation level address to network order binary form. - * return: - * 1 if `src' is a valid [RFC1884 2.2] address, else 0. - * notice: - * (1) does not touch `dst' unless it's returning 1. - * (2) :: in a full address is silently ignored. - * credit: - * inspired by Mark Andrews. - * author: - * Paul Vixie, 1996. - */ -static int -inet_pton6(src, dst) - const char *src; - u_char *dst; -{ - static const char xdigits_l[] = "0123456789abcdef", - xdigits_u[] = "0123456789ABCDEF"; - u_char tmp[IN6ADDRSZ], *tp, *endp, *colonp; - const char *xdigits, *curtok; - int ch, saw_xdigit; - u_int val; - - memset((tp = tmp), 0, IN6ADDRSZ); - endp = tp + IN6ADDRSZ; - colonp = NULL; - /* Leading :: requires some special handling. */ - if (*src == ':') - if (*++src != ':') - return (0); - curtok = src; - saw_xdigit = 0; - val = 0; - while ((ch = *src++) != '\0') { - const char *pch; - - if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) - pch = strchr((xdigits = xdigits_u), ch); - if (pch != NULL) { - val <<= 4; - val |= (pch - xdigits); - if (val > 0xffff) - return (0); - saw_xdigit = 1; - continue; - } - if (ch == ':') { - curtok = src; - if (!saw_xdigit) { - if (colonp) - return (0); - colonp = tp; - continue; - } - if (tp + INT16SZ > endp) - return (0); - *tp++ = (u_char) (val >> 8) & 0xff; - *tp++ = (u_char) val & 0xff; - saw_xdigit = 0; - val = 0; - continue; - } - if (ch == '.' && ((tp + INADDRSZ) <= endp) && - inet_pton4(curtok, tp) > 0) { - tp += INADDRSZ; - saw_xdigit = 0; - break; /* '\0' was seen by inet_pton4(). */ - } - return (0); - } - if (saw_xdigit) { - if (tp + INT16SZ > endp) - return (0); - *tp++ = (u_char) (val >> 8) & 0xff; - *tp++ = (u_char) val & 0xff; - } - if (colonp != NULL) { - /* - * Since some memmove()'s erroneously fail to handle - * overlapping regions, we'll do the shift by hand. - */ - const int n = tp - colonp; - int i; - - for (i = 1; i <= n; i++) { - endp[- i] = colonp[n - i]; - colonp[n - i] = 0; - } - tp = endp; - } - if (tp != endp) - return (0); - /* bcopy(tmp, dst, IN6ADDRSZ); */ - memcpy(dst, tmp, IN6ADDRSZ); - return (1); -} - -const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) { - switch(af) { - case AF_INET: - return inet_ntop4(src, dst, size); - case AF_INET6: - return inet_ntop6(src, dst, size); - default: - errno = EAFNOSUPPORT; - return NULL; - } -} - -int inet_pton(int af, const char *src, void *dst) { - switch(af) { - case AF_INET: - return inet_pton4(src, dst); - case AF_INET6: - return inet_pton6(src, dst); - default: - errno = EAFNOSUPPORT; - return -1; - } -} - -char *inet_ntoa(struct in_addr in) { - static __thread char buffer[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &in.s_addr, buffer, INET_ADDRSTRLEN); - return buffer; -} - -int inet_aton(const char *cp, struct in_addr *inp) { - int base; - size_t numBytes; - return _socketInetAtonDetail(&base, &numBytes, cp, inp); -} - -in_addr_t inet_addr(const char *cp) { - struct in_addr addr = { .s_addr = INADDR_BROADCAST }; - inet_aton(cp, &addr); - return addr.s_addr; -} - -/***********************************************************************************************************************/ - -static struct hostent *_socketDeserializeHostent(int *err, const void *out_he_serialized) { - const char *buf = (const char *)out_he_serialized; - const char *pos, *pos_aliases, *pos_addresses; - size_t name_size, total_aliases_size = 0; - size_t nb_addresses; - size_t nb_aliases = 0; - size_t nb_pos; - size_t len; - - int addrtype, addrlen; - struct hostent *he; - - // Calculate the size of the buffer to allocate - pos = buf; - name_size = strlen(pos) + 1; - pos += name_size; - - nb_aliases = ntohl(*(const u32 *)pos); - pos += 4; - - pos_aliases = pos; - - if(nb_aliases>0) { - for(nb_pos=0, len = 1; nb_posh_name = NULL; - he->h_aliases = (char**)((char*)he + sizeof(struct hostent)); - } - else { - he->h_name = (char*)he + sizeof(struct hostent); - memcpy(he->h_name, buf, name_size); - he->h_aliases = (char **)(he->h_name + name_size); - } - - he->h_addrtype = addrtype; - he->h_length = addrlen; - he->h_addr_list = he->h_aliases + nb_aliases + 1; - - if(nb_aliases>0) { - char *alias = (char *)(he->h_addr_list + nb_addresses + 1); - memcpy(alias, pos_aliases, total_aliases_size); - for(size_t i = 0; i < nb_aliases; i++) { - he->h_aliases[i] = alias; - alias += strlen(alias) + 1; - } - } - he->h_aliases[nb_aliases] = NULL; - - if(nb_addresses>0) { - struct in_addr *addresses = (struct in_addr *)(he->h_addr_list + nb_addresses + 1 + total_aliases_size); - memcpy(addresses, pos_addresses, addrlen * nb_addresses); - for(size_t i = 0; i < nb_addresses; i++) { - he->h_addr_list[i] = (char *)&addresses[i]; - addresses[i].s_addr = ntohl(addresses[i].s_addr); // lol Nintendo - } - } - he->h_addr_list[nb_addresses] = NULL; - - return he; -} - -struct addrinfo_serialized_hdr { - u32 magic; - int ai_flags; - int ai_family; - int ai_socktype; - int ai_protocol; - u32 ai_addrlen; -}; - -static size_t _socketSerializeAddrInfo(struct addrinfo_serialized_hdr *hdr, const struct addrinfo *ai) { - size_t subsize1 = (ai->ai_addr == NULL || ai->ai_addrlen == 0) ? 4 : ai->ai_addrlen; // not posix-compliant ? - size_t subsize2 = ai->ai_canonname == NULL ? 1 : (strlen(ai->ai_canonname) + 1); - - hdr->magic = htonl(0xBEEFCAFE); // Seriously. - hdr->ai_flags = htonl(ai->ai_flags); - hdr->ai_family = htonl(ai->ai_family); - hdr->ai_socktype = htonl(ai->ai_socktype); - hdr->ai_protocol = htonl(ai->ai_protocol); - hdr->ai_addrlen = ai->ai_addr == NULL ? 0 : htonl((u32)ai->ai_addrlen); - - if(hdr->ai_addrlen == 0) - *(u32 *)((u8 *)hdr + sizeof(struct addrinfo_serialized_hdr)) = 0; - else { - // Nintendo just byteswaps everything recursively... even fields that are already byteswapped. - switch(ai->ai_family) { - case AF_INET: { - struct sockaddr_in sa = {0}; - memcpy(&sa, ai->ai_addr, subsize1 <= sizeof(struct sockaddr_in) ? subsize1 : sizeof(struct sockaddr_in)); - sa.sin_port = htons(sa.sin_port); - sa.sin_addr.s_addr = htonl(sa.sin_addr.s_addr); - memcpy((u8 *)hdr + sizeof(struct addrinfo_serialized_hdr), &sa, sizeof(struct sockaddr_in)); - break; - } - case AF_INET6: { - struct sockaddr_in6 sa6 = {0}; - memcpy(&sa6, ai->ai_addr, subsize1 <= sizeof(struct sockaddr_in6) ? subsize1 : sizeof(struct sockaddr_in6)); - sa6.sin6_port = htons(sa6.sin6_port); - sa6.sin6_flowinfo = htonl(sa6.sin6_flowinfo); - sa6.sin6_scope_id = htonl(sa6.sin6_scope_id); - memcpy((u8 *)hdr + sizeof(struct addrinfo_serialized_hdr), &sa6, sizeof(struct sockaddr_in6)); - break; - } - default: - memcpy((u8 *)hdr + sizeof(struct addrinfo_serialized_hdr), ai->ai_addr, subsize1); - } - } - - if(ai->ai_canonname == NULL) - *((u8 *)hdr + sizeof(struct addrinfo_serialized_hdr) + subsize1) = 0; - else - memcpy((u8 *)hdr + sizeof(struct addrinfo_serialized_hdr) + subsize1, ai->ai_canonname, subsize2); - - return sizeof(struct addrinfo_serialized_hdr) + subsize1 + subsize2; -} - -static struct addrinfo_serialized_hdr *_socketSerializeAddrInfoList(size_t *out_size, const struct addrinfo *ai) { - size_t total_addrlen = 0, total_namelen = 0, n = 0; - struct addrinfo_serialized_hdr *out, *pos; - - for(const struct addrinfo *node = ai; node != NULL; node = node->ai_next) { - total_addrlen += node->ai_addrlen == 0 ? 4 : node->ai_addrlen; - total_namelen += node->ai_canonname == NULL ? 1 : (strlen(node->ai_canonname) + 1); - n++; - } - - out = (struct addrinfo_serialized_hdr *)malloc(sizeof(struct addrinfo_serialized_hdr) * n + total_addrlen + total_namelen + 4); - if(out == NULL) - return NULL; - - pos = out; - for(const struct addrinfo *node = ai; node != NULL; node = node->ai_next) { - size_t len = _socketSerializeAddrInfo(pos, node); - pos = (struct addrinfo_serialized_hdr *)((u8 *)pos + len); - } - *(u32 *)pos = 0; // Sentinel value - - *out_size = (u8 *)pos - (u8 *)out + 4; - return out; -} - -static struct addrinfo *_socketDeserializeAddrInfo(size_t *out_len, const struct addrinfo_serialized_hdr *hdr) { - struct addrinfo_node { - struct addrinfo info; - struct sockaddr_storage addr; - char canonname[]; - }; - - size_t subsize1 = hdr->ai_addrlen == 0 ? 4 : ntohl(hdr->ai_addrlen); - size_t subsize2 = strlen((const char *)hdr + sizeof(struct addrinfo_serialized_hdr) + subsize1) + 1; - struct addrinfo_node *node = (struct addrinfo_node *)malloc(sizeof(struct addrinfo_node) + subsize2); - - *out_len = sizeof(struct addrinfo_serialized_hdr) + subsize1 + subsize2; - if(node == NULL) - return NULL; - - node->info.ai_flags = ntohl(hdr->ai_flags); - node->info.ai_family = ntohl(hdr->ai_family); - node->info.ai_socktype = ntohl(hdr->ai_socktype); - node->info.ai_protocol = ntohl(hdr->ai_protocol); - node->info.ai_addrlen = ntohl(hdr->ai_addrlen); - - // getaddrinfo enforces addrlen = sizeof(struct sockaddr) and family = AF_INET, ie. only IPv4, anyways... - if(node->info.ai_addrlen > sizeof(struct sockaddr_storage)) - node->info.ai_addrlen = sizeof(struct sockaddr_storage); - - if(node->info.ai_addrlen == 0) - node->info.ai_addr = NULL; - else { - node->info.ai_addr = (struct sockaddr *)&node->addr; - memcpy(node->info.ai_addr, (const u8 *)hdr + sizeof(struct addrinfo_serialized_hdr), node->info.ai_addrlen); - // Nintendo just byteswaps everything recursively... even fields that are already byteswapped. - switch(node->info.ai_family) { - case AF_INET: { - struct sockaddr_in *sa = (struct sockaddr_in *)node->info.ai_addr; - sa->sin_port = ntohs(sa->sin_port); - sa->sin_addr.s_addr = ntohl(sa->sin_addr.s_addr); - break; - } - case AF_INET6: { - struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)node->info.ai_addr; - sa6->sin6_port = ntohs(sa6->sin6_port); - sa6->sin6_flowinfo = ntohl(sa6->sin6_flowinfo); - sa6->sin6_scope_id = ntohl(sa6->sin6_scope_id); - break; - } - default: - break; - } - } - - if(subsize2 == 1) - node->info.ai_canonname = NULL; - else { - node->info.ai_canonname = node->canonname; - memcpy(node->info.ai_canonname, (const u8 *)hdr + sizeof(struct addrinfo_serialized_hdr) + subsize1, subsize2); - } - - node->info.ai_next = NULL; - - return &node->info; -} - -static struct addrinfo *_socketDeserializeAddrInfoList(struct addrinfo_serialized_hdr *hdr) { - struct addrinfo *first = NULL, *prev = NULL; - - while(hdr->magic == htonl(0xBEEFCAFE)) { - size_t len; - struct addrinfo *node = _socketDeserializeAddrInfo(&len, hdr); - if(node == NULL) { - if(first != NULL) - freeaddrinfo(first); - return NULL; - } - - if(first == NULL) - first = node; - - if(prev != NULL) - prev->ai_next = node; - - prev = node; - hdr = (struct addrinfo_serialized_hdr *)((u8 *)hdr + len); - } - - return first; -} - -void freehostent(struct hostent *he) { - free(he); -} - -void freeaddrinfo(struct addrinfo *ai) { - if(ai == NULL) return; // not specified by POSIX, but that's convenient (FreeBSD does this too, etc.). - for(struct addrinfo *node = ai, *next; node != NULL; node = next) { - next = node->ai_next; - free(node); - } -} - -struct hostent *gethostbyname(const char *name) { - Result rc = 0; - void *out_he_serialized = malloc(g_sfdnsresConfig.serialized_out_hostent_max_size); - struct hostent *he = NULL; - u32 ret = 0; - u32 errno_ = 0; - - if(out_he_serialized == NULL) { - h_errno = NO_RECOVERY; - errno = ENOMEM; // POSIX leaves this unspecified - goto cleanup; - } - rc = sfdnsresGetHostByNameRequest( - g_sfdnsresConfig.timeout, - !g_sfdnsresConfig.bypass_nsd, - name, - &ret, - &errno_, - out_he_serialized, g_sfdnsresConfig.serialized_out_hostent_max_size, - NULL); - if(rc == 0xD401) { - h_errno = NO_RECOVERY; - errno = EFAULT; // POSIX leaves this unspecified - goto cleanup; - } - else if(R_FAILED(rc) && R_MODULE(rc) == 1) { // Kernel - h_errno = TRY_AGAIN; - errno = EAGAIN; // POSIX leaves this unspecified - goto cleanup; - } - else if(R_FAILED(rc)) { - h_errno = NO_RECOVERY; - errno = EINVAL; // POSIX leaves this unspecified - goto cleanup; - } - if(ret != NETDB_SUCCESS) { - h_errno = ret; - errno = errno_; // POSIX leaves this unspecified - goto cleanup; - } - - he = _socketDeserializeHostent(&h_errno, out_he_serialized); - if(he == NULL) { - h_errno = NO_RECOVERY; - errno = ENOMEM; // POSIX leaves this unspecified - } -cleanup: - g_sfdnsresResult = rc; - free(out_he_serialized); - return he; -} - -struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type) { - Result rc = 0; - void *out_he_serialized = malloc(g_sfdnsresConfig.serialized_out_hostent_max_size); - struct hostent *he = NULL; - u32 ret = 0; - u32 errno_ = 0; - - if(out_he_serialized == NULL) { - h_errno = NO_RECOVERY; - errno = ENOMEM; // POSIX leaves this unspecified - goto cleanup; - } - rc = sfdnsresGetHostByAddrRequest( - addr, len, - type, - g_sfdnsresConfig.timeout, - &ret, - &errno_, - out_he_serialized, g_sfdnsresConfig.serialized_out_hostent_max_size, - NULL); - if(rc == 0xD401) { - h_errno = NO_RECOVERY; // POSIX leaves this unspecified - errno = EFAULT; - goto cleanup; - } - else if(R_FAILED(rc) && R_MODULE(rc) == 1) { // Kernel - h_errno = TRY_AGAIN; - errno = EAGAIN; // POSIX leaves this unspecified - goto cleanup; - } - else if(R_FAILED(rc)) { - h_errno = NO_RECOVERY; - errno = EINVAL; // POSIX leaves this unspecified - goto cleanup; - } - if(ret != NETDB_SUCCESS) { - h_errno = ret; - errno = errno_; // POSIX leaves this unspecified - goto cleanup; - } - - he = _socketDeserializeHostent(&h_errno, out_he_serialized); - if(he == NULL) { - h_errno = NO_RECOVERY; - errno = ENOMEM; // POSIX leaves this unspecified - } -cleanup: - g_sfdnsresResult = rc; - free(out_he_serialized); - return he; -} - -const char *hstrerror(int err) { - static __thread char buf[0x80]; - Result rc = sfdnsresGetHostStringErrorRequest(err, buf, sizeof(buf)); - if(R_FAILED(rc)) // a bit limiting, given the broad range of errors the kernel can give to us... - strcpy(buf, "System busy, try again."); - g_sfdnsresResult = rc; - return buf; -} - -void herror(const char *str) { - fprintf(stderr, "%s: %s\n", str, hstrerror(h_errno)); -} - -const char *gai_strerror(int err) { - static __thread char buf[0x80]; - Result rc = sfdnsresGetGaiStringErrorRequest(err, buf, sizeof(buf)); - if(R_FAILED(rc)) - strcpy(buf, "System busy, try again."); - g_sfdnsresResult = rc; - return buf; -} - -int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { - Result rc = 0; - size_t hints_sz; - struct addrinfo_serialized_hdr *hints_serialized = _socketSerializeAddrInfoList(&hints_sz, hints); - struct addrinfo_serialized_hdr *out_serialized = NULL; - struct addrinfo *out = NULL; - u32 errno_ = 0; - s32 gaie = 0; - - if(hints_serialized == NULL) { - gaie = EAI_MEMORY; - goto cleanup; - } - - out_serialized = (struct addrinfo_serialized_hdr *)malloc(g_sfdnsresConfig.serialized_out_addrinfos_max_size); - - if(out_serialized == NULL) { - gaie = EAI_MEMORY; - goto cleanup; - } - - rc = sfdnsresGetAddrInfoRequest( - g_sfdnsresConfig.timeout, - !g_sfdnsresConfig.bypass_nsd, - node, - service, - hints_serialized, hints_sz, - out_serialized, g_sfdnsresConfig.serialized_out_addrinfos_max_size, - &errno_, - &gaie, - NULL); - - if(rc == 0xD401) { - gaie = EAI_SYSTEM; - errno = EFAULT; - goto cleanup; - } - else if(R_FAILED(rc) && R_MODULE(rc) == 1) { // Kernel - gaie = EAI_AGAIN; - goto cleanup; - } - else if(R_FAILED(rc)) { - gaie = EAI_FAIL; - goto cleanup; - } - - if(gaie != 0) { - if(gaie == EAI_SYSTEM) - errno = errno_; - goto cleanup; - } - - out = _socketDeserializeAddrInfoList(out_serialized); - if(out == NULL) - gaie = EAI_MEMORY; -cleanup: - *res = out; - free(hints_serialized); - free(out_serialized); - g_sfdnsresResult = rc; - - return gaie; -} - -int getnameinfo(const struct sockaddr *sa, socklen_t salen, - char *host, socklen_t hostlen, - char *serv, socklen_t servlen, - int flags) { - Result rc = 0; - u32 errno_ = 0; - s32 gaie = 0; - - rc = sfdnsresGetNameInfoRequest( - flags, - sa, salen, - host, hostlen, - serv, servlen, - g_sfdnsresConfig.timeout, - &errno_, - &gaie); - if(rc == 0xD401) { - gaie = EAI_SYSTEM; - errno = EFAULT; - goto cleanup; - } - else if(R_FAILED(rc) && R_MODULE(rc) == 1) { // Kernel - gaie = EAI_AGAIN; - goto cleanup; - } - else if(R_FAILED(rc)) { - gaie = EAI_FAIL; - goto cleanup; - } - - if(gaie != 0) { - if(gaie == EAI_SYSTEM) - errno = errno_; - } - -cleanup: - g_sfdnsresResult = rc; - return gaie; -} - -long gethostid(void) { - Result rc; - u32 id; - rc = nifmGetCurrentIpAddress(&id); - if(R_SUCCEEDED(rc)) - return id; - return INADDR_LOOPBACK; -} - -int gethostname(char *name, size_t namelen) { - // The Switch doesn't have a proper name, so let's use its IP - struct in_addr in; - in.s_addr = gethostid(); - const char *hostname = inet_ntop(AF_INET, &in, name, namelen); - return hostname == NULL ? -1 : 0; -} - -// Unimplementable functions, left for compliance: -struct hostent *gethostent(void) { h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } -struct netent *getnetbyaddr(uint32_t a, int b) { (void)a; (void)b; h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } -struct netent *getnetbyname(const char *s) { (void)s; h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } -struct netent *getnetent(void) { h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } -struct protoent *getprotobyname(const char *s) { (void)s; h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } -struct protoent *getprotobynumber(int a) { (void)a; h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } -struct protoent *getprotoent(void) { h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } -struct servent *getservbyname(const char *s1, const char *s2) { (void)s1; (void)s2; h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } -struct servent *getservbyport(int a, const char *s) { (void)a; (void)s; h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } -struct servent *getservent(void) { h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } -void sethostent(int a) { (void)a;} -void setnetent(int a) { (void)a;} -void setprotoent(int a) { (void)a; } diff --git a/nx/source/runtime/resolver.c b/nx/source/runtime/resolver.c new file mode 100644 index 00000000..b71e3cb6 --- /dev/null +++ b/nx/source/runtime/resolver.c @@ -0,0 +1,577 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "result.h" +#include "services/sfdnsres.h" +#include "services/nifm.h" +#include "runtime/resolver.h" + +__thread int h_errno; + +static __thread Result g_resolverResult; + +static size_t g_resolverHostByNameBufferSize = 0x200; // ResolverOptionLocalKey::GetHostByNameBufferSizeUnsigned64 +static size_t g_resolverHostByAddrBufferSize = 0x200; // ResolverOptionLocalKey::GetHostByAddrBufferSizeUnsigned64 +static size_t g_resolverAddrInfoBufferSize = 0x1000; // ResolverOptionLocalKey::GetAddrInfoBufferSizeUnsigned64 +static size_t g_resolverAddrInfoHintsBufferSize = 0x400; // ResolverOptionLocalKey::GetAddrInfoHintsBufferSizeUnsigned64 + +Result resolverGetLastResult(void) { + return g_resolverResult; +} + +static struct hostent *_resolverDeserializeHostent(int *err, const void *out_he_serialized) { + const char *buf = (const char *)out_he_serialized; + const char *pos, *pos_aliases, *pos_addresses; + size_t name_size, total_aliases_size = 0; + size_t nb_addresses; + size_t nb_aliases = 0; + size_t nb_pos; + size_t len; + + int addrtype, addrlen; + struct hostent *he; + + // Calculate the size of the buffer to allocate + pos = buf; + name_size = strlen(pos) + 1; + pos += name_size; + + nb_aliases = ntohl(*(const u32 *)pos); + pos += 4; + + pos_aliases = pos; + + if(nb_aliases>0) { + for(nb_pos=0, len = 1; nb_posh_name = NULL; + he->h_aliases = (char**)((char*)he + sizeof(struct hostent)); + } + else { + he->h_name = (char*)he + sizeof(struct hostent); + memcpy(he->h_name, buf, name_size); + he->h_aliases = (char **)(he->h_name + name_size); + } + + he->h_addrtype = addrtype; + he->h_length = addrlen; + he->h_addr_list = he->h_aliases + nb_aliases + 1; + + if(nb_aliases>0) { + char *alias = (char *)(he->h_addr_list + nb_addresses + 1); + memcpy(alias, pos_aliases, total_aliases_size); + for(size_t i = 0; i < nb_aliases; i++) { + he->h_aliases[i] = alias; + alias += strlen(alias) + 1; + } + } + he->h_aliases[nb_aliases] = NULL; + + if(nb_addresses>0) { + struct in_addr *addresses = (struct in_addr *)(he->h_addr_list + nb_addresses + 1 + total_aliases_size); + memcpy(addresses, pos_addresses, addrlen * nb_addresses); + for(size_t i = 0; i < nb_addresses; i++) { + he->h_addr_list[i] = (char *)&addresses[i]; + addresses[i].s_addr = ntohl(addresses[i].s_addr); // lol Nintendo + } + } + he->h_addr_list[nb_addresses] = NULL; + + return he; +} + +struct addrinfo_serialized_hdr { + u32 magic; + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + u32 ai_addrlen; +}; + +static size_t _resolverSerializeAddrInfo(struct addrinfo_serialized_hdr *hdr, const struct addrinfo *ai) { + size_t subsize1 = (ai->ai_addr == NULL || ai->ai_addrlen == 0) ? 4 : ai->ai_addrlen; // not posix-compliant ? + size_t subsize2 = ai->ai_canonname == NULL ? 1 : (strlen(ai->ai_canonname) + 1); + + hdr->magic = htonl(0xBEEFCAFE); // Seriously. + hdr->ai_flags = htonl(ai->ai_flags); + hdr->ai_family = htonl(ai->ai_family); + hdr->ai_socktype = htonl(ai->ai_socktype); + hdr->ai_protocol = htonl(ai->ai_protocol); + hdr->ai_addrlen = ai->ai_addr == NULL ? 0 : htonl((u32)ai->ai_addrlen); + + if(hdr->ai_addrlen == 0) + *(u32 *)((u8 *)hdr + sizeof(struct addrinfo_serialized_hdr)) = 0; + else { + // Nintendo just byteswaps everything recursively... even fields that are already byteswapped. + switch(ai->ai_family) { + case AF_INET: { + struct sockaddr_in sa = {0}; + memcpy(&sa, ai->ai_addr, subsize1 <= sizeof(struct sockaddr_in) ? subsize1 : sizeof(struct sockaddr_in)); + sa.sin_port = htons(sa.sin_port); + sa.sin_addr.s_addr = htonl(sa.sin_addr.s_addr); + memcpy((u8 *)hdr + sizeof(struct addrinfo_serialized_hdr), &sa, sizeof(struct sockaddr_in)); + break; + } + case AF_INET6: { + struct sockaddr_in6 sa6 = {0}; + memcpy(&sa6, ai->ai_addr, subsize1 <= sizeof(struct sockaddr_in6) ? subsize1 : sizeof(struct sockaddr_in6)); + sa6.sin6_port = htons(sa6.sin6_port); + sa6.sin6_flowinfo = htonl(sa6.sin6_flowinfo); + sa6.sin6_scope_id = htonl(sa6.sin6_scope_id); + memcpy((u8 *)hdr + sizeof(struct addrinfo_serialized_hdr), &sa6, sizeof(struct sockaddr_in6)); + break; + } + default: + memcpy((u8 *)hdr + sizeof(struct addrinfo_serialized_hdr), ai->ai_addr, subsize1); + } + } + + if(ai->ai_canonname == NULL) + *((u8 *)hdr + sizeof(struct addrinfo_serialized_hdr) + subsize1) = 0; + else + memcpy((u8 *)hdr + sizeof(struct addrinfo_serialized_hdr) + subsize1, ai->ai_canonname, subsize2); + + return sizeof(struct addrinfo_serialized_hdr) + subsize1 + subsize2; +} + +static struct addrinfo_serialized_hdr *_resolverSerializeAddrInfoList(size_t *out_size, const struct addrinfo *ai) { + size_t total_addrlen = 0, total_namelen = 0, n = 0; + struct addrinfo_serialized_hdr *out, *pos; + + for(const struct addrinfo *node = ai; node != NULL; node = node->ai_next) { + total_addrlen += node->ai_addrlen == 0 ? 4 : node->ai_addrlen; + total_namelen += node->ai_canonname == NULL ? 1 : (strlen(node->ai_canonname) + 1); + n++; + } + + out = (struct addrinfo_serialized_hdr *)malloc(sizeof(struct addrinfo_serialized_hdr) * n + total_addrlen + total_namelen + 4); + if(out == NULL) + return NULL; + + pos = out; + for(const struct addrinfo *node = ai; node != NULL; node = node->ai_next) { + size_t len = _resolverSerializeAddrInfo(pos, node); + pos = (struct addrinfo_serialized_hdr *)((u8 *)pos + len); + } + *(u32 *)pos = 0; // Sentinel value + + *out_size = (u8 *)pos - (u8 *)out + 4; + return out; +} + +static struct addrinfo *_resolverDeserializeAddrInfo(size_t *out_len, const struct addrinfo_serialized_hdr *hdr) { + struct addrinfo_node { + struct addrinfo info; + struct sockaddr_storage addr; + char canonname[]; + }; + + size_t subsize1 = hdr->ai_addrlen == 0 ? 4 : ntohl(hdr->ai_addrlen); + size_t subsize2 = strlen((const char *)hdr + sizeof(struct addrinfo_serialized_hdr) + subsize1) + 1; + struct addrinfo_node *node = (struct addrinfo_node *)malloc(sizeof(struct addrinfo_node) + subsize2); + + *out_len = sizeof(struct addrinfo_serialized_hdr) + subsize1 + subsize2; + if(node == NULL) + return NULL; + + node->info.ai_flags = ntohl(hdr->ai_flags); + node->info.ai_family = ntohl(hdr->ai_family); + node->info.ai_socktype = ntohl(hdr->ai_socktype); + node->info.ai_protocol = ntohl(hdr->ai_protocol); + node->info.ai_addrlen = ntohl(hdr->ai_addrlen); + + // getaddrinfo enforces addrlen = sizeof(struct sockaddr) and family = AF_INET, ie. only IPv4, anyways... + if(node->info.ai_addrlen > sizeof(struct sockaddr_storage)) + node->info.ai_addrlen = sizeof(struct sockaddr_storage); + + if(node->info.ai_addrlen == 0) + node->info.ai_addr = NULL; + else { + node->info.ai_addr = (struct sockaddr *)&node->addr; + memcpy(node->info.ai_addr, (const u8 *)hdr + sizeof(struct addrinfo_serialized_hdr), node->info.ai_addrlen); + // Nintendo just byteswaps everything recursively... even fields that are already byteswapped. + switch(node->info.ai_family) { + case AF_INET: { + struct sockaddr_in *sa = (struct sockaddr_in *)node->info.ai_addr; + sa->sin_len = 6; + sa->sin_port = ntohs(sa->sin_port); + sa->sin_addr.s_addr = ntohl(sa->sin_addr.s_addr); + break; + } + case AF_INET6: { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)node->info.ai_addr; + sa6->sin6_port = ntohs(sa6->sin6_port); + sa6->sin6_flowinfo = ntohl(sa6->sin6_flowinfo); + sa6->sin6_scope_id = ntohl(sa6->sin6_scope_id); + break; + } + default: + break; + } + } + + if(subsize2 == 1) + node->info.ai_canonname = NULL; + else { + node->info.ai_canonname = node->canonname; + memcpy(node->info.ai_canonname, (const u8 *)hdr + sizeof(struct addrinfo_serialized_hdr) + subsize1, subsize2); + } + + node->info.ai_next = NULL; + + return &node->info; +} + +static struct addrinfo *_resolverDeserializeAddrInfoList(struct addrinfo_serialized_hdr *hdr) { + struct addrinfo *first = NULL, *prev = NULL; + + while(hdr->magic == htonl(0xBEEFCAFE)) { + size_t len; + struct addrinfo *node = _resolverDeserializeAddrInfo(&len, hdr); + if(node == NULL) { + if(first != NULL) + freeaddrinfo(first); + return NULL; + } + + if(first == NULL) + first = node; + + if(prev != NULL) + prev->ai_next = node; + + prev = node; + hdr = (struct addrinfo_serialized_hdr *)((u8 *)hdr + len); + } + + return first; +} + +void freehostent(struct hostent *he) { + free(he); +} + +void freeaddrinfo(struct addrinfo *ai) { + if(ai == NULL) return; // not specified by POSIX, but that's convenient (FreeBSD does this too, etc.). + for(struct addrinfo *node = ai, *next; node != NULL; node = next) { + next = node->ai_next; + free(node); + } +} + +struct hostent *gethostbyname(const char *name) { + Result rc = 0; + void *out_he_serialized = malloc(g_resolverHostByNameBufferSize); + struct hostent *he = NULL; + u32 ret = 0; + u32 errno_ = 0; + + if(out_he_serialized == NULL) { + h_errno = NO_RECOVERY; + errno = ENOMEM; // POSIX leaves this unspecified + goto cleanup; + } + rc = sfdnsresGetHostByNameRequest( + 0, + true, + name, + &ret, + &errno_, + out_he_serialized, g_resolverHostByNameBufferSize, + NULL); + if(rc == 0xD401) { + h_errno = NO_RECOVERY; + errno = EFAULT; // POSIX leaves this unspecified + goto cleanup; + } + else if(R_FAILED(rc) && R_MODULE(rc) == 1) { // Kernel + h_errno = TRY_AGAIN; + errno = EAGAIN; // POSIX leaves this unspecified + goto cleanup; + } + else if(R_FAILED(rc)) { + h_errno = NO_RECOVERY; + errno = EINVAL; // POSIX leaves this unspecified + goto cleanup; + } + if(ret != NETDB_SUCCESS) { + h_errno = ret; + errno = errno_; // POSIX leaves this unspecified + goto cleanup; + } + + he = _resolverDeserializeHostent(&h_errno, out_he_serialized); + if(he == NULL) { + h_errno = NO_RECOVERY; + errno = ENOMEM; // POSIX leaves this unspecified + } +cleanup: + g_resolverResult = rc; + free(out_he_serialized); + return he; +} + +struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type) { + Result rc = 0; + void *out_he_serialized = malloc(g_resolverHostByAddrBufferSize); + struct hostent *he = NULL; + u32 ret = 0; + u32 errno_ = 0; + + if(out_he_serialized == NULL) { + h_errno = NO_RECOVERY; + errno = ENOMEM; // POSIX leaves this unspecified + goto cleanup; + } + rc = sfdnsresGetHostByAddrRequest( + addr, len, + type, + 0, + &ret, + &errno_, + out_he_serialized, g_resolverHostByAddrBufferSize, + NULL); + if(rc == 0xD401) { + h_errno = NO_RECOVERY; // POSIX leaves this unspecified + errno = EFAULT; + goto cleanup; + } + else if(R_FAILED(rc) && R_MODULE(rc) == 1) { // Kernel + h_errno = TRY_AGAIN; + errno = EAGAIN; // POSIX leaves this unspecified + goto cleanup; + } + else if(R_FAILED(rc)) { + h_errno = NO_RECOVERY; + errno = EINVAL; // POSIX leaves this unspecified + goto cleanup; + } + if(ret != NETDB_SUCCESS) { + h_errno = ret; + errno = errno_; // POSIX leaves this unspecified + goto cleanup; + } + + he = _resolverDeserializeHostent(&h_errno, out_he_serialized); + if(he == NULL) { + h_errno = NO_RECOVERY; + errno = ENOMEM; // POSIX leaves this unspecified + } +cleanup: + g_resolverResult = rc; + free(out_he_serialized); + return he; +} + +const char *hstrerror(int err) { + static __thread char buf[0x80]; // ResolverOptionLocalKey::GetHostErrorStringBufferSizeUnsigned64 + Result rc = sfdnsresGetHostStringErrorRequest(err, buf, sizeof(buf)); + if(R_FAILED(rc)) // a bit limiting, given the broad range of errors the kernel can give to us... + strcpy(buf, "System busy, try again."); + g_resolverResult = rc; + return buf; +} + +void herror(const char *str) { + fprintf(stderr, "%s: %s\n", str, hstrerror(h_errno)); +} + +const char *gai_strerror(int err) { + static __thread char buf[0x80]; // ResolverOptionLocalKey::GaiErrorStringBufferSizeUnsigned64 + Result rc = sfdnsresGetGaiStringErrorRequest(err, buf, sizeof(buf)); + if(R_FAILED(rc)) + strcpy(buf, "System busy, try again."); + g_resolverResult = rc; + return buf; +} + +int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { + if (!node && !service) + return EAI_NONAME; + + if (!res) { + errno = EINVAL; + return EAI_SYSTEM; + } + + if (!g_resolverAddrInfoBufferSize) { + errno = ENOSPC; + return EAI_SYSTEM; + } + + size_t hints_sz = 0; + struct addrinfo_serialized_hdr *hints_serialized = NULL; + if (hints) { + // TODO: fixed size for serialized hints (g_resolverAddrInfoHintsBufferSize) + (void)g_resolverAddrInfoHintsBufferSize; + hints_serialized = _resolverSerializeAddrInfoList(&hints_sz, hints); + if (!hints_serialized) { + errno = ENOMEM; + return EAI_MEMORY; + } + } + + struct addrinfo_serialized_hdr *out_serialized = malloc(g_resolverAddrInfoBufferSize); + if (!out_serialized) { + free(hints_serialized); + errno = ENOMEM; + return EAI_FAIL; + } + + s32 gaie = 0; + Result rc = sfdnsresGetAddrInfoRequest( + 0, + true, + node, + service, + hints_serialized, hints_sz, + out_serialized, g_resolverAddrInfoBufferSize, + (u32*)&errno, + &gaie, + NULL); + g_resolverResult = rc; + free(hints_serialized); + + if (R_FAILED(rc)) { + if (R_MODULE(rc) == 21) // SM + errno = EAGAIN; + else if (R_MODULE(rc) == 1) // Kernel + errno = EFAULT; + else + errno = EPIPE; + gaie = EAI_SYSTEM; + } + + if (gaie == 0) { + *res = _resolverDeserializeAddrInfoList(out_serialized); + if (!*res) { + errno = ENOMEM; + gaie = EAI_MEMORY; + } + } + + free(out_serialized); + return gaie; +} + +int getnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, socklen_t hostlen, + char *serv, socklen_t servlen, + int flags) { + Result rc = 0; + u32 errno_ = 0; + s32 gaie = 0; + + rc = sfdnsresGetNameInfoRequest( + flags, + sa, salen, + host, hostlen, + serv, servlen, + 0, + &errno_, + &gaie); + if(rc == 0xD401) { + gaie = EAI_SYSTEM; + errno = EFAULT; + goto cleanup; + } + else if(R_FAILED(rc) && R_MODULE(rc) == 1) { // Kernel + gaie = EAI_AGAIN; + goto cleanup; + } + else if(R_FAILED(rc)) { + gaie = EAI_FAIL; + goto cleanup; + } + + if(gaie != 0) { + if(gaie == EAI_SYSTEM) + errno = errno_; + } + +cleanup: + g_resolverResult = rc; + return gaie; +} + +long gethostid(void) { + u32 id = INADDR_LOOPBACK; + + Result rc = nifmInitialize(); + if (R_SUCCEEDED(rc)) { + rc = nifmGetCurrentIpAddress(&id); + nifmExit(); + } + + g_resolverResult = rc; + return id; +} + +int gethostname(char *name, size_t namelen) { + // The Switch doesn't have a proper name, so let's use its IP + struct in_addr in; + in.s_addr = gethostid(); + const char *hostname = inet_ntop(AF_INET, &in, name, namelen); + return hostname == NULL ? -1 : 0; +} + +// Unimplementable functions, left for compliance: +struct hostent *gethostent(void) { h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } +struct netent *getnetbyaddr(uint32_t a, int b) { (void)a; (void)b; h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } +struct netent *getnetbyname(const char *s) { (void)s; h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } +struct netent *getnetent(void) { h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } +struct protoent *getprotobyname(const char *s) { (void)s; h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } +struct protoent *getprotobynumber(int a) { (void)a; h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } +struct protoent *getprotoent(void) { h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } +struct servent *getservbyname(const char *s1, const char *s2) { (void)s1; (void)s2; h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } +struct servent *getservbyport(int a, const char *s) { (void)a; (void)s; h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } +struct servent *getservent(void) { h_errno = NO_RECOVERY; errno = ENOSYS; return NULL; } +void sethostent(int a) { (void)a;} +void setnetent(int a) { (void)a;} +void setprotoent(int a) { (void)a; } diff --git a/nx/source/runtime/util/inet_addr.c b/nx/source/runtime/util/inet_addr.c new file mode 100644 index 00000000..dfa8b17c --- /dev/null +++ b/nx/source/runtime/util/inet_addr.c @@ -0,0 +1,404 @@ +#include +#include +#include +#include + +#include +#include + +const struct in6_addr in6addr_any = {0}; +const struct in6_addr in6addr_loopback = {.__u6_addr32 = {0, 0, 0, __builtin_bswap32(1)}}; + +// Adapted from libctru +static int _inetAtonDetail(int *outBase, size_t *outNumBytes, const char *cp, struct in_addr *inp) { + int base; + uint32_t val; + int c; + char bytes[4]; + size_t num_bytes = 0; + + c = *cp; + for(;;) { + if(!isdigit(c)) return 0; + + val = 0; + base = 10; + if(c == '0') { + c = *++cp; + if(c == 'x' || c == 'X') { + base = 16; + c = *++cp; + } + else base = 8; + } + + for(;;) { + if(isdigit(c)) { + if(base == 8 && c >= '8') return 0; + val *= base; + val += c - '0'; + c = *++cp; + } + else if(base == 16 && isxdigit(c)) { + val *= base; + val += c + 10 - (islower(c) ? 'a' : 'A'); + c = *++cp; + } + else break; + } + + if(c == '.') { + if(num_bytes > 3) return 0; + if(val > 0xFF) return 0; + bytes[num_bytes++] = val; + c = *++cp; + } + else break; + } + + if(c != 0) { + *outNumBytes = num_bytes; + *outBase = base; + return 0; + } + + switch(num_bytes) { + case 0: + break; + + case 1: + if(val > 0xFFFFFF) return 0; + val |= bytes[0] << 24; + break; + + case 2: + if(val > 0xFFFF) return 0; + val |= bytes[0] << 24; + val |= bytes[1] << 16; + break; + + case 3: + if(val > 0xFF) return 0; + val |= bytes[0] << 24; + val |= bytes[1] << 16; + val |= bytes[2] << 8; + break; + } + + if(inp) + inp->s_addr = htonl(val); + + *outNumBytes = num_bytes; + *outBase = base; + + return 1; +} + +// Adapted from libctru +static const char *inet_ntop4(const void *src, char *dst, socklen_t size) { + const uint8_t *ip = src; + + char *p; + size_t i; + unsigned int n; + + if(size < INET_ADDRSTRLEN) { + errno = ENOSPC; + return NULL; + } + + for(p = dst, i = 0; i < 4; ++i) { + if(i > 0) *p++ = '.'; + + n = ip[i]; + if(n >= 100) { + *p++ = n/100 + '0'; + n %= 100; + } + if(n >= 10 || ip[i] >= 100) { + *p++ = n/10 + '0'; + n %= 10; + } + *p++ = n + '0'; + } + *p = 0; + + return dst; +} + +static int inet_pton4(const char *src, void *dst) { + int base; + size_t numBytes; + + int ret = _inetAtonDetail(&base, &numBytes, src, (struct in_addr *)dst); + return (ret == 1 && base == 10 && numBytes == 3) ? 1 : 0; +} + +/* Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#define INADDRSZ 4 +#define IN6ADDRSZ 16 +#define INT16SZ 2 +/* const char * + * inet_ntop6(src, dst, size) + * convert IPv6 binary address into presentation (printable) format + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop6(src, dst, size) + const u_char *src; + char *dst; + size_t size; +{ + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; + struct { int base, len; } best = {0}, cur = {0}; + u_int words[IN6ADDRSZ / INT16SZ]; + int i; + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, 0, sizeof words); + for (i = 0; i < IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + cur.base = -1; + for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && + (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { + if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) + return (NULL); + tp += strlen(tp); + break; + } + //TuxSH: + //sprintf(tp, "%x", words[i]); + { + char hexbuf[8]; + char *e = hexbuf + 7; + u_int word = words[i]; + while(word > 0) { + static const char digits[] = "0123456789abcdef"; + *e-- = digits[word & 0xF]; + word >>= 4; + } + + memcpy(tp, e + 1, hexbuf + 8 - (e + 1)); + } + } + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) + *tp++ = ':'; + *tp++ = '\0'; + + /* + * Check for overflow, copy, and we're done. + */ + if ((tp - tmp) > size) { + errno = ENOSPC; + return (NULL); + } + strcpy(dst, tmp); + return (dst); +} + +/* int + * inet_pton6(src, dst) + * convert presentation level address to network order binary form. + * return: + * 1 if `src' is a valid [RFC1884 2.2] address, else 0. + * notice: + * (1) does not touch `dst' unless it's returning 1. + * (2) :: in a full address is silently ignored. + * credit: + * inspired by Mark Andrews. + * author: + * Paul Vixie, 1996. + */ +static int +inet_pton6(src, dst) + const char *src; + u_char *dst; +{ + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + u_char tmp[IN6ADDRSZ], *tp, *endp, *colonp; + const char *xdigits, *curtok; + int ch, saw_xdigit; + u_int val; + + memset((tp = tmp), 0, IN6ADDRSZ); + endp = tp + IN6ADDRSZ; + colonp = NULL; + /* Leading :: requires some special handling. */ + if (*src == ':') + if (*++src != ':') + return (0); + curtok = src; + saw_xdigit = 0; + val = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) + pch = strchr((xdigits = xdigits_u), ch); + if (pch != NULL) { + val <<= 4; + val |= (pch - xdigits); + if (val > 0xffff) + return (0); + saw_xdigit = 1; + continue; + } + if (ch == ':') { + curtok = src; + if (!saw_xdigit) { + if (colonp) + return (0); + colonp = tp; + continue; + } + if (tp + INT16SZ > endp) + return (0); + *tp++ = (u_char) (val >> 8) & 0xff; + *tp++ = (u_char) val & 0xff; + saw_xdigit = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + INADDRSZ) <= endp) && + inet_pton4(curtok, tp) > 0) { + tp += INADDRSZ; + saw_xdigit = 0; + break; /* '\0' was seen by inet_pton4(). */ + } + return (0); + } + if (saw_xdigit) { + if (tp + INT16SZ > endp) + return (0); + *tp++ = (u_char) (val >> 8) & 0xff; + *tp++ = (u_char) val & 0xff; + } + if (colonp != NULL) { + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we'll do the shift by hand. + */ + const int n = tp - colonp; + int i; + + for (i = 1; i <= n; i++) { + endp[- i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + return (0); + /* bcopy(tmp, dst, IN6ADDRSZ); */ + memcpy(dst, tmp, IN6ADDRSZ); + return (1); +} + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) { + switch(af) { + case AF_INET: + return inet_ntop4(src, dst, size); + case AF_INET6: + return inet_ntop6(src, dst, size); + default: + errno = EAFNOSUPPORT; + return NULL; + } +} + +int inet_pton(int af, const char *src, void *dst) { + switch(af) { + case AF_INET: + return inet_pton4(src, dst); + case AF_INET6: + return inet_pton6(src, dst); + default: + errno = EAFNOSUPPORT; + return -1; + } +} + +char *inet_ntoa(struct in_addr in) { + static __thread char buffer[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &in.s_addr, buffer, INET_ADDRSTRLEN); + return buffer; +} + +int inet_aton(const char *cp, struct in_addr *inp) { + int base; + size_t numBytes; + return _inetAtonDetail(&base, &numBytes, cp, inp); +} + +in_addr_t inet_addr(const char *cp) { + struct in_addr addr = { .s_addr = INADDR_BROADCAST }; + inet_aton(cp, &addr); + return addr.s_addr; +}