diff --git a/libctru/include/netdb.h b/libctru/include/netdb.h index 5840deb..e3259b0 100644 --- a/libctru/include/netdb.h +++ b/libctru/include/netdb.h @@ -17,6 +17,16 @@ struct hostent { char *h_addr; }; + +#define AI_PASSIVE 0x01 +#define AI_CANONNAME 0x02 +#define AI_NUMERICHOST 0x04 +#define AI_NUMERICSERV 0x00 /* probably 0x08 but services names are never resolved */ + +// doesn't apply to 3ds +#define AI_ADDRCONFIG 0x00 + + #define NI_MAXHOST 1025 #define NI_MAXSERV 32 @@ -31,6 +41,16 @@ struct hostent { #define EAI_NONAME (-305) #define EAI_SOCKTYPE (-307) +struct addrinfo { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + socklen_t ai_addrlen; + char *ai_canonname; + struct sockaddr *ai_addr; + struct addrinfo *ai_next; +}; #ifdef __cplusplus extern "C" { @@ -46,6 +66,12 @@ extern "C" { char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags); + int getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res); + + void freeaddrinfo(struct addrinfo *ai); + #ifdef __cplusplus } #endif diff --git a/libctru/source/services/soc/soc_getaddrinfo.c b/libctru/source/services/soc/soc_getaddrinfo.c new file mode 100644 index 0000000..11c8634 --- /dev/null +++ b/libctru/source/services/soc/soc_getaddrinfo.c @@ -0,0 +1,162 @@ +#include "soc_common.h" +#include +#include <3ds/ipc.h> +#include <3ds/result.h> +#include + +#define DEFAULT_NUM_ADDRINFO 4 + +typedef struct addrinfo_3ds_t addrinfo_3ds_t; +struct addrinfo_3ds_t +{ + s32 ai_flags; + s32 ai_family; + s32 ai_socktype; + s32 ai_protocol; + u32 ai_addrlen; + char ai_canonname[256]; + struct sockaddr_storage ai_addr; +}; + +void freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next_ai = ai; + while(ai != NULL) + { + next_ai = ai->ai_next; + free(ai); + ai = next_ai; + } +} + +static struct addrinfo * buffer2addrinfo(addrinfo_3ds_t * entry) +{ + int ai_canonname_len = strnlen(entry->ai_canonname, sizeof(entry->ai_canonname)); + struct addrinfo *ai; + size_t len = sizeof(*ai) + + sizeof(struct sockaddr_storage) + + ai_canonname_len + + 1; + + ai = (struct addrinfo*)calloc(1,len); + if(ai != NULL) + { + ai->ai_canonname = (char*)ai + sizeof(*ai) + sizeof(struct sockaddr_storage); + ai->ai_addr = (struct sockaddr*)((char*)ai + sizeof(*ai)); + + ai->ai_flags = entry->ai_flags; + ai->ai_family = entry->ai_family; + ai->ai_socktype = entry->ai_socktype; + ai->ai_protocol = entry->ai_protocol; + ai->ai_addrlen = entry->ai_addrlen; + + memcpy(ai->ai_canonname, entry->ai_canonname, ai_canonname_len); + memcpy(ai->ai_addr, &entry->ai_addr, ai->ai_addrlen); + } + return ai; +} + + +static int getaddrinfo_detail(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res, addrinfo_3ds_t *info, s32 info_count, s32 * count) +{ + int i; + u32 *cmdbuf = getThreadCommandBuffer(); + u32 saved_threadstorage[2]; + + if(node == NULL && service == NULL) + { + return EAI_NONAME; + } + + cmdbuf[ 0] = IPC_MakeHeader(0xF,4,6); // 0x00F0106 + cmdbuf[ 1] = node == NULL ? 0 : strlen(node)+1; + cmdbuf[ 2] = service == NULL ? 0 : strlen(service)+1; + cmdbuf[ 3] = hints == NULL ? 0 : sizeof(*hints); + cmdbuf[ 4] = sizeof(addrinfo_3ds_t) * info_count; + cmdbuf[ 5] = IPC_Desc_StaticBuffer(cmdbuf[1], 5); + cmdbuf[ 6] = (u32)node; + cmdbuf[ 7] = IPC_Desc_StaticBuffer(cmdbuf[2], 6); + cmdbuf[ 8] = (u32)service; + cmdbuf[ 9] = IPC_Desc_StaticBuffer(cmdbuf[3], 7); + cmdbuf[10] = (u32)hints; + + u32 * staticbufs = getThreadStaticBuffers(); + + // Save the thread storage values + for(i = 0 ; i < 2 ; ++i) + saved_threadstorage[i] = staticbufs[i]; + + staticbufs[0] = IPC_Desc_StaticBuffer(sizeof(addrinfo_3ds_t) * info_count, 0); + staticbufs[1] = (u32)info; + + int ret = svcSendSyncRequest(SOCU_handle); + + // Restore the thread storage values + for(i = 0 ; i < 2 ; ++i) + staticbufs[i] = saved_threadstorage[i]; + + if(R_FAILED(ret)) { + errno = SYNC_ERROR; + return ret; + } + + ret = cmdbuf[1]; + if(R_FAILED(ret)) { + errno = SYNC_ERROR; + return ret; + } + if(cmdbuf[2] != 0) + { + return cmdbuf[2]; + } + + *count = cmdbuf[3]; + if(*count <= 0) + *res = NULL; + else if(*count <= info_count) + { + struct addrinfo **ptr = res; + for(i = 0; i < *count; ++i) + { + *ptr = buffer2addrinfo(&info[i]); + if(*ptr == NULL) + { + freeaddrinfo(*res); + *res = NULL; + return EAI_MEMORY; + } + ptr = &(*ptr)->ai_next; + } + *ptr = NULL; + } + + return 0; +} + +int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) +{ + Result ret; + addrinfo_3ds_t *info = NULL, *tmp; + s32 count = DEFAULT_NUM_ADDRINFO, info_count; + + if(node == NULL && service == NULL) + { + return EAI_NONAME; + } + + do + { + info_count = count; + tmp = (addrinfo_3ds_t*)realloc(info, sizeof(addrinfo_3ds_t) * info_count); + if(tmp == NULL) + { + free(info); + return EAI_MEMORY; + } + info = tmp; + ret = getaddrinfo_detail(node,service,hints,res,info,info_count,&count); + } while(count > info_count && R_SUCCEEDED(ret)); + + free(info); + return ret; +}