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
__clnt_bindresvport(cl)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