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 **)©); 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 **)©); 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 **)©); 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 **)©); 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 **)©); 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 **)©); 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 **)©); 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