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