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