import less(1)
[unleashed/tickless.git] / usr / src / lib / libc / port / nsl / netdir.c
blob528b0d6f0bfc4ac2ea35672e239e799e7882d9e1
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * Portions of this source code were derived from Berkeley 4.3 BSD
32 * under license from the Regents of the University of California.
36 * netdir.c
38 * This is the library routines that do the name to address
39 * translation.
42 #include "mt.h"
43 #include "rpc_mt.h" /* for MT declarations only */
44 #include <stdio.h>
45 #include <sys/types.h>
46 #include <errno.h>
47 #include <tiuser.h>
48 #include <netdir.h>
49 #include <netconfig.h>
50 #include <string.h>
51 #include <sys/file.h>
52 #include <dlfcn.h>
53 #include <stdlib.h>
54 #include <malloc.h>
55 #include <syslog.h>
56 #include <nss_netdir.h>
57 #include <netinet/in.h>
58 #include <netdb.h>
60 /* messaging stuff. */
62 extern const char __nsl_dom[];
63 extern char *dgettext(const char *, const char *);
65 struct translator {
66 struct nd_addrlist *(*gbn)(); /* _netdir_getbyname */
67 struct nd_hostservlist *(*gba)(); /* _netdir_getbyaddr */
68 int (*opt)(); /* _netdir_options */
69 char *(*t2u)(); /* _taddr2uaddr */
70 struct netbuf *(*u2t)(); /* _uaddr2taddr */
71 void *tr_fd; /* dyn library handle */
72 char *tr_name; /* Full path */
73 struct translator *next;
77 * xlate_lock protects xlate_list during updates only. The xlate_list linked
78 * list is pre-pended when new entries are added, so threads that are already
79 * using the list will continue correctly to the end of the list.
81 static struct translator *xlate_list = NULL;
82 static mutex_t xlate_lock = DEFAULTMUTEX;
84 static struct translator *load_xlate(char *);
87 * This is the common data (global data) that is exported
88 * by public interfaces. It has been moved here from nd_comdata.c
89 * which no longer exists. This fixes the problem for applications
90 * that do not link directly with -lnsl but dlopen a shared object
91 * that has a NEEDED dependency on -lnsl and uses the netdir
92 * interface.
95 #undef _nderror
97 int _nderror;
99 int *
100 __nderror(void)
102 static pthread_key_t nderror_key = PTHREAD_ONCE_KEY_NP;
103 int *ret;
105 if (thr_main())
106 return (&_nderror);
107 ret = thr_get_storage(&nderror_key, sizeof (int), free);
108 /* if thr_get_storage fails we return the address of _nderror */
109 return (ret ? ret : &_nderror);
112 #define _nderror (*(__nderror()))
115 * Adds a translator library to the xlate_list, but first check to see if
116 * it's already on the list. Must be called while holding xlate_lock.
117 * We have to be careful for the case of the same library being loaded
118 * with different names (e.g., straddr.so and /usr/lib/straddr.so).
119 * We check for this case by looking at the gbn and name fields.
120 * If the gbn address is the same, but the names are different, then we
121 * have accidentally reloaded the library. We dlclose the new version,
122 * and then update 'translate' with the old versions of the symbols.
124 void
125 add_to_xlate_list(struct translator *translate)
127 struct translator *t;
129 for (t = xlate_list; t; t = t->next) {
130 if (strcmp(translate->tr_name, t->tr_name) == 0) {
131 return;
134 translate->next = xlate_list;
135 xlate_list = translate;
139 * This routine is the main routine that resolves host/service/xprt triples
140 * into a bunch of netbufs that should connect you to that particular
141 * service. RPC uses it to contact the binder service (rpcbind).
143 * In the interest of consistency with the gethost/servbyYY() routines,
144 * this routine calls a common interface _get_hostserv_inetnetdir_byname
145 * if it's called with a netconfig with "inet" type transports and
146 * an empty list of nametoaddr libs (i.e. a "-" in /etc/netconfig),
147 * which indicates the use of the switch. For non-inet transports or
148 * inet transports with nametoaddr libs specified, it simply calls
149 * the SVr4-classic netdir_getbyname, which loops through the libs.
151 * After all, any problem can be solved by one more layer of abstraction..
153 * This routine when called with a netconfig with "inet6" type of transports
154 * returns pure IPv6 addresses only and if no IPv6 address is found it
155 * returns none - Bug Id. 4276329
158 netdir_getbyname(struct netconfig *tp, struct nd_hostserv *serv,
159 struct nd_addrlist **addrs)
161 if (tp == 0) {
162 _nderror = ND_BADARG;
163 return (_nderror);
165 if ((strcmp(tp->nc_protofmly, NC_INET) == 0) &&
166 (tp->nc_nlookups == 0)) {
167 struct nss_netdirbyname_in nssin;
168 union nss_netdirbyname_out nssout;
170 nssin.op_t = NETDIR_BY;
171 nssin.arg.nd_hs = serv;
173 * In code path of case NETDIR_BY,
174 * it also calls DOOR_GETIPNODEBYNAME_R.
175 * So af_family and flags are set to
176 * get V4 addresses only.
178 nssin.arg.nss.host6.af_family = AF_INET;
179 nssin.arg.nss.host6.flags = 0;
180 nssout.nd_alist = addrs;
181 return (_get_hostserv_inetnetdir_byname(tp, &nssin, &nssout));
183 if ((strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
184 (tp->nc_nlookups == 0)) {
185 struct nss_netdirbyname_in nssin;
186 union nss_netdirbyname_out nssout;
188 nssin.op_t = NETDIR_BY6;
189 nssin.arg.nd_hs = serv;
190 /* get both V4 & V6 addresses */
191 nssin.arg.nss.host6.af_family = AF_INET6;
192 nssin.arg.nss.host6.flags = (AI_ALL | AI_V4MAPPED);
193 nssout.nd_alist = addrs;
194 return (_get_hostserv_inetnetdir_byname(tp, &nssin, &nssout));
196 return (__classic_netdir_getbyname(tp, serv, addrs));
200 * This routine is the svr4_classic routine for resolving host/service/xprt
201 * triples into a bunch of netbufs that should connect you to that particular
202 * service. RPC uses it to contact the binder service (rpcbind).
204 * It's either called by the real netdir_getbyname() interface above
205 * or by gethost/servbyname when nametoaddr libs are specified in
206 * /etc/netconfig with an intent of bypassing the name service switch.
209 __classic_netdir_getbyname(struct netconfig *tp, struct nd_hostserv *serv,
210 struct nd_addrlist **addrs)
212 struct translator *t; /* pointer to translator list */
213 struct nd_addrlist *nn; /* the results */
214 char *lr; /* routines to try */
215 int i; /* counts the routines */
217 _nderror = ND_SYSTEM;
218 for (i = 0; i < tp->nc_nlookups; i++) {
219 lr = *((tp->nc_lookups) + i);
220 for (t = xlate_list; t; t = t->next) {
221 if (strcmp(lr, t->tr_name) == 0) {
222 nn = (*(t->gbn))(tp, serv);
223 if (nn) {
224 *addrs = nn;
225 return (0);
227 if (_nderror < 0) {
228 return (_nderror);
230 break;
233 /* If we didn't find it try loading it */
234 if (!t) {
235 if ((t = load_xlate(lr)) != NULL) {
236 /* add it to the list */
237 (void) mutex_lock(&xlate_lock);
238 add_to_xlate_list(t);
239 (void) mutex_unlock(&xlate_lock);
240 nn = (*(t->gbn))(tp, serv);
241 if (nn) {
242 *addrs = nn;
243 return (0);
245 if (_nderror < 0) {
246 return (_nderror);
248 } else {
249 if (_nderror == ND_SYSTEM) { /* retry cache */
250 _nderror = ND_OK;
251 i--;
252 continue;
257 return (_nderror); /* No one works */
261 * This routine is similar to the one above except that it tries to resolve
262 * the name by the address passed.
265 netdir_getbyaddr(struct netconfig *tp, struct nd_hostservlist **serv,
266 struct netbuf *addr)
268 if (tp == 0) {
269 _nderror = ND_BADARG;
270 return (_nderror);
272 if ((strcmp(tp->nc_protofmly, NC_INET) == 0) &&
273 (tp->nc_nlookups == 0)) {
274 struct nss_netdirbyaddr_in nssin;
275 union nss_netdirbyaddr_out nssout;
277 nssin.op_t = NETDIR_BY;
278 nssin.arg.nd_nbuf = addr;
279 nssout.nd_hslist = serv;
280 return (_get_hostserv_inetnetdir_byaddr(tp, &nssin, &nssout));
282 if ((strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
283 (tp->nc_nlookups == 0)) {
284 struct nss_netdirbyaddr_in nssin;
285 union nss_netdirbyaddr_out nssout;
287 nssin.op_t = NETDIR_BY6;
288 nssin.arg.nd_nbuf = addr;
289 nssout.nd_hslist = serv;
290 return (_get_hostserv_inetnetdir_byaddr(tp, &nssin, &nssout));
292 return (__classic_netdir_getbyaddr(tp, serv, addr));
295 * This routine is similar to the one above except that it instructs the
296 * _get_hostserv_inetnetdir_byaddr not to do a service lookup.
299 __netdir_getbyaddr_nosrv(struct netconfig *tp, struct nd_hostservlist **serv,
300 struct netbuf *addr)
302 if (tp == 0) {
303 _nderror = ND_BADARG;
304 return (_nderror);
306 if ((strcmp(tp->nc_protofmly, NC_INET) == 0) &&
307 (tp->nc_nlookups == 0)) {
308 struct nss_netdirbyaddr_in nssin;
309 union nss_netdirbyaddr_out nssout;
311 nssin.op_t = NETDIR_BY_NOSRV;
312 nssin.arg.nd_nbuf = addr;
313 nssout.nd_hslist = serv;
314 return (_get_hostserv_inetnetdir_byaddr(tp, &nssin, &nssout));
316 if ((strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
317 (tp->nc_nlookups == 0)) {
318 struct nss_netdirbyaddr_in nssin;
319 union nss_netdirbyaddr_out nssout;
321 nssin.op_t = NETDIR_BY_NOSRV6;
322 nssin.arg.nd_nbuf = addr;
323 nssout.nd_hslist = serv;
324 return (_get_hostserv_inetnetdir_byaddr(tp, &nssin, &nssout));
326 return (__classic_netdir_getbyaddr(tp, serv, addr));
330 * This routine is the svr4_classic routine for resolving a netbuf struct
331 * into a bunch of host/service name pairs.
333 * It's either called by the real netdir_getbyaddr() interface above
334 * or by gethost/servbyaddr when nametoaddr libs are specified in
335 * /etc/netconfig with an intent of bypassing the name service switch.
338 __classic_netdir_getbyaddr(struct netconfig *tp, struct nd_hostservlist **serv,
339 struct netbuf *addr)
341 struct translator *t; /* pointer to translator list */
342 struct nd_hostservlist *hs; /* the results */
343 char *lr; /* routines to try */
344 int i; /* counts the routines */
346 _nderror = ND_SYSTEM;
347 for (i = 0; i < tp->nc_nlookups; i++) {
348 lr = *((tp->nc_lookups) + i);
349 for (t = xlate_list; t; t = t->next) {
350 if (strcmp(lr, t->tr_name) == 0) {
351 hs = (*(t->gba))(tp, addr);
352 if (hs) {
353 *serv = hs;
354 return (0);
356 if (_nderror < 0)
357 return (_nderror);
358 break;
361 /* If we didn't find it try loading it */
362 if (!t) {
363 if ((t = load_xlate(lr)) != NULL) {
364 /* add it to the list */
365 (void) mutex_lock(&xlate_lock);
366 add_to_xlate_list(t);
367 (void) mutex_unlock(&xlate_lock);
368 hs = (*(t->gba))(tp, addr);
369 if (hs) {
370 *serv = hs;
371 return (0);
373 if (_nderror < 0)
374 return (_nderror);
375 } else {
376 if (_nderror == ND_SYSTEM) { /* retry cache */
377 _nderror = ND_OK;
378 i--;
379 continue;
384 return (_nderror); /* No one works */
388 * This is the library routine to do transport specific stuff.
389 * The code is same as the other similar routines except that it does
390 * not bother to try whole bunch of routines since if the first
391 * libray cannot resolve the option, then no one can.
393 * If it gets a netconfig structure for inet transports with nametoddr libs,
394 * it simply calls the inet-specific built in implementation.
397 netdir_options(struct netconfig *tp, int option, int fd, char *par)
399 struct translator *t; /* pointer to translator list */
400 char *lr; /* routines to try */
401 int i; /* counts the routines */
403 if (tp == 0) {
404 _nderror = ND_BADARG;
405 return (_nderror);
408 if ((strcmp(tp->nc_protofmly, NC_INET) == 0 ||
409 strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
410 (tp->nc_nlookups == 0)) {
411 return (__inet_netdir_options(tp, option, fd, par));
415 for (i = 0; i < tp->nc_nlookups; i++) {
416 lr = *((tp->nc_lookups) + i);
417 for (t = xlate_list; t; t = t->next) {
418 if (strcmp(lr, t->tr_name) == 0)
419 return ((*(t->opt))(tp, option, fd, par));
421 /* If we didn't find it try loading it */
422 if (!t) {
423 if ((t = load_xlate(lr)) != NULL) {
424 /* add it to the list */
425 (void) mutex_lock(&xlate_lock);
426 add_to_xlate_list(t);
427 (void) mutex_unlock(&xlate_lock);
428 return ((*(t->opt))(tp, option, fd, par));
430 if (_nderror == ND_SYSTEM) { /* retry cache */
431 _nderror = ND_OK;
432 i--;
433 continue;
437 return (_nderror); /* No one works */
441 * This is the library routine for translating universal addresses to
442 * transport specific addresses. Again it uses the same code as above
443 * to search for the appropriate translation routine. Only it doesn't
444 * bother trying a whole bunch of routines since either the transport
445 * can translate it or it can't.
447 struct netbuf *
448 uaddr2taddr(struct netconfig *tp, char *addr)
450 struct translator *t; /* pointer to translator list */
451 struct netbuf *x; /* the answer we want */
452 char *lr; /* routines to try */
453 int i; /* counts the routines */
455 if (tp == 0) {
456 _nderror = ND_BADARG;
457 return (0);
459 if ((strcmp(tp->nc_protofmly, NC_INET) == 0 ||
460 strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
461 (tp->nc_nlookups == 0)) {
462 return (__inet_uaddr2taddr(tp, addr));
464 for (i = 0; i < tp->nc_nlookups; i++) {
465 lr = *((tp->nc_lookups) + i);
466 for (t = xlate_list; t; t = t->next) {
467 if (strcmp(lr, t->tr_name) == 0) {
468 x = (*(t->u2t))(tp, addr);
469 if (x)
470 return (x);
471 if (_nderror < 0)
472 return (0);
475 /* If we didn't find it try loading it */
476 if (!t) {
477 if ((t = load_xlate(lr)) != NULL) {
478 /* add it to the list */
479 (void) mutex_lock(&xlate_lock);
480 add_to_xlate_list(t);
481 (void) mutex_unlock(&xlate_lock);
482 x = (*(t->u2t))(tp, addr);
483 if (x)
484 return (x);
485 if (_nderror < 0)
486 return (0);
487 } else {
488 if (_nderror == ND_SYSTEM) { /* retry cache */
489 _nderror = ND_OK;
490 i--;
491 continue;
496 return (0); /* No one works */
500 * This is the library routine for translating transport specific
501 * addresses to universal addresses. Again it uses the same code as above
502 * to search for the appropriate translation routine. Only it doesn't
503 * bother trying a whole bunch of routines since either the transport
504 * can translate it or it can't.
506 char *
507 taddr2uaddr(struct netconfig *tp, struct netbuf *addr)
509 struct translator *t; /* pointer to translator list */
510 char *lr; /* routines to try */
511 char *x; /* the answer */
512 int i; /* counts the routines */
514 if (tp == 0) {
515 _nderror = ND_BADARG;
516 return (0);
518 if ((strcmp(tp->nc_protofmly, NC_INET) == 0 ||
519 strcmp(tp->nc_protofmly, NC_INET6) == 0) &&
520 (tp->nc_nlookups == 0)) {
521 return (__inet_taddr2uaddr(tp, addr));
523 for (i = 0; i < tp->nc_nlookups; i++) {
524 lr = *((tp->nc_lookups) + i);
525 for (t = xlate_list; t; t = t->next) {
526 if (strcmp(lr, t->tr_name) == 0) {
527 x = (*(t->t2u))(tp, addr);
528 if (x)
529 return (x);
530 if (_nderror < 0)
531 return (0);
534 /* If we didn't find it try loading it */
535 if (!t) {
536 if ((t = load_xlate(lr)) != NULL) {
537 /* add it to the list */
538 (void) mutex_lock(&xlate_lock);
539 add_to_xlate_list(t);
540 (void) mutex_unlock(&xlate_lock);
541 x = (*(t->t2u))(tp, addr);
542 if (x)
543 return (x);
544 if (_nderror < 0)
545 return (0);
546 } else {
547 if (_nderror == ND_SYSTEM) { /* retry cache */
548 _nderror = ND_OK;
549 i--;
550 continue;
555 return (0); /* No one works */
559 * This is the routine that frees the objects that these routines allocate.
561 void
562 netdir_free(void *ptr, int type)
564 struct netbuf *na;
565 struct nd_addrlist *nas;
566 struct nd_hostserv *hs;
567 struct nd_hostservlist *hss;
568 int i;
570 if (ptr == NULL)
571 return;
572 switch (type) {
573 case ND_ADDR :
574 na = (struct netbuf *)ptr;
575 free(na->buf);
576 free(na);
577 break;
579 case ND_ADDRLIST :
580 nas = (struct nd_addrlist *)ptr;
582 * XXX: We do NOT try to free all individual netbuf->buf
583 * pointers. Free only the first one since they are allocated
584 * using one calloc in
585 * libnsl/nss/netdir_inet.c:order_haddrlist().
586 * This potentially causes memory leaks if a nametoaddr
587 * implementation -- from a third party -- has a different
588 * allocation scheme.
590 free(nas->n_addrs->buf);
591 free(nas->n_addrs);
592 free(nas);
593 break;
595 case ND_HOSTSERV :
596 hs = (struct nd_hostserv *)ptr;
597 free(hs->h_host);
598 free(hs->h_serv);
599 free(hs);
600 break;
602 case ND_HOSTSERVLIST :
603 hss = (struct nd_hostservlist *)ptr;
604 for (hs = hss->h_hostservs, i = 0; i < hss->h_cnt; i++, hs++) {
605 free(hs->h_host);
606 free(hs->h_serv);
608 free(hss->h_hostservs);
609 free(hss);
610 break;
612 default :
613 _nderror = ND_UKNWN;
614 break;
619 * load_xlate is a routine that will attempt to dynamically link in the
620 * file specified by the network configuration structure.
622 static struct translator *
623 load_xlate(char *name)
625 struct translator *t;
626 static struct xlate_list {
627 char *library;
628 struct xlate_list *next;
629 } *xlistp = NULL;
630 struct xlate_list *xlp, **xlastp;
631 static mutex_t xlist_lock = DEFAULTMUTEX;
633 (void) mutex_lock(&xlist_lock);
635 * We maintain a list of libraries we have loaded. Loading a library
636 * twice is double-plus ungood!
638 for (xlp = xlistp, xlastp = &xlistp; xlp != NULL;
639 xlastp = &xlp->next, xlp = xlp->next) {
640 if (xlp->library != NULL) {
641 if (strcmp(xlp->library, name) == 0) {
642 _nderror = ND_SYSTEM; /* seen this lib */
643 (void) mutex_unlock(&xlist_lock);
644 return (0);
648 t = malloc(sizeof (struct translator));
649 if (!t) {
650 _nderror = ND_NOMEM;
651 (void) mutex_unlock(&xlist_lock);
652 return (0);
654 t->tr_name = strdup(name);
655 if (!t->tr_name) {
656 _nderror = ND_NOMEM;
657 free(t);
658 (void) mutex_unlock(&xlist_lock);
659 return (NULL);
662 t->tr_fd = dlopen(name, RTLD_LAZY);
663 if (t->tr_fd == NULL) {
664 _nderror = ND_OPEN;
665 goto error;
668 /* Resolve the getbyname symbol */
669 t->gbn = (struct nd_addrlist *(*)())dlsym(t->tr_fd,
670 "_netdir_getbyname");
671 if (!(t->gbn)) {
672 _nderror = ND_NOSYM;
673 goto error;
676 /* resolve the getbyaddr symbol */
677 t->gba = (struct nd_hostservlist *(*)())dlsym(t->tr_fd,
678 "_netdir_getbyaddr");
679 if (!(t->gba)) {
680 _nderror = ND_NOSYM;
681 goto error;
684 /* resolve the taddr2uaddr symbol */
685 t->t2u = (char *(*)())dlsym(t->tr_fd, "_taddr2uaddr");
686 if (!(t->t2u)) {
687 _nderror = ND_NOSYM;
688 goto error;
691 /* resolve the uaddr2taddr symbol */
692 t->u2t = (struct netbuf *(*)())dlsym(t->tr_fd, "_uaddr2taddr");
693 if (!(t->u2t)) {
694 _nderror = ND_NOSYM;
695 goto error;
698 /* resolve the netdir_options symbol */
699 t->opt = (int (*)())dlsym(t->tr_fd, "_netdir_options");
700 if (!(t->opt)) {
701 _nderror = ND_NOSYM;
702 goto error;
705 * Add this library to the list of loaded libraries.
707 *xlastp = malloc(sizeof (struct xlate_list));
708 if (*xlastp == NULL) {
709 _nderror = ND_NOMEM;
710 goto error;
712 (*xlastp)->library = strdup(name);
713 if ((*xlastp)->library == NULL) {
714 _nderror = ND_NOMEM;
715 free(*xlastp);
716 goto error;
718 (*xlastp)->next = NULL;
719 (void) mutex_unlock(&xlist_lock);
720 return (t);
721 error:
722 if (t->tr_fd != NULL)
723 (void) dlclose(t->tr_fd);
724 free(t->tr_name);
725 free(t);
726 (void) mutex_unlock(&xlist_lock);
727 return (NULL);
731 #define NDERR_BUFSZ 512
734 * This is a routine that returns a string related to the current
735 * error in _nderror.
737 char *
738 netdir_sperror(void)
740 static pthread_key_t nderrbuf_key = PTHREAD_ONCE_KEY_NP;
741 static char buf_main[NDERR_BUFSZ];
742 char *str;
743 char *dlerrstr;
745 str = thr_main()?
746 buf_main :
747 thr_get_storage(&nderrbuf_key, NDERR_BUFSZ, free);
748 if (str == NULL)
749 return (NULL);
750 dlerrstr = dlerror();
751 switch (_nderror) {
752 case ND_NOMEM :
753 (void) snprintf(str, NDERR_BUFSZ,
754 dgettext(__nsl_dom, "n2a: memory allocation failed"));
755 break;
756 case ND_OK :
757 (void) snprintf(str, NDERR_BUFSZ,
758 dgettext(__nsl_dom, "n2a: successful completion"));
759 break;
760 case ND_NOHOST :
761 (void) snprintf(str, NDERR_BUFSZ,
762 dgettext(__nsl_dom, "n2a: hostname not found"));
763 break;
764 case ND_NOSERV :
765 (void) snprintf(str, NDERR_BUFSZ,
766 dgettext(__nsl_dom, "n2a: service name not found"));
767 break;
768 case ND_NOSYM :
769 (void) snprintf(str, NDERR_BUFSZ, "%s : %s ",
770 dgettext(__nsl_dom,
771 "n2a: symbol missing in shared object"),
772 dlerrstr ? dlerrstr : " ");
773 break;
774 case ND_OPEN :
775 (void) snprintf(str, NDERR_BUFSZ, "%s - %s ",
776 dgettext(__nsl_dom, "n2a: couldn't open shared object"),
777 dlerrstr ? dlerrstr : " ");
778 break;
779 case ND_ACCESS :
780 (void) snprintf(str, NDERR_BUFSZ,
781 dgettext(__nsl_dom,
782 "n2a: access denied for shared object"));
783 break;
784 case ND_UKNWN :
785 (void) snprintf(str, NDERR_BUFSZ,
786 dgettext(__nsl_dom,
787 "n2a: attempt to free unknown object"));
788 break;
789 case ND_BADARG :
790 (void) snprintf(str, NDERR_BUFSZ,
791 dgettext(__nsl_dom,
792 "n2a: bad arguments passed to routine"));
793 break;
794 case ND_NOCTRL:
795 (void) snprintf(str, NDERR_BUFSZ,
796 dgettext(__nsl_dom, "n2a: unknown option passed"));
797 break;
798 case ND_FAILCTRL:
799 (void) snprintf(str, NDERR_BUFSZ,
800 dgettext(__nsl_dom, "n2a: control operation failed"));
801 break;
802 case ND_SYSTEM:
803 (void) snprintf(str, NDERR_BUFSZ, "%s: %s",
804 dgettext(__nsl_dom, "n2a: system error"),
805 strerror(errno));
806 break;
807 default :
808 (void) snprintf(str, NDERR_BUFSZ, "%s#%d",
809 dgettext(__nsl_dom, "n2a: unknown error "), _nderror);
810 break;
812 return (str);
816 * This is a routine that prints out strings related to the current
817 * error in _nderror. Like perror() it takes a string to print with a
818 * colon first.
820 void
821 netdir_perror(char *s)
823 char *err;
825 err = netdir_sperror();
826 (void) fprintf(stderr, "%s: %s\n", s, err ? err : "n2a: error");