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