diff options
author | Guy Harris <guy@alum.mit.edu> | 2018-01-27 23:09:39 -0800 |
---|---|---|
committer | Guy Harris <guy@alum.mit.edu> | 2018-01-27 23:09:39 -0800 |
commit | ac690905a78f794507b209e87197f636f598d51f (patch) | |
tree | 720f328e0254d23e0c04be5c51e33f40abbbc721 /nametoaddr.c | |
parent | 76aeb920f4698a9544c32da50fc3b84cdc080ae7 (diff) |
Try to make pcap_compile() thread-safer.
Always use getaddrinfo() for name-to-address lookups; it's the only
portable API that lets you do those lookups in a thread-safe fashion.
This means we require getaddrinfo(); yes, this leaves older OSes out in
the cold - you'd have to provide your own getaddrinfo() on older OSes
that lack it, just as you now will need to provide versions of Flex and
bison/Berkeley YACC that can support reentrant scanners/parsers on older
OSes that lack it, and compilers capable of supporting some C99 features
on older platforms that lack it.
Try to find reentrant versions of getnetbyname() and getprotobyname();
that's a pain, and, if none are found, we'll just have to hope that the
native versions are thread-safe, which they aren't on NetBSD and
OpenBSD.
Diffstat (limited to 'nametoaddr.c')
-rw-r--r-- | nametoaddr.c | 236 |
1 files changed, 218 insertions, 18 deletions
diff --git a/nametoaddr.c b/nametoaddr.c index 17c45567..45743c71 100644 --- a/nametoaddr.c +++ b/nametoaddr.c @@ -61,7 +61,7 @@ #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> @@ -155,6 +155,7 @@ 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) @@ -180,7 +181,6 @@ pcap_nametoaddr(const char *name) return 0; } -#ifdef INET6 struct addrinfo * pcap_nametoaddrinfo(const char *name) { @@ -197,23 +197,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. */ bpf_u_int32 pcap_nametonetaddr(const char *name) { -#ifndef _WIN32 - struct netent *np; - - if ((np = getnetbyname(name)) != NULL) - return np->n_net; - else - return 0; -#else +#ifdef _WIN32 /* * There's no "getnetbyname()" on Windows. * @@ -227,7 +221,66 @@ pcap_nametonetaddr(const char *name) * of *UN*X* machines.) */ return 0; -#endif +#else + /* + * 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; + + 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 */ } /* @@ -238,20 +291,111 @@ 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) { + /* + * 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) { + /* + * 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; @@ -330,12 +474,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 @@ -508,6 +702,9 @@ pcap_ether_aton(const char *s) return (e); } +/* + * XXX - not thread-safe! + */ #ifndef HAVE_ETHER_HOSTTON /* Roll our own */ u_char * @@ -557,6 +754,9 @@ pcap_ether_hostton(const char *name) } #endif +/* + * XXX - not guaranteed to be thread-safe! + */ int __pcap_nametodnaddr(const char *name, u_short *res) { |