/src/ndpi/src/lib/protocols/stun.c
Line | Count | Source |
1 | | /* |
2 | | * stun.c |
3 | | * |
4 | | * Copyright (C) 2011-25 - 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_STUN |
27 | | |
28 | | #include "ndpi_api.h" |
29 | | #include "ndpi_private.h" |
30 | | |
31 | | // #define DEBUG_LRU 1 |
32 | | |
33 | 0 | #define STUN_HDR_LEN 20 /* STUN message header length, Classic-STUN (RFC 3489) and STUN (RFC 8489) both */ |
34 | | |
35 | | |
36 | | /* Methods */ |
37 | | #define METHOD_BINDING 0x0001 /* RFC8489 */ |
38 | | #define METHOD_SHARED_SECRET 0x0002 /* RFC3489 */ |
39 | 0 | #define METHOD_ALLOCATE 0x0003 /* RFC8489 */ |
40 | 0 | #define METHOD_REFRESH 0x0004 /* RFC8489 */ |
41 | 0 | #define METHOD_DATA_IND_OLD 0x0005 |
42 | 0 | #define METHOD_SEND 0x0006 /* RFC8656 */ |
43 | 0 | #define METHOD_DATA_IND 0x0007 /* RFC8656 */ |
44 | 0 | #define METHOD_CREATE_PERMISSION 0x0008 /* RFC8656 */ |
45 | 0 | #define METHOD_CHANNELBIND 0x0009 /* RFC8656 */ |
46 | | /* TCP specific */ |
47 | 0 | #define METHOD_CONNECT 0x000a /* RFC6062 */ |
48 | 0 | #define METHOD_CONNECTION_BIND 0x000b /* RFC6062 */ |
49 | 0 | #define METHOD_CONNECTION_ATTEMPT 0x000c /* RFC6062 */ |
50 | | |
51 | | |
52 | | static u_int64_t get_stun_lru_key(struct ndpi_flow_struct *flow, u_int8_t rev); |
53 | | static u_int64_t get_stun_lru_key_raw4(u_int32_t ip, u_int16_t port); |
54 | | static u_int64_t get_stun_lru_key_raw6(u_int8_t *ip, u_int16_t port); |
55 | | static void ndpi_int_stun_add_connection(struct ndpi_detection_module_struct *ndpi_struct, |
56 | | struct ndpi_flow_struct *flow, |
57 | | u_int16_t app_proto, |
58 | | u_int16_t master_proto, |
59 | | ndpi_protocol_category_t category); |
60 | | static int stun_search_again(struct ndpi_detection_module_struct *ndpi_struct, |
61 | | struct ndpi_flow_struct *flow); |
62 | | |
63 | | |
64 | | /* Valid classifications: |
65 | | * STUN, DTLS, STUN/RTP, DTLS/SRTP, RTP or RTCP (only from RTP dissector) |
66 | | and TELEGRAM (only from Telegram dissector, note that TELEGRAM != TELEGRAM_VOIP!!) |
67 | | * STUN/APP, DTLS/APP, SRTP/APP ["real" sub-classification] |
68 | | The idea is: |
69 | | * the specific "real" application (WA/FB/Signal/...), if present, should |
70 | | be always set as "app" protocol, with STUN or DTLS or SRTP as "master" protocol |
71 | | * every "real" application that we handle, if it uses RTP, it is |
72 | | encrypted --> SRTP |
73 | | * keep STUN/RTP for the generic case without sub-classification [because |
74 | | nDPI uses SRTP only when it is sure that there is encryption] |
75 | | */ |
76 | | |
77 | | static int is_subclassification_real_by_proto(u_int16_t proto) |
78 | 0 | { |
79 | 0 | if(proto == NDPI_PROTOCOL_UNKNOWN || |
80 | 0 | proto == NDPI_PROTOCOL_STUN || |
81 | 0 | proto == NDPI_PROTOCOL_RTP || |
82 | 0 | proto == NDPI_PROTOCOL_RTCP || |
83 | 0 | proto == NDPI_PROTOCOL_SRTP || |
84 | 0 | proto == NDPI_PROTOCOL_DTLS || |
85 | 0 | proto == NDPI_PROTOCOL_TELEGRAM) |
86 | 0 | return 0; |
87 | 0 | return 1; |
88 | 0 | } |
89 | | |
90 | | /* ***************************************************** */ |
91 | | |
92 | | static int is_subclassification_real(struct ndpi_flow_struct *flow) |
93 | 0 | { |
94 | | /* No previous subclassification */ |
95 | 0 | if(flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN) |
96 | 0 | return 0; |
97 | 0 | return is_subclassification_real_by_proto(flow->detected_protocol_stack[0]); |
98 | 0 | } |
99 | | |
100 | | /* ***************************************************** */ |
101 | | |
102 | | static int is_new_subclassification_better(struct ndpi_detection_module_struct *ndpi_struct, |
103 | | struct ndpi_flow_struct *flow, |
104 | | u_int16_t new_app_proto) |
105 | 0 | { |
106 | 0 | NDPI_LOG_DBG(ndpi_struct, "%d/%d -> %d\n", |
107 | 0 | flow->detected_protocol_stack[1], flow->detected_protocol_stack[0], |
108 | 0 | new_app_proto); |
109 | | |
110 | | /* If we don't have a real subclassification, we might want to lookup into the cache again |
111 | | (even if new_app_proto == NDPI_PROTOCOL_UNKNOWN) */ |
112 | |
|
113 | 0 | if(is_subclassification_real(flow) && |
114 | 0 | new_app_proto == NDPI_PROTOCOL_UNKNOWN) |
115 | 0 | return 0; |
116 | | |
117 | | /* Debug */ |
118 | 0 | if(new_app_proto != NDPI_PROTOCOL_UNKNOWN && |
119 | 0 | is_subclassification_real(flow) && |
120 | 0 | new_app_proto != flow->detected_protocol_stack[0]) { |
121 | 0 | NDPI_LOG_DBG(ndpi_struct, "Incoherent sub-classification change %d/%d->%d \n", |
122 | 0 | flow->detected_protocol_stack[1], |
123 | 0 | flow->detected_protocol_stack[0], new_app_proto); |
124 | 0 | } |
125 | |
|
126 | 0 | if(new_app_proto != flow->detected_protocol_stack[0]) |
127 | 0 | return 1; |
128 | 0 | return 0; |
129 | 0 | } |
130 | | |
131 | | /* ***************************************************** */ |
132 | | |
133 | | static u_int16_t search_into_cache(struct ndpi_detection_module_struct *ndpi_struct, |
134 | | struct ndpi_flow_struct *flow) |
135 | 0 | { |
136 | 0 | u_int16_t proto; |
137 | 0 | u_int64_t key; |
138 | 0 | int rc; |
139 | |
|
140 | 0 | if(ndpi_struct->stun_cache) { |
141 | 0 | key = get_stun_lru_key(flow, 0); |
142 | 0 | rc = ndpi_lru_find_cache(ndpi_struct->stun_cache, key, &proto, |
143 | 0 | 0 /* Don't remove it as it can be used for other connections */, |
144 | 0 | ndpi_get_current_time(flow)); |
145 | | #ifdef DEBUG_LRU |
146 | | printf("[LRU] Searching 0x%llx\n", (long long unsigned int)key); |
147 | | #endif |
148 | |
|
149 | 0 | if(!rc) { |
150 | 0 | key = get_stun_lru_key(flow, 1); |
151 | 0 | rc = ndpi_lru_find_cache(ndpi_struct->stun_cache, key, &proto, |
152 | 0 | 0 /* Don't remove it as it can be used for other connections */, |
153 | 0 | ndpi_get_current_time(flow)); |
154 | | #ifdef DEBUG_LRU |
155 | | printf("[LRU] Searching 0x%llx\n", (long long unsigned int)key); |
156 | | #endif |
157 | 0 | } |
158 | |
|
159 | 0 | if(rc) { |
160 | | #ifdef DEBUG_LRU |
161 | | printf("[LRU] Cache FOUND 0x%llx / %u\n", (long long unsigned int)key, proto); |
162 | | #endif |
163 | |
|
164 | 0 | return proto; |
165 | 0 | } else { |
166 | | #ifdef DEBUG_LRU |
167 | | printf("[LRU] NOT FOUND 0x%llx\n", (long long unsigned int)key); |
168 | | #endif |
169 | 0 | } |
170 | 0 | } else { |
171 | | #ifdef DEBUG_LRU |
172 | | printf("[LRU] NO/EMPTY CACHE\n"); |
173 | | #endif |
174 | 0 | } |
175 | 0 | return NDPI_PROTOCOL_UNKNOWN; |
176 | 0 | } |
177 | | |
178 | | /* ***************************************************** */ |
179 | | |
180 | | static void add_to_cache(struct ndpi_detection_module_struct *ndpi_struct, |
181 | | struct ndpi_flow_struct *flow, |
182 | | u_int16_t app_proto) |
183 | 0 | { |
184 | 0 | u_int64_t key, key_rev; |
185 | |
|
186 | 0 | if(ndpi_struct->stun_cache) { |
187 | 0 | key = get_stun_lru_key(flow, 0); |
188 | 0 | ndpi_lru_add_to_cache(ndpi_struct->stun_cache, key, app_proto, ndpi_get_current_time(flow)); |
189 | 0 | key_rev = get_stun_lru_key(flow, 1); |
190 | 0 | ndpi_lru_add_to_cache(ndpi_struct->stun_cache, key_rev, app_proto, ndpi_get_current_time(flow)); |
191 | |
|
192 | | #ifdef DEBUG_LRU |
193 | | printf("[LRU] ADDING 0x%llx 0x%llx app %u [%u -> %u]\n", |
194 | | (long long unsigned int)key, (long long unsigned int)key_rev, app_proto, |
195 | | ntohs(flow->c_port), ntohs(flow->s_port)); |
196 | | #endif |
197 | 0 | } |
198 | 0 | } |
199 | | |
200 | | /* ***************************************************** */ |
201 | | |
202 | | static void parse_ip_port_attribute(const u_int8_t *payload, u_int16_t payload_length, |
203 | | int off, u_int16_t real_len, ndpi_address_port *ap, |
204 | | ndpi_address_port *ap_monit) |
205 | 0 | { |
206 | 0 | if(off + 4 + real_len <= payload_length && |
207 | 0 | (real_len == 8 || real_len == 20)) { |
208 | 0 | u_int8_t protocol_family = payload[off+5]; |
209 | |
|
210 | 0 | if(protocol_family == 0x01 /* IPv4 */ && |
211 | 0 | real_len == 8) { |
212 | 0 | u_int16_t port = ntohs(*((u_int16_t*)&payload[off+6])); |
213 | 0 | u_int32_t ip = ntohl(*((u_int32_t*)&payload[off+8])); |
214 | | |
215 | | /* Only the first attribute ever in the flow */ |
216 | 0 | if(ap->port == 0) { |
217 | 0 | ap->port = port; |
218 | 0 | ap->address.v4 = htonl(ip); |
219 | 0 | ap->is_ipv6 = 0; |
220 | 0 | } |
221 | |
|
222 | 0 | if(ap_monit) { |
223 | 0 | ap_monit->port = port; |
224 | 0 | ap_monit->address.v4 = htonl(ip); |
225 | 0 | ap_monit->is_ipv6 = 0; |
226 | 0 | } |
227 | 0 | } else if(protocol_family == 0x02 /* IPv6 */ && |
228 | 0 | real_len == 20) { |
229 | 0 | u_int16_t port = ntohs(*((u_int16_t*)&payload[off+6])); |
230 | 0 | u_int32_t ip[4]; |
231 | |
|
232 | 0 | ip[0] = *((u_int32_t *)&payload[off + 8]); |
233 | 0 | ip[1] = *((u_int32_t *)&payload[off + 12]); |
234 | 0 | ip[2] = *((u_int32_t *)&payload[off + 16]); |
235 | 0 | ip[3] = *((u_int32_t *)&payload[off + 20]); |
236 | | |
237 | | /* Only the first attribute ever in the flow */ |
238 | 0 | if(ap->port == 0) { |
239 | 0 | ap->port = port; |
240 | 0 | memcpy(&ap->address, &ip, 16); |
241 | 0 | ap->is_ipv6 = 1; |
242 | 0 | } |
243 | |
|
244 | 0 | if(ap_monit) { |
245 | 0 | ap_monit->port = port; |
246 | 0 | memcpy(&ap_monit->address, &ip, 16); |
247 | 0 | ap_monit->is_ipv6 = 1; |
248 | 0 | } |
249 | 0 | } |
250 | 0 | } |
251 | 0 | } |
252 | | |
253 | | /* ***************************************************** */ |
254 | | |
255 | | static void parse_xor_ip_port_attribute(struct ndpi_detection_module_struct *ndpi_struct, |
256 | | struct ndpi_flow_struct *flow, |
257 | | const u_int8_t *payload, u_int16_t payload_length, |
258 | | int off, u_int16_t real_len, |
259 | | ndpi_address_port *ap, ndpi_address_port *ap_monit, |
260 | | u_int32_t transaction_id[3], u_int32_t magic_cookie, |
261 | | int add_to_cache) |
262 | 0 | { |
263 | | #ifdef NDPI_ENABLE_DEBUG_MESSAGES |
264 | | char buf[128]; |
265 | | #endif |
266 | |
|
267 | 0 | if(off + 4 + real_len <= payload_length && |
268 | 0 | (real_len == 8 || real_len == 20)) { |
269 | 0 | u_int8_t protocol_family = payload[off+5]; |
270 | |
|
271 | 0 | if(protocol_family == 0x01 /* IPv4 */ && |
272 | 0 | real_len == 8) { |
273 | 0 | u_int32_t ip; |
274 | 0 | u_int16_t port; |
275 | |
|
276 | 0 | port = ntohs(*((u_int16_t *)&payload[off + 6])) ^ (magic_cookie >> 16); |
277 | 0 | ip = *((u_int32_t *)&payload[off + 8]) ^ htonl(magic_cookie); |
278 | | |
279 | | /* Only the first attribute ever in the flow */ |
280 | 0 | if(ap->port == 0) { |
281 | 0 | ap->port = port; |
282 | 0 | ap->address.v4 = ip; |
283 | 0 | ap->is_ipv6 = 0; |
284 | 0 | } |
285 | |
|
286 | 0 | if(ap_monit) { |
287 | 0 | ap_monit->port = port; |
288 | 0 | ap_monit->address.v4 = ip; |
289 | 0 | ap_monit->is_ipv6 = 0; |
290 | 0 | } |
291 | |
|
292 | 0 | if(add_to_cache) { |
293 | 0 | NDPI_LOG_DBG(ndpi_struct, "Peer %s:%d [proto %d]\n", |
294 | 0 | inet_ntop(AF_INET, &ip, buf, sizeof(buf)), port, |
295 | 0 | flow->detected_protocol_stack[0]); |
296 | |
|
297 | 0 | if(ndpi_struct->stun_cache && |
298 | 0 | is_subclassification_real(flow)) { |
299 | 0 | u_int64_t key = get_stun_lru_key_raw4(ip, port); |
300 | |
|
301 | 0 | ndpi_lru_add_to_cache(ndpi_struct->stun_cache, key, |
302 | 0 | flow->detected_protocol_stack[0], |
303 | 0 | ndpi_get_current_time(flow)); |
304 | | #ifdef DEBUG_LRU |
305 | | printf("[LRU] Add peer 0x%llx %d\n", (long long unsigned int)key, flow->detected_protocol_stack[0]); |
306 | | #endif |
307 | 0 | } |
308 | 0 | } |
309 | 0 | } else if(protocol_family == 0x02 /* IPv6 */ && |
310 | 0 | real_len == 20) { |
311 | 0 | u_int32_t ip[4]; |
312 | 0 | u_int16_t port; |
313 | |
|
314 | 0 | port = ntohs(*((u_int16_t *)&payload[off + 6])) ^ (magic_cookie >> 16); |
315 | 0 | ip[0] = *((u_int32_t *)&payload[off + 8]) ^ htonl(magic_cookie); |
316 | 0 | ip[1] = *((u_int32_t *)&payload[off + 12]) ^ htonl(transaction_id[0]); |
317 | 0 | ip[2] = *((u_int32_t *)&payload[off + 16]) ^ htonl(transaction_id[1]); |
318 | 0 | ip[3] = *((u_int32_t *)&payload[off + 20]) ^ htonl(transaction_id[2]); |
319 | | |
320 | | /* Only the first attribute ever in the flow */ |
321 | 0 | if(ap->port == 0) { |
322 | 0 | ap->port = port; |
323 | 0 | memcpy(&ap->address, &ip, 16); |
324 | 0 | ap->is_ipv6 = 1; |
325 | 0 | } |
326 | |
|
327 | 0 | if(ap_monit) { |
328 | 0 | ap_monit->port = port; |
329 | 0 | memcpy(&ap_monit->address, &ip, 16); |
330 | 0 | ap_monit->is_ipv6 = 1; |
331 | 0 | } |
332 | |
|
333 | 0 | if(add_to_cache) { |
334 | 0 | NDPI_LOG_DBG(ndpi_struct, "Peer %s:%d [proto %d]\n", |
335 | 0 | inet_ntop(AF_INET6, &ip, buf, sizeof(buf)), port, |
336 | 0 | flow->detected_protocol_stack[0]); |
337 | |
|
338 | 0 | if(ndpi_struct->stun_cache && |
339 | 0 | is_subclassification_real(flow)) { |
340 | 0 | u_int64_t key = get_stun_lru_key_raw6((u_int8_t *)ip, port); |
341 | |
|
342 | 0 | ndpi_lru_add_to_cache(ndpi_struct->stun_cache, key, |
343 | 0 | flow->detected_protocol_stack[0], |
344 | 0 | ndpi_get_current_time(flow)); |
345 | | #ifdef DEBUG_LRU |
346 | | printf("[LRU] Add peer 0x%llx %d\n", (long long unsigned int)key, flow->detected_protocol_stack[0]); |
347 | | #endif |
348 | 0 | } |
349 | 0 | } |
350 | 0 | } |
351 | 0 | } |
352 | 0 | } |
353 | | |
354 | | /* ***************************************************** */ |
355 | | |
356 | | int is_stun(struct ndpi_detection_module_struct *ndpi_struct, |
357 | | struct ndpi_flow_struct *flow, |
358 | | u_int16_t *app_proto, |
359 | 0 | ndpi_protocol_category_t *category) { |
360 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
361 | 0 | u_int16_t msg_type, msg_len, method; |
362 | 0 | int off; |
363 | 0 | const u_int8_t *payload = packet->payload; |
364 | 0 | u_int16_t payload_length = packet->payload_packet_len; |
365 | 0 | const u_int8_t *orig_payload; |
366 | 0 | u_int16_t orig_payload_length; |
367 | 0 | u_int32_t magic_cookie; |
368 | 0 | u_int32_t transaction_id[3]; |
369 | |
|
370 | 0 | *category = NDPI_PROTOCOL_CATEGORY_UNSPECIFIED; |
371 | | |
372 | 0 | if(payload_length < STUN_HDR_LEN) |
373 | 0 | return(-1); |
374 | | |
375 | | /* Some really old/legacy stuff */ |
376 | 0 | if(strncmp((const char *)payload, "RSP/", 4) == 0 && |
377 | 0 | strncmp((const char *)&payload[7], " STUN_", 6) == 0) { |
378 | 0 | NDPI_LOG_DBG(ndpi_struct, "found old/legacy stun in rsp\n"); |
379 | 0 | return 1; /* No real metadata */ |
380 | 0 | } |
381 | | |
382 | | /* STUN may be encapsulated in TCP packets with a special TCP framing described in RFC 4571 */ |
383 | 0 | if(packet->tcp && |
384 | 0 | payload_length >= STUN_HDR_LEN + 2 && |
385 | | /* TODO: multiple STUN messagges */ |
386 | 0 | ((ntohs(get_u_int16_t(payload, 0)) + 2) == payload_length)) { |
387 | 0 | payload += 2; |
388 | 0 | payload_length -=2; |
389 | 0 | } |
390 | | |
391 | | /* Microsoft Multiplexed TURN messages */ |
392 | 0 | if(payload_length >= STUN_HDR_LEN + 12 && |
393 | 0 | ntohs(get_u_int16_t(payload, 0)) == 0xFF10 && |
394 | 0 | ntohs(get_u_int16_t(payload, 2)) + 4 == payload_length) { |
395 | 0 | payload += 12; |
396 | 0 | payload_length -= 12; |
397 | 0 | } |
398 | |
|
399 | 0 | msg_type = ntohs(*((u_int16_t *)&payload[0])); |
400 | 0 | msg_len = ntohs(*((u_int16_t *)&payload[2])); |
401 | 0 | magic_cookie = ntohl(*((u_int32_t *)&payload[4])); |
402 | 0 | transaction_id[0] = ntohl(*((u_int32_t *)&payload[8])); |
403 | 0 | transaction_id[1] = ntohl(*((u_int32_t *)&payload[12])); |
404 | 0 | transaction_id[2] = ntohl(*((u_int32_t *)&payload[16])); |
405 | | |
406 | | /* No magic_cookie on classic-stun */ |
407 | | /* Let's hope that we don't have anymore classic-stun over TCP */ |
408 | 0 | if(packet->tcp && magic_cookie != 0x2112A442) { |
409 | 0 | return 0; |
410 | 0 | } |
411 | | |
412 | 0 | NDPI_LOG_DBG2(ndpi_struct, "msg_type = %04X msg_len = %d\n", msg_type, msg_len); |
413 | | |
414 | | /* With tcp, we might have multiple msg in the same TCP pkt. |
415 | | Parse only the first one. TODO */ |
416 | 0 | if(packet->tcp) { |
417 | 0 | if(msg_len + STUN_HDR_LEN > payload_length) |
418 | 0 | return 0; |
419 | | |
420 | 0 | payload_length = msg_len + STUN_HDR_LEN; |
421 | 0 | } |
422 | | |
423 | 0 | if(msg_type == 0 || (msg_len + STUN_HDR_LEN != payload_length)) { |
424 | 0 | NDPI_LOG_DBG(ndpi_struct, "Invalid msg_type = %04X or len %d %d\n", |
425 | 0 | msg_type, msg_len, payload_length); |
426 | 0 | return -1; |
427 | 0 | } |
428 | | |
429 | | /* https://www.iana.org/assignments/stun-parameters/stun-parameters.xhtml */ |
430 | 0 | if(((msg_type & 0x3EEF) > 0x000B) && |
431 | 0 | msg_type != 0x0800 && msg_type != 0x0801 && msg_type != 0x0802 && |
432 | 0 | msg_type != 0x0804 && msg_type != 0x0805) { |
433 | 0 | NDPI_LOG_DBG(ndpi_struct, "Invalid msg_type = %04X\n", msg_type); |
434 | 0 | return -1; |
435 | 0 | } |
436 | | |
437 | 0 | if(magic_cookie != 0x2112A442) { |
438 | | /* Some heuristic to detect classic-stun: |
439 | | * msg type check (list from Wireshark) |
440 | | * let's see if attributes list seems ok */ |
441 | 0 | if(msg_type != 0x0001 && msg_type != 0x0101 && msg_type != 0x0111 && /* Binding */ |
442 | 0 | msg_type != 0x0002 && msg_type != 0x0102 && msg_type != 0x0112 && /* Shared secret */ |
443 | 0 | msg_type != 0x0003 && msg_type != 0x0103 && msg_type != 0x0113 && /* Allocate */ |
444 | 0 | msg_type != 0x0004 && msg_type != 0x0104 && msg_type != 0x0114 && /* Send */ |
445 | 0 | msg_type != 0x0115 && /* Data Indication */ |
446 | 0 | msg_type != 0x0006 && msg_type != 0x0106 && msg_type != 0x0116 /* Set Active Destination */) { |
447 | 0 | NDPI_LOG_DBG(ndpi_struct, "No classic-stun 0x%x\n", msg_type); |
448 | 0 | return 0; |
449 | 0 | } |
450 | | |
451 | 0 | off = STUN_HDR_LEN; |
452 | 0 | while(off + 4 < payload_length) { |
453 | 0 | u_int16_t len = ntohs(*((u_int16_t *)&payload[off + 2])); |
454 | 0 | u_int16_t real_len = (len + 3) & 0xFFFFFFFC; |
455 | |
|
456 | 0 | off += 4 + real_len; |
457 | 0 | } |
458 | 0 | if(off != payload_length) { |
459 | 0 | NDPI_LOG_DBG(ndpi_struct, "No classic-stun %d/%d\n", off, payload_length); |
460 | 0 | return 0; |
461 | 0 | } |
462 | 0 | } |
463 | | |
464 | | /* STUN */ |
465 | | |
466 | 0 | if(flow->monit == NULL && |
467 | 0 | is_monitoring_enabled(ndpi_struct, NDPI_PROTOCOL_STUN)) |
468 | 0 | flow->monit = ndpi_calloc(1, sizeof(struct ndpi_metadata_monitoring)); |
469 | |
|
470 | 0 | if(msg_type == 0x0800 || msg_type == 0x0801 || msg_type == 0x0802 || |
471 | 0 | msg_type == 0x0804 || msg_type == 0x0805) { |
472 | 0 | *app_proto = NDPI_PROTOCOL_WHATSAPP_CALL; |
473 | 0 | return 1; |
474 | 0 | } |
475 | | |
476 | 0 | method = (msg_type & 0x000F) | ((msg_type & 0x00E0) >> 1) | ((msg_type & 0x3E00) >> 2); |
477 | 0 | switch(method) { |
478 | 0 | case METHOD_ALLOCATE: |
479 | 0 | case METHOD_REFRESH: |
480 | 0 | case METHOD_SEND: |
481 | 0 | case METHOD_DATA_IND: |
482 | 0 | case METHOD_DATA_IND_OLD: |
483 | 0 | case METHOD_CREATE_PERMISSION: |
484 | 0 | case METHOD_CHANNELBIND: |
485 | 0 | case METHOD_CONNECT: |
486 | 0 | case METHOD_CONNECTION_BIND: |
487 | 0 | case METHOD_CONNECTION_ATTEMPT: |
488 | 0 | NDPI_LOG_DBG(ndpi_struct, "TURN flow (method %d)\n", method); |
489 | 0 | flow->stun.is_turn = 1; |
490 | 0 | break; |
491 | 0 | } |
492 | | |
493 | | /* See https://support.signal.org/hc/en-us/articles/360007320291-Firewall-and-Internet-settings. |
494 | | Since the check is quite weak, give time to other applications to kick in */ |
495 | 0 | if(flow->packet_counter > 4 && !flow->stun.is_turn && |
496 | 0 | !is_subclassification_real(flow) && |
497 | 0 | (ntohs(flow->c_port) == 10000 || ntohs(flow->s_port) == 10000)) { |
498 | 0 | *app_proto = NDPI_PROTOCOL_SIGNAL_VOIP; |
499 | 0 | } |
500 | |
|
501 | 0 | if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_TELEGRAM) |
502 | 0 | *app_proto = NDPI_PROTOCOL_TELEGRAM_VOIP; |
503 | |
|
504 | 0 | off = STUN_HDR_LEN; |
505 | 0 | while(off + 4 < payload_length) { |
506 | 0 | u_int16_t attribute = ntohs(*((u_int16_t *)&payload[off])); |
507 | 0 | u_int16_t len = ntohs(*((u_int16_t *)&payload[off + 2])); |
508 | 0 | u_int16_t real_len = (len + 3) & 0xFFFFFFFC; |
509 | |
|
510 | 0 | NDPI_LOG_DBG(ndpi_struct, "Attribute 0x%x (%d/%d)\n", attribute, len, real_len); |
511 | |
|
512 | 0 | switch(attribute) { |
513 | 0 | case 0x0001: /* MAPPED-ADDRESS */ |
514 | 0 | if(ndpi_struct->cfg.stun_mapped_address_enabled) { |
515 | 0 | parse_ip_port_attribute(payload, payload_length, off, real_len, &flow->stun.mapped_address, |
516 | 0 | flow->monit ? &flow->monit->protos.dtls_stun_rtp.mapped_address : NULL); |
517 | 0 | } |
518 | 0 | break; |
519 | | |
520 | 0 | case 0x802b: /* RESPONSE-ORIGIN */ |
521 | 0 | if(ndpi_struct->cfg.stun_response_origin_enabled) { |
522 | 0 | parse_ip_port_attribute(payload, payload_length, off, real_len, &flow->stun.response_origin, |
523 | 0 | flow->monit ? &flow->monit->protos.dtls_stun_rtp.response_origin : NULL); |
524 | 0 | } |
525 | 0 | break; |
526 | | |
527 | 0 | case 0x802c: /* OTHER-ADDRESS */ |
528 | 0 | if(ndpi_struct->cfg.stun_other_address_enabled) { |
529 | 0 | parse_ip_port_attribute(payload, payload_length, off, real_len, &flow->stun.other_address, |
530 | 0 | flow->monit ? &flow->monit->protos.dtls_stun_rtp.other_address : NULL); |
531 | 0 | } |
532 | 0 | break; |
533 | | |
534 | 0 | case 0x0012: /* XOR-PEER-ADDRESS */ |
535 | 0 | if(ndpi_struct->cfg.stun_peer_address_enabled) { |
536 | 0 | parse_xor_ip_port_attribute(ndpi_struct, flow, |
537 | 0 | payload, payload_length, off, real_len, |
538 | 0 | &flow->stun.peer_address, |
539 | 0 | flow->monit ? &flow->monit->protos.dtls_stun_rtp.peer_address : NULL, |
540 | 0 | transaction_id, magic_cookie, 1); |
541 | 0 | } |
542 | 0 | break; |
543 | | |
544 | 0 | case 0x0101: |
545 | 0 | case 0x0103: |
546 | 0 | *app_proto = NDPI_PROTOCOL_ZOOM; |
547 | 0 | return 1; |
548 | | |
549 | 0 | case 0x4000: |
550 | 0 | case 0x4001: |
551 | 0 | case 0x4002: |
552 | 0 | case 0x4003: |
553 | 0 | case 0x4004: |
554 | 0 | case 0x4007: |
555 | | /* These are the only messages apparently whatsapp voice can use */ |
556 | 0 | *app_proto = NDPI_PROTOCOL_WHATSAPP_CALL; |
557 | 0 | break; |
558 | | |
559 | 0 | case 0x0014: /* Realm */ |
560 | 0 | if(flow->host_server_name[0] == '\0') { |
561 | 0 | int i; |
562 | 0 | bool valid = true; |
563 | |
|
564 | 0 | ndpi_hostname_sni_set(flow, payload + off + 4, ndpi_min(len, payload_length - off - 4), NDPI_HOSTNAME_NORM_ALL); |
565 | 0 | NDPI_LOG_DBG(ndpi_struct, "Realm [%s]\n", flow->host_server_name); |
566 | | |
567 | | /* Some Realm contain junk, so let's validate it */ |
568 | 0 | for(i=0; flow->host_server_name[i] != '\0'; i++) { |
569 | 0 | if(flow->host_server_name[i] == '?') { |
570 | 0 | valid = false; |
571 | 0 | break; |
572 | 0 | } |
573 | 0 | } |
574 | |
|
575 | 0 | if(valid) { |
576 | 0 | if(strstr(flow->host_server_name, "google.com") != NULL) { |
577 | 0 | *app_proto = NDPI_PROTOCOL_GOOGLE_CALL; |
578 | 0 | } else if(strstr(flow->host_server_name, "whispersystems.org") != NULL || |
579 | 0 | strstr(flow->host_server_name, "signal.org") != NULL) { |
580 | 0 | *app_proto = NDPI_PROTOCOL_SIGNAL_VOIP; |
581 | 0 | } else if(strstr(flow->host_server_name, "facebook") != NULL) { |
582 | 0 | *app_proto = NDPI_PROTOCOL_FACEBOOK_VOIP; |
583 | 0 | } else if(strstr(flow->host_server_name, "stripcdn.com") != NULL) { |
584 | 0 | *category = NDPI_PROTOCOL_CATEGORY_ADULT_CONTENT; |
585 | 0 | } else if(strstr(flow->host_server_name, "telegram") != NULL) { |
586 | 0 | *app_proto = NDPI_PROTOCOL_TELEGRAM_VOIP; |
587 | 0 | } else if(strstr(flow->host_server_name, "viber") != NULL) { |
588 | 0 | *app_proto = NDPI_PROTOCOL_VIBER_VOIP; |
589 | 0 | } else if(strstr(flow->host_server_name, "turn.cloudflare.com") != NULL) { |
590 | | /* The latest signal implementations hide behind cloudflare */ |
591 | 0 | if(signal_search_into_cache(ndpi_struct, flow)) { |
592 | 0 | *app_proto = NDPI_PROTOCOL_SIGNAL_VOIP; |
593 | 0 | } |
594 | 0 | } |
595 | 0 | } else |
596 | 0 | flow->host_server_name[0] = '\0'; |
597 | 0 | } |
598 | 0 | break; |
599 | | |
600 | | /* Proprietary fields found on Microsoft Teams/Skype calls */ |
601 | 0 | case 0x8054: /* Candidate Identifier: Either skype for business or "normal" skype with multiparty call */ |
602 | 0 | case 0x24DF: |
603 | 0 | case 0x3802: |
604 | 0 | case 0x8036: |
605 | 0 | case 0x8095: /* MS-Multiplexed-TURN-Session-ID */ |
606 | 0 | case 0x0800: |
607 | 0 | case 0x8006: |
608 | 0 | case 0x8070: /* MS Implementation Version */ |
609 | 0 | case 0x8055: /* MS Service Quality */ |
610 | 0 | *app_proto = NDPI_PROTOCOL_MSTEAMS_CALL; |
611 | 0 | break; |
612 | | |
613 | 0 | case 0x8029: /* ICE-CONTROLLED */ |
614 | 0 | if(current_pkt_from_client_to_server(ndpi_struct, flow)) |
615 | 0 | flow->stun.is_client_controlling = 0; |
616 | 0 | else |
617 | 0 | flow->stun.is_client_controlling = 1; |
618 | 0 | break; |
619 | | |
620 | 0 | case 0x802A: /* ICE-CONTROLLING */ |
621 | 0 | if(current_pkt_from_client_to_server(ndpi_struct, flow)) |
622 | 0 | flow->stun.is_client_controlling = 1; |
623 | 0 | else |
624 | 0 | flow->stun.is_client_controlling = 0; |
625 | 0 | break; |
626 | | |
627 | 0 | case 0xFF03: |
628 | 0 | *app_proto = NDPI_PROTOCOL_GOOGLE_CALL; |
629 | 0 | break; |
630 | | |
631 | 0 | case 0x0013: |
632 | 0 | NDPI_LOG_DBG(ndpi_struct, "DATA attribute (%d/%d)\n", |
633 | 0 | real_len, payload_length - off - 4); |
634 | 0 | if(real_len <= payload_length - off - 4) { |
635 | 0 | orig_payload = packet->payload; |
636 | 0 | orig_payload_length = packet->payload_packet_len; |
637 | 0 | packet->payload = payload + off + 4; |
638 | 0 | packet->payload_packet_len = real_len; |
639 | |
|
640 | 0 | stun_search_again(ndpi_struct, flow); |
641 | 0 | NDPI_LOG_DBG(ndpi_struct, "End recursion\n"); |
642 | |
|
643 | 0 | packet->payload = orig_payload; |
644 | 0 | packet->payload_packet_len = orig_payload_length; |
645 | 0 | } |
646 | 0 | break; |
647 | | |
648 | 0 | case 0x0020: /* XOR-MAPPED-ADDRESS */ |
649 | 0 | if(ndpi_struct->cfg.stun_mapped_address_enabled) { |
650 | 0 | parse_xor_ip_port_attribute(ndpi_struct, flow, |
651 | 0 | payload, payload_length, off, real_len, |
652 | 0 | &flow->stun.mapped_address, |
653 | 0 | flow->monit ? &flow->monit->protos.dtls_stun_rtp.mapped_address : NULL, |
654 | 0 | transaction_id, magic_cookie, 0); |
655 | 0 | flow->stun.num_xor_mapped_addresses++; |
656 | 0 | } |
657 | 0 | break; |
658 | | |
659 | 0 | case 0x0016: /* XOR-RELAYED-ADDRESS */ |
660 | 0 | if(ndpi_struct->cfg.stun_relayed_address_enabled) { |
661 | 0 | parse_xor_ip_port_attribute(ndpi_struct, flow, |
662 | 0 | payload, payload_length, off, real_len, |
663 | 0 | &flow->stun.relayed_address, |
664 | 0 | flow->monit ? &flow->monit->protos.dtls_stun_rtp.relayed_address : NULL, |
665 | 0 | transaction_id, magic_cookie, 0); |
666 | 0 | flow->stun.num_xor_relayed_addresses++; |
667 | 0 | } |
668 | 0 | break; |
669 | | |
670 | 0 | default: |
671 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Unknown attribute %04X\n", attribute); |
672 | 0 | break; |
673 | 0 | } |
674 | | |
675 | 0 | off += 4 + real_len; |
676 | 0 | } |
677 | | |
678 | 0 | return 1; |
679 | 0 | } |
680 | | |
681 | | /* ***************************************************** */ |
682 | | |
683 | | static int keep_extra_dissection(struct ndpi_detection_module_struct *ndpi_struct, |
684 | | struct ndpi_flow_struct *flow) |
685 | 0 | { |
686 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
687 | | |
688 | | /* We want extra dissection for: |
689 | | * sub-classification |
690 | | * metadata extraction (*-ADDRESS) or looking for RTP |
691 | | * At the moment: |
692 | | * it seems ZOOM doens't have any meaningful attributes |
693 | | * we want (all) XOR-PEER-ADDRESS only for Telegram. |
694 | | * for the other protocols, we stop after we have all metadata (if enabled) |
695 | | * for some specific protocol, we might know that some attributes are never used |
696 | | * if monitoring is enabled, keep looking for (S)RTP anyway |
697 | | |
698 | | **After** extra dissection is ended, we might move to monitoring. Note that: |
699 | | * classification doesn't change while in monitoring! |
700 | | */ |
701 | |
|
702 | 0 | if(packet->udp |
703 | 0 | && (ntohs(packet->udp->source) == 3478) |
704 | 0 | && (packet->payload_packet_len > 0) |
705 | 0 | && (packet->payload[0] != 0x0) && (packet->payload[0] != 0x1)) { |
706 | 0 | if(flow->stun.num_non_stun_pkt < 2) { |
707 | 0 | flow->stun.non_stun_pkt_len[flow->stun.num_non_stun_pkt++] = packet->payload_packet_len; |
708 | |
|
709 | | #ifdef STUN_DEBUG |
710 | | if(flow->stun.num_non_stun_pkt == 2) |
711 | | printf("%d %d\n", flow->stun.non_stun_pkt_len[0], flow->stun.non_stun_pkt_len[1]); |
712 | | #endif |
713 | 0 | } |
714 | 0 | } |
715 | |
|
716 | 0 | if(packet->payload_packet_len > 699) { |
717 | 0 | if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_TELEGRAM_VOIP) { |
718 | 0 | if((packet->payload[0] == 0x16) && (packet->payload[1] == 0xfe) |
719 | 0 | && ((packet->payload[2] == 0xff) /* DTLS 1.0 */ |
720 | 0 | || (packet->payload[2] == 0xfd) /* DTLS 1.2 */ )) |
721 | 0 | ; /* Skip DTLS */ |
722 | 0 | else { |
723 | | /* STUN or RTP */ |
724 | | /* This packet is too big to be audio: add video */ |
725 | 0 | flow->flow_multimedia_types |= ndpi_multimedia_video_flow; |
726 | 0 | } |
727 | 0 | } |
728 | 0 | } |
729 | |
|
730 | 0 | if(flow->monitoring) |
731 | 0 | return 1; |
732 | | |
733 | 0 | if(flow->num_extra_packets_checked + 1 == flow->max_extra_packets_to_check) { |
734 | 0 | if(is_monitoring_enabled(ndpi_struct, NDPI_PROTOCOL_STUN)) { |
735 | 0 | NDPI_LOG_DBG(ndpi_struct, "Enabling monitoring (end extra dissection)\n"); |
736 | 0 | flow->monitoring = 1; |
737 | 0 | return 1; |
738 | 0 | } |
739 | 0 | } |
740 | | |
741 | 0 | if(!is_subclassification_real(flow)) |
742 | 0 | return 1; |
743 | | |
744 | 0 | if(is_monitoring_enabled(ndpi_struct, NDPI_PROTOCOL_STUN) && |
745 | 0 | (flow->detected_protocol_stack[1] != NDPI_PROTOCOL_SRTP && |
746 | 0 | flow->detected_protocol_stack[1] != NDPI_PROTOCOL_DTLS)) |
747 | 0 | return 1; |
748 | | |
749 | 0 | if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_TELEGRAM_VOIP && |
750 | 0 | ndpi_struct->cfg.stun_peer_address_enabled) |
751 | 0 | return 1; |
752 | | |
753 | | /* General rule */ |
754 | 0 | if((flow->stun.mapped_address.port || !ndpi_struct->cfg.stun_mapped_address_enabled) && |
755 | 0 | (flow->stun.peer_address.port || !ndpi_struct->cfg.stun_peer_address_enabled) && |
756 | 0 | (flow->stun.relayed_address.port || !ndpi_struct->cfg.stun_relayed_address_enabled) && |
757 | 0 | (flow->stun.response_origin.port || !ndpi_struct->cfg.stun_response_origin_enabled) && |
758 | 0 | (flow->stun.other_address.port || !ndpi_struct->cfg.stun_other_address_enabled)) { |
759 | 0 | if(is_monitoring_enabled(ndpi_struct, NDPI_PROTOCOL_STUN)) { |
760 | 0 | NDPI_LOG_DBG(ndpi_struct, "Enabling monitoring (found all metadata)\n"); |
761 | 0 | flow->monitoring = 1; |
762 | 0 | return 1; |
763 | 0 | } |
764 | 0 | return 0; |
765 | 0 | } |
766 | | |
767 | | /* Exception WA: only relayed and mapped address attributes but we keep looking for RTP packets */ |
768 | 0 | if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_WHATSAPP_CALL && |
769 | 0 | flow->detected_protocol_stack[1] == NDPI_PROTOCOL_SRTP && |
770 | 0 | (flow->stun.mapped_address.port || !ndpi_struct->cfg.stun_mapped_address_enabled) && |
771 | 0 | (flow->stun.relayed_address.port || !ndpi_struct->cfg.stun_relayed_address_enabled)) { |
772 | 0 | if(is_monitoring_enabled(ndpi_struct, NDPI_PROTOCOL_STUN)) { |
773 | 0 | NDPI_LOG_DBG(ndpi_struct, "Enabling monitor (found all metadata; wa case)\n"); |
774 | 0 | flow->monitoring = 1; |
775 | 0 | return 1; |
776 | 0 | } |
777 | 0 | return 0; |
778 | 0 | } |
779 | | |
780 | | /* Exception Zoom: no metadata */ |
781 | 0 | if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_ZOOM) { |
782 | 0 | if(is_monitoring_enabled(ndpi_struct, NDPI_PROTOCOL_STUN)) { |
783 | 0 | NDPI_LOG_DBG(ndpi_struct, "Enabling monitor (zoom case)\n"); |
784 | 0 | flow->monitoring = 1; |
785 | 0 | return 1; |
786 | 0 | } |
787 | 0 | return 0; |
788 | 0 | } |
789 | | |
790 | 0 | return 1; |
791 | 0 | } |
792 | | |
793 | | /* ***************************************************** */ |
794 | | |
795 | 0 | static u_int32_t __get_master(struct ndpi_flow_struct *flow) { |
796 | |
|
797 | 0 | if(flow->detected_protocol_stack[1] != NDPI_PROTOCOL_UNKNOWN) |
798 | 0 | return flow->detected_protocol_stack[1]; |
799 | 0 | if(flow->detected_protocol_stack[0] != NDPI_PROTOCOL_UNKNOWN && |
800 | 0 | flow->detected_protocol_stack[0] != NDPI_PROTOCOL_TELEGRAM) |
801 | 0 | return flow->detected_protocol_stack[0]; |
802 | 0 | return NDPI_PROTOCOL_STUN; |
803 | 0 | } |
804 | | |
805 | | /* ***************************************************** */ |
806 | | |
807 | | static int stun_search_again(struct ndpi_detection_module_struct *ndpi_struct, |
808 | | struct ndpi_flow_struct *flow) |
809 | 0 | { |
810 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
811 | 0 | int rtp_rtcp; |
812 | 0 | u_int8_t first_byte; |
813 | 0 | u_int16_t msg_type, app_proto = NDPI_PROTOCOL_UNKNOWN; |
814 | 0 | u_int32_t unused; |
815 | 0 | int first_dtls_pkt = 0; |
816 | 0 | u_int16_t old_proto_stack[2] = {NDPI_PROTOCOL_UNKNOWN, NDPI_PROTOCOL_UNKNOWN}; |
817 | |
|
818 | 0 | NDPI_LOG_DBG2(ndpi_struct, "Packet counter %d protos %d/%d Monitoring? %d\n", |
819 | 0 | flow->packet_counter, |
820 | 0 | flow->detected_protocol_stack[0], flow->detected_protocol_stack[1], |
821 | 0 | flow->monitoring); |
822 | | |
823 | | /* TODO: check TCP support. We need to pay some attention because: |
824 | | * multiple msg in the same TCP segment |
825 | | * same msg split across multiple segments */ |
826 | |
|
827 | 0 | if(packet->payload_packet_len <= 1) |
828 | 0 | return keep_extra_dissection(ndpi_struct, flow); |
829 | | |
830 | 0 | first_byte = packet->payload[0]; |
831 | 0 | msg_type = ntohs(*((u_int16_t *)&packet->payload[0])); |
832 | | |
833 | | /* RFC9443 */ |
834 | 0 | if(first_byte <= 3 || |
835 | | /* Whatsapp special case */ |
836 | 0 | (flow->detected_protocol_stack[0] == NDPI_PROTOCOL_WHATSAPP_CALL && |
837 | 0 | (msg_type == 0x0800 || msg_type == 0x0801 || msg_type == 0x0802 || |
838 | 0 | msg_type == 0x0804 || msg_type == 0x0805))) { |
839 | 0 | ndpi_protocol_category_t category; |
840 | | |
841 | 0 | NDPI_LOG_DBG(ndpi_struct, "Still STUN\n"); |
842 | | |
843 | 0 | if(is_stun(ndpi_struct, flow, &app_proto, &category) == 1) { /* To extract other metadata */ |
844 | 0 | if(is_new_subclassification_better(ndpi_struct, flow, app_proto)) { |
845 | 0 | ndpi_int_stun_add_connection(ndpi_struct, flow, |
846 | 0 | app_proto, __get_master(flow), category); |
847 | 0 | } |
848 | 0 | } |
849 | 0 | } else if(first_byte <= 15) { |
850 | 0 | NDPI_LOG_DBG(ndpi_struct, "DROP range. Unexpected\n"); |
851 | 0 | } else if(first_byte <= 19) { |
852 | 0 | NDPI_LOG_DBG(ndpi_struct, "ZRTP range. Unexpected\n"); |
853 | 0 | } else if(first_byte <= 63) { |
854 | 0 | NDPI_LOG_DBG(ndpi_struct, "DTLS\n"); |
855 | |
|
856 | 0 | if(ndpi_struct->cfg.stun_opportunistic_tls_enabled && |
857 | 0 | is_dtls(packet->payload, packet->payload_packet_len, &unused)) { |
858 | | |
859 | | /* Process this DTLS packet via TLS/DTLS code but keep using STUN dissection. |
860 | | This way we can keep demultiplexing DTLS/STUN/RTP */ |
861 | | |
862 | | /* Switching to TLS dissector is tricky, because we are calling one dissector |
863 | | from another one, and that is not a common operation... |
864 | | Additionally: |
865 | | * at that point protocol stack is already set to STUN or STUN/XXX |
866 | | * we have room for only two protocols in flow->detected_protocol_stack[] so |
867 | | we can't have something like STUN/DTLS/SNAPCHAT_CALL |
868 | | * the easiest (!?) solution is to remove everything, and let the TLS dissector |
869 | | to set both master (i.e. DTLS) and subprotocol (if any) */ |
870 | | |
871 | | /* If we already have a real sub-classification, and the DTLS code doesn't set any |
872 | | subclassification iself (it is quite unlikely that we have a subprotocol only via |
873 | | Client Hello, for example), keep the original one */ |
874 | | |
875 | | /* In same rare cases, with malformed/fuzzed traffic, `is_dtls()` might return false |
876 | | positives. In that case, the TLS dissector doesn't set the master protocol, so we |
877 | | need to rollback to the current state */ |
878 | |
|
879 | 0 | if(flow->tls_quic.certificate_processed == 1) { |
880 | 0 | NDPI_LOG_DBG(ndpi_struct, "Interesting DTLS stuff already processed. Ignoring\n"); |
881 | 0 | } else if(!flow->monitoring) { |
882 | 0 | NDPI_LOG_DBG(ndpi_struct, "Switch to DTLS (%d/%d)\n", |
883 | 0 | flow->detected_protocol_stack[0], flow->detected_protocol_stack[1]); |
884 | |
|
885 | 0 | if(flow->stun.maybe_dtls == 0) { |
886 | | /* First DTLS packet of the flow */ |
887 | 0 | first_dtls_pkt = 1; |
888 | | |
889 | | /* We might need to rollback this change... */ |
890 | 0 | old_proto_stack[0] = flow->detected_protocol_stack[0]; |
891 | 0 | old_proto_stack[1] = flow->detected_protocol_stack[1]; |
892 | | |
893 | | /* TODO: right way? It is a bit scary... do we need to reset something else too? */ |
894 | 0 | reset_detected_protocol(flow); |
895 | | /* We keep the category/breed related to STUN traffic */ |
896 | | /* TODO: clear some risks? */ |
897 | | |
898 | | /* Give room for DTLS handshake, where we might have |
899 | | retransmissions and fragments */ |
900 | 0 | flow->max_extra_packets_to_check = ndpi_min(255, (int)flow->max_extra_packets_to_check + 10); |
901 | 0 | flow->stun.maybe_dtls = 1; |
902 | 0 | } |
903 | |
|
904 | 0 | switch_to_tls(ndpi_struct, flow, first_dtls_pkt); |
905 | |
|
906 | 0 | if(first_dtls_pkt && |
907 | 0 | flow->detected_protocol_stack[0] == NDPI_PROTOCOL_DTLS && |
908 | 0 | flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN && |
909 | 0 | old_proto_stack[0] != NDPI_PROTOCOL_UNKNOWN && |
910 | 0 | old_proto_stack[0] != NDPI_PROTOCOL_STUN) { |
911 | 0 | NDPI_LOG_DBG(ndpi_struct, "Keeping old subclassification %d\n", old_proto_stack[0]); |
912 | 0 | ndpi_int_stun_add_connection(ndpi_struct, flow, |
913 | 0 | old_proto_stack[0] == NDPI_PROTOCOL_RTP ? NDPI_PROTOCOL_SRTP : old_proto_stack[0], |
914 | 0 | __get_master(flow), NDPI_PROTOCOL_CATEGORY_UNSPECIFIED); |
915 | 0 | } |
916 | | |
917 | | /* If this is not a real DTLS packet, we need to restore the old state */ |
918 | 0 | if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN && |
919 | 0 | first_dtls_pkt) { |
920 | 0 | NDPI_LOG_DBG(ndpi_struct, "Switch to TLS failed. Rollback to old classification\n"); |
921 | |
|
922 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, |
923 | 0 | old_proto_stack[0], old_proto_stack[1], |
924 | 0 | NDPI_CONFIDENCE_DPI); |
925 | |
|
926 | 0 | flow->stun.maybe_dtls = 0; |
927 | 0 | flow->max_extra_packets_to_check -= 10; |
928 | 0 | } |
929 | |
|
930 | 0 | NDPI_LOG_DBG(ndpi_struct, "(%d/%d)\n", |
931 | 0 | flow->detected_protocol_stack[0], flow->detected_protocol_stack[1]); |
932 | 0 | } else { |
933 | 0 | NDPI_LOG_DBG(ndpi_struct, "Skip DTLS packet because in monitoring\n"); |
934 | 0 | } |
935 | 0 | } |
936 | 0 | } else if(first_byte <= 79) { |
937 | 0 | if(flow->stun.is_turn) { |
938 | 0 | NDPI_LOG_DBG(ndpi_struct, "TURN range\n"); |
939 | |
|
940 | 0 | if(packet->payload_packet_len >= 4) { |
941 | 0 | u_int16_t ch_len; |
942 | |
|
943 | 0 | ch_len = ntohs(*(u_int16_t *)&packet->payload[2]); |
944 | |
|
945 | 0 | if(ch_len <= packet->payload_packet_len - 4) { |
946 | 0 | const u_int8_t *orig_payload; |
947 | 0 | u_int16_t orig_payload_length; |
948 | |
|
949 | 0 | orig_payload = packet->payload; |
950 | 0 | orig_payload_length = packet->payload_packet_len; |
951 | 0 | packet->payload = packet->payload + 4; |
952 | 0 | packet->payload_packet_len = ch_len; |
953 | |
|
954 | 0 | stun_search_again(ndpi_struct, flow); |
955 | 0 | NDPI_LOG_DBG(ndpi_struct, "End recursion on turn channel\n"); |
956 | |
|
957 | 0 | packet->payload = orig_payload; |
958 | 0 | packet->payload_packet_len = orig_payload_length; |
959 | |
|
960 | 0 | } else { |
961 | 0 | if(flow->l4_proto == IPPROTO_UDP) /* The error is quite common on TCP since we don't reassemble msgs */ |
962 | 0 | NDPI_LOG_DBG(ndpi_struct, "Invalid channel length %d %d\n", |
963 | 0 | ch_len, packet->payload_packet_len - 4); |
964 | 0 | } |
965 | 0 | } |
966 | 0 | } else { |
967 | 0 | NDPI_LOG_DBG(ndpi_struct, "QUIC range (not turn). Unexpected\n"); |
968 | 0 | } |
969 | 0 | } else if(first_byte <= 127) { |
970 | 0 | NDPI_LOG_DBG(ndpi_struct, "QUIC range. Unexpected\n"); |
971 | 0 | } else if(first_byte <= 191) { |
972 | |
|
973 | 0 | rtp_rtcp = is_rtp_or_rtcp(ndpi_struct, packet->payload, packet->payload_packet_len, NULL); |
974 | 0 | if(rtp_rtcp == IS_RTP) { |
975 | 0 | NDPI_LOG_DBG(ndpi_struct, "RTP (dir %d) [%d/%d]\n", packet->packet_direction, |
976 | 0 | flow->stun.rtp_counters[0], flow->stun.rtp_counters[1]); |
977 | |
|
978 | 0 | flow->stun.rtp_counters[packet->packet_direction]++; |
979 | | /* TODO: store RTP information in 'struct rtp_info' */ |
980 | 0 | NDPI_LOG_INFO(ndpi_struct, "Found RTP over STUN\n"); |
981 | |
|
982 | 0 | if(flow->stun.t_start != 0) { |
983 | 0 | flow->stun.t_end = ndpi_get_current_time(flow); |
984 | 0 | } else if(flow->stun.rtp_counters[0] != 0 && flow->stun.rtp_counters[1] != 0) { |
985 | 0 | flow->stun.t_start = ndpi_get_current_time(flow); |
986 | 0 | flow->stun.t_end = ndpi_get_current_time(flow); |
987 | 0 | } |
988 | |
|
989 | 0 | rtp_get_stream_type(packet->payload[1] & 0x7F, &flow->flow_multimedia_types, flow->detected_protocol_stack[0]); |
990 | |
|
991 | 0 | if(flow->detected_protocol_stack[0] != NDPI_PROTOCOL_RTP && |
992 | 0 | flow->detected_protocol_stack[0] != NDPI_PROTOCOL_RTCP && |
993 | 0 | flow->detected_protocol_stack[1] != NDPI_PROTOCOL_SRTP) { |
994 | |
|
995 | 0 | if(flow->detected_protocol_stack[1] != NDPI_PROTOCOL_UNKNOWN) { |
996 | 0 | if(flow->detected_protocol_stack[1] == NDPI_PROTOCOL_DTLS) { |
997 | | /* Keep DTLS/SUBPROTO since we already wrote to flow->protos.tls_quic */ |
998 | 0 | } else { |
999 | | /* STUN/SUBPROTO -> SRTP/SUBPROTO */ |
1000 | 0 | ndpi_int_stun_add_connection(ndpi_struct, flow, |
1001 | 0 | flow->detected_protocol_stack[0], NDPI_PROTOCOL_SRTP, |
1002 | 0 | NDPI_PROTOCOL_CATEGORY_UNSPECIFIED); |
1003 | 0 | } |
1004 | 0 | } else { |
1005 | | /* STUN -> STUN/RTP, or |
1006 | | DTLS -> DTLS/SRTP */ |
1007 | 0 | ndpi_int_stun_add_connection(ndpi_struct, flow, |
1008 | 0 | __get_master(flow) == NDPI_PROTOCOL_STUN ? NDPI_PROTOCOL_RTP: NDPI_PROTOCOL_SRTP, |
1009 | 0 | __get_master(flow), NDPI_PROTOCOL_CATEGORY_UNSPECIFIED); |
1010 | 0 | } |
1011 | 0 | } else if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_RTCP && |
1012 | 0 | flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN) { |
1013 | | /* From RTP dissector; if we have RTP and RTCP multiplexed together (but not STUN, yet) we always |
1014 | | use RTP, as we do in RTP dissector */ |
1015 | 0 | if(!flow->monitoring) |
1016 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN, NDPI_PROTOCOL_RTP, NDPI_CONFIDENCE_DPI); |
1017 | 0 | else |
1018 | 0 | NDPI_LOG_DBG(ndpi_struct, "Skip RTP packet because in monitoring\n"); |
1019 | 0 | } |
1020 | 0 | } else if(rtp_rtcp == IS_RTCP) { |
1021 | 0 | NDPI_LOG_DBG(ndpi_struct, "RTCP\n"); |
1022 | 0 | flow->stun.rtcp_seen = 1; |
1023 | 0 | } else { |
1024 | 0 | NDPI_LOG_DBG(ndpi_struct, "Unexpected\n"); |
1025 | 0 | } |
1026 | 0 | } else { |
1027 | | /* Microsoft Multiplexed TURN messages. |
1028 | | See: https://msopenspecs.azureedge.net/files/MS-TURN/%5bMS-TURN%5d.pdf 2.2.3 */ |
1029 | 0 | if(packet->payload_packet_len >= 12 && |
1030 | 0 | ntohs(get_u_int16_t(packet->payload, 0)) == 0xFF10 && |
1031 | 0 | flow->detected_protocol_stack[0] == NDPI_PROTOCOL_MSTEAMS_CALL) { |
1032 | 0 | u_int16_t ch_len; |
1033 | |
|
1034 | 0 | ch_len = ntohs(get_u_int16_t(packet->payload, 2)); |
1035 | |
|
1036 | 0 | if(ch_len == packet->payload_packet_len - 4 && |
1037 | 0 | ch_len >= 8) { |
1038 | 0 | const u_int8_t *orig_payload; |
1039 | 0 | u_int16_t orig_payload_length; |
1040 | |
|
1041 | 0 | orig_payload = packet->payload; |
1042 | 0 | orig_payload_length = packet->payload_packet_len; |
1043 | 0 | packet->payload = packet->payload + 12; |
1044 | 0 | packet->payload_packet_len = ch_len - 8; |
1045 | |
|
1046 | 0 | stun_search_again(ndpi_struct, flow); |
1047 | |
|
1048 | 0 | NDPI_LOG_DBG(ndpi_struct, "End recursion on MS channel\n"); |
1049 | |
|
1050 | 0 | packet->payload = orig_payload; |
1051 | 0 | packet->payload_packet_len = orig_payload_length; |
1052 | |
|
1053 | 0 | } else { |
1054 | 0 | NDPI_LOG_DBG(ndpi_struct, "Invalid MS channel length %d %d\n", |
1055 | 0 | ch_len, packet->payload_packet_len - 4); |
1056 | 0 | } |
1057 | 0 | } else { |
1058 | 0 | NDPI_LOG_DBG(ndpi_struct, "QUIC other range. Unexpected\n"); |
1059 | 0 | } |
1060 | 0 | } |
1061 | 0 | return keep_extra_dissection(ndpi_struct, flow); |
1062 | 0 | } |
1063 | | |
1064 | | /* ************************************************************ */ |
1065 | | |
1066 | | static int stun_telegram_search_again(struct ndpi_detection_module_struct *ndpi_struct, |
1067 | | struct ndpi_flow_struct *flow) |
1068 | 0 | { |
1069 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
1070 | 0 | const u_int8_t *orig_payload; |
1071 | 0 | u_int16_t orig_payload_length; |
1072 | 0 | char pattern[12] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
1073 | 0 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; |
1074 | 0 | u_int16_t length; |
1075 | |
|
1076 | 0 | NDPI_LOG_DBG2(ndpi_struct, "[T] Packet counter %d protos %d/%d Monitoring? %d\n", |
1077 | 0 | flow->packet_counter, |
1078 | 0 | flow->detected_protocol_stack[0], flow->detected_protocol_stack[1], |
1079 | 0 | flow->monitoring); |
1080 | | |
1081 | | /* For SOME of its STUN flows, Telegram uses a custom encapsulation |
1082 | | There is no documentation. It seems: |
1083 | | * some unknown packets (especially at the beginning/end of the flow) have a bunch of 0xFF |
1084 | | * the other packets encapsulate standard STUN/DTLS/RTP payload at offset 24 |
1085 | | (with a previous field containing the payload length) |
1086 | | */ |
1087 | |
|
1088 | 0 | if(packet->payload_packet_len <= 28) { |
1089 | 0 | NDPI_LOG_DBG(ndpi_struct, "Malformed custom Telegram packet (too short)\n"); |
1090 | 0 | return keep_extra_dissection(ndpi_struct, flow); |
1091 | 0 | } |
1092 | | |
1093 | 0 | if(memcmp(&packet->payload[16], pattern, sizeof(pattern)) == 0) { |
1094 | 0 | NDPI_LOG_DBG(ndpi_struct, "Custom/Unknown Telegram packet\n"); |
1095 | 0 | return keep_extra_dissection(ndpi_struct, flow); |
1096 | 0 | } |
1097 | | |
1098 | | /* It should be STUN/DTLS/RTP */ |
1099 | | |
1100 | 0 | length = ntohs(*(u_int16_t *)&packet->payload[22]); |
1101 | 0 | if(24 + length > packet->payload_packet_len) { |
1102 | 0 | NDPI_LOG_DBG(ndpi_struct, "Malformed custom Telegram packet (too long: %d %d)\n", |
1103 | 0 | length, packet->payload_packet_len); |
1104 | 0 | return keep_extra_dissection(ndpi_struct, flow); |
1105 | 0 | } |
1106 | | |
1107 | 0 | orig_payload = packet->payload; |
1108 | 0 | orig_payload_length = packet->payload_packet_len ; |
1109 | 0 | packet->payload = packet->payload + 24; |
1110 | 0 | packet->payload_packet_len = length; |
1111 | |
|
1112 | 0 | stun_search_again(ndpi_struct, flow); |
1113 | |
|
1114 | 0 | packet->payload = orig_payload; |
1115 | 0 | packet->payload_packet_len = orig_payload_length; |
1116 | |
|
1117 | 0 | return keep_extra_dissection(ndpi_struct, flow); |
1118 | 0 | } |
1119 | | |
1120 | | /* ************************************************************ */ |
1121 | | |
1122 | 0 | static u_int64_t get_stun_lru_key(struct ndpi_flow_struct *flow, u_int8_t rev) { |
1123 | 0 | if(rev) { |
1124 | 0 | if(flow->is_ipv6) |
1125 | 0 | return (ndpi_quick_hash64((const char *)flow->s_address.v6, 16) << 16) | ntohs(flow->s_port); |
1126 | 0 | else |
1127 | 0 | return ((u_int64_t)flow->s_address.v4 << 32) | flow->s_port; |
1128 | 0 | } else { |
1129 | 0 | if(flow->is_ipv6) |
1130 | 0 | return (ndpi_quick_hash64((const char *)flow->c_address.v6, 16) << 16) | ntohs(flow->c_port); |
1131 | 0 | else |
1132 | 0 | return ((u_int64_t)flow->c_address.v4 << 32) | flow->c_port; |
1133 | 0 | } |
1134 | 0 | } |
1135 | | |
1136 | | /* ************************************************************ */ |
1137 | | |
1138 | 0 | static u_int64_t get_stun_lru_key_raw4(u_int32_t ip, u_int16_t port_host_order) { |
1139 | 0 | return ((u_int64_t)ip << 32) | htons(port_host_order); |
1140 | 0 | } |
1141 | | |
1142 | | /* ************************************************************ */ |
1143 | | |
1144 | 0 | static u_int64_t get_stun_lru_key_raw6(u_int8_t *ip, u_int16_t port_host_order) { |
1145 | 0 | return ((u_int64_t)ndpi_quick_hash(ip, 16) << 32) | htons(port_host_order); |
1146 | 0 | } |
1147 | | |
1148 | | /* ************************************************************ */ |
1149 | | |
1150 | | static void ndpi_int_stun_add_connection(struct ndpi_detection_module_struct *ndpi_struct, |
1151 | | struct ndpi_flow_struct *flow, |
1152 | | u_int16_t app_proto, |
1153 | | u_int16_t master_proto, |
1154 | 0 | ndpi_protocol_category_t category) { |
1155 | 0 | ndpi_confidence_t confidence = NDPI_CONFIDENCE_DPI; |
1156 | 0 | u_int16_t new_app_proto; |
1157 | | |
1158 | | /* In monitoring the classification can't change again */ |
1159 | 0 | if(flow->monitoring) |
1160 | 0 | return; |
1161 | | |
1162 | 0 | NDPI_LOG_DBG(ndpi_struct, "Wanting %d/%d\n", master_proto, app_proto); |
1163 | |
|
1164 | 0 | if(app_proto == NDPI_PROTOCOL_UNKNOWN) { |
1165 | | /* https://support.google.com/a/answer/1279090?hl=en */ |
1166 | 0 | if((ntohs(flow->c_port) >= 19302 && ntohs(flow->c_port) <= 19309) || |
1167 | 0 | ntohs(flow->c_port) == 3478 || |
1168 | 0 | (ntohs(flow->s_port) >= 19302 && ntohs(flow->s_port) <= 19309) || |
1169 | 0 | ntohs(flow->s_port) == 3478) { |
1170 | 0 | if(flow->is_ipv6) { |
1171 | 0 | u_int64_t pref1 = ndpi_htonll(0x2001486048640005); /* 2001:4860:4864:5::/64 */ |
1172 | 0 | u_int64_t pref2 = ndpi_htonll(0x2001486048640006); /* 2001:4860:4864:6::/64 */ |
1173 | |
|
1174 | 0 | if(memcmp(flow->c_address.v6, &pref1, sizeof(pref1)) == 0 || |
1175 | 0 | memcmp(flow->c_address.v6, &pref2, sizeof(pref2)) == 0 || |
1176 | 0 | memcmp(flow->s_address.v6, &pref1, sizeof(pref1)) == 0 || |
1177 | 0 | memcmp(flow->s_address.v6, &pref2, sizeof(pref2)) == 0) { |
1178 | 0 | app_proto = NDPI_PROTOCOL_GOOGLE_CALL; |
1179 | 0 | } |
1180 | 0 | } else { |
1181 | 0 | u_int32_t c_address, s_address; |
1182 | |
|
1183 | 0 | c_address = ntohl(flow->c_address.v4); |
1184 | 0 | s_address = ntohl(flow->s_address.v4); |
1185 | 0 | if((c_address & 0xFFFFFF00) == 0x4a7dfa00 || /* 74.125.250.0/24 */ |
1186 | 0 | (c_address & 0xFFFFFF00) == 0x8efa5200 || /* 142.250.82.0/24 */ |
1187 | 0 | (s_address & 0xFFFFFF00) == 0x4a7dfa00 || |
1188 | 0 | (s_address & 0xFFFFFF00) == 0x8efa5200) { |
1189 | 0 | app_proto = NDPI_PROTOCOL_GOOGLE_CALL; |
1190 | 0 | } |
1191 | 0 | } |
1192 | 0 | } |
1193 | 0 | } |
1194 | |
|
1195 | 0 | if(!is_subclassification_real_by_proto(app_proto)) { |
1196 | 0 | new_app_proto = search_into_cache(ndpi_struct, flow); |
1197 | 0 | if(new_app_proto != NDPI_PROTOCOL_UNKNOWN) { |
1198 | 0 | confidence = NDPI_CONFIDENCE_DPI_CACHE; |
1199 | 0 | if(app_proto == NDPI_PROTOCOL_RTP) |
1200 | 0 | master_proto = NDPI_PROTOCOL_SRTP; /* STUN/RTP --> SRTP/APP */ |
1201 | 0 | if(master_proto == NDPI_PROTOCOL_RTP || master_proto == NDPI_PROTOCOL_RTCP) |
1202 | 0 | master_proto = NDPI_PROTOCOL_SRTP; /* RTP|RTCP --> SRTP/APP */ |
1203 | 0 | app_proto = new_app_proto; |
1204 | 0 | } |
1205 | 0 | } |
1206 | | |
1207 | | /* From RTP dissector */ |
1208 | 0 | if(master_proto == NDPI_PROTOCOL_RTP || master_proto == NDPI_PROTOCOL_RTCP) { |
1209 | 0 | if(app_proto == NDPI_PROTOCOL_UNKNOWN) { |
1210 | 0 | app_proto = NDPI_PROTOCOL_RTP; |
1211 | 0 | master_proto = NDPI_PROTOCOL_STUN; /* RTP|RTCP -> STUN/RTP */ |
1212 | 0 | } else { |
1213 | 0 | master_proto = NDPI_PROTOCOL_SRTP; |
1214 | 0 | } |
1215 | 0 | } |
1216 | | |
1217 | | /* Adding only real subclassifications */ |
1218 | 0 | if(is_subclassification_real_by_proto(app_proto)) |
1219 | 0 | add_to_cache(ndpi_struct, flow, app_proto); |
1220 | |
|
1221 | 0 | if(category != NDPI_PROTOCOL_CATEGORY_UNSPECIFIED) |
1222 | 0 | flow->category = category; |
1223 | | |
1224 | 0 | if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN || |
1225 | 0 | app_proto != NDPI_PROTOCOL_UNKNOWN) { |
1226 | 0 | NDPI_LOG_DBG(ndpi_struct, "Setting %d/%d\n", master_proto, app_proto); |
1227 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, app_proto, master_proto, confidence); |
1228 | | |
1229 | | /* In "normal" data-path the generic code in `ndpi_internal_detection_process_packet()` |
1230 | | takes care of setting the category */ |
1231 | 0 | if(flow->extra_packets_func) { |
1232 | 0 | ndpi_master_app_protocol proto; |
1233 | |
|
1234 | 0 | proto.master_protocol = master_proto; |
1235 | 0 | proto.app_protocol = app_proto; |
1236 | 0 | flow->category = get_proto_category(ndpi_struct, proto); |
1237 | 0 | flow->breed = get_proto_breed(ndpi_struct, proto); |
1238 | 0 | } |
1239 | 0 | } |
1240 | | |
1241 | 0 | switch_extra_dissection_to_stun(ndpi_struct, flow, 1); |
1242 | 0 | } |
1243 | | |
1244 | | /* ************************************************************ */ |
1245 | | |
1246 | | void switch_extra_dissection_to_stun(struct ndpi_detection_module_struct *ndpi_struct, |
1247 | | struct ndpi_flow_struct *flow, |
1248 | | int std_callback) |
1249 | 0 | { |
1250 | 0 | if(!flow->extra_packets_func) { |
1251 | 0 | if(keep_extra_dissection(ndpi_struct, flow)) { |
1252 | 0 | NDPI_LOG_DBG(ndpi_struct, "Enabling extra dissection\n"); |
1253 | 0 | flow->max_extra_packets_to_check = ndpi_struct->cfg.stun_max_packets_extra_dissection; |
1254 | 0 | if(std_callback) |
1255 | 0 | flow->extra_packets_func = stun_search_again; |
1256 | 0 | else |
1257 | 0 | flow->extra_packets_func = stun_telegram_search_again; |
1258 | 0 | } |
1259 | 0 | } |
1260 | 0 | } |
1261 | | |
1262 | | /* ************************************************************ */ |
1263 | | |
1264 | | static void ndpi_search_stun(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) |
1265 | 0 | { |
1266 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
1267 | 0 | u_int16_t app_proto; |
1268 | 0 | ndpi_protocol_category_t category; |
1269 | 0 | int rc; |
1270 | |
|
1271 | 0 | NDPI_LOG_DBG(ndpi_struct, "search stun\n"); |
1272 | |
|
1273 | 0 | app_proto = NDPI_PROTOCOL_UNKNOWN; |
1274 | |
|
1275 | 0 | if(packet->iph && |
1276 | 0 | ((packet->iph->daddr == 0xFFFFFFFF /* 255.255.255.255 */) || |
1277 | 0 | ((ntohl(packet->iph->daddr) & 0xF0000000) == 0xE0000000 /* A multicast address */))) { |
1278 | 0 | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
1279 | 0 | return; |
1280 | 0 | } |
1281 | | |
1282 | 0 | rc = is_stun(ndpi_struct, flow, &app_proto, &category); |
1283 | |
|
1284 | 0 | if(rc == 1) { |
1285 | 0 | ndpi_int_stun_add_connection(ndpi_struct, flow, app_proto, |
1286 | 0 | __get_master(flow), category); |
1287 | 0 | return; |
1288 | 0 | } |
1289 | | |
1290 | | /* TODO: can we stop earlier? */ |
1291 | 0 | if(flow->packet_counter > 5) |
1292 | 0 | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
1293 | 0 | } |
1294 | | |
1295 | | /* ************************************************************* */ |
1296 | | |
1297 | | static u_int64_t get_signal_key(struct ndpi_flow_struct *flow) |
1298 | 0 | { |
1299 | 0 | if(flow->is_ipv6) |
1300 | 0 | return ndpi_quick_hash64((const char *)flow->c_address.v6, 16); |
1301 | 0 | else |
1302 | 0 | return flow->c_address.v4; |
1303 | 0 | } |
1304 | | |
1305 | | /* ************************************************************* */ |
1306 | | |
1307 | | int signal_search_into_cache(struct ndpi_detection_module_struct *ndpi_struct, |
1308 | | struct ndpi_flow_struct *flow) |
1309 | 0 | { |
1310 | 0 | u_int64_t key; |
1311 | 0 | u_int16_t dummy; |
1312 | |
|
1313 | 0 | if(ndpi_struct->signal_cache) { |
1314 | 0 | key = get_signal_key(flow); |
1315 | |
|
1316 | 0 | if(ndpi_lru_find_cache(ndpi_struct->signal_cache, key, |
1317 | 0 | &dummy, 0 /* Don't remove it as it can be used for other connections */, |
1318 | 0 | ndpi_get_current_time(flow))) { |
1319 | | #ifdef DEBUG_SIGNAL_LRU |
1320 | | printf("[LRU SIGNAL] Found %lu [%u <-> %u]\n", key, ntohs(flow->c_port), ntohs(flow->s_port)); |
1321 | | #endif |
1322 | 0 | return 1; |
1323 | 0 | } else { |
1324 | | #ifdef DEBUG_SIGNAL_LRU |
1325 | | printf("[LRU SIGNAL] Not found %lu [%u <-> %u]\n", key, ntohs(flow->c_port), ntohs(flow->s_port)); |
1326 | | #endif |
1327 | 0 | } |
1328 | 0 | } |
1329 | | |
1330 | 0 | return 0; |
1331 | 0 | } |
1332 | | |
1333 | | /* ************************************************************* */ |
1334 | | |
1335 | | void signal_add_to_cache(struct ndpi_detection_module_struct *ndpi_struct, |
1336 | | struct ndpi_flow_struct *flow) |
1337 | 0 | { |
1338 | 0 | u_int64_t key; |
1339 | |
|
1340 | 0 | if(ndpi_struct->signal_cache) { |
1341 | 0 | key = get_signal_key(flow); |
1342 | | #ifdef DEBUG_SIGNAL_LRU |
1343 | | printf("[LRU SIGNAL] ADDING %lu [%u <-> %u]\n", key, ntohs(flow->c_port), ntohs(flow->s_port)); |
1344 | | #endif |
1345 | 0 | ndpi_lru_add_to_cache(ndpi_struct->signal_cache, key, 1 /* dummy */, |
1346 | 0 | ndpi_get_current_time(flow)); |
1347 | 0 | } |
1348 | 0 | } |
1349 | | |
1350 | | /* ************************************************************ */ |
1351 | | |
1352 | 1 | void init_stun_dissector(struct ndpi_detection_module_struct *ndpi_struct) { |
1353 | 1 | register_dissector("STUN", ndpi_struct, |
1354 | 1 | ndpi_search_stun, |
1355 | 1 | NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION, |
1356 | 1 | 1, NDPI_PROTOCOL_STUN); |
1357 | 1 | } |