]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-answer.c
resolved: rework synthesizing logic
[thirdparty/systemd.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 "dns-domain.h"
24
25 DnsAnswer *dns_answer_new(unsigned n) {
26 DnsAnswer *a;
27
28 a = malloc0(offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * n);
29 if (!a)
30 return NULL;
31
32 a->n_ref = 1;
33 a->n_allocated = n;
34
35 return a;
36 }
37
38 DnsAnswer *dns_answer_ref(DnsAnswer *a) {
39 if (!a)
40 return NULL;
41
42 assert(a->n_ref > 0);
43 a->n_ref++;
44 return a;
45 }
46
47 DnsAnswer *dns_answer_unref(DnsAnswer *a) {
48 if (!a)
49 return NULL;
50
51 assert(a->n_ref > 0);
52
53 if (a->n_ref == 1) {
54 unsigned i;
55
56 for (i = 0; i < a->n_rrs; i++)
57 dns_resource_record_unref(a->items[i].rr);
58
59 free(a);
60 } else
61 a->n_ref--;
62
63 return NULL;
64 }
65
66 int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex) {
67 unsigned i;
68 int r;
69
70 assert(rr);
71
72 if (!a)
73 return -ENOSPC;
74
75 for (i = 0; i < a->n_rrs; i++) {
76 if (a->items[i].ifindex != ifindex)
77 continue;
78
79 r = dns_resource_record_equal(a->items[i].rr, rr);
80 if (r < 0)
81 return r;
82 if (r > 0) {
83 /* Entry already exists, keep the entry with
84 * the higher RR, or the one with TTL 0 */
85
86 if (rr->ttl == 0 || (rr->ttl > a->items[i].rr->ttl && a->items[i].rr->ttl != 0)) {
87 dns_resource_record_ref(rr);
88 dns_resource_record_unref(a->items[i].rr);
89 a->items[i].rr = rr;
90 }
91
92 return 0;
93 }
94 }
95
96 if (a->n_rrs >= a->n_allocated)
97 return -ENOSPC;
98
99 a->items[a->n_rrs].rr = dns_resource_record_ref(rr);
100 a->items[a->n_rrs].ifindex = ifindex;
101 a->n_rrs++;
102
103 return 1;
104 }
105
106 int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) {
107 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *soa = NULL;
108
109 soa = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, name);
110 if (!soa)
111 return -ENOMEM;
112
113 soa->ttl = ttl;
114
115 soa->soa.mname = strdup(name);
116 if (!soa->soa.mname)
117 return -ENOMEM;
118
119 soa->soa.rname = strappend("root.", name);
120 if (!soa->soa.rname)
121 return -ENOMEM;
122
123 soa->soa.serial = 1;
124 soa->soa.refresh = 1;
125 soa->soa.retry = 1;
126 soa->soa.expire = 1;
127 soa->soa.minimum = ttl;
128
129 return dns_answer_add(a, soa, 0);
130 }
131
132 int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) {
133 unsigned i;
134 int r;
135
136 assert(key);
137
138 if (!a)
139 return 0;
140
141 for (i = 0; i < a->n_rrs; i++) {
142 r = dns_resource_key_match_rr(key, a->items[i].rr);
143 if (r < 0)
144 return r;
145 if (r > 0)
146 return 1;
147 }
148
149 return 0;
150 }
151
152 int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret) {
153 unsigned i;
154
155 assert(key);
156 assert(ret);
157
158 if (!a)
159 return 0;
160
161 /* For a SOA record we can never find a matching SOA record */
162 if (key->type == DNS_TYPE_SOA)
163 return 0;
164
165 for (i = 0; i < a->n_rrs; i++) {
166
167 if (a->items[i].rr->key->class != DNS_CLASS_IN)
168 continue;
169
170 if (a->items[i].rr->key->type != DNS_TYPE_SOA)
171 continue;
172
173 if (dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(a->items[i].rr->key))) {
174 *ret = a->items[i].rr;
175 return 1;
176 }
177 }
178
179 return 0;
180 }
181
182 DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) {
183 _cleanup_(dns_answer_unrefp) DnsAnswer *ret = NULL;
184 DnsAnswer *k;
185 unsigned i;
186 int r;
187
188 if (a && (!b || b->n_rrs <= 0))
189 return dns_answer_ref(a);
190 if ((!a || a->n_rrs <= 0) && b)
191 return dns_answer_ref(b);
192
193 ret = dns_answer_new((a ? a->n_rrs : 0) + (b ? b->n_rrs : 0));
194 if (!ret)
195 return NULL;
196
197 if (a) {
198 for (i = 0; i < a->n_rrs; i++) {
199 r = dns_answer_add(ret, a->items[i].rr, a->items[i].ifindex);
200 if (r < 0)
201 return NULL;
202 }
203 }
204
205 if (b) {
206 for (i = 0; i < b->n_rrs; i++) {
207 r = dns_answer_add(ret, b->items[i].rr, b->items[i].ifindex);
208 if (r < 0)
209 return NULL;
210 }
211 }
212
213 k = ret;
214 ret = NULL;
215
216 return k;
217 }
218
219 void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
220 DnsAnswerItem *items;
221 unsigned i, start, end;
222
223 if (!a)
224 return;
225
226 if (a->n_rrs <= 1)
227 return;
228
229 start = 0;
230 end = a->n_rrs-1;
231
232 /* RFC 4795, Section 2.6 suggests we should order entries
233 * depending on whether the sender is a link-local address. */
234
235 items = newa(DnsAnswerItem, a->n_rrs);
236 for (i = 0; i < a->n_rrs; i++) {
237
238 if (a->items[i].rr->key->class == DNS_CLASS_IN &&
239 ((a->items[i].rr->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->items[i].rr->a.in_addr) != prefer_link_local) ||
240 (a->items[i].rr->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->items[i].rr->aaaa.in6_addr) != prefer_link_local)))
241 /* Order address records that are are not preferred to the end of the array */
242 items[end--] = a->items[i];
243 else
244 /* Order all other records to the beginning of the array */
245 items[start++] = a->items[i];
246 }
247
248 assert(start == end+1);
249 memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs);
250 }
251
252 int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
253 DnsAnswer *n;
254
255 if (n_free <= 0)
256 return 0;
257
258 if (*a) {
259 unsigned ns;
260
261 if ((*a)->n_ref > 1)
262 return -EBUSY;
263
264 ns = (*a)->n_rrs + n_free;
265
266 if ((*a)->n_allocated >= ns)
267 return 0;
268
269 n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns);
270 if (!n)
271 return -ENOMEM;
272
273 n->n_allocated = ns;
274 } else {
275 n = dns_answer_new(n_free);
276 if (!n)
277 return -ENOMEM;
278 }
279
280 *a = n;
281 return 0;
282 }