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 39struct 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 59struct 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 69static 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 81AvahiMulticastLookup *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 128static 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 144static 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 168void 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 179void 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 195struct cbdata { 196 AvahiMulticastLookupEngine *engine; 197 AvahiMulticastLookupCallback callback; 198 void *userdata; 199 AvahiKey *key, *cname_key; 200 AvahiInterface *interface; 201 unsigned n_found; 202}; 203 204static 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 226static 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 243unsigned 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 276void 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 292void 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 327AvahiMulticastLookupEngine *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 343void 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 353