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