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