mirror of
https://github.com/esphome/esphome.git
synced 2026-02-10 19:47:35 -07:00
Compare commits
2 Commits
hardening/
...
socket_rea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd683a5609 | ||
|
|
4a6eb0b16d |
@@ -16,19 +16,13 @@ namespace esphome::socket {
|
||||
|
||||
class BSDSocketImpl final : public Socket {
|
||||
public:
|
||||
BSDSocketImpl(int fd, bool monitor_loop = false) : fd_(fd) {
|
||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||
BSDSocketImpl(int fd, bool monitor_loop = false) {
|
||||
this->fd_ = fd;
|
||||
// Register new socket with the application for select() if monitoring requested
|
||||
if (monitor_loop && this->fd_ >= 0) {
|
||||
// Only set loop_monitored_ to true if registration succeeds
|
||||
this->loop_monitored_ = App.register_socket_fd(this->fd_);
|
||||
} else {
|
||||
this->loop_monitored_ = false;
|
||||
}
|
||||
#else
|
||||
// Without select support, ignore monitor_loop parameter
|
||||
(void) monitor_loop;
|
||||
#endif
|
||||
}
|
||||
~BSDSocketImpl() override {
|
||||
if (!this->closed_) {
|
||||
@@ -52,12 +46,10 @@ class BSDSocketImpl final : public Socket {
|
||||
int bind(const struct sockaddr *addr, socklen_t addrlen) override { return ::bind(this->fd_, addr, addrlen); }
|
||||
int close() override {
|
||||
if (!this->closed_) {
|
||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||
// Unregister from select() before closing if monitored
|
||||
if (this->loop_monitored_) {
|
||||
App.unregister_socket_fd(this->fd_);
|
||||
}
|
||||
#endif
|
||||
int ret = ::close(this->fd_);
|
||||
this->closed_ = true;
|
||||
return ret;
|
||||
@@ -130,23 +122,6 @@ class BSDSocketImpl final : public Socket {
|
||||
::fcntl(this->fd_, F_SETFL, fl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_fd() const override { return this->fd_; }
|
||||
|
||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||
bool ready() const override {
|
||||
if (!this->loop_monitored_)
|
||||
return true;
|
||||
return App.is_socket_ready(this->fd_);
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
int fd_;
|
||||
bool closed_{false};
|
||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||
bool loop_monitored_{false};
|
||||
#endif
|
||||
};
|
||||
|
||||
// Helper to create a socket with optional monitoring
|
||||
|
||||
@@ -452,6 +452,8 @@ class LWIPRawImpl : public Socket {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
bool ready() const override { return this->rx_buf_ != nullptr || this->rx_closed_ || this->pcb_ == nullptr; }
|
||||
|
||||
int setblocking(bool blocking) final {
|
||||
if (pcb_ == nullptr) {
|
||||
errno = ECONNRESET;
|
||||
@@ -576,6 +578,8 @@ class LWIPRawListenImpl final : public LWIPRawImpl {
|
||||
tcp_err(pcb_, LWIPRawImpl::s_err_fn); // Use base class error handler
|
||||
}
|
||||
|
||||
bool ready() const override { return this->accepted_socket_count_ > 0; }
|
||||
|
||||
std::unique_ptr<Socket> accept(struct sockaddr *addr, socklen_t *addrlen) override {
|
||||
if (pcb_ == nullptr) {
|
||||
errno = EBADF;
|
||||
|
||||
@@ -11,19 +11,13 @@ namespace esphome::socket {
|
||||
|
||||
class LwIPSocketImpl final : public Socket {
|
||||
public:
|
||||
LwIPSocketImpl(int fd, bool monitor_loop = false) : fd_(fd) {
|
||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||
LwIPSocketImpl(int fd, bool monitor_loop = false) {
|
||||
this->fd_ = fd;
|
||||
// Register new socket with the application for select() if monitoring requested
|
||||
if (monitor_loop && this->fd_ >= 0) {
|
||||
// Only set loop_monitored_ to true if registration succeeds
|
||||
this->loop_monitored_ = App.register_socket_fd(this->fd_);
|
||||
} else {
|
||||
this->loop_monitored_ = false;
|
||||
}
|
||||
#else
|
||||
// Without select support, ignore monitor_loop parameter
|
||||
(void) monitor_loop;
|
||||
#endif
|
||||
}
|
||||
~LwIPSocketImpl() override {
|
||||
if (!this->closed_) {
|
||||
@@ -49,12 +43,10 @@ class LwIPSocketImpl final : public Socket {
|
||||
int bind(const struct sockaddr *addr, socklen_t addrlen) override { return lwip_bind(this->fd_, addr, addrlen); }
|
||||
int close() override {
|
||||
if (!this->closed_) {
|
||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||
// Unregister from select() before closing if monitored
|
||||
if (this->loop_monitored_) {
|
||||
App.unregister_socket_fd(this->fd_);
|
||||
}
|
||||
#endif
|
||||
int ret = lwip_close(this->fd_);
|
||||
this->closed_ = true;
|
||||
return ret;
|
||||
@@ -97,23 +89,6 @@ class LwIPSocketImpl final : public Socket {
|
||||
lwip_fcntl(this->fd_, F_SETFL, fl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_fd() const override { return this->fd_; }
|
||||
|
||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||
bool ready() const override {
|
||||
if (!this->loop_monitored_)
|
||||
return true;
|
||||
return App.is_socket_ready(this->fd_);
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
int fd_;
|
||||
bool closed_{false};
|
||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||
bool loop_monitored_{false};
|
||||
#endif
|
||||
};
|
||||
|
||||
// Helper to create a socket with optional monitoring
|
||||
|
||||
@@ -10,6 +10,10 @@ namespace esphome::socket {
|
||||
|
||||
Socket::~Socket() {}
|
||||
|
||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||
bool Socket::ready() const { return !this->loop_monitored_ || App.is_socket_ready_(this->fd_); }
|
||||
#endif
|
||||
|
||||
// Platform-specific inet_ntop wrappers
|
||||
#if defined(USE_SOCKET_IMPL_LWIP_TCP)
|
||||
// LWIP raw TCP (ESP8266) uses inet_ntoa_r which takes struct by value
|
||||
|
||||
@@ -63,13 +63,29 @@ class Socket {
|
||||
virtual int setblocking(bool blocking) = 0;
|
||||
virtual int loop() { return 0; };
|
||||
|
||||
/// Get the underlying file descriptor (returns -1 if not supported)
|
||||
virtual int get_fd() const { return -1; }
|
||||
/// Get the underlying file descriptor (returns -1 if not supported)
|
||||
/// Non-virtual: only one socket implementation is active per build.
|
||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||
int get_fd() const { return this->fd_; }
|
||||
#else
|
||||
int get_fd() const { return -1; }
|
||||
#endif
|
||||
|
||||
/// Check if socket has data ready to read
|
||||
/// For loop-monitored sockets, checks with the Application's select() results
|
||||
/// For non-monitored sockets, always returns true (assumes data may be available)
|
||||
/// For select()-based sockets: non-virtual, checks Application's select() results
|
||||
/// For LWIP raw TCP sockets: virtual, checks internal buffer state
|
||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||
bool ready() const;
|
||||
#else
|
||||
virtual bool ready() const { return true; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||
int fd_{-1};
|
||||
bool closed_{false};
|
||||
bool loop_monitored_{false};
|
||||
#endif
|
||||
};
|
||||
|
||||
/// Create a socket of the given domain, type and protocol.
|
||||
|
||||
@@ -605,15 +605,6 @@ void Application::unregister_socket_fd(int fd) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::is_socket_ready(int fd) const {
|
||||
// This function is thread-safe for reading the result of select()
|
||||
// However, it should only be called after select() has been executed in the main loop
|
||||
// The read_fds_ is only modified by select() in the main loop
|
||||
if (fd < 0 || fd >= FD_SETSIZE)
|
||||
return false;
|
||||
|
||||
return FD_ISSET(fd, &this->read_fds_);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Application::yield_with_select_(uint32_t delay_ms) {
|
||||
|
||||
@@ -101,6 +101,10 @@
|
||||
#include "esphome/components/update/update_entity.h"
|
||||
#endif
|
||||
|
||||
namespace esphome::socket {
|
||||
class Socket;
|
||||
} // namespace esphome::socket
|
||||
|
||||
namespace esphome {
|
||||
|
||||
// Teardown timeout constant (in milliseconds)
|
||||
@@ -491,7 +495,8 @@ class Application {
|
||||
void unregister_socket_fd(int fd);
|
||||
/// Check if there's data available on a socket without blocking
|
||||
/// This function is thread-safe for reading, but should be called after select() has run
|
||||
bool is_socket_ready(int fd) const;
|
||||
/// The read_fds_ is only modified by select() in the main loop
|
||||
bool is_socket_ready(int fd) const { return fd >= 0 && this->is_socket_ready_(fd); }
|
||||
|
||||
#ifdef USE_WAKE_LOOP_THREADSAFE
|
||||
/// Wake the main event loop from a FreeRTOS task
|
||||
@@ -503,6 +508,15 @@ class Application {
|
||||
|
||||
protected:
|
||||
friend Component;
|
||||
friend class socket::Socket;
|
||||
|
||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||
/// Fast path for Socket::ready() via friendship - skips negative fd check.
|
||||
/// Safe because: fd was validated in register_socket_fd() at registration time,
|
||||
/// and Socket::ready() only calls this when loop_monitored_ is true (registration succeeded).
|
||||
/// FD_ISSET may include its own upper bounds check depending on platform.
|
||||
bool is_socket_ready_(int fd) const { return FD_ISSET(fd, &this->read_fds_); }
|
||||
#endif
|
||||
|
||||
void register_component_(Component *comp);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user