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 * @return name as 0-terminated C string on success, NULL if the payload is malformed
80 parse_name (const char *udp_payload,
81 size_t udp_payload_length,
84 const uint8_t *input = (const uint8_t *) udp_payload;
91 ret = GNUNET_strdup ("");
94 if (*off >= udp_payload_length)
104 if (*off + 1 + len > udp_payload_length)
106 GNUNET_asprintf (&tmp,
110 &udp_payload[*off + 1]);
115 else if ((64 | 128) == (len & (64 | 128)) )
117 /* pointer to string */
118 if (*off + 1 > udp_payload_length)
120 xoff = ((len - (64 | 128)) << 8) + input[*off+1];
121 xstr = parse_name (udp_payload,
124 GNUNET_asprintf (&tmp,
132 /* pointers always terminate names */
137 /* neither pointer nor inline string, not supported... */
142 ret[strlen(ret)-1] = '\0'; /* eat tailing '.' */
151 * Parse a DNS query entry.
153 * @param udp_payload entire UDP payload
154 * @param udp_payload_length length of udp_payload
155 * @param off pointer to the offset of the query to parse in the udp_payload (to be
156 * incremented by the size of the query)
157 * @param q where to write the query information
158 * @return GNUNET_OK on success, GNUNET_SYSERR if the query is malformed
161 parse_query (const char *udp_payload,
162 size_t udp_payload_length,
164 struct GNUNET_DNSPARSER_Query *q)
167 struct query_line ql;
169 name = parse_name (udp_payload,
173 return GNUNET_SYSERR;
175 if (*off + sizeof (struct query_line) > udp_payload_length)
176 return GNUNET_SYSERR;
177 memcpy (&ql, &udp_payload[*off], sizeof (ql));
179 q->type = ntohs (ql.type);
180 q->class = ntohs (ql.class);
186 * Parse a DNS record entry.
188 * @param udp_payload entire UDP payload
189 * @param udp_payload_length length of udp_payload
190 * @param off pointer to the offset of the record to parse in the udp_payload (to be
191 * incremented by the size of the record)
192 * @param r where to write the record information
193 * @return GNUNET_OK on success, GNUNET_SYSERR if the record is malformed
196 parse_record (const char *udp_payload,
197 size_t udp_payload_length,
199 struct GNUNET_DNSPARSER_Record *r)
202 struct record_line rl;
207 name = parse_name (udp_payload,
211 return GNUNET_SYSERR;
213 if (*off + sizeof (struct record_line) > udp_payload_length)
214 return GNUNET_SYSERR;
215 memcpy (&rl, &udp_payload[*off], sizeof (rl));
216 (*off) += sizeof (rl);
217 r->type = ntohs (rl.type);
218 r->class = ntohs (rl.class);
219 r->expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
221 r->data_len = ntohs (rl.data_len);
222 if (*off + r->data_len > udp_payload_length)
223 return GNUNET_SYSERR;
224 if (0 == r->data_len)
228 case GNUNET_DNSPARSER_TYPE_NS:
229 case GNUNET_DNSPARSER_TYPE_CNAME:
230 case GNUNET_DNSPARSER_TYPE_PTR:
232 r->data.hostname = parse_name (udp_payload,
235 if ( (NULL == r->data.hostname) ||
236 (old_off + r->data_len != *off) )
237 return GNUNET_SYSERR;
239 case GNUNET_DNSPARSER_TYPE_SOA:
241 r->data.soa = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_SoaRecord));
242 r->data.soa->mname = parse_name (udp_payload,
245 r->data.soa->rname = parse_name (udp_payload,
248 if ( (NULL == r->data.soa->mname) ||
249 (NULL == r->data.soa->rname) ||
250 (*off + sizeof (soa) > udp_payload_length) )
251 return GNUNET_SYSERR;
252 memcpy (&soa, &udp_payload[*off], sizeof (soa));
253 r->data.soa->serial = ntohl (soa.serial);
254 r->data.soa->refresh = ntohl (soa.refresh);
255 r->data.soa->retry = ntohl (soa.retry);
256 r->data.soa->expire = ntohl (soa.expire);
257 r->data.soa->minimum_ttl = ntohl (soa.minimum);
258 (*off) += sizeof (soa);
259 if (old_off + r->data_len != *off)
260 return GNUNET_SYSERR;
262 case GNUNET_DNSPARSER_TYPE_MX:
264 if (*off + sizeof (uint16_t) > udp_payload_length)
265 return GNUNET_SYSERR;
266 memcpy (&mxpref, &udp_payload[*off], sizeof (uint16_t));
267 (*off) += sizeof (uint16_t);
268 r->data.mx = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_MxRecord));
269 r->data.mx->preference = ntohs (mxpref);
270 r->data.mx->mxhost = parse_name (udp_payload,
273 if (old_off + r->data_len != *off)
274 return GNUNET_SYSERR;
277 r->data.raw = GNUNET_malloc (r->data_len);
278 memcpy (r->data.raw, &udp_payload[*off], r->data_len);
281 (*off) += r->data_len;
287 * Parse a UDP payload of a DNS packet in to a nice struct for further
288 * processing and manipulation.
290 * @param udp_payload wire-format of the DNS packet
291 * @param udp_payload_length number of bytes in udp_payload
292 * @return NULL on error, otherwise the parsed packet
294 struct GNUNET_DNSPARSER_Packet *
295 GNUNET_DNSPARSER_parse (const char *udp_payload,
296 size_t udp_payload_length)
298 struct GNUNET_DNSPARSER_Packet *p;
299 const struct dns_header *dns;
304 if (udp_payload_length < sizeof (struct dns_header))
306 dns = (const struct dns_header *) udp_payload;
307 off = sizeof (struct dns_header);
308 p = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_Packet));
309 p->flags = dns->flags;
311 n = ntohs (dns->query_count);
314 p->queries = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Query));
318 parse_query (udp_payload,
324 n = ntohs (dns->answer_rcount);
327 p->answers = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
331 parse_record (udp_payload,
337 n = ntohs (dns->authority_rcount);
340 p->authority_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
341 p->num_authority_records = n;
344 parse_record (udp_payload,
347 &p->authority_records[i]))
350 n = ntohs (dns->additional_rcount);
353 p->additional_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
354 p->num_additional_records = n;
357 parse_record (udp_payload,
360 &p->additional_records[i]))
365 GNUNET_DNSPARSER_free_packet (p);
371 * Free SOA information record.
373 * @param soa record to free
376 free_soa (struct GNUNET_DNSPARSER_SoaRecord *soa)
380 GNUNET_free_non_null (soa->mname);
381 GNUNET_free_non_null (soa->rname);
387 * Free MX information record.
389 * @param mx record to free
392 free_mx (struct GNUNET_DNSPARSER_MxRecord *mx)
396 GNUNET_free_non_null (mx->mxhost);
402 free_record (struct GNUNET_DNSPARSER_Record *r)
404 GNUNET_free_non_null (r->name);
407 case GNUNET_DNSPARSER_TYPE_MX:
408 free_mx (r->data.mx);
410 case GNUNET_DNSPARSER_TYPE_SOA:
411 free_soa (r->data.soa);
413 case GNUNET_DNSPARSER_TYPE_NS:
414 case GNUNET_DNSPARSER_TYPE_CNAME:
415 case GNUNET_DNSPARSER_TYPE_PTR:
416 GNUNET_free_non_null (r->data.hostname);
419 GNUNET_free_non_null (r->data.raw);
426 * Free memory taken by a packet.
428 * @param p packet to free
431 GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
435 for (i=0;i<p->num_queries;i++)
436 GNUNET_free_non_null (p->queries[i].name);
437 GNUNET_free_non_null (p->queries);
438 for (i=0;i<p->num_answers;i++)
439 free_record (&p->answers[i]);
440 GNUNET_free_non_null (p->answers);
441 for (i=0;i<p->num_authority_records;i++)
442 free_record (&p->authority_records[i]);
443 GNUNET_free_non_null (p->authority_records);
444 for (i=0;i<p->num_additional_records;i++)
445 free_record (&p->additional_records[i]);
446 GNUNET_free_non_null (p->additional_records);
452 * Given a DNS packet, generate the corresponding UDP payload.
454 * @param p packet to pack
455 * @param buf set to a buffer with the packed message
456 * @param buf_length set to the length of buf
457 * @return GNUNET_SYSERR if 'p' is invalid
458 * GNUNET_NO if 'p' was truncated (but there is still a result in 'buf')
459 * GNUNET_OK if 'p' was packed completely into '*buf'
462 GNUNET_DNSPARSER_pack (struct GNUNET_DNSPARSER_Packet *p,
466 // FIXME: not implemented
468 return GNUNET_SYSERR;
474 /* legacy code follows */
477 * Parse a name from DNS to a normal .-delimited, 0-terminated string.
479 * @param d The destination of the name. Should have at least 255 bytes allocated.
480 * @param src The DNS-Packet
481 * @param idx The offset inside the Packet from which on the name should be read
482 * @returns The offset of the first unparsed byte (the byte right behind the name)
485 parse_dns_name (char *d, const unsigned char *src, unsigned short idx)
489 int len = src[idx++];
494 { /* Compressed name, offset in this and the next octet */
495 unsigned short offset = ((len & 0x3F) << 8) | src[idx++];
497 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 */
500 memcpy (dest, src + idx, len);
515 * Parse a complete DNS-Record from raw DNS-data to a struct dns_record
517 * @param data The DNS-data
518 * @param dst Pointer to count pointers; individual pointers will be allocated
519 * @param count Number of records to parse
520 * @param idx The offset inside the Packet from which on the name should be read
521 * @returns The offset of the first unparsed byte (the byte right behind the last record)
523 static unsigned short
524 parse_dns_record (unsigned char *data, /*{{{ */
525 struct dns_record **dst, unsigned short count,
531 for (i = 0; i < count; i++)
533 dst[i] = GNUNET_malloc (sizeof (struct dns_record));
534 dst[i]->name = alloca (255); // see RFC1035, no name can be longer than this.
535 char *name = dst[i]->name;
537 _idx = parse_dns_name (name, data, idx);
538 dst[i]->namelen = _idx - idx;
540 dst[i]->name = GNUNET_malloc (dst[i]->namelen);
541 memcpy (dst[i]->name, name, dst[i]->namelen);
545 dst[i]->type = *((unsigned short *) (data + idx));
547 dst[i]->class = *((unsigned short *) (data + idx));
549 dst[i]->ttl = *((unsigned int *) (data + idx));
551 dst[i]->data_len = *((unsigned short *) (data + idx));
553 dst[i]->data = GNUNET_malloc (ntohs (dst[i]->data_len));
554 memcpy (dst[i]->data, data + idx, ntohs (dst[i]->data_len));
555 idx += ntohs (dst[i]->data_len);
561 * Parse a raw DNS-Packet into an usable struct
563 struct dns_pkt_parsed *
564 parse_dns_packet (struct dns_pkt *pkt)
566 struct dns_pkt_parsed *ppkt = GNUNET_malloc (sizeof (struct dns_pkt_parsed));
568 memcpy (&ppkt->s, &pkt->s, sizeof pkt->s);
570 unsigned short qdcount = ntohs (ppkt->s.qdcount);
571 unsigned short ancount = ntohs (ppkt->s.ancount);
572 unsigned short nscount = ntohs (ppkt->s.nscount);
573 unsigned short arcount = ntohs (ppkt->s.arcount);
575 ppkt->queries = GNUNET_malloc (qdcount * sizeof (struct dns_query *));
576 ppkt->answers = GNUNET_malloc (ancount * sizeof (struct dns_record *));
577 ppkt->nameservers = GNUNET_malloc (nscount * sizeof (struct dns_record *));
578 ppkt->additional = GNUNET_malloc (arcount * sizeof (struct dns_record *));
580 unsigned short idx = 0, _idx; /* This keeps track how far we have parsed the data */
582 /* Parse the Query */
585 for (i = 0; i < qdcount; i++)
587 ppkt->queries[i] = GNUNET_malloc (sizeof (struct dns_query));
588 char *name = alloca (255); /* see RFC1035, it can't be more than this. */
590 _idx = parse_dns_name (name, pkt->data, idx);
591 ppkt->queries[i]->namelen = _idx - idx;
594 ppkt->queries[i]->name = GNUNET_malloc (ppkt->queries[i]->namelen);
595 memcpy (ppkt->queries[i]->name, name, ppkt->queries[i]->namelen);
597 ppkt->queries[i]->qtype = *((unsigned short *) (pkt->data + idx));
599 ppkt->queries[i]->qclass = *((unsigned short *) (pkt->data + idx));
603 idx = parse_dns_record (pkt->data, ppkt->answers, ancount, idx);
604 idx = parse_dns_record (pkt->data, ppkt->nameservers, nscount, idx);
605 idx = parse_dns_record (pkt->data, ppkt->additional, arcount, idx);
610 unparse_dns_name (char *dest, char *src, size_t len)
618 while (*src != '.' && *src != 0)
635 unparse_dns_packet (struct dns_pkt_parsed *ppkt)
637 size_t size = sizeof (struct dns_pkt) - 1;
640 for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
641 size += ppkt->queries[i]->namelen + 1;
643 for (i = 0; i < ntohs (ppkt->s.ancount); i++)
645 size += ppkt->answers[i]->namelen + 1;
646 size += ppkt->answers[i]->data_len;
648 for (i = 0; i < ntohs (ppkt->s.nscount); i++)
650 size += ppkt->nameservers[i]->namelen + 1;
651 size += ppkt->nameservers[i]->data_len;
653 for (i = 0; i < ntohs (ppkt->s.arcount); i++)
655 size += ppkt->additional[i]->namelen + 1;
656 size += ppkt->additional[i]->data_len;
660 4 * ntohs (ppkt->s.qdcount) + 10 * (ntohs (ppkt->s.ancount) +
661 ntohs (ppkt->s.arcount) +
662 ntohs (ppkt->s.nscount));
664 struct dns_pkt *pkt = GNUNET_malloc (size);
665 char *pkt_c = (char *) pkt;
667 memcpy (&pkt->s, &ppkt->s, sizeof ppkt->s);
668 size_t idx = sizeof ppkt->s;
670 for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
672 unparse_dns_name (&pkt_c[idx], ppkt->queries[i]->name,
673 ppkt->queries[i]->namelen);
674 idx += ppkt->queries[i]->namelen;
675 struct dns_query_line *d = (struct dns_query_line *) &pkt_c[idx];
677 d->class = ppkt->queries[i]->qclass;
678 d->type = ppkt->queries[i]->qtype;
679 idx += sizeof (struct dns_query_line);
682 for (i = 0; i < ntohs (ppkt->s.ancount); i++)
684 unparse_dns_name (&pkt_c[idx], ppkt->answers[i]->name,
685 ppkt->answers[i]->namelen);
686 idx += ppkt->answers[i]->namelen;
687 struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
689 r->type = ppkt->answers[i]->type;
690 r->class = ppkt->answers[i]->class;
691 r->ttl = ppkt->answers[i]->ttl;
692 r->data_len = ppkt->answers[i]->data_len;
693 idx += sizeof (struct dns_record_line);
694 memcpy (&r->data, ppkt->answers[i]->data, ppkt->answers[i]->data_len);
695 idx += ppkt->answers[i]->data_len;
698 for (i = 0; i < ntohs (ppkt->s.nscount); i++)
700 unparse_dns_name (&pkt_c[idx], ppkt->nameservers[i]->name,
701 ppkt->nameservers[i]->namelen);
702 idx += ppkt->nameservers[i]->namelen;
703 struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
705 r->type = ppkt->nameservers[i]->type;
706 r->class = ppkt->nameservers[i]->class;
707 r->ttl = ppkt->nameservers[i]->ttl;
708 r->data_len = ppkt->nameservers[i]->data_len;
709 idx += sizeof (struct dns_record_line);
710 memcpy (&r->data, ppkt->nameservers[i]->data,
711 ppkt->nameservers[i]->data_len);
712 idx += ppkt->nameservers[i]->data_len;
715 for (i = 0; i < ntohs (ppkt->s.arcount); i++)
717 unparse_dns_name (&pkt_c[idx], ppkt->additional[i]->name,
718 ppkt->additional[i]->namelen);
719 idx += ppkt->additional[i]->namelen;
720 struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
722 r->type = ppkt->additional[i]->type;
723 r->class = ppkt->additional[i]->class;
724 r->ttl = ppkt->additional[i]->ttl;
725 r->data_len = ppkt->additional[i]->data_len;
726 idx += sizeof (struct dns_record_line);
727 memcpy (&r->data, ppkt->additional[i]->data, ppkt->additional[i]->data_len);
728 idx += ppkt->additional[i]->data_len;
735 free_parsed_dns_packet (struct dns_pkt_parsed *ppkt)
737 unsigned short qdcount = ntohs (ppkt->s.qdcount);
738 unsigned short ancount = ntohs (ppkt->s.ancount);
739 unsigned short nscount = ntohs (ppkt->s.nscount);
740 unsigned short arcount = ntohs (ppkt->s.arcount);
744 for (i = 0; i < qdcount; i++)
746 GNUNET_free (ppkt->queries[i]->name);
747 GNUNET_free (ppkt->queries[i]);
749 GNUNET_free (ppkt->queries);
750 for (i = 0; i < ancount; i++)
752 GNUNET_free (ppkt->answers[i]->name);
753 GNUNET_free (ppkt->answers[i]->data);
754 GNUNET_free (ppkt->answers[i]);
756 GNUNET_free (ppkt->answers);
757 for (i = 0; i < nscount; i++)
759 GNUNET_free (ppkt->nameservers[i]->name);
760 GNUNET_free (ppkt->nameservers[i]->data);
761 GNUNET_free (ppkt->nameservers[i]);
763 GNUNET_free (ppkt->nameservers);
764 for (i = 0; i < arcount; i++)
766 GNUNET_free (ppkt->additional[i]->name);
767 GNUNET_free (ppkt->additional[i]->data);
768 GNUNET_free (ppkt->additional[i]);
770 GNUNET_free (ppkt->additional);