]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/nss-myhostname/nss-myhostname.c
nss-myhostname: do not apply non-zero offset to null pointer
[thirdparty/systemd.git] / src / nss-myhostname / nss-myhostname.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
6b21f0cf 2
6b21f0cf 3#include <errno.h>
4e8c8252 4#include <net/if.h>
07630cea
LP
5#include <netdb.h>
6#include <nss.h>
8041b5ba 7#include <stdlib.h>
8041b5ba 8
b5efdb8a 9#include "alloc-util.h"
2b2fec7d 10#include "errno-util.h"
07630cea 11#include "hostname-util.h"
e80af1bd 12#include "local-addresses.h"
1c633045 13#include "macro.h"
c9fdc26e 14#include "nss-util.h"
0c5eb056 15#include "signal-util.h"
07630cea 16#include "string-util.h"
4e8c8252
LP
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
3fdcecc8 21 * hostname but instead something like "localhost" or so. */
4e8c8252 22
8e38570e 23#define LOCALADDRESS_IPV4 (htobe32(0x7F000002))
4e8c8252 24#define LOCALADDRESS_IPV6 &in6addr_loopback
4e8c8252 25
c9fdc26e
LP
26NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
27NSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
8041b5ba 28
4e8c8252
LP
29enum 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
8041b5ba 36 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
e80af1bd 37 _cleanup_free_ struct local_address *addresses = NULL;
5502f0d9
LP
38 _cleanup_free_ char *hn = NULL;
39 const char *canonical = NULL;
a1077c84 40 int n_addresses = 0;
e8a7a315 41 uint32_t local_address_ipv4;
5502f0d9
LP
42 size_t l, idx, ms;
43 char *r_name;
4e8c8252 44
06202b9e 45 PROTECT_ERRNO;
0c5eb056
LP
46 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
47
5502f0d9
LP
48 assert(name);
49 assert(pat);
50 assert(buffer);
51 assert(errnop);
52 assert(h_errnop);
53
54 if (is_localhost(name)) {
a1fdbcbe 55 /* We respond to 'localhost', so that /etc/hosts is optional */
4e8c8252 56
e8a7a315 57 canonical = "localhost";
8e38570e 58 local_address_ipv4 = htobe32(INADDR_LOOPBACK);
e9140aff 59
46a5e0e7 60 } else if (is_gateway_hostname(name)) {
e9140aff 61
1d050e1e 62 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
e09e7ac3
LP
63 if (n_addresses <= 0)
64 goto not_found;
e9140aff 65
5248e7e1 66 canonical = "_gateway";
e9140aff 67
a1fdbcbe
LP
68 } else if (is_outbound_hostname(name)) {
69
70 n_addresses = local_outbounds(NULL, 0, AF_UNSPEC, &addresses);
71 if (n_addresses <= 0)
72 goto not_found;
73
74 canonical = "_outbound";
75
e8a7a315 76 } else {
5502f0d9
LP
77 hn = gethostname_malloc();
78 if (!hn) {
cdccd29f 79 UNPROTECT_ERRNO;
0192cbdb 80 *errnop = ENOMEM;
e8a7a315 81 *h_errnop = NO_RECOVERY;
5502f0d9 82 return NSS_STATUS_TRYAGAIN;
e8a7a315
LP
83 }
84
38b38500 85 /* We respond to our local hostname, our hostname suffixed with a single dot. */
e09e7ac3
LP
86 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), "."))
87 goto not_found;
e8a7a315 88
1d050e1e 89 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
e80af1bd
LP
90 if (n_addresses < 0)
91 n_addresses = 0;
e8a7a315
LP
92
93 canonical = hn;
94 local_address_ipv4 = LOCALADDRESS_IPV4;
95 }
8041b5ba 96
e8a7a315 97 l = strlen(canonical);
5502f0d9 98 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
4e8c8252 99 if (buflen < ms) {
cdccd29f 100 UNPROTECT_ERRNO;
0192cbdb 101 *errnop = ERANGE;
cda458a5 102 *h_errnop = NETDB_INTERNAL;
4e8c8252
LP
103 return NSS_STATUS_TRYAGAIN;
104 }
105
106 /* First, fill in hostname */
107 r_name = buffer;
e8a7a315 108 memcpy(r_name, canonical, l+1);
4e8c8252
LP
109 idx = ALIGN(l+1);
110
68a9c7c4
ZJS
111 assert(n_addresses >= 0);
112 if (n_addresses == 0) {
8041b5ba
LP
113 /* Second, fill in IPv6 tuple */
114 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
115 r_tuple->next = r_tuple_prev;
116 r_tuple->name = r_name;
117 r_tuple->family = AF_INET6;
118 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
a1077c84 119 r_tuple->scopeid = 0;
8041b5ba
LP
120
121 idx += ALIGN(sizeof(struct gaih_addrtuple));
122 r_tuple_prev = r_tuple;
123
124 /* Third, fill in IPv4 tuple */
125 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
126 r_tuple->next = r_tuple_prev;
127 r_tuple->name = r_name;
128 r_tuple->family = AF_INET;
e8a7a315 129 *(uint32_t*) r_tuple->addr = local_address_ipv4;
a1077c84 130 r_tuple->scopeid = 0;
8041b5ba
LP
131
132 idx += ALIGN(sizeof(struct gaih_addrtuple));
133 r_tuple_prev = r_tuple;
134 }
135
136 /* Fourth, fill actual addresses in, but in backwards order */
92e9df9c
YW
137 for (int i = n_addresses; i > 0; i--) {
138 struct local_address *a = addresses + i - 1;
139
8041b5ba
LP
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;
94876904 144 r_tuple->scopeid = a->family == AF_INET6 && in6_addr_is_link_local(&a->address.in6) ? a->ifindex : 0;
5502f0d9 145 memcpy(r_tuple->addr, &a->address, 16);
8041b5ba
LP
146
147 idx += ALIGN(sizeof(struct gaih_addrtuple));
148 r_tuple_prev = r_tuple;
149 }
4e8c8252
LP
150
151 /* Verify the size matches */
152 assert(idx == ms);
153
d2f1f23a
ED
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;
4e8c8252
LP
159
160 if (ttlp)
161 *ttlp = 0;
162
06202b9e
YW
163 /* Explicitly reset both *h_errnop and h_errno to work around
164 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
e70df46b
LP
165 *h_errnop = NETDB_SUCCESS;
166 h_errno = 0;
167
4e8c8252 168 return NSS_STATUS_SUCCESS;
e09e7ac3
LP
169
170not_found:
171 *h_errnop = HOST_NOT_FOUND;
172 return NSS_STATUS_NOTFOUND;
4e8c8252 173}
6b21f0cf
LP
174
175static enum nss_status fill_in_hostent(
e8a7a315 176 const char *canonical, const char *additional,
4e8c8252 177 int af,
e80af1bd 178 struct local_address *addresses, unsigned n_addresses,
e8a7a315 179 uint32_t local_address_ipv4,
4e8c8252
LP
180 struct hostent *result,
181 char *buffer, size_t buflen,
0192cbdb 182 int *errnop, int *h_errnop,
4e8c8252
LP
183 int32_t *ttlp,
184 char **canonp) {
185
d4c9895d 186 size_t l_canonical, l_additional, idx, ms, alen;
e8a7a315 187 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
e80af1bd 188 struct local_address *a;
e8a7a315 189 unsigned n, c;
8041b5ba 190
5502f0d9
LP
191 assert(canonical);
192 assert(result);
193 assert(buffer);
194 assert(errnop);
195 assert(h_errnop);
196
cdccd29f
LP
197 PROTECT_ERRNO;
198
9d485985 199 alen = FAMILY_ADDRESS_SIZE(af);
8041b5ba 200
8041b5ba
LP
201 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
202 if (af == a->family)
203 c++;
4e8c8252 204
e8a7a315 205 l_canonical = strlen(canonical);
7bf7ce28 206 l_additional = strlen_ptr(additional);
e8a7a315
LP
207 ms = ALIGN(l_canonical+1)+
208 (additional ? ALIGN(l_additional+1) : 0) +
5502f0d9 209 sizeof(char*) +
e8a7a315 210 (additional ? sizeof(char*) : 0) +
d4c9895d 211 (c > 0 ? c : 1) * ALIGN(alen) +
5502f0d9 212 (c > 0 ? c+1 : 2) * sizeof(char*);
8041b5ba 213
4e8c8252 214 if (buflen < ms) {
cdccd29f 215 UNPROTECT_ERRNO;
0192cbdb 216 *errnop = ERANGE;
cda458a5 217 *h_errnop = NETDB_INTERNAL;
4e8c8252
LP
218 return NSS_STATUS_TRYAGAIN;
219 }
220
e8a7a315 221 /* First, fill in hostnames */
4e8c8252 222 r_name = buffer;
e8a7a315
LP
223 memcpy(r_name, canonical, l_canonical+1);
224 idx = ALIGN(l_canonical+1);
4e8c8252 225
e8a7a315
LP
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 */
4e8c8252 233 r_aliases = buffer + idx;
e8a7a315
LP
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 }
4e8c8252 242
8041b5ba 243 /* Third, add addresses */
4e8c8252 244 r_addr = buffer + idx;
8041b5ba
LP
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
5502f0d9 252 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
8041b5ba
LP
253 i++;
254 }
255
256 assert(i == c);
257 idx += c*ALIGN(alen);
258 } else {
259 if (af == AF_INET)
e8a7a315 260 *(uint32_t*) r_addr = local_address_ipv4;
8041b5ba
LP
261 else
262 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
263
264 idx += ALIGN(alen);
265 }
4e8c8252
LP
266
267 /* Fourth, add address pointer array */
268 r_addr_list = buffer + idx;
8041b5ba 269 if (c > 0) {
d4c9895d 270 unsigned i;
8041b5ba 271
d4c9895d
LP
272 for (i = 0; i < c; i++)
273 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
8041b5ba 274
d4c9895d
LP
275 ((char**) r_addr_list)[i] = NULL;
276 idx += (c+1) * sizeof(char*);
8041b5ba
LP
277
278 } else {
279 ((char**) r_addr_list)[0] = r_addr;
280 ((char**) r_addr_list)[1] = NULL;
d4c9895d 281 idx += 2 * sizeof(char*);
8041b5ba 282 }
4e8c8252
LP
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
06202b9e
YW
299 /* Explicitly reset both *h_errnop and h_errno to work around
300 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
e70df46b
LP
301 *h_errnop = NETDB_SUCCESS;
302 h_errno = 0;
303
4e8c8252 304 return NSS_STATUS_SUCCESS;
6b21f0cf
LP
305}
306
4e8c8252
LP
307enum 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) {
6b21f0cf 315
e80af1bd 316 _cleanup_free_ struct local_address *addresses = NULL;
e8a7a315 317 const char *canonical, *additional = NULL;
5502f0d9 318 _cleanup_free_ char *hn = NULL;
e9140aff 319 uint32_t local_address_ipv4 = 0;
e80af1bd 320 int n_addresses = 0;
5502f0d9 321
06202b9e 322 PROTECT_ERRNO;
0c5eb056
LP
323 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
324
5502f0d9
LP
325 assert(name);
326 assert(host);
327 assert(buffer);
328 assert(errnop);
329 assert(h_errnop);
6b21f0cf 330
4e8c8252
LP
331 if (af == AF_UNSPEC)
332 af = AF_INET;
6b21f0cf 333
ec2ce0c5 334 if (!IN_SET(af, AF_INET, AF_INET6)) {
cdccd29f 335 UNPROTECT_ERRNO;
0192cbdb 336 *errnop = EAFNOSUPPORT;
4e8c8252
LP
337 *h_errnop = NO_DATA;
338 return NSS_STATUS_UNAVAIL;
339 }
6b21f0cf 340
5502f0d9 341 if (is_localhost(name)) {
e8a7a315 342 canonical = "localhost";
8e38570e 343 local_address_ipv4 = htobe32(INADDR_LOOPBACK);
e9140aff 344
46a5e0e7 345 } else if (is_gateway_hostname(name)) {
e9140aff 346
1d050e1e 347 n_addresses = local_gateways(NULL, 0, af, &addresses);
e09e7ac3
LP
348 if (n_addresses <= 0)
349 goto not_found;
e9140aff 350
5248e7e1 351 canonical = "_gateway";
e9140aff 352
a1fdbcbe
LP
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
e8a7a315 361 } else {
5502f0d9
LP
362 hn = gethostname_malloc();
363 if (!hn) {
cdccd29f 364 UNPROTECT_ERRNO;
0192cbdb 365 *errnop = ENOMEM;
e8a7a315 366 *h_errnop = NO_RECOVERY;
5502f0d9 367 return NSS_STATUS_TRYAGAIN;
e8a7a315 368 }
6b21f0cf 369
e09e7ac3
LP
370 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), "."))
371 goto not_found;
e8a7a315 372
1d050e1e 373 n_addresses = local_addresses(NULL, 0, af, &addresses);
e80af1bd
LP
374 if (n_addresses < 0)
375 n_addresses = 0;
e8a7a315
LP
376
377 canonical = hn;
378 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
379 local_address_ipv4 = LOCALADDRESS_IPV4;
4e8c8252 380 }
6b21f0cf 381
cdccd29f
LP
382 UNPROTECT_ERRNO;
383
e8a7a315
LP
384 return fill_in_hostent(
385 canonical, additional,
386 af,
387 addresses, n_addresses,
388 local_address_ipv4,
389 host,
390 buffer, buflen,
0192cbdb 391 errnop, h_errnop,
e8a7a315
LP
392 ttlp,
393 canonp);
e09e7ac3
LP
394
395not_found:
396 *h_errnop = HOST_NOT_FOUND;
397 return NSS_STATUS_NOTFOUND;
4e8c8252 398}
6b21f0cf 399
4e8c8252
LP
400enum 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) {
6b21f0cf 407
e8a7a315 408 const char *canonical = NULL, *additional = NULL;
5502f0d9 409 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
e80af1bd 410 _cleanup_free_ struct local_address *addresses = NULL;
5502f0d9 411 _cleanup_free_ char *hn = NULL;
e80af1bd
LP
412 int n_addresses = 0;
413 struct local_address *a;
3fdcecc8 414 bool additional_from_hostname = false;
e80af1bd 415 unsigned n;
5502f0d9 416
06202b9e 417 PROTECT_ERRNO;
0c5eb056
LP
418 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
419
5502f0d9
LP
420 assert(addr);
421 assert(host);
422 assert(buffer);
423 assert(errnop);
424 assert(h_errnop);
8041b5ba 425
555bd6e9 426 if (!IN_SET(af, AF_INET, AF_INET6)) {
cdccd29f 427 UNPROTECT_ERRNO;
0192cbdb 428 *errnop = EAFNOSUPPORT;
555bd6e9
LP
429 *h_errnop = NO_DATA;
430 return NSS_STATUS_UNAVAIL;
431 }
432
9d485985 433 if (len != FAMILY_ADDRESS_SIZE(af)) {
cdccd29f 434 UNPROTECT_ERRNO;
0192cbdb 435 *errnop = EINVAL;
8041b5ba
LP
436 *h_errnop = NO_RECOVERY;
437 return NSS_STATUS_UNAVAIL;
438 }
6b21f0cf 439
4e8c8252 440 if (af == AF_INET) {
8041b5ba
LP
441 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
442 goto found;
6b21f0cf 443
8e38570e 444 if ((*(uint32_t*) addr) == htobe32(INADDR_LOOPBACK)) {
e8a7a315 445 canonical = "localhost";
8e38570e 446 local_address_ipv4 = htobe32(INADDR_LOOPBACK);
e8a7a315
LP
447 goto found;
448 }
449
555bd6e9
LP
450 } else {
451 assert(af == AF_INET6);
8041b5ba 452
e8a7a315 453 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
3fdcecc8
LP
454 canonical = "localhost";
455 additional_from_hostname = true;
8041b5ba 456 goto found;
e8a7a315 457 }
4e8c8252
LP
458 }
459
1d050e1e 460 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
68a9c7c4
ZJS
461 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
462 if (af != a->family)
463 continue;
8041b5ba 464
68a9c7c4
ZJS
465 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
466 goto found;
e9140aff
LP
467 }
468
97b11eed 469 addresses = mfree(addresses);
e9140aff 470
1d050e1e 471 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
68a9c7c4
ZJS
472 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
473 if (af != a->family)
474 continue;
e9140aff 475
68a9c7c4 476 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) {
5248e7e1 477 canonical = "_gateway";
68a9c7c4 478 goto found;
e9140aff 479 }
8041b5ba
LP
480 }
481
8041b5ba 482 *h_errnop = HOST_NOT_FOUND;
8041b5ba
LP
483 return NSS_STATUS_NOTFOUND;
484
485found:
82e4c2d6 486 if (!canonical || additional_from_hostname) {
3fdcecc8
LP
487 hn = gethostname_malloc();
488 if (!hn) {
cdccd29f 489 UNPROTECT_ERRNO;
0192cbdb 490 *errnop = ENOMEM;
3fdcecc8
LP
491 *h_errnop = NO_RECOVERY;
492 return NSS_STATUS_TRYAGAIN;
493 }
494
495 if (!canonical)
496 canonical = hn;
82e4c2d6 497 else
3fdcecc8
LP
498 additional = hn;
499 }
4e8c8252 500
cdccd29f 501 UNPROTECT_ERRNO;
e8a7a315
LP
502 return fill_in_hostent(
503 canonical, additional,
504 af,
505 addresses, n_addresses,
506 local_address_ipv4,
507 host,
508 buffer, buflen,
0192cbdb 509 errnop, h_errnop,
e8a7a315
LP
510 ttlp,
511 NULL);
4e8c8252 512}
6b21f0cf 513
c9fdc26e
LP
514NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
515NSS_GETHOSTBYADDR_FALLBACKS(myhostname);