chiark / gitweb /
resolved: rework logic so that we can share transactions between queries of different...
[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         assert(q);
69         assert(key);
70
71         if (q->n_keys >= q->n_allocated)
72                 return -ENOSPC;
73
74         q->keys[q->n_keys++] = dns_resource_key_ref(key);
75         return 0;
76 }
77
78 int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) {
79         unsigned i;
80         int r;
81
82         assert(q);
83         assert(rr);
84
85         for (i = 0; i < q->n_keys; i++) {
86                 r = dns_resource_key_match_rr(q->keys[i], rr);
87                 if (r != 0)
88                         return r;
89         }
90
91         return 0;
92 }
93
94 int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) {
95         unsigned i;
96         int r;
97
98         assert(q);
99         assert(rr);
100
101         for (i = 0; i < q->n_keys; i++) {
102                 r = dns_resource_key_match_cname(q->keys[i], rr);
103                 if (r != 0)
104                         return r;
105         }
106
107         return 1;
108 }
109
110 int dns_question_is_valid(DnsQuestion *q) {
111         const char *name;
112         unsigned i;
113         int r;
114
115         assert(q);
116
117         if (q->n_keys <= 0)
118                 return 0;
119
120         if (q->n_keys > 65535)
121                 return 0;
122
123         name = DNS_RESOURCE_KEY_NAME(q->keys[0]);
124         if (!name)
125                 return 0;
126
127         /* Check that all keys in this question bear the same name */
128         for (i = 1; i < q->n_keys; i++) {
129                 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name);
130                 if (r <= 0)
131                         return r;
132         }
133
134         return 1;
135 }
136
137 int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other) {
138         unsigned j;
139         int r;
140
141         assert(q);
142         assert(other);
143
144         /* Checks if all keys in "other" are also contained in "q" */
145
146         for (j = 0; j < other->n_keys; j++) {
147                 DnsResourceKey *b = other->keys[j];
148                 bool found = false;
149                 unsigned i;
150
151                 for (i = 0; i < q->n_keys; i++) {
152                         DnsResourceKey *a = q->keys[i];
153
154                         r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b));
155                         if (r < 0)
156                                 return r;
157
158                         if (r == 0)
159                                 continue;
160
161                         if (a->class != b->class && a->class != DNS_CLASS_ANY)
162                                 continue;
163
164                         if (a->type != b->type && a->type != DNS_TYPE_ANY)
165                                 continue;
166
167                         found = true;
168                         break;
169                 }
170
171                 if (!found)
172                         return 0;
173         }
174
175         return 1;
176 }
177
178 int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **ret) {
179         _cleanup_(dns_question_unrefp) DnsQuestion *n = NULL;
180         bool same = true;
181         unsigned i;
182         int r;
183
184         assert(q);
185         assert(name);
186         assert(ret);
187
188         for (i = 0; i < q->n_keys; i++) {
189                 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name);
190                 if (r < 0)
191                         return r;
192
193                 if (r == 0) {
194                         same = false;
195                         break;
196                 }
197         }
198
199         if (same) {
200                 /* Shortcut, the names are already right */
201                 *ret = dns_question_ref(q);
202                 return 0;
203         }
204
205         n = dns_question_new(q->n_keys);
206         if (!n)
207                 return -ENOMEM;
208
209         /* Create a new question, and patch in the new name */
210         for (n->n_keys = 0; n->n_keys < q->n_keys; n->n_keys++) {
211                 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
212
213                 k = dns_resource_key_new(q->keys[i]->class, q->keys[i]->type, name);
214                 if (!k)
215                         return -ENOMEM;
216
217                 r = dns_question_add(n, k);
218                 if (r < 0)
219                         return r;
220         }
221
222         *ret = n;
223         n = NULL;
224
225         return 1;
226 }