nss-myhostname.c revision 9d485985338a46b8cb1acdf1af6c1eb2e88acfee
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer/***
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer This file is part of systemd.
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
fff87a35d9e26c0d4ea41273a963c0eb20e18da4Zbigniew Jędrzejewski-Szmek Copyright 2008-2011 Lennart Poettering
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier systemd is free software; you can redistribute it and/or modify it
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer under the terms of the GNU Lesser General Public License as published by
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer the Free Software Foundation; either version 2.1 of the License, or
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer (at your option) any later version.
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier systemd is distributed in the hope that it will be useful, but
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier WITHOUT ANY WARRANTY; without even the implied warranty of
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier Lesser General Public License for more details.
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
32d965851d8cbb39f8ee0eeaf76a89e8f5fc174fLennart Poettering You should have received a copy of the GNU Lesser General Public License
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer along with systemd; If not, see <http://www.gnu.org/licenses/>.
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer***/
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
d9890f4ed47c0d565915360d8bae3b7a1428f285Harald Hoyer#include <limits.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <nss.h>
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#include <sys/types.h>
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer#include <netdb.h>
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier#include <errno.h>
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer#include <string.h>
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer#include <unistd.h>
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#include <net/if.h>
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier#include <stdlib.h>
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer#include <arpa/inet.h>
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer
7b17d413b11bfbdb4442c87b1db25b37ed5162acHarald Hoyer#include "local-addresses.h"
d9890f4ed47c0d565915360d8bae3b7a1428f285Harald Hoyer#include "macro.h"
d9890f4ed47c0d565915360d8bae3b7a1428f285Harald Hoyer#include "nss-util.h"
d9890f4ed47c0d565915360d8bae3b7a1428f285Harald Hoyer#include "util.h"
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier/* Ensure that glibc's assert is used. We cannot use assert from macro.h, as
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * libnss_myhostname will be linked into arbitrary programs which will, in turn
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * attempt to write to the journal via log_dispatch() */
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer#include <assert.h>
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer/* We use 127.0.0.2 as IPv4 address. This has the advantage over
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer * 127.0.0.1 that it can be translated back to the local hostname. For
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer * IPv6 we use ::1 which unfortunately will not translate back to the
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer * hostname but instead something like "localhost6" or so. */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer#define LOCALADDRESS_IPV4 (htonl(0x7F000002))
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer#define LOCALADDRESS_IPV6 &in6addr_loopback
6b197f2a03fa03a2a853cf726d47be2ea4c623b6Harald Hoyer#define LOOPBACK_INTERFACE "lo"
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald HoyerNSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald HoyerNSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerenum nss_status _nss_myhostname_gethostbyname4_r(
5c404f1ab8e96efedb983806443ca982a1b2a372Evgeny Vereshchagin const char *name,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer struct gaih_addrtuple **pat,
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer char *buffer, size_t buflen,
c90feab4ff8bc23d88d4f9c67d9652ba189cb51bMichal Schmidt int *errnop, int *h_errnop,
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier int32_t *ttlp) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer _cleanup_free_ struct local_address *addresses = NULL;
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer _cleanup_free_ char *hn = NULL;
d9890f4ed47c0d565915360d8bae3b7a1428f285Harald Hoyer const char *canonical = NULL;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer int n_addresses = 0, lo_ifi;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer uint32_t local_address_ipv4;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer struct local_address *a;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer size_t l, idx, ms;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer char *r_name;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer unsigned n;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert(name);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer assert(pat);
assert(buffer);
assert(errnop);
assert(h_errnop);
if (is_localhost(name)) {
/* We respond to 'localhost', so that /etc/hosts
* is optional */
canonical = "localhost";
local_address_ipv4 = htonl(INADDR_LOOPBACK);
} else {
hn = gethostname_malloc();
if (!hn) {
*errnop = ENOMEM;
*h_errnop = NO_RECOVERY;
return NSS_STATUS_TRYAGAIN;
}
/* We respond to our local host name, our our hostname suffixed with a single dot. */
if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
*errnop = ENOENT;
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
n_addresses = local_addresses(&addresses);
if (n_addresses < 0)
n_addresses = 0;
canonical = hn;
local_address_ipv4 = LOCALADDRESS_IPV4;
}
/* If this call fails we fill in 0 as scope. Which is fine */
lo_ifi = n_addresses <= 0 ? if_nametoindex(LOOPBACK_INTERFACE) : 0;
l = strlen(canonical);
ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
if (buflen < ms) {
*errnop = ENOMEM;
*h_errnop = NO_RECOVERY;
return NSS_STATUS_TRYAGAIN;
}
/* First, fill in hostname */
r_name = buffer;
memcpy(r_name, canonical, l+1);
idx = ALIGN(l+1);
if (n_addresses <= 0) {
/* Second, fill in IPv6 tuple */
r_tuple = (struct gaih_addrtuple*) (buffer + idx);
r_tuple->next = r_tuple_prev;
r_tuple->name = r_name;
r_tuple->family = AF_INET6;
memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
r_tuple->scopeid = (uint32_t) lo_ifi;
idx += ALIGN(sizeof(struct gaih_addrtuple));
r_tuple_prev = r_tuple;
/* Third, fill in IPv4 tuple */
r_tuple = (struct gaih_addrtuple*) (buffer + idx);
r_tuple->next = r_tuple_prev;
r_tuple->name = r_name;
r_tuple->family = AF_INET;
*(uint32_t*) r_tuple->addr = local_address_ipv4;
r_tuple->scopeid = (uint32_t) lo_ifi;
idx += ALIGN(sizeof(struct gaih_addrtuple));
r_tuple_prev = r_tuple;
}
/* Fourth, fill actual addresses in, but in backwards order */
for (a = addresses + n_addresses - 1, n = 0; (int) n < n_addresses; n++, a--) {
r_tuple = (struct gaih_addrtuple*) (buffer + idx);
r_tuple->next = r_tuple_prev;
r_tuple->name = r_name;
r_tuple->family = a->family;
r_tuple->scopeid = a->ifindex;
memcpy(r_tuple->addr, &a->address, 16);
idx += ALIGN(sizeof(struct gaih_addrtuple));
r_tuple_prev = r_tuple;
}
/* Verify the size matches */
assert(idx == ms);
/* Nscd expects us to store the first record in **pat. */
if (*pat)
**pat = *r_tuple_prev;
else
*pat = r_tuple_prev;
if (ttlp)
*ttlp = 0;
return NSS_STATUS_SUCCESS;
}
static enum nss_status fill_in_hostent(
const char *canonical, const char *additional,
int af,
struct local_address *addresses, unsigned n_addresses,
uint32_t local_address_ipv4,
struct hostent *result,
char *buffer, size_t buflen,
int *errnop, int *h_errnop,
int32_t *ttlp,
char **canonp) {
size_t l_canonical, l_additional, idx, ms, alen;
char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
struct local_address *a;
unsigned n, c;
assert(canonical);
assert(result);
assert(buffer);
assert(errnop);
assert(h_errnop);
alen = FAMILY_ADDRESS_SIZE(af);
for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
if (af == a->family)
c++;
l_canonical = strlen(canonical);
l_additional = additional ? strlen(additional) : 0;
ms = ALIGN(l_canonical+1)+
(additional ? ALIGN(l_additional+1) : 0) +
sizeof(char*) +
(additional ? sizeof(char*) : 0) +
(c > 0 ? c : 1) * ALIGN(alen) +
(c > 0 ? c+1 : 2) * sizeof(char*);
if (buflen < ms) {
*errnop = ENOMEM;
*h_errnop = NO_RECOVERY;
return NSS_STATUS_TRYAGAIN;
}
/* First, fill in hostnames */
r_name = buffer;
memcpy(r_name, canonical, l_canonical+1);
idx = ALIGN(l_canonical+1);
if (additional) {
r_alias = buffer + idx;
memcpy(r_alias, additional, l_additional+1);
idx += ALIGN(l_additional+1);
}
/* Second, create aliases array */
r_aliases = buffer + idx;
if (additional) {
((char**) r_aliases)[0] = r_alias;
((char**) r_aliases)[1] = NULL;
idx += 2*sizeof(char*);
} else {
((char**) r_aliases)[0] = NULL;
idx += sizeof(char*);
}
/* Third, add addresses */
r_addr = buffer + idx;
if (c > 0) {
unsigned i = 0;
for (a = addresses, n = 0; n < n_addresses; a++, n++) {
if (af != a->family)
continue;
memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
i++;
}
assert(i == c);
idx += c*ALIGN(alen);
} else {
if (af == AF_INET)
*(uint32_t*) r_addr = local_address_ipv4;
else
memcpy(r_addr, LOCALADDRESS_IPV6, 16);
idx += ALIGN(alen);
}
/* Fourth, add address pointer array */
r_addr_list = buffer + idx;
if (c > 0) {
unsigned i;
for (i = 0; i < c; i++)
((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
((char**) r_addr_list)[i] = NULL;
idx += (c+1) * sizeof(char*);
} else {
((char**) r_addr_list)[0] = r_addr;
((char**) r_addr_list)[1] = NULL;
idx += 2 * sizeof(char*);
}
/* Verify the size matches */
assert(idx == ms);
result->h_name = r_name;
result->h_aliases = (char**) r_aliases;
result->h_addrtype = af;
result->h_length = alen;
result->h_addr_list = (char**) r_addr_list;
if (ttlp)
*ttlp = 0;
if (canonp)
*canonp = r_name;
return NSS_STATUS_SUCCESS;
}
enum nss_status _nss_myhostname_gethostbyname3_r(
const char *name,
int af,
struct hostent *host,
char *buffer, size_t buflen,
int *errnop, int *h_errnop,
int32_t *ttlp,
char **canonp) {
_cleanup_free_ struct local_address *addresses = NULL;
const char *canonical, *additional = NULL;
_cleanup_free_ char *hn = NULL;
uint32_t local_address_ipv4;
int n_addresses = 0;
assert(name);
assert(host);
assert(buffer);
assert(errnop);
assert(h_errnop);
if (af == AF_UNSPEC)
af = AF_INET;
if (af != AF_INET && af != AF_INET6) {
*errnop = EAFNOSUPPORT;
*h_errnop = NO_DATA;
return NSS_STATUS_UNAVAIL;
}
if (is_localhost(name)) {
canonical = "localhost";
local_address_ipv4 = htonl(INADDR_LOOPBACK);
} else {
hn = gethostname_malloc();
if (!hn) {
*errnop = ENOMEM;
*h_errnop = NO_RECOVERY;
return NSS_STATUS_TRYAGAIN;
}
if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
*errnop = ENOENT;
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
}
n_addresses = local_addresses(&addresses);
if (n_addresses < 0)
n_addresses = 0;
canonical = hn;
additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
local_address_ipv4 = LOCALADDRESS_IPV4;
}
return fill_in_hostent(
canonical, additional,
af,
addresses, n_addresses,
local_address_ipv4,
host,
buffer, buflen,
errnop, h_errnop,
ttlp,
canonp);
}
enum nss_status _nss_myhostname_gethostbyaddr2_r(
const void* addr, socklen_t len,
int af,
struct hostent *host,
char *buffer, size_t buflen,
int *errnop, int *h_errnop,
int32_t *ttlp) {
const char *canonical = NULL, *additional = NULL;
uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
_cleanup_free_ struct local_address *addresses = NULL;
_cleanup_free_ char *hn = NULL;
int n_addresses = 0;
struct local_address *a;
unsigned n;
assert(addr);
assert(host);
assert(buffer);
assert(errnop);
assert(h_errnop);
if (!IN_SET(af, AF_INET, AF_INET6)) {
*errnop = EAFNOSUPPORT;
*h_errnop = NO_DATA;
return NSS_STATUS_UNAVAIL;
}
if (len != FAMILY_ADDRESS_SIZE(af)) {
*errnop = EINVAL;
*h_errnop = NO_RECOVERY;
return NSS_STATUS_UNAVAIL;
}
if (af == AF_INET) {
if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
goto found;
if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
canonical = "localhost";
local_address_ipv4 = htonl(INADDR_LOOPBACK);
goto found;
}
} else {
assert(af == AF_INET6);
if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
additional = "localhost";
goto found;
}
}
n_addresses = local_addresses(&addresses);
if (n_addresses < 0)
n_addresses = 0;
for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
if (af != a->family)
continue;
if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
goto found;
}
*errnop = ENOENT;
*h_errnop = HOST_NOT_FOUND;
return NSS_STATUS_NOTFOUND;
found:
if (!canonical) {
hn = gethostname_malloc();
if (!hn) {
*errnop = ENOMEM;
*h_errnop = NO_RECOVERY;
return NSS_STATUS_TRYAGAIN;
}
canonical = hn;
}
return fill_in_hostent(
canonical, additional,
af,
addresses, n_addresses,
local_address_ipv4,
host,
buffer, buflen,
errnop, h_errnop,
ttlp,
NULL);
}
NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
NSS_GETHOSTBYADDR_FALLBACKS(myhostname);