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