xref: /reactos/sdk/lib/3rdparty/adns/src/query.c (revision c2c66aff)
1 /*
2  * query.c
3  * - overall query management (allocation, completion)
4  * - per-query memory management
5  * - query submission and cancellation (user-visible and internal)
6  */
7 /*
8  *  This file is
9  *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
10  *
11  *  It is part of adns, which is
12  *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
13  *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
14  *
15  *  This program is free software; you can redistribute it and/or modify
16  *  it under the terms of the GNU General Public License as published by
17  *  the Free Software Foundation; either version 2, or (at your option)
18  *  any later version.
19  *
20  *  This program is distributed in the hope that it will be useful,
21  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  *  GNU General Public License for more details.
24  *
25  *  You should have received a copy of the GNU General Public License
26  *  along with this program; if not, write to the Free Software Foundation,
27  *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28  */
29 
30 #ifdef ADNS_JGAA_WIN32
31 # include "adns_win32.h"
32 #else
33 # include <stdlib.h>
34 # include <unistd.h>
35 # include <errno.h>
36 # include <sys/time.h>
37 #endif
38 
39 #include "internal.h"
40 
query_alloc(adns_state ads,const typeinfo * typei,adns_queryflags flags,struct timeval now)41 static adns_query query_alloc(adns_state ads, const typeinfo *typei,
42 			      adns_queryflags flags, struct timeval now) {
43   /* Allocate a virgin query and return it. */
44   adns_query qu;
45 
46   qu= malloc(sizeof(*qu));  if (!qu) return 0;
47   qu->answer= malloc(sizeof(*qu->answer));  if (!qu->answer) { free(qu); return 0; }
48 
49   qu->ads= ads;
50   qu->state= query_tosend;
51   qu->back= qu->next= qu->parent= 0;
52   LIST_INIT(qu->children);
53   LINK_INIT(qu->siblings);
54   LIST_INIT(qu->allocations);
55   qu->interim_allocd= 0;
56   qu->preserved_allocd= 0;
57   qu->final_allocspace= 0;
58 
59   qu->typei= typei;
60   qu->query_dgram= 0;
61   qu->query_dglen= 0;
62   adns__vbuf_init(&qu->vb);
63 
64   qu->cname_dgram= 0;
65   qu->cname_dglen= qu->cname_begin= 0;
66 
67   adns__vbuf_init(&qu->search_vb);
68   qu->search_origlen= qu->search_pos= qu->search_doneabs= 0;
69 
70   qu->id= -2; /* will be overwritten with real id before we leave adns */
71   qu->flags= flags;
72   qu->retries= 0;
73   qu->udpnextserver= 0;
74   qu->udpsent= 0;
75   timerclear(&qu->timeout);
76   qu->expires= now.tv_sec + MAXTTLBELIEVE;
77 
78   memset(&qu->ctx,0,sizeof(qu->ctx));
79 
80   qu->answer->status= adns_s_ok;
81   qu->answer->cname= qu->answer->owner= 0;
82   qu->answer->type= typei->type;
83   qu->answer->expires= -1;
84   qu->answer->nrrs= 0;
85   qu->answer->rrs.untyped= 0;
86   qu->answer->rrsz= typei->rrsz;
87 
88   return qu;
89 }
90 
query_submit(adns_state ads,adns_query qu,const typeinfo * typei,vbuf * qumsg_vb,int id,adns_queryflags flags,struct timeval now)91 static void query_submit(adns_state ads, adns_query qu,
92 			 const typeinfo *typei, vbuf *qumsg_vb, int id,
93 			 adns_queryflags flags, struct timeval now) {
94   /* Fills in the query message in for a previously-allocated query,
95    * and submits it.  Cannot fail.  Takes over the memory for qumsg_vb.
96    */
97 
98   qu->vb= *qumsg_vb;
99   adns__vbuf_init(qumsg_vb);
100 
101   qu->query_dgram= malloc( (size_t) qu->vb.used);
102   if (!qu->query_dgram) { adns__query_fail(qu,adns_s_nomemory); return; }
103 
104   qu->id= id;
105   qu->query_dglen= qu->vb.used;
106   memcpy(qu->query_dgram,qu->vb.buf,(size_t) qu->vb.used);
107 
108   adns__query_send(qu,now);
109 }
110 
adns__internal_submit(adns_state ads,adns_query * query_r,const typeinfo * typei,vbuf * qumsg_vb,int id,adns_queryflags flags,struct timeval now,const qcontext * ctx)111 adns_status adns__internal_submit(adns_state ads, adns_query *query_r,
112 				  const typeinfo *typei, vbuf *qumsg_vb, int id,
113 				  adns_queryflags flags, struct timeval now,
114 				  const qcontext *ctx) {
115   adns_query qu;
116 
117   qu= query_alloc(ads,typei,flags,now);
118   if (!qu) { adns__vbuf_free(qumsg_vb); return adns_s_nomemory; }
119   *query_r= qu;
120 
121   memcpy(&qu->ctx,ctx,(size_t) sizeof(qu->ctx));
122   query_submit(ads,qu, typei,qumsg_vb,id,flags,now);
123 
124   return adns_s_ok;
125 }
126 
query_simple(adns_state ads,adns_query qu,const char * owner,int ol,const typeinfo * typei,adns_queryflags flags,struct timeval now)127 static void query_simple(adns_state ads, adns_query qu,
128 			 const char *owner, int ol,
129 			 const typeinfo *typei, adns_queryflags flags,
130 			 struct timeval now) {
131   vbuf vb_new;
132   int id;
133   adns_status stat;
134 
135   stat= adns__mkquery(ads,&qu->vb,&id, owner,ol, typei,flags);
136   if (stat) {
137     if (stat == adns_s_querydomaintoolong && (flags & adns_qf_search)) {
138       adns__search_next(ads,qu,now);
139       return;
140     } else {
141       adns__query_fail(qu,stat);
142       return;
143     }
144   }
145 
146   vb_new= qu->vb;
147   adns__vbuf_init(&qu->vb);
148   query_submit(ads,qu, typei,&vb_new,id, flags,now);
149 }
150 
adns__search_next(adns_state ads,adns_query qu,struct timeval now)151 void adns__search_next(adns_state ads, adns_query qu, struct timeval now) {
152   const char *nextentry;
153   adns_status stat;
154 
155   if (qu->search_doneabs<0) {
156     nextentry= 0;
157     qu->search_doneabs= 1;
158   } else {
159     if (qu->search_pos >= ads->nsearchlist) {
160       if (qu->search_doneabs) {
161 	stat= adns_s_nxdomain; goto x_fail;
162 	return;
163       } else {
164 	nextentry= 0;
165 	qu->search_doneabs= 1;
166       }
167     } else {
168       nextentry= ads->searchlist[qu->search_pos++];
169     }
170   }
171 
172   qu->search_vb.used= qu->search_origlen;
173   if (nextentry) {
174     if (!adns__vbuf_append(&qu->search_vb,(byte*)".",1) ||
175 	!adns__vbuf_appendstr(&qu->search_vb,nextentry)) {
176       stat= adns_s_nomemory; goto x_fail;
177     }
178   }
179 
180   free(qu->query_dgram);
181   qu->query_dgram= 0; qu->query_dglen= 0;
182 
183   query_simple(ads,qu, (char*)qu->search_vb.buf, qu->search_vb.used, qu->typei, qu->flags, now);
184   return;
185 
186 x_fail:
187   adns__query_fail(qu,stat);
188 }
189 
save_owner(adns_query qu,const char * owner,int ol)190 static int save_owner(adns_query qu, const char *owner, int ol) {
191   /* Returns 1 if OK, otherwise there was no memory. */
192   adns_answer *ans;
193 
194   ans= qu->answer;
195   assert(!ans->owner);
196 
197   ans->owner= adns__alloc_preserved(qu, (size_t) ol+1);  if (!ans->owner) return 0;
198 
199   memcpy(ans->owner,owner, (size_t) ol);
200   ans->owner[ol]= 0;
201   return 1;
202 }
203 
adns_submit(adns_state ads,const char * owner,adns_rrtype type,adns_queryflags flags,void * context,adns_query * query_r)204 int adns_submit(adns_state ads,
205 		const char *owner,
206 		adns_rrtype type,
207 		adns_queryflags flags,
208 		void *context,
209 		adns_query *query_r) {
210   int r, ol, ndots;
211   adns_status stat;
212   const typeinfo *typei;
213   struct timeval now;
214   adns_query qu;
215   const char *p;
216 
217   adns__consistency(ads,0,cc_entex);
218 
219   typei= adns__findtype(type);
220   if (!typei) return ENOSYS;
221 
222   r= gettimeofday(&now,0); if (r) goto x_errno;
223   qu= query_alloc(ads,typei,flags,now); if (!qu) goto x_errno;
224 
225   qu->ctx.ext= context;
226   qu->ctx.callback= 0;
227   memset(&qu->ctx.info,0,sizeof(qu->ctx.info));
228 
229   *query_r= qu;
230 
231   ol= strlen(owner);
232   if (!ol) { stat= adns_s_querydomaininvalid; goto x_adnsfail; }
233   if (ol>DNS_MAXDOMAIN+1) { stat= adns_s_querydomaintoolong; goto x_adnsfail; }
234 
235   if (ol>=1 && owner[ol-1]=='.' && (ol<2 || owner[ol-2]!='\\')) {
236     flags &= ~adns_qf_search;
237     qu->flags= flags;
238     ol--;
239   }
240 
241   if (flags & adns_qf_search) {
242     r= adns__vbuf_append(&qu->search_vb,(byte*)owner,ol);
243     if (!r) { stat= adns_s_nomemory; goto x_adnsfail; }
244 
245     for (ndots=0, p=owner; (p= strchr(p,'.')); p++, ndots++);
246     qu->search_doneabs= (ndots >= ads->searchndots) ? -1 : 0;
247     qu->search_origlen= ol;
248     adns__search_next(ads,qu,now);
249   } else {
250     if (flags & adns_qf_owner) {
251       if (!save_owner(qu,owner,ol)) { stat= adns_s_nomemory; goto x_adnsfail; }
252     }
253     query_simple(ads,qu, owner,ol, typei,flags, now);
254   }
255   adns__autosys(ads,now);
256   adns__consistency(ads,qu,cc_entex);
257   return 0;
258 
259  x_adnsfail:
260   adns__query_fail(qu,stat);
261   adns__consistency(ads,qu,cc_entex);
262   return 0;
263 
264  x_errno:
265   r= errno;
266   assert(r);
267   adns__consistency(ads,0,cc_entex);
268   return r;
269 }
270 
adns_submit_reverse_any(adns_state ads,const struct sockaddr * addr,const char * zone,adns_rrtype type,adns_queryflags flags,void * context,adns_query * query_r)271 int adns_submit_reverse_any(adns_state ads,
272 			    const struct sockaddr *addr,
273 			    const char *zone,
274 			    adns_rrtype type,
275 			    adns_queryflags flags,
276 			    void *context,
277 			    adns_query *query_r) {
278   const unsigned char *iaddr;
279   char *buf, *buf_free;
280   char shortbuf[100];
281   int r, lreq;
282 
283   flags &= ~adns_qf_search;
284 
285   if (addr->sa_family != AF_INET) return ENOSYS;
286   iaddr= (const unsigned char*) &(((const struct sockaddr_in*)addr) -> sin_addr);
287 
288   lreq= strlen(zone) + 4*4 + 1;
289   if (lreq > (int)sizeof(shortbuf)) {
290     buf= malloc( strlen(zone) + 4*4 + 1 );
291     if (!buf) return errno;
292     buf_free= buf;
293   } else {
294     buf= shortbuf;
295     buf_free= 0;
296   }
297   sprintf(buf, "%d.%d.%d.%d.%s", iaddr[3], iaddr[2], iaddr[1], iaddr[0], zone);
298 
299   r= adns_submit(ads,buf,type,flags,context,query_r);
300   free(buf_free);
301   return r;
302 }
303 
adns_submit_reverse(adns_state ads,const struct sockaddr * addr,adns_rrtype type,adns_queryflags flags,void * context,adns_query * query_r)304 int adns_submit_reverse(adns_state ads,
305 			const struct sockaddr *addr,
306 			adns_rrtype type,
307 			adns_queryflags flags,
308 			void *context,
309 			adns_query *query_r) {
310   if (type != adns_r_ptr && type != adns_r_ptr_raw) return EINVAL;
311   return adns_submit_reverse_any(ads,addr,"in-addr.arpa",type,flags,context,query_r);
312 }
313 
adns_synchronous(adns_state ads,const char * owner,adns_rrtype type,adns_queryflags flags,adns_answer ** answer_r)314 int adns_synchronous(adns_state ads,
315 		     const char *owner,
316 		     adns_rrtype type,
317 		     adns_queryflags flags,
318 		     adns_answer **answer_r) {
319   adns_query qu;
320   int r;
321 
322   r= adns_submit(ads,owner,type,flags,0,&qu);
323   if (r) return r;
324 
325   r= adns_wait(ads,&qu,answer_r,0);
326   if (r) adns_cancel(qu);
327 
328   return r;
329 }
330 
alloc_common(adns_query qu,size_t sz)331 static void *alloc_common(adns_query qu, size_t sz) {
332   allocnode *an;
333 
334   if (!sz) return qu; /* Any old pointer will do */
335   assert(!qu->final_allocspace);
336   an= malloc(MEM_ROUND(MEM_ROUND(sizeof(*an)) + sz));
337   if (!an) return 0;
338   LIST_LINK_TAIL(qu->allocations,an);
339   return (byte*)an + MEM_ROUND(sizeof(*an));
340 }
341 
adns__alloc_interim(adns_query qu,size_t sz)342 void *adns__alloc_interim(adns_query qu, size_t sz) {
343   void *rv;
344 
345   sz= MEM_ROUND(sz);
346   rv= alloc_common(qu,sz);
347   if (!rv) return 0;
348   qu->interim_allocd += sz;
349   return rv;
350 }
351 
adns__alloc_preserved(adns_query qu,size_t sz)352 void *adns__alloc_preserved(adns_query qu, size_t sz) {
353   void *rv;
354 
355   sz= MEM_ROUND(sz);
356   rv= adns__alloc_interim(qu,sz);
357   if (!rv) return 0;
358   qu->preserved_allocd += sz;
359   return rv;
360 }
361 
adns__alloc_mine(adns_query qu,size_t sz)362 void *adns__alloc_mine(adns_query qu, size_t sz) {
363   return alloc_common(qu,MEM_ROUND(sz));
364 }
365 
adns__transfer_interim(adns_query from,adns_query to,void * block,size_t sz)366 void adns__transfer_interim(adns_query from, adns_query to, void *block, size_t sz) {
367   allocnode *an;
368 
369   if (!block) return;
370   an= (void*)((byte*)block - MEM_ROUND(sizeof(*an)));
371 
372   assert(!to->final_allocspace);
373   assert(!from->final_allocspace);
374 
375   LIST_UNLINK(from->allocations,an);
376   LIST_LINK_TAIL(to->allocations,an);
377 
378   sz= MEM_ROUND(sz);
379   from->interim_allocd -= sz;
380   to->interim_allocd += sz;
381 
382   if (to->expires > from->expires) to->expires= from->expires;
383 }
384 
adns__alloc_final(adns_query qu,size_t sz)385 void *adns__alloc_final(adns_query qu, size_t sz) {
386   /* When we're in the _final stage, we _subtract_ from interim_alloc'd
387    * each allocation, and use final_allocspace to point to the next free
388    * bit.
389    */
390   void *rp;
391 
392   sz= MEM_ROUND(sz);
393   rp= qu->final_allocspace;
394   assert(rp);
395   qu->interim_allocd -= sz;
396   assert(qu->interim_allocd>=0);
397   qu->final_allocspace= (byte*)rp + sz;
398   return rp;
399 }
400 
cancel_children(adns_query qu)401 static void cancel_children(adns_query qu) {
402   adns_query cqu, ncqu;
403 
404   for (cqu= qu->children.head; cqu; cqu= ncqu) {
405     ncqu= cqu->siblings.next;
406     adns_cancel(cqu);
407   }
408 }
409 
adns__reset_preserved(adns_query qu)410 void adns__reset_preserved(adns_query qu) {
411   assert(!qu->final_allocspace);
412   cancel_children(qu);
413   qu->answer->nrrs= 0;
414   qu->answer->rrs.untyped= 0;
415   qu->interim_allocd= qu->preserved_allocd;
416 }
417 
free_query_allocs(adns_query qu)418 static void free_query_allocs(adns_query qu) {
419   allocnode *an, *ann;
420 
421   cancel_children(qu);
422   for (an= qu->allocations.head; an; an= ann) { ann= an->next; free(an); }
423   LIST_INIT(qu->allocations);
424   adns__vbuf_free(&qu->vb);
425   adns__vbuf_free(&qu->search_vb);
426   free(qu->query_dgram);
427   qu->query_dgram= 0;
428 }
429 
adns_cancel(adns_query qu)430 void adns_cancel(adns_query qu) {
431   adns_state ads;
432 
433   ads= qu->ads;
434   adns__consistency(ads,qu,cc_entex);
435   if (qu->parent) LIST_UNLINK_PART(qu->parent->children,qu,siblings.);
436   switch (qu->state) {
437   case query_tosend:
438     LIST_UNLINK(ads->udpw,qu);
439     break;
440   case query_tcpw:
441     LIST_UNLINK(ads->tcpw,qu);
442     break;
443   case query_childw:
444     LIST_UNLINK(ads->childw,qu);
445     break;
446   case query_done:
447     LIST_UNLINK(ads->output,qu);
448     break;
449   default:
450     abort();
451   }
452   free_query_allocs(qu);
453   free(qu->answer);
454   free(qu);
455   adns__consistency(ads,0,cc_entex);
456 }
457 
adns__update_expires(adns_query qu,unsigned long ttl,struct timeval now)458 void adns__update_expires(adns_query qu, unsigned long ttl, struct timeval now) {
459   time_t max;
460 
461   assert(ttl <= MAXTTLBELIEVE);
462   max= now.tv_sec + ttl;
463   if (qu->expires < max) return;
464   qu->expires= max;
465 }
466 
makefinal_query(adns_query qu)467 static void makefinal_query(adns_query qu) {
468   adns_answer *ans;
469   int rrn;
470 
471   ans= qu->answer;
472 
473   if (qu->interim_allocd) {
474     ans= realloc(qu->answer, MEM_ROUND(MEM_ROUND(sizeof(*ans)) + qu->interim_allocd));
475     if (!ans) goto x_nomem;
476     qu->answer= ans;
477   }
478 
479   qu->final_allocspace= (byte*)ans + MEM_ROUND(sizeof(*ans));
480   adns__makefinal_str(qu,&ans->cname);
481   adns__makefinal_str(qu,&ans->owner);
482 
483   if (ans->nrrs) {
484     adns__makefinal_block(qu, &ans->rrs.untyped, (size_t) ans->nrrs*ans->rrsz);
485 
486     for (rrn=0; rrn<ans->nrrs; rrn++)
487       qu->typei->makefinal(qu, ans->rrs.bytes + rrn*ans->rrsz);
488   }
489 
490   free_query_allocs(qu);
491   return;
492 
493  x_nomem:
494   qu->preserved_allocd= 0;
495   qu->answer->cname= 0;
496   qu->answer->owner= 0;
497   adns__reset_preserved(qu); /* (but we just threw away the preserved stuff) */
498 
499   qu->answer->status= adns_s_nomemory;
500   free_query_allocs(qu);
501 }
502 
adns__query_done(adns_query qu)503 void adns__query_done(adns_query qu) {
504   adns_answer *ans;
505   adns_query parent;
506 
507   cancel_children(qu);
508 
509   qu->id= -1;
510   ans= qu->answer;
511 
512   if (qu->flags & adns_qf_owner && qu->flags & adns_qf_search &&
513       ans->status != adns_s_nomemory) {
514     if (!save_owner(qu, (char*)qu->search_vb.buf, qu->search_vb.used)) {
515       adns__query_fail(qu,adns_s_nomemory);
516       return;
517     }
518   }
519 
520   if (ans->nrrs && qu->typei->diff_needswap) {
521     if (!adns__vbuf_ensure(&qu->vb,qu->typei->rrsz)) {
522       adns__query_fail(qu,adns_s_nomemory);
523       return;
524     }
525     adns__isort(ans->rrs.bytes, ans->nrrs, ans->rrsz,
526 		qu->vb.buf,
527 		(int(*)(void*, const void*, const void*))qu->typei->diff_needswap,
528 		qu->ads);
529   }
530 
531   ans->expires= qu->expires;
532   parent= qu->parent;
533   if (parent) {
534     LIST_UNLINK_PART(parent->children,qu,siblings.);
535     LIST_UNLINK(qu->ads->childw,parent);
536     qu->ctx.callback(parent,qu);
537     free_query_allocs(qu);
538     free(qu->answer);
539     free(qu);
540   } else {
541     makefinal_query(qu);
542     LIST_LINK_TAIL(qu->ads->output,qu);
543     qu->state= query_done;
544   }
545 }
546 
adns__query_fail(adns_query qu,adns_status stat)547 void adns__query_fail(adns_query qu, adns_status stat) {
548   adns__reset_preserved(qu);
549   qu->answer->status= stat;
550   adns__query_done(qu);
551 }
552 
adns__makefinal_str(adns_query qu,char ** strp)553 void adns__makefinal_str(adns_query qu, char **strp) {
554   int l;
555   char *before, *after;
556 
557   before= *strp;
558   if (!before) return;
559   l= strlen(before)+1;
560   after= adns__alloc_final(qu, (size_t) l);
561   memcpy(after,before,(size_t) l);
562   *strp= after;
563 }
564 
adns__makefinal_block(adns_query qu,void ** blpp,size_t sz)565 void adns__makefinal_block(adns_query qu, void **blpp, size_t sz) {
566   void *before, *after;
567 
568   before= *blpp;
569   if (!before) return;
570   after= adns__alloc_final(qu,sz);
571   memcpy(after,before, (size_t) sz);
572   *blpp= after;
573 }
574