ESPHome  2022.11.3
bsd_sockets_impl.cpp
Go to the documentation of this file.
1 #include "socket.h"
2 #include "esphome/core/defines.h"
3 #include "esphome/core/helpers.h"
4 
5 #ifdef USE_SOCKET_IMPL_BSD_SOCKETS
6 
7 #include <cstring>
8 
9 #ifdef USE_ESP32
10 #include <esp_idf_version.h>
11 #include <lwip/sockets.h>
12 #endif
13 
14 namespace esphome {
15 namespace socket {
16 
17 std::string format_sockaddr(const struct sockaddr_storage &storage) {
18  if (storage.ss_family == AF_INET) {
19  const struct sockaddr_in *addr = reinterpret_cast<const struct sockaddr_in *>(&storage);
20  char buf[INET_ADDRSTRLEN];
21  const char *ret = inet_ntop(AF_INET, &addr->sin_addr, buf, sizeof(buf));
22  if (ret == nullptr)
23  return {};
24  return std::string{buf};
25  } else if (storage.ss_family == AF_INET6) {
26  const struct sockaddr_in6 *addr = reinterpret_cast<const struct sockaddr_in6 *>(&storage);
27  char buf[INET6_ADDRSTRLEN];
28  const char *ret = inet_ntop(AF_INET6, &addr->sin6_addr, buf, sizeof(buf));
29  if (ret == nullptr)
30  return {};
31  return std::string{buf};
32  }
33  return {};
34 }
35 
36 class BSDSocketImpl : public Socket {
37  public:
38  BSDSocketImpl(int fd) : fd_(fd) {}
39  ~BSDSocketImpl() override {
40  if (!closed_) {
41  close(); // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall)
42  }
43  }
44  std::unique_ptr<Socket> accept(struct sockaddr *addr, socklen_t *addrlen) override {
45  int fd = ::accept(fd_, addr, addrlen);
46  if (fd == -1)
47  return {};
48  return make_unique<BSDSocketImpl>(fd);
49  }
50  int bind(const struct sockaddr *addr, socklen_t addrlen) override { return ::bind(fd_, addr, addrlen); }
51  int close() override {
52  int ret = ::close(fd_);
53  closed_ = true;
54  return ret;
55  }
56  int shutdown(int how) override { return ::shutdown(fd_, how); }
57 
58  int getpeername(struct sockaddr *addr, socklen_t *addrlen) override { return ::getpeername(fd_, addr, addrlen); }
59  std::string getpeername() override {
60  struct sockaddr_storage storage;
61  socklen_t len = sizeof(storage);
62  int err = this->getpeername((struct sockaddr *) &storage, &len);
63  if (err != 0)
64  return {};
65  return format_sockaddr(storage);
66  }
67  int getsockname(struct sockaddr *addr, socklen_t *addrlen) override { return ::getsockname(fd_, addr, addrlen); }
68  std::string getsockname() override {
69  struct sockaddr_storage storage;
70  socklen_t len = sizeof(storage);
71  int err = this->getsockname((struct sockaddr *) &storage, &len);
72  if (err != 0)
73  return {};
74  return format_sockaddr(storage);
75  }
76  int getsockopt(int level, int optname, void *optval, socklen_t *optlen) override {
77  return ::getsockopt(fd_, level, optname, optval, optlen);
78  }
79  int setsockopt(int level, int optname, const void *optval, socklen_t optlen) override {
80  return ::setsockopt(fd_, level, optname, optval, optlen);
81  }
82  int listen(int backlog) override { return ::listen(fd_, backlog); }
83  ssize_t read(void *buf, size_t len) override { return ::read(fd_, buf, len); }
84  ssize_t readv(const struct iovec *iov, int iovcnt) override {
85 #if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 4
86  // esp-idf v3 doesn't have readv, emulate it
87  ssize_t ret = 0;
88  for (int i = 0; i < iovcnt; i++) {
89  ssize_t err = this->read(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
90  if (err == -1) {
91  if (ret != 0) {
92  // if we already read some don't return an error
93  break;
94  }
95  return err;
96  }
97  ret += err;
98  if (err != iov[i].iov_len)
99  break;
100  }
101  return ret;
102 #elif defined(USE_ESP32)
103  // ESP-IDF v4 only has symbol lwip_readv
104  return ::lwip_readv(fd_, iov, iovcnt);
105 #else
106  return ::readv(fd_, iov, iovcnt);
107 #endif
108  }
109  ssize_t write(const void *buf, size_t len) override { return ::write(fd_, buf, len); }
110  ssize_t send(void *buf, size_t len, int flags) { return ::send(fd_, buf, len, flags); }
111  ssize_t writev(const struct iovec *iov, int iovcnt) override {
112 #if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 4
113  // esp-idf v3 doesn't have writev, emulate it
114  ssize_t ret = 0;
115  for (int i = 0; i < iovcnt; i++) {
116  ssize_t err =
117  this->send(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len, i == iovcnt - 1 ? 0 : MSG_MORE);
118  if (err == -1) {
119  if (ret != 0) {
120  // if we already wrote some don't return an error
121  break;
122  }
123  return err;
124  }
125  ret += err;
126  if (err != iov[i].iov_len)
127  break;
128  }
129  return ret;
130 #elif defined(USE_ESP32)
131  // ESP-IDF v4 only has symbol lwip_writev
132  return ::lwip_writev(fd_, iov, iovcnt);
133 #else
134  return ::writev(fd_, iov, iovcnt);
135 #endif
136  }
137  int setblocking(bool blocking) override {
138  int fl = ::fcntl(fd_, F_GETFL, 0);
139  if (blocking) {
140  fl &= ~O_NONBLOCK;
141  } else {
142  fl |= O_NONBLOCK;
143  }
144  ::fcntl(fd_, F_SETFL, fl);
145  return 0;
146  }
147 
148  protected:
149  int fd_;
150  bool closed_ = false;
151 };
152 
153 std::unique_ptr<Socket> socket(int domain, int type, int protocol) {
154  int ret = ::socket(domain, type, protocol);
155  if (ret == -1)
156  return nullptr;
157  return std::unique_ptr<Socket>{new BSDSocketImpl(ret)};
158 }
159 
160 } // namespace socket
161 } // namespace esphome
162 
163 #endif // USE_SOCKET_IMPL_BSD_SOCKETS
sa_family_t ss_family
Definition: headers.h:81
uint32_t socklen_t
Definition: headers.h:86
uint8_t type
Definition: headers.h:89
const uint32_t flags
Definition: stm32flash.h:85
struct in_addr sin_addr
Definition: headers.h:56
std::string size_t len
Definition: helpers.h:281
Definition: a4988.cpp:4
std::string format_sockaddr(const struct sockaddr_storage &storage)
struct in6_addr sin6_addr
Definition: headers.h:67
std::unique_ptr< Socket > socket(int domain, int type, int protocol)
Create a socket of the given domain, type and protocol.