From d3348ff7307781b37a2dd6f8f2e6f1317f56bfb6 Mon Sep 17 00:00:00 2001 From: marco-testa <55734062+marco-testa@users.noreply.github.com> Date: Tue, 24 Sep 2019 09:41:58 +0200 Subject: Changed the packets handling with STUN msg_type > 0x000C and other fixes. Eliminated double call to the ndpi_int_stun_add_connection function. --- stun.c | 539 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 539 insertions(+) create mode 100644 stun.c diff --git a/stun.c b/stun.c new file mode 100644 index 000000000..9475192da --- /dev/null +++ b/stun.c @@ -0,0 +1,539 @@ +/* + * stun.c + * + * Copyright (C) 2011-19 - ntop.org + * + * This file is part of nDPI, an open source deep packet inspection + * library based on the OpenDPI and PACE technology by ipoque GmbH + * + * nDPI is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * nDPI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with nDPI. If not, see . + * + */ + +#include "ndpi_protocol_ids.h" + +#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_STUN + +#include "ndpi_api.h" + +#define MAX_NUM_STUN_PKTS 8 + +// #define DEBUG_STUN 1 +// #define DEBUG_LRU 1 + +struct stun_packet_header { + u_int16_t msg_type, msg_len; + u_int32_t cookie; + u_int8_t transaction_id[8]; +}; + + +/* ************************************************************ */ + +u_int32_t get_stun_lru_key(struct ndpi_flow_struct *flow, u_int8_t rev) { + if(rev) + return(flow->packet.iph->daddr + flow->packet.udp->dest); + else + return(flow->packet.iph->saddr + flow->packet.udp->source); +} + +/* ************************************************************ */ + +void ndpi_int_stun_add_connection(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow, + u_int proto, u_int app_proto) { + if(ndpi_struct->stun_cache == NULL) + ndpi_struct->stun_cache = ndpi_lru_cache_init(1024); + + if(ndpi_struct->stun_cache + && flow->packet.iph + && flow->packet.udp + && (app_proto != NDPI_PROTOCOL_UNKNOWN) + ) /* Cache flow sender info */ { + u_int32_t key = get_stun_lru_key(flow, 0); + u_int16_t cached_proto; + + if(ndpi_lru_find_cache(ndpi_struct->stun_cache, key, + &cached_proto, 0 /* Don't remove it as it can be used for other connections */)) { +#ifdef DEBUG_LRU + printf("[LRU] FOUND %u / %u: no need to cache %u.%u\n", key, cached_proto, proto, app_proto); +#endif + app_proto = cached_proto, proto = NDPI_PROTOCOL_STUN; + } else { + u_int32_t key_rev = get_stun_lru_key(flow, 1); + + if(ndpi_lru_find_cache(ndpi_struct->stun_cache, key_rev, + &cached_proto, 0 /* Don't remove it as it can be used for other connections */)) { +#ifdef DEBUG_LRU + printf("[LRU] FOUND %u / %u: no need to cache %u.%u\n", key_rev, cached_proto, proto, app_proto); +#endif + app_proto = cached_proto, proto = NDPI_PROTOCOL_STUN; + } else { + if(app_proto != NDPI_PROTOCOL_STUN) { + /* No sense to ass STUN, but only subprotocols */ + +#ifdef DEBUG_LRU + printf("[LRU] ADDING %u / %u.%u [%u -> %u]\n", key, proto, app_proto, + ntohs(flow->packet.udp->source), ntohs(flow->packet.udp->dest)); +#endif + + ndpi_lru_add_to_cache(ndpi_struct->stun_cache, key, app_proto); + ndpi_lru_add_to_cache(ndpi_struct->stun_cache, key_rev, app_proto); + } + } + } + } + + ndpi_set_detected_protocol(ndpi_struct, flow, app_proto, proto); +} + +typedef enum { + NDPI_IS_STUN, + NDPI_IS_NOT_STUN +} ndpi_int_stun_t; + +/* ************************************************************ */ + +static int is_google_ip_address(u_int32_t host) { + if( + ((host & 0xFFFF0000 /* 255.255.0.0 */) == 0x4A7D0000 /* 74.125.0.0/16 */) + || ((host & 0xFFFF0000 /* 255.255.0.0 */) == 0x42660000 /* 66.102.0.0/16 */) + ) + return(1); + else + return(0); +} + +/* ************************************************************ */ + +/* + WhatsApp + 31.13.86.48 + 31.13.92.50 + 157.240.20.51 + 157.240.21.51 + 185.60.216.51 + + Messenger + 31.13.86.5 +*/ + +static int is_messenger_ip_address(u_int32_t host) { + if(host == 0x1F0D5605 /* 31.13.86.5 */) + return(1); + else + return(0); +} + +/* ************************************************************ */ + +static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow, + const u_int8_t * payload, + const u_int16_t payload_length) { + u_int16_t msg_type, msg_len; + struct stun_packet_header *h = (struct stun_packet_header*)payload; + + /* STUN over TCP does not look good */ + if (flow->packet.tcp) + return(NDPI_IS_NOT_STUN); + + if(payload_length >= 512) { + return(NDPI_IS_NOT_STUN); + } else if(payload_length < sizeof(struct stun_packet_header)) { + /* This looks like an invalid packet */ + + if(flow->protos.stun_ssl.stun.num_udp_pkts > 0) { + flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; + return NDPI_IS_STUN; + } else + return NDPI_IS_NOT_STUN; + } + + if((strncmp((const char*)payload, (const char*)"RSP/", 4) == 0) + && (strncmp((const char*)&payload[7], (const char*)" STUN_", 6) == 0)) { + NDPI_LOG_INFO(ndpi_struct, "found stun\n"); + goto udp_stun_found; + } + + msg_type = ntohs(h->msg_type); + msg_len = ntohs(h->msg_len); + + /* https://www.iana.org/assignments/stun-parameters/stun-parameters.xhtml */ + if ((msg_type & 0x3EEF) > 0x000B && msg_type != 0x0800) { +#ifdef DEBUG_STUN + printf("[STUN] msg_type = %04X\n", msg_type); +#endif + + /* + If we're here it's because this does not look like STUN anymore + as this was a flow that started as STUN and turned into something + else. Let's investigate what is that about + */ + if (payload[0] == 0x16) { + /* Let's check if this is DTLS used by some socials */ + struct ndpi_packet_struct *packet = &flow->packet; + u_int16_t total_len, version = htons(*((u_int16_t*) &packet->payload[1])); + + switch (version) { + case 0xFEFF: /* DTLS 1.0 */ + case 0xFEFD: /* DTLS 1.2 */ + total_len = ntohs(*((u_int16_t*) &packet->payload[11])) + 13; + + if (payload_length == total_len) { + /* This is DTLS and the only protocol we know behaves like this is signal */ + flow->guessed_host_protocol_id = NDPI_PROTOCOL_SIGNAL; + return NDPI_IS_STUN; + } + } + } + + return NDPI_IS_NOT_STUN; + } + +#if 0 + if((flow->packet.udp->dest == htons(3480)) || + (flow->packet.udp->source == htons(3480)) + ) + printf("[STUN] Here we go\n");; +#endif + + if (ndpi_struct->stun_cache) { + u_int16_t proto; + u_int32_t key = get_stun_lru_key(flow, 0); + int rc = ndpi_lru_find_cache(ndpi_struct->stun_cache, key, &proto, + 0 /* Don't remove it as it can be used for other connections */); + +#ifdef DEBUG_LRU + printf("[LRU] Searching %u\n", key); +#endif + + if (!rc) { + key = get_stun_lru_key(flow, 1); + rc = ndpi_lru_find_cache(ndpi_struct->stun_cache, key, &proto, + 0 /* Don't remove it as it can be used for other connections */); + +#ifdef DEBUG_LRU + printf("[LRU] Searching %u\n", key); +#endif + } + + if (rc) { +#ifdef DEBUG_LRU + printf("[LRU] Cache FOUND %u / %u\n", key, proto); +#endif + + flow->guessed_host_protocol_id = proto; + return NDPI_IS_STUN; + } else { +#ifdef DEBUG_LRU + printf("[LRU] NOT FOUND %u\n", key); +#endif + } + } else { +#ifdef DEBUG_LRU + printf("[LRU] NO/EMPTY CACHE\n"); +#endif + } + + if(msg_type == 0x01 /* Binding Request */) { + flow->protos.stun_ssl.stun.num_binding_requests++; + + if (!msg_len && flow->guessed_host_protocol_id == NDPI_PROTOCOL_GOOGLE) + flow->guessed_host_protocol_id = NDPI_PROTOCOL_HANGOUT_DUO; + else + flow->guessed_protocol_id = NDPI_PROTOCOL_STUN; + + if (!msg_len) { + /* flow->protos.stun_ssl.stun.num_udp_pkts++; */ + return NDPI_IS_NOT_STUN; /* This to keep analyzing STUN instead of giving up */ + } + } + + if (!msg_len && flow->guessed_host_protocol_id == NDPI_PROTOCOL_UNKNOWN) { + NDPI_EXCLUDE_PROTO(ndpi_struct, flow); + return(NDPI_IS_NOT_STUN); + } + + flow->protos.stun_ssl.stun.num_udp_pkts++; + + if((payload[0] == 0x80 && payload_length < 512 && ((msg_len+20) <= payload_length))) { + flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; + return NDPI_IS_STUN; /* This is WhatsApp Call */ + } else if((payload[0] == 0x90) && (((msg_len+11) == payload_length) || + (flow->protos.stun_ssl.stun.num_binding_requests >= 4))) { + flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; + return NDPI_IS_STUN; /* This is WhatsApp Call */ + } + + if (payload[0] != 0x80 && (msg_len + 20) > payload_length) + return(NDPI_IS_NOT_STUN); + else { + switch(flow->guessed_protocol_id) { + case NDPI_PROTOCOL_HANGOUT_DUO: + case NDPI_PROTOCOL_MESSENGER: + case NDPI_PROTOCOL_WHATSAPP_CALL: + /* Don't overwrite the protocol with sub-STUN protocols */ + break; + + default: + flow->guessed_protocol_id = NDPI_PROTOCOL_STUN; + break; + } + } + + if (payload_length == (msg_len+20)) { + if ((msg_type & 0x3EEF) <= 0x000B) /* http://www.3cx.com/blog/voip-howto/stun-details/ */ { + u_int offset = 20; + + /* + This can either be the standard RTCP or Ms Lync RTCP that + later will become Ms Lync RTP. In this case we need to + be careful before deciding about the protocol before dissecting the packet + + MS Lync = Skype + https://en.wikipedia.org/wiki/Skype_for_Business + */ + + while((offset+2) < payload_length) { + u_int16_t attribute = ntohs(*((u_int16_t*)&payload[offset])); + u_int16_t len = ntohs(*((u_int16_t*)&payload[offset+2])); + u_int16_t x = (len + 4) % 4; + + if (x) + len += 4-x; + +#ifdef DEBUG_STUN + printf("==> Attribute: %04X\n", attribute); +#endif + + switch(attribute) { + case 0x4000: + case 0x4001: + case 0x4002: + /* These are the only messages apparently whatsapp voice can use */ + flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; + return NDPI_IS_STUN; + break; + + case 0x0014: /* Realm */ + { + u_int16_t realm_len = ntohs(*((u_int16_t*)&payload[offset+2])); + + if(flow->host_server_name[0] == '\0') { + u_int j, i = (realm_len > sizeof(flow->host_server_name)) ? sizeof(flow->host_server_name) : realm_len; + u_int k = offset+4; + + memset(flow->host_server_name, 0, sizeof(flow->host_server_name)); + + for(j=0; jhost_server_name[j] = payload[k++]; + +#ifdef DEBUG_STUN + printf("==> [%s]\n", flow->host_server_name); +#endif + + if (strstr((char*) flow->host_server_name, "google.com") != NULL) { + flow->guessed_host_protocol_id = NDPI_PROTOCOL_HANGOUT_DUO; + return NDPI_IS_STUN; + } else if (strstr((char*) flow->host_server_name, "whispersystems.org") != NULL) { + flow->guessed_host_protocol_id = NDPI_PROTOCOL_SIGNAL; + return NDPI_IS_STUN; + } + } + } + break; + + case 0xC057: /* Messeger */ + if (msg_type == 0x0001) { + if ((msg_len == 100) || (msg_len == 104)) { + flow->guessed_host_protocol_id = NDPI_PROTOCOL_MESSENGER; + return NDPI_IS_STUN; + } else if(msg_len == 76) { +#if 0 + if(1) { + flow->guessed_host_protocol_id = NDPI_PROTOCOL_HANGOUT_DUO; + return NDPI_IS_NOT_STUN; /* This case is found also with signal traffic */ + } else + return NDPI_IS_STUN; +#endif + } + } + break; + + case 0x8054: /* Candidate Identifier */ + if((len == 4) + && ((offset+7) < payload_length) + && (payload[offset+5] == 0x00) + && (payload[offset+6] == 0x00) + && (payload[offset+7] == 0x00)) { + /* Either skype for business or "normal" skype with multiparty call */ +#ifdef DEBUG_STUN + printf("==> Skype found\n"); +#endif + flow->guessed_host_protocol_id = NDPI_PROTOCOL_SKYPE_CALL; + return NDPI_IS_STUN; + } + + break; + + case 0x8055: /* MS Service Quality (skype?) */ + break; + + /* Proprietary fields found on skype calls */ + case 0x24DF: + case 0x3802: + case 0x8036: + case 0x8095: + case 0x0800: + case 0x8006: /* This is found on skype calls) */ + /* printf("====>>>> %04X\n", attribute); */ +#ifdef DEBUG_STUN + printf("==> Skype (2) found\n"); +#endif + + flow->guessed_host_protocol_id = NDPI_PROTOCOL_SKYPE_CALL; + return NDPI_IS_STUN; + break; + + case 0x8070: /* Implementation Version */ + if (len == 4 && ((offset+7) < payload_length) + && (payload[offset+4] == 0x00) && (payload[offset+5] == 0x00) && (payload[offset+6] == 0x00) && + ((payload[offset+7] == 0x02) || (payload[offset+7] == 0x03))) { +#ifdef DEBUG_STUN + printf("==> Skype (3) found\n"); +#endif + + flow->guessed_host_protocol_id = NDPI_PROTOCOL_SKYPE_CALL; + return NDPI_IS_STUN; + } + break; + + case 0xFF03: + flow->guessed_host_protocol_id = NDPI_PROTOCOL_HANGOUT_DUO; + return NDPI_IS_STUN; + break; + + default: +#ifdef DEBUG_STUN + printf("==> %04X\n", attribute); +#endif + break; + } + + offset += len + 4; + } + + goto udp_stun_found; + } else if(msg_type == 0x0800) { + flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; + return NDPI_IS_STUN; + } + } + + if ((flow->protos.stun_ssl.stun.num_udp_pkts > 0) && (msg_type <= 0x00FF)) { + flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; + return NDPI_IS_STUN; + } else + return NDPI_IS_NOT_STUN; + +udp_stun_found: + flow->protos.stun_ssl.stun.num_processed_pkts++; + + struct ndpi_packet_struct *packet = &flow->packet; + +#ifdef DEBUG_STUN + printf("==>> NDPI_PROTOCOL_WHATSAPP_CALL\n"); +#endif + + if ((ntohs(packet->udp->source) == 3478) || (ntohs(packet->udp->dest) == 3478)) { + flow->guessed_host_protocol_id = (is_messenger_ip_address(ntohl(packet->iph->saddr)) || + is_messenger_ip_address(ntohl(packet->iph->daddr))) ? NDPI_PROTOCOL_MESSENGER : + NDPI_PROTOCOL_WHATSAPP_CALL; + } else + flow->guessed_host_protocol_id = (is_google_ip_address(ntohl(packet->iph->saddr)) || + is_google_ip_address(ntohl(packet->iph->daddr))) ? NDPI_PROTOCOL_HANGOUT_DUO : NDPI_PROTOCOL_WHATSAPP_CALL; + + int rc = (flow->protos.stun_ssl.stun.num_udp_pkts < MAX_NUM_STUN_PKTS) ? NDPI_IS_NOT_STUN : NDPI_IS_STUN; + + return rc; +} + + +void ndpi_search_stun(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) +{ + struct ndpi_packet_struct *packet = &flow->packet; + + NDPI_LOG_DBG(ndpi_struct, "search stun\n"); + + if(packet->payload == NULL) + return; + else if(packet->iphv6 != NULL) { + NDPI_EXCLUDE_PROTO(ndpi_struct, flow); + return; + } + + if(packet->tcp) { + /* STUN may be encapsulated in TCP packets */ + if((packet->payload_packet_len >= 22) + && ((ntohs(get_u_int16_t(packet->payload, 0)) + 2) == packet->payload_packet_len)) { + /* TODO there could be several STUN packets in a single TCP packet so maybe the detection could be + * improved by checking only the STUN packet of given length */ + + if(ndpi_int_check_stun(ndpi_struct, flow, packet->payload + 2, + packet->payload_packet_len - 2) == NDPI_IS_STUN) { + goto udp_stun_match; + } + } + } + + /* UDP */ + if(ndpi_int_check_stun(ndpi_struct, flow, packet->payload, + packet->payload_packet_len) == NDPI_IS_STUN) { + udp_stun_match: + if (flow->guessed_protocol_id == NDPI_PROTOCOL_UNKNOWN) + flow->guessed_protocol_id = NDPI_PROTOCOL_STUN; + + if(flow->guessed_host_protocol_id == NDPI_PROTOCOL_UNKNOWN) { + flow->guessed_host_protocol_id = flow->guessed_protocol_id; + flow->guessed_protocol_id = NDPI_PROTOCOL_STUN; + } + + ndpi_int_stun_add_connection(ndpi_struct, flow, + flow->guessed_protocol_id, + flow->guessed_host_protocol_id); + return; + } + + if(flow->protos.stun_ssl.stun.num_udp_pkts >= MAX_NUM_STUN_PKTS) + NDPI_EXCLUDE_PROTO(ndpi_struct, flow); + + if(flow->packet_counter > 0) { + /* This might be a RTP stream: let's make sure we check it */ + NDPI_CLR(&flow->excluded_protocol_bitmask, NDPI_PROTOCOL_RTP); + } +} + + +void init_stun_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id, + NDPI_PROTOCOL_BITMASK *detection_bitmask) { + ndpi_set_bitmask_protocol_detection("STUN", ndpi_struct, detection_bitmask, *id, + NDPI_PROTOCOL_STUN, + ndpi_search_stun, + NDPI_SELECTION_BITMASK_PROTOCOL_TCP_OR_UDP_WITH_PAYLOAD, + SAVE_DETECTION_BITMASK_AS_UNKNOWN, + ADD_TO_DETECTION_BITMASK); + + *id += 1; +} -- cgit v1.2.3 From bd8384702736c84bdb184636c5fcde3f03859d0d Mon Sep 17 00:00:00 2001 From: marco-testa <55734062+marco-testa@users.noreply.github.com> Date: Tue, 24 Sep 2019 09:43:47 +0200 Subject: Changed the packets handling with STUN msg_type > 0x000C and other fixes. Eliminated double call to the ndpi_int_stun_add_connection function. --- src/lib/protocols/stun.c | 401 ++++++++++++++++++++--------------------------- 1 file changed, 173 insertions(+), 228 deletions(-) diff --git a/src/lib/protocols/stun.c b/src/lib/protocols/stun.c index bf2eb0b86..9475192da 100644 --- a/src/lib/protocols/stun.c +++ b/src/lib/protocols/stun.c @@ -38,22 +38,6 @@ struct stun_packet_header { u_int8_t transaction_id[8]; }; -/* ************************************************************ */ - -static u_int8_t is_stun_based_proto(u_int16_t proto) { - - switch(proto) { - case NDPI_PROTOCOL_WHATSAPP_CALL: - case NDPI_PROTOCOL_MESSENGER: - case NDPI_PROTOCOL_HANGOUT_DUO: - case NDPI_PROTOCOL_SKYPE_CALL: - case NDPI_PROTOCOL_SIGNAL: - case NDPI_PROTOCOL_STUN: - return(1); - } - - return(0); -} /* ************************************************************ */ @@ -160,10 +144,10 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct * const u_int16_t payload_length) { u_int16_t msg_type, msg_len; struct stun_packet_header *h = (struct stun_packet_header*)payload; - u_int8_t can_this_be_whatsapp_voice = 1; /* STUN over TCP does not look good */ - if(flow->packet.tcp) return(NDPI_IS_NOT_STUN); + if (flow->packet.tcp) + return(NDPI_IS_NOT_STUN); if(payload_length >= 512) { return(NDPI_IS_NOT_STUN); @@ -172,9 +156,9 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct * if(flow->protos.stun_ssl.stun.num_udp_pkts > 0) { flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; - return(NDPI_IS_STUN); /* This is WhatsApp Voice */ + return NDPI_IS_STUN; } else - return(NDPI_IS_NOT_STUN); + return NDPI_IS_NOT_STUN; } if((strncmp((const char*)payload, (const char*)"RSP/", 4) == 0) @@ -183,53 +167,39 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct * goto udp_stun_found; } - msg_type = ntohs(h->msg_type) /* & 0x3EEF */, msg_len = ntohs(h->msg_len); + msg_type = ntohs(h->msg_type); + msg_len = ntohs(h->msg_len); /* https://www.iana.org/assignments/stun-parameters/stun-parameters.xhtml */ - if(msg_type > 0x000C) { + if ((msg_type & 0x3EEF) > 0x000B && msg_type != 0x0800) { #ifdef DEBUG_STUN printf("[STUN] msg_type = %04X\n", msg_type); #endif - if(is_stun_based_proto(flow->guessed_host_protocol_id)) { - /* - In this case we have the detected the typical STUN pattern - of modern protocols where the flow starts as STUN and becomes - something else that has nothing to do with STUN anymore - */ - ndpi_int_stun_add_connection(ndpi_struct, flow, - flow->guessed_protocol_id, - flow->guessed_host_protocol_id); - return(NDPI_IS_STUN); - } - /* - If we're here it's because this does not look like STUN anymore - as this was a flow that started as STUN and turned into something - else. Let's investigate what is that about - */ - if(payload[0] == 0x16) { + If we're here it's because this does not look like STUN anymore + as this was a flow that started as STUN and turned into something + else. Let's investigate what is that about + */ + if (payload[0] == 0x16) { /* Let's check if this is DTLS used by some socials */ struct ndpi_packet_struct *packet = &flow->packet; - u_int16_t total_len, version = htons(*((u_int16_t*)&packet->payload[1])); - - switch(version) { - case 0xFEFF: /* DTLS 1.0 */ - case 0xFEFD: /* DTLS 1.2 */ - total_len = ntohs(*((u_int16_t*)&packet->payload[11]))+13; - - if(payload_length == total_len) { - /* This is DTLS and the only protocol we know behaves like this is signal */ - flow->guessed_host_protocol_id = NDPI_PROTOCOL_SIGNAL; - ndpi_int_stun_add_connection(ndpi_struct, flow, - flow->guessed_protocol_id, - flow->guessed_host_protocol_id); - return(NDPI_IS_STUN); - } + u_int16_t total_len, version = htons(*((u_int16_t*) &packet->payload[1])); + + switch (version) { + case 0xFEFF: /* DTLS 1.0 */ + case 0xFEFD: /* DTLS 1.2 */ + total_len = ntohs(*((u_int16_t*) &packet->payload[11])) + 13; + + if (payload_length == total_len) { + /* This is DTLS and the only protocol we know behaves like this is signal */ + flow->guessed_host_protocol_id = NDPI_PROTOCOL_SIGNAL; + return NDPI_IS_STUN; + } } } - - return(NDPI_IS_NOT_STUN); + + return NDPI_IS_NOT_STUN; } #if 0 @@ -239,32 +209,33 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct * printf("[STUN] Here we go\n");; #endif - if(ndpi_struct->stun_cache) { + if (ndpi_struct->stun_cache) { u_int16_t proto; u_int32_t key = get_stun_lru_key(flow, 0); - int rc = ndpi_lru_find_cache(ndpi_struct->stun_cache, key, &proto, 0 /* Don't remove it as it can be used for other connections */); + int rc = ndpi_lru_find_cache(ndpi_struct->stun_cache, key, &proto, + 0 /* Don't remove it as it can be used for other connections */); #ifdef DEBUG_LRU printf("[LRU] Searching %u\n", key); #endif - if(!rc) { + if (!rc) { key = get_stun_lru_key(flow, 1); - rc = ndpi_lru_find_cache(ndpi_struct->stun_cache, key, &proto, 0 /* Don't remove it as it can be used for other connections */); + rc = ndpi_lru_find_cache(ndpi_struct->stun_cache, key, &proto, + 0 /* Don't remove it as it can be used for other connections */); #ifdef DEBUG_LRU - printf("[LRU] Searching %u\n", key); + printf("[LRU] Searching %u\n", key); #endif } - if(rc) { + if (rc) { #ifdef DEBUG_LRU printf("[LRU] Cache FOUND %u / %u\n", key, proto); #endif - flow->guessed_host_protocol_id = proto, flow->guessed_protocol_id = NDPI_PROTOCOL_STUN; - - return(NDPI_IS_STUN); + flow->guessed_host_protocol_id = proto; + return NDPI_IS_STUN; } else { #ifdef DEBUG_LRU printf("[LRU] NOT FOUND %u\n", key); @@ -279,42 +250,34 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct * if(msg_type == 0x01 /* Binding Request */) { flow->protos.stun_ssl.stun.num_binding_requests++; - if((msg_len == 0) && (flow->guessed_host_protocol_id == NDPI_PROTOCOL_GOOGLE)) + if (!msg_len && flow->guessed_host_protocol_id == NDPI_PROTOCOL_GOOGLE) flow->guessed_host_protocol_id = NDPI_PROTOCOL_HANGOUT_DUO; else flow->guessed_protocol_id = NDPI_PROTOCOL_STUN; - if(msg_len == 0) { + if (!msg_len) { /* flow->protos.stun_ssl.stun.num_udp_pkts++; */ - return(NDPI_IS_NOT_STUN); /* This to keep analyzing STUN instead of giving up */ + return NDPI_IS_NOT_STUN; /* This to keep analyzing STUN instead of giving up */ } } - if((msg_len == 0) && (flow->guessed_host_protocol_id == NDPI_PROTOCOL_UNKNOWN)) { + if (!msg_len && flow->guessed_host_protocol_id == NDPI_PROTOCOL_UNKNOWN) { NDPI_EXCLUDE_PROTO(ndpi_struct, flow); return(NDPI_IS_NOT_STUN); } flow->protos.stun_ssl.stun.num_udp_pkts++; - /* - printf("[msg_type: %04X][payload_length: %u][num_binding_request: %u]\n", - msg_type, payload_length, flow->protos.stun_ssl.stun.num_binding_requests); - */ - - if(((payload[0] == 0x80) - && (payload_length < 512) - && ((msg_len+20) <= payload_length)) /* WhatsApp Voice */) { + if((payload[0] == 0x80 && payload_length < 512 && ((msg_len+20) <= payload_length))) { flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; - return(NDPI_IS_STUN); /* This is WhatsApp Voice */ - } else if((payload[0] == 0x90) - && (((msg_len+11) == payload_length) /* WhatsApp Video */ - || (flow->protos.stun_ssl.stun.num_binding_requests >= 4))) { + return NDPI_IS_STUN; /* This is WhatsApp Call */ + } else if((payload[0] == 0x90) && (((msg_len+11) == payload_length) || + (flow->protos.stun_ssl.stun.num_binding_requests >= 4))) { flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; - return(NDPI_IS_STUN); /* This is WhatsApp Video */ + return NDPI_IS_STUN; /* This is WhatsApp Call */ } - if((payload[0] != 0x80) && ((msg_len+20) > payload_length)) + if (payload[0] != 0x80 && (msg_len + 20) > payload_length) return(NDPI_IS_NOT_STUN); else { switch(flow->guessed_protocol_id) { @@ -330,202 +293,184 @@ static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct * } } - if(payload_length == (msg_len+20)) { - if(msg_type <= 0x000b) /* http://www.3cx.com/blog/voip-howto/stun-details/ */ { + if (payload_length == (msg_len+20)) { + if ((msg_type & 0x3EEF) <= 0x000B) /* http://www.3cx.com/blog/voip-howto/stun-details/ */ { u_int offset = 20; - // printf("[%02X][%02X][%02X][%02X][payload_length: %u]\n", payload[offset], payload[offset+1], payload[offset+2], payload[offset+3],payload_length); - /* - This can either be the standard RTCP or Ms Lync RTCP that - later will become Ms Lync RTP. In this case we need to - be careful before deciding about the protocol before dissecting the packet + This can either be the standard RTCP or Ms Lync RTCP that + later will become Ms Lync RTP. In this case we need to + be careful before deciding about the protocol before dissecting the packet - MS Lync = Skype - https://en.wikipedia.org/wiki/Skype_for_Business - */ + MS Lync = Skype + https://en.wikipedia.org/wiki/Skype_for_Business + */ while((offset+2) < payload_length) { - u_int16_t attribute = ntohs(*((u_int16_t*)&payload[offset])); - u_int16_t len = ntohs(*((u_int16_t*)&payload[offset+2])); - u_int16_t x = (len + 4) % 4; + u_int16_t attribute = ntohs(*((u_int16_t*)&payload[offset])); + u_int16_t len = ntohs(*((u_int16_t*)&payload[offset+2])); + u_int16_t x = (len + 4) % 4; - if(x != 0) - len += 4-x; + if (x) + len += 4-x; #ifdef DEBUG_STUN - printf("==> Attribute: %04X\n", attribute); + printf("==> Attribute: %04X\n", attribute); #endif - switch(attribute) { - // case 0x0008: /* Message Integrity */ - // case 0x0020: /* XOR-MAPPED-ADDRESSES */ - case 0x4000: - case 0x4001: - case 0x4002: - /* These are the only messages apparently whatsapp voice can use */ - break; + switch(attribute) { + case 0x4000: + case 0x4001: + case 0x4002: + /* These are the only messages apparently whatsapp voice can use */ + flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; + return NDPI_IS_STUN; + break; - case 0x0014: /* Realm */ - { - u_int16_t realm_len = ntohs(*((u_int16_t*)&payload[offset+2])); + case 0x0014: /* Realm */ + { + u_int16_t realm_len = ntohs(*((u_int16_t*)&payload[offset+2])); - if(flow->host_server_name[0] == '\0') { - u_int j, i = (realm_len > sizeof(flow->host_server_name)) ? sizeof(flow->host_server_name) : realm_len; - u_int k = offset+4; + if(flow->host_server_name[0] == '\0') { + u_int j, i = (realm_len > sizeof(flow->host_server_name)) ? sizeof(flow->host_server_name) : realm_len; + u_int k = offset+4; - memset(flow->host_server_name, 0, sizeof(flow->host_server_name)); + memset(flow->host_server_name, 0, sizeof(flow->host_server_name)); - for(j=0; jhost_server_name[j] = payload[k++]; + for(j=0; jhost_server_name[j] = payload[k++]; #ifdef DEBUG_STUN - printf("==> [%s]\n", flow->host_server_name); + printf("==> [%s]\n", flow->host_server_name); #endif - if(strstr((char*)flow->host_server_name, "google.com") != NULL) { - flow->guessed_host_protocol_id = NDPI_PROTOCOL_HANGOUT_DUO, flow->guessed_protocol_id = NDPI_PROTOCOL_STUN; - return(NDPI_IS_STUN); - } else if(strstr((char*)flow->host_server_name, "whispersystems.org") != NULL) { - flow->guessed_host_protocol_id = NDPI_PROTOCOL_SIGNAL, flow->guessed_protocol_id = NDPI_PROTOCOL_STUN; - return(NDPI_IS_STUN); - } - } - } - break; - - case 0xC057: /* Messeger */ - if(msg_type == 0x0001) { - if((msg_len == 100) || (msg_len == 104)) { - flow->guessed_host_protocol_id = NDPI_PROTOCOL_MESSENGER, flow->guessed_protocol_id = NDPI_PROTOCOL_STUN; - return(NDPI_IS_STUN); - } else if(msg_len == 76) { + if (strstr((char*) flow->host_server_name, "google.com") != NULL) { + flow->guessed_host_protocol_id = NDPI_PROTOCOL_HANGOUT_DUO; + return NDPI_IS_STUN; + } else if (strstr((char*) flow->host_server_name, "whispersystems.org") != NULL) { + flow->guessed_host_protocol_id = NDPI_PROTOCOL_SIGNAL; + return NDPI_IS_STUN; + } + } + } + break; + + case 0xC057: /* Messeger */ + if (msg_type == 0x0001) { + if ((msg_len == 100) || (msg_len == 104)) { + flow->guessed_host_protocol_id = NDPI_PROTOCOL_MESSENGER; + return NDPI_IS_STUN; + } else if(msg_len == 76) { #if 0 - if(1) { - flow->guessed_host_protocol_id = NDPI_PROTOCOL_HANGOUT_DUO, flow->guessed_protocol_id = NDPI_PROTOCOL_STUN; - return(NDPI_IS_NOT_STUN); /* This case is found also with signal traffic */ - } else - return(NDPI_IS_STUN); + if(1) { + flow->guessed_host_protocol_id = NDPI_PROTOCOL_HANGOUT_DUO; + return NDPI_IS_NOT_STUN; /* This case is found also with signal traffic */ + } else + return NDPI_IS_STUN; #endif - } - } - break; - - case 0x8054: /* Candidate Identifier */ - if((len == 4) - && ((offset+7) < payload_length) - && (payload[offset+5] == 0x00) - && (payload[offset+6] == 0x00) - && (payload[offset+7] == 0x00)) { - /* Either skype for business or "normal" skype with multiparty call */ + } + } + break; + + case 0x8054: /* Candidate Identifier */ + if((len == 4) + && ((offset+7) < payload_length) + && (payload[offset+5] == 0x00) + && (payload[offset+6] == 0x00) + && (payload[offset+7] == 0x00)) { + /* Either skype for business or "normal" skype with multiparty call */ #ifdef DEBUG_STUN - printf("==> Skype found\n"); + printf("==> Skype found\n"); #endif - flow->guessed_protocol_id = NDPI_PROTOCOL_SKYPE_CALL; - return(NDPI_IS_STUN); - } - break; - - case 0x8055: /* MS Service Quality (skype?) */ - break; - - /* Proprietary fields found on skype calls */ - case 0x24DF: - case 0x3802: - case 0x8036: - case 0x8095: - case 0x0800: - case 0x8006: /* This is found on skype calls) */ - /* printf("====>>>> %04X\n", attribute); */ + flow->guessed_host_protocol_id = NDPI_PROTOCOL_SKYPE_CALL; + return NDPI_IS_STUN; + } + + break; + + case 0x8055: /* MS Service Quality (skype?) */ + break; + + /* Proprietary fields found on skype calls */ + case 0x24DF: + case 0x3802: + case 0x8036: + case 0x8095: + case 0x0800: + case 0x8006: /* This is found on skype calls) */ + /* printf("====>>>> %04X\n", attribute); */ #ifdef DEBUG_STUN - printf("==> Skype (2) found\n"); + printf("==> Skype (2) found\n"); #endif - flow->guessed_protocol_id = NDPI_PROTOCOL_SKYPE_CALL; - return(NDPI_IS_STUN); - break; - - case 0x8070: /* Implementation Version */ - if((len == 4) - && ((offset+7) < payload_length) - && (payload[offset+4] == 0x00) - && (payload[offset+5] == 0x00) - && (payload[offset+6] == 0x00) - && ((payload[offset+7] == 0x02) || (payload[offset+7] == 0x03)) - ) { - flow->guessed_protocol_id = NDPI_PROTOCOL_SKYPE_CALL; + flow->guessed_host_protocol_id = NDPI_PROTOCOL_SKYPE_CALL; + return NDPI_IS_STUN; + break; + + case 0x8070: /* Implementation Version */ + if (len == 4 && ((offset+7) < payload_length) + && (payload[offset+4] == 0x00) && (payload[offset+5] == 0x00) && (payload[offset+6] == 0x00) && + ((payload[offset+7] == 0x02) || (payload[offset+7] == 0x03))) { #ifdef DEBUG_STUN - printf("==> Skype (3) found\n"); + printf("==> Skype (3) found\n"); #endif - return(NDPI_IS_STUN); - } - break; + flow->guessed_host_protocol_id = NDPI_PROTOCOL_SKYPE_CALL; + return NDPI_IS_STUN; + } + break; - case 0xFF03: - can_this_be_whatsapp_voice = 0; - flow->guessed_host_protocol_id = NDPI_PROTOCOL_HANGOUT_DUO; - break; + case 0xFF03: + flow->guessed_host_protocol_id = NDPI_PROTOCOL_HANGOUT_DUO; + return NDPI_IS_STUN; + break; - default: - /* This means this STUN packet cannot be confused with whatsapp voice */ + default: #ifdef DEBUG_STUN - printf("==> %04X\n", attribute); + printf("==> %04X\n", attribute); #endif - can_this_be_whatsapp_voice = 0; - break; - } + break; + } - offset += len + 4; + offset += len + 4; } + goto udp_stun_found; } else if(msg_type == 0x0800) { flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; - return(NDPI_IS_STUN); /* This is WhatsApp */ + return NDPI_IS_STUN; } } - if((flow->protos.stun_ssl.stun.num_udp_pkts > 0) && (msg_type <= 0x00FF)) { + if ((flow->protos.stun_ssl.stun.num_udp_pkts > 0) && (msg_type <= 0x00FF)) { flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; - return(NDPI_IS_STUN); /* This is WhatsApp Voice */ + return NDPI_IS_STUN; } else - return(NDPI_IS_NOT_STUN); + return NDPI_IS_NOT_STUN; - udp_stun_found: - if(can_this_be_whatsapp_voice) { - struct ndpi_packet_struct *packet = &flow->packet; - int rc; +udp_stun_found: + flow->protos.stun_ssl.stun.num_processed_pkts++; + + struct ndpi_packet_struct *packet = &flow->packet; - flow->protos.stun_ssl.stun.num_processed_pkts++; #ifdef DEBUG_STUN - printf("==>> NDPI_PROTOCOL_WHATSAPP_CALL\n"); + printf("==>> NDPI_PROTOCOL_WHATSAPP_CALL\n"); #endif - if((ntohs(packet->udp->source) == 3478) || (ntohs(packet->udp->dest) == 3478)) { - flow->guessed_host_protocol_id = (is_messenger_ip_address(ntohl(packet->iph->saddr)) || is_messenger_ip_address(ntohl(packet->iph->daddr))) ? - NDPI_PROTOCOL_MESSENGER : NDPI_PROTOCOL_WHATSAPP_CALL; - } else - flow->guessed_host_protocol_id = (is_google_ip_address(ntohl(packet->iph->saddr)) || is_google_ip_address(ntohl(packet->iph->daddr))) - ? NDPI_PROTOCOL_HANGOUT_DUO : NDPI_PROTOCOL_WHATSAPP_CALL; - - rc = (flow->protos.stun_ssl.stun.num_udp_pkts < MAX_NUM_STUN_PKTS) ? NDPI_IS_NOT_STUN : NDPI_IS_STUN; + if ((ntohs(packet->udp->source) == 3478) || (ntohs(packet->udp->dest) == 3478)) { + flow->guessed_host_protocol_id = (is_messenger_ip_address(ntohl(packet->iph->saddr)) || + is_messenger_ip_address(ntohl(packet->iph->daddr))) ? NDPI_PROTOCOL_MESSENGER : + NDPI_PROTOCOL_WHATSAPP_CALL; + } else + flow->guessed_host_protocol_id = (is_google_ip_address(ntohl(packet->iph->saddr)) || + is_google_ip_address(ntohl(packet->iph->daddr))) ? NDPI_PROTOCOL_HANGOUT_DUO : NDPI_PROTOCOL_WHATSAPP_CALL; - if(rc == NDPI_IS_STUN) - ndpi_int_stun_add_connection(ndpi_struct, flow, - NDPI_IS_STUN, - flow->guessed_host_protocol_id); + int rc = (flow->protos.stun_ssl.stun.num_udp_pkts < MAX_NUM_STUN_PKTS) ? NDPI_IS_NOT_STUN : NDPI_IS_STUN; - return(rc); - } else { - /* - We cannot immediately say that this is STUN as there are other protocols - like GoogleHangout that might be candidates, thus we set the - guessed protocol to STUN - */ - return(NDPI_IS_NOT_STUN); - } + return rc; } + void ndpi_search_stun(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { struct ndpi_packet_struct *packet = &flow->packet; @@ -557,7 +502,7 @@ void ndpi_search_stun(struct ndpi_detection_module_struct *ndpi_struct, struct n if(ndpi_int_check_stun(ndpi_struct, flow, packet->payload, packet->payload_packet_len) == NDPI_IS_STUN) { udp_stun_match: - if(flow->guessed_protocol_id == NDPI_PROTOCOL_UNKNOWN) + if (flow->guessed_protocol_id == NDPI_PROTOCOL_UNKNOWN) flow->guessed_protocol_id = NDPI_PROTOCOL_STUN; if(flow->guessed_host_protocol_id == NDPI_PROTOCOL_UNKNOWN) { -- cgit v1.2.3 From b706730e10ce6d97779a24c83c551440d82c1b21 Mon Sep 17 00:00:00 2001 From: marco-testa <55734062+marco-testa@users.noreply.github.com> Date: Tue, 24 Sep 2019 09:45:34 +0200 Subject: Loaded in the wrong place --- stun.c | 539 ----------------------------------------------------------------- 1 file changed, 539 deletions(-) delete mode 100644 stun.c diff --git a/stun.c b/stun.c deleted file mode 100644 index 9475192da..000000000 --- a/stun.c +++ /dev/null @@ -1,539 +0,0 @@ -/* - * stun.c - * - * Copyright (C) 2011-19 - ntop.org - * - * This file is part of nDPI, an open source deep packet inspection - * library based on the OpenDPI and PACE technology by ipoque GmbH - * - * nDPI is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * nDPI is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with nDPI. If not, see . - * - */ - -#include "ndpi_protocol_ids.h" - -#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_STUN - -#include "ndpi_api.h" - -#define MAX_NUM_STUN_PKTS 8 - -// #define DEBUG_STUN 1 -// #define DEBUG_LRU 1 - -struct stun_packet_header { - u_int16_t msg_type, msg_len; - u_int32_t cookie; - u_int8_t transaction_id[8]; -}; - - -/* ************************************************************ */ - -u_int32_t get_stun_lru_key(struct ndpi_flow_struct *flow, u_int8_t rev) { - if(rev) - return(flow->packet.iph->daddr + flow->packet.udp->dest); - else - return(flow->packet.iph->saddr + flow->packet.udp->source); -} - -/* ************************************************************ */ - -void ndpi_int_stun_add_connection(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow, - u_int proto, u_int app_proto) { - if(ndpi_struct->stun_cache == NULL) - ndpi_struct->stun_cache = ndpi_lru_cache_init(1024); - - if(ndpi_struct->stun_cache - && flow->packet.iph - && flow->packet.udp - && (app_proto != NDPI_PROTOCOL_UNKNOWN) - ) /* Cache flow sender info */ { - u_int32_t key = get_stun_lru_key(flow, 0); - u_int16_t cached_proto; - - if(ndpi_lru_find_cache(ndpi_struct->stun_cache, key, - &cached_proto, 0 /* Don't remove it as it can be used for other connections */)) { -#ifdef DEBUG_LRU - printf("[LRU] FOUND %u / %u: no need to cache %u.%u\n", key, cached_proto, proto, app_proto); -#endif - app_proto = cached_proto, proto = NDPI_PROTOCOL_STUN; - } else { - u_int32_t key_rev = get_stun_lru_key(flow, 1); - - if(ndpi_lru_find_cache(ndpi_struct->stun_cache, key_rev, - &cached_proto, 0 /* Don't remove it as it can be used for other connections */)) { -#ifdef DEBUG_LRU - printf("[LRU] FOUND %u / %u: no need to cache %u.%u\n", key_rev, cached_proto, proto, app_proto); -#endif - app_proto = cached_proto, proto = NDPI_PROTOCOL_STUN; - } else { - if(app_proto != NDPI_PROTOCOL_STUN) { - /* No sense to ass STUN, but only subprotocols */ - -#ifdef DEBUG_LRU - printf("[LRU] ADDING %u / %u.%u [%u -> %u]\n", key, proto, app_proto, - ntohs(flow->packet.udp->source), ntohs(flow->packet.udp->dest)); -#endif - - ndpi_lru_add_to_cache(ndpi_struct->stun_cache, key, app_proto); - ndpi_lru_add_to_cache(ndpi_struct->stun_cache, key_rev, app_proto); - } - } - } - } - - ndpi_set_detected_protocol(ndpi_struct, flow, app_proto, proto); -} - -typedef enum { - NDPI_IS_STUN, - NDPI_IS_NOT_STUN -} ndpi_int_stun_t; - -/* ************************************************************ */ - -static int is_google_ip_address(u_int32_t host) { - if( - ((host & 0xFFFF0000 /* 255.255.0.0 */) == 0x4A7D0000 /* 74.125.0.0/16 */) - || ((host & 0xFFFF0000 /* 255.255.0.0 */) == 0x42660000 /* 66.102.0.0/16 */) - ) - return(1); - else - return(0); -} - -/* ************************************************************ */ - -/* - WhatsApp - 31.13.86.48 - 31.13.92.50 - 157.240.20.51 - 157.240.21.51 - 185.60.216.51 - - Messenger - 31.13.86.5 -*/ - -static int is_messenger_ip_address(u_int32_t host) { - if(host == 0x1F0D5605 /* 31.13.86.5 */) - return(1); - else - return(0); -} - -/* ************************************************************ */ - -static ndpi_int_stun_t ndpi_int_check_stun(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow, - const u_int8_t * payload, - const u_int16_t payload_length) { - u_int16_t msg_type, msg_len; - struct stun_packet_header *h = (struct stun_packet_header*)payload; - - /* STUN over TCP does not look good */ - if (flow->packet.tcp) - return(NDPI_IS_NOT_STUN); - - if(payload_length >= 512) { - return(NDPI_IS_NOT_STUN); - } else if(payload_length < sizeof(struct stun_packet_header)) { - /* This looks like an invalid packet */ - - if(flow->protos.stun_ssl.stun.num_udp_pkts > 0) { - flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; - return NDPI_IS_STUN; - } else - return NDPI_IS_NOT_STUN; - } - - if((strncmp((const char*)payload, (const char*)"RSP/", 4) == 0) - && (strncmp((const char*)&payload[7], (const char*)" STUN_", 6) == 0)) { - NDPI_LOG_INFO(ndpi_struct, "found stun\n"); - goto udp_stun_found; - } - - msg_type = ntohs(h->msg_type); - msg_len = ntohs(h->msg_len); - - /* https://www.iana.org/assignments/stun-parameters/stun-parameters.xhtml */ - if ((msg_type & 0x3EEF) > 0x000B && msg_type != 0x0800) { -#ifdef DEBUG_STUN - printf("[STUN] msg_type = %04X\n", msg_type); -#endif - - /* - If we're here it's because this does not look like STUN anymore - as this was a flow that started as STUN and turned into something - else. Let's investigate what is that about - */ - if (payload[0] == 0x16) { - /* Let's check if this is DTLS used by some socials */ - struct ndpi_packet_struct *packet = &flow->packet; - u_int16_t total_len, version = htons(*((u_int16_t*) &packet->payload[1])); - - switch (version) { - case 0xFEFF: /* DTLS 1.0 */ - case 0xFEFD: /* DTLS 1.2 */ - total_len = ntohs(*((u_int16_t*) &packet->payload[11])) + 13; - - if (payload_length == total_len) { - /* This is DTLS and the only protocol we know behaves like this is signal */ - flow->guessed_host_protocol_id = NDPI_PROTOCOL_SIGNAL; - return NDPI_IS_STUN; - } - } - } - - return NDPI_IS_NOT_STUN; - } - -#if 0 - if((flow->packet.udp->dest == htons(3480)) || - (flow->packet.udp->source == htons(3480)) - ) - printf("[STUN] Here we go\n");; -#endif - - if (ndpi_struct->stun_cache) { - u_int16_t proto; - u_int32_t key = get_stun_lru_key(flow, 0); - int rc = ndpi_lru_find_cache(ndpi_struct->stun_cache, key, &proto, - 0 /* Don't remove it as it can be used for other connections */); - -#ifdef DEBUG_LRU - printf("[LRU] Searching %u\n", key); -#endif - - if (!rc) { - key = get_stun_lru_key(flow, 1); - rc = ndpi_lru_find_cache(ndpi_struct->stun_cache, key, &proto, - 0 /* Don't remove it as it can be used for other connections */); - -#ifdef DEBUG_LRU - printf("[LRU] Searching %u\n", key); -#endif - } - - if (rc) { -#ifdef DEBUG_LRU - printf("[LRU] Cache FOUND %u / %u\n", key, proto); -#endif - - flow->guessed_host_protocol_id = proto; - return NDPI_IS_STUN; - } else { -#ifdef DEBUG_LRU - printf("[LRU] NOT FOUND %u\n", key); -#endif - } - } else { -#ifdef DEBUG_LRU - printf("[LRU] NO/EMPTY CACHE\n"); -#endif - } - - if(msg_type == 0x01 /* Binding Request */) { - flow->protos.stun_ssl.stun.num_binding_requests++; - - if (!msg_len && flow->guessed_host_protocol_id == NDPI_PROTOCOL_GOOGLE) - flow->guessed_host_protocol_id = NDPI_PROTOCOL_HANGOUT_DUO; - else - flow->guessed_protocol_id = NDPI_PROTOCOL_STUN; - - if (!msg_len) { - /* flow->protos.stun_ssl.stun.num_udp_pkts++; */ - return NDPI_IS_NOT_STUN; /* This to keep analyzing STUN instead of giving up */ - } - } - - if (!msg_len && flow->guessed_host_protocol_id == NDPI_PROTOCOL_UNKNOWN) { - NDPI_EXCLUDE_PROTO(ndpi_struct, flow); - return(NDPI_IS_NOT_STUN); - } - - flow->protos.stun_ssl.stun.num_udp_pkts++; - - if((payload[0] == 0x80 && payload_length < 512 && ((msg_len+20) <= payload_length))) { - flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; - return NDPI_IS_STUN; /* This is WhatsApp Call */ - } else if((payload[0] == 0x90) && (((msg_len+11) == payload_length) || - (flow->protos.stun_ssl.stun.num_binding_requests >= 4))) { - flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; - return NDPI_IS_STUN; /* This is WhatsApp Call */ - } - - if (payload[0] != 0x80 && (msg_len + 20) > payload_length) - return(NDPI_IS_NOT_STUN); - else { - switch(flow->guessed_protocol_id) { - case NDPI_PROTOCOL_HANGOUT_DUO: - case NDPI_PROTOCOL_MESSENGER: - case NDPI_PROTOCOL_WHATSAPP_CALL: - /* Don't overwrite the protocol with sub-STUN protocols */ - break; - - default: - flow->guessed_protocol_id = NDPI_PROTOCOL_STUN; - break; - } - } - - if (payload_length == (msg_len+20)) { - if ((msg_type & 0x3EEF) <= 0x000B) /* http://www.3cx.com/blog/voip-howto/stun-details/ */ { - u_int offset = 20; - - /* - This can either be the standard RTCP or Ms Lync RTCP that - later will become Ms Lync RTP. In this case we need to - be careful before deciding about the protocol before dissecting the packet - - MS Lync = Skype - https://en.wikipedia.org/wiki/Skype_for_Business - */ - - while((offset+2) < payload_length) { - u_int16_t attribute = ntohs(*((u_int16_t*)&payload[offset])); - u_int16_t len = ntohs(*((u_int16_t*)&payload[offset+2])); - u_int16_t x = (len + 4) % 4; - - if (x) - len += 4-x; - -#ifdef DEBUG_STUN - printf("==> Attribute: %04X\n", attribute); -#endif - - switch(attribute) { - case 0x4000: - case 0x4001: - case 0x4002: - /* These are the only messages apparently whatsapp voice can use */ - flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; - return NDPI_IS_STUN; - break; - - case 0x0014: /* Realm */ - { - u_int16_t realm_len = ntohs(*((u_int16_t*)&payload[offset+2])); - - if(flow->host_server_name[0] == '\0') { - u_int j, i = (realm_len > sizeof(flow->host_server_name)) ? sizeof(flow->host_server_name) : realm_len; - u_int k = offset+4; - - memset(flow->host_server_name, 0, sizeof(flow->host_server_name)); - - for(j=0; jhost_server_name[j] = payload[k++]; - -#ifdef DEBUG_STUN - printf("==> [%s]\n", flow->host_server_name); -#endif - - if (strstr((char*) flow->host_server_name, "google.com") != NULL) { - flow->guessed_host_protocol_id = NDPI_PROTOCOL_HANGOUT_DUO; - return NDPI_IS_STUN; - } else if (strstr((char*) flow->host_server_name, "whispersystems.org") != NULL) { - flow->guessed_host_protocol_id = NDPI_PROTOCOL_SIGNAL; - return NDPI_IS_STUN; - } - } - } - break; - - case 0xC057: /* Messeger */ - if (msg_type == 0x0001) { - if ((msg_len == 100) || (msg_len == 104)) { - flow->guessed_host_protocol_id = NDPI_PROTOCOL_MESSENGER; - return NDPI_IS_STUN; - } else if(msg_len == 76) { -#if 0 - if(1) { - flow->guessed_host_protocol_id = NDPI_PROTOCOL_HANGOUT_DUO; - return NDPI_IS_NOT_STUN; /* This case is found also with signal traffic */ - } else - return NDPI_IS_STUN; -#endif - } - } - break; - - case 0x8054: /* Candidate Identifier */ - if((len == 4) - && ((offset+7) < payload_length) - && (payload[offset+5] == 0x00) - && (payload[offset+6] == 0x00) - && (payload[offset+7] == 0x00)) { - /* Either skype for business or "normal" skype with multiparty call */ -#ifdef DEBUG_STUN - printf("==> Skype found\n"); -#endif - flow->guessed_host_protocol_id = NDPI_PROTOCOL_SKYPE_CALL; - return NDPI_IS_STUN; - } - - break; - - case 0x8055: /* MS Service Quality (skype?) */ - break; - - /* Proprietary fields found on skype calls */ - case 0x24DF: - case 0x3802: - case 0x8036: - case 0x8095: - case 0x0800: - case 0x8006: /* This is found on skype calls) */ - /* printf("====>>>> %04X\n", attribute); */ -#ifdef DEBUG_STUN - printf("==> Skype (2) found\n"); -#endif - - flow->guessed_host_protocol_id = NDPI_PROTOCOL_SKYPE_CALL; - return NDPI_IS_STUN; - break; - - case 0x8070: /* Implementation Version */ - if (len == 4 && ((offset+7) < payload_length) - && (payload[offset+4] == 0x00) && (payload[offset+5] == 0x00) && (payload[offset+6] == 0x00) && - ((payload[offset+7] == 0x02) || (payload[offset+7] == 0x03))) { -#ifdef DEBUG_STUN - printf("==> Skype (3) found\n"); -#endif - - flow->guessed_host_protocol_id = NDPI_PROTOCOL_SKYPE_CALL; - return NDPI_IS_STUN; - } - break; - - case 0xFF03: - flow->guessed_host_protocol_id = NDPI_PROTOCOL_HANGOUT_DUO; - return NDPI_IS_STUN; - break; - - default: -#ifdef DEBUG_STUN - printf("==> %04X\n", attribute); -#endif - break; - } - - offset += len + 4; - } - - goto udp_stun_found; - } else if(msg_type == 0x0800) { - flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; - return NDPI_IS_STUN; - } - } - - if ((flow->protos.stun_ssl.stun.num_udp_pkts > 0) && (msg_type <= 0x00FF)) { - flow->guessed_host_protocol_id = NDPI_PROTOCOL_WHATSAPP_CALL; - return NDPI_IS_STUN; - } else - return NDPI_IS_NOT_STUN; - -udp_stun_found: - flow->protos.stun_ssl.stun.num_processed_pkts++; - - struct ndpi_packet_struct *packet = &flow->packet; - -#ifdef DEBUG_STUN - printf("==>> NDPI_PROTOCOL_WHATSAPP_CALL\n"); -#endif - - if ((ntohs(packet->udp->source) == 3478) || (ntohs(packet->udp->dest) == 3478)) { - flow->guessed_host_protocol_id = (is_messenger_ip_address(ntohl(packet->iph->saddr)) || - is_messenger_ip_address(ntohl(packet->iph->daddr))) ? NDPI_PROTOCOL_MESSENGER : - NDPI_PROTOCOL_WHATSAPP_CALL; - } else - flow->guessed_host_protocol_id = (is_google_ip_address(ntohl(packet->iph->saddr)) || - is_google_ip_address(ntohl(packet->iph->daddr))) ? NDPI_PROTOCOL_HANGOUT_DUO : NDPI_PROTOCOL_WHATSAPP_CALL; - - int rc = (flow->protos.stun_ssl.stun.num_udp_pkts < MAX_NUM_STUN_PKTS) ? NDPI_IS_NOT_STUN : NDPI_IS_STUN; - - return rc; -} - - -void ndpi_search_stun(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) -{ - struct ndpi_packet_struct *packet = &flow->packet; - - NDPI_LOG_DBG(ndpi_struct, "search stun\n"); - - if(packet->payload == NULL) - return; - else if(packet->iphv6 != NULL) { - NDPI_EXCLUDE_PROTO(ndpi_struct, flow); - return; - } - - if(packet->tcp) { - /* STUN may be encapsulated in TCP packets */ - if((packet->payload_packet_len >= 22) - && ((ntohs(get_u_int16_t(packet->payload, 0)) + 2) == packet->payload_packet_len)) { - /* TODO there could be several STUN packets in a single TCP packet so maybe the detection could be - * improved by checking only the STUN packet of given length */ - - if(ndpi_int_check_stun(ndpi_struct, flow, packet->payload + 2, - packet->payload_packet_len - 2) == NDPI_IS_STUN) { - goto udp_stun_match; - } - } - } - - /* UDP */ - if(ndpi_int_check_stun(ndpi_struct, flow, packet->payload, - packet->payload_packet_len) == NDPI_IS_STUN) { - udp_stun_match: - if (flow->guessed_protocol_id == NDPI_PROTOCOL_UNKNOWN) - flow->guessed_protocol_id = NDPI_PROTOCOL_STUN; - - if(flow->guessed_host_protocol_id == NDPI_PROTOCOL_UNKNOWN) { - flow->guessed_host_protocol_id = flow->guessed_protocol_id; - flow->guessed_protocol_id = NDPI_PROTOCOL_STUN; - } - - ndpi_int_stun_add_connection(ndpi_struct, flow, - flow->guessed_protocol_id, - flow->guessed_host_protocol_id); - return; - } - - if(flow->protos.stun_ssl.stun.num_udp_pkts >= MAX_NUM_STUN_PKTS) - NDPI_EXCLUDE_PROTO(ndpi_struct, flow); - - if(flow->packet_counter > 0) { - /* This might be a RTP stream: let's make sure we check it */ - NDPI_CLR(&flow->excluded_protocol_bitmask, NDPI_PROTOCOL_RTP); - } -} - - -void init_stun_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id, - NDPI_PROTOCOL_BITMASK *detection_bitmask) { - ndpi_set_bitmask_protocol_detection("STUN", ndpi_struct, detection_bitmask, *id, - NDPI_PROTOCOL_STUN, - ndpi_search_stun, - NDPI_SELECTION_BITMASK_PROTOCOL_TCP_OR_UDP_WITH_PAYLOAD, - SAVE_DETECTION_BITMASK_AS_UNKNOWN, - ADD_TO_DETECTION_BITMASK); - - *id += 1; -} -- cgit v1.2.3