5#include <lwip/sockets.h>
11#include <pico/stdlib.h>
15#if PICO_TCP_ENABLE_TLS
16#include <lwip/altcp.h>
17#include <lwip/altcp_tls.h>
25 : sockfd(-1), connected(false), use_tls(false)
26#if PICO_TCP_ENABLE_TLS
28 tls_config(nullptr), server_tls_config(nullptr),
35 : sockfd(fd), connected(true), use_tls(false)
36#if PICO_TCP_ENABLE_TLS
38 tls_config(nullptr), server_tls_config(nullptr),
51 *
this = std::move(other);
59 #if PICO_TCP_ENABLE_TLS
60 tls_pcb = other.tls_pcb;
61 tls_config = other.tls_config;
62 server_tls_config = other.server_tls_config;
64 connected = other.connected;
65 use_tls = other.use_tls;
66 recv_buffer = other.recv_buffer;
69 #if PICO_TCP_ENABLE_TLS
70 other.tls_pcb =
nullptr;
71 other.tls_config =
nullptr;
72 other.server_tls_config =
nullptr;
74 other.recv_buffer =
nullptr;
82 socklen_t len =
sizeof(addr);
83 if (lwip_getpeername(
sockfd,
reinterpret_cast<sockaddr *
>(&addr), &len) == 0)
86 ip.addr = addr.sin_addr.s_addr;
87 return std::string(ipaddr_ntoa(&ip));
92#if PICO_TCP_ENABLE_TLS
111 printf(
"[Tcp] Failed to create TLS server config\n");
123 printf(
"[Tcp] DNS resolution failed for %s\n", host);
126#if PICO_TCP_ENABLE_TLS
135 struct sockaddr_in addr = {};
136 addr.sin_family = AF_INET;
137 addr.sin_port = lwip_htons(port);
138 addr.sin_addr.s_addr = ip.addr;
140 sockfd = lwip_socket(AF_INET, SOCK_STREAM, 0);
143 printf(
"[Tcp] Failed to create socket\n");
147 if (lwip_connect(
sockfd,
reinterpret_cast<struct sockaddr *
>(&addr),
sizeof(addr)) < 0)
149 printf(
"[Tcp] Failed to connect to server\n");
154 TRACE(
"[Tcp] Connected to server (plain)\n");
160#if PICO_TCP_ENABLE_TLS
166 printf(
"[Tcp] DNS resolution failed for %s\n", host);
172err_t Tcp::onConnected(
void *arg,
struct altcp_pcb *conn, err_t err)
174 TRACE(
"[Tcp] onConnected callback\n");
177 printf(
"[Tcp] Connection failed: %d\n", err);
182 auto *self =
static_cast<Tcp *
>(arg);
186 if (self->connectingTask)
188 TRACE(
"TLS Connection State in onConnect: %d\n", conn->state);
195void Tcp::onError(
void *arg, err_t err)
197 auto *self =
static_cast<Tcp *
>(arg);
198 printf(
"[Tcp] altcp error: %d\n", err);
200 self->connectResult = err;
203 if (self->connectingTask)
208 self->tls_pcb =
nullptr;
216 tls_config = altcp_tls_create_config_client(
217 reinterpret_cast<const uint8_t *
>(
root_ca_cert.c_str()),
222 printf(
"[Tcp] TLS config creation failed %d\n", tls_config);
225 TRACE(
"[Tcp] TLS config created\n");
227 tls_pcb = altcp_tls_new(tls_config, IPADDR_TYPE_ANY);
231 printf(
"[Tcp] Failed to create TLS connection\n");
237 auto *ssl_ctx =
static_cast<mbedtls_ssl_context *
>(altcp_tls_context(tls_pcb));
238 mbedtls_ssl_set_hostname(ssl_ctx,
hostname);
241 altcp_arg(tls_pcb,
this);
242 TRACE(
"[Tcp] Registering TLS recv callback\n");
247 altcp_err(tls_pcb, &Tcp::onError);
248 TRACE(
"[Tcp] TLS Connecting to %s:%d\n", ipaddr_ntoa(&ip), port);
249 err_t err = altcp_connect(tls_pcb, &ip, port, &Tcp::onConnected);
252 printf(
"[Tcp] altcp_connect failed: %d\n", err);
253 altcp_close(tls_pcb);
260 ulTaskNotifyTakeIndexed(
NotifyConnect, pdTRUE, pdMS_TO_TICKS(portMAX_DELAY));
261 TRACE(
"[Tcp] TLS handshake completed\n");
264 printf(
"[Tcp] TLS connection established\n");
270 altcp_close(tls_pcb);
281 TRACE(
"[Tcp] Sending %zu bytes\n", size);
282 #if PICO_TCP_ENABLE_TLS
288 printf(
"[Tcp] Invalid buffer, size, or connection\n");
293 size_t totalSent = 0;
297 while (totalSent < size)
299 size_t toSend = (size - totalSent > chunkSize) ? chunkSize : (size - totalSent);
300 #if PICO_TCP_ENABLE_TLS
304 if (tls_pcb->state ==
nullptr)
306 printf(
"[Tcp] TLS connection is not established\n");
310 err_t err = altcp_write(tls_pcb, buffer + totalSent, toSend, TCP_WRITE_FLAG_COPY);
313 printf(
"[Tcp] altcp_write failed: %d\n", err);
316 if (altcp_output(tls_pcb) != ERR_OK)
318 printf(
"[Tcp] altcp_output failed\n");
327 int ret = lwip_send(
sockfd, buffer + totalSent, toSend, 0);
330 warning(
"[Tcp] lwip_send failed: ", ret);
336 printf(
"[Tcp] No valid socket or TLS connection\n");
346 vTaskDelay(pdMS_TO_TICKS(20));
348 return static_cast<int>(size);
353 auto *self =
static_cast<Tcp *
>(arg);
360 if (self->recv_buffer)
362 pbuf_free(self->recv_buffer);
363 self->recv_buffer =
nullptr;
365 self->recv_offset = 0;
370 if (self->recv_buffer)
376 self->recv_buffer = p;
377 self->recv_offset = 0;
380 if (self->waiting_task)
382 xTaskNotifyGiveIndexed(self->waiting_task,
NotifyRecv);
383 self->waiting_task =
nullptr;
389int Tcp::recv(
char *buffer,
size_t size, uint32_t timeout_ms)
393 TRACE(
"Plain recv: %zu bytes\n", size);
394 int received = lwip_recv(
sockfd, buffer, size, 0);
397 TRACE(
"Plain recv error: %d, %s\n", errno, strerror(errno));
403#if PICO_TCP_ENABLE_TLS
404 if (!tls_pcb || !buffer || size == 0)
413 BaseType_t result = ulTaskNotifyTakeIndexed(
NotifyRecv, pdTRUE, pdMS_TO_TICKS(timeout_ms));
425 size_t to_copy = (size < available) ? size : available;
436 return static_cast<int>(to_copy);
438 printf(
"[Tcp] TLS not enabled in this build\n");
446 #if PICO_TCP_ENABLE_TLS
449 altcp_close(tls_pcb);
456 result = lwip_close(
sockfd);
473 auto *self =
static_cast<Tcp *
>(arg);
475 if (err != ERR_OK || !new_conn || !self)
482 if (self->waiting_task)
484 xTaskNotifyGiveIndexed(self->waiting_task,
NotifyAccept);
485 self->waiting_task =
nullptr;
495 #if PICO_TCP_ENABLE_TLS
498 ulTaskNotifyTakeIndexed(
NotifyAccept, pdTRUE, portMAX_DELAY);
512 printf(
"[Tcp] TLS not enabled in this build\n");
518 struct sockaddr_in client_addr{};
519 socklen_t addr_len =
sizeof(client_addr);
521 int client_fd = lwip_accept(
sockfd,
reinterpret_cast<struct sockaddr *
>(&client_addr), &addr_len);
522 TRACE(
"[Tcp] Accept returned new socket: %d\n", client_fd);
527 struct timeval recv_timeout = {.tv_sec = 0, .tv_usec = 100000};
528 lwip_setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &recv_timeout,
sizeof(recv_timeout));
531 struct linger so_linger = {.l_onoff = 1, .l_linger = 0};
532 lwip_setsockopt(client_fd, SOL_SOCKET, SO_LINGER, &so_linger,
sizeof(so_linger));
534 Tcp *client =
new Tcp(client_fd);
538 printf(
"[Tcp] Accept timeout, no client.\n");
544 #if PICO_TCP_ENABLE_TLS
554 sockfd = lwip_socket(AF_INET, SOCK_STREAM, 0);
557 printf(
"[Tcp] Failed to create socket\n");
562 int flags = fcntl(
sockfd, F_GETFL, 0);
563 fcntl(
sockfd, F_SETFL, flags | O_NONBLOCK);
565 struct sockaddr_in addr = {};
566 addr.sin_family = AF_INET;
567 addr.sin_port = lwip_htons(port);
568 addr.sin_addr.s_addr = PP_HTONL(INADDR_ANY);
570 if (lwip_bind(
sockfd,
reinterpret_cast<struct sockaddr *
>(&addr),
sizeof(addr)) < 0)
572 printf(
"[Tcp] Failed to bind socket\n");
578 if (lwip_listen(
sockfd, 10) < 0)
580 printf(
"[Tcp] Failed to listen on socket\n");
586 TRACE(
"[Tcp] Listening on port: %d, (socket: %d)\n", port,
sockfd);
592#if PICO_TCP_ENABLE_TLS
597 printf(
"[Tcp] TLS config not set for server\n");
604 printf(
"[Tcp] Failed to create TLS PCB\n");
608 struct sockaddr_in addr = {};
609 addr.sin_family = AF_INET;
610 addr.sin_port = lwip_htons(port);
611 addr.sin_addr.s_addr = PP_HTONL(INADDR_ANY);
613 err_t err = altcp_bind(tls_pcb,
reinterpret_cast<ip_addr_t *
>(&addr.sin_addr), port);
616 printf(
"[Tcp] altcp_bind failed: %d\n", err);
617 altcp_close(tls_pcb);
Macro-based debug trace system with optional SD file logging.
#define TRACE_INIT(MODULE_NAME)
Declare trace usage in a source file for a given module.
#define TRACE(...)
Default trace (INFO level).
General-purpose TCP socket abstraction with optional TLS support for both client and server use.
General-purpose TCP socket wrapper with optional TLS support via mbedTLS (altcp).
Tcp * accept()
Accept a new incoming connection (for server use).
bool connect(const char *host, int port, bool useTls=false)
Connect to a remote host.
static err_t tlsRecvCallback(void *arg, struct altcp_pcb *conn, struct pbuf *p, err_t err)
bool bindAndListenTls(int port)
char hostname[64]
Hostname for TLS connections.
TaskHandle_t waiting_task
Task handle for async operations.
void setRootCACertificate(const std::string &pem)
Set the Root CA certificate to be used for client TLS connections (PEM format).
std::string getPeerIp() const
static err_t acceptCallback(void *arg, struct altcp_pcb *new_conn, err_t err)
struct altcp_tls_config * server_tls_config
bool connectTls(const ip_addr_t &ip, int port)
struct pbuf * recv_buffer
Buffer for TLS receive.
bool connectPlain(const ip_addr_t &ip, int port)
int send(const char *buffer, size_t size)
Send data over the connection.
Tcp & operator=(const Tcp &)=delete
void setServerTlsConfig(const std::string &certPem, const std::string &keyPem)
Set the certificate and key to use for server-side TLS (PEM format).
bool bindAndListenPlain(int port)
TaskHandle_t connectingTask
Task handle for async operations.
int close()
Close the connection and free resources.
bool bindAndListen(int port)
Bind and listen on a port for incoming connections (for server use).
int recv(char *buffer, size_t size, uint32_t timeout_ms)
Receive data from the connection.
std::string server_tls_cert
altcp_pcb * pending_client
For TLS: set by acceptCallback.
std::string server_tls_key
Delegates to user or system configuration.
#define HTTP_BUFFER_SIZE
Size of the HTTP buffer for request/response data.
#define STREAM_SEND_DELAY_MS
bool resolveHostnameBlocking(const char *hostname, ip_addr_t *result, uint32_t timeout_ms=5000)
Blocking DNS resolution using lwIP.
void warning(const std::string &msg)
System utilities for diagnostics, memory, stack usage, and tracing.