]> The Tcpdump Group git mirrors - libpcap/blobdiff - nametoaddr.c
Update config.{guess,sub}, timestamps 2023-01-01,2023-01-21
[libpcap] / nametoaddr.c
index 6ffb64b61b5d72d89eca1cc78974299a868235a1..7a04a61dbd6c39f17a5ff204cc61391f1c9c634a 100644 (file)
      *
      * We use getaddrinfo(), so we include Wspiapi.h here.
      */
-    #include <Wspiapi.h>
+    #include <wspiapi.h>
   #endif /* INET6 */
 #else /* _WIN32 */
   #include <sys/param.h>
-  #include <sys/types.h>                       /* concession to AIX */
+  #include <sys/types.h>
   #include <sys/socket.h>
   #include <sys/time.h>
 
                unsigned char ether_addr_octet[6];
        };
       #endif /* HAVE_STRUCT_ETHER_ADDR */
-    #endif
+    #endif /* what declares ether_hostton() */
 
     #ifdef NEED_NETINET_IF_ETHER_H
       #include <net/if.h>      /* Needed on some platforms */
        * No header declares it, so declare it ourselves.
        */
       extern int ether_hostton(const char *, struct ether_addr *);
-    #endif /* defined(HAVE_DECL_ETHER_HOSTTON) || !HAVE_DECL_ETHER_HOSTTON */
+    #endif /* !defined(HAVE_DECL_ETHER_HOSTTON) */
   #endif /* HAVE_ETHER_HOSTTON */
 
   #include <arpa/inet.h>
   #include <netdb.h>
 #endif /* _WIN32 */
 
-#include <ctype.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "pcap-int.h"
 
+#include "diag-control.h"
+
 #include "gencode.h"
 #include <pcap/namedb.h>
 #include "nametoaddr.h"
 #define NTOHS(x) (x) = ntohs(x)
 #endif
 
-static inline int xdtoi(int);
-
 /*
  *  Convert host name to internet address.
  *  Return 0 upon failure.
+ *  XXX - not thread-safe; don't use it inside libpcap.
  */
 bpf_u_int32 **
 pcap_nametoaddr(const char *name)
@@ -163,7 +163,23 @@ pcap_nametoaddr(const char *name)
        bpf_u_int32 **p;
        struct hostent *hp;
 
+       /*
+        * gethostbyname() is deprecated on Windows, perhaps because
+        * it's not thread-safe, or because it doesn't support IPv6,
+        * or both.
+        *
+        * We deprecate pcap_nametoaddr() on all platforms because
+        * it's not thread-safe; we supply it for backwards compatibility,
+        * so suppress the deprecation warning.  We could, I guess,
+        * use getaddrinfo() and construct the array ourselves, but
+        * that's probably not worth the effort, as that wouldn't make
+        * this thread-safe - we can't change the API to require that
+        * our caller free the address array, so we still have to reuse
+        * a local array.
+        */
+DIAG_OFF_DEPRECATION
        if ((hp = gethostbyname(name)) != NULL) {
+DIAG_ON_DEPRECATION
 #ifndef h_addr
                hlist[0] = (bpf_u_int32 *)hp->h_addr;
                NTOHL(hp->h_addr);
@@ -178,7 +194,6 @@ pcap_nametoaddr(const char *name)
                return 0;
 }
 
-#ifdef INET6
 struct addrinfo *
 pcap_nametoaddrinfo(const char *name)
 {
@@ -195,23 +210,17 @@ pcap_nametoaddrinfo(const char *name)
        else
                return res;
 }
-#endif /*INET6*/
 
 /*
  *  Convert net name to internet address.
  *  Return 0 upon failure.
+ *  XXX - not guaranteed to be thread-safe!  See below for platforms
+ *  on which it is thread-safe and on which it isn't.
  */
+#if defined(_WIN32) || defined(__CYGWIN__)
 bpf_u_int32
