browse-dns-server.c revision af548e38c2c282132ddac2a75a76218ff3be3175
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 <string.h>
27
28#include <avahi-common/domain.h>
29#include <avahi-common/malloc.h>
30#include <avahi-common/error.h>
31
32#include "browse.h"
33#include "log.h"
34#include "rr.h"
35
36typedef struct AvahiDNSServerInfo AvahiDNSServerInfo;
37
38struct AvahiDNSServerInfo {
39    AvahiSDNSServerBrowser *browser;
40
41    AvahiIfIndex interface;
42    AvahiProtocol protocol;
43    AvahiRecord *srv_record;
44    AvahiSHostNameResolver *host_name_resolver;
45    AvahiAddress address;
46
47    AVAHI_LLIST_FIELDS(AvahiDNSServerInfo, info);
48};
49
50struct AvahiSDNSServerBrowser {
51    AvahiServer *server;
52    char *domain_name;
53
54    AvahiSRecordBrowser *record_browser;
55    AvahiSDNSServerBrowserCallback callback;
56    void* userdata;
57    AvahiProtocol aprotocol;
58
59    unsigned n_info;
60
61    AVAHI_LLIST_FIELDS(AvahiSDNSServerBrowser, browser);
62    AVAHI_LLIST_HEAD(AvahiDNSServerInfo, info);
63};
64
65static AvahiDNSServerInfo* get_server_info(AvahiSDNSServerBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r) {
66    AvahiDNSServerInfo *i;
67
68    assert(b);
69    assert(r);
70
71    for (i = b->info; i; i = i->info_next)
72        if (i->interface == interface &&
73            i->protocol == protocol &&
74            avahi_record_equal_no_ttl(r, i->srv_record))
75            return i;
76
77    return NULL;
78}
79
80static void server_info_free(AvahiSDNSServerBrowser *b, AvahiDNSServerInfo *i) {
81    assert(b);
82    assert(i);
83
84    avahi_record_unref(i->srv_record);
85    if (i->host_name_resolver)
86        avahi_s_host_name_resolver_free(i->host_name_resolver);
87
88    AVAHI_LLIST_REMOVE(AvahiDNSServerInfo, info, b->info, i);
89
90    assert(b->n_info >= 1);
91    b->n_info--;
92
93    avahi_free(i);
94}
95
96static void host_name_resolver_callback(AvahiSHostNameResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *host_name, const AvahiAddress *a, void* userdata) {
97    AvahiDNSServerInfo *i = userdata;
98
99    assert(r);
100    assert(host_name);
101    assert(i);
102
103    if (event == AVAHI_RESOLVER_FOUND) {
104        i->address = *a;
105
106        i->browser->callback(i->browser, i->interface, i->protocol, AVAHI_BROWSER_NEW, i->srv_record->data.srv.name, &i->address, i->srv_record->data.srv.port, i->browser->userdata);
107    }
108
109    avahi_s_host_name_resolver_free(i->host_name_resolver);
110    i->host_name_resolver = NULL;
111}
112
113static void record_browser_callback(AvahiSRecordBrowser*rr, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, void* userdata) {
114    AvahiSDNSServerBrowser *b = userdata;
115
116    assert(rr);
117    assert(record);
118    assert(b);
119
120    assert(record->key->type == AVAHI_DNS_TYPE_SRV);
121
122    if (event == AVAHI_BROWSER_NEW) {
123        AvahiDNSServerInfo *i;
124
125        if (get_server_info(b, interface, protocol, record))
126            return;
127
128        if (b->n_info >= 10)
129            return;
130
131        if (!(i = avahi_new(AvahiDNSServerInfo, 1)))
132            return; /* OOM */
133
134        i->browser = b;
135        i->interface = interface;
136        i->protocol = protocol;
137        i->srv_record = avahi_record_ref(record);
138        i->host_name_resolver = avahi_s_host_name_resolver_new(b->server, interface, protocol, record->data.srv.name, b->aprotocol, host_name_resolver_callback, i);
139
140        AVAHI_LLIST_PREPEND(AvahiDNSServerInfo, info, b->info, i);
141
142        b->n_info++;
143    } else if (event == AVAHI_BROWSER_REMOVE) {
144        AvahiDNSServerInfo *i;
145
146        if (!(i = get_server_info(b, interface, protocol, record)))
147            return;
148
149        if (!i->host_name_resolver)
150            b->callback(b, interface, protocol, event, i->srv_record->data.srv.name, &i->address, i->srv_record->data.srv.port, b->userdata);
151
152        server_info_free(b, i);
153    }
154}
155
156AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const char *domain, AvahiDNSServerType type, AvahiProtocol aprotocol, AvahiSDNSServerBrowserCallback callback, void* userdata) {
157    AvahiSDNSServerBrowser *b;
158    AvahiKey *k;
159    char *n = NULL;
160
161    assert(server);
162    assert(callback);
163    assert(type == AVAHI_DNS_SERVER_RESOLVE || type == AVAHI_DNS_SERVER_UPDATE);
164
165    if (domain && !avahi_is_valid_domain_name(domain)) {
166        avahi_server_set_errno(server, AVAHI_ERR_INVALID_DOMAIN_NAME);
167        return NULL;
168    }
169
170    if (!(b = avahi_new(AvahiSDNSServerBrowser, 1))) {
171        avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
172        return NULL;
173    }
174
175    b->server = server;
176    b->domain_name = avahi_normalize_name(domain ? domain : "local");
177    b->callback = callback;
178    b->userdata = userdata;
179    b->aprotocol = aprotocol;
180    b->n_info = 0;
181
182    AVAHI_LLIST_HEAD_INIT(AvahiDNSServerInfo, b->info);
183    AVAHI_LLIST_PREPEND(AvahiSDNSServerBrowser, browser, server->dns_server_browsers, b);
184
185    n = avahi_strdup_printf("%s.%s",type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", b->domain_name);
186    k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
187    avahi_free(n);
188
189    b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, b);
190    avahi_key_unref(k);
191
192    if (!b->record_browser) {
193        avahi_s_dns_server_browser_free(b);
194        return NULL;
195    }
196
197    return b;
198}
199
200void avahi_s_dns_server_browser_free(AvahiSDNSServerBrowser *b) {
201    assert(b);
202
203    while (b->info)
204        server_info_free(b, b->info);
205
206    AVAHI_LLIST_REMOVE(AvahiSDNSServerBrowser, browser, b->server->dns_server_browsers, b);
207
208    if (b->record_browser)
209        avahi_s_record_browser_free(b->record_browser);
210    avahi_free(b->domain_name);
211    avahi_free(b);
212}
213
214