ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
tcp_listener.cpp
Go to the documentation of this file.
1#include "tcp_listener.h"
2
3#if defined(USE_SOCKET_IMPL_LWIP_TCP) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS) || defined(USE_SOCKET_IMPL_BSD_SOCKETS)
4
6#include "esphome/core/log.h"
7
8#include <cerrno>
9#include <span>
10
11namespace esphome::socket {
12
13// One client at a time; a second connection waits in the stack until the first drops.
14static constexpr int LISTEN_BACKLOG = 1;
15#ifdef USE_SOCKET_IPV4_ALLOW
16static constexpr uint32_t REJECT_LOG_INTERVAL_MS = 5000;
17#endif
18
20 this->listen_ = socket_ip_loop_monitored(SOCK_STREAM, IPPROTO_TCP);
21 int err = errno;
22 if (this->listen_ != nullptr) {
23 int yes = 1;
24 this->listen_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
25 struct sockaddr_storage local;
26 socklen_t local_len = set_sockaddr_any(reinterpret_cast<struct sockaddr *>(&local), sizeof(local), link.port());
27 // A blocking listener would stall loop() inside accept(), so its
28 // setblocking result is part of the success condition.
29 if (this->listen_->setblocking(false) == 0 && local_len != 0 &&
30 this->listen_->bind(reinterpret_cast<struct sockaddr *>(&local), local_len) == 0 &&
31 this->listen_->listen(LISTEN_BACKLOG) == 0) {
32 ESP_LOGI(this->tag_, "Listening on %u", link.port());
33 return;
34 }
35 // Captured before reset(); the close inside can overwrite errno.
36 err = errno;
37 this->listen_.reset();
38 }
39 ESP_LOGW(this->tag_, "Listen on %u failed: %d", link.port(), err);
40 link.note_attempt();
41}
42
44 struct sockaddr_storage peer {};
45 socklen_t peer_len = sizeof(peer);
46 auto client = this->listen_->accept_loop_monitored(reinterpret_cast<struct sockaddr *>(&peer), &peer_len);
47 if (client == nullptr) {
48 // A reset during the handshake or a signal only affects that connection.
49 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ECONNABORTED || errno == EINTR) {
50 return;
51 }
52 // Rebuild the listener after the backoff instead of spinning on it.
53 int err = errno;
54 this->listen_.reset();
55 ESP_LOGW(this->tag_, "Accept failed: %d", err);
56 link.note_attempt();
57 return;
58 }
59 const auto *sa = reinterpret_cast<const struct sockaddr *>(&peer);
60 char text[SOCKADDR_STR_LEN];
61 format_sockaddr_to(sa, peer_len, std::span<char, SOCKADDR_STR_LEN>(text));
62#ifdef USE_SOCKET_IPV4_ALLOW
63 if (!this->allow_.allows(sa)) {
65 if (this->last_reject_log_ms_ == 0 || now - this->last_reject_log_ms_ >= REJECT_LOG_INTERVAL_MS) {
66 this->last_reject_log_ms_ = now;
67 ESP_LOGW(this->tag_, "Rejected %s", text);
68 }
69 return;
70 }
71#endif
72 link.adopt(std::move(client));
73 ESP_LOGI(this->tag_, "Client connected from %s", text);
74}
75
77#ifdef USE_SOCKET_IPV4_ALLOW
78 for (size_t i = 0; i < this->allow_.size(); i++) {
79 Ipv4AllowEntry e = this->allow_.entry(i);
80 // Network order is dotted order, and the contiguous mask's popcount is the prefix.
81 const auto *b = reinterpret_cast<const uint8_t *>(&e.addr);
82 ESP_LOGCONFIG(this->tag_, " Allowed IP: %u.%u.%u.%u/%u", b[0], b[1], b[2], b[3],
83 static_cast<unsigned>(__builtin_popcount(e.mask)));
84 }
85#endif
86}
87
88} // namespace esphome::socket
89
90#endif
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
Ipv4AllowEntry entry(size_t i) const
A copy of entry i, read from flash.
Definition ipv4_allow.h:52
bool allows(const struct sockaddr *peer) const
A v4 mapped IPv6 peer is unwrapped; any other family fails a non empty list.
Definition ipv4_allow.h:28
void dump_config() const
One config line per allowed network.
std::unique_ptr< ListenSocket > listen_
void try_listen_(TcpClientLink &link)
void accept_(TcpClientLink &link)
uint32_t socklen_t
Definition headers.h:109
size_t format_sockaddr_to(const struct sockaddr *addr_ptr, socklen_t len, std::span< char, SOCKADDR_STR_LEN > buf)
Format sockaddr into caller-provided buffer, returns length written (excluding null)
socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port)
Set a sockaddr to the any address and specified port for the IP version used by socket_ip().
std::unique_ptr< ListenSocket > socket_ip_loop_monitored(int type, int protocol)
Definition socket.h:175
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t
One allowed IPv4 network, network byte order, host bits cleared.
Definition ipv4_allow.h:14
uint32_t mask
Definition ipv4_allow.h:16
uint32_t addr
Definition ipv4_allow.h:15