]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-myhostname/nss-myhostname.c
Merge pull request #19134 from poettering/outbound-special-hostname
[thirdparty/systemd.git] / src / nss-myhostname / nss-myhostname.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <net/if.h>
5 #include <netdb.h>
6 #include <nss.h>
7 #include <stdlib.h>
8
9 #include "alloc-util.h"
10 #include "errno-util.h"
11 #include "hostname-util.h"
12 #include "local-addresses.h"
13 #include "macro.h"
14 #include "nss-util.h"
15 #include "signal-util.h"
16 #include "string-util.h"
17
18 /* We use 127.0.0.2 as IPv4 address. This has the advantage over
19 * 127.0.0.1 that it can be translated back to the local hostname. For
20 * IPv6 we use ::1 which unfortunately will not translate back to the
21 * hostname but instead something like "localhost" or so. */
22
23 #define LOCALADDRESS_IPV4 (htobe32(0x7F000002))
24 #define LOCALADDRESS_IPV6 &in6addr_loopback
25
26 NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
27 NSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
28
29 enum nss_status _nss_myhostname_gethostbyname4_r(
30 const char *name,
31 struct gaih_addrtuple **pat,
32 char *buffer, size_t buflen,
33 int *errnop, int *h_errnop,
34 int32_t *ttlp) {
35
36 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
37 _cleanup_free_ struct local_address *addresses = NULL;
38 _cleanup_free_ char *hn = NULL;
39 const char *canonical = NULL;
40 int n_addresses = 0;
41 uint32_t local_address_ipv4;
42 struct local_address *a;
43 size_t l, idx, ms;
44 char *r_name;
45 unsigned n;
46
47 PROTECT_ERRNO;
48 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
49
50 assert(name);
51 assert(pat);
52 assert(buffer);
53 assert(errnop);
54 assert(h_errnop);
55
56 if (is_localhost(name)) {
57 /* We respond to 'localhost', so that /etc/hosts is optional */
58
59 canonical = "localhost";
60 local_address_ipv4 = htobe32(INADDR_LOOPBACK);
61
62 } else if (is_gateway_hostname(name)) {
63
64 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
65 if (n_addresses <= 0)
66 goto not_found;
67
68 canonical = "_gateway";
69
70 } else if (is_outbound_hostname(name)) {
71
72 n_addresses = local_outbounds(NULL, 0, AF_UNSPEC, &addresses);
73 if (n_addresses <= 0)
74 goto not_found;
75
76 canonical = "_outbound";
77
78 } else {
79 hn = gethostname_malloc();
80 if (!hn) {
81 UNPROTECT_ERRNO;
82 *errnop = ENOMEM;
83 *h_errnop = NO_RECOVERY;
84 return NSS_STATUS_TRYAGAIN;
85 }
86
87 /* We respond to our local hostname, our hostname suffixed with a single dot. */
88 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), "."))
89 goto not_found;
90
91 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
92 if (n_addresses < 0)
93 n_addresses = 0;
94
95 canonical = hn;
96 local_address_ipv4 = LOCALADDRESS_IPV4;
97 }
98
99 l = strlen(canonical);
100 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
101 if (buflen < ms) {
102 UNPROTECT_ERRNO;
103 *errnop = ERANGE;
104 *h_errnop = NETDB_INTERNAL;
105 return NSS_STATUS_TRYAGAIN;
106 }
107
108 /* First, fill in hostname */
109 r_name = buffer;
110 memcpy(r_name, canonical, l+1);
111 idx = ALIGN(l+1);
112
113 assert(n_addresses >= 0);
114 if (n_addresses == 0) {
115 /* Second, fill in IPv6 tuple */
116 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
117 r_tuple->next = r_tuple_prev;
118 r_tuple->name = r_name;
119 r_tuple->family = AF_INET6;
120 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
121 r_tuple->scopeid = 0;
122
123 idx += ALIGN(sizeof(struct gaih_addrtuple));
124 r_tuple_prev = r_tuple;
125
126 /* Third, fill in IPv4 tuple */
127 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
128 r_tuple->next = r_tuple_prev;
129 r_tuple->name = r_name;
130 r_tuple->family = AF_INET;
131 *(uint32_t*) r_tuple->addr = local_address_ipv4;
132 r_tuple->scopeid = 0;
133
134 idx += ALIGN(sizeof(struct gaih_addrtuple));
135 r_tuple_prev = r_tuple;
136 }
137
138 /* Fourth, fill actual addresses in, but in backwards order */
139 for (a = addresses + n_addresses - 1, n = 0; (int) n < n_addresses; n++, a--) {
140 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
141 r_tuple->next = r_tuple_prev;
142 r_tuple->name = r_name;
143 r_tuple->family = a->family;
144 r_tuple->scopeid = a->family == AF_INET6 && in6_addr_is_link_local(&a->address.in6) ? a->ifindex : 0;
145 memcpy(r_tuple->addr, &a->address, 16);
146
147 idx += ALIGN(sizeof(struct gaih_addrtuple));
148 r_tuple_prev = r_tuple;
149 }
150
151 /* Verify the size matches */
152 assert(idx == ms);
153
154 /* Nscd expects us to store the first record in **pat. */
155 if (*pat)
156 **pat = *r_tuple_prev;
157 else
158 *pat = r_tuple_prev;
159
160 if (ttlp)
161 *ttlp = 0;
162
163 /* Explicitly reset both *h_errnop and h_errno to work around
164 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
165 *h_errnop = NETDB_SUCCESS;
166 h_errno = 0;
167
168 return NSS_STATUS_SUCCESS;
169
170 not_found:
171 *h_errnop = HOST_NOT_FOUND;
172 return NSS_STATUS_NOTFOUND;
173 }
174
175 static enum nss_status fill_in_hostent(
176 const char *canonical, const char *additional,
177 int af,
178 struct local_address *addresses, unsigned n_addresses,
179 uint32_t local_address_ipv4,
180 struct hostent *result,
181 char *buffer, size_t buflen,
182 int *errnop, int *h_errnop,
183 int32_t *ttlp,
184 char **canonp) {
185
186 size_t l_canonical, l_additional, idx, ms, alen;
187 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
188 struct local_address *a;
189 unsigned n, c;
190
191 assert(canonical);
192 assert(result);
193 assert(buffer);
194 assert(errnop);
195 assert(h_errnop);
196
197 PROTECT_ERRNO;
198
199 alen = FAMILY_ADDRESS_SIZE(af);
200
201 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
202 if (af == a->family)
203 c++;
204
205 l_canonical = strlen(canonical);
206 l_additional = strlen_ptr(additional);
207 ms = ALIGN(l_canonical+1)+
208 (additional ? ALIGN(l_additional+1) : 0) +
209 sizeof(char*) +
210 (additional ? sizeof(char*) : 0) +
211 (c > 0 ? c : 1) * ALIGN(alen) +
212 (c > 0 ? c+1 : 2) * sizeof(char*);
213
214 if (buflen < ms) {
215 UNPROTECT_ERRNO;
216 *errnop = ERANGE;
217 *h_errnop = NETDB_INTERNAL;
218 return NSS_STATUS_TRYAGAIN;
219 }
220
221 /* First, fill in hostnames */
222 r_name = buffer;
223 memcpy(r_name, canonical, l_canonical+1);
224 idx = ALIGN(l_canonical+1);
225
226 if (additional) {
227 r_alias = buffer + idx;
228 memcpy(r_alias, additional, l_additional+1);
229 idx += ALIGN(l_additional+1);
230 }
231
232 /* Second, create aliases array */
233 r_aliases = buffer + idx;
234 if (additional) {
235 ((char**) r_aliases)[0] = r_alias;
236 ((char**) r_aliases)[1] = NULL;
237 idx += 2*sizeof(char*);
238 } else {
239 ((char**) r_aliases)[0] = NULL;
240 idx += sizeof(char*);
241 }
242
243 /* Third, add addresses */
244 r_addr = buffer + idx;
245 if (c > 0) {
246 unsigned i = 0;
247
248 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
249 if (af != a->family)
250 continue;
251
252 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
253 i++;
254 }
255
256 assert(i == c);
257 idx += c*ALIGN(alen);
258 } else {
259 if (af == AF_INET)
260 *(uint32_t*) r_addr = local_address_ipv4;
261 else
262 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
263
264 idx += ALIGN(alen);
265 }
266
267 /* Fourth, add address pointer array */
268 r_addr_list = buffer + idx;
269 if (c > 0) {
270 unsigned i;
271
272 for (i = 0; i < c; i++)
273 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
274
275 ((char**) r_addr_list)[i] = NULL;
276 idx += (c+1) * sizeof(char*);
277
278 } else {
279 ((char**) r_addr_list)[0] = r_addr;
280 ((char**) r_addr_list)[1] = NULL;
281 idx += 2 * sizeof(char*);
282 }
283
284 /* Verify the size matches */
285 assert(idx == ms);
286
287 result->h_name = r_name;
288 result->h_aliases = (char**) r_aliases;
289 result->h_addrtype = af;
290 result->h_length = alen;
291 result->h_addr_list = (char**) r_addr_list;
292
293 if (ttlp)
294 *ttlp = 0;
295
296 if (canonp)
297 *canonp = r_name;
298
299 /* Explicitly reset both *h_errnop and h_errno to work around
300 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
301 *h_errnop = NETDB_SUCCESS;
302 h_errno = 0;
303
304 return NSS_STATUS_SUCCESS;
305 }
306
307 enum nss_status _nss_myhostname_gethostbyname3_r(
308 const char *name,
309 int af,
310 struct hostent *host,
311 char *buffer, size_t buflen,
312 int *errnop, int *h_errnop,
313 int32_t *ttlp,
314 char **canonp) {
315
316 _cleanup_free_ struct local_address *addresses = NULL;
317 const char *canonical, *additional = NULL;
318 _cleanup_free_ char *hn = NULL;
319 uint32_t local_address_ipv4 = 0;
320 int n_addresses = 0;
321
322 PROTECT_ERRNO;
323 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
324
325 assert(name);
326 assert(host);
327 assert(buffer);
328 assert(errnop);
329 assert(h_errnop);
330
331 if (af == AF_UNSPEC)
332 af = AF_INET;
333
334 if (!IN_SET(af, AF_INET, AF_INET6)) {
335 UNPROTECT_ERRNO;
336 *errnop = EAFNOSUPPORT;
337 *h_errnop = NO_DATA;
338 return NSS_STATUS_UNAVAIL;
339 }
340
341 if (is_localhost(name)) {
342 canonical = "localhost";
343 local_address_ipv4 = htobe32(INADDR_LOOPBACK);
344
345 } else if (is_gateway_hostname(name)) {
346
347 n_addresses = local_gateways(NULL, 0, af, &addresses);
348 if (n_addresses <= 0)
349 goto not_found;
350
351 canonical = "_gateway";
352
353 } else if (is_outbound_hostname(name)) {
354
355 n_addresses = local_outbounds(NULL, 0, af, &addresses);
356 if (n_addresses <= 0)
357 goto not_found;
358
359 canonical = "_outbound";
360
361 } else {
362 hn = gethostname_malloc();
363 if (!hn) {
364 UNPROTECT_ERRNO;
365 *errnop = ENOMEM;
366 *h_errnop = NO_RECOVERY;
367 return NSS_STATUS_TRYAGAIN;
368 }
369
370 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), "."))
371 goto not_found;
372
373 n_addresses = local_addresses(NULL, 0, af, &addresses);
374 if (n_addresses < 0)
375 n_addresses = 0;
376
377 canonical = hn;
378 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
379 local_address_ipv4 = LOCALADDRESS_IPV4;
380 }
381
382 UNPROTECT_ERRNO;
383
384 return fill_in_hostent(
385 canonical, additional,
386 af,
387 addresses, n_addresses,
388 local_address_ipv4,
389 host,
390 buffer, buflen,
391 errnop, h_errnop,
392 ttlp,
393 canonp);
394
395 not_found:
396 *h_errnop = HOST_NOT_FOUND;
397 return NSS_STATUS_NOTFOUND;
398 }
399
400 enum nss_status _nss_myhostname_gethostbyaddr2_r(
401 const void* addr, socklen_t len,
402 int af,
403 struct hostent *host,
404 char *buffer, size_t buflen,
405 int *errnop, int *h_errnop,
406 int32_t *ttlp) {
407
408 const char *canonical = NULL, *additional = NULL;
409 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
410 _cleanup_free_ struct local_address *addresses = NULL;
411 _cleanup_free_ char *hn = NULL;
412 int n_addresses = 0;
413 struct local_address *a;
414 bool additional_from_hostname = false;
415 unsigned n;
416
417 PROTECT_ERRNO;
418 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
419
420 assert(addr);
421 assert(host);
422 assert(buffer);
423 assert(errnop);
424 assert(h_errnop);
425
426 if (!IN_SET(af, AF_INET, AF_INET6)) {
427 UNPROTECT_ERRNO;
428 *errnop = EAFNOSUPPORT;
429 *h_errnop = NO_DATA;
430 return NSS_STATUS_UNAVAIL;
431 }
432
433 if (len != FAMILY_ADDRESS_SIZE(af)) {
434 UNPROTECT_ERRNO;
435 *errnop = EINVAL;
436 *h_errnop = NO_RECOVERY;
437 return NSS_STATUS_UNAVAIL;
438 }
439
440 if (af == AF_INET) {
441 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
442 goto found;
443
444 if ((*(uint32_t*) addr) == htobe32(INADDR_LOOPBACK)) {
445 canonical = "localhost";
446 local_address_ipv4 = htobe32(INADDR_LOOPBACK);
447 goto found;
448 }
449
450 } else {
451 assert(af == AF_INET6);
452
453 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
454 canonical = "localhost";
455 additional_from_hostname = true;
456 goto found;
457 }
458 }
459
460 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
461 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
462 if (af != a->family)
463 continue;
464
465 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
466 goto found;
467 }
468
469 addresses = mfree(addresses);
470
471 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
472 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
473 if (af != a->family)
474 continue;
475
476 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) {
477 canonical = "_gateway";
478 goto found;
479 }
480 }
481
482 *h_errnop = HOST_NOT_FOUND;
483 return NSS_STATUS_NOTFOUND;
484
485 found:
486 if (!canonical || additional_from_hostname) {
487 hn = gethostname_malloc();
488 if (!hn) {
489 UNPROTECT_ERRNO;
490 *errnop = ENOMEM;
491 *h_errnop = NO_RECOVERY;
492 return NSS_STATUS_TRYAGAIN;
493 }
494
495 if (!canonical)
496 canonical = hn;
497 else
498 additional = hn;
499 }
500
501 UNPROTECT_ERRNO;
502 return fill_in_hostent(
503 canonical, additional,
504 af,
505 addresses, n_addresses,
506 local_address_ipv4,
507 host,
508 buffer, buflen,
509 errnop, h_errnop,
510 ttlp,
511 NULL);
512 }
513
514 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
515 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);