xref: /illumos-gate/usr/src/lib/nametoaddr/straddr/common/straddr.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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 /*
23  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
24  */
25 
26 /*
27  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
32 /*	 All Rights Reserved 	*/
33 
34 #include <ctype.h>
35 #include <stdio.h>
36 #include <tiuser.h>
37 #include <netdir.h>
38 #include <netconfig.h>
39 #include <sys/utsname.h>
40 #include <sys/param.h>
41 #include <string.h>
42 #include <stdlib.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 #define	LOCALHOST	"localhost"
75 
76 static int searchhost(struct netconfig *, char *, int, char *);
77 static int searchserv(struct netconfig *, char *, int, char *);
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 		if (argp->m_uaddr == NULL) {
391 			_nderror = ND_NOMEM;
392 			return (-1);
393 		}
394 		return (0);
395 	default:
396 		_nderror = ND_NOCTRL;
397 		return (-1);
398 	}
399 }
400 
401 /*
402  *	searchhost() looks for the specified token in the host file.
403  *	The "field" parameter signifies which field to compare the token
404  *	on, and returns all comma separated values associated with the token.
405  */
406 
407 static int
408 searchhost(struct netconfig *netconfigp, char *token, int field, char *hostbuf)
409 {
410 	char searchfile[MAXPATHLEN];  /* the name of file to be opened	    */
411 	char buf[BUFSIZ];	/* holds each line of the file		    */
412 	char *fileaddr;		/* the first token in each line		    */
413 	char *filehost;		/* the second token in each line	    */
414 	char *cmpstr;		/* the string to compare token to	    */
415 	char *retstr;		/* the string to return if compare succeeds */
416 	char *nexttok;		/* next token to process		    */
417 	FILE *fp;		/* the opened searchfile		    */
418 	int   nelements = 0;	/* total number of elements found	    */
419 	struct utsname utsname;
420 
421 	/*
422 	 *	Unless /etc/netconfig has been altered, the only transport that
423 	 *	will use straddr.so is loopback.  In this case, we always
424 	 *	return "localhost" if either our nodename, or "localhost", or
425 	 *	some of special-case host names were passed, or we fail.
426 	 */
427 
428 	if ((strcmp(token, HOST_SELF_BIND) == 0) ||
429 	    (strcmp(token, HOST_SELF_CONNECT) == 0) ||
430 	    (strcmp(token, HOST_ANY) == 0) ||
431 	    (strcmp(token, LOCALHOST) == 0) ||
432 	    (uname(&utsname) >= 0 && strcmp(token, utsname.nodename) == 0)) {
433 		(void) strcpy(hostbuf, LOCALHOST);
434 		return (1);
435 	}
436 
437 	if (strcmp(netconfigp->nc_protofmly, NC_LOOPBACK) == 0)
438 		return (0);
439 
440 	/*
441 	 * 	We only get here if an administrator has modified
442 	 * 	/etc/netconfig to use straddr.so for a transport other than
443 	 * 	loopback (which is questionable but something we'll need to
444 	 * 	EOL at a later point in time).  In this case, we fallback to
445 	 * 	searching for the associated key in the appropriate hosts
446 	 * 	file (based on nc_netid).
447 	 */
448 
449 	(void) snprintf(searchfile, sizeof (searchfile), HOSTFILE,
450 	    netconfigp->nc_netid);
451 
452 	fp = fopen(searchfile, "rF");
453 	if (fp == NULL)
454 		return (0);
455 
456 	/*
457 	 *	Loop through the file looking for the tokens and creating
458 	 *	the list of strings to be returned.
459 	 */
460 
461 	while (fgets(buf, BUFSIZ, fp) != NULL) {
462 
463 		/*
464 		 *	Ignore comments and bad lines.
465 		 */
466 
467 		fileaddr = strtok_r(buf, " \t\n", &nexttok);
468 		if (fileaddr == NULL || *fileaddr == '#')
469 			continue;
470 
471 		if ((filehost = strtok_r(NULL, " \t\n", &nexttok)) == NULL)
472 			continue;
473 
474 		/*
475 		 *	determine which to compare the token to, then
476 		 *	compare it, and if they match, add the return
477 		 *	string to the list.
478 		 */
479 
480 		cmpstr = (field == FIELD1)? fileaddr : filehost;
481 		retstr = (field == FIELD1)? filehost : fileaddr;
482 
483 		if (strcmp(token, cmpstr) == 0) {
484 			nelements++;
485 			if (field == FIELD2) {
486 				/*
487 				 * called by _netdir_getbyname
488 				 */
489 
490 				(void) strcpy(hostbuf, retstr);
491 				break;
492 			}
493 			if (nelements > 1) {
494 				/*
495 				 * Assuming that "," will never be a part
496 				 * of any host name.
497 				 */
498 				(void) strcat(hostbuf, ",");
499 			}
500 			(void) strcat(hostbuf, retstr);
501 		}
502 	}
503 
504 	(void) fclose(fp);
505 	return (nelements);
506 }
507 
508 /*
509  *	searchserv() looks for the specified token in the service file.
510  *	The "field" parameter signifies which field to compare the token
511  *	on, and returns the string associated with the token in servname.
512  */
513 
514 static int
515 searchserv(struct netconfig *netconfigp, char *token, int field, char *servname)
516 {
517 	char searchfile[MAXPATHLEN];  /* the name of file to be opened  */
518 	char buf[BUFSIZ];	/* buffer space for lines in file	*/
519 	char *fileservice;	/* the first token in each line		*/
520 	char *fileport;		/* the second token in each line	*/
521 	char *cmpstr;		/* the string to compare the token to	*/
522 	char *retstr;		/* temporarily hold token in line of file */
523 	char *nexttok;		/* next token to process		*/
524 	FILE *fp;		/* the opened searchfile		*/
525 
526 	(void) snprintf(searchfile, sizeof (searchfile), SERVICEFILE,
527 	    netconfigp->nc_netid);
528 
529 	fp = fopen(searchfile, "rF");
530 	if (fp == NULL)
531 		return (0);
532 
533 	/*
534 	 *	Loop through the services file looking for the token.
535 	 */
536 
537 	while (fgets(buf, BUFSIZ, fp) != NULL) {
538 		/*
539 		 *	If comment or bad line, continue.
540 		 */
541 		fileservice = strtok_r(buf, " \t\n", &nexttok);
542 		if (fileservice == NULL || *fileservice == '#')
543 			continue;
544 
545 		if ((fileport = strtok_r(NULL, " \t\n", &nexttok)) == NULL)
546 			continue;
547 
548 		cmpstr = (field == FIELD1)? fileservice : fileport;
549 		retstr = (field == FIELD1)? fileport : fileservice;
550 
551 		if (strcmp(token, cmpstr) == 0) {
552 			(void) strcpy(servname, retstr);
553 			(void) fclose(fp);
554 			return (1);
555 		}
556 	}
557 
558 	(void) fclose(fp);
559 	return (0);
560 }
561