DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
listener.h File Reference

Layer 4 (Listener) - per-port TCP listener abstraction. More...

#include "ServerConfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "lwip/tcp.h"
#include "tcp.h"

Go to the source code of this file.

Classes

struct  Listener
 State for one TCP listening port. More...
 

Functions

int32_t listener_add (uint8_t idx, uint16_t port, ConnProto proto, bool tls=false)
 Create a listening socket on port and register it at idx.
 
void listener_stop (uint8_t idx)
 Stop listening on the port at idx and release its resources.
 
void listener_stop_all ()
 Stop all active listeners.
 
int32_t listener_add_dynamic (uint8_t idx, uint16_t port, ConnProto proto)
 Add / stop a listener from a running task (thread-safe variant).
 
void listener_stop_dynamic (uint8_t idx)
 
bool listener_enqueue (uint8_t listener_id, const TcpEvt *evt)
 Post evt to the queue owned by listener listener_id.
 
bool listener_accept_allowed (uint32_t now_ms)
 Fixed-window global accept-rate gate (connection-flood defense).
 
void listener_accept_throttle_reset (void)
 Reset the accept-throttle window counters.
 
bool listener_accept_allowed_ip (const DetIp *ip, uint32_t now_ms)
 Fixed-window per-IP accept-rate gate (connection-flood defense, keyed by source address).
 
void listener_per_ip_throttle_reset (void)
 Reset the per-IP throttle bucket table.
 
bool listener_ip_allow_add (const DetIp *network, uint8_t prefix_len)
 Add a CIDR rule to the source-IP allowlist.
 
bool listener_ip_allow_add_cidr (const char *cidr)
 Add an allowlist rule from CIDR text (the ergonomic public entry point).
 
bool listener_ip_allowed (const DetIp *ip)
 Test a source address against the allowlist (accept-time firewall).
 
void listener_ip_allowlist_reset (void)
 Clear all allowlist rules (the allowlist becomes empty = allow all).
 

Variables

Listener listener_pool [MAX_LISTENERS]
 Static pool of listener contexts. Defined in listener.cpp.
 

Detailed Description

Layer 4 (Listener) - per-port TCP listener abstraction.

Each active listener owns one lwIP listening PCB and one statically- allocated FreeRTOS queue. When a new client connects, listener_accept_cb claims a slot from the shared conn_pool, wires the standard per-connection callbacks, and posts EvtType::EVT_CONNECT to the owning listener's queue.

The session layer drains all active listener queues each server_tick(), routing events to the correct protocol handler via TcpConn::proto.

Single accept callback tcp_arg(listen_pcb, (void*)(uintptr_t)idx) embeds the listener index in the PCB user-data so a single static listener_accept_cb handles all ports.

Circular-dependency resolution tcp.cpp needs to post events to listener queues but cannot include this header (listener.h already includes tcp.h). The symbol listener_enqueue() is exported from listener.cpp; tcp.cpp calls it via a forward declaration added to tcp.h so no circular include is introduced.

Author
Douglas Quigg (dstroy0)
Date
2026

Definition in file listener.h.

Function Documentation

◆ listener_add()

int32_t listener_add ( uint8_t  idx,
uint16_t  port,
ConnProto  proto,
bool  tls = false 
)

Create a listening socket on port and register it at idx.

If the slot at idx is already active it is stopped first. Creates a per-listener FreeRTOS static queue and an lwIP listening PCB. Wires listener_accept_cb as the accept handler with the listener index embedded as the PCB user-data argument.

Parameters
idxSlot in listener_pool[] (0 … MAX_LISTENERS-1).
portTCP port to bind and listen on.
protoApplication protocol spoken on connections from this port.
tlsWhen true, connections accepted here start a TLS handshake.
Returns
Positive value on success; -1 on failure (pool full or lwIP error).

Definition at line 466 of file listener.cpp.

References Listener::_queue_storage, Listener::_queue_struct, Listener::active, EVT_QUEUE_DEPTH, Listener::listen_pcb, listener_pool, listener_stop(), MAX_CONNS, MAX_LISTENERS, Listener::port, Listener::proto, Listener::queue, and Listener::tls.

Referenced by DetWebServer::begin().

◆ listener_stop()

void listener_stop ( uint8_t  idx)

Stop listening on the port at idx and release its resources.

Idempotent - safe to call on an already-stopped slot. Closes the lwIP listening PCB and deletes the FreeRTOS queue. Does not close any connections already accepted on this port.

