2 * Routines for DNS packet disassembly
4 * $Id: packet-dns.c,v 1.15 1999/01/28 21:29:35 gram Exp $
6 * Ethereal - Network traffic analyzer
7 * By Gerald Combs <gerald@zing.org>
8 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36 #ifdef HAVE_SYS_TYPES_H
37 # include <sys/types.h>
40 #ifdef HAVE_NETINET_IN_H
41 # include <netinet/in.h>
46 #include "packet-dns.h"
50 /* DNS structs and definitions */
52 /* Offsets of fields in the DNS header. */
60 /* Length of DNS header. */
64 #define T_A 1 /* host address */
65 #define T_NS 2 /* authoritative server */
66 #define T_CNAME 5 /* canonical name */
67 #define T_SOA 6 /* start of authority zone */
68 #define T_WKS 11 /* well known service */
69 #define T_PTR 12 /* domain name pointer */
70 #define T_HINFO 13 /* host information */
71 #define T_MX 15 /* mail routing information */
72 #define T_TXT 16 /* text strings */
73 #define T_AAAA 28 /* IP6 Address */
75 /* Bit fields in the flags */
76 #define F_RESPONSE (1<<15) /* packet is response */
77 #define F_OPCODE (0xF<<11) /* query opcode */
78 #define F_AUTHORITATIVE (1<<10) /* response is authoritative */
79 #define F_TRUNCATED (1<<9) /* response is truncated */
80 #define F_RECDESIRED (1<<8) /* recursion desired */
81 #define F_RECAVAIL (1<<7) /* recursion available */
82 #define F_RCODE (0xF<<0) /* reply code */
85 #define OPCODE_QUERY (0<<11) /* standard query */
86 #define OPCODE_IQUERY (1<<11) /* inverse query */
87 #define OPCODE_STATUS (2<<11) /* server status request */
90 #define RCODE_NOERROR (0<<0)
91 #define RCODE_FMTERROR (1<<0)
92 #define RCODE_SERVFAIL (2<<0)
93 #define RCODE_NAMEERROR (3<<0)
94 #define RCODE_NOTIMPL (4<<0)
95 #define RCODE_REFUSED (5<<0)
98 dns_type_name (int type)
100 char *type_names[36] = {
101 "unused", "A", "NS", "MD", "MF", "CNAME", "SOA", "MB", "MG", "MR",
102 "NULL", "WKS", "PTR", "HINFO", "MINFO", "MX", "TXT", "RP", "AFSDB",
103 "X25", "ISDN", "RT", "NSAP", "NSAP_PTR", "SIG", "KEY", "PX", "GPOS",
104 "AAAA", "LOC", "NXT", "EID", "NIMLOC", "SRV", "ATMA", "NAPTR"
108 return type_names[type];
141 dns_class_name(int class)
150 class_name = "chaos";
153 class_name = "hesiod";
156 class_name = "unknown";
164 is_compressed_name(const u_char *foo)
166 return (0xc0 == (*foo & 0xc0));
171 get_compressed_name_offset(const u_char *ptr)
173 return ((*ptr & ~0xc0) << 8) | *(ptr+1);
178 copy_one_name_component(const u_char *dataptr, char *nameptr)
183 len = n = *dataptr++;
188 *nameptr++ = *dataptr++;
195 copy_name_component_rec(const u_char *dns_data_ptr, const u_char *dataptr,
196 char *nameptr, int *real_string_len)
203 if (is_compressed_name(dataptr)) {
205 offset = get_compressed_name_offset(dataptr);
206 dataptr = dns_data_ptr + offset;
207 copy_name_component_rec(dns_data_ptr, dataptr, nameptr, &str_len);
208 *real_string_len += str_len;
213 str_len = copy_one_name_component(dataptr, nameptr);
214 *real_string_len = str_len;
215 dataptr += str_len + 1;
223 (*real_string_len)++;
227 len += copy_name_component_rec(dns_data_ptr, dataptr, nameptr, &str_len);
228 *real_string_len += str_len;
237 get_dns_name(const u_char *dns_data_ptr, const u_char *pd, int offset,
238 char *nameptr, int maxname)
241 const u_char *dataptr = pd + offset;
244 memset (nameptr, 0, maxname);
245 len = copy_name_component_rec(dns_data_ptr, dataptr, nameptr, &str_len);
252 get_dns_name_type_class (const u_char *dns_data_ptr,
265 const u_char *pd_save;
267 name_len = get_dns_name(dns_data_ptr, pd, offset, name, sizeof(name));
277 strcpy (name_ret, name);
280 *name_len_ret = name_len;
288 dissect_dns_query(const u_char *dns_data_ptr, const u_char *pd, int offset,
299 const u_char *data_start;
300 GtkWidget *q_tree, *tq;
302 data_start = dptr = pd + offset;
304 len = get_dns_name_type_class(dns_data_ptr, pd, offset, name, &name_len,
308 type_name = dns_type_name(type);
309 class_name = dns_class_name(class);
311 tq = add_item_to_tree(dns_tree, offset, len, "%s: type %s, class %s",
312 name, type_name, class_name);
313 q_tree = gtk_tree_new();
314 add_subtree(tq, q_tree, ETT_DNS_QD);
316 add_item_to_tree(q_tree, offset, name_len, "Name: %s", name);
319 add_item_to_tree(q_tree, offset, 2, "Type: %s", type_name);
322 add_item_to_tree(q_tree, offset, 2, "Class: %s", class_name);
325 return dptr - data_start;
330 add_rr_to_tree(GtkWidget *trr, int rr_type, int offset, const char *name,
331 int namelen, const char *type_name, const char *class_name, u_int ttl,
336 rr_tree = gtk_tree_new();
337 add_subtree(trr, rr_tree, rr_type);
338 add_item_to_tree(rr_tree, offset, namelen, "Name: %s", name);
340 add_item_to_tree(rr_tree, offset, 2, "Type: %s", type_name);
342 add_item_to_tree(rr_tree, offset, 2, "Class: %s", class_name);
344 add_item_to_tree(rr_tree, offset, 4, "Time to live: %s",
345 time_secs_to_str(ttl));
347 add_item_to_tree(rr_tree, offset, 2, "Data length: %u", data_len);
352 dissect_dns_answer(const u_char *dns_data_ptr, const u_char *pd, int offset,
363 const u_char *data_start;
366 GtkWidget *rr_tree, *trr;
368 data_start = dptr = pd + offset;
370 len = get_dns_name_type_class(dns_data_ptr, pd, offset, name, &name_len,
374 type_name = dns_type_name(type);
375 class_name = dns_class_name(class);
380 data_len = pntohs(dptr);
384 case T_A: /* "A" record */
385 trr = add_item_to_tree(dns_tree, offset, (dptr - data_start) + data_len,
386 "%s: type %s, class %s, addr %s",
387 name, type_name, class_name,
388 ip_to_str((guint8 *)dptr));
389 rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len, type_name,
390 class_name, ttl, data_len);
391 offset += (dptr - data_start);
392 add_item_to_tree(rr_tree, offset, 4, "Addr: %s",
393 ip_to_str((guint8 *)dptr));
396 case T_NS: /* "NS" record */
398 char ns_name[MAXDNAME];
401 ns_name_len = get_dns_name(dns_data_ptr, dptr, 0, ns_name, sizeof(ns_name));
402 trr = add_item_to_tree(dns_tree, offset, (dptr - data_start) + data_len,
403 "%s: type %s, class %s, ns %s",
404 name, type_name, class_name, ns_name);
405 rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
406 type_name, class_name, ttl, data_len);
407 offset += (dptr - data_start);
408 add_item_to_tree(rr_tree, offset, ns_name_len, "Name server: %s", ns_name);
412 /* TODO: parse more record types */
415 trr = add_item_to_tree(dns_tree, offset, (dptr - data_start) + data_len,
416 "%s: type %s, class %s",
417 name, type_name, class_name);
418 rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len, type_name,
419 class_name, ttl, data_len);
420 offset += (dptr - data_start);
421 add_item_to_tree(rr_tree, offset, data_len, "Data");
426 return dptr - data_start;
430 dissect_query_records(const u_char *dns_data_ptr, int count, const u_char *pd,
431 int cur_off, GtkWidget *dns_tree)
434 GtkWidget *qatree, *ti;
437 ti = add_item_to_tree(GTK_WIDGET(dns_tree),
438 start_off, 0, "Queries");
439 qatree = gtk_tree_new();
440 add_subtree(ti, qatree, ETT_DNS_QRY);
442 cur_off += dissect_dns_query(dns_data_ptr, pd, cur_off, qatree);
443 set_item_len(ti, cur_off - start_off);
445 return cur_off - start_off;
451 dissect_answer_records(const u_char *dns_data_ptr, int count,
452 const u_char *pd, int cur_off, GtkWidget *dns_tree,
456 GtkWidget *qatree, *ti;
459 ti = add_item_to_tree(GTK_WIDGET(dns_tree),
461 qatree = gtk_tree_new();
462 add_subtree(ti, qatree, ETT_DNS_ANS);
464 cur_off += dissect_dns_answer(dns_data_ptr, pd, cur_off, qatree);
465 set_item_len(ti, cur_off - start_off);
467 return cur_off - start_off;
472 dissect_dns(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
473 const u_char *dns_data_ptr;
474 GtkWidget *dns_tree, *ti, *field_tree, *tf;
475 guint16 id, flags, quest, ans, auth, add;
478 static const value_string opcode_vals[] = {
479 { OPCODE_QUERY, "Standard query" },
480 { OPCODE_IQUERY, "Inverse query" },
481 { OPCODE_STATUS, "Server status request" },
483 static const value_string rcode_vals[] = {
484 { RCODE_NOERROR, "No error" },
485 { RCODE_FMTERROR, "Format error" },
486 { RCODE_SERVFAIL, "Server failure" },
487 { RCODE_NAMEERROR, "Name error" },
488 { RCODE_NOTIMPL, "Not implemented" },
489 { RCODE_REFUSED, "Refused" },
492 dns_data_ptr = &pd[offset];
494 /* To do: check for runts, errs, etc. */
495 id = pntohs(&pd[offset + DNS_ID]);
496 flags = pntohs(&pd[offset + DNS_FLAGS]);
497 quest = pntohs(&pd[offset + DNS_QUEST]);
498 ans = pntohs(&pd[offset + DNS_ANS]);
499 auth = pntohs(&pd[offset + DNS_AUTH]);
500 add = pntohs(&pd[offset + DNS_ADD]);
502 if (check_col(fd, COL_PROTOCOL))
503 col_add_str(fd, COL_PROTOCOL, "DNS (UDP)");
504 if (check_col(fd, COL_INFO)) {
505 col_add_fstr(fd, COL_INFO, "%s%s",
506 val_to_str(flags & F_OPCODE, opcode_vals,
507 "Unknown operation (%x)"),
508 (flags & F_RESPONSE) ? " response" : "");
512 ti = add_item_to_tree(GTK_WIDGET(tree), offset, 4,
513 (flags & F_RESPONSE) ? "DNS response" : "DNS query");
515 dns_tree = gtk_tree_new();
516 add_subtree(ti, dns_tree, ETT_DNS);
518 add_item_to_tree(dns_tree, offset + DNS_ID, 2, "Transaction ID: 0x%04x",
521 strcpy(buf, val_to_str(flags & F_OPCODE, opcode_vals, "Unknown operation"));
522 if (flags & F_RESPONSE) {
523 strcat(buf, " response");
525 strcat(buf, val_to_str(flags & F_RCODE, rcode_vals,
528 tf = add_item_to_tree(dns_tree, offset + DNS_FLAGS, 2, "Flags: 0x%04x (%s)",
530 field_tree = gtk_tree_new();
531 add_subtree(tf, field_tree, ETT_DNS_FLAGS);
532 add_item_to_tree(field_tree, offset + DNS_FLAGS, 2, "%s",
533 decode_boolean_bitfield(flags, F_RESPONSE,
534 2*8, "Response", "Query"));
535 add_item_to_tree(field_tree, offset + DNS_FLAGS, 2, "%s",
536 decode_enumerated_bitfield(flags, F_OPCODE,
537 2*8, opcode_vals, "%s"));
538 if (flags & F_RESPONSE) {
539 add_item_to_tree(field_tree, offset + DNS_FLAGS, 2, "%s",
540 decode_boolean_bitfield(flags, F_AUTHORITATIVE,
542 "Server is an authority for domain",
543 "Server isn't an authority for domain"));
545 add_item_to_tree(field_tree, offset + DNS_FLAGS, 2, "%s",
546 decode_boolean_bitfield(flags, F_TRUNCATED,
548 "Message is truncated",
549 "Message is not truncated"));
550 add_item_to_tree(field_tree, offset + DNS_FLAGS, 2, "%s",
551 decode_boolean_bitfield(flags, F_RECDESIRED,
553 "Do query recursively",
554 "Don't do query recursively"));
555 if (flags & F_RESPONSE) {
556 add_item_to_tree(field_tree, offset + DNS_FLAGS, 2, "%s",
557 decode_boolean_bitfield(flags, F_RECAVAIL,
559 "Server can do recursive queries",
560 "Server can't do recursive queries"));
561 add_item_to_tree(field_tree, offset + DNS_FLAGS, 2, "%s",
562 decode_enumerated_bitfield(flags, F_RCODE,
563 2*8, rcode_vals, "%s"));
565 add_item_to_tree(dns_tree, offset + DNS_QUEST, 2, "Questions: %d", quest);
566 add_item_to_tree(dns_tree, offset + DNS_ANS, 2, "Answer RRs: %d", ans);
567 add_item_to_tree(dns_tree, offset + DNS_AUTH, 2, "Authority RRs: %d", auth);
568 add_item_to_tree(dns_tree, offset + DNS_ADD, 2, "Additional RRs: %d", add);
570 cur_off = offset + DNS_HDRLEN;
573 cur_off += dissect_query_records(dns_data_ptr, quest, pd, cur_off,
577 cur_off += dissect_answer_records(dns_data_ptr, ans, pd, cur_off,
578 dns_tree, "Answers");
581 cur_off += dissect_answer_records(dns_data_ptr, auth, pd, cur_off,
582 dns_tree, "Authoritative nameservers");
585 cur_off += dissect_answer_records(dns_data_ptr, add, pd, cur_off,
586 dns_tree, "Additional records");