xref: /titanic_50/usr/src/lib/nametoaddr/straddr/common/straddr.c (revision fa9e4066f08beec538e775443c5be79dd423fcab)
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