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 <sys/types.h> 25#include <sys/socket.h> 26#include <netinet/in.h> 27#include <arpa/inet.h> 28#include <string.h> 29#include <sys/utsname.h> 30#include <unistd.h> 31#include <errno.h> 32#include <stdio.h> 33#include <assert.h> 34#include <stdlib.h> 35 36#include <avahi-common/domain.h> 37#include <avahi-common/timeval.h> 38#include <avahi-common/malloc.h> 39#include <avahi-common/error.h> 40 41#include "internal.h" 42#include "iface.h" 43#include "socket.h" 44#include "browse.h" 45#include "log.h" 46#include "util.h" 47#include "dns-srv-rr.h" 48#include "addr-util.h" 49#include "domain-util.h" 50#include "rr-util.h" 51 52#define AVAHI_DEFAULT_CACHE_ENTRIES_MAX 4096 53 54static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const char *name, uint16_t type, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) { 55 assert(s); 56 assert(i); 57 assert(name); 58 assert(callback); 59 60 if (type == AVAHI_DNS_TYPE_ANY) { 61 AvahiEntry *e; 62 63 for (e = s->mdns.entries; e; e = e->entries_next) 64 if (!e->dead && 65 e->type == AVAHI_ENTRY_MDNS && 66 avahi_entry_is_registered(s, e, i) && 67 e->record->key->clazz == AVAHI_DNS_CLASS_IN && 68 avahi_domain_equal(name, e->record->key->name)) 69 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata); 70 71 } else { 72 AvahiEntry *e; 73 AvahiKey *k; 74 75 if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type))) 76 return; /** OOM */ 77 78 for (e = avahi_hashmap_lookup(s->mdns.entries_by_key, k); e; e = e->by_key_next) 79 if (!e->dead && e->type == AVAHI_ENTRY_MDNS && avahi_entry_is_registered(s, e, i)) 80 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata); 81 82 avahi_key_unref(k); 83 } 84} 85 86void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) { 87 assert(s); 88 assert(i); 89 assert(r); 90 assert(callback); 91 92 /* Call the specified callback far all records referenced by the one specified in *r */ 93 94 if (r->key->clazz == AVAHI_DNS_CLASS_IN) { 95 if (r->key->type == AVAHI_DNS_TYPE_PTR) { 96 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata); 97 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata); 98 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) { 99 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata); 100 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata); 101 } else if (r->key->type == AVAHI_DNS_TYPE_CNAME) 102 enum_aux_records(s, i, r->data.cname.name, AVAHI_DNS_TYPE_ANY, callback, userdata); 103 } 104} 105 106void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) { 107 assert(s); 108 assert(i); 109 assert(e); 110 assert(e->type == AVAHI_ENTRY_MDNS); 111 112 avahi_record_list_push(s->mdns.record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary); 113} 114 115void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) { 116 assert(s); 117 assert(i); 118 assert(k); 119 120 /* Push all records that match the specified key to the record list */ 121 122 if (avahi_key_is_pattern(k)) { 123 AvahiEntry *e; 124 125 /* Handle ANY query */ 126 127 for (e = s->mdns.entries; e; e = e->entries_next) 128 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i)) 129 avahi_server_prepare_response(s, i, e, unicast_response, 0); 130 131 } else { 132 AvahiEntry *e; 133 134 /* Handle all other queries */ 135 136 for (e = avahi_hashmap_lookup(s->mdns.entries_by_key, k); e; e = e->by_key_next) 137 if (!e->dead && avahi_entry_is_registered(s, e, i)) 138 avahi_server_prepare_response(s, i, e, unicast_response, 0); 139 } 140 141 /* Look for CNAME records */ 142 143 if ((k->clazz == AVAHI_DNS_CLASS_IN || k->clazz == AVAHI_DNS_CLASS_ANY) 144 && k->type != AVAHI_DNS_TYPE_CNAME && k->type != AVAHI_DNS_TYPE_ANY) { 145 146 AvahiKey *cname_key; 147 148 if (!(cname_key = avahi_key_new(k->name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME))) 149 return; 150 151 avahi_server_prepare_matching_responses(s, i, cname_key, unicast_response); 152 avahi_key_unref(cname_key); 153 } 154} 155 156static void withdraw_entry(AvahiServer *s, AvahiEntry *e) { 157 assert(s); 158 assert(e && e->type == AVAHI_ENTRY_MDNS); 159 160 /* Withdraw the specified entry, and if is part of an entry group, 161 * put that into COLLISION state */ 162 163 if (e->dead) 164 return; 165 166 if (e->group) { 167 AvahiEntry *k; 168 169 assert(e->group->type == AVAHI_GROUP_MDNS); 170 for (k = e->group->entries; k; k = k->by_group_next) { 171 assert(k->type == AVAHI_ENTRY_MDNS); 172 if (!k->dead) { 173 avahi_goodbye_entry(s, k, 0, 1); 174 k->dead = 1; 175 } 176 } 177 178 e->group->proto.mdns.n_probing = 0; 179 180 avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION); 181 } else { 182 avahi_goodbye_entry(s, e, 0, 1); 183 e->dead = 1; 184 } 185 186 s->mdns.need_entry_cleanup = 1; 187} 188 189static void withdraw_rrset(AvahiServer *s, AvahiKey *key) { 190 AvahiEntry *e; 191 192 assert(s); 193 assert(key); 194 195 /* Withdraw an entry RRSset */ 196 197 for (e = avahi_hashmap_lookup(s->mdns.entries_by_key, key); e; e = e->by_key_next) 198 withdraw_entry(s, e); 199} 200 201static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) { 202 AvahiEntry *e, *n; 203 int ours = 0, won = 0, lost = 0; 204 205 assert(s); 206 assert(record); 207 assert(i); 208 209 /* Handle incoming probes and check if they conflict our own probes */ 210 211 for (e = avahi_hashmap_lookup(s->mdns.entries_by_key, record->key); e; e = n) { 212 int cmp; 213 n = e->by_key_next; 214 215 if (e->dead) 216 continue; 217 218 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) { 219 ours = 1; 220 break; 221 } else { 222 223 if (avahi_entry_is_probing(s, e, i)) { 224 if (cmp > 0) 225 won = 1; 226 else /* cmp < 0 */ 227 lost = 1; 228 } 229 } 230 } 231 232 if (!ours) { 233 char *t = avahi_record_to_string(record); 234 235 if (won) 236 avahi_log_debug("Received conflicting probe [%s]. Local host won.", t); 237 else if (lost) { 238 avahi_log_debug("Received conflicting probe [%s]. Local host lost. Withdrawing.", t); 239 withdraw_rrset(s, record->key); 240 } 241 242 avahi_free(t); 243 } 244} 245 246static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique) { 247 int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0; 248 AvahiEntry *e, *n, *conflicting_entry = NULL; 249 250 assert(s); 251 assert(i); 252 assert(record); 253 254 /* Check whether an incoming record conflicts with one of our own */ 255 256 for (e = avahi_hashmap_lookup(s->mdns.entries_by_key, record->key); e; e = n) { 257 assert(e->type == AVAHI_ENTRY_MDNS); 258 n = e->by_key_next; 259 260 if (e->dead) 261 continue; 262 263 /* Check if the incoming is a goodbye record */ 264 if (avahi_record_is_goodbye(record)) { 265 266 if (avahi_record_equal_no_ttl(e->record, record)) { 267 char *t; 268 269 /* Refresh */ 270 t = avahi_record_to_string(record); 271 avahi_log_debug("Received goodbye record for one of our records [%s]. Refreshing.", t); 272 avahi_server_prepare_matching_responses(s, i, e->record->key, 0); 273 274 valid = 0; 275 avahi_free(t); 276 break; 277 } 278 279 /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */ 280 continue; 281 } 282 283 if (!(e->flags & AVAHI_PUBLISH_UNIQUE) && !unique) 284 continue; 285 286 /* Either our entry or the other is intended to be unique, so let's check */ 287 288 if (avahi_record_equal_no_ttl(e->record, record)) { 289 ours = 1; /* We have an identical record, so this is no conflict */ 290 291 /* Check wheter there is a TTL conflict */ 292 if (record->ttl <= e->record->ttl/2 && 293 avahi_entry_is_registered(s, e, i)) { 294 char *t; 295 /* Refresh */ 296 t = avahi_record_to_string(record); 297 298 avahi_log_debug("Received record with bad TTL [%s]. Refreshing.", t); 299 avahi_server_prepare_matching_responses(s, i, e->record->key, 0); 300 valid = 0; 301 302 avahi_free(t); 303 } 304 305 /* There's no need to check the other entries of this RRset */ 306 break; 307 308 } else { 309 310 if (avahi_entry_is_registered(s, e, i)) { 311 312 /* A conflict => we have to return to probe mode */ 313 conflict = 1; 314 conflicting_entry = e; 315 316 } else if (avahi_entry_is_probing(s, e, i)) { 317 318 /* We are currently registering a matching record, but 319 * someone else already claimed it, so let's 320 * withdraw */ 321 conflict = 1; 322 withdraw_immediately = 1; 323 } 324 } 325 } 326 327 if (!ours && conflict) { 328 char *t; 329 330 valid = 0; 331 332 t = avahi_record_to_string(record); 333 334 if (withdraw_immediately) { 335 avahi_log_debug("Received conflicting record [%s] with local record to be. Withdrawing.", t); 336 withdraw_rrset(s, record->key); 337 } else { 338 assert(conflicting_entry); 339 avahi_log_debug("Received conflicting record [%s]. Resetting our record.", t); 340 avahi_entry_return_to_initial_state(s, conflicting_entry, i); 341 342 /* Local unique records are returned to probing 343 * state. Local shared records are reannounced. */ 344 } 345 346 avahi_free(t); 347 } 348 349 return valid; 350} 351 352static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) { 353 int *unicast_response = userdata; 354 355 assert(s); 356 assert(r); 357 assert(unicast_response); 358 359 avahi_record_list_push(s->mdns.record_list, r, flush_cache, *unicast_response, 1); 360} 361 362static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) { 363 assert(s); 364 assert(r); 365 366 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response); 367} 368 369void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) { 370 371 assert(s); 372 assert(i); 373 assert(!legacy_unicast || (a && port > 0 && p)); 374 375 if (legacy_unicast) { 376 AvahiDnsPacket *reply; 377 AvahiRecord *r; 378 379 if (!(reply = avahi_dns_packet_new_reply(p, 512 + AVAHI_DNS_PACKET_EXTRA_SIZE /* unicast DNS maximum packet size is 512 */ , 1, 1))) 380 return; /* OOM */ 381 382 while ((r = avahi_record_list_next(s->mdns.record_list, NULL, NULL, NULL))) { 383 384 append_aux_records_to_list(s, i, r, 0); 385 386 if (avahi_dns_packet_append_record(reply, r, 0, 10)) 387 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT); 388 else { 389 char *t = avahi_record_to_string(r); 390 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t); 391 avahi_free(t); 392 } 393 394 avahi_record_unref(r); 395 } 396 397 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0) 398 avahi_interface_send_packet_unicast(i, reply, a, port, /*for fd*/ AVAHI_MDNS); 399 400 avahi_dns_packet_free(reply); 401 402 } else { 403 int unicast_response, flush_cache, auxiliary; 404 AvahiDnsPacket *reply = NULL; 405 AvahiRecord *r; 406 407 /* In case the query packet was truncated never respond 408 immediately, because known answer suppression records might be 409 contained in later packets */ 410 int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC); 411 412 while ((r = avahi_record_list_next(s->mdns.record_list, &flush_cache, &unicast_response, &auxiliary))) { 413 414 int im = immediately; 415 416 /* Only send the response immediately if it contains a 417 * unique entry AND it is not in reply to a truncated 418 * packet AND it is not an auxiliary record AND all other 419 * responses for this record are unique too. */ 420 421 if (flush_cache && !tc && !auxiliary && avahi_record_list_all_flush_cache(s->mdns.record_list)) 422 im = 1; 423 424 if (!avahi_interface_post_response(i, r, flush_cache, a, im) && unicast_response) { 425 426 /* Due to some reasons the record has not been scheduled. 427 * The client requested an unicast response in that 428 * case. Therefore we prepare such a response */ 429 430 append_aux_records_to_list(s, i, r, unicast_response); 431 432 for (;;) { 433 434 if (!reply) { 435 assert(p); 436 437 if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0))) 438 break; /* OOM */ 439 } 440 441 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) { 442 443 /* Appending this record succeeded, so incremeant 444 * the specific header field, and return to the caller */ 445 446 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT); 447 break; 448 } 449 450 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) { 451 size_t size; 452 453 /* The record is too large for one packet, so create a larger packet */ 454 455 avahi_dns_packet_free(reply); 456 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE; 457 458 if (!(reply = avahi_dns_packet_new_reply(p, size + AVAHI_DNS_PACKET_EXTRA_SIZE, 0, 1))) 459 break; /* OOM */ 460 461 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) { 462 463 /* Appending this record succeeded, so incremeant 464 * the specific header field, and return to the caller */ 465 466 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT); 467 break; 468 469 } else { 470 471 /* We completely fucked up, there's 472 * nothing we can do. The RR just doesn't 473 * fit in. Let's ignore it. */ 474 475 char *t; 476 avahi_dns_packet_free(reply); 477 reply = NULL; 478 t = avahi_record_to_string(r); 479 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t); 480 avahi_free(t); 481 break; 482 } 483 } 484 485 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */ 486 avahi_interface_send_packet_unicast(i, reply, a, port, AVAHI_MDNS); 487 avahi_dns_packet_free(reply); 488 reply = NULL; 489 } 490 } 491 492 avahi_record_unref(r); 493 } 494 495 if (reply) { 496 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0) 497 avahi_interface_send_packet_unicast(i, reply, a, port, AVAHI_MDNS); 498 avahi_dns_packet_free(reply); 499 } 500 } 501 502 avahi_record_list_flush(s->mdns.record_list); 503} 504 505static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) { 506 AvahiInterface *j; 507 508 assert(s); 509 assert(i); 510 assert(r); 511 512 if (!s->config.enable_reflector) 513 return; 514 515 for (j = s->monitor->interfaces; j; j = j->interface_next) 516 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) 517 avahi_interface_post_response(j, r, flush_cache, NULL, 1); 518} 519 520static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) { 521 AvahiServer *s = userdata; 522 AvahiRecord* r; 523 524 assert(c); 525 assert(pattern); 526 assert(e); 527 assert(s); 528 529 /* Don't reflect cache entry with ipv6 link-local addresses. */ 530 r = e->record; 531 if ((r->key->type == AVAHI_DNS_TYPE_AAAA) && 532 (r->data.aaaa.address.address[0] == 0xFE) && 533 (r->data.aaaa.address.address[1] == 0x80)) 534 return NULL; 535 536 avahi_record_list_push(s->mdns.record_list, e->record, e->cache_flush, 0, 0); 537 return NULL; 538} 539 540static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) { 541 AvahiInterface *j; 542 543 assert(s); 544 assert(i); 545 assert(k); 546 547 if (!s->config.enable_reflector) 548 return; 549 550 for (j = s->monitor->interfaces; j; j = j->interface_next) 551 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) { 552 /* Post the query to other networks */ 553 avahi_interface_post_query(j, k, 1, NULL); 554 555 /* Reply from caches of other network. This is needed to 556 * "work around" known answer suppression. */ 557 558 avahi_cache_walk(j->mdns.cache, k, reflect_cache_walk_callback, s); 559 } 560} 561 562static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) { 563 AvahiInterface *j; 564 565 assert(s); 566 assert(i); 567 assert(r); 568 569 if (!s->config.enable_reflector) 570 return; 571 572 for (j = s->monitor->interfaces; j; j = j->interface_next) 573 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) 574 avahi_interface_post_probe(j, r, 1); 575} 576 577static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) { 578 size_t n; 579 int is_probe; 580 581 assert(s); 582 assert(p); 583 assert(i); 584 assert(a); 585 586 assert(avahi_record_list_is_empty(s->mdns.record_list)); 587 588 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0; 589 590 /* Handle the questions */ 591 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) { 592 AvahiKey *key; 593 int unicast_response = 0; 594 595 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) { 596 avahi_log_warn(__FILE__": Packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)"); 597 goto fail; 598 } 599 600 if (!legacy_unicast && !from_local_iface) { 601 reflect_query(s, i, key); 602 if (!unicast_response) 603 avahi_cache_start_poof(i->mdns.cache, key, a); 604 } 605 606 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 && 607 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC)) 608 /* Allow our own queries to be suppressed by incoming 609 * queries only when they do not include known answers */ 610 avahi_query_scheduler_incoming(i->mdns.query_scheduler, key); 611 612 avahi_server_prepare_matching_responses(s, i, key, unicast_response); 613 avahi_key_unref(key); 614 } 615 616 if (!legacy_unicast) { 617 618 /* Known Answer Suppression */ 619 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) { 620 AvahiRecord *record; 621 int unique = 0; 622 623 if (!(record = avahi_dns_packet_consume_record(p, &unique))) { 624 avahi_log_warn(__FILE__": Packet too short or invalid while reading known answer record. (Maybe a UTF-8 problem?)"); 625 goto fail; 626 } 627 628 avahi_response_scheduler_suppress(i->mdns.response_scheduler, record, a); 629 avahi_record_list_drop(s->mdns.record_list, record); 630 avahi_cache_stop_poof(i->mdns.cache, record, a); 631 632 avahi_record_unref(record); 633 } 634 635 /* Probe record */ 636 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) { 637 AvahiRecord *record; 638 int unique = 0; 639 640 if (!(record = avahi_dns_packet_consume_record(p, &unique))) { 641 avahi_log_warn(__FILE__": Packet too short or invalid while reading probe record. (Maybe a UTF-8 problem?)"); 642 goto fail; 643 } 644 645 if (!avahi_key_is_pattern(record->key)) { 646 if (!from_local_iface) 647 reflect_probe(s, i, record); 648 incoming_probe(s, record, i); 649 } 650 651 avahi_record_unref(record); 652 } 653 } 654 655 if (!avahi_record_list_is_empty(s->mdns.record_list)) 656 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe); 657 658 return; 659 660fail: 661 avahi_record_list_flush(s->mdns.record_list); 662} 663 664static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) { 665 unsigned n; 666 667 assert(s); 668 assert(p); 669 assert(i); 670 assert(a); 671 672 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) + 673 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) { 674 AvahiRecord *record; 675 int cache_flush = 0; 676 677 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) { 678 avahi_log_warn(__FILE__": Packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)"); 679 break; 680 } 681 682 if (!avahi_key_is_pattern(record->key)) { 683 684 if (handle_conflict(s, i, record, cache_flush)) { 685 if (!from_local_iface && !avahi_record_is_link_local_address(record)) 686 reflect_response(s, i, record, cache_flush); 687 avahi_cache_update(i->mdns.cache, record, cache_flush, a); 688 avahi_response_scheduler_incoming(i->mdns.response_scheduler, record, cache_flush); 689 } 690 } 691 692 avahi_record_unref(record); 693 } 694 695 /* If the incoming response contained a conflicting record, some 696 records have been scheduled for sending. We need to flush them 697 here. */ 698 if (!avahi_record_list_is_empty(s->mdns.record_list)) 699 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1); 700} 701 702static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) { 703 unsigned n, idx = (unsigned) -1; 704 AvahiLegacyUnicastReflectSlot *slot; 705 706 assert(s); 707 708 if (!s->legacy_unicast_reflect_slots) 709 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX); 710 711 for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) { 712 idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; 713 714 if (!s->legacy_unicast_reflect_slots[idx]) 715 break; 716 } 717 718 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx]) 719 return NULL; 720 721 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1))) 722 return NULL; /* OOM */ 723 724 s->legacy_unicast_reflect_slots[idx] = slot; 725 slot->id = s->legacy_unicast_reflect_id++; 726 slot->server = s; 727 728 return slot; 729} 730 731static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) { 732 unsigned idx; 733 734 assert(s); 735 assert(slot); 736 737 idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; 738 739 assert(s->legacy_unicast_reflect_slots[idx] == slot); 740 741 avahi_time_event_free(slot->time_event); 742 743 avahi_free(slot); 744 s->legacy_unicast_reflect_slots[idx] = NULL; 745} 746 747static void free_slots(AvahiServer *s) { 748 unsigned idx; 749 assert(s); 750 751 if (!s->legacy_unicast_reflect_slots) 752 return; 753 754 for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++) 755 if (s->legacy_unicast_reflect_slots[idx]) 756 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]); 757 758 avahi_free(s->legacy_unicast_reflect_slots); 759 s->legacy_unicast_reflect_slots = NULL; 760} 761 762static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) { 763 unsigned idx; 764 765 assert(s); 766 767 if (!s->legacy_unicast_reflect_slots) 768 return NULL; 769 770 idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; 771 772 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id) 773 return NULL; 774 775 return s->legacy_unicast_reflect_slots[idx]; 776} 777 778static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) { 779 AvahiLegacyUnicastReflectSlot *slot = userdata; 780 781 assert(e); 782 assert(slot); 783 assert(slot->time_event == e); 784 785 deallocate_slot(slot->server, slot); 786} 787 788static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) { 789 AvahiLegacyUnicastReflectSlot *slot; 790 AvahiInterface *j; 791 792 assert(s); 793 assert(p); 794 assert(i); 795 assert(a); 796 assert(port > 0); 797 assert(i->protocol == a->proto); 798 799 if (!s->config.enable_reflector) 800 return; 801 802 /* Reflecting legacy unicast queries is a little more complicated 803 than reflecting normal queries, since we must route the 804 responses back to the right client. Therefore we must store 805 some information for finding the right client contact data for 806 response packets. In contrast to normal queries legacy 807 unicast query and response packets are reflected untouched and 808 are not reassembled into larger packets */ 809 810 if (!(slot = allocate_slot(s))) { 811 /* No slot available, we drop this legacy unicast query */ 812 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet."); 813 return; 814 } 815 816 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID); 817 slot->address = *a; 818 slot->port = port; 819 slot->interface = i->hardware->index; 820 821 avahi_elapse_time(&slot->elapse_time, 2000, 0); 822 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot); 823 824 /* Patch the packet with our new locally generatet id */ 825 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id); 826 827 for (j = s->monitor->interfaces; j; j = j->interface_next) 828 if (j->mdns.announcing && 829 j != i && 830 (s->config.reflect_ipv || j->protocol == i->protocol)) { 831 832 if (j->protocol == AVAHI_PROTO_INET && s->mdns.fd_legacy_unicast_ipv4 >= 0) { 833 avahi_send_dns_packet_ipv4(s->mdns.fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0, AVAHI_MDNS); 834 } else if (j->protocol == AVAHI_PROTO_INET6 && s->mdns.fd_legacy_unicast_ipv6 >= 0) 835 avahi_send_dns_packet_ipv6(s->mdns.fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0, AVAHI_MDNS); 836 } 837 838 /* Reset the id */ 839 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id); 840} 841 842static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) { 843 assert(s); 844 assert(address); 845 assert(port > 0); 846 847 if (!s->config.enable_reflector) 848 return 0; 849 850 if (!avahi_address_is_local(s->monitor, address)) 851 return 0; 852 853 if (address->proto == AVAHI_PROTO_INET && s->mdns.fd_legacy_unicast_ipv4 >= 0) { 854 struct sockaddr_in lsa; 855 socklen_t l = sizeof(lsa); 856 857 if (getsockname(s->mdns.fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0) 858 avahi_log_warn("getsockname(): %s", strerror(errno)); 859 else 860 return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port; 861 862 } 863 864 if (address->proto == AVAHI_PROTO_INET6 && s->mdns.fd_legacy_unicast_ipv6 >= 0) { 865 struct sockaddr_in6 lsa; 866 socklen_t l = sizeof(lsa); 867 868 if (getsockname(s->mdns.fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0) 869 avahi_log_warn("getsockname(): %s", strerror(errno)); 870 else 871 return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port; 872 } 873 874 return 0; 875} 876 877static int is_mdns_mcast_address(const AvahiAddress *a) { 878 AvahiAddress b; 879 assert(a); 880 881 avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b); 882 return avahi_address_cmp(a, &b) == 0; 883} 884 885static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) { 886 assert(s); 887 assert(iface != AVAHI_IF_UNSPEC); 888 assert(a); 889 890 /* If it isn't the MDNS port it can't be generated by us */ 891 if (port != AVAHI_MDNS_PORT) 892 return 0; 893 894 return avahi_interface_has_address(s->monitor, iface, a); 895} 896 897static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) { 898 AvahiInterface *i; 899 int from_local_iface = 0; 900 901 assert(s); 902 assert(p); 903 assert(src_address); 904 assert(dst_address); 905 assert(iface > 0); 906 assert(src_address->proto == dst_address->proto); 907 908 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) || 909 !i->mdns.announcing) { 910 avahi_log_warn("Received packet from invalid interface."); 911 return; 912 } 913 914 if (port <= 0) { 915 /* This fixes RHBZ #475394 */ 916 avahi_log_warn("Received packet from invalid source port %u.", (unsigned) port); 917 return; 918 } 919 920 if (avahi_address_is_ipv4_in_ipv6(src_address)) 921 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */ 922 return; 923 924 if (originates_from_local_legacy_unicast_socket(s, src_address, port)) 925 /* This originates from our local reflector, so let's ignore it */ 926 return; 927 928 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */ 929 if (s->config.enable_reflector) 930 from_local_iface = originates_from_local_iface(s, iface, src_address, port); 931 932 if (avahi_dns_packet_check_valid_multicast(p) < 0) { 933 avahi_log_warn("Received invalid packet."); 934 return; 935 } 936 937 if (avahi_dns_packet_is_query(p)) { 938 int legacy_unicast = 0; 939 940 /* For queries EDNS0 might allow ARCOUNT != 0. We ignore the 941 * AR section completely here, so far. Until the day we add 942 * EDNS0 support. */ 943 944 if (port != AVAHI_MDNS_PORT) { 945 /* Legacy Unicast */ 946 947 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 || 948 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) { 949 avahi_log_warn("Invalid legacy unicast query packet."); 950 return; 951 } 952 953 legacy_unicast = 1; 954 } 955 956 if (legacy_unicast) 957 reflect_legacy_unicast_query_packet(s, p, i, src_address, port); 958 959 handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface); 960 961 } else { 962 char t[AVAHI_ADDRESS_STR_MAX]; 963 964 if (port != AVAHI_MDNS_PORT) { 965 avahi_log_warn("Received response from host %s with invalid source port %u on interface '%s.%i'", avahi_address_snprint(t, sizeof(t), src_address), port, i->hardware->name, i->protocol); 966 return; 967 } 968 969 if (ttl != 255 && s->config.check_response_ttl) { 970 avahi_log_warn("Received response from host %s with invalid TTL %u on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), ttl, i->hardware->name, i->protocol); 971 return; 972 } 973 974 if (!is_mdns_mcast_address(dst_address) && 975 !avahi_interface_address_on_link(i, src_address)) { 976 977 avahi_log_warn("Received non-local response from host %s on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), i->hardware->name, i->protocol); 978 return; 979 } 980 981 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 || 982 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 || 983 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) { 984 985 avahi_log_warn("Invalid response packet from host %s.", avahi_address_snprint(t, sizeof(t), src_address)); 986 return; 987 } 988 989 handle_response_packet(s, p, i, src_address, from_local_iface); 990 } 991} 992 993static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) { 994 AvahiInterface *j; 995 AvahiLegacyUnicastReflectSlot *slot; 996 997 assert(s); 998 assert(p); 999 1000 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) { 1001 avahi_log_warn("Received invalid packet."); 1002 return; 1003 } 1004 1005 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) { 1006 avahi_log_warn("Received legacy unicast response with unknown id"); 1007 return; 1008 } 1009 1010 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) || 1011 !j->mdns.announcing) 1012 return; 1013 1014 /* Patch the original ID into this response */ 1015 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id); 1016 1017 /* Forward the response to the correct client */ 1018 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port, AVAHI_MDNS); 1019 1020 /* Undo changes to packet */ 1021 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id); 1022} 1023 1024static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) { 1025 AvahiServer *s = userdata; 1026 AvahiAddress dest, src; 1027 AvahiDnsPacket *p = NULL; 1028 AvahiIfIndex iface; 1029 uint16_t port; 1030 uint8_t ttl; 1031 1032 assert(w); 1033 assert(fd >= 0); 1034 assert(events & AVAHI_WATCH_IN); 1035 1036 if (fd == s->mdns.fd_ipv4) { 1037 dest.proto = src.proto = AVAHI_PROTO_INET; 1038 p = avahi_recv_dns_packet_ipv4(s->mdns.fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl); 1039 } else { 1040 assert(fd == s->mdns.fd_ipv6); 1041 dest.proto = src.proto = AVAHI_PROTO_INET6; 1042 p = avahi_recv_dns_packet_ipv6(s->mdns.fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl); 1043 } 1044 1045 if (p) { 1046 if (iface == AVAHI_IF_UNSPEC) 1047 iface = avahi_find_interface_for_address(s->monitor, &dest); 1048 1049 if (iface != AVAHI_IF_UNSPEC) 1050 dispatch_packet(s, p, &src, port, &dest, iface, ttl); 1051 else 1052 avahi_log_error("Incoming packet received on address that isn't local."); 1053 1054 avahi_dns_packet_free(p); 1055 1056 avahi_cleanup_dead_entries(s); 1057 } 1058} 1059 1060static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) { 1061 AvahiServer *s = userdata; 1062 AvahiDnsPacket *p = NULL; 1063 1064 assert(w); 1065 assert(fd >= 0); 1066 assert(events & AVAHI_WATCH_IN); 1067 1068 if (fd == s->mdns.fd_legacy_unicast_ipv4) 1069 p = avahi_recv_dns_packet_ipv4(s->mdns.fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL); 1070 else { 1071 assert(fd == s->mdns.fd_legacy_unicast_ipv6); 1072 p = avahi_recv_dns_packet_ipv6(s->mdns.fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL); 1073 } 1074 1075 if (p) { 1076 dispatch_legacy_unicast_packet(s, p); 1077 avahi_dns_packet_free(p); 1078 1079 avahi_cleanup_dead_entries(s); 1080 } 1081} 1082 1083static void server_set_state(AvahiServer *s, AvahiServerState state) { 1084 assert(s); 1085 1086 if (s->state == state) 1087 return; 1088 1089 s->state = state; 1090 1091 avahi_interface_monitor_update_rrs(s->monitor, 0); 1092 1093 if (s->callback) 1094 s->callback(s, state, s->userdata); 1095} 1096 1097static void withdraw_host_rrs(AvahiServer *s) { 1098 assert(s); 1099 1100 if (s->mdns.hinfo_entry_group) 1101 avahi_s_entry_group_reset(s->mdns.hinfo_entry_group); 1102 1103 if (s->mdns.browse_domain_entry_group) 1104 avahi_s_entry_group_reset(s->mdns.browse_domain_entry_group); 1105 1106 avahi_interface_monitor_update_rrs(s->monitor, 1); 1107 s->n_host_rr_pending = 0; 1108} 1109 1110void avahi_server_decrease_host_rr_pending(AvahiServer *s) { 1111 assert(s); 1112 1113 assert(s->n_host_rr_pending > 0); 1114 1115 if (--s->n_host_rr_pending == 0) { 1116 server_set_state(s, AVAHI_SERVER_RUNNING); 1117 avahi_log_info("\nAll host RR's have been announced/verified : SERVER RUNNING"); 1118 } 1119} 1120 1121void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) { 1122 assert(s); 1123 assert(g); 1124 1125 if ((state == AVAHI_ENTRY_GROUP_REGISTERING || state == AVAHI_ENTRY_GROUP_LLMNR_VERIFYING) && 1126 s->state == AVAHI_SERVER_REGISTERING) 1127 s->n_host_rr_pending ++; 1128 1129 else if ((state == AVAHI_ENTRY_GROUP_COLLISION || state == AVAHI_ENTRY_GROUP_LLMNR_COLLISION) && 1130 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) { 1131 withdraw_host_rrs(s); 1132 server_set_state(s, AVAHI_SERVER_COLLISION); 1133 1134 } else if ((state == AVAHI_ENTRY_GROUP_ESTABLISHED || state == AVAHI_ENTRY_GROUP_LLMNR_ESTABLISHED) && 1135 s->state == AVAHI_SERVER_REGISTERING) 1136 avahi_server_decrease_host_rr_pending(s); 1137} 1138 1139static void register_hinfo(AvahiServer *s) { 1140 struct utsname utsname; 1141 AvahiRecord *r; 1142 1143 assert(s); 1144 1145 if (!s->config.publish_hinfo) 1146 return; 1147 1148 if (s->mdns.hinfo_entry_group) 1149 assert(avahi_s_entry_group_is_empty(s->mdns.hinfo_entry_group)); 1150 else 1151 s->mdns.hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL); 1152 1153 if (!s->mdns.hinfo_entry_group) { 1154 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error)); 1155 return; 1156 } 1157 1158 /* Fill in HINFO rr */ 1159 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) { 1160 1161 if (uname(&utsname) < 0) 1162 avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno)); 1163 else { 1164 1165 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine)); 1166 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname)); 1167 1168 avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os); 1169 1170 if (avahi_server_add(s, s->mdns.hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) { 1171 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error)); 1172 return; 1173 } 1174 } 1175 1176 avahi_record_unref(r); 1177 } 1178 1179 if (avahi_s_entry_group_commit(s->mdns.hinfo_entry_group) < 0) 1180 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error)); 1181 1182} 1183 1184static void register_localhost(AvahiServer *s) { 1185 AvahiAddress a; 1186 assert(s); 1187 1188 /* Add localhost entries */ 1189 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a); 1190 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE|AVAHI_PUBLISH_USE_MULTICAST, "localhost", &a); 1191 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_VERIFY|AVAHI_PUBLISH_USE_LLMNR, "localhost", &a); 1192 1193 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a); 1194 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE|AVAHI_PUBLISH_USE_MULTICAST, "ip6-localhost", &a); 1195 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_VERIFY|AVAHI_PUBLISH_USE_LLMNR, "ip6-localhost", &a); 1196} 1197 1198static void register_browse_domain(AvahiServer *s) { 1199 assert(s); 1200 1201 if (!s->config.publish_domain) 1202 return; 1203 1204 if (avahi_domain_equal(s->domain_name, "local")) 1205 return; 1206 1207 if (s->mdns.browse_domain_entry_group) 1208 assert(avahi_s_entry_group_is_empty(s->mdns.browse_domain_entry_group)); 1209 else 1210 s->mdns.browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL); 1211 1212 if (!s->mdns.browse_domain_entry_group) { 1213 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error)); 1214 return; 1215 } 1216 1217 if (avahi_server_add_ptr(s, s->mdns.browse_domain_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, AVAHI_DEFAULT_TTL, "b._dns-sd._udp.local", s->domain_name) < 0) { 1218 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error)); 1219 return; 1220 } 1221 1222 if (avahi_s_entry_group_commit(s->mdns.browse_domain_entry_group) < 0) 1223 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error)); 1224} 1225 1226static void register_stuff(AvahiServer *s) { 1227 assert(s); 1228 1229 server_set_state(s, AVAHI_SERVER_REGISTERING); 1230 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */ 1231 1232 register_hinfo(s); 1233 register_browse_domain(s); 1234 avahi_interface_monitor_update_rrs(s->monitor, 0); 1235 1236 assert(s->n_host_rr_pending > 0); 1237 s->n_host_rr_pending --; 1238 1239 if (s->n_host_rr_pending == 0) 1240 server_set_state(s, AVAHI_SERVER_RUNNING); 1241} 1242 1243static void update_fqdn(AvahiServer *s) { 1244 char *n; 1245 1246 assert(s); 1247 assert(s->host_name); 1248 assert(s->domain_name); 1249 1250 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name))) 1251 return; /* OOM */ 1252 1253 avahi_free(s->host_name_fqdn); 1254 s->host_name_fqdn = n; 1255} 1256 1257int avahi_server_set_host_name(AvahiServer *s, const char *host_name) { 1258 char *hn = NULL; 1259 assert(s); 1260 1261 AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME); 1262 1263 if (!host_name) 1264 hn = avahi_get_host_name_strdup(); 1265 else 1266 hn = avahi_normalize_name_strdup(host_name); 1267 1268 hn[strcspn(hn, ".")] = 0; 1269 1270 if (avahi_domain_equal(s->host_name, hn) && s->state != AVAHI_SERVER_COLLISION) { 1271 avahi_free(hn); 1272 return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE); 1273 } 1274 1275 withdraw_host_rrs(s); 1276 1277 avahi_free(s->host_name); 1278 s->host_name = hn; 1279 1280 update_fqdn(s); 1281 1282 register_stuff(s); 1283 return AVAHI_OK; 1284} 1285 1286int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) { 1287 char *dn = NULL; 1288 assert(s); 1289 1290 AVAHI_CHECK_VALIDITY(s, !domain_name || avahi_is_valid_domain_name(domain_name), AVAHI_ERR_INVALID_DOMAIN_NAME); 1291 1292 if (!domain_name) 1293 dn = avahi_strdup("local"); 1294 else 1295 dn = avahi_normalize_name_strdup(domain_name); 1296 1297 if (avahi_domain_equal(s->domain_name, domain_name)) { 1298 avahi_free(dn); 1299 return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE); 1300 } 1301 1302 withdraw_host_rrs(s); 1303 1304 avahi_free(s->domain_name); 1305 s->domain_name = dn; 1306 update_fqdn(s); 1307 1308 register_stuff(s); 1309 1310 avahi_free(dn); 1311 return AVAHI_OK; 1312} 1313 1314static int valid_server_config(const AvahiServerConfig *sc) { 1315 AvahiStringList *l; 1316 1317 assert(sc); 1318 1319 if (sc->n_wide_area_servers > AVAHI_WIDE_AREA_SERVERS_MAX) 1320 return AVAHI_ERR_INVALID_CONFIG; 1321 1322 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name)) 1323 return AVAHI_ERR_INVALID_HOST_NAME; 1324 1325 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name)) 1326 return AVAHI_ERR_INVALID_DOMAIN_NAME; 1327 1328 for (l = sc->browse_domains; l; l = l->next) 1329 if (!avahi_is_valid_domain_name((char*) l->text)) 1330 return AVAHI_ERR_INVALID_DOMAIN_NAME; 1331 1332 return AVAHI_OK; 1333} 1334 1335static int setup_sockets(AvahiServer *s) { 1336 assert(s); 1337 1338 s->mdns.fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks, AVAHI_MDNS) : -1; 1339 s->mdns.fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks, AVAHI_MDNS) : -1; 1340 1341 if (s->mdns.fd_ipv6 < 0 && s->mdns.fd_ipv4 < 0) 1342 return AVAHI_ERR_NO_NETWORK; 1343 1344 if (s->mdns.fd_ipv4 < 0 && s->config.use_ipv4) 1345 avahi_log_notice("Failed to create IPv4 mDNS socket, proceeding in IPv6 only mode"); 1346 else if (s->mdns.fd_ipv6 < 0 && s->config.use_ipv6) 1347 avahi_log_notice("Failed to create IPv6 mDNS socket, proceeding in IPv4 only mode"); 1348 1349 s->mdns.fd_legacy_unicast_ipv4 = s->mdns.fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1; 1350 s->mdns.fd_legacy_unicast_ipv6 = s->mdns.fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1; 1351 1352 s->mdns.watch_ipv4 = 1353 s->mdns.watch_ipv6 = 1354 s->mdns.watch_legacy_unicast_ipv4 = 1355 s->mdns.watch_legacy_unicast_ipv6 = NULL; 1356 1357 if (s->mdns.fd_ipv4 >= 0) 1358 s->mdns.watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->mdns.fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s); 1359 if (s->mdns.fd_ipv6 >= 0) 1360 s->mdns.watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->mdns.fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s); 1361 1362 if (s->mdns.fd_legacy_unicast_ipv4 >= 0) 1363 s->mdns.watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->mdns.fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, legacy_unicast_socket_event, s); 1364 if (s->mdns.fd_legacy_unicast_ipv6 >= 0) 1365 s->mdns.watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->mdns.fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, legacy_unicast_socket_event, s); 1366 1367 return 0; 1368} 1369 1370AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) { 1371 AvahiServer *s; 1372 int e; 1373 1374 if (sc && (e = valid_server_config(sc)) < 0) { 1375 if (error) 1376 *error = e; 1377 return NULL; 1378 } 1379 1380 if (!(s = avahi_new(AvahiServer, 1))) { 1381 if (error) 1382 *error = AVAHI_ERR_NO_MEMORY; 1383 1384 return NULL; 1385 } 1386 1387 s->poll_api = poll_api; 1388 1389 if (sc) 1390 avahi_server_config_copy(&s->config, sc); 1391 else 1392 avahi_server_config_init(&s->config); 1393 1394 if ((e = setup_sockets(s)) < 0) { 1395 if (error) 1396 *error = e; 1397 1398 avahi_server_config_free(&s->config); 1399 avahi_free(s); 1400 1401 return NULL; 1402 } 1403 1404 if ((e = setup_llmnr_sockets(s)) < 0) { 1405 if (error) 1406 *error = e; 1407 1408 avahi_server_config_free(&s->config); 1409 avahi_free(s); 1410 1411 return NULL; 1412 } 1413 1414 s->n_host_rr_pending = 0; 1415 s->mdns.need_entry_cleanup = 0; 1416 s->mdns.need_group_cleanup = 0; 1417 s->llmnr.need_entry_cleanup = 0; 1418 s->llmnr.need_group_cleanup = 0; 1419 s->need_browser_cleanup = 0; 1420 s->cleanup_time_event = NULL; 1421 s->mdns.hinfo_entry_group = NULL; 1422 s->mdns.browse_domain_entry_group = NULL; 1423 s->error = AVAHI_OK; 1424 s->state = AVAHI_SERVER_INVALID; 1425 1426 s->callback = callback; 1427 s->userdata = userdata; 1428 1429 s->time_event_queue = avahi_time_event_queue_new(poll_api); 1430 1431 s->mdns.entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL); 1432 s->llmnr.entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL); 1433 1434 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->mdns.entries); 1435 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->llmnr.entries); 1436 1437 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->mdns.groups); 1438 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->llmnr.groups); 1439 1440 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL); 1441 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers); 1442 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers); 1443 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers); 1444 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers); 1445 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers); 1446 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers); 1447 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers); 1448 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers); 1449 1450 s->legacy_unicast_reflect_slots = NULL; 1451 s->legacy_unicast_reflect_id = 0; 1452 1453 s->mdns.record_list = avahi_record_list_new(); 1454 s->llmnr.record_list = avahi_record_list_new(); 1455 1456 /* Get host name */ 1457 s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup(); 1458 s->host_name[strcspn(s->host_name, ".")] = 0; 1459 s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local"); 1460 s->host_name_fqdn = NULL; 1461 update_fqdn(s); 1462 1463 do { 1464 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand(); 1465 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID); 1466 1467 if (s->config.enable_wide_area) { 1468 s->wide_area.wide_area_lookup_engine = avahi_wide_area_engine_new(s); 1469 avahi_wide_area_set_servers(s->wide_area.wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers); 1470 } else 1471 s->wide_area.wide_area_lookup_engine = NULL; 1472 1473 s->mdns.multicast_lookup_engine = avahi_multicast_lookup_engine_new(s); 1474 s->llmnr.llmnr_lookup_engine = avahi_llmnr_lookup_engine_new(s); 1475 1476 s->monitor = avahi_interface_monitor_new(s); 1477 avahi_interface_monitor_sync(s->monitor); 1478 1479 register_localhost(s); 1480 register_stuff(s); 1481 1482 return s; 1483} 1484 1485void avahi_server_free(AvahiServer* s) { 1486 assert(s); 1487 1488 /* Remove all browsers */ 1489 1490 while (s->dns_server_browsers) 1491 avahi_s_dns_server_browser_free(s->dns_server_browsers); 1492 while (s->host_name_resolvers) 1493 avahi_s_host_name_resolver_free(s->host_name_resolvers); 1494 while (s->address_resolvers) 1495 avahi_s_address_resolver_free(s->address_resolvers); 1496 while (s->domain_browsers) 1497 avahi_s_domain_browser_free(s->domain_browsers); 1498 while (s->service_type_browsers) 1499 avahi_s_service_type_browser_free(s->service_type_browsers); 1500 while (s->service_browsers) 1501 avahi_s_service_browser_free(s->service_browsers); 1502 while (s->service_resolvers) 1503 avahi_s_service_resolver_free(s->service_resolvers); 1504 while (s->record_browsers) 1505 avahi_s_record_browser_destroy(s->record_browsers); 1506 1507 /* Remove all locally rgeistered stuff */ 1508 1509 while (s->mdns.entries) 1510 avahi_entry_free(s, s->mdns.entries); 1511 1512 while (s->llmnr.entries) 1513 avahi_entry_free(s, s->llmnr.entries); 1514 1515 avahi_interface_monitor_free(s->monitor); 1516 free_slots(s); 1517 1518 avahi_hashmap_free(s->mdns.entries_by_key); 1519 avahi_hashmap_free(s->llmnr.entries_by_key); 1520 1521 avahi_record_list_free(s->mdns.record_list); 1522 avahi_record_list_free(s->llmnr.record_list); 1523 1524 avahi_hashmap_free(s->record_browser_hashmap); 1525 1526 if (s->wide_area.wide_area_lookup_engine) 1527 avahi_wide_area_engine_free(s->wide_area.wide_area_lookup_engine); 1528 1529 avahi_multicast_lookup_engine_free(s->mdns.multicast_lookup_engine); 1530 avahi_llmnr_lookup_engine_free(s->llmnr.llmnr_lookup_engine); 1531 1532 if (s->cleanup_time_event) 1533 avahi_time_event_free(s->cleanup_time_event); 1534 1535 avahi_time_event_queue_free(s->time_event_queue); 1536 1537 /* Free watches */ 1538 1539 if (s->mdns.watch_ipv4) 1540 s->poll_api->watch_free(s->mdns.watch_ipv4); 1541 if (s->mdns.watch_ipv6) 1542 s->poll_api->watch_free(s->mdns.watch_ipv6); 1543 1544 if (s->mdns.watch_legacy_unicast_ipv4) 1545 s->poll_api->watch_free(s->mdns.watch_legacy_unicast_ipv4); 1546 if (s->mdns.watch_legacy_unicast_ipv6) 1547 s->poll_api->watch_free(s->mdns.watch_legacy_unicast_ipv6); 1548 1549 if (s->llmnr.watch_ipv4) 1550 s->poll_api->watch_free(s->llmnr.watch_ipv4); 1551 if (s->llmnr.watch_ipv6) 1552 s->poll_api->watch_free(s->llmnr.watch_ipv6); 1553 1554 /* Free sockets */ 1555 1556 if (s->mdns.fd_ipv4 >= 0) 1557 close(s->mdns.fd_ipv4); 1558 if (s->mdns.fd_ipv6 >= 0) 1559 close(s->mdns.fd_ipv6); 1560 1561 if (s->mdns.fd_legacy_unicast_ipv4 >= 0) 1562 close(s->mdns.fd_legacy_unicast_ipv4); 1563 if (s->mdns.fd_legacy_unicast_ipv6 >= 0) 1564 close(s->mdns.fd_legacy_unicast_ipv6); 1565 1566 if (s->llmnr.fd_ipv4 >= 0) 1567 close(s->llmnr.fd_ipv4); 1568 if (s->llmnr.fd_ipv6 >= 0) 1569 close(s->mdns.fd_ipv6); 1570 1571 /* Free other stuff */ 1572 1573 avahi_free(s->host_name); 1574 avahi_free(s->domain_name); 1575 avahi_free(s->host_name_fqdn); 1576 1577 avahi_server_config_free(&s->config); 1578 1579 avahi_free(s); 1580} 1581 1582const char* avahi_server_get_domain_name(AvahiServer *s) { 1583 assert(s); 1584 1585 return s->domain_name; 1586} 1587 1588const char* avahi_server_get_host_name(AvahiServer *s) { 1589 assert(s); 1590 1591 return s->host_name; 1592} 1593 1594const char* avahi_server_get_host_name_fqdn(AvahiServer *s) { 1595 assert(s); 1596 1597 return s->host_name_fqdn; 1598} 1599 1600void* avahi_server_get_data(AvahiServer *s) { 1601 assert(s); 1602 1603 return s->userdata; 1604} 1605 1606void avahi_server_set_data(AvahiServer *s, void* userdata) { 1607 assert(s); 1608 1609 s->userdata = userdata; 1610} 1611 1612AvahiServerState avahi_server_get_state(AvahiServer *s) { 1613 assert(s); 1614 1615 return s->state; 1616} 1617 1618AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) { 1619 assert(c); 1620 1621 memset(c, 0, sizeof(AvahiServerConfig)); 1622 c->use_ipv6 = 1; 1623 c->use_ipv4 = 1; 1624 c->allow_interfaces = NULL; 1625 c->deny_interfaces = NULL; 1626 c->host_name = NULL; 1627 c->domain_name = NULL; 1628 c->check_response_ttl = 0; 1629 c->publish_hinfo = 1; 1630 c->publish_addresses = 1; 1631 c->publish_workstation = 1; 1632 c->publish_domain = 1; 1633 c->use_iff_running = 0; 1634 c->enable_reflector = 0; 1635 c->reflect_ipv = 0; 1636 c->add_service_cookie = 0; 1637 c->enable_wide_area = 0; 1638 c->n_wide_area_servers = 0; 1639 c->disallow_other_stacks = 0; 1640 c->browse_domains = NULL; 1641 c->disable_publishing = 0; 1642 c->allow_point_to_point = 0; 1643 c->publish_aaaa_on_ipv4 = 1; 1644 c->publish_a_on_ipv6 = 0; 1645 c->n_cache_entries_max = AVAHI_DEFAULT_CACHE_ENTRIES_MAX; 1646 c->ratelimit_interval = 0; 1647 c->ratelimit_burst = 0; 1648 1649 return c; 1650} 1651 1652void avahi_server_config_free(AvahiServerConfig *c) { 1653 assert(c); 1654 1655 avahi_free(c->host_name); 1656 avahi_free(c->domain_name); 1657 avahi_string_list_free(c->browse_domains); 1658 avahi_string_list_free(c->allow_interfaces); 1659 avahi_string_list_free(c->deny_interfaces); 1660} 1661 1662AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) { 1663 char *d = NULL, *h = NULL; 1664 AvahiStringList *browse = NULL, *allow = NULL, *deny = NULL; 1665 assert(ret); 1666 assert(c); 1667 1668 if (c->host_name) 1669 if (!(h = avahi_strdup(c->host_name))) 1670 return NULL; 1671 1672 if (c->domain_name) 1673 if (!(d = avahi_strdup(c->domain_name))) { 1674 avahi_free(h); 1675 return NULL; 1676 } 1677 1678 if (!(browse = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) { 1679 avahi_free(h); 1680 avahi_free(d); 1681 return NULL; 1682 } 1683 1684 if (!(allow = avahi_string_list_copy(c->allow_interfaces)) && c->allow_interfaces) { 1685 avahi_string_list_free(browse); 1686 avahi_free(h); 1687 avahi_free(d); 1688 return NULL; 1689 } 1690 1691 if (!(deny = avahi_string_list_copy(c->deny_interfaces)) && c->deny_interfaces) { 1692 avahi_string_list_free(allow); 1693 avahi_string_list_free(browse); 1694 avahi_free(h); 1695 avahi_free(d); 1696 return NULL; 1697 } 1698 1699 *ret = *c; 1700 ret->host_name = h; 1701 ret->domain_name = d; 1702 ret->browse_domains = browse; 1703 ret->allow_interfaces = allow; 1704 ret->deny_interfaces = deny; 1705 1706 return ret; 1707} 1708 1709int avahi_server_errno(AvahiServer *s) { 1710 assert(s); 1711 1712 return s->error; 1713} 1714 1715/* Just for internal use */ 1716int avahi_server_set_errno(AvahiServer *s, int error) { 1717 assert(s); 1718 1719 return s->error = error; 1720} 1721 1722uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) { 1723 assert(s); 1724 1725 return s->local_service_cookie; 1726} 1727 1728static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) { 1729 AvahiEntry *e; 1730 1731 assert(s); 1732 assert(key); 1733 1734 for (e = avahi_hashmap_lookup(s->mdns.entries_by_key, key); e; e = e->by_key_next) { 1735 1736 assert(e->type == AVAHI_ENTRY_MDNS); 1737 if ((e->interface == interface || e->interface <= 0 || interface <= 0) && 1738 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) && 1739 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING)) 1740 1741 return e; 1742 } 1743 1744 return NULL; 1745} 1746 1747int avahi_server_get_group_of_service(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiSEntryGroup** ret_group) { 1748 AvahiKey *key = NULL; 1749 AvahiEntry *e; 1750 int ret; 1751 char n[AVAHI_DOMAIN_NAME_MAX]; 1752 1753 assert(s); 1754 assert(name); 1755 assert(type); 1756 assert(ret_group); 1757 1758 AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); 1759 AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); 1760 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); 1761 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); 1762 AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); 1763 1764 if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0)) 1765 return avahi_server_set_errno(s, ret); 1766 1767 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV))) 1768 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 1769 1770 e = find_entry(s, interface, protocol, key); 1771 avahi_key_unref(key); 1772 1773 if (e) { 1774 *ret_group = e->group; 1775 return AVAHI_OK; 1776 } 1777 1778 return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND); 1779} 1780 1781int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) { 1782 AvahiKey *key = NULL; 1783 AvahiEntry *e; 1784 1785 assert(s); 1786 assert(name); 1787 1788 if (!s->host_name_fqdn) 1789 return 0; 1790 1791 if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV))) 1792 return 0; 1793 1794 e = find_entry(s, interface, protocol, key); 1795 avahi_key_unref(key); 1796 1797 if (!e) 1798 return 0; 1799 1800 return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name); 1801} 1802 1803int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) { 1804 AvahiEntry *e; 1805 1806 assert(s); 1807 assert(record); 1808 1809 for (e = avahi_hashmap_lookup(s->mdns.entries_by_key, record->key); e; e = e->by_key_next) { 1810 1811 assert(e->type == AVAHI_ENTRY_MDNS); 1812 if ((e->interface == interface || e->interface <= 0 || interface <= 0) && 1813 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) && 1814 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) && 1815 avahi_record_equal_no_ttl(record, e->record)) 1816 return 1; 1817 } 1818 1819 return 0; 1820} 1821 1822/** Set the wide area DNS servers */ 1823int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) { 1824 assert(s); 1825 1826 if (!s->wide_area.wide_area_lookup_engine) 1827 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG); 1828 1829 avahi_wide_area_set_servers(s->wide_area.wide_area_lookup_engine, a, n); 1830 return AVAHI_OK; 1831} 1832 1833const AvahiServerConfig* avahi_server_get_config(AvahiServer *s) { 1834 assert(s); 1835 1836 return &s->config; 1837} 1838 1839/** Set the browsing domains */ 1840int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains) { 1841 AvahiStringList *l; 1842 1843 assert(s); 1844 1845 for (l = s->config.browse_domains; l; l = l->next) 1846 if (!avahi_is_valid_domain_name((char*) l->text)) 1847 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME); 1848 1849 avahi_string_list_free(s->config.browse_domains); 1850 s->config.browse_domains = avahi_string_list_copy(domains); 1851 1852 return AVAHI_OK; 1853} 1854