X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=support%2Fexport%2Fhostname.c;h=3e949a18fe52255b51a4d77713f28b1679f16b75;hb=405eb02c20c348307a696d3afc8b22d212a77cdb;hp=8a23a89b7874466b8146946e5ec65a15b96d57b8;hpb=1408e610ac2db753543a546c4312b2eb6799f044;p=nfs-utils.git
diff --git a/support/export/hostname.c b/support/export/hostname.c
index 8a23a89..3e949a1 100644
--- a/support/export/hostname.c
+++ b/support/export/hostname.c
@@ -1,315 +1,396 @@
/*
- * support/export/hostname.c
+ * Copyright 2010 Oracle. All rights reserved.
*
- * Functions for hostname.
+ * 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 .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
-/*
-#define TEST
-*/
-
#include
-#include
-#include
-#include
#include
-#include
-#ifdef TEST
-#define xmalloc malloc
-#else
-#include "xmalloc.h"
-#include "misc.h"
-#endif
+#include
+#include
+#include
-#define ALIGNMENT sizeof (char *)
+#include "sockaddr.h"
+#include "exportfs.h"
-static int
-align (int len, int al)
+/**
+ * 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)
{
- int i;
- i = len % al;
- if (i)
- len += al - i;
- return len;
-}
+ socklen_t salen = nfs_sockaddr_length(sap);
+ int error;
+
+ memset(buf, 0, buflen);
+
+ if (salen == 0) {
+ (void)strncpy(buf, "bad family", buflen - 1);
+ return buf;
+ }
-struct hostent *
-get_hostent (const char *addr, int len, int type)
+ 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)
{
- struct hostent *cp;
- int len_ent;
- const char *name;
- int len_name;
- int num_aliases = 1;
- int len_aliases = sizeof (char *);
- int num_addr_list = 1;
- int len_addr_list = sizeof (char *);
- int pos;
- struct in_addr *ipv4;
-
- switch (type)
- {
- case AF_INET:
- ipv4 = (struct in_addr *) addr;
- name = inet_ntoa (*ipv4);
- break;
-
- default:
- return NULL;
- }
-
- len_ent = align (sizeof (*cp), ALIGNMENT);
- len_name = align (strlen (name) + 1, ALIGNMENT);
-
- num_addr_list++;
- len_addr_list += align (len, ALIGNMENT) + sizeof (char *);
-
- cp = (struct hostent *) xmalloc (len_ent + len_name + len_aliases
- + len_addr_list);
-
- cp->h_addrtype = type;
- cp->h_length = len;
- pos = len_ent;
- cp->h_name = (char *) &(((char *) cp) [pos]);
- strcpy (cp->h_name, name);
-
- pos += len_name;
- cp->h_aliases = (char **) &(((char *) cp) [pos]);
- pos += num_aliases * sizeof (char *);
- cp->h_aliases [0] = NULL;
-
- pos = len_ent + len_name + len_aliases;
- cp->h_addr_list = (char **) &(((char *) cp) [pos]);
- pos += num_addr_list * sizeof (char *);
- cp->h_addr_list [0] = (char *) &(((char *) cp) [pos]);
- memcpy (cp->h_addr_list [0], addr, cp->h_length);
- pos += align (cp->h_length, ALIGNMENT);
- cp->h_addr_list [1] = NULL;
-
- return cp;
+ 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 */
-struct hostent *
-hostent_dup (struct hostent *hp)
+/**
+ * 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)
{
- int len_ent = align (sizeof (*hp), ALIGNMENT);
- int len_name = align (strlen (hp->h_name) + 1, ALIGNMENT);
- int num_aliases = 1;
- int len_aliases = sizeof (char *);
- int num_addr_list = 1;
- int len_addr_list = sizeof (char *);
- int pos, i;
- char **sp;
- struct hostent *cp;
-
- for (sp = hp->h_aliases; sp && *sp; sp++)
- {
- num_aliases++;
- len_aliases += align (strlen (*sp) + 1, ALIGNMENT)
- + sizeof (char *);
- }
-
- for (sp = hp->h_addr_list; *sp; sp++)
- {
- num_addr_list++;
- len_addr_list += align (hp->h_length, ALIGNMENT)
- + sizeof (char *);
- }
-
- cp = (struct hostent *) xmalloc (len_ent + len_name + len_aliases
- + len_addr_list);
-
- *cp = *hp;
- pos = len_ent;
- cp->h_name = (char *) &(((char *) cp) [pos]);
- strcpy (cp->h_name, hp->h_name);
-
- pos += len_name;
- cp->h_aliases = (char **) &(((char *) cp) [pos]);
- pos += num_aliases * sizeof (char *);
- for (sp = hp->h_aliases, i = 0; i < num_aliases; i++, sp++)
- if (sp && *sp)
- {
- cp->h_aliases [i] = (char *) &(((char *) cp) [pos]);
- strcpy (cp->h_aliases [i], *sp);
- pos += align (strlen (*sp) + 1, ALIGNMENT);
- }
- else
- cp->h_aliases [i] = NULL;
-
- pos = len_ent + len_name + len_aliases;
- cp->h_addr_list = (char **) &(((char *) cp) [pos]);
- pos += num_addr_list * sizeof (char *);
- for (sp = hp->h_addr_list, i = 0; i < num_addr_list; i++, sp++)
- if (*sp)
- {
- cp->h_addr_list [i] = (char *) &(((char *) cp) [pos]);
- memcpy (cp->h_addr_list [i], *sp, hp->h_length);
- pos += align (hp->h_length, ALIGNMENT);
- }
- else
- cp->h_addr_list [i] = *sp;
-
- return cp;
+ 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, inet4;
+
+ /*
+ * 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.
+ */
+ inet4 = 1;
+ if (inet_pton(AF_INET, paddr, &sin.sin_addr) == 0)
+ inet4 = 0;
+
+ error = getaddrinfo(paddr, NULL, &hint, &ai);
+ switch (error) {
+ case 0:
+ if (!inet4 && ai->ai_addr->sa_family == AF_INET) {
+ freeaddrinfo(ai);
+ break;
+ }
+ 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;
}
-static int
-is_hostname(const char *sp)
+/**
+ * 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)
{
- if (*sp == '\0' || *sp == '@')
- return 0;
-
- for (; *sp; sp++)
- {
- if (*sp == '*' || *sp == '?' || *sp == '[' || *sp == '/')
- return 0;
- if (*sp == '\\' && sp[1])
- sp++;
- }
-
- return 1;
+ struct addrinfo *ai = NULL;
+ struct addrinfo hint = {
+#ifdef IPV6_SUPPORTED
+ .ai_family = AF_UNSPEC,
+#else
+ .ai_family = AF_INET,
+#endif
+ /* don't return duplicates */
+ .ai_protocol = (int)IPPROTO_UDP,
+ .ai_flags = 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;
}
-int
-matchhostname (const char *h1, const char *h2)
+/**
+ * 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)
{
- struct hostent *hp1, *hp2;
- int status;
-
- if (strcasecmp (h1, h2) == 0)
- return 1;
-
- if (!is_hostname (h1) || !is_hostname (h2))
- return 0;
-
- hp1 = gethostbyname (h1);
- if (hp1 == NULL)
- return 0;
-
- hp1 = hostent_dup (hp1);
-
- hp2 = gethostbyname (h2);
- if (hp2)
- {
- if (strcasecmp (hp1->h_name, hp2->h_name) == 0)
- status = 1;
- else
- {
- char **ap1, **ap2;
-
- status = 0;
- for (ap1 = hp1->h_addr_list; *ap1 && status == 0; ap1++)
- for (ap2 = hp2->h_addr_list; *ap2; ap2++)
- if (memcmp (*ap1, *ap2, sizeof (struct in_addr)) == 0)
- {
- status = 1;
- break;
- }
+ socklen_t salen = nfs_sockaddr_length(sap);
+ char buf[NI_MAXHOST];
+ int error;
+
+ if (salen == 0) {
+ xlog(D_GENERAL, "%s: unsupported address family %d",
+ __func__, sap->sa_family);
+ return NULL;
}
- }
- else
- status = 0;
- free (hp1);
- return status;
+ 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;
-/* Map IP to hostname, and then map back to addr to make sure it is a
- * reliable hostname
+ 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
+ * matching forward and reverse mappings.
+ *
+ * Returns addrinfo structure with just the provided address with
+ * ai_canonname filled in. If there is a problem with resolution or
+ * the resolved records don't match up properly then it returns NULL
+ *
+ * Caller must free the returned structure with freeaddrinfo(3).
*/
-struct hostent *
-get_reliable_hostbyaddr(const char *addr, int len, int type)
+__attribute_malloc__
+struct addrinfo *
+host_reliable_addrinfo(const struct sockaddr *sap)
{
- struct hostent *hp = NULL;
-
- struct hostent *reverse;
- struct hostent *forward;
- char **sp;
+ struct addrinfo *ai, *a;
+ char *hostname;
- reverse = gethostbyaddr (addr, len, type);
- if (!reverse)
+ hostname = host_canonname(sap);
+ if (hostname == NULL)
return NULL;
- /* must make sure the hostent is authorative. */
+ ai = host_addrinfo(hostname);
+ if (!ai)
+ goto out_free_hostname;
- reverse = hostent_dup (reverse);
- forward = gethostbyname (reverse->h_name);
+ /* make sure there's a matching address in the list */
+ for (a = ai; a; a = a->ai_next)
+ if (nfs_compare_sockaddr(a->ai_addr, sap))
+ break;
- if (forward) {
- /* now make sure the "addr" is in the list */
- for (sp = forward->h_addr_list ; *sp ; sp++) {
- if (memcmp (*sp, addr, forward->h_length) == 0)
- break;
- }
+ freeaddrinfo(ai);
+ if (!a)
+ goto out_free_hostname;
- if (*sp) {
- /* it's valid */
- hp = hostent_dup (forward);
- }
- else {
- /* it was a FAKE */
- xlog (L_WARNING, "Fake hostname %s for %s - forward lookup doesn't match reverse",
- reverse->h_name, inet_ntoa(*(struct in_addr*)addr));
- }
+ /* get addrinfo with just the original address */
+ ai = host_numeric_addrinfo(sap);
+ if (!ai)
+ goto out_free_hostname;
+
+ /* and populate its ai_canonname field */
+ free(ai->ai_canonname);
+ ai->ai_canonname = hostname;
+ return ai;
+
+out_free_hostname:
+ free(hostname);
+ return NULL;
+}
+
+/**
+ * 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 = nfs_sockaddr_length(sap);
+ char buf[INET6_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;
}
- else {
- /* never heard of it. misconfigured DNS? */
- xlog (L_WARNING, "Fake hostname %s for %s - forward lookup doesn't exist",
- reverse->h_name, inet_ntoa(*(struct in_addr*)addr));
+
+ 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;
+ }
}
- free (reverse);
- return hp;
+ 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;
-#ifdef TEST
-void
-print_host (struct hostent *hp)
-{
- char **sp;
-
- if (hp)
- {
- printf ("official hostname: %s\n", hp->h_name);
- printf ("aliases:\n");
- for (sp = hp->h_aliases; *sp; sp++)
- printf (" %s\n", *sp);
- printf ("IP addresses:\n");
- for (sp = hp->h_addr_list; *sp; sp++)
- printf (" %s\n", inet_ntoa (*(struct in_addr *) *sp));
- }
- else
- printf ("Not host information\n");
-}
+ memset(buf, 0, sizeof(buf));
+ if (inet_ntop(AF_INET, (char *)addr, buf,
+ (socklen_t)sizeof(buf)) == NULL)
+ return NULL;
-int
-main (int argc, char **argv)
-{
- struct hostent *hp = gethostbyname (argv [1]);
- struct hostent *cp;
- struct in_addr addr;
-
- print_host (hp);
-
- if (hp)
- {
- cp = hostent_dup (hp);
- print_host (cp);
- free (cp);
- }
- printf ("127.0.0.1 == %s: %d\n", argv [1],
- matchhostname ("127.0.0.1", argv [1]));
- addr.s_addr = inet_addr(argv [2]);
- printf ("%s\n", inet_ntoa (addr));
- cp = get_hostent ((const char *)&addr, sizeof(addr), AF_INET);
- print_host (cp);
- return 0;
+ 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
+#endif /* !HAVE_GETNAMEINFO */