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 *
_netdir_getbyname(struct netconfig * netconfigp,struct nd_hostserv * nd_hostservp)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 *
_netdir_getbyaddr(struct netconfig * netconfigp,struct netbuf * netbufp)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 *
_taddr2uaddr(struct netconfig * netconfigp,struct netbuf * netbufp)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 *
_uaddr2taddr(struct netconfig * netconfigp,char * uaddr)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
_netdir_options(struct netconfig * netconfigp,int option,int fd,void * par)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
searchhost(struct netconfig * netconfigp,char * token,int field,char * hostbuf)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
searchserv(struct netconfig * netconfigp,char * token,int field,char * servname)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 *
nodename(void)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