]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-myhostname/nss-myhostname.c
hostname-util: introduce new is_gateway_hostname() call
[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 enum nss_status _nss_myhostname_gethostbyname4_r(
47 const char *name,
48 struct gaih_addrtuple **pat,
49 char *buffer, size_t buflen,
50 int *errnop, int *h_errnop,
51 int32_t *ttlp) {
52
53 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
54 _cleanup_free_ struct local_address *addresses = NULL;
55 _cleanup_free_ char *hn = NULL;
56 const char *canonical = NULL;
57 int n_addresses = 0, lo_ifi;
58 uint32_t local_address_ipv4;
59 struct local_address *a;
60 size_t l, idx, ms;
61 char *r_name;
62 unsigned n;
63
64 assert(name);
65 assert(pat);
66 assert(buffer);
67 assert(errnop);
68 assert(h_errnop);
69
70 if (is_localhost(name)) {
71 /* We respond to 'localhost', so that /etc/hosts
72 * is optional */
73
74 canonical = "localhost";
75 local_address_ipv4 = htonl(INADDR_LOOPBACK);
76
77 } else if (is_gateway_hostname(name)) {
78
79 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
80 if (n_addresses <= 0) {
81 *errnop = ENOENT;
82 *h_errnop = HOST_NOT_FOUND;
83 return NSS_STATUS_NOTFOUND;
84 }
85
86 canonical = "gateway";
87
88 } else {
89 hn = gethostname_malloc();
90 if (!hn) {
91 *errnop = ENOMEM;
92 *h_errnop = NO_RECOVERY;
93 return NSS_STATUS_TRYAGAIN;
94 }
95
96 /* We respond to our local host name, our our hostname suffixed with a single dot. */
97 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
98 *errnop = ENOENT;
99 *h_errnop = HOST_NOT_FOUND;
100 return NSS_STATUS_NOTFOUND;
101 }
102
103 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
104 if (n_addresses < 0)
105 n_addresses = 0;
106
107 canonical = hn;
108 local_address_ipv4 = LOCALADDRESS_IPV4;
109 }
110
111 /* If this call fails we fill in 0 as scope. Which is fine */
112 lo_ifi = n_addresses <= 0 ? LOOPBACK_IFINDEX : 0;
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 if (n_addresses <= 0) {
128 /* Second, fill in IPv6 tuple */
129 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
130 r_tuple->next = r_tuple_prev;
131 r_tuple->name = r_name;
132 r_tuple->family = AF_INET6;
133 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
134 r_tuple->scopeid = (uint32_t) lo_ifi;
135
136 idx += ALIGN(sizeof(struct gaih_addrtuple));
137 r_tuple_prev = r_tuple;
138
139 /* Third, fill in IPv4 tuple */
140 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
141 r_tuple->next = r_tuple_prev;
142 r_tuple->name = r_name;
143 r_tuple->family = AF_INET;
144 *(uint32_t*) r_tuple->addr = local_address_ipv4;
145 r_tuple->scopeid = (uint32_t) lo_ifi;
146
147 idx += ALIGN(sizeof(struct gaih_addrtuple));
148 r_tuple_prev = r_tuple;
149 }
150
151 /* Fourth, fill actual addresses in, but in backwards order */
152 for (a = addresses + n_addresses - 1, n = 0; (int) n < n_addresses; n++, a--) {
153 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
154 r_tuple->next = r_tuple_prev;
155 r_tuple->name = r_name;
156 r_tuple->family = a->family;
157 r_tuple->scopeid = a->ifindex;
158 memcpy(r_tuple->addr, &a->address, 16);
159
160 idx += ALIGN(sizeof(struct gaih_addrtuple));
161 r_tuple_prev = r_tuple;
162 }
163
164 /* Verify the size matches */
165 assert(idx == ms);
166
167 /* Nscd expects us to store the first record in **pat. */
168 if (*pat)
169 **pat = *r_tuple_prev;
170 else
171 *pat = r_tuple_prev;
172
173 if (ttlp)
174 *ttlp = 0;
175
176 /* Explicitly reset all error variables */
177 *errnop = 0;
178 *h_errnop = NETDB_SUCCESS;
179 h_errno = 0;
180
181 return NSS_STATUS_SUCCESS;
182 }
183
184 static enum nss_status fill_in_hostent(
185 const char *canonical, const char *additional,
186 int af,
187 struct local_address *addresses, unsigned n_addresses,
188 uint32_t local_address_ipv4,
189 struct hostent *result,
190 char *buffer, size_t buflen,
191 int *errnop, int *h_errnop,
192 int32_t *ttlp,
193 char **canonp) {
194
195 size_t l_canonical, l_additional, idx, ms, alen;
196 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
197 struct local_address *a;
198 unsigned n, c;
199
200 assert(canonical);
201 assert(result);
202 assert(buffer);
203 assert(errnop);
204 assert(h_errnop);
205
206 alen = FAMILY_ADDRESS_SIZE(af);
207
208 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
209 if (af == a->family)
210 c++;
211
212 l_canonical = strlen(canonical);
213 l_additional = additional ? strlen(additional) : 0;
214 ms = ALIGN(l_canonical+1)+
215 (additional ? ALIGN(l_additional+1) : 0) +
216 sizeof(char*) +
217 (additional ? sizeof(char*) : 0) +
218 (c > 0 ? c : 1) * ALIGN(alen) +
219 (c > 0 ? c+1 : 2) * sizeof(char*);
220
221 if (buflen < ms) {
222 *errnop = ENOMEM;
223 *h_errnop = NO_RECOVERY;
224 return NSS_STATUS_TRYAGAIN;
225 }
226
227 /* First, fill in hostnames */
228 r_name = buffer;
229 memcpy(r_name, canonical, l_canonical+1);
230 idx = ALIGN(l_canonical+1);
231
232 if (additional) {
233 r_alias = buffer + idx;
234 memcpy(r_alias, additional, l_additional+1);
235 idx += ALIGN(l_additional+1);
236 }
237
238 /* Second, create aliases array */
239 r_aliases = buffer + idx;
240 if (additional) {
241 ((char**) r_aliases)[0] = r_alias;
242 ((char**) r_aliases)[1] = NULL;
243 idx += 2*sizeof(char*);
244 } else {
245 ((char**) r_aliases)[0] = NULL;
246 idx += sizeof(char*);
247 }
248
249 /* Third, add addresses */
250 r_addr = buffer + idx;
251 if (c > 0) {
252 unsigned i = 0;
253
254 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
255 if (af != a->family)
256 continue;
257
258 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
259 i++;
260 }
261
262 assert(i == c);
263 idx += c*ALIGN(alen);
264 } else {
265 if (af == AF_INET)
266 *(uint32_t*) r_addr = local_address_ipv4;
267 else
268 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
269
270 idx += ALIGN(alen);
271 }
272
273 /* Fourth, add address pointer array */
274 r_addr_list = buffer + idx;
275 if (c > 0) {
276 unsigned i;
277
278 for (i = 0; i < c; i++)
279 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
280
281 ((char**) r_addr_list)[i] = NULL;
282 idx += (c+1) * sizeof(char*);
283
284 } else {
285 ((char**) r_addr_list)[0] = r_addr;
286 ((char**) r_addr_list)[1] = NULL;
287 idx += 2 * sizeof(char*);
288 }
289
290 /* Verify the size matches */
291 assert(idx == ms);
292
293 result->h_name = r_name;
294 result->h_aliases = (char**) r_aliases;
295 result->h_addrtype = af;
296 result->h_length = alen;
297 result->h_addr_list = (char**) r_addr_list;
298
299 if (ttlp)
300 *ttlp = 0;
301
302 if (canonp)
303 *canonp = r_name;
304
305 /* Explicitly reset all error variables */
306 *errnop = 0;
307 *h_errnop = NETDB_SUCCESS;
308 h_errno = 0;
309
310 return NSS_STATUS_SUCCESS;
311 }
312
313 enum nss_status _nss_myhostname_gethostbyname3_r(
314 const char *name,
315 int af,
316 struct hostent *host,
317 char *buffer, size_t buflen,
318 int *errnop, int *h_errnop,
319 int32_t *ttlp,
320 char **canonp) {
321
322 _cleanup_free_ struct local_address *addresses = NULL;
323 const char *canonical, *additional = NULL;
324 _cleanup_free_ char *hn = NULL;
325 uint32_t local_address_ipv4 = 0;
326 int n_addresses = 0;
327
328 assert(name);
329 assert(host);
330 assert(buffer);
331 assert(errnop);
332 assert(h_errnop);
333
334 if (af == AF_UNSPEC)
335 af = AF_INET;
336
337 if (af != AF_INET && af != AF_INET6) {
338 *errnop = EAFNOSUPPORT;
339 *h_errnop = NO_DATA;
340 return NSS_STATUS_UNAVAIL;
341 }
342
343 if (is_localhost(name)) {
344 canonical = "localhost";
345 local_address_ipv4 = htonl(INADDR_LOOPBACK);
346
347 } else if (is_gateway_hostname(name)) {
348
349 n_addresses = local_gateways(NULL, 0, af, &addresses);
350 if (n_addresses <= 0) {
351 *errnop = ENOENT;
352 *h_errnop = HOST_NOT_FOUND;
353 return NSS_STATUS_NOTFOUND;
354 }
355
356 canonical = "gateway";
357
358 } else {
359 hn = gethostname_malloc();
360 if (!hn) {
361 *errnop = ENOMEM;
362 *h_errnop = NO_RECOVERY;
363 return NSS_STATUS_TRYAGAIN;
364 }
365
366 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
367 *errnop = ENOENT;
368 *h_errnop = HOST_NOT_FOUND;
369 return NSS_STATUS_NOTFOUND;
370 }
371
372 n_addresses = local_addresses(NULL, 0, af, &addresses);
373 if (n_addresses < 0)
374 n_addresses = 0;
375
376 canonical = hn;
377 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
378 local_address_ipv4 = LOCALADDRESS_IPV4;
379 }
380
381 return fill_in_hostent(
382 canonical, additional,
383 af,
384 addresses, n_addresses,
385 local_address_ipv4,
386 host,
387 buffer, buflen,
388 errnop, h_errnop,
389 ttlp,
390 canonp);
391 }
392
393 enum nss_status _nss_myhostname_gethostbyaddr2_r(
394 const void* addr, socklen_t len,
395 int af,
396 struct hostent *host,
397 char *buffer, size_t buflen,
398 int *errnop, int *h_errnop,
399 int32_t *ttlp) {
400
401 const char *canonical = NULL, *additional = NULL;
402 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
403 _cleanup_free_ struct local_address *addresses = NULL;
404 _cleanup_free_ char *hn = NULL;
405 int n_addresses = 0;
406 struct local_address *a;
407 bool additional_from_hostname = false;
408 unsigned n;
409
410 assert(addr);
411 assert(host);
412 assert(buffer);
413 assert(errnop);
414 assert(h_errnop);
415
416 if (!IN_SET(af, AF_INET, AF_INET6)) {
417 *errnop = EAFNOSUPPORT;
418 *h_errnop = NO_DATA;
419 return NSS_STATUS_UNAVAIL;
420 }
421
422 if (len != FAMILY_ADDRESS_SIZE(af)) {
423 *errnop = EINVAL;
424 *h_errnop = NO_RECOVERY;
425 return NSS_STATUS_UNAVAIL;
426 }
427
428 if (af == AF_INET) {
429 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
430 goto found;
431
432 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
433 canonical = "localhost";
434 local_address_ipv4 = htonl(INADDR_LOOPBACK);
435 goto found;
436 }
437
438 } else {
439 assert(af == AF_INET6);
440
441 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
442 canonical = "localhost";
443 additional_from_hostname = true;
444 goto found;
445 }
446 }
447
448 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
449 if (n_addresses > 0) {
450 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
451 if (af != a->family)
452 continue;
453
454 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
455 goto found;
456 }
457 }
458
459 addresses = mfree(addresses);
460
461 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
462 if (n_addresses > 0) {
463 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
464 if (af != a->family)
465 continue;
466
467 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) {
468 canonical = "gateway";
469 goto found;
470 }
471 }
472 }
473
474 *errnop = ENOENT;
475 *h_errnop = HOST_NOT_FOUND;
476
477 return NSS_STATUS_NOTFOUND;
478
479 found:
480 if (!canonical || (!additional && 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
491 if (!additional && additional_from_hostname)
492 additional = hn;
493 }
494
495 return fill_in_hostent(
496 canonical, additional,
497 af,
498 addresses, n_addresses,
499 local_address_ipv4,
500 host,
501 buffer, buflen,
502 errnop, h_errnop,
503 ttlp,
504 NULL);
505 }
506
507 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
508 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);