Parameters
idxSlot in listener_pool[].

Definition at line 525 of file listener.cpp.

References Listener::active, Listener::listen_pcb, listener_pool, MAX_LISTENERS, and Listener::queue.

Referenced by listener_add(), and listener_stop_all().

◆ listener_stop_all()

void listener_stop_all ( )

Stop all active listeners.

Convenience wrapper that calls listener_stop() for every slot in listener_pool[]. Called by DetWebServer::stop().

Definition at line 545 of file listener.cpp.

References listener_stop(), and MAX_LISTENERS.

Referenced by DetWebServer::stop().

◆ listener_add_dynamic()

int32_t listener_add_dynamic ( uint8_t  idx,
uint16_t  port,
ConnProto  proto 
)

Add / stop a listener from a running task (thread-safe variant).

listener_add() runs the raw lwIP tcp_bind/tcp_listen inline, which is only safe at setup (before tcpip_thread is servicing our sockets). These variants marshal the lwIP operations onto tcpip_thread via tcpip_api_call(), so a listener may be created or torn down dynamically from a worker/session task - used by the SSH remote-forward owner (ssh -R), which opens a listener when a client requests one. TLS listeners are not supported here (forwarded ports are plaintext bridges). On the native host (no lwIP) these behave like the inline path for unit tests.

Returns
listener_add_dynamic: 1 on success, -1 on failure (bad idx, bind in use, or lwIP error). listener_stop_dynamic: void, idempotent.

Definition at line 619 of file listener.cpp.

References Listener::_queue_storage, Listener::_queue_struct, Listener::active, EVT_QUEUE_DEPTH, Listener::listen_pcb, listener_pool, listener_stop_dynamic(), MAX_LISTENERS, Listener::port, Listener::proto, Listener::queue, and Listener::tls.

◆ listener_stop_dynamic()

void listener_stop_dynamic ( uint8_t  idx)

◆ listener_enqueue()

bool listener_enqueue ( uint8_t  listener_id,
const TcpEvt evt 
)

Post evt to the queue owned by listener listener_id.

Called from tcp.cpp callbacks (running in tcpip_thread context) to deliver connection events to the session layer. Uses xQueueSend with timeout=0 - a full queue means the application is not calling server_tick() fast enough; the dropped event is recoverable via connection timeout.

Parameters
listener_idIndex into listener_pool[]; must be < MAX_LISTENERS.
evtEvent to copy into the queue.
Returns
true if queued; false if dropped (full queue / inactive listener).

Definition at line 291 of file listener.cpp.

◆ listener_accept_allowed()

bool listener_accept_allowed ( uint32_t  now_ms)

Fixed-window global accept-rate gate (connection-flood defense).

Returns true if a new connection accepted at now_ms is within the DETWS_ACCEPT_THROTTLE_MAX-per-DETWS_ACCEPT_THROTTLE_WINDOW_MS budget (and counts it), false if the budget for the current window is exhausted. State is two static counters shared across all listeners. The accept callback consults this only when DETWS_ENABLE_ACCEPT_THROTTLE is set; the function is always compiled so it can be unit-tested. Call listener_accept_throttle_reset() to clear the window (e.g. between tests).

Definition at line 55 of file listener.cpp.

References AcceptThrottleCtx::count, DETWS_ACCEPT_THROTTLE_MAX, DETWS_ACCEPT_THROTTLE_WINDOW_MS, and AcceptThrottleCtx::window_start.

◆ listener_accept_throttle_reset()

void listener_accept_throttle_reset ( void  )

Reset the accept-throttle window counters.

Definition at line 69 of file listener.cpp.

References AcceptThrottleCtx::count, and AcceptThrottleCtx::window_start.

◆ listener_accept_allowed_ip()

bool listener_accept_allowed_ip ( const DetIp ip,
uint32_t  now_ms 
)

Fixed-window per-IP accept-rate gate (connection-flood defense, keyed by source address).

