/src/ndpi/src/lib/protocols/stun.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * stun.c |
3 | | * |
4 | | * Copyright (C) 2011-22 - 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 | 137 | #define NDPI_CURRENT_PROTO NDPI_PROTOCOL_STUN |
27 | | |
28 | | #include "ndpi_api.h" |
29 | | |
30 | 6.58k | #define MAX_NUM_STUN_PKTS 3 |
31 | | |
32 | | // #define DEBUG_STUN 1 |
33 | | // #define DEBUG_LRU 1 |
34 | | // #define DEBUG_ZOOM_LRU 1 |
35 | | // #define DEBUG_MONITORING 1 |
36 | | |
37 | 3.16k | #define STUN_HDR_LEN 20 /* STUN message header length, Classic-STUN (RFC 3489) and STUN (RFC 8489) both */ |
38 | | |
39 | | extern void switch_to_tls(struct ndpi_detection_module_struct *ndpi_struct, |
40 | | struct ndpi_flow_struct *flow); |
41 | | extern int is_rtp_or_rtcp(struct ndpi_detection_module_struct *ndpi_struct, |
42 | | struct ndpi_flow_struct *flow); |
43 | | extern u_int8_t rtp_get_stream_type(u_int8_t payloadType, ndpi_multimedia_flow_type *s_type); |
44 | | extern int is_dtls(const u_int8_t *buf, u_int32_t buf_len, u_int32_t *block_len); |
45 | | |
46 | | static int stun_monitoring(struct ndpi_detection_module_struct *ndpi_struct, |
47 | | struct ndpi_flow_struct *flow) |
48 | 0 | { |
49 | 0 | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
50 | 0 | int rtp_rtcp; |
51 | 0 | u_int8_t first_byte; |
52 | |
|
53 | | #ifdef DEBUG_MONITORING |
54 | | printf("[STUN-MON] Packet counter %d\n", flow->packet_counter); |
55 | | #endif |
56 | |
|
57 | 0 | if(packet->payload_packet_len == 0) |
58 | 0 | return 1; |
59 | | |
60 | 0 | first_byte = packet->payload[0]; |
61 | | |
62 | | /* draft-ietf-avtcore-rfc7983bis */ |
63 | 0 | if(first_byte <= 3) { |
64 | | #ifdef DEBUG_MONITORING |
65 | | printf("[STUN-MON] Still STUN\n"); |
66 | | #endif |
67 | 0 | return 1; |
68 | 0 | } else if(first_byte <= 19) { |
69 | | #ifdef DEBUG_MONITORING |
70 | | printf("[STUN-MON] DROP or ZRTP range. Unexpected but keep looking\n"); |
71 | | #endif |
72 | 0 | return 1; |
73 | 0 | } else if(first_byte <= 63) { |
74 | | #ifdef DEBUG_MONITORING |
75 | | printf("[STUN-MON] DTLS\n"); |
76 | | #endif |
77 | | /* TODO */ |
78 | 0 | return 1; |
79 | 0 | } else if(first_byte <= 127) { |
80 | | #ifdef DEBUG_MONITORING |
81 | | printf("[STUN-MON] QUIC or TURN range. Unexpected but keep looking\n"); |
82 | | #endif |
83 | 0 | return 1; |
84 | 0 | } else if(first_byte <= 191) { |
85 | |
|
86 | 0 | rtp_rtcp = is_rtp_or_rtcp(ndpi_struct, flow); |
87 | 0 | if(rtp_rtcp == IS_RTP) { |
88 | | #ifdef DEBUG_MONITORING |
89 | | printf("[STUN-MON] RTP (dir %d)\n", packet->packet_direction); |
90 | | #endif |
91 | 0 | NDPI_LOG_INFO(ndpi_struct, "Found RTP over STUN\n"); |
92 | |
|
93 | 0 | rtp_get_stream_type(packet->payload[1] & 0x7F, &flow->flow_multimedia_type); |
94 | |
|
95 | 0 | if(flow->detected_protocol_stack[1] != NDPI_PROTOCOL_UNKNOWN) { |
96 | | /* STUN/SUBPROTO -> SUBPROTO/RTP */ |
97 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, |
98 | 0 | NDPI_PROTOCOL_RTP, flow->detected_protocol_stack[0], |
99 | 0 | NDPI_CONFIDENCE_DPI); |
100 | 0 | } else { |
101 | | /* STUN -> STUN/RTP */ |
102 | 0 | ndpi_set_detected_protocol(ndpi_struct, flow, |
103 | 0 | NDPI_PROTOCOL_RTP, NDPI_PROTOCOL_STUN, |
104 | 0 | NDPI_CONFIDENCE_DPI); |
105 | 0 | } |
106 | 0 | return 0; /* Stop */ |
107 | 0 | } else if(rtp_rtcp == IS_RTCP) { |
108 | | #ifdef DEBUG_MONITORING |
109 | | printf("[STUN-MON] RTCP\n"); |
110 | | #endif |
111 | 0 | return 1; |
112 | 0 | } else { |
113 | | #ifdef DEBUG_MONITORING |
114 | | printf("[STUN-MON] Unexpected\n"); |
115 | | #endif |
116 | 0 | return 1; |
117 | 0 | } |
118 | 0 | } else { |
119 | | #ifdef DEBUG_MONITORING |
120 | | printf("[STUN-MON] QUIC range. Unexpected but keep looking\n"); |
121 | | #endif |
122 | 0 | return 1; |
123 | 0 | } |
124 | 0 | } |
125 | | |
126 | | /* ************************************************************ */ |
127 | | |
128 | 907 | u_int32_t get_stun_lru_key(struct ndpi_flow_struct *flow, u_int8_t rev) { |
129 | 907 | if(rev) { |
130 | 11 | if(flow->is_ipv6) |
131 | 11 | return ndpi_quick_hash(flow->s_address.v6, 16) + ntohs(flow->s_port); |
132 | 0 | else |
133 | 0 | return ntohl(flow->s_address.v4) + ntohs(flow->s_port); |
134 | 896 | } else { |
135 | 896 | if(flow->is_ipv6) |
136 | 454 | return ndpi_quick_hash(flow->c_address.v6, 16) + ntohs(flow->c_port); |
137 | 442 | else |
138 | 442 | return ntohl(flow->c_address.v4) + ntohs(flow->c_port); |
139 | 896 | } |
140 | 907 | } |
141 | | |
142 | | /* ************************************************************ */ |
143 | | |
144 | | int stun_search_into_zoom_cache(struct ndpi_detection_module_struct *ndpi_struct, |
145 | | struct ndpi_flow_struct *flow) |
146 | 5.40k | { |
147 | 5.40k | u_int16_t dummy; |
148 | 5.40k | u_int32_t key; |
149 | | |
150 | 5.40k | if(ndpi_struct->stun_zoom_cache && |
151 | 5.40k | flow->l4_proto == IPPROTO_UDP) { |
152 | 880 | key = get_stun_lru_key(flow, 0); /* Src */ |
153 | | #ifdef DEBUG_ZOOM_LRU |
154 | | printf("[LRU ZOOM] Search %u [src_port %u]\n", key, ntohs(flow->c_port)); |
155 | | #endif |
156 | | |
157 | 880 | if(ndpi_lru_find_cache(ndpi_struct->stun_zoom_cache, key, |
158 | 880 | &dummy, 0 /* Don't remove it as it can be used for other connections */, |
159 | 880 | ndpi_get_current_time(flow))) { |
160 | | #ifdef DEBUG_ZOOM_LRU |
161 | | printf("[LRU ZOOM] Found"); |
162 | | #endif |
163 | 0 | return 1; |
164 | 0 | } |
165 | 880 | } |
166 | 5.40k | return 0; |
167 | 5.40k | } |
168 | | |
169 | | /* ************************************************************ */ |
170 | | |
171 | | static void ndpi_int_stun_add_connection(struct ndpi_detection_module_struct *ndpi_struct, |
172 | | struct ndpi_flow_struct *flow, |
173 | 7 | u_int app_proto) { |
174 | 7 | ndpi_confidence_t confidence = NDPI_CONFIDENCE_DPI; |
175 | | |
176 | 7 | if(app_proto == NDPI_PROTOCOL_UNKNOWN) { |
177 | | /* https://support.google.com/a/answer/1279090?hl=en */ |
178 | 0 | if((ntohs(flow->c_port) >= 19302 && ntohs(flow->c_port) <= 19309) || |
179 | 0 | ntohs(flow->c_port) == 3478 || |
180 | 0 | (ntohs(flow->s_port) >= 19302 && ntohs(flow->s_port) <= 19309) || |
181 | 0 | ntohs(flow->s_port) == 3478) { |
182 | 0 | if(flow->is_ipv6) { |
183 | 0 | u_int64_t pref1 = 0x2001486048640005; /* 2001:4860:4864:5::/64 */ |
184 | 0 | u_int64_t pref2 = 0x2001486048640006; /* 2001:4860:4864:6::/64 */ |
185 | |
|
186 | 0 | if(memcmp(&flow->c_address.v6, &pref1, sizeof(pref1)) == 0 || |
187 | 0 | memcmp(&flow->c_address.v6, &pref2, sizeof(pref2)) == 0 || |
188 | 0 | memcmp(&flow->s_address.v6, &pref1, sizeof(pref1)) == 0 || |
189 | 0 | memcmp(&flow->s_address.v6, &pref2, sizeof(pref2)) == 0) { |
190 | 0 | app_proto = NDPI_PROTOCOL_HANGOUT_DUO; |
191 | 0 | } |
192 | 0 | } else { |
193 | 0 | u_int32_t c_address, s_address; |
194 | |
|
195 | 0 | c_address = ntohl(flow->c_address.v4); |
196 | 0 | s_address = ntohl(flow->s_address.v4); |
197 | 0 | if((c_address & 0xFFFFFFF0) == 0x4a7dfa00 || /* 74.125.250.0/24 */ |
198 | 0 | (c_address & 0xFFFFFFF0) == 0x8efa5200 || /* 142.250.82.0/24 */ |
199 | 0 | (s_address & 0xFFFFFFF0) == 0x4a7dfa00 || |
200 | 0 | (s_address & 0xFFFFFFF0) == 0x8efa5200) { |
201 | 0 | app_proto = NDPI_PROTOCOL_HANGOUT_DUO; |
202 | 0 | } |
203 | 0 | } |
204 | 0 | } |
205 | 0 | } |
206 | | |
207 | 7 | if(ndpi_struct->stun_cache |
208 | 7 | && (app_proto != NDPI_PROTOCOL_UNKNOWN) |
209 | 7 | ) /* Cache flow sender info */ { |
210 | 5 | u_int32_t key = get_stun_lru_key(flow, 0); |
211 | 5 | u_int16_t cached_proto; |
212 | | |
213 | 5 | if(ndpi_lru_find_cache(ndpi_struct->stun_cache, key, |
214 | 5 | &cached_proto, 0 /* Don't remove it as it can be used for other connections */, |
215 | 5 | ndpi_get_current_time(flow))) { |
216 | | #ifdef DEBUG_LRU |
217 | | printf("[LRU] FOUND %u / %u: no need to cache %u.%u\n", key, cached_proto, proto, app_proto); |
218 | | #endif |
219 | 0 | if(app_proto != cached_proto) { |
220 | 0 | app_proto = cached_proto; |
221 | 0 | confidence = NDPI_CONFIDENCE_DPI_CACHE; |
222 | 0 | } |
223 | 5 | } else { |
224 | 5 | u_int32_t key_rev = get_stun_lru_key(flow, 1); |
225 | | |
226 | 5 | if(ndpi_lru_find_cache(ndpi_struct->stun_cache, key_rev, |
227 | 5 | &cached_proto, 0 /* Don't remove it as it can be used for other connections */, |
228 | 5 | ndpi_get_current_time(flow))) { |
229 | | #ifdef DEBUG_LRU |
230 | | printf("[LRU] FOUND %u / %u: no need to cache %u.%u\n", key_rev, cached_proto, proto, app_proto); |
231 | | #endif |
232 | 0 | if(app_proto != cached_proto) { |
233 | 0 | app_proto = cached_proto; |
234 | 0 | confidence = NDPI_CONFIDENCE_DPI_CACHE; |
235 | 0 | } |
236 | 5 | } else { |
237 | 5 | if(app_proto != NDPI_PROTOCOL_STUN) { |
238 | | /* No sense to add STUN, but only subprotocols */ |
239 | | |
240 | | #ifdef DEBUG_LRU |
241 | | printf("[LRU] ADDING %u / %u.%u [%u -> %u]\n", key, proto, app_proto, |
242 | | ntohs(packet->udp->source), ntohs(packet->udp->dest)); |
243 | | #endif |
244 | | |
245 | 5 | ndpi_lru_add_to_cache(ndpi_struct->stun_cache, key, app_proto, ndpi_get_current_time(flow)); |
246 | 5 | ndpi_lru_add_to_cache(ndpi_struct->stun_cache, key_rev, app_proto, ndpi_get_current_time(flow)); |
247 | 5 | } |
248 | 5 | } |
249 | 5 | } |
250 | 5 | } |
251 | | |
252 | | /* TODO: extend to other protocols? */ |
253 | 7 | if(ndpi_struct->stun_zoom_cache && |
254 | 7 | app_proto == NDPI_PROTOCOL_ZOOM && |
255 | 7 | flow->l4_proto == IPPROTO_UDP) { |
256 | 5 | u_int32_t key = get_stun_lru_key(flow, 0); /* Src */ |
257 | | #ifdef DEBUG_ZOOM_LRU |
258 | | printf("[LRU ZOOM] ADDING %u [src_port %u]\n", key, ntohs(flow->c_port)); |
259 | | #endif |
260 | 5 | ndpi_lru_add_to_cache(ndpi_struct->stun_zoom_cache, key, |
261 | 5 | 0 /* dummy */, ndpi_get_current_time(flow)); |
262 | 5 | } |
263 | | |
264 | 7 | ndpi_set_detected_protocol(ndpi_struct, flow, app_proto, NDPI_PROTOCOL_STUN, confidence); |
265 | | |
266 | 7 | if(ndpi_struct->monitoring_stun_pkts_to_process > 0 && |
267 | | flow->l4_proto == IPPROTO_UDP /* TODO: support TCP. We need to pay some attention because: |
268 | | * multiple msg in the same TCP segment |
269 | 7 | * same msg split across multiple segments */) { |
270 | 6 | if((ndpi_struct->monitoring_stun_flags & NDPI_MONITORING_STUN_SUBCLASSIFIED) || |
271 | 6 | app_proto == NDPI_PROTOCOL_UNKNOWN /* No-subclassification */) { |
272 | 3 | flow->max_extra_packets_to_check = ndpi_struct->monitoring_stun_pkts_to_process; |
273 | 3 | flow->extra_packets_func = stun_monitoring; |
274 | 3 | } |
275 | 6 | } |
276 | 7 | } |
277 | | |
278 | | typedef enum { |
279 | | NDPI_IS_STUN, |
280 | | NDPI_IS_NOT_STUN |
281 | | } ndpi_int_stun_t; |
282 | | |
283 | | /* ************************************************************ */ |
284 | | |
285 | | static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct *ndpi_struct, |
286 | | struct ndpi_flow_struct *flow, |
287 | | const u_int8_t * payload, |
288 | | u_int16_t payload_length, |
289 | 3.29k | u_int16_t *app_proto) { |
290 | 3.29k | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
291 | 3.29k | u_int16_t msg_type, msg_len; |
292 | 3.29k | u_int32_t unused; |
293 | 3.29k | int rc; |
294 | | |
295 | 3.29k | if(packet->iph && |
296 | 3.29k | ((packet->iph->daddr == 0xFFFFFFFF /* 255.255.255.255 */) || |
297 | 2.70k | ((ntohl(packet->iph->daddr) & 0xF0000000) == 0xE0000000 /* A multicast address */))) { |
298 | 137 | NDPI_EXCLUDE_PROTO(ndpi_struct, flow); |
299 | 137 | return(NDPI_IS_NOT_STUN); |
300 | 137 | } |
301 | | |
302 | | /* If we're here it's because this does not look like STUN anymore |
303 | | as this was a flow that started as STUN and turned into something |
304 | | else. Let's investigate what is that about */ |
305 | 3.16k | if(flow->stun.num_pkts > 0 && is_dtls(payload, payload_length, &unused)) { |
306 | | #ifdef DEBUG_STUN |
307 | | printf("[STUN] DTLS?\n"); |
308 | | #endif |
309 | | /* Switching to TLS dissector is tricky, because we are calling one dissector |
310 | | from another one, and that is not a common operation... |
311 | | Additionally: |
312 | | * at that point protocol stack is still empty |
313 | | * we have room for only two protocols in flow->detected_protocol_stack[] so |
314 | | we can't have something like STUN/DTLS/SNAPCHAT_CALL |
315 | | * the easiest solution is skipping STUN, and let TLS dissector to set both |
316 | | master (i.e. DTLS) and subprotocol (if any) */ |
317 | 0 | if(ndpi_struct->opportunistic_tls_stun_enabled) { |
318 | 0 | flow->stun.maybe_dtls = 1; |
319 | 0 | switch_to_tls(ndpi_struct, flow); |
320 | 0 | } |
321 | | /* We don't want to mess up with TLS classification/results but we don't want to |
322 | | exclude STUN right away to keep trying it in the case that this packet is |
323 | | not a real DTLS one */ |
324 | 0 | return(NDPI_IS_NOT_STUN); |
325 | 0 | } |
326 | | |
327 | 3.16k | if(payload_length < STUN_HDR_LEN) { |
328 | | /* This looks like an invalid packet */ |
329 | | |
330 | 438 | if(flow->stun.num_pkts > 0) { |
331 | 0 | return(NDPI_IS_STUN); |
332 | 0 | } else |
333 | 438 | return(NDPI_IS_NOT_STUN); |
334 | 438 | } |
335 | | |
336 | 2.72k | if((strncmp((const char*)payload, (const char*)"RSP/", 4) == 0) |
337 | 2.72k | && (strncmp((const char*)&payload[7], (const char*)" STUN_", 6) == 0)) { |
338 | 1 | NDPI_LOG_INFO(ndpi_struct, "found stun\n"); |
339 | 1 | goto stun_found; |
340 | 1 | } |
341 | | |
342 | 2.72k | msg_type = ntohs(*((u_int16_t*)payload)); |
343 | 2.72k | msg_len = ntohs(*((u_int16_t*)&payload[2])); |
344 | | |
345 | | /* With tcp, we might have multiple msg in the same TCP pkt. |
346 | | Parse only the first one. TODO */ |
347 | 2.72k | if(packet->tcp) { |
348 | 1.99k | if(msg_len + 20 > payload_length) |
349 | 1.55k | return(NDPI_IS_NOT_STUN); |
350 | | /* Let's hope that classic-stun is no more used over TCP */ |
351 | 444 | if(ntohl(*((u_int32_t *)&payload[4])) != 0x2112A442) |
352 | 444 | return(NDPI_IS_NOT_STUN); |
353 | | |
354 | 0 | payload_length = msg_len + 20; |
355 | 0 | } |
356 | | |
357 | 725 | if((msg_type == 0) || ((msg_len+20) != payload_length)) |
358 | 712 | return(NDPI_IS_NOT_STUN); |
359 | | |
360 | | /* https://www.iana.org/assignments/stun-parameters/stun-parameters.xhtml */ |
361 | 13 | if(((msg_type & 0x3EEF) > 0x000B) && |
362 | 13 | (msg_type != 0x0800 && msg_type != 0x0801 && msg_type != 0x0802)) { |
363 | | #ifdef DEBUG_STUN |
364 | | printf("[STUN] msg_type = %04X\n", msg_type); |
365 | | #endif |
366 | 1 | return(NDPI_IS_NOT_STUN); |
367 | 1 | } |
368 | | |
369 | 12 | if(ndpi_struct->stun_cache) { |
370 | 6 | u_int16_t proto; |
371 | 6 | u_int32_t key = get_stun_lru_key(flow, 0); |
372 | 6 | int rc = ndpi_lru_find_cache(ndpi_struct->stun_cache, key, &proto, |
373 | 6 | 0 /* Don't remove it as it can be used for other connections */, |
374 | 6 | ndpi_get_current_time(flow)); |
375 | | |
376 | | #ifdef DEBUG_LRU |
377 | | printf("[LRU] Searching %u\n", key); |
378 | | #endif |
379 | | |
380 | 6 | if(!rc) { |
381 | 6 | key = get_stun_lru_key(flow, 1); |
382 | 6 | rc = ndpi_lru_find_cache(ndpi_struct->stun_cache, key, &proto, |
383 | 6 | 0 /* Don't remove it as it can be used for other connections */, |
384 | 6 | ndpi_get_current_time(flow)); |
385 | | |
386 | | #ifdef DEBUG_LRU |
387 | | printf("[LRU] Searching %u\n", key); |
388 | | #endif |
389 | 6 | } |
390 | | |
391 | 6 | if(rc) { |
392 | | #ifdef DEBUG_LRU |
393 | | printf("[LRU] Cache FOUND %u / %u\n", key, proto); |
394 | | #endif |
395 | |
|
396 | 0 | *app_proto = proto; |
397 | 0 | return(NDPI_IS_STUN); |
398 | 6 | } else { |
399 | | #ifdef DEBUG_LRU |
400 | | printf("[LRU] NOT FOUND %u\n", key); |
401 | | #endif |
402 | 6 | } |
403 | 6 | } else { |
404 | | #ifdef DEBUG_LRU |
405 | | printf("[LRU] NO/EMPTY CACHE\n"); |
406 | | #endif |
407 | 6 | } |
408 | | |
409 | 12 | if(msg_type == 0x01 /* Binding Request */) { |
410 | 10 | flow->stun.num_binding_requests++; |
411 | | |
412 | 10 | flow->guessed_protocol_id = NDPI_PROTOCOL_STUN; |
413 | | |
414 | 10 | if(!msg_len) { |
415 | | /* flow->stun.num_pkts++; */ |
416 | 0 | return(NDPI_IS_NOT_STUN); /* This to keep analyzing STUN instead of giving up */ |
417 | 0 | } |
418 | 10 | } |
419 | | |
420 | 12 | flow->stun.num_pkts++; |
421 | | |
422 | 12 | flow->guessed_protocol_id = NDPI_PROTOCOL_STUN; |
423 | | |
424 | 12 | if(payload_length == (msg_len+20)) { |
425 | 12 | if((msg_type & 0x3EEF) <= 0x000B) /* http://www.3cx.com/blog/voip-howto/stun-details/ */ { |
426 | 12 | u_int offset = 20; |
427 | | |
428 | | /* |
429 | | This can either be the standard RTCP or Ms Lync RTCP that |
430 | | later will become Ms Lync RTP. In this case we need to |
431 | | be careful before deciding about the protocol before dissecting the packet |
432 | | |
433 | | MS Lync = Skype |
434 | | https://en.wikipedia.org/wiki/Skype_for_Business |
435 | | */ |
436 | | |
437 | 20 | while((offset+4) < payload_length) { |
438 | 15 | u_int16_t attribute = ntohs(*((u_int16_t*)&payload[offset])); |
439 | 15 | u_int16_t len = ntohs(*((u_int16_t*)&payload[offset+2])); |
440 | 15 | u_int16_t x = (len + 4) % 4; |
441 | | |
442 | 15 | if(x) |
443 | 12 | len += 4-x; |
444 | | |
445 | | #ifdef DEBUG_STUN |
446 | | printf("==> Attribute: %04X\n", attribute); |
447 | | #endif |
448 | | |
449 | 15 | switch(attribute) { |
450 | 7 | case 0x0101: |
451 | 7 | case 0x0103: |
452 | 7 | *app_proto = NDPI_PROTOCOL_ZOOM; |
453 | 7 | return(NDPI_IS_STUN); |
454 | | |
455 | 0 | case 0x4000: |
456 | 0 | case 0x4001: |
457 | 0 | case 0x4002: |
458 | 0 | case 0x4003: |
459 | 0 | case 0x4004: |
460 | 0 | case 0x4007: |
461 | | /* These are the only messages apparently whatsapp voice can use */ |
462 | 0 | *app_proto = NDPI_PROTOCOL_WHATSAPP_CALL; |
463 | 0 | return(NDPI_IS_STUN); |
464 | | |
465 | 0 | case 0x0014: /* Realm */ |
466 | 0 | { |
467 | 0 | u_int16_t realm_len = ntohs(*((u_int16_t*)&payload[offset+2])); |
468 | |
|
469 | 0 | if(flow->host_server_name[0] == '\0') { |
470 | 0 | u_int k = offset+4; |
471 | |
|
472 | 0 | ndpi_hostname_sni_set(flow, payload + k, ndpi_min(realm_len, payload_length - k)); |
473 | |
|
474 | | #ifdef DEBUG_STUN |
475 | | printf("==> [%s]\n", flow->host_server_name); |
476 | | #endif |
477 | |
|
478 | 0 | if(strstr(flow->host_server_name, "google.com") != NULL) { |
479 | 0 | *app_proto = NDPI_PROTOCOL_HANGOUT_DUO; |
480 | 0 | return(NDPI_IS_STUN); |
481 | 0 | } else if(strstr(flow->host_server_name, "whispersystems.org") != NULL || |
482 | 0 | (strstr(flow->host_server_name, "signal.org") != NULL)) { |
483 | 0 | *app_proto = NDPI_PROTOCOL_SIGNAL_VOIP; |
484 | 0 | return(NDPI_IS_STUN); |
485 | 0 | } else if(strstr(flow->host_server_name, "facebook") != NULL) { |
486 | 0 | *app_proto = NDPI_PROTOCOL_FACEBOOK_VOIP; |
487 | 0 | return(NDPI_IS_STUN); |
488 | 0 | } else if(strstr(flow->host_server_name, "stripcdn.com") != NULL) { |
489 | 0 | *app_proto = NDPI_PROTOCOL_ADULT_CONTENT; |
490 | 0 | return(NDPI_IS_STUN); |
491 | 0 | } |
492 | 0 | } |
493 | 0 | } |
494 | 0 | break; |
495 | | |
496 | 0 | case 0xC057: /* Messeger */ |
497 | 0 | if(msg_type == 0x0001) { |
498 | 0 | if((msg_len == 100) || (msg_len == 104)) { |
499 | 0 | *app_proto = NDPI_PROTOCOL_FACEBOOK_VOIP; |
500 | 0 | return(NDPI_IS_STUN); |
501 | 0 | } |
502 | 0 | } |
503 | 0 | break; |
504 | | |
505 | 0 | case 0x8054: /* Candidate Identifier */ |
506 | 0 | if((len == 4) |
507 | 0 | && ((offset+7) < payload_length) |
508 | 0 | && (payload[offset+5] == 0x00) |
509 | 0 | && (payload[offset+6] == 0x00) |
510 | 0 | && (payload[offset+7] == 0x00)) { |
511 | | /* Either skype for business or "normal" skype with multiparty call */ |
512 | | #ifdef DEBUG_STUN |
513 | | printf("==> Skype found\n"); |
514 | | #endif |
515 | 0 | *app_proto = NDPI_PROTOCOL_SKYPE_TEAMS_CALL; |
516 | 0 | return(NDPI_IS_STUN); |
517 | 0 | } |
518 | | |
519 | 0 | break; |
520 | | |
521 | 0 | case 0x8055: /* MS Service Quality (skype?) */ |
522 | 0 | break; |
523 | | |
524 | | /* Proprietary fields found on skype calls */ |
525 | 0 | case 0x24DF: |
526 | 0 | case 0x3802: |
527 | 0 | case 0x8036: |
528 | 0 | case 0x8095: |
529 | 0 | case 0x0800: |
530 | 0 | case 0x8006: /* This is found on skype calls) */ |
531 | | /* printf("====>>>> %04X\n", attribute); */ |
532 | | #ifdef DEBUG_STUN |
533 | | printf("==> Skype (2) found\n"); |
534 | | #endif |
535 | |
|
536 | 0 | *app_proto = NDPI_PROTOCOL_SKYPE_TEAMS_CALL; |
537 | 0 | return(NDPI_IS_STUN); |
538 | | |
539 | 0 | case 0x8070: /* Implementation Version */ |
540 | 0 | if(len == 4 && ((offset+7) < payload_length) |
541 | 0 | && (payload[offset+4] == 0x00) && (payload[offset+5] == 0x00) && (payload[offset+6] == 0x00) && |
542 | 0 | ((payload[offset+7] == 0x02) || (payload[offset+7] == 0x03))) { |
543 | | #ifdef DEBUG_STUN |
544 | | printf("==> Skype (3) found\n"); |
545 | | #endif |
546 | |
|
547 | 0 | *app_proto = NDPI_PROTOCOL_SKYPE_TEAMS_CALL; |
548 | 0 | return(NDPI_IS_STUN); |
549 | 0 | } |
550 | 0 | break; |
551 | | |
552 | 0 | case 0xFF03: |
553 | 0 | *app_proto = NDPI_PROTOCOL_HANGOUT_DUO; |
554 | 0 | return(NDPI_IS_STUN); |
555 | | |
556 | 8 | default: |
557 | | #ifdef DEBUG_STUN |
558 | | printf("==> %04X\n", attribute); |
559 | | #endif |
560 | 8 | break; |
561 | 15 | } |
562 | | |
563 | 8 | offset += len + 4; |
564 | 8 | } |
565 | | |
566 | 5 | goto stun_found; |
567 | 12 | } else if(msg_type == 0x0800 || |
568 | 0 | msg_type == 0x0801 || |
569 | 0 | msg_type == 0x0802) { |
570 | 0 | *app_proto = NDPI_PROTOCOL_WHATSAPP_CALL; |
571 | 0 | return(NDPI_IS_STUN); |
572 | 0 | } |
573 | 12 | } |
574 | | |
575 | 0 | if((flow->stun.num_pkts > 0) && (msg_type <= 0x00FF)) { |
576 | 0 | *app_proto = NDPI_PROTOCOL_WHATSAPP_CALL; |
577 | 0 | return(NDPI_IS_STUN); |
578 | 0 | } else |
579 | 0 | return(NDPI_IS_NOT_STUN); |
580 | | |
581 | 6 | stun_found: |
582 | 6 | flow->stun.num_processed_pkts++; |
583 | | |
584 | 6 | rc = (flow->stun.num_pkts < MAX_NUM_STUN_PKTS) ? NDPI_IS_NOT_STUN : NDPI_IS_STUN; |
585 | | |
586 | | #ifdef DEBUG_STUN |
587 | | printf("stun.num_pkts %d, stun.num_processed_pkts %d, rc: %d\n", |
588 | | flow->stun.num_pkts, flow->stun.num_processed_pkts, rc); |
589 | | #endif |
590 | | |
591 | 6 | return rc; |
592 | 0 | } |
593 | | |
594 | | static void ndpi_search_stun(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) |
595 | 3.29k | { |
596 | 3.29k | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
597 | 3.29k | u_int16_t app_proto; |
598 | | |
599 | 3.29k | NDPI_LOG_DBG(ndpi_struct, "search stun\n"); |
600 | | |
601 | 3.29k | app_proto = NDPI_PROTOCOL_UNKNOWN; |
602 | | |
603 | | /* STUN may be encapsulated in TCP packets with a special TCP framing described in RFC 4571 */ |
604 | 3.29k | if(packet->tcp && |
605 | 3.29k | packet->payload_packet_len >= 22 && |
606 | 3.29k | ((ntohs(get_u_int16_t(packet->payload, 0)) + 2) == packet->payload_packet_len)) { |
607 | | /* TODO there could be several STUN packets in a single TCP packet so maybe the detection could be |
608 | | * improved by checking only the STUN packet of given length */ |
609 | | |
610 | 1 | if(ndpi_int_check_stun(ndpi_struct, flow, packet->payload + 2, |
611 | 1 | packet->payload_packet_len - 2, &app_proto) == NDPI_IS_STUN) { |
612 | 0 | ndpi_int_stun_add_connection(ndpi_struct, flow, app_proto); |
613 | 0 | return; |
614 | 0 | } |
615 | 3.29k | } else { /* UDP or TCP without framing */ |
616 | 3.29k | if(ndpi_int_check_stun(ndpi_struct, flow, packet->payload, |
617 | 3.29k | packet->payload_packet_len, &app_proto) == NDPI_IS_STUN) { |
618 | 7 | ndpi_int_stun_add_connection(ndpi_struct, flow, app_proto); |
619 | 7 | return; |
620 | 7 | } |
621 | 3.29k | } |
622 | | |
623 | 3.29k | if(flow->stun.num_pkts >= MAX_NUM_STUN_PKTS || |
624 | 3.29k | flow->packet_counter > 10) |
625 | 0 | NDPI_EXCLUDE_PROTO(ndpi_struct, flow); |
626 | | |
627 | 3.29k | if(flow->packet_counter > 0) { |
628 | | /* This might be a RTP stream: let's make sure we check it */ |
629 | | /* At this point the flow has not been fully classified as STUN yet */ |
630 | 3.29k | NDPI_LOG_DBG(ndpi_struct, "re-enable RTP\n"); |
631 | 3.29k | NDPI_CLR(&flow->excluded_protocol_bitmask, NDPI_PROTOCOL_RTP); |
632 | 3.29k | } |
633 | 3.29k | } |
634 | | |
635 | | |
636 | 8.54k | void init_stun_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id) { |
637 | 8.54k | ndpi_set_bitmask_protocol_detection("STUN", ndpi_struct, *id, |
638 | 8.54k | NDPI_PROTOCOL_STUN, |
639 | 8.54k | ndpi_search_stun, |
640 | 8.54k | NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION, |
641 | 8.54k | SAVE_DETECTION_BITMASK_AS_UNKNOWN, |
642 | 8.54k | ADD_TO_DETECTION_BITMASK); |
643 | | |
644 | 8.54k | *id += 1; |
645 | 8.54k | } |