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