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