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