-pcap_nametonetaddr(const char *name)
+pcap_nametonetaddr(const char *name _U_)
 {
-#ifndef _WIN32
-       struct netent *np;
-
-       if ((np = getnetbyname(name)) != NULL)
-               return np->n_net;
-       else
-               return 0;
-#else
        /*
         * There's no "getnetbyname()" on Windows.
         *
@@ -225,8 +234,86 @@ pcap_nametonetaddr(const char *name)
         * of *UN*X* machines.)
         */
        return 0;
-#endif
 }
+#else /* _WIN32 */
+bpf_u_int32
+pcap_nametonetaddr(const char *name)
+{
+       /*
+        * UN*X.
+        */
+       struct netent *np;
+  #if defined(HAVE_LINUX_GETNETBYNAME_R)
+       /*
+        * We have Linux's reentrant getnetbyname_r().
+        */
+       struct netent result_buf;
+       char buf[1024]; /* arbitrary size */
+       int h_errnoval;
+       int err;
+
+       /*
+        * Apparently, the man page at
+        *
+        *    http://man7.org/linux/man-pages/man3/getnetbyname_r.3.html
+        *
+        * lies when it says
+        *
+        *    If the function call successfully obtains a network record,
+        *    then *result is set pointing to result_buf; otherwise, *result
+        *    is set to NULL.
+        *
+        * and, in fact, at least in some versions of GNU libc, it does
+        * *not* always get set if getnetbyname_r() succeeds.
+        */
+       np = NULL;
+       err = getnetbyname_r(name, &result_buf, buf, sizeof buf, &np,
+           &h_errnoval);
+       if (err != 0) {
+               /*
+                * XXX - dynamically allocate the buffer, and make it
+                * bigger if we get ERANGE back?
+                */
+               return 0;
+       }
+  #elif defined(HAVE_SOLARIS_IRIX_GETNETBYNAME_R)
+       /*
+        * We have Solaris's and IRIX's reentrant getnetbyname_r().
+        */
+       struct netent result_buf;
+       char buf[1024]; /* arbitrary size */
+
+       np = getnetbyname_r(name, &result_buf, buf, (int)sizeof buf);
+  #elif defined(HAVE_AIX_GETNETBYNAME_R)
+       /*
+        * We have AIX's reentrant getnetbyname_r().
+        */
+       struct netent result_buf;
+       struct netent_data net_data;
+
+       if (getnetbyname_r(name, &result_buf, &net_data) == -1)
+               np = NULL;
+       else
+               np = &result_buf;
+  #else
+       /*
+        * We don't have any getnetbyname_r(); either we have a
+        * getnetbyname() that uses thread-specific data, in which
+        * case we're thread-safe (sufficiently recent FreeBSD,
+        * sufficiently recent Darwin-based OS, sufficiently recent
+        * HP-UX, sufficiently recent Tru64 UNIX), or we have the
+        * traditional getnetbyname() (everything else, including
+        * current NetBSD and OpenBSD), in which case we're not
+        * thread-safe.
+        */
+       np = getnetbyname(name);
+  #endif
+       if (np != NULL)
+               return np->n_net;
+       else
+               return 0;
+}
+#endif /* _WIN32 */
 
 /*
  * Convert a port name to its port and protocol numbers.
@@ -236,20 +323,113 @@ pcap_nametonetaddr(const char *name)
 int
 pcap_nametoport(const char *name, int *port, int *proto)
 {
-       struct servent *sp;
+       struct addrinfo hints, *res, *ai;
+       int error;
+       struct sockaddr_in *in4;
+#ifdef INET6
+       struct sockaddr_in6 *in6;
+#endif
        int tcp_port = -1;
        int udp_port = -1;
 
+       /*
+        * We check for both TCP and UDP in case there are
+        * ambiguous entries.
+        */
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = PF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+       hints.ai_protocol = IPPROTO_TCP;
+       error = getaddrinfo(NULL, name, &hints, &res);
+       if (error != 0) {
+               if (error != EAI_NONAME &&
+                   error != EAI_SERVICE) {
+                       /*
+                        * This is a real error, not just "there's
+                        * no such service name".
+                        * XXX - this doesn't return an error string.
+                        */
+                       return 0;
+               }
+       } else {
+               /*
+                * OK, we found it.  Did it find anything?
+                */
+               for (ai = res; ai != NULL; ai = ai->ai_next) {
+                       /*
+                        * Does it have an address?
+                        */
+                       if (ai->ai_addr != NULL) {
+                               /*
+                                * Yes.  Get a port number; we're done.
+                                */
+                               if (ai->ai_addr->sa_family == AF_INET) {
+                                       in4 = (struct sockaddr_in *)ai->ai_addr;
+                                       tcp_port = ntohs(in4->sin_port);
+                                       break;
+                               }
+#ifdef INET6
+                               if (ai->ai_addr->sa_family == AF_INET6) {
+                                       in6 = (struct sockaddr_in6 *)ai->ai_addr;
+                                       tcp_port = ntohs(in6->sin6_port);
+                                       break;
+                               }
+#endif
+                       }
+               }
+               freeaddrinfo(res);
+       }
+
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = PF_UNSPEC;
+       hints.ai_socktype = SOCK_DGRAM;
+       hints.ai_protocol = IPPROTO_UDP;
+       error = getaddrinfo(NULL, name, &hints, &res);
+       if (error != 0) {
+               if (error != EAI_NONAME &&
+                   error != EAI_SERVICE) {
+                       /*
+                        * This is a real error, not just "there's
+                        * no such service name".
+                        * XXX - this doesn't return an error string.
+                        */
+                       return 0;
+               }
+       } else {
+               /*
+                * OK, we found it.  Did it find anything?
+                */
+               for (ai = res; ai != NULL; ai = ai->ai_next) {
+                       /*
+                        * Does it have an address?
+                        */
+                       if (ai->ai_addr != NULL) {
+                               /*
+                                * Yes.  Get a port number; we're done.
+                                */
+                               if (ai->ai_addr->sa_family == AF_INET) {
+                                       in4 = (struct sockaddr_in *)ai->ai_addr;
+                                       udp_port = ntohs(in4->sin_port);
+                                       break;
+                               }
+#ifdef INET6
+                               if (ai->ai_addr->sa_family == AF_INET6) {
+                                       in6 = (struct sockaddr_in6 *)ai->ai_addr;
+                                       udp_port = ntohs(in6->sin6_port);
+                                       break;
+                               }
+#endif
+                       }
+               }
+               freeaddrinfo(res);
+       }
+
        /*
         * We need to check /etc/services for ambiguous entries.
-        * If we find the ambiguous entry, and it has the
+        * If we find an ambiguous entry, and it has the
         * same port number, change the proto to PROTO_UNDEF
         * so both TCP and UDP will be checked.
         */
