Initial revision
[obnox/wireshark/wip.git] / packet-dns.c
1 /* packet-dns.c
2  * Routines for DNS packet disassembly
3  *
4  * Ethereal - Network traffic analyzer
5  * By Gerald Combs <gerald@zing.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * 
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  * 
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <gtk/gtk.h>
29 #include <pcap.h>
30
31 #include <stdio.h>
32
33 #ifdef HAVE_SYS_TYPES_H
34 # include <sys/types.h>
35 #endif
36
37 #ifdef HAVE_NETINET_IN_H
38 # include <netinet/in.h>
39 #endif
40
41 #include "packet.h"
42
43
44 /* DNS structs and definitions */
45
46 typedef struct _e_dns {
47   guint16 dns_id;
48   guint16 dns_flags;
49   guint16 dns_quest;
50   guint16 dns_ans;
51   guint16 dns_auth;
52   guint16 dns_add;
53 } e_dns;
54
55 #define MAXDNAME        1025            /* maximum domain name */
56
57 /* type values  */
58 #define T_A             1               /* host address */
59 #define T_NS            2               /* authoritative server */
60 #define T_CNAME         5               /* canonical name */
61 #define T_SOA           6               /* start of authority zone */
62 #define T_WKS           11              /* well known service */
63 #define T_PTR           12              /* domain name pointer */
64 #define T_HINFO         13              /* host information */
65 #define T_MX            15              /* mail routing information */
66 #define T_TXT           16              /* text strings */
67 #define T_AAAA          28              /* IP6 Address */
68
69
70 static const u_char *dns_data_ptr;
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 static 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 *dataptr, char *nameptr, int *real_string_len)
171 {
172   int len = 0;
173   int str_len;
174   int offset;
175   int compress = 0;
176   
177   if (is_compressed_name(dataptr)) {
178     compress = 1;
179     offset = get_compressed_name_offset(dataptr);
180     dataptr = dns_data_ptr + offset;
181     copy_name_component_rec(dataptr, nameptr, &str_len);
182     *real_string_len += str_len;
183     nameptr += str_len;
184     len = 2;
185   }
186   else {
187     str_len = copy_one_name_component(dataptr, nameptr);
188     *real_string_len = str_len;
189     dataptr += str_len + 1;
190     len     += str_len + 1;
191     nameptr += str_len;
192   }
193
194   if (compress)
195     return len;
196   
197   (*real_string_len)++;
198
199   if (*dataptr > 0) {
200     *nameptr++ = '.';
201     len += copy_name_component_rec(dataptr, nameptr, &str_len);
202     *real_string_len += str_len;
203     return len;
204   }
205
206   return len + 1;
207 }
208
209
210 static int
211 get_dns_name(const u_char *pd, int offset, char *nameptr, int maxname)
212 {
213   int len;
214   const u_char *dataptr = pd + offset;
215   int str_len = 0;
216
217   memset (nameptr, 0, maxname);
218   len = copy_name_component_rec(dataptr, nameptr, &str_len);
219   
220   return len;
221 }
222
223
224 static int
225 get_dns_name_type_class (const u_char *pd,
226                          int offset,
227                          char *name_ret, 
228                          int *type_ret,
229                          int *class_ret)
230 {
231   int len;
232   int name_len;
233   int type;
234   int class;
235   char name[MAXDNAME];
236   const u_char *pd_save;
237
238   name_len = get_dns_name(pd, offset, name, sizeof(name));
239   pd += offset;
240   pd_save = pd;
241   pd += name_len;
242   
243   type = (*pd << 8) | *(pd + 1);
244   pd += 2;
245   class = (*pd << 8) | *(pd + 1);
246   pd += 2;
247
248   strcpy (name_ret, name);
249   *type_ret = type;
250   *class_ret = class;
251
252   len = pd - pd_save;
253   return len;
254 }
255
256
257 static int
258 dissect_dns_query(const u_char *pd, int offset, GtkWidget *dns_tree)
259 {
260   int len;
261   char name[MAXDNAME];
262   int type;
263   int class;
264   char *class_name;
265   char *type_name;
266
267   len = get_dns_name_type_class (pd, offset, name, &type, &class);
268
269   type_name = dns_type_name(type);
270   class_name = dns_class_name(class);
271   
272   add_item_to_tree(dns_tree, offset, len, "%s: type %s, class %s", 
273                    name, type_name, class_name );
274   
275   return len;
276 }
277
278
279 static int
280 dissect_dns_answer(const u_char *pd, int offset, GtkWidget *dns_tree)
281 {
282   int len;
283   char name[MAXDNAME];
284   int type;
285   int class;
286   char *class_name;
287   char *type_name;
288   const u_char *dptr;
289   const u_char *data_start;
290   const u_char *res_ptr;
291   u_int ttl;
292   u_short data_len;
293
294   data_start = dptr = pd + offset;
295
296   len = get_dns_name_type_class (pd, offset, name, &type, &class);
297   dptr += len;
298
299   /* this works regardless of the alignment  */
300   ttl = (*dptr << 24) | *(dptr + 1) << 16 | *(dptr + 2) << 8 | *(dptr + 3);
301   dptr += 4;
302   data_len = (*dptr << 8) | *(dptr + 1);
303   dptr += 2;
304
305   type_name = dns_type_name(type);
306   class_name = dns_class_name(class);
307   res_ptr = dptr;
308
309   /* skip the resource data  */
310   dptr += data_len;
311   
312   len = dptr - data_start;
313   
314   switch (type) {
315   case T_A:             /* "A" record */
316     add_item_to_tree(dns_tree, offset, len, 
317                      "%s: type %s, class %s, addr %d.%d.%d.%d",
318                      name, type_name, class_name,
319                      *res_ptr, *(res_ptr+1), *(res_ptr+2), *(res_ptr+3));
320     break;
321
322   case T_NS:            /* "NS" record */
323     {
324       char ns_name[MAXDNAME];
325       
326       get_dns_name(res_ptr, 0, ns_name, sizeof(ns_name));
327       add_item_to_tree(dns_tree, offset, len, 
328                        "%s: %s, type %s, class %s",
329                        name, ns_name, type_name, class_name);
330       
331     }
332     break;
333
334     /* TODO: parse more record types */
335       
336     default:
337     add_item_to_tree(dns_tree, offset, len, "%s: type %s, class %s", 
338                      name, type_name, class_name);
339   }
340   
341   return len;
342 }
343
344
345 static int
346 dissect_answer_records(int count, const u_char *pd, int cur_off, 
347                        GtkWidget *dns_tree, char *name)
348 {
349   int start_off;
350   GtkWidget *qatree, *ti;
351   
352   qatree = gtk_tree_new();
353   start_off = cur_off;
354
355   while (count-- > 0)
356     cur_off += dissect_dns_answer(pd, cur_off, qatree);
357   ti = add_item_to_tree(GTK_WIDGET(dns_tree), start_off, cur_off - start_off, name);
358   add_subtree(ti, qatree, ETT_DNS_ANS);
359
360   return cur_off - start_off;
361 }
362
363
364 static int
365 dissect_query_records(int count, const u_char *pd, 
366                       int cur_off, GtkWidget *dns_tree)
367 {
368   int start_off;
369   GtkWidget *qatree, *ti;
370   
371   qatree = gtk_tree_new();
372   start_off = cur_off;
373   
374   while (count-- > 0)
375     cur_off += dissect_dns_query(pd, cur_off, qatree);
376   ti = add_item_to_tree(GTK_WIDGET(dns_tree), 
377                         start_off, cur_off - start_off, "Queries");
378   add_subtree(ti, qatree, ETT_DNS_QRY);
379
380   return cur_off - start_off;
381 }
382
383
384
385 void
386 dissect_dns(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
387   e_dns     *dh;
388   GtkWidget *dns_tree, *ti;
389   guint16    id, flags, quest, ans, auth, add;
390   int query = 0;
391   int cur_off;
392
393   dns_data_ptr = &pd[offset];
394   dh = (e_dns *) dns_data_ptr;
395
396   /* To do: check for runts, errs, etc. */
397   id    = ntohs(dh->dns_id);
398   flags = ntohs(dh->dns_flags);
399   quest = ntohs(dh->dns_quest);
400   ans   = ntohs(dh->dns_ans);
401   auth  = ntohs(dh->dns_auth);
402   add   = ntohs(dh->dns_add);
403   
404   query = ! (flags & (1 << 15));
405   
406   if (fd->win_info[0]) {    
407     strcpy(fd->win_info[3], "DNS (UDP)");
408     strcpy(fd->win_info[4], query ? "Query" : "Response");
409   }
410   
411   if (tree) {
412     ti = add_item_to_tree(GTK_WIDGET(tree), offset, 4,
413                           query ? "DNS query" : "DNS response");
414     
415     dns_tree = gtk_tree_new();
416     add_subtree(ti, dns_tree, ETT_DNS);
417     
418     add_item_to_tree(dns_tree, offset,      2, "ID: 0x%04x", id);
419
420     add_item_to_tree(dns_tree, offset +  2, 2, "Flags: 0x%04x", flags);
421     add_item_to_tree(dns_tree, offset +  4, 2, "Questions: %d", quest);
422     add_item_to_tree(dns_tree, offset +  6, 2, "Answer RRs: %d", ans);
423     add_item_to_tree(dns_tree, offset +  8, 2, "Authority RRs: %d", auth);
424     add_item_to_tree(dns_tree, offset + 10, 2, "Additional RRs: %d", add);
425
426     cur_off = offset + 12;
427     
428     if (quest > 0)
429       cur_off += dissect_query_records(quest, pd, cur_off, dns_tree);
430     
431     if (ans > 0)
432       cur_off += dissect_answer_records(ans, pd, cur_off, dns_tree, "Answers");
433     
434     if (auth > 0)
435       cur_off += dissect_answer_records(auth, pd, cur_off, dns_tree, 
436                                         "Authoritative nameservers");
437
438     if (add > 0)
439       cur_off += dissect_answer_records(add, pd, cur_off, dns_tree, 
440                                         "Additional records");
441   }
442 }