diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:24:08 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:24:08 +0000 |
commit | f449f278dd3c70e479a035f50a9bb817a9b433ba (patch) | |
tree | 8ca2bfb785dda9bb4d573acdf9b42aea9cd51383 /src/utils/common/netio.c | |
parent | Initial commit. (diff) | |
download | knot-upstream/3.2.6.tar.xz knot-upstream/3.2.6.zip |
Adding upstream version 3.2.6.upstream/3.2.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/utils/common/netio.c | 896 |
1 files changed, 896 insertions, 0 deletions
diff --git a/src/utils/common/netio.c b/src/utils/common/netio.c new file mode 100644 index 0000000..4f31551 --- /dev/null +++ b/src/utils/common/netio.c @@ -0,0 +1,896 @@ +/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <arpa/inet.h> +#include <fcntl.h> +#include <netdb.h> +#include <poll.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <sys/types.h> // OpenBSD +#include <netinet/tcp.h> // TCP_FASTOPEN +#include <sys/socket.h> + +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif + +#include "utils/common/netio.h" +#include "utils/common/msg.h" +#include "utils/common/tls.h" +#include "libknot/libknot.h" +#include "contrib/proxyv2/proxyv2.h" +#include "contrib/sockaddr.h" + +srv_info_t *srv_info_create(const char *name, const char *service) +{ + if (name == NULL || service == NULL) { + DBG_NULL; + return NULL; + } + + // Create output structure. + srv_info_t *server = calloc(1, sizeof(srv_info_t)); + + // Check output. + if (server == NULL) { + return NULL; + } + + // Fill output. + server->name = strdup(name); + server->service = strdup(service); + + if (server->name == NULL || server->service == NULL) { + srv_info_free(server); + return NULL; + } + + // Return result. + return server; +} + +void srv_info_free(srv_info_t *server) +{ + if (server == NULL) { + DBG_NULL; + return; + } + + free(server->name); + free(server->service); + free(server); +} + +int get_iptype(const ip_t ip, const srv_info_t *server) +{ + bool unix_socket = (server->name[0] == '/'); + + switch (ip) { + case IP_4: + return AF_INET; + case IP_6: + return AF_INET6; + default: + return unix_socket ? AF_UNIX : AF_UNSPEC; + } +} + +int get_socktype(const protocol_t proto, const uint16_t type) +{ + switch (proto) { + case PROTO_TCP: + return SOCK_STREAM; + case PROTO_UDP: + return SOCK_DGRAM; + default: + if (type == KNOT_RRTYPE_AXFR || type == KNOT_RRTYPE_IXFR) { + return SOCK_STREAM; + } else { + return SOCK_DGRAM; + } + } +} + +const char *get_sockname(const int socktype) +{ + switch (socktype) { + case SOCK_STREAM: + return "TCP"; + case SOCK_DGRAM: + return "UDP"; + default: + return "UNKNOWN"; + } +} + +static int get_addr(const srv_info_t *server, + const int iptype, + const int socktype, + struct addrinfo **info) +{ + struct addrinfo hints; + + // Set connection hints. + memset(&hints, 0, sizeof(hints)); + hints.ai_family = iptype; + hints.ai_socktype = socktype; + + // Get connection parameters. + int ret = getaddrinfo(server->name, server->service, &hints, info); + switch (ret) { + case 0: + return 0; +#ifdef EAI_ADDRFAMILY /* EAI_ADDRFAMILY isn't implemented in FreeBSD/macOS anymore. */ + case EAI_ADDRFAMILY: + break; +#else /* FreeBSD, macOS, and likely others return EAI_NONAME instead. */ + case EAI_NONAME: + if (iptype != AF_UNSPEC) { + break; + } + /* FALLTHROUGH */ +#endif /* EAI_ADDRFAMILY */ + default: + ERR("%s for %s@%s", gai_strerror(ret), server->name, server->service); + } + return -1; +} + +void get_addr_str(const struct sockaddr_storage *ss, + const int socktype, + char **dst) +{ + char addr_str[SOCKADDR_STRLEN] = {0}; + + // Get network address string and port number. + sockaddr_tostr(addr_str, sizeof(addr_str), ss); + + // Calculate needed buffer size + const char *sock_name = get_sockname(socktype); + size_t buflen = strlen(addr_str) + strlen(sock_name) + 3 /* () */; + + // Free previous string if any and write result + free(*dst); + *dst = malloc(buflen); + if (*dst != NULL) { + int ret = snprintf(*dst, buflen, "%s(%s)", addr_str, sock_name); + if (ret <= 0 || ret >= buflen) { + **dst = '\0'; + } + } +} + +int net_init(const srv_info_t *local, + const srv_info_t *remote, + const int iptype, + const int socktype, + const int wait, + const net_flags_t flags, + const tls_params_t *tls_params, + const https_params_t *https_params, + const quic_params_t *quic_params, + const struct sockaddr *proxy_src, + const struct sockaddr *proxy_dst, + net_t *net) +{ + if (remote == NULL || net == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + // Clean network structure. + memset(net, 0, sizeof(*net)); + net->sockfd = -1; + + if (iptype == AF_UNIX) { + struct addrinfo *info = calloc(1, sizeof(struct addrinfo)); + info->ai_addr = calloc(1, sizeof(struct sockaddr_storage)); + info->ai_addrlen = sizeof(struct sockaddr_un); + info->ai_socktype = socktype; + info->ai_family = iptype; + int ret = sockaddr_set_raw((struct sockaddr_storage *)info->ai_addr, + AF_UNIX, (const uint8_t *)remote->name, + strlen(remote->name)); + if (ret != KNOT_EOK) { + free(info->ai_addr); + free(info); + return ret; + } + net->remote_info = info; + } else { + // Get remote address list. + if (get_addr(remote, iptype, socktype, &net->remote_info) != 0) { + net_clean(net); + return KNOT_NET_EADDR; + } + } + + // Set current remote address. + net->srv = net->remote_info; + + // Get local address if specified. + if (local != NULL) { + if (get_addr(local, iptype, socktype, &net->local_info) != 0) { + net_clean(net); + return KNOT_NET_EADDR; + } + } + + // Store network parameters. + net->sockfd = -1; + net->iptype = iptype; + net->socktype = socktype; + net->wait = wait; + net->local = local; + net->remote = remote; + net->flags = flags; + net->proxy.src = proxy_src; + net->proxy.dst = proxy_dst; + + if ((bool)(proxy_src == NULL) != (bool)(proxy_dst == NULL) || + (proxy_src != NULL && proxy_src->sa_family != proxy_dst->sa_family)) { + net_clean(net); + return KNOT_EINVAL; + } + + // Prepare for TLS. + if (tls_params != NULL && tls_params->enable) { + int ret = 0; +#ifdef LIBNGHTTP2 + // Prepare for HTTPS. + if (https_params != NULL && https_params->enable) { + ret = tls_ctx_init(&net->tls, tls_params, + GNUTLS_NONBLOCK, net->wait); + if (ret != KNOT_EOK) { + net_clean(net); + return ret; + } + ret = https_ctx_init(&net->https, &net->tls, https_params); + if (ret != KNOT_EOK) { + net_clean(net); + return ret; + } + } else +#endif //LIBNGHTTP2 +#ifdef ENABLE_QUIC + if (quic_params != NULL && quic_params->enable) { + ret = tls_ctx_init(&net->tls, tls_params, + GNUTLS_NONBLOCK | GNUTLS_ENABLE_EARLY_DATA | + GNUTLS_NO_END_OF_EARLY_DATA, net->wait); + if (ret != KNOT_EOK) { + net_clean(net); + return ret; + } + ret = quic_ctx_init(&net->quic, &net->tls, quic_params); + if (ret != KNOT_EOK) { + net_clean(net); + return ret; + } + } else +#endif //ENABLE_QUIC + { + ret = tls_ctx_init(&net->tls, tls_params, + GNUTLS_NONBLOCK, net->wait); + if (ret != KNOT_EOK) { + net_clean(net); + return ret; + } + } + } + + return KNOT_EOK; +} + +/*! + * Connect with TCP Fast Open. + */ +static int fastopen_connect(int sockfd, const struct addrinfo *srv) +{ +#if defined( __FreeBSD__) + const int enable = 1; + return setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN, &enable, sizeof(enable)); +#elif defined(__APPLE__) + // connection is performed lazily when first data are sent + struct sa_endpoints ep = {0}; + ep.sae_dstaddr = srv->ai_addr; + ep.sae_dstaddrlen = srv->ai_addrlen; + int flags = CONNECT_DATA_IDEMPOTENT|CONNECT_RESUME_ON_READ_WRITE; + + return connectx(sockfd, &ep, SAE_ASSOCID_ANY, flags, NULL, 0, NULL, NULL); +#elif defined(__linux__) + // connect() will be called implicitly with sendto(), sendmsg() + return 0; +#else + errno = ENOTSUP; + return -1; +#endif +} + +/*! + * Sends data with TCP Fast Open. + */ +static int fastopen_send(int sockfd, const struct msghdr *msg, int timeout) +{ +#if defined(__FreeBSD__) || defined(__APPLE__) + return sendmsg(sockfd, msg, 0); +#elif defined(__linux__) + int ret = sendmsg(sockfd, msg, MSG_FASTOPEN); + if (ret == -1 && errno == EINPROGRESS) { + struct pollfd pfd = { + .fd = sockfd, + .events = POLLOUT, + .revents = 0, + }; + if (poll(&pfd, 1, 1000 * timeout) != 1) { + errno = ETIMEDOUT; + return -1; + } + ret = sendmsg(sockfd, msg, 0); + } + return ret; +#else + errno = ENOTSUP; + return -1; +#endif +} + +static char *net_get_remote(const net_t *net) +{ + if (net->tls.params->sni != NULL) { + return net->tls.params->sni; + } else if (net->tls.params->hostname != NULL) { + return net->tls.params->hostname; + } else if (strchr(net->remote_str, ':') == NULL) { + char *at = strchr(net->remote_str, '@'); + if (at != NULL && strncmp(net->remote->name, net->remote_str, + at - net->remote_str)) { + return net->remote->name; + } + } + return NULL; +} + +#ifdef ENABLE_QUIC +static int fd_set_recv_ecn(int fd, int family) +{ + unsigned int tos = 1; + switch (family) { + case AF_INET: +#ifdef IP_RECVTOS + if (setsockopt(fd, IPPROTO_IP, IP_RECVTOS, &tos, sizeof(tos)) == -1) { + return knot_map_errno(); + } +#endif + break; + case AF_INET6: + if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVTCLASS, &tos, sizeof(tos)) == -1) { + return knot_map_errno(); + } + break; + default: + return KNOT_EINVAL; + } + return KNOT_EOK; +} +#endif + + +int net_connect(net_t *net) +{ + if (net == NULL || net->srv == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + // Set remote information string. + get_addr_str((struct sockaddr_storage *)net->srv->ai_addr, + net->socktype, &net->remote_str); + + // Create socket. + int sockfd = socket(net->srv->ai_family, net->socktype, 0); + if (sockfd == -1) { + WARN("can't create socket for %s", net->remote_str); + return KNOT_NET_ESOCKET; + } + + // Initialize poll descriptor structure. + struct pollfd pfd = { + .fd = sockfd, + .events = POLLOUT, + .revents = 0, + }; + + // Set non-blocking socket. + if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) { + WARN("can't set non-blocking socket for %s", net->remote_str); + return KNOT_NET_ESOCKET; + } + + // Bind address to socket if specified. + if (net->local_info != NULL) { + if (bind(sockfd, net->local_info->ai_addr, + net->local_info->ai_addrlen) == -1) { + WARN("can't assign address %s", net->local->name); + return KNOT_NET_ESOCKET; + } + } else { + // Ensure source port is always randomized (even for TCP). + struct sockaddr_storage local = { .ss_family = net->srv->ai_family }; + (void)bind(sockfd, (struct sockaddr *)&local, sockaddr_len(&local)); + } + + int ret = 0; + if (net->socktype == SOCK_STREAM) { + int cs = 1, err; + socklen_t err_len = sizeof(err); + bool fastopen = net->flags & NET_FLAGS_FASTOPEN; + +#ifdef TCP_NODELAY + (void)setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &cs, sizeof(cs)); +#endif + + // Establish a connection. + if (net->tls.params == NULL || !fastopen) { + if (fastopen) { + ret = fastopen_connect(sockfd, net->srv); + } else { + ret = connect(sockfd, net->srv->ai_addr, net->srv->ai_addrlen); + } + if (ret != 0 && errno != EINPROGRESS) { + WARN("can't connect to %s", net->remote_str); + close(sockfd); + return KNOT_NET_ECONNECT; + } + + // Check for connection timeout. + if (!fastopen && poll(&pfd, 1, 1000 * net->wait) != 1) { + WARN("connection timeout for %s", net->remote_str); + close(sockfd); + return KNOT_NET_ECONNECT; + } + + // Check if NB socket is writeable. + cs = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, &err_len); + if (cs < 0 || err != 0) { + WARN("can't connect to %s", net->remote_str); + close(sockfd); + return KNOT_NET_ECONNECT; + } + } + + if (net->tls.params != NULL) { +#ifdef LIBNGHTTP2 + if (net->https.params.enable) { + // Establish HTTPS connection. + char *remote = net_get_remote(net); + ret = tls_ctx_setup_remote_endpoint(&net->tls, &doh_alpn, 1, NULL, + remote); + if (ret != 0) { + close(sockfd); + return ret; + } + if (remote && net->https.authority == NULL) { + net->https.authority = strdup(remote); + } + ret = https_ctx_connect(&net->https, sockfd, fastopen, + (struct sockaddr_storage *)net->srv->ai_addr); + } else +#endif //LIBNGHTTP2 + { + // Establish TLS connection. + ret = tls_ctx_setup_remote_endpoint(&net->tls, &dot_alpn, 1, NULL, + net_get_remote(net)); + if (ret != 0) { + close(sockfd); + return ret; + } + ret = tls_ctx_connect(&net->tls, sockfd, fastopen, + (struct sockaddr_storage *)net->srv->ai_addr); + } + if (ret != KNOT_EOK) { + close(sockfd); + return ret; + } + } + } +#ifdef ENABLE_QUIC + else if (net->socktype == SOCK_DGRAM) { + if (net->quic.params.enable) { + // Establish QUIC connection. + ret = fd_set_recv_ecn(sockfd, net->srv->ai_family); + if (ret != KNOT_EOK) { + close(sockfd); + return ret; + } + ret = tls_ctx_setup_remote_endpoint(&net->tls, + doq_alpn, 4, QUIC_PRIORITY, net_get_remote(net)); + if (ret != 0) { + close(sockfd); + return ret; + } + ret = quic_ctx_connect(&net->quic, sockfd, + (struct addrinfo *)net->srv); + if (ret != KNOT_EOK) { + close(sockfd); + return ret; + } + } + } +#endif + + // Store socket descriptor. + net->sockfd = sockfd; + + return KNOT_EOK; +} + +int net_set_local_info(net_t *net) +{ + if (net == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + socklen_t local_addr_len = sizeof(struct sockaddr_storage); + + struct addrinfo *new_info = calloc(1, sizeof(*new_info) + local_addr_len); + if (new_info == NULL) { + return KNOT_ENOMEM; + } + + new_info->ai_addr = (struct sockaddr *)(new_info + 1); + new_info->ai_family = net->srv->ai_family; + new_info->ai_socktype = net->srv->ai_socktype; + new_info->ai_protocol = net->srv->ai_protocol; + new_info->ai_addrlen = local_addr_len; + + if (getsockname(net->sockfd, new_info->ai_addr, &local_addr_len) == -1) { + WARN("can't get local address"); + free(new_info); + return KNOT_NET_ESOCKET; + } + + if (net->local_info != NULL) { + if (net->local == NULL) { + free(net->local_info); + } else { + freeaddrinfo(net->local_info); + } + } + + net->local_info = new_info; + + get_addr_str((struct sockaddr_storage *)net->local_info->ai_addr, + net->socktype, &net->local_str); + + return KNOT_EOK; +} + +int net_send(const net_t *net, const uint8_t *buf, const size_t buf_len) +{ + if (net == NULL || buf == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + +#ifdef ENABLE_QUIC + // Send data over QUIC. + if (net->quic.params.enable) { + int ret = quic_send_dns_query((quic_ctx_t *)&net->quic, + net->sockfd, net->srv, buf, buf_len); + if (ret != KNOT_EOK) { + WARN("can't send query to %s", net->remote_str); + return KNOT_NET_ESEND; + } + } else +#endif + // Send data over UDP. + if (net->socktype == SOCK_DGRAM) { + char proxy_buf[PROXYV2_HEADER_MAXLEN]; + struct iovec iov[2] = { + { .iov_base = proxy_buf, .iov_len = 0 }, + { .iov_base = (void *)buf, .iov_len = buf_len } + }; + + struct msghdr msg = { + .msg_name = net->srv->ai_addr, + .msg_namelen = net->srv->ai_addrlen, + .msg_iov = &iov[1], + .msg_iovlen = 1 + }; + + if (net->proxy.src != NULL && net->proxy.src->sa_family != 0) { + int ret = proxyv2_write_header(proxy_buf, sizeof(proxy_buf), + SOCK_DGRAM, net->proxy.src, + net->proxy.dst); + if (ret < 0) { + WARN("can't send proxied query to %s", net->remote_str); + return KNOT_NET_ESEND; + } + iov[0].iov_len = ret; + msg.msg_iov--; + msg.msg_iovlen++; + } + + ssize_t total = iov[0].iov_len + iov[1].iov_len; + + if (sendmsg(net->sockfd, &msg, 0) != total) { + WARN("can't send query to %s", net->remote_str); + return KNOT_NET_ESEND; + } +#ifdef LIBNGHTTP2 + // Send data over HTTPS + } else if (net->https.params.enable) { + int ret = https_send_dns_query((https_ctx_t *)&net->https, buf, buf_len); + if (ret != KNOT_EOK) { + WARN("can't send query to %s", net->remote_str); + return KNOT_NET_ESEND; + } +#endif //LIBNGHTTP2 + // Send data over TLS. + } else if (net->tls.params != NULL) { + int ret = tls_ctx_send((tls_ctx_t *)&net->tls, buf, buf_len); + if (ret != KNOT_EOK) { + WARN("can't send query to %s", net->remote_str); + return KNOT_NET_ESEND; + } + // Send data over TCP. + } else { + bool fastopen = net->flags & NET_FLAGS_FASTOPEN; + + char proxy_buf[PROXYV2_HEADER_MAXLEN]; + uint16_t pktsize = htons(buf_len); // Leading packet length bytes. + struct iovec iov[3] = { + { .iov_base = proxy_buf, .iov_len = 0 }, + { .iov_base = &pktsize, .iov_len = sizeof(pktsize) }, + { .iov_base = (void *)buf, .iov_len = buf_len } + }; + + struct msghdr msg = { + .msg_name = net->srv->ai_addr, + .msg_namelen = net->srv->ai_addrlen, + .msg_iov = &iov[1], + .msg_iovlen = 2 + }; + + if (net->srv->ai_addr->sa_family == AF_UNIX) { + msg.msg_name = NULL; + } + + if (net->proxy.src != NULL && net->proxy.src->sa_family != 0) { + int ret = proxyv2_write_header(proxy_buf, sizeof(proxy_buf), + SOCK_STREAM, net->proxy.src, + net->proxy.dst); + if (ret < 0) { + WARN("can't send proxied query to %s", net->remote_str); + return KNOT_NET_ESEND; + } + iov[0].iov_len = ret; + msg.msg_iov--; + msg.msg_iovlen++; + } + + ssize_t total = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len; + + int ret = 0; + if (fastopen) { + ret = fastopen_send(net->sockfd, &msg, net->wait); + } else { + ret = sendmsg(net->sockfd, &msg, 0); + } + if (ret != total) { + WARN("can't send query to %s", net->remote_str); + return KNOT_NET_ESEND; + } + } + + return KNOT_EOK; +} + +int net_receive(const net_t *net, uint8_t *buf, const size_t buf_len) +{ + if (net == NULL || buf == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + // Initialize poll descriptor structure. + struct pollfd pfd = { + .fd = net->sockfd, + .events = POLLIN, + .revents = 0, + }; + +#ifdef ENABLE_QUIC + // Receive data over QUIC. + if (net->quic.params.enable) { + int ret = quic_recv_dns_response((quic_ctx_t *)&net->quic, buf, + buf_len, net->srv); + if (ret < 0) { + WARN("can't receive reply from %s", net->remote_str); + return KNOT_NET_ERECV; + } + return ret; + } else +#endif + // Receive data over UDP. + if (net->socktype == SOCK_DGRAM) { + struct sockaddr_storage from; + memset(&from, '\0', sizeof(from)); + + // Receive replies unless correct reply or timeout. + while (true) { + socklen_t from_len = sizeof(from); + + // Wait for datagram data. + if (poll(&pfd, 1, 1000 * net->wait) != 1) { + WARN("response timeout for %s", + net->remote_str); + return KNOT_NET_ETIMEOUT; + } + + // Receive whole UDP datagram. + ssize_t ret = recvfrom(net->sockfd, buf, buf_len, 0, + (struct sockaddr *)&from, &from_len); + if (ret <= 0) { + WARN("can't receive reply from %s", + net->remote_str); + return KNOT_NET_ERECV; + } + + // Compare reply address with the remote one. + if (from_len > sizeof(from) || + memcmp(&from, net->srv->ai_addr, from_len) != 0) { + char *src = NULL; + get_addr_str(&from, net->socktype, &src); + WARN("unexpected reply source %s", src); + free(src); + continue; + } + + return ret; + } +#ifdef LIBNGHTTP2 + // Receive data over HTTPS. + } else if (net->https.params.enable) { + int ret = https_recv_dns_response((https_ctx_t *)&net->https, buf, buf_len); + if (ret < 0) { + WARN("can't receive reply from %s", net->remote_str); + return KNOT_NET_ERECV; + } + return ret; +#endif //LIBNGHTTP2 + // Receive data over TLS. + } else if (net->tls.params != NULL) { + int ret = tls_ctx_receive((tls_ctx_t *)&net->tls, buf, buf_len); + if (ret < 0) { + WARN("can't receive reply from %s", net->remote_str); + return KNOT_NET_ERECV; + } + return ret; + // Receive data over TCP. + } else { + uint32_t total = 0; + + uint16_t msg_len = 0; + // Receive TCP message header. + while (total < sizeof(msg_len)) { + if (poll(&pfd, 1, 1000 * net->wait) != 1) { + WARN("response timeout for %s", + net->remote_str); + return KNOT_NET_ETIMEOUT; + } + + // Receive piece of message. + ssize_t ret = recv(net->sockfd, (uint8_t *)&msg_len + total, + sizeof(msg_len) - total, 0); + if (ret <= 0) { + WARN("can't receive reply from %s", + net->remote_str); + return KNOT_NET_ERECV; + } + total += ret; + } + + // Convert number to host format. + msg_len = ntohs(msg_len); + if (msg_len > buf_len) { + return KNOT_ESPACE; + } + + total = 0; + + // Receive whole answer message by parts. + while (total < msg_len) { + if (poll(&pfd, 1, 1000 * net->wait) != 1) { + WARN("response timeout for %s", + net->remote_str); + return KNOT_NET_ETIMEOUT; + } + + // Receive piece of message. + ssize_t ret = recv(net->sockfd, buf + total, msg_len - total, 0); + if (ret <= 0) { + WARN("can't receive reply from %s", + net->remote_str); + return KNOT_NET_ERECV; + } + total += ret; + } + + return total; + } + + return KNOT_NET_ERECV; +} + +void net_close(net_t *net) +{ + if (net == NULL) { + DBG_NULL; + return; + } + +#ifdef ENABLE_QUIC + if (net->quic.params.enable) { + quic_ctx_close(&net->quic); + } +#endif + tls_ctx_close(&net->tls); + close(net->sockfd); + net->sockfd = -1; +} + +void net_clean(net_t *net) +{ + if (net == NULL) { + DBG_NULL; + return; + } + + free(net->local_str); + free(net->remote_str); + net->local_str = NULL; + net->remote_str = NULL; + + if (net->local_info != NULL) { + if (net->local == NULL) { + free(net->local_info); + } else { + freeaddrinfo(net->local_info); + } + net->local_info = NULL; + } + + if (net->remote_info != NULL) { + if (net->remote_info->ai_addr->sa_family == AF_UNIX) { + free(net->remote_info->ai_addr); + free(net->remote_info); + } else { + freeaddrinfo(net->remote_info); + } + net->remote_info = NULL; + } + +#ifdef LIBNGHTTP2 + https_ctx_deinit(&net->https); +#endif +#ifdef ENABLE_QUIC + quic_ctx_deinit(&net->quic); +#endif + tls_ctx_deinit(&net->tls); +} |