Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


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