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 <pthread.h> 25#include <assert.h> 26#include <unistd.h> 27#include <stdio.h> 28#include <errno.h> 29#include <string.h> 30#include <signal.h> 31#include <netinet/in.h> 32#include <fcntl.h> 33 34#include <sys/types.h> 35#include <sys/socket.h> 36 37#include <avahi-common/simple-watch.h> 38#include "avahi-common/avahi-malloc.h" 39#include <avahi-common/error.h> 40#include <avahi-common/domain.h> 41#include <avahi-common/alternative.h> 42 43#include <avahi-client/client.h> 44#include <avahi-client/publish.h> 45#include <avahi-client/lookup.h> 46 47#include "warn.h" 48#include "dns_sd.h" 49 50enum { 51 COMMAND_POLL = 'p', 52 COMMAND_QUIT = 'q', 53 COMMAND_POLL_DONE = 'P', 54 COMMAND_POLL_FAILED = 'F' 55}; 56 57struct type_info { 58 char *type; 59 AvahiStringList *subtypes; 60 int n_subtypes; 61}; 62 63struct _DNSServiceRef_t { 64 int n_ref; 65 66 AvahiSimplePoll *simple_poll; 67 68 int thread_fd, main_fd; 69 70 pthread_t thread; 71 int thread_running; 72 73 pthread_mutex_t mutex; 74 75 void *context; 76 DNSServiceBrowseReply service_browser_callback; 77 DNSServiceResolveReply service_resolver_callback; 78 DNSServiceDomainEnumReply domain_browser_callback; 79 DNSServiceRegisterReply service_register_callback; 80 DNSServiceQueryRecordReply query_resolver_callback; 81 82 AvahiClient *client; 83 AvahiServiceBrowser *service_browser; 84 AvahiServiceResolver *service_resolver; 85 AvahiDomainBrowser *domain_browser; 86 AvahiRecordBrowser *record_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 else if (sdref->query_resolver_callback) 588 sdref->query_resolver_callback(sdref, 0, 0, error, NULL, 0, 0, 0, NULL, 0, sdref->context); 589 590 break; 591 592 case AVAHI_CLIENT_S_RUNNING: 593 case AVAHI_CLIENT_S_COLLISION: 594 case AVAHI_CLIENT_S_REGISTERING: 595 case AVAHI_CLIENT_CONNECTING: 596 break; 597 } 598} 599 600DNSServiceErrorType DNSSD_API DNSServiceBrowse( 601 DNSServiceRef *ret_sdref, 602 DNSServiceFlags flags, 603 uint32_t interface, 604 const char *regtype, 605 const char *domain, 606 DNSServiceBrowseReply callback, 607 void *context) { 608 609 DNSServiceErrorType ret = kDNSServiceErr_Unknown; 610 int error; 611 DNSServiceRef sdref = NULL; 612 AvahiIfIndex ifindex; 613 struct type_info type_info; 614 615 AVAHI_WARN_LINKAGE; 616 617 if (!ret_sdref || !regtype) 618 return kDNSServiceErr_BadParam; 619 *ret_sdref = NULL; 620 621 if (interface == kDNSServiceInterfaceIndexLocalOnly || flags != 0) { 622 AVAHI_WARN_UNSUPPORTED; 623 return kDNSServiceErr_Unsupported; 624 } 625 626 type_info_init(&type_info); 627 628 if (type_info_parse(&type_info, regtype) < 0 || type_info.n_subtypes > 1) { 629 type_info_free(&type_info); 630 631 if (!avahi_is_valid_service_type_generic(regtype)) 632 return kDNSServiceErr_Unsupported; 633 } else 634 regtype = type_info.subtypes ? (char*) type_info.subtypes->text : type_info.type; 635 636 if (!(sdref = sdref_new())) { 637 type_info_free(&type_info); 638 return kDNSServiceErr_Unknown; 639 } 640 641 sdref->context = context; 642 sdref->service_browser_callback = callback; 643 644 ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex)); 645 646 if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), 0, generic_client_callback, sdref, &error))) { 647 ret = map_error(error); 648 goto finish; 649 } 650 651 ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface; 652 653 if (!(sdref->service_browser = avahi_service_browser_new(sdref->client, ifindex, AVAHI_PROTO_UNSPEC, regtype, domain, 0, service_browser_callback, sdref))) { 654 ret = map_error(avahi_client_errno(sdref->client)); 655 goto finish; 656 } 657 658 ret = kDNSServiceErr_NoError; 659 *ret_sdref = sdref; 660 661finish: 662 663 ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex)); 664 665 if (ret != kDNSServiceErr_NoError) 666 DNSServiceRefDeallocate(sdref); 667 668 type_info_free(&type_info); 669 670 return ret; 671} 672 673static void service_resolver_callback( 674 AvahiServiceResolver *r, 675 AvahiIfIndex interface, 676 AVAHI_GCC_UNUSED AvahiProtocol protocol, 677 AvahiResolverEvent event, 678 const char *name, 679 const char *type, 680 const char *domain, 681 const char *host_name, 682 AVAHI_GCC_UNUSED const AvahiAddress *a, 683 uint16_t port, 684 AvahiStringList *txt, 685 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, 686 void *userdata) { 687 688 DNSServiceRef sdref = userdata; 689 690 assert(r); 691 assert(sdref); 692 assert(sdref->n_ref >= 1); 693 694 switch (event) { 695 case AVAHI_RESOLVER_FOUND: { 696 697 char host_name_fixed[AVAHI_DOMAIN_NAME_MAX]; 698 char full_name[AVAHI_DOMAIN_NAME_MAX]; 699 int ret; 700 char *p = NULL; 701 size_t l = 0; 702 703 host_name = add_trailing_dot(host_name, host_name_fixed, sizeof(host_name_fixed)); 704 705 if ((p = avahi_new0(char, (l = avahi_string_list_serialize(txt, NULL, 0))+1))) 706 avahi_string_list_serialize(txt, p, l); 707 708 ret = avahi_service_name_join(full_name, sizeof(full_name), name, type, domain); 709 assert(ret == AVAHI_OK); 710 711 strcat(full_name, "."); 712 713 sdref->service_resolver_callback(sdref, 0, interface, kDNSServiceErr_NoError, full_name, host_name, htons(port), l, (unsigned char*) p, sdref->context); 714 715 avahi_free(p); 716 break; 717 } 718 719 case AVAHI_RESOLVER_FAILURE: 720 sdref->service_resolver_callback(sdref, 0, interface, map_error(avahi_client_errno(sdref->client)), NULL, NULL, 0, 0, NULL, sdref->context); 721 break; 722 } 723} 724 725DNSServiceErrorType DNSSD_API DNSServiceResolve( 726 DNSServiceRef *ret_sdref, 727 DNSServiceFlags flags, 728 uint32_t interface, 729 const char *name, 730 const char *regtype, 731 const char *domain, 732 DNSServiceResolveReply callback, 733 void *context) { 734 735 DNSServiceErrorType ret = kDNSServiceErr_Unknown; 736 int error; 737 DNSServiceRef sdref = NULL; 738 AvahiIfIndex ifindex; 739 740 AVAHI_WARN_LINKAGE; 741 742 if (!ret_sdref || !name || !regtype || !domain || !callback) 743 return kDNSServiceErr_BadParam; 744 *ret_sdref = NULL; 745 746 if (interface == kDNSServiceInterfaceIndexLocalOnly || flags != 0) { 747 AVAHI_WARN_UNSUPPORTED; 748 return kDNSServiceErr_Unsupported; 749 } 750 751 if (!(sdref = sdref_new())) 752 return kDNSServiceErr_Unknown; 753 754 sdref->context = context; 755 sdref->service_resolver_callback = callback; 756 757 ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex)); 758 759 if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), 0, generic_client_callback, sdref, &error))) { 760 ret = map_error(error); 761 goto finish; 762 } 763 764 ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface; 765 766 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))) { 767 ret = map_error(avahi_client_errno(sdref->client)); 768 goto finish; 769 } 770 771 772 ret = kDNSServiceErr_NoError; 773 *ret_sdref = sdref; 774 775finish: 776 777 ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex)); 778 779 if (ret != kDNSServiceErr_NoError) 780 DNSServiceRefDeallocate(sdref); 781 782 return ret; 783} 784 785int DNSSD_API DNSServiceConstructFullName ( 786 char *fullName, 787 const char *service, 788 const char *regtype, 789 const char *domain) { 790 791 AVAHI_WARN_LINKAGE; 792 793 if (!fullName || !regtype || !domain) 794 return -1; 795 796 if (avahi_service_name_join(fullName, kDNSServiceMaxDomainName, service, regtype, domain) < 0) 797 return -1; 798 799 return 0; 800} 801 802static void domain_browser_callback( 803 AvahiDomainBrowser *b, 804 AvahiIfIndex interface, 805 AVAHI_GCC_UNUSED AvahiProtocol protocol, 806 AvahiBrowserEvent event, 807 const char *domain, 808 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, 809 void *userdata) { 810 811 DNSServiceRef sdref = userdata; 812 static char domain_fixed[AVAHI_DOMAIN_NAME_MAX]; 813 814 assert(b); 815 assert(sdref); 816 assert(sdref->n_ref >= 1); 817 818 domain = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed)); 819 820 switch (event) { 821 case AVAHI_BROWSER_NEW: 822 sdref->domain_browser_callback(sdref, kDNSServiceFlagsAdd, interface, kDNSServiceErr_NoError, domain, sdref->context); 823 break; 824 825 case AVAHI_BROWSER_REMOVE: 826 sdref->domain_browser_callback(sdref, 0, interface, kDNSServiceErr_NoError, domain, sdref->context); 827 break; 828 829 case AVAHI_BROWSER_FAILURE: 830 sdref->domain_browser_callback(sdref, 0, interface, map_error(avahi_client_errno(sdref->client)), domain, sdref->context); 831 break; 832 833 case AVAHI_BROWSER_CACHE_EXHAUSTED: 834 case AVAHI_BROWSER_ALL_FOR_NOW: 835 break; 836 } 837} 838 839DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains( 840 DNSServiceRef *ret_sdref, 841 DNSServiceFlags flags, 842 uint32_t interface, 843 DNSServiceDomainEnumReply callback, 844 void *context) { 845 846 DNSServiceErrorType ret = kDNSServiceErr_Unknown; 847 int error; 848 DNSServiceRef sdref = NULL; 849 AvahiIfIndex ifindex; 850 851 AVAHI_WARN_LINKAGE; 852 853 if (!ret_sdref || !callback) 854 return kDNSServiceErr_BadParam; 855 *ret_sdref = NULL; 856 857 if (interface == kDNSServiceInterfaceIndexLocalOnly || 858 (flags != kDNSServiceFlagsBrowseDomains && flags != kDNSServiceFlagsRegistrationDomains)) { 859 AVAHI_WARN_UNSUPPORTED; 860 return kDNSServiceErr_Unsupported; 861 } 862 863 if (!(sdref = sdref_new())) 864 return kDNSServiceErr_Unknown; 865 866 sdref->context = context; 867 sdref->domain_browser_callback = callback; 868 869 ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex)); 870 871 if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), 0, generic_client_callback, sdref, &error))) { 872 ret = map_error(error); 873 goto finish; 874 } 875 876 ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface; 877 878 if (!(sdref->domain_browser = avahi_domain_browser_new(sdref->client, ifindex, AVAHI_PROTO_UNSPEC, "local", 879 flags == kDNSServiceFlagsRegistrationDomains ? AVAHI_DOMAIN_BROWSER_REGISTER : AVAHI_DOMAIN_BROWSER_BROWSE, 880 0, domain_browser_callback, sdref))) { 881 ret = map_error(avahi_client_errno(sdref->client)); 882 goto finish; 883 } 884 885 ret = kDNSServiceErr_NoError; 886 *ret_sdref = sdref; 887 888finish: 889 890 ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex)); 891 892 if (ret != kDNSServiceErr_NoError) 893 DNSServiceRefDeallocate(sdref); 894 895 return ret; 896} 897 898static void reg_report_error(DNSServiceRef sdref, DNSServiceErrorType error) { 899 char regtype_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX]; 900 const char *regtype, *domain; 901 assert(sdref); 902 assert(sdref->n_ref >= 1); 903 904 if (!sdref->service_register_callback) 905 return; 906 907 regtype = add_trailing_dot(sdref->type_info.type, regtype_fixed, sizeof(regtype_fixed)); 908 domain = add_trailing_dot(sdref->service_domain, domain_fixed, sizeof(domain_fixed)); 909 910 sdref->service_register_callback( 911 sdref, 0, error, 912 sdref->service_name_chosen ? sdref->service_name_chosen : sdref->service_name, 913 regtype, 914 domain, 915 sdref->context); 916} 917 918static int reg_create_service(DNSServiceRef sdref) { 919 int ret; 920 AvahiStringList *l; 921 922 assert(sdref); 923 assert(sdref->n_ref >= 1); 924 925 if ((ret = avahi_entry_group_add_service_strlst( 926 sdref->entry_group, 927 sdref->service_interface, 928 AVAHI_PROTO_UNSPEC, 929 0, 930 sdref->service_name_chosen, 931 sdref->type_info.type, 932 sdref->service_domain, 933 sdref->service_host, 934 sdref->service_port, 935 sdref->service_txt)) < 0) 936 return ret; 937 938 for (l = sdref->type_info.subtypes; l; l = l->next) { 939 /* Create a subtype entry */ 940 941 if (avahi_entry_group_add_service_subtype( 942 sdref->entry_group, 943 sdref->service_interface, 944 AVAHI_PROTO_UNSPEC, 945 0, 946 sdref->service_name_chosen, 947 sdref->type_info.type, 948 sdref->service_domain, 949 (const char*) l->text) < 0) 950 return ret; 951 } 952 953 if ((ret = avahi_entry_group_commit(sdref->entry_group)) < 0) 954 return ret; 955 956 return 0; 957} 958 959static void reg_client_callback(AvahiClient *s, AvahiClientState state, void* userdata) { 960 DNSServiceRef sdref = userdata; 961 962 assert(s); 963 assert(sdref); 964 assert(sdref->n_ref >= 1); 965 966 /* We've not been setup completely */ 967 if (!sdref->entry_group) 968 return; 969 970 switch (state) { 971 case AVAHI_CLIENT_FAILURE: 972 reg_report_error(sdref, kDNSServiceErr_Unknown); 973 break; 974 975 case AVAHI_CLIENT_S_RUNNING: { 976 int ret; 977 978 if (!sdref->service_name) { 979 const char *n; 980 /* If the service name is taken from the host name, copy that */ 981 982 avahi_free(sdref->service_name_chosen); 983 sdref->service_name_chosen = NULL; 984 985 if (!(n = avahi_client_get_host_name(sdref->client))) { 986 reg_report_error(sdref, map_error(avahi_client_errno(sdref->client))); 987 return; 988 } 989 990 if (!(sdref->service_name_chosen = avahi_strdup(n))) { 991 reg_report_error(sdref, kDNSServiceErr_NoMemory); 992 return; 993 } 994 } 995 996 if (!sdref->service_name_chosen) { 997 998 assert(sdref->service_name); 999 1000 if (!(sdref->service_name_chosen = avahi_strdup(sdref->service_name))) { 1001 reg_report_error(sdref, kDNSServiceErr_NoMemory); 1002 return; 1003 } 1004 } 1005 1006 /* Register the service */ 1007 1008 if ((ret = reg_create_service(sdref)) < 0) { 1009 reg_report_error(sdref, map_error(ret)); 1010 return; 1011 } 1012 1013 break; 1014 } 1015 1016 case AVAHI_CLIENT_S_COLLISION: 1017 case AVAHI_CLIENT_S_REGISTERING: 1018 1019 /* Remove our entry */ 1020 avahi_entry_group_reset(sdref->entry_group); 1021 1022 break; 1023 1024 case AVAHI_CLIENT_CONNECTING: 1025 /* Ignore */ 1026 break; 1027 } 1028 1029} 1030 1031static void reg_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { 1032 DNSServiceRef sdref = userdata; 1033 1034 assert(g); 1035 1036 switch (state) { 1037 case AVAHI_ENTRY_GROUP_ESTABLISHED: 1038 1039 /* Inform the user */ 1040 reg_report_error(sdref, kDNSServiceErr_NoError); 1041 1042 break; 1043 1044 case AVAHI_ENTRY_GROUP_COLLISION: { 1045 char *n; 1046 int ret; 1047 1048 /* Remove our entry */ 1049 avahi_entry_group_reset(sdref->entry_group); 1050 1051 assert(sdref->service_name_chosen); 1052 1053 /* Pick a new name */ 1054 if (!(n = avahi_alternative_service_name(sdref->service_name_chosen))) { 1055 reg_report_error(sdref, kDNSServiceErr_NoMemory); 1056 return; 1057 } 1058 avahi_free(sdref->service_name_chosen); 1059 sdref->service_name_chosen = n; 1060 1061 /* Register the service with that new name */ 1062 if ((ret = reg_create_service(sdref)) < 0) { 1063 reg_report_error(sdref, map_error(ret)); 1064 return; 1065 } 1066 1067 break; 1068 } 1069 1070 case AVAHI_ENTRY_GROUP_REGISTERING: 1071 case AVAHI_ENTRY_GROUP_UNCOMMITED: 1072 /* Ignore */ 1073 break; 1074 1075 case AVAHI_ENTRY_GROUP_FAILURE: 1076 /* Inform the user */ 1077 reg_report_error(sdref, map_error(avahi_client_errno(sdref->client))); 1078 break; 1079 1080 } 1081} 1082 1083DNSServiceErrorType DNSSD_API DNSServiceRegister ( 1084 DNSServiceRef *ret_sdref, 1085 DNSServiceFlags flags, 1086 uint32_t interface, 1087 const char *name, 1088 const char *regtype, 1089 const char *domain, 1090 const char *host, 1091 uint16_t port, 1092 uint16_t txtLen, 1093 const void *txtRecord, 1094 DNSServiceRegisterReply callback, 1095 void *context) { 1096 1097 DNSServiceErrorType ret = kDNSServiceErr_Unknown; 1098 int error; 1099 DNSServiceRef sdref = NULL; 1100 AvahiStringList *txt = NULL; 1101 struct type_info type_info; 1102 1103 AVAHI_WARN_LINKAGE; 1104 1105 if (!ret_sdref || !regtype) 1106 return kDNSServiceErr_BadParam; 1107 *ret_sdref = NULL; 1108 1109 if (!txtRecord) { 1110 txtLen = 1; 1111 txtRecord = ""; 1112 } 1113 1114 if (interface == kDNSServiceInterfaceIndexLocalOnly || flags) { 1115 AVAHI_WARN_UNSUPPORTED; 1116 return kDNSServiceErr_Unsupported; 1117 } 1118 1119 if (txtLen > 0) 1120 if (avahi_string_list_parse(txtRecord, txtLen, &txt) < 0) 1121 return kDNSServiceErr_Invalid; 1122 1123 if (type_info_parse(&type_info, regtype) < 0) { 1124 avahi_string_list_free(txt); 1125 return kDNSServiceErr_Invalid; 1126 } 1127 1128 if (!(sdref = sdref_new())) { 1129 avahi_string_list_free(txt); 1130 type_info_free(&type_info); 1131 return kDNSServiceErr_Unknown; 1132 } 1133 1134 sdref->context = context; 1135 sdref->service_register_callback = callback; 1136 1137 sdref->type_info = type_info; 1138 sdref->service_name = avahi_strdup(name); 1139 sdref->service_domain = domain ? avahi_normalize_name_strdup(domain) : NULL; 1140 sdref->service_host = host ? avahi_normalize_name_strdup(host) : NULL; 1141 sdref->service_interface = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface; 1142 sdref->service_port = ntohs(port); 1143 sdref->service_txt = txt; 1144 1145 /* Some OOM checking would be cool here */ 1146 1147 ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex)); 1148 1149 if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), 0, reg_client_callback, sdref, &error))) { 1150 ret = map_error(error); 1151 goto finish; 1152 } 1153 1154 if (!sdref->service_domain) { 1155 const char *d; 1156 1157 if (!(d = avahi_client_get_domain_name(sdref->client))) { 1158 ret = map_error(avahi_client_errno(sdref->client)); 1159 goto finish; 1160 } 1161 1162 if (!(sdref->service_domain = avahi_strdup(d))) { 1163 ret = kDNSServiceErr_NoMemory; 1164 goto finish; 1165 } 1166 } 1167 1168 if (!(sdref->entry_group = avahi_entry_group_new(sdref->client, reg_entry_group_callback, sdref))) { 1169 ret = map_error(avahi_client_errno(sdref->client)); 1170 goto finish; 1171 } 1172 1173 if (avahi_client_get_state(sdref->client) == AVAHI_CLIENT_S_RUNNING) { 1174 const char *n; 1175 1176 if (sdref->service_name) 1177 n = sdref->service_name; 1178 else { 1179 if (!(n = avahi_client_get_host_name(sdref->client))) { 1180 ret = map_error(avahi_client_errno(sdref->client)); 1181 goto finish; 1182 } 1183 } 1184 1185 if (!(sdref->service_name_chosen = avahi_strdup(n))) { 1186 ret = kDNSServiceErr_NoMemory; 1187 goto finish; 1188 } 1189 1190 1191 if ((error = reg_create_service(sdref)) < 0) { 1192 ret = map_error(error); 1193 goto finish; 1194 } 1195 } 1196 1197 ret = kDNSServiceErr_NoError; 1198 *ret_sdref = sdref; 1199 1200finish: 1201 1202 ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex)); 1203 1204 if (ret != kDNSServiceErr_NoError) 1205 DNSServiceRefDeallocate(sdref); 1206 1207 return ret; 1208} 1209 1210DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord( 1211 DNSServiceRef sdref, 1212 DNSRecordRef rref, 1213 DNSServiceFlags flags, 1214 uint16_t rdlen, 1215 const void *rdata, 1216 AVAHI_GCC_UNUSED uint32_t ttl) { 1217 1218 int ret = kDNSServiceErr_Unknown; 1219 AvahiStringList *txt = NULL; 1220 1221 AVAHI_WARN_LINKAGE; 1222 1223 if (!sdref || sdref->n_ref <= 0) 1224 return kDNSServiceErr_BadParam; 1225 1226 if (flags || rref) { 1227 AVAHI_WARN_UNSUPPORTED; 1228 return kDNSServiceErr_Unsupported; 1229 } 1230 1231 if (rdlen > 0) 1232 if (avahi_string_list_parse(rdata, rdlen, &txt) < 0) 1233 return kDNSServiceErr_Invalid; 1234 1235 ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex)); 1236 1237 if (!avahi_string_list_equal(txt, sdref->service_txt)) { 1238 1239 avahi_string_list_free(sdref->service_txt); 1240 sdref->service_txt = txt; 1241 1242 if (avahi_client_get_state(sdref->client) == AVAHI_CLIENT_S_RUNNING && 1243 sdref->entry_group && 1244 (avahi_entry_group_get_state(sdref->entry_group) == AVAHI_ENTRY_GROUP_ESTABLISHED || 1245 avahi_entry_group_get_state(sdref->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING)) 1246 1247 if (avahi_entry_group_update_service_txt_strlst( 1248 sdref->entry_group, 1249 sdref->service_interface, 1250 AVAHI_PROTO_UNSPEC, 1251 0, 1252 sdref->service_name_chosen, 1253 sdref->type_info.type, 1254 sdref->service_domain, 1255 sdref->service_txt) < 0) { 1256 1257 ret = map_error(avahi_client_errno(sdref->client)); 1258 goto finish; 1259 } 1260 1261 } else 1262 avahi_string_list_free(txt); 1263 1264 ret = kDNSServiceErr_NoError; 1265 1266finish: 1267 ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex)); 1268 1269 return ret; 1270} 1271 1272static void query_resolver_callback( 1273 AvahiRecordBrowser *r, 1274 AvahiIfIndex interface, 1275 AVAHI_GCC_UNUSED AvahiProtocol protocol, 1276 AvahiBrowserEvent event, 1277 const char *name, 1278 uint16_t clazz, 1279 uint16_t type, 1280 const void* rdata, 1281 size_t size, 1282 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, 1283 void *userdata) { 1284 1285 DNSServiceRef sdref = userdata; 1286 1287 assert(r); 1288 assert(sdref); 1289 assert(sdref->n_ref >= 1); 1290 1291 switch (event) { 1292 1293 case AVAHI_BROWSER_NEW: 1294 case AVAHI_BROWSER_REMOVE: { 1295 1296 DNSServiceFlags qflags = 0; 1297 if (event == AVAHI_BROWSER_NEW) 1298 qflags |= kDNSServiceFlagsAdd; 1299 1300 sdref->query_resolver_callback(sdref, qflags, interface, kDNSServiceErr_NoError, name, type, clazz, size, rdata, 0, sdref->context); 1301 break; 1302 } 1303 1304 case AVAHI_BROWSER_ALL_FOR_NOW: 1305 case AVAHI_BROWSER_CACHE_EXHAUSTED: 1306 /* not implemented */ 1307 break; 1308 1309 case AVAHI_BROWSER_FAILURE: 1310 sdref->query_resolver_callback(sdref, 0, interface, map_error(avahi_client_errno(sdref->client)), NULL, 0, 0, 0, NULL, 0, sdref->context); 1311 break; 1312 } 1313} 1314 1315DNSServiceErrorType DNSSD_API DNSServiceQueryRecord ( 1316 DNSServiceRef *ret_sdref, 1317 DNSServiceFlags flags, 1318 uint32_t interface, 1319 const char *fullname, 1320 uint16_t type, 1321 uint16_t clazz, 1322 DNSServiceQueryRecordReply callback, 1323 void *context) { 1324 1325 DNSServiceErrorType ret = kDNSServiceErr_Unknown; 1326 int error; 1327 DNSServiceRef sdref = NULL; 1328 AvahiIfIndex ifindex; 1329 1330 AVAHI_WARN_LINKAGE; 1331 1332 if (!ret_sdref || !fullname) 1333 return kDNSServiceErr_BadParam; 1334 *ret_sdref = NULL; 1335 1336 if (interface == kDNSServiceInterfaceIndexLocalOnly || flags != 0) { 1337 AVAHI_WARN_UNSUPPORTED; 1338 return kDNSServiceErr_Unsupported; 1339 } 1340 1341 if (!(sdref = sdref_new())) 1342 return kDNSServiceErr_Unknown; 1343 1344 sdref->context = context; 1345 sdref->query_resolver_callback = callback; 1346 1347 ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex)); 1348 1349 if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), 0, generic_client_callback, sdref, &error))) { 1350 ret = map_error(error); 1351 goto finish; 1352 } 1353 1354 ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface; 1355 1356 if (!(sdref->record_browser = avahi_record_browser_new(sdref->client, ifindex, AVAHI_PROTO_UNSPEC, fullname, clazz, type, 0, query_resolver_callback, sdref))) { 1357 ret = map_error(avahi_client_errno(sdref->client)); 1358 goto finish; 1359 } 1360 1361 ret = kDNSServiceErr_NoError; 1362 *ret_sdref = sdref; 1363 1364finish: 1365 1366 ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex)); 1367 1368 if (ret != kDNSServiceErr_NoError) 1369 DNSServiceRefDeallocate(sdref); 1370 1371 return ret; 1372} 1373