xref: /minix/minix/lib/libc/sys/getsockopt.c (revision 90b80121)
1 #include <sys/cdefs.h>
2 #include "namespace.h"
3 #include <lib.h>
4 
5 #include <assert.h>
6 #include <errno.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <sys/ioctl.h>
10 #include <sys/socket.h>
11 #include <sys/types.h>
12 #include <sys/ucred.h>
13 #include <netinet/tcp.h>
14 
15 #include <net/gen/in.h>
16 #include <net/gen/tcp.h>
17 #include <net/gen/tcp_io.h>
18 #include <net/gen/udp.h>
19 #include <net/gen/udp_io.h>
20 
21 #include <minix/type.h>
22 
23 #define DEBUG 0
24 
25 static int _tcp_getsockopt(int sock, int level, int option_name,
26 	void *__restrict option_value, socklen_t *__restrict option_len);
27 static int _udp_getsockopt(int sock, int level, int option_name,
28 	void *__restrict option_value, socklen_t *__restrict option_len);
29 static int _uds_getsockopt(int sock, int level, int option_name,
30 	void *__restrict option_value, socklen_t *__restrict option_len);
31 static void getsockopt_copy(void *return_value, size_t return_len,
32 	void *__restrict option_value, socklen_t *__restrict option_len);
33 
34 /*
35  * Get socket options.
36  */
37 static int
38 __getsockopt(int fd, int level, int option_name,
39 	void * __restrict option_value, socklen_t * __restrict option_len)
40 {
41 	message m;
42 
43 	if (option_len == NULL) {
44 		errno = EFAULT;
45 		return -1;
46 	}
47 
48 	memset(&m, 0, sizeof(m));
49 	m.m_lc_vfs_sockopt.fd = fd;
50 	m.m_lc_vfs_sockopt.level = level;
51 	m.m_lc_vfs_sockopt.name = option_name;
52 	m.m_lc_vfs_sockopt.buf = (vir_bytes)option_value;
53 	m.m_lc_vfs_sockopt.len = *option_len;
54 
55 	if (_syscall(VFS_PROC_NR, VFS_GETSOCKOPT, &m) < 0)
56 		return -1;
57 
58 	*option_len = m.m_vfs_lc_socklen.len;
59 	return 0;
60 }
61 
62 int getsockopt(int sock, int level, int option_name,
63         void *__restrict option_value, socklen_t *__restrict option_len)
64 {
65 	int r;
66 	nwio_tcpopt_t tcpopt;
67 	nwio_udpopt_t udpopt;
68 	struct sockaddr_un uds_addr;
69 
70 	r = __getsockopt(sock, level, option_name, option_value, option_len);
71 	if (r != -1 || (errno != ENOTSOCK && errno != ENOSYS))
72 		return r;
73 
74 	r= ioctl(sock, NWIOGTCPOPT, &tcpopt);
75 	if (r != -1 || errno != ENOTTY)
76 	{
77 		if (r == -1)
78 		{
79 			/* Bad file descriptor */
80 			return -1;
81 		}
82 		return _tcp_getsockopt(sock, level, option_name,
83 			option_value, option_len);
84 	}
85 
86 	r= ioctl(sock, NWIOGUDPOPT, &udpopt);
87 	if (r != -1 || errno != ENOTTY)
88 	{
89 		if (r == -1)
90 		{
91 			/* Bad file descriptor */
92 			return -1;
93 		}
94 		return _udp_getsockopt(sock, level, option_name,
95 			option_value, option_len);
96 	}
97 
98 	r= ioctl(sock, NWIOGUDSADDR, &uds_addr);
99 	if (r != -1 || errno != ENOTTY)
100 	{
101 		if (r == -1)
102 		{
103 			/* Bad file descriptor */
104 			return -1;
105 		}
106 		return _uds_getsockopt(sock, level, option_name,
107 			option_value, option_len);
108 	}
109 
110 	errno = ENOTSOCK;
111 	return -1;
112 }
113 
114 static void getsockopt_copy(void *return_value, size_t return_len,
115 	void *__restrict option_value, socklen_t *__restrict option_len)
116 {
117 	/* copy as much data as possible */
118 	if (*option_len < return_len)
119 		memcpy(option_value, return_value, *option_len);
120 	else
121 		memcpy(option_value, return_value, return_len);
122 
123 	/* return length */
124 	*option_len = return_len;
125 }
126 
127 static int _tcp_getsockopt(int sock, int level, int option_name,
128 	void *__restrict option_value, socklen_t *__restrict option_len)
129 {
130 	int i, r, err;
131 
132 	if (level == SOL_SOCKET && option_name == SO_REUSEADDR)
133 	{
134 		i = 1;	/* Binds to TIME_WAIT sockets never cause errors */
135 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
136 		return 0;
137 	}
138 	if (level == SOL_SOCKET && option_name == SO_KEEPALIVE)
139 	{
140 		i = 1;	/* Keepalive is always on */
141 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
142 		return 0;
143 	}
144 	if (level == SOL_SOCKET && option_name == SO_ERROR)
145 	{
146 		r = ioctl(sock, NWIOTCPGERROR, &err);
147 		if (r != 0)
148 			return r;
149 
150 		getsockopt_copy(&err, sizeof(err), option_value, option_len);
151 		return 0;
152 	}
153 	if (level == SOL_SOCKET && option_name == SO_RCVBUF)
154 	{
155 		i = 32 * 1024;	/* Receive buffer in the current
156 		              	 * implementation
157 				 */
158 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
159 		return 0;
160 	}
161 	if (level == SOL_SOCKET && option_name == SO_SNDBUF)
162 	{
163 		i = 32 * 1024;	/* Send buffer in the current implementation */
164 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
165 		return 0;
166 	}
167 	if (level == SOL_SOCKET && option_name == SO_TYPE)
168 	{
169 		i = SOCK_STREAM;	/* this is a TCP socket */
170 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
171 		return 0;
172 	}
173 	if (level == IPPROTO_TCP && option_name == TCP_NODELAY)
174 	{
175 		i = 0;	/* nodelay is always off */
176 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
177 		return 0;
178 	}
179 #if DEBUG
180 	fprintf(stderr, "_tcp_getsocketopt: level %d, name %d\n",
181 		level, option_name);
182 #endif
183 
184 	errno= ENOPROTOOPT;
185 	return -1;
186 }
187 
188 static int _udp_getsockopt(int sock, int level, int option_name,
189 	void *__restrict option_value, socklen_t *__restrict option_len)
190 {
191 	int i;
192 
193 	if (level == SOL_SOCKET && option_name == SO_TYPE)
194 	{
195 		i = SOCK_DGRAM;	/* this is a UDP socket */
196 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
197 		return 0;
198 	}
199 #if DEBUG
200 	fprintf(stderr, "_udp_getsocketopt: level %d, name %d\n",
201 		level, option_name);
202 #endif
203 
204 	errno= ENOSYS;
205 	return -1;
206 }
207 
208 static int _uds_getsockopt(int sock, int level, int option_name,
209 	void *__restrict option_value, socklen_t *__restrict option_len)
210 {
211 	int i, r;
212 	size_t size;
213 
214 	if (level == SOL_SOCKET && option_name == SO_RCVBUF)
215 	{
216  		r= ioctl(sock, NWIOGUDSRCVBUF, &size);
217 		if (r == -1) {
218 			return r;
219 		}
220 
221 		getsockopt_copy(&size, sizeof(size), option_value, option_len);
222 		return 0;
223 	}
224 
225 	if (level == SOL_SOCKET && option_name == SO_SNDBUF)
226 	{
227  		r= ioctl(sock, NWIOGUDSSNDBUF, &size);
228 		if (r == -1) {
229 			return r;
230 		}
231 
232 		getsockopt_copy(&size, sizeof(size), option_value, option_len);
233 		return 0;
234 	}
235 
236 	if (level == SOL_SOCKET && option_name == SO_TYPE)
237 	{
238  		r= ioctl(sock, NWIOGUDSSOTYPE, &i);
239 		if (r == -1) {
240 			return r;
241 		}
242 
243 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
244 		return 0;
245 	}
246 
247 #ifdef SO_PEERCRED
248 	if (level == SOL_SOCKET && option_name == SO_PEERCRED)
249 	{
250 		struct uucred cred;
251 
252 		r= ioctl(sock, NWIOGUDSPEERCRED, &cred);
253 		if (r == -1) {
254 			return -1;
255 		}
256 
257 		getsockopt_copy(&cred, sizeof(struct uucred), option_value,
258 							option_len);
259 		return 0;
260 	}
261 #endif
262 
263 
264 	if (level == SOL_SOCKET && option_name == SO_REUSEADDR)
265 	{
266 		i = 1;	/* as long as nobody is listen()ing on the address,
267 			 * it can be reused without waiting for a
268 			 * timeout to expire.
269 			 */
270 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
271 		return 0;
272 	}
273 
274 #ifdef SO_PASSCRED
275 	if (level == SOL_SOCKET && option_name == SO_PASSCRED)
276 	{
277 		i = 1;	/* option is always 'on' */
278 		getsockopt_copy(&i, sizeof(i), option_value, option_len);
279 		return 0;
280 	}
281 #endif
282 
283 #if DEBUG
284 	fprintf(stderr, "_uds_getsocketopt: level %d, name %d\n",
285 		level, option_name);
286 #endif
287 
288 	errno= ENOSYS;
289 	return -1;
290 }
291