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/timeval.h> 27#include <avahi-common/malloc.h> 28#include <avahi-common/error.h> 29#include <avahi-common/domain.h> 30 31#include "browse.h" 32 33#define TIMEOUT_MSEC 10000 34 35struct AvahiSAddressResolver { 36 AvahiServer *server; 37 AvahiAddress address; 38 39 AvahiSRecordBrowser *record_browser; 40 41 AvahiSAddressResolverCallback callback; 42 void* userdata; 43 44 AvahiRecord *ptr_record; 45 AvahiIfIndex interface; 46 AvahiProtocol protocol; 47 AvahiLookupResultFlags flags; 48 49 int retry_with_multicast; 50 int retry_with_llmnr; 51 int llmnr_has_record; 52 AvahiKey *key; 53 54 AvahiTimeEvent *time_event; 55 56 AVAHI_LLIST_FIELDS(AvahiSAddressResolver, resolver); 57}; 58 59static void finish(AvahiSAddressResolver *r, AvahiResolverEvent event) { 60 assert(r); 61 62 if (r->time_event) { 63 avahi_time_event_free(r->time_event); 64 r->time_event = NULL; 65 } 66 67 switch (event) { 68 case AVAHI_RESOLVER_FAILURE: 69 r->callback(r, r->interface, r->protocol, event, &r->address, NULL, r->flags, r->userdata); 70 break; 71 72 case AVAHI_RESOLVER_FOUND: 73 assert(r->ptr_record); 74 r->callback(r, r->interface, r->protocol, event, &r->address, r->ptr_record->data.ptr.name, r->flags, r->userdata); 75 break; 76 } 77} 78 79static void time_event_callback(AvahiTimeEvent *e, void *userdata) { 80 AvahiSAddressResolver *r = userdata; 81 82 assert(e); 83 assert(r); 84 85 avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT); 86 finish(r, AVAHI_RESOLVER_FAILURE); 87} 88 89static void start_timeout(AvahiSAddressResolver *r) { 90 struct timeval tv; 91 assert(r); 92 93 if (r->time_event) 94 return; 95 96 avahi_elapse_time(&tv, TIMEOUT_MSEC, 0); 97 r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r); 98} 99 100static void record_browser_callback( 101 AvahiSRecordBrowser*rr, 102 AvahiIfIndex interface, 103 AvahiProtocol protocol, 104 AvahiBrowserEvent event, 105 AvahiRecord *record, 106 AvahiLookupResultFlags flags, 107 void* userdata) { 108 109 AvahiSAddressResolver *r = userdata; 110 111 assert(rr); 112 assert(r); 113 114 switch (event) { 115 case AVAHI_BROWSER_NEW: 116 assert(record); 117 assert(record->key->type == AVAHI_DNS_TYPE_PTR); 118 119 if (flags & AVAHI_LOOKUP_RESULT_LLMNR) 120 r->llmnr_has_record = 1; 121 122 if (r->interface > 0 && interface != r->interface) 123 return; 124 125 if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) 126 return; 127 128 if (r->interface <= 0) 129 r->interface = interface; 130 131 if (r->protocol == AVAHI_PROTO_UNSPEC) 132 r->protocol = protocol; 133 134 if (!r->ptr_record) { 135 r->ptr_record = avahi_record_ref(record); 136 r->flags = flags; 137 138 finish(r, AVAHI_RESOLVER_FOUND); 139 } 140 break; 141 142 case AVAHI_BROWSER_REMOVE: 143 assert(record); 144 assert(record->key->type == AVAHI_DNS_TYPE_PTR); 145 146 if (r->ptr_record && avahi_record_equal_no_ttl(record, r->ptr_record)) { 147 avahi_record_unref(r->ptr_record); 148 r->ptr_record = NULL; 149 r->flags = flags; 150 151 /** Look for a replacement */ 152 avahi_s_record_browser_restart(r->record_browser); 153 start_timeout(r); 154 } 155 break; 156 157 case AVAHI_BROWSER_CACHE_EXHAUSTED: 158 break; 159 160 case AVAHI_BROWSER_ALL_FOR_NOW: 161 /* when called by _LLMNR we trigger lookup by _MULTICAST*/ 162 if ((flags & AVAHI_LOOKUP_RESULT_LLMNR) && (r->retry_with_multicast) && !r->llmnr_has_record) { 163 r->retry_with_multicast = 0; 164 avahi_s_record_browser_free(r->record_browser); 165 r->record_browser = avahi_s_record_browser_new(r->server, r->interface, r->protocol, r->key, AVAHI_LOOKUP_USE_MULTICAST, record_browser_callback, r); 166 167 if (r->record_browser) { 168 start_timeout(r); 169 break; 170 } 171 } 172 break; 173 174 case AVAHI_BROWSER_FAILURE: 175 if (r->retry_with_llmnr) { 176 r->retry_with_llmnr = 0; 177 avahi_s_record_browser_free(r->record_browser); 178 r->record_browser = avahi_s_record_browser_new(r->server, r->interface, r->protocol, r->key, AVAHI_LOOKUP_USE_LLMNR, record_browser_callback, r); 179 180 if (r->record_browser) { 181 start_timeout(r); 182 break; 183 } 184 } 185 break; 186 } 187} 188 189AvahiSAddressResolver *avahi_s_address_resolver_new( 190 AvahiServer *server, 191 AvahiIfIndex interface, 192 AvahiProtocol protocol, 193 const AvahiAddress *address, 194 AvahiLookupFlags flags, 195 AvahiSAddressResolverCallback callback, 196 void* userdata) { 197 198 AvahiSAddressResolver *r; 199 AvahiKey *k; 200 char n[AVAHI_DOMAIN_NAME_MAX]; 201 202 assert(server); 203 assert(address); 204 assert(callback); 205 206 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); 207 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); 208 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, address->proto == AVAHI_PROTO_INET || address->proto == AVAHI_PROTO_INET6, AVAHI_ERR_INVALID_PROTOCOL); 209 AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_USE_LLMNR), AVAHI_ERR_INVALID_FLAGS); 210 211 avahi_reverse_lookup_name(address, n, sizeof(n)); 212 if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) { 213 avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); 214 return NULL; 215 } 216 217 if (!(r = avahi_new(AvahiSAddressResolver, 1))) { 218 avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY); 219 avahi_key_unref(k); 220 return NULL; 221 } 222 223 r->server = server; 224 r->address = *address; 225 r->callback = callback; 226 r->userdata = userdata; 227 r->ptr_record = NULL; 228 r->interface = interface; 229 r->protocol = protocol; 230 r->flags = 0; 231 r->retry_with_multicast = 0; 232 r->retry_with_llmnr = 0; 233 r->llmnr_has_record = 0; 234 r->key = k; 235 236 r->record_browser = NULL; 237 AVAHI_LLIST_PREPEND(AvahiSAddressResolver, resolver, server->address_resolvers, r); 238 239 r->time_event = NULL; 240 241 if (!(flags & (AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_LLMNR))) { 242 243 if (!server->wide_area.wide_area_lookup_engine || !avahi_wide_area_has_servers(server->wide_area.wide_area_lookup_engine)) 244 flags |= AVAHI_LOOKUP_USE_LLMNR; 245 else { 246 flags |= AVAHI_LOOKUP_USE_WIDE_AREA; 247 r->retry_with_llmnr = 1; 248 } 249 r->retry_with_multicast = 1; 250 } 251 252 r->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r); 253 254 if (!r->record_browser) { 255 avahi_s_address_resolver_free(r); 256 return NULL; 257 } 258 259 start_timeout(r); 260 261 return r; 262} 263 264void avahi_s_address_resolver_free(AvahiSAddressResolver *r) { 265 assert(r); 266 267 AVAHI_LLIST_REMOVE(AvahiSAddressResolver, resolver, r->server->address_resolvers, r); 268 269 if (r->record_browser) 270 avahi_s_record_browser_free(r->record_browser); 271 272 if (r->time_event) 273 avahi_time_event_free(r->time_event); 274 275 if (r->ptr_record) 276 avahi_record_unref(r->ptr_record); 277 278 if (r->key) 279 avahi_key_unref(r->key); 280 281 avahi_free(r); 282} 283