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