Dissect AAAA resource records.
[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.30 1999/11/27 07:46:44 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 static int hf_dns_response = -1;
45 static int hf_dns_query = -1;
46 static int hf_dns_flags = -1;
47 static int hf_dns_transaction_id = -1;
48 static int hf_dns_count_questions = -1;
49 static int hf_dns_count_answers = -1;
50 static int hf_dns_count_auth_rr = -1;
51 static int hf_dns_count_add_rr = -1;
52
53 static gint ett_dns = -1;
54 static gint ett_dns_qd = -1;
55 static gint ett_dns_rr = -1;
56 static gint ett_dns_qry = -1;
57 static gint ett_dns_ans = -1;
58 static gint ett_dns_flags = -1;
59
60 /* DNS structs and definitions */
61
62 /* Offsets of fields in the DNS header. */
63 #define DNS_ID          0
64 #define DNS_FLAGS       2
65 #define DNS_QUEST       4
66 #define DNS_ANS         6
67 #define DNS_AUTH        8
68 #define DNS_ADD         10
69
70 /* Length of DNS header. */
71 #define DNS_HDRLEN      12
72
73 /* type values  */
74 #define T_A             1               /* host address */
75 #define T_NS            2               /* authoritative name server */
76 #define T_MD            3               /* mail destination (obsolete) */
77 #define T_MF            4               /* mail forwarder (obsolete) */
78 #define T_CNAME         5               /* canonical name */
79 #define T_SOA           6               /* start of authority zone */
80 #define T_MB            7               /* mailbox domain name (experimental) */
81 #define T_MG            8               /* mail group member (experimental) */
82 #define T_MR            9               /* mail rename domain name (experimental) */
83 #define T_NULL          10              /* null RR (experimental) */
84 #define T_WKS           11              /* well known service */
85 #define T_PTR           12              /* domain name pointer */
86 #define T_HINFO         13              /* host information */
87 #define T_MINFO         14              /* mailbox or mail list information */
88 #define T_MX            15              /* mail routing information */
89 #define T_TXT           16              /* text strings */
90 #define T_RP            17              /* responsible person (RFC 1183) */
91 #define T_AFSDB         18              /* AFS data base location (RFC 1183) */
92 #define T_X25           19              /* X.25 address (RFC 1183) */
93 #define T_ISDN          20              /* ISDN address (RFC 1183) */
94 #define T_RT            21              /* route-through (RFC 1183) */
95 #define T_NSAP          22              /* OSI NSAP (RFC 1706) */
96 #define T_NSAP_PTR      23              /* PTR equivalent for OSI NSAP (RFC 1348 - obsolete) */
97 #define T_SIG           24              /* digital signature (RFC 2065) */
98 #define T_KEY           25              /* public key (RFC 2065) */
99 #define T_PX            26              /* pointer to X.400/RFC822 mapping info (RFC 1664) */
100 #define T_GPOS          27              /* geographical position (RFC 1712) */
101 #define T_AAAA          28              /* IPv6 address (RFC 1886) */
102 #define T_LOC           29              /* geographical location (RFC 1876) */
103 #define T_NXT           30              /* "next" name (RFC 2065) */
104 #define T_EID           31              /* ??? (Nimrod?) */
105 #define T_NIMLOC        32              /* ??? (Nimrod?) */
106 #define T_SRV           33              /* service location (RFC 2052) */
107 #define T_ATMA          34              /* ??? */
108 #define T_NAPTR         35              /* naming authority pointer (RFC 2168) */
109
110 /* Bit fields in the flags */
111 #define F_RESPONSE      (1<<15)         /* packet is response */
112 #define F_OPCODE        (0xF<<11)       /* query opcode */
113 #define F_AUTHORITATIVE (1<<10)         /* response is authoritative */
114 #define F_TRUNCATED     (1<<9)          /* response is truncated */
115 #define F_RECDESIRED    (1<<8)          /* recursion desired */
116 #define F_RECAVAIL      (1<<7)          /* recursion available */
117 #define F_RCODE         (0xF<<0)        /* reply code */
118
119 /* Opcodes */
120 #define OPCODE_QUERY    (0<<11)         /* standard query */
121 #define OPCODE_IQUERY   (1<<11)         /* inverse query */
122 #define OPCODE_STATUS   (2<<11)         /* server status request */
123
124 /* Reply codes */
125 #define RCODE_NOERROR   (0<<0)
126 #define RCODE_FMTERROR  (1<<0)
127 #define RCODE_SERVFAIL  (2<<0)
128 #define RCODE_NAMEERROR (3<<0)
129 #define RCODE_NOTIMPL   (4<<0)
130 #define RCODE_REFUSED   (5<<0)
131
132 /* See RFC 1035 for all RR types for which no RFC is listed. */
133 static char *
134 dns_type_name (int type)
135 {
136   char *type_names[36] = {
137     "unused",
138     "A",
139     "NS",
140     "MD",
141     "MF",
142     "CNAME",
143     "SOA",
144     "MB",
145     "MG",
146     "MR",
147     "NULL",
148     "WKS",
149     "PTR",
150     "HINFO",
151     "MINFO",
152     "MX",
153     "TXT",
154     "RP",                               /* RFC 1183 */
155     "AFSDB",                            /* RFC 1183 */
156     "X25",                              /* RFC 1183 */
157     "ISDN",                             /* RFC 1183 */
158     "RT",                               /* RFC 1183 */
159     "NSAP",                             /* RFC 1706 */
160     "NSAP-PTR",                         /* RFC 1348 */
161     "SIG",                              /* RFC 2065 */
162     "KEY",                              /* RFC 2065 */
163     "PX",                               /* RFC 1664 */
164     "GPOS",                             /* RFC 1712 */
165     "AAAA",                             /* RFC 1886 */
166     "LOC",                              /* RFC 1876 */
167     "NXT",                              /* RFC 2065 */
168     "EID",
169     "NIMLOC",
170     "SRV",                              /* RFC 2052 */
171     "ATMA",
172     "NAPTR"                             /* RFC 2168 */
173   };
174   
175   if (type <= 35)
176     return type_names[type];
177   
178   /* special cases */
179   switch (type) 
180     {
181       /* non standard  */
182     case 100:
183       return "UINFO";
184     case 101:
185       return "UID";
186     case 102:
187       return "GID";
188     case 103:
189       return "UNSPEC";
190       
191       /* queries  */
192     case 251:
193       return "IXFR";    /* RFC 1995 */
194     case 252:
195       return "AXFR";
196     case 253:
197       return "MAILB";
198     case 254:
199       return "MAILA";
200     case 255:
201       return "ANY";
202     }
203   
204   return "unknown";
205 }
206
207
208 static char *
209 dns_long_type_name (int type)
210 {
211   char *type_names[36] = {
212     "unused",
213     "Host address",
214     "Authoritative name server",        
215     "Mail destination",
216     "Mail forwarder",
217     "Canonical name for an alias",
218     "Start of zone of authority",
219     "Mailbox domain name",
220     "Mail group member",
221     "Mail rename domain name",
222     "Null resource record",
223     "Well-known service description",
224     "Domain name pointer",
225     "Host information",
226     "Mailbox or mail list information",
227     "Mail exchange",
228     "Text strings",
229     "Responsible person",               /* RFC 1183 */
230     "AFS data base location",           /* RFC 1183 */
231     "X.25 address",                     /* RFC 1183 */
232     "ISDN number",                      /* RFC 1183 */
233     "Route through",                    /* RFC 1183 */
234     "OSI NSAP",                         /* RFC 1706 */
235     "OSI NSAP name pointer",            /* RFC 1348 */
236     "Signature",                        /* RFC 2065 */
237     "Public key",                       /* RFC 2065 */
238     "Pointer to X.400/RFC822 mapping info", /* RFC 1664 */
239     "Geographical position",            /* RFC 1712 */
240     "IPv6 address",                     /* RFC 1886 */
241     "Location",                         /* RFC 1876 */
242     "Next",                             /* RFC 2065 */
243     "EID",
244     "NIMLOC",
245     "Service location",                 /* RFC 2052 */
246     "ATMA",
247     "Naming authority pointer"          /* RFC 2168 */
248   };
249   static char unkbuf[7+1+2+1+4+1+1+10+1+1];     /* "Unknown RR type (%d)" */
250   
251   if (type <= 35)
252     return type_names[type];
253   
254   /* special cases */
255   switch (type) 
256     {
257       /* non standard  */
258     case 100:
259       return "UINFO";
260     case 101:
261       return "UID";
262     case 102:
263       return "GID";
264     case 103:
265       return "UNSPEC";
266       
267       /* queries  */
268     case 251:
269       return "Request for incremental zone transfer";   /* RFC 1995 */
270     case 252:
271       return "Request for full zone transfer";
272     case 253:
273       return "Request for mailbox-related records";
274     case 254:
275       return "Request for mail agent resource records";
276     case 255:
277       return "Request for all records";
278     }
279   
280   sprintf(unkbuf, "Unknown RR type (%d)", type);
281   return unkbuf;
282 }
283
284
285 char *
286 dns_class_name(int class)
287 {
288   char *class_name;
289   
290   switch (class) {
291   case 1:
292     class_name = "inet";
293     break;
294   case 3:
295     class_name = "chaos";
296     break;
297   case 4:
298     class_name = "hesiod";
299     break;
300   default:
301     class_name = "unknown";
302   }
303
304   return class_name;
305 }
306
307 int
308 get_dns_name(const u_char *pd, int offset, int dns_data_offset,
309     char *name, int maxname)
310 {
311   const u_char *dp = pd + offset;
312   const u_char *dptr = dp;
313   char *np = name;
314   int len = -1;
315   u_int component_len;
316
317   maxname--;    /* reserve space for the trailing '\0' */
318   for (;;) {
319     if (!BYTES_ARE_IN_FRAME(offset, 1))
320       goto overflow;
321     component_len = *dp++;
322     offset++;
323     if (component_len == 0)
324       break;
325     switch (component_len & 0xc0) {
326
327     case 0x00:
328       /* Label */
329       if (np != name) {
330         /* Not the first component - put in a '.'. */
331         if (maxname > 0) {
332           *np++ = '.';
333           maxname--;
334         }
335       }
336       if (!BYTES_ARE_IN_FRAME(offset, component_len))
337         goto overflow;
338       while (component_len > 0) {
339         if (maxname > 0) {
340           *np++ = *dp;
341           maxname--;
342         }
343         component_len--;
344         dp++;
345         offset++;
346       }
347       break;
348
349     case 0x40:
350     case 0x80:
351       goto error;       /* error */
352
353     case 0xc0:
354       /* Pointer. */
355       /* XXX - check to make sure we aren't looping, by keeping track
356          of how many characters are in the DNS packet, and of how many
357          characters we've looked at, and quitting if the latter
358          becomes bigger than the former. */
359       if (!BYTES_ARE_IN_FRAME(offset, 1))
360         goto overflow;
361       offset = dns_data_offset + (((component_len & ~0xc0) << 8) | (*dp++));
362       /* If "len" is negative, we are still working on the original name,
363          not something pointed to by a pointer, and so we should set "len"
364          to the length of the original name. */
365       if (len < 0)
366         len = dp - dptr;
367       dp = pd + offset;
368       break;    /* now continue processing from there */
369     }
370   }
371         
372 error:
373   *np = '\0';
374   /* If "len" is negative, we haven't seen a pointer, and thus haven't
375      set the length, so set it. */
376   if (len < 0)
377     len = dp - dptr;
378   /* Zero-length name means "root server" */
379   if (*name == '\0')
380     strcpy(name, "<Root>");
381   return len;
382
383 overflow:
384   /* We ran past the end of the captured data in the packet. */
385   strcpy(name, "<Name goes past end of captured data in packet>");
386   /* If "len" is negative, we haven't seen a pointer, and thus haven't
387      set the length, so set it. */
388   if (len < 0)
389     len = dp - dptr;
390   return len;
391 }
392
393
394 static int
395 get_dns_name_type_class(const u_char *pd, int offset, int dns_data_offset,
396     char *name_ret, int *name_len_ret, int *type_ret, int *class_ret)
397 {
398   int len;
399   int name_len;
400   int type;
401   int class;
402   char name[MAXDNAME];
403   int start_offset = offset;
404
405   name_len = get_dns_name(pd, offset, dns_data_offset, name, sizeof(name));
406   offset += name_len;
407   
408   if (!BYTES_ARE_IN_FRAME(offset, 2)) {
409     /* We ran past the end of the captured data in the packet. */
410     return -1;
411   }
412   type = pntohs(&pd[offset]);
413   offset += 2;
414
415   if (!BYTES_ARE_IN_FRAME(offset, 2)) {
416     /* We ran past the end of the captured data in the packet. */
417     return -1;
418   }
419   class = pntohs(&pd[offset]);
420   offset += 2;
421
422   strcpy (name_ret, name);
423   *type_ret = type;
424   *class_ret = class;
425   *name_len_ret = name_len;
426
427   len = offset - start_offset;
428   return len;
429 }
430
431 static double
432 rfc1867_size(u_char val)
433 {
434   double size;
435   guint32 exponent;
436
437   size = (val & 0xF0) >> 4;
438   exponent = (val & 0x0F);
439   while (exponent != 0) {
440     size *= 10;
441     exponent--;
442   }
443   return size / 100;    /* return size in meters, not cm */
444 }
445
446 static char *
447 rfc1867_angle(const u_char *dptr, const char *nsew)
448 {
449   guint32 angle;
450   char direction;
451   guint32 degrees, minutes, secs, tsecs;
452   static char buf[10+1+3+1 + 2+1+3+1 + 2+1+3+1+3+1 + 1 + 1];
453
454   angle = pntohl(dptr);
455
456   if (angle < 0x80000000U) {
457     angle = 0x80000000U - angle;
458     direction = nsew[1];
459   } else {
460     angle = angle - 0x80000000U;
461     direction = nsew[0];
462   }
463   tsecs = angle % 1000;
464   angle = angle / 1000;
465   secs = angle % 60;
466   angle = angle / 60;
467   minutes = angle % 60;
468   degrees = angle / 60;
469   sprintf(buf, "%u deg %u min %u.%03u sec %c", degrees, minutes, secs,
470                 tsecs, direction);
471   return buf;
472 }
473
474 static int
475 dissect_dns_query(const u_char *pd, int offset, int dns_data_offset,
476   frame_data *fd, proto_tree *dns_tree)
477 {
478   int len;
479   char name[MAXDNAME];
480   int name_len;
481   int type;
482   int class;
483   char *class_name;
484   char *type_name;
485   char *long_type_name;
486   const u_char *dptr;
487   const u_char *data_start;
488   proto_tree *q_tree;
489   proto_item *tq;
490
491   data_start = dptr = pd + offset;
492
493   len = get_dns_name_type_class(pd, offset, dns_data_offset, name, &name_len,
494     &type, &class);
495   if (len < 0) {
496     /* We ran past the end of the data in the packet. */
497     return 0;
498   }
499   dptr += len;
500
501   type_name = dns_type_name(type);
502   class_name = dns_class_name(class);
503   long_type_name = dns_long_type_name(type);
504
505   if (fd != NULL)
506     col_append_fstr(fd, COL_INFO, " %s %s", type_name, name);
507   if (dns_tree != NULL) {
508     tq = proto_tree_add_text(dns_tree, offset, len, "%s: type %s, class %s", 
509                    name, type_name, class_name);
510     q_tree = proto_item_add_subtree(tq, ett_dns_qd);
511
512     proto_tree_add_text(q_tree, offset, name_len, "Name: %s", name);
513     offset += name_len;
514
515     proto_tree_add_text(q_tree, offset, 2, "Type: %s", long_type_name);
516     offset += 2;
517
518     proto_tree_add_text(q_tree, offset, 2, "Class: %s", class_name);
519     offset += 2;
520   }
521   
522   return dptr - data_start;
523 }
524
525
526 proto_tree *
527 add_rr_to_tree(proto_item *trr, int rr_type, int offset, const char *name,
528   int namelen, const char *type_name, const char *class_name, u_int ttl,
529   u_short data_len)
530 {
531   proto_tree *rr_tree;
532
533   rr_tree = proto_item_add_subtree(trr, rr_type);
534   proto_tree_add_text(rr_tree, offset, namelen, "Name: %s", name);
535   offset += namelen;
536   proto_tree_add_text(rr_tree, offset, 2, "Type: %s", type_name);
537   offset += 2;
538   proto_tree_add_text(rr_tree, offset, 2, "Class: %s", class_name);
539   offset += 2;
540   proto_tree_add_text(rr_tree, offset, 4, "Time to live: %s",
541                                                 time_secs_to_str(ttl));
542   offset += 4;
543   proto_tree_add_text(rr_tree, offset, 2, "Data length: %u", data_len);
544   return rr_tree;
545 }
546
547 static int
548 dissect_dns_answer(const u_char *pd, int offset, int dns_data_offset,
549   frame_data *fd, proto_tree *dns_tree)
550 {
551   int len;
552   char name[MAXDNAME];
553   int name_len;
554   int type;
555   int class;
556   char *class_name;
557   char *type_name;
558   char *long_type_name;
559   const u_char *dptr;
560   int cur_offset;
561   const u_char *data_start;
562   u_int ttl;
563   u_short data_len;
564   proto_tree *rr_tree;
565   proto_item *trr;
566
567   data_start = dptr = pd + offset;
568   cur_offset = offset;
569
570   len = get_dns_name_type_class(pd, offset, dns_data_offset, name, &name_len,
571     &type, &class);
572   if (len < 0) {
573     /* We ran past the end of the captured data in the packet. */
574     return 0;
575   }
576   dptr += len;
577   cur_offset += len;
578
579   type_name = dns_type_name(type);
580   class_name = dns_class_name(class);
581   long_type_name = dns_long_type_name(type);
582
583   if (!BYTES_ARE_IN_FRAME(cur_offset, 4)) {
584     /* We ran past the end of the captured data in the packet. */
585     return 0;
586   }
587   ttl = pntohl(dptr);
588   dptr += 4;
589   cur_offset += 4;
590
591   if (!BYTES_ARE_IN_FRAME(cur_offset, 2)) {
592     /* We ran past the end of the captured data in the packet. */
593     return 0;
594   }
595   data_len = pntohs(dptr);
596   dptr += 2;
597   cur_offset += 2;
598
599   switch (type) {
600   case T_A:
601     if (fd != NULL) {
602       col_append_fstr(fd, COL_INFO, " %s %s", type_name,
603                         ip_to_str((guint8 *)dptr));
604     }
605     if (dns_tree != NULL) {
606       trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
607                      "%s: type %s, class %s, addr %s",
608                      name, type_name, class_name,
609                      ip_to_str((guint8 *)dptr));
610       rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
611                      long_type_name, class_name, ttl, data_len);
612       if (!BYTES_ARE_IN_FRAME(cur_offset, 4)) {
613         /* We ran past the end of the captured data in the packet. */
614         return 0;
615       }
616       proto_tree_add_text(rr_tree, cur_offset, 4, "Addr: %s",
617                      ip_to_str((guint8 *)dptr));
618     }
619     break;
620
621   case T_NS:
622     {
623       char ns_name[MAXDNAME];
624       int ns_name_len;
625       
626       ns_name_len = get_dns_name(pd, cur_offset, dns_data_offset, ns_name, sizeof(ns_name));
627       if (fd != NULL)
628         col_append_fstr(fd, COL_INFO, " %s %s", type_name, ns_name);
629       if (dns_tree != NULL) {
630         trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
631                        "%s: type %s, class %s, ns %s",
632                        name, type_name, class_name, ns_name);
633         rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
634                        long_type_name, class_name, ttl, data_len);
635         if (ns_name_len < 0) {
636           /* We ran past the end of the captured data in the packet. */
637           return 0;
638         }
639         proto_tree_add_text(rr_tree, cur_offset, ns_name_len, "Name server: %s",
640                         ns_name);
641       }
642     }
643     break;
644
645   case T_CNAME:
646     {
647       char cname[MAXDNAME];
648       int cname_len;
649       
650       cname_len = get_dns_name(pd, cur_offset, dns_data_offset, cname, sizeof(cname));
651       if (fd != NULL)
652         col_append_fstr(fd, COL_INFO, " %s %s", type_name, cname);
653       if (dns_tree != NULL) {
654         trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
655                      "%s: type %s, class %s, cname %s",
656                      name, type_name, class_name, cname);
657         rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
658                        long_type_name, class_name, ttl, data_len);
659         if (cname_len < 0) {
660           /* We ran past the end of the captured data in the packet. */
661           return 0;
662         }
663         proto_tree_add_text(rr_tree, cur_offset, cname_len, "Primary name: %s",
664                         cname);
665       }
666     }
667     break;
668
669   case T_SOA:
670     {
671       char mname[MAXDNAME];
672       int mname_len;
673       char rname[MAXDNAME];
674       int rname_len;
675       guint32 serial;
676       guint32 refresh;
677       guint32 retry;
678       guint32 expire;
679       guint32 minimum;
680
681       mname_len = get_dns_name(pd, cur_offset, dns_data_offset, mname, sizeof(mname));
682       if (mname_len >= 0)
683         rname_len = get_dns_name(pd, cur_offset + mname_len, dns_data_offset, rname, sizeof(rname));
684       else {
685         /* We ran past the end of the captured data in the packet. */
686         rname_len = -1;
687       }
688       if (fd != NULL)
689         col_append_fstr(fd, COL_INFO, " %s %s", type_name, mname);
690       if (dns_tree != NULL) {
691         trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
692                      "%s: type %s, class %s, mname %s",
693                      name, type_name, class_name, mname);
694         rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
695                        long_type_name, class_name, ttl, data_len);
696         if (mname_len < 0) {
697           /* We ran past the end of the captured data in the packet. */
698           return 0;
699         }
700         proto_tree_add_text(rr_tree, cur_offset, mname_len, "Primary name server: %s",
701                        mname);
702         cur_offset += mname_len;
703       
704         if (rname_len < 0) {
705           /* We ran past the end of the captured data in the packet. */
706           return 0;
707         }
708         proto_tree_add_text(rr_tree, cur_offset, rname_len, "Responsible authority's mailbox: %s",
709                        rname);
710         cur_offset += rname_len;
711
712         if (!BYTES_ARE_IN_FRAME(cur_offset, 4)) {
713           /* We ran past the end of the captured data in the packet. */
714           return 0;
715         }
716         serial = pntohl(&pd[cur_offset]);
717         proto_tree_add_text(rr_tree, cur_offset, 4, "Serial number: %u",
718                        serial);
719         cur_offset += 4;
720
721         if (!BYTES_ARE_IN_FRAME(cur_offset, 4)) {
722           /* We ran past the end of the captured data in the packet. */
723           return 0;
724         }
725         refresh = pntohl(&pd[cur_offset]);
726         proto_tree_add_text(rr_tree, cur_offset, 4, "Refresh interval: %s",
727                        time_secs_to_str(refresh));
728         cur_offset += 4;
729
730         if (!BYTES_ARE_IN_FRAME(cur_offset, 4)) {
731           /* We ran past the end of the captured data in the packet. */
732           return 0;
733         }
734         retry = pntohl(&pd[cur_offset]);
735         proto_tree_add_text(rr_tree, cur_offset, 4, "Retry interval: %s",
736                        time_secs_to_str(retry));
737         cur_offset += 4;
738
739         if (!BYTES_ARE_IN_FRAME(cur_offset, 4)) {
740           /* We ran past the end of the captured data in the packet. */
741           return 0;
742         }
743         expire = pntohl(&pd[cur_offset]);
744         proto_tree_add_text(rr_tree, cur_offset, 4, "Expiration limit: %s",
745                        time_secs_to_str(expire));
746         cur_offset += 4;
747
748         if (!BYTES_ARE_IN_FRAME(cur_offset, 4)) {
749           /* We ran past the end of the captured data in the packet. */
750           return 0;
751         }
752         minimum = pntohl(&pd[cur_offset]);
753         proto_tree_add_text(rr_tree, cur_offset, 4, "Minimum TTL: %s",
754                        time_secs_to_str(minimum));
755       }
756     }
757     break;
758
759   case T_PTR:
760     {
761       char pname[MAXDNAME];
762       int pname_len;
763       
764       pname_len = get_dns_name(pd, cur_offset, dns_data_offset, pname, sizeof(pname));
765       if (fd != NULL)
766         col_append_fstr(fd, COL_INFO, " %s %s", type_name, pname);
767       if (dns_tree != NULL) {
768         trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
769                      "%s: type %s, class %s, ptr %s",
770                      name, type_name, class_name, pname);
771         rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
772                        long_type_name, class_name, ttl, data_len);
773         if (pname_len < 0) {
774           /* We ran past the end of the captured data in the packet. */
775           return 0;
776         }
777         proto_tree_add_text(rr_tree, cur_offset, pname_len, "Domain name: %s",
778                         pname);
779       }
780       break;
781     }
782     break;
783
784
785   case T_HINFO:
786     {
787       int cpu_offset;
788       int cpu_len;
789       int os_offset;
790       int os_len;
791
792       cpu_offset = cur_offset;
793       if (!BYTES_ARE_IN_FRAME(cpu_offset, 1)) {
794         /* We ran past the end of the captured data in the packet. */
795         if (dns_tree != NULL) {
796           trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
797                        "%s: type %s, class %s, <CPU goes past end of captured data in packet>",
798                        name, type_name, class_name);
799           rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
800                        long_type_name, class_name, ttl, data_len);
801         }
802         return 0;
803       }
804       cpu_len = pd[cpu_offset];
805       if (!BYTES_ARE_IN_FRAME(cpu_offset + 1, cpu_len)) {
806         /* We ran past the end of the captured data in the packet. */
807         if (dns_tree != NULL) {
808           trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
809                        "%s: type %s, class %s, <CPU goes past end of captured data in packet>",
810                        name, type_name, class_name);
811           rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
812                        long_type_name, class_name, ttl, data_len);
813         }
814         return 0;
815       }
816       os_offset = cpu_offset + 1 + cpu_len;
817       if (!BYTES_ARE_IN_FRAME(os_offset, 1)) {
818         /* We ran past the end of the captured data in the packet. */
819         if (dns_tree != NULL) {
820           trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
821                        "%s: type %s, class %s, CPU %.*s, <OS goes past end of captured data in packet>",
822                        name, type_name, class_name, cpu_len, &pd[cpu_offset + 1]);
823           rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
824                        long_type_name, class_name, ttl, data_len);
825         }
826         return 0;
827       }
828       os_len = pd[os_offset];
829       if (!BYTES_ARE_IN_FRAME(os_offset + 1, os_len)) {
830         /* We ran past the end of the captured data in the packet. */
831         if (dns_tree != NULL) {
832           trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
833                        "%s: type %s, class %s, CPU %*.s, <OS goes past end of captured data in packet>",
834                        name, type_name, class_name, cpu_len, &pd[cpu_offset + 1]);
835           rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
836                        long_type_name, class_name, ttl, data_len);
837         }
838         return 0;
839       }
840       if (fd != NULL)
841         col_append_fstr(fd, COL_INFO, " %s %.*s %.*s", type_name, cpu_len,
842             &pd[cpu_offset + 1], os_len, &pd[os_offset + 1]);
843       if (dns_tree != NULL) {
844         trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
845                      "%s: type %s, class %s, CPU %.*s, OS %.*s",
846                      name, type_name, class_name,
847                      cpu_len, &pd[cpu_offset + 1], os_len, &pd[os_offset + 1]);
848         rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
849                        long_type_name, class_name, ttl, data_len);
850         proto_tree_add_text(rr_tree, cpu_offset, 1 + cpu_len, "CPU: %.*s",
851                         cpu_len, &pd[cpu_offset + 1]);
852         proto_tree_add_text(rr_tree, os_offset, 1 + os_len, "OS: %.*s",
853                         os_len, &pd[os_offset + 1]);
854       }
855       break;
856     }
857     break;
858
859   case T_MX:
860     {
861       guint16 preference = 0;
862       char mx_name[MAXDNAME];
863       int mx_name_len;
864       
865       if (!BYTES_ARE_IN_FRAME(cur_offset, 2)) {
866         /* We ran past the end of the captured data in the packet. */
867         if (dns_tree != NULL) {
868           trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
869                        "%s: type %s, class %s, <preference goes past end of captured data in packet>",
870                        name, type_name, class_name);
871           rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
872                        long_type_name, class_name, ttl, data_len);
873         }
874         return 0;
875       }
876       preference = pntohs(&pd[cur_offset]);
877       mx_name_len = get_dns_name(pd, cur_offset + 2, dns_data_offset, mx_name, sizeof(mx_name));
878       if (mx_name_len < 0) {
879         /* We ran past the end of the captured data in the packet. */
880         if (dns_tree != NULL) {
881           trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
882                        "%s: type %s, class %s, preference %u, <mx goes past end of captured data in packet>",
883                        name, type_name, class_name, preference);
884           rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
885                        long_type_name, class_name, ttl, data_len);
886         }
887         return 0;
888       }
889       if (fd != NULL)
890         col_append_fstr(fd, COL_INFO, " %s %u %s", type_name, preference, mx_name);
891       if (dns_tree != NULL) {
892         trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
893                        "%s: type %s, class %s, preference %u, mx %s",
894                        name, type_name, class_name, preference, mx_name);
895         rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
896                        long_type_name, class_name, ttl, data_len);
897         proto_tree_add_text(rr_tree, cur_offset, 2, "Preference: %u", preference);
898         proto_tree_add_text(rr_tree, cur_offset + 2, mx_name_len, "Mail exchange: %s",
899                         mx_name);
900       }
901     }
902     break;
903
904   case T_AAAA:
905     if (fd != NULL) {
906       col_append_fstr(fd, COL_INFO, " %s %s", type_name,
907                         ip6_to_str((struct e_in6_addr *)dptr));
908     }
909     if (dns_tree != NULL) {
910       trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
911                      "%s: type %s, class %s, addr %s",
912                      name, type_name, class_name,
913                      ip6_to_str((struct e_in6_addr *)dptr));
914       rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
915                      long_type_name, class_name, ttl, data_len);
916       if (!BYTES_ARE_IN_FRAME(cur_offset, 16)) {
917         /* We ran past the end of the captured data in the packet. */
918         return 0;
919       }
920       proto_tree_add_text(rr_tree, cur_offset, 16, "Addr: %s",
921                      ip6_to_str((struct e_in6_addr *)dptr));
922     }
923     break;
924
925   case T_LOC:
926     {
927       if (fd != NULL)
928         col_append_fstr(fd, COL_INFO, " %s", type_name);
929       if (dns_tree != NULL) {
930         trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
931                      "%s: type %s, class %s",
932                      name, type_name, class_name);
933         rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
934                        long_type_name, class_name, ttl, data_len);
935         if (!BYTES_ARE_IN_FRAME(cur_offset, 1)) {
936           /* We ran past the end of the captured data in the packet. */
937           return 0;
938         }
939         proto_tree_add_text(rr_tree, cur_offset, 1, "Version: %u", pd[cur_offset]);
940         if (pd[cur_offset] == 0) {
941           /* Version 0, the only version RFC 1876 discusses. */
942           cur_offset++;
943
944           if (!BYTES_ARE_IN_FRAME(cur_offset, 1)) {
945             /* We ran past the end of the captured data in the packet. */
946             return 0;
947           }
948           proto_tree_add_text(rr_tree, cur_offset, 1, "Size: %g m",
949                                 rfc1867_size(pd[cur_offset]));
950           cur_offset++;
951
952           if (!BYTES_ARE_IN_FRAME(cur_offset, 1)) {
953             /* We ran past the end of the captured data in the packet. */
954             return 0;
955           }
956           proto_tree_add_text(rr_tree, cur_offset, 1, "Horizontal precision: %g m",
957                                 rfc1867_size(pd[cur_offset]));
958           cur_offset++;
959
960           if (!BYTES_ARE_IN_FRAME(cur_offset, 1)) {
961             /* We ran past the end of the captured data in the packet. */
962             return 0;
963           }
964           proto_tree_add_text(rr_tree, cur_offset, 1, "Vertical precision: %g m",
965                                 rfc1867_size(pd[cur_offset]));
966           cur_offset++;
967
968           if (!BYTES_ARE_IN_FRAME(cur_offset, 4)) {
969             /* We ran past the end of the captured data in the packet. */
970             return 0;
971           }
972           proto_tree_add_text(rr_tree, cur_offset, 4, "Latitude: %s",
973                                 rfc1867_angle(&pd[cur_offset], "NS"));
974           cur_offset += 4;
975
976           if (!BYTES_ARE_IN_FRAME(cur_offset, 4)) {
977             /* We ran past the end of the captured data in the packet. */
978             return 0;
979           }
980           proto_tree_add_text(rr_tree, cur_offset, 4, "Longitude: %s",
981                                 rfc1867_angle(&pd[cur_offset], "EW"));
982           cur_offset += 4;
983
984           if (!BYTES_ARE_IN_FRAME(cur_offset, 4)) {
985             /* We ran past the end of the captured data in the packet. */
986             return 0;
987           }
988           proto_tree_add_text(rr_tree, cur_offset, 4, "Altitude: %g m",
989                                 (pntohl(&pd[cur_offset]) - 10000000)/100.0);
990         } else
991           proto_tree_add_text(rr_tree, cur_offset, data_len, "Data");
992       }
993       break;
994     }
995     break;
996       
997     /* TODO: parse more record types */
998
999   default:
1000     if (fd != NULL)
1001       col_append_fstr(fd, COL_INFO, " %s", type_name);
1002     if (dns_tree != NULL) {
1003       trr = proto_tree_add_text(dns_tree, offset, (dptr - data_start) + data_len,
1004                      "%s: type %s, class %s",
1005                      name, type_name, class_name);
1006       rr_tree = add_rr_to_tree(trr, ett_dns_rr, offset, name, name_len,
1007                        long_type_name, class_name, ttl, data_len);
1008       proto_tree_add_text(rr_tree, cur_offset, data_len, "Data");
1009     }
1010   }
1011   
1012   dptr += data_len;
1013         
1014   return dptr - data_start;
1015 }
1016
1017 static int
1018 dissect_query_records(const u_char *pd, int cur_off, int dns_data_offset,
1019     int count, frame_data *fd, proto_tree *dns_tree)
1020 {
1021   int start_off, add_off;
1022   proto_tree *qatree = NULL;
1023   proto_item *ti = NULL;
1024   
1025   start_off = cur_off;
1026   if (dns_tree) {
1027     ti = proto_tree_add_text(dns_tree, start_off, 0, "Queries");
1028     qatree = proto_item_add_subtree(ti, ett_dns_qry);
1029   }
1030   while (count-- > 0) {
1031     add_off = dissect_dns_query(pd, cur_off, dns_data_offset, fd, qatree);
1032     if (add_off <= 0) {
1033       /* We ran past the end of the captured data in the packet. */
1034       break;
1035     }
1036     cur_off += add_off;
1037   }
1038   if (ti)
1039     proto_item_set_len(ti, cur_off - start_off);
1040
1041   return cur_off - start_off;
1042 }
1043
1044 static int
1045 dissect_answer_records(const u_char *pd, int cur_off, int dns_data_offset,
1046     int count, frame_data *fd, proto_tree *dns_tree, char *name)
1047 {
1048   int start_off, add_off;
1049   proto_tree *qatree = NULL;
1050   proto_item *ti = NULL;
1051   
1052   start_off = cur_off;
1053   if (dns_tree) {
1054     ti = proto_tree_add_text(dns_tree, start_off, 0, name);
1055     qatree = proto_item_add_subtree(ti, ett_dns_ans);
1056   }
1057   while (count-- > 0) {
1058     add_off = dissect_dns_answer(pd, cur_off, dns_data_offset, fd, qatree);
1059     if (add_off <= 0) {
1060       /* We ran past the end of the captured data in the packet. */
1061       break;
1062     }
1063     cur_off += add_off;
1064   }
1065   if (ti)
1066     proto_item_set_len(ti, cur_off - start_off);
1067
1068   return cur_off - start_off;
1069 }
1070
1071 void
1072 dissect_dns(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
1073 {
1074   int dns_data_offset;
1075   proto_tree *dns_tree = NULL, *field_tree;
1076   proto_item *ti, *tf;
1077   guint16    id, flags, quest, ans, auth, add;
1078   char buf[128+1];
1079   int cur_off;
1080   static const value_string opcode_vals[] = {
1081                   { OPCODE_QUERY,  "Standard query"        },
1082                   { OPCODE_IQUERY, "Inverse query"         },
1083                   { OPCODE_STATUS, "Server status request" },
1084                   { 0,              NULL                   } };
1085   static const value_string rcode_vals[] = {
1086                   { RCODE_NOERROR,   "No error"        },
1087                   { RCODE_FMTERROR,  "Format error"    },
1088                   { RCODE_SERVFAIL,  "Server failure"  },
1089                   { RCODE_NAMEERROR, "Name error"      },
1090                   { RCODE_NOTIMPL,   "Not implemented" },
1091                   { RCODE_REFUSED,   "Refused"         },
1092                   { 0,               NULL              } };
1093
1094   dns_data_offset = offset;
1095
1096   if (check_col(fd, COL_PROTOCOL))
1097     col_add_str(fd, COL_PROTOCOL, "DNS (UDP)");
1098
1099   if (pi.captured_len < DNS_HDRLEN) {
1100     col_add_str(fd, COL_INFO, "Short DNS packet");
1101     dissect_data(pd, offset, fd, tree);
1102     return;
1103   }
1104
1105   /* To do: check for errs, etc. */
1106   id    = pntohs(&pd[offset + DNS_ID]);
1107   flags = pntohs(&pd[offset + DNS_FLAGS]);
1108   quest = pntohs(&pd[offset + DNS_QUEST]);
1109   ans   = pntohs(&pd[offset + DNS_ANS]);
1110   auth  = pntohs(&pd[offset + DNS_AUTH]);
1111   add   = pntohs(&pd[offset + DNS_ADD]);
1112
1113   if (check_col(fd, COL_INFO)) {
1114     col_add_fstr(fd, COL_INFO, "%s%s",
1115                 val_to_str(flags & F_OPCODE, opcode_vals,
1116                            "Unknown operation (%x)"),
1117                 (flags & F_RESPONSE) ? " response" : "");
1118   } else {
1119     /* Set "fd" to NULL; we pass a NULL "fd" to the query and answer
1120        dissectors, as a way of saying that they shouldn't add stuff
1121        to the COL_INFO column (a call to "check_col(fd, COL_INFO)"
1122        is more expensive than a check that a pointer isn't NULL). */
1123     fd = NULL;
1124   }
1125   
1126   if (tree) {
1127     ti = proto_tree_add_item_format(tree, proto_dns, offset, 4, NULL,
1128                           (flags & F_RESPONSE) ? "DNS response" : "DNS query");
1129     
1130     dns_tree = proto_item_add_subtree(ti, ett_dns);
1131
1132     if (flags & F_RESPONSE)
1133       proto_tree_add_item_hidden(dns_tree, hf_dns_response, offset, 4, 1);
1134     else
1135       proto_tree_add_item_hidden(dns_tree, hf_dns_query, offset, 4, 1);
1136
1137     proto_tree_add_item(dns_tree, hf_dns_transaction_id, 
1138                         offset + DNS_ID, 2, id);
1139
1140     strcpy(buf, val_to_str(flags & F_OPCODE, opcode_vals, "Unknown operation"));
1141     if (flags & F_RESPONSE) {
1142       strcat(buf, " response");
1143       strcat(buf, ", ");
1144       strcat(buf, val_to_str(flags & F_RCODE, rcode_vals,
1145             "Unknown error"));
1146     }
1147     tf = proto_tree_add_item_format(dns_tree, hf_dns_flags, 
1148                                     offset + DNS_FLAGS, 2, 
1149                                     flags,
1150                                     "Flags: 0x%04x (%s)",
1151                                     flags, buf);
1152     field_tree = proto_item_add_subtree(tf, ett_dns_flags);
1153     proto_tree_add_text(field_tree, offset + DNS_FLAGS, 2, "%s",
1154        decode_boolean_bitfield(flags, F_RESPONSE,
1155             2*8, "Response", "Query"));
1156     proto_tree_add_text(field_tree, offset + DNS_FLAGS, 2, "%s",
1157        decode_enumerated_bitfield(flags, F_OPCODE,
1158             2*8, opcode_vals, "%s"));
1159     if (flags & F_RESPONSE) {
1160       proto_tree_add_text(field_tree, offset + DNS_FLAGS, 2, "%s",
1161          decode_boolean_bitfield(flags, F_AUTHORITATIVE,
1162               2*8,
1163               "Server is an authority for domain",
1164               "Server isn't an authority for domain"));
1165     }
1166     proto_tree_add_text(field_tree, offset + DNS_FLAGS, 2, "%s",
1167        decode_boolean_bitfield(flags, F_TRUNCATED,
1168             2*8,
1169             "Message is truncated",
1170             "Message is not truncated"));
1171     proto_tree_add_text(field_tree, offset + DNS_FLAGS, 2, "%s",
1172        decode_boolean_bitfield(flags, F_RECDESIRED,
1173             2*8,
1174             "Do query recursively",
1175             "Don't do query recursively"));
1176     if (flags & F_RESPONSE) {
1177       proto_tree_add_text(field_tree, offset + DNS_FLAGS, 2, "%s",
1178          decode_boolean_bitfield(flags, F_RECAVAIL,
1179               2*8,
1180               "Server can do recursive queries",
1181               "Server can't do recursive queries"));
1182       proto_tree_add_text(field_tree, offset + DNS_FLAGS, 2, "%s",
1183          decode_enumerated_bitfield(flags, F_RCODE,
1184               2*8, rcode_vals, "%s"));
1185     }
1186     proto_tree_add_item(dns_tree, hf_dns_count_questions, 
1187                         offset + DNS_QUEST, 2, quest);
1188     proto_tree_add_item(dns_tree, hf_dns_count_answers, 
1189                         offset + DNS_ANS, 2, ans);
1190     proto_tree_add_item(dns_tree, hf_dns_count_auth_rr, 
1191                         offset + DNS_AUTH, 2, auth);
1192     proto_tree_add_item(dns_tree, hf_dns_count_add_rr, 
1193                         offset + DNS_ADD, 2, add);
1194
1195   }
1196   cur_off = offset + DNS_HDRLEN;
1197
1198   if (quest > 0) {
1199     /* If this is a response, don't add information about the queries
1200        to the summary, just add information about the answers. */
1201     cur_off += dissect_query_records(pd, cur_off, dns_data_offset, quest,
1202                                         (!(flags & F_RESPONSE) ? fd : NULL),
1203                                         dns_tree);
1204   }
1205     
1206   if (ans > 0) {
1207     /* If this is a request, don't add information about the answers
1208        to the summary, just add information about the queries. */
1209     cur_off += dissect_answer_records(pd, cur_off, dns_data_offset, ans,
1210                                         ((flags & F_RESPONSE) ? fd : NULL),
1211                                         dns_tree, "Answers");
1212   }
1213     
1214   if (tree) {
1215     /* Don't add information about the authoritative name servers, or the
1216        additional records, to the summary. */
1217     if (auth > 0)
1218       cur_off += dissect_answer_records(pd, cur_off, dns_data_offset, auth,
1219           NULL, dns_tree, "Authoritative nameservers");
1220
1221     if (add > 0)
1222       cur_off += dissect_answer_records(pd, cur_off, dns_data_offset, add,
1223           NULL, dns_tree, "Additional records");
1224   }
1225 }
1226
1227 void
1228 proto_register_dns(void)
1229 {
1230   static hf_register_info hf[] = {
1231     { &hf_dns_response,
1232       { "Response",             "dns.response",  
1233         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1234         "TRUE if DNS response" }},
1235     { &hf_dns_query,
1236       { "Query",                "dns.query",  
1237         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1238         "TRUE if DNS query" }},
1239     { &hf_dns_flags,
1240       { "Flags",                "dns.flags",  
1241         FT_UINT16, BASE_HEX, NULL, 0x0,
1242         "" }},
1243     { &hf_dns_transaction_id,
1244       { "Transaction ID",       "dns.id",  
1245         FT_UINT16, BASE_HEX, NULL, 0x0,
1246         "Identification of transaction" }},
1247     { &hf_dns_count_questions,
1248       { "Questions",            "dns.count.queries",  
1249         FT_UINT16, BASE_DEC, NULL, 0x0,
1250         "Number of queries in packet" }},
1251     { &hf_dns_count_answers,
1252       { "Answer RRs",           "dns.count.answers",  
1253         FT_UINT16, BASE_DEC, NULL, 0x0,
1254         "Number of answers in packet" }},
1255     { &hf_dns_count_auth_rr,
1256       { "Authority RRs",        "dns.count.auth_rr",  
1257         FT_UINT16, BASE_DEC, NULL, 0x0,
1258         "Number of authoritative records in packet" }},
1259     { &hf_dns_count_add_rr,
1260       { "Additional RRs",       "dns.count.add_rr",  
1261         FT_UINT16, BASE_DEC, NULL, 0x0,
1262         "Number of additional records in packet" }}
1263   };
1264   static gint *ett[] = {
1265     &ett_dns,
1266     &ett_dns_qd,
1267     &ett_dns_rr,
1268     &ett_dns_qry,
1269     &ett_dns_ans,
1270     &ett_dns_flags,
1271   };
1272
1273   proto_dns = proto_register_protocol("Domain Name Service", "dns");
1274   proto_register_field_array(proto_dns, hf, array_length(hf));
1275   proto_register_subtree_array(ett, array_length(ett));
1276 }