]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-myhostname/nss-myhostname.c
nss: always explicitly reset all error variables
[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 /* We use 127.0.0.2 as IPv4 address. This has the advantage over
39 * 127.0.0.1 that it can be translated back to the local hostname. For
40 * IPv6 we use ::1 which unfortunately will not translate back to the
41 * hostname but instead something like "localhost6" or so. */
42
43 #define LOCALADDRESS_IPV4 (htonl(0x7F000002))
44 #define LOCALADDRESS_IPV6 &in6addr_loopback
45 #define LOOPBACK_INTERFACE "lo"
46
47 NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
48 NSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
49
50 enum nss_status _nss_myhostname_gethostbyname4_r(
51 const char *name,
52 struct gaih_addrtuple **pat,
53 char *buffer, size_t buflen,
54 int *errnop, int *h_errnop,
55 int32_t *ttlp) {
56
57 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
58 _cleanup_free_ struct local_address *addresses = NULL;
59 _cleanup_free_ char *hn = NULL;
60 const char *canonical = NULL;
61 int n_addresses = 0, lo_ifi;
62 uint32_t local_address_ipv4;
63 struct local_address *a;
64 size_t l, idx, ms;
65 char *r_name;
66 unsigned n;
67
68 assert(name);
69 assert(pat);
70 assert(buffer);
71 assert(errnop);
72 assert(h_errnop);
73
74 if (is_localhost(name)) {
75 /* We respond to 'localhost', so that /etc/hosts
76 * is optional */
77
78 canonical = "localhost";
79 local_address_ipv4 = htonl(INADDR_LOOPBACK);
80 } else {
81 hn = gethostname_malloc();
82 if (!hn) {
83 *errnop = ENOMEM;
84 *h_errnop = NO_RECOVERY;
85 return NSS_STATUS_TRYAGAIN;
86 }
87
88 /* We respond to our local host name, our our hostname suffixed with a single dot. */
89 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
90 *errnop = ENOENT;
91 *h_errnop = HOST_NOT_FOUND;
92 return NSS_STATUS_NOTFOUND;
93 }
94
95 n_addresses = local_addresses(&addresses);
96 if (n_addresses < 0)
97 n_addresses = 0;
98
99 canonical = hn;
100 local_address_ipv4 = LOCALADDRESS_IPV4;
101 }
102
103 /* If this call fails we fill in 0 as scope. Which is fine */
104 lo_ifi = n_addresses <= 0 ? if_nametoindex(LOOPBACK_INTERFACE) : 0;
105
106 l = strlen(canonical);
107 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
108 if (buflen < ms) {
109 *errnop = ENOMEM;
110 *h_errnop = NO_RECOVERY;
111 return NSS_STATUS_TRYAGAIN;
112 }
113
114 /* First, fill in hostname */
115 r_name = buffer;
116 memcpy(r_name, canonical, l+1);
117 idx = ALIGN(l+1);
118
119 if (n_addresses <= 0) {
120 /* Second, fill in IPv6 tuple */
121 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
122 r_tuple->next = r_tuple_prev;
123 r_tuple->name = r_name;
124 r_tuple->family = AF_INET6;
125 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
126 r_tuple->scopeid = (uint32_t) lo_ifi;
127
128 idx += ALIGN(sizeof(struct gaih_addrtuple));
129 r_tuple_prev = r_tuple;
130
131 /* Third, fill in IPv4 tuple */
132 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
133 r_tuple->next = r_tuple_prev;
134 r_tuple->name = r_name;
135 r_tuple->family = AF_INET;
136 *(uint32_t*) r_tuple->addr = local_address_ipv4;
137 r_tuple->scopeid = (uint32_t) lo_ifi;
138
139 idx += ALIGN(sizeof(struct gaih_addrtuple));
140 r_tuple_prev = r_tuple;
141 }
142
143 /* Fourth, fill actual addresses in, but in backwards order */
144 for (a = addresses + n_addresses - 1, n = 0; (int) n < n_addresses; n++, a--) {
145 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
146 r_tuple->next = r_tuple_prev;
147 r_tuple->name = r_name;
148 r_tuple->family = a->family;
149 r_tuple->scopeid = a->ifindex;
150 memcpy(r_tuple->addr, &a->address, 16);
151
152 idx += ALIGN(sizeof(struct gaih_addrtuple));
153 r_tuple_prev = r_tuple;
154 }
155
156 /* Verify the size matches */
157 assert(idx == ms);
158
159 /* Nscd expects us to store the first record in **pat. */
160 if (*pat)
161 **pat = *r_tuple_prev;
162 else
163 *pat = r_tuple_prev;
164
165 if (ttlp)
166 *ttlp = 0;
167
168 /* Explicitly reset all error variables */
169 *errnop = 0;
170 *h_errnop = NETDB_SUCCESS;
171 h_errno = 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 = FAMILY_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 /* Explicitly reset all error variables */
298 *errnop = 0;
299 *h_errnop = NETDB_SUCCESS;
300 h_errno = 0;
301
302 return NSS_STATUS_SUCCESS;
303 }
304
305 enum nss_status _nss_myhostname_gethostbyname3_r(
306 const char *name,
307 int af,
308 struct hostent *host,
309 char *buffer, size_t buflen,
310 int *errnop, int *h_errnop,
311 int32_t *ttlp,
312 char **canonp) {
313
314 _cleanup_free_ struct local_address *addresses = NULL;
315 const char *canonical, *additional = NULL;
316 _cleanup_free_ char *hn = NULL;
317 uint32_t local_address_ipv4;
318 int n_addresses = 0;
319
320 assert(name);
321 assert(host);
322 assert(buffer);
323 assert(errnop);
324 assert(h_errnop);
325
326 if (af == AF_UNSPEC)
327 af = AF_INET;
328
329 if (af != AF_INET && af != AF_INET6) {
330 *errnop = EAFNOSUPPORT;
331 *h_errnop = NO_DATA;
332 return NSS_STATUS_UNAVAIL;
333 }
334
335 if (is_localhost(name)) {
336 canonical = "localhost";
337 local_address_ipv4 = htonl(INADDR_LOOPBACK);
338 } else {
339 hn = gethostname_malloc();
340 if (!hn) {
341 *errnop = ENOMEM;
342 *h_errnop = NO_RECOVERY;
343 return NSS_STATUS_TRYAGAIN;
344 }
345
346 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
347 *errnop = ENOENT;
348 *h_errnop = HOST_NOT_FOUND;
349 return NSS_STATUS_NOTFOUND;
350 }
351
352 n_addresses = local_addresses(&addresses);
353 if (n_addresses < 0)
354 n_addresses = 0;
355
356 canonical = hn;
357 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
358 local_address_ipv4 = LOCALADDRESS_IPV4;
359 }
360
361 return fill_in_hostent(
362 canonical, additional,
363 af,
364 addresses, n_addresses,
365 local_address_ipv4,
366 host,
367 buffer, buflen,
368 errnop, h_errnop,
369 ttlp,
370 canonp);
371 }
372
373 enum nss_status _nss_myhostname_gethostbyaddr2_r(
374 const void* addr, socklen_t len,
375 int af,
376 struct hostent *host,
377 char *buffer, size_t buflen,
378 int *errnop, int *h_errnop,
379 int32_t *ttlp) {
380
381 const char *canonical = NULL, *additional = NULL;
382 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
383 _cleanup_free_ struct local_address *addresses = NULL;
384 _cleanup_free_ char *hn = NULL;
385 int n_addresses = 0;
386 struct local_address *a;
387 unsigned n;
388
389 assert(addr);
390 assert(host);
391 assert(buffer);
392 assert(errnop);
393 assert(h_errnop);
394
395 if (!IN_SET(af, AF_INET, AF_INET6)) {
396 *errnop = EAFNOSUPPORT;
397 *h_errnop = NO_DATA;
398 return NSS_STATUS_UNAVAIL;
399 }
400
401 if (len != FAMILY_ADDRESS_SIZE(af)) {
402 *errnop = EINVAL;
403 *h_errnop = NO_RECOVERY;
404 return NSS_STATUS_UNAVAIL;
405 }
406
407 if (af == AF_INET) {
408
409 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
410 goto found;
411
412 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
413 canonical = "localhost";
414 local_address_ipv4 = htonl(INADDR_LOOPBACK);
415 goto found;
416 }
417
418 } else {
419 assert(af == AF_INET6);
420
421 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
422 additional = "localhost";
423 goto found;
424 }
425
426 }
427
428 n_addresses = local_addresses(&addresses);
429 if (n_addresses < 0)
430 n_addresses = 0;
431
432 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
433 if (af != a->family)
434 continue;
435
436 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
437 goto found;
438 }
439
440 *errnop = ENOENT;
441 *h_errnop = HOST_NOT_FOUND;
442
443 return NSS_STATUS_NOTFOUND;
444
445 found:
446 if (!canonical) {
447 hn = gethostname_malloc();
448 if (!hn) {
449 *errnop = ENOMEM;
450 *h_errnop = NO_RECOVERY;
451 return NSS_STATUS_TRYAGAIN;
452 }
453
454 canonical = hn;
455 }
456
457 return fill_in_hostent(
458 canonical, additional,
459 af,
460 addresses, n_addresses,
461 local_address_ipv4,
462 host,
463 buffer, buflen,
464 errnop, h_errnop,
465 ttlp,
466 NULL);
467
468 }
469
470 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
471 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);