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