]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-synthesize.c
core: fix undefined behaviour due to uninitialized string buffer (#7597)
[thirdparty/systemd.git] / src / resolve / resolved-dns-synthesize.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
839a4a20
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2014 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
21#include "alloc-util.h"
22#include "hostname-util.h"
23#include "local-addresses.h"
24#include "resolved-dns-synthesize.h"
25
dd0bc0f1 26int dns_synthesize_ifindex(int ifindex) {
839a4a20
LP
27
28 /* When the caller asked for resolving on a specific
29 * interface, we synthesize the answer for that
30 * interface. However, if nothing specific was claimed and we
31 * only return localhost RRs, we synthesize the answer for
32 * localhost. */
33
34 if (ifindex > 0)
35 return ifindex;
36
37 return LOOPBACK_IFINDEX;
38}
39
dd0bc0f1 40int dns_synthesize_family(uint64_t flags) {
839a4a20
LP
41
42 /* Picks an address family depending on set flags. This is
43 * purely for synthesized answers, where the family we return
44 * for the reply should match what was requested in the
45 * question, even though we are synthesizing the answer
46 * here. */
47
48 if (!(flags & SD_RESOLVED_DNS)) {
49 if (flags & (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_MDNS_IPV4))
50 return AF_INET;
51 if (flags & (SD_RESOLVED_LLMNR_IPV6|SD_RESOLVED_MDNS_IPV6))
52 return AF_INET6;
53 }
54
55 return AF_UNSPEC;
56}
57
dd0bc0f1 58DnsProtocol dns_synthesize_protocol(uint64_t flags) {
839a4a20 59
dd0bc0f1 60 /* Similar as dns_synthesize_family() but does this for the
839a4a20
LP
61 * protocol. If resolving via DNS was requested, we claim it
62 * was DNS. Similar, if nothing specific was
63 * requested. However, if only resolving via LLMNR was
64 * requested we return that. */
65
66 if (flags & SD_RESOLVED_DNS)
67 return DNS_PROTOCOL_DNS;
68 if (flags & SD_RESOLVED_LLMNR)
69 return DNS_PROTOCOL_LLMNR;
70 if (flags & SD_RESOLVED_MDNS)
71 return DNS_PROTOCOL_MDNS;
72
73 return DNS_PROTOCOL_DNS;
74}
75
76static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
77 int r;
78
79 assert(m);
80 assert(key);
81 assert(answer);
82
83 r = dns_answer_reserve(answer, 2);
84 if (r < 0)
85 return r;
86
87 if (IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_ANY)) {
88 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
89
1c02e7ba 90 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, dns_resource_key_name(key));
839a4a20
LP
91 if (!rr)
92 return -ENOMEM;
93
94 rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK);
95
dd0bc0f1 96 r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
839a4a20
LP
97 if (r < 0)
98 return r;
99 }
100
101 if (IN_SET(key->type, DNS_TYPE_AAAA, DNS_TYPE_ANY)) {
102 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
103
1c02e7ba 104 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, dns_resource_key_name(key));
839a4a20
LP
105 if (!rr)
106 return -ENOMEM;
107
108 rr->aaaa.in6_addr = in6addr_loopback;
109
dd0bc0f1 110 r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
839a4a20
LP
111 if (r < 0)
112 return r;
113 }
114
115 return 0;
116}
117
118static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex, DnsAnswerFlags flags) {
119 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
120
121 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, from);
122 if (!rr)
123 return -ENOMEM;
124
125 rr->ptr.name = strdup(to);
126 if (!rr->ptr.name)
127 return -ENOMEM;
128
129 return dns_answer_add(*answer, rr, ifindex, flags);
130}
131
132static int synthesize_localhost_ptr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
133 int r;
134
135 assert(m);
136 assert(key);
137 assert(answer);
138
139 if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) {
140 r = dns_answer_reserve(answer, 1);
141 if (r < 0)
142 return r;
143
1c02e7ba 144 r = answer_add_ptr(answer, dns_resource_key_name(key), "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
839a4a20
LP
145 if (r < 0)
146 return r;
147 }
148
149 return 0;
150}
151
152static int answer_add_addresses_rr(
153 DnsAnswer **answer,
154 const char *name,
155 struct local_address *addresses,
156 unsigned n_addresses) {
157
158 unsigned j;
159 int r;
160
161 assert(answer);
162 assert(name);
163
164 r = dns_answer_reserve(answer, n_addresses);
165 if (r < 0)
166 return r;
167
168 for (j = 0; j < n_addresses; j++) {
169 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
170
171 r = dns_resource_record_new_address(&rr, addresses[j].family, &addresses[j].address, name);
172 if (r < 0)
173 return r;
174
175 r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
176 if (r < 0)
177 return r;
178 }
179
180 return 0;
181}
182
183static int answer_add_addresses_ptr(
184 DnsAnswer **answer,
185 const char *name,
186 struct local_address *addresses,
187 unsigned n_addresses,
188 int af, const union in_addr_union *match) {
189
2855b6c3 190 bool added = false;
839a4a20
LP
191 unsigned j;
192 int r;
193
194 assert(answer);
195 assert(name);
196
197 for (j = 0; j < n_addresses; j++) {
198 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
199
200 if (af != AF_UNSPEC) {
201
202 if (addresses[j].family != af)
203 continue;
204
205 if (match && !in_addr_equal(af, match, &addresses[j].address))
206 continue;
207 }
208
209 r = dns_answer_reserve(answer, 1);
210 if (r < 0)
211 return r;
212
213 r = dns_resource_record_new_reverse(&rr, addresses[j].family, &addresses[j].address, name);
214 if (r < 0)
215 return r;
216
217 r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
218 if (r < 0)
219 return r;
2855b6c3
LP
220
221 added = true;
839a4a20
LP
222 }
223
2855b6c3 224 return added;
839a4a20
LP
225}
226
227static int synthesize_system_hostname_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
228 _cleanup_free_ struct local_address *addresses = NULL;
229 int n = 0, af;
230
231 assert(m);
232 assert(key);
233 assert(answer);
234
235 af = dns_type_to_af(key->type);
236 if (af >= 0) {
237 n = local_addresses(m->rtnl, ifindex, af, &addresses);
238 if (n < 0)
239 return n;
240
241 if (n == 0) {
242 struct local_address buffer[2];
243
244 /* If we have no local addresses then use ::1
245 * and 127.0.0.2 as local ones. */
246
3742095b 247 if (IN_SET(af, AF_INET, AF_UNSPEC))
839a4a20
LP
248 buffer[n++] = (struct local_address) {
249 .family = AF_INET,
dd0bc0f1 250 .ifindex = dns_synthesize_ifindex(ifindex),
839a4a20
LP
251 .address.in.s_addr = htobe32(0x7F000002),
252 };
253
3742095b 254 if (IN_SET(af, AF_INET6, AF_UNSPEC))
839a4a20
LP
255 buffer[n++] = (struct local_address) {
256 .family = AF_INET6,
dd0bc0f1 257 .ifindex = dns_synthesize_ifindex(ifindex),
839a4a20
LP
258 .address.in6 = in6addr_loopback,
259 };
260
3742095b
AR
261 return answer_add_addresses_rr(answer,
262 dns_resource_key_name(key),
263 buffer, n);
839a4a20
LP
264 }
265 }
266
1c02e7ba 267 return answer_add_addresses_rr(answer, dns_resource_key_name(key), addresses, n);
839a4a20
LP
268}
269
270static int synthesize_system_hostname_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) {
271 _cleanup_free_ struct local_address *addresses = NULL;
2855b6c3 272 bool added = false;
839a4a20
LP
273 int n, r;
274
275 assert(m);
276 assert(address);
277 assert(answer);
278
279 if (af == AF_INET && address->in.s_addr == htobe32(0x7F000002)) {
280
a4f3375d 281 /* Always map the IPv4 address 127.0.0.2 to the local hostname, in addition to "localhost": */
839a4a20 282
a4f3375d
LP
283 r = dns_answer_reserve(answer, 4);
284 if (r < 0)
285 return r;
286
287 r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->full_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
839a4a20
LP
288 if (r < 0)
289 return r;
290
dd0bc0f1 291 r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->llmnr_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
839a4a20
LP
292 if (r < 0)
293 return r;
294
dd0bc0f1 295 r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->mdns_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
839a4a20
LP
296 if (r < 0)
297 return r;
298
dd0bc0f1 299 r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
839a4a20
LP
300 if (r < 0)
301 return r;
302
2855b6c3 303 return 1;
839a4a20
LP
304 }
305
306 n = local_addresses(m->rtnl, ifindex, af, &addresses);
2855b6c3 307 if (n <= 0)
839a4a20
LP
308 return n;
309
a4f3375d
LP
310 r = answer_add_addresses_ptr(answer, m->full_hostname, addresses, n, af, address);
311 if (r < 0)
312 return r;
313 if (r > 0)
314 added = true;
315
839a4a20
LP
316 r = answer_add_addresses_ptr(answer, m->llmnr_hostname, addresses, n, af, address);
317 if (r < 0)
318 return r;
2855b6c3
LP
319 if (r > 0)
320 added = true;
321
322 r = answer_add_addresses_ptr(answer, m->mdns_hostname, addresses, n, af, address);
323 if (r < 0)
324 return r;
325 if (r > 0)
326 added = true;
839a4a20 327
2855b6c3 328 return added;
839a4a20
LP
329}
330
331static int synthesize_gateway_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
332 _cleanup_free_ struct local_address *addresses = NULL;
acf06088 333 int n = 0, af, r;
839a4a20
LP
334
335 assert(m);
336 assert(key);
337 assert(answer);
338
339 af = dns_type_to_af(key->type);
340 if (af >= 0) {
341 n = local_gateways(m->rtnl, ifindex, af, &addresses);
acf06088
LP
342 if (n <= 0)
343 return n; /* < 0 means: error; == 0 means we have no gateway */
839a4a20
LP
344 }
345
acf06088
LP
346 r = answer_add_addresses_rr(answer, dns_resource_key_name(key), addresses, n);
347 if (r < 0)
348 return r;
349
350 return 1; /* > 0 means: we have some gateway */
839a4a20
LP
351}
352
353static int synthesize_gateway_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) {
354 _cleanup_free_ struct local_address *addresses = NULL;
355 int n;
356
357 assert(m);
358 assert(address);
359 assert(answer);
360
361 n = local_gateways(m->rtnl, ifindex, af, &addresses);
2855b6c3 362 if (n <= 0)
839a4a20
LP
363 return n;
364
5248e7e1 365 return answer_add_addresses_ptr(answer, "_gateway", addresses, n, af, address);
839a4a20
LP
366}
367
368int dns_synthesize_answer(
369 Manager *m,
370 DnsQuestion *q,
371 int ifindex,
dd0bc0f1 372 DnsAnswer **ret) {
839a4a20
LP
373
374 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
375 DnsResourceKey *key;
acf06088 376 bool found = false, nxdomain = false;
839a4a20
LP
377 int r;
378
379 assert(m);
380 assert(q);
381
382 DNS_QUESTION_FOREACH(key, q) {
383 union in_addr_union address;
384 const char *name;
385 int af;
386
4c701096 387 if (!IN_SET(key->class, DNS_CLASS_IN, DNS_CLASS_ANY))
839a4a20
LP
388 continue;
389
1c02e7ba 390 name = dns_resource_key_name(key);
839a4a20
LP
391
392 if (is_localhost(name)) {
393
394 r = synthesize_localhost_rr(m, key, ifindex, &answer);
395 if (r < 0)
396 return log_error_errno(r, "Failed to synthesize localhost RRs: %m");
397
398 } else if (manager_is_own_hostname(m, name)) {
399
400 r = synthesize_system_hostname_rr(m, key, ifindex, &answer);
401 if (r < 0)
402 return log_error_errno(r, "Failed to synthesize system hostname RRs: %m");
403
404 } else if (is_gateway_hostname(name)) {
405
406 r = synthesize_gateway_rr(m, key, ifindex, &answer);
407 if (r < 0)
408 return log_error_errno(r, "Failed to synthesize gateway RRs: %m");
acf06088
LP
409 if (r == 0) { /* if we have no gateway return NXDOMAIN */
410 nxdomain = true;
411 continue;
412 }
839a4a20
LP
413
414 } else if ((dns_name_endswith(name, "127.in-addr.arpa") > 0 && dns_name_equal(name, "2.0.0.127.in-addr.arpa") == 0) ||
415 dns_name_equal(name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) {
416
417 r = synthesize_localhost_ptr(m, key, ifindex, &answer);
418 if (r < 0)
419 return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m");
420
421 } else if (dns_name_address(name, &af, &address) > 0) {
2855b6c3 422 int v, w;
839a4a20 423
2855b6c3
LP
424 v = synthesize_system_hostname_ptr(m, af, &address, ifindex, &answer);
425 if (v < 0)
839a4a20
LP
426 return log_error_errno(r, "Failed to synthesize system hostname PTR RR: %m");
427
2855b6c3
LP
428 w = synthesize_gateway_ptr(m, af, &address, ifindex, &answer);
429 if (w < 0)
839a4a20 430 return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m");
2855b6c3
LP
431
432 if (v == 0 && w == 0) /* This IP address is neither a local one nor a gateway */
433 continue;
434
528e685e
LP
435 } else
436 continue;
437
438 found = true;
839a4a20
LP
439 }
440
acf06088 441 if (found) {
839a4a20 442
acf06088
LP
443 if (ret) {
444 *ret = answer;
445 answer = NULL;
446 }
447
448 return 1;
449 } else if (nxdomain)
450 return -ENXIO;
839a4a20 451
acf06088 452 return 0;
839a4a20 453}