chiark / gitweb /
resolved: when there's already somebody listening on the LLMNR ports, simple disable...
[elogind.git] / src / resolve / resolved-dns-answer.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "resolved-dns-answer.h"
23 #include "resolved-dns-domain.h"
24
25 DnsAnswer *dns_answer_new(unsigned n) {
26         DnsAnswer *a;
27
28         assert(n > 0);
29
30         a = malloc0(offsetof(DnsAnswer, rrs) + sizeof(DnsResourceRecord*) * n);
31         if (!a)
32                 return NULL;
33
34         a->n_ref = 1;
35         a->n_allocated = n;
36
37         return a;
38 }
39
40 DnsAnswer *dns_answer_ref(DnsAnswer *a) {
41         if (!a)
42                 return NULL;
43
44         assert(a->n_ref > 0);
45         a->n_ref++;
46         return a;
47 }
48
49 DnsAnswer *dns_answer_unref(DnsAnswer *a) {
50         if (!a)
51                 return NULL;
52
53         assert(a->n_ref > 0);
54
55         if (a->n_ref == 1) {
56                 unsigned i;
57
58                 for (i = 0; i < a->n_rrs; i++)
59                         dns_resource_record_unref(a->rrs[i]);
60
61                 free(a);
62         } else
63                 a->n_ref--;
64
65         return NULL;
66 }
67
68 int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr) {
69         unsigned i;
70         int r;
71
72         assert(a);
73         assert(rr);
74
75         for (i = 0; i < a->n_rrs; i++) {
76                 r = dns_resource_record_equal(a->rrs[i], rr);
77                 if (r < 0)
78                         return r;
79                 if (r > 0) {
80                         /* Entry already exists, keep the entry with
81                          * the higher RR, or the one with TTL 0 */
82
83                         if (rr->ttl == 0 || (rr->ttl > a->rrs[i]->ttl && a->rrs[i]->ttl != 0)) {
84                                 dns_resource_record_ref(rr);
85                                 dns_resource_record_unref(a->rrs[i]);
86                                 a->rrs[i] = rr;
87                         }
88
89                         return 0;
90                 }
91         }
92
93         if (a->n_rrs >= a->n_allocated)
94                 return -ENOSPC;
95
96         a->rrs[a->n_rrs++] = dns_resource_record_ref(rr);
97         return 1;
98 }
99
100 int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) {
101         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *soa = NULL;
102
103         soa = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, name);
104         if (!soa)
105                 return -ENOMEM;
106
107         soa->ttl = ttl;
108
109         soa->soa.mname = strdup(name);
110         if (!soa->soa.mname)
111                 return -ENOMEM;
112
113         soa->soa.rname = strappend("root.", name);
114         if (!soa->soa.rname)
115                 return -ENOMEM;
116
117         soa->soa.serial = 1;
118         soa->soa.refresh = 1;
119         soa->soa.retry = 1;
120         soa->soa.expire = 1;
121         soa->soa.minimum = ttl;
122
123         return dns_answer_add(a, soa);
124 }
125
126 int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) {
127         unsigned i;
128         int r;
129
130         assert(a);
131         assert(key);
132
133         for (i = 0; i < a->n_rrs; i++) {
134                 r = dns_resource_key_match_rr(key, a->rrs[i]);
135                 if (r < 0)
136                         return r;
137                 if (r > 0)
138                         return 1;
139         }
140
141         return 0;
142 }
143
144 int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret) {
145         unsigned i;
146
147         assert(a);
148         assert(key);
149         assert(ret);
150
151         /* For a SOA record we can never find a matching SOA record */
152         if (key->type == DNS_TYPE_SOA)
153                 return 0;
154
155         for (i = 0; i < a->n_rrs; i++) {
156
157                 if (a->rrs[i]->key->class != DNS_CLASS_IN)
158                         continue;
159
160                 if (a->rrs[i]->key->type != DNS_TYPE_SOA)
161                         continue;
162
163                 if (dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(a->rrs[i]->key))) {
164                         *ret = a->rrs[i];
165                         return 1;
166                 }
167         }
168
169         return 0;
170 }
171
172 DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) {
173         _cleanup_(dns_answer_unrefp) DnsAnswer *ret = NULL;
174         DnsAnswer *k;
175         unsigned i;
176         int r;
177
178         if (a && (!b || b->n_rrs <= 0))
179                 return dns_answer_ref(a);
180         if ((!a || a->n_rrs <= 0) && b)
181                 return dns_answer_ref(b);
182
183         ret = dns_answer_new((a ? a->n_rrs : 0) + (b ? b->n_rrs : 0));
184         if (!ret)
185                 return NULL;
186
187         if (a) {
188                 for (i = 0; i < a->n_rrs; i++) {
189                         r = dns_answer_add(ret, a->rrs[i]);
190                         if (r < 0)
191                                 return NULL;
192                 }
193         }
194
195         if (b) {
196                 for (i = 0; i < b->n_rrs; i++) {
197                         r = dns_answer_add(ret, b->rrs[i]);
198                         if (r < 0)
199                                 return NULL;
200                 }
201         }
202
203         k = ret;
204         ret = NULL;
205
206         return k;
207 }
208
209 void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
210         DnsResourceRecord **rrs;
211         unsigned i, start, end;
212         assert(a);
213
214         if (a->n_rrs <= 1)
215                 return;
216
217         start = 0;
218         end = a->n_rrs-1;
219
220         /* RFC 4795, Section 2.6 suggests we should order entries
221          * depending on whether the sender is a link-local address. */
222
223         rrs = newa(DnsResourceRecord*, a->n_rrs);
224         for (i = 0; i < a->n_rrs; i++) {
225
226                 if (a->rrs[i]->key->class == DNS_CLASS_IN &&
227                     ((a->rrs[i]->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->rrs[i]->a.in_addr) != prefer_link_local) ||
228                      (a->rrs[i]->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->rrs[i]->aaaa.in6_addr) != prefer_link_local)))
229                         /* Order address records that are are not preferred to the end of the array */
230                         rrs[end--] = a->rrs[i];
231                 else
232                         /* Order all other records to the beginning of the array */
233                         rrs[start++] = a->rrs[i];
234         }
235
236         assert(start == end+1);
237         memcpy(a->rrs, rrs, sizeof(DnsResourceRecord*) * a->n_rrs);
238 }