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