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