aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/include/ndpi_main.h3
-rw-r--r--src/include/ndpi_protocol_ids.h1
-rw-r--r--src/include/ndpi_typedefs.h2
-rw-r--r--src/lib/ndpi_main.c21
-rw-r--r--src/lib/ndpi_utils.c10
-rw-r--r--src/lib/protocols/rtp.c17
-rw-r--r--src/lib/protocols/stun.c834
7 files changed, 423 insertions, 465 deletions
diff --git a/src/include/ndpi_main.h b/src/include/ndpi_main.h
index b4ef20f5f..f52d9c959 100644
--- a/src/include/ndpi_main.h
+++ b/src/include/ndpi_main.h
@@ -84,6 +84,9 @@ extern "C" {
u_int16_t lower_detected_protocol,
ndpi_confidence_t confidence);
+ void ndpi_reset_detected_protocol(struct ndpi_detection_module_struct *ndpi_struct,
+ struct ndpi_flow_struct *flow);
+
void ndpi_set_detected_protocol_keeping_master(struct ndpi_detection_module_struct *ndpi_str,
struct ndpi_flow_struct *flow,
u_int16_t detected_protocol,
diff --git a/src/include/ndpi_protocol_ids.h b/src/include/ndpi_protocol_ids.h
index 278a6714d..8a9e093db 100644
--- a/src/include/ndpi_protocol_ids.h
+++ b/src/include/ndpi_protocol_ids.h
@@ -383,6 +383,7 @@ typedef enum {
NDPI_PROTOCOL_CAN = 352,
NDPI_PROTOCOL_PROTOBUF = 353,
NDPI_PROTOCOL_ETHEREUM = 354,
+ NDPI_PROTOCOL_TELEGRAM_VOIP = 355,
#ifdef CUSTOM_NDPI_PROTOCOLS
#include "../../../nDPI-custom/custom_ndpi_protocol_ids.h"
diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h
index 365638aa3..8b1874f38 100644
--- a/src/include/ndpi_typedefs.h
+++ b/src/include/ndpi_typedefs.h
@@ -1533,7 +1533,7 @@ struct ndpi_flow_struct {
} kerberos_buf;
struct {
- u_int8_t num_pkts, num_binding_requests, num_processed_pkts, maybe_dtls;
+ u_int8_t maybe_dtls;
} stun;
struct {
diff --git a/src/lib/ndpi_main.c b/src/lib/ndpi_main.c
index b14297c16..cd457c3d9 100644
--- a/src/lib/ndpi_main.c
+++ b/src/lib/ndpi_main.c
@@ -2176,6 +2176,11 @@ static void ndpi_init_protocol_defaults(struct ndpi_detection_module_struct *ndp
"ETHEREUM", NDPI_PROTOCOL_CATEGORY_CRYPTO_CURRENCY,
ndpi_build_default_ports(ports_a, 30303, 0, 0, 0, 0) /* TCP */,
ndpi_build_default_ports(ports_b, 0, 0, 0, 0, 0) /* UDP */);
+ ndpi_set_proto_defaults(ndpi_str, 0 /* encrypted */, 1 /* app proto */, NDPI_PROTOCOL_ACCEPTABLE, NDPI_PROTOCOL_TELEGRAM_VOIP,
+ "TelegramVoip", NDPI_PROTOCOL_CATEGORY_VOIP,
+ 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
#include "../../../nDPI-custom/custom_ndpi_main.c"
@@ -7082,14 +7087,6 @@ ndpi_protocol ndpi_detection_giveup(struct ndpi_detection_module_struct *ndpi_st
if(ret.app_protocol != NDPI_PROTOCOL_UNKNOWN)
return(ret);
- if((flow->guessed_protocol_id == NDPI_PROTOCOL_STUN) ||
- (enable_guess &&
- flow->stun.num_binding_requests > 0 &&
- flow->stun.num_processed_pkts > 0)) {
- ndpi_set_detected_protocol(ndpi_str, flow, NDPI_PROTOCOL_STUN, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI_PARTIAL);
- ret.app_protocol = flow->detected_protocol_stack[0];
- }
-
/* Check some caches */
/* Does it looks like BitTorrent? */
@@ -8582,6 +8579,14 @@ void ndpi_set_detected_protocol(struct ndpi_detection_module_struct *ndpi_str, s
/* ********************************************************************************* */
+void ndpi_reset_detected_protocol(struct ndpi_detection_module_struct *ndpi_str, struct ndpi_flow_struct *flow) {
+ flow->detected_protocol_stack[1] = NDPI_PROTOCOL_UNKNOWN;
+ flow->detected_protocol_stack[0] = NDPI_PROTOCOL_UNKNOWN;
+ flow->confidence = NDPI_CONFIDENCE_UNKNOWN;
+}
+
+/* ********************************************************************************* */
+
u_int16_t ndpi_get_flow_masterprotocol(struct ndpi_detection_module_struct *ndpi_str, struct ndpi_flow_struct *flow) {
return(flow->detected_protocol_stack[1]);
}
diff --git a/src/lib/ndpi_utils.c b/src/lib/ndpi_utils.c
index 7fbecd9f9..645dfc12a 100644
--- a/src/lib/ndpi_utils.c
+++ b/src/lib/ndpi_utils.c
@@ -1373,16 +1373,6 @@ int ndpi_dpi2json(struct ndpi_detection_module_struct *ndpi_struct,
ndpi_serialize_end_of_block(serializer);
break;
- case NDPI_PROTOCOL_STUN:
- ndpi_serialize_start_of_block(serializer, "stun");
- ndpi_serialize_string_uint32(serializer, "num_pkts", flow->stun.num_pkts);
- ndpi_serialize_string_uint32(serializer, "num_binding_requests",
- flow->stun.num_binding_requests);
- ndpi_serialize_string_uint32(serializer, "num_processed_pkts",
- flow->stun.num_processed_pkts);
- ndpi_serialize_end_of_block(serializer);
- break;
-
case NDPI_PROTOCOL_TELNET:
ndpi_serialize_start_of_block(serializer, "telnet");
ndpi_serialize_string_string(serializer, "username", flow->protos.telnet.username);
diff --git a/src/lib/protocols/rtp.c b/src/lib/protocols/rtp.c
index 9d48aecb1..1cc112be4 100644
--- a/src/lib/protocols/rtp.c
+++ b/src/lib/protocols/rtp.c
@@ -282,19 +282,10 @@ static void ndpi_rtp_search(struct ndpi_detection_module_struct *ndpi_struct,
} else {
rtp_get_stream_type(payload[1] & 0x7F, &flow->flow_multimedia_type);
- /* Previous pkts were STUN */
- if(flow->stun.num_binding_requests > 0 ||
- flow->stun.num_processed_pkts > 0) {
- NDPI_LOG_INFO(ndpi_struct, "Found RTP (previous traffic was STUN)\n");
- ndpi_set_detected_protocol(ndpi_struct, flow,
- NDPI_PROTOCOL_RTP, NDPI_PROTOCOL_STUN,
- NDPI_CONFIDENCE_DPI);
- } else {
- NDPI_LOG_INFO(ndpi_struct, "Found RTP\n");
- ndpi_set_detected_protocol(ndpi_struct, flow,
- NDPI_PROTOCOL_UNKNOWN, NDPI_PROTOCOL_RTP,
- NDPI_CONFIDENCE_DPI);
- }
+ NDPI_LOG_INFO(ndpi_struct, "Found RTP\n");
+ ndpi_set_detected_protocol(ndpi_struct, flow,
+ NDPI_PROTOCOL_UNKNOWN, NDPI_PROTOCOL_RTP,
+ NDPI_CONFIDENCE_DPI);
}
return;
}
diff --git a/src/lib/protocols/stun.c b/src/lib/protocols/stun.c
index 2914a0814..0f3810b81 100644
--- a/src/lib/protocols/stun.c
+++ b/src/lib/protocols/stun.c
@@ -27,12 +27,8 @@
#include "ndpi_api.h"
-#define MAX_NUM_STUN_PKTS 3
-
-// #define DEBUG_STUN 1
// #define DEBUG_LRU 1
// #define DEBUG_ZOOM_LRU 1
-// #define DEBUG_MONITORING 1
#define STUN_HDR_LEN 20 /* STUN message header length, Classic-STUN (RFC 3489) and STUN (RFC 8489) both */
@@ -43,17 +39,323 @@ extern int is_rtp_or_rtcp(struct ndpi_detection_module_struct *ndpi_struct,
extern u_int8_t rtp_get_stream_type(u_int8_t payloadType, ndpi_multimedia_flow_type *s_type);
extern int is_dtls(const u_int8_t *buf, u_int32_t buf_len, u_int32_t *block_len);
-static int stun_monitoring(struct ndpi_detection_module_struct *ndpi_struct,
- struct ndpi_flow_struct *flow)
+static u_int32_t get_stun_lru_key(struct ndpi_flow_struct *flow, u_int8_t rev);
+static u_int32_t get_stun_lru_key_raw4(u_int32_t ip, u_int16_t port);
+static void ndpi_int_stun_add_connection(struct ndpi_detection_module_struct *ndpi_struct,
+ struct ndpi_flow_struct *flow,
+ u_int app_proto);
+
+
+static u_int16_t search_into_cache(struct ndpi_detection_module_struct *ndpi_struct,
+ struct ndpi_flow_struct *flow)
+{
+ u_int16_t proto;
+ u_int32_t key;
+ int rc;
+
+ if(ndpi_struct->stun_cache) {
+ key = get_stun_lru_key(flow, 0);
+ rc = ndpi_lru_find_cache(ndpi_struct->stun_cache, key, &proto,
+ 0 /* Don't remove it as it can be used for other connections */,
+ ndpi_get_current_time(flow));
+#ifdef DEBUG_LRU
+ printf("[LRU] Searching %u\n", key);
+#endif
+
+ if(!rc) {
+ key = get_stun_lru_key(flow, 1);
+ rc = ndpi_lru_find_cache(ndpi_struct->stun_cache, key, &proto,
+ 0 /* Don't remove it as it can be used for other connections */,
+ ndpi_get_current_time(flow));
+#ifdef DEBUG_LRU
+ printf("[LRU] Searching %u\n", key);
+#endif
+ }
+
+ if(rc) {
+#ifdef DEBUG_LRU
+ printf("[LRU] Cache FOUND %u / %u\n", key, proto);
+#endif
+
+ return proto;
+ } else {
+#ifdef DEBUG_LRU
+ printf("[LRU] NOT FOUND %u\n", key);
+#endif
+ }
+ } else {
+#ifdef DEBUG_LRU
+ printf("[LRU] NO/EMPTY CACHE\n");
+#endif
+ }
+ return NDPI_PROTOCOL_UNKNOWN;
+}
+
+static void add_to_caches(struct ndpi_detection_module_struct *ndpi_struct,
+ struct ndpi_flow_struct *flow,
+ u_int16_t app_proto)
+{
+ u_int32_t key, key_rev;
+
+ if(ndpi_struct->stun_cache &&
+ app_proto != NDPI_PROTOCOL_STUN &&
+ app_proto != NDPI_PROTOCOL_UNKNOWN) {
+ /* No sense to add STUN, but only subprotocols */
+
+ key = get_stun_lru_key(flow, 0);
+ ndpi_lru_add_to_cache(ndpi_struct->stun_cache, key, app_proto, ndpi_get_current_time(flow));
+ key_rev = get_stun_lru_key(flow, 1);
+ ndpi_lru_add_to_cache(ndpi_struct->stun_cache, key_rev, app_proto, ndpi_get_current_time(flow));
+
+#ifdef DEBUG_LRU
+ printf("[LRU] ADDING %u 0x%x app %u [%u -> %u]\n", key, key_rev, app_proto,
+ ntohs(flow->c_port), ntohs(flow->s_port));
+#endif
+ }
+
+ /* TODO: extend to other protocols? */
+ if(ndpi_struct->stun_zoom_cache &&
+ app_proto == NDPI_PROTOCOL_ZOOM &&
+ flow->l4_proto == IPPROTO_UDP) {
+ key = get_stun_lru_key(flow, 0); /* Src */
+ ndpi_lru_add_to_cache(ndpi_struct->stun_zoom_cache, key,
+ 0 /* dummy */, ndpi_get_current_time(flow));
+
+#ifdef DEBUG_ZOOM_LRU
+ printf("[LRU ZOOM] ADDING %u [src_port %u]\n", key, ntohs(flow->c_port));
+#endif
+ }
+}
+
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+static
+#endif
+int is_stun(struct ndpi_detection_module_struct *ndpi_struct,
+ struct ndpi_flow_struct *flow,
+ u_int16_t *app_proto)
+{
+ struct ndpi_packet_struct *packet = &ndpi_struct->packet;
+ u_int16_t msg_type, msg_len;
+ int off;
+ const u_int8_t *payload = packet->payload;
+ u_int16_t payload_length = packet->payload_packet_len;
+ u_int32_t magic_cookie;
+
+ if(payload_length < STUN_HDR_LEN) {
+ return 0;
+ }
+
+ /* Some really old/legacy stuff */
+ if(strncmp((const char *)payload, "RSP/", 4) == 0 &&
+ strncmp((const char *)&payload[7], " STUN_", 6) == 0) {
+ NDPI_LOG_DBG(ndpi_struct, "found old/legacy stun in rsp\n");
+ return 1; /* No real metadata */
+ }
+
+ /* STUN may be encapsulated in TCP packets with a special TCP framing described in RFC 4571 */
+ if(packet->tcp &&
+ payload_length >= STUN_HDR_LEN + 2 &&
+ /* TODO: multiple STUN messagges */
+ ((ntohs(get_u_int16_t(payload, 0)) + 2) == payload_length)) {
+ payload += 2;
+ payload_length -=2;
+ }
+
+ msg_type = ntohs(*((u_int16_t *)&payload[0]));
+ msg_len = ntohs(*((u_int16_t *)&payload[2]));
+ magic_cookie = ntohl(*((u_int32_t *)&payload[4]));
+
+ /* No magic_cookie on classic-stun */
+ /* Let's hope that we don't have anymore classic-stun over TCP */
+ if(packet->tcp && magic_cookie != 0x2112A442) {
+ return 0;
+ }
+
+ NDPI_LOG_DBG2(ndpi_struct, "msg_type = %04X msg_len = %d\n", msg_type, msg_len);
+
+ /* With tcp, we might have multiple msg in the same TCP pkt.
+ Parse only the first one. TODO */
+ if(packet->tcp) {
+ if(msg_len + STUN_HDR_LEN > payload_length)
+ return 0;
+ payload_length = msg_len + STUN_HDR_LEN;
+ }
+
+ if(msg_type == 0 || (msg_len + STUN_HDR_LEN != payload_length)) {
+ NDPI_LOG_DBG(ndpi_struct, "Invalid msg_type = %04X or len %d %d\n",
+ msg_type, msg_len, payload_length);
+ return 0;
+ }
+
+ /* https://www.iana.org/assignments/stun-parameters/stun-parameters.xhtml */
+ if(((msg_type & 0x3EEF) > 0x000B) &&
+ msg_type != 0x0800 && msg_type != 0x0801 && msg_type != 0x0802) {
+ NDPI_LOG_DBG(ndpi_struct, "Invalid msg_type = %04X\n", msg_type);
+ return 0;
+ }
+
+ if(magic_cookie != 0x2112A442) {
+ /* Some heuristic to detect classic-stun: let's see if attributes list seems ok */
+ off = STUN_HDR_LEN;
+ while(off + 4 < payload_length) {
+ u_int16_t len = ntohs(*((u_int16_t *)&payload[off + 2]));
+ u_int16_t real_len = (len + 3) & 0xFFFFFFFC;
+
+ off += 4 + real_len;
+ }
+ if(off != payload_length) {
+ NDPI_LOG_DBG(ndpi_struct, "No classic-stun %d/%d\n", off, payload_length);
+ return 0;
+ }
+ }
+
+ /* STUN */
+
+ if(msg_type == 0x0800 || msg_type == 0x0801 || msg_type == 0x0802) {
+ *app_proto = NDPI_PROTOCOL_WHATSAPP_CALL;
+ return 1;
+ }
+
+ off = STUN_HDR_LEN;
+ while(off + 4 < payload_length) {
+ u_int16_t attribute = ntohs(*((u_int16_t *)&payload[off]));
+ u_int16_t len = ntohs(*((u_int16_t *)&payload[off + 2]));
+ u_int16_t real_len = (len + 3) & 0xFFFFFFFC;
+
+ NDPI_LOG_DBG(ndpi_struct, "Attribute 0x%x (%d/%d)\n", attribute, len, real_len);
+
+ switch(attribute) {
+ case 0x0012: /* XOR-PEER-ADDRESS */
+ if(off + 12 < payload_length &&
+ len == 8 && payload[off + 5] == 0x01) { /* TODO: ipv6 */
+ u_int16_t port;
+ u_int32_t ip;
+#ifdef NDPI_ENABLE_DEBUG_MESSAGES
+ char buf[128];
+#endif
+
+ port = ntohs(*((u_int16_t *)&payload[off + 6])) ^ (magic_cookie >> 16);
+ ip = *((u_int32_t *)&payload[off + 8]) ^ htonl(magic_cookie);
+
+ NDPI_LOG_DBG(ndpi_struct, "Peer %s:%d [proto %d]\n",
+ inet_ntop(AF_INET, &ip, buf, sizeof(buf)), port,
+ flow->detected_protocol_stack[0]);
+
+ if(1 /* TODO: enable/disable */ &&
+ ndpi_struct->stun_cache) {
+ u_int32_t key = get_stun_lru_key_raw4(ip, port);
+
+ ndpi_lru_add_to_cache(ndpi_struct->stun_cache, key,
+ flow->detected_protocol_stack[0],
+ ndpi_get_current_time(flow));
+#ifdef DEBUG_LRU
+ printf("[LRU] Add peer %u %d\n", key, flow->detected_protocol_stack[0]);
+#endif
+ }
+ }
+ break;
+
+ case 0x0101:
+ case 0x0103:
+ *app_proto = NDPI_PROTOCOL_ZOOM;
+ return 1;
+
+ case 0x4000:
+ case 0x4001:
+ case 0x4002:
+ case 0x4003:
+ case 0x4004:
+ case 0x4007:
+ /* These are the only messages apparently whatsapp voice can use */
+ *app_proto = NDPI_PROTOCOL_WHATSAPP_CALL;
+ return 1;
+
+ case 0x0014: /* Realm */
+ if(flow->host_server_name[0] == '\0') {
+ ndpi_hostname_sni_set(flow, payload + off + 4, ndpi_min(len, payload_length - off - 4));
+ NDPI_LOG_DBG(ndpi_struct, "Realm [%s]\n", flow->host_server_name);
+
+ if(strstr(flow->host_server_name, "google.com") != NULL) {
+ *app_proto = NDPI_PROTOCOL_HANGOUT_DUO;
+ return 1;
+ } else if(strstr(flow->host_server_name, "whispersystems.org") != NULL ||
+ strstr(flow->host_server_name, "signal.org") != NULL) {
+ *app_proto = NDPI_PROTOCOL_SIGNAL_VOIP;
+ return 1;
+ } else if(strstr(flow->host_server_name, "facebook") != NULL) {
+ *app_proto = NDPI_PROTOCOL_FACEBOOK_VOIP;
+ return 1;
+ } else if(strstr(flow->host_server_name, "stripcdn.com") != NULL) {
+ *app_proto = NDPI_PROTOCOL_ADULT_CONTENT;
+ return 1;
+ } else if(strstr(flow->host_server_name, "telegram") != NULL) {
+ *app_proto = NDPI_PROTOCOL_TELEGRAM_VOIP;
+ return 1;
+ }
+ }
+ break;
+
+ /* Proprietary fields found on skype calls */
+ case 0x8054: /* Candidate Identifier: Either skype for business or "normal" skype with multiparty call */
+ case 0x24DF:
+ case 0x3802:
+ case 0x8036:
+ case 0x8095: /* MS-Multiplexed-TURN-Session-ID */
+ case 0x0800:
+ case 0x8006:
+ case 0x8070: /* MS Implementation Version */
+ case 0x8055: /* MS Service Quality */
+ *app_proto = NDPI_PROTOCOL_SKYPE_TEAMS_CALL;
+ return 1;
+
+ case 0xFF03:
+ *app_proto = NDPI_PROTOCOL_HANGOUT_DUO;
+ return 1;
+
+ default:
+ NDPI_LOG_DBG2(ndpi_struct, "Unknown attribute %04X\n", attribute);
+ break;
+ }
+
+ off += 4 + real_len;
+ }
+
+ return 1;
+}
+
+static int keep_extra_dissection(struct ndpi_detection_module_struct *ndpi_struct,
+ struct ndpi_flow_struct *flow)
+{
+ if(flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN /* No subclassification */)
+ return 1;
+
+ /* We have a sub-classification */
+
+ if((ndpi_struct->monitoring_stun_flags & NDPI_MONITORING_STUN_SUBCLASSIFIED) &&
+ flow->detected_protocol_stack[1] != NDPI_PROTOCOL_RTP)
+ return 1;
+
+ /* Looking for XOR-PEER-ADDRESS metadata; TODO: other protocols? */
+ if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_TELEGRAM_VOIP)
+ return 1;
+ return 0;
+}
+
+static int stun_search_again(struct ndpi_detection_module_struct *ndpi_struct,
+ struct ndpi_flow_struct *flow)
{
struct ndpi_packet_struct *packet = &ndpi_struct->packet;
int rtp_rtcp;
u_int8_t first_byte;
+ u_int16_t app_proto = NDPI_PROTOCOL_UNKNOWN;
+ u_int32_t unused;
-#ifdef DEBUG_MONITORING
- printf("[STUN-MON] Packet counter %d protos %d/%d\n", flow->packet_counter,
- flow->detected_protocol_stack[0], flow->detected_protocol_stack[1]);
-#endif
+ NDPI_LOG_DBG2(ndpi_struct, "Packet counter %d protos %d/%d\n", flow->packet_counter,
+ flow->detected_protocol_stack[0], flow->detected_protocol_stack[1]);
+
+ /* TODO: check TCP support. We need to pay some attention because:
+ * multiple msg in the same TCP segment
+ * same msg split across multiple segments */
if(packet->payload_packet_len == 0)
return 1;
@@ -62,33 +364,45 @@ static int stun_monitoring(struct ndpi_detection_module_struct *ndpi_struct,
/* draft-ietf-avtcore-rfc7983bis */
if(first_byte <= 3) {
-#ifdef DEBUG_MONITORING
- printf("[STUN-MON] Still STUN\n");
-#endif
- return 1;
+ NDPI_LOG_DBG(ndpi_struct, "Still STUN\n");
+ if(is_stun(ndpi_struct, flow, &app_proto) /* To extract other metadata */ &&
+ flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN /* No previous subclassification */) {
+ ndpi_int_stun_add_connection(ndpi_struct, flow, app_proto);
+ /* TODO */
+ ndpi_protocol ret = { NDPI_PROTOCOL_STUN, app_proto, NDPI_PROTOCOL_UNKNOWN /* unused */, NDPI_PROTOCOL_CATEGORY_UNSPECIFIED, NULL};
+ flow->category = ndpi_get_proto_category(ndpi_struct, ret);
+ }
} else if(first_byte <= 19) {
-#ifdef DEBUG_MONITORING
- printf("[STUN-MON] DROP or ZRTP range. Unexpected but keep looking\n");
-#endif
- return 1;
+ NDPI_LOG_DBG(ndpi_struct, "DROP or ZRTP range. Unexpected\n");
} else if(first_byte <= 63) {
-#ifdef DEBUG_MONITORING
- printf("[STUN-MON] DTLS\n");
-#endif
- /* TODO */
- return 1;
+ NDPI_LOG_DBG(ndpi_struct, "DTLS\n");
+ if(is_dtls(packet->payload, packet->payload_packet_len, &unused) &&
+ flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN /* No previous subclassification */) {
+ /* Switching to TLS dissector is tricky, because we are calling one dissector
+ from another one, and that is not a common operation...
+ Additionally:
+ * at that point protocol stack is already set to STUN
+ * we have room for only two protocols in flow->detected_protocol_stack[] so
+ we can't have something like STUN/DTLS/SNAPCHAT_CALL
+ * the easiest (!?) solution is to remove STUN, and let TLS dissector to set both
+ master (i.e. DTLS) and subprotocol (if any) */
+ if(ndpi_struct->opportunistic_tls_stun_enabled) {
+ /* TODO: right way? It is a bit scary... do we need to reset something else too? */
+ ndpi_reset_detected_protocol(ndpi_struct, flow);
+ ndpi_int_change_category(ndpi_struct, flow, NDPI_PROTOCOL_CATEGORY_UNSPECIFIED);
+
+ flow->stun.maybe_dtls = 1;
+ NDPI_LOG_DBG(ndpi_struct, "Switch to TLS\n");
+ switch_to_tls(ndpi_struct, flow);
+ }
+ }
} else if(first_byte <= 127) {
-#ifdef DEBUG_MONITORING
- printf("[STUN-MON] QUIC or TURN range. Unexpected but keep looking\n");
-#endif
- return 1;
+ NDPI_LOG_DBG(ndpi_struct, "QUIC or TURN range. Unexpected\n");
} else if(first_byte <= 191) {
rtp_rtcp = is_rtp_or_rtcp(ndpi_struct, flow);
if(rtp_rtcp == IS_RTP) {
-#ifdef DEBUG_MONITORING
- printf("[STUN-MON] RTP (dir %d)\n", packet->packet_direction);
-#endif
+ NDPI_LOG_DBG(ndpi_struct, "RTP (dir %d)\n", packet->packet_direction);
NDPI_LOG_INFO(ndpi_struct, "Found RTP over STUN\n");
rtp_get_stream_type(packet->payload[1] & 0x7F, &flow->flow_multimedia_type);
@@ -106,27 +420,19 @@ static int stun_monitoring(struct ndpi_detection_module_struct *ndpi_struct,
}
return 0; /* Stop */
} else if(rtp_rtcp == IS_RTCP) {
-#ifdef DEBUG_MONITORING
- printf("[STUN-MON] RTCP\n");
-#endif
- return 1;
+ NDPI_LOG_DBG(ndpi_struct, "RTCP\n");
} else {
-#ifdef DEBUG_MONITORING
- printf("[STUN-MON] Unexpected\n");
-#endif
- return 1;
+ NDPI_LOG_DBG(ndpi_struct, "Unexpected\n");
}
} else {
-#ifdef DEBUG_MONITORING
- printf("[STUN-MON] QUIC range. Unexpected but keep looking\n");
-#endif
- return 1;
+ NDPI_LOG_DBG(ndpi_struct, "QUIC range. Unexpected\n");
}
+ return keep_extra_dissection(ndpi_struct, flow);
}
/* ************************************************************ */
-u_int32_t get_stun_lru_key(struct ndpi_flow_struct *flow, u_int8_t rev) {
+static u_int32_t get_stun_lru_key(struct ndpi_flow_struct *flow, u_int8_t rev) {
if(rev) {
if(flow->is_ipv6)
return ndpi_quick_hash(flow->s_address.v6, 16) + ntohs(flow->s_port);
@@ -142,6 +448,12 @@ u_int32_t get_stun_lru_key(struct ndpi_flow_struct *flow, u_int8_t rev) {
/* ************************************************************ */
+static u_int32_t get_stun_lru_key_raw4(u_int32_t ip, u_int16_t port_host_order) {
+ return ntohl(ip) + port_host_order;
+}
+
+/* ************************************************************ */
+
int stun_search_into_zoom_cache(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow)
{
@@ -205,387 +517,52 @@ static void ndpi_int_stun_add_connection(struct ndpi_detection_module_struct *nd
}
}
- if(ndpi_struct->stun_cache
- && (app_proto != NDPI_PROTOCOL_UNKNOWN)
- ) /* Cache flow sender info */ {
- u_int32_t key = get_stun_lru_key(flow, 0);
- u_int16_t cached_proto;
+ if(app_proto == NDPI_PROTOCOL_UNKNOWN) {
+ app_proto = search_into_cache(ndpi_struct, flow);
+ if(app_proto != NDPI_PROTOCOL_UNKNOWN)
+ confidence = NDPI_CONFIDENCE_DPI_CACHE;
+ }
+ if(app_proto != NDPI_PROTOCOL_UNKNOWN)
+ add_to_caches(ndpi_struct, flow, app_proto);
- if(ndpi_lru_find_cache(ndpi_struct->stun_cache, key,
- &cached_proto, 0 /* Don't remove it as it can be used for other connections */,
- ndpi_get_current_time(flow))) {
-#ifdef DEBUG_LRU
- printf("[LRU] FOUND %u / %u: no need to cache %u.%u\n", key, cached_proto, proto, app_proto);
-#endif
- if(app_proto != cached_proto) {
- app_proto = cached_proto;
- confidence = NDPI_CONFIDENCE_DPI_CACHE;
- }
- } else {
- u_int32_t key_rev = get_stun_lru_key(flow, 1);
+ if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN ||
+ app_proto != NDPI_PROTOCOL_UNKNOWN) {
+ NDPI_LOG_DBG(ndpi_struct, "Setting %d\n", app_proto);
+ ndpi_set_detected_protocol(ndpi_struct, flow, app_proto, NDPI_PROTOCOL_STUN, confidence);
+ }
- if(ndpi_lru_find_cache(ndpi_struct->stun_cache, key_rev,
- &cached_proto, 0 /* Don't remove it as it can be used for other connections */,
- ndpi_get_current_time(flow))) {
-#ifdef DEBUG_LRU
- printf("[LRU] FOUND %u / %u: no need to cache %u.%u\n", key_rev, cached_proto, proto, app_proto);
-#endif
- if(app_proto != cached_proto) {
- app_proto = cached_proto;
- confidence = NDPI_CONFIDENCE_DPI_CACHE;
- }
+ /* This is quite complex. We want extra dissection for:
+ * sub-classification
+ * metadata extraction in general
+ * Telegram: we need more packets to find all XOR-PEER-ADDRESS attributes
+ * monitoring, i.e. looking for RTP
+ And all these cases might overlap...
+ */
+ if(!flow->extra_packets_func) {
+ if(flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN /* No-subclassification */ ||
+ flow->detected_protocol_stack[0] == NDPI_PROTOCOL_TELEGRAM_VOIP /* Metadata. TODO: other protocols? */ ||
+ (ndpi_struct->monitoring_stun_pkts_to_process > 0 &&
+ (ndpi_struct->monitoring_stun_flags & NDPI_MONITORING_STUN_SUBCLASSIFIED))) {
+ NDPI_LOG_DBG(ndpi_struct, "Enabling extra dissection\n");
+
+ if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_TELEGRAM_VOIP) {
+ flow->max_extra_packets_to_check = 10; /* Looking for metadata. There are no really RTP packets
+ in Telegram flows, so no need to enable monitoring for them */
} else {
- if(app_proto != NDPI_PROTOCOL_STUN) {
- /* No sense to add STUN, but only subprotocols */
-
-#ifdef DEBUG_LRU
- printf("[LRU] ADDING %u / %u.%u [%u -> %u]\n", key, proto, app_proto,
- ntohs(packet->udp->source), ntohs(packet->udp->dest));
-#endif
-
- ndpi_lru_add_to_cache(ndpi_struct->stun_cache, key, app_proto, ndpi_get_current_time(flow));
- ndpi_lru_add_to_cache(ndpi_struct->stun_cache, key_rev, app_proto, ndpi_get_current_time(flow));
- }
+ flow->max_extra_packets_to_check = ndpi_max(4, ndpi_struct->monitoring_stun_pkts_to_process);
+ flow->extra_packets_func = stun_search_again;
}
}
- }
-
- /* TODO: extend to other protocols? */
- if(ndpi_struct->stun_zoom_cache &&
- app_proto == NDPI_PROTOCOL_ZOOM &&
- flow->l4_proto == IPPROTO_UDP) {
- u_int32_t key = get_stun_lru_key(flow, 0); /* Src */
-#ifdef DEBUG_ZOOM_LRU
- printf("[LRU ZOOM] ADDING %u [src_port %u]\n", key, ntohs(flow->c_port));
-#endif
- ndpi_lru_add_to_cache(ndpi_struct->stun_zoom_cache, key,
- 0 /* dummy */, ndpi_get_current_time(flow));
- }
-
-
-#ifdef DEBUG_STUN
- printf("[STUN] Setting %d\n", app_proto);
-#endif
- ndpi_set_detected_protocol(ndpi_struct, flow, app_proto, NDPI_PROTOCOL_STUN, confidence);
-
- if(ndpi_struct->monitoring_stun_pkts_to_process > 0 &&
- flow->l4_proto == IPPROTO_UDP /* TODO: support TCP. We need to pay some attention because:
- * multiple msg in the same TCP segment
- * same msg split across multiple segments */) {
- if((ndpi_struct->monitoring_stun_flags & NDPI_MONITORING_STUN_SUBCLASSIFIED) ||
- flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN /* No-subclassification */) {
- flow->max_extra_packets_to_check = ndpi_struct->monitoring_stun_pkts_to_process;
- flow->extra_packets_func = stun_monitoring;
+ } else {
+ /* Already in extra dissection, but we just sub-classied */
+ if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_TELEGRAM_VOIP) {
+ flow->max_extra_packets_to_check = 10;
}
}
}
-typedef enum {
- NDPI_IS_STUN,
- NDPI_IS_NOT_STUN
-} ndpi_int_stun_t;
-
/* ************************************************************ */
-static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct *ndpi_struct,
- struct ndpi_flow_struct *flow,
- const u_int8_t * payload,
- u_int16_t payload_length,
- u_int16_t *app_proto) {
- struct ndpi_packet_struct *packet = &ndpi_struct->packet;
- u_int16_t msg_type, msg_len;
- u_int32_t unused;
- int rc;
-
- if(packet->iph &&
- ((packet->iph->daddr == 0xFFFFFFFF /* 255.255.255.255 */) ||
- ((ntohl(packet->iph->daddr) & 0xF0000000) == 0xE0000000 /* A multicast address */))) {
- NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
- return(NDPI_IS_NOT_STUN);
- }
-
- /* If we're here it's because this does not look like STUN anymore
- as this was a flow that started as STUN and turned into something
- else. Let's investigate what is that about */
- if(flow->stun.num_pkts > 0 && is_dtls(payload, payload_length, &unused)) {
-#ifdef DEBUG_STUN
- printf("[STUN] DTLS?\n");
-#endif
- /* Switching to TLS dissector is tricky, because we are calling one dissector
- from another one, and that is not a common operation...
- Additionally:
- * at that point protocol stack is still empty
- * we have room for only two protocols in flow->detected_protocol_stack[] so
- we can't have something like STUN/DTLS/SNAPCHAT_CALL
- * the easiest solution is skipping STUN, and let TLS dissector to set both
- master (i.e. DTLS) and subprotocol (if any) */
- if(ndpi_struct->opportunistic_tls_stun_enabled) {
- flow->stun.maybe_dtls = 1;
- switch_to_tls(ndpi_struct, flow);
- }
- /* We don't want to mess up with TLS classification/results but we don't want to
- exclude STUN right away to keep trying it in the case that this packet is
- not a real DTLS one */
- return(NDPI_IS_NOT_STUN);
- }
-
- if(payload_length < STUN_HDR_LEN) {
- /* This looks like an invalid packet */
-
- if(flow->stun.num_pkts > 0) {
- return(NDPI_IS_STUN);
- } else
- return(NDPI_IS_NOT_STUN);
- }
-
- if((strncmp((const char*)payload, (const char*)"RSP/", 4) == 0)
- && (strncmp((const char*)&payload[7], (const char*)" STUN_", 6) == 0)) {
- NDPI_LOG_INFO(ndpi_struct, "found stun\n");
- goto stun_found;
- }
-
- msg_type = ntohs(*((u_int16_t*)payload));
- msg_len = ntohs(*((u_int16_t*)&payload[2]));
-
- /* With tcp, we might have multiple msg in the same TCP pkt.
- Parse only the first one. TODO */
- if(packet->tcp) {
- if(msg_len + 20 > payload_length)
- return(NDPI_IS_NOT_STUN);
- /* Let's hope that classic-stun is no more used over TCP */
- if(ntohl(*((u_int32_t *)&payload[4])) != 0x2112A442)
- return(NDPI_IS_NOT_STUN);
-
- payload_length = msg_len + 20;
- }
-
- if((msg_type == 0) || ((msg_len+20) != payload_length))
- return(NDPI_IS_NOT_STUN);
-
- /* https://www.iana.org/assignments/stun-parameters/stun-parameters.xhtml */
- if(((msg_type & 0x3EEF) > 0x000B) &&
- (msg_type != 0x0800 && msg_type != 0x0801 && msg_type != 0x0802)) {
-#ifdef DEBUG_STUN
- printf("[STUN] msg_type = %04X\n", msg_type);
-#endif
- return(NDPI_IS_NOT_STUN);
- }
-
- if(ndpi_struct->stun_cache) {
- u_int16_t proto;
- u_int32_t key = get_stun_lru_key(flow, 0);
- int rc = ndpi_lru_find_cache(ndpi_struct->stun_cache, key, &proto,
- 0 /* Don't remove it as it can be used for other connections */,
- ndpi_get_current_time(flow));
-
-#ifdef DEBUG_LRU
- printf("[LRU] Searching %u\n", key);
-#endif
-
- if(!rc) {
- key = get_stun_lru_key(flow, 1);
- rc = ndpi_lru_find_cache(ndpi_struct->stun_cache, key, &proto,
- 0 /* Don't remove it as it can be used for other connections */,
- ndpi_get_current_time(flow));
-
-#ifdef DEBUG_LRU
- printf("[LRU] Searching %u\n", key);
-#endif
- }
-
- if(rc) {
-#ifdef DEBUG_LRU
- printf("[LRU] Cache FOUND %u / %u\n", key, proto);
-#endif
-
- *app_proto = proto;
- return(NDPI_IS_STUN);
- } else {
-#ifdef DEBUG_LRU
- printf("[LRU] NOT FOUND %u\n", key);
-#endif
- }
- } else {
-#ifdef DEBUG_LRU
- printf("[LRU] NO/EMPTY CACHE\n");
-#endif
- }
-
- if(msg_type == 0x01 /* Binding Request */) {
- flow->stun.num_binding_requests++;
-
- flow->guessed_protocol_id = NDPI_PROTOCOL_STUN;
-
- if(!msg_len) {
- /* flow->stun.num_pkts++; */
- return(NDPI_IS_NOT_STUN); /* This to keep analyzing STUN instead of giving up */
- }
- }
-
- flow->stun.num_pkts++;
-
- flow->guessed_protocol_id = NDPI_PROTOCOL_STUN;
-
- if(payload_length == (msg_len+20)) {
- if((msg_type & 0x3EEF) <= 0x000B) /* http://www.3cx.com/blog/voip-howto/stun-details/ */ {
- u_int offset = 20;
-
- /*
- This can either be the standard RTCP or Ms Lync RTCP that
- later will become Ms Lync RTP. In this case we need to
- be careful before deciding about the protocol before dissecting the packet
-
- MS Lync = Skype
- https://en.wikipedia.org/wiki/Skype_for_Business
- */
-
- while((offset+4) < payload_length) {
- u_int16_t attribute = ntohs(*((u_int16_t*)&payload[offset]));
- u_int16_t len = ntohs(*((u_int16_t*)&payload[offset+2]));
- u_int16_t x = (len + 4) % 4;
-
- if(x)
- len += 4-x;
-
-#ifdef DEBUG_STUN
- printf("==> Attribute: %04X\n", attribute);
-#endif
-
- switch(attribute) {
- case 0x0101:
- case 0x0103:
- *app_proto = NDPI_PROTOCOL_ZOOM;
- return(NDPI_IS_STUN);
-
- case 0x4000:
- case 0x4001:
- case 0x4002:
- case 0x4003:
- case 0x4004:
- case 0x4007:
- /* These are the only messages apparently whatsapp voice can use */
- *app_proto = NDPI_PROTOCOL_WHATSAPP_CALL;
- return(NDPI_IS_STUN);
-
- case 0x0014: /* Realm */
- {
- u_int16_t realm_len = ntohs(*((u_int16_t*)&payload[offset+2]));
-
- if(flow->host_server_name[0] == '\0') {
- u_int k = offset+4;
-
- ndpi_hostname_sni_set(flow, payload + k, ndpi_min(realm_len, payload_length - k));
-
-#ifdef DEBUG_STUN
- printf("==> [%s]\n", flow->host_server_name);
-#endif
-
- if(strstr(flow->host_server_name, "google.com") != NULL) {
- *app_proto = NDPI_PROTOCOL_HANGOUT_DUO;
- return(NDPI_IS_STUN);
- } else if(strstr(flow->host_server_name, "whispersystems.org") != NULL ||
- (strstr(flow->host_server_name, "signal.org") != NULL)) {
- *app_proto = NDPI_PROTOCOL_SIGNAL_VOIP;
- return(NDPI_IS_STUN);
- } else if(strstr(flow->host_server_name, "facebook") != NULL) {
- *app_proto = NDPI_PROTOCOL_FACEBOOK_VOIP;
- return(NDPI_IS_STUN);
- } else if(strstr(flow->host_server_name, "stripcdn.com") != NULL) {
- *app_proto = NDPI_PROTOCOL_ADULT_CONTENT;
- return(NDPI_IS_STUN);
- }
- }
- }
- break;
-
- case 0x8054: /* Candidate Identifier */
- if((len == 4)
- && ((offset+7) < payload_length)
- && (payload[offset+5] == 0x00)
- && (payload[offset+6] == 0x00)
- && (payload[offset+7] == 0x00)) {
- /* Either skype for business or "normal" skype with multiparty call */
-#ifdef DEBUG_STUN
- printf("==> Skype found\n");
-#endif
- *app_proto = NDPI_PROTOCOL_SKYPE_TEAMS_CALL;
- return(NDPI_IS_STUN);
- }
-
- break;
-
- case 0x8055: /* MS Service Quality (skype?) */
- break;
-
- /* Proprietary fields found on skype calls */
- case 0x24DF:
- case 0x3802:
- case 0x8036:
- case 0x8095:
- case 0x0800:
- case 0x8006: /* This is found on skype calls) */
- /* printf("====>>>> %04X\n", attribute); */
-#ifdef DEBUG_STUN
- printf("==> Skype (2) found\n");
-#endif
-
- *app_proto = NDPI_PROTOCOL_SKYPE_TEAMS_CALL;
- return(NDPI_IS_STUN);
-
- case 0x8070: /* Implementation Version */
- if(len == 4 && ((offset+7) < payload_length)
- && (payload[offset+4] == 0x00) && (payload[offset+5] == 0x00) && (payload[offset+6] == 0x00) &&
- ((payload[offset+7] == 0x02) || (payload[offset+7] == 0x03))) {
-#ifdef DEBUG_STUN
- printf("==> Skype (3) found\n");
-#endif
-
- *app_proto = NDPI_PROTOCOL_SKYPE_TEAMS_CALL;
- return(NDPI_IS_STUN);
- }
- break;
-
- case 0xFF03:
- *app_proto = NDPI_PROTOCOL_HANGOUT_DUO;
- return(NDPI_IS_STUN);
-
- default:
-#ifdef DEBUG_STUN
- printf("==> %04X\n", attribute);
-#endif
- break;
- }
-
- offset += len + 4;
- }
-
- goto stun_found;
- } else if(msg_type == 0x0800 ||
- msg_type == 0x0801 ||
- msg_type == 0x0802) {
- *app_proto = NDPI_PROTOCOL_WHATSAPP_CALL;
- return(NDPI_IS_STUN);
- }
- }
-
- if((flow->stun.num_pkts > 0) && (msg_type <= 0x00FF)) {
- *app_proto = NDPI_PROTOCOL_WHATSAPP_CALL;
- return(NDPI_IS_STUN);
- } else
- return(NDPI_IS_NOT_STUN);
-
-stun_found:
- flow->stun.num_processed_pkts++;
-
- rc = (flow->stun.num_pkts < MAX_NUM_STUN_PKTS) ? NDPI_IS_NOT_STUN : NDPI_IS_STUN;
-
-#ifdef DEBUG_STUN
- printf("stun.num_pkts %d, stun.num_processed_pkts %d, rc: %d\n",
- flow->stun.num_pkts, flow->stun.num_processed_pkts, rc);
-#endif
-
- return rc;
-}
static void ndpi_search_stun(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow)
{
@@ -596,28 +573,20 @@ static void ndpi_search_stun(struct ndpi_detection_module_struct *ndpi_struct, s
app_proto = NDPI_PROTOCOL_UNKNOWN;
- /* STUN may be encapsulated in TCP packets with a special TCP framing described in RFC 4571 */
- if(packet->tcp &&
- packet->payload_packet_len >= 22 &&
- ((ntohs(get_u_int16_t(packet->payload, 0)) + 2) == packet->payload_packet_len)) {
- /* TODO there could be several STUN packets in a single TCP packet so maybe the detection could be
- * improved by checking only the STUN packet of given length */
+ if(packet->iph &&
+ ((packet->iph->daddr == 0xFFFFFFFF /* 255.255.255.255 */) ||
+ ((ntohl(packet->iph->daddr) & 0xF0000000) == 0xE0000000 /* A multicast address */))) {
+ NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
+ return;
+ }
- if(ndpi_int_check_stun(ndpi_struct, flow, packet->payload + 2,
- packet->payload_packet_len - 2, &app_proto) == NDPI_IS_STUN) {
- ndpi_int_stun_add_connection(ndpi_struct, flow, app_proto);
- return;
- }
- } else { /* UDP or TCP without framing */
- if(ndpi_int_check_stun(ndpi_struct, flow, packet->payload,
- packet->payload_packet_len, &app_proto) == NDPI_IS_STUN) {
- ndpi_int_stun_add_connection(ndpi_struct, flow, app_proto);
- return;
- }
+ if(is_stun(ndpi_struct, flow, &app_proto)) {
+ ndpi_int_stun_add_connection(ndpi_struct, flow, app_proto);
+ return;
}
- if(flow->stun.num_pkts >= MAX_NUM_STUN_PKTS ||
- flow->packet_counter > 10)
+ /* TODO: can we stop earlier? */
+ if(flow->packet_counter > 10)
NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
if(flow->packet_counter > 0) {
@@ -628,7 +597,6 @@ static void ndpi_search_stun(struct ndpi_detection_module_struct *ndpi_struct, s
}
}
-
void init_stun_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id) {
ndpi_set_bitmask_protocol_detection("STUN", ndpi_struct, *id,
NDPI_PROTOCOL_STUN,