Merge branch 'collectd-5.4' into collectd-5.5
[collectd.git] / src / network.c
index 083571b..d52da68 100644 (file)
@@ -22,6 +22,7 @@
  *   Aman Gupta <aman at tmm1.net>
  **/
 
+#define _DEFAULT_SOURCE
 #define _BSD_SOURCE /* For struct ip_mreq */
 
 #include "collectd.h"
@@ -119,6 +120,8 @@ struct sockent_client
        gcry_cipher_hd_t cypher;
        unsigned char password_hash[32];
 #endif
+       cdtime_t next_resolve_reconnect;
+       cdtime_t resolve_interval;
 };
 
 struct sockent_server
@@ -921,15 +924,19 @@ static int parse_part_number (void **ret_buffer, size_t *ret_buffer_len,
 } /* int parse_part_number */
 
 static int parse_part_string (void **ret_buffer, size_t *ret_buffer_len,
-               char *output, int output_len)
+               char *output, size_t const output_len)
 {
        char *buffer = *ret_buffer;
        size_t buffer_len = *ret_buffer_len;
 
        uint16_t tmp16;
-       size_t header_size = 2 * sizeof (uint16_t);
+       size_t const header_size = 2 * sizeof (uint16_t);
 
        uint16_t pkg_length;
+       size_t payload_size;
+
+       if (output_len <= 0)
+               return (EINVAL);
 
        if (buffer_len < header_size)
        {
@@ -948,6 +955,7 @@ static int parse_part_string (void **ret_buffer, size_t *ret_buffer_len,
        memcpy ((void *) &tmp16, buffer, sizeof (tmp16));
        buffer += sizeof (tmp16);
        pkg_length = ntohs (tmp16);
+       payload_size = ((size_t) pkg_length) - header_size;
 
        /* Check that packet fits in the input buffer */
        if (pkg_length > buffer_len)
@@ -973,22 +981,24 @@ static int parse_part_string (void **ret_buffer, size_t *ret_buffer_len,
        /* Check that the package data fits into the output buffer.
         * The previous if-statement ensures that:
         * `pkg_length > header_size' */
-       if ((output_len < 0)
-                       || ((size_t) output_len < ((size_t) pkg_length - header_size)))
+       if (output_len < payload_size)
        {
                WARNING ("network plugin: parse_part_string: "
-                               "Output buffer too small.");
+                               "Buffer too small: "
+                               "Output buffer holds %zu bytes, "
+                               "which is too small to hold the received "
+                               "%zu byte string.",
+                               output_len, payload_size);
                return (-1);
        }
 
        /* All sanity checks successfull, let's copy the data over */
-       output_len = pkg_length - header_size;
-       memcpy ((void *) output, (void *) buffer, output_len);
-       buffer += output_len;
+       memcpy ((void *) output, (void *) buffer, payload_size);
+       buffer += payload_size;
 
        /* For some very weird reason '\0' doesn't do the trick on SPARC in
         * this statement. */
-       if (output[output_len - 1] != 0)
+       if (output[payload_size - 1] != 0)
        {
                WARNING ("network plugin: parse_part_string: "
                                "Received string does not end "
@@ -2029,6 +2039,8 @@ static sockent_t *sockent_create (int type) /* {{{ */
        {
                se->data.client.fd = -1;
                se->data.client.addr = NULL;
+               se->data.client.resolve_interval = 0;
+               se->data.client.next_resolve_reconnect = 0;
 #if HAVE_LIBGCRYPT
                se->data.client.security_level = SECURITY_LEVEL_NONE;
                se->data.client.username = NULL;
@@ -2095,6 +2107,26 @@ static int sockent_init_crypto (sockent_t *se) /* {{{ */
        return (0);
 } /* }}} int sockent_init_crypto */
 
+static int sockent_client_disconnect (sockent_t *se) /* {{{ */
+{
+       struct sockent_client *client;
+
+       if ((se == NULL) || (se->type != SOCKENT_TYPE_CLIENT))
+               return (EINVAL);
+
+       client = &se->data.client;
+       if (client->fd >= 0) /* connected */
+       {
+               close (client->fd);
+               client->fd = -1;
+       }
+
+       sfree (client->addr);
+       client->addrlen = 0;
+
+       return (0);
+} /* }}} int sockent_client_disconnect */
+
 static int sockent_client_connect (sockent_t *se) /* {{{ */
 {
        static c_complain_t complaint = C_COMPLAIN_INIT_STATIC;
@@ -2103,12 +2135,22 @@ static int sockent_client_connect (sockent_t *se) /* {{{ */
        struct addrinfo  ai_hints;
        struct addrinfo *ai_list = NULL, *ai_ptr;
        int status;
+       _Bool reconnect = 0;
+       cdtime_t now;
 
        if ((se == NULL) || (se->type != SOCKENT_TYPE_CLIENT))
                return (EINVAL);
 
        client = &se->data.client;
-       if (client->fd >= 0) /* already connected */
+
+       now = cdtime ();
+       if (client->resolve_interval != 0 && client->next_resolve_reconnect < now) {
+               DEBUG("network plugin: Reconnecting socket, resolve_interval = %lf, next_resolve_reconnect = %lf",
+                       CDTIME_T_TO_DOUBLE(client->resolve_interval), CDTIME_T_TO_DOUBLE(client->next_resolve_reconnect));
+               reconnect = 1;
+       }
+
+       if (client->fd >= 0 && !reconnect) /* already connected and not stale*/
                return (0);
 
        memset (&ai_hints, 0, sizeof (ai_hints));
@@ -2140,6 +2182,9 @@ static int sockent_client_connect (sockent_t *se) /* {{{ */
 
        for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
        {
+               if (client->fd >= 0) /* when we reconnect */
+                       sockent_client_disconnect(se);
+
                client->fd = socket (ai_ptr->ai_family,
                                ai_ptr->ai_socktype,
                                ai_ptr->ai_protocol);
@@ -2177,28 +2222,11 @@ static int sockent_client_connect (sockent_t *se) /* {{{ */
        freeaddrinfo (ai_list);
        if (client->fd < 0)
                return (-1);
-       return (0);
-} /* }}} int sockent_client_connect */
-
-static int sockent_client_disconnect (sockent_t *se) /* {{{ */
-{
-       struct sockent_client *client;
-
-       if ((se == NULL) || (se->type != SOCKENT_TYPE_CLIENT))
-               return (EINVAL);
-
-       client = &se->data.client;
-       if (client->fd >= 0) /* connected */
-       {
-               close (client->fd);
-               client->fd = -1;
-       }
-
-       sfree (client->addr);
-       client->addrlen = 0;
 
+       if (client->resolve_interval > 0)
+               client->next_resolve_reconnect = now + client->resolve_interval;
        return (0);
-} /* }}} int sockent_client_disconnect */
+} /* }}} int sockent_client_connect */
 
 /* Open the file descriptors for a initialized sockent structure. */
 static int sockent_server_listen (sockent_t *se) /* {{{ */
@@ -2989,7 +3017,7 @@ static int network_config_set_ttl (const oconfig_item_t *ci) /* {{{ */
     network_config_ttl = tmp;
   else {
     WARNING ("network plugin: The `TimeToLive' must be between 1 and 255.");
-    return (-1);    
+    return (-1);
   }
 
   return (0);
@@ -3216,6 +3244,8 @@ static int network_config_add_server (const oconfig_item_t *ci) /* {{{ */
     if (strcasecmp ("Interface", child->key) == 0)
       network_config_set_interface (child,
           &se->interface);
+               else if (strcasecmp ("ResolveInterval", child->key) == 0)
+                       cf_util_get_cdtime(child, &se->data.client.resolve_interval);
     else
     {
       WARNING ("network plugin: Option `%s' is not allowed here.",