/* $OpenBSD: getnameinfo.c,v 1.8 2015/10/02 20:56:14 deraadt Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #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, 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);