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 #include "mt.h"
38 #include "../rpc/rpc_mt.h"		/* for MT declarations only */
39 #include <rpc/types.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <rpc/trace.h>
44 #include <netconfig.h>
45 #include <malloc.h>
46 #include <syslog.h>
47 #include "netcspace.h"
48 #include "nsl_stdio_prv.h"
49 
50 #define	FAILURE  (unsigned)(-1)
51 
52 /*
53  *	Local routines used by the library procedures
54  */
55 
56 static int blank();
57 static int comment();
58 static struct netconfig *fgetnetconfig();
59 static void netconfig_free();
60 static unsigned int getflag();
61 static char **getlookups();
62 static struct netconfig **getnetlist();
63 static unsigned int getnlookups();
64 static char *gettoken();
65 static unsigned int getvalue();
66 static void shift1left();
67 static void netlist_free();
68 static void free_entry();
69 static struct netconfig *netconfig_dup();
70 
71 /*
72  *	System V routines used by the library procedures.
73  */
74 extern char *getenv();
75 
76 /* messaging stuff. */
77 
78 extern const char __nsl_dom[];
79 extern char *dgettext(const char *, const char *);
80 
81 /*
82  *	Static global variables used by the library procedures:
83  *
84  *	netpp - points to the beginning of the list of netconfig
85  *		entries used by setnetconfig() and setnetpath().
86  *		Once netpp is initialized, that memory is *never*
87  *		released.  This was necessary to improve performance.
88  *
89  *	linenum - the current line number of the /etc/netconfig
90  *		  file (used for debugging and for nc_perror()).
91  *
92  *	fieldnum - the current field number of the current line
93  *		   of /etc/netconfig (used for debugging and for
94  *		   nc_perror()).
95  *
96  *	nc_error - the error condition encountered.
97  */
98 
99 static struct netconfig **netpp = NULL;
100 mutex_t netpp_mutex = DEFAULTMUTEX;
101 /*
102  * The following two variables are used by the /etc/netconfig parsing
103  * routines, which will always be executed once, and within the netpp_mutex.
104  * They are global to allow the nc_sperror routine to provide better
105  * information to the user about /etc/netconfig file problems.
106  */
107 static int linenum = 0;			/* "owned" by getnetlist() */
108 static int fieldnum = 0;		/* "owned" by fgetnetconfig() */
109 
110 
111 static int *
112 __nc_error()
113 {
114 	static pthread_key_t nc_error_key = 0;
115 	static int nc_error = NC_NOERROR;
116 	int *ret;
117 
118 	if (thr_main())
119 		return (&nc_error);
120 	ret = thr_get_storage(&nc_error_key, sizeof (int), free);
121 	/* if thr_get_storage fails we return the address of nc_error */
122 	return (ret ? ret : &nc_error);
123 }
124 #define	nc_error	(*(__nc_error()))
125 
126 /*
127  *	setnetconfig() has the effect of "initializing" the
128  *	network configuration database.   It reads in the
129  *	netcf entries (if not already read in).
130  */
131 
132 void *
133 setnetconfig()
134 {
135 	NCONF_HANDLE *retp;
136 
137 	trace1(TR_setnetconfig, 0);
138 	mutex_lock(&netpp_mutex);
139 	if ((netpp == NULL) && ((netpp = getnetlist()) == NULL)) {
140 		mutex_unlock(&netpp_mutex);
141 		trace1(TR_setnetconfig, 1);
142 		return (NULL);
143 	}
144 	mutex_unlock(&netpp_mutex);
145 	if ((retp = (NCONF_HANDLE *) malloc(sizeof (NCONF_HANDLE))) == NULL) {
146 		nc_error = NC_NOMEM;
147 		trace1(TR_setnetconfig, 1);
148 		return (NULL);
149 	}
150 	nc_error = NC_NOERROR;
151 	retp->nc_head = retp->nc_curr = netpp;
152 	trace1(TR_setnetconfig, 1);
153 	return ((void *)retp);
154 }
155 
156 /*
157  *	endnetconfig() frees up all data allocated by setnetconfig()
158  */
159 
160 int
161 endnetconfig(vdata)
162 void  *vdata;
163 {
164 	NCONF_HANDLE *nconf_handlep = (NCONF_HANDLE *)vdata;
165 
166 	trace1(TR_endnetconfig, 0);
167 	mutex_lock(&netpp_mutex);
168 	if (netpp == NULL || nconf_handlep == NULL) {
169 		nc_error = NC_NOSET;
170 		mutex_unlock(&netpp_mutex);
171 		trace1(TR_endnetconfig, 1);
172 		return (-1);
173 	}
174 	mutex_unlock(&netpp_mutex);
175 
176 	nc_error = NC_NOERROR;
177 	free((void *)nconf_handlep);
178 	trace1(TR_endnetconfig, 1);
179 	return (0);
180 }
181 
182 /*
183  *	getnetconfig() returns the current entry in the list
184  *	of netconfig structures.  It uses the nconf_handlep argument
185  *	to determine the current entry. If setnetconfig() was not
186  *	called previously to set up the list, return failure.
187  *      It also check if ipv6 interface is present(ipv6_present) and
188  *	skips udp6 & tcp6 entries if ipv6 is not supported.
189  */
190 
191 struct netconfig *
192 getnetconfig(vdata)
193 void *vdata;
194 {
195 	NCONF_HANDLE *nconf_handlep = (NCONF_HANDLE *)vdata;
196 	struct netconfig *retp;  /* holds the return value */
197 	int ipv6_present = -1;
198 
199 	trace1(TR_getnetconfig, 0);
200 	mutex_lock(&netpp_mutex);
201 	if ((netpp == NULL) || (nconf_handlep == NULL)) {
202 		nc_error = NC_NOSET;
203 		mutex_unlock(&netpp_mutex);
204 		trace1(TR_getnetconfig, 1);
205 		return (NULL);
206 	}
207 	mutex_unlock(&netpp_mutex);
208 	for (;;) {
209 		retp = *(nconf_handlep->nc_curr);
210 		if (retp && (strcmp(retp->nc_netid, "udp6") == 0 ||
211 		    strcmp(retp->nc_netid, "tcp6") == 0)) {
212 			if (ipv6_present == -1)
213 				ipv6_present = __can_use_af(AF_INET6);
214 			if (!ipv6_present) {
215 				++(nconf_handlep->nc_curr);
216 				continue;
217 			}
218 		}
219 		break;
220 	}
221 	if (retp != NULL) {
222 		++(nconf_handlep->nc_curr);
223 		nc_error = NC_NOERROR;
224 	} else {
225 		nc_error = NC_NOMOREENTRIES;
226 	}
227 	trace1(TR_getnetconfig, 1);
228 	return (retp);
229 }
230 
231 /*
232  *	getnetconfig() searches the netconfig database for a
233  *	given network id.  Returns a pointer to the netconfig
234  *	structure or a NULL if not found.
235  *      It also check if ipv6 interface is present(ipv6_present) and
236  *	skips udp6 & tcp6 entries if ipv6 is not supported.
237  */
238 
239 struct netconfig *
240 getnetconfigent(netid)
241 const char *netid;
242 {
243 	struct netconfig **tpp;
244 	int ipv6_present;
245 
246 	trace1(TR_getnetconfigent, 0);
247 	mutex_lock(&netpp_mutex);
248 	if ((netpp == NULL) && ((netpp = getnetlist()) == NULL)) {
249 		mutex_unlock(&netpp_mutex);
250 		trace1(TR_getnetconfigent, 1);
251 		return (NULL);
252 	}
253 	mutex_unlock(&netpp_mutex);
254 	for (tpp = netpp; *tpp; tpp++) {
255 		if (strcmp((*tpp)->nc_netid, netid) == 0) {
256 			if (*tpp && (strcmp((*tpp)->nc_netid, "udp6") == 0 ||
257 			    strcmp((*tpp)->nc_netid, "tcp6") == 0)) {
258 				ipv6_present = __can_use_af(AF_INET6);
259 				if (!ipv6_present) {
260 					nc_error = NC_NOTFOUND;
261 					trace1(TR_getnetconfigent, 1);
262 					return (NULL);
263 				}
264 			}
265 			trace1(TR_getnetconfigent, 1);
266 			return (netconfig_dup(*tpp));
267 		}
268 	}
269 	nc_error = NC_NOTFOUND;
270 	trace1(TR_getnetconfigent, 1);
271 	return (NULL);
272 }
273 
274 /*
275  *	freenetconfigent frees the data allocated by getnetconfigent()
276  */
277 
278 void
279 freenetconfigent(netp)
280 struct netconfig *netp;
281 {
282 	trace1(TR_freenetconfigent, 0);
283 	netconfig_free(netp);
284 	trace1(TR_freenetconfigent, 1);
285 }
286 
287 /*
288  *	getnetlist() reads the netconfig file and creates a
289  *	NULL-terminated list of entries.
290  *	Returns the pointer to the head of the list or a NULL
291  *	on failure.
292  */
293 
294 static struct netconfig **
295 getnetlist()
296 {
297 	char line[BUFSIZ];	/* holds each line of NETCONFIG */
298 	__NSL_FILE *fp;		/* file stream for NETCONFIG */
299 	struct netconfig **listpp; /* the beginning of the netconfig list */
300 	struct netconfig **tpp;	/* used to traverse the netconfig list */
301 	int count;		/* the number of entries in file */
302 
303 	trace1(TR_getnetlist, 0);
304 	if ((fp = __nsl_fopen(NETCONFIG, "r")) == NULL) {
305 		nc_error = NC_OPENFAIL;
306 		trace1(TR_getnetlist, 1);
307 		return (NULL);
308 	}
309 
310 	count = 0;
311 	while (__nsl_fgets(line, BUFSIZ, fp)) {
312 		if (!(blank(line) || comment(line))) {
313 			++count;
314 		}
315 	}
316 	__nsl_rewind(fp);
317 
318 	if (count == 0) {
319 		nc_error = NC_NOTFOUND;
320 		(void) __nsl_fclose(fp);
321 		trace1(TR_getnetlist, 1);
322 		return (NULL);
323 	}
324 	if ((listpp = (struct netconfig **)malloc((count + 1) *
325 	    sizeof (struct netconfig *))) == NULL) {
326 		nc_error = NC_NOMEM;
327 		(void) __nsl_fclose(fp);
328 		trace1(TR_getnetlist, 1);
329 		return (NULL);
330 	}
331 
332 	/*
333 	 *	The following loop fills in the list (loops until
334 	 *	fgetnetconfig() returns a NULL) and counts the
335 	 *	number of entries placed in the list.  Note that
336 	 *	when the loop is completed, the last entry in the
337 	 *	list will contain a NULL (signifying the end of
338 	 *	the list).
339 	 */
340 	linenum = 0;
341 	for (tpp = listpp; *tpp = fgetnetconfig(fp, NULL); tpp++)
342 		;
343 	(void) __nsl_fclose(fp);
344 
345 	if (nc_error != NC_NOMOREENTRIES) /* Something is screwed up */
346 		netlist_free(&listpp);
347 	trace1(TR_getnetlist, 1);
348 	return (listpp);
349 }
350 
351 /*
352  *	fgetnetconfig() parses a line of the netconfig file into
353  *	a netconfig structure.  It returns a pointer to the
354  *	structure of success and a NULL on failure or EOF.
355  */
356 
357 static struct netconfig *
358 fgetnetconfig(fp, netid)
359 __NSL_FILE *fp;
360 char *netid;
361 {
362 	char linep[BUFSIZ];	/* pointer to a line in the file */
363 	struct netconfig *netconfigp; /* holds the new netconfig structure */
364 	char  *tok1, *tok2, *tok3; /* holds a token from the line */
365 	char  *retvalp;		/* the return value of fgets() */
366 	char *entnetid;		/* netid for the current entry */
367 
368 	trace1(TR_fgetnetconfig, 0);
369 	/* skip past blank lines and comments. */
370 	while (retvalp = __nsl_fgets(linep, BUFSIZ, fp)) {
371 		linenum++;
372 		if (!(blank(linep) || comment(linep))) {
373 			break;
374 		}
375 		retvalp = NULL;
376 	}
377 	if (retvalp == NULL) {
378 		nc_error = NC_NOMOREENTRIES;
379 		trace1(TR_fgetnetconfig, 1);
380 		return (NULL);
381 	}
382 	fieldnum = 0;
383 	if ((entnetid = gettoken(linep, FALSE)) == NULL) {
384 		nc_error = NC_BADLINE;
385 		trace1(TR_fgetnetconfig, 1);
386 		return (NULL);
387 	}
388 	if (netid && (strcmp(netid, entnetid) != 0)) {
389 		free(entnetid);
390 		nc_error = NC_NOTFOUND;
391 		trace1(TR_fgetnetconfig, 1);
392 		return (NULL);
393 	}
394 	if ((netconfigp = (struct netconfig *)
395 		calloc(1, sizeof (struct netconfig))) == NULL) {
396 		free(entnetid);
397 		nc_error = NC_NOMEM;
398 		trace1(TR_fgetnetconfig, 1);
399 		return (NULL);
400 	}
401 
402 	tok1 = tok2 = tok3 = NULL;
403 	netconfigp->nc_netid = entnetid;
404 	if (((tok1 = gettoken(NULL, FALSE)) == NULL) ||
405 	    ((netconfigp->nc_semantics =
406 		getvalue(tok1, nc_semantics)) == FAILURE) ||
407 	    ((tok2 = gettoken(NULL, FALSE)) == NULL) ||
408 	    ((netconfigp->nc_flag = getflag(tok2)) == FAILURE) ||
409 	    ((netconfigp->nc_protofmly = gettoken(NULL, FALSE)) == NULL) ||
410 	    ((netconfigp->nc_proto = gettoken(NULL, FALSE)) == NULL) ||
411 	    ((netconfigp->nc_device = gettoken(NULL, FALSE)) == NULL) ||
412 	    ((tok3 = gettoken(NULL, TRUE)) == NULL) ||
413 	    (((netconfigp->nc_nlookups = getnlookups(tok3)) != 0) &&
414 		((netconfigp->nc_lookups = getlookups(tok3)) == NULL))) {
415 		netconfig_free(netconfigp);
416 		nc_error = NC_BADLINE;
417 		netconfigp = NULL;
418 	}
419 	free(tok1);
420 	free(tok2);
421 	free(tok3);
422 	trace1(TR_fgetnetconfig, 1);
423 	return (netconfigp);
424 }
425 
426 /*
427  *	setnetpath() has the effect of "initializing" the
428  *	NETPATH variable.  It reads in the netcf entries (if not
429  *	already read in), creates a list corresponding to the entries
430  *	in the NETPATH variable (or the "visible" entries og netconfig
431  *	if NETPATH is not set).
432  */
433 
434 void *
435 setnetpath()
436 {
437 	int count;		    /* the number of entries in NETPATH	    */
438 	char valid_netpath[BUFSIZ]; /* holds the valid entries if NETPATH   */
439 	char templine[BUFSIZ];	    /* has value of NETPATH when scanning   */
440 	struct netconfig **curr_pp; /* scans the list from NETPATH	    */
441 	struct netconfig **tpp;	    /* scans the list from netconfig file   */
442 	struct netconfig **rnetpp;  /* the list of entries from NETPATH	    */
443 	char *netpath;		    /* value of NETPATH from environment    */
444 	char *netid;		    /* holds a component of NETPATH	    */
445 	char *tp;		    /* used to scan NETPATH string	    */
446 	NCONF_HANDLE *retp;	    /* the return value			    */
447 
448 	trace1(TR_setnetpath, 0);
449 	/*
450 	 *	Read in the netconfig database if not already read in
451 	 */
452 	mutex_lock(&netpp_mutex);
453 	if ((netpp == NULL) && ((netpp = getnetlist()) == NULL)) {
454 		mutex_unlock(&netpp_mutex);
455 		trace1(TR_setnetpath, 1);
456 		return (NULL);
457 	}
458 	mutex_unlock(&netpp_mutex);
459 
460 	if ((retp = (NCONF_HANDLE *) malloc(sizeof (NCONF_HANDLE))) == NULL) {
461 		nc_error = NC_NOMEM;
462 		trace1(TR_setnetpath, 1);
463 		return (NULL);
464 	}
465 
466 	/*
467 	 *	Get the valid entries of the NETPATH variable (and
468 	 *	count the number of entries while doing it).
469 	 *
470 	 *	This is done every time the procedure is called just
471 	 *	in case NETPATH has changed from call to call.
472 	 *
473 	 * 	If NETPATH is too long, we ignore it altogether as
474 	 *	it can only be a buffer overflow attack.
475 	 *	Since we add one colon for each entry, but colons only
476 	 *	need to exist between entries, we have to subtract one.
477 	 */
478 	count = 0;
479 	valid_netpath[0] = '\0';
480 	if ((netpath = getenv(NETPATH)) == NULL ||
481 	    strlen(netpath) >= sizeof (templine) - 1) {
482 		/*
483 		 *	If NETPATH variable is not set or invalid,
484 		 *	the valid NETPATH consist of all "visible"
485 		 *	netids from the netconfig database.
486 		 */
487 
488 		for (tpp = netpp; *tpp; tpp++) {
489 			if ((*tpp)->nc_flag & NC_VISIBLE) {
490 				(void) strcat(valid_netpath, (*tpp)->nc_netid);
491 				(void) strcat(valid_netpath, ":");
492 				count++;
493 			}
494 		}
495 	} else {
496 
497 		/*
498 		 *	Copy the value of NETPATH (since '\0's will be
499 		 *	put into the string) and create the valid NETPATH
500 		 *	(by throwing away all netids not in the database).
501 		 *	If an entry appears more than one, it *will* be
502 		 *	listed twice in the list of valid netpath entries.
503 		 */
504 
505 		(void) strcpy(templine, netpath);
506 		tp = templine;
507 
508 		while (*tp) {
509 			/* Skip all leading ':'s */
510 			while (*tp && *tp == ':')
511 				tp++;
512 			if (*tp == NULL)
513 				break;  /* last one */
514 			netid = tp;
515 			while (*tp && *tp != ':')
516 				tp++;
517 			if (*tp)
518 				*tp++ = '\0'; /* isolate netid */
519 
520 			for (tpp = netpp; *tpp; tpp++) {
521 				if (strcmp(netid, (*tpp)->nc_netid) == 0) {
522 					(void) strcat(valid_netpath,
523 						(*tpp)->nc_netid);
524 					(void) strcat(valid_netpath, ":");
525 					count++;
526 					break;
527 				}
528 			}
529 		}
530 	}
531 
532 	/* Get space to hold the valid list (+1 for the NULL) */
533 
534 	if ((rnetpp = (struct netconfig **)malloc((count + 1) *
535 			sizeof (struct netconfig *))) == NULL) {
536 		free((void *) retp);
537 		nc_error = NC_NOMEM;
538 		trace1(TR_setnetpath, 1);
539 		return (NULL);
540 	}
541 
542 	/*
543 	 *	Populate the NETPATH list, ending it with a NULL.
544 	 *	Each entry in the list points to the structure in the
545 	 *	"netpp" list (the entry must exist in the list, otherwise
546 	 *	it wouldn't appear in valid_netpath[]).
547 	 */
548 
549 	curr_pp = rnetpp;
550 	netid = tp = valid_netpath;
551 	while (*tp) {
552 		netid = tp;
553 		while (*tp && *tp != ':')
554 			tp++;
555 		if (*tp)
556 			*tp++ = '\0';
557 		for (tpp = netpp; *tpp; tpp++) {
558 			if (strcmp(netid, (*tpp)->nc_netid) == 0) {
559 				*curr_pp++ = *tpp;
560 				break;
561 			}
562 		}
563 	}
564 	*curr_pp = NULL;
565 
566 	retp->nc_curr = retp->nc_head = rnetpp;
567 	trace1(TR_setnetpath, 1);
568 	return ((void *)retp);
569 }
570 
571 /*
572  *	endnetpath() frees up all of the memory allocated by setnetpath().
573  *	It returns -1 (error) if setnetpath was never called.
574  */
575 
576 int
577 endnetpath(vdata)
578 void *vdata;
579 {
580 	/* The argument is really a NCONF_HANDLE;  cast it here */
581 	NCONF_HANDLE *nconf_handlep = (NCONF_HANDLE *)vdata;
582 
583 	trace1(TR_endnetpath, 0);
584 	mutex_lock(&netpp_mutex);
585 	if (netpp == NULL || nconf_handlep == NULL) {
586 		nc_error = NC_NOSET;
587 		mutex_unlock(&netpp_mutex);
588 		trace1(TR_endnetpath, 1);
589 		return (-1);
590 	}
591 	mutex_unlock(&netpp_mutex);
592 
593 	free(nconf_handlep->nc_head);
594 	free(nconf_handlep);
595 	trace1(TR_endnetpath, 1);
596 	return (0);
597 }
598 
599 /*
600  *	getnetpath() returns the current entry in the list
601  *	from the NETPATH variable.  If setnetpath() was not called
602  *	previously to set up the list, return NULL.
603  */
604 
605 struct netconfig *
606 getnetpath(vdata)
607 void *vdata;
608 {
609 	/* The argument is really a NCONF_HANDLE;  cast it here */
610 	NCONF_HANDLE *nconf_handlep = (NCONF_HANDLE *)vdata;
611 	struct netconfig *retp;  /* holds the return value */
612 	int ipv6_present = -1;
613 
614 	trace1(TR_getnetpath, 0);
615 	mutex_lock(&netpp_mutex);
616 	if (netpp == NULL) {
617 		nc_error = NC_NOSET;
618 		mutex_unlock(&netpp_mutex);
619 		trace1(TR_getnetpath, 1);
620 		return (NULL);
621 	}
622 	mutex_unlock(&netpp_mutex);
623 	for (;;) {
624 		retp = *(nconf_handlep->nc_curr);
625 		if (retp && (strcmp(retp->nc_netid, "udp6") == 0 ||
626 		    strcmp(retp->nc_netid, "tcp6") == 0)) {
627 			if (ipv6_present == -1)
628 				ipv6_present = __can_use_af(AF_INET6);
629 			if (!ipv6_present) {
630 				++(nconf_handlep->nc_curr);
631 				continue;
632 			}
633 		}
634 		break;
635 	}
636 	if (retp) {
637 		++(nconf_handlep->nc_curr);
638 		nc_error = NC_NOERROR;
639 	} else {
640 		nc_error = NC_NOMOREENTRIES;
641 	}
642 
643 	trace1(TR_getnetpath, 1);
644 	return (retp);
645 }
646 
647 /*
648  *	blank() returns true if the line is a blank line, 0 otherwise
649  */
650 
651 static int
652 blank(cp)
653 char *cp;
654 {
655 	trace1(TR_blank, 0);
656 	while (*cp && isspace(*cp)) {
657 		cp++;
658 	}
659 	trace1(TR_blank, 1);
660 	return (*cp == '\0');
661 }
662 
663 /*
664  *	comment() returns true if the line is a comment, 0 otherwise.
665  */
666 
667 static int
668 comment(cp)
669 char *cp;
670 {
671 	trace1(TR_comment, 0);
672 	while (*cp && isspace(*cp)) {
673 		cp++;
674 	}
675 	trace1(TR_comment, 1);
676 	return (*cp == '#');
677 }
678 
679 /*
680  *	getvalue() searches for the given string in the given array,
681  *	and return the integer value associated with the string.
682  */
683 
684 static unsigned int
685 getvalue(cp, nc_data)
686 char *cp;
687 struct nc_data nc_data[];
688 {
689 	int i;	/* used to index through the given struct nc_data array */
690 
691 	trace1(TR_getvalue, 0);
692 	for (i = 0; nc_data[i].string; i++) {
693 		if (strcmp(nc_data[i].string, cp) == 0) {
694 			break;
695 		}
696 	}
697 	trace1(TR_getvalue, 1);
698 	return (nc_data[i].value);
699 }
700 
701 /*
702  *	getflag() creates a bitmap of the one-character flags in
703  *	the given string.  It uses nc_flags array to get the values.
704  */
705 
706 static unsigned int
707 getflag(cp)
708 char *cp;
709 {
710 	int i;			/* indexs through the nc_flag array */
711 	unsigned int mask = 0; /* holds bitmask of flags */
712 
713 	trace1(TR_getflag, 0);
714 	while (*cp) {
715 		for (i = 0; nc_flag[i].string; i++) {
716 			if (*nc_flag[i].string == *cp) {
717 				mask |= nc_flag[i].value;
718 				break;
719 			}
720 		}
721 		cp++;
722 	}
723 	trace1(TR_getflag, 1);
724 	return (mask);
725 }
726 
727 /*
728  *	getlookups() creates and returns an array of string representing
729  *	the directory lookup libraries, given as a comma-seperated list
730  *	in the argument "cp".
731  */
732 
733 static char **
734 getlookups(cp)
735 char *cp;
736 {
737 	unsigned int num;	/* holds the number of entries in the list   */
738 	char **listpp;		/* the beginning of the list of dir routines */
739 	char **tpp;		/* traverses the list, populating it */
740 	char *start;
741 
742 	trace1(TR_getlookups, 0);
743 	num = getnlookups(cp);
744 	if (num == 0) {
745 		trace1(TR_getlookups, 1);
746 		return (NULL);
747 	}
748 	if ((listpp = (char **)malloc((num + 1) * sizeof (char *))) == NULL) {
749 		trace1(TR_getlookups, 1);
750 		return (NULL);
751 	}
752 
753 	tpp = listpp;
754 	while (num--) {
755 		start  = cp;
756 
757 		/*
758 		 *	Traverse the string looking for the next entry
759 		 *	of the list (i.e, where the ',' or end of the
760 		 *	string appears).  If a "\" is found, shift the
761 		 *	token over 1 to the left (taking the next char
762 		 *	literally).
763 		 */
764 
765 		while (*cp && *cp != ',') {
766 			if (*cp == '\\' && *(cp + 1)) {
767 				shift1left(cp);
768 			}
769 			cp++;
770 		}
771 		if (*cp)
772 			*cp++ = '\0';
773 		if ((*tpp++ = strdup(start)) == NULL) {
774 			for (tpp = listpp; *tpp; tpp++)
775 				free(*tpp);
776 			free(listpp);
777 			trace1(TR_getlookups, 1);
778 			return (NULL);
779 		}
780 	}
781 	*tpp = NULL;
782 	trace1(TR_getlookups, 1);
783 	return (listpp);
784 }
785 
786 /*
787  *	getnlookups() returns the number of entries in a comma-separated
788  *	string of tokens.  A "-" means no strings are present.
789  */
790 
791 static unsigned int
792 getnlookups(cp)
793 char *cp;
794 {
795 	unsigned int count;	/* the number of tokens in the string */
796 
797 	trace1(TR_getnlookups, 0);
798 	if (strcmp(cp, "-") == 0) {
799 		trace1(TR_getnlookups, 1);
800 		return (0);
801 	}
802 
803 	count = 1;
804 	while (*cp) {
805 		if (*cp == ',') {
806 			count++;
807 		}
808 
809 		/*
810 		 *	If a "\" is in the string, take the next character
811 		 *	literally.  Onlly skip the character if "\" is
812 		 *	not the last character of the token.
813 		 */
814 		if (*cp == '\\' && *(cp + 1)) {
815 			cp++;
816 		}
817 		cp++;
818 	}
819 	trace1(TR_getnlookups, 1);
820 	return (count);
821 }
822 
823 /*
824  *	gettoken() behaves much like strtok(), except that
825  *	it knows about escaped space characters (i.e., space characters
826  *	preceeded by a '\' are taken literally).
827  */
828 
829 static char *
830 gettoken(cp, skip)
831 char	*cp;
832 int skip;
833 {
834 	static char	*savep;	/* the place where we left off    */
835 	char	*p;		/* the beginning of the new token */
836 	char	*retp;		/* the token to be returned	  */
837 
838 	trace1(TR_gettoken, 0);
839 	fieldnum++;
840 
841 	/* Determine if first or subsequent call  */
842 	p = (cp == NULL)? savep: cp;
843 
844 	/* Return if no tokens remain.  */
845 	if (p == 0) {
846 		trace1(TR_gettoken, 1);
847 		return (NULL);
848 	}
849 
850 	while (isspace(*p))
851 		p++;
852 
853 	if (*p == '\0') {
854 		trace1(TR_gettoken, 1);
855 		return (NULL);
856 	}
857 
858 	/*
859 	 *	Save the location of the token and then skip past it
860 	 */
861 
862 	retp = p;
863 	while (*p) {
864 		if (isspace(*p))
865 			if (skip == TRUE) {
866 				shift1left(p);
867 				continue;
868 			} else
869 				break;
870 		/*
871 		 *	Only process the escape of the space seperator;
872 		 *	since the token may contain other separators,
873 		 *	let the other routines handle the escape of
874 		 *	specific characters in the token.
875 		 */
876 
877 		if (*p == '\\' && *(p + 1) != '\n' && isspace(*(p + 1))) {
878 			shift1left(p);
879 		}
880 		p++;
881 	}
882 	if (*p == '\0') {
883 		savep = 0;	/* indicate this is last token */
884 	} else {
885 		*p = '\0';
886 		savep = ++p;
887 	}
888 	trace1(TR_gettoken, 1);
889 	return (strdup(retp));
890 }
891 
892 /*
893  *	shift1left() moves all characters in the string over 1 to
894  *	the left.
895  */
896 
897 static void
898 shift1left(p)
899 char *p;
900 {
901 	trace1(TR_shift1left, 0);
902 	for (; *p; p++)
903 		*p = *(p + 1);
904 	trace1(TR_shift1left, 1);
905 }
906 
907 char *
908 nc_sperror()
909 {
910 	static char buf_main[BUFSIZ];
911 	static pthread_key_t perror_key;
912 	char *retstr = thr_main()?
913 		buf_main :
914 		thr_get_storage(&perror_key, BUFSIZ, free);
915 
916 	trace1(TR_nc_sperror, 0);
917 
918 	if (retstr == NULL) {
919 		syslog(LOG_WARNING,
920 		"nc_sperror: malloc failed when trying to create buffer\n");
921 		return (NULL);
922 	}
923 
924 	switch (nc_error) {
925 	case NC_NOERROR:
926 		(void) strlcpy(retstr, dgettext(__nsl_dom, "no error"), BUFSIZ);
927 		break;
928 	case NC_NOMEM:
929 		(void) strlcpy(retstr, dgettext(__nsl_dom, "out of memory"),
930 		    BUFSIZ);
931 		break;
932 	case NC_NOSET:
933 		(void) strlcpy(retstr, dgettext(__nsl_dom,
934 		    "routine called before calling \
935 		    setnetpath() or setnetconfig()"), BUFSIZ);
936 		break;
937 	case NC_OPENFAIL:
938 		(void) strlcpy(retstr,
939 			dgettext(__nsl_dom, "cannot open /etc/netconfig"),
940 			BUFSIZ);
941 		break;
942 	case NC_BADLINE:
943 		(void) snprintf(retstr, BUFSIZ, dgettext(__nsl_dom,
944 			"error in /etc/netconfig: field %d of line %d\n"),
945 				fieldnum, linenum);
946 		break;
947 	case NC_NOTFOUND:
948 		(void) snprintf(retstr, BUFSIZ,
949 			dgettext(__nsl_dom,
950 				"netid not found in /etc/netconfig"));
951 		break;
952 	case NC_NOMOREENTRIES:
953 		(void) snprintf(retstr, BUFSIZ,
954 			dgettext(__nsl_dom,
955 				"no more entries in /etc/netconfig"));
956 		break;
957 	default:
958 		(void) strlcpy(retstr, dgettext(__nsl_dom, "unknown error"),
959 		    BUFSIZ);
960 		break;
961 	}
962 	trace1(TR_nc_sperror, 1);
963 	return (retstr);
964 }
965 
966 void
967 nc_perror(const char *string)
968 {
969 	trace1(TR_nc_perror, 0);
970 	if (string)
971 		fprintf(stderr, "%s: %s\n", string, nc_sperror());
972 	else
973 		fprintf(stderr, "%s\n", nc_sperror());
974 	trace1(TR_nc_perror, 1);
975 }
976 
977 static void
978 netlist_free(netppp)
979 	struct netconfig ***netppp;
980 {
981 	struct netconfig **tpp;
982 
983 	trace1(TR_netlist_free, 0);
984 	for (tpp = *netppp; *tpp; tpp++) {
985 		netconfig_free(*tpp);
986 	}
987 	free(*netppp);
988 	*netppp = NULL;
989 	trace1(TR_netlist_free, 1);
990 }
991 
992 static void
993 netconfig_free(netconfigp)
994 struct netconfig *netconfigp;
995 {
996 	int i;
997 
998 	trace1(TR_netconfig_free, 0);
999 	if (netconfigp == NULL) {
1000 		trace1(TR_netconfig_free, 1);
1001 		return;
1002 	}
1003 	free_entry(netconfigp->nc_netid);
1004 	free_entry(netconfigp->nc_protofmly);
1005 	free_entry(netconfigp->nc_proto);
1006 	free_entry(netconfigp->nc_device);
1007 	if (netconfigp->nc_lookups)
1008 		for (i = 0; i < netconfigp->nc_nlookups; i++)
1009 			free_entry(netconfigp->nc_lookups[i]);
1010 	free_entry(netconfigp->nc_lookups);
1011 	free(netconfigp);
1012 	trace1(TR_netconfig_free, 1);
1013 }
1014 
1015 static struct netconfig *
1016 netconfig_dup(netconfigp)
1017 struct netconfig *netconfigp;
1018 {
1019 	struct netconfig *nconf;
1020 	int i;
1021 
1022 	trace1(TR_netconfig_dup, 0);
1023 	nconf = (struct netconfig *)calloc(1, sizeof (struct netconfig));
1024 	if (nconf == NULL) {
1025 		trace1(TR_netconfig_dup, 1);
1026 		nc_error = NC_NOMEM;
1027 		return (NULL);
1028 	}
1029 	nconf->nc_netid = strdup(netconfigp->nc_netid);
1030 	nconf->nc_protofmly = strdup(netconfigp->nc_protofmly);
1031 	nconf->nc_proto = strdup(netconfigp->nc_proto);
1032 	nconf->nc_device = strdup(netconfigp->nc_device);
1033 	nconf->nc_lookups = (char **)malloc((netconfigp->nc_nlookups + 1)
1034 					* sizeof (char *));
1035 	if (!(nconf->nc_lookups && nconf->nc_netid &&
1036 		nconf->nc_protofmly && nconf->nc_proto &&
1037 		nconf->nc_device)) {
1038 		nc_error = NC_NOMEM;
1039 		netconfig_free(nconf);
1040 		trace1(TR_netconfig_dup, 1);
1041 		return (NULL);
1042 	}
1043 
1044 	for (i = 0; i < netconfigp->nc_nlookups; i++) {
1045 		nconf->nc_lookups[i] = strdup(netconfigp->nc_lookups[i]);
1046 		if (nconf->nc_lookups[i] == NULL) {
1047 			nconf->nc_nlookups = i;
1048 			netconfig_free(nconf);
1049 			nc_error = NC_NOMEM;
1050 			trace1(TR_netconfig_dup, 1);
1051 			return (NULL);
1052 		}
1053 	}
1054 	nconf->nc_lookups[i] = NULL;
1055 	nconf->nc_nlookups = netconfigp->nc_nlookups;
1056 	nconf->nc_flag = netconfigp->nc_flag;
1057 	nconf->nc_semantics = netconfigp->nc_semantics;
1058 	trace1(TR_netconfig_dup, 1);
1059 	return (nconf);
1060 }
1061 
1062 static void
1063 free_entry(foo)
1064 	void *foo;
1065 {
1066 	trace1(TR_free_entry, 0);
1067 	if (foo)
1068 		free(foo);
1069 	trace1(TR_free_entry, 1);
1070 }
1071