]> git.meshlink.io Git - catta/blob - avahi-compat-libdns_sd/compat.c
Allow calls to DNSServiceRefDeallocate() with NULL passed
[catta] / avahi-compat-libdns_sd / compat.c
1 /* $Id$ */
2
3 /***
4   This file is part of avahi.
5  
6   avahi is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2.1 of the
9   License, or (at your option) any later version.
10  
11   avahi is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14   Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with avahi; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <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 <avahi-common/simple-watch.h>
37 #include <avahi-common/malloc.h>
38 #include <avahi-common/error.h>
39 #include <avahi-common/domain.h>
40 #include <avahi-common/alternative.h>
41
42 #include <avahi-client/client.h>
43 #include <avahi-client/publish.h>
44 #include <avahi-client/lookup.h>
45
46 #include "warn.h"
47 #include "dns_sd.h"
48
49 enum {
50     COMMAND_COME_AGAIN = 0,
51     COMMAND_POLL = 'p',
52     COMMAND_QUIT = 'q',
53     COMMAND_POLL_DONE = 'P',
54     COMMAND_POLL_FAILED = 'F'
55 };
56
57 struct _DNSServiceRef_t {
58     int n_ref;
59     
60     AvahiSimplePoll *simple_poll;
61
62     int thread_fd, main_fd;
63
64     pthread_t thread;
65     int thread_running;
66
67     pthread_mutex_t mutex;
68
69     void *context;
70     DNSServiceBrowseReply service_browser_callback;
71     DNSServiceResolveReply service_resolver_callback;
72     DNSServiceDomainEnumReply domain_browser_callback;
73     DNSServiceRegisterReply service_register_callback;
74
75     AvahiClient *client;
76     AvahiServiceBrowser *service_browser;
77     AvahiServiceResolver *service_resolver;
78     AvahiDomainBrowser *domain_browser;
79
80     char *service_name, *service_name_chosen, *service_regtype, *service_domain, *service_host;
81     uint16_t service_port;
82     AvahiIfIndex service_interface;
83     AvahiStringList *service_txt;
84
85     AvahiEntryGroup *entry_group;
86 };
87
88 #define ASSERT_SUCCESS(r) { int __ret = (r); assert(__ret == 0); }
89
90 static DNSServiceErrorType map_error(int error) {
91     switch (error) {
92         case AVAHI_OK :
93             return kDNSServiceErr_NoError;
94             
95         case AVAHI_ERR_BAD_STATE :
96             return kDNSServiceErr_BadState;
97             
98         case AVAHI_ERR_INVALID_HOST_NAME:
99         case AVAHI_ERR_INVALID_DOMAIN_NAME:
100         case AVAHI_ERR_INVALID_TTL:
101         case AVAHI_ERR_IS_PATTERN:
102         case AVAHI_ERR_INVALID_RECORD:
103         case AVAHI_ERR_INVALID_SERVICE_NAME:
104         case AVAHI_ERR_INVALID_SERVICE_TYPE:
105         case AVAHI_ERR_INVALID_PORT:
106         case AVAHI_ERR_INVALID_KEY:
107         case AVAHI_ERR_INVALID_ADDRESS:
108         case AVAHI_ERR_INVALID_SERVICE_SUBTYPE:
109             return kDNSServiceErr_BadParam;
110
111
112         case AVAHI_ERR_LOCAL_COLLISION:
113             return kDNSServiceErr_NameConflict;
114
115         case AVAHI_ERR_TOO_MANY_CLIENTS:
116         case AVAHI_ERR_TOO_MANY_OBJECTS:
117         case AVAHI_ERR_TOO_MANY_ENTRIES:
118         case AVAHI_ERR_ACCESS_DENIED:
119             return kDNSServiceErr_Refused;
120
121         case AVAHI_ERR_INVALID_OPERATION:
122         case AVAHI_ERR_INVALID_OBJECT:
123             return kDNSServiceErr_Invalid;
124
125         case AVAHI_ERR_NO_MEMORY:
126             return kDNSServiceErr_NoMemory;
127
128         case AVAHI_ERR_INVALID_INTERFACE:
129         case AVAHI_ERR_INVALID_PROTOCOL:
130             return kDNSServiceErr_BadInterfaceIndex;
131         
132         case AVAHI_ERR_INVALID_FLAGS:
133             return kDNSServiceErr_BadFlags;
134             
135         case AVAHI_ERR_NOT_FOUND:
136             return kDNSServiceErr_NoSuchName;
137             
138         case AVAHI_ERR_VERSION_MISMATCH:
139             return kDNSServiceErr_Incompatible;
140
141         case AVAHI_ERR_NO_NETWORK:
142         case AVAHI_ERR_OS:
143         case AVAHI_ERR_INVALID_CONFIG:
144         case AVAHI_ERR_TIMEOUT:
145         case AVAHI_ERR_DBUS_ERROR:
146         case AVAHI_ERR_NOT_CONNECTED:
147         case AVAHI_ERR_NO_DAEMON:
148             break;
149
150     }
151
152     return kDNSServiceErr_Unknown;
153 }
154
155 static const char *add_trailing_dot(const char *s, char *buf, size_t buf_len) {
156     if (!s)
157         return NULL;
158
159     if (*s == 0)
160         return s;
161
162     if (s[strlen(s)-1] == '.')
163         return s;
164
165     snprintf(buf, buf_len, "%s.", s);
166     return buf;
167 }
168
169 static int read_command(int fd) {
170     ssize_t r;
171     char command;
172
173     assert(fd >= 0);
174     
175     if ((r = read(fd, &command, 1)) != 1) {
176
177         if (errno == EAGAIN)
178             return COMMAND_COME_AGAIN;
179         
180         fprintf(stderr, __FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
181         return -1;
182     }
183
184     return command;
185 }
186
187 static int write_command(int fd, char reply) {
188     assert(fd >= 0);
189
190     if (write(fd, &reply, 1) != 1) {
191         fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno));
192         return -1;
193     }
194
195     return 0;
196 }
197
198 static int set_nonblock(int fd) {
199     int n;
200
201     assert(fd >= 0);
202
203     if ((n = fcntl(fd, F_GETFL)) < 0)
204         return -1;
205
206     if (n & O_NONBLOCK)
207         return 0;
208
209     return fcntl(fd, F_SETFL, n|O_NONBLOCK);
210 }
211
212
213 static int poll_func(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) {
214     DNSServiceRef sdref = userdata;
215     int ret;
216     
217     assert(sdref);
218     
219     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
220
221 /*     fprintf(stderr, "pre-syscall\n"); */
222     ret = poll(ufds, nfds, timeout);
223 /*     fprintf(stderr, "post-syscall\n"); */
224     
225     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
226
227     return ret;
228 }
229
230 static void * thread_func(void *data) {
231     DNSServiceRef sdref = data;
232     sigset_t mask;
233
234     sigfillset(&mask);
235     pthread_sigmask(SIG_BLOCK, &mask, NULL);
236     
237     sdref->thread = pthread_self();
238     sdref->thread_running = 1;
239
240     for (;;) {
241         char command;
242
243         if ((command = read_command(sdref->thread_fd)) < 0)
244             break;
245
246 /*         fprintf(stderr, "Command: %c\n", command); */
247         
248         switch (command) {
249
250             case COMMAND_POLL: {
251                 int ret;
252
253                 ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
254
255                 for (;;) {
256                     errno = 0;
257                     
258                     if ((ret = avahi_simple_poll_run(sdref->simple_poll)) < 0) {
259
260                         if (errno == EINTR)
261                             continue;
262                         
263                         fprintf(stderr, __FILE__": avahi_simple_poll_run() failed: %s\n", strerror(errno));
264                     }
265
266                     break;
267                 }
268
269                 ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
270
271                 if (write_command(sdref->thread_fd, ret < 0 ? COMMAND_POLL_FAILED : COMMAND_POLL_DONE) < 0)
272                     break;
273                 
274                 break;
275             }
276
277             case COMMAND_QUIT:
278                 return NULL;
279
280             case COMMAND_COME_AGAIN:
281                 break;
282         }
283         
284     }
285
286     return NULL;
287 }
288
289 static DNSServiceRef sdref_new(void) {
290     int fd[2] = { -1, -1 };
291     DNSServiceRef sdref = NULL;
292     pthread_mutexattr_t mutex_attr;
293
294     if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0)
295         goto fail;
296
297     if (!(sdref = avahi_new(struct _DNSServiceRef_t, 1)))
298         goto fail;
299
300     sdref->n_ref = 1;
301     sdref->thread_fd = fd[0];
302     sdref->main_fd = fd[1];
303
304     set_nonblock(sdref->main_fd);
305
306     sdref->client = NULL;
307     sdref->service_browser = NULL;
308     sdref->service_resolver = NULL;
309     sdref->domain_browser = NULL;
310     sdref->entry_group = NULL;
311
312     sdref->service_name = sdref->service_name_chosen = sdref->service_regtype = sdref->service_domain = sdref->service_host = NULL;
313     sdref->service_txt = NULL;
314
315     ASSERT_SUCCESS(pthread_mutexattr_init(&mutex_attr));
316     pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);
317     ASSERT_SUCCESS(pthread_mutex_init(&sdref->mutex, &mutex_attr));
318
319     sdref->thread_running = 0;
320
321     if (!(sdref->simple_poll = avahi_simple_poll_new()))
322         goto fail;
323
324     avahi_simple_poll_set_func(sdref->simple_poll, poll_func, sdref);
325
326     /* Start simple poll */
327     if (avahi_simple_poll_prepare(sdref->simple_poll, -1) < 0)
328         goto fail;
329
330     /* Queue an initial POLL command for the thread */
331     if (write_command(sdref->main_fd, COMMAND_POLL) < 0)
332         goto fail;
333     
334     if (pthread_create(&sdref->thread, NULL, thread_func, sdref) != 0)
335         goto fail;
336
337     sdref->thread_running = 1;
338     
339     return sdref;
340
341 fail:
342
343     if (sdref)
344         DNSServiceRefDeallocate(sdref);
345
346     return NULL;
347 }
348
349 static void sdref_free(DNSServiceRef sdref) {
350     assert(sdref);
351     
352     if (sdref->thread_running) {
353         ASSERT_SUCCESS(write_command(sdref->main_fd, COMMAND_QUIT));
354         avahi_simple_poll_wakeup(sdref->simple_poll);
355         ASSERT_SUCCESS(pthread_join(sdref->thread, NULL));
356     }
357
358     if (sdref->client)
359         avahi_client_free(sdref->client);
360
361     if (sdref->simple_poll)
362         avahi_simple_poll_free(sdref->simple_poll);
363
364
365     if (sdref->thread_fd >= 0)
366         close(sdref->thread_fd);
367
368     if (sdref->main_fd >= 0)
369         close(sdref->main_fd);
370
371     ASSERT_SUCCESS(pthread_mutex_destroy(&sdref->mutex));
372
373     avahi_free(sdref->service_name);
374     avahi_free(sdref->service_name_chosen);
375     avahi_free(sdref->service_regtype);
376     avahi_free(sdref->service_domain);
377     avahi_free(sdref->service_host);
378
379     avahi_string_list_free(sdref->service_txt);
380     
381     avahi_free(sdref);
382 }
383
384 static void sdref_ref(DNSServiceRef sdref) {
385     assert(sdref);
386     assert(sdref->n_ref >= 1);
387
388     sdref->n_ref++;
389 }
390
391 static void sdref_unref(DNSServiceRef sdref) {
392     assert(sdref);
393     assert(sdref->n_ref >= 1);
394
395     if (--(sdref->n_ref) <= 0)
396         sdref_free(sdref);
397 }
398
399 int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdref) {
400     assert(sdref);
401     assert(sdref->n_ref >= 1);
402
403     AVAHI_WARN_LINKAGE;
404     
405     return sdref->main_fd;
406 }
407
408 DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdref) {
409     DNSServiceErrorType ret = kDNSServiceErr_Unknown;
410     int t;
411
412     assert(sdref);
413     assert(sdref->n_ref >= 1);
414     
415     AVAHI_WARN_LINKAGE;
416
417     sdref_ref(sdref);
418
419     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
420     
421     /* Cleanup notification socket */
422     if ((t = read_command(sdref->main_fd)) != COMMAND_POLL_DONE) {
423         if (t == COMMAND_COME_AGAIN)
424             ret = kDNSServiceErr_Unknown;
425         goto finish;
426     }
427     
428     if (avahi_simple_poll_dispatch(sdref->simple_poll) < 0)
429         goto finish;
430
431     if (sdref->n_ref > 1) /* Perhaps we should die */
432
433         /* Dispatch events */
434         if (avahi_simple_poll_prepare(sdref->simple_poll, -1) < 0)
435             goto finish;
436
437     if (sdref->n_ref > 1)
438
439         /* Request the poll */
440         if (write_command(sdref->main_fd, COMMAND_POLL) < 0)
441             goto finish;
442     
443     ret = kDNSServiceErr_NoError;
444     
445 finish:
446
447     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
448
449     sdref_unref(sdref);
450     
451     return ret;
452 }
453
454 void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdref) {
455     AVAHI_WARN_LINKAGE;
456
457     if (sdref)
458         sdref_unref(sdref);
459 }
460
461 static void service_browser_callback(
462     AvahiServiceBrowser *b,
463     AvahiIfIndex interface,
464     AvahiProtocol protocol,
465     AvahiBrowserEvent event,
466     const char *name,
467     const char *type,
468     const char *domain,
469     AvahiLookupResultFlags flags,
470     void *userdata) {
471
472     DNSServiceRef sdref = userdata;
473     char type_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX];
474     assert(b);
475     assert(sdref);
476     assert(sdref->n_ref >= 1);
477
478     type = add_trailing_dot(type, type_fixed, sizeof(type_fixed));
479     domain  = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
480     
481     switch (event) {
482         case AVAHI_BROWSER_NEW:
483             sdref->service_browser_callback(sdref, kDNSServiceFlagsAdd, interface, kDNSServiceErr_NoError, name, type, domain, sdref->context);
484             break;
485
486         case AVAHI_BROWSER_REMOVE:
487             sdref->service_browser_callback(sdref, 0, interface, kDNSServiceErr_NoError, name, type, domain, sdref->context);
488             break;
489
490         case AVAHI_BROWSER_FAILURE:
491             sdref->service_browser_callback(sdref, 0, interface, map_error(avahi_client_errno(sdref->client)), NULL, NULL, NULL, sdref->context);
492             break;
493             
494         case AVAHI_BROWSER_CACHE_EXHAUSTED:
495         case AVAHI_BROWSER_ALL_FOR_NOW:
496             break;
497     }
498 }
499
500 static void generic_client_callback(AvahiClient *s, AvahiClientState state, void* userdata) {
501     DNSServiceRef sdref = userdata;
502
503     assert(s);
504     assert(sdref);
505     assert(sdref->n_ref >= 1);
506
507     switch (state) {
508         case AVAHI_CLIENT_DISCONNECTED: {
509
510             if (sdref->service_browser_callback)
511                 sdref->service_browser_callback(sdref, 0, 0, kDNSServiceErr_Unknown, NULL, NULL, NULL, sdref->context);
512             else if (sdref->service_resolver_callback)
513                 sdref->service_resolver_callback(sdref, 0, 0, kDNSServiceErr_Unknown, NULL, NULL, 0, 0, NULL, sdref->context);
514             else if (sdref->domain_browser_callback)
515                 sdref->domain_browser_callback(sdref, 0, 0, kDNSServiceErr_Unknown, NULL, sdref->context);
516
517             break;
518         }
519
520         case AVAHI_CLIENT_S_RUNNING:
521         case AVAHI_CLIENT_S_COLLISION:
522         case AVAHI_CLIENT_S_INVALID:
523         case AVAHI_CLIENT_S_REGISTERING:
524             break;
525     }
526 }
527
528 DNSServiceErrorType DNSSD_API DNSServiceBrowse(
529     DNSServiceRef *ret_sdref,
530     DNSServiceFlags flags,
531     uint32_t interface,
532     const char *regtype,
533     const char *domain,
534     DNSServiceBrowseReply callback,
535     void *context) {
536
537     DNSServiceErrorType ret = kDNSServiceErr_Unknown;
538     int error;
539     DNSServiceRef sdref = NULL;
540     AvahiIfIndex ifindex;
541     
542     AVAHI_WARN_LINKAGE;
543     
544     assert(ret_sdref);
545     assert(regtype);
546     assert(domain);
547     assert(callback);
548
549     if (interface == kDNSServiceInterfaceIndexLocalOnly || flags != 0) {
550         AVAHI_WARN_UNSUPPORTED;
551         return kDNSServiceErr_Unsupported;
552     }
553
554     if (!(sdref = sdref_new()))
555         return kDNSServiceErr_Unknown;
556
557     sdref->context = context;
558     sdref->service_browser_callback = callback;
559
560     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
561     
562     if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), generic_client_callback, sdref, &error))) {
563         ret =  map_error(error);
564         goto finish;
565     }
566
567     ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
568     
569     if (!(sdref->service_browser = avahi_service_browser_new(sdref->client, ifindex, AVAHI_PROTO_UNSPEC, regtype, domain, 0, service_browser_callback, sdref))) {
570         ret = map_error(avahi_client_errno(sdref->client));
571         goto finish;
572     }
573     
574     ret = kDNSServiceErr_NoError;
575     *ret_sdref = sdref;
576                                                               
577 finish:
578
579     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
580     
581     if (ret != kDNSServiceErr_NoError)
582         DNSServiceRefDeallocate(sdref);
583
584     return ret;
585 }
586
587 static void service_resolver_callback(
588     AvahiServiceResolver *r,
589     AvahiIfIndex interface,
590     AvahiProtocol protocol,
591     AvahiResolverEvent event,
592     const char *name,
593     const char *type,
594     const char *domain,
595     const char *host_name,
596     const AvahiAddress *a,
597     uint16_t port,
598     AvahiStringList *txt,
599     AvahiLookupResultFlags flags,
600     void *userdata) {
601
602     DNSServiceRef sdref = userdata;
603
604     assert(r);
605     assert(sdref);
606     assert(sdref->n_ref >= 1);
607
608     switch (event) {
609         case AVAHI_RESOLVER_FOUND: {
610
611             char host_name_fixed[AVAHI_DOMAIN_NAME_MAX];
612             char full_name[AVAHI_DOMAIN_NAME_MAX];
613             int ret;
614             char *p = NULL;
615             size_t l = 0;
616
617             host_name = add_trailing_dot(host_name, host_name_fixed, sizeof(host_name_fixed));
618
619             if ((p = avahi_new0(char, (l = avahi_string_list_serialize(txt, NULL, 0))+1)))
620                 avahi_string_list_serialize(txt, p, l);
621
622             ret = avahi_service_name_join(full_name, sizeof(full_name), name, type, domain);
623             assert(ret == AVAHI_OK);
624
625             strcat(full_name, ".");
626             
627             sdref->service_resolver_callback(sdref, 0, interface, kDNSServiceErr_NoError, full_name, host_name, htons(port), l, p, sdref->context);
628
629             avahi_free(p);
630             break;
631         }
632
633         case AVAHI_RESOLVER_FAILURE:
634             sdref->service_resolver_callback(sdref, 0, interface, map_error(avahi_client_errno(sdref->client)), NULL, NULL, 0, 0, NULL, sdref->context);
635             break;
636     }
637 }
638
639 DNSServiceErrorType DNSSD_API DNSServiceResolve(
640     DNSServiceRef *ret_sdref,
641     DNSServiceFlags flags,
642     uint32_t interface,
643     const char *name,
644     const char *regtype,
645     const char *domain,
646     DNSServiceResolveReply callback,
647     void *context) {
648
649     DNSServiceErrorType ret = kDNSServiceErr_Unknown;
650     int error;
651     DNSServiceRef sdref = NULL;
652     AvahiIfIndex ifindex;
653
654     AVAHI_WARN_LINKAGE;
655
656     assert(ret_sdref);
657     assert(name);
658     assert(regtype);
659     assert(domain);
660     assert(callback);
661
662     if (interface == kDNSServiceInterfaceIndexLocalOnly || flags != 0) {
663         AVAHI_WARN_UNSUPPORTED;
664         return kDNSServiceErr_Unsupported;
665     }
666
667     if (!(sdref = sdref_new()))
668         return kDNSServiceErr_Unknown;
669
670     sdref->context = context;
671     sdref->service_resolver_callback = callback;
672
673     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
674     
675     if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), generic_client_callback, sdref, &error))) {
676         ret =  map_error(error);
677         goto finish;
678     }
679
680     ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
681     
682     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))) {
683         ret = map_error(avahi_client_errno(sdref->client));
684         goto finish;
685     }
686     
687
688     ret = kDNSServiceErr_NoError;
689     *ret_sdref = sdref;
690                                                               
691 finish:
692
693     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
694     
695     if (ret != kDNSServiceErr_NoError)
696         DNSServiceRefDeallocate(sdref);
697
698     return ret;
699 }
700
701 int DNSSD_API DNSServiceConstructFullName (
702     char *fullName,
703     const char *service,   
704     const char *regtype,
705     const char *domain) {
706
707     AVAHI_WARN_LINKAGE;
708
709     assert(fullName);
710     assert(regtype);
711     assert(domain);
712
713     if (avahi_service_name_join(fullName, kDNSServiceMaxDomainName, service, regtype, domain) < 0)
714         return -1;
715     
716     return 0;
717 }
718
719 static void domain_browser_callback(
720     AvahiDomainBrowser *b,
721     AvahiIfIndex interface,
722     AvahiProtocol protocol,
723     AvahiBrowserEvent event,
724     const char *domain,
725     AvahiLookupResultFlags flags,
726     void *userdata) {
727
728     DNSServiceRef sdref = userdata;
729     static char domain_fixed[AVAHI_DOMAIN_NAME_MAX];
730
731     assert(b);
732     assert(sdref);
733     assert(sdref->n_ref >= 1);
734
735     domain  = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
736
737     switch (event) {
738         case AVAHI_BROWSER_NEW:
739             sdref->domain_browser_callback(sdref, kDNSServiceFlagsAdd, interface, kDNSServiceErr_NoError, domain, sdref->context);
740             break;
741
742         case AVAHI_BROWSER_REMOVE:
743             sdref->domain_browser_callback(sdref, 0, interface, kDNSServiceErr_NoError, domain, sdref->context);
744             break;
745
746         case AVAHI_BROWSER_FAILURE:
747             sdref->domain_browser_callback(sdref, 0, interface, map_error(avahi_client_errno(sdref->client)), domain, sdref->context);
748             break;
749             
750         case AVAHI_BROWSER_CACHE_EXHAUSTED:
751         case AVAHI_BROWSER_ALL_FOR_NOW:
752             break;
753     }
754 }
755
756 DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains(
757     DNSServiceRef *ret_sdref,
758     DNSServiceFlags flags,
759     uint32_t interface,
760     DNSServiceDomainEnumReply callback,
761     void *context) {
762
763     DNSServiceErrorType ret = kDNSServiceErr_Unknown;
764     int error;
765     DNSServiceRef sdref = NULL;
766     AvahiIfIndex ifindex;
767
768     AVAHI_WARN_LINKAGE;
769
770     assert(ret_sdref);
771     assert(callback);
772
773     if (interface == kDNSServiceInterfaceIndexLocalOnly ||
774         (flags != kDNSServiceFlagsBrowseDomains &&  flags != kDNSServiceFlagsRegistrationDomains)) {
775         AVAHI_WARN_UNSUPPORTED;
776         return kDNSServiceErr_Unsupported;
777     }
778
779     if (!(sdref = sdref_new()))
780         return kDNSServiceErr_Unknown;
781
782     sdref->context = context;
783     sdref->domain_browser_callback = callback;
784
785     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
786     
787     if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), generic_client_callback, sdref, &error))) {
788         ret =  map_error(error);
789         goto finish;
790     }
791
792     ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
793     
794     if (!(sdref->domain_browser = avahi_domain_browser_new(sdref->client, ifindex, AVAHI_PROTO_UNSPEC, "local",
795                                                            flags == kDNSServiceFlagsRegistrationDomains ? AVAHI_DOMAIN_BROWSER_REGISTER : AVAHI_DOMAIN_BROWSER_BROWSE,
796                                                            0, domain_browser_callback, sdref))) {
797         ret = map_error(avahi_client_errno(sdref->client));
798         goto finish;
799     }
800     
801     ret = kDNSServiceErr_NoError;
802     *ret_sdref = sdref;
803                                                               
804 finish:
805
806     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
807     
808     if (ret != kDNSServiceErr_NoError)
809         DNSServiceRefDeallocate(sdref);
810
811     return ret;
812 }
813
814 static void reg_report_error(DNSServiceRef sdref, DNSServiceErrorType error) {
815     char regtype_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX];
816     const char *regtype, *domain;
817     assert(sdref);
818     assert(sdref->n_ref >= 1);
819
820     assert(sdref->service_register_callback);
821
822     regtype = add_trailing_dot(sdref->service_regtype, regtype_fixed, sizeof(regtype_fixed));
823     domain = add_trailing_dot(sdref->service_domain, domain_fixed, sizeof(domain_fixed));
824     
825     sdref->service_register_callback(
826         sdref, 0, error,
827         sdref->service_name_chosen ? sdref->service_name_chosen : sdref->service_name,
828         regtype,
829         domain,
830         sdref->context);
831 }
832
833 static int reg_create_service(DNSServiceRef sdref) {
834     int ret;
835     const char *real_type;
836     
837     assert(sdref);
838     assert(sdref->n_ref >= 1);
839
840     real_type = avahi_get_type_from_subtype(sdref->service_regtype);
841     
842     if ((ret = avahi_entry_group_add_service_strlst(
843         sdref->entry_group,
844         sdref->service_interface,
845         AVAHI_PROTO_UNSPEC,
846         0,
847         sdref->service_name_chosen,
848         real_type ? real_type : sdref->service_regtype,
849         sdref->service_domain,
850         sdref->service_host,
851         sdref->service_port,
852         sdref->service_txt)) < 0)
853         return ret;
854
855     
856     if (real_type) {
857         /* Create a subtype entry */
858
859         if (avahi_entry_group_add_service_subtype(
860                 sdref->entry_group,
861                 sdref->service_interface,
862                 AVAHI_PROTO_UNSPEC,
863                 0,
864                 sdref->service_name_chosen,
865                 real_type,
866                 sdref->service_domain,
867                 sdref->service_regtype) < 0)
868             return ret;
869
870     }
871
872     if ((ret = avahi_entry_group_commit(sdref->entry_group)) < 0)
873         return ret;
874
875     return 0;
876 }
877
878 static void reg_client_callback(AvahiClient *s, AvahiClientState state, void* userdata) {
879     DNSServiceRef sdref = userdata;
880
881     assert(s);
882     assert(sdref);
883     assert(sdref->n_ref >= 1);
884
885     /* We've not been setup completely */
886     if (!sdref->entry_group)
887         return;
888     
889     switch (state) {
890         case AVAHI_CLIENT_DISCONNECTED: 
891
892             reg_report_error(sdref, kDNSServiceErr_NoError);
893             break;
894         
895         case AVAHI_CLIENT_S_RUNNING: {
896             int ret;
897
898             if (!sdref->service_name) {
899                 const char *n;
900                 /* If the service name is taken from the host name, copy that */
901
902                 avahi_free(sdref->service_name_chosen);
903                 sdref->service_name_chosen = NULL;
904
905                 if (!(n = avahi_client_get_host_name(sdref->client))) {
906                     reg_report_error(sdref, map_error(avahi_client_errno(sdref->client)));
907                     return;
908                 }
909
910                 if (!(sdref->service_name_chosen = avahi_strdup(n))) {
911                     reg_report_error(sdref, kDNSServiceErr_NoMemory);
912                     return;
913                 }
914             }
915             
916             /* Register the service */
917
918             if ((ret = reg_create_service(sdref)) < 0) {
919                 reg_report_error(sdref, map_error(ret));
920                 return;
921             }
922             
923             break;
924         }
925             
926         case AVAHI_CLIENT_S_COLLISION:
927
928             /* Remove our entry */
929             avahi_entry_group_reset(sdref->entry_group);
930             
931             break;
932
933         case AVAHI_CLIENT_S_INVALID:
934         case AVAHI_CLIENT_S_REGISTERING:
935             /* Ignore */
936             break;
937     }
938
939 }
940
941 static void reg_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
942     DNSServiceRef sdref = userdata;
943
944     assert(g);
945
946     switch (state) {
947         case AVAHI_ENTRY_GROUP_ESTABLISHED:
948             /* Inform the user */
949             reg_report_error(sdref, kDNSServiceErr_NoError);
950
951             break;
952
953         case AVAHI_ENTRY_GROUP_COLLISION: {
954             char *n;
955             int ret;
956             
957             /* Remove our entry */
958             avahi_entry_group_reset(sdref->entry_group);
959
960             assert(sdref->service_name_chosen);
961
962             /* Pick a new name */
963             if (!(n = avahi_alternative_service_name(sdref->service_name_chosen))) {
964                 reg_report_error(sdref, kDNSServiceErr_NoMemory);
965                 return;
966             }
967             avahi_free(sdref->service_name_chosen);
968             sdref->service_name_chosen = n;
969
970             /* Register the service with that new name */
971             if ((ret = reg_create_service(sdref)) < 0) {
972                 reg_report_error(sdref, map_error(ret));
973                 return;
974             }
975             
976             break;
977         }
978
979         case AVAHI_ENTRY_GROUP_REGISTERING:
980         case AVAHI_ENTRY_GROUP_UNCOMMITED:
981             /* Ignore */
982             break;
983     }
984 }
985
986 DNSServiceErrorType DNSSD_API DNSServiceRegister (
987     DNSServiceRef *ret_sdref,
988     DNSServiceFlags flags,
989     uint32_t interface,
990     const char *name,        
991     const char *regtype,
992     const char *domain,      
993     const char *host,        
994     uint16_t port,
995     uint16_t txtLen,
996     const void *txtRecord,   
997     DNSServiceRegisterReply callback,    
998     void *context) {
999
1000     DNSServiceErrorType ret = kDNSServiceErr_Unknown;
1001     int error;
1002     DNSServiceRef sdref = NULL;
1003
1004     AVAHI_WARN_LINKAGE;
1005
1006     assert(ret_sdref);
1007     assert(callback);
1008     assert(regtype);
1009
1010     if (interface == kDNSServiceInterfaceIndexLocalOnly || flags) {
1011         AVAHI_WARN_UNSUPPORTED;
1012         return kDNSServiceErr_Unsupported;
1013     }
1014
1015     if (!(sdref = sdref_new()))
1016         return kDNSServiceErr_Unknown;
1017
1018     sdref->context = context;
1019     sdref->service_register_callback = callback;
1020
1021     sdref->service_name = avahi_strdup(name);
1022     sdref->service_regtype = regtype ? avahi_normalize_name_strdup(regtype) : NULL;
1023     sdref->service_domain = domain ? avahi_normalize_name_strdup(domain) : NULL;
1024     sdref->service_host = host ? avahi_normalize_name_strdup(host) : NULL;
1025     sdref->service_interface = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
1026     sdref->service_port = ntohs(port);
1027     sdref->service_txt = txtRecord && txtLen > 0 ? avahi_string_list_parse(txtRecord, txtLen) : NULL;
1028
1029     /* Some OOM checking would be cool here */
1030     
1031     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
1032     
1033     if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), reg_client_callback, sdref, &error))) {
1034         ret =  map_error(error);
1035         goto finish;
1036     }
1037
1038     if (!sdref->service_domain) {
1039         const char *d;
1040
1041         if (!(d = avahi_client_get_domain_name(sdref->client))) {
1042             ret = map_error(avahi_client_errno(sdref->client));
1043             goto finish;
1044         }
1045
1046         if (!(sdref->service_domain = avahi_strdup(d))) {
1047             ret = kDNSServiceErr_NoMemory;
1048             goto finish;
1049         }
1050     }
1051
1052     if (!(sdref->entry_group = avahi_entry_group_new(sdref->client, reg_entry_group_callback, sdref))) {
1053         ret = map_error(avahi_client_errno(sdref->client));
1054         goto finish;
1055     }
1056
1057     if (avahi_client_get_state(sdref->client) == AVAHI_CLIENT_S_RUNNING) {
1058         const char *n;
1059
1060         if (sdref->service_name)
1061             n = sdref->service_name;
1062         else {
1063             if (!(n = avahi_client_get_host_name(sdref->client))) {
1064                 ret = map_error(avahi_client_errno(sdref->client));
1065                 goto finish;
1066             }
1067         }
1068
1069         if (!(sdref->service_name_chosen = avahi_strdup(n))) {
1070             ret = kDNSServiceErr_NoMemory;
1071             goto finish;
1072         }
1073
1074             
1075         if ((error = reg_create_service(sdref)) < 0) {
1076             ret = map_error(error);
1077             goto finish;
1078         }
1079     }
1080     
1081     ret = kDNSServiceErr_NoError;
1082     *ret_sdref = sdref;
1083                                                               
1084 finish:
1085
1086     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
1087     
1088     if (ret != kDNSServiceErr_NoError)
1089         DNSServiceRefDeallocate(sdref);
1090
1091     return ret;
1092 }
1093