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