2cd728f37d30d57c19421ec81aa9378601c329a2
[oweals/gnunet.git] / src / dns / dnsparser.c
1 /*
2       This file is part of GNUnet
3       (C) 2010, 2011, 2012 Christian Grothoff (and other contributing authors)
4
5       GNUnet is free software; you can redistribute it and/or modify
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 2, or (at your
8       option) any later version.
9
10       GNUnet is distributed in the hope that it will be useful, but
11       WITHOUT ANY WARRANTY; without even the implied warranty of
12       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13       General Public License for more details.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18       Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file dns/dnsparser.c
23  * @brief helper library to parse DNS packets. 
24  * @author Philipp Toelke
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_dnsparser_lib.h"
30
31
32 // DNS-Stuff
33 GNUNET_NETWORK_STRUCT_BEGIN
34 struct dns_header
35 {
36   uint16_t id GNUNET_PACKED;
37   struct GNUNET_DNSPARSER_Flags flags; 
38   uint16_t query_count GNUNET_PACKED;       // number of questions
39   uint16_t answer_rcount GNUNET_PACKED;       // number of answers
40   uint16_t authority_rcount GNUNET_PACKED;       // number of authority-records
41   uint16_t additional_rcount GNUNET_PACKED;       // number of additional records
42 };
43
44 struct query_line
45 {
46   uint16_t type GNUNET_PACKED;
47   uint16_t class GNUNET_PACKED;
48 };
49
50 struct record_line
51 {
52   uint16_t type GNUNET_PACKED;
53   uint16_t class GNUNET_PACKED;
54   uint32_t ttl GNUNET_PACKED;
55   uint16_t data_len GNUNET_PACKED;
56 };
57
58 GNUNET_NETWORK_STRUCT_END
59
60
61 /**
62  * Parse name inside of a DNS query or record.
63  *
64  * @param udp_payload entire UDP payload
65  * @param udp_payload_length length of udp_payload
66  * @param off pointer to the offset of the name to parse in the udp_payload (to be
67  *                    incremented by the size of the name)
68  * @return name as 0-terminated C string on success, NULL if the payload is malformed
69  */
70 static char *
71 parse_name (const char *udp_payload,
72             size_t udp_payload_length,
73             size_t *off)
74 {
75   const uint8_t *input = (const uint8_t *) udp_payload;
76   char *ret;
77   char *tmp;
78   char *xstr;
79   uint8_t len;
80   size_t xoff;
81   
82   ret = GNUNET_strdup ("");
83   while (1)
84   {
85     if (*off == udp_payload_length)
86       goto error;
87     len = input[*off];
88     if (0 == len)
89       break;
90     if (len < 64)
91     {
92       if (*off + 1 + len > udp_payload_length)
93         goto error;
94       GNUNET_asprintf (&tmp,
95                        "%s%.*s.",
96                        ret,
97                        (int) len,
98                        &udp_payload[*off + 1]);
99       GNUNET_free (ret);
100       ret = tmp;
101       off += 1 + len;
102     }
103     else if ((64 | 128) == (len & (64 | 128)) )
104     {
105       /* pointer to string */
106       if (*off + 1 > udp_payload_length)
107         goto error;
108       xoff = ((len - (64 | 128)) << 8) + input[*off+1];
109       xstr = parse_name (udp_payload,
110                          udp_payload_length,
111                          &xoff);
112       GNUNET_asprintf (&tmp,
113                        "%s%s.",
114                        ret,
115                        xstr);
116       GNUNET_free (ret);
117       GNUNET_free (xstr);
118       ret = tmp;
119       off += 2;
120       /* pointers always terminate names */
121       break;
122     } 
123     else
124     {
125       /* neither pointer nor inline string, not supported... */
126       goto error;
127     }
128   }
129   if (0 < strlen(ret))
130     ret[strlen(ret)-1] = '\0'; /* eat tailing '.' */
131   return ret;
132  error:  
133   GNUNET_free (ret);
134   return NULL;
135 }
136
137
138 /**
139  * Parse a DNS query entry.
140  *
141  * @param udp_payload entire UDP payload
142  * @param udp_payload_length length of udp_payload
143  * @param off pointer to the offset of the query to parse in the udp_payload (to be
144  *                    incremented by the size of the query)
145  * @param q where to write the query information
146  * @return GNUNET_OK on success, GNUNET_SYSERR if the query is malformed
147  */
148 static int
149 parse_query (const char *udp_payload,
150              size_t udp_payload_length,
151              size_t *off,
152              struct GNUNET_DNSPARSER_Query *q)
153 {
154   char *name;
155   struct query_line ql;
156
157   name = parse_name (udp_payload, 
158                      udp_payload_length,
159                      off);
160   if (NULL == name)
161     return GNUNET_SYSERR;
162   q->name = name;
163   if (*off + sizeof (struct query_line) > udp_payload_length)
164     return GNUNET_SYSERR;
165   memcpy (&ql, &udp_payload[*off], sizeof (ql));
166   *off += sizeof (ql);
167   q->type = ntohs (ql.type);
168   q->class = ntohs (ql.class);
169   return GNUNET_OK;
170 }
171
172
173 /**
174  * Parse a DNS record entry.
175  *
176  * @param udp_payload entire UDP payload
177  * @param udp_payload_length length of udp_payload
178  * @param off pointer to the offset of the record to parse in the udp_payload (to be
179  *                    incremented by the size of the record)
180  * @param r where to write the record information
181  * @return GNUNET_OK on success, GNUNET_SYSERR if the record is malformed
182  */
183 static int
184 parse_record (const char *udp_payload,
185               size_t udp_payload_length,
186               size_t *off,
187               struct GNUNET_DNSPARSER_Record *r)
188 {
189   char *name;
190   struct record_line rl;
191
192   name = parse_name (udp_payload, 
193                      udp_payload_length,
194                      off);
195   if (NULL == name)
196     return GNUNET_SYSERR;
197   r->name = name;
198   if (*off + sizeof (struct record_line) > udp_payload_length)
199     return GNUNET_SYSERR;
200   memcpy (&rl, &udp_payload[*off], sizeof (rl));
201   *off += sizeof (rl);
202   r->type = ntohs (rl.type);
203   r->class = ntohs (rl.class);
204   r->expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
205                                                                                         ntohl (rl.ttl)));
206   r->data_len = ntohs (rl.data_len);
207   if (*off + r->data_len > udp_payload_length)
208     return GNUNET_SYSERR;
209   if (0 == r->data_len)
210     return GNUNET_OK;
211   r->data = GNUNET_malloc (r->data_len);
212   memcpy (r->data, &udp_payload[*off], r->data_len);
213   *off += r->data_len;
214   return GNUNET_OK;  
215 }
216
217
218 /**
219  * Parse a UDP payload of a DNS packet in to a nice struct for further
220  * processing and manipulation.
221  *
222  * @param udp_payload wire-format of the DNS packet
223  * @param udp_payload_length number of bytes in udp_payload 
224  * @return NULL on error, otherwise the parsed packet
225  */
226 struct GNUNET_DNSPARSER_Packet *
227 GNUNET_DNSPARSER_parse (const char *udp_payload,
228                         size_t udp_payload_length)
229 {
230   struct GNUNET_DNSPARSER_Packet *p;
231   const struct dns_header *dns;
232   size_t off;
233   unsigned int n;  
234   unsigned int i;
235
236   if (udp_payload_length < sizeof (struct dns_header))
237     return NULL;
238   dns = (const struct dns_header *) udp_payload;
239   off = sizeof (struct dns_header);
240   p = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_Packet));
241   p->flags = dns->flags;
242   p->id = dns->id;
243   n = ntohs (dns->query_count);
244   if (n > 0)
245   {
246     p->queries = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Query));
247     p->num_queries = n;
248     for (i=0;i<n;i++)
249       if (GNUNET_OK !=
250           parse_query (udp_payload,
251                        udp_payload_length,
252                        &off,
253                        &p->queries[i]))
254         goto error;
255   }
256   n = ntohs (dns->answer_rcount);
257   if (n > 0)
258   {
259     p->answers = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
260     p->num_answers = n;
261     for (i=0;i<n;i++)
262       if (GNUNET_OK !=
263           parse_record (udp_payload,
264                         udp_payload_length,
265                         &off,
266                         &p->answers[i]))
267         goto error;
268   }
269   n = ntohs (dns->authority_rcount);
270   if (n > 0)
271   {
272     p->authority_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
273     p->num_authority_records = n;
274     for (i=0;i<n;i++)
275       if (GNUNET_OK !=
276           parse_record (udp_payload,
277                         udp_payload_length,
278                         &off,
279                         &p->authority_records[i]))
280         goto error;  
281   }
282   n = ntohs (dns->additional_rcount);
283   if (n > 0)
284   {
285     p->additional_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
286     p->num_additional_records = n;
287     for (i=0;i<n;i++)
288       if (GNUNET_OK !=
289           parse_record (udp_payload,
290                         udp_payload_length,
291                         &off,
292                         &p->additional_records[i]))
293         goto error;   
294   }
295   return p;
296  error:
297   GNUNET_DNSPARSER_free_packet (p);
298   return NULL;
299 }
300
301
302 /**
303  * Free memory taken by a packet.
304  *
305  * @param p packet to free
306  */
307 void
308 GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
309 {
310   unsigned int i;
311
312   for (i=0;i<p->num_queries;i++)
313     GNUNET_free_non_null (p->queries[i].name);
314   GNUNET_free_non_null (p->queries);
315   for (i=0;i<p->num_answers;i++)
316   {
317     GNUNET_free_non_null (p->answers[i].name);
318     GNUNET_free_non_null (p->answers[i].data);
319   }
320   GNUNET_free_non_null (p->answers);
321   for (i=0;i<p->num_authority_records;i++)
322   {
323     GNUNET_free_non_null (p->authority_records[i].name);
324     GNUNET_free_non_null (p->authority_records[i].data);
325   }
326   GNUNET_free_non_null (p->authority_records);
327   for (i=0;i<p->num_additional_records;i++)
328   {
329     GNUNET_free_non_null (p->additional_records[i].name);
330     GNUNET_free_non_null (p->additional_records[i].data);
331   }
332   GNUNET_free_non_null (p->additional_records);
333   GNUNET_free (p);
334 }
335
336
337 /**
338  * Given a DNS packet, generate the corresponding UDP payload.
339  *
340  * @param p packet to pack
341  * @param buf set to a buffer with the packed message
342  * @param buf_length set to the length of buf
343  * @return GNUNET_SYSERR if 'p' is invalid
344  *         GNUNET_NO if 'p' was truncated (but there is still a result in 'buf')
345  *         GNUNET_OK if 'p' was packed completely into '*buf'
346  */
347 int
348 GNUNET_DNSPARSER_pack (struct GNUNET_DNSPARSER_Packet *p,
349                        char **buf,
350                        size_t *buf_length)
351 {
352   // FIXME: not implemented
353   GNUNET_break (0);
354   return GNUNET_SYSERR;
355 }
356
357
358
359
360 /* legacy code follows */
361
362 /**
363  * Parse a name from DNS to a normal .-delimited, 0-terminated string.
364  *
365  * @param d The destination of the name. Should have at least 255 bytes allocated.
366  * @param src The DNS-Packet
367  * @param idx The offset inside the Packet from which on the name should be read
368  * @returns The offset of the first unparsed byte (the byte right behind the name)
369  */
370 static unsigned int
371 parse_dns_name (char *d, const unsigned char *src, unsigned short idx)
372 {                               /*{{{ */
373   char *dest = d;
374
375   int len = src[idx++];
376
377   while (len != 0)
378   {
379     if (len & 0xC0)
380     {                           /* Compressed name, offset in this and the next octet */
381       unsigned short offset = ((len & 0x3F) << 8) | src[idx++];
382
383       parse_dns_name (dest, src, offset - 12);  /* 12 for the Header of the DNS-Packet, idx starts at 0 which is 12 bytes from the start of the packet */
384       return idx;
385     }
386     memcpy (dest, src + idx, len);
387     idx += len;
388     dest += len;
389     *dest = '.';
390     dest++;
391     len = src[idx++];
392   };
393   *dest = 0;
394
395   return idx;
396 }
397
398 /*}}}*/
399
400 /**
401  * Parse a complete DNS-Record from raw DNS-data to a struct dns_record
402  *
403  * @param data The DNS-data
404  * @param dst Pointer to count pointers; individual pointers will be allocated
405  * @param count Number of records to parse
406  * @param idx The offset inside the Packet from which on the name should be read
407  * @returns The offset of the first unparsed byte (the byte right behind the last record)
408  */
409 static unsigned short
410 parse_dns_record (unsigned char *data,  /*{{{ */
411                   struct dns_record **dst, unsigned short count,
412                   unsigned short idx)
413 {
414   int i;
415   unsigned short _idx;
416
417   for (i = 0; i < count; i++)
418   {
419     dst[i] = GNUNET_malloc (sizeof (struct dns_record));
420     dst[i]->name = alloca (255);        // see RFC1035, no name can be longer than this.
421     char *name = dst[i]->name;
422
423     _idx = parse_dns_name (name, data, idx);
424     dst[i]->namelen = _idx - idx;
425
426     dst[i]->name = GNUNET_malloc (dst[i]->namelen);
427     memcpy (dst[i]->name, name, dst[i]->namelen);
428
429     idx = _idx;
430
431     dst[i]->type = *((unsigned short *) (data + idx));
432     idx += 2;
433     dst[i]->class = *((unsigned short *) (data + idx));
434     idx += 2;
435     dst[i]->ttl = *((unsigned int *) (data + idx));
436     idx += 4;
437     dst[i]->data_len = *((unsigned short *) (data + idx));
438     idx += 2;
439     dst[i]->data = GNUNET_malloc (ntohs (dst[i]->data_len));
440     memcpy (dst[i]->data, data + idx, ntohs (dst[i]->data_len));
441     idx += ntohs (dst[i]->data_len);
442   }
443   return idx;
444 }                               /*}}} */
445
446 /**
447  * Parse a raw DNS-Packet into an usable struct
448  */
449 struct dns_pkt_parsed *
450 parse_dns_packet (struct dns_pkt *pkt)
451 {                               /*{{{ */
452   struct dns_pkt_parsed *ppkt = GNUNET_malloc (sizeof (struct dns_pkt_parsed));
453
454   memcpy (&ppkt->s, &pkt->s, sizeof pkt->s);
455
456   unsigned short qdcount = ntohs (ppkt->s.qdcount);
457   unsigned short ancount = ntohs (ppkt->s.ancount);
458   unsigned short nscount = ntohs (ppkt->s.nscount);
459   unsigned short arcount = ntohs (ppkt->s.arcount);
460
461   ppkt->queries = GNUNET_malloc (qdcount * sizeof (struct dns_query *));
462   ppkt->answers = GNUNET_malloc (ancount * sizeof (struct dns_record *));
463   ppkt->nameservers = GNUNET_malloc (nscount * sizeof (struct dns_record *));
464   ppkt->additional = GNUNET_malloc (arcount * sizeof (struct dns_record *));
465
466   unsigned short idx = 0, _idx; /* This keeps track how far we have parsed the data */
467
468   /* Parse the Query */
469   int i;
470
471   for (i = 0; i < qdcount; i++)
472   {                             /*{{{ */
473     ppkt->queries[i] = GNUNET_malloc (sizeof (struct dns_query));
474     char *name = alloca (255);  /* see RFC1035, it can't be more than this. */
475
476     _idx = parse_dns_name (name, pkt->data, idx);
477     ppkt->queries[i]->namelen = _idx - idx;
478     idx = _idx;
479
480     ppkt->queries[i]->name = GNUNET_malloc (ppkt->queries[i]->namelen);
481     memcpy (ppkt->queries[i]->name, name, ppkt->queries[i]->namelen);
482
483     ppkt->queries[i]->qtype = *((unsigned short *) (pkt->data + idx));
484     idx += 2;
485     ppkt->queries[i]->qclass = *((unsigned short *) (pkt->data + idx));
486     idx += 2;
487   }
488   /*}}} */
489   idx = parse_dns_record (pkt->data, ppkt->answers, ancount, idx);
490   idx = parse_dns_record (pkt->data, ppkt->nameservers, nscount, idx);
491   idx = parse_dns_record (pkt->data, ppkt->additional, arcount, idx);
492   return ppkt;
493 }                               /*}}} */
494
495 static void
496 unparse_dns_name (char *dest, char *src, size_t len)
497 {
498   char *b = dest;
499   char cnt = 0;
500
501   dest++;
502   while (*src != 0)
503   {
504     while (*src != '.' && *src != 0)
505     {
506       *dest = *src;
507       src++;
508       dest++;
509       cnt++;
510     }
511     *b = cnt;
512     cnt = 0;
513     b = dest;
514     dest++;
515     src++;
516   }
517   *b = 0;
518 }
519
520 struct dns_pkt *
521 unparse_dns_packet (struct dns_pkt_parsed *ppkt)
522 {
523   size_t size = sizeof (struct dns_pkt) - 1;
524   int i;
525
526   for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
527     size += ppkt->queries[i]->namelen + 1;
528
529   for (i = 0; i < ntohs (ppkt->s.ancount); i++)
530   {
531     size += ppkt->answers[i]->namelen + 1;
532     size += ppkt->answers[i]->data_len;
533   }
534   for (i = 0; i < ntohs (ppkt->s.nscount); i++)
535   {
536     size += ppkt->nameservers[i]->namelen + 1;
537     size += ppkt->nameservers[i]->data_len;
538   }
539   for (i = 0; i < ntohs (ppkt->s.arcount); i++)
540   {
541     size += ppkt->additional[i]->namelen + 1;
542     size += ppkt->additional[i]->data_len;
543   }
544
545   size +=
546       4 * ntohs (ppkt->s.qdcount) + 10 * (ntohs (ppkt->s.ancount) +
547                                           ntohs (ppkt->s.arcount) +
548                                           ntohs (ppkt->s.nscount));
549
550   struct dns_pkt *pkt = GNUNET_malloc (size);
551   char *pkt_c = (char *) pkt;
552
553   memcpy (&pkt->s, &ppkt->s, sizeof ppkt->s);
554   size_t idx = sizeof ppkt->s;
555
556   for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
557   {
558     unparse_dns_name (&pkt_c[idx], ppkt->queries[i]->name,
559                       ppkt->queries[i]->namelen);
560     idx += ppkt->queries[i]->namelen;
561     struct dns_query_line *d = (struct dns_query_line *) &pkt_c[idx];
562
563     d->class = ppkt->queries[i]->qclass;
564     d->type = ppkt->queries[i]->qtype;
565     idx += sizeof (struct dns_query_line);
566   }
567
568   for (i = 0; i < ntohs (ppkt->s.ancount); i++)
569   {
570     unparse_dns_name (&pkt_c[idx], ppkt->answers[i]->name,
571                       ppkt->answers[i]->namelen);
572     idx += ppkt->answers[i]->namelen;
573     struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
574
575     r->type = ppkt->answers[i]->type;
576     r->class = ppkt->answers[i]->class;
577     r->ttl = ppkt->answers[i]->ttl;
578     r->data_len = ppkt->answers[i]->data_len;
579     idx += sizeof (struct dns_record_line);
580     memcpy (&r->data, ppkt->answers[i]->data, ppkt->answers[i]->data_len);
581     idx += ppkt->answers[i]->data_len;
582   }
583
584   for (i = 0; i < ntohs (ppkt->s.nscount); i++)
585   {
586     unparse_dns_name (&pkt_c[idx], ppkt->nameservers[i]->name,
587                       ppkt->nameservers[i]->namelen);
588     idx += ppkt->nameservers[i]->namelen;
589     struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
590
591     r->type = ppkt->nameservers[i]->type;
592     r->class = ppkt->nameservers[i]->class;
593     r->ttl = ppkt->nameservers[i]->ttl;
594     r->data_len = ppkt->nameservers[i]->data_len;
595     idx += sizeof (struct dns_record_line);
596     memcpy (&r->data, ppkt->nameservers[i]->data,
597             ppkt->nameservers[i]->data_len);
598     idx += ppkt->nameservers[i]->data_len;
599   }
600
601   for (i = 0; i < ntohs (ppkt->s.arcount); i++)
602   {
603     unparse_dns_name (&pkt_c[idx], ppkt->additional[i]->name,
604                       ppkt->additional[i]->namelen);
605     idx += ppkt->additional[i]->namelen;
606     struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
607
608     r->type = ppkt->additional[i]->type;
609     r->class = ppkt->additional[i]->class;
610     r->ttl = ppkt->additional[i]->ttl;
611     r->data_len = ppkt->additional[i]->data_len;
612     idx += sizeof (struct dns_record_line);
613     memcpy (&r->data, ppkt->additional[i]->data, ppkt->additional[i]->data_len);
614     idx += ppkt->additional[i]->data_len;
615   }
616
617   return pkt;
618 }
619
620 void
621 free_parsed_dns_packet (struct dns_pkt_parsed *ppkt)
622 {
623   unsigned short qdcount = ntohs (ppkt->s.qdcount);
624   unsigned short ancount = ntohs (ppkt->s.ancount);
625   unsigned short nscount = ntohs (ppkt->s.nscount);
626   unsigned short arcount = ntohs (ppkt->s.arcount);
627
628   int i;
629
630   for (i = 0; i < qdcount; i++)
631   {
632     GNUNET_free (ppkt->queries[i]->name);
633     GNUNET_free (ppkt->queries[i]);
634   }
635   GNUNET_free (ppkt->queries);
636   for (i = 0; i < ancount; i++)
637   {
638     GNUNET_free (ppkt->answers[i]->name);
639     GNUNET_free (ppkt->answers[i]->data);
640     GNUNET_free (ppkt->answers[i]);
641   }
642   GNUNET_free (ppkt->answers);
643   for (i = 0; i < nscount; i++)
644   {
645     GNUNET_free (ppkt->nameservers[i]->name);
646     GNUNET_free (ppkt->nameservers[i]->data);
647     GNUNET_free (ppkt->nameservers[i]);
648   }
649   GNUNET_free (ppkt->nameservers);
650   for (i = 0; i < arcount; i++)
651   {
652     GNUNET_free (ppkt->additional[i]->name);
653     GNUNET_free (ppkt->additional[i]->data);
654     GNUNET_free (ppkt->additional[i]);
655   }
656   GNUNET_free (ppkt->additional);
657   GNUNET_free (ppkt);
658 }