From dcf3e563000262e38d8ab6a454d8169b344ffafc Mon Sep 17 00:00:00 2001 From: TuxSH Date: Mon, 5 Feb 2018 18:48:23 +0100 Subject: [PATCH] Implement netdb.h functions, fix bugs, etc. --- nx/include/netdb.h | 2 +- nx/include/switch/runtime/devices/socket.h | 31 +- nx/include/switch/services/sfdnsres.h | 6 +- nx/source/runtime/devices/socket.c | 493 ++++++++++++++++++++- nx/source/services/sfdnsres.c | 107 ++--- 5 files changed, 560 insertions(+), 79 deletions(-) diff --git a/nx/include/netdb.h b/nx/include/netdb.h index 90d0ee2c..46dda840 100644 --- a/nx/include/netdb.h +++ b/nx/include/netdb.h @@ -228,7 +228,7 @@ int getaddrinfo(const char *node, const char *service, struct addrinfo **res); void freeaddrinfo(struct addrinfo *ai); -const char *gai_strerror(int ecode); +const char *gai_strerror(int err); void setservent(int); #if __BSD_VISIBLE diff --git a/nx/include/switch/runtime/devices/socket.h b/nx/include/switch/runtime/devices/socket.h index 234fb3d5..7a78012d 100644 --- a/nx/include/switch/runtime/devices/socket.h +++ b/nx/include/switch/runtime/devices/socket.h @@ -3,25 +3,36 @@ /// Configuration structure for socketInitalize typedef struct { - u32 bsdsockets_version; ///< Observed 1 on 2.0 LibAppletWeb, 2 on 3.0. + u32 bsdsockets_version; ///< Observed 1 on 2.0 LibAppletWeb, 2 on 3.0. - u32 tcp_tx_buf_size; ///< Size of the TCP transfer (send) buffer (initial or fixed). - u32 tcp_rx_buf_size; ///< Size of the TCP recieve buffer (initial or fixed). - u32 tcp_tx_buf_max_size; ///< Maximum size of the TCP transfer (send) buffer. If it is 0, the size of the buffer is fixed to its initial value. - u32 tcp_rx_buf_max_size; ///< Maximum size of the TCP receive buffer. If it is 0, the size of the buffer is fixed to its initial value. + u32 tcp_tx_buf_size; ///< Size of the TCP transfer (send) buffer (initial or fixed). + u32 tcp_rx_buf_size; ///< Size of the TCP recieve buffer (initial or fixed). + u32 tcp_tx_buf_max_size; ///< Maximum size of the TCP transfer (send) buffer. If it is 0, the size of the buffer is fixed to its initial value. + u32 tcp_rx_buf_max_size; ///< Maximum size of the TCP receive buffer. If it is 0, the size of the buffer is fixed to its initial value. - u32 udp_tx_buf_size; ///< Size of the UDP transfer (send) buffer (typically 0x2400 bytes). - u32 udp_rx_buf_size; ///< Size of the UDP receive buffer (typically 0xA500 bytes). + u32 udp_tx_buf_size; ///< Size of the UDP transfer (send) buffer (typically 0x2400 bytes). + u32 udp_rx_buf_size; ///< Size of the UDP receive buffer (typically 0xA500 bytes). - u32 sb_efficiency; ///< Number of buffers for each socket (standard values range from 1 to 8). + u32 sb_efficiency; ///< Number of buffers for each socket (standard values range from 1 to 8). + + 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. const SocketInitConfig *socketGetDefaultInitConfig(void); +/// Initalize the socket driver. Result socketInitialize(const SocketInitConfig *config); -Result socketGetLastResult(void); - +/// 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); +/// Deinitializes the socket driver. void socketExit(void); +/// Initalize the socket driver using the default configuration. static inline Result socketInitializeDefault(void) { return socketInitialize(socketGetDefaultInitConfig()); diff --git a/nx/include/switch/services/sfdnsres.h b/nx/include/switch/services/sfdnsres.h index 4a2f001f..d1eb7400 100644 --- a/nx/include/switch/services/sfdnsres.h +++ b/nx/include/switch/services/sfdnsres.h @@ -7,7 +7,7 @@ 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 DNS requests: bypass the Name Server Daemon. + bool bypass_nsd; ///< For name gethostbyname/getaddrinfo: bypass the Name Server Daemon. int timeout; ///< For DNS requests: timeout or 0. } SfdnsresConfig; @@ -23,8 +23,8 @@ typedef struct { Result sfdnsresGetHostByName(SfdnsresRequestResults *ret, const SfdnsresConfig *config, void *out_he_serialized, const char *name); Result sfdnsresGetHostByAddr(SfdnsresRequestResults *ret, const SfdnsresConfig *config, void *out_he_serialized, const void *addr, socklen_t len, int type); -Result sfdnsresGetHostStringError(char *str, size_t str_size); -Result sfdnsresGetGaiStringError(char *str, size_t str_size); +Result sfdnsresGetHostStringError(int err, char *str, size_t str_size); +Result sfdnsresGetGaiStringError(int err, char *str, size_t str_size); Result sfdnsresGetAddrInfo(SfdnsresRequestResults *ret, const SfdnsresConfig *config, const char *node, const char *service, const void *hints_serialized, size_t hints_serialized_size, void *res_serialized); diff --git a/nx/source/runtime/devices/socket.c b/nx/source/runtime/devices/socket.c index 2a43848c..f504a799 100644 --- a/nx/source/runtime/devices/socket.c +++ b/nx/source/runtime/devices/socket.c @@ -1,7 +1,7 @@ #include +#include #include #include -//#include #include #include //#include @@ -19,11 +19,18 @@ #include #include +#include #include "runtime/devices/socket.h" #include "services/bsd.h" +#include "services/sfdnsres.h" #include "result.h" +__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)}}; @@ -73,6 +80,11 @@ static const SocketInitConfig g_defaultSocketInitConfig = { .udp_rx_buf_size = 0xA500, .sb_efficiency = 4, + + .serialized_out_addrinfos_max_size = 0x1000, + .serialized_out_hostent_max_size = 0x200, + .bypass_nsd = false, + .dns_timeout = 0, }; static int _socketParseBsdResult(struct _reent *r, int ret) { @@ -527,12 +539,18 @@ Result socketInitialize(const SocketInitConfig *config) { .sb_efficiency = config->sb_efficiency, }; + 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 = bsdInitialize(&bcfg); - if(R_SUCCEEDED(ret)) dev = AddDevice(&g_socketDevoptab); @@ -540,6 +558,13 @@ Result socketInitialize(const SocketInitConfig *config) { socketExit(); return MAKERESULT(Module_Libnx, LibnxError_TooManyDevOpTabs); } + else { + g_bsdResult = 0; + g_bsdErrno = 0; + + g_sfdnsresConfig = sfdnsresConfig; + g_sfdnsresResult = 0; + } return ret; } @@ -549,10 +574,14 @@ void socketExit(void) { bsdExit(); } -Result socketGetLastResult(void) { +Result socketGetLastBsdResult(void) { return g_bsdResult; } +Result socketGetLastSfdnsresResult(void) { + return g_sfdnsresResult; +} + /* It is way too complicated and inefficient to use devoptab with bsdSelect. We're therefore implementing select with poll. @@ -1009,7 +1038,7 @@ int inet_pton(int af, const char *src, void *dst) { } char *inet_ntoa(struct in_addr in) { - static char buffer[INET_ADDRSTRLEN]; + static __thread char buffer[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &in.s_addr, buffer, INET_ADDRSTRLEN); return buffer; } @@ -1037,3 +1066,459 @@ int gethostname(char *name, size_t namelen) const char *hostname = inet_ntop(AF_INET, &hostid, name, namelen); return hostname == NULL ? -1 : 0; } + +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 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; + pos_aliases = pos; + for(pos = buf, len = 1; len != 0; pos += len + 1) { + len = strlen(buf); + if(len != 0) + nb_aliases++; + } + + total_aliases_size = pos - pos_aliases - 1; + + // Nintendo uses unsigned short here... + addrtype = ntohl(*(const u16 *)pos); + pos += 2; + addrlen = ntohl(*(const u16 *)pos); + pos += 2; + + // sfdnsres will only return IPv4 addresses for the "host" commands + if(addrtype != AF_INET || addrlen != sizeof(struct in_addr)) { + *err = NO_ADDRESS; + return NULL; + } + + // The official hostent (de)serializer doesn't support IPv6, at least not currently. + pos_addresses = pos; + for(nb_addresses = 0; ((const struct in_addr *)pos)->s_addr != 0; nb_addresses++); + + he = (struct hostent *)malloc( + sizeof(struct hostent) + + name_size + + 8 * (nb_aliases + 1 + nb_addresses + 1) + + total_aliases_size + + addrlen * nb_addresses + ); + + if(he == NULL) { + *err = NO_RECOVERY; + return NULL; + } + + he->h_name = (char*)he + sizeof(struct hostent); + 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; + + memcpy(he->h_name, buf, name_size); + + 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; + + 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_addrlen == 0 ? 4 : ai->ai_addrlen; // not posix-compliant ? + size_t subsize2 = 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 = htonl((u32)ai->ai_addrlen); + + // 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); + } + + 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 += 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); + + node->info.ai_addr = (struct sockaddr *)&node->addr; + node->info.ai_canonname = node->canonname; + // 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); + memcpy(node->info.ai_addr, (const u8 *)hdr + sizeof(struct addrinfo_serialized_hdr), node->info.ai_addrlen); + memcpy(node->info.ai_canonname, (const u8 *)hdr + sizeof(struct addrinfo_serialized_hdr) + subsize1, subsize2); + + // 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; + } + 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) { + 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; + SfdnsresRequestResults ret; + + if(out_he_serialized == NULL) { + h_errno = NO_RECOVERY; + errno = ENOMEM; // POSIX leaves this unspecified + goto cleanup; + } + rc = sfdnsresGetHostByName(&ret, &g_sfdnsresConfig, out_he_serialized, name); + 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.ret != NETDB_SUCCESS) { + h_errno = ret.ret; + errno = ret.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; + SfdnsresRequestResults ret; + + if(out_he_serialized == NULL) { + h_errno = NO_RECOVERY; + errno = ENOMEM; // POSIX leaves this unspecified + goto cleanup; + } + rc = sfdnsresGetHostByAddr(&ret, &g_sfdnsresConfig, out_he_serialized, addr, len, type); + 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.ret != NETDB_SUCCESS) { + h_errno = ret.ret; + errno = ret.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 = sfdnsresGetHostStringError(err, buf, 0x80); + 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 = sfdnsresGetGaiStringError(err, buf, 0x80); + 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) { + int gaie = 0; + 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; + SfdnsresRequestResults ret; + + 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 = sfdnsresGetAddrInfo(&ret, &g_sfdnsresConfig, node, service, hints_serialized, hints_sz, out_serialized); + 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; + } + + gaie = ret.ret; + if(gaie != 0) { + if(gaie == EAI_SYSTEM) + errno = ret.errno_; + goto cleanup; + } + + out = _socketDeserializeAddrInfoList(out_serialized); + if(out == NULL) + gaie = EAI_MEMORY; + *res = out; +cleanup: + 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) { + int gaie = 0; + Result rc = 0; + SfdnsresRequestResults ret; + + rc = sfdnsresGetNameInfo(&ret, &g_sfdnsresConfig, sa, salen, host, hostlen, serv, servlen, flags); + 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; + } + + + gaie = ret.ret; + if(gaie != 0) { + if(gaie == EAI_SYSTEM) + errno = ret.errno_; + } + +cleanup: + g_sfdnsresResult = rc; + return gaie; +} + +// Unimplementable functions, left for compliance: +struct hostent *gethostent(void) { return NULL; } +struct netent *getnetbyaddr(uint32_t a, int b) { (void)a; (void)b; return NULL; } +struct netent *getnetbyname(const char *s) { (void)s; return NULL; } +struct netent *getnetent(void) { return NULL; } +struct protoent *getprotobyname(const char *s) { (void)s; return NULL; } +struct protoent *getprotobynumber(int a) { (void)a; return NULL; } +struct protoent *getprotoent(void) { return NULL; } +struct servent *getservbyname(const char *s1, const char *s2) { (void)s1; (void)s2; return NULL; } +struct servent *getservbyport(int a, const char *s) { (void)a; (void)s; return NULL; } +struct servent *getservent(void) { 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/services/sfdnsres.c b/nx/source/services/sfdnsres.c index 8c6d2474..6e0a1396 100644 --- a/nx/source/services/sfdnsres.c +++ b/nx/source/services/sfdnsres.c @@ -24,25 +24,11 @@ cleanup: return rc; } -static Result _sfdnsresDnsRequestCommand(IpcCommand *c, u64 cmd_id, SfdnsresRequestResults *ret, const SfdnsresConfig *config, bool has_serialized_data_out, int *arg) { +static Result _sfdnsresDispatchDnsRequest(IpcCommand *c, SfdnsresRequestResults *ret, const void *raw, size_t raw_size, bool has_serialized_data_out) { Result rc; IpcParsedCommand r; - struct { - u64 magic; - u64 cmd_id; - int arg; - int timeout; - u64 pid_placeholder; - } raw; - ipcSendPid(c); - raw.magic = SFCI_MAGIC; - raw.cmd_id = cmd_id; - raw.arg = arg == NULL ? (config->bypass_nsd ? 0 : 1) : *arg; - raw.timeout = config->timeout; - raw.pid_placeholder = 0; - rc = _sfdnsresDispatchCommand(&r, c, &raw, sizeof(raw)); if(R_FAILED(rc)) return rc; @@ -64,7 +50,25 @@ static Result _sfdnsresDnsRequestCommand(IpcCommand *c, u64 cmd_id, SfdnsresRequ return rc; } -static Result _sfdnsresErrorStringGetterCommand(u64 cmd_id, char *str, size_t str_size) { +static Result _sfdnsresDnsRequestCommand(IpcCommand *c, u64 cmd_id, SfdnsresRequestResults *ret, const SfdnsresConfig *config, bool has_serialized_data_out, int *arg) { + struct { + u64 magic; + u64 cmd_id; + int arg; + int timeout; + u64 pid_placeholder; + } raw; + + raw.magic = SFCI_MAGIC; + raw.cmd_id = cmd_id; + raw.arg = arg == NULL ? (config->bypass_nsd ? 0 : 1) : *arg; + raw.timeout = config->timeout; + raw.pid_placeholder = 0; + + return _sfdnsresDispatchDnsRequest(c, ret, &raw, sizeof(raw), has_serialized_data_out); +} + +static Result _sfdnsresErrorStringGetterCommand(u64 cmd_id, int err, char *str, size_t str_size) { IpcCommand c; Result rc; IpcParsedCommand r; @@ -75,10 +79,12 @@ static Result _sfdnsresErrorStringGetterCommand(u64 cmd_id, char *str, size_t st struct { u64 magic; u64 cmd_id; + int err; } raw; raw.magic = SFCI_MAGIC; raw.cmd_id = cmd_id; + raw.err = err; rc = _sfdnsresDispatchCommand(&r, &c, &raw, sizeof(raw)); if(R_FAILED(rc)) return rc; @@ -104,18 +110,35 @@ Result sfdnsresGetHostByName(SfdnsresRequestResults *ret, const SfdnsresConfig * Result sfdnsresGetHostByAddr(SfdnsresRequestResults *ret, const SfdnsresConfig *config, void *out_he_serialized, const void *addr, socklen_t len, int type) { IpcCommand c; + struct { + u64 magic; + u64 cmd_id; + socklen_t len; // wtf nintendo + int type; + int timeout; + u64 pid_placeholder; + } raw; + ipcInitialize(&c); + ipcAddSendBuffer(&c, addr, len, 0); ipcAddRecvBuffer(&c, out_he_serialized, config->serialized_out_hostent_max_size, 0); - return _sfdnsresDnsRequestCommand(&c, 3, ret, config, true, &type); + raw.magic = SFCI_MAGIC; + raw.cmd_id = 3; + raw.len = len; + raw.type = type; + raw.timeout = config->timeout; + raw.pid_placeholder = 0; + + return _sfdnsresDispatchDnsRequest(&c, ret, &raw, sizeof(raw), true); } -Result sfdnsresGetHostStringError(char *str, size_t str_size) { - return _sfdnsresErrorStringGetterCommand(4, str, str_size); +Result sfdnsresGetHostStringError(int err, char *str, size_t str_size) { + return _sfdnsresErrorStringGetterCommand(4, err, str, str_size); } -Result sfdnsresGetGaiStringError(char *str, size_t str_size) { - return _sfdnsresErrorStringGetterCommand(5, str, str_size); +Result sfdnsresGetGaiStringError(int err, char *str, size_t str_size) { + return _sfdnsresErrorStringGetterCommand(5, err, str, str_size); } Result sfdnsresGetAddrInfo(SfdnsresRequestResults *ret, const SfdnsresConfig *config, const char *node, const char *service, @@ -181,9 +204,7 @@ Result sfdnsresRequestCancelHandle(u32 *out_handle) { /// Bug: always sets errno ? Result sfdnsresCancelSocketCall(SfdnsresRequestResults *ret, u32 handle) { - Result rc; IpcCommand c; - IpcParsedCommand r; struct { u64 magic; u64 cmd_id; @@ -192,37 +213,18 @@ Result sfdnsresCancelSocketCall(SfdnsresRequestResults *ret, u32 handle) { } raw; ipcInitialize(&c); - ipcSendPid(&c); raw.magic = SFCI_MAGIC; raw.cmd_id = 9; raw.handle = handle; raw.pid_placeholder = 0; - rc = _sfdnsresDispatchCommand(&r, &c, &raw, sizeof(raw)); - if(R_FAILED(rc)) return rc; - - struct { - u64 magic; - u64 result; - int ret; - int errno_; - } *resp = r.Raw; - - rc = resp->result; - if(R_FAILED(rc)) return rc; - - ret->ret = resp->ret; - ret->errno_ = resp->errno_; - - return rc; + return _sfdnsresDispatchDnsRequest(&c, ret, &raw, sizeof(raw), false); } /// Bug: always sets errno ? Result sfdnsresCancelAllSocketCalls(SfdnsresRequestResults *ret) { - Result rc; IpcCommand c; - IpcParsedCommand r; struct { u64 magic; u64 cmd_id; @@ -230,29 +232,12 @@ Result sfdnsresCancelAllSocketCalls(SfdnsresRequestResults *ret) { } raw; ipcInitialize(&c); - ipcSendPid(&c); raw.magic = SFCI_MAGIC; raw.cmd_id = 10; raw.pid_placeholder = 0; - rc = _sfdnsresDispatchCommand(&r, &c, &raw, sizeof(raw)); - if(R_FAILED(rc)) return rc; - - struct { - u64 magic; - u64 result; - int ret; - int errno_; - } *resp = r.Raw; - - rc = resp->result; - if(R_FAILED(rc)) return rc; - - ret->ret = resp->ret; - ret->errno_ = resp->errno_; - - return rc; + return _sfdnsresDispatchDnsRequest(&c, ret, &raw, sizeof(raw), false); } Result sfdnsresClearDnsIpServerAddressArray(void) {