chiark / gitweb /
Make ADNS wait for the event loop before collecting replies.
[mLib] / bres-adns.c
CommitLineData
14d7100d 1/* -*-c-*-
2 *
d46d1a2f 3 * $Id: bres-adns.c,v 1.3 2004/04/03 03:28:54 mdw Exp $
14d7100d 4 *
5 * Background reverse name resolution (ADNS version)
6 *
7 * (c) 2003 Straylight/Edgeware
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of the mLib utilities library.
13 *
14 * mLib is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
18 *
19 * mLib is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public
25 * License along with mLib; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27 * MA 02111-1307, USA.
28 *
29 *
30 * HOWEVER, since GNU adns is covered by the full GNU General Public
31 * License, this file (only) is also covered by the full GNU GPL, and
32 * you may NOT take advantage of the more liberal conditions of the
33 * LGPL when modifying or redistributing this file. This doesn't mean
34 * that a program which uses the interface provided by this file is
35 * covered by the GPL, since @bres.c@ provides the same interface and
36 * is LGPL. However, it does mean that if your program depends, for
37 * some reason (e.g., to meet particular performance criteria), on
38 * this adns-based implementation of mLib's background resolver then it
39 * must be licensed under the full GPL.
40 */
41
42/*----- Revision history --------------------------------------------------*
43 *
44 * $Log: bres-adns.c,v $
d46d1a2f 45 * Revision 1.3 2004/04/03 03:28:54 mdw
46 * Make ADNS wait for the event loop before collecting replies.
47 *
4f6d400b 48 * Revision 1.2 2003/12/14 14:46:38 mdw
49 * Qualify name given to @bres_byname@.
50 *
14d7100d 51 * Revision 1.1 2003/12/13 20:37:59 mdw
52 * Add adns support in background resolver.
53 *
54 */
55
56#ifndef HAVE_ADNS
57# error "You need the ADNS library to compile this file."
58#endif
59
60/*----- Header files ------------------------------------------------------*/
61
62#include <assert.h>
63#include <errno.h>
64#include <signal.h>
65#include <stdio.h>
66#include <stdlib.h>
67#include <string.h>
68
69#include <sys/types.h>
70#include <sys/time.h>
71#include <unistd.h>
72#include <sys/wait.h>
73
74#include <sys/socket.h>
75#include <netinet/in.h>
76#include <arpa/inet.h>
77#include <netdb.h>
78
79#include <adns.h>
80
81#include "alloc.h"
82#include "bres.h"
83#include "macros.h"
84#include "report.h"
85#include "sel.h"
86
87/*----- Static variables --------------------------------------------------*/
88
89static adns_state ads;
90static sel_state *sel;
91static sel_hook selhook;
92
93/*----- Main code ---------------------------------------------------------*/
94
95/* --- @bres_abort@ --- *
96 *
97 * Arguments: @bres_client *rc@ = pointer to client block
98 *
99 * Returns: ---
100 *
101 * Use: Removes a queued job.
102 */
103
104void bres_abort(bres_client *rc)
105{
106 if (rc->q == adns_r_addr) xfree(rc->u.name);
107 if (rc->a) free(rc->a);
108 adns_cancel(rc->aq);
109}
110
111/* --- @bres_byaddr@ --- *
112 *
113 * Arguments: @bres_client *rc@ = pointer to client block
114 * @struct in_addr addr@ = address to resolve
115 * @void (*func)(struct hostent *h, void *p)@ = handler function
116 * @void *p@ = argument for handler function
117 *
118 * Returns: ---
119 *
120 * Use: Adds an address lookup job to the queue. The job will be
121 * processed when there's a spare resolver process to deal with
122 * it.
123 */
124
125void bres_byaddr(bres_client *rc, struct in_addr addr,
126 void (*func)(struct hostent */*h*/, void */*p*/),
127 void *p)
128{
129 int e;
130 struct sockaddr_in sin;
131
132 if (!ads) goto fail;
133 sin.sin_family = AF_INET;
134 sin.sin_addr = addr;
135 sin.sin_port = 0;
136 if ((e = adns_submit_reverse(ads, (struct sockaddr *)&sin, adns_r_ptr,
137 0, rc, &rc->aq)) != 0)
138 goto fail;
139 rc->a = 0;
140 rc->q = adns_r_ptr;
141 rc->u.addr = addr;
142 rc->func = func;
143 rc->p = p;
144 return;
145
146fail:
147 func(0, p);
148}
149
150/* --- @bres_byname@ --- *
151 *
152 * Arguments: @bres_client *rc@ = pointer to client block
153 * @const char *name@ = name to resolve
154 * @void (*func)(struct hostent *h, void *p)@ = handler function
155 * @void *p@ = argument for handler function
156 *
157 * Returns: ---
158 *
159 * Use: Adds a name lookup job to the queue. The job will be
160 * processed when there's a spare resolver process to deal with
161 * it.
162 */
163
164void bres_byname(bres_client *rc, const char *name,
165 void (*func)(struct hostent */*h*/, void */*p*/),
166 void *p)
167{
168 int e;
169
170 if (!ads) goto fail;
171 if ((e = adns_submit(ads, name, adns_r_addr,
4f6d400b 172 adns_qf_search | adns_qf_owner, rc, &rc->aq)) != 0)
14d7100d 173 goto fail;
174 rc->a = 0;
175 rc->q = adns_r_addr;
176 rc->u.name = xstrdup(name);
177 rc->func = func;
178 rc->p = p;
179 return;
180
181fail:
182 func(0, p);
183}
184
185/* --- @bres_exec@ --- *
186 *
187 * Arguments: @const char *file@ = file containing server code or null
188 *
189 * Returns: ---
190 *
191 * Use: Makes `bres' use a standalone server rather than copies of
192 * the current process. This can reduce memory consumption for
193 * large processes, at the expense of startup time (which
194 * shouldn't be too bad anyway, because of the resolver design).
195 * If the filename is null, a default set up at install time is
196 * used. It's probably a good idea to leave it alone.
197 */
198
199void bres_exec(const char *file)
200{
201 /* Nothin' doin' */
202}
203
204/* --- @report@ --- *
205 *
206 * Arguments: @bres_client *c@ = client descriptor block
207 * @adns_answer *a@ = A-record answer from resolver library
208 * @adns_rr_addr *av@ = vector of address records
209 * @int an@ = number of address records (must be positive)
210 * @char **nv@ = vector of name strings
211 * @int nn@ = number of name strings (must be positive)
212 *
213 * Returns: ---
214 *
215 * Use: Formats the given answer into a @struct hostent@ and reports
216 * it to the waiting client application.
217 */
218
219static void report(bres_client *rc, adns_answer *a,
220 adns_rr_addr *av, int an,
221 char **nv, int nn)
222{
223 struct hostent h;
224 char *n[16];
225 char *aa[16];
226 int i, j;
227
228 j = 0;
229 if (a->cname) n[j++] = a->cname;
230 else { n[j++] = *nv; nv++; nn--; }
231 for (i = 0; i < nn && j < N(n) - 1; i++)
232 if (strcmp(n[0], nv[i]) != 0) n[j++] = nv[i];
233 n[j++] = 0;
234 for (i = j = 0; i < an && j < N(av) - 1; i++) {
235 if (av[i].addr.sa.sa_family == AF_INET)
236 aa[j++] = (char *)&av[i].addr.inet.sin_addr;
237 }
238 aa[j++] = 0;
239 h.h_name = n[0];
240 h.h_aliases = n + 1;
241 h.h_addrtype = AF_INET;
242 h.h_length = sizeof(struct in_addr);
243 h.h_addr_list = aa;
244 rc->func(&h, rc->p);
245}
246
247/* --- @beforehook@, @afterhook@ --- *
248 *
249 * Arguments: @sel_state *s@ = select state
250 * @sel_args *sa@ = argument block
251 * @void *p@ = uninteresting pointer
252 *
253 * Returns: ---
254 *
255 * Use: Processes the selector's arguments before @select@ is
256 * called, to allow ADNS to do its thing.
257 */
258
259static void beforehook(sel_state *s, sel_args *sa, void *p)
260{
261 adns_beforeselect(ads, &sa->maxfd,
262 &sa->fd[SEL_READ], &sa->fd[SEL_WRITE], &sa->fd[SEL_EXC],
263 &sa->tvp, &sa->tv, &sa->now);
264}
265
266static void afterhook(sel_state *s, sel_args *sa, void *p)
267{
268 void *c;
269 bres_client *rc;
270 adns_query q;
271 adns_answer *a;
272 int e;
273 int i;
274
275 adns_afterselect(ads, sa->maxfd,
276 &sa->fd[SEL_READ], &sa->fd[SEL_WRITE], &sa->fd[SEL_EXC],
277 &sa->now);
278 while (q = 0, (e = adns_check(ads, &q, &a, &c)) == 0) {
279 rc = c;
280 if (a->status != 0)
281 goto fail;
282 else switch (rc->q) {
283 case adns_r_addr:
284 assert(a->type == adns_r_addr);
4f6d400b 285 report(rc, a, a->rrs.addr, a->nrrs, &a->owner, 1);
14d7100d 286 free(rc->u.name);
287 free(a);
288 break;
289 case adns_r_ptr:
290 if (a->type == adns_r_ptr) {
291 rc->a = a;
292 if ((e = adns_submit(ads, a->rrs.str[0], adns_r_addr,
293 0, rc, &q)) != 0)
294 goto fail;
295 rc->aq = q;
296 } else {
297 assert(a->type == adns_r_addr);
298 for (i = 0; i < a->nrrs; i++) {
299 if (a->rrs.addr[i].addr.sa.sa_family == AF_INET &&
300 a->rrs.addr[i].addr.inet.sin_addr.s_addr ==
301 rc->u.addr.s_addr)
302 goto match;
303 }
304 goto fail;
305 match:
306 report(rc, a, &a->rrs.addr[i], 1, rc->a->rrs.str, rc->a->nrrs);
307 free(rc->a);
308 free(a);
309 }
310 break;
311 default:
312 abort();
313 }
314 continue;
315
316 fail:
317 rc->func(0, rc->p);
318 if (rc->q == adns_r_addr) xfree(rc->u.name);
319 if (rc->a) free(rc->a);
320 free(a);
321 }
322}
323
324/* --- @bres_init@ --- *
325 *
326 * Arguments: @sel_state *s@ = pointer to select multiplexor
327 *
328 * Returns: ---
329 *
330 * Use: Initializes the background resolver for use.
331 */
332
333void bres_init(sel_state *s)
334{
335 int e;
336
d46d1a2f 337 if ((e = adns_init(&ads, adns_if_noautosys, 0)) != 0) {
14d7100d 338 moan("adns_init failed: resolver won't work");
339 return;
340 }
341 sel_addhook(s, &selhook, beforehook, afterhook, 0);
342 sel = s;
343}
344
345/*----- That's all, folks -------------------------------------------------*/