]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-myhostname/nss-myhostname.c
Merge pull request #2138 from stefwalter/journal-combine
[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 <errno.h>
23 #include <net/if.h>
24 #include <netdb.h>
25 #include <nss.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "alloc-util.h"
30 #include "hostname-util.h"
31 #include "local-addresses.h"
32 #include "macro.h"
33 #include "nss-util.h"
34 #include "signal-util.h"
35 #include "string-util.h"
36 #include "util.h"
37
38 /* We use 127.0.0.2 as IPv4 address. This has the advantage over
39 * 127.0.0.1 that it can be translated back to the local hostname. For
40 * IPv6 we use ::1 which unfortunately will not translate back to the
41 * hostname but instead something like "localhost" or so. */
42
43 #define LOCALADDRESS_IPV4 (htonl(0x7F000002))
44 #define LOCALADDRESS_IPV6 &in6addr_loopback
45
46 NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
47 NSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
48
49 enum nss_status _nss_myhostname_gethostbyname4_r(
50 const char *name,
51 struct gaih_addrtuple **pat,
52 char *buffer, size_t buflen,
53 int *errnop, int *h_errnop,
54 int32_t *ttlp) {
55
56 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
57 _cleanup_free_ struct local_address *addresses = NULL;
58 _cleanup_free_ char *hn = NULL;
59 const char *canonical = NULL;
60 int n_addresses = 0, lo_ifi;
61 uint32_t local_address_ipv4;
62 struct local_address *a;
63 size_t l, idx, ms;
64 char *r_name;
65 unsigned n;
66
67 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
68
69 assert(name);
70 assert(pat);
71 assert(buffer);
72 assert(errnop);
73 assert(h_errnop);
74
75 if (is_localhost(name)) {
76 /* We respond to 'localhost', so that /etc/hosts
77 * is optional */
78
79 canonical = "localhost";
80 local_address_ipv4 = htonl(INADDR_LOOPBACK);
81
82 } else if (is_gateway_hostname(name)) {
83
84 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
85 if (n_addresses <= 0) {
86 *errnop = ENOENT;
87 *h_errnop = HOST_NOT_FOUND;
88 return NSS_STATUS_NOTFOUND;
89 }
90
91 canonical = "gateway";
92
93 } else {
94 hn = gethostname_malloc();
95 if (!hn) {
96 *errnop = ENOMEM;
97 *h_errnop = NO_RECOVERY;
98 return NSS_STATUS_TRYAGAIN;
99 }
100
101 /* We respond to our local host name, our our hostname suffixed with a single dot. */
102 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
103 *errnop = ENOENT;
104 *h_errnop = HOST_NOT_FOUND;
105 return NSS_STATUS_NOTFOUND;
106 }
107
108 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
109 if (n_addresses < 0)
110 n_addresses = 0;
111
112 canonical = hn;
113 local_address_ipv4 = LOCALADDRESS_IPV4;
114 }
115
116 /* If this call fails we fill in 0 as scope. Which is fine */
117 lo_ifi = n_addresses <= 0 ? LOOPBACK_IFINDEX : 0;
118
119 l = strlen(canonical);
120 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
121 if (buflen < ms) {
122 *errnop = ENOMEM;
123 *h_errnop = NO_RECOVERY;
124 return NSS_STATUS_TRYAGAIN;
125 }
126
127 /* First, fill in hostname */
128 r_name = buffer;
129 memcpy(r_name, canonical, l+1);
130 idx = ALIGN(l+1);
131
132 if (n_addresses <= 0) {
133 /* Second, fill in IPv6 tuple */
134 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
135 r_tuple->next = r_tuple_prev;
136 r_tuple->name = r_name;
137 r_tuple->family = AF_INET6;
138 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
139 r_tuple->scopeid = (uint32_t) lo_ifi;
140
141 idx += ALIGN(sizeof(struct gaih_addrtuple));
142 r_tuple_prev = r_tuple;
143
144 /* Third, fill in IPv4 tuple */
145 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
146 r_tuple->next = r_tuple_prev;
147 r_tuple->name = r_name;
148 r_tuple->family = AF_INET;
149 *(uint32_t*) r_tuple->addr = local_address_ipv4;
150 r_tuple->scopeid = (uint32_t) lo_ifi;
151
152 idx += ALIGN(sizeof(struct gaih_addrtuple));
153 r_tuple_prev = r_tuple;
154 }
155
156 /* Fourth, fill actual addresses in, but in backwards order */
157 for (a = addresses + n_addresses - 1, n = 0; (int) n < n_addresses; n++, a--) {
158 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
159 r_tuple->next = r_tuple_prev;
160 r_tuple->name = r_name;
161 r_tuple->family = a->family;
162 r_tuple->scopeid = a->ifindex;
163 memcpy(r_tuple->addr, &a->address, 16);
164
165 idx += ALIGN(sizeof(struct gaih_addrtuple));
166 r_tuple_prev = r_tuple;
167 }
168
169 /* Verify the size matches */
170 assert(idx == ms);
171
172 /* Nscd expects us to store the first record in **pat. */
173 if (*pat)
174 **pat = *r_tuple_prev;
175 else
176 *pat = r_tuple_prev;
177
178 if (ttlp)
179 *ttlp = 0;
180
181 /* Explicitly reset all error variables */
182 *errnop = 0;
183 *h_errnop = NETDB_SUCCESS;
184 h_errno = 0;
185
186 return NSS_STATUS_SUCCESS;
187 }
188
189 static enum nss_status fill_in_hostent(
190 const char *canonical, const char *additional,
191 int af,
192 struct local_address *addresses, unsigned n_addresses,
193 uint32_t local_address_ipv4,
194 struct hostent *result,
195 char *buffer, size_t buflen,
196 int *errnop, int *h_errnop,
197 int32_t *ttlp,
198 char **canonp) {
199
200 size_t l_canonical, l_additional, idx, ms, alen;
201 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
202 struct local_address *a;
203 unsigned n, c;
204
205 assert(canonical);
206 assert(result);
207 assert(buffer);
208 assert(errnop);
209 assert(h_errnop);
210
211 alen = FAMILY_ADDRESS_SIZE(af);
212
213 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
214 if (af == a->family)
215 c++;
216
217 l_canonical = strlen(canonical);
218 l_additional = additional ? strlen(additional) : 0;
219 ms = ALIGN(l_canonical+1)+
220 (additional ? ALIGN(l_additional+1) : 0) +
221 sizeof(char*) +
222 (additional ? sizeof(char*) : 0) +
223 (c > 0 ? c : 1) * ALIGN(alen) +
224 (c > 0 ? c+1 : 2) * sizeof(char*);
225
226 if (buflen < ms) {
227 *errnop = ENOMEM;
228 *h_errnop = NO_RECOVERY;
229 return NSS_STATUS_TRYAGAIN;
230 }
231
232 /* First, fill in hostnames */
233 r_name = buffer;
234 memcpy(r_name, canonical, l_canonical+1);
235 idx = ALIGN(l_canonical+1);
236
237 if (additional) {
238 r_alias = buffer + idx;
239 memcpy(r_alias, additional, l_additional+1);
240 idx += ALIGN(l_additional+1);
241 }
242
243 /* Second, create aliases array */
244 r_aliases = buffer + idx;
245 if (additional) {
246 ((char**) r_aliases)[0] = r_alias;
247 ((char**) r_aliases)[1] = NULL;
248 idx += 2*sizeof(char*);
249 } else {
250 ((char**) r_aliases)[0] = NULL;
251 idx += sizeof(char*);
252 }
253
254 /* Third, add addresses */
255 r_addr = buffer + idx;
256 if (c > 0) {
257 unsigned i = 0;
258
259 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
260 if (af != a->family)
261 continue;
262
263 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
264 i++;
265 }
266
267 assert(i == c);
268 idx += c*ALIGN(alen);
269 } else {
270 if (af == AF_INET)
271 *(uint32_t*) r_addr = local_address_ipv4;
272 else
273 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
274
275 idx += ALIGN(alen);
276 }
277
278 /* Fourth, add address pointer array */
279 r_addr_list = buffer + idx;
280 if (c > 0) {
281 unsigned i;
282
283 for (i = 0; i < c; i++)
284 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
285
286 ((char**) r_addr_list)[i] = NULL;
287 idx += (c+1) * sizeof(char*);
288
289 } else {
290 ((char**) r_addr_list)[0] = r_addr;
291 ((char**) r_addr_list)[1] = NULL;
292 idx += 2 * sizeof(char*);
293 }
294
295 /* Verify the size matches */
296 assert(idx == ms);
297
298 result->h_name = r_name;
299 result->h_aliases = (char**) r_aliases;
300 result->h_addrtype = af;
301 result->h_length = alen;
302 result->h_addr_list = (char**) r_addr_list;
303
304 if (ttlp)
305 *ttlp = 0;
306
307 if (canonp)
308 *canonp = r_name;
309
310 /* Explicitly reset all error variables */
311 *errnop = 0;
312 *h_errnop = NETDB_SUCCESS;
313 h_errno = 0;
314
315 return NSS_STATUS_SUCCESS;
316 }
317
318 enum nss_status _nss_myhostname_gethostbyname3_r(
319 const char *name,
320 int af,
321 struct hostent *host,
322 char *buffer, size_t buflen,
323 int *errnop, int *h_errnop,
324 int32_t *ttlp,
325 char **canonp) {
326
327 _cleanup_free_ struct local_address *addresses = NULL;
328 const char *canonical, *additional = NULL;
329 _cleanup_free_ char *hn = NULL;
330 uint32_t local_address_ipv4 = 0;
331 int n_addresses = 0;
332
333 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
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_hostname(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 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
418
419 assert(addr);
420 assert(host);
421 assert(buffer);
422 assert(errnop);
423 assert(h_errnop);
424
425 if (!IN_SET(af, AF_INET, AF_INET6)) {
426 *errnop = EAFNOSUPPORT;
427 *h_errnop = NO_DATA;
428 return NSS_STATUS_UNAVAIL;
429 }
430
431 if (len != FAMILY_ADDRESS_SIZE(af)) {
432 *errnop = EINVAL;
433 *h_errnop = NO_RECOVERY;
434 return NSS_STATUS_UNAVAIL;
435 }
436
437 if (af == AF_INET) {
438 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
439 goto found;
440
441 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
442 canonical = "localhost";
443 local_address_ipv4 = htonl(INADDR_LOOPBACK);
444 goto found;
445 }
446
447 } else {
448 assert(af == AF_INET6);
449
450 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
451 canonical = "localhost";
452 additional_from_hostname = true;
453 goto found;
454 }
455 }
456
457 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
458 if (n_addresses > 0) {
459 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
460 if (af != a->family)
461 continue;
462
463 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
464 goto found;
465 }
466 }
467
468 addresses = mfree(addresses);
469
470 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
471 if (n_addresses > 0) {
472 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
473 if (af != a->family)
474 continue;
475
476 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) {
477 canonical = "gateway";
478 goto found;
479 }
480 }
481 }
482
483 *errnop = ENOENT;
484 *h_errnop = HOST_NOT_FOUND;
485
486 return NSS_STATUS_NOTFOUND;
487
488 found:
489 if (!canonical || (!additional && additional_from_hostname)) {
490 hn = gethostname_malloc();
491 if (!hn) {
492 *errnop = ENOMEM;
493 *h_errnop = NO_RECOVERY;
494 return NSS_STATUS_TRYAGAIN;
495 }
496
497 if (!canonical)
498 canonical = hn;
499
500 if (!additional && additional_from_hostname)
501 additional = hn;
502 }
503
504 return fill_in_hostent(
505 canonical, additional,
506 af,
507 addresses, n_addresses,
508 local_address_ipv4,
509 host,
510 buffer, buflen,
511 errnop, h_errnop,
512 ttlp,
513 NULL);
514 }
515
516 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
517 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);