1/*** 2 This file is part of avahi. 3 4 avahi is free software; you can redistribute it and/or modify it 5 under the terms of the GNU Lesser General Public License as 6 published by the Free Software Foundation; either version 2.1 of the 7 License, or (at your option) any later version. 8 9 avahi is distributed in the hope that it will be useful, but WITHOUT 10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General 12 Public License for more details. 13 14 You should have received a copy of the GNU Lesser General Public 15 License along with avahi; if not, write to the Free Software 16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 17 USA. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <stdlib.h> 25 26#include <avahi-common/timeval.h> 27#include <avahi-common/malloc.h> 28#include <avahi-common/error.h> 29#include <avahi-common/domain.h> 30#include <avahi-common/rlist.h> 31#include <avahi-common/address.h> 32 33#include "browse.h" 34#include "log.h" 35#include "querier.h" 36#include "domain-util.h" 37#include "rr-util.h" 38 39#define AVAHI_LOOKUPS_PER_BROWSER_MAX 15 40 41struct AvahiSRBLookup { 42 AvahiSRecordBrowser *record_browser; 43 44 unsigned ref; 45 46 AvahiIfIndex interface; 47 AvahiProtocol protocol; 48 AvahiLookupFlags flags; 49 50 AvahiKey *key; 51 52 AvahiWideAreaLookup *wide_area; 53 AvahiMulticastLookup *multicast; 54 AvahiLLMNRLookup *llmnr; 55 56 AvahiRList *cname_lookups; 57 58 AVAHI_LLIST_FIELDS(AvahiSRBLookup, lookups); 59}; 60 61static void lookup_handle_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r); 62static void lookup_drop_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r); 63 64static void transport_flags_from_domain(AvahiServer *s, AvahiLookupFlags *flags, const char *domain) { 65 assert(flags); 66 assert(domain); 67 68 assert(!((*flags & AVAHI_LOOKUP_USE_MULTICAST) && (*flags & AVAHI_LOOKUP_USE_WIDE_AREA))); 69 assert(!((*flags & AVAHI_LOOKUP_USE_MULTICAST) && (*flags & AVAHI_LOOKUP_USE_LLMNR))); 70 assert(!((*flags & AVAHI_LOOKUP_USE_WIDE_AREA) && (*flags & AVAHI_LOOKUP_USE_LLMNR))); 71 72 if (*flags & (AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_LLMNR)) 73 return; 74 75 if (avahi_is_valid_host_name(domain)) 76 *flags |= AVAHI_LOOKUP_USE_LLMNR; 77 else if (!s->wide_area.wide_area_lookup_engine || 78 !avahi_wide_area_has_servers(s->wide_area.wide_area_lookup_engine) || 79 avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_LOCAL) || 80 avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV4) || 81 avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV6)) 82 *flags |= AVAHI_LOOKUP_USE_MULTICAST; 83 else 84 *flags |= AVAHI_LOOKUP_USE_WIDE_AREA; 85} 86 87static AvahiSRBLookup* lookup_new( 88 AvahiSRecordBrowser *b, 89 AvahiIfIndex interface, 90 AvahiProtocol protocol, 91 AvahiLookupFlags flags, 92 AvahiKey *key) { 93 94 AvahiSRBLookup *l; 95 96 assert(b); 97 assert(AVAHI_IF_VALID(interface)); 98 assert(AVAHI_PROTO_VALID(protocol)); 99 100 if (b->n_lookups >= AVAHI_LOOKUPS_PER_BROWSER_MAX) 101 /* We don't like cyclic CNAMEs */ 102 return NULL; 103 104 if (!(l = avahi_new(AvahiSRBLookup, 1))) 105 return NULL; 106 107 l->ref = 1; 108 l->record_browser = b; 109 l->interface = interface; 110 l->protocol = protocol; 111 l->key = avahi_key_ref(key); 112 l->wide_area = NULL; 113 l->multicast = NULL; 114 l->llmnr = NULL; 115 l->cname_lookups = NULL; 116 l->flags = flags; 117 118 transport_flags_from_domain(b->server, &l->flags, key->name); 119 120 AVAHI_LLIST_PREPEND(AvahiSRBLookup, lookups, b->lookups, l); 121 122 b->n_lookups ++; 123 124 return l; 125} 126 127static void lookup_unref(AvahiSRBLookup *l) { 128 assert(l); 129 assert(l->ref >= 1); 130 131 if (--l->ref >= 1) 132 return; 133 134 AVAHI_LLIST_REMOVE(AvahiSRBLookup, lookups, l->record_browser->lookups, l); 135 l->record_browser->n_lookups --; 136 137 if (l->wide_area) { 138 avahi_wide_area_lookup_free(l->wide_area); 139 l->wide_area = NULL; 140 } 141 142 if (l->multicast) { 143 avahi_multicast_lookup_free(l->multicast); 144 l->multicast = NULL; 145 } 146 147 if (l->llmnr) { 148 avahi_llmnr_lookup_free(l->llmnr); 149 l->llmnr = NULL; 150 } 151 152 while (l->cname_lookups) { 153 lookup_unref(l->cname_lookups->data); 154 l->cname_lookups = avahi_rlist_remove_by_link(l->cname_lookups, l->cname_lookups); 155 } 156 157 avahi_key_unref(l->key); 158 avahi_free(l); 159} 160 161static AvahiSRBLookup* lookup_ref(AvahiSRBLookup *l) { 162 assert(l); 163 assert(l->ref >= 1); 164 165 l->ref++; 166 return l; 167} 168 169static AvahiSRBLookup *lookup_find( 170 AvahiSRecordBrowser *b, 171 AvahiIfIndex interface, 172 AvahiProtocol protocol, 173 AvahiLookupFlags flags, 174 AvahiKey *key) { 175 176 AvahiSRBLookup *l; 177 178 assert(b); 179 180 for (l = b->lookups; l; l = l->lookups_next) { 181 182 if ((l->interface == AVAHI_IF_UNSPEC || l->interface == interface) && 183 (l->interface == AVAHI_PROTO_UNSPEC || l->protocol == protocol) && 184 l->flags == flags && 185 avahi_key_equal(l->key, key)) 186 187 return l; 188 } 189 190 return NULL; 191} 192 193static void browser_cancel(AvahiSRecordBrowser *b) { 194 assert(b); 195 196 if (b->root_lookup) { 197 lookup_unref(b->root_lookup); 198 b->root_lookup = NULL; 199 } 200 201 if (b->defer_time_event) { 202 avahi_time_event_free(b->defer_time_event); 203 b->defer_time_event = NULL; 204 } 205} 206 207static void lookup_wide_area_callback( 208 AvahiWideAreaLookupEngine *e, 209 AvahiBrowserEvent event, 210 AvahiLookupResultFlags flags, 211 AvahiRecord *r, 212 void *userdata) { 213 214 AvahiSRBLookup *l = userdata; 215 AvahiSRecordBrowser *b; 216 217 assert(e); 218 assert(l); 219 assert(l->ref >= 1); 220 221 b = l->record_browser; 222 223 if (b->dead) 224 return; 225 226 lookup_ref(l); 227 228 switch (event) { 229 case AVAHI_BROWSER_NEW: 230 assert(r); 231 232 if (r->key->clazz == AVAHI_DNS_CLASS_IN && 233 r->key->type == AVAHI_DNS_TYPE_CNAME) 234 /* It's a CNAME record, so let's follow it. We only follow it on wide area DNS! */ 235 lookup_handle_cname(l, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_LOOKUP_USE_WIDE_AREA, r); 236 else { 237 /* It's a normal record, so let's call the user callback */ 238 assert(avahi_key_equal(r->key, l->key)); 239 240 b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, r, flags, b->userdata); 241 } 242 break; 243 244 case AVAHI_BROWSER_REMOVE: 245 case AVAHI_BROWSER_CACHE_EXHAUSTED: 246 /* Not defined for wide area DNS */ 247 abort(); 248 249 case AVAHI_BROWSER_ALL_FOR_NOW: 250 case AVAHI_BROWSER_FAILURE: 251 252 b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, NULL, flags, b->userdata); 253 break; 254 } 255 256 lookup_unref(l); 257 258} 259 260static void lookup_multicast_callback( 261 AvahiMulticastLookupEngine *e, 262 AvahiIfIndex interface, 263 AvahiProtocol protocol, 264 AvahiBrowserEvent event, 265 AvahiLookupResultFlags flags, 266 AvahiRecord *r, 267 void *userdata) { 268 269 AvahiSRBLookup *l = userdata; 270 AvahiSRecordBrowser *b; 271 272 assert(e); 273 assert(l); 274 275 b = l->record_browser; 276 277 if (b->dead) 278 return; 279 280 lookup_ref(l); 281 282 switch (event) { 283 case AVAHI_BROWSER_NEW: 284 assert(r); 285 286 if (r->key->clazz == AVAHI_DNS_CLASS_IN && 287 r->key->type == AVAHI_DNS_TYPE_CNAME) 288 /* It's a CNAME record, so let's follow it. We allow browsing on both multicast and wide area. */ 289 lookup_handle_cname(l, interface, protocol, b->flags, r); 290 else { 291 /* It's a normal record, so let's call the user callback */ 292 293 if (avahi_server_is_record_local(b->server, interface, protocol, r)) 294 flags |= AVAHI_LOOKUP_RESULT_LOCAL; 295 296 b->callback(b, interface, protocol, event, r, flags, b->userdata); 297 } 298 break; 299 300 case AVAHI_BROWSER_REMOVE: 301 assert(r); 302 303 if (r->key->clazz == AVAHI_DNS_CLASS_IN && 304 r->key->type == AVAHI_DNS_TYPE_CNAME) 305 /* It's a CNAME record, so let's drop that query! */ 306 lookup_drop_cname(l, interface, protocol, 0, r); 307 else { 308 /* It's a normal record, so let's call the user callback */ 309 assert(avahi_key_equal(b->key, l->key)); 310 311 b->callback(b, interface, protocol, event, r, flags, b->userdata); 312 } 313 break; 314 315 case AVAHI_BROWSER_ALL_FOR_NOW: 316 317 b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, NULL, flags, b->userdata); 318 break; 319 320 case AVAHI_BROWSER_CACHE_EXHAUSTED: 321 case AVAHI_BROWSER_FAILURE: 322 /* Not defined for multicast DNS */ 323 abort(); 324 325 } 326 327 lookup_unref(l); 328} 329 330static void lookup_llmnr_callback( 331 AvahiLLMNRLookupEngine *e, 332 AvahiIfIndex interface, 333 AvahiProtocol protocol, 334 AvahiBrowserEvent event, 335 AvahiLookupResultFlags flags, 336 AvahiRecord *r, 337 void *userdata) { 338 339 AvahiSRBLookup *l = userdata; 340 AvahiSRecordBrowser *b; 341 342 assert(e); 343 assert(l); 344 345 b = l->record_browser; 346 347 if (b->dead) 348 return; 349 350 lookup_ref(l); 351 352 switch(event) { 353 case AVAHI_BROWSER_NEW: 354 assert(r); 355 356 if (r->key->clazz == AVAHI_DNS_CLASS_IN && 357 r->key->type == AVAHI_DNS_TYPE_CNAME) 358 lookup_handle_cname(l, interface, protocol, b->flags, r); 359 else 360 /* We are discrading packets originating from our own interface/s 361 or should we accept them? TODO AVAHI_LOOKUP_RESULT_LOCAL*/ 362 b->callback(b, interface, protocol, event, r, flags, b->userdata); 363 break; 364 365 case AVAHI_BROWSER_REMOVE: 366 case AVAHI_BROWSER_CACHE_EXHAUSTED: 367 abort(); 368 369 case AVAHI_BROWSER_ALL_FOR_NOW: 370 371 b->callback(b, interface, protocol, event, NULL, flags, b->userdata); 372 break; 373 374 case AVAHI_BROWSER_FAILURE: 375 /* This event states that LLMNR query has been sent three times on specified 376 interface and protocol and we don't have any records available */ 377 /*b->callback(b, interface, protocol, event, NULL, flags, b->userdata); 378 lookup_ref(l);*/ 379 abort(); 380 } 381 382 lookup_unref(l); 383} 384 385static int lookup_start(AvahiSRBLookup *l) { 386 387 assert(l); 388 389 assert((!(l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) && !(l->flags & AVAHI_LOOKUP_USE_LLMNR)) || 390 (!(l->flags & AVAHI_LOOKUP_USE_LLMNR) && !(l->flags & AVAHI_LOOKUP_USE_MULTICAST)) || 391 (!(l->flags & AVAHI_LOOKUP_USE_MULTICAST) && !(l->flags & AVAHI_LOOKUP_USE_WIDE_AREA))); 392 393 assert(!l->wide_area && !l->multicast && !l->llmnr); 394 395 if (l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) { 396 /* Wide Area */ 397 if (!(l->wide_area = avahi_wide_area_lookup_new(l->record_browser->server->wide_area.wide_area_lookup_engine, l->key, lookup_wide_area_callback, l))) 398 return -1; 399 } else if (l->flags & AVAHI_LOOKUP_USE_MULTICAST) { 400 /* Multicast */ 401 if (!(l->multicast = avahi_multicast_lookup_new(l->record_browser->server->mdns.multicast_lookup_engine, l->interface, l->protocol, l->key, lookup_multicast_callback, l))) 402 return -1; 403 } else { 404 /* LLMNR */ 405 assert(l->flags & AVAHI_LOOKUP_USE_LLMNR); 406 if (!(l->llmnr = avahi_llmnr_lookup_new(l->record_browser->server->llmnr.llmnr_lookup_engine, l->interface, l->protocol, l->key, lookup_llmnr_callback, l))) 407 return -1; 408 } 409 410 return 0; 411} 412 413static int lookup_scan_cache(AvahiSRBLookup *l) { 414 int n = 0; 415 416 assert(l); 417 418 assert((!(l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) && !(l->flags & AVAHI_LOOKUP_USE_LLMNR)) || 419 (!(l->flags & AVAHI_LOOKUP_USE_LLMNR) && !(l->flags & AVAHI_LOOKUP_USE_MULTICAST)) || 420 (!(l->flags & AVAHI_LOOKUP_USE_MULTICAST) && !(l->flags & AVAHI_LOOKUP_USE_WIDE_AREA)) ); 421 422 if (l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) { 423 n = (int) avahi_wide_area_scan_cache(l->record_browser->server->wide_area.wide_area_lookup_engine, l->key, lookup_wide_area_callback, l); 424 425 } else if (l->flags & AVAHI_LOOKUP_USE_MULTICAST) { 426 n = (int) avahi_multicast_lookup_engine_scan_cache(l->record_browser->server->mdns.multicast_lookup_engine, l->interface, l->protocol, l->key, lookup_multicast_callback, l); 427 428 } else { 429 assert(l->flags & AVAHI_LOOKUP_USE_LLMNR); 430 n = (int) avahi_scan_llmnr_cache(l->record_browser->server->llmnr.llmnr_lookup_engine, l->interface, l->protocol, l->key, lookup_llmnr_callback, l); 431 } 432 433 return n; 434} 435 436static AvahiSRBLookup* lookup_add(AvahiSRecordBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiKey *key) { 437 AvahiSRBLookup *l; 438 439 assert(b); 440 assert(!b->dead); 441 442 if ((l = lookup_find(b, interface, protocol, flags, key))) 443 return lookup_ref(l); 444 445 if (!(l = lookup_new(b, interface, protocol, flags, key))) 446 return NULL; 447 448 return l; 449} 450 451static int lookup_go(AvahiSRBLookup *l) { 452 int n = 0; 453 assert(l); 454 455 if (l->record_browser->dead) 456 return 0; 457 458 lookup_ref(l); 459 460 /* Browse the cache for the root request */ 461 n = lookup_scan_cache(l); 462 463 /* Start the lookup */ 464 if (!l->record_browser->dead && l->ref > 1) { 465 466 if ((l->flags & AVAHI_LOOKUP_USE_MULTICAST) || (l->flags & AVAHI_LOOKUP_USE_LLMNR) || n == 0) { 467 /* We do no start a query if the cache contained entries and we're on wide area */ 468 if (lookup_start(l) < 0) 469 n = -1; 470 } 471 } 472 473 lookup_unref(l); 474 475 return n; 476} 477 478static void lookup_handle_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r) { 479 AvahiKey *k; 480 AvahiSRBLookup *n; 481 482 assert(l); 483 assert(r); 484 485 assert(r->key->clazz == AVAHI_DNS_CLASS_IN); 486 assert(r->key->type == AVAHI_DNS_TYPE_CNAME); 487 488 k = avahi_key_new(r->data.ptr.name, l->record_browser->key->clazz, l->record_browser->key->type); 489 n = lookup_add(l->record_browser, interface, protocol, flags, k); 490 avahi_key_unref(k); 491 492 if (!n) { 493 avahi_log_debug(__FILE__": Failed to create SRBLookup."); 494 return; 495 } 496 497 l->cname_lookups = avahi_rlist_prepend(l->cname_lookups, lookup_ref(n)); 498 499 lookup_go(n); 500 lookup_unref(n); 501} 502 503static void lookup_drop_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r) { 504 AvahiKey *k; 505 AvahiSRBLookup *n = NULL; 506 AvahiRList *rl; 507 508 assert(r->key->clazz == AVAHI_DNS_CLASS_IN); 509 assert(r->key->type == AVAHI_DNS_TYPE_CNAME); 510 511 k = avahi_key_new(r->data.ptr.name, l->record_browser->key->clazz, l->record_browser->key->type); 512 513 for (rl = l->cname_lookups; rl; rl = rl->rlist_next) { 514 n = rl->data; 515 516 assert(n); 517 518 if ((n->interface == AVAHI_IF_UNSPEC || n->interface == interface) && 519 (n->interface == AVAHI_PROTO_UNSPEC || n->protocol == protocol) && 520 n->flags == flags && 521 avahi_key_equal(n->key, k)) 522 break; 523 } 524 525 avahi_key_unref(k); 526 527 if (rl) { 528 l->cname_lookups = avahi_rlist_remove_by_link(l->cname_lookups, rl); 529 lookup_unref(n); 530 } 531} 532 533static void defer_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void *userdata) { 534 AvahiSRecordBrowser *b = userdata; 535 int n = 0; 536 537 assert(b); 538 assert(!b->dead); 539 540 /* Remove the defer timeout */ 541 if (b->defer_time_event) { 542 avahi_time_event_free(b->defer_time_event); 543 b->defer_time_event = NULL; 544 } 545 546 /* Create initial query */ 547 assert(!b->root_lookup); 548 b->root_lookup = lookup_add(b, b->interface, b->protocol, b->flags, b->key); 549 assert(b->root_lookup); 550 551 n = lookup_go(b->root_lookup); 552 553 if (b->dead) 554 return; 555 556 if (n < 0) { 557 /* sending of the initial query failed */ 558 559 avahi_server_set_errno(b->server, AVAHI_ERR_FAILURE); 560 561 b->callback( 562 b, b->interface, b->protocol, AVAHI_BROWSER_FAILURE, NULL, 563 b->flags & AVAHI_LOOKUP_USE_WIDE_AREA ? AVAHI_LOOKUP_RESULT_WIDE_AREA : 564 (b->flags & AVAHI_LOOKUP_RESULT_MULTICAST ? AVAHI_LOOKUP_RESULT_MULTICAST : AVAHI_LOOKUP_RESULT_LLMNR), 565 b->userdata); 566 567 browser_cancel(b); 568 return; 569 } 570 571 /* Tell the client that we're done with the cache */ 572 b->callback( 573 b, b->interface, b->protocol, AVAHI_BROWSER_CACHE_EXHAUSTED, NULL, 574 b->flags & AVAHI_LOOKUP_USE_WIDE_AREA ? AVAHI_LOOKUP_RESULT_WIDE_AREA : 575 (b->flags & AVAHI_LOOKUP_RESULT_MULTICAST ? AVAHI_LOOKUP_RESULT_MULTICAST : AVAHI_LOOKUP_RESULT_LLMNR), 576 b->userdata); 577 578 if (!b->dead && b->root_lookup && 579 (b->root_lookup->flags & AVAHI_LOOKUP_USE_WIDE_AREA) && 580 n > 0 ) { 581 582 /* If we do wide area lookups and the the cache contained 583 * entries, we assume that it is complete, and tell the user 584 * so by firing ALL_FOR_NOW. */ 585 586 b->callback(b, b->interface, b->protocol, AVAHI_BROWSER_ALL_FOR_NOW, NULL, AVAHI_LOOKUP_RESULT_LLMNR, b->userdata); 587 } 588} 589 590void avahi_s_record_browser_restart(AvahiSRecordBrowser *b) { 591 assert(b); 592 assert(!b->dead); 593 594 browser_cancel(b); 595 596 /* Request a new iteration of the cache scanning */ 597 if (!b->defer_time_event) { 598 b->defer_time_event = avahi_time_event_new(b->server->time_event_queue, NULL, defer_callback, b); 599 assert(b->defer_time_event); 600 } 601} 602 603AvahiSRecordBrowser *avahi_s_record_browser_new( 604 AvahiServer *server, 605 AvahiIfIndex interface, 606 AvahiProtocol protocol, 607 AvahiKey *key, 608 AvahiLookupFlags flags, 609 AvahiSRecordBrowserCallback callback, 610 void* userdata) { 611 612 AvahiSRecordBrowser *b; 613 614 assert(server); 615 assert(key); 616 assert(callback); 617 618 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); 619 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); 620 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !avahi_key_is_pattern(key), AVAHI_ERR_IS_PATTERN); 621 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_key_is_valid(key), AVAHI_ERR_INVALID_KEY); 622 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_USE_LLMNR), AVAHI_ERR_INVALID_FLAGS); 623 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !(flags & AVAHI_LOOKUP_USE_WIDE_AREA) || !(flags & AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); 624 625 if (!(b = avahi_new(AvahiSRecordBrowser, 1))) { 626 avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); 627 return NULL; 628 } 629 630 b->dead = 0; 631 b->server = server; 632 b->interface = interface; 633 b->protocol = protocol; 634 b->key = avahi_key_ref(key); 635 b->flags = flags; 636 b->callback = callback; 637 b->userdata = userdata; 638 b->n_lookups = 0; 639 AVAHI_LLIST_HEAD_INIT(AvahiSRBLookup, b->lookups); 640 b->root_lookup = NULL; 641 642 AVAHI_LLIST_PREPEND(AvahiSRecordBrowser, browser, server->record_browsers, b); 643 644 /* The currently cached entries are scanned a bit later, and than we will start querying, too */ 645 b->defer_time_event = avahi_time_event_new(server->time_event_queue, NULL, defer_callback, b); 646 assert(b->defer_time_event); 647 648 return b; 649} 650 651void avahi_s_record_browser_free(AvahiSRecordBrowser *b) { 652 assert(b); 653 assert(!b->dead); 654 655 b->dead = 1; 656 b->server->need_browser_cleanup = 1; 657 658 browser_cancel(b); 659} 660 661void avahi_s_record_browser_destroy(AvahiSRecordBrowser *b) { 662 assert(b); 663 664 browser_cancel(b); 665 666 AVAHI_LLIST_REMOVE(AvahiSRecordBrowser, browser, b->server->record_browsers, b); 667 668 avahi_key_unref(b->key); 669 670 avahi_free(b); 671} 672 673void avahi_browser_cleanup(AvahiServer *server) { 674 AvahiSRecordBrowser *b; 675 AvahiSRecordBrowser *n; 676 677 assert(server); 678 679 while (server->need_browser_cleanup) { 680 server->need_browser_cleanup = 0; 681 682 for (b = server->record_browsers; b; b = n) { 683 n = b->browser_next; 684 685 if (b->dead) 686 avahi_s_record_browser_destroy(b); 687 } 688 } 689 690 if (server->wide_area.wide_area_lookup_engine) 691 avahi_wide_area_cleanup(server->wide_area.wide_area_lookup_engine); 692 693 avahi_multicast_lookup_engine_cleanup(server->mdns.multicast_lookup_engine); 694 avahi_llmnr_lookup_engine_cleanup(server->llmnr.llmnr_lookup_engine); 695} 696 697