/* 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)->x_sockaddr_storage.ss_family) # define _get_addr_len(addr) ((addr)->x_sockaddr_storage.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)->x_sockaddr_in.sin_family) # define _get_addr_len(addr) ((addr)->x_sockaddr_in.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 = &self->addr.x_sockaddr_in.sin_addr; addrlen = 4; break; #if ASYNC_NETDB_USE_GAI case AF_INET6: raw_addr = &self->addr.x_sockaddr_in6.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: */