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