chiark / gitweb /
resolved: only cache answer RRs, never additional or authoritative RRs of responses
[elogind.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 "resolved-dns-question.h"
23 #include "resolved-dns-domain.h"
24
25 DnsQuestion *dns_question_new(unsigned n) {
26         DnsQuestion *q;
27
28         assert(n > 0);
29
30         q = malloc0(offsetof(DnsQuestion, keys) + sizeof(DnsResourceKey*) * n);
31         if (!q)
32                 return NULL;
33
34         q->n_ref = 1;
35         q->n_allocated = n;
36
37         return q;
38 }
39
40 DnsQuestion *dns_question_ref(DnsQuestion *q) {
41         if (!q)
42                 return NULL;
43
44         assert(q->n_ref > 0);
45         q->n_ref++;
46         return q;
47 }
48
49 DnsQuestion *dns_question_unref(DnsQuestion *q) {
50         if (!q)
51                 return NULL;
52
53         assert(q->n_ref > 0);
54
55         if (q->n_ref == 1) {
56                 unsigned i;
57
58                 for (i = 0; i < q->n_keys; i++)
59                         dns_resource_key_unref(q->keys[i]);
60                 free(q);
61         } else
62                 q->n_ref--;
63
64         return  NULL;
65 }
66
67 int dns_question_add(DnsQuestion *q, DnsResourceKey *key) {
68         unsigned i;
69         int r;
70
71         assert(q);
72         assert(key);
73
74         for (i = 0; i < q->n_keys; i++) {
75                 r = dns_resource_key_equal(q->keys[i], key);
76                 if (r < 0)
77                         return r;
78                 if (r > 0)
79                         return 0;
80         }
81
82         if (q->n_keys >= q->n_allocated)
83                 return -ENOSPC;
84
85         q->keys[q->n_keys++] = dns_resource_key_ref(key);
86         return 0;
87 }
88
89 int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) {
90         unsigned i;
91         int r;
92
93         assert(q);
94         assert(rr);
95
96         for (i = 0; i < q->n_keys; i++) {
97                 r = dns_resource_key_match_rr(q->keys[i], rr);
98                 if (r != 0)
99                         return r;
100         }
101
102         return 0;
103 }
104
105 int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) {
106         unsigned i;
107         int r;
108
109         assert(q);
110         assert(rr);
111
112         for (i = 0; i < q->n_keys; i++) {
113                 r = dns_resource_key_match_cname(q->keys[i], rr);
114                 if (r != 0)
115                         return r;
116         }
117
118         return 1;
119 }
120
121 int dns_question_is_valid(DnsQuestion *q) {
122         const char *name;
123         unsigned i;
124         int r;
125
126         assert(q);
127
128         if (q->n_keys <= 0)
129                 return 0;
130
131         if (q->n_keys > 65535)
132                 return 0;
133
134         name = DNS_RESOURCE_KEY_NAME(q->keys[0]);
135         if (!name)
136                 return 0;
137
138         /* Check that all keys in this question bear the same name */
139         for (i = 1; i < q->n_keys; i++) {
140                 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name);
141                 if (r <= 0)
142                         return r;
143         }
144
145         return 1;
146 }
147
148 int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other) {
149         unsigned j;
150         int r;
151
152         assert(q);
153         assert(other);
154
155         /* Checks if all keys in "other" are also contained in "q" */
156
157         for (j = 0; j < other->n_keys; j++) {
158                 DnsResourceKey *b = other->keys[j];
159                 bool found = false;
160                 unsigned i;
161
162                 for (i = 0; i < q->n_keys; i++) {
163                         DnsResourceKey *a = q->keys[i];
164
165                         r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b));
166                         if (r < 0)
167                                 return r;
168
169                         if (r == 0)
170                                 continue;
171
172                         if (a->class != b->class && a->class != DNS_CLASS_ANY)
173                                 continue;
174
175                         if (a->type != b->type && a->type != DNS_TYPE_ANY)
176                                 continue;
177
178                         found = true;
179                         break;
180                 }
181
182                 if (!found)
183                         return 0;
184         }
185
186         return 1;
187 }
188
189 int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **ret) {
190         _cleanup_(dns_question_unrefp) DnsQuestion *n = NULL;
191         bool same = true;
192         unsigned i;
193         int r;
194
195         assert(q);
196         assert(name);
197         assert(ret);
198
199         for (i = 0; i < q->n_keys; i++) {
200                 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name);
201                 if (r < 0)
202                         return r;
203
204                 if (r == 0) {
205                         same = false;
206                         break;
207                 }
208         }
209
210         if (same) {
211                 /* Shortcut, the names are already right */
212                 *ret = dns_question_ref(q);
213                 return 0;
214         }
215
216         n = dns_question_new(q->n_keys);
217         if (!n)
218                 return -ENOMEM;
219
220         /* Create a new question, and patch in the new name */
221         for (n->n_keys = 0; n->n_keys < q->n_keys; n->n_keys++) {
222                 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
223
224                 k = dns_resource_key_new(q->keys[i]->class, q->keys[i]->type, name);
225                 if (!k)
226                         return -ENOMEM;
227
228                 r = dns_question_add(n, k);
229                 if (r < 0)
230                         return r;
231         }
232
233         *ret = n;
234         n = NULL;
235
236         return 1;
237 }
238
239 int dns_question_endswith(DnsQuestion *q, const char *suffix) {
240         unsigned i;
241
242         assert(q);
243         assert(suffix);
244
245         for (i = 0; i < q->n_keys; i++) {
246                 int k;
247
248                 k = dns_name_endswith(DNS_RESOURCE_KEY_NAME(q->keys[i]), suffix);
249                 if (k <= 0)
250                         return k;
251         }
252
253         return 1;
254 }
255
256 int dns_question_extract_reverse_address(DnsQuestion *q, int *family, union in_addr_union *address) {
257         unsigned i;
258
259         assert(q);
260         assert(family);
261         assert(address);
262
263         for (i = 0; i < q->n_keys; i++) {
264                 int k;
265
266                 k = dns_name_address(DNS_RESOURCE_KEY_NAME(q->keys[i]), family, address);
267                 if (k != 0)
268                         return k;
269         }
270
271         return 0;
272 }