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