chiark / gitweb /
Make ADNS wait for the event loop before collecting replies.
[mLib] / bres-adns.c
1 /* -*-c-*-
2  *
3  * $Id: bres-adns.c,v 1.3 2004/04/03 03:28:54 mdw Exp $
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 $
45  * Revision 1.3  2004/04/03 03:28:54  mdw
46  * Make ADNS wait for the event loop before collecting replies.
47  *
48  * Revision 1.2  2003/12/14 14:46:38  mdw
49  * Qualify name given to @bres_byname@.
50  *
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
89 static adns_state ads;
90 static sel_state *sel;
91 static 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
104 void 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
125 void 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
146 fail:
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
164 void 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,
172                        adns_qf_search | adns_qf_owner, rc, &rc->aq)) != 0)
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
181 fail:
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
199 void 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
219 static 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
259 static 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
266 static 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);
285         report(rc, a, a->rrs.addr, a->nrrs, &a->owner, 1);
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
333 void bres_init(sel_state *s)
334 {
335   int e;
336
337   if ((e = adns_init(&ads, adns_if_noautosys, 0)) != 0) {
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 -------------------------------------------------*/