xref: /titanic_44/usr/src/lib/libnsl/netdir/netdir.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * Portions of this source code were derived from Berkeley 4.3 BSD
32  * under license from the Regents of the University of California.
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 /*
38  * netdir.c
39  *
40  * This is the library routines that do the name to address
41  * translation.
42  */
43 
44 #include "mt.h"
45 #include "../rpc/rpc_mt.h"		/* for MT declarations only */
46 #include <stdio.h>
47 #include <sys/types.h>
48 #include <errno.h>
49 #include <tiuser.h>
50 #include <netdir.h>
51 #include <netconfig.h>
52 #include <string.h>
53 #include <sys/file.h>
54 #include <dlfcn.h>
55 #include <rpc/trace.h>
56 #include <malloc.h>
57 #include <syslog.h>
58 #include <nss_netdir.h>
59 #include <netinet/in.h>
60 #include <netdb.h>
61 
62 /* messaging stuff. */
63 
64 extern const char __nsl_dom[];
65 extern char *dgettext(const char *, const char *);
66 
67 struct translator {
68 	struct nd_addrlist	*(*gbn)();	/* _netdir_getbyname	*/
69 	struct nd_hostservlist 	*(*gba)();	/* _netdir_getbyaddr	*/
70 	int			(*opt)();	/* _netdir_options	*/
71 	char			*(*t2u)();	/* _taddr2uaddr		*/
72 	struct netbuf		*(*u2t)();	/* _uaddr2taddr		*/
73 	void			*tr_fd;		/* dyn library handle	*/
74 	char			*tr_name;	/* Full path		*/
75 	struct translator	*next;
76 };
77 
78 /*
79  * xlate_lock protects xlate_list during updates only.  The xlate_list linked
80  * list is pre-pended when new entries are added, so threads that are already
81  * using the list will continue correctly to the end of the list.
82  */
83 static struct translator *xlate_list = NULL;
84 static mutex_t xlate_lock = DEFAULTMUTEX;
85 
86 static struct translator *load_xlate(char *);
87 
88 /*
89  * This is the common data (global data) that is exported
90  * by public interfaces. It has been moved here from nd_comdata.c
91  * which no longer exists. This fixes the problem for applications
92  * that do not link directly with -lnsl but dlopen a shared object
93  * that has a NEEDED dependency on -lnsl and uses the netdir
94  * interface.
95  */
96 
97 #undef	_nderror
98 
99 int	_nderror;
100 
101 int *
102 __nderror()
103 {
104 	static pthread_key_t nderror_key = 0;
105 	int *ret;
106 
107 	if (thr_main())
108 		return (&_nderror);
109 	ret = thr_get_storage(&nderror_key, sizeof (int), free);
110 	/* if thr_get_storage fails we return the address of _nderror */
111 	return (ret ? ret : &_nderror);
112 }
113 
114 #define	_nderror	(*(__nderror()))
115 
116 /*
117  * Adds a translator library to the xlate_list, but first check to see if
118  * it's already on the list.  Must be called while holding xlate_lock.
119  * We have to be careful for the case of the same library being loaded
120  * with different names (e.g., straddr.so and /usr/lib/straddr.so).
121  * We check for this case by looking at the gbn and name fields.
122  * If the gbn address is the same, but the names are different, then we
123  * have accidentally reloaded the library.  We dlclose the new version,
124  * and then update 'translate' with the old versions of the symbols.
125  */
126 void
127 add_to_xlate_list(translate)
128 struct translator *translate;
129 {
130 	struct translator	*t;
131 
132 	for (t = xlate_list; t; t = t->next) {
133 		if (strcmp(translate->tr_name, t->tr_name) == 0) {
134 			return;
135 		}
136 	}
137 	translate->next = xlate_list;
138 	xlate_list = translate;
139 }
140 
141 /*
142  * This routine is the main routine that resolves host/service/xprt triples
143  * into a bunch of netbufs that should connect you to that particular
144  * service. RPC uses it to contact the binder service (rpcbind).
145  *
146  * In the interest of consistency with the gethost/servbyYY() routines,
147  * this routine calls a common interface _get_hostserv_inetnetdir_byname
148  * if it's called with a netconfig with "inet" type transports and
149  * an empty list of nametoaddr libs (i.e. a "-" in /etc/netconfig),
150  * which indicates the use of the switch. For non-inet transports or
151  * inet transports with nametoaddr libs specified, it simply calls
152  * the SVr4-classic netdir_getbyname, which loops through the libs.
153  *
154  * After all, any problem can be solved by one more layer of abstraction..
155  *
156  * This routine when called with a netconfig with "inet6" type of transports
157  * returns pure IPv6 addresses only and if no IPv6 address is found it
158  * returns none - Bug Id. 4276329
159  */
160 int
161 netdir_getbyname(tp, serv, addrs)
162 	struct netconfig	*tp;	/* The network config entry	*/
163 	struct nd_hostserv	*serv;	/* the service, host pair	*/
164 	struct nd_addrlist	**addrs; /* the answer			*/
165 {
166 	trace1(TR_netdir_getbyname, 0);
167 	if (tp == 0) {
168 		_nderror = ND_BADARG;
169 		trace1(TR_netdir_getbyname, 1);
170 		return (_nderror);
171 	}
172 	if ((strcmp(tp->nc_protofmly, NC_INET) == 0) &&
173 		(tp->nc_nlookups == 0)) {
174 		struct	nss_netdirbyname_in nssin;
175 		union	nss_netdirbyname_out nssout;
176 
177 		nssin.op_t = NETDIR_BY;
178 		nssin.arg.nd_hs = serv;
179 		/*
180 		 * In code path of case NETDIR_BY,
181 		 * it also calls DOOR_GETIPNODEBYNAME_R.
182 		 * So af_family and flags are set to
183 		 * get V4 addresses only.
184 		 */
185 		nssin.arg.nss.host6.af_family = AF_INET;
186 		nssin.arg.nss.host6.flags = 0;
187 		nssout.nd_alist = addrs;
188 		return (_get_hostserv_inetnetdir_byname(tp, &nssin, &nssout));
189 	} else if ((strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
190 		(tp->nc_nlookups == 0)) {
191 		struct	nss_netdirbyname_in nssin;
192 		union	nss_netdirbyname_out nssout;
193 
194 		nssin.op_t = NETDIR_BY6;
195 		nssin.arg.nd_hs = serv;
196 		/* get both V4 & V6 addresses */
197 		nssin.arg.nss.host6.af_family = AF_INET6;
198 		nssin.arg.nss.host6.flags = (AI_ALL | AI_V4MAPPED);
199 		nssout.nd_alist = addrs;
200 		return (_get_hostserv_inetnetdir_byname(tp, &nssin, &nssout));
201 	} else {
202 		trace1(TR_netdir_getbyname, 1);
203 		return (__classic_netdir_getbyname(tp, serv, addrs));
204 	}
205 }
206 
207 /*
208  * This routine is the svr4_classic routine for resolving host/service/xprt
209  * triples into a bunch of netbufs that should connect you to that particular
210  * service. RPC uses it to contact the binder service (rpcbind).
211  *
212  * It's either called by the real netdir_getbyname() interface above
213  * or by gethost/servbyname when nametoaddr libs are specified in
214  * /etc/netconfig with an intent of bypassing the name service switch.
215  */
216 int
217 __classic_netdir_getbyname(tp, serv, addrs)
218 	struct netconfig	*tp;	/* The network config entry	*/
219 	struct nd_hostserv	*serv;	/* the service, host pair	*/
220 	struct nd_addrlist	**addrs; /* the answer			*/
221 {
222 	struct translator	*t;	/* pointer to translator list	*/
223 	struct nd_addrlist	*nn;	/* the results			*/
224 	char			*lr;	/* routines to try		*/
225 	int			i;	/* counts the routines		*/
226 
227 	trace1(TR_netdir_getbyname, 0);
228 	_nderror = ND_SYSTEM;
229 	for (i = 0; i < tp->nc_nlookups; i++) {
230 		lr = *((tp->nc_lookups) + i);
231 		for (t = xlate_list; t; t = t->next) {
232 			if (strcmp(lr, t->tr_name) == 0) {
233 				nn = (*(t->gbn))(tp, serv);
234 				if (nn) {
235 					*addrs = nn;
236 					trace1(TR_netdir_getbyname, 1);
237 					return (0);
238 				} else {
239 					if (_nderror < 0) {
240 						trace1(TR_netdir_getbyname, 1);
241 						return (_nderror);
242 					}
243 					break;
244 				}
245 			}
246 		}
247 		/* If we didn't find it try loading it */
248 		if (!t) {
249 			if ((t = load_xlate(lr)) != NULL) {
250 				/* add it to the list */
251 				mutex_lock(&xlate_lock);
252 				add_to_xlate_list(t);
253 				mutex_unlock(&xlate_lock);
254 				nn = (*(t->gbn))(tp, serv);
255 				if (nn) {
256 					*addrs = nn;
257 					trace1(TR_netdir_getbyname, 1);
258 					return (0);
259 				} else {
260 					if (_nderror < 0) {
261 						trace1(TR_netdir_getbyname, 1);
262 						return (_nderror);
263 					}
264 				}
265 			} else {
266 				if (_nderror == ND_SYSTEM) { /* retry cache */
267 					_nderror = ND_OK;
268 					i--;
269 					continue;
270 				}
271 			}
272 		}
273 	}
274 	trace1(TR_netdir_getbyname, 1);
275 	return (_nderror);	/* No one works */
276 }
277 
278 /*
279  * This routine is similar to the one above except that it tries to resolve
280  * the name by the address passed.
281  */
282 int
283 netdir_getbyaddr(tp, serv, addr)
284 	struct netconfig	*tp;	/* The netconfig entry		*/
285 	struct nd_hostservlist	**serv;	/* the answer(s)		*/
286 	struct netbuf		*addr;	/* the address we have		*/
287 {
288 	trace1(TR_netdir_getbyaddr, 0);
289 	if (tp == 0) {
290 		_nderror = ND_BADARG;
291 		trace1(TR_netdir_getbyaddr, 1);
292 		return (_nderror);
293 	}
294 	if ((strcmp(tp->nc_protofmly, NC_INET) == 0) &&
295 		(tp->nc_nlookups == 0)) {
296 		struct	nss_netdirbyaddr_in nssin;
297 		union	nss_netdirbyaddr_out nssout;
298 
299 		nssin.op_t = NETDIR_BY;
300 		nssin.arg.nd_nbuf = addr;
301 		nssout.nd_hslist = serv;
302 		trace1(TR_netdir_getbyaddr, 1);
303 		return (_get_hostserv_inetnetdir_byaddr(tp, &nssin, &nssout));
304 	} else if ((strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
305 		(tp->nc_nlookups == 0)) {
306 		struct	nss_netdirbyaddr_in nssin;
307 		union	nss_netdirbyaddr_out nssout;
308 
309 		nssin.op_t = NETDIR_BY6;
310 		nssin.arg.nd_nbuf = addr;
311 		nssout.nd_hslist = serv;
312 		trace1(TR_netdir_getbyaddr, 1);
313 		return (_get_hostserv_inetnetdir_byaddr(tp, &nssin, &nssout));
314 	} else {
315 		trace1(TR_netdir_getbyaddr, 1);
316 		return (__classic_netdir_getbyaddr(tp, serv, addr));
317 	}
318 }
319 /*
320  * This routine is similar to the one above except that it instructs the
321  * _get_hostserv_inetnetdir_byaddr not to do a service lookup.
322  */
323 int
324 __netdir_getbyaddr_nosrv(tp, serv, addr)
325 	struct netconfig	*tp;	/* The netconfig entry		*/
326 	struct nd_hostservlist	**serv;	/* the answer(s)		*/
327 	struct netbuf		*addr;	/* the address we have		*/
328 {
329 	trace1(TR_netdir_getbyaddr, 0);
330 	if (tp == 0) {
331 		_nderror = ND_BADARG;
332 		trace1(TR_netdir_getbyaddr, 1);
333 		return (_nderror);
334 	}
335 	if ((strcmp(tp->nc_protofmly, NC_INET) == 0) &&
336 		(tp->nc_nlookups == 0)) {
337 		struct	nss_netdirbyaddr_in nssin;
338 		union	nss_netdirbyaddr_out nssout;
339 
340 		nssin.op_t = NETDIR_BY_NOSRV;
341 		nssin.arg.nd_nbuf = addr;
342 		nssout.nd_hslist = serv;
343 		trace1(TR_netdir_getbyaddr, 1);
344 		return (_get_hostserv_inetnetdir_byaddr(tp, &nssin, &nssout));
345 	} else if ((strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
346 		(tp->nc_nlookups == 0)) {
347 		struct	nss_netdirbyaddr_in nssin;
348 		union	nss_netdirbyaddr_out nssout;
349 
350 		nssin.op_t = NETDIR_BY_NOSRV6;
351 		nssin.arg.nd_nbuf = addr;
352 		nssout.nd_hslist = serv;
353 		trace1(TR_netdir_getbyaddr, 1);
354 		return (_get_hostserv_inetnetdir_byaddr(tp, &nssin, &nssout));
355 	} else {
356 		trace1(TR_netdir_getbyaddr, 1);
357 		return (__classic_netdir_getbyaddr(tp, serv, addr));
358 	}
359 }
360 
361 /*
362  * This routine is the svr4_classic routine for resolving a netbuf struct
363  * into a bunch of host/service name pairs.
364  *
365  * It's either called by the real netdir_getbyaddr() interface above
366  * or by gethost/servbyaddr when nametoaddr libs are specified in
367  * /etc/netconfig with an intent of bypassing the name service switch.
368  */
369 int
370 __classic_netdir_getbyaddr(tp, serv, addr)
371 	struct netconfig	*tp;	/* The netconfig entry		*/
372 	struct nd_hostservlist	**serv;	/* the answer(s)		*/
373 	struct netbuf		*addr;	/* the address we have		*/
374 {
375 	struct translator	*t;	/* pointer to translator list	*/
376 	struct nd_hostservlist	*hs;	/* the results			*/
377 	char			*lr;	/* routines to try		*/
378 	int			i;	/* counts the routines		*/
379 
380 	trace1(TR_netdir_getbyaddr, 0);
381 	_nderror = ND_SYSTEM;
382 	for (i = 0; i < tp->nc_nlookups; i++) {
383 		lr = *((tp->nc_lookups) + i);
384 		for (t = xlate_list; t; t = t->next) {
385 			if (strcmp(lr, t->tr_name) == 0) {
386 				hs = (*(t->gba))(tp, addr);
387 				if (hs) {
388 					*serv = hs;
389 					trace1(TR_netdir_getbyaddr, 1);
390 					return (0);
391 				} else {
392 					if (_nderror < 0) {
393 						trace1(TR_netdir_getbyaddr, 1);
394 						return (_nderror);
395 					}
396 					break;
397 				}
398 			}
399 		}
400 		/* If we didn't find it try loading it */
401 		if (!t) {
402 			if ((t = load_xlate(lr)) != NULL) {
403 				/* add it to the list */
404 				mutex_lock(&xlate_lock);
405 				add_to_xlate_list(t);
406 				mutex_unlock(&xlate_lock);
407 				hs = (*(t->gba))(tp, addr);
408 				if (hs) {
409 					*serv = hs;
410 					trace1(TR_netdir_getbyaddr, 1);
411 					return (0);
412 				} else {
413 					if (_nderror < 0) {
414 						trace1(TR_netdir_getbyaddr, 1);
415 						return (_nderror);
416 					}
417 				}
418 			} else {
419 				if (_nderror == ND_SYSTEM) { /* retry cache */
420 					_nderror = ND_OK;
421 					i--;
422 					continue;
423 				}
424 			}
425 		}
426 	}
427 	trace1(TR_netdir_getbyaddr, 1);
428 	return (_nderror);	/* No one works */
429 }
430 
431 /*
432  * This is the library routine to do transport specific stuff.
433  * The code is same as the other similar routines except that it does
434  * not bother to try whole bunch of routines since if the first
435  * libray cannot resolve the option, then no one can.
436  *
437  * If it gets a netconfig structure for inet transports with nametoddr libs,
438  * it simply calls the inet-specific built in implementation.
439  */
440 int
441 netdir_options(tp, option, fd, par)
442 	struct netconfig	*tp;	/* the netconfig entry		*/
443 	int			option;	/* option number		*/
444 	int			fd;	/* open file descriptor		*/
445 	char			*par;	/* parameters if any		*/
446 {
447 	struct translator	*t;	/* pointer to translator list	*/
448 	char			*lr;	/* routines to try		*/
449 	int			i;	/* counts the routines		*/
450 
451 	trace3(TR_netdir_options, 0, option, fd);
452 	if (tp == 0) {
453 		_nderror = ND_BADARG;
454 		trace3(TR_netdir_options, 1, option, fd);
455 		return (_nderror);
456 	}
457 
458 	if ((strcmp(tp->nc_protofmly, NC_INET) == 0 ||
459 		strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
460 		(tp->nc_nlookups == 0)) {
461 		trace3(TR_netdir_options, 1, option, fd);
462 		return (__inet_netdir_options(tp, option, fd, par));
463 	}
464 
465 
466 	for (i = 0; i < tp->nc_nlookups; i++) {
467 		lr = *((tp->nc_lookups) + i);
468 		for (t = xlate_list; t; t = t->next) {
469 			if (strcmp(lr, t->tr_name) == 0) {
470 				trace3(TR_netdir_options, 1, option, fd);
471 				return ((*(t->opt))(tp, option, fd, par));
472 			}
473 		}
474 		/* If we didn't find it try loading it */
475 		if (!t) {
476 			if ((t = load_xlate(lr)) != NULL) {
477 				/* add it to the list */
478 				mutex_lock(&xlate_lock);
479 				add_to_xlate_list(t);
480 				mutex_unlock(&xlate_lock);
481 				trace3(TR_netdir_options, 1, option, fd);
482 				return ((*(t->opt))(tp, option, fd, par));
483 			} else {
484 				if (_nderror == ND_SYSTEM) { /* retry cache */
485 					_nderror = ND_OK;
486 					i--;
487 					continue;
488 				}
489 			}
490 		}
491 	}
492 	trace3(TR_netdir_options, 1, option, fd);
493 	return (_nderror);	/* No one works */
494 }
495 
496 /*
497  * This is the library routine for translating universal addresses to
498  * transport specific addresses. Again it uses the same code as above
499  * to search for the appropriate translation routine. Only it doesn't
500  * bother trying a whole bunch of routines since either the transport
501  * can translate it or it can't.
502  */
503 struct netbuf *
504 uaddr2taddr(tp, addr)
505 	struct netconfig	*tp;	/* the netconfig entry		*/
506 	char			*addr;	/* The address in question	*/
507 {
508 	struct translator	*t;	/* pointer to translator list 	*/
509 	struct netbuf		*x;	/* the answer we want 		*/
510 	char			*lr;	/* routines to try		*/
511 	int			i;	/* counts the routines		*/
512 
513 	trace1(TR_uaddr2taddr, 0);
514 	if (tp == 0) {
515 		_nderror = ND_BADARG;
516 		trace1(TR_uaddr2taddr, 1);
517 		return (0);
518 	}
519 	if ((strcmp(tp->nc_protofmly, NC_INET) == 0 ||
520 		strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
521 		(tp->nc_nlookups == 0)) {
522 		trace1(TR_uaddr2taddr, 1);
523 		return (__inet_uaddr2taddr(tp, addr));
524 	}
525 	for (i = 0; i < tp->nc_nlookups; i++) {
526 		lr = *((tp->nc_lookups) + i);
527 		for (t = xlate_list; t; t = t->next) {
528 			if (strcmp(lr, t->tr_name) == 0) {
529 				x = (*(t->u2t))(tp, addr);
530 				if (x) {
531 					trace1(TR_uaddr2taddr, 1);
532 					return (x);
533 				}
534 				if (_nderror < 0) {
535 					trace1(TR_uaddr2taddr, 1);
536 					return (0);
537 				}
538 			}
539 		}
540 		/* If we didn't find it try loading it */
541 		if (!t) {
542 			if ((t = load_xlate(lr)) != NULL) {
543 				/* add it to the list */
544 				mutex_lock(&xlate_lock);
545 				add_to_xlate_list(t);
546 				mutex_unlock(&xlate_lock);
547 				x = (*(t->u2t))(tp, addr);
548 				if (x) {
549 					trace1(TR_uaddr2taddr, 1);
550 					return (x);
551 				}
552 				if (_nderror < 0) {
553 					trace1(TR_uaddr2taddr, 1);
554 					return (0);
555 				}
556 			} else {
557 				if (_nderror == ND_SYSTEM) { /* retry cache */
558 					_nderror = ND_OK;
559 					i--;
560 					continue;
561 				}
562 			}
563 		}
564 	}
565 	trace1(TR_uaddr2taddr, 1);
566 	return (0);	/* No one works */
567 }
568 
569 /*
570  * This is the library routine for translating transport specific
571  * addresses to universal addresses. Again it uses the same code as above
572  * to search for the appropriate translation routine. Only it doesn't
573  * bother trying a whole bunch of routines since either the transport
574  * can translate it or it can't.
575  */
576 char *
577 taddr2uaddr(tp, addr)
578 	struct netconfig	*tp;	/* the netconfig entry		*/
579 	struct netbuf		*addr;	/* The address in question	*/
580 {
581 	struct translator	*t;	/* pointer to translator list	*/
582 	char			*lr;	/* routines to try		*/
583 	char			*x;	/* the answer			*/
584 	int			i;	/* counts the routines		*/
585 
586 	trace1(TR_taddr2uaddr, 0);
587 	if (tp == 0) {
588 		_nderror = ND_BADARG;
589 		trace1(TR_taddr2uaddr, 1);
590 		return (0);
591 	}
592 	if ((strcmp(tp->nc_protofmly, NC_INET) == 0 ||
593 		strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
594 		(tp->nc_nlookups == 0)) {
595 		trace1(TR_taddr2uaddr, 1);
596 		return (__inet_taddr2uaddr(tp, addr));
597 	}
598 	for (i = 0; i < tp->nc_nlookups; i++) {
599 		lr = *((tp->nc_lookups) + i);
600 		for (t = xlate_list; t; t = t->next) {
601 			if (strcmp(lr, t->tr_name) == 0) {
602 				x = (*(t->t2u))(tp, addr);
603 				if (x) {
604 					trace1(TR_taddr2uaddr, 1);
605 					return (x);
606 				}
607 				if (_nderror < 0) {
608 					trace1(TR_taddr2uaddr, 1);
609 					return (0);
610 				}
611 			}
612 		}
613 		/* If we didn't find it try loading it */
614 		if (!t) {
615 			if ((t = load_xlate(lr)) != NULL) {
616 				/* add it to the list */
617 				mutex_lock(&xlate_lock);
618 				add_to_xlate_list(t);
619 				mutex_unlock(&xlate_lock);
620 				x = (*(t->t2u))(tp, addr);
621 				if (x) {
622 					trace1(TR_taddr2uaddr, 1);
623 					return (x);
624 				}
625 				if (_nderror < 0) {
626 					trace1(TR_taddr2uaddr, 1);
627 					return (0);
628 				}
629 			} else {
630 				if (_nderror == ND_SYSTEM) { /* retry cache */
631 					_nderror = ND_OK;
632 					i--;
633 					continue;
634 				}
635 			}
636 		}
637 	}
638 	trace1(TR_taddr2uaddr, 1);
639 	return (0);	/* No one works */
640 }
641 
642 /*
643  * This is the routine that frees the objects that these routines allocate.
644  */
645 void
646 netdir_free(ptr, type)
647 	void	*ptr;	/* generic pointer	*/
648 	int	type;	/* thing we are freeing */
649 {
650 	struct netbuf		*na;
651 	struct nd_addrlist	*nas;
652 	struct nd_hostserv	*hs;
653 	struct nd_hostservlist	*hss;
654 	int			i;
655 
656 	trace2(TR_netdir_free, 0, type);
657 	if (ptr == NULL) {
658 		trace2(TR_netdir_free, 1, type);
659 		return;
660 	}
661 	switch (type) {
662 	case ND_ADDR :
663 		na = (struct netbuf *)ptr;
664 		if (na->buf)
665 			free(na->buf);
666 		free((char *)na);
667 		break;
668 
669 	case ND_ADDRLIST :
670 		nas = (struct nd_addrlist *)ptr;
671 		/*
672 		 * XXX: We do NOT try to free all individual netbuf->buf
673 		 * pointers. Free only the first one since they are allocated
674 		 * using one calloc in
675 		 * libnsl/nss/netdir_inet.c:order_haddrlist().
676 		 * This potentially causes memory leaks if a nametoaddr
677 		 * implementation -- from a third party -- has a different
678 		 * allocation scheme.
679 		 */
680 		if (nas->n_addrs->buf)
681 			free(nas->n_addrs->buf);
682 		free((char *)nas->n_addrs);
683 		free((char *)nas);
684 		break;
685 
686 	case ND_HOSTSERV :
687 		hs = (struct nd_hostserv *)ptr;
688 		if (hs->h_host)
689 			free(hs->h_host);
690 		if (hs->h_serv)
691 			free(hs->h_serv);
692 		free((char *)hs);
693 		break;
694 
695 	case ND_HOSTSERVLIST :
696 		hss = (struct nd_hostservlist *)ptr;
697 		for (hs = hss->h_hostservs, i = 0; i < hss->h_cnt; i++, hs++) {
698 			if (hs->h_host)
699 				free(hs->h_host);
700 			if (hs->h_serv)
701 				free(hs->h_serv);
702 		}
703 		free((char *)hss->h_hostservs);
704 		free((char *)hss);
705 		break;
706 
707 	default :
708 		_nderror = ND_UKNWN;
709 		break;
710 	}
711 	trace2(TR_netdir_free, 1, type);
712 }
713 
714 /*
715  * load_xlate is a routine that will attempt to dynamically link in the
716  * file specified by the network configuration structure.
717  */
718 static struct translator *
719 load_xlate(name)
720 	char	*name;		/* file name to load */
721 {
722 	struct translator	*t;
723 	static struct xlate_list {
724 		char *library;
725 		struct xlate_list *next;
726 	} *xlistp = NULL;
727 	struct xlate_list *xlp, **xlastp;
728 	static mutex_t xlist_lock = DEFAULTMUTEX;
729 
730 	trace1(TR_load_xlate, 0);
731 	mutex_lock(&xlist_lock);
732 	/*
733 	 * We maintain a list of libraries we have loaded.  Loading a library
734 	 * twice is double-plus ungood!
735 	 */
736 	for (xlp = xlistp, xlastp = &xlistp; xlp != NULL;
737 			xlastp = &xlp->next, xlp = xlp->next) {
738 		if (xlp->library != NULL) {
739 			if (strcmp(xlp->library, name) == 0) {
740 				_nderror = ND_SYSTEM;	/* seen this lib */
741 				mutex_unlock(&xlist_lock);
742 				trace1(TR_load_xlate, 1);
743 				return (0);
744 			}
745 		}
746 	}
747 	t = (struct translator *)malloc(sizeof (struct translator));
748 	if (!t) {
749 		_nderror = ND_NOMEM;
750 		mutex_unlock(&xlist_lock);
751 		trace1(TR_load_xlate, 1);
752 		return (0);
753 	}
754 	t->tr_name = strdup(name);
755 	if (!t->tr_name) {
756 		_nderror = ND_NOMEM;
757 		free((char *)t);
758 		mutex_unlock(&xlist_lock);
759 		trace1(TR_load_xlate, 1);
760 		return (NULL);
761 	}
762 
763 	t->tr_fd = dlopen(name, RTLD_LAZY);
764 	if (t->tr_fd == NULL) {
765 		_nderror = ND_OPEN;
766 		goto error;
767 	}
768 
769 	/* Resolve the getbyname symbol */
770 	t->gbn = (struct nd_addrlist *(*)())dlsym(t->tr_fd,
771 				"_netdir_getbyname");
772 	if (!(t->gbn)) {
773 		_nderror = ND_NOSYM;
774 		goto error;
775 	}
776 
777 	/* resolve the getbyaddr symbol */
778 	t->gba = (struct nd_hostservlist *(*)())dlsym(t->tr_fd,
779 				"_netdir_getbyaddr");
780 	if (!(t->gba)) {
781 		_nderror = ND_NOSYM;
782 		goto error;
783 	}
784 
785 	/* resolve the taddr2uaddr symbol */
786 	t->t2u = (char *(*)())dlsym(t->tr_fd, "_taddr2uaddr");
787 	if (!(t->t2u)) {
788 		_nderror = ND_NOSYM;
789 		goto error;
790 	}
791 
792 	/* resolve the uaddr2taddr symbol */
793 	t->u2t = (struct netbuf *(*)())dlsym(t->tr_fd, "_uaddr2taddr");
794 	if (!(t->u2t)) {
795 		_nderror = ND_NOSYM;
796 		goto error;
797 	}
798 
799 	/* resolve the netdir_options symbol */
800 	t->opt = (int (*)())dlsym(t->tr_fd, "_netdir_options");
801 	if (!(t->opt)) {
802 		_nderror = ND_NOSYM;
803 		goto error;
804 	}
805 	/*
806 	 * Add this library to the list of loaded libraries.
807 	 */
808 	*xlastp = (struct xlate_list *)malloc(sizeof (struct xlate_list));
809 	if (*xlastp == NULL) {
810 		_nderror = ND_NOMEM;
811 		goto error;
812 	}
813 	(*xlastp)->library = strdup(name);
814 	if ((*xlastp)->library == NULL) {
815 		_nderror = ND_NOMEM;
816 		free(*xlastp);
817 		goto error;
818 	}
819 	(*xlastp)->next = NULL;
820 	mutex_unlock(&xlist_lock);
821 	trace1(TR_load_xlate, 1);
822 	return (t);
823 error:
824 	if (t->tr_fd != NULL)
825 		(void) dlclose(t->tr_fd);
826 	free(t->tr_name);
827 	free((char *)t);
828 	mutex_unlock(&xlist_lock);
829 	trace1(TR_load_xlate, 1);
830 	return (NULL);
831 }
832 
833 
834 #define	NDERR_BUFSZ	512
835 
836 /*
837  * This is a routine that returns a string related to the current
838  * error in _nderror.
839  */
840 char *
841 netdir_sperror()
842 {
843 	static pthread_key_t nderrbuf_key = 0;
844 	static char buf_main[NDERR_BUFSZ];
845 	char	*str;
846 	char *dlerrstr;
847 
848 	trace1(TR_netdir_sperror, 0);
849 	str = thr_main()?
850 		buf_main :
851 		thr_get_storage(&nderrbuf_key, NDERR_BUFSZ, free);
852 	if (str == NULL) {
853 		trace1(TR_netdir_sperror, 1);
854 		return (NULL);
855 	}
856 	dlerrstr = dlerror();
857 	switch (_nderror) {
858 	case ND_NOMEM :
859 		(void) snprintf(str, NDERR_BUFSZ,
860 			dgettext(__nsl_dom, "n2a: memory allocation failed"));
861 		break;
862 	case ND_OK :
863 		(void) snprintf(str, NDERR_BUFSZ,
864 			dgettext(__nsl_dom, "n2a: successful completion"));
865 		break;
866 	case ND_NOHOST :
867 		(void) snprintf(str, NDERR_BUFSZ,
868 			dgettext(__nsl_dom, "n2a: hostname not found"));
869 		break;
870 	case ND_NOSERV :
871 		(void) snprintf(str, NDERR_BUFSZ,
872 			dgettext(__nsl_dom, "n2a: service name not found"));
873 		break;
874 	case ND_NOSYM :
875 		(void) snprintf(str, NDERR_BUFSZ, "%s : %s ",
876 			dgettext(__nsl_dom,
877 			"n2a: symbol missing in shared object"),
878 			dlerrstr ? dlerrstr : " ");
879 		break;
880 	case ND_OPEN :
881 		(void) snprintf(str, NDERR_BUFSZ, "%s - %s ",
882 			dgettext(__nsl_dom, "n2a: couldn't open shared object"),
883 			dlerrstr ? dlerrstr : " ");
884 		break;
885 	case ND_ACCESS :
886 		(void) snprintf(str, NDERR_BUFSZ,
887 			dgettext(__nsl_dom,
888 			"n2a: access denied for shared object"));
889 		break;
890 	case ND_UKNWN :
891 		(void) snprintf(str, NDERR_BUFSZ,
892 			dgettext(__nsl_dom,
893 			"n2a: attempt to free unknown object"));
894 		break;
895 	case ND_BADARG :
896 		(void) snprintf(str, NDERR_BUFSZ,
897 			dgettext(__nsl_dom,
898 			"n2a: bad arguments passed to routine"));
899 		break;
900 	case ND_NOCTRL:
901 		(void) snprintf(str, NDERR_BUFSZ,
902 			dgettext(__nsl_dom, "n2a: unknown option passed"));
903 		break;
904 	case ND_FAILCTRL:
905 		(void) snprintf(str, NDERR_BUFSZ,
906 			dgettext(__nsl_dom, "n2a: control operation failed"));
907 		break;
908 	case ND_SYSTEM:
909 		(void) snprintf(str, NDERR_BUFSZ, "%s: %s",
910 			dgettext(__nsl_dom, "n2a: system error"),
911 			strerror(errno));
912 		break;
913 	default :
914 		(void) snprintf(str, NDERR_BUFSZ, "%s#%d",
915 			dgettext(__nsl_dom, "n2a: unknown error "), _nderror);
916 		break;
917 	}
918 	trace1(TR_netdir_sperror, 1);
919 	return (str);
920 }
921 
922 /*
923  * This is a routine that prints out strings related to the current
924  * error in _nderror. Like perror() it takes a string to print with a
925  * colon first.
926  */
927 void
928 netdir_perror(s)
929 	char	*s;
930 {
931 	char	*err;
932 
933 	trace1(TR_netdir_perror, 0);
934 	err = netdir_sperror();
935 	(void) fprintf(stderr, "%s: %s\n", s, err ? err : "n2a: error");
936 	trace1(TR_netdir_perror, 1);
937 }
938