]> source.dussan.org Git - rspamd.git/commitdiff
[Project] Rdns: Add reaper for inactive TCP connections
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Sun, 2 Jan 2022 23:53:55 +0000 (23:53 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Sun, 2 Jan 2022 23:53:55 +0000 (23:53 +0000)
contrib/librdns/resolver.c
contrib/librdns/util.c
contrib/librdns/util.h

index 520e85588226922c7b2d342fbd375cac7b076e57..3197230edab2383337ac331d6fe8709e74947413 100644 (file)
@@ -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
index e33ab709cbe7647388fece01e23569dad569bb79..61c244199497b6ca1ba9c5b531ea5f0b072564cd 100644 (file)
@@ -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)
index eea818deebc86adf85e8905f78f1c40b59ed362e..70ad053a08e8d6366131588ed8b160fefb156d74 100644 (file)
@@ -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