1 /*
2 firedns.c - firedns library
3 Copyright (C) 2002 Ian Gulliver
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of version 2 of the GNU General Public License as
7 published by the Free Software Foundation.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18 
19 #define _FIREDNS_C
20 
21 #include <stdlib.h>
22 #include <time.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <sys/time.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <pthread.h>
32 #include <firestring.h>
33 #include "firemake.h"
34 #include "firedns.h"
35 
36 static const char tagstring[] = "$Id: firedns.c,v 1.81 2004/03/10 15:14:35 ian Exp $";
37 const char firedns_version[] = VERSION;
38 
39 /* MXPS protocol default ports */
40 const int firedns_mx_port[] = { 25, 209, 0, 0,
41 				0, 0, 0, 0,
42 				0, 0, 0, 0,
43 				0, 0, 0, 0 };
44 
45 const char *firedns_mx_name[] = { "SMTP", "QMTP", "Unknown", "Unknown",
46 					"Unknown", "Unknown", "Unknown", "Unknown",
47 					"Unknown", "Unknown", "Unknown", "Unknown",
48 					"Unknown", "Unknown", "Unknown", "Unknown" };
49 
50 #define max(a,b) (a > b ? a : b)
51 #define FDNS_MAX              8                    /* max number of nameservers used */
52 #define FDNS_CONFIG_PREF     "/etc/firedns.conf"   /* preferred firedns config file */
53 #define FDNS_CONFIG_FBCK     "/etc/resolv.conf"    /* fallback config file */
54 #define FDNS_PORT            53                    /* DNS well known port */
55 #define FDNS_QRY_A            1                    /* name to IP address */
56 #define FDNS_QRY_AAAA        28                    /* name to IP6 address */
57 #define FDNS_QRY_PTR         12                    /* IP address to name */
58 #define FDNS_QRY_MX          15                    /* name to MX */
59 #define FDNS_QRY_TXT         16                    /* name to TXT */
60 #define FDNS_QRY_CNAME       5
61 
62 #define FDNS_MXPS_START 12800
63 #define FDNS_MXPS_STOP  13055
64 
65 #define FIREDNS_ALIGN (sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long))
66 #define FIREDNS_TRIES 3
67 #define RESULTSIZE 1024
68 #define min(a,b) (a < b ? a : b)
69 
70 static struct in_addr servers4[FDNS_MAX]; /* up to FDNS_MAX nameservers; populated by firedns_init() */
71 static int i4; /* actual count of nameservers; set by firedns_init() */
72 #ifdef HAVE_IPV6
73 static int i6;
74 static struct in6_addr servers6[FDNS_MAX];
75 #endif
76 
77 static int initdone = 0; /* to ensure firedns_init() only runs once (on the first call) */
78 static int wantclose = 0;
79 static int lastcreate = -1;
80 
81 struct s_connection { /* open DNS query */
82 	struct s_connection *next; /* next in list */
83 	unsigned char id[2]; /* unique ID (random number), matches header ID; both set by firedns_add_query() */
84 	unsigned int class;
85 	unsigned int type;
86 	int want_list;
87 	int fd; /* file descriptor returned from sockets */
88 #ifdef HAVE_IPV6
89 	int v6;
90 #endif
91 };
92 
93 struct s_rr_middle {
94 	unsigned int type;
95 	unsigned int class;
96 	unsigned long ttl;
97 	unsigned int rdlength;
98 };
99 
100 #define FIREDNS_POINTER_VALUE 0xc000
101 
102 static pthread_mutex_t connlist_lock = PTHREAD_MUTEX_INITIALIZER;
103 static struct s_connection *connection_head = NULL; /* linked list of open DNS queries; populated by firedns_add_query(), decimated by firedns_getresult_s() */
104 
105 struct s_header { /* DNS query header */
106 	unsigned char id[2];
107 	unsigned int flags1;
108 #define FLAGS1_MASK_QR 0x80
109 #define FLAGS1_MASK_OPCODE 0x78 /* bitshift right 3 */
110 #define FLAGS1_MASK_AA 0x04
111 #define FLAGS1_MASK_TC 0x02
112 #define FLAGS1_MASK_RD 0x01
113 	unsigned int flags2;
114 #define FLAGS2_MASK_RA 0x80
115 #define FLAGS2_MASK_Z  0x70
116 #define FLAGS2_MASK_RCODE 0x0f
117 	unsigned int qdcount;
118 	unsigned int ancount;
119 	unsigned int nscount;
120 	unsigned int arcount;
121 	unsigned char payload[512]; /* DNS question, populated by firedns_build_query_payload() */
122 };
123 
firedns_align(void * inp)124 static inline void *firedns_align(void *inp) {
125 	char *p = inp;
126 	int offby = ((char *)p - (char *)0) % FIREDNS_ALIGN;
127 	if (offby != 0)
128 		return p + (FIREDNS_ALIGN - offby);
129 	else
130 		return p;
131 }
132 
133 /*
134  * These little hacks are here to avoid alignment and type sizing issues completely by doing manual copies
135  */
firedns_fill_rr(struct s_rr_middle * restrict const rr,const unsigned char * const restrict input)136 static inline void firedns_fill_rr(struct s_rr_middle * restrict const rr, const unsigned char * const restrict input) {
137 	rr->type = input[0] * 256 + input[1];
138 	rr->class = input[2] * 256 + input[3];
139 	rr->ttl = input[4] * 16777216 + input[5] * 65536 + input[6] * 256 + input[7];
140 	rr->rdlength = input[8] * 256 + input[9];
141 }
142 
firedns_fill_header(struct s_header * const restrict header,const unsigned char * const restrict input,const int l)143 static inline void firedns_fill_header(struct s_header * const restrict header, const unsigned char * const restrict input, const int l) {
144 	header->id[0] = input[0];
145 	header->id[1] = input[1];
146 	header->flags1 = input[2];
147 	header->flags2 = input[3];
148 	header->qdcount = input[4] * 256 + input[5];
149 	header->ancount = input[6] * 256 + input[7];
150 	header->nscount = input[8] * 256 + input[9];
151 	header->arcount = input[10] * 256 + input[11];
152 	memcpy(header->payload,&input[12],l);
153 }
154 
firedns_empty_header(unsigned char * const restrict output,const struct s_header * const restrict header,const int l)155 static inline void firedns_empty_header(unsigned char * const restrict output, const struct s_header * const restrict header, const int l) {
156 	output[0] = header->id[0];
157 	output[1] = header->id[1];
158 	output[2] = header->flags1;
159 	output[3] = header->flags2;
160 	output[4] = header->qdcount / 256;
161 	output[5] = header->qdcount % 256;
162 	output[6] = header->ancount / 256;
163 	output[7] = header->ancount % 256;
164 	output[8] = header->nscount / 256;
165 	output[9] = header->nscount % 256;
166 	output[10] = header->arcount / 256;
167 	output[11] = header->arcount % 256;
168 	memcpy(&output[12],header->payload,l);
169 }
170 
firedns_close(int fd)171 static inline void firedns_close(int fd) { /* close query */
172 	if (fd == lastcreate) {
173 		wantclose = 1;
174 		return;
175 	}
176 	close(fd);
177 	return;
178 }
179 
firedns_init()180 void firedns_init() { /* on first call only: populates servers4 (or -6) struct with up to FDNS_MAX nameserver IP addresses from /etc/firedns.conf (or /etc/resolv.conf) */
181 	FILE *f;
182 	int i;
183 	struct in_addr addr4;
184 	char buf[1024];
185 #ifdef HAVE_IPV6
186 	struct in6_addr addr6;
187 #endif
188 	if (initdone == 1)
189 		return;
190 #ifdef HAVE_IPV6
191 	i6 = 0;
192 #endif
193 	i4 = 0;
194 
195 	initdone = 1;
196 	srand((unsigned int) time(NULL));
197 	memset(servers4,'\0',sizeof(struct in_addr) * FDNS_MAX);
198 #ifdef HAVE_IPV6
199 	memset(servers6,'\0',sizeof(struct in6_addr) * FDNS_MAX);
200 #endif
201 	/* read /etc/firedns.conf if we've got it, otherwise parse /etc/resolv.conf */
202 	f = fopen(FDNS_CONFIG_PREF,"r");
203 	if (f == NULL) {
204 		f = fopen(FDNS_CONFIG_FBCK,"r");
205 		if (f == NULL)
206 			return;
207 		while (fgets(buf,1024,f) != NULL) {
208 			if (strncmp(buf,"nameserver",10) == 0) {
209 				i = 10;
210 				while (buf[i] == ' ' || buf[i] == '\t')
211 					i++;
212 #ifdef HAVE_IPV6
213 				/* glibc /etc/resolv.conf seems to allow ipv6 server names */
214 				if (i6 < FDNS_MAX) {
215 					if (firedns_aton6_s(&buf[i],&addr6) != NULL) {
216 						memcpy(&servers6[i6++],&addr6,sizeof(struct in6_addr));
217 						continue;
218 					}
219 				}
220 #endif
221 				if (i4 < FDNS_MAX) {
222 					if (firedns_aton4_s(&buf[i],&addr4) != NULL)
223 						memcpy(&servers4[i4++],&addr4,sizeof(struct in_addr));
224 				}
225 			}
226 		}
227 	} else {
228 		while (fgets(buf,1024,f) != NULL) {
229 #ifdef HAVE_IPV6
230 			if (i6 < FDNS_MAX) {
231 				if (firedns_aton6_s(buf,&addr6) != NULL) {
232 					memcpy(&servers6[i6++],&addr6,sizeof(struct in6_addr));
233 					continue;
234 				}
235 			}
236 #endif
237 			if (i4 < FDNS_MAX) {
238 				if (firedns_aton4_s(buf,&addr4) != NULL)
239 					memcpy(&servers4[i4++],&addr4,sizeof(struct in_addr));
240 			}
241 		}
242 	}
243 	fclose(f);
244 
245 }
246 
firedns_send_requests(const struct s_header * restrict const h,const struct s_connection * restrict const s,const int l)247 static int firedns_send_requests(const struct s_header * restrict const h, const struct s_connection * restrict const s, const int l) { /* send DNS query */
248 	int i;
249 	struct sockaddr_in addr4;
250 	unsigned char payload[sizeof(struct s_header)];
251 #ifdef HAVE_IPV6
252 	struct sockaddr_in6 addr6;
253 #endif
254 
255 	firedns_empty_header(payload,h,l);
256 
257 #ifdef HAVE_IPV6
258 	/* if we've got ipv6 support, an ip v6 socket, and ipv6 servers, send to them */
259 	if (i6 > 0 && s->v6 == 1) {
260 		for (i = 0; i < i6; i++) {
261 			memset(&addr6,0,sizeof(addr6));
262 			memcpy(&addr6.sin6_addr,&servers6[i],sizeof(addr6.sin6_addr));
263 			addr6.sin6_family = AF_INET6;
264 			addr6.sin6_port = htons(FDNS_PORT);
265 			sendto(s->fd, payload, l + 12, 0, (struct sockaddr *) &addr6, sizeof(addr6));
266 		}
267 	}
268 #endif
269 
270 	for (i = 0; i < i4; i++) {
271 #ifdef HAVE_IPV6
272 		/* send via ipv4-over-ipv6 if we've got an ipv6 socket */
273 		if (s->v6 == 1) {
274 			memset(&addr6,0,sizeof(addr6));
275 			memcpy(addr6.sin6_addr.s6_addr,"\0\0\0\0\0\0\0\0\0\0\xff\xff",12);
276 			memcpy(&addr6.sin6_addr.s6_addr[12],&servers4[i].s_addr,4);
277 			addr6.sin6_family = AF_INET6;
278 			addr6.sin6_port = htons(FDNS_PORT);
279 			sendto(s->fd, payload, l + 12, 0, (struct sockaddr *) &addr6, sizeof(addr6));
280 			continue;
281 		}
282 #endif
283 		/* otherwise send via standard ipv4 boringness */
284 		memset(&addr4,0,sizeof(addr4));
285 		memcpy(&addr4.sin_addr,&servers4[i],sizeof(addr4.sin_addr));
286 		addr4.sin_family = AF_INET;
287 		addr4.sin_port = htons(FDNS_PORT);
288 		sendto(s->fd, payload, l + 12, 0, (struct sockaddr *) &addr4, sizeof(addr4));
289 	}
290 
291 	return 0;
292 }
293 
firedns_add_query(struct s_header * restrict const h)294 static struct s_connection *firedns_add_query(struct s_header * restrict const h) { /* build DNS query, add to list */
295 	struct s_connection * restrict s;
296 
297 	s = firestring_malloc(sizeof(struct s_connection));
298 
299 	/* set header flags */
300 	h->id[0] = s->id[0] = rand() % 255; /* verified by firedns_getresult_s() */
301 	h->id[1] = s->id[1] = rand() % 255;
302 	h->flags1 = 0 | FLAGS1_MASK_RD;
303 	h->flags2 = 0;
304 	h->qdcount = 1;
305 	h->ancount = 0;
306 	h->nscount = 0;
307 	h->arcount = 0;
308 
309 	/* turn off want_list by default */
310 	s->want_list = 0;
311 
312 	/* try to create ipv6 or ipv4 socket */
313 #ifdef HAVE_IPV6
314 	s->v6 = 0;
315 	if (i6 > 0) {
316 		s->fd = socket(PF_INET6, SOCK_DGRAM, 0);
317 		if (s->fd != -1) {
318 			if (fcntl(s->fd, F_SETFL, O_NONBLOCK) != 0) {
319 				close(s->fd);
320 				s->fd = -1;
321 			}
322 		}
323 		if (s->fd != -1) {
324 			struct sockaddr_in6 addr6;
325 			memset(&addr6,0,sizeof(addr6));
326 			addr6.sin6_family = AF_INET6;
327 			if (bind(s->fd,(struct sockaddr *)&addr6,sizeof(addr6)) == 0)
328 				s->v6 = 1;
329 			else
330 				close(s->fd);
331 		}
332 	}
333 	if (s->v6 == 0) {
334 #endif
335 		s->fd = socket(PF_INET, SOCK_DGRAM, 0);
336 		if (s->fd != -1) {
337 			if (fcntl(s->fd, F_SETFL, O_NONBLOCK) != 0) {
338 				close(s->fd);
339 				s->fd = -1;
340 			}
341 		}
342 		if (s->fd != -1) {
343 			struct sockaddr_in addr;
344 			memset(&addr,0,sizeof(addr));
345 			addr.sin_family = AF_INET;
346 			addr.sin_port = 0;
347 			addr.sin_addr.s_addr = INADDR_ANY;
348 			if (bind(s->fd,(struct sockaddr *)&addr,sizeof(addr)) != 0) {
349 				close(s->fd);
350 				s->fd = -1;
351 			}
352 		}
353 		if (s->fd == -1) {
354 			free(s);
355 			return NULL;
356 		}
357 #ifdef HAVE_IPV6
358 	}
359 #endif
360 	/* create new connection object, add to linked list */
361 	pthread_mutex_lock(&connlist_lock);
362 	s->next = connection_head;
363 	connection_head = s;
364 
365 	if (wantclose == 1) {
366 		close(lastcreate);
367 		wantclose = 0;
368 	}
369 	lastcreate = s->fd;
370 	pthread_mutex_unlock(&connlist_lock);
371 	return s;
372 }
373 
firedns_build_query_payload(const char * const name,const unsigned short rr,const unsigned short class,unsigned char * const payload)374 static int firedns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short class, unsigned char * const payload) { /* populate payload with query: name= question, rr= record type */
375 	short payloadpos;
376 	const char * tempchr, * tempchr2;
377 	unsigned short l;
378 
379 	payloadpos = 0;
380 	tempchr2 = name;
381 
382 	/* split name up into labels, create query */
383 	while ((tempchr = strchr(tempchr2,'.')) != NULL) {
384 		l = tempchr - tempchr2;
385 		if (payloadpos + l + 1 > 507)
386 			return -1;
387 		payload[payloadpos++] = l;
388 		memcpy(&payload[payloadpos],tempchr2,l);
389 		payloadpos += l;
390 		tempchr2 = &tempchr[1];
391 	}
392 	l = strlen(tempchr2);
393 	if (l) {
394 		if (payloadpos + l + 2 > 507)
395 			return -1;
396 		payload[payloadpos++] = l;
397 		memcpy(&payload[payloadpos],tempchr2,l);
398 		payloadpos += l;
399 		payload[payloadpos++] = '\0';
400 	}
401 	if (payloadpos > 508)
402 		return -1;
403 	l = htons(rr);
404 	memcpy(&payload[payloadpos],&l,2);
405 	l = htons(class);
406 	memcpy(&payload[payloadpos + 2],&l,2);
407 	return payloadpos + 4;
408 }
409 
firedns_aton4(const char * const ipstring)410 struct in_addr *firedns_aton4(const char * const ipstring) { /* ascii to numeric: convert string to static 4part IP addr struct */
411 	static struct in_addr ip;
412 	return firedns_aton4_s(ipstring,&ip);
413 }
414 
firedns_aton4_r(const char * restrict const ipstring)415 struct in_addr *firedns_aton4_r(const char * restrict const ipstring) { /* ascii to numeric (reentrant): convert string to new 4part IP addr struct */
416 	struct in_addr * restrict ip;
417 	ip = firestring_malloc(sizeof(struct in_addr));
418 	if(firedns_aton4_s(ipstring,ip) == NULL) {
419 		free(ip);
420 		return NULL;
421 	}
422 	return ip;
423 }
424 
firedns_aton4_s(const char * restrict const ipstring,struct in_addr * restrict const ip)425 struct in_addr *firedns_aton4_s(const char * restrict const ipstring, struct in_addr * restrict const ip) { /* ascii to numeric (buffered): convert string to given 4part IP addr struct */
426 	unsigned char *myip;
427 	int i,part = 0;
428 	myip = (unsigned char *)ip;
429 
430 	memset(myip,'\0',4);
431 	for (i = 0; i < 16; i++) {
432 		switch (ipstring[i]) {
433 			case '\0':
434 				if (part != 3)
435 					return NULL;
436 				return ip;
437 				break;
438 			case '0':
439 			case '1':
440 			case '2':
441 			case '3':
442 			case '4':
443 			case '5':
444 			case '6':
445 			case '7':
446 			case '8':
447 			case '9':
448 				if (myip[part] > 25)
449 					return NULL;
450 				myip[part] *= 10;
451 				if (myip[part] == 250 && ipstring[i] - '0' > 6)
452 					return NULL;
453 				myip[part] += ipstring[i] - '0';
454 				break;
455 			case '.':
456 				if (part == 3)
457 					return ip;
458 				else
459 					part++;
460 				break;
461 			default:
462 				if (part == 3)
463 					return ip;
464 				else
465 					return NULL;
466 				break;
467 		}
468 	}
469 	if (part == 3)
470 		return ip;
471 	else
472 		return NULL;
473 }
474 
firedns_aton6(const char * const ipstring)475 struct in6_addr *firedns_aton6(const char * const ipstring) {
476 	static struct in6_addr ip;
477 	return firedns_aton6_s(ipstring,&ip);
478 }
479 
firedns_aton6_r(const char * restrict const ipstring)480 struct in6_addr *firedns_aton6_r(const char * restrict const ipstring) {
481 	struct in6_addr * restrict ip;
482 	ip = firestring_malloc(sizeof(struct in6_addr));
483 	if(firedns_aton6_s(ipstring,ip) == NULL) {
484 		free(ip);
485 		return NULL;
486 	}
487 	return ip;
488 }
489 
firedns_aton6_s(const char * restrict const ipstring,struct in6_addr * restrict const ip)490 struct in6_addr *firedns_aton6_s(const char * restrict const ipstring, struct in6_addr * restrict const ip) {
491 	/* black magic */
492 	char instring[40];
493 	char tempstr[5];
494 	int i,o;
495 	int direction = 1;
496 	char *tempchr,*tempchr2;;
497 
498 	i = strlen(ipstring);
499 	if (i > 39)
500 		return NULL;
501 	memcpy(instring,ipstring,i+1);
502 
503 	memset(ip->s6_addr,'\0',16);
504 
505 	tempchr2 = instring;
506 	i = 0;
507 	while (direction > 0) {
508 		if (direction == 1) {
509 			tempchr = strchr(tempchr2,':');
510 			if (tempchr == NULL && i != 14)
511 				return NULL;
512 			if (tempchr != NULL)
513 				tempchr[0] = '\0';
514 			o = strlen(tempchr2);
515 			if (o > 4)
516 				return NULL;
517 			strcpy(tempstr,"0000");
518 			strcpy(&tempstr[4 - o],tempchr2);
519 			o = firestring_hextoi(tempstr);
520 			if (o == -1)
521 				return NULL;
522 			ip->s6_addr[i++] = o;
523 			o = firestring_hextoi(&tempstr[2]);
524 			if (o == -1)
525 				return NULL;
526 			ip->s6_addr[i++] = o;
527 			if (i >= 15)
528 				break;
529 			tempchr2 = tempchr + 1;
530 			if (tempchr2[0] == ':') {
531 				tempchr2++;
532 				direction = 2;
533 				i = 15;
534 				continue;
535 			}
536 		}
537 		if (direction == 2) {
538 			tempchr = strrchr(tempchr2,':');
539 			if (tempchr == NULL)
540 				tempchr = tempchr2;
541 			else {
542 				tempchr[0] = '\0';
543 				tempchr++;
544 			}
545 			o = strlen(tempchr);
546 			if (o > 4)
547 				return NULL;
548 			strcpy(tempstr,"0000");
549 			strcpy(&tempstr[4 - o],tempchr);
550 			o = firestring_hextoi(&tempstr[2]);
551 			if (o == -1)
552 				return NULL;
553 			ip->s6_addr[i--] = o;
554 			o = firestring_hextoi(tempstr);
555 			if (o == -1)
556 				return NULL;
557 			ip->s6_addr[i--] = o;
558 			if (i <= 1)
559 				break;
560 			if (tempchr == tempchr2)
561 				break;
562 		}
563 	}
564 	return ip;
565 }
566 
firedns_getip4(const char * restrict const name)567 int firedns_getip4(const char * restrict const name) { /* build, add and send A query; retrieve result with firedns_getresult() */
568 	struct s_header h;
569 	struct s_connection * restrict s;
570 	int l;
571 
572 	firedns_init();
573 
574 
575 	l = firedns_build_query_payload(name,FDNS_QRY_A,1,(unsigned char *)&h.payload);
576 	if (l == -1)
577 		return -1;
578 	s = firedns_add_query(&h);
579 	if (s == NULL)
580 		return -1;
581 	s->class = 1;
582 	s->type = FDNS_QRY_A;
583 	if (firedns_send_requests(&h,s,l) == -1)
584 		return -1;
585 
586 	return s->fd;
587 }
588 
firedns_getip4list(const char * restrict const name)589 int firedns_getip4list(const char * restrict const name) { /* build, add and send A query; retrieve result with firedns_getresult() */
590 	struct s_header h;
591 	struct s_connection * restrict s;
592 	int l;
593 
594 	firedns_init();
595 
596 
597 	l = firedns_build_query_payload(name,FDNS_QRY_A,1,(unsigned char *)&h.payload);
598 	if (l == -1)
599 		return -1;
600 	s = firedns_add_query(&h);
601 	if (s == NULL)
602 		return -1;
603 	s->class = 1;
604 	s->type = FDNS_QRY_A;
605 	s->want_list = 1;
606 	if (firedns_send_requests(&h,s,l) == -1)
607 		return -1;
608 
609 	return s->fd;
610 }
611 
firedns_getip6(const char * restrict const name)612 int firedns_getip6(const char * restrict const name) {
613 	struct s_header h;
614 	struct s_connection * restrict s;
615 	int l;
616 
617 	firedns_init();
618 
619 	l = firedns_build_query_payload(name,FDNS_QRY_AAAA,1,(unsigned char *)&h.payload);
620 	if (l == -1)
621 		return -1;
622 	s = firedns_add_query(&h);
623 	if (s == NULL)
624 		return -1;
625 	s->class = 1;
626 	s->type = FDNS_QRY_AAAA;
627 	if (firedns_send_requests(&h,s,l) == -1)
628 		return -1;
629 
630 	return s->fd;
631 }
632 
firedns_getip6list(const char * restrict const name)633 int firedns_getip6list(const char * restrict const name) {
634 	struct s_header h;
635 	struct s_connection * restrict s;
636 	int l;
637 
638 	firedns_init();
639 
640 	l = firedns_build_query_payload(name,FDNS_QRY_AAAA,1,(unsigned char *)&h.payload);
641 	if (l == -1)
642 		return -1;
643 	s = firedns_add_query(&h);
644 	if (s == NULL)
645 		return -1;
646 	s->class = 1;
647 	s->want_list = 1;
648 	s->type = FDNS_QRY_AAAA;
649 	if (firedns_send_requests(&h,s,l) == -1)
650 		return -1;
651 
652 	return s->fd;
653 }
654 
firedns_gettxt(const char * restrict const name)655 int firedns_gettxt(const char * restrict const name) { /* build, add and send TXT query; retrieve result with firedns_getresult() */
656 	struct s_header h;
657 	struct s_connection * restrict s;
658 	int l;
659 
660 	firedns_init();
661 
662 	l = firedns_build_query_payload(name,FDNS_QRY_TXT,1,(unsigned char *)&h.payload);
663 	if (l == -1)
664 		return -1;
665 	s = firedns_add_query(&h);
666 	if (s == NULL)
667 		return -1;
668 	s->class = 1;
669 	s->type = FDNS_QRY_TXT;
670 	if (firedns_send_requests(&h,s,l) == -1)
671 		return -1;
672 
673 	return s->fd;
674 }
675 
firedns_gettxtlist(const char * restrict const name)676 int firedns_gettxtlist(const char * restrict const name) { /* build, add and send TXT query; retrieve result with firedns_getresult() */
677 	struct s_header h;
678 	struct s_connection * restrict s;
679 	int l;
680 
681 	firedns_init();
682 
683 	l = firedns_build_query_payload(name,FDNS_QRY_TXT,1,(unsigned char *)&h.payload);
684 	if (l == -1)
685 		return -1;
686 	s = firedns_add_query(&h);
687 	if (s == NULL)
688 		return -1;
689 	s->class = 1;
690 	s->want_list = 1;
691 	s->type = FDNS_QRY_TXT;
692 	if (firedns_send_requests(&h,s,l) == -1)
693 		return -1;
694 
695 	return s->fd;
696 }
697 
firedns_getmx(const char * restrict const name)698 int firedns_getmx(const char * restrict const name) { /* build, add and send MX query; retrieve result with firedns_getresult() */
699 	struct s_header h;
700 	struct s_connection * restrict s;
701 	int l;
702 
703 	firedns_init();
704 
705 	l = firedns_build_query_payload(name,FDNS_QRY_MX,1,(unsigned char *)&h.payload);
706 	if (l == -1)
707 		return -1;
708 	s = firedns_add_query(&h);
709 	if (s == NULL)
710 		return -1;
711 	s->class = 1;
712 	s->type = FDNS_QRY_MX;
713 	if (firedns_send_requests(&h,s,l) == -1)
714 		return -1;
715 
716 	return s->fd;
717 }
718 
firedns_getmxlist(const char * const name)719 int firedns_getmxlist(const char * const name) {
720 	struct s_header h;
721 	struct s_connection * restrict s;
722 	int l;
723 
724 	firedns_init();
725 
726 	l = firedns_build_query_payload(name,FDNS_QRY_MX,1,(unsigned char *)&h.payload);
727 	if (l == -1)
728 		return -1;
729 	s = firedns_add_query(&h);
730 	if (s == NULL)
731 		return -1;
732 	s->class = 1;
733 	s->type = FDNS_QRY_MX;
734 	s->want_list = 1;
735 	if (firedns_send_requests(&h,s,l) == -1)
736 		return -1;
737 
738 	return s->fd;
739 }
740 
firedns_getname4(const struct in_addr * restrict const ip)741 int firedns_getname4(const struct in_addr * restrict const ip) { /* build, add and send PTR query; retrieve result with firedns_getresult() */
742 	char query[512];
743 	struct s_header h;
744 	struct s_connection * restrict s;
745 	unsigned char *c;
746 	int l;
747 
748 	firedns_init();
749 
750 	c = (unsigned char *)&ip->s_addr;
751 
752 	sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
753 
754 	l = firedns_build_query_payload(query,FDNS_QRY_PTR,1,(unsigned char *)&h.payload);
755 	if (l == -1)
756 		return -1;
757 	s = firedns_add_query(&h);
758 	if (s == NULL)
759 		return -1;
760 	s->class = 1;
761 	s->type = FDNS_QRY_PTR;
762 	if (firedns_send_requests(&h,s,l) == -1)
763 		return -1;
764 
765 	return s->fd;
766 }
767 
firedns_getname6(const struct in6_addr * restrict const ip)768 int firedns_getname6(const struct in6_addr * restrict const ip) {
769 	char query[512];
770 	struct s_header h;
771 	struct s_connection * restrict s;
772 	int l;
773 
774 	firedns_init();
775 
776 	sprintf(query,"%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.ip6.int",
777 			ip->s6_addr[15] & 0x0f,
778 			(ip->s6_addr[15] & 0xf0) >> 4,
779 			ip->s6_addr[14] & 0x0f,
780 			(ip->s6_addr[14] & 0xf0) >> 4,
781 			ip->s6_addr[13] & 0x0f,
782 			(ip->s6_addr[13] & 0xf0) >> 4,
783 			ip->s6_addr[12] & 0x0f,
784 			(ip->s6_addr[12] & 0xf0) >> 4,
785 			ip->s6_addr[11] & 0x0f,
786 			(ip->s6_addr[11] & 0xf0) >> 4,
787 			ip->s6_addr[10] & 0x0f,
788 			(ip->s6_addr[10] & 0xf0) >> 4,
789 			ip->s6_addr[9] & 0x0f,
790 			(ip->s6_addr[9] & 0xf0) >> 4,
791 			ip->s6_addr[8] & 0x0f,
792 			(ip->s6_addr[8] & 0xf0) >> 4,
793 			ip->s6_addr[7] & 0x0f,
794 			(ip->s6_addr[7] & 0xf0) >> 4,
795 			ip->s6_addr[6] & 0x0f,
796 			(ip->s6_addr[6] & 0xf0) >> 4,
797 			ip->s6_addr[5] & 0x0f,
798 			(ip->s6_addr[5] & 0xf0) >> 4,
799 			ip->s6_addr[4] & 0x0f,
800 			(ip->s6_addr[4] & 0xf0) >> 4,
801 			ip->s6_addr[3] & 0x0f,
802 			(ip->s6_addr[3] & 0xf0) >> 4,
803 			ip->s6_addr[2] & 0x0f,
804 			(ip->s6_addr[2] & 0xf0) >> 4,
805 			ip->s6_addr[1] & 0x0f,
806 			(ip->s6_addr[1] & 0xf0) >> 4,
807 			ip->s6_addr[0] & 0x0f,
808 			(ip->s6_addr[0] & 0xf0) >> 4
809 			);
810 
811 	l = firedns_build_query_payload(query,FDNS_QRY_PTR,1,(unsigned char *)&h.payload);
812 	if (l == -1)
813 		return -1;
814 	s = firedns_add_query(&h);
815 	if (s == NULL)
816 		return -1;
817 	s->class = 1;
818 	s->type = FDNS_QRY_PTR;
819 	if (firedns_send_requests(&h,s,l) == -1)
820 		return -1;
821 
822 	return s->fd;
823 }
824 
firedns_getcname(const char * restrict const name)825 int firedns_getcname(const char * restrict const name) {
826 	struct s_header h;
827 	struct s_connection * restrict s;
828 	int l;
829 
830 	firedns_init();
831 
832 	l = firedns_build_query_payload(name,FDNS_QRY_CNAME,1,(unsigned char *)&h.payload);
833 	if (l == -1)
834 		return -1;
835 	s = firedns_add_query(&h);
836 	if (s == NULL)
837 		return -1;
838 	s->class = 1;
839 	s->type = FDNS_QRY_CNAME;
840 	if (firedns_send_requests(&h,s,l) == -1)
841 		return -1;
842 
843 	return s->fd;
844 }
845 
firedns_dnsbl_lookup_a(const struct in_addr * restrict const ip,const char * restrict const name)846 int firedns_dnsbl_lookup_a(const struct in_addr * restrict const ip, const char * restrict const name) { /* build, add and send A query to given DNSBL list; retrieve result with firedns_getresult() */
847 	char hostname[256];
848 	firestring_snprintf(hostname,256,"%u.%u.%u.%u.%s",(unsigned int) ((unsigned char *)&ip->s_addr)[3],(unsigned int) ((unsigned char *)&ip->s_addr)[2],(unsigned int) ((unsigned char *)&ip->s_addr)[1],(unsigned int) ((unsigned char *)&ip->s_addr)[0],name);
849 	return firedns_getip4(hostname);
850 }
851 
firedns_dnsbl_lookup_txt(const struct in_addr * restrict const ip,const char * restrict const name)852 int firedns_dnsbl_lookup_txt(const struct in_addr * restrict const ip, const char * restrict const name) { /* build, add and send TXT query to given DNSBL list; retrieve result with firedns_getresult() */
853 	char hostname[256];
854 	firestring_snprintf(hostname,256,"%u.%u.%u.%u.%s",(unsigned int) ((unsigned char *)&ip->s_addr)[3],(unsigned int) ((unsigned char *)&ip->s_addr)[2],(unsigned int) ((unsigned char *)&ip->s_addr)[1],(unsigned int) ((unsigned char *)&ip->s_addr)[0],name);
855 	return firedns_gettxt(hostname);
856 }
857 
firedns_ntoa4(const struct in_addr * const ip)858 char *firedns_ntoa4(const struct in_addr * const ip) { /* numeric to ascii: convert 4part IP addr struct to static string */
859 	static char result[256];
860 	return firedns_ntoa4_s(ip,result);
861 }
862 
firedns_ntoa4_r(const struct in_addr * restrict const ip)863 char *firedns_ntoa4_r(const struct in_addr * restrict const ip) { /* numeric to ascii (reentrant): convert 4part IP addr struct to new string */
864 	char * restrict result;
865 	result = firestring_malloc(256);
866 	return firedns_ntoa4_s(ip,result);
867 }
868 
firedns_ntoa4_s(const struct in_addr * restrict const ip,char * restrict const result)869 char *firedns_ntoa4_s(const struct in_addr * restrict const ip, char * restrict const result) { /* numeric to ascii (buffered): convert 4part IP addr struct to given string */
870 	unsigned char *m;
871 	m = (unsigned char *)&ip->s_addr;
872 	sprintf(result,"%d.%d.%d.%d",m[0],m[1],m[2],m[3]);
873 	return result;
874 }
875 
firedns_ntoa6(const struct in6_addr * ip)876 char *firedns_ntoa6(const struct in6_addr *ip) {
877 	static char result[256];
878 	return firedns_ntoa6_s(ip,result);
879 }
880 
firedns_ntoa6_r(const struct in6_addr * restrict ip)881 char *firedns_ntoa6_r(const struct in6_addr * restrict ip) {
882 	char * restrict result;
883 	result = firestring_malloc(256);
884 	return firedns_ntoa6_s(ip,result);
885 }
886 
firedns_ntoa6_s(const struct in6_addr * restrict const ip,char * restrict const result)887 char *firedns_ntoa6_s(const struct in6_addr * restrict const ip, char * restrict const result) {
888 	char *c;
889 	sprintf(result,"%x:%x:%x:%x:%x:%x:%x:%x",
890 			ntohs(*((unsigned short *)&ip->s6_addr[0])),
891 			ntohs(*((unsigned short *)&ip->s6_addr[2])),
892 			ntohs(*((unsigned short *)&ip->s6_addr[4])),
893 			ntohs(*((unsigned short *)&ip->s6_addr[6])),
894 			ntohs(*((unsigned short *)&ip->s6_addr[8])),
895 			ntohs(*((unsigned short *)&ip->s6_addr[10])),
896 			ntohs(*((unsigned short *)&ip->s6_addr[12])),
897 			ntohs(*((unsigned short *)&ip->s6_addr[14])));
898 	c = strstr(result,":0:");
899 	if (c != NULL) {
900 		memmove(c+1,c+2,strlen(c+2) + 1);
901 		c += 2;
902 		while (memcmp(c,"0:",2) == 0)
903 			memmove(c,c+2,strlen(c+2) + 1);
904 		if (memcmp(c,"0",2) == 0)
905 			*c = '\0';
906 		if (memcmp(result,"0::",3) == 0)
907 			memmove(result,result + 1, strlen(result + 1) + 1);
908 	}
909 
910 	return result;
911 }
912 
firedns_getresult(const int fd)913 char *firedns_getresult(const int fd) { /* retrieve result of DNS query */
914 	static char result[RESULTSIZE];
915 	return firedns_getresult_s(fd,result);
916 }
917 
firedns_getresult_r(const int fd)918 char *firedns_getresult_r(const int fd) { /* retrieve result of DNS query (reentrant) */
919 	char *result;
920 	result = firestring_malloc(RESULTSIZE);
921 	if(firedns_getresult_s(fd,result) == NULL) {
922 		free(result);
923 		return NULL;
924 	}
925 	return result;
926 }
927 
firedns_getresult_s(const int fd,char * restrict const result)928 char *firedns_getresult_s(const int fd, char * restrict const result) { /* retrieve result of DNS query (buffered) */
929 	struct s_header h;
930 	struct s_connection * restrict c, *prev;
931 	int l,i,q,curanswer,o;
932 	struct s_rr_middle rr;
933 	unsigned char buffer[sizeof(struct s_header)];
934 	unsigned short p;
935 
936 	prev = NULL;
937 	pthread_mutex_lock(&connlist_lock);
938 	c = connection_head;
939 	while (c != NULL) { /* find query in list of open queries */
940 		if (c->fd == fd)
941 			break;
942 		prev = c;
943 		c = c->next;
944 	}
945 	if (c == NULL) {
946 		pthread_mutex_unlock(&connlist_lock);
947 		return NULL; /* query not found */
948 	}
949 	/* query found-- pull from list: */
950 	if (prev != NULL)
951 		prev->next = c->next;
952 	else
953 		connection_head = c->next;
954 	pthread_mutex_unlock(&connlist_lock);
955 
956 	l = recv(c->fd,buffer,sizeof(struct s_header),0);
957 	firedns_close(c->fd);
958 	if (l < 12) {
959 		free(c);
960 		return NULL;
961 	}
962 	firedns_fill_header(&h,buffer,l - 12);
963 	if (c->id[0] != h.id[0] || c->id[1] != h.id[1]) {
964 		free(c);
965 		return NULL; /* ID mismatch */
966 	}
967 	if ((h.flags1 & FLAGS1_MASK_QR) == 0) {
968 		free(c);
969 		return NULL;
970 	}
971 	if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0) {
972 		free(c);
973 		return NULL;
974 	}
975 	if ((h.flags2 & FLAGS2_MASK_RCODE) != 0) {
976 		free(c);
977 		return NULL;
978 	}
979 	if (h.ancount < 1)  { /* no sense going on if we don't have any answers */
980 		free(c);
981 		return NULL;
982 	}
983 	/* skip queries */
984 	i = 0;
985 	q = 0;
986 	l -= 12;
987 	while (q < h.qdcount && i < l) {
988 		if (h.payload[i] > 63) { /* pointer */
989 			i += 6; /* skip pointer, class and type */
990 			q++;
991 		} else { /* label */
992 			if (h.payload[i] == 0) {
993 				q++;
994 				i += 5; /* skip nil, class and type */
995 			} else
996 				i += h.payload[i] + 1; /* skip length and label */
997 		}
998 	}
999 	/* &h.payload[i] should now be the start of the first response */
1000 	curanswer = 0;
1001 	while (curanswer < h.ancount) {
1002 		q = 0;
1003 		while (q == 0 && i < l) {
1004 			if (h.payload[i] > 63) { /* pointer */
1005 				i += 2; /* skip pointer */
1006 				q = 1;
1007 			} else { /* label */
1008 				if (h.payload[i] == 0) {
1009 					i++;
1010 					q = 1;
1011 				} else
1012 					i += h.payload[i] + 1; /* skip length and label */
1013 			}
1014 		}
1015 		if (l - i < 10) {
1016 			free(c);
1017 			return NULL;
1018 		}
1019 		firedns_fill_rr(&rr,&h.payload[i]);
1020 		i += 10;
1021 		if (rr.type != c->type) {
1022 			curanswer++;
1023 			i += rr.rdlength;
1024 			continue;
1025 		}
1026 		if (rr.class != c->class) {
1027 			curanswer++;
1028 			i += rr.rdlength;
1029 			continue;
1030 		}
1031 		break;
1032 	}
1033 	if (curanswer == h.ancount)
1034 		return NULL;
1035 	if (i + rr.rdlength > l)
1036 		return NULL;
1037 	if (rr.rdlength > 1023)
1038 		return NULL;
1039 
1040 	switch (rr.type) {
1041 		case FDNS_QRY_PTR:
1042 		case FDNS_QRY_CNAME:
1043 			o = 0;
1044 			q = 0;
1045 			while (q == 0 && i < l && o + 256 < 1023) {
1046 				if (h.payload[i] > 63) { /* pointer */
1047 					memcpy(&p,&h.payload[i],2);
1048 					i = ntohs(p) - FIREDNS_POINTER_VALUE - 12;
1049 				} else { /* label */
1050 					if (h.payload[i] == 0)
1051 						q = 1;
1052 					else {
1053 						result[o] = '\0';
1054 						if (o != 0)
1055 							result[o++] = '.';
1056 						memcpy(&result[o],&h.payload[i + 1],h.payload[i]);
1057 						o += h.payload[i];
1058 						i += h.payload[i] + 1;
1059 					}
1060 				}
1061 			}
1062 			result[o] = '\0';
1063 			break;
1064 		case FDNS_QRY_MX:
1065 			if (c->want_list) {
1066 				struct firedns_mxlist *mxa = (struct firedns_mxlist *) result; /* we have to trust that this is aligned */
1067 				int g;
1068 				while ((char *)mxa - (char *)result < 700) {
1069 					mxa->ip4list = NULL;
1070 					mxa->cname = NULL;
1071 					g = i + rr.rdlength;
1072 					curanswer++;
1073 					mxa->priority = h.payload[i] * 256 + h.payload[i+1];
1074 					if (mxa->priority >= FDNS_MXPS_START && mxa->priority <= FDNS_MXPS_STOP)
1075 						mxa->protocol = mxa->priority % 16;
1076 					else
1077 						mxa->protocol = FIREDNS_MX_SMTP;
1078 					mxa->name = (char *)mxa + sizeof(struct firedns_mxlist);
1079 					i += 2;
1080 					o = 0;
1081 					q = 0;
1082 					while (q == 0 && i < l && o + 256 < 1023) {
1083 						if (h.payload[i] > 63) { /* pointer */
1084 							memcpy(&p,&h.payload[i],2);
1085 							i = ntohs(p) - FIREDNS_POINTER_VALUE - 12;
1086 						} else { /* label */
1087 							if (h.payload[i] == 0)
1088 								q = 1;
1089 							else {
1090 								mxa->name[o] = '\0';
1091 								if (o != 0)
1092 									mxa->name[o++] = '.';
1093 								memcpy(&mxa->name[o],&h.payload[i + 1],h.payload[i]);
1094 								o += h.payload[i];
1095 								i += h.payload[i] + 1;
1096 							}
1097 						}
1098 					}
1099 					mxa->name[o++] = '\0';
1100 					mxa->next = NULL;
1101 					i = g;
1102 					if (curanswer < h.ancount) {
1103 						q = 0;
1104 						while (q == 0 && i < l) {
1105 							if (h.payload[i] > 63) { /* pointer */
1106 								i += 2; /* skip pointer */
1107 								q = 1;
1108 							} else { /* label */
1109 								if (h.payload[i] == 0) {
1110 									i++;
1111 									q = 1;
1112 								} else
1113 									i += h.payload[i] + 1; /* skip length and label */
1114 							}
1115 						}
1116 						if (l - i < 10) {
1117 							free(c);
1118 							return NULL;
1119 						}
1120 						firedns_fill_rr(&rr,&h.payload[i]);
1121 						i += 10;
1122 						if (rr.type != FDNS_QRY_MX)
1123 							break;
1124 						if (rr.class != 1)
1125 							break;
1126 						mxa->next = (struct firedns_mxlist *) firedns_align((((char *) mxa) + sizeof(struct firedns_mxlist) + o));
1127 						mxa = mxa->next;
1128 					} else {
1129 						break;
1130 					}
1131 				}
1132 				mxa->next = NULL;
1133 			} else {
1134 				i += 2;
1135 				o = 0;
1136 				q = 0;
1137 				while (q == 0 && i < l && o + 256 < 1023) {
1138 					if (h.payload[i] > 63) { /* pointer */
1139 						memcpy(&p,&h.payload[i],2);
1140 						i = ntohs(p) - FIREDNS_POINTER_VALUE - 12;
1141 					} else { /* label */
1142 						if (h.payload[i] == 0)
1143 							q = 1;
1144 						else {
1145 							result[o] = '\0';
1146 							if (o != 0)
1147 								result[o++] = '.';
1148 							memcpy(&result[o],&h.payload[i + 1],h.payload[i]);
1149 							o += h.payload[i];
1150 							i += h.payload[i] + 1;
1151 						}
1152 					}
1153 				}
1154 				result[o] = '\0';
1155 			}
1156 			break;
1157 		case FDNS_QRY_TXT:
1158 			if (c->want_list) {
1159 				struct firedns_txtlist *txtlist = (struct firedns_txtlist *) result; /* we have to trust that this is aligned */
1160 				while ((char *)txtlist - (char *)result < 700) {
1161 					if (rr.type != FDNS_QRY_TXT)
1162 						break;
1163 					if (rr.class != 1)
1164 						break;
1165 					{
1166 						unsigned char *end, *trailer;
1167 						int o;
1168 
1169 						txtlist->txt = firedns_align(((char *)txtlist) + sizeof(struct firedns_txtlist));
1170 
1171 						trailer = &h.payload[i];
1172 						end = &h.payload[i] + rr.rdlength;
1173 
1174 						o = 0;
1175 						while (trailer < end) {
1176 							unsigned char l;
1177 							l = trailer[0];
1178 							if (trailer + l > end) { /* cheating the system */
1179 								free(c);
1180 								return NULL;
1181 							}
1182 							memcpy(&txtlist->txt[o],&trailer[1],l);
1183 							o += l;
1184 							trailer += l + 1;
1185 						}
1186 						txtlist->txt[o] = '\0';
1187 					}
1188 					if (++curanswer >= h.ancount)
1189 						break;
1190 					i += rr.rdlength;
1191 					{
1192 						/* skip next name */
1193 						q = 0;
1194 						while (q == 0 && i < l) {
1195 							if (h.payload[i] > 63) { /* pointer */
1196 								i += 2; /* skip pointer */
1197 								q = 1;
1198 							} else { /* label */
1199 								if (h.payload[i] == 0) {
1200 									i++;
1201 									q = 1;
1202 								} else
1203 									i += h.payload[i] + 1; /* skip length and label */
1204 							}
1205 						}
1206 					}
1207 					if (l - i < 10) {
1208 						free(c);
1209 						return NULL;
1210 					}
1211 					firedns_fill_rr(&rr,&h.payload[i]);
1212 					i += 10;
1213 					txtlist->next = (struct firedns_txtlist *) firedns_align(txtlist->txt + strlen(txtlist->txt) + 1);
1214 					txtlist = txtlist->next;
1215 					txtlist->next = NULL;
1216 				}
1217 				txtlist->next = NULL;
1218 				break;
1219 			} else {
1220 				unsigned char *end, *trailer;
1221 				int o = 0;
1222 
1223 				trailer = &h.payload[i];
1224 				end = &h.payload[i] + rr.rdlength;
1225 
1226 				while (trailer < end) {
1227 					unsigned char l = trailer[0];
1228 					if (trailer + l > end) { /* cheating the system */
1229 						free(c);
1230 						return NULL;
1231 					}
1232 					memcpy(&result[o],&trailer[1],l);
1233 					o += l;
1234 					trailer += l + 1;
1235 				}
1236 				result[o] = '\0';
1237 			}
1238 			break;
1239 		case FDNS_QRY_A:
1240 			if (c->want_list) {
1241 				struct firedns_ip4list *alist = (struct firedns_ip4list *) result; /* we have to trust that this is aligned */
1242 				while ((char *)alist - (char *)result < 700) {
1243 					if (rr.type != FDNS_QRY_A)
1244 						break;
1245 					if (rr.class != 1)
1246 						break;
1247 					if (rr.rdlength != 4) {
1248 						free(c);
1249 						return NULL;
1250 					}
1251 					memcpy(&alist->ip,&h.payload[i],4);
1252 					if (++curanswer >= h.ancount)
1253 						break;
1254 					i += rr.rdlength;
1255 					{
1256 						/* skip next name */
1257 						q = 0;
1258 						while (q == 0 && i < l) {
1259 							if (h.payload[i] > 63) { /* pointer */
1260 								i += 2; /* skip pointer */
1261 								q = 1;
1262 							} else { /* label */
1263 								if (h.payload[i] == 0) {
1264 									i++;
1265 									q = 1;
1266 								} else
1267 									i += h.payload[i] + 1; /* skip length and label */
1268 							}
1269 						}
1270 					}
1271 					if (l - i < 10) {
1272 						free(c);
1273 						return NULL;
1274 					}
1275 					firedns_fill_rr(&rr,&h.payload[i]);
1276 					i += 10;
1277 					alist->next = (struct firedns_ip4list *) firedns_align(((char *) alist) + sizeof(struct firedns_ip4list));
1278 					alist = alist->next;
1279 					alist->next = NULL;
1280 				}
1281 				alist->next = NULL;
1282 				break;
1283 			}
1284 			goto defaultcase;
1285 			break;
1286 		case FDNS_QRY_AAAA:
1287 			if (c->want_list) {
1288 				struct firedns_ip6list *alist = (struct firedns_ip6list *) result; /* we have to trust that this is aligned */
1289 				while ((char *)alist - (char *)result < 700) {
1290 					if (rr.type != FDNS_QRY_AAAA)
1291 						break;
1292 					if (rr.class != 1)
1293 						break;
1294 					if (rr.rdlength != 16) {
1295 						free(c);
1296 						return NULL;
1297 					}
1298 					memcpy(&alist->ip,&h.payload[i],16);
1299 					if (++curanswer >= h.ancount)
1300 						break;
1301 					i += rr.rdlength;
1302 					{
1303 						/* skip next name */
1304 						q = 0;
1305 						while (q == 0 && i < l) {
1306 							if (h.payload[i] > 63) { /* pointer */
1307 								i += 2; /* skip pointer */
1308 								q = 1;
1309 							} else { /* label */
1310 								if (h.payload[i] == 0) {
1311 									i++;
1312 									q = 1;
1313 								} else
1314 									i += h.payload[i] + 1; /* skip length and label */
1315 							}
1316 						}
1317 					}
1318 					if (l - i < 10) {
1319 						free(c);
1320 						return NULL;
1321 					}
1322 					firedns_fill_rr(&rr,&h.payload[i]);
1323 					i += 10;
1324 					alist->next = (struct firedns_ip6list *) firedns_align(((char *) alist) + sizeof(struct firedns_ip6list));
1325 					alist = alist->next;
1326 					alist->next = NULL;
1327 				}
1328 				alist->next = NULL;
1329 				break;
1330 			}
1331 			goto defaultcase;
1332 			break;
1333 		default:
1334 		defaultcase:
1335 			memcpy(result,&h.payload[i],rr.rdlength);
1336 			result[rr.rdlength] = '\0';
1337 			break;
1338 	}
1339 	free(c);
1340 	return result;
1341 }
1342 
firedns_resolveip4_i(const char * restrict const name,char * (* const result)(int))1343 static inline struct in_addr *firedns_resolveip4_i(const char * restrict const name, char *(* const result)(int)) { /* immediate A query */
1344 	int fd;
1345 	int t,i;
1346 	struct in_addr * restrict ret;
1347 	fd_set s;
1348 	struct timeval tv;
1349 
1350 	for (t = 0; t < FIREDNS_TRIES; t++) {
1351 		fd = firedns_getip4(name);
1352 		if (fd == -1)
1353 			return NULL;
1354 		tv.tv_sec = 5;
1355 		tv.tv_usec = 0;
1356 		FD_ZERO(&s);
1357 		FD_SET(fd,&s);
1358 		i = select(fd + 1,&s,NULL,NULL,&tv);
1359 		ret = (struct in_addr *) result(fd);
1360 		if (ret != NULL || i != 0)
1361 			return ret;
1362 	}
1363 	return NULL;
1364 }
1365 
firedns_resolveip4(const char * const name)1366 struct in_addr *firedns_resolveip4(const char * const name) { /* immediate A query */
1367 	return firedns_resolveip4_i(name,firedns_getresult);
1368 }
1369 
firedns_resolveip4_r(const char * const name)1370 struct in_addr *firedns_resolveip4_r(const char * const name) { /* immediate A query (reentrant) */
1371 	return firedns_resolveip4_i(name,firedns_getresult_r);
1372 }
1373 
firedns_resolveip4list_i(const char * restrict const name,char * (* const result)(int))1374 static inline struct firedns_ip4list *firedns_resolveip4list_i(const char * restrict const name, char *(* const result)(int)) { /* immediate A query */
1375 	int fd;
1376 	int t,i;
1377 	struct firedns_ip4list * restrict ret;
1378 	fd_set s;
1379 	struct timeval tv;
1380 
1381 	for (t = 0; t < FIREDNS_TRIES; t++) {
1382 		fd = firedns_getip4list(name);
1383 		if (fd == -1)
1384 			return NULL;
1385 		tv.tv_sec = 5;
1386 		tv.tv_usec = 0;
1387 		FD_ZERO(&s);
1388 		FD_SET(fd,&s);
1389 		i = select(fd + 1,&s,NULL,NULL,&tv);
1390 		ret = (struct firedns_ip4list *) result(fd);
1391 		if (ret != NULL || i != 0)
1392 			return ret;
1393 	}
1394 	return NULL;
1395 }
1396 
firedns_resolveip4list(const char * const name)1397 struct firedns_ip4list *firedns_resolveip4list(const char * const name) { /* immediate A query */
1398 	return firedns_resolveip4list_i(name,firedns_getresult);
1399 }
1400 
firedns_resolveip4list_r(const char * const name)1401 struct firedns_ip4list *firedns_resolveip4list_r(const char * const name) { /* immediate A query (reentrant) */
1402 	return firedns_resolveip4list_i(name,firedns_getresult_r);
1403 }
1404 
firedns_resolveip6_i(const char * restrict const name,char * (* const result)(int))1405 static inline struct in6_addr *firedns_resolveip6_i(const char * restrict const name, char *(* const result)(int)) {
1406 	int fd;
1407 	int t,i;
1408 	struct in6_addr * restrict ret;
1409 	fd_set s;
1410 	struct timeval tv;
1411 
1412 	for (t = 0; t < FIREDNS_TRIES; t++) {
1413 		fd = firedns_getip6(name);
1414 		if (fd == -1)
1415 			return NULL;
1416 		tv.tv_sec = 5;
1417 		tv.tv_usec = 0;
1418 		FD_ZERO(&s);
1419 		FD_SET(fd,&s);
1420 		i = select(fd + 1,&s,NULL,NULL,&tv);
1421 		ret = (struct in6_addr *) result(fd);
1422 		if (ret != NULL || i != 0)
1423 			return ret;
1424 	}
1425 	return NULL;
1426 }
1427 
firedns_resolveip6(const char * const name)1428 struct in6_addr *firedns_resolveip6(const char * const name) {
1429 	return firedns_resolveip6_i(name,firedns_getresult);
1430 }
1431 
firedns_resolevip6_r(const char * const name)1432 struct in6_addr *firedns_resolevip6_r(const char * const name) {
1433 	return firedns_resolveip6_i(name,firedns_getresult_r);
1434 }
1435 
firedns_resolveip6list_i(const char * restrict const name,char * (* const result)(int))1436 static inline struct firedns_ip6list *firedns_resolveip6list_i(const char * restrict const name, char *(* const result)(int)) {
1437 	int fd;
1438 	int t,i;
1439 	struct firedns_ip6list * restrict ret;
1440 	fd_set s;
1441 	struct timeval tv;
1442 
1443 	for (t = 0; t < FIREDNS_TRIES; t++) {
1444 		fd = firedns_getip6list(name);
1445 		if (fd == -1)
1446 			return NULL;
1447 		tv.tv_sec = 5;
1448 		tv.tv_usec = 0;
1449 		FD_ZERO(&s);
1450 		FD_SET(fd,&s);
1451 		i = select(fd + 1,&s,NULL,NULL,&tv);
1452 		ret = (struct firedns_ip6list *) result(fd);
1453 		if (ret != NULL || i != 0)
1454 			return ret;
1455 	}
1456 	return NULL;
1457 }
1458 
firedns_resolveip6list(const char * const name)1459 struct firedns_ip6list *firedns_resolveip6list(const char * const name) {
1460 	return firedns_resolveip6list_i(name,firedns_getresult);
1461 }
1462 
firedns_resolveip6list_r(const char * const name)1463 struct firedns_ip6list *firedns_resolveip6list_r(const char * const name) {
1464 	return firedns_resolveip6list_i(name,firedns_getresult_r);
1465 }
1466 
firedns_resolvetxt_i(const char * restrict const name,char * (* const result)(int))1467 static inline char *firedns_resolvetxt_i(const char * restrict const name, char *(* const result)(int)) {
1468 	int fd;
1469 	int t,i;
1470 	char * restrict ret;
1471 	fd_set s;
1472 	struct timeval tv;
1473 
1474 	for (t = 0; t < FIREDNS_TRIES; t++) {
1475 		fd = firedns_gettxt(name);
1476 		if (fd == -1)
1477 			return NULL;
1478 		tv.tv_sec = 5;
1479 		tv.tv_usec = 0;
1480 		FD_ZERO(&s);
1481 		FD_SET(fd,&s);
1482 		i = select(fd + 1,&s,NULL,NULL,&tv);
1483 		ret = result(fd);
1484 		if (ret != NULL || i != 0)
1485 			return ret;
1486 	}
1487 	return NULL;
1488 }
1489 
firedns_resolvetxt(const char * const name)1490 char *firedns_resolvetxt(const char * const name) { /* immediate TXT query */
1491 	return firedns_resolvetxt_i(name,firedns_getresult);
1492 }
1493 
firedns_resolvetxt_r(const char * const name)1494 char *firedns_resolvetxt_r(const char * const name) {
1495 	return firedns_resolvetxt_i(name,firedns_getresult_r);
1496 }
1497 
firedns_resolvetxtlist_i(const char * restrict const name,char * (* const result)(int))1498 static inline struct firedns_txtlist *firedns_resolvetxtlist_i(const char * restrict const name, char *(* const result)(int)) {
1499 	int fd;
1500 	int t,i;
1501 	struct firedns_txtlist * restrict ret;
1502 	fd_set s;
1503 	struct timeval tv;
1504 
1505 	for (t = 0; t < FIREDNS_TRIES; t++) {
1506 		fd = firedns_gettxtlist(name);
1507 		if (fd == -1)
1508 			return NULL;
1509 		tv.tv_sec = 5;
1510 		tv.tv_usec = 0;
1511 		FD_ZERO(&s);
1512 		FD_SET(fd,&s);
1513 		i = select(fd + 1,&s,NULL,NULL,&tv);
1514 		ret = (struct firedns_txtlist *) result(fd);
1515 		if (ret != NULL || i != 0)
1516 			return ret;
1517 	}
1518 	return NULL;
1519 }
1520 
firedns_resolvetxtlist(const char * const name)1521 struct firedns_txtlist *firedns_resolvetxtlist(const char * const name) {
1522 	return firedns_resolvetxtlist_i(name,firedns_getresult);
1523 }
1524 
firedns_resolvetxtlist_r(const char * const name)1525 struct firedns_txtlist *firedns_resolvetxtlist_r(const char * const name) {
1526 	return firedns_resolvetxtlist_i(name,firedns_getresult_r);
1527 }
1528 
firedns_resolvemx_i(const char * restrict const name,char * (* const result)(int))1529 static inline char *firedns_resolvemx_i(const char * restrict const name, char *(* const result)(int)) {
1530 	int fd;
1531 	int t,i;
1532 	char * restrict ret;
1533 	fd_set s;
1534 	struct timeval tv;
1535 
1536 	for (t = 0; t < FIREDNS_TRIES; t++) {
1537 		fd = firedns_getmx(name);
1538 		if (fd == -1)
1539 			return NULL;
1540 		tv.tv_sec = 5;
1541 		tv.tv_usec = 0;
1542 		FD_ZERO(&s);
1543 		FD_SET(fd,&s);
1544 		i = select(fd + 1,&s,NULL,NULL,&tv);
1545 		ret = result(fd);
1546 		if (ret != NULL || i != 0)
1547 			return ret;
1548 
1549 	}
1550 	return NULL;
1551 }
1552 
firedns_resolvemx(const char * const name)1553 char *firedns_resolvemx(const char * const name) { /* immediate MX query */
1554 	return firedns_resolvemx_i(name,firedns_getresult);
1555 }
1556 
firedns_resolvemx_r(const char * const name)1557 char *firedns_resolvemx_r(const char * const name) {
1558 	return firedns_resolvemx_i(name,firedns_getresult_r);
1559 }
1560 
firedns_resolvemxlist_i(const char * restrict const name,char * (* const result)(int))1561 static inline struct firedns_mxlist *firedns_resolvemxlist_i(const char * restrict const name, char *(* const result)(int)) {
1562 	int fd;
1563 	int t,i;
1564 	struct firedns_mxlist * restrict ret;
1565 	fd_set s;
1566 	struct timeval tv;
1567 
1568 	for (t = 0; t < FIREDNS_TRIES; t++) {
1569 		fd = firedns_getmxlist(name);
1570 		if (fd == -1)
1571 			return NULL;
1572 		tv.tv_sec = 5;
1573 		tv.tv_usec = 0;
1574 		FD_ZERO(&s);
1575 		FD_SET(fd,&s);
1576 		i = select(fd + 1,&s,NULL,NULL,&tv);
1577 		ret = (struct firedns_mxlist *) result(fd);
1578 		if (ret != NULL || i != 0)
1579 			return ret;
1580 
1581 	}
1582 	return NULL;
1583 }
1584 
firedns_resolvemxlist(const char * const name)1585 struct firedns_mxlist *firedns_resolvemxlist(const char * const name) {
1586 	return firedns_resolvemxlist_i(name,firedns_getresult);
1587 }
1588 
firedns_resolvemxlist_r(const char * const name)1589 struct firedns_mxlist *firedns_resolvemxlist_r(const char * const name) {
1590 	return firedns_resolvemxlist_i(name,firedns_getresult_r);
1591 }
1592 
firedns_resolvemxalist(const char * const name)1593 struct firedns_mxlist *firedns_resolvemxalist(const char * const name) {
1594 	int t,i,n,c = 0;
1595 	int cname_fd[256] = {0};
1596 	int alist_fd[256] = {0};
1597 	int a6list_fd[256] = {0};
1598 	void *ret;
1599 	struct firedns_mxlist *mxlist, *iter;
1600 	fd_set s;
1601 	struct timeval tv;
1602 	int firstround = 1;
1603 
1604 	mxlist = firedns_resolvemxlist_r(name);
1605 
1606 	if (mxlist == NULL) {
1607 		mxlist = firestring_malloc(sizeof(struct firedns_mxlist) + strlen(name) + 1 + FIREDNS_ALIGN);
1608 		mxlist->next = NULL;
1609 		mxlist->cname = NULL;
1610 		mxlist->ip4list = NULL;
1611 		mxlist->ip6list = NULL;
1612 		mxlist->protocol = FIREDNS_MX_SMTP;
1613 		mxlist->priority = 0;
1614 		mxlist->name = firedns_align(((char *)mxlist) + sizeof(struct firedns_mxlist));
1615 		strcpy(mxlist->name,name);
1616 	}
1617 
1618 	/* walk the list and allocate A space */
1619 	iter = mxlist;
1620 	while (iter != NULL) {
1621 		iter->ip4list = firestring_malloc(RESULTSIZE);
1622 		iter->ip6list = firestring_malloc(RESULTSIZE);
1623 		iter->cname = firestring_malloc(RESULTSIZE);
1624 		iter = iter->next;
1625 		c += 3;
1626 	}
1627 
1628 	/* check CNAME and A list records for each  MX returned */
1629 	for (t = 0; t < FIREDNS_TRIES; t++) {
1630 		iter = mxlist;
1631 		n = i = 0;
1632 		FD_ZERO(&s);
1633 		while (iter != NULL) {
1634 			if (cname_fd[i] != -1) {
1635 				if (!firstround)
1636 					(void) firedns_getresult(cname_fd[i]);
1637 				cname_fd[i] = firedns_getcname(iter->name);
1638 				if (cname_fd[i] == -1) {
1639 					firedns_free_mxalist(mxlist);
1640 					return NULL;
1641 				}
1642 				FD_SET(cname_fd[i],&s);
1643 				n = max(n,cname_fd[i]);
1644 			}
1645 			if (alist_fd[i] != -1) {
1646 				if (!firstround)
1647 					(void) firedns_getresult(alist_fd[i]);
1648 				alist_fd[i] = firedns_getip4list(iter->name);
1649 				if (alist_fd[i] == -1) {
1650 					firedns_free_mxalist(mxlist);
1651 					return NULL;
1652 				}
1653 				FD_SET(alist_fd[i],&s);
1654 				n = max(n,alist_fd[i]);
1655 			}
1656 			if (a6list_fd[i] != -1) {
1657 				if (!firstround)
1658 					(void) firedns_getresult(a6list_fd[i]);
1659 				a6list_fd[i] = firedns_getip6list(iter->name);
1660 				if (a6list_fd[i] == -1) {
1661 					firedns_free_mxalist(mxlist);
1662 					return NULL;
1663 				}
1664 				FD_SET(a6list_fd[i],&s);
1665 				n = max(n,a6list_fd[i]);
1666 			}
1667 			i++;
1668 			iter = iter->next;
1669 		}
1670 		firstround = 0;
1671 		tv.tv_sec = 5;
1672 		tv.tv_usec = 0;
1673 		i = select(n + 1,&s,NULL,NULL,&tv);
1674 
1675 		/* hack to make FIREDNS_TRIES sorta work as expected */
1676 		if (i == 0)
1677 			continue;
1678 		else
1679 			t--;
1680 
1681 		iter = mxlist;
1682 		i = 0;
1683 		while (iter != NULL) {
1684 			if (cname_fd[i] != -1 && FD_ISSET(cname_fd[i],&s)) {
1685 				ret = firedns_getresult_s(cname_fd[i],iter->cname);
1686 				if (ret == NULL) {
1687 					free(iter->cname);
1688 					iter->cname = NULL;
1689 				}
1690 				cname_fd[i] = -1;
1691 				c--;
1692 			}
1693 			if (alist_fd[i] != -1 && FD_ISSET(alist_fd[i],&s)) {
1694 				ret = firedns_getresult_s(alist_fd[i],(char *)iter->ip4list);
1695 				if (ret == NULL) {
1696 					free(iter->ip4list);
1697 					iter->ip4list = NULL;
1698 				}
1699 				alist_fd[i] = -1;
1700 				c--;
1701 			}
1702 			if (a6list_fd[i] != -1 && FD_ISSET(a6list_fd[i],&s)) {
1703 				ret = firedns_getresult_s(a6list_fd[i],(char *)iter->ip6list);
1704 				if (ret == NULL) {
1705 					free(iter->ip6list);
1706 					iter->ip6list = NULL;
1707 				}
1708 				a6list_fd[i] = -1;
1709 				c--;
1710 			}
1711 			i++;
1712 			iter = iter->next;
1713 		}
1714 		if (c == 0)
1715 			return mxlist;
1716 	}
1717 
1718 	iter = mxlist;
1719 	i = 0;
1720 	while (iter != NULL && c > 0) {
1721 		if (cname_fd[i] != -1) {
1722 			(void) firedns_getresult(cname_fd[i]);
1723 			free(iter->cname);
1724 			iter->cname = NULL;
1725 			cname_fd[i] = -1;
1726 			c--;
1727 		}
1728 		if (alist_fd[i] != -1) {
1729 			(void) firedns_getresult(alist_fd[i]);
1730 			free(iter->ip4list);
1731 			iter->ip4list = NULL;
1732 			alist_fd[i] = -1;
1733 			c--;
1734 		}
1735 		if (a6list_fd[i] != -1) {
1736 			(void) firedns_getresult(a6list_fd[i]);
1737 			free(iter->ip6list);
1738 			iter->ip6list = NULL;
1739 			a6list_fd[i] = -1;
1740 			c--;
1741 		}
1742 		i++;
1743 		iter = iter->next;
1744 	}
1745 
1746 	free(mxlist);
1747 	return NULL;
1748 }
1749 
firedns_resolvename4_i(const struct in_addr * restrict const ip,char * (* const result)(int))1750 static inline char *firedns_resolvename4_i(const struct in_addr * restrict const ip, char *(* const result)(int)) {
1751 	int fd;
1752 	int t,i;
1753 	char * restrict ret;
1754 	fd_set s;
1755 	struct timeval tv;
1756 
1757 	for (t = 0; t < FIREDNS_TRIES; t++) {
1758 		fd = firedns_getname4(ip);
1759 		if (fd == -1)
1760 			return NULL;
1761 		tv.tv_sec = 5;
1762 		tv.tv_usec = 0;
1763 		FD_ZERO(&s);
1764 		FD_SET(fd,&s);
1765 		i = select(fd + 1,&s,NULL,NULL,&tv);
1766 		ret = result(fd);
1767 		if (ret != NULL || i != 0)
1768 			return ret;
1769 	}
1770 	return NULL;
1771 }
1772 
firedns_resolvename4(const struct in_addr * const ip)1773 char *firedns_resolvename4(const struct in_addr * const ip) { /* immediate PTR query */
1774 	return firedns_resolvename4_i(ip,firedns_getresult);
1775 }
1776 
firedns_resolvename4_r(const struct in_addr * const ip)1777 char *firedns_resolvename4_r(const struct in_addr * const ip) {
1778 	return firedns_resolvename4_i(ip,firedns_getresult_r);
1779 }
1780 
firedns_resolvename6_i(const struct in6_addr * restrict const ip,char * (* const result)(int))1781 static inline char *firedns_resolvename6_i(const struct in6_addr * restrict const ip, char *(* const result)(int)) {
1782 	int fd;
1783 	int t,i;
1784 	char * restrict ret;
1785 	fd_set s;
1786 	struct timeval tv;
1787 
1788 	for (t = 0; t < FIREDNS_TRIES; t++) {
1789 		fd = firedns_getname6(ip);
1790 		if (fd == -1)
1791 			return NULL;
1792 		tv.tv_sec = 5;
1793 		tv.tv_usec = 0;
1794 		FD_ZERO(&s);
1795 		FD_SET(fd,&s);
1796 		i = select(fd + 1,&s,NULL,NULL,&tv);
1797 		ret = result(fd);
1798 		if (ret != NULL || i != 0)
1799 			return ret;
1800 	}
1801 	return NULL;
1802 }
1803 
firedns_resolvename6(const struct in6_addr * const ip)1804 char *firedns_resolvename6(const struct in6_addr * const ip) {
1805 	return firedns_resolvename6_i(ip,firedns_getresult);
1806 }
1807 
firedns_resolvename6_r(const struct in6_addr * const ip)1808 char *firedns_resolvename6_r(const struct in6_addr * const ip) {
1809 	return firedns_resolvename6_i(ip,firedns_getresult_r);
1810 }
1811 
firedns_resolvecname_i(const char * restrict const name,char * (* const result)(int))1812 static inline char *firedns_resolvecname_i(const char * restrict const name, char *(* const result)(int)) {
1813 	int fd;
1814 	int t,i;
1815 	char * restrict ret;
1816 	fd_set s;
1817 	struct timeval tv;
1818 
1819 	for (t = 0; t < FIREDNS_TRIES; t++) {
1820 		fd = firedns_getcname(name);
1821 		if (fd == -1)
1822 			return NULL;
1823 		tv.tv_sec = 5;
1824 		tv.tv_usec = 0;
1825 		FD_ZERO(&s);
1826 		FD_SET(fd,&s);
1827 		i = select(fd + 1,&s,NULL,NULL,&tv);
1828 		ret = result(fd);
1829 		if (ret != NULL || i != 0)
1830 			return ret;
1831 	}
1832 	return NULL;
1833 }
1834 
firedns_resolvecname(const char * const name)1835 char *firedns_resolvecname(const char * const name) {
1836 	return firedns_resolvecname_i(name,firedns_getresult);
1837 }
1838 
firedns_resolvecname_r(const char * const name)1839 char *firedns_resolvecname_r(const char * const name) {
1840 	return firedns_resolvecname_i(name,firedns_getresult_r);
1841 }
1842 
firedns_free_mxalist(struct firedns_mxlist * list)1843 void firedns_free_mxalist(struct firedns_mxlist *list) {
1844 	struct firedns_mxlist *iter;
1845 
1846 	iter = list;
1847 	while (iter != NULL) {
1848 		if (iter->cname != NULL)
1849 			free(iter->cname);
1850 		if (iter->ip4list != NULL)
1851 			free(iter->ip4list);
1852 		if (iter->ip6list != NULL)
1853 			free(iter->ip6list);
1854 		iter = iter->next;
1855 	}
1856 
1857 	free(list);
1858 }
1859