1 /*** 2 This file is part of avahi. 3 4 avahi is free software; you can redistribute it and/or modify it 5 under the terms of the GNU Lesser General Public License as 6 published by the Free Software Foundation; either version 2.1 of the 7 License, or (at your option) any later version. 8 9 avahi is distributed in the hope that it will be useful, but WITHOUT 10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General 12 Public License for more details. 13 14 You should have received a copy of the GNU Lesser General Public 15 License along with avahi; if not, write to the Free Software 16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 17 USA. 18 ***/ 19 20 #ifdef HAVE_CONFIG_H 21 #include <config.h> 22 #endif 23 24 #include <stdlib.h> 25 26 #include <avahi-common/domain.h> 27 #include "avahi-common/avahi-malloc.h" 28 #include <avahi-common/error.h> 29 30 #include "browse.h" 31 #include "log.h" 32 33 struct AvahiSDomainBrowser { 34 int ref; 35 36 AvahiServer *server; 37 38 AvahiSRecordBrowser *record_browser; 39 40 AvahiDomainBrowserType type; 41 AvahiSDomainBrowserCallback callback; 42 void* userdata; 43 44 AvahiTimeEvent *defer_event; 45 46 int all_for_now_scheduled; 47 48 AVAHI_LLIST_FIELDS(AvahiSDomainBrowser, browser); 49 }; 50 51 static void inc_ref(AvahiSDomainBrowser *b) { 52 assert(b); 53 assert(b->ref >= 1); 54 55 b->ref++; 56 } 57 58 static void record_browser_callback( 59 AvahiSRecordBrowser*rr, 60 AvahiIfIndex interface, 61 AvahiProtocol protocol, 62 AvahiBrowserEvent event, 63 AvahiRecord *record, 64 AvahiLookupResultFlags flags, 65 void* userdata) { 66 67 AvahiSDomainBrowser *b = userdata; 68 char *n = NULL; 69 70 assert(rr); 71 assert(b); 72 73 if (event == AVAHI_BROWSER_ALL_FOR_NOW && 74 b->defer_event) { 75 76 b->all_for_now_scheduled = 1; 77 return; 78 } 79 80 /* Filter flags */ 81 flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA; 82 83 if (record) { 84 assert(record->key->type == AVAHI_DNS_TYPE_PTR); 85 n = record->data.ptr.name; 86 87 if (b->type == AVAHI_DOMAIN_BROWSER_BROWSE) { 88 AvahiStringList *l; 89 90 /* Filter out entries defined statically */ 91 92 for (l = b->server->config.browse_domains; l; l = l->next) 93 if (avahi_domain_equal((char*) l->text, n)) 94 return; 95 } 96 97 } 98 99 b->callback(b, interface, protocol, event, n, flags, b->userdata); 100 } 101 102 static void defer_callback(AvahiTimeEvent *e, void *userdata) { 103 AvahiSDomainBrowser *b = userdata; 104 AvahiStringList *l; 105 106 assert(e); 107 assert(b); 108 109 assert(b->type == AVAHI_DOMAIN_BROWSER_BROWSE); 110 111 avahi_time_event_free(b->defer_event); 112 b->defer_event = NULL; 113 114 /* Increase ref counter */ 115 inc_ref(b); 116 117 for (l = b->server->config.browse_domains; l; l = l->next) { 118 119 /* Check whether this object still exists outside our own 120 * stack frame */ 121 if (b->ref <= 1) 122 break; 123 124 b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_NEW, (char*) l->text, AVAHI_LOOKUP_RESULT_STATIC, b->userdata); 125 } 126 127 if (b->ref > 1) { 128 /* If the ALL_FOR_NOW event has already been scheduled, execute it now */ 129 130 if (b->all_for_now_scheduled) 131 b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_ALL_FOR_NOW, NULL, 0, b->userdata); 132 } 133 134 /* Decrease ref counter */ 135 avahi_s_domain_browser_free(b); 136 } 137 138 AvahiSDomainBrowser *avahi_s_domain_browser_new( 139 AvahiServer *server, 140 AvahiIfIndex interface, 141 AvahiProtocol protocol, 142 const char *domain, 143 AvahiDomainBrowserType type, 144 AvahiLookupFlags flags, 145 AvahiSDomainBrowserCallback callback, 146 void* userdata) { 147 148 static const char * const type_table[AVAHI_DOMAIN_BROWSER_MAX] = { 149 "b", 150 "db", 151 "r", 152 "dr", 153 "lb" 154 }; 155 156 AvahiSDomainBrowser *b; 157 AvahiKey *k = NULL; 158 char n[AVAHI_DOMAIN_NAME_MAX]; 159 int r; 160 161 assert(server); 162 assert(callback); 163 164 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); 165 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); 166 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, type < AVAHI_DOMAIN_BROWSER_MAX, AVAHI_ERR_INVALID_FLAGS); 167 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME); 168 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); 169 170 if (!domain) 171 domain = server->domain_name; 172 173 if ((r = avahi_service_name_join(n, sizeof(n), type_table[type], "_dns-sd._udp", domain)) < 0) { 174 avahi_server_set_errno(server, r); 175 return NULL; 176 } 177 178 if (!(b = avahi_new(AvahiSDomainBrowser, 1))) { 179 avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); 180 return NULL; 181 } 182 183 b->ref = 1; 184 b->server = server; 185 b->callback = callback; 186 b->userdata = userdata; 187 b->record_browser = NULL; 188 b->type = type; 189 b->all_for_now_scheduled = 0; 190 b->defer_event = NULL; 191 192 AVAHI_LLIST_PREPEND(AvahiSDomainBrowser, browser, server->domain_browsers, b); 193 194 if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) { 195 avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); 196 goto fail; 197 } 198 199 if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b))) 200 goto fail; 201 202 avahi_key_unref(k); 203 204 if (type == AVAHI_DOMAIN_BROWSER_BROWSE && b->server->config.browse_domains) 205 b->defer_event = avahi_time_event_new(server->time_event_queue, NULL, defer_callback, b); 206 207 return b; 208 209 fail: 210 211 if (k) 212 avahi_key_unref(k); 213 214 avahi_s_domain_browser_free(b); 215 216 return NULL; 217 } 218 219 void avahi_s_domain_browser_free(AvahiSDomainBrowser *b) { 220 assert(b); 221 222 assert(b->ref >= 1); 223 if (--b->ref > 0) 224 return; 225 226 AVAHI_LLIST_REMOVE(AvahiSDomainBrowser, browser, b->server->domain_browsers, b); 227 228 if (b->record_browser) 229 avahi_s_record_browser_free(b->record_browser); 230 231 if (b->defer_event) 232 avahi_time_event_free(b->defer_event); 233 234 avahi_free(b); 235 } 236