]> git.ipfire.org Git - thirdparty/systemd.git/blob - 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
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2008-2011 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 <errno.h>
22 #include <net/if.h>
23 #include <netdb.h>
24 #include <nss.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "alloc-util.h"
29 #include "hostname-util.h"
30 #include "local-addresses.h"
31 #include "macro.h"
32 #include "nss-util.h"
33 #include "signal-util.h"
34 #include "string-util.h"
35 #include "util.h"
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
40 * hostname but instead something like "localhost" or so. */
41
42 #define LOCALADDRESS_IPV4 (htobe32(0x7F000002))
43 #define LOCALADDRESS_IPV6 &in6addr_loopback
44
45 NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
46 NSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
47
48 enum 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
55 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
56 _cleanup_free_ struct local_address *addresses = NULL;
57 _cleanup_free_ char *hn = NULL;
58 const char *canonical = NULL;
59 int n_addresses = 0;
60 uint32_t local_address_ipv4;
61 struct local_address *a;
62 size_t l, idx, ms;
63 char *r_name;
64 unsigned n;
65
66 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
67
68 assert(name);
69 assert(pat);
70 assert(buffer);
71 assert(errnop);
72 assert(h_errnop);
73
74 if (is_localhost(name)) {
75 /* We respond to 'localhost', so that /etc/hosts
76 * is optional */
77
78 canonical = "localhost";
79 local_address_ipv4 = htobe32(INADDR_LOOPBACK);
80
81 } else if (is_gateway_hostname(name)) {
82
83 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
84 if (n_addresses <= 0) {
85 *errnop = ENOENT;
86 *h_errnop = HOST_NOT_FOUND;
87 return NSS_STATUS_NOTFOUND;
88 }
89
90 canonical = "_gateway";
91
92 } else {
93 hn = gethostname_malloc();
94 if (!hn) {
95 *errnop = ENOMEM;
96 *h_errnop = NO_RECOVERY;
97 return NSS_STATUS_TRYAGAIN;
98 }
99
100 /* We respond to our local host name, our hostname suffixed with a single dot. */
101 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
102 *errnop = ENOENT;
103 *h_errnop = HOST_NOT_FOUND;
104 return NSS_STATUS_NOTFOUND;
105 }
106
107 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
108 if (n_addresses < 0)
109 n_addresses = 0;
110
111 canonical = hn;
112 local_address_ipv4 = LOCALADDRESS_IPV4;
113 }
114
115 l = strlen(canonical);
116 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
117 if (buflen < ms) {
118 *errnop = ERANGE;
119 *h_errnop = NETDB_INTERNAL;
120 return NSS_STATUS_TRYAGAIN;
121 }
122
123 /* First, fill in hostname */
124 r_name = buffer;
125 memcpy(r_name, canonical, l+1);
126 idx = ALIGN(l+1);
127
128 assert(n_addresses >= 0);
129 if (n_addresses == 0) {
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);
136 r_tuple->scopeid = 0;
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;
146 *(uint32_t*) r_tuple->addr = local_address_ipv4;
147 r_tuple->scopeid = 0;
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 */
154 for (a = addresses + n_addresses - 1, n = 0; (int) n < n_addresses; n++, a--) {
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;
159 r_tuple->scopeid = a->family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&a->address.in6) ? a->ifindex : 0;
160 memcpy(r_tuple->addr, &a->address, 16);
161
162 idx += ALIGN(sizeof(struct gaih_addrtuple));
163 r_tuple_prev = r_tuple;
164 }
165
166 /* Verify the size matches */
167 assert(idx == ms);
168
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;
174
175 if (ttlp)
176 *ttlp = 0;
177
178 /* Explicitly reset all error variables */
179 *errnop = 0;
180 *h_errnop = NETDB_SUCCESS;
181 h_errno = 0;
182
183 return NSS_STATUS_SUCCESS;
184 }
185
186 static enum nss_status fill_in_hostent(
187 const char *canonical, const char *additional,
188 int af,
189 struct local_address *addresses, unsigned n_addresses,
190 uint32_t local_address_ipv4,
191 struct hostent *result,
192 char *buffer, size_t buflen,
193 int *errnop, int *h_errnop,
194 int32_t *ttlp,
195 char **canonp) {
196
197 size_t l_canonical, l_additional, idx, ms, alen;
198 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
199 struct local_address *a;
200 unsigned n, c;
201
202 assert(canonical);
203 assert(result);
204 assert(buffer);
205 assert(errnop);
206 assert(h_errnop);
207
208 alen = FAMILY_ADDRESS_SIZE(af);
209
210 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
211 if (af == a->family)
212 c++;
213
214 l_canonical = strlen(canonical);
215 l_additional = strlen_ptr(additional);
216 ms = ALIGN(l_canonical+1)+
217 (additional ? ALIGN(l_additional+1) : 0) +
218 sizeof(char*) +
219 (additional ? sizeof(char*) : 0) +
220 (c > 0 ? c : 1) * ALIGN(alen) +
221 (c > 0 ? c+1 : 2) * sizeof(char*);
222
223 if (buflen < ms) {
224 *errnop = ERANGE;
225 *h_errnop = NETDB_INTERNAL;
226 return NSS_STATUS_TRYAGAIN;
227 }
228
229 /* First, fill in hostnames */
230 r_name = buffer;
231 memcpy(r_name, canonical, l_canonical+1);
232 idx = ALIGN(l_canonical+1);
233
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 */
241 r_aliases = buffer + idx;
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 }
250
251 /* Third, add addresses */
252 r_addr = buffer + idx;
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
260 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
261 i++;
262 }
263
264 assert(i == c);
265 idx += c*ALIGN(alen);
266 } else {
267 if (af == AF_INET)
268 *(uint32_t*) r_addr = local_address_ipv4;
269 else
270 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
271
272 idx += ALIGN(alen);
273 }
274
275 /* Fourth, add address pointer array */
276 r_addr_list = buffer + idx;
277 if (c > 0) {
278 unsigned i;
279
280 for (i = 0; i < c; i++)
281 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
282
283 ((char**) r_addr_list)[i] = NULL;
284 idx += (c+1) * sizeof(char*);
285
286 } else {
287 ((char**) r_addr_list)[0] = r_addr;
288 ((char**) r_addr_list)[1] = NULL;
289 idx += 2 * sizeof(char*);
290 }
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
307 /* Explicitly reset all error variables */
308 *errnop = 0;
309 *h_errnop = NETDB_SUCCESS;
310 h_errno = 0;
311
312 return NSS_STATUS_SUCCESS;
313 }
314
315 enum 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) {
323
324 _cleanup_free_ struct local_address *addresses = NULL;
325 const char *canonical, *additional = NULL;
326 _cleanup_free_ char *hn = NULL;
327 uint32_t local_address_ipv4 = 0;
328 int n_addresses = 0;
329
330 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
331
332 assert(name);
333 assert(host);
334 assert(buffer);
335 assert(errnop);
336 assert(h_errnop);
337
338 if (af == AF_UNSPEC)
339 af = AF_INET;
340
341 if (!IN_SET(af, AF_INET, AF_INET6)) {
342 *errnop = EAFNOSUPPORT;
343 *h_errnop = NO_DATA;
344 return NSS_STATUS_UNAVAIL;
345 }
346
347 if (is_localhost(name)) {
348 canonical = "localhost";
349 local_address_ipv4 = htobe32(INADDR_LOOPBACK);
350
351 } else if (is_gateway_hostname(name)) {
352
353 n_addresses = local_gateways(NULL, 0, af, &addresses);
354 if (n_addresses <= 0) {
355 *errnop = ENOENT;
356 *h_errnop = HOST_NOT_FOUND;
357 return NSS_STATUS_NOTFOUND;
358 }
359
360 canonical = "_gateway";
361
362 } else {
363 hn = gethostname_malloc();
364 if (!hn) {
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 *errnop = ENOENT;
372 *h_errnop = HOST_NOT_FOUND;
373 return NSS_STATUS_NOTFOUND;
374 }
375
376 n_addresses = local_addresses(NULL, 0, af, &addresses);
377 if (n_addresses < 0)
378 n_addresses = 0;
379
380 canonical = hn;
381 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
382 local_address_ipv4 = LOCALADDRESS_IPV4;
383 }
384
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);
395 }
396
397 enum 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) {
404
405 const char *canonical = NULL, *additional = NULL;
406 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
407 _cleanup_free_ struct local_address *addresses = NULL;
408 _cleanup_free_ char *hn = NULL;
409 int n_addresses = 0;
410 struct local_address *a;
411 bool additional_from_hostname = false;
412 unsigned n;
413
414 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
415
416 assert(addr);
417 assert(host);
418 assert(buffer);
419 assert(errnop);
420 assert(h_errnop);
421
422 if (!IN_SET(af, AF_INET, AF_INET6)) {
423 *errnop = EAFNOSUPPORT;
424 *h_errnop = NO_DATA;
425 return NSS_STATUS_UNAVAIL;
426 }
427
428 if (len != FAMILY_ADDRESS_SIZE(af)) {
429 *errnop = EINVAL;
430 *h_errnop = NO_RECOVERY;
431 return NSS_STATUS_UNAVAIL;
432 }
433
434 if (af == AF_INET) {
435 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
436 goto found;
437
438 if ((*(uint32_t*) addr) == htobe32(INADDR_LOOPBACK)) {
439 canonical = "localhost";
440 local_address_ipv4 = htobe32(INADDR_LOOPBACK);
441 goto found;
442 }
443
444 } else {
445 assert(af == AF_INET6);
446
447 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
448 canonical = "localhost";
449 additional_from_hostname = true;
450 goto found;
451 }
452 }
453
454 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
455 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
456 if (af != a->family)
457 continue;
458
459 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
460 goto found;
461 }
462
463 addresses = mfree(addresses);
464
465 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
466 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
467 if (af != a->family)
468 continue;
469
470 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) {
471 canonical = "_gateway";
472 goto found;
473 }
474 }
475
476 *errnop = ENOENT;
477 *h_errnop = HOST_NOT_FOUND;
478 return NSS_STATUS_NOTFOUND;
479
480 found:
481 if (!canonical || additional_from_hostname) {
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;
491 else
492 additional = hn;
493 }
494
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);
505 }
506
507 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
508 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);