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