diff options
Diffstat (limited to 'src/lib/protocols')
-rw-r--r-- | src/lib/protocols/dns.c | 101 | ||||
-rw-r--r-- | src/lib/protocols/dnscrypt.c | 69 | ||||
-rw-r--r-- | src/lib/protocols/http.c | 174 | ||||
-rw-r--r-- | src/lib/protocols/kerberos.c | 4 | ||||
-rw-r--r-- | src/lib/protocols/mdns_proto.c | 153 | ||||
-rw-r--r-- | src/lib/protocols/netbios.c | 2 | ||||
-rw-r--r-- | src/lib/protocols/quic.c | 74 | ||||
-rw-r--r-- | src/lib/protocols/teamspeak.c | 68 | ||||
-rw-r--r-- | src/lib/protocols/tls.c | 69 | ||||
-rw-r--r-- | src/lib/protocols/tor.c | 2 |
10 files changed, 400 insertions, 316 deletions
diff --git a/src/lib/protocols/dns.c b/src/lib/protocols/dns.c index 5e6d01d69..12c6d0338 100644 --- a/src/lib/protocols/dns.c +++ b/src/lib/protocols/dns.c @@ -27,15 +27,47 @@ #include "ndpi_api.h" - #define FLAGS_MASK 0x8000 -// #define DNS_DEBUG 1 +/* #define DNS_DEBUG 1 */ + +#define DNS_PORT 53 +#define LLMNR_PORT 5355 +#define MDNS_PORT 5353 static void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow); /* *********************************************** */ +static u_int16_t checkPort(u_int16_t port) { + switch(port) { + case DNS_PORT: + return(NDPI_PROTOCOL_DNS); + break; + case LLMNR_PORT: + return(NDPI_PROTOCOL_LLMNR); + break; + case MDNS_PORT: + return(NDPI_PROTOCOL_MDNS); + break; + } + + return(0); +} + +/* *********************************************** */ + +static u_int16_t checkDNSSubprotocol(u_int16_t sport, u_int16_t dport) { + u_int16_t rc = checkPort(sport); + + if(rc == 0) + return(checkPort(dport)); + else + return(rc); +} + +/* *********************************************** */ + static u_int16_t get16(int *i, const u_int8_t *payload) { u_int16_t v = *(u_int16_t*)&payload[*i]; @@ -108,7 +140,7 @@ static int search_valid_dns(struct ndpi_detection_module_struct *ndpi_struct, NDPI_SET_BIT(flow->risk, NDPI_MALFORMED_PACKET); return(1 /* invalid */); } - + if(*is_query) { /* DNS Request */ if((dns_header->num_queries > 0) && (dns_header->num_queries <= NDPI_MAX_DNS_REQUESTS) @@ -182,7 +214,7 @@ static int search_valid_dns(struct ndpi_detection_module_struct *ndpi_struct, if((x+12) <= flow->packet.payload_packet_len) { x += 6; data_len = get16(&x, flow->packet.payload); - + if((x + data_len) <= flow->packet.payload_packet_len) { // printf("[rsp_type: %u][data_len: %u]\n", rsp_type, data_len); @@ -190,7 +222,7 @@ static int search_valid_dns(struct ndpi_detection_module_struct *ndpi_struct, x += data_len; continue; /* Skip CNAME */ } - + if((((rsp_type == 0x1) && (data_len == 4)) /* A */ #ifdef NDPI_DETECTION_SUPPORT_IPV6 || ((rsp_type == 0x1c) && (data_len == 16)) /* AAAA */ @@ -200,11 +232,11 @@ static int search_valid_dns(struct ndpi_detection_module_struct *ndpi_struct, } } } - + break; } } - + if((flow->packet.detected_protocol_stack[0] == NDPI_PROTOCOL_DNS) || (flow->packet.detected_protocol_stack[1] == NDPI_PROTOCOL_DNS)) { /* Request already set the protocol */ @@ -212,10 +244,8 @@ static int search_valid_dns(struct ndpi_detection_module_struct *ndpi_struct, } else { /* We missed the request */ u_int16_t s_port = flow->packet.udp ? ntohs(flow->packet.udp->source) : ntohs(flow->packet.tcp->source); - - ndpi_set_detected_protocol(ndpi_struct, flow, - (s_port == 5355) ? NDPI_PROTOCOL_LLMNR : NDPI_PROTOCOL_DNS, - NDPI_PROTOCOL_UNKNOWN); + + ndpi_set_detected_protocol(ndpi_struct, flow, checkPort(s_port), NDPI_PROTOCOL_UNKNOWN); } } } @@ -256,15 +286,18 @@ static void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, st return; } - if((s_port == 53 || d_port == 53 || d_port == 5355) + if(((s_port == DNS_PORT) || (d_port == DNS_PORT) + || (s_port == MDNS_PORT) || (d_port == MDNS_PORT) + || (d_port == LLMNR_PORT)) && (flow->packet.payload_packet_len > sizeof(struct ndpi_dns_packet_header)+payload_offset)) { struct ndpi_dns_packet_header dns_header; int j = 0, max_len, off; int invalid = search_valid_dns(ndpi_struct, flow, &dns_header, payload_offset, &is_query); ndpi_protocol ret; + u_int num_queries, idx; - ret.master_protocol = NDPI_PROTOCOL_UNKNOWN; - ret.app_protocol = (d_port == 5355) ? NDPI_PROTOCOL_LLMNR : NDPI_PROTOCOL_DNS; + ret.master_protocol = NDPI_PROTOCOL_UNKNOWN; + ret.app_protocol = (d_port == LLMNR_PORT) ? NDPI_PROTOCOL_LLMNR : ((d_port == MDNS_PORT) ? NDPI_PROTOCOL_MDNS : NDPI_PROTOCOL_DNS); if(invalid) { NDPI_EXCLUDE_PROTO(ndpi_struct, flow); @@ -274,7 +307,35 @@ static void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, st /* extract host name server */ max_len = sizeof(flow->host_server_name)-1; off = sizeof(struct ndpi_dns_packet_header) + payload_offset; - + + /* Before continuing let's dissect the following queries to see if they are valid */ + for(idx=off, num_queries=0; (num_queries < dns_header.num_queries) && (idx < flow->packet.payload_packet_len);) { + u_int8_t name_len = flow->packet.payload[idx]; + +#ifdef DNS_DEBUG + printf("[DNS] [name_len: %u]\n", name_len); +#endif + + if(name_len == 0) { + /* End of query */ + num_queries++; + idx += 5; + continue; + } + + if((name_len+idx) >= flow->packet.payload_packet_len) { + /* Invalid */ +#ifdef DNS_DEBUG + printf("[DNS] Invalid query len [%u >= %u]\n", + (name_len+idx), + flow->packet.payload_packet_len); +#endif + NDPI_SET_BIT(flow->risk, NDPI_MALFORMED_PACKET); + break; + } else + idx += name_len+1; + } + while(j < max_len && off < flow->packet.payload_packet_len && flow->packet.payload[off] != '\0') { uint8_t c, cl = flow->packet.payload[off++]; @@ -288,7 +349,7 @@ static void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, st while(j < max_len && cl != 0) { u_int32_t shift; - + c = flow->packet.payload[off++]; shift = ((u_int32_t) 1) << (c & 0x1f); flow->host_server_name[j++] = tolower((dns_validchar[c >> 5] & shift) ? c : '_'); @@ -297,12 +358,12 @@ static void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, st } flow->host_server_name[j] = '\0'; - + if(j > 0) { ndpi_protocol_match_result ret_match; - ndpi_check_dga_name(ndpi_struct, flow, (char*)flow->host_server_name); - + ndpi_check_dga_name(ndpi_struct, flow, (char*)flow->host_server_name, 1); + ret.app_protocol = ndpi_match_host_subprotocol(ndpi_struct, flow, (char *)flow->host_server_name, strlen((const char*)flow->host_server_name), @@ -313,7 +374,7 @@ static void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, st flow->category = ret_match.protocol_category; if(ret.app_protocol == NDPI_PROTOCOL_UNKNOWN) - ret.master_protocol = (d_port == 5355) ? NDPI_PROTOCOL_LLMNR : NDPI_PROTOCOL_DNS; + ret.master_protocol = checkDNSSubprotocol(s_port, d_port); else ret.master_protocol = NDPI_PROTOCOL_DNS; } diff --git a/src/lib/protocols/dnscrypt.c b/src/lib/protocols/dnscrypt.c new file mode 100644 index 000000000..6c89466f1 --- /dev/null +++ b/src/lib/protocols/dnscrypt.c @@ -0,0 +1,69 @@ +/* + * dnscrypt.c + * + * Copyright (C) 2020 - ntop.org + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +#include "ndpi_protocol_ids.h" + +#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_DNSCRYPT + +#include "ndpi_api.h" + +static void ndpi_int_dnscrypt_add_connection(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow) +{ + ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_DNSCRYPT, NDPI_PROTOCOL_UNKNOWN); +} + +void ndpi_search_dnscrypt(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow) +{ + struct ndpi_packet_struct *packet = &flow->packet; + static char const * const dnscrypt_initial = "2\rdnscrypt"; + + NDPI_LOG_DBG(ndpi_struct, "search dnscrypt\n"); + + if (flow->packet_counter > 2) + { + NDPI_EXCLUDE_PROTO(ndpi_struct, flow); + } + + /* dnscrypt protocol version 1: check magic */ + if (packet->payload_packet_len >= 64 && + strncmp((char*)packet->payload, "r6fnvWj8", strlen("r6fnvWj8")) == 0) + { + ndpi_int_dnscrypt_add_connection(ndpi_struct, flow); + } + /* dnscrypt protocol version 1 and 2: resolver ping */ + if (packet->payload_packet_len > 13 + strlen(dnscrypt_initial) && + strncasecmp((char*)packet->payload + 13, dnscrypt_initial, strlen(dnscrypt_initial)) == 0) + { + ndpi_int_dnscrypt_add_connection(ndpi_struct, flow); + } +} + +void init_dnscrypt_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id, + NDPI_PROTOCOL_BITMASK *detection_bitmask) +{ + ndpi_set_bitmask_protocol_detection( + "DNScrypt", ndpi_struct, detection_bitmask, *id, + NDPI_PROTOCOL_DNSCRYPT, ndpi_search_dnscrypt, NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD, + SAVE_DETECTION_BITMASK_AS_UNKNOWN, ADD_TO_DETECTION_BITMASK); + *id += 1; +} + diff --git a/src/lib/protocols/http.c b/src/lib/protocols/http.c index 0e3e0d413..983a53b1c 100644 --- a/src/lib/protocols/http.c +++ b/src/lib/protocols/http.c @@ -263,21 +263,102 @@ static void ndpi_check_user_agent(struct ndpi_detection_module_struct *ndpi_stru char *ua) { if((!ua) || (ua[0] == '\0')) return; - // printf("***** [%s:%d] ==> '%s'\n", __FILE__, __LINE__, ua); - // printf("***** %u\n", ndpi_check_dga_name(ndpi_struct, NULL, "uclient-fetch]")); - if((strlen(ua) < 4) || (!strncmp(ua, "test", 4)) || (!strncmp(ua, "<?", 2)) || strchr(ua, '{') || strchr(ua, '}') - || ndpi_check_dga_name(ndpi_struct, NULL, ua) + || ndpi_check_dga_name(ndpi_struct, NULL, ua, 0) // || ndpi_match_bigram(ndpi_struct, &ndpi_struct->impossible_bigrams_automa, ua) ) { NDPI_SET_BIT(flow->risk, NDPI_HTTP_SUSPICIOUS_USER_AGENT); } } +int http_process_user_agent(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow, + const u_int8_t *ua_ptr, u_int16_t ua_ptr_len) +{ + /** + Format examples: + Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) .... + Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0 + */ + if(ua_ptr_len > 7) { + char ua[256]; + u_int mlen = ndpi_min(ua_ptr_len, sizeof(ua)-1); + + strncpy(ua, (const char *)ua_ptr, mlen); + ua[mlen] = '\0'; + + if(strncmp(ua, "Mozilla", 7) == 0) { + char *parent = strchr(ua, '('); + + if(parent) { + char *token, *end; + + parent++; + end = strchr(parent, ')'); + if(end) end[0] = '\0'; + + token = strsep(&parent, ";"); + if(token) { + if((strcmp(token, "X11") == 0) + || (strcmp(token, "compatible") == 0) + || (strcmp(token, "Linux") == 0) + || (strcmp(token, "Macintosh") == 0) + ) { + token = strsep(&parent, ";"); + if(token && (token[0] == ' ')) token++; /* Skip space */ + + if(token + && ((strcmp(token, "U") == 0) + || (strncmp(token, "MSIE", 4) == 0))) { + token = strsep(&parent, ";"); + if(token && (token[0] == ' ')) token++; /* Skip space */ + + if(token && (strncmp(token, "Update", 6) == 0)) { + token = strsep(&parent, ";"); + + if(token && (token[0] == ' ')) token++; /* Skip space */ + + if(token && (strncmp(token, "AOL", 3) == 0)) { + + token = strsep(&parent, ";"); + if(token && (token[0] == ' ')) token++; /* Skip space */ + } + } + } + } + + if(token) + setHttpUserAgent(ndpi_struct, flow, token); + } + } + } else if((ua_ptr_len > 14) && (memcmp(ua, "netflix-ios-app", 15) == 0)) { + NDPI_LOG_INFO(ndpi_struct, "found netflix\n"); + ndpi_int_http_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_NETFLIX, NDPI_PROTOCOL_CATEGORY_STREAMING); + return -1; + } + } + + if(flow->http.user_agent == NULL) { + int len = ua_ptr_len + 1; + + flow->http.user_agent = ndpi_malloc(len); + if(flow->http.user_agent) { + strncpy(flow->http.user_agent, (char*)ua_ptr, ua_ptr_len); + flow->http.user_agent[ua_ptr_len] = '\0'; + + ndpi_check_user_agent(ndpi_struct, flow, flow->http.user_agent); + } + } + + NDPI_LOG_DBG2(ndpi_struct, "User Agent Type line found %.*s\n", + ua_ptr_len, ua_ptr); + return 0; +} + /* ************************************************************* */ static void ndpi_check_numeric_ip(struct ndpi_detection_module_struct *ndpi_struct, @@ -311,6 +392,7 @@ static void ndpi_check_http_url(struct ndpi_detection_module_struct *ndpi_struct static void check_content_type_and_change_protocol(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { struct ndpi_packet_struct *packet = &flow->packet; + int ret; ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_HTTP, NDPI_PROTOCOL_UNKNOWN); @@ -345,84 +427,10 @@ static void check_content_type_and_change_protocol(struct ndpi_detection_module_ } if(packet->user_agent_line.ptr != NULL && packet->user_agent_line.len != 0) { - /** - Format examples: - Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) .... - Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0 - */ - if(packet->user_agent_line.len > 7) { - char ua[256]; - u_int mlen = ndpi_min(packet->user_agent_line.len, sizeof(ua)-1); - - strncpy(ua, (const char *)packet->user_agent_line.ptr, mlen); - ua[mlen] = '\0'; - - if(strncmp(ua, "Mozilla", 7) == 0) { - char *parent = strchr(ua, '('); - - if(parent) { - char *token, *end; - - parent++; - end = strchr(parent, ')'); - if(end) end[0] = '\0'; - - token = strsep(&parent, ";"); - if(token) { - if((strcmp(token, "X11") == 0) - || (strcmp(token, "compatible") == 0) - || (strcmp(token, "Linux") == 0) - || (strcmp(token, "Macintosh") == 0) - ) { - token = strsep(&parent, ";"); - if(token && (token[0] == ' ')) token++; /* Skip space */ - - if(token - && ((strcmp(token, "U") == 0) - || (strncmp(token, "MSIE", 4) == 0))) { - token = strsep(&parent, ";"); - if(token && (token[0] == ' ')) token++; /* Skip space */ - - if(token && (strncmp(token, "Update", 6) == 0)) { - token = strsep(&parent, ";"); - - if(token && (token[0] == ' ')) token++; /* Skip space */ - - if(token && (strncmp(token, "AOL", 3) == 0)) { - - token = strsep(&parent, ";"); - if(token && (token[0] == ' ')) token++; /* Skip space */ - } - } - } - } - - if(token) - setHttpUserAgent(ndpi_struct, flow, token); - } - } - } else if((packet->user_agent_line.len > 14) && (memcmp(ua, "netflix-ios-app", 15) == 0)) { - NDPI_LOG_INFO(ndpi_struct, "found netflix\n"); - ndpi_int_http_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_NETFLIX, NDPI_PROTOCOL_CATEGORY_STREAMING); - return; - } - } - - if(flow->http.user_agent == NULL) { - int len = packet->user_agent_line.len + 1; - - flow->http.user_agent = ndpi_malloc(len); - if(flow->http.user_agent) { - strncpy(flow->http.user_agent, (char*)packet->user_agent_line.ptr, - packet->user_agent_line.len); - flow->http.user_agent[packet->user_agent_line.len] = '\0'; - - ndpi_check_user_agent(ndpi_struct, flow, flow->http.user_agent); - } - } - - NDPI_LOG_DBG2(ndpi_struct, "User Agent Type line found %.*s\n", - packet->user_agent_line.len, packet->user_agent_line.ptr); + ret = http_process_user_agent(ndpi_struct, flow, packet->user_agent_line.ptr, packet->user_agent_line.len); + /* TODO: Is it correct to avoid setting ua, host_name,... if we have a (Netflix) subclassification? */ + if(ret != 0) + return; } /* check for host line */ @@ -438,7 +446,7 @@ static void check_content_type_and_change_protocol(struct ndpi_detection_module_ flow->host_server_name[len] = '\0'; flow->extra_packets_func = NULL; /* We're good now */ - if(len > 0) ndpi_check_dga_name(ndpi_struct, flow, (char*)flow->host_server_name); + if(len > 0) ndpi_check_dga_name(ndpi_struct, flow, (char*)flow->host_server_name, 1); flow->server_id = flow->dst; if(packet->forwarded_line.ptr) { diff --git a/src/lib/protocols/kerberos.c b/src/lib/protocols/kerberos.c index 10c2b5a65..fa0ab6cb6 100644 --- a/src/lib/protocols/kerberos.c +++ b/src/lib/protocols/kerberos.c @@ -252,7 +252,9 @@ void ndpi_search_kerberos(struct ndpi_detection_module_struct *ndpi_struct, realm_offset = cname_len + name_offset + 3; /* if cname does not end with a $ then it's a username */ - if(cname_len && cname_str[cname_len-1] == '$') { + if(cname_len + && (cname_len < sizeof(cname_str)) + && (cname_str[cname_len-1] == '$')) { cname_str[cname_len-1] = '\0'; snprintf(flow->protos.kerberos.hostname, sizeof(flow->protos.kerberos.hostname), "%s", cname_str); } else diff --git a/src/lib/protocols/mdns_proto.c b/src/lib/protocols/mdns_proto.c deleted file mode 100644 index 2b75f19ec..000000000 --- a/src/lib/protocols/mdns_proto.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * mdns.c - * - * Copyright (C) 2016-20 - 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 <http://www.gnu.org/licenses/>. - * - */ -#include "ndpi_protocol_ids.h" - -#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_MDNS - -#include "ndpi_api.h" - -#define NDPI_MAX_MDNS_REQUESTS 128 - -PACK_ON -struct mdns_header { - u_int16_t transaction_id, flags, questions, answers, authority_rr, additional_rr; -} PACK_OFF; - -/** - MDNS header is similar to dns header - - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | ID = 0x0000 | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | FLAGS | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | QDCOUNT | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | ANCOUNT | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | NSCOUNT | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | ARCOUNT | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ -*/ - - -static void ndpi_int_mdns_add_connection(struct ndpi_detection_module_struct - *ndpi_struct, struct ndpi_flow_struct *flow) { - ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_MDNS, NDPI_PROTOCOL_UNKNOWN); -} - -static int ndpi_int_check_mdns_payload(struct ndpi_detection_module_struct - *ndpi_struct, struct ndpi_flow_struct *flow) { - struct ndpi_packet_struct *packet = &flow->packet; - struct mdns_header *h = (struct mdns_header*)packet->payload; - u_int16_t questions = ntohs(h->questions), answers = ntohs(h->answers); - - if((questions > NDPI_MAX_MDNS_REQUESTS) - || (answers > NDPI_MAX_MDNS_REQUESTS)) - return(0); - - if((packet->payload[2] & 0x80) == 0) { - NDPI_LOG_INFO(ndpi_struct, "found MDNS with question query\n"); - return 1; - } else if((packet->payload[2] & 0x80) != 0) { - char answer[256]; - int i, j, len; - - for(i=13, j=0; (i < packet->payload_packet_len) && (i < (sizeof(answer)-1)) && (packet->payload[i] != 0); i++) - answer[j++] = (packet->payload[i] < 13) ? '.' : packet->payload[i]; - - answer[j] = '\0'; - - /* printf("==> [%d] %s\n", j, answer); */ - - len = ndpi_min(sizeof(flow->protos.mdns.answer)-1, j); - strncpy(flow->protos.mdns.answer, (const char *)answer, len); - flow->protos.mdns.answer[len] = '\0'; - - NDPI_LOG_INFO(ndpi_struct, "found MDNS with answer query\n"); - return 1; - } - - return 0; -} - -void ndpi_search_mdns(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 MDNS\n"); - - /** - information from http://www.it-administrator.de/lexikon/multicast-dns.html - */ - - /* check if UDP packet */ - if(packet->udp != NULL) { - /* read destination port */ - u_int16_t sport = ntohs(packet->udp->source); - u_int16_t dport = ntohs(packet->udp->dest); - - /* check standard MDNS ON port 5353 */ - if(((dport == 5353) || (sport == 5353)) - && (packet->payload_packet_len >= 12)) { - if(packet->iph != NULL) { - if(ndpi_int_check_mdns_payload(ndpi_struct, flow) == 1) { - ndpi_int_mdns_add_connection(ndpi_struct, flow); - return; - } - } -#ifdef NDPI_DETECTION_SUPPORT_IPV6 - if(packet->iphv6 != NULL) { - u_int32_t daddr_0 = packet->iphv6->ip6_dst.u6_addr.u6_addr32[0]; - - if(daddr_0 == htonl(0xff020000) /* && daddr[1] == 0 && daddr[2] == 0 && daddr[3] == htonl(0xfb) */) { - - NDPI_LOG_INFO(ndpi_struct, "found MDNS with destination address ff02::fb\n"); - - if(ndpi_int_check_mdns_payload(ndpi_struct, flow) == 1) { - ndpi_int_mdns_add_connection(ndpi_struct, flow); - return; - } - } - } -#endif - } - } - - NDPI_EXCLUDE_PROTO(ndpi_struct, flow); -} - - -void init_mdns_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id, NDPI_PROTOCOL_BITMASK *detection_bitmask) -{ - ndpi_set_bitmask_protocol_detection("MDNS", ndpi_struct, detection_bitmask, *id, - NDPI_PROTOCOL_MDNS, - ndpi_search_mdns, - NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_UDP_WITH_PAYLOAD, - SAVE_DETECTION_BITMASK_AS_UNKNOWN, - ADD_TO_DETECTION_BITMASK); - - *id += 1; -} - diff --git a/src/lib/protocols/netbios.c b/src/lib/protocols/netbios.c index 6ca691c7e..1f3850cbd 100644 --- a/src/lib/protocols/netbios.c +++ b/src/lib/protocols/netbios.c @@ -85,7 +85,7 @@ static void ndpi_int_netbios_add_connection(struct ndpi_detection_module_struct flow->packet.payload_packet_len - off, name, sizeof(name)) > 0) { snprintf((char*)flow->host_server_name, sizeof(flow->host_server_name)-1, "%s", name); - ndpi_check_dga_name(ndpi_struct, flow, (char*)flow->host_server_name); + ndpi_check_dga_name(ndpi_struct, flow, (char*)flow->host_server_name, 1); } if(sub_protocol == NDPI_PROTOCOL_UNKNOWN) diff --git a/src/lib/protocols/quic.c b/src/lib/protocols/quic.c index 84ad23799..2a4c7294b 100644 --- a/src/lib/protocols/quic.c +++ b/src/lib/protocols/quic.c @@ -43,7 +43,10 @@ */ extern int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow, int is_quic); + struct ndpi_flow_struct *flow, uint32_t quic_version); +extern int http_process_user_agent(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow, + const u_int8_t *ua_ptr, u_int16_t ua_ptr_len); /* Versions */ #define V_Q024 0x51303234 @@ -141,9 +144,13 @@ static int is_version_with_tls(uint32_t version) return is_version_quic(version) || ((version & 0xFFFFFF00) == 0x54303500) /* T05X */; } +int is_version_with_var_int_transport_params(uint32_t version) +{ + return (is_version_quic(version) && is_quic_ver_greater_than(version, 27)) || + (version == V_T051); +} - -static int quic_len(const uint8_t *buf, uint64_t *value) +int quic_len(const uint8_t *buf, uint64_t *value) { *value = buf[0]; switch((*value) >> 6) { @@ -163,6 +170,22 @@ static int quic_len(const uint8_t *buf, uint64_t *value) return 0; } } +int quic_len_buffer_still_required(uint8_t value) +{ + switch(value >> 6) { + case 0: + return 0; + case 1: + return 1; + case 2: + return 3; + case 3: + return 7; + default: /* No Possible */ + return 0; + } +} + static uint16_t gquic_get_u16(const uint8_t *buf, uint32_t version) { @@ -903,7 +926,9 @@ static const uint8_t *get_crypto_data(struct ndpi_detection_module_struct *ndpi_ if(counter + 2 + offset_len + 2 /*gquic_get_u16 reads 2 bytes */ > clear_payload_len) return NULL; if(clear_payload[counter + 1] != 0x01) { +#ifdef QUIC_DEBUG NDPI_LOG_ERR(ndpi_struct, "Unexpected stream ID version 0x%x\n", version); +#endif return NULL; } counter += 2 + offset_len; @@ -936,7 +961,9 @@ static const uint8_t *get_crypto_data(struct ndpi_detection_module_struct *ndpi_ if(first_nonzero_payload_byte != 0x06) { if(first_nonzero_payload_byte != 0x02 && first_nonzero_payload_byte != 0x1C) { +#ifdef QUIC_DEBUG NDPI_LOG_ERR(ndpi_struct, "Unexpected frame 0x%x\n", first_nonzero_payload_byte); +#endif } else { NDPI_LOG_DBG(ndpi_struct, "Unexpected ACK/CC frame\n"); } @@ -945,8 +972,10 @@ static const uint8_t *get_crypto_data(struct ndpi_detection_module_struct *ndpi_ if(counter + 2 + 8 >= clear_payload_len) /* quic_len reads 8 bytes, at most */ return NULL; if(clear_payload[counter + 1] != 0x00) { +#ifdef QUIC_DEBUG NDPI_LOG_ERR(ndpi_struct, "Unexpected crypto stream offset 0x%x\n", clear_payload[counter + 1]); +#endif return NULL; } counter += 2; @@ -955,8 +984,10 @@ static const uint8_t *get_crypto_data(struct ndpi_detection_module_struct *ndpi_ } if(*crypto_data_len + counter > clear_payload_len) { +#ifdef QUIC_DEBUG NDPI_LOG_ERR(ndpi_struct, "Invalid length %lu + %d > %d version 0x%x\n", (unsigned long)*crypto_data_len, counter, clear_payload_len, version); +#endif return NULL; } return crypto_data; @@ -1015,7 +1046,8 @@ static uint8_t *get_clear_payload(struct ndpi_detection_module_struct *ndpi_stru } static void process_tls(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow, - const u_int8_t *crypto_data, uint32_t crypto_data_len) + const u_int8_t *crypto_data, uint32_t crypto_data_len, + uint32_t version) { struct ndpi_packet_struct *packet = &flow->packet; @@ -1027,7 +1059,7 @@ static void process_tls(struct ndpi_detection_module_struct *ndpi_struct, packet->payload = crypto_data; packet->payload_packet_len = crypto_data_len; - processClientServerHello(ndpi_struct, flow, 1); + processClientServerHello(ndpi_struct, flow, version); /* Restore */ packet->payload = p; @@ -1049,11 +1081,14 @@ static void process_chlo(struct ndpi_detection_module_struct *ndpi_struct, uint32_t prev_offset; uint32_t tag_offset_start, offset, len, sni_len; ndpi_protocol_match_result ret_match; + int sni_found = 0, ua_found = 0; if(crypto_data_len < 6) return; if(memcmp(crypto_data, "CHLO", 4) != 0) { +#ifdef QUIC_DEBUG NDPI_LOG_ERR(ndpi_struct, "Unexpected handshake message"); +#endif return; } num_tags = (*(uint16_t *)&crypto_data[4]); @@ -1086,7 +1121,19 @@ static void process_chlo(struct ndpi_detection_module_struct *ndpi_struct, (char *)flow->host_server_name, strlen((const char*)flow->host_server_name), &ret_match, NDPI_PROTOCOL_QUIC); - return; + sni_found = 1; + if (ua_found) + return; + } + if((memcmp(tag, "UAID", 4) == 0) && + (tag_offset_start + prev_offset + len < crypto_data_len)) { + NDPI_LOG_DBG2(ndpi_struct, "UA: [%.*s]\n", len, &crypto_data[tag_offset_start + prev_offset]); + + http_process_user_agent(ndpi_struct, flow, + &crypto_data[tag_offset_start + prev_offset], len); + ua_found = 1; + if (sni_found) + return; } prev_offset = offset; @@ -1137,13 +1184,16 @@ static int may_be_initial_pkt(struct ndpi_detection_module_struct *ndpi_struct, if(is_gquic_ver_less_than(*version, 43) && (!pub_bit5 || pub_bit3 != 0 || pub_bit4 != 0)) { - NDPI_LOG_ERR(ndpi_struct, "Version 0x%x invalid flags 0x%x\n", - *version, first_byte); +#ifdef QUIC_DEBUG + NDPI_LOG_ERR(ndpi_struct, "Version 0x%x invalid flags 0x%x\n", *version, first_byte); +#endif return 0; } if((*version == V_Q046) && (pub_bit7 != 1 || pub_bit8 != 1)) { +#ifdef QUIC_DEBUG NDPI_LOG_ERR(ndpi_struct, "Q46 invalid flag 0x%x\n", first_byte); +#endif return 0; } if((is_version_quic(*version) || (*version == V_Q046) || (*version == V_Q050)) && @@ -1201,8 +1251,10 @@ void ndpi_search_quic(struct ndpi_detection_module_struct *ndpi_struct, */ if(!is_version_supported(version)) { - NDPI_LOG_ERR(ndpi_struct, "Unsupported version 0x%x\n", version) - NDPI_EXCLUDE_PROTO(ndpi_struct, flow); +#ifdef QUIC_DEBUG + NDPI_LOG_ERR(ndpi_struct, "Unsupported version 0x%x\n", version); +#endif + NDPI_EXCLUDE_PROTO(ndpi_struct, flow); return; } @@ -1235,7 +1287,7 @@ void ndpi_search_quic(struct ndpi_detection_module_struct *ndpi_struct, if(!is_version_with_tls(version)) { process_chlo(ndpi_struct, flow, crypto_data, crypto_data_len); } else { - process_tls(ndpi_struct, flow, crypto_data, crypto_data_len); + process_tls(ndpi_struct, flow, crypto_data, crypto_data_len, version); } if(is_version_with_encrypted_header(version)) { ndpi_free(clear_payload); diff --git a/src/lib/protocols/teamspeak.c b/src/lib/protocols/teamspeak.c index 0fb538e36..a2a1002ff 100644 --- a/src/lib/protocols/teamspeak.c +++ b/src/lib/protocols/teamspeak.c @@ -24,7 +24,7 @@ #include "ndpi_api.h" static void ndpi_int_teamspeak_add_connection(struct ndpi_detection_module_struct - *ndpi_struct, struct ndpi_flow_struct *flow) + *ndpi_struct, struct ndpi_flow_struct *flow) { ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_TEAMSPEAK, NDPI_PROTOCOL_UNKNOWN); } @@ -36,56 +36,38 @@ void ndpi_search_teamspeak(struct ndpi_detection_module_struct *ndpi_struct, str NDPI_LOG_DBG(ndpi_struct, "search teamspeak\n"); - -#ifdef WEAK_DETECTION_CODE_DISABLED - if(packet->udp != NULL) { - u_int16_t udport, usport; - - usport = ntohs(packet->udp->source), udport = ntohs(packet->udp->dest); - - /* http://www.imfirewall.com/en/protocols/teamSpeak.htm */ - if(((usport == 9987 || udport == 9987) || (usport == 8767 || udport == 8767)) && packet->payload_packet_len >= 20) { - NDPI_LOG_INFO(ndpi_struct, "found TEAMSPEAK udp\n"); - ndpi_int_teamspeak_add_connection(ndpi_struct, flow); - } - } - else -#endif - - if(packet->tcp != NULL) { -#if WEAK_DETECTION_CODE_DISABLED - u_int16_t tdport, tsport; - tsport = ntohs(packet->tcp->source), tdport = ntohs(packet->tcp->dest); -#endif - /* https://github.com/Youx/soliloque-server/wiki/Connection-packet */ - if(packet->payload_packet_len >= 20) { - if(((memcmp(packet->payload, "\xf4\xbe\x03\x00", 4) == 0)) || - ((memcmp(packet->payload, "\xf4\xbe\x02\x00", 4) == 0)) || - ((memcmp(packet->payload, "\xf4\xbe\x01\x00", 4) == 0))) { - NDPI_LOG_INFO(ndpi_struct, "found TEAMSPEAK tcp\n"); - ndpi_int_teamspeak_add_connection(ndpi_struct, flow); - } /* http://www.imfirewall.com/en/protocols/teamSpeak.htm */ + if (packet->payload_packet_len >= 20) { + if (packet->udp != NULL) { + if (memcmp(packet->payload, "TS3INIT1", strlen("TS3INIT1")) == 0) + { + NDPI_LOG_INFO(ndpi_struct, "found TEAMSPEAK udp\n"); + ndpi_int_teamspeak_add_connection(ndpi_struct, flow); } -#if WEAK_DETECTION_CODE_DISABLED - else if((tsport == 14534 || tdport == 14534) || (tsport == 51234 || tdport == 51234)) { - NDPI_LOG_INFO(ndpi_struct, "found TEAMSPEAK\n"); - ndpi_int_teamspeak_add_connection(ndpi_struct, flow); - } -#endif + } else if(packet->tcp != NULL) { + /* https://github.com/Youx/soliloque-server/wiki/Connection-packet */ + if(((memcmp(packet->payload, "\xf4\xbe\x03\x00", 4) == 0)) || + ((memcmp(packet->payload, "\xf4\xbe\x02\x00", 4) == 0)) || + ((memcmp(packet->payload, "\xf4\xbe\x01\x00", 4) == 0))) + { + NDPI_LOG_INFO(ndpi_struct, "found TEAMSPEAK tcp\n"); + ndpi_int_teamspeak_add_connection(ndpi_struct, flow); + } /* http://www.imfirewall.com/en/protocols/teamSpeak.htm */ } - + } + NDPI_EXCLUDE_PROTO(ndpi_struct, flow); return; } -void init_teamspeak_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id, NDPI_PROTOCOL_BITMASK *detection_bitmask) +void init_teamspeak_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id, + NDPI_PROTOCOL_BITMASK *detection_bitmask) { ndpi_set_bitmask_protocol_detection("TeamSpeak", ndpi_struct, detection_bitmask, *id, - NDPI_PROTOCOL_TEAMSPEAK, - ndpi_search_teamspeak, - NDPI_SELECTION_BITMASK_PROTOCOL_TCP_OR_UDP_WITH_PAYLOAD, - SAVE_DETECTION_BITMASK_AS_UNKNOWN, - ADD_TO_DETECTION_BITMASK); + NDPI_PROTOCOL_TEAMSPEAK, + ndpi_search_teamspeak, + NDPI_SELECTION_BITMASK_PROTOCOL_TCP_OR_UDP_WITH_PAYLOAD, + SAVE_DETECTION_BITMASK_AS_UNKNOWN, + ADD_TO_DETECTION_BITMASK); *id += 1; } diff --git a/src/lib/protocols/tls.c b/src/lib/protocols/tls.c index aa3836442..134dfe614 100644 --- a/src/lib/protocols/tls.c +++ b/src/lib/protocols/tls.c @@ -31,7 +31,14 @@ extern char *strptime(const char *s, const char *format, struct tm *tm); extern int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow, int is_quic); + struct ndpi_flow_struct *flow, uint32_t quic_version); +extern int http_process_user_agent(struct ndpi_detection_module_struct *ndpi_struct, + struct ndpi_flow_struct *flow, + const u_int8_t *ua_ptr, u_int16_t ua_ptr_len); +/* QUIC/GQUIC stuff */ +extern int quic_len(const uint8_t *buf, uint64_t *value); +extern int quic_len_buffer_still_required(uint8_t value); +extern int is_version_with_var_int_transport_params(uint32_t version); // #define DEBUG_TLS_MEMORY 1 // #define DEBUG_TLS 1 @@ -864,7 +871,7 @@ struct ja3_info { /* **************************************** */ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, - struct ndpi_flow_struct *flow, int is_quic) { + struct ndpi_flow_struct *flow, uint32_t quic_version) { struct ndpi_packet_struct *packet = &flow->packet; struct ja3_info ja3; u_int8_t invalid_ja3 = 0; @@ -876,6 +883,7 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, u_int16_t total_len; u_int8_t handshake_type; char buffer[64] = { '\0' }; + int is_quic = (quic_version != 0); int is_dtls = packet->udp && (!is_quic); #ifdef DEBUG_TLS @@ -1161,7 +1169,8 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, flow->l4.tcp.tls.subprotocol_detected = 1; } - ndpi_check_dga_name(ndpi_struct, flow, flow->protos.stun_ssl.ssl.client_requested_server_name); + ndpi_check_dga_name(ndpi_struct, flow, + flow->protos.stun_ssl.ssl.client_requested_server_name, 1); } else { #ifdef DEBUG_TLS printf("[TLS] Extensions server len too short: %u vs %u\n", @@ -1365,6 +1374,60 @@ int processClientServerHello(struct ndpi_detection_module_struct *ndpi_struct, } } } + } else if(extension_id == 65445 /* QUIC transport parameters */) { + u_int16_t s_offset = offset+extension_offset; + uint16_t final_offset; + int using_var_int = is_version_with_var_int_transport_params(quic_version); + + if(!using_var_int) { + if(s_offset+1 >= total_len) { + final_offset = 0; /* Force skipping extension */ + } else { + u_int16_t seq_len = ntohs(*((u_int16_t*)&packet->payload[s_offset])); + s_offset += 2; + final_offset = MIN(total_len, s_offset + seq_len); + } + } else { + final_offset = MIN(total_len, s_offset + extension_len); + } + + while(s_offset < final_offset) { + u_int64_t param_type, param_len; + + if(!using_var_int) { + if(s_offset+3 >= final_offset) + break; + param_type = ntohs(*((u_int16_t*)&packet->payload[s_offset])); + param_len = ntohs(*((u_int16_t*)&packet->payload[s_offset + 2])); + s_offset += 4; + } else { + if(s_offset >= final_offset || + (s_offset + quic_len_buffer_still_required(packet->payload[s_offset])) >= final_offset) + break; + s_offset += quic_len(&packet->payload[s_offset], ¶m_type); + + if(s_offset >= final_offset || + (s_offset + quic_len_buffer_still_required(packet->payload[s_offset])) >= final_offset) + break; + s_offset += quic_len(&packet->payload[s_offset], ¶m_len); + } + +#ifdef DEBUG_TLS + printf("Client SSL [QUIC TP: Param 0x%x Len %d]\n", (int)param_type, (int)param_len); +#endif + if(s_offset+param_len >= final_offset) + break; + + if(param_type==0x3129) { +#ifdef DEBUG_TLS + printf("UA [%.*s]\n", (int)param_len, &packet->payload[s_offset]); +#endif + http_process_user_agent(ndpi_struct, flow, + &packet->payload[s_offset], param_len); + break; + } + s_offset += param_len; + } } extension_offset += extension_len; /* Move to the next extension */ diff --git a/src/lib/protocols/tor.c b/src/lib/protocols/tor.c index 7318685e7..71172e211 100644 --- a/src/lib/protocols/tor.c +++ b/src/lib/protocols/tor.c @@ -48,7 +48,7 @@ int ndpi_is_tls_tor(struct ndpi_detection_module_struct *ndpi_struct, if((dot = strrchr(dummy, '.')) == NULL) return(0); name = &dot[1]; - if(ndpi_check_dga_name(ndpi_struct, flow, name)) { + if(ndpi_check_dga_name(ndpi_struct, flow, name, 1)) { ndpi_int_tor_add_connection(ndpi_struct, flow); return(1); } else { |