]> git.meshlink.io Git - catta/blob - avahi-core/multicast-lookup.c
75e01f18aa13ab8e09a13197f8d44978c043e4b5
[catta] / avahi-core / multicast-lookup.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 <stdlib.h>
27
28 #include <avahi-common/malloc.h>
29 #include <avahi-common/timeval.h>
30
31 #include "internal.h"
32 #include "browse.h"
33 #include "socket.h"
34 #include "log.h"
35 #include "hashmap.h"
36 #include "multicast-lookup.h"
37 #include "rr-util.h"
38
39 struct AvahiMulticastLookup {
40     AvahiMulticastLookupEngine *engine;
41     int dead;
42
43     AvahiKey *key, *cname_key;
44
45     AvahiMulticastLookupCallback callback;
46     void *userdata;
47
48     AvahiIfIndex interface;
49     AvahiProtocol protocol;
50
51     int queriers_added;
52
53     AvahiTimeEvent *all_for_now_event;
54
55     AVAHI_LLIST_FIELDS(AvahiMulticastLookup, lookups);
56     AVAHI_LLIST_FIELDS(AvahiMulticastLookup, by_key);
57 };
58
59 struct AvahiMulticastLookupEngine {
60     AvahiServer *server;
61
62     /* Lookups */
63     AVAHI_LLIST_HEAD(AvahiMulticastLookup, lookups);
64     AvahiHashmap *lookups_by_key;
65
66     int cleanup_dead;
67 };
68
69 static void all_for_now_callback(AvahiTimeEvent *e, void* userdata) {
70     AvahiMulticastLookup *l = userdata;
71
72     assert(e);
73     assert(l);
74
75     avahi_time_event_free(l->all_for_now_event);
76     l->all_for_now_event = NULL;
77
78     l->callback(l->engine, l->interface, l->protocol, AVAHI_BROWSER_ALL_FOR_NOW, AVAHI_LOOKUP_RESULT_MULTICAST, NULL, l->userdata);
79 }
80
81 AvahiMulticastLookup *avahi_multicast_lookup_new(
82     AvahiMulticastLookupEngine *e,
83     AvahiIfIndex interface,
84     AvahiProtocol protocol,
85     AvahiKey *key,
86     AvahiMulticastLookupCallback callback,
87     void *userdata) {
88
89     AvahiMulticastLookup *l, *t;
90     struct timeval tv;
91
92     assert(e);
93     assert(AVAHI_IF_VALID(interface));
94     assert(AVAHI_PROTO_VALID(protocol));
95     assert(key);
96     assert(callback);
97
98     l = avahi_new(AvahiMulticastLookup, 1);
99     l->engine = e;
100     l->dead = 0;
101     l->key = avahi_key_ref(key);
102     l->cname_key = avahi_key_new_cname(l->key);
103     l->callback = callback;
104     l->userdata = userdata;
105     l->interface = interface;
106     l->protocol = protocol;
107     l->all_for_now_event = NULL;
108     l->queriers_added = 0;
109
110     t = avahi_hashmap_lookup(e->lookups_by_key, l->key);
111     AVAHI_LLIST_PREPEND(AvahiMulticastLookup, by_key, t, l);
112     avahi_hashmap_replace(e->lookups_by_key, avahi_key_ref(l->key), t);
113
114     AVAHI_LLIST_PREPEND(AvahiMulticastLookup, lookups, e->lookups, l);
115
116     avahi_querier_add_for_all(e->server, interface, protocol, l->key, &tv);
117     l->queriers_added = 1;
118
119     /* Add a second */
120     avahi_timeval_add(&tv, 1000000);
121
122     /* Issue the ALL_FOR_NOW event one second after the querier was initially created */
123     l->all_for_now_event = avahi_time_event_new(e->server->time_event_queue, &tv, all_for_now_callback, l);
124
125     return l;
126 }
127
128 static void lookup_stop(AvahiMulticastLookup *l) {
129     assert(l);
130
131     l->callback = NULL;
132
133     if (l->queriers_added) {
134         avahi_querier_remove_for_all(l->engine->server, l->interface, l->protocol, l->key);
135         l->queriers_added = 0;
136     }
137
138     if (l->all_for_now_event) {
139         avahi_time_event_free(l->all_for_now_event);
140         l->all_for_now_event = NULL;
141     }
142 }
143
144 static void lookup_destroy(AvahiMulticastLookup *l) {
145     AvahiMulticastLookup *t;
146     assert(l);
147
148     lookup_stop(l);
149
150     t = avahi_hashmap_lookup(l->engine->lookups_by_key, l->key);
151     AVAHI_LLIST_REMOVE(AvahiMulticastLookup, by_key, t, l);
152     if (t)
153         avahi_hashmap_replace(l->engine->lookups_by_key, avahi_key_ref(l->key), t);
154     else
155         avahi_hashmap_remove(l->engine->lookups_by_key, l->key);
156
157     AVAHI_LLIST_REMOVE(AvahiMulticastLookup, lookups, l->engine->lookups, l);
158
159     if (l->key)
160         avahi_key_unref(l->key);
161
162     if (l->cname_key)
163         avahi_key_unref(l->cname_key);
164
165     avahi_free(l);
166 }
167
168 void avahi_multicast_lookup_free(AvahiMulticastLookup *l) {
169     assert(l);
170
171     if (l->dead)
172         return;
173
174     l->dead = 1;
175     l->engine->cleanup_dead = 1;
176     lookup_stop(l);
177 }
178
179 void avahi_multicast_lookup_engine_cleanup(AvahiMulticastLookupEngine *e) {
180     AvahiMulticastLookup *l, *n;
181     assert(e);
182
183     while (e->cleanup_dead) {
184         e->cleanup_dead = 0;
185
186         for (l = e->lookups; l; l = n) {
187             n = l->lookups_next;
188
189             if (l->dead)
190                 lookup_destroy(l);
191         }
192     }
193 }
194
195 struct cbdata {
196     AvahiMulticastLookupEngine *engine;
197     AvahiMulticastLookupCallback callback;
198     void *userdata;
199     AvahiKey *key, *cname_key;
200     AvahiInterface *interface;
201     unsigned n_found;
202 };
203
204 static void* scan_cache_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
205     struct cbdata *cbdata = userdata;
206
207     assert(c);
208     assert(pattern);
209     assert(e);
210     assert(cbdata);
211
212     cbdata->callback(
213         cbdata->engine,
214         cbdata->interface->hardware->index,
215         cbdata->interface->protocol,
216         AVAHI_BROWSER_NEW,
217         AVAHI_LOOKUP_RESULT_CACHED|AVAHI_LOOKUP_RESULT_MULTICAST,
218         e->record,
219         cbdata->userdata);
220
221     cbdata->n_found ++;
222
223     return NULL;
224 }
225
226 static void scan_interface_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
227     struct cbdata *cbdata = userdata;
228
229     assert(m);
230     assert(i);
231     assert(cbdata);
232
233     cbdata->interface = i;
234
235     avahi_cache_walk(i->cache, cbdata->key, scan_cache_callback, cbdata);
236
237     if (cbdata->cname_key)
238         avahi_cache_walk(i->cache, cbdata->cname_key, scan_cache_callback, cbdata);
239
240     cbdata->interface = NULL;
241 }
242
243 unsigned avahi_multicast_lookup_engine_scan_cache(
244     AvahiMulticastLookupEngine *e,
245     AvahiIfIndex interface,
246     AvahiProtocol protocol,
247     AvahiKey *key,
248     AvahiMulticastLookupCallback callback,
249     void *userdata) {
250
251     struct cbdata cbdata;
252
253     assert(e);
254     assert(key);
255     assert(callback);
256
257     assert(AVAHI_IF_VALID(interface));
258     assert(AVAHI_PROTO_VALID(protocol));
259
260     cbdata.engine = e;
261     cbdata.key = key;
262     cbdata.cname_key = avahi_key_new_cname(key);
263     cbdata.callback = callback;
264     cbdata.userdata = userdata;
265     cbdata.interface = NULL;
266     cbdata.n_found = 0;
267
268     avahi_interface_monitor_walk(e->server->monitor, interface, protocol, scan_interface_callback, &cbdata);
269
270     if (cbdata.cname_key)
271         avahi_key_unref(cbdata.cname_key);
272
273     return cbdata.n_found;
274 }
275
276 void avahi_multicast_lookup_engine_new_interface(AvahiMulticastLookupEngine *e, AvahiInterface *i) {
277     AvahiMulticastLookup *l;
278
279     assert(e);
280     assert(i);
281
282     for (l = e->lookups; l; l = l->lookups_next) {
283
284         if (l->dead || !l->callback)
285             continue;
286
287         if (l->queriers_added && avahi_interface_match(i, l->interface, l->protocol))
288             avahi_querier_add(i, l->key, NULL);
289     }
290 }
291
292 void avahi_multicast_lookup_engine_notify(AvahiMulticastLookupEngine *e, AvahiInterface *i, AvahiRecord *record, AvahiBrowserEvent event) {
293     AvahiMulticastLookup *l;
294
295     assert(e);
296     assert(record);
297     assert(i);
298
299     for (l = avahi_hashmap_lookup(e->lookups_by_key, record->key); l; l = l->by_key_next) {
300         if (l->dead || !l->callback)
301             continue;
302
303         if (avahi_interface_match(i, l->interface, l->protocol))
304             l->callback(e, i->hardware->index, i->protocol, event, AVAHI_LOOKUP_RESULT_MULTICAST, record, l->userdata);
305     }
306
307
308     if (record->key->clazz == AVAHI_DNS_CLASS_IN && record->key->type == AVAHI_DNS_TYPE_CNAME) {
309         /* It's a CNAME record, so we have to scan the all lookups to see if one matches */
310
311         for (l = e->lookups; l; l = l->lookups_next) {
312             AvahiKey *key;
313
314             if (l->dead || !l->callback)
315                 continue;
316
317             if ((key = avahi_key_new_cname(l->key))) {
318                 if (avahi_key_equal(record->key, key))
319                     l->callback(e, i->hardware->index, i->protocol, event, AVAHI_LOOKUP_RESULT_MULTICAST, record, l->userdata);
320
321                 avahi_key_unref(key);
322             }
323         }
324     }
325 }
326
327 AvahiMulticastLookupEngine *avahi_multicast_lookup_engine_new(AvahiServer *s) {
328     AvahiMulticastLookupEngine *e;
329
330     assert(s);
331
332     e = avahi_new(AvahiMulticastLookupEngine, 1);
333     e->server = s;
334     e->cleanup_dead = 0;
335
336     /* Initialize lookup list */
337     e->lookups_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, (AvahiFreeFunc) avahi_key_unref, NULL);
338     AVAHI_LLIST_HEAD_INIT(AvahiWideAreaLookup, e->lookups);
339
340     return e;
341 }
342
343 void avahi_multicast_lookup_engine_free(AvahiMulticastLookupEngine *e) {
344     assert(e);
345
346     while (e->lookups)
347         lookup_destroy(e->lookups);
348
349     avahi_hashmap_free(e->lookups_by_key);
350     avahi_free(e);
351 }
352