diff options
-rw-r--r-- | support/export/hostname.c | 351 | ||||
-rw-r--r-- | support/export/nfsctl.c | 2 | ||||
-rw-r--r-- | support/include/exportfs.h | 13 | ||||
-rw-r--r-- | utils/exportfs/exportfs.c | 11 | ||||
-rw-r--r-- | utils/mountd/cache.c | 6 | ||||
-rw-r--r-- | utils/mountd/rmtab.c | 15 |
6 files changed, 382 insertions, 16 deletions
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 <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> +#include <errno.h> + #include <xlog.h> #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) { diff --git a/support/export/nfsctl.c b/support/export/nfsctl.c index ae357c7..3b9876a 100644 --- a/support/export/nfsctl.c +++ b/support/export/nfsctl.c @@ -79,7 +79,7 @@ cltsetup(struct nfsctl_client *cltarg, nfs_client *clp) j = 0; for (i = 0; i < cltarg->cl_naddr && i < NFSCLNT_ADDRMAX; i++) { - struct sockaddr_in *sin = get_addrlist_in(clp, i); + const struct sockaddr_in *sin = get_addrlist_in(clp, i); if (sin->sin_family == AF_INET) cltarg->cl_addrlist[j++] = sin->sin_addr; } diff --git a/support/include/exportfs.h b/support/include/exportfs.h index 70bdd57..97bb68e 100644 --- a/support/include/exportfs.h +++ b/support/include/exportfs.h @@ -146,6 +146,19 @@ void xtab_append(nfs_export *); int secinfo_addflavor(struct flav_info *, struct exportent *); +char * host_ntop(const struct sockaddr *sap, + char *buf, const size_t buflen); +__attribute_malloc__ +struct addrinfo * host_pton(const char *paddr); +__attribute_malloc__ +struct addrinfo * host_addrinfo(const char *hostname); +__attribute_malloc__ +char * host_canonname(const struct sockaddr *sap); +__attribute_malloc__ +struct addrinfo * host_reliable_addrinfo(const struct sockaddr *sap); +__attribute_malloc__ +struct addrinfo * host_numeric_addrinfo(const struct sockaddr *sap); + int rmtab_read(void); struct nfskey * key_lookup(char *hname); diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c index 331e57e..83d00a0 100644 --- a/utils/exportfs/exportfs.c +++ b/utils/exportfs/exportfs.c @@ -287,7 +287,7 @@ static void unexportfs(char *arg, int verbose) { nfs_export *exp; - struct hostent *hp = NULL; + struct addrinfo *ai = NULL; char *path; char *hname = arg; int htype; @@ -302,10 +302,9 @@ unexportfs(char *arg, int verbose) } if ((htype = client_gettype(hname)) == MCL_FQDN) { - if ((hp = gethostbyname(hname)) != 0) { - hp = hostent_dup (hp); - hname = (char *) hp->h_name; - } + ai = host_addrinfo(hname); + if (ai) + hname = ai->ai_canonname; } for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) { @@ -341,7 +340,7 @@ unexportfs(char *arg, int verbose) exp->m_mayexport = 0; } - if (hp) free (hp); + freeaddrinfo(ai); } static int can_test(void) diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c index 85cd829..1176c62 100644 --- a/utils/mountd/cache.c +++ b/utils/mountd/cache.c @@ -912,7 +912,7 @@ int cache_export_ent(char *domain, struct exportent *exp, char *path) int cache_export(nfs_export *exp, char *path) { - struct sockaddr_in *sin = get_addrlist_in(exp->m_client, 0); + char buf[INET_ADDRSTRLEN]; int err; FILE *f; @@ -920,8 +920,10 @@ int cache_export(nfs_export *exp, char *path) if (!f) return -1; + qword_print(f, "nfsd"); - qword_print(f, inet_ntoa(sin->sin_addr)); + qword_print(f, + host_ntop(get_addrlist(exp->m_client, 0), buf, sizeof(buf))); qword_printint(f, time(0)+30*60); qword_print(f, exp->m_client->m_hostname); err = qword_eol(f); diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c index 19b22ee..ba0fcf6 100644 --- a/utils/mountd/rmtab.c +++ b/utils/mountd/rmtab.c @@ -133,8 +133,7 @@ mountlist_del(char *hname, const char *path) void mountlist_del_all(struct sockaddr_in *sin) { - struct in_addr addr = sin->sin_addr; - struct hostent *hp; + char *hostname; struct rmtabent *rep; nfs_export *exp; FILE *fp; @@ -142,11 +141,13 @@ mountlist_del_all(struct sockaddr_in *sin) if ((lockid = xflock(_PATH_RMTABLCK, "w")) < 0) return; - if (!(hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET))) { - xlog(L_ERROR, "can't get hostname of %s", inet_ntoa(addr)); + hostname = host_canonname((struct sockaddr *)sin); + if (hostname == NULL) { + char buf[INET_ADDRSTRLEN]; + xlog(L_ERROR, "can't get hostname of %s", + host_ntop((struct sockaddr *)sin, buf, sizeof(buf))); goto out_unlock; } - hp = hostent_dup (hp); if (!setrmtabent("r")) goto out_free; @@ -155,7 +156,7 @@ mountlist_del_all(struct sockaddr_in *sin) goto out_close; while ((rep = getrmtabent(1, NULL)) != NULL) { - if (strcmp(rep->r_client, hp->h_name) == 0 && + if (strcmp(rep->r_client, hostname) == 0 && (exp = auth_authenticate("umountall", sin, rep->r_path))) continue; fputrmtabent(fp, rep, NULL); @@ -168,7 +169,7 @@ mountlist_del_all(struct sockaddr_in *sin) out_close: endrmtabent(); /* close & unlink */ out_free: - free (hp); + free(hostname); out_unlock: xfunlock(lockid); } |