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