summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGunnar Beutner <gunnar@beutner.name>2021-04-12 17:30:08 +0200
committerAndreas Kling <kling@serenityos.org>2021-04-12 22:44:49 +0200
commit30b038f8d9d13ef9a3885883f3cfacf965f81c12 (patch)
treefe663ea0255fb60b0fc4d03ea325b966f56a8de1
parent8fcf91b030ceb17105ce02372c4a57bf49951678 (diff)
downloadserenity-30b038f8d9d13ef9a3885883f3cfacf965f81c12.zip
LibC: Implement getaddrinfo(), freeaddrinfo(), gai_strerror() and getnameinfo()
-rw-r--r--Userland/Libraries/LibC/netdb.cpp155
-rw-r--r--Userland/Libraries/LibC/netdb.h5
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