Skip to content

Commit

Permalink
Add Apache Thrift protocol dissector.
Browse files Browse the repository at this point in the history
Signed-off-by: Toni Uhlig <[email protected]>
  • Loading branch information
utoni committed Jun 21, 2023
1 parent cc58205 commit 4b06aaf
Show file tree
Hide file tree
Showing 182 changed files with 484 additions and 175 deletions.
1 change: 1 addition & 0 deletions src/include/ndpi_protocol_ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ typedef enum {
NDPI_PROTOCOL_NVIDIA = 342,
NDPI_PROTOCOL_BITCOIN = 343,
NDPI_PROTOCOL_PROTONVPN = 344,
NDPI_PROTOCOL_APACHE_THRIFT = 345,

#ifdef CUSTOM_NDPI_PROTOCOLS
#include "../../../nDPI-custom/custom_ndpi_protocol_ids.h"
Expand Down
1 change: 1 addition & 0 deletions src/include/ndpi_protocols.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ void init_bacnet_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_i
void init_oicq_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id);
void init_epicgames_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id);
void init_bitcoin_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id);
void init_apache_thrift_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id);

/* ndpi_main.c */
extern u_int32_t ndpi_ip_port_hash_funct(u_int32_t ip, u_int16_t port);
Expand Down
5 changes: 5 additions & 0 deletions src/include/ndpi_typedefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1596,6 +1596,11 @@ struct ndpi_flow_struct {
u_int16_t external_port;
ndpi_ip_addr_t external_address;
} natpmp;

struct {
u_int8_t message_type;
char method[64];
} thrift;
} protos;

/*** ALL protocol specific 64 bit variables here ***/
Expand Down
8 changes: 8 additions & 0 deletions src/lib/ndpi_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1108,6 +1108,7 @@ static void ndpi_init_protocol_defaults(struct ndpi_detection_module_struct *ndp
NDPI_PROTOCOL_IPP,
NDPI_PROTOCOL_MPEGDASH,
NDPI_PROTOCOL_RTSP,
NDPI_PROTOCOL_APACHE_THRIFT,
NDPI_PROTOCOL_MATCHED_BY_CONTENT,
NDPI_PROTOCOL_NO_MORE_SUBPROTOCOLS); /* NDPI_PROTOCOL_HTTP can have (content-matched) subprotocols */
ndpi_set_proto_defaults(ndpi_str, 1 /* cleartext */, 0 /* nw proto */, NDPI_PROTOCOL_ACCEPTABLE, NDPI_PROTOCOL_MDNS,
Expand Down Expand Up @@ -2097,6 +2098,10 @@ static void ndpi_init_protocol_defaults(struct ndpi_detection_module_struct *ndp
"ProtonVPN", NDPI_PROTOCOL_CATEGORY_VPN,
ndpi_build_default_ports(ports_a, 0, 0, 0, 0, 0) /* TCP */,
ndpi_build_default_ports(ports_b, 0, 0, 0, 0, 0) /* UDP */);
ndpi_set_proto_defaults(ndpi_str, 1 /* cleartext */, 0 /* nw proto */, NDPI_PROTOCOL_ACCEPTABLE, NDPI_PROTOCOL_APACHE_THRIFT,
"Thrift", NDPI_PROTOCOL_CATEGORY_RPC,
ndpi_build_default_ports(ports_a, 0, 0, 0, 0, 0) /* TCP */,
ndpi_build_default_ports(ports_b, 0, 0, 0, 0, 0) /* UDP */);


#ifdef CUSTOM_NDPI_PROTOCOLS
Expand Down Expand Up @@ -4976,6 +4981,9 @@ static int ndpi_callback_init(struct ndpi_detection_module_struct *ndpi_str) {
/*BITCOIN*/
init_bitcoin_dissector(ndpi_str, &a);

/* Apache Thrift */
init_apache_thrift_dissector(ndpi_str, &a);

#ifdef CUSTOM_NDPI_PROTOCOLS
#include "../../../nDPI-custom/custom_ndpi_main_init.c"
#endif
Expand Down
2 changes: 2 additions & 0 deletions src/lib/protocols/coap.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ struct ndpi_coap_hdr
u_int8_t version:2, type:2, tkl:4;
#elif defined(__LITTLE_ENDIAN__)
u_int8_t tkl:4, type:2, version:2;
#else
#error "Missing endian macro definitions."
#endif
u_int8_t code;
u_int16_t message_id; //if needed, remember to convert in host number
Expand Down
264 changes: 264 additions & 0 deletions src/lib/protocols/thrift.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
/*
* thrift.c
*
* Copyright (C) 2023 - ntop.org
*
* This file is part of nDPI, an open source deep packet inspection
* library based on the OpenDPI and PACE technology by ipoque GmbH
*
* nDPI is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nDPI is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with nDPI. If not, see <http://www.gnu.org/licenses/>.
*
*/

#include "ndpi_protocol_ids.h"

#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_APACHE_THRIFT

#include "ndpi_api.h"

#include <stdint.h>

// References: https://thrift.apache.org AND https://github.com/apache/thrift
// Not Implemented (sub)protocols: TJSONProtocol, TSimpleJSONProtocol and TDebugProtocol

// TBinaryProtocol
PACK_ON
struct thrift_strict_hdr {
uint8_t protocol_id;
uint8_t version;
uint8_t unused_byte_pad;
uint8_t message_type;
uint32_t method_length;
char method[0];
} PACK_OFF;

// TCompactProtocol
PACK_ON
struct thrift_compact_hdr {
uint8_t protocol_id;
#if defined(__BIG_ENDIAN__)
uint8_t message_type : 3;
uint8_t version : 5;
#elif defined(__LITTLE_ENDIAN__)
uint8_t version : 5;
uint8_t message_type : 3;
#else
#error "Missing endian macro definitions."
#endif
uint8_t sequence_id[3];
uint8_t method_length;
char method[0];
} PACK_OFF;

enum thrift_message_type {
TMT_INVALID_TMESSAGE_TYPE = 0,
TMT_CALL = 1,
TMT_REPLY = 2,
TMT_EXCEPTION = 3,
TMT_ONEWAY = 4,
TMT_TYPE_MAX
};

static void ndpi_int_thrift_add_connection(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow,
uint16_t master_protocol)
{
switch (master_protocol)
{
case NDPI_PROTOCOL_UNKNOWN:
NDPI_LOG_DBG(ndpi_struct, "found Apache Thrift TCP/UDP\n");
break;
case NDPI_PROTOCOL_HTTP:
NDPI_LOG_DBG(ndpi_struct, "found Apache Thrift HTTP\n");
break;
default:
NDPI_LOG_DBG(ndpi_struct, "found Apache Thrift\n");
break;
}

ndpi_set_detected_protocol(ndpi_struct, flow,
NDPI_PROTOCOL_APACHE_THRIFT, master_protocol,
NDPI_CONFIDENCE_DPI);
}

static int thrift_validate_method(char const * const method, size_t method_length)
{
return ndpi_is_printable_buffer((uint8_t const *)method, method_length);
}

static int thrift_validate_version(uint8_t version)
{
return version <= 0x01;
}

static int thrift_validate_type(uint8_t message_type)
{
return message_type < TMT_TYPE_MAX;
}

static void thrift_set_method(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow,
char const * const method, size_t method_length)
{
if (thrift_validate_method(method, method_length) == 0) {
ndpi_set_risk(ndpi_struct, flow, NDPI_INVALID_CHARACTERS, "Invalid method name");
flow->protos.thrift.method[0] = '\0';
} else {
strncpy(flow->protos.thrift.method, method, method_length);
}
}

static void thrift_set_type(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow,
uint8_t message_type)
{
if (message_type == TMT_INVALID_TMESSAGE_TYPE) {
ndpi_set_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET, "Invalid message type");
}
flow->protos.thrift.message_type = message_type;

if (message_type == TMT_EXCEPTION) {
ndpi_set_risk(ndpi_struct, flow, NDPI_ERROR_CODE_DETECTED, "Apache Thrift Exception");
}
}

static void ndpi_dissect_strict_hdr(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow,
struct thrift_strict_hdr const * const strict_hdr)
{
struct ndpi_packet_struct const * const packet = &ndpi_struct->packet;
const size_t method_length = ntohl(strict_hdr->method_length);

if (packet->tcp == NULL) {
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
return;
}

if (packet->payload_packet_len < sizeof(*strict_hdr) + method_length) {
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
return;
}

if (thrift_validate_version(strict_hdr->version) == 0) {
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
return;
}

if (thrift_validate_type(strict_hdr->message_type) == 0) {
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
return;
}

ndpi_int_thrift_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN);

thrift_set_method(ndpi_struct, flow, strict_hdr->method, method_length);
thrift_set_type(ndpi_struct, flow, strict_hdr->message_type);
}

static void ndpi_dissect_compact_hdr(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow,
struct thrift_compact_hdr const * const compact_hdr)
{
struct ndpi_packet_struct const * const packet = &ndpi_struct->packet;

if (packet->udp == NULL) {
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
return;
}

if (packet->payload_packet_len < sizeof(*compact_hdr) + compact_hdr->method_length) {
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
return;
}

if (thrift_validate_version(compact_hdr->version) == 0) {
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
return;
}

if (thrift_validate_type(compact_hdr->message_type) == 0) {
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
return;
}

ndpi_int_thrift_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN);

thrift_set_method(ndpi_struct, flow, compact_hdr->method, compact_hdr->method_length);
thrift_set_type(ndpi_struct, flow, compact_hdr->message_type);
}

static void ndpi_search_thrift_tcp_udp(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow)
{
struct ndpi_packet_struct const * const packet = &ndpi_struct->packet;

NDPI_LOG_DBG(ndpi_struct, "search Apache Thrift\n");

if (flow->detected_protocol_stack[0] == NDPI_PROTOCOL_HTTP ||
flow->detected_protocol_stack[1] == NDPI_PROTOCOL_HTTP)
{
/* Check Thrift over HTTP */
if (packet->content_line.ptr != NULL)
{
if ((LINE_ENDS(packet->content_line, "application/vnd.apache.thrift.binary") != 0) ||
(LINE_ENDS(packet->content_line, "application/vnd.apache.thrift.compact") != 0) ||
(LINE_ENDS(packet->content_line, "application/vnd.apache.thrift.json") != 0))
{
NDPI_LOG_INFO(ndpi_struct, "found Apache Thrift over HTTP\n");
ndpi_int_thrift_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_HTTP);
return;
}
}
} else if (packet->payload_packet_len >= sizeof(struct thrift_compact_hdr)) {
const union {
uint8_t const * const raw_ptr;
struct thrift_strict_hdr const * const strict_hdr;
struct thrift_compact_hdr const * const compact_hdr;
} thrift_data = { .raw_ptr = &packet->payload[0] };

if (thrift_data.raw_ptr[0] == 0x80)
{
/* Strict Binary Protocol */
if (packet->payload_packet_len < sizeof(*thrift_data.strict_hdr))
{
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
return;
}

ndpi_dissect_strict_hdr(ndpi_struct, flow, thrift_data.strict_hdr);
return;
} else if (thrift_data.raw_ptr[0] == 0x82) {
/* Compact Protocol */
ndpi_dissect_compact_hdr(ndpi_struct, flow, thrift_data.compact_hdr);
return;
} else {
/* Probably not Apache Thrift. */
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
return;
}
}

NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
}

void init_apache_thrift_dissector(struct ndpi_detection_module_struct *ndpi_struct, uint32_t *id)
{
ndpi_set_bitmask_protocol_detection("Thrift", ndpi_struct, *id,
NDPI_PROTOCOL_APACHE_THRIFT,
ndpi_search_thrift_tcp_udp,
NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION,
SAVE_DETECTION_BITMASK_AS_UNKNOWN,
ADD_TO_DETECTION_BITMASK);
*id += 1;
}
2 changes: 1 addition & 1 deletion tests/cfgs/caches_cfg/result/ookla.pcap.out
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Guessed flow protos: 1
DPI Packets (TCP): 40 (6.67 pkts/flow)
Confidence Match by port : 1 (flows)
Confidence DPI : 5 (flows)
Num dissector calls: 489 (81.50 diss/flow)
Num dissector calls: 493 (82.17 diss/flow)
LRU cache ookla: 0/0/0 (insert/search/found)
LRU cache bittorrent: 0/3/0 (insert/search/found)
LRU cache zoom: 0/0/0 (insert/search/found)
Expand Down
2 changes: 1 addition & 1 deletion tests/cfgs/caches_cfg/result/teams.pcap.out
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Confidence Unknown : 1 (flows)
Confidence Match by port : 1 (flows)
Confidence DPI (partial) : 1 (flows)
Confidence DPI : 80 (flows)
Num dissector calls: 495 (5.96 diss/flow)
Num dissector calls: 497 (5.99 diss/flow)
LRU cache ookla: 0/0/0 (insert/search/found)
LRU cache bittorrent: 0/9/0 (insert/search/found)
LRU cache zoom: 0/0/0 (insert/search/found)
Expand Down
Binary file added tests/cfgs/default/pcap/thrift.pcap
Binary file not shown.
2 changes: 1 addition & 1 deletion tests/cfgs/default/result/1kxun.pcap.out
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ DPI Packets (UDP): 120 (1.21 pkts/flow)
Confidence Unknown : 14 (flows)
Confidence Match by port : 6 (flows)
Confidence DPI : 177 (flows)
Num dissector calls: 4431 (22.49 diss/flow)
Num dissector calls: 4534 (23.02 diss/flow)
LRU cache ookla: 0/0/0 (insert/search/found)
LRU cache bittorrent: 0/60/0 (insert/search/found)
LRU cache zoom: 0/0/0 (insert/search/found)
Expand Down
2 changes: 1 addition & 1 deletion tests/cfgs/default/result/443-chrome.pcap.out
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Guessed flow protos: 1

DPI Packets (TCP): 1 (1.00 pkts/flow)
Confidence Match by port : 1 (flows)
Num dissector calls: 117 (117.00 diss/flow)
Num dissector calls: 118 (118.00 diss/flow)
LRU cache ookla: 0/0/0 (insert/search/found)
LRU cache bittorrent: 0/3/0 (insert/search/found)
LRU cache zoom: 0/0/0 (insert/search/found)
Expand Down
2 changes: 1 addition & 1 deletion tests/cfgs/default/result/443-opvn.pcap.out
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Guessed flow protos: 0

DPI Packets (TCP): 6 (6.00 pkts/flow)
Confidence DPI : 1 (flows)
Num dissector calls: 118 (118.00 diss/flow)
Num dissector calls: 119 (119.00 diss/flow)
LRU cache ookla: 0/0/0 (insert/search/found)
LRU cache bittorrent: 0/0/0 (insert/search/found)
LRU cache zoom: 0/0/0 (insert/search/found)
Expand Down
2 changes: 1 addition & 1 deletion tests/cfgs/default/result/4in4tunnel.pcap.out
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Guessed flow protos: 1

DPI Packets (UDP): 5 (5.00 pkts/flow)
Confidence Unknown : 1 (flows)
Num dissector calls: 175 (175.00 diss/flow)
Num dissector calls: 176 (176.00 diss/flow)
LRU cache ookla: 0/0/0 (insert/search/found)
LRU cache bittorrent: 0/3/0 (insert/search/found)
LRU cache zoom: 0/0/0 (insert/search/found)
Expand Down
2 changes: 1 addition & 1 deletion tests/cfgs/default/result/6in4tunnel.pcap.out
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ DPI Packets (TCP): 29 (5.80 pkts/flow)
DPI Packets (UDP): 4 (2.00 pkts/flow)
DPI Packets (other): 3 (1.00 pkts/flow)
Confidence DPI : 10 (flows)
Num dissector calls: 25 (2.50 diss/flow)
Num dissector calls: 26 (2.60 diss/flow)
LRU cache ookla: 0/0/0 (insert/search/found)
LRU cache bittorrent: 0/0/0 (insert/search/found)
LRU cache zoom: 0/0/0 (insert/search/found)
Expand Down
Loading

0 comments on commit 4b06aaf

Please sign in to comment.