]> git.ipfire.org Git - thirdparty/systemd.git/blob - 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
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 "dns-domain.h"
23 #include "resolved-dns-answer.h"
24 #include "string-util.h"
25
26 DnsAnswer *dns_answer_new(unsigned n) {
27 DnsAnswer *a;
28
29 a = malloc0(offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * n);
30 if (!a)
31 return NULL;
32
33 a->n_ref = 1;
34 a->n_allocated = n;
35
36 return a;
37 }
38
39 DnsAnswer *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
48 DnsAnswer *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++)
58 dns_resource_record_unref(a->items[i].rr);
59
60 free(a);
61 } else
62 a->n_ref--;
63
64 return NULL;
65 }
66
67 int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex) {
68 unsigned i;
69 int r;
70
71 assert(rr);
72
73 if (!a)
74 return -ENOSPC;
75
76 for (i = 0; i < a->n_rrs; i++) {
77 if (a->items[i].ifindex != ifindex)
78 continue;
79
80 r = dns_resource_record_equal(a->items[i].rr, rr);
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
87 if (rr->ttl == 0 || (rr->ttl > a->items[i].rr->ttl && a->items[i].rr->ttl != 0)) {
88 dns_resource_record_ref(rr);
89 dns_resource_record_unref(a->items[i].rr);
90 a->items[i].rr = rr;
91 }
92
93 return 0;
94 }
95 }
96
97 if (a->n_rrs >= a->n_allocated)
98 return -ENOSPC;
99
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
104 return 1;
105 }
106
107 int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) {
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
114 soa->ttl = ttl;
115
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;
128 soa->soa.minimum = ttl;
129
130 return dns_answer_add(a, soa, 0);
131 }
132
133 int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) {
134 unsigned i;
135 int r;
136
137 assert(key);
138
139 if (!a)
140 return 0;
141
142 for (i = 0; i < a->n_rrs; i++) {
143 r = dns_resource_key_match_rr(key, a->items[i].rr);
144 if (r < 0)
145 return r;
146 if (r > 0)
147 return 1;
148 }
149
150 return 0;
151 }
152
153 int 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
166 int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret) {
167 unsigned i;
168
169 assert(key);
170 assert(ret);
171
172 if (!a)
173 return 0;
174
175 /* For a SOA record we can never find a matching SOA record */
176 if (key->type == DNS_TYPE_SOA)
177 return 0;
178
179 for (i = 0; i < a->n_rrs; i++) {
180
181 if (dns_answer_match_soa(key, a->items[i].rr->key)) {
182 *ret = a->items[i].rr;
183 return 1;
184 }
185 }
186
187 return 0;
188 }
189
190 DnsAnswer *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++) {
207 r = dns_answer_add(ret, a->items[i].rr, a->items[i].ifindex);
208 if (r < 0)
209 return NULL;
210 }
211 }
212
213 if (b) {
214 for (i = 0; i < b->n_rrs; i++) {
215 r = dns_answer_add(ret, b->items[i].rr, b->items[i].ifindex);
216 if (r < 0)
217 return NULL;
218 }
219 }
220
221 k = ret;
222 ret = NULL;
223
224 return k;
225 }
226
227 void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
228 DnsAnswerItem *items;
229 unsigned i, start, end;
230
231 if (!a)
232 return;
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
243 items = newa(DnsAnswerItem, a->n_rrs);
244 for (i = 0; i < a->n_rrs; i++) {
245
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)))
249 /* Order address records that are are not preferred to the end of the array */
250 items[end--] = a->items[i];
251 else
252 /* Order all other records to the beginning of the array */
253 items[start++] = a->items[i];
254 }
255
256 assert(start == end+1);
257 memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs);
258 }
259
260 int 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;
290 }