4 This file is part of avahi.
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.
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.
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
30 #include <avahi-common/domain.h>
31 #include <avahi-common/timeval.h>
32 #include <avahi-common/malloc.h>
33 #include <avahi-common/error.h>
37 #define TIMEOUT_MSEC 1000
39 struct AvahiSServiceResolver {
44 AvahiProtocol address_protocol;
46 AvahiIfIndex interface;
47 AvahiProtocol protocol;
49 AvahiSRecordBrowser *record_browser_srv;
50 AvahiSRecordBrowser *record_browser_txt;
51 AvahiSRecordBrowser *record_browser_a;
52 AvahiSRecordBrowser *record_browser_aaaa;
54 AvahiRecord *srv_record, *txt_record, *address_record;
56 AvahiSServiceResolverCallback callback;
59 AvahiTimeEvent *time_event;
61 AVAHI_LLIST_FIELDS(AvahiSServiceResolver, resolver);
64 static void finish(AvahiSServiceResolver *r, AvahiResolverEvent event) {
68 avahi_time_event_free(r->time_event);
72 if (event == AVAHI_RESOLVER_TIMEOUT)
73 r->callback(r, r->interface, r->protocol, event, r->service_name, r->service_type, r->domain_name, NULL, NULL, 0, NULL, r->userdata);
76 char sn[256], st[256];
79 assert(event == AVAHI_RESOLVER_FOUND);
81 assert(r->srv_record);
82 assert(r->txt_record);
83 assert(r->address_record);
85 switch (r->address_record->key->type) {
86 case AVAHI_DNS_TYPE_A:
87 a.family = AVAHI_PROTO_INET;
88 a.data.ipv4 = r->address_record->data.a.address;
91 case AVAHI_DNS_TYPE_AAAA:
92 a.family = AVAHI_PROTO_INET6;
93 a.data.ipv6 = r->address_record->data.aaaa.address;
100 snprintf(sn, sizeof(sn), r->service_name);
101 snprintf(st, sizeof(st), r->service_type);
103 if ((i = strlen(sn)) > 0 && sn[i-1] == '.')
106 if ((i = strlen(st)) > 0 && st[i-1] == '.')
109 r->callback(r, r->interface, r->protocol, event, sn, st, r->domain_name, r->srv_record->data.srv.name, &a, r->srv_record->data.srv.port, r->txt_record->data.txt.string_list, r->userdata);
113 static void time_event_callback(AvahiTimeEvent *e, void *userdata) {
114 AvahiSServiceResolver *r = userdata;
119 finish(r, AVAHI_RESOLVER_TIMEOUT);
122 static void start_timeout(AvahiSServiceResolver *r) {
129 avahi_elapse_time(&tv, TIMEOUT_MSEC, 0);
130 r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r);
133 static void record_browser_callback(AvahiSRecordBrowser*rr, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, void* userdata) {
134 AvahiSServiceResolver *r = userdata;
140 if (event == AVAHI_BROWSER_NEW) {
143 if (r->interface > 0 && interface != r->interface)
146 if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol)
149 if (r->interface <= 0)
150 r->interface = interface;
152 if (r->protocol == AVAHI_PROTO_UNSPEC)
153 r->protocol = protocol;
155 switch (record->key->type) {
156 case AVAHI_DNS_TYPE_SRV:
157 if (!r->srv_record) {
158 r->srv_record = avahi_record_ref(record);
161 if (r->record_browser_a) {
162 avahi_s_record_browser_free(r->record_browser_a);
163 r->record_browser_a = NULL;
166 if (r->record_browser_aaaa) {
167 avahi_s_record_browser_free(r->record_browser_aaaa);
168 r->record_browser_aaaa = NULL;
171 if (r->address_protocol == AVAHI_PROTO_INET || r->address_protocol == AVAHI_PROTO_UNSPEC) {
172 AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
173 r->record_browser_a = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, record_browser_callback, r);
177 if (r->address_protocol == AVAHI_PROTO_INET6 || r->address_protocol == AVAHI_PROTO_UNSPEC) {
178 AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
179 r->record_browser_aaaa = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, record_browser_callback, r);
185 case AVAHI_DNS_TYPE_TXT:
186 if (!r->txt_record) {
187 r->txt_record = avahi_record_ref(record);
192 case AVAHI_DNS_TYPE_A:
193 case AVAHI_DNS_TYPE_AAAA:
194 if (!r->address_record) {
195 r->address_record = avahi_record_ref(record);
205 if (changed && r->txt_record && r->srv_record && r->address_record)
206 finish(r, AVAHI_RESOLVER_FOUND);
209 assert(event == AVAHI_BROWSER_REMOVE);
212 switch (record->key->type) {
213 case AVAHI_DNS_TYPE_SRV:
215 if (r->srv_record && avahi_record_equal_no_ttl(record, r->srv_record)) {
216 avahi_record_unref(r->srv_record);
217 r->srv_record = NULL;
219 /** Look for a replacement */
220 avahi_s_record_browser_restart(r->record_browser_srv);
226 case AVAHI_DNS_TYPE_TXT:
228 if (r->txt_record && avahi_record_equal_no_ttl(record, r->txt_record)) {
229 avahi_record_unref(r->txt_record);
230 r->txt_record = NULL;
232 /** Look for a replacement */
233 avahi_s_record_browser_restart(r->record_browser_txt);
238 case AVAHI_DNS_TYPE_A:
239 case AVAHI_DNS_TYPE_AAAA:
241 if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) {
242 avahi_record_unref(r->address_record);
243 r->address_record = NULL;
245 /** Look for a replacement */
246 if (r->record_browser_aaaa)
247 avahi_s_record_browser_restart(r->record_browser_aaaa);
248 if (r->record_browser_a)
249 avahi_s_record_browser_restart(r->record_browser_a);
260 AvahiSServiceResolver *avahi_s_service_resolver_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiProtocol aprotocol, AvahiSServiceResolverCallback callback, void* userdata) {
261 AvahiSServiceResolver *r;
271 assert(aprotocol == AVAHI_PROTO_UNSPEC || aprotocol == AVAHI_PROTO_INET || aprotocol == AVAHI_PROTO_INET6);
273 if (!avahi_is_valid_service_name(name)) {
274 avahi_server_set_errno(server, AVAHI_ERR_INVALID_SERVICE_NAME);
278 if (!avahi_is_valid_service_type(type)) {
279 avahi_server_set_errno(server, AVAHI_ERR_INVALID_SERVICE_TYPE);
283 if (!avahi_is_valid_domain_name(domain)) {
284 avahi_server_set_errno(server, AVAHI_ERR_INVALID_DOMAIN_NAME);
288 if (!(r = avahi_new(AvahiSServiceResolver, 1))) {
289 avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
294 r->service_name = avahi_strdup(name);
295 r->service_type = avahi_normalize_name(type);
296 r->domain_name = avahi_normalize_name(domain);
297 r->callback = callback;
298 r->userdata = userdata;
299 r->address_protocol = aprotocol;
300 r->srv_record = r->txt_record = r->address_record = NULL;
301 r->interface = interface;
302 r->protocol = protocol;
306 avahi_escape_label((const uint8_t*) name, strlen(name), &n, &l);
307 snprintf(n, l, ".%s.%s", r->service_type, r->domain_name);
309 r->time_event = NULL;
312 AVAHI_LLIST_PREPEND(AvahiSServiceResolver, resolver, server->service_resolvers, r);
314 r->record_browser_a = r->record_browser_aaaa = r->record_browser_srv = r->record_browser_txt = NULL;
316 k = avahi_key_new(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
317 r->record_browser_srv = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, r);
320 if (!r->record_browser_srv) {
321 avahi_s_service_resolver_free(r);
325 k = avahi_key_new(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
326 r->record_browser_txt = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, r);
329 if (!r->record_browser_txt) {
330 avahi_s_service_resolver_free(r);
337 void avahi_s_service_resolver_free(AvahiSServiceResolver *r) {
340 AVAHI_LLIST_REMOVE(AvahiSServiceResolver, resolver, r->server->service_resolvers, r);
343 avahi_time_event_free(r->time_event);
345 if (r->record_browser_srv)
346 avahi_s_record_browser_free(r->record_browser_srv);
347 if (r->record_browser_txt)
348 avahi_s_record_browser_free(r->record_browser_txt);
349 if (r->record_browser_a)
350 avahi_s_record_browser_free(r->record_browser_a);
351 if (r->record_browser_aaaa)
352 avahi_s_record_browser_free(r->record_browser_aaaa);
355 avahi_record_unref(r->srv_record);
357 avahi_record_unref(r->txt_record);
358 if (r->address_record)
359 avahi_record_unref(r->address_record);
361 avahi_free(r->service_name);
362 avahi_free(r->service_type);
363 avahi_free(r->domain_name);