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
__getsockopt(int fd,int level,int option_name,void * __restrict option_value,socklen_t * __restrict option_len)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
getsockopt(int sock,int level,int option_name,void * __restrict option_value,socklen_t * __restrict option_len)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
getsockopt_copy(void * return_value,size_t return_len,void * __restrict option_value,socklen_t * __restrict option_len)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
_tcp_getsockopt(int sock,int level,int option_name,void * __restrict option_value,socklen_t * __restrict option_len)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
_udp_getsockopt(int sock,int level,int option_name,void * __restrict option_value,socklen_t * __restrict option_len)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
_uds_getsockopt(int sock,int level,int option_name,void * __restrict option_value,socklen_t * __restrict option_len)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