Skip to content

Commit 252be78

Browse files
authored
STUN: improve detection of Telegram calls (#2671)
1 parent af011e3 commit 252be78

File tree

9 files changed

+129
-51
lines changed

9 files changed

+129
-51
lines changed

src/include/ndpi_private.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@ int search_into_bittorrent_cache(struct ndpi_detection_module_struct *ndpi_struc
700700
int is_stun(struct ndpi_detection_module_struct *ndpi_struct,
701701
struct ndpi_flow_struct *flow,
702702
u_int16_t *app_proto);
703-
void switch_extra_dissection_to_stun(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow);
703+
void switch_extra_dissection_to_stun(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow, int std_callback);
704704

705705
/* TPKT */
706706
int tpkt_verify_hdr(const struct ndpi_packet_struct * const packet);

src/lib/protocols/rtp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ static void ndpi_int_rtp_add_connection(struct ndpi_detection_module_struct *ndp
269269
from the beginning */
270270
if(!(flow->l4_proto == IPPROTO_TCP && ndpi_seen_flow_beginning(flow))) {
271271
NDPI_LOG_DBG(ndpi_struct, "Enabling (STUN) extra dissection\n");
272-
switch_extra_dissection_to_stun(ndpi_struct, flow);
272+
switch_extra_dissection_to_stun(ndpi_struct, flow, 1);
273273
}
274274
}
275275
}

src/lib/protocols/stun.c

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ static int stun_search_again(struct ndpi_detection_module_struct *ndpi_struct,
6161

6262

6363
/* Valid classifications:
64-
* STUN, DTLS, STUN/RTP, DTLS/SRTP, RTP or RTCP (the last two, only from RTP dissector)
64+
* STUN, DTLS, STUN/RTP, DTLS/SRTP, RTP or RTCP (only from RTP dissector)
65+
and TELEGRAM (only from Telegram dissector, note that TELEGRAM != TELEGRAM_VOIP!!)
6566
* STUN/APP, DTLS/APP, SRTP/APP ["real" sub-classification]
6667
The idea is:
6768
* the specific "real" application (WA/FB/Signal/...), if present, should
@@ -79,7 +80,8 @@ static int is_subclassification_real_by_proto(u_int16_t proto)
7980
proto == NDPI_PROTOCOL_RTP ||
8081
proto == NDPI_PROTOCOL_RTCP ||
8182
proto == NDPI_PROTOCOL_SRTP ||
82-
proto == NDPI_PROTOCOL_DTLS)
83+
proto == NDPI_PROTOCOL_DTLS ||
84+
proto == NDPI_PROTOCOL_TELEGRAM)
8385
return 0;
8486
return 1;
8587
}
@@ -493,6 +495,9 @@ int is_stun(struct ndpi_detection_module_struct *ndpi_struct,
493495
*app_proto = NDPI_PROTOCOL_SIGNAL_VOIP;
494496
}
495497

498+
if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_TELEGRAM)
499+
*app_proto = NDPI_PROTOCOL_TELEGRAM_VOIP;
500+
496501
off = STUN_HDR_LEN;
497502
while(off + 4 < payload_length) {
498503
u_int16_t attribute = ntohs(*((u_int16_t *)&payload[off]));
@@ -783,7 +788,8 @@ static u_int32_t __get_master(struct ndpi_flow_struct *flow) {
783788

784789
if(flow->detected_protocol_stack[1] != NDPI_PROTOCOL_UNKNOWN)
785790
return flow->detected_protocol_stack[1];
786-
if(flow->detected_protocol_stack[0] != NDPI_PROTOCOL_UNKNOWN)
791+
if(flow->detected_protocol_stack[0] != NDPI_PROTOCOL_UNKNOWN &&
792+
flow->detected_protocol_stack[0] != NDPI_PROTOCOL_TELEGRAM)
787793
return flow->detected_protocol_stack[0];
788794
return NDPI_PROTOCOL_STUN;
789795
}
@@ -1039,6 +1045,62 @@ static int stun_search_again(struct ndpi_detection_module_struct *ndpi_struct,
10391045

10401046
/* ************************************************************ */
10411047

1048+
static int stun_telegram_search_again(struct ndpi_detection_module_struct *ndpi_struct,
1049+
struct ndpi_flow_struct *flow)
1050+
{
1051+
struct ndpi_packet_struct *packet = &ndpi_struct->packet;
1052+
const u_int8_t *orig_payload;
1053+
u_int16_t orig_payload_length;
1054+
char pattern[12] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1055+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1056+
u_int16_t length;
1057+
1058+
NDPI_LOG_DBG2(ndpi_struct, "[T] Packet counter %d protos %d/%d Monitoring? %d\n",
1059+
flow->packet_counter,
1060+
flow->detected_protocol_stack[0], flow->detected_protocol_stack[1],
1061+
flow->monitoring);
1062+
1063+
/* For SOME of its STUN flows, Telegram uses a custom encapsulation
1064+
There is no documentation. It seems:
1065+
* some unknown packets (especially at the beginning/end of the flow) have a bunch of 0xFF
1066+
* the other packets encapsulate standard STUN/DTLS/RTP payload at offset 24
1067+
(with a previous field containing the payload length)
1068+
*/
1069+
1070+
if(packet->payload_packet_len <= 28) {
1071+
NDPI_LOG_DBG(ndpi_struct, "Malformed custom Telegram packet (too short)\n");
1072+
return keep_extra_dissection(ndpi_struct, flow);
1073+
}
1074+
1075+
if(memcmp(&packet->payload[16], pattern, sizeof(pattern)) == 0) {
1076+
NDPI_LOG_DBG(ndpi_struct, "Custom/Unknown Telegram packet\n");
1077+
return keep_extra_dissection(ndpi_struct, flow);
1078+
}
1079+
1080+
/* It should be STUN/DTLS/RTP */
1081+
1082+
length = ntohs(*(u_int16_t *)&packet->payload[22]);
1083+
if(24 + length > packet->payload_packet_len) {
1084+
NDPI_LOG_DBG(ndpi_struct, "Malformed custom Telegram packet (too long: %d %d)\n",
1085+
length, packet->payload_packet_len);
1086+
return keep_extra_dissection(ndpi_struct, flow);
1087+
}
1088+
1089+
orig_payload = packet->payload;
1090+
orig_payload_length = packet->payload_packet_len ;
1091+
packet->payload = packet->payload + 24;
1092+
packet->payload_packet_len = length;
1093+
1094+
stun_search_again(ndpi_struct, flow);
1095+
1096+
packet->payload = orig_payload;
1097+
packet->payload_packet_len = orig_payload_length;
1098+
1099+
return keep_extra_dissection(ndpi_struct, flow);
1100+
}
1101+
1102+
/* ************************************************************ */
1103+
10421104
static u_int64_t get_stun_lru_key(struct ndpi_flow_struct *flow, u_int8_t rev) {
10431105
if(rev) {
10441106
if(flow->is_ipv6)
@@ -1150,19 +1212,23 @@ static void ndpi_int_stun_add_connection(struct ndpi_detection_module_struct *nd
11501212
}
11511213
}
11521214

1153-
switch_extra_dissection_to_stun(ndpi_struct, flow);
1215+
switch_extra_dissection_to_stun(ndpi_struct, flow, 1);
11541216
}
11551217

11561218
/* ************************************************************ */
11571219

11581220
void switch_extra_dissection_to_stun(struct ndpi_detection_module_struct *ndpi_struct,
1159-
struct ndpi_flow_struct *flow)
1221+
struct ndpi_flow_struct *flow,
1222+
int std_callback)
11601223
{
11611224
if(!flow->extra_packets_func) {
11621225
if(keep_extra_dissection(ndpi_struct, flow)) {
11631226
NDPI_LOG_DBG(ndpi_struct, "Enabling extra dissection\n");
11641227
flow->max_extra_packets_to_check = ndpi_struct->cfg.stun_max_packets_extra_dissection;
1165-
flow->extra_packets_func = stun_search_again;
1228+
if(std_callback)
1229+
flow->extra_packets_func = stun_search_again;
1230+
else
1231+
flow->extra_packets_func = stun_telegram_search_again;
11661232
}
11671233
}
11681234
}

src/lib/protocols/telegram.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,14 @@ static void ndpi_search_telegram(struct ndpi_detection_module_struct *ndpi_struc
9393

9494
if(found == 12) {
9595
ndpi_int_telegram_add_connection(ndpi_struct, flow, NDPI_CONFIDENCE_DPI);
96+
/* It seems this kind of traffic is used:
97+
* for "normal" stuff (at least years ago... and now? TODO)
98+
* for calls, as a custom encapsulation of STUN/DTLS/RTP packets
99+
Since we are not able to tell the former from the latter, always
100+
switch to STUN dissection. If we find STUN/DTLS/RTP stuff we will
101+
update the classification to something like STUN/Telegram_voip,
102+
otherwise it will remain Telegram */
103+
switch_extra_dissection_to_stun(ndpi_struct, flow, 0);
96104
return;
97105
}
98106
}

0 commit comments

Comments
 (0)