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