summaryrefslogtreecommitdiffstats
path: root/utils/async_netdb.h
blob: a72f4d599d3aef8962482d3168a47e9d6848b138 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/* vi: set tw=78: */

/* async_netdb.h, Copyright (c) Dave Odell <dmo2118@gmail.com>
 *
 * 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.
 */

#ifndef ASYNC_NETDB_H
#define ASYNC_NETDB_H

#include "thread_util.h"

#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

/*
   Both async_name_from_addr_* and async_addr_from_name_* follow basic pattern
   for io_thread clients as described in thread_util.h:
   1. async_*_from_*_start
   2. async_*_from_*_is_done (repeat as necessary)
   3a. async_*_from_*_finish to retrieve the results, or:
   3b. async_*_from_*_cancel to abort the lookup.

   On systems that can't do asynchronous host lookups, the *_finish functions
   do the actual lookup.
 */

#ifndef NI_MAXHOST
/*
   From the glibc man page for getnameinfo(3):
      Since glibc 2.8, these definitions are exposed only if one of the
      feature test macros _BSD_SOURCE, _SVID_SOURCE, or _GNU_SOURCE is
      defined.
 */
# define NI_MAXHOST 1025
#endif

#if HAVE_PTHREAD && HAVE_GETADDRINFO
/*
   If threads or getaddrinfo() are unavailable, then the older gethostbyname()
   and gethostbyaddr() functions are used, and IPv6 is disabled.
 */
# define ASYNC_NETDB_USE_GAI 1
#endif

#if ASYNC_NETDB_USE_GAI

/* Without using union, gcc-6 warns for
   breaking strict aliasing rules
 */
typedef union {
	struct sockaddr_storage x_sockaddr_storage;
	struct sockaddr_in x_sockaddr_in;
	struct sockaddr_in6 x_sockaddr_in6;
} async_netdb_sockaddr_storage_t;

int _async_netdb_is_done (struct io_thread *io);

#else

/* Because the definition for the above case is now union,
   the definition for this case must also be union...
*/
typedef union {
	struct sockaddr_in x_sockaddr_in;
} async_netdb_sockaddr_storage_t;

# ifndef EAI_SYSTEM
/* The EAI_* codes are specified specifically as preprocessor macros, so
   the #ifdef here should always work...
   http://pubs.opengroup.org/onlinepubs/009604499/basedefs/netdb.h.html */

#   define ASYNC_NETDB_FAKE_EAI 1

/* Even without addrinfo, the EAI_* error codes are used. The numbers are from
   Linux's netdb.h. */
#   define EAI_NONAME -2
#   define EAI_AGAIN  -3
#   define EAI_FAIL   -4
#   define EAI_MEMORY -10
#   define EAI_SYSTEM -11

const char *_async_netdb_strerror (int errcode);

#   define gai_strerror(errcode) _async_netdb_strerror (errcode)
# endif

# define _async_netdb_is_done(io) 1

#endif

/* In non-threaded mode, _async_name_from_addr_param is used in place of
   async_name_from_addr. */
struct _async_name_from_addr_param
{
  socklen_t addrlen;
  async_netdb_sockaddr_storage_t addr;
};

typedef struct async_name_from_addr
{
  /*
     Stupid memory trick, thwarted: The host string could be at the beginning
     of this structure, and the memory block that contains this struct could
     be resized and returned directly in async_name_from_addr_finish. But...

     There is no aligned_realloc. In fact, aligned_realloc is a bit of a
     problem, mostly because:
     1. realloc() is the only way to resize a heap-allocated memory block.
     2. realloc() moves memory.
     3. The location that realloc() moves memory to won't be aligned.
   */

  struct _async_name_from_addr_param param;
  struct io_thread io;

  char host[NI_MAXHOST];
  int gai_error;
  int errno_error;

} *async_name_from_addr_t;

async_name_from_addr_t async_name_from_addr_start (Display *dpy,
                                                   const struct sockaddr *addr,
                                                   socklen_t addrlen);
/*
   Starts an asynchronous name-from-address lookup.
   dpy:     An X11 Display with a .useThreads resource.
   addr:    An address. Like various socket functions (e.g. bind(2),
            connect(2), sendto(2)), this isn't really a struct sockaddr so
            much as a "subtype" of sockaddr, like sockaddr_in, or
            sockaddr_in6, or whatever.
   addrlen: The (actual) length of *addr.
   Returns NULL if the request couldn't be created (due to low memory).
 */

#define async_name_from_addr_is_done(self) _async_netdb_is_done (&(self)->io)

#if ASYNC_NETDB_USE_GAI
void async_name_from_addr_cancel (async_name_from_addr_t self);
#else
# define async_name_from_addr_cancel(self) (free (self))
#endif

int async_name_from_addr_finish (async_name_from_addr_t self,
                                 char **host, int *errno_error);
/*
   Gets the result of an asynchronous name-from-address lookup. If the lookup
   operation is still in progress, or if the system can't do async lookups,
   this will block. This cleans up the lookup operation; do not use 'self'
   after calling this function.
   self:        The lookup operation.
   host:        If no error, the name of the host. Free this with free(3).
   errno_error: The value of errno if EAI_SYSTEM is returned. Can be NULL.
   Returns 0 on success, otherwise an error from getnameinfo(3).
 */

/* In non-threaded mode, async_addr_from_name contains different stuff. */
typedef struct async_addr_from_name
{
#if ASYNC_NETDB_USE_GAI
  struct io_thread io;

  int gai_error;
  int errno_error;

  struct addrinfo *res;
#else
  char dont_complain_about_empty_structs;
#endif
} *async_addr_from_name_t;

async_addr_from_name_t async_addr_from_name_start (Display *dpy,
                                                   const char *host);
/*
   Starts an asynchronous address-from-name lookup.
   dpy:  An X11 display.
   host: The hostname to look up.
   Returns NULL if the request couldn't be created (due to low memory).
 */

#define async_addr_from_name_is_done(self) _async_netdb_is_done (&(self)->io)

#if ASYNC_NETDB_USE_GAI
void async_addr_from_name_cancel (async_addr_from_name_t self);
#else
# define async_addr_from_name_cancel(self) (thread_free (self));
#endif

/* sockaddr must be sizeof(async_netdb_sockaddr_storage_t) in size. */
int async_addr_from_name_finish (async_addr_from_name_t self, void *addr,
                                 socklen_t *addrlen, int *errno_error);
/*
   Returns the address from an asynchronous address-from-name operation. If
   the lookup is still in progress, or the system can't do an asynchronous
   lookup, this blocks. This cleans up the lookup operation; do not use 'self'
   after calling this function.
   self:        The lookup operation.
   addr:        A sockaddr. This must be as large as or larger than
                sizeof(async_netdb_sockaddr_storage_t). (Hint: just use an
                instance of async_netdb_sockaddr_storage_t itself here.)
   addrlen:     The length of the obtained sockaddr.
   errno_error: The value of errno if EAI_SYSTEM is returned. Can be NULL.
   Returns 0 on success, or an error from getaddrinfo(3).
 */

#endif

/* Local Variables:      */
/* mode: c               */
/* fill-column: 78       */
/* c-file-style: "gnu"   */
/* c-basic-offset: 2     */
/* indent-tabs-mode: nil */
/* End:                  */