]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/nss-myhostname/nss-myhostname.c
man: describe unit load path in systemd.unit(5)
[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
8041b5ba 179 *pat = r_tuple_prev;
4e8c8252
LP
180
181 if (ttlp)
182 *ttlp = 0;
183
8041b5ba
LP
184 free(addresses);
185
4e8c8252
LP
186 return NSS_STATUS_SUCCESS;
187}
6b21f0cf
LP
188
189static enum nss_status fill_in_hostent(
4e8c8252
LP
190 const char *hn,
191 int af,
192 struct hostent *result,
193 char *buffer, size_t buflen,
194 int *errnop, int *h_errnop,
195 int32_t *ttlp,
196 char **canonp) {
197
198 size_t l, idx, ms;
199 char *r_addr, *r_name, *r_aliases, *r_addr_list;
200 size_t alen;
8041b5ba
LP
201 struct address *addresses = NULL, *a;
202 unsigned n_addresses = 0, n, c;
203
204 alen = PROTO_ADDRESS_SIZE(af);
205
1df2a0fc 206 ifconf_acquire_addresses(&addresses, &n_addresses);
4e8c8252 207
8041b5ba
LP
208 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
209 if (af == a->family)
210 c++;
4e8c8252
LP
211
212 l = strlen(hn);
8041b5ba
LP
213 ms = ALIGN(l+1)+
214 sizeof(char*)+
215 (c > 0 ? c : 1)*ALIGN(alen)+
216 (c > 0 ? c+1 : 2)*sizeof(char*);
217
4e8c8252
LP
218 if (buflen < ms) {
219 *errnop = ENOMEM;
220 *h_errnop = NO_RECOVERY;
8041b5ba 221 free(addresses);
4e8c8252
LP
222 return NSS_STATUS_TRYAGAIN;
223 }
224
225 /* First, fill in hostname */
226 r_name = buffer;
227 memcpy(r_name, hn, l+1);
228 idx = ALIGN(l+1);
229
8041b5ba 230 /* Second, create (empty) aliases array */
4e8c8252
LP
231 r_aliases = buffer + idx;
232 *(char**) r_aliases = NULL;
233 idx += sizeof(char*);
234
8041b5ba 235 /* Third, add addresses */
4e8c8252 236 r_addr = buffer + idx;
8041b5ba
LP
237 if (c > 0) {
238 unsigned i = 0;
239
240 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
241 if (af != a->family)
242 continue;
243
244 memcpy(r_addr + i*ALIGN(alen), a->address, alen);
245 i++;
246 }
247
248 assert(i == c);
249 idx += c*ALIGN(alen);
250 } else {
251 if (af == AF_INET)
252 *(uint32_t*) r_addr = LOCALADDRESS_IPV4;
253 else
254 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
255
256 idx += ALIGN(alen);
257 }
4e8c8252
LP
258
259 /* Fourth, add address pointer array */
260 r_addr_list = buffer + idx;
8041b5ba
LP
261 if (c > 0) {
262 unsigned i = 0;
263
264 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
265 if (af != a->family)
266 continue;
267
268 ((char**) r_addr_list)[i] = (r_addr + i*ALIGN(alen));
269 i++;
270 }
271
272 assert(i == c);
273 ((char**) r_addr_list)[c] = NULL;
274 idx += (c+1)*sizeof(char*);
275
276 } else {
277 ((char**) r_addr_list)[0] = r_addr;
278 ((char**) r_addr_list)[1] = NULL;
279 idx += 2*sizeof(char*);
280 }
4e8c8252
LP
281
282 /* Verify the size matches */
283 assert(idx == ms);
284
285 result->h_name = r_name;
286 result->h_aliases = (char**) r_aliases;
287 result->h_addrtype = af;
288 result->h_length = alen;
289 result->h_addr_list = (char**) r_addr_list;
290
291 if (ttlp)
292 *ttlp = 0;
293
294 if (canonp)
295 *canonp = r_name;
296
8041b5ba
LP
297 free(addresses);
298
4e8c8252 299 return NSS_STATUS_SUCCESS;
6b21f0cf
LP
300}
301
4e8c8252
LP
302enum nss_status _nss_myhostname_gethostbyname3_r(
303 const char *name,
304 int af,
305 struct hostent *host,
306 char *buffer, size_t buflen,
307 int *errnop, int *h_errnop,
308 int32_t *ttlp,
309 char **canonp) {
6b21f0cf 310
4e8c8252 311 char hn[HOST_NAME_MAX+1];
6b21f0cf 312
4e8c8252
LP
313 if (af == AF_UNSPEC)
314 af = AF_INET;
6b21f0cf 315
4e8c8252
LP
316 if (af != AF_INET && af != AF_INET6) {
317 *errnop = EAFNOSUPPORT;
318 *h_errnop = NO_DATA;
319 return NSS_STATUS_UNAVAIL;
320 }
6b21f0cf 321
4e8c8252
LP
322 memset(hn, 0, sizeof(hn));
323 if (gethostname(hn, sizeof(hn)-1) < 0) {
324 *errnop = errno;
325 *h_errnop = NO_RECOVERY;
326 return NSS_STATUS_UNAVAIL;
327 }
6b21f0cf 328
4e8c8252
LP
329 if (strcasecmp(name, hn) != 0) {
330 *errnop = ENOENT;
331 *h_errnop = HOST_NOT_FOUND;
332 return NSS_STATUS_NOTFOUND;
333 }
6b21f0cf 334
4e8c8252
LP
335 return fill_in_hostent(hn, af, host, buffer, buflen, errnop, h_errnop, ttlp, canonp);
336}
6b21f0cf 337
4e8c8252
LP
338enum nss_status _nss_myhostname_gethostbyname2_r(
339 const char *name,
340 int af,
341 struct hostent *host,
342 char *buffer, size_t buflen,
343 int *errnop, int *h_errnop) {
6b21f0cf 344
4e8c8252
LP
345 return _nss_myhostname_gethostbyname3_r(
346 name,
347 af,
348 host,
349 buffer, buflen,
350 errnop, h_errnop,
351 NULL,
352 NULL);
6b21f0cf
LP
353}
354
8041b5ba 355enum nss_status _nss_myhostname_gethostbyname_r(
4e8c8252
LP
356 const char *name,
357 struct hostent *host,
358 char *buffer, size_t buflen,
359 int *errnop, int *h_errnop) {
360
361 return _nss_myhostname_gethostbyname3_r(
362 name,
363 AF_UNSPEC,
364 host,
365 buffer, buflen,
366 errnop, h_errnop,
367 NULL,
368 NULL);
6b21f0cf
LP
369}
370
4e8c8252
LP
371enum nss_status _nss_myhostname_gethostbyaddr2_r(
372 const void* addr, socklen_t len,
373 int af,
374 struct hostent *host,
375 char *buffer, size_t buflen,
376 int *errnop, int *h_errnop,
377 int32_t *ttlp) {
6b21f0cf 378
4e8c8252 379 char hn[HOST_NAME_MAX+1];
8041b5ba
LP
380 struct address *addresses = NULL, *a;
381 unsigned n_addresses = 0, n;
382
383 if (len != PROTO_ADDRESS_SIZE(af)) {
384 *errnop = EINVAL;
385 *h_errnop = NO_RECOVERY;
386 return NSS_STATUS_UNAVAIL;
387 }
6b21f0cf 388
4e8c8252 389 if (af == AF_INET) {
8041b5ba
LP
390
391 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
392 goto found;
6b21f0cf 393
4e8c8252 394 } else if (af == AF_INET6) {
8041b5ba
LP
395
396 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0)
397 goto found;
398
4e8c8252
LP
399 } else {
400 *errnop = EAFNOSUPPORT;
401 *h_errnop = NO_DATA;
402 return NSS_STATUS_UNAVAIL;
403 }
404
1df2a0fc 405 ifconf_acquire_addresses(&addresses, &n_addresses);
8041b5ba
LP
406
407 for (a = addresses, n = 0; n < n_addresses; n++, a++) {
408 if (af != a->family)
409 continue;
410
411 if (memcmp(addr, a->address, PROTO_ADDRESS_SIZE(af)) == 0)
412 goto found;
413 }
414
415 *errnop = ENOENT;
416 *h_errnop = HOST_NOT_FOUND;
417
418 free(addresses);
419 return NSS_STATUS_NOTFOUND;
420
421found:
422 free(addresses);
423
4e8c8252
LP
424 memset(hn, 0, sizeof(hn));
425 if (gethostname(hn, sizeof(hn)-1) < 0) {
426 *errnop = errno;
427 *h_errnop = NO_RECOVERY;
8041b5ba 428
4e8c8252
LP
429 return NSS_STATUS_UNAVAIL;
430 }
431
432 return fill_in_hostent(hn, af, host, buffer, buflen, errnop, h_errnop, ttlp, NULL);
433
4e8c8252 434}
6b21f0cf 435
4e8c8252
LP
436enum nss_status _nss_myhostname_gethostbyaddr_r(
437 const void* addr, socklen_t len,
438 int af,
439 struct hostent *host,
440 char *buffer, size_t buflen,
441 int *errnop, int *h_errnop) {
6b21f0cf 442
4e8c8252
LP
443 return _nss_myhostname_gethostbyaddr2_r(
444 addr, len,
445 af,
446 host,
447 buffer, buflen,
448 errnop, h_errnop,
449 NULL);
6b21f0cf 450}