]> git.ipfire.org Git - thirdparty/systemd.git/blob - 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
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2008-2011 Lennart Poettering
7
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.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
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/>.
20 ***/
21
22 #include <nss.h>
23 #include <netdb.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <net/if.h>
27 #include <stdlib.h>
28
29 #include "local-addresses.h"
30 #include "macro.h"
31 #include "nss-util.h"
32 #include "hostname-util.h"
33 #include "util.h"
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
38 * hostname but instead something like "localhost" or so. */
39
40 #define LOCALADDRESS_IPV4 (htonl(0x7F000002))
41 #define LOCALADDRESS_IPV6 &in6addr_loopback
42
43 NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
44 NSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
45
46 static bool is_gateway(const char *hostname) {
47 assert(hostname);
48
49 return streq(hostname, "gateway") ||
50 streq(hostname, "gateway.");
51 }
52
53 enum 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
60 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
61 _cleanup_free_ struct local_address *addresses = NULL;
62 _cleanup_free_ char *hn = NULL;
63 const char *canonical = NULL;
64 int n_addresses = 0, lo_ifi;
65 uint32_t local_address_ipv4;
66 struct local_address *a;
67 size_t l, idx, ms;
68 char *r_name;
69 unsigned n;
70
71 assert(name);
72 assert(pat);
73 assert(buffer);
74 assert(errnop);
75 assert(h_errnop);
76
77 if (is_localhost(name)) {
78 /* We respond to 'localhost', so that /etc/hosts
79 * is optional */
80
81 canonical = "localhost";
82 local_address_ipv4 = htonl(INADDR_LOOPBACK);
83
84 } else if (is_gateway(name)) {
85
86 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
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
95 } else {
96 hn = gethostname_malloc();
97 if (!hn) {
98 *errnop = ENOMEM;
99 *h_errnop = NO_RECOVERY;
100 return NSS_STATUS_TRYAGAIN;
101 }
102
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), ".")) {
105 *errnop = ENOENT;
106 *h_errnop = HOST_NOT_FOUND;
107 return NSS_STATUS_NOTFOUND;
108 }
109
110 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
111 if (n_addresses < 0)
112 n_addresses = 0;
113
114 canonical = hn;
115 local_address_ipv4 = LOCALADDRESS_IPV4;
116 }
117
118 /* If this call fails we fill in 0 as scope. Which is fine */
119 lo_ifi = n_addresses <= 0 ? LOOPBACK_IFINDEX : 0;
120
121 l = strlen(canonical);
122 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
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;
131 memcpy(r_name, canonical, l+1);
132 idx = ALIGN(l+1);
133
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;
151 *(uint32_t*) r_tuple->addr = local_address_ipv4;
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 */
159 for (a = addresses + n_addresses - 1, n = 0; (int) n < n_addresses; n++, a--) {
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;
165 memcpy(r_tuple->addr, &a->address, 16);
166
167 idx += ALIGN(sizeof(struct gaih_addrtuple));
168 r_tuple_prev = r_tuple;
169 }
170
171 /* Verify the size matches */
172 assert(idx == ms);
173
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;
179
180 if (ttlp)
181 *ttlp = 0;
182
183 /* Explicitly reset all error variables */
184 *errnop = 0;
185 *h_errnop = NETDB_SUCCESS;
186 h_errno = 0;
187
188 return NSS_STATUS_SUCCESS;
189 }
190
191 static enum nss_status fill_in_hostent(
192 const char *canonical, const char *additional,
193 int af,
194 struct local_address *addresses, unsigned n_addresses,
195 uint32_t local_address_ipv4,
196 struct hostent *result,
197 char *buffer, size_t buflen,
198 int *errnop, int *h_errnop,
199 int32_t *ttlp,
200 char **canonp) {
201
202 size_t l_canonical, l_additional, idx, ms, alen;
203 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
204 struct local_address *a;
205 unsigned n, c;
206
207 assert(canonical);
208 assert(result);
209 assert(buffer);
210 assert(errnop);
211 assert(h_errnop);
212
213 alen = FAMILY_ADDRESS_SIZE(af);
214
215 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
216 if (af == a->family)
217 c++;
218
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) +
223 sizeof(char*) +
224 (additional ? sizeof(char*) : 0) +
225 (c > 0 ? c : 1) * ALIGN(alen) +
226 (c > 0 ? c+1 : 2) * sizeof(char*);
227
228 if (buflen < ms) {
229 *errnop = ENOMEM;
230 *h_errnop = NO_RECOVERY;
231 return NSS_STATUS_TRYAGAIN;
232 }
233
234 /* First, fill in hostnames */
235 r_name = buffer;
236 memcpy(r_name, canonical, l_canonical+1);
237 idx = ALIGN(l_canonical+1);
238
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 */
246 r_aliases = buffer + idx;
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 }
255
256 /* Third, add addresses */
257 r_addr = buffer + idx;
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
265 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
266 i++;
267 }
268
269 assert(i == c);
270 idx += c*ALIGN(alen);
271 } else {
272 if (af == AF_INET)
273 *(uint32_t*) r_addr = local_address_ipv4;
274 else
275 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
276
277 idx += ALIGN(alen);
278 }
279
280 /* Fourth, add address pointer array */
281 r_addr_list = buffer + idx;
282 if (c > 0) {
283 unsigned i;
284
285 for (i = 0; i < c; i++)
286 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
287
288 ((char**) r_addr_list)[i] = NULL;
289 idx += (c+1) * sizeof(char*);
290
291 } else {
292 ((char**) r_addr_list)[0] = r_addr;
293 ((char**) r_addr_list)[1] = NULL;
294 idx += 2 * sizeof(char*);
295 }
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
312 /* Explicitly reset all error variables */
313 *errnop = 0;
314 *h_errnop = NETDB_SUCCESS;
315 h_errno = 0;
316
317 return NSS_STATUS_SUCCESS;
318 }
319
320 enum 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) {
328
329 _cleanup_free_ struct local_address *addresses = NULL;
330 const char *canonical, *additional = NULL;
331 _cleanup_free_ char *hn = NULL;
332 uint32_t local_address_ipv4 = 0;
333 int n_addresses = 0;
334
335 assert(name);
336 assert(host);
337 assert(buffer);
338 assert(errnop);
339 assert(h_errnop);
340
341 if (af == AF_UNSPEC)
342 af = AF_INET;
343
344 if (af != AF_INET && af != AF_INET6) {
345 *errnop = EAFNOSUPPORT;
346 *h_errnop = NO_DATA;
347 return NSS_STATUS_UNAVAIL;
348 }
349
350 if (is_localhost(name)) {
351 canonical = "localhost";
352 local_address_ipv4 = htonl(INADDR_LOOPBACK);
353
354 } else if (is_gateway(name)) {
355
356 n_addresses = local_gateways(NULL, 0, af, &addresses);
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
365 } else {
366 hn = gethostname_malloc();
367 if (!hn) {
368 *errnop = ENOMEM;
369 *h_errnop = NO_RECOVERY;
370 return NSS_STATUS_TRYAGAIN;
371 }
372
373 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
374 *errnop = ENOENT;
375 *h_errnop = HOST_NOT_FOUND;
376 return NSS_STATUS_NOTFOUND;
377 }
378
379 n_addresses = local_addresses(NULL, 0, af, &addresses);
380 if (n_addresses < 0)
381 n_addresses = 0;
382
383 canonical = hn;
384 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
385 local_address_ipv4 = LOCALADDRESS_IPV4;
386 }
387
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);
398 }
399
400 enum 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) {
407
408 const char *canonical = NULL, *additional = NULL;
409 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
410 _cleanup_free_ struct local_address *addresses = NULL;
411 _cleanup_free_ char *hn = NULL;
412 int n_addresses = 0;
413 struct local_address *a;
414 bool additional_from_hostname = false;
415 unsigned n;
416
417 assert(addr);
418 assert(host);
419 assert(buffer);
420 assert(errnop);
421 assert(h_errnop);
422
423 if (!IN_SET(af, AF_INET, AF_INET6)) {
424 *errnop = EAFNOSUPPORT;
425 *h_errnop = NO_DATA;
426 return NSS_STATUS_UNAVAIL;
427 }
428
429 if (len != FAMILY_ADDRESS_SIZE(af)) {
430 *errnop = EINVAL;
431 *h_errnop = NO_RECOVERY;
432 return NSS_STATUS_UNAVAIL;
433 }
434
435 if (af == AF_INET) {
436 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
437 goto found;
438
439 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
440 canonical = "localhost";
441 local_address_ipv4 = htonl(INADDR_LOOPBACK);
442 goto found;
443 }
444
445 } else {
446 assert(af == AF_INET6);
447
448 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
449 canonical = "localhost";
450 additional_from_hostname = true;
451 goto found;
452 }
453 }
454
455 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
456 if (n_addresses > 0) {
457 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
458 if (af != a->family)
459 continue;
460
461 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
462 goto found;
463 }
464 }
465
466 addresses = mfree(addresses);
467
468 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
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) {
475 canonical = "gateway";
476 goto found;
477 }
478 }
479 }
480
481 *errnop = ENOENT;
482 *h_errnop = HOST_NOT_FOUND;
483
484 return NSS_STATUS_NOTFOUND;
485
486 found:
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 }
501
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);
512 }
513
514 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
515 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);