X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=support%2Fexport%2Fhostname.c;h=79b9a8cba6081af5d91c93de78ac57757f275fe1;hp=8a23a89b7874466b8146946e5ec65a15b96d57b8;hb=94ce1eb94babb4c587b2826452fb053cba745098;hpb=8d61f2518bebe11c5fd0624857f83de1dceca991 diff --git a/support/export/hostname.c b/support/export/hostname.c index 8a23a89..79b9a8c 100644 --- a/support/export/hostname.c +++ b/support/export/hostname.c @@ -18,6 +18,8 @@ #include #include #include +#include + #include #ifdef TEST #define xmalloc malloc @@ -26,6 +28,13 @@ #include "misc.h" #endif +#include "sockaddr.h" +#include "exportfs.h" + +#ifndef HAVE_DECL_AI_ADDRCONFIG +#define AI_ADDRCONFIG 0 +#endif + #define ALIGNMENT sizeof (char *) static int @@ -158,6 +167,348 @@ hostent_dup (struct hostent *hp) return cp; } +#ifdef HAVE_GETNAMEINFO +static socklen_t +sockaddr_size(const struct sockaddr *sap) +{ + if (sap->sa_family != AF_INET) + return 0; + return (socklen_t)sizeof(struct sockaddr_in); +} +#endif /* HAVE_GETNAMEINFO */ + +/** + * host_ntop - generate presentation address given a sockaddr + * @sap: pointer to socket address + * @buf: working storage + * @buflen: size of @buf in bytes + * + * Returns a pointer to a @buf. + */ +#ifdef HAVE_GETNAMEINFO +char * +host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen) +{ + socklen_t salen = sockaddr_size(sap); + int error; + + memset(buf, 0, buflen); + + if (salen == 0) { + (void)strncpy(buf, "bad family", buflen - 1); + return buf; + } + + error = getnameinfo(sap, salen, buf, (socklen_t)buflen, + NULL, 0, NI_NUMERICHOST); + if (error != 0) { + buf[0] = '\0'; + (void)strncpy(buf, "bad address", buflen - 1); + } + + return buf; +} +#else /* !HAVE_GETNAMEINFO */ +char * +host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen) +{ + const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap; + + memset(buf, 0, buflen); + + if (sin->sin_family != AF_INET) + (void)strncpy(buf, "bad family", buflen - 1); + return buf; + } + + if (inet_ntop(AF_INET, &sin->sin_addr.s_addr, buf, buflen) != NULL) + return buf; + + buf[0] = '\0'; + (void)strncpy(buf, "bad address", buflen - 1); + return buf; +} +#endif /* !HAVE_GETNAMEINFO */ + +/** + * host_pton - return addrinfo for a given presentation address + * @paddr: pointer to a '\0'-terminated ASCII string containing an + * IP presentation address + * + * Returns address info structure, or NULL if an error occurs. Caller + * must free the returned structure with freeaddrinfo(3). + */ +__attribute_malloc__ +struct addrinfo * +host_pton(const char *paddr) +{ + struct addrinfo *ai = NULL; + struct addrinfo hint = { + /* don't return duplicates */ + .ai_protocol = (int)IPPROTO_UDP, + .ai_flags = AI_NUMERICHOST, + .ai_family = AF_UNSPEC, + }; + struct sockaddr_in sin; + int error; + + /* + * Although getaddrinfo(3) is easier to use and supports + * IPv6, it recognizes incomplete addresses like "10.4" + * as valid AF_INET addresses. It also accepts presentation + * addresses that end with a blank. + * + * inet_pton(3) is much stricter. Use it to be certain we + * have a real AF_INET presentation address, before invoking + * getaddrinfo(3) to generate the full addrinfo list. + */ + if (inet_pton(AF_INET, paddr, &sin.sin_addr) == 0) + return NULL; + + error = getaddrinfo(paddr, NULL, &hint, &ai); + switch (error) { + case 0: + return ai; + case EAI_NONAME: + if (paddr == NULL) + xlog(D_GENERAL, "%s: passed a NULL presentation address", + __func__); + break; + case EAI_SYSTEM: + xlog(D_GENERAL, "%s: failed to convert %s: (%d) %m", + __func__, paddr, errno); + break; + default: + xlog(D_GENERAL, "%s: failed to convert %s: %s", + __func__, paddr, gai_strerror(error)); + break; + } + + return NULL; +} + +/** + * host_addrinfo - return addrinfo for a given hostname + * @hostname: pointer to a '\0'-terminated ASCII string containing a hostname + * + * Returns address info structure with ai_canonname filled in, or NULL + * if no information is available for @hostname. Caller must free the + * returned structure with freeaddrinfo(3). + */ +__attribute_malloc__ +struct addrinfo * +host_addrinfo(const char *hostname) +{ + struct addrinfo *ai = NULL; + struct addrinfo hint = { + .ai_family = AF_INET, + /* don't return duplicates */ + .ai_protocol = (int)IPPROTO_UDP, + .ai_flags = AI_ADDRCONFIG | AI_CANONNAME, + }; + int error; + + error = getaddrinfo(hostname, NULL, &hint, &ai); + switch (error) { + case 0: + return ai; + case EAI_SYSTEM: + xlog(D_GENERAL, "%s: failed to resolve %s: (%d) %m", + __func__, hostname, errno); + break; + default: + xlog(D_GENERAL, "%s: failed to resolve %s: %s", + __func__, hostname, gai_strerror(error)); + break; + } + + return NULL; +} + +/** + * host_canonname - return canonical hostname bound to an address + * @sap: pointer to socket address to look up + * + * Discover the canonical hostname associated with the given socket + * address. The host's reverse mapping is verified in the process. + * + * Returns a '\0'-terminated ASCII string containing a hostname, or + * NULL if no hostname can be found for @sap. Caller must free + * the string. + */ +#ifdef HAVE_GETNAMEINFO +__attribute_malloc__ +char * +host_canonname(const struct sockaddr *sap) +{ + socklen_t salen = sockaddr_size(sap); + char buf[NI_MAXHOST]; + int error; + + if (salen == 0) { + xlog(D_GENERAL, "%s: unsupported address family %d", + __func__, sap->sa_family); + return NULL; + } + + memset(buf, 0, sizeof(buf)); + error = getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf), + NULL, 0, NI_NAMEREQD); + switch (error) { + case 0: + break; + case EAI_SYSTEM: + xlog(D_GENERAL, "%s: getnameinfo(3) failed: (%d) %m", + __func__, errno); + return NULL; + default: + (void)getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf), + NULL, 0, NI_NUMERICHOST); + xlog(D_GENERAL, "%s: failed to resolve %s: %s", + __func__, buf, gai_strerror(error)); + return NULL; + } + + return strdup(buf); +} +#else /* !HAVE_GETNAMEINFO */ +__attribute_malloc__ +char * +host_canonname(const struct sockaddr *sap) +{ + const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap; + const struct in_addr *addr = &sin->sin_addr; + struct hostent *hp; + + if (sap->sa_family != AF_INET) + return NULL; + + hp = gethostbyaddr(addr, (socklen_t)sizeof(addr), AF_INET); + if (hp == NULL) + return NULL; + + return strdup(hp->h_name); +} +#endif /* !HAVE_GETNAMEINFO */ + +/** + * host_reliable_addrinfo - return addrinfo for a given address + * @sap: pointer to socket address to look up + * + * Reverse and forward lookups are performed to ensure the address has + * proper forward and reverse mappings. + * + * Returns address info structure with ai_canonname filled in, or NULL + * if no information is available for @sap. Caller must free the returned + * structure with freeaddrinfo(3). + */ +__attribute_malloc__ +struct addrinfo * +host_reliable_addrinfo(const struct sockaddr *sap) +{ + struct addrinfo *ai; + char *hostname; + + hostname = host_canonname(sap); + if (hostname == NULL) + return NULL; + + ai = host_addrinfo(hostname); + + free(hostname); + return ai; +} + +/** + * host_numeric_addrinfo - return addrinfo without doing DNS queries + * @sap: pointer to socket address + * + * Returns address info structure, or NULL if an error occurred. + * Caller must free the returned structure with freeaddrinfo(3). + */ +#ifdef HAVE_GETNAMEINFO +__attribute_malloc__ +struct addrinfo * +host_numeric_addrinfo(const struct sockaddr *sap) +{ + socklen_t salen = sockaddr_size(sap); + char buf[INET_ADDRSTRLEN]; + struct addrinfo *ai; + int error; + + if (salen == 0) { + xlog(D_GENERAL, "%s: unsupported address family %d", + __func__, sap->sa_family); + return NULL; + } + + memset(buf, 0, sizeof(buf)); + error = getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf), + NULL, 0, NI_NUMERICHOST); + switch (error) { + case 0: + break; + case EAI_SYSTEM: + xlog(D_GENERAL, "%s: getnameinfo(3) failed: (%d) %m", + __func__, errno); + return NULL; + default: + xlog(D_GENERAL, "%s: getnameinfo(3) failed: %s", + __func__, gai_strerror(error)); + return NULL; + } + + ai = host_pton(buf); + + /* + * getaddrinfo(AI_NUMERICHOST) never fills in ai_canonname + */ + if (ai != NULL) { + free(ai->ai_canonname); /* just in case */ + ai->ai_canonname = strdup(buf); + if (ai->ai_canonname == NULL) { + freeaddrinfo(ai); + ai = NULL; + } + } + + return ai; +} +#else /* !HAVE_GETNAMEINFO */ +__attribute_malloc__ +struct addrinfo * +host_numeric_addrinfo(const struct sockaddr *sap) +{ + const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; + const struct in_addr *addr = &sin->sin_addr; + char buf[INET_ADDRSTRLEN]; + struct addrinfo *ai; + + if (sap->sa_family != AF_INET) + return NULL; + + memset(buf, 0, sizeof(buf)); + if (inet_ntop(AF_INET, (char *)addr, buf, + (socklen_t)sizeof(buf)) == NULL) + return NULL; + + ai = host_pton(buf); + + /* + * getaddrinfo(AI_NUMERICHOST) never fills in ai_canonname + */ + if (ai != NULL) { + ai->ai_canonname = strdup(buf); + if (ai->ai_canonname == NULL) { + freeaddrinfo(ai); + ai = NULL; + } + } + + return ai; +} +#endif /* !HAVE_GETNAMEINFO */ + static int is_hostname(const char *sp) {