]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-answer.c
Merge pull request #1279 from teg/resolved-cname-cache
[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_match_soa(DnsResourceKey *key, DnsResourceKey *soa) {
153 if (soa->class != DNS_CLASS_IN)
154 return 0;
155
156 if (soa->type != DNS_TYPE_SOA)
157 return 0;
158
159 if (!dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(soa)))
160 return 0;
161
162 return 1;
163 }
164
165 int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret) {
166 unsigned i;
167
168 assert(key);
169 assert(ret);
170
171 if (!a)
172 return 0;
173
174 /* For a SOA record we can never find a matching SOA record */
175 if (key->type == DNS_TYPE_SOA)
176 return 0;
177
178 for (i = 0; i < a->n_rrs; i++) {
179
180 if (dns_answer_match_soa(key, a->items[i].rr->key)) {
181 *ret = a->items[i].rr;
182 return 1;
183 }
184 }
185
186 return 0;
187 }
188
189 DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) {
190 _cleanup_(dns_answer_unrefp) DnsAnswer *ret = NULL;
191 DnsAnswer *k;
192 unsigned i;
193 int r;
194
195 if (a && (!b || b->n_rrs <= 0))
196 return dns_answer_ref(a);
197 if ((!a || a->n_rrs <= 0) && b)
198 return dns_answer_ref(b);
199
200 ret = dns_answer_new((a ? a->n_rrs : 0) + (b ? b->n_rrs : 0));
201 if (!ret)
202 return NULL;
203
204 if (a) {
205 for (i = 0; i < a->n_rrs; i++) {
206 r = dns_answer_add(ret, a->items[i].rr, a->items[i].ifindex);
207 if (r < 0)
208 return NULL;
209 }
210 }
211
212 if (b) {
213 for (i = 0; i < b->n_rrs; i++) {
214 r = dns_answer_add(ret, b->items[i].rr, b->items[i].ifindex);
215 if (r < 0)
216 return NULL;
217 }
218 }
219
220 k = ret;
221 ret = NULL;
222
223 return k;
224 }
225
226 void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
227 DnsAnswerItem *items;
228 unsigned i, start, end;
229
230 if (!a)
231 return;
232
233 if (a->n_rrs <= 1)
234 return;
235
236 start = 0;
237 end = a->n_rrs-1;
238
239 /* RFC 4795, Section 2.6 suggests we should order entries
240 * depending on whether the sender is a link-local address. */
241
242 items = newa(DnsAnswerItem, a->n_rrs);
243 for (i = 0; i < a->n_rrs; i++) {
244
245 if (a->items[i].rr->key->class == DNS_CLASS_IN &&
246 ((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) ||
247 (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)))
248 /* Order address records that are are not preferred to the end of the array */
249 items[end--] = a->items[i];
250 else
251 /* Order all other records to the beginning of the array */
252 items[start++] = a->items[i];
253 }
254
255 assert(start == end+1);
256 memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs);
257 }
258
259 int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
260 DnsAnswer *n;
261
262 if (n_free <= 0)
263 return 0;
264
265 if (*a) {
266 unsigned ns;
267
268 if ((*a)->n_ref > 1)
269 return -EBUSY;
270
271 ns = (*a)->n_rrs + n_free;
272
273 if ((*a)->n_allocated >= ns)
274 return 0;
275
276 n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns);
277 if (!n)
278 return -ENOMEM;
279
280 n->n_allocated = ns;
281 } else {
282 n = dns_answer_new(n_free);
283 if (!n)
284 return -ENOMEM;
285 }
286
287 *a = n;
288 return 0;
289 }