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