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