Support for AS-external LSAs added.
[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.4 1998/09/27 22:12:28 gerald 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
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 #define MAXDNAME        1025            /* maximum domain name */
60
61 /* type values  */
62 #define T_A             1               /* host address */
63 #define T_NS            2               /* authoritative server */
64 #define T_CNAME         5               /* canonical name */
65 #define T_SOA           6               /* start of authority zone */
66 #define T_WKS           11              /* well known service */
67 #define T_PTR           12              /* domain name pointer */
68 #define T_HINFO         13              /* host information */
69 #define T_MX            15              /* mail routing information */
70 #define T_TXT           16              /* text strings */
71 #define T_AAAA          28              /* IP6 Address */
72
73
74 static const u_char *dns_data_ptr;
75
76 static char *
77 dns_type_name (int type)
78 {
79   char *type_names[36] = {
80     "unused", "A", "NS", "MD", "MF", "CNAME", "SOA", "MB", "MG", "MR",
81     "NULL", "WKS", "PTR", "HINFO", "MINFO", "MX", "TXT", "RP", "AFSDB",
82     "X25", "ISDN", "RT", "NSAP", "NSAP_PTR", "SIG", "KEY", "PX", "GPOS",
83     "AAAA", "LOC", "NXT", "EID", "NIMLOC", "SRV", "ATMA", "NAPTR"
84   };
85   
86   if (type <= 35)
87     return type_names[type];
88   
89   /* special cases */
90   switch (type) 
91     {
92       /* non standard  */
93     case 100:
94       return "UINFO";
95     case 101:
96       return "UID";
97     case 102:
98       return "GID";
99     case 103:
100       return "UNSPEC";
101       
102       /* queries  */
103     case 251:
104       return "IXFR";
105     case 252:
106       return "AXFR";
107     case 253:
108       return "MAILB";
109     case 254:
110       return "MAILA";
111     case 255:
112       return "ANY";
113     }
114   
115   return "unknown";
116 }
117
118
119 static char *
120 dns_class_name(int class)
121 {
122   char *class_name;
123   
124   switch (class) {
125   case 1:
126     class_name = "inet";
127     break;
128   case 3:
129     class_name = "chaos";
130     break;
131   case 4:
132     class_name = "hesiod";
133     break;
134   default:
135     class_name = "unknown";
136   }
137
138   return class_name;
139 }
140   
141
142 static int
143 is_compressed_name(const u_char *foo)
144 {
145   return (0xc0 == (*foo & 0xc0));
146 }
147
148
149 static int
150 get_compressed_name_offset(const u_char *ptr)
151 {
152   return ((*ptr & ~0xc0) << 8) | *(ptr+1);
153 }
154
155
156 static int
157 copy_one_name_component(const u_char *dataptr, char *nameptr)
158 {
159   int len;
160   int n;
161   
162   len = n  = *dataptr++;
163   if (0 == len)
164     return 0;
165   
166   while (n-- > 0)
167     *nameptr++ = *dataptr++;
168
169   return len;
170 }
171
172
173 static int
174 copy_name_component_rec(const u_char *dataptr, char *nameptr, int *real_string_len)
175 {
176   int len = 0;
177   int str_len;
178   int offset;
179   int compress = 0;
180   
181   if (is_compressed_name(dataptr)) {
182     compress = 1;
183     offset = get_compressed_name_offset(dataptr);
184     dataptr = dns_data_ptr + offset;
185     copy_name_component_rec(dataptr, nameptr, &str_len);
186     *real_string_len += str_len;
187     nameptr += str_len;
188     len = 2;
189   }
190   else {
191     str_len = copy_one_name_component(dataptr, nameptr);
192     *real_string_len = str_len;
193     dataptr += str_len + 1;
194     len     += str_len + 1;
195     nameptr += str_len;
196   }
197
198   if (compress)
199     return len;
200   
201   (*real_string_len)++;
202
203   if (*dataptr > 0) {
204     *nameptr++ = '.';
205     len += copy_name_component_rec(dataptr, nameptr, &str_len);
206     *real_string_len += str_len;
207     return len;
208   }
209
210   return len + 1;
211 }
212
213
214 static int
215 get_dns_name(const u_char *pd, int offset, char *nameptr, int maxname)
216 {
217   int len;
218   const u_char *dataptr = pd + offset;
219   int str_len = 0;
220
221   memset (nameptr, 0, maxname);
222   len = copy_name_component_rec(dataptr, nameptr, &str_len);
223   
224   return len;
225 }
226
227
228 static int
229 get_dns_name_type_class (const u_char *pd,
230                          int offset,
231                          char *name_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(pd, offset, name, sizeof(name));
243   pd += offset;
244   pd_save = pd;
245   pd += name_len;
246   
247   type = (*pd << 8) | *(pd + 1);
248   pd += 2;
249   class = (*pd << 8) | *(pd + 1);
250   pd += 2;
251
252   strcpy (name_ret, name);
253   *type_ret = type;
254   *class_ret = class;
255
256   len = pd - pd_save;
257   return len;
258 }
259
260
261 static int
262 dissect_dns_query(const u_char *pd, int offset, GtkWidget *dns_tree)
263 {
264   int len;
265   char name[MAXDNAME];
266   int type;
267   int class;
268   char *class_name;
269   char *type_name;
270
271   len = get_dns_name_type_class (pd, offset, name, &type, &class);
272
273   type_name = dns_type_name(type);
274   class_name = dns_class_name(class);
275   
276   add_item_to_tree(dns_tree, offset, len, "%s: type %s, class %s", 
277                    name, type_name, class_name );
278   
279   return len;
280 }
281
282
283 static int
284 dissect_dns_answer(const u_char *pd, int offset, GtkWidget *dns_tree)
285 {
286   int len;
287   char name[MAXDNAME];
288   int type;
289   int class;
290   char *class_name;
291   char *type_name;
292   const u_char *dptr;
293   const u_char *data_start;
294   const u_char *res_ptr;
295   u_int ttl;
296   u_short data_len;
297
298   data_start = dptr = pd + offset;
299
300   len = get_dns_name_type_class (pd, offset, name, &type, &class);
301   dptr += len;
302
303   /* this works regardless of the alignment  */
304   ttl = (*dptr << 24) | *(dptr + 1) << 16 | *(dptr + 2) << 8 | *(dptr + 3);
305   dptr += 4;
306   data_len = (*dptr << 8) | *(dptr + 1);
307   dptr += 2;
308
309   type_name = dns_type_name(type);
310   class_name = dns_class_name(class);
311   res_ptr = dptr;
312
313   /* skip the resource data  */
314   dptr += data_len;
315   
316   len = dptr - data_start;
317   
318   switch (type) {
319   case T_A:             /* "A" record */
320     add_item_to_tree(dns_tree, offset, len, 
321                      "%s: type %s, class %s, addr %d.%d.%d.%d",
322                      name, type_name, class_name,
323                      *res_ptr, *(res_ptr+1), *(res_ptr+2), *(res_ptr+3));
324     break;
325
326   case T_NS:            /* "NS" record */
327     {
328       char ns_name[MAXDNAME];
329       
330       get_dns_name(res_ptr, 0, ns_name, sizeof(ns_name));
331       add_item_to_tree(dns_tree, offset, len, 
332                        "%s: %s, type %s, class %s",
333                        name, ns_name, type_name, class_name);
334       
335     }
336     break;
337
338     /* TODO: parse more record types */
339       
340     default:
341     add_item_to_tree(dns_tree, offset, len, "%s: type %s, class %s", 
342                      name, type_name, class_name);
343   }
344   
345   return len;
346 }
347
348
349 static int
350 dissect_answer_records(int count, const u_char *pd, int cur_off, 
351                        GtkWidget *dns_tree, char *name)
352 {
353   int start_off;
354   GtkWidget *qatree, *ti;
355   
356   qatree = gtk_tree_new();
357   start_off = cur_off;
358
359   while (count-- > 0)
360     cur_off += dissect_dns_answer(pd, cur_off, qatree);
361   ti = add_item_to_tree(GTK_WIDGET(dns_tree), start_off, cur_off - start_off, name);
362   add_subtree(ti, qatree, ETT_DNS_ANS);
363
364   return cur_off - start_off;
365 }
366
367
368 static int
369 dissect_query_records(int count, const u_char *pd, 
370                       int cur_off, GtkWidget *dns_tree)
371 {
372   int start_off;
373   GtkWidget *qatree, *ti;
374   
375   qatree = gtk_tree_new();
376   start_off = cur_off;
377   
378   while (count-- > 0)
379     cur_off += dissect_dns_query(pd, cur_off, qatree);
380   ti = add_item_to_tree(GTK_WIDGET(dns_tree), 
381                         start_off, cur_off - start_off, "Queries");
382   add_subtree(ti, qatree, ETT_DNS_QRY);
383
384   return cur_off - start_off;
385 }
386
387
388
389 void
390 dissect_dns(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
391   e_dns     *dh;
392   GtkWidget *dns_tree, *ti;
393   guint16    id, flags, quest, ans, auth, add;
394   int query = 0;
395   int cur_off;
396
397   dns_data_ptr = &pd[offset];
398   dh = (e_dns *) dns_data_ptr;
399
400   /* To do: check for runts, errs, etc. */
401   id    = ntohs(dh->dns_id);
402   flags = ntohs(dh->dns_flags);
403   quest = ntohs(dh->dns_quest);
404   ans   = ntohs(dh->dns_ans);
405   auth  = ntohs(dh->dns_auth);
406   add   = ntohs(dh->dns_add);
407   
408   query = ! (flags & (1 << 15));
409   
410   if (fd->win_info[COL_NUM]) {    
411     strcpy(fd->win_info[COL_PROTOCOL], "DNS (UDP)");
412     strcpy(fd->win_info[COL_INFO], query ? "Query" : "Response");
413   }
414   
415   if (tree) {
416     ti = add_item_to_tree(GTK_WIDGET(tree), offset, 4,
417                           query ? "DNS query" : "DNS response");
418     
419     dns_tree = gtk_tree_new();
420     add_subtree(ti, dns_tree, ETT_DNS);
421     
422     add_item_to_tree(dns_tree, offset,      2, "ID: 0x%04x", id);
423
424     add_item_to_tree(dns_tree, offset +  2, 2, "Flags: 0x%04x", flags);
425     add_item_to_tree(dns_tree, offset +  4, 2, "Questions: %d", quest);
426     add_item_to_tree(dns_tree, offset +  6, 2, "Answer RRs: %d", ans);
427     add_item_to_tree(dns_tree, offset +  8, 2, "Authority RRs: %d", auth);
428     add_item_to_tree(dns_tree, offset + 10, 2, "Additional RRs: %d", add);
429
430     cur_off = offset + 12;
431     
432     if (quest > 0)
433       cur_off += dissect_query_records(quest, pd, cur_off, dns_tree);
434     
435     if (ans > 0)
436       cur_off += dissect_answer_records(ans, pd, cur_off, dns_tree, "Answers");
437     
438     if (auth > 0)
439       cur_off += dissect_answer_records(auth, pd, cur_off, dns_tree, 
440                                         "Authoritative nameservers");
441
442     if (add > 0)
443       cur_off += dissect_answer_records(add, pd, cur_off, dns_tree, 
444                                         "Additional records");
445   }
446 }