3 * - overall query management (allocation, completion)
4 * - per-query memory management
5 * - query submission and cancellation (user-visible and internal)
8 * This file is part of adns, which is Copyright Ian Jackson
9 * and contributors (see the file INSTALL for full details).
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3, or (at your option)
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software Foundation.
35 static adns_query query_alloc(adns_state ads,
36 const typeinfo *typei, adns_rrtype type,
37 adns_queryflags flags, struct timeval now) {
38 /* Allocate a virgin query and return it. */
41 qu= malloc(sizeof(*qu)); if (!qu) return 0;
42 qu->answer= malloc(sizeof(*qu->answer));
43 if (!qu->answer) { free(qu); return 0; }
46 qu->state= query_tosend;
47 qu->back= qu->next= qu->parent= 0;
48 LIST_INIT(qu->children);
49 LINK_INIT(qu->siblings);
50 LIST_INIT(qu->allocations);
51 qu->interim_allocd= 0;
52 qu->preserved_allocd= 0;
53 qu->final_allocspace= 0;
58 adns__vbuf_init(&qu->vb);
61 qu->cname_dglen= qu->cname_begin= 0;
63 adns__vbuf_init(&qu->search_vb);
64 qu->search_origlen= qu->search_pos= qu->search_doneabs= 0;
66 qu->id= -2; /* will be overwritten with real id before we leave adns */
71 adns__timeout_clear(qu);
72 qu->expires= now.tv_sec + MAXTTLBELIEVE;
74 memset(&qu->ctx,0,sizeof(qu->ctx));
76 qu->answer->status= adns_s_ok;
77 qu->answer->cname= qu->answer->owner= 0;
78 qu->answer->type= type;
79 qu->answer->expires= -1;
81 qu->answer->rrs.untyped= 0;
82 qu->answer->rrsz= typei->getrrsz(typei,type);
87 static void query_submit(adns_state ads, adns_query qu,
88 const typeinfo *typei, vbuf *qumsg_vb, int id,
89 adns_queryflags flags, struct timeval now) {
90 /* Fills in the query message in for a previously-allocated query,
91 * and submits it. Cannot fail. Takes over the memory for qumsg_vb.
95 adns__vbuf_init(qumsg_vb);
97 qu->query_dgram= malloc(qu->vb.used);
98 if (!qu->query_dgram) { adns__query_fail(qu,adns_s_nomemory); return; }
101 qu->query_dglen= qu->vb.used;
102 memcpy(qu->query_dgram,qu->vb.buf,qu->vb.used);
104 typei->query_send(qu,now);
107 adns_status adns__ckl_hostname(adns_state ads, adns_queryflags flags,
108 union checklabel_state *cls,
109 qcontext *ctx, int labnum,
110 const char *dgram, int labstart, int lablen)
113 const char *label = dgram+labstart;
115 if (flags & adns_qf_quoteok_query) return adns_s_ok;
116 for (i=0; i<lablen; i++) {
119 if (!i) return adns_s_querydomaininvalid;
120 } else if (!ctype_alpha(c) && !ctype_digit(c)) {
121 return adns_s_querydomaininvalid;
127 static adns_status check_domain_name(adns_state ads, adns_queryflags flags,
128 qcontext *ctx, const typeinfo *typei,
129 const byte *dgram, int dglen)
133 int labnum= 0, labstart, lablen;
134 union checklabel_state cls;
136 adns__findlabel_start(&fls,ads, -1,0, dgram,dglen,dglen, DNS_HDRSIZE,0);
138 st= adns__findlabel_next(&fls, &lablen,&labstart);
139 assert(!st); assert(lablen >= 0);
140 st= typei->checklabel(ads,flags, &cls,ctx,
141 labnum++, dgram,labstart,lablen);
147 adns_status adns__internal_submit(adns_state ads, adns_query *query_r,
149 const typeinfo *typei, adns_rrtype type,
150 vbuf *qumsg_vb, int id,
151 adns_queryflags flags, struct timeval now,
156 st= check_domain_name(ads, flags,ctx,typei, qumsg_vb->buf,qumsg_vb->used);
158 qu= query_alloc(ads,typei,type,flags,now);
159 if (!qu) { st = adns_s_nomemory; goto x_err; }
163 LIST_LINK_TAIL_PART(parent->children,qu,siblings.);
164 memcpy(&qu->ctx,ctx,sizeof(qu->ctx));
165 query_submit(ads,qu, typei,qumsg_vb,id,flags,now);
170 adns__vbuf_free(qumsg_vb);
174 static void query_simple(adns_state ads, adns_query qu,
175 const char *owner, int ol,
176 const typeinfo *typei, adns_queryflags flags,
177 struct timeval now) {
182 st= adns__mkquery(ads,&qu->vb,&id, owner,ol,
183 typei,qu->answer->type, flags);
185 if (st == adns_s_querydomaintoolong && (flags & adns_qf_search)) {
186 adns__search_next(ads,qu,now);
189 adns__query_fail(qu,st);
194 st= check_domain_name(ads, flags,&qu->ctx,typei, qu->vb.buf,qu->vb.used);
195 if (st) { adns__query_fail(qu,st); return; }
198 adns__vbuf_init(&qu->vb);
199 query_submit(ads,qu, typei,&vb_new,id, flags,now);
202 void adns__search_next(adns_state ads, adns_query qu, struct timeval now) {
203 const char *nextentry;
206 if (qu->search_doneabs<0) {
208 qu->search_doneabs= 1;
210 if (qu->search_pos >= ads->nsearchlist) {
211 if (qu->search_doneabs) {
212 qu->search_vb.used= qu->search_origlen;
213 st= adns_s_nxdomain; goto x_fail;
216 qu->search_doneabs= 1;
219 nextentry= ads->searchlist[qu->search_pos++];
223 qu->search_vb.used= qu->search_origlen;
225 if (!adns__vbuf_append(&qu->search_vb,".",1) ||
226 !adns__vbuf_appendstr(&qu->search_vb,nextentry))
230 free(qu->query_dgram);
231 qu->query_dgram= 0; qu->query_dglen= 0;
233 query_simple(ads,qu, qu->search_vb.buf, qu->search_vb.used,
234 qu->typei, qu->flags, now);
240 adns__query_fail(qu,st);
243 static int save_owner(adns_query qu, const char *owner, int ol) {
244 /* Returns 1 if OK, otherwise there was no memory. */
247 if (!(qu->flags & adns_qf_owner)) return 1;
252 ans->owner= adns__alloc_preserved(qu,ol+1); if (!ans->owner) return 0;
254 memcpy(ans->owner,owner,ol);
259 int adns_submit(adns_state ads,
262 adns_queryflags flags,
264 adns_query *query_r) {
267 const typeinfo *typei;
272 adns__consistency(ads,0,cc_enter);
274 if (flags & ~(adns_queryflags)0x4009ffff)
275 /* 0x40080000 are reserved for `harmless' future expansion
276 * 0x00000020 used to be adns_qf_quoteok_cname, now the default;
277 * see also addrfam.c:textaddr_check_qf */
280 typei= adns__findtype(type);
281 if (!typei) return ENOSYS;
283 r= adns__gettimeofday(ads,&now); if (r) goto x_errno;
284 qu= query_alloc(ads,typei,type,flags,now); if (!qu) goto x_errno;
286 qu->ctx.ext= context;
288 memset(&qu->ctx.pinfo,0,sizeof(qu->ctx.pinfo));
289 memset(&qu->ctx.tinfo,0,sizeof(qu->ctx.tinfo));
294 if (!ol) { st= adns_s_querydomaininvalid; goto x_adnsfail; }
295 if (ol>DNS_MAXDOMAIN+1) { st= adns_s_querydomaintoolong; goto x_adnsfail; }
297 if (ol>=1 && owner[ol-1]=='.' && (ol<2 || owner[ol-2]!='\\')) {
298 flags &= ~adns_qf_search;
303 if (flags & adns_qf_search) {
304 r= adns__vbuf_append(&qu->search_vb,owner,ol);
305 if (!r) { st= adns_s_nomemory; goto x_adnsfail; }
307 for (ndots=0, p=owner; (p= strchr(p,'.')); p++, ndots++);
308 qu->search_doneabs= (ndots >= ads->searchndots) ? -1 : 0;
309 qu->search_origlen= ol;
310 adns__search_next(ads,qu,now);
312 if (flags & adns_qf_owner) {
313 if (!save_owner(qu,owner,ol)) { st= adns_s_nomemory; goto x_adnsfail; }
315 query_simple(ads,qu, owner,ol, typei,flags, now);
317 adns__autosys(ads,now);
318 adns__returning(ads,qu);
322 adns__query_fail(qu,st);
323 adns__returning(ads,qu);
329 adns__returning(ads,0);
333 int adns_submit_reverse_any(adns_state ads,
334 const struct sockaddr *addr,
337 adns_queryflags flags,
339 adns_query *query_r) {
340 char *buf, *buf_free = 0;
344 flags &= ~adns_qf_search;
347 r= adns__make_reverse_domain(addr,zone, &buf,sizeof(shortbuf),&buf_free);
349 r= adns_submit(ads,buf,type,flags,context,query_r);
354 int adns_submit_reverse(adns_state ads,
355 const struct sockaddr *addr,
357 adns_queryflags flags,
359 adns_query *query_r) {
360 if (((type^adns_r_ptr) & adns_rrt_reprmask) &&
361 ((type^adns_r_ptr_raw) & adns_rrt_reprmask))
363 return adns_submit_reverse_any(ads,addr,0,type,flags,context,query_r);
366 int adns_synchronous(adns_state ads,
369 adns_queryflags flags,
370 adns_answer **answer_r) {
374 r= adns_submit(ads,owner,type,flags,0,&qu);
377 r= adns_wait(ads,&qu,answer_r,0);
378 if (r) adns_cancel(qu);
383 static void *alloc_common(adns_query qu, size_t sz) {
386 if (!sz) return qu; /* Any old pointer will do */
387 assert(!qu->final_allocspace);
388 an= malloc(MEM_ROUND(MEM_ROUND(sizeof(*an)) + sz));
390 LIST_LINK_TAIL(qu->allocations,an);
392 return (byte*)an + MEM_ROUND(sizeof(*an));
395 void *adns__alloc_interim(adns_query qu, size_t sz) {
399 rv= alloc_common(qu,sz);
401 qu->interim_allocd += sz;
405 void *adns__alloc_preserved(adns_query qu, size_t sz) {
409 rv= adns__alloc_interim(qu,sz);
411 qu->preserved_allocd += sz;
415 static allocnode *alloc__info(adns_query qu, void *p, size_t *sz_r) {
418 if (!p || p == qu) { *sz_r= 0; return 0; }
419 an= (allocnode *)((byte *)p - MEM_ROUND(sizeof(allocnode)));
420 *sz_r= MEM_ROUND(an->sz);
424 void adns__free_interim(adns_query qu, void *p) {
426 allocnode *an= alloc__info(qu, p, &sz);
429 assert(!qu->final_allocspace);
430 qu->interim_allocd -= sz;
431 assert(qu->interim_allocd >= 0);
432 LIST_UNLINK(qu->allocations, an);
436 void *adns__alloc_mine(adns_query qu, size_t sz) {
437 return alloc_common(qu,MEM_ROUND(sz));
440 void adns__transfer_interim(adns_query from, adns_query to, void *block) {
442 allocnode *an= alloc__info(from, block, &sz);
446 assert(!to->final_allocspace);
447 assert(!from->final_allocspace);
449 LIST_UNLINK(from->allocations,an);
450 LIST_LINK_TAIL(to->allocations,an);
452 from->interim_allocd -= sz;
453 to->interim_allocd += sz;
455 if (to->expires > from->expires) to->expires= from->expires;
458 void *adns__alloc_final(adns_query qu, size_t sz) {
459 /* When we're in the _final stage, we _subtract_ from interim_alloc'd
460 * each allocation, and use final_allocspace to point to the next free
466 rp= qu->final_allocspace;
468 qu->interim_allocd -= sz;
469 assert(qu->interim_allocd>=0);
470 qu->final_allocspace= (byte*)rp + sz;
474 void adns__cancel_children(adns_query qu) {
475 adns_query cqu, ncqu;
477 for (cqu= qu->children.head; cqu; cqu= ncqu) {
478 ncqu= cqu->siblings.next;
483 void adns__reset_preserved(adns_query qu) {
484 assert(!qu->final_allocspace);
485 adns__cancel_children(qu);
487 qu->answer->rrs.untyped= 0;
488 qu->interim_allocd= qu->preserved_allocd;
491 static void free_query_allocs(adns_query qu) {
494 adns__cancel_children(qu);
495 for (an= qu->allocations.head; an; an= ann) { ann= an->next; free(an); }
496 LIST_INIT(qu->allocations);
497 adns__vbuf_free(&qu->vb);
498 adns__vbuf_free(&qu->search_vb);
499 free(qu->query_dgram);
503 void adns__intdone_process(adns_state ads) {
504 while (ads->intdone.head) {
505 adns_query iq= ads->intdone.head;
506 adns_query parent= iq->parent;
507 LIST_UNLINK_PART(parent->children,iq,siblings.);
508 LIST_UNLINK(iq->ads->childw,parent);
509 LIST_UNLINK(ads->intdone,iq);
510 iq->ctx.callback(parent,iq);
511 free_query_allocs(iq);
517 void adns__returning(adns_state ads, adns_query qu_for_caller) {
518 adns__intdone_process(ads);
519 adns__consistency(ads,qu_for_caller,cc_exit);
522 void adns__cancel(adns_query qu) {
526 adns__consistency(ads,qu,cc_freq);
527 if (qu->parent) LIST_UNLINK_PART(qu->parent->children,qu,siblings.);
530 LIST_UNLINK(ads->udpw,qu);
533 LIST_UNLINK(ads->tcpw,qu);
536 LIST_UNLINK(ads->childw,qu);
540 LIST_UNLINK(ads->intdone,qu);
542 LIST_UNLINK(ads->output,qu);
547 free_query_allocs(qu);
552 void adns_cancel(adns_query qu) {
557 adns__consistency(ads,qu,cc_enter);
559 adns__returning(ads,0);
562 void adns__update_expires(adns_query qu, unsigned long ttl,
563 struct timeval now) {
566 assert(ttl <= MAXTTLBELIEVE);
567 max= now.tv_sec + ttl;
568 if (qu->expires < max) return;
572 static void makefinal_query(adns_query qu) {
578 if (qu->interim_allocd) {
579 ans= realloc(qu->answer,
580 MEM_ROUND(MEM_ROUND(sizeof(*ans)) + qu->interim_allocd));
581 if (!ans) goto x_nomem;
585 qu->final_allocspace= (byte*)ans + MEM_ROUND(sizeof(*ans));
586 adns__makefinal_str(qu,&ans->cname);
587 adns__makefinal_str(qu,&ans->owner);
590 adns__makefinal_block(qu, &ans->rrs.untyped, ans->nrrs*ans->rrsz);
592 for (rrn=0; rrn<ans->nrrs; rrn++)
593 qu->typei->makefinal(qu, ans->rrs.bytes + rrn*ans->rrsz);
596 free_query_allocs(qu);
600 qu->preserved_allocd= 0;
601 qu->answer->cname= 0;
602 qu->answer->owner= 0;
603 adns__reset_preserved(qu); /* (but we just threw away the preserved stuff) */
605 qu->answer->status= adns_s_nomemory;
606 free_query_allocs(qu);
609 void adns__query_done(adns_query qu) {
610 adns_state ads=qu->ads;
613 adns__cancel_children(qu);
618 if (qu->flags & adns_qf_search && ans->status != adns_s_nomemory) {
619 if (!save_owner(qu, qu->search_vb.buf, qu->search_vb.used)) {
620 adns__query_fail(qu,adns_s_nomemory);
625 if (ans->nrrs && qu->typei->diff_needswap) {
626 if (!adns__vbuf_ensure(&qu->vb,qu->answer->rrsz)) {
627 adns__query_fail(qu,adns_s_nomemory);
630 adns__isort(ans->rrs.bytes, ans->nrrs, ans->rrsz,
632 (int(*)(void*, const void*, const void*))
633 qu->typei->diff_needswap,
636 if (ans->nrrs && qu->typei->postsort) {
637 qu->typei->postsort(qu->ads, ans->rrs.bytes,
638 ans->nrrs,ans->rrsz, qu->typei);
641 ans->expires= qu->expires;
642 qu->state= query_done;
644 LIST_LINK_TAIL(ads->intdone,qu);
647 LIST_LINK_TAIL(qu->ads->output,qu);
651 void adns__query_fail(adns_query qu, adns_status st) {
652 adns__reset_preserved(qu);
653 qu->answer->status= st;
654 adns__query_done(qu);
657 void adns__makefinal_str(adns_query qu, char **strp) {
659 char *before, *after;
664 after= adns__alloc_final(qu,l);
665 memcpy(after,before,l);
669 void adns__makefinal_block(adns_query qu, void **blpp, size_t sz) {
670 void *before, *after;
674 after= adns__alloc_final(qu,sz);
675 memcpy(after,before,sz);