]> git.meshlink.io Git - catta/blob - avahi-compat-libdns_sd/compat.c
set O_NONBLOCK for expoted libdns_sd sockets
[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     assert(sdref);
456     assert(sdref->n_ref >= 1);
457
458     AVAHI_WARN_LINKAGE;
459
460     sdref_unref(sdref);
461 }
462
463 static void service_browser_callback(
464     AvahiServiceBrowser *b,
465     AvahiIfIndex interface,
466     AvahiProtocol protocol,
467     AvahiBrowserEvent event,
468     const char *name,
469     const char *type,
470     const char *domain,
471     AvahiLookupResultFlags flags,
472     void *userdata) {
473
474     DNSServiceRef sdref = userdata;
475     char type_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX];
476     assert(b);
477     assert(sdref);
478     assert(sdref->n_ref >= 1);
479
480     type = add_trailing_dot(type, type_fixed, sizeof(type_fixed));
481     domain  = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
482     
483     switch (event) {
484         case AVAHI_BROWSER_NEW:
485             sdref->service_browser_callback(sdref, kDNSServiceFlagsAdd, interface, kDNSServiceErr_NoError, name, type, domain, sdref->context);
486             break;
487
488         case AVAHI_BROWSER_REMOVE:
489             sdref->service_browser_callback(sdref, 0, interface, kDNSServiceErr_NoError, name, type, domain, sdref->context);
490             break;
491
492         case AVAHI_BROWSER_FAILURE:
493             sdref->service_browser_callback(sdref, 0, interface, map_error(avahi_client_errno(sdref->client)), NULL, NULL, NULL, sdref->context);
494             break;
495             
496         case AVAHI_BROWSER_CACHE_EXHAUSTED:
497         case AVAHI_BROWSER_ALL_FOR_NOW:
498             break;
499     }
500 }
501
502 static void generic_client_callback(AvahiClient *s, AvahiClientState state, void* userdata) {
503     DNSServiceRef sdref = userdata;
504
505     assert(s);
506     assert(sdref);
507     assert(sdref->n_ref >= 1);
508
509     switch (state) {
510         case AVAHI_CLIENT_DISCONNECTED: {
511
512             if (sdref->service_browser_callback)
513                 sdref->service_browser_callback(sdref, 0, 0, kDNSServiceErr_Unknown, NULL, NULL, NULL, sdref->context);
514             else if (sdref->service_resolver_callback)
515                 sdref->service_resolver_callback(sdref, 0, 0, kDNSServiceErr_Unknown, NULL, NULL, 0, 0, NULL, sdref->context);
516             else if (sdref->domain_browser_callback)
517                 sdref->domain_browser_callback(sdref, 0, 0, kDNSServiceErr_Unknown, NULL, sdref->context);
518
519             break;
520         }
521
522         case AVAHI_CLIENT_S_RUNNING:
523         case AVAHI_CLIENT_S_COLLISION:
524         case AVAHI_CLIENT_S_INVALID:
525         case AVAHI_CLIENT_S_REGISTERING:
526             break;
527     }
528 }
529
530 DNSServiceErrorType DNSSD_API DNSServiceBrowse(
531     DNSServiceRef *ret_sdref,
532     DNSServiceFlags flags,
533     uint32_t interface,
534     const char *regtype,
535     const char *domain,
536     DNSServiceBrowseReply callback,
537     void *context) {
538
539     DNSServiceErrorType ret = kDNSServiceErr_Unknown;
540     int error;
541     DNSServiceRef sdref = NULL;
542     AvahiIfIndex ifindex;
543     
544     AVAHI_WARN_LINKAGE;
545     
546     assert(ret_sdref);
547     assert(regtype);
548     assert(domain);
549     assert(callback);
550
551     if (interface == kDNSServiceInterfaceIndexLocalOnly || flags != 0) {
552         AVAHI_WARN_UNSUPPORTED;
553         return kDNSServiceErr_Unsupported;
554     }
555
556     if (!(sdref = sdref_new()))
557         return kDNSServiceErr_Unknown;
558
559     sdref->context = context;
560     sdref->service_browser_callback = callback;
561
562     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
563     
564     if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), generic_client_callback, sdref, &error))) {
565         ret =  map_error(error);
566         goto finish;
567     }
568
569     ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
570     
571     if (!(sdref->service_browser = avahi_service_browser_new(sdref->client, ifindex, AVAHI_PROTO_UNSPEC, regtype, domain, 0, service_browser_callback, sdref))) {
572         ret = map_error(avahi_client_errno(sdref->client));
573         goto finish;
574     }
575     
576     ret = kDNSServiceErr_NoError;
577     *ret_sdref = sdref;
578                                                               
579 finish:
580
581     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
582     
583     if (ret != kDNSServiceErr_NoError)
584         DNSServiceRefDeallocate(sdref);
585
586     return ret;
587 }
588
589 static void service_resolver_callback(
590     AvahiServiceResolver *r,
591     AvahiIfIndex interface,
592     AvahiProtocol protocol,
593     AvahiResolverEvent event,
594     const char *name,
595     const char *type,
596     const char *domain,
597     const char *host_name,
598     const AvahiAddress *a,
599     uint16_t port,
600     AvahiStringList *txt,
601     AvahiLookupResultFlags flags,
602     void *userdata) {
603
604     DNSServiceRef sdref = userdata;
605
606     assert(r);
607     assert(sdref);
608     assert(sdref->n_ref >= 1);
609
610     switch (event) {
611         case AVAHI_RESOLVER_FOUND: {
612
613             char host_name_fixed[AVAHI_DOMAIN_NAME_MAX];
614             char full_name[AVAHI_DOMAIN_NAME_MAX];
615             int ret;
616             char *p = NULL;
617             size_t l = 0;
618
619             host_name = add_trailing_dot(host_name, host_name_fixed, sizeof(host_name_fixed));
620
621             if ((p = avahi_new0(char, (l = avahi_string_list_serialize(txt, NULL, 0))+1)))
622                 avahi_string_list_serialize(txt, p, l);
623
624             ret = avahi_service_name_join(full_name, sizeof(full_name), name, type, domain);
625             assert(ret == AVAHI_OK);
626
627             strcat(full_name, ".");
628             
629             sdref->service_resolver_callback(sdref, 0, interface, kDNSServiceErr_NoError, full_name, host_name, htons(port), l, p, sdref->context);
630
631             avahi_free(p);
632             break;
633         }
634
635         case AVAHI_RESOLVER_FAILURE:
636             sdref->service_resolver_callback(sdref, 0, interface, map_error(avahi_client_errno(sdref->client)), NULL, NULL, 0, 0, NULL, sdref->context);
637             break;
638     }
639 }
640
641 DNSServiceErrorType DNSSD_API DNSServiceResolve(
642     DNSServiceRef *ret_sdref,
643     DNSServiceFlags flags,
644     uint32_t interface,
645     const char *name,
646     const char *regtype,
647     const char *domain,
648     DNSServiceResolveReply callback,
649     void *context) {
650
651     DNSServiceErrorType ret = kDNSServiceErr_Unknown;
652     int error;
653     DNSServiceRef sdref = NULL;
654     AvahiIfIndex ifindex;
655
656     AVAHI_WARN_LINKAGE;
657
658     assert(ret_sdref);
659     assert(name);
660     assert(regtype);
661     assert(domain);
662     assert(callback);
663
664     if (interface == kDNSServiceInterfaceIndexLocalOnly || flags != 0) {
665         AVAHI_WARN_UNSUPPORTED;
666         return kDNSServiceErr_Unsupported;
667     }
668
669     if (!(sdref = sdref_new()))
670         return kDNSServiceErr_Unknown;
671
672     sdref->context = context;
673     sdref->service_resolver_callback = callback;
674
675     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
676     
677     if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), generic_client_callback, sdref, &error))) {
678         ret =  map_error(error);
679         goto finish;
680     }
681
682     ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
683     
684     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))) {
685         ret = map_error(avahi_client_errno(sdref->client));
686         goto finish;
687     }
688     
689
690     ret = kDNSServiceErr_NoError;
691     *ret_sdref = sdref;
692                                                               
693 finish:
694
695     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
696     
697     if (ret != kDNSServiceErr_NoError)
698         DNSServiceRefDeallocate(sdref);
699
700     return ret;
701 }
702
703 int DNSSD_API DNSServiceConstructFullName (
704     char *fullName,
705     const char *service,   
706     const char *regtype,
707     const char *domain) {
708
709     AVAHI_WARN_LINKAGE;
710
711     assert(fullName);
712     assert(regtype);
713     assert(domain);
714
715     if (avahi_service_name_join(fullName, kDNSServiceMaxDomainName, service, regtype, domain) < 0)
716         return -1;
717     
718     return 0;
719 }
720
721 static void domain_browser_callback(
722     AvahiDomainBrowser *b,
723     AvahiIfIndex interface,
724     AvahiProtocol protocol,
725     AvahiBrowserEvent event,
726     const char *domain,
727     AvahiLookupResultFlags flags,
728     void *userdata) {
729
730     DNSServiceRef sdref = userdata;
731     static char domain_fixed[AVAHI_DOMAIN_NAME_MAX];
732
733     assert(b);
734     assert(sdref);
735     assert(sdref->n_ref >= 1);
736
737     domain  = add_trailing_dot(domain, domain_fixed, sizeof(domain_fixed));
738
739     switch (event) {
740         case AVAHI_BROWSER_NEW:
741             sdref->domain_browser_callback(sdref, kDNSServiceFlagsAdd, interface, kDNSServiceErr_NoError, domain, sdref->context);
742             break;
743
744         case AVAHI_BROWSER_REMOVE:
745             sdref->domain_browser_callback(sdref, 0, interface, kDNSServiceErr_NoError, domain, sdref->context);
746             break;
747
748         case AVAHI_BROWSER_FAILURE:
749             sdref->domain_browser_callback(sdref, 0, interface, map_error(avahi_client_errno(sdref->client)), domain, sdref->context);
750             break;
751             
752         case AVAHI_BROWSER_CACHE_EXHAUSTED:
753         case AVAHI_BROWSER_ALL_FOR_NOW:
754             break;
755     }
756 }
757
758 DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains(
759     DNSServiceRef *ret_sdref,
760     DNSServiceFlags flags,
761     uint32_t interface,
762     DNSServiceDomainEnumReply callback,
763     void *context) {
764
765     DNSServiceErrorType ret = kDNSServiceErr_Unknown;
766     int error;
767     DNSServiceRef sdref = NULL;
768     AvahiIfIndex ifindex;
769
770     AVAHI_WARN_LINKAGE;
771
772     assert(ret_sdref);
773     assert(callback);
774
775     if (interface == kDNSServiceInterfaceIndexLocalOnly ||
776         (flags != kDNSServiceFlagsBrowseDomains &&  flags != kDNSServiceFlagsRegistrationDomains)) {
777         AVAHI_WARN_UNSUPPORTED;
778         return kDNSServiceErr_Unsupported;
779     }
780
781     if (!(sdref = sdref_new()))
782         return kDNSServiceErr_Unknown;
783
784     sdref->context = context;
785     sdref->domain_browser_callback = callback;
786
787     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
788     
789     if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), generic_client_callback, sdref, &error))) {
790         ret =  map_error(error);
791         goto finish;
792     }
793
794     ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
795     
796     if (!(sdref->domain_browser = avahi_domain_browser_new(sdref->client, ifindex, AVAHI_PROTO_UNSPEC, "local",
797                                                            flags == kDNSServiceFlagsRegistrationDomains ? AVAHI_DOMAIN_BROWSER_REGISTER : AVAHI_DOMAIN_BROWSER_BROWSE,
798                                                            0, domain_browser_callback, sdref))) {
799         ret = map_error(avahi_client_errno(sdref->client));
800         goto finish;
801     }
802     
803     ret = kDNSServiceErr_NoError;
804     *ret_sdref = sdref;
805                                                               
806 finish:
807
808     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
809     
810     if (ret != kDNSServiceErr_NoError)
811         DNSServiceRefDeallocate(sdref);
812
813     return ret;
814 }
815
816 static void reg_report_error(DNSServiceRef sdref, DNSServiceErrorType error) {
817     char regtype_fixed[AVAHI_DOMAIN_NAME_MAX], domain_fixed[AVAHI_DOMAIN_NAME_MAX];
818     const char *regtype, *domain;
819     assert(sdref);
820     assert(sdref->n_ref >= 1);
821
822     assert(sdref->service_register_callback);
823
824     regtype = add_trailing_dot(sdref->service_regtype, regtype_fixed, sizeof(regtype_fixed));
825     domain = add_trailing_dot(sdref->service_domain, domain_fixed, sizeof(domain_fixed));
826     
827     sdref->service_register_callback(
828         sdref, 0, error,
829         sdref->service_name_chosen ? sdref->service_name_chosen : sdref->service_name,
830         regtype,
831         domain,
832         sdref->context);
833 }
834
835 static int reg_create_service(DNSServiceRef sdref) {
836     int ret;
837     const char *real_type;
838     
839     assert(sdref);
840     assert(sdref->n_ref >= 1);
841
842     real_type = avahi_get_type_from_subtype(sdref->service_regtype);
843     
844     if ((ret = avahi_entry_group_add_service_strlst(
845         sdref->entry_group,
846         sdref->service_interface,
847         AVAHI_PROTO_UNSPEC,
848         0,
849         sdref->service_name_chosen,
850         real_type ? real_type : sdref->service_regtype,
851         sdref->service_domain,
852         sdref->service_host,
853         sdref->service_port,
854         sdref->service_txt)) < 0)
855         return ret;
856
857     
858     if (real_type) {
859         /* Create a subtype entry */
860
861         if (avahi_entry_group_add_service_subtype(
862                 sdref->entry_group,
863                 sdref->service_interface,
864                 AVAHI_PROTO_UNSPEC,
865                 0,
866                 sdref->service_name_chosen,
867                 real_type,
868                 sdref->service_domain,
869                 sdref->service_regtype) < 0)
870             return ret;
871
872     }
873
874     if ((ret = avahi_entry_group_commit(sdref->entry_group)) < 0)
875         return ret;
876
877     return 0;
878 }
879
880 static void reg_client_callback(AvahiClient *s, AvahiClientState state, void* userdata) {
881     DNSServiceRef sdref = userdata;
882
883     assert(s);
884     assert(sdref);
885     assert(sdref->n_ref >= 1);
886
887     /* We've not been setup completely */
888     if (!sdref->entry_group)
889         return;
890     
891     switch (state) {
892         case AVAHI_CLIENT_DISCONNECTED: 
893
894             reg_report_error(sdref, kDNSServiceErr_NoError);
895             break;
896         
897         case AVAHI_CLIENT_S_RUNNING: {
898             int ret;
899
900             if (!sdref->service_name) {
901                 const char *n;
902                 /* If the service name is taken from the host name, copy that */
903
904                 avahi_free(sdref->service_name_chosen);
905                 sdref->service_name_chosen = NULL;
906
907                 if (!(n = avahi_client_get_host_name(sdref->client))) {
908                     reg_report_error(sdref, map_error(avahi_client_errno(sdref->client)));
909                     return;
910                 }
911
912                 if (!(sdref->service_name_chosen = avahi_strdup(n))) {
913                     reg_report_error(sdref, kDNSServiceErr_NoMemory);
914                     return;
915                 }
916             }
917             
918             /* Register the service */
919
920             if ((ret = reg_create_service(sdref)) < 0) {
921                 reg_report_error(sdref, map_error(ret));
922                 return;
923             }
924             
925             break;
926         }
927             
928         case AVAHI_CLIENT_S_COLLISION:
929
930             /* Remove our entry */
931             avahi_entry_group_reset(sdref->entry_group);
932             
933             break;
934
935         case AVAHI_CLIENT_S_INVALID:
936         case AVAHI_CLIENT_S_REGISTERING:
937             /* Ignore */
938             break;
939     }
940
941 }
942
943 static void reg_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
944     DNSServiceRef sdref = userdata;
945
946     assert(g);
947
948     switch (state) {
949         case AVAHI_ENTRY_GROUP_ESTABLISHED:
950             /* Inform the user */
951             reg_report_error(sdref, kDNSServiceErr_NoError);
952
953             break;
954
955         case AVAHI_ENTRY_GROUP_COLLISION: {
956             char *n;
957             int ret;
958             
959             /* Remove our entry */
960             avahi_entry_group_reset(sdref->entry_group);
961
962             assert(sdref->service_name_chosen);
963
964             /* Pick a new name */
965             if (!(n = avahi_alternative_service_name(sdref->service_name_chosen))) {
966                 reg_report_error(sdref, kDNSServiceErr_NoMemory);
967                 return;
968             }
969             avahi_free(sdref->service_name_chosen);
970             sdref->service_name_chosen = n;
971
972             /* Register the service with that new name */
973             if ((ret = reg_create_service(sdref)) < 0) {
974                 reg_report_error(sdref, map_error(ret));
975                 return;
976             }
977             
978             break;
979         }
980
981         case AVAHI_ENTRY_GROUP_REGISTERING:
982         case AVAHI_ENTRY_GROUP_UNCOMMITED:
983             /* Ignore */
984             break;
985     }
986 }
987
988 DNSServiceErrorType DNSSD_API DNSServiceRegister (
989     DNSServiceRef *ret_sdref,
990     DNSServiceFlags flags,
991     uint32_t interface,
992     const char *name,        
993     const char *regtype,
994     const char *domain,      
995     const char *host,        
996     uint16_t port,
997     uint16_t txtLen,
998     const void *txtRecord,   
999     DNSServiceRegisterReply callback,    
1000     void *context) {
1001
1002     DNSServiceErrorType ret = kDNSServiceErr_Unknown;
1003     int error;
1004     DNSServiceRef sdref = NULL;
1005
1006     AVAHI_WARN_LINKAGE;
1007
1008     assert(ret_sdref);
1009     assert(callback);
1010     assert(regtype);
1011
1012     if (interface == kDNSServiceInterfaceIndexLocalOnly || flags) {
1013         AVAHI_WARN_UNSUPPORTED;
1014         return kDNSServiceErr_Unsupported;
1015     }
1016
1017     if (!(sdref = sdref_new()))
1018         return kDNSServiceErr_Unknown;
1019
1020     sdref->context = context;
1021     sdref->service_register_callback = callback;
1022
1023     sdref->service_name = avahi_strdup(name);
1024     sdref->service_regtype = regtype ? avahi_normalize_name_strdup(regtype) : NULL;
1025     sdref->service_domain = domain ? avahi_normalize_name_strdup(domain) : NULL;
1026     sdref->service_host = host ? avahi_normalize_name_strdup(host) : NULL;
1027     sdref->service_interface = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
1028     sdref->service_port = ntohs(port);
1029     sdref->service_txt = txtRecord && txtLen > 0 ? avahi_string_list_parse(txtRecord, txtLen) : NULL;
1030
1031     /* Some OOM checking would be cool here */
1032     
1033     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
1034     
1035     if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), reg_client_callback, sdref, &error))) {
1036         ret =  map_error(error);
1037         goto finish;
1038     }
1039
1040     if (!sdref->service_domain) {
1041         const char *d;
1042
1043         if (!(d = avahi_client_get_domain_name(sdref->client))) {
1044             ret = map_error(avahi_client_errno(sdref->client));
1045             goto finish;
1046         }
1047
1048         if (!(sdref->service_domain = avahi_strdup(d))) {
1049             ret = kDNSServiceErr_NoMemory;
1050             goto finish;
1051         }
1052     }
1053
1054     if (!(sdref->entry_group = avahi_entry_group_new(sdref->client, reg_entry_group_callback, sdref))) {
1055         ret = map_error(avahi_client_errno(sdref->client));
1056         goto finish;
1057     }
1058
1059     if (avahi_client_get_state(sdref->client) == AVAHI_CLIENT_S_RUNNING) {
1060         const char *n;
1061
1062         if (sdref->service_name)
1063             n = sdref->service_name;
1064         else {
1065             if (!(n = avahi_client_get_host_name(sdref->client))) {
1066                 ret = map_error(avahi_client_errno(sdref->client));
1067                 goto finish;
1068             }
1069         }
1070
1071         if (!(sdref->service_name_chosen = avahi_strdup(n))) {
1072             ret = kDNSServiceErr_NoMemory;
1073             goto finish;
1074         }
1075
1076             
1077         if ((error = reg_create_service(sdref)) < 0) {
1078             ret = map_error(error);
1079             goto finish;
1080         }
1081     }
1082     
1083     ret = kDNSServiceErr_NoError;
1084     *ret_sdref = sdref;
1085                                                               
1086 finish:
1087
1088     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
1089     
1090     if (ret != kDNSServiceErr_NoError)
1091         DNSServiceRefDeallocate(sdref);
1092
1093     return ret;
1094 }
1095