xref: /minix/minix/lib/libc/sys/setsockopt.c (revision 08cbf5a0)
1 #include <sys/cdefs.h>
2 #include "namespace.h"
3 #include <lib.h>
4 
5 #include <string.h>
6 #include <assert.h>
7 #include <errno.h>
8 #include <stdio.h>
9 #include <sys/ioctl.h>
10 #include <sys/socket.h>
11 #include <sys/types.h>
12 #include <netinet/tcp.h>
13 
14 #include <net/gen/in.h>
15 #include <net/gen/tcp.h>
16 #include <net/gen/tcp_io.h>
17 #include <net/gen/udp.h>
18 #include <net/gen/udp_io.h>
19 
20 #define DEBUG 0
21 
22 static int _tcp_setsockopt(int sock, int level, int option_name,
23 	const void *option_value, socklen_t option_len);
24 
25 static int _udp_setsockopt(int sock, int level, int option_name,
26 	const void *option_value, socklen_t option_len);
27 
28 static int _uds_setsockopt(int sock, int level, int option_name,
29 	const void *option_value, socklen_t option_len);
30 
31 /*
32  * Set socket options.
33  */
34 static int
35 __setsockopt(int fd, int level, int option_name, const void * option_value,
36 	socklen_t option_len)
37 {
38 	message m;
39 
40 	memset(&m, 0, sizeof(m));
41 	m.m_lc_vfs_sockopt.fd = fd;
42 	m.m_lc_vfs_sockopt.level = level;
43 	m.m_lc_vfs_sockopt.name = option_name;
44 	m.m_lc_vfs_sockopt.buf = (vir_bytes)option_value;
45 	m.m_lc_vfs_sockopt.len = option_len;
46 
47 	return _syscall(VFS_PROC_NR, VFS_SETSOCKOPT, &m);
48 }
49 
50 int setsockopt(int sock, int level, int option_name,
51         const void *option_value, socklen_t option_len)
52 {
53 	int r;
54 	nwio_tcpopt_t tcpopt;
55 	nwio_udpopt_t udpopt;
56 	struct sockaddr_un uds_addr;
57 
58 	r = __setsockopt(sock, level, option_name, option_value, option_len);
59 	if (r != -1 || (errno != ENOTSOCK && errno != ENOSYS))
60 		return r;
61 
62 	r= ioctl(sock, NWIOGTCPOPT, &tcpopt);
63 	if (r != -1 || errno != ENOTTY)
64 	{
65 		if (r == -1)
66 		{
67 			/* Bad file descriptor */
68 			return -1;
69 		}
70 		return _tcp_setsockopt(sock, level, option_name,
71 			option_value, option_len);
72 	}
73 
74 	r= ioctl(sock, NWIOGUDPOPT, &udpopt);
75 	if (r != -1 || errno != ENOTTY)
76 	{
77 		if (r == -1)
78 		{
79 			/* Bad file descriptor */
80 			return -1;
81 		}
82 		return _udp_setsockopt(sock, level, option_name,
83 			option_value, option_len);
84 	}
85 
86 	r= ioctl(sock, NWIOGUDSADDR, &uds_addr);
87 	if (r != -1 || errno != ENOTTY)
88 	{
89 		if (r == -1)
90 		{
91 			/* Bad file descriptor */
92 			return -1;
93 		}
94 		return _uds_setsockopt(sock, level, option_name,
95 			option_value, option_len);
96 	}
97 
98 	errno = ENOTSOCK;
99 	return -1;
100 }
101 
102 static int _tcp_setsockopt(int sock, int level, int option_name,
103 	const void *option_value, socklen_t option_len)
104 {
105 	int i;
106 
107 	if (level == SOL_SOCKET && option_name == SO_REUSEADDR)
108 	{
109 		if (option_len != sizeof(i))
110 		{
111 			errno= EINVAL;
112 			return -1;
113 		}
114 		i= *(const int *)option_value;
115 		if (!i)
116 		{
117 			/* At the moment there is no way to turn off
118 			 * reusing addresses.
119 			 */
120 			errno= ENOSYS;
121 			return -1;
122 		}
123 		return 0;
124 	}
125 	if (level == SOL_SOCKET && option_name == SO_KEEPALIVE)
126 	{
127 		if (option_len != sizeof(i))
128 		{
129 			errno= EINVAL;
130 			return -1;
131 		}
132 		i= *(const int *)option_value;
133 		if (!i)
134 		{
135 			/* At the moment there is no way to turn off
136 			 * keepalives.
137 			 */
138 			errno= ENOSYS;
139 			return -1;
140 		}
141 		return 0;
142 	}
143 	if (level == SOL_SOCKET && option_name == SO_RCVBUF)
144 	{
145 		if (option_len != sizeof(i))
146 		{
147 			errno= EINVAL;
148 			return -1;
149 		}
150 		i= *(const int *)option_value;
151 		if (i > 32*1024)
152 		{
153 			/* The receive buffer is limited to 32K at the moment.
154 			 */
155 			errno= ENOSYS;
156 			return -1;
157 		}
158 		/* There is no way to reduce the receive buffer, do we have to
159 		 * let this call fail for smaller buffers?
160 		 */
161 		return 0;
162 	}
163 	if (level == SOL_SOCKET && option_name == SO_SNDBUF)
164 	{
165 		if (option_len != sizeof(i))
166 		{
167 			errno= EINVAL;
168 			return -1;
169 		}
170 		i= *(const int *)option_value;
171 		if (i > 32*1024)
172 		{
173 			/* The send buffer is limited to 32K at the moment.
174 			 */
175 			errno= ENOSYS;
176 			return -1;
177 		}
178 		/* There is no way to reduce the send buffer, do we have to
179 		 * let this call fail for smaller buffers?
180 		 */
181 		return 0;
182 	}
183 	if (level == IPPROTO_TCP && option_name == TCP_NODELAY)
184 	{
185 		if (option_len != sizeof(i))
186 		{
187 			errno= EINVAL;
188 			return -1;
189 		}
190 		i= *(const int *)option_value;
191 		if (i)
192 		{
193 			/* At the moment there is no way to turn on
194 			 * nodelay.
195 			 */
196 			errno= ENOSYS;
197 			return -1;
198 		}
199 		return 0;
200 	}
201 #if DEBUG
202 	fprintf(stderr, "_tcp_setsocketopt: level %d, name %d\n",
203 		level, option_name);
204 #endif
205 
206 	errno= ENOSYS;
207 	return -1;
208 }
209 
210 static int _udp_setsockopt(int sock, int level, int option_name,
211 	const void *option_value, socklen_t option_len)
212 {
213 #if DEBUG
214 	fprintf(stderr, "_udp_setsocketopt: level %d, name %d\n",
215 		level, option_name);
216 #endif
217 
218 	errno= ENOSYS;
219 	return -1;
220 }
221 
222 
223 static int _uds_setsockopt(int sock, int level, int option_name,
224 	const void *option_value, socklen_t option_len)
225 {
226 	int i;
227 	size_t size;
228 
229 	if (level == SOL_SOCKET && option_name == SO_RCVBUF)
230 	{
231 		if (option_len != sizeof(size))
232 		{
233 			errno= EINVAL;
234 			return -1;
235 		}
236 		size= *(const size_t *)option_value;
237 		return ioctl(sock, NWIOSUDSRCVBUF, &size);
238 	}
239 
240 	if (level == SOL_SOCKET && option_name == SO_SNDBUF)
241 	{
242 		if (option_len != sizeof(size))
243 		{
244 			errno= EINVAL;
245 			return -1;
246 		}
247 		size= *(const size_t *)option_value;
248 		return ioctl(sock, NWIOSUDSSNDBUF, &size);
249 	}
250 
251 	if (level == SOL_SOCKET && option_name == SO_REUSEADDR)
252 	{
253 		if (option_len != sizeof(i))
254 		{
255 			errno= EINVAL;
256 			return -1;
257 		}
258 		i= *(const int *)option_value;
259 		if (!i)
260 		{
261 			/* At the moment there is no way to turn off
262 			 * reusing addresses.
263 			 */
264 			errno= ENOSYS;
265 			return -1;
266 		}
267 		return 0;
268 	}
269 
270 #ifdef SO_PASSCRED
271 	if (level == SOL_SOCKET && option_name == SO_PASSCRED)
272 	{
273 		if (option_len != sizeof(i))
274 		{
275 			errno= EINVAL;
276 			return -1;
277 		}
278 		i= *(const int *)option_value;
279 		if (!i)
280 		{
281 			/* credentials can always be received. */
282 			errno= ENOSYS;
283 			return -1;
284 		}
285 		return 0;
286 	}
287 #endif
288 
289 #if DEBUG
290 	fprintf(stderr, "_uds_setsocketopt: level %d, name %d\n",
291 		level, option_name);
292 #endif
293 
294 	errno= ENOSYS;
295 	return -1;
296 }
297