1/* ****************************************************************************
2 *  PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
3 *  See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
4 *  .
5 *  Authors: Toon Stegen, Jelle De Vleeschouwer
6 * ****************************************************************************/
7#include "pico_config.h"
8#include "pico_protocol.h"
9#include "pico_stack.h"
10#include "pico_addressing.h"
11#include "pico_ipv4.h"
12#include "pico_ipv6.h"
13#include "pico_dns_common.h"
14#include "pico_tree.h"
15
16#ifdef DEBUG_DNS
17    #define dns_dbg dbg
18#else
19    #define dns_dbg(...) do {} while(0)
20#endif
21
22/* MARK: v NAME & IP FUNCTIONS */
23#define dns_name_foreach_label_safe(label, name, next, maxlen) \
24    for ((label) = (name), (next) = (char *)((name) + *(unsigned char*)(name) + 1); \
25         (*(label) != '\0') && ((uint16_t)((label) - (name)) < (maxlen)); \
26         (label) = (next), (next) = (char *)((next) + *(unsigned char*)(next) + 1))
27
28/* ****************************************************************************
29 *  Checks if the DNS name doesn't exceed 256 bytes including zero-byte.
30 *
31 *  @param namelen Length of the DNS name-string including zero-byte
32 *  @return 0 when the length is correct
33 * ****************************************************************************/
34int
35pico_dns_check_namelen( uint16_t namelen )
36{
37    return ((namelen > 2u) && (namelen < 256u)) ? (0) : (-1);
38}
39
40/* ****************************************************************************
41 *  Returns the length of a name in a DNS-packet as if DNS name compression
42 *  would be applied to the packet. If there's no compression present
43 *
44 *  @param name Compressed name you want the calculate the strlen from
45 *  @return Returns strlen of a compressed name, takes the first byte of compr-
46 *			ession pointer into account but not the second byte, which acts
47 *			like a trailing zero-byte
48 * ****************************************************************************/
49uint16_t
50pico_dns_namelen_comp( char *name )
51{
52    uint16_t len = 0;
53    char *label = NULL, *next = NULL;
54
55    /* Check params */
56    if (!name) {
57        pico_err = PICO_ERR_EINVAL;
58        return 0;
59    }
60
61    /* Just count until the zero-byte or a pointer */
62    dns_name_foreach_label_safe(label, name, next, 255) {
63        if ((0xC0 & *label))
64            break;
65    }
66
67    /* Calculate the length */
68    len = (uint16_t)(label - name);
69    if(*label != '\0')
70        len++;
71
72    return len;
73}
74
75/* ****************************************************************************
76 *  Returns the uncompressed name in DNS name format when DNS name compression
77 *  is applied to the packet-buffer.
78 *
79 *  @param name   Compressed name, should be in the bounds of the actual packet
80 *  @param packet Packet that contains the compressed name
81 *  @return Returns the decompressed name, NULL on failure.
82 * ****************************************************************************/
83char *
84pico_dns_decompress_name( char *name, pico_dns_packet *packet )
85{
86    char decompressed_name[PICO_DNS_NAMEBUF_SIZE] = {
87        0
88    };
89    char *return_name = NULL;
90    uint8_t *dest_iterator = NULL;
91    uint8_t *iterator = NULL;
92    uint16_t ptr = 0, nslen = 0;
93
94    /* Initialise iterators */
95    iterator = (uint8_t *) name;
96    dest_iterator = (uint8_t *) decompressed_name;
97    while (*iterator != '\0') {
98        if ((*iterator) & 0xC0) {
99            /* We have a pointer */
100            ptr = (uint16_t)((((uint16_t) *iterator) & 0x003F) << 8);
101            ptr = (uint16_t)(ptr | (uint16_t) *(iterator + 1));
102            iterator = (uint8_t *)((uint8_t *)packet + ptr);
103        } else {
104            /* We want to keep the label lengths */
105            *dest_iterator = (uint8_t) *iterator;
106            /* Copy the label */
107            memcpy(dest_iterator + 1, iterator + 1, *iterator);
108            /* Move to next length label */
109            dest_iterator += (*iterator) + 1;
110            iterator += (*iterator) + 1;
111        }
112    }
113    /* Append final zero-byte */
114    *dest_iterator = (uint8_t) '\0';
115
116    /* Provide storage for the name to return */
117    nslen = (uint16_t)(pico_dns_strlen(decompressed_name) + 1);
118    if(!(return_name = PICO_ZALLOC((size_t)nslen))) {
119        pico_err = PICO_ERR_ENOMEM;
120        return NULL;
121    }
122
123    memcpy((void *)return_name, (void *)decompressed_name, (size_t)nslen);
124
125    return return_name;
126}
127
128/* ****************************************************************************
129 *  Determines the length of a given url as if it where a DNS name in reverse
130 *  resolution format.
131 *
132 *  @param url     URL wanted to create a reverse resolution name from.
133 *  @param arpalen Will get filled with the length of the ARPA-suffix depending
134 *                 on the proto-parameter.
135 *  @param proto   The protocol to create a ARPA-suffix for. Can be either
136 *				   'PICO_PROTO_IPV4' or 'PICO_PROTO_IPV6'
137 *  @return Returns the length of the reverse name
138 * ****************************************************************************/
139static uint16_t
140pico_dns_url_get_reverse_len( const char *url,
141                              uint16_t *arpalen,
142                              uint16_t proto )
143{
144    uint16_t slen = (uint16_t)(pico_dns_strlen(url) + 2u);
145
146    /* Check if pointers given are not NULL */
147    if (pico_dns_check_namelen(slen) && !arpalen) {
148        pico_err = PICO_ERR_EINVAL;
149        return 0;
150    }
151
152    /* Get the length of arpa-suffix if needed */
153    if (proto == PICO_PROTO_IPV4)
154        *arpalen = (uint16_t) pico_dns_strlen(PICO_ARPA_IPV4_SUFFIX);
155
156#ifdef PICO_SUPPORT_IPV6
157    else if (proto == PICO_PROTO_IPV6)
158    {
159        *arpalen = (uint16_t) pico_dns_strlen(PICO_ARPA_IPV6_SUFFIX);
160        slen = STRLEN_PTR_IP6 + 2u;
161    }
162#endif
163    return slen;
164}
165
166/* ****************************************************************************
167 *  Converts a DNS name in URL format to a reverse name in DNS name format.
168 *  Provides space for the DNS name as well. PICO_FREE() should be called on the
169 *  returned string buffer that contains the reverse DNS name.
170 *
171 *  @param url   DNS name in URL format to convert to reverse name
172 *  @param proto Depending on the protocol given the ARPA-suffix will be added.
173 *  @return Returns a pointer to a string-buffer with the reverse DNS name.
174 * ****************************************************************************/
175static char *
176pico_dns_url_to_reverse_qname( const char *url, uint8_t proto )
177{
178    char *reverse_qname = NULL;
179    uint16_t arpalen = 0;
180    uint16_t slen = pico_dns_url_get_reverse_len(url, &arpalen, proto);
181
182    /* Check namelen */
183    if (pico_dns_check_namelen(slen)) {
184        pico_err = PICO_ERR_EINVAL;
185        return NULL;
186    }
187
188    /* Provide space for the reverse name */
189    if (!(reverse_qname = PICO_ZALLOC((size_t)(slen + arpalen)))) {
190        pico_err = PICO_ERR_ENOMEM;
191        return NULL;
192    }
193
194    /* If reverse IPv4 address resolving, convert to IPv4 arpa-format */
195    if (PICO_PROTO_IPV4 == proto) {
196        memcpy(reverse_qname + 1u, url, slen - 1u);
197        pico_dns_mirror_addr(reverse_qname + 1u);
198        memcpy(reverse_qname + slen - 1, PICO_ARPA_IPV4_SUFFIX, arpalen);
199    }
200
201    /* If reverse IPv6 address resolving, convert to IPv6 arpa-format */
202#ifdef PICO_SUPPORT_IPV6
203    else if (proto == PICO_PROTO_IPV6) {
204        pico_dns_ipv6_set_ptr(url, reverse_qname + 1u);
205        memcpy(reverse_qname + 1u + STRLEN_PTR_IP6,
206               PICO_ARPA_IPV6_SUFFIX, arpalen);
207    }
208#endif
209    else { /* This shouldn't happen */
210        PICO_FREE(reverse_qname);
211        return NULL;
212    }
213
214    pico_dns_name_to_dns_notation(reverse_qname, (uint16_t)(slen + arpalen));
215    return reverse_qname;
216}
217
218/* ****************************************************************************
219 *  Converts a DNS name in DNS name format to a name in URL format. Provides
220 *  space for the name in URL format as well. PICO_FREE() should be called on
221 *  the returned string buffer that contains the name in URL format.
222 *
223 *  @param qname DNS name in DNS name format to convert
224 *  @return Returns a pointer to a string-buffer with the URL name on success.
225 * ****************************************************************************/
226char *
227pico_dns_qname_to_url( const char *qname )
228{
229    char *url = NULL;
230    char temp[256] = {
231        0
232    };
233    uint16_t namelen = pico_dns_strlen(qname);
234
235    /* Check if qname is not a NULL-pointer and if the length is OK */
236    if (pico_dns_check_namelen(namelen)) {
237        pico_err = PICO_ERR_EINVAL;
238        return NULL;
239    }
240
241    /* Provide space for the URL */
242    if (!(url = PICO_ZALLOC(namelen))) {
243        pico_err = PICO_ERR_ENOMEM;
244        return NULL;
245    }
246
247    /* Convert qname to an URL */
248    memcpy(temp, qname, namelen);
249    pico_dns_notation_to_name(temp, namelen);
250    memcpy((void *)url, (void *)(temp + 1), (size_t)(namelen - 1));
251
252    return url;
253}
254
255/* ****************************************************************************
256 *  Converts a DNS name in URL format to a name in DNS name format. Provides
257 *  space for the DNS name as well. PICO_FREE() should be called on the returned
258 *  string buffer that contains the DNS name.
259 *
260 *  @param url DNS name in URL format to convert
261 *  @return Returns a pointer to a string-buffer with the DNS name on success.
262 * ****************************************************************************/
263char *
264pico_dns_url_to_qname( const char *url )
265{
266    char *qname = NULL;
267    uint16_t namelen = (uint16_t)(pico_dns_strlen(url) + 2u);
268
269    /* Check if url or qname_addr is not a NULL-pointer */
270    if (pico_dns_check_namelen(namelen)) {
271        pico_err = PICO_ERR_EINVAL;
272        return NULL;
273    }
274
275    /* Provide space for the qname */
276    if (!(qname = PICO_ZALLOC(namelen))) {
277        pico_err = PICO_ERR_ENOMEM;
278        return NULL;
279    }
280
281    /* Copy in the URL (+1 to leave space for leading '.') */
282    memcpy(qname + 1, url, (size_t)(namelen - 1));
283    pico_dns_name_to_dns_notation(qname, namelen);
284    return qname;
285}
286
287/* ****************************************************************************
288 *  @param url String-buffer
289 *  @return Length of string-buffer in an uint16_t
290 * ****************************************************************************/
291uint16_t
292pico_dns_strlen( const char *url )
293{
294    if (!url)
295        return 0;
296
297    return (uint16_t) strlen(url);
298}
299
300/* ****************************************************************************
301 *  Replaces .'s in a DNS name in URL format by the label lengths. So it
302 *  actually converts a name in URL format to a name in DNS name format.
303 *  f.e. "*www.google.be" => "3www6google2be0"
304 *
305 *  @param url    Location to buffer with name in URL format. The URL needs to
306 *                be +1 byte offset in the actual buffer. Size is should be
307 *                pico_dns_strlen(url) + 2.
308 *  @param maxlen Maximum length of buffer so it doesn't cause a buffer overflow
309 *  @return 0 on success, something else on failure.
310 * ****************************************************************************/
311int pico_dns_name_to_dns_notation( char *url, uint16_t maxlen )
312{
313    char c = '\0';
314    char *lbl = url, *i = url;
315
316    /* Check params */
317    if (!url || pico_dns_check_namelen(maxlen)) {
318        pico_err = PICO_ERR_EINVAL;
319        return -1;
320    }
321
322    /* Iterate over url */
323    while ((c = *++i) != '\0') {
324        if ('.' == c) {
325            *lbl = (char)(i - lbl - 1);
326            lbl = i;
327        }
328
329        if ((uint16_t)(i - url) > (uint16_t)maxlen) break;
330    }
331    *lbl = (char)(i - lbl - 1);
332
333    return 0;
334}
335
336/* ****************************************************************************
337 *  Replaces the label lengths in a DNS-name by .'s. So it actually converts a
338 *  name in DNS format to a name in URL format.
339 *  f.e. 3www6google2be0 => .www.google.be
340 *
341 *  @param ptr    Location to buffer with name in DNS name format
342 *  @param maxlen Maximum length of buffer so it doesn't cause a buffer overflow
343 *  @return 0 on success, something else on failure.
344 * ****************************************************************************/
345int pico_dns_notation_to_name( char *ptr, uint16_t maxlen )
346{
347    char *label = NULL, *next = NULL;
348
349    /* Iterate safely over the labels and update each label */
350    dns_name_foreach_label_safe(label, ptr, next, maxlen) {
351        *label = '.';
352    }
353
354    return 0;
355}
356
357/* ****************************************************************************
358 *  Determines the length of the first label of a DNS name in URL-format
359 *
360 *  @param url DNS name in URL-format
361 *  @return Length of the first label of DNS name in URL-format
362 * ****************************************************************************/
363uint16_t
364pico_dns_first_label_length( const char *url )
365{
366    const char *i = NULL;
367    uint16_t len = 0;
368
369    /* Check params */
370    if (!url) return 0;
371
372    /* Count */
373    i = url;
374    while (*i != '.' && *i != '\0') {
375        ++i;
376        ++len;
377    }
378    return len;
379}
380
381/* ****************************************************************************
382 *  Mirrors a dotted IPv4-address string.
383 *	f.e. 192.168.0.1 => 1.0.168.192
384 *
385 *  @param ptr
386 *  @return 0 on success, something else on failure.
387 * ****************************************************************************/
388int
389pico_dns_mirror_addr( char *ip )
390{
391    uint32_t addr = 0;
392
393    /* Convert IPv4-string to network-order 32-bit number */
394    if (pico_string_to_ipv4(ip, &addr) < 0)
395        return -1;
396
397    /* Mirror the 32-bit number */
398    addr = (uint32_t)((uint32_t)((addr & (uint32_t)0xFF000000u) >> 24) |
399                      (uint32_t)((addr & (uint32_t)0xFF0000u) >> 8) |
400                      (uint32_t)((addr & (uint32_t)0xFF00u) << 8) |
401                      (uint32_t)((addr & (uint32_t)0xFFu) << 24));
402
403    return pico_ipv4_to_string(ip, addr);
404}
405
406#ifdef PICO_SUPPORT_IPV6
407/* ****************************************************************************
408 *  Get the ASCII value of the Most Significant Nibble of a byte
409 *
410 *  @param byte Byte you want to extract the MSN from.
411 *  @return The ASCII value of the Most Significant Nibble of the byte
412 * ****************************************************************************/
413static inline char
414dns_ptr_ip6_nibble_lo( uint8_t byte )
415{
416    uint8_t nibble = byte & 0x0f;
417    if (nibble < 10)
418        return (char)(nibble + '0');
419    else
420        return (char)(nibble - 0xa + 'a');
421}
422
423/* ****************************************************************************
424 *  Get the ASCII value of the Least Significant Nibble of a byte
425 *
426 *  @param byte Byte you want to extract the LSN from.
427 *  @return The ASCII value of the Least Significant Nibble of the byte
428 * ****************************************************************************/
429static inline char
430dns_ptr_ip6_nibble_hi( uint8_t byte )
431{
432    uint8_t nibble = (byte & 0xf0u) >> 4u;
433    if (nibble < 10u)
434        return (char)(nibble + '0');
435    else
436        return (char)(nibble - 0xa + 'a');
437}
438
439/* ****************************************************************************
440 *  Convert an IPv6-address in string-format to a IPv6-address in nibble-format.
441 *	Doesn't add a IPv6 ARPA-suffix though.
442 *
443 *  @param ip  IPv6-address stored as a string
444 *  @param dst Destination to store IPv6-address in nibble-format
445 * ****************************************************************************/
446void
447pico_dns_ipv6_set_ptr( const char *ip, char *dst )
448{
449    int i = 0, j = 0;
450    struct pico_ip6 ip6;
451    memset(&ip6, 0, sizeof(struct pico_ip6));
452    pico_string_to_ipv6(ip, ip6.addr);
453    for (i = 15; i >= 0; i--) {
454        if ((j + 3) > 64) return; /* Don't want j to go out of bounds */
455
456        dst[j++] = dns_ptr_ip6_nibble_lo(ip6.addr[i]);
457        dst[j++] = '.';
458        dst[j++] = dns_ptr_ip6_nibble_hi(ip6.addr[i]);
459        dst[j++] = '.';
460    }
461}
462#endif
463
464/* MARK: ^ NAME & IP FUNCTIONS */
465/* MARK: v QUESTION FUNCTIONS */
466
467/* ****************************************************************************
468 *  Calculates the size of a single DNS Question. Void-pointer allows this
469 *  function to be used with pico_tree_size.
470 *
471 *  @param question Void-point to DNS Question
472 *  @return Size in bytes of single DNS Question if it was copied flat.
473 * ****************************************************************************/
474static uint16_t pico_dns_question_size( void *question )
475{
476    uint16_t size = 0;
477    struct pico_dns_question *q = (struct pico_dns_question *)question;
478    if (!q)
479        return 0;
480
481    size = q->qname_length;
482    size = (uint16_t)(size + sizeof(struct pico_dns_question_suffix));
483    return size;
484}
485
486/* ****************************************************************************
487 *  Deletes a single DNS Question.
488 *
489 *  @param question Void-pointer to DNS Question. Can be used with pico_tree_-
490 *					destroy.
491 *  @return Returns 0 on success, something else on failure.
492 * ****************************************************************************/
493int
494pico_dns_question_delete( void **question )
495{
496    struct pico_dns_question **q = (struct pico_dns_question **)question;
497
498    /* Check params */
499    if ((!q) || !(*q)) {
500        pico_err = PICO_ERR_EINVAL;
501        return -1;
502    }
503
504    if ((*q)->qname)
505        PICO_FREE(((*q)->qname));
506
507    if ((*q)->qsuffix)
508        PICO_FREE((*q)->qsuffix);
509
510    PICO_FREE((*q));
511    *question = NULL;
512
513    return 0;
514}
515
516/* ****************************************************************************
517 *  Fills in the DNS question suffix-fields with the correct values.
518 *
519 *  todo: Update pico_dns_client to make the same mechanism possible like with
520 *        filling DNS Resource Record-suffixes.
521 *
522 *  @param suf    Pointer to the suffix member of the DNS question.
523 *  @param qtype  DNS type of the DNS question to be.
524 *  @param qclass DNS class of the DNS question to be.
525 *  @return Returns 0 on success, something else on failure.
526 * ****************************************************************************/
527int
528pico_dns_question_fill_suffix( struct pico_dns_question_suffix *suf,
529                               uint16_t qtype,
530                               uint16_t qclass )
531{
532    if (!suf)
533        return -1;
534
535    suf->qtype = short_be(qtype);
536    suf->qclass = short_be(qclass);
537    return 0;
538}
539
540/* ****************************************************************************
541 *  Fills in the name of the DNS question.
542 *
543 *  @param qname   Pointer-pointer to the name-member of the DNS-question
544 *  @param url     Name in URL format you want to convert to a name in DNS name
545 *				   format. When reverse resolving, only the IP, either IPV4 or
546 *				   IPV6, should be given in string format.
547 *				   f.e. => for IPv4: "192.168.2.1"
548 *						=> for IPv6: "2001:0db8:85a3:0042:1000:8a2e:0370:7334"
549 *  @param qtype   DNS type type of the DNS question to be.
550 *  @param proto   When reverse is true the reverse resolution name will be
551 *				   generated depending on the protocol. Can be either
552 *				   PICO_PROTO_IPV4 or PICO_PROTO_IPV6.
553 *  @param reverse When this is true a reverse resolution name will be generated
554 *				   from the URL.
555 *  @return The eventual length of the generated name, 0 on failure.
556 * ****************************************************************************/
557static uint16_t
558pico_dns_question_fill_name( char **qname,
559                             const char *url,
560                             uint16_t qtype,
561                             uint8_t proto,
562                             uint8_t reverse )
563{
564    uint16_t slen = 0;
565
566    /* Try to convert the URL to an FQDN */
567    if (reverse && qtype == PICO_DNS_TYPE_PTR)
568        *qname = pico_dns_url_to_reverse_qname(url, proto);
569    else {
570        (*qname) = pico_dns_url_to_qname(url);
571    }
572
573    if (!(*qname)) {
574        return 0;
575    }
576
577    slen = (uint16_t)(pico_dns_strlen(*qname) + 1u);
578    return (pico_dns_check_namelen(slen)) ? ((uint16_t)0) : (slen);
579}
580
581/* ****************************************************************************
582 *  Creates a standalone DNS Question with a given name and type.
583 *
584 *  @param url     DNS question name in URL format. Will be converted to DNS
585 *				   name notation format.
586 *  @param len     Will be filled with the total length of the DNS question.
587 *  @param proto   Protocol for which you want to create a question. Can be
588 *				   either PICO_PROTO_IPV4 or PICO_PROTO_IPV6.
589 *  @param qtype   DNS type of the question to be.
590 *  @param qclass  DNS class of the question to be.
591 *  @param reverse When this is true, a reverse resolution name will be
592 *				   generated from the URL
593 *  @return Returns pointer to the created DNS Question on success, NULL on
594 *			failure.
595 * ****************************************************************************/
596struct pico_dns_question *
597pico_dns_question_create( const char *url,
598                          uint16_t *len,
599                          uint8_t proto,
600                          uint16_t qtype,
601                          uint16_t qclass,
602                          uint8_t reverse )
603{
604    struct pico_dns_question *question = NULL;
605    uint16_t slen = 0;
606    int ret = 0;
607
608    /* Check if valid arguments are provided */
609    if (!url || !len) {
610        pico_err = PICO_ERR_EINVAL;
611        return NULL;
612    }
613
614    /* Allocate space for the question and the subfields */
615    if (!(question = PICO_ZALLOC(sizeof(struct pico_dns_question)))) {
616        pico_err = PICO_ERR_ENOMEM;
617        return NULL;
618    }
619
620    /* Fill name field */
621    slen = pico_dns_question_fill_name(&(question->qname), url,
622                                       qtype, proto, reverse);
623    question->qname_length = (uint8_t)(slen);
624    question->proto = proto;
625
626    /* Provide space for the question suffix & try to fill in */
627    question->qsuffix = PICO_ZALLOC(sizeof(struct pico_dns_question_suffix));
628    ret = pico_dns_question_fill_suffix(question->qsuffix, qtype, qclass);
629    if (ret || pico_dns_check_namelen(slen)) {
630        pico_dns_question_delete((void **)&question);
631        return NULL;
632    }
633
634    /* Determine the entire length of the question */
635    *len = (uint16_t)(slen + (uint16_t)sizeof(struct pico_dns_question_suffix));
636
637    return question;
638}
639
640/* ****************************************************************************
641 *  Decompresses the name of a single DNS question.
642 *
643 *  @param question Question you want to decompress the name of
644 *  @param packet   Packet in which the DNS question is contained.
645 *  @return Pointer to original name of the DNS question before decompressing.
646 * ****************************************************************************/
647char *
648pico_dns_question_decompress( struct pico_dns_question *question,
649                              pico_dns_packet *packet )
650{
651    char *qname_original = question->qname;
652
653    /* Try to decompress the question name */
654    if (!(question->qname = pico_dns_decompress_name(question->qname, packet))) {
655        question->qname = qname_original;
656    }
657
658    return qname_original;
659}
660
661
662/* MARK: ^ QUESTION FUNCTIONS */
663/* MARK: v RESOURCE RECORD FUNCTIONS */
664
665/* ****************************************************************************
666 *  Copies the contents of DNS Resource Record to a single flat memory-buffer.
667 *
668 *  @param record      Pointer to DNS record you want to copy flat.
669 *  @param destination Pointer-pointer to flat memory buffer to copy DNS record
670 *					   to. When function returns, this will point to location
671 *					   right after the flat copied DNS Resource Record.
672 *  @return Returns 0 on success, something else on failure.
673 * ****************************************************************************/
674static int
675pico_dns_record_copy_flat( struct pico_dns_record *record,
676                           uint8_t **destination )
677{
678    char *dest_rname = NULL; /* rname destination location */
679    struct pico_dns_record_suffix *dest_rsuffix = NULL; /* rsuffix destin. */
680    uint8_t *dest_rdata = NULL; /* rdata destination location */
681
682    /* Check if there are no NULL-pointers given */
683    if (!record || !destination || !(*destination)) {
684        pico_err = PICO_ERR_EINVAL;
685        return -1;
686    }
687
688    /* Initialise the destination pointers to the right locations */
689    dest_rname = (char *) *destination;
690    dest_rsuffix = (struct pico_dns_record_suffix *)
691                   (dest_rname + record->rname_length);
692    dest_rdata = ((uint8_t *)dest_rsuffix +
693                  sizeof(struct pico_dns_record_suffix));
694
695    /* Copy the rname of the resource record into the flat location */
696    strcpy(dest_rname, record->rname);
697
698    /* Copy the question suffix fields */
699    dest_rsuffix->rtype = record->rsuffix->rtype;
700    dest_rsuffix->rclass = record->rsuffix->rclass;
701    dest_rsuffix->rttl = record->rsuffix->rttl;
702    dest_rsuffix->rdlength = record->rsuffix->rdlength;
703
704    /* Copy the rdata of the resource */
705    memcpy(dest_rdata, record->rdata, short_be(dest_rsuffix->rdlength));
706
707    /* Point to location right after flat resource record */
708    *destination = (uint8_t *)(dest_rdata +
709                               short_be(record->rsuffix->rdlength));
710    return 0;
711}
712
713/* ****************************************************************************
714 *  Calculates the size of a single DNS Resource Record. Void-pointer allows
715 *	this function to be used with pico_tree_size.
716 *
717 *  @param record void-pointer to DNS record you want to know the size of.
718 *  @return Size of single DNS record if it was copied flat.
719 * ****************************************************************************/
720static uint16_t
721pico_dns_record_size( void *record )
722{
723    uint16_t size = 0;
724    struct pico_dns_record *rr = (struct pico_dns_record *)record;
725
726    if (!rr || !(rr->rsuffix))
727        return 0;
728
729    size = rr->rname_length;
730    size = (uint16_t)(size + sizeof(struct pico_dns_record_suffix));
731    size = (uint16_t)(size + short_be(rr->rsuffix->rdlength));
732    return size;
733}
734
735/* ****************************************************************************
736 *  Deletes a single DNS resource record. Void-pointer-pointer allows this
737 *	function to be used with pico_tree_destroy.
738 *
739 *  @param record void-pointer-pointer to DNS record you want to delete.
740 *  @return Returns 0 on success, something else on failure.
741 * ****************************************************************************/
742int
743pico_dns_record_delete( void **record )
744{
745    struct pico_dns_record **rr = (struct pico_dns_record **)record;
746
747    if ((!rr) || !(*rr))
748        return 0;
749
750    if ((*rr)->rname)
751        PICO_FREE((*rr)->rname);
752
753    if ((*rr)->rsuffix)
754        PICO_FREE((*rr)->rsuffix);
755
756    if ((*rr)->rdata)
757        PICO_FREE((*rr)->rdata);
758
759    PICO_FREE((*rr));
760    *record = NULL;
761
762    return 0;
763}
764
765/* ****************************************************************************
766 *  Just copies a resource record hard.
767 *
768 *  @param record DNS record you want to copy
769 *  @return Pointer to copy of DNS record.
770 * ****************************************************************************/
771struct pico_dns_record *
772pico_dns_record_copy( struct pico_dns_record *record )
773{
774    struct pico_dns_record *copy = NULL;
775
776    /* Check params */
777    if (!record || !(record->rname) || !(record->rdata) || !(record->rsuffix)) {
778        pico_err = PICO_ERR_EINVAL;
779        return NULL;
780    }
781
782    /* Provide space for the copy */
783    if (!(copy = PICO_ZALLOC(sizeof(struct pico_dns_record)))) {
784        pico_err = PICO_ERR_ENOMEM;
785        return NULL;
786    }
787
788    /* Provide space for the subfields */
789    copy->rname = PICO_ZALLOC((size_t)record->rname_length);
790    copy->rsuffix = PICO_ZALLOC(sizeof(struct pico_dns_record_suffix));
791    copy->rdata = PICO_ZALLOC((size_t)short_be(record->rsuffix->rdlength));
792    if (!(copy->rname) || !(copy->rsuffix) || !(copy->rdata)) {
793        pico_dns_record_delete((void **)&copy);
794        pico_err = PICO_ERR_ENOMEM;
795        return NULL;
796    }
797
798    /* Fill in the rname field */
799    memcpy((void *)(copy->rname), (void *)(record->rname),
800           (size_t)(record->rname_length));
801    copy->rname_length = record->rname_length;
802
803    /* Fill in the rsuffix fields */
804    copy->rsuffix->rtype = record->rsuffix->rtype;
805    copy->rsuffix->rclass = record->rsuffix->rclass;
806    copy->rsuffix->rttl = record->rsuffix->rttl;
807    copy->rsuffix->rdlength = record->rsuffix->rdlength;
808
809    /* Fill in the rdata field */
810    memcpy(copy->rdata, record->rdata, short_be(record->rsuffix->rdlength));
811
812    return copy;
813}
814
815/* ****************************************************************************
816 *  Fills in the DNS resource record suffix-fields with the correct values.
817 *
818 *  @param suf      Pointer-pointer to rsuffix-member of struct pico_dns_record.
819 *  @param rtype    DNS type of the resource record to be.
820 *  @param rclass   DNS class of the resource record to be.
821 *  @param rttl     DNS ttl of the resource record to be.
822 *  @param rdlength DNS rdlength of the resource record to be.
823 *  @return Returns 0 on success, something else on failure.
824 * ****************************************************************************/
825static int
826pico_dns_record_fill_suffix( struct pico_dns_record_suffix **suf,
827                             uint16_t rtype,
828                             uint16_t rclass,
829                             uint32_t rttl,
830                             uint16_t rdlength )
831{
832    /* Try to provide space for the rsuffix */
833    if (!(*suf = PICO_ZALLOC(sizeof(struct pico_dns_record_suffix)))) {
834        pico_err = PICO_ERR_ENOMEM;
835        return -1;
836    }
837
838    /* Fill in the fields */
839    (*suf)->rtype = short_be(rtype);
840    (*suf)->rclass = short_be(rclass);
841    (*suf)->rttl = long_be(rttl);
842    (*suf)->rdlength = short_be(rdlength);
843
844    return 0;
845}
846
847/* ****************************************************************************
848 *  Fills the data-buffer of a DNS resource record.
849 *
850 *  @param rdata   Pointer-pointer to rdata-member of struct pico_dns_record.
851 *  @param _rdata  Memory buffer with data to insert in the resource record. If
852 *				   data should contain a DNS name, the name in the databuffer
853 *				   needs to be in URL-format.
854 *  @param datalen The exact length in bytes of the _rdata-buffer. If data of
855 *				   record should contain a DNS name, datalen needs to be
856 *				   pico_dns_strlen(_rdata).
857 *  @param rtype   DNS type of the resource record to be
858 *  @return Returns 0 on failure, length of filled in rdata-member on success.
859 *			Can differ from datalen-param because of URL to DNS Name conversion.
860 * ****************************************************************************/
861static uint16_t
862pico_dns_record_fill_rdata( uint8_t **rdata,
863                            void *_rdata,
864                            uint16_t datalen,
865                            uint16_t rtype )
866{
867    uint16_t _datalen = 0;
868
869    /* If type is PTR, rdata will be a DNS name in URL format */
870    if (rtype == PICO_DNS_TYPE_PTR) {
871        _datalen = (uint16_t)(datalen + 2u);
872        if (!(*rdata = (uint8_t *)pico_dns_url_to_qname(_rdata))) {
873            pico_err = PICO_ERR_ENOMEM;
874            return 0;
875        }
876    } else {
877        /* Otherwise just copy in the databuffer */
878        if (datalen == 0) {
879            return datalen;
880        }
881
882        _datalen = datalen;
883        if (!(*rdata = (uint8_t *)PICO_ZALLOC((size_t)datalen))) {
884            pico_err = PICO_ERR_ENOMEM;
885            return 0;
886        }
887
888        memcpy((void *)*rdata, (void *)_rdata, datalen);
889    }
890
891    return _datalen;
892}
893
894/* ****************************************************************************
895 *  Create a standalone DNS Resource Record with a given name.
896 *
897 *  @param url     DNS rrecord name in URL format. Will be converted to DNS
898 *                 name notation format.
899 *  @param _rdata  Memory buffer with data to insert in the resource record. If
900 *				   data should contain a DNS name, the name in the databuffer
901 *				   needs to be in URL-format.
902 *  @param datalen The exact length in bytes of the _rdata-buffer. If data of
903 *				   record should contain a DNS name, datalen needs to be
904 *				   pico_dns_strlen(_rdata).
905 *  @param len     Will be filled with the total length of the DNS rrecord.
906 *  @param rtype   DNS type of the resource record to be.
907 *  @param rclass  DNS class of the resource record to be.
908 *  @param rttl    DNS ttl of the resource record to be.
909 *  @return Returns pointer to the created DNS Resource Record
910 * ****************************************************************************/
911struct pico_dns_record *
912pico_dns_record_create( const char *url,
913                        void *_rdata,
914                        uint16_t datalen,
915                        uint16_t *len,
916                        uint16_t rtype,
917                        uint16_t rclass,
918                        uint32_t rttl )
919{
920    struct pico_dns_record *record = NULL;
921    uint16_t slen = (uint16_t)(pico_dns_strlen(url) + 2u);
922    int ret = 0;
923
924    /* Check params */
925    if (pico_dns_check_namelen(slen) || !_rdata || !len) {
926        pico_err = PICO_ERR_EINVAL;
927        return NULL;
928    }
929
930    /* Allocate space for the record and subfields */
931    if (!(record = PICO_ZALLOC(sizeof(struct pico_dns_record)))) {
932        pico_err = PICO_ERR_ENOMEM;
933        return NULL;
934    }
935
936    /* Provide space and convert the URL to a DNS name */
937    record->rname = pico_dns_url_to_qname(url);
938    record->rname_length = slen;
939
940    /* Provide space & fill in the rdata field */
941    datalen = pico_dns_record_fill_rdata(&(record->rdata), _rdata,
942                                         datalen, rtype);
943
944    /* Provide space & fill in the rsuffix */
945    ret = pico_dns_record_fill_suffix(&(record->rsuffix), rtype, rclass, rttl,
946                                      datalen);
947
948    /* Check if everything succeeded */
949    if (!(record->rname) || ret) {
950        pico_dns_record_delete((void **)&record);
951        return NULL;
952    }
953
954    /* Determine the complete length of resource record */
955    *len = (uint16_t)(slen + sizeof(struct pico_dns_record_suffix) + datalen);
956    return record;
957}
958
959/* ****************************************************************************
960 *  Decompresses the name of single DNS record.
961 *
962 *  @param record DNS record to decompress the name of.
963 *  @param packet Packet in which is DNS record is present
964 *  @return Pointer to original name of the DNS record before decompressing.
965 * ****************************************************************************/
966char *
967pico_dns_record_decompress( struct pico_dns_record *record,
968                            pico_dns_packet *packet )
969{
970    char *rname_original = record->rname;
971
972    /* Try to decompress the record name */
973    if (!(record->rname = pico_dns_decompress_name(record->rname, packet))) {
974        record->rname = rname_original;
975    }
976
977    return rname_original;
978}
979
980static int pico_tolower(int c)
981{
982    if ((c >= 'A')  && (c <= 'Z'))
983        c += 'a' - 'A';
984
985    return c;
986}
987
988/* MARK: ^ RESOURCE RECORD FUNCTIONS */
989/* MARK: v COMPARING */
990
991/* ****************************************************************************
992 *  Compares two databuffers against each other.
993 *
994 *  @param a          1st Memory buffer to compare
995 *  @param b          2nd Memory buffer to compare
996 *  @param rdlength_a Length of 1st memory buffer
997 *  @param rdlength_b Length of 2nd memory buffer
998 *  @param caseinsensitive Whether or not the bytes are compared
999 *                         case-insensitive. Should be either
1000 *                         PICO_DNS_CASE_SENSITIVE or PICO_DNS_CASE_INSENSITIVE
1001 *  @return 0 when the buffers are equal, returns difference when they're not.
1002 * ****************************************************************************/
1003int
1004pico_dns_rdata_cmp( uint8_t *a, uint8_t *b,
1005                    uint16_t rdlength_a, uint16_t rdlength_b, uint8_t caseinsensitive )
1006{
1007    uint16_t i = 0;
1008    uint16_t slen = 0;
1009    int dif = 0;
1010
1011    /* Check params */
1012    if (!a || !b) {
1013        if (!a && !b)
1014            return 0;
1015
1016        pico_err = PICO_ERR_EINVAL;
1017        return -1;
1018    }
1019
1020    /* Determine the smallest length */
1021    slen = rdlength_a;
1022    if (rdlength_b < slen)
1023        slen = rdlength_b;
1024
1025    /* loop over slen */
1026    if(caseinsensitive) {
1027        for (i = 0; i < slen; i++) {
1028            if ((dif = pico_tolower((int)a[i]) - pico_tolower((int)b[i]))) {
1029                return dif;
1030            }
1031        }
1032    }else{
1033        for (i = 0; i < slen; i++) {
1034            if ((dif = (int)a[i] - (int)b[i])) {
1035                return dif;
1036            }
1037        }
1038    }
1039
1040    /* Return difference of buffer lengths */
1041    return (int)((int)rdlength_a - (int)rdlength_b);
1042}
1043
1044/* ****************************************************************************
1045 *  Compares 2 DNS questions
1046 *
1047 *  @param qa DNS question A as a void-pointer (for pico_tree)
1048 *  @param qb DNS question A as a void-pointer (for pico_tree)
1049 *  @return 0 when questions are equal, returns difference when they're not.
1050 * ****************************************************************************/
1051int
1052pico_dns_question_cmp( void *qa,
1053                       void *qb )
1054{
1055    int dif = 0;
1056    uint16_t at = 0, bt = 0;
1057    struct pico_dns_question *a = (struct pico_dns_question *)qa;
1058    struct pico_dns_question *b = (struct pico_dns_question *)qb;
1059
1060    /* Check params */
1061    if (!a || !b) {
1062        pico_err = PICO_ERR_EINVAL;
1063        return -1;
1064    }
1065
1066    /* First, compare the qtypes */
1067    at = short_be(a->qsuffix->qtype);
1068    bt = short_be(b->qsuffix->qtype);
1069    if ((dif = (int)((int)at - (int)bt)))
1070        return dif;
1071
1072    /* Then compare qnames */
1073    return pico_dns_rdata_cmp((uint8_t *)a->qname, (uint8_t *)b->qname,
1074                              pico_dns_strlen(a->qname),
1075                              pico_dns_strlen(b->qname), PICO_DNS_CASE_INSENSITIVE);
1076}
1077
1078/* ****************************************************************************
1079 *  Compares 2 DNS records by type and name only
1080 *
1081 *  @param ra DNS record A as a void-pointer (for pico_tree)
1082 *  @param rb DNS record B as a void-pointer (for pico_tree)
1083 *  @return 0 when name and type of records are equal, returns difference when
1084 *			they're not.
1085 * ****************************************************************************/
1086int
1087pico_dns_record_cmp_name_type( void *ra,
1088                               void *rb )
1089{
1090    int dif;
1091    uint16_t at = 0, bt = 0;
1092    struct pico_dns_record *a = (struct pico_dns_record *)ra;
1093    struct pico_dns_record *b = (struct pico_dns_record *)rb;
1094
1095    /* Check params */
1096    if (!a || !b) {
1097        pico_err = PICO_ERR_EINVAL;
1098        return -1;
1099    }
1100
1101    /* First, compare the rrtypes */
1102    at = short_be(a->rsuffix->rtype);
1103    bt = short_be(b->rsuffix->rtype);
1104    if ((dif = (int)((int)at - (int)bt)))
1105        return dif;
1106
1107    /* Then compare names */
1108    return pico_dns_rdata_cmp((uint8_t *)(a->rname), (uint8_t *)(b->rname),
1109                              (uint16_t)strlen(a->rname),
1110                              (uint16_t)strlen(b->rname), PICO_DNS_CASE_INSENSITIVE);
1111}
1112
1113/* ****************************************************************************
1114 *  Compares 2 DNS records by type, name AND rdata for a truly unique result
1115 *
1116 *  @param ra DNS record A as a void-pointer (for pico_tree)
1117 *  @param rb DNS record B as a void-pointer (for pico_tree)
1118 *  @return 0 when records are equal, returns difference when they're not
1119 * ****************************************************************************/
1120int
1121pico_dns_record_cmp( void *ra,
1122                     void *rb )
1123{
1124    int dif = 0;
1125    struct pico_dns_record *a = (struct pico_dns_record *)ra;
1126    struct pico_dns_record *b = (struct pico_dns_record *)rb;
1127
1128    /* Check params */
1129    if (!a || !b) {
1130        pico_err = PICO_ERR_EINVAL;
1131        return -1;
1132    }
1133
1134    /* Compare type and name */
1135    if ((dif = pico_dns_record_cmp_name_type(a, b)))
1136        return dif;
1137
1138    /* Then compare rdata */
1139    return pico_dns_rdata_cmp(a->rdata, b->rdata,
1140                              short_be(a->rsuffix->rdlength),
1141                              short_be(b->rsuffix->rdlength), PICO_DNS_CASE_SENSITIVE);
1142}
1143
1144/* MARK: ^ COMPARING */
1145/* MARK: v PICO_TREE */
1146
1147/* ****************************************************************************
1148 *  Erases a pico_tree entirely.
1149 *
1150 *  @param tree        Pointer to a pico_tree-instance
1151 *  @param node_delete Helper-function for type-specific deleting.
1152 *  @return Returns 0 on success, something else on failure.
1153 * ****************************************************************************/
1154int
1155pico_tree_destroy( struct pico_tree *tree, int (*node_delete)(void **))
1156{
1157    struct pico_tree_node *node = NULL, *next = NULL;
1158    void *item = NULL;
1159
1160    /* Check params */
1161    if (!tree) {
1162        pico_err = PICO_ERR_EINVAL;
1163        return -1;
1164    }
1165
1166    pico_tree_foreach_safe(node, tree, next) {
1167        item = node->keyValue;
1168        pico_tree_delete(tree, node->keyValue);
1169        if (item && node_delete) {
1170            node_delete((void **)&item);
1171        }
1172    }
1173
1174    return 0;
1175}
1176
1177/* ****************************************************************************
1178 *  Calculates the size in bytes of all the nodes contained in the tree summed
1179 *  up. And gets the amount of items in the tree as well.
1180 *
1181 *  @param tree      Pointer to pico_tree-instance
1182 *  @param size      Will get filled with the size of all the nodes summed up.
1183 *					 Make sure you clear out (set to 0) this param before you
1184 *					 call this function because it doesn't happen inside and
1185 *					 each size will be added to the initial value.
1186 *  @param node_size Helper-function for type-specific size-determination
1187 *  @return Amount of items in the tree.
1188 * ****************************************************************************/
1189static uint16_t
1190pico_tree_size( struct pico_tree *tree,
1191                uint16_t *size,
1192                uint16_t (*node_size)(void *))
1193{
1194    struct pico_tree_node *node = NULL;
1195    void *node_item = NULL;
1196    uint16_t count = 0;
1197
1198    /* Check params */
1199    if (!tree || !size) {
1200        pico_err = PICO_ERR_EINVAL;
1201        return 0;
1202    }
1203
1204    /* Add up the node sizes */
1205    pico_tree_foreach(node, tree) {
1206        if ((node_item = node->keyValue)) {
1207            *size = (uint16_t)((*size) + node_size(node_item));
1208            count++;
1209        }
1210    }
1211
1212    return count;
1213}
1214
1215/* ****************************************************************************
1216 *  Determines the amount of nodes in a pico_tere
1217 *
1218 *  @param tree Pointer to pico_tree-instance
1219 *  @return Amount of items in the tree.
1220 * ****************************************************************************/
1221uint16_t
1222pico_tree_count( struct pico_tree *tree )
1223{
1224    struct pico_tree_node *node = NULL;
1225    uint16_t count = 0;
1226
1227    pico_tree_foreach(node, tree) {
1228        if (node->keyValue)
1229            count++;
1230    }
1231
1232    return count;
1233}
1234
1235/* ****************************************************************************
1236 *  Deletes all the questions with given DNS name from a pico_tree
1237 *
1238 *  @param qtree Pointer to pico_tree-instance which contains DNS questions
1239 *  @param name  Name of the questions you want to delete
1240 *  @return Returns 0 on success, something else on failure.
1241 * ****************************************************************************/
1242int
1243pico_dns_qtree_del_name( struct pico_tree *qtree,
1244                         const char *name )
1245{
1246    struct pico_tree_node *node = NULL, *next = NULL;
1247    struct pico_dns_question *question = NULL;
1248
1249    /* Check params */
1250    if (!qtree || !name) {
1251        pico_err = PICO_ERR_EINVAL;
1252        return -1;
1253    }
1254
1255    /* Iterate over tree and delete every node with given name */
1256    pico_tree_foreach_safe(node, qtree, next) {
1257        question = (struct pico_dns_question *)node->keyValue;
1258        if ((question) && (strcasecmp(question->qname, name) == 0)) {
1259            question = pico_tree_delete(qtree, (void *)question);
1260            pico_dns_question_delete((void **)&question);
1261        }
1262    }
1263
1264    return 0;
1265}
1266
1267/* ****************************************************************************
1268 *  Checks whether a question with given name is in the tree or not.
1269 *
1270 *  @param qtree Pointer to pico_tree-instance which contains DNS questions
1271 *  @param name  Name you want to check for
1272 *  @return 1 when the name is present in the qtree, 0 when it's not.
1273 * ****************************************************************************/
1274int
1275pico_dns_qtree_find_name( struct pico_tree *qtree,
1276                          const char *name )
1277{
1278    struct pico_tree_node *node = NULL;
1279    struct pico_dns_question *question = NULL;
1280
1281    /* Check params */
1282    if (!qtree || !name) {
1283        pico_err = PICO_ERR_EINVAL;
1284        return 0;
1285    }
1286
1287    /* Iterate over tree and compare names */
1288    pico_tree_foreach(node, qtree) {
1289        question = (struct pico_dns_question *)node->keyValue;
1290        if ((question) && (strcasecmp(question->qname, name) == 0))
1291            return 1;
1292    }
1293
1294    return 0;
1295}
1296
1297/* MARK: ^ PICO_TREE */
1298/* MARK: v DNS PACKET FUNCTIONS */
1299
1300/* ****************************************************************************
1301 *  Fills the header section of a DNS packet with the correct flags and section
1302 *  -counts.
1303 *
1304 *  @param hdr     Header to fill in.
1305 *  @param qdcount Amount of questions added to the packet
1306 *  @param ancount Amount of answer records added to the packet
1307 *  @param nscount Amount of authority records added to the packet
1308 *  @param arcount Amount of additional records added to the packet
1309 * ****************************************************************************/
1310void
1311pico_dns_fill_packet_header( struct pico_dns_header *hdr,
1312                             uint16_t qdcount,
1313                             uint16_t ancount,
1314                             uint16_t nscount,
1315                             uint16_t arcount )
1316{
1317    /* ID should be filled by caller */
1318
1319    if(qdcount > 0) { /* Questions present? Make it a query */
1320        hdr->qr = PICO_DNS_QR_QUERY;
1321        hdr->aa = PICO_DNS_AA_NO_AUTHORITY;
1322    } else { /* No questions present? Make it an answer*/
1323        hdr->qr = PICO_DNS_QR_RESPONSE;
1324        hdr->aa = PICO_DNS_AA_IS_AUTHORITY;
1325    }
1326
1327    /* Fill in the flags and the fields */
1328    hdr->opcode = PICO_DNS_OPCODE_QUERY;
1329    hdr->tc = PICO_DNS_TC_NO_TRUNCATION;
1330    hdr->rd = PICO_DNS_RD_IS_DESIRED;
1331    hdr->ra = PICO_DNS_RA_NO_SUPPORT;
1332    hdr->z = 0; /* Z, AD, CD are 0 */
1333    hdr->rcode = PICO_DNS_RCODE_NO_ERROR;
1334    hdr->qdcount = short_be(qdcount);
1335    hdr->ancount = short_be(ancount);
1336    hdr->nscount = short_be(nscount);
1337    hdr->arcount = short_be(arcount);
1338}
1339
1340/* ****************************************************************************
1341 *  Fills a single DNS resource record section of a DNS packet.
1342 *
1343 *  @param rtree Tree that contains the DNS resource records.
1344 *  @param dest  Pointer-pointer to location where you want to insert records.
1345 *				 Will point to location after current section on return.
1346 *  @return 0 on success, something else on failure.
1347 * ****************************************************************************/
1348static int
1349pico_dns_fill_packet_rr_section( struct pico_tree *rtree,
1350                                 uint8_t **dest )
1351{
1352    struct pico_tree_node *node = NULL;
1353    struct pico_dns_record *record = NULL;
1354
1355    pico_tree_foreach(node, rtree) {
1356        record = node->keyValue;
1357        if ((record) && pico_dns_record_copy_flat(record, dest)) {
1358            dns_dbg("Could not copy record into Answer Section!\n");
1359            return -1;
1360        }
1361    }
1362    return 0;
1363}
1364
1365/* ****************************************************************************
1366 *  Fills the resource record sections of a DNS packet with provided record-
1367 *  trees.
1368 *
1369 *  @param packet Packet you want to fill
1370 *  @param qtree  Question tree to determine where the rrsections begin.
1371 *  @param antree DNS records to put in Answer section
1372 *  @param nstree DNS records to put in Authority section
1373 *  @param artree DNS records to put in Additional section
1374 *  @return 0 on success, something else on failure.
1375 * ****************************************************************************/
1376static int
1377pico_dns_fill_packet_rr_sections( pico_dns_packet *packet,
1378                                  struct pico_tree *qtree,
1379                                  struct pico_tree *antree,
1380                                  struct pico_tree *nstree,
1381                                  struct pico_tree *artree )
1382{
1383    int anret = 0, nsret = 0, arret = 0;
1384    uint16_t temp = 0;
1385    uint8_t *destination = NULL;
1386
1387    /* Check params */
1388    if (!packet || !qtree || !antree || !nstree || !artree) {
1389        pico_err = PICO_ERR_EINVAL;
1390        return -1;
1391    }
1392
1393    /* Initialise the destination pointers before iterating */
1394    destination = (uint8_t *)packet + sizeof(struct pico_dns_header);
1395    pico_tree_size(qtree, &temp, &pico_dns_question_size);
1396    destination = destination + temp;
1397
1398    /* Iterate over ANSWERS */
1399    anret = pico_dns_fill_packet_rr_section(antree, &destination);
1400
1401    /* Iterate over AUTHORITIES */
1402    nsret = pico_dns_fill_packet_rr_section(nstree, &destination);
1403
1404    /* Iterate over ADDITIONALS */
1405    arret = pico_dns_fill_packet_rr_section(artree, &destination);
1406
1407    if (anret || nsret || arret)
1408        return -1;
1409
1410    return 0;
1411}
1412
1413/* ****************************************************************************
1414 *  Fills the question section of a DNS packet with provided questions in the
1415 *  tree.
1416 *
1417 *  @param packet Packet you want to fill
1418 *  @param qtree  Question tree with question you want to insert
1419 *  @return 0 on success, something else on failure.
1420 * ****************************************************************************/
1421static int
1422pico_dns_fill_packet_question_section( pico_dns_packet *packet,
1423                                       struct pico_tree *qtree )
1424{
1425    struct pico_tree_node *node = NULL;
1426    struct pico_dns_question *question = NULL;
1427    struct pico_dns_question_suffix *dest_qsuffix = NULL;
1428    char *dest_qname = NULL;
1429
1430    /* Check params */
1431    if (!packet || !qtree) {
1432        pico_err = PICO_ERR_EINVAL;
1433        return -1;
1434    }
1435
1436    /* Initialise pointer */
1437    dest_qname = (char *)((char *)packet + sizeof(struct pico_dns_header));
1438
1439    pico_tree_foreach(node, qtree) {
1440        question = node->keyValue;
1441        if (question) {
1442            /* Copy the name */
1443            memcpy(dest_qname, question->qname, question->qname_length);
1444
1445            /* Copy the suffix */
1446            dest_qsuffix = (struct pico_dns_question_suffix *)
1447                           (dest_qname + question->qname_length);
1448            dest_qsuffix->qtype = question->qsuffix->qtype;
1449            dest_qsuffix->qclass = question->qsuffix->qclass;
1450
1451            /* Move to next question */
1452            dest_qname = (char *)((char *)dest_qsuffix +
1453                                  sizeof(struct pico_dns_question_suffix));
1454        }
1455    }
1456    return 0;
1457}
1458
1459/* ****************************************************************************
1460 *  Looks for a name somewhere else in packet, more specifically between the
1461 *  beginning of the data buffer and the name itself.
1462 * ****************************************************************************/
1463static uint8_t *
1464pico_dns_packet_compress_find_ptr( uint8_t *name,
1465                                   uint8_t *data,
1466                                   uint16_t len )
1467{
1468    uint8_t *iterator = NULL;
1469
1470    /* Check params */
1471    if (!name || !data || !len)
1472        return NULL;
1473
1474    if ((name < data) || (name > (data + len)))
1475        return NULL;
1476
1477    iterator = data;
1478
1479    /* Iterate from the beginning of data up until the name-ptr */
1480    while (iterator < name) {
1481        /* Compare in each iteration of current name is equal to a section of
1482           the DNS packet and if so return the pointer to that section */
1483        if (memcmp((void *)iterator++, (void *)name,
1484                   pico_dns_strlen((char *)name) + 1u) == 0)
1485            return (iterator - 1);
1486    }
1487    return NULL;
1488}
1489
1490/* ****************************************************************************
1491 *  Compresses a single name by looking for the same name somewhere else in the
1492 *  packet-buffer.
1493 * ****************************************************************************/
1494static int
1495pico_dns_packet_compress_name( uint8_t *name,
1496                               uint8_t *packet,
1497                               uint16_t *len)
1498{
1499    uint8_t *lbl_iterator = NULL;    /* To iterate over labels */
1500    uint8_t *compression_ptr = NULL; /* PTR to somewhere else in the packet */
1501    uint8_t *offset = NULL;          /* PTR after compression pointer */
1502    uint8_t *ptr_after_str = NULL;
1503    uint8_t *last_byte = NULL;
1504    uint8_t *i = NULL;
1505    uint16_t ptr = 0;
1506    uint16_t difference = 0;
1507
1508    /* Check params */
1509    if (!name || !packet || !len) {
1510        pico_err = PICO_ERR_EINVAL;
1511        return -1;
1512    }
1513
1514    if ((name < packet) || (name > (packet + *len))) {
1515        dns_dbg("Name ptr OOB. name: %p max: %p\n", name, packet + *len);
1516        pico_err = PICO_ERR_EINVAL;
1517        return -1;
1518    }
1519
1520    /* Try to compress name */
1521    lbl_iterator = name;
1522    while (lbl_iterator != '\0') {
1523        /* Try to find a compression pointer with current name */
1524        compression_ptr = pico_dns_packet_compress_find_ptr(lbl_iterator,
1525                                                            packet + 12, *len);
1526        /* If name can be compressed */
1527        if (compression_ptr) {
1528            /* Point to place after current string */
1529            ptr_after_str = lbl_iterator + strlen((char *)lbl_iterator) + 1u;
1530
1531            /* Calculate the compression pointer value */
1532            ptr = (uint16_t)(compression_ptr - packet);
1533
1534            /* Set the compression pointer in the packet */
1535            *lbl_iterator = (uint8_t)(0xC0 | (uint8_t)(ptr >> 8));
1536            *(lbl_iterator + 1) = (uint8_t)(ptr & 0xFF);
1537
1538            /* Move up the rest of the packet data to right after the pointer */
1539            offset = lbl_iterator + 2;
1540
1541            /* Move up left over data */
1542            difference = (uint16_t)(ptr_after_str - offset);
1543            last_byte = packet + *len;
1544            for (i = ptr_after_str; i < last_byte; i++) {
1545                *((uint8_t *)(i - difference)) = *i;
1546            }
1547            /* Update length */
1548            *len = (uint16_t)(*len - difference);
1549            break;
1550        }
1551
1552        /* Move to next length label */
1553        lbl_iterator = lbl_iterator + *(lbl_iterator) + 1;
1554    }
1555    return 0;
1556}
1557
1558/* ****************************************************************************
1559 *  Utility function compress a record section
1560 * ****************************************************************************/
1561static int
1562pico_dns_compress_record_sections( uint16_t qdcount, uint16_t count,
1563                                   uint8_t *buf, uint8_t **iterator,
1564                                   uint16_t *len )
1565{
1566    struct pico_dns_record_suffix *rsuffix = NULL;
1567    uint8_t *_iterator = NULL;
1568    uint16_t i = 0;
1569
1570    /* Check params */
1571    if (!iterator || !(*iterator) || !buf || !len) {
1572        pico_err = PICO_ERR_EINVAL;
1573        return -1;
1574    }
1575
1576    _iterator = *iterator;
1577
1578    for (i = 0; i < count; i++) {
1579        if (qdcount || i)
1580            pico_dns_packet_compress_name(_iterator, buf, len);
1581
1582        /* To get rdlength */
1583        rsuffix = (struct pico_dns_record_suffix *)
1584                  (_iterator + pico_dns_namelen_comp((char *)_iterator) + 1u);
1585
1586        /* Move to next res record */
1587        _iterator = ((uint8_t *)rsuffix +
1588                     sizeof(struct pico_dns_record_suffix) +
1589                     short_be(rsuffix->rdlength));
1590    }
1591    *iterator = _iterator;
1592    return 0;
1593}
1594
1595/* ****************************************************************************
1596 *  Applies DNS name compression to an entire DNS packet
1597 * ****************************************************************************/
1598static int
1599pico_dns_packet_compress( pico_dns_packet *packet, uint16_t *len )
1600{
1601    uint8_t *packet_buf = NULL;
1602    uint8_t *iterator = NULL;
1603    uint16_t qdcount = 0, rcount = 0, i = 0;
1604
1605    /* Check params */
1606    if (!packet || !len) {
1607        pico_err = PICO_ERR_EINVAL;
1608        return -1;
1609    }
1610
1611    packet_buf = (uint8_t *)packet;
1612
1613    /* Temporarily store the question & record counts */
1614    qdcount = short_be(packet->qdcount);
1615    rcount = (uint16_t)(rcount + short_be(packet->ancount));
1616    rcount = (uint16_t)(rcount + short_be(packet->nscount));
1617    rcount = (uint16_t)(rcount + short_be(packet->arcount));
1618
1619    /* Move past the DNS packet header */
1620    iterator = (uint8_t *)((uint8_t *) packet + 12u);
1621
1622    /* Start with the questions */
1623    for (i = 0; i < qdcount; i++) {
1624        if(i) { /* First question can't be compressed */
1625            pico_dns_packet_compress_name(iterator, packet_buf, len);
1626        }
1627
1628        /* Move to next question */
1629        iterator = (uint8_t *)(iterator +
1630                               pico_dns_namelen_comp((char *)iterator) +
1631                               sizeof(struct pico_dns_question_suffix) + 1u);
1632    }
1633    /* Then onto the answers */
1634    pico_dns_compress_record_sections(qdcount, rcount, packet_buf, &iterator,
1635                                      len);
1636    return 0;
1637}
1638
1639/* ****************************************************************************
1640 *  Calculates how big a packet needs be in order to store all the questions &
1641 *  records in the tree. Also determines the amount of questions and records.
1642 *
1643 *  @param qtree   Tree with Questions.
1644 *  @param antree  Tree with Answer Records.
1645 *  @param nstree  Tree with Authority Records.
1646 *  @param artree  Tree with Additional Records.
1647 *  @param qdcount Pointer to var to store amount of questions
1648 *  @param ancount Pointer to var to store amount of answers.
1649 *  @param nscount Pointer to var to store amount of authorities.
1650 *  @param arcount Pointer to var to store amount of additionals.
1651 *  @return Returns the total length that the DNS packet needs to be.
1652 * ****************************************************************************/
1653static uint16_t
1654pico_dns_packet_len( struct pico_tree *qtree,
1655                     struct pico_tree *antree,
1656                     struct pico_tree *nstree,
1657                     struct pico_tree *artree,
1658                     uint8_t *qdcount, uint8_t *ancount,
1659                     uint8_t *nscount, uint8_t *arcount )
1660{
1661    uint16_t len = (uint16_t) sizeof(pico_dns_packet);
1662
1663    /* Check params */
1664    if (!qtree || !antree || !nstree || !artree) {
1665        pico_err = PICO_ERR_EINVAL;
1666        return 0;
1667    }
1668
1669    *qdcount = (uint8_t)pico_tree_size(qtree, &len, &pico_dns_question_size);
1670    *ancount = (uint8_t)pico_tree_size(antree, &len, &pico_dns_record_size);
1671    *nscount = (uint8_t)pico_tree_size(nstree, &len, &pico_dns_record_size);
1672    *arcount = (uint8_t)pico_tree_size(artree, &len, &pico_dns_record_size);
1673    return len;
1674}
1675
1676/* ****************************************************************************
1677 *  Generic packet creation utility that just creates a DNS packet with given
1678 *  questions and resource records to put in the Resource Record Sections. If a
1679 *  NULL-pointer is provided for a certain tree, no records will be added to
1680 *  that particular section of the packet.
1681 *
1682 *  @param qtree  DNS Questions to put in the Question Section.
1683 *  @param antree DNS Records to put in the Answer Section.
1684 *  @param nstree DNS Records to put in the Authority Section.
1685 *  @param artree DNS Records to put in the Additional Section.
1686 *  @param len    Will get fill with the entire size of the packet
1687 *  @return Pointer to created DNS packet
1688 * ****************************************************************************/
1689static pico_dns_packet *
1690pico_dns_packet_create( struct pico_tree *qtree,
1691                        struct pico_tree *antree,
1692                        struct pico_tree *nstree,
1693                        struct pico_tree *artree,
1694                        uint16_t *len )
1695{
1696    PICO_DNS_QTREE_DECLARE(_qtree);
1697    PICO_DNS_RTREE_DECLARE(_antree);
1698    PICO_DNS_RTREE_DECLARE(_nstree);
1699    PICO_DNS_RTREE_DECLARE(_artree);
1700    pico_dns_packet *packet = NULL;
1701    uint8_t qdcount = 0, ancount = 0, nscount = 0, arcount = 0;
1702
1703    /* Set default vector, if arguments are NULL-pointers */
1704    _qtree = (qtree) ? (*qtree) : (_qtree);
1705    _antree = (antree) ? (*antree) : (_antree);
1706    _nstree = (nstree) ? (*nstree) : (_nstree);
1707    _artree = (artree) ? (*artree) : (_artree);
1708
1709    /* Get the size of the entire packet and determine the header counters */
1710    *len = pico_dns_packet_len(&_qtree, &_antree, &_nstree, &_artree,
1711                               &qdcount, &ancount, &nscount, &arcount);
1712
1713    /* Provide space for the entire packet */
1714    if (!(packet = PICO_ZALLOC(*len))) {
1715        pico_err = PICO_ERR_ENOMEM;
1716        return NULL;
1717    }
1718
1719    /* Fill the Question Section with questions */
1720    if (qtree && pico_tree_count(&_qtree) != 0) {
1721        if (pico_dns_fill_packet_question_section(packet, &_qtree)) {
1722            dns_dbg("Could not fill Question Section correctly!\n");
1723            PICO_FREE(packet);
1724            return NULL;
1725        }
1726    }
1727
1728    /* Fill the Resource Record Sections with resource records */
1729    if (pico_dns_fill_packet_rr_sections(packet, &_qtree, &_antree,
1730                                         &_nstree, &_artree)) {
1731        dns_dbg("Could not fill Resource Record Sections correctly!\n");
1732        PICO_FREE(packet);
1733        return NULL;
1734    }
1735
1736    /* Fill the DNS packet header and try to compress */
1737    pico_dns_fill_packet_header(packet, qdcount, ancount, nscount, arcount);
1738    pico_dns_packet_compress(packet, len);
1739
1740    return packet;
1741}
1742
1743/* ****************************************************************************
1744 *  Creates a DNS Query packet with given question and resource records to put
1745 *  the Resource Record Sections. If a NULL-pointer is provided for a certain
1746 *  tree, no records will be added to that particular section of the packet.
1747 *
1748 *  @param qtree  DNS Questions to put in the Question Section
1749 *  @param antree DNS Records to put in the Answer Section
1750 *  @param nstree DNS Records to put in the Authority Section
1751 *  @param artree DNS Records to put in the Additional Section
1752 *  @param len    Will get filled with the entire size of the packet
1753 *  @return Pointer to created DNS packet
1754 * ****************************************************************************/
1755pico_dns_packet *
1756pico_dns_query_create( struct pico_tree *qtree,
1757                       struct pico_tree *antree,
1758                       struct pico_tree *nstree,
1759                       struct pico_tree *artree,
1760                       uint16_t *len )
1761{
1762    return pico_dns_packet_create(qtree, antree, nstree, artree, len);
1763}
1764
1765/* ****************************************************************************
1766 *  Creates a DNS Answer packet with given resource records to put in the
1767 *  Resource Record Sections. If a NULL-pointer is provided for a certain tree,
1768 *  no records will be added to that particular section of the packet.
1769 *
1770 *  @param antree DNS Records to put in the Answer Section
1771 *  @param nstree DNS Records to put in the Authority Section
1772 *  @param artree DNS Records to put in the Additional Section
1773 *  @param len    Will get filled with the entire size of the packet
1774 *  @return Pointer to created DNS packet.
1775 * ****************************************************************************/
1776pico_dns_packet *
1777pico_dns_answer_create( struct pico_tree *antree,
1778                        struct pico_tree *nstree,
1779                        struct pico_tree *artree,
1780                        uint16_t *len )
1781{
1782    return pico_dns_packet_create(NULL, antree, nstree, artree, len);
1783}
1784/* MARK: ^ DNS PACKET FUNCTIONS */
1785