]> git.meshlink.io Git - catta/blob - avahi-compat-howl/compat.c
9b8b18c5b5246495643c08f494a756bf265dd8e6
[catta] / avahi-compat-howl / compat.c
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/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 }