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