]> git.meshlink.io Git - catta/blob - avahi-core/resolve-service.c
347de777bc9280c6138fef2296532005c03e50b2
[catta] / avahi-core / resolve-service.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 <string.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29
30 #include <avahi-common/domain.h>
31 #include <avahi-common/timeval.h>
32 #include <avahi-common/malloc.h>
33 #include <avahi-common/error.h>
34
35 #include "browse.h"
36
37 #define TIMEOUT_MSEC 1000
38
39 struct AvahiSServiceResolver {
40     AvahiServer *server;
41     char *service_name;
42     char *service_type;
43     char *domain_name;
44     AvahiProtocol address_protocol;
45
46     AvahiIfIndex interface;
47     AvahiProtocol protocol;
48
49     AvahiSRecordBrowser *record_browser_srv;
50     AvahiSRecordBrowser *record_browser_txt;
51     AvahiSRecordBrowser *record_browser_a;
52     AvahiSRecordBrowser *record_browser_aaaa;
53
54     AvahiRecord *srv_record, *txt_record, *address_record;
55     
56     AvahiSServiceResolverCallback callback;
57     void* userdata;
58
59     AvahiTimeEvent *time_event;
60
61     AVAHI_LLIST_FIELDS(AvahiSServiceResolver, resolver);
62 };
63
64 static void finish(AvahiSServiceResolver *r, AvahiResolverEvent event) {
65     assert(r);
66
67     if (r->time_event) {
68         avahi_time_event_free(r->time_event);
69         r->time_event = NULL;
70     }
71
72     if (event == AVAHI_RESOLVER_TIMEOUT)
73         r->callback(r, r->interface, r->protocol, event, r->service_name, r->service_type, r->domain_name, NULL, NULL, 0, NULL, r->userdata);
74     else {
75         AvahiAddress a;
76         char sn[256], st[256];
77         size_t i;
78         
79         assert(r->srv_record);
80         assert(r->txt_record);
81         assert(r->address_record);
82         
83         switch (r->address_record->key->type) {
84             case AVAHI_DNS_TYPE_A:
85                 a.family = AVAHI_PROTO_INET;
86                 a.data.ipv4 = r->address_record->data.a.address;
87                 break;
88                 
89             case AVAHI_DNS_TYPE_AAAA:
90                 a.family = AVAHI_PROTO_INET6;
91                 a.data.ipv6 = r->address_record->data.aaaa.address;
92                 break;
93                 
94             default:
95                 assert(0);
96         }
97
98         snprintf(sn, sizeof(sn), r->service_name);
99         snprintf(st, sizeof(st), r->service_type);
100
101         if ((i = strlen(sn)) > 0 && sn[i-1] == '.')
102             sn[i-1] = 0;
103
104         if ((i = strlen(st)) > 0 && st[i-1] == '.')
105             st[i-1] = 0;
106
107         r->callback(r, r->interface, r->protocol, event, sn, st, r->domain_name, r->srv_record->data.srv.name, &a, r->srv_record->data.srv.port, r->txt_record->data.txt.string_list, r->userdata);
108     }
109 }
110
111 static void time_event_callback(AvahiTimeEvent *e, void *userdata) {
112     AvahiSServiceResolver *r = userdata;
113     
114     assert(e);
115     assert(r);
116
117     finish(r, AVAHI_RESOLVER_TIMEOUT);
118 }
119
120 static void start_timeout(AvahiSServiceResolver *r) {
121     struct timeval tv;
122     assert(r);
123
124     if (r->time_event)
125         return;
126     
127     avahi_elapse_time(&tv, TIMEOUT_MSEC, 0);
128     r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r);
129 }
130
131 static void record_browser_callback(AvahiSRecordBrowser*rr, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, void* userdata) {
132     AvahiSServiceResolver *r = userdata;
133
134     assert(rr);
135     assert(record);
136     assert(r);
137
138     if (event == AVAHI_BROWSER_NEW) {
139         int changed = 0;
140
141         if (r->interface > 0 && interface != r->interface)
142             return;
143         
144         if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol)
145             return;
146         
147         if (r->interface <= 0)
148             r->interface = interface;
149         
150         if (r->protocol == AVAHI_PROTO_UNSPEC)
151             r->protocol = protocol;
152         
153         switch (record->key->type) {
154             case AVAHI_DNS_TYPE_SRV:
155                 if (!r->srv_record) {
156                     r->srv_record = avahi_record_ref(record);
157                     changed = 1;
158                     
159                     assert(!r->record_browser_a && !r->record_browser_aaaa);
160                     
161                     if (r->address_protocol == AVAHI_PROTO_INET || r->address_protocol == AVAHI_PROTO_UNSPEC) {
162                         AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
163                         r->record_browser_a = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, record_browser_callback, r);
164                         avahi_key_unref(k);
165                     } 
166                     
167                     if (r->address_protocol == AVAHI_PROTO_INET6 || r->address_protocol == AVAHI_PROTO_UNSPEC) {
168                         AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
169                         r->record_browser_aaaa = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, record_browser_callback, r);
170                         avahi_key_unref(k);
171                     }
172                 }
173                 break;
174                 
175             case AVAHI_DNS_TYPE_TXT:
176                 if (!r->txt_record) {
177                     r->txt_record = avahi_record_ref(record);
178                     changed = 1;
179                 }
180                 break;
181                 
182             case AVAHI_DNS_TYPE_A:
183             case AVAHI_DNS_TYPE_AAAA:
184                 if (!r->address_record) {
185                     r->address_record = avahi_record_ref(record);
186                     changed = 1;
187                 }
188                 break;
189                 
190             default:
191                 abort();
192         }
193
194
195         if (changed && r->txt_record && r->srv_record && r->address_record)
196             finish(r, AVAHI_RESOLVER_FOUND);
197
198     } else {
199         assert(event == AVAHI_BROWSER_REMOVE);
200
201         switch (record->key->type) {
202             case AVAHI_DNS_TYPE_SRV:
203
204                 if (avahi_record_equal_no_ttl(record, r->srv_record)) {
205                     avahi_record_unref(r->srv_record);
206                     r->srv_record = NULL;
207
208                     /** Look for a replacement */
209                     avahi_s_record_browser_restart(r->record_browser_srv);
210                     start_timeout(r);
211                 }
212                 
213                 break;
214
215             case AVAHI_DNS_TYPE_TXT:
216
217                 if (avahi_record_equal_no_ttl(record, r->txt_record)) {
218                     avahi_record_unref(r->txt_record);
219                     r->txt_record = NULL;
220
221                     /** Look for a replacement */
222                     avahi_s_record_browser_restart(r->record_browser_txt);
223                     start_timeout(r);
224                 }
225                 break;
226
227             case AVAHI_DNS_TYPE_A:
228             case AVAHI_DNS_TYPE_AAAA:
229
230                 if (avahi_record_equal_no_ttl(record, r->address_record)) {
231                     avahi_record_unref(r->address_record);
232                     r->address_record = NULL;
233
234                     /** Look for a replacement */
235                     if (r->record_browser_aaaa)
236                         avahi_s_record_browser_restart(r->record_browser_aaaa);
237                     if (r->record_browser_a)
238                         avahi_s_record_browser_restart(r->record_browser_a);
239                     start_timeout(r);
240                 }
241                 break;
242
243             default:
244                 abort();
245         }
246     }
247 }
248
249 AvahiSServiceResolver *avahi_s_service_resolver_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiProtocol aprotocol, AvahiSServiceResolverCallback callback, void* userdata) {
250     AvahiSServiceResolver *r;
251     AvahiKey *k;
252     char t[256], *n;
253     size_t l;
254     
255     assert(server);
256     assert(name);
257     assert(type);
258     assert(callback);
259
260     assert(aprotocol == AVAHI_PROTO_UNSPEC || aprotocol == AVAHI_PROTO_INET || aprotocol == AVAHI_PROTO_INET6);
261
262     if (!avahi_is_valid_service_name(name)) {
263         avahi_server_set_errno(server, AVAHI_ERR_INVALID_SERVICE_NAME);
264         return NULL;
265     }
266
267     if (!avahi_is_valid_service_type(type)) {
268         avahi_server_set_errno(server, AVAHI_ERR_INVALID_SERVICE_TYPE);
269         return NULL;
270     }
271
272     if (!avahi_is_valid_domain_name(domain)) {
273         avahi_server_set_errno(server, AVAHI_ERR_INVALID_DOMAIN_NAME);
274         return NULL;
275     }
276     
277     if (!(r = avahi_new(AvahiSServiceResolver, 1))) {
278         avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
279         return NULL;
280     }
281     
282     r->server = server;
283     r->service_name = avahi_strdup(name);
284     r->service_type = avahi_normalize_name(type);
285     r->domain_name = avahi_normalize_name(domain);
286     r->callback = callback;
287     r->userdata = userdata;
288     r->address_protocol = aprotocol;
289     r->srv_record = r->txt_record = r->address_record = NULL;
290     r->interface = interface;
291     r->protocol = protocol;
292     
293     n = t;
294     l = sizeof(t);
295     avahi_escape_label((const uint8_t*) name, strlen(name), &n, &l);
296     snprintf(n, l, ".%s.%s", r->service_type, r->domain_name);
297
298     r->time_event = NULL;
299     start_timeout(r);
300     
301     AVAHI_LLIST_PREPEND(AvahiSServiceResolver, resolver, server->service_resolvers, r);
302
303     r->record_browser_a = r->record_browser_aaaa = r->record_browser_srv = r->record_browser_txt = NULL;
304     
305     k = avahi_key_new(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
306     r->record_browser_srv = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, r);
307     avahi_key_unref(k);
308
309     if (!r->record_browser_srv) {
310         avahi_s_service_resolver_free(r);
311         return NULL;
312     }
313     
314     k = avahi_key_new(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
315     r->record_browser_txt = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, r);
316     avahi_key_unref(k);
317
318     if (!r->record_browser_txt) {
319         avahi_s_service_resolver_free(r);
320         return NULL;
321     }
322
323     return r;
324 }
325
326 void avahi_s_service_resolver_free(AvahiSServiceResolver *r) {
327     assert(r);
328
329     AVAHI_LLIST_REMOVE(AvahiSServiceResolver, resolver, r->server->service_resolvers, r);
330
331     if (r->time_event)
332         avahi_time_event_free(r->time_event);
333     
334     if (r->record_browser_srv)
335         avahi_s_record_browser_free(r->record_browser_srv);
336     if (r->record_browser_txt)
337         avahi_s_record_browser_free(r->record_browser_txt);
338     if (r->record_browser_a)
339         avahi_s_record_browser_free(r->record_browser_a);
340     if (r->record_browser_aaaa)
341         avahi_s_record_browser_free(r->record_browser_aaaa);
342
343     if (r->srv_record)
344         avahi_record_unref(r->srv_record);
345     if (r->txt_record)
346         avahi_record_unref(r->txt_record);
347     if (r->address_record)
348         avahi_record_unref(r->address_record);
349     
350     avahi_free(r->service_name);
351     avahi_free(r->service_type);
352     avahi_free(r->domain_name);
353     avahi_free(r);
354 }