]>
Commit | Line | Data |
---|---|---|
1 | /** | |
2 | * @file | |
3 | * Sockets BSD-Like API module | |
4 | * | |
5 | */ | |
6 | ||
7 | /* | |
8 | * Copyright (c) 2001-2004 Swedish Institute of Computer Science. | |
9 | * All rights reserved. | |
10 | * | |
11 | * Redistribution and use in source and binary forms, with or without modification, | |
12 | * are permitted provided that the following conditions are met: | |
13 | * | |
14 | * 1. Redistributions of source code must retain the above copyright notice, | |
15 | * this list of conditions and the following disclaimer. | |
16 | * 2. Redistributions in binary form must reproduce the above copyright notice, | |
17 | * this list of conditions and the following disclaimer in the documentation | |
18 | * and/or other materials provided with the distribution. | |
19 | * 3. The name of the author may not be used to endorse or promote products | |
20 | * derived from this software without specific prior written permission. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
23 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
24 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT | |
25 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
26 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT | |
27 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | |
30 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY | |
31 | * OF SUCH DAMAGE. | |
32 | * | |
33 | * This file is part of the lwIP TCP/IP stack. | |
34 | * | |
35 | * Author: Adam Dunkels <adam@sics.se> | |
36 | * | |
37 | * Improved by Marc Boucher <marc@mbsi.ca> and David Haas <dhaas@alum.rpi.edu> | |
38 | * | |
39 | */ | |
40 | ||
41 | #include "lwip/opt.h" | |
42 | ||
43 | #if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ | |
44 | ||
45 | #include "lwip/sockets.h" | |
46 | #include "lwip/api.h" | |
47 | #include "lwip/sys.h" | |
48 | #include "lwip/igmp.h" | |
49 | #include "lwip/inet.h" | |
50 | #include "lwip/tcp.h" | |
51 | #include "lwip/raw.h" | |
52 | #include "lwip/udp.h" | |
53 | #include "lwip/tcpip.h" | |
54 | ||
55 | #include <string.h> | |
56 | ||
57 | #define NUM_SOCKETS MEMP_NUM_NETCONN | |
58 | ||
59 | /** Contains all internal pointers and states used for a socket */ | |
60 | struct lwip_socket { | |
61 | /** sockets currently are built on netconns, each socket has one netconn */ | |
62 | struct netconn *conn; | |
63 | /** data that was left from the previous read */ | |
64 | struct netbuf *lastdata; | |
65 | /** offset in the data that was left from the previous read */ | |
66 | u16_t lastoffset; | |
67 | /** number of times data was received, set by event_callback(), | |
68 | tested by the receive and select functions */ | |
69 | u16_t rcvevent; | |
70 | /** number of times data was received, set by event_callback(), | |
71 | tested by select */ | |
72 | u16_t sendevent; | |
73 | /** socket flags (currently, only used for O_NONBLOCK) */ | |
74 | u16_t flags; | |
75 | /** last error that occurred on this socket */ | |
76 | int err; | |
77 | }; | |
78 | ||
79 | /** Description for a task waiting in select */ | |
80 | struct lwip_select_cb { | |
81 | /** Pointer to the next waiting task */ | |
82 | struct lwip_select_cb *next; | |
83 | /** readset passed to select */ | |
84 | fd_set *readset; | |
85 | /** writeset passed to select */ | |
86 | fd_set *writeset; | |
87 | /** unimplemented: exceptset passed to select */ | |
88 | fd_set *exceptset; | |
89 | /** don't signal the same semaphore twice: set to 1 when signalled */ | |
90 | int sem_signalled; | |
91 | /** semaphore to wake up a task waiting for select */ | |
92 | sys_sem_t sem; | |
93 | }; | |
94 | ||
95 | /** This struct is used to pass data to the set/getsockopt_internal | |
96 | * functions running in tcpip_thread context (only a void* is allowed) */ | |
97 | struct lwip_setgetsockopt_data { | |
98 | /** socket struct for which to change options */ | |
99 | struct lwip_socket *sock; | |
100 | /** socket index for which to change options */ | |
101 | int s; | |
102 | /** level of the option to process */ | |
103 | int level; | |
104 | /** name of the option to process */ | |
105 | int optname; | |
106 | /** set: value to set the option to | |
107 | * get: value of the option is stored here */ | |
108 | void *optval; | |
109 | /** size of *optval */ | |
110 | socklen_t *optlen; | |
111 | /** if an error occures, it is temporarily stored here */ | |
112 | err_t err; | |
113 | }; | |
114 | ||
115 | /** The global array of available sockets */ | |
116 | static struct lwip_socket sockets[NUM_SOCKETS]; | |
117 | /** The global list of tasks waiting for select */ | |
118 | static struct lwip_select_cb *select_cb_list; | |
119 | ||
120 | /** Semaphore protecting the sockets array */ | |
121 | static sys_sem_t socksem; | |
122 | /** Semaphore protecting select_cb_list */ | |
123 | static sys_sem_t selectsem; | |
124 | ||
125 | /** Table to quickly map an lwIP error (err_t) to a socket error | |
126 | * by using -err as an index */ | |
127 | static const int err_to_errno_table[] = { | |
128 | 0, /* ERR_OK 0 No error, everything OK. */ | |
129 | ENOMEM, /* ERR_MEM -1 Out of memory error. */ | |
130 | ENOBUFS, /* ERR_BUF -2 Buffer error. */ | |
131 | EHOSTUNREACH, /* ERR_RTE -3 Routing problem. */ | |
132 | ECONNABORTED, /* ERR_ABRT -4 Connection aborted. */ | |
133 | ECONNRESET, /* ERR_RST -5 Connection reset. */ | |
134 | ESHUTDOWN, /* ERR_CLSD -6 Connection closed. */ | |
135 | ENOTCONN, /* ERR_CONN -7 Not connected. */ | |
136 | EINVAL, /* ERR_VAL -8 Illegal value. */ | |
137 | EIO, /* ERR_ARG -9 Illegal argument. */ | |
138 | EADDRINUSE, /* ERR_USE -10 Address in use. */ | |
139 | -1, /* ERR_IF -11 Low-level netif error */ | |
140 | -1, /* ERR_ISCONN -12 Already connected. */ | |
141 | ETIMEDOUT, /* ERR_TIMEOUT -13 Timeout */ | |
142 | EINPROGRESS /* ERR_INPROGRESS -14 Operation in progress */ | |
143 | }; | |
144 | ||
145 | #define ERR_TO_ERRNO_TABLE_SIZE \ | |
146 | (sizeof(err_to_errno_table)/sizeof(err_to_errno_table[0])) | |
147 | ||
148 | #define err_to_errno(err) \ | |
149 | ((unsigned)(-(err)) < ERR_TO_ERRNO_TABLE_SIZE ? \ | |
150 | err_to_errno_table[-(err)] : EIO) | |
151 | ||
152 | #ifdef ERRNO | |
153 | #define set_errno(err) errno = (err) | |
154 | #else | |
155 | #define set_errno(err) | |
156 | #endif | |
157 | ||
158 | #define sock_set_errno(sk, e) do { \ | |
159 | sk->err = (e); \ | |
160 | set_errno(sk->err); \ | |
161 | } while (0) | |
162 | ||
163 | /* Forward delcaration of some functions */ | |
164 | static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len); | |
165 | static void lwip_getsockopt_internal(void *arg); | |
166 | static void lwip_setsockopt_internal(void *arg); | |
167 | ||
168 | /** | |
169 | * Initialize this module. This function has to be called before any other | |
170 | * functions in this module! | |
171 | */ | |
172 | void | |
173 | lwip_socket_init(void) | |
174 | { | |
175 | socksem = sys_sem_new(1); | |
176 | selectsem = sys_sem_new(1); | |
177 | } | |
178 | ||
179 | /** | |
180 | * Map a externally used socket index to the internal socket representation. | |
181 | * | |
182 | * @param s externally used socket index | |
183 | * @return struct lwip_socket for the socket or NULL if not found | |
184 | */ | |
185 | static struct lwip_socket * | |
186 | get_socket(int s) | |
187 | { | |
188 | struct lwip_socket *sock; | |
189 | ||
190 | if ((s < 0) || (s >= NUM_SOCKETS)) { | |
191 | LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s)); | |
192 | set_errno(EBADF); | |
193 | return NULL; | |
194 | } | |
195 | ||
196 | sock = &sockets[s]; | |
197 | ||
198 | if (!sock->conn) { | |
199 | LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s)); | |
200 | set_errno(EBADF); | |
201 | return NULL; | |
202 | } | |
203 | ||
204 | return sock; | |
205 | } | |
206 | ||
207 | /** | |
208 | * Allocate a new socket for a given netconn. | |
209 | * | |
210 | * @param newconn the netconn for which to allocate a socket | |
211 | * @return the index of the new socket; -1 on error | |
212 | */ | |
213 | static int | |
214 | alloc_socket(struct netconn *newconn) | |
215 | { | |
216 | int i; | |
217 | ||
218 | /* Protect socket array */ | |
219 | sys_sem_wait(socksem); | |
220 | ||
221 | /* allocate a new socket identifier */ | |
222 | for (i = 0; i < NUM_SOCKETS; ++i) { | |
223 | if (!sockets[i].conn) { | |
224 | sockets[i].conn = newconn; | |
225 | sockets[i].lastdata = NULL; | |
226 | sockets[i].lastoffset = 0; | |
227 | sockets[i].rcvevent = 0; | |
228 | sockets[i].sendevent = 1; /* TCP send buf is empty */ | |
229 | sockets[i].flags = 0; | |
230 | sockets[i].err = 0; | |
231 | sys_sem_signal(socksem); | |
232 | return i; | |
233 | } | |
234 | } | |
235 | sys_sem_signal(socksem); | |
236 | return -1; | |
237 | } | |
238 | ||
239 | /* Below this, the well-known socket functions are implemented. | |
240 | * Use google.com or opengroup.org to get a good description :-) | |
241 | * | |
242 | * Exceptions are documented! | |
243 | */ | |
244 | ||
245 | int | |
246 | lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) | |
247 | { | |
248 | struct lwip_socket *sock, *nsock; | |
249 | struct netconn *newconn; | |
250 | struct ip_addr naddr; | |
251 | u16_t port; | |
252 | int newsock; | |
253 | struct sockaddr_in sin; | |
254 | err_t err; | |
255 | ||
256 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s)); | |
257 | sock = get_socket(s); | |
258 | if (!sock) | |
259 | return -1; | |
260 | ||
261 | newconn = netconn_accept(sock->conn); | |
262 | if (!newconn) { | |
263 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) failed, err=%d\n", s, sock->conn->err)); | |
264 | sock_set_errno(sock, err_to_errno(sock->conn->err)); | |
265 | return -1; | |
266 | } | |
267 | ||
268 | /* get the IP address and port of the remote host */ | |
269 | err = netconn_peer(newconn, &naddr, &port); | |
270 | if (err != ERR_OK) { | |
271 | netconn_delete(newconn); | |
272 | sock_set_errno(sock, err_to_errno(err)); | |
273 | return -1; | |
274 | } | |
275 | ||
276 | memset(&sin, 0, sizeof(sin)); | |
277 | sin.sin_len = sizeof(sin); | |
278 | sin.sin_family = AF_INET; | |
279 | sin.sin_port = htons(port); | |
280 | sin.sin_addr.s_addr = naddr.addr; | |
281 | ||
282 | if (*addrlen > sizeof(sin)) | |
283 | *addrlen = sizeof(sin); | |
284 | ||
285 | SMEMCPY(addr, &sin, *addrlen); | |
286 | ||
287 | newsock = alloc_socket(newconn); | |
288 | if (newsock == -1) { | |
289 | netconn_delete(newconn); | |
290 | sock_set_errno(sock, ENFILE); | |
291 | return -1; | |
292 | } | |
293 | LWIP_ASSERT("invalid socket index", (newsock >= 0) && (newsock < NUM_SOCKETS)); | |
294 | newconn->callback = event_callback; | |
295 | nsock = &sockets[newsock]; | |
296 | LWIP_ASSERT("invalid socket pointer", nsock != NULL); | |
297 | ||
298 | sys_sem_wait(socksem); | |
299 | /* See event_callback: If data comes in right away after an accept, even | |
300 | * though the server task might not have created a new socket yet. | |
301 | * In that case, newconn->socket is counted down (newconn->socket--), | |
302 | * so nsock->rcvevent is >= 1 here! | |
303 | */ | |
304 | nsock->rcvevent += -1 - newconn->socket; | |
305 | newconn->socket = newsock; | |
306 | sys_sem_signal(socksem); | |
307 | ||
308 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock)); | |
309 | ip_addr_debug_print(SOCKETS_DEBUG, &naddr); | |
310 | LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u\n", port)); | |
311 | ||
312 | sock_set_errno(sock, 0); | |
313 | return newsock; | |
314 | } | |
315 | ||
316 | int | |
317 | lwip_bind(int s, struct sockaddr *name, socklen_t namelen) | |
318 | { | |
319 | struct lwip_socket *sock; | |
320 | struct ip_addr local_addr; | |
321 | u16_t local_port; | |
322 | err_t err; | |
323 | ||
324 | sock = get_socket(s); | |
325 | if (!sock) | |
326 | return -1; | |
327 | ||
328 | LWIP_ERROR("lwip_bind: invalid address", ((namelen == sizeof(struct sockaddr_in)) && | |
329 | ((((struct sockaddr_in *)name)->sin_family) == AF_INET)), | |
330 | sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); | |
331 | ||
332 | local_addr.addr = ((struct sockaddr_in *)name)->sin_addr.s_addr; | |
333 | local_port = ((struct sockaddr_in *)name)->sin_port; | |
334 | ||
335 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s)); | |
336 | ip_addr_debug_print(SOCKETS_DEBUG, &local_addr); | |
337 | LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u)\n", ntohs(local_port))); | |
338 | ||
339 | err = netconn_bind(sock->conn, &local_addr, ntohs(local_port)); | |
340 | ||
341 | if (err != ERR_OK) { | |
342 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err)); | |
343 | sock_set_errno(sock, err_to_errno(err)); | |
344 | return -1; | |
345 | } | |
346 | ||
347 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s)); | |
348 | sock_set_errno(sock, 0); | |
349 | return 0; | |
350 | } | |
351 | ||
352 | int | |
353 | lwip_close(int s) | |
354 | { | |
355 | struct lwip_socket *sock; | |
356 | ||
357 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s)); | |
358 | ||
359 | sock = get_socket(s); | |
360 | if (!sock) { | |
361 | return -1; | |
362 | } | |
363 | ||
364 | netconn_delete(sock->conn); | |
365 | ||
366 | sys_sem_wait(socksem); | |
367 | if (sock->lastdata) { | |
368 | netbuf_delete(sock->lastdata); | |
369 | } | |
370 | sock->lastdata = NULL; | |
371 | sock->lastoffset = 0; | |
372 | sock->conn = NULL; | |
373 | sock_set_errno(sock, 0); | |
374 | sys_sem_signal(socksem); | |
375 | return 0; | |
376 | } | |
377 | ||
378 | int | |
379 | lwip_connect(int s, const struct sockaddr *name, socklen_t namelen) | |
380 | { | |
381 | struct lwip_socket *sock; | |
382 | err_t err; | |
383 | ||
384 | sock = get_socket(s); | |
385 | if (!sock) | |
386 | return -1; | |
387 | ||
388 | LWIP_ERROR("lwip_connect: invalid address", ((namelen == sizeof(struct sockaddr_in)) && | |
389 | ((((struct sockaddr_in *)name)->sin_family) == AF_INET)), | |
390 | sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); | |
391 | ||
392 | if (((struct sockaddr_in *)name)->sin_family == AF_UNSPEC) { | |
393 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s)); | |
394 | err = netconn_disconnect(sock->conn); | |
395 | } else { | |
396 | struct ip_addr remote_addr; | |
397 | u16_t remote_port; | |
398 | ||
399 | remote_addr.addr = ((struct sockaddr_in *)name)->sin_addr.s_addr; | |
400 | remote_port = ((struct sockaddr_in *)name)->sin_port; | |
401 | ||
402 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s)); | |
403 | ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr); | |
404 | LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u)\n", ntohs(remote_port))); | |
405 | ||
406 | err = netconn_connect(sock->conn, &remote_addr, ntohs(remote_port)); | |
407 | } | |
408 | ||
409 | if (err != ERR_OK) { | |
410 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err)); | |
411 | sock_set_errno(sock, err_to_errno(err)); | |
412 | return -1; | |
413 | } | |
414 | ||
415 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s)); | |
416 | sock_set_errno(sock, 0); | |
417 | return 0; | |
418 | } | |
419 | ||
420 | /** | |
421 | * Set a socket into listen mode. | |
422 | * The socket may not have been used for another connection previously. | |
423 | * | |
424 | * @param s the socket to set to listening mode | |
425 | * @param backlog (ATTENTION: need TCP_LISTEN_BACKLOG=1) | |
426 | * @return 0 on success, non-zero on failure | |
427 | */ | |
428 | int | |
429 | lwip_listen(int s, int backlog) | |
430 | { | |
431 | struct lwip_socket *sock; | |
432 | err_t err; | |
433 | ||
434 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog)); | |
435 | ||
436 | sock = get_socket(s); | |
437 | if (!sock) | |
438 | return -1; | |
439 | ||
440 | /* limit the "backlog" parameter to fit in an u8_t */ | |
441 | if (backlog < 0) { | |
442 | backlog = 0; | |
443 | } | |
444 | if (backlog > 0xff) { | |
445 | backlog = 0xff; | |
446 | } | |
447 | ||
448 | err = netconn_listen_with_backlog(sock->conn, backlog); | |
449 | ||
450 | if (err != ERR_OK) { | |
451 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err)); | |
452 | sock_set_errno(sock, err_to_errno(err)); | |
453 | return -1; | |
454 | } | |
455 | ||
456 | sock_set_errno(sock, 0); | |
457 | return 0; | |
458 | } | |
459 | ||
460 | int | |
461 | lwip_recvfrom(int s, void *mem, int len, unsigned int flags, | |
462 | struct sockaddr *from, socklen_t *fromlen) | |
463 | { | |
464 | struct lwip_socket *sock; | |
465 | struct netbuf *buf; | |
466 | u16_t buflen, copylen, off = 0; | |
467 | struct ip_addr *addr; | |
468 | u16_t port; | |
469 | u8_t done = 0; | |
470 | ||
471 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %d, 0x%x, ..)\n", s, mem, len, flags)); | |
472 | sock = get_socket(s); | |
473 | if (!sock) | |
474 | return -1; | |
475 | ||
476 | do { | |
477 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", (void*)sock->lastdata)); | |
478 | /* Check if there is data left from the last recv operation. */ | |
479 | if (sock->lastdata) { | |
480 | buf = sock->lastdata; | |
481 | } else { | |
482 | /* If this is non-blocking call, then check first */ | |
483 | if (((flags & MSG_DONTWAIT) || (sock->flags & O_NONBLOCK)) && !sock->rcvevent) { | |
484 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s)); | |
485 | sock_set_errno(sock, EWOULDBLOCK); | |
486 | return -1; | |
487 | } | |
488 | ||
489 | /* No data was left from the previous operation, so we try to get | |
490 | some from the network. */ | |
491 | sock->lastdata = buf = netconn_recv(sock->conn); | |
492 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv netbuf=%p\n", (void*)buf)); | |
493 | ||
494 | if (!buf) { | |
495 | /* We should really do some error checking here. */ | |
496 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL!\n", s)); | |
497 | sock_set_errno(sock, (((sock->conn->pcb.ip!=NULL) && (sock->conn->err==ERR_OK))?ETIMEDOUT:err_to_errno(sock->conn->err))); | |
498 | return 0; | |
499 | } | |
500 | } | |
501 | ||
502 | buflen = netbuf_len(buf); | |
503 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%d len=%d off=%d sock->lastoffset=%d\n", buflen, len, off, sock->lastoffset)); | |
504 | ||
505 | buflen -= sock->lastoffset; | |
506 | ||
507 | if (len > buflen) { | |
508 | copylen = buflen; | |
509 | } else { | |
510 | copylen = len; | |
511 | } | |
512 | ||
513 | /* copy the contents of the received buffer into | |
514 | the supplied memory pointer mem */ | |
515 | netbuf_copy_partial(buf, (u8_t*)mem + off, copylen, sock->lastoffset); | |
516 | ||
517 | off += copylen; | |
518 | ||
519 | if (netconn_type(sock->conn) == NETCONN_TCP) { | |
520 | len -= copylen; | |
521 | if ( (len <= 0) || (buf->p->flags & PBUF_FLAG_PUSH) || !sock->rcvevent) { | |
522 | done = 1; | |
523 | } | |
524 | } else { | |
525 | done = 1; | |
526 | } | |
527 | ||
528 | /* If we don't peek the incoming message... */ | |
529 | if ((flags & MSG_PEEK)==0) { | |
530 | /* If this is a TCP socket, check if there is data left in the | |
531 | buffer. If so, it should be saved in the sock structure for next | |
532 | time around. */ | |
533 | if ((sock->conn->type == NETCONN_TCP) && (buflen - copylen > 0)) { | |
534 | sock->lastdata = buf; | |
535 | sock->lastoffset += copylen; | |
536 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", (void*)buf)); | |
537 | } else { | |
538 | sock->lastdata = NULL; | |
539 | sock->lastoffset = 0; | |
540 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", (void*)buf)); | |
541 | netbuf_delete(buf); | |
542 | } | |
543 | } else { | |
544 | done = 1; | |
545 | } | |
546 | } while (!done); | |
547 | ||
548 | /* Check to see from where the data was.*/ | |
549 | if (from && fromlen) { | |
550 | struct sockaddr_in sin; | |
551 | ||
552 | if (netconn_type(sock->conn) == NETCONN_TCP) { | |
553 | addr = (struct ip_addr*)&(sin.sin_addr.s_addr); | |
554 | netconn_getaddr(sock->conn, addr, &port, 0); | |
555 | } else { | |
556 | addr = netbuf_fromaddr(buf); | |
557 | port = netbuf_fromport(buf); | |
558 | } | |
559 | ||
560 | memset(&sin, 0, sizeof(sin)); | |
561 | sin.sin_len = sizeof(sin); | |
562 | sin.sin_family = AF_INET; | |
563 | sin.sin_port = htons(port); | |
564 | sin.sin_addr.s_addr = addr->addr; | |
565 | ||
566 | if (*fromlen > sizeof(sin)) | |
567 | *fromlen = sizeof(sin); | |
568 | ||
569 | SMEMCPY(from, &sin, *fromlen); | |
570 | ||
571 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); | |
572 | ip_addr_debug_print(SOCKETS_DEBUG, addr); | |
573 | LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u len=%u\n", port, off)); | |
574 | } else { | |
575 | #if SOCKETS_DEBUG | |
576 | struct sockaddr_in sin; | |
577 | ||
578 | if (netconn_type(sock->conn) == NETCONN_TCP) { | |
579 | addr = (struct ip_addr*)&(sin.sin_addr.s_addr); | |
580 | netconn_getaddr(sock->conn, addr, &port, 0); | |
581 | } else { | |
582 | addr = netbuf_fromaddr(buf); | |
583 | port = netbuf_fromport(buf); | |
584 | } | |
585 | ||
586 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); | |
587 | ip_addr_debug_print(SOCKETS_DEBUG, addr); | |
588 | LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u len=%u\n", port, off)); | |
589 | #endif /* SOCKETS_DEBUG */ | |
590 | } | |
591 | ||
592 | sock_set_errno(sock, 0); | |
593 | return off; | |
594 | } | |
595 | ||
596 | int | |
597 | lwip_read(int s, void *mem, int len) | |
598 | { | |
599 | return lwip_recvfrom(s, mem, len, 0, NULL, NULL); | |
600 | } | |
601 | ||
602 | int | |
603 | lwip_recv(int s, void *mem, int len, unsigned int flags) | |
604 | { | |
605 | return lwip_recvfrom(s, mem, len, flags, NULL, NULL); | |
606 | } | |
607 | ||
608 | int | |
609 | lwip_send(int s, const void *data, int size, unsigned int flags) | |
610 | { | |
611 | struct lwip_socket *sock; | |
612 | err_t err; | |
613 | ||
614 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%d, flags=0x%x)\n", | |
615 | s, data, size, flags)); | |
616 | ||
617 | sock = get_socket(s); | |
618 | if (!sock) | |
619 | return -1; | |
620 | ||
621 | if (sock->conn->type!=NETCONN_TCP) { | |
622 | #if (LWIP_UDP || LWIP_RAW) | |
623 | return lwip_sendto(s, data, size, flags, NULL, 0); | |
624 | #else | |
625 | sock_set_errno(sock, err_to_errno(ERR_ARG)); | |
626 | return -1; | |
627 | #endif /* (LWIP_UDP || LWIP_RAW) */ | |
628 | } | |
629 | ||
630 | err = netconn_write(sock->conn, data, size, NETCONN_COPY | ((flags & MSG_MORE)?NETCONN_MORE:0)); | |
631 | ||
632 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d size=%d\n", s, err, size)); | |
633 | sock_set_errno(sock, err_to_errno(err)); | |
634 | return (err==ERR_OK?size:-1); | |
635 | } | |
636 | ||
637 | int | |
638 | lwip_sendto(int s, const void *data, int size, unsigned int flags, | |
639 | struct sockaddr *to, socklen_t tolen) | |
640 | { | |
641 | struct lwip_socket *sock; | |
642 | struct ip_addr remote_addr; | |
643 | int err; | |
644 | #if !LWIP_TCPIP_CORE_LOCKING | |
645 | struct netbuf buf; | |
646 | u16_t remote_port; | |
647 | #endif | |
648 | ||
649 | sock = get_socket(s); | |
650 | if (!sock) | |
651 | return -1; | |
652 | ||
653 | if (sock->conn->type==NETCONN_TCP) { | |
654 | #if LWIP_TCP | |
655 | return lwip_send(s, data, size, flags); | |
656 | #else | |
657 | sock_set_errno(sock, err_to_errno(ERR_ARG)); | |
658 | return -1; | |
659 | #endif /* LWIP_TCP */ | |
660 | } | |
661 | ||
662 | LWIP_ASSERT("lwip_sendto: size must fit in u16_t", | |
663 | ((size >= 0) && (size <= 0xffff))); | |
664 | LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) || | |
665 | ((tolen == sizeof(struct sockaddr_in)) && | |
666 | ((((struct sockaddr_in *)to)->sin_family) == AF_INET))), | |
667 | sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); | |
668 | ||
669 | #if LWIP_TCPIP_CORE_LOCKING | |
670 | /* Should only be consider like a sample or a simple way to experiment this option (no check of "to" field...) */ | |
671 | { struct pbuf* p; | |
672 | ||
673 | p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF); | |
674 | if (p == NULL) { | |
675 | err = ERR_MEM; | |
676 | } else { | |
677 | p->payload = (void*)data; | |
678 | p->len = p->tot_len = size; | |
679 | ||
680 | remote_addr.addr = ((struct sockaddr_in *)to)->sin_addr.s_addr; | |
681 | ||
682 | LOCK_TCPIP_CORE(); | |
683 | if (sock->conn->type==NETCONN_RAW) { | |
684 | err = sock->conn->err = raw_sendto(sock->conn->pcb.raw, p, &remote_addr); | |
685 | } else { | |
686 | err = sock->conn->err = udp_sendto(sock->conn->pcb.udp, p, &remote_addr, ntohs(((struct sockaddr_in *)to)->sin_port)); | |
687 | } | |
688 | UNLOCK_TCPIP_CORE(); | |
689 | ||
690 | pbuf_free(p); | |
691 | } | |
692 | } | |
693 | #else | |
694 | /* initialize a buffer */ | |
695 | buf.p = buf.ptr = NULL; | |
696 | if (to) { | |
697 | remote_addr.addr = ((struct sockaddr_in *)to)->sin_addr.s_addr; | |
698 | remote_port = ntohs(((struct sockaddr_in *)to)->sin_port); | |
699 | buf.addr = &remote_addr; | |
700 | buf.port = remote_port; | |
701 | } else { | |
702 | remote_addr.addr = 0; | |
703 | remote_port = 0; | |
704 | buf.addr = NULL; | |
705 | buf.port = 0; | |
706 | } | |
707 | ||
708 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, size=%d, flags=0x%x to=", | |
709 | s, data, size, flags)); | |
710 | ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr); | |
711 | LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u\n", remote_port)); | |
712 | ||
713 | /* make the buffer point to the data that should be sent */ | |
714 | if ((err = netbuf_ref(&buf, data, size)) == ERR_OK) { | |
715 | /* send the data */ | |
716 | err = netconn_send(sock->conn, &buf); | |
717 | } | |
718 | ||
719 | /* deallocated the buffer */ | |
720 | if (buf.p != NULL) { | |
721 | pbuf_free(buf.p); | |
722 | } | |
723 | #endif /* LWIP_TCPIP_CORE_LOCKING */ | |
724 | sock_set_errno(sock, err_to_errno(err)); | |
725 | return (err==ERR_OK?size:-1); | |
726 | } | |
727 | ||
728 | int | |
729 | lwip_socket(int domain, int type, int protocol) | |
730 | { | |
731 | struct netconn *conn; | |
732 | int i; | |
733 | ||
734 | LWIP_UNUSED_ARG(domain); | |
735 | ||
736 | /* create a netconn */ | |
737 | switch (type) { | |
738 | case SOCK_RAW: | |
739 | conn = netconn_new_with_proto_and_callback(NETCONN_RAW, (u8_t)protocol, event_callback); | |
740 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ", | |
741 | domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); | |
742 | break; | |
743 | case SOCK_DGRAM: | |
744 | conn = netconn_new_with_callback( (protocol == IPPROTO_UDPLITE) ? | |
745 | NETCONN_UDPLITE : NETCONN_UDP, event_callback); | |
746 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ", | |
747 | domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); | |
748 | break; | |
749 | case SOCK_STREAM: | |
750 | conn = netconn_new_with_callback(NETCONN_TCP, event_callback); | |
751 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ", | |
752 | domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); | |
753 | break; | |
754 | default: | |
755 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n", | |
756 | domain, type, protocol)); | |
757 | set_errno(EINVAL); | |
758 | return -1; | |
759 | } | |
760 | ||
761 | if (!conn) { | |
762 | LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n")); | |
763 | set_errno(ENOBUFS); | |
764 | return -1; | |
765 | } | |
766 | ||
767 | i = alloc_socket(conn); | |
768 | ||
769 | if (i == -1) { | |
770 | netconn_delete(conn); | |
771 | set_errno(ENFILE); | |
772 | return -1; | |
773 | } | |
774 | conn->socket = i; | |
775 | LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i)); | |
776 | set_errno(0); | |
777 | return i; | |
778 | } | |
779 | ||
780 | int | |
781 | lwip_write(int s, const void *data, int size) | |
782 | { | |
783 | return lwip_send(s, data, size, 0); | |
784 | } | |
785 | ||
786 | /** | |
787 | * Go through the readset and writeset lists and see which socket of the sockets | |
788 | * set in the sets has events. On return, readset, writeset and exceptset have | |
789 | * the sockets enabled that had events. | |
790 | * | |
791 | * exceptset is not used for now!!! | |
792 | * | |
793 | * @param maxfdp1 the highest socket index in the sets | |
794 | * @param readset in: set of sockets to check for read events; | |
795 | * out: set of sockets that had read events | |
796 | * @param writeset in: set of sockets to check for write events; | |
797 | * out: set of sockets that had write events | |
798 | * @param exceptset not yet implemented | |
799 | * @return number of sockets that had events (read+write) | |
800 | */ | |
801 | static int | |
802 | lwip_selscan(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset) | |
803 | { | |
804 | int i, nready = 0; | |
805 | fd_set lreadset, lwriteset, lexceptset; | |
806 | struct lwip_socket *p_sock; | |
807 | ||
808 | FD_ZERO(&lreadset); | |
809 | FD_ZERO(&lwriteset); | |
810 | FD_ZERO(&lexceptset); | |
811 | ||
812 | /* Go through each socket in each list to count number of sockets which | |
813 | currently match */ | |
814 | for(i = 0; i < maxfdp1; i++) { | |
815 | if (FD_ISSET(i, readset)) { | |
816 | /* See if netconn of this socket is ready for read */ | |
817 | p_sock = get_socket(i); | |
818 | if (p_sock && (p_sock->lastdata || p_sock->rcvevent)) { | |
819 | FD_SET(i, &lreadset); | |
820 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i)); | |
821 | nready++; | |
822 | } | |
823 | } | |
824 | if (FD_ISSET(i, writeset)) { | |
825 | /* See if netconn of this socket is ready for write */ | |
826 | p_sock = get_socket(i); | |
827 | if (p_sock && p_sock->sendevent) { | |
828 | FD_SET(i, &lwriteset); | |
829 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i)); | |
830 | nready++; | |
831 | } | |
832 | } | |
833 | } | |
834 | *readset = lreadset; | |
835 | *writeset = lwriteset; | |
836 | FD_ZERO(exceptset); | |
837 | ||
838 | return nready; | |
839 | } | |
840 | ||
841 | ||
842 | /** | |
843 | * Processing exceptset is not yet implemented. | |
844 | */ | |
845 | int | |
846 | lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, | |
847 | struct timeval *timeout) | |
848 | { | |
849 | int i; | |
850 | int nready; | |
851 | fd_set lreadset, lwriteset, lexceptset; | |
852 | u32_t msectimeout; | |
853 | struct lwip_select_cb select_cb; | |
854 | struct lwip_select_cb *p_selcb; | |
855 | ||
856 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%ld tvusec=%ld)\n", | |
857 | maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset, | |
858 | timeout ? timeout->tv_sec : -1L, timeout ? timeout->tv_usec : -1L)); | |
859 | ||
860 | select_cb.next = 0; | |
861 | select_cb.readset = readset; | |
862 | select_cb.writeset = writeset; | |
863 | select_cb.exceptset = exceptset; | |
864 | select_cb.sem_signalled = 0; | |
865 | ||
866 | /* Protect ourselves searching through the list */ | |
867 | sys_sem_wait(selectsem); | |
868 | ||
869 | if (readset) | |
870 | lreadset = *readset; | |
871 | else | |
872 | FD_ZERO(&lreadset); | |
873 | if (writeset) | |
874 | lwriteset = *writeset; | |
875 | else | |
876 | FD_ZERO(&lwriteset); | |
877 | if (exceptset) | |
878 | lexceptset = *exceptset; | |
879 | else | |
880 | FD_ZERO(&lexceptset); | |
881 | ||
882 | /* Go through each socket in each list to count number of sockets which | |
883 | currently match */ | |
884 | nready = lwip_selscan(maxfdp1, &lreadset, &lwriteset, &lexceptset); | |
885 | ||
886 | /* If we don't have any current events, then suspend if we are supposed to */ | |
887 | if (!nready) { | |
888 | if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) { | |
889 | sys_sem_signal(selectsem); | |
890 | if (readset) | |
891 | FD_ZERO(readset); | |
892 | if (writeset) | |
893 | FD_ZERO(writeset); | |
894 | if (exceptset) | |
895 | FD_ZERO(exceptset); | |
896 | ||
897 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n")); | |
898 | set_errno(0); | |
899 | ||
900 | return 0; | |
901 | } | |
902 | ||
903 | /* add our semaphore to list */ | |
904 | /* We don't actually need any dynamic memory. Our entry on the | |
905 | * list is only valid while we are in this function, so it's ok | |
906 | * to use local variables */ | |
907 | ||
908 | select_cb.sem = sys_sem_new(0); | |
909 | /* Note that we are still protected */ | |
910 | /* Put this select_cb on top of list */ | |
911 | select_cb.next = select_cb_list; | |
912 | select_cb_list = &select_cb; | |
913 | ||
914 | /* Now we can safely unprotect */ | |
915 | sys_sem_signal(selectsem); | |
916 | ||
917 | /* Now just wait to be woken */ | |
918 | if (timeout == 0) | |
919 | /* Wait forever */ | |
920 | msectimeout = 0; | |
921 | else { | |
922 | msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000)); | |
923 | if(msectimeout == 0) | |
924 | msectimeout = 1; | |
925 | } | |
926 | ||
927 | i = sys_sem_wait_timeout(select_cb.sem, msectimeout); | |
928 | ||
929 | /* Take us off the list */ | |
930 | sys_sem_wait(selectsem); | |
931 | if (select_cb_list == &select_cb) | |
932 | select_cb_list = select_cb.next; | |
933 | else | |
934 | for (p_selcb = select_cb_list; p_selcb; p_selcb = p_selcb->next) { | |
935 | if (p_selcb->next == &select_cb) { | |
936 | p_selcb->next = select_cb.next; | |
937 | break; | |
938 | } | |
939 | } | |
940 | ||
941 | sys_sem_signal(selectsem); | |
942 | ||
943 | sys_sem_free(select_cb.sem); | |
944 | if (i == 0) { | |
945 | /* Timeout */ | |
946 | if (readset) | |
947 | FD_ZERO(readset); | |
948 | if (writeset) | |
949 | FD_ZERO(writeset); | |
950 | if (exceptset) | |
951 | FD_ZERO(exceptset); | |
952 | ||
953 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n")); | |
954 | set_errno(0); | |
955 | ||
956 | return 0; | |
957 | } | |
958 | ||
959 | if (readset) | |
960 | lreadset = *readset; | |
961 | else | |
962 | FD_ZERO(&lreadset); | |
963 | if (writeset) | |
964 | lwriteset = *writeset; | |
965 | else | |
966 | FD_ZERO(&lwriteset); | |
967 | if (exceptset) | |
968 | lexceptset = *exceptset; | |
969 | else | |
970 | FD_ZERO(&lexceptset); | |
971 | ||
972 | /* See what's set */ | |
973 | nready = lwip_selscan(maxfdp1, &lreadset, &lwriteset, &lexceptset); | |
974 | } else | |
975 | sys_sem_signal(selectsem); | |
976 | ||
977 | if (readset) | |
978 | *readset = lreadset; | |
979 | if (writeset) | |
980 | *writeset = lwriteset; | |
981 | if (exceptset) | |
982 | *exceptset = lexceptset; | |
983 | ||
984 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready)); | |
985 | set_errno(0); | |
986 | ||
987 | return nready; | |
988 | } | |
989 | ||
990 | /** | |
991 | * Callback registered in the netconn layer for each socket-netconn. | |
992 | * Processes recvevent (data available) and wakes up tasks waiting for select. | |
993 | */ | |
994 | static void | |
995 | event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len) | |
996 | { | |
997 | int s; | |
998 | struct lwip_socket *sock; | |
999 | struct lwip_select_cb *scb; | |
1000 | ||
1001 | LWIP_UNUSED_ARG(len); | |
1002 | ||
1003 | /* Get socket */ | |
1004 | if (conn) { | |
1005 | s = conn->socket; | |
1006 | if (s < 0) { | |
1007 | /* Data comes in right away after an accept, even though | |
1008 | * the server task might not have created a new socket yet. | |
1009 | * Just count down (or up) if that's the case and we | |
1010 | * will use the data later. Note that only receive events | |
1011 | * can happen before the new socket is set up. */ | |
1012 | sys_sem_wait(socksem); | |
1013 | if (conn->socket < 0) { | |
1014 | if (evt == NETCONN_EVT_RCVPLUS) { | |
1015 | conn->socket--; | |
1016 | } | |
1017 | sys_sem_signal(socksem); | |
1018 | return; | |
1019 | } | |
1020 | sys_sem_signal(socksem); | |
1021 | } | |
1022 | ||
1023 | sock = get_socket(s); | |
1024 | if (!sock) { | |
1025 | return; | |
1026 | } | |
1027 | } else { | |
1028 | return; | |
1029 | } | |
1030 | ||
1031 | sys_sem_wait(selectsem); | |
1032 | /* Set event as required */ | |
1033 | switch (evt) { | |
1034 | case NETCONN_EVT_RCVPLUS: | |
1035 | sock->rcvevent++; | |
1036 | break; | |
1037 | case NETCONN_EVT_RCVMINUS: | |
1038 | sock->rcvevent--; | |
1039 | break; | |
1040 | case NETCONN_EVT_SENDPLUS: | |
1041 | sock->sendevent = 1; | |
1042 | break; | |
1043 | case NETCONN_EVT_SENDMINUS: | |
1044 | sock->sendevent = 0; | |
1045 | break; | |
1046 | default: | |
1047 | LWIP_ASSERT("unknown event", 0); | |
1048 | break; | |
1049 | } | |
1050 | sys_sem_signal(selectsem); | |
1051 | ||
1052 | /* Now decide if anyone is waiting for this socket */ | |
1053 | /* NOTE: This code is written this way to protect the select link list | |
1054 | but to avoid a deadlock situation by releasing socksem before | |
1055 | signalling for the select. This means we need to go through the list | |
1056 | multiple times ONLY IF a select was actually waiting. We go through | |
1057 | the list the number of waiting select calls + 1. This list is | |
1058 | expected to be small. */ | |
1059 | while (1) { | |
1060 | sys_sem_wait(selectsem); | |
1061 | for (scb = select_cb_list; scb; scb = scb->next) { | |
1062 | if (scb->sem_signalled == 0) { | |
1063 | /* Test this select call for our socket */ | |
1064 | if (scb->readset && FD_ISSET(s, scb->readset)) | |
1065 | if (sock->rcvevent) | |
1066 | break; | |
1067 | if (scb->writeset && FD_ISSET(s, scb->writeset)) | |
1068 | if (sock->sendevent) | |
1069 | break; | |
1070 | } | |
1071 | } | |
1072 | if (scb) { | |
1073 | scb->sem_signalled = 1; | |
1074 | sys_sem_signal(selectsem); | |
1075 | sys_sem_signal(scb->sem); | |
1076 | } else { | |
1077 | sys_sem_signal(selectsem); | |
1078 | break; | |
1079 | } | |
1080 | } | |
1081 | } | |
1082 | ||
1083 | /** | |
1084 | * Unimplemented: Close one end of a full-duplex connection. | |
1085 | * Currently, the full connection is closed. | |
1086 | */ | |
1087 | int | |
1088 | lwip_shutdown(int s, int how) | |
1089 | { | |
1090 | LWIP_UNUSED_ARG(how); | |
1091 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how)); | |
1092 | return lwip_close(s); /* XXX temporary hack until proper implementation */ | |
1093 | } | |
1094 | ||
1095 | static int | |
1096 | lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local) | |
1097 | { | |
1098 | struct lwip_socket *sock; | |
1099 | struct sockaddr_in sin; | |
1100 | struct ip_addr naddr; | |
1101 | ||
1102 | sock = get_socket(s); | |
1103 | if (!sock) | |
1104 | return -1; | |
1105 | ||
1106 | memset(&sin, 0, sizeof(sin)); | |
1107 | sin.sin_len = sizeof(sin); | |
1108 | sin.sin_family = AF_INET; | |
1109 | ||
1110 | /* get the IP address and port */ | |
1111 | netconn_getaddr(sock->conn, &naddr, &sin.sin_port, local); | |
1112 | ||
1113 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s)); | |
1114 | ip_addr_debug_print(SOCKETS_DEBUG, &naddr); | |
1115 | LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%d)\n", sin.sin_port)); | |
1116 | ||
1117 | sin.sin_port = htons(sin.sin_port); | |
1118 | sin.sin_addr.s_addr = naddr.addr; | |
1119 | ||
1120 | if (*namelen > sizeof(sin)) | |
1121 | *namelen = sizeof(sin); | |
1122 | ||
1123 | SMEMCPY(name, &sin, *namelen); | |
1124 | sock_set_errno(sock, 0); | |
1125 | return 0; | |
1126 | } | |
1127 | ||
1128 | int | |
1129 | lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen) | |
1130 | { | |
1131 | return lwip_getaddrname(s, name, namelen, 0); | |
1132 | } | |
1133 | ||
1134 | int | |
1135 | lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen) | |
1136 | { | |
1137 | return lwip_getaddrname(s, name, namelen, 1); | |
1138 | } | |
1139 | ||
1140 | int | |
1141 | lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) | |
1142 | { | |
1143 | err_t err = ERR_OK; | |
1144 | struct lwip_socket *sock = get_socket(s); | |
1145 | struct lwip_setgetsockopt_data data; | |
1146 | ||
1147 | if (!sock) | |
1148 | return -1; | |
1149 | ||
1150 | if ((NULL == optval) || (NULL == optlen)) { | |
1151 | sock_set_errno(sock, EFAULT); | |
1152 | return -1; | |
1153 | } | |
1154 | ||
1155 | /* Do length and type checks for the various options first, to keep it readable. */ | |
1156 | switch (level) { | |
1157 | ||
1158 | /* Level: SOL_SOCKET */ | |
1159 | case SOL_SOCKET: | |
1160 | switch (optname) { | |
1161 | ||
1162 | case SO_ACCEPTCONN: | |
1163 | case SO_BROADCAST: | |
1164 | /* UNIMPL case SO_DEBUG: */ | |
1165 | /* UNIMPL case SO_DONTROUTE: */ | |
1166 | case SO_ERROR: | |
1167 | case SO_KEEPALIVE: | |
1168 | /* UNIMPL case SO_CONTIMEO: */ | |
1169 | /* UNIMPL case SO_SNDTIMEO: */ | |
1170 | #if LWIP_SO_RCVTIMEO | |
1171 | case SO_RCVTIMEO: | |
1172 | #endif /* LWIP_SO_RCVTIMEO */ | |
1173 | #if LWIP_SO_RCVBUF | |
1174 | case SO_RCVBUF: | |
1175 | #endif /* LWIP_SO_RCVBUF */ | |
1176 | /* UNIMPL case SO_OOBINLINE: */ | |
1177 | /* UNIMPL case SO_SNDBUF: */ | |
1178 | /* UNIMPL case SO_RCVLOWAT: */ | |
1179 | /* UNIMPL case SO_SNDLOWAT: */ | |
1180 | #if SO_REUSE | |
1181 | case SO_REUSEADDR: | |
1182 | case SO_REUSEPORT: | |
1183 | #endif /* SO_REUSE */ | |
1184 | case SO_TYPE: | |
1185 | /* UNIMPL case SO_USELOOPBACK: */ | |
1186 | if (*optlen < sizeof(int)) { | |
1187 | err = EINVAL; | |
1188 | } | |
1189 | break; | |
1190 | ||
1191 | case SO_NO_CHECK: | |
1192 | if (*optlen < sizeof(int)) { | |
1193 | err = EINVAL; | |
1194 | } | |
1195 | #if LWIP_UDP | |
1196 | if ((sock->conn->type != NETCONN_UDP) || | |
1197 | ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { | |
1198 | /* this flag is only available for UDP, not for UDP lite */ | |
1199 | err = EAFNOSUPPORT; | |
1200 | } | |
1201 | #endif /* LWIP_UDP */ | |
1202 | break; | |
1203 | ||
1204 | default: | |
1205 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", | |
1206 | s, optname)); | |
1207 | err = ENOPROTOOPT; | |
1208 | } /* switch (optname) */ | |
1209 | break; | |
1210 | ||
1211 | /* Level: IPPROTO_IP */ | |
1212 | case IPPROTO_IP: | |
1213 | switch (optname) { | |
1214 | /* UNIMPL case IP_HDRINCL: */ | |
1215 | /* UNIMPL case IP_RCVDSTADDR: */ | |
1216 | /* UNIMPL case IP_RCVIF: */ | |
1217 | case IP_TTL: | |
1218 | case IP_TOS: | |
1219 | if (*optlen < sizeof(int)) { | |
1220 | err = EINVAL; | |
1221 | } | |
1222 | break; | |
1223 | #if LWIP_IGMP | |
1224 | case IP_MULTICAST_TTL: | |
1225 | if (*optlen < sizeof(u8_t)) { | |
1226 | err = EINVAL; | |
1227 | } | |
1228 | break; | |
1229 | case IP_MULTICAST_IF: | |
1230 | if (*optlen < sizeof(struct in_addr)) { | |
1231 | err = EINVAL; | |
1232 | } | |
1233 | break; | |
1234 | #endif /* LWIP_IGMP */ | |
1235 | ||
1236 | default: | |
1237 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", | |
1238 | s, optname)); | |
1239 | err = ENOPROTOOPT; | |
1240 | } /* switch (optname) */ | |
1241 | break; | |
1242 | ||
1243 | #if LWIP_TCP | |
1244 | /* Level: IPPROTO_TCP */ | |
1245 | case IPPROTO_TCP: | |
1246 | if (*optlen < sizeof(int)) { | |
1247 | err = EINVAL; | |
1248 | break; | |
1249 | } | |
1250 | ||
1251 | /* If this is no TCP socket, ignore any options. */ | |
1252 | if (sock->conn->type != NETCONN_TCP) | |
1253 | return 0; | |
1254 | ||
1255 | switch (optname) { | |
1256 | case TCP_NODELAY: | |
1257 | case TCP_KEEPALIVE: | |
1258 | #if LWIP_TCP_KEEPALIVE | |
1259 | case TCP_KEEPIDLE: | |
1260 | case TCP_KEEPINTVL: | |
1261 | case TCP_KEEPCNT: | |
1262 | #endif /* LWIP_TCP_KEEPALIVE */ | |
1263 | break; | |
1264 | ||
1265 | default: | |
1266 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", | |
1267 | s, optname)); | |
1268 | err = ENOPROTOOPT; | |
1269 | } /* switch (optname) */ | |
1270 | break; | |
1271 | #endif /* LWIP_TCP */ | |
1272 | #if LWIP_UDP && LWIP_UDPLITE | |
1273 | /* Level: IPPROTO_UDPLITE */ | |
1274 | case IPPROTO_UDPLITE: | |
1275 | if (*optlen < sizeof(int)) { | |
1276 | err = EINVAL; | |
1277 | break; | |
1278 | } | |
1279 | ||
1280 | /* If this is no UDP lite socket, ignore any options. */ | |
1281 | if (sock->conn->type != NETCONN_UDPLITE) | |
1282 | return 0; | |
1283 | ||
1284 | switch (optname) { | |
1285 | case UDPLITE_SEND_CSCOV: | |
1286 | case UDPLITE_RECV_CSCOV: | |
1287 | break; | |
1288 | ||
1289 | default: | |
1290 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", | |
1291 | s, optname)); | |
1292 | err = ENOPROTOOPT; | |
1293 | } /* switch (optname) */ | |
1294 | break; | |
1295 | #endif /* LWIP_UDP && LWIP_UDPLITE*/ | |
1296 | /* UNDEFINED LEVEL */ | |
1297 | default: | |
1298 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", | |
1299 | s, level, optname)); | |
1300 | err = ENOPROTOOPT; | |
1301 | } /* switch */ | |
1302 | ||
1303 | ||
1304 | if (err != ERR_OK) { | |
1305 | sock_set_errno(sock, err); | |
1306 | return -1; | |
1307 | } | |
1308 | ||
1309 | /* Now do the actual option processing */ | |
1310 | data.sock = sock; | |
1311 | data.level = level; | |
1312 | data.optname = optname; | |
1313 | data.optval = optval; | |
1314 | data.optlen = optlen; | |
1315 | data.err = err; | |
1316 | tcpip_callback(lwip_getsockopt_internal, &data); | |
1317 | sys_arch_sem_wait(sock->conn->op_completed, 0); | |
1318 | /* maybe lwip_getsockopt_internal has changed err */ | |
1319 | err = data.err; | |
1320 | ||
1321 | sock_set_errno(sock, err); | |
1322 | return err ? -1 : 0; | |
1323 | } | |
1324 | ||
1325 | static void | |
1326 | lwip_getsockopt_internal(void *arg) | |
1327 | { | |
1328 | struct lwip_socket *sock; | |
1329 | #ifdef LWIP_DEBUG | |
1330 | int s; | |
1331 | #endif /* LWIP_DEBUG */ | |
1332 | int level, optname; | |
1333 | void *optval; | |
1334 | struct lwip_setgetsockopt_data *data; | |
1335 | ||
1336 | LWIP_ASSERT("arg != NULL", arg != NULL); | |
1337 | ||
1338 | data = (struct lwip_setgetsockopt_data*)arg; | |
1339 | sock = data->sock; | |
1340 | #ifdef LWIP_DEBUG | |
1341 | s = data->s; | |
1342 | #endif /* LWIP_DEBUG */ | |
1343 | level = data->level; | |
1344 | optname = data->optname; | |
1345 | optval = data->optval; | |
1346 | ||
1347 | switch (level) { | |
1348 | ||
1349 | /* Level: SOL_SOCKET */ | |
1350 | case SOL_SOCKET: | |
1351 | switch (optname) { | |
1352 | ||
1353 | /* The option flags */ | |
1354 | case SO_ACCEPTCONN: | |
1355 | case SO_BROADCAST: | |
1356 | /* UNIMPL case SO_DEBUG: */ | |
1357 | /* UNIMPL case SO_DONTROUTE: */ | |
1358 | case SO_KEEPALIVE: | |
1359 | /* UNIMPL case SO_OOBINCLUDE: */ | |
1360 | #if SO_REUSE | |
1361 | case SO_REUSEADDR: | |
1362 | case SO_REUSEPORT: | |
1363 | #endif /* SO_REUSE */ | |
1364 | /*case SO_USELOOPBACK: UNIMPL */ | |
1365 | *(int*)optval = sock->conn->pcb.ip->so_options & optname; | |
1366 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n", | |
1367 | s, optname, (*(int*)optval?"on":"off"))); | |
1368 | break; | |
1369 | ||
1370 | case SO_TYPE: | |
1371 | switch (NETCONNTYPE_GROUP(sock->conn->type)) { | |
1372 | case NETCONN_RAW: | |
1373 | *(int*)optval = SOCK_RAW; | |
1374 | break; | |
1375 | case NETCONN_TCP: | |
1376 | *(int*)optval = SOCK_STREAM; | |
1377 | break; | |
1378 | case NETCONN_UDP: | |
1379 | *(int*)optval = SOCK_DGRAM; | |
1380 | break; | |
1381 | default: /* unrecognized socket type */ | |
1382 | *(int*)optval = sock->conn->type; | |
1383 | LWIP_DEBUGF(SOCKETS_DEBUG, | |
1384 | ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n", | |
1385 | s, *(int *)optval)); | |
1386 | } /* switch (sock->conn->type) */ | |
1387 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n", | |
1388 | s, *(int *)optval)); | |
1389 | break; | |
1390 | ||
1391 | case SO_ERROR: | |
1392 | if (sock->err == 0) { | |
1393 | sock_set_errno(sock, err_to_errno(sock->conn->err)); | |
1394 | } | |
1395 | *(int *)optval = sock->err; | |
1396 | sock->err = 0; | |
1397 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n", | |
1398 | s, *(int *)optval)); | |
1399 | break; | |
1400 | ||
1401 | #if LWIP_SO_RCVTIMEO | |
1402 | case SO_RCVTIMEO: | |
1403 | *(int *)optval = sock->conn->recv_timeout; | |
1404 | break; | |
1405 | #endif /* LWIP_SO_RCVTIMEO */ | |
1406 | #if LWIP_SO_RCVBUF | |
1407 | case SO_RCVBUF: | |
1408 | *(int *)optval = sock->conn->recv_bufsize; | |
1409 | break; | |
1410 | #endif /* LWIP_SO_RCVBUF */ | |
1411 | #if LWIP_UDP | |
1412 | case SO_NO_CHECK: | |
1413 | *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0; | |
1414 | break; | |
1415 | #endif /* LWIP_UDP*/ | |
1416 | } /* switch (optname) */ | |
1417 | break; | |
1418 | ||
1419 | /* Level: IPPROTO_IP */ | |
1420 | case IPPROTO_IP: | |
1421 | switch (optname) { | |
1422 | case IP_TTL: | |
1423 | *(int*)optval = sock->conn->pcb.ip->ttl; | |
1424 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n", | |
1425 | s, *(int *)optval)); | |
1426 | break; | |
1427 | case IP_TOS: | |
1428 | *(int*)optval = sock->conn->pcb.ip->tos; | |
1429 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n", | |
1430 | s, *(int *)optval)); | |
1431 | break; | |
1432 | #if LWIP_IGMP | |
1433 | case IP_MULTICAST_TTL: | |
1434 | *(u8_t*)optval = sock->conn->pcb.ip->ttl; | |
1435 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n", | |
1436 | s, *(int *)optval)); | |
1437 | break; | |
1438 | case IP_MULTICAST_IF: | |
1439 | ((struct in_addr*) optval)->s_addr = sock->conn->pcb.udp->multicast_ip.addr; | |
1440 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%x\n", | |
1441 | s, *(u32_t *)optval)); | |
1442 | break; | |
1443 | #endif /* LWIP_IGMP */ | |
1444 | } /* switch (optname) */ | |
1445 | break; | |
1446 | ||
1447 | #if LWIP_TCP | |
1448 | /* Level: IPPROTO_TCP */ | |
1449 | case IPPROTO_TCP: | |
1450 | switch (optname) { | |
1451 | case TCP_NODELAY: | |
1452 | *(int*)optval = (sock->conn->pcb.tcp->flags & TF_NODELAY); | |
1453 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n", | |
1454 | s, (*(int*)optval)?"on":"off") ); | |
1455 | break; | |
1456 | case TCP_KEEPALIVE: | |
1457 | *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle; | |
1458 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPALIVE) = %d\n", | |
1459 | s, *(int *)optval)); | |
1460 | break; | |
1461 | ||
1462 | #if LWIP_TCP_KEEPALIVE | |
1463 | case TCP_KEEPIDLE: | |
1464 | *(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000); | |
1465 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPIDLE) = %d\n", | |
1466 | s, *(int *)optval)); | |
1467 | break; | |
1468 | case TCP_KEEPINTVL: | |
1469 | *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000); | |
1470 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPINTVL) = %d\n", | |
1471 | s, *(int *)optval)); | |
1472 | break; | |
1473 | case TCP_KEEPCNT: | |
1474 | *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt; | |
1475 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPCNT) = %d\n", | |
1476 | s, *(int *)optval)); | |
1477 | break; | |
1478 | #endif /* LWIP_TCP_KEEPALIVE */ | |
1479 | ||
1480 | } /* switch (optname) */ | |
1481 | break; | |
1482 | #endif /* LWIP_TCP */ | |
1483 | #if LWIP_UDP && LWIP_UDPLITE | |
1484 | /* Level: IPPROTO_UDPLITE */ | |
1485 | case IPPROTO_UDPLITE: | |
1486 | switch (optname) { | |
1487 | case UDPLITE_SEND_CSCOV: | |
1488 | *(int*)optval = sock->conn->pcb.udp->chksum_len_tx; | |
1489 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n", | |
1490 | s, (*(int*)optval)) ); | |
1491 | break; | |
1492 | case UDPLITE_RECV_CSCOV: | |
1493 | *(int*)optval = sock->conn->pcb.udp->chksum_len_rx; | |
1494 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n", | |
1495 | s, (*(int*)optval)) ); | |
1496 | break; | |
1497 | } /* switch (optname) */ | |
1498 | break; | |
1499 | #endif /* LWIP_UDP */ | |
1500 | } /* switch (level) */ | |
1501 | sys_sem_signal(sock->conn->op_completed); | |
1502 | } | |
1503 | ||
1504 | int | |
1505 | lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen) | |
1506 | { | |
1507 | struct lwip_socket *sock = get_socket(s); | |
1508 | int err = ERR_OK; | |
1509 | struct lwip_setgetsockopt_data data; | |
1510 | ||
1511 | if (!sock) | |
1512 | return -1; | |
1513 | ||
1514 | if (NULL == optval) { | |
1515 | sock_set_errno(sock, EFAULT); | |
1516 | return -1; | |
1517 | } | |
1518 | ||
1519 | /* Do length and type checks for the various options first, to keep it readable. */ | |
1520 | switch (level) { | |
1521 | ||
1522 | /* Level: SOL_SOCKET */ | |
1523 | case SOL_SOCKET: | |
1524 | switch (optname) { | |
1525 | ||
1526 | case SO_BROADCAST: | |
1527 | /* UNIMPL case SO_DEBUG: */ | |
1528 | /* UNIMPL case SO_DONTROUTE: */ | |
1529 | case SO_KEEPALIVE: | |
1530 | /* UNIMPL case case SO_CONTIMEO: */ | |
1531 | /* UNIMPL case case SO_SNDTIMEO: */ | |
1532 | #if LWIP_SO_RCVTIMEO | |
1533 | case SO_RCVTIMEO: | |
1534 | #endif /* LWIP_SO_RCVTIMEO */ | |
1535 | #if LWIP_SO_RCVBUF | |
1536 | case SO_RCVBUF: | |
1537 | #endif /* LWIP_SO_RCVBUF */ | |
1538 | /* UNIMPL case SO_OOBINLINE: */ | |
1539 | /* UNIMPL case SO_SNDBUF: */ | |
1540 | /* UNIMPL case SO_RCVLOWAT: */ | |
1541 | /* UNIMPL case SO_SNDLOWAT: */ | |
1542 | #if SO_REUSE | |
1543 | case SO_REUSEADDR: | |
1544 | case SO_REUSEPORT: | |
1545 | #endif /* SO_REUSE */ | |
1546 | /* UNIMPL case SO_USELOOPBACK: */ | |
1547 | if (optlen < sizeof(int)) { | |
1548 | err = EINVAL; | |
1549 | } | |
1550 | break; | |
1551 | case SO_NO_CHECK: | |
1552 | if (optlen < sizeof(int)) { | |
1553 | err = EINVAL; | |
1554 | } | |
1555 | #if LWIP_UDP | |
1556 | if ((sock->conn->type != NETCONN_UDP) || | |
1557 | ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { | |
1558 | /* this flag is only available for UDP, not for UDP lite */ | |
1559 | err = EAFNOSUPPORT; | |
1560 | } | |
1561 | #endif /* LWIP_UDP */ | |
1562 | break; | |
1563 | default: | |
1564 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", | |
1565 | s, optname)); | |
1566 | err = ENOPROTOOPT; | |
1567 | } /* switch (optname) */ | |
1568 | break; | |
1569 | ||
1570 | /* Level: IPPROTO_IP */ | |
1571 | case IPPROTO_IP: | |
1572 | switch (optname) { | |
1573 | /* UNIMPL case IP_HDRINCL: */ | |
1574 | /* UNIMPL case IP_RCVDSTADDR: */ | |
1575 | /* UNIMPL case IP_RCVIF: */ | |
1576 | case IP_TTL: | |
1577 | case IP_TOS: | |
1578 | if (optlen < sizeof(int)) { | |
1579 | err = EINVAL; | |
1580 | } | |
1581 | break; | |
1582 | #if LWIP_IGMP | |
1583 | case IP_MULTICAST_TTL: | |
1584 | if (optlen < sizeof(u8_t)) { | |
1585 | err = EINVAL; | |
1586 | } | |
1587 | if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { | |
1588 | err = EAFNOSUPPORT; | |
1589 | } | |
1590 | break; | |
1591 | case IP_MULTICAST_IF: | |
1592 | if (optlen < sizeof(struct in_addr)) { | |
1593 | err = EINVAL; | |
1594 | } | |
1595 | if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { | |
1596 | err = EAFNOSUPPORT; | |
1597 | } | |
1598 | break; | |
1599 | case IP_ADD_MEMBERSHIP: | |
1600 | case IP_DROP_MEMBERSHIP: | |
1601 | if (optlen < sizeof(struct ip_mreq)) { | |
1602 | err = EINVAL; | |
1603 | } | |
1604 | if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { | |
1605 | err = EAFNOSUPPORT; | |
1606 | } | |
1607 | break; | |
1608 | #endif /* LWIP_IGMP */ | |
1609 | default: | |
1610 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", | |
1611 | s, optname)); | |
1612 | err = ENOPROTOOPT; | |
1613 | } /* switch (optname) */ | |
1614 | break; | |
1615 | ||
1616 | #if LWIP_TCP | |
1617 | /* Level: IPPROTO_TCP */ | |
1618 | case IPPROTO_TCP: | |
1619 | if (optlen < sizeof(int)) { | |
1620 | err = EINVAL; | |
1621 | break; | |
1622 | } | |
1623 | ||
1624 | /* If this is no TCP socket, ignore any options. */ | |
1625 | if (sock->conn->type != NETCONN_TCP) | |
1626 | return 0; | |
1627 | ||
1628 | switch (optname) { | |
1629 | case TCP_NODELAY: | |
1630 | case TCP_KEEPALIVE: | |
1631 | #if LWIP_TCP_KEEPALIVE | |
1632 | case TCP_KEEPIDLE: | |
1633 | case TCP_KEEPINTVL: | |
1634 | case TCP_KEEPCNT: | |
1635 | #endif /* LWIP_TCP_KEEPALIVE */ | |
1636 | break; | |
1637 | ||
1638 | default: | |
1639 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", | |
1640 | s, optname)); | |
1641 | err = ENOPROTOOPT; | |
1642 | } /* switch (optname) */ | |
1643 | break; | |
1644 | #endif /* LWIP_TCP */ | |
1645 | #if LWIP_UDP && LWIP_UDPLITE | |
1646 | /* Level: IPPROTO_UDPLITE */ | |
1647 | case IPPROTO_UDPLITE: | |
1648 | if (optlen < sizeof(int)) { | |
1649 | err = EINVAL; | |
1650 | break; | |
1651 | } | |
1652 | ||
1653 | /* If this is no UDP lite socket, ignore any options. */ | |
1654 | if (sock->conn->type != NETCONN_UDPLITE) | |
1655 | return 0; | |
1656 | ||
1657 | switch (optname) { | |
1658 | case UDPLITE_SEND_CSCOV: | |
1659 | case UDPLITE_RECV_CSCOV: | |
1660 | break; | |
1661 | ||
1662 | default: | |
1663 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", | |
1664 | s, optname)); | |
1665 | err = ENOPROTOOPT; | |
1666 | } /* switch (optname) */ | |
1667 | break; | |
1668 | #endif /* LWIP_UDP && LWIP_UDPLITE */ | |
1669 | /* UNDEFINED LEVEL */ | |
1670 | default: | |
1671 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", | |
1672 | s, level, optname)); | |
1673 | err = ENOPROTOOPT; | |
1674 | } /* switch (level) */ | |
1675 | ||
1676 | ||
1677 | if (err != ERR_OK) { | |
1678 | sock_set_errno(sock, err); | |
1679 | return -1; | |
1680 | } | |
1681 | ||
1682 | ||
1683 | /* Now do the actual option processing */ | |
1684 | data.sock = sock; | |
1685 | data.level = level; | |
1686 | data.optname = optname; | |
1687 | data.optval = (void*)optval; | |
1688 | data.optlen = &optlen; | |
1689 | data.err = err; | |
1690 | tcpip_callback(lwip_setsockopt_internal, &data); | |
1691 | sys_arch_sem_wait(sock->conn->op_completed, 0); | |
1692 | /* maybe lwip_setsockopt_internal has changed err */ | |
1693 | err = data.err; | |
1694 | ||
1695 | sock_set_errno(sock, err); | |
1696 | return err ? -1 : 0; | |
1697 | } | |
1698 | ||
1699 | static void | |
1700 | lwip_setsockopt_internal(void *arg) | |
1701 | { | |
1702 | struct lwip_socket *sock; | |
1703 | #ifdef LWIP_DEBUG | |
1704 | int s; | |
1705 | #endif /* LWIP_DEBUG */ | |
1706 | int level, optname; | |
1707 | const void *optval; | |
1708 | struct lwip_setgetsockopt_data *data; | |
1709 | ||
1710 | LWIP_ASSERT("arg != NULL", arg != NULL); | |
1711 | ||
1712 | data = (struct lwip_setgetsockopt_data*)arg; | |
1713 | sock = data->sock; | |
1714 | #ifdef LWIP_DEBUG | |
1715 | s = data->s; | |
1716 | #endif /* LWIP_DEBUG */ | |
1717 | level = data->level; | |
1718 | optname = data->optname; | |
1719 | optval = data->optval; | |
1720 | ||
1721 | switch (level) { | |
1722 | ||
1723 | /* Level: SOL_SOCKET */ | |
1724 | case SOL_SOCKET: | |
1725 | switch (optname) { | |
1726 | ||
1727 | /* The option flags */ | |
1728 | case SO_BROADCAST: | |
1729 | /* UNIMPL case SO_DEBUG: */ | |
1730 | /* UNIMPL case SO_DONTROUTE: */ | |
1731 | case SO_KEEPALIVE: | |
1732 | /* UNIMPL case SO_OOBINCLUDE: */ | |
1733 | #if SO_REUSE | |
1734 | case SO_REUSEADDR: | |
1735 | case SO_REUSEPORT: | |
1736 | #endif /* SO_REUSE */ | |
1737 | /* UNIMPL case SO_USELOOPBACK: */ | |
1738 | if (*(int*)optval) { | |
1739 | sock->conn->pcb.ip->so_options |= optname; | |
1740 | } else { | |
1741 | sock->conn->pcb.ip->so_options &= ~optname; | |
1742 | } | |
1743 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n", | |
1744 | s, optname, (*(int*)optval?"on":"off"))); | |
1745 | break; | |
1746 | #if LWIP_SO_RCVTIMEO | |
1747 | case SO_RCVTIMEO: | |
1748 | sock->conn->recv_timeout = ( *(int*)optval ); | |
1749 | break; | |
1750 | #endif /* LWIP_SO_RCVTIMEO */ | |
1751 | #if LWIP_SO_RCVBUF | |
1752 | case SO_RCVBUF: | |
1753 | sock->conn->recv_bufsize = ( *(int*)optval ); | |
1754 | break; | |
1755 | #endif /* LWIP_SO_RCVBUF */ | |
1756 | #if LWIP_UDP | |
1757 | case SO_NO_CHECK: | |
1758 | if (*(int*)optval) { | |
1759 | udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM); | |
1760 | } else { | |
1761 | udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM); | |
1762 | } | |
1763 | break; | |
1764 | #endif /* LWIP_UDP */ | |
1765 | } /* switch (optname) */ | |
1766 | break; | |
1767 | ||
1768 | /* Level: IPPROTO_IP */ | |
1769 | case IPPROTO_IP: | |
1770 | switch (optname) { | |
1771 | case IP_TTL: | |
1772 | sock->conn->pcb.ip->ttl = (u8_t)(*(int*)optval); | |
1773 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %u\n", | |
1774 | s, sock->conn->pcb.ip->ttl)); | |
1775 | break; | |
1776 | case IP_TOS: | |
1777 | sock->conn->pcb.ip->tos = (u8_t)(*(int*)optval); | |
1778 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %u\n", | |
1779 | s, sock->conn->pcb.ip->tos)); | |
1780 | break; | |
1781 | #if LWIP_IGMP | |
1782 | case IP_MULTICAST_TTL: | |
1783 | sock->conn->pcb.udp->ttl = (u8_t)(*(u8_t*)optval); | |
1784 | break; | |
1785 | case IP_MULTICAST_IF: | |
1786 | sock->conn->pcb.udp->multicast_ip.addr = ((struct in_addr*) optval)->s_addr; | |
1787 | break; | |
1788 | case IP_ADD_MEMBERSHIP: | |
1789 | case IP_DROP_MEMBERSHIP: | |
1790 | { | |
1791 | /* If this is a TCP or a RAW socket, ignore these options. */ | |
1792 | struct ip_mreq *imr = (struct ip_mreq *)optval; | |
1793 | if(optname == IP_ADD_MEMBERSHIP){ | |
1794 | data->err = igmp_joingroup((struct ip_addr*)&(imr->imr_interface.s_addr), (struct ip_addr*)&(imr->imr_multiaddr.s_addr)); | |
1795 | } else { | |
1796 | data->err = igmp_leavegroup((struct ip_addr*)&(imr->imr_interface.s_addr), (struct ip_addr*)&(imr->imr_multiaddr.s_addr)); | |
1797 | } | |
1798 | if(data->err != ERR_OK) { | |
1799 | data->err = EADDRNOTAVAIL; | |
1800 | } | |
1801 | } | |
1802 | break; | |
1803 | #endif /* LWIP_IGMP */ | |
1804 | } /* switch (optname) */ | |
1805 | break; | |
1806 | ||
1807 | #if LWIP_TCP | |
1808 | /* Level: IPPROTO_TCP */ | |
1809 | case IPPROTO_TCP: | |
1810 | switch (optname) { | |
1811 | case TCP_NODELAY: | |
1812 | if (*(int*)optval) { | |
1813 | sock->conn->pcb.tcp->flags |= TF_NODELAY; | |
1814 | } else { | |
1815 | sock->conn->pcb.tcp->flags &= ~TF_NODELAY; | |
1816 | } | |
1817 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n", | |
1818 | s, (*(int *)optval)?"on":"off") ); | |
1819 | break; | |
1820 | case TCP_KEEPALIVE: | |
1821 | sock->conn->pcb.tcp->keep_idle = (u32_t)(*(int*)optval); | |
1822 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %lu\n", | |
1823 | s, sock->conn->pcb.tcp->keep_idle)); | |
1824 | break; | |
1825 | ||
1826 | #if LWIP_TCP_KEEPALIVE | |
1827 | case TCP_KEEPIDLE: | |
1828 | sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(int*)optval); | |
1829 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %lu\n", | |
1830 | s, sock->conn->pcb.tcp->keep_idle)); | |
1831 | break; | |
1832 | case TCP_KEEPINTVL: | |
1833 | sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(int*)optval); | |
1834 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %lu\n", | |
1835 | s, sock->conn->pcb.tcp->keep_intvl)); | |
1836 | break; | |
1837 | case TCP_KEEPCNT: | |
1838 | sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(int*)optval); | |
1839 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %lu\n", | |
1840 | s, sock->conn->pcb.tcp->keep_cnt)); | |
1841 | break; | |
1842 | #endif /* LWIP_TCP_KEEPALIVE */ | |
1843 | ||
1844 | } /* switch (optname) */ | |
1845 | break; | |
1846 | #endif /* LWIP_TCP*/ | |
1847 | #if LWIP_UDP && LWIP_UDPLITE | |
1848 | /* Level: IPPROTO_UDPLITE */ | |
1849 | case IPPROTO_UDPLITE: | |
1850 | switch (optname) { | |
1851 | case UDPLITE_SEND_CSCOV: | |
1852 | if ((*(int*)optval != 0) && (*(int*)optval < 8)) { | |
1853 | /* don't allow illegal values! */ | |
1854 | sock->conn->pcb.udp->chksum_len_tx = 8; | |
1855 | } else { | |
1856 | sock->conn->pcb.udp->chksum_len_tx = *(int*)optval; | |
1857 | } | |
1858 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n", | |
1859 | s, (*(int*)optval)) ); | |
1860 | break; | |
1861 | case UDPLITE_RECV_CSCOV: | |
1862 | if ((*(int*)optval != 0) && (*(int*)optval < 8)) { | |
1863 | /* don't allow illegal values! */ | |
1864 | sock->conn->pcb.udp->chksum_len_rx = 8; | |
1865 | } else { | |
1866 | sock->conn->pcb.udp->chksum_len_rx = *(int*)optval; | |
1867 | } | |
1868 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n", | |
1869 | s, (*(int*)optval)) ); | |
1870 | break; | |
1871 | } /* switch (optname) */ | |
1872 | break; | |
1873 | #endif /* LWIP_UDP */ | |
1874 | } /* switch (level) */ | |
1875 | sys_sem_signal(sock->conn->op_completed); | |
1876 | } | |
1877 | ||
1878 | int | |
1879 | lwip_ioctl(int s, long cmd, void *argp) | |
1880 | { | |
1881 | struct lwip_socket *sock = get_socket(s); | |
1882 | u16_t buflen = 0; | |
1883 | ||
1884 | if (!sock) | |
1885 | return -1; | |
1886 | ||
1887 | switch (cmd) { | |
1888 | case FIONREAD: | |
1889 | if (!argp) { | |
1890 | sock_set_errno(sock, EINVAL); | |
1891 | return -1; | |
1892 | } | |
1893 | ||
1894 | SYS_ARCH_GET(sock->conn->recv_avail, *((u16_t*)argp)); | |
1895 | ||
1896 | /* Check if there is data left from the last recv operation. /maq 041215 */ | |
1897 | if (sock->lastdata) { | |
1898 | buflen = netbuf_len(sock->lastdata); | |
1899 | buflen -= sock->lastoffset; | |
1900 | ||
1901 | *((u16_t*)argp) += buflen; | |
1902 | } | |
1903 | ||
1904 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %u\n", s, argp, *((u16_t*)argp))); | |
1905 | sock_set_errno(sock, 0); | |
1906 | return 0; | |
1907 | ||
1908 | case FIONBIO: | |
1909 | if (argp && *(u32_t*)argp) | |
1910 | sock->flags |= O_NONBLOCK; | |
1911 | else | |
1912 | sock->flags &= ~O_NONBLOCK; | |
1913 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, !!(sock->flags & O_NONBLOCK))); | |
1914 | sock_set_errno(sock, 0); | |
1915 | return 0; | |
1916 | ||
1917 | default: | |
1918 | LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp)); | |
1919 | sock_set_errno(sock, ENOSYS); /* not yet implemented */ | |
1920 | return -1; | |
1921 | } /* switch (cmd) */ | |
1922 | } | |
1923 | ||
1924 | #endif /* LWIP_SOCKET */ |