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