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