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/malloc.h> 40#include <avahi-common/error.h> 41#include <avahi-common/domain.h> 42#include <avahi-common/defs.h> 43 44#include "internal.h" 45#include "iface.h" 46#include "socket.h" 47#include "browse.h" 48#include "log.h" 49#include "util.h" 50#include "dns-srv-rr.h" 51#include "rr-util.h" 52#include "domain-util.h" 53 54static void transport_flags_from_domain(AvahiServer *s, AvahiPublishFlags *flags, const char *domain, int check_for_llmnr) { 55 assert(flags); 56 assert(domain); 57 assert(!((*flags & AVAHI_PUBLISH_USE_MULTICAST) && (*flags & AVAHI_PUBLISH_USE_WIDE_AREA))); 58 59 if (check_for_llmnr) { 60 assert(!((*flags & AVAHI_PUBLISH_USE_WIDE_AREA) && (*flags & AVAHI_PUBLISH_USE_LLMNR))); 61 assert(!((*flags & AVAHI_PUBLISH_USE_MULTICAST) && (*flags & AVAHI_PUBLISH_USE_LLMNR))); 62 63 if (*flags & (AVAHI_PUBLISH_USE_MULTICAST | AVAHI_PUBLISH_USE_WIDE_AREA | AVAHI_PUBLISH_USE_LLMNR)) 64 return; 65 66 } else /*!check_for_llmnr*/ 67 if (*flags & (AVAHI_PUBLISH_USE_MULTICAST | AVAHI_PUBLISH_USE_WIDE_AREA)) 68 return; 69 70 if (check_for_llmnr && avahi_is_valid_host_name(domain)) 71 *flags |= AVAHI_PUBLISH_USE_LLMNR; 72 else if (!s->wide_area.wide_area_lookup_engine || 73 !avahi_wide_area_has_servers(s->wide_area.wide_area_lookup_engine) || 74 avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_LOCAL) || 75 avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV4) || 76 avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV6)) 77 *flags |= AVAHI_PUBLISH_USE_MULTICAST; 78 else 79 *flags |= AVAHI_PUBLISH_USE_WIDE_AREA; 80} 81 82void avahi_entry_free(AvahiServer*s, AvahiEntry *e) { 83 AvahiEntry *t; 84 AvahiHashmap *entries_by_key; 85 86 assert(s); 87 assert(e); 88 89 if (e->type == AVAHI_ENTRY_MDNS) { 90 avahi_goodbye_entry(s, e, 1, 1); 91 AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->mdns.entries, e); 92 entries_by_key = s->mdns.entries_by_key; 93 } else { 94 assert (e->type == AVAHI_ENTRY_LLMNR); 95 avahi_remove_verifiers (s, e); 96 AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->llmnr.entries, e); 97 entries_by_key = s->llmnr.entries_by_key; 98 } 99 100 t = avahi_hashmap_lookup(entries_by_key, e->record->key); 101 AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e); 102 if (t) 103 avahi_hashmap_replace(entries_by_key, t->record->key, t); 104 else 105 avahi_hashmap_remove(entries_by_key, e->record->key); 106 107 /* Remove from associated group */ 108 if (e->group) 109 AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e); 110 111 avahi_record_unref(e->record); 112 avahi_free(e); 113} 114 115void avahi_entry_group_free(AvahiServer *s, AvahiSEntryGroup *g) { 116 assert(s); 117 assert(g); 118 119 while (g->entries) 120 avahi_entry_free(s, g->entries); 121 122 if (g->type == AVAHI_GROUP_MDNS && g->proto.mdns.register_time_event) 123 avahi_time_event_free(g->proto.mdns.register_time_event); 124 125 if (g->type == AVAHI_GROUP_MDNS) 126 AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->mdns.groups, g); 127 else 128 AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->llmnr.groups, g); 129 130 avahi_free(g); 131} 132 133void avahi_cleanup_dead_entries(AvahiServer *s) { 134 assert(s); 135 136 if (s->mdns.need_group_cleanup) { 137 AvahiSEntryGroup *g, *next; 138 139 for (g = s->mdns.groups; g; g = next) { 140 next = g->groups_next; 141 142 if (g->dead) 143 avahi_entry_group_free(s, g); 144 } 145 146 s->mdns.need_group_cleanup = 0; 147 } 148 149 if (s->mdns.need_entry_cleanup) { 150 AvahiEntry *e, *next; 151 152 for (e = s->mdns.entries; e; e = next) { 153 next = e->entries_next; 154 155 if (e->dead) 156 avahi_entry_free(s, e); 157 } 158 159 s->mdns.need_entry_cleanup = 0; 160 } 161 162 if (s->need_browser_cleanup) 163 avahi_browser_cleanup(s); 164 165 if (s->cleanup_time_event) { 166 avahi_time_event_free(s->cleanup_time_event); 167 s->cleanup_time_event = NULL; 168 } 169} 170 171static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiPublishFlags flags, AvahiPublishProtocol proto) { 172 AvahiEntry *e; 173 174 assert(s); 175 assert(r); 176 177 for (e = avahi_hashmap_lookup(proto == AVAHI_MDNS ? s->mdns.entries_by_key : s->llmnr.entries_by_key, r->key); e; e = e->by_key_next) { 178 179 if (proto == AVAHI_MDNS) 180 assert(e->type == AVAHI_ENTRY_MDNS); 181 else 182 assert(e->type == AVAHI_ENTRY_LLMNR); 183 184 if (e->dead) 185 continue; 186 187 if (!(flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_UNIQUE)) 188 continue; 189 190 if ((flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) && (e->flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) ) 191 continue; 192 193 if (avahi_record_equal_no_ttl(r, e->record)) { 194 /* The records are the same, not a conflict in any case */ 195 continue; 196 } 197 198 if ((interface <= 0 || 199 e->interface <= 0 || 200 e->interface == interface) && 201 (protocol == AVAHI_PROTO_UNSPEC || 202 e->protocol == AVAHI_PROTO_UNSPEC || 203 e->protocol == protocol)) 204 205 return -1; 206 } 207 208 return 0; 209} 210 211static AvahiEntry *server_add_llmnr_internal( 212 AvahiServer *s, 213 AvahiSEntryGroup *g, 214 AvahiIfIndex interface, 215 AvahiProtocol protocol, 216 AvahiPublishFlags flags, 217 AvahiRecord *r) { 218 219 AvahiEntry *e; 220 221 assert(s); 222 assert(r); 223 224 /* Flags should be LLMNR flags only */ 225 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID( 226 flags, 227 AVAHI_PUBLISH_UNIQUE| 228 AVAHI_PUBLISH_ALLOW_MULTIPLE| 229 AVAHI_PUBLISH_UPDATE| 230 AVAHI_PUBLISH_NO_VERIFY| 231 AVAHI_PUBLISH_USE_LLMNR), AVAHI_ERR_INVALID_FLAGS); 232 233 /* Publishing of only A/AAAA/PTR records is supported using LLMNR */ 234 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, 235 (r->key->type == AVAHI_DNS_TYPE_A) || 236 (r->key->type == AVAHI_DNS_TYPE_AAAA) || 237 (r->key->type == AVAHI_DNS_TYPE_CNAME) || 238 (r->key->type == AVAHI_DNS_TYPE_PTR), AVAHI_ERR_INVALID_DNS_TYPE); 239 240 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, 241 !g || 242 (g->state != AVAHI_ENTRY_GROUP_LLMNR_ESTABLISHED && 243 g->state != AVAHI_ENTRY_GROUP_LLMNR_VERIFYING) || 244 (flags & AVAHI_PUBLISH_UPDATE), AVAHI_ERR_BAD_STATE); 245 246 /* Copy Copy Copy. */ 247 if (flags & AVAHI_PUBLISH_UPDATE) { 248 AvahiRecord *old_record; 249 int is_first = 1; 250 251 /* type can't be _UNSET*/ 252 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !g || g->type == AVAHI_GROUP_LLMNR, AVAHI_ERR_INVALID_GROUP); 253 254 /* Find the first matching entry */ 255 for (e = avahi_hashmap_lookup(s->llmnr.entries_by_key, r->key); e; e = e->by_key_next) { 256 257 assert(e->type == AVAHI_ENTRY_LLMNR); 258 if (!e->dead && e->group == g && e->interface == interface && e->protocol == protocol) 259 break; 260 261 is_first = 0; 262 } 263 264 if (!e) { 265 avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND); 266 return NULL; 267 } 268 269 /* Update the entry */ 270 old_record = e->record; 271 e->record = avahi_record_ref(r); 272 e->flags = flags; 273 274 /* Reverify changes, if needed */ 275 if (!avahi_record_equal_no_ttl(old_record, r) && (!g || g->state != AVAHI_ENTRY_GROUP_LLMNR_UNCOMMITED)) 276 /* Reverify our updated entry */ 277 avahi_reverify_entry(s, e); 278 279 /* If we were the first entry in the list, we need to update the key */ 280 if (is_first) 281 avahi_hashmap_replace(s->llmnr.entries_by_key, e->record->key, e); 282 283 avahi_record_unref(old_record); 284 285 } else { 286 AvahiEntry *t; 287 288 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, 289 !g || 290 g->type == AVAHI_GROUP_UNSET || 291 g->type == AVAHI_GROUP_LLMNR, AVAHI_ERR_INVALID_GROUP); 292 293 if (g && g->type == AVAHI_GROUP_UNSET) { 294 g->proto.llmnr.n_verifying = 0; 295 g->proto.llmnr.n_entries = 0; 296 g->type = AVAHI_GROUP_LLMNR; 297 298 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries); 299 AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->llmnr.groups, g); 300 } 301 /* Check for the conflict */ 302 303 if (check_record_conflict(s, interface, protocol, r, flags, AVAHI_LLMNR) < 0) { 304 avahi_server_set_errno(s, AVAHI_ERR_COLLISION); 305 return NULL; 306 } 307 308 if (!(e = avahi_new(AvahiEntry, 1))) { 309 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 310 return NULL; 311 } 312 313 e->server = s; 314 e->record = avahi_record_ref(r); 315 e->group = g; 316 e->interface = interface; 317 e->protocol = protocol; 318 e->flags = flags; 319 e->dead = 0; 320 e->type = AVAHI_ENTRY_LLMNR; 321 AVAHI_LLIST_HEAD_INIT(AvahiLLMNREntryVerify, e->proto.llmnr.verifiers); 322 323 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->llmnr.entries, e); 324 325 /* Insert into hash table indexed by name */ 326 t = avahi_hashmap_lookup(s->llmnr.entries_by_key, r->key); 327 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e); 328 avahi_hashmap_replace(s->llmnr.entries_by_key, e->record->key, t); 329 330 /* Insert into group list */ 331 if (g) 332 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); 333 else 334 /* Verify now if it doesn't belong to any group otherwise entry will be verified 335 when group will be commited */ 336 avahi_verify_entry(s, e); 337 } 338 339 return e; 340} 341static AvahiEntry * server_add_internal( 342 AvahiServer *s, 343 AvahiSEntryGroup *g, 344 AvahiIfIndex interface, 345 AvahiProtocol protocol, 346 AvahiPublishFlags flags, 347 AvahiRecord *r) { 348 349 AvahiEntry *e; 350 351 assert(s); 352 assert(r); 353 354 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, s->state != AVAHI_SERVER_FAILURE && s->state != AVAHI_SERVER_INVALID, AVAHI_ERR_BAD_STATE); 355 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); 356 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); 357 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID( 358 flags, 359 AVAHI_PUBLISH_NO_ANNOUNCE| 360 AVAHI_PUBLISH_NO_PROBE| 361 AVAHI_PUBLISH_NO_VERIFY| 362 AVAHI_PUBLISH_UNIQUE| 363 AVAHI_PUBLISH_ALLOW_MULTIPLE| 364 AVAHI_PUBLISH_UPDATE| 365 AVAHI_PUBLISH_USE_WIDE_AREA| 366 AVAHI_PUBLISH_USE_MULTICAST| 367 AVAHI_PUBLISH_USE_LLMNR), AVAHI_ERR_INVALID_FLAGS); 368 369 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(r->key->name), AVAHI_ERR_INVALID_HOST_NAME); 370 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->ttl != 0, AVAHI_ERR_INVALID_TTL); 371 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !avahi_key_is_pattern(r->key), AVAHI_ERR_IS_PATTERN); 372 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_record_is_valid(r), AVAHI_ERR_INVALID_RECORD); 373 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->key->clazz == AVAHI_DNS_CLASS_IN, AVAHI_ERR_INVALID_DNS_CLASS); 374 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, 375 (r->key->type != 0) && 376 (r->key->type != AVAHI_DNS_TYPE_ANY) && 377 (r->key->type != AVAHI_DNS_TYPE_OPT) && 378 (r->key->type != AVAHI_DNS_TYPE_TKEY) && 379 (r->key->type != AVAHI_DNS_TYPE_TSIG) && 380 (r->key->type != AVAHI_DNS_TYPE_IXFR) && 381 (r->key->type != AVAHI_DNS_TYPE_AXFR), AVAHI_ERR_INVALID_DNS_TYPE); 382 383 transport_flags_from_domain(s, &flags, r->key->name, 1); 384 385 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, (flags & AVAHI_PUBLISH_USE_MULTICAST) || (flags & AVAHI_PUBLISH_USE_LLMNR), AVAHI_ERR_NOT_SUPPORTED); 386 387 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !s->config.disable_publishing, AVAHI_ERR_NOT_PERMITTED); 388 389 if (flags & AVAHI_PUBLISH_USE_LLMNR) 390 return server_add_llmnr_internal(s, g, interface, protocol, flags, r); 391 392 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !(flags & AVAHI_PUBLISH_NO_VERIFY), AVAHI_ERR_INVALID_FLAGS); 393 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, 394 !g || 395 (g->state != AVAHI_ENTRY_GROUP_ESTABLISHED && g->state != AVAHI_ENTRY_GROUP_REGISTERING) || 396 (flags & AVAHI_PUBLISH_UPDATE), AVAHI_ERR_BAD_STATE); 397 398 if (flags & AVAHI_PUBLISH_UPDATE) { 399 AvahiRecord *old_record; 400 int is_first = 1; 401 402 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !g || g->type == AVAHI_GROUP_MDNS, AVAHI_ERR_INVALID_GROUP); 403 404 /* Update an existing record */ 405 406 /* Find the first matching entry */ 407 for (e = avahi_hashmap_lookup(s->mdns.entries_by_key, r->key); e; e = e->by_key_next) { 408 assert(e->type == AVAHI_ENTRY_MDNS); 409 if (!e->dead && e->group == g && e->interface == interface && e->protocol == protocol) 410 break; 411 412 is_first = 0; 413 } 414 415 /* Hmm, nothing found? */ 416 if (!e) { 417 avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND); 418 return NULL; 419 } 420 421 /* Update the entry */ 422 old_record = e->record; 423 e->record = avahi_record_ref(r); 424 e->flags = flags; 425 426 /* Announce our changes when needed */ 427 if (!avahi_record_equal_no_ttl(old_record, r) && (!g || g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)) { 428 429 /* Remove the old entry from all caches, if needed */ 430 if (!(e->flags & AVAHI_PUBLISH_UNIQUE)) 431 avahi_goodbye_entry(s, e, 1, 0); 432 433 /* Reannounce our updated entry */ 434 avahi_reannounce_entry(s, e); 435 } 436 437 /* If we were the first entry in the list, we need to update the key */ 438 if (is_first) 439 avahi_hashmap_replace(s->mdns.entries_by_key, e->record->key, e); 440 441 avahi_record_unref(old_record); 442 443 } else { 444 AvahiEntry *t; 445 446 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, 447 !g || 448 g->type == AVAHI_GROUP_UNSET || 449 g->type == AVAHI_GROUP_MDNS, AVAHI_ERR_INVALID_GROUP); 450 451 if (g && g->type == AVAHI_GROUP_UNSET) { 452 g->proto.mdns.n_probing = 0; 453 g->proto.mdns.n_register_try = 0; 454 g->proto.mdns.register_time_event = NULL; 455 g->proto.mdns.register_time.tv_sec = 0; 456 g->proto.mdns.register_time.tv_usec = 0; 457 g->type = AVAHI_GROUP_MDNS; 458 459 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries); 460 AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->mdns.groups, g); 461 } 462 /* Add a new record */ 463 464 if (check_record_conflict(s, interface, protocol, r, flags, AVAHI_MDNS) < 0) { 465 avahi_server_set_errno(s, AVAHI_ERR_COLLISION); 466 return NULL; 467 } 468 469 if (!(e = avahi_new(AvahiEntry, 1))) { 470 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 471 return NULL; 472 } 473 474 e->server = s; 475 e->record = avahi_record_ref(r); 476 e->group = g; 477 e->interface = interface; 478 e->protocol = protocol; 479 e->flags = flags; 480 e->dead = 0; 481 e->type = AVAHI_ENTRY_MDNS; 482 483 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, e->proto.mdns.announcers); 484 485 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->mdns.entries, e); 486 487 /* Insert into hash table indexed by name */ 488 t = avahi_hashmap_lookup(s->mdns.entries_by_key, e->record->key); 489 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e); 490 avahi_hashmap_replace(s->mdns.entries_by_key, e->record->key, t); 491 492 /* Insert into group list */ 493 if (g) 494 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); 495 496 avahi_announce_entry(s, e); 497 } 498 499 return e; 500} 501 502int avahi_server_add( 503 AvahiServer *s, 504 AvahiSEntryGroup *g, 505 AvahiIfIndex interface, 506 AvahiProtocol protocol, 507 AvahiPublishFlags flags, 508 AvahiRecord *r) { 509 510 if (!server_add_internal(s, g, interface, protocol, flags, r)) 511 return avahi_server_errno(s); 512 513 return AVAHI_OK; 514} 515 516const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state, AvahiPublishProtocol proto) { 517 AvahiEntry **e = (AvahiEntry**) state; 518 assert(s); 519 assert(e); 520 521 if (!*e) 522 *e = g ? g->entries : (proto == AVAHI_MDNS ? s->mdns.entries : s->llmnr.entries); 523 524 while (*e && (*e)->dead) 525 *e = g ? (*e)->by_group_next : (*e)->entries_next; 526 527 if (!*e) 528 return NULL; 529 530 return avahi_record_ref((*e)->record); 531} 532 533static void zone_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata, AvahiPublishProtocol proto) { 534 AvahiEntry *e; 535 536 for (e = ((proto == AVAHI_MDNS) ? (s->mdns.entries) : (s->llmnr.entries)); e; e = e->entries_next) { 537 char *t; 538 char ln[256]; 539 540 if (e->dead) 541 continue; 542 543 if (!(t = avahi_record_to_string(e->record))) { 544 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 545 return; 546 } 547 548 snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol); 549 avahi_free(t); 550 551 callback(ln, userdata); 552 } 553} 554 555int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) { 556 assert(s); 557 assert(callback); 558 559 callback(";;; mDNS ZONE DUMP FOLLOWS ;;;", userdata); 560 zone_dump(s, callback, userdata, AVAHI_MDNS); 561 avahi_dump_caches(s->monitor, callback, userdata); 562 563 callback(";;; LLMNR ZONE DUMP FOLLOWS ;;;", userdata); 564 zone_dump(s, callback, userdata, AVAHI_LLMNR); 565 avahi_llmnr_cache_dump(s->llmnr.llmnr_lookup_engine, callback, userdata); 566 567 if (s->wide_area.wide_area_lookup_engine) 568 avahi_wide_area_cache_dump(s->wide_area.wide_area_lookup_engine, callback, userdata); 569 570 return AVAHI_OK; 571} 572 573static AvahiEntry *server_add_ptr_internal( 574 AvahiServer *s, 575 AvahiSEntryGroup *g, 576 AvahiIfIndex interface, 577 AvahiProtocol protocol, 578 AvahiPublishFlags flags, 579 uint32_t ttl, 580 const char *name, 581 const char *dest) { 582 583 AvahiRecord *r; 584 AvahiEntry *e; 585 586 assert(s); 587 assert(dest); 588 589 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !name || avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME); 590 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(dest), AVAHI_ERR_INVALID_HOST_NAME); 591 592 if (!name) 593 name = s->host_name_fqdn; 594 595 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl))) { 596 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 597 return NULL; 598 } 599 600 r->data.ptr.name = avahi_normalize_name_strdup(dest); 601 e = server_add_internal(s, g, interface, protocol, flags, r); 602 avahi_record_unref(r); 603 return e; 604} 605 606int avahi_server_add_ptr( 607 AvahiServer *s, 608 AvahiSEntryGroup *g, 609 AvahiIfIndex interface, 610 AvahiProtocol protocol, 611 AvahiPublishFlags flags, 612 uint32_t ttl, 613 const char *name, 614 const char *dest) { 615 616 AvahiEntry *e; 617 618 assert(s); 619 620 if (!(e = server_add_ptr_internal(s, g, interface, protocol, flags, ttl, name, dest))) 621 return avahi_server_errno(s); 622 623 return AVAHI_OK; 624} 625 626int avahi_server_add_cname( 627 AvahiServer *s, 628 AvahiSEntryGroup *g, 629 AvahiIfIndex interface, 630 AvahiProtocol protocol, 631 AvahiPublishFlags flags, 632 uint32_t ttl, 633 const char *name) { 634 635 AvahiRecord *r; 636 AvahiEntry *e; 637 char *fq; 638 639 assert(s); 640 assert(s->domain_name); 641 assert(name); 642 643 AVAHI_CHECK_VALIDITY(s, !name || avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME); 644 645 if (!(fq = avahi_strdup_printf("%s.%s", name, s->domain_name))) 646 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 647 648 if (!(r = avahi_record_new_full(fq, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME, ttl))) 649 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 650 651 avahi_free(fq); 652 653 r->data.cname.name = avahi_normalize_name_strdup(s->host_name_fqdn); 654 e= server_add_internal(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r); 655 avahi_record_unref(r); 656 657 if (!e) 658 return avahi_server_errno(s); 659 660 return AVAHI_OK; 661} 662 663int avahi_server_add_llmnr_cname( 664 AvahiServer *s, 665 AvahiSEntryGroup *g, 666 AvahiIfIndex interface, 667 AvahiProtocol protocol, 668 AvahiPublishFlags flags, 669 uint32_t ttl, 670 const char *name) { 671 672 AvahiRecord *r; 673 AvahiEntry *e; 674 char *fq; 675 676 assert(s); 677 assert(s->domain_name); 678 assert(name); 679 680 AVAHI_CHECK_VALIDITY(s, !name || avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME); 681 682 if (!(fq = avahi_strdup_printf("%s", name))) 683 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 684 685 if (!(r = avahi_record_new_full(fq, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME, ttl))) 686 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 687 688 avahi_free(fq); 689 690 r->data.cname.name = avahi_normalize_name_strdup(s->host_name_fqdn); 691 e= server_add_internal(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r); 692 avahi_record_unref(r); 693 694 if (!e) 695 return avahi_server_errno(s); 696 697 return AVAHI_OK; 698} 699 700 701static int server_add_address_internal ( 702 AvahiServer *s, 703 AvahiSEntryGroup *g, 704 AvahiIfIndex interface, 705 AvahiProtocol protocol, 706 AvahiPublishFlags flags, 707 const char *name, 708 AvahiAddress *a, 709 uint32_t ttl) { 710 711 AvahiRecord *r; 712 int ret = AVAHI_OK; 713 AvahiEntry *reverse = NULL, *entry = NULL; 714 715 assert(s); 716 assert(a); 717 assert(name); 718 719 /* Create the A/AAAA record */ 720 if (a->proto == AVAHI_PROTO_INET) { 721 722 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, ttl))) { 723 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 724 goto finish; 725 } 726 727 r->data.a.address = a->data.ipv4; 728 729 } else { 730 assert(a->proto == AVAHI_PROTO_INET6); 731 732 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, ttl))) { 733 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 734 goto finish; 735 } 736 737 r->data.aaaa.address = a->data.ipv6; 738 } 739 740 entry = server_add_internal(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r); 741 avahi_record_unref(r); 742 743 if (!entry) { 744 ret = avahi_server_errno(s); 745 goto finish; 746 } 747 748 /* Create the reverse lookup entry */ 749 750 if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) { 751 char reverse_n[AVAHI_DOMAIN_NAME_MAX]; 752 avahi_reverse_lookup_name(a, reverse_n, sizeof(reverse_n)); 753 754 if (!(reverse = server_add_ptr_internal(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, ttl, reverse_n, name))) { 755 ret = avahi_server_errno(s); 756 goto finish; 757 } 758 } 759 760finish: 761 762 if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) { 763 if (entry) 764 avahi_entry_free(s, entry); 765 if (reverse) 766 avahi_entry_free(s, reverse); 767 } 768 769 return ret; 770} 771 772 773int avahi_server_add_address( 774 AvahiServer *s, 775 AvahiSEntryGroup *g, 776 AvahiIfIndex interface, 777 AvahiProtocol protocol, 778 AvahiPublishFlags flags, 779 const char *name, 780 AvahiAddress *a) { 781 782 char n[AVAHI_DOMAIN_NAME_MAX]; 783 int ret_fqdn, null_name = 0; 784 785 assert(s); 786 assert(a); 787 /*assert(name || (flags & (AVAHI_PUBLISH_USE_MULTICAST | AVAHI_PUBLISH_USE_LLMNR | AVAHI_PUBLISH_USE_WIDE_AREA));*/ 788 789 AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); 790 AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(a->proto), AVAHI_ERR_INVALID_PROTOCOL); 791 AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags, 792 AVAHI_PUBLISH_NO_REVERSE| 793 AVAHI_PUBLISH_NO_ANNOUNCE| 794 AVAHI_PUBLISH_NO_PROBE| 795 AVAHI_PUBLISH_NO_VERIFY| 796 AVAHI_PUBLISH_UPDATE| 797 AVAHI_PUBLISH_USE_WIDE_AREA| 798 AVAHI_PUBLISH_USE_MULTICAST| 799 AVAHI_PUBLISH_USE_LLMNR), AVAHI_ERR_INVALID_FLAGS); 800 801 AVAHI_CHECK_VALIDITY(s, 802 !name || 803 ((flags & AVAHI_PUBLISH_USE_MULTICAST) ? (avahi_is_valid_fqdn(name)) : (avahi_is_valid_domain_name(name))), 804 AVAHI_ERR_INVALID_HOST_NAME); 805 806 /* Prepare the host name */ 807 if (!name) { 808 name = s->host_name_fqdn; 809 null_name = 1; 810 } else { 811 AVAHI_ASSERT_TRUE(avahi_normalize_name(name, n, sizeof(n))); 812 name = n; 813 } 814 815 /* transport flags */ 816 transport_flags_from_domain(s, &flags, name, 1); 817 AVAHI_CHECK_VALIDITY(s, (flags & (AVAHI_PUBLISH_USE_MULTICAST | AVAHI_PUBLISH_USE_LLMNR)), AVAHI_ERR_NOT_SUPPORTED); 818 819 /* No matter mDNS or LLMNR */ 820 if (flags & AVAHI_PUBLISH_USE_MULTICAST) 821 ret_fqdn = server_add_address_internal(s, g, interface, protocol, flags, name, a, AVAHI_DEFAULT_TTL_HOST_NAME); 822 else 823 ret_fqdn = server_add_address_internal(s, g, 824 interface, 825 protocol, 826 (null_name == 1) ? (flags |= AVAHI_PUBLISH_NO_REVERSE) : (flags), 827 name, a, 828 AVAHI_DEFAULT_LLMNR_TTL_HOST_NAME); 829 830 /* If previous entries have been added successfully && name parameter was NULL && we are using LLMNR, 831 publish hostname -> A/AAAA entry and PTR entry*/ 832 if (ret_fqdn == AVAHI_OK && null_name == 1 && flags & AVAHI_PUBLISH_USE_LLMNR ) 833 return server_add_address_internal(s, g, interface, protocol, flags & ~ AVAHI_PUBLISH_NO_REVERSE, s->host_name, a, AVAHI_DEFAULT_LLMNR_TTL_HOST_NAME); 834 835 return ret_fqdn; 836} 837 838static AvahiEntry *server_add_txt_strlst_nocopy( 839 AvahiServer *s, 840 AvahiSEntryGroup *g, 841 AvahiIfIndex interface, 842 AvahiProtocol protocol, 843 AvahiPublishFlags flags, 844 uint32_t ttl, 845 const char *name, 846 AvahiStringList *strlst) { 847 848 AvahiRecord *r; 849 AvahiEntry *e; 850 851 assert(s); 852 853 if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl))) { 854 avahi_string_list_free(strlst); 855 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 856 return NULL; 857 } 858 859 r->data.txt.string_list = strlst; 860 e = server_add_internal(s, g, interface, protocol, flags, r); 861 avahi_record_unref(r); 862 863 return e; 864} 865 866static AvahiStringList *add_magic_cookie( 867 AvahiServer *s, 868 AvahiStringList *strlst) { 869 870 assert(s); 871 872 if (!s->config.add_service_cookie) 873 return strlst; 874 875 if (avahi_string_list_find(strlst, AVAHI_SERVICE_COOKIE)) 876 /* This string list already contains a magic cookie */ 877 return strlst; 878 879 return avahi_string_list_add_printf(strlst, AVAHI_SERVICE_COOKIE"=%u", s->local_service_cookie); 880} 881 882static int server_add_service_strlst_nocopy( 883 AvahiServer *s, 884 AvahiSEntryGroup *g, 885 AvahiIfIndex interface, 886 AvahiProtocol protocol, 887 AvahiPublishFlags flags, 888 const char *name, 889 const char *type, 890 const char *domain, 891 const char *host, 892 uint16_t port, 893 AvahiStringList *strlst) { 894 895 char ptr_name[AVAHI_DOMAIN_NAME_MAX], svc_name[AVAHI_DOMAIN_NAME_MAX], enum_ptr[AVAHI_DOMAIN_NAME_MAX], *h = NULL; 896 AvahiRecord *r = NULL; 897 int ret = AVAHI_OK; 898 AvahiEntry *srv_entry = NULL, *txt_entry = NULL, *ptr_entry = NULL, *enum_entry = NULL; 899 900 assert(s); 901 assert(type); 902 assert(name); 903 904 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); 905 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); 906 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, 907 AVAHI_PUBLISH_NO_COOKIE| 908 AVAHI_PUBLISH_UPDATE| 909 AVAHI_PUBLISH_USE_WIDE_AREA| 910 AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); 911 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); 912 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); 913 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); 914 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !host || avahi_is_valid_fqdn(host), AVAHI_ERR_INVALID_HOST_NAME); 915 916 if (!domain) 917 domain = s->domain_name; 918 919 if (!host) 920 host = s->host_name_fqdn; 921 922 transport_flags_from_domain(s, &flags, domain, 0); 923 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); 924 925 if (!(h = avahi_normalize_name_strdup(host))) { 926 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 927 goto fail; 928 } 929 930 if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 || 931 (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, type, domain)) < 0 || 932 (ret = avahi_service_name_join(enum_ptr, sizeof(enum_ptr), NULL, "_services._dns-sd._udp", domain)) < 0) { 933 avahi_server_set_errno(s, ret); 934 goto fail; 935 } 936 937 /* Add service enumeration PTR record */ 938 939 if (!(ptr_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name))) { 940 ret = avahi_server_errno(s); 941 goto fail; 942 } 943 944 /* Add SRV record */ 945 946 if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) { 947 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 948 goto fail; 949 } 950 951 r->data.srv.priority = 0; 952 r->data.srv.weight = 0; 953 r->data.srv.port = port; 954 r->data.srv.name = h; 955 h = NULL; 956 srv_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, r); 957 avahi_record_unref(r); 958 959 if (!srv_entry) { 960 ret = avahi_server_errno(s); 961 goto fail; 962 } 963 964 /* Add TXT record */ 965 966 if (!(flags & AVAHI_PUBLISH_NO_COOKIE)) 967 strlst = add_magic_cookie(s, strlst); 968 969 txt_entry = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst); 970 strlst = NULL; 971 972 if (!txt_entry) { 973 ret = avahi_server_errno(s); 974 goto fail; 975 } 976 977 /* Add service type enumeration record */ 978 979 if (!(enum_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name))) { 980 ret = avahi_server_errno(s); 981 goto fail; 982 } 983 984fail: 985 if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) { 986 if (srv_entry) 987 avahi_entry_free(s, srv_entry); 988 if (txt_entry) 989 avahi_entry_free(s, txt_entry); 990 if (ptr_entry) 991 avahi_entry_free(s, ptr_entry); 992 if (enum_entry) 993 avahi_entry_free(s, enum_entry); 994 } 995 996 avahi_string_list_free(strlst); 997 avahi_free(h); 998 999 return ret; 1000} 1001 1002int avahi_server_add_service_strlst( 1003 AvahiServer *s, 1004 AvahiSEntryGroup *g, 1005 AvahiIfIndex interface, 1006 AvahiProtocol protocol, 1007 AvahiPublishFlags flags, 1008 const char *name, 1009 const char *type, 1010 const char *domain, 1011 const char *host, 1012 uint16_t port, 1013 AvahiStringList *strlst) { 1014 1015 assert(s); 1016 assert(type); 1017 assert(name); 1018 1019 return server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_copy(strlst)); 1020} 1021 1022int avahi_server_add_service( 1023 AvahiServer *s, 1024 AvahiSEntryGroup *g, 1025 AvahiIfIndex interface, 1026 AvahiProtocol protocol, 1027 AvahiPublishFlags flags, 1028 const char *name, 1029 const char *type, 1030 const char *domain, 1031 const char *host, 1032 uint16_t port, 1033 ... ){ 1034 1035 va_list va; 1036 int ret; 1037 1038 va_start(va, port); 1039 ret = server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_new_va(va)); 1040 va_end(va); 1041 1042 return ret; 1043} 1044 1045static int server_update_service_txt_strlst_nocopy( 1046 AvahiServer *s, 1047 AvahiSEntryGroup *g, 1048 AvahiIfIndex interface, 1049 AvahiProtocol protocol, 1050 AvahiPublishFlags flags, 1051 const char *name, 1052 const char *type, 1053 const char *domain, 1054 AvahiStringList *strlst) { 1055 1056 char svc_name[AVAHI_DOMAIN_NAME_MAX]; 1057 int ret = AVAHI_OK; 1058 AvahiEntry *e; 1059 1060 assert(s); 1061 assert(type); 1062 assert(name); 1063 assert (flags & AVAHI_PUBLISH_USE_MULTICAST || flags & AVAHI_PUBLISH_USE_WIDE_AREA); 1064 1065 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); 1066 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); 1067 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, 1068 AVAHI_PUBLISH_NO_COOKIE| 1069 AVAHI_PUBLISH_USE_WIDE_AREA| 1070 AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); 1071 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); 1072 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); 1073 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); 1074 1075 if (!domain) 1076 domain = s->domain_name; 1077 1078 transport_flags_from_domain(s, &flags, domain, 0); 1079 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); 1080 1081 if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0) { 1082 avahi_server_set_errno(s, ret); 1083 goto fail; 1084 } 1085 1086 /* Add TXT record */ 1087 if (!(flags & AVAHI_PUBLISH_NO_COOKIE)) 1088 strlst = add_magic_cookie(s, strlst); 1089 1090 e = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_UPDATE, AVAHI_DEFAULT_TTL, svc_name, strlst); 1091 strlst = NULL; 1092 1093 if (!e) 1094 ret = avahi_server_errno(s); 1095 1096fail: 1097 1098 avahi_string_list_free(strlst); 1099 1100 return ret; 1101} 1102 1103int avahi_server_update_service_txt_strlst( 1104 AvahiServer *s, 1105 AvahiSEntryGroup *g, 1106 AvahiIfIndex interface, 1107 AvahiProtocol protocol, 1108 AvahiPublishFlags flags, 1109 const char *name, 1110 const char *type, 1111 const char *domain, 1112 AvahiStringList *strlst) { 1113 1114 return server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_copy(strlst)); 1115} 1116 1117/** Update the TXT record for a service with the NULL termonate list of strings */ 1118int avahi_server_update_service_txt( 1119 AvahiServer *s, 1120 AvahiSEntryGroup *g, 1121 AvahiIfIndex interface, 1122 AvahiProtocol protocol, 1123 AvahiPublishFlags flags, 1124 const char *name, 1125 const char *type, 1126 const char *domain, 1127 ...) { 1128 1129 va_list va; 1130 int ret; 1131 1132 va_start(va, domain); 1133 ret = server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_new_va(va)); 1134 va_end(va); 1135 1136 return ret; 1137} 1138 1139int avahi_server_add_service_subtype( 1140 AvahiServer *s, 1141 AvahiSEntryGroup *g, 1142 AvahiIfIndex interface, 1143 AvahiProtocol protocol, 1144 AvahiPublishFlags flags, 1145 const char *name, 1146 const char *type, 1147 const char *domain, 1148 const char *subtype) { 1149 1150 int ret = AVAHI_OK; 1151 char svc_name[AVAHI_DOMAIN_NAME_MAX], ptr_name[AVAHI_DOMAIN_NAME_MAX]; 1152 1153 assert(name); 1154 assert(type); 1155 assert(subtype); 1156 assert (flags & AVAHI_PUBLISH_USE_MULTICAST || flags & AVAHI_PUBLISH_USE_WIDE_AREA); 1157 1158 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); 1159 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); 1160 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); 1161 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME); 1162 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE); 1163 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); 1164 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_subtype(subtype), AVAHI_ERR_INVALID_SERVICE_SUBTYPE); 1165 1166 if (!domain) 1167 domain = s->domain_name; 1168 1169 transport_flags_from_domain(s, &flags, domain, 0); 1170 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); 1171 1172 if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 || 1173 (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, subtype, domain)) < 0) { 1174 avahi_server_set_errno(s, ret); 1175 goto fail; 1176 } 1177 1178 if ((ret = avahi_server_add_ptr(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0) 1179 goto fail; 1180 1181fail: 1182 1183 return ret; 1184} 1185 1186static void hexstring(char *s, size_t sl, const void *p, size_t pl) { 1187 static const char hex[] = "0123456789abcdef"; 1188 int b = 0; 1189 const uint8_t *k = p; 1190 1191 while (sl > 1 && pl > 0) { 1192 *(s++) = hex[(b ? *k : *k >> 4) & 0xF]; 1193 1194 if (b) { 1195 k++; 1196 pl--; 1197 } 1198 1199 b = !b; 1200 1201 sl--; 1202 } 1203 1204 if (sl > 0) 1205 *s = 0; 1206} 1207 1208static AvahiEntry *server_add_dns_server_name( 1209 AvahiServer *s, 1210 AvahiSEntryGroup *g, 1211 AvahiIfIndex interface, 1212 AvahiProtocol protocol, 1213 AvahiPublishFlags flags, 1214 const char *domain, 1215 AvahiDNSServerType type, 1216 const char *name, 1217 uint16_t port /** should be 53 */) { 1218 1219 AvahiEntry *e; 1220 char t[AVAHI_DOMAIN_NAME_MAX], normalized_d[AVAHI_DOMAIN_NAME_MAX], *n; 1221 1222 AvahiRecord *r; 1223 1224 assert(s); 1225 assert(name); 1226 assert (flags & AVAHI_PUBLISH_USE_MULTICAST || flags & AVAHI_PUBLISH_USE_WIDE_AREA); 1227 1228 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_WIDE_AREA|AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); 1229 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS); 1230 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, port != 0, AVAHI_ERR_INVALID_PORT); 1231 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_fqdn(name), AVAHI_ERR_INVALID_HOST_NAME); 1232 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); 1233 1234 if (!domain) 1235 domain = s->domain_name; 1236 1237 transport_flags_from_domain(s, &flags, domain, 0); 1238 AVAHI_CHECK_VALIDITY_RETURN_NULL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); 1239 1240 if (!(n = avahi_normalize_name_strdup(name))) { 1241 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 1242 return NULL; 1243 } 1244 1245 AVAHI_ASSERT_TRUE(avahi_normalize_name(domain, normalized_d, sizeof(normalized_d))); 1246 1247 snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", normalized_d); 1248 1249 if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) { 1250 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 1251 avahi_free(n); 1252 return NULL; 1253 } 1254 1255 r->data.srv.priority = 0; 1256 r->data.srv.weight = 0; 1257 r->data.srv.port = port; 1258 r->data.srv.name = n; 1259 e = server_add_internal(s, g, interface, protocol, 0, r); 1260 avahi_record_unref(r); 1261 1262 return e; 1263} 1264 1265int avahi_server_add_dns_server_address( 1266 AvahiServer *s, 1267 AvahiSEntryGroup *g, 1268 AvahiIfIndex interface, 1269 AvahiProtocol protocol, 1270 AvahiPublishFlags flags, 1271 const char *domain, 1272 AvahiDNSServerType type, 1273 const AvahiAddress *address, 1274 uint16_t port /** should be 53 */) { 1275 1276 AvahiRecord *r; 1277 char n[64], h[64]; 1278 AvahiEntry *a_entry, *s_entry; 1279 1280 assert(s); 1281 assert(address); 1282 assert (flags & AVAHI_PUBLISH_USE_MULTICAST || flags & AVAHI_PUBLISH_USE_WIDE_AREA); 1283 1284 AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); 1285 AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(address->proto), AVAHI_ERR_INVALID_PROTOCOL); 1286 AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA), AVAHI_ERR_INVALID_FLAGS); 1287 AVAHI_CHECK_VALIDITY(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS); 1288 AVAHI_CHECK_VALIDITY(s, port != 0, AVAHI_ERR_INVALID_PORT); 1289 AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); 1290 1291 if (!domain) 1292 domain = s->domain_name; 1293 1294 transport_flags_from_domain(s, &flags, domain, 0); 1295 AVAHI_CHECK_VALIDITY(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); 1296 1297 if (address->proto == AVAHI_PROTO_INET) { 1298 hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv4Address)); 1299 snprintf(n, sizeof(n), "ip-%s.%s", h, domain); 1300 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME); 1301 r->data.a.address = address->data.ipv4; 1302 } else { 1303 hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv6Address)); 1304 snprintf(n, sizeof(n), "ip6-%s.%s", h, domain); 1305 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME); 1306 r->data.aaaa.address = address->data.ipv6; 1307 } 1308 1309 if (!r) 1310 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 1311 1312 a_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r); 1313 avahi_record_unref(r); 1314 1315 if (!a_entry) 1316 return avahi_server_errno(s); 1317 1318 if (!(s_entry = server_add_dns_server_name(s, g, interface, protocol, flags, domain, type, n, port))) { 1319 if (!(flags & AVAHI_PUBLISH_UPDATE)) 1320 avahi_entry_free(s, a_entry); 1321 return avahi_server_errno(s); 1322 } 1323 1324 return AVAHI_OK; 1325} 1326 1327void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) { 1328 assert(g); 1329 assert(g->type != AVAHI_GROUP_UNSET); 1330 1331 if (g->state == state) 1332 return; 1333 1334 assert(state <= AVAHI_ENTRY_GROUP_COLLISION); 1335 1336 if (g->state == AVAHI_ENTRY_GROUP_ESTABLISHED && g->type == AVAHI_GROUP_MDNS) { 1337 1338 /* If the entry group was established for a time longer then 1339 * 5s, reset the establishment trial counter */ 1340 1341 if (avahi_age(&g->proto.mdns.established_at) > 5000000) 1342 g->proto.mdns.n_register_try = 0; 1343 } else if (g->state == AVAHI_ENTRY_GROUP_REGISTERING && g->type == AVAHI_GROUP_MDNS) { 1344 if (g->proto.mdns.register_time_event) { 1345 avahi_time_event_free(g->proto.mdns.register_time_event); 1346 g->proto.mdns.register_time_event = NULL; 1347 } 1348 } 1349 1350 if (state == AVAHI_ENTRY_GROUP_ESTABLISHED && g->type == AVAHI_GROUP_MDNS) 1351 1352 /* If the entry group is now established, remember the time 1353 * this happened */ 1354 1355 gettimeofday(&g->proto.mdns.established_at, NULL); 1356 1357 g->state = state; 1358 1359 if (g->callback) 1360 g->callback(g->server, g, state, g->userdata); 1361} 1362 1363AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) { 1364 AvahiSEntryGroup *g; 1365 1366 assert(s); 1367 1368 if (!(g = avahi_new(AvahiSEntryGroup, 1))) { 1369 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); 1370 return NULL; 1371 } 1372 1373 g->server = s; 1374 g->callback = callback; 1375 g->userdata = userdata; 1376 g->dead = 0; 1377 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED; 1378 g->type = AVAHI_GROUP_UNSET; 1379 1380 return g; 1381} 1382 1383static void cleanup_time_event_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* userdata) { 1384 AvahiServer *s = userdata; 1385 1386 assert(s); 1387 1388 avahi_cleanup_dead_entries(s); 1389} 1390 1391static void schedule_cleanup(AvahiServer *s) { 1392 struct timeval tv; 1393 1394 assert(s); 1395 1396 if (!s->cleanup_time_event) 1397 s->cleanup_time_event = avahi_time_event_new(s->time_event_queue, avahi_elapse_time(&tv, 1000, 0), &cleanup_time_event_callback, s); 1398} 1399 1400void avahi_s_entry_group_free(AvahiSEntryGroup *g) { 1401 AvahiEntry *e; 1402 1403 assert(g); 1404 assert(g->server); 1405 1406 if (g->type == AVAHI_GROUP_UNSET) { 1407 avahi_free(g); 1408 1409 } else if (g->type == AVAHI_GROUP_MDNS) { 1410 for (e = g->entries; e; e = e->by_group_next) { 1411 assert(e->type == AVAHI_ENTRY_MDNS); 1412 1413 if (!e->dead) { 1414 avahi_goodbye_entry(g->server, e, 1, 1); 1415 e->dead = 1; 1416 } 1417 } 1418 1419 if (g->proto.mdns.register_time_event) { 1420 avahi_time_event_free(g->proto.mdns.register_time_event); 1421 g->proto.mdns.register_time_event = NULL; 1422 } 1423 1424 g->server->mdns.need_group_cleanup = 1; 1425 g->server->mdns.need_entry_cleanup = 1; 1426 1427 } else { 1428 assert (g->type == AVAHI_GROUP_LLMNR); 1429 for (e = g->entries; e; e = e->by_group_next) { 1430 assert(e->type == AVAHI_ENTRY_LLMNR); 1431 1432 if (!e->dead) { 1433 avahi_remove_verifiers(g->server, e); 1434 e->dead = 1; 1435 } 1436 } 1437 1438 g->server->llmnr.need_entry_cleanup = 1; 1439 g->server->llmnr.need_group_cleanup = 1; 1440 } 1441 1442 g->dead = 1; 1443 1444 schedule_cleanup(g->server); 1445} 1446 1447static void entry_group_commit_real(AvahiSEntryGroup *g) { 1448 assert(g); 1449 assert(g->type == AVAHI_GROUP_MDNS); 1450 1451 gettimeofday(&g->proto.mdns.register_time, NULL); 1452 1453 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING); 1454 1455 if (g->dead) 1456 return; 1457 1458 avahi_announce_group(g->server, g); 1459 avahi_s_entry_group_check_probed(g, 0); 1460} 1461 1462static void entry_group_register_time_event_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* userdata) { 1463 AvahiSEntryGroup *g = userdata; 1464 assert(g); 1465 assert(g->type == AVAHI_GROUP_MDNS); 1466 1467 avahi_time_event_free(g->proto.mdns.register_time_event); 1468 g->proto.mdns.register_time_event = NULL; 1469 1470 /* Holdoff time passed, so let's start probing */ 1471 entry_group_commit_real(g); 1472} 1473 1474int avahi_s_entry_group_commit(AvahiSEntryGroup *g) { 1475 struct timeval now; 1476 1477 assert(g); 1478 assert(!g->dead); 1479 1480 if (g->type == AVAHI_GROUP_UNSET) 1481 /* Group is empty */ 1482 return avahi_server_set_errno(g->server, AVAHI_ERR_IS_EMPTY); 1483 1484 if (g->type == AVAHI_GROUP_MDNS) { 1485 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION) 1486 return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE); 1487 1488/* if (avahi_s_entry_group_is_empty(g)) 1489 return avahi_server_set_errno(g->server, AVAHI_ERR_IS_EMPTY);*/ 1490 1491 g->proto.mdns.n_register_try++; 1492 1493 avahi_timeval_add(&g->proto.mdns.register_time, 1494 1000*(g->proto.mdns.n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ? 1495 AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT : 1496 AVAHI_RR_HOLDOFF_MSEC)); 1497 1498 gettimeofday(&now, NULL); 1499 1500 if (avahi_timeval_compare(&g->proto.mdns.register_time, &now) <= 0) { 1501 1502 /* Holdoff time passed, so let's start probing */ 1503 entry_group_commit_real(g); 1504 } else { 1505 1506 /* Holdoff time has not yet passed, so let's wait */ 1507 assert(!g->proto.mdns.register_time_event); 1508 g->proto.mdns.register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->proto.mdns.register_time, entry_group_register_time_event_callback, g); 1509 1510 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING); 1511 } 1512 1513 } else { 1514 assert(g->type == AVAHI_GROUP_LLMNR); 1515 1516 if (g->state != AVAHI_ENTRY_GROUP_LLMNR_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_LLMNR_COLLISION) 1517 return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE); 1518 1519/* if (avahi_s_entry_group_is_empty(g)) 1520 return avahi_server_set_errno(g->server, AVAHI_ERR_IS_EMPTY);*/ 1521 1522 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_LLMNR_VERIFYING); 1523 avahi_verify_group(g->server, g); 1524 } 1525 1526 return AVAHI_OK; 1527} 1528 1529void avahi_s_entry_group_reset(AvahiSEntryGroup *g) { 1530 AvahiEntry *e; 1531 assert(g); 1532 1533 if (g->type == AVAHI_GROUP_UNSET) 1534 return; 1535 1536 if (g->type == AVAHI_GROUP_MDNS) { 1537 1538 for (e = g->entries; e; e = e->by_group_next) { 1539 assert(e->type == AVAHI_ENTRY_MDNS); 1540 if (!e->dead) { 1541 avahi_goodbye_entry(g->server, e, 1, 1); 1542 e->dead = 1; 1543 } 1544 } 1545 g->server->mdns.need_entry_cleanup = 1; 1546 1547 g->proto.mdns.n_probing = 0; 1548 1549 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED); 1550 1551 } else { 1552 assert(g->type == AVAHI_GROUP_LLMNR); 1553 1554 for (e = g->entries; e; e = e->by_group_next) { 1555 assert(e->type == AVAHI_ENTRY_LLMNR); 1556 if (!e->dead) { 1557 avahi_remove_verifiers(g->server, e); 1558 e->dead = 1; 1559 } 1560 } 1561 g->server->llmnr.need_entry_cleanup = 1; 1562 1563 g->proto.llmnr.n_verifying = 0; 1564 1565 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_LLMNR_UNCOMMITED); 1566 } 1567 1568 schedule_cleanup(g->server); 1569} 1570 1571int avahi_entry_is_commited(AvahiEntry *e) { 1572 assert(e); 1573 assert(!e->dead); 1574 1575 if (e->type == AVAHI_ENTRY_MDNS) 1576 return !e->group || 1577 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING || 1578 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED; 1579 1580 else 1581 return (!e->group || 1582 e->group->state == AVAHI_ENTRY_GROUP_LLMNR_VERIFYING || 1583 e->group->state == AVAHI_ENTRY_GROUP_LLMNR_ESTABLISHED ); 1584} 1585 1586AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) { 1587 assert(g); 1588 assert(!g->dead); 1589 1590 return g->state; 1591} 1592 1593void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) { 1594 assert(g); 1595 1596 g->userdata = userdata; 1597} 1598 1599void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) { 1600 assert(g); 1601 1602 return g->userdata; 1603} 1604 1605int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) { 1606 AvahiEntry *e; 1607 assert(g); 1608 1609 if (g->type == AVAHI_GROUP_UNSET) 1610 /* There has not been any entry added in this group so far. */ 1611 return 1; 1612 1613 /* Look for an entry that is not dead */ 1614 for (e = g->entries; e; e = e->by_group_next) 1615 if (!e->dead) 1616 return 0; 1617 1618 return 1; 1619} 1620