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