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)
95 if (*off + 1 + len > udp_payload_length)
97 GNUNET_asprintf (&tmp,
101 &udp_payload[*off + 1]);
106 else if ((64 | 128) == (len & (64 | 128)) )
108 /* pointer to string */
109 if (*off + 1 > udp_payload_length)
111 xoff = ((len - (64 | 128)) << 8) + input[*off+1];
112 xstr = parse_name (udp_payload,
115 GNUNET_asprintf (&tmp,
123 /* pointers always terminate names */
128 /* neither pointer nor inline string, not supported... */
133 ret[strlen(ret)-1] = '\0'; /* eat tailing '.' */
142 * Parse a DNS query entry.
144 * @param udp_payload entire UDP payload
145 * @param udp_payload_length length of udp_payload
146 * @param off pointer to the offset of the query to parse in the udp_payload (to be
147 * incremented by the size of the query)
148 * @param q where to write the query information
149 * @return GNUNET_OK on success, GNUNET_SYSERR if the query is malformed
152 parse_query (const char *udp_payload,
153 size_t udp_payload_length,
155 struct GNUNET_DNSPARSER_Query *q)
158 struct query_line ql;
160 name = parse_name (udp_payload,
164 return GNUNET_SYSERR;
166 if (*off + sizeof (struct query_line) > udp_payload_length)
167 return GNUNET_SYSERR;
168 memcpy (&ql, &udp_payload[*off], sizeof (ql));
170 q->type = ntohs (ql.type);
171 q->class = ntohs (ql.class);
177 * Parse a DNS record entry.
179 * @param udp_payload entire UDP payload
180 * @param udp_payload_length length of udp_payload
181 * @param off pointer to the offset of the record to parse in the udp_payload (to be
182 * incremented by the size of the record)
183 * @param r where to write the record information
184 * @return GNUNET_OK on success, GNUNET_SYSERR if the record is malformed
187 parse_record (const char *udp_payload,
188 size_t udp_payload_length,
190 struct GNUNET_DNSPARSER_Record *r)
193 struct record_line rl;
195 name = parse_name (udp_payload,
199 return GNUNET_SYSERR;
201 if (*off + sizeof (struct record_line) > udp_payload_length)
202 return GNUNET_SYSERR;
203 memcpy (&rl, &udp_payload[*off], sizeof (rl));
205 r->type = ntohs (rl.type);
206 r->class = ntohs (rl.class);
207 r->expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
209 r->data_len = ntohs (rl.data_len);
210 if (*off + r->data_len > udp_payload_length)
211 return GNUNET_SYSERR;
212 if (0 == r->data_len)
214 r->data = GNUNET_malloc (r->data_len);
215 memcpy (r->data, &udp_payload[*off], r->data_len);
222 * Parse a UDP payload of a DNS packet in to a nice struct for further
223 * processing and manipulation.
225 * @param udp_payload wire-format of the DNS packet
226 * @param udp_payload_length number of bytes in udp_payload
227 * @return NULL on error, otherwise the parsed packet
229 struct GNUNET_DNSPARSER_Packet *
230 GNUNET_DNSPARSER_parse (const char *udp_payload,
231 size_t udp_payload_length)
233 struct GNUNET_DNSPARSER_Packet *p;
234 const struct dns_header *dns;
239 if (udp_payload_length < sizeof (struct dns_header))
241 dns = (const struct dns_header *) udp_payload;
242 off = sizeof (struct dns_header);
243 p = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_Packet));
244 p->flags = dns->flags;
246 n = ntohs (dns->query_count);
249 p->queries = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Query));
253 parse_query (udp_payload,
259 n = ntohs (dns->answer_rcount);
262 p->answers = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
266 parse_record (udp_payload,
272 n = ntohs (dns->authority_rcount);
275 p->authority_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
276 p->num_authority_records = n;
279 parse_record (udp_payload,
282 &p->authority_records[i]))
285 n = ntohs (dns->additional_rcount);
288 p->additional_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
289 p->num_additional_records = n;
292 parse_record (udp_payload,
295 &p->additional_records[i]))
300 GNUNET_DNSPARSER_free_packet (p);
306 * Free memory taken by a packet.
308 * @param p packet to free
311 GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
315 for (i=0;i<p->num_queries;i++)
316 GNUNET_free_non_null (p->queries[i].name);
317 GNUNET_free_non_null (p->queries);
318 for (i=0;i<p->num_answers;i++)
320 GNUNET_free_non_null (p->answers[i].name);
321 GNUNET_free_non_null (p->answers[i].data);
323 GNUNET_free_non_null (p->answers);
324 for (i=0;i<p->num_authority_records;i++)
326 GNUNET_free_non_null (p->authority_records[i].name);
327 GNUNET_free_non_null (p->authority_records[i].data);
329 GNUNET_free_non_null (p->authority_records);
330 for (i=0;i<p->num_additional_records;i++)
332 GNUNET_free_non_null (p->additional_records[i].name);
333 GNUNET_free_non_null (p->additional_records[i].data);
335 GNUNET_free_non_null (p->additional_records);
341 * Given a DNS packet, generate the corresponding UDP payload.
343 * @param p packet to pack
344 * @param buf set to a buffer with the packed message
345 * @param buf_length set to the length of buf
346 * @return GNUNET_SYSERR if 'p' is invalid
347 * GNUNET_NO if 'p' was truncated (but there is still a result in 'buf')
348 * GNUNET_OK if 'p' was packed completely into '*buf'
351 GNUNET_DNSPARSER_pack (struct GNUNET_DNSPARSER_Packet *p,
355 // FIXME: not implemented
357 return GNUNET_SYSERR;
363 /* legacy code follows */
366 * Parse a name from DNS to a normal .-delimited, 0-terminated string.
368 * @param d The destination of the name. Should have at least 255 bytes allocated.
369 * @param src The DNS-Packet
370 * @param idx The offset inside the Packet from which on the name should be read
371 * @returns The offset of the first unparsed byte (the byte right behind the name)
374 parse_dns_name (char *d, const unsigned char *src, unsigned short idx)
378 int len = src[idx++];
383 { /* Compressed name, offset in this and the next octet */
384 unsigned short offset = ((len & 0x3F) << 8) | src[idx++];
386 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 */
389 memcpy (dest, src + idx, len);
404 * Parse a complete DNS-Record from raw DNS-data to a struct dns_record
406 * @param data The DNS-data
407 * @param dst Pointer to count pointers; individual pointers will be allocated
408 * @param count Number of records to parse
409 * @param idx The offset inside the Packet from which on the name should be read
410 * @returns The offset of the first unparsed byte (the byte right behind the last record)
412 static unsigned short
413 parse_dns_record (unsigned char *data, /*{{{ */
414 struct dns_record **dst, unsigned short count,
420 for (i = 0; i < count; i++)
422 dst[i] = GNUNET_malloc (sizeof (struct dns_record));
423 dst[i]->name = alloca (255); // see RFC1035, no name can be longer than this.
424 char *name = dst[i]->name;
426 _idx = parse_dns_name (name, data, idx);
427 dst[i]->namelen = _idx - idx;
429 dst[i]->name = GNUNET_malloc (dst[i]->namelen);
430 memcpy (dst[i]->name, name, dst[i]->namelen);
434 dst[i]->type = *((unsigned short *) (data + idx));
436 dst[i]->class = *((unsigned short *) (data + idx));
438 dst[i]->ttl = *((unsigned int *) (data + idx));
440 dst[i]->data_len = *((unsigned short *) (data + idx));
442 dst[i]->data = GNUNET_malloc (ntohs (dst[i]->data_len));
443 memcpy (dst[i]->data, data + idx, ntohs (dst[i]->data_len));
444 idx += ntohs (dst[i]->data_len);
450 * Parse a raw DNS-Packet into an usable struct
452 struct dns_pkt_parsed *
453 parse_dns_packet (struct dns_pkt *pkt)
455 struct dns_pkt_parsed *ppkt = GNUNET_malloc (sizeof (struct dns_pkt_parsed));
457 memcpy (&ppkt->s, &pkt->s, sizeof pkt->s);
459 unsigned short qdcount = ntohs (ppkt->s.qdcount);
460 unsigned short ancount = ntohs (ppkt->s.ancount);
461 unsigned short nscount = ntohs (ppkt->s.nscount);
462 unsigned short arcount = ntohs (ppkt->s.arcount);
464 ppkt->queries = GNUNET_malloc (qdcount * sizeof (struct dns_query *));
465 ppkt->answers = GNUNET_malloc (ancount * sizeof (struct dns_record *));
466 ppkt->nameservers = GNUNET_malloc (nscount * sizeof (struct dns_record *));
467 ppkt->additional = GNUNET_malloc (arcount * sizeof (struct dns_record *));
469 unsigned short idx = 0, _idx; /* This keeps track how far we have parsed the data */
471 /* Parse the Query */
474 for (i = 0; i < qdcount; i++)
476 ppkt->queries[i] = GNUNET_malloc (sizeof (struct dns_query));
477 char *name = alloca (255); /* see RFC1035, it can't be more than this. */
479 _idx = parse_dns_name (name, pkt->data, idx);
480 ppkt->queries[i]->namelen = _idx - idx;
483 ppkt->queries[i]->name = GNUNET_malloc (ppkt->queries[i]->namelen);
484 memcpy (ppkt->queries[i]->name, name, ppkt->queries[i]->namelen);
486 ppkt->queries[i]->qtype = *((unsigned short *) (pkt->data + idx));
488 ppkt->queries[i]->qclass = *((unsigned short *) (pkt->data + idx));
492 idx = parse_dns_record (pkt->data, ppkt->answers, ancount, idx);
493 idx = parse_dns_record (pkt->data, ppkt->nameservers, nscount, idx);
494 idx = parse_dns_record (pkt->data, ppkt->additional, arcount, idx);
499 unparse_dns_name (char *dest, char *src, size_t len)
507 while (*src != '.' && *src != 0)
524 unparse_dns_packet (struct dns_pkt_parsed *ppkt)
526 size_t size = sizeof (struct dns_pkt) - 1;
529 for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
530 size += ppkt->queries[i]->namelen + 1;
532 for (i = 0; i < ntohs (ppkt->s.ancount); i++)
534 size += ppkt->answers[i]->namelen + 1;
535 size += ppkt->answers[i]->data_len;
537 for (i = 0; i < ntohs (ppkt->s.nscount); i++)
539 size += ppkt->nameservers[i]->namelen + 1;
540 size += ppkt->nameservers[i]->data_len;
542 for (i = 0; i < ntohs (ppkt->s.arcount); i++)
544 size += ppkt->additional[i]->namelen + 1;
545 size += ppkt->additional[i]->data_len;
549 4 * ntohs (ppkt->s.qdcount) + 10 * (ntohs (ppkt->s.ancount) +
550 ntohs (ppkt->s.arcount) +
551 ntohs (ppkt->s.nscount));
553 struct dns_pkt *pkt = GNUNET_malloc (size);
554 char *pkt_c = (char *) pkt;
556 memcpy (&pkt->s, &ppkt->s, sizeof ppkt->s);
557 size_t idx = sizeof ppkt->s;
559 for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
561 unparse_dns_name (&pkt_c[idx], ppkt->queries[i]->name,
562 ppkt->queries[i]->namelen);
563 idx += ppkt->queries[i]->namelen;
564 struct dns_query_line *d = (struct dns_query_line *) &pkt_c[idx];
566 d->class = ppkt->queries[i]->qclass;
567 d->type = ppkt->queries[i]->qtype;
568 idx += sizeof (struct dns_query_line);
571 for (i = 0; i < ntohs (ppkt->s.ancount); i++)
573 unparse_dns_name (&pkt_c[idx], ppkt->answers[i]->name,
574 ppkt->answers[i]->namelen);
575 idx += ppkt->answers[i]->namelen;
576 struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
578 r->type = ppkt->answers[i]->type;
579 r->class = ppkt->answers[i]->class;
580 r->ttl = ppkt->answers[i]->ttl;
581 r->data_len = ppkt->answers[i]->data_len;
582 idx += sizeof (struct dns_record_line);
583 memcpy (&r->data, ppkt->answers[i]->data, ppkt->answers[i]->data_len);
584 idx += ppkt->answers[i]->data_len;
587 for (i = 0; i < ntohs (ppkt->s.nscount); i++)
589 unparse_dns_name (&pkt_c[idx], ppkt->nameservers[i]->name,
590 ppkt->nameservers[i]->namelen);
591 idx += ppkt->nameservers[i]->namelen;
592 struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
594 r->type = ppkt->nameservers[i]->type;
595 r->class = ppkt->nameservers[i]->class;
596 r->ttl = ppkt->nameservers[i]->ttl;
597 r->data_len = ppkt->nameservers[i]->data_len;
598 idx += sizeof (struct dns_record_line);
599 memcpy (&r->data, ppkt->nameservers[i]->data,
600 ppkt->nameservers[i]->data_len);
601 idx += ppkt->nameservers[i]->data_len;
604 for (i = 0; i < ntohs (ppkt->s.arcount); i++)
606 unparse_dns_name (&pkt_c[idx], ppkt->additional[i]->name,
607 ppkt->additional[i]->namelen);
608 idx += ppkt->additional[i]->namelen;
609 struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
611 r->type = ppkt->additional[i]->type;
612 r->class = ppkt->additional[i]->class;
613 r->ttl = ppkt->additional[i]->ttl;
614 r->data_len = ppkt->additional[i]->data_len;
615 idx += sizeof (struct dns_record_line);
616 memcpy (&r->data, ppkt->additional[i]->data, ppkt->additional[i]->data_len);
617 idx += ppkt->additional[i]->data_len;
624 free_parsed_dns_packet (struct dns_pkt_parsed *ppkt)
626 unsigned short qdcount = ntohs (ppkt->s.qdcount);
627 unsigned short ancount = ntohs (ppkt->s.ancount);
628 unsigned short nscount = ntohs (ppkt->s.nscount);
629 unsigned short arcount = ntohs (ppkt->s.arcount);
633 for (i = 0; i < qdcount; i++)
635 GNUNET_free (ppkt->queries[i]->name);
636 GNUNET_free (ppkt->queries[i]);
638 GNUNET_free (ppkt->queries);
639 for (i = 0; i < ancount; i++)
641 GNUNET_free (ppkt->answers[i]->name);
642 GNUNET_free (ppkt->answers[i]->data);
643 GNUNET_free (ppkt->answers[i]);
645 GNUNET_free (ppkt->answers);
646 for (i = 0; i < nscount; i++)
648 GNUNET_free (ppkt->nameservers[i]->name);
649 GNUNET_free (ppkt->nameservers[i]->data);
650 GNUNET_free (ppkt->nameservers[i]);
652 GNUNET_free (ppkt->nameservers);
653 for (i = 0; i < arcount; i++)
655 GNUNET_free (ppkt->additional[i]->name);
656 GNUNET_free (ppkt->additional[i]->data);
657 GNUNET_free (ppkt->additional[i]);
659 GNUNET_free (ppkt->additional);