mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-03 00:21:56 +00:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			2025.7.0b1
			...
			socket-cli
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					d70ee03010 | ||
| 
						 | 
					a81fc6e85d | ||
| 
						 | 
					c19d893e4e | ||
| 
						 | 
					f4183778e3 | ||
| 
						 | 
					11e8bd77e2 | ||
| 
						 | 
					a39b2c4ac7 | 
@@ -1,5 +1,6 @@
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.core import CORE
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@esphome/core"]
 | 
			
		||||
 | 
			
		||||
@@ -28,3 +29,6 @@ async def to_code(config):
 | 
			
		||||
        cg.add_define("USE_SOCKET_IMPL_LWIP_TCP")
 | 
			
		||||
    elif impl == IMPLEMENTATION_BSD_SOCKETS:
 | 
			
		||||
        cg.add_define("USE_SOCKET_IMPL_BSD_SOCKETS")
 | 
			
		||||
 | 
			
		||||
    if CORE.target_platform in ["esp8266", "esp32"]:
 | 
			
		||||
        cg.add_define("USE_SOCKET_HAS_LWIP")
 | 
			
		||||
 
 | 
			
		||||
@@ -53,6 +53,43 @@ class BSDSocketImpl : public Socket {
 | 
			
		||||
    return make_unique<BSDSocketImpl>(fd);
 | 
			
		||||
  }
 | 
			
		||||
  int bind(const struct sockaddr *addr, socklen_t addrlen) override { return ::bind(fd_, addr, addrlen); }
 | 
			
		||||
  int connect(const struct sockaddr *addr, socklen_t addrlen) override { return ::connect(fd_, addr, addrlen); }
 | 
			
		||||
  int connect_finished() override {
 | 
			
		||||
    fd_set wfds;
 | 
			
		||||
    struct timeval tv;
 | 
			
		||||
    FD_ZERO(&wfds);
 | 
			
		||||
    FD_SET(fd_, &wfds);
 | 
			
		||||
    tv.tv_sec = 0;
 | 
			
		||||
    tv.tv_usec = 0;
 | 
			
		||||
    int retval = ::select(fd_ + 1, nullptr, &wfds, nullptr, &tv);
 | 
			
		||||
    if (retval == -1) {
 | 
			
		||||
      // reuse errno
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (retval == 0) {
 | 
			
		||||
      // timeout, not writable yet
 | 
			
		||||
      errno = EINPROGRESS;
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (!FD_ISSET(fd_, &wfds)) {
 | 
			
		||||
      errno = ECONNREFUSED;
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int so_error;
 | 
			
		||||
    socklen_t len = sizeof(so_error);
 | 
			
		||||
    int ret = this->getsockopt(SOL_SOCKET, SO_ERROR, &so_error, &len);
 | 
			
		||||
    if (ret == -1) {
 | 
			
		||||
      // reuse errno
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (so_error == 0) {
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
    errno = ECONNREFUSED;
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int close() override {
 | 
			
		||||
    int ret = ::close(fd_);
 | 
			
		||||
    closed_ = true;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										34
									
								
								esphome/components/socket/getaddrinfo.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								esphome/components/socket/getaddrinfo.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include "headers.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace socket {
 | 
			
		||||
 | 
			
		||||
struct GetaddrinfoFuture {
 | 
			
		||||
 public:
 | 
			
		||||
  virtual ~GetaddrinfoFuture() = default;
 | 
			
		||||
  // returns true when the request has completed (successfully or with an error)
 | 
			
		||||
  virtual bool completed() = 0;
 | 
			
		||||
  /**
 | 
			
		||||
   * @brief Fetch the completed result into res.
 | 
			
		||||
   *
 | 
			
		||||
   * Should only be called after completed() returned true.
 | 
			
		||||
   * Make sure to call freeaddrinfo() to free the addrinfo storage
 | 
			
		||||
   * when it's no longer needed.
 | 
			
		||||
   *
 | 
			
		||||
   * @return See posix getaddrinfo() return values.
 | 
			
		||||
   */
 | 
			
		||||
  virtual int fetch_result(struct addrinfo **res) = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<GetaddrinfoFuture> getaddrinfo_async(const char *node, const char *service,
 | 
			
		||||
                                                     const struct addrinfo *hints);
 | 
			
		||||
 | 
			
		||||
}  // namespace socket
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
void freeaddrinfo(struct addrinfo *ai);
 | 
			
		||||
const char *gai_strerror(int errcode);
 | 
			
		||||
#endif
 | 
			
		||||
@@ -8,6 +8,7 @@
 | 
			
		||||
 | 
			
		||||
#define LWIP_INTERNAL
 | 
			
		||||
#include "lwip/inet.h"
 | 
			
		||||
#include "lwip/netdb.h"
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
@@ -118,6 +119,34 @@ struct iovec {
 | 
			
		||||
#define ESPHOME_INADDR_NONE INADDR_NONE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef EAI_FAIL
 | 
			
		||||
#define EAI_BADFLAGS (-1)
 | 
			
		||||
#define EAI_NONAME (-2)
 | 
			
		||||
#define EAI_AGAIN (-3)
 | 
			
		||||
#define EAI_FAIL (-4)
 | 
			
		||||
#define EAI_FAMILY (-6)
 | 
			
		||||
#define EAI_SOCKTYPE (-7)
 | 
			
		||||
#define EAI_SERVICE (-8)
 | 
			
		||||
#define EAI_MEMORY (-10)
 | 
			
		||||
#define EAI_SYSTEM (-11)
 | 
			
		||||
#define EAI_OVERFLOW (-12)
 | 
			
		||||
#endif  // !EAI_FAIL
 | 
			
		||||
 | 
			
		||||
#ifndef IPPROTO_UDP
 | 
			
		||||
#define IPPROTO_UDP 17
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct addrinfo {  // NOLINT(readability-identifier-naming)
 | 
			
		||||
  int ai_flags;
 | 
			
		||||
  int ai_family;
 | 
			
		||||
  int ai_socktype;
 | 
			
		||||
  int ai_protocol;
 | 
			
		||||
  socklen_t ai_addrlen;
 | 
			
		||||
  struct sockaddr *ai_addr;
 | 
			
		||||
  char *ai_canonname;
 | 
			
		||||
  struct addrinfo *ai_next;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif  // USE_SOCKET_IMPL_LWIP_TCP
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SOCKET_IMPL_BSD_SOCKETS
 | 
			
		||||
@@ -129,6 +158,7 @@ struct iovec {
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/uio.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <netdb.h>
 | 
			
		||||
 | 
			
		||||
#ifdef USE_HOST
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										197
									
								
								esphome/components/socket/lwip_getaddrinfo_impl.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								esphome/components/socket/lwip_getaddrinfo_impl.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,197 @@
 | 
			
		||||
#include "getaddrinfo.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SOCKET_HAS_LWIP
 | 
			
		||||
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include "lwip/dns.h"
 | 
			
		||||
#include "lwip/ip_addr.h"
 | 
			
		||||
#include "lwip/netdb.h"
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace socket {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "socket.lwipgetaddrinfo";
 | 
			
		||||
 | 
			
		||||
struct LwipDNSResult {
 | 
			
		||||
  bool completed;
 | 
			
		||||
  bool error;
 | 
			
		||||
  ip_addr_t ipaddr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct LwipDNSCallbackArg {
 | 
			
		||||
  std::weak_ptr<LwipDNSResult> res;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void lwip_dns_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
 | 
			
		||||
  LwipDNSCallbackArg *arg = reinterpret_cast<LwipDNSCallbackArg *>(callback_arg);
 | 
			
		||||
  {
 | 
			
		||||
    std::shared_ptr<LwipDNSResult> result = arg->res.lock();
 | 
			
		||||
    if (result) {
 | 
			
		||||
      if (ipaddr == nullptr) {
 | 
			
		||||
        result->error = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        result->error = false;
 | 
			
		||||
        ip_addr_copy(result->ipaddr, *ipaddr);
 | 
			
		||||
      }
 | 
			
		||||
      result->completed = true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  delete arg;  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class LwipGetaddrinfoFuture : public GetaddrinfoFuture {
 | 
			
		||||
 public:
 | 
			
		||||
  LwipGetaddrinfoFuture(std::shared_ptr<LwipDNSResult> result, int hint_ai_socktype, int hint_ai_protocol,
 | 
			
		||||
                        uint16_t portno)
 | 
			
		||||
      : result_(std::move(result)),
 | 
			
		||||
        hint_ai_socktype_(hint_ai_socktype),
 | 
			
		||||
        hint_ai_protocol_(hint_ai_protocol),
 | 
			
		||||
        portno_(portno) {}
 | 
			
		||||
  ~LwipGetaddrinfoFuture() override = default;
 | 
			
		||||
 | 
			
		||||
  bool completed() override { return result_->completed; }
 | 
			
		||||
  int fetch_result(struct addrinfo **res) override {
 | 
			
		||||
    if (res == nullptr)
 | 
			
		||||
      return EAI_FAIL;
 | 
			
		||||
    *res = nullptr;
 | 
			
		||||
    if (!result_->completed)
 | 
			
		||||
      return EAI_FAIL;
 | 
			
		||||
    if (result_->error)
 | 
			
		||||
      return EAI_FAIL;
 | 
			
		||||
 | 
			
		||||
    size_t alloc_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
 | 
			
		||||
    // NOLINTNEXTLINE(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
 | 
			
		||||
    void *storage = malloc(alloc_size);
 | 
			
		||||
    memset(storage, 0, alloc_size);
 | 
			
		||||
    struct addrinfo *ai = reinterpret_cast<struct addrinfo *>(storage);
 | 
			
		||||
    struct sockaddr_storage *sa = reinterpret_cast<struct sockaddr_storage *>(ai + 1);
 | 
			
		||||
 | 
			
		||||
    bool isipv6 = IP_IS_V6(result_->ipaddr);
 | 
			
		||||
 | 
			
		||||
    bool istcp = true;
 | 
			
		||||
    if ((hint_ai_socktype_ != 0 && hint_ai_socktype_ == SOCK_DGRAM) ||
 | 
			
		||||
        (hint_ai_protocol_ != 0 && hint_ai_protocol_ == IPPROTO_UDP)) {
 | 
			
		||||
      istcp = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ai->ai_family = isipv6 ? AF_INET6 : AF_INET;
 | 
			
		||||
    ai->ai_socktype = istcp ? SOCK_STREAM : SOCK_DGRAM;
 | 
			
		||||
    ai->ai_protocol = istcp ? IPPROTO_TCP : IPPROTO_UDP;
 | 
			
		||||
 | 
			
		||||
    if (isipv6) {
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
      struct sockaddr_in6 *sa6 = reinterpret_cast<struct sockaddr_in6 *>(sa);
 | 
			
		||||
      inet6_addr_from_ip6addr(&sa6->sin6_addr, ip_2_ip6(&result_->ipaddr)) sa6->sin6_family = AF_INET6;
 | 
			
		||||
      sa6->sin6_len = sizeof(struct sockaddr_in6);
 | 
			
		||||
      sa6->sin6_port = htons(portno_);
 | 
			
		||||
#endif  // LWIP_IPV6
 | 
			
		||||
    } else {
 | 
			
		||||
      struct sockaddr_in *sa4 = reinterpret_cast<struct sockaddr_in *>(sa);
 | 
			
		||||
      inet_addr_from_ip4addr(&sa4->sin_addr, ip_2_ip4(&result_->ipaddr));
 | 
			
		||||
      sa4->sin_family = AF_INET;
 | 
			
		||||
      sa4->sin_len = sizeof(struct sockaddr_in);
 | 
			
		||||
      sa4->sin_port = htons(portno_);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ai->ai_addrlen = sizeof(struct sockaddr_storage);
 | 
			
		||||
    ai->ai_addr = reinterpret_cast<struct sockaddr *>(sa);
 | 
			
		||||
    *res = ai;
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::shared_ptr<LwipDNSResult> result_;
 | 
			
		||||
  int hint_ai_socktype_;
 | 
			
		||||
  int hint_ai_protocol_;
 | 
			
		||||
  uint16_t portno_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<GetaddrinfoFuture> getaddrinfo_async(const char *node, const char *service,
 | 
			
		||||
                                                     const struct addrinfo *hints) {
 | 
			
		||||
  std::shared_ptr<LwipDNSResult> result = std::make_shared<LwipDNSResult>();
 | 
			
		||||
  result->completed = false;
 | 
			
		||||
 | 
			
		||||
  uint16_t portno = 0;
 | 
			
		||||
  if (service != nullptr) {
 | 
			
		||||
    optional<uint16_t> i = parse_number<uint16_t>(service);
 | 
			
		||||
    if (!i.has_value()) {
 | 
			
		||||
      result->completed = true;
 | 
			
		||||
      result->error = true;
 | 
			
		||||
      return std::unique_ptr<GetaddrinfoFuture>{new LwipGetaddrinfoFuture(result, 0, 0, 0)};
 | 
			
		||||
    }
 | 
			
		||||
    portno = *i;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int hint_ai_socktype = 0, hint_ai_protocol = 0;
 | 
			
		||||
  uint8_t dns_addrtype = LWIP_DNS_ADDRTYPE_DEFAULT;
 | 
			
		||||
  if (hints != nullptr) {
 | 
			
		||||
    hint_ai_socktype = hints->ai_socktype;
 | 
			
		||||
    hint_ai_protocol = hints->ai_protocol;
 | 
			
		||||
    if (hints->ai_family == AF_INET) {
 | 
			
		||||
      dns_addrtype = LWIP_DNS_ADDRTYPE_IPV4;
 | 
			
		||||
    } else if (hints->ai_family == AF_INET6) {
 | 
			
		||||
      dns_addrtype = LWIP_DNS_ADDRTYPE_IPV6;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
 | 
			
		||||
  LwipDNSCallbackArg *callback_arg = new LwipDNSCallbackArg;
 | 
			
		||||
  callback_arg->res = result;
 | 
			
		||||
 | 
			
		||||
  ip_addr_t immediate_result;
 | 
			
		||||
  err_t err = dns_gethostbyname_addrtype(node, &immediate_result, lwip_dns_callback, callback_arg, dns_addrtype);
 | 
			
		||||
  if (err == ERR_OK) {
 | 
			
		||||
    // immediate result
 | 
			
		||||
    result->completed = true;
 | 
			
		||||
    result->error = false;
 | 
			
		||||
    ip_addr_copy(result->ipaddr, immediate_result);
 | 
			
		||||
 | 
			
		||||
    // callback won't be called
 | 
			
		||||
    delete callback_arg;  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
  } else if (err == ERR_INPROGRESS) {
 | 
			
		||||
    // result notified via callback
 | 
			
		||||
  } else {
 | 
			
		||||
    // error
 | 
			
		||||
    result->completed = true;
 | 
			
		||||
    result->error = true;
 | 
			
		||||
 | 
			
		||||
    // callback won't be called
 | 
			
		||||
    delete callback_arg;  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return std::unique_ptr<GetaddrinfoFuture>{
 | 
			
		||||
      new LwipGetaddrinfoFuture(result, hint_ai_socktype, hint_ai_protocol, portno)};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace socket
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
void freeaddrinfo(struct addrinfo *ai) {
 | 
			
		||||
  while (ai != nullptr) {
 | 
			
		||||
    struct addrinfo *next = ai->ai_next;
 | 
			
		||||
    delete ai;  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
    ai = next;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
const char *gai_strerror(int errcode) {
 | 
			
		||||
  switch (errcode) {
 | 
			
		||||
    case EAI_BADFLAGS: return "badflags";
 | 
			
		||||
    case EAI_NONAME: return "noname";
 | 
			
		||||
    case EAI_AGAIN: return "again";
 | 
			
		||||
    case EAI_FAMILY: return "family";
 | 
			
		||||
    case EAI_SOCKTYPE: return "socktype";
 | 
			
		||||
    case EAI_SERVICE: return "service";
 | 
			
		||||
    case EAI_MEMORY: return "memory";
 | 
			
		||||
    case EAI_SYSTEM: return "system";
 | 
			
		||||
    case EAI_OVERFLOW: return "overflow";
 | 
			
		||||
    default: return "unknown";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif  // USE_SOCKET_HAS_LWIP
 | 
			
		||||
@@ -69,7 +69,7 @@ class LWIPRawImpl : public Socket {
 | 
			
		||||
    }
 | 
			
		||||
    if (name == nullptr) {
 | 
			
		||||
      errno = EINVAL;
 | 
			
		||||
      return 0;
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
    ip_addr_t ip;
 | 
			
		||||
    in_port_t port;
 | 
			
		||||
@@ -126,6 +126,76 @@ class LWIPRawImpl : public Socket {
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
  int connect(const struct sockaddr *addr, socklen_t addrlen) override {
 | 
			
		||||
    if (pcb_ == nullptr) {
 | 
			
		||||
      errno = EBADF;
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (addr == nullptr) {
 | 
			
		||||
      errno = EINVAL;
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (connecting_) {
 | 
			
		||||
      errno = EALREADY;
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ip_addr_t ipaddr;
 | 
			
		||||
    uint16_t port;
 | 
			
		||||
 | 
			
		||||
    if (addr->sa_family == AF_INET) {
 | 
			
		||||
      const struct sockaddr_in *sa4 = reinterpret_cast<const struct sockaddr_in *>(addr);
 | 
			
		||||
      inet_addr_to_ip4addr(ip_2_ip4(&ipaddr), &sa4->sin_addr);
 | 
			
		||||
#if LWIP_IPV4 && LWIP_IPV6
 | 
			
		||||
      ipaddr.type = IPADDR_TYPE_V4;
 | 
			
		||||
#endif
 | 
			
		||||
      port = ntohs(sa4->sin_port);
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
    } else if (addr->sa_family == AF_INET6) {
 | 
			
		||||
      const struct sockaddr_in6 *sa6 = reinterpret_cast<const struct sockaddr_in6 *>(addr);
 | 
			
		||||
      inet6_addr_to_ip6addr(ip_2_ip6(&ipaddr), &sa6->sin_addr);
 | 
			
		||||
      ipaddr.type = IPADDR_TYPE_V6;
 | 
			
		||||
      port = ntohs(sa6->sin_port);
 | 
			
		||||
#endif  // LWIP_IPV6
 | 
			
		||||
    } else {
 | 
			
		||||
      errno = EAFNOSUPPORT;
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    connecting_ = true;
 | 
			
		||||
    connected_ = false;
 | 
			
		||||
    connect_error_ = false;
 | 
			
		||||
    LWIP_LOG("tcp_connect(%u)", port);
 | 
			
		||||
    err_t err = tcp_connect(pcb_, &ipaddr, port, LWIPRawImpl::s_connected_fn);
 | 
			
		||||
    if (err == ERR_VAL) {
 | 
			
		||||
      errno = EINVAL;
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (err != ERR_OK) {
 | 
			
		||||
      errno = EIO;
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    errno = EINPROGRESS;
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
  int connect_finished() override {
 | 
			
		||||
    if (connected_) {
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (connect_error_) {
 | 
			
		||||
      errno = ECONNREFUSED;
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (connecting_) {
 | 
			
		||||
      errno = EINPROGRESS;
 | 
			
		||||
      return -1;
 | 
			
		||||
    }
 | 
			
		||||
    // no connect started
 | 
			
		||||
    errno = EALREADY;
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int close() override {
 | 
			
		||||
    if (pcb_ == nullptr) {
 | 
			
		||||
      errno = ECONNRESET;
 | 
			
		||||
@@ -369,9 +439,10 @@ class LWIPRawImpl : public Socket {
 | 
			
		||||
    for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
      ssize_t err = read(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
 | 
			
		||||
      if (err == -1) {
 | 
			
		||||
        if (ret != 0)
 | 
			
		||||
        if (ret != 0) {
 | 
			
		||||
          // if we already read some don't return an error
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        return err;
 | 
			
		||||
      }
 | 
			
		||||
      ret += err;
 | 
			
		||||
@@ -433,9 +504,10 @@ class LWIPRawImpl : public Socket {
 | 
			
		||||
    ssize_t written = internal_write(buf, len);
 | 
			
		||||
    if (written == -1)
 | 
			
		||||
      return -1;
 | 
			
		||||
    if (written == 0)
 | 
			
		||||
    if (written == 0) {
 | 
			
		||||
      // no need to output if nothing written
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (nodelay_) {
 | 
			
		||||
      int err = internal_output();
 | 
			
		||||
      if (err == -1)
 | 
			
		||||
@@ -448,18 +520,20 @@ class LWIPRawImpl : public Socket {
 | 
			
		||||
    for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
      ssize_t err = internal_write(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
 | 
			
		||||
      if (err == -1) {
 | 
			
		||||
        if (written != 0)
 | 
			
		||||
        if (written != 0) {
 | 
			
		||||
          // if we already read some don't return an error
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        return err;
 | 
			
		||||
      }
 | 
			
		||||
      written += err;
 | 
			
		||||
      if ((size_t) err != iov[i].iov_len)
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    if (written == 0)
 | 
			
		||||
    if (written == 0) {
 | 
			
		||||
      // no need to output if nothing written
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (nodelay_) {
 | 
			
		||||
      int err = internal_output();
 | 
			
		||||
      if (err == -1)
 | 
			
		||||
@@ -528,6 +602,18 @@ class LWIPRawImpl : public Socket {
 | 
			
		||||
    }
 | 
			
		||||
    return ERR_OK;
 | 
			
		||||
  }
 | 
			
		||||
  err_t connected_fn(err_t err) {
 | 
			
		||||
    LWIP_LOG("connected(err=%d)", err);
 | 
			
		||||
    if (err != ERR_OK) {
 | 
			
		||||
      connected_ = false;
 | 
			
		||||
      connect_error_ = false;
 | 
			
		||||
    } else {
 | 
			
		||||
      connected_ = true;
 | 
			
		||||
      connect_error_ = true;
 | 
			
		||||
    }
 | 
			
		||||
    connecting_ = false;
 | 
			
		||||
    return ERR_OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static err_t s_accept_fn(void *arg, struct tcp_pcb *newpcb, err_t err) {
 | 
			
		||||
    LWIPRawImpl *arg_this = reinterpret_cast<LWIPRawImpl *>(arg);
 | 
			
		||||
@@ -544,6 +630,11 @@ class LWIPRawImpl : public Socket {
 | 
			
		||||
    return arg_this->recv_fn(pb, err);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static err_t s_connected_fn(void *arg, struct tcp_pcb *pcb, err_t err) {
 | 
			
		||||
    LWIPRawImpl *arg_this = reinterpret_cast<LWIPRawImpl *>(arg);
 | 
			
		||||
    return arg_this->connected_fn(err);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  int ip2sockaddr_(ip_addr_t *ip, uint16_t port, struct sockaddr *name, socklen_t *addrlen) {
 | 
			
		||||
    if (family_ == AF_INET) {
 | 
			
		||||
@@ -594,6 +685,9 @@ class LWIPRawImpl : public Socket {
 | 
			
		||||
  // instead use it for determining whether to call lwip_output
 | 
			
		||||
  bool nodelay_ = false;
 | 
			
		||||
  sa_family_t family_ = 0;
 | 
			
		||||
  bool connecting_ = false;
 | 
			
		||||
  bool connected_ = false;
 | 
			
		||||
  bool connect_error_ = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Socket> socket(int domain, int type, int protocol) {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ namespace socket {
 | 
			
		||||
Socket::~Socket() {}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Socket> socket_ip(int type, int protocol) {
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
#ifdef USE_SOCKET_IPV6
 | 
			
		||||
  return socket(AF_INET6, type, protocol);
 | 
			
		||||
#else
 | 
			
		||||
  return socket(AF_INET, type, protocol);
 | 
			
		||||
@@ -51,7 +51,7 @@ socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::stri
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port) {
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
#if USE_SOCKET_IPV6
 | 
			
		||||
  if (addrlen < sizeof(sockaddr_in6)) {
 | 
			
		||||
    errno = EINVAL;
 | 
			
		||||
    return 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,12 @@
 | 
			
		||||
#include "esphome/core/optional.h"
 | 
			
		||||
#include "headers.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SOCKET_IMPL_LWIP_TCP
 | 
			
		||||
#if LWIP_IPV6
 | 
			
		||||
#define USE_SOCKET_IPV6
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace socket {
 | 
			
		||||
 | 
			
		||||
@@ -17,10 +23,17 @@ class Socket {
 | 
			
		||||
 | 
			
		||||
  virtual std::unique_ptr<Socket> accept(struct sockaddr *addr, socklen_t *addrlen) = 0;
 | 
			
		||||
  virtual int bind(const struct sockaddr *addr, socklen_t addrlen) = 0;
 | 
			
		||||
  virtual int connect(const struct sockaddr *addr, socklen_t addrlen) = 0;
 | 
			
		||||
  /**
 | 
			
		||||
   * @brief Helper to check if a socket connect() that was EINPROGRESS is now finished.
 | 
			
		||||
   *
 | 
			
		||||
   * If the connect finnished successfully, returns 0.
 | 
			
		||||
   * If it's still in progress, returns -1 and sets errno to EINPROGRESS.
 | 
			
		||||
   * Other errors result in return code -1 and errno like in blocking connect().
 | 
			
		||||
   */
 | 
			
		||||
  virtual int connect_finished() = 0;
 | 
			
		||||
 | 
			
		||||
  virtual int close() = 0;
 | 
			
		||||
  // not supported yet:
 | 
			
		||||
  // virtual int connect(const std::string &address) = 0;
 | 
			
		||||
  // virtual int connect(const struct sockaddr *addr, socklen_t addrlen) = 0;
 | 
			
		||||
  virtual int shutdown(int how) = 0;
 | 
			
		||||
 | 
			
		||||
  virtual int getpeername(struct sockaddr *addr, socklen_t *addrlen) = 0;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										99
									
								
								esphome/components/socket/thread_getaddrinfo_impl.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								esphome/components/socket/thread_getaddrinfo_impl.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
			
		||||
#include "getaddrinfo.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
 | 
			
		||||
#ifndef USE_SOCKET_HAS_LWIP
 | 
			
		||||
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <netdb.h>
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace socket {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "socket.threadgetaddrinfo";
 | 
			
		||||
 | 
			
		||||
struct ThreadGetaddrinfoResult {
 | 
			
		||||
  bool completed;
 | 
			
		||||
  int return_code;
 | 
			
		||||
  struct addrinfo *res;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ThreadGetaddrinfoFuture : public GetaddrinfoFuture {
 | 
			
		||||
 public:
 | 
			
		||||
  ThreadGetaddrinfoFuture(std::shared_ptr<ThreadGetaddrinfoResult> result) : result_(result) {}
 | 
			
		||||
  ~ThreadGetaddrinfoFuture() override = default;
 | 
			
		||||
 | 
			
		||||
  bool completed() override { return result_->completed; }
 | 
			
		||||
  int fetch_result(struct addrinfo **res) {
 | 
			
		||||
    if (res == nullptr)
 | 
			
		||||
      return EAI_FAIL;
 | 
			
		||||
    *res = nullptr;
 | 
			
		||||
    if (!result_->completed)
 | 
			
		||||
      return EAI_FAIL;
 | 
			
		||||
    if (result_->return_code != 0)
 | 
			
		||||
      return result_->return_code;
 | 
			
		||||
 | 
			
		||||
    *res = result_->res;
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::shared_ptr<ThreadGetaddrinfoResult> result_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void worker(std::shared_ptr<ThreadGetaddrinfoResult> result, const char *node, const char *service,
 | 
			
		||||
            const struct addrinfo *hints) {
 | 
			
		||||
  result->return_code = getaddrinfo(node, service, hints, &result->res);
 | 
			
		||||
  result->completed = true;
 | 
			
		||||
  if (hints != nullptr) {
 | 
			
		||||
    delete hints->ai_addr;
 | 
			
		||||
    delete hints->ai_canonname;
 | 
			
		||||
    delete hints;
 | 
			
		||||
  }
 | 
			
		||||
  delete node;
 | 
			
		||||
  delete service;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<GetaddrinfoFuture> getaddrinfo_async(const char *node, const char *service,
 | 
			
		||||
                                                     const struct addrinfo *hints) {
 | 
			
		||||
  std::shared_ptr<ThreadGetaddrinfoResult> result = std::make_shared<ThreadGetaddrinfoResult>();
 | 
			
		||||
  result->completed = false;
 | 
			
		||||
 | 
			
		||||
  struct addrinfo *hints_copy = nullptr;
 | 
			
		||||
  if (hints != nullptr) {
 | 
			
		||||
    hints_copy = new struct addrinfo;
 | 
			
		||||
    hints_copy->ai_flags = hints->ai_flags;
 | 
			
		||||
    hints_copy->ai_family = hints->ai_family;
 | 
			
		||||
    hints_copy->ai_socktype = hints->ai_socktype;
 | 
			
		||||
    hints_copy->ai_protocol = hints->ai_protocol;
 | 
			
		||||
    hints_copy->ai_addrlen = hints->ai_addrlen;
 | 
			
		||||
    if (ai->ai_addr != nullptr) {
 | 
			
		||||
      hints_copy->ai_addr = malloc(hints->ai_addrlen);
 | 
			
		||||
      memcpy(hints_copy->ai_addr, hints->ai_addr, hints->ai_addrlen);
 | 
			
		||||
    }
 | 
			
		||||
    if (ai->ai_canonname != nullptr) {
 | 
			
		||||
      hints_copy->ai_canonname = strdup(hints->ai_canonname);
 | 
			
		||||
    }
 | 
			
		||||
    hints_copy->ai_next = nullptr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const char *node_copy = nullptr, *service_copy = nullptr;
 | 
			
		||||
  if (node != nullptr)
 | 
			
		||||
    node_copy = strdup(node);
 | 
			
		||||
  if (service != nullptr)
 | 
			
		||||
    service_copy = strdup(service);
 | 
			
		||||
 | 
			
		||||
  std::thread thread(worker, result, node_copy, service_copy, hints_copy);
 | 
			
		||||
  thread.detach();
 | 
			
		||||
 | 
			
		||||
  return std::unique_ptr<GetaddrinfoFuture>{new ThreadGetaddrinfoFuture(result)};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace socket
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // !USE_SOCKET_HAS_LWIP
 | 
			
		||||
@@ -85,6 +85,7 @@
 | 
			
		||||
#ifdef USE_ESP_IDF
 | 
			
		||||
#define USE_ESP_IDF_VERSION_CODE VERSION_CODE(4, 4, 2)
 | 
			
		||||
#endif
 | 
			
		||||
#define USE_SOCKET_HAS_LWIP
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// ESP8266-specific feature flags
 | 
			
		||||
@@ -94,6 +95,7 @@
 | 
			
		||||
#define USE_ESP8266_PREFERENCES_FLASH
 | 
			
		||||
#define USE_HTTP_REQUEST_ESP8266_HTTPS
 | 
			
		||||
#define USE_SOCKET_IMPL_LWIP_TCP
 | 
			
		||||
#define USE_SOCKET_HAS_LWIP
 | 
			
		||||
 | 
			
		||||
// Dummy firmware payload for shelly_dimmer
 | 
			
		||||
#define USE_SHD_FIRMWARE_MAJOR_VERSION 56
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user