chiark / gitweb /
resolved: fix cname handling
[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_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 }