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 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 <errno.h> 46 #include <netconfig.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <rpc/rpc.h> 50 #include "rpc_com.h" 51 52 #ifdef __weak_alias 53 __weak_alias(getnetconfig,_getnetconfig) 54 __weak_alias(setnetconfig,_setnetconfig) 55 __weak_alias(endnetconfig,_endnetconfig) 56 __weak_alias(getnetconfigent,_getnetconfigent) 57 __weak_alias(freenetconfigent,_freenetconfigent) 58 __weak_alias(nc_perror,_nc_perror) 59 __weak_alias(nc_sperror,_nc_sperror) 60 #endif 61 62 /* 63 * The five library routines in this file provide application access to the 64 * system network configuration database, /etc/netconfig. In addition to the 65 * netconfig database and the routines for accessing it, the environment 66 * variable NETPATH and its corresponding routines in getnetpath.c may also be 67 * used to specify the network transport to be used. 68 */ 69 70 71 /* 72 * netconfig errors 73 */ 74 75 #define NC_NONETCONFIG ENOENT 76 #define NC_NOMEM ENOMEM 77 #define NC_NOTINIT EINVAL /* setnetconfig was not called first */ 78 #define NC_BADFILE EBADF /* format for netconfig file is bad */ 79 80 /* 81 * semantics as strings (should be in netconfig.h) 82 */ 83 #define NC_TPI_CLTS_S "tpi_clts" 84 #define NC_TPI_COTS_S "tpi_cots" 85 #define NC_TPI_COTS_ORD_S "tpi_cots_ord" 86 #define NC_TPI_RAW_S "tpi_raw" 87 88 /* 89 * flags as characters (also should be in netconfig.h) 90 */ 91 #define NC_NOFLAG_C '-' 92 #define NC_VISIBLE_C 'v' 93 #define NC_BROADCAST_C 'b' 94 95 /* 96 * Character used to indicate there is no name-to-address lookup library 97 */ 98 #define NC_NOLOOKUP "-" 99 100 static char *_nc_errors[] = { 101 "Netconfig database not found", 102 "Not enough memory", 103 "Not initialized", 104 "Netconfig database has invalid format" 105 }; 106 107 struct netconfig_info { 108 int eof; /* all entries has been read */ 109 int ref; /* # of times setnetconfig() has been called */ 110 struct netconfig_list *head; /* head of the list */ 111 struct netconfig_list *tail; /* last of the list */ 112 }; 113 114 struct netconfig_list { 115 char *linep; /* hold line read from netconfig */ 116 struct netconfig *ncp; 117 struct netconfig_list *next; 118 }; 119 120 struct netconfig_vars { 121 int valid; /* token that indicates a valid netconfig_vars */ 122 int flag; /* first time flag */ 123 struct netconfig_list *nc_configs; /* pointer to the current netconfig entry */ 124 }; 125 126 #define NC_VALID 0xfeed 127 #define NC_STORAGE 0xf00d 128 #define NC_INVALID 0 129 130 131 static int *__nc_error __P((void)); 132 static int parse_ncp __P((char *, struct netconfig *)); 133 static struct netconfig *dup_ncp __P((struct netconfig *)); 134 135 136 static FILE *nc_file; /* for netconfig db */ 137 static struct netconfig_info ni = { 0, 0, NULL, NULL}; 138 139 #define MAXNETCONFIGLINE 1000 140 141 static int * 142 __nc_error() 143 { 144 #ifdef __REENT 145 static thread_key_t nc_key = 0; 146 int *nc_addr = NULL; 147 #endif 148 static int nc_error = 0; 149 150 #ifdef __REENT 151 if (_thr_getspecific(nc_key, (void **) &nc_addr) != 0) { 152 mutex_lock(&nc_lock); 153 if (_thr_keycreate(&rce_key, free) != 0) { 154 mutex_unlock(&nc_lock); 155 return nc_addr; 156 } 157 mutex_unlock(&nc_lock); 158 } 159 if (nc_addr == NULL) { 160 nc_addr = (int *)malloc(sizeof (int)); 161 if (_thr_setspecific(nc_key, (void *) nc_addr) != 0) { 162 if (nc_addr) 163 free(nc_addr); 164 return &nc_error; 165 } 166 *nc_addr = 0; 167 return nc_addr; 168 } 169 return nc_addr; 170 #else 171 return &nc_error; 172 #endif 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 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 if (netid == NULL || strlen(netid) == 0) { 419 return (NULL); 420 } 421 422 /* 423 * Look up table if the entries have already been read and parsed in 424 * getnetconfig(), then copy this entry into a buffer and return it. 425 * If we cannot find the entry in the current list and there are more 426 * entries in the netconfig db that has not been read, we then read the 427 * db and try find the match netid. 428 * If all the netconfig db has been read and placed into the list and 429 * there is no match for the netid, return NULL. 430 */ 431 if (ni.head != NULL) { 432 for (list = ni.head; list; list = list->next) { 433 if (strcmp(list->ncp->nc_netid, netid) == 0) { 434 return(dup_ncp(list->ncp)); 435 } 436 } 437 if (ni.eof == 1) /* that's all the entries */ 438 return(NULL); 439 } 440 441 442 if ((file = fopen(NETCONFIG, "r")) == NULL) { 443 return (NULL); 444 } 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)) == 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 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 default: 625 message = "Unknown network selection error"; 626 } 627 return (message); 628 } 629 630 /* 631 * Prints a message onto standard error describing the reason for failure. 632 */ 633 void 634 nc_perror(s) 635 const char *s; 636 { 637 fprintf(stderr, "%s: %s", s, nc_sperror()); 638 } 639 640 /* 641 * Duplicates the matched netconfig buffer. 642 */ 643 static struct netconfig * 644 dup_ncp(ncp) 645 struct netconfig *ncp; 646 { 647 struct netconfig *p; 648 char *tmp; 649 int i; 650 651 if ((tmp=malloc(MAXNETCONFIGLINE)) == NULL) 652 return(NULL); 653 if ((p=(struct netconfig *)malloc(sizeof(struct netconfig))) == NULL) { 654 free(tmp); 655 return(NULL); 656 } 657 /* 658 * First we dup all the data from matched netconfig buffer. Then we 659 * adjust some of the member pointer to a pre-allocated buffer where 660 * contains part of the data. 661 * To follow the convention used in parse_ncp(), we store all the 662 * neccessary information in the pre-allocated buffer and let each 663 * of the netconfig char pointer member point to the right address 664 * in the buffer. 665 */ 666 *p = *ncp; 667 p->nc_netid = (char *)strcpy(tmp,ncp->nc_netid); 668 tmp = strchr(tmp, NULL) + 1; 669 p->nc_protofmly = (char *)strcpy(tmp,ncp->nc_protofmly); 670 tmp = strchr(tmp, NULL) + 1; 671 p->nc_proto = (char *)strcpy(tmp,ncp->nc_proto); 672 tmp = strchr(tmp, NULL) + 1; 673 p->nc_device = (char *)strcpy(tmp,ncp->nc_device); 674 p->nc_lookups = (char **)malloc((size_t)(p->nc_nlookups+1) * sizeof(char *)); 675 if (p->nc_lookups == NULL) { 676 free(p->nc_netid); 677 return(NULL); 678 } 679 for (i=0; i < p->nc_nlookups; i++) { 680 tmp = strchr(tmp, NULL) + 1; 681 p->nc_lookups[i] = (char *)strcpy(tmp,ncp->nc_lookups[i]); 682 } 683 return(p); 684 } 685