xref: /openbsd/lib/libc/rpc/bindresvport.c (revision ae5feee3)
1 /*	$OpenBSD: bindresvport.c,v 1.17 2005/12/21 01:40:22 millert Exp $	*/
2 
3 /*
4  * Copyright 1996, Jason Downs.  All rights reserved.
5  * Copyright 1998, Theo de Raadt.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <errno.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 
34 /*
35  * Bind a socket to a privileged IP port
36  */
37 int
38 bindresvport(int sd, struct sockaddr_in *sin)
39 {
40 	return bindresvport_sa(sd, (struct sockaddr *)sin);
41 }
42 
43 /*
44  * Bind a socket to a privileged port for whatever protocol.
45  */
46 int
47 bindresvport_sa(int sd, struct sockaddr *sa)
48 {
49 	int old, error, af;
50 	struct sockaddr_storage myaddr;
51 	struct sockaddr_in *sin;
52 	struct sockaddr_in6 *sin6;
53 	int proto, portrange, portlow;
54 	u_int16_t port;
55 	socklen_t salen;
56 
57 	if (sa == NULL) {
58 		salen = sizeof(myaddr);
59 		sa = (struct sockaddr *)&myaddr;
60 
61 		if (getsockname(sd, sa, &salen) == -1)
62 			return -1;	/* errno is correctly set */
63 
64 		af = sa->sa_family;
65 		memset(&myaddr, 0, salen);
66 	} else
67 		af = sa->sa_family;
68 
69 	if (af == AF_INET) {
70 		proto = IPPROTO_IP;
71 		portrange = IP_PORTRANGE;
72 		portlow = IP_PORTRANGE_LOW;
73 		sin = (struct sockaddr_in *)sa;
74 		salen = sizeof(struct sockaddr_in);
75 		port = sin->sin_port;
76 	} else if (af == AF_INET6) {
77 		proto = IPPROTO_IPV6;
78 		portrange = IPV6_PORTRANGE;
79 		portlow = IPV6_PORTRANGE_LOW;
80 		sin6 = (struct sockaddr_in6 *)sa;
81 		salen = sizeof(struct sockaddr_in6);
82 		port = sin6->sin6_port;
83 	} else {
84 		errno = EPFNOSUPPORT;
85 		return (-1);
86 	}
87 	sa->sa_family = af;
88 	sa->sa_len = salen;
89 
90 	if (port == 0) {
91 		socklen_t oldlen = sizeof(old);
92 
93 		error = getsockopt(sd, proto, portrange, &old, &oldlen);
94 		if (error < 0)
95 			return (error);
96 
97 		error = setsockopt(sd, proto, portrange, &portlow,
98 		    sizeof(portlow));
99 		if (error < 0)
100 			return (error);
101 	}
102 
103 	error = bind(sd, sa, salen);
104 
105 	if (port == 0) {
106 		int saved_errno = errno;
107 
108 		if (error) {
109 			if (setsockopt(sd, proto, portrange, &old,
110 			    sizeof(old)) < 0)
111 				errno = saved_errno;
112 			return (error);
113 		}
114 
115 		if (sa != (struct sockaddr *)&myaddr) {
116 			/* Hmm, what did the kernel assign... */
117 			if (getsockname(sd, sa, &salen) < 0)
118 				errno = saved_errno;
119 			return (error);
120 		}
121 	}
122 	return (error);
123 }
124