• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500-V1.0.1.40_1.0.68/ap/gpl/timemachine/avahi-0.6.25/avahi-compat-howl/
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
46enum {
47    COMMAND_POLL = 'p',
48    COMMAND_QUIT = 'q',
49    COMMAND_POLL_DONE = 'P',
50    COMMAND_POLL_FAILED = 'F'
51};
52
53typedef 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
61typedef struct service_data service_data;
62
63typedef 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
73struct 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
81struct _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
103static sw_discovery discovery_ref(sw_discovery self);
104static void discovery_unref(sw_discovery self);
105
106static 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
120static 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
132static 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
146static 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
157static 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
170static 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
226static 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
252static 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
266static 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
278static 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
292static 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
306static void reg_client_callback(oid_data *data, AvahiClientState state);
307
308static 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
339sw_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), 0, 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
403fail:
404
405    if (*self)
406        sw_discovery_fina(*self);
407
408    return result;
409}
410
411static 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
427static 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
436static 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
466sw_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
477sw_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
485sw_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
493int sw_discovery_socket(sw_discovery self) {
494    assert(self);
495
496    AVAHI_WARN_LINKAGE;
497
498    return self->main_fd;
499}
500
501sw_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
531finish:
532
533    ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
534
535    discovery_unref(self);
536
537    return result;
538}
539
540sw_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
551sw_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
591sw_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
603sw_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
614sw_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
623sw_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
633static 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
646static 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
689static 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_FAILURE:
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        case AVAHI_CLIENT_S_REGISTERING:
715
716            /* Remove our entry */
717            avahi_entry_group_reset(data->object);
718            break;
719
720        case AVAHI_CLIENT_CONNECTING:
721            /* Ignore */
722            break;
723    }
724
725}
726
727static 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        case AVAHI_ENTRY_GROUP_FAILURE:
750            reg_report_status(data, SW_DISCOVERY_PUBLISH_INVALID);
751            break;
752
753    }
754}
755
756sw_result sw_discovery_publish(
757    sw_discovery self,
758    sw_uint32 interface_index,
759    sw_const_string name,
760    sw_const_string type,
761    sw_const_string domain,
762    sw_const_string host,
763    sw_port port,
764    sw_octets text_record,
765    sw_uint32 text_record_len,
766    sw_discovery_publish_reply reply,
767    sw_opaque extra,
768    sw_discovery_oid * oid) {
769
770    oid_data *data;
771    sw_result result = SW_E_UNKNOWN;
772    service_data *sdata;
773    AvahiStringList *txt = NULL;
774
775    assert(self);
776    assert(name);
777    assert(type);
778    assert(reply);
779    assert(oid);
780
781    AVAHI_WARN_LINKAGE;
782
783    if (text_record && text_record_len > 0)
784        if (avahi_string_list_parse(text_record, text_record_len, &txt) < 0)
785            return SW_E_UNKNOWN;
786
787    if ((*oid = oid_alloc(self, OID_ENTRY_GROUP)) == (sw_discovery_oid) -1) {
788        avahi_string_list_free(txt);
789        return SW_E_UNKNOWN;
790    }
791
792    if (!(sdata = service_data_new(self))) {
793        avahi_string_list_free(txt);
794        oid_release(self, *oid);
795        return SW_E_MEM;
796    }
797
798    data = oid_get(self, *oid);
799    assert(data);
800    data->reply = (sw_result (*)(void)) reply;
801    data->extra = extra;
802    data->service_data = sdata;
803
804    sdata->interface = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
805    sdata->name = avahi_strdup(name);
806    sdata->regtype = type ? avahi_normalize_name_strdup(type) : NULL;
807    sdata->domain = domain ? avahi_normalize_name_strdup(domain) : NULL;
808    sdata->host = host ? avahi_normalize_name_strdup(host) : NULL;
809    sdata->port = port;
810    sdata->txt = txt;
811
812    /* Some OOM checking would be cool here */
813
814    ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
815
816    if (!(data->object = avahi_entry_group_new(self->client, reg_entry_group_callback, data))) {
817        result = map_error(avahi_client_errno(self->client));
818        goto finish;
819    }
820
821    if (avahi_client_get_state(self->client) == AVAHI_CLIENT_S_RUNNING) {
822        int error;
823
824        if ((error = reg_create_service(data)) < 0) {
825            result = map_error(error);
826            goto finish;
827        }
828    }
829
830    result = SW_OKAY;
831
832finish:
833
834    ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
835
836    if (result != SW_OKAY)
837        if (*oid != (sw_discovery_oid) -1)
838            sw_discovery_cancel(self, *oid);
839
840    return result;
841}
842
843static void domain_browser_callback(
844    AvahiDomainBrowser *b,
845    AvahiIfIndex interface,
846    AVAHI_GCC_UNUSED AvahiProtocol protocol,
847    AvahiBrowserEvent event,
848    const char *domain,
849    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
850    void *userdata) {
851
852    oid_data* data = userdata;
853    sw_discovery_browse_reply reply;
854    static char domain_fixed[AVAHI_DOMAIN_NAME_MAX];
855
856    assert(b);
857    assert(data);
858
859    reply = (sw_discovery_browse_reply) data->reply;
860
861    domain  = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
862
863    switch (event) {
864        case AVAHI_BROWSER_NEW:
865            reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_ADD_DOMAIN, interface, NULL, NULL, domain, data->extra);
866            break;
867
868        case AVAHI_BROWSER_REMOVE:
869            reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_REMOVE_DOMAIN, interface, NULL, NULL, domain, data->extra);
870            break;
871
872        case AVAHI_BROWSER_FAILURE:
873            reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, NULL, NULL, domain, data->extra);
874            break;
875
876        case AVAHI_BROWSER_CACHE_EXHAUSTED:
877        case AVAHI_BROWSER_ALL_FOR_NOW:
878            break;
879    }
880}
881
882sw_result sw_discovery_browse_domains(
883    sw_discovery self,
884    sw_uint32 interface_index,
885    sw_discovery_browse_reply reply,
886    sw_opaque extra,
887    sw_discovery_oid * oid) {
888
889    oid_data *data;
890    AvahiIfIndex ifindex;
891    sw_result result = SW_E_UNKNOWN;
892
893    assert(self);
894    assert(reply);
895    assert(oid);
896
897    AVAHI_WARN_LINKAGE;
898
899    if ((*oid = oid_alloc(self, OID_DOMAIN_BROWSER)) == (sw_discovery_oid) -1)
900        return SW_E_UNKNOWN;
901
902    data = oid_get(self, *oid);
903    assert(data);
904    data->reply = (sw_result (*)(void)) reply;
905    data->extra = extra;
906
907    ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
908
909    ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
910
911    if (!(data->object = avahi_domain_browser_new(self->client, ifindex, AVAHI_PROTO_INET, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, domain_browser_callback, data))) {
912        result = map_error(avahi_client_errno(self->client));
913        goto finish;
914    }
915
916    result = SW_OKAY;
917
918finish:
919
920    ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
921
922    if (result != SW_OKAY)
923        if (*oid != (sw_discovery_oid) -1)
924            sw_discovery_cancel(self, *oid);
925
926    return result;
927}
928
929static void service_resolver_callback(
930    AvahiServiceResolver *r,
931    AvahiIfIndex interface,
932    AVAHI_GCC_UNUSED AvahiProtocol protocol,
933    AvahiResolverEvent event,
934    const char *name,
935    const char *type,
936    const char *domain,
937    const char *host_name,
938    const AvahiAddress *a,
939    uint16_t port,
940    AvahiStringList *txt,
941    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
942    void *userdata) {
943
944    oid_data* data = userdata;
945    sw_discovery_resolve_reply reply;
946
947    assert(r);
948    assert(data);
949
950    reply = (sw_discovery_resolve_reply) data->reply;
951
952    switch (event) {
953        case AVAHI_RESOLVER_FOUND: {
954
955            char host_name_fixed[AVAHI_DOMAIN_NAME_MAX];
956            uint8_t *p = NULL;
957            size_t l = 0;
958            sw_ipv4_address addr;
959
960            sw_ipv4_address_init_from_saddr(&addr, a->data.ipv4.address);
961
962            host_name = add_trailing_dot(host_name, host_name_fixed, sizeof(host_name_fixed));
963
964            if ((p = avahi_new0(uint8_t, (l = avahi_string_list_serialize(txt, NULL, 0))+1)))
965                avahi_string_list_serialize(txt, p, l);
966
967            reply(data->discovery, OID_GET_INDEX(data), interface, name, type, domain, addr, port, p, l, data->extra);
968
969            avahi_free(p);
970            break;
971        }
972
973        case AVAHI_RESOLVER_FAILURE:
974
975            /* Apparently there is no way in HOWL to inform about failed resolvings ... */
976
977            avahi_warn("A service failed to resolve in the HOWL compatiblity layer of Avahi which is used by '%s'. "
978                       "Since the HOWL API doesn't offer any means to inform the application about this, we have to ignore the failure. "
979                       "Please fix your application to use the native API of Avahi!",
980                       avahi_exe_name());
981
982            break;
983    }
984}
985
986sw_result sw_discovery_resolve(
987    sw_discovery self,
988    sw_uint32 interface_index,
989    sw_const_string name,
990    sw_const_string type,
991    sw_const_string domain,
992    sw_discovery_resolve_reply reply,
993    sw_opaque extra,
994    sw_discovery_oid * oid) {
995
996    oid_data *data;
997    AvahiIfIndex ifindex;
998    sw_result result = SW_E_UNKNOWN;
999
1000    assert(self);
1001    assert(name);
1002    assert(type);
1003    assert(reply);
1004    assert(oid);
1005
1006    AVAHI_WARN_LINKAGE;
1007
1008    if ((*oid = oid_alloc(self, OID_SERVICE_RESOLVER)) == (sw_discovery_oid) -1)
1009        return SW_E_UNKNOWN;
1010
1011    data = oid_get(self, *oid);
1012    assert(data);
1013    data->reply = (sw_result (*)(void)) reply;
1014    data->extra = extra;
1015
1016    ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
1017
1018    ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
1019
1020    if (!(data->object = avahi_service_resolver_new(self->client, ifindex, AVAHI_PROTO_INET, name, type, domain, AVAHI_PROTO_INET, 0, service_resolver_callback, data))) {
1021        result = map_error(avahi_client_errno(self->client));
1022        goto finish;
1023    }
1024
1025    result = SW_OKAY;
1026
1027finish:
1028
1029    ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
1030
1031    if (result != SW_OKAY)
1032        if (*oid != (sw_discovery_oid) -1)
1033            sw_discovery_cancel(self, *oid);
1034
1035    return result;
1036}
1037
1038static void service_browser_callback(
1039    AvahiServiceBrowser *b,
1040    AvahiIfIndex interface,
1041    AVAHI_GCC_UNUSED AvahiProtocol protocol,
1042    AvahiBrowserEvent event,
1043    const char *name,
1044    const char *type,
1045    const char *domain,
1046    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
1047    void *userdata) {
1048
1049    oid_data* data = userdata;
1050    char type_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX];
1051    sw_discovery_browse_reply reply;
1052
1053    assert(b);
1054    assert(data);
1055
1056    reply = (sw_discovery_browse_reply) data->reply;
1057
1058    type = add_trailing_dot(type, type_fixed, sizeof(type_fixed));
1059    domain = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
1060
1061    switch (event) {
1062        case AVAHI_BROWSER_NEW:
1063            reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_ADD_SERVICE, interface, name, type, domain, data->extra);
1064            break;
1065
1066        case AVAHI_BROWSER_REMOVE:
1067            reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_REMOVE_SERVICE, interface, name, type, domain, data->extra);
1068            break;
1069
1070        case AVAHI_BROWSER_FAILURE:
1071            reply(data->discovery, OID_GET_INDEX(data), SW_DISCOVERY_BROWSE_INVALID, interface, name, type, domain, data->extra);
1072            break;
1073
1074        case AVAHI_BROWSER_CACHE_EXHAUSTED:
1075        case AVAHI_BROWSER_ALL_FOR_NOW:
1076            break;
1077    }
1078}
1079
1080sw_result sw_discovery_browse(
1081    sw_discovery self,
1082    sw_uint32 interface_index,
1083    sw_const_string type,
1084    sw_const_string domain,
1085    sw_discovery_browse_reply reply,
1086    sw_opaque extra,
1087    sw_discovery_oid * oid) {
1088
1089    oid_data *data;
1090    AvahiIfIndex ifindex;
1091    sw_result result = SW_E_UNKNOWN;
1092
1093    assert(self);
1094    assert(type);
1095    assert(reply);
1096    assert(oid);
1097
1098    AVAHI_WARN_LINKAGE;
1099
1100    if ((*oid = oid_alloc(self, OID_SERVICE_BROWSER)) == (sw_discovery_oid) -1)
1101        return SW_E_UNKNOWN;
1102
1103    data = oid_get(self, *oid);
1104    assert(data);
1105    data->reply = (sw_result (*)(void)) reply;
1106    data->extra = extra;
1107
1108    ifindex = interface_index == 0 ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface_index;
1109
1110    ASSERT_SUCCESS(pthread_mutex_lock(&self->mutex));
1111
1112    if (!(data->object = avahi_service_browser_new(self->client, ifindex, AVAHI_PROTO_INET, type, domain, 0, service_browser_callback, data))) {
1113        result = map_error(avahi_client_errno(self->client));
1114        goto finish;
1115    }
1116
1117    result = SW_OKAY;
1118
1119finish:
1120
1121    ASSERT_SUCCESS(pthread_mutex_unlock(&self->mutex));
1122
1123    if (result != SW_OKAY)
1124        if (*oid != (sw_discovery_oid) -1)
1125            sw_discovery_cancel(self, *oid);
1126
1127    return result;
1128}
1129
1130sw_result sw_discovery_cancel(sw_discovery self, sw_discovery_oid oid) {
1131    oid_data *data;
1132    assert(self);
1133
1134    AVAHI_WARN_LINKAGE;
1135
1136    if (!(data = oid_get(self, oid)))
1137        return SW_E_UNKNOWN;
1138
1139    if (data->object) {
1140        switch (data->type) {
1141            case OID_SERVICE_BROWSER:
1142                avahi_service_browser_free(data->object);
1143                break;
1144
1145            case OID_SERVICE_RESOLVER:
1146                avahi_service_resolver_free(data->object);
1147                break;
1148
1149            case OID_DOMAIN_BROWSER:
1150                avahi_domain_browser_free(data->object);
1151                break;
1152
1153            case OID_ENTRY_GROUP:
1154                avahi_entry_group_free(data->object);
1155                break;
1156
1157            case OID_UNUSED:
1158            ;
1159        }
1160    }
1161
1162    if (data->service_data) {
1163        assert(data->type == OID_ENTRY_GROUP);
1164        service_data_free(self, data->service_data);
1165    }
1166
1167    oid_release(self, oid);
1168
1169    return SW_OKAY;
1170}
1171
1172sw_result sw_discovery_init_with_flags(
1173    sw_discovery * self,
1174    sw_discovery_init_flags flags) {
1175
1176    assert(self);
1177
1178    AVAHI_WARN_LINKAGE;
1179
1180    if (flags != SW_DISCOVERY_USE_SHARED_SERVICE)
1181        return SW_E_NO_IMPL;
1182
1183    return sw_discovery_init(self);
1184}
1185