2 This file is part of GNUnet
3 (C) 2010, 2011, 2012 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file dns/dnsparser.c
23 * @brief helper library to parse DNS packets.
24 * @author Philipp Toelke
25 * @author Christian Grothoff
28 #include "gnunet_util_lib.h"
29 #include "gnunet_dnsparser_lib.h"
33 GNUNET_NETWORK_STRUCT_BEGIN
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
46 uint16_t type GNUNET_PACKED;
47 uint16_t class GNUNET_PACKED;
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;
60 uint32_t serial GNUNET_PACKED;
61 uint32_t refresh GNUNET_PACKED;
62 uint32_t retry GNUNET_PACKED;
63 uint32_t expire GNUNET_PACKED;
64 uint32_t minimum GNUNET_PACKED;
67 GNUNET_NETWORK_STRUCT_END
71 * Parse name inside of a DNS query or record.
73 * @param udp_payload entire UDP payload
74 * @param udp_payload_length length of udp_payload
75 * @param off pointer to the offset of the name to parse in the udp_payload (to be
76 * incremented by the size of the name)
77 * @param depth current depth of our recursion (to prevent stack overflow)
78 * @return name as 0-terminated C string on success, NULL if the payload is malformed
81 parse_name (const char *udp_payload,
82 size_t udp_payload_length,
86 const uint8_t *input = (const uint8_t *) udp_payload;
93 ret = GNUNET_strdup ("");
96 if (*off >= udp_payload_length)
106 if (*off + 1 + len > udp_payload_length)
108 GNUNET_asprintf (&tmp,
112 &udp_payload[*off + 1]);
117 else if ((64 | 128) == (len & (64 | 128)) )
120 goto error; /* hard bound on stack to prevent "infinite" recursion, disallow! */
121 /* pointer to string */
122 if (*off + 1 > udp_payload_length)
124 xoff = ((len - (64 | 128)) << 8) + input[*off+1];
125 xstr = parse_name (udp_payload,
131 GNUNET_asprintf (&tmp,
138 if (strlen (ret) > udp_payload_length)
139 goto error; /* we are looping (building an infinite string) */
141 /* pointers always terminate names */
146 /* neither pointer nor inline string, not supported... */
151 ret[strlen(ret)-1] = '\0'; /* eat tailing '.' */
160 * Parse a DNS query entry.
162 * @param udp_payload entire UDP payload
163 * @param udp_payload_length length of udp_payload
164 * @param off pointer to the offset of the query to parse in the udp_payload (to be
165 * incremented by the size of the query)
166 * @param q where to write the query information
167 * @return GNUNET_OK on success, GNUNET_SYSERR if the query is malformed
170 parse_query (const char *udp_payload,
171 size_t udp_payload_length,
173 struct GNUNET_DNSPARSER_Query *q)
176 struct query_line ql;
178 name = parse_name (udp_payload,
182 return GNUNET_SYSERR;
184 if (*off + sizeof (struct query_line) > udp_payload_length)
185 return GNUNET_SYSERR;
186 memcpy (&ql, &udp_payload[*off], sizeof (ql));
188 q->type = ntohs (ql.type);
189 q->class = ntohs (ql.class);
195 * Parse a DNS record entry.
197 * @param udp_payload entire UDP payload
198 * @param udp_payload_length length of udp_payload
199 * @param off pointer to the offset of the record to parse in the udp_payload (to be
200 * incremented by the size of the record)
201 * @param r where to write the record information
202 * @return GNUNET_OK on success, GNUNET_SYSERR if the record is malformed
205 parse_record (const char *udp_payload,
206 size_t udp_payload_length,
208 struct GNUNET_DNSPARSER_Record *r)
211 struct record_line rl;
216 name = parse_name (udp_payload,
220 return GNUNET_SYSERR;
222 if (*off + sizeof (struct record_line) > udp_payload_length)
223 return GNUNET_SYSERR;
224 memcpy (&rl, &udp_payload[*off], sizeof (rl));
225 (*off) += sizeof (rl);
226 r->type = ntohs (rl.type);
227 r->class = ntohs (rl.class);
228 r->expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
230 r->data_len = ntohs (rl.data_len);
231 if (*off + r->data_len > udp_payload_length)
232 return GNUNET_SYSERR;
233 if (0 == r->data_len)
237 case GNUNET_DNSPARSER_TYPE_NS:
238 case GNUNET_DNSPARSER_TYPE_CNAME:
239 case GNUNET_DNSPARSER_TYPE_PTR:
241 r->data.hostname = parse_name (udp_payload,
244 if ( (NULL == r->data.hostname) ||
245 (old_off + r->data_len != *off) )
246 return GNUNET_SYSERR;
248 case GNUNET_DNSPARSER_TYPE_SOA:
250 r->data.soa = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_SoaRecord));
251 r->data.soa->mname = parse_name (udp_payload,
254 r->data.soa->rname = parse_name (udp_payload,
257 if ( (NULL == r->data.soa->mname) ||
258 (NULL == r->data.soa->rname) ||
259 (*off + sizeof (soa) > udp_payload_length) )
260 return GNUNET_SYSERR;
261 memcpy (&soa, &udp_payload[*off], sizeof (soa));
262 r->data.soa->serial = ntohl (soa.serial);
263 r->data.soa->refresh = ntohl (soa.refresh);
264 r->data.soa->retry = ntohl (soa.retry);
265 r->data.soa->expire = ntohl (soa.expire);
266 r->data.soa->minimum_ttl = ntohl (soa.minimum);
267 (*off) += sizeof (soa);
268 if (old_off + r->data_len != *off)
269 return GNUNET_SYSERR;
271 case GNUNET_DNSPARSER_TYPE_MX:
273 if (*off + sizeof (uint16_t) > udp_payload_length)
274 return GNUNET_SYSERR;
275 memcpy (&mxpref, &udp_payload[*off], sizeof (uint16_t));
276 (*off) += sizeof (uint16_t);
277 r->data.mx = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_MxRecord));
278 r->data.mx->preference = ntohs (mxpref);
279 r->data.mx->mxhost = parse_name (udp_payload,
282 if (old_off + r->data_len != *off)
283 return GNUNET_SYSERR;
286 r->data.raw = GNUNET_malloc (r->data_len);
287 memcpy (r->data.raw, &udp_payload[*off], r->data_len);
290 (*off) += r->data_len;
296 * Parse a UDP payload of a DNS packet in to a nice struct for further
297 * processing and manipulation.
299 * @param udp_payload wire-format of the DNS packet
300 * @param udp_payload_length number of bytes in udp_payload
301 * @return NULL on error, otherwise the parsed packet
303 struct GNUNET_DNSPARSER_Packet *
304 GNUNET_DNSPARSER_parse (const char *udp_payload,
305 size_t udp_payload_length)
307 struct GNUNET_DNSPARSER_Packet *p;
308 const struct dns_header *dns;
313 if (udp_payload_length < sizeof (struct dns_header))
315 dns = (const struct dns_header *) udp_payload;
316 off = sizeof (struct dns_header);
317 p = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_Packet));
318 p->flags = dns->flags;
320 n = ntohs (dns->query_count);
323 p->queries = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Query));
327 parse_query (udp_payload,
333 n = ntohs (dns->answer_rcount);
336 p->answers = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
340 parse_record (udp_payload,
346 n = ntohs (dns->authority_rcount);
349 p->authority_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
350 p->num_authority_records = n;
353 parse_record (udp_payload,
356 &p->authority_records[i]))
359 n = ntohs (dns->additional_rcount);
362 p->additional_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
363 p->num_additional_records = n;
366 parse_record (udp_payload,
369 &p->additional_records[i]))
374 GNUNET_DNSPARSER_free_packet (p);
380 * Free SOA information record.
382 * @param soa record to free
385 free_soa (struct GNUNET_DNSPARSER_SoaRecord *soa)
389 GNUNET_free_non_null (soa->mname);
390 GNUNET_free_non_null (soa->rname);
396 * Free MX information record.
398 * @param mx record to free
401 free_mx (struct GNUNET_DNSPARSER_MxRecord *mx)
405 GNUNET_free_non_null (mx->mxhost);
411 free_record (struct GNUNET_DNSPARSER_Record *r)
413 GNUNET_free_non_null (r->name);
416 case GNUNET_DNSPARSER_TYPE_MX:
417 free_mx (r->data.mx);
419 case GNUNET_DNSPARSER_TYPE_SOA:
420 free_soa (r->data.soa);
422 case GNUNET_DNSPARSER_TYPE_NS:
423 case GNUNET_DNSPARSER_TYPE_CNAME:
424 case GNUNET_DNSPARSER_TYPE_PTR:
425 GNUNET_free_non_null (r->data.hostname);
428 GNUNET_free_non_null (r->data.raw);
435 * Free memory taken by a packet.
437 * @param p packet to free
440 GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
444 for (i=0;i<p->num_queries;i++)
445 GNUNET_free_non_null (p->queries[i].name);
446 GNUNET_free_non_null (p->queries);
447 for (i=0;i<p->num_answers;i++)
448 free_record (&p->answers[i]);
449 GNUNET_free_non_null (p->answers);
450 for (i=0;i<p->num_authority_records;i++)
451 free_record (&p->authority_records[i]);
452 GNUNET_free_non_null (p->authority_records);
453 for (i=0;i<p->num_additional_records;i++)
454 free_record (&p->additional_records[i]);
455 GNUNET_free_non_null (p->additional_records);
461 * Given a DNS packet, generate the corresponding UDP payload.
463 * @param p packet to pack
464 * @param buf set to a buffer with the packed message
465 * @param buf_length set to the length of buf
466 * @return GNUNET_SYSERR if 'p' is invalid
467 * GNUNET_NO if 'p' was truncated (but there is still a result in 'buf')
468 * GNUNET_OK if 'p' was packed completely into '*buf'
471 GNUNET_DNSPARSER_pack (struct GNUNET_DNSPARSER_Packet *p,
475 // FIXME: not implemented
477 return GNUNET_SYSERR;
483 /* legacy code follows */
486 * Parse a name from DNS to a normal .-delimited, 0-terminated string.
488 * @param d The destination of the name. Should have at least 255 bytes allocated.
489 * @param src The DNS-Packet
490 * @param idx The offset inside the Packet from which on the name should be read
491 * @returns The offset of the first unparsed byte (the byte right behind the name)
494 parse_dns_name (char *d, const unsigned char *src, unsigned short idx)
498 int len = src[idx++];
503 { /* Compressed name, offset in this and the next octet */
504 unsigned short offset = ((len & 0x3F) << 8) | src[idx++];
506 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 */
509 memcpy (dest, src + idx, len);
524 * Parse a complete DNS-Record from raw DNS-data to a struct dns_record
526 * @param data The DNS-data
527 * @param dst Pointer to count pointers; individual pointers will be allocated
528 * @param count Number of records to parse
529 * @param idx The offset inside the Packet from which on the name should be read
530 * @returns The offset of the first unparsed byte (the byte right behind the last record)
532 static unsigned short
533 parse_dns_record (unsigned char *data, /*{{{ */
534 struct dns_record **dst, unsigned short count,
540 for (i = 0; i < count; i++)
542 dst[i] = GNUNET_malloc (sizeof (struct dns_record));
543 dst[i]->name = alloca (255); // see RFC1035, no name can be longer than this.
544 char *name = dst[i]->name;
546 _idx = parse_dns_name (name, data, idx);
547 dst[i]->namelen = _idx - idx;
549 dst[i]->name = GNUNET_malloc (dst[i]->namelen);
550 memcpy (dst[i]->name, name, dst[i]->namelen);
554 dst[i]->type = *((unsigned short *) (data + idx));
556 dst[i]->class = *((unsigned short *) (data + idx));
558 dst[i]->ttl = *((unsigned int *) (data + idx));
560 dst[i]->data_len = *((unsigned short *) (data + idx));
562 dst[i]->data = GNUNET_malloc (ntohs (dst[i]->data_len));
563 memcpy (dst[i]->data, data + idx, ntohs (dst[i]->data_len));
564 idx += ntohs (dst[i]->data_len);
570 * Parse a raw DNS-Packet into an usable struct
572 struct dns_pkt_parsed *
573 parse_dns_packet (struct dns_pkt *pkt)
575 struct dns_pkt_parsed *ppkt = GNUNET_malloc (sizeof (struct dns_pkt_parsed));
577 memcpy (&ppkt->s, &pkt->s, sizeof pkt->s);
579 unsigned short qdcount = ntohs (ppkt->s.qdcount);
580 unsigned short ancount = ntohs (ppkt->s.ancount);
581 unsigned short nscount = ntohs (ppkt->s.nscount);
582 unsigned short arcount = ntohs (ppkt->s.arcount);
584 ppkt->queries = GNUNET_malloc (qdcount * sizeof (struct dns_query *));
585 ppkt->answers = GNUNET_malloc (ancount * sizeof (struct dns_record *));
586 ppkt->nameservers = GNUNET_malloc (nscount * sizeof (struct dns_record *));
587 ppkt->additional = GNUNET_malloc (arcount * sizeof (struct dns_record *));
589 unsigned short idx = 0, _idx; /* This keeps track how far we have parsed the data */
591 /* Parse the Query */
594 for (i = 0; i < qdcount; i++)
596 ppkt->queries[i] = GNUNET_malloc (sizeof (struct dns_query));
597 char *name = alloca (255); /* see RFC1035, it can't be more than this. */
599 _idx = parse_dns_name (name, pkt->data, idx);
600 ppkt->queries[i]->namelen = _idx - idx;
603 ppkt->queries[i]->name = GNUNET_malloc (ppkt->queries[i]->namelen);
604 memcpy (ppkt->queries[i]->name, name, ppkt->queries[i]->namelen);
606 ppkt->queries[i]->qtype = *((unsigned short *) (pkt->data + idx));
608 ppkt->queries[i]->qclass = *((unsigned short *) (pkt->data + idx));
612 idx = parse_dns_record (pkt->data, ppkt->answers, ancount, idx);
613 idx = parse_dns_record (pkt->data, ppkt->nameservers, nscount, idx);
614 idx = parse_dns_record (pkt->data, ppkt->additional, arcount, idx);
619 unparse_dns_name (char *dest, char *src, size_t len)
627 while (*src != '.' && *src != 0)
644 unparse_dns_packet (struct dns_pkt_parsed *ppkt)
646 size_t size = sizeof (struct dns_pkt) - 1;
649 for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
650 size += ppkt->queries[i]->namelen + 1;
652 for (i = 0; i < ntohs (ppkt->s.ancount); i++)
654 size += ppkt->answers[i]->namelen + 1;
655 size += ppkt->answers[i]->data_len;
657 for (i = 0; i < ntohs (ppkt->s.nscount); i++)
659 size += ppkt->nameservers[i]->namelen + 1;
660 size += ppkt->nameservers[i]->data_len;
662 for (i = 0; i < ntohs (ppkt->s.arcount); i++)
664 size += ppkt->additional[i]->namelen + 1;
665 size += ppkt->additional[i]->data_len;
669 4 * ntohs (ppkt->s.qdcount) + 10 * (ntohs (ppkt->s.ancount) +
670 ntohs (ppkt->s.arcount) +
671 ntohs (ppkt->s.nscount));
673 struct dns_pkt *pkt = GNUNET_malloc (size);
674 char *pkt_c = (char *) pkt;
676 memcpy (&pkt->s, &ppkt->s, sizeof ppkt->s);
677 size_t idx = sizeof ppkt->s;
679 for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
681 unparse_dns_name (&pkt_c[idx], ppkt->queries[i]->name,
682 ppkt->queries[i]->namelen);
683 idx += ppkt->queries[i]->namelen;
684 struct dns_query_line *d = (struct dns_query_line *) &pkt_c[idx];
686 d->class = ppkt->queries[i]->qclass;
687 d->type = ppkt->queries[i]->qtype;
688 idx += sizeof (struct dns_query_line);
691 for (i = 0; i < ntohs (ppkt->s.ancount); i++)
693 unparse_dns_name (&pkt_c[idx], ppkt->answers[i]->name,
694 ppkt->answers[i]->namelen);
695 idx += ppkt->answers[i]->namelen;
696 struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
698 r->type = ppkt->answers[i]->type;
699 r->class = ppkt->answers[i]->class;
700 r->ttl = ppkt->answers[i]->ttl;
701 r->data_len = ppkt->answers[i]->data_len;
702 idx += sizeof (struct dns_record_line);
703 memcpy (&r->data, ppkt->answers[i]->data, ppkt->answers[i]->data_len);
704 idx += ppkt->answers[i]->data_len;
707 for (i = 0; i < ntohs (ppkt->s.nscount); i++)
709 unparse_dns_name (&pkt_c[idx], ppkt->nameservers[i]->name,
710 ppkt->nameservers[i]->namelen);
711 idx += ppkt->nameservers[i]->namelen;
712 struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
714 r->type = ppkt->nameservers[i]->type;
715 r->class = ppkt->nameservers[i]->class;
716 r->ttl = ppkt->nameservers[i]->ttl;
717 r->data_len = ppkt->nameservers[i]->data_len;
718 idx += sizeof (struct dns_record_line);
719 memcpy (&r->data, ppkt->nameservers[i]->data,
720 ppkt->nameservers[i]->data_len);
721 idx += ppkt->nameservers[i]->data_len;
724 for (i = 0; i < ntohs (ppkt->s.arcount); i++)
726 unparse_dns_name (&pkt_c[idx], ppkt->additional[i]->name,
727 ppkt->additional[i]->namelen);
728 idx += ppkt->additional[i]->namelen;
729 struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
731 r->type = ppkt->additional[i]->type;
732 r->class = ppkt->additional[i]->class;
733 r->ttl = ppkt->additional[i]->ttl;
734 r->data_len = ppkt->additional[i]->data_len;
735 idx += sizeof (struct dns_record_line);
736 memcpy (&r->data, ppkt->additional[i]->data, ppkt->additional[i]->data_len);
737 idx += ppkt->additional[i]->data_len;
744 free_parsed_dns_packet (struct dns_pkt_parsed *ppkt)
746 unsigned short qdcount = ntohs (ppkt->s.qdcount);
747 unsigned short ancount = ntohs (ppkt->s.ancount);
748 unsigned short nscount = ntohs (ppkt->s.nscount);
749 unsigned short arcount = ntohs (ppkt->s.arcount);
753 for (i = 0; i < qdcount; i++)
755 GNUNET_free (ppkt->queries[i]->name);
756 GNUNET_free (ppkt->queries[i]);
758 GNUNET_free (ppkt->queries);
759 for (i = 0; i < ancount; i++)
761 GNUNET_free (ppkt->answers[i]->name);
762 GNUNET_free (ppkt->answers[i]->data);
763 GNUNET_free (ppkt->answers[i]);
765 GNUNET_free (ppkt->answers);
766 for (i = 0; i < nscount; i++)
768 GNUNET_free (ppkt->nameservers[i]->name);
769 GNUNET_free (ppkt->nameservers[i]->data);
770 GNUNET_free (ppkt->nameservers[i]);
772 GNUNET_free (ppkt->nameservers);
773 for (i = 0; i < arcount; i++)
775 GNUNET_free (ppkt->additional[i]->name);
776 GNUNET_free (ppkt->additional[i]->data);
777 GNUNET_free (ppkt->additional[i]);
779 GNUNET_free (ppkt->additional);