xref: /nrf52832-nimble/rt-thread/components/net/lwip-2.1.0/src/core/altcp_tcp.c (revision 104654410c56c573564690304ae786df310c91fc)
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 <[email protected]>
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/mem.h"
53 
54 #include <string.h>
55 
56 #define ALTCP_TCP_ASSERT_CONN(conn) do { \
57   LWIP_ASSERT("conn->inner_conn == NULL", (conn)->inner_conn == NULL); \
58   LWIP_UNUSED_ARG(conn); /* for LWIP_NOASSERT */ } while(0)
59 #define ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb) do { \
60   LWIP_ASSERT("pcb mismatch", (conn)->state == tpcb); \
61   LWIP_UNUSED_ARG(tpcb); /* for LWIP_NOASSERT */ \
62   ALTCP_TCP_ASSERT_CONN(conn); } while(0)
63 
64 
65 /* Variable prototype, the actual declaration is at the end of this file
66    since it contains pointers to static functions declared here */
67 extern const struct altcp_functions altcp_tcp_functions;
68 
69 static void altcp_tcp_setup(struct altcp_pcb *conn, struct tcp_pcb *tpcb);
70 
71 /* callback functions for TCP */
72 static err_t
altcp_tcp_accept(void * arg,struct tcp_pcb * new_tpcb,err_t err)73 altcp_tcp_accept(void *arg, struct tcp_pcb *new_tpcb, err_t err)
74 {
75   struct altcp_pcb *listen_conn = (struct altcp_pcb *)arg;
76   if (listen_conn && listen_conn->accept) {
77     /* create a new altcp_conn to pass to the next 'accept' callback */
78     struct altcp_pcb *new_conn = altcp_alloc();
79     if (new_conn == NULL) {
80       return ERR_MEM;
81     }
82     altcp_tcp_setup(new_conn, new_tpcb);
83     return listen_conn->accept(listen_conn->arg, new_conn, err);
84   }
85   return ERR_ARG;
86 }
87 
88 static err_t
altcp_tcp_connected(void * arg,struct tcp_pcb * tpcb,err_t err)89 altcp_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
90 {
91   struct altcp_pcb *conn = (struct altcp_pcb *)arg;
92   if (conn) {
93     ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
94     if (conn->connected) {
95       return conn->connected(conn->arg, conn, err);
96     }
97   }
98   return ERR_OK;
99 }
100 
101 static err_t
altcp_tcp_recv(void * arg,struct tcp_pcb * tpcb,struct pbuf * p,err_t err)102 altcp_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
103 {
104   struct altcp_pcb *conn = (struct altcp_pcb *)arg;
105   if (conn) {
106     ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
107     if (conn->recv) {
108       return conn->recv(conn->arg, conn, p, err);
109     }
110   }
111   if (p != NULL) {
112     /* prevent memory leaks */
113     pbuf_free(p);
114   }
115   return ERR_OK;
116 }
117 
118 static err_t
altcp_tcp_sent(void * arg,struct tcp_pcb * tpcb,u16_t len)119 altcp_tcp_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
120 {
121   struct altcp_pcb *conn = (struct altcp_pcb *)arg;
122   if (conn) {
123     ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
124     if (conn->sent) {
125       return conn->sent(conn->arg, conn, len);
126     }
127   }
128   return ERR_OK;
129 }
130 
131 static err_t
altcp_tcp_poll(void * arg,struct tcp_pcb * tpcb)132 altcp_tcp_poll(void *arg, struct tcp_pcb *tpcb)
133 {
134   struct altcp_pcb *conn = (struct altcp_pcb *)arg;
135   if (conn) {
136     ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
137     if (conn->poll) {
138       return conn->poll(conn->arg, conn);
139     }
140   }
141   return ERR_OK;
142 }
143 
144 static void
altcp_tcp_err(void * arg,err_t err)145 altcp_tcp_err(void *arg, err_t err)
146 {
147   struct altcp_pcb *conn = (struct altcp_pcb *)arg;
148   if (conn) {
149     conn->state = NULL; /* already freed */
150     if (conn->err) {
151       conn->err(conn->arg, err);
152     }
153     altcp_free(conn);
154   }
155 }
156 
157 /* setup functions */
158 
159 static void
altcp_tcp_remove_callbacks(struct tcp_pcb * tpcb)160 altcp_tcp_remove_callbacks(struct tcp_pcb *tpcb)
161 {
162   tcp_arg(tpcb, NULL);
163   tcp_recv(tpcb, NULL);
164   tcp_sent(tpcb, NULL);
165   tcp_err(tpcb, NULL);
166   tcp_poll(tpcb, NULL, tpcb->pollinterval);
167 }
168 
169 static void
altcp_tcp_setup_callbacks(struct altcp_pcb * conn,struct tcp_pcb * tpcb)170 altcp_tcp_setup_callbacks(struct altcp_pcb *conn, struct tcp_pcb *tpcb)
171 {
172   tcp_arg(tpcb, conn);
173   tcp_recv(tpcb, altcp_tcp_recv);
174   tcp_sent(tpcb, altcp_tcp_sent);
175   tcp_err(tpcb, altcp_tcp_err);
176   /* tcp_poll is set when interval is set by application */
177   /* listen is set totally different :-) */
178 }
179 
180 static void
altcp_tcp_setup(struct altcp_pcb * conn,struct tcp_pcb * tpcb)181 altcp_tcp_setup(struct altcp_pcb *conn, struct tcp_pcb *tpcb)
182 {
183   altcp_tcp_setup_callbacks(conn, tpcb);
184   conn->state = tpcb;
185   conn->fns = &altcp_tcp_functions;
186 }
187 
188 struct altcp_pcb *
altcp_tcp_new_ip_type(u8_t ip_type)189 altcp_tcp_new_ip_type(u8_t ip_type)
190 {
191   /* Allocate the tcp pcb first to invoke the priority handling code
192      if we're out of pcbs */
193   struct tcp_pcb *tpcb = tcp_new_ip_type(ip_type);
194   if (tpcb != NULL) {
195     struct altcp_pcb *ret = altcp_alloc();
196     if (ret != NULL) {
197       altcp_tcp_setup(ret, tpcb);
198       return ret;
199     } else {
200       /* altcp_pcb allocation failed -> free the tcp_pcb too */
201       tcp_close(tpcb);
202     }
203   }
204   return NULL;
205 }
206 
207 /** altcp_tcp allocator function fitting to @ref altcp_allocator_t / @ref altcp_new.
208 *
209 * arg pointer is not used for TCP.
210 */
211 struct altcp_pcb *
altcp_tcp_alloc(void * arg,u8_t ip_type)212 altcp_tcp_alloc(void *arg, u8_t ip_type)
213 {
214   LWIP_UNUSED_ARG(arg);
215   return altcp_tcp_new_ip_type(ip_type);
216 }
217 
218 struct altcp_pcb *
altcp_tcp_wrap(struct tcp_pcb * tpcb)219 altcp_tcp_wrap(struct tcp_pcb *tpcb)
220 {
221   if (tpcb != NULL) {
222     struct altcp_pcb *ret = altcp_alloc();
223     if (ret != NULL) {
224       altcp_tcp_setup(ret, tpcb);
225       return ret;
226     }
227   }
228   return NULL;
229 }
230 
231 
232 /* "virtual" functions calling into tcp */
233 static void
altcp_tcp_set_poll(struct altcp_pcb * conn,u8_t interval)234 altcp_tcp_set_poll(struct altcp_pcb *conn, u8_t interval)
235 {
236   if (conn != NULL) {
237     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
238     ALTCP_TCP_ASSERT_CONN(conn);
239     tcp_poll(pcb, altcp_tcp_poll, interval);
240   }
241 }
242 
243 static void
altcp_tcp_recved(struct altcp_pcb * conn,u16_t len)244 altcp_tcp_recved(struct altcp_pcb *conn, u16_t len)
245 {
246   if (conn != NULL) {
247     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
248     ALTCP_TCP_ASSERT_CONN(conn);
249     tcp_recved(pcb, len);
250   }
251 }
252 
253 static err_t
altcp_tcp_bind(struct altcp_pcb * conn,const ip_addr_t * ipaddr,u16_t port)254 altcp_tcp_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port)
255 {
256   struct tcp_pcb *pcb;
257   if (conn == NULL) {
258     return ERR_VAL;
259   }
260   ALTCP_TCP_ASSERT_CONN(conn);
261   pcb = (struct tcp_pcb *)conn->state;
262   return tcp_bind(pcb, ipaddr, port);
263 }
264 
265 static err_t
altcp_tcp_connect(struct altcp_pcb * conn,const ip_addr_t * ipaddr,u16_t port,altcp_connected_fn connected)266 altcp_tcp_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
267 {
268   struct tcp_pcb *pcb;
269   if (conn == NULL) {
270     return ERR_VAL;
271   }
272   ALTCP_TCP_ASSERT_CONN(conn);
273   conn->connected = connected;
274   pcb = (struct tcp_pcb *)conn->state;
275   return tcp_connect(pcb, ipaddr, port, altcp_tcp_connected);
276 }
277 
278 static struct altcp_pcb *
altcp_tcp_listen(struct altcp_pcb * conn,u8_t backlog,err_t * err)279 altcp_tcp_listen(struct altcp_pcb *conn, u8_t backlog, err_t *err)
280 {
281   struct tcp_pcb *pcb;
282   struct tcp_pcb *lpcb;
283   if (conn == NULL) {
284     return NULL;
285   }
286   ALTCP_TCP_ASSERT_CONN(conn);
287   pcb = (struct tcp_pcb *)conn->state;
288   lpcb = tcp_listen_with_backlog_and_err(pcb, backlog, err);
289   if (lpcb != NULL) {
290     conn->state = lpcb;
291     tcp_accept(lpcb, altcp_tcp_accept);
292     return conn;
293   }
294   return NULL;
295 }
296 
297 static void
altcp_tcp_abort(struct altcp_pcb * conn)298 altcp_tcp_abort(struct altcp_pcb *conn)
299 {
300   if (conn != NULL) {
301     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
302     ALTCP_TCP_ASSERT_CONN(conn);
303     if (pcb) {
304       tcp_abort(pcb);
305     }
306   }
307 }
308 
309 static err_t
altcp_tcp_close(struct altcp_pcb * conn)310 altcp_tcp_close(struct altcp_pcb *conn)
311 {
312   struct tcp_pcb *pcb;
313   if (conn == NULL) {
314     return ERR_VAL;
315   }
316   ALTCP_TCP_ASSERT_CONN(conn);
317   pcb = (struct tcp_pcb *)conn->state;
318   if (pcb) {
319     err_t err;
320     tcp_poll_fn oldpoll = pcb->poll;
321     altcp_tcp_remove_callbacks(pcb);
322     err = tcp_close(pcb);
323     if (err != ERR_OK) {
324       /* not closed, set up all callbacks again */
325       altcp_tcp_setup_callbacks(conn, pcb);
326       /* poll callback is not included in the above */
327       tcp_poll(pcb, oldpoll, pcb->pollinterval);
328       return err;
329     }
330     conn->state = NULL; /* unsafe to reference pcb after tcp_close(). */
331   }
332   altcp_free(conn);
333   return ERR_OK;
334 }
335 
336 static err_t
altcp_tcp_shutdown(struct altcp_pcb * conn,int shut_rx,int shut_tx)337 altcp_tcp_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx)
338 {
339   struct tcp_pcb *pcb;
340   if (conn == NULL) {
341     return ERR_VAL;
342   }
343   ALTCP_TCP_ASSERT_CONN(conn);
344   pcb = (struct tcp_pcb *)conn->state;
345   return tcp_shutdown(pcb, shut_rx, shut_tx);
346 }
347 
348 static err_t
altcp_tcp_write(struct altcp_pcb * conn,const void * dataptr,u16_t len,u8_t apiflags)349 altcp_tcp_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
350 {
351   struct tcp_pcb *pcb;
352   if (conn == NULL) {
353     return ERR_VAL;
354   }
355   ALTCP_TCP_ASSERT_CONN(conn);
356   pcb = (struct tcp_pcb *)conn->state;
357   return tcp_write(pcb, dataptr, len, apiflags);
358 }
359 
360 static err_t
altcp_tcp_output(struct altcp_pcb * conn)361 altcp_tcp_output(struct altcp_pcb *conn)
362 {
363   struct tcp_pcb *pcb;
364   if (conn == NULL) {
365     return ERR_VAL;
366   }
367   ALTCP_TCP_ASSERT_CONN(conn);
368   pcb = (struct tcp_pcb *)conn->state;
369   return tcp_output(pcb);
370 }
371 
372 static u16_t
altcp_tcp_mss(struct altcp_pcb * conn)373 altcp_tcp_mss(struct altcp_pcb *conn)
374 {
375   struct tcp_pcb *pcb;
376   if (conn == NULL) {
377     return 0;
378   }
379   ALTCP_TCP_ASSERT_CONN(conn);
380   pcb = (struct tcp_pcb *)conn->state;
381   return tcp_mss(pcb);
382 }
383 
384 static u16_t
altcp_tcp_sndbuf(struct altcp_pcb * conn)385 altcp_tcp_sndbuf(struct altcp_pcb *conn)
386 {
387   struct tcp_pcb *pcb;
388   if (conn == NULL) {
389     return 0;
390   }
391   ALTCP_TCP_ASSERT_CONN(conn);
392   pcb = (struct tcp_pcb *)conn->state;
393   return tcp_sndbuf(pcb);
394 }
395 
396 static u16_t
altcp_tcp_sndqueuelen(struct altcp_pcb * conn)397 altcp_tcp_sndqueuelen(struct altcp_pcb *conn)
398 {
399   struct tcp_pcb *pcb;
400   if (conn == NULL) {
401     return 0;
402   }
403   ALTCP_TCP_ASSERT_CONN(conn);
404   pcb = (struct tcp_pcb *)conn->state;
405   return tcp_sndqueuelen(pcb);
406 }
407 
408 static void
altcp_tcp_nagle_disable(struct altcp_pcb * conn)409 altcp_tcp_nagle_disable(struct altcp_pcb *conn)
410 {
411   if (conn && conn->state) {
412     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
413     ALTCP_TCP_ASSERT_CONN(conn);
414     tcp_nagle_disable(pcb);
415   }
416 }
417 
418 static void
altcp_tcp_nagle_enable(struct altcp_pcb * conn)419 altcp_tcp_nagle_enable(struct altcp_pcb *conn)
420 {
421   if (conn && conn->state) {
422     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
423     ALTCP_TCP_ASSERT_CONN(conn);
424     tcp_nagle_enable(pcb);
425   }
426 }
427 
428 static int
altcp_tcp_nagle_disabled(struct altcp_pcb * conn)429 altcp_tcp_nagle_disabled(struct altcp_pcb *conn)
430 {
431   if (conn && conn->state) {
432     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
433     ALTCP_TCP_ASSERT_CONN(conn);
434     return tcp_nagle_disabled(pcb);
435   }
436   return 0;
437 }
438 
439 static void
altcp_tcp_setprio(struct altcp_pcb * conn,u8_t prio)440 altcp_tcp_setprio(struct altcp_pcb *conn, u8_t prio)
441 {
442   if (conn != NULL) {
443     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
444     ALTCP_TCP_ASSERT_CONN(conn);
445     tcp_setprio(pcb, prio);
446   }
447 }
448 
449 static void
altcp_tcp_dealloc(struct altcp_pcb * conn)450 altcp_tcp_dealloc(struct altcp_pcb *conn)
451 {
452   LWIP_UNUSED_ARG(conn);
453   ALTCP_TCP_ASSERT_CONN(conn);
454   /* no private state to clean up */
455 }
456 
457 static err_t
altcp_tcp_get_tcp_addrinfo(struct altcp_pcb * conn,int local,ip_addr_t * addr,u16_t * port)458 altcp_tcp_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port)
459 {
460   if (conn) {
461     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
462     ALTCP_TCP_ASSERT_CONN(conn);
463     return tcp_tcp_get_tcp_addrinfo(pcb, local, addr, port);
464   }
465   return ERR_VAL;
466 }
467 
468 static ip_addr_t *
altcp_tcp_get_ip(struct altcp_pcb * conn,int local)469 altcp_tcp_get_ip(struct altcp_pcb *conn, int local)
470 {
471   if (conn) {
472     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
473     ALTCP_TCP_ASSERT_CONN(conn);
474     if (pcb) {
475       if (local) {
476         return &pcb->local_ip;
477       } else {
478         return &pcb->remote_ip;
479       }
480     }
481   }
482   return NULL;
483 }
484 
485 static u16_t
altcp_tcp_get_port(struct altcp_pcb * conn,int local)486 altcp_tcp_get_port(struct altcp_pcb *conn, int local)
487 {
488   if (conn) {
489     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
490     ALTCP_TCP_ASSERT_CONN(conn);
491     if (pcb) {
492       if (local) {
493         return pcb->local_port;
494       } else {
495         return pcb->remote_port;
496       }
497     }
498   }
499   return 0;
500 }
501 
502 #ifdef LWIP_DEBUG
503 static enum tcp_state
altcp_tcp_dbg_get_tcp_state(struct altcp_pcb * conn)504 altcp_tcp_dbg_get_tcp_state(struct altcp_pcb *conn)
505 {
506   if (conn) {
507     struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
508     ALTCP_TCP_ASSERT_CONN(conn);
509     if (pcb) {
510       return pcb->state;
511     }
512   }
513   return CLOSED;
514 }
515 #endif
516 const struct altcp_functions altcp_tcp_functions = {
517   altcp_tcp_set_poll,
518   altcp_tcp_recved,
519   altcp_tcp_bind,
520   altcp_tcp_connect,
521   altcp_tcp_listen,
522   altcp_tcp_abort,
523   altcp_tcp_close,
524   altcp_tcp_shutdown,
525   altcp_tcp_write,
526   altcp_tcp_output,
527   altcp_tcp_mss,
528   altcp_tcp_sndbuf,
529   altcp_tcp_sndqueuelen,
530   altcp_tcp_nagle_disable,
531   altcp_tcp_nagle_enable,
532   altcp_tcp_nagle_disabled,
533   altcp_tcp_setprio,
534   altcp_tcp_dealloc,
535   altcp_tcp_get_tcp_addrinfo,
536   altcp_tcp_get_ip,
537   altcp_tcp_get_port
538 #ifdef LWIP_DEBUG
539   , altcp_tcp_dbg_get_tcp_state
540 #endif
541 };
542 
543 #endif /* LWIP_ALTCP */
544