Coverage Report

Created: 2026-08-03 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ndpi/src/lib/protocols/collectd.c
Line
Count
Source
1
/*
2
 * collectd.c
3
 *
4
 * Copyright (C) 2022-23 - ntop.org
5
 *
6
 * nDPI is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * nDPI is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with nDPI.  If not, see <http://www.gnu.org/licenses/>.
18
 *
19
 */
20
21
22
#include "ndpi_protocol_ids.h"
23
24
#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_COLLECTD
25
26
#include "ndpi_api.h"
27
#include "ndpi_private.h"
28
29
1.01M
#define COLLECTD_MIN_BLOCKS_REQUIRED 3
30
1.07M
#define COLLECTD_MAX_BLOCKS_TO_DISSECT 5
31
32
930
#define COLLECTD_ENCR_AES256_MIN_BLOCK_SIZE 6
33
248
#define COLLECTD_ENCR_AES256_IV_SIZE 16
34
35
enum collectd_type {
36
  COLLECTD_TYPE_HOST            = 0x0000,
37
  COLLECTD_TYPE_TIME            = 0x0001,
38
  COLLECTD_TYPE_TIME_HR         = 0x0008,
39
  COLLECTD_TYPE_PLUGIN          = 0x0002,
40
  COLLECTD_TYPE_PLUGIN_INSTANCE = 0x0003,
41
  COLLECTD_TYPE_TYPE            = 0x0004,
42
  COLLECTD_TYPE_TYPE_INSTANCE   = 0x0005,
43
  COLLECTD_TYPE_VALUES          = 0x0006,
44
  COLLECTD_TYPE_INTERVAL        = 0x0007,
45
  COLLECTD_TYPE_INTERVAL_HR     = 0x0009,
46
  COLLECTD_TYPE_MESSAGE         = 0x0100,
47
  COLLECTD_TYPE_SEVERITY        = 0x0101,
48
  COLLECTD_TYPE_SIGN_SHA256     = 0x0200,
49
  COLELCTD_TYPE_ENCR_AES256     = 0x0210,
50
};
51
52
static u_int16_t const collectd_types[] = {
53
  COLLECTD_TYPE_HOST, COLLECTD_TYPE_TIME, COLLECTD_TYPE_TIME_HR, COLLECTD_TYPE_PLUGIN,
54
  COLLECTD_TYPE_PLUGIN_INSTANCE, COLLECTD_TYPE_TYPE, COLLECTD_TYPE_TYPE_INSTANCE,
55
  COLLECTD_TYPE_VALUES, COLLECTD_TYPE_INTERVAL, COLLECTD_TYPE_INTERVAL_HR,
56
  COLLECTD_TYPE_MESSAGE, COLLECTD_TYPE_SEVERITY, COLLECTD_TYPE_SIGN_SHA256,
57
  COLELCTD_TYPE_ENCR_AES256
58
};
59
static const size_t collectd_types_length = NDPI_ARRAY_LENGTH(collectd_types);
60
61
static void ndpi_int_collectd_add_connection(struct ndpi_detection_module_struct * const ndpi_struct,
62
                                             struct ndpi_flow_struct * const flow)
63
1.07k
{
64
1.07k
  NDPI_LOG_INFO(ndpi_struct, "found collectd\n");
65
1.07k
  ndpi_set_detected_protocol(ndpi_struct, flow,
66
1.07k
                             NDPI_PROTOCOL_COLLECTD,
67
1.07k
                             NDPI_PROTOCOL_UNKNOWN,
68
1.07k
                             NDPI_CONFIDENCE_DPI);
69
1.07k
}
70
71
static u_int16_t npdi_int_collectd_block_size(struct ndpi_packet_struct const * const packet,
72
                                              u_int16_t const block_offset)
73
1.07M
{
74
1.07M
  if (block_offset + 4 > packet->payload_packet_len)
75
24.3k
  {
76
24.3k
    return 0;
77
24.3k
  }
78
79
1.05M
  u_int16_t next_block = ntohs(get_u_int16_t(packet->payload, block_offset + 2));
80
1.05M
  if (block_offset + next_block > packet->payload_packet_len ||
81
359k
      (u_int16_t)(block_offset + next_block) <= block_offset /* possible overflow or next_block is zero */)
82
904k
  {
83
904k
    return 0;
84
904k
  }
85
86
146k
  return next_block;
87
1.05M
}
88
89
static int ndpi_int_collectd_check_type(u_int16_t block_type)
90
146k
{
91
146k
  size_t i;
92
93
1.54M
  for (i = 0; i < collectd_types_length; ++i)
94
1.45M
  {
95
1.45M
    if (block_type == collectd_types[i])
96
58.5k
    {
97
58.5k
      return 0;
98
58.5k
    }
99
1.45M
  }
100
101
87.8k
  return 1;
102
146k
}
103
104
static void ndpi_int_collectd_dissect_hostname(struct ndpi_flow_struct * const flow,
105
                                               struct ndpi_packet_struct const * const packet,
106
                                               u_int16_t block_length)
107
791
{
108
791
  ndpi_hostname_sni_set(flow, &packet->payload[4], block_length, NDPI_HOSTNAME_NORM_ALL);
109
791
}
110
111
static int ndpi_int_collectd_dissect_username(struct ndpi_flow_struct * const flow,
112
                                              struct ndpi_packet_struct const * const packet)
113
248
{
114
248
  u_int16_t username_length = ntohs(get_u_int16_t(packet->payload, 4));
115
116
248
  if(username_length > packet->payload_packet_len -
117
248
     COLLECTD_ENCR_AES256_MIN_BLOCK_SIZE -
118
248
     COLLECTD_ENCR_AES256_IV_SIZE)
119
136
  {
120
136
    return 1;
121
136
  }
122
123
112
  size_t sz_len = ndpi_min(sizeof(flow->protos.collectd.client_username) - 1, username_length);
124
112
  memcpy(flow->protos.collectd.client_username, &packet->payload[6], sz_len);
125
112
  flow->protos.collectd.client_username[sz_len] = '\0';
126
127
112
  return 0;
128
248
}
129
130
static void ndpi_search_collectd(struct ndpi_detection_module_struct *ndpi_struct,
131
                                 struct ndpi_flow_struct *flow)
132
1.01M
{
133
1.01M
  struct ndpi_packet_struct const * const packet = &ndpi_struct->packet;
134
1.01M
  u_int16_t num_blocks;
135
1.01M
  u_int16_t block_offset = 0, block_type, block_length;
136
1.01M
  u_int16_t hostname_length = 0;
137
138
1.01M
  NDPI_LOG_DBG(ndpi_struct, "search collectd\n");
139
140
1.07M
  for (num_blocks = 0; num_blocks < COLLECTD_MAX_BLOCKS_TO_DISSECT;
141
1.01M
       ++num_blocks, block_offset += block_length)
142
1.07M
  {
143
1.07M
    block_length = npdi_int_collectd_block_size(packet, block_offset);
144
1.07M
    if (block_length == 0)
145
929k
    {
146
929k
      break;
147
929k
    }
148
149
146k
    block_type = ntohs(get_u_int16_t(packet->payload, block_offset));
150
146k
    if (ndpi_int_collectd_check_type(block_type) != 0)
151
87.8k
    {
152
87.8k
      break;
153
87.8k
    } else {
154
58.5k
      if (block_type == COLLECTD_TYPE_HOST)
155
23.1k
      {
156
        /*
157
         * Dissect the hostname later, when we are sure that it is
158
         * the collectd protocol.
159
         */
160
23.1k
        if(block_length > 4)
161
11.7k
          hostname_length = block_length - 4; /* Ignore type and length fields */
162
35.4k
      } else if (block_type == COLELCTD_TYPE_ENCR_AES256) {
163
        /*
164
         * The encrypted data block is a special case.
165
         * It is the only dissectable block as everything else in it
166
         * is encrypted.
167
         */
168
410
        if (block_length != packet->payload_packet_len ||
169
272
            block_length < COLLECTD_ENCR_AES256_MIN_BLOCK_SIZE ||
170
248
            ndpi_int_collectd_dissect_username(flow, packet) != 0)
171
298
        {
172
298
          NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
173
298
        } else {
174
112
          ndpi_int_collectd_add_connection(ndpi_struct, flow);
175
112
        }
176
410
        return;
177
410
      }
178
58.5k
    }
179
146k
  }
180
181
1.01M
  if (num_blocks < COLLECTD_MIN_BLOCKS_REQUIRED)
182
1.01M
  {
183
1.01M
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
184
1.01M
    return;
185
1.01M
  }
186
187
966
  if (hostname_length > 0)
188
791
    ndpi_int_collectd_dissect_hostname(flow, packet, hostname_length);
189
190
966
  ndpi_int_collectd_add_connection(ndpi_struct, flow);
191
966
}
192
193
void init_collectd_dissector(struct ndpi_detection_module_struct *ndpi_struct)
194
17.6k
{
195
17.6k
  ndpi_register_dissector("collectd", ndpi_struct,
196
17.6k
                     ndpi_search_collectd,
197
17.6k
                     NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_UDP_WITH_PAYLOAD,
198
17.6k
                     1, NDPI_PROTOCOL_COLLECTD);
199
17.6k
}