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