Add code to dissect MX and LOC records ("sparc.com" has both).
[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.21 1999/09/21 07:15:38 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 #ifdef HAVE_SYS_TYPES_H
31 # include <sys/types.h>
32 #endif
33
34 #include <stdio.h>
35 #include <string.h>
36 #include <memory.h>
37
38 #include <glib.h>
39 #include "packet.h"
40 #include "packet-dns.h"
41 #include "util.h"
42
43 static int proto_dns = -1;
44
45 /* DNS structs and definitions */
46
47 /* Offsets of fields in the DNS header. */
48 #define DNS_ID          0
49 #define DNS_FLAGS       2
50 #define DNS_QUEST       4
51 #define DNS_ANS 6
52 #define DNS_AUTH        8
53 #define DNS_ADD 10
54
55 /* Length of DNS header. */
56 #define DNS_HDRLEN      12
57
58 /* type values  */
59 #define T_A             1               /* host address */
60 #define T_NS            2               /* authoritative name server */
61 #define T_MD            3               /* mail destination (obsolete) */
62 #define T_MF            4               /* mail forwarder (obsolete) */
63 #define T_CNAME         5               /* canonical name */
64 #define T_SOA           6               /* start of authority zone */
65 #define T_MB            7               /* mailbox domain name (experimental) */
66 #define T_MG            8               /* mail group member (experimental) */
67 #define T_MR            9               /* mail rename domain name (experimental) */
68 #define T_NULL          10              /* null RR (experimental) */
69 #define T_WKS           11              /* well known service */
70 #define T_PTR           12              /* domain name pointer */
71 #define T_HINFO         13              /* host information */
72 #define T_MINFO         14              /* mailbox or mail list information */
73 #define T_MX            15              /* mail routing information */
74 #define T_TXT           16              /* text strings */
75 #define T_RP            17              /* responsible person (RFC 1183) */
76 #define T_AFSDB         18              /* AFS data base location (RFC 1183) */
77 #define T_X25           19              /* X.25 address (RFC 1183) */
78 #define T_ISDN          20              /* ISDN address (RFC 1183) */
79 #define T_RT            21              /* route-through (RFC 1183) */
80 #define T_NSAP          22              /* OSI NSAP (RFC 1706) */
81 #define T_NSAP_PTR      23              /* PTR equivalent for OSI NSAP (RFC 1348 - obsolete) */
82 #define T_SIG           24              /* digital signature (RFC 2065) */
83 #define T_KEY           25              /* public key (RFC 2065) */
84 #define T_PX            26              /* pointer to X.400/RFC822 mapping info (RFC 1664) */
85 #define T_GPOS          27              /* geographical position (RFC 1712) */
86 #define T_AAAA          28              /* IPv6 address (RFC 1886) */
87 #define T_LOC           29              /* geographical location (RFC 1876) */
88 #define T_NXT           30              /* "next" name (RFC 2065) */
89 #define T_EID           31              /* ??? (Nimrod?) */
90 #define T_NIMLOC        32              /* ??? (Nimrod?) */
91 #define T_SRV           33              /* service location (RFC 2052) */
92 #define T_ATMA          34              /* ??? */
93 #define T_NAPTR         35              /* naming authority pointer (RFC 2168) */
94
95 /* Bit fields in the flags */
96 #define F_RESPONSE      (1<<15)         /* packet is response */
97 #define F_OPCODE        (0xF<<11)       /* query opcode */
98 #define F_AUTHORITATIVE (1<<10)         /* response is authoritative */
99 #define F_TRUNCATED     (1<<9)          /* response is truncated */
100 #define F_RECDESIRED    (1<<8)          /* recursion desired */
101 #define F_RECAVAIL      (1<<7)          /* recursion available */
102 #define F_RCODE         (0xF<<0)        /* reply code */
103
104 /* Opcodes */
105 #define OPCODE_QUERY    (0<<11)         /* standard query */
106 #define OPCODE_IQUERY   (1<<11)         /* inverse query */
107 #define OPCODE_STATUS   (2<<11)         /* server status request */
108
109 /* Reply codes */
110 #define RCODE_NOERROR   (0<<0)
111 #define RCODE_FMTERROR  (1<<0)
112 #define RCODE_SERVFAIL  (2<<0)
113 #define RCODE_NAMEERROR (3<<0)
114 #define RCODE_NOTIMPL   (4<<0)
115 #define RCODE_REFUSED   (5<<0)
116
117 /* See RFC 1035 for all RR types for which no RFC is listed. */
118 static char *
119 dns_type_name (int type)
120 {
121   char *type_names[36] = {
122     "unused",
123     "A",
124     "NS",
125     "MD",
126     "MF",
127     "CNAME",
128     "SOA",
129     "MB",
130     "MG",
131     "MR",
132     "NULL",
133     "WKS",
134     "PTR",
135     "HINFO",
136     "MINFO",
137     "MX",
138     "TXT",
139     "RP",                               /* RFC 1183 */
140     "AFSDB",                            /* RFC 1183 */
141     "X25",                              /* RFC 1183 */
142     "ISDN",                             /* RFC 1183 */
143     "RT",                               /* RFC 1183 */
144     "NSAP",                             /* RFC 1706 */
145     "NSAP-PTR",                         /* RFC 1348 */
146     "SIG",                              /* RFC 2065 */
147     "KEY",                              /* RFC 2065 */
148     "PX",                               /* RFC 1664 */
149     "GPOS",                             /* RFC 1712 */
150     "AAAA",                             /* RFC 1886 */
151     "LOC",                              /* RFC 1876 */
152     "NXT",                              /* RFC 2065 */
153     "EID",
154     "NIMLOC",
155     "SRV",                              /* RFC 2052 */
156     "ATMA",
157     "NAPTR"                             /* RFC 2168 */
158   };
159   
160   if (type <= 35)
161     return type_names[type];
162   
163   /* special cases */
164   switch (type) 
165     {
166       /* non standard  */
167     case 100:
168       return "UINFO";
169     case 101:
170       return "UID";
171     case 102:
172       return "GID";
173     case 103:
174       return "UNSPEC";
175       
176       /* queries  */
177     case 251:
178       return "IXFR";    /* RFC 1995 */
179     case 252:
180       return "AXFR";
181     case 253:
182       return "MAILB";
183     case 254:
184       return "MAILA";
185     case 255:
186       return "ANY";
187     }
188   
189   return "unknown";
190 }
191
192
193 static char *
194 dns_long_type_name (int type)
195 {
196   char *type_names[36] = {
197     "unused",
198     "Host address",
199     "Authoritative name server",        
200     "Mail destination",
201     "Mail forwarder",
202     "Canonical name for an alias",
203     "Start of zone of authority",
204     "Mailbox domain name",
205     "Mail group member",
206     "Mail rename domain name",
207     "Null resource record",
208     "Well-known service description",
209     "Domain name pointer",
210     "Host information",
211     "Mailbox or mail list information",
212     "Mail exchange",
213     "Text strings",
214     "Responsible person",               /* RFC 1183 */
215     "AFS data base location",           /* RFC 1183 */
216     "X.25 address",                     /* RFC 1183 */
217     "ISDN number",                      /* RFC 1183 */
218     "Route through",                    /* RFC 1183 */
219     "OSI NSAP",                         /* RFC 1706 */
220     "OSI NSAP name pointer",            /* RFC 1348 */
221     "Signature",                        /* RFC 2065 */
222     "Public key",                       /* RFC 2065 */
223     "Pointer to X.400/RFC822 mapping info", /* RFC 1664 */
224     "Geographical position",            /* RFC 1712 */
225     "IPv6 address",                     /* RFC 1886 */
226     "Location",                         /* RFC 1876 */
227     "Next",                             /* RFC 2065 */
228     "EID",
229     "NIMLOC",
230     "Service location",                 /* RFC 2052 */
231     "ATMA",
232     "Naming authority pointer"          /* RFC 2168 */
233   };
234   static char unkbuf[7+1+2+1+4+1+1+10+1+1];     /* "Unknown RR type (%d)" */
235   
236   if (type <= 35)
237     return type_names[type];
238   
239   /* special cases */
240   switch (type) 
241     {
242       /* non standard  */
243     case 100:
244       return "UINFO";
245     case 101:
246       return "UID";
247     case 102:
248       return "GID";
249     case 103:
250       return "UNSPEC";
251       
252       /* queries  */
253     case 251:
254       return "Request for incremental zone transfer";   /* RFC 1995 */
255     case 252:
256       return "Request for full zone transfer";
257     case 253:
258       return "Request for mailbox-related records";
259     case 254:
260       return "Request for mail agent resource records";
261     case 255:
262       return "Request for all records";
263     }
264   
265   sprintf(unkbuf, "Unknown RR type (%d)", type);
266   return unkbuf;
267 }
268
269
270 char *
271 dns_class_name(int class)
272 {
273   char *class_name;
274   
275   switch (class) {
276   case 1:
277     class_name = "inet";
278     break;
279   case 3:
280     class_name = "chaos";
281     break;
282   case 4:
283     class_name = "hesiod";
284     break;
285   default:
286     class_name = "unknown";
287   }
288
289   return class_name;
290 }
291
292 int
293 get_dns_name(const u_char *dns_data_ptr, const u_char *dptr, char *name,
294     int maxname)
295 {
296   const u_char *dp = dptr;
297   char *np = name;
298   int len = -1;
299   u_int component_len;
300   int offset;
301
302   maxname--;    /* reserve space for the trailing '\0' */
303   while ((component_len = *dp++) != 0) {
304     switch (component_len & 0xc0) {
305
306     case 0x00:
307       /* Label */
308       if (np != name) {
309         /* Not the first component - put in a '.'. */
310         if (maxname > 0) {
311           *np++ = '.';
312           maxname--;
313         }
314       }
315       while (component_len > 0) {
316         if (maxname > 0) {
317           *np++ = *dp;
318           maxname--;
319         }
320         component_len--;
321         dp++;
322       }
323       break;
324
325     case 0x40:
326     case 0x80:
327       goto error;       /* error */
328
329     case 0xc0:
330       /* Pointer. */
331       /* XXX - check to make sure we aren't looping, by keeping track
332          of how many characters are in the DNS packet, and of how many
333          characters we've looked at, and quitting if the latter
334          becomes bigger than the former. */
335       offset = ((component_len & ~0xc0) << 8) | *dp++;
336       /* If "len" is negative, we are still working on the original name,
337          not something pointed to by a pointer, and so we should set "len"
338          to the length of the original name. */
339       if (len < 0)
340         len = dp - dptr;
341       dp = dns_data_ptr + offset;
342       break;    /* now continue processing from there */
343     }
344   }
345         
346 error:
347   *np = '\0';
348   /* If "len" is negative, we haven't seen a pointer, and thus haven't
349      set the length, so set it. */
350   if (len < 0)
351     len = dp - dptr;
352   /* Zero-length name means "root server" */
353   if (*name == '\0')
354     strcpy(name, "<Root>");
355   return len;
356 }
357
358
359 static int
360 get_dns_name_type_class (const u_char *dns_data_ptr,
361                          const u_char *dptr,
362                          char *name_ret,
363                          int *name_len_ret,
364                          int *type_ret,
365                          int *class_ret)
366 {
367   int len;
368   int name_len;
369   int type;
370   int class;
371   char name[MAXDNAME];
372   const u_char *dptr_save;
373
374   name_len = get_dns_name(dns_data_ptr, dptr, name, sizeof(name));
375   dptr_save = dptr;
376   dptr += name_len;
377   
378   type = pntohs(dptr);
379   dptr += 2;
380   class = pntohs(dptr);
381   dptr += 2;
382
383   strcpy (name_ret, name);
384   *type_ret = type;
385   *class_ret = class;
386   *name_len_ret = name_len;
387
388   len = dptr - dptr_save;
389   return len;
390 }
391
392 static double
393 rfc1867_size(const u_char *dptr)
394 {
395   double size;
396   guint32 exponent;
397
398   size = (*dptr & 0xF0) >> 4;
399   exponent = (*dptr & 0x0F);
400   while (exponent != 0) {
401     size *= 10;
402     exponent--;
403   }
404   return size / 100;    /* return size in meters, not cm */
405 }
406
407 static char *
408 rfc1867_angle(const u_char *dptr, const char *nsew)
409 {
410   guint32 angle;
411   char direction;
412   guint32 degrees, minutes, secs, tsecs;
413   static char buf[10+1+3+1 + 2+1+3+1 + 2+1+3+1+3+1 + 1 + 1];
414
415   angle = pntohl(dptr);
416
417   if (angle < 0x80000000U) {
418     angle = 0x80000000U - angle;
419     direction = nsew[1];
420   } else {
421     angle = angle - 0x80000000U;
422     direction = nsew[0];
423   }
424   tsecs = angle % 1000;
425   angle = angle / 1000;
426   secs = angle % 60;
427   angle = angle / 60;
428   minutes = angle % 60;
429   degrees = angle / 60;
430   sprintf(buf, "%u deg %u min %u.%03u sec %c", degrees, minutes, secs,
431                 tsecs, direction);
432   return buf;
433 }
434
435 static int
436 dissect_dns_query(const u_char *dns_data_ptr, const u_char *pd, int offset,
437   proto_tree *dns_tree)
438 {
439   int len;
440   char name[MAXDNAME];
441   int name_len;
442   int type;
443   int class;
444   char *class_name;
445   char *type_name;
446   char *long_type_name;
447   const u_char *dptr;
448   const u_char *data_start;
449   proto_tree *q_tree;
450   proto_item *tq;
451
452   data_start = dptr = pd + offset;
453
454   len = get_dns_name_type_class(dns_data_ptr, dptr, name, &name_len,
455     &type, &class);
456   dptr += len;
457
458   type_name = dns_type_name(type);
459   class_name = dns_class_name(class);
460   long_type_name = dns_long_type_name(type);
461
462   tq = proto_tree_add_text(dns_tree, offset, len, "%s: type %s, class %s", 
463                    name, type_name, class_name);
464   q_tree = proto_item_add_subtree(tq, ETT_DNS_QD);
465
466   proto_tree_add_text(q_tree, offset, name_len, "Name: %s", name);
467   offset += name_len;
468
469   proto_tree_add_text(q_tree, offset, 2, "Type: %s", long_type_name);
470   offset += 2;
471
472   proto_tree_add_text(q_tree, offset, 2, "Class: %s", class_name);
473   offset += 2;
474   
475   return dptr - data_start;
476 }
477
478
479 proto_tree *
480 add_rr_to_tree(proto_item *trr, int rr_type, int offset, const char *name,
481   int namelen, const char *type_name, const char *class_name, u_int ttl,
482   u_short data_len)
483 {
484   proto_tree *rr_tree;
485
486   rr_tree = proto_item_add_subtree(trr, rr_type);
487   proto_tree_add_text(rr_tree, offset, namelen, "Name: %s", name);
488   offset += namelen;
489   proto_tree_add_text(rr_tree, offset, 2, "Type: %s", type_name);
490   offset += 2;
491   proto_tree_add_text(rr_tree, offset, 2, "Class: %s", class_name);
492   offset += 2;
493   proto_tree_add_text(rr_tree, offset, 4, "Time to live: %s",
494                                                 time_secs_to_str(ttl));
495   offset += 4;
496   proto_tree_add_text(rr_tree, offset, 2, "Data length: %u", data_len);
497   return rr_tree;
498 }
499
500 static int
501 dissect_dns_answer(const u_char *dns_data_ptr, const u_char *pd, int offset,
502   proto_tree *dns_tree)
503 {
504   int len;
505   char name[MAXDNAME];
506   int name_len;
507   int type;
508   int class;
509   char *class_name;
510   char *type_name;
511   char *long_type_name;
512   const u_char *dptr;
513   const u_char *data_start;
514   u_int ttl;
515   u_short data_len;
516   proto_tree *rr_tree;
517   proto_item *trr;
518   const u_char *rrptr;
519
520   data_start = dptr = pd + offset;
521
522   len = get_dns_name_type_class(dns_data_ptr, dptr, name, &name_len,
523     &type, &class);
524   dptr += len;
525
526   type_name = dns_type_name(type);
527   class_name = dns_class_name(class);
528   long_type_name = dns_long_type_name(type);
529
530   ttl = pntohl(dptr);
531   dptr += 4;
532
533   data_len = pntohs(dptr);
534   dptr += 2;
535
536   switch (type) {
537   case T_A:
538     trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
539                      "%s: type %s, class %s, addr %s",
540                      name, type_name, class_name,
541                      ip_to_str((guint8 *)dptr));
542     rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
543                      long_type_name, class_name, ttl, data_len);
544     offset += (dptr - data_start);
545     proto_tree_add_text(rr_tree, offset, 4, "Addr: %s",
546                      ip_to_str((guint8 *)dptr));
547     break;
548
549   case T_NS:
550     {
551       char ns_name[MAXDNAME];
552       int ns_name_len;
553       
554       ns_name_len = get_dns_name(dns_data_ptr, dptr, ns_name, sizeof(ns_name));
555       trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
556                        "%s: type %s, class %s, ns %s",
557                        name, type_name, class_name, ns_name);
558       rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
559                        long_type_name, class_name, ttl, data_len);
560       offset += (dptr - data_start);
561       proto_tree_add_text(rr_tree, offset, ns_name_len, "Name server: %s", ns_name);
562     }
563     break;
564
565   case T_CNAME:
566     {
567       char cname[MAXDNAME];
568       int cname_len;
569       
570       cname_len = get_dns_name(dns_data_ptr, dptr, cname, sizeof(cname));
571       trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
572                      "%s: type %s, class %s, cname %s",
573                      name, type_name, class_name, cname);
574       rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
575                        long_type_name, class_name, ttl, data_len);
576       offset += (dptr - data_start);
577       proto_tree_add_text(rr_tree, offset, cname_len, "Primary name: %s", cname);
578     }
579     break;
580
581   case T_SOA:
582     {
583       char mname[MAXDNAME];
584       int mname_len;
585       char rname[MAXDNAME];
586       int rname_len;
587       guint32 serial;
588       guint32 refresh;
589       guint32 retry;
590       guint32 expire;
591       guint32 minimum;
592
593       rrptr = dptr;
594       mname_len = get_dns_name(dns_data_ptr, rrptr, mname, sizeof(mname));
595       rrptr += mname_len;
596       trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
597                      "%s: type %s, class %s, mname %s",
598                      name, type_name, class_name, mname);
599       rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
600                        long_type_name, class_name, ttl, data_len);
601       offset += (dptr - data_start);
602       proto_tree_add_text(rr_tree, offset, mname_len, "Primary name server: %s",
603                        mname);
604       offset += mname_len;
605       rname_len = get_dns_name(dns_data_ptr, rrptr, rname, sizeof(rname));
606       proto_tree_add_text(rr_tree, offset, rname_len, "Responsible authority's mailbox: %s",
607                        rname);
608       rrptr += rname_len;
609       offset += rname_len;
610       serial = pntohl(rrptr);
611       proto_tree_add_text(rr_tree, offset, 4, "Serial number: %u",
612                        serial);
613       rrptr += 4;
614       offset += 4;
615       refresh = pntohl(rrptr);
616       proto_tree_add_text(rr_tree, offset, 4, "Refresh interval: %s",
617                        time_secs_to_str(refresh));
618       rrptr += 4;
619       offset += 4;
620       retry = pntohl(rrptr);
621       proto_tree_add_text(rr_tree, offset, 4, "Retry interval: %s",
622                        time_secs_to_str(retry));
623       rrptr += 4;
624       offset += 4;
625       expire = pntohl(rrptr);
626       proto_tree_add_text(rr_tree, offset, 4, "Expiration limit: %s",
627                        time_secs_to_str(expire));
628       rrptr += 4;
629       offset += 4;
630       minimum = pntohl(rrptr);
631       proto_tree_add_text(rr_tree, offset, 4, "Minimum TTL: %s",
632                        time_secs_to_str(minimum));
633     }
634     break;
635
636   case T_PTR:
637     {
638       char pname[MAXDNAME];
639       int pname_len;
640       
641       pname_len = get_dns_name(dns_data_ptr, dptr, pname, sizeof(pname));
642       trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
643                      "%s: type %s, class %s, ptr %s",
644                      name, type_name, class_name, pname);
645       rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
646                        long_type_name, class_name, ttl, data_len);
647       offset += (dptr - data_start);
648       proto_tree_add_text(rr_tree, offset, pname_len, "Domain name: %s", pname);
649       break;
650     }
651     break;
652
653   case T_MX:
654     {
655       guint16 preference;
656       char mx_name[MAXDNAME];
657       int mx_name_len;
658       
659       rrptr = dptr;
660       preference = pntohs(rrptr);
661       rrptr += 2;
662       mx_name_len = get_dns_name(dns_data_ptr, rrptr, mx_name, sizeof(mx_name));
663       trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
664                        "%s: type %s, class %s, preference %u, mx %s",
665                        name, type_name, class_name, preference, mx_name);
666       rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
667                        long_type_name, class_name, ttl, data_len);
668       offset += (dptr - data_start);
669       proto_tree_add_text(rr_tree, offset, 2, "Preference: %u", preference);
670       offset += 2;
671       proto_tree_add_text(rr_tree, offset, mx_name_len, "Mail exchange: %s", mx_name);
672     }
673     break;
674       
675   case T_LOC:
676     {
677       rrptr = dptr;
678       trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
679                      "%s: type %s, class %s",
680                      name, type_name, class_name);
681       rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
682                        long_type_name, class_name, ttl, data_len);
683       offset += (dptr - data_start);
684       proto_tree_add_text(rr_tree, offset, 1, "Version: %u", *dptr);
685       if (*rrptr == 0) {
686         /* Version 0, the only version RFC 1876 discusses. */
687         rrptr++;
688         offset++;
689         proto_tree_add_text(rr_tree, offset, 1, "Size: %g m",
690                                 rfc1867_size(rrptr));
691         rrptr++;
692         offset++;
693         proto_tree_add_text(rr_tree, offset, 1, "Horizontal precision: %g m",
694                                 rfc1867_size(rrptr));
695         rrptr++;
696         offset++;
697         proto_tree_add_text(rr_tree, offset, 1, "Vertical precision: %g m",
698                                 rfc1867_size(rrptr));
699         rrptr++;
700         offset++;
701         proto_tree_add_text(rr_tree, offset, 4, "Latitude: %s",
702                                 rfc1867_angle(rrptr, "NS"));
703         rrptr += 4;
704         offset += 4;
705         proto_tree_add_text(rr_tree, offset, 4, "Longitude: %s",
706                                 rfc1867_angle(rrptr, "EW"));
707         rrptr += 4;
708         offset += 4;
709         proto_tree_add_text(rr_tree, offset, 4, "Altitude: %g m",
710                                 (pntohl(rrptr) - 10000000)/100.0);
711       } else
712         proto_tree_add_text(rr_tree, offset, data_len, "Data");
713       break;
714     }
715     break;
716       
717     /* TODO: parse more record types */
718
719   default:
720     trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
721                      "%s: type %s, class %s",
722                      name, type_name, class_name);
723     rr_tree = add_rr_to_tree(trr, ETT_DNS_RR, offset, name, name_len,
724                        long_type_name, class_name, ttl, data_len);
725     offset += (dptr - data_start);
726     proto_tree_add_text(rr_tree, offset, data_len, "Data");
727   }
728   
729   dptr += data_len;
730         
731   return dptr - data_start;
732 }
733
734 static int
735 dissect_query_records(const u_char *dns_data_ptr, int count, const u_char *pd, 
736                       int cur_off, proto_tree *dns_tree)
737 {
738   int start_off;
739   proto_tree *qatree;
740   proto_item *ti;
741   
742   start_off = cur_off;
743   ti = proto_tree_add_text(dns_tree, start_off, 0, "Queries");
744   qatree = proto_item_add_subtree(ti, ETT_DNS_QRY);
745   while (count-- > 0)
746     cur_off += dissect_dns_query(dns_data_ptr, pd, cur_off, qatree);
747   proto_item_set_len(ti, cur_off - start_off);
748
749   return cur_off - start_off;
750 }
751
752
753
754 static int
755 dissect_answer_records(const u_char *dns_data_ptr, int count,
756                        const u_char *pd, int cur_off, proto_tree *dns_tree,
757                        char *name)
758 {
759   int start_off;
760   proto_tree *qatree;
761   proto_item *ti;
762   
763   start_off = cur_off;
764   ti = proto_tree_add_text(dns_tree, start_off, 0, name);
765   qatree = proto_item_add_subtree(ti, ETT_DNS_ANS);
766   while (count-- > 0)
767     cur_off += dissect_dns_answer(dns_data_ptr, pd, cur_off, qatree);
768   proto_item_set_len(ti, cur_off - start_off);
769
770   return cur_off - start_off;
771 }
772
773
774 void
775 dissect_dns(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
776   const u_char *dns_data_ptr;
777   proto_tree *dns_tree, *field_tree;
778   proto_item *ti, *tf;
779   guint16    id, flags, quest, ans, auth, add;
780   char buf[128+1];
781   int cur_off;
782   static const value_string opcode_vals[] = {
783                   { OPCODE_QUERY,  "Standard query"        },
784                   { OPCODE_IQUERY, "Inverse query"         },
785                   { OPCODE_STATUS, "Server status request" },
786                   { 0,              NULL                   } };
787   static const value_string rcode_vals[] = {
788                   { RCODE_NOERROR,   "No error"        },
789                   { RCODE_FMTERROR,  "Format error"    },
790                   { RCODE_SERVFAIL,  "Server failure"  },
791                   { RCODE_NAMEERROR, "Name error"      },
792                   { RCODE_NOTIMPL,   "Not implemented" },
793                   { RCODE_REFUSED,   "Refused"         },
794                   { 0,               NULL              } };
795
796   dns_data_ptr = &pd[offset];
797
798   /* To do: check for runts, errs, etc. */
799   id    = pntohs(&pd[offset + DNS_ID]);
800   flags = pntohs(&pd[offset + DNS_FLAGS]);
801   quest = pntohs(&pd[offset + DNS_QUEST]);
802   ans   = pntohs(&pd[offset + DNS_ANS]);
803   auth  = pntohs(&pd[offset + DNS_AUTH]);
804   add   = pntohs(&pd[offset + DNS_ADD]);
805   
806   if (check_col(fd, COL_PROTOCOL))
807     col_add_str(fd, COL_PROTOCOL, "DNS (UDP)");
808   if (check_col(fd, COL_INFO)) {
809     col_add_fstr(fd, COL_INFO, "%s%s",
810                 val_to_str(flags & F_OPCODE, opcode_vals,
811                            "Unknown operation (%x)"),
812                 (flags & F_RESPONSE) ? " response" : "");
813   }
814   
815   if (tree) {
816     ti = proto_tree_add_item_format(tree, proto_dns, offset, 4, NULL,
817                           (flags & F_RESPONSE) ? "DNS response" : "DNS query");
818     
819     dns_tree = proto_item_add_subtree(ti, ETT_DNS);
820     
821     proto_tree_add_text(dns_tree, offset + DNS_ID, 2, "Transaction ID: 0x%04x",
822                         id);
823
824     strcpy(buf, val_to_str(flags & F_OPCODE, opcode_vals, "Unknown operation"));
825     if (flags & F_RESPONSE) {
826       strcat(buf, " response");
827       strcat(buf, ", ");
828       strcat(buf, val_to_str(flags & F_RCODE, rcode_vals,
829             "Unknown error"));
830     }
831     tf = proto_tree_add_text(dns_tree, offset + DNS_FLAGS, 2, "Flags: 0x%04x (%s)",
832                           flags, buf);
833     field_tree = proto_item_add_subtree(tf, ETT_DNS_FLAGS);
834     proto_tree_add_text(field_tree, offset + DNS_FLAGS, 2, "%s",
835        decode_boolean_bitfield(flags, F_RESPONSE,
836             2*8, "Response", "Query"));
837     proto_tree_add_text(field_tree, offset + DNS_FLAGS, 2, "%s",
838        decode_enumerated_bitfield(flags, F_OPCODE,
839             2*8, opcode_vals, "%s"));
840     if (flags & F_RESPONSE) {
841       proto_tree_add_text(field_tree, offset + DNS_FLAGS, 2, "%s",
842          decode_boolean_bitfield(flags, F_AUTHORITATIVE,
843               2*8,
844               "Server is an authority for domain",
845               "Server isn't an authority for domain"));
846     }
847     proto_tree_add_text(field_tree, offset + DNS_FLAGS, 2, "%s",
848        decode_boolean_bitfield(flags, F_TRUNCATED,
849             2*8,
850             "Message is truncated",
851             "Message is not truncated"));
852     proto_tree_add_text(field_tree, offset + DNS_FLAGS, 2, "%s",
853        decode_boolean_bitfield(flags, F_RECDESIRED,
854             2*8,
855             "Do query recursively",
856             "Don't do query recursively"));
857     if (flags & F_RESPONSE) {
858       proto_tree_add_text(field_tree, offset + DNS_FLAGS, 2, "%s",
859          decode_boolean_bitfield(flags, F_RECAVAIL,
860               2*8,
861               "Server can do recursive queries",
862               "Server can't do recursive queries"));
863       proto_tree_add_text(field_tree, offset + DNS_FLAGS, 2, "%s",
864          decode_enumerated_bitfield(flags, F_RCODE,
865               2*8, rcode_vals, "%s"));
866     }
867     proto_tree_add_text(dns_tree, offset + DNS_QUEST, 2, "Questions: %d", quest);
868     proto_tree_add_text(dns_tree, offset + DNS_ANS, 2, "Answer RRs: %d", ans);
869     proto_tree_add_text(dns_tree, offset + DNS_AUTH, 2, "Authority RRs: %d", auth);
870     proto_tree_add_text(dns_tree, offset + DNS_ADD, 2, "Additional RRs: %d", add);
871
872     cur_off = offset + DNS_HDRLEN;
873     
874     if (quest > 0)
875       cur_off += dissect_query_records(dns_data_ptr, quest, pd, cur_off,
876                                         dns_tree);
877     
878     if (ans > 0)
879       cur_off += dissect_answer_records(dns_data_ptr, ans, pd, cur_off,
880           dns_tree, "Answers");
881     
882     if (auth > 0)
883       cur_off += dissect_answer_records(dns_data_ptr, auth, pd, cur_off,
884           dns_tree, "Authoritative nameservers");
885
886     if (add > 0)
887       cur_off += dissect_answer_records(dns_data_ptr, add, pd, cur_off,
888           dns_tree, "Additional records");
889   }
890 }
891
892 void
893 proto_register_dns(void)
894 {
895 /*        static hf_register_info hf[] = {
896                 { &variable,
897                 { "Name",           "dns.abbreviation", TYPE, VALS_POINTER }},
898         };*/
899
900         proto_dns = proto_register_protocol("Domain Name Service", "dns");
901  /*       proto_register_field_array(proto_dns, hf, array_length(hf));*/
902 }