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