1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * This is the DNS backend for IPv6 addresses.
29 * getbyname() is a local routine, but getbyaddr() actually shares the
30 * same codes as the one in gethostent.c.
31 */
32
33 #define endhostent res_endhostent
34
35 #include <malloc.h>
36 #include <stddef.h>
37 #include <string.h>
38 #include "dns_common.h"
39
40 /*
41 * If the DNS name service switch routines are used in a binary that depends
42 * on an older libresolv (libresolv.so.1, say), then having nss_dns.so.1 or
43 * libnss_dns.a depend on a newer libresolv (libresolv.so.2) will cause
44 * relocation problems. In particular, copy relocation of the _res structure
45 * (which changes in size from libresolv.so.1 to libresolv.so.2) could
46 * cause corruption, and result in a number of strange problems, including
47 * core dumps. Hence, we check if a libresolv is already loaded.
48 */
49
50
51 #pragma weak res_endhostent
52
53 extern struct hostent *_gethostbyname(int *, const char *);
54 extern struct hostent *_nss_dns_gethostbyname2(int *, const char *);
55
56 typedef union {
57 long al;
58 char ac;
59 } align;
60
61
62 static void
_endhostent(errp)63 _endhostent(errp)
64 nss_status_t *errp;
65 {
66 int ret;
67
68 ret = endhostent();
69 if (ret == 0)
70 *errp = NSS_SUCCESS;
71 else
72 *errp = NSS_UNAVAIL;
73 }
74
75
76 #ifdef RNDUP
77 #undef RNDUP
78 #endif
79 #define RNDUP(x) ((1 + (((x)-1)/sizeof (void *))) * sizeof (void *))
80
81 #ifdef PTROFF
82 #undef PTROFF
83 #endif
84 #define PTROFF(p, o) (((o) == 0) ? 0 : (void *)((char *)(p) + (o)))
85
86
87 /*
88 * Make a copy of h->h_name.
89 */
90 static char *
cloneName(struct hostent * h,int * outerr)91 cloneName(struct hostent *h, int *outerr) {
92
93 char *name;
94 int len;
95 int error, *errp;
96
97 if (outerr)
98 errp = outerr;
99 else
100 errp = &error;
101
102 if (h == 0 || h->h_name == 0) {
103 *errp = 0;
104 return (0);
105 }
106
107 len = strlen(h->h_name);
108
109 if ((name = malloc(len+1)) == 0) {
110 *errp = 1;
111 return (0);
112 }
113
114 (void) memcpy(name, h->h_name, len+1);
115
116 *errp = 0;
117 return (name);
118 }
119
120
121 /*
122 * Copy the h->h_addr_list[] array to a new array, and append the
123 * moreAddrs[] list. If h->h_addr_list[] contains IPv4 addresses,
124 * convert them to v4 mapped IPv6 addresses.
125 *
126 * Note: The pointers to the addresses in the moreAddrs[] array are copied,
127 * but not the IP addresses themselves.
128 */
129 static struct in6_addr **
cloneAddrList(struct hostent * h,struct in6_addr ** moreAddrs,int * outerr)130 cloneAddrList(struct hostent *h, struct in6_addr **moreAddrs, int *outerr) {
131
132 struct in6_addr **addrArray, *addrList;
133 int domap, addrlen, i, j, addrCount, moreAddrCount = 0;
134
135 int error, *errp;
136
137 if (outerr)
138 errp = outerr;
139 else
140 errp = &error;
141
142 if (h == 0 || h->h_addr_list == 0) {
143 *errp = 0;
144 return (0);
145 }
146
147 /* Should we map v4 to IPv6 ? */
148 domap = (h->h_length == sizeof (struct in_addr)) &&
149 (h->h_addrtype == AF_INET);
150
151 /* If mapping, make sure we allocate enough memory for addresses */
152 addrlen = h->h_length;
153 if (domap && addrlen < sizeof (struct in6_addr))
154 addrlen = sizeof (struct in6_addr);
155
156 for (addrCount = 0; h->h_addr_list[addrCount]; addrCount++);
157
158 if (moreAddrs != 0) {
159 for (moreAddrCount = 0; moreAddrs[moreAddrCount];
160 moreAddrCount++);
161 }
162
163 if ((addrArray = malloc((addrCount+moreAddrCount+1)*sizeof (addrList) +
164 addrCount*addrlen)) == 0) {
165 *errp = 1;
166 return (0);
167 }
168
169 addrList = PTROFF(addrArray, (addrCount+moreAddrCount+1) *
170 sizeof (addrList));
171
172 for (i = 0; i < addrCount; i++) {
173 addrArray[i] = addrList;
174 if (domap) {
175 /* LINTED: E_BAD_PTR_CAST_ALIGN */
176 IN6_INADDR_TO_V4MAPPED(
177 (struct in_addr *)h->h_addr_list[i], addrArray[i]);
178 } else {
179 (void) memcpy(addrArray[i], h->h_addr_list[i],
180 addrlen);
181 }
182 addrList = PTROFF(addrList, addrlen);
183 }
184
185 for (j = 0; j < moreAddrCount; j++, i++) {
186 addrArray[i] = moreAddrs[j];
187 }
188
189 /* Last pointer should be NULL */
190 addrArray[i] = 0;
191
192 *errp = 0;
193 return (addrArray);
194 }
195
196
197 /*
198 * Create a new alias array that is is a copy of h->h_aliases[] plus
199 * the aliases in mergeAliases[] which aren't duplicates of any alias
200 * in h->h_aliases[].
201 *
202 * Note 1: Only the string pointers (NOT the strings) in the mergeAliases[]
203 * array are copied.
204 *
205 * Note 2: The duplicate aliases in mergeAliases[] are replaced by NULL
206 * pointers.
207 */
208 static char **
cloneAliasList(struct hostent * h,char ** mergeAliases,int * outerr)209 cloneAliasList(struct hostent *h, char **mergeAliases, int *outerr) {
210
211 char **aliasArray, *aliasList;
212 int i, j, aliasCount, mergeAliasCount = 0, realMac = 0;
213 int stringSize = 0;
214 int error, *errp;
215
216 if (outerr)
217 errp = outerr;
218 else
219 errp = &error;
220
221
222 if (h == 0 || h->h_aliases == 0) {
223 *errp = 0;
224 return (0);
225 }
226
227 for (aliasCount = 0; h->h_aliases[aliasCount]; aliasCount++) {
228 stringSize += RNDUP(strlen(h->h_aliases[aliasCount])+1);
229 }
230
231 if (mergeAliases != 0) {
232 for (; mergeAliases[mergeAliasCount]; mergeAliasCount++) {
233 int countThis = 1;
234 /* Skip duplicates */
235 for (j = 0; j < aliasCount; j++) {
236 if (strcmp(mergeAliases[mergeAliasCount],
237 h->h_aliases[j]) == 0) {
238 countThis = 0;
239 break;
240 }
241 }
242 if (countThis)
243 realMac++;
244 else
245 mergeAliases[mergeAliasCount] = 0;
246 }
247 }
248
249 if ((aliasArray = malloc((aliasCount+realMac+1)*sizeof (char **)+
250 stringSize)) == 0) {
251 *errp = 1;
252 return (0);
253 }
254
255 aliasList = PTROFF(aliasArray,
256 (aliasCount+realMac+1)*sizeof (char **));
257 for (i = 0; i < aliasCount; i++) {
258 int len = strlen(h->h_aliases[i]);
259 aliasArray[i] = aliasList;
260 (void) memcpy(aliasArray[i], h->h_aliases[i], len+1);
261 aliasList = PTROFF(aliasList, RNDUP(len+1));
262 }
263
264 for (j = 0; j < mergeAliasCount; j++) {
265 if (mergeAliases[j] != 0) {
266 aliasArray[i++] = mergeAliases[j];
267 }
268 }
269
270 aliasArray[i] = 0;
271
272 *errp = 0;
273 return (aliasArray);
274 }
275
276 /*ARGSUSED*/
277 static nss_status_t
getbyname(be,a)278 getbyname(be, a)
279 dns_backend_ptr_t be;
280 void *a;
281 {
282 struct hostent *he = NULL;
283 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a;
284 int ret, mt_disabled;
285 sigset_t oldmask;
286 int converr = 0, gotv6 = 0;
287 struct hostent v6he;
288 struct hostent mhe;
289 char *v6Name = 0;
290 struct in6_addr **v6Addrs = 0, **mergeAddrs = 0;
291 char **v6Aliases = 0, **mergeAliases = 0;
292 int v6_h_errno;
293 int old_retry;
294 int af = argp->key.ipnode.af_family;
295 int flags = argp->key.ipnode.flags;
296
297 switch_resolver_setup(&mt_disabled, &oldmask, &old_retry);
298
299 /* Now get the AAAA records */
300 if (af == AF_INET6)
301 he = _nss_dns_gethostbyname2(&argp->h_errno,
302 argp->key.ipnode.name);
303 if (he != NULL) {
304 /*
305 * pointer in "he" is part of a static pthread key in libresolv
306 * It should be treated as read only.
307 * So clone a copy first.
308 */
309 v6Name = cloneName(he, &converr);
310 if (converr) {
311 argp->h_errno = HOST_NOT_FOUND;
312 argp->erange = 1;
313 switch_resolver_reset(mt_disabled, oldmask, old_retry);
314 return (_herrno2nss(argp->h_errno));
315 }
316 v6Addrs = cloneAddrList(he, 0, &converr);
317 if (converr) {
318 if (v6Name != 0)
319 free(v6Name);
320 argp->h_errno = HOST_NOT_FOUND;
321 argp->erange = 1;
322 switch_resolver_reset(mt_disabled, oldmask, old_retry);
323 return (_herrno2nss(argp->h_errno));
324 }
325 v6Aliases = cloneAliasList(he, 0, &converr);
326 if (converr) {
327 if (v6Name != 0)
328 free(v6Name);
329 if (v6Addrs != 0)
330 free(v6Addrs);
331 argp->h_errno = HOST_NOT_FOUND;
332 argp->erange = 1;
333 switch_resolver_reset(mt_disabled, oldmask, old_retry);
334 return (_herrno2nss(argp->h_errno));
335 }
336 v6_h_errno = argp->h_errno;
337 gotv6 = 1;
338 }
339
340 /*
341 * The conditions to search "A" records:
342 * 1. af is AF_INET
343 * 2. if af is AF_INET6
344 * then flags are either
345 * 1) (AI_ALL | AI_V4MAPPED) or
346 * 2) AI_V4MAPPED and he == NULL
347 * (No V6 addresses found or no search for V6 at all)
348 */
349
350 /* Get the A records, and store the information */
351 if ((af == AF_INET) ||
352 ((af == AF_INET6) &&
353 ((flags & (AI_ALL | AI_V4MAPPED)) ||
354 ((flags & AI_V4MAPPED) && he == NULL))))
355 he = _gethostbyname(&argp->h_errno, argp->key.ipnode.name);
356 else
357 he = NULL;
358
359 /* Merge the results */
360 if (he != NULL) {
361 mhe = *he;
362 mergeAddrs = cloneAddrList(he, v6Addrs, &converr);
363 if (converr) {
364 if (v6Name != 0)
365 free(v6Name);
366 if (v6Addrs != 0)
367 free(v6Addrs);
368 if (v6Aliases != 0)
369 free(v6Aliases);
370 argp->h_errno = HOST_NOT_FOUND;
371 argp->erange = 1;
372 switch_resolver_reset(mt_disabled, oldmask,
373 old_retry);
374 return (_herrno2nss(argp->h_errno));
375 }
376 mhe.h_addr_list = (char **)mergeAddrs;
377
378 mergeAliases = cloneAliasList(he, v6Aliases, &converr);
379 if (converr) {
380 if (v6Name != 0)
381 free(v6Name);
382 if (v6Addrs != 0)
383 free(v6Addrs);
384 if (v6Aliases != 0)
385 free(v6Aliases);
386 if (mergeAddrs != 0)
387 free(mergeAddrs);
388 argp->h_errno = HOST_NOT_FOUND;
389 argp->erange = 1;
390 switch_resolver_reset(mt_disabled, oldmask,
391 old_retry);
392 return (_herrno2nss(argp->h_errno));
393 }
394 mhe.h_aliases = mergeAliases;
395
396 /* reset h_length, h_addrtype */
397 mhe.h_length = sizeof (struct in6_addr);
398 mhe.h_addrtype = AF_INET6;
399 he = &mhe;
400
401 } else if (gotv6) {
402 v6he.h_name = v6Name;
403 v6he.h_length = sizeof (struct in6_addr);
404 v6he.h_addrtype = AF_INET6;
405 v6he.h_addr_list = (char **)v6Addrs;
406 v6he.h_aliases = v6Aliases;
407 he = &v6he;
408 argp->h_errno = v6_h_errno;
409 }
410
411 if (he != NULL) {
412 /*
413 * if asked to return data in string,
414 * convert the hostent structure into
415 * string data
416 */
417 if (argp->buf.result == NULL) {
418 ret = ent2str(he, a, AF_INET6);
419 if (ret == NSS_STR_PARSE_SUCCESS)
420 argp->returnval = argp->buf.buffer;
421 } else {
422 ret = ent2result(he, a, AF_INET6);
423 if (ret == NSS_STR_PARSE_SUCCESS)
424 argp->returnval = argp->buf.result;
425 }
426
427 if (ret != NSS_STR_PARSE_SUCCESS) {
428 argp->h_errno = HOST_NOT_FOUND;
429 if (ret == NSS_STR_PARSE_ERANGE) {
430 argp->erange = 1;
431 }
432 }
433 }
434
435 if (v6Name != 0)
436 free(v6Name);
437 if (v6Addrs != 0)
438 free(v6Addrs);
439 if (v6Aliases != 0)
440 free(v6Aliases);
441 if (mergeAddrs != 0)
442 free(mergeAddrs);
443 if (mergeAliases != 0)
444 free(mergeAliases);
445
446 switch_resolver_reset(mt_disabled, oldmask, old_retry);
447
448 return (_herrno2nss(argp->h_errno));
449 }
450
451
452 extern nss_status_t __nss_dns_getbyaddr(dns_backend_ptr_t, void *);
453
454 static nss_status_t
getbyaddr(be,a)455 getbyaddr(be, a)
456 dns_backend_ptr_t be;
457 void *a;
458 {
459 /* uses the same getbyaddr from IPv4 */
460 return (__nss_dns_getbyaddr(be, a));
461 }
462
463
464 /*ARGSUSED*/
465 static nss_status_t
_nss_dns_getent(be,args)466 _nss_dns_getent(be, args)
467 dns_backend_ptr_t be;
468 void *args;
469 {
470 return (NSS_UNAVAIL);
471 }
472
473
474 /*ARGSUSED*/
475 static nss_status_t
_nss_dns_setent(be,dummy)476 _nss_dns_setent(be, dummy)
477 dns_backend_ptr_t be;
478 void *dummy;
479 {
480 /* XXXX not implemented at this point */
481 return (NSS_UNAVAIL);
482 }
483
484
485 /*ARGSUSED*/
486 static nss_status_t
_nss_dns_endent(be,dummy)487 _nss_dns_endent(be, dummy)
488 dns_backend_ptr_t be;
489 void *dummy;
490 {
491 /* XXXX not implemented at this point */
492 return (NSS_UNAVAIL);
493 }
494
495
496 /*ARGSUSED*/
497 static nss_status_t
_nss_dns_destr(be,dummy)498 _nss_dns_destr(be, dummy)
499 dns_backend_ptr_t be;
500 void *dummy;
501 {
502 nss_status_t errp;
503
504 if (be != 0) {
505 /* === Should change to invoke ops[ENDENT] ? */
506 sigset_t oldmask, newmask;
507 int mt_disabled = 1;
508
509 if (enable_mt == 0 || (mt_disabled = (*enable_mt)()) != 0) {
510 (void) sigfillset(&newmask);
511 (void) thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
512 (void) mutex_lock(&one_lane);
513 }
514
515 _endhostent(&errp);
516
517 if (mt_disabled) {
518 (void) mutex_unlock(&one_lane);
519 (void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
520 } else {
521 (void) (*disable_mt)();
522 }
523
524 free(be);
525 }
526 return (NSS_SUCCESS); /* In case anyone is dumb enough to check */
527 }
528
529
530
531 static dns_backend_op_t ipnodes_ops[] = {
532 _nss_dns_destr,
533 _nss_dns_endent,
534 _nss_dns_setent,
535 _nss_dns_getent,
536 getbyname,
537 getbyaddr,
538 };
539
540 /*ARGSUSED*/
541 nss_backend_t *
_nss_dns_ipnodes_constr(dummy1,dummy2,dummy3)542 _nss_dns_ipnodes_constr(dummy1, dummy2, dummy3)
543 const char *dummy1, *dummy2, *dummy3;
544 {
545 return (_nss_dns_constr(ipnodes_ops,
546 sizeof (ipnodes_ops) / sizeof (ipnodes_ops[0])));
547 }
548
549 /*
550 * optional NSS2 packed backend gethostsbyipnode with ttl
551 * entry point.
552 *
553 * Returns:
554 * NSS_SUCCESS - successful
555 * NSS_NOTFOUND - successful but nothing found
556 * NSS_ERROR - fallback to NSS backend lookup mode
557 * If successful, buffer will be filled with valid data
558 *
559 */
560
561 /*ARGSUSED*/
562 nss_status_t
_nss_get_dns_ipnodes_name(dns_backend_ptr_t * be,void ** bufp,size_t * sizep)563 _nss_get_dns_ipnodes_name(dns_backend_ptr_t *be, void **bufp, size_t *sizep)
564 {
565 return (_nss_dns_gethost_withttl(*bufp, *sizep, 1));
566 }
567