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