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/domain.h> 29#include <avahi-common/malloc.h> 30#include <avahi-common/error.h> 31 32#include "browse.h" 33#include "log.h" 34 35struct AvahiSDomainBrowser { 36 int ref; 37 38 AvahiServer *server; 39 40 AvahiSRecordBrowser *record_browser; 41 42 AvahiDomainBrowserType type; 43 AvahiSDomainBrowserCallback callback; 44 void* userdata; 45 46 AvahiTimeEvent *defer_event; 47 48 int all_for_now_scheduled; 49 50 AVAHI_LLIST_FIELDS(AvahiSDomainBrowser, browser); 51}; 52 53static void inc_ref(AvahiSDomainBrowser *b) { 54 assert(b); 55 assert(b->ref >= 1); 56 57 b->ref++; 58} 59 60static void record_browser_callback( 61 AvahiSRecordBrowser*rr, 62 AvahiIfIndex interface, 63 AvahiProtocol protocol, 64 AvahiBrowserEvent event, 65 AvahiRecord *record, 66 AvahiLookupResultFlags flags, 67 void* userdata) { 68 69 AvahiSDomainBrowser *b = userdata; 70 char *n = NULL; 71 72 assert(rr); 73 assert(b); 74 75 if (event == AVAHI_BROWSER_ALL_FOR_NOW && 76 b->defer_event) { 77 78 b->all_for_now_scheduled = 1; 79 return; 80 } 81 82 /* Filter flags */ 83 flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA; 84 85 if (record) { 86 assert(record->key->type == AVAHI_DNS_TYPE_PTR); 87 n = record->data.ptr.name; 88 89 if (b->type == AVAHI_DOMAIN_BROWSER_BROWSE) { 90 AvahiStringList *l; 91 92 /* Filter out entries defined statically */ 93 94 for (l = b->server->config.browse_domains; l; l = l->next) 95 if (avahi_domain_equal((char*) l->text, n)) 96 return; 97 } 98 99 } 100 101 b->callback(b, interface, protocol, event, n, flags, b->userdata); 102} 103 104static void defer_callback(AvahiTimeEvent *e, void *userdata) { 105 AvahiSDomainBrowser *b = userdata; 106 AvahiStringList *l; 107 108 assert(e); 109 assert(b); 110 111 assert(b->type == AVAHI_DOMAIN_BROWSER_BROWSE); 112 113 avahi_time_event_free(b->defer_event); 114 b->defer_event = NULL; 115 116 /* Increase ref counter */ 117 inc_ref(b); 118 119 for (l = b->server->config.browse_domains; l; l = l->next) { 120 121 /* Check whether this object still exists outside our own 122 * stack frame */ 123 if (b->ref <= 1) 124 break; 125 126 b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_NEW, (char*) l->text, AVAHI_LOOKUP_RESULT_STATIC, b->userdata); 127 } 128 129 if (b->ref > 1) { 130 /* If the ALL_FOR_NOW event has already been scheduled, execute it now */ 131 132 if (b->all_for_now_scheduled) 133 b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_ALL_FOR_NOW, NULL, 0, b->userdata); 134 } 135 136 /* Decrease ref counter */ 137 avahi_s_domain_browser_free(b); 138} 139 140AvahiSDomainBrowser *avahi_s_domain_browser_new( 141 AvahiServer *server, 142 AvahiIfIndex interface, 143 AvahiProtocol protocol, 144 const char *domain, 145 AvahiDomainBrowserType type, 146 AvahiLookupFlags flags, 147 AvahiSDomainBrowserCallback callback, 148 void* userdata) { 149 150 static const char * const type_table[AVAHI_DOMAIN_BROWSER_MAX] = { 151 "b", 152 "db", 153 "r", 154 "dr", 155 "lb" 156 }; 157 158 AvahiSDomainBrowser *b; 159 AvahiKey *k = NULL; 160 char n[AVAHI_DOMAIN_NAME_MAX]; 161 int r; 162 163 assert(server); 164 assert(callback); 165 166 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); 167 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); 168 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, type < AVAHI_DOMAIN_BROWSER_MAX, AVAHI_ERR_INVALID_FLAGS); 169 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); 170 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); 171 172 if (!domain) 173 domain = server->domain_name; 174 175 if ((r = avahi_service_name_join(n, sizeof(n), type_table[type], "_dns-sd._udp", domain)) < 0) { 176 avahi_server_set_errno(server, r); 177 return NULL; 178 } 179 180 if (!(b = avahi_new(AvahiSDomainBrowser, 1))) { 181 avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); 182 return NULL; 183 } 184 185 b->ref = 1; 186 b->server = server; 187 b->callback = callback; 188 b->userdata = userdata; 189 b->record_browser = NULL; 190 b->type = type; 191 b->all_for_now_scheduled = 0; 192 b->defer_event = NULL; 193 194 AVAHI_LLIST_PREPEND(AvahiSDomainBrowser, browser, server->domain_browsers, b); 195 196 if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) { 197 avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); 198 goto fail; 199 } 200 201 if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b))) 202 goto fail; 203 204 avahi_key_unref(k); 205 206 if (type == AVAHI_DOMAIN_BROWSER_BROWSE && b->server->config.browse_domains) 207 b->defer_event = avahi_time_event_new(server->time_event_queue, NULL, defer_callback, b); 208 209 return b; 210 211fail: 212 213 if (k) 214 avahi_key_unref(k); 215 216 avahi_s_domain_browser_free(b); 217 218 return NULL; 219} 220 221void avahi_s_domain_browser_free(AvahiSDomainBrowser *b) { 222 assert(b); 223 224 assert(b->ref >= 1); 225 if (--b->ref > 0) 226 return; 227 228 AVAHI_LLIST_REMOVE(AvahiSDomainBrowser, browser, b->server->domain_browsers, b); 229 230 if (b->record_browser) 231 avahi_s_record_browser_free(b->record_browser); 232 233 if (b->defer_event) 234 avahi_time_event_free(b->defer_event); 235 236 avahi_free(b); 237} 238