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