maint: update copyright
[gnulib.git] / lib / canon-host.c
index 161fdb3..862a701 100644 (file)
@@ -1,80 +1,90 @@
 /* Host name canonicalization
 
-   Copyright (C) 1995 Free Software Foundation, Inc.
+   Copyright (C) 2005-2014 Free Software Foundation, Inc.
 
-   Written by Miles Bader <miles@gnu.ai.mit.edu>
+   Written by Derek Price <derek@ximbiot.com>.
 
-   This program 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, or (at
-   your option) any later version.
+   This program 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 3, or (at your option)
+   any later version.
 
-   This program 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.
+   This program 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 this program; if not, write to the Free Software Foundation,
-   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-#ifdef HAVE_STRING_H
-# include <string.h>
-#endif
-#ifdef HAVE_NETDB_H
-# include <netdb.h>
-#endif
-#ifdef HAVE_SYS_SOCKET_H
-# include <sys/socket.h>
-#endif
-
-#ifdef HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-# include <arpa/inet.h>
-#endif
-
-/* Returns the canonical hostname associated with HOST (allocated in a static
-   buffer), or 0 if it can't be determined.  */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include "canon-host.h"
+
+#include <string.h>
+#include <netdb.h>
+
+/* Store the last error for the single-threaded version of this function.  */
+static int last_cherror;
+
+/* Single-threaded of wrapper for canon_host_r.  After a NULL return, error
+   messages may be retrieved via ch_strerror().  */
 char *
-canon_host (host)
-     char *host;
+canon_host (const char *host)
 {
-#ifdef HAVE_GETHOSTBYNAME
-  struct hostent *he = gethostbyname (host);
+  return canon_host_r (host, &last_cherror);
+}
+
+/* Return a malloc'd string containing the canonical hostname associated with
+   HOST, or NULL if a canonical name cannot be determined.  On NULL return,
+   if CHERROR is not NULL, set *CHERROR to an error code as returned by
+   getaddrinfo().  Use ch_strerror_r() or gai_strerror() to convert a *CHERROR
+   value to a string suitable for error messages.
+
+   WARNINGS
+     HOST must be a string representation of a resolvable name for this host.
+     Strings containing an IP address in dotted decimal notation will be
+     returned as-is, without further resolution.
 
-  if (he)
+     The use of the word "canonical" in this context is unfortunate but
+     entrenched.  The value returned by this function will be the end result
+     of the resolution of any CNAME chains in the DNS.  There may only be one
+     such value for any given hostname, though the actual IP address
+     referenced by this value and the device using that IP address may each
+     actually have any number of such "canonical" hostnames.  See the POSIX
+     getaddrinfo spec <http://www.opengroup.org/susv3xsh/getaddrinfo.html">,
+     RFC 1034 <http://www.faqs.org/rfcs/rfc1034.html>, & RFC 2181
+     <http://www.faqs.org/rfcs/rfc2181.html> for more on what this confusing
+     term really refers to. */
+char *
+canon_host_r (char const *host, int *cherror)
+{
+  char *retval = NULL;
+  static struct addrinfo hints;
+  struct addrinfo *res = NULL;
+  int status;
+
+  hints.ai_flags = AI_CANONNAME;
+  status = getaddrinfo (host, NULL, &hints, &res);
+  if (!status)
     {
-# ifdef HAVE_GETHOSTBYADDR
-      char *addr = 0;
-
-      /* Try and get an ascii version of the numeric host address.  */
-      switch (he->h_addrtype)
-       {
-#  ifdef HAVE_INET_NTOA
-       case AF_INET:
-         addr = inet_ntoa (*(struct in_addr *) he->h_addr);
-         break;
-#  endif /* HAVE_INET_NTOA */
-       }
-
-      if (addr && strcmp (he->h_name, addr) == 0)
-       /* gethostbyname() cheated!  Lookup the host name via the address
-          this time to get the actual host name.  */
-       he = gethostbyaddr (he->h_addr, he->h_length, he->h_addrtype);
-# endif /* HAVE_GETHOSTBYADDR */
-
-      if (he)
-       return (char *) (he->h_name);
+      /* http://lists.gnu.org/archive/html/bug-coreutils/2006-09/msg00300.html
+         says Darwin 7.9.0 getaddrinfo returns 0 but sets
+         res->ai_canonname to NULL.  */
+      retval = strdup (res->ai_canonname ? res->ai_canonname : host);
+      if (!retval && cherror)
+        *cherror = EAI_MEMORY;
+      freeaddrinfo (res);
     }
-#endif /* HAVE_GETHOSTBYNAME */
-  return 0;
+  else if (cherror)
+    *cherror = status;
+
+  return retval;
+}
+
+/* Return a string describing the last error encountered by canon_host.  */
+const char *
+ch_strerror (void)
+{
+  return gai_strerror (last_cherror);
 }