/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include /* * The generic name to address mappings for any transport that * has strings for address (e.g., ISO Starlan). * * Address in ISO Starlan consist of arbitrary strings of * characters. Because of this, the following routines * create an "address" based on two strings, one gotten * from a "host" file and one gotten from a "services" file. * The two strings are catenated together (with a "." between * them). The hosts file is /etc/net/starlan/hosts and * contain lines of the form: * * arbitrary_string machname * * To make things simple, the "arbitrary string" should be the * machine name. * * The services file is /etc/net/starlan/services and has lines * of the form: * * service_name arbitrary_string * * Again, to make things easer, the "arbitrary name" should be the * service name. */ #define HOSTFILE "/etc/net/%s/hosts" #define SERVICEFILE "/etc/net/%s/services" #define FIELD1 1 #define FIELD2 2 static int searchhost(struct netconfig *, char *, int, char *); static int searchserv(struct netconfig *, char *, int, char *); static const char *nodename(void); /* * _netdir_getbyname() returns all of the addresses for * a specified host and service. */ struct nd_addrlist * _netdir_getbyname(struct netconfig *netconfigp, struct nd_hostserv *nd_hostservp) { char fulladdr[BUFSIZ]; /* holds the full address string */ struct nd_addrlist *retp; /* the return structure */ struct netbuf *netbufp; /* indexes through the addresses */ /* * HOST_BROADCAST is not supported. */ if (strcmp(nd_hostservp->h_host, HOST_BROADCAST) == 0) { _nderror = ND_NOHOST; return (NULL); } if (searchhost(netconfigp, nd_hostservp->h_host, FIELD2, fulladdr) == 0) { _nderror = ND_NOHOST; return (NULL); } /* * Now simply fill in the address by forming strings of the * form "string_from_hosts.string_from_services" */ if (nd_hostservp->h_serv && (strcmp(nd_hostservp->h_serv, "rpcbind") == 0)) { (void) strcat(fulladdr, "."); (void) strcat(fulladdr, "rpc"); /* hard coded */ } else { /* * Get the address from the services file */ if (nd_hostservp->h_serv && (nd_hostservp->h_serv[0] != '\0')) { (void) strcat(fulladdr, "."); if (searchserv(netconfigp, nd_hostservp->h_serv, FIELD1, fulladdr + strlen(fulladdr)) == 0) { _nderror = ND_NOSERV; return (NULL); } } } if ((retp = malloc(sizeof (struct nd_addrlist))) == NULL) { _nderror = ND_NOMEM; return (NULL); } /* * We do not worry about multiple addresses here. Loopbacks * have only one interface. */ retp->n_cnt = 1; if ((retp->n_addrs = malloc(sizeof (struct netbuf))) == NULL) { free(retp); _nderror = ND_NOMEM; return (NULL); } netbufp = retp->n_addrs; /* * Don't include the terminating NULL character in the * length. */ netbufp->len = netbufp->maxlen = (int)strlen(fulladdr); if ((netbufp->buf = strdup(fulladdr)) == NULL) { free(netbufp); free(retp); _nderror = ND_NOMEM; return (NULL); } _nderror = ND_OK; return (retp); } /* * _netdir_getbyaddr() takes an address (hopefully obtained from * someone doing a _netdir_getbyname()) and returns all hosts with * that address. */ struct nd_hostservlist * _netdir_getbyaddr(struct netconfig *netconfigp, struct netbuf *netbufp) { char fulladdr[BUFSIZ]; /* a copy of the address string */ char servbuf[BUFSIZ]; /* a buffer for service string */ char hostbuf[BUFSIZ]; /* points to list of host names */ char *hostname; /* the "first" path of the string */ char *servname; /* the "second" part of string */ struct nd_hostservlist *retp; /* the return structure */ char *serv; /* resultant service name obtained */ int nhost; /* the number of hosts in hostpp */ struct nd_hostserv *nd_hostservp; /* traverses the host structures */ char *nexttok; /* next token to process */ /* * Separate the two parts of the address string. */ (void) strlcpy(fulladdr, netbufp->buf, sizeof (fulladdr)); hostname = strtok_r(fulladdr, ".", &nexttok); if (hostname == NULL) { _nderror = ND_NOHOST; return (NULL); } servname = strtok_r(NULL, " \n\t", &nexttok); /* * Search for all the hosts associated with the * first part of the address string. */ nhost = searchhost(netconfigp, hostname, FIELD1, hostbuf); if (nhost == 0) { _nderror = ND_NOHOST; return (NULL); } /* * Search for the service associated with the second * path of the address string. */ if (servname == NULL) { _nderror = ND_NOSERV; return (NULL); } servbuf[0] = '\0'; serv = servbuf; if (searchserv(netconfigp, servname, FIELD2, servbuf) == 0) { serv = _taddr2uaddr(netconfigp, netbufp); (void) strcpy(servbuf, serv); free(serv); serv = servbuf; while (*serv != '.') serv++; } /* * Allocate space to hold the return structure, set the number * of hosts, and allocate space to hold them. */ if ((retp = malloc(sizeof (struct nd_hostservlist))) == NULL) { _nderror = ND_NOMEM; return (NULL); } retp->h_cnt = nhost; retp->h_hostservs = calloc(nhost, sizeof (struct nd_hostserv)); if (retp->h_hostservs == NULL) { free(retp); _nderror = ND_NOMEM; return (NULL); } /* * Loop through the host structues and fill them in with * each host name (and service name). */ nd_hostservp = retp->h_hostservs; hostname = strtok_r(hostbuf, ",", &nexttok); while (hostname && nhost--) { if (((nd_hostservp->h_host = strdup(hostname)) == NULL) || ((nd_hostservp->h_serv = strdup(serv)) == NULL)) { netdir_free(retp, ND_HOSTSERVLIST); _nderror = ND_NOMEM; return (NULL); } nd_hostservp++; hostname = strtok_r(NULL, ",", &nexttok); } _nderror = ND_OK; return (retp); } /* * _taddr2uaddr() translates a address into a "universal" address. * Since the address is a string, simply return the string as the * universal address (but replace all non-printable characters with * the \ddd form, where ddd is three octal digits). The '\n' character * is also replace by \ddd and the '\' character is placed as two * '\' characters. */ /* ARGSUSED */ char * _taddr2uaddr(struct netconfig *netconfigp, struct netbuf *netbufp) { char *retp; /* pointer the return string */ char *to; /* traverses and populates the return string */ char *from; /* traverses the string to be converted */ int i; /* indexes through the given string */ /* * BUFSIZ is perhaps too big for this one and there is a better * way to optimize it, but for now we will just assume BUFSIZ */ if ((retp = malloc(BUFSIZ)) == NULL) { _nderror = ND_NOMEM; return (NULL); } to = retp; from = netbufp->buf; for (i = 0; i < netbufp->len; i++) { if (*from == '\\') { *to++ = '\\'; *to++ = '\\'; } else { if (*from == '\n' || !isprint((unsigned char)*from)) { (void) sprintf(to, "\\%.3o", *from & 0xff); to += 4; } else { *to++ = *from; } } from++; } *to = '\0'; return (retp); } /* * _uaddr2taddr() translates a universal address back into a * netaddr structure. Since the universal address is a string, * put that into the TLI buffer (making sure to change all \ddd * characters back and strip off the trailing \0 character). */ /* ARGSUSED */ struct netbuf * _uaddr2taddr(struct netconfig *netconfigp, char *uaddr) { struct netbuf *retp; /* the return structure */ char *holdp; /* holds the converted address */ char *to; /* traverses and populates the new address */ char *from; /* traverses the universal address */ holdp = malloc(strlen(uaddr) + 1); if (holdp == NULL) { _nderror = ND_NOMEM; return (NULL); } from = uaddr; to = holdp; while (*from) { if (*from == '\\') { if (*(from+1) == '\\') { *to = '\\'; from += 2; } else { *to = ((*(from+1) - '0') << 6) + ((*(from+2) - '0') << 3) + (*(from+3) - '0'); from += 4; } } else { *to = *from++; } to++; } *to = '\0'; if ((retp = malloc(sizeof (struct netbuf))) == NULL) { free(holdp); _nderror = ND_NOMEM; return (NULL); } retp->maxlen = retp->len = (int)(to - holdp); retp->buf = holdp; return (retp); } /* * _netdir_options() is a "catch-all" routine that does * transport specific things. The only thing that these * routines have to worry about is ND_MERGEADDR. */ /* ARGSUSED */ int _netdir_options(struct netconfig *netconfigp, int option, int fd, void *par) { struct nd_mergearg *argp; /* the argument for mergeaddr */ switch (option) { case ND_MERGEADDR: /* * Translate the universal address into something that * makes sense to the caller. This is a no-op in * loopback's case, so just return the universal address. */ argp = (struct nd_mergearg *)par; argp->m_uaddr = strdup(argp->s_uaddr); return (argp->m_uaddr == NULL? -1 : 0); default: _nderror = ND_NOCTRL; return (-1); } } /* * searchhost() looks for the specified token in the host file. * The "field" parameter signifies which field to compare the token * on, and returns all comma separated values associated with the token. */ static int searchhost(struct netconfig *netconfigp, char *token, int field, char *hostbuf) { char searchfile[MAXPATHLEN]; /* the name of file to be opened */ char buf[BUFSIZ]; /* holds each line of the file */ char *fileaddr; /* the first token in each line */ char *filehost; /* the second token in each line */ char *cmpstr; /* the string to compare token to */ char *retstr; /* the string to return if compare succeeds */ char *nexttok; /* next token to process */ FILE *fp; /* the opened searchfile */ int nelements = 0; /* total number of elements found */ const char *myname; /* my own nodename */ myname = nodename(); /* * Unless /etc/netconfig has been altered, the only transport * that will use straddr.so is loopback. In this case, we * always return our nodename if that's what we were passed, * or we fail (note that we'd like to return a constant like * "localhost" so that changes to the machine name won't cause * problems, but things like autofs actually assume that we're * using our nodename). */ if ((strcmp(token, HOST_SELF_BIND) == 0) || (strcmp(token, HOST_SELF_CONNECT) == 0) || (strcmp(token, HOST_ANY) == 0) || (myname != NULL && (strcmp(token, myname) == 0))) { if (myname == NULL) return (0); (void) strcpy(hostbuf, myname); return (1); } if (strcmp(netconfigp->nc_protofmly, NC_LOOPBACK) == 0) return (0); /* * We only get here if an administrator has modified * /etc/netconfig to use straddr.so for a transport other than * loopback (which is questionable but something we'll need to * EOL at a later point in time). In this case, we fallback to * searching for the associated key in the appropriate hosts * file (based on nc_netid). */ (void) snprintf(searchfile, sizeof (searchfile), HOSTFILE, netconfigp->nc_netid); fp = fopen(searchfile, "rF"); if (fp == NULL) return (0); /* * Loop through the file looking for the tokens and creating * the list of strings to be returned. */ while (fgets(buf, BUFSIZ, fp) != NULL) { /* * Ignore comments and bad lines. */ fileaddr = strtok_r(buf, " \t\n", &nexttok); if (fileaddr == NULL || *fileaddr == '#') continue; if ((filehost = strtok_r(NULL, " \t\n", &nexttok)) == NULL) continue; /* * determine which to compare the token to, then * compare it, and if they match, add the return * string to the list. */ cmpstr = (field == FIELD1)? fileaddr : filehost; retstr = (field == FIELD1)? filehost : fileaddr; if (strcmp(token, cmpstr) == 0) { nelements++; if (field == FIELD2) { /* * called by _netdir_getbyname */ (void) strcpy(hostbuf, retstr); break; } if (nelements > 1) { /* * Assuming that "," will never be a part * of any host name. */ (void) strcat(hostbuf, ","); } (void) strcat(hostbuf, retstr); } } (void) fclose(fp); return (nelements); } /* * searchserv() looks for the specified token in the service file. * The "field" parameter signifies which field to compare the token * on, and returns the string associated with the token in servname. */ static int searchserv(struct netconfig *netconfigp, char *token, int field, char *servname) { char searchfile[MAXPATHLEN]; /* the name of file to be opened */ char buf[BUFSIZ]; /* buffer space for lines in file */ char *fileservice; /* the first token in each line */ char *fileport; /* the second token in each line */ char *cmpstr; /* the string to compare the token to */ char *retstr; /* temporarily hold token in line of file */ char *nexttok; /* next token to process */ FILE *fp; /* the opened searchfile */ (void) snprintf(searchfile, sizeof (searchfile), SERVICEFILE, netconfigp->nc_netid); fp = fopen(searchfile, "rF"); if (fp == NULL) return (0); /* * Loop through the services file looking for the token. */ while (fgets(buf, BUFSIZ, fp) != NULL) { /* * If comment or bad line, continue. */ fileservice = strtok_r(buf, " \t\n", &nexttok); if (fileservice == NULL || *fileservice == '#') continue; if ((fileport = strtok_r(NULL, " \t\n", &nexttok)) == NULL) continue; cmpstr = (field == FIELD1)? fileservice : fileport; retstr = (field == FIELD1)? fileport : fileservice; if (strcmp(token, cmpstr) == 0) { (void) strcpy(servname, retstr); (void) fclose(fp); return (1); } } (void) fclose(fp); return (0); } static const char * nodename(void) { static mutex_t nodename_lock = DEFAULTMUTEX; static const char *myname; struct utsname utsname; (void) mutex_lock(&nodename_lock); if (myname != NULL) { (void) mutex_unlock(&nodename_lock); return (myname); } if (uname(&utsname) == -1) { (void) mutex_unlock(&nodename_lock); _nderror = ND_SYSTEM; return (NULL); } myname = strdup(utsname.nodename); if (myname == NULL) _nderror = ND_NOMEM; (void) mutex_unlock(&nodename_lock); return (myname); }