X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=utils%2Fasync_netdb.c;fp=utils%2Fasync_netdb.c;h=5c52161799ecae9928131f6bea893655a0f21270;hp=0000000000000000000000000000000000000000;hb=d5186197bc394e10a4402f7f6d23fbb14103bc50;hpb=6afd6db0ae9396cd7ff897ade597cd5483f49b0e diff --git a/utils/async_netdb.c b/utils/async_netdb.c new file mode 100644 index 00000000..5c521617 --- /dev/null +++ b/utils/async_netdb.c @@ -0,0 +1,449 @@ +/* vi: set tw=78: */ + +/* async_netdb.c, Copyright (c) Dave Odell + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * Threaded versions of get(name/addr)info to replace calls to + * gethostby(name/addr), for Sonar. + */ + +#include "async_netdb.h" + +#include "thread_util.h" + +#include +#include +#include +#include +#include +#include + +/* This is very much system-dependent, but hopefully 64K covers it just about + everywhere. The threads here shouldn't need much. */ +#define ASYNC_NETDB_STACK 65536 + +#if ASYNC_NETDB_USE_GAI + +# define _get_addr_family(addr) ((addr)->ss_family) +# define _get_addr_len(addr) ((addr)->ss_len) + +static int _has_threads; + +int _async_netdb_is_done (struct io_thread *io) +{ + if (_has_threads >= 0) + return io_thread_is_done (io); + return 1; +} + +#else /* ASYNC_NETDB_USE_GAI */ + +# define _get_addr_family(addr) ((addr)->sin_family) +# define _get_addr_len(addr) ((addr)->sin_len) + +static const int _has_threads = -1; + +# if ASYNC_NETDB_FAKE_EAI + +const char *_async_netdb_strerror (int errcode) +{ + /* (h)strerror should return messages in the user's preferred language. */ + + /* On Solaris, gethostbyname(3) is in libnsl, but hstrerror(3) is in + libresolv. Not that it matters, since Solaris has real gai_strerror in + libsocket. */ + + switch (errcode) + { + case EAI_NONAME: + return hstrerror (HOST_NOT_FOUND); + case EAI_AGAIN: + return hstrerror (TRY_AGAIN); + case EAI_FAIL: + return hstrerror (NO_RECOVERY); + case EAI_MEMORY: + return strerror (ENOMEM); + } + /* case EAI_SYSTEM: default: */ + return strerror (EINVAL); /* No good errno equivalent here. */ +} + +# endif /* ASYNC_NETDB_FAKE_EAI */ + +#endif /* !ASYNC_NETDB_USE_GAI */ + +static int _translate_h_errno (int error) +{ + switch (error) + { + case HOST_NOT_FOUND: + case NO_DATA: + return EAI_NONAME; + case TRY_AGAIN: + return EAI_AGAIN; + } + + /* case NO_RECOVERY: default: */ + return EAI_FAIL; +} + +/* async_name_from_addr - */ + +#if ASYNC_NETDB_USE_GAI + +static void * +_async_name_from_addr_thread (void *self_raw) +{ + /* The stack is unusually small here. If this crashes, you may need to bump + the value of ASYNC_NETDB_STACK. */ + + async_name_from_addr_t self = (async_name_from_addr_t)self_raw; + + /* getnameinfo() is thread-safe, gethostbyaddr() is not. */ + self->gai_error = getnameinfo ((void *)&self->param.addr, + self->param.addrlen, + self->host, sizeof(self->host), NULL, 0, + NI_NAMEREQD); + /* Removing NI_MAKEREQD would require a call to inet_ntoa in + async_name_from_addr_finish. */ + + self->errno_error = errno; + + if (io_thread_return (&self->io)) + thread_free(self); + + return NULL; +} + +#endif /* ASYNC_NETDB_USE_GAI */ + +static void +_async_name_from_addr_set_param (struct _async_name_from_addr_param *self, + const struct sockaddr *addr, + socklen_t addrlen) +{ + self->addrlen = addrlen; + memcpy (&self->addr, addr, addrlen); + +#if HAVE_STRUCT_SOCKADDR_SA_LEN + _get_addr_len (&self->addr) = addrlen; /* The BSDs need this. */ +#endif +} + +async_name_from_addr_t +async_name_from_addr_start (Display *dpy, const struct sockaddr *addr, + socklen_t addrlen) +{ + assert (addrlen); + assert (addrlen <= sizeof (async_netdb_sockaddr_storage_t)); + +#if ASYNC_NETDB_USE_GAI + _has_threads = threads_available (dpy); + if (_has_threads >= 0) + { + async_name_from_addr_t self, result; + + if (thread_malloc ((void **)&self, dpy, + sizeof (struct async_name_from_addr))) + return NULL; + + _async_name_from_addr_set_param (&self->param, addr, addrlen); + + result = io_thread_create (&self->io, self, + _async_name_from_addr_thread, dpy, + ASYNC_NETDB_STACK); + if (!result) + thread_free (self); + return result; + } +#endif /* ASYNC_NETDB_USE_GAI */ + + { + struct _async_name_from_addr_param *result = + (struct _async_name_from_addr_param *) + malloc (sizeof (struct _async_name_from_addr_param)); + + if (result) + _async_name_from_addr_set_param (result, addr, addrlen); + + return (async_name_from_addr_t)result; + } +} + +#if ASYNC_NETDB_USE_GAI +void +async_name_from_addr_cancel (async_name_from_addr_t self) +{ + if (_has_threads >= 0) + { + if(io_thread_cancel (&self->io)) + thread_free (self); + } + else + { + free (self); + } +} +#endif /* ASYNC_NETDB_USE_GAI */ + +int +async_name_from_addr_finish (async_name_from_addr_t self_raw, + char **host, int *errno_error) +{ +#if ASYNC_NETDB_USE_GAI + if (_has_threads >= 0) + { + async_name_from_addr_t self = self_raw; + int gai_error; + + io_thread_finish (&self->io); + + gai_error = self->gai_error; + if (gai_error) + { + if (errno_error) + *errno_error = self->errno_error; + *host = NULL; /* For safety's sake. */ + } + else + { + *host = strdup(self->host); + if (!*host) + gai_error = EAI_MEMORY; + } + + thread_free (self); + return gai_error; + } +#endif /* ASYNC_NETDB_USE_GAI */ + + { + struct _async_name_from_addr_param *self = + (struct _async_name_from_addr_param *)self_raw; + + const struct hostent *he; + int error; + const void *raw_addr; + socklen_t addrlen; + + switch (_get_addr_family (&self->addr)) + { + case AF_INET: + raw_addr = &((const struct sockaddr_in *)&self->addr)->sin_addr; + addrlen = 4; + break; +#if ASYNC_NETDB_USE_GAI + case AF_INET6: + raw_addr = &((const struct sockaddr_in6 *)&self->addr)->sin6_addr; + addrlen = 16; + break; +#endif /* ASYNC_NETDB_USE_GAI */ + default: + return EAI_NONAME; + } + + he = gethostbyaddr(raw_addr, addrlen, _get_addr_family (&self->addr)); + error = h_errno; + + free (self); + + if (!he) + { + *host = NULL; /* For safety's sake. */ + return _translate_h_errno(error); + } + + if (!he->h_name) + return EAI_NONAME; + + *host = strdup (he->h_name); + if (!*host) + return EAI_MEMORY; + + return 0; + } +} + +/* async_addr_from_name - */ + +static char * +_async_addr_from_name_hostname (async_addr_from_name_t self) +{ + return (char *)(self + 1); +} + +static void +_async_addr_from_name_free (async_addr_from_name_t self) +{ +#if ASYNC_NETDB_USE_GAI + if (self->res) /* FreeBSD won't do freeaddrinfo (NULL). */ + freeaddrinfo (self->res); +#endif + thread_free (self); +} + +#if ASYNC_NETDB_USE_GAI + +static void * +_async_addr_from_name_thread (void *self_raw) +{ + /* The stack is unusually small here. If this crashes, you may need to bump + the value of ASYNC_NETDB_STACK. */ + + async_addr_from_name_t self = (async_addr_from_name_t)self_raw; + self->gai_error = getaddrinfo (_async_addr_from_name_hostname (self), NULL, + NULL, &self->res); + self->errno_error = errno; + + if (io_thread_return (&self->io)) + _async_addr_from_name_free (self); + + return NULL; +} + +#endif /* ASYNC_NETDB_USE_GAI */ + +/* getaddrinfo supports more than a hostname, but gethostbyname does not. */ +async_addr_from_name_t +async_addr_from_name_start (Display *dpy, const char *hostname) +{ + async_addr_from_name_t self; + if (thread_malloc ((void **)&self, dpy, + sizeof(struct async_addr_from_name) + strlen(hostname) + 1)) + return NULL; + + strcpy (_async_addr_from_name_hostname (self), hostname); + +#if ASYNC_NETDB_USE_GAI + _has_threads = threads_available (dpy); + self->res = NULL; + if (_has_threads >= 0) + { + async_addr_from_name_t result = + io_thread_create (&self->io, self, _async_addr_from_name_thread, dpy, + ASYNC_NETDB_STACK); + + if (!result) + thread_free(result); + self = result; + } +#endif /* ASYNC_NETDB_USE_GAI */ + + return self; +} + +#if ASYNC_NETDB_USE_GAI +void +async_addr_from_name_cancel (async_addr_from_name_t self) +{ + if (_has_threads >= 0) + { + if (io_thread_cancel (&self->io)) + _async_addr_from_name_free (self); + } + else + { + thread_free (self); + } +} +#endif /* ASYNC_NETDB_USE_GAI */ + +/* async_name_from_addr_finish does sockaddr_in or sockaddr_in6. */ + +int +async_addr_from_name_finish (async_addr_from_name_t self, void *addr, + socklen_t *addrlen, int *errno_error) +{ +#if ASYNC_NETDB_USE_GAI + if (_has_threads >= 0) + { + int gai_error; + io_thread_finish (&self->io); + + gai_error = self->gai_error; + if (errno_error) + *errno_error = self->errno_error; + + if (!gai_error) + { + struct addrinfo *ai = self->res; + if (!ai) + gai_error = EAI_NONAME; + else + { + assert (ai->ai_addrlen <= + sizeof (async_netdb_sockaddr_storage_t)); + memcpy (addr, ai->ai_addr, ai->ai_addrlen); + *addrlen = ai->ai_addrlen; + } + } + + _async_addr_from_name_free (self); + return gai_error; + } +#endif /* ASYNC_NETDB_USE_GAI */ + + { + struct hostent *he = + gethostbyname (_async_addr_from_name_hostname (self)); + int error = h_errno; + void *raw_addr; + async_netdb_sockaddr_storage_t *addr_storage = + (async_netdb_sockaddr_storage_t *)addr; + + _async_addr_from_name_free (self); + + if (!he) + return _translate_h_errno (error); + + switch (he->h_addrtype) + { + case AF_INET: + { + struct sockaddr_in *addr_in = (struct sockaddr_in *)addr; + addr_in->sin_port = 0; + raw_addr = &addr_in->sin_addr; + *addrlen = sizeof(*addr_in); + assert (he->h_length == 4); + } + break; +#if ASYNC_NETDB_USE_GAI + case AF_INET6: + { + struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr; + addr_in6->sin6_port = 0; + addr_in6->sin6_flowinfo = 0; + raw_addr = &addr_in6->sin6_addr; + *addrlen = sizeof(*addr_in6); + assert (he->h_length == 16); + } + break; +#endif /* ASYNC_NETDB_USE_GAI */ + default: + return EAI_NONAME; + } + +#if HAVE_STRUCT_SOCKADDR_SA_LEN + _get_addr_len (addr_storage) = *addrlen; +#endif + _get_addr_family (addr_storage) = he->h_addrtype; + + memcpy (raw_addr, he->h_addr_list[0], he->h_length); + return 0; + } +} + +/* Local Variables: */ +/* mode: c */ +/* fill-column: 78 */ +/* c-file-style: "gnu" */ +/* c-basic-offset: 2 */ +/* indent-tabs-mode: nil */ +/* End: */