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