xref: /btstack/3rd-party/lwip/core/src/core/altcp_tcp.c (revision 97dc5e692c7d94a280158af58036a0efee5b0e56)
1 /**
2  * @file
3  * Application layered TCP connection API (to be used from TCPIP thread)
4  *
5  * This interface mimics the tcp callback API to the application while preventing
6  * direct linking (much like virtual functions).
7  * This way, an application can make use of other application layer protocols
8  * on top of TCP without knowing the details (e.g. TLS, proxy connection).
9  *
10  * This file contains the base implementation calling into tcp.
11  */
12 
13 /*
14  * Copyright (c) 2017 Simon Goldschmidt
15  * All rights reserved.
16  *
17  * Redistribution and use in source and binary forms, with or without modification,
18  * are permitted provided that the following conditions are met:
19  *
20  * 1. Redistributions of source code must retain the above copyright notice,
21  *    this list of conditions and the following disclaimer.
22  * 2. Redistributions in binary form must reproduce the above copyright notice,
23  *    this list of conditions and the following disclaimer in the documentation
24  *    and/or other materials provided with the distribution.
25  * 3. The name of the author may not be used to endorse or promote products
26  *    derived from this software without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
29  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
30  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
31  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
32  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
33  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
36  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
37  * OF SUCH DAMAGE.
38  *
39  * This file is part of the lwIP TCP/IP stack.
40  *
41  * Author: Simon Goldschmidt <[email protected]>
42  *
43  */
44 
45 #include "lwip/opt.h"
46 
47 #if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
48 
49 #include "lwip/altcp.h"
50 #include "lwip/altcp_tcp.h"
51 #include "lwip/priv/altcp_priv.h"
52 #include "lwip/tcp.h"
53 #include "lwip/priv/tcp_priv.h"
54 #include "lwip/mem.h"
55 
56 #include <string.h>
57 
58 #define ALTCP_TCP_ASSERT_CONN(conn) do { \
59   LWIP_ASSERT("conn->inner_conn == NULL", (conn)->inner_conn == NULL); \
60   LWIP_UNUSED_ARG(conn); /* for LWIP_NOASSERT */ } while(0)
61 #define ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb) do { \
62   LWIP_ASSERT("pcb mismatch", (conn)->state == tpcb); \
63   LWIP_UNUSED_ARG(tpcb); /* for LWIP_NOASSERT */ \
64   ALTCP_TCP_ASSERT_CONN(conn); } while(0)
65 
66 
67 /* Variable prototype, the actual declaration is at the end of this file
68    since it contains pointers to static functions declared here */
69 extern const struct altcp_functions altcp_tcp_functions;
70 
71 static void altcp_tcp_setup(struct altcp_pcb *conn, struct tcp_pcb *tpcb);
72 
73 /* callback functions for TCP */
74 static err_t
altcp_tcp_accept(void * arg,struct tcp_pcb * new_tpcb,err_t err)75 altcp_tcp_accept(void *arg, struct tcp_pcb *new_tpcb, err_t err)
76 {
77   struct altcp_pcb *listen_conn = (struct altcp_pcb *)arg;
78   if (listen_conn && listen_conn->accept) {
79     /* create a new altcp_conn to pass to the next 'accept' callback */
80     struct altcp_pcb *new_conn = altcp_alloc();
81     if (new_conn == NULL) {
82       return ERR_MEM;
83     }
84     altcp_tcp_setup(new_conn, new_tpcb);
85     return listen_conn->accept(listen_conn->arg, new_conn, err);
86   }
87   return ERR_ARG;
88 }
89 
90 static err_t
altcp_tcp_connected(void * arg,struct tcp_pcb * tpcb,err_t err)91 altcp_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
92 {
93   struct altcp_pcb *conn = (struct altcp_pcb *)arg;
94   if (conn) {
95     ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
96     if (conn->connected) {
97       return conn->connected(conn->arg, conn, err);
98     }
99   }
100   return ERR_OK;
101 }
102 
103 static err_t
altcp_tcp_recv(void * arg,struct tcp_pcb * tpcb,struct pbuf * p,err_t err)104 altcp_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
105 {
106   struct altcp_pcb *conn = (struct altcp_pcb *)arg;
107   if (conn) {
108     ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
109     if (conn->recv) {
110       return conn->recv(conn->arg, conn, p, err);
111     }
112   }
113   if (p != NULL) {
114     /* prevent memory leaks */
115     pbuf_free(p);
116   }
117   return ERR_OK;
118 }
119 
120 static err_t
altcp_tcp_sent(void * arg,struct tcp_pcb * tpcb,u16_t len)121 altcp_tcp_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
122 {
123   struct altcp_pcb *conn = (struct altcp_pcb *)arg;
124   if (conn) {
125     ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
126     if (conn->sent) {
127       return conn->sent(conn->arg, conn, len);
128     }
129   }
130   return ERR_OK;
131 }
132 
133 static err_t
altcp_tcp_poll(void * arg,struct tcp_pcb * tpcb)134 altcp_tcp_poll(void *arg, struct tcp_pcb *tpcb)
135 {
136   struct altcp_pcb *conn = (struct altcp_pcb *)arg;
137   if (conn) {
138     ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
139     if (conn->poll) {
140       return conn->poll(conn->arg, conn);
141     }
142   }
143   return ERR_OK;
144 }
145 
146 static void
altcp_tcp_err(void * arg,err_t err)147 altcp_tcp_err(void *arg, err_t err)
148 {
149   struct altcp_pcb *conn = (struct altcp_pcb *)arg;
150   if (conn) {
151     conn->state = NULL; /* already freed */
152     if (conn->err) {
153       conn->err(conn->arg, err);
154     }
155     altcp_free(conn);
156   }
157 }
158 
159 /* setup functions */
160 
161 static void
altcp_tcp_remove_callbacks(struct tcp_pcb * tpcb)162 altcp_tcp_remove_callbacks(struct tcp_pcb *tpcb)
163 {
164   tcp_arg(tpcb, NULL);
165   tcp_recv(tpcb, NULL);
166   tcp_sent(tpcb, NULL);
167   tcp_err(tpcb, NULL);
168   tcp_poll(tpcb, NULL, tpcb->pollinterval);
169 }
170 
171 static void
altcp_tcp_setup_callbacks(struct altcp_pcb * conn,struct tcp_pcb * tpcb)172 altcp_tcp_setup_callbacks(struct altcp_pcb *conn, struct tcp_pcb *tpcb)
173 {
174   tcp_arg(tpcb, conn);
175   tcp_recv(tpcb, altcp_tcp_recv);
176   tcp_sent(tpcb, altcp_tcp_sent);
177   tcp_err(tpcb, altcp_tcp_err);
178   /* tcp_poll is set when interval is set by application */
179   /* listen is set totally different :-) */
180 }
181 
182 static void
altcp_tcp_setup(struct altcp_pcb * conn,struct tcp_pcb * tpcb)183 altcp_tcp_setup(struct altcp_pcb *conn, struct tcp_pcb *tpcb)
184 {
185   altcp_tcp_setup_callbacks(conn, tpcb);
186   conn->state = tpcb;
187   conn->fns = &altcp_tcp_functions;
188 }
189 
190 struct altcp_pcb *
altcp_tcp_new_ip_type(u8_t ip_type)191 altcp_tcp_new_ip_type(u8_t ip_type)
192 {
193   /* Allocate the tcp pcb first to invoke the priority handling code
194      if we're out of pcbs */
195   struct tcp_pcb *tpcb = tcp_new_ip_type(ip_type);
196   if (tpcb != NULL) {
197     struct altcp_pcb *ret = altcp_alloc();
198     if (ret != NULL) {
199       altcp_tcp_setup(ret, tpcb);
200       return ret;
201     } else {
202       /* altcp_pcb allocation failed -> free the tcp_pcb too */
203       tcp_close(tpcb);
204     }
205   }
206   return NULL;
207 }
208 
209 /** altcp_tcp allocator function fitting to @ref altcp_allocator_t / @ref altcp_new.
210 *
211 * arg pointer is not used for TCP.
212 */
213 struct altcp_pcb *
altcp_tcp_alloc(void * arg,u8_t ip_type)214 altcp_tcp_alloc(void *arg, u8_t ip_type)
215 {
216   LWIP_UNUSED_ARG(arg);
217   return altcp_tcp_new_ip_type(ip_type);
218 }
219 
220 struct altcp_pcb *
altcp_tcp_wrap(struct tcp_pcb * tpcb)221 altcp_tcp_wrap(struct tcp_pcb *tpcb)
222 {
223   if (tpcb != NULL) {
224     struct altcp_pcb *ret = altcp_alloc();
225     if (ret != NULL) {
226       altcp_tcp_setup(ret, tpcb);
227       return ret;
228     }
229   }
230   return NULL;
231 }
232 
233 
234 /* "virtual" functions calling into tcp */
235 static void
altcp_tcp_set_poll(struct altcp_pcb * conn,u8_t interval)236 altcp_tcp_set_poll(struct altcp_pcb *conn, u8_t interval)
237 {
238   if (conn != NULL) {
239     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
240     ALTCP_TCP_ASSERT_CONN(conn);
241     tcp_poll(pcb, altcp_tcp_poll, interval);
242   }
243 }
244 
245 static void
altcp_tcp_recved(struct altcp_pcb * conn,u16_t len)246 altcp_tcp_recved(struct altcp_pcb *conn, u16_t len)
247 {
248   if (conn != NULL) {
249     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
250     ALTCP_TCP_ASSERT_CONN(conn);
251     tcp_recved(pcb, len);
252   }
253 }
254 
255 static err_t
altcp_tcp_bind(struct altcp_pcb * conn,const ip_addr_t * ipaddr,u16_t port)256 altcp_tcp_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port)
257 {
258   struct tcp_pcb *pcb;
259   if (conn == NULL) {
260     return ERR_VAL;
261   }
262   ALTCP_TCP_ASSERT_CONN(conn);
263   pcb = (struct tcp_pcb *)conn->state;
264   return tcp_bind(pcb, ipaddr, port);
265 }
266 
267 static err_t
altcp_tcp_connect(struct altcp_pcb * conn,const ip_addr_t * ipaddr,u16_t port,altcp_connected_fn connected)268 altcp_tcp_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
269 {
270   struct tcp_pcb *pcb;
271   if (conn == NULL) {
272     return ERR_VAL;
273   }
274   ALTCP_TCP_ASSERT_CONN(conn);
275   conn->connected = connected;
276   pcb = (struct tcp_pcb *)conn->state;
277   return tcp_connect(pcb, ipaddr, port, altcp_tcp_connected);
278 }
279 
280 static struct altcp_pcb *
altcp_tcp_listen(struct altcp_pcb * conn,u8_t backlog,err_t * err)281 altcp_tcp_listen(struct altcp_pcb *conn, u8_t backlog, err_t *err)
282 {
283   struct tcp_pcb *pcb;
284   struct tcp_pcb *lpcb;
285   if (conn == NULL) {
286     return NULL;
287   }
288   ALTCP_TCP_ASSERT_CONN(conn);
289   pcb = (struct tcp_pcb *)conn->state;
290   lpcb = tcp_listen_with_backlog_and_err(pcb, backlog, err);
291   if (lpcb != NULL) {
292     conn->state = lpcb;
293     tcp_accept(lpcb, altcp_tcp_accept);
294     return conn;
295   }
296   return NULL;
297 }
298 
299 static void
altcp_tcp_abort(struct altcp_pcb * conn)300 altcp_tcp_abort(struct altcp_pcb *conn)
301 {
302   if (conn != NULL) {
303     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
304     ALTCP_TCP_ASSERT_CONN(conn);
305     if (pcb) {
306       tcp_abort(pcb);
307     }
308   }
309 }
310 
311 static err_t
altcp_tcp_close(struct altcp_pcb * conn)312 altcp_tcp_close(struct altcp_pcb *conn)
313 {
314   struct tcp_pcb *pcb;
315   if (conn == NULL) {
316     return ERR_VAL;
317   }
318   ALTCP_TCP_ASSERT_CONN(conn);
319   pcb = (struct tcp_pcb *)conn->state;
320   if (pcb) {
321     err_t err;
322     tcp_poll_fn oldpoll = pcb->poll;
323     altcp_tcp_remove_callbacks(pcb);
324     err = tcp_close(pcb);
325     if (err != ERR_OK) {
326       /* not closed, set up all callbacks again */
327       altcp_tcp_setup_callbacks(conn, pcb);
328       /* poll callback is not included in the above */
329       tcp_poll(pcb, oldpoll, pcb->pollinterval);
330       return err;
331     }
332     conn->state = NULL; /* unsafe to reference pcb after tcp_close(). */
333   }
334   altcp_free(conn);
335   return ERR_OK;
336 }
337 
338 static err_t
altcp_tcp_shutdown(struct altcp_pcb * conn,int shut_rx,int shut_tx)339 altcp_tcp_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx)
340 {
341   struct tcp_pcb *pcb;
342   if (conn == NULL) {
343     return ERR_VAL;
344   }
345   ALTCP_TCP_ASSERT_CONN(conn);
346   pcb = (struct tcp_pcb *)conn->state;
347   return tcp_shutdown(pcb, shut_rx, shut_tx);
348 }
349 
350 static err_t
altcp_tcp_write(struct altcp_pcb * conn,const void * dataptr,u16_t len,u8_t apiflags)351 altcp_tcp_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
352 {
353   struct tcp_pcb *pcb;
354   if (conn == NULL) {
355     return ERR_VAL;
356   }
357   ALTCP_TCP_ASSERT_CONN(conn);
358   pcb = (struct tcp_pcb *)conn->state;
359   return tcp_write(pcb, dataptr, len, apiflags);
360 }
361 
362 static err_t
altcp_tcp_output(struct altcp_pcb * conn)363 altcp_tcp_output(struct altcp_pcb *conn)
364 {
365   struct tcp_pcb *pcb;
366   if (conn == NULL) {
367     return ERR_VAL;
368   }
369   ALTCP_TCP_ASSERT_CONN(conn);
370   pcb = (struct tcp_pcb *)conn->state;
371   return tcp_output(pcb);
372 }
373 
374 static u16_t
altcp_tcp_mss(struct altcp_pcb * conn)375 altcp_tcp_mss(struct altcp_pcb *conn)
376 {
377   struct tcp_pcb *pcb;
378   if (conn == NULL) {
379     return 0;
380   }
381   ALTCP_TCP_ASSERT_CONN(conn);
382   pcb = (struct tcp_pcb *)conn->state;
383   return tcp_mss(pcb);
384 }
385 
386 static u16_t
altcp_tcp_sndbuf(struct altcp_pcb * conn)387 altcp_tcp_sndbuf(struct altcp_pcb *conn)
388 {
389   struct tcp_pcb *pcb;
390   if (conn == NULL) {
391     return 0;
392   }
393   ALTCP_TCP_ASSERT_CONN(conn);
394   pcb = (struct tcp_pcb *)conn->state;
395   return tcp_sndbuf(pcb);
396 }
397 
398 static u16_t
altcp_tcp_sndqueuelen(struct altcp_pcb * conn)399 altcp_tcp_sndqueuelen(struct altcp_pcb *conn)
400 {
401   struct tcp_pcb *pcb;
402   if (conn == NULL) {
403     return 0;
404   }
405   ALTCP_TCP_ASSERT_CONN(conn);
406   pcb = (struct tcp_pcb *)conn->state;
407   return tcp_sndqueuelen(pcb);
408 }
409 
410 static void
altcp_tcp_nagle_disable(struct altcp_pcb * conn)411 altcp_tcp_nagle_disable(struct altcp_pcb *conn)
412 {
413   if (conn && conn->state) {
414     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
415     ALTCP_TCP_ASSERT_CONN(conn);
416     tcp_nagle_disable(pcb);
417   }
418 }
419 
420 static void
altcp_tcp_nagle_enable(struct altcp_pcb * conn)421 altcp_tcp_nagle_enable(struct altcp_pcb *conn)
422 {
423   if (conn && conn->state) {
424     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
425     ALTCP_TCP_ASSERT_CONN(conn);
426     tcp_nagle_enable(pcb);
427   }
428 }
429 
430 static int
altcp_tcp_nagle_disabled(struct altcp_pcb * conn)431 altcp_tcp_nagle_disabled(struct altcp_pcb *conn)
432 {
433   if (conn && conn->state) {
434     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
435     ALTCP_TCP_ASSERT_CONN(conn);
436     return tcp_nagle_disabled(pcb);
437   }
438   return 0;
439 }
440 
441 static void
altcp_tcp_setprio(struct altcp_pcb * conn,u8_t prio)442 altcp_tcp_setprio(struct altcp_pcb *conn, u8_t prio)
443 {
444   if (conn != NULL) {
445     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
446     ALTCP_TCP_ASSERT_CONN(conn);
447     tcp_setprio(pcb, prio);
448   }
449 }
450 
451 #if LWIP_TCP_KEEPALIVE
452 static void
altcp_tcp_keepalive_disable(struct altcp_pcb * conn)453 altcp_tcp_keepalive_disable(struct altcp_pcb *conn)
454 {
455   if (conn && conn->state) {
456     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
457     ALTCP_TCP_ASSERT_CONN(conn);
458     ip_reset_option(pcb, SOF_KEEPALIVE);
459   }
460 }
461 
462 static void
altcp_tcp_keepalive_enable(struct altcp_pcb * conn,u32_t idle,u32_t intvl,u32_t cnt)463 altcp_tcp_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t cnt)
464 {
465   if (conn && conn->state) {
466     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
467     ALTCP_TCP_ASSERT_CONN(conn);
468     ip_set_option(pcb, SOF_KEEPALIVE);
469     pcb->keep_idle = idle ? idle : TCP_KEEPIDLE_DEFAULT;
470     pcb->keep_intvl = intvl ? intvl : TCP_KEEPINTVL_DEFAULT;
471     pcb->keep_cnt = cnt ? cnt : TCP_KEEPCNT_DEFAULT;
472   }
473 }
474 #endif
475 
476 static void
altcp_tcp_dealloc(struct altcp_pcb * conn)477 altcp_tcp_dealloc(struct altcp_pcb *conn)
478 {
479   LWIP_UNUSED_ARG(conn);
480   ALTCP_TCP_ASSERT_CONN(conn);
481   /* no private state to clean up */
482 }
483 
484 static err_t
altcp_tcp_get_tcp_addrinfo(struct altcp_pcb * conn,int local,ip_addr_t * addr,u16_t * port)485 altcp_tcp_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port)
486 {
487   if (conn) {
488     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
489     ALTCP_TCP_ASSERT_CONN(conn);
490     return tcp_tcp_get_tcp_addrinfo(pcb, local, addr, port);
491   }
492   return ERR_VAL;
493 }
494 
495 static ip_addr_t *
altcp_tcp_get_ip(struct altcp_pcb * conn,int local)496 altcp_tcp_get_ip(struct altcp_pcb *conn, int local)
497 {
498   if (conn) {
499     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
500     ALTCP_TCP_ASSERT_CONN(conn);
501     if (pcb) {
502       if (local) {
503         return &pcb->local_ip;
504       } else {
505         return &pcb->remote_ip;
506       }
507     }
508   }
509   return NULL;
510 }
511 
512 static u16_t
altcp_tcp_get_port(struct altcp_pcb * conn,int local)513 altcp_tcp_get_port(struct altcp_pcb *conn, int local)
514 {
515   if (conn) {
516     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
517     ALTCP_TCP_ASSERT_CONN(conn);
518     if (pcb) {
519       if (local) {
520         return pcb->local_port;
521       } else {
522         return pcb->remote_port;
523       }
524     }
525   }
526   return 0;
527 }
528 
529 #ifdef LWIP_DEBUG
530 static enum tcp_state
altcp_tcp_dbg_get_tcp_state(struct altcp_pcb * conn)531 altcp_tcp_dbg_get_tcp_state(struct altcp_pcb *conn)
532 {
533   if (conn) {
534     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
535     ALTCP_TCP_ASSERT_CONN(conn);
536     if (pcb) {
537       return pcb->state;
538     }
539   }
540   return CLOSED;
541 }
542 #endif
543 const struct altcp_functions altcp_tcp_functions = {
544   altcp_tcp_set_poll,
545   altcp_tcp_recved,
546   altcp_tcp_bind,
547   altcp_tcp_connect,
548   altcp_tcp_listen,
549   altcp_tcp_abort,
550   altcp_tcp_close,
551   altcp_tcp_shutdown,
552   altcp_tcp_write,
553   altcp_tcp_output,
554   altcp_tcp_mss,
555   altcp_tcp_sndbuf,
556   altcp_tcp_sndqueuelen,
557   altcp_tcp_nagle_disable,
558   altcp_tcp_nagle_enable,
559   altcp_tcp_nagle_disabled,
560   altcp_tcp_setprio,
561   altcp_tcp_dealloc,
562   altcp_tcp_get_tcp_addrinfo,
563   altcp_tcp_get_ip,
564   altcp_tcp_get_port
565 #if LWIP_TCP_KEEPALIVE
566   , altcp_tcp_keepalive_disable
567   , altcp_tcp_keepalive_enable
568 #endif
569 #ifdef LWIP_DEBUG
570   , altcp_tcp_dbg_get_tcp_state
571 #endif
572 };
573 
574 #endif /* LWIP_ALTCP */
575