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