xref: /freebsd/lib/libc/rpc/getnetconfig.c (revision f5463265955b829775bbb32e1fd0bc11dafc36ce)
1 /*	$NetBSD: getnetconfig.c,v 1.3 2000/07/06 03:10:34 christos Exp $	*/
2 
3 /*-
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  * Copyright (c) 2009, Sun Microsystems, Inc.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions are met:
11  * - Redistributions of source code must retain the above copyright notice,
12  *   this list of conditions and the following disclaimer.
13  * - Redistributions in binary form must reproduce the above copyright notice,
14  *   this list of conditions and the following disclaimer in the documentation
15  *   and/or other materials provided with the distribution.
16  * - Neither the name of Sun Microsystems, Inc. nor the names of its
17  *   contributors may be used to endorse or promote products derived
18  *   from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * Copyright (c) 1989 by Sun Microsystems, Inc.
35  */
36 
37 #include "namespace.h"
38 #include "reentrant.h"
39 #include <stdio.h>
40 #include <errno.h>
41 #include <netconfig.h>
42 #include <stddef.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <rpc/rpc.h>
46 #include <unistd.h>
47 #include "un-namespace.h"
48 #include "rpc_com.h"
49 
50 /*
51  * The five library routines in this file provide application access to the
52  * system network configuration database, /etc/netconfig.  In addition to the
53  * netconfig database and the routines for accessing it, the environment
54  * variable NETPATH and its corresponding routines in getnetpath.c may also be
55  * used to specify the network transport to be used.
56  */
57 
58 
59 /*
60  * netconfig errors
61  */
62 
63 #define NC_NONETCONFIG	ENOENT
64 #define NC_NOMEM	ENOMEM
65 #define NC_NOTINIT	EINVAL	    /* setnetconfig was not called first */
66 #define NC_BADFILE	EBADF	    /* format for netconfig file is bad */
67 #define NC_NOTFOUND	ENOPROTOOPT /* specified netid was not found */
68 
69 /*
70  * semantics as strings (should be in netconfig.h)
71  */
72 #define NC_TPI_CLTS_S	    "tpi_clts"
73 #define	NC_TPI_COTS_S	    "tpi_cots"
74 #define	NC_TPI_COTS_ORD_S   "tpi_cots_ord"
75 #define	NC_TPI_RAW_S        "tpi_raw"
76 
77 /*
78  * flags as characters (also should be in netconfig.h)
79  */
80 #define	NC_NOFLAG_C	'-'
81 #define	NC_VISIBLE_C	'v'
82 #define	NC_BROADCAST_C	'b'
83 
84 /*
85  * Character used to indicate there is no name-to-address lookup library
86  */
87 #define NC_NOLOOKUP	"-"
88 
89 static const char * const _nc_errors[] = {
90     "Netconfig database not found",
91     "Not enough memory",
92     "Not initialized",
93     "Netconfig database has invalid format",
94     "Netid not found in netconfig database"
95 };
96 
97 struct netconfig_info {
98     int		eof;	/* all entries has been read */
99     int		ref;	/* # of times setnetconfig() has been called */
100     struct netconfig_list	*head;	/* head of the list */
101     struct netconfig_list	*tail;	/* last of the list */
102 };
103 
104 struct netconfig_list {
105     char			*linep;	/* hold line read from netconfig */
106     struct netconfig		*ncp;
107     struct netconfig_list	*next;
108 };
109 
110 struct netconfig_vars {
111     int   valid;	/* token that indicates a valid netconfig_vars */
112     int   flag;		/* first time flag */
113     struct netconfig_list *nc_configs;  /* pointer to the current netconfig entry */
114 };
115 
116 #define NC_VALID	0xfeed
117 #define NC_STORAGE	0xf00d
118 #define NC_INVALID	0
119 
120 
121 static int *__nc_error(void);
122 static int parse_ncp(char *, struct netconfig *);
123 static struct netconfig *dup_ncp(struct netconfig *);
124 
125 
126 static FILE *nc_file;		/* for netconfig db */
127 static mutex_t nc_file_lock = MUTEX_INITIALIZER;
128 
129 static struct netconfig_info	ni = { 0, 0, NULL, NULL};
130 static mutex_t ni_lock = MUTEX_INITIALIZER;
131 
132 static thread_key_t nc_key;
133 static once_t nc_once = ONCE_INITIALIZER;
134 static int nc_key_error;
135 
136 static void
137 nc_key_init(void)
138 {
139 
140 	nc_key_error = thr_keycreate(&nc_key, free);
141 }
142 
143 #define MAXNETCONFIGLINE    1000
144 
145 static int *
146 __nc_error(void)
147 {
148 	static int nc_error = 0;
149 	int *nc_addr;
150 
151 	/*
152 	 * Use the static `nc_error' if we are the main thread
153 	 * (including non-threaded programs), or if an allocation
154 	 * fails.
155 	 */
156 	if (thr_main())
157 		return (&nc_error);
158 	if (thr_once(&nc_once, nc_key_init) != 0 || nc_key_error != 0)
159 		return (&nc_error);
160 	if ((nc_addr = (int *)thr_getspecific(nc_key)) == NULL) {
161 		nc_addr = (int *)malloc(sizeof (int));
162 		if (thr_setspecific(nc_key, (void *) nc_addr) != 0) {
163 			free(nc_addr);
164 			return (&nc_error);
165 		}
166 		*nc_addr = 0;
167 	}
168 	return (nc_addr);
169 }
170 
171 #define nc_error        (*(__nc_error()))
172 /*
173  * A call to setnetconfig() establishes a /etc/netconfig "session".  A session
174  * "handle" is returned on a successful call.  At the start of a session (after
175  * a call to setnetconfig()) searches through the /etc/netconfig database will
176  * proceed from the start of the file.  The session handle must be passed to
177  * getnetconfig() to parse the file.  Each call to getnetconfig() using the
178  * current handle will process one subsequent entry in /etc/netconfig.
179  * setnetconfig() must be called before the first call to getnetconfig().
180  * (Handles are used to allow for nested calls to setnetpath()).
181  *
182  * A new session is established with each call to setnetconfig(), with a new
183  * handle being returned on each call.  Previously established sessions remain
184  * active until endnetconfig() is called with that session's handle as an
185  * argument.
186  *
187  * setnetconfig() need *not* be called before a call to getnetconfigent().
188  * setnetconfig() returns a NULL pointer on failure (for example, if
189  * the netconfig database is not present).
190  */
191 void *
192 setnetconfig(void)
193 {
194     struct netconfig_vars *nc_vars;
195 
196     if ((nc_vars = (struct netconfig_vars *)malloc(sizeof
197 		(struct netconfig_vars))) == NULL) {
198 	return(NULL);
199     }
200 
201     /*
202      * For multiple calls, i.e. nc_file is not NULL, we just return the
203      * handle without reopening the netconfig db.
204      */
205     mutex_lock(&ni_lock);
206     ni.ref++;
207     mutex_unlock(&ni_lock);
208 
209     mutex_lock(&nc_file_lock);
210     if ((nc_file != NULL) || (nc_file = fopen(NETCONFIG, "r")) != NULL) {
211 	nc_vars->valid = NC_VALID;
212 	nc_vars->flag = 0;
213 	nc_vars->nc_configs = ni.head;
214 	mutex_unlock(&nc_file_lock);
215 	return ((void *)nc_vars);
216     }
217     mutex_unlock(&nc_file_lock);
218 
219     mutex_lock(&ni_lock);
220     ni.ref--;
221     mutex_unlock(&ni_lock);
222 
223     nc_error = NC_NONETCONFIG;
224     free(nc_vars);
225     return (NULL);
226 }
227 
228 
229 /*
230  * When first called, getnetconfig() returns a pointer to the first entry in
231  * the netconfig database, formatted as a struct netconfig.  On each subsequent
232  * call, getnetconfig() returns a pointer to the next entry in the database.
233  * getnetconfig() can thus be used to search the entire netconfig file.
234  * getnetconfig() returns NULL at end of file.
235  */
236 
237 struct netconfig *
238 getnetconfig(void *handlep)
239 {
240     struct netconfig_vars *ncp = (struct netconfig_vars *)handlep;
241     char *stringp;		/* tmp string pointer */
242     struct netconfig_list	*list;
243     struct netconfig *np;
244     struct netconfig *result;
245 
246     /*
247      * Verify that handle is valid
248      */
249     mutex_lock(&nc_file_lock);
250     if (ncp == NULL || nc_file == NULL) {
251 	nc_error = NC_NOTINIT;
252 	mutex_unlock(&nc_file_lock);
253 	return (NULL);
254     }
255     mutex_unlock(&nc_file_lock);
256 
257     switch (ncp->valid) {
258     case NC_VALID:
259 	/*
260 	 * If entry has already been read into the list,
261 	 * we return the entry in the linked list.
262 	 * If this is the first time call, check if there are any entries in
263 	 * linked list.  If no entries, we need to read the netconfig db.
264 	 * If we have been here and the next entry is there, we just return
265 	 * it.
266 	 */
267 	if (ncp->flag == 0) {	/* first time */
268 	    ncp->flag = 1;
269 	    mutex_lock(&ni_lock);
270 	    ncp->nc_configs = ni.head;
271 	    mutex_unlock(&ni_lock);
272 	    if (ncp->nc_configs != NULL)	/* entry already exist */
273 		return(ncp->nc_configs->ncp);
274 	}
275 	else if (ncp->nc_configs != NULL && ncp->nc_configs->next != NULL) {
276 	    ncp->nc_configs = ncp->nc_configs->next;
277 	    return(ncp->nc_configs->ncp);
278 	}
279 
280 	/*
281 	 * If we cannot find the entry in the list and is end of file,
282 	 * we give up.
283 	 */
284 	mutex_lock(&ni_lock);
285 	if (ni.eof == 1) {
286 		mutex_unlock(&ni_lock);
287 		return(NULL);
288 	}
289 	mutex_unlock(&ni_lock);
290 
291 	break;
292     default:
293 	nc_error = NC_NOTINIT;
294 	return (NULL);
295     }
296 
297     stringp = (char *) malloc(MAXNETCONFIGLINE);
298     if (stringp == NULL)
299     	return (NULL);
300 
301 #ifdef MEM_CHK
302     if (malloc_verify() == 0) {
303 	fprintf(stderr, "memory heap corrupted in getnetconfig\n");
304 	exit(1);
305     }
306 #endif
307 
308     /*
309      * Read a line from netconfig file.
310      */
311     mutex_lock(&nc_file_lock);
312     do {
313 	if (fgets(stringp, MAXNETCONFIGLINE, nc_file) == NULL) {
314 	    free(stringp);
315 	    mutex_lock(&ni_lock);
316 	    ni.eof = 1;
317 	    mutex_unlock(&ni_lock);
318 	    mutex_unlock(&nc_file_lock);
319 	    return (NULL);
320         }
321     } while (*stringp == '#');
322     mutex_unlock(&nc_file_lock);
323 
324     list = (struct netconfig_list *) malloc(sizeof (struct netconfig_list));
325     if (list == NULL) {
326     	free(stringp);
327     	return(NULL);
328     }
329     np = (struct netconfig *) malloc(sizeof (struct netconfig));
330     if (np == NULL) {
331     	free(stringp);
332 	free(list);
333     	return(NULL);
334     }
335     list->ncp = np;
336     list->next = NULL;
337     list->ncp->nc_lookups = NULL;
338     list->linep = stringp;
339     if (parse_ncp(stringp, list->ncp) == -1) {
340 	free(stringp);
341 	free(np);
342 	free(list);
343 	return (NULL);
344     }
345     else {
346 	/*
347 	 * If this is the first entry that's been read, it is the head of
348 	 * the list.  If not, put the entry at the end of the list.
349 	 * Reposition the current pointer of the handle to the last entry
350 	 * in the list.
351 	 */
352 	mutex_lock(&ni_lock);
353 	if (ni.head == NULL) {	/* first entry */
354 	    ni.head = ni.tail = list;
355 	}
356     	else {
357     	    ni.tail->next = list;
358     	    ni.tail = ni.tail->next;
359     	}
360 	ncp->nc_configs = ni.tail;
361 	result = ni.tail->ncp;
362 	mutex_unlock(&ni_lock);
363 	return(result);
364     }
365 }
366 
367 /*
368  * endnetconfig() may be called to "unbind" or "close" the netconfig database
369  * when processing is complete, releasing resources for reuse.  endnetconfig()
370  * may not be called before setnetconfig().  endnetconfig() returns 0 on
371  * success and -1 on failure (for example, if setnetconfig() was not called
372  * previously).
373  */
374 int
375 endnetconfig(void *handlep)
376 {
377     struct netconfig_vars *nc_handlep = (struct netconfig_vars *)handlep;
378 
379     struct netconfig_list *q, *p;
380 
381     /*
382      * Verify that handle is valid
383      */
384     if (nc_handlep == NULL || (nc_handlep->valid != NC_VALID &&
385 	    nc_handlep->valid != NC_STORAGE)) {
386 	nc_error = NC_NOTINIT;
387 	return (-1);
388     }
389 
390     /*
391      * Return 0 if anyone still needs it.
392      */
393     nc_handlep->valid = NC_INVALID;
394     nc_handlep->flag = 0;
395     nc_handlep->nc_configs = NULL;
396     mutex_lock(&ni_lock);
397     if (--ni.ref > 0) {
398 	mutex_unlock(&ni_lock);
399     	free(nc_handlep);
400 	return(0);
401     }
402 
403     /*
404      * No one needs these entries anymore, then frees them.
405      * Make sure all info in netconfig_info structure has been reinitialized.
406      */
407     q = ni.head;
408     ni.eof = ni.ref = 0;
409     ni.head = NULL;
410     ni.tail = NULL;
411     mutex_unlock(&ni_lock);
412 
413     while (q != NULL) {
414 	p = q->next;
415 	free(q->ncp->nc_lookups);
416 	free(q->ncp);
417 	free(q->linep);
418 	free(q);
419 	q = p;
420     }
421     free(nc_handlep);
422 
423     mutex_lock(&nc_file_lock);
424     fclose(nc_file);
425     nc_file = NULL;
426     mutex_unlock(&nc_file_lock);
427 
428     return (0);
429 }
430 
431 /*
432  * getnetconfigent(netid) returns a pointer to the struct netconfig structure
433  * corresponding to netid.  It returns NULL if netid is invalid (that is, does
434  * not name an entry in the netconfig database).  It returns NULL and sets
435  * errno in case of failure (for example, if the netconfig database cannot be
436  * opened).
437  */
438 
439 struct netconfig *
440 getnetconfigent(const char *netid)
441 {
442     FILE *file;		/* NETCONFIG db's file pointer */
443     char *linep;	/* holds current netconfig line */
444     char *stringp;	/* temporary string pointer */
445     struct netconfig *ncp = NULL;   /* returned value */
446     struct netconfig_list *list;	/* pointer to cache list */
447 
448     nc_error = NC_NOTFOUND;	/* default error. */
449     if (netid == NULL || strlen(netid) == 0) {
450 	return (NULL);
451     }
452 
453     /*
454      * Look up table if the entries have already been read and parsed in
455      * getnetconfig(), then copy this entry into a buffer and return it.
456      * If we cannot find the entry in the current list and there are more
457      * entries in the netconfig db that has not been read, we then read the
458      * db and try find the match netid.
459      * If all the netconfig db has been read and placed into the list and
460      * there is no match for the netid, return NULL.
461      */
462     mutex_lock(&ni_lock);
463     if (ni.head != NULL) {
464 	for (list = ni.head; list; list = list->next) {
465 	    if (strcmp(list->ncp->nc_netid, netid) == 0) {
466 		mutex_unlock(&ni_lock);
467 	        return(dup_ncp(list->ncp));
468 	    }
469 	}
470 	if (ni.eof == 1) {	/* that's all the entries */
471 		mutex_unlock(&ni_lock);
472 		return(NULL);
473 	}
474     }
475     mutex_unlock(&ni_lock);
476 
477 
478     if ((file = fopen(NETCONFIG, "r")) == NULL) {
479 	nc_error = NC_NONETCONFIG;
480 	return (NULL);
481     }
482 
483     if ((linep = malloc(MAXNETCONFIGLINE)) == NULL) {
484 	fclose(file);
485 	nc_error = NC_NOMEM;
486 	return (NULL);
487     }
488     do {
489 	ptrdiff_t len;
490 	char *tmpp;	/* tmp string pointer */
491 
492 	do {
493 	    if ((stringp = fgets(linep, MAXNETCONFIGLINE, file)) == NULL) {
494 		break;
495 	    }
496 	} while (*stringp == '#');
497 	if (stringp == NULL) {	    /* eof */
498 	    break;
499 	}
500 	if ((tmpp = strpbrk(stringp, "\t ")) == NULL) {	/* can't parse file */
501 	    nc_error = NC_BADFILE;
502 	    break;
503 	}
504 	if (strlen(netid) == (size_t) (len = tmpp - stringp) &&	/* a match */
505 		strncmp(stringp, netid, (size_t)len) == 0) {
506 	    if ((ncp = (struct netconfig *)
507 		    malloc(sizeof (struct netconfig))) == NULL) {
508 		break;
509 	    }
510 	    ncp->nc_lookups = NULL;
511 	    if (parse_ncp(linep, ncp) == -1) {
512 		free(ncp);
513 		ncp = NULL;
514 	    }
515 	    break;
516 	}
517     } while (stringp != NULL);
518     if (ncp == NULL) {
519 	free(linep);
520     }
521     fclose(file);
522     return(ncp);
523 }
524 
525 /*
526  * freenetconfigent(netconfigp) frees the netconfig structure pointed to by
527  * netconfigp (previously returned by getnetconfigent()).
528  */
529 
530 void
531 freenetconfigent(struct netconfig *netconfigp)
532 {
533     if (netconfigp != NULL) {
534 	free(netconfigp->nc_netid);	/* holds all netconfigp's strings */
535 	free(netconfigp->nc_lookups);
536 	free(netconfigp);
537     }
538     return;
539 }
540 
541 /*
542  * Parse line and stuff it in a struct netconfig
543  * Typical line might look like:
544  *	udp tpi_cots vb inet udp /dev/udp /usr/lib/ip.so,/usr/local/ip.so
545  *
546  * We return -1 if any of the tokens don't parse, or malloc fails.
547  *
548  * Note that we modify stringp (putting NULLs after tokens) and
549  * we set the ncp's string field pointers to point to these tokens within
550  * stringp.
551  *
552  * stringp - string to parse
553  * ncp     - where to put results
554  */
555 
556 static int
557 parse_ncp(char *stringp, struct netconfig *ncp)
558 {
559     char    *tokenp;	/* for processing tokens */
560     char    *lasts;
561     char    **nc_lookups;
562 
563     nc_error = NC_BADFILE;	/* nearly anything that breaks is for this reason */
564     stringp[strlen(stringp)-1] = '\0';	/* get rid of newline */
565     /* netid */
566     if ((ncp->nc_netid = strtok_r(stringp, "\t ", &lasts)) == NULL) {
567 	return (-1);
568     }
569 
570     /* semantics */
571     if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL) {
572 	return (-1);
573     }
574     if (strcmp(tokenp, NC_TPI_COTS_ORD_S) == 0)
575 	ncp->nc_semantics = NC_TPI_COTS_ORD;
576     else if (strcmp(tokenp, NC_TPI_COTS_S) == 0)
577 	ncp->nc_semantics = NC_TPI_COTS;
578     else if (strcmp(tokenp, NC_TPI_CLTS_S) == 0)
579 	ncp->nc_semantics = NC_TPI_CLTS;
580     else if (strcmp(tokenp, NC_TPI_RAW_S) == 0)
581 	ncp->nc_semantics = NC_TPI_RAW;
582     else
583 	return (-1);
584 
585     /* flags */
586     if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL) {
587 	return (-1);
588     }
589     for (ncp->nc_flag = NC_NOFLAG; *tokenp != '\0';
590 	    tokenp++) {
591 	switch (*tokenp) {
592 	case NC_NOFLAG_C:
593 	    break;
594 	case NC_VISIBLE_C:
595 	    ncp->nc_flag |= NC_VISIBLE;
596 	    break;
597 	case NC_BROADCAST_C:
598 	    ncp->nc_flag |= NC_BROADCAST;
599 	    break;
600 	default:
601 	    return (-1);
602 	}
603     }
604     /* protocol family */
605     if ((ncp->nc_protofmly = strtok_r(NULL, "\t ", &lasts)) == NULL) {
606 	return (-1);
607     }
608     /* protocol name */
609     if ((ncp->nc_proto = strtok_r(NULL, "\t ", &lasts)) == NULL) {
610 	return (-1);
611     }
612     /* network device */
613     if ((ncp->nc_device = strtok_r(NULL, "\t ", &lasts)) == NULL) {
614 	return (-1);
615     }
616     if ((tokenp = strtok_r(NULL, "\t ", &lasts)) == NULL) {
617 	return (-1);
618     }
619     if (strcmp(tokenp, NC_NOLOOKUP) == 0) {
620 	ncp->nc_nlookups = 0;
621 	ncp->nc_lookups = NULL;
622     } else {
623 	char *cp;	    /* tmp string */
624 
625 	free(ncp->nc_lookups); /* from last visit */
626 	ncp->nc_lookups = NULL;
627 	ncp->nc_nlookups = 0;
628 	while ((cp = tokenp) != NULL) {
629 	    if ((nc_lookups = reallocarray(ncp->nc_lookups,
630 		ncp->nc_nlookups + 1, sizeof(*ncp->nc_lookups))) == NULL) {
631 		    free(ncp->nc_lookups);
632 		    ncp->nc_lookups = NULL;
633 		    return (-1);
634 	    }
635 	    tokenp = _get_next_token(cp, ',');
636 	    ncp->nc_lookups = nc_lookups;
637 	    ncp->nc_lookups[ncp->nc_nlookups++] = cp;
638 	}
639     }
640     return (0);
641 }
642 
643 
644 /*
645  * Returns a string describing the reason for failure.
646  */
647 char *
648 nc_sperror(void)
649 {
650     const char *message;
651 
652     switch(nc_error) {
653     case NC_NONETCONFIG:
654 	message = _nc_errors[0];
655 	break;
656     case NC_NOMEM:
657 	message = _nc_errors[1];
658 	break;
659     case NC_NOTINIT:
660 	message = _nc_errors[2];
661 	break;
662     case NC_BADFILE:
663 	message = _nc_errors[3];
664 	break;
665     case NC_NOTFOUND:
666 	message = _nc_errors[4];
667 	break;
668     default:
669 	message = "Unknown network selection error";
670     }
671     /* LINTED const castaway */
672     return ((char *)message);
673 }
674 
675 /*
676  * Prints a message onto standard error describing the reason for failure.
677  */
678 void
679 nc_perror(const char *s)
680 {
681     fprintf(stderr, "%s: %s\n", s, nc_sperror());
682 }
683 
684 /*
685  * Duplicates the matched netconfig buffer.
686  */
687 static struct netconfig *
688 dup_ncp(struct netconfig *ncp)
689 {
690     struct netconfig	*p;
691     char	*tmp;
692     u_int	i;
693 
694     if ((tmp=malloc(MAXNETCONFIGLINE)) == NULL)
695 	return(NULL);
696     if ((p=(struct netconfig *)malloc(sizeof(struct netconfig))) == NULL) {
697 	free(tmp);
698 	return(NULL);
699     }
700     /*
701      * First we dup all the data from matched netconfig buffer.  Then we
702      * adjust some of the member pointer to a pre-allocated buffer where
703      * contains part of the data.
704      * To follow the convention used in parse_ncp(), we store all the
705      * necessary information in the pre-allocated buffer and let each
706      * of the netconfig char pointer member point to the right address
707      * in the buffer.
708      */
709     *p = *ncp;
710     p->nc_netid = (char *)strcpy(tmp,ncp->nc_netid);
711     tmp = strchr(tmp, '\0') + 1;
712     p->nc_protofmly = (char *)strcpy(tmp,ncp->nc_protofmly);
713     tmp = strchr(tmp, '\0') + 1;
714     p->nc_proto = (char *)strcpy(tmp,ncp->nc_proto);
715     tmp = strchr(tmp, '\0') + 1;
716     p->nc_device = (char *)strcpy(tmp,ncp->nc_device);
717     p->nc_lookups = (char **)malloc((size_t)(p->nc_nlookups+1) * sizeof(char *));
718     if (p->nc_lookups == NULL) {
719 	free(p->nc_netid);
720 	free(p);
721 	return(NULL);
722     }
723     for (i=0; i < p->nc_nlookups; i++) {
724     	tmp = strchr(tmp, '\0') + 1;
725     	p->nc_lookups[i] = (char *)strcpy(tmp,ncp->nc_lookups[i]);
726     }
727     return(p);
728 }
729