]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-myhostname/nss-myhostname.c
nss: various minor fixes to nss-myhostname + nss-mymachines
[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 <limits.h>
23 #include <nss.h>
24 #include <sys/types.h>
25 #include <netdb.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <net/if.h>
30 #include <stdlib.h>
31 #include <arpa/inet.h>
32
33 #include "local-addresses.h"
34 #include "macro.h"
35 #include "nss-util.h"
36 #include "util.h"
37
38 /* Ensure that glibc's assert is used. We cannot use assert from macro.h, as
39 * libnss_myhostname will be linked into arbitrary programs which will, in turn
40 * attempt to write to the journal via log_dispatch() */
41 #include <assert.h>
42
43 /* We use 127.0.0.2 as IPv4 address. This has the advantage over
44 * 127.0.0.1 that it can be translated back to the local hostname. For
45 * IPv6 we use ::1 which unfortunately will not translate back to the
46 * hostname but instead something like "localhost6" or so. */
47
48 #define LOCALADDRESS_IPV4 (htonl(0x7F000002))
49 #define LOCALADDRESS_IPV6 &in6addr_loopback
50 #define LOOPBACK_INTERFACE "lo"
51
52 NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
53 NSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
54
55 enum nss_status _nss_myhostname_gethostbyname4_r(
56 const char *name,
57 struct gaih_addrtuple **pat,
58 char *buffer, size_t buflen,
59 int *errnop, int *h_errnop,
60 int32_t *ttlp) {
61
62 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
63 _cleanup_free_ struct local_address *addresses = NULL;
64 _cleanup_free_ char *hn = NULL;
65 const char *canonical = NULL;
66 int n_addresses = 0, lo_ifi;
67 uint32_t local_address_ipv4;
68 struct local_address *a;
69 size_t l, idx, ms;
70 char *r_name;
71 unsigned n;
72
73 assert(name);
74 assert(pat);
75 assert(buffer);
76 assert(errnop);
77 assert(h_errnop);
78
79 if (is_localhost(name)) {
80 /* We respond to 'localhost', so that /etc/hosts
81 * is optional */
82
83 canonical = "localhost";
84 local_address_ipv4 = htonl(INADDR_LOOPBACK);
85 } else {
86 hn = gethostname_malloc();
87 if (!hn) {
88 *errnop = ENOMEM;
89 *h_errnop = NO_RECOVERY;
90 return NSS_STATUS_TRYAGAIN;
91 }
92
93 /* We respond to our local host name, our our hostname suffixed with a single dot. */
94 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
95 *errnop = ENOENT;
96 *h_errnop = HOST_NOT_FOUND;
97 return NSS_STATUS_NOTFOUND;
98 }
99
100 n_addresses = local_addresses(&addresses);
101 if (n_addresses < 0)
102 n_addresses = 0;
103
104 canonical = hn;
105 local_address_ipv4 = LOCALADDRESS_IPV4;
106 }
107
108 /* If this call fails we fill in 0 as scope. Which is fine */
109 lo_ifi = n_addresses <= 0 ? if_nametoindex(LOOPBACK_INTERFACE) : 0;
110
111 l = strlen(canonical);
112 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
113 if (buflen < ms) {
114 *errnop = ENOMEM;
115 *h_errnop = NO_RECOVERY;
116 return NSS_STATUS_TRYAGAIN;
117 }
118
119 /* First, fill in hostname */
120 r_name = buffer;
121 memcpy(r_name, canonical, l+1);
122 idx = ALIGN(l+1);
123
124 if (n_addresses <= 0) {
125 /* Second, fill in IPv6 tuple */
126 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
127 r_tuple->next = r_tuple_prev;
128 r_tuple->name = r_name;
129 r_tuple->family = AF_INET6;
130 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
131 r_tuple->scopeid = (uint32_t) lo_ifi;
132
133 idx += ALIGN(sizeof(struct gaih_addrtuple));
134 r_tuple_prev = r_tuple;
135
136 /* Third, fill in IPv4 tuple */
137 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
138 r_tuple->next = r_tuple_prev;
139 r_tuple->name = r_name;
140 r_tuple->family = AF_INET;
141 *(uint32_t*) r_tuple->addr = local_address_ipv4;
142 r_tuple->scopeid = (uint32_t) lo_ifi;
143
144 idx += ALIGN(sizeof(struct gaih_addrtuple));
145 r_tuple_prev = r_tuple;
146 }
147
148 /* Fourth, fill actual addresses in, but in backwards order */
149 for (a = addresses + n_addresses - 1, n = 0; (int) n < n_addresses; n++, a--) {
150 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
151 r_tuple->next = r_tuple_prev;
152 r_tuple->name = r_name;
153 r_tuple->family = a->family;
154 r_tuple->scopeid = a->ifindex;
155 memcpy(r_tuple->addr, &a->address, 16);
156
157 idx += ALIGN(sizeof(struct gaih_addrtuple));
158 r_tuple_prev = r_tuple;
159 }
160
161 /* Verify the size matches */
162 assert(idx == ms);
163
164 /* Nscd expects us to store the first record in **pat. */
165 if (*pat)
166 **pat = *r_tuple_prev;
167 else
168 *pat = r_tuple_prev;
169
170 if (ttlp)
171 *ttlp = 0;
172
173 return NSS_STATUS_SUCCESS;
174 }
175
176 static enum nss_status fill_in_hostent(
177 const char *canonical, const char *additional,
178 int af,
179 struct local_address *addresses, unsigned n_addresses,
180 uint32_t local_address_ipv4,
181 struct hostent *result,
182 char *buffer, size_t buflen,
183 int *errnop, int *h_errnop,
184 int32_t *ttlp,
185 char **canonp) {
186
187 size_t l_canonical, l_additional, idx, ms, alen;
188 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
189 struct local_address *a;
190 unsigned n, c;
191
192 assert(canonical);
193 assert(result);
194 assert(buffer);
195 assert(errnop);
196 assert(h_errnop);
197
198 alen = PROTO_ADDRESS_SIZE(af);
199
200 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
201 if (af == a->family)
202 c++;
203
204 l_canonical = strlen(canonical);
205 l_additional = additional ? strlen(additional) : 0;
206 ms = ALIGN(l_canonical+1)+
207 (additional ? ALIGN(l_additional+1) : 0) +
208 sizeof(char*) +
209 (additional ? sizeof(char*) : 0) +
210 (c > 0 ? c : 1) * ALIGN(alen) +
211 (c > 0 ? c+1 : 2) * sizeof(char*);
212
213 if (buflen < ms) {
214 *errnop = ENOMEM;
215 *h_errnop = NO_RECOVERY;
216 return NSS_STATUS_TRYAGAIN;
217 }
218
219 /* First, fill in hostnames */
220 r_name = buffer;
221 memcpy(r_name, canonical, l_canonical+1);
222 idx = ALIGN(l_canonical+1);
223
224 if (additional) {
225 r_alias = buffer + idx;
226 memcpy(r_alias, additional, l_additional+1);
227 idx += ALIGN(l_additional+1);
228 }
229
230 /* Second, create aliases array */
231 r_aliases = buffer + idx;
232 if (additional) {
233 ((char**) r_aliases)[0] = r_alias;
234 ((char**) r_aliases)[1] = NULL;
235 idx += 2*sizeof(char*);
236 } else {
237 ((char**) r_aliases)[0] = NULL;
238 idx += sizeof(char*);
239 }
240
241 /* Third, add addresses */
242 r_addr = buffer + idx;
243 if (c > 0) {
244 unsigned i = 0;
245
246 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
247 if (af != a->family)
248 continue;
249
250 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
251 i++;
252 }
253
254 assert(i == c);
255 idx += c*ALIGN(alen);
256 } else {
257 if (af == AF_INET)
258 *(uint32_t*) r_addr = local_address_ipv4;
259 else
260 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
261
262 idx += ALIGN(alen);
263 }
264
265 /* Fourth, add address pointer array */
266 r_addr_list = buffer + idx;
267 if (c > 0) {
268 unsigned i;
269
270 for (i = 0; i < c; i++)
271 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
272
273 ((char**) r_addr_list)[i] = 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 }
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
297 return NSS_STATUS_SUCCESS;
298 }
299
300 enum nss_status _nss_myhostname_gethostbyname3_r(
301 const char *name,
302 int af,
303 struct hostent *host,
304 char *buffer, size_t buflen,
305 int *errnop, int *h_errnop,
306 int32_t *ttlp,
307 char **canonp) {
308
309 _cleanup_free_ struct local_address *addresses = NULL;
310 const char *canonical, *additional = NULL;
311 _cleanup_free_ char *hn = NULL;
312 uint32_t local_address_ipv4;
313 int n_addresses = 0;
314
315 assert(name);
316 assert(host);
317 assert(buffer);
318 assert(errnop);
319 assert(h_errnop);
320
321 if (af == AF_UNSPEC)
322 af = AF_INET;
323
324 if (af != AF_INET && af != AF_INET6) {
325 *errnop = EAFNOSUPPORT;
326 *h_errnop = NO_DATA;
327 return NSS_STATUS_UNAVAIL;
328 }
329
330 if (is_localhost(name)) {
331 canonical = "localhost";
332 local_address_ipv4 = htonl(INADDR_LOOPBACK);
333 } else {
334 hn = gethostname_malloc();
335 if (!hn) {
336 *errnop = ENOMEM;
337 *h_errnop = NO_RECOVERY;
338 return NSS_STATUS_TRYAGAIN;
339 }
340
341 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
342 *errnop = ENOENT;
343 *h_errnop = HOST_NOT_FOUND;
344 return NSS_STATUS_NOTFOUND;
345 }
346
347 n_addresses = local_addresses(&addresses);
348 if (n_addresses < 0)
349 n_addresses = 0;
350
351 canonical = hn;
352 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
353 local_address_ipv4 = LOCALADDRESS_IPV4;
354 }
355
356 return fill_in_hostent(
357 canonical, additional,
358 af,
359 addresses, n_addresses,
360 local_address_ipv4,
361 host,
362 buffer, buflen,
363 errnop, h_errnop,
364 ttlp,
365 canonp);
366 }
367
368 enum nss_status _nss_myhostname_gethostbyaddr2_r(
369 const void* addr, socklen_t len,
370 int af,
371 struct hostent *host,
372 char *buffer, size_t buflen,
373 int *errnop, int *h_errnop,
374 int32_t *ttlp) {
375
376 const char *canonical = NULL, *additional = NULL;
377 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
378 _cleanup_free_ struct local_address *addresses = NULL;
379 _cleanup_free_ char *hn = NULL;
380 int n_addresses = 0;
381 struct local_address *a;
382 unsigned n;
383
384 assert(addr);
385 assert(host);
386 assert(buffer);
387 assert(errnop);
388 assert(h_errnop);
389
390 if (!IN_SET(af, AF_INET, AF_INET6)) {
391 *errnop = EAFNOSUPPORT;
392 *h_errnop = NO_DATA;
393 return NSS_STATUS_UNAVAIL;
394 }
395
396 if (len != PROTO_ADDRESS_SIZE(af)) {
397 *errnop = EINVAL;
398 *h_errnop = NO_RECOVERY;
399 return NSS_STATUS_UNAVAIL;
400 }
401
402 if (af == AF_INET) {
403
404 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
405 goto found;
406
407 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
408 canonical = "localhost";
409 local_address_ipv4 = htonl(INADDR_LOOPBACK);
410 goto found;
411 }
412
413 } else {
414 assert(af == AF_INET6);
415
416 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
417 additional = "localhost";
418 goto found;
419 }
420
421 }
422
423 n_addresses = local_addresses(&addresses);
424 if (n_addresses < 0)
425 n_addresses = 0;
426
427 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
428 if (af != a->family)
429 continue;
430
431 if (memcmp(addr, &a->address, PROTO_ADDRESS_SIZE(af)) == 0)
432 goto found;
433 }
434
435 *errnop = ENOENT;
436 *h_errnop = HOST_NOT_FOUND;
437
438 return NSS_STATUS_NOTFOUND;
439
440 found:
441 if (!canonical) {
442 hn = gethostname_malloc();
443 if (!hn) {
444 *errnop = ENOMEM;
445 *h_errnop = NO_RECOVERY;
446 return NSS_STATUS_TRYAGAIN;
447 }
448
449 canonical = hn;
450 }
451
452 return fill_in_hostent(
453 canonical, additional,
454 af,
455 addresses, n_addresses,
456 local_address_ipv4,
457 host,
458 buffer, buflen,
459 errnop, h_errnop,
460 ttlp,
461 NULL);
462
463 }
464
465 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
466 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);