xref: /minix3/minix/lib/libc/sys/getsockopt.c (revision 27852ebe53d5bf221cf5058cb7e858fa8fa8895e)
1433d6423SLionel Sambuc #include <sys/cdefs.h>
2433d6423SLionel Sambuc #include "namespace.h"
3c38dbb97SDavid van Moolenbroek #include <lib.h>
4433d6423SLionel Sambuc 
5433d6423SLionel Sambuc #include <assert.h>
6433d6423SLionel Sambuc #include <errno.h>
7433d6423SLionel Sambuc #include <stdio.h>
8433d6423SLionel Sambuc #include <string.h>
9433d6423SLionel Sambuc #include <sys/ioctl.h>
10433d6423SLionel Sambuc #include <sys/socket.h>
11433d6423SLionel Sambuc #include <sys/types.h>
12433d6423SLionel Sambuc #include <sys/ucred.h>
13433d6423SLionel Sambuc #include <netinet/tcp.h>
14433d6423SLionel Sambuc 
15433d6423SLionel Sambuc #include <net/gen/in.h>
16433d6423SLionel Sambuc #include <net/gen/tcp.h>
17433d6423SLionel Sambuc #include <net/gen/tcp_io.h>
18433d6423SLionel Sambuc #include <net/gen/udp.h>
19433d6423SLionel Sambuc #include <net/gen/udp_io.h>
20433d6423SLionel Sambuc 
21433d6423SLionel Sambuc #include <minix/type.h>
22433d6423SLionel Sambuc 
23433d6423SLionel Sambuc #define DEBUG 0
24433d6423SLionel Sambuc 
25433d6423SLionel Sambuc static int _tcp_getsockopt(int sock, int level, int option_name,
26433d6423SLionel Sambuc 	void *__restrict option_value, socklen_t *__restrict option_len);
27433d6423SLionel Sambuc static int _udp_getsockopt(int sock, int level, int option_name,
28433d6423SLionel Sambuc 	void *__restrict option_value, socklen_t *__restrict option_len);
29433d6423SLionel Sambuc static int _uds_getsockopt(int sock, int level, int option_name,
30433d6423SLionel Sambuc 	void *__restrict option_value, socklen_t *__restrict option_len);
31433d6423SLionel Sambuc static void getsockopt_copy(void *return_value, size_t return_len,
32433d6423SLionel Sambuc 	void *__restrict option_value, socklen_t *__restrict option_len);
33433d6423SLionel Sambuc 
34c38dbb97SDavid van Moolenbroek /*
35c38dbb97SDavid van Moolenbroek  * Get socket options.
36c38dbb97SDavid van Moolenbroek  */
37c38dbb97SDavid van Moolenbroek static int
__getsockopt(int fd,int level,int option_name,void * __restrict option_value,socklen_t * __restrict option_len)38c38dbb97SDavid van Moolenbroek __getsockopt(int fd, int level, int option_name,
39c38dbb97SDavid van Moolenbroek 	void * __restrict option_value, socklen_t * __restrict option_len)
40c38dbb97SDavid van Moolenbroek {
41c38dbb97SDavid van Moolenbroek 	message m;
42c38dbb97SDavid van Moolenbroek 
43c38dbb97SDavid van Moolenbroek 	if (option_len == NULL) {
44c38dbb97SDavid van Moolenbroek 		errno = EFAULT;
45c38dbb97SDavid van Moolenbroek 		return -1;
46c38dbb97SDavid van Moolenbroek 	}
47c38dbb97SDavid van Moolenbroek 
48c38dbb97SDavid van Moolenbroek 	memset(&m, 0, sizeof(m));
49c38dbb97SDavid van Moolenbroek 	m.m_lc_vfs_sockopt.fd = fd;
50c38dbb97SDavid van Moolenbroek 	m.m_lc_vfs_sockopt.level = level;
51c38dbb97SDavid van Moolenbroek 	m.m_lc_vfs_sockopt.name = option_name;
52c38dbb97SDavid van Moolenbroek 	m.m_lc_vfs_sockopt.buf = (vir_bytes)option_value;
53c38dbb97SDavid van Moolenbroek 	m.m_lc_vfs_sockopt.len = *option_len;
54c38dbb97SDavid van Moolenbroek 
55c38dbb97SDavid van Moolenbroek 	if (_syscall(VFS_PROC_NR, VFS_GETSOCKOPT, &m) < 0)
56c38dbb97SDavid van Moolenbroek 		return -1;
57c38dbb97SDavid van Moolenbroek 
58c38dbb97SDavid van Moolenbroek 	*option_len = m.m_vfs_lc_socklen.len;
59c38dbb97SDavid van Moolenbroek 	return 0;
60c38dbb97SDavid van Moolenbroek }
61c38dbb97SDavid van Moolenbroek 
getsockopt(int sock,int level,int option_name,void * __restrict option_value,socklen_t * __restrict option_len)62433d6423SLionel Sambuc int getsockopt(int sock, int level, int option_name,
63433d6423SLionel Sambuc         void *__restrict option_value, socklen_t *__restrict option_len)
64433d6423SLionel Sambuc {
65433d6423SLionel Sambuc 	int r;
66433d6423SLionel Sambuc 	nwio_tcpopt_t tcpopt;
67433d6423SLionel Sambuc 	nwio_udpopt_t udpopt;
68433d6423SLionel Sambuc 	struct sockaddr_un uds_addr;
69433d6423SLionel Sambuc 
70c38dbb97SDavid van Moolenbroek 	r = __getsockopt(sock, level, option_name, option_value, option_len);
7184ed480eSDavid van Moolenbroek 	if (r != -1 || (errno != ENOTSOCK && errno != ENOSYS))
72c38dbb97SDavid van Moolenbroek 		return r;
73c38dbb97SDavid van Moolenbroek 
74433d6423SLionel Sambuc 	r= ioctl(sock, NWIOGTCPOPT, &tcpopt);
75433d6423SLionel Sambuc 	if (r != -1 || errno != ENOTTY)
76433d6423SLionel Sambuc 	{
77433d6423SLionel Sambuc 		if (r == -1)
78433d6423SLionel Sambuc 		{
79433d6423SLionel Sambuc 			/* Bad file descriptor */
80433d6423SLionel Sambuc 			return -1;
81433d6423SLionel Sambuc 		}
82433d6423SLionel Sambuc 		return _tcp_getsockopt(sock, level, option_name,
83433d6423SLionel Sambuc 			option_value, option_len);
84433d6423SLionel Sambuc 	}
85433d6423SLionel Sambuc 
86433d6423SLionel Sambuc 	r= ioctl(sock, NWIOGUDPOPT, &udpopt);
87433d6423SLionel Sambuc 	if (r != -1 || errno != ENOTTY)
88433d6423SLionel Sambuc 	{
89433d6423SLionel Sambuc 		if (r == -1)
90433d6423SLionel Sambuc 		{
91433d6423SLionel Sambuc 			/* Bad file descriptor */
92433d6423SLionel Sambuc 			return -1;
93433d6423SLionel Sambuc 		}
94433d6423SLionel Sambuc 		return _udp_getsockopt(sock, level, option_name,
95433d6423SLionel Sambuc 			option_value, option_len);
96433d6423SLionel Sambuc 	}
97433d6423SLionel Sambuc 
98433d6423SLionel Sambuc 	r= ioctl(sock, NWIOGUDSADDR, &uds_addr);
99433d6423SLionel Sambuc 	if (r != -1 || errno != ENOTTY)
100433d6423SLionel Sambuc 	{
101433d6423SLionel Sambuc 		if (r == -1)
102433d6423SLionel Sambuc 		{
103433d6423SLionel Sambuc 			/* Bad file descriptor */
104433d6423SLionel Sambuc 			return -1;
105433d6423SLionel Sambuc 		}
106433d6423SLionel Sambuc 		return _uds_getsockopt(sock, level, option_name,
107433d6423SLionel Sambuc 			option_value, option_len);
108433d6423SLionel Sambuc 	}
109433d6423SLionel Sambuc 
110433d6423SLionel Sambuc 	errno = ENOTSOCK;
111433d6423SLionel Sambuc 	return -1;
112433d6423SLionel Sambuc }
113433d6423SLionel Sambuc 
getsockopt_copy(void * return_value,size_t return_len,void * __restrict option_value,socklen_t * __restrict option_len)114433d6423SLionel Sambuc static void getsockopt_copy(void *return_value, size_t return_len,
115433d6423SLionel Sambuc 	void *__restrict option_value, socklen_t *__restrict option_len)
116433d6423SLionel Sambuc {
117433d6423SLionel Sambuc 	/* copy as much data as possible */
118433d6423SLionel Sambuc 	if (*option_len < return_len)
119433d6423SLionel Sambuc 		memcpy(option_value, return_value, *option_len);
120433d6423SLionel Sambuc 	else
121433d6423SLionel Sambuc 		memcpy(option_value, return_value, return_len);
122433d6423SLionel Sambuc 
123433d6423SLionel Sambuc 	/* return length */
124433d6423SLionel Sambuc 	*option_len = return_len;
125433d6423SLionel Sambuc }
126433d6423SLionel Sambuc 
_tcp_getsockopt(int sock,int level,int option_name,void * __restrict option_value,socklen_t * __restrict option_len)127433d6423SLionel Sambuc static int _tcp_getsockopt(int sock, int level, int option_name,
128433d6423SLionel Sambuc 	void *__restrict option_value, socklen_t *__restrict option_len)
129433d6423SLionel Sambuc {
130433d6423SLionel Sambuc 	int i, r, err;
131433d6423SLionel Sambuc 
132433d6423SLionel Sambuc 	if (level == SOL_SOCKET && option_name == SO_REUSEADDR)
133433d6423SLionel Sambuc 	{
134433d6423SLionel Sambuc 		i = 1;	/* Binds to TIME_WAIT sockets never cause errors */
135433d6423SLionel Sambuc 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
136433d6423SLionel Sambuc 		return 0;
137433d6423SLionel Sambuc 	}
138433d6423SLionel Sambuc 	if (level == SOL_SOCKET && option_name == SO_KEEPALIVE)
139433d6423SLionel Sambuc 	{
140433d6423SLionel Sambuc 		i = 1;	/* Keepalive is always on */
141433d6423SLionel Sambuc 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
142433d6423SLionel Sambuc 		return 0;
143433d6423SLionel Sambuc 	}
144433d6423SLionel Sambuc 	if (level == SOL_SOCKET && option_name == SO_ERROR)
145433d6423SLionel Sambuc 	{
146433d6423SLionel Sambuc 		r = ioctl(sock, NWIOTCPGERROR, &err);
147433d6423SLionel Sambuc 		if (r != 0)
148433d6423SLionel Sambuc 			return r;
149433d6423SLionel Sambuc 
150433d6423SLionel Sambuc 		getsockopt_copy(&err, sizeof(err), option_value, option_len);
151433d6423SLionel Sambuc 		return 0;
152433d6423SLionel Sambuc 	}
153433d6423SLionel Sambuc 	if (level == SOL_SOCKET && option_name == SO_RCVBUF)
154433d6423SLionel Sambuc 	{
155433d6423SLionel Sambuc 		i = 32 * 1024;	/* Receive buffer in the current
156433d6423SLionel Sambuc 		              	 * implementation
157433d6423SLionel Sambuc 				 */
158433d6423SLionel Sambuc 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
159433d6423SLionel Sambuc 		return 0;
160433d6423SLionel Sambuc 	}
161433d6423SLionel Sambuc 	if (level == SOL_SOCKET && option_name == SO_SNDBUF)
162433d6423SLionel Sambuc 	{
163433d6423SLionel Sambuc 		i = 32 * 1024;	/* Send buffer in the current implementation */
164433d6423SLionel Sambuc 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
165433d6423SLionel Sambuc 		return 0;
166433d6423SLionel Sambuc 	}
167433d6423SLionel Sambuc 	if (level == SOL_SOCKET && option_name == SO_TYPE)
168433d6423SLionel Sambuc 	{
169433d6423SLionel Sambuc 		i = SOCK_STREAM;	/* this is a TCP socket */
170433d6423SLionel Sambuc 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
171433d6423SLionel Sambuc 		return 0;
172433d6423SLionel Sambuc 	}
173433d6423SLionel Sambuc 	if (level == IPPROTO_TCP && option_name == TCP_NODELAY)
174433d6423SLionel Sambuc 	{
175433d6423SLionel Sambuc 		i = 0;	/* nodelay is always off */
176433d6423SLionel Sambuc 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
177433d6423SLionel Sambuc 		return 0;
178433d6423SLionel Sambuc 	}
179433d6423SLionel Sambuc #if DEBUG
180433d6423SLionel Sambuc 	fprintf(stderr, "_tcp_getsocketopt: level %d, name %d\n",
181433d6423SLionel Sambuc 		level, option_name);
182433d6423SLionel Sambuc #endif
183433d6423SLionel Sambuc 
184433d6423SLionel Sambuc 	errno= ENOPROTOOPT;
185433d6423SLionel Sambuc 	return -1;
186433d6423SLionel Sambuc }
187433d6423SLionel Sambuc 
_udp_getsockopt(int sock,int level,int option_name,void * __restrict option_value,socklen_t * __restrict option_len)188433d6423SLionel Sambuc static int _udp_getsockopt(int sock, int level, int option_name,
189433d6423SLionel Sambuc 	void *__restrict option_value, socklen_t *__restrict option_len)
190433d6423SLionel Sambuc {
191433d6423SLionel Sambuc 	int i;
192433d6423SLionel Sambuc 
193433d6423SLionel Sambuc 	if (level == SOL_SOCKET && option_name == SO_TYPE)
194433d6423SLionel Sambuc 	{
195433d6423SLionel Sambuc 		i = SOCK_DGRAM;	/* this is a UDP socket */
196433d6423SLionel Sambuc 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
197433d6423SLionel Sambuc 		return 0;
198433d6423SLionel Sambuc 	}
199433d6423SLionel Sambuc #if DEBUG
200433d6423SLionel Sambuc 	fprintf(stderr, "_udp_getsocketopt: level %d, name %d\n",
201433d6423SLionel Sambuc 		level, option_name);
202433d6423SLionel Sambuc #endif
203433d6423SLionel Sambuc 
204433d6423SLionel Sambuc 	errno= ENOSYS;
205433d6423SLionel Sambuc 	return -1;
206433d6423SLionel Sambuc }
207433d6423SLionel Sambuc 
_uds_getsockopt(int sock,int level,int option_name,void * __restrict option_value,socklen_t * __restrict option_len)208433d6423SLionel Sambuc static int _uds_getsockopt(int sock, int level, int option_name,
209433d6423SLionel Sambuc 	void *__restrict option_value, socklen_t *__restrict option_len)
210433d6423SLionel Sambuc {
211433d6423SLionel Sambuc 	int i, r;
212433d6423SLionel Sambuc 	size_t size;
213433d6423SLionel Sambuc 
214433d6423SLionel Sambuc 	if (level == SOL_SOCKET && option_name == SO_RCVBUF)
215433d6423SLionel Sambuc 	{
216433d6423SLionel Sambuc  		r= ioctl(sock, NWIOGUDSRCVBUF, &size);
217433d6423SLionel Sambuc 		if (r == -1) {
218433d6423SLionel Sambuc 			return r;
219433d6423SLionel Sambuc 		}
220433d6423SLionel Sambuc 
221433d6423SLionel Sambuc 		getsockopt_copy(&size, sizeof(size), option_value, option_len);
222433d6423SLionel Sambuc 		return 0;
223433d6423SLionel Sambuc 	}
224433d6423SLionel Sambuc 
225433d6423SLionel Sambuc 	if (level == SOL_SOCKET && option_name == SO_SNDBUF)
226433d6423SLionel Sambuc 	{
227433d6423SLionel Sambuc  		r= ioctl(sock, NWIOGUDSSNDBUF, &size);
228433d6423SLionel Sambuc 		if (r == -1) {
229433d6423SLionel Sambuc 			return r;
230433d6423SLionel Sambuc 		}
231433d6423SLionel Sambuc 
232433d6423SLionel Sambuc 		getsockopt_copy(&size, sizeof(size), option_value, option_len);
233433d6423SLionel Sambuc 		return 0;
234433d6423SLionel Sambuc 	}
235433d6423SLionel Sambuc 
236433d6423SLionel Sambuc 	if (level == SOL_SOCKET && option_name == SO_TYPE)
237433d6423SLionel Sambuc 	{
238433d6423SLionel Sambuc  		r= ioctl(sock, NWIOGUDSSOTYPE, &i);
239433d6423SLionel Sambuc 		if (r == -1) {
240433d6423SLionel Sambuc 			return r;
241433d6423SLionel Sambuc 		}
242433d6423SLionel Sambuc 
243433d6423SLionel Sambuc 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
244433d6423SLionel Sambuc 		return 0;
245433d6423SLionel Sambuc 	}
246433d6423SLionel Sambuc 
247*27852ebeSDavid van Moolenbroek #ifdef SO_PEERCRED
248433d6423SLionel Sambuc 	if (level == SOL_SOCKET && option_name == SO_PEERCRED)
249433d6423SLionel Sambuc 	{
250433d6423SLionel Sambuc 		struct uucred cred;
251433d6423SLionel Sambuc 
252433d6423SLionel Sambuc 		r= ioctl(sock, NWIOGUDSPEERCRED, &cred);
253433d6423SLionel Sambuc 		if (r == -1) {
254433d6423SLionel Sambuc 			return -1;
255433d6423SLionel Sambuc 		}
256433d6423SLionel Sambuc 
257433d6423SLionel Sambuc 		getsockopt_copy(&cred, sizeof(struct uucred), option_value,
258433d6423SLionel Sambuc 							option_len);
259433d6423SLionel Sambuc 		return 0;
260433d6423SLionel Sambuc 	}
261*27852ebeSDavid van Moolenbroek #endif
262433d6423SLionel Sambuc 
263433d6423SLionel Sambuc 
264433d6423SLionel Sambuc 	if (level == SOL_SOCKET && option_name == SO_REUSEADDR)
265433d6423SLionel Sambuc 	{
266433d6423SLionel Sambuc 		i = 1;	/* as long as nobody is listen()ing on the address,
267433d6423SLionel Sambuc 			 * it can be reused without waiting for a
268433d6423SLionel Sambuc 			 * timeout to expire.
269433d6423SLionel Sambuc 			 */
270433d6423SLionel Sambuc 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
271433d6423SLionel Sambuc 		return 0;
272433d6423SLionel Sambuc 	}
273433d6423SLionel Sambuc 
274*27852ebeSDavid van Moolenbroek #ifdef SO_PASSCRED
275433d6423SLionel Sambuc 	if (level == SOL_SOCKET && option_name == SO_PASSCRED)
276433d6423SLionel Sambuc 	{
277433d6423SLionel Sambuc 		i = 1;	/* option is always 'on' */
278433d6423SLionel Sambuc 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
279433d6423SLionel Sambuc 		return 0;
280433d6423SLionel Sambuc 	}
281*27852ebeSDavid van Moolenbroek #endif
282433d6423SLionel Sambuc 
283433d6423SLionel Sambuc #if DEBUG
284433d6423SLionel Sambuc 	fprintf(stderr, "_uds_getsocketopt: level %d, name %d\n",
285433d6423SLionel Sambuc 		level, option_name);
286433d6423SLionel Sambuc #endif
287433d6423SLionel Sambuc 
288433d6423SLionel Sambuc 	errno= ENOSYS;
289433d6423SLionel Sambuc 	return -1;
290433d6423SLionel Sambuc }
291