From 4945baa2cc46a3b5d759ce1f64480fe621093a20 Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 25 May 2015 19:16:08 +0000 Subject: [PATCH] getnameinfo(3) doesn't need to initialize the resolver when it's only used for address/port formatting (e.g. NI_NUMERICHOST). ok deraadt@ jca@ --- lib/libc/asr/getnameinfo.c | 151 ++++++++++++++++++++++++++++++++++++- 1 file changed, 150 insertions(+), 1 deletion(-) diff --git a/lib/libc/asr/getnameinfo.c b/lib/libc/asr/getnameinfo.c index 60b5b17ceb6..49819b027d4 100644 --- a/lib/libc/asr/getnameinfo.c +++ b/lib/libc/asr/getnameinfo.c @@ -1,4 +1,4 @@ -/* $OpenBSD: getnameinfo.c,v 1.5 2014/03/26 18:13:15 eric Exp $ */ +/* $OpenBSD: getnameinfo.c,v 1.6 2015/05/25 19:16:08 eric Exp $ */ /* * Copyright (c) 2012 Eric Faurot * @@ -17,12 +17,132 @@ #include #include +#include +#include #include #include #include #include #include +#include + +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 == -1) /* Actually, this can not happen */ + return (0); + + return (r); +} int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, @@ -31,6 +151,35 @@ getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, 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(); -- 2.20.1