diff options
author | Gunnar Beutner <gunnar@beutner.name> | 2021-04-12 17:30:08 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-04-12 22:44:49 +0200 |
commit | 30b038f8d9d13ef9a3885883f3cfacf965f81c12 (patch) | |
tree | fe663ea0255fb60b0fc4d03ea325b966f56a8de1 | |
parent | 8fcf91b030ceb17105ce02372c4a57bf49951678 (diff) | |
download | serenity-30b038f8d9d13ef9a3885883f3cfacf965f81c12.zip |
LibC: Implement getaddrinfo(), freeaddrinfo(), gai_strerror() and getnameinfo()
-rw-r--r-- | Userland/Libraries/LibC/netdb.cpp | 155 | ||||
-rw-r--r-- | Userland/Libraries/LibC/netdb.h | 5 |
2 files changed, 151 insertions, 9 deletions
diff --git a/Userland/Libraries/LibC/netdb.cpp b/Userland/Libraries/LibC/netdb.cpp index 915d96332f..257bfc7431 100644 --- a/Userland/Libraries/LibC/netdb.cpp +++ b/Userland/Libraries/LibC/netdb.cpp @@ -657,20 +657,157 @@ static bool fill_getproto_buffers(const char* line, ssize_t read) int getaddrinfo(const char* __restrict node, const char* __restrict service, const struct addrinfo* __restrict hints, struct addrinfo** __restrict res) { - (void)node; - (void)service; - (void)hints; - (void)res; - VERIFY_NOT_REACHED(); + dbgln("getaddrinfo: node={}, service={}, hints->ai_family={}", (const char*)node, (const char*)service, hints ? hints->ai_family : 0); + + *res = nullptr; + + if (hints && hints->ai_family != AF_INET && hints->ai_family != AF_UNSPEC) + return EAI_FAMILY; + + auto host_ent = gethostbyname(node); + if (!host_ent) + return EAI_FAIL; + + const char* proto = nullptr; + if (hints && hints->ai_socktype) { + switch (hints->ai_socktype) { + case SOCK_STREAM: + proto = "tcp"; + break; + case SOCK_DGRAM: + proto = "udp"; + break; + default: + return EAI_SOCKTYPE; + } + } + + long port; + int socktype; + servent* svc_ent = nullptr; + if (!hints || (hints->ai_flags & AI_NUMERICSERV) == 0) { + svc_ent = getservbyname(service, proto); + } + if (!svc_ent) { + char* end; + port = htons(strtol(service, &end, 10)); + if (*end) + return EAI_FAIL; + + if (hints && hints->ai_socktype != 0) + socktype = hints->ai_socktype; + else + socktype = SOCK_STREAM; + } else { + port = svc_ent->s_port; + socktype = strcmp(svc_ent->s_proto, "tcp") ? SOCK_STREAM : SOCK_DGRAM; + } + + addrinfo* first_info = nullptr; + addrinfo* prev_info = nullptr; + + for (int host_index = 0; host_ent->h_addr_list[host_index]; host_index++) { + sockaddr_in* sin = new sockaddr_in; + sin->sin_family = AF_INET; + sin->sin_port = port; + memcpy(&sin->sin_addr.s_addr, host_ent->h_addr_list[host_index], host_ent->h_length); + + addrinfo* info = new addrinfo; + info->ai_flags = 0; + info->ai_family = AF_INET; + info->ai_socktype = socktype; + info->ai_protocol = PF_INET; + info->ai_addrlen = sizeof(*sin); + info->ai_addr = reinterpret_cast<sockaddr*>(sin); + + if (hints && hints->ai_flags & AI_CANONNAME) + info->ai_canonname = strdup(host_ent->h_name); + else + info->ai_canonname = nullptr; + + info->ai_next = nullptr; + + if (!first_info) + first_info = info; + + if (prev_info) + prev_info->ai_next = info; + + prev_info = info; + } + + if (first_info) { + *res = first_info; + return 0; + } else + return EAI_NONAME; } + void freeaddrinfo(struct addrinfo* res) { - (void)res; - VERIFY_NOT_REACHED(); + if (res) { + delete reinterpret_cast<sockaddr_in*>(res->ai_addr); + free(res->ai_canonname); + freeaddrinfo(res->ai_next); + delete res; + } } + const char* gai_strerror(int errcode) { - (void)errcode; - return "Not yet implemented"; + switch (errcode) { + case EAI_ADDRFAMILY: + return "no address for this address family available"; + case EAI_AGAIN: + return "name server returned temporary failure"; + case EAI_BADFLAGS: + return "invalid flags"; + case EAI_FAIL: + return "name server returned permanent failure"; + case EAI_FAMILY: + return "unsupported address family"; + case EAI_MEMORY: + return "out of memory"; + case EAI_NODATA: + return "no address available"; + case EAI_NONAME: + return "node or service is not known"; + case EAI_SERVICE: + return "service not available"; + case EAI_SOCKTYPE: + return "unsupported socket type"; + case EAI_SYSTEM: + return "system error"; + case EAI_OVERFLOW: + return "buffer too small"; + default: + return "invalid error code"; + } +} + +int getnameinfo(const struct sockaddr* __restrict addr, socklen_t addrlen, char* __restrict host, socklen_t hostlen, char* __restrict serv, socklen_t servlen, int flags) +{ + (void)flags; + + if (addr->sa_family != AF_INET || addrlen < sizeof(sockaddr_in)) + return EAI_FAMILY; + + const sockaddr_in* sin = reinterpret_cast<const sockaddr_in*>(addr); + + if (host && hostlen > 0) { + if (!inet_ntop(AF_INET, &sin->sin_addr, host, hostlen)) { + if (errno == ENOSPC) + return EAI_OVERFLOW; + else + return EAI_SYSTEM; + } + } + + if (serv && servlen > 0) { + if (snprintf(serv, servlen, "%d", (int)ntohs(sin->sin_port)) > (int)servlen) + return EAI_OVERFLOW; + } + + return 0; } } diff --git a/Userland/Libraries/LibC/netdb.h b/Userland/Libraries/LibC/netdb.h index d60f07217f..4560466fa4 100644 --- a/Userland/Libraries/LibC/netdb.h +++ b/Userland/Libraries/LibC/netdb.h @@ -97,6 +97,7 @@ struct addrinfo { #define EAI_SERVICE 9 #define EAI_SOCKTYPE 10 #define EAI_SYSTEM 11 +#define EAI_OVERFLOW 12 #define AI_PASSIVE 0x0001 #define AI_CANONNAME 0x0002 @@ -109,8 +110,12 @@ struct addrinfo { #define NI_MAXHOST 1025 #define NI_MAXSERV 32 +#define NI_NUMERICHOST 1 +#define NI_NUMERICSERV 2 + int getaddrinfo(const char* __restrict node, const char* __restrict service, const struct addrinfo* __restrict hints, struct addrinfo** __restrict res); void freeaddrinfo(struct addrinfo* res); const char* gai_strerror(int errcode); +int getnameinfo(const struct sockaddr* __restrict addr, socklen_t addrlen, char* __restrict host, socklen_t hostlen, char* __restrict serv, socklen_t servlen, int flags); __END_DECLS |