1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 #include <ctype.h> 34 #include <stdio.h> 35 #include <tiuser.h> 36 #include <netdir.h> 37 #include <netconfig.h> 38 #include <sys/utsname.h> 39 #include <sys/param.h> 40 #include <string.h> 41 #include <stdlib.h> 42 #include <synch.h> 43 44 /* 45 * The generic name to address mappings for any transport that 46 * has strings for address (e.g., ISO Starlan). 47 * 48 * Address in ISO Starlan consist of arbitrary strings of 49 * characters. Because of this, the following routines 50 * create an "address" based on two strings, one gotten 51 * from a "host" file and one gotten from a "services" file. 52 * The two strings are catenated together (with a "." between 53 * them). The hosts file is /etc/net/starlan/hosts and 54 * contain lines of the form: 55 * 56 * arbitrary_string machname 57 * 58 * To make things simple, the "arbitrary string" should be the 59 * machine name. 60 * 61 * The services file is /etc/net/starlan/services and has lines 62 * of the form: 63 * 64 * service_name arbitrary_string 65 * 66 * Again, to make things easer, the "arbitrary name" should be the 67 * service name. 68 */ 69 70 #define HOSTFILE "/etc/net/%s/hosts" 71 #define SERVICEFILE "/etc/net/%s/services" 72 #define FIELD1 1 73 #define FIELD2 2 74 75 static int searchhost(struct netconfig *, char *, int, char *); 76 static int searchserv(struct netconfig *, char *, int, char *); 77 static const char *nodename(void); 78 79 /* 80 * _netdir_getbyname() returns all of the addresses for 81 * a specified host and service. 82 */ 83 84 struct nd_addrlist * 85 _netdir_getbyname(struct netconfig *netconfigp, 86 struct nd_hostserv *nd_hostservp) 87 { 88 char fulladdr[BUFSIZ]; /* holds the full address string */ 89 struct nd_addrlist *retp; /* the return structure */ 90 struct netbuf *netbufp; /* indexes through the addresses */ 91 92 /* 93 * HOST_BROADCAST is not supported. 94 */ 95 96 if (strcmp(nd_hostservp->h_host, HOST_BROADCAST) == 0) { 97 _nderror = ND_NOHOST; 98 return (NULL); 99 } 100 101 if (searchhost(netconfigp, nd_hostservp->h_host, FIELD2, 102 fulladdr) == 0) { 103 _nderror = ND_NOHOST; 104 return (NULL); 105 } 106 107 /* 108 * Now simply fill in the address by forming strings of the 109 * form "string_from_hosts.string_from_services" 110 */ 111 112 if (nd_hostservp->h_serv && 113 (strcmp(nd_hostservp->h_serv, "rpcbind") == 0)) { 114 (void) strcat(fulladdr, "."); 115 (void) strcat(fulladdr, "rpc"); /* hard coded */ 116 } else { 117 /* 118 * Get the address from the services file 119 */ 120 121 if (nd_hostservp->h_serv && (nd_hostservp->h_serv[0] != '\0')) { 122 (void) strcat(fulladdr, "."); 123 if (searchserv(netconfigp, nd_hostservp->h_serv, FIELD1, 124 fulladdr + strlen(fulladdr)) == 0) { 125 _nderror = ND_NOSERV; 126 return (NULL); 127 } 128 } 129 } 130 131 if ((retp = malloc(sizeof (struct nd_addrlist))) == NULL) { 132 _nderror = ND_NOMEM; 133 return (NULL); 134 } 135 136 /* 137 * We do not worry about multiple addresses here. Loopbacks 138 * have only one interface. 139 */ 140 141 retp->n_cnt = 1; 142 if ((retp->n_addrs = malloc(sizeof (struct netbuf))) == NULL) { 143 free(retp); 144 _nderror = ND_NOMEM; 145 return (NULL); 146 } 147 148 netbufp = retp->n_addrs; 149 150 /* 151 * Don't include the terminating NULL character in the 152 * length. 153 */ 154 155 netbufp->len = netbufp->maxlen = (int)strlen(fulladdr); 156 if ((netbufp->buf = strdup(fulladdr)) == NULL) { 157 free(netbufp); 158 free(retp); 159 _nderror = ND_NOMEM; 160 return (NULL); 161 } 162 _nderror = ND_OK; 163 return (retp); 164 } 165 166 /* 167 * _netdir_getbyaddr() takes an address (hopefully obtained from 168 * someone doing a _netdir_getbyname()) and returns all hosts with 169 * that address. 170 */ 171 172 struct nd_hostservlist * 173 _netdir_getbyaddr(struct netconfig *netconfigp, struct netbuf *netbufp) 174 { 175 char fulladdr[BUFSIZ]; /* a copy of the address string */ 176 char servbuf[BUFSIZ]; /* a buffer for service string */ 177 char hostbuf[BUFSIZ]; /* points to list of host names */ 178 char *hostname; /* the "first" path of the string */ 179 char *servname; /* the "second" part of string */ 180 struct nd_hostservlist *retp; /* the return structure */ 181 char *serv; /* resultant service name obtained */ 182 int nhost; /* the number of hosts in hostpp */ 183 struct nd_hostserv *nd_hostservp; /* traverses the host structures */ 184 char *nexttok; /* next token to process */ 185 186 /* 187 * Separate the two parts of the address string. 188 */ 189 190 (void) strlcpy(fulladdr, netbufp->buf, sizeof (fulladdr)); 191 hostname = strtok_r(fulladdr, ".", &nexttok); 192 if (hostname == NULL) { 193 _nderror = ND_NOHOST; 194 return (NULL); 195 } 196 servname = strtok_r(NULL, " \n\t", &nexttok); 197 198 /* 199 * Search for all the hosts associated with the 200 * first part of the address string. 201 */ 202 203 nhost = searchhost(netconfigp, hostname, FIELD1, hostbuf); 204 if (nhost == 0) { 205 _nderror = ND_NOHOST; 206 return (NULL); 207 } 208 209 /* 210 * Search for the service associated with the second 211 * path of the address string. 212 */ 213 214 if (servname == NULL) { 215 _nderror = ND_NOSERV; 216 return (NULL); 217 } 218 219 servbuf[0] = '\0'; 220 serv = servbuf; 221 if (searchserv(netconfigp, servname, FIELD2, servbuf) == 0) { 222 serv = _taddr2uaddr(netconfigp, netbufp); 223 (void) strcpy(servbuf, serv); 224 free(serv); 225 serv = servbuf; 226 while (*serv != '.') 227 serv++; 228 } 229 230 /* 231 * Allocate space to hold the return structure, set the number 232 * of hosts, and allocate space to hold them. 233 */ 234 235 if ((retp = malloc(sizeof (struct nd_hostservlist))) == NULL) { 236 _nderror = ND_NOMEM; 237 return (NULL); 238 } 239 240 retp->h_cnt = nhost; 241 retp->h_hostservs = calloc(nhost, sizeof (struct nd_hostserv)); 242 if (retp->h_hostservs == NULL) { 243 free(retp); 244 _nderror = ND_NOMEM; 245 return (NULL); 246 } 247 248 /* 249 * Loop through the host structues and fill them in with 250 * each host name (and service name). 251 */ 252 253 nd_hostservp = retp->h_hostservs; 254 hostname = strtok_r(hostbuf, ",", &nexttok); 255 while (hostname && nhost--) { 256 if (((nd_hostservp->h_host = strdup(hostname)) == NULL) || 257 ((nd_hostservp->h_serv = strdup(serv)) == NULL)) { 258 netdir_free(retp, ND_HOSTSERVLIST); 259 _nderror = ND_NOMEM; 260 return (NULL); 261 } 262 nd_hostservp++; 263 hostname = strtok_r(NULL, ",", &nexttok); 264 } 265 266 _nderror = ND_OK; 267 return (retp); 268 } 269 270 /* 271 * _taddr2uaddr() translates a address into a "universal" address. 272 * Since the address is a string, simply return the string as the 273 * universal address (but replace all non-printable characters with 274 * the \ddd form, where ddd is three octal digits). The '\n' character 275 * is also replace by \ddd and the '\' character is placed as two 276 * '\' characters. 277 */ 278 279 /* ARGSUSED */ 280 char * 281 _taddr2uaddr(struct netconfig *netconfigp, struct netbuf *netbufp) 282 { 283 char *retp; /* pointer the return string */ 284 char *to; /* traverses and populates the return string */ 285 char *from; /* traverses the string to be converted */ 286 int i; /* indexes through the given string */ 287 288 /* 289 * BUFSIZ is perhaps too big for this one and there is a better 290 * way to optimize it, but for now we will just assume BUFSIZ 291 */ 292 if ((retp = malloc(BUFSIZ)) == NULL) { 293 _nderror = ND_NOMEM; 294 return (NULL); 295 } 296 to = retp; 297 from = netbufp->buf; 298 299 for (i = 0; i < netbufp->len; i++) { 300 if (*from == '\\') { 301 *to++ = '\\'; 302 *to++ = '\\'; 303 } else { 304 if (*from == '\n' || !isprint((unsigned char)*from)) { 305 (void) sprintf(to, "\\%.3o", *from & 0xff); 306 to += 4; 307 } else { 308 *to++ = *from; 309 } 310 } 311 from++; 312 } 313 *to = '\0'; 314 return (retp); 315 } 316 317 /* 318 * _uaddr2taddr() translates a universal address back into a 319 * netaddr structure. Since the universal address is a string, 320 * put that into the TLI buffer (making sure to change all \ddd 321 * characters back and strip off the trailing \0 character). 322 */ 323 324 /* ARGSUSED */ 325 struct netbuf * 326 _uaddr2taddr(struct netconfig *netconfigp, char *uaddr) 327 { 328 struct netbuf *retp; /* the return structure */ 329 char *holdp; /* holds the converted address */ 330 char *to; /* traverses and populates the new address */ 331 char *from; /* traverses the universal address */ 332 333 holdp = malloc(strlen(uaddr) + 1); 334 if (holdp == NULL) { 335 _nderror = ND_NOMEM; 336 return (NULL); 337 } 338 from = uaddr; 339 to = holdp; 340 341 while (*from) { 342 if (*from == '\\') { 343 if (*(from+1) == '\\') { 344 *to = '\\'; 345 from += 2; 346 } else { 347 *to = ((*(from+1) - '0') << 6) + 348 ((*(from+2) - '0') << 3) + 349 (*(from+3) - '0'); 350 from += 4; 351 } 352 } else { 353 *to = *from++; 354 } 355 to++; 356 } 357 *to = '\0'; 358 359 if ((retp = malloc(sizeof (struct netbuf))) == NULL) { 360 free(holdp); 361 _nderror = ND_NOMEM; 362 return (NULL); 363 } 364 retp->maxlen = retp->len = (int)(to - holdp); 365 retp->buf = holdp; 366 return (retp); 367 } 368 369 /* 370 * _netdir_options() is a "catch-all" routine that does 371 * transport specific things. The only thing that these 372 * routines have to worry about is ND_MERGEADDR. 373 */ 374 375 /* ARGSUSED */ 376 int 377 _netdir_options(struct netconfig *netconfigp, int option, int fd, void *par) 378 { 379 struct nd_mergearg *argp; /* the argument for mergeaddr */ 380 381 switch (option) { 382 case ND_MERGEADDR: 383 /* 384 * Translate the universal address into something that 385 * makes sense to the caller. This is a no-op in 386 * loopback's case, so just return the universal address. 387 */ 388 argp = (struct nd_mergearg *)par; 389 argp->m_uaddr = strdup(argp->s_uaddr); 390 return (argp->m_uaddr == NULL? -1 : 0); 391 default: 392 _nderror = ND_NOCTRL; 393 return (-1); 394 } 395 } 396 397 /* 398 * searchhost() looks for the specified token in the host file. 399 * The "field" parameter signifies which field to compare the token 400 * on, and returns all comma separated values associated with the token. 401 */ 402 403 static int 404 searchhost(struct netconfig *netconfigp, char *token, int field, char *hostbuf) 405 { 406 char searchfile[MAXPATHLEN]; /* the name of file to be opened */ 407 char buf[BUFSIZ]; /* holds each line of the file */ 408 char *fileaddr; /* the first token in each line */ 409 char *filehost; /* the second token in each line */ 410 char *cmpstr; /* the string to compare token to */ 411 char *retstr; /* the string to return if compare succeeds */ 412 char *nexttok; /* next token to process */ 413 FILE *fp; /* the opened searchfile */ 414 int nelements = 0; /* total number of elements found */ 415 const char *myname; /* my own nodename */ 416 417 myname = nodename(); 418 419 /* 420 * Unless /etc/netconfig has been altered, the only transport 421 * that will use straddr.so is loopback. In this case, we 422 * always return our nodename if that's what we were passed, 423 * or we fail (note that we'd like to return a constant like 424 * "localhost" so that changes to the machine name won't cause 425 * problems, but things like autofs actually assume that we're 426 * using our nodename). 427 */ 428 429 if ((strcmp(token, HOST_SELF_BIND) == 0) || 430 (strcmp(token, HOST_SELF_CONNECT) == 0) || 431 (strcmp(token, HOST_ANY) == 0) || 432 (myname != NULL && (strcmp(token, myname) == 0))) { 433 if (myname == NULL) 434 return (0); 435 436 (void) strcpy(hostbuf, myname); 437 return (1); 438 } 439 440 if (strcmp(netconfigp->nc_protofmly, NC_LOOPBACK) == 0) 441 return (0); 442 443 /* 444 * We only get here if an administrator has modified 445 * /etc/netconfig to use straddr.so for a transport other than 446 * loopback (which is questionable but something we'll need to 447 * EOL at a later point in time). In this case, we fallback to 448 * searching for the associated key in the appropriate hosts 449 * file (based on nc_netid). 450 */ 451 452 (void) snprintf(searchfile, sizeof (searchfile), HOSTFILE, 453 netconfigp->nc_netid); 454 455 fp = fopen(searchfile, "r"); 456 if (fp == NULL) 457 return (0); 458 459 /* 460 * Loop through the file looking for the tokens and creating 461 * the list of strings to be returned. 462 */ 463 464 while (fgets(buf, BUFSIZ, fp) != NULL) { 465 466 /* 467 * Ignore comments and bad lines. 468 */ 469 470 fileaddr = strtok_r(buf, " \t\n", &nexttok); 471 if (fileaddr == NULL || *fileaddr == '#') 472 continue; 473 474 if ((filehost = strtok_r(NULL, " \t\n", &nexttok)) == NULL) 475 continue; 476 477 /* 478 * determine which to compare the token to, then 479 * compare it, and if they match, add the return 480 * string to the list. 481 */ 482 483 cmpstr = (field == FIELD1)? fileaddr : filehost; 484 retstr = (field == FIELD1)? filehost : fileaddr; 485 486 if (strcmp(token, cmpstr) == 0) { 487 nelements++; 488 if (field == FIELD2) { 489 /* 490 * called by _netdir_getbyname 491 */ 492 493 (void) strcpy(hostbuf, retstr); 494 break; 495 } 496 if (nelements > 1) { 497 /* 498 * Assuming that "," will never be a part 499 * of any host name. 500 */ 501 (void) strcat(hostbuf, ","); 502 } 503 (void) strcat(hostbuf, retstr); 504 } 505 } 506 507 (void) fclose(fp); 508 return (nelements); 509 } 510 511 /* 512 * searchserv() looks for the specified token in the service file. 513 * The "field" parameter signifies which field to compare the token 514 * on, and returns the string associated with the token in servname. 515 */ 516 517 static int 518 searchserv(struct netconfig *netconfigp, char *token, int field, char *servname) 519 { 520 char searchfile[MAXPATHLEN]; /* the name of file to be opened */ 521 char buf[BUFSIZ]; /* buffer space for lines in file */ 522 char *fileservice; /* the first token in each line */ 523 char *fileport; /* the second token in each line */ 524 char *cmpstr; /* the string to compare the token to */ 525 char *retstr; /* temporarily hold token in line of file */ 526 char *nexttok; /* next token to process */ 527 FILE *fp; /* the opened searchfile */ 528 529 (void) snprintf(searchfile, sizeof (searchfile), SERVICEFILE, 530 netconfigp->nc_netid); 531 532 fp = fopen(searchfile, "r"); 533 if (fp == NULL) 534 return (0); 535 536 /* 537 * Loop through the services file looking for the token. 538 */ 539 540 while (fgets(buf, BUFSIZ, fp) != NULL) { 541 /* 542 * If comment or bad line, continue. 543 */ 544 fileservice = strtok_r(buf, " \t\n", &nexttok); 545 if (fileservice == NULL || *fileservice == '#') 546 continue; 547 548 if ((fileport = strtok_r(NULL, " \t\n", &nexttok)) == NULL) 549 continue; 550 551 cmpstr = (field == FIELD1)? fileservice : fileport; 552 retstr = (field == FIELD1)? fileport : fileservice; 553 554 if (strcmp(token, cmpstr) == 0) { 555 (void) strcpy(servname, retstr); 556 (void) fclose(fp); 557 return (1); 558 } 559 } 560 561 (void) fclose(fp); 562 return (0); 563 } 564 565 static const char * 566 nodename(void) 567 { 568 static mutex_t nodename_lock = DEFAULTMUTEX; 569 static const char *myname; 570 struct utsname utsname; 571 572 (void) mutex_lock(&nodename_lock); 573 if (myname != NULL) { 574 (void) mutex_unlock(&nodename_lock); 575 return (myname); 576 } 577 578 if (uname(&utsname) == -1) { 579 (void) mutex_unlock(&nodename_lock); 580 _nderror = ND_SYSTEM; 581 return (NULL); 582 } 583 584 myname = strdup(utsname.nodename); 585 if (myname == NULL) 586 _nderror = ND_NOMEM; 587 588 (void) mutex_unlock(&nodename_lock); 589 return (myname); 590 } 591