xref: /illumos-gate/usr/src/cmd/ypcmd/rpc_bootstrap.c (revision d3d50737e566cade9a08d73d2af95105ac7cd960)
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  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
22  * Use is subject to license terms.
23  */
24 
25 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
26 /*	  All Rights Reserved   */
27 
28 /*
29  * Portions of this source code were derived from Berkeley
30  * under license from the Regents of the University of
31  * California.
32  */
33 
34 #pragma ident	"%Z%%M%	%I%	%E% SMI"
35 
36 #include <sys/types.h>
37 #include <sys/file.h>
38 #include <stdlib.h>
39 #include <ctype.h>
40 #include <string.h>
41 #include <tiuser.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
44 #include <sys/socket.h>
45 #include <netdir.h>
46 #include <netdb.h>
47 #include <rpc/rpc.h>
48 #include <rpc/pmap_clnt.h>
49 #include <rpcsvc/nis.h>
50 
51 CLIENT *__clnt_tp_create_bootstrap();
52 int __rpcb_getaddr_bootstrap();
53 struct hostent *__files_gethostbyname(char *, sa_family_t);
54 
55 extern int hostNotKnownLocally;
56 
57 static char *__map_addr();
58 static struct hostent host;
59 static char hostaddr[sizeof (struct in6_addr)];
60 static char *host_aliases[MAXALIASES];
61 static char *host_addrs[] = {
62 	hostaddr,
63 	NULL
64 };
65 
66 /*
67  * __clnt_tp_create_bootstrap()
68  *
69  * This routine is NOT TRANSPORT INDEPENDENT.
70  *
71  * It relies on the local /etc/hosts file for hostname to address
72  * translation and does it itself instead of calling netdir_getbyname
73  * thereby avoids recursion.  Secondarily, it will use a validated
74  * IP address directly.
75  */
76 CLIENT *
77 __clnt_tp_create_bootstrap(hostname, prog, vers, nconf)
78 	char *hostname;
79 	ulong_t prog, vers;
80 	struct netconfig    *nconf;
81 {
82 	CLIENT *cl;
83 	struct netbuf	*svc_taddr;
84 	struct sockaddr_in6	*sa;
85 	int fd;
86 
87 	if (nconf == (struct netconfig *)NULL) {
88 		rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
89 		return (NULL);
90 	}
91 	if ((fd = t_open(nconf->nc_device, O_RDWR, NULL)) == -1) {
92 		rpc_createerr.cf_stat = RPC_TLIERROR;
93 		return (NULL);
94 	}
95 	svc_taddr = (struct netbuf *)malloc(sizeof (struct netbuf));
96 	if (! svc_taddr) {
97 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
98 		t_close(fd);
99 		return (NULL);
100 	}
101 	sa = (struct sockaddr_in6 *)calloc(1, sizeof (*sa));
102 	if (! sa) {
103 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
104 		t_close(fd);
105 		free(svc_taddr);
106 		return (NULL);
107 	}
108 	svc_taddr->maxlen = svc_taddr->len = sizeof (*sa);
109 	svc_taddr->buf = (char *)sa;
110 	if (__rpcb_getaddr_bootstrap(prog,
111 		vers, nconf, svc_taddr, hostname) == FALSE) {
112 		t_close(fd);
113 		free(svc_taddr);
114 		free(sa);
115 		return (NULL);
116 	}
117 	rpc_createerr.cf_stat = RPC_SUCCESS;
118 	cl = __nis_clnt_create(fd, nconf, 0, svc_taddr, 0, prog, vers, 0, 0);
119 	if (cl == 0) {
120 		if (rpc_createerr.cf_stat == RPC_SUCCESS)
121 			rpc_createerr.cf_stat = RPC_TLIERROR;
122 		t_close(fd);
123 	}
124 	free(svc_taddr);
125 	free(sa);
126 	return (cl);
127 }
128 
129 /*
130  * __rpcb_getaddr_bootstrap()
131  *
132  * This is our internal function that replaces rpcb_getaddr(). We
133  * build our own to prevent calling netdir_getbyname() which could
134  * recurse to the nameservice.
135  */
136 int
137 __rpcb_getaddr_bootstrap(program, version, nconf, address, hostname)
138 	ulong_t program;
139 	ulong_t version;
140 	struct netconfig *nconf;
141 	struct netbuf *address; /* populate with the taddr of the service */
142 	char *hostname;
143 {
144 	char *svc_uaddr;
145 	struct hostent *hent, tmphent;
146 	struct sockaddr_in *sa;
147 	struct sockaddr_in6 *sa6;
148 	struct netbuf rpcb_taddr;
149 	struct sockaddr_in local_sa;
150 	struct sockaddr_in6 local_sa6;
151 	in_port_t inport;
152 	int p1, p2;
153 	char *ipaddr, *port;
154 	int i, ipaddrlen;
155 	sa_family_t type;
156 	char addr[sizeof (in6_addr_t)];
157 	char *tmphost_addrs[2];
158 
159 	if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
160 		type = AF_INET6;
161 	} else if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
162 		type = AF_INET;
163 	} else {
164 		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
165 		return (FALSE);
166 	}
167 
168 	/* Get the address of the RPCBIND at hostname */
169 	hent = __files_gethostbyname(hostname, type);
170 	if (hent == (struct hostent *)NULL) {
171 		/* Make sure this is not an IP address before giving up */
172 		if (inet_pton(type, hostname, addr) == 1) {
173 			/* This is a numeric address, fill in the blanks */
174 			hent = &tmphent;
175 			memset(&tmphent, 0, sizeof (struct hostent));
176 			hent->h_addrtype = type;
177 			hent->h_length = (type == AF_INET6) ?
178 			    sizeof (in6_addr_t) : sizeof (in_addr_t);
179 			hent->h_addr_list = tmphost_addrs;
180 			tmphost_addrs[0] = addr;
181 			tmphost_addrs[1] = NULL;
182 		} else {
183 			rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
184 			hostNotKnownLocally = 1;
185 			return (FALSE);
186 		}
187 	}
188 
189 	switch (hent->h_addrtype) {
190 	case AF_INET:
191 		local_sa.sin_family = AF_INET;
192 		local_sa.sin_port = htons(111); /* RPCBIND port */
193 		memcpy((char *)&(local_sa.sin_addr.s_addr),
194 		    hent->h_addr_list[0], hent->h_length);
195 		rpcb_taddr.buf = (char *)&local_sa;
196 		rpcb_taddr.maxlen = sizeof (local_sa);
197 		rpcb_taddr.len = rpcb_taddr.maxlen;
198 		break;
199 	case AF_INET6:
200 		local_sa6.sin6_family = AF_INET6;
201 		local_sa6.sin6_port = htons(111); /* RPCBIND port */
202 		memcpy((char *)&(local_sa6.sin6_addr.s6_addr),
203 		    hent->h_addr_list[0], hent->h_length);
204 		rpcb_taddr.buf = (char *)&local_sa6;
205 		rpcb_taddr.maxlen = sizeof (local_sa6);
206 		rpcb_taddr.len = rpcb_taddr.maxlen;
207 		break;
208 	default:
209 		rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
210 		return (FALSE);
211 	}
212 
213 	svc_uaddr = __map_addr(nconf, &rpcb_taddr, program, version);
214 	if (! svc_uaddr)
215 		return (FALSE);
216 
217 /* do a local uaddr2taddr and stuff in the memory supplied by the caller */
218 	ipaddr = svc_uaddr;
219 	ipaddrlen = strlen(ipaddr);
220 	/* Look for the first '.' starting from the end */
221 	for (i = ipaddrlen-1; i >= 0; i--)
222 		if (ipaddr[i] == '.')
223 			break;
224 	/* Find the second dot (still counting from the end) */
225 	for (i--; i >= 0; i--)
226 		if (ipaddr[i] == '.')
227 			break;
228 	/* If we didn't find it, the uaddr has a syntax error */
229 	if (i < 0) {
230 		rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
231 		return (FALSE);
232 	}
233 	port = &ipaddr[i+1];
234 	ipaddr[i] = '\0';
235 	sscanf(port, "%d.%d", &p1, &p2);
236 	inport = (p1 << 8) + p2;
237 	if (hent->h_addrtype == AF_INET) {
238 		sa = (struct sockaddr_in *)address->buf;
239 		address->len = sizeof (*sa);
240 		if (inet_pton(AF_INET, ipaddr, &sa->sin_addr) != 1) {
241 			rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
242 			return (FALSE);
243 		}
244 		sa->sin_port = htons(inport);
245 		sa->sin_family = AF_INET;
246 	} else {
247 		sa6 = (struct sockaddr_in6 *)address->buf;
248 		address->len = sizeof (*sa6);
249 		if (inet_pton(AF_INET6, ipaddr, &sa6->sin6_addr) != 1) {
250 			rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
251 			return (FALSE);
252 		}
253 		sa6->sin6_port = htons(inport);
254 		sa6->sin6_family = AF_INET6;
255 	}
256 	return (TRUE);
257 }
258 
259 /*
260  * __map_addr()
261  *
262  */
263 static char *
264 __map_addr(nc, rpcb_taddr, prog, ver)
265 	struct netconfig	*nc;		/* Our transport	*/
266 	struct netbuf		*rpcb_taddr;	/* RPCBIND address */
267 	ulong_t			prog, ver;	/* Name service Prog/vers */
268 {
269 	register CLIENT *client;
270 	RPCB 		parms;		/* Parameters for RPC binder	  */
271 	enum clnt_stat	clnt_st;	/* Result from the rpc call	  */
272 	int		fd;		/* Stream file descriptor	  */
273 	char 		*ua = NULL;	/* Universal address of service	  */
274 	struct timeval	tv;		/* Timeout for our rpcb call	  */
275 
276 	/*
277 	 * First we open a connection to the remote rpcbind process.
278 	 */
279 	if ((fd = t_open(nc->nc_device, O_RDWR, NULL)) == -1) {
280 		rpc_createerr.cf_stat = RPC_TLIERROR;
281 		return (NULL);
282 	}
283 
284 	client = __nis_clnt_create(fd, nc, 0, rpcb_taddr, 0,
285 	    RPCBPROG, RPCBVERS, 0, 0);
286 	if (!client) {
287 		t_close(fd);
288 		rpc_createerr.cf_stat = RPC_TLIERROR;
289 		return (NULL);
290 	}
291 
292 	/*
293 	 * Now make the call to get the NIS service address.
294 	 */
295 	tv.tv_sec = 10;
296 	tv.tv_usec = 0;
297 	parms.r_prog = prog;
298 	parms.r_vers = ver;
299 	parms.r_netid = nc->nc_netid;	/* not needed */
300 	parms.r_addr = "";	/* not needed; just for xdring */
301 	parms.r_owner = "";	/* not needed; just for xdring */
302 	clnt_st = clnt_call(client, RPCBPROC_GETADDR, xdr_rpcb, (char *)&parms,
303 	    xdr_wrapstring, (char *)&ua, tv);
304 
305 	rpc_createerr.cf_stat = clnt_st;
306 	if (clnt_st == RPC_SUCCESS) {
307 
308 		clnt_destroy(client);
309 		t_close(fd);
310 		if (*ua == '\0') {
311 			xdr_free(xdr_wrapstring, (char *)&ua);
312 			return (NULL);
313 		}
314 		return (ua);
315 	} else if (((clnt_st == RPC_PROGVERSMISMATCH) ||
316 	    (clnt_st == RPC_PROGUNAVAIL) ||
317 	    (clnt_st == RPC_TIMEDOUT)) &&
318 	    (strcmp(nc->nc_protofmly, NC_INET) == 0)) {
319 		/*
320 		 * version 3 not available. Try version 2
321 		 * The assumption here is that the netbuf
322 		 * is arranged in the sockaddr_in
323 		 * style for IP cases.
324 		 */
325 		ushort_t	port;
326 		struct sockaddr_in	*sa;
327 		struct netbuf 		remote;
328 		int		protocol;
329 		char	buf[32];
330 		char	*res;
331 
332 		clnt_control(client, CLGET_SVC_ADDR, (char *)&remote);
333 		sa = (struct sockaddr_in *)(remote.buf);
334 		protocol = strcmp(nc->nc_proto, NC_TCP) ? IPPROTO_UDP :
335 		    IPPROTO_TCP;
336 		port = (ushort_t)pmap_getport(sa, prog, ver, protocol);
337 
338 		if (port != 0) {
339 			/* print s_addr (and port) in host byte order */
340 			sa->sin_addr.s_addr = ntohl(sa->sin_addr.s_addr);
341 			sprintf(buf, "%d.%d.%d.%d.%d.%d",
342 			    (sa->sin_addr.s_addr >> 24) & 0xff,
343 			    (sa->sin_addr.s_addr >> 16) & 0xff,
344 			    (sa->sin_addr.s_addr >>  8) & 0xff,
345 			    (sa->sin_addr.s_addr) & 0xff,
346 			    (port >> 8) & 0xff,
347 			    port & 0xff);
348 			res = strdup(buf);
349 			if (res != 0) {
350 				rpc_createerr.cf_stat = RPC_SUCCESS;
351 			} else {
352 				rpc_createerr.cf_stat = RPC_SYSTEMERROR;
353 			}
354 		} else {
355 			rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
356 			res = NULL;
357 		}
358 		clnt_destroy(client);
359 		t_close(fd);
360 		return (res);
361 	}
362 	clnt_destroy(client);
363 	t_close(fd);
364 	return (NULL);
365 }
366 
367 #define	bcmp(s1, s2, len)	memcmp(s1, s2, len)
368 #define	bcopy(s1, s2, len)	memcpy(s2, s1, len)
369 
370 #define	MAXALIASES	35
371 
372 static char line[BUFSIZ+1];
373 
374 static char *_hosts4_6[] = { "/etc/inet/hosts", "/etc/inet/ipnodes", 0 };
375 
376 static char *any();
377 
378 static struct hostent *__files_gethostent();
379 
380 struct hostent *
381 __files_gethostbyname(char *nam, sa_family_t af)
382 {
383 	register struct hostent *hp;
384 	register char **cp;
385 	char **file = _hosts4_6;
386 	FILE *hostf;
387 
388 	if ((af != AF_INET) && (af != AF_INET6))
389 		return (0);
390 
391 	for (; *file != 0; file++) {
392 
393 		if ((hostf = fopen(*file, "r")) == 0)
394 			continue;
395 
396 		while (hp = __files_gethostent(hostf)) {
397 			if (hp->h_addrtype != af)
398 				continue;
399 			if (strcasecmp(hp->h_name, nam) == 0) {
400 				(void) fclose(hostf);
401 				return (hp);
402 			}
403 			for (cp = hp->h_aliases; cp != 0 && *cp != 0; cp++)
404 				if (strcasecmp(*cp, nam) == 0) {
405 					(void) fclose(hostf);
406 					return (hp);
407 				}
408 		}
409 
410 		(void) fclose(hostf);
411 	}
412 
413 	return (0);
414 }
415 
416 #define	isV6Addr(s)	(strchr(s, (int)':') != 0)
417 
418 static struct hostent *
419 __files_gethostent(FILE *hostf)
420 {
421 	char *p;
422 	register char *cp, **q;
423 	struct in6_addr in6;
424 	struct in_addr in4;
425 	void *addr;
426 	sa_family_t af;
427 	int len;
428 
429 	if (hostf == NULL)
430 		return (NULL);
431 again:
432 	if ((p = fgets(line, BUFSIZ, hostf)) == NULL)
433 		return (NULL);
434 	if (*p == '#')
435 		goto again;
436 	cp = any(p, "#\n");
437 	if (cp == NULL)
438 		goto again;
439 	*cp = '\0';
440 	cp = any(p, " \t");
441 	if (cp == NULL)
442 		goto again;
443 	*cp++ = '\0';
444 	/* THIS STUFF IS INTERNET SPECIFIC */
445 	host.h_addr_list = host_addrs;
446 	if (isV6Addr(p)) {
447 		af = AF_INET6;
448 		addr = (void *)&in6;
449 		len = sizeof (in6);
450 	} else {
451 		af = AF_INET;
452 		addr = (void *)&in4;
453 		len = sizeof (in4);
454 	}
455 	if (inet_pton(af, p, addr) != 1)
456 		goto again;
457 	bcopy(addr, host.h_addr_list[0], len);
458 	host.h_length = len;
459 	host.h_addrtype = af;
460 	while (*cp == ' ' || *cp == '\t')
461 		cp++;
462 	host.h_name = cp;
463 	q = host.h_aliases = host_aliases;
464 	cp = any(cp, " \t");
465 	if (cp != NULL)
466 		*cp++ = '\0';
467 	while (cp && *cp) {
468 		if (*cp == ' ' || *cp == '\t') {
469 			cp++;
470 			continue;
471 		}
472 		if (q < &host_aliases[MAXALIASES - 1])
473 			*q++ = cp;
474 		cp = any(cp, " \t");
475 		if (cp != NULL)
476 			*cp++ = '\0';
477 	}
478 	*q = NULL;
479 	return (&host);
480 }
481 
482 static char *
483 any(cp, match)
484 	register char *cp;
485 	char *match;
486 {
487 	register char *mp, c;
488 
489 	while (c = *cp) {
490 		for (mp = match; *mp; mp++)
491 			if (*mp == c)
492 				return (cp);
493 		cp++;
494 	}
495 	return ((char *)0);
496 }
497