commit 3aec358: [Project] Rdns: Add reaper for inactive TCP connections

Vsevolod Stakhov vsevolod at highsecure.ru
Wed Jan 5 11:28:12 UTC 2022


Author: Vsevolod Stakhov
Date: 2022-01-02 23:53:55 +0000
URL: https://github.com/rspamd/rspamd/commit/3aec3589a45dd71191f47da93656b6b5614903de

[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


More information about the Commits mailing list