14#include <sys/socket.h>
18#include <netinet/in.h>
19#include <netinet/tcp.h>
20#ifdef LIBE3_ENABLE_SCTP
21#include <netinet/sctp.h>
38constexpr const char* LOG_TAG =
"PosixConn";
39constexpr const char* IPC_BASE_DIR =
"/tmp/dapps";
40constexpr int POLL_TIMEOUT_MS = 500;
41constexpr int SEND_TIMEOUT_MS = 500;
42constexpr int SETUP_RECV_TIMEOUT_MS = 2000;
50int wait_for_socket(
int sockfd,
int timeout_ms) {
55 return poll(&pfd, 1, timeout_ms);
81int sctp_stream_socket() {
82#ifdef LIBE3_ENABLE_SCTP
83 return socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
85 E3_LOG_ERROR(LOG_TAG) <<
"SCTP transport requested but libe3 was built without it. "
86 "Rebuild with -DLIBE3_ENABLE_SCTP=ON (needs libsctp-dev), "
87 "or use the TCP or IPC transport.";
88 errno = EPROTONOSUPPORT;
97 ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &one,
sizeof(one));
99#ifdef LIBE3_ENABLE_SCTP
100 ret = setsockopt(sockfd, IPPROTO_SCTP, SCTP_NODELAY, &one,
sizeof(one));
107 E3_LOG_WARN(LOG_TAG) <<
"Failed to set NODELAY: " << strerror(errno);
121void set_send_timeout(
int sockfd,
int timeout_ms) {
123 tv.tv_sec = timeout_ms / 1000;
124 tv.tv_usec = (timeout_ms % 1000) * 1000;
125 if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv,
sizeof(tv)) != 0) {
126 E3_LOG_WARN(LOG_TAG) <<
"Failed to set SO_SNDTIMEO: " << strerror(errno);
139void set_recv_timeout(
int sockfd,
int timeout_ms) {
141 tv.tv_sec = timeout_ms / 1000;
142 tv.tv_usec = (timeout_ms % 1000) * 1000;
143 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv,
sizeof(tv)) != 0) {
144 E3_LOG_WARN(LOG_TAG) <<
"Failed to set SO_RCVTIMEO: " << strerror(errno);
157void set_nonblocking(
int sockfd) {
158 int flags = fcntl(sockfd, F_GETFL, 0);
159 if (flags < 0 || fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0) {
160 E3_LOG_WARN(LOG_TAG) <<
"Failed to set O_NONBLOCK: " << strerror(errno);
173 std::vector<int>& peers,
const char* label) {
175 while (listener >= 0 && wait_for_socket(listener, 0) == 1) {
176 int fd = accept(listener,
nullptr,
nullptr);
180 set_nodelay(fd, transport);
181 set_send_timeout(fd, SEND_TIMEOUT_MS);
184 E3_LOG_INFO(LOG_TAG) << label <<
" peer connected ("
185 << peers.size() <<
" total)";
199std::string strip_uri_prefix(
const std::string& endpoint) {
200 auto pos = endpoint.find(
"://");
201 if (pos != std::string::npos) {
202 return endpoint.substr(pos + 3);
214 uint16_t inbound_port,
215 uint16_t outbound_port
218 , setup_connection_socket_(UNUSED_SOCKET)
219 , inbound_connection_socket_(UNUSED_SOCKET)
220 , outbound_connection_socket_(UNUSED_SOCKET)
221 , setup_port_(setup_port)
222 , inbound_port_(inbound_port)
223 , outbound_port_(outbound_port)
229 E3_LOG_INFO(LOG_TAG) <<
"Creating POSIX connector";
233 E3_LOG_DEBUG(LOG_TAG) <<
" Setup port: " << setup_port_;
234 E3_LOG_DEBUG(LOG_TAG) <<
" Inbound port: " << inbound_port_;
235 E3_LOG_DEBUG(LOG_TAG) <<
" Outbound port: " << outbound_port_;
249 if (stat(IPC_BASE_DIR, &st) == -1) {
250 if (mkdir(IPC_BASE_DIR, 0777) == -1 && errno != EEXIST) {
251 E3_LOG_ERROR(LOG_TAG) <<
"Failed to create IPC directory: " << strerror(errno);
252 return ErrorCode::CONNECTION_FAILED;
255 chmod(IPC_BASE_DIR, 0777);
259 sock = sctp_stream_socket();
261 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse,
sizeof(reuse));
262 struct sockaddr_in addr{};
263 addr.sin_family = AF_INET;
264 addr.sin_port = htons(setup_port_);
265 addr.sin_addr.s_addr = INADDR_ANY;
266 ret = bind(sock,
reinterpret_cast<struct sockaddr*
>(&addr),
sizeof(addr));
269 sock = socket(AF_INET, SOCK_STREAM, 0);
271 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse,
sizeof(reuse));
272 struct sockaddr_in addr{};
273 addr.sin_family = AF_INET;
274 addr.sin_port = htons(setup_port_);
275 addr.sin_addr.s_addr = INADDR_ANY;
276 ret = bind(sock,
reinterpret_cast<struct sockaddr*
>(&addr),
sizeof(addr));
279 sock = socket(AF_UNIX, SOCK_STREAM, 0);
280 struct sockaddr_un addr{};
281 addr.sun_family = AF_UNIX;
282 strncpy(addr.sun_path,
setup_endpoint_.c_str(),
sizeof(addr.sun_path) - 1);
284 ret = bind(sock,
reinterpret_cast<struct sockaddr*
>(&addr),
sizeof(addr));
288 E3_LOG_ERROR(LOG_TAG) <<
"Failed to create setup socket: " << strerror(errno);
289 return ErrorCode::CONNECTION_FAILED;
293 E3_LOG_ERROR(LOG_TAG) <<
"Failed to bind setup socket: " << strerror(errno);
295 return ErrorCode::CONNECTION_FAILED;
298 ret = listen(sock, 5);
300 E3_LOG_ERROR(LOG_TAG) <<
"Failed to listen on setup socket: " << strerror(errno);
302 return ErrorCode::CONNECTION_FAILED;
304 set_nonblocking(sock);
306 setup_socket_ = sock;
310 return ErrorCode::SUCCESS;
314 if (setup_socket_ < 0) {
315 return static_cast<int>(ErrorCode::NOT_CONNECTED);
319 int poll_ret = wait_for_socket(setup_socket_, POLL_TIMEOUT_MS);
325 if (errno == EINTR) {
328 E3_LOG_ERROR(LOG_TAG) <<
"Poll failed on setup socket: " << strerror(errno);
329 return static_cast<int>(ErrorCode::TRANSPORT_ERROR);
336 if (setup_connection_socket_ >= 0) {
337 close(setup_connection_socket_);
338 setup_connection_socket_ = -1;
342 setup_connection_socket_ = accept(setup_socket_,
nullptr,
nullptr);
343 if (setup_connection_socket_ < 0) {
344 if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK || shutdown_requested_.load()) {
347 E3_LOG_ERROR(LOG_TAG) <<
"Failed to accept setup connection: " << strerror(errno);
348 return static_cast<int>(ErrorCode::TRANSPORT_ERROR);
350 set_nodelay(setup_connection_socket_, transport_layer_);
353 set_recv_timeout(setup_connection_socket_, SETUP_RECV_TIMEOUT_MS);
356 ssize_t ret = recv(setup_connection_socket_, buffer.data(), buffer.size(), 0);
358 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
362 close(setup_connection_socket_);
363 setup_connection_socket_ = -1;
366 E3_LOG_ERROR(LOG_TAG) <<
"Failed to receive setup request: " << strerror(errno);
367 return static_cast<int>(ErrorCode::TRANSPORT_ERROR);
370 buffer.resize(
static_cast<size_t>(ret));
371 E3_LOG_DEBUG(LOG_TAG) <<
"Received setup request: " << ret <<
" bytes";
372 return static_cast<int>(ret);
376 if (setup_connection_socket_ < 0) {
377 return ErrorCode::NOT_CONNECTED;
380 int ret = send_in_chunks(setup_connection_socket_, data.data(), data.size());
382 return ErrorCode::TRANSPORT_ERROR;
385 E3_LOG_DEBUG(LOG_TAG) <<
"Sent response: " << data.size() <<
" bytes";
386 return ErrorCode::SUCCESS;
396 if (stat(IPC_BASE_DIR, &st) == -1) {
397 if (mkdir(IPC_BASE_DIR, 0777) == -1 && errno != EEXIST) {
398 E3_LOG_ERROR(LOG_TAG) <<
"Failed to create IPC directory: " << strerror(errno);
399 return ErrorCode::CONNECTION_FAILED;
402 chmod(IPC_BASE_DIR, 0777);
406 sock = sctp_stream_socket();
408 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse,
sizeof(reuse));
409 struct sockaddr_in addr{};
410 addr.sin_family = AF_INET;
411 addr.sin_port = htons(inbound_port_);
412 addr.sin_addr.s_addr = INADDR_ANY;
413 ret = bind(sock,
reinterpret_cast<struct sockaddr*
>(&addr),
sizeof(addr));
416 sock = socket(AF_INET, SOCK_STREAM, 0);
418 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse,
sizeof(reuse));
419 struct sockaddr_in addr{};
420 addr.sin_family = AF_INET;
421 addr.sin_port = htons(inbound_port_);
422 addr.sin_addr.s_addr = INADDR_ANY;
423 ret = bind(sock,
reinterpret_cast<struct sockaddr*
>(&addr),
sizeof(addr));
426 sock = socket(AF_UNIX, SOCK_STREAM, 0);
427 struct sockaddr_un addr{};
428 addr.sun_family = AF_UNIX;
431 ret = bind(sock,
reinterpret_cast<struct sockaddr*
>(&addr),
sizeof(addr));
435 E3_LOG_ERROR(LOG_TAG) <<
"Failed to create inbound socket: " << strerror(errno);
436 return ErrorCode::CONNECTION_FAILED;
440 E3_LOG_ERROR(LOG_TAG) <<
"Failed to bind inbound socket: " << strerror(errno);
442 return ErrorCode::CONNECTION_FAILED;
445 ret = listen(sock, 5);
447 E3_LOG_ERROR(LOG_TAG) <<
"Failed to listen on inbound socket: " << strerror(errno);
449 return ErrorCode::CONNECTION_FAILED;
451 set_nonblocking(sock);
453 inbound_socket_ = sock;
458 E3_LOG_INFO(LOG_TAG) <<
"Inbound listener ready (multi-peer)";
459 return ErrorCode::SUCCESS;
467 if (inbound_connection_socket_ < 0) {
468 return static_cast<int>(ErrorCode::NOT_CONNECTED);
470 int poll_ret = wait_for_socket(inbound_connection_socket_, POLL_TIMEOUT_MS);
475 if (errno == EINTR) {
478 E3_LOG_ERROR(LOG_TAG) <<
"Poll failed on receive: " << strerror(errno);
479 return static_cast<int>(ErrorCode::TRANSPORT_ERROR);
481 return recv_with_size(inbound_connection_socket_, buffer);
487 if (inbound_socket_ < 0) {
488 return static_cast<int>(ErrorCode::NOT_CONNECTED);
491 std::vector<struct pollfd> pfds;
492 pfds.reserve(1 + inbound_peer_sockets_.size());
493 pfds.push_back({inbound_socket_, POLLIN, 0});
494 for (
int fd : inbound_peer_sockets_) {
495 pfds.push_back({fd, POLLIN, 0});
498 int poll_ret = poll(pfds.data(), pfds.size(), POLL_TIMEOUT_MS);
503 if (errno == EINTR) {
506 E3_LOG_ERROR(LOG_TAG) <<
"Poll failed on receive: " << strerror(errno);
507 return static_cast<int>(ErrorCode::TRANSPORT_ERROR);
510 if (pfds[0].revents & POLLIN) {
511 drain_accept(inbound_socket_, transport_layer_, inbound_peer_sockets_,
512 "Inbound (subscriber)");
519 const size_t n_peers = pfds.size() - 1;
520 for (
size_t k = 0; k < n_peers; ++k) {
521 const size_t idx = (inbound_rr_ + k) % n_peers;
522 const size_t i = idx + 1;
523 const short re = pfds[i].revents;
524 if (!(re & (POLLIN | POLLHUP | POLLERR | POLLNVAL))) {
527 const int fd = pfds[i].fd;
528 bool broken = (re & (POLLHUP | POLLERR | POLLNVAL)) != 0;
532 ret = recv(fd, buffer.data(), buffer.size(), 0);
535 }
else if (ret < 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
547 buffer.resize(
static_cast<size_t>(ret));
548 E3_LOG_TRACE(LOG_TAG) <<
"Received: " << ret <<
" bytes";
549 inbound_rr_ = idx + 1;
550 return static_cast<int>(ret);
557 inbound_peer_sockets_.erase(
558 std::remove(inbound_peer_sockets_.begin(),
559 inbound_peer_sockets_.end(), fd),
560 inbound_peer_sockets_.end());
561 E3_LOG_INFO(LOG_TAG) <<
"Inbound (subscriber) peer disconnected ("
562 << inbound_peer_sockets_.size() <<
" remain)";
568 inbound_rr_ = (inbound_rr_ + 1) % n_peers;
580 if (stat(IPC_BASE_DIR, &st) == -1) {
581 if (mkdir(IPC_BASE_DIR, 0777) == -1 && errno != EEXIST) {
582 E3_LOG_ERROR(LOG_TAG) <<
"Failed to create IPC directory: " << strerror(errno);
583 return ErrorCode::CONNECTION_FAILED;
586 chmod(IPC_BASE_DIR, 0777);
590 sock = sctp_stream_socket();
591 struct sockaddr_in addr{};
592 addr.sin_family = AF_INET;
593 addr.sin_port = htons(outbound_port_);
594 addr.sin_addr.s_addr = INADDR_ANY;
595 ret = bind(sock,
reinterpret_cast<struct sockaddr*
>(&addr),
sizeof(addr));
598 sock = socket(AF_INET, SOCK_STREAM, 0);
599 struct sockaddr_in addr{};
600 addr.sin_family = AF_INET;
601 addr.sin_port = htons(outbound_port_);
602 addr.sin_addr.s_addr = INADDR_ANY;
603 ret = bind(sock,
reinterpret_cast<struct sockaddr*
>(&addr),
sizeof(addr));
606 sock = socket(AF_UNIX, SOCK_STREAM, 0);
607 struct sockaddr_un addr{};
608 addr.sun_family = AF_UNIX;
611 ret = bind(sock,
reinterpret_cast<struct sockaddr*
>(&addr),
sizeof(addr));
615 E3_LOG_ERROR(LOG_TAG) <<
"Failed to create outbound socket: " << strerror(errno);
616 return ErrorCode::CONNECTION_FAILED;
620 E3_LOG_ERROR(LOG_TAG) <<
"Failed to bind outbound socket: " << strerror(errno);
622 return ErrorCode::CONNECTION_FAILED;
625 ret = listen(sock, 5);
627 E3_LOG_ERROR(LOG_TAG) <<
"Failed to listen on outbound socket: " << strerror(errno);
629 return ErrorCode::CONNECTION_FAILED;
631 set_nonblocking(sock);
633 outbound_socket_ = sock;
638 E3_LOG_INFO(LOG_TAG) <<
"Outbound listener ready (multi-peer)";
639 return ErrorCode::SUCCESS;
650 if (shutdown_requested_.load()) {
651 return ErrorCode::CANCELLED;
657 if (outbound_connection_socket_ < 0) {
658 return ErrorCode::NOT_CONNECTED;
660 ssize_t sent =
::send(outbound_connection_socket_, data.data(), data.size(), 0);
661 if (sent < 0 ||
static_cast<size_t>(sent) != data.size()) {
662 E3_LOG_ERROR(LOG_TAG) <<
"Failed to send dApp outbound: " << strerror(errno);
663 return ErrorCode::TRANSPORT_ERROR;
665 E3_LOG_TRACE(LOG_TAG) <<
"Sent (dApp raw): " << data.size() <<
" bytes";
666 return ErrorCode::SUCCESS;
673 if (outbound_socket_ < 0) {
674 return ErrorCode::NOT_CONNECTED;
676 drain_accept(outbound_socket_, transport_layer_, outbound_peer_sockets_,
677 "Outbound (indication)");
679 bool any_failed =
false;
680 for (
auto it = outbound_peer_sockets_.begin();
681 it != outbound_peer_sockets_.end();) {
682 if (send_in_chunks(*it, data.data(), data.size()) < 0) {
685 it = outbound_peer_sockets_.erase(it);
687 E3_LOG_INFO(LOG_TAG) <<
"Outbound (indication) peer disconnected ("
688 << outbound_peer_sockets_.size() <<
" remain)";
694 if (any_failed && outbound_peer_sockets_.empty()) {
695 return ErrorCode::TRANSPORT_ERROR;
697 E3_LOG_TRACE(LOG_TAG) <<
"Sent: " << data.size() <<
" bytes to "
698 << outbound_peer_sockets_.size() <<
" peer(s)";
699 return ErrorCode::SUCCESS;
717 const std::string& endpoint,
720 int sock = socket(AF_UNIX, SOCK_STREAM, 0);
721 if (sock < 0)
return -1;
722 struct sockaddr_un addr{};
723 addr.sun_family = AF_UNIX;
724 strncpy(addr.sun_path, endpoint.c_str(),
sizeof(addr.sun_path) - 1);
725 if (connect(sock,
reinterpret_cast<struct sockaddr*
>(&addr),
sizeof(addr)) < 0) {
733 sock = sctp_stream_socket();
735 sock = socket(AF_INET, SOCK_STREAM, 0);
737 if (sock < 0)
return -1;
738 struct sockaddr_in addr{};
739 addr.sin_family = AF_INET;
740 addr.sin_port = htons(port);
741 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
742 if (connect(sock,
reinterpret_cast<struct sockaddr*
>(&addr),
sizeof(addr)) < 0) {
746 set_nodelay(sock, transport);
754 for (
int retry = 0; retry < 10 && sock < 0; ++retry) {
755 sock = posix_connect_for(transport_layer_,
setup_endpoint_, setup_port_);
757 if (shutdown_requested_.load())
return ErrorCode::CANCELLED;
758 std::this_thread::sleep_for(std::chrono::milliseconds(200));
762 E3_LOG_ERROR(LOG_TAG) <<
"Failed to connect setup socket: " << strerror(errno);
763 return ErrorCode::CONNECTION_FAILED;
765 setup_connection_socket_ = sock;
767 E3_LOG_INFO(LOG_TAG) <<
"Setup socket connected (client) to "
769 return ErrorCode::SUCCESS;
773 if (setup_connection_socket_ < 0)
return ErrorCode::NOT_CONNECTED;
777 ssize_t sent =
::send(setup_connection_socket_, data.data(), data.size(), 0);
778 if (sent < 0 ||
static_cast<size_t>(sent) != data.size()) {
779 E3_LOG_ERROR(LOG_TAG) <<
"Failed to send setup request: " << strerror(errno);
780 return ErrorCode::TRANSPORT_ERROR;
782 E3_LOG_DEBUG(LOG_TAG) <<
"Sent SetupRequest: " << data.size() <<
" bytes";
783 return ErrorCode::SUCCESS;
787 if (setup_connection_socket_ < 0)
return static_cast<int>(ErrorCode::NOT_CONNECTED);
790 int poll_ret = wait_for_socket(setup_connection_socket_, POLL_TIMEOUT_MS);
791 if (poll_ret == 0)
return 0;
792 if (poll_ret < 0)
return static_cast<int>(ErrorCode::TRANSPORT_ERROR);
794 return recv_with_size(setup_connection_socket_, buffer);
800 for (
int retry = 0; retry < 10 && sock < 0; ++retry) {
803 if (shutdown_requested_.load())
return ErrorCode::CANCELLED;
804 std::this_thread::sleep_for(std::chrono::milliseconds(200));
808 E3_LOG_ERROR(LOG_TAG) <<
"Failed to connect inbound socket: " << strerror(errno);
809 return ErrorCode::CONNECTION_FAILED;
811 inbound_connection_socket_ = sock;
812 E3_LOG_INFO(LOG_TAG) <<
"Inbound socket connected (client) to "
814 return ErrorCode::SUCCESS;
820 for (
int retry = 0; retry < 10 && sock < 0; ++retry) {
823 if (shutdown_requested_.load())
return ErrorCode::CANCELLED;
824 std::this_thread::sleep_for(std::chrono::milliseconds(200));
828 E3_LOG_ERROR(LOG_TAG) <<
"Failed to connect outbound socket: " << strerror(errno);
829 return ErrorCode::CONNECTION_FAILED;
831 outbound_connection_socket_ = sock;
832 E3_LOG_INFO(LOG_TAG) <<
"Outbound socket connected (client) to "
834 return ErrorCode::SUCCESS;
838 E3_LOG_DEBUG(LOG_TAG) <<
"Shutting down POSIX connector";
839 shutdown_requested_.store(
true);
842 if (setup_socket_ >= 0) {
845 if (inbound_socket_ >= 0) {
850 if (outbound_socket_ >= 0) {
858 if (inbound_connection_socket_ != UNUSED_SOCKET && inbound_connection_socket_ >= 0) {
859 close(inbound_connection_socket_);
862 if (setup_connection_socket_ != UNUSED_SOCKET && setup_connection_socket_ >= 0) {
863 close(setup_connection_socket_);
866 if (outbound_connection_socket_ != UNUSED_SOCKET && outbound_connection_socket_ >= 0) {
867 close(outbound_connection_socket_);
870 for (
int fd : inbound_peer_sockets_) {
873 inbound_peer_sockets_.clear();
874 for (
int fd : outbound_peer_sockets_) {
877 outbound_peer_sockets_.clear();
879 if (inbound_socket_ >= 0) {
880 close(inbound_socket_);
883 if (outbound_socket_ >= 0) {
884 close(outbound_socket_);
887 if (setup_socket_ >= 0) {
888 close(setup_socket_);
903 setup_connection_socket_ = UNUSED_SOCKET;
904 inbound_socket_ = -1;
905 inbound_connection_socket_ = UNUSED_SOCKET;
906 outbound_socket_ = -1;
907 outbound_connection_socket_ = UNUSED_SOCKET;
910 E3_LOG_INFO(LOG_TAG) <<
"POSIX connector disposed";
913int PosixE3Connector::send_in_chunks(
int sockfd,
const uint8_t* buffer,
size_t buffer_size) {
919 uint32_t network_order_size = htonl(
static_cast<uint32_t
>(buffer_size));
921 iov[0].iov_base = &network_order_size;
922 iov[0].iov_len =
sizeof(network_order_size);
923 iov[1].iov_base =
const_cast<uint8_t*
>(buffer);
924 iov[1].iov_len = buffer_size;
926 size_t total_len =
sizeof(network_order_size) + buffer_size;
927 size_t total_sent = 0;
928 while (total_sent < total_len) {
930 if (total_sent <
sizeof(network_order_size)) {
934 cur[0].iov_base =
reinterpret_cast<uint8_t*
>(&network_order_size) + total_sent;
935 cur[0].iov_len =
sizeof(network_order_size) - total_sent;
937 sent = ::writev(sockfd, cur, 2);
939 size_t payload_sent = total_sent -
sizeof(network_order_size);
940 size_t to_send = std::min(
static_cast<size_t>(CHUNK_SIZE),
941 buffer_size - payload_sent);
942 sent =
::send(sockfd, buffer + payload_sent, to_send, 0);
945 if (errno == EINTR)
continue;
946 E3_LOG_ERROR(LOG_TAG) <<
"Failed to send data: " << strerror(errno);
949 total_sent +=
static_cast<size_t>(sent);
955int PosixE3Connector::recv_with_size(
int sockfd, std::vector<uint8_t>& buffer) {
957 uint32_t network_order_size = 0;
958 size_t header_received = 0;
959 while (header_received <
sizeof(network_order_size)) {
960 ssize_t ret = ::recv(sockfd,
961 reinterpret_cast<uint8_t*
>(&network_order_size) + header_received,
962 sizeof(network_order_size) - header_received, 0);
965 E3_LOG_DEBUG(LOG_TAG) <<
"Connection closed while reading size header";
967 E3_LOG_ERROR(LOG_TAG) <<
"Failed to receive size header: " << strerror(errno);
969 return ret == 0 ? 0 :
static_cast<int>(ErrorCode::TRANSPORT_ERROR);
971 header_received +=
static_cast<size_t>(ret);
974 uint32_t payload_size = ntohl(network_order_size);
976 E3_LOG_ERROR(LOG_TAG) <<
"Invalid payload size: " << payload_size;
977 return static_cast<int>(ErrorCode::TRANSPORT_ERROR);
980 buffer.resize(payload_size);
981 size_t total_received = 0;
982 while (total_received < payload_size) {
983 size_t to_read = std::min(
static_cast<size_t>(CHUNK_SIZE), payload_size - total_received);
984 ssize_t ret = ::recv(sockfd, buffer.data() + total_received, to_read, 0);
987 E3_LOG_ERROR(LOG_TAG) <<
"Connection closed during chunked receive";
989 E3_LOG_ERROR(LOG_TAG) <<
"Failed to receive chunk: " << strerror(errno);
991 return static_cast<int>(ErrorCode::TRANSPORT_ERROR);
993 total_received +=
static_cast<size_t>(ret);
996 E3_LOG_TRACE(LOG_TAG) <<
"recv_with_size: " << payload_size <<
" bytes";
997 return static_cast<int>(payload_size);
std::string inbound_endpoint_
std::string outbound_endpoint_
std::string setup_endpoint_
const std::string & inbound_endpoint() const noexcept
Get inbound endpoint.
const std::string & setup_endpoint() const noexcept
Get setup endpoint.
const std::string & outbound_endpoint() const noexcept
Get outbound endpoint.
ErrorCode send_setup_request_client(const std::vector< uint8_t > &data) override
Send a SetupRequest on the setup channel (dApp role).
~PosixE3Connector() override
int recv_setup_request(std::vector< uint8_t > &buffer) override
Receive a setup request.
void dispose() override
Clean up and release all resources.
int receive(std::vector< uint8_t > &buffer) override
Receive data from the inbound channel.
ErrorCode send(const std::vector< uint8_t > &data) override
Send data on the outbound channel.
ErrorCode setup_initial_connection_client() override
Connect the setup channel (dApp role: REQ connect).
E3TransportLayer transport_layer() const noexcept override
Get the transport layer type.
int recv_setup_response_client(std::vector< uint8_t > &buffer) override
Receive a SetupResponse on the setup channel (dApp role).
PosixE3Connector(E3TransportLayer transport_layer, const std::string &setup_endpoint, const std::string &inbound_endpoint, const std::string &outbound_endpoint, uint16_t setup_port, uint16_t inbound_port, uint16_t outbound_port)
ErrorCode send_response(const std::vector< uint8_t > &data) override
Send a setup response.
void shutdown() override
Interrupt blocking operations for shutdown.
ErrorCode setup_outbound_connection_client() override
Connect the outbound channel (dApp role: PUB connect to the RAN's subscriber endpoint).
ErrorCode setup_inbound_connection_client() override
Connect the inbound channel (dApp role: SUB connect to the RAN's publisher endpoint).
ErrorCode setup_initial_connection() override
Set up the initial connection (setup channel).
ErrorCode setup_inbound_connection() override
Set up the inbound connection (SUB channel).
ErrorCode setup_outbound_connection() override
Set up the outbound connection (PUB channel).
Lock-free per-thread latency stamp recorder (header-only, C99).
Logging facility for libe3.
#define E3_LOG_TRACE(component)
#define E3_LOG_WARN(component)
#define E3_LOG_DEBUG(component)
#define E3_LOG_INFO(component)
#define E3_LOG_ERROR(component)
ErrorCode
Error codes returned by libe3 operations Use shared error-code list (see libe3/error_codes....
E3TransportLayer
Transport layer types (matches Python E3TransportLayer).
@ SCTP
SCTP (O-RAN standard).
@ IPC
Unix Domain Sockets / IPC.
@ RAN
RAN function (binds sockets; today's only behaviour).
@ DAPP
dApp (connects to a RAN agent)
constexpr size_t MAX_PROTOCOL_DATA_SIZE
constexpr size_t DEFAULT_BUFFER_SIZE
POSIX socket-based E3 Connector.