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