ESPHome 2026.3.0-dev
Loading...
Searching...
No Matches
lwip_raw_tcp_impl.cpp
Go to the documentation of this file.
1#include "socket.h"
3
4#ifdef USE_SOCKET_IMPL_LWIP_TCP
5
6#include <cerrno>
7#include <cstring>
8
10#include "esphome/core/log.h"
11
12#ifdef USE_ESP8266
13#include <coredecls.h> // For esp_schedule()
14#endif
15
16namespace esphome::socket {
17
18#ifdef USE_ESP8266
19// Flag to signal socket activity - checked by socket_delay() to exit early
20// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
21static volatile bool s_socket_woke = false;
22
23void socket_delay(uint32_t ms) {
24 // Use esp_delay with a callback that checks if socket data arrived.
25 // This allows the delay to exit early when socket_wake() is called by
26 // lwip recv_fn/accept_fn callbacks, reducing socket latency.
27 //
28 // When ms is 0, we must use delay(0) because esp_delay(0, callback)
29 // exits immediately without yielding, which can cause watchdog timeouts
30 // when the main loop runs in high-frequency mode (e.g., during light effects).
31 if (ms == 0) {
32 delay(0);
33 return;
34 }
35 s_socket_woke = false;
36 esp_delay(ms, []() { return !s_socket_woke; });
37}
38
39void IRAM_ATTR socket_wake() {
40 s_socket_woke = true;
41 esp_schedule();
42}
43#endif
44
45static const char *const TAG = "socket.lwip";
46
47// set to 1 to enable verbose lwip logging
48#if 0 // NOLINT(readability-avoid-unconditional-preprocessor-if)
49#define LWIP_LOG(msg, ...) ESP_LOGVV(TAG, "socket %p: " msg, this, ##__VA_ARGS__)
50#else
51#define LWIP_LOG(msg, ...)
52#endif
53
54// ---- LWIPRawCommon methods ----
55
57 if (this->pcb_ != nullptr) {
58 LWIP_LOG("tcp_abort(%p)", this->pcb_);
59 tcp_abort(this->pcb_);
60 this->pcb_ = nullptr;
61 }
62}
63
64int LWIPRawCommon::bind(const struct sockaddr *name, socklen_t addrlen) {
65 if (this->pcb_ == nullptr) {
66 errno = EBADF;
67 return -1;
68 }
69 if (name == nullptr) {
70 errno = EINVAL;
71 return -1;
72 }
73 ip_addr_t ip;
74 in_port_t port;
75#if LWIP_IPV6
76 if (this->family_ == AF_INET) {
77 if (addrlen < sizeof(sockaddr_in)) {
78 errno = EINVAL;
79 return -1;
80 }
81 auto *addr4 = reinterpret_cast<const sockaddr_in *>(name);
82 port = ntohs(addr4->sin_port);
83 ip.type = IPADDR_TYPE_V4;
84 ip.u_addr.ip4.addr = addr4->sin_addr.s_addr;
85 LWIP_LOG("tcp_bind(%p ip=%s port=%u)", this->pcb_, ip4addr_ntoa(&ip.u_addr.ip4), port);
86 } else if (this->family_ == AF_INET6) {
87 if (addrlen < sizeof(sockaddr_in6)) {
88 errno = EINVAL;
89 return -1;
90 }
91 auto *addr6 = reinterpret_cast<const sockaddr_in6 *>(name);
92 port = ntohs(addr6->sin6_port);
93 ip.type = IPADDR_TYPE_ANY;
94 memcpy(&ip.u_addr.ip6.addr, &addr6->sin6_addr.un.u8_addr, 16);
95 LWIP_LOG("tcp_bind(%p ip=%s port=%u)", this->pcb_, ip6addr_ntoa(&ip.u_addr.ip6), port);
96 } else {
97 errno = EINVAL;
98 return -1;
99 }
100#else
101 if (this->family_ != AF_INET) {
102 errno = EINVAL;
103 return -1;
104 }
105 auto *addr4 = reinterpret_cast<const sockaddr_in *>(name);
106 port = ntohs(addr4->sin_port);
107 ip.addr = addr4->sin_addr.s_addr;
108 LWIP_LOG("tcp_bind(%p ip=%u port=%u)", this->pcb_, ip.addr, port);
109#endif
110 err_t err = tcp_bind(this->pcb_, &ip, port);
111 if (err == ERR_USE) {
112 LWIP_LOG(" -> err ERR_USE");
113 errno = EADDRINUSE;
114 return -1;
115 }
116 if (err == ERR_VAL) {
117 LWIP_LOG(" -> err ERR_VAL");
118 errno = EINVAL;
119 return -1;
120 }
121 if (err != ERR_OK) {
122 LWIP_LOG(" -> err %d", err);
123 errno = EIO;
124 return -1;
125 }
126 return 0;
127}
128
130 if (this->pcb_ == nullptr) {
131 errno = ECONNRESET;
132 return -1;
133 }
134 LWIP_LOG("tcp_close(%p)", this->pcb_);
135 err_t err = tcp_close(this->pcb_);
136 if (err != ERR_OK) {
137 LWIP_LOG(" -> err %d", err);
138 tcp_abort(this->pcb_);
139 this->pcb_ = nullptr;
140 errno = err == ERR_MEM ? ENOMEM : EIO;
141 return -1;
142 }
143 this->pcb_ = nullptr;
144 return 0;
145}
146
148 if (this->pcb_ == nullptr) {
149 errno = ECONNRESET;
150 return -1;
151 }
152 bool shut_rx = false, shut_tx = false;
153 if (how == SHUT_RD) {
154 shut_rx = true;
155 } else if (how == SHUT_WR) {
156 shut_tx = true;
157 } else if (how == SHUT_RDWR) {
158 shut_rx = shut_tx = true;
159 } else {
160 errno = EINVAL;
161 return -1;
162 }
163 LWIP_LOG("tcp_shutdown(%p shut_rx=%d shut_tx=%d)", this->pcb_, shut_rx ? 1 : 0, shut_tx ? 1 : 0);
164 err_t err = tcp_shutdown(this->pcb_, shut_rx, shut_tx);
165 if (err != ERR_OK) {
166 LWIP_LOG(" -> err %d", err);
167 errno = err == ERR_MEM ? ENOMEM : EIO;
168 return -1;
169 }
170 return 0;
171}
172
173int LWIPRawCommon::getpeername(struct sockaddr *name, socklen_t *addrlen) {
174 if (this->pcb_ == nullptr) {
175 errno = ECONNRESET;
176 return -1;
177 }
178 if (name == nullptr || addrlen == nullptr) {
179 errno = EINVAL;
180 return -1;
181 }
182 return this->ip2sockaddr_(&this->pcb_->remote_ip, this->pcb_->remote_port, name, addrlen);
183}
184
185int LWIPRawCommon::getsockname(struct sockaddr *name, socklen_t *addrlen) {
186 if (this->pcb_ == nullptr) {
187 errno = ECONNRESET;
188 return -1;
189 }
190 if (name == nullptr || addrlen == nullptr) {
191 errno = EINVAL;
192 return -1;
193 }
194 return this->ip2sockaddr_(&this->pcb_->local_ip, this->pcb_->local_port, name, addrlen);
195}
196
197size_t LWIPRawCommon::getpeername_to(std::span<char, SOCKADDR_STR_LEN> buf) {
198 struct sockaddr_storage storage;
199 socklen_t len = sizeof(storage);
200 if (this->getpeername(reinterpret_cast<struct sockaddr *>(&storage), &len) != 0) {
201 buf[0] = '\0';
202 return 0;
203 }
204 return format_sockaddr_to(reinterpret_cast<struct sockaddr *>(&storage), len, buf);
205}
206
207size_t LWIPRawCommon::getsockname_to(std::span<char, SOCKADDR_STR_LEN> buf) {
208 struct sockaddr_storage storage;
209 socklen_t len = sizeof(storage);
210 if (this->getsockname(reinterpret_cast<struct sockaddr *>(&storage), &len) != 0) {
211 buf[0] = '\0';
212 return 0;
213 }
214 return format_sockaddr_to(reinterpret_cast<struct sockaddr *>(&storage), len, buf);
215}
216
217int LWIPRawCommon::getsockopt(int level, int optname, void *optval, socklen_t *optlen) {
218 if (this->pcb_ == nullptr) {
219 errno = ECONNRESET;
220 return -1;
221 }
222 if (optlen == nullptr || optval == nullptr) {
223 errno = EINVAL;
224 return -1;
225 }
226 if (level == SOL_SOCKET && optname == SO_REUSEADDR) {
227 if (*optlen < 4) {
228 errno = EINVAL;
229 return -1;
230 }
231 // lwip doesn't seem to have this feature. Don't send an error
232 // to prevent warnings
233 *reinterpret_cast<int *>(optval) = 1;
234 *optlen = 4;
235 return 0;
236 }
237 if (level == IPPROTO_TCP && optname == TCP_NODELAY) {
238 if (*optlen < 4) {
239 errno = EINVAL;
240 return -1;
241 }
242 *reinterpret_cast<int *>(optval) = this->nodelay_;
243 *optlen = 4;
244 return 0;
245 }
246
247 errno = EINVAL;
248 return -1;
249}
250
251int LWIPRawCommon::setsockopt(int level, int optname, const void *optval, socklen_t optlen) {
252 if (this->pcb_ == nullptr) {
253 errno = ECONNRESET;
254 return -1;
255 }
256 if (level == SOL_SOCKET && optname == SO_REUSEADDR) {
257 if (optlen != 4) {
258 errno = EINVAL;
259 return -1;
260 }
261 // lwip doesn't seem to have this feature. Don't send an error
262 // to prevent warnings
263 return 0;
264 }
265 if (level == IPPROTO_TCP && optname == TCP_NODELAY) {
266 if (optlen != 4) {
267 errno = EINVAL;
268 return -1;
269 }
270 int val = *reinterpret_cast<const int *>(optval);
271 this->nodelay_ = val;
272 return 0;
273 }
274
275 errno = EINVAL;
276 return -1;
277}
278
279int LWIPRawCommon::ip2sockaddr_(ip_addr_t *ip, uint16_t port, struct sockaddr *name, socklen_t *addrlen) {
280 if (this->family_ == AF_INET) {
281 if (*addrlen < sizeof(struct sockaddr_in)) {
282 errno = EINVAL;
283 return -1;
284 }
285
286 struct sockaddr_in *addr = reinterpret_cast<struct sockaddr_in *>(name);
287 addr->sin_family = AF_INET;
288 *addrlen = addr->sin_len = sizeof(struct sockaddr_in);
289 addr->sin_port = port;
290 inet_addr_from_ip4addr(&addr->sin_addr, ip_2_ip4(ip));
291 return 0;
292 }
293#if LWIP_IPV6
294 else if (this->family_ == AF_INET6) {
295 if (*addrlen < sizeof(struct sockaddr_in6)) {
296 errno = EINVAL;
297 return -1;
298 }
299
300 struct sockaddr_in6 *addr = reinterpret_cast<struct sockaddr_in6 *>(name);
301 addr->sin6_family = AF_INET6;
302 *addrlen = addr->sin6_len = sizeof(struct sockaddr_in6);
303 addr->sin6_port = port;
304
305 // AF_INET6 sockets are bound to IPv4 as well, so we may encounter IPv4 addresses that must be converted to IPv6.
306 if (IP_IS_V4(ip)) {
307 ip_addr_t mapped;
308 ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&mapped), ip_2_ip4(ip));
309 inet6_addr_from_ip6addr(&addr->sin6_addr, ip_2_ip6(&mapped));
310 } else {
311 inet6_addr_from_ip6addr(&addr->sin6_addr, ip_2_ip6(ip));
312 }
313 return 0;
314 }
315#endif
316 return -1;
317}
318
319// ---- LWIPRawImpl methods ----
320
322 // Free any received pbufs that LWIP transferred ownership of via recv_fn.
323 // tcp_abort() in the base destructor won't free these since LWIP considers
324 // ownership transferred once the recv callback accepts them.
325 if (this->rx_buf_ != nullptr) {
326 pbuf_free(this->rx_buf_);
327 this->rx_buf_ = nullptr;
328 }
329 // Base class destructor handles pcb_ cleanup via tcp_abort
330}
331
333 LWIP_LOG("init(%p)", this->pcb_);
334 tcp_arg(this->pcb_, this);
335 tcp_recv(this->pcb_, LWIPRawImpl::s_recv_fn);
336 tcp_err(this->pcb_, LWIPRawImpl::s_err_fn);
337}
338
339void LWIPRawImpl::s_err_fn(void *arg, err_t err) {
340 // "If a connection is aborted because of an error, the application is alerted of this event by
341 // the err callback."
342 // pcb is already freed when this callback is called
343 // ERR_RST: connection was reset by remote host
344 // ERR_ABRT: aborted through tcp_abort or TCP timer
345 auto *arg_this = reinterpret_cast<LWIPRawImpl *>(arg);
346 ESP_LOGVV(TAG, "socket %p: err(err=%d)", arg_this, err);
347 arg_this->pcb_ = nullptr;
348}
349
350err_t LWIPRawImpl::s_recv_fn(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, err_t err) {
351 auto *arg_this = reinterpret_cast<LWIPRawImpl *>(arg);
352 return arg_this->recv_fn(pb, err);
353}
354
355err_t LWIPRawImpl::recv_fn(struct pbuf *pb, err_t err) {
356 LWIP_LOG("recv(pb=%p err=%d)", pb, err);
357 if (err != 0) {
358 // "An error code if there has been an error receiving Only return ERR_ABRT if you have
359 // called tcp_abort from within the callback function!"
360 this->rx_closed_ = true;
361 return ERR_OK;
362 }
363 if (pb == nullptr) {
364 this->rx_closed_ = true;
365 return ERR_OK;
366 }
367 if (this->rx_buf_ == nullptr) {
368 // no need to copy because lwIP gave control of it to us
369 this->rx_buf_ = pb;
370 this->rx_buf_offset_ = 0;
371 } else {
372 pbuf_cat(this->rx_buf_, pb);
373 }
374#ifdef USE_ESP8266
375 // Wake the main loop immediately so it can process the received data.
376 socket_wake();
377#endif
378 return ERR_OK;
379}
380
381ssize_t LWIPRawImpl::read(void *buf, size_t len) {
382 if (this->pcb_ == nullptr) {
383 errno = ECONNRESET;
384 return -1;
385 }
386 if (this->rx_closed_ && this->rx_buf_ == nullptr) {
387 return 0;
388 }
389 if (len == 0) {
390 return 0;
391 }
392 if (this->rx_buf_ == nullptr) {
393 errno = EWOULDBLOCK;
394 return -1;
395 }
396
397 size_t read = 0;
398 uint8_t *buf8 = reinterpret_cast<uint8_t *>(buf);
399 while (len && this->rx_buf_ != nullptr) {
400 size_t pb_len = this->rx_buf_->len;
401 size_t pb_left = pb_len - this->rx_buf_offset_;
402 if (pb_left == 0)
403 break;
404 size_t copysize = std::min(len, pb_left);
405 memcpy(buf8, reinterpret_cast<uint8_t *>(this->rx_buf_->payload) + this->rx_buf_offset_, copysize);
406
407 if (pb_left == copysize) {
408 // full pb copied, free it
409 if (this->rx_buf_->next == nullptr) {
410 // last buffer in chain
411 pbuf_free(this->rx_buf_);
412 this->rx_buf_ = nullptr;
413 this->rx_buf_offset_ = 0;
414 } else {
415 auto *old_buf = this->rx_buf_;
416 this->rx_buf_ = this->rx_buf_->next;
417 pbuf_ref(this->rx_buf_);
418 pbuf_free(old_buf);
419 this->rx_buf_offset_ = 0;
420 }
421 } else {
422 this->rx_buf_offset_ += copysize;
423 }
424 LWIP_LOG("tcp_recved(%p %u)", this->pcb_, copysize);
425 tcp_recved(this->pcb_, copysize);
426
427 buf8 += copysize;
428 len -= copysize;
429 read += copysize;
430 }
431
432 if (read == 0) {
433 errno = EWOULDBLOCK;
434 return -1;
435 }
436
437 return read;
438}
439
440ssize_t LWIPRawImpl::readv(const struct iovec *iov, int iovcnt) {
441 ssize_t ret = 0;
442 for (int i = 0; i < iovcnt; i++) {
443 ssize_t err = this->read(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
444 if (err == -1) {
445 if (ret != 0) {
446 // if we already read some don't return an error
447 break;
448 }
449 return err;
450 }
451 ret += err;
452 if ((size_t) err != iov[i].iov_len)
453 break;
454 }
455 return ret;
456}
457
458ssize_t LWIPRawImpl::internal_write_(const void *buf, size_t len) {
459 if (this->pcb_ == nullptr) {
460 errno = ECONNRESET;
461 return -1;
462 }
463 if (len == 0)
464 return 0;
465 if (buf == nullptr) {
466 errno = EINVAL;
467 return 0;
468 }
469 auto space = tcp_sndbuf(this->pcb_);
470 if (space == 0) {
471 errno = EWOULDBLOCK;
472 return -1;
473 }
474 size_t to_send = std::min((size_t) space, len);
475 LWIP_LOG("tcp_write(%p buf=%p %u)", this->pcb_, buf, to_send);
476 err_t err = tcp_write(this->pcb_, buf, to_send, TCP_WRITE_FLAG_COPY);
477 if (err == ERR_MEM) {
478 LWIP_LOG(" -> err ERR_MEM");
479 errno = EWOULDBLOCK;
480 return -1;
481 }
482 if (err != ERR_OK) {
483 LWIP_LOG(" -> err %d", err);
484 errno = ECONNRESET;
485 return -1;
486 }
487 return to_send;
488}
489
491 LWIP_LOG("tcp_output(%p)", this->pcb_);
492 err_t err = tcp_output(this->pcb_);
493 if (err == ERR_ABRT) {
494 // sometimes lwip returns ERR_ABRT for no apparent reason
495 // the connection works fine afterwards, and back with ESPAsyncTCP we
496 // indirectly also ignored this error
497 // FIXME: figure out where this is returned and what it means in this context
498 LWIP_LOG(" -> err ERR_ABRT");
499 return 0;
500 }
501 if (err != ERR_OK) {
502 LWIP_LOG(" -> err %d", err);
503 errno = ECONNRESET;
504 return -1;
505 }
506 return 0;
507}
508
509ssize_t LWIPRawImpl::write(const void *buf, size_t len) {
510 ssize_t written = this->internal_write_(buf, len);
511 if (written == -1)
512 return -1;
513 if (written == 0) {
514 // no need to output if nothing written
515 return 0;
516 }
517 if (this->nodelay_) {
518 int err = this->internal_output_();
519 if (err == -1)
520 return -1;
521 }
522 return written;
523}
524
525ssize_t LWIPRawImpl::writev(const struct iovec *iov, int iovcnt) {
526 ssize_t written = 0;
527 for (int i = 0; i < iovcnt; i++) {
528 ssize_t err = this->internal_write_(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
529 if (err == -1) {
530 if (written != 0) {
531 // if we already read some don't return an error
532 break;
533 }
534 return err;
535 }
536 written += err;
537 if ((size_t) err != iov[i].iov_len)
538 break;
539 }
540 if (written == 0) {
541 // no need to output if nothing written
542 return 0;
543 }
544 if (this->nodelay_) {
545 int err = this->internal_output_();
546 if (err == -1)
547 return -1;
548 }
549 return written;
550}
551
552// ---- LWIPRawListenImpl methods ----
553
555 // Listen PCBs must use tcp_close(), not tcp_abort().
556 // tcp_abandon() asserts pcb->state != LISTEN and would access
557 // fields that don't exist in the smaller tcp_pcb_listen struct.
558 // Close here and null pcb_ so the base destructor skips tcp_abort.
559 if (this->pcb_ != nullptr) {
560 tcp_close(this->pcb_);
561 this->pcb_ = nullptr;
562 }
563}
564
566 LWIP_LOG("init(%p)", this->pcb_);
567 tcp_arg(this->pcb_, this);
568 tcp_accept(this->pcb_, LWIPRawListenImpl::s_accept_fn);
569 tcp_err(this->pcb_, LWIPRawListenImpl::s_err_fn);
570}
571
572void LWIPRawListenImpl::s_err_fn(void *arg, err_t err) {
573 auto *arg_this = reinterpret_cast<LWIPRawListenImpl *>(arg);
574 ESP_LOGVV(TAG, "socket %p: err(err=%d)", arg_this, err);
575 arg_this->pcb_ = nullptr;
576}
577
578err_t LWIPRawListenImpl::s_accept_fn(void *arg, struct tcp_pcb *newpcb, err_t err) {
579 auto *arg_this = reinterpret_cast<LWIPRawListenImpl *>(arg);
580 return arg_this->accept_fn_(newpcb, err);
581}
582
583std::unique_ptr<LWIPRawImpl> LWIPRawListenImpl::accept(struct sockaddr *addr, socklen_t *addrlen) {
584 if (this->pcb_ == nullptr) {
585 errno = EBADF;
586 return nullptr;
587 }
588 if (this->accepted_socket_count_ == 0) {
589 errno = EWOULDBLOCK;
590 return nullptr;
591 }
592 // Take from front for FIFO ordering
593 std::unique_ptr<LWIPRawImpl> sock = std::move(this->accepted_sockets_[0]);
594 // Shift remaining sockets forward
595 for (uint8_t i = 1; i < this->accepted_socket_count_; i++) {
596 this->accepted_sockets_[i - 1] = std::move(this->accepted_sockets_[i]);
597 }
598 this->accepted_socket_count_--;
599 LWIP_LOG("Connection accepted by application, queue size: %d", this->accepted_socket_count_);
600 if (addr != nullptr) {
601 sock->getpeername(addr, addrlen);
602 }
603 LWIP_LOG("accept(%p)", sock.get());
604 return sock;
605}
606
608 if (this->pcb_ == nullptr) {
609 errno = EBADF;
610 return -1;
611 }
612 LWIP_LOG("tcp_listen_with_backlog(%p backlog=%d)", this->pcb_, backlog);
613 struct tcp_pcb *listen_pcb = tcp_listen_with_backlog(this->pcb_, backlog);
614 if (listen_pcb == nullptr) {
615 tcp_abort(this->pcb_);
616 this->pcb_ = nullptr;
617 errno = EOPNOTSUPP;
618 return -1;
619 }
620 // tcp_listen reallocates the pcb, replace ours
621 this->pcb_ = listen_pcb;
622 // set callbacks on new pcb
623 LWIP_LOG("tcp_arg(%p)", this->pcb_);
624 tcp_arg(this->pcb_, this);
625 tcp_accept(this->pcb_, LWIPRawListenImpl::s_accept_fn);
626 // Note: tcp_err() is NOT re-registered here. tcp_listen_with_backlog() converts the
627 // full tcp_pcb to a smaller tcp_pcb_listen struct that lacks the errf field.
628 // Calling tcp_err() on a listen PCB writes past the struct boundary (undefined behavior).
629 return 0;
630}
631
632err_t LWIPRawListenImpl::accept_fn_(struct tcp_pcb *newpcb, err_t err) {
633 LWIP_LOG("accept(newpcb=%p err=%d)", newpcb, err);
634 if (err != ERR_OK || newpcb == nullptr) {
635 // "An error code if there has been an error accepting. Only return ERR_ABRT if you have
636 // called tcp_abort from within the callback function!"
637 // https://www.nongnu.org/lwip/2_1_x/tcp_8h.html#a00517abce6856d6c82f0efebdafb734d
638 // nothing to do here, we just don't push it to the queue
639 return ERR_OK;
640 }
641 // Check if we've reached the maximum accept queue size
642 if (this->accepted_socket_count_ >= MAX_ACCEPTED_SOCKETS) {
643 LWIP_LOG("Rejecting connection, queue full (%d)", this->accepted_socket_count_);
644 // Abort the connection when queue is full
645 tcp_abort(newpcb);
646 // Must return ERR_ABRT since we called tcp_abort()
647 return ERR_ABRT;
648 }
649 auto sock = make_unique<LWIPRawImpl>(this->family_, newpcb);
650 sock->init();
651 this->accepted_sockets_[this->accepted_socket_count_++] = std::move(sock);
652 LWIP_LOG("Accepted connection, queue size: %d", this->accepted_socket_count_);
653#ifdef USE_ESP8266
654 // Wake the main loop immediately so it can accept the new connection.
655 socket_wake();
656#endif
657 return ERR_OK;
658}
659
660// ---- Factory functions ----
661
662std::unique_ptr<Socket> socket(int domain, int type, int protocol) {
663 if (type != SOCK_STREAM) {
664 ESP_LOGE(TAG, "UDP sockets not supported on this platform, use WiFiUDP");
665 errno = EPROTOTYPE;
666 return nullptr;
667 }
668 auto *pcb = tcp_new();
669 if (pcb == nullptr)
670 return nullptr;
671 auto *sock = new LWIPRawImpl((sa_family_t) domain, pcb); // NOLINT(cppcoreguidelines-owning-memory)
672 sock->init();
673 return std::unique_ptr<Socket>{sock};
674}
675
676std::unique_ptr<Socket> socket_loop_monitored(int domain, int type, int protocol) {
677 // LWIPRawImpl doesn't use file descriptors, so monitoring is not applicable
678 return socket(domain, type, protocol);
679}
680
681std::unique_ptr<ListenSocket> socket_listen(int domain, int type, int protocol) {
682 if (type != SOCK_STREAM) {
683 ESP_LOGE(TAG, "UDP sockets not supported on this platform, use WiFiUDP");
684 errno = EPROTOTYPE;
685 return nullptr;
686 }
687 auto *pcb = tcp_new();
688 if (pcb == nullptr)
689 return nullptr;
690 auto *sock = new LWIPRawListenImpl((sa_family_t) domain, pcb); // NOLINT(cppcoreguidelines-owning-memory)
691 sock->init();
692 return std::unique_ptr<ListenSocket>{sock};
693}
694
695std::unique_ptr<ListenSocket> socket_listen_loop_monitored(int domain, int type, int protocol) {
696 // LWIPRawImpl doesn't use file descriptors, so monitoring is not applicable
697 return socket_listen(domain, type, protocol);
698}
699
700} // namespace esphome::socket
701
702#endif // USE_SOCKET_IMPL_LWIP_TCP
int getsockname(struct sockaddr *name, socklen_t *addrlen)
size_t getsockname_to(std::span< char, SOCKADDR_STR_LEN > buf)
Format local address into a fixed-size buffer (no heap allocation)
int bind(const struct sockaddr *name, socklen_t addrlen)
int ip2sockaddr_(ip_addr_t *ip, uint16_t port, struct sockaddr *name, socklen_t *addrlen)
int setsockopt(int level, int optname, const void *optval, socklen_t optlen)
int getsockopt(int level, int optname, void *optval, socklen_t *optlen)
int getpeername(struct sockaddr *name, socklen_t *addrlen)
size_t getpeername_to(std::span< char, SOCKADDR_STR_LEN > buf)
Format peer address into a fixed-size buffer (no heap allocation)
Connected socket implementation for LWIP raw TCP.
static err_t s_recv_fn(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, err_t err)
ssize_t readv(const struct iovec *iov, int iovcnt)
static void s_err_fn(void *arg, err_t err)
err_t recv_fn(struct pbuf *pb, err_t err)
ssize_t internal_write_(const void *buf, size_t len)
ssize_t write(const void *buf, size_t len)
ssize_t read(void *buf, size_t len)
ssize_t writev(const struct iovec *iov, int iovcnt)
Listening socket implementation for LWIP raw TCP.
static void s_err_fn(void *arg, err_t err)
std::unique_ptr< LWIPRawImpl > accept(struct sockaddr *addr, socklen_t *addrlen)
uint16_t type
uint16_t in_port_t
Definition headers.h:58
uint32_t socklen_t
Definition headers.h:97
uint8_t sa_family_t
Definition headers.h:57
__int64 ssize_t
Definition httplib.h:178
in_addr ip_addr_t
Definition ip_address.h:22
mopeka_std_values val[4]
size_t format_sockaddr_to(const struct sockaddr *addr_ptr, socklen_t len, std::span< char, SOCKADDR_STR_LEN > buf)
Format sockaddr into caller-provided buffer, returns length written (excluding null)
Definition socket.cpp:53
std::unique_ptr< ListenSocket > socket_listen(int domain, int type, int protocol)
Create a listening socket of the given domain, type and protocol.
std::unique_ptr< ListenSocket > socket_listen_loop_monitored(int domain, int type, int protocol)
Create a listening socket and monitor it for data in the main loop.
void IRAM_ATTR socket_wake()
Signal socket/IO activity and wake the main loop from esp_delay() early.
std::unique_ptr< Socket > socket(int domain, int type, int protocol)
Create a socket of the given domain, type and protocol.
std::unique_ptr< Socket > socket_loop_monitored(int domain, int type, int protocol)
Create a socket and monitor it for data in the main loop.
void socket_delay(uint32_t ms)
Delay that can be woken early by socket activity.
std::string size_t len
Definition helpers.h:817
void HOT delay(uint32_t ms)
Definition core.cpp:27
int written
Definition helpers.h:861
uint8_t sin6_len
Definition headers.h:73
in_port_t sin6_port
Definition headers.h:75
struct in6_addr sin6_addr
Definition headers.h:77
sa_family_t sin6_family
Definition headers.h:74
struct in_addr sin_addr
Definition headers.h:65
uint8_t sin_len
Definition headers.h:62
sa_family_t sin_family
Definition headers.h:63
in_port_t sin_port
Definition headers.h:64