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