xref: /illumos-gate/usr/src/lib/nametoaddr/straddr/common/straddr.c (revision 2983dda76a6d296fdb560c88114fe41caad1b84f)
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