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