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