]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-answer.c
resolved: never attempt negative caching of SOA records
[thirdparty/systemd.git] / src / resolve / resolved-dns-answer.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-answer.h"
23 #include "resolved-dns-domain.h"
24
25 DnsAnswer *dns_answer_new(unsigned n) {
26 DnsAnswer *a;
27
28 assert(n > 0);
29
30 a = malloc0(offsetof(DnsAnswer, rrs) + sizeof(DnsResourceRecord*) * n);
31 if (!a)
32 return NULL;
33
34 a->n_ref = 1;
35 a->n_allocated = n;
36
37 return a;
38 }
39
40 DnsAnswer *dns_answer_ref(DnsAnswer *a) {
41 if (!a)
42 return NULL;
43
44 assert(a->n_ref > 0);
45 a->n_ref++;
46 return a;
47 }
48
49 DnsAnswer *dns_answer_unref(DnsAnswer *a) {
50 if (!a)
51 return NULL;
52
53 assert(a->n_ref > 0);
54
55 if (a->n_ref == 1) {
56 unsigned i;
57
58 for (i = 0; i < a->n_rrs; i++)
59 dns_resource_record_unref(a->rrs[i]);
60
61 free(a);
62 } else
63 a->n_ref--;
64
65 return NULL;
66 }
67
68 int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr) {
69 unsigned i;
70 int r;
71
72 assert(a);
73 assert(rr);
74
75 for (i = 0; i < a->n_rrs; i++) {
76 r = dns_resource_record_equal(a->rrs[i], rr);
77 if (r < 0)
78 return r;
79 if (r > 0) {
80 /* Entry already exists, keep the entry with
81 * the higher RR, or the one with TTL 0 */
82
83 if (rr->ttl == 0 || (rr->ttl > a->rrs[i]->ttl && a->rrs[i]->ttl != 0)) {
84 dns_resource_record_ref(rr);
85 dns_resource_record_unref(a->rrs[i]);
86 a->rrs[i] = rr;
87 }
88
89 return 0;
90 }
91 }
92
93 if (a->n_rrs >= a->n_allocated)
94 return -ENOSPC;
95
96 a->rrs[a->n_rrs++] = dns_resource_record_ref(rr);
97 return 1;
98 }
99
100 int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) {
101 unsigned i;
102 int r;
103
104 assert(a);
105 assert(key);
106
107 for (i = 0; i < a->n_rrs; i++) {
108 r = dns_resource_key_match_rr(key, a->rrs[i]);
109 if (r < 0)
110 return r;
111 if (r > 0)
112 return 1;
113 }
114
115 return 0;
116 }
117
118 int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret) {
119 unsigned i;
120
121 assert(a);
122 assert(key);
123 assert(ret);
124
125 /* For a SOA record we can never find a matching SOA record */
126 if (key->type == DNS_TYPE_SOA)
127 return 0;
128
129 for (i = 0; i < a->n_rrs; i++) {
130
131 if (a->rrs[i]->key->class != DNS_CLASS_IN)
132 continue;
133
134 if (a->rrs[i]->key->type != DNS_TYPE_SOA)
135 continue;
136
137 if (dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(a->rrs[i]->key))) {
138 *ret = a->rrs[i];
139 return 1;
140 }
141 }
142
143 return 0;
144 }
145
146 DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) {
147 _cleanup_(dns_answer_unrefp) DnsAnswer *ret = NULL;
148 DnsAnswer *k;
149 unsigned i;
150 int r;
151
152 if (a && (!b || b->n_rrs <= 0))
153 return dns_answer_ref(a);
154 if ((!a || a->n_rrs <= 0) && b)
155 return dns_answer_ref(b);
156
157 ret = dns_answer_new((a ? a->n_rrs : 0) + (b ? b->n_rrs : 0));
158 if (!ret)
159 return NULL;
160
161 if (a) {
162 for (i = 0; i < a->n_rrs; i++) {
163 r = dns_answer_add(ret, a->rrs[i]);
164 if (r < 0)
165 return NULL;
166 }
167 }
168
169 if (b) {
170 for (i = 0; i < b->n_rrs; i++) {
171 r = dns_answer_add(ret, b->rrs[i]);
172 if (r < 0)
173 return NULL;
174 }
175 }
176
177 k = ret;
178 ret = NULL;
179
180 return k;
181 }
182
183 void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
184 DnsResourceRecord **rrs;
185 unsigned i, start, end;
186 assert(a);
187
188 if (a->n_rrs <= 1)
189 return;
190
191 start = 0;
192 end = a->n_rrs-1;
193
194 /* RFC 4795, Section 2.6 suggests we should order entries
195 * depending on whether the sender is a link-local address. */
196
197 rrs = newa(DnsResourceRecord*, a->n_rrs);
198 for (i = 0; i < a->n_rrs; i++) {
199
200 if (a->rrs[i]->key->class == DNS_CLASS_IN &&
201 ((a->rrs[i]->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->rrs[i]->a.in_addr) != prefer_link_local) ||
202 (a->rrs[i]->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->rrs[i]->aaaa.in6_addr) != prefer_link_local)))
203 /* Order address records that are are not preferred to the end of the array */
204 rrs[end--] = a->rrs[i];
205 else
206 /* Order all other records to the beginning of the array */
207 rrs[start++] = a->rrs[i];
208 }
209
210 assert(start == end+1);
211 memcpy(a->rrs, rrs, sizeof(DnsResourceRecord*) * a->n_rrs);
212 }