diff --git a/include/netdb.h b/include/netdb.h index 0fa4d16c57f8..ba3ed32f39a9 100644 --- a/include/netdb.h +++ b/include/netdb.h @@ -82,7 +82,7 @@ typedef __socklen_t socklen_t; #define _PATH_PROTOCOLS "/etc/protocols" #define _PATH_SERVICES "/etc/services" -extern int h_errno; +#define h_errno (*__h_error()) /* * Structures returned by network data base library. All addresses are @@ -135,7 +135,7 @@ struct addrinfo { /* * Error return codes from gethostbyname() and gethostbyaddr() - * (left in extern int h_errno). + * (left in h_errno). */ #define NETDB_INTERNAL -1 /* see errno */ @@ -254,6 +254,7 @@ void setservent(int); */ /* DO NOT USE THESE, THEY ARE SUBJECT TO CHANGE AND ARE NOT PORTABLE!!! */ +int * __h_error(void); void _sethosthtent(int); void _endhosthtent(void); void _sethostdnsent(int); diff --git a/include/resolv.h b/include/resolv.h index 9305023dbb9a..942d2d6f8721 100644 --- a/include/resolv.h +++ b/include/resolv.h @@ -200,7 +200,12 @@ struct res_sym { char * humanname; /* Its fun name, like "mail exchanger" */ }; -extern struct __res_state _res; +__BEGIN_DECLS +extern struct __res_state *___res(void); +extern struct __res_state_ext *___res_ext(void); +__END_DECLS +#define _res (*___res()) +#define _res_ext (*___res_ext()) /* for INET6 */ extern struct __res_state_ext _res_ext; diff --git a/lib/libc/include/reentrant.h b/lib/libc/include/reentrant.h index 8ce3af67e304..8ab328bc6279 100644 --- a/lib/libc/include/reentrant.h +++ b/lib/libc/include/reentrant.h @@ -94,10 +94,12 @@ #define mutex_t pthread_mutex_t #define cond_t pthread_cond_t #define rwlock_t pthread_rwlock_t +#define once_t pthread_once_t #define thread_key_t pthread_key_t #define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER #define RWLOCK_INITIALIZER PTHREAD_RWLOCK_INITIALIZER +#define ONCE_INITIALIZER PTHREAD_ONCE_INIT #define mutex_init(m, a) _pthread_mutex_init(m, a) #define mutex_lock(m) if (__isthreaded) \ @@ -127,6 +129,7 @@ #define thr_getspecific(k) _pthread_getspecific(k) #define thr_sigsetmask(f, n, o) _pthread_sigmask(f, n, o) +#define thr_once(o, i) _pthread_once(o, i) #define thr_self() _pthread_self() #define thr_exit(x) _pthread_exit(x) #define thr_main() _pthread_main_np() diff --git a/lib/libc/net/getaddrinfo.3 b/lib/libc/net/getaddrinfo.3 index 36922dab8521..472a9183ac18 100644 --- a/lib/libc/net/getaddrinfo.3 +++ b/lib/libc/net/getaddrinfo.3 @@ -618,10 +618,14 @@ and documented in (RFC2553). .\" .Sh BUGS -Though the current implementation should be thread-safe, using +Although the current implementation is otherwise thread-safe, using .Fn getaddrinfo in conjunction with .Fn gethostby* -breaks thread-safeness. +(see +.Xr gethostbyname 3 ) +or +.Xr yp 8 +breaks the thread-safety of both. .Pp The text was shamelessly copied from RFC2553. diff --git a/lib/libc/net/getaddrinfo.c b/lib/libc/net/getaddrinfo.c index df05b0e53847..dc21cc231f04 100644 --- a/lib/libc/net/getaddrinfo.c +++ b/lib/libc/net/getaddrinfo.c @@ -305,10 +305,10 @@ static struct ai_errlist { }; /* - * XXX: Our res_*() is not thread-safe. So, we share lock between + * XXX: Many dependencies are not thread-safe. So, we share lock between * getaddrinfo() and getipnodeby*(). Still, we cannot use * getaddrinfo() and getipnodeby*() in conjunction with other - * functions which call res_*(). + * functions which call them. */ pthread_mutex_t __getaddrinfo_thread_lock = PTHREAD_MUTEX_INITIALIZER; #define THREAD_LOCK() \ @@ -1348,9 +1348,13 @@ get_port(ai, servname, matchonly) break; } - if ((sp = getservbyname(servname, proto)) == NULL) + THREAD_LOCK(); + if ((sp = getservbyname(servname, proto)) == NULL) { + THREAD_UNLOCK(); return EAI_SERVICE; + } port = sp->s_port; + THREAD_UNLOCK(); } if (!matchonly) { @@ -1501,15 +1505,11 @@ explore_fqdn(pai, hostname, servname, res) result = NULL; - THREAD_LOCK(); - /* * if the servname does not match socktype/protocol, ignore it. */ - if (get_portmatch(pai, servname) != 0) { - THREAD_UNLOCK(); + if (get_portmatch(pai, servname) != 0) return 0; - } switch (_nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo", default_dns_files, hostname, pai)) { @@ -1530,14 +1530,12 @@ explore_fqdn(pai, hostname, servname, res) } break; } - THREAD_UNLOCK(); *res = result; return 0; free: - THREAD_UNLOCK(); if (result) freeaddrinfo(result); return error; @@ -2037,6 +2035,7 @@ _files_getaddrinfo(rv, cb_data, ap) memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; + THREAD_LOCK(); _sethtent(); while ((p = _gethtent(name, pai)) != NULL) { cur->ai_next = p; @@ -2044,6 +2043,7 @@ _files_getaddrinfo(rv, cb_data, ap) cur = cur->ai_next; } _endhtent(); + THREAD_UNLOCK(); *((struct addrinfo **)rv) = sentinel.ai_next; if (sentinel.ai_next == NULL) @@ -2152,9 +2152,12 @@ _yp_getaddrinfo(rv, cb_data, ap) memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; + THREAD_LOCK(); if (!__ypdomain) { - if (_yp_check(&__ypdomain) == 0) + if (_yp_check(&__ypdomain) == 0) { + THREAD_UNLOCK(); return NS_UNAVAIL; + } } if (__ypcurrent) free(__ypcurrent); @@ -2189,6 +2192,7 @@ _yp_getaddrinfo(rv, cb_data, ap) cur = cur->ai_next; } } + THREAD_UNLOCK(); if (sentinel.ai_next == NULL) { h_errno = HOST_NOT_FOUND; @@ -2202,7 +2206,6 @@ _yp_getaddrinfo(rv, cb_data, ap) /* resolver logic */ extern const char *__hostalias(const char *); -extern int h_errno; /* * Formulate a normal query, send, and await answer. diff --git a/lib/libc/net/gethostbydns.c b/lib/libc/net/gethostbydns.c index 02cd223b2439..82f515214caf 100644 --- a/lib/libc/net/gethostbydns.c +++ b/lib/libc/net/gethostbydns.c @@ -116,7 +116,6 @@ typedef union { char ac; } align; -extern int h_errno; int _dns_ttl_; #ifdef DEBUG diff --git a/lib/libc/net/gethostbyname.3 b/lib/libc/net/gethostbyname.3 index e703e3221275..a4486bc6e9f8 100644 --- a/lib/libc/net/gethostbyname.3 +++ b/lib/libc/net/gethostbyname.3 @@ -369,6 +369,12 @@ version 4.9.4. These functions use static data storage; if the data is needed for future use, it should be copied before any subsequent calls overwrite it. +Threaded applications should never use them, as they will also conflict +with the +.Xr getaddrinfo 3 +and +.Xr getipnodebyname 3 +families of functions (which should be used instead). Only the Internet address format is currently understood. .Pp diff --git a/lib/libc/net/gethostbynis.c b/lib/libc/net/gethostbynis.c index 73b12edbc15b..88bd310b8dca 100644 --- a/lib/libc/net/gethostbynis.c +++ b/lib/libc/net/gethostbynis.c @@ -48,8 +48,6 @@ __FBSDID("$FreeBSD$"); #define MAXALIASES 35 #define MAXADDRS 35 -extern int h_errno; - #ifdef YP static char *host_aliases[MAXALIASES]; static char hostaddr[MAXADDRS]; diff --git a/lib/libc/net/getipnodebyname.3 b/lib/libc/net/getipnodebyname.3 index d0b0b8cffefe..1ade42d2cee8 100644 --- a/lib/libc/net/getipnodebyname.3 +++ b/lib/libc/net/getipnodebyname.3 @@ -455,6 +455,15 @@ are documented in (RFC2553). .\" .Sh BUGS +Although the current implementation is otherwise thread-safe, using +it in conjunction with +.Fn gethostby* +(see +.Xr gethostbyname 3 ) +or +.Xr yp 8 +breaks the thread-safety of both. +.Pp The .Fn getipnodebyname and @@ -469,6 +478,4 @@ and .Fn getnameinfo 3 are recommended. .Pp -The current implementation is not thread-safe. -.Pp The text was shamelessly copied from RFC2553. diff --git a/lib/libc/net/herror.c b/lib/libc/net/herror.c index 9f3dbb7d18f2..b84c06c1069f 100644 --- a/lib/libc/net/herror.c +++ b/lib/libc/net/herror.c @@ -69,9 +69,7 @@ const char *h_errlist[] = { "Unknown server error", /* 3 NO_RECOVERY */ "No address associated with name", /* 4 NO_ADDRESS */ }; -int h_nerr = { sizeof h_errlist / sizeof h_errlist[0] }; - -int h_errno; +const int h_nerr = { sizeof h_errlist / sizeof h_errlist[0] }; /* * herror -- @@ -110,3 +108,6 @@ hstrerror(err) return (h_errlist[err]); return ("Unknown resolver error"); } + +#undef h_errno +int h_errno; diff --git a/lib/libc/net/name6.c b/lib/libc/net/name6.c index c8abf9a4d76c..3d42e3c196bc 100644 --- a/lib/libc/net/name6.c +++ b/lib/libc/net/name6.c @@ -211,10 +211,10 @@ static int _icmp_ghbyaddr(void *, void *, va_list); #endif /* ICMPNL */ /* - * XXX: Our res_*() is not thread-safe. So, we share lock between + * XXX: Many dependencies are not thread-safe. So, we share lock between * getaddrinfo() and getipnodeby*(). Still, we cannot use * getaddrinfo() and getipnodeby*() in conjunction with other - * functions which call res_*(). + * functions which call them. */ #include "libc_private.h" extern pthread_mutex_t __getaddrinfo_thread_lock; @@ -309,10 +309,8 @@ _ghbyname(const char *name, int af, int flags, int *errp) } } - THREAD_LOCK(); rval = _nsdispatch(&hp, dtab, NSDB_HOSTS, "ghbyname", default_src, name, af, errp); - THREAD_UNLOCK(); return (rval == NS_SUCCESS) ? hp : NULL; } @@ -456,10 +454,8 @@ getipnodebyaddr(const void *src, size_t len, int af, int *errp) return NULL; } - THREAD_LOCK(); rval = _nsdispatch(&hp, dtab, NSDB_HOSTS, "ghbyaddr", default_src, src, len, af, errp); - THREAD_UNLOCK(); return (rval == NS_SUCCESS) ? hp : NULL; } @@ -1169,9 +1165,11 @@ _nis_ghbyname(void *rval, void *cb_data, va_list ap) if (af == AF_UNSPEC) af = AF_INET; if (af == AF_INET) { + THREAD_LOCK(); hp = _gethostbynisname(name, af); if (hp != NULL) hp = _hpcopy(hp, errp); + THREAD_UNLOCK(); } *(struct hostent **)rval = hp; @@ -1193,9 +1191,11 @@ _nis_ghbyaddr(void *rval, void *cb_data, va_list ap) af = va_arg(ap, int); if (af == AF_INET) { + THREAD_LOCK(); hp = _gethostbynisaddr(addr, addrlen, af); if (hp != NULL) hp = _hpcopy(hp, errp); + THREAD_UNLOCK(); } *(struct hostent **)rval = hp; return (hp != NULL) ? NS_SUCCESS : NS_NOTFOUND; @@ -1932,18 +1932,17 @@ _icmp_fqdn_query(const struct in6_addr *addr, int ifindex) struct timeval tout; int len; char *name; - static int pid; static struct _icmp_host_cache *hc_head; + THREAD_LOCK(); for (hc = hc_head; hc; hc = hc->hc_next) { if (hc->hc_ifindex == ifindex - && IN6_ARE_ADDR_EQUAL(&hc->hc_addr, addr)) - return hc->hc_name; + && IN6_ARE_ADDR_EQUAL(&hc->hc_addr, addr)) { + THREAD_UNLOCK(); + return hc->hc_name; /* XXX: never freed */ + } } - if (pid == 0) - pid = getpid(); - ICMP6_FILTER_SETBLOCKALL(&filter); ICMP6_FILTER_SETPASS(ICMP6_FQDN_REPLY, &filter); @@ -1955,7 +1954,7 @@ _icmp_fqdn_query(const struct in6_addr *addr, int ifindex) fq->icmp6_fqdn_type = ICMP6_FQDN_QUERY; fq->icmp6_fqdn_code = 0; fq->icmp6_fqdn_cksum = 0; - fq->icmp6_fqdn_id = (u_short)pid; + fq->icmp6_fqdn_id = (u_short)getpid(); fq->icmp6_fqdn_unused = 0; fq->icmp6_fqdn_cookie[0] = 0; fq->icmp6_fqdn_cookie[1] = 0; @@ -2038,8 +2037,10 @@ _icmp_fqdn_query(const struct in6_addr *addr, int ifindex) hc->hc_ifindex = ifindex; hc->hc_addr = *addr; hc->hc_name = strdup(name); + THREAD_LOCK(); hc->hc_next = hc_head; hc_head = hc; + THREAD_UNLOCK(); return hc->hc_name; } diff --git a/lib/libc/net/res_init.c b/lib/libc/net/res_init.c index 6c229730df77..1d48f606d156 100644 --- a/lib/libc/net/res_init.c +++ b/lib/libc/net/res_init.c @@ -91,7 +91,14 @@ __FBSDID("$FreeBSD$"); #include #include +#include "namespace.h" +#include "reentrant.h" +#include "un-namespace.h" #include "res_config.h" +#include "res_send_private.h" + +#undef h_errno +extern int h_errno; static void res_setoptions(char *, char *); @@ -106,16 +113,14 @@ static u_int32_t net_mask(struct in_addr); #endif /* - * Resolver state default settings. + * Check structure for failed per-thread allocations. */ - -struct __res_state _res -# if defined(__BIND_RES_TEXT) - = { RES_TIMEOUT, } /* Motorola, et al. */ -# endif - ; - -struct __res_state_ext _res_ext; +static struct res_per_thread { + struct __res_state res_state; + struct __res_state_ext res_state_ext; + struct __res_send_private res_send_private; + int h_errno; +} _res_per_thread_bogus = { .res_send_private = { .s = -1 } }; /* socket */ /* * Set up default settings. If the configuration file exist, the values @@ -142,6 +147,7 @@ int res_init() { FILE *fp; + struct __res_send_private *rsp; char *cp, **pp; int n; char buf[MAXDNAME]; @@ -156,6 +162,19 @@ res_init() int dots; #endif + /* + * If allocation of memory for this thread's resolver has failed, + * return the error to the user. + */ + if (&_res == &_res_per_thread_bogus.res_state) + return (-1); + rsp = ___res_send_private(); + rsp->s = -1; + rsp->connected = 0; + rsp->vc = 0; + rsp->af = 0; + rsp->Qhook = NULL; + rsp->Rhook = NULL; /* * These three fields used to be statically initialized. This made * it hard to use this code in a shared library. It is necessary, @@ -597,6 +616,97 @@ res_randomid() return (0xffff & (now.tv_sec ^ now.tv_usec ^ getpid())); } +/* + * Resolver state default settings. + */ + +#undef _res +#undef _res_ext +#ifdef __BIND_RES_TEXT +struct __res_state _res = { RES_TIMEOUT }; /* Motorola, et al. */ +#else +struct __res_state _res; +#endif +struct __res_state_ext _res_ext; +static struct __res_send_private _res_send_private = { .s = -1 }; /* socket */ + +static thread_key_t res_key; +static once_t res_init_once = ONCE_INITIALIZER; +static int res_thr_keycreated = 0; + +static void +free_res(void *ptr) +{ + struct res_per_thread *myrsp = ptr; + + if (myrsp->res_state.options & RES_INIT) + res_close(); + free(myrsp); +} + +static void +res_keycreate(void) +{ + res_thr_keycreated = thr_keycreate(&res_key, free_res) == 0; +} + +static struct res_per_thread * +allocate_res(void) +{ + struct res_per_thread *myrsp; + + if (thr_once(&res_init_once, res_keycreate) != 0 || + !res_thr_keycreated) + return (&_res_per_thread_bogus); + + myrsp = thr_getspecific(res_key); + if (myrsp != NULL) + return (myrsp); + myrsp = calloc(1, sizeof(*myrsp)); + if (myrsp == NULL) + return (&_res_per_thread_bogus); +#ifdef __BIND_RES_TEXT + myrsp->res_state.options = RES_TIMEOUT; /* Motorola, et al. */ +#endif + myrsp->res_send_private.s = -1; /* socket */ + if (thr_setspecific(res_key, myrsp) == 0) + return (myrsp); + free(myrsp); + return (&_res_per_thread_bogus); +} + +struct __res_state * +___res(void) +{ + if (thr_main() != 0) + return (&_res); + return (&allocate_res()->res_state); +} + +struct __res_state_ext * +___res_ext(void) +{ + if (thr_main() != 0) + return (&_res_ext); + return (&allocate_res()->res_state_ext); +} + +struct __res_send_private * +___res_send_private(void) +{ + if (thr_main() != 0) + return (&_res_send_private); + return (&allocate_res()->res_send_private); +} + +int * +__h_error(void) +{ + if (thr_main() != 0) + return (&h_errno); + return (&allocate_res()->h_errno); +} + /* * Weak aliases for applications that use certain private entry points, * and fail to include . diff --git a/lib/libc/net/res_send.c b/lib/libc/net/res_send.c index fea5f5423d45..d39bbc5a09fa 100644 --- a/lib/libc/net/res_send.c +++ b/lib/libc/net/res_send.c @@ -101,14 +101,15 @@ __FBSDID("$FreeBSD$"); #include "un-namespace.h" #include "res_config.h" +#include "res_send_private.h" -static int s = -1; /* socket used for communications */ -static int connected = 0; /* is the socket connected */ -static int vc = 0; /* is the socket a virtual circuit? */ -static int af = 0; /* address family of socket */ -static res_send_qhook Qhook = NULL; -static res_send_rhook Rhook = NULL; +#define s ___res_send_private()->s +#define connected ___res_send_private()->connected +#define vc ___res_send_private()->vc +#define af ___res_send_private()->af +#define Qhook ___res_send_private()->Qhook +#define Rhook ___res_send_private()->Rhook #define CAN_RECONNECT 1 @@ -123,8 +124,6 @@ static res_send_rhook Rhook = NULL; fprintf args;\ __fp_nquery(query, size, stdout);\ } else {} -static char abuf[NI_MAXHOST]; -static char pbuf[NI_MAXSERV]; static void Aerror(FILE *, char *, int, struct sockaddr *); static void Perror(FILE *, char *, int); @@ -138,6 +137,9 @@ static void Perror(FILE *, char *, int); int save = errno; if (_res.options & RES_DEBUG) { + char abuf[NI_MAXHOST]; + char pbuf[NI_MAXSERV]; + if (getnameinfo(address, address->sa_len, abuf, sizeof(abuf), pbuf, sizeof(pbuf), NI_NUMERICHOST|NI_NUMERICSERV|NI_WITHSCOPEID) != 0) { @@ -388,6 +390,7 @@ res_send(buf, buflen, ans, anssiz) */ for (try = 0; try < _res.retry; try++) { for (ns = 0; ns < _res.nscount; ns++) { + char abuf[NI_MAXHOST]; struct sockaddr *nsap = get_nsaddr(ns); socklen_t salen; diff --git a/lib/libc/net/res_send_private.h b/lib/libc/net/res_send_private.h new file mode 100644 index 000000000000..df96dda27608 --- /dev/null +++ b/lib/libc/net/res_send_private.h @@ -0,0 +1,82 @@ +/* $FreeBSD$ */ + +/* + * Copyright (c) 1985, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +struct __res_send_private { + int s; /* socket used for communications */ + int connected; /* is the socket connected */ + int vc; /* is the socket a virtual circuit? */ + int af; /* address family of socket */ + res_send_qhook Qhook; + res_send_rhook Rhook; +}; + +struct __res_send_private *___res_send_private(void); diff --git a/lib/libc/net/resolver.3 b/lib/libc/net/resolver.3 index 7c5e7bb61d32..68d80d8eeb8a 100644 --- a/lib/libc/net/resolver.3 +++ b/lib/libc/net/resolver.3 @@ -396,6 +396,17 @@ function puts a 32-bit quantity .Fa src to a buffer pointed to by .Fa dst . +.Sh RETURN VALUES +The +.Fn res_init +function will return 0 on success, or -1 in a threaded program if +per-thread storage could not be allocated. +.Sh IMPLEMENTATION NOTES +This implementation of the resolver is thread-safe, but it will not +function properly if the programmer attempts to declare his or her own +.Va _res +structure in an attempt to replace the per-thread version referred to +by that macro. .Sh FILES .Bl -tag -width /etc/resolv.conf .It Pa /etc/resolv.conf