]> git.meshlink.io Git - catta/blob - avahi-core/resolve-service.c
* implement hashmap
[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
28 #include <avahi-common/domain.h>
29 #include <avahi-common/timeval.h>
30 #include "browse.h"
31
32 struct AvahiServiceResolver {
33     AvahiServer *server;
34     gchar *service_name;
35     gchar *service_type;
36     gchar *domain_name;
37     AvahiProtocol address_protocol;
38
39     AvahiIfIndex interface;
40     AvahiProtocol protocol;
41
42     AvahiRecordBrowser *record_browser_srv;
43     AvahiRecordBrowser *record_browser_txt;
44     AvahiRecordBrowser *record_browser_a;
45     AvahiRecordBrowser *record_browser_aaaa;
46
47     AvahiRecord *srv_record, *txt_record, *address_record;
48     
49     AvahiServiceResolverCallback callback;
50     gpointer userdata;
51
52     AvahiTimeEvent *time_event;
53
54     AVAHI_LLIST_FIELDS(AvahiServiceResolver, resolver);
55 };
56
57 static void finish(AvahiServiceResolver *r, AvahiResolverEvent event) {
58     g_assert(r);
59
60     if (r->record_browser_a) {
61         avahi_record_browser_free(r->record_browser_a);
62         r->record_browser_a = NULL;
63     }
64
65     if (r->record_browser_aaaa) {
66         avahi_record_browser_free(r->record_browser_aaaa);
67         r->record_browser_aaaa = NULL;
68     }
69
70     if (r->record_browser_srv) {
71         avahi_record_browser_free(r->record_browser_srv);
72         r->record_browser_srv = NULL;
73     }
74
75     if (r->record_browser_txt) {
76         avahi_record_browser_free(r->record_browser_txt);
77         r->record_browser_txt = NULL;
78     }
79
80     if (r->time_event) {
81         avahi_time_event_queue_remove(r->server->time_event_queue, r->time_event);
82         r->time_event = NULL;
83     }
84
85     if (event == AVAHI_RESOLVER_TIMEOUT)
86         r->callback(r, r->interface, r->protocol, event, r->service_name, r->service_type, r->domain_name, NULL, NULL, 0, NULL, r->userdata);
87     else {
88         AvahiAddress a;
89         gchar sn[256], st[256];
90         size_t i;
91         
92         g_assert(r->srv_record);
93         g_assert(r->txt_record);
94         g_assert(r->address_record);
95         
96         switch (r->address_record->key->type) {
97             case AVAHI_DNS_TYPE_A:
98                 a.family = AVAHI_PROTO_INET;
99                 a.data.ipv4 = r->address_record->data.a.address;
100                 break;
101                 
102             case AVAHI_DNS_TYPE_AAAA:
103                 a.family = AVAHI_PROTO_INET6;
104                 a.data.ipv6 = r->address_record->data.aaaa.address;
105                 break;
106                 
107             default:
108                 g_assert(FALSE);
109         }
110
111         g_snprintf(sn, sizeof(sn), r->service_name);
112         g_snprintf(st, sizeof(st), r->service_type);
113
114         if ((i = strlen(sn)) > 0 && sn[i-1] == '.')
115             sn[i-1] = 0;
116
117         if ((i = strlen(st)) > 0 && st[i-1] == '.')
118             st[i-1] = 0;
119
120         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);
121
122     }
123 }
124
125 static void record_browser_callback(AvahiRecordBrowser*rr, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, gpointer userdata) {
126     AvahiServiceResolver *r = userdata;
127
128     g_assert(rr);
129     g_assert(record);
130     g_assert(r);
131
132     if (!(event == AVAHI_BROWSER_NEW))
133         return;
134
135     if (r->interface > 0 && interface != r->interface)
136         return;
137
138     if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol)
139         return;
140     
141     if (r->interface <= 0)
142         r->interface = interface;
143
144     if (r->protocol == AVAHI_PROTO_UNSPEC)
145         r->protocol = protocol;
146     
147     switch (record->key->type) {
148         case AVAHI_DNS_TYPE_SRV:
149             if (!r->srv_record) {
150                 r->srv_record = avahi_record_ref(record);
151
152                 g_assert(!r->record_browser_a && !r->record_browser_aaaa);
153                 
154                 if (r->address_protocol == AVAHI_PROTO_INET || r->address_protocol == AVAHI_PROTO_UNSPEC) {
155                     AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
156                     r->record_browser_a = avahi_record_browser_new(r->server, r->interface, r->protocol, k, record_browser_callback, r);
157                     avahi_key_unref(k);
158                 } 
159                 
160                 if (r->address_protocol == AVAHI_PROTO_INET6 || r->address_protocol == AVAHI_PROTO_UNSPEC) {
161                     AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
162                     r->record_browser_aaaa = avahi_record_browser_new(r->server, r->interface, r->protocol, k, record_browser_callback, r);
163                     avahi_key_unref(k);
164                 }
165             }
166             break;
167             
168         case AVAHI_DNS_TYPE_TXT:
169             if (!r->txt_record)
170                 r->txt_record = avahi_record_ref(record);
171             break;
172
173         case AVAHI_DNS_TYPE_A:
174         case AVAHI_DNS_TYPE_AAAA:
175             if (!r->address_record)
176                 r->address_record = avahi_record_ref(record);
177             break;
178             
179         default:
180             g_assert(FALSE);
181     }
182
183     if (r->txt_record && r->srv_record && r->address_record)
184         finish(r, AVAHI_RESOLVER_FOUND);
185 }
186
187 static void time_event_callback(AvahiTimeEvent *e, void *userdata) {
188     AvahiServiceResolver *r = userdata;
189     
190     g_assert(e);
191     g_assert(r);
192
193     finish(r, AVAHI_RESOLVER_TIMEOUT);
194 }
195
196 AvahiServiceResolver *avahi_service_resolver_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const gchar *name, const gchar *type, const gchar *domain, AvahiProtocol aprotocol, AvahiServiceResolverCallback callback, gpointer userdata) {
197     AvahiServiceResolver *r;
198     AvahiKey *k;
199     struct timeval tv;
200     gchar t[256], *n;
201     size_t l;
202     
203     g_assert(server);
204     g_assert(name);
205     g_assert(type);
206     g_assert(callback);
207
208     g_assert(aprotocol == AVAHI_PROTO_UNSPEC || aprotocol == AVAHI_PROTO_INET || aprotocol == AVAHI_PROTO_INET6);
209
210     if (!avahi_is_valid_service_name(name)) {
211         avahi_server_set_errno(server, AVAHI_ERR_INVALID_SERVICE_NAME);
212         return NULL;
213     }
214
215     if (!avahi_is_valid_service_type(type)) {
216         avahi_server_set_errno(server, AVAHI_ERR_INVALID_SERVICE_TYPE);
217         return NULL;
218     }
219
220     if (!avahi_is_valid_domain_name(domain)) {
221         avahi_server_set_errno(server, AVAHI_ERR_INVALID_DOMAIN_NAME);
222         return NULL;
223     }
224     
225     r = g_new(AvahiServiceResolver, 1);
226     r->server = server;
227     r->service_name = g_strdup(name);
228     r->service_type = avahi_normalize_name(type);
229     r->domain_name = avahi_normalize_name(domain);
230     r->callback = callback;
231     r->userdata = userdata;
232     r->address_protocol = aprotocol;
233     r->srv_record = r->txt_record = r->address_record = NULL;
234     r->interface = interface;
235     r->protocol = protocol;
236     
237     n = t;
238     l = sizeof(t);
239     avahi_escape_label((const guint8*) name, strlen(name), &n, &l);
240     g_snprintf(n, l, ".%s.%s", r->service_type, r->domain_name);
241
242     avahi_elapse_time(&tv, 1000, 0);
243     r->time_event = avahi_time_event_queue_add(server->time_event_queue, &tv, time_event_callback, r);
244     
245     AVAHI_LLIST_PREPEND(AvahiServiceResolver, resolver, server->service_resolvers, r);
246
247     r->record_browser_a = r->record_browser_aaaa = r->record_browser_srv = r->record_browser_txt = NULL;
248     
249     k = avahi_key_new(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
250     r->record_browser_srv = avahi_record_browser_new(server, interface, protocol, k, record_browser_callback, r);
251     avahi_key_unref(k);
252
253     if (!r->record_browser_srv) {
254         avahi_service_resolver_free(r);
255         return NULL;
256     }
257     
258     k = avahi_key_new(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
259     r->record_browser_txt = avahi_record_browser_new(server, interface, protocol, k, record_browser_callback, r);
260     avahi_key_unref(k);
261
262     if (!r->record_browser_txt) {
263         avahi_service_resolver_free(r);
264         return NULL;
265     }
266
267     return r;
268 }
269
270 void avahi_service_resolver_free(AvahiServiceResolver *r) {
271     g_assert(r);
272
273     AVAHI_LLIST_REMOVE(AvahiServiceResolver, resolver, r->server->service_resolvers, r);
274
275     if (r->time_event)
276         avahi_time_event_queue_remove(r->server->time_event_queue, r->time_event);
277     
278     if (r->record_browser_srv)
279         avahi_record_browser_free(r->record_browser_srv);
280     if (r->record_browser_txt)
281         avahi_record_browser_free(r->record_browser_txt);
282     if (r->record_browser_a)
283         avahi_record_browser_free(r->record_browser_a);
284     if (r->record_browser_aaaa)
285         avahi_record_browser_free(r->record_browser_aaaa);
286
287     if (r->srv_record)
288         avahi_record_unref(r->srv_record);
289     if (r->txt_record)
290         avahi_record_unref(r->txt_record);
291     if (r->address_record)
292         avahi_record_unref(r->address_record);
293     
294     g_free(r->service_name);
295     g_free(r->service_type);
296     g_free(r->domain_name);
297     g_free(r);
298 }