]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-myhostname/nss-myhostname.c
Merge pull request #1607 from keszybz/lz4-remove-v1
[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 "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 (htonl(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, lo_ifi;
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 assert(name);
67 assert(pat);
68 assert(buffer);
69 assert(errnop);
70 assert(h_errnop);
71
72 if (is_localhost(name)) {
73 /* We respond to 'localhost', so that /etc/hosts
74 * is optional */
75
76 canonical = "localhost";
77 local_address_ipv4 = htonl(INADDR_LOOPBACK);
78
79 } else if (is_gateway_hostname(name)) {
80
81 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
82 if (n_addresses <= 0) {
83 *errnop = ENOENT;
84 *h_errnop = HOST_NOT_FOUND;
85 return NSS_STATUS_NOTFOUND;
86 }
87
88 canonical = "gateway";
89
90 } else {
91 hn = gethostname_malloc();
92 if (!hn) {
93 *errnop = ENOMEM;
94 *h_errnop = NO_RECOVERY;
95 return NSS_STATUS_TRYAGAIN;
96 }
97
98 /* We respond to our local host name, our our hostname suffixed with a single dot. */
99 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
100 *errnop = ENOENT;
101 *h_errnop = HOST_NOT_FOUND;
102 return NSS_STATUS_NOTFOUND;
103 }
104
105 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
106 if (n_addresses < 0)
107 n_addresses = 0;
108
109 canonical = hn;
110 local_address_ipv4 = LOCALADDRESS_IPV4;
111 }
112
113 /* If this call fails we fill in 0 as scope. Which is fine */
114 lo_ifi = n_addresses <= 0 ? LOOPBACK_IFINDEX : 0;
115
116 l = strlen(canonical);
117 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
118 if (buflen < ms) {
119 *errnop = ENOMEM;
120 *h_errnop = NO_RECOVERY;
121 return NSS_STATUS_TRYAGAIN;
122 }
123
124 /* First, fill in hostname */
125 r_name = buffer;
126 memcpy(r_name, canonical, l+1);
127 idx = ALIGN(l+1);
128
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 = (uint32_t) lo_ifi;
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 = (uint32_t) lo_ifi;
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->ifindex;
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 = additional ? strlen(additional) : 0;
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 = ENOMEM;
225 *h_errnop = NO_RECOVERY;
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 assert(name);
331 assert(host);
332 assert(buffer);
333 assert(errnop);
334 assert(h_errnop);
335
336 if (af == AF_UNSPEC)
337 af = AF_INET;
338
339 if (af != AF_INET && af != AF_INET6) {
340 *errnop = EAFNOSUPPORT;
341 *h_errnop = NO_DATA;
342 return NSS_STATUS_UNAVAIL;
343 }
344
345 if (is_localhost(name)) {
346 canonical = "localhost";
347 local_address_ipv4 = htonl(INADDR_LOOPBACK);
348
349 } else if (is_gateway_hostname(name)) {
350
351 n_addresses = local_gateways(NULL, 0, af, &addresses);
352 if (n_addresses <= 0) {
353 *errnop = ENOENT;
354 *h_errnop = HOST_NOT_FOUND;
355 return NSS_STATUS_NOTFOUND;
356 }
357
358 canonical = "gateway";
359
360 } else {
361 hn = gethostname_malloc();
362 if (!hn) {
363 *errnop = ENOMEM;
364 *h_errnop = NO_RECOVERY;
365 return NSS_STATUS_TRYAGAIN;
366 }
367
368 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
369 *errnop = ENOENT;
370 *h_errnop = HOST_NOT_FOUND;
371 return NSS_STATUS_NOTFOUND;
372 }
373
374 n_addresses = local_addresses(NULL, 0, af, &addresses);
375 if (n_addresses < 0)
376 n_addresses = 0;
377
378 canonical = hn;
379 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
380 local_address_ipv4 = LOCALADDRESS_IPV4;
381 }
382
383 return fill_in_hostent(
384 canonical, additional,
385 af,
386 addresses, n_addresses,
387 local_address_ipv4,
388 host,
389 buffer, buflen,
390 errnop, h_errnop,
391 ttlp,
392 canonp);
393 }
394
395 enum nss_status _nss_myhostname_gethostbyaddr2_r(
396 const void* addr, socklen_t len,
397 int af,
398 struct hostent *host,
399 char *buffer, size_t buflen,
400 int *errnop, int *h_errnop,
401 int32_t *ttlp) {
402
403 const char *canonical = NULL, *additional = NULL;
404 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
405 _cleanup_free_ struct local_address *addresses = NULL;
406 _cleanup_free_ char *hn = NULL;
407 int n_addresses = 0;
408 struct local_address *a;
409 bool additional_from_hostname = false;
410 unsigned n;
411
412 assert(addr);
413 assert(host);
414 assert(buffer);
415 assert(errnop);
416 assert(h_errnop);
417
418 if (!IN_SET(af, AF_INET, AF_INET6)) {
419 *errnop = EAFNOSUPPORT;
420 *h_errnop = NO_DATA;
421 return NSS_STATUS_UNAVAIL;
422 }
423
424 if (len != FAMILY_ADDRESS_SIZE(af)) {
425 *errnop = EINVAL;
426 *h_errnop = NO_RECOVERY;
427 return NSS_STATUS_UNAVAIL;
428 }
429
430 if (af == AF_INET) {
431 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
432 goto found;
433
434 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
435 canonical = "localhost";
436 local_address_ipv4 = htonl(INADDR_LOOPBACK);
437 goto found;
438 }
439
440 } else {
441 assert(af == AF_INET6);
442
443 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
444 canonical = "localhost";
445 additional_from_hostname = true;
446 goto found;
447 }
448 }
449
450 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
451 if (n_addresses > 0) {
452 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
453 if (af != a->family)
454 continue;
455
456 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
457 goto found;
458 }
459 }
460
461 addresses = mfree(addresses);
462
463 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
464 if (n_addresses > 0) {
465 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
466 if (af != a->family)
467 continue;
468
469 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) {
470 canonical = "gateway";
471 goto found;
472 }
473 }
474 }
475
476 *errnop = ENOENT;
477 *h_errnop = HOST_NOT_FOUND;
478
479 return NSS_STATUS_NOTFOUND;
480
481 found:
482 if (!canonical || (!additional && additional_from_hostname)) {
483 hn = gethostname_malloc();
484 if (!hn) {
485 *errnop = ENOMEM;
486 *h_errnop = NO_RECOVERY;
487 return NSS_STATUS_TRYAGAIN;
488 }
489
490 if (!canonical)
491 canonical = hn;
492
493 if (!additional && additional_from_hostname)
494 additional = hn;
495 }
496
497 return fill_in_hostent(
498 canonical, additional,
499 af,
500 addresses, n_addresses,
501 local_address_ipv4,
502 host,
503 buffer, buflen,
504 errnop, h_errnop,
505 ttlp,
506 NULL);
507 }
508
509 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
510 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);