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