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 <string.h> 25 #include <unistd.h> 26 #include <errno.h> 27 #include <stdio.h> 28 #include <assert.h> 29 #include <stdlib.h> 30 31 #include <arpa/inet.h> 32 33 #include <sys/utsname.h> 34 #include <sys/types.h> 35 #include <sys/socket.h> 36 37 #include <avahi-common/domain.h> 38 #include <avahi-common/timeval.h> 39 #include "avahi-common/avahi-malloc.h" 40 #include <avahi-common/error.h> 41 #include <avahi-common/domain.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 "rr-util.h" 51 #include "domain-util.h" 52 53 static void transport_flags_from_domain(AvahiServer *s, AvahiPublishFlags *flags, const char *domain) { 54 assert(flags); 55 assert(domain); 56 57 assert(!((*flags & AVAHI_PUBLISH_USE_MULTICAST) && (*flags & AVAHI_PUBLISH_USE_WIDE_AREA))); 58 59 if (*flags & (AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA)) 60 return; 61 62 if (!s->wide_area_lookup_engine || 63 !avahi_wide_area_has_servers(s->wide_area_lookup_engine) || 64 avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_LOCAL) || 65 avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV4) || 66 avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV6)) 67 *flags |= AVAHI_PUBLISH_USE_MULTICAST; 68 else 69 *flags |= AVAHI_PUBLISH_USE_WIDE_AREA; 70 } 71 72 void avahi_entry_free(AvahiServer*s, AvahiEntry *e) { 73 AvahiEntry *t; 74 75 assert(s); 76 assert(e); 77 78 avahi_goodbye_entry(s, e, 1, 1); 79 80 /* Remove from linked list */ 81 AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e); 82 83 /* Remove from hash table indexed by name */ 84 t = avahi_hashmap_lookup(s->entries_by_key, e->record->key); 85 AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e); 86 if (t) 87 avahi_hashmap_replace(s->entries_by_key, t->record->key, t); 88 else 89 avahi_hashmap_remove(s->entries_by_key, e->record->key); 90 91 /* Remove from associated group */ 92 if (e->group) 93 AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e); 94 95 avahi_record_unref(e->record); 96 avahi_free(e); 97 } 98 99 void avahi_entry_group_free(AvahiServer *s, AvahiSEntryGroup *g) { 100 assert(s); 101 assert(g); 102 103 while (g->entries) 104 avahi_entry_free(s, g->entries); 105 106 if (g->register_time_event) 107 avahi_time_event_free(g->register_time_event); 108 109 AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->groups, g); 110 avahi_free(g); 111 } 112 113 void avahi_cleanup_dead_entries(AvahiServer *s) { 114 assert(s); 115 116 if (s->need_group_cleanup) { 117 AvahiSEntryGroup *g, *next; 118 119 for (g = s->groups; g; g = next) { 120 next = g->groups_next; 121 122 if (g->dead) 123 avahi_entry_group_free(s, g); 124 } 125 126 s->need_group_cleanup = 0; 127 } 128 129 if (s->need_entry_cleanup) { 130 AvahiEntry *e, *next; 131 132 for (e = s->entries; e; e = next) { 133 next = e->entries_next; 134 135 if (e->dead) 136 avahi_entry_free(s, e); 137 } 138 139 s->need_entry_cleanup = 0; 140 } 141 142 if (s->need_browser_cleanup) 143 avahi_browser_cleanup(s); 144 145 if (s->cleanup_time_event) { 146 avahi_time_event_free(s->cleanup_time_event); 147 s->cleanup_time_event = NULL; 148 } 149 } 150 151 static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiPublishFlags flags) { 152 AvahiEntry *e; 153 154 assert(s); 155 assert(r); 156 157 for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) { 158 if (e->dead) 159 continue; 160 161 if (!(flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_UNIQUE)) 162 continue; 163 164 if ((flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) && (e->flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) ) 165 continue; 166 167 if (avahi_record_equal_no_ttl(r, e->record)) { 168 /* The records are the same, not a conflict in any case */ 169 continue; 170 } 171 172 if ((interface <= 0 || 173 e->interface <= 0 || 174 e->interface == interface) && 175 (protocol == AVAHI_PROTO_UNSPEC || 176 e->protocol == AVAHI_PROTO_UNSPEC || 177 e->protocol == protocol)) 178 179 return -1; 180 } 181 182 return 0; 183 } 184 185 static AvahiEntry * server_add_internal( 186 AvahiServer *s, 187 AvahiSEntryGroup *g, 188 AvahiIfIndex interface, 189 AvahiProtocol protocol, 190 AvahiPublishFlags flags, 191 AvahiRecord *r) { 192 193 AvahiEntry *e; 194 195 assert(s); 196 assert(r); 197 198 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, s->state != AVAHI_SERVER_FAILURE && s->state != AVAHI_SERVER_INVALID, AVAHI_ERR_BAD_STATE); 199 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); 200 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); 201 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID( 202 flags, 203 AVAHI_PUBLISH_NO_ANNOUNCE| 204 AVAHI_PUBLISH_NO_PROBE| 205 AVAHI_PUBLISH_UNIQUE| 206 AVAHI_PUBLISH_ALLOW_MULTIPLE| 207 AVAHI_PUBLISH_UPDATE| 208 AVAHI_PUBLISH_USE_WIDE_AREA| 209 AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); 210 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(r->key->name), AVAHI_ERR_INVALID_HOST_NAME); 211 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->ttl != 0, AVAHI_ERR_INVALID_TTL); 212 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !avahi_key_is_pattern(r->key), AVAHI_ERR_IS_PATTERN); 213 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_record_is_valid(r), AVAHI_ERR_INVALID_RECORD); 214 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->key->clazz == AVAHI_DNS_CLASS_IN, AVAHI_ERR_INVALID_DNS_CLASS); 215 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, 216 (r->key->type != 0) && 217 (r->key->type != AVAHI_DNS_TYPE_ANY) && 218 (r->key->type != AVAHI_DNS_TYPE_OPT) && 219 (r->key->type != AVAHI_DNS_TYPE_TKEY) && 220 (r->key->type != AVAHI_DNS_TYPE_TSIG) && 221 (r->key->type != AVAHI_DNS_TYPE_IXFR) && 222 (r->key->type != AVAHI_DNS_TYPE_AXFR), AVAHI_ERR_INVALID_DNS_TYPE); 223 224 transport_flags_from_domain(s, &flags, r->key->name); 225 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); 226 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !s->config.disable_publishing, AVAHI_ERR_NOT_PERMITTED); 227 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, 228 !g || 229 (g->state != AVAHI_ENTRY_GROUP_ESTABLISHED && g->state != AVAHI_ENTRY_GROUP_REGISTERING) || 230 (flags & AVAHI_PUBLISH_UPDATE), AVAHI_ERR_BAD_STATE); 231 232 if (flags & AVAHI_PUBLISH_UPDATE) { 233 AvahiRecord *old_record; 234 int is_first = 1; 235 236 /* Update and existing record */ 237 238 /* Find the first matching entry */ 239 for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) { 240 if (!e->dead && e->group == g && e->interface == interface && e->protocol == protocol) 241 break; 242 243 is_first = 0; 244 } 245 246 /* Hmm, nothing found? */ 247 if (!e) { 248 avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND); 249 return NULL; 250 } 251 252 /* Update the entry */ 253 old_record = e->record; 254 e->record = avahi_record_ref(r); 255 e->flags = flags; 256 257 /* Announce our changes when needed */ 258 if (!avahi_record_equal_no_ttl(old_record, r) && (!g || g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)) { 259 260 /* Remove the old entry from all caches, if needed */ 261 if (!(e->flags & AVAHI_PUBLISH_UNIQUE)) 262 avahi_goodbye_entry(s, e, 1, 0); 263 264 /* Reannounce our updated entry */ 265 avahi_reannounce_entry(s, e); 266 } 267 268 /* If we were the first entry in the list, we need to update the key */ 269 if (is_first) 270 avahi_hashmap_replace(s->entries_by_key, e->record->key, e); 271 272 avahi_record_unref(old_record); 273 274 } else { 275 AvahiEntry *t; 276 277 /* Add a new record */ 278 279 if (check_record_conflict(s, interface, protocol, r, flags) < 0) { 280 avahi_server_set_errno(s, AVAHI_ERR_COLLISION); 281 return NULL; 282 } 283 284 if (!(e = avahi_new(AvahiEntry, 1))) { 285 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 286 return NULL; 287 } 288 289 e->server = s; 290 e->record = avahi_record_ref(r); 291 e->group = g; 292 e->interface = interface; 293 e->protocol = protocol; 294 e->flags = flags; 295 e->dead = 0; 296 297 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, e->announcers); 298 299 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e); 300 301 /* Insert into hash table indexed by name */ 302 t = avahi_hashmap_lookup(s->entries_by_key, e->record->key); 303 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e); 304 avahi_hashmap_replace(s->entries_by_key, e->record->key, t); 305 306 /* Insert into group list */ 307 if (g) 308 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); 309 310 avahi_announce_entry(s, e); 311 } 312 313 return e; 314 } 315 316 int avahi_server_add( 317 AvahiServer *s, 318 AvahiSEntryGroup *g, 319 AvahiIfIndex interface, 320 AvahiProtocol protocol, 321 AvahiPublishFlags flags, 322 AvahiRecord *r) { 323 324 if (!server_add_internal(s, g, interface, protocol, flags, r)) 325 return avahi_server_errno(s); 326 327 return AVAHI_OK; 328 } 329 330 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) { 331 AvahiEntry **e = (AvahiEntry**) state; 332 assert(s); 333 assert(e); 334 335 if (!*e) 336 *e = g ? g->entries : s->entries; 337 338 while (*e && (*e)->dead) 339 *e = g ? (*e)->by_group_next : (*e)->entries_next; 340 341 if (!*e) 342 return NULL; 343 344 return avahi_record_ref((*e)->record); 345 } 346 347 int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) { 348 AvahiEntry *e; 349 350 assert(s); 351 assert(callback); 352 353 callback(";;; ZONE DUMP FOLLOWS ;;;", userdata); 354 355 for (e = s->entries; e; e = e->entries_next) { 356 char *t; 357 char ln[256]; 358 359 if (e->dead) 360 continue; 361 362 if (!(t = avahi_record_to_string(e->record))) 363 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 364 365 snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol); 366 avahi_free(t); 367 368 callback(ln, userdata); 369 } 370 371 avahi_dump_caches(s->monitor, callback, userdata); 372 373 if (s->wide_area_lookup_engine) 374 avahi_wide_area_cache_dump(s->wide_area_lookup_engine, callback, userdata); 375 return AVAHI_OK; 376 } 377 378 static AvahiEntry *server_add_ptr_internal( 379 AvahiServer *s, 380 AvahiSEntryGroup *g, 381 AvahiIfIndex interface, 382 AvahiProtocol protocol, 383 AvahiPublishFlags flags, 384 uint32_t ttl, 385 const char *name, 386 const char *dest) { 387 388 AvahiRecord *r; 389 AvahiEntry *e; 390 391 assert(s); 392 assert(dest); 393 394 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !name || avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME); 395 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(dest), AVAHI_ERR_INVALID_HOST_NAME); 396 397 if (!name) 398 name = s->host_name_fqdn; 399 400 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl))) { 401 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 402 return NULL; 403 } 404 405 r->data.ptr.name = avahi_normalize_name_strdup(dest); 406 e = server_add_internal(s, g, interface, protocol, flags, r); 407 avahi_record_unref(r); 408 return e; 409 } 410 411 int avahi_server_add_ptr( 412 AvahiServer *s, 413 AvahiSEntryGroup *g, 414 AvahiIfIndex interface, 415 AvahiProtocol protocol, 416 AvahiPublishFlags flags, 417 uint32_t ttl, 418 const char *name, 419 const char *dest) { 420 421 AvahiEntry *e; 422 423 assert(s); 424 425 if (!(e = server_add_ptr_internal(s, g, interface, protocol, flags, ttl, name, dest))) 426 return avahi_server_errno(s); 427 428 return AVAHI_OK; 429 } 430 431 int avahi_server_add_address( 432 AvahiServer *s, 433 AvahiSEntryGroup *g, 434 AvahiIfIndex interface, 435 AvahiProtocol protocol, 436 AvahiPublishFlags flags, 437 const char *name, 438 AvahiAddress *a) { 439 440 char n[AVAHI_DOMAIN_NAME_MAX]; 441 int ret = AVAHI_OK; 442 AvahiEntry *entry = NULL, *reverse = NULL; 443 AvahiRecord *r; 444 445 assert(s); 446 assert(a); 447 448 AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); 449 AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(a->proto), AVAHI_ERR_INVALID_PROTOCOL); 450 AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags, 451 AVAHI_PUBLISH_NO_REVERSE| 452 AVAHI_PUBLISH_NO_ANNOUNCE| 453 AVAHI_PUBLISH_NO_PROBE| 454 AVAHI_PUBLISH_UPDATE| 455 AVAHI_PUBLISH_USE_WIDE_AREA| 456 AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); 457 AVAHI_CHECK_VALIDITY(s, !name || avahi_is_valid_fqdn(name), AVAHI_ERR_INVALID_HOST_NAME); 458 459 /* Prepare the host naem */ 460 461 if (!name) 462 name = s->host_name_fqdn; 463 else { 464 AVAHI_ASSERT_TRUE(avahi_normalize_name(name, n, sizeof(n))); 465 name = n; 466 } 467 468 transport_flags_from_domain(s, &flags, name); 469 AVAHI_CHECK_VALIDITY(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); 470 471 /* Create the A/AAAA record */ 472 473 if (a->proto == AVAHI_PROTO_INET) { 474 475 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) { 476 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 477 goto finish; 478 } 479 480 r->data.a.address = a->data.ipv4; 481 482 } else { 483 assert(a->proto == AVAHI_PROTO_INET6); 484 485 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) { 486 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 487 goto finish; 488 } 489 490 r->data.aaaa.address = a->data.ipv6; 491 } 492 493 entry = server_add_internal(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r); 494 avahi_record_unref(r); 495 496 if (!entry) { 497 ret = avahi_server_errno(s); 498 goto finish; 499 } 500 501 /* Create the reverse lookup entry */ 502 503 if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) { 504 char reverse_n[AVAHI_DOMAIN_NAME_MAX]; 505 avahi_reverse_lookup_name(a, reverse_n, sizeof(reverse_n)); 506 507 if (!(reverse = server_add_ptr_internal(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse_n, name))) { 508 ret = avahi_server_errno(s); 509 goto finish; 510 } 511 } 512 513 finish: 514 515 if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) { 516 if (entry) 517 avahi_entry_free(s, entry); 518 if (reverse) 519 avahi_entry_free(s, reverse); 520 } 521 522 return ret; 523 } 524 525 static AvahiEntry *server_add_txt_strlst_nocopy( 526 AvahiServer *s, 527 AvahiSEntryGroup *g, 528 AvahiIfIndex interface, 529 AvahiProtocol protocol, 530 AvahiPublishFlags flags, 531 uint32_t ttl, 532 const char *name, 533 AvahiStringList *strlst) { 534 535 AvahiRecord *r; 536 AvahiEntry *e; 537 538 assert(s); 539 540 if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl))) { 541 avahi_string_list_free(strlst); 542 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 543 return NULL; 544 } 545 546 r->data.txt.string_list = strlst; 547 e = server_add_internal(s, g, interface, protocol, flags, r); 548 avahi_record_unref(r); 549 550 return e; 551 } 552 553 static AvahiStringList *add_magic_cookie( 554 AvahiServer *s, 555 AvahiStringList *strlst) { 556 557 assert(s); 558 559 if (!s->config.add_service_cookie) 560 return strlst; 561 562 if (avahi_string_list_find(strlst, AVAHI_SERVICE_COOKIE)) 563 /* This string list already contains a magic cookie */ 564 return strlst; 565 566 return avahi_string_list_add_printf(strlst, AVAHI_SERVICE_COOKIE"=%u", s->local_service_cookie); 567 } 568 569 static int server_add_service_strlst_nocopy( 570 AvahiServer *s, 571 AvahiSEntryGroup *g, 572 AvahiIfIndex interface, 573 AvahiProtocol protocol, 574 AvahiPublishFlags flags, 575 const char *name, 576 const char *type, 577 const char *domain, 578 const char *host, 579 uint16_t port, 580 AvahiStringList *strlst) { 581 582 char ptr_name[AVAHI_DOMAIN_NAME_MAX], svc_name[AVAHI_DOMAIN_NAME_MAX], enum_ptr[AVAHI_DOMAIN_NAME_MAX], *h = NULL; 583 AvahiRecord *r = NULL; 584 int ret = AVAHI_OK; 585 AvahiEntry *srv_entry = NULL, *txt_entry = NULL, *ptr_entry = NULL, *enum_entry = NULL; 586 587 assert(s); 588 assert(type); 589 assert(name); 590 591 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); 592 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); 593 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, 594 AVAHI_PUBLISH_NO_COOKIE| 595 AVAHI_PUBLISH_UPDATE| 596 AVAHI_PUBLISH_USE_WIDE_AREA| 597 AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); 598 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); 599 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); 600 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); 601 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !host || avahi_is_valid_fqdn(host), AVAHI_ERR_INVALID_HOST_NAME); 602 603 if (!domain) 604 domain = s->domain_name; 605 606 if (!host) 607 host = s->host_name_fqdn; 608 609 transport_flags_from_domain(s, &flags, domain); 610 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); 611 612 if (!(h = avahi_normalize_name_strdup(host))) { 613 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 614 goto fail; 615 } 616 617 if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 || 618 (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, type, domain)) < 0 || 619 (ret = avahi_service_name_join(enum_ptr, sizeof(enum_ptr), NULL, "_services._dns-sd._udp", domain)) < 0) { 620 avahi_server_set_errno(s, ret); 621 goto fail; 622 } 623 624 /* Add service enumeration PTR record */ 625 626 if (!(ptr_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name))) { 627 ret = avahi_server_errno(s); 628 goto fail; 629 } 630 631 /* Add SRV record */ 632 633 if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) { 634 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 635 goto fail; 636 } 637 638 r->data.srv.priority = 0; 639 r->data.srv.weight = 0; 640 r->data.srv.port = port; 641 r->data.srv.name = h; 642 h = NULL; 643 srv_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, r); 644 avahi_record_unref(r); 645 646 if (!srv_entry) { 647 ret = avahi_server_errno(s); 648 goto fail; 649 } 650 651 /* Add TXT record */ 652 653 if (!(flags & AVAHI_PUBLISH_NO_COOKIE)) 654 strlst = add_magic_cookie(s, strlst); 655 656 txt_entry = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst); 657 strlst = NULL; 658 659 if (!txt_entry) { 660 ret = avahi_server_errno(s); 661 goto fail; 662 } 663 664 /* Add service type enumeration record */ 665 666 if (!(enum_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name))) { 667 ret = avahi_server_errno(s); 668 goto fail; 669 } 670 671 fail: 672 if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) { 673 if (srv_entry) 674 avahi_entry_free(s, srv_entry); 675 if (txt_entry) 676 avahi_entry_free(s, txt_entry); 677 if (ptr_entry) 678 avahi_entry_free(s, ptr_entry); 679 if (enum_entry) 680 avahi_entry_free(s, enum_entry); 681 } 682 683 avahi_string_list_free(strlst); 684 avahi_free(h); 685 686 return ret; 687 } 688 689 int avahi_server_add_service_strlst( 690 AvahiServer *s, 691 AvahiSEntryGroup *g, 692 AvahiIfIndex interface, 693 AvahiProtocol protocol, 694 AvahiPublishFlags flags, 695 const char *name, 696 const char *type, 697 const char *domain, 698 const char *host, 699 uint16_t port, 700 AvahiStringList *strlst) { 701 702 assert(s); 703 assert(type); 704 assert(name); 705 706 return server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_copy(strlst)); 707 } 708 709 int avahi_server_add_service( 710 AvahiServer *s, 711 AvahiSEntryGroup *g, 712 AvahiIfIndex interface, 713 AvahiProtocol protocol, 714 AvahiPublishFlags flags, 715 const char *name, 716 const char *type, 717 const char *domain, 718 const char *host, 719 uint16_t port, 720 ... ){ 721 722 va_list va; 723 int ret; 724 725 va_start(va, port); 726 ret = server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_new_va(va)); 727 va_end(va); 728 729 return ret; 730 } 731 732 static int server_update_service_txt_strlst_nocopy( 733 AvahiServer *s, 734 AvahiSEntryGroup *g, 735 AvahiIfIndex interface, 736 AvahiProtocol protocol, 737 AvahiPublishFlags flags, 738 const char *name, 739 const char *type, 740 const char *domain, 741 AvahiStringList *strlst) { 742 743 char svc_name[AVAHI_DOMAIN_NAME_MAX]; 744 int ret = AVAHI_OK; 745 AvahiEntry *e; 746 747 assert(s); 748 assert(type); 749 assert(name); 750 751 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); 752 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); 753 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, 754 AVAHI_PUBLISH_NO_COOKIE| 755 AVAHI_PUBLISH_USE_WIDE_AREA| 756 AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); 757 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); 758 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); 759 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); 760 761 if (!domain) 762 domain = s->domain_name; 763 764 transport_flags_from_domain(s, &flags, domain); 765 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); 766 767 if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0) { 768 avahi_server_set_errno(s, ret); 769 goto fail; 770 } 771 772 /* Add TXT record */ 773 if (!(flags & AVAHI_PUBLISH_NO_COOKIE)) 774 strlst = add_magic_cookie(s, strlst); 775 776 e = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_UPDATE, AVAHI_DEFAULT_TTL, svc_name, strlst); 777 strlst = NULL; 778 779 if (!e) 780 ret = avahi_server_errno(s); 781 782 fail: 783 784 avahi_string_list_free(strlst); 785 786 return ret; 787 } 788 789 int avahi_server_update_service_txt_strlst( 790 AvahiServer *s, 791 AvahiSEntryGroup *g, 792 AvahiIfIndex interface, 793 AvahiProtocol protocol, 794 AvahiPublishFlags flags, 795 const char *name, 796 const char *type, 797 const char *domain, 798 AvahiStringList *strlst) { 799 800 return server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_copy(strlst)); 801 } 802 803 /** Update the TXT record for a service with the NULL termonate list of strings */ 804 int avahi_server_update_service_txt( 805 AvahiServer *s, 806 AvahiSEntryGroup *g, 807 AvahiIfIndex interface, 808 AvahiProtocol protocol, 809 AvahiPublishFlags flags, 810 const char *name, 811 const char *type, 812 const char *domain, 813 ...) { 814 815 va_list va; 816 int ret; 817 818 va_start(va, domain); 819 ret = server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_new_va(va)); 820 va_end(va); 821 822 return ret; 823 } 824 825 int avahi_server_add_service_subtype( 826 AvahiServer *s, 827 AvahiSEntryGroup *g, 828 AvahiIfIndex interface, 829 AvahiProtocol protocol, 830 AvahiPublishFlags flags, 831 const char *name, 832 const char *type, 833 const char *domain, 834 const char *subtype) { 835 836 int ret = AVAHI_OK; 837 char svc_name[AVAHI_DOMAIN_NAME_MAX], ptr_name[AVAHI_DOMAIN_NAME_MAX]; 838 839 assert(name); 840 assert(type); 841 assert(subtype); 842 843 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); 844 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); 845 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA), AVAHI_ERR_INVALID_FLAGS); 846 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); 847 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); 848 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); 849 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_subtype(subtype), AVAHI_ERR_INVALID_SERVICE_SUBTYPE); 850 851 if (!domain) 852 domain = s->domain_name; 853 854 transport_flags_from_domain(s, &flags, domain); 855 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); 856 857 if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 || 858 (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, subtype, domain)) < 0) { 859 avahi_server_set_errno(s, ret); 860 goto fail; 861 } 862 863 if ((ret = avahi_server_add_ptr(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0) 864 goto fail; 865 866 fail: 867 868 return ret; 869 } 870 871 static void hexstring(char *s, size_t sl, const void *p, size_t pl) { 872 static const char hex[] = "0123456789abcdef"; 873 int b = 0; 874 const uint8_t *k = p; 875 876 while (sl > 1 && pl > 0) { 877 *(s++) = hex[(b ? *k : *k >> 4) & 0xF]; 878 879 if (b) { 880 k++; 881 pl--; 882 } 883 884 b = !b; 885 886 sl--; 887 } 888 889 if (sl > 0) 890 *s = 0; 891 } 892 893 static AvahiEntry *server_add_dns_server_name( 894 AvahiServer *s, 895 AvahiSEntryGroup *g, 896 AvahiIfIndex interface, 897 AvahiProtocol protocol, 898 AvahiPublishFlags flags, 899 const char *domain, 900 AvahiDNSServerType type, 901 const char *name, 902 uint16_t port /** should be 53 */) { 903 904 AvahiEntry *e; 905 char t[AVAHI_DOMAIN_NAME_MAX], normalized_d[AVAHI_DOMAIN_NAME_MAX], *n; 906 907 AvahiRecord *r; 908 909 assert(s); 910 assert(name); 911 912 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_WIDE_AREA|AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); 913 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS); 914 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, port != 0, AVAHI_ERR_INVALID_PORT); 915 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_fqdn(name), AVAHI_ERR_INVALID_HOST_NAME); 916 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); 917 918 if (!domain) 919 domain = s->domain_name; 920 921 transport_flags_from_domain(s, &flags, domain); 922 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); 923 924 if (!(n = avahi_normalize_name_strdup(name))) { 925 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 926 return NULL; 927 } 928 929 AVAHI_ASSERT_TRUE(avahi_normalize_name(domain, normalized_d, sizeof(normalized_d))); 930 931 snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", normalized_d); 932 933 if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) { 934 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 935 avahi_free(n); 936 return NULL; 937 } 938 939 r->data.srv.priority = 0; 940 r->data.srv.weight = 0; 941 r->data.srv.port = port; 942 r->data.srv.name = n; 943 e = server_add_internal(s, g, interface, protocol, 0, r); 944 avahi_record_unref(r); 945 946 return e; 947 } 948 949 int avahi_server_add_dns_server_address( 950 AvahiServer *s, 951 AvahiSEntryGroup *g, 952 AvahiIfIndex interface, 953 AvahiProtocol protocol, 954 AvahiPublishFlags flags, 955 const char *domain, 956 AvahiDNSServerType type, 957 const AvahiAddress *address, 958 uint16_t port /** should be 53 */) { 959 960 AvahiRecord *r; 961 char n[64], h[64]; 962 AvahiEntry *a_entry, *s_entry; 963 964 assert(s); 965 assert(address); 966 967 AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); 968 AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(address->proto), AVAHI_ERR_INVALID_PROTOCOL); 969 AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA), AVAHI_ERR_INVALID_FLAGS); 970 AVAHI_CHECK_VALIDITY(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS); 971 AVAHI_CHECK_VALIDITY(s, port != 0, AVAHI_ERR_INVALID_PORT); 972 AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); 973 974 if (!domain) 975 domain = s->domain_name; 976 977 transport_flags_from_domain(s, &flags, domain); 978 AVAHI_CHECK_VALIDITY(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); 979 980 if (address->proto == AVAHI_PROTO_INET) { 981 hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv4Address)); 982 snprintf(n, sizeof(n), "ip-%s.%s", h, domain); 983 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME); 984 r->data.a.address = address->data.ipv4; 985 } else { 986 hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv6Address)); 987 snprintf(n, sizeof(n), "ip6-%s.%s", h, domain); 988 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME); 989 r->data.aaaa.address = address->data.ipv6; 990 } 991 992 if (!r) 993 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 994 995 a_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r); 996 avahi_record_unref(r); 997 998 if (!a_entry) 999 return avahi_server_errno(s); 1000 1001 if (!(s_entry = server_add_dns_server_name(s, g, interface, protocol, flags, domain, type, n, port))) { 1002 if (!(flags & AVAHI_PUBLISH_UPDATE)) 1003 avahi_entry_free(s, a_entry); 1004 return avahi_server_errno(s); 1005 } 1006 1007 return AVAHI_OK; 1008 } 1009 1010 void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) { 1011 assert(g); 1012 1013 if (g->state == state) 1014 return; 1015 1016 assert(state <= AVAHI_ENTRY_GROUP_COLLISION); 1017 1018 if (g->state == AVAHI_ENTRY_GROUP_ESTABLISHED) { 1019 1020 /* If the entry group was established for a time longer then 1021 * 5s, reset the establishment trial counter */ 1022 1023 if (avahi_age(&g->established_at) > 5000000) 1024 g->n_register_try = 0; 1025 } else if (g->state == AVAHI_ENTRY_GROUP_REGISTERING) { 1026 if (g->register_time_event) { 1027 avahi_time_event_free(g->register_time_event); 1028 g->register_time_event = NULL; 1029 } 1030 } 1031 1032 if (state == AVAHI_ENTRY_GROUP_ESTABLISHED) 1033 1034 /* If the entry group is now established, remember the time 1035 * this happened */ 1036 1037 gettimeofday(&g->established_at, NULL); 1038 1039 g->state = state; 1040 1041 if (g->callback) 1042 g->callback(g->server, g, state, g->userdata); 1043 } 1044 1045 AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) { 1046 AvahiSEntryGroup *g; 1047 1048 assert(s); 1049 1050 if (!(g = avahi_new(AvahiSEntryGroup, 1))) { 1051 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 1052 return NULL; 1053 } 1054 1055 g->server = s; 1056 g->callback = callback; 1057 g->userdata = userdata; 1058 g->dead = 0; 1059 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED; 1060 g->n_probing = 0; 1061 g->n_register_try = 0; 1062 g->register_time_event = NULL; 1063 g->register_time.tv_sec = 0; 1064 g->register_time.tv_usec = 0; 1065 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries); 1066 1067 AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g); 1068 return g; 1069 } 1070 1071 static void cleanup_time_event_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* userdata) { 1072 AvahiServer *s = userdata; 1073 1074 assert(s); 1075 1076 avahi_cleanup_dead_entries(s); 1077 } 1078 1079 static void schedule_cleanup(AvahiServer *s) { 1080 struct timeval tv; 1081 1082 assert(s); 1083 1084 if (!s->cleanup_time_event) 1085 s->cleanup_time_event = avahi_time_event_new(s->time_event_queue, avahi_elapse_time(&tv, 1000, 0), &cleanup_time_event_callback, s); 1086 } 1087 1088 void avahi_s_entry_group_free(AvahiSEntryGroup *g) { 1089 AvahiEntry *e; 1090 1091 assert(g); 1092 assert(g->server); 1093 1094 for (e = g->entries; e; e = e->by_group_next) { 1095 if (!e->dead) { 1096 avahi_goodbye_entry(g->server, e, 1, 1); 1097 e->dead = 1; 1098 } 1099 } 1100 1101 if (g->register_time_event) { 1102 avahi_time_event_free(g->register_time_event); 1103 g->register_time_event = NULL; 1104 } 1105 1106 g->dead = 1; 1107 1108 g->server->need_group_cleanup = 1; 1109 g->server->need_entry_cleanup = 1; 1110 1111 schedule_cleanup(g->server); 1112 } 1113 1114 static void entry_group_commit_real(AvahiSEntryGroup *g) { 1115 assert(g); 1116 1117 gettimeofday(&g->register_time, NULL); 1118 1119 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING); 1120 1121 if (g->dead) 1122 return; 1123 1124 avahi_announce_group(g->server, g); 1125 avahi_s_entry_group_check_probed(g, 0); 1126 } 1127 1128 static void entry_group_register_time_event_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* userdata) { 1129 AvahiSEntryGroup *g = userdata; 1130 assert(g); 1131 1132 avahi_time_event_free(g->register_time_event); 1133 g->register_time_event = NULL; 1134 1135 /* Holdoff time passed, so let's start probing */ 1136 entry_group_commit_real(g); 1137 } 1138 1139 int avahi_s_entry_group_commit(AvahiSEntryGroup *g) { 1140 struct timeval now; 1141 1142 assert(g); 1143 assert(!g->dead); 1144 1145 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION) 1146 return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE); 1147 1148 if (avahi_s_entry_group_is_empty(g)) 1149 return avahi_server_set_errno(g->server, AVAHI_ERR_IS_EMPTY); 1150 1151 g->n_register_try++; 1152 1153 avahi_timeval_add(&g->register_time, 1154 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ? 1155 AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT : 1156 AVAHI_RR_HOLDOFF_MSEC)); 1157 1158 gettimeofday(&now, NULL); 1159 1160 if (avahi_timeval_compare(&g->register_time, &now) <= 0) { 1161 1162 /* Holdoff time passed, so let's start probing */ 1163 entry_group_commit_real(g); 1164 } else { 1165 1166 /* Holdoff time has not yet passed, so let's wait */ 1167 assert(!g->register_time_event); 1168 g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g); 1169 1170 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING); 1171 } 1172 1173 return AVAHI_OK; 1174 } 1175 1176 void avahi_s_entry_group_reset(AvahiSEntryGroup *g) { 1177 AvahiEntry *e; 1178 assert(g); 1179 1180 for (e = g->entries; e; e = e->by_group_next) { 1181 if (!e->dead) { 1182 avahi_goodbye_entry(g->server, e, 1, 1); 1183 e->dead = 1; 1184 } 1185 } 1186 g->server->need_entry_cleanup = 1; 1187 1188 g->n_probing = 0; 1189 1190 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED); 1191 1192 schedule_cleanup(g->server); 1193 } 1194 1195 int avahi_entry_is_commited(AvahiEntry *e) { 1196 assert(e); 1197 assert(!e->dead); 1198 1199 return !e->group || 1200 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING || 1201 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED; 1202 } 1203 1204 AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) { 1205 assert(g); 1206 assert(!g->dead); 1207 1208 return g->state; 1209 } 1210 1211 void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) { 1212 assert(g); 1213 1214 g->userdata = userdata; 1215 } 1216 1217 void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) { 1218 assert(g); 1219 1220 return g->userdata; 1221 } 1222 1223 int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) { 1224 AvahiEntry *e; 1225 assert(g); 1226 1227 /* Look for an entry that is not dead */ 1228 for (e = g->entries; e; e = e->by_group_next) 1229 if (!e->dead) 1230 return 0; 1231 1232 return 1; 1233 } 1234