3 /* async_netdb.c, Copyright (c) Dave Odell <dmo2118@gmail.com>
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation. No representations are made about the suitability of this
10 * software for any purpose. It is provided "as is" without express or
13 * Threaded versions of get(name/addr)info to replace calls to
14 * gethostby(name/addr), for Sonar.
17 #include "async_netdb.h"
19 #include "thread_util.h"
24 #include <netinet/in.h>
28 /* This is very much system-dependent, but hopefully 64K covers it just about
29 everywhere. The threads here shouldn't need much. */
30 #define ASYNC_NETDB_STACK 65536
32 #if ASYNC_NETDB_USE_GAI
34 # define _get_addr_family(addr) ((addr)->ss_family)
35 # define _get_addr_len(addr) ((addr)->ss_len)
37 static int _has_threads;
39 int _async_netdb_is_done (struct io_thread *io)
41 if (_has_threads >= 0)
42 return io_thread_is_done (io);
46 #else /* ASYNC_NETDB_USE_GAI */
48 # define _get_addr_family(addr) ((addr)->sin_family)
49 # define _get_addr_len(addr) ((addr)->sin_len)
51 static const int _has_threads = -1;
53 # if ASYNC_NETDB_FAKE_EAI
55 const char *_async_netdb_strerror (int errcode)
57 /* (h)strerror should return messages in the user's preferred language. */
59 /* On Solaris, gethostbyname(3) is in libnsl, but hstrerror(3) is in
60 libresolv. Not that it matters, since Solaris has real gai_strerror in
66 return hstrerror (HOST_NOT_FOUND);
68 return hstrerror (TRY_AGAIN);
70 return hstrerror (NO_RECOVERY);
72 return strerror (ENOMEM);
74 /* case EAI_SYSTEM: default: */
75 return strerror (EINVAL); /* No good errno equivalent here. */
78 # endif /* ASYNC_NETDB_FAKE_EAI */
80 #endif /* !ASYNC_NETDB_USE_GAI */
82 static int _translate_h_errno (int error)
93 /* case NO_RECOVERY: default: */
97 /* async_name_from_addr - */
99 #if ASYNC_NETDB_USE_GAI
102 _async_name_from_addr_thread (void *self_raw)
104 /* The stack is unusually small here. If this crashes, you may need to bump
105 the value of ASYNC_NETDB_STACK. */
107 async_name_from_addr_t self = (async_name_from_addr_t)self_raw;
109 /* getnameinfo() is thread-safe, gethostbyaddr() is not. */
110 self->gai_error = getnameinfo ((void *)&self->param.addr,
112 self->host, sizeof(self->host), NULL, 0,
114 /* Removing NI_MAKEREQD would require a call to inet_ntoa in
115 async_name_from_addr_finish. */
117 self->errno_error = errno;
119 if (io_thread_return (&self->io))
125 #endif /* ASYNC_NETDB_USE_GAI */
128 _async_name_from_addr_set_param (struct _async_name_from_addr_param *self,
129 const struct sockaddr *addr,
132 self->addrlen = addrlen;
133 memcpy (&self->addr, addr, addrlen);
135 #if HAVE_STRUCT_SOCKADDR_SA_LEN
136 _get_addr_len (&self->addr) = addrlen; /* The BSDs need this. */
140 async_name_from_addr_t
141 async_name_from_addr_start (Display *dpy, const struct sockaddr *addr,
145 assert (addrlen <= sizeof (async_netdb_sockaddr_storage_t));
147 #if ASYNC_NETDB_USE_GAI
148 _has_threads = threads_available (dpy);
149 if (_has_threads >= 0)
151 async_name_from_addr_t self, result;
153 if (thread_malloc ((void **)&self, dpy,
154 sizeof (struct async_name_from_addr)))
157 _async_name_from_addr_set_param (&self->param, addr, addrlen);
159 result = io_thread_create (&self->io, self,
160 _async_name_from_addr_thread, dpy,
166 #endif /* ASYNC_NETDB_USE_GAI */
169 struct _async_name_from_addr_param *result =
170 (struct _async_name_from_addr_param *)
171 malloc (sizeof (struct _async_name_from_addr_param));
174 _async_name_from_addr_set_param (result, addr, addrlen);
176 return (async_name_from_addr_t)result;
180 #if ASYNC_NETDB_USE_GAI
182 async_name_from_addr_cancel (async_name_from_addr_t self)
184 if (_has_threads >= 0)
186 if(io_thread_cancel (&self->io))
194 #endif /* ASYNC_NETDB_USE_GAI */
197 async_name_from_addr_finish (async_name_from_addr_t self_raw,
198 char **host, int *errno_error)
200 #if ASYNC_NETDB_USE_GAI
201 if (_has_threads >= 0)
203 async_name_from_addr_t self = self_raw;
206 io_thread_finish (&self->io);
208 gai_error = self->gai_error;
212 *errno_error = self->errno_error;
213 *host = NULL; /* For safety's sake. */
217 *host = strdup(self->host);
219 gai_error = EAI_MEMORY;
225 #endif /* ASYNC_NETDB_USE_GAI */
228 struct _async_name_from_addr_param *self =
229 (struct _async_name_from_addr_param *)self_raw;
231 const struct hostent *he;
233 const void *raw_addr;
236 switch (_get_addr_family (&self->addr))
239 raw_addr = &((const struct sockaddr_in *)&self->addr)->sin_addr;
242 #if ASYNC_NETDB_USE_GAI
244 raw_addr = &((const struct sockaddr_in6 *)&self->addr)->sin6_addr;
247 #endif /* ASYNC_NETDB_USE_GAI */
252 he = gethostbyaddr(raw_addr, addrlen, _get_addr_family (&self->addr));
259 *host = NULL; /* For safety's sake. */
260 return _translate_h_errno(error);
266 *host = strdup (he->h_name);
274 /* async_addr_from_name - */
277 _async_addr_from_name_hostname (async_addr_from_name_t self)
279 return (char *)(self + 1);
283 _async_addr_from_name_free (async_addr_from_name_t self)
285 #if ASYNC_NETDB_USE_GAI
286 if (self->res) /* FreeBSD won't do freeaddrinfo (NULL). */
287 freeaddrinfo (self->res);
292 #if ASYNC_NETDB_USE_GAI
295 _async_addr_from_name_thread (void *self_raw)
297 /* The stack is unusually small here. If this crashes, you may need to bump
298 the value of ASYNC_NETDB_STACK. */
300 async_addr_from_name_t self = (async_addr_from_name_t)self_raw;
301 self->gai_error = getaddrinfo (_async_addr_from_name_hostname (self), NULL,
303 self->errno_error = errno;
305 if (io_thread_return (&self->io))
306 _async_addr_from_name_free (self);
311 #endif /* ASYNC_NETDB_USE_GAI */
313 /* getaddrinfo supports more than a hostname, but gethostbyname does not. */
314 async_addr_from_name_t
315 async_addr_from_name_start (Display *dpy, const char *hostname)
317 async_addr_from_name_t self;
318 if (thread_malloc ((void **)&self, dpy,
319 sizeof(struct async_addr_from_name) + strlen(hostname) + 1))
322 strcpy (_async_addr_from_name_hostname (self), hostname);
324 #if ASYNC_NETDB_USE_GAI
325 _has_threads = threads_available (dpy);
327 if (_has_threads >= 0)
329 async_addr_from_name_t result =
330 io_thread_create (&self->io, self, _async_addr_from_name_thread, dpy,
337 #endif /* ASYNC_NETDB_USE_GAI */
342 #if ASYNC_NETDB_USE_GAI
344 async_addr_from_name_cancel (async_addr_from_name_t self)
346 if (_has_threads >= 0)
348 if (io_thread_cancel (&self->io))
349 _async_addr_from_name_free (self);
356 #endif /* ASYNC_NETDB_USE_GAI */
358 /* async_name_from_addr_finish does sockaddr_in or sockaddr_in6. */
361 async_addr_from_name_finish (async_addr_from_name_t self, void *addr,
362 socklen_t *addrlen, int *errno_error)
364 #if ASYNC_NETDB_USE_GAI
365 if (_has_threads >= 0)
368 io_thread_finish (&self->io);
370 gai_error = self->gai_error;
372 *errno_error = self->errno_error;
376 struct addrinfo *ai = self->res;
378 gai_error = EAI_NONAME;
381 assert (ai->ai_addrlen <=
382 sizeof (async_netdb_sockaddr_storage_t));
383 memcpy (addr, ai->ai_addr, ai->ai_addrlen);
384 *addrlen = ai->ai_addrlen;
388 _async_addr_from_name_free (self);
391 #endif /* ASYNC_NETDB_USE_GAI */
395 gethostbyname (_async_addr_from_name_hostname (self));
398 async_netdb_sockaddr_storage_t *addr_storage =
399 (async_netdb_sockaddr_storage_t *)addr;
401 _async_addr_from_name_free (self);
404 return _translate_h_errno (error);
406 switch (he->h_addrtype)
410 struct sockaddr_in *addr_in = (struct sockaddr_in *)addr;
411 addr_in->sin_port = 0;
412 raw_addr = &addr_in->sin_addr;
413 *addrlen = sizeof(*addr_in);
414 assert (he->h_length == 4);
417 #if ASYNC_NETDB_USE_GAI
420 struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr;
421 addr_in6->sin6_port = 0;
422 addr_in6->sin6_flowinfo = 0;
423 raw_addr = &addr_in6->sin6_addr;
424 *addrlen = sizeof(*addr_in6);
425 assert (he->h_length == 16);
428 #endif /* ASYNC_NETDB_USE_GAI */
433 #if HAVE_STRUCT_SOCKADDR_SA_LEN
434 _get_addr_len (addr_storage) = *addrlen;
436 _get_addr_family (addr_storage) = he->h_addrtype;
438 memcpy (raw_addr, he->h_addr_list[0], he->h_length);
443 /* Local Variables: */
445 /* fill-column: 78 */
446 /* c-file-style: "gnu" */
447 /* c-basic-offset: 2 */
448 /* indent-tabs-mode: nil */