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
__setsockopt(int fd,int level,int option_name,const void * option_value,socklen_t option_len)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
setsockopt(int sock,int level,int option_name,const void * option_value,socklen_t option_len)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
_tcp_setsockopt(int sock,int level,int option_name,const void * option_value,socklen_t option_len)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
_udp_setsockopt(int sock,int level,int option_name,const void * option_value,socklen_t option_len)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
_uds_setsockopt(int sock,int level,int option_name,const void * option_value,socklen_t option_len)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