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 <pthread.h> 27#include <assert.h> 28#include <unistd.h> 29#include <stdio.h> 30#include <errno.h> 31#include <string.h> 32#include <signal.h> 33#include <netinet/in.h> 34#include <fcntl.h> 35 36#include <sys/types.h> 37#include <sys/socket.h> 38 39#include <avahi-common/simple-watch.h> 40#include <avahi-common/malloc.h> 41#include <avahi-common/error.h> 42#include <avahi-common/domain.h> 43#include <avahi-common/alternative.h> 44 45#include <avahi-client/client.h> 46#include <avahi-client/publish.h> 47#include <avahi-client/lookup.h> 48 49#include "warn.h" 50#include "dns_sd.h" 51 52enum { 53 COMMAND_POLL = 'p', 54 COMMAND_QUIT = 'q', 55 COMMAND_POLL_DONE = 'P', 56 COMMAND_POLL_FAILED = 'F' 57}; 58 59struct type_info { 60 char *type; 61 AvahiStringList *subtypes; 62 int n_subtypes; 63}; 64 65struct _DNSServiceRef_t { 66 int n_ref; 67 68 AvahiSimplePoll *simple_poll; 69 70 int thread_fd, main_fd; 71 72 pthread_t thread; 73 int thread_running; 74 75 pthread_mutex_t mutex; 76 77 void *context; 78 DNSServiceBrowseReply service_browser_callback; 79 DNSServiceResolveReply service_resolver_callback; 80 DNSServiceDomainEnumReply domain_browser_callback; 81 DNSServiceRegisterReply service_register_callback; 82 83 AvahiClient *client; 84 AvahiServiceBrowser *service_browser; 85 AvahiServiceResolver *service_resolver; 86 AvahiDomainBrowser *domain_browser; 87 88 struct type_info type_info; 89 char *service_name, *service_name_chosen, *service_domain, *service_host; 90 uint16_t service_port; 91 AvahiIfIndex service_interface; 92 AvahiStringList *service_txt; 93 94 AvahiEntryGroup *entry_group; 95}; 96 97#define ASSERT_SUCCESS(r) { int __ret = (r); assert(__ret == 0); } 98 99static DNSServiceErrorType map_error(int error) { 100 switch (error) { 101 case AVAHI_OK : 102 return kDNSServiceErr_NoError; 103 104 case AVAHI_ERR_BAD_STATE : 105 return kDNSServiceErr_BadState; 106 107 case AVAHI_ERR_INVALID_HOST_NAME: 108 case AVAHI_ERR_INVALID_DOMAIN_NAME: 109 case AVAHI_ERR_INVALID_TTL: 110 case AVAHI_ERR_IS_PATTERN: 111 case AVAHI_ERR_INVALID_RECORD: 112 case AVAHI_ERR_INVALID_SERVICE_NAME: 113 case AVAHI_ERR_INVALID_SERVICE_TYPE: 114 case AVAHI_ERR_INVALID_PORT: 115 case AVAHI_ERR_INVALID_KEY: 116 case AVAHI_ERR_INVALID_ADDRESS: 117 case AVAHI_ERR_INVALID_SERVICE_SUBTYPE: 118 return kDNSServiceErr_BadParam; 119 120 121 case AVAHI_ERR_COLLISION: 122 return kDNSServiceErr_NameConflict; 123 124 case AVAHI_ERR_TOO_MANY_CLIENTS: 125 case AVAHI_ERR_TOO_MANY_OBJECTS: 126 case AVAHI_ERR_TOO_MANY_ENTRIES: 127 case AVAHI_ERR_ACCESS_DENIED: 128 return kDNSServiceErr_Refused; 129 130 case AVAHI_ERR_INVALID_OPERATION: 131 case AVAHI_ERR_INVALID_OBJECT: 132 return kDNSServiceErr_Invalid; 133 134 case AVAHI_ERR_NO_MEMORY: 135 return kDNSServiceErr_NoMemory; 136 137 case AVAHI_ERR_INVALID_INTERFACE: 138 case AVAHI_ERR_INVALID_PROTOCOL: 139 return kDNSServiceErr_BadInterfaceIndex; 140 141 case AVAHI_ERR_INVALID_FLAGS: 142 return kDNSServiceErr_BadFlags; 143 144 case AVAHI_ERR_NOT_FOUND: 145 return kDNSServiceErr_NoSuchName; 146 147 case AVAHI_ERR_VERSION_MISMATCH: 148 return kDNSServiceErr_Incompatible; 149 150 case AVAHI_ERR_NO_NETWORK: 151 case AVAHI_ERR_OS: 152 case AVAHI_ERR_INVALID_CONFIG: 153 case AVAHI_ERR_TIMEOUT: 154 case AVAHI_ERR_DBUS_ERROR: 155 case AVAHI_ERR_DISCONNECTED: 156 case AVAHI_ERR_NO_DAEMON: 157 break; 158 159 } 160 161 return kDNSServiceErr_Unknown; 162} 163 164static void type_info_init(struct type_info *i) { 165 assert(i); 166 i->type = NULL; 167 i->subtypes = NULL; 168 i->n_subtypes = 0; 169} 170 171static void type_info_free(struct type_info *i) { 172 assert(i); 173 174 avahi_free(i->type); 175 avahi_string_list_free(i->subtypes); 176 177 type_info_init(i); 178} 179 180static int type_info_parse(struct type_info *i, const char *t) { 181 char *token = NULL; 182 183 assert(i); 184 assert(t); 185 186 type_info_init(i); 187 188 for (;;) { 189 size_t l; 190 191 if (*t == 0) 192 break; 193 194 l = strcspn(t, ","); 195 196 if (l <= 0) 197 goto fail; 198 199 token = avahi_strndup(t, l); 200 201 if (!token) 202 goto fail; 203 204 if (!i->type) { 205 /* This is the first token, hence the main type */ 206 207 if (!avahi_is_valid_service_type_strict(token)) 208 goto fail; 209 210 i->type = token; 211 token = NULL; 212 } else { 213 char *fst; 214 215 /* This is not the first token, hence a subtype */ 216 217 if (!(fst = avahi_strdup_printf("%s._sub.%s", token, i->type))) 218 goto fail; 219 220 if (!avahi_is_valid_service_subtype(fst)) { 221 avahi_free(fst); 222 goto fail; 223 } 224 225 i->subtypes = avahi_string_list_add(i->subtypes, fst); 226 avahi_free(fst); 227 228 avahi_free(token); 229 token = NULL; 230 231 i->n_subtypes++; 232 } 233 234 t += l; 235 236 if (*t == ',') 237 t++; 238 } 239 240 if (i->type) 241 return 0; 242 243fail: 244 type_info_free(i); 245 avahi_free(token); 246 return -1; 247} 248 249static const char *add_trailing_dot(const char *s, char *buf, size_t buf_len) { 250 if (!s) 251 return NULL; 252 253 if (*s == 0) 254 return s; 255 256 if (s[strlen(s)-1] == '.') 257 return s; 258 259 snprintf(buf, buf_len, "%s.", s); 260 return buf; 261} 262 263static int read_command(int fd) { 264 ssize_t r; 265 char command; 266 267 assert(fd >= 0); 268 269 if ((r = read(fd, &command, 1)) != 1) { 270 fprintf(stderr, __FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF"); 271 return -1; 272 } 273 274 return command; 275} 276 277static int write_command(int fd, char reply) { 278 assert(fd >= 0); 279 280 if (write(fd, &reply, 1) != 1) { 281 fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno)); 282 return -1; 283 } 284 285 return 0; 286} 287 288static int poll_func(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) { 289 DNSServiceRef sdref = userdata; 290 int ret; 291 292 assert(sdref); 293 294 ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex)); 295 296/* fprintf(stderr, "pre-syscall\n"); */ 297 ret = poll(ufds, nfds, timeout); 298/* fprintf(stderr, "post-syscall\n"); */ 299 300 ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex)); 301 302 return ret; 303} 304 305static void * thread_func(void *data) { 306 DNSServiceRef sdref = data; 307 sigset_t mask; 308 309 sigfillset(&mask); 310 pthread_sigmask(SIG_BLOCK, &mask, NULL); 311 312 sdref->thread = pthread_self(); 313 sdref->thread_running = 1; 314 315 for (;;) { 316 char command; 317 318 if ((command = read_command(sdref->thread_fd)) < 0) 319 break; 320 321/* fprintf(stderr, "Command: %c\n", command); */ 322 323 switch (command) { 324 325 case COMMAND_POLL: { 326 int ret; 327 328 ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex)); 329 330 for (;;) { 331 errno = 0; 332 333 if ((ret = avahi_simple_poll_run(sdref->simple_poll)) < 0) { 334 335 if (errno == EINTR) 336 continue; 337 338 fprintf(stderr, __FILE__": avahi_simple_poll_run() failed: %s\n", strerror(errno)); 339 } 340 341 break; 342 } 343 344 ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex)); 345 346 if (write_command(sdref->thread_fd, ret < 0 ? COMMAND_POLL_FAILED : COMMAND_POLL_DONE) < 0) 347 break; 348 349 break; 350 } 351 352 case COMMAND_QUIT: 353 return NULL; 354 } 355 356 } 357 358 return NULL; 359} 360 361static DNSServiceRef sdref_new(void) { 362 int fd[2] = { -1, -1 }; 363 DNSServiceRef sdref = NULL; 364 pthread_mutexattr_t mutex_attr; 365 366 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) 367 goto fail; 368 369 if (!(sdref = avahi_new(struct _DNSServiceRef_t, 1))) 370 goto fail; 371 372 sdref->n_ref = 1; 373 sdref->thread_fd = fd[0]; 374 sdref->main_fd = fd[1]; 375 376 sdref->client = NULL; 377 sdref->service_browser = NULL; 378 sdref->service_resolver = NULL; 379 sdref->domain_browser = NULL; 380 sdref->entry_group = NULL; 381 382 sdref->service_name = sdref->service_name_chosen = sdref->service_domain = sdref->service_host = NULL; 383 sdref->service_txt = NULL; 384 385 type_info_init(&sdref->type_info); 386 387 ASSERT_SUCCESS(pthread_mutexattr_init(&mutex_attr)); 388 pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE); 389 ASSERT_SUCCESS(pthread_mutex_init(&sdref->mutex, &mutex_attr)); 390 391 sdref->thread_running = 0; 392 393 if (!(sdref->simple_poll = avahi_simple_poll_new())) 394 goto fail; 395 396 avahi_simple_poll_set_func(sdref->simple_poll, poll_func, sdref); 397 398 /* Start simple poll */ 399 if (avahi_simple_poll_prepare(sdref->simple_poll, -1) < 0) 400 goto fail; 401 402 /* Queue an initial POLL command for the thread */ 403 if (write_command(sdref->main_fd, COMMAND_POLL) < 0) 404 goto fail; 405 406 if (pthread_create(&sdref->thread, NULL, thread_func, sdref) != 0) 407 goto fail; 408 409 sdref->thread_running = 1; 410 411 return sdref; 412 413fail: 414 415 if (sdref) 416 DNSServiceRefDeallocate(sdref); 417 418 return NULL; 419} 420 421static void sdref_free(DNSServiceRef sdref) { 422 assert(sdref); 423 424 if (sdref->thread_running) { 425 ASSERT_SUCCESS(write_command(sdref->main_fd, COMMAND_QUIT)); 426 avahi_simple_poll_wakeup(sdref->simple_poll); 427 ASSERT_SUCCESS(pthread_join(sdref->thread, NULL)); 428 } 429 430 if (sdref->client) 431 avahi_client_free(sdref->client); 432 433 if (sdref->simple_poll) 434 avahi_simple_poll_free(sdref->simple_poll); 435 436 if (sdref->thread_fd >= 0) 437 close(sdref->thread_fd); 438 439 if (sdref->main_fd >= 0) 440 close(sdref->main_fd); 441 442 ASSERT_SUCCESS(pthread_mutex_destroy(&sdref->mutex)); 443 444 avahi_free(sdref->service_name); 445 avahi_free(sdref->service_name_chosen); 446 avahi_free(sdref->service_domain); 447 avahi_free(sdref->service_host); 448 449 type_info_free(&sdref->type_info); 450 451 avahi_string_list_free(sdref->service_txt); 452 453 avahi_free(sdref); 454} 455 456static void sdref_ref(DNSServiceRef sdref) { 457 assert(sdref); 458 assert(sdref->n_ref >= 1); 459 460 sdref->n_ref++; 461} 462 463static void sdref_unref(DNSServiceRef sdref) { 464 assert(sdref); 465 assert(sdref->n_ref >= 1); 466 467 if (--(sdref->n_ref) <= 0) 468 sdref_free(sdref); 469} 470 471int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdref) { 472 473 AVAHI_WARN_LINKAGE; 474 475 if (!sdref || sdref->n_ref <= 0) 476 return -1; 477 478 return sdref->main_fd; 479} 480 481DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdref) { 482 DNSServiceErrorType ret = kDNSServiceErr_Unknown; 483 484 AVAHI_WARN_LINKAGE; 485 486 if (!sdref || sdref->n_ref <= 0) 487 return kDNSServiceErr_BadParam; 488 489 sdref_ref(sdref); 490 491 ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex)); 492 493 /* Cleanup notification socket */ 494 if (read_command(sdref->main_fd) != COMMAND_POLL_DONE) 495 goto finish; 496 497 if (avahi_simple_poll_dispatch(sdref->simple_poll) < 0) 498 goto finish; 499 500 if (sdref->n_ref > 1) /* Perhaps we should die */ 501 502 /* Dispatch events */ 503 if (avahi_simple_poll_prepare(sdref->simple_poll, -1) < 0) 504 goto finish; 505 506 if (sdref->n_ref > 1) 507 508 /* Request the poll */ 509 if (write_command(sdref->main_fd, COMMAND_POLL) < 0) 510 goto finish; 511 512 ret = kDNSServiceErr_NoError; 513 514finish: 515 516 ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex)); 517 518 sdref_unref(sdref); 519 520 return ret; 521} 522 523void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdref) { 524 AVAHI_WARN_LINKAGE; 525 526 if (sdref) 527 sdref_unref(sdref); 528} 529 530static void service_browser_callback( 531 AvahiServiceBrowser *b, 532 AvahiIfIndex interface, 533 AVAHI_GCC_UNUSED AvahiProtocol protocol, 534 AvahiBrowserEvent event, 535 const char *name, 536 const char *type, 537 const char *domain, 538 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, 539 void *userdata) { 540 541 DNSServiceRef sdref = userdata; 542 char type_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX]; 543 assert(b); 544 assert(sdref); 545 assert(sdref->n_ref >= 1); 546 547 type = add_trailing_dot(type, type_fixed, sizeof(type_fixed)); 548 domain = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed)); 549 550 switch (event) { 551 case AVAHI_BROWSER_NEW: 552 sdref->service_browser_callback(sdref, kDNSServiceFlagsAdd, interface, kDNSServiceErr_NoError, name, type, domain, sdref->context); 553 break; 554 555 case AVAHI_BROWSER_REMOVE: 556 sdref->service_browser_callback(sdref, 0, interface, kDNSServiceErr_NoError, name, type, domain, sdref->context); 557 break; 558 559 case AVAHI_BROWSER_FAILURE: 560 sdref->service_browser_callback(sdref, 0, interface, map_error(avahi_client_errno(sdref->client)), NULL, NULL, NULL, sdref->context); 561 break; 562 563 case AVAHI_BROWSER_CACHE_EXHAUSTED: 564 case AVAHI_BROWSER_ALL_FOR_NOW: 565 break; 566 } 567} 568 569static void generic_client_callback(AvahiClient *s, AvahiClientState state, void* userdata) { 570 DNSServiceRef sdref = userdata; 571 int error = kDNSServiceErr_Unknown; 572 573 assert(s); 574 assert(sdref); 575 assert(sdref->n_ref >= 1); 576 577 switch (state) { 578 579 case AVAHI_CLIENT_FAILURE: 580 581 if (sdref->service_browser_callback) 582 sdref->service_browser_callback(sdref, 0, 0, error, NULL, NULL, NULL, sdref->context); 583 else if (sdref->service_resolver_callback) 584 sdref->service_resolver_callback(sdref, 0, 0, error, NULL, NULL, 0, 0, NULL, sdref->context); 585 else if (sdref->domain_browser_callback) 586 sdref->domain_browser_callback(sdref, 0, 0, error, NULL, sdref->context); 587 588 break; 589 590 case AVAHI_CLIENT_S_RUNNING: 591 case AVAHI_CLIENT_S_COLLISION: 592 case AVAHI_CLIENT_S_REGISTERING: 593 case AVAHI_CLIENT_CONNECTING: 594 break; 595 } 596} 597 598DNSServiceErrorType DNSSD_API DNSServiceBrowse( 599 DNSServiceRef *ret_sdref, 600 DNSServiceFlags flags, 601 uint32_t interface, 602 const char *regtype, 603 const char *domain, 604 DNSServiceBrowseReply callback, 605 void *context) { 606 607 DNSServiceErrorType ret = kDNSServiceErr_Unknown; 608 int error; 609 DNSServiceRef sdref = NULL; 610 AvahiIfIndex ifindex; 611 struct type_info type_info; 612 613 AVAHI_WARN_LINKAGE; 614 615 if (!ret_sdref || !regtype) 616 return kDNSServiceErr_BadParam; 617 *ret_sdref = NULL; 618 619 if (interface == kDNSServiceInterfaceIndexLocalOnly || flags != 0) { 620 AVAHI_WARN_UNSUPPORTED; 621 return kDNSServiceErr_Unsupported; 622 } 623 624 type_info_init(&type_info); 625 626 if (type_info_parse(&type_info, regtype) < 0 || type_info.n_subtypes > 1) { 627 type_info_free(&type_info); 628 629 if (!avahi_is_valid_service_type_generic(regtype)) 630 return kDNSServiceErr_Unsupported; 631 } else 632 regtype = type_info.subtypes ? (char*) type_info.subtypes->text : type_info.type; 633 634 if (!(sdref = sdref_new())) { 635 type_info_free(&type_info); 636 return kDNSServiceErr_Unknown; 637 } 638 639 sdref->context = context; 640 sdref->service_browser_callback = callback; 641 642 ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex)); 643 644 if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), 0, generic_client_callback, sdref, &error))) { 645 ret = map_error(error); 646 goto finish; 647 } 648 649 ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface; 650 651 if (!(sdref->service_browser = avahi_service_browser_new(sdref->client, ifindex, AVAHI_PROTO_UNSPEC, regtype, domain, 0, service_browser_callback, sdref))) { 652 ret = map_error(avahi_client_errno(sdref->client)); 653 goto finish; 654 } 655 656 ret = kDNSServiceErr_NoError; 657 *ret_sdref = sdref; 658 659finish: 660 661 ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex)); 662 663 if (ret != kDNSServiceErr_NoError) 664 DNSServiceRefDeallocate(sdref); 665 666 type_info_free(&type_info); 667 668 return ret; 669} 670 671static void service_resolver_callback( 672 AvahiServiceResolver *r, 673 AvahiIfIndex interface, 674 AVAHI_GCC_UNUSED AvahiProtocol protocol, 675 AvahiResolverEvent event, 676 const char *name, 677 const char *type, 678 const char *domain, 679 const char *host_name, 680 AVAHI_GCC_UNUSED const AvahiAddress *a, 681 uint16_t port, 682 AvahiStringList *txt, 683 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, 684 void *userdata) { 685 686 DNSServiceRef sdref = userdata; 687 688 assert(r); 689 assert(sdref); 690 assert(sdref->n_ref >= 1); 691 692 switch (event) { 693 case AVAHI_RESOLVER_FOUND: { 694 695 char host_name_fixed[AVAHI_DOMAIN_NAME_MAX]; 696 char full_name[AVAHI_DOMAIN_NAME_MAX]; 697 int ret; 698 char *p = NULL; 699 size_t l = 0; 700 701 host_name = add_trailing_dot(host_name, host_name_fixed, sizeof(host_name_fixed)); 702 703 if ((p = avahi_new0(char, (l = avahi_string_list_serialize(txt, NULL, 0))+1))) 704 avahi_string_list_serialize(txt, p, l); 705 706 ret = avahi_service_name_join(full_name, sizeof(full_name), name, type, domain); 707 assert(ret == AVAHI_OK); 708 709 strcat(full_name, "."); 710 711 sdref->service_resolver_callback(sdref, 0, interface, kDNSServiceErr_NoError, full_name, host_name, htons(port), l, (unsigned char*) p, sdref->context); 712 713 avahi_free(p); 714 break; 715 } 716 717 case AVAHI_RESOLVER_FAILURE: 718 sdref->service_resolver_callback(sdref, 0, interface, map_error(avahi_client_errno(sdref->client)), NULL, NULL, 0, 0, NULL, sdref->context); 719 break; 720 } 721} 722 723DNSServiceErrorType DNSSD_API DNSServiceResolve( 724 DNSServiceRef *ret_sdref, 725 DNSServiceFlags flags, 726 uint32_t interface, 727 const char *name, 728 const char *regtype, 729 const char *domain, 730 DNSServiceResolveReply callback, 731 void *context) { 732 733 DNSServiceErrorType ret = kDNSServiceErr_Unknown; 734 int error; 735 DNSServiceRef sdref = NULL; 736 AvahiIfIndex ifindex; 737 738 AVAHI_WARN_LINKAGE; 739 740 if (!ret_sdref || !name || !regtype || !domain || !callback) 741 return kDNSServiceErr_BadParam; 742 *ret_sdref = NULL; 743 744 if (interface == kDNSServiceInterfaceIndexLocalOnly || flags != 0) { 745 AVAHI_WARN_UNSUPPORTED; 746 return kDNSServiceErr_Unsupported; 747 } 748 749 if (!(sdref = sdref_new())) 750 return kDNSServiceErr_Unknown; 751 752 sdref->context = context; 753 sdref->service_resolver_callback = callback; 754 755 ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex)); 756 757 if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), 0, generic_client_callback, sdref, &error))) { 758 ret = map_error(error); 759 goto finish; 760 } 761 762 ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface; 763 764 if (!(sdref->service_resolver = avahi_service_resolver_new(sdref->client, ifindex, AVAHI_PROTO_UNSPEC, name, regtype, domain, AVAHI_PROTO_UNSPEC, 0, service_resolver_callback, sdref))) { 765 ret = map_error(avahi_client_errno(sdref->client)); 766 goto finish; 767 } 768 769 770 ret = kDNSServiceErr_NoError; 771 *ret_sdref = sdref; 772 773finish: 774 775 ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex)); 776 777 if (ret != kDNSServiceErr_NoError) 778 DNSServiceRefDeallocate(sdref); 779 780 return ret; 781} 782 783int DNSSD_API DNSServiceConstructFullName ( 784 char *fullName, 785 const char *service, 786 const char *regtype, 787 const char *domain) { 788 789 AVAHI_WARN_LINKAGE; 790 791 if (!fullName || !regtype || !domain) 792 return -1; 793 794 if (avahi_service_name_join(fullName, kDNSServiceMaxDomainName, service, regtype, domain) < 0) 795 return -1; 796 797 return 0; 798} 799 800static void domain_browser_callback( 801 AvahiDomainBrowser *b, 802 AvahiIfIndex interface, 803 AVAHI_GCC_UNUSED AvahiProtocol protocol, 804 AvahiBrowserEvent event, 805 const char *domain, 806 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, 807 void *userdata) { 808 809 DNSServiceRef sdref = userdata; 810 static char domain_fixed[AVAHI_DOMAIN_NAME_MAX]; 811 812 assert(b); 813 assert(sdref); 814 assert(sdref->n_ref >= 1); 815 816 domain = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed)); 817 818 switch (event) { 819 case AVAHI_BROWSER_NEW: 820 sdref->domain_browser_callback(sdref, kDNSServiceFlagsAdd, interface, kDNSServiceErr_NoError, domain, sdref->context); 821 break; 822 823 case AVAHI_BROWSER_REMOVE: 824 sdref->domain_browser_callback(sdref, 0, interface, kDNSServiceErr_NoError, domain, sdref->context); 825 break; 826 827 case AVAHI_BROWSER_FAILURE: 828 sdref->domain_browser_callback(sdref, 0, interface, map_error(avahi_client_errno(sdref->client)), domain, sdref->context); 829 break; 830 831 case AVAHI_BROWSER_CACHE_EXHAUSTED: 832 case AVAHI_BROWSER_ALL_FOR_NOW: 833 break; 834 } 835} 836 837DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains( 838 DNSServiceRef *ret_sdref, 839 DNSServiceFlags flags, 840 uint32_t interface, 841 DNSServiceDomainEnumReply callback, 842 void *context) { 843 844 DNSServiceErrorType ret = kDNSServiceErr_Unknown; 845 int error; 846 DNSServiceRef sdref = NULL; 847 AvahiIfIndex ifindex; 848 849 AVAHI_WARN_LINKAGE; 850 851 if (!ret_sdref || !callback) 852 return kDNSServiceErr_BadParam; 853 *ret_sdref = NULL; 854 855 if (interface == kDNSServiceInterfaceIndexLocalOnly || 856 (flags != kDNSServiceFlagsBrowseDomains && flags != kDNSServiceFlagsRegistrationDomains)) { 857 AVAHI_WARN_UNSUPPORTED; 858 return kDNSServiceErr_Unsupported; 859 } 860 861 if (!(sdref = sdref_new())) 862 return kDNSServiceErr_Unknown; 863 864 sdref->context = context; 865 sdref->domain_browser_callback = callback; 866 867 ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex)); 868 869 if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), 0, generic_client_callback, sdref, &error))) { 870 ret = map_error(error); 871 goto finish; 872 } 873 874 ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface; 875 876 if (!(sdref->domain_browser = avahi_domain_browser_new(sdref->client, ifindex, AVAHI_PROTO_UNSPEC, "local", 877 flags == kDNSServiceFlagsRegistrationDomains ? AVAHI_DOMAIN_BROWSER_REGISTER : AVAHI_DOMAIN_BROWSER_BROWSE, 878 0, domain_browser_callback, sdref))) { 879 ret = map_error(avahi_client_errno(sdref->client)); 880 goto finish; 881 } 882 883 ret = kDNSServiceErr_NoError; 884 *ret_sdref = sdref; 885 886finish: 887 888 ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex)); 889 890 if (ret != kDNSServiceErr_NoError) 891 DNSServiceRefDeallocate(sdref); 892 893 return ret; 894} 895 896static void reg_report_error(DNSServiceRef sdref, DNSServiceErrorType error) { 897 char regtype_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX]; 898 const char *regtype, *domain; 899 assert(sdref); 900 assert(sdref->n_ref >= 1); 901 902 if (!sdref->service_register_callback) 903 return; 904 905 regtype = add_trailing_dot(sdref->type_info.type, regtype_fixed, sizeof(regtype_fixed)); 906 domain = add_trailing_dot(sdref->service_domain, domain_fixed, sizeof(domain_fixed)); 907 908 sdref->service_register_callback( 909 sdref, 0, error, 910 sdref->service_name_chosen ? sdref->service_name_chosen : sdref->service_name, 911 regtype, 912 domain, 913 sdref->context); 914} 915 916static int reg_create_service(DNSServiceRef sdref) { 917 int ret; 918 AvahiStringList *l; 919 920 assert(sdref); 921 assert(sdref->n_ref >= 1); 922 923 if ((ret = avahi_entry_group_add_service_strlst( 924 sdref->entry_group, 925 sdref->service_interface, 926 AVAHI_PROTO_UNSPEC, 927 0, 928 sdref->service_name_chosen, 929 sdref->type_info.type, 930 sdref->service_domain, 931 sdref->service_host, 932 sdref->service_port, 933 sdref->service_txt)) < 0) 934 return ret; 935 936 for (l = sdref->type_info.subtypes; l; l = l->next) { 937 /* Create a subtype entry */ 938 939 if (avahi_entry_group_add_service_subtype( 940 sdref->entry_group, 941 sdref->service_interface, 942 AVAHI_PROTO_UNSPEC, 943 0, 944 sdref->service_name_chosen, 945 sdref->type_info.type, 946 sdref->service_domain, 947 (const char*) l->text) < 0) 948 return ret; 949 } 950 951 if ((ret = avahi_entry_group_commit(sdref->entry_group)) < 0) 952 return ret; 953 954 return 0; 955} 956 957static void reg_client_callback(AvahiClient *s, AvahiClientState state, void* userdata) { 958 DNSServiceRef sdref = userdata; 959 960 assert(s); 961 assert(sdref); 962 assert(sdref->n_ref >= 1); 963 964 /* We've not been setup completely */ 965 if (!sdref->entry_group) 966 return; 967 968 switch (state) { 969 case AVAHI_CLIENT_FAILURE: 970 reg_report_error(sdref, kDNSServiceErr_Unknown); 971 break; 972 973 case AVAHI_CLIENT_S_RUNNING: { 974 int ret; 975 976 if (!sdref->service_name) { 977 const char *n; 978 /* If the service name is taken from the host name, copy that */ 979 980 avahi_free(sdref->service_name_chosen); 981 sdref->service_name_chosen = NULL; 982 983 if (!(n = avahi_client_get_host_name(sdref->client))) { 984 reg_report_error(sdref, map_error(avahi_client_errno(sdref->client))); 985 return; 986 } 987 988 if (!(sdref->service_name_chosen = avahi_strdup(n))) { 989 reg_report_error(sdref, kDNSServiceErr_NoMemory); 990 return; 991 } 992 } 993 994 if (!sdref->service_name_chosen) { 995 996 assert(sdref->service_name); 997 998 if (!(sdref->service_name_chosen = avahi_strdup(sdref->service_name))) { 999 reg_report_error(sdref, kDNSServiceErr_NoMemory); 1000 return; 1001 } 1002 } 1003 1004 /* Register the service */ 1005 1006 if ((ret = reg_create_service(sdref)) < 0) { 1007 reg_report_error(sdref, map_error(ret)); 1008 return; 1009 } 1010 1011 break; 1012 } 1013 1014 case AVAHI_CLIENT_S_COLLISION: 1015 case AVAHI_CLIENT_S_REGISTERING: 1016 1017 /* Remove our entry */ 1018 avahi_entry_group_reset(sdref->entry_group); 1019 1020 break; 1021 1022 case AVAHI_CLIENT_CONNECTING: 1023 /* Ignore */ 1024 break; 1025 } 1026 1027} 1028 1029static void reg_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { 1030 DNSServiceRef sdref = userdata; 1031 1032 assert(g); 1033 1034 switch (state) { 1035 case AVAHI_ENTRY_GROUP_ESTABLISHED: 1036 1037 /* Inform the user */ 1038 reg_report_error(sdref, kDNSServiceErr_NoError); 1039 1040 break; 1041 1042 case AVAHI_ENTRY_GROUP_COLLISION: { 1043 char *n; 1044 int ret; 1045 1046 /* Remove our entry */ 1047 avahi_entry_group_reset(sdref->entry_group); 1048 1049 assert(sdref->service_name_chosen); 1050 1051 /* Pick a new name */ 1052 if (!(n = avahi_alternative_service_name(sdref->service_name_chosen))) { 1053 reg_report_error(sdref, kDNSServiceErr_NoMemory); 1054 return; 1055 } 1056 avahi_free(sdref->service_name_chosen); 1057 sdref->service_name_chosen = n; 1058 1059 /* Register the service with that new name */ 1060 if ((ret = reg_create_service(sdref)) < 0) { 1061 reg_report_error(sdref, map_error(ret)); 1062 return; 1063 } 1064 1065 break; 1066 } 1067 1068 case AVAHI_ENTRY_GROUP_REGISTERING: 1069 case AVAHI_ENTRY_GROUP_UNCOMMITED: 1070 /* Ignore */ 1071 break; 1072 1073 case AVAHI_ENTRY_GROUP_FAILURE: 1074 /* Inform the user */ 1075 reg_report_error(sdref, map_error(avahi_client_errno(sdref->client))); 1076 break; 1077 1078 } 1079} 1080 1081DNSServiceErrorType DNSSD_API DNSServiceRegister ( 1082 DNSServiceRef *ret_sdref, 1083 DNSServiceFlags flags, 1084 uint32_t interface, 1085 const char *name, 1086 const char *regtype, 1087 const char *domain, 1088 const char *host, 1089 uint16_t port, 1090 uint16_t txtLen, 1091 const void *txtRecord, 1092 DNSServiceRegisterReply callback, 1093 void *context) { 1094 1095 DNSServiceErrorType ret = kDNSServiceErr_Unknown; 1096 int error; 1097 DNSServiceRef sdref = NULL; 1098 AvahiStringList *txt = NULL; 1099 struct type_info type_info; 1100 1101 AVAHI_WARN_LINKAGE; 1102 1103 if (!ret_sdref || !regtype) 1104 return kDNSServiceErr_BadParam; 1105 *ret_sdref = NULL; 1106 1107 if (!txtRecord) { 1108 txtLen = 1; 1109 txtRecord = ""; 1110 } 1111 1112 if (interface == kDNSServiceInterfaceIndexLocalOnly || flags) { 1113 AVAHI_WARN_UNSUPPORTED; 1114 return kDNSServiceErr_Unsupported; 1115 } 1116 1117 if (txtLen > 0) 1118 if (avahi_string_list_parse(txtRecord, txtLen, &txt) < 0) 1119 return kDNSServiceErr_Invalid; 1120 1121 if (type_info_parse(&type_info, regtype) < 0) { 1122 avahi_string_list_free(txt); 1123 return kDNSServiceErr_Invalid; 1124 } 1125 1126 if (!(sdref = sdref_new())) { 1127 avahi_string_list_free(txt); 1128 type_info_free(&type_info); 1129 return kDNSServiceErr_Unknown; 1130 } 1131 1132 sdref->context = context; 1133 sdref->service_register_callback = callback; 1134 1135 sdref->type_info = type_info; 1136 sdref->service_name = avahi_strdup(name); 1137 sdref->service_domain = domain ? avahi_normalize_name_strdup(domain) : NULL; 1138 sdref->service_host = host ? avahi_normalize_name_strdup(host) : NULL; 1139 sdref->service_interface = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface; 1140 sdref->service_port = ntohs(port); 1141 sdref->service_txt = txt; 1142 1143 /* Some OOM checking would be cool here */ 1144 1145 ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex)); 1146 1147 if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), 0, reg_client_callback, sdref, &error))) { 1148 ret = map_error(error); 1149 goto finish; 1150 } 1151 1152 if (!sdref->service_domain) { 1153 const char *d; 1154 1155 if (!(d = avahi_client_get_domain_name(sdref->client))) { 1156 ret = map_error(avahi_client_errno(sdref->client)); 1157 goto finish; 1158 } 1159 1160 if (!(sdref->service_domain = avahi_strdup(d))) { 1161 ret = kDNSServiceErr_NoMemory; 1162 goto finish; 1163 } 1164 } 1165 1166 if (!(sdref->entry_group = avahi_entry_group_new(sdref->client, reg_entry_group_callback, sdref))) { 1167 ret = map_error(avahi_client_errno(sdref->client)); 1168 goto finish; 1169 } 1170 1171 if (avahi_client_get_state(sdref->client) == AVAHI_CLIENT_S_RUNNING) { 1172 const char *n; 1173 1174 if (sdref->service_name) 1175 n = sdref->service_name; 1176 else { 1177 if (!(n = avahi_client_get_host_name(sdref->client))) { 1178 ret = map_error(avahi_client_errno(sdref->client)); 1179 goto finish; 1180 } 1181 } 1182 1183 if (!(sdref->service_name_chosen = avahi_strdup(n))) { 1184 ret = kDNSServiceErr_NoMemory; 1185 goto finish; 1186 } 1187 1188 1189 if ((error = reg_create_service(sdref)) < 0) { 1190 ret = map_error(error); 1191 goto finish; 1192 } 1193 } 1194 1195 ret = kDNSServiceErr_NoError; 1196 *ret_sdref = sdref; 1197 1198finish: 1199 1200 ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex)); 1201 1202 if (ret != kDNSServiceErr_NoError) 1203 DNSServiceRefDeallocate(sdref); 1204 1205 return ret; 1206} 1207 1208DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord( 1209 DNSServiceRef sdref, 1210 DNSRecordRef rref, 1211 DNSServiceFlags flags, 1212 uint16_t rdlen, 1213 const void *rdata, 1214 AVAHI_GCC_UNUSED uint32_t ttl) { 1215 1216 int ret = kDNSServiceErr_Unknown; 1217 AvahiStringList *txt = NULL; 1218 1219 AVAHI_WARN_LINKAGE; 1220 1221 if (!sdref || sdref->n_ref <= 0) 1222 return kDNSServiceErr_BadParam; 1223 1224 if (flags || rref) { 1225 AVAHI_WARN_UNSUPPORTED; 1226 return kDNSServiceErr_Unsupported; 1227 } 1228 1229 if (rdlen > 0) 1230 if (avahi_string_list_parse(rdata, rdlen, &txt) < 0) 1231 return kDNSServiceErr_Invalid; 1232 1233 ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex)); 1234 1235 if (!avahi_string_list_equal(txt, sdref->service_txt)) { 1236 1237 avahi_string_list_free(sdref->service_txt); 1238 sdref->service_txt = txt; 1239 1240 if (avahi_client_get_state(sdref->client) == AVAHI_CLIENT_S_RUNNING && 1241 sdref->entry_group && 1242 (avahi_entry_group_get_state(sdref->entry_group) == AVAHI_ENTRY_GROUP_ESTABLISHED || 1243 avahi_entry_group_get_state(sdref->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING)) 1244 1245 if (avahi_entry_group_update_service_txt_strlst( 1246 sdref->entry_group, 1247 sdref->service_interface, 1248 AVAHI_PROTO_UNSPEC, 1249 0, 1250 sdref->service_name_chosen, 1251 sdref->type_info.type, 1252 sdref->service_domain, 1253 sdref->service_txt) < 0) { 1254 1255 ret = map_error(avahi_client_errno(sdref->client)); 1256 goto finish; 1257 } 1258 1259 } else 1260 avahi_string_list_free(txt); 1261 1262 ret = kDNSServiceErr_NoError; 1263 1264finish: 1265 ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex)); 1266 1267 return ret; 1268} 1269 1270