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