X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fstatd%2Fhostname.c;h=616a3cbf45e2668ea57efe69c5aacf6995f80cd5;hp=3d10d6f1fa1f59e489388615ec2b80b69bf2d26e;hb=1ea2c3be33f2eb4630c5cdb78edf2bb670b294ab;hpb=cbd3a131e5c02bbd7b92a72b3ac467d71cfee1c4 diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c index 3d10d6f..616a3cb 100644 --- a/utils/statd/hostname.c +++ b/utils/statd/hostname.c @@ -30,13 +30,77 @@ #include #include +#include #include #include +#include #include "sockaddr.h" #include "statd.h" #include "xlog.h" +/** + * statd_present_address - convert sockaddr to presentation address + * @sap: pointer to socket address to convert + * @buf: pointer to buffer to fill in + * @buflen: length of buffer + * + * Convert the passed-in sockaddr-style address to presentation format. + * The presentation format address is placed in @buf and is + * '\0'-terminated. + * + * Returns true if successful; otherwise false. + * + * getnameinfo(3) is preferred, since it can parse IPv6 scope IDs. + * An alternate version of statd_present_address() is available to + * handle older glibcs that do not have getnameinfo(3). + */ +#ifdef HAVE_GETNAMEINFO +_Bool +statd_present_address(const struct sockaddr *sap, char *buf, const size_t buflen) +{ + socklen_t salen; + int error; + + salen = nfs_sockaddr_length(sap); + if (salen == 0) { + xlog(D_GENERAL, "%s: unsupported address family", + __func__); + return false; + } + + error = getnameinfo(sap, salen, buf, (socklen_t)buflen, + NULL, 0, NI_NUMERICHOST); + if (error != 0) { + xlog(D_GENERAL, "%s: getnameinfo(3): %s", + __func__, gai_strerror(error)); + return false; + } + return true; +} +#else /* !HAVE_GETNAMEINFO */ +_Bool +statd_present_address(const struct sockaddr *sap, char *buf, const size_t buflen) +{ + const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; + + if (sin->sin_family != AF_INET) { + xlog(D_GENERAL, "%s: unsupported address family", __func__); + return false; + } + + /* ensure '\0' termination */ + memset(buf, 0, buflen); + + if (inet_ntop(AF_INET, (char *)&sin->sin_addr, + buf, (socklen_t)buflen) == NULL) { + xlog(D_GENERAL, "%s: inet_ntop(3): %m", __func__); + return false; + } + return true; +} +#endif /* !HAVE_GETNAMEINFO */ + /* * Look up the hostname; report exceptional errors. Caller must * call freeaddrinfo(3) if a valid addrinfo is returned. @@ -62,6 +126,105 @@ get_addrinfo(const char *hostname, const struct addrinfo *hint) return NULL; } +#ifdef HAVE_GETNAMEINFO +static _Bool +get_nameinfo(const struct sockaddr *sap, const socklen_t salen, + /*@out@*/ char *buf, const socklen_t buflen) +{ + int error; + + error = getnameinfo(sap, salen, buf, buflen, NULL, 0, NI_NAMEREQD); + if (error != 0) { + xlog(D_GENERAL, "%s: failed to resolve address: %s", + __func__, gai_strerror(error)); + return false; + } + + return true; +} +#else /* !HAVE_GETNAMEINFO */ +static _Bool +get_nameinfo(const struct sockaddr *sap, + __attribute__ ((unused)) const socklen_t salen, + /*@out@*/ char *buf, socklen_t buflen) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)(char *)sap; + struct hostent *hp; + + if (sin->sin_family != AF_INET) { + xlog(D_GENERAL, "%s: unknown address family: %d", + sin->sin_family); + return false; + } + + hp = gethostbyaddr((const char *)&(sin->sin_addr.s_addr), + sizeof(struct in_addr), AF_INET); + if (hp == NULL) { + xlog(D_GENERAL, "%s: failed to resolve address: %m", __func__); + return false; + } + + strncpy(buf, hp->h_name, (size_t)buflen); + return true; +} +#endif /* !HAVE_GETNAMEINFO */ + +/** + * statd_canonical_name - choose file name for monitor record files + * @hostname: C string containing hostname or presentation address + * + * Returns a '\0'-terminated ASCII string containing a fully qualified + * canonical hostname, or NULL if @hostname does not have a reverse + * mapping. Caller must free the result with free(3). + * + * Incoming hostnames are looked up to determine the canonical hostname, + * and incoming presentation addresses are converted to canonical + * hostnames. + * + * We won't monitor peers that don't have a reverse map. The canonical + * name gives us a key for our monitor list. + */ +__attribute_malloc__ +char * +statd_canonical_name(const char *hostname) +{ + struct addrinfo hint = { +#ifdef IPV6_SUPPORTED + .ai_family = AF_UNSPEC, +#else /* !IPV6_SUPPORTED */ + .ai_family = AF_INET, +#endif /* !IPV6_SUPPORTED */ + .ai_flags = AI_NUMERICHOST, + .ai_protocol = (int)IPPROTO_UDP, + }; + char buf[NI_MAXHOST]; + struct addrinfo *ai; + + ai = get_addrinfo(hostname, &hint); + if (ai != NULL) { + /* @hostname was a presentation address */ + _Bool result; + result = get_nameinfo(ai->ai_addr, ai->ai_addrlen, + buf, (socklen_t)sizeof(buf)); + freeaddrinfo(ai); + if (!result) + /* OK to use presentation address, + * if no reverse map exists */ + return strdup(hostname); + return strdup(buf); + } + + /* @hostname was a hostname */ + hint.ai_flags = AI_CANONNAME; + ai = get_addrinfo(hostname, &hint); + if (ai == NULL) + return NULL; + strcpy(buf, ai->ai_canonname); + freeaddrinfo(ai); + + return strdup(buf); +} + /** * statd_matchhostname - check if two hostnames are equivalent * @hostname1: C string containing hostname