From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / utils / async_netdb.c
1 /* vi: set tw=78: */
2
3 /* async_netdb.c, Copyright (c) Dave Odell <dmo2118@gmail.com>
4  *
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
11  * implied warranty.
12  *
13  * Threaded versions of get(name/addr)info to replace calls to
14  * gethostby(name/addr), for Sonar.
15  */
16
17 #include "async_netdb.h"
18
19 #include "thread_util.h"
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <netdb.h>
24 #include <netinet/in.h>
25 #include <string.h>
26 #include <unistd.h>
27
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
31
32 #if ASYNC_NETDB_USE_GAI
33
34 # define _get_addr_family(addr) ((addr)->x_sockaddr_storage.ss_family)
35 # define _get_addr_len(addr) ((addr)->x_sockaddr_storage.ss_len)
36
37 static int _has_threads;
38
39 int _async_netdb_is_done (struct io_thread *io)
40 {
41   if (_has_threads >= 0)
42     return io_thread_is_done (io);
43   return 1;
44 }
45
46 #else /* ASYNC_NETDB_USE_GAI */
47
48 # define _get_addr_family(addr) ((addr)->x_sockaddr_in.sin_family)
49 # define _get_addr_len(addr) ((addr)->x_sockaddr_in.sin_len)
50
51 static const int _has_threads = -1;
52
53 # if ASYNC_NETDB_FAKE_EAI
54
55 const char *_async_netdb_strerror (int errcode)
56 {
57   /* (h)strerror should return messages in the user's preferred language. */
58
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
61      libsocket. */
62
63   switch (errcode)
64     {
65     case EAI_NONAME:
66       return hstrerror (HOST_NOT_FOUND);
67     case EAI_AGAIN:
68       return hstrerror (TRY_AGAIN);
69     case EAI_FAIL:
70       return hstrerror (NO_RECOVERY);
71     case EAI_MEMORY:
72       return strerror (ENOMEM);
73     }
74   /* case EAI_SYSTEM: default: */
75   return strerror (EINVAL); /* No good errno equivalent here. */
76 }
77
78 # endif /* ASYNC_NETDB_FAKE_EAI */
79
80 #endif /* !ASYNC_NETDB_USE_GAI */
81
82 static int _translate_h_errno (int error)
83 {
84   switch (error)
85     {
86     case HOST_NOT_FOUND:
87     case NO_DATA:
88       return EAI_NONAME;
89     case TRY_AGAIN:
90       return EAI_AGAIN;
91     }
92
93   /* case NO_RECOVERY: default: */
94   return EAI_FAIL;
95 }
96
97 /* async_name_from_addr - */
98
99 #if ASYNC_NETDB_USE_GAI
100
101 static void *
102 _async_name_from_addr_thread (void *self_raw)
103 {
104   /* The stack is unusually small here. If this crashes, you may need to bump
105      the value of ASYNC_NETDB_STACK. */
106
107   async_name_from_addr_t self = (async_name_from_addr_t)self_raw;
108
109   /* getnameinfo() is thread-safe, gethostbyaddr() is not. */
110   self->gai_error = getnameinfo ((void *)&self->param.addr,
111                                  self->param.addrlen,
112                                  self->host, sizeof(self->host), NULL, 0,
113                                  NI_NAMEREQD);
114   /* Removing NI_MAKEREQD would require a call to inet_ntoa in
115      async_name_from_addr_finish. */
116
117   self->errno_error = errno;
118
119   if (io_thread_return (&self->io))
120     thread_free(self);
121
122   return NULL;
123 }
124
125 #endif /* ASYNC_NETDB_USE_GAI */
126
127 static void
128 _async_name_from_addr_set_param (struct _async_name_from_addr_param *self,
129                                  const struct sockaddr *addr,
130                                  socklen_t addrlen)
131 {
132   self->addrlen = addrlen;
133   memcpy (&self->addr, addr, addrlen);
134
135 #if HAVE_STRUCT_SOCKADDR_SA_LEN
136   _get_addr_len (&self->addr) = addrlen; /* The BSDs need this. */
137 #endif
138 }
139
140 async_name_from_addr_t
141 async_name_from_addr_start (Display *dpy, const struct sockaddr *addr,
142                             socklen_t addrlen)
143 {
144   assert (addrlen);
145   assert (addrlen <= sizeof (async_netdb_sockaddr_storage_t));
146
147 #if ASYNC_NETDB_USE_GAI
148   _has_threads = threads_available (dpy);
149   if (_has_threads >= 0)
150     {
151       async_name_from_addr_t self, result;
152
153       if (thread_malloc ((void **)&self, dpy,
154                          sizeof (struct async_name_from_addr)))
155         return NULL;
156
157       _async_name_from_addr_set_param (&self->param, addr, addrlen);
158
159       result = io_thread_create (&self->io, self,
160                                  _async_name_from_addr_thread, dpy,
161                                  ASYNC_NETDB_STACK);
162       if (!result)
163         thread_free (self);
164       return result;
165     }
166 #endif /* ASYNC_NETDB_USE_GAI */
167
168   {
169     struct _async_name_from_addr_param *result =
170       (struct _async_name_from_addr_param *)
171       malloc (sizeof (struct _async_name_from_addr_param));
172
173     if (result)
174       _async_name_from_addr_set_param (result, addr, addrlen);
175
176     return (async_name_from_addr_t)result;
177   }
178 }
179
180 #if ASYNC_NETDB_USE_GAI
181 void
182 async_name_from_addr_cancel (async_name_from_addr_t self)
183 {
184   if (_has_threads >= 0)
185     {
186       if(io_thread_cancel (&self->io))
187         thread_free (self);
188     }
189   else
190     {
191       free (self);
192     }
193 }
194 #endif /* ASYNC_NETDB_USE_GAI */
195
196 int
197 async_name_from_addr_finish (async_name_from_addr_t self_raw,
198                              char **host, int *errno_error)
199 {
200 #if ASYNC_NETDB_USE_GAI
201   if (_has_threads >= 0)
202     {
203       async_name_from_addr_t self = self_raw;
204       int gai_error;
205
206       io_thread_finish (&self->io);
207
208       gai_error = self->gai_error;
209       if (gai_error)
210         {
211           if (errno_error)
212             *errno_error = self->errno_error;
213           *host = NULL; /* For safety's sake. */
214         }
215       else
216         {
217           *host = strdup(self->host);
218           if (!*host)
219             gai_error = EAI_MEMORY;
220         }
221
222       thread_free (self);
223       return gai_error;
224     }
225 #endif /* ASYNC_NETDB_USE_GAI */
226
227   {
228     struct _async_name_from_addr_param *self =
229       (struct _async_name_from_addr_param *)self_raw;
230
231     const struct hostent *he;
232     int error;
233     const void *raw_addr;
234     socklen_t addrlen;
235
236     switch (_get_addr_family (&self->addr))
237       {
238       case AF_INET:
239         raw_addr = &self->addr.x_sockaddr_in.sin_addr;
240         addrlen = 4;
241         break;
242 #if ASYNC_NETDB_USE_GAI
243       case AF_INET6:
244         raw_addr = &self->addr.x_sockaddr_in6.sin6_addr;
245         addrlen = 16;
246         break;
247 #endif /* ASYNC_NETDB_USE_GAI */
248       default:
249         return EAI_NONAME;
250       }
251
252     he = gethostbyaddr(raw_addr, addrlen, _get_addr_family (&self->addr));
253     error = h_errno;
254
255     free (self);
256
257     if (!he)
258     {
259       *host = NULL; /* For safety's sake. */
260       return _translate_h_errno(error);
261     }
262
263     if (!he->h_name)
264       return EAI_NONAME;
265
266     *host = strdup (he->h_name);
267     if (!*host)
268       return EAI_MEMORY;
269
270     return 0;
271   }
272 }
273
274 /* async_addr_from_name - */
275
276 static char *
277 _async_addr_from_name_hostname (async_addr_from_name_t self)
278 {
279   return (char *)(self + 1);
280 }
281
282 static void
283 _async_addr_from_name_free (async_addr_from_name_t self)
284 {
285 #if ASYNC_NETDB_USE_GAI
286   if (self->res) /* FreeBSD won't do freeaddrinfo (NULL). */
287     freeaddrinfo (self->res);
288 #endif
289   thread_free (self);
290 }
291
292 #if ASYNC_NETDB_USE_GAI
293
294 static void *
295 _async_addr_from_name_thread (void *self_raw)
296 {
297   /* The stack is unusually small here. If this crashes, you may need to bump
298      the value of ASYNC_NETDB_STACK. */
299
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,
302                                  NULL, &self->res);
303   self->errno_error = errno;
304
305   if (io_thread_return (&self->io))
306     _async_addr_from_name_free (self);
307
308   return NULL;
309 }
310
311 #endif /* ASYNC_NETDB_USE_GAI */
312
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)
316 {
317   async_addr_from_name_t self;
318   if (thread_malloc ((void **)&self, dpy,
319                      sizeof(struct async_addr_from_name) + strlen(hostname) + 1))
320     return NULL;
321
322   strcpy (_async_addr_from_name_hostname (self), hostname);
323
324 #if ASYNC_NETDB_USE_GAI
325   _has_threads = threads_available (dpy);
326   self->res = NULL;
327   if (_has_threads >= 0)
328     {
329       async_addr_from_name_t result =
330         io_thread_create (&self->io, self, _async_addr_from_name_thread, dpy,
331                           ASYNC_NETDB_STACK);
332
333       if (!result)
334         thread_free(result);
335       self = result;
336     }
337 #endif /* ASYNC_NETDB_USE_GAI */
338
339   return self;
340 }
341
342 #if ASYNC_NETDB_USE_GAI
343 void
344 async_addr_from_name_cancel (async_addr_from_name_t self)
345 {
346   if (_has_threads >= 0)
347     {
348       if (io_thread_cancel (&self->io))
349         _async_addr_from_name_free (self);
350     }
351   else
352     {
353       thread_free (self);
354     }
355 }
356 #endif /* ASYNC_NETDB_USE_GAI */
357
358 /* async_name_from_addr_finish does sockaddr_in or sockaddr_in6. */
359
360 int
361 async_addr_from_name_finish (async_addr_from_name_t self, void *addr,
362                              socklen_t *addrlen, int *errno_error)
363 {
364 #if ASYNC_NETDB_USE_GAI
365   if (_has_threads >= 0)
366     {
367       int gai_error;
368       io_thread_finish (&self->io);
369
370       gai_error = self->gai_error;
371       if (errno_error)
372         *errno_error = self->errno_error;
373
374       if (!gai_error)
375         {
376           struct addrinfo *ai = self->res;
377           if (!ai)
378             gai_error = EAI_NONAME;
379           else
380             {
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;
385             }
386         }
387
388       _async_addr_from_name_free (self);
389       return gai_error;
390     }
391 #endif /* ASYNC_NETDB_USE_GAI */
392
393   {
394     struct hostent *he =
395       gethostbyname (_async_addr_from_name_hostname (self));
396     int error = h_errno;
397     void *raw_addr;
398     async_netdb_sockaddr_storage_t *addr_storage =
399       (async_netdb_sockaddr_storage_t *)addr;
400
401     _async_addr_from_name_free (self);
402
403     if (!he)
404       return _translate_h_errno (error);
405
406     switch (he->h_addrtype)
407       {
408       case AF_INET:
409         {
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);
415         }
416         break;
417 #if ASYNC_NETDB_USE_GAI
418       case AF_INET6:
419         {
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);
426         }
427         break;
428 #endif /* ASYNC_NETDB_USE_GAI */
429       default:
430         return EAI_NONAME;
431       }
432
433 #if HAVE_STRUCT_SOCKADDR_SA_LEN
434     _get_addr_len (addr_storage) = *addrlen;
435 #endif
436     _get_addr_family (addr_storage) = he->h_addrtype;
437
438     memcpy (raw_addr, he->h_addr_list[0], he->h_length);
439     return 0;
440   }
441 }
442
443 /* Local Variables:      */
444 /* mode: c               */
445 /* fill-column: 78       */
446 /* c-file-style: "gnu"   */
447 /* c-basic-offset: 2     */
448 /* indent-tabs-mode: nil */
449 /* End:                  */