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 <assert.h> 25 26 #include <pthread.h> 27 28 #include <avahi-common/strlst.h> 29 #include "avahi-common/avahi-malloc.h" 30 #include <avahi-common/domain.h> 31 #include <avahi-common/simple-watch.h> 32 #include <avahi-common/error.h> 33 #include <avahi-common/llist.h> 34 35 #include <avahi-client/client.h> 36 #include <avahi-client/publish.h> 37 #include <avahi-client/lookup.h> 38 39 #include "howl.h" 40 #include "warn.h" 41 42 #define OID_MAX 50 43 44 enum { 45 COMMAND_POLL = 'p', 46 COMMAND_QUIT = 'q', 47 COMMAND_POLL_DONE = 'P', 48 COMMAND_POLL_FAILED = 'F' 49 }; 50 51 typedef enum { 52 OID_UNUSED = 0, 53 OID_SERVICE_BROWSER, 54 OID_SERVICE_RESOLVER, 55 OID_DOMAIN_BROWSER, 56 OID_ENTRY_GROUP 57 } oid_type; 58 59 typedef struct service_data service_data; 60 61 typedef struct oid_data { 62 oid_type type; 63 sw_opaque extra; 64 sw_discovery discovery; 65 void *object; 66 sw_result (*reply)(void); 67 service_data *service_data; 68 } oid_data; 69 70 71 struct service_data { 72 char *name, *regtype, *domain, *host; 73 uint16_t port; 74 AvahiIfIndex interface; 75 AvahiStringList *txt; 76 AVAHI_LLIST_FIELDS(service_data, services); 77 }; 78 79 struct _sw_discovery { 80 int n_ref; 81 AvahiSimplePoll *simple_poll; 82 AvahiClient *client; 83 84 oid_data oid_table[OID_MAX]; 85 sw_discovery_oid oid_index; 86 87 int thread_fd, main_fd; 88 89 pthread_t thread; 90 int thread_running; 91 92 pthread_mutex_t mutex, salt_mutex; 93 94 AVAHI_LLIST_HEAD(service_data, services); 95 }; 96 97 #define ASSERT_SUCCESS(r) { int __ret = (r); assert(__ret == 0); } 98 99 #define OID_GET_INDEX(data) ((sw_discovery_oid) (((data) - ((data)->discovery->oid_table)))) 100 101 static sw_discovery discovery_ref(sw_discovery self); 102 static void discovery_unref(sw_discovery self); 103 104 static const char *add_trailing_dot(const char *s, char *buf, size_t buf_len) { 105 if (!s) 106 return NULL; 107 108 if (*s == 0) 109 return s; 110 111 if (s[strlen(s)-1] == '.') 112 return s; 113 114 snprintf(buf, buf_len, "%s.", s); 115 return buf; 116 } 117 118 static sw_result map_error(int error) { 119 switch (error) { 120 case AVAHI_OK: 121 return SW_OKAY; 122 123 case AVAHI_ERR_NO_MEMORY: 124 return SW_E_MEM; 125 } 126 127 return SW_E_UNKNOWN; 128 } 129 130 static int read_command(int fd) { 131 ssize_t r; 132 char command; 133 134 assert(fd >= 0); 135 136 if ((r = read(fd, &command, 1)) != 1) { 137 fprintf(stderr, __FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF"); 138 return -1; 139 } 140 141 return command; 142 } 143 144 static int write_command(int fd, char reply) { 145 assert(fd >= 0); 146 147 if (write(fd, &reply, 1) != 1) { 148 fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno)); 149 return -1; 150 } 151 152 return 0; 153 } 154 155 static int poll_func(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) { 156 sw_discovery self = userdata; 157 int ret; 158 159 assert(self); 160 161 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); 162 ret = poll(ufds, nfds, timeout); 163 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); 164 165 return ret; 166 } 167 168 static void * thread_func(void *data) { 169 sw_discovery self = data; 170 sigset_t mask; 171 172 sigfillset(&mask); 173 pthread_sigmask(SIG_BLOCK, &mask, NULL); 174 175 self->thread = pthread_self(); 176 self->thread_running = 1; 177 178 for (;;) { 179 char command; 180 181 if ((command = read_command(self->thread_fd)) < 0) 182 break; 183 184 /* fprintf(stderr, "Command: %c\n", command); */ 185 186 switch (command) { 187 188 case COMMAND_POLL: { 189 int ret; 190 191 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); 192 193 for (;;) { 194 errno = 0; 195 196 if ((ret = avahi_simple_poll_run(self->simple_poll)) < 0) { 197 198 if (errno == EINTR) 199 continue; 200 201 fprintf(stderr, __FILE__": avahi_simple_poll_run() failed: %s\n", strerror(errno)); 202 } 203 204 break; 205 } 206 207 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); 208 209 if (write_command(self->thread_fd, ret < 0 ? COMMAND_POLL_FAILED : COMMAND_POLL_DONE) < 0) 210 break; 211 212 break; 213 } 214 215 case COMMAND_QUIT: 216 return NULL; 217 } 218 219 } 220 221 return NULL; 222 } 223 224 static int oid_alloc(sw_discovery self, oid_type type) { 225 sw_discovery_oid i; 226 assert(self); 227 228 for (i = 0; i < OID_MAX; i++) { 229 230 while (self->oid_index >= OID_MAX) 231 self->oid_index -= OID_MAX; 232 233 if (self->oid_table[self->oid_index].type == OID_UNUSED) { 234 self->oid_table[self->oid_index].type = type; 235 self->oid_table[self->oid_index].discovery = self; 236 237 assert(OID_GET_INDEX(&self->oid_table[self->oid_index]) == self->oid_index); 238 239 return self->oid_index ++; 240 } 241 242 self->oid_index ++; 243 } 244 245 /* No free entry found */ 246 247 return (sw_discovery_oid) -1; 248 } 249 250 static void oid_release(sw_discovery self, sw_discovery_oid oid) { 251 assert(self); 252 assert(oid < OID_MAX); 253 254 assert(self->oid_table[oid].type != OID_UNUSED); 255 256 self->oid_table[oid].type = OID_UNUSED; 257 self->oid_table[oid].discovery = NULL; 258 self->oid_table[oid].reply = NULL; 259 self->oid_table[oid].object = NULL; 260 self->oid_table[oid].extra = NULL; 261 self->oid_table[oid].service_data = NULL; 262 } 263 264 static oid_data* oid_get(sw_discovery self, sw_discovery_oid oid) { 265 assert(self); 266 267 if (oid >= OID_MAX) 268 return NULL; 269 270 if (self->oid_table[oid].type == OID_UNUSED) 271 return NULL; 272 273 return &self->oid_table[oid]; 274 } 275 276 static service_data* service_data_new(sw_discovery self) { 277 service_data *sdata; 278 279 assert(self); 280 281 if (!(sdata = avahi_new0(service_data, 1))) 282 return NULL; 283 284 AVAHI_LLIST_PREPEND(service_data, services, self->services, sdata); 285 286 return sdata; 287 288 } 289 290 static void service_data_free(sw_discovery self, service_data* sdata) { 291 assert(self); 292 assert(sdata); 293 294 AVAHI_LLIST_REMOVE(service_data, services, self->services, sdata); 295 296 avahi_free(sdata->name); 297 avahi_free(sdata->regtype); 298 avahi_free(sdata->domain); 299 avahi_free(sdata->host); 300 avahi_string_list_free(sdata->txt); 301 avahi_free(sdata); 302 } 303 304 static void reg_client_callback(oid_data *data, AvahiClientState state); 305 306 static void client_callback(AvahiClient *s, AvahiClientState state, void* userdata) { 307 sw_discovery self = userdata; 308 sw_discovery_oid oid; 309 310 assert(s); 311 assert(self); 312 313 discovery_ref(self); 314 315 for (oid = 0; oid < OID_MAX; oid++) { 316 317 switch (self->oid_table[oid].type) { 318 319 case OID_ENTRY_GROUP: 320 reg_client_callback(&self->oid_table[oid], state); 321 break; 322 323 case OID_DOMAIN_BROWSER: 324 case OID_SERVICE_BROWSER: 325 ((sw_discovery_browse_reply) self->oid_table[oid].reply)(self, oid, SW_DISCOVERY_BROWSE_INVALID, 0, NULL, NULL, NULL, self->oid_table[oid].extra); 326 break; 327 328 case OID_SERVICE_RESOLVER: 329 case OID_UNUSED: 330 ; 331 } 332 } 333 334 discovery_unref(self); 335 } 336 337 sw_result sw_discovery_init(sw_discovery * self) { 338 int fd[2] = { -1, -1}; 339 sw_result result = SW_E_UNKNOWN; 340 pthread_mutexattr_t mutex_attr; 341 int error; 342 343 assert(self); 344 345 AVAHI_WARN_LINKAGE; 346 347 *self = NULL; 348 349 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) 350 goto fail; 351 352 if (!(*self = avahi_new(struct _sw_discovery, 1))) { 353 result = SW_E_MEM; 354 goto fail; 355 } 356 357 (*self)->n_ref = 1; 358 (*self)->thread_fd = fd[0]; 359 (*self)->main_fd = fd[1]; 360 361 (*self)->client = NULL; 362 (*self)->simple_poll = NULL; 363 364 memset((*self)->oid_table, 0, sizeof((*self)->oid_table)); 365 (*self)->oid_index = 0; 366 367 (*self)->thread_running = 0; 368 369 AVAHI_LLIST_HEAD_INIT(service_info, (*self)->services); 370 371 ASSERT_SUCCESS(pthread_mutexattr_init(&mutex_attr)); 372 pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE); 373 ASSERT_SUCCESS(pthread_mutex_init(&(*self)->mutex, &mutex_attr)); 374 ASSERT_SUCCESS(pthread_mutex_init(&(*self)->salt_mutex, &mutex_attr)); 375 376 if (!((*self)->simple_poll = avahi_simple_poll_new())) 377 goto fail; 378 379 avahi_simple_poll_set_func((*self)->simple_poll, poll_func, *self); 380 381 if (!((*self)->client = avahi_client_new(avahi_simple_poll_get((*self)->simple_poll), 0, client_callback, *self, &error))) { 382 result = map_error(error); 383 goto fail; 384 } 385 386 /* Start simple poll */ 387 if (avahi_simple_poll_prepare((*self)->simple_poll, -1) < 0) 388 goto fail; 389 390 /* Queue an initial POLL command for the thread */ 391 if (write_command((*self)->main_fd, COMMAND_POLL) < 0) 392 goto fail; 393 394 if (pthread_create(&(*self)->thread, NULL, thread_func, *self) != 0) 395 goto fail; 396 397 (*self)->thread_running = 1; 398 399 return SW_OKAY; 400 401 fail: 402 403 if (*self) 404 sw_discovery_fina(*self); 405 406 return result; 407 } 408 409 static int stop_thread(sw_discovery self) { 410 assert(self); 411 412 if (!self->thread_running) 413 return 0; 414 415 if (write_command(self->main_fd, COMMAND_QUIT) < 0) 416 return -1; 417 418 avahi_simple_poll_wakeup(self->simple_poll); 419 420 ASSERT_SUCCESS(pthread_join(self->thread, NULL)); 421 self->thread_running = 0; 422 return 0; 423 } 424 425 static sw_discovery discovery_ref(sw_discovery self) { 426 assert(self); 427 assert(self->n_ref >= 1); 428 429 self->n_ref++; 430 431 return self; 432 } 433 434 static void discovery_unref(sw_discovery self) { 435 assert(self); 436 assert(self->n_ref >= 1); 437 438 if (--self->n_ref > 0) 439 return; 440 441 stop_thread(self); 442 443 if (self->client) 444 avahi_client_free(self->client); 445 446 if (self->simple_poll) 447 avahi_simple_poll_free(self->simple_poll); 448 449 if (self->thread_fd >= 0) 450 close(self->thread_fd); 451 452 if (self->main_fd >= 0) 453 close(self->main_fd); 454 455 ASSERT_SUCCESS(pthread_mutex_destroy(&self->mutex)); 456 ASSERT_SUCCESS(pthread_mutex_destroy(&self->salt_mutex)); 457 458 while (self->services) 459 service_data_free(self, self->services); 460 461 avahi_free(self); 462 } 463 464 sw_result sw_discovery_fina(sw_discovery self) { 465 assert(self); 466 467 AVAHI_WARN_LINKAGE; 468 469 stop_thread(self); 470 discovery_unref(self); 471 472 return SW_OKAY; 473 } 474 475 sw_result sw_discovery_run(sw_discovery self) { 476 assert(self); 477 478 AVAHI_WARN_LINKAGE; 479 480 return sw_salt_run((sw_salt) self); 481 } 482 483 sw_result sw_discovery_stop_run(sw_discovery self) { 484 assert(self); 485 486 AVAHI_WARN_LINKAGE; 487 488 return sw_salt_stop_run((sw_salt) self); 489 } 490 491 int sw_discovery_socket(sw_discovery self) { 492 assert(self); 493 494 AVAHI_WARN_LINKAGE; 495 496 return self->main_fd; 497 } 498 499 sw_result sw_discovery_read_socket(sw_discovery self) { 500 sw_result result = SW_E_UNKNOWN; 501 502 assert(self); 503 504 discovery_ref(self); 505 506 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); 507 508 /* Cleanup notification socket */ 509 if (read_command(self->main_fd) != COMMAND_POLL_DONE) 510 goto finish; 511 512 if (avahi_simple_poll_dispatch(self->simple_poll) < 0) 513 goto finish; 514 515 if (self->n_ref > 1) /* Perhaps we should die */ 516 517 /* Dispatch events */ 518 if (avahi_simple_poll_prepare(self->simple_poll, -1) < 0) 519 goto finish; 520 521 if (self->n_ref > 1) 522 523 /* Request the poll */ 524 if (write_command(self->main_fd, COMMAND_POLL) < 0) 525 goto finish; 526 527 result = SW_OKAY; 528 529 finish: 530 531 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); 532 533 discovery_unref(self); 534 535 return result; 536 } 537 538 sw_result sw_discovery_salt(sw_discovery self, sw_salt *salt) { 539 assert(self); 540 assert(salt); 541 542 AVAHI_WARN_LINKAGE; 543 544 *salt = (sw_salt) self; 545 546 return SW_OKAY; 547 } 548 549 sw_result sw_salt_step(sw_salt self, sw_uint32 * msec) { 550 struct pollfd p; 551 int r; 552 sw_result result; 553 554 AVAHI_WARN_LINKAGE; 555 556 if (!((sw_discovery) self)->thread_running) 557 return SW_E_UNKNOWN; 558 559 memset(&p, 0, sizeof(p)); 560 p.fd = ((sw_discovery) self)->main_fd; 561 p.events = POLLIN; 562 563 if ((r = poll(&p, 1, msec ? (int) *msec : -1)) < 0) { 564 565 /* Don't treat EINTR as error */ 566 if (errno == EINTR) 567 return SW_OKAY; 568 569 return SW_E_UNKNOWN; 570 571 } else if (r == 0) { 572 573 /* Timeoout */ 574 return SW_OKAY; 575 576 } else { 577 /* Success */ 578 579 if (p.revents != POLLIN) 580 return SW_E_UNKNOWN; 581 582 if ((result = sw_discovery_read_socket((sw_discovery) self)) != SW_OKAY) 583 return result; 584 } 585 586 return SW_OKAY; 587 } 588 589 sw_result sw_salt_run(sw_salt self) { 590 sw_result ret; 591 592 AVAHI_WARN_LINKAGE; 593 594 assert(self); 595 596 for (;;) 597 if ((ret = sw_salt_step(self, NULL)) != SW_OKAY) 598 return ret; 599 } 600 601 sw_result sw_salt_stop_run(sw_salt self) { 602 AVAHI_WARN_LINKAGE; 603 604 assert(self); 605 606 if (stop_thread((sw_discovery) self) < 0) 607 return SW_E_UNKNOWN; 608 609 return SW_OKAY; 610 } 611 612 sw_result sw_salt_lock(sw_salt self) { 613 AVAHI_WARN_LINKAGE; 614 615 assert(self); 616 ASSERT_SUCCESS(pthread_mutex_lock(&((sw_discovery) self)->salt_mutex)); 617 618 return SW_OKAY; 619 } 620 621 sw_result sw_salt_unlock(sw_salt self) { 622 assert(self); 623 624 AVAHI_WARN_LINKAGE; 625 626 ASSERT_SUCCESS(pthread_mutex_unlock(&((sw_discovery) self)->salt_mutex)); 627 628 return SW_OKAY; 629 } 630 631 static void reg_report_status(oid_data *data, sw_discovery_publish_status status) { 632 sw_discovery_publish_reply reply; 633 634 assert(data); 635 636 reply = (sw_discovery_publish_reply) data->reply; 637 638 reply(data->discovery, 639 OID_GET_INDEX(data), 640 status, 641 data->extra); 642 } 643 644 static int reg_create_service(oid_data *data) { 645 int ret; 646 const char *real_type; 647 648 assert(data); 649 650 real_type = avahi_get_type_from_subtype(data->service_data->regtype); 651 652 if ((ret = avahi_entry_group_add_service_strlst( 653 data->object, 654 data->service_data->interface, 655 AVAHI_PROTO_INET, 656 0, 657 data->service_data->name, 658 real_type ? real_type : data->service_data->regtype, 659 data->service_data->domain, 660 data->service_data->host, 661 data->service_data->port, 662 data->service_data->txt)) < 0) 663 return ret; 664 665 if (real_type) { 666 /* Create a subtype entry */ 667 668 if (avahi_entry_group_add_service_subtype( 669 data->object, 670 data->service_data->interface, 671 AVAHI_PROTO_INET, 672 0, 673 data->service_data->name, 674 real_type, 675 data->service_data->domain, 676 data->service_data->regtype) < 0) 677 return ret; 678 679 } 680 681 if ((ret = avahi_entry_group_commit(data->object)) < 0) 682 return ret; 683 684 return 0; 685 } 686 687 static void reg_client_callback(oid_data *data, AvahiClientState state) { 688 assert(data); 689 690 /* We've not been setup completely */ 691 if (!data->object) 692 return; 693 694 switch (state) { 695 case AVAHI_CLIENT_FAILURE: 696 reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID); 697 break; 698 699 case AVAHI_CLIENT_S_RUNNING: { 700 int ret; 701 702 /* Register the service */ 703 if ((ret = reg_create_service(data)) < 0) { 704 reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID); 705 return; 706 } 707 708 break; 709 } 710 711 case AVAHI_CLIENT_S_COLLISION: 712 case AVAHI_CLIENT_S_REGISTERING: 713 714 /* Remove our entry */ 715 avahi_entry_group_reset(data->object); 716 break; 717 718 case AVAHI_CLIENT_CONNECTING: 719 /* Ignore */ 720 break; 721 } 722 723 } 724 725 static void reg_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { 726 oid_data *data = userdata; 727 728 assert(g); 729 assert(data); 730 731 switch (state) { 732 case AVAHI_ENTRY_GROUP_ESTABLISHED: 733 734 reg_report_status(data, SW_DISCOVERY_PUBLISH_STARTED); 735 break; 736 737 case AVAHI_ENTRY_GROUP_COLLISION: 738 739 reg_report_status(data, SW_DISCOVERY_PUBLISH_NAME_COLLISION); 740 break; 741 742 case AVAHI_ENTRY_GROUP_REGISTERING: 743 case AVAHI_ENTRY_GROUP_UNCOMMITED: 744 /* Ignore */ 745 break; 746 747 case AVAHI_ENTRY_GROUP_FAILURE: 748 reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID); 749 break; 750 751 } 752 } 753 754 sw_result sw_discovery_publish( 755 sw_discovery self, 756 sw_uint32 interface_index, 757 sw_const_string name, 758 sw_const_string type, 759 sw_const_string domain, 760 sw_const_string host, 761 sw_port port, 762 sw_octets text_record, 763 sw_uint32 text_record_len, 764 sw_discovery_publish_reply reply, 765 sw_opaque extra, 766 sw_discovery_oid * oid) { 767 768 oid_data *data; 769 sw_result result = SW_E_UNKNOWN; 770 service_data *sdata; 771 AvahiStringList *txt = NULL; 772 773 assert(self); 774 assert(name); 775 assert(type); 776 assert(reply); 777 assert(oid); 778 779 AVAHI_WARN_LINKAGE; 780 781 if (text_record && text_record_len > 0) 782 if (avahi_string_list_parse(text_record, text_record_len, &txt) < 0) 783 return SW_E_UNKNOWN; 784 785 if ((*oid = oid_alloc(self, OID_ENTRY_GROUP)) == (sw_discovery_oid) -1) { 786 avahi_string_list_free(txt); 787 return SW_E_UNKNOWN; 788 } 789 790 if (!(sdata = service_data_new(self))) { 791 avahi_string_list_free(txt); 792 oid_release(self, *oid); 793 return SW_E_MEM; 794 } 795 796 data = oid_get(self, *oid); 797 assert(data); 798 data->reply = (sw_result (*)(void)) reply; 799 data->extra = extra; 800 data->service_data = sdata; 801 802 sdata->interface = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index; 803 sdata->name = avahi_strdup(name); 804 sdata->regtype = type ? avahi_normalize_name_strdup(type) : NULL; 805 sdata->domain = domain ? avahi_normalize_name_strdup(domain) : NULL; 806 sdata->host = host ? avahi_normalize_name_strdup(host) : NULL; 807 sdata->port = port; 808 sdata->txt = txt; 809 810 /* Some OOM checking would be cool here */ 811 812 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); 813 814 if (!(data->object = avahi_entry_group_new(self->client, reg_entry_group_callback, data))) { 815 result = map_error(avahi_client_errno(self->client)); 816 goto finish; 817 } 818 819 if (avahi_client_get_state(self->client) == AVAHI_CLIENT_S_RUNNING) { 820 int error; 821 822 if ((error = reg_create_service(data)) < 0) { 823 result = map_error(error); 824 goto finish; 825 } 826 } 827 828 result = SW_OKAY; 829 830 finish: 831 832 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); 833 834 if (result != SW_OKAY) 835 if (*oid != (sw_discovery_oid) -1) 836 sw_discovery_cancel(self, *oid); 837 838 return result; 839 } 840 841 static void domain_browser_callback( 842 AvahiDomainBrowser *b, 843 AvahiIfIndex interface, 844 AVAHI_GCC_UNUSED AvahiProtocol protocol, 845 AvahiBrowserEvent event, 846 const char *domain, 847 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, 848 void *userdata) { 849 850 oid_data* data = userdata; 851 sw_discovery_browse_reply reply; 852 static char domain_fixed[AVAHI_DOMAIN_NAME_MAX]; 853 854 assert(b); 855 assert(data); 856 857 reply = (sw_discovery_browse_reply) data->reply; 858 859 domain = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed)); 860 861 switch (event) { 862 case AVAHI_BROWSER_NEW: 863 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_ADD_DOMAIN, interface, NULL, NULL, domain, data->extra); 864 break; 865 866 case AVAHI_BROWSER_REMOVE: 867 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_REMOVE_DOMAIN, interface, NULL, NULL, domain, data->extra); 868 break; 869 870 case AVAHI_BROWSER_FAILURE: 871 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, NULL, NULL, domain, data->extra); 872 break; 873 874 case AVAHI_BROWSER_CACHE_EXHAUSTED: 875 case AVAHI_BROWSER_ALL_FOR_NOW: 876 break; 877 } 878 } 879 880 sw_result sw_discovery_browse_domains( 881 sw_discovery self, 882 sw_uint32 interface_index, 883 sw_discovery_browse_reply reply, 884 sw_opaque extra, 885 sw_discovery_oid * oid) { 886 887 oid_data *data; 888 AvahiIfIndex ifindex; 889 sw_result result = SW_E_UNKNOWN; 890 891 assert(self); 892 assert(reply); 893 assert(oid); 894 895 AVAHI_WARN_LINKAGE; 896 897 if ((*oid = oid_alloc(self, OID_DOMAIN_BROWSER)) == (sw_discovery_oid) -1) 898 return SW_E_UNKNOWN; 899 900 data = oid_get(self, *oid); 901 assert(data); 902 data->reply = (sw_result (*)(void)) reply; 903 data->extra = extra; 904 905 ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index; 906 907 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); 908 909 if (!(data->object = avahi_domain_browser_new(self->client, ifindex, AVAHI_PROTO_INET, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, domain_browser_callback, data))) { 910 result = map_error(avahi_client_errno(self->client)); 911 goto finish; 912 } 913 914 result = SW_OKAY; 915 916 finish: 917 918 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); 919 920 if (result != SW_OKAY) 921 if (*oid != (sw_discovery_oid) -1) 922 sw_discovery_cancel(self, *oid); 923 924 return result; 925 } 926 927 static void service_resolver_callback( 928 AvahiServiceResolver *r, 929 AvahiIfIndex interface, 930 AVAHI_GCC_UNUSED AvahiProtocol protocol, 931 AvahiResolverEvent event, 932 const char *name, 933 const char *type, 934 const char *domain, 935 const char *host_name, 936 const AvahiAddress *a, 937 uint16_t port, 938 AvahiStringList *txt, 939 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, 940 void *userdata) { 941 942 oid_data* data = userdata; 943 sw_discovery_resolve_reply reply; 944 945 assert(r); 946 assert(data); 947 948 reply = (sw_discovery_resolve_reply) data->reply; 949 950 switch (event) { 951 case AVAHI_RESOLVER_FOUND: { 952 953 char host_name_fixed[AVAHI_DOMAIN_NAME_MAX]; 954 uint8_t *p = NULL; 955 size_t l = 0; 956 sw_ipv4_address addr; 957 958 sw_ipv4_address_init_from_saddr(&addr, a->data.ipv4.address); 959 960 host_name = add_trailing_dot(host_name, host_name_fixed, sizeof(host_name_fixed)); 961 962 if ((p = avahi_new0(uint8_t, (l = avahi_string_list_serialize(txt, NULL, 0))+1))) 963 avahi_string_list_serialize(txt, p, l); 964 965 reply(data->discovery, OID_GET_INDEX(data), interface, name, type, domain, addr, port, p, l, data->extra); 966 967 avahi_free(p); 968 break; 969 } 970 971 case AVAHI_RESOLVER_FAILURE: 972 973 /* Apparently there is no way in HOWL to inform about failed resolvings ... */ 974 975 avahi_warn("A service failed to resolve in the HOWL compatiblity layer of Avahi which is used by '%s'. " 976 "Since the HOWL API doesn't offer any means to inform the application about this, we have to ignore the failure. " 977 "Please fix your application to use the native API of Avahi!", 978 avahi_exe_name()); 979 980 break; 981 } 982 } 983 984 sw_result sw_discovery_resolve( 985 sw_discovery self, 986 sw_uint32 interface_index, 987 sw_const_string name, 988 sw_const_string type, 989 sw_const_string domain, 990 sw_discovery_resolve_reply reply, 991 sw_opaque extra, 992 sw_discovery_oid * oid) { 993 994 oid_data *data; 995 AvahiIfIndex ifindex; 996 sw_result result = SW_E_UNKNOWN; 997 998 assert(self); 999 assert(name); 1000 assert(type); 1001 assert(reply); 1002 assert(oid); 1003 1004 AVAHI_WARN_LINKAGE; 1005 1006 if ((*oid = oid_alloc(self, OID_SERVICE_RESOLVER)) == (sw_discovery_oid) -1) 1007 return SW_E_UNKNOWN; 1008 1009 data = oid_get(self, *oid); 1010 assert(data); 1011 data->reply = (sw_result (*)(void)) reply; 1012 data->extra = extra; 1013 1014 ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index; 1015 1016 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); 1017 1018 if (!(data->object = avahi_service_resolver_new(self->client, ifindex, AVAHI_PROTO_INET, name, type, domain, AVAHI_PROTO_INET, 0, service_resolver_callback, data))) { 1019 result = map_error(avahi_client_errno(self->client)); 1020 goto finish; 1021 } 1022 1023 result = SW_OKAY; 1024 1025 finish: 1026 1027 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); 1028 1029 if (result != SW_OKAY) 1030 if (*oid != (sw_discovery_oid) -1) 1031 sw_discovery_cancel(self, *oid); 1032 1033 return result; 1034 } 1035 1036 static void service_browser_callback( 1037 AvahiServiceBrowser *b, 1038 AvahiIfIndex interface, 1039 AVAHI_GCC_UNUSED AvahiProtocol protocol, 1040 AvahiBrowserEvent event, 1041 const char *name, 1042 const char *type, 1043 const char *domain, 1044 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, 1045 void *userdata) { 1046 1047 oid_data* data = userdata; 1048 char type_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX]; 1049 sw_discovery_browse_reply reply; 1050 1051 assert(b); 1052 assert(data); 1053 1054 reply = (sw_discovery_browse_reply) data->reply; 1055 1056 type = add_trailing_dot(type, type_fixed, sizeof(type_fixed)); 1057 domain = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed)); 1058 1059 switch (event) { 1060 case AVAHI_BROWSER_NEW: 1061 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_ADD_SERVICE, interface, name, type, domain, data->extra); 1062 break; 1063 1064 case AVAHI_BROWSER_REMOVE: 1065 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_REMOVE_SERVICE, interface, name, type, domain, data->extra); 1066 break; 1067 1068 case AVAHI_BROWSER_FAILURE: 1069 reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, name, type, domain, data->extra); 1070 break; 1071 1072 case AVAHI_BROWSER_CACHE_EXHAUSTED: 1073 case AVAHI_BROWSER_ALL_FOR_NOW: 1074 break; 1075 } 1076 } 1077 1078 sw_result sw_discovery_browse( 1079 sw_discovery self, 1080 sw_uint32 interface_index, 1081 sw_const_string type, 1082 sw_const_string domain, 1083 sw_discovery_browse_reply reply, 1084 sw_opaque extra, 1085 sw_discovery_oid * oid) { 1086 1087 oid_data *data; 1088 AvahiIfIndex ifindex; 1089 sw_result result = SW_E_UNKNOWN; 1090 1091 assert(self); 1092 assert(type); 1093 assert(reply); 1094 assert(oid); 1095 1096 AVAHI_WARN_LINKAGE; 1097 1098 if ((*oid = oid_alloc(self, OID_SERVICE_BROWSER)) == (sw_discovery_oid) -1) 1099 return SW_E_UNKNOWN; 1100 1101 data = oid_get(self, *oid); 1102 assert(data); 1103 data->reply = (sw_result (*)(void)) reply; 1104 data->extra = extra; 1105 1106 ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index; 1107 1108 ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex)); 1109 1110 if (!(data->object = avahi_service_browser_new(self->client, ifindex, AVAHI_PROTO_INET, type, domain, 0, service_browser_callback, data))) { 1111 result = map_error(avahi_client_errno(self->client)); 1112 goto finish; 1113 } 1114 1115 result = SW_OKAY; 1116 1117 finish: 1118 1119 ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex)); 1120 1121 if (result != SW_OKAY) 1122 if (*oid != (sw_discovery_oid) -1) 1123 sw_discovery_cancel(self, *oid); 1124 1125 return result; 1126 } 1127 1128 sw_result sw_discovery_cancel(sw_discovery self, sw_discovery_oid oid) { 1129 oid_data *data; 1130 assert(self); 1131 1132 AVAHI_WARN_LINKAGE; 1133 1134 if (!(data = oid_get(self, oid))) 1135 return SW_E_UNKNOWN; 1136 1137 if (data->object) { 1138 switch (data->type) { 1139 case OID_SERVICE_BROWSER: 1140 avahi_service_browser_free(data->object); 1141 break; 1142 1143 case OID_SERVICE_RESOLVER: 1144 avahi_service_resolver_free(data->object); 1145 break; 1146 1147 case OID_DOMAIN_BROWSER: 1148 avahi_domain_browser_free(data->object); 1149 break; 1150 1151 case OID_ENTRY_GROUP: 1152 avahi_entry_group_free(data->object); 1153 break; 1154 1155 case OID_UNUSED: 1156 ; 1157 } 1158 } 1159 1160 if (data->service_data) { 1161 assert(data->type == OID_ENTRY_GROUP); 1162 service_data_free(self, data->service_data); 1163 } 1164 1165 oid_release(self, oid); 1166 1167 return SW_OKAY; 1168 } 1169 1170 sw_result sw_discovery_init_with_flags( 1171 sw_discovery * self, 1172 sw_discovery_init_flags flags) { 1173 1174 assert(self); 1175 1176 AVAHI_WARN_LINKAGE; 1177 1178 if (flags != SW_DISCOVERY_USE_SHARED_SERVICE) 1179 return SW_E_NO_IMPL; 1180 1181 return sw_discovery_init(self); 1182 } 1183