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 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 {
39 register char *c; /* Current character in name */
40
41 /* Examine question data, save labels found therein. The question data should begin with
42 the name we've already parsed into t->qname. I believe it is safe to assume that no
43 compression will be possible in the question. */
44 for (c = t->qname; *c; c++)
45 if ((c == t->qname || *c == '.') && c[1])
46 if (name_remember(t, (c == t->qname) ? c : (c+1),
47 (((c == t->qname) ? c : (c+1)) - t->qname) + DNS_HEADERSIZE) < -1)
48 return (-1);
49 return (0);
50 }
51 /*--- reply_init() ------------------------------------------------------------------------------*/
52
53
54 /**************************************************************************************************
55 REPLY_ADD_ADDITIONAL
56 Add ADDITIONAL for each item in the provided list.
57 **************************************************************************************************/
58 static void
reply_add_additional(TASK * t,RRLIST * rrlist,datasection_t section)59 reply_add_additional(TASK *t, RRLIST *rrlist, datasection_t section)
60 {
61 register RR *p;
62
63 if (!rrlist)
64 return;
65
66 /* Examine each RR in the rrlist */
67 for (p = rrlist->head; p; p = p->next)
68 {
69 if (p->rrtype == DNS_RRTYPE_RR)
70 {
71 MYDNS_RR *rr = (MYDNS_RR *)p->rr;
72 if (rr->type == DNS_QTYPE_NS || rr->type == DNS_QTYPE_MX || rr->type == DNS_QTYPE_SRV)
73 {
74 #if DEBUG_ENABLED && DEBUG_REPLY
75 Debug("%s: resolving `%s' (A) for ADDITIONAL data", desctask(t), rr->data);
76 #endif
77 (void)resolve(t, ADDITIONAL, DNS_QTYPE_A, rr->data, 0);
78 }
79 else if (rr->type == DNS_QTYPE_CNAME)
80 {
81 #if DEBUG_ENABLED && DEBUG_REPLY
82 Debug("%s: resolving `%s' (CNAME) for ADDITIONAL data", desctask(t), rr->data);
83 #endif
84 /* Don't do this */
85 (void)resolve(t, ADDITIONAL, DNS_QTYPE_CNAME, rr->data, 0);
86 }
87 }
88 t->sort_level++;
89 }
90 }
91 /*--- reply_add_additional() --------------------------------------------------------------------*/
92
93
94 /**************************************************************************************************
95 RDATA_ENLARGE
96 Expands t->rdata by `size' bytes. Returns a pointer to the destination.
97 **************************************************************************************************/
98 static inline char *
rdata_enlarge(TASK * t,size_t size)99 rdata_enlarge(TASK *t, size_t size)
100 {
101 if (!size)
102 return (NULL);
103
104 t->rdlen += size;
105 if (!t->rdata)
106 {
107 if (!(t->rdata = malloc(t->rdlen)))
108 Err(_("out of memory"));
109 }
110 else
111 {
112 if (!(t->rdata = realloc(t->rdata, t->rdlen)))
113 Err(_("out of memory"));
114 }
115 return (t->rdata + t->rdlen - size);
116 }
117 /*--- rdata_enlarge() ---------------------------------------------------------------------------*/
118
119
120 /**************************************************************************************************
121 REPLY_START_RR
122 Begins an RR. Appends to t->rdata all the header fields prior to rdlength.
123 Returns the numeric offset of the start of this record within the reply, or -1 on error.
124 **************************************************************************************************/
125 static inline int
reply_start_rr(TASK * t,RR * r,char * name,dns_qtype_t type,uint32_t ttl,char * desc)126 reply_start_rr(TASK *t, RR *r, char *name, dns_qtype_t type, uint32_t ttl, char *desc)
127 {
128 char enc[DNS_MAXNAMELEN+1];
129 char *dest;
130 int enclen;
131
132 /* name_encode returns dnserror() */
133 if ((enclen = name_encode(t, enc, name, t->replylen + t->rdlen, 1)) < 0)
134 return rr_error(r->id, "rr %u: %s (%s %s) (name=\"%s\")", r->id,
135 _("invalid name in \"name\""), desc, _("record"), name);
136
137 r->length = enclen + SIZE16 + SIZE16 + SIZE32;
138
139 if (!(dest = rdata_enlarge(t, r->length)))
140 return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
141
142 r->offset = dest - t->rdata + DNS_HEADERSIZE + t->qdlen;
143
144 DNS_PUT(dest, enc, enclen);
145 DNS_PUT16(dest, type);
146 #if STATUS_ENABLED
147 if (r->rrtype == DNS_RRTYPE_RR && r->rr)
148 DNS_PUT16(dest, ((MYDNS_RR *)(r->rr))->class)
149 else
150 #endif
151 DNS_PUT16(dest, DNS_CLASS_IN);
152 DNS_PUT32(dest, ttl);
153 return (0);
154 }
155 /*--- reply_start_rr() --------------------------------------------------------------------------*/
156
157
158 /**************************************************************************************************
159 REPLY_ADD_GENERIC_RR
160 Adds a generic resource record whose sole piece of data is a domain-name,
161 or a 16-bit value plus a domain-name.
162 Returns the numeric offset of the start of this record within the reply, or -1 on error.
163 **************************************************************************************************/
164 static inline int
reply_add_generic_rr(TASK * t,RR * r,char * desc)165 reply_add_generic_rr(TASK *t, RR *r, char *desc)
166 {
167 char enc[DNS_MAXNAMELEN+1], *dest;
168 int size, enclen;
169 MYDNS_RR *rr = (MYDNS_RR *)r->rr;
170
171 #if DEBUG_ENABLED && DEBUG_REPLY
172 Debug("%s: REPLY_ADD: `%s' IN %s `%s'", desctask(t), r->name, mydns_qtype_str(rr->type), rr->data);
173 #endif
174
175 if (reply_start_rr(t, r, r->name, rr->type, rr->ttl, desc) < 0)
176 return (-1);
177
178 if ((enclen = name_encode(t, enc, rr->data, CUROFFSET(t), 1)) < 0)
179 return rr_error(r->id, "rr %u: %s (%s) (data=\"%s\")", r->id,
180 _("invalid name in \"data\""), desc, rr->data);
181
182 size = enclen;
183 r->length += SIZE16 + size;
184
185 if (!(dest = rdata_enlarge(t, SIZE16 + size)))
186 return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
187
188 DNS_PUT16(dest, size);
189 DNS_PUT(dest, enc, enclen);
190 return (0);
191 }
192 /*--- reply_add_generic_rr() --------------------------------------------------------------------*/
193
194
195 /**************************************************************************************************
196 REPLY_ADD_A
197 Adds an A record to the reply.
198 Returns the numeric offset of the start of this record within the reply, or -1 on error.
199 **************************************************************************************************/
200 static inline int
reply_add_a(TASK * t,RR * r)201 reply_add_a(TASK *t, RR *r)
202 {
203 char *dest;
204 int size;
205 MYDNS_RR *rr = (MYDNS_RR *)r->rr;
206 struct in_addr addr;
207 uint32_t ip;
208
209 if (inet_pton(AF_INET, rr->data, (void *)&addr) <= 0)
210 {
211 dnserror(t, DNS_RCODE_SERVFAIL, ERR_INVALID_ADDRESS);
212 return rr_error(r->id, "rr %u: %s (A %s) (address=\"%s\")", r->id,
213 _("invalid address in \"data\""), _("record"), rr->data);
214 }
215 ip = ntohl(addr.s_addr);
216
217 #if DEBUG_ENABLED && DEBUG_REPLY
218 Debug("%s: REPLY_ADD: `%s' IN A %s", desctask(t), r->name, inet_ntoa(addr));
219 #endif
220 if (reply_start_rr(t, r, r->name, DNS_QTYPE_A, rr->ttl, "A") < 0)
221 return (-1);
222
223 size = SIZE32;
224 r->length += SIZE16 + size;
225
226 if (!(dest = rdata_enlarge(t, SIZE16 + size)))
227 return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
228
229 DNS_PUT16(dest, size);
230 DNS_PUT32(dest, ip);
231
232 return (0);
233 }
234 /*--- reply_add_a() -----------------------------------------------------------------------------*/
235
236
237 /**************************************************************************************************
238 REPLY_ADD_AAAA
239 Adds an AAAA record to the reply.
240 Returns the numeric offset of the start of this record within the reply, or -1 on error.
241 **************************************************************************************************/
242 static inline int
reply_add_aaaa(TASK * t,RR * r)243 reply_add_aaaa(TASK *t, RR *r)
244 {
245 char *dest;
246 int size;
247 MYDNS_RR *rr = (MYDNS_RR *)r->rr;
248 uint8_t addr[16];
249
250 #if DEBUG_ENABLED && DEBUG_REPLY
251 Debug("%s: REPLY_ADD: `%s' IN AAAA %s", desctask(t), r->name, rr->data);
252 #endif
253
254 if (inet_pton(AF_INET6, rr->data, (void *)&addr) <= 0)
255 {
256 dnserror(t, DNS_RCODE_SERVFAIL, ERR_INVALID_ADDRESS);
257 return rr_error(r->id, "rr %u: %s (AAAA %s) (address=\"%s\")", r->id,
258 _("invalid address in \"data\""), _("record"), rr->data);
259 }
260
261 if (reply_start_rr(t, r, r->name, DNS_QTYPE_AAAA, rr->ttl, "AAAA") < 0)
262 return (-1);
263
264 size = sizeof(uint8_t) * 16;
265 r->length += SIZE16 + size;
266
267 if (!(dest = rdata_enlarge(t, SIZE16 + size)))
268 return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
269
270 DNS_PUT16(dest, size);
271 memcpy(dest, &addr, size);
272 dest += size;
273
274 return (0);
275 }
276 /*--- reply_add_aaaa() --------------------------------------------------------------------------*/
277
278
279 /**************************************************************************************************
280 REPLY_ADD_HINFO
281 Adds an HINFO record to the reply.
282 Returns the numeric offset of the start of this record within the reply, or -1 on error.
283 **************************************************************************************************/
284 static inline int
reply_add_hinfo(TASK * t,RR * r)285 reply_add_hinfo(TASK *t, RR *r)
286 {
287 char *dest;
288 size_t oslen, cpulen;
289 MYDNS_RR *rr = (MYDNS_RR *)r->rr;
290 char os[DNS_MAXNAMELEN + 1] = "", cpu[DNS_MAXNAMELEN + 1] = "";
291
292 if (hinfo_parse(rr->data, cpu, os, DNS_MAXNAMELEN) < 0)
293 {
294 dnserror(t, DNS_RCODE_SERVFAIL, ERR_RR_NAME_TOO_LONG);
295 return rr_error(r->id, "rr %u: %s (HINFO %s) (data=\"%s\")", r->id,
296 _("name too long in \"data\""), _("record"), rr->data);
297 }
298
299 #if DEBUG_ENABLED && DEBUG_REPLY
300 Debug("%s: REPLY_ADD: `%s' IN HINFO `%s %s'", desctask(t), r->name, cpu, os);
301 #endif
302 cpulen = strlen(cpu);
303 oslen = strlen(os);
304
305 if (reply_start_rr(t, r, r->name, DNS_QTYPE_HINFO, rr->ttl, "HINFO") < 0)
306 return (-1);
307
308 r->length += SIZE16 + cpulen + oslen + 2;
309
310 if (!(dest = rdata_enlarge(t, SIZE16 + cpulen + SIZE16 + oslen)))
311 return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
312
313 DNS_PUT16(dest, cpulen + oslen + 2);
314
315 *dest++ = cpulen;
316 memcpy(dest, cpu, cpulen);
317 dest += cpulen;
318
319 *dest++ = oslen;
320 memcpy(dest, os, oslen);
321 dest += oslen;
322
323 return (0);
324 }
325 /*--- reply_add_hinfo() -------------------------------------------------------------------------*/
326
327
328 /**************************************************************************************************
329 REPLY_ADD_MX
330 Adds an MX record to the reply.
331 Returns the numeric offset of the start of this record within the reply, or -1 on error.
332 **************************************************************************************************/
333 static inline int
reply_add_mx(TASK * t,RR * r)334 reply_add_mx(TASK *t, RR *r)
335 {
336 char enc[DNS_MAXNAMELEN+1], *dest;
337 int size, enclen;
338 MYDNS_RR *rr = (MYDNS_RR *)r->rr;
339
340 #if DEBUG_ENABLED && DEBUG_REPLY
341 Debug("%s: REPLY_ADD: `%s' IN MX `%u %s'", desctask(t), r->name, (uint16_t)rr->aux, rr->data);
342 #endif
343
344 if (reply_start_rr(t, r, r->name, DNS_QTYPE_MX, rr->ttl, "MX") < 0)
345 return (-1);
346
347 if ((enclen = name_encode(t, enc, rr->data, CUROFFSET(t) + SIZE16, 1)) < 0)
348 return rr_error(r->id, "rr %u: %s (MX %s) (data=\"%s\")", r->id,
349 _("invalid name in \"data\""), _("record"), rr->data);
350
351 size = SIZE16 + enclen;
352 r->length += SIZE16 + size;
353
354 if (!(dest = rdata_enlarge(t, SIZE16 + size)))
355 return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
356
357 DNS_PUT16(dest, size);
358 DNS_PUT16(dest, (uint16_t)rr->aux);
359 DNS_PUT(dest, enc, enclen);
360 return (0);
361 }
362 /*--- reply_add_mx() ----------------------------------------------------------------------------*/
363
364
365 /**************************************************************************************************
366 REPLY_ADD_NAPTR
367 Adds an NAPTR record to the reply.
368 Returns the numeric offset of the start of this record within the reply, or -1 on error.
369 **************************************************************************************************/
370 static inline int
reply_add_naptr(TASK * t,RR * r)371 reply_add_naptr(TASK *t, RR *r)
372 {
373 MYDNS_RR *rr = (MYDNS_RR *)r->rr;
374 size_t flags_len, service_len, regex_len;
375 char enc[DNS_MAXNAMELEN+1], *dest;
376 int size, enclen, offset;
377
378 #if DEBUG_ENABLED && DEBUG_REPLY
379 Debug("%s: REPLY_ADD: `%s' IN NAPTR `%u %u \"%s\" \"%s\" \"%s\" \"%s\"'", desctask(t),
380 r->name, rr->naptr_order, rr->naptr_pref, rr->naptr_flags, rr->naptr_service,
381 rr->naptr_regex, rr->naptr_replacement);
382 #endif
383
384 flags_len = strlen(rr->naptr_flags);
385 service_len = strlen(rr->naptr_service);
386 regex_len = strlen(rr->naptr_regex);
387
388 if (reply_start_rr(t, r, r->name, DNS_QTYPE_NAPTR, rr->ttl, "NAPTR") < 0)
389 return (-1);
390
391 /* We are going to write "something else" and then a name, just like an MX record or something.
392 In this case, though, the "something else" is lots of data. Calculate the size of
393 "something else" in 'offset' */
394 offset = SIZE16 + SIZE16 + 1 + flags_len + 1 + service_len + 1 + regex_len;
395
396 /* Encode the name at the offset */
397 if ((enclen = name_encode(t, enc, rr->naptr_replacement, CUROFFSET(t) + offset, 1)) < 0)
398 return rr_error(r->id, "rr %u: %s (NAPTR %s) (%s=\"%s\")", r->id,
399 _("invalid name in \"replacement\""), _("record"), _("replacement"),
400 rr->naptr_replacement);
401
402 size = offset + enclen;
403 r->length += SIZE16 + size;
404
405 if (!(dest = rdata_enlarge(t, SIZE16 + size)))
406 return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
407
408 DNS_PUT16(dest, size);
409 DNS_PUT16(dest, (uint16_t)rr->naptr_order);
410 DNS_PUT16(dest, (uint16_t)rr->naptr_pref);
411
412 *dest++ = flags_len;
413 memcpy(dest, rr->naptr_flags, flags_len);
414 dest += flags_len;
415
416 *dest++ = service_len;
417 memcpy(dest, rr->naptr_service, service_len);
418 dest += service_len;
419
420 *dest++ = regex_len;
421 memcpy(dest, rr->naptr_regex, regex_len);
422 dest += regex_len;
423
424 DNS_PUT(dest, enc, enclen);
425
426 return (0);
427 }
428 /*--- reply_add_naptr() -------------------------------------------------------------------------*/
429
430
431 /**************************************************************************************************
432 REPLY_ADD_RP
433 Adds an RP record to the reply.
434 Returns the numeric offset of the start of this record within the reply, or -1 on error.
435 **************************************************************************************************/
436 static inline int
reply_add_rp(TASK * t,RR * r)437 reply_add_rp(TASK *t, RR *r)
438 {
439 char *mbox, *txt, *dest;
440 char encmbox[DNS_MAXNAMELEN+1], enctxt[DNS_MAXNAMELEN+1];
441 int size, mboxlen, txtlen;
442 MYDNS_RR *rr = (MYDNS_RR *)r->rr;
443
444 mbox = rr->data;
445 txt = rr->rp_txt;
446
447 #if DEBUG_ENABLED && DEBUG_REPLY
448 Debug("%s: REPLY_ADD: `%s' IN RP `%s %s'", desctask(t), r->name, mbox, txt);
449 #endif
450
451 if (reply_start_rr(t, r, r->name, DNS_QTYPE_RP, rr->ttl, "RP") < 0)
452 return (-1);
453
454 if ((mboxlen = name_encode(t, encmbox, mbox, CUROFFSET(t), 1)) < 0)
455 return rr_error(r->id, "rr %u: %s (RP %s) (mbox=\"%s\")", r->id,
456 _("invalid name in \"mbox\""), _("record"), mbox);
457
458 if ((txtlen = name_encode(t, enctxt, txt, CUROFFSET(t) + mboxlen, 1)) < 0)
459 return rr_error(r->id, "rr %u: %s (RP %s) (txt=\"%s\")", r->id,
460 _("invalid name in \"txt\""), _("record"), txt);
461
462 size = mboxlen + txtlen;
463 r->length += SIZE16 + size;
464
465 if (!(dest = rdata_enlarge(t, SIZE16 + size)))
466 return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
467
468 DNS_PUT16(dest, size);
469 DNS_PUT(dest, encmbox, mboxlen);
470 DNS_PUT(dest, enctxt, txtlen);
471 return (0);
472 }
473 /*--- reply_add_rp() ----------------------------------------------------------------------------*/
474
475
476 /**************************************************************************************************
477 REPLY_ADD_SOA
478 Add a SOA record to the reply.
479 Returns the numeric offset of the start of this record within the reply, or -1 on error.
480 **************************************************************************************************/
481 static inline int
reply_add_soa(TASK * t,RR * r)482 reply_add_soa(TASK *t, RR *r)
483 {
484 char *dest, ns[DNS_MAXNAMELEN+1], mbox[DNS_MAXNAMELEN+1];
485 int size, nslen, mboxlen;
486 MYDNS_SOA *soa = (MYDNS_SOA *)r->rr;
487
488 #if DEBUG_ENABLED && DEBUG_REPLY
489 Debug("%s: REPLY_ADD: `%s' IN SOA (mbox=[%s])", desctask(t), soa->origin, soa->mbox);
490 #endif
491
492 if (reply_start_rr(t, r, r->name, DNS_QTYPE_SOA, soa->ttl, "SOA") < 0)
493 return (-1);
494
495 if ((nslen = name_encode(t, ns, soa->ns, CUROFFSET(t), 1)) < 0)
496 return rr_error(r->id, "rr %u: %s (SOA %s) (ns=\"%s\")", r->id,
497 _("invalid name in \"ns\""), _("record"), soa->ns);
498
499 if ((mboxlen = name_encode(t, mbox, soa->mbox, CUROFFSET(t) + nslen, 1)) < 0)
500 return rr_error(r->id, "rr %u: %s (SOA %s) (mbox=\"%s\")", r->id,
501 _("invalid name in \"mbox\""), _("record"), soa->mbox);
502
503 size = nslen + mboxlen + (SIZE32 * 5);
504 r->length += SIZE16 + size;
505
506 if (!(dest = rdata_enlarge(t, SIZE16 + size)))
507 return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
508
509 DNS_PUT16(dest, size);
510 DNS_PUT(dest, ns, nslen);
511 DNS_PUT(dest, mbox, mboxlen);
512 DNS_PUT32(dest, soa->serial);
513 DNS_PUT32(dest, soa->refresh);
514 DNS_PUT32(dest, soa->retry);
515 DNS_PUT32(dest, soa->expire);
516 DNS_PUT32(dest, soa->minimum);
517 return (0);
518 }
519 /*--- reply_add_soa() ---------------------------------------------------------------------------*/
520
521
522 /**************************************************************************************************
523 REPLY_ADD_SRV
524 Adds a SRV record to the reply.
525 Returns the numeric offset of the start of this record within the reply, or -1 on error.
526 **************************************************************************************************/
527 static inline int
reply_add_srv(TASK * t,RR * r)528 reply_add_srv(TASK *t, RR *r)
529 {
530 char enc[DNS_MAXNAMELEN+1], *dest;
531 int size, enclen;
532 MYDNS_RR *rr = (MYDNS_RR *)r->rr;
533
534 #if DEBUG_ENABLED && DEBUG_REPLY
535 Debug("%s: REPLY_ADD: `%s' IN SRV `%u %u %u %s'",
536 desctask(t), r->name, (uint16_t)rr->aux, rr->srv_weight, rr->srv_port, rr->data);
537 #endif
538
539 if (reply_start_rr(t, r, r->name, DNS_QTYPE_SRV, rr->ttl, "SRV") < 0)
540 return (-1);
541
542 /* RFC 2782 says that we can't use name compression on this field... */
543 /* Arnt Gulbrandsen advises against using compression in the SRV target, although
544 most clients should support it */
545 if ((enclen = name_encode(t, enc, rr->data, CUROFFSET(t) + SIZE16 + SIZE16 + SIZE16, 0)) < 0)
546 return rr_error(r->id, "rr %u: %s (SRV %s) (data=\"%s\")", r->id,
547 _("invalid name in \"data\""), _("record"), rr->data);
548
549 size = SIZE16 + SIZE16 + SIZE16 + enclen;
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 DNS_PUT16(dest, (uint16_t)rr->aux);
557 DNS_PUT16(dest, (uint16_t)rr->srv_weight);
558 DNS_PUT16(dest, (uint16_t)rr->srv_port);
559 DNS_PUT(dest, enc, enclen);
560 return (0);
561 }
562 /*--- reply_add_srv() ---------------------------------------------------------------------------*/
563
564
565 /**************************************************************************************************
566 REPLY_ADD_TXT
567 Adds a TXT record to the reply.
568 Returns the numeric offset of the start of this record within the reply, or -1 on error.
569 **************************************************************************************************/
570 static inline int
reply_add_txt(TASK * t,RR * r)571 reply_add_txt(TASK *t, RR *r)
572 {
573 char *dest;
574 char size;
575 size_t len;
576 MYDNS_RR *rr = (MYDNS_RR *)r->rr;
577
578 #if DEBUG_ENABLED && DEBUG_REPLY
579 Debug("%s: REPLY_ADD: `%s' IN TXT", desctask(t), r->name);
580 #endif
581 len = strlen(rr->data);
582
583 if (reply_start_rr(t, r, r->name, DNS_QTYPE_TXT, rr->ttl, "TXT") < 0)
584 return (-1);
585
586 size = len + 1;
587 r->length += SIZE16 + size;
588
589 if (!(dest = rdata_enlarge(t, SIZE16 + size)))
590 return dnserror(t, DNS_RCODE_SERVFAIL, ERR_INTERNAL);
591
592 DNS_PUT16(dest, size);
593 *dest++ = len;
594 memcpy(dest, rr->data, len);
595 dest += len;
596 return (0);
597 }
598 /*--- reply_add_txt() ---------------------------------------------------------------------------*/
599
600
601 /**************************************************************************************************
602 REPLY_PROCESS_RRLIST
603 Adds each resource record found in `rrlist' to the reply.
604 **************************************************************************************************/
605 static int
reply_process_rrlist(TASK * t,RRLIST * rrlist)606 reply_process_rrlist(TASK *t, RRLIST *rrlist)
607 {
608 register RR *r;
609
610 if (!rrlist)
611 return (0);
612
613 for (r = rrlist->head; r; r = r->next)
614 {
615 switch (r->rrtype)
616 {
617 case DNS_RRTYPE_SOA:
618 if (reply_add_soa(t, r) < 0)
619 return (-1);
620 break;
621
622 case DNS_RRTYPE_RR:
623 {
624 MYDNS_RR *rr = (MYDNS_RR *)r->rr;
625
626 if (!rr)
627 break;
628
629 switch (rr->type)
630 {
631 case DNS_QTYPE_A:
632 if (reply_add_a(t, r) < 0)
633 return (-1);
634 break;
635
636 case DNS_QTYPE_AAAA:
637 if (reply_add_aaaa(t, r) < 0)
638 return (-1);
639 break;
640
641 case DNS_QTYPE_CNAME:
642 if (reply_add_generic_rr(t, r, "CNAME") < 0)
643 return (-1);
644 break;
645
646 case DNS_QTYPE_HINFO:
647 if (reply_add_hinfo(t, r) < 0)
648 return (-1);
649 break;
650
651 case DNS_QTYPE_MX:
652 if (reply_add_mx(t, r) < 0)
653 return (-1);
654 break;
655
656 case DNS_QTYPE_NAPTR:
657 if (reply_add_naptr(t, r) < 0)
658 return (-1);
659 break;
660
661 case DNS_QTYPE_NS:
662 if (reply_add_generic_rr(t, r, "NS") < 0)
663 return (-1);
664 break;
665
666 case DNS_QTYPE_PTR:
667 if (reply_add_generic_rr(t, r, "PTR") < 0)
668 return (-1);
669 break;
670
671 case DNS_QTYPE_RP:
672 if (reply_add_rp(t, r) < 0)
673 return (-1);
674 break;
675
676 case DNS_QTYPE_SRV:
677 if (reply_add_srv(t, r) < 0)
678 return (-1);
679 break;
680
681 case DNS_QTYPE_TXT:
682 if (reply_add_txt(t, r) < 0)
683 return (-1);
684 break;
685
686 default:
687 Warnx("%s: %s: %s", desctask(t), mydns_qtype_str(rr->type),
688 _("unsupported resource record type"));
689 }
690 }
691 break;
692 }
693 }
694 return (0);
695 }
696 /*--- reply_process_rrlist() --------------------------------------------------------------------*/
697
698
699 /**************************************************************************************************
700 TRUNCATE_RRLIST
701 Returns new count of items in this list.
702 The TC flag is _not_ set if data was truncated from the ADDITIONAL section.
703 **************************************************************************************************/
704 static int
truncate_rrlist(TASK * t,off_t maxpkt,RRLIST * rrlist,datasection_t ds)705 truncate_rrlist(TASK *t, off_t maxpkt, RRLIST *rrlist, datasection_t ds)
706 {
707 register RR *rr;
708 register int recs;
709 #if DEBUG_ENABLED && DEBUG_REPLY
710 int orig_recs = rrlist->size;
711 #endif
712
713 /* Warn about truncated packets, but only if TCP is not enabled. Most resolvers will try
714 TCP if a UDP packet is truncated. */
715 if (!tcp_enabled)
716 Verbose("%s: %s", desctask(t), _("query truncated"));
717
718 recs = rrlist->size;
719 for (rr = rrlist->head; rr; rr = rr->next)
720 {
721 if (rr->offset + rr->length >= maxpkt)
722 {
723 recs--;
724 if (ds != ADDITIONAL)
725 t->hdr.tc = 1;
726 }
727 else
728 t->rdlen += rr->length;
729 }
730 #if DEBUG_ENABLED && DEBUG_REPLY
731 Debug("%s section truncated from %d records to %d records",
732 reply_datasection_str[ds], orig_recs, recs);
733 #endif
734 return (recs);
735 }
736 /*--- truncate_rrlist() -------------------------------------------------------------------------*/
737
738
739 /**************************************************************************************************
740 REPLY_CHECK_TRUNCATION
741 If this reply would be truncated, removes any RR's that won't fit and sets the truncation flag.
742 **************************************************************************************************/
743 static void
reply_check_truncation(TASK * t,int * ancount,int * nscount,int * arcount)744 reply_check_truncation(TASK *t, int *ancount, int *nscount, int *arcount)
745 {
746 size_t maxpkt = (t->protocol == SOCK_STREAM ? DNS_MAXPACKETLEN_TCP : DNS_MAXPACKETLEN_UDP);
747 size_t maxrd = maxpkt - (DNS_HEADERSIZE + t->qdlen);
748
749 if (t->rdlen <= maxrd)
750 return;
751
752 #if DEBUG_ENABLED && DEBUG_REPLY
753 Debug("reply_check_truncation() needs to truncate reply (%d) to fit packet max (%d)",
754 t->rdlen, maxrd);
755 #endif
756
757 /* Loop through an/ns/ar sections, truncating as necessary, and updating counts */
758 t->rdlen = 0;
759 *ancount = truncate_rrlist(t, maxpkt, &t->an, ANSWER);
760 *nscount = truncate_rrlist(t, maxpkt, &t->ns, AUTHORITY);
761 *arcount = truncate_rrlist(t, maxpkt, &t->ar, ADDITIONAL);
762 }
763 /*--- reply_check_truncation() ------------------------------------------------------------------*/
764
765
766 /**************************************************************************************************
767 BUILD_CACHE_REPLY
768 Builds reply data from cached answer.
769 **************************************************************************************************/
770 void
build_cache_reply(TASK * t)771 build_cache_reply(TASK *t)
772 {
773 char *dest = t->reply;
774
775 DNS_PUT16(dest, t->id); /* Query ID */
776 DNS_PUT(dest, &t->hdr, SIZE16); /* Header */
777 }
778 /*--- build_cache_reply() -----------------------------------------------------------------------*/
779
780
781 /**************************************************************************************************
782 BUILD_REPLY
783 Given a task, constructs the reply data.
784 **************************************************************************************************/
785 void
build_reply(TASK * t,int want_additional)786 build_reply(TASK *t, int want_additional)
787 {
788 char *dest;
789 int ancount, nscount, arcount;
790
791 /* Add data to ADDITIONAL section */
792 if (want_additional)
793 {
794 reply_add_additional(t, &t->an, ANSWER);
795 reply_add_additional(t, &t->ns, AUTHORITY);
796 }
797
798 /* Sort records where necessary */
799 if (t->an.a_records > 1) /* ANSWER section: Sort A/AAAA records */
800 sort_a_recs(t, &t->an, ANSWER);
801 if (t->an.mx_records > 1) /* ANSWER section: Sort MX records */
802 sort_mx_recs(t, &t->an, ANSWER);
803 if (t->an.srv_records > 1) /* ANSWER section: Sort SRV records */
804 sort_srv_recs(t, &t->an, ANSWER);
805 if (t->ar.a_records > 1) /* AUTHORITY section: Sort A/AAAA records */
806 sort_a_recs(t, &t->ar, AUTHORITY);
807
808 /* Build `rdata' containing resource records in ANSWER, AUTHORITY, and ADDITIONAL */
809 t->replylen = DNS_HEADERSIZE + t->qdlen + t->rdlen;
810 if (reply_process_rrlist(t, &t->an) || reply_process_rrlist(t, &t->ns) || reply_process_rrlist(t, &t->ar))
811 {
812 /* Empty RR lists */
813 rrlist_free(&t->an);
814 rrlist_free(&t->ns);
815 rrlist_free(&t->ar);
816
817 /* Make sure reply is empty */
818 t->replylen = 0;
819 t->rdlen = 0;
820 Free(t->rdata);
821 }
822
823 ancount = t->an.size;
824 nscount = t->ns.size;
825 arcount = t->ar.size;
826
827 /* Verify reply length */
828 reply_check_truncation(t, &ancount, &nscount, &arcount);
829
830 /* Make sure header bits are set correctly */
831 t->hdr.qr = 1;
832 t->hdr.cd = 0;
833
834 /* Construct the reply */
835 t->replylen = DNS_HEADERSIZE + t->qdlen + t->rdlen;
836 dest = t->reply = malloc(t->replylen);
837 if (!t->reply)
838 Err(_("out of memory"));
839
840 DNS_PUT16(dest, t->id); /* Query ID */
841 DNS_PUT(dest, &t->hdr, SIZE16); /* Header */
842 DNS_PUT16(dest, t->qdcount); /* QUESTION count */
843 DNS_PUT16(dest, ancount); /* ANSWER count */
844 DNS_PUT16(dest, nscount); /* AUTHORITY count */
845 DNS_PUT16(dest, arcount); /* ADDITIONAL count */
846 if (t->qdlen && t->qd)
847 DNS_PUT(dest, t->qd, t->qdlen); /* Data for QUESTION section */
848 DNS_PUT(dest, t->rdata, t->rdlen); /* Resource record data */
849
850 #if DEBUG_ENABLED && DEBUG_REPLY
851 Debug("%s: reply: id = %u", desctask(t), t->id);
852 Debug("%s: reply: qr = %u (message is a %s)", desctask(t), t->hdr.qr, t->hdr.qr ? "response" : "query");
853 Debug("%s: reply: opcode = %u (%s)", desctask(t), t->hdr.opcode, mydns_opcode_str(t->hdr.opcode));
854 Debug("%s: reply: aa = %u (answer %s)", desctask(t), t->hdr.aa, t->hdr.aa ? "is authoritative" : "not authoritative");
855 Debug("%s: reply: tc = %u (message %s)", desctask(t), t->hdr.tc, t->hdr.tc ? "truncated" : "not truncated");
856 Debug("%s: reply: rd = %u (%s)", desctask(t), t->hdr.rd, t->hdr.rd ? "recursion desired" : "no recursion");
857 Debug("%s: reply: ra = %u (recursion %s)", desctask(t), t->hdr.ra, t->hdr.ra ? "available" : "unavailable");
858 Debug("%s: reply: rcode = %u (%s)", desctask(t), t->hdr.rcode, mydns_rcode_str(t->hdr.rcode));
859 /* escdata(t->reply, t->replylen); */
860 #endif
861 }
862 /*--- build_reply() -----------------------------------------------------------------------------*/
863
864 /* vi:set ts=3: */
865 /* NEED_PO */
866