Home | History | Annotate | Download | only in avahi-core
      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 
     35 struct 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 
     57 static 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 
     77 static 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 
     87 static 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 
     98 static 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 
    177 AvahiSAddressResolver *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 
    250 void 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