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