42#include "mbedtls/ctr_drbg.h"
43#include "mbedtls/pk.h"
44#include "mbedtls/ssl.h"
45#include "mbedtls/x509_crt.h"
48#define CWIST_LWIP_TLS_IN_MAX (MBEDTLS_SSL_IN_CONTENT_LEN + 2048)
53#if defined(LWIP_TCP) && (!LWIP_TCP || !LWIP_CALLBACK_API)
54#error "cwist lwIP adapter needs LWIP_TCP and LWIP_CALLBACK_API"
58#define CWIST_LWIP_POLL_TICKS 2
60static const char CWIST_LWIP_100_CONTINUE[] =
"HTTP/1.1 100 Continue\r\n\r\n";
121static const char *find_crlfcrlf(
const char *buf,
size_t len) {
122 for (
size_t i = 0;
i + 3 < len;
i++) {
123 if (buf[
i] ==
'\r' && buf[
i + 1] ==
'\n' && buf[
i + 2] ==
'\r' && buf[
i + 3] ==
'\n')
129static bool header_is(
const char *name,
size_t name_len,
const char *
want) {
135static void trim_ows(
const char **
v,
size_t *len) {
136 while (*len > 0 && (**
v ==
' ' || **
v ==
'\t')) (*v)++, (*len)--;
137 while (*len > 0 && ((*
v)[*len - 1] ==
' ' || (*
v)[*len - 1] ==
'\t')) (*len)--;
149 const char *
end = find_crlfcrlf(
c->rx,
c->rx_len);
166 while (
line && (
size_t)(
line + 1 -
c->rx) < head_len - 2) {
173 const char *
v =
colon + 1;
179 for (
size_t i = 0;
i <
vlen;
i++) {
186 }
else if (header_is(
line,
name_len,
"Transfer-Encoding")) {
215static void conn_unlink(cwist_lwip_conn *
c) {
217 for (cwist_lwip_conn **
pp = &srv->conns; *
pp;
pp = &(*pp)->next) {
226static void conn_free(cwist_lwip_conn *
c) {
243static bool rx_reserve(cwist_lwip_conn *
c,
size_t need,
size_t limit) {
245 size_t cap =
c->rx_cap ?
c->rx_cap : 512;
249 if (!
grown)
return false;
255static void conn_detach_pcb(cwist_lwip_conn *
c) {
265static void conn_abort(cwist_lwip_conn *
c) {
275static err_t conn_close(cwist_lwip_conn *
c) {
295static err_t conn_push(cwist_lwip_conn *
c) {
299 while (
c->tx &&
c->tx_queued <
c->tx_len) {
301 if (
room == 0)
break;
302 size_t n =
c->tx_len -
c->tx_queued;
304 if (
n > 0xFFFF)
n = 0xFFFF;
319static err_t conn_send(cwist_lwip_conn *
c,
char *buf,
size_t len,
bool close_after) {
329static err_t conn_send_error(cwist_lwip_conn *
c,
int status) {
346 return conn_send(
c, out, out_len,
true);
350static size_t response_head_len(
const char *buf,
size_t len) {
351 const char *
end = find_crlfcrlf(buf, len);
359static err_t conn_process(cwist_lwip_conn *
c) {
360 if (
c->tx || !
c->pcb)
return ERR_OK;
361 if (
c->rx_len == 0) {
362 if (
c->peer_closed)
return conn_close(
c);
371 return conn_send_error(
c, status);
373 if (
c->peer_closed)
return conn_close(
c);
375 c->continue_sent =
true;
379 return conn_send(
c, (
char *)CWIST_LWIP_100_CONTINUE,
380 sizeof(CWIST_LWIP_100_CONTINUE) - 1,
false);
383 if (
tcp_write(
c->pcb, CWIST_LWIP_100_CONTINUE,
sizeof(CWIST_LWIP_100_CONTINUE) - 1, 0) ==
385 c->ack_skip +=
sizeof(CWIST_LWIP_100_CONTINUE) - 1;
397 c->continue_sent =
false;
418 if (
head) out_len = response_head_len(out, out_len);
419 return conn_send(
c, out, out_len, !
keep);
426static int tls_bio_send(
void *ctx,
const unsigned char *buf,
size_t len) {
432 if (
n > 0xFFFF)
n = 0xFFFF;
440static int tls_bio_recv(
void *ctx,
unsigned char *buf,
size_t len) {
442 size_t avail =
c->tin_len -
c->tin_off;
447 if (
c->tin_off ==
c->tin_len)
c->tin_off =
c->tin_len = 0;
455 return conn_close(
c);
463 c->tx_static =
false;
464 c->tx_len =
c->tx_queued =
c->tx_acked = 0;
471 while (
c->tx &&
c->tx_queued <
c->tx_len) {
473 c->tx_len -
c->tx_queued);
491 if (!
c->handshake_done) {
499 c->handshake_done =
true;
501 if (
c->close_after_tx)
return ERR_OK;
503 size_t limit =
c->srv->cfg.max_header_bytes +
c->srv->cfg.max_body_bytes;
504 while (
c->rx_len <
limit + 1) {
518 c->peer_closed =
true;
525 return conn_process(
c);
539 if (
c->tin_off > 0) {
540 memmove(
c->tin,
c->tin +
c->tin_off,
c->tin_len -
c->tin_off);
541 c->tin_len -=
c->tin_off;
544 size_t need =
c->tin_len +
p->tot_len;
546 if (
need >
c->tin_cap) {
547 size_t cap =
c->tin_cap ?
c->tin_cap : 1024;
560 c->tin_len +=
p->tot_len;
567static int tls_entropy(
void *ctx,
unsigned char *out,
size_t len) {
589 if (
c->tx_acked <
c->tx_len)
return conn_push(
c);
593 c->tx_len =
c->tx_queued =
c->tx_acked = 0;
594 if (
c->close_after_tx)
return conn_close(
c);
595 return conn_process(
c);
608 c->peer_closed =
true;
609 return conn_process(
c);
616 if (
c->close_after_tx) {
624 size_t limit =
c->srv->cfg.max_header_bytes +
c->srv->cfg.max_body_bytes;
625 size_t need =
c->rx_len +
p->tot_len;
640 return conn_process(
c);
643static void on_err(
void *arg,
err_t err) {
657 if (
c->tx &&
c->tx_queued <
c->tx_len)
return conn_push(
c);
667 if (srv->nconns >= srv->cfg.max_conns) {
680 c->next = srv->conns;
730 if (!app)
return NULL;
732 if (!srv)
return NULL;
733 memset(srv, 0,
sizeof(*srv));
739 if (srv->cfg.max_conns == 0) srv->cfg.max_conns = 1;
740#ifndef CWIST_LWIP_TLS
741 if (srv->cfg.tls)
goto fail;
760 srv->listen_pcb =
lpcb;
776 if (srv->listen_pcb) {
780 srv->listen_pcb =
NULL;
782 while (srv->conns) conn_abort(srv->conns);
792 return srv ? srv->nconns : 0;
805 const unsigned char *key,
size_t key_len) {
807 static const unsigned char pers[] =
"cwist-lwip-tls";
808 if (!cert || !key)
return NULL;
810 if (!tls)
return NULL;
Memory allocation wrappers backed by libttak.
void cwist_free(void *ptr)
Free memory obtained via cwist_alloc/cwist_strdup/etc.
void * cwist_realloc(void *ptr, size_t new_size)
Resize an existing allocation (libttak tracked).
void * cwist_alloc(size_t size)
Allocate zeroed memory tracked by libttak.
@ CWIST_HTTP_INTERNAL_ERROR
@ CWIST_HTTP_NOT_IMPLEMENTED
void cwist_http_request_destroy(cwist_http_request *req)
Destroy an HTTP request object.
void cwist_http_response_destroy(cwist_http_response *res)
Destroy an HTTP response object.
cwist_http_response * cwist_http_response_create(void)
Create a new HTTP response object.
void cwist_lwip_tls_destroy(cwist_lwip_tls *tls)
Free a TLS context; close every server using it first.
void cwist_lwip_close(cwist_lwip_server *srv)
Stop listening, abort every open connection, and free srv.
cwist_lwip_server * cwist_lwip_listen(cwist_app *app, const cwist_lwip_config *cfg)
Bind a listening tcp_pcb on cfg->port and serve app on it.
cwist_lwip_tls * cwist_lwip_tls_create(const unsigned char *cert, size_t cert_len, const unsigned char *key, size_t key_len)
Create a TLS server context from a certificate chain and private key.
#define CWIST_LWIP_POLL_TICKS
struct cwist_lwip_conn cwist_lwip_conn
void cwist_lwip_config_init(cwist_lwip_config *cfg)
Fill cfg with the defaults documented on cwist_lwip_config.
size_t cwist_lwip_active_connections(const cwist_lwip_server *srv)
Number of connections the server currently holds open.
Serve a CWIST app over lwIP's raw TCP API (issue #447 Phase 3).
struct cwist_lwip_server cwist_lwip_server
Opaque listener handle returned by cwist_lwip_listen().
struct cwist_lwip_tls cwist_lwip_tls
Opaque TLS server context: certificate chain, key, DRBG and mbedTLS config.
HTTP Protocol Definitions and Helpers.
int cwist_http_response_stream_end(cwist_http_response *res)
Terminate a streaming response with the final zero-length chunk and flush.
void cwist_http_response_set_body_ptr(cwist_http_response *res, const void *ptr, size_t len)
Sets a direct pointer for the response body (Zero Copy). Use this when serving large files from memor...
@ CWIST_HTTP_PAYLOAD_TOO_LARGE
@ CWIST_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE
cwist_http_request * cwist_http_parse_request_len(const char *buf, size_t len)
Parse an HTTP/1.x request held in a memory buffer (length-based; the buffer may contain a binary body...
int cwist_http_response_serialize(cwist_http_response *res, char **out, size_t *out_len)
Serialize a response into a freshly allocated HTTP/1.x buffer (status line + headers + body)....
const char * cwist_http_status_reason(int status)
Return the standard reason phrase for an HTTP status code (e.g. 404 -> "Not Found")....
Hooks a bare-metal firmware supplies to CWIST (issue #447).
int cwist_port_entropy(void *buf, size_t len)
Fill a buffer with cryptographically secure random bytes.
Main Application Context.
cwist_http_method_t method
HTTP Response Object. Supports standard string body or Zero-Copy pointer body.
cwist_http_status_t status_code
bool stream_mode
cwist_http_response_stream_begin() ran.
bool stream_ended
cwist_http_response_stream_end() ran.
Listener settings; start from cwist_lwip_config_init().
struct cwist_lwip_tls * tls
void cwist_app_dispatch(cwist_app *app, cwist_http_request *req, cwist_http_response *res)
Dispatch a request internally through the app's router and middleware. Used by the test client and ad...