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