Returns true if a connection from source address ip accepted at now_ms is within that address's DETWS_PER_IP_THROTTLE_MAX-per-DETWS_PER_IP_THROTTLE_WINDOW_MS budget (and counts it), false once that address has exhausted its budget for the current window. The key is the full family-tagged address (DetIp): an IPv4 and an IPv6 peer are always distinct buckets, and an IPv6 attacker cannot fold many addresses onto one bucket (or evict a victim's) through a lossy hash. State is a fixed BSS table of DETWS_PER_IP_THROTTLE_SLOTS buckets; a new address reuses an empty, expired, or least-recently-started bucket so memory stays bounded. An unspecified ip (family DetIpFamily::DET_IP_NONE) is passed through (allowed) since it cannot be tracked. The accept callback consults this only when DETWS_ENABLE_PER_IP_THROTTLE is set; the function is always compiled so it can be unit-tested. Call listener_per_ip_throttle_reset() to clear the table.

Definition at line 95 of file listener.cpp.

References IpThrottleBucket::addr, IpThrottleCtx::buckets, IpThrottleBucket::count, det_ip_equal(), det_ip_is_unspecified(), DET_IP_NONE, DETWS_PER_IP_THROTTLE_MAX, DETWS_PER_IP_THROTTLE_SLOTS, DETWS_PER_IP_THROTTLE_WINDOW_MS, DetIp::family, and IpThrottleBucket::window_start.

◆ listener_per_ip_throttle_reset()

void listener_per_ip_throttle_reset ( void  )

◆ listener_ip_allow_add()

bool listener_ip_allow_add ( const DetIp network,
uint8_t  prefix_len 
)

Add a CIDR rule to the source-IP allowlist.

Parameters
networkFamily-tagged network address (DetIp, IPv4 or IPv6). Host bits outside the prefix do not need to be pre-masked; matching masks them at compare time.
prefix_lenCIDR prefix length: 0..32 for IPv4, 0..128 for IPv6 (32 / 128 for a single host, 0 to match every address of that family).
Returns
true if the rule was stored; false if network is unspecified, the prefix length exceeds the family width, or the table (DETWS_IP_ALLOWLIST_SLOTS entries) is full.

Definition at line 175 of file listener.cpp.

References IpAllowCtx::count, DET_IP_V4, DET_IP_V6, DETWS_IP_ALLOWLIST_SLOTS, DetIp::family, IpAllowRule::network, IpAllowRule::prefix_len, and IpAllowCtx::rules.

Referenced by listener_ip_allow_add_cidr().

◆ listener_ip_allow_add_cidr()

bool listener_ip_allow_add_cidr ( const char *  cidr)

Add an allowlist rule from CIDR text (the ergonomic public entry point).

Accepts IPv4 or IPv6 in address/prefix form (e.g. "192.168.1.0/24", "2001:db8::/32") or a bare address (e.g. "10.0.0.5", "::1") which is treated as a host route (/32 for v4, /128 for v6). The address is parsed with det_ip_parse so every RFC 4291 v6 text form is accepted.

Returns
true if the rule was stored; false if cidr is malformed, the prefix is out of range for the family, or the table is full.

Definition at line 191 of file listener.cpp.

References DET_IP_NONE, det_ip_parse(), DET_IP_STR_MAX, DET_IP_V4, DetIp::family, and listener_ip_allow_add().

◆ listener_ip_allowed()

bool listener_ip_allowed ( const DetIp ip)

Test a source address against the allowlist (accept-time firewall).

Parameters
ipFamily-tagged source address (DetIp).
Returns
true if the address is allowed: always true while the allowlist is empty (so enabling the feature without rules never locks the device out), otherwise true only if ip is contained in at least one CIDR rule of the same family (prefix match on the full address, never a hash). The accept callback consults this only when DETWS_ENABLE_IP_ALLOWLIST is set; the function is always compiled so it can be unit-tested.

Definition at line 242 of file listener.cpp.

References IpAllowCtx::count, det_ip_prefix_match(), IpAllowRule::network, IpAllowRule::prefix_len, and IpAllowCtx::rules.

◆ listener_ip_allowlist_reset()

void listener_ip_allowlist_reset ( void  )

Clear all allowlist rules (the allowlist becomes empty = allow all).

Definition at line 255 of file listener.cpp.

References IpAllowCtx::count, DET_IP_NONE, DETWS_IP_ALLOWLIST_SLOTS, DetIp::family, IpAllowRule::network, and IpAllowCtx::rules.

Variable Documentation

◆ listener_pool

Listener listener_pool[MAX_LISTENERS]
extern

Static pool of listener contexts. Defined in listener.cpp.

Definition at line 34 of file listener.cpp.

Referenced by listener_add(), listener_add_dynamic(), listener_enqueue(), listener_stop(), listener_stop_dynamic(), and server_tick().