• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/avahi-0.6.25/avahi-compat-libdns_sd/
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 <pthread.h>
27#include <assert.h>
28#include <unistd.h>
29#include <stdio.h>
30#include <errno.h>
31#include <string.h>
32#include <signal.h>
33#include <netinet/in.h>
34#include <fcntl.h>
35
36#include <sys/types.h>
37#include <sys/socket.h>
38
39#include <avahi-common/simple-watch.h>
40#include <avahi-common/malloc.h>
41#include <avahi-common/error.h>
42#include <avahi-common/domain.h>
43#include <avahi-common/alternative.h>
44
45#include <avahi-client/client.h>
46#include <avahi-client/publish.h>
47#include <avahi-client/lookup.h>
48
49#include "warn.h"
50#include "dns_sd.h"
51
52enum {
53    COMMAND_POLL = 'p',
54    COMMAND_QUIT = 'q',
55    COMMAND_POLL_DONE = 'P',
56    COMMAND_POLL_FAILED = 'F'
57};
58
59struct type_info {
60    char *type;
61    AvahiStringList *subtypes;
62    int n_subtypes;
63};
64
65struct _DNSServiceRef_t {
66    int n_ref;
67
68    AvahiSimplePoll *simple_poll;
69
70    int thread_fd, main_fd;
71
72    pthread_t thread;
73    int thread_running;
74
75    pthread_mutex_t mutex;
76
77    void *context;
78    DNSServiceBrowseReply service_browser_callback;
79    DNSServiceResolveReply service_resolver_callback;
80    DNSServiceDomainEnumReply domain_browser_callback;
81    DNSServiceRegisterReply service_register_callback;
82
83    AvahiClient *client;
84    AvahiServiceBrowser *service_browser;
85    AvahiServiceResolver *service_resolver;
86    AvahiDomainBrowser *domain_browser;
87
88    struct type_info type_info;
89    char *service_name, *service_name_chosen, *service_domain, *service_host;
90    uint16_t service_port;
91    AvahiIfIndex service_interface;
92    AvahiStringList *service_txt;
93
94    AvahiEntryGroup *entry_group;
95};
96
97#define ASSERT_SUCCESS(r) { int __ret = (r); assert(__ret == 0); }
98
99static DNSServiceErrorType map_error(int error) {
100    switch (error) {
101        case AVAHI_OK :
102            return kDNSServiceErr_NoError;
103
104        case AVAHI_ERR_BAD_STATE :
105            return kDNSServiceErr_BadState;
106
107        case AVAHI_ERR_INVALID_HOST_NAME:
108        case AVAHI_ERR_INVALID_DOMAIN_NAME:
109        case AVAHI_ERR_INVALID_TTL:
110        case AVAHI_ERR_IS_PATTERN:
111        case AVAHI_ERR_INVALID_RECORD:
112        case AVAHI_ERR_INVALID_SERVICE_NAME:
113        case AVAHI_ERR_INVALID_SERVICE_TYPE:
114        case AVAHI_ERR_INVALID_PORT:
115        case AVAHI_ERR_INVALID_KEY:
116        case AVAHI_ERR_INVALID_ADDRESS:
117        case AVAHI_ERR_INVALID_SERVICE_SUBTYPE:
118            return kDNSServiceErr_BadParam;
119
120
121        case AVAHI_ERR_COLLISION:
122            return kDNSServiceErr_NameConflict;
123
124        case AVAHI_ERR_TOO_MANY_CLIENTS:
125        case AVAHI_ERR_TOO_MANY_OBJECTS:
126        case AVAHI_ERR_TOO_MANY_ENTRIES:
127        case AVAHI_ERR_ACCESS_DENIED:
128            return kDNSServiceErr_Refused;
129
130        case AVAHI_ERR_INVALID_OPERATION:
131        case AVAHI_ERR_INVALID_OBJECT:
132            return kDNSServiceErr_Invalid;
133
134        case AVAHI_ERR_NO_MEMORY:
135            return kDNSServiceErr_NoMemory;
136
137        case AVAHI_ERR_INVALID_INTERFACE:
138        case AVAHI_ERR_INVALID_PROTOCOL:
139            return kDNSServiceErr_BadInterfaceIndex;
140
141        case AVAHI_ERR_INVALID_FLAGS:
142            return kDNSServiceErr_BadFlags;
143
144        case AVAHI_ERR_NOT_FOUND:
145            return kDNSServiceErr_NoSuchName;
146
147        case AVAHI_ERR_VERSION_MISMATCH:
148            return kDNSServiceErr_Incompatible;
149
150        case AVAHI_ERR_NO_NETWORK:
151        case AVAHI_ERR_OS:
152        case AVAHI_ERR_INVALID_CONFIG:
153        case AVAHI_ERR_TIMEOUT:
154        case AVAHI_ERR_DBUS_ERROR:
155        case AVAHI_ERR_DISCONNECTED:
156        case AVAHI_ERR_NO_DAEMON:
157            break;
158
159    }
160
161    return kDNSServiceErr_Unknown;
162}
163
164static void type_info_init(struct type_info *i) {
165    assert(i);
166    i->type = NULL;
167    i->subtypes = NULL;
168    i->n_subtypes = 0;
169}
170
171static void type_info_free(struct type_info *i) {
172    assert(i);
173
174    avahi_free(i->type);
175    avahi_string_list_free(i->subtypes);
176
177    type_info_init(i);
178}
179
180static int type_info_parse(struct type_info *i, const char *t) {
181    char *token = NULL;
182
183    assert(i);
184    assert(t);
185
186    type_info_init(i);
187
188    for (;;) {
189        size_t l;
190
191        if (*t == 0)
192            break;
193
194        l = strcspn(t, ",");
195
196        if (l <= 0)
197            goto fail;
198
199        token = avahi_strndup(t, l);
200
201        if (!token)
202            goto fail;
203
204        if (!i->type) {
205            /* This is the first token, hence the main type */
206
207            if (!avahi_is_valid_service_type_strict(token))
208                goto fail;
209
210            i->type = token;
211            token = NULL;
212        } else {
213            char *fst;
214
215            /* This is not the first token, hence a subtype */
216
217            if (!(fst = avahi_strdup_printf("%s._sub.%s", token, i->type)))
218                goto fail;
219
220            if (!avahi_is_valid_service_subtype(fst)) {
221                avahi_free(fst);
222                goto fail;
223            }
224
225            i->subtypes = avahi_string_list_add(i->subtypes, fst);
226            avahi_free(fst);
227
228            avahi_free(token);
229            token = NULL;
230
231            i->n_subtypes++;
232        }
233
234        t += l;
235
236        if (*t == ',')
237            t++;
238    }
239
240    if (i->type)
241        return 0;
242
243fail:
244    type_info_free(i);
245    avahi_free(token);
246    return -1;
247}
248
249static const char *add_trailing_dot(const char *s, char *buf, size_t buf_len) {
250    if (!s)
251        return NULL;
252
253    if (*s == 0)
254        return s;
255
256    if (s[strlen(s)-1] == '.')
257        return s;
258
259    snprintf(buf, buf_len, "%s.", s);
260    return buf;
261}
262
263static int read_command(int fd) {
264    ssize_t r;
265    char command;
266
267    assert(fd >= 0);
268
269    if ((r = read(fd, &command, 1)) != 1) {
270        fprintf(stderr, __FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
271        return -1;
272    }
273
274    return command;
275}
276
277static int write_command(int fd, char reply) {
278    assert(fd >= 0);
279
280    if (write(fd, &reply, 1) != 1) {
281        fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno));
282        return -1;
283    }
284
285    return 0;
286}
287
288static int poll_func(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) {
289    DNSServiceRef sdref = userdata;
290    int ret;
291
292    assert(sdref);
293
294    ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
295
296/*     fprintf(stderr, "pre-syscall\n"); */
297    ret = poll(ufds, nfds, timeout);
298/*     fprintf(stderr, "post-syscall\n"); */
299
300    ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
301
302    return ret;
303}
304
305static void * thread_func(void *data) {
306    DNSServiceRef sdref = data;
307    sigset_t mask;
308
309    sigfillset(&mask);
310    pthread_sigmask(SIG_BLOCK, &mask, NULL);
311
312    sdref->thread = pthread_self();
313    sdref->thread_running = 1;
314
315    for (;;) {
316        char command;
317
318        if ((command = read_command(sdref->thread_fd)) < 0)
319            break;
320
321/*         fprintf(stderr, "Command: %c\n", command); */
322
323        switch (command) {
324
325            case COMMAND_POLL: {
326                int ret;
327
328                ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
329
330                for (;;) {
331                    errno = 0;
332
333                    if ((ret = avahi_simple_poll_run(sdref->simple_poll)) < 0) {
334
335                        if (errno == EINTR)
336                            continue;
337
338                        fprintf(stderr, __FILE__": avahi_simple_poll_run() failed: %s\n", strerror(errno));
339                    }
340
341                    break;
342                }
343
344                ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
345
346                if (write_command(sdref->thread_fd, ret < 0 ? COMMAND_POLL_FAILED : COMMAND_POLL_DONE) < 0)
347                    break;
348
349                break;
350            }
351
352            case COMMAND_QUIT:
353                return NULL;
354        }
355
356    }
357
358    return NULL;
359}
360
361static DNSServiceRef sdref_new(void) {
362    int fd[2] = { -1, -1 };
363    DNSServiceRef sdref = NULL;
364    pthread_mutexattr_t mutex_attr;
365
366    if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0)
367        goto fail;
368
369    if (!(sdref = avahi_new(struct _DNSServiceRef_t, 1)))
370        goto fail;
371
372    sdref->n_ref = 1;
373    sdref->thread_fd = fd[0];
374    sdref->main_fd = fd[1];
375
376    sdref->client = NULL;
377    sdref->service_browser = NULL;
378    sdref->service_resolver = NULL;
379    sdref->domain_browser = NULL;
380    sdref->entry_group = NULL;
381
382    sdref->service_name = sdref->service_name_chosen = sdref->service_domain = sdref->service_host = NULL;
383    sdref->service_txt = NULL;
384
385    type_info_init(&sdref->type_info);
386
387    ASSERT_SUCCESS(pthread_mutexattr_init(&mutex_attr));
388    pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);
389    ASSERT_SUCCESS(pthread_mutex_init(&sdref->mutex, &mutex_attr));
390
391    sdref->thread_running = 0;
392
393    if (!(sdref->simple_poll = avahi_simple_poll_new()))
394        goto fail;
395
396    avahi_simple_poll_set_func(sdref->simple_poll, poll_func, sdref);
397
398    /* Start simple poll */
399    if (avahi_simple_poll_prepare(sdref->simple_poll, -1) < 0)
400        goto fail;
401
402    /* Queue an initial POLL command for the thread */
403    if (write_command(sdref->main_fd, COMMAND_POLL) < 0)
404        goto fail;
405
406    if (pthread_create(&sdref->thread, NULL, thread_func, sdref) != 0)
407        goto fail;
408
409    sdref->thread_running = 1;
410
411    return sdref;
412
413fail:
414
415    if (sdref)
416        DNSServiceRefDeallocate(sdref);
417
418    return NULL;
419}
420
421static void sdref_free(DNSServiceRef sdref) {
422    assert(sdref);
423
424    if (sdref->thread_running) {
425        ASSERT_SUCCESS(write_command(sdref->main_fd, COMMAND_QUIT));
426        avahi_simple_poll_wakeup(sdref->simple_poll);
427        ASSERT_SUCCESS(pthread_join(sdref->thread, NULL));
428    }
429
430    if (sdref->client)
431        avahi_client_free(sdref->client);
432
433    if (sdref->simple_poll)
434        avahi_simple_poll_free(sdref->simple_poll);
435
436    if (sdref->thread_fd >= 0)
437        close(sdref->thread_fd);
438
439    if (sdref->main_fd >= 0)
440        close(sdref->main_fd);
441
442    ASSERT_SUCCESS(pthread_mutex_destroy(&sdref->mutex));
443
444    avahi_free(sdref->service_name);
445    avahi_free(sdref->service_name_chosen);
446    avahi_free(sdref->service_domain);
447    avahi_free(sdref->service_host);
448
449    type_info_free(&sdref->type_info);
450
451    avahi_string_list_free(sdref->service_txt);
452
453    avahi_free(sdref);
454}
455
456static void sdref_ref(DNSServiceRef sdref) {
457    assert(sdref);
458    assert(sdref->n_ref >= 1);
459
460    sdref->n_ref++;
461}
462
463static void sdref_unref(DNSServiceRef sdref) {
464    assert(sdref);
465    assert(sdref->n_ref >= 1);
466
467    if (--(sdref->n_ref) <= 0)
468        sdref_free(sdref);
469}
470
471int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdref) {
472
473    AVAHI_WARN_LINKAGE;
474
475    if (!sdref || sdref->n_ref <= 0)
476        return -1;
477
478    return sdref->main_fd;
479}
480
481DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdref) {
482    DNSServiceErrorType ret = kDNSServiceErr_Unknown;
483
484    AVAHI_WARN_LINKAGE;
485
486    if (!sdref || sdref->n_ref <= 0)
487        return kDNSServiceErr_BadParam;
488
489    sdref_ref(sdref);
490
491    ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
492
493    /* Cleanup notification socket */
494    if (read_command(sdref->main_fd) != COMMAND_POLL_DONE)
495        goto finish;
496
497    if (avahi_simple_poll_dispatch(sdref->simple_poll) < 0)
498        goto finish;
499
500    if (sdref->n_ref > 1) /* Perhaps we should die */
501
502        /* Dispatch events */
503        if (avahi_simple_poll_prepare(sdref->simple_poll, -1) < 0)
504            goto finish;
505
506    if (sdref->n_ref > 1)
507
508        /* Request the poll */
509        if (write_command(sdref->main_fd, COMMAND_POLL) < 0)
510            goto finish;
511
512    ret = kDNSServiceErr_NoError;
513
514finish:
515
516    ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
517
518    sdref_unref(sdref);
519
520    return ret;
521}
522
523void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdref) {
524    AVAHI_WARN_LINKAGE;
525
526    if (sdref)
527        sdref_unref(sdref);
528}
529
530static void service_browser_callback(
531    AvahiServiceBrowser *b,
532    AvahiIfIndex interface,
533    AVAHI_GCC_UNUSED AvahiProtocol protocol,
534    AvahiBrowserEvent event,
535    const char *name,
536    const char *type,
537    const char *domain,
538    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
539    void *userdata) {
540
541    DNSServiceRef sdref = userdata;
542    char type_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX];
543    assert(b);
544    assert(sdref);
545    assert(sdref->n_ref >= 1);
546
547    type = add_trailing_dot(type, type_fixed, sizeof(type_fixed));
548    domain  = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
549
550    switch (event) {
551        case AVAHI_BROWSER_NEW:
552            sdref->service_browser_callback(sdref, kDNSServiceFlagsAdd, interface, kDNSServiceErr_NoError, name, type, domain, sdref->context);
553            break;
554
555        case AVAHI_BROWSER_REMOVE:
556            sdref->service_browser_callback(sdref, 0, interface, kDNSServiceErr_NoError, name, type, domain, sdref->context);
557            break;
558
559        case AVAHI_BROWSER_FAILURE:
560            sdref->service_browser_callback(sdref, 0, interface, map_error(avahi_client_errno(sdref->client)), NULL, NULL, NULL, sdref->context);
561            break;
562
563        case AVAHI_BROWSER_CACHE_EXHAUSTED:
564        case AVAHI_BROWSER_ALL_FOR_NOW:
565            break;
566    }
567}
568
569static void generic_client_callback(AvahiClient *s, AvahiClientState state, void* userdata) {
570    DNSServiceRef sdref = userdata;
571    int error = kDNSServiceErr_Unknown;
572
573    assert(s);
574    assert(sdref);
575    assert(sdref->n_ref >= 1);
576
577    switch (state) {
578
579        case AVAHI_CLIENT_FAILURE:
580
581            if (sdref->service_browser_callback)
582                sdref->service_browser_callback(sdref, 0, 0, error, NULL, NULL, NULL, sdref->context);
583            else if (sdref->service_resolver_callback)
584                sdref->service_resolver_callback(sdref, 0, 0, error, NULL, NULL, 0, 0, NULL, sdref->context);
585            else if (sdref->domain_browser_callback)
586                sdref->domain_browser_callback(sdref, 0, 0, error, NULL, sdref->context);
587
588            break;
589
590        case AVAHI_CLIENT_S_RUNNING:
591        case AVAHI_CLIENT_S_COLLISION:
592        case AVAHI_CLIENT_S_REGISTERING:
593        case AVAHI_CLIENT_CONNECTING:
594            break;
595    }
596}
597
598DNSServiceErrorType DNSSD_API DNSServiceBrowse(
599        DNSServiceRef *ret_sdref,
600        DNSServiceFlags flags,
601        uint32_t interface,
602        const char *regtype,
603        const char *domain,
604        DNSServiceBrowseReply callback,
605        void *context) {
606
607    DNSServiceErrorType ret = kDNSServiceErr_Unknown;
608    int error;
609    DNSServiceRef sdref = NULL;
610    AvahiIfIndex ifindex;
611    struct type_info type_info;
612
613    AVAHI_WARN_LINKAGE;
614
615    if (!ret_sdref || !regtype)
616        return kDNSServiceErr_BadParam;
617    *ret_sdref = NULL;
618
619    if (interface == kDNSServiceInterfaceIndexLocalOnly || flags != 0) {
620        AVAHI_WARN_UNSUPPORTED;
621        return kDNSServiceErr_Unsupported;
622    }
623
624    type_info_init(&type_info);
625
626    if (type_info_parse(&type_info, regtype) < 0 || type_info.n_subtypes > 1) {
627        type_info_free(&type_info);
628
629        if (!avahi_is_valid_service_type_generic(regtype))
630            return kDNSServiceErr_Unsupported;
631    } else
632        regtype = type_info.subtypes ? (char*) type_info.subtypes->text : type_info.type;
633
634    if (!(sdref = sdref_new())) {
635        type_info_free(&type_info);
636        return kDNSServiceErr_Unknown;
637    }
638
639    sdref->context = context;
640    sdref->service_browser_callback = callback;
641
642    ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
643
644    if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), 0, generic_client_callback, sdref, &error))) {
645        ret =  map_error(error);
646        goto finish;
647    }
648
649    ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
650
651    if (!(sdref->service_browser = avahi_service_browser_new(sdref->client, ifindex, AVAHI_PROTO_UNSPEC, regtype, domain, 0, service_browser_callback, sdref))) {
652        ret = map_error(avahi_client_errno(sdref->client));
653        goto finish;
654    }
655
656    ret = kDNSServiceErr_NoError;
657    *ret_sdref = sdref;
658
659finish:
660
661    ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
662
663    if (ret != kDNSServiceErr_NoError)
664        DNSServiceRefDeallocate(sdref);
665
666    type_info_free(&type_info);
667
668    return ret;
669}
670
671static void service_resolver_callback(
672    AvahiServiceResolver *r,
673    AvahiIfIndex interface,
674    AVAHI_GCC_UNUSED AvahiProtocol protocol,
675    AvahiResolverEvent event,
676    const char *name,
677    const char *type,
678    const char *domain,
679    const char *host_name,
680    AVAHI_GCC_UNUSED const AvahiAddress *a,
681    uint16_t port,
682    AvahiStringList *txt,
683    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
684    void *userdata) {
685
686    DNSServiceRef sdref = userdata;
687
688    assert(r);
689    assert(sdref);
690    assert(sdref->n_ref >= 1);
691
692    switch (event) {
693        case AVAHI_RESOLVER_FOUND: {
694
695            char host_name_fixed[AVAHI_DOMAIN_NAME_MAX];
696            char full_name[AVAHI_DOMAIN_NAME_MAX];
697            int ret;
698            char *p = NULL;
699            size_t l = 0;
700
701            host_name = add_trailing_dot(host_name, host_name_fixed, sizeof(host_name_fixed));
702
703            if ((p = avahi_new0(char, (l = avahi_string_list_serialize(txt, NULL, 0))+1)))
704                avahi_string_list_serialize(txt, p, l);
705
706            ret = avahi_service_name_join(full_name, sizeof(full_name), name, type, domain);
707            assert(ret == AVAHI_OK);
708
709            strcat(full_name, ".");
710
711            sdref->service_resolver_callback(sdref, 0, interface, kDNSServiceErr_NoError, full_name, host_name, htons(port), l, (unsigned char*) p, sdref->context);
712
713            avahi_free(p);
714            break;
715        }
716
717        case AVAHI_RESOLVER_FAILURE:
718            sdref->service_resolver_callback(sdref, 0, interface, map_error(avahi_client_errno(sdref->client)), NULL, NULL, 0, 0, NULL, sdref->context);
719            break;
720    }
721}
722
723DNSServiceErrorType DNSSD_API DNSServiceResolve(
724    DNSServiceRef *ret_sdref,
725    DNSServiceFlags flags,
726    uint32_t interface,
727    const char *name,
728    const char *regtype,
729    const char *domain,
730    DNSServiceResolveReply callback,
731    void *context) {
732
733    DNSServiceErrorType ret = kDNSServiceErr_Unknown;
734    int error;
735    DNSServiceRef sdref = NULL;
736    AvahiIfIndex ifindex;
737
738    AVAHI_WARN_LINKAGE;
739
740    if (!ret_sdref || !name || !regtype || !domain || !callback)
741        return kDNSServiceErr_BadParam;
742    *ret_sdref = NULL;
743
744    if (interface == kDNSServiceInterfaceIndexLocalOnly || flags != 0) {
745        AVAHI_WARN_UNSUPPORTED;
746        return kDNSServiceErr_Unsupported;
747    }
748
749    if (!(sdref = sdref_new()))
750        return kDNSServiceErr_Unknown;
751
752    sdref->context = context;
753    sdref->service_resolver_callback = callback;
754
755    ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
756
757    if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), 0, generic_client_callback, sdref, &error))) {
758        ret =  map_error(error);
759        goto finish;
760    }
761
762    ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
763
764    if (!(sdref->service_resolver = avahi_service_resolver_new(sdref->client, ifindex, AVAHI_PROTO_UNSPEC, name, regtype, domain, AVAHI_PROTO_UNSPEC, 0, service_resolver_callback, sdref))) {
765        ret = map_error(avahi_client_errno(sdref->client));
766        goto finish;
767    }
768
769
770    ret = kDNSServiceErr_NoError;
771    *ret_sdref = sdref;
772
773finish:
774
775    ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
776
777    if (ret != kDNSServiceErr_NoError)
778        DNSServiceRefDeallocate(sdref);
779
780    return ret;
781}
782
783int DNSSD_API DNSServiceConstructFullName (
784    char *fullName,
785    const char *service,
786    const char *regtype,
787    const char *domain) {
788
789    AVAHI_WARN_LINKAGE;
790
791    if (!fullName || !regtype || !domain)
792        return -1;
793
794    if (avahi_service_name_join(fullName, kDNSServiceMaxDomainName, service, regtype, domain) < 0)
795        return -1;
796
797    return 0;
798}
799
800static void domain_browser_callback(
801    AvahiDomainBrowser *b,
802    AvahiIfIndex interface,
803    AVAHI_GCC_UNUSED AvahiProtocol protocol,
804    AvahiBrowserEvent event,
805    const char *domain,
806    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
807    void *userdata) {
808
809    DNSServiceRef sdref = userdata;
810    static char domain_fixed[AVAHI_DOMAIN_NAME_MAX];
811
812    assert(b);
813    assert(sdref);
814    assert(sdref->n_ref >= 1);
815
816    domain  = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
817
818    switch (event) {
819        case AVAHI_BROWSER_NEW:
820            sdref->domain_browser_callback(sdref, kDNSServiceFlagsAdd, interface, kDNSServiceErr_NoError, domain, sdref->context);
821            break;
822
823        case AVAHI_BROWSER_REMOVE:
824            sdref->domain_browser_callback(sdref, 0, interface, kDNSServiceErr_NoError, domain, sdref->context);
825            break;
826
827        case AVAHI_BROWSER_FAILURE:
828            sdref->domain_browser_callback(sdref, 0, interface, map_error(avahi_client_errno(sdref->client)), domain, sdref->context);
829            break;
830
831        case AVAHI_BROWSER_CACHE_EXHAUSTED:
832        case AVAHI_BROWSER_ALL_FOR_NOW:
833            break;
834    }
835}
836
837DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains(
838    DNSServiceRef *ret_sdref,
839    DNSServiceFlags flags,
840    uint32_t interface,
841    DNSServiceDomainEnumReply callback,
842    void *context) {
843
844    DNSServiceErrorType ret = kDNSServiceErr_Unknown;
845    int error;
846    DNSServiceRef sdref = NULL;
847    AvahiIfIndex ifindex;
848
849    AVAHI_WARN_LINKAGE;
850
851    if (!ret_sdref || !callback)
852        return kDNSServiceErr_BadParam;
853    *ret_sdref = NULL;
854
855    if (interface == kDNSServiceInterfaceIndexLocalOnly ||
856        (flags != kDNSServiceFlagsBrowseDomains &&  flags != kDNSServiceFlagsRegistrationDomains)) {
857        AVAHI_WARN_UNSUPPORTED;
858        return kDNSServiceErr_Unsupported;
859    }
860
861    if (!(sdref = sdref_new()))
862        return kDNSServiceErr_Unknown;
863
864    sdref->context = context;
865    sdref->domain_browser_callback = callback;
866
867    ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
868
869    if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), 0, generic_client_callback, sdref, &error))) {
870        ret =  map_error(error);
871        goto finish;
872    }
873
874    ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
875
876    if (!(sdref->domain_browser = avahi_domain_browser_new(sdref->client, ifindex, AVAHI_PROTO_UNSPEC, "local",
877                                                           flags == kDNSServiceFlagsRegistrationDomains ? AVAHI_DOMAIN_BROWSER_REGISTER : AVAHI_DOMAIN_BROWSER_BROWSE,
878                                                           0, domain_browser_callback, sdref))) {
879        ret = map_error(avahi_client_errno(sdref->client));
880        goto finish;
881    }
882
883    ret = kDNSServiceErr_NoError;
884    *ret_sdref = sdref;
885
886finish:
887
888    ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
889
890    if (ret != kDNSServiceErr_NoError)
891        DNSServiceRefDeallocate(sdref);
892
893    return ret;
894}
895
896static void reg_report_error(DNSServiceRef sdref, DNSServiceErrorType error) {
897    char regtype_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX];
898    const char *regtype, *domain;
899    assert(sdref);
900    assert(sdref->n_ref >= 1);
901
902    if (!sdref->service_register_callback)
903        return;
904
905    regtype = add_trailing_dot(sdref->type_info.type, regtype_fixed, sizeof(regtype_fixed));
906    domain = add_trailing_dot(sdref->service_domain, domain_fixed, sizeof(domain_fixed));
907
908    sdref->service_register_callback(
909        sdref, 0, error,
910        sdref->service_name_chosen ? sdref->service_name_chosen : sdref->service_name,
911        regtype,
912        domain,
913        sdref->context);
914}
915
916static int reg_create_service(DNSServiceRef sdref) {
917    int ret;
918    AvahiStringList *l;
919
920    assert(sdref);
921    assert(sdref->n_ref >= 1);
922
923    if ((ret = avahi_entry_group_add_service_strlst(
924        sdref->entry_group,
925        sdref->service_interface,
926        AVAHI_PROTO_UNSPEC,
927        0,
928        sdref->service_name_chosen,
929        sdref->type_info.type,
930        sdref->service_domain,
931        sdref->service_host,
932        sdref->service_port,
933        sdref->service_txt)) < 0)
934        return ret;
935
936    for (l = sdref->type_info.subtypes; l; l = l->next) {
937        /* Create a subtype entry */
938
939        if (avahi_entry_group_add_service_subtype(
940                sdref->entry_group,
941                sdref->service_interface,
942                AVAHI_PROTO_UNSPEC,
943                0,
944                sdref->service_name_chosen,
945                sdref->type_info.type,
946                sdref->service_domain,
947                (const char*) l->text) < 0)
948            return ret;
949    }
950
951    if ((ret = avahi_entry_group_commit(sdref->entry_group)) < 0)
952        return ret;
953
954    return 0;
955}
956
957static void reg_client_callback(AvahiClient *s, AvahiClientState state, void* userdata) {
958    DNSServiceRef sdref = userdata;
959
960    assert(s);
961    assert(sdref);
962    assert(sdref->n_ref >= 1);
963
964    /* We've not been setup completely */
965    if (!sdref->entry_group)
966        return;
967
968    switch (state) {
969        case AVAHI_CLIENT_FAILURE:
970            reg_report_error(sdref, kDNSServiceErr_Unknown);
971            break;
972
973        case AVAHI_CLIENT_S_RUNNING: {
974            int ret;
975
976            if (!sdref->service_name) {
977                const char *n;
978                /* If the service name is taken from the host name, copy that */
979
980                avahi_free(sdref->service_name_chosen);
981                sdref->service_name_chosen = NULL;
982
983                if (!(n = avahi_client_get_host_name(sdref->client))) {
984                    reg_report_error(sdref, map_error(avahi_client_errno(sdref->client)));
985                    return;
986                }
987
988                if (!(sdref->service_name_chosen = avahi_strdup(n))) {
989                    reg_report_error(sdref, kDNSServiceErr_NoMemory);
990                    return;
991                }
992            }
993
994            if (!sdref->service_name_chosen) {
995
996                assert(sdref->service_name);
997
998                if (!(sdref->service_name_chosen = avahi_strdup(sdref->service_name))) {
999                    reg_report_error(sdref, kDNSServiceErr_NoMemory);
1000                    return;
1001                }
1002            }
1003
1004            /* Register the service */
1005
1006            if ((ret = reg_create_service(sdref)) < 0) {
1007                reg_report_error(sdref, map_error(ret));
1008                return;
1009            }
1010
1011            break;
1012        }
1013
1014        case AVAHI_CLIENT_S_COLLISION:
1015        case AVAHI_CLIENT_S_REGISTERING:
1016
1017            /* Remove our entry */
1018            avahi_entry_group_reset(sdref->entry_group);
1019
1020            break;
1021
1022        case AVAHI_CLIENT_CONNECTING:
1023            /* Ignore */
1024            break;
1025    }
1026
1027}
1028
1029static void reg_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1030    DNSServiceRef sdref = userdata;
1031
1032    assert(g);
1033
1034    switch (state) {
1035        case AVAHI_ENTRY_GROUP_ESTABLISHED:
1036
1037            /* Inform the user */
1038            reg_report_error(sdref, kDNSServiceErr_NoError);
1039
1040            break;
1041
1042        case AVAHI_ENTRY_GROUP_COLLISION: {
1043            char *n;
1044            int ret;
1045
1046            /* Remove our entry */
1047            avahi_entry_group_reset(sdref->entry_group);
1048
1049            assert(sdref->service_name_chosen);
1050
1051            /* Pick a new name */
1052            if (!(n = avahi_alternative_service_name(sdref->service_name_chosen))) {
1053                reg_report_error(sdref, kDNSServiceErr_NoMemory);
1054                return;
1055            }
1056            avahi_free(sdref->service_name_chosen);
1057            sdref->service_name_chosen = n;
1058
1059            /* Register the service with that new name */
1060            if ((ret = reg_create_service(sdref)) < 0) {
1061                reg_report_error(sdref, map_error(ret));
1062                return;
1063            }
1064
1065            break;
1066        }
1067
1068        case AVAHI_ENTRY_GROUP_REGISTERING:
1069        case AVAHI_ENTRY_GROUP_UNCOMMITED:
1070            /* Ignore */
1071            break;
1072
1073        case AVAHI_ENTRY_GROUP_FAILURE:
1074            /* Inform the user */
1075            reg_report_error(sdref, map_error(avahi_client_errno(sdref->client)));
1076            break;
1077
1078    }
1079}
1080
1081DNSServiceErrorType DNSSD_API DNSServiceRegister (
1082        DNSServiceRef *ret_sdref,
1083        DNSServiceFlags flags,
1084        uint32_t interface,
1085        const char *name,
1086        const char *regtype,
1087        const char *domain,
1088        const char *host,
1089        uint16_t port,
1090        uint16_t txtLen,
1091        const void *txtRecord,
1092        DNSServiceRegisterReply callback,
1093        void *context) {
1094
1095    DNSServiceErrorType ret = kDNSServiceErr_Unknown;
1096    int error;
1097    DNSServiceRef sdref = NULL;
1098    AvahiStringList *txt = NULL;
1099    struct type_info type_info;
1100
1101    AVAHI_WARN_LINKAGE;
1102
1103    if (!ret_sdref || !regtype)
1104        return kDNSServiceErr_BadParam;
1105    *ret_sdref = NULL;
1106
1107    if (!txtRecord) {
1108        txtLen = 1;
1109        txtRecord = "";
1110    }
1111
1112    if (interface == kDNSServiceInterfaceIndexLocalOnly || flags) {
1113        AVAHI_WARN_UNSUPPORTED;
1114        return kDNSServiceErr_Unsupported;
1115    }
1116
1117    if (txtLen > 0)
1118        if (avahi_string_list_parse(txtRecord, txtLen, &txt) < 0)
1119            return kDNSServiceErr_Invalid;
1120
1121    if (type_info_parse(&type_info, regtype) < 0) {
1122        avahi_string_list_free(txt);
1123        return kDNSServiceErr_Invalid;
1124    }
1125
1126    if (!(sdref = sdref_new())) {
1127        avahi_string_list_free(txt);
1128        type_info_free(&type_info);
1129        return kDNSServiceErr_Unknown;
1130    }
1131
1132    sdref->context = context;
1133    sdref->service_register_callback = callback;
1134
1135    sdref->type_info = type_info;
1136    sdref->service_name = avahi_strdup(name);
1137    sdref->service_domain = domain ? avahi_normalize_name_strdup(domain) : NULL;
1138    sdref->service_host = host ? avahi_normalize_name_strdup(host) : NULL;
1139    sdref->service_interface = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
1140    sdref->service_port = ntohs(port);
1141    sdref->service_txt = txt;
1142
1143    /* Some OOM checking would be cool here */
1144
1145    ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
1146
1147    if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), 0, reg_client_callback, sdref, &error))) {
1148        ret =  map_error(error);
1149        goto finish;
1150    }
1151
1152    if (!sdref->service_domain) {
1153        const char *d;
1154
1155        if (!(d = avahi_client_get_domain_name(sdref->client))) {
1156            ret = map_error(avahi_client_errno(sdref->client));
1157            goto finish;
1158        }
1159
1160        if (!(sdref->service_domain = avahi_strdup(d))) {
1161            ret = kDNSServiceErr_NoMemory;
1162            goto finish;
1163        }
1164    }
1165
1166    if (!(sdref->entry_group = avahi_entry_group_new(sdref->client, reg_entry_group_callback, sdref))) {
1167        ret = map_error(avahi_client_errno(sdref->client));
1168        goto finish;
1169    }
1170
1171    if (avahi_client_get_state(sdref->client) == AVAHI_CLIENT_S_RUNNING) {
1172        const char *n;
1173
1174        if (sdref->service_name)
1175            n = sdref->service_name;
1176        else {
1177            if (!(n = avahi_client_get_host_name(sdref->client))) {
1178                ret = map_error(avahi_client_errno(sdref->client));
1179                goto finish;
1180            }
1181        }
1182
1183        if (!(sdref->service_name_chosen = avahi_strdup(n))) {
1184            ret = kDNSServiceErr_NoMemory;
1185            goto finish;
1186        }
1187
1188
1189        if ((error = reg_create_service(sdref)) < 0) {
1190            ret = map_error(error);
1191            goto finish;
1192        }
1193    }
1194
1195    ret = kDNSServiceErr_NoError;
1196    *ret_sdref = sdref;
1197
1198finish:
1199
1200    ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
1201
1202    if (ret != kDNSServiceErr_NoError)
1203        DNSServiceRefDeallocate(sdref);
1204
1205    return ret;
1206}
1207
1208DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord(
1209         DNSServiceRef sdref,
1210         DNSRecordRef rref,
1211         DNSServiceFlags flags,
1212         uint16_t rdlen,
1213         const void *rdata,
1214         AVAHI_GCC_UNUSED uint32_t ttl) {
1215
1216    int ret = kDNSServiceErr_Unknown;
1217    AvahiStringList *txt = NULL;
1218
1219    AVAHI_WARN_LINKAGE;
1220
1221    if (!sdref || sdref->n_ref <= 0)
1222        return kDNSServiceErr_BadParam;
1223
1224    if (flags || rref) {
1225        AVAHI_WARN_UNSUPPORTED;
1226        return kDNSServiceErr_Unsupported;
1227    }
1228
1229    if (rdlen > 0)
1230        if (avahi_string_list_parse(rdata, rdlen, &txt) < 0)
1231            return kDNSServiceErr_Invalid;
1232
1233    ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
1234
1235    if (!avahi_string_list_equal(txt, sdref->service_txt)) {
1236
1237        avahi_string_list_free(sdref->service_txt);
1238        sdref->service_txt = txt;
1239
1240        if (avahi_client_get_state(sdref->client) == AVAHI_CLIENT_S_RUNNING &&
1241            sdref->entry_group &&
1242            (avahi_entry_group_get_state(sdref->entry_group) == AVAHI_ENTRY_GROUP_ESTABLISHED ||
1243            avahi_entry_group_get_state(sdref->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING))
1244
1245            if (avahi_entry_group_update_service_txt_strlst(
1246                        sdref->entry_group,
1247                        sdref->service_interface,
1248                        AVAHI_PROTO_UNSPEC,
1249                        0,
1250                        sdref->service_name_chosen,
1251                        sdref->type_info.type,
1252                        sdref->service_domain,
1253                        sdref->service_txt) < 0) {
1254
1255                ret = map_error(avahi_client_errno(sdref->client));
1256                goto finish;
1257            }
1258
1259    } else
1260        avahi_string_list_free(txt);
1261
1262    ret = kDNSServiceErr_NoError;
1263
1264finish:
1265    ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
1266
1267    return ret;
1268}
1269
1270