1 /**************************************************************************************************
2 	$Id: reply.c,v 1.65 2006/01/18 20:46:47 bboy Exp $
3 
4 	Copyright (C) 2002-2005  Don Moore <bboy@bboy.net>
5 
6 	This program is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 2 of the License, or
9 	(at Your option) any later version.
10 
11 	This program is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 	GNU General Public License for more details.
15 
16 	You should have received a copy of the GNU General Public License
17 	along with this program; if not, write to the Free Software
18 	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 **************************************************************************************************/
20 
21 #include "named.h"
22 
23 /* Make this nonzero to enable debugging for this source file */
24 #define	DEBUG_REPLY	1
25 
26 #if DEBUG_ENABLED && DEBUG_REPLY
27 /* Strings describing the datasections */
28 const char *reply_datasection_str[] = { "QUESTION", "ANSWER", "AUTHORITY", "ADDITIONAL" };
29 #endif
30 
31 
32 /**************************************************************************************************
33 	REPLY_INIT
34 	Examines the question data, storing the name offsets (from DNS_HEADERSIZE) for compression.
35 **************************************************************************************************/
36 int
reply_init(TASK * t)37 reply_init(TASK *t) {
38   register char *c = NULL;						/* Current character in name */
39 
40   /* Examine question data, save labels found therein. The question data should begin with
41      the name we've already parsed into t->qname.  I believe it is safe to assume that no
42      compression will be possible in the question. */
43   for (c = t->qname; *c; c++)
44     if ((c == t->qname || *c == '.') && c[1])
45       if (name_remember(t, (c == t->qname) ? c : (c+1),
46 			(((c == t->qname) ? c : (c+1)) - t->qname) + DNS_HEADERSIZE) < -1)
47 	return (-1);
48   return (0);
49 }
50 /*--- reply_init() ------------------------------------------------------------------------------*/
51 
52 
53 /**************************************************************************************************
54 	REPLY_ADD_ADDITIONAL
55 	Add ADDITIONAL for each item in the provided list.
56 **************************************************************************************************/
57 static void
reply_add_additional(TASK * t,RRLIST * rrlist)58 reply_add_additional(TASK *t, RRLIST *rrlist) {
59   register RR *p = NULL;
60 
61   if (!rrlist)
62     return;
63 
64   /* Examine each RR in the rrlist */
65   for (p = rrlist->head; p; p = p->next) {
66     if (p->rrtype == DNS_RRTYPE_RR) {
67       MYDNS_RR *rr = (MYDNS_RR *)p->rr;
68       if (rr->type == DNS_QTYPE_NS || rr->type == DNS_QTYPE_MX || rr->type == DNS_QTYPE_SRV) {
69 	(void)resolve(t, ADDITIONAL, DNS_QTYPE_A, MYDNS_RR_DATA_VALUE(rr), 0);
70       }	else if (rr->type == DNS_QTYPE_CNAME) {
71 	/* Don't do this */
72 	(void)resolve(t, ADDITIONAL, DNS_QTYPE_CNAME, MYDNS_RR_DATA_VALUE(rr), 0);
73       }
74     }
75     t->sort_level++;
76   }
77 }
78 /*--- reply_add_additional() --------------------------------------------------------------------*/
79 
80 
81 /**************************************************************************************************
82 	RDATA_ENLARGE
83 	Expands t->rdata by `size' bytes.  Returns a pointer to the destination.
84 **************************************************************************************************/
85 static char *
rdata_enlarge(TASK * t,size_t size)86 rdata_enlarge(TASK *t, size_t size) {
87   if (!size)
88     return (NULL);
89 
90   t->rdlen += size;
91   t->rdata = REALLOCATE(t->rdata, t->rdlen, char[]);
92   return (t->rdata + t->rdlen - size);
93 }
94 /*--- rdata_enlarge() ---------------------------------------------------------------------------*/
95 
96 
97 /**************************************************************************************************
98 	REPLY_START_RR
99 	Begins an RR.  Appends to t->rdata all the header fields prior to rdlength.
100 	Returns the numeric offset of the start of this record within the reply, or -1 on error.
101 **************************************************************************************************/
102 static int
reply_start_rr(TASK * t,RR * r,const char * name,dns_qtype_t type,uint32_t ttl,const char * desc)103 reply_start_rr(TASK *t, RR *r, const char *name, dns_qtype_t type, uint32_t ttl, const char *desc) {
104   char	*enc = NULL;
105   char	*dest = NULL;
106   int	enclen = 0;
107 
108   /* name_encode returns dnserror() */
109   if ((enclen = name_encode2(t, &enc, name, t->replylen + t->rdlen, 1)) < 0) {
110     return rr_error(r->id, _("rr %u: %s (%s %s) (name=\"%s\")"), r->id,
111 		    _("invalid name in \"name\""), desc, _("record"), name);
112   }
113 
114   r->length = enclen + SIZE16 + SIZE16 + SIZE32;
115 
116   if (!(dest = rdata_enlarge(t, r->length))) {
117     RELEASE(enc);
118     return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
119   }
120 
121   r->offset = dest - t->rdata + DNS_HEADERSIZE + t->qdlen;
122 
123   DNS_PUT(dest, enc, enclen);
124   RELEASE(enc);
125   DNS_PUT16(dest, type);
126 #if STATUS_ENABLED
127   if (r->rrtype == DNS_RRTYPE_RR && r->rr)
128     DNS_PUT16(dest, ((MYDNS_RR *)(r->rr))->class)
129     else
130 #endif
131       DNS_PUT16(dest, DNS_CLASS_IN);
132   DNS_PUT32(dest, ttl);
133   return (0);
134 }
135 /*--- reply_start_rr() --------------------------------------------------------------------------*/
136 
137 
138 /**************************************************************************************************
139 	REPLY_ADD_GENERIC_RR
140 	Adds a generic resource record whose sole piece of data is a domain-name,
141 	or a 16-bit value plus a domain-name.
142 	Returns the numeric offset of the start of this record within the reply, or -1 on error.
143 **************************************************************************************************/
144 static inline int
reply_add_generic_rr(TASK * t,RR * r,const char * desc)145 reply_add_generic_rr(TASK *t, RR *r, const char *desc) {
146   char		*enc = NULL, *dest = NULL;
147   int		size = 0, enclen = 0;
148   MYDNS_RR	*rr = (MYDNS_RR *)r->rr;
149 
150   if (reply_start_rr(t, r, (char*)r->name, rr->type, rr->ttl, desc) < 0)
151     return (-1);
152 
153   if ((enclen = name_encode2(t, &enc, MYDNS_RR_DATA_VALUE(rr), CUROFFSET(t), 1)) < 0) {
154     return rr_error(r->id, _("rr %u: %s (%s) (data=\"%s\")"), r->id,
155 		    _("invalid name in \"data\""), desc, (char*)MYDNS_RR_DATA_VALUE(rr));
156   }
157 
158   size = enclen;
159   r->length += SIZE16 + size;
160 
161   if (!(dest = rdata_enlarge(t, SIZE16 + size))) {
162     RELEASE(enc);
163     return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
164   }
165 
166   DNS_PUT16(dest, size);
167   DNS_PUT(dest, enc, enclen);
168   RELEASE(enc);
169   return (0);
170 }
171 /*--- reply_add_generic_rr() --------------------------------------------------------------------*/
172 
173 
174 /**************************************************************************************************
175 	REPLY_ADD_A
176 	Adds an A record to the reply.
177 	Returns the numeric offset of the start of this record within the reply, or -1 on error.
178 **************************************************************************************************/
179 static inline int
reply_add_a(TASK * t,RR * r)180 reply_add_a(TASK *t, RR *r) {
181   char		*dest = NULL;
182   int		size = 0;
183   MYDNS_RR	*rr = (MYDNS_RR *)r->rr;
184   struct in_addr addr;
185   uint32_t	ip = 0;
186 
187   memset(&addr, 0, sizeof(addr));
188 
189   if (inet_pton(AF_INET, MYDNS_RR_DATA_VALUE(rr), (void *)&addr) <= 0) {
190     dnserror(t, DNS_RCODE_SERVFAIL, ERR_INVALID_ADDRESS);
191     return rr_error(r->id, _("rr %u: %s (A %s) (address=\"%s\")"), r->id,
192 		    _("invalid address in \"data\""), _("record"), (char*)MYDNS_RR_DATA_VALUE(rr));
193   }
194   ip = ntohl(addr.s_addr);
195 
196   if (reply_start_rr(t, r, (char*)r->name, DNS_QTYPE_A, rr->ttl, "A") < 0)
197     return (-1);
198 
199   size = SIZE32;
200   r->length += SIZE16 + size;
201 
202   if (!(dest = rdata_enlarge(t, SIZE16 + size)))
203     return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
204 
205   DNS_PUT16(dest, size);
206   DNS_PUT32(dest, ip);
207 
208   return (0);
209 }
210 /*--- reply_add_a() -----------------------------------------------------------------------------*/
211 
212 
213 /**************************************************************************************************
214 	REPLY_ADD_AAAA
215 	Adds an AAAA record to the reply.
216 	Returns the numeric offset of the start of this record within the reply, or -1 on error.
217 **************************************************************************************************/
218 static inline int
reply_add_aaaa(TASK * t,RR * r)219 reply_add_aaaa(TASK *t, RR *r) {
220   char		*dest = NULL;
221   int		size = 0;
222   MYDNS_RR	*rr = (MYDNS_RR *)r->rr;
223   uint8_t	addr[16];
224 
225   memset(&addr, 0, sizeof(addr));
226 
227   if (inet_pton(AF_INET6, MYDNS_RR_DATA_VALUE(rr), (void *)&addr) <= 0) {
228     dnserror(t, DNS_RCODE_SERVFAIL, ERR_INVALID_ADDRESS);
229     return rr_error(r->id, _("rr %u: %s (AAAA %s) (address=\"%s\")"), r->id,
230 		    _("invalid address in \"data\""), _("record"), (char*)MYDNS_RR_DATA_VALUE(rr));
231   }
232 
233   if (reply_start_rr(t, r, (char*)r->name, DNS_QTYPE_AAAA, rr->ttl, "AAAA") < 0)
234     return (-1);
235 
236   size = sizeof(uint8_t) * 16;
237   r->length += SIZE16 + size;
238 
239   if (!(dest = rdata_enlarge(t, SIZE16 + size)))
240     return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
241 
242   DNS_PUT16(dest, size);
243   memcpy(dest, &addr, size);
244   dest += size;
245 
246   return (0);
247 }
248 /*--- reply_add_aaaa() --------------------------------------------------------------------------*/
249 
250 
251 /**************************************************************************************************
252 	REPLY_ADD_HINFO
253 	Adds an HINFO record to the reply.
254 	Returns the numeric offset of the start of this record within the reply, or -1 on error.
255 **************************************************************************************************/
256 static int
reply_add_hinfo(TASK * t,RR * r)257 reply_add_hinfo(TASK *t, RR *r) {
258   char		*dest = NULL;
259   size_t	oslen = 0, cpulen = 0;
260   MYDNS_RR	*rr = (MYDNS_RR *)r->rr;
261   char		os[DNS_MAXNAMELEN + 1] = "", cpu[DNS_MAXNAMELEN + 1] = "";
262 
263   if (hinfo_parse(MYDNS_RR_DATA_VALUE(rr), cpu, os, DNS_MAXNAMELEN) < 0) {
264     dnserror(t, DNS_RCODE_SERVFAIL, ERR_RR_NAME_TOO_LONG);
265     return rr_error(r->id, _("rr %u: %s (HINFO %s) (data=\"%s\")"), r->id,
266 		    _("name too long in \"data\""), _("record"), (char*)MYDNS_RR_DATA_VALUE(rr));
267   }
268 
269   cpulen = strlen(cpu);
270   oslen = strlen(os);
271 
272   if (reply_start_rr(t, r, (char*)r->name, DNS_QTYPE_HINFO, rr->ttl, "HINFO") < 0)
273     return (-1);
274 
275   r->length += SIZE16 + cpulen + oslen + 2;
276 
277   if (!(dest = rdata_enlarge(t, SIZE16 + cpulen + SIZE16 + oslen)))
278     return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
279 
280   DNS_PUT16(dest, cpulen + oslen + 2);
281 
282   *dest++ = cpulen;
283   memcpy(dest, cpu, cpulen);
284   dest += cpulen;
285 
286   *dest++ = oslen;
287   memcpy(dest, os, oslen);
288   dest += oslen;
289 
290   return (0);
291 }
292 /*--- reply_add_hinfo() -------------------------------------------------------------------------*/
293 
294 
295 /**************************************************************************************************
296 	REPLY_ADD_MX
297 	Adds an MX record to the reply.
298 	Returns the numeric offset of the start of this record within the reply, or -1 on error.
299 **************************************************************************************************/
300 static inline int
reply_add_mx(TASK * t,RR * r)301 reply_add_mx(TASK *t, RR *r) {
302   char		*enc = NULL, *dest = NULL;
303   int		size = 0, enclen = 0;
304   MYDNS_RR	*rr = (MYDNS_RR *)r->rr;
305 
306   if (reply_start_rr(t, r, (char*)r->name, DNS_QTYPE_MX, rr->ttl, "MX") < 0)
307     return (-1);
308 
309   if ((enclen = name_encode2(t, &enc, MYDNS_RR_DATA_VALUE(rr), CUROFFSET(t) + SIZE16, 1)) < 0) {
310     return rr_error(r->id, _("rr %u: %s (MX %s) (data=\"%s\")"), r->id,
311 		    _("invalid name in \"data\""), _("record"), (char*)MYDNS_RR_DATA_VALUE(rr));
312   }
313 
314   size = SIZE16 + enclen;
315   r->length += SIZE16 + size;
316 
317   if (!(dest = rdata_enlarge(t, SIZE16 + size))) {
318     RELEASE(enc);
319     return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
320   }
321 
322   DNS_PUT16(dest, size);
323   DNS_PUT16(dest, (uint16_t)rr->aux);
324   DNS_PUT(dest, enc, enclen);
325   RELEASE(enc);
326   return (0);
327 }
328 /*--- reply_add_mx() ----------------------------------------------------------------------------*/
329 
330 
331 /**************************************************************************************************
332 	REPLY_ADD_NAPTR
333 	Adds an NAPTR record to the reply.
334 	Returns the numeric offset of the start of this record within the reply, or -1 on error.
335 **************************************************************************************************/
336 static inline int
reply_add_naptr(TASK * t,RR * r)337 reply_add_naptr(TASK *t, RR *r) {
338   MYDNS_RR	*rr = (MYDNS_RR *)r->rr;
339   size_t	flags_len = 0, service_len = 0, regex_len = 0;
340   char		*enc = NULL, *dest = NULL;
341   int		size = 0, enclen = 0, offset = 0;
342 
343   flags_len = sizeof(MYDNS_RR_NAPTR_FLAGS(rr));
344   service_len = strlen(MYDNS_RR_NAPTR_SERVICE(rr));
345   regex_len = strlen(MYDNS_RR_NAPTR_REGEX(rr));
346 
347   if (reply_start_rr(t, r, (char*)r->name, DNS_QTYPE_NAPTR, rr->ttl, "NAPTR") < 0)
348     return (-1);
349 
350   /* We are going to write "something else" and then a name, just like an MX record or something.
351      In this case, though, the "something else" is lots of data.  Calculate the size of
352      "something else" in 'offset' */
353   offset = SIZE16 + SIZE16 + 1 + flags_len + 1 + service_len + 1 + regex_len;
354 
355   /* Encode the name at the offset */
356   if ((enclen = name_encode2(t, &enc, MYDNS_RR_NAPTR_REPLACEMENT(rr), CUROFFSET(t) + offset, 1)) < 0) {
357     return rr_error(r->id, _("rr %u: %s (NAPTR %s) (%s=\"%s\")"), r->id,
358 		    _("invalid name in \"replacement\""), _("record"), _("replacement"),
359 		    MYDNS_RR_NAPTR_REPLACEMENT(rr));
360   }
361 
362   size = offset + enclen;
363   r->length += SIZE16 + size;
364 
365   if (!(dest = rdata_enlarge(t, SIZE16 + size))) {
366     RELEASE(enc);
367     return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
368   }
369 
370   DNS_PUT16(dest, size);
371   DNS_PUT16(dest, (uint16_t)MYDNS_RR_NAPTR_ORDER(rr));
372   DNS_PUT16(dest, (uint16_t)MYDNS_RR_NAPTR_PREF(rr));
373 
374   *dest++ = flags_len;
375   memcpy(dest, MYDNS_RR_NAPTR_FLAGS(rr), flags_len);
376   dest += flags_len;
377 
378   *dest++ = service_len;
379   memcpy(dest, MYDNS_RR_NAPTR_SERVICE(rr), service_len);
380   dest += service_len;
381 
382   *dest++ = regex_len;
383   memcpy(dest, MYDNS_RR_NAPTR_REGEX(rr), regex_len);
384   dest += regex_len;
385 
386   DNS_PUT(dest, enc, enclen);
387   RELEASE(enc);
388 
389   return (0);
390 }
391 /*--- reply_add_naptr() -------------------------------------------------------------------------*/
392 
393 
394 /**************************************************************************************************
395 	REPLY_ADD_RP
396 	Adds an RP record to the reply.
397 	Returns the numeric offset of the start of this record within the reply, or -1 on error.
398 **************************************************************************************************/
399 static inline int
reply_add_rp(TASK * t,RR * r)400 reply_add_rp(TASK *t, RR *r) {
401   char		*mbox = NULL, *txt = NULL, *dest = NULL;
402   char		*encmbox = NULL, *enctxt = NULL;
403   int		size = 0, mboxlen = 0, txtlen = 0;
404   MYDNS_RR	*rr = (MYDNS_RR *)r->rr;
405 
406   mbox = MYDNS_RR_DATA_VALUE(rr);
407   txt = MYDNS_RR_RP_TXT(rr);
408 
409   if (reply_start_rr(t, r, (char*)r->name, DNS_QTYPE_RP, rr->ttl, "RP") < 0)
410     return (-1);
411 
412   if ((mboxlen = name_encode2(t, &encmbox, mbox, CUROFFSET(t), 1)) < 0) {
413     return rr_error(r->id, _("rr %u: %s (RP %s) (mbox=\"%s\")"), r->id,
414 		    _("invalid name in \"mbox\""), _("record"), mbox);
415   }
416 
417   if ((txtlen = name_encode2(t, &enctxt, txt, CUROFFSET(t) + mboxlen, 1)) < 0) {
418     RELEASE(encmbox);
419     return rr_error(r->id, _("rr %u: %s (RP %s) (txt=\"%s\")"), r->id,
420 		    _("invalid name in \"txt\""), _("record"), txt);
421   }
422 
423   size = mboxlen + txtlen;
424   r->length += SIZE16 + size;
425 
426   if (!(dest = rdata_enlarge(t, SIZE16 + size))) {
427     RELEASE(encmbox);
428     RELEASE(enctxt);
429     return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
430   }
431 
432   DNS_PUT16(dest, size);
433   DNS_PUT(dest, encmbox, mboxlen);
434   DNS_PUT(dest, enctxt, txtlen);
435   RELEASE(encmbox);
436   RELEASE(enctxt);
437   return (0);
438 }
439 /*--- reply_add_rp() ----------------------------------------------------------------------------*/
440 
441 
442 /**************************************************************************************************
443 	REPLY_ADD_SOA
444 	Add a SOA record to the reply.
445 	Returns the numeric offset of the start of this record within the reply, or -1 on error.
446 **************************************************************************************************/
447 static inline int
reply_add_soa(TASK * t,RR * r)448 reply_add_soa(TASK *t, RR *r) {
449   char		*dest = NULL, *ns = NULL, *mbox = NULL;
450   int		size = 0, nslen = 0, mboxlen = 0;
451   MYDNS_SOA	*soa = (MYDNS_SOA *)r->rr;
452 
453   if (reply_start_rr(t, r, (char*)r->name, DNS_QTYPE_SOA, soa->ttl, "SOA") < 0)
454     return (-1);
455 
456   if ((nslen = name_encode2(t, &ns, soa->ns, CUROFFSET(t), 1)) < 0) {
457     return rr_error(r->id, _("rr %u: %s (SOA %s) (ns=\"%s\")"), r->id,
458 		    _("invalid name in \"ns\""), _("record"), soa->ns);
459   }
460 
461   if ((mboxlen = name_encode2(t, &mbox, soa->mbox, CUROFFSET(t) + nslen, 1)) < 0) {
462     RELEASE(ns);
463     return rr_error(r->id, _("rr %u: %s (SOA %s) (mbox=\"%s\")"), r->id,
464 		    _("invalid name in \"mbox\""), _("record"), soa->mbox);
465   }
466 
467   size = nslen + mboxlen + (SIZE32 * 5);
468   r->length += SIZE16 + size;
469 
470   if (!(dest = rdata_enlarge(t, SIZE16 + size))) {
471     RELEASE(ns);
472     RELEASE(mbox);
473     return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
474   }
475 
476   DNS_PUT16(dest, size);
477   DNS_PUT(dest, ns, nslen);
478   DNS_PUT(dest, mbox, mboxlen);
479   RELEASE(ns);
480   RELEASE(mbox);
481   DNS_PUT32(dest, soa->serial);
482   DNS_PUT32(dest, soa->refresh);
483   DNS_PUT32(dest, soa->retry);
484   DNS_PUT32(dest, soa->expire);
485   DNS_PUT32(dest, soa->minimum);
486   return (0);
487 }
488 /*--- reply_add_soa() ---------------------------------------------------------------------------*/
489 
490 
491 /**************************************************************************************************
492 	REPLY_ADD_SRV
493 	Adds a SRV record to the reply.
494 	Returns the numeric offset of the start of this record within the reply, or -1 on error.
495 **************************************************************************************************/
496 static inline int
reply_add_srv(TASK * t,RR * r)497 reply_add_srv(TASK *t, RR *r) {
498   char		*enc = NULL, *dest = NULL;
499   int		size = 0, enclen = 0;
500   MYDNS_RR	*rr = (MYDNS_RR *)r->rr;
501 
502   if (reply_start_rr(t, r, (char*)r->name, DNS_QTYPE_SRV, rr->ttl, "SRV") < 0)
503     return (-1);
504 
505   /* RFC 2782 says that we can't use name compression on this field... */
506   /* Arnt Gulbrandsen advises against using compression in the SRV target, although
507      most clients should support it */
508   if ((enclen = name_encode2(t, &enc, MYDNS_RR_DATA_VALUE(rr), CUROFFSET(t) + SIZE16 + SIZE16 + SIZE16, 0)) < 0) {
509     return rr_error(r->id, _("rr %u: %s (SRV %s) (data=\"%s\")"), r->id,
510 		    _("invalid name in \"data\""), _("record"), (char*)MYDNS_RR_DATA_VALUE(rr));
511   }
512 
513   size = SIZE16 + SIZE16 + SIZE16 + enclen;
514   r->length += SIZE16 + size;
515 
516   if (!(dest = rdata_enlarge(t, SIZE16 + size))) {
517     RELEASE(enc);
518     return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
519   }
520 
521   DNS_PUT16(dest, size);
522   DNS_PUT16(dest, (uint16_t)rr->aux);
523   DNS_PUT16(dest, (uint16_t)MYDNS_RR_SRV_WEIGHT(rr));
524   DNS_PUT16(dest, (uint16_t)MYDNS_RR_SRV_PORT(rr));
525   DNS_PUT(dest, enc, enclen);
526   RELEASE(enc);
527   return (0);
528 }
529 /*--- reply_add_srv() ---------------------------------------------------------------------------*/
530 
531 
532 /**************************************************************************************************
533 	REPLY_ADD_TXT
534 	Adds a TXT record to the reply.
535 	Returns the numeric offset of the start of this record within the reply, or -1 on error.
536 **************************************************************************************************/
537 static inline int
reply_add_txt(TASK * t,RR * r)538 reply_add_txt(TASK *t, RR *r) {
539   char		*dest = NULL;
540   size_t	size = 0;
541   size_t	len = 0;
542   MYDNS_RR	*rr = (MYDNS_RR *)r->rr;
543 
544   len = MYDNS_RR_DATA_LENGTH(rr);
545 
546   if (reply_start_rr(t, r, (char*)r->name, DNS_QTYPE_TXT, rr->ttl, "TXT") < 0)
547     return (-1);
548 
549   size = len + 1;
550   r->length += SIZE16 + size;
551 
552   if (!(dest = rdata_enlarge(t, SIZE16 + size)))
553     return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
554 
555   DNS_PUT16(dest, size);
556   *dest++ = len;
557   memcpy(dest, MYDNS_RR_DATA_VALUE(rr), len);
558   dest += len;
559   return (0);
560 }
561 /*--- reply_add_txt() ---------------------------------------------------------------------------*/
562 
563 
564 /**************************************************************************************************
565 	REPLY_PROCESS_RRLIST
566 	Adds each resource record found in `rrlist' to the reply.
567 **************************************************************************************************/
568 static int
reply_process_rrlist(TASK * t,RRLIST * rrlist)569 reply_process_rrlist(TASK *t, RRLIST *rrlist) {
570   register RR *r = NULL;
571 
572   if (!rrlist)
573     return (0);
574 
575   for (r = rrlist->head; r; r = r->next) {
576     switch (r->rrtype) {
577     case DNS_RRTYPE_SOA:
578       if (reply_add_soa(t, r) < 0)
579 	return (-1);
580       break;
581 
582     case DNS_RRTYPE_RR:
583       {
584 	MYDNS_RR *rr = (MYDNS_RR *)r->rr;
585 
586 	if (!rr)
587 	  break;
588 
589 	switch (rr->type) {
590 	case DNS_QTYPE_UNKNOWN:
591 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
592 		_("unexpected resource record type - logic problem"));
593 	  break;
594 
595 	case DNS_QTYPE_NONE:
596 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
597 		_("unexpected resource record type - logic problem"));
598 	  break;
599 
600 	case DNS_QTYPE_A:
601 	  if (reply_add_a(t, r) < 0)
602 	    return (-1);
603 	  break;
604 
605 	case DNS_QTYPE_NS:
606 	  if (reply_add_generic_rr(t, r, "NS") < 0)
607 	    return (-1);
608 	  break;
609 
610 	case DNS_QTYPE_MD:
611 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
612 		_("unsupported resource record type"));
613 	  break;
614 
615 	case DNS_QTYPE_MF:
616 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
617 		_("unsupported resource record type"));
618 	  break;
619 
620 	case DNS_QTYPE_CNAME:
621 	  if (reply_add_generic_rr(t, r, "CNAME") < 0)
622 	    return (-1);
623 	  break;
624 
625 	case DNS_QTYPE_SOA:
626 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
627 		_("unexpected resource record type - logic problem"));
628 	  break;
629 
630 	case DNS_QTYPE_MB:
631 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
632 		_("unsupported resource record type"));
633 	  break;
634 
635 	case DNS_QTYPE_MG:
636 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
637 		_("unsupported resource record type"));
638 	  break;
639 
640 	case DNS_QTYPE_MR:
641 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
642 		_("unsupported resource record type"));
643 	  break;
644 
645 	case DNS_QTYPE_NULL:
646 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
647 		_("unsupported resource record type"));
648 	  break;
649 
650 	case DNS_QTYPE_WKS:
651 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
652 		_("unsupported resource record type"));
653 	  break;
654 
655 	case DNS_QTYPE_PTR:
656 	  if (reply_add_generic_rr(t, r, "PTR") < 0)
657 	    return (-1);
658 	  break;
659 
660 	case DNS_QTYPE_HINFO:
661 	  if (reply_add_hinfo(t, r) < 0)
662 	    return (-1);
663 	  break;
664 
665 	case DNS_QTYPE_MINFO:
666 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
667 		_("unsupported resource record type"));
668 	  break;
669 
670 	case DNS_QTYPE_MX:
671 	  if (reply_add_mx(t, r) < 0)
672 	    return (-1);
673 	  break;
674 
675 	case DNS_QTYPE_TXT:
676 	  if (reply_add_txt(t, r) < 0)
677 	    return (-1);
678 	  break;
679 
680 	case DNS_QTYPE_RP:
681 	  if (reply_add_rp(t, r) < 0)
682 	    return (-1);
683 	  break;
684 
685 	case DNS_QTYPE_AFSDB:
686 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
687 		_("unsupported resource record type"));
688 	  break;
689 
690 	case DNS_QTYPE_X25:
691 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
692 		_("unsupported resource record type"));
693 	  break;
694 
695 	case DNS_QTYPE_ISDN:
696 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
697 		_("unsupported resource record type"));
698 	  break;
699 
700 	case DNS_QTYPE_RT:
701 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
702 		_("unsupported resource record type"));
703 	  break;
704 
705 	case DNS_QTYPE_NSAP:
706 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
707 		_("unsupported resource record type"));
708 	  break;
709 
710 	case DNS_QTYPE_NSAP_PTR:
711 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
712 		_("unsupported resource record type"));
713 	  break;
714 
715 	case DNS_QTYPE_SIG:
716 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
717 		_("unsupported resource record type"));
718 	  break;
719 
720 	case DNS_QTYPE_KEY:
721 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
722 		_("unsupported resource record type"));
723 	  break;
724 
725 	case DNS_QTYPE_PX:
726 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
727 		_("unsupported resource record type"));
728 	  break;
729 
730 	case DNS_QTYPE_GPOS:
731 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
732 		_("unsupported resource record type"));
733 	  break;
734 
735 	case DNS_QTYPE_AAAA:
736 	  if (reply_add_aaaa(t, r) < 0)
737 	    return (-1);
738 	  break;
739 
740 	case DNS_QTYPE_LOC:
741 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
742 		_("unsupported resource record type"));
743 	  break;
744 
745 	case DNS_QTYPE_NXT:
746 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
747 		_("unsupported resource record type"));
748 	  break;
749 
750 	case DNS_QTYPE_EID:
751 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
752 		_("unsupported resource record type"));
753 	  break;
754 
755 	case DNS_QTYPE_NIMLOC:
756 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
757 		_("unsupported resource record type"));
758 	  break;
759 
760 	case DNS_QTYPE_SRV:
761 	  if (reply_add_srv(t, r) < 0)
762 	    return (-1);
763 	  break;
764 
765 	case DNS_QTYPE_ATMA:
766 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
767 		_("unsupported resource record type"));
768 	  break;
769 
770 	case DNS_QTYPE_NAPTR:
771 	  if (reply_add_naptr(t, r) < 0)
772 	    return (-1);
773 	  break;
774 
775 	case DNS_QTYPE_KX:
776 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
777 		_("unsupported resource record type"));
778 	  break;
779 
780 	case DNS_QTYPE_CERT:
781 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
782 		_("unsupported resource record type"));
783 	  break;
784 
785 	case DNS_QTYPE_A6:
786 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
787 		_("unsupported resource record type"));
788 	  break;
789 
790 	case DNS_QTYPE_DNAME:
791 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
792 		_("unsupported resource record type"));
793 	  break;
794 
795 	case DNS_QTYPE_SINK:
796 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
797 		_("unsupported resource record type"));
798 	  break;
799 
800 	case DNS_QTYPE_OPT:
801 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
802 		_("unsupported resource record type"));
803 	  break;
804 
805 	case DNS_QTYPE_APL:
806 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
807 		_("unsupported resource record type"));
808 	  break;
809 
810 	case DNS_QTYPE_DS:
811 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
812 		_("unsupported resource record type"));
813 	  break;
814 
815 	case DNS_QTYPE_SSHFP:
816 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
817 		_("unsupported resource record type"));
818 	  break;
819 
820 	case DNS_QTYPE_IPSECKEY:
821 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
822 		_("unsupported resource record type"));
823 	  break;
824 
825 	case DNS_QTYPE_RRSIG:
826 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
827 		_("unsupported resource record type"));
828 	  break;
829 
830 	case DNS_QTYPE_NSEC:
831 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
832 		_("unsupported resource record type"));
833 	  break;
834 
835 	case DNS_QTYPE_DNSKEY:
836 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
837 		_("unsupported resource record type"));
838 	  break;
839 
840 	case DNS_QTYPE_DHCID:
841 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
842 		_("unsupported resource record type"));
843 	  break;
844 
845 	case DNS_QTYPE_NSEC3:
846 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
847 		_("unsupported resource record type"));
848 	  break;
849 
850 	case DNS_QTYPE_NSEC3PARAM:
851 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
852 		_("unsupported resource record type"));
853 	  break;
854 
855 	case DNS_QTYPE_HIP:
856 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
857 		_("unsupported resource record type"));
858 	  break;
859 
860 	case DNS_QTYPE_SPF:
861 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
862 		_("unsupported resource record type"));
863 	  break;
864 
865 	case DNS_QTYPE_UINFO:
866 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
867 		_("unsupported resource record type"));
868 	  break;
869 
870 	case DNS_QTYPE_UID:
871 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
872 		_("unsupported resource record type"));
873 	  break;
874 
875 	case DNS_QTYPE_GID:
876 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
877 		_("unsupported resource record type"));
878 	  break;
879 
880 	case DNS_QTYPE_UNSPEC:
881 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
882 		_("unsupported resource record type"));
883 	  break;
884 
885 	case DNS_QTYPE_TKEY:
886 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
887 		_("unsupported resource record type"));
888 	  break;
889 
890 	case DNS_QTYPE_TSIG:
891 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
892 		_("unsupported resource record type"));
893 	  break;
894 
895 	case DNS_QTYPE_IXFR:
896 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
897 		_("unexpect resource record type - logic problem"));
898 	  break;
899 
900 	case DNS_QTYPE_AXFR:
901 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
902 		_("unexpected resource record type - logic problem"));
903 	  break;
904 
905 	case DNS_QTYPE_MAILB:
906 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
907 		_("unsupported resource record type"));
908 	  break;
909 
910 	case DNS_QTYPE_MAILA:
911 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
912 		_("unsupported resource record type"));
913 	  break;
914 
915 	case DNS_QTYPE_ANY:
916 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
917 		_("unexpected resource record type - logic problem"));
918 	  break;
919 
920 	case DNS_QTYPE_TA:
921 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
922 		_("unsupported resource record type"));
923 	  break;
924 
925 	case DNS_QTYPE_DLV:
926 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
927 		_("unsupported resource record type"));
928 	  break;
929 
930 #if ALIAS_ENABLED
931 	case DNS_QTYPE_ALIAS:
932 	  Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
933 		_("unexpected resource record type - logic problem"));
934 	  break;
935 #endif
936 
937 	}
938       }
939       break;
940     }
941   }
942   return (0);
943 }
944 /*--- reply_process_rrlist() --------------------------------------------------------------------*/
945 
946 
947 /**************************************************************************************************
948 	TRUNCATE_RRLIST
949 	Returns new count of items in this list.
950 	The TC flag is _not_ set if data was truncated from the ADDITIONAL section.
951 **************************************************************************************************/
952 static int
truncate_rrlist(TASK * t,off_t maxpkt,RRLIST * rrlist,datasection_t ds)953 truncate_rrlist(TASK *t, off_t maxpkt, RRLIST *rrlist, datasection_t ds) {
954   register RR *rr = NULL;
955   register int recs = 0;
956 #if DEBUG_ENABLED && DEBUG_REPLY
957   int orig_recs = rrlist->size;
958 #endif
959 
960   /* Warn about truncated packets, but only if TCP is not enabled.  Most resolvers will try
961      TCP if a UDP packet is truncated. */
962   if (!tcp_enabled)
963     Verbose("%s: %s", desctask(t), _("query truncated"));
964 
965   recs = rrlist->size;
966   for (rr = rrlist->head; rr; rr = rr->next) {
967     if ((off_t)(rr->offset + rr->length) >= maxpkt) {
968       recs--;
969       if (ds != ADDITIONAL)
970 	t->hdr.tc = 1;
971     } else
972       t->rdlen += rr->length;
973   }
974 #if DEBUG_ENABLED && DEBUG_REPLY
975   DebugX("reply", 1, _("%s section truncated from %d records to %d records"),
976 	 reply_datasection_str[ds], orig_recs, recs);
977 #endif
978   return (recs);
979 }
980 /*--- truncate_rrlist() -------------------------------------------------------------------------*/
981 
982 
983 /**************************************************************************************************
984 	REPLY_CHECK_TRUNCATION
985 	If this reply would be truncated, removes any RR's that won't fit and sets the truncation flag.
986 **************************************************************************************************/
987 static void
reply_check_truncation(TASK * t,int * ancount,int * nscount,int * arcount)988 reply_check_truncation(TASK *t, int *ancount, int *nscount, int *arcount) {
989   size_t maxpkt = (t->protocol == SOCK_STREAM ? DNS_MAXPACKETLEN_TCP : DNS_MAXPACKETLEN_UDP);
990   size_t maxrd = maxpkt - (DNS_HEADERSIZE + t->qdlen);
991 
992   if (t->rdlen <= maxrd)
993     return;
994 
995 #if DEBUG_ENABLED && DEBUG_REPLY
996   DebugX("reply", 1, _("reply_check_truncation() needs to truncate reply (%u) to fit packet max (%u)"),
997 	 (unsigned int)t->rdlen, (unsigned int)maxrd);
998 #endif
999 
1000   /* Loop through an/ns/ar sections, truncating as necessary, and updating counts */
1001   t->rdlen = 0;
1002   *ancount = truncate_rrlist(t, maxpkt, &t->an, ANSWER);
1003   *nscount = truncate_rrlist(t, maxpkt, &t->ns, AUTHORITY);
1004   *arcount = truncate_rrlist(t, maxpkt, &t->ar, ADDITIONAL);
1005 }
1006 /*--- reply_check_truncation() ------------------------------------------------------------------*/
1007 
1008 void
abandon_reply(TASK * t)1009 abandon_reply(TASK *t) {
1010   /* Empty RR lists */
1011   rrlist_free(&t->an);
1012   rrlist_free(&t->ns);
1013   rrlist_free(&t->ar);
1014 
1015   /* Make sure reply is empty */
1016   t->replylen = 0;
1017   t->rdlen = 0;
1018   RELEASE(t->rdata);
1019 }
1020 
1021 /**************************************************************************************************
1022 	BUILD_CACHE_REPLY
1023 	Builds reply data from cached answer.
1024 **************************************************************************************************/
1025 void
build_cache_reply(TASK * t)1026 build_cache_reply(TASK *t) {
1027   char *dest = t->reply;
1028 
1029   DNS_PUT16(dest, t->id);							/* Query ID */
1030   DNS_PUT(dest, &t->hdr, SIZE16);						/* Header */
1031 }
1032 /*--- build_cache_reply() -----------------------------------------------------------------------*/
1033 
1034 
1035 /**************************************************************************************************
1036 	BUILD_REPLY
1037 	Given a task, constructs the reply data.
1038 **************************************************************************************************/
1039 void
build_reply(TASK * t,int want_additional)1040 build_reply(TASK *t, int want_additional) {
1041   char	*dest = NULL;
1042   int	ancount = 0, nscount = 0, arcount = 0;
1043 
1044   /* Add data to ADDITIONAL section */
1045   if (want_additional) {
1046     reply_add_additional(t, &t->an);
1047     reply_add_additional(t, &t->ns);
1048   }
1049 
1050   /* Sort records where necessary */
1051   if (t->an.a_records > 1)			/* ANSWER section: Sort A/AAAA records */
1052     sort_a_recs(t, &t->an, ANSWER);
1053   if (t->an.mx_records > 1)			/* ANSWER section: Sort MX records */
1054     sort_mx_recs(t, &t->an, ANSWER);
1055   if (t->an.srv_records > 1)			/* ANSWER section: Sort SRV records */
1056     sort_srv_recs(t, &t->an, ANSWER);
1057   if (t->ar.a_records > 1)			/* AUTHORITY section: Sort A/AAAA records */
1058     sort_a_recs(t, &t->ar, AUTHORITY);
1059 
1060   /* Build `rdata' containing resource records in ANSWER, AUTHORITY, and ADDITIONAL */
1061   t->replylen = DNS_HEADERSIZE + t->qdlen + t->rdlen;
1062   if (reply_process_rrlist(t, &t->an)
1063       || reply_process_rrlist(t, &t->ns)
1064       || reply_process_rrlist(t, &t->ar)) {
1065     abandon_reply(t);
1066   }
1067 
1068   ancount = t->an.size;
1069   nscount = t->ns.size;
1070   arcount = t->ar.size;
1071 
1072   /* Verify reply length */
1073   reply_check_truncation(t, &ancount, &nscount, &arcount);
1074 
1075   /* Make sure header bits are set correctly */
1076   t->hdr.qr = 1;
1077   t->hdr.cd = 0;
1078 
1079   /* Construct the reply */
1080   t->replylen = DNS_HEADERSIZE + t->qdlen + t->rdlen;
1081   dest = t->reply = ALLOCATE(t->replylen, char[]);
1082 
1083   DNS_PUT16(dest, t->id);					/* Query ID */
1084   DNS_PUT(dest, &t->hdr, SIZE16);				/* Header */
1085   DNS_PUT16(dest, t->qdcount);					/* QUESTION count */
1086   DNS_PUT16(dest, ancount);					/* ANSWER count */
1087   DNS_PUT16(dest, nscount);					/* AUTHORITY count */
1088   DNS_PUT16(dest, arcount);					/* ADDITIONAL count */
1089   if (t->qdlen && t->qd)
1090     DNS_PUT(dest, t->qd, t->qdlen);				/* Data for QUESTION section */
1091   DNS_PUT(dest, t->rdata, t->rdlen);				/* Resource record data */
1092 
1093 #if DEBUG_ENABLED && DEBUG_REPLY
1094   DebugX("reply", 1, _("%s: reply:     id = %u"), desctask(t),
1095 	 t->id);
1096   DebugX("reply", 1, _("%s: reply:     qr = %u (message is a %s)"), desctask(t),
1097 	 t->hdr.qr, t->hdr.qr ? "response" : "query");
1098   DebugX("reply", 1, _("%s: reply: opcode = %u (%s)"), desctask(t),
1099 	 t->hdr.opcode, mydns_opcode_str(t->hdr.opcode));
1100   DebugX("reply", 1, _("%s: reply:     aa = %u (answer %s)"), desctask(t),
1101 	 t->hdr.aa, t->hdr.aa ? "is authoritative" : "not authoritative");
1102   DebugX("reply", 1, _("%s: reply:     tc = %u (message %s)"), desctask(t),
1103 	 t->hdr.tc, t->hdr.tc ? "truncated" : "not truncated");
1104   DebugX("reply", 1, _("%s: reply:     rd = %u (%s)"), desctask(t),
1105 	 t->hdr.rd, t->hdr.rd ? "recursion desired" : "no recursion");
1106   DebugX("reply", 1, _("%s: reply:     ra = %u (recursion %s)"), desctask(t),
1107 	 t->hdr.ra, t->hdr.ra ? "available" : "unavailable");
1108   DebugX("reply", 1, _("%s: reply:  rcode = %u (%s)"), desctask(t),
1109 	 t->hdr.rcode, mydns_rcode_str(t->hdr.rcode));
1110   /* escdata(t->reply, t->replylen); */
1111 #endif
1112 }
1113 /*--- build_reply() -----------------------------------------------------------------------------*/
1114 
1115 /* vi:set ts=3: */
1116 /* NEED_PO */
1117