-       sp = getservbyname(name, "tcp");
-       if (sp != NULL) tcp_port = ntohs(sp->s_port);
-       sp = getservbyname(name, "udp");
-       if (sp != NULL) udp_port = ntohs(sp->s_port);
        if (tcp_port >= 0) {
                *port = tcp_port;
                *proto = IPPROTO_TCP;
@@ -328,12 +508,62 @@ pcap_nametoportrange(const char *name, int *port1, int *port2, int *proto)
        return 1;
 }
 
+/*
+ * XXX - not guaranteed to be thread-safe!  See below for platforms
+ * on which it is thread-safe and on which it isn't.
+ */
 int
 pcap_nametoproto(const char *str)
 {
        struct protoent *p;
+  #if defined(HAVE_LINUX_GETNETBYNAME_R)
+       /*
+        * We have Linux's reentrant getprotobyname_r().
+        */
+       struct protoent result_buf;
+       char buf[1024]; /* arbitrary size */
+       int err;
+
+       err = getprotobyname_r(str, &result_buf, buf, sizeof buf, &p);
+       if (err != 0) {
+               /*
+                * XXX - dynamically allocate the buffer, and make it
+                * bigger if we get ERANGE back?
+                */
+               return 0;
+       }
+  #elif defined(HAVE_SOLARIS_IRIX_GETNETBYNAME_R)
+       /*
+        * We have Solaris's and IRIX's reentrant getprotobyname_r().
+        */
+       struct protoent result_buf;
+       char buf[1024]; /* arbitrary size */
 
+       p = getprotobyname_r(str, &result_buf, buf, (int)sizeof buf);
+  #elif defined(HAVE_AIX_GETNETBYNAME_R)
+       /*
+        * We have AIX's reentrant getprotobyname_r().
+        */
+       struct protoent result_buf;
+       struct protoent_data proto_data;
+
+       if (getprotobyname_r(str, &result_buf, &proto_data) == -1)
+               p = NULL;
+       else
+               p = &result_buf;
+  #else
+       /*
+        * We don't have any getprotobyname_r(); either we have a
+        * getprotobyname() that uses thread-specific data, in which
+        * case we're thread-safe (sufficiently recent FreeBSD,
+        * sufficiently recent Darwin-based OS, sufficiently recent
+        * HP-UX, sufficiently recent Tru64 UNIX, Windows), or we have
+        * the traditional getprotobyname() (everything else, including
+        * current NetBSD and OpenBSD), in which case we're not
+        * thread-safe.
+        */
        p = getprotobyname(str);
+  #endif
        if (p != 0)
                return p->p_proto;
        else
@@ -353,30 +583,27 @@ struct eproto {
  * Debian, at least, so make it a public symbol, even though we
  * don't officially export it by declaring it in a header file.
  * (Programs *should* do this themselves, as tcpdump now does.)
+ *
+ * We declare it here, right before defining it, to squelch any
+ * warnings we might get from compilers about the lack of a
+ * declaration.
  */
+PCAP_API struct eproto eproto_db[];
 PCAP_API_DEF struct eproto eproto_db[] = {
-       { "pup", ETHERTYPE_PUP },
-       { "xns", ETHERTYPE_NS },
+       { "aarp", ETHERTYPE_AARP },
+       { "arp", ETHERTYPE_ARP },
+       { "atalk", ETHERTYPE_ATALK },
+       { "decnet", ETHERTYPE_DN },
        { "ip", ETHERTYPE_IP },
 #ifdef INET6
        { "ip6", ETHERTYPE_IPV6 },
 #endif
-       { "arp", ETHERTYPE_ARP },
-       { "rarp", ETHERTYPE_REVARP },
-       { "sprite", ETHERTYPE_SPRITE },
+       { "lat", ETHERTYPE_LAT },
+       { "loopback", ETHERTYPE_LOOPBACK },
        { "mopdl", ETHERTYPE_MOPDL },
        { "moprc", ETHERTYPE_MOPRC },
-       { "decnet", ETHERTYPE_DN },
-       { "lat", ETHERTYPE_LAT },
+       { "rarp", ETHERTYPE_REVARP },
        { "sca", ETHERTYPE_SCA },
-       { "lanbridge", ETHERTYPE_LANBRIDGE },
-       { "vexp", ETHERTYPE_VEXP },
-       { "vprod", ETHERTYPE_VPROD },
-       { "atalk", ETHERTYPE_ATALK },
-       { "atalkarp", ETHERTYPE_AARP },
-       { "loopback", ETHERTYPE_LOOPBACK },
-       { "decdts", ETHERTYPE_DECDTS },
-       { "decdns", ETHERTYPE_DECDNS },
        { (char *)0, 0 }
 };
 
@@ -417,17 +644,16 @@ pcap_nametollc(const char *s)
        return PROTO_UNDEF;
 }
 
-/* Hex digit to integer. */
-static inline int
-xdtoi(c)
-       register int c;
+/* Hex digit to 8-bit unsigned integer. */
+static inline u_char
+xdtoi(u_char c)
 {
-       if (isdigit(c))
-               return c - '0';
-       else if (islower(c))
-               return c - 'a' + 10;
+       if (c >= '0' && c <= '9')
+               return (u_char)(c - '0');
+       else if (c >= 'a' && c <= 'f')
+               return (u_char)(c - 'a' + 10);
        else
-               return c - 'A' + 10;
+               return (u_char)(c - 'A' + 10);
 }
 
 int
@@ -438,10 +664,17 @@ __pcap_atoin(const char *s, bpf_u_int32 *addr)
 
        *addr = 0;
        len = 0;
-       while (1) {
+       for (;;) {
                n = 0;
-               while (*s && *s != '.')
+               while (*s && *s != '.') {
+                       if (n > 25) {
+                               /* The result will be > 255 */
+                               return -1;
+                       }
                        n = n * 10 + *s++ - '0';
+               }
+               if (n > 255)
+                       return -1;
                *addr <<= 8;
                *addr |= n & 0xff;
                len += 8;
@@ -486,7 +719,7 @@ u_char *
 pcap_ether_aton(const char *s)
 {
        register u_char *ep, *e;
-       register u_int d;
+       register u_char d;
 
        e = ep = (u_char *)malloc(6);
        if (e == NULL)
@@ -496,7 +729,7 @@ pcap_ether_aton(const char *s)
                if (*s == ':' || *s == '.' || *s == '-')
                        s += 1;
                d = xdtoi(*s++);
-               if (isxdigit((unsigned char)*s)) {
+               if (PCAP_ISXDIGIT(*s)) {
                        d <<= 4;
                        d |= xdtoi(*s++);
                }
@@ -507,7 +740,11 @@ pcap_ether_aton(const char *s)
 }
 
 #ifndef HAVE_ETHER_HOSTTON
-/* Roll our own */
+/*
+ * Roll our own.
+ * XXX - not thread-safe, because pcap_next_etherent() isn't thread-
+ * safe!  Needs a mutex or a thread-safe pcap_next_etherent().
+ */
 u_char *
 pcap_ether_hostton(const char *name)
 {
@@ -539,15 +776,23 @@ pcap_ether_hostton(const char *name)
        return (NULL);
 }
 #else
-/* Use the os supplied routines */
+/*
+ * Use the OS-supplied routine.
+ * This *should* be thread-safe; the API doesn't have a static buffer.
+ */
 u_char *
 pcap_ether_hostton(const char *name)
 {
        register u_char *ap;
        u_char a[6];
+       char namebuf[1024];
 
+       /*
+        * In AIX 7.1 and 7.2: int ether_hostton(char *, struct ether_addr *);
+        */
+       pcap_strlcpy(namebuf, name, sizeof(namebuf));
        ap = NULL;
-       if (ether_hostton(name, (struct ether_addr *)a) == 0) {
+       if (ether_hostton(namebuf, (struct ether_addr *)a) == 0) {
                ap = (u_char *)malloc(6);
                if (ap != NULL)
                        memcpy((char *)ap, (char *)a, 6);
@@ -556,10 +801,13 @@ pcap_ether_hostton(const char *name)
 }
 #endif
 
+/*
+ * XXX - not guaranteed to be thread-safe!
+ */
 int
+#ifdef DECNETLIB
 __pcap_nametodnaddr(const char *name, u_short *res)
 {
-#ifdef DECNETLIB
        struct nodeent *getnodebyname();
        struct nodeent *nep;
 
@@ -570,6 +818,8 @@ __pcap_nametodnaddr(const char *name, u_short *res)
        memcpy((char *)res, (char *)nep->n_addr, sizeof(unsigned short));
        return(1);
 #else
+__pcap_nametodnaddr(const char *name _U_, u_short *res _U_)
+{
        return(0);
 #endif
 }