]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-question.c
resolved: be slightly stricter when validating DnsQuestion
[thirdparty/systemd.git] / src / resolve / resolved-dns-question.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
b5efdb8a 22#include "alloc-util.h"
4ad7f276 23#include "dns-domain.h"
0f7091e6 24#include "dns-type.h"
b5efdb8a 25#include "resolved-dns-question.h"
faa133f3
LP
26
27DnsQuestion *dns_question_new(unsigned n) {
28 DnsQuestion *q;
29
30 assert(n > 0);
31
32 q = malloc0(offsetof(DnsQuestion, keys) + sizeof(DnsResourceKey*) * n);
33 if (!q)
34 return NULL;
35
36 q->n_ref = 1;
37 q->n_allocated = n;
38
39 return q;
40}
41
42DnsQuestion *dns_question_ref(DnsQuestion *q) {
43 if (!q)
44 return NULL;
45
46 assert(q->n_ref > 0);
47 q->n_ref++;
48 return q;
49}
50
51DnsQuestion *dns_question_unref(DnsQuestion *q) {
52 if (!q)
53 return NULL;
54
55 assert(q->n_ref > 0);
56
57 if (q->n_ref == 1) {
58 unsigned i;
59
60 for (i = 0; i < q->n_keys; i++)
61 dns_resource_key_unref(q->keys[i]);
62 free(q);
63 } else
64 q->n_ref--;
65
66 return NULL;
67}
68
69int dns_question_add(DnsQuestion *q, DnsResourceKey *key) {
7e8e0422
LP
70 unsigned i;
71 int r;
72
faa133f3
LP
73 assert(key);
74
8013e860
LP
75 if (!q)
76 return -ENOSPC;
77
7e8e0422
LP
78 for (i = 0; i < q->n_keys; i++) {
79 r = dns_resource_key_equal(q->keys[i], key);
80 if (r < 0)
81 return r;
82 if (r > 0)
83 return 0;
84 }
85
faa133f3
LP
86 if (q->n_keys >= q->n_allocated)
87 return -ENOSPC;
88
89 q->keys[q->n_keys++] = dns_resource_key_ref(key);
90 return 0;
91}
92
801ad6a6 93int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
faa133f3
LP
94 unsigned i;
95 int r;
96
faa133f3
LP
97 assert(rr);
98
8013e860
LP
99 if (!q)
100 return 0;
101
faa133f3 102 for (i = 0; i < q->n_keys; i++) {
801ad6a6 103 r = dns_resource_key_match_rr(q->keys[i], rr, search_domain);
faa133f3
LP
104 if (r != 0)
105 return r;
106 }
107
108 return 0;
109}
110
801ad6a6 111int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
faa133f3
LP
112 unsigned i;
113 int r;
114
faa133f3
LP
115 assert(rr);
116
8013e860
LP
117 if (!q)
118 return 0;
119
faa133f3 120 for (i = 0; i < q->n_keys; i++) {
5d27351f 121 r = dns_resource_key_match_cname_or_dname(q->keys[i], rr->key, search_domain);
faa133f3
LP
122 if (r != 0)
123 return r;
124 }
125
34b9656f 126 return 0;
faa133f3
LP
127}
128
703e4f5e 129int dns_question_is_valid_for_query(DnsQuestion *q) {
faa133f3
LP
130 const char *name;
131 unsigned i;
132 int r;
133
8013e860
LP
134 if (!q)
135 return 0;
faa133f3
LP
136
137 if (q->n_keys <= 0)
138 return 0;
139
140 if (q->n_keys > 65535)
141 return 0;
142
143 name = DNS_RESOURCE_KEY_NAME(q->keys[0]);
144 if (!name)
145 return 0;
146
147 /* Check that all keys in this question bear the same name */
0f7091e6 148 for (i = 0; i < q->n_keys; i++) {
34b9656f
LP
149 assert(q->keys[i]);
150
0f7091e6
LP
151 if (i > 0) {
152 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name);
153 if (r <= 0)
154 return r;
155 }
156
157 if (!dns_type_is_valid_query(q->keys[i]->type))
158 return 0;
faa133f3
LP
159 }
160
161 return 1;
162}
163
6a21960c 164int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k) {
1086182d
LP
165 unsigned j;
166 int r;
167
1086182d
LP
168 assert(k);
169
8013e860
LP
170 if (!a)
171 return 0;
172
1086182d
LP
173 for (j = 0; j < a->n_keys; j++) {
174 r = dns_resource_key_equal(a->keys[j], k);
175 if (r != 0)
176 return r;
177 }
178
179 return 0;
180}
181
182int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) {
183 unsigned j;
184 int r;
185
8013e860
LP
186 if (!a)
187 return !b || b->n_keys == 0;
188 if (!b)
189 return a->n_keys == 0;
1086182d
LP
190
191 /* Checks if all keys in a are also contained b, and vice versa */
192
193 for (j = 0; j < a->n_keys; j++) {
194 r = dns_question_contains(b, a->keys[j]);
195 if (r <= 0)
196 return r;
197 }
198
199 for (j = 0; j < b->n_keys; j++) {
200 r = dns_question_contains(a, b->keys[j]);
201 if (r <= 0)
202 return r;
203 }
204
205 return 1;
206}
207
36d9205d 208int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret) {
faa133f3
LP
209 _cleanup_(dns_question_unrefp) DnsQuestion *n = NULL;
210 bool same = true;
211 unsigned i;
212 int r;
213
36d9205d 214 assert(cname);
faa133f3 215 assert(ret);
58db254a 216 assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME));
faa133f3 217
8013e860
LP
218 if (!q) {
219 n = dns_question_new(0);
220 if (!n)
221 return -ENOMEM;
222
223 *ret = n;
224 n = 0;
225 return 0;
226 }
227
faa133f3 228 for (i = 0; i < q->n_keys; i++) {
58db254a
LP
229 _cleanup_free_ char *destination = NULL;
230 const char *d;
231
232 if (cname->key->type == DNS_TYPE_CNAME)
233 d = cname->cname.name;
234 else {
235 r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(q->keys[i]), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination);
236 if (r < 0)
237 return r;
238 if (r == 0)
239 continue;
240
241 d = destination;
242 }
243
244 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), d);
faa133f3
LP
245 if (r < 0)
246 return r;
247
248 if (r == 0) {
249 same = false;
250 break;
251 }
252 }
253
254 if (same) {
255 /* Shortcut, the names are already right */
256 *ret = dns_question_ref(q);
257 return 0;
258 }
259
260 n = dns_question_new(q->n_keys);
261 if (!n)
262 return -ENOMEM;
263
264 /* Create a new question, and patch in the new name */
34b9656f 265 for (i = 0; i < q->n_keys; i++) {
faa133f3
LP
266 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
267
36d9205d 268 k = dns_resource_key_new_redirect(q->keys[i], cname);
faa133f3
LP
269 if (!k)
270 return -ENOMEM;
271
272 r = dns_question_add(n, k);
273 if (r < 0)
274 return r;
275 }
276
277 *ret = n;
278 n = NULL;
279
280 return 1;
281}
45ec7efb 282
703e4f5e
LP
283const char *dns_question_first_name(DnsQuestion *q) {
284
285 if (!q)
286 return NULL;
45ec7efb
LP
287
288 if (q->n_keys < 1)
289 return NULL;
290
291 return DNS_RESOURCE_KEY_NAME(q->keys[0]);
292}
293
294int dns_question_new_address(DnsQuestion **ret, int family, const char *name) {
295 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
296 int r;
297
298 assert(ret);
299 assert(name);
300
301 if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
302 return -EAFNOSUPPORT;
303
304 q = dns_question_new(family == AF_UNSPEC ? 2 : 1);
305 if (!q)
306 return -ENOMEM;
307
308 if (family != AF_INET6) {
309 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
310
311 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, name);
312 if (!key)
313 return -ENOMEM;
314
315 r = dns_question_add(q, key);
316 if (r < 0)
317 return r;
318 }
319
320 if (family != AF_INET) {
321 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
322
323 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, name);
324 if (!key)
325 return -ENOMEM;
326
327 r = dns_question_add(q, key);
328 if (r < 0)
329 return r;
330 }
331
332 *ret = q;
333 q = NULL;
334
335 return 0;
336}
337
338int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a) {
339 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
340 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
341 _cleanup_free_ char *reverse = NULL;
342 int r;
343
344 assert(ret);
345 assert(a);
346
347 if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
348 return -EAFNOSUPPORT;
349
350 r = dns_name_reverse(family, a, &reverse);
351 if (r < 0)
352 return r;
353
354 q = dns_question_new(1);
355 if (!q)
356 return -ENOMEM;
357
358 key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
359 if (!key)
360 return -ENOMEM;
361
362 reverse = NULL;
363
364 r = dns_question_add(q, key);
365 if (r < 0)
366 return r;
367
368 *ret = q;
369 q = NULL;
370
371 return 0;
372}
373
374int dns_question_new_service(DnsQuestion **ret, const char *name, bool with_txt) {
375 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
376 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
377 int r;
378
379 assert(ret);
380 assert(name);
381
382 q = dns_question_new(1 + with_txt);
383 if (!q)
384 return -ENOMEM;
385
386 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_SRV, name);
387 if (!key)
388 return -ENOMEM;
389
390 r = dns_question_add(q, key);
391 if (r < 0)
392 return r;
393
394 if (with_txt) {
395 dns_resource_key_unref(key);
396 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_TXT, name);
397 if (!key)
398 return -ENOMEM;
399
400 r = dns_question_add(q, key);
401 if (r < 0)
402 return r;
403 }
404
405 *ret = q;
406 q = NULL;
407
408 return 0;
409}