diff options
Diffstat (limited to 'openbsd-compat/libasr/getnameinfo.c')
-rw-r--r-- | openbsd-compat/libasr/getnameinfo.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/openbsd-compat/libasr/getnameinfo.c b/openbsd-compat/libasr/getnameinfo.c new file mode 100644 index 00000000..7bee468d --- /dev/null +++ b/openbsd-compat/libasr/getnameinfo.c @@ -0,0 +1,205 @@ +/* $OpenBSD: getnameinfo.c,v 1.9 2019/07/03 03:24:03 deraadt Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * 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 THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <netdb.h> + +#include <asr.h> +#include <errno.h> +#include <resolv.h> +#include <string.h> + +static size_t asr_print_addr(const struct sockaddr *, char *, size_t); +static size_t asr_print_port(const struct sockaddr *, const char *, char *, size_t); + +#define SA_IN(sa) ((struct sockaddr_in*)(sa)) +#define SA_IN6(sa) ((struct sockaddr_in6*)(sa)) + +/* + * Print the textual representation (as given by inet_ntop(3)) of the address + * set in "sa". + * + * Return the total length of the string it tried to create or 0 if an error + * occured, in which case errno is set. On success, the constructed string + * is guaranteed to be NUL-terminated. Overflow must be detected by checking + * the returned size against buflen. + * + */ +static size_t +asr_print_addr(const struct sockaddr *sa, char *buf, size_t buflen) +{ + unsigned int ifidx; + char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; + char scope[IF_NAMESIZE + 1], *ifname; + const void *addr; + size_t s; + + switch(sa->sa_family) { + case AF_INET: + addr = &SA_IN(sa)->sin_addr; + break; + case AF_INET6: + addr = &SA_IN6(sa)->sin6_addr; + break; + default: + errno = EINVAL; + return (0); + } + + if (inet_ntop(sa->sa_family, addr, tmp, sizeof(tmp)) == NULL) + return (0); /* errno set */ + + s = strlcpy(buf, tmp, buflen); + + if (sa->sa_family == AF_INET6 && SA_IN6(sa)->sin6_scope_id) { + + scope[0] = SCOPE_DELIMITER; + scope[1] = '\0'; + + ifidx = SA_IN6(sa)->sin6_scope_id; + ifname = NULL; + + if (IN6_IS_ADDR_LINKLOCAL(&(SA_IN6(sa)->sin6_addr)) || + IN6_IS_ADDR_MC_LINKLOCAL(&(SA_IN6(sa)->sin6_addr)) || + IN6_IS_ADDR_MC_INTFACELOCAL(&(SA_IN6(sa)->sin6_addr))) + ifname = if_indextoname(ifidx, scope + 1); + + if (ifname == NULL) + (void)snprintf(scope + 1, sizeof(scope) - 1, "%u", ifidx); + + if (s < buflen) + (void)strlcat(buf, scope, buflen); + + s += strlen(scope); + } + + return (s); +} + +/* + * Print the textual representation of the port set on "sa". + * + * If proto is not NULL, it is used as parameter to "getservbyport_r(3)" to + * return a service name. If it's not set, or if no matching service is found, + * it prints the portno. + * + * Return the total length of the string it tried to create or 0 if an error + * occured, in which case errno is set. On success, the constructed string + * is guaranteed to be NUL-terminated. Overflow must be detected by checking + * the returned size against buflen. + */ +static size_t +asr_print_port(const struct sockaddr *sa, const char *proto, char *buf, size_t buflen) +{ + struct servent s; + struct servent_data sd; + int port, r, saved_errno; + size_t n; + + switch(sa->sa_family) { + case AF_INET: + port = SA_IN(sa)->sin_port; + break; + case AF_INET6: + port = SA_IN6(sa)->sin6_port; + break; + default: + errno = EINVAL; + return (0); + } + + if (proto) { + memset(&sd, 0, sizeof (sd)); + saved_errno = errno; + if (getservbyport_r(port, proto, &s, &sd) != -1) { + n = strlcpy(buf, s.s_name, buflen); + endservent_r(&sd); + return (n); + } + errno = saved_errno; + } + + r = snprintf(buf, buflen, "%u", ntohs(port)); + if (r < 0 || r >= buflen) /* Actually, this can not happen */ + return (0); + + return (r); +} + +int +getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, + size_t hostlen, char *serv, size_t servlen, int flags) +{ + struct asr_query *as; + struct asr_result ar; + int saved_errno = errno; + const char *proto; + size_t r; + + /* + * Take a shortcut if we don't care about hostname, + * or if NI_NUMERICHOST is set. + */ + if (host == NULL || hostlen == 0 || + (host && hostlen && (flags & NI_NUMERICHOST))) { + if (host) { + r = asr_print_addr(sa, host, hostlen); + if (r == 0) + return (EAI_SYSTEM); /* errno set */ + if (r >= hostlen) + return (EAI_OVERFLOW); + } + + if (serv && servlen) { + if (flags & NI_NUMERICSERV) + proto = NULL; + else + proto = (flags & NI_DGRAM) ? "udp" : "tcp"; + r = asr_print_port(sa, proto, serv, servlen); + if (r == 0) + return (EAI_SYSTEM); /* errno set */ + if (r >= servlen) + return (EAI_OVERFLOW); + } + + errno = saved_errno; + return (0); + } + + res_init(); + + as = getnameinfo_async(sa, salen, host, hostlen, serv, servlen, flags, + NULL); + if (as == NULL) { + if (errno == ENOMEM) { + errno = saved_errno; + return (EAI_MEMORY); + } + return (EAI_SYSTEM); + } + + asr_run_sync(as, &ar); + if (ar.ar_gai_errno == EAI_SYSTEM) + errno = ar.ar_errno; + + return (ar.ar_gai_errno); +} +DEF_WEAK(getnameinfo); |