]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-synthesize.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / resolve / resolved-dns-synthesize.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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
26 int dns_synthesize_ifindex(int ifindex) {
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
40 int dns_synthesize_family(uint64_t flags) {
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
58 DnsProtocol dns_synthesize_protocol(uint64_t flags) {
59
60 /* Similar as dns_synthesize_family() but does this for the
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
76 static 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
90 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, dns_resource_key_name(key));
91 if (!rr)
92 return -ENOMEM;
93
94 rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK);
95
96 r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
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
104 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, dns_resource_key_name(key));
105 if (!rr)
106 return -ENOMEM;
107
108 rr->aaaa.in6_addr = in6addr_loopback;
109
110 r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
111 if (r < 0)
112 return r;
113 }
114
115 return 0;
116 }
117
118 static 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
132 static 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
144 r = answer_add_ptr(answer, dns_resource_key_name(key), "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
145 if (r < 0)
146 return r;
147 }
148
149 return 0;
150 }
151
152 static 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
183 static 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
190 bool added = false;
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;
220
221 added = true;
222 }
223
224 return added;
225 }
226
227 static 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
247 if (IN_SET(af, AF_INET, AF_UNSPEC))
248 buffer[n++] = (struct local_address) {
249 .family = AF_INET,
250 .ifindex = dns_synthesize_ifindex(ifindex),
251 .address.in.s_addr = htobe32(0x7F000002),
252 };
253
254 if (IN_SET(af, AF_INET6, AF_UNSPEC))
255 buffer[n++] = (struct local_address) {
256 .family = AF_INET6,
257 .ifindex = dns_synthesize_ifindex(ifindex),
258 .address.in6 = in6addr_loopback,
259 };
260
261 return answer_add_addresses_rr(answer,
262 dns_resource_key_name(key),
263 buffer, n);
264 }
265 }
266
267 return answer_add_addresses_rr(answer, dns_resource_key_name(key), addresses, n);
268 }
269
270 static 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;
272 bool added = false;
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
281 /* Always map the IPv4 address 127.0.0.2 to the local hostname, in addition to "localhost": */
282
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);
288 if (r < 0)
289 return r;
290
291 r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->llmnr_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
292 if (r < 0)
293 return r;
294
295 r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->mdns_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
296 if (r < 0)
297 return r;
298
299 r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
300 if (r < 0)
301 return r;
302
303 return 1;
304 }
305
306 n = local_addresses(m->rtnl, ifindex, af, &addresses);
307 if (n <= 0)
308 return n;
309
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
316 r = answer_add_addresses_ptr(answer, m->llmnr_hostname, addresses, n, af, address);
317 if (r < 0)
318 return r;
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;
327
328 return added;
329 }
330
331 static int synthesize_gateway_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
332 _cleanup_free_ struct local_address *addresses = NULL;
333 int n = 0, af, r;
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);
342 if (n <= 0)
343 return n; /* < 0 means: error; == 0 means we have no gateway */
344 }
345
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 */
351 }
352
353 static 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);
362 if (n <= 0)
363 return n;
364
365 return answer_add_addresses_ptr(answer, "_gateway", addresses, n, af, address);
366 }
367
368 int dns_synthesize_answer(
369 Manager *m,
370 DnsQuestion *q,
371 int ifindex,
372 DnsAnswer **ret) {
373
374 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
375 DnsResourceKey *key;
376 bool found = false, nxdomain = false;
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
387 if (!IN_SET(key->class, DNS_CLASS_IN, DNS_CLASS_ANY))
388 continue;
389
390 name = dns_resource_key_name(key);
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");
409 if (r == 0) { /* if we have no gateway return NXDOMAIN */
410 nxdomain = true;
411 continue;
412 }
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) {
422 int v, w;
423
424 v = synthesize_system_hostname_ptr(m, af, &address, ifindex, &answer);
425 if (v < 0)
426 return log_error_errno(r, "Failed to synthesize system hostname PTR RR: %m");
427
428 w = synthesize_gateway_ptr(m, af, &address, ifindex, &answer);
429 if (w < 0)
430 return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m");
431
432 if (v == 0 && w == 0) /* This IP address is neither a local one nor a gateway */
433 continue;
434
435 } else
436 continue;
437
438 found = true;
439 }
440
441 if (found) {
442
443 if (ret) {
444 *ret = answer;
445 answer = NULL;
446 }
447
448 return 1;
449 } else if (nxdomain)
450 return -ENXIO;
451
452 return 0;
453 }