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