aboutsummaryrefslogtreecommitdiff
path: root/src/util/dnsparser.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/dnsparser.c')
-rw-r--r--src/util/dnsparser.c1334
1 files changed, 1334 insertions, 0 deletions
diff --git a/src/util/dnsparser.c b/src/util/dnsparser.c
new file mode 100644
index 000000000..32ad7c0c2
--- /dev/null
+++ b/src/util/dnsparser.c
@@ -0,0 +1,1334 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2010-2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/**
20 * @file dns/dnsparser.c
21 * @brief helper library to parse DNS packets.
22 * @author Philipp Toelke
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include <idna.h>
27#if WINDOWS
28#include <idn-free.h>
29#endif
30#include "gnunet_util_lib.h"
31#include "gnunet_dnsparser_lib.h"
32#include "gnunet_tun_lib.h"
33
34
35/**
36 * Check if a label in UTF-8 format can be coded into valid IDNA.
37 * This can fail if the ASCII-conversion becomes longer than 63 characters.
38 *
39 * @param label label to check (UTF-8 string)
40 * @return #GNUNET_OK if the label can be converted to IDNA,
41 * #GNUNET_SYSERR if the label is not valid for DNS names
42 */
43int
44GNUNET_DNSPARSER_check_label (const char *label)
45{
46 char *output;
47 size_t slen;
48
49 if (NULL != strchr (label, '.'))
50 return GNUNET_SYSERR; /* not a label! Did you mean GNUNET_DNSPARSER_check_name? */
51 if (IDNA_SUCCESS !=
52 idna_to_ascii_8z (label, &output, IDNA_ALLOW_UNASSIGNED))
53 return GNUNET_SYSERR;
54 slen = strlen (output);
55#if WINDOWS
56 idn_free (output);
57#else
58 free (output);
59#endif
60 return (slen > 63) ? GNUNET_SYSERR : GNUNET_OK;
61}
62
63
64/**
65 * Check if a label in UTF-8 format can be coded into valid IDNA.
66 * This can fail if the ASCII-conversion becomes longer than 253 characters.
67 *
68 * @param name name to check (UTF-8 string)
69 * @return #GNUNET_OK if the label can be converted to IDNA,
70 * #GNUNET_SYSERR if the label is not valid for DNS names
71 */
72int
73GNUNET_DNSPARSER_check_name (const char *name)
74{
75 char *ldup;
76 char *output;
77 size_t slen;
78 char *tok;
79
80 ldup = GNUNET_strdup (name);
81 for (tok = strtok (ldup, "."); NULL != tok; tok = strtok (NULL, "."))
82 if (GNUNET_OK !=
83 GNUNET_DNSPARSER_check_label (tok))
84 {
85 GNUNET_free (ldup);
86 return GNUNET_SYSERR;
87 }
88 GNUNET_free (ldup);
89 if (IDNA_SUCCESS !=
90 idna_to_ascii_8z (name, &output, IDNA_ALLOW_UNASSIGNED))
91 return GNUNET_SYSERR;
92 slen = strlen (output);
93#if WINDOWS
94 idn_free (output);
95#else
96 free (output);
97#endif
98 return (slen > 253) ? GNUNET_SYSERR : GNUNET_OK;
99}
100
101
102/**
103 * Free SOA information record.
104 *
105 * @param soa record to free
106 */
107void
108GNUNET_DNSPARSER_free_soa (struct GNUNET_DNSPARSER_SoaRecord *soa)
109{
110 if (NULL == soa)
111 return;
112 GNUNET_free_non_null (soa->mname);
113 GNUNET_free_non_null (soa->rname);
114 GNUNET_free (soa);
115}
116
117
118/**
119 * Free CERT information record.
120 *
121 * @param cert record to free
122 */
123void
124GNUNET_DNSPARSER_free_cert (struct GNUNET_DNSPARSER_CertRecord *cert)
125{
126 if (NULL == cert)
127 return;
128 GNUNET_free_non_null (cert->certificate_data);
129 GNUNET_free (cert);
130}
131
132
133/**
134 * Free SRV information record.
135 *
136 * @param srv record to free
137 */
138void
139GNUNET_DNSPARSER_free_srv (struct GNUNET_DNSPARSER_SrvRecord *srv)
140{
141 if (NULL == srv)
142 return;
143 GNUNET_free_non_null (srv->target);
144 GNUNET_free (srv);
145}
146
147
148/**
149 * Free MX information record.
150 *
151 * @param mx record to free
152 */
153void
154GNUNET_DNSPARSER_free_mx (struct GNUNET_DNSPARSER_MxRecord *mx)
155{
156 if (NULL == mx)
157 return;
158 GNUNET_free_non_null (mx->mxhost);
159 GNUNET_free (mx);
160}
161
162
163/**
164 * Free the given DNS record.
165 *
166 * @param r record to free
167 */
168void
169GNUNET_DNSPARSER_free_record (struct GNUNET_DNSPARSER_Record *r)
170{
171 GNUNET_free_non_null (r->name);
172 switch (r->type)
173 {
174 case GNUNET_DNSPARSER_TYPE_MX:
175 GNUNET_DNSPARSER_free_mx (r->data.mx);
176 break;
177 case GNUNET_DNSPARSER_TYPE_SOA:
178 GNUNET_DNSPARSER_free_soa (r->data.soa);
179 break;
180 case GNUNET_DNSPARSER_TYPE_SRV:
181 GNUNET_DNSPARSER_free_srv (r->data.srv);
182 break;
183 case GNUNET_DNSPARSER_TYPE_CERT:
184 GNUNET_DNSPARSER_free_cert (r->data.cert);
185 break;
186 case GNUNET_DNSPARSER_TYPE_NS:
187 case GNUNET_DNSPARSER_TYPE_CNAME:
188 case GNUNET_DNSPARSER_TYPE_PTR:
189 GNUNET_free_non_null (r->data.hostname);
190 break;
191 default:
192 GNUNET_free_non_null (r->data.raw.data);
193 break;
194 }
195}
196
197
198/**
199 * Parse name inside of a DNS query or record.
200 *
201 * @param udp_payload entire UDP payload
202 * @param udp_payload_length length of @a udp_payload
203 * @param off pointer to the offset of the name to parse in the udp_payload (to be
204 * incremented by the size of the name)
205 * @param depth current depth of our recursion (to prevent stack overflow)
206 * @return name as 0-terminated C string on success, NULL if the payload is malformed
207 */
208static char *
209parse_name (const char *udp_payload,
210 size_t udp_payload_length,
211 size_t *off,
212 unsigned int depth)
213{
214 const uint8_t *input = (const uint8_t *) udp_payload;
215 char *ret;
216 char *tmp;
217 char *xstr;
218 uint8_t len;
219 size_t xoff;
220 char *utf8;
221 Idna_rc rc;
222
223 ret = GNUNET_strdup ("");
224 while (1)
225 {
226 if (*off >= udp_payload_length)
227 {
228 GNUNET_break_op (0);
229 goto error;
230 }
231 len = input[*off];
232 if (0 == len)
233 {
234 (*off)++;
235 break;
236 }
237 if (len < 64)
238 {
239 if (*off + 1 + len > udp_payload_length)
240 {
241 GNUNET_break_op (0);
242 goto error;
243 }
244 GNUNET_asprintf (&tmp,
245 "%.*s",
246 (int) len,
247 &udp_payload[*off + 1]);
248 if (IDNA_SUCCESS !=
249 (rc = idna_to_unicode_8z8z (tmp, &utf8, IDNA_ALLOW_UNASSIGNED)))
250 {
251 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
252 _("Failed to convert DNS IDNA name `%s' to UTF-8: %s\n"),
253 tmp,
254 idna_strerror (rc));
255 GNUNET_free (tmp);
256 GNUNET_asprintf (&tmp,
257 "%s%.*s.",
258 ret,
259 (int) len,
260 &udp_payload[*off + 1]);
261 }
262 else
263 {
264 GNUNET_free (tmp);
265 GNUNET_asprintf (&tmp,
266 "%s%s.",
267 ret,
268 utf8);
269#if WINDOWS
270 idn_free (utf8);
271#else
272 free (utf8);
273#endif
274 }
275 GNUNET_free (ret);
276 ret = tmp;
277 *off += 1 + len;
278 }
279 else if ((64 | 128) == (len & (64 | 128)) )
280 {
281 if (depth > 32)
282 {
283 GNUNET_break_op (0);
284 goto error; /* hard bound on stack to prevent "infinite" recursion, disallow! */
285 }
286 /* pointer to string */
287 if (*off + 1 > udp_payload_length)
288 {
289 GNUNET_break_op (0);
290 goto error;
291 }
292 xoff = ((len - (64 | 128)) << 8) + input[*off+1];
293 xstr = parse_name (udp_payload,
294 udp_payload_length,
295 &xoff,
296 depth + 1);
297 if (NULL == xstr)
298 {
299 GNUNET_break_op (0);
300 goto error;
301 }
302 GNUNET_asprintf (&tmp,
303 "%s%s.",
304 ret,
305 xstr);
306 GNUNET_free (ret);
307 GNUNET_free (xstr);
308 ret = tmp;
309 if (strlen (ret) > udp_payload_length)
310 {
311 GNUNET_break_op (0);
312 goto error; /* we are looping (building an infinite string) */
313 }
314 *off += 2;
315 /* pointers always terminate names */
316 break;
317 }
318 else
319 {
320 /* neither pointer nor inline string, not supported... */
321 GNUNET_break_op (0);
322 goto error;
323 }
324 }
325 if (0 < strlen(ret))
326 ret[strlen(ret)-1] = '\0'; /* eat tailing '.' */
327 return ret;
328 error:
329 GNUNET_break_op (0);
330 GNUNET_free (ret);
331 return NULL;
332}
333
334
335/**
336 * Parse name inside of a DNS query or record.
337 *
338 * @param udp_payload entire UDP payload
339 * @param udp_payload_length length of @a udp_payload
340 * @param off pointer to the offset of the name to parse in the udp_payload (to be
341 * incremented by the size of the name)
342 * @return name as 0-terminated C string on success, NULL if the payload is malformed
343 */
344char *
345GNUNET_DNSPARSER_parse_name (const char *udp_payload,
346 size_t udp_payload_length,
347 size_t *off)
348{
349 return parse_name (udp_payload, udp_payload_length, off, 0);
350}
351
352
353/**
354 * Parse a DNS query entry.
355 *
356 * @param udp_payload entire UDP payload
357 * @param udp_payload_length length of @a udp_payload
358 * @param off pointer to the offset of the query to parse in the udp_payload (to be
359 * incremented by the size of the query)
360 * @param q where to write the query information
361 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the query is malformed
362 */
363int
364GNUNET_DNSPARSER_parse_query (const char *udp_payload,
365 size_t udp_payload_length,
366 size_t *off,
367 struct GNUNET_DNSPARSER_Query *q)
368{
369 char *name;
370 struct GNUNET_TUN_DnsQueryLine ql;
371
372 name = GNUNET_DNSPARSER_parse_name (udp_payload,
373 udp_payload_length,
374 off);
375 if (NULL == name)
376 {
377 GNUNET_break_op (0);
378 return GNUNET_SYSERR;
379 }
380 q->name = name;
381 if (*off + sizeof (struct GNUNET_TUN_DnsQueryLine) > udp_payload_length)
382 {
383 GNUNET_break_op (0);
384 return GNUNET_SYSERR;
385 }
386 GNUNET_memcpy (&ql, &udp_payload[*off], sizeof (ql));
387 *off += sizeof (ql);
388 q->type = ntohs (ql.type);
389 q->dns_traffic_class = ntohs (ql.dns_traffic_class);
390 return GNUNET_OK;
391}
392
393
394/**
395 * Parse a DNS SOA record.
396 *
397 * @param udp_payload reference to UDP packet
398 * @param udp_payload_length length of @a udp_payload
399 * @param off pointer to the offset of the query to parse in the SOA record (to be
400 * incremented by the size of the record), unchanged on error
401 * @return the parsed SOA record, NULL on error
402 */
403struct GNUNET_DNSPARSER_SoaRecord *
404GNUNET_DNSPARSER_parse_soa (const char *udp_payload,
405 size_t udp_payload_length,
406 size_t *off)
407{
408 struct GNUNET_DNSPARSER_SoaRecord *soa;
409 struct GNUNET_TUN_DnsSoaRecord soa_bin;
410 size_t old_off;
411
412 old_off = *off;
413 soa = GNUNET_new (struct GNUNET_DNSPARSER_SoaRecord);
414 soa->mname = GNUNET_DNSPARSER_parse_name (udp_payload,
415 udp_payload_length,
416 off);
417 soa->rname = GNUNET_DNSPARSER_parse_name (udp_payload,
418 udp_payload_length,
419 off);
420 if ( (NULL == soa->mname) ||
421 (NULL == soa->rname) ||
422 (*off + sizeof (struct GNUNET_TUN_DnsSoaRecord) > udp_payload_length) )
423 {
424 GNUNET_break_op (0);
425 GNUNET_DNSPARSER_free_soa (soa);
426 *off = old_off;
427 return NULL;
428 }
429 GNUNET_memcpy (&soa_bin,
430 &udp_payload[*off],
431 sizeof (struct GNUNET_TUN_DnsSoaRecord));
432 soa->serial = ntohl (soa_bin.serial);
433 soa->refresh = ntohl (soa_bin.refresh);
434 soa->retry = ntohl (soa_bin.retry);
435 soa->expire = ntohl (soa_bin.expire);
436 soa->minimum_ttl = ntohl (soa_bin.minimum);
437 (*off) += sizeof (struct GNUNET_TUN_DnsSoaRecord);
438 return soa;
439}
440
441
442/**
443 * Parse a DNS MX record.
444 *
445 * @param udp_payload reference to UDP packet
446 * @param udp_payload_length length of @a udp_payload
447 * @param off pointer to the offset of the query to parse in the MX record (to be
448 * incremented by the size of the record), unchanged on error
449 * @return the parsed MX record, NULL on error
450 */
451struct GNUNET_DNSPARSER_MxRecord *
452GNUNET_DNSPARSER_parse_mx (const char *udp_payload,
453 size_t udp_payload_length,
454 size_t *off)
455{
456 struct GNUNET_DNSPARSER_MxRecord *mx;
457 uint16_t mxpref;
458 size_t old_off;
459
460 old_off = *off;
461 if (*off + sizeof (uint16_t) > udp_payload_length)
462 {
463 GNUNET_break_op (0);
464 return NULL;
465 }
466 GNUNET_memcpy (&mxpref, &udp_payload[*off], sizeof (uint16_t));
467 (*off) += sizeof (uint16_t);
468 mx = GNUNET_new (struct GNUNET_DNSPARSER_MxRecord);
469 mx->preference = ntohs (mxpref);
470 mx->mxhost = GNUNET_DNSPARSER_parse_name (udp_payload,
471 udp_payload_length,
472 off);
473 if (NULL == mx->mxhost)
474 {
475 GNUNET_break_op (0);
476 GNUNET_DNSPARSER_free_mx (mx);
477 *off = old_off;
478 return NULL;
479 }
480 return mx;
481}
482
483
484/**
485 * Parse a DNS SRV record.
486 *
487 * @param udp_payload reference to UDP packet
488 * @param udp_payload_length length of @a udp_payload
489 * @param off pointer to the offset of the query to parse in the SRV record (to be
490 * incremented by the size of the record), unchanged on error
491 * @return the parsed SRV record, NULL on error
492 */
493struct GNUNET_DNSPARSER_SrvRecord *
494GNUNET_DNSPARSER_parse_srv (const char *udp_payload,
495 size_t udp_payload_length,
496 size_t *off)
497{
498 struct GNUNET_DNSPARSER_SrvRecord *srv;
499 struct GNUNET_TUN_DnsSrvRecord srv_bin;
500 size_t old_off;
501
502 old_off = *off;
503 if (*off + sizeof (struct GNUNET_TUN_DnsSrvRecord) > udp_payload_length)
504 return NULL;
505 GNUNET_memcpy (&srv_bin,
506 &udp_payload[*off],
507 sizeof (struct GNUNET_TUN_DnsSrvRecord));
508 (*off) += sizeof (struct GNUNET_TUN_DnsSrvRecord);
509 srv = GNUNET_new (struct GNUNET_DNSPARSER_SrvRecord);
510 srv->priority = ntohs (srv_bin.prio);
511 srv->weight = ntohs (srv_bin.weight);
512 srv->port = ntohs (srv_bin.port);
513 srv->target = GNUNET_DNSPARSER_parse_name (udp_payload,
514 udp_payload_length,
515 off);
516 if (NULL == srv->target)
517 {
518 GNUNET_DNSPARSER_free_srv (srv);
519 *off = old_off;
520 return NULL;
521 }
522 return srv;
523}
524
525
526/**
527 * Parse a DNS CERT record.
528 *
529 * @param udp_payload reference to UDP packet
530 * @param udp_payload_length length of @a udp_payload
531 * @param off pointer to the offset of the query to parse in the CERT record (to be
532 * incremented by the size of the record), unchanged on error
533 * @return the parsed CERT record, NULL on error
534 */
535struct GNUNET_DNSPARSER_CertRecord *
536GNUNET_DNSPARSER_parse_cert (const char *udp_payload,
537 size_t udp_payload_length,
538 size_t *off)
539{
540 struct GNUNET_DNSPARSER_CertRecord *cert;
541 struct GNUNET_TUN_DnsCertRecord dcert;
542
543 if (*off + sizeof (struct GNUNET_TUN_DnsCertRecord) >= udp_payload_length)
544 {
545 GNUNET_break_op (0);
546 return NULL;
547 }
548 GNUNET_memcpy (&dcert, &udp_payload[*off], sizeof (struct GNUNET_TUN_DnsCertRecord));
549 (*off) += sizeof (struct GNUNET_TUN_DnsCertRecord);
550 cert = GNUNET_new (struct GNUNET_DNSPARSER_CertRecord);
551 cert->cert_type = ntohs (dcert.cert_type);
552 cert->cert_tag = ntohs (dcert.cert_tag);
553 cert->algorithm = dcert.algorithm;
554 cert->certificate_size = udp_payload_length - (*off);
555 cert->certificate_data = GNUNET_malloc (cert->certificate_size);
556 GNUNET_memcpy (cert->certificate_data,
557 &udp_payload[*off],
558 cert->certificate_size);
559 (*off) += cert->certificate_size;
560 return cert;
561}
562
563
564/**
565 * Parse a DNS record entry.
566 *
567 * @param udp_payload entire UDP payload
568 * @param udp_payload_length length of @a udp_payload
569 * @param off pointer to the offset of the record to parse in the udp_payload (to be
570 * incremented by the size of the record)
571 * @param r where to write the record information
572 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the record is malformed
573 */
574int
575GNUNET_DNSPARSER_parse_record (const char *udp_payload,
576 size_t udp_payload_length,
577 size_t *off,
578 struct GNUNET_DNSPARSER_Record *r)
579{
580 char *name;
581 struct GNUNET_TUN_DnsRecordLine rl;
582 size_t old_off;
583 uint16_t data_len;
584
585 name = GNUNET_DNSPARSER_parse_name (udp_payload,
586 udp_payload_length,
587 off);
588 if (NULL == name)
589 {
590 GNUNET_break_op (0);
591 return GNUNET_SYSERR;
592 }
593 r->name = name;
594 if (*off + sizeof (struct GNUNET_TUN_DnsRecordLine) > udp_payload_length)
595 {
596 GNUNET_break_op (0);
597 return GNUNET_SYSERR;
598 }
599 GNUNET_memcpy (&rl, &udp_payload[*off], sizeof (rl));
600 (*off) += sizeof (rl);
601 r->type = ntohs (rl.type);
602 r->dns_traffic_class = ntohs (rl.dns_traffic_class);
603 r->expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
604 ntohl (rl.ttl)));
605 data_len = ntohs (rl.data_len);
606 if (*off + data_len > udp_payload_length)
607 {
608 GNUNET_break_op (0);
609 return GNUNET_SYSERR;
610 }
611 old_off = *off;
612 switch (r->type)
613 {
614 case GNUNET_DNSPARSER_TYPE_NS:
615 case GNUNET_DNSPARSER_TYPE_CNAME:
616 case GNUNET_DNSPARSER_TYPE_DNAME:
617 case GNUNET_DNSPARSER_TYPE_PTR:
618 r->data.hostname = GNUNET_DNSPARSER_parse_name (udp_payload,
619 udp_payload_length,
620 off);
621 if ( (NULL == r->data.hostname) ||
622 (old_off + data_len != *off) )
623 return GNUNET_SYSERR;
624 return GNUNET_OK;
625 case GNUNET_DNSPARSER_TYPE_SOA:
626 r->data.soa = GNUNET_DNSPARSER_parse_soa (udp_payload,
627 udp_payload_length,
628 off);
629 if ( (NULL == r->data.soa) ||
630 (old_off + data_len != *off) )
631 {
632 GNUNET_break_op (0);
633 return GNUNET_SYSERR;
634 }
635 return GNUNET_OK;
636 case GNUNET_DNSPARSER_TYPE_MX:
637 r->data.mx = GNUNET_DNSPARSER_parse_mx (udp_payload,
638 udp_payload_length,
639 off);
640 if ( (NULL == r->data.mx) ||
641 (old_off + data_len != *off) )
642 {
643 GNUNET_break_op (0);
644 return GNUNET_SYSERR;
645 }
646 return GNUNET_OK;
647 case GNUNET_DNSPARSER_TYPE_SRV:
648 r->data.srv = GNUNET_DNSPARSER_parse_srv (udp_payload,
649 udp_payload_length,
650 off);
651 if ( (NULL == r->data.srv) ||
652 (old_off + data_len != *off) )
653 {
654 GNUNET_break_op (0);
655 return GNUNET_SYSERR;
656 }
657 return GNUNET_OK;
658 default:
659 r->data.raw.data = GNUNET_malloc (data_len);
660 r->data.raw.data_len = data_len;
661 GNUNET_memcpy (r->data.raw.data,
662 &udp_payload[*off],
663 data_len);
664 break;
665 }
666 (*off) += data_len;
667 return GNUNET_OK;
668}
669
670
671/**
672 * Parse a UDP payload of a DNS packet in to a nice struct for further
673 * processing and manipulation.
674 *
675 * @param udp_payload wire-format of the DNS packet
676 * @param udp_payload_length number of bytes in @a udp_payload
677 * @return NULL on error, otherwise the parsed packet
678 */
679struct GNUNET_DNSPARSER_Packet *
680GNUNET_DNSPARSER_parse (const char *udp_payload,
681 size_t udp_payload_length)
682{
683 struct GNUNET_DNSPARSER_Packet *p;
684 const struct GNUNET_TUN_DnsHeader *dns;
685 size_t off;
686 unsigned int n;
687 unsigned int i;
688
689 if (udp_payload_length < sizeof (struct GNUNET_TUN_DnsHeader))
690 return NULL;
691 dns = (const struct GNUNET_TUN_DnsHeader *) udp_payload;
692 off = sizeof (struct GNUNET_TUN_DnsHeader);
693 p = GNUNET_new (struct GNUNET_DNSPARSER_Packet);
694 p->flags = dns->flags;
695 p->id = dns->id;
696 n = ntohs (dns->query_count);
697 if (n > 0)
698 {
699 p->queries = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Query));
700 p->num_queries = n;
701 for (i=0;i<n;i++)
702 if (GNUNET_OK !=
703 GNUNET_DNSPARSER_parse_query (udp_payload,
704 udp_payload_length,
705 &off,
706 &p->queries[i]))
707 goto error;
708 }
709 n = ntohs (dns->answer_rcount);
710 if (n > 0)
711 {
712 p->answers = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
713 p->num_answers = n;
714 for (i=0;i<n;i++)
715 if (GNUNET_OK !=
716 GNUNET_DNSPARSER_parse_record (udp_payload,
717 udp_payload_length,
718 &off,
719 &p->answers[i]))
720 goto error;
721 }
722 n = ntohs (dns->authority_rcount);
723 if (n > 0)
724 {
725 p->authority_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
726 p->num_authority_records = n;
727 for (i=0;i<n;i++)
728 if (GNUNET_OK !=
729 GNUNET_DNSPARSER_parse_record (udp_payload,
730 udp_payload_length,
731 &off,
732 &p->authority_records[i]))
733 goto error;
734 }
735 n = ntohs (dns->additional_rcount);
736 if (n > 0)
737 {
738 p->additional_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
739 p->num_additional_records = n;
740 for (i=0;i<n;i++)
741 if (GNUNET_OK !=
742 GNUNET_DNSPARSER_parse_record (udp_payload,
743 udp_payload_length,
744 &off,
745 &p->additional_records[i]))
746 goto error;
747 }
748 return p;
749 error:
750 GNUNET_break_op (0);
751 GNUNET_DNSPARSER_free_packet (p);
752 return NULL;
753}
754
755
756/**
757 * Free memory taken by a packet.
758 *
759 * @param p packet to free
760 */
761void
762GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
763{
764 unsigned int i;
765
766 for (i=0;i<p->num_queries;i++)
767 GNUNET_free_non_null (p->queries[i].name);
768 GNUNET_free_non_null (p->queries);
769 for (i=0;i<p->num_answers;i++)
770 GNUNET_DNSPARSER_free_record (&p->answers[i]);
771 GNUNET_free_non_null (p->answers);
772 for (i=0;i<p->num_authority_records;i++)
773 GNUNET_DNSPARSER_free_record (&p->authority_records[i]);
774 GNUNET_free_non_null (p->authority_records);
775 for (i=0;i<p->num_additional_records;i++)
776 GNUNET_DNSPARSER_free_record (&p->additional_records[i]);
777 GNUNET_free_non_null (p->additional_records);
778 GNUNET_free (p);
779}
780
781
782/* ********************** DNS packet assembly code **************** */
783
784
785/**
786 * Add a DNS name to the UDP packet at the given location, converting
787 * the name to IDNA notation as necessary.
788 *
789 * @param dst where to write the name (UDP packet)
790 * @param dst_len number of bytes in @a dst
791 * @param off pointer to offset where to write the name (increment by bytes used)
792 * must not be changed if there is an error
793 * @param name name to write
794 * @return #GNUNET_SYSERR if @a name is invalid
795 * #GNUNET_NO if @a name did not fit
796 * #GNUNET_OK if @a name was added to @a dst
797 */
798int
799GNUNET_DNSPARSER_builder_add_name (char *dst,
800 size_t dst_len,
801 size_t *off,
802 const char *name)
803{
804 const char *dot;
805 const char *idna_name;
806 char *idna_start;
807 size_t start;
808 size_t pos;
809 size_t len;
810 Idna_rc rc;
811
812 if (NULL == name)
813 return GNUNET_SYSERR;
814
815 if (IDNA_SUCCESS !=
816 (rc = idna_to_ascii_8z (name,
817 &idna_start,
818 IDNA_ALLOW_UNASSIGNED)))
819 {
820 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
821 _("Failed to convert UTF-8 name `%s' to DNS IDNA format: %s\n"),
822 name,
823 idna_strerror (rc));
824 return GNUNET_NO;
825 }
826 idna_name = idna_start;
827 start = *off;
828 if (start + strlen (idna_name) + 2 > dst_len)
829 goto fail;
830 pos = start;
831 do
832 {
833 dot = strchr (idna_name, '.');
834 if (NULL == dot)
835 len = strlen (idna_name);
836 else
837 len = dot - idna_name;
838 if ( (len >= 64) || (0 == len) )
839 {
840 GNUNET_break (0);
841 goto fail; /* segment too long or empty */
842 }
843 dst[pos++] = (char) (uint8_t) len;
844 GNUNET_memcpy (&dst[pos],
845 idna_name,
846 len);
847 pos += len;
848 idna_name += len + 1; /* also skip dot */
849 }
850 while (NULL != dot);
851 dst[pos++] = '\0'; /* terminator */
852 *off = pos;
853#if WINDOWS
854 idn_free (idna_start);
855#else
856 free (idna_start);
857#endif
858 return GNUNET_OK;
859 fail:
860#if WINDOWS
861 idn_free (idna_start);
862#else
863 free (idna_start);
864#endif
865 return GNUNET_NO;
866}
867
868
869/**
870 * Add a DNS query to the UDP packet at the given location.
871 *
872 * @param dst where to write the query
873 * @param dst_len number of bytes in @a dst
874 * @param off pointer to offset where to write the query (increment by bytes used)
875 * must not be changed if there is an error
876 * @param query query to write
877 * @return #GNUNET_SYSERR if @a query is invalid
878 * #GNUNET_NO if @a query did not fit
879 * #GNUNET_OK if @a query was added to @a dst
880 */
881int
882GNUNET_DNSPARSER_builder_add_query (char *dst,
883 size_t dst_len,
884 size_t *off,
885 const struct GNUNET_DNSPARSER_Query *query)
886{
887 int ret;
888 struct GNUNET_TUN_DnsQueryLine ql;
889
890 ret = GNUNET_DNSPARSER_builder_add_name (dst, dst_len - sizeof (struct GNUNET_TUN_DnsQueryLine), off, query->name);
891 if (ret != GNUNET_OK)
892 return ret;
893 ql.type = htons (query->type);
894 ql.dns_traffic_class = htons (query->dns_traffic_class);
895 GNUNET_memcpy (&dst[*off], &ql, sizeof (ql));
896 (*off) += sizeof (ql);
897 return GNUNET_OK;
898}
899
900
901/**
902 * Add an MX record to the UDP packet at the given location.
903 *
904 * @param dst where to write the mx record
905 * @param dst_len number of bytes in @a dst
906 * @param off pointer to offset where to write the mx information (increment by bytes used);
907 * can also change if there was an error
908 * @param mx mx information to write
909 * @return #GNUNET_SYSERR if @a mx is invalid
910 * #GNUNET_NO if @a mx did not fit
911 * #GNUNET_OK if @a mx was added to @a dst
912 */
913int
914GNUNET_DNSPARSER_builder_add_mx (char *dst,
915 size_t dst_len,
916 size_t *off,
917 const struct GNUNET_DNSPARSER_MxRecord *mx)
918{
919 uint16_t mxpref;
920
921 if (*off + sizeof (uint16_t) > dst_len)
922 return GNUNET_NO;
923 mxpref = htons (mx->preference);
924 GNUNET_memcpy (&dst[*off],
925 &mxpref,
926 sizeof (mxpref));
927 (*off) += sizeof (mxpref);
928 return GNUNET_DNSPARSER_builder_add_name (dst,
929 dst_len,
930 off,
931 mx->mxhost);
932}
933
934
935/**
936 * Add a CERT record to the UDP packet at the given location.
937 *
938 * @param dst where to write the CERT record
939 * @param dst_len number of bytes in @a dst
940 * @param off pointer to offset where to write the CERT information (increment by bytes used);
941 * can also change if there was an error
942 * @param cert CERT information to write
943 * @return #GNUNET_SYSERR if @a cert is invalid
944 * #GNUNET_NO if @a cert did not fit
945 * #GNUNET_OK if @a cert was added to @a dst
946 */
947int
948GNUNET_DNSPARSER_builder_add_cert (char *dst,
949 size_t dst_len,
950 size_t *off,
951 const struct GNUNET_DNSPARSER_CertRecord *cert)
952{
953 struct GNUNET_TUN_DnsCertRecord dcert;
954
955 if ( (cert->cert_type > UINT16_MAX) ||
956 (cert->cert_tag > UINT16_MAX) ||
957 (cert->algorithm > UINT8_MAX) )
958 {
959 GNUNET_break (0);
960 return GNUNET_SYSERR;
961 }
962 if (*off + sizeof (struct GNUNET_TUN_DnsCertRecord) + cert->certificate_size > dst_len)
963 return GNUNET_NO;
964 dcert.cert_type = htons ((uint16_t) cert->cert_type);
965 dcert.cert_tag = htons ((uint16_t) cert->cert_tag);
966 dcert.algorithm = (uint8_t) cert->algorithm;
967 GNUNET_memcpy (&dst[*off], &dcert, sizeof (dcert));
968 (*off) += sizeof (dcert);
969 GNUNET_memcpy (&dst[*off], cert->certificate_data, cert->certificate_size);
970 (*off) += cert->certificate_size;
971 return GNUNET_OK;
972}
973
974
975/**
976 * Add an SOA record to the UDP packet at the given location.
977 *
978 * @param dst where to write the SOA record
979 * @param dst_len number of bytes in @a dst
980 * @param off pointer to offset where to write the SOA information (increment by bytes used)
981 * can also change if there was an error
982 * @param soa SOA information to write
983 * @return #GNUNET_SYSERR if @a soa is invalid
984 * #GNUNET_NO if @a soa did not fit
985 * #GNUNET_OK if @a soa was added to @a dst
986 */
987int
988GNUNET_DNSPARSER_builder_add_soa (char *dst,
989 size_t dst_len,
990 size_t *off,
991 const struct GNUNET_DNSPARSER_SoaRecord *soa)
992{
993 struct GNUNET_TUN_DnsSoaRecord sd;
994 int ret;
995
996 if ( (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
997 dst_len,
998 off,
999 soa->mname))) ||
1000 (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
1001 dst_len,
1002 off,
1003 soa->rname)) ) )
1004 return ret;
1005 if (*off + sizeof (struct GNUNET_TUN_DnsSoaRecord) > dst_len)
1006 return GNUNET_NO;
1007 sd.serial = htonl (soa->serial);
1008 sd.refresh = htonl (soa->refresh);
1009 sd.retry = htonl (soa->retry);
1010 sd.expire = htonl (soa->expire);
1011 sd.minimum = htonl (soa->minimum_ttl);
1012 GNUNET_memcpy (&dst[*off], &sd, sizeof (sd));
1013 (*off) += sizeof (sd);
1014 return GNUNET_OK;
1015}
1016
1017
1018/**
1019 * Add an SRV record to the UDP packet at the given location.
1020 *
1021 * @param dst where to write the SRV record
1022 * @param dst_len number of bytes in @a dst
1023 * @param off pointer to offset where to write the SRV information (increment by bytes used)
1024 * can also change if there was an error
1025 * @param srv SRV information to write
1026 * @return #GNUNET_SYSERR if @a srv is invalid
1027 * #GNUNET_NO if @a srv did not fit
1028 * #GNUNET_OK if @a srv was added to @a dst
1029 */
1030int
1031GNUNET_DNSPARSER_builder_add_srv (char *dst,
1032 size_t dst_len,
1033 size_t *off,
1034 const struct GNUNET_DNSPARSER_SrvRecord *srv)
1035{
1036 struct GNUNET_TUN_DnsSrvRecord sd;
1037 int ret;
1038
1039 if (*off + sizeof (struct GNUNET_TUN_DnsSrvRecord) > dst_len)
1040 return GNUNET_NO;
1041 sd.prio = htons (srv->priority);
1042 sd.weight = htons (srv->weight);
1043 sd.port = htons (srv->port);
1044 GNUNET_memcpy (&dst[*off], &sd, sizeof (sd));
1045 (*off) += sizeof (sd);
1046 if (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
1047 dst_len,
1048 off,
1049 srv->target)))
1050 return ret;
1051 return GNUNET_OK;
1052}
1053
1054
1055/**
1056 * Add a DNS record to the UDP packet at the given location.
1057 *
1058 * @param dst where to write the query
1059 * @param dst_len number of bytes in @a dst
1060 * @param off pointer to offset where to write the query (increment by bytes used)
1061 * must not be changed if there is an error
1062 * @param record record to write
1063 * @return #GNUNET_SYSERR if @a record is invalid
1064 * #GNUNET_NO if @a record did not fit
1065 * #GNUNET_OK if @a record was added to @a dst
1066 */
1067static int
1068add_record (char *dst,
1069 size_t dst_len,
1070 size_t *off,
1071 const struct GNUNET_DNSPARSER_Record *record)
1072{
1073 int ret;
1074 size_t start;
1075 size_t pos;
1076 struct GNUNET_TUN_DnsRecordLine rl;
1077
1078 start = *off;
1079 ret = GNUNET_DNSPARSER_builder_add_name (dst,
1080 dst_len - sizeof (struct GNUNET_TUN_DnsRecordLine),
1081 off,
1082 record->name);
1083 if (GNUNET_OK != ret)
1084 return ret;
1085 /* '*off' is now the position where we will need to write the record line */
1086
1087 pos = *off + sizeof (struct GNUNET_TUN_DnsRecordLine);
1088 switch (record->type)
1089 {
1090 case GNUNET_DNSPARSER_TYPE_MX:
1091 ret = GNUNET_DNSPARSER_builder_add_mx (dst,
1092 dst_len,
1093 &pos,
1094 record->data.mx);
1095 break;
1096 case GNUNET_DNSPARSER_TYPE_CERT:
1097 ret = GNUNET_DNSPARSER_builder_add_cert (dst,
1098 dst_len,
1099 &pos,
1100 record->data.cert);
1101 break;
1102 case GNUNET_DNSPARSER_TYPE_SOA:
1103 ret = GNUNET_DNSPARSER_builder_add_soa (dst,
1104 dst_len,
1105 &pos,
1106 record->data.soa);
1107 break;
1108 case GNUNET_DNSPARSER_TYPE_NS:
1109 case GNUNET_DNSPARSER_TYPE_CNAME:
1110 case GNUNET_DNSPARSER_TYPE_PTR:
1111 ret = GNUNET_DNSPARSER_builder_add_name (dst,
1112 dst_len,
1113 &pos,
1114 record->data.hostname);
1115 break;
1116 case GNUNET_DNSPARSER_TYPE_SRV:
1117 ret = GNUNET_DNSPARSER_builder_add_srv (dst,
1118 dst_len,
1119 &pos,
1120 record->data.srv);
1121 break;
1122 default:
1123 if (pos + record->data.raw.data_len > dst_len)
1124 {
1125 ret = GNUNET_NO;
1126 break;
1127 }
1128 GNUNET_memcpy (&dst[pos],
1129 record->data.raw.data,
1130 record->data.raw.data_len);
1131 pos += record->data.raw.data_len;
1132 ret = GNUNET_OK;
1133 break;
1134 }
1135 if (GNUNET_OK != ret)
1136 {
1137 *off = start;
1138 return GNUNET_NO;
1139 }
1140
1141 if (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine)) > UINT16_MAX)
1142 {
1143 /* record data too long */
1144 *off = start;
1145 return GNUNET_NO;
1146 }
1147 rl.type = htons (record->type);
1148 rl.dns_traffic_class = htons (record->dns_traffic_class);
1149 rl.ttl = htonl (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value_us / 1000LL / 1000LL); /* in seconds */
1150 rl.data_len = htons ((uint16_t) (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine))));
1151 GNUNET_memcpy (&dst[*off], &rl, sizeof (struct GNUNET_TUN_DnsRecordLine));
1152 *off = pos;
1153 return GNUNET_OK;
1154}
1155
1156
1157/**
1158 * Given a DNS packet @a p, generate the corresponding UDP payload.
1159 * Note that we do not attempt to pack the strings with pointers
1160 * as this would complicate the code and this is about being
1161 * simple and secure, not fast, fancy and broken like bind.
1162 *
1163 * @param p packet to pack
1164 * @param max maximum allowed size for the resulting UDP payload
1165 * @param buf set to a buffer with the packed message
1166 * @param buf_length set to the length of @a buf
1167 * @return #GNUNET_SYSERR if @a p is invalid
1168 * #GNUNET_NO if @a p was truncated (but there is still a result in @a buf)
1169 * #GNUNET_OK if @a p was packed completely into @a buf
1170 */
1171int
1172GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
1173 uint16_t max,
1174 char **buf,
1175 size_t *buf_length)
1176{
1177 struct GNUNET_TUN_DnsHeader dns;
1178 size_t off;
1179 char tmp[max];
1180 unsigned int i;
1181 int ret;
1182 int trc;
1183
1184 if ( (p->num_queries > UINT16_MAX) ||
1185 (p->num_answers > UINT16_MAX) ||
1186 (p->num_authority_records > UINT16_MAX) ||
1187 (p->num_additional_records > UINT16_MAX) )
1188 return GNUNET_SYSERR;
1189 dns.id = p->id;
1190 dns.flags = p->flags;
1191 dns.query_count = htons (p->num_queries);
1192 dns.answer_rcount = htons (p->num_answers);
1193 dns.authority_rcount = htons (p->num_authority_records);
1194 dns.additional_rcount = htons (p->num_additional_records);
1195
1196 off = sizeof (struct GNUNET_TUN_DnsHeader);
1197 trc = GNUNET_NO;
1198 for (i=0;i<p->num_queries;i++)
1199 {
1200 ret = GNUNET_DNSPARSER_builder_add_query (tmp,
1201 sizeof (tmp),
1202 &off,
1203 &p->queries[i]);
1204 if (GNUNET_SYSERR == ret)
1205 return GNUNET_SYSERR;
1206 if (GNUNET_NO == ret)
1207 {
1208 dns.query_count = htons ((uint16_t) (i-1));
1209 trc = GNUNET_YES;
1210 break;
1211 }
1212 }
1213 for (i=0;i<p->num_answers;i++)
1214 {
1215 ret = add_record (tmp,
1216 sizeof (tmp),
1217 &off,
1218 &p->answers[i]);
1219 if (GNUNET_SYSERR == ret)
1220 return GNUNET_SYSERR;
1221 if (GNUNET_NO == ret)
1222 {
1223 dns.answer_rcount = htons ((uint16_t) (i-1));
1224 trc = GNUNET_YES;
1225 break;
1226 }
1227 }
1228 for (i=0;i<p->num_authority_records;i++)
1229 {
1230 ret = add_record (tmp,
1231 sizeof (tmp),
1232 &off,
1233 &p->authority_records[i]);
1234 if (GNUNET_SYSERR == ret)
1235 return GNUNET_SYSERR;
1236 if (GNUNET_NO == ret)
1237 {
1238 dns.authority_rcount = htons ((uint16_t) (i-1));
1239 trc = GNUNET_YES;
1240 break;
1241 }
1242 }
1243 for (i=0;i<p->num_additional_records;i++)
1244 {
1245 ret = add_record (tmp,
1246 sizeof (tmp),
1247 &off,
1248 &p->additional_records[i]);
1249 if (GNUNET_SYSERR == ret)
1250 return GNUNET_SYSERR;
1251 if (GNUNET_NO == ret)
1252 {
1253 dns.additional_rcount = htons (i-1);
1254 trc = GNUNET_YES;
1255 break;
1256 }
1257 }
1258
1259 if (GNUNET_YES == trc)
1260 dns.flags.message_truncated = 1;
1261 GNUNET_memcpy (tmp,
1262 &dns,
1263 sizeof (struct GNUNET_TUN_DnsHeader));
1264
1265 *buf = GNUNET_malloc (off);
1266 *buf_length = off;
1267 GNUNET_memcpy (*buf,
1268 tmp,
1269 off);
1270 if (GNUNET_YES == trc)
1271 return GNUNET_NO;
1272 return GNUNET_OK;
1273}
1274
1275
1276/**
1277 * Convert a block of binary data to HEX.
1278 *
1279 * @param data binary data to convert
1280 * @param data_size number of bytes in @a data
1281 * @return HEX string (lower case)
1282 */
1283char *
1284GNUNET_DNSPARSER_bin_to_hex (const void *data,
1285 size_t data_size)
1286{
1287 char *ret;
1288 size_t off;
1289 const uint8_t *idata;
1290
1291 idata = data;
1292 ret = GNUNET_malloc (data_size * 2 + 1);
1293 for (off = 0; off < data_size; off++)
1294 sprintf (&ret[off * 2],
1295 "%02x",
1296 idata[off]);
1297 return ret;
1298}
1299
1300
1301/**
1302 * Convert a HEX string to block of binary data.
1303 *
1304 * @param hex HEX string to convert (may contain mixed case)
1305 * @param data where to write result, must be
1306 * at least `strlen(hex)/2` bytes long
1307 * @return number of bytes written to data
1308 */
1309size_t
1310GNUNET_DNSPARSER_hex_to_bin (const char *hex,
1311 void *data)
1312{
1313 size_t data_size;
1314 size_t off;
1315 uint8_t *idata;
1316 unsigned int h;
1317 char in[3];
1318
1319 data_size = strlen (hex) / 2;
1320 idata = data;
1321 in[2] = '\0';
1322 for (off = 0; off < data_size; off++)
1323 {
1324 in[0] = tolower ((unsigned char) hex[off * 2]);
1325 in[1] = tolower ((unsigned char) hex[off * 2 + 1]);
1326 if (1 != sscanf (in, "%x", &h))
1327 return off;
1328 idata[off] = (uint8_t) h;
1329 }
1330 return off;
1331}
1332
1333
1334/* end of dnsparser.c */