summaryrefslogtreecommitdiffstats
path: root/lib/libc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc')
-rw-r--r--lib/libc/include/reentrant.h3
-rw-r--r--lib/libc/net/getaddrinfo.38
-rw-r--r--lib/libc/net/getaddrinfo.c27
-rw-r--r--lib/libc/net/gethostbydns.c1
-rw-r--r--lib/libc/net/gethostbyname.36
-rw-r--r--lib/libc/net/gethostbynis.c2
-rw-r--r--lib/libc/net/getipnodebyname.311
-rw-r--r--lib/libc/net/herror.c7
-rw-r--r--lib/libc/net/name6.c27
-rw-r--r--lib/libc/net/res_init.c128
-rw-r--r--lib/libc/net/res_send.c19
-rw-r--r--lib/libc/net/res_send_private.h82
-rw-r--r--lib/libc/net/resolver.311
13 files changed, 280 insertions, 52 deletions
diff --git a/lib/libc/include/reentrant.h b/lib/libc/include/reentrant.h
index 8ce3af6..8ab328b 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 36922da..472a918 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 df05b0e..dc21cc2 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 02cd223..82f5152 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 e703e32..a4486bc 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 73b12ed..88bd310 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 d0b0b8c..1ade42d 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 9f3dbb7..b84c06c 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 c8abf9a..3d42e3c 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 6c22973..1d48f60 100644
--- a/lib/libc/net/res_init.c
+++ b/lib/libc/net/res_init.c
@@ -91,7 +91,14 @@ __FBSDID("$FreeBSD$");
#include <unistd.h>
#include <netdb.h>
+#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];
@@ -157,6 +163,19 @@ res_init()
#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,
* now that we're doing dynamic initialization here, that we preserve
@@ -598,6 +617,97 @@ res_randomid()
}
/*
+ * 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 <resolv.h>.
*/
diff --git a/lib/libc/net/res_send.c b/lib/libc/net/res_send.c
index fea5f54..d39bbc5 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 0000000..df96dda
--- /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 7c5e7bb..68d80d8 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
OpenPOWER on IntegriCloud