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