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;
58 GNUNET_NETWORK_STRUCT_END
62 * Parse name inside of a DNS query or record.
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
71 parse_name (const char *udp_payload,
72 size_t udp_payload_length,
75 const uint8_t *input = (const uint8_t *) udp_payload;
82 ret = GNUNET_strdup ("");
85 if (*off == udp_payload_length)
92 if (*off + 1 + len > udp_payload_length)
94 GNUNET_asprintf (&tmp,
98 &udp_payload[*off + 1]);
103 else if ((64 | 128) == (len & (64 | 128)) )
105 /* pointer to string */
106 if (*off + 1 > udp_payload_length)
108 xoff = ((len - (64 | 128)) << 8) + input[*off+1];
109 xstr = parse_name (udp_payload,
112 GNUNET_asprintf (&tmp,
120 /* pointers always terminate names */
125 /* neither pointer nor inline string, not supported... */
130 ret[strlen(ret)-1] = '\0'; /* eat tailing '.' */
139 * Parse a DNS query entry.
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
149 parse_query (const char *udp_payload,
150 size_t udp_payload_length,
152 struct GNUNET_DNSPARSER_Query *q)
155 struct query_line ql;
157 name = parse_name (udp_payload,
161 return GNUNET_SYSERR;
163 if (*off + sizeof (struct query_line) > udp_payload_length)
164 return GNUNET_SYSERR;
165 memcpy (&ql, &udp_payload[*off], sizeof (ql));
167 q->type = ntohs (ql.type);
168 q->class = ntohs (ql.class);
174 * Parse a DNS record entry.
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
184 parse_record (const char *udp_payload,
185 size_t udp_payload_length,
187 struct GNUNET_DNSPARSER_Record *r)
190 struct record_line rl;
192 name = parse_name (udp_payload,
196 return GNUNET_SYSERR;
198 if (*off + sizeof (struct record_line) > udp_payload_length)
199 return GNUNET_SYSERR;
200 memcpy (&rl, &udp_payload[*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,
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)
211 r->data = GNUNET_malloc (r->data_len);
212 memcpy (r->data, &udp_payload[*off], r->data_len);
219 * Parse a UDP payload of a DNS packet in to a nice struct for further
220 * processing and manipulation.
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
226 struct GNUNET_DNSPARSER_Packet *
227 GNUNET_DNSPARSER_parse (const char *udp_payload,
228 size_t udp_payload_length)
230 struct GNUNET_DNSPARSER_Packet *p;
231 const struct dns_header *dns;
236 if (udp_payload_length < sizeof (struct dns_header))
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;
243 n = ntohs (dns->query_count);
246 p->queries = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Query));
250 parse_query (udp_payload,
256 n = ntohs (dns->answer_rcount);
259 p->answers = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
263 parse_record (udp_payload,
269 n = ntohs (dns->authority_rcount);
272 p->authority_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
273 p->num_authority_records = n;
276 parse_record (udp_payload,
279 &p->authority_records[i]))
282 n = ntohs (dns->additional_rcount);
285 p->additional_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
286 p->num_additional_records = n;
289 parse_record (udp_payload,
292 &p->additional_records[i]))
297 GNUNET_DNSPARSER_free_packet (p);
303 * Free memory taken by a packet.
305 * @param p packet to free
308 GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
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++)
317 GNUNET_free_non_null (p->answers[i].name);
318 GNUNET_free_non_null (p->answers[i].data);
320 GNUNET_free_non_null (p->answers);
321 for (i=0;i<p->num_authority_records;i++)
323 GNUNET_free_non_null (p->authority_records[i].name);
324 GNUNET_free_non_null (p->authority_records[i].data);
326 GNUNET_free_non_null (p->authority_records);
327 for (i=0;i<p->num_additional_records;i++)
329 GNUNET_free_non_null (p->additional_records[i].name);
330 GNUNET_free_non_null (p->additional_records[i].data);
332 GNUNET_free_non_null (p->additional_records);
338 * Given a DNS packet, generate the corresponding UDP payload.
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'
348 GNUNET_DNSPARSER_pack (struct GNUNET_DNSPARSER_Packet *p,
352 // FIXME: not implemented
354 return GNUNET_SYSERR;
360 /* legacy code follows */
363 * Parse a name from DNS to a normal .-delimited, 0-terminated string.
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)
371 parse_dns_name (char *d, const unsigned char *src, unsigned short idx)
375 int len = src[idx++];
380 { /* Compressed name, offset in this and the next octet */
381 unsigned short offset = ((len & 0x3F) << 8) | src[idx++];
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 */
386 memcpy (dest, src + idx, len);
401 * Parse a complete DNS-Record from raw DNS-data to a struct dns_record
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)
409 static unsigned short
410 parse_dns_record (unsigned char *data, /*{{{ */
411 struct dns_record **dst, unsigned short count,
417 for (i = 0; i < count; i++)
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;
423 _idx = parse_dns_name (name, data, idx);
424 dst[i]->namelen = _idx - idx;
426 dst[i]->name = GNUNET_malloc (dst[i]->namelen);
427 memcpy (dst[i]->name, name, dst[i]->namelen);
431 dst[i]->type = *((unsigned short *) (data + idx));
433 dst[i]->class = *((unsigned short *) (data + idx));
435 dst[i]->ttl = *((unsigned int *) (data + idx));
437 dst[i]->data_len = *((unsigned short *) (data + idx));
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);
447 * Parse a raw DNS-Packet into an usable struct
449 struct dns_pkt_parsed *
450 parse_dns_packet (struct dns_pkt *pkt)
452 struct dns_pkt_parsed *ppkt = GNUNET_malloc (sizeof (struct dns_pkt_parsed));
454 memcpy (&ppkt->s, &pkt->s, sizeof pkt->s);
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);
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 *));
466 unsigned short idx = 0, _idx; /* This keeps track how far we have parsed the data */
468 /* Parse the Query */
471 for (i = 0; i < qdcount; i++)
473 ppkt->queries[i] = GNUNET_malloc (sizeof (struct dns_query));
474 char *name = alloca (255); /* see RFC1035, it can't be more than this. */
476 _idx = parse_dns_name (name, pkt->data, idx);
477 ppkt->queries[i]->namelen = _idx - idx;
480 ppkt->queries[i]->name = GNUNET_malloc (ppkt->queries[i]->namelen);
481 memcpy (ppkt->queries[i]->name, name, ppkt->queries[i]->namelen);
483 ppkt->queries[i]->qtype = *((unsigned short *) (pkt->data + idx));
485 ppkt->queries[i]->qclass = *((unsigned short *) (pkt->data + idx));
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);
496 unparse_dns_name (char *dest, char *src, size_t len)
504 while (*src != '.' && *src != 0)
521 unparse_dns_packet (struct dns_pkt_parsed *ppkt)
523 size_t size = sizeof (struct dns_pkt) - 1;
526 for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
527 size += ppkt->queries[i]->namelen + 1;
529 for (i = 0; i < ntohs (ppkt->s.ancount); i++)
531 size += ppkt->answers[i]->namelen + 1;
532 size += ppkt->answers[i]->data_len;
534 for (i = 0; i < ntohs (ppkt->s.nscount); i++)
536 size += ppkt->nameservers[i]->namelen + 1;
537 size += ppkt->nameservers[i]->data_len;
539 for (i = 0; i < ntohs (ppkt->s.arcount); i++)
541 size += ppkt->additional[i]->namelen + 1;
542 size += ppkt->additional[i]->data_len;
546 4 * ntohs (ppkt->s.qdcount) + 10 * (ntohs (ppkt->s.ancount) +
547 ntohs (ppkt->s.arcount) +
548 ntohs (ppkt->s.nscount));
550 struct dns_pkt *pkt = GNUNET_malloc (size);
551 char *pkt_c = (char *) pkt;
553 memcpy (&pkt->s, &ppkt->s, sizeof ppkt->s);
554 size_t idx = sizeof ppkt->s;
556 for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
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];
563 d->class = ppkt->queries[i]->qclass;
564 d->type = ppkt->queries[i]->qtype;
565 idx += sizeof (struct dns_query_line);
568 for (i = 0; i < ntohs (ppkt->s.ancount); i++)
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];
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;
584 for (i = 0; i < ntohs (ppkt->s.nscount); i++)
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];
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;
601 for (i = 0; i < ntohs (ppkt->s.arcount); i++)
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];
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;
621 free_parsed_dns_packet (struct dns_pkt_parsed *ppkt)
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);
630 for (i = 0; i < qdcount; i++)
632 GNUNET_free (ppkt->queries[i]->name);
633 GNUNET_free (ppkt->queries[i]);
635 GNUNET_free (ppkt->queries);
636 for (i = 0; i < ancount; i++)
638 GNUNET_free (ppkt->answers[i]->name);
639 GNUNET_free (ppkt->answers[i]->data);
640 GNUNET_free (ppkt->answers[i]);
642 GNUNET_free (ppkt->answers);
643 for (i = 0; i < nscount; i++)
645 GNUNET_free (ppkt->nameservers[i]->name);
646 GNUNET_free (ppkt->nameservers[i]->data);
647 GNUNET_free (ppkt->nameservers[i]);
649 GNUNET_free (ppkt->nameservers);
650 for (i = 0; i < arcount; i++)
652 GNUNET_free (ppkt->additional[i]->name);
653 GNUNET_free (ppkt->additional[i]->data);
654 GNUNET_free (ppkt->additional[i]);
656 GNUNET_free (ppkt->additional);