1 /* dns.c - Declarations for dns handling and generic dns functions
2
3 Copyright (C) 2000, 2001 Thomas Moestl
4 Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2011 Paul A. Rombouts
5
6 This file is part of the pdnsd package.
7
8 pdnsd is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
13 pdnsd is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with pdnsd; see the file COPYING. If not, see
20 <http://www.gnu.org/licenses/>.
21 */
22
23 #include <config.h>
24 #include <ctype.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include "error.h"
29 #include "helpers.h"
30 #include "dns.h"
31
32
33 /* Decompress a name record, taking the whole message as msg, returning its results in tgt
34 * (which should be able hold at least DNSNAMEBUFSIZE chars),
35 * taking sz as the remaining msg size (it is returned decremented by the name length, ready for further use) and
36 * a source pointer (it is returned pointing to the location after the name). msgsize is the size of the whole message,
37 * len is the total name length.
38 * msg and msgsz are needed for decompression (see rfc1035). The returned data is decompressed, but still in the
39 * rr name form (length byte - string of that length, terminated by a 0 length byte).
40 *
41 * Returned is a dns return code, with one exception: RC_TRUNC, as defined in dns.h, indicates that the message is
42 * truncated at the name (which needs a special return code, as it might or might not be fatal).
43 */
decompress_name(unsigned char * msg,size_t msgsz,unsigned char ** src,size_t * sz,unsigned char * tgt,unsigned int * len)44 int decompress_name(unsigned char *msg, size_t msgsz, unsigned char **src, size_t *sz, unsigned char *tgt, unsigned int *len)
45 {
46 unsigned int lb,offs;
47 unsigned int hops=0,tpos=0;
48 unsigned char *lptr=*src;
49 size_t oldsz=*sz;
50 size_t newsz=oldsz;
51
52 if (newsz==0)
53 goto name_outside_data;
54 if (lptr-msg>=msgsz)
55 goto name_outside_msg;
56
57 for(;;) {
58 newsz--;
59 lb=*lptr++;
60
61 if(lb>0x3f) {
62 if (lb<0xc0) /* The two highest bits must be either 00 or 11 */
63 goto unsupported_lbl_bits;
64 if (newsz==0)
65 goto name_outside_data;
66 if (lptr-msg>=msgsz)
67 goto name_outside_msg;
68 newsz--;
69 offs=((lb&0x3f)<<8)|(*lptr);
70 if (offs>=msgsz)
71 goto offset_outside_msg;
72 lptr=msg+offs;
73 goto jumped;
74 }
75 tgt[tpos++]=lb;
76 if (lb==0)
77 break;
78
79 if (newsz<=lb)
80 goto name_outside_data;
81 if (lptr+lb-msg>=msgsz)
82 goto name_outside_msg;
83 if (tpos+lb>DNSNAMEBUFSIZE-1) /* terminating null byte has to follow */
84 goto name_buf_full;
85 newsz -= lb;
86 do {
87 /* if (!*lptr || *lptr=='.')
88 return RC_FORMAT; */
89 tgt[tpos++]=*lptr++;
90 } while(--lb);
91 }
92 goto return_OK;
93
94 jumped:
95 ++hops;
96 for(;;) {
97 lb=*lptr++;
98
99 while(lb>0x3f) {
100 if (lb<0xc0) /* The two highest bits must be either 00 or 11 */
101 goto unsupported_lbl_bits;
102 if (lptr-msg>=msgsz)
103 goto name_outside_msg;
104 if (++hops>255)
105 goto too_many_hops;
106 offs=((lb&0x3f)<<8)|(*lptr);
107 if (offs>=msgsz)
108 goto offset_outside_msg;
109 lptr=msg+offs;
110 lb=*lptr++;
111 }
112 tgt[tpos++]=lb;
113 if (lb==0)
114 break;
115
116 if (lptr+lb-msg>=msgsz)
117 goto name_outside_msg;
118 if(tpos+lb>DNSNAMEBUFSIZE-1) /* terminating null byte has to follow */
119 goto name_buf_full;
120 do {
121 /* if (!*lptr || *lptr=='.')
122 return RC_FORMAT; */
123 tgt[tpos++]=*lptr++;
124 } while(--lb);
125 }
126 return_OK:
127 *src += oldsz-newsz;
128 *sz = newsz;
129 if(len) *len=tpos;
130 return RC_OK;
131
132 name_outside_data:
133 DEBUG_MSG("decompress_name: compressed name extends outside data field.\n");
134 return RC_TRUNC;
135
136 name_outside_msg:
137 DEBUG_MSG("decompress_name: compressed name extends outside message.\n");
138 return RC_FORMAT;
139
140 unsupported_lbl_bits:
141 DEBUG_MSG(lb==0x41?"decompress_name: Bit-string labels not supported.\n":
142 "decompress_name: unsupported label type.\n");
143 return RC_FORMAT;
144
145 offset_outside_msg:
146 DEBUG_MSG("decompress_name: offset points outside message.\n");
147 return RC_FORMAT;
148
149 name_buf_full:
150 DEBUG_MSG("decompress_name: decompressed name larger than %u bytes.\n", DNSNAMEBUFSIZE);
151 return RC_FORMAT;
152
153 too_many_hops:
154 DEBUG_MSG("decompress_name: too many offsets in compressed name.\n");
155 return RC_FORMAT;
156 }
157
158 #if 0
159 /* Compare two names (ordinary C-strings) back-to-forth and return the longest match.
160 The comparison is done at name granularity.
161 The return value is the length of the match in name elements.
162 *os (*od) is set to the offset in the domain name ms (md) of the match.
163 */
164 int domain_name_match(const unsigned char *ms, const unsigned char *md, int *os, int *od)
165 {
166 int i,j,k=0,offs,offd;
167
168 offs=i=strlen(ms); offd=j=strlen(md);
169 if(i && ms[i-1]=='.') --offs;
170 if(j && md[j-1]=='.') --offd;
171
172 if(i==0 || (i==1 && *ms=='.') || j==0 || (j==1 && *md=='.'))
173 /* Special case: root domain */
174 ;
175 else {
176 --i; if(ms[i]=='.') --i;
177 --j; if(md[j]=='.') --j;
178 while(tolower(ms[i]) == tolower(md[j])) {
179 if(ms[i]=='.') {
180 ++k;
181 offs=i+1; offd=j+1;
182 }
183 if(i==0 || j==0) {
184 if((i==0 || ms[i-1]=='.') && (j==0 || md[j-1]=='.')) {
185 ++k;
186 offs=i; offd=j;
187 }
188 break;
189 }
190 --i; --j;
191 }
192 }
193 if(os) *os=offs;
194 if(od) *od=offd;
195 return k;
196 }
197 #endif
198
199 /* Compare the names (in length byte-string notation) back-to-forth and return the longest match.
200 The comparison is done at name granularity.
201 The return value is the length of the match in name elements.
202 *os (*od) is set to the offset in the domain name ms (md) of the match.
203 */
domain_match(const unsigned char * ms,const unsigned char * md,unsigned int * os,unsigned int * od)204 unsigned int domain_match(const unsigned char *ms, const unsigned char *md, unsigned int *os, unsigned int *od)
205 {
206 unsigned int i,j,k,n,ns=0,nd=0,offs,offd;
207 unsigned char lb,ls[128],ld[128];
208
209 /* first collect all length bytes */
210 i=0;
211 while((lb=ms[i])) {
212 PDNSD_ASSERT(ns<128, "domain_match: too many name segments");
213 ls[ns++]=lb;
214 i += ((unsigned)lb)+1;
215 }
216
217 j=0;
218 while((lb=md[j])) {
219 PDNSD_ASSERT(nd<128, "domain_match: too many name segments");
220 ld[nd++]=lb;
221 j += ((unsigned)lb)+1;
222 }
223
224 n=ns; if(n>nd) n=nd;
225
226 for(k=1; offs=i,offd=j,k<=n; ++k) {
227 lb=ls[ns-k];
228 if(lb!=ld[nd-k]) goto mismatch;
229 for(;lb;--lb)
230 if(tolower(ms[--i]) != tolower(md[--j])) goto mismatch;
231 --i; --j;
232 }
233 mismatch:
234
235 if(os) *os=offs;
236 if(od) *od=offd;
237 return k-1;
238 }
239
240 /* compress the domain name in in and put the result (of maximum length of rhnlen(in)) and
241 * fill cb with compression information for further strings.*cb may be NULL initially.
242 * offs is the offset the generated string will be placed in the packet.
243 * retval: 0 - error, otherwise length
244 * When done, just free() cb (if it is NULL, free will behave correctly).
245 * It is guaranteed (and insured by assertions) that the output is smaller or equal in
246 * size to the input.
247 */
compress_name(unsigned char * in,unsigned char * out,unsigned int offs,dlist * cb)248 unsigned int compress_name(unsigned char *in, unsigned char *out, unsigned int offs, dlist *cb)
249 {
250 compel_t *ci;
251 unsigned int longest=0,lrem=0,coffs=0;
252 unsigned int rl=0;
253 unsigned ilen = rhnlen(in);
254 unsigned short add=1;
255
256 PDNSD_ASSERT(ilen<=DNSNAMEBUFSIZE, "compress_name: name too long");
257
258 /* part 1: compression */
259 for (ci=dlist_first(*cb); ci; ci=dlist_next(ci)) {
260 unsigned int rv,rem,to;
261 if ((rv=domain_match(in, ci->s, &rem,&to))>longest) {
262 /*
263 * This has some not obvious implications that should be noted: If a
264 * domain name as saved in the list has been compressed, we only can
265 * index the non-compressed part. We rely here that the first occurence
266 * can't be compressed. So we take the first occurence of a given length.
267 * This works perfectly, but watch it if you change something.
268 */
269 unsigned int newoffs= ci->index + to;
270 /* Only use if the offset is not too large. */
271 if(newoffs<=0x3fff) {
272 longest=rv;
273 lrem=rem;
274 coffs= newoffs;
275 }
276 }
277 }
278 if (longest>0) {
279 PDNSD_ASSERT(lrem+2 <= ilen, "compress_name: length increased");
280 memcpy(out, in,lrem);
281 out[lrem]=0xc0|((coffs&0x3f00)>>8);
282 out[lrem+1]=coffs&0xff;
283 rl=lrem+2;
284 add= lrem!=0;
285 }
286 else {
287 memcpy(out,in,ilen);
288 rl=ilen;
289 }
290
291 /* part 2: addition to the cache structure */
292 if (add) {
293 if (!(*cb=dlist_grow(*cb,sizeof(compel_t)+ilen)))
294 return 0;
295 ci=dlist_last(*cb);
296 ci->index=offs;
297 memcpy(ci->s,in,ilen);
298 }
299 return rl;
300 }
301
302 /* Convert a numeric IP address into a domain name representation
303 (C string) suitable for PTR records.
304 buf is assumed to be at least DNSNAMEBUFSIZE bytes in size.
305 */
a2ptrstr(pdnsd_ca * a,int tp,unsigned char * buf)306 int a2ptrstr(pdnsd_ca *a, int tp, unsigned char *buf)
307 {
308 if(tp==T_A) {
309 unsigned char *p=(unsigned char *)&a->ipv4.s_addr;
310 int n=snprintf(charp buf,DNSNAMEBUFSIZE,"%u.%u.%u.%u.in-addr.arpa.",p[3],p[2],p[1],p[0]);
311 if(n<0 || n>=DNSNAMEBUFSIZE)
312 return 0;
313 }
314 else
315 #if ALLOW_LOCAL_AAAA
316 if(tp==T_AAAA) {
317 unsigned char *p=(unsigned char *)&a->ipv6;
318 int i,offs=0;
319 for (i=15;i>=0;--i) {
320 unsigned char bt=p[i];
321 int n=snprintf(charp(buf+offs), DNSNAMEBUFSIZE-offs,"%x.%x.",bt&0xf,(bt>>4)&0xf);
322 if(n<0) return 0;
323 offs+=n;
324 if(offs>=DNSNAMEBUFSIZE) return 0;
325 }
326 if(!strncp(charp(buf+offs),"ip6.arpa.",DNSNAMEBUFSIZE-offs))
327 return 0;
328 }
329 else
330 #endif
331 return 0;
332 return 1;
333 }
334
335 /*
336 * Add records for a host as read from a hosts-style file.
337 * Returns 1 on success, 0 in an out of memory condition, and -1 when there was a problem with
338 * the record data.
339 */
add_host(unsigned char * pn,unsigned char * rns,pdnsd_ca * a,int tp,int a_sz,time_t ttl,unsigned flags,int reverse)340 static int add_host(unsigned char *pn, unsigned char *rns, pdnsd_ca *a, int tp, int a_sz, time_t ttl, unsigned flags, int reverse)
341 {
342 dns_cent_t ce;
343
344 if (!init_cent(&ce, pn, 0, 0, flags DBG0))
345 return 0;
346 if (!add_cent_rr(&ce,tp,ttl,0,CF_LOCAL,a_sz,a DBG0))
347 goto free_cent_return0;
348 if (!add_cent_rr(&ce,T_NS,ttl,0,CF_LOCAL,rhnlen(rns),rns DBG0))
349 goto free_cent_return0;
350 add_cache(&ce);
351 free_cent(&ce DBG0);
352 if (reverse) {
353 unsigned char b2[DNSNAMEBUFSIZE],rhn[DNSNAMEBUFSIZE];
354 if(!a2ptrstr(a,tp,b2))
355 return -1;
356 if (!str2rhn(b2,rhn))
357 return -1;
358 if (!init_cent(&ce, rhn, 0, 0, flags DBG0))
359 return 0;
360 if (!add_cent_rr(&ce,T_PTR,ttl,0,CF_LOCAL,rhnlen(pn),pn DBG0))
361 goto free_cent_return0;
362 if (!add_cent_rr(&ce,T_NS,ttl,0,CF_LOCAL,rhnlen(rns),rns DBG0))
363 goto free_cent_return0;
364 add_cache(&ce);
365 free_cent(&ce DBG0);
366 }
367 return 1;
368
369 free_cent_return0:
370 free_cent(&ce DBG0);
371 return 0;
372 }
373
374 /*
375 * Read a file in /etc/hosts-format and add generate rrs for it.
376 * Errors are largely ignored so that we can skip entries we do not understand
377 * (but others possibly do).
378 */
read_hosts(const char * fn,unsigned char * rns,time_t ttl,unsigned flags,int aliases,char ** errstr)379 int read_hosts(const char *fn, unsigned char *rns, time_t ttl, unsigned flags, int aliases, char **errstr)
380 {
381 int rv=0;
382 FILE *f;
383 char *buf;
384 size_t buflen=256;
385
386 if (!(f=fopen(fn,"r"))) {
387 if(asprintf(errstr, "Failed to source %s: %s", fn, strerror(errno))<0) *errstr=NULL;
388 return 0;
389 }
390 buf=malloc(buflen);
391 if(!buf) {
392 *errstr=NULL;
393 goto fclose_return;
394 }
395 while(getline(&buf,&buflen,f)>=0) {
396 unsigned int len;
397 unsigned char *p,*pn,*pi;
398 unsigned char rhn[DNSNAMEBUFSIZE];
399 int tp,sz;
400 pdnsd_ca a;
401
402 p= ucharp strchr(buf,'#');
403 if(p) *p=0;
404 p= ucharp buf;
405 for(;;) {
406 if(!*p) goto nextline;
407 if(!isspace(*p)) break;
408 ++p;
409 }
410 pi=p;
411 do {
412 if(!*++p) goto nextline;
413 } while(!isspace(*p));
414 *p=0;
415 do {
416 if(!*++p) goto nextline;
417 } while (isspace(*p));
418 pn=p;
419 do {
420 ++p;
421 } while(*p && !isspace(*p));
422 len=p-pn;
423 if (parsestr2rhn(pn,len,rhn)!=NULL)
424 continue;
425 if (inet_aton(charp pi,&a.ipv4)) {
426 tp=T_A;
427 sz=sizeof(struct in_addr);
428 } else {
429 #if ALLOW_LOCAL_AAAA /* We don't read them otherwise, as the C library may not be able to to that.*/
430 if (inet_pton(AF_INET6,charp pi,&a.ipv6)>0) {
431 tp=T_AAAA;
432 sz=sizeof(struct in6_addr);
433 } else
434 #endif
435 continue;
436 }
437 {
438 int res=add_host(rhn, rns, &a, tp,sz, ttl, flags, 1);
439 if(res==0) {
440 *errstr= NULL;
441 goto cleanup_return;
442 }
443 else if(res<0)
444 continue;
445 }
446 if(aliases) {
447 for(;;) {
448 for(;;) {
449 if(!*p) goto nextline;
450 if(!isspace(*p)) break;
451 ++p;
452 }
453 pn=p;
454 do {
455 ++p;
456 } while(*p && !isspace(*p));
457 len=p-pn;
458 if (parsestr2rhn(pn,len,rhn)!=NULL)
459 break;
460 if (add_host(rhn, rns, &a, tp,sz, ttl, flags, 0) == 0) {
461 *errstr= NULL;
462 goto cleanup_return;
463 }
464 }
465 }
466 nextline:;
467 }
468 if (feof(f))
469 rv=1;
470 else if(asprintf(errstr, "Failed to source %s: %s", fn, strerror(errno))<0) *errstr=NULL;
471 cleanup_return:
472 free(buf);
473 fclose_return:
474 fclose(f);
475 return rv;
476 }
477
478
479 /* Get the name of an RR type given its value. */
getrrtpname(int tp)480 const char *getrrtpname(int tp)
481 {
482 return tp>=T_MIN && tp<=T_MAX? rrnames[tp-T_MIN]: "[unknown]";
483 }
484
485 #if DEBUG>0
486 /*
487 * Const decoders for debugging display
488 */
489 static const char *const c_names[C_NUM] = {"IN","CS","CH","HS"};
490 static const char *const qt_names[QT_NUM]={"IXFR","AXFR","MAILB","MAILA","*"};
491
get_cname(int id)492 const char *get_cname(int id)
493 {
494 if (id>=C_MIN && id<=C_MAX)
495 return c_names[id-C_MIN];
496 if (id==QC_ALL)
497 return "*";
498 return "[unknown]";
499 }
500
get_tname(int id)501 const char *get_tname(int id)
502 {
503 if (id>=T_MIN && id<=T_MAX)
504 return rrnames[id-T_MIN];
505 else if (id>=QT_MIN && id<=QT_MAX)
506 return qt_names[id-QT_MIN];
507 return "[unknown]";
508 }
509
510
511 #define NRC 17
512 static const char *const e_names[NRC]={
513 "no error",
514 "query format error",
515 "server failed",
516 "non-existent domain",
517 "not supported",
518 "query refused",
519 "name exists when it should not",
520 "RR set exists when it should not",
521 "RR set that should exist does not",
522 "server not authoritative for zone",
523 "name not contained in zone",
524 "11",
525 "12",
526 "13",
527 "14",
528 "15",
529 "bad OPT version"
530 };
531
get_ename(int id)532 const char *get_ename(int id)
533 {
534 if (id>=0 && id<NRC)
535 return e_names[id];
536 return "[unknown]";
537 }
538
539
540 /* Construct a human-readable string listing the flags that are set
541 in a dns header. buf must have a size of at least DNSFLAGSMAXSTRSIZE.
542 Used for debugging purposes only.
543 */
dnsflags2str(dns_hdr_t * hdr,char * buf)544 char *dnsflags2str(dns_hdr_t *hdr, char *buf)
545 {
546 char *p= buf;
547
548 if (hdr->aa)
549 p=mempcpy(p, " AA", 3);
550 if (hdr->tc)
551 p=mempcpy(p, " TC", 3);
552 if (hdr->rd)
553 p=mempcpy(p, " RD", 3);
554 if (hdr->ra)
555 p=mempcpy(p, " RA", 3);
556 if (hdr->z)
557 p=mempcpy(p, " Z", 2);
558 if (hdr->ad)
559 p=mempcpy(p, " AD", 3);
560 if (hdr->cd)
561 p=mempcpy(p, " CD", 3);
562 *p=0;
563
564 return buf;
565 }
566
567 #endif
568
569
570 #if DEBUG>=9
571 /* Based on debug code contributed by Kiyo Kelvin Lee. */
572
debug_dump_dns_msg(void * data,size_t len)573 void debug_dump_dns_msg(void *data, size_t len)
574 {
575 unsigned char *udata = (unsigned char *)data;
576 # define dmpchksz 16
577 char buf[dmpchksz*4+2];
578 size_t i, j, k, l;
579
580 DEBUG_MSG("pointer=%p len=%lu\n", udata, (unsigned long)len);
581
582 for (i = 0; i < len; i += dmpchksz) {
583 char *cp = buf;
584 k = l = i + dmpchksz;
585 if(k > len) k = len;
586 for (j = i; j < k; ++j) {
587 int n = sprintf(cp, "%02x ", udata[j]);
588 cp += n;
589 }
590 for (; j < l; ++j) {
591 *cp++ = ' ';
592 *cp++ = ' ';
593 *cp++ = ' ';
594 }
595 *cp++ = ' ';
596 for (j = i; j < k; ++j) {
597 *cp++ = isprint(udata[j]) ? udata[j] : '.';
598 }
599 PDNSD_ASSERT(cp < buf + sizeof(buf), "debug_dump_dns_msg: line buffer overflowed");
600 *cp = '\0';
601 DEBUG_MSG("%s\n", buf);
602 }
603
604 if(len >= sizeof(dns_hdr_t)) {
605 dns_hdr_t *hdr = (dns_hdr_t *)data;
606
607 DEBUG_MSG(
608 "id=%04x qr=%x opcode=%x aa=%x tc=%x rd=%x "
609 "ra=%x z=%x ad=%x cd=%x rcode=%x\n",
610 ntohs(hdr->id), hdr->qr, hdr->opcode, hdr->aa, hdr->tc, hdr->rd,
611 hdr->ra, hdr->z, hdr->ad, hdr->cd, hdr->rcode);
612 DEBUG_MSG(
613 "qdcount=%04x ancount=%04x nscount=%04x arcount=%04x\n",
614 ntohs(hdr->qdcount), ntohs(hdr->ancount), ntohs(hdr->nscount), ntohs(hdr->arcount));
615 }
616 }
617 #endif
618