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