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