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/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 */ 67extern const struct altcp_functions altcp_tcp_functions; 68 69static void altcp_tcp_setup(struct altcp_pcb *conn, struct tcp_pcb *tpcb); 70 71/* callback functions for TCP */ 72static err_t 73altcp_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 88static err_t 89altcp_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 101static err_t 102altcp_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 118static err_t 119altcp_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 131static err_t 132altcp_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 144static void 145altcp_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 159static void 160altcp_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 169static void 170altcp_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 180static void 181altcp_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 188struct altcp_pcb * 189altcp_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*/ 211struct altcp_pcb * 212altcp_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 218struct altcp_pcb * 219altcp_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 */ 233static void 234altcp_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 243static void 244altcp_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 253static err_t 254altcp_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 265static err_t 266altcp_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 278static struct altcp_pcb * 279altcp_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 297static void 298altcp_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 309static err_t 310altcp_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 336static err_t 337altcp_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 348static err_t 349altcp_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 360static err_t 361altcp_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 372static u16_t 373altcp_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 384static u16_t 385altcp_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 396static u16_t 397altcp_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 408static void 409altcp_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 418static void 419altcp_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 428static int 429altcp_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 439static void 440altcp_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 449static void 450altcp_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 457static err_t 458altcp_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 468static ip_addr_t * 469altcp_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 485static u16_t 486altcp_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 503static enum tcp_state 504altcp_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 516const 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