Coverage Report

Created: 2025-11-06 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ndpi/src/lib/protocols/thrift.c
Line
Count
Source
1
/*
2
 * thrift.c
3
 *
4
 * Copyright (C) 2023 - ntop.org
5
 *
6
 * This file is part of nDPI, an open source deep packet inspection
7
 * library based on the OpenDPI and PACE technology by ipoque GmbH
8
 *
9
 * nDPI is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Lesser General Public License as published by
11
 * the Free Software Foundation, either version 3 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * nDPI is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20
 * along with nDPI.  If not, see <http://www.gnu.org/licenses/>.
21
 *
22
 */
23
24
#include "ndpi_protocol_ids.h"
25
26
#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_APACHE_THRIFT
27
28
#include "ndpi_api.h"
29
#include "ndpi_private.h"
30
31
#include <stdint.h>
32
33
// References: https://thrift.apache.org AND https://github.com/apache/thrift
34
// Not Implemented (sub)protocols: TJSONProtocol, TSimpleJSONProtocol and TDebugProtocol
35
36
// TBinaryProtocol
37
PACK_ON
38
struct thrift_strict_hdr {
39
  uint8_t protocol_id;
40
  uint8_t version;
41
  uint8_t unused_byte_pad;
42
  uint8_t message_type;
43
  uint32_t method_length;
44
  char method[0];
45
} PACK_OFF;
46
47
// TCompactProtocol
48
PACK_ON
49
struct thrift_compact_hdr {
50
  uint8_t protocol_id;
51
#if defined(__BIG_ENDIAN__)
52
  uint8_t message_type : 3;
53
  uint8_t version : 5;
54
#elif defined(__LITTLE_ENDIAN__)
55
  uint8_t version : 5;
56
  uint8_t message_type : 3;
57
#else
58
#error "Missing endian macro definitions."
59
#endif
60
  uint8_t sequence_id[3];
61
  uint8_t method_length;
62
  char method[0];
63
} PACK_OFF;
64
65
enum thrift_message_type {
66
  TMT_INVALID_TMESSAGE_TYPE = 0,
67
  TMT_CALL                  = 1,
68
  TMT_REPLY                 = 2,
69
  TMT_EXCEPTION             = 3,
70
  TMT_ONEWAY                = 4,
71
  TMT_TYPE_MAX
72
};
73
74
static void ndpi_int_thrift_add_connection(struct ndpi_detection_module_struct *ndpi_struct,
75
                                           struct ndpi_flow_struct *flow,
76
                                           uint16_t master_protocol)
77
0
{
78
0
  switch (master_protocol)
79
0
  {
80
0
    case NDPI_PROTOCOL_UNKNOWN:
81
0
      NDPI_LOG_DBG(ndpi_struct, "found Apache Thrift TCP/UDP\n");
82
0
      break;
83
0
    case NDPI_PROTOCOL_HTTP:
84
0
      NDPI_LOG_DBG(ndpi_struct, "found Apache Thrift HTTP\n");
85
0
      break;
86
0
  }
87
88
0
  ndpi_set_detected_protocol(ndpi_struct, flow,
89
0
                             NDPI_PROTOCOL_APACHE_THRIFT, master_protocol,
90
0
                             NDPI_CONFIDENCE_DPI);
91
0
}
92
93
static int thrift_validate_method(char const * const method, size_t method_length)
94
0
{
95
0
  const union {
96
0
    uint8_t const * const ptr;
97
0
    char const * const str;
98
0
  } m = { .str = method };
99
100
0
  return ndpi_is_printable_buffer(m.ptr, method_length);
101
0
}
102
103
static int thrift_validate_version(uint8_t version)
104
0
{
105
0
  return version <= 0x01;
106
0
}
107
108
static int thrift_validate_type(uint8_t message_type)
109
0
{
110
0
  return message_type < TMT_TYPE_MAX;
111
0
}
112
113
static void thrift_set_method(struct ndpi_detection_module_struct *ndpi_struct,
114
                              struct ndpi_flow_struct *flow,
115
                              char const * const method, size_t method_length)
116
0
{
117
0
  if (thrift_validate_method(method, method_length) == 0) {
118
0
    ndpi_set_risk(ndpi_struct, flow, NDPI_INVALID_CHARACTERS, "Invalid method name");
119
0
    flow->protos.thrift.method[0] = '\0';
120
0
  } else {
121
0
    strncpy(flow->protos.thrift.method, method, ndpi_min(sizeof(flow->protos.thrift.method), method_length));
122
0
  }
123
0
}
124
125
static void thrift_set_type(struct ndpi_detection_module_struct *ndpi_struct,
126
                            struct ndpi_flow_struct *flow,
127
                            uint8_t message_type)
128
0
{
129
0
  if (message_type == TMT_INVALID_TMESSAGE_TYPE) {
130
0
    ndpi_set_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET, "Invalid message type");
131
0
  }
132
0
  flow->protos.thrift.message_type = message_type;
133
134
0
  if (message_type == TMT_EXCEPTION) {
135
0
    ndpi_set_risk(ndpi_struct, flow, NDPI_ERROR_CODE_DETECTED, "Apache Thrift Exception");
136
0
  }
137
0
}
138
139
static void ndpi_dissect_strict_hdr(struct ndpi_detection_module_struct *ndpi_struct,
140
                                    struct ndpi_flow_struct *flow,
141
                                    struct thrift_strict_hdr const * const strict_hdr)
142
0
{
143
0
  struct ndpi_packet_struct const * const packet = &ndpi_struct->packet;
144
0
  const size_t method_length = ntohl(strict_hdr->method_length);
145
146
0
  if (packet->tcp == NULL) {
147
0
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
148
0
    return;
149
0
  }
150
151
0
  if (packet->payload_packet_len < sizeof(*strict_hdr) + method_length) {
152
0
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
153
0
    return;
154
0
  }
155
156
0
  if (thrift_validate_version(strict_hdr->version) == 0) {
157
0
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
158
0
    return;
159
0
  }
160
161
0
  if (thrift_validate_type(strict_hdr->message_type) == 0) {
162
0
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
163
0
    return;
164
0
  }
165
166
0
  ndpi_int_thrift_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN);
167
168
0
  thrift_set_method(ndpi_struct, flow, strict_hdr->method, method_length);
169
0
  thrift_set_type(ndpi_struct, flow, strict_hdr->message_type);
170
0
}
171
172
static void ndpi_dissect_compact_hdr(struct ndpi_detection_module_struct *ndpi_struct,
173
                                     struct ndpi_flow_struct *flow,
174
                                     struct thrift_compact_hdr const * const compact_hdr)
175
0
{
176
0
  struct ndpi_packet_struct const * const packet = &ndpi_struct->packet;
177
178
0
  if (packet->udp == NULL) {
179
0
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
180
0
    return;
181
0
  }
182
183
0
  if (packet->payload_packet_len < sizeof(*compact_hdr) + compact_hdr->method_length) {
184
0
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
185
0
    return;
186
0
  }
187
188
0
  if (thrift_validate_version(compact_hdr->version) == 0) {
189
0
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
190
0
    return;
191
0
  }
192
193
0
  if (thrift_validate_type(compact_hdr->message_type) == 0) {
194
0
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
195
0
    return;
196
0
  }
197
198
0
  ndpi_int_thrift_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN);
199
200
0
  thrift_set_method(ndpi_struct, flow, compact_hdr->method, compact_hdr->method_length);
201
0
  thrift_set_type(ndpi_struct, flow, compact_hdr->message_type);
202
0
}
203
204
static void ndpi_search_thrift_tcp_udp(struct ndpi_detection_module_struct *ndpi_struct,
205
                                       struct ndpi_flow_struct *flow)
206
0
{
207
0
  struct ndpi_packet_struct const * const packet = &ndpi_struct->packet;
208
209
0
  NDPI_LOG_DBG(ndpi_struct, "search Apache Thrift\n");
210
211
0
  if (flow->detected_protocol_stack[0] == NDPI_PROTOCOL_HTTP ||
212
0
      flow->detected_protocol_stack[1] == NDPI_PROTOCOL_HTTP)
213
0
  {
214
    /* Check Thrift over HTTP */
215
0
    if (packet->content_line.ptr != NULL)
216
0
    {
217
0
      if ((LINE_ENDS(packet->content_line, "application/vnd.apache.thrift.binary") != 0) ||
218
0
          (LINE_ENDS(packet->content_line, "application/vnd.apache.thrift.compact") != 0) ||
219
0
          (LINE_ENDS(packet->content_line, "application/vnd.apache.thrift.json") != 0))
220
0
      {
221
0
        NDPI_LOG_INFO(ndpi_struct, "found Apache Thrift over HTTP\n");
222
0
        ndpi_int_thrift_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_HTTP);
223
0
        return;
224
0
      }
225
0
    }
226
0
  } else if (packet->payload_packet_len >= sizeof(struct thrift_compact_hdr)) {
227
0
    const union {
228
0
      uint8_t const * const raw_ptr;
229
0
      struct thrift_strict_hdr const * const strict_hdr;
230
0
      struct thrift_compact_hdr const * const compact_hdr;
231
0
    } thrift_data = { .raw_ptr = &packet->payload[0] };
232
233
0
    if (thrift_data.raw_ptr[0] == 0x80)
234
0
    {
235
      /* Strict Binary Protocol */
236
0
      if (packet->payload_packet_len < sizeof(*thrift_data.strict_hdr))
237
0
      {
238
0
        NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
239
0
        return;
240
0
      }
241
242
0
      ndpi_dissect_strict_hdr(ndpi_struct, flow, thrift_data.strict_hdr);
243
0
      return;
244
0
    } else if (thrift_data.raw_ptr[0] == 0x82) {
245
      /* Compact Protocol */
246
0
      ndpi_dissect_compact_hdr(ndpi_struct, flow, thrift_data.compact_hdr);
247
0
      return;
248
0
    } else {
249
      /* Probably not Apache Thrift. */
250
0
      NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
251
0
      return;
252
0
    }
253
0
  }
254
255
0
  NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
256
0
}
257
258
void init_apache_thrift_dissector(struct ndpi_detection_module_struct *ndpi_struct)
259
1
{
260
1
  register_dissector("Thrift", ndpi_struct,
261
1
                     ndpi_search_thrift_tcp_udp,
262
1
                     NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION,
263
1
                     1, NDPI_PROTOCOL_APACHE_THRIFT);
264
1
}