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