xref: /illumos-gate/usr/src/cmd/rpcbind/check_bound.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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  * Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28 /*
29  * University Copyright- Copyright (c) 1982, 1986, 1988
30  * The Regents of the University of California
31  * All Rights Reserved
32  *
33  * University Acknowledgment- Portions of this document are derived from
34  * software developed by the University of California, Berkeley, and its
35  * contributors.
36  */
37 
38 #pragma ident	"%Z%%M%	%I%	%E% SMI"
39 
40 /*
41  * check_bound.c
42  * Checks to see whether the program is still bound to the
43  * claimed address and returns the univeral merged address
44  *
45  */
46 
47 #include <stdio.h>
48 #include <rpc/rpc.h>
49 #include <netconfig.h>
50 #include <netdir.h>
51 #include <sys/syslog.h>
52 #include <stdlib.h>
53 #include "rpcbind.h"
54 #include <string.h>
55 /* the following just to get my address */
56 #include <errno.h>
57 #include <sys/socket.h>
58 #include <netinet/in.h>
59 
60 struct fdlist {
61 	int fd;
62 	struct netconfig *nconf;
63 	struct fdlist *next;
64 	int check_binding;
65 };
66 
67 static struct fdlist *fdhead;	/* Link list of the check fd's */
68 static struct fdlist *fdtail;
69 static char *nullstring = "";
70 
71 /*
72  * Returns 1 if the given address is bound for the given addr & transport
73  * For all error cases, we assume that the address is bound
74  * Returns 0 for success.
75  */
76 static bool_t
77 check_bound(fdl, uaddr)
78 	struct fdlist *fdl;	/* My FD list */
79 	char *uaddr;		/* the universal address */
80 {
81 	int fd;
82 	struct netbuf *na;
83 	struct t_bind taddr, *baddr;
84 	int ans;
85 
86 	if (fdl->check_binding == FALSE)
87 		return (TRUE);
88 
89 	na = uaddr2taddr(fdl->nconf, uaddr);
90 	if (!na)
91 		return (TRUE); /* punt, should never happen */
92 
93 	fd = fdl->fd;
94 	taddr.addr = *na;
95 	taddr.qlen = 1;
96 	baddr = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
97 	if (baddr == NULL) {
98 		netdir_free((char *)na, ND_ADDR);
99 		return (TRUE);
100 	}
101 	if (t_bind(fd, &taddr, baddr) != 0) {
102 		netdir_free((char *)na, ND_ADDR);
103 		(void) t_free((char *)baddr, T_BIND);
104 		return (TRUE);
105 	}
106 	ans = memcmp(taddr.addr.buf, baddr->addr.buf, baddr->addr.len);
107 	netdir_free((char *)na, ND_ADDR);
108 	(void) t_free((char *)baddr, T_BIND);
109 	if (t_unbind(fd) != 0) {
110 		/* Bad fd. Purge this fd */
111 		(void) t_close(fd);
112 		fdl->fd = t_open(fdl->nconf->nc_device, O_RDWR, NULL);
113 		if (fdl->fd == -1)
114 			fdl->check_binding = FALSE;
115 	}
116 	return (ans == 0 ? FALSE : TRUE);
117 }
118 
119 /*
120  * Keep open one more file descriptor for this transport, which
121  * will be used to determine whether the given service is up
122  * or not by trying to bind to the registered address.
123  * We are ignoring errors here. It trashes taddr and baddr;
124  * but that perhaps should not matter.
125  *
126  * We check for the following conditions:
127  *	1. Is it possible for t_bind to fail in the case where
128  *		we bind to an already bound address and have any
129  *		other error number besides TNOADDR.
130  *	2. If a address is specified in bind addr, can I bind to
131  *		the same address.
132  *	3. If NULL is specified in bind addr, can I bind to the
133  *		address to which the fd finally got bound.
134  */
135 int
136 add_bndlist(nconf, taddr, baddr)
137 	struct netconfig *nconf;
138 	struct t_bind *taddr, *baddr;
139 {
140 	int fd;
141 	struct fdlist *fdl;
142 	struct netconfig *newnconf;
143 	struct t_info tinfo;
144 	struct t_bind tmpaddr;
145 
146 	newnconf = getnetconfigent(nconf->nc_netid);
147 	if (newnconf == NULL)
148 		return (-1);
149 	fdl = (struct fdlist *)malloc((uint_t)sizeof (struct fdlist));
150 	if (fdl == NULL) {
151 		freenetconfigent(newnconf);
152 		syslog(LOG_ERR, "no memory!");
153 		return (-1);
154 	}
155 	fdl->nconf = newnconf;
156 	fdl->next = NULL;
157 	if (fdhead == NULL) {
158 		fdhead = fdl;
159 		fdtail = fdl;
160 	} else {
161 		fdtail->next = fdl;
162 		fdtail = fdl;
163 	}
164 	fdl->check_binding = FALSE;
165 	if ((fdl->fd = t_open(nconf->nc_device, O_RDWR, &tinfo)) < 0) {
166 		/*
167 		 * Note that we haven't dequeued this entry nor have we freed
168 		 * the netconfig structure.
169 		 */
170 		if (debugging) {
171 			fprintf(stderr,
172 			    "%s: add_bndlist cannot open connection: %s",
173 			    nconf->nc_netid, t_errlist[t_errno]);
174 		}
175 		return (-1);
176 	}
177 
178 	/* Set the qlen only for cots transports */
179 	switch (tinfo.servtype) {
180 	case T_COTS:
181 	case T_COTS_ORD:
182 		taddr->qlen = 1;
183 		break;
184 	case T_CLTS:
185 		taddr->qlen = 0;
186 		break;
187 	default:
188 		goto error;
189 	}
190 
191 	if (t_bind(fdl->fd, taddr, baddr) != 0) {
192 		if (t_errno == TNOADDR) {
193 			fdl->check_binding = TRUE;
194 			return (0);	/* All is fine */
195 		}
196 		/* Perhaps condition #1 */
197 		if (debugging) {
198 			fprintf(stderr, "%s: add_bndlist cannot bind (1): %s",
199 				nconf->nc_netid, t_errlist[t_errno]);
200 		}
201 		goto not_bound;
202 	}
203 
204 	/* Condition #2 */
205 	if (!memcmp(taddr->addr.buf, baddr->addr.buf,
206 		(int)baddr->addr.len)) {
207 #ifdef BIND_DEBUG
208 		fprintf(stderr, "Condition #2\n");
209 #endif
210 		goto not_bound;
211 	}
212 
213 	/* Condition #3 */
214 	t_unbind(fdl->fd);
215 	/* Set the qlen only for cots transports */
216 	switch (tinfo.servtype) {
217 	case T_COTS:
218 	case T_COTS_ORD:
219 		tmpaddr.qlen = 1;
220 		break;
221 	case T_CLTS:
222 		tmpaddr.qlen = 0;
223 		break;
224 	default:
225 		goto error;
226 	}
227 	tmpaddr.addr.len = tmpaddr.addr.maxlen = 0;
228 	tmpaddr.addr.buf = NULL;
229 	if (t_bind(fdl->fd, &tmpaddr, taddr) != 0) {
230 		if (debugging) {
231 			fprintf(stderr, "%s: add_bndlist cannot bind (2): %s",
232 				nconf->nc_netid, t_errlist[t_errno]);
233 		}
234 		goto error;
235 	}
236 	/* Now fdl->fd is bound to a transport chosen address */
237 	if ((fd = t_open(nconf->nc_device, O_RDWR, &tinfo)) < 0) {
238 		if (debugging) {
239 			fprintf(stderr,
240 				"%s: add_bndlist cannot open connection: %s",
241 				nconf->nc_netid, t_errlist[t_errno]);
242 		}
243 		goto error;
244 	}
245 	if (t_bind(fd, taddr, baddr) != 0) {
246 		if (t_errno == TNOADDR) {
247 			/*
248 			 * This transport is schizo.  Previously it handled a
249 			 * request to bind to an already bound transport by
250 			 * returning a different bind address, and now it's
251 			 * returning a TNOADDR for essentially the same
252 			 * request.  The spec may allow this behavior, so
253 			 * we'll just assume we can't do bind checking with
254 			 * this transport.
255 			 */
256 			goto not_bound;
257 		}
258 		if (debugging) {
259 			fprintf(stderr, "%s: add_bndlist cannot bind (3): %s",
260 				nconf->nc_netid, t_errlist[t_errno]);
261 		}
262 		t_close(fd);
263 		goto error;
264 	}
265 	t_close(fd);
266 	if (!memcmp(taddr->addr.buf, baddr->addr.buf,
267 		(int)baddr->addr.len)) {
268 		switch (tinfo.servtype) {
269 		case T_COTS:
270 		case T_COTS_ORD:
271 			if (baddr->qlen == 1) {
272 #ifdef BIND_DEBUG
273 				fprintf(stderr, "Condition #3\n");
274 #endif
275 				goto not_bound;
276 			}
277 			break;
278 		case T_CLTS:
279 #ifdef BIND_DEBUG
280 			fprintf(stderr, "Condition #3\n");
281 #endif
282 			goto not_bound;
283 		default:
284 			goto error;
285 		}
286 	}
287 
288 	t_unbind(fdl->fd);
289 	fdl->check_binding = TRUE;
290 	return (0);
291 
292 not_bound:
293 	t_close(fdl->fd);
294 	fdl->fd = -1;
295 	return (1);
296 
297 error:
298 	t_close(fdl->fd);
299 	fdl->fd = -1;
300 	return (-1);
301 }
302 
303 bool_t
304 is_bound(netid, uaddr)
305 	char *netid;
306 	char *uaddr;
307 {
308 	struct fdlist *fdl;
309 
310 	for (fdl = fdhead; fdl; fdl = fdl->next)
311 		if (strcmp(fdl->nconf->nc_netid, netid) == 0)
312 			break;
313 	if (fdl == NULL)
314 		return (TRUE);
315 	return (check_bound(fdl, uaddr));
316 }
317 
318 /* Return pointer to port string in the universal address */
319 #define	UADDR_PRT_INDX(UADDR, PORT) { \
320 	PORT = strrchr(UADDR, '.'); \
321 	while (*--PORT != '.'); }
322 /*
323  * Returns NULL if there was some system error.
324  * Returns "" if the address was not bound, i.e the server crashed.
325  * Returns the merged address otherwise.
326  */
327 char *
328 mergeaddr(xprt, netid, uaddr, saddr)
329 	SVCXPRT *xprt;
330 	char *netid;
331 	char *uaddr;
332 	char *saddr;
333 {
334 	struct fdlist *fdl;
335 	struct nd_mergearg ma;
336 	int stat;
337 
338 	for (fdl = fdhead; fdl; fdl = fdl->next)
339 		if (strcmp(fdl->nconf->nc_netid, netid) == 0)
340 			break;
341 	if (fdl == NULL)
342 		return (NULL);
343 	if (check_bound(fdl, uaddr) == FALSE)
344 		/* that server died */
345 		return (nullstring);
346 	/*
347 	 * If saddr is not NULL, the remote client may have included the
348 	 * address by which it contacted us.  Use that for the "client" uaddr,
349 	 * otherwise use the info from the SVCXPRT.
350 	 */
351 	if (saddr != NULL) {
352 		ma.c_uaddr = saddr;
353 	} else {
354 
355 		/* retrieve the client's address */
356 		ma.c_uaddr = taddr2uaddr(fdl->nconf, svc_getrpccaller(xprt));
357 		if (ma.c_uaddr == NULL) {
358 			syslog(LOG_ERR, "taddr2uaddr failed for %s: %s",
359 				fdl->nconf->nc_netid, netdir_sperror());
360 			return (NULL);
361 		}
362 
363 	}
364 #ifdef ND_DEBUG
365 	if (saddr == NULL) {
366 		fprintf(stderr, "mergeaddr: client uaddr = %s\n", ma.c_uaddr);
367 	} else {
368 		fprintf(stderr, "mergeaddr: contact uaddr = %s\n", ma.c_uaddr);
369 	}
370 #endif
371 
372 	/* Not an  INET adaress? */
373 	if ((strcmp(fdl->nconf->nc_protofmly, NC_INET) != 0) &&
374 		(strcmp(fdl->nconf->nc_protofmly, NC_INET6) != 0)) {
375 		ma.s_uaddr = uaddr;
376 #ifdef ND_DEBUG
377 		fprintf(stderr, "mergeaddr: Call to the original"
378 			" ND_MERGEADDR interface\n");
379 #endif
380 		stat = netdir_options(fdl->nconf, ND_MERGEADDR, 0, (char *)&ma);
381 	}
382 	/* Inet address, but no xp_ltaddr */
383 	else if ((ma.s_uaddr = taddr2uaddr(fdl->nconf,
384 				&(xprt)->xp_ltaddr)) == NULL) {
385 		ma.s_uaddr = uaddr;
386 #ifdef ND_DEBUG
387 		fprintf(stderr, "mergeaddr: Call to the original"
388 			" ND_MERGEADDR interface\n");
389 #endif
390 		stat = netdir_options(fdl->nconf, ND_MERGEADDR, 0, (char *)&ma);
391 	} else {
392 		/*
393 		 * (xprt)->xp_ltaddr contains portmap's port address.
394 		 * Overwrite this with actual application's port address
395 		 * before returning to the caller.
396 		 */
397 		char *s_uport, *uport;
398 
399 		/* Get the INET/INET6 address part from ma.s_uaddr */
400 		UADDR_PRT_INDX(ma.s_uaddr, s_uport);
401 		*s_uport = '\0';
402 
403 		/* Get the port info from uaddr */
404 		UADDR_PRT_INDX(uaddr, uport);
405 
406 		ma.m_uaddr = malloc(strlen(ma.s_uaddr) + strlen(uport) + 1);
407 		if (ma.m_uaddr == NULL) {
408 			syslog(LOG_ERR, "mergeaddr: no memory!");
409 			free(ma.s_uaddr);
410 			if (saddr == NULL)
411 				free(ma.c_uaddr);
412 			return (NULL);
413 		}
414 
415 		/* Copy IP address into the Universal address holder */
416 		strcpy(ma.m_uaddr, ma.s_uaddr);
417 		/* Append port info to the Universal address holder */
418 		strcat(ma.m_uaddr, uport);
419 		free(ma.s_uaddr);
420 		stat = 0;
421 
422 #ifdef ND_DEBUG
423 		fprintf(stderr, "mergeaddr: Just return the address which was"
424 			" used for contacting us\n");
425 #endif
426 	}
427 	if (saddr == NULL) {
428 		free(ma.c_uaddr);
429 	}
430 	if (stat) {
431 		syslog(LOG_ERR, "netdir_merge failed for %s: %s",
432 			fdl->nconf->nc_netid, netdir_sperror());
433 		return (NULL);
434 	}
435 #ifdef ND_DEBUG
436 	fprintf(stderr, "mergeaddr: uaddr = %s, merged uaddr = %s\n",
437 				uaddr, ma.m_uaddr);
438 #endif
439 	return (ma.m_uaddr);
440 }
441 
442 /*
443  * Returns a netconf structure from its internal list.  This
444  * structure should not be freed.
445  */
446 struct netconfig *
447 rpcbind_get_conf(netid)
448 	char *netid;
449 {
450 	struct fdlist *fdl;
451 
452 	for (fdl = fdhead; fdl; fdl = fdl->next)
453 		if (strcmp(fdl->nconf->nc_netid, netid) == 0)
454 			break;
455 	if (fdl == NULL)
456 		return (NULL);
457 	return (fdl->nconf);
458 }
459 
460 #ifdef BIND_DEBUG
461 syslog(a, msg, b, c, d)
462 	int a;
463 	char *msg;
464 	caddr_t b, c, d;
465 {
466 	char buf[1024];
467 
468 	sprintf(buf, msg, b, c, d);
469 	fprintf(stderr, "Syslog: %s\n", buf);
470 }
471 #endif
472