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