From 3aec3589a45dd71191f47da93656b6b5614903de Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Sun, 2 Jan 2022 23:53:55 +0000 Subject: [PATCH] [Project] Rdns: Add reaper for inactive TCP connections --- contrib/librdns/resolver.c | 13 ++++ contrib/librdns/util.c | 124 ++++++++++++++++++++++++++++++------- contrib/librdns/util.h | 14 ++++- 3 files changed, 127 insertions(+), 24 deletions(-) diff --git a/contrib/librdns/resolver.c b/contrib/librdns/resolver.c index 520e85588..3197230ed 100644 --- a/contrib/librdns/resolver.c +++ b/contrib/librdns/resolver.c @@ -526,8 +526,21 @@ static void rdns_process_periodic (void *arg) { struct rdns_resolver *resolver = (struct rdns_resolver*)arg; + struct rdns_server *serv; UPSTREAM_RESCAN (resolver->servers, time (NULL)); + + UPSTREAM_FOREACH (resolver->servers, serv) { + for (int i = 0; i < serv->tcp_io_cnt; i ++) { + if (IS_CHANNEL_CONNECTED(serv->tcp_io_channels[i])) { + /* Disconnect channels with no requests in flight */ + if (kh_size(serv->tcp_io_channels[i]->requests) == 0) { + rdns_debug ("reset inactive TCP connection to %s", serv->name); + rdns_ioc_tcp_reset (serv->tcp_io_channels[i]); + } + } + } + } } static void diff --git a/contrib/librdns/util.c b/contrib/librdns/util.c index e33ab709c..61c244199 100644 --- a/contrib/librdns/util.c +++ b/contrib/librdns/util.c @@ -515,8 +515,15 @@ rdns_ioc_free (struct rdns_io_channel *ioc) ioc->resolver->async->del_read (ioc->resolver->async->data, ioc->async_io); kh_destroy(rdns_requests_hash, ioc->requests); - close (ioc->sock); - free (ioc->saddr); + + if (ioc->sock != -1) { + close(ioc->sock); + } + + if (ioc->saddr != NULL) { + free(ioc->saddr); + } + free (ioc); } @@ -553,28 +560,12 @@ rdns_ioc_new (struct rdns_server *serv, if (is_tcp) { /* We also need to connect a TCP channel and set a TCP buffer */ nioc->tcp = (struct rdns_tcp_channel *)(((unsigned char *)nioc) + sizeof(*nioc)); - int r = connect (nioc->sock, nioc->saddr, nioc->slen); - - if (r == -1) { - if (errno != EAGAIN && errno != EINTR && errno != EINPROGRESS) { - rdns_err ("cannot connect a TCP socket: %s for server %s", - strerror(errno), serv->name); - close(nioc->sock); - free(nioc); - return NULL; - } - else { - /* We need to wait for write readiness here */ - nioc->async_io = resolver->async->add_write (resolver->async->data, - nioc->sock, nioc); - } - } - else { - /* Always be ready to read from a TCP socket */ - nioc->flags |= RDNS_CHANNEL_CONNECTED|RDNS_CHANNEL_ACTIVE; - nioc->tcp->async_read = resolver->async->add_read(resolver->async->data, - nioc->sock, nioc); + if (!rdns_ioc_tcp_connect(nioc)) { + rdns_err ("cannot connect TCP socket to %s: %s", serv->name, + strerror (errno)); + free (nioc); + return NULL; } nioc->flags |= RDNS_CHANNEL_TCP; @@ -636,6 +627,93 @@ rdns_request_release (struct rdns_request *req) REF_RELEASE (req); } +void +rdns_ioc_tcp_reset (struct rdns_io_channel *ioc) +{ + struct rdns_resolver *resolver = ioc->resolver; + + if (IS_CHANNEL_CONNECTED(ioc)) { + if (ioc->tcp->async_write) { + resolver->async->del_write (resolver->async->data, ioc->tcp->async_write); + ioc->tcp->async_write = NULL; + } + if (ioc->tcp->async_read) { + resolver->async->del_read (resolver->async->data, ioc->tcp->async_read); + ioc->tcp->async_read = NULL; + } + + ioc->flags &= ~RDNS_CHANNEL_CONNECTED; + } + + if (ioc->sock != -1) { + close (ioc->sock); + ioc->sock = -1; + } + if (ioc->saddr) { + free (ioc->saddr); + ioc->saddr = NULL; + } +} + +bool +rdns_ioc_tcp_connect (struct rdns_io_channel *ioc) +{ + struct rdns_resolver *resolver = ioc->resolver; + + if (IS_CHANNEL_CONNECTED(ioc)) { + rdns_err ("trying to connect already connected IO channel!"); + return false; + } + + if (ioc->sock == -1) { + ioc->sock = rdns_make_client_socket (ioc->srv->name, ioc->srv->port, + SOCK_STREAM, &ioc->saddr, &ioc->slen); + if (ioc->sock == -1) { + rdns_err ("cannot open socket to %s: %s", ioc->srv->name, + strerror (errno)); + + if (ioc->saddr) { + free (ioc->saddr); + ioc->saddr = NULL; + } + + return false; + } + } + + int r = connect (ioc->sock, ioc->saddr, ioc->slen); + + if (r == -1) { + if (errno != EAGAIN && errno != EINTR && errno != EINPROGRESS) { + rdns_err ("cannot connect a TCP socket: %s for server %s", + strerror(errno), ioc->srv->name); + close (ioc->sock); + + if (ioc->saddr) { + free (ioc->saddr); + ioc->saddr = NULL; + } + + ioc->sock = -1; + + return false; + } + else { + /* We need to wait for write readiness here */ + ioc->tcp->async_write = resolver->async->add_write (resolver->async->data, + ioc->sock, ioc); + } + } + else { + /* Always be ready to read from a TCP socket */ + ioc->flags |= RDNS_CHANNEL_CONNECTED|RDNS_CHANNEL_ACTIVE; + ioc->tcp->async_read = resolver->async->add_read(resolver->async->data, + ioc->sock, ioc); + } + + return true; +} + static bool rdns_resolver_conf_process_line (struct rdns_resolver *resolver, const char *line, rdns_resolv_conf_cb cb, void *ud) diff --git a/contrib/librdns/util.h b/contrib/librdns/util.h index eea818dee..70ad053a0 100644 --- a/contrib/librdns/util.h +++ b/contrib/librdns/util.h @@ -51,12 +51,24 @@ uint16_t rdns_permutor_generate_id (void); void rdns_ioc_free (struct rdns_io_channel *ioc); /** - * C + * Creates a new IO channel */ struct rdns_io_channel * rdns_ioc_new (struct rdns_server *srv, struct rdns_resolver *resolver, bool is_tcp); +/** + * Resets inactive/errored TCP chain as recommended by RFC + * @param ioc + */ +void rdns_ioc_tcp_reset (struct rdns_io_channel *ioc); + +/** + * Connect TCP IO channel to a server + * @param ioc + */ +bool rdns_ioc_tcp_connect (struct rdns_io_channel *ioc); + /** * Free request * @param req -- 2.39.5