• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/avahi-0.6.31/avahi-compat-howl/
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
44enum {
45    COMMAND_POLL = 'p',
46    COMMAND_QUIT = 'q',
47    COMMAND_POLL_DONE = 'P',
48    COMMAND_POLL_FAILED = 'F'
49};
50
51typedef 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
59typedef struct service_data service_data;
60
61typedef 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
71struct 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
79struct _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
101static sw_discovery discovery_ref(sw_discovery self);
102static void discovery_unref(sw_discovery self);
103
104static 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
118static 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
130static 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
144static 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
155static 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
168static 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
224static 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
250static 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
264static 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
276static 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
290static 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
304static void reg_client_callback(oid_data *data, AvahiClientState state);
305
306static 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
337sw_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
401fail:
402
403    if (*self)
404        sw_discovery_fina(*self);
405
406    return result;
407}
408
409static 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
425static 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
434static 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
464sw_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
475sw_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
483sw_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
491int sw_discovery_socket(sw_discovery self) {
492    assert(self);
493
494    AVAHI_WARN_LINKAGE;
495
496    return self->main_fd;
497}
498
499sw_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
529finish:
530
531    ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
532
533    discovery_unref(self);
534
535    return result;
536}
537
538sw_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
549sw_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
589sw_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
601sw_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
612sw_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
621sw_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
631static 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
644static 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
687static 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
725static 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
754sw_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
830finish:
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
841static 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
880sw_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
916finish:
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
927static 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
984sw_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
1025finish:
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
1036static 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
1078sw_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
1117finish:
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
1128sw_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
1170sw_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