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
add_trailing_dot(const char * s,char * buf,size_t buf_len)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
map_error(int error)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
read_command(int fd)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
write_command(int fd,char reply)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
poll_func(struct pollfd * ufds,unsigned int nfds,int timeout,void * userdata)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
thread_func(void * data)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
oid_alloc(sw_discovery self,oid_type type)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
oid_release(sw_discovery self,sw_discovery_oid oid)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
oid_get(sw_discovery self,sw_discovery_oid oid)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
service_data_new(sw_discovery self)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
service_data_free(sw_discovery self,service_data * sdata)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
client_callback(AvahiClient * s,AvahiClientState state,void * userdata)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
sw_discovery_init(sw_discovery * self)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
stop_thread(sw_discovery self)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
discovery_ref(sw_discovery self)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
discovery_unref(sw_discovery self)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
sw_discovery_fina(sw_discovery self)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
sw_discovery_run(sw_discovery self)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
sw_discovery_stop_run(sw_discovery self)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
sw_discovery_socket(sw_discovery self)491 int sw_discovery_socket(sw_discovery self) {
492 assert(self);
493
494 AVAHI_WARN_LINKAGE;
495
496 return self->main_fd;
497 }
498
sw_discovery_read_socket(sw_discovery self)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
sw_discovery_salt(sw_discovery self,sw_salt * salt)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
sw_salt_step(sw_salt self,sw_uint32 * msec)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
sw_salt_run(sw_salt self)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
sw_salt_stop_run(sw_salt self)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
sw_salt_lock(sw_salt self)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
sw_salt_unlock(sw_salt self)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
reg_report_status(oid_data * data,sw_discovery_publish_status status)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
reg_create_service(oid_data * data)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
reg_client_callback(oid_data * data,AvahiClientState state)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
reg_entry_group_callback(AvahiEntryGroup * g,AvahiEntryGroupState state,void * userdata)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
sw_discovery_publish(sw_discovery self,sw_uint32 interface_index,sw_const_string name,sw_const_string type,sw_const_string domain,sw_const_string host,sw_port port,sw_octets text_record,sw_uint32 text_record_len,sw_discovery_publish_reply reply,sw_opaque extra,sw_discovery_oid * oid)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
domain_browser_callback(AvahiDomainBrowser * b,AvahiIfIndex interface,AVAHI_GCC_UNUSED AvahiProtocol protocol,AvahiBrowserEvent event,const char * domain,AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,void * userdata)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
sw_discovery_browse_domains(sw_discovery self,sw_uint32 interface_index,sw_discovery_browse_reply reply,sw_opaque extra,sw_discovery_oid * oid)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
service_resolver_callback(AvahiServiceResolver * r,AvahiIfIndex interface,AVAHI_GCC_UNUSED AvahiProtocol protocol,AvahiResolverEvent event,const char * name,const char * type,const char * domain,const char * host_name,const AvahiAddress * a,uint16_t port,AvahiStringList * txt,AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,void * userdata)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
sw_discovery_resolve(sw_discovery self,sw_uint32 interface_index,sw_const_string name,sw_const_string type,sw_const_string domain,sw_discovery_resolve_reply reply,sw_opaque extra,sw_discovery_oid * oid)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
service_browser_callback(AvahiServiceBrowser * b,AvahiIfIndex interface,AVAHI_GCC_UNUSED AvahiProtocol protocol,AvahiBrowserEvent event,const char * name,const char * type,const char * domain,AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,void * userdata)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
sw_discovery_browse(sw_discovery self,sw_uint32 interface_index,sw_const_string type,sw_const_string domain,sw_discovery_browse_reply reply,sw_opaque extra,sw_discovery_oid * oid)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
sw_discovery_cancel(sw_discovery self,sw_discovery_oid oid)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
sw_discovery_init_with_flags(sw_discovery * self,sw_discovery_init_flags flags)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