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