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