When dissecting DNS or NBNS queries or replies, add the item to the tree
[obnox/wireshark/wip.git] / packet-dns.c
1 /* packet-dns.c
2  * Routines for DNS packet disassembly
3  *
4  * $Id: packet-dns.c,v 1.10 1998/12/04 05:59:10 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * 
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.
15  * 
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.
20  * 
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.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <gtk/gtk.h>
31
32 #include <stdio.h>
33 #include <memory.h>
34
35 #ifdef HAVE_SYS_TYPES_H
36 # include <sys/types.h>
37 #endif
38
39 #ifdef HAVE_NETINET_IN_H
40 # include <netinet/in.h>
41 #endif
42
43 #include "ethereal.h"
44 #include "packet.h"
45 #include "packet-dns.h"
46
47
48 /* DNS structs and definitions */
49
50 typedef struct _e_dns {
51   guint16 dns_id;
52   guint16 dns_flags;
53   guint16 dns_quest;
54   guint16 dns_ans;
55   guint16 dns_auth;
56   guint16 dns_add;
57 } e_dns;
58
59 /* type values  */
60 #define T_A             1               /* host address */
61 #define T_NS            2               /* authoritative server */
62 #define T_CNAME         5               /* canonical name */
63 #define T_SOA           6               /* start of authority zone */
64 #define T_WKS           11              /* well known service */
65 #define T_PTR           12              /* domain name pointer */
66 #define T_HINFO         13              /* host information */
67 #define T_MX            15              /* mail routing information */
68 #define T_TXT           16              /* text strings */
69 #define T_AAAA          28              /* IP6 Address */
70
71
72 static char *
73 dns_type_name (int type)
74 {
75   char *type_names[36] = {
76     "unused", "A", "NS", "MD", "MF", "CNAME", "SOA", "MB", "MG", "MR",
77     "NULL", "WKS", "PTR", "HINFO", "MINFO", "MX", "TXT", "RP", "AFSDB",
78     "X25", "ISDN", "RT", "NSAP", "NSAP_PTR", "SIG", "KEY", "PX", "GPOS",
79     "AAAA", "LOC", "NXT", "EID", "NIMLOC", "SRV", "ATMA", "NAPTR"
80   };
81   
82   if (type <= 35)
83     return type_names[type];
84   
85   /* special cases */
86   switch (type) 
87     {
88       /* non standard  */
89     case 100:
90       return "UINFO";
91     case 101:
92       return "UID";
93     case 102:
94       return "GID";
95     case 103:
96       return "UNSPEC";
97       
98       /* queries  */
99     case 251:
100       return "IXFR";
101     case 252:
102       return "AXFR";
103     case 253:
104       return "MAILB";
105     case 254:
106       return "MAILA";
107     case 255:
108       return "ANY";
109     }
110   
111   return "unknown";
112 }
113
114
115 char *
116 dns_class_name(int class)
117 {
118   char *class_name;
119   
120   switch (class) {
121   case 1:
122     class_name = "inet";
123     break;
124   case 3:
125     class_name = "chaos";
126     break;
127   case 4:
128     class_name = "hesiod";
129     break;
130   default:
131     class_name = "unknown";
132   }
133
134   return class_name;
135 }
136   
137
138 static int
139 is_compressed_name(const u_char *foo)
140 {
141   return (0xc0 == (*foo & 0xc0));
142 }
143
144
145 static int
146 get_compressed_name_offset(const u_char *ptr)
147 {
148   return ((*ptr & ~0xc0) << 8) | *(ptr+1);
149 }
150
151
152 static int
153 copy_one_name_component(const u_char *dataptr, char *nameptr)
154 {
155   int len;
156   int n;
157   
158   len = n  = *dataptr++;
159   if (0 == len)
160     return 0;
161   
162   while (n-- > 0)
163     *nameptr++ = *dataptr++;
164
165   return len;
166 }
167
168
169 static int
170 copy_name_component_rec(const u_char *dns_data_ptr, const u_char *dataptr,
171   char *nameptr, int *real_string_len)
172 {
173   int len = 0;
174   int str_len;
175   int offset;
176   int compress = 0;
177   
178   if (is_compressed_name(dataptr)) {
179     compress = 1;
180     offset = get_compressed_name_offset(dataptr);
181     dataptr = dns_data_ptr + offset;
182     copy_name_component_rec(dns_data_ptr, dataptr, nameptr, &str_len);
183     *real_string_len += str_len;
184     nameptr += str_len;
185     len = 2;
186   }
187   else {
188     str_len = copy_one_name_component(dataptr, nameptr);
189     *real_string_len = str_len;
190     dataptr += str_len + 1;
191     len     += str_len + 1;
192     nameptr += str_len;
193   }
194
195   if (compress)
196     return len;
197   
198   (*real_string_len)++;
199
200   if (*dataptr > 0) {
201     *nameptr++ = '.';
202     len += copy_name_component_rec(dns_data_ptr, dataptr, nameptr, &str_len);
203     *real_string_len += str_len;
204     return len;
205   }
206
207   return len + 1;
208 }
209
210
211 int
212 get_dns_name(const u_char *dns_data_ptr, const u_char *pd, int offset,
213   char *nameptr, int maxname)
214 {
215   int len;
216   const u_char *dataptr = pd + offset;
217   int str_len = 0;
218
219   memset (nameptr, 0, maxname);
220   len = copy_name_component_rec(dns_data_ptr, dataptr, nameptr, &str_len);
221   
222   return len;
223 }
224
225
226 static int
227 get_dns_name_type_class (const u_char *dns_data_ptr,
228                          const u_char *pd,
229                          int offset,
230                          char *name_ret,
231                          int *name_len_ret,
232                          int *type_ret,
233                          int *class_ret)
234 {
235   int len;
236   int name_len;
237   int type;
238   int class;
239   char name[MAXDNAME];
240   const u_char *pd_save;
241
242   name_len = get_dns_name(dns_data_ptr, pd, offset, name, sizeof(name));
243   pd += offset;
244   pd_save = pd;
245   pd += name_len;
246   
247   type = pntohs(pd);
248   pd += 2;
249   class = pntohs(pd);
250   pd += 2;
251
252   strcpy (name_ret, name);
253   *type_ret = type;
254   *class_ret = class;
255   *name_len_ret = name_len;
256
257   len = pd - pd_save;
258   return len;
259 }
260
261
262 static int
263 dissect_dns_query(const u_char *dns_data_ptr, const u_char *pd, int offset,
264   GtkWidget *dns_tree)
265 {
266   int len;
267   char name[MAXDNAME];
268   int name_len;
269   int type;
270   int class;
271   char *class_name;
272   char *type_name;
273   const u_char *dptr;
274   const u_char *data_start;
275   GtkWidget *q_tree, *tq;
276
277   data_start = dptr = pd + offset;
278
279   len = get_dns_name_type_class(dns_data_ptr, pd, offset, name, &name_len,
280     &type, &class);
281   dptr += len;
282
283   type_name = dns_type_name(type);
284   class_name = dns_class_name(class);
285
286   tq = add_item_to_tree(dns_tree, offset, len, "%s: type %s, class %s", 
287                    name, type_name, class_name);
288   q_tree = gtk_tree_new();
289   add_subtree(tq, q_tree, ETT_DNS_QD);
290
291   add_item_to_tree(q_tree, offset, name_len, "Name: %s", name);
292   offset += name_len;
293
294   add_item_to_tree(q_tree, offset, 2, "Type: %s", type_name);
295   offset += 2;
296
297   add_item_to_tree(q_tree, offset, 2, "Class: %s", class_name);
298   offset += 2;
299   
300   return dptr - data_start;
301 }
302
303
304 GtkWidget *
305 add_rr_to_tree(GtkWidget *trr, int rr_type, int offset, const char *name,
306   int namelen, const char *type_name, const char *class_name, u_int ttl,
307   u_short data_len)
308 {
309   GtkWidget *rr_tree;
310
311   rr_tree = gtk_tree_new();
312   add_subtree(trr, rr_tree, rr_type);
313   add_item_to_tree(rr_tree, offset, namelen, "Name: %s", name);
314   offset += namelen;
315   add_item_to_tree(rr_tree, offset, 2, "Type: %s", type_name);
316   offset += 2;
317   add_item_to_tree(rr_tree, offset, 2, "Class: %s", class_name);
318   offset += 2;
319   add_item_to_tree(rr_tree, offset, 4, "Time to live: %u", ttl);
320   offset += 4;
321   add_item_to_tree(rr_tree, offset, 2, "Data length: %u", data_len);
322   return rr_tree;
323 }
324
325 static int
326 dissect_dns_answer(const u_char *dns_data_ptr, const u_char *pd, int offset,
327   GtkWidget *dns_tree)
328 {
329   int len;
330   char name[MAXDNAME];
331   int name_len;
332   int type;
333   int class;
334   char *class_name;
335   char *type_name;
336   const u_char *dptr;
337   const u_char *data_start;
338   u_int ttl;
339   u_short data_len;
340   GtkWidget *rr_tree, *trr;
341
342   data_start = dptr = pd + offset;
343
344   len = get_dns_name_type_class(dns_data_ptr, pd, offset, name, &name_len,
345     &type, &class);
346   dptr += len;
347
348   type_name = dns_type_name(type);
349   class_name = dns_class_name(class);
350
351   ttl = pntohl(dptr);
352   dptr += 4;
353
354   data_len = pntohs(dptr);
355   dptr += 2;
356
357   switch (type) {
358   case T_A:             /* "A" record */
359     trr = add_item_to_tree(dns_tree, offset, (dptr - data_start) + data_len,
360                      "%s: type %s, class %s, addr %s",
361                      name, type_name, class_name,
362                      ip_to_str((guint8 *)dptr));
363     rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len, type_name,
364                      class_name, ttl, data_len);
365     offset += (dptr - data_start);
366     add_item_to_tree(rr_tree, offset, 4, "Addr: %s",
367                      ip_to_str((guint8 *)dptr));
368     break;
369
370   case T_NS:            /* "NS" record */
371     {
372       char ns_name[MAXDNAME];
373       int ns_name_len;
374       
375       ns_name_len = get_dns_name(dns_data_ptr, dptr, 0, ns_name, sizeof(ns_name));
376       trr = add_item_to_tree(dns_tree, offset, (dptr - data_start) + data_len,
377                        "%s: type %s, class %s, ns %s",
378                        name, type_name, class_name, ns_name);
379       rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
380                        type_name, class_name, ttl, data_len);
381       offset += (dptr - data_start);
382       add_item_to_tree(rr_tree, offset, ns_name_len, "Name server: %s", ns_name);
383     }
384     break;
385
386     /* TODO: parse more record types */
387       
388   default:
389     trr = add_item_to_tree(dns_tree, offset, (dptr - data_start) + data_len,
390                      "%s: type %s, class %s",
391                      name, type_name, class_name);
392     rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len, type_name,
393                        class_name, ttl, data_len);
394     offset += (dptr - data_start);
395     add_item_to_tree(rr_tree, offset, data_len, "Data");
396   }
397   
398   dptr += data_len;
399         
400   return dptr - data_start;
401 }
402
403 static int
404 dissect_query_records(const u_char *dns_data_ptr, int count, const u_char *pd, 
405                       int cur_off, GtkWidget *dns_tree)
406 {
407   int start_off;
408   GtkWidget *qatree, *ti;
409   
410   start_off = cur_off;
411   ti = add_item_to_tree(GTK_WIDGET(dns_tree), 
412                         start_off, 0, "Queries");
413   qatree = gtk_tree_new();
414   add_subtree(ti, qatree, ETT_DNS_QRY);
415   while (count-- > 0)
416     cur_off += dissect_dns_query(dns_data_ptr, pd, cur_off, qatree);
417   set_item_len(ti, cur_off - start_off);
418
419   return cur_off - start_off;
420 }
421
422
423
424 static int
425 dissect_answer_records(const u_char *dns_data_ptr, int count,
426                        const u_char *pd, int cur_off, GtkWidget *dns_tree,
427                        char *name)
428 {
429   int start_off;
430   GtkWidget *qatree, *ti;
431   
432   start_off = cur_off;
433   ti = add_item_to_tree(GTK_WIDGET(dns_tree),
434                         start_off, 0, name);
435   qatree = gtk_tree_new();
436   add_subtree(ti, qatree, ETT_DNS_ANS);
437   while (count-- > 0)
438     cur_off += dissect_dns_answer(dns_data_ptr, pd, cur_off, qatree);
439   set_item_len(ti, cur_off - start_off);
440
441   return cur_off - start_off;
442 }
443
444
445 void
446 dissect_dns(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
447   const u_char *dns_data_ptr;
448   e_dns     *dh;
449   GtkWidget *dns_tree, *ti;
450   guint16    id, flags, quest, ans, auth, add;
451   int query = 0;
452   int cur_off;
453
454   dns_data_ptr = &pd[offset];
455   dh = (e_dns *) dns_data_ptr;
456
457   /* To do: check for runts, errs, etc. */
458   id    = ntohs(dh->dns_id);
459   flags = ntohs(dh->dns_flags);
460   quest = ntohs(dh->dns_quest);
461   ans   = ntohs(dh->dns_ans);
462   auth  = ntohs(dh->dns_auth);
463   add   = ntohs(dh->dns_add);
464   
465   query = ! (flags & (1 << 15));
466   
467   if (check_col(fd, COL_PROTOCOL))
468     col_add_str(fd, COL_PROTOCOL, "DNS (UDP)");
469   if (check_col(fd, COL_INFO))
470     col_add_str(fd, COL_INFO, query ? "Query" : "Response");
471   
472   if (tree) {
473     ti = add_item_to_tree(GTK_WIDGET(tree), offset, 4,
474                           query ? "DNS query" : "DNS response");
475     
476     dns_tree = gtk_tree_new();
477     add_subtree(ti, dns_tree, ETT_DNS);
478     
479     add_item_to_tree(dns_tree, offset,      2, "ID: 0x%04x", id);
480
481     add_item_to_tree(dns_tree, offset +  2, 2, "Flags: 0x%04x", flags);
482     add_item_to_tree(dns_tree, offset +  4, 2, "Questions: %d", quest);
483     add_item_to_tree(dns_tree, offset +  6, 2, "Answer RRs: %d", ans);
484     add_item_to_tree(dns_tree, offset +  8, 2, "Authority RRs: %d", auth);
485     add_item_to_tree(dns_tree, offset + 10, 2, "Additional RRs: %d", add);
486
487     cur_off = offset + 12;
488     
489     if (quest > 0)
490       cur_off += dissect_query_records(dns_data_ptr, quest, pd, cur_off,
491                                         dns_tree);
492     
493     if (ans > 0)
494       cur_off += dissect_answer_records(dns_data_ptr, ans, pd, cur_off,
495           dns_tree, "Answers");
496     
497     if (auth > 0)
498       cur_off += dissect_answer_records(dns_data_ptr, auth, pd, cur_off,
499           dns_tree, "Authoritative nameservers");
500
501     if (add > 0)
502       cur_off += dissect_answer_records(dns_data_ptr, add, pd, cur_off,
503           dns_tree, "Additional records");
504   }
505 }