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