1/**
2 * @file
3 * Sequential API Internal 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 */
38
39#include "lwip/opt.h"
40
41#if LWIP_NETCONN                /* don't build if not configured for use in lwipopts.h */
42
43#include "lwip/api_msg.h"
44
45#include "lwip/ip.h"
46#include "lwip/udp.h"
47#include "lwip/tcp.h"
48#include "lwip/raw.h"
49
50#include "lwip/memp.h"
51#include "lwip/tcpip.h"
52#include "lwip/igmp.h"
53#include "lwip/dns.h"
54
55#include <string.h>
56
57/* forward declarations */
58#if LWIP_TCP
59static err_t do_writemore(struct netconn *conn);
60static void do_close_internal(struct netconn *conn);
61#endif
62
63#if LWIP_RAW
64/**
65 * Receive callback function for RAW netconns.
66 * Doesn't 'eat' the packet, only references it and sends it to
67 * conn->recvmbox
68 *
69 * @see raw.h (struct raw_pcb.recv) for parameters and return value
70 */
71static u8_t
72recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p, struct ip_addr *addr)
73{
74    struct pbuf *q;
75    struct netbuf *buf;
76    struct netconn *conn;
77
78#if LWIP_SO_RCVBUF
79    int recv_avail;
80#endif                          /* LWIP_SO_RCVBUF */
81
82    LWIP_UNUSED_ARG(addr);
83    conn = arg;
84
85#if LWIP_SO_RCVBUF
86    SYS_ARCH_GET(conn->recv_avail, recv_avail);
87    if ((conn != NULL) && (conn->recvmbox != SYS_MBOX_NULL) &&
88        ((recv_avail + (int) (p->tot_len)) <= conn->recv_bufsize)) {
89#else                           /* LWIP_SO_RCVBUF */
90    if ((conn != NULL) && (conn->recvmbox != SYS_MBOX_NULL)) {
91#endif                          /* LWIP_SO_RCVBUF */
92        /* copy the whole packet into new pbufs */
93        q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
94        if (q != NULL) {
95            if (pbuf_copy(q, p) != ERR_OK) {
96                pbuf_free(q);
97                q = NULL;
98            }
99        }
100
101        if (q != NULL) {
102            buf = memp_malloc(MEMP_NETBUF);
103            if (buf == NULL) {
104                pbuf_free(q);
105                return 0;
106            }
107
108            buf->p = q;
109            buf->ptr = q;
110            buf->addr = &(((struct ip_hdr *) (q->payload))->src);
111            buf->port = pcb->protocol;
112
113            if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) {
114                netbuf_delete(buf);
115                return 0;
116            } else {
117                SYS_ARCH_INC(conn->recv_avail, q->tot_len);
118                /* Register event with callback */
119                API_EVENT(conn, NETCONN_EVT_RCVPLUS, q->tot_len);
120            }
121        }
122    }
123
124    return 0;                   /* do not eat the packet */
125}
126#endif                          /* LWIP_RAW */
127
128#if LWIP_UDP
129/**
130 * Receive callback function for UDP netconns.
131 * Posts the packet to conn->recvmbox or deletes it on memory error.
132 *
133 * @see udp.h (struct udp_pcb.recv) for parameters
134 */
135static void
136recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
137         struct ip_addr *addr, u16_t port)
138{
139    struct netbuf *buf;
140    struct netconn *conn;
141
142#if LWIP_SO_RCVBUF
143    int recv_avail;
144#endif                          /* LWIP_SO_RCVBUF */
145
146    LWIP_UNUSED_ARG(pcb);       /* only used for asserts... */
147    LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL);
148    LWIP_ASSERT("recv_udp must have an argument", arg != NULL);
149    conn = arg;
150    LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb);
151
152#if LWIP_SO_RCVBUF
153    SYS_ARCH_GET(conn->recv_avail, recv_avail);
154    if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL) ||
155        ((recv_avail + (int) (p->tot_len)) > conn->recv_bufsize)) {
156#else                           /* LWIP_SO_RCVBUF */
157    if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL)) {
158#endif                          /* LWIP_SO_RCVBUF */
159        pbuf_free(p);
160        return;
161    }
162
163    buf = memp_malloc(MEMP_NETBUF);
164    if (buf == NULL) {
165        pbuf_free(p);
166        return;
167    } else {
168        buf->p = p;
169        buf->ptr = p;
170        buf->addr = addr;
171        buf->port = port;
172    }
173
174    if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) {
175        netbuf_delete(buf);
176        return;
177    } else {
178        SYS_ARCH_INC(conn->recv_avail, p->tot_len);
179        /* Register event with callback */
180        API_EVENT(conn, NETCONN_EVT_RCVPLUS, p->tot_len);
181    }
182}
183#endif                          /* LWIP_UDP */
184
185#if LWIP_TCP
186/**
187 * Receive callback function for TCP netconns.
188 * Posts the packet to conn->recvmbox, but doesn't delete it on errors.
189 *
190 * @see tcp.h (struct tcp_pcb.recv) for parameters and return value
191 */
192static err_t recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
193{
194    struct netconn *conn;
195    u16_t len;
196
197    LWIP_UNUSED_ARG(pcb);
198    LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL);
199    LWIP_ASSERT("recv_tcp must have an argument", arg != NULL);
200    conn = arg;
201    LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb);
202
203    if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL)) {
204        return ERR_VAL;
205    }
206
207    conn->err = err;
208    if (p != NULL) {
209        len = p->tot_len;
210        SYS_ARCH_INC(conn->recv_avail, len);
211    } else {
212        len = 0;
213    }
214    if (sys_mbox_trypost(conn->recvmbox, p) != ERR_OK) {
215        return ERR_MEM;
216    } else {
217        /* Register event with callback */
218        API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
219    }
220
221    return ERR_OK;
222}
223
224/**
225 * Poll callback function for TCP netconns.
226 * Wakes up an application thread that waits for a connection to close
227 * or data to be sent. The application thread then takes the
228 * appropriate action to go on.
229 *
230 * Signals the conn->sem.
231 * netconn_close waits for conn->sem if closing failed.
232 *
233 * @see tcp.h (struct tcp_pcb.poll) for parameters and return value
234 */
235static err_t poll_tcp(void *arg, struct tcp_pcb *pcb)
236{
237    struct netconn *conn = arg;
238
239    LWIP_UNUSED_ARG(pcb);
240    LWIP_ASSERT("conn != NULL", (conn != NULL));
241
242    if (conn->state == NETCONN_WRITE) {
243        do_writemore(conn);
244    } else if (conn->state == NETCONN_CLOSE) {
245        do_close_internal(conn);
246    }
247
248    return ERR_OK;
249}
250
251/**
252 * Sent callback function for TCP netconns.
253 * Signals the conn->sem and calls API_EVENT.
254 * netconn_write waits for conn->sem if send buffer is low.
255 *
256 * @see tcp.h (struct tcp_pcb.sent) for parameters and return value
257 */
258static err_t sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
259{
260    struct netconn *conn = arg;
261
262    LWIP_UNUSED_ARG(pcb);
263    LWIP_ASSERT("conn != NULL", (conn != NULL));
264
265    if (conn->state == NETCONN_WRITE) {
266        LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
267        do_writemore(conn);
268    } else if (conn->state == NETCONN_CLOSE) {
269        do_close_internal(conn);
270    }
271
272    if (conn) {
273        if ((conn->pcb.tcp != NULL)
274            && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT)) {
275            API_EVENT(conn, NETCONN_EVT_SENDPLUS, len);
276        }
277    }
278
279    return ERR_OK;
280}
281
282/**
283 * Error callback function for TCP netconns.
284 * Signals conn->sem, posts to all conn mboxes and calls API_EVENT.
285 * The application thread has then to decide what to do.
286 *
287 * @see tcp.h (struct tcp_pcb.err) for parameters
288 */
289static void err_tcp(void *arg, err_t err)
290{
291    struct netconn *conn;
292
293    conn = arg;
294    LWIP_ASSERT("conn != NULL", (conn != NULL));
295
296    conn->pcb.tcp = NULL;
297
298    conn->err = err;
299    if (conn->recvmbox != SYS_MBOX_NULL) {
300        /* Register event with callback */
301        API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
302        sys_mbox_post(conn->recvmbox, NULL);
303    }
304    if (conn->op_completed != SYS_SEM_NULL && conn->state == NETCONN_CONNECT) {
305        conn->state = NETCONN_NONE;
306        sys_sem_signal(conn->op_completed);
307    }
308    if (conn->acceptmbox != SYS_MBOX_NULL) {
309        /* Register event with callback */
310        API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
311        sys_mbox_post(conn->acceptmbox, NULL);
312    }
313    if ((conn->state == NETCONN_WRITE) || (conn->state == NETCONN_CLOSE)) {
314        /* calling do_writemore/do_close_internal is not necessary
315           since the pcb has already been deleted! */
316        conn->state = NETCONN_NONE;
317        /* wake up the waiting task */
318        sys_sem_signal(conn->op_completed);
319    }
320}
321
322/**
323 * Setup a tcp_pcb with the correct callback function pointers
324 * and their arguments.
325 *
326 * @param conn the TCP netconn to setup
327 */
328static void setup_tcp(struct netconn *conn)
329{
330    struct tcp_pcb *pcb;
331
332    pcb = conn->pcb.tcp;
333    tcp_arg(pcb, conn);
334    tcp_recv(pcb, recv_tcp);
335    tcp_sent(pcb, sent_tcp);
336    tcp_poll(pcb, poll_tcp, 4);
337    tcp_err(pcb, err_tcp);
338}
339
340/**
341 * Accept callback function for TCP netconns.
342 * Allocates a new netconn and posts that to conn->acceptmbox.
343 *
344 * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value
345 */
346static err_t accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
347{
348    struct netconn *newconn;
349    struct netconn *conn;
350
351#if API_MSG_DEBUG
352#if TCP_DEBUG
353    tcp_debug_print_state(newpcb->state);
354#endif                          /* TCP_DEBUG */
355#endif                          /* API_MSG_DEBUG */
356    conn = (struct netconn *) arg;
357
358    LWIP_ERROR("accept_function: invalid conn->acceptmbox",
359               conn->acceptmbox != SYS_MBOX_NULL, return ERR_VAL;
360      );
361
362    /* We have to set the callback here even though
363     * the new socket is unknown. conn->socket is marked as -1. */
364    newconn = netconn_alloc(conn->type, conn->callback);
365    if (newconn == NULL) {
366        return ERR_MEM;
367    }
368    newconn->pcb.tcp = newpcb;
369
370    setup_tcp(newconn);
371    newconn->err = err;
372
373    if (sys_mbox_trypost(conn->acceptmbox, newconn) != ERR_OK) {
374        /* When returning != ERR_OK, the connection is aborted in tcp_process(),
375           so do nothing here! */
376        newconn->pcb.tcp = NULL;
377        netconn_free(newconn);
378        return ERR_MEM;
379    } else {
380        /* Register event with callback */
381        API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
382    }
383
384    return ERR_OK;
385}
386#endif                          /* LWIP_TCP */
387
388/**
389 * Create a new pcb of a specific type.
390 * Called from do_newconn().
391 *
392 * @param msg the api_msg_msg describing the connection type
393 * @return msg->conn->err, but the return value is currently ignored
394 */
395static err_t pcb_new(struct api_msg_msg *msg)
396{
397    msg->conn->err = ERR_OK;
398
399    LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
400
401    /* Allocate a PCB for this connection */
402    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
403#if LWIP_RAW
404        case NETCONN_RAW:
405            msg->conn->pcb.raw = raw_new(msg->msg.n.proto);
406            if (msg->conn->pcb.raw == NULL) {
407                msg->conn->err = ERR_MEM;
408                break;
409            }
410            raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
411            break;
412#endif                          /* LWIP_RAW */
413#if LWIP_UDP
414        case NETCONN_UDP:
415            msg->conn->pcb.udp = udp_new();
416            if (msg->conn->pcb.udp == NULL) {
417                msg->conn->err = ERR_MEM;
418                break;
419            }
420#if LWIP_UDPLITE
421            if (msg->conn->type == NETCONN_UDPLITE) {
422                udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
423            }
424#endif                          /* LWIP_UDPLITE */
425            if (msg->conn->type == NETCONN_UDPNOCHKSUM) {
426                udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
427            }
428            udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
429            break;
430#endif                          /* LWIP_UDP */
431#if LWIP_TCP
432        case NETCONN_TCP:
433            msg->conn->pcb.tcp = tcp_new();
434            if (msg->conn->pcb.tcp == NULL) {
435                msg->conn->err = ERR_MEM;
436                break;
437            }
438            setup_tcp(msg->conn);
439            break;
440#endif                          /* LWIP_TCP */
441        default:
442            /* Unsupported netconn type, e.g. protocol disabled */
443            msg->conn->err = ERR_VAL;
444            break;
445    }
446
447    return msg->conn->err;
448}
449
450/**
451 * Create a new pcb of a specific type inside a netconn.
452 * Called from netconn_new_with_proto_and_callback.
453 *
454 * @param msg the api_msg_msg describing the connection type
455 */
456void do_newconn(struct api_msg_msg *msg)
457{
458    if (msg->conn->pcb.tcp == NULL) {
459        pcb_new(msg);
460    }
461    /* Else? This "new" connection already has a PCB allocated. */
462    /* Is this an error condition? Should it be deleted? */
463    /* We currently just are happy and return. */
464
465    TCPIP_APIMSG_ACK(msg);
466}
467
468/**
469 * Create a new netconn (of a specific type) that has a callback function.
470 * The corresponding pcb is NOT created!
471 *
472 * @param t the type of 'connection' to create (@see enum netconn_type)
473 * @param proto the IP protocol for RAW IP pcbs
474 * @param callback a function to call on status changes (RX available, TX'ed)
475 * @return a newly allocated struct netconn or
476 *         NULL on memory error
477 */
478struct netconn *netconn_alloc(enum netconn_type t, netconn_callback callback)
479{
480    struct netconn *conn;
481    int size;
482
483    conn = memp_malloc(MEMP_NETCONN);
484    if (conn == NULL) {
485        return NULL;
486    }
487
488    conn->err = ERR_OK;
489    conn->type = t;
490    conn->pcb.tcp = NULL;
491
492#if (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_UDP_RECVMBOX_SIZE) && \
493    (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_TCP_RECVMBOX_SIZE)
494    size = DEFAULT_RAW_RECVMBOX_SIZE;
495#else
496    switch (NETCONNTYPE_GROUP(t)) {
497#if LWIP_RAW
498        case NETCONN_RAW:
499            size = DEFAULT_RAW_RECVMBOX_SIZE;
500            break;
501#endif                          /* LWIP_RAW */
502#if LWIP_UDP
503        case NETCONN_UDP:
504            size = DEFAULT_UDP_RECVMBOX_SIZE;
505            break;
506#endif                          /* LWIP_UDP */
507#if LWIP_TCP
508        case NETCONN_TCP:
509            size = DEFAULT_TCP_RECVMBOX_SIZE;
510            break;
511#endif                          /* LWIP_TCP */
512        default:
513            LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
514            break;
515    }
516#endif
517
518    if ((conn->op_completed = sys_sem_new(0)) == SYS_SEM_NULL) {
519        memp_free(MEMP_NETCONN, conn);
520        return NULL;
521    }
522    if ((conn->recvmbox = sys_mbox_new(size)) == SYS_MBOX_NULL) {
523        sys_sem_free(conn->op_completed);
524        memp_free(MEMP_NETCONN, conn);
525        return NULL;
526    }
527
528    conn->acceptmbox = SYS_MBOX_NULL;
529    conn->state = NETCONN_NONE;
530    /* initialize socket to -1 since 0 is a valid socket */
531    conn->socket = -1;
532    conn->callback = callback;
533    conn->recv_avail = 0;
534#if LWIP_TCP
535    conn->write_msg = NULL;
536    conn->write_offset = 0;
537#if LWIP_TCPIP_CORE_LOCKING
538    conn->write_delayed = 0;
539#endif                          /* LWIP_TCPIP_CORE_LOCKING */
540#endif                          /* LWIP_TCP */
541#if LWIP_SO_RCVTIMEO
542    conn->recv_timeout = 0;
543#endif                          /* LWIP_SO_RCVTIMEO */
544#if LWIP_SO_RCVBUF
545    conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
546#endif                          /* LWIP_SO_RCVBUF */
547    return conn;
548}
549
550/**
551 * Delete a netconn and all its resources.
552 * The pcb is NOT freed (since we might not be in the right thread context do this).
553 *
554 * @param conn the netconn to free
555 */
556void netconn_free(struct netconn *conn)
557{
558    void *mem;
559
560    LWIP_ASSERT("PCB must be deallocated outside this function",
561                conn->pcb.tcp == NULL);
562
563    /* Drain the recvmbox. */
564    if (conn->recvmbox != SYS_MBOX_NULL) {
565        while (sys_mbox_tryfetch(conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
566            if (conn->type == NETCONN_TCP) {
567                if (mem != NULL) {
568                    pbuf_free((struct pbuf *) mem);
569                }
570            } else {
571                netbuf_delete((struct netbuf *) mem);
572            }
573        }
574        sys_mbox_free(conn->recvmbox);
575        conn->recvmbox = SYS_MBOX_NULL;
576    }
577
578    /* Drain the acceptmbox. */
579    if (conn->acceptmbox != SYS_MBOX_NULL) {
580        while (sys_mbox_tryfetch(conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
581            netconn_delete((struct netconn *) mem);
582        }
583        sys_mbox_free(conn->acceptmbox);
584        conn->acceptmbox = SYS_MBOX_NULL;
585    }
586
587    sys_sem_free(conn->op_completed);
588    conn->op_completed = SYS_SEM_NULL;
589
590    memp_free(MEMP_NETCONN, conn);
591}
592
593#if LWIP_TCP
594/**
595 * Internal helper function to close a TCP netconn: since this sometimes
596 * doesn't work at the first attempt, this function is called from multiple
597 * places.
598 *
599 * @param conn the TCP netconn to close
600 */
601static void do_close_internal(struct netconn *conn)
602{
603    err_t err;
604
605    LWIP_ASSERT("invalid conn", (conn != NULL));
606    LWIP_ASSERT("this is for tcp netconns only", (conn->type == NETCONN_TCP));
607    LWIP_ASSERT("conn must be in state NETCONN_CLOSE",
608                (conn->state == NETCONN_CLOSE));
609    LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
610
611    /* Set back some callback pointers */
612    tcp_arg(conn->pcb.tcp, NULL);
613    if (conn->pcb.tcp->state == LISTEN) {
614        tcp_accept(conn->pcb.tcp, NULL);
615    } else {
616        tcp_recv(conn->pcb.tcp, NULL);
617        tcp_accept(conn->pcb.tcp, NULL);
618        /* some callbacks have to be reset if tcp_close is not successful */
619        tcp_sent(conn->pcb.tcp, NULL);
620        tcp_poll(conn->pcb.tcp, NULL, 4);
621        tcp_err(conn->pcb.tcp, NULL);
622    }
623    /* Try to close the connection */
624    err = tcp_close(conn->pcb.tcp);
625    if (err == ERR_OK) {
626        /* Closing succeeded */
627        conn->state = NETCONN_NONE;
628        /* Set back some callback pointers as conn is going away */
629        conn->pcb.tcp = NULL;
630        conn->err = ERR_OK;
631        /* Trigger select() in socket layer. This send should something else so the
632           errorfd is set, not the read and write fd! */
633        API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
634        API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
635        /* wake up the application task */
636        sys_sem_signal(conn->op_completed);
637    } else {
638        /* Closing failed, restore some of the callbacks */
639        /* Closing of listen pcb will never fail! */
640        LWIP_ASSERT("Closing a listen pcb may not fail!",
641                    (conn->pcb.tcp->state != LISTEN));
642        tcp_sent(conn->pcb.tcp, sent_tcp);
643        tcp_poll(conn->pcb.tcp, poll_tcp, 4);
644        tcp_err(conn->pcb.tcp, err_tcp);
645        tcp_arg(conn->pcb.tcp, conn);
646    }
647    /* If closing didn't succeed, we get called again either
648       from poll_tcp or from sent_tcp */
649}
650#endif                          /* LWIP_TCP */
651
652/**
653 * Delete the pcb inside a netconn.
654 * Called from netconn_delete.
655 *
656 * @param msg the api_msg_msg pointing to the connection
657 */
658void do_delconn(struct api_msg_msg *msg)
659{
660    if (msg->conn->pcb.tcp != NULL) {
661        switch (NETCONNTYPE_GROUP(msg->conn->type)) {
662#if LWIP_RAW
663            case NETCONN_RAW:
664                raw_remove(msg->conn->pcb.raw);
665                break;
666#endif                          /* LWIP_RAW */
667#if LWIP_UDP
668            case NETCONN_UDP:
669                msg->conn->pcb.udp->recv_arg = NULL;
670                udp_remove(msg->conn->pcb.udp);
671                break;
672#endif                          /* LWIP_UDP */
673#if LWIP_TCP
674            case NETCONN_TCP:
675                msg->conn->state = NETCONN_CLOSE;
676                do_close_internal(msg->conn);
677                /* API_EVENT is called inside do_close_internal, before releasing
678                   the application thread, so we can return at this point! */
679                return;
680#endif                          /* LWIP_TCP */
681            default:
682                break;
683        }
684    }
685    /* tcp netconns don't come here! */
686
687    /* Trigger select() in socket layer. This send should something else so the
688       errorfd is set, not the read and write fd! */
689    API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0);
690    API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0);
691
692    if (msg->conn->op_completed != SYS_SEM_NULL) {
693        sys_sem_signal(msg->conn->op_completed);
694    }
695}
696
697/**
698 * Bind a pcb contained in a netconn
699 * Called from netconn_bind.
700 *
701 * @param msg the api_msg_msg pointing to the connection and containing
702 *            the IP address and port to bind to
703 */
704void do_bind(struct api_msg_msg *msg)
705{
706    if (!ERR_IS_FATAL(msg->conn->err)) {
707        if (msg->conn->pcb.tcp != NULL) {
708            switch (NETCONNTYPE_GROUP(msg->conn->type)) {
709#if LWIP_RAW
710                case NETCONN_RAW:
711                    msg->conn->err =
712                      raw_bind(msg->conn->pcb.raw, msg->msg.bc.ipaddr);
713                    break;
714#endif                          /* LWIP_RAW */
715#if LWIP_UDP
716                case NETCONN_UDP:
717                    msg->conn->err =
718                      udp_bind(msg->conn->pcb.udp, msg->msg.bc.ipaddr,
719                               msg->msg.bc.port);
720                    break;
721#endif                          /* LWIP_UDP */
722#if LWIP_TCP
723                case NETCONN_TCP:
724                    msg->conn->err =
725                      tcp_bind(msg->conn->pcb.tcp, msg->msg.bc.ipaddr,
726                               msg->msg.bc.port);
727                    break;
728#endif                          /* LWIP_TCP */
729                default:
730                    break;
731            }
732        } else {
733            /* msg->conn->pcb is NULL */
734            msg->conn->err = ERR_VAL;
735        }
736    }
737    TCPIP_APIMSG_ACK(msg);
738}
739
740void do_redirect(struct api_msg_msg *msg)
741{
742    if (!ERR_IS_FATAL(msg->conn->err)) {
743        if (msg->conn->pcb.tcp != NULL) {
744            switch (NETCONNTYPE_GROUP(msg->conn->type)) {
745#if LWIP_TCP
746                case NETCONN_TCP:
747                    msg->conn->err = tcp_redirect(msg->conn->pcb.tcp,
748                                                  msg->msg.red.local_ip,
749                                                  msg->msg.red.local_port,
750                                                  msg->msg.red.remote_ip,
751                                                  msg->msg.red.remote_port);
752                    break;
753#endif                          /* LWIP_TCP */
754                default:
755                    break;
756            }
757        } else {
758            /* msg->conn->pcb is NULL */
759            msg->conn->err = ERR_VAL;
760        }
761    }
762    TCPIP_APIMSG_ACK(msg);
763}
764
765void do_pause(struct api_msg_msg *msg)
766{
767    if (!ERR_IS_FATAL(msg->conn->err)) {
768        if (msg->conn->pcb.tcp != NULL) {
769            switch (NETCONNTYPE_GROUP(msg->conn->type)) {
770#if LWIP_TCP
771                case NETCONN_TCP:
772                    msg->conn->err = tcp_pause(msg->conn->pcb.tcp,
773                                               msg->msg.red.local_ip,
774                                               msg->msg.red.local_port,
775                                               msg->msg.red.remote_ip,
776                                               msg->msg.red.remote_port);
777                    break;
778#endif                          /* LWIP_TCP */
779                default:
780                    break;
781            }
782        } else {
783            /* msg->conn->pcb is NULL */
784            msg->conn->err = ERR_VAL;
785        }
786    }
787    TCPIP_APIMSG_ACK(msg);
788}
789
790
791#if LWIP_TCP
792/**
793 * TCP callback function if a connection (opened by tcp_connect/do_connect) has
794 * been established (or reset by the remote host).
795 *
796 * @see tcp.h (struct tcp_pcb.connected) for parameters and return values
797 */
798static err_t do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
799{
800    struct netconn *conn;
801
802    LWIP_UNUSED_ARG(pcb);
803
804    conn = arg;
805
806    if (conn == NULL) {
807        return ERR_VAL;
808    }
809
810    conn->err = err;
811    if ((conn->type == NETCONN_TCP) && (err == ERR_OK)) {
812        setup_tcp(conn);
813    }
814    conn->state = NETCONN_NONE;
815    sys_sem_signal(conn->op_completed);
816    return ERR_OK;
817}
818#endif                          /* LWIP_TCP */
819
820/**
821 * Connect a pcb contained inside a netconn
822 * Called from netconn_connect.
823 *
824 * @param msg the api_msg_msg pointing to the connection and containing
825 *            the IP address and port to connect to
826 */
827void do_connect(struct api_msg_msg *msg)
828{
829    if (msg->conn->pcb.tcp == NULL) {
830        sys_sem_signal(msg->conn->op_completed);
831        return;
832    }
833
834    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
835#if LWIP_RAW
836        case NETCONN_RAW:
837            msg->conn->err =
838              raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr);
839            sys_sem_signal(msg->conn->op_completed);
840            break;
841#endif                          /* LWIP_RAW */
842#if LWIP_UDP
843        case NETCONN_UDP:
844            msg->conn->err =
845              udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr,
846                          msg->msg.bc.port);
847            sys_sem_signal(msg->conn->op_completed);
848            break;
849#endif                          /* LWIP_UDP */
850#if LWIP_TCP
851        case NETCONN_TCP:
852            msg->conn->state = NETCONN_CONNECT;
853            setup_tcp(msg->conn);
854            msg->conn->err =
855              tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr,
856                          msg->msg.bc.port, do_connected);
857            /* sys_sem_signal() is called from do_connected (or err_tcp()),
858             * when the connection is established! */
859            break;
860#endif                          /* LWIP_TCP */
861        default:
862            break;
863    }
864}
865
866/**
867 * Connect a pcb contained inside a netconn
868 * Only used for UDP netconns.
869 * Called from netconn_disconnect.
870 *
871 * @param msg the api_msg_msg pointing to the connection to disconnect
872 */
873void do_disconnect(struct api_msg_msg *msg)
874{
875#if LWIP_UDP
876    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
877        udp_disconnect(msg->conn->pcb.udp);
878    }
879#endif                          /* LWIP_UDP */
880    TCPIP_APIMSG_ACK(msg);
881}
882
883/**
884 * Set a TCP pcb contained in a netconn into listen mode
885 * Called from netconn_listen.
886 *
887 * @param msg the api_msg_msg pointing to the connection
888 */
889void do_listen(struct api_msg_msg *msg)
890{
891#if LWIP_TCP
892    if (!ERR_IS_FATAL(msg->conn->err)) {
893        if (msg->conn->pcb.tcp != NULL) {
894            if (msg->conn->type == NETCONN_TCP) {
895                if (msg->conn->pcb.tcp->state == CLOSED) {
896#if TCP_LISTEN_BACKLOG
897                    struct tcp_pcb *lpcb =
898                      tcp_listen_with_backlog(msg->conn->pcb.tcp,
899                                              msg->msg.lb.backlog);
900#else                           /* TCP_LISTEN_BACKLOG */
901                    struct tcp_pcb *lpcb = tcp_listen(msg->conn->pcb.tcp);
902#endif                          /* TCP_LISTEN_BACKLOG */
903                    if (lpcb == NULL) {
904                        msg->conn->err = ERR_MEM;
905                    } else {
906                        /* delete the recvmbox and allocate the acceptmbox */
907                        if (msg->conn->recvmbox != SYS_MBOX_NULL) {
908              /** @todo: should we drain the recvmbox here? */
909                            sys_mbox_free(msg->conn->recvmbox);
910                            msg->conn->recvmbox = SYS_MBOX_NULL;
911                        }
912                        if (msg->conn->acceptmbox == SYS_MBOX_NULL) {
913                            if ((msg->conn->acceptmbox =
914                                 sys_mbox_new(DEFAULT_ACCEPTMBOX_SIZE)) ==
915                                SYS_MBOX_NULL) {
916                                msg->conn->err = ERR_MEM;
917                            }
918                        }
919                        if (msg->conn->err == ERR_OK) {
920                            msg->conn->state = NETCONN_LISTEN;
921                            msg->conn->pcb.tcp = lpcb;
922                            tcp_arg(msg->conn->pcb.tcp, msg->conn);
923                            tcp_accept(msg->conn->pcb.tcp, accept_function);
924                        }
925                    }
926                } else {
927                    msg->conn->err = ERR_CONN;
928                }
929            }
930        }
931    }
932#endif                          /* LWIP_TCP */
933    TCPIP_APIMSG_ACK(msg);
934}
935
936/**
937 * Send some data on a RAW or UDP pcb contained in a netconn
938 * Called from netconn_send
939 *
940 * @param msg the api_msg_msg pointing to the connection
941 */
942void do_send(struct api_msg_msg *msg)
943{
944    if (!ERR_IS_FATAL(msg->conn->err)) {
945        if (msg->conn->pcb.tcp != NULL) {
946            switch (NETCONNTYPE_GROUP(msg->conn->type)) {
947#if LWIP_RAW
948                case NETCONN_RAW:
949                    if (msg->msg.b->addr == NULL) {
950                        msg->conn->err =
951                          raw_send(msg->conn->pcb.raw, msg->msg.b->p);
952                    } else {
953                        msg->conn->err =
954                          raw_sendto(msg->conn->pcb.raw, msg->msg.b->p,
955                                     msg->msg.b->addr);
956                    }
957                    break;
958#endif
959#if LWIP_UDP
960                case NETCONN_UDP:
961                    if (msg->msg.b->addr == NULL) {
962                        msg->conn->err =
963                          udp_send(msg->conn->pcb.udp, msg->msg.b->p);
964                    } else {
965                        msg->conn->err =
966                          udp_sendto(msg->conn->pcb.udp, msg->msg.b->p,
967                                     msg->msg.b->addr, msg->msg.b->port);
968                    }
969                    break;
970#endif                          /* LWIP_UDP */
971                default:
972                    break;
973            }
974        }
975    }
976    TCPIP_APIMSG_ACK(msg);
977}
978
979/**
980 * Indicate data has been received from a TCP pcb contained in a netconn
981 * Called from netconn_recv
982 *
983 * @param msg the api_msg_msg pointing to the connection
984 */
985void do_recv(struct api_msg_msg *msg)
986{
987#if LWIP_TCP
988    if (!ERR_IS_FATAL(msg->conn->err)) {
989        if (msg->conn->pcb.tcp != NULL) {
990            if (msg->conn->type == NETCONN_TCP) {
991#if TCP_LISTEN_BACKLOG
992                if (msg->conn->pcb.tcp->state == LISTEN) {
993                    tcp_accepted(msg->conn->pcb.tcp);
994                } else
995#endif                          /* TCP_LISTEN_BACKLOG */
996                {
997                    tcp_recved(msg->conn->pcb.tcp, msg->msg.r.len);
998                }
999            }
1000        }
1001    }
1002#endif                          /* LWIP_TCP */
1003    TCPIP_APIMSG_ACK(msg);
1004}
1005
1006#if LWIP_TCP
1007// antoinek: Might need to reenable this when we enable multi threaded lwip
1008// again. See idc_barrelfish.c and two occurrances below:
1009//extern bool lwip_in_packet_received;
1010
1011/**
1012 * See if more data needs to be written from a previous call to netconn_write.
1013 * Called initially from do_write. If the first call can't send all data
1014 * (because of low memory or empty send-buffer), this function is called again
1015 * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the
1016 * blocking application thread (waiting in netconn_write) is released.
1017 *
1018 * @param conn netconn (that is currently in state NETCONN_WRITE) to process
1019 * @return ERR_OK
1020 *         ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
1021 */
1022static err_t do_writemore(struct netconn *conn)
1023{
1024    err_t err;
1025    void *dataptr;
1026    u16_t len, available;
1027    u8_t write_finished = 0;
1028    size_t diff;
1029
1030    LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
1031
1032    dataptr = (u8_t *) conn->write_msg->msg.w.dataptr + conn->write_offset;
1033    diff = conn->write_msg->msg.w.len - conn->write_offset;
1034    if (diff > 0xffffUL) {      /* max_u16_t */
1035        len = 0xffff;
1036#if LWIP_TCPIP_CORE_LOCKING
1037        conn->write_delayed = 1;
1038#endif
1039    } else {
1040        len = (u16_t) diff;
1041    }
1042    available = tcp_sndbuf(conn->pcb.tcp);
1043    if (available < len) {
1044        /* don't try to write more than sendbuf */
1045        len = available;
1046#if LWIP_TCPIP_CORE_LOCKING
1047        conn->write_delayed = 1;
1048#endif
1049    }
1050
1051    err =
1052      tcp_write(conn->pcb.tcp, dataptr, len, conn->write_msg->msg.w.apiflags);
1053    LWIP_ASSERT("do_writemore: invalid length!",
1054                ((conn->write_offset + len) <= conn->write_msg->msg.w.len));
1055    if (err == ERR_OK) {
1056        conn->write_offset += len;
1057        if (conn->write_offset == conn->write_msg->msg.w.len) {
1058            /* everything was written */
1059            write_finished = 1;
1060            conn->write_msg = NULL;
1061            conn->write_offset = 0;
1062            /* API_EVENT might call tcp_tmr, so reset conn->state now */
1063            conn->state = NETCONN_NONE;
1064        }
1065        err = tcp_output_nagle(conn->pcb.tcp);
1066        conn->err = err;
1067        if ((err == ERR_OK) && (tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT)) {
1068            API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
1069        }
1070    } else if (err == ERR_MEM) {
1071        /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called
1072           we do NOT return to the application thread, since ERR_MEM is
1073           only a temporary error! */
1074
1075        /* tcp_enqueue returned ERR_MEM, try tcp_output anyway */
1076        err = tcp_output(conn->pcb.tcp);
1077
1078#if LWIP_TCPIP_CORE_LOCKING
1079        conn->write_delayed = 1;
1080#endif
1081    } else {
1082        /* On errors != ERR_MEM, we don't try writing any more but return
1083           the error to the application thread. */
1084        conn->err = err;
1085        write_finished = 1;
1086    }
1087
1088    if (write_finished) {
1089        /* everything was written: set back connection state
1090           and back to application task */
1091        conn->state = NETCONN_NONE;
1092#if LWIP_TCPIP_CORE_LOCKING
1093        if (conn->write_delayed != 0)
1094#endif
1095        {
1096            sys_sem_signal(conn->op_completed);
1097            /*if (lwip_in_packet_received) {
1098                lwip_mutex_lock();
1099            }*/
1100        }
1101    } else {
1102        /*if (!lwip_in_packet_received) {
1103            lwip_mutex_unlock();
1104        }*/
1105    }
1106
1107#if LWIP_TCPIP_CORE_LOCKING
1108    else
1109    return ERR_MEM;
1110#endif
1111    return ERR_OK;
1112}
1113#endif                          /* LWIP_TCP */
1114
1115/**
1116 * Send some data on a TCP pcb contained in a netconn
1117 * Called from netconn_write
1118 *
1119 * @param msg the api_msg_msg pointing to the connection
1120 */
1121void do_write(struct api_msg_msg *msg)
1122{
1123    if (!ERR_IS_FATAL(msg->conn->err)) {
1124        if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) {
1125#if LWIP_TCP
1126            msg->conn->state = NETCONN_WRITE;
1127            /* set all the variables used by do_writemore */
1128            LWIP_ASSERT("already writing", msg->conn->write_msg == NULL &&
1129                        msg->conn->write_offset == 0);
1130            msg->conn->write_msg = msg;
1131            msg->conn->write_offset = 0;
1132#if LWIP_TCPIP_CORE_LOCKING
1133            msg->conn->write_delayed = 0;
1134            if (do_writemore(msg->conn) != ERR_OK) {
1135                LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
1136                UNLOCK_TCPIP_CORE();
1137                sys_arch_sem_wait(msg->conn->op_completed, 0);
1138                LOCK_TCPIP_CORE();
1139                LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
1140            }
1141#else
1142            do_writemore(msg->conn);
1143#endif
1144            /* for both cases: if do_writemore was called, don't ACK the APIMSG! */
1145            return;
1146#endif                          /* LWIP_TCP */
1147#if (LWIP_UDP || LWIP_RAW)
1148        } else {
1149            msg->conn->err = ERR_VAL;
1150#endif                          /* (LWIP_UDP || LWIP_RAW) */
1151        }
1152    }
1153    TCPIP_APIMSG_ACK(msg);
1154}
1155
1156/**
1157 * Return a connection's local or remote address
1158 * Called from netconn_getaddr
1159 *
1160 * @param msg the api_msg_msg pointing to the connection
1161 */
1162void do_getaddr(struct api_msg_msg *msg)
1163{
1164    if (msg->conn->pcb.ip != NULL) {
1165        *(msg->msg.ad.ipaddr) =
1166          (msg->msg.ad.local ? msg->conn->pcb.ip->local_ip : msg->conn->pcb.ip->
1167           remote_ip);
1168
1169        switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1170#if LWIP_RAW
1171            case NETCONN_RAW:
1172                if (msg->msg.ad.local) {
1173                    *(msg->msg.ad.port) = msg->conn->pcb.raw->protocol;
1174                } else {
1175                    /* return an error as connecting is only a helper for upper layers */
1176                    msg->conn->err = ERR_CONN;
1177                }
1178                break;
1179#endif                          /* LWIP_RAW */
1180#if LWIP_UDP
1181            case NETCONN_UDP:
1182                if (msg->msg.ad.local) {
1183                    *(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
1184                } else {
1185                    if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) {
1186                        msg->conn->err = ERR_CONN;
1187                    } else {
1188                        *(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
1189                    }
1190                }
1191                break;
1192#endif                          /* LWIP_UDP */
1193#if LWIP_TCP
1194            case NETCONN_TCP:
1195                *(msg->msg.ad.port) =
1196                  (msg->msg.ad.local ? msg->conn->pcb.tcp->local_port : msg->
1197                   conn->pcb.tcp->remote_port);
1198                break;
1199#endif                          /* LWIP_TCP */
1200        }
1201    } else {
1202        msg->conn->err = ERR_CONN;
1203    }
1204    TCPIP_APIMSG_ACK(msg);
1205}
1206
1207/**
1208 * Close a TCP pcb contained in a netconn
1209 * Called from netconn_close
1210 *
1211 * @param msg the api_msg_msg pointing to the connection
1212 */
1213void do_close(struct api_msg_msg *msg)
1214{
1215#if LWIP_TCP
1216    if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) {
1217        msg->conn->state = NETCONN_CLOSE;
1218        do_close_internal(msg->conn);
1219        /* for tcp netconns, do_close_internal ACKs the message */
1220    } else
1221#endif                          /* LWIP_TCP */
1222    {
1223        msg->conn->err = ERR_VAL;
1224        TCPIP_APIMSG_ACK(msg);
1225    }
1226}
1227
1228#if LWIP_IGMP
1229/**
1230 * Join multicast groups for UDP netconns.
1231 * Called from netconn_join_leave_group
1232 *
1233 * @param msg the api_msg_msg pointing to the connection
1234 */
1235void do_join_leave_group(struct api_msg_msg *msg)
1236{
1237    if (!ERR_IS_FATAL(msg->conn->err)) {
1238        if (msg->conn->pcb.tcp != NULL) {
1239            if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
1240#if LWIP_UDP
1241                if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
1242                    msg->conn->err =
1243                      igmp_joingroup(msg->msg.jl.interface,
1244                                     msg->msg.jl.multiaddr);
1245                } else {
1246                    msg->conn->err =
1247                      igmp_leavegroup(msg->msg.jl.interface,
1248                                      msg->msg.jl.multiaddr);
1249                }
1250#endif                          /* LWIP_UDP */
1251#if (LWIP_TCP || LWIP_RAW)
1252            } else {
1253                msg->conn->err = ERR_VAL;
1254#endif                          /* (LWIP_TCP || LWIP_RAW) */
1255            }
1256        }
1257    }
1258    TCPIP_APIMSG_ACK(msg);
1259}
1260#endif                          /* LWIP_IGMP */
1261
1262#if LWIP_DNS
1263/**
1264 * Callback function that is called when DNS name is resolved
1265 * (or on timeout). A waiting application thread is waked up by
1266 * signaling the semaphore.
1267 */
1268static void do_dns_found(const char *name, struct ip_addr *ipaddr, void *arg)
1269{
1270    struct dns_api_msg *msg = (struct dns_api_msg *) arg;
1271
1272    LWIP_ASSERT("DNS response for wrong host name",
1273                strcmp(msg->name, name) == 0);
1274
1275    if (ipaddr == NULL) {
1276        /* timeout or memory error */
1277        *msg->err = ERR_VAL;
1278    } else {
1279        /* address was resolved */
1280        *msg->err = ERR_OK;
1281        *msg->addr = *ipaddr;
1282    }
1283    /* wake up the application task waiting in netconn_gethostbyname */
1284    sys_sem_signal(msg->sem);
1285}
1286
1287/**
1288 * Execute a DNS query
1289 * Called from netconn_gethostbyname
1290 *
1291 * @param arg the dns_api_msg pointing to the query
1292 */
1293void do_gethostbyname(void *arg)
1294{
1295    struct dns_api_msg *msg = (struct dns_api_msg *) arg;
1296
1297    *msg->err = dns_gethostbyname(msg->name, msg->addr, do_dns_found, msg);
1298    if (*msg->err != ERR_INPROGRESS) {
1299        /* on error or immediate success, wake up the application
1300         * task waiting in netconn_gethostbyname */
1301        sys_sem_signal(msg->sem);
1302    }
1303}
1304#endif                          /* LWIP_DNS */
1305
1306#endif                          /* LWIP_NETCONN */
1307