chiark / gitweb /
resolve-host: use the usual log message when encountering a dbus parse failure
[elogind.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_add_soa(DnsAnswer *a, const char *name) {
101         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *soa = NULL;
102
103         soa = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, name);
104         if (!soa)
105                 return -ENOMEM;
106
107         soa->soa.mname = strdup(name);
108         if (!soa->soa.mname)
109                 return -ENOMEM;
110
111         soa->soa.rname = strappend("root.", name);
112         if (!soa->soa.rname)
113                 return -ENOMEM;
114
115         soa->soa.serial = 1;
116         soa->soa.refresh = 1;
117         soa->soa.retry = 1;
118         soa->soa.expire = 1;
119         soa->soa.minimum = 1;
120
121         return dns_answer_add(a, soa);
122 }
123
124 int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) {
125         unsigned i;
126         int r;
127
128         assert(a);
129         assert(key);
130
131         for (i = 0; i < a->n_rrs; i++) {
132                 r = dns_resource_key_match_rr(key, a->rrs[i]);
133                 if (r < 0)
134                         return r;
135                 if (r > 0)
136                         return 1;
137         }
138
139         return 0;
140 }
141
142 int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret) {
143         unsigned i;
144
145         assert(a);
146         assert(key);
147         assert(ret);
148
149         /* For a SOA record we can never find a matching SOA record */
150         if (key->type == DNS_TYPE_SOA)
151                 return 0;
152
153         for (i = 0; i < a->n_rrs; i++) {
154
155                 if (a->rrs[i]->key->class != DNS_CLASS_IN)
156                         continue;
157
158                 if (a->rrs[i]->key->type != DNS_TYPE_SOA)
159                         continue;
160
161                 if (dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(a->rrs[i]->key))) {
162                         *ret = a->rrs[i];
163                         return 1;
164                 }
165         }
166
167         return 0;
168 }
169
170 DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) {
171         _cleanup_(dns_answer_unrefp) DnsAnswer *ret = NULL;
172         DnsAnswer *k;
173         unsigned i;
174         int r;
175
176         if (a && (!b || b->n_rrs <= 0))
177                 return dns_answer_ref(a);
178         if ((!a || a->n_rrs <= 0) && b)
179                 return dns_answer_ref(b);
180
181         ret = dns_answer_new((a ? a->n_rrs : 0) + (b ? b->n_rrs : 0));
182         if (!ret)
183                 return NULL;
184
185         if (a) {
186                 for (i = 0; i < a->n_rrs; i++) {
187                         r = dns_answer_add(ret, a->rrs[i]);
188                         if (r < 0)
189                                 return NULL;
190                 }
191         }
192
193         if (b) {
194                 for (i = 0; i < b->n_rrs; i++) {
195                         r = dns_answer_add(ret, b->rrs[i]);
196                         if (r < 0)
197                                 return NULL;
198                 }
199         }
200
201         k = ret;
202         ret = NULL;
203
204         return k;
205 }
206
207 void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
208         DnsResourceRecord **rrs;
209         unsigned i, start, end;
210         assert(a);
211
212         if (a->n_rrs <= 1)
213                 return;
214
215         start = 0;
216         end = a->n_rrs-1;
217
218         /* RFC 4795, Section 2.6 suggests we should order entries
219          * depending on whether the sender is a link-local address. */
220
221         rrs = newa(DnsResourceRecord*, a->n_rrs);
222         for (i = 0; i < a->n_rrs; i++) {
223
224                 if (a->rrs[i]->key->class == DNS_CLASS_IN &&
225                     ((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) ||
226                      (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)))
227                         /* Order address records that are are not preferred to the end of the array */
228                         rrs[end--] = a->rrs[i];
229                 else
230                         /* Order all other records to the beginning of the array */
231                         rrs[start++] = a->rrs[i];
232         }
233
234         assert(start == end+1);
235         memcpy(a->rrs, rrs, sizeof(DnsResourceRecord*) * a->n_rrs);
236 }