xref: /illumos-gate/usr/src/lib/librpcsvc/common/bindresvport.c (revision 355b4669e025ff377602b6fc7caaf30dbc218371)
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 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * Portions of this source code were derived from Berkeley 4.3 BSD
32  * under license from the Regents of the University of California.
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 /*
38  * XXX This routine should be changed to use
39  * ND_CHECK_RESERVED_PORT and ND_SET_RESERVED_PORT
40  * which can be invoked via netdir_options.
41  */
42 #include <stdio.h>
43 #include <rpc/rpc.h>
44 #include <netinet/in.h>
45 #include <sys/socket.h>
46 #include <netdb.h>
47 #include <errno.h>
48 #include <rpc/nettype.h>
49 #include <stropts.h>
50 #include <string.h>
51 #include <tiuser.h>
52 #include <unistd.h>
53 
54 #define	STARTPORT 600
55 #define	ENDPORT (IPPORT_RESERVED - 1)
56 #define	NPORTS	(ENDPORT - STARTPORT + 1)
57 
58 /*
59  * The argument is a client handle for a UDP connection.
60  * Unbind its transport endpoint from the existing port
61  * and rebind it to a reserved port.
62  * On failure, the client handle can be unbound even if it
63  * was previously bound.  Callers should destroy the client
64  * handle after a failure.
65  */
66 int
67 __clnt_bindresvport(cl)
68 	CLIENT *cl;
69 {
70 	int fd;
71 	int res;
72 	short port;
73 	struct sockaddr_in *sin;
74 	struct sockaddr_in6 *sin6;
75 	extern int errno;
76 	/* extern int t_errno; */
77 	struct t_bind *tbind, *tres;
78 	int i;
79 	bool_t	ipv6_fl = FALSE;
80 	struct netconfig *nconf;
81 
82 	/* make sure it's a UDP connection */
83 	nconf = getnetconfigent(cl->cl_netid);
84 	if (nconf == NULL)
85 		return (-1);
86 	if ((nconf->nc_semantics != NC_TPI_CLTS) ||
87 		(strcmp(nconf->nc_protofmly, NC_INET) &&
88 		strcmp(nconf->nc_protofmly, NC_INET)) ||
89 		strcmp(nconf->nc_proto, NC_UDP)) {
90 		freenetconfigent(nconf);
91 		return (0);	/* not udp - don't need resv port */
92 	}
93 	if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
94 		ipv6_fl = TRUE;
95 	freenetconfigent(nconf);
96 
97 	if (!clnt_control(cl, CLGET_FD, (char *)&fd)) {
98 		return (-1);
99 	}
100 
101 	/* If fd is already bound - unbind it */
102 	if (t_getstate(fd) != T_UNBND) {
103 		while ((t_unbind(fd) < 0) && (t_errno == TLOOK)) {
104 			/*
105 			 * If there is a message queued to this descriptor,
106 			 * remove it.
107 			 */
108 			struct strbuf ctl[1], data[1];
109 			char ctlbuf[sizeof (union T_primitives) + 32];
110 			char databuf[256];
111 			int flags;
112 
113 			ctl->maxlen = sizeof (ctlbuf);
114 			ctl->buf = ctlbuf;
115 			data->maxlen = sizeof (databuf);
116 			data->buf = databuf;
117 			flags = 0;
118 			if (getmsg(fd, ctl, data, &flags) < 0)
119 				return (-1);
120 
121 		}
122 		if (t_getstate(fd) != T_UNBND)
123 			return (-1);
124 	}
125 
126 	tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
127 	if (tbind == NULL) {
128 		if (t_errno == TBADF)
129 			errno = EBADF;
130 		return (-1);
131 	}
132 	tres = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
133 	if (tres == NULL) {
134 		(void) t_free((char *)tbind, T_BIND);
135 		return (-1);
136 	}
137 
138 	(void) memset((char *)tbind->addr.buf, 0, tbind->addr.len);
139 	/* warning: this sockaddr_in is truncated to 8 bytes */
140 
141 	if (ipv6_fl == TRUE) {
142 		sin6 = (struct sockaddr_in6 *)tbind->addr.buf;
143 		sin6->sin6_family = AF_INET6;
144 	} else {
145 		sin = (struct sockaddr_in *)tbind->addr.buf;
146 		sin->sin_family = AF_INET;
147 	}
148 
149 	tbind->qlen = 0;
150 	tbind->addr.len = tbind->addr.maxlen;
151 
152 	/*
153 	 * Need to find a reserved port in the interval
154 	 * STARTPORT - ENDPORT.  Choose a random starting
155 	 * place in the interval based on the process pid
156 	 * and sequentially search the ports for one
157 	 * that is available.
158 	 */
159 	port = (getpid() % NPORTS) + STARTPORT;
160 
161 	for (i = 0; i < NPORTS; i++) {
162 		sin->sin_port = htons(port++);
163 		if (port > ENDPORT)
164 			port = STARTPORT;
165 		/*
166 		 * Try to bind to the requested address.  If
167 		 * the call to t_bind succeeds, then we need
168 		 * to make sure that the address that we bound
169 		 * to was the address that we requested.  If it
170 		 * was, then we are done.  If not, we fake an
171 		 * EADDRINUSE error by setting res, t_errno,
172 		 * and errno to indicate that a bind failure
173 		 * occurred.  Otherwise, if the t_bind call
174 		 * failed, we check to see whether it makes
175 		 * sense to continue trying to t_bind requests.
176 		 */
177 		res = t_bind(fd, tbind, tres);
178 		if (res == 0) {
179 			if (memcmp(tbind->addr.buf, tres->addr.buf,
180 					(int)tres->addr.len) == 0)
181 				break;
182 			(void) t_unbind(fd);
183 			res = -1;
184 			t_errno = TSYSERR;
185 			errno = EADDRINUSE;
186 		} else if (t_errno != TSYSERR || errno != EADDRINUSE) {
187 			if (t_errno == TACCES)
188 				errno = EACCES;
189 			break;
190 		}
191 	}
192 
193 	(void) t_free((char *)tbind, T_BIND);
194 	(void) t_free((char *)tres,  T_BIND);
195 	return (res);
196 }
197