]> git.meshlink.io Git - catta/blob - avahi-core/resolve-address.c
140fc3c725de6d1a3a4360f43ac562d134ed4db9
[catta] / avahi-core / resolve-address.c
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/timeval.h>
29 #include <avahi-common/malloc.h>
30 #include <avahi-common/error.h>
31 #include <avahi-common/domain.h>
32
33 #include "browse.h"
34
35 #define TIMEOUT_MSEC 5000
36
37 struct AvahiSAddressResolver {
38     AvahiServer *server;
39     AvahiAddress address;
40
41     AvahiSRecordBrowser *record_browser;
42
43     AvahiSAddressResolverCallback callback;
44     void* userdata;
45
46     AvahiRecord *ptr_record;
47     AvahiIfIndex interface;
48     AvahiProtocol protocol;
49     AvahiLookupResultFlags flags;
50
51     int retry_with_multicast;
52     AvahiKey *key;
53
54     AvahiTimeEvent *time_event;
55
56     AVAHI_LLIST_FIELDS(AvahiSAddressResolver, resolver);
57 };
58
59 static 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
79 static 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
89 static 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
100 static 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 (r->interface > 0 && interface != r->interface)
120                 return;
121
122             if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol)
123                 return;
124
125             if (r->interface <= 0)
126                 r->interface = interface;
127
128             if (r->protocol == AVAHI_PROTO_UNSPEC)
129                 r->protocol = protocol;
130
131             if (!r->ptr_record) {
132                 r->ptr_record = avahi_record_ref(record);
133                 r->flags = flags;
134
135                 finish(r, AVAHI_RESOLVER_FOUND);
136             }
137             break;
138
139         case AVAHI_BROWSER_REMOVE:
140             assert(record);
141             assert(record->key->type == AVAHI_DNS_TYPE_PTR);
142
143             if (r->ptr_record && avahi_record_equal_no_ttl(record, r->ptr_record)) {
144                 avahi_record_unref(r->ptr_record);
145                 r->ptr_record = NULL;
146                 r->flags = flags;
147
148                 /** Look for a replacement */
149                 avahi_s_record_browser_restart(r->record_browser);
150                 start_timeout(r);
151             }
152
153             break;
154
155         case AVAHI_BROWSER_CACHE_EXHAUSTED:
156         case AVAHI_BROWSER_ALL_FOR_NOW:
157             break;
158
159         case AVAHI_BROWSER_FAILURE:
160
161             if (r->retry_with_multicast) {
162                 r->retry_with_multicast = 0;
163
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
173             r->flags = flags;
174             finish(r, AVAHI_RESOLVER_FAILURE);
175             break;
176     }
177 }
178
179 AvahiSAddressResolver *avahi_s_address_resolver_new(
180     AvahiServer *server,
181     AvahiIfIndex interface,
182     AvahiProtocol protocol,
183     const AvahiAddress *address,
184     AvahiLookupFlags flags,
185     AvahiSAddressResolverCallback callback,
186     void* userdata) {
187
188     AvahiSAddressResolver *r;
189     AvahiKey *k;
190     char n[AVAHI_DOMAIN_NAME_MAX];
191
192     assert(server);
193     assert(address);
194     assert(callback);
195
196     AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
197     AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
198     AVAHI_CHECK_VALIDITY_RETURN_NULL(server, address->proto == AVAHI_PROTO_INET || address->proto == AVAHI_PROTO_INET6, AVAHI_ERR_INVALID_PROTOCOL);
199     AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
200
201     avahi_reverse_lookup_name(address, n, sizeof(n));
202
203     if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) {
204         avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
205         return NULL;
206     }
207
208     if (!(r = avahi_new(AvahiSAddressResolver, 1))) {
209         avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
210         avahi_key_unref(k);
211         return NULL;
212     }
213
214     r->server = server;
215     r->address = *address;
216     r->callback = callback;
217     r->userdata = userdata;
218     r->ptr_record = NULL;
219     r->interface = interface;
220     r->protocol = protocol;
221     r->flags = 0;
222     r->retry_with_multicast = 0;
223     r->key = k;
224
225     r->record_browser = NULL;
226     AVAHI_LLIST_PREPEND(AvahiSAddressResolver, resolver, server->address_resolvers, r);
227
228     r->time_event = NULL;
229
230     if (!(flags & (AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_USE_WIDE_AREA))) {
231
232         if (!server->wide_area_lookup_engine || !avahi_wide_area_has_servers(server->wide_area_lookup_engine))
233             flags |= AVAHI_LOOKUP_USE_MULTICAST;
234         else {
235             flags |= AVAHI_LOOKUP_USE_WIDE_AREA;
236             r->retry_with_multicast = 1;
237         }
238     }
239
240     r->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r);
241
242     if (!r->record_browser) {
243         avahi_s_address_resolver_free(r);
244         return NULL;
245     }
246
247     start_timeout(r);
248
249     return r;
250 }
251
252 void avahi_s_address_resolver_free(AvahiSAddressResolver *r) {
253     assert(r);
254
255     AVAHI_LLIST_REMOVE(AvahiSAddressResolver, resolver, r->server->address_resolvers, r);
256
257     if (r->record_browser)
258         avahi_s_record_browser_free(r->record_browser);
259
260     if (r->time_event)
261         avahi_time_event_free(r->time_event);
262
263     if (r->ptr_record)
264         avahi_record_unref(r->ptr_record);
265
266     if (r->key)
267         avahi_key_unref(r->key);
268
269     avahi_free(r);
270 }