/* * Copyright 2009 Oracle. All rights reserved. * * This file is part of nfs-utils. * * nfs-utils is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * nfs-utils is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with nfs-utils. If not, see . */ /* * NSM for Linux. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "sockaddr.h" #include "statd.h" #include "xlog.h" #ifndef HAVE_DECL_AI_ADDRCONFIG #define AI_ADDRCONFIG 0 #endif /** * 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. */ __attribute_malloc__ static struct addrinfo * get_addrinfo(const char *hostname, const struct addrinfo *hint) { struct addrinfo *ai = NULL; int error; error = getaddrinfo(hostname, NULL, hint, &ai); switch (error) { case 0: return ai; case EAI_NONAME: break; default: xlog(D_GENERAL, "%s: failed to resolve host %s: %s", __func__, hostname, gai_strerror(error)); } 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 * @hostname2: C string containing hostname * * Returns true if the hostnames are the same, the hostnames resolve * to the same canonical name, or the hostnames resolve to at least * one address that is the same. False is returned if the hostnames * do not match in any of these ways, if either hostname contains * wildcard characters, if either hostname is a netgroup name, or * if an error occurs. */ _Bool statd_matchhostname(const char *hostname1, const char *hostname2) { struct addrinfo *ai1, *ai2, *results1 = NULL, *results2 = NULL; struct addrinfo hint = { .ai_family = AF_UNSPEC, .ai_flags = AI_CANONNAME, .ai_protocol = (int)IPPROTO_UDP, }; _Bool result = false; if (strcasecmp(hostname1, hostname2) == 0) { result = true; goto out; } results1 = get_addrinfo(hostname1, &hint); if (results1 == NULL) goto out; results2 = get_addrinfo(hostname2, &hint); if (results2 == NULL) goto out; if (strcasecmp(results1->ai_canonname, results2->ai_canonname) == 0) { result = true; goto out; } for (ai1 = results1; ai1 != NULL; ai1 = ai1->ai_next) for (ai2 = results2; ai2 != NULL; ai2 = ai2->ai_next) if (nfs_compare_sockaddr(ai1->ai_addr, ai2->ai_addr)) { result = true; break; } out: freeaddrinfo(results2); freeaddrinfo(results1); xlog(D_CALL, "%s: hostnames %s", __func__, (result ? "matched" : "did not match")); return result; }