diff options
-rw-r--r-- | src/include/ndpi_private.h | 14 | ||||
-rw-r--r-- | src/include/ndpi_typedefs.h | 19 | ||||
-rw-r--r-- | src/lib/ndpi_main.c | 96 | ||||
-rw-r--r-- | src/lib/ndpi_utils.c | 70 | ||||
-rw-r--r-- | src/lib/protocols/ssdp.c | 147 | ||||
-rw-r--r-- | tests/cfgs/default/pcap/ssdp.pcapng | bin | 0 -> 1260 bytes | |||
-rw-r--r-- | tests/cfgs/default/result/ssdp.pcapng.out | 27 |
7 files changed, 363 insertions, 10 deletions
diff --git a/src/include/ndpi_private.h b/src/include/ndpi_private.h index c96b64d93..accafdcd2 100644 --- a/src/include/ndpi_private.h +++ b/src/include/ndpi_private.h @@ -126,6 +126,20 @@ struct ndpi_packet_struct { struct ndpi_int_one_line_struct upgrade_line; struct ndpi_int_one_line_struct http_response; /* the first "word" in this pointer is the response code in the packet (200, etc) */ + struct ndpi_int_one_line_struct bootid; + struct ndpi_int_one_line_struct usn; + struct ndpi_int_one_line_struct cache_controle; + struct ndpi_int_one_line_struct location; + struct ndpi_int_one_line_struct household_smart_speaker_audio; + struct ndpi_int_one_line_struct rincon_household; + struct ndpi_int_one_line_struct rincon_bootseq; + struct ndpi_int_one_line_struct rincon_wifimode; + struct ndpi_int_one_line_struct rincon_variant; + struct ndpi_int_one_line_struct sonos_securelocation; + struct ndpi_int_one_line_struct securelocation_upnp; + struct ndpi_int_one_line_struct location_smart_speaker_audio; + struct ndpi_int_one_line_struct nt; + struct ndpi_int_one_line_struct nts; u_int16_t l3_packet_len; u_int16_t payload_packet_len; diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h index c1c57bb6f..30b208441 100644 --- a/src/include/ndpi_typedefs.h +++ b/src/include/ndpi_typedefs.h @@ -1577,6 +1577,25 @@ struct ndpi_flow_struct { u_int32_t ipv4_addr, uptime; struct ndpi_in6_addr ipv6_addr; } mikrotik; + + struct { + char *method; + char *bootid; + char *usn; + char *cache_controle; + char *location; + char *household_smart_speaker_audio; + char *rincon_household; + char *rincon_bootseq; + char *rincon_wifimode; + char *rincon_variant; + char *sonos_securelocation; + char *securelocation_upnp; + char *location_smart_speaker_audio; + char *nt; + char *nts; + char *server; + } ssdp; } protos; /* **Packet** metadata for flows where monitoring is enabled. It is reset after each packet! */ diff --git a/src/lib/ndpi_main.c b/src/lib/ndpi_main.c index b3e7b264e..8dba3f920 100644 --- a/src/lib/ndpi_main.c +++ b/src/lib/ndpi_main.c @@ -6858,6 +6858,56 @@ void ndpi_free_flow_data(struct ndpi_flow_struct* flow) { ndpi_free(flow->protos.sip.to); } + if (flow_is_proto(flow, NDPI_PROTOCOL_SSDP)) { + if(flow->protos.ssdp.bootid) + ndpi_free(flow->protos.ssdp.bootid); + + if(flow->protos.ssdp.usn) + ndpi_free(flow->protos.ssdp.usn); + + if(flow->protos.ssdp.cache_controle) + ndpi_free(flow->protos.ssdp.cache_controle); + + if(flow->protos.ssdp.location) + ndpi_free(flow->protos.ssdp.location); + + if(flow->protos.ssdp.household_smart_speaker_audio) + ndpi_free(flow->protos.ssdp.household_smart_speaker_audio); + + if(flow->protos.ssdp.rincon_household) + ndpi_free(flow->protos.ssdp.rincon_household); + + if(flow->protos.ssdp.rincon_bootseq) + ndpi_free(flow->protos.ssdp.rincon_bootseq); + + if(flow->protos.ssdp.rincon_wifimode) + ndpi_free(flow->protos.ssdp.rincon_wifimode); + + if(flow->protos.ssdp.rincon_variant) + ndpi_free(flow->protos.ssdp.rincon_variant); + + if(flow->protos.ssdp.sonos_securelocation) + ndpi_free(flow->protos.ssdp.sonos_securelocation); + + if(flow->protos.ssdp.securelocation_upnp) + ndpi_free(flow->protos.ssdp.securelocation_upnp); + + if(flow->protos.ssdp.location_smart_speaker_audio) + ndpi_free(flow->protos.ssdp.location_smart_speaker_audio); + + if(flow->protos.ssdp.nt) + ndpi_free(flow->protos.ssdp.nt); + + if(flow->protos.ssdp.nts) + ndpi_free(flow->protos.ssdp.nts); + + if(flow->protos.ssdp.server) + ndpi_free(flow->protos.ssdp.server); + + if(flow->protos.ssdp.method) + ndpi_free(flow->protos.ssdp.method); + } + if(flow->tls_quic.message[0].buffer) ndpi_free(flow->tls_quic.message[0].buffer); if(flow->tls_quic.message[1].buffer) @@ -8485,7 +8535,21 @@ static void ndpi_reset_packet_line_info(struct ndpi_packet_struct *packet) { packet->server_line.len = 0, packet->http_method.ptr = NULL, packet->http_method.len = 0, packet->http_response.ptr = NULL, packet->http_response.len = 0, packet->forwarded_line.ptr = NULL, packet->forwarded_line.len = 0; - packet->upgrade_line.ptr = NULL, packet->upgrade_line.len = 0; + packet->upgrade_line.ptr = NULL, packet->upgrade_line.len = 0; + packet->bootid.ptr = NULL, packet->bootid.len = 0; + packet->usn.ptr = NULL, packet->usn.len = 0; + packet->cache_controle.ptr = NULL, packet->cache_controle.len = 0; + packet->location.ptr = NULL, packet->location.len = 0; + packet->household_smart_speaker_audio.ptr = NULL, packet->household_smart_speaker_audio.len = 0; + packet->rincon_household.ptr = NULL, packet->rincon_household.len = 0; + packet->rincon_bootseq.ptr = NULL, packet->rincon_bootseq.len = 0; + packet->rincon_wifimode.ptr = NULL, packet->rincon_wifimode.len = 0; + packet->rincon_variant.ptr = NULL, packet->rincon_variant.len = 0; + packet->sonos_securelocation.ptr = NULL, packet->sonos_securelocation.len = 0; + packet->securelocation_upnp.ptr = NULL, packet->securelocation_upnp.len = 0; + packet->location_smart_speaker_audio.ptr = NULL, packet->location_smart_speaker_audio.len = 0; + packet->nt.ptr = NULL, packet->nt.len = 0; + packet->nts.ptr = NULL, packet->nts.len = 0; } /* ********************************************************************************* */ @@ -9229,23 +9293,39 @@ static void parse_single_packet_line(struct ndpi_detection_module_struct *ndpi_s struct header_line headers_a[] = { { "Accept:", &packet->accept_line }, { "Authorization:", &packet->authorization_line }, { NULL, NULL} }; + struct header_line headers_b[] = { { "BOOTID.UPNP.ORG:", &packet->bootid}, + { NULL, NULL} }; struct header_line headers_u[] = { { "User-agent:", &packet->user_agent_line }, { "Upgrade:", &packet->upgrade_line }, + { "USN:", &packet->usn }, { NULL, NULL} }; struct header_line headers_c[] = { { "Content-Disposition:", &packet->content_disposition_line }, { "Content-type:", &packet->content_line }, + { "CACHE-CONTROL:", &packet->cache_controle}, { NULL, NULL} }; struct header_line headers_o[] = { { "Origin:", &packet->http_origin }, { NULL, NULL} }; struct header_line headers_h[] = { { "Host:", &packet->host_line }, + { "HOUSEHOLD.SMARTSPEAKER.AUDIO:", &packet->household_smart_speaker_audio }, { NULL, NULL} }; struct header_line headers_x[] = { { "X-Forwarded-For:", &packet->forwarded_line }, + { "X-RINCON-HOUSEHOLD:", &packet->rincon_household }, + { "X-RINCON-BOOTSEQ:", &packet->rincon_bootseq }, + { "X-RINCON-WIFIMODE:", &packet->rincon_wifimode }, + { "X-RINCON-VARIANT:", &packet->rincon_variant }, + { "X-SONOS-HHSECURELOCATION:", &packet->sonos_securelocation }, { NULL, NULL} }; struct header_line headers_r[] = { { "Referer:", &packet->referer_line }, { NULL, NULL} }; struct header_line headers_s[] = { { "Server:", &packet->server_line }, + { "SECURELOCATION.UPNP.ORG:", &packet->securelocation_upnp }, { NULL, NULL} }; - + struct header_line headers_l[] = { { "LOCATION:", &packet->location }, + { "LOCATION.SMARTSPEAKER.AUDIO:", &packet->location_smart_speaker_audio }, + { NULL, NULL}}; + struct header_line headers_n[] = { { "NT:", &packet->nt }, + { "NTS:", &packet->nts }, + { NULL, NULL}}; line = &packet->line[packet->parsed_lines]; if(line->len == 0) @@ -9274,6 +9354,10 @@ static void parse_single_packet_line(struct ndpi_detection_module_struct *ndpi_s case 'A': hs = headers_a; break; + case 'b': + case 'B': + hs = headers_b; + break; case 'c': case 'C': hs = headers_c; @@ -9302,6 +9386,14 @@ static void parse_single_packet_line(struct ndpi_detection_module_struct *ndpi_s case 'X': hs = headers_x; break; + case 'l': + case 'L': + hs = headers_l; + break; + case 'n': + case 'N': + hs = headers_n; + break; default: return; } diff --git a/src/lib/ndpi_utils.c b/src/lib/ndpi_utils.c index 64c6c2d6d..0a3babe3f 100644 --- a/src/lib/ndpi_utils.c +++ b/src/lib/ndpi_utils.c @@ -1592,6 +1592,76 @@ int ndpi_dpi2json(struct ndpi_detection_module_struct *ndpi_struct, } break; + case NDPI_PROTOCOL_SSDP: + ndpi_serialize_start_of_block(serializer, "ssdp"); + + if (flow->protos.ssdp.method) { + ndpi_serialize_string_string(serializer, "METHOD", flow->protos.ssdp.method); + } + + if (flow->protos.ssdp.cache_controle) { + ndpi_serialize_string_string(serializer, "CACHE-CONTROL", flow->protos.ssdp.cache_controle); + } + + if (flow->protos.ssdp.location) { + ndpi_serialize_string_string(serializer, "LOCATION", flow->protos.ssdp.location); + } + + if (flow->protos.ssdp.nt) { + ndpi_serialize_string_string(serializer, "NT", flow->protos.ssdp.nt); + } + + if (flow->protos.ssdp.nts) { + ndpi_serialize_string_string(serializer, "NTS", flow->protos.ssdp.nts); + } + + if (flow->protos.ssdp.server) { + ndpi_serialize_string_string(serializer, "SERVER", flow->protos.ssdp.server); + } + + if (flow->protos.ssdp.usn) { + ndpi_serialize_string_string(serializer, "USN", flow->protos.ssdp.usn); + } + + if (flow->protos.ssdp.rincon_household) { + ndpi_serialize_string_string(serializer, "X-RINCON-HOUSEHOLD", flow->protos.ssdp.rincon_household); + } + + if (flow->protos.ssdp.rincon_bootseq) { + ndpi_serialize_string_string(serializer, "X-RINCON-BOOTSEQ", flow->protos.ssdp.rincon_bootseq); + } + + if (flow->protos.ssdp.bootid) { + ndpi_serialize_string_string(serializer, "BOOTID.UPNP.ORG", flow->protos.ssdp.bootid); + } + + if (flow->protos.ssdp.rincon_wifimode) { + ndpi_serialize_string_string(serializer, "X-RINCON-WIFIMODE", flow->protos.ssdp.rincon_wifimode); + } + + if (flow->protos.ssdp.rincon_variant) { + ndpi_serialize_string_string(serializer, "X-RINCON-VARIANT", flow->protos.ssdp.rincon_variant); + } + + if (flow->protos.ssdp.household_smart_speaker_audio) { + ndpi_serialize_string_string(serializer, "HOUSEHOLD.SMARTSPEAKER.AUDIO", flow->protos.ssdp.household_smart_speaker_audio); + } + + if (flow->protos.ssdp.location_smart_speaker_audio) { + ndpi_serialize_string_string(serializer, "LOCATION.SMARTSPEAKER.AUDIO", flow->protos.ssdp.location_smart_speaker_audio); + } + + if (flow->protos.ssdp.securelocation_upnp) { + ndpi_serialize_string_string(serializer, "SECURELOCATION.UPNP.ORG", flow->protos.ssdp.securelocation_upnp); + } + + if (flow->protos.ssdp.sonos_securelocation) { + ndpi_serialize_string_string(serializer, "X-SONOS-HHSECURELOCATION", flow->protos.ssdp.sonos_securelocation); + } + + ndpi_serialize_end_of_block(serializer); + break; + case NDPI_PROTOCOL_DISCORD: if (l7_protocol.proto.master_protocol != NDPI_PROTOCOL_TLS) { ndpi_serialize_start_of_block(serializer, "discord"); diff --git a/src/lib/protocols/ssdp.c b/src/lib/protocols/ssdp.c index 949ff8bb3..acaf14e47 100644 --- a/src/lib/protocols/ssdp.c +++ b/src/lib/protocols/ssdp.c @@ -29,6 +29,13 @@ #include "ndpi_api.h" #include "ndpi_private.h" +static struct SSDP { + const char *detection_line; + const char *method; +} SSDP_METHODS[] = { + { "M-SEARCH * HTTP/1.1", "M-SEARCH" }, + { "NOTIFY * HTTP/1.1", "NOTIFY" } +}; static void ssdp_parse_lines(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) @@ -49,6 +56,126 @@ static void ssdp_parse_lines(struct ndpi_detection_module_struct if (packet->host_line.ptr != NULL && packet->host_line.len > 0) { ndpi_hostname_sni_set(flow, packet->host_line.ptr, packet->host_line.len, NDPI_HOSTNAME_NORM_ALL); } + + if (packet->bootid.ptr != NULL && packet->bootid.len > 0) { + flow->protos.ssdp.bootid = ndpi_malloc(packet->bootid.len + 1); + if (flow->protos.ssdp.bootid) { + memcpy(flow->protos.ssdp.bootid, packet->bootid.ptr, packet->bootid.len); + flow->protos.ssdp.bootid[packet->bootid.len] = '\0'; + } + } + + if (packet->usn.ptr != NULL && packet->usn.len > 0) { + flow->protos.ssdp.usn = ndpi_malloc(packet->usn.len + 1); + if (flow->protos.ssdp.usn) { + memcpy(flow->protos.ssdp.usn, packet->usn.ptr, packet->usn.len); + flow->protos.ssdp.usn[packet->usn.len] = '\0'; + } + } + + if (packet->cache_controle.ptr != NULL && packet->cache_controle.len > 0) { + flow->protos.ssdp.cache_controle = ndpi_malloc(packet->cache_controle.len + 1); + if (flow->protos.ssdp.cache_controle) { + memcpy(flow->protos.ssdp.cache_controle, packet->cache_controle.ptr, packet->cache_controle.len); + flow->protos.ssdp.cache_controle[packet->cache_controle.len] = '\0'; + } + } + + if (packet->location.ptr != NULL && packet->location.len > 0) { + flow->protos.ssdp.location = ndpi_malloc(packet->location.len + 1); + if (flow->protos.ssdp.location) { + memcpy(flow->protos.ssdp.location, packet->location.ptr, packet->location.len); + flow->protos.ssdp.location[packet->location.len] = '\0'; + } + } + + if (packet->household_smart_speaker_audio.ptr != NULL && packet->household_smart_speaker_audio.len > 0) { + flow->protos.ssdp.household_smart_speaker_audio = ndpi_malloc(packet->household_smart_speaker_audio.len + 1); + if (flow->protos.ssdp.household_smart_speaker_audio) { + memcpy(flow->protos.ssdp.household_smart_speaker_audio, packet->household_smart_speaker_audio.ptr, packet->household_smart_speaker_audio.len); + flow->protos.ssdp.household_smart_speaker_audio[packet->household_smart_speaker_audio.len] = '\0'; + } + } + + if (packet->rincon_household.ptr != NULL && packet->rincon_household.len > 0) { + flow->protos.ssdp.rincon_household = ndpi_malloc(packet->rincon_household.len + 1); + if (flow->protos.ssdp.rincon_household) { + memcpy(flow->protos.ssdp.rincon_household, packet->rincon_household.ptr, packet->rincon_household.len); + flow->protos.ssdp.rincon_household[packet->rincon_household.len] = '\0'; + } + } + + if (packet->rincon_bootseq.ptr != NULL && packet->rincon_bootseq.len > 0) { + flow->protos.ssdp.rincon_bootseq = ndpi_malloc(packet->rincon_bootseq.len + 1); + if (flow->protos.ssdp.rincon_bootseq) { + memcpy(flow->protos.ssdp.rincon_bootseq, packet->rincon_bootseq.ptr, packet->rincon_bootseq.len); + flow->protos.ssdp.rincon_bootseq[packet->rincon_bootseq.len] = '\0'; + } + } + + if (packet->rincon_wifimode.ptr != NULL && packet->rincon_wifimode.len > 0) { + flow->protos.ssdp.rincon_wifimode = ndpi_malloc(packet->rincon_wifimode.len + 1); + if (flow->protos.ssdp.rincon_wifimode) { + memcpy(flow->protos.ssdp.rincon_wifimode, packet->rincon_wifimode.ptr, packet->rincon_wifimode.len); + flow->protos.ssdp.rincon_wifimode[packet->rincon_wifimode.len] = '\0'; + } + } + + if (packet->rincon_variant.ptr != NULL && packet->rincon_variant.len > 0) { + flow->protos.ssdp.rincon_variant = ndpi_malloc(packet->rincon_variant.len + 1); + if (flow->protos.ssdp.rincon_variant) { + memcpy(flow->protos.ssdp.rincon_variant, packet->rincon_variant.ptr, packet->rincon_variant.len); + flow->protos.ssdp.rincon_variant[packet->rincon_variant.len] = '\0'; + } + } + + if (packet->sonos_securelocation.ptr != NULL && packet->sonos_securelocation.len > 0) { + flow->protos.ssdp.sonos_securelocation = ndpi_malloc(packet->sonos_securelocation.len + 1); + if (flow->protos.ssdp.sonos_securelocation) { + memcpy(flow->protos.ssdp.sonos_securelocation, packet->sonos_securelocation.ptr, packet->sonos_securelocation.len); + flow->protos.ssdp.sonos_securelocation[packet->sonos_securelocation.len] = '\0'; + } + } + + if (packet->securelocation_upnp.ptr != NULL && packet->securelocation_upnp.len > 0) { + flow->protos.ssdp.securelocation_upnp = ndpi_malloc(packet->securelocation_upnp.len + 1); + if (flow->protos.ssdp.securelocation_upnp) { + memcpy(flow->protos.ssdp.securelocation_upnp, packet->securelocation_upnp.ptr, packet->securelocation_upnp.len); + flow->protos.ssdp.securelocation_upnp[packet->securelocation_upnp.len] = '\0'; + } + } + + if (packet->location_smart_speaker_audio.ptr != NULL && packet->location_smart_speaker_audio.len > 0) { + flow->protos.ssdp.location_smart_speaker_audio = ndpi_malloc(packet->location_smart_speaker_audio.len + 1); + if (flow->protos.ssdp.location_smart_speaker_audio) { + memcpy(flow->protos.ssdp.location_smart_speaker_audio, packet->location_smart_speaker_audio.ptr, packet->location_smart_speaker_audio.len); + flow->protos.ssdp.location_smart_speaker_audio[packet->location_smart_speaker_audio.len] = '\0'; + } + } + + if (packet->nt.ptr != NULL && packet->nt.len > 0) { + flow->protos.ssdp.nt = ndpi_malloc(packet->nt.len + 1); + if (flow->protos.ssdp.nt) { + memcpy(flow->protos.ssdp.nt, packet->nt.ptr, packet->nt.len); + flow->protos.ssdp.nt[packet->nt.len] = '\0'; + } + } + + if (packet->nts.ptr != NULL && packet->nts.len > 0) { + flow->protos.ssdp.nts = ndpi_malloc(packet->nts.len + 1); + if (flow->protos.ssdp.nts) { + memcpy(flow->protos.ssdp.nts, packet->nts.ptr, packet->nts.len); + flow->protos.ssdp.nts[packet->nts.len] = '\0'; + } + } + + if (packet->server_line.ptr != NULL && packet->server_line.len > 0) { + flow->protos.ssdp.server = ndpi_malloc(packet->server_line.len + 1); + if (flow->protos.ssdp.server) { + memcpy(flow->protos.ssdp.server, packet->server_line.ptr, packet->server_line.len); + flow->protos.ssdp.server[packet->server_line.len] = '\0'; + } + } } static void ndpi_int_ssdp_add_connection(struct ndpi_detection_module_struct @@ -67,14 +194,18 @@ static void ndpi_search_ssdp(struct ndpi_detection_module_struct *ndpi_struct, s if (packet->udp != NULL) { if (packet->payload_packet_len >= 19) { - if ((memcmp(packet->payload, "M-SEARCH * HTTP/1.1", 19) == 0) - || memcmp(packet->payload, "NOTIFY * HTTP/1.1", 17) == 0) { - - - NDPI_LOG_INFO(ndpi_struct, "found ssdp\n"); - ndpi_int_ssdp_add_connection(ndpi_struct, flow); - return; - } + for (unsigned int i=0; i < sizeof(SSDP_METHODS)/sizeof(SSDP_METHODS[0]); i++) { + if(memcmp(packet->payload, SSDP_METHODS[i].detection_line, strlen(SSDP_METHODS[i].detection_line)) == 0) { + flow->protos.ssdp.method = ndpi_malloc(strlen(SSDP_METHODS[i].detection_line) + 1); + if (flow->protos.ssdp.method) { + memcpy(flow->protos.ssdp.method, SSDP_METHODS[i].method, strlen(SSDP_METHODS[i].method)); + flow->protos.ssdp.method[strlen(SSDP_METHODS[i].method)] = '\0'; + } + NDPI_LOG_INFO(ndpi_struct, "found ssdp\n"); + ndpi_int_ssdp_add_connection(ndpi_struct, flow); + return; + } + } #define SSDP_HTTP "HTTP/1.1 200 OK\r\n" if(memcmp(packet->payload, SSDP_HTTP, strlen(SSDP_HTTP)) == 0) { diff --git a/tests/cfgs/default/pcap/ssdp.pcapng b/tests/cfgs/default/pcap/ssdp.pcapng Binary files differnew file mode 100644 index 000000000..bdaafb544 --- /dev/null +++ b/tests/cfgs/default/pcap/ssdp.pcapng diff --git a/tests/cfgs/default/result/ssdp.pcapng.out b/tests/cfgs/default/result/ssdp.pcapng.out new file mode 100644 index 000000000..77e8af8c6 --- /dev/null +++ b/tests/cfgs/default/result/ssdp.pcapng.out @@ -0,0 +1,27 @@ +DPI Packets (UDP): 1 (1.00 pkts/flow) +Confidence DPI : 1 (flows) +Num dissector calls: 25 (25.00 diss/flow) +LRU cache ookla: 0/0/0 (insert/search/found) +LRU cache bittorrent: 0/0/0 (insert/search/found) +LRU cache stun: 0/0/0 (insert/search/found) +LRU cache tls_cert: 0/0/0 (insert/search/found) +LRU cache mining: 0/0/0 (insert/search/found) +LRU cache msteams: 0/0/0 (insert/search/found) +LRU cache fpc_dns: 0/0/0 (insert/search/found) +Automa host: 0/0 (search/found) +Automa domain: 0/0 (search/found) +Automa tls cert: 0/0 (search/found) +Automa risk mask: 0/0 (search/found) +Automa common alpns: 0/0 (search/found) +Patricia risk mask: 0/0 (search/found) +Patricia risk mask IPv6: 0/0 (search/found) +Patricia risk: 0/0 (search/found) +Patricia risk IPv6: 0/0 (search/found) +Patricia protocols: 2/0 (search/found) +Patricia protocols IPv6: 0/0 (search/found) + +SSDP 1 848 1 + +Acceptable 1 848 1 + + 1 UDP 192.168.1.173:58006 -> 239.255.255.250:1900 [proto: 12/SSDP][IP: 0/Unknown][ClearText][Confidence: DPI][FPC: 12/SSDP, Confidence: DPI][DPI packets: 1][cat: System/18][1 pkts/848 bytes -> 0 pkts/0 bytes][Goodput ratio: 95/0][< 1 sec][Hostname/SNI: 239.255.255.250:1900][PLAIN TEXT (ENOTIFY )][Plen Bins: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] |