1/*********************************************************************
2   PicoTCP. Copyright (c) 2014-2017 Altran Intelligent Systems. Some rights reserved.
3   See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
4   .
5   Author: Toon Stegen, Jelle De Vleeschouwer
6 *********************************************************************/
7#include "pico_config.h"
8#include "pico_stack.h"
9#include "pico_addressing.h"
10#include "pico_socket.h"
11#include "pico_ipv4.h"
12#include "pico_ipv6.h"
13#include "pico_tree.h"
14#include "pico_mdns.h"
15
16#ifdef PICO_SUPPORT_MDNS
17
18/* --- Debugging --- */
19#ifdef DEBUG_MDNS
20#define mdns_dbg dbg
21#else
22#define mdns_dbg(...) do {} while(0)
23#endif
24
25#define PICO_MDNS_QUERY_TIMEOUT (10000) /* Ten seconds */
26#define PICO_MDNS_RR_TTL_TICK (1000)    /* One second */
27
28/* mDNS MTU size */
29#define PICO_MDNS_MAXBUF (1400u)
30
31/* --- Cookie flags --- */
32#define PICO_MDNS_PACKET_TYPE_ANNOUNCEMENT (0x01u)
33#define PICO_MDNS_PACKET_TYPE_ANSWER (0x02u)
34#define PICO_MDNS_PACKET_TYPE_QUERY (0x04u)
35#define PICO_MDNS_PACKET_TYPE_PROBE (0x08u)
36#define PICO_MDNS_PACKET_TYPE_QUERY_ANY (0x00u)
37/* --- Cookie status ---  */
38#define PICO_MDNS_COOKIE_STATUS_ACTIVE (0xffu)
39#define PICO_MDNS_COOKIE_STATUS_INACTIVE (0x00u)
40#define PICO_MDNS_COOKIE_STATUS_CANCELLED (0x77u)
41#define PICO_MDNS_COOKIE_TIMEOUT (10u)
42
43#define PICO_MDNS_SECTION_ANSWERS (0)
44#define PICO_MDNS_SECTION_AUTHORITIES (1)
45#define PICO_MDNS_SETCTIO_ADDITIONALS (2)
46
47#define PICO_MDNS_CTREE_DESTROY(rtree) \
48    pico_tree_destroy((rtree), pico_mdns_cookie_delete);
49
50/* --- Question flags --- */
51#define PICO_MDNS_QUESTION_FLAG_PROBE (0x01u)
52#define PICO_MDNS_QUESTION_FLAG_NO_PROBE (0x00u)
53#define PICO_MDNS_QUESTION_FLAG_UNICAST_RES (0x02u)
54#define PICO_MDNS_QUESTION_FLAG_MULTICAST_RES (0x00u)
55
56#define IS_QUESTION_PROBE_FLAG_SET(x) \
57    (((x) & PICO_MDNS_QUESTION_FLAG_PROBE) ? (1) : (0))
58#define IS_QUESTION_UNICAST_FLAG_SET(x) \
59    (((x) & PICO_MDNS_QUESTION_FLAG_UNICAST_RES) ? (1) : (0))
60#define IS_QUESTION_MULTICAST_FLAG_SET(x) \
61    (((x) & PICO_MDNS_QUESTION_FLAG_UNICAST_RES) ? (0) : (1))
62
63/* Resource Record flags */
64#define PICO_MDNS_RECORD_ADDITIONAL (0x08u)
65#define PICO_MDNS_RECORD_SEND_UNICAST (0x10u)
66#define PICO_MDNS_RECORD_CURRENTLY_PROBING (0x20u)
67#define PICO_MDNS_RECORD_PROBED (0x40u)
68#define PICO_MDNS_RECORD_CLAIMED (0x80u)
69
70#define IS_SHARED_RECORD(x) \
71    ((x)->flags & PICO_MDNS_RECORD_SHARED)
72#define IS_UNIQUE_RECORD(x) \
73    (!((x)->flags & PICO_MDNS_RECORD_SHARED))
74#define IS_RECORD_PROBING(x) \
75    ((x)->flags & PICO_MDNS_RECORD_CURRENTLY_PROBING)
76#define IS_UNICAST_REQUESTED(x) \
77    ((x)->flags & PICO_MDNS_RECORD_SEND_UNICAST)
78#define IS_RECORD_VERIFIED(x) \
79    ((x)->flags & PICO_MDNS_RECORD_PROBED)
80#define IS_RECORD_CLAIMED(x) \
81    ((x)->flags & PICO_MDNS_RECORD_CLAIMED)
82
83/* Set and clear flags */
84#define PICO_MDNS_SET_FLAG(x, b) (x = ((x) | (uint8_t)(b)))
85#define PICO_MDNS_CLR_FLAG(x, b) (x = (uint8_t)(((x) & (~((uint8_t)(b))))))
86
87/* Set and clear MSB of BE short */
88#define PICO_MDNS_SET_MSB(x) (x = x | (uint16_t)(0x8000u))
89#define PICO_MDNS_CLR_MSB(x) (x = x & (uint16_t)(0x7fffu))
90#define PICO_MDNS_SET_MSB_BE(x) (x = x | (uint16_t)(short_be(0x8000u)))
91#define PICO_MDNS_CLR_MSB_BE(x) (x = x & (uint16_t)(short_be(0x7fffu)))
92#define PICO_MDNS_IS_MSB_SET(x) ((x & 0x8000u) ? 1 : 0)
93
94/* ****************************************************************************
95 *  mDNS cookie
96 * *****************************************************************************/
97struct pico_mdns_cookie
98{
99    pico_dns_qtree qtree;               /* Question tree */
100    pico_mdns_rtree antree;             /* Answer tree */
101    pico_mdns_rtree artree;             /* Additional record tree */
102    uint8_t count;                      /* Times to send the query */
103    uint8_t type;                       /* QUERY/ANNOUNCE/PROBE/ANSWER */
104    uint8_t status;                     /* Active status */
105    uint8_t timeout;                    /* Timeout counter */
106    uint32_t send_timer;      /* For sending events */
107    void (*callback)(pico_mdns_rtree *,
108                     char *,
109                     void *);           /* Callback */
110    void *arg;                          /* Argument to pass to callback */
111};
112
113/* MARK: TREES & GLOBAL VARIABLES */
114
115/* MDNS Communication variables */
116static struct pico_socket *mdns_sock_ipv4 = NULL;
117static uint16_t mdns_port = 5353u;
118static struct pico_ip4 inaddr_any = {
119    0
120};
121
122/* ****************************************************************************
123 *  Hostname for this machine, only 1 hostname can be set.
124 *  Following RFC6267: 15.4 Recommendation
125 * *****************************************************************************/
126static char *_hostname = NULL;
127
128static void (*init_callback)(pico_mdns_rtree *, char *, void *) = 0;
129
130/* ****************************************************************************
131 *  Compares 2 mDNS records by name and type only
132 *
133 *  @param a mDNS record A
134 *  @param b mDNS record B
135 *  @return 0 when name and type of records are equal, returns difference when
136 *			they're not.
137 * ****************************************************************************/
138static int
139pico_mdns_record_cmp_name_type( void *a, void *b )
140{
141    struct pico_mdns_record *_a = NULL, *_b = NULL;
142
143    /* Check params */
144    if (!(_a = (struct pico_mdns_record *)a) ||
145        !(_b = (struct pico_mdns_record *)b)) {
146        pico_err = PICO_ERR_EINVAL;
147        return -1; /* Don't want a wrong result when NULL-pointers are passed */
148    }
149
150    return pico_dns_record_cmp_name_type(_a->record, _b->record);
151}
152
153/* ****************************************************************************
154 *  Compares 2 mDNS records by type, name AND rdata for a truly unique result
155 *
156 *  @param ra mDNS record A
157 *  @param rb mDNS record B
158 *  @return 0 when records are equal, returns difference when they're not.
159 * ****************************************************************************/
160int
161pico_mdns_record_cmp( void *a, void *b )
162{
163    /* Check params */
164    if (!a || !b) {
165        if (!a && !b)
166            return 0;
167
168        pico_err = PICO_ERR_EINVAL;
169        return -1; /* Don't want a wrong result when NULL-pointers are passed */
170    }
171
172    return pico_dns_record_cmp((void*)(((struct pico_mdns_record *)a)->record),
173                               (void*)(((struct pico_mdns_record *)b)->record));
174}
175
176/* ****************************************************************************
177 *  Compares 2 mDNS cookies again each other. Only compares questions since a
178 *  only a cookie query will be added to the tree. And there shouldn't be 2
179 *  different cookies with the same questions in the tree.
180 *
181 *  @param ka mDNS cookie A
182 *  @param kb mDNS cookie B
183 *  @return 0 when cookies are equal, returns difference when they're not.
184 * ****************************************************************************/
185static int
186pico_mdns_cookie_cmp( void *ka, void *kb )
187{
188    struct pico_mdns_cookie *a = (struct pico_mdns_cookie *)ka;
189    struct pico_mdns_cookie *b = (struct pico_mdns_cookie *)kb;
190    struct pico_dns_question *qa = NULL, *qb = 0;
191    struct pico_tree_node *na = NULL, *nb = 0;
192    uint16_t ca = 0, cb = 0;
193    int ret = 0;
194
195    /* Check params */
196    if (!a || !b) {
197        pico_err = PICO_ERR_EINVAL;
198        return -1; /* Don't want a wrong result when NULL-pointers are passed */
199    }
200
201    /* Start comparing the questions */
202    for (na = pico_tree_firstNode(a->qtree.root),
203         nb = pico_tree_firstNode(b->qtree.root);
204         (na != &LEAF) && (nb != &LEAF);
205         na = pico_tree_next(na),
206         nb = pico_tree_next(nb)) {
207        qa = na->keyValue;
208        qb = nb->keyValue;
209        if ((qa) && (qb) && (ret = pico_dns_question_cmp(qa, qb)))
210            return ret;
211    }
212    /* Check for lengths difference */
213    ca = pico_tree_count(&(a->qtree));
214    cb = pico_tree_count(&(b->qtree));
215    if (ca != cb)
216        return (int)((int)ca - (int)cb);
217
218    /* Cookies contain same questions, shouldn't happen */
219    return 0;
220}
221
222/*
223 *  Hash to identify mDNS timers with
224 */
225static uint32_t mdns_hash = 0;
226
227/*
228 *  mDNS specific timer creation, to identify if timers are
229 *  created by mDNS module
230 */
231static uint32_t
232pico_mdns_timer_add(pico_time expire,
233                    void (*timer)(pico_time, void *),
234                    void *arg)
235{
236    return pico_timer_add_hashed(expire, timer, arg, mdns_hash);
237}
238
239#if PICO_MDNS_ALLOW_CACHING == 1
240/* Cache records from mDNS peers on the network */
241static PICO_TREE_DECLARE(Cache, &pico_mdns_record_cmp);
242#endif
243
244/* My records for which I want to have the authority */
245static PICO_TREE_DECLARE(MyRecords, &pico_mdns_record_cmp_name_type);
246
247/* Cookie-tree */
248static PICO_TREE_DECLARE(Cookies, &pico_mdns_cookie_cmp);
249
250/* ****************************************************************************
251 *  MARK: PROTOTYPES                                                          */
252static int
253pico_mdns_getrecord_generic( const char *url, uint16_t type,
254                             void (*callback)(pico_mdns_rtree *,
255                                              char *,
256                                              void *),
257                             void *arg);
258
259static void
260pico_mdns_send_probe_packet( pico_time now, void *arg );
261
262static int
263pico_mdns_reclaim( pico_mdns_rtree record_tree,
264                   void (*callback)(pico_mdns_rtree *,
265                                    char *,
266                                    void *),
267                   void *arg );
268/*  EOF PROTOTYPES
269 * ****************************************************************************/
270
271/* MARK: v MDNS NAMES */
272
273#define IS_NUM(c) (((c) >= '0') && ((c) <= '9'))
274/* ****************************************************************************
275 *  Tries to convert the characters after '-' to a numeric value.
276 *
277 *  @param opening Pointer to dash index.
278 *  @param closing Pointer to end of label.
279 *  @return Numeric value of suffix on success
280 * ****************************************************************************/
281static inline uint16_t
282pico_mdns_suffix_to_uint16( char *opening, char *closing)
283{
284    uint16_t n = 0;
285    char *i = 0;
286
287    /* Check params */
288    if (!opening || !closing ||
289        ((closing - opening) > 5) ||
290        ((closing - opening) < 0))
291        return 0;
292
293    for (i = (char *)(opening + 1); i < closing; i++) {
294        if (!IS_NUM(*i))
295            return 0;
296
297        n = (uint16_t)((n * 10) + (*i - '0'));
298    }
299    return n;
300}
301
302#define iterate_first_label_name_reverse(iterator, name) \
303    for ((iterator) = \
304             (*name < (char)63) ? ((char *)(name + *name)) : (name); \
305         (iterator) > (name); \
306         (iterator)--)
307
308/* ****************************************************************************
309 *  Checks whether there is already a conflict-suffix already present in the
310 *  first label of a name or not.
311 *
312 *  @param name   Name in DNS name notation you want to check for a suffix.
313 *  @param o_i    Pointer-pointer, will get filled with location to '-'-char.
314 *  @param c_i    Pointer-pointer, will get filled with end of label.
315 *  @return Returns value of the suffix, when it's present, 0 when no correct
316 *          suffix is present.
317 * ****************************************************************************/
318static uint16_t
319pico_mdns_is_suffix_present( char name[],
320                             char **o_i,
321                             char **c_i )
322{
323    char *i = NULL;
324    uint16_t n = 0;
325
326    *o_i = NULL; /* Clear out indexes */
327    *c_i = NULL;
328
329    /* Find the end of label. */
330    *c_i = (name + *name + 1);
331
332    iterate_first_label_name_reverse(i, name) {
333        /* Find the last dash */
334        if ((*c_i) && (i < *c_i) && *i == '-') {
335            *o_i = i;
336            break;
337        }
338    }
339
340    /* Convert the string suffix to a number */
341    if (!(n = pico_mdns_suffix_to_uint16(*o_i, *c_i))) {
342        *o_i = NULL;
343        *c_i = NULL;
344    }
345
346    return n;
347}
348
349/* ****************************************************************************
350 *  Manual string to uint16_t conversion.
351 *
352 *  @param n Numeric value you want to convert.
353 *  @param s String to convert to
354 *  @return void
355 * ****************************************************************************/
356static void pico_itoa( uint16_t n, char s[] )
357{
358    int i = 0, j = 0;
359    char c = 0;
360
361    /* Get char values */
362    do {
363        s[i++] = (char)(n % 10 + '0');
364    } while ((n /= 10) > 0);
365
366    /* Reverse the string */
367    for (i = 0, j = (int)(pico_dns_strlen(s) - 1); i < j; i++, j--) {
368        c = s[i];
369        s[i] = s[j];
370        s[j] = c;
371    }
372}
373
374/* ****************************************************************************
375 *  Generates a new name by appending a conflict resolution suffix to the first
376 *  label of an FQDN.
377 *
378 *  @param rname Name you want to append the suffix to
379 *  @return Newly created FQDN with suffix appended to first label.
380 * ****************************************************************************/
381static char *
382pico_mdns_resolve_name_conflict( char rname[] )
383{
384    char *new_rname = NULL;
385    char suffix[5] = {
386        0
387    }, nsuffix[5] = {
388        0
389    }, copy_offset = 0;
390    char *o_i = NULL, *c_i = NULL;
391    uint16_t new_len = (uint16_t)(pico_dns_strlen(rname) + 1);
392    uint8_t nslen = 0, slen = 0, ns = 0;
393
394    /* Check params */
395    if (pico_dns_check_namelen(new_len)) {
396        pico_err = PICO_ERR_EINVAL;
397        return NULL;
398    }
399
400    /* Check whether a conflict-suffix is already present in the name */
401    if ((ns = (uint8_t)pico_mdns_is_suffix_present(rname, &o_i, &c_i))) {
402        pico_itoa(ns, suffix);
403        pico_itoa(++ns, nsuffix);
404        slen = (uint8_t)pico_dns_strlen(suffix);
405        nslen = (uint8_t)pico_dns_strlen(nsuffix);
406        new_len = (uint16_t)(new_len + nslen - slen);
407    } else {
408        /* If no suffix is present */
409        c_i = (o_i = rname + *rname) + 1;
410        new_len = (uint16_t)(new_len + 2u);
411        memcpy((void *)nsuffix, "-2\0", (size_t)3);
412    }
413
414    /* Provide space for the new name */
415    if (!(new_rname = PICO_ZALLOC(new_len))) {
416        pico_err = PICO_ERR_ENOMEM;
417        return NULL;
418    }
419
420    /* Assemble the new name again */
421    copy_offset = (char)((o_i - rname + 1));
422    memcpy(new_rname, rname, (size_t)(copy_offset));
423    strcpy(new_rname + copy_offset, nsuffix);
424    strcpy(new_rname + copy_offset + pico_dns_strlen(nsuffix), c_i);
425    /* Set the first length-byte */
426    new_rname[0] = (char)(new_rname[0] + new_len - pico_dns_strlen(rname) - 1);
427    return new_rname;
428}
429
430/* MARK: ^ MDNS NAMES */
431/* MARK: v MDNS QUESTIONS */
432
433/* ****************************************************************************
434 *  Creates a standalone mDNS Question with a given name and type.
435 *
436 *  @param url     DNS question name in URL format. Will be converted to DNS
437 *				   name notation format.
438 *  @param len     Will be filled with the total length of the DNS question.
439 *  @param proto   Protocol for which you want to create a question. Can be
440 *				   either PICO_PROTO_IPV4 or PICO_PROTO_IPV6.
441 *  @param qtype   DNS type of the question to be.
442 *  @param flags   With the flags you can specify if the question should be
443 *				   a QU-question rather than a QM-question
444 *  @param reverse When this is true, a reverse resolution name will be gene-
445 *				   from the URL
446 *  @return Returns pointer to the created mDNS Question on success, NULL on
447 *			failure.
448 * ****************************************************************************/
449static struct pico_dns_question *
450pico_mdns_question_create( const char *url,
451                           uint16_t *len,
452                           uint8_t proto,
453                           uint16_t qtype,
454                           uint8_t flags,
455                           uint8_t reverse )
456{
457    uint16_t qclass = PICO_DNS_CLASS_IN;
458
459    /* Set the MSB of the qclass field according to the mDNS format */
460    if (IS_QUESTION_UNICAST_FLAG_SET(flags))
461        PICO_MDNS_SET_MSB(qclass);
462
463    /* Fill in the question suffix */
464    if (IS_QUESTION_PROBE_FLAG_SET(flags))
465        qtype = PICO_DNS_TYPE_ANY;
466
467    /* Create a question as you would with plain DNS */
468    return pico_dns_question_create(url, len, proto, qtype, qclass, reverse);
469}
470
471/* MARK: ^ MDNS QUESTIONS */
472/* MARK: v MDNS RECORDS */
473
474/* ****************************************************************************
475 *  Just makes a hardcopy from a single mDNS resource record.
476 *
477 *  @param record mDNS record you want to create a copy from
478 *  @return Pointer to copied mDNS resource record
479 * ****************************************************************************/
480static struct pico_mdns_record *
481pico_mdns_record_copy( struct pico_mdns_record *record )
482{
483    struct pico_mdns_record *copy = NULL;
484
485    /* Check params */
486    if (!record) {
487        pico_err = PICO_ERR_EINVAL;
488        return NULL;
489    }
490
491    /* Provide space for the copy */
492    if (!(copy = PICO_ZALLOC(sizeof(struct pico_mdns_record)))) {
493        pico_err = PICO_ERR_ENOMEM;
494        return NULL;
495    }
496
497    /* Copy the DNS record */
498    if (!(copy->record = pico_dns_record_copy(record->record))) {
499        PICO_FREE(copy);
500        return NULL;
501    }
502
503    /* Copy the fields */
504    copy->current_ttl = record->current_ttl;
505    copy->flags = record->flags;
506    copy->claim_id = record->claim_id;
507
508    return copy;
509}
510
511/* ****************************************************************************
512 *  Looks for multiple mDNS records in a tree with the same name.
513 *
514 *  @param tree Tree in which you want to search.
515 *  @param name Name you want to search for.
516 *  @return Tree with found hits, can possibly be empty
517 * ****************************************************************************/
518static pico_mdns_rtree
519pico_mdns_rtree_find_name( pico_mdns_rtree *tree,
520                           const char *name,
521                           uint8_t copy )
522{
523    PICO_MDNS_RTREE_DECLARE(hits);
524    struct pico_tree_node *node = NULL;
525    struct pico_mdns_record *record = NULL;
526
527    /* Check params */
528    if (!name || !tree) {
529        pico_err = PICO_ERR_EINVAL;
530        return hits;
531    }
532
533    /* Iterate over tree */
534    pico_tree_foreach(node, tree) {
535        record = node->keyValue;
536        if (record && strcasecmp(record->record->rname, name) == 0) {
537            if (copy)
538                record = pico_mdns_record_copy(record);
539
540            if (record)
541                if (pico_tree_insert(&hits, record) != NULL)
542                    /* either key was already in there, or couldn't be inserted. */
543                    /* Only delete record if it was copied */
544                    if (copy)
545                        pico_mdns_record_delete((void **)&record);
546        }
547    }
548
549    return hits;
550}
551
552/* ****************************************************************************
553 *  Looks for (possibly) multiple mDNS records in a tree with the same name and
554 *  type.
555 *
556 *  @param tree  Tree in which you want to search.
557 *  @param name  Name you want to search for.
558 *  @param rtype DNS type you want to search for.
559 *  @return Tree with found hits, can possibly be empty.
560 * ****************************************************************************/
561static pico_mdns_rtree
562pico_mdns_rtree_find_name_type( pico_mdns_rtree *tree,
563                                char *name,
564                                uint16_t rtype,
565                                uint8_t copy )
566{
567    PICO_MDNS_RTREE_DECLARE(hits);
568
569    struct pico_dns_record_suffix test_dns_suffix = {
570        0, 1, 0, 0
571    };
572    struct pico_dns_record test_dns_record = {
573        0
574    };
575    struct pico_mdns_record test = {
576        0
577    };
578    struct pico_tree_node *node = NULL;
579    struct pico_mdns_record *record = NULL;
580    test_dns_record.rsuffix = &test_dns_suffix;
581    test.record = &test_dns_record;
582
583    /* Check params */
584    if (!name || !tree) {
585        pico_err = PICO_ERR_EINVAL;
586        return hits;
587    }
588
589    test.record->rname = name;
590    test.record->rsuffix->rtype = short_be(rtype);
591
592    /* Iterate over the tree */
593    pico_tree_foreach(node, tree) {
594        record = node->keyValue;
595        if ((record) && (0 == pico_mdns_record_cmp_name_type(record, &test))) {
596            if (copy)
597                record = pico_mdns_record_copy(record);
598
599            if (record){
600                if (pico_tree_insert(&hits, record) != NULL) {
601                    /* either key was already in there, or couldn't be inserted. */
602                    /* Only delete record if it was copied */
603                    if (copy)
604                        pico_mdns_record_delete((void **)&record);
605            	}
606            }
607        }
608    }
609
610    return hits;
611}
612
613/* ****************************************************************************
614 *  Deletes multiple mDNS records in a tree with the same name.
615 *
616 *  @param tree Tree from which you want to delete records by name.
617 *  @param name Name of records you want to delete from the tree.
618 *  @return 0 on success, something else on failure.
619 * ****************************************************************************/
620static int
621pico_mdns_rtree_del_name( pico_mdns_rtree *tree,
622                          const char *name )
623{
624    struct pico_tree_node *node = NULL, *safe = NULL;
625    struct pico_mdns_record *record = NULL;
626
627    /* Check params */
628    if (!name || !tree) {
629        pico_err = PICO_ERR_EINVAL;
630        return -1;
631    }
632
633    /* Iterate over tree */
634    pico_tree_foreach_safe(node, tree, safe) {
635        record = node->keyValue;
636        if (record && strcasecmp(record->record->rname, name) == 0) {
637            record = pico_tree_delete(tree, record);
638            pico_mdns_record_delete((void **)&record);
639        }
640    }
641
642    return 0;
643}
644
645/* ****************************************************************************
646 *  Deletes (possibly) multiple mDNS records from a tree with same name and
647 *  type.
648 *
649 *  @param tree Tree from which you want to delete records by name and type.
650 *  @param name Name of records you want to delete.
651 *  @param type DNS type of records you want to delete.
652 *  @return 0 on success, something else on failure.
653 * ****************************************************************************/
654#if PICO_MDNS_ALLOW_CACHING == 1
655static int
656pico_mdns_rtree_del_name_type( pico_mdns_rtree *tree,
657                               char *name,
658                               uint16_t type )
659{
660    struct pico_tree_node *node = NULL, *next = NULL;
661    struct pico_mdns_record *record = NULL;
662    struct pico_dns_record_suffix test_dns_suffix = {
663        0, 1, 0, 0
664    };
665    struct pico_dns_record test_dns_record = {
666        0
667    };
668    struct pico_mdns_record test = {
669        0
670    };
671
672    test_dns_record.rsuffix = &test_dns_suffix;
673    test.record = &test_dns_record;
674
675    /* Check params */
676    if (!name || !tree) {
677        pico_err = PICO_ERR_EINVAL;
678        return -1;
679    }
680
681    test.record->rname = name;
682    test.record->rsuffix->rtype = short_be(type);
683
684    /* Iterate over the tree */
685    pico_tree_foreach_safe(node, tree, next) {
686        record = node->keyValue;
687        if ((record) && (0 == pico_mdns_record_cmp_name_type(record, &test))) {
688            record = pico_tree_delete(tree, record);
689            pico_mdns_record_delete((void **)&record);
690        }
691    }
692
693    return 0;
694}
695#endif
696
697/* ****************************************************************************
698 *  Makes a hardcopy from a single mDNS resource record, but sets a new name
699 *  for the copy.
700 *
701 *  @param record    mDNS record you want to copy.
702 *  @param new_rname New name you want to set the name of the record to.
703 *  @return Pointer to the copy on success, NULL-pointer on failure.
704 * ****************************************************************************/
705static struct pico_mdns_record *
706pico_mdns_record_copy_with_new_name( struct pico_mdns_record *record,
707                                     const char *new_rname )
708{
709    struct pico_mdns_record *copy = NULL;
710    uint16_t slen = (uint16_t)(pico_dns_strlen(new_rname) + 1u);
711
712    /* Check params */
713    if (!new_rname || pico_dns_check_namelen(slen)) {
714        pico_err = PICO_ERR_EINVAL;
715        return NULL;
716    }
717
718    /* Copy the record */
719    if (!(copy = pico_mdns_record_copy(record)))
720        return NULL;
721
722    /* Provide a new string */
723    PICO_FREE(copy->record->rname);
724    if (!(copy->record->rname = PICO_ZALLOC(slen))) {
725        pico_err = PICO_ERR_ENOMEM;
726        pico_mdns_record_delete((void **)&copy);
727        return NULL;
728    }
729
730    memcpy((void *)(copy->record->rname), new_rname, slen);
731    copy->record->rname_length = slen;
732
733    return copy;
734}
735
736/* ****************************************************************************
737 *  Generates (copies) new records from conflicting ones with another name.
738 *  deletes
739 *
740 *  @param conflict_records mDNS record tree that contains conflicting records
741 *  @param conflict_name    Name for which the conflict occurred. This is to be
742 *                          able to delete the conflicting records from the tree
743 *  @param new_name         To generate new records from the conflicting ones,
744 *                          with this new name.
745 *  @return A mDNS record tree that contains all the newly generated records.
746 * ****************************************************************************/
747static pico_mdns_rtree
748pico_mdns_generate_new_records( pico_mdns_rtree *conflict_records,
749                                char *conflict_name,
750                                char *new_name )
751{
752    PICO_MDNS_RTREE_DECLARE(new_records);
753    struct pico_tree_node *node = NULL, *next = NULL;
754    struct pico_mdns_record *record = NULL, *new_record = NULL;
755
756    /* Delete all the conflicting records from MyRecords */
757    if (pico_mdns_rtree_del_name(&MyRecords, conflict_name))
758        return new_records;
759
760    pico_tree_foreach_safe(node, conflict_records, next) {
761        record = node->keyValue;
762        if (record && strcasecmp(record->record->rname, conflict_name) == 0) {
763            /* Create a new record */
764            new_record = pico_mdns_record_copy_with_new_name(record, new_name);
765            if (!new_record) {
766                mdns_dbg("Could not create new non-conflicting record!\n");
767                return new_records;
768            }
769
770            new_record->flags &= (uint8_t)(~(PICO_MDNS_RECORD_PROBED |
771                                             PICO_MDNS_RECORD_SHARED |
772                                             PICO_MDNS_RECORD_CURRENTLY_PROBING));
773
774            /* Add the record to the new tree */
775            if (pico_tree_insert(&new_records, new_record)) {
776            	mdns_dbg("Could not add new non-conflicting record to the tree!\n");
777                pico_mdns_record_delete((void **)&new_record);
778				return new_records;
779			}
780
781            /* Delete the old conflicting record */
782            record = pico_tree_delete(conflict_records, record);
783            if (pico_mdns_record_delete((void **)&record)) {
784                mdns_dbg("Could not delete old conflict record from tree!\n");
785                return new_records;
786            }
787        }
788    }
789
790    return new_records;
791}
792
793/* ****************************************************************************
794 *  When hosts observe an unsolicited record, no cookie is currently active
795 *  for that, so it has to check in MyRecords if no conflict occurred for a
796 *  record it has already registered. When this occurs the conflict should be
797 *  resolved as with a normal cookie, just without the cookie.
798 *
799 *  @param record mDNS record for which the conflict occurred.
800 *  @param rname  DNS name for which the conflict occurred in DNS name notation.
801 *  @return 0 when the resolving is applied successfully, 1 otherwise.
802 * ****************************************************************************/
803static int
804pico_mdns_record_resolve_conflict( struct pico_mdns_record *record,
805                                   char *rname )
806{
807    int retval;
808    PICO_MDNS_RTREE_DECLARE(new_records);
809    struct pico_mdns_record *copy = NULL;
810    char *new_name = NULL;
811
812    /* Check params */
813    if (!record || !rname || IS_SHARED_RECORD(record)) {
814        pico_err = PICO_ERR_EINVAL;
815        return -1;
816    }
817
818    /* Step 2: Create a new name depending on current name */
819    if (!(new_name = pico_mdns_resolve_name_conflict(rname)))
820        return -1;
821
822    copy = pico_mdns_record_copy_with_new_name(record, new_name);
823    PICO_FREE(new_name);
824    if (copy){
825    	if (pico_tree_insert(&new_records, copy)) {
826            mdns_dbg("MDNS: Failed to insert copy in tree\n");
827            pico_mdns_record_delete((void **)&copy);
828            return -1;
829		}
830    }
831
832    /* Step 3: delete conflicting record from my records */
833    pico_tree_delete(&MyRecords, record);
834    pico_mdns_record_delete((void **)&record);
835
836    /* Step 4: Try to reclaim the newly created records */
837    retval = pico_mdns_reclaim(new_records, init_callback, NULL);
838    pico_tree_destroy(&new_records, NULL);
839    return retval;
840}
841
842/* ****************************************************************************
843 *  Determines if my_record is lexicographically later than peer_record, returns
844 *  positive value when this is the case. Check happens by comparing rtype first
845 *  and then rdata as prescribed by RFC6762.
846 *
847 *  @param my_record   Record this hosts want to claim.
848 *  @param peer_record Record the peer host wants to claim (the enemy!)
849 *  @return positive value when my record is lexicographically later
850 * ****************************************************************************/
851static int
852pico_mdns_record_am_i_lexi_later( struct pico_mdns_record *my_record,
853                                  struct pico_mdns_record *peer_record)
854{
855    struct pico_dns_record *my = NULL, *peer = NULL;
856    uint16_t mclass = 0, pclass = 0, mtype = 0, ptype = 0;
857    int dif = 0;
858
859    /* Check params */
860    if (!my_record || !peer_record ||
861        !(my = my_record->record) || !(peer = peer_record->record)) {
862        pico_err = PICO_ERR_EINVAL;
863        return -1;
864    }
865
866    /*
867     * First compare the record class (excluding cache-flush bit described in
868     * section 10.2)
869     * The numerically greater class wins
870     */
871    mclass = PICO_MDNS_CLR_MSB_BE(my->rsuffix->rclass);
872    pclass = PICO_MDNS_CLR_MSB_BE(peer->rsuffix->rclass);
873    if ((dif = (int)((int)mclass - (int)pclass))) {
874        return dif;
875    }
876
877    /* Second, compare the rrtypes */
878    mtype = (my->rsuffix->rtype);
879    ptype = (peer->rsuffix->rtype);
880    if ((dif = (int)((int)mtype - (int)ptype))) {
881        return dif;
882    }
883
884    /* Third compare binary content of rdata (no regard for meaning or structure) */
885
886    /* When using name compression, names MUST be uncompressed before comparison. See secion 8.2 in RFC6762
887       This is already the case, but we won't check for it here.
888       The current execution stack to get here is:
889       > pico_mdns_handle_data_as_answers_generic
890       >  > pico_dns_record_decompress
891       >  > pico_mdns_handle_single_authority
892       >  >  > pico_mdns_cookie_apply_spt
893       >  >  >  > pico_mdns_record_am_i_lexi_later
894
895       Make sure pico_dns_record_decompress is executed before pico_mdns_record_am_i_lexi_later gets called, if problems ever arise with this function.
896     */
897
898    /* Then compare rdata */
899    return pico_dns_rdata_cmp(my->rdata, peer->rdata,
900                              short_be(my->rsuffix->rdlength),
901                              short_be(peer->rsuffix->rdlength), PICO_DNS_CASE_SENSITIVE);
902}
903
904/* ****************************************************************************
905 *  Deletes a single mDNS resource record.
906 *
907 *  @param record Void-pointer to mDNS Resource Record. Can be used with pico_-
908 *         tree-destroy.
909 *  @return Returns 0 on success, something else on failure.
910 * ****************************************************************************/
911int
912pico_mdns_record_delete( void **record )
913{
914    struct pico_mdns_record **rr = (struct pico_mdns_record **)record;
915
916    /* Check params */
917    if (!rr || !(*rr)) {
918        pico_err = PICO_ERR_EINVAL;
919        return -1;
920    }
921
922    /* Delete DNS record contained */
923    if (((*rr)->record)) {
924        pico_dns_record_delete((void **)&((*rr)->record));
925    }
926
927    /* Delete the record itself */
928    PICO_FREE(*rr);
929    *record = NULL;
930
931    return 0;
932}
933
934/* ****************************************************************************
935 *  Creates a single standalone mDNS resource record with given name, type and
936 *  data.
937 *
938 *  @param url     DNS rrecord name in URL format. Will be converted to DNS
939 *                 name notation format.
940 *  @param _rdata  Memory buffer with data to insert in the resource record. If
941 *				   data of record should contain a DNS name, the name in the
942 *				   data buffer needs to be in URL-format.
943 *  @param datalen The exact length in bytes of the _rdata-buffer. If data of
944 *				   record should contain a DNS name, datalen needs to be
945 *				   pico_dns_strlen(_rdata).
946 *  @param len     Will be filled with the total length of the DNS rrecord.
947 *  @param rtype   DNS type of the resource record to be.
948 *  @param rclass  DNS class of the resource record to be.
949 *  @param rttl    DNS ttl of the resource record to be.
950 *  @param flags   You can specify if the mDNS record should be a shared record
951 *                 rather than a unique record.
952 *  @return Pointer to newly created mDNS resource record.
953 * ****************************************************************************/
954struct pico_mdns_record *
955pico_mdns_record_create( const char *url,
956                         void *_rdata,
957                         uint16_t datalen,
958                         uint16_t rtype,
959                         uint32_t rttl,
960                         uint8_t flags )
961{
962    struct pico_mdns_record *record = NULL;
963    uint16_t len = 0;
964    uint16_t cl = 0;
965
966    /* Check params */
967    if (!url || !_rdata) {
968        pico_err = PICO_ERR_EINVAL;
969        return NULL;
970    } /* Block 1, 2 paths */
971
972    /* Provide space for the new mDNS resource record */
973    if (!(record = PICO_ZALLOC(sizeof(struct pico_mdns_record)))) {
974        pico_err = PICO_ERR_ENOMEM;
975        return NULL;
976    } /* Block 2, 1 path */
977    else {
978        /* Try to create the actual DNS record */
979        if (!(record->record = pico_dns_record_create(url, _rdata, datalen,
980                                                    &len, rtype,
981                                                    PICO_DNS_CLASS_IN, rttl))) {
982            mdns_dbg("Could not create DNS record for mDNS!\n");
983            PICO_FREE(record);
984            return NULL;
985        } /* Block 3, 2 paths */
986    } /* Block 4, Block 3 = 2 paths */
987    /* Block 5, (Block 4 + Block 2) * Block 1 = 6 paths */
988
989    /* Initialise fields */
990    record->current_ttl = rttl;
991
992    /* Set the MSB of the DNS class if it's a unique record */
993    if (!((flags) & PICO_MDNS_RECORD_SHARED)) {
994        cl = record->record->rsuffix->rclass;
995        record->record->rsuffix->rclass = PICO_MDNS_SET_MSB_BE(cl);
996    } /* Block 6, 2 paths */
997    /* Block 7, Block 6 * Block 5 * Block 1 = 12 paths */
998
999    record->flags = flags;
1000    record->claim_id = 0;
1001
1002    return record;
1003}
1004
1005/* MARK: ^ MDNS RECORDS */
1006/* MARK: v MDNS COOKIES */
1007
1008/* ****************************************************************************
1009 *  Deletes a single mDNS packet cookie and frees memory.
1010 *
1011 *  @param cookie Void-pointer to mDNS cookie, allow to be used with pico_tree-
1012 *         destroy.
1013 *  @return Returns 0 on success, something else on failure.
1014 * ****************************************************************************/
1015static int
1016pico_mdns_cookie_delete( void **ptr )
1017{
1018    struct pico_mdns_cookie **c = (struct pico_mdns_cookie **)ptr;
1019
1020    /* Check params */
1021    if (!c || !(*c)) {
1022        pico_err = PICO_ERR_EINVAL;
1023        return -1;
1024    }
1025
1026    /* Destroy the vectors contained */
1027    PICO_DNS_QTREE_DESTROY(&((*c)->qtree));
1028    PICO_MDNS_RTREE_DESTROY(&((*c)->antree));
1029    PICO_MDNS_RTREE_DESTROY(&((*c)->artree));
1030
1031    /* Delete the cookie itself */
1032    PICO_FREE(*c);
1033    *c = NULL;
1034
1035    return 0;
1036}
1037
1038/* ****************************************************************************
1039 *  Creates a single standalone mDNS cookie
1040 *
1041 *  @param qtree    DNS questions you want to insert in the cookie.
1042 *  @param antree   mDNS answers/authority records you want to add to cookie.
1043 *  @param artree   mDNS additional records you want to add to cookie.
1044 *  @param count    Times you want to send the cookie as a packet on the wire.
1045 *  @param type     Type of packet you want to create from the cookie.
1046 *  @param callback Callback when the host receives responses for the cookie.
1047 *  @return Pointer to newly create cookie, NULL on failure.
1048 * ****************************************************************************/
1049static struct pico_mdns_cookie *
1050pico_mdns_cookie_create( pico_dns_qtree qtree,
1051                         pico_mdns_rtree antree,
1052                         pico_mdns_rtree artree,
1053                         uint8_t count,
1054                         uint8_t type,
1055                         void (*callback)(pico_mdns_rtree *,
1056                                          char *,
1057                                          void *),
1058                         void *arg )
1059{
1060    struct pico_mdns_cookie *cookie = NULL; /* Packet cookie to send */
1061
1062    /* Provide space for the mDNS packet cookie */
1063    cookie = PICO_ZALLOC(sizeof(struct pico_mdns_cookie));
1064    if (!cookie) {
1065        pico_err = PICO_ERR_ENOMEM;
1066        return NULL;
1067    }
1068
1069    /* Fill in the fields */
1070    cookie->qtree = qtree;
1071    cookie->antree = antree;
1072    cookie->artree = artree;
1073    cookie->count = count;
1074    cookie->type = type;
1075    cookie->status = PICO_MDNS_COOKIE_STATUS_INACTIVE;
1076    cookie->timeout = PICO_MDNS_COOKIE_TIMEOUT;
1077    cookie->callback = callback;
1078    cookie->arg = arg;
1079    return cookie;
1080}
1081
1082/* ****************************************************************************
1083 *  Apply Simultaneous Probe Tiebreakin (S.P.T.) on a probe-cookie.
1084 *  See RFC6762: 8.2. Simultaneous Probe Tiebreaking
1085 *
1086 *  @param cookie Cookie which contains the record which is simult. probed.
1087 *  @param answer Authority record received from peer which is simult. probed.
1088 *  @return 0 when SPT is applied correctly, -1 otherwise.
1089 * ****************************************************************************/
1090static int
1091pico_mdns_cookie_apply_spt( struct pico_mdns_cookie *cookie,
1092                            struct pico_dns_record *answer)
1093{
1094    struct pico_mdns_record *my_record = NULL;
1095    struct pico_mdns_record peer_record;
1096
1097    /* Check params */
1098    if ((!cookie) || !answer || (cookie->type != PICO_MDNS_PACKET_TYPE_PROBE)) {
1099        pico_err = PICO_ERR_EINVAL;
1100        return -1;
1101    }
1102
1103    cookie->status = PICO_MDNS_COOKIE_STATUS_INACTIVE;
1104
1105    /* Implement Simultaneous Probe Tiebreaking */
1106    peer_record.record = answer;
1107    my_record = pico_tree_findKey(&MyRecords, &peer_record);
1108    if (!my_record || !IS_RECORD_PROBING(my_record)) {
1109        mdns_dbg("This is weird! My record magically removed...\n");
1110        return -1;
1111    }
1112
1113    if (pico_mdns_record_am_i_lexi_later(my_record, &peer_record) > 0) {
1114        mdns_dbg("My record is lexicographically later! Yay!\n");
1115        cookie->status = PICO_MDNS_COOKIE_STATUS_ACTIVE;
1116    } else {
1117        pico_timer_cancel(cookie->send_timer);
1118        cookie->timeout = PICO_MDNS_COOKIE_TIMEOUT;
1119        cookie->count = PICO_MDNS_PROBE_COUNT;
1120        cookie->send_timer = pico_mdns_timer_add(1000, pico_mdns_send_probe_packet,
1121                                            cookie);
1122        if (!cookie->send_timer) {
1123            mdns_dbg("cookie_apply_spt: failed to start timer\n");
1124            return -1;
1125        }
1126        mdns_dbg("Probing postponed by one second because of S.P.T.\n");
1127    }
1128
1129    return 0;
1130}
1131
1132static int
1133pico_mdns_cookie_del_questions( struct pico_mdns_cookie *cookie,
1134                                char *rname )
1135{
1136    uint16_t qc = 0;
1137
1138    /* Step 1: Remove question with that name from cookie */
1139    pico_dns_qtree_del_name(&(cookie->qtree), rname);
1140    cookie->antree.root = &LEAF;
1141
1142    /* Check if there are no questions left, cancel events if so and delete */
1143    if (!(qc = pico_tree_count(&(cookie->qtree)))) {
1144        pico_timer_cancel(cookie->send_timer);
1145        cookie = pico_tree_delete(&Cookies, cookie);
1146        pico_mdns_cookie_delete((void **)&cookie);
1147    }
1148
1149    return 0;
1150}
1151
1152/* ****************************************************************************
1153 *  Applies conflict resolution mechanism to a cookie, when a conflict occurs
1154 *  for a name which is present in the cookie.
1155 *
1156 *  @param cookie Cookie on which you want to apply the conflict resolution-
1157 *                mechanism.
1158 *  @param rname  Name for which the conflict occurred. A new non-conflicting
1159 *                name will be generated from this string.
1160 *  @return Returns 0 on success, something else on failure.
1161 * ****************************************************************************/
1162static int
1163pico_mdns_cookie_resolve_conflict( struct pico_mdns_cookie *cookie,
1164                                   char *rname )
1165{
1166    struct pico_tree_node *node = NULL;
1167    struct pico_dns_question *question = NULL;
1168    PICO_MDNS_RTREE_DECLARE(new_records);
1169    PICO_MDNS_RTREE_DECLARE(antree);
1170    char *new_name = NULL;
1171    void (*callback)(pico_mdns_rtree *, char *, void *);
1172    void *arg = NULL;
1173    int retval;
1174
1175    /* Check params */
1176    if ((!cookie) || !rname || (cookie->type != PICO_MDNS_PACKET_TYPE_PROBE)) {
1177        pico_err = PICO_ERR_EINVAL;
1178        return -1;
1179    }
1180
1181    /* Convert rname to url */
1182    mdns_dbg("CONFLICT for probe query with name '%s' occurred!\n", rname);
1183
1184    /* Store some information about a cookie for later on */
1185    antree = cookie->antree;
1186    callback = cookie->callback;
1187    arg = cookie->arg;
1188
1189    /* Find the first question in the cookie with the name for which
1190     * the conflict occured. When found, generate a new name.
1191     *
1192     * DNS conflict is case-insensitive. However, we want to keep the original
1193     * capitalisation for the new probe. */
1194    pico_tree_foreach(node, &(cookie->qtree)) {
1195        question = (struct pico_dns_question *)node->keyValue;
1196        if ((question) && (strcasecmp(question->qname, rname) == 0)) {
1197            /* Create a new name depending on current name */
1198            new_name = pico_mdns_resolve_name_conflict(question->qname);
1199
1200            /* Step 1: Check if the new name succeeded, if not: error. */
1201            if (!new_name) {
1202                /* Delete questions from cookie even if generating a new name failed */
1203                pico_mdns_cookie_del_questions(cookie, rname);
1204                return -1;
1205            }
1206
1207            break;
1208        }
1209    }
1210
1211    /* Step 2: Remove questions with this name from the cookie */
1212    pico_mdns_cookie_del_questions(cookie, rname);
1213
1214    /* Step 3: Create records with new name for the records with that name */
1215    new_records = pico_mdns_generate_new_records(&antree, rname, new_name);
1216    PICO_FREE(new_name);
1217
1218    /* Step 4: Try to reclaim the newly created records */
1219    retval = pico_mdns_reclaim(new_records, callback, arg);
1220    pico_tree_destroy(&new_records, NULL);
1221    return retval;
1222}
1223
1224/* ****************************************************************************
1225 *  Find a query cookie that contains a question for a specific name.
1226 *
1227 *  @param name Name of question you want to look for.
1228 *  @return Pointer to cookie in tree when one is found, NULL on failure.
1229 * ****************************************************************************/
1230static struct pico_mdns_cookie *
1231pico_mdns_ctree_find_cookie( const char *name, uint8_t type )
1232{
1233    struct pico_mdns_cookie *cookie = NULL;
1234    struct pico_tree_node *node = NULL;
1235
1236    /* Check params */
1237    if (!name) {
1238        pico_err = PICO_ERR_EINVAL;
1239        return NULL;
1240    }
1241
1242    /* Find the cookie in the tree wherein the question is present */
1243    pico_tree_foreach(node, &Cookies) {
1244        if ((cookie = node->keyValue) &&
1245            pico_dns_qtree_find_name(&(cookie->qtree), name)) {
1246            if (type == PICO_MDNS_PACKET_TYPE_QUERY_ANY)
1247                return cookie;
1248            else if (cookie->type == type)
1249                return cookie;
1250        }
1251    }
1252
1253    return NULL;
1254}
1255
1256/* MARK: ^ MDNS COOKIES */
1257/* MARK: v MY RECORDS */
1258
1259/* ****************************************************************************
1260 *  Adds records contained in records-tree to MyRecords. Suppresses adding of
1261 *  duplicates.
1262 *
1263 *  @param records Tree with records to add to 'MyRecords'.
1264 *  @param reclaim If the records contained in records are claimed again.
1265 *  @return 0 on success, something else on failure.
1266 * ****************************************************************************/
1267static int
1268pico_mdns_my_records_add( pico_mdns_rtree *records, uint8_t reclaim )
1269{
1270    struct pico_tree_node *node = NULL;
1271    struct pico_mdns_record *record = NULL;
1272    static uint8_t claim_id_count = 0;
1273
1274    if (!reclaim) {
1275        ++claim_id_count;
1276    }
1277
1278    /* Iterate over record vector */
1279    pico_tree_foreach(node, records) {
1280        record = node->keyValue;
1281        if (record) {
1282            /* Set probed flag if record is a shared record */
1283            if (IS_SHARED_RECORD(record)) {
1284                PICO_MDNS_SET_FLAG(record->flags, PICO_MDNS_RECORD_PROBED);
1285            }
1286
1287            /* If record is not claimed again, set new claim-ID */
1288            if (!reclaim) {
1289                record->claim_id = claim_id_count;
1290            }
1291
1292            if (pico_tree_insert(&MyRecords, record) == &LEAF) {
1293                mdns_dbg("MDNS: Failed to insert record in tree\n");
1294                return -1;
1295			}
1296        }
1297    }
1298    return 0;
1299}
1300
1301/* ****************************************************************************
1302 *  Generates a tree of all My Records for which the probe flag has already
1303 *  been set, and for which the CLAIMED flag has NOT been set.
1304 *  Copies the records from MyRecords into a new tree.
1305 *
1306 *  @return Tree with all records in MyRecords with the PROBED-flag set.
1307 * ****************************************************************************/
1308static pico_mdns_rtree
1309pico_mdns_my_records_find_probed( void )
1310{
1311    PICO_MDNS_RTREE_DECLARE(probed);
1312    struct pico_tree_node *node = NULL;
1313    struct pico_mdns_record *record = NULL, *copy = NULL;
1314
1315    /* Iterate over MyRecords */
1316    pico_tree_foreach(node, &MyRecords) {
1317        record = node->keyValue;
1318
1319        /* IS_RECORD_VERIFIED() checks the PICO_MDNS_RECORD_PROBED flag */
1320        if (record && IS_RECORD_VERIFIED(record) && !IS_RECORD_CLAIMED(record)) {
1321            copy = pico_mdns_record_copy(record);
1322            if (copy && pico_tree_insert(&probed, copy)) {
1323                pico_mdns_record_delete((void **)&copy);
1324            }
1325        }
1326    }
1327
1328    return probed;
1329}
1330
1331/* ****************************************************************************
1332 *  Generates a tree of all My Records for which the PROBED-flag has not yet
1333 *  been set. Copies the record from MyRecords into a new tree.
1334 *
1335 *  @return Tree with all records in MyRecords with the PROBED-flag not set.
1336 * ****************************************************************************/
1337static pico_mdns_rtree
1338pico_mdns_my_records_find_to_probe( void )
1339{
1340    PICO_MDNS_RTREE_DECLARE(to_probe);
1341    struct pico_tree_node *node = NULL;
1342    struct pico_mdns_record *record = NULL, *copy = NULL;
1343
1344    pico_tree_foreach(node, &MyRecords) {
1345        record = node->keyValue;
1346        /* Check if probed flag is not set of a record */
1347        if (record &&
1348            IS_UNIQUE_RECORD(record) &&
1349            !IS_RECORD_VERIFIED(record) &&
1350            !IS_RECORD_PROBING(record)) {
1351            /* Set record to currently being probed status */
1352            record->flags |= PICO_MDNS_RECORD_CURRENTLY_PROBING;
1353            copy = pico_mdns_record_copy(record);
1354            if (copy && pico_tree_insert(&to_probe, copy))
1355                pico_mdns_record_delete((void **)&copy);
1356        }
1357    }
1358    return to_probe;
1359}
1360
1361/* ****************************************************************************
1362 *  Checks whether all MyRecords with a certain claim ID are claimed or not.
1363 *
1364 *  @param claim_id    Claim ID of the records to check for already been probed.
1365 *  @param reg_records Tree in which all MyRecords with claim ID are inserted.
1366 *  @return 1 when all MyRecords with claim ID are probed, 0 when they're not.
1367 * ****************************************************************************/
1368static uint8_t
1369pico_mdns_my_records_claimed_id( uint8_t claim_id,
1370                                 pico_mdns_rtree *reg_records )
1371{
1372    struct pico_tree_node *node = NULL;
1373    struct pico_mdns_record *record = NULL;
1374
1375    /* Initialise the iterator for iterating over my records */
1376    pico_tree_foreach(node, &MyRecords) {
1377        record = node->keyValue;
1378        if (record && record->claim_id == claim_id) {
1379            if (IS_RECORD_VERIFIED(record)) {
1380                if (pico_tree_insert(reg_records, record) == &LEAF) {
1381                    mdns_dbg("MDNS: Failed to insert record in tree\n");
1382                    return 0;
1383				}
1384            } else {
1385                return 0;
1386            }
1387        }
1388    }
1389
1390    return 1;
1391}
1392
1393/* ****************************************************************************
1394 *  Marks mDNS resource records in the tree as registered. Checks MyRecords for
1395 *  for other records with the same claim ID. If all records with the same
1396 *  claim ID as the records in the tree are claimed,
1397 *  the callback will get called.
1398 *
1399 *  @param rtree    Tree with mDNS records that are registered.
1400 *  @param callback Callback will get called when all records are registered.
1401 *  @return Returns 0 when everything went smooth, something else otherwise.
1402 * ****************************************************************************/
1403static int
1404pico_mdns_my_records_claimed( pico_mdns_rtree rtree,
1405                              void (*callback)(pico_mdns_rtree *,
1406                                               char *,
1407                                               void *),
1408                              void *arg )
1409{
1410    PICO_MDNS_RTREE_DECLARE(claimed_records);
1411    struct pico_mdns_record *record = NULL, *myrecord = NULL;
1412    struct pico_tree_node *node = NULL;
1413    uint8_t claim_id = 0;
1414
1415    /* Iterate over records and set the PROBED flag */
1416    pico_tree_foreach(node, &rtree) {
1417        if ((record = node->keyValue)) {
1418            if (!claim_id) {
1419                claim_id = record->claim_id;
1420            }
1421        }
1422
1423        if ((myrecord = pico_tree_findKey(&MyRecords, record))) {
1424            PICO_MDNS_SET_FLAG(myrecord->flags, PICO_MDNS_RECORD_CLAIMED);
1425        }
1426    }
1427
1428    /* If all_claimed is still true */
1429    if (pico_mdns_my_records_claimed_id(claim_id, &claimed_records)) {
1430        callback(&claimed_records, _hostname, arg);
1431    }
1432
1433    pico_tree_destroy(&claimed_records, NULL);
1434
1435    mdns_dbg(">>>>>> DONE - CLAIM SESSION: %d\n", claim_id);
1436
1437    return 0;
1438}
1439
1440/* ****************************************************************************
1441 *  Makes sure the cache flush bit is set of the records which are probed, and
1442 *  set the corresponding MyRecords from 'being probed' to
1443 *  'has been probed'-state.
1444 *
1445 *  @param records mDNS records which are probed.
1446 * ****************************************************************************/
1447static void
1448pico_mdns_my_records_probed( pico_mdns_rtree *records )
1449{
1450    struct pico_tree_node *node = NULL;
1451    struct pico_mdns_record *record = NULL, *found = NULL;
1452
1453    pico_tree_foreach(node, records) {
1454        if ((record = node->keyValue)) {
1455            /* Set the cache flush bit again */
1456            PICO_MDNS_SET_MSB_BE(record->record->rsuffix->rclass);
1457            if ((found = pico_tree_findKey(&MyRecords, record))) {
1458                if (IS_HOSTNAME_RECORD(found)) {
1459                    if (_hostname) {
1460                        PICO_FREE(_hostname);
1461                    }
1462
1463                    _hostname = pico_dns_qname_to_url(found->record->rname);
1464                }
1465
1466                PICO_MDNS_CLR_FLAG(found->flags, PICO_MDNS_RECORD_CURRENTLY_PROBING);
1467                PICO_MDNS_SET_FLAG(found->flags, PICO_MDNS_RECORD_PROBED);
1468            } else{
1469                mdns_dbg("Could not find my corresponding record...\n");
1470            }
1471        }
1472    }
1473}
1474
1475/* MARK: ^ MY RECORDS */
1476/* MARK: v CACHE COHERENCY */
1477#if PICO_MDNS_ALLOW_CACHING == 1
1478/* ****************************************************************************
1479 *  Updates TTL of a cache entry.
1480 *
1481 *  @param record Record of which you want to update the TTL of
1482 *  @param ttl    TTL you want to update the TTL of the record to.
1483 *  @return void
1484 * ****************************************************************************/
1485static inline void
1486pico_mdns_cache_update_ttl( struct pico_mdns_record *record,
1487                            uint32_t ttl )
1488{
1489    if(ttl > 0) {
1490        /* Update the TTL's */
1491        record->record->rsuffix->rttl = long_be(ttl);
1492        record->current_ttl = ttl;
1493    } else {
1494        /* TTL 0 means delete from cache but we need to wait one second */
1495        record->record->rsuffix->rttl = long_be(1u);
1496        record->current_ttl = 1u;
1497    }
1498}
1499
1500static int
1501pico_mdns_cache_flush_name( char *name, struct pico_dns_record_suffix *suffix )
1502{
1503    /* Check if cache flush bit is set */
1504    if (PICO_MDNS_IS_MSB_SET(short_be(suffix->rclass))) {
1505        mdns_dbg("FLUSH - Cache flush bit was set, triggered flush.\n");
1506        if (pico_mdns_rtree_del_name_type(&Cache, name, short_be(suffix->rtype))) {
1507            mdns_dbg("Could not flush records from cache!\n");
1508            return -1;
1509        }
1510    }
1511
1512    return 0;
1513}
1514
1515/* ****************************************************************************
1516 *  Adds a mDNS record to the cache.
1517 *
1518 *  @param record mDNS record to add to the Cache.
1519 *  @return 0 when entry successfully added, something else when it all went ho-
1520 *			rribly wrong...
1521 * ****************************************************************************/
1522static int
1523pico_mdns_cache_add( struct pico_mdns_record *record )
1524{
1525    struct pico_dns_record_suffix *suffix = NULL;
1526    char *name = NULL;
1527    uint32_t rttl = 0;
1528
1529    /* Check params */
1530    if (!record) {
1531        pico_err = PICO_ERR_EINVAL;
1532        return -1;
1533    }
1534    /* 2 paths */
1535
1536    suffix = record->record->rsuffix;
1537    name = record->record->rname;
1538    rttl = long_be(suffix->rttl);
1539
1540    if (pico_mdns_cache_flush_name(name, suffix)) {
1541        return -1;
1542    }
1543    /* 4 paths */
1544
1545    /* Check if the TTL is not 0*/
1546    if (!rttl) {
1547        return -1;
1548    } else {
1549        /* Set current TTL to the original TTL before inserting */
1550        record->current_ttl = rttl;
1551
1552        if (pico_tree_insert(&Cache, record) != NULL)
1553            return -1;
1554
1555        mdns_dbg("RR cached. TICK TACK TICK TACK...\n");
1556
1557        return 0;
1558    }
1559    /* 12 paths */
1560}
1561
1562/* ****************************************************************************
1563 *  Add a copy of an mDNS resource record to the cache tree. Checks whether the
1564 *  entry is already present in the Cache or not.
1565 *
1566 *  @param record Record to add to the Cache-tree
1567 *  @return 0 on grrrreat success, something else on awkward failure.
1568 * ****************************************************************************/
1569static int
1570pico_mdns_cache_add_record( struct pico_mdns_record *record )
1571{
1572    struct pico_mdns_record *found = NULL, *copy = NULL;
1573    uint32_t rttl = 0;
1574
1575    /* Check params */
1576    if (!record) {
1577        pico_err = PICO_ERR_EINVAL;
1578        return -1;
1579    }
1580
1581    /* See if the record is already contained in the cache */
1582    if ((found = pico_tree_findKey(&Cache, record))) {
1583        rttl = long_be(record->record->rsuffix->rttl);
1584        pico_mdns_cache_update_ttl(found, rttl);
1585    } else if ((copy = pico_mdns_record_copy(record))) {
1586        if (pico_mdns_cache_add(copy)) {
1587            pico_mdns_record_delete((void **)&copy);
1588            return -1;
1589        }
1590    } else
1591        return -1;
1592
1593    return 0;
1594}
1595
1596#if PICO_MDNS_CONTINUOUS_REFRESH == 1
1597/* ****************************************************************************
1598 *  Determine if the current TTL is at a refreshing point.
1599 *
1600 *  @param original Original TTL to calculate refreshing points
1601 *  @param current  Current TTL to check.
1602 *  @return 1 when Current TTL is at refresh point. 0 when it's not.
1603 * ****************************************************************************/
1604static int
1605pico_mdns_ttl_at_refresh_time( uint32_t original,
1606                               uint32_t current )
1607{
1608    uint32_t rnd = 0;
1609    rnd = pico_rand() % 3;
1610
1611    if (((original - current ==
1612          ((original * (80 + rnd)) / 100)) ? 1 : 0) ||
1613        ((original - current ==
1614          ((original * (85 + rnd)) / 100)) ? 1 : 0) ||
1615        ((original - current ==
1616          ((original * (90 + rnd)) / 100)) ? 1 : 0) ||
1617        ((original - current ==
1618          ((original * (95 + rnd)) / 100)) ? 1 : 0))
1619        return 1;
1620    else
1621        return 0;
1622}
1623#endif
1624
1625/* ****************************************************************************
1626 *  Utility function to update the TTL of cache entries and check for expired
1627 *  ones. When continuous refreshing is enabled the records will be reconfirmed
1628 *	@ 80%, 85%, 90% and 95% of their original TTL.
1629 * ****************************************************************************/
1630static void
1631pico_mdns_cache_check_expiries( void )
1632{
1633    struct pico_tree_node *node = NULL, *next = NULL;
1634    struct pico_mdns_record *record = NULL;
1635#if PICO_MDNS_CONTINUOUS_REFRESH == 1
1636    uint32_t current = 0, original = 0;
1637    uint16_t type 0;
1638    char *url = NULL;
1639#endif
1640
1641    /* Check for expired cache records */
1642    pico_tree_foreach_safe(node, &Cache, next) {
1643        if ((record = node->keyValue)) {
1644            /* Update current ttl and delete when TTL is 0*/
1645            if ((--(record->current_ttl)) == 0) {
1646                record = pico_tree_delete(&Cache, record);
1647                pico_mdns_record_delete((void **)&record);
1648            }
1649
1650#if PICO_MDNS_CONTINUOUS_REFRESH == 1
1651            /* Determine original and current ttl */
1652            original = long_be(record->record->rsuffix->rttl);
1653            current = record->current_ttl;
1654
1655            /* Cache refresh at 80 or 85/90/95% of TTL + 2% rnd */
1656            if (pico_mdns_ttl_at_refresh_time(original, current)) {
1657                url = pico_dns_qname_to_url(record->record->rname);
1658                type = short_be(record->record->rsuffix->rtype)
1659                       pico_mdns_getrecord_generic(url, type, NULL, NULL);
1660                PICO_FREE(url);
1661            }
1662
1663#endif
1664        }
1665    }
1666}
1667#endif /* PICO_MDNS_ALLOW_CACHING */
1668
1669/* ****************************************************************************
1670 *  Utility function to update the TTL of cookies and check for expired
1671 *  ones. Deletes the expired ones as well.
1672 * ****************************************************************************/
1673static void
1674pico_mdns_cookies_check_timeouts( void )
1675{
1676    struct pico_tree_node *node = NULL, *next = NULL;
1677    struct pico_mdns_cookie *cookie = NULL;
1678
1679    pico_tree_foreach_safe(node, &Cookies, next) {
1680        if ((cookie = node->keyValue) && --(cookie->timeout) == 0) {
1681            /* Call callback to allow error checking */
1682            if (cookie->callback) {
1683                cookie->callback(NULL, NULL, cookie->arg);
1684            }
1685
1686            /* Delete cookie */
1687            cookie = pico_tree_delete(&Cookies, cookie);
1688            pico_mdns_cookie_delete((void **)&cookie);
1689
1690            /* If the request was for a reconfirmation of a record,
1691                flush the corresponding record after the timeout */
1692        }
1693    }
1694}
1695
1696/* ****************************************************************************
1697 *  Global mDNS module tick-function, central point where all the timing is
1698 *  handled.
1699 *
1700 *  @param now  Ignore
1701 *  @param _arg Ignore
1702 * ****************************************************************************/
1703static void
1704pico_mdns_tick( pico_time now, void *_arg )
1705{
1706    IGNORE_PARAMETER(now);
1707    IGNORE_PARAMETER(_arg);
1708
1709#if PICO_MDNS_ALLOW_CACHING == 1
1710    /* Update the cache */
1711    pico_mdns_cache_check_expiries();
1712#endif
1713
1714    /* Update the cookies */
1715    pico_mdns_cookies_check_timeouts();
1716
1717    /* Schedule new tick */
1718    if (!pico_mdns_timer_add(PICO_MDNS_RR_TTL_TICK, pico_mdns_tick, NULL)) {
1719        mdns_dbg("MDNS: Failed to start tick timer\n");
1720        /* TODO Not ticking anymore, what to do? */
1721    }
1722}
1723
1724/* MARK: v MDNS PACKET UTILITIES */
1725
1726/* ****************************************************************************
1727 *  Sends a Multicast packet on the wire to the mDNS destination port.
1728 *
1729 *  @param packet Packet buffer in memory
1730 *  @param len    Size of the packet in bytes
1731 *  @return 0 When the packet is passed successfully on to the lower layers of
1732 *			picoTCP. Doesn't mean the packet is successfully sent on the wire.
1733 * ****************************************************************************/
1734static int
1735pico_mdns_send_packet( pico_dns_packet *packet, uint16_t len )
1736{
1737    /* TODO: why only ipv4 support? */
1738    struct pico_ip4 dst4;
1739
1740    /* Set the destination address to the mDNS multicast-address */
1741    pico_string_to_ipv4(PICO_MDNS_DEST_ADDR4, &dst4.addr);
1742
1743    /* Send packet to IPv4 socket */
1744    return pico_socket_sendto(mdns_sock_ipv4, packet, (int)len, &dst4,
1745                              short_be(mdns_port));
1746}
1747
1748/* ****************************************************************************
1749 *  Sends a Unicast packet on the wire to the mDNS destination port of specific
1750 *  peer in the network
1751 *
1752 *  @param packet Packet buffer in memory
1753 *  @param len    Size of the packet in bytes
1754 *  @param peer   Peer in the network you want to send the packet to.
1755 *  @return 0 When the packet is passed successfully on to the lower layers of
1756 *			picoTCP. Doesn't mean the packet is successfully send on the wire.
1757 * ****************************************************************************/
1758static int
1759pico_mdns_send_packet_unicast( pico_dns_packet *packet,
1760                               uint16_t len,
1761                               struct pico_ip4 peer )
1762{
1763    /* Send packet to IPv4 socket */
1764    return pico_socket_sendto(mdns_sock_ipv4, packet, (int)len, &peer,
1765                              short_be(mdns_port));
1766}
1767
1768
1769/* ****************************************************************************
1770 *  Send DNS records as answers to a peer via unicast
1771 *
1772 *  @param unicast_tree Tree with DNS records to send as answers.
1773 *  @param peer         Peer IPv4-address
1774 *  @return 0 when the packet is properly send, something else otherwise.
1775 * ****************************************************************************/
1776static int
1777pico_mdns_unicast_reply( pico_dns_rtree *unicast_tree,
1778                         pico_dns_rtree *artree,
1779                         struct pico_ip4 peer )
1780{
1781    union pico_address *local_addr = NULL;
1782    pico_dns_packet *packet = NULL;
1783    uint16_t len = 0;
1784
1785    if (pico_tree_count(unicast_tree) > 0) {
1786        /* Create response DNS packet */
1787        packet = pico_dns_answer_create(unicast_tree, NULL, artree, &len);
1788        if (!packet || !len) {
1789            pico_err = PICO_ERR_ENOMEM;
1790            return -1;
1791        }
1792
1793        packet->id = 0;
1794
1795        /* Check if source address is on the local link */
1796        local_addr = (union pico_address *) pico_ipv4_source_find(&peer);
1797        if (!local_addr) {
1798            mdns_dbg("Peer not on same link!\n");
1799            /* Forced response via multicast */
1800
1801            /* RFC6762: 18.6: In both multicast query and response messages,
1802               the RD bit SHOULD be zero on transmission. In
1803               pico_dns_fill_packet_header, the RD bit is set to
1804               PICO_DNS_RD_IS_DESIRED, which is defined to be 1 */
1805            packet->rd = PICO_DNS_RD_NO_DESIRE;
1806
1807
1808            if (pico_mdns_send_packet(packet, len) != (int)len) {
1809                mdns_dbg("Could not send multicast response!\n");
1810                return -1;
1811            }
1812        } else {
1813            /* Send the packet via unicast */
1814            if (pico_mdns_send_packet_unicast(packet, len, peer) != (int)len) {
1815                mdns_dbg("Could not send unicast response!\n");
1816                return -1;
1817            }
1818
1819            mdns_dbg("Unicast response sent successfully!\n");
1820        }
1821
1822        PICO_FREE(packet);
1823    }
1824
1825    return 0;
1826}
1827
1828/* ****************************************************************************
1829 *  Send DNS records as answers to mDNS peers via multicast
1830 *
1831 *  @param multicast_tree Tree with DNS records to send as answers.
1832 *  @return 0 when the packet is properly send, something else otherwise.
1833 * ****************************************************************************/
1834static int
1835pico_mdns_multicast_reply( pico_dns_rtree *multicast_tree,
1836                           pico_dns_rtree *artree )
1837{
1838    pico_dns_packet *packet = NULL;
1839    uint16_t len = 0;
1840
1841    /* If there are any multicast records */
1842    if (pico_tree_count(multicast_tree) > 0) {
1843        /* Create response DNS packet */
1844        packet = pico_dns_answer_create(multicast_tree, NULL, artree, &len);
1845        if (!packet || len == 0) {
1846            pico_err = PICO_ERR_ENOMEM;
1847            return -1;
1848        }
1849
1850        packet->id = 0;
1851
1852        /* RFC6762: 18.6: In both multicast query and response messages,
1853           the RD bit SHOULD be zero on transmission.
1854           In pico_dns_fill_packet_header, the RD bit is set to
1855           PICO_DNS_RD_IS_DESIRED, which is defined to be 1 */
1856        packet->rd = PICO_DNS_RD_NO_DESIRE;
1857
1858        /* Send the packet via multicast */
1859        if (pico_mdns_send_packet(packet, len) != (int)len) {
1860            mdns_dbg("Could not send multicast response!\n");
1861            return -1;
1862        }
1863
1864        mdns_dbg("Multicast response sent successfully!\n");
1865
1866        PICO_FREE(packet);
1867    }
1868
1869    return 0;
1870}
1871
1872/* MARK: ^ MDNS PACKET UTILITIES */
1873/* MARK: ASYNCHRONOUS MDNS RECEPTION */
1874
1875/* ****************************************************************************
1876 *  Merges 2 pico_trees with each other.
1877 *
1878 *  @param dest Destination tree to merge the other tree in.
1879 *  @param src  Source tree to get the node from to insert into the dest-tree.
1880 *  @return Returns 0 when properly merged, or not..
1881 * ****************************************************************************/
1882static int
1883pico_tree_merge( struct pico_tree *dest, struct pico_tree *src )
1884{
1885    struct pico_tree_node *node = NULL;
1886
1887    /* Check params */
1888    if (!dest || !src) {
1889        pico_err = PICO_ERR_EINVAL;
1890        return -1;
1891    }
1892
1893    /* Insert source nodes */
1894    pico_tree_foreach(node, src) {
1895        if (node->keyValue) {
1896            if (pico_tree_insert(dest, node->keyValue) == &LEAF) {
1897                mdns_dbg("MDNS: Failed to insert record in tree\n");
1898                return -1;
1899			}
1900        }
1901    }
1902
1903    return 0;
1904}
1905
1906/* ****************************************************************************
1907 *  Populates an mDNS record tree with answers from MyRecords depending on name
1908 *  , qtype and qclass.
1909 *
1910 *  @param name   Name of records to look for in MyRecords
1911 *  @param qtype  Type of records to look for in MyRecords
1912 *  @param qclass Whether the answer should be sent via unicast or not.
1913 *  @return mDNS record tree with possible answers from MyRecords
1914 * ****************************************************************************/
1915static pico_mdns_rtree
1916pico_mdns_populate_antree( char *name, uint16_t qtype, uint16_t qclass )
1917{
1918    PICO_MDNS_RTREE_DECLARE(antree);
1919    struct pico_tree_node *node = NULL, *next;
1920    struct pico_mdns_record *record = NULL;
1921
1922    /* Create an answer record vector */
1923    if (PICO_DNS_TYPE_ANY == qtype)
1924        antree = pico_mdns_rtree_find_name(&MyRecords, name, 1);
1925    else
1926        antree = pico_mdns_rtree_find_name_type(&MyRecords, name, qtype, 1);
1927
1928    /* Remove answers which aren't successfully registered yet */
1929    pico_tree_foreach_safe(node, &antree, next) {
1930        if ((record = node->keyValue) && !IS_RECORD_VERIFIED(record)) {
1931            pico_tree_delete(&antree, record);
1932        }
1933    }
1934
1935    /* Check if question is a QU-question */
1936    if (PICO_MDNS_IS_MSB_SET(qclass)) {
1937        /* Set all the flags of the answer accordingly */
1938        pico_tree_foreach(node, &antree) {
1939            if ((record = node->keyValue))
1940                PICO_MDNS_SET_FLAG(record->flags,
1941                                   PICO_MDNS_RECORD_SEND_UNICAST);
1942        }
1943    }
1944
1945    return antree;
1946}
1947
1948/* ****************************************************************************
1949 *  Handles a single received question.
1950 *
1951 *  @param question DNS question to parse and handle.
1952 *  @param packet   Received packet in which the DNS question was present.
1953 *  @return mDNS record tree with possible answer to the question. Can possibly
1954 *			be empty.
1955 * ****************************************************************************/
1956static pico_mdns_rtree
1957pico_mdns_handle_single_question( struct pico_dns_question *question,
1958                                  pico_dns_packet *packet )
1959{
1960    struct pico_mdns_cookie *cookie = NULL;
1961    PICO_MDNS_RTREE_DECLARE(antree);
1962    char *qname_original = NULL;
1963    uint16_t qtype = 0, qclass = 0;
1964
1965    /* Check params */
1966    if (!question || !packet) {
1967        pico_err = PICO_ERR_EINVAL;
1968        return antree;
1969    }
1970
1971    /* Decompress single DNS question */
1972    qname_original = pico_dns_question_decompress(question, packet);
1973    mdns_dbg("Question RCVD for '%s'\n", question->qname);
1974
1975    /* Find currently active query cookie */
1976    if ((cookie = pico_mdns_ctree_find_cookie(question->qname,
1977                                              PICO_MDNS_PACKET_TYPE_QUERY))) {
1978        mdns_dbg("Query cookie found for question, suppress duplicate.\n");
1979        cookie->status = PICO_MDNS_COOKIE_STATUS_CANCELLED;
1980    } else {
1981        qtype = short_be(question->qsuffix->qtype);
1982        qclass = short_be(question->qsuffix->qclass);
1983        antree = pico_mdns_populate_antree(question->qname, qtype, qclass);
1984    }
1985
1986    PICO_FREE(question->qname);
1987    question->qname = qname_original;
1988    return antree;
1989}
1990
1991/* ****************************************************************************
1992 *  When a query-cookie is found for a RCVD answer, the cookie should be
1993 *  handled accordingly. This function does that.
1994 *
1995 *  @param cookie Cookie that contains the question for the RCVD answer.
1996 *  @param answer RCVD answer to handle cookie with
1997 *  @return Returns 0 when handling went OK, something else when it didn't.
1998 * ****************************************************************************/
1999static int
2000pico_mdns_handle_cookie_with_answer( struct pico_mdns_cookie *cookie,
2001                                     struct pico_mdns_record *answer )
2002{
2003    PICO_MDNS_RTREE_DECLARE(antree);
2004    uint8_t type = 0, status = 0;
2005
2006    /* Check params */
2007    if (!cookie || !answer) {
2008        pico_err = PICO_ERR_EINVAL;
2009        return -1;
2010    }
2011
2012    type = cookie->type;
2013    status = cookie->status;
2014    if (PICO_MDNS_COOKIE_STATUS_ACTIVE == status) {
2015        if (PICO_MDNS_PACKET_TYPE_PROBE == type) {
2016            /* Conflict occurred, resolve it! */
2017            pico_mdns_cookie_resolve_conflict(cookie, answer->record->rname);
2018        } else if (PICO_MDNS_PACKET_TYPE_QUERY == type) {
2019            if (cookie->callback) {
2020                /* RCVD Answer on query, callback with answer. Callback is
2021                 * responsible for aggregating all the received answers. */
2022                if (pico_tree_insert(&antree, answer) == &LEAF) {
2023                    mdns_dbg("MDNS: Failed to insert answer in tree\n");
2024                    return -1;
2025				}
2026                cookie->callback(&antree, NULL, cookie->arg);
2027            }
2028        } else { /* Don't handle answer cookies with answer */
2029        }
2030    }
2031
2032    return 0;
2033}
2034
2035/* ****************************************************************************
2036 *  Handles a single received answer record.
2037 *
2038 *  @param answer Answer mDNS record.
2039 *  @return 0 when answer is properly handled, something else when it's not.
2040 * ****************************************************************************/
2041static int
2042pico_mdns_handle_single_answer( struct pico_mdns_record *answer )
2043{
2044    struct pico_mdns_cookie *found = NULL;
2045    struct pico_mdns_record *record = NULL;
2046
2047    mdns_dbg("Answer RCVD for '%s'\n", answer->record->rname);
2048
2049    /* Find currently active query cookie */
2050    found = pico_mdns_ctree_find_cookie(answer->record->rname,
2051                                        PICO_MDNS_PACKET_TYPE_QUERY_ANY);
2052    if (found && pico_mdns_handle_cookie_with_answer(found, answer)) {
2053        mdns_dbg("Could not handle found cookie correctly!\n");
2054        return -1;
2055    } else {
2056        mdns_dbg("RCVD an unsolicited record!\n");
2057        if ((record = pico_tree_findKey(&MyRecords, answer)) &&
2058            !IS_RECORD_PROBING(record))
2059            return pico_mdns_record_resolve_conflict(record,
2060                                                     answer->record->rname);
2061    }
2062
2063    return 0;
2064}
2065
2066/* ****************************************************************************
2067 *  Handles a single received authority record.
2068 *
2069 *  @param answer Authority mDNS record.
2070 *  @return 0 when authority is properly handled. -1 when it's not.
2071 * ****************************************************************************/
2072static int
2073pico_mdns_handle_single_authority( struct pico_mdns_record *answer )
2074{
2075    struct pico_mdns_cookie *found = NULL;
2076    char *name = NULL;
2077
2078    name = answer->record->rname;
2079    mdns_dbg("Authority RCVD for '%s'\n", name);
2080
2081    /* Find currently active probe cookie */
2082    if ((found = pico_mdns_ctree_find_cookie(name, PICO_MDNS_PACKET_TYPE_PROBE))
2083        && PICO_MDNS_COOKIE_STATUS_ACTIVE == found->status) {
2084        mdns_dbg("Simultaneous Probing occurred, went tiebreaking...\n");
2085        if (pico_mdns_cookie_apply_spt(found, answer->record) < 0) {
2086            mdns_dbg("Could not apply S.P.T. to cookie!\n");
2087            return -1;
2088        }
2089    }
2090
2091    return 0;
2092}
2093
2094/* ****************************************************************************
2095 *  Handles a single received additional [Temporarily unused]
2096 *
2097 *  @param answer Additional mDNS record.
2098 *  @return 0
2099 * ****************************************************************************/
2100static int
2101pico_mdns_handle_single_additional( struct pico_mdns_record *answer )
2102{
2103    /* Don't need this for now ... */
2104    IGNORE_PARAMETER(answer);
2105    return 0;
2106}
2107
2108/* ****************************************************************************
2109 *  Handles a flat chunk of memory as if it were all questions in it.
2110 *  Generates a tree with responses if there are any questions for records for
2111 *  which host has the authority to answer.
2112 *
2113 *  @param ptr     Pointer-Pointer to location of question section of packet.
2114 *                 Will point to right after the question section on return.
2115 *  @param qdcount Amount of questions contained in the packet
2116 *  @param packet  DNS packet where the questions are present.
2117 *  @return Tree with possible responses on the questions.
2118 * ****************************************************************************/
2119static pico_mdns_rtree
2120pico_mdns_handle_data_as_questions ( uint8_t **ptr,
2121                                     uint16_t qdcount,
2122                                     pico_dns_packet *packet )
2123{
2124    PICO_MDNS_RTREE_DECLARE(antree);
2125    PICO_MDNS_RTREE_DECLARE(rtree);
2126    struct pico_dns_question question;
2127    uint16_t i = 0;
2128
2129    /* Check params */
2130    if ((!ptr) || !packet || !(*ptr)) {
2131        pico_err = PICO_ERR_EINVAL;
2132        return antree;
2133    }
2134
2135    for (i = 0; i < qdcount; i++) {
2136        /* Set qname of the question to the correct location */
2137        question.qname = (char *)(*ptr);
2138
2139        /* Set qsuffix of the question to the correct location */
2140        question.qsuffix = (struct pico_dns_question_suffix *)
2141                           (question.qname + pico_dns_namelen_comp(question.qname) + 1);
2142
2143        /* Handle a single question and merge the returned tree */
2144        rtree = pico_mdns_handle_single_question(&question, packet);
2145        pico_tree_merge(&antree, &rtree);
2146        pico_tree_destroy(&rtree, NULL);
2147
2148        /* Move to next question */
2149        *ptr = (uint8_t *)question.qsuffix +
2150               sizeof(struct pico_dns_question_suffix);
2151    }
2152    if (pico_tree_count(&antree) == 0) {
2153        mdns_dbg("No 'MyRecords' found that corresponds with this query.\n");
2154    }
2155
2156    return antree;
2157}
2158
2159static int
2160pico_mdns_handle_data_as_answers_generic( uint8_t **ptr,
2161                                          uint16_t count,
2162                                          pico_dns_packet *packet,
2163                                          uint8_t type )
2164{
2165    struct pico_mdns_record mdns_answer = {
2166        .record = NULL, .current_ttl = 0,
2167        .flags = 0, .claim_id = 0
2168    };
2169    struct pico_dns_record answer;
2170    char *orname = NULL;
2171    uint16_t i = 0;
2172
2173    /* Check params */
2174    if ((!ptr) || !packet || !(*ptr)) {
2175        pico_err = PICO_ERR_EINVAL;
2176        return -1;
2177    }
2178
2179    /* TODO: When receiving multiple authoritative answers, */
2180    /* they should be sorted in lexicographical order */
2181    /* (just like in pico_mdns_record_am_i_lexi_later) */
2182
2183    for (i = 0; i < count; i++) {
2184        /* Set rname of the record to the correct location */
2185        answer.rname = (char *)(*ptr);
2186
2187        /* Set rsuffix of the record to the correct location */
2188        answer.rsuffix = (struct pico_dns_record_suffix *)
2189                         (answer.rname +
2190                          pico_dns_namelen_comp(answer.rname) + 1u);
2191
2192        /* Set rdata of the record to the correct location */
2193        answer.rdata = (uint8_t *) answer.rsuffix +
2194                       sizeof(struct pico_dns_record_suffix);
2195
2196        /* Make an mDNS record from the DNS answer */
2197        orname = pico_dns_record_decompress(&answer, packet);
2198        mdns_answer.record = &answer;
2199        mdns_answer.record->rname_length = (uint16_t)(pico_dns_strlen(answer.rname) + 1u);
2200
2201        /* Handle a single aswer */
2202        switch (type) {
2203        case 1:
2204            pico_mdns_handle_single_authority(&mdns_answer);
2205            break;
2206        case 2:
2207            pico_mdns_handle_single_additional(&mdns_answer);
2208            break;
2209        default:
2210            pico_mdns_handle_single_answer(&mdns_answer);
2211#if PICO_MDNS_ALLOW_CACHING == 1
2212            pico_mdns_cache_add_record(&mdns_answer);
2213#endif
2214            break;
2215        }
2216
2217        /* Free decompressed name and mDNS record */
2218        PICO_FREE(mdns_answer.record->rname);
2219        answer.rname = orname;
2220
2221        /* Move to next record */
2222        *ptr = (uint8_t *) answer.rdata + short_be(answer.rsuffix->rdlength);
2223    }
2224    return 0;
2225}
2226
2227/* ****************************************************************************
2228 *  Splits an mDNS record tree into two DNS record tree, one to send via
2229 *  unicast, one to send via multicast.
2230 *
2231 *  @param answers        mDNS record tree to split up
2232 *  @param unicast_tree   DNS record tree with unicast answers.
2233 *  @param multicast_tree DNS record tee with multicast answers.
2234 *  @return 0 when the tree is properly split up.
2235 * ****************************************************************************/
2236static int
2237pico_mdns_sort_unicast_multicast( pico_mdns_rtree *answers,
2238                                  pico_dns_rtree *unicast_tree,
2239                                  pico_dns_rtree *multicast_tree )
2240{
2241    struct pico_mdns_record *record = NULL;
2242    struct pico_tree_node *node = NULL;
2243
2244    /* Check params */
2245    if (!answers || !unicast_tree || !multicast_tree) {
2246        pico_err = PICO_ERR_EINVAL;
2247        return -1;
2248    }
2249
2250    pico_tree_foreach(node, answers) {
2251        record = node->keyValue;
2252        if ((record = node->keyValue)) {
2253            if (IS_UNICAST_REQUESTED(record)) {
2254                if (record->record){
2255                	if (pico_tree_insert(unicast_tree, record->record) == &LEAF) {
2256                        mdns_dbg("MDNS: Failed to instert unicast record in tree\n");
2257                        return -1;
2258					}
2259                }
2260            } else {
2261                if (record->record){
2262                	if (pico_tree_insert(multicast_tree, record->record) == &LEAF) {
2263                        mdns_dbg("MDNS: Failed to instert multicast record in tree\n");
2264                        return -1;
2265					}
2266                }
2267            }
2268        }
2269    }
2270
2271    return 0;
2272}
2273
2274static uint16_t
2275pico_mdns_nsec_highest_type( pico_mdns_rtree *rtree )
2276{
2277    struct pico_tree_node *node = NULL, *next = NULL;
2278    struct pico_mdns_record *record = NULL;
2279    uint16_t highest_type = 0, type = 0;
2280
2281    pico_tree_foreach_safe(node, rtree, next) {
2282        if ((record = node->keyValue)) {
2283            if (IS_SHARED_RECORD(record))
2284                pico_tree_delete(rtree, record);
2285
2286            type = short_be(record->record->rsuffix->rtype);
2287            highest_type = (type > highest_type) ? (type) : (highest_type);
2288        }
2289    }
2290
2291    return highest_type;
2292}
2293
2294static void
2295pico_mdns_nsec_gen_bitmap( uint8_t *ptr, pico_mdns_rtree *rtree )
2296{
2297    struct pico_tree_node *node = NULL;
2298    struct pico_mdns_record *record = NULL;
2299    uint16_t type = 0;
2300
2301    pico_tree_foreach(node, rtree) {
2302        if ((record = node->keyValue)) {
2303            type = short_be(record->record->rsuffix->rtype);
2304            *(ptr + 1 + (type / 8)) = (uint8_t)(0x80 >> (type % 8));
2305        }
2306    }
2307}
2308
2309/* ****************************************************************************
2310 *  Generates an NSEC record for a specific name. Looks in MyRecords for unique
2311 *  records with given name and generates the NSEC bitmap from them.
2312 *
2313 *  @param name Name of the records you want to generate a bitmap for.
2314 *  @return Pointer to newly created NSEC record on success, NULL on failure.
2315 * ****************************************************************************/
2316static struct pico_mdns_record *
2317pico_mdns_gen_nsec_record( char *name )
2318{
2319    PICO_MDNS_RTREE_DECLARE(rtree);
2320    struct pico_mdns_record *record = NULL;
2321    uint16_t highest_type = 0, rdlen = 0;
2322    uint8_t bitmap_len = 0, *rdata = NULL, *ptr = NULL;
2323    char *url = NULL;
2324
2325    if (!name) { /* Check params */
2326        pico_err = PICO_ERR_EINVAL;
2327        return NULL;
2328    }
2329
2330    /* Determine the highest type of my unique records with this name */
2331    rtree = pico_mdns_rtree_find_name(&MyRecords, name, 0);
2332    highest_type = pico_mdns_nsec_highest_type(&rtree);
2333
2334    /* Determine the bimap_len */
2335    bitmap_len = (uint8_t)(highest_type / 8);
2336    bitmap_len = (uint8_t)(bitmap_len + ((highest_type % 8) ? (1) : (0)));
2337
2338    /* Provide rdata */
2339    rdlen = (uint16_t)(pico_dns_strlen(name) + 3u + bitmap_len);
2340    if (!(rdata = PICO_ZALLOC((size_t)rdlen))) {
2341        pico_err = PICO_ERR_ENOMEM;
2342        pico_tree_destroy(&rtree, NULL);
2343        return NULL;
2344    }
2345
2346    /* Set the next domain name */
2347    strcpy((char *)rdata, name);
2348    /* Set the bitmap length */
2349    *(ptr = (uint8_t *)(rdata + pico_dns_strlen(name) + 2)) = bitmap_len;
2350    /* Generate the bitmap */
2351    pico_mdns_nsec_gen_bitmap(ptr, &rtree);
2352    pico_tree_destroy(&rtree, NULL);
2353
2354    /* Generate the actual mDNS NSEC record */
2355    if (!(url = pico_dns_qname_to_url(name))) {
2356        PICO_FREE(rdata);
2357        return NULL;
2358    }
2359
2360    record = pico_mdns_record_create(url, (void *)rdata, rdlen,
2361                                     PICO_DNS_TYPE_NSEC,
2362                                     PICO_MDNS_SERVICE_TTL,
2363                                     PICO_MDNS_RECORD_UNIQUE);
2364    PICO_FREE(rdata);
2365    PICO_FREE(url);
2366    return record;
2367}
2368
2369/* ****************************************************************************
2370 *  Checks in additionals if there is an NSEC record already present with given
2371 *  name. If there's not, a new NSEC records will be generated and added to the
2372 *  additional tree.
2373 *
2374 *  @param artree mDNS record-tree containing additional records.
2375 *  @param name   Name to check for.
2376 *  @return 0 when NSEC is present in additional, whether it was already present
2377 *			or a new one is generated doesn't matter.
2378 * ****************************************************************************/
2379static int
2380pico_mdns_additionals_add_nsec( pico_mdns_rtree *artree,
2381                                char *name )
2382{
2383    struct pico_mdns_record *record = NULL, *nsec = NULL;
2384    struct pico_tree_node *node = NULL;
2385    uint16_t type = 0;
2386
2387    /* Check params */
2388    if (!artree || !name) {
2389        pico_err = PICO_ERR_EINVAL;
2390        return -1;
2391    }
2392
2393    /* Check if there is a NSEC already for this name */
2394    pico_tree_foreach(node, artree) {
2395        if (node != &LEAF && (record = node->keyValue)) {
2396            type = short_be(record->record->rsuffix->rtype);
2397            if ((PICO_DNS_TYPE_NSEC == type) && 0 == strcasecmp(record->record->rname, name)) {
2398                return 0;
2399            }
2400        }
2401    }
2402
2403    /* If there is none present generate one for given name */
2404    if ((nsec = pico_mdns_gen_nsec_record(name))) {
2405        if (pico_tree_insert(artree, nsec)) {
2406            pico_mdns_record_delete((void **)nsec);
2407            return -1;
2408        }
2409    }
2410
2411    return 0;
2412}
2413
2414/* ****************************************************************************
2415 *  Adds hostname records to the additional records
2416 *
2417 *  @param artree mDNS record-tree containing additional records.
2418 *  @return 0 when hostname records are added successfully to additionals. Rets
2419 *			something else on failure.
2420 * ****************************************************************************/
2421static int
2422pico_mdns_additionals_add_host( pico_mdns_rtree *artree )
2423{
2424    struct pico_tree_node *node = NULL;
2425    struct pico_mdns_record *record = NULL, *copy = NULL;
2426
2427    pico_tree_foreach(node, &MyRecords) {
2428        record = node->keyValue;
2429        if (record) {
2430            if (IS_HOSTNAME_RECORD(record) && IS_RECORD_VERIFIED(record)) {
2431                copy = pico_mdns_record_copy(record);
2432                if (copy && pico_tree_insert(artree, copy))
2433                    pico_mdns_record_delete((void **)&copy);
2434            }
2435        }
2436    }
2437
2438    return 0;
2439} /* Satic path count: 4 */
2440
2441static void
2442pico_rtree_add_copy( pico_mdns_rtree *tree, struct pico_mdns_record *record )
2443{
2444    struct pico_mdns_record *copy = NULL;
2445
2446    if (!tree || !record) {
2447        pico_err = PICO_ERR_EINVAL;
2448        return;
2449    }
2450
2451    if ((copy = pico_mdns_record_copy(record))) {
2452        if (pico_tree_insert(tree, copy))
2453            pico_mdns_record_delete((void **)&copy);
2454    }
2455}
2456
2457/* ****************************************************************************
2458 *  When a service is found, additional records should be generated and
2459 *  added to either the answer section or the additional sections.
2460 *  This happens here
2461 *
2462 *  @param antree     mDNS record tree with answers to send
2463 *  @param artree     mDNS record tree with additionals to send
2464 *  @param srv_record Found SRV record in the answers
2465 *  @return 0 When additional records are properly generated
2466 * ****************************************************************************/
2467static int
2468pico_mdns_gather_service_meta( pico_mdns_rtree *antree,
2469                               pico_mdns_rtree *artree,
2470                               struct pico_mdns_record *srv_record )
2471{
2472    struct pico_mdns_record *ptr_record = NULL, *meta_record = NULL;
2473    char *sin = NULL, *service = NULL;
2474    uint32_t ttl = 0;
2475
2476    /* Generate proper service instance name and service */
2477    sin = pico_dns_qname_to_url(srv_record->record->rname); // May be leaking
2478
2479    if (!antree || !artree || !sin) {
2480        pico_err = PICO_ERR_EINVAL;
2481        PICO_FREE(sin);
2482        return -1;
2483    } else {
2484        /* Add hostname records */
2485        pico_mdns_additionals_add_host(artree);
2486
2487        service = sin + pico_dns_first_label_length(sin) + 1u;
2488        ttl = long_be(srv_record->record->rsuffix->rttl);
2489
2490        /* Generate PTR records */
2491        ptr_record = pico_mdns_record_create(service, (void *)sin,
2492                                            (uint16_t)strlen(sin),
2493                                            PICO_DNS_TYPE_PTR,
2494                                            ttl, PICO_MDNS_RECORD_SHARED);
2495        /* Meta DNS-SD record */
2496        meta_record = pico_mdns_record_create("_services._dns-sd._udp.local",
2497                                            (void *)service,
2498                                            (uint16_t)strlen(service),
2499                                            PICO_DNS_TYPE_PTR,
2500                                            ttl, PICO_MDNS_RECORD_SHARED);
2501        PICO_FREE(sin); // Free allocated memory
2502        if (!meta_record || !ptr_record) {
2503            mdns_dbg("Could not generate META or PTR records!\n");
2504            pico_mdns_record_delete((void **)&ptr_record);
2505            pico_mdns_record_delete((void **)&meta_record);
2506            return -1;
2507        }
2508
2509        ptr_record->flags |= (PICO_MDNS_RECORD_PROBED |
2510                              PICO_MDNS_RECORD_CLAIMED);
2511        meta_record->flags |= (PICO_MDNS_RECORD_PROBED |
2512                               PICO_MDNS_RECORD_CLAIMED);
2513
2514        /* Add copies to the answer tree */
2515        pico_rtree_add_copy(antree, meta_record);
2516        pico_rtree_add_copy(antree, ptr_record);
2517
2518        /* Insert the created service record in MyRecords, alread in, destroy */
2519        if (pico_tree_insert(&MyRecords, meta_record)) {
2520            mdns_dbg("MDNS: Failed to insert meta record in tree\n");
2521            pico_mdns_record_delete((void **)&meta_record);
2522            pico_mdns_record_delete((void **)&ptr_record);
2523            return -1;
2524        }
2525
2526        if (pico_tree_insert(&MyRecords, ptr_record)) {
2527            mdns_dbg("MDNS: Failed to insert ptr record in tree\n");
2528            pico_mdns_record_delete((void **)&ptr_record);
2529            pico_tree_delete(&MyRecords, meta_record);
2530            pico_mdns_record_delete((void **)&meta_record);
2531        }
2532    }
2533    return 0;
2534} /* Static path count: 9 */
2535
2536/* ****************************************************************************
2537 *  Gathers additional records for a to send response. Checks for services and
2538 *  whether or not there should be NSEC records added to the additional section
2539 *
2540 *  @param antree mDNS record tree with answers to send
2541 *  @param artree mDNS record tree with additionals to send
2542 *  @return Returns 0 when additionals are properly generated and added
2543 * ****************************************************************************/
2544static int
2545pico_mdns_gather_additionals( pico_mdns_rtree *antree,
2546                              pico_mdns_rtree *artree )
2547{
2548    struct pico_tree_node *node = NULL;
2549    struct pico_mdns_record *record = NULL;
2550    int ret = 0;
2551
2552    /* Check params */
2553    if (!antree || !artree) {
2554        pico_err = PICO_ERR_EINVAL;
2555        return -1;
2556    } else {
2557        /* Look for SRV records in the tree */
2558        pico_tree_foreach(node, antree) {
2559            if ((record = node->keyValue) &&
2560                short_be(record->record->rsuffix->rtype) == PICO_DNS_TYPE_SRV &&
2561                (ret = pico_mdns_gather_service_meta(antree, artree, record)))
2562                return ret;
2563        }
2564
2565        /* Look for unique records in the tree to generate NSEC records */
2566        pico_tree_foreach(node, antree) {
2567            if ((record = node->keyValue) && IS_UNIQUE_RECORD(record) &&
2568                (ret = pico_mdns_additionals_add_nsec(artree,
2569                                                    record->record->rname)))
2570                return ret;
2571        }
2572
2573        /* Look for unique records in the additionals to generate NSEC records*/
2574        pico_tree_foreach(node, artree) {
2575            if ((record = node->keyValue) && IS_UNIQUE_RECORD(record) &&
2576                (ret = pico_mdns_additionals_add_nsec(artree,
2577                                                    record->record->rname)))
2578                return ret;
2579        }
2580    }
2581
2582    return 0;
2583} /* Static path count: 9 */
2584
2585/* ****************************************************************************
2586 * Sends mDNS records to either multicast peer via unicast to a single peer.
2587 *
2588 *  @param antree Tree with mDNS records to send as answers
2589 *  @param peer   IPv4-address of peer who this host has RCVD a packet.
2590 *  @return 0 when answers are properly handled, something else otherwise.
2591 * ****************************************************************************/
2592static int
2593pico_mdns_reply( pico_mdns_rtree *antree, struct pico_ip4 peer )
2594{
2595    PICO_DNS_RTREE_DECLARE(antree_m);
2596    PICO_DNS_RTREE_DECLARE(antree_u);
2597    PICO_MDNS_RTREE_DECLARE(artree);
2598    PICO_DNS_RTREE_DECLARE(artree_dummy);
2599    PICO_DNS_RTREE_DECLARE(artree_dns);
2600
2601    /* Try to gather additionals for the to send response */
2602    if (pico_mdns_gather_additionals(antree, &artree)) {
2603        mdns_dbg("Could not gather additionals properly!\n");
2604        return -1;
2605    }
2606
2607    /* Sort the answers into multicast and unicast answers */
2608    pico_mdns_sort_unicast_multicast(antree, &antree_u, &antree_m);
2609
2610    /* Convert the mDNS additional tree to a DNS additional tree to send with
2611     * the the unicast AND the multicast response */
2612    pico_mdns_sort_unicast_multicast(&artree, &artree_dummy, &artree_dns);
2613
2614    /* Send response via unicast */
2615    if (pico_mdns_unicast_reply(&antree_u, &artree_dns, peer)) {
2616        mdns_dbg("Could not sent reply via unicast!\n");
2617        return -1;
2618    }
2619
2620    /* Send response via multicast */
2621    if (pico_mdns_multicast_reply(&antree_m, &artree_dns)) {
2622        mdns_dbg("Could not sent reply via multicast!\n");
2623        return -1;
2624    }
2625
2626    pico_tree_destroy(&antree_m, NULL);
2627    pico_tree_destroy(&antree_u, NULL);
2628    pico_tree_destroy(&artree_dummy, NULL);
2629    pico_tree_destroy(&artree_dns, NULL);
2630    PICO_MDNS_RTREE_DESTROY(&artree);
2631
2632    return 0;
2633}
2634
2635/* ****************************************************************************
2636 *  Parses DNS records from a plain chunk of data and looks for them in the
2637 *  answer tree. If they're found, they will be removed from the tree.
2638 *
2639 *  @param rtree   Tree to look in for known answers
2640 *  @param packet  DNS packet in which to look for known answers
2641 *  @param ancount Amount of answers in the DNS packet
2642 *  @param data    Answer section of the DNS packet as a flat chunk of memory.
2643 *  @return 0 K.A.S. could be properly applied, something else when not.
2644 * ****************************************************************************/
2645static int
2646pico_mdns_apply_k_a_s( pico_mdns_rtree *rtree,
2647                       pico_dns_packet *packet,
2648                       uint16_t ancount,
2649                       uint8_t **data )
2650{
2651    struct pico_tree_node *node = NULL, *next = NULL;
2652    struct pico_mdns_record *record = NULL, ka = {
2653        0
2654    };
2655    struct pico_dns_record answer = {
2656        0
2657    };
2658    uint16_t i = 0;
2659
2660    /* Check params */
2661    if ((!data) || !rtree || !packet || !(*data)) {
2662        pico_err = PICO_ERR_EINVAL;
2663        return -1;
2664    }
2665
2666    for (i = 0; i < ancount; i++) {
2667        /* Set rname of the record to the correct location */
2668        answer.rname = (char *)(*data);
2669
2670        /* Set rsuffix of the record to the correct location */
2671        answer.rsuffix = (struct pico_dns_record_suffix *)
2672                         (answer.rname + pico_dns_namelen_comp(answer.rname) + 1u);
2673
2674        /* Set rdata of the record to the correct location */
2675        answer.rdata = (uint8_t *) answer.rsuffix +
2676                       sizeof(struct pico_dns_record_suffix);
2677
2678        pico_dns_record_decompress(&answer, packet);
2679        ka.record = &answer;
2680
2681        /* If the answer is in the record vector */
2682        pico_tree_foreach_safe(node, rtree, next) {
2683            if ((record = node->keyValue)) {
2684                if (pico_mdns_record_cmp(record, &ka) == 0)
2685                    record = pico_tree_delete(rtree, record);
2686            }
2687        }
2688        PICO_FREE(ka.record->rname);
2689        ka.record = NULL;
2690
2691        /* Move to next record */
2692        *data = (uint8_t *) answer.rdata + short_be(answer.rsuffix->rdlength);
2693    }
2694    return 0;
2695}
2696
2697/* ****************************************************************************
2698 *  Handles a single incoming query packet. Applies Known Answer Suppression
2699 *  after handling as well.
2700 *
2701 *  @param packet Received packet
2702 *  @param peer   IPv4 address of the peer who sent the received packet.
2703 *  @return Returns 0 when the query packet is properly handled.
2704 * ****************************************************************************/
2705static int
2706pico_mdns_handle_query_packet( pico_dns_packet *packet, struct pico_ip4 peer )
2707{
2708    PICO_MDNS_RTREE_DECLARE(antree);
2709    uint16_t qdcount = 0, ancount = 0;
2710    uint8_t *data = NULL;
2711
2712    /* Move to the data section of the packet */
2713    data = (uint8_t *)packet + sizeof(struct pico_dns_header);
2714
2715    /* Generate a list of answers */
2716    qdcount = short_be(packet->qdcount);
2717    antree = pico_mdns_handle_data_as_questions(&data, qdcount, packet);
2718    if (pico_tree_count(&antree) == 0) {
2719        mdns_dbg("No records found that correspond with this query!\n");
2720        return 0;
2721    }
2722
2723    /* Apply Known Answer Suppression */
2724    ancount = short_be(packet->ancount);
2725    if (pico_mdns_apply_k_a_s(&antree, packet, ancount, &data)) {
2726        mdns_dbg("Could not apply known answer suppression!\n");
2727        return -1;
2728    }
2729
2730    /* Try to reply with the left-over answers */
2731    pico_mdns_reply(&antree, peer);
2732    PICO_MDNS_RTREE_DESTROY(&antree);
2733
2734    return 0;
2735}
2736
2737/* ****************************************************************************
2738 *  Handles a single incoming probe packet. Checks for Simultaneous Probe
2739 *  Tiebreaking as well.
2740 *
2741 *  @param packet Received probe packet.
2742 *  @param peer   IPv4 address of the peer who sent the probe packet.
2743 *  @return Returns 0 when the probe packet is properly handled.
2744 * ****************************************************************************/
2745static int
2746pico_mdns_handle_probe_packet( pico_dns_packet *packet, struct pico_ip4 peer )
2747{
2748    PICO_MDNS_RTREE_DECLARE(antree);
2749    uint16_t qdcount = 0, nscount = 0;
2750    uint8_t *data = NULL;
2751
2752    /* Move to the data section of the packet */
2753    data = (uint8_t *)packet + sizeof(struct pico_dns_header);
2754
2755    /* Generate a list of answers */
2756    qdcount = short_be(packet->qdcount);
2757    antree = pico_mdns_handle_data_as_questions(&data, qdcount, packet);
2758
2759    /* Check for Simultaneous Probe Tiebreaking */
2760    nscount = short_be(packet->nscount);
2761    pico_mdns_handle_data_as_answers_generic(&data, nscount, packet, 1);
2762
2763    /* Try to reply with the answers */
2764    if (pico_tree_count(&antree) != 0) {
2765        int retval = pico_mdns_reply(&antree, peer);
2766        PICO_MDNS_RTREE_DESTROY(&antree);
2767        return retval;
2768    }
2769
2770    return 0;
2771}
2772
2773/* ****************************************************************************
2774 *  Handles a single incoming answer packet.
2775 *
2776 *  @param packet Received answer packet.
2777 *  @return Returns 0 when the response packet is properly handled.
2778 * ****************************************************************************/
2779static int
2780pico_mdns_handle_response_packet( pico_dns_packet *packet )
2781{
2782    uint8_t *data = NULL;
2783    uint16_t ancount = 0;
2784
2785    /* Move to the data section of the packet */
2786    data = (uint8_t *)packet + sizeof(struct pico_dns_header);
2787
2788    /* Generate a list of answers */
2789    ancount = short_be(packet->ancount);
2790    if (pico_mdns_handle_data_as_answers_generic(&data, ancount, packet, 0)) {
2791        mdns_dbg("Could not handle data as answers\n");
2792        return -1;
2793    }
2794
2795    return 0;
2796}
2797
2798/* ****************************************************************************
2799 *  Parses an incoming packet and handles it according to the type of the
2800 *  packet. Packet type determination happens in this function.
2801 *
2802 *  @param buf    Memory buffer containing the received packet
2803 *  @param buflen Length in bytes of the memory buffer
2804 *  @param peer   IPv4 address of the peer who sent the received packet.
2805 *  @return 0 when the packet is properly handled. Something else when it's not
2806 * ****************************************************************************/
2807static int
2808pico_mdns_recv( void *buf, int buflen, struct pico_ip4 peer )
2809{
2810    pico_dns_packet *packet = (pico_dns_packet *) buf;
2811    uint16_t qdcount = short_be(packet->qdcount);
2812    uint16_t ancount = short_be(packet->ancount);
2813    uint16_t authcount = short_be(packet->nscount);
2814    uint16_t addcount = short_be(packet->arcount);
2815
2816    /* RFC6762: */
2817    /* 18.3: Messages received with an opcode other than zero MUST be silently */
2818    /* ignored. */
2819    /* 18.11: messages received with non-zero Response Codes MUST be silently */
2820    /* ignored */
2821    if(packet->opcode == 0 && packet->rcode == 0) {
2822        mdns_dbg(">>>>>>> QDcount: %u, ANcount: %u, NScount: %u, ARcount: %u\n",
2823                 qdcount, ancount, authcount, addcount);
2824
2825        IGNORE_PARAMETER(buflen);
2826        IGNORE_PARAMETER(addcount);
2827
2828        /* DNS PACKET TYPE DETERMINATION */
2829        if ((qdcount > 0)) {
2830            if (authcount > 0) {
2831                mdns_dbg(">>>>>>> RCVD a mDNS probe query:\n");
2832                /* Packet is probe query */
2833                if (pico_mdns_handle_probe_packet(packet, peer) < 0) {
2834                    mdns_dbg("Could not handle mDNS probe query!\n");
2835                    return -1;
2836                }
2837            } else {
2838                mdns_dbg(">>>>>>> RCVD a plain mDNS query:\n");
2839                /* Packet is a plain query */
2840                if (pico_mdns_handle_query_packet(packet, peer) < 0) {
2841                    mdns_dbg("Could not handle plain DNS query!\n");
2842                    return -1;
2843                }
2844            }
2845        } else {
2846            if (ancount > 0) {
2847                mdns_dbg(">>>>>>> RCVD a mDNS response:\n");
2848                /* Packet is a response */
2849                if (pico_mdns_handle_response_packet(packet) < 0) {
2850                    mdns_dbg("Could not handle DNS response!\n");
2851                    return -1;
2852                }
2853            } else {
2854                /* Something went wrong here... */
2855                mdns_dbg("RCVD Packet contains no questions or answers...\n");
2856                return -1;
2857            }
2858        }
2859    }
2860
2861    return 0;
2862}
2863
2864/* ****************************************************************************
2865 *  picoTCP callback for UDP IPv4 Socket events
2866 *
2867 *  @param ev Determination of the occurred event
2868 *  @param s  Socket on which the event occurred
2869 * ****************************************************************************/
2870static void
2871pico_mdns_event4( uint16_t ev, struct pico_socket *s )
2872{
2873    char *recvbuf = NULL;
2874    struct pico_ip4 peer = {
2875        0
2876    };
2877    int pico_read = 0;
2878    uint16_t port = 0;
2879
2880    /* process read event, data available */
2881    if (ev == PICO_SOCK_EV_RD) {
2882        mdns_dbg("\n>>>>>>> READ EVENT! <<<<<<<\n");
2883        recvbuf = PICO_ZALLOC(PICO_MDNS_MAXBUF);
2884        if (!recvbuf) {
2885            pico_err = PICO_ERR_ENOMEM;
2886            return;
2887        }
2888
2889        /* Receive while data is available in socket buffer */
2890        while((pico_read = pico_socket_recvfrom(s, recvbuf, PICO_MDNS_MAXBUF,
2891                                                &peer, &port)) > 0) {
2892            /* Handle the MDNS data received */
2893            pico_mdns_recv(recvbuf, pico_read, peer);
2894        }
2895        PICO_FREE(recvbuf);
2896        mdns_dbg(">>>>>>>>>>>>>><<<<<<<<<<<<<\n\n");
2897    } else
2898        mdns_dbg("Socket Error received. Bailing out.\n");
2899}
2900
2901/* MARK: ADDRESS RESOLUTION */
2902
2903/* ****************************************************************************
2904 *  Send a mDNS query packet on the wire. This is scheduled with a pico_timer-
2905 *  event.
2906 *
2907 *  @param now Ignore
2908 *  @param arg Void-pointer to query-cookie
2909 * ****************************************************************************/
2910static void
2911pico_mdns_send_query_packet( pico_time now, void *arg )
2912{
2913    struct pico_mdns_cookie *cookie = (struct pico_mdns_cookie *)arg;
2914    pico_dns_qtree *questions = NULL;
2915    pico_dns_packet *packet = NULL;
2916    uint16_t len = 0;
2917
2918    IGNORE_PARAMETER(now);
2919
2920    /* Parse in the cookie */
2921    if (!cookie || cookie->type != PICO_MDNS_PACKET_TYPE_QUERY)
2922        return;
2923
2924    /* Create DNS query packet */
2925    questions = &(cookie->qtree);
2926    if (!(packet = pico_dns_query_create(questions, NULL, NULL, NULL, &len))) {
2927        mdns_dbg("Could not create query packet!\n");
2928        return;
2929    }
2930
2931    packet->id = 0;
2932
2933    /* RFC6762: 18.6: In both multicast query and response messages,
2934       the RD bit SHOULD be zero on transmission. In pico_dns_fill_packet_header,
2935       the RD bit is set to PICO_DNS_RD_IS_DESIRED, which is defined to be 1 */
2936    packet->rd = PICO_DNS_RD_NO_DESIRE;
2937
2938    if (cookie->status != PICO_MDNS_COOKIE_STATUS_CANCELLED) {
2939        cookie->status = PICO_MDNS_COOKIE_STATUS_ACTIVE;
2940        if(pico_mdns_send_packet(packet, len) != (int)len) {
2941            mdns_dbg("Send error occurred!\n");
2942            return;
2943        }
2944
2945        mdns_dbg("DONE - Sent query.\n");
2946    } else {
2947        mdns_dbg("DONE - Duplicate query suppressed.\n");
2948        pico_timer_cancel(cookie->send_timer);
2949        /* Remove cookie from Cookies */
2950        cookie = pico_tree_delete(&Cookies, cookie);
2951        pico_mdns_cookie_delete((void **)&cookie);
2952    }
2953
2954    PICO_FREE(packet);
2955}
2956
2957/* ****************************************************************************
2958 *  Generates a mDNS query packet and schedules a sending on the wire.
2959 *
2960 *  @param url      URL for the name of the question contained in the query
2961 *  @param type     DNS type of the question contained in the query
2962 *  @param callback Callback to call when a response on this query is RCVD.
2963 *  @return 0 When the query is successfully generated and scheduled for sending
2964 * ****************************************************************************/
2965static int
2966pico_mdns_getrecord_generic( const char *url, uint16_t type,
2967                             void (*callback)(pico_mdns_rtree *,
2968                                              char *,
2969                                              void *),
2970                             void *arg)
2971{
2972    struct pico_mdns_cookie *cookie = NULL;
2973    PICO_DNS_QTREE_DECLARE(qtree);
2974    PICO_MDNS_RTREE_DECLARE(antree);
2975    PICO_MDNS_RTREE_DECLARE(artree);
2976    struct pico_dns_question *q = NULL;
2977    uint16_t l = 0;
2978
2979    /* Create a single question and add it to the tree */
2980    q = pico_mdns_question_create(url, &l, PICO_PROTO_IPV4, type, 0, 0);
2981    if (!q) {
2982        mdns_dbg("question_create returned NULL!\n");
2983        return -1;
2984    }
2985
2986    if (pico_tree_insert(&qtree, q)) {
2987    	mdns_dbg("inserting query into tree failed!\n");
2988        pico_dns_question_delete((void **)&q);
2989		return -1;
2990	}
2991
2992
2993    /* Create a mDNS cookie to send */
2994    if (!(cookie = pico_mdns_cookie_create(qtree, antree, artree, 1,
2995                                           PICO_MDNS_PACKET_TYPE_QUERY,
2996                                           callback, arg))) {
2997        PICO_DNS_QTREE_DESTROY(&qtree);
2998        mdns_dbg("cookie_create returned NULL!\n");
2999        return -1;
3000    }
3001
3002    /* Add cookie to Cookies to be able to find it afterwards */
3003    if(pico_tree_insert(&Cookies, cookie) ){
3004		mdns_dbg("inserting cookie into tree failed!\n");
3005        PICO_DNS_QTREE_DESTROY(&qtree);
3006        pico_mdns_cookie_delete((void **)&cookie);
3007        return -1;
3008	}
3009
3010    /* Create new pico_timer-event to send packet */
3011    if (!pico_mdns_timer_add((pico_rand() % 120) + 20, pico_mdns_send_query_packet,
3012                   (void *)cookie)) {
3013        mdns_dbg("MDNS: Failed to start send_query_packet timer\n");
3014        pico_tree_delete(&Cookies, cookie);
3015        pico_mdns_cookie_delete((void**)&cookie);
3016        pico_dns_question_delete((void**)&q);
3017        return -1;
3018    }
3019
3020    return 0;
3021}
3022
3023/* ****************************************************************************
3024 *  API-call to query a record with a certain URL and type. First checks the
3025 *  Cache for this record. If no cache-entry is found, a query will be sent on
3026 *  the wire for this record.
3027 *
3028 *  @param url      URL to query for.
3029 *  @param type     DNS type to query for.
3030 *  @param callback Callback to call when records are found for the query.
3031 *  @return 0 when query is correctly parsed, something else on failure.
3032 * ****************************************************************************/
3033int
3034pico_mdns_getrecord( const char *url, uint16_t type,
3035                     void (*callback)(pico_mdns_rtree *,
3036                                      char *,
3037                                      void *),
3038                     void *arg )
3039{
3040#if PICO_MDNS_ALLOW_CACHING == 1
3041    PICO_MDNS_RTREE_DECLARE(cache_hits);
3042    char *name = NULL;
3043#endif
3044
3045    /* Check params */
3046    if (!url) {
3047        pico_err = PICO_ERR_EINVAL;
3048        return -1;
3049    }
3050
3051    /* First, try to find records in the cache */
3052#if PICO_MDNS_ALLOW_CACHING == 1
3053    name = pico_dns_url_to_qname(url);
3054    cache_hits = pico_mdns_rtree_find_name_type(&Cache, name, type, 0);
3055    PICO_FREE(name);
3056    if (pico_tree_count(&cache_hits) > 0) {
3057        mdns_dbg("CACHE HIT! Passed cache records to callback.\n");
3058        callback(&cache_hits, NULL, arg);
3059    } else {
3060#endif
3061    mdns_dbg("CACHE MISS! Trying to resolve URL '%s'...\n", url);
3062    return pico_mdns_getrecord_generic(url, type, callback, arg);
3063#if PICO_MDNS_ALLOW_CACHING == 1
3064}
3065return 0;
3066#endif
3067}
3068
3069/* MARK: PROBING & ANNOUNCING */
3070
3071/* ****************************************************************************
3072 *  Function to create an announcement from an mDNS cookie and send it on the
3073 *  wire.
3074 *
3075 *  @param now Ignore
3076 *  @param arg Void-pointer to mDNS announcement cookie
3077 * ***************************************************************************/
3078static void
3079pico_mdns_send_announcement_packet( pico_time now, void *arg )
3080{
3081    struct pico_mdns_cookie *cookie = (struct pico_mdns_cookie *)arg;
3082
3083    /* Check params */
3084    IGNORE_PARAMETER(now);
3085    if (!cookie) {
3086        return;
3087    }
3088
3089    cookie->status = PICO_MDNS_COOKIE_STATUS_ACTIVE;
3090    if (cookie->count > 0) {
3091        /* Send the announcement on the wire */
3092        pico_mdns_reply(&(cookie->antree), inaddr_any);
3093        mdns_dbg("DONE - Sent announcement!\n");
3094
3095        /* The Multicast DNS responder MUST send at least two unsolicited
3096           responses, one second apart.  To provide increased robustness
3097           against packet loss, a responder MAY send up to eight unsolicited
3098           responses, provided that the interval between unsolicited
3099           responses increases by at least a factor of two with
3100           every response sent.
3101         */
3102        --(cookie->count);
3103        if (cookie->count == 0) {
3104            cookie->status = PICO_MDNS_COOKIE_STATUS_INACTIVE;
3105
3106            /* Update the states of the records */
3107            pico_mdns_my_records_claimed(cookie->antree,
3108                                         cookie->callback,
3109                                         cookie->arg);
3110
3111            /* Try to delete the cookie */
3112            pico_tree_delete(&Cookies, cookie);
3113            pico_mdns_cookie_delete((void **)&cookie);
3114        }
3115        else{
3116            /*
3117               A responder MAY send up to eight unsolicited responses,
3118               provided that the interval between unsolicited responses increases
3119               by at least a factor of two with every response sent.
3120               Starting at 1 second.
3121               So we bithsift to get our powers of two and we multiply by 1000 to
3122               get our miliseconds.
3123             */
3124            if (!pico_mdns_timer_add((pico_time)((1 << (PICO_MDNS_ANNOUNCEMENT_COUNT - cookie->count - 1))
3125                                       * 1000), pico_mdns_send_announcement_packet, cookie)) {
3126                mdns_dbg("MDNS: Failed to start send_announcement_packet timer\n");
3127                /* TODO no idea what the consequences of this are */
3128
3129            }
3130        }
3131    }
3132}
3133
3134/* ****************************************************************************
3135 *  Announces all 'my records' which passed the probing-step or just shared
3136 *  records.
3137 *
3138 *  @param callback Gets called when all records in the cookie are announced.
3139 *  @return 0 When the host successfully started announcing.
3140 * ****************************************************************************/
3141static int
3142pico_mdns_announce( void (*callback)(pico_mdns_rtree *,
3143                                     char *,
3144                                     void *),
3145                    void *arg )
3146{
3147    struct pico_mdns_cookie *announcement_cookie = NULL;
3148    PICO_DNS_QTREE_DECLARE(qtree);
3149    PICO_MDNS_RTREE_DECLARE(antree);
3150    PICO_MDNS_RTREE_DECLARE(artree);
3151
3152    /* Check params */
3153    if (!callback) {
3154        pico_err = PICO_ERR_EINVAL;
3155        return -1;
3156    }
3157
3158    IGNORE_PARAMETER(arg);
3159
3160    /* Find out which resource records can be announced */
3161    antree = pico_mdns_my_records_find_probed();
3162    if (pico_tree_count(&antree) == 0) {
3163        return 0;
3164    }
3165
3166    /* Create a mDNS packet cookie */
3167    if (!(announcement_cookie = pico_mdns_cookie_create(qtree, antree, artree,
3168                                                        PICO_MDNS_ANNOUNCEMENT_COUNT,
3169                                                        PICO_MDNS_PACKET_TYPE_ANNOUNCEMENT,
3170                                                        callback, arg))) {
3171        mdns_dbg("cookie_create returned NULL!\n");
3172        PICO_MDNS_RTREE_DESTROY(&antree);
3173        return -1;
3174    }
3175
3176    /* Send a first unsolicited announcement */
3177    pico_mdns_send_announcement_packet(0, announcement_cookie);
3178    mdns_dbg("DONE - Started announcing.\n");
3179
3180    return 0;
3181}
3182
3183/* ****************************************************************************
3184 *  Makes sure the cache flush bit of the to probe records is cleared, and
3185 *  generates a DNS record tree to insert in the Authority Section of the DNS
3186 *  packet
3187 *
3188 *  @param records mDNS records to probe.
3189 *  @return DNS record tree to with actual DNS records to insert in Authority
3190 *			Section of probe packet.
3191 * ****************************************************************************/
3192static pico_dns_rtree
3193pico_mdns_gen_probe_auths( pico_mdns_rtree *records )
3194{
3195    PICO_DNS_RTREE_DECLARE(nstree);
3196    struct pico_tree_node *node = NULL;
3197    struct pico_mdns_record *record = NULL;
3198
3199    pico_tree_foreach(node, records) {
3200        if ((record = node->keyValue) && record->record) {
3201            /* Clear the cache flush bit for authority records in probes */
3202            PICO_MDNS_CLR_MSB_BE(record->record->rsuffix->rclass);
3203            /* Only the actual DNS records is required */
3204            if (pico_tree_insert(&nstree, record->record) == &LEAF) {
3205                mdns_dbg("MDNS: Failed to insert record in tree\n");
3206                break;
3207			}
3208        }
3209    }
3210
3211    return nstree;
3212}
3213
3214/* ****************************************************************************
3215 *  Function to create a probe from an mDNS cookie and send it on the wire.
3216 *
3217 *  @param now Ignore
3218 *  @param arg Void-pointer to mDNS probe cookie
3219 * ****************************************************************************/
3220static void
3221pico_mdns_send_probe_packet( pico_time now, void *arg )
3222{
3223    struct pico_mdns_cookie *cookie = (struct pico_mdns_cookie *)arg;
3224    pico_dns_packet *packet = NULL;
3225    PICO_DNS_RTREE_DECLARE(nstree);
3226    uint16_t len = 0;
3227
3228    /* Check params */
3229    IGNORE_PARAMETER(now);
3230    /* if (!cookie || (cookie->type == PICO_MDNS_COOKIE_STATUS_INACTIVE)) { */
3231    if (!cookie || (cookie->type != PICO_MDNS_PACKET_TYPE_PROBE)) {
3232        pico_err = PICO_ERR_EINVAL;
3233        return;
3234    } else {
3235        /* Set the cookie to the active state */
3236        cookie->status = PICO_MDNS_COOKIE_STATUS_ACTIVE;
3237        if (cookie->count > 0) {
3238            --(cookie->count);
3239
3240            /* Generate authority records */
3241            nstree = pico_mdns_gen_probe_auths(&(cookie->antree));
3242
3243            /* Create an mDNS answer */
3244            if (!(packet = pico_dns_query_create(&(cookie->qtree), NULL,
3245                                                &nstree, NULL, &len))) {
3246                PICO_DNS_RTREE_DESTROY(&nstree);
3247                mdns_dbg("Could not create probe packet!\n");
3248                return;
3249            }
3250
3251            pico_tree_destroy(&nstree, NULL);
3252
3253            /* RFC6762: 18.1 */
3254            packet->id = 0;
3255
3256            /* RFC6762: 18.6: In both multicast query and response messages,
3257            the RD bit SHOULD be zero on transmission.
3258            In pico_dns_fill_packet_header, the RD bit is set to
3259            PICO_DNS_RD_IS_DESIRED, which is defined to be 1 */
3260            packet->rd = PICO_DNS_RD_NO_DESIRE;
3261
3262            /* Send the mDNS answer unsolicited via multicast */
3263            if(pico_mdns_send_packet(packet, len) != (int)len) {
3264                mdns_dbg("Send error occurred!\n");
3265                return;
3266            }
3267
3268            PICO_FREE(packet);
3269
3270            mdns_dbg("DONE - Sent probe!\n");
3271
3272            /* Probes should be sent with a delay in between of 250 ms */
3273            if (PICO_MDNS_COOKIE_STATUS_ACTIVE == cookie->status ) {
3274                cookie->send_timer = pico_mdns_timer_add(250,
3275                                                    pico_mdns_send_probe_packet,
3276                                                    (void *)cookie);
3277                if (!cookie->send_timer) {
3278                    mdns_dbg("MDNS: Failed to start send_probe_packet timer\n");
3279                    /* TODO no idea what the consequences of this are */
3280                    return;
3281                }
3282            }
3283        } else {
3284            mdns_dbg("DONE - Probing.\n");
3285
3286            pico_mdns_my_records_probed(&(cookie->antree));
3287
3288            /* Start announcing */
3289            cookie->count = PICO_MDNS_ANNOUNCEMENT_COUNT;
3290            cookie->type = PICO_MDNS_PACKET_TYPE_ANNOUNCEMENT;
3291            pico_mdns_send_announcement_packet(0, (void*) cookie);
3292        }
3293    }
3294} /* Static path count: 10 */
3295
3296/* ****************************************************************************
3297 *  Adds a new probe question to the probe cookie questions, if a probe question
3298 *  for a new is already present in the question-tree, it will not be generated
3299 *  and inserted again
3300 *
3301 *  @param qtree Probe question tree
3302 *  @param name  Name for which the function has to create a probe question
3303 *  @return 0 when the probe question is already present or added successfully.
3304 * ****************************************************************************/
3305static int
3306pico_mdns_add_probe_question( pico_dns_qtree *qtree,
3307                              char *name )
3308{
3309    struct pico_dns_question *new = NULL;
3310    char *url = NULL;
3311    uint16_t qlen = 0;
3312    uint8_t flags = PICO_MDNS_QUESTION_FLAG_PROBE;
3313
3314#if PICO_MDNS_PROBE_UNICAST == 1
3315    flags |= PICO_MDNS_QUESTION_FLAG_UNICAST_RES;
3316#endif
3317
3318    /* Convert name to URL and try to create a new probe question */
3319    if (!(url = pico_dns_qname_to_url(name)))
3320        return -1;
3321
3322    mdns_dbg("Probe question for URL: %s\n", url);
3323    if (!(new = pico_mdns_question_create(url, &qlen, PICO_PROTO_IPV4,
3324                                          PICO_DNS_TYPE_ANY, flags, 0))) {
3325        PICO_FREE(url);
3326        return -1;
3327    }
3328
3329    PICO_FREE(url);
3330
3331    /* Try to find an existing question in the vector */
3332    if (pico_tree_insert(qtree, new))
3333        pico_dns_question_delete((void **)&new);
3334
3335    return 0;
3336}
3337
3338/* ****************************************************************************
3339 *  Find any of my record that need to be probed and try to probe them.
3340 *
3341 *  @param callback Callback to call when all records are properly registered
3342 *  @return When host successfully started probing.
3343 * ****************************************************************************/
3344static int pico_mdns_probe( void (*callback)(pico_mdns_rtree *,
3345                                             char *,
3346                                             void *),
3347                            void *arg )
3348{
3349    struct pico_mdns_cookie *cookie = NULL;
3350    struct pico_mdns_record *record = NULL;
3351    struct pico_tree_node *node = NULL;
3352    PICO_DNS_QTREE_DECLARE(qtree);
3353    PICO_MDNS_RTREE_DECLARE(antree);
3354    PICO_MDNS_RTREE_DECLARE(artree);
3355
3356    /* Check params */
3357    if (!callback) {
3358        pico_err = PICO_ERR_EINVAL;
3359        return -1;
3360    } else {
3361        /* Find my records that need to pass the probing step first
3362        * All records that don't have their PROBED flag set and
3363        * are not being probed at hte moment are added to the tree
3364        */
3365        antree = pico_mdns_my_records_find_to_probe();
3366
3367        /* Create probe questions for the records to be probed  */
3368        pico_tree_foreach(node, &antree) {
3369            if ((record = node->keyValue)) {
3370                pico_mdns_add_probe_question(&qtree, record->record->rname);
3371            }
3372        }
3373
3374        /* Create a mDNS packet to send */
3375        cookie = pico_mdns_cookie_create(qtree, antree, artree,
3376                                        PICO_MDNS_PROBE_COUNT,
3377                                        PICO_MDNS_PACKET_TYPE_PROBE,
3378                                        callback, arg);
3379        if (!cookie) {
3380            mdns_dbg("Cookie_create returned NULL @ probe()!\n");
3381            PICO_DNS_QTREE_DESTROY(&qtree);
3382            PICO_MDNS_RTREE_DESTROY(&antree);
3383            return -1;
3384        }
3385
3386        /* Add the probe cookie to the cookie tree */
3387        if (pico_tree_insert(&Cookies, cookie)) {
3388            pico_mdns_cookie_delete((void **)&cookie);
3389            return -1;
3390        }
3391
3392        /* RFC6762: 8.1. Probing */
3393        /* When ready to send its Multicast DNS probe packet(s) the host should */
3394        /* first wait for a short random delay time, uniformly distributed in */
3395        /* the range 0-250 ms. */
3396        cookie->send_timer = pico_mdns_timer_add(pico_rand() % 250,
3397                                            pico_mdns_send_probe_packet,
3398                                            (void *)cookie);
3399        if (!cookie->send_timer) {
3400            mdns_dbg("MDNS: Failed to start send_probe_packet timer\n");
3401            pico_tree_delete(&Cookies, cookie);
3402            pico_mdns_cookie_delete((void**)&cookie);
3403            return -1;
3404        }
3405
3406        mdns_dbg("DONE - Started probing.\n");
3407    }
3408    return 0;
3409} /* Static path count: 9 */
3410
3411/* MARK: API functions */
3412
3413/* ****************************************************************************
3414 *  Claim or reclaim all the mDNS records contain in a tree in one single call
3415 *
3416 *  @param rtree    mDNS record tree with records to claim
3417 *  @param reclaim  Whether or not the records in tree should be reclaimed.
3418 *  @param callback Callback to call when all records are properly registered
3419 *  @return 0 When claiming didn't horribly fail.
3420 * ****************************************************************************/
3421static int
3422pico_mdns_claim_generic( pico_mdns_rtree rtree,
3423                         uint8_t reclaim,
3424                         void (*callback)(pico_mdns_rtree *,
3425                                          char *,
3426                                          void *),
3427                         void *arg )
3428{
3429    /* Check if arguments are passed correctly */
3430    if (!callback) {
3431        mdns_dbg("NULL pointers passed to 'pico_mdns_claim()'!\n");
3432        pico_err = PICO_ERR_EINVAL;
3433        return -1;
3434    }
3435
3436    /* Check if module is initialised */
3437    if (!mdns_sock_ipv4) {
3438        mdns_dbg("Socket not initialised, did you call 'pico_mdns_init()'?\n");
3439        pico_err = PICO_ERR_EINVAL;
3440        return -1;
3441    }
3442
3443    /* 1.) Appending records to 'my records' */
3444    pico_mdns_my_records_add(&rtree, reclaim);
3445
3446    /* 2a.) Try to probe any records */
3447    pico_mdns_probe(callback, arg);
3448
3449    /* 2b.) Try to announce any records */
3450    pico_mdns_announce(callback, arg);
3451
3452    return 0;
3453}
3454
3455/* ****************************************************************************
3456 *  Claim all different mDNS records in a tree in a single API-call. All records
3457 *  in tree are called in a single new claim-session.
3458 *
3459 *  @param rtree    mDNS record tree with records to claim
3460 *  @param callback Callback to call when all record are properly claimed.
3461 *  @return 0 When claiming didn't horribly fail.
3462 * ****************************************************************************/
3463int
3464pico_mdns_claim( pico_mdns_rtree rtree,
3465                 void (*callback)(pico_mdns_rtree *,
3466                                  char *,
3467                                  void *),
3468                 void *arg )
3469{
3470    return pico_mdns_claim_generic(rtree, PICO_MDNS_NO_RECLAIM, callback, arg);
3471}
3472
3473/* ****************************************************************************
3474 *  Reclaim records when a conflict occurred, claim-session will stay the same
3475 *  as the session in which the conflict occurred.
3476 *
3477 *  @param rtree    mDNS record tree with records to claim
3478 *  @param callback Callback to call when all record are properly claimed.
3479 *  @return 0 When claiming didn't horribly fail.
3480 * ****************************************************************************/
3481static int
3482pico_mdns_reclaim( pico_mdns_rtree rtree,
3483                   void (*callback)(pico_mdns_rtree *,
3484                                    char *,
3485                                    void *),
3486                   void *arg )
3487{
3488    return pico_mdns_claim_generic(rtree, PICO_MDNS_RECLAIM, callback, arg);
3489}
3490
3491/* ****************************************************************************
3492 *  Tries to claim a hostname for this machine. Claims automatically a
3493 *  unique A record with the IPv4-address of this host.
3494 *  The hostname won't be set directly when this functions returns,
3495 *  but only if the claiming of the unique record succeeded.
3496 *  Init-callback will be called when the hostname-record is successfully
3497 *  registered.
3498 *
3499 *  @param url URL to set the hostname to.
3500 *  @param arg Argument to pass to the init-callback.
3501 *  @return 0 when the host started registering the hostname-record successfully,
3502 *          Returns something else when it didn't succeeded.
3503 * ****************************************************************************/
3504int
3505pico_mdns_tryclaim_hostname( const char *url, void *arg )
3506{
3507    PICO_MDNS_RTREE_DECLARE(rtree);
3508    struct pico_mdns_record *record = NULL;
3509
3510    /* Check if module is initialised */
3511    if (!mdns_sock_ipv4) {
3512        mdns_dbg("mDNS socket not initialised, did you call 'pico_mdns_init()'?\n");
3513        pico_err = PICO_ERR_EINVAL;
3514        return -1;
3515    } else {
3516        /* Create an A record for hostname */
3517        record = pico_mdns_record_create(url,
3518                                        &(mdns_sock_ipv4->local_addr.ip4.addr),
3519                                        PICO_SIZE_IP4, PICO_DNS_TYPE_A,
3520                                        PICO_MDNS_DEFAULT_TTL,
3521                                        (PICO_MDNS_RECORD_UNIQUE |
3522                                        PICO_MDNS_RECORD_HOSTNAME));
3523        if (!record) {
3524            mdns_dbg("Could not create A record for hostname %s!\n",
3525                    strerror(pico_err));
3526            return -1;
3527        }
3528
3529        /* TODO: Create IPv6 record */
3530        /* TODO: Create a reverse resolution record */
3531
3532        /* Try to claim the record */
3533        if (pico_tree_insert(&rtree, record)) {
3534            pico_mdns_record_delete((void **)&record);
3535            return -1;
3536        }
3537
3538        if (pico_mdns_claim(rtree, init_callback, arg)) {
3539            mdns_dbg("Could not claim record for hostname %s!\n", url);
3540            PICO_MDNS_RTREE_DESTROY(&rtree);
3541            return -1;
3542        }
3543
3544        pico_tree_destroy(&rtree, NULL);
3545    }
3546    return 0;
3547} /* Static path count: 9 */
3548
3549/* ****************************************************************************
3550 *  Get the hostname for this machine.
3551 *
3552 *  @return Returns the hostname for this machine when the module is initialised
3553 *			Returns NULL when the module is not initialised.
3554 * ****************************************************************************/
3555const char *
3556pico_mdns_get_hostname( void )
3557{
3558    /* Check if module is initialised */
3559    if (!mdns_sock_ipv4) {
3560        mdns_dbg("mDNS socket not initialised, did you call 'pico_mdns_init()'?\n");
3561        pico_err = PICO_ERR_EINVAL;
3562        return NULL;
3563    }
3564
3565    return (const char *)_hostname;
3566}
3567
3568static void
3569pico_mdns_cleanup( void )
3570{
3571    /* Delete socket if it was previously opened */
3572    if (mdns_sock_ipv4) {
3573        pico_socket_del(mdns_sock_ipv4);
3574    }
3575
3576    /* Clear out every memory structure used by mDNS */
3577#if PICO_MDNS_ALLOW_CACHING == 1
3578    PICO_MDNS_RTREE_DESTROY(&Cache);
3579#endif /* PICO_MDNS_ALLOW_CACHING */
3580    PICO_MDNS_RTREE_DESTROY(&MyRecords);
3581    PICO_MDNS_CTREE_DESTROY(&Cookies);
3582
3583    /* Cancel every timer */
3584    pico_timer_cancel_hashed(mdns_hash);
3585}
3586
3587/* ****************************************************************************
3588 *  Initialises the entire mDNS-module and sets the hostname for this machine.
3589 *  Sets up the global mDNS socket properly and calls callback when succeeded.
3590 *	Only when the module is properly initialised records can be registered on
3591 *  the module.
3592 *
3593 *  @param hostname_url URL to set the hostname to.
3594 *  @param address      IPv4-address of this host to bind to.
3595 *  @param callback     Callback to call when the hostname is registered and
3596 *						also the global mDNS module callback. Gets called when
3597 *						Passive conflicts occur, so changes in records can be
3598 *						tracked in this callback.
3599 *	@param arg			Argument to pass to the init-callback.
3600 *  @return 0 when the module is properly initialised and the host started regis-
3601 *			tering the hostname. Returns something else went the host failed
3602 *			initialising the module or registering the hostname.
3603 * ****************************************************************************/
3604int
3605pico_mdns_init( const char *hostname,
3606                struct pico_ip4 address,
3607                void (*callback)(pico_mdns_rtree *,
3608                                 char *,
3609                                 void *),
3610                void *arg )
3611{
3612    struct pico_ip_mreq mreq4;
3613    uint16_t proto4 = PICO_PROTO_IPV4, port = 0, loop = 0, ttl = 255;
3614
3615    /* Initialise port */
3616    port = short_be(mdns_port);
3617
3618    /* Check callback parameter */
3619    if(!callback || !hostname) {
3620        mdns_dbg("No callback function supplied!\n");
3621        pico_err = PICO_ERR_EINVAL;
3622        return -1;
3623    }
3624
3625    /* Clear out all the memory structure's and delete socket if it was
3626     * already opened before */
3627    pico_mdns_cleanup();
3628
3629    /* Create a hash to identify mDNS timers with */
3630    mdns_hash = pico_hash(hostname, (uint32_t)strlen(hostname));
3631
3632    /* Open global IPv4 mDNS socket */
3633    mdns_sock_ipv4 = pico_socket_open(proto4, PICO_PROTO_UDP, &pico_mdns_event4);
3634    if(!mdns_sock_ipv4) {
3635        mdns_dbg("pico_socket_open returned NULL-ptr...\n");
3636        return -1;
3637    }
3638
3639    /* Convert the mDNS IPv4 destination address to struct */
3640    if(pico_string_to_ipv4(PICO_MDNS_DEST_ADDR4, &mreq4.mcast_group_addr.ip4.addr)) {
3641        mdns_dbg("String to IPv4 error\n");
3642        return -1;
3643    }
3644
3645    /* Receive data on any network interface */
3646    mreq4.mcast_link_addr.ip4 = inaddr_any;
3647
3648    /* Don't want the multicast data to be looped back to the host */
3649    if(pico_socket_setoption(mdns_sock_ipv4, PICO_IP_MULTICAST_LOOP, &loop)) {
3650        mdns_dbg("socket_setoption PICO_IP_MULTICAST_LOOP failed\n");
3651        return -1;
3652    }
3653
3654    /* Tell the stack we're interested in this particular multicast group */
3655    if(pico_socket_setoption(mdns_sock_ipv4, PICO_IP_ADD_MEMBERSHIP, &mreq4)) {
3656        mdns_dbg("socket_setoption PICO_IP_ADD_MEMBERSHIP failed\n");
3657        return -1;
3658    }
3659
3660    /* RFC6762:
3661     * 11.  Source Address Check
3662     *  All Multicast DNS responses (including responses sent via unicast)
3663     *  SHOULD be sent with IP TTL set to 255.
3664     */
3665    if(pico_socket_setoption(mdns_sock_ipv4, PICO_IP_MULTICAST_TTL, &ttl)) {
3666        mdns_dbg("socket_setoption PICO_IP_MULTICAST_TTL failed\n");
3667        return -1;
3668    }
3669
3670    /* Bind to mDNS port */
3671    if (pico_socket_bind(mdns_sock_ipv4, (void *)&address, &port)) {
3672        mdns_dbg("Bind error!\n");
3673        return -1;
3674    }
3675
3676    /* Set the global init callback variable */
3677    init_callback = callback;
3678    if (!pico_mdns_timer_add(PICO_MDNS_RR_TTL_TICK, pico_mdns_tick, NULL)) {
3679        mdns_dbg("MDNS: Failed to start tick timer\n");
3680        return -1;
3681    }
3682
3683    /* Set the hostname eventually */
3684    return pico_mdns_tryclaim_hostname(hostname, arg);
3685}
3686
3687#endif /* PICO_SUPPORT_MDNS */
3688