getnameinfo(3) doesn't need to initialize the resolver when it's only used
authoreric <eric@openbsd.org>
Mon, 25 May 2015 19:16:08 +0000 (19:16 +0000)
committereric <eric@openbsd.org>
Mon, 25 May 2015 19:16:08 +0000 (19:16 +0000)
for address/port formatting (e.g. NI_NUMERICHOST).

ok deraadt@ jca@

lib/libc/asr/getnameinfo.c

index 60b5b17..49819b0 100644 (file)
@@ -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 <eric@openbsd.org>
  *
 
 #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 == -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();