mirror of
https://github.com/esphome/esphome.git
synced 2026-09-11 15:27:33 +00:00
Report address parse failures from set_sockaddr and drop duplicated policy
This commit is contained in:
@@ -295,14 +295,11 @@ def _consume_api_sockets(config: ConfigType) -> ConfigType:
|
||||
|
||||
|
||||
def _validate_outgoing_connection(config: ConfigType) -> ConfigType:
|
||||
# The socket-layer constraint (raw lwip_tcp cannot dial out, the default
|
||||
# on esp8266/rp2040) is checked against the resolved implementation in
|
||||
# _validate_outgoing_socket_implementation at final validate
|
||||
if CONF_OUTGOING_CONNECTION not in config:
|
||||
return config
|
||||
if CORE.is_esp8266 or CORE.is_rp2:
|
||||
raise cv.Invalid(
|
||||
"outgoing_connection is not supported on this platform because its "
|
||||
"socket layer cannot make outgoing connections",
|
||||
path=[CONF_OUTGOING_CONNECTION],
|
||||
)
|
||||
if CONF_ENCRYPTION not in config:
|
||||
raise cv.Invalid(
|
||||
"outgoing_connection requires 'encryption' so the peer is verified by key",
|
||||
|
||||
@@ -81,10 +81,7 @@ void OutgoingConnectionManager::try_dial_(APIServer *server, uint32_t now) {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addr_len =
|
||||
socket::set_sockaddr((struct sockaddr *) &addr, sizeof(addr), host, API_OUTGOING_CONNECTION_PORT);
|
||||
// inet_addr() cannot signal failure: unparsable IPv4 text yields the
|
||||
// broadcast address, which is never a valid target either
|
||||
if (addr_len == 0 || (((struct sockaddr *) &addr)->sa_family == AF_INET &&
|
||||
((struct sockaddr_in *) &addr)->sin_addr.s_addr == ESPHOME_INADDR_NONE)) {
|
||||
if (addr_len == 0) {
|
||||
ESP_LOGW(TAG, "Invalid target %s", host);
|
||||
#ifndef API_OUTGOING_CONNECTION_HOST
|
||||
// A corrupt remembered value can never become dialable; forget it
|
||||
@@ -204,9 +201,8 @@ void OutgoingConnectionManager::on_client_removed(APIConnection *conn, bool was_
|
||||
const uint32_t now = App.get_loop_component_start_time();
|
||||
if (was_authenticated) {
|
||||
// A working peer (e.g. a host: target that never sends the flag)
|
||||
// disconnected normally
|
||||
// disconnected normally; state is IDLE, so loop() applies the delay
|
||||
this->backoff_ = BACKOFF_MIN_MS;
|
||||
this->schedule_wait_(now, API_OUTGOING_CONNECTION_DELAY);
|
||||
} else {
|
||||
this->schedule_retry_(now);
|
||||
}
|
||||
@@ -245,11 +241,10 @@ void OutgoingConnectionManager::on_target_client(APIConnection *conn) {
|
||||
}
|
||||
|
||||
void OutgoingConnectionManager::dump_config() const {
|
||||
#ifdef API_OUTGOING_CONNECTION_HOST
|
||||
const char *host = API_OUTGOING_CONNECTION_HOST;
|
||||
#else
|
||||
const char *host = this->saved_.host[0] != '\0' ? this->saved_.host : "none remembered yet";
|
||||
#endif
|
||||
const char *host = this->target_host_();
|
||||
if (host == nullptr) {
|
||||
host = "none remembered yet";
|
||||
}
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Outgoing connection port: %u\n"
|
||||
" Outgoing connection host: %s",
|
||||
|
||||
@@ -298,8 +298,9 @@ void APIServer::add_client_(APIConnection *conn) {
|
||||
APIConnection *APIServer::add_outgoing_client_(std::unique_ptr<socket::Socket> sock) {
|
||||
// Re-check at the handoff: inbound clients may have filled the slots and
|
||||
// the PSK may have been cleared (mark_outgoing() needs the noise helper)
|
||||
if (this->at_client_limit_() || !this->noise_ctx_.has_psk()) {
|
||||
ESP_LOGW(TAG, "Dropping outgoing connection (%s)", this->at_client_limit_() ? "max connections" : "no key active");
|
||||
const bool at_limit = this->at_client_limit_();
|
||||
if (at_limit || !this->noise_ctx_.has_psk()) {
|
||||
ESP_LOGW(TAG, "Dropping outgoing connection (%s)", at_limit ? "max connections" : "no key");
|
||||
return nullptr;
|
||||
}
|
||||
auto *conn = new APIConnection(std::move(sock), this);
|
||||
|
||||
@@ -165,7 +165,10 @@ socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const char *ip_
|
||||
#else
|
||||
// Use LWIP-specific functions
|
||||
ip6_addr_t ip6;
|
||||
inet6_aton(ip_address, &ip6);
|
||||
if (inet6_aton(ip_address, &ip6) == 0) {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
memcpy(server->sin6_addr.un.u32_addr, ip6.addr, sizeof(ip6.addr));
|
||||
#endif
|
||||
return sizeof(sockaddr_in6);
|
||||
@@ -185,7 +188,12 @@ socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const char *ip_
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
server->sin_addr.s_addr = inet_addr(ip_address);
|
||||
// Unlike inet_addr(), inet_aton() can signal failure while still
|
||||
// accepting the broadcast address 255.255.255.255
|
||||
if (inet_aton(ip_address, &server->sin_addr) == 0) {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
server->sin_port = htons(port);
|
||||
return sizeof(sockaddr_in);
|
||||
|
||||
@@ -67,9 +67,14 @@ def test_outgoing_connection_rejected_on_raw_lwip_platforms(
|
||||
set_core_config: SetCoreConfigCallable,
|
||||
platform_framework: PlatformFramework,
|
||||
) -> None:
|
||||
set_core_config(platform_framework)
|
||||
with pytest.raises(cv.Invalid, match="not supported on this platform"):
|
||||
CONFIG_SCHEMA(_api_config({"host": "192.168.1.2"}))
|
||||
"""esp8266 and rp2040 default to lwip_tcp and are rejected at final validate."""
|
||||
set_core_config(
|
||||
platform_framework,
|
||||
full_config={"socket": {"implementation": "lwip_tcp"}},
|
||||
)
|
||||
config = CONFIG_SCHEMA(_api_config({"host": "192.168.1.2"}))
|
||||
with pytest.raises(cv.Invalid, match="lwip_tcp"):
|
||||
_validate_outgoing_socket_implementation(config)
|
||||
|
||||
|
||||
def test_outgoing_connection_rejects_lwip_tcp_socket(
|
||||
|
||||
Reference in New Issue
Block a user