]> git.meshlink.io Git - catta/blob - compat-bonjour/compat.c
* Implement DNSServiceEnumerateDomains()
[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-client/client.h>
40
41 #include "warn.h"
42 #include "dns_sd.h"
43
44 enum {
45     COMMAND_POLL = 'P',
46     COMMAND_QUIT = 'Q',
47     COMMAND_POLLED = 'D'
48 };
49
50 struct _DNSServiceRef_t {
51     AvahiSimplePoll *simple_poll;
52
53     int thread_fd, main_fd;
54
55     pthread_t thread;
56     int thread_running;
57
58     pthread_mutex_t mutex;
59
60     void *context;
61     DNSServiceBrowseReply service_browser_callback;
62     DNSServiceResolveReply service_resolver_callback;
63     DNSServiceDomainEnumReply domain_browser_callback;
64
65     AvahiClient *client;
66     AvahiServiceBrowser *service_browser;
67     AvahiServiceResolver *service_resolver;
68     AvahiDomainBrowser *domain_browser;
69 };
70
71 #define ASSERT_SUCCESS(r) { int __ret = (r); assert(__ret == 0); }
72
73 static int read_command(int fd) {
74     ssize_t r;
75     char command;
76
77     assert(fd >= 0);
78     
79     if ((r = read(fd, &command, 1)) != 1) {
80         fprintf(stderr, __FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
81         return -1;
82     }
83
84     return command;
85 }
86
87 static int write_command(int fd, char reply) {
88     assert(fd >= 0);
89
90     if (write(fd, &reply, 1) != 1) {
91         fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno));
92         return -1;
93     }
94
95     return 0;
96 }
97
98 static int poll_func(struct pollfd *ufds, unsigned int nfds, int timeout, void *userdata) {
99     DNSServiceRef sdref = userdata;
100     int ret;
101     
102     assert(sdref);
103     
104     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
105
106 /*     fprintf(stderr, "pre-syscall\n"); */
107     ret = poll(ufds, nfds, timeout);
108 /*     fprintf(stderr, "post-syscall\n"); */
109     
110     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
111
112     return ret;
113 }
114
115 static void * thread_func(void *data) {
116     DNSServiceRef sdref = data;
117     sigset_t mask;
118
119     sigfillset(&mask);
120     pthread_sigmask(SIG_BLOCK, &mask, NULL);
121     
122     sdref->thread = pthread_self();
123     sdref->thread_running = 1;
124
125     for (;;) {
126         char command;
127
128         if ((command = read_command(sdref->thread_fd)) < 0)
129             break;
130
131 /*         fprintf(stderr, "Command: %c\n", command); */
132         
133         switch (command) {
134
135             case COMMAND_POLL:
136
137                 ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
138                     
139                 
140                 if (avahi_simple_poll_run(sdref->simple_poll) < 0) {
141                     fprintf(stderr, __FILE__": avahi_simple_poll_run() failed.\n");
142                     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
143                     break;
144                 }
145
146                 if (write_command(sdref->thread_fd, COMMAND_POLLED) < 0) {
147                     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
148                     break;
149                 }
150
151                 ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
152                 
153                 break;
154
155             case COMMAND_QUIT:
156                 return NULL;
157         }
158         
159     }
160
161     return NULL;
162 }
163
164 static DNSServiceRef sdref_new(void) {
165     int fd[2] = { -1, -1 };
166     DNSServiceRef sdref = NULL;
167     pthread_mutexattr_t mutex_attr;
168
169     if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0)
170         goto fail;
171
172     if (!(sdref = avahi_new(struct _DNSServiceRef_t, 1)))
173         goto fail;
174
175     sdref->thread_fd = fd[0];
176     sdref->main_fd = fd[1];
177
178     sdref->client = NULL;
179     sdref->service_browser = NULL;
180     sdref->service_resolver = NULL;
181     sdref->domain_browser = NULL;
182
183     ASSERT_SUCCESS(pthread_mutexattr_init(&mutex_attr));
184     pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);
185     ASSERT_SUCCESS(pthread_mutex_init(&sdref->mutex, NULL));
186
187     sdref->thread_running = 0;
188
189     if (!(sdref->simple_poll = avahi_simple_poll_new()))
190         goto fail;
191
192     avahi_simple_poll_set_func(sdref->simple_poll, poll_func, sdref);
193
194     /* Start simple poll */
195     if (avahi_simple_poll_prepare(sdref->simple_poll, -1) < 0)
196         goto fail;
197
198     /* Queue a initiall POLL command for the thread */
199     if (write_command(sdref->main_fd, COMMAND_POLL) < 0)
200         goto fail;
201     
202     if (pthread_create(&sdref->thread, NULL, thread_func, sdref) != 0)
203         goto fail;
204
205     sdref->thread_running = 1;
206     
207     return sdref;
208
209 fail:
210
211     if (sdref)
212         DNSServiceRefDeallocate(sdref);
213
214     return NULL;
215 }
216
217 int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef) {
218     assert(sdRef);
219
220     AVAHI_WARN_LINKAGE;
221     
222     return sdRef->main_fd;
223 }
224
225 DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdref) {
226     DNSServiceErrorType ret = kDNSServiceErr_Unknown;
227
228     AVAHI_WARN_LINKAGE;
229
230     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
231     
232     /* Cleanup notification socket */
233     if (read_command(sdref->main_fd) != COMMAND_POLLED)
234         goto finish;
235     
236     if (avahi_simple_poll_dispatch(sdref->simple_poll) < 0)
237         goto finish;
238     
239     if (avahi_simple_poll_prepare(sdref->simple_poll, -1) < 0)
240         goto finish;
241
242     /* Request the poll */
243     if (write_command(sdref->main_fd, COMMAND_POLL) < 0)
244         goto finish;
245     
246     ret = kDNSServiceErr_NoError;
247     
248 finish:
249
250     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
251     
252     return ret;
253 }
254
255 void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdref) {
256     assert(sdref);
257
258     AVAHI_WARN_LINKAGE;
259
260     if (sdref->thread_running) {
261         write_command(sdref->main_fd, COMMAND_QUIT);
262         avahi_simple_poll_wakeup(sdref->simple_poll);
263         pthread_join(sdref->thread, NULL);
264     }
265
266     if (sdref->client)
267         avahi_client_free(sdref->client);
268
269     if (sdref->thread_fd >= 0)
270         close(sdref->thread_fd);
271
272     if (sdref->main_fd >= 0)
273         close(sdref->main_fd);
274
275     if (sdref->simple_poll)
276         avahi_simple_poll_free(sdref->simple_poll);
277
278     pthread_mutex_destroy(&sdref->mutex);
279     
280     avahi_free(sdref);
281 }
282
283 static DNSServiceErrorType map_error(int error) {
284     switch (error) {
285         case AVAHI_OK :
286             return kDNSServiceErr_NoError;
287             
288         case AVAHI_ERR_BAD_STATE :
289             return kDNSServiceErr_BadState;
290             
291         case AVAHI_ERR_INVALID_HOST_NAME:
292         case AVAHI_ERR_INVALID_DOMAIN_NAME:
293         case AVAHI_ERR_INVALID_TTL:
294         case AVAHI_ERR_IS_PATTERN:
295         case AVAHI_ERR_INVALID_RECORD:
296         case AVAHI_ERR_INVALID_SERVICE_NAME:
297         case AVAHI_ERR_INVALID_SERVICE_TYPE:
298         case AVAHI_ERR_INVALID_PORT:
299         case AVAHI_ERR_INVALID_KEY:
300         case AVAHI_ERR_INVALID_ADDRESS:
301             return kDNSServiceErr_BadParam;
302
303
304         case AVAHI_ERR_LOCAL_COLLISION:
305             return kDNSServiceErr_NameConflict;
306
307         case AVAHI_ERR_TOO_MANY_CLIENTS:
308         case AVAHI_ERR_TOO_MANY_OBJECTS:
309         case AVAHI_ERR_TOO_MANY_ENTRIES:
310         case AVAHI_ERR_ACCESS_DENIED:
311             return kDNSServiceErr_Refused;
312
313         case AVAHI_ERR_INVALID_OPERATION:
314         case AVAHI_ERR_INVALID_OBJECT:
315             return kDNSServiceErr_Invalid;
316
317         case AVAHI_ERR_NO_MEMORY:
318             return kDNSServiceErr_NoMemory;
319
320         case AVAHI_ERR_INVALID_INTERFACE:
321         case AVAHI_ERR_INVALID_PROTOCOL:
322             return kDNSServiceErr_BadInterfaceIndex;
323         
324         case AVAHI_ERR_INVALID_FLAGS:
325             return kDNSServiceErr_BadFlags;
326             
327         case AVAHI_ERR_NOT_FOUND:
328             return kDNSServiceErr_NoSuchName;
329             
330         case AVAHI_ERR_VERSION_MISMATCH:
331             return kDNSServiceErr_Incompatible;
332
333         case AVAHI_ERR_NO_NETWORK:
334         case AVAHI_ERR_OS:
335         case AVAHI_ERR_INVALID_CONFIG:
336         case AVAHI_ERR_TIMEOUT:
337         case AVAHI_ERR_DBUS_ERROR:
338         case AVAHI_ERR_NOT_CONNECTED:
339         case AVAHI_ERR_NO_DAEMON:
340             break;
341
342     }
343
344     return kDNSServiceErr_Unknown;
345 }
346
347 static void service_browser_callback(
348     AvahiServiceBrowser *b,
349     AvahiIfIndex interface,
350     AvahiProtocol protocol,
351     AvahiBrowserEvent event,
352     const char *name,
353     const char *type,
354     const char *domain,
355     AvahiLookupResultFlags flags,
356     void *userdata) {
357
358     DNSServiceRef sdref = userdata;
359
360     assert(b);
361     assert(sdref);
362
363     switch (event) {
364         case AVAHI_BROWSER_NEW:
365             sdref->service_browser_callback(sdref, kDNSServiceFlagsAdd, interface, kDNSServiceErr_NoError, name, type, domain, sdref->context);
366             break;
367
368         case AVAHI_BROWSER_REMOVE:
369             sdref->service_browser_callback(sdref, 0, interface, kDNSServiceErr_NoError, name, type, domain, sdref->context);
370             break;
371
372         case AVAHI_BROWSER_FAILURE:
373             sdref->service_browser_callback(sdref, 0, interface, kDNSServiceErr_Unknown, name, type, domain, sdref->context);
374             break;
375             
376         case AVAHI_BROWSER_NOT_FOUND:
377             sdref->service_browser_callback(sdref, 0, interface, kDNSServiceErr_NoSuchName, name, type, domain, sdref->context);
378             break;
379             
380         case AVAHI_BROWSER_CACHE_EXHAUSTED:
381         case AVAHI_BROWSER_ALL_FOR_NOW:
382             break;
383     }
384 }
385
386 DNSServiceErrorType DNSSD_API DNSServiceBrowse(
387     DNSServiceRef *ret_sdref,
388     DNSServiceFlags flags,
389     uint32_t interface,
390     const char *regtype,
391     const char *domain,
392     DNSServiceBrowseReply callback,
393     void *context) {
394
395     DNSServiceErrorType ret = kDNSServiceErr_Unknown;
396     int error;
397     DNSServiceRef sdref = NULL;
398     AvahiIfIndex ifindex;
399     
400     AVAHI_WARN_LINKAGE;
401     
402     assert(ret_sdref);
403     assert(regtype);
404     assert(domain);
405     assert(callback);
406
407     if (interface == kDNSServiceInterfaceIndexLocalOnly || flags != 0)
408         return kDNSServiceErr_Unsupported;
409
410     if (!(sdref = sdref_new()))
411         return kDNSServiceErr_Unknown;
412
413     sdref->context = context;
414     sdref->service_browser_callback = callback;
415
416     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
417     
418     if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), NULL, NULL, &error))) {
419         ret =  map_error(error);
420         goto finish;
421     }
422
423     ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
424     
425     if (!(sdref->service_browser = avahi_service_browser_new(sdref->client, ifindex, AVAHI_PROTO_UNSPEC, regtype, domain, 0, service_browser_callback, sdref))) {
426         ret = map_error(avahi_client_errno(sdref->client));
427         goto finish;
428     }
429     
430
431     ret = kDNSServiceErr_NoError;
432     *ret_sdref = sdref;
433                                                               
434 finish:
435
436     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
437     
438     if (ret != kDNSServiceErr_NoError)
439         DNSServiceRefDeallocate(sdref);
440
441     return ret;
442 }
443
444 static void service_resolver_callback(
445     AvahiServiceResolver *r,
446     AvahiIfIndex interface,
447     AvahiProtocol protocol,
448     AvahiResolverEvent event,
449     const char *name,
450     const char *type,
451     const char *domain,
452     const char *host_name,
453     const AvahiAddress *a,
454     uint16_t port,
455     AvahiStringList *txt,
456     AvahiLookupResultFlags flags,
457     void *userdata) {
458
459     DNSServiceRef sdref = userdata;
460
461     assert(r);
462     assert(sdref);
463
464     switch (event) {
465         case AVAHI_RESOLVER_FOUND: {
466
467             char full_name[kDNSServiceMaxDomainName];
468             int ret;
469             char *p = NULL;
470             size_t l = 0;
471
472             if ((p = avahi_new0(char, (l = avahi_string_list_serialize(txt, NULL, 0))+1)))
473                 avahi_string_list_serialize(txt, p, l);
474
475             ret = avahi_service_name_snprint(full_name, sizeof(full_name), name, type, domain);
476             assert(ret == AVAHI_OK);
477             
478             sdref->service_resolver_callback(sdref, 0, interface, kDNSServiceErr_NoError, full_name, host_name, htons(port), l, p, sdref->context);
479
480             avahi_free(p);
481             break;
482         }
483
484         case AVAHI_RESOLVER_TIMEOUT:
485         case AVAHI_RESOLVER_NOT_FOUND:
486             sdref->service_resolver_callback(sdref, 0, interface, kDNSServiceErr_NoSuchName, NULL, NULL, 0, 0, NULL, sdref->context);
487             break;
488             
489         case AVAHI_RESOLVER_FAILURE:
490             sdref->service_resolver_callback(sdref, 0, interface, kDNSServiceErr_Unknown, NULL, NULL, 0, 0, NULL, sdref->context);
491             
492     }
493 }
494
495 DNSServiceErrorType DNSSD_API DNSServiceResolve(
496     DNSServiceRef *ret_sdref,
497     DNSServiceFlags flags,
498     uint32_t interface,
499     const char *name,
500     const char *regtype,
501     const char *domain,
502     DNSServiceResolveReply callback,
503     void *context) {
504
505     DNSServiceErrorType ret = kDNSServiceErr_Unknown;
506     int error;
507     DNSServiceRef sdref = NULL;
508     AvahiIfIndex ifindex;
509
510     AVAHI_WARN_LINKAGE;
511
512     assert(ret_sdref);
513     assert(name);
514     assert(regtype);
515     assert(domain);
516     assert(callback);
517
518     if (interface == kDNSServiceInterfaceIndexLocalOnly || flags != 0)
519         return kDNSServiceErr_Unsupported;
520
521     if (!(sdref = sdref_new()))
522         return kDNSServiceErr_Unknown;
523
524     sdref->context = context;
525     sdref->service_resolver_callback = callback;
526
527     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
528     
529     if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), NULL, NULL, &error))) {
530         ret =  map_error(error);
531         goto finish;
532     }
533
534     ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
535     
536     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))) {
537         ret = map_error(avahi_client_errno(sdref->client));
538         goto finish;
539     }
540     
541
542     ret = kDNSServiceErr_NoError;
543     *ret_sdref = sdref;
544                                                               
545 finish:
546
547     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
548     
549     if (ret != kDNSServiceErr_NoError)
550         DNSServiceRefDeallocate(sdref);
551
552     return ret;
553 }
554
555 int DNSSD_API DNSServiceConstructFullName (
556     char *fullName,
557     const char *service,   
558     const char *regtype,
559     const char *domain) {
560
561     AVAHI_WARN_LINKAGE;
562
563     assert(fullName);
564     assert(regtype);
565     assert(domain);
566
567     if (avahi_service_name_snprint(fullName, kDNSServiceMaxDomainName, service, regtype, domain) < 0)
568         return -1;
569     
570     return 0;
571 }
572
573 static void domain_browser_callback(
574     AvahiDomainBrowser *b,
575     AvahiIfIndex interface,
576     AvahiProtocol protocol,
577     AvahiBrowserEvent event,
578     const char *domain,
579     AvahiLookupResultFlags flags,
580     void *userdata) {
581
582     DNSServiceRef sdref = userdata;
583
584     assert(b);
585     assert(sdref);
586
587     switch (event) {
588         case AVAHI_BROWSER_NEW:
589             sdref->domain_browser_callback(sdref, kDNSServiceFlagsAdd, interface, kDNSServiceErr_NoError, domain, sdref->context);
590             break;
591
592         case AVAHI_BROWSER_REMOVE:
593             sdref->domain_browser_callback(sdref, 0, interface, kDNSServiceErr_NoError, domain, sdref->context);
594             break;
595
596         case AVAHI_BROWSER_FAILURE:
597             sdref->domain_browser_callback(sdref, 0, interface, kDNSServiceErr_Unknown, domain, sdref->context);
598             break;
599             
600         case AVAHI_BROWSER_NOT_FOUND:
601             sdref->domain_browser_callback(sdref, 0, interface, kDNSServiceErr_NoSuchName, domain, sdref->context);
602             break;
603             
604         case AVAHI_BROWSER_CACHE_EXHAUSTED:
605         case AVAHI_BROWSER_ALL_FOR_NOW:
606             break;
607     }
608 }
609
610 DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains(
611     DNSServiceRef *ret_sdref,
612     DNSServiceFlags flags,
613     uint32_t interface,
614     DNSServiceDomainEnumReply callback,
615     void *context) {
616
617     DNSServiceErrorType ret = kDNSServiceErr_Unknown;
618     int error;
619     DNSServiceRef sdref = NULL;
620     AvahiIfIndex ifindex;
621
622     AVAHI_WARN_LINKAGE;
623
624     assert(ret_sdref);
625     assert(callback);
626
627     if (interface == kDNSServiceInterfaceIndexLocalOnly ||
628         (flags != kDNSServiceFlagsBrowseDomains &&  flags != kDNSServiceFlagsRegistrationDomains))
629         return kDNSServiceErr_Unsupported;
630
631     if (!(sdref = sdref_new()))
632         return kDNSServiceErr_Unknown;
633
634     sdref->context = context;
635     sdref->domain_browser_callback = callback;
636
637     ASSERT_SUCCESS(pthread_mutex_lock(&sdref->mutex));
638     
639     if (!(sdref->client = avahi_client_new(avahi_simple_poll_get(sdref->simple_poll), NULL, NULL, &error))) {
640         ret =  map_error(error);
641         goto finish;
642     }
643
644     ifindex = interface == kDNSServiceInterfaceIndexAny ? AVAHI_IF_UNSPEC : (AvahiIfIndex) interface;
645     
646     if (!(sdref->domain_browser = avahi_domain_browser_new(sdref->client, ifindex, AVAHI_PROTO_UNSPEC, NULL,
647                                                            flags == kDNSServiceFlagsRegistrationDomains ? AVAHI_DOMAIN_BROWSER_REGISTER : AVAHI_DOMAIN_BROWSER_BROWSE,
648                                                            0, domain_browser_callback, sdref))) {
649         ret = map_error(avahi_client_errno(sdref->client));
650         goto finish;
651     }
652     
653     ret = kDNSServiceErr_NoError;
654     *ret_sdref = sdref;
655                                                               
656 finish:
657
658     ASSERT_SUCCESS(pthread_mutex_unlock(&sdref->mutex));
659     
660     if (ret != kDNSServiceErr_NoError)
661         DNSServiceRefDeallocate(sdref);
662
663     return ret;
664 }
665