]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-question.c
resolved: cache stringified transaction key once per transaction
[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, const char *search_domain) {
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, search_domain);
103 if (r != 0)
104 return r;
105 }
106
107 return 0;
108 }
109
110 int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
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_or_dname(q->keys[i], rr->key, search_domain);
121 if (r != 0)
122 return r;
123 }
124
125 return 0;
126 }
127
128 int dns_question_is_valid_for_query(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_first_name(DnsQuestion *q) {
278
279 if (!q)
280 return NULL;
281
282 if (q->n_keys < 1)
283 return NULL;
284
285 return DNS_RESOURCE_KEY_NAME(q->keys[0]);
286 }
287
288 int dns_question_new_address(DnsQuestion **ret, int family, const char *name) {
289 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
290 int r;
291
292 assert(ret);
293 assert(name);
294
295 if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
296 return -EAFNOSUPPORT;
297
298 q = dns_question_new(family == AF_UNSPEC ? 2 : 1);
299 if (!q)
300 return -ENOMEM;
301
302 if (family != AF_INET6) {
303 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
304
305 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, name);
306 if (!key)
307 return -ENOMEM;
308
309 r = dns_question_add(q, key);
310 if (r < 0)
311 return r;
312 }
313
314 if (family != AF_INET) {
315 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
316
317 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, name);
318 if (!key)
319 return -ENOMEM;
320
321 r = dns_question_add(q, key);
322 if (r < 0)
323 return r;
324 }
325
326 *ret = q;
327 q = NULL;
328
329 return 0;
330 }
331
332 int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a) {
333 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
334 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
335 _cleanup_free_ char *reverse = NULL;
336 int r;
337
338 assert(ret);
339 assert(a);
340
341 if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
342 return -EAFNOSUPPORT;
343
344 r = dns_name_reverse(family, a, &reverse);
345 if (r < 0)
346 return r;
347
348 q = dns_question_new(1);
349 if (!q)
350 return -ENOMEM;
351
352 key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
353 if (!key)
354 return -ENOMEM;
355
356 reverse = NULL;
357
358 r = dns_question_add(q, key);
359 if (r < 0)
360 return r;
361
362 *ret = q;
363 q = NULL;
364
365 return 0;
366 }
367
368 int dns_question_new_service(DnsQuestion **ret, const char *name, bool with_txt) {
369 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
370 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
371 int r;
372
373 assert(ret);
374 assert(name);
375
376 q = dns_question_new(1 + with_txt);
377 if (!q)
378 return -ENOMEM;
379
380 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_SRV, name);
381 if (!key)
382 return -ENOMEM;
383
384 r = dns_question_add(q, key);
385 if (r < 0)
386 return r;
387
388 if (with_txt) {
389 dns_resource_key_unref(key);
390 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_TXT, name);
391 if (!key)
392 return -ENOMEM;
393
394 r = dns_question_add(q, key);
395 if (r < 0)
396 return r;
397 }
398
399 *ret = q;
400 q = NULL;
401
402 return 0;
403 }