/* * Replacement for a missing getnameinfo. * * This is an implementation of getnameinfo for systems that don't have one so * that networking code can use a consistant interface without #ifdef. It is * a fairly minimal implementation, with the following limitations: * * - IPv4 support only. IPv6 is not supported. * - NI_NOFQDN is ignored. * - Not thread-safe due to gethostbyaddr, getservbyport, and inet_ntoa. * * The last two issues could probably be easily remedied, but haven't been * needed so far. Adding IPv6 support isn't worth it; systems with IPv6 * support should already support getnameinfo natively. * * The canonical version of this file is maintained in the rra-c-util package, * which can be found at . * * Written by Russ Allbery * Copyright 2005 Russ Allbery * Copyright 2008, 2011, 2013-2014 * The Board of Trustees of the Leland Stanford Junior University * * Copying and distribution of this file, with or without modification, are * permitted in any medium without royalty provided the copyright notice and * this notice are preserved. This file is offered as-is, without any * warranty. * * SPDX-License-Identifier: FSFAP */ #include #include #include #include #include /* * If we're running the test suite, rename inet_ntoa to avoid conflicts with * the system version. Note that we don't rename the structures and * constants, but that should be okay (except possibly for gai_strerror). */ #if TESTING # undef getnameinfo # define getnameinfo test_getnameinfo int test_getnameinfo(const struct sockaddr *, socklen_t, char *, socklen_t, char *, socklen_t, int); /* Linux doesn't provide EAI_OVERFLOW, so make up our own for testing. */ # ifndef EAI_OVERFLOW # define EAI_OVERFLOW 10 # endif #endif /* Used for unused parameters to silence gcc warnings. */ #define UNUSED __attribute__((__unused__)) /* * Check to see if a name is fully qualified by seeing if it contains a * period. If it does, try to copy it into the provided node buffer and set * status accordingly, returning true. If not, return false. */ static bool try_name(const char *name, char *node, socklen_t nodelen, int *status) { size_t namelen; if (strchr(name, '.') == NULL) return false; namelen = strlen(name); if (namelen + 1 > (size_t) nodelen) *status = EAI_OVERFLOW; else { memcpy(node, name, namelen + 1); *status = 0; } return true; } /* * Look up an address (or convert it to ASCII form) and put it in the provided * buffer, depending on what is requested by flags. */ static int lookup_name(const struct in_addr *addr, char *node, socklen_t nodelen, int flags) { struct hostent *host; char **alias; int status; char *name; size_t namelen; /* Do the name lookup first unless told not to. */ if (!(flags & NI_NUMERICHOST)) { host = gethostbyaddr((const void *) addr, sizeof(struct in_addr), AF_INET); if (host == NULL) { if (flags & NI_NAMEREQD) return EAI_NONAME; } else { if (try_name(host->h_name, node, nodelen, &status)) return status; for (alias = host->h_aliases; *alias != NULL; alias++) if (try_name(*alias, node, nodelen, &status)) return status; } /* * We found some results, but none of them were fully-qualified, so * act as if we found nothing and either fail or fall through. */ if (flags & NI_NAMEREQD) return EAI_NONAME; } /* Just convert the address to ASCII. */ name = inet_ntoa(*addr); namelen = strlen(name); if (namelen + 1 > (size_t) nodelen) return EAI_OVERFLOW; memcpy(node, name, namelen + 1); return 0; } /* * Look up a service (or convert it to ASCII form) and put it in the provided * buffer, depending on what is requested by flags. */ static int lookup_service(unsigned short port, char *service, socklen_t servicelen, int flags) { struct servent *srv; const char *protocol; int status; size_t namelen; /* Do the name lookup first unless told not to. */ if (!(flags & NI_NUMERICSERV)) { protocol = (flags & NI_DGRAM) ? "udp" : "tcp"; srv = getservbyport(htons(port), protocol); if (srv != NULL) { namelen = strlen(srv->s_name); if (namelen + 1 > (size_t) servicelen) return EAI_OVERFLOW; memcpy(service, srv->s_name, namelen + 1); return 0; } } /* Just convert the port number to ASCII. */ status = snprintf(service, servicelen, "%hu", port); if (status < 0 || (socklen_t) status >= servicelen) return EAI_OVERFLOW; return 0; } /* * The getnameinfo implementation. */ int getnameinfo(const struct sockaddr *sa, socklen_t salen UNUSED, char *node, socklen_t nodelen, char *service, socklen_t servicelen, int flags) { const struct sockaddr_in *sin; int status; unsigned short port; if ((node == NULL || nodelen <= 0) && (service == NULL || servicelen <= 0)) return EAI_NONAME; /* We only support AF_INET. */ if (sa->sa_family != AF_INET) return EAI_FAMILY; sin = (const struct sockaddr_in *) (const void *) sa; /* Name lookup. */ if (node != NULL && nodelen > 0) { status = lookup_name(&sin->sin_addr, node, nodelen, flags); if (status != 0) return status; } /* Service lookup. */ if (service != NULL && servicelen > 0) { port = ntohs(sin->sin_port); return lookup_service(port, service, servicelen, flags); } else return 0; }