]> git.ipfire.org Git - thirdparty/systemd.git/blame - 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
CommitLineData
8041b5ba 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4e8c8252 2
4e8c8252 3/***
cbc06dcd 4 This file is part of systemd.
4e8c8252 5
8041b5ba 6 Copyright 2008-2011 Lennart Poettering
4e8c8252 7
cbc06dcd
TG
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.
4e8c8252 12
cbc06dcd
TG
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
8041b5ba
LP
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
4e8c8252 17
cbc06dcd
TG
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/>.
4e8c8252 20***/
6b21f0cf 21
6b21f0cf 22#include <errno.h>
4e8c8252 23#include <net/if.h>
07630cea
LP
24#include <netdb.h>
25#include <nss.h>
8041b5ba 26#include <stdlib.h>
07630cea 27#include <string.h>
8041b5ba 28
07630cea 29#include "hostname-util.h"
e80af1bd 30#include "local-addresses.h"
1c633045 31#include "macro.h"
c9fdc26e 32#include "nss-util.h"
07630cea 33#include "string-util.h"
c9fdc26e 34#include "util.h"
4e8c8252
LP
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
3fdcecc8 39 * hostname but instead something like "localhost" or so. */
4e8c8252
LP
40
41#define LOCALADDRESS_IPV4 (htonl(0x7F000002))
42#define LOCALADDRESS_IPV6 &in6addr_loopback
4e8c8252 43
c9fdc26e
LP
44NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
45NSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
8041b5ba 46
4e8c8252
LP
47enum 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
8041b5ba 54 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
e80af1bd 55 _cleanup_free_ struct local_address *addresses = NULL;
5502f0d9
LP
56 _cleanup_free_ char *hn = NULL;
57 const char *canonical = NULL;
e80af1bd 58 int n_addresses = 0, lo_ifi;
e8a7a315 59 uint32_t local_address_ipv4;
e80af1bd 60 struct local_address *a;
5502f0d9
LP
61 size_t l, idx, ms;
62 char *r_name;
e80af1bd 63 unsigned n;
4e8c8252 64
5502f0d9
LP
65 assert(name);
66 assert(pat);
67 assert(buffer);
68 assert(errnop);
69 assert(h_errnop);
70
71 if (is_localhost(name)) {
e8a7a315
LP
72 /* We respond to 'localhost', so that /etc/hosts
73 * is optional */
4e8c8252 74
e8a7a315
LP
75 canonical = "localhost";
76 local_address_ipv4 = htonl(INADDR_LOOPBACK);
e9140aff 77
46a5e0e7 78 } else if (is_gateway_hostname(name)) {
e9140aff 79
1d050e1e 80 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
e9140aff
LP
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
e8a7a315 89 } else {
5502f0d9
LP
90 hn = gethostname_malloc();
91 if (!hn) {
92 *errnop = ENOMEM;
e8a7a315 93 *h_errnop = NO_RECOVERY;
5502f0d9 94 return NSS_STATUS_TRYAGAIN;
e8a7a315
LP
95 }
96
5502f0d9
LP
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), ".")) {
e8a7a315
LP
99 *errnop = ENOENT;
100 *h_errnop = HOST_NOT_FOUND;
101 return NSS_STATUS_NOTFOUND;
102 }
103
1d050e1e 104 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
e80af1bd
LP
105 if (n_addresses < 0)
106 n_addresses = 0;
e8a7a315
LP
107
108 canonical = hn;
109 local_address_ipv4 = LOCALADDRESS_IPV4;
110 }
8041b5ba 111
4e8c8252 112 /* If this call fails we fill in 0 as scope. Which is fine */
f4324288 113 lo_ifi = n_addresses <= 0 ? LOOPBACK_IFINDEX : 0;
4e8c8252 114
e8a7a315 115 l = strlen(canonical);
5502f0d9 116 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
4e8c8252
LP
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;
e8a7a315 125 memcpy(r_name, canonical, l+1);
4e8c8252
LP
126 idx = ALIGN(l+1);
127
8041b5ba
LP
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;
e8a7a315 145 *(uint32_t*) r_tuple->addr = local_address_ipv4;
8041b5ba
LP
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 */
e80af1bd 153 for (a = addresses + n_addresses - 1, n = 0; (int) n < n_addresses; n++, a--) {
8041b5ba
LP
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;
5502f0d9 159 memcpy(r_tuple->addr, &a->address, 16);
8041b5ba
LP
160
161 idx += ALIGN(sizeof(struct gaih_addrtuple));
162 r_tuple_prev = r_tuple;
163 }
4e8c8252
LP
164
165 /* Verify the size matches */
166 assert(idx == ms);
167
d2f1f23a
ED
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;
4e8c8252
LP
173
174 if (ttlp)
175 *ttlp = 0;
176
e70df46b
LP
177 /* Explicitly reset all error variables */
178 *errnop = 0;
179 *h_errnop = NETDB_SUCCESS;
180 h_errno = 0;
181
4e8c8252
LP
182 return NSS_STATUS_SUCCESS;
183}
6b21f0cf
LP
184
185static enum nss_status fill_in_hostent(
e8a7a315 186 const char *canonical, const char *additional,
4e8c8252 187 int af,
e80af1bd 188 struct local_address *addresses, unsigned n_addresses,
e8a7a315 189 uint32_t local_address_ipv4,
4e8c8252
LP
190 struct hostent *result,
191 char *buffer, size_t buflen,
192 int *errnop, int *h_errnop,
193 int32_t *ttlp,
194 char **canonp) {
195
d4c9895d 196 size_t l_canonical, l_additional, idx, ms, alen;
e8a7a315 197 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
e80af1bd 198 struct local_address *a;
e8a7a315 199 unsigned n, c;
8041b5ba 200
5502f0d9
LP
201 assert(canonical);
202 assert(result);
203 assert(buffer);
204 assert(errnop);
205 assert(h_errnop);
206
9d485985 207 alen = FAMILY_ADDRESS_SIZE(af);
8041b5ba 208
8041b5ba
LP
209 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
210 if (af == a->family)
211 c++;
4e8c8252 212
e8a7a315
LP
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) +
5502f0d9 217 sizeof(char*) +
e8a7a315 218 (additional ? sizeof(char*) : 0) +
d4c9895d 219 (c > 0 ? c : 1) * ALIGN(alen) +
5502f0d9 220 (c > 0 ? c+1 : 2) * sizeof(char*);
8041b5ba 221
4e8c8252
LP
222 if (buflen < ms) {
223 *errnop = ENOMEM;
224 *h_errnop = NO_RECOVERY;
225 return NSS_STATUS_TRYAGAIN;
226 }
227
e8a7a315 228 /* First, fill in hostnames */
4e8c8252 229 r_name = buffer;
e8a7a315
LP
230 memcpy(r_name, canonical, l_canonical+1);
231 idx = ALIGN(l_canonical+1);
4e8c8252 232
e8a7a315
LP
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 */
4e8c8252 240 r_aliases = buffer + idx;
e8a7a315
LP
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 }
4e8c8252 249
8041b5ba 250 /* Third, add addresses */
4e8c8252 251 r_addr = buffer + idx;
8041b5ba
LP
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
5502f0d9 259 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
8041b5ba
LP
260 i++;
261 }
262
263 assert(i == c);
264 idx += c*ALIGN(alen);
265 } else {
266 if (af == AF_INET)
e8a7a315 267 *(uint32_t*) r_addr = local_address_ipv4;
8041b5ba
LP
268 else
269 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
270
271 idx += ALIGN(alen);
272 }
4e8c8252
LP
273
274 /* Fourth, add address pointer array */
275 r_addr_list = buffer + idx;
8041b5ba 276 if (c > 0) {
d4c9895d 277 unsigned i;
8041b5ba 278
d4c9895d
LP
279 for (i = 0; i < c; i++)
280 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
8041b5ba 281
d4c9895d
LP
282 ((char**) r_addr_list)[i] = NULL;
283 idx += (c+1) * sizeof(char*);
8041b5ba
LP
284
285 } else {
286 ((char**) r_addr_list)[0] = r_addr;
287 ((char**) r_addr_list)[1] = NULL;
d4c9895d 288 idx += 2 * sizeof(char*);
8041b5ba 289 }
4e8c8252
LP
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
e70df46b
LP
306 /* Explicitly reset all error variables */
307 *errnop = 0;
308 *h_errnop = NETDB_SUCCESS;
309 h_errno = 0;
310
4e8c8252 311 return NSS_STATUS_SUCCESS;
6b21f0cf
LP
312}
313
4e8c8252
LP
314enum 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) {
6b21f0cf 322
e80af1bd 323 _cleanup_free_ struct local_address *addresses = NULL;
e8a7a315 324 const char *canonical, *additional = NULL;
5502f0d9 325 _cleanup_free_ char *hn = NULL;
e9140aff 326 uint32_t local_address_ipv4 = 0;
e80af1bd 327 int n_addresses = 0;
5502f0d9
LP
328
329 assert(name);
330 assert(host);
331 assert(buffer);
332 assert(errnop);
333 assert(h_errnop);
6b21f0cf 334
4e8c8252
LP
335 if (af == AF_UNSPEC)
336 af = AF_INET;
6b21f0cf 337
4e8c8252
LP
338 if (af != AF_INET && af != AF_INET6) {
339 *errnop = EAFNOSUPPORT;
340 *h_errnop = NO_DATA;
341 return NSS_STATUS_UNAVAIL;
342 }
6b21f0cf 343
5502f0d9 344 if (is_localhost(name)) {
e8a7a315
LP
345 canonical = "localhost";
346 local_address_ipv4 = htonl(INADDR_LOOPBACK);
e9140aff 347
46a5e0e7 348 } else if (is_gateway_hostname(name)) {
e9140aff 349
1d050e1e 350 n_addresses = local_gateways(NULL, 0, af, &addresses);
e9140aff
LP
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
e8a7a315 359 } else {
5502f0d9
LP
360 hn = gethostname_malloc();
361 if (!hn) {
362 *errnop = ENOMEM;
e8a7a315 363 *h_errnop = NO_RECOVERY;
5502f0d9 364 return NSS_STATUS_TRYAGAIN;
e8a7a315 365 }
6b21f0cf 366
5502f0d9 367 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
e8a7a315
LP
368 *errnop = ENOENT;
369 *h_errnop = HOST_NOT_FOUND;
370 return NSS_STATUS_NOTFOUND;
371 }
372
1d050e1e 373 n_addresses = local_addresses(NULL, 0, af, &addresses);
e80af1bd
LP
374 if (n_addresses < 0)
375 n_addresses = 0;
e8a7a315
LP
376
377 canonical = hn;
378 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
379 local_address_ipv4 = LOCALADDRESS_IPV4;
4e8c8252 380 }
6b21f0cf 381
e8a7a315
LP
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);
4e8c8252 392}
6b21f0cf 393
4e8c8252
LP
394enum 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) {
6b21f0cf 401
e8a7a315 402 const char *canonical = NULL, *additional = NULL;
5502f0d9 403 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
e80af1bd 404 _cleanup_free_ struct local_address *addresses = NULL;
5502f0d9 405 _cleanup_free_ char *hn = NULL;
e80af1bd
LP
406 int n_addresses = 0;
407 struct local_address *a;
3fdcecc8 408 bool additional_from_hostname = false;
e80af1bd 409 unsigned n;
5502f0d9
LP
410
411 assert(addr);
412 assert(host);
413 assert(buffer);
414 assert(errnop);
415 assert(h_errnop);
8041b5ba 416
555bd6e9
LP
417 if (!IN_SET(af, AF_INET, AF_INET6)) {
418 *errnop = EAFNOSUPPORT;
419 *h_errnop = NO_DATA;
420 return NSS_STATUS_UNAVAIL;
421 }
422
9d485985 423 if (len != FAMILY_ADDRESS_SIZE(af)) {
8041b5ba
LP
424 *errnop = EINVAL;
425 *h_errnop = NO_RECOVERY;
426 return NSS_STATUS_UNAVAIL;
427 }
6b21f0cf 428
4e8c8252 429 if (af == AF_INET) {
8041b5ba
LP
430 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
431 goto found;
6b21f0cf 432
e8a7a315
LP
433 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
434 canonical = "localhost";
435 local_address_ipv4 = htonl(INADDR_LOOPBACK);
436 goto found;
437 }
438
555bd6e9
LP
439 } else {
440 assert(af == AF_INET6);
8041b5ba 441
e8a7a315 442 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
3fdcecc8
LP
443 canonical = "localhost";
444 additional_from_hostname = true;
8041b5ba 445 goto found;
e8a7a315 446 }
4e8c8252
LP
447 }
448
1d050e1e 449 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
e9140aff
LP
450 if (n_addresses > 0) {
451 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
452 if (af != a->family)
453 continue;
8041b5ba 454
3fdcecc8 455 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
e9140aff 456 goto found;
e9140aff
LP
457 }
458 }
459
97b11eed 460 addresses = mfree(addresses);
e9140aff 461
1d050e1e 462 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
e9140aff
LP
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) {
e9140aff
LP
469 canonical = "gateway";
470 goto found;
471 }
472 }
8041b5ba
LP
473 }
474
475 *errnop = ENOENT;
476 *h_errnop = HOST_NOT_FOUND;
477
8041b5ba
LP
478 return NSS_STATUS_NOTFOUND;
479
480found:
3fdcecc8
LP
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 }
4e8c8252 495
e8a7a315
LP
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);
4e8c8252 506}
6b21f0cf 507
c9fdc26e
LP
508NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
509NSS_GETHOSTBYADDR_FALLBACKS(myhostname);