Imported Upstream version 0.2.5
[platform/upstream/libtirpc.git] / src / bindresvport.c
1 /*
2  * Copyright (c) 2009, Sun Microsystems, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * - Redistributions of source code must retain the above copyright notice,
8  *   this list of conditions and the following disclaimer.
9  * - Redistributions in binary form must reproduce the above copyright notice,
10  *   this list of conditions and the following disclaimer in the documentation
11  *   and/or other materials provided with the distribution.
12  * - Neither the name of Sun Microsystems, Inc. nor the names of its
13  *   contributors may be used to endorse or promote products derived
14  *   from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30
31 /*
32  * Copyright (c) 1987 by Sun Microsystems, Inc.
33  *
34  * Portions Copyright(C) 1996, Jason Downs.  All rights reserved.
35  */
36
37 #include <sys/types.h>
38 #include <sys/socket.h>
39
40 #include <netinet/in.h>
41
42 #include <errno.h>
43 #include <string.h>
44 #include <unistd.h>
45
46 #include <rpc/rpc.h>
47
48 #include <string.h>
49 #include <reentrant.h>
50
51 extern pthread_mutex_t port_lock;
52
53 /*
54  * Bind a socket to a privileged IP port
55  */
56 int
57 bindresvport(sd, sin)
58         int sd;
59         struct sockaddr_in *sin;
60 {
61         return bindresvport_sa(sd, (struct sockaddr *)sin);
62 }
63
64 #ifdef __linux__
65
66 #define STARTPORT 600
67 #define LOWPORT 512
68 #define ENDPORT (IPPORT_RESERVED - 1)
69 #define NPORTS  (ENDPORT - STARTPORT + 1)
70
71 int
72 bindresvport_sa(sd, sa)
73         int sd;
74         struct sockaddr *sa;
75 {
76         int res, af;
77         struct sockaddr_storage myaddr;
78         struct sockaddr_in *sin;
79 #ifdef INET6
80         struct sockaddr_in6 *sin6;
81 #endif
82         u_int16_t *portp;
83         static u_int16_t port;
84         static short startport = STARTPORT;
85         socklen_t salen;
86         int nports;
87         int endport = ENDPORT;
88         int i;
89
90         mutex_lock(&port_lock);
91         nports = ENDPORT - startport + 1;
92
93         if (sa == NULL) {
94                 salen = sizeof(myaddr);
95                 sa = (struct sockaddr *)&myaddr;
96
97                 if (getsockname(sd, (struct sockaddr *)&myaddr, &salen) == -1) {
98                     mutex_unlock(&port_lock);
99                     return -1;      /* errno is correctly set */
100                 }
101
102                 af = myaddr.ss_family;
103         } else
104                 af = sa->sa_family;
105
106         switch (af) {
107         case AF_INET:
108                 sin = (struct sockaddr_in *)sa;
109                 salen = sizeof(struct sockaddr_in);
110                 port = ntohs(sin->sin_port);
111                 portp = &sin->sin_port;
112                 break;
113 #ifdef INET6
114         case AF_INET6:
115                 sin6 = (struct sockaddr_in6 *)sa;
116                 salen = sizeof(struct sockaddr_in6);
117                 port = ntohs(sin6->sin6_port);
118                 portp = &sin6->sin6_port;
119                 break;
120 #endif
121         default:
122                 errno = EPFNOSUPPORT;
123                 mutex_unlock(&port_lock);
124                 return (-1);
125         }
126         sa->sa_family = af;
127
128         if (port == 0) {
129                 port = (getpid() % NPORTS) + STARTPORT;
130         }
131         res = -1;
132         errno = EADDRINUSE;
133                 again:
134         for (i = 0; i < nports; ++i) {
135                 *portp = htons(port++);
136                  if (port > endport) 
137                         port = startport;
138                 res = bind(sd, sa, salen);
139                 if (res >= 0 || errno != EADDRINUSE)
140                         break;
141         }
142         if (i == nports && startport != LOWPORT) {
143             startport = LOWPORT;
144             endport = STARTPORT - 1;
145             nports = STARTPORT - LOWPORT;
146             port = LOWPORT + port % (STARTPORT - LOWPORT);
147             goto again;
148         }
149         mutex_unlock(&port_lock);
150
151         return (res);
152 }
153 #else
154
155 #define IP_PORTRANGE 19
156 #define IP_PORTRANGE_LOW 2
157
158 /*
159  * Bind a socket to a privileged IP port
160  */
161 int
162 bindresvport_sa(sd, sa)
163         int sd;
164         struct sockaddr *sa;
165 {
166         int old, error, af;
167         struct sockaddr_storage myaddr;
168         struct sockaddr_in *sin;
169 #ifdef INET6
170         struct sockaddr_in6 *sin6;
171 #endif
172         int proto, portrange, portlow;
173         u_int16_t *portp;
174         socklen_t salen;
175
176         if (sa == NULL) {
177                 salen = sizeof(myaddr);
178                 sa = (struct sockaddr *)&myaddr;
179
180                 if (getsockname(sd, sa, &salen) == -1)
181                         return -1;      /* errno is correctly set */
182
183                 af = sa->sa_family;
184                 memset(sa, 0, salen);
185         } else
186                 af = sa->sa_family;
187
188         switch (af) {
189         case AF_INET:
190                 proto = IPPROTO_IP;
191                 portrange = IP_PORTRANGE;
192                 portlow = IP_PORTRANGE_LOW;
193                 sin = (struct sockaddr_in *)sa;
194                 salen = sizeof(struct sockaddr_in);
195                 portp = &sin->sin_port;
196                 break;
197 #ifdef INET6
198         case AF_INET6:
199                 proto = IPPROTO_IPV6;
200                 portrange = IPV6_PORTRANGE;
201                 portlow = IPV6_PORTRANGE_LOW;
202                 sin6 = (struct sockaddr_in6 *)sa;
203                 salen = sizeof(struct sockaddr_in6);
204                 portp = &sin6->sin6_port;
205                 break;
206 #endif
207         default:
208                 errno = EPFNOSUPPORT;
209                 return (-1);
210         }
211         sa->sa_family = af;
212
213         if (*portp == 0) {
214                 socklen_t oldlen = sizeof(old);
215
216                 error = getsockopt(sd, proto, portrange, &old, &oldlen);
217                 if (error < 0)
218                         return (error);
219
220                 error = setsockopt(sd, proto, portrange, &portlow,
221                     sizeof(portlow));
222                 if (error < 0)
223                         return (error);
224         }
225
226         error = bind(sd, sa, salen);
227
228         if (*portp == 0) {
229                 int saved_errno = errno;
230
231                 if (error < 0) {
232                         if (setsockopt(sd, proto, portrange, &old,
233                             sizeof(old)) < 0)
234                                 errno = saved_errno;
235                         return (error);
236                 }
237
238                 if (sa != (struct sockaddr *)&myaddr) {
239                         /* Hmm, what did the kernel assign? */
240                         if (getsockname(sd, sa, &salen) < 0)
241                                 errno = saved_errno;
242                         return (error);
243                 }
244         }
245         return (error);
246 }
247 #endif