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