diff --git a/doc/flow_risks.rst b/doc/flow_risks.rst index 078ea7e245e..874579d91f7 100644 --- a/doc/flow_risks.rst +++ b/doc/flow_risks.rst @@ -154,7 +154,7 @@ TLS needs to carry the the `SNI | Min human readeable string match len. Default %u\n" + " -e | Min human readable string match len. Default %u\n" " -q | Quiet mode\n" " -F | Enable flow stats\n" " -t | Dissect GTP/TZSP tunnels\n" @@ -718,7 +718,7 @@ static void help(u_int long_help) { " --cfg=proto,param,value | Configure the specific attribute of this protocol\n" " --dump-fpc-stats | Print FPC statistics\n" , - human_readeable_string_len, + human_readable_string_len, min_pattern_len, max_pattern_len, max_num_packets_per_flow, max_packet_payload_dissection, max_num_reported_top_payloads, max_num_tcp_dissected_pkts, max_num_udp_dissected_pkts); @@ -1132,7 +1132,7 @@ static void parse_parameters(int argc, char **argv) break; case 'e': - human_readeable_string_len = atoi(optarg); + human_readable_string_len = atoi(optarg); break; case 'E': @@ -2233,8 +2233,8 @@ static void printFlow(u_int32_t id, struct ndpi_flow_info *flow, u_int16_t threa if(flow->dhcp_class_ident) fprintf(out, "[DHCP Class Ident: %s]", flow->dhcp_class_ident); - if(flow->has_human_readeable_strings) fprintf(out, "[PLAIN TEXT (%s)]", - flow->human_readeable_string_buffer); + if(flow->has_human_readable_strings) fprintf(out, "[PLAIN TEXT (%s)]", + flow->human_readable_string_buffer); #ifdef DIRECTION_BINS print_bin(out, "Plen c2s", &flow->payload_len_bin_src2dst); @@ -2962,7 +2962,7 @@ static void dump_realtime_protocol(struct ndpi_workflow * workflow, struct ndpi_ fprintf(out, "Detected Realtime protocol %s --> [%s] %s:%d <--> %s:%d app=%s <%s>\n", date, ndpi_get_ip_proto_name(flow->protocol, ip_proto, sizeof(ip_proto)), srcip, ntohs(flow->src_port), dstip, ntohs(flow->dst_port), - app_name, flow->human_readeable_string_buffer); + app_name, flow->human_readable_string_buffer); } } diff --git a/example/reader_util.c b/example/reader_util.c index 8fc8db21ebb..380700ac7ec 100644 --- a/example/reader_util.c +++ b/example/reader_util.c @@ -76,7 +76,7 @@ #include "ndpi_classify.h" extern u_int8_t enable_flow_stats, enable_payload_analyzer; -extern u_int8_t verbose, human_readeable_string_len; +extern u_int8_t verbose, human_readable_string_len; extern u_int8_t max_num_udp_dissected_pkts /* 24 */, max_num_tcp_dissected_pkts /* 80 */; static u_int32_t flow_id = 0; extern FILE *fingerprint_fp; @@ -1754,6 +1754,138 @@ void update_tcp_flags_count(struct ndpi_flow_info* flow, struct ndpi_tcphdr* tcp /* ****************************************************** */ +#define MIN_ENTROPY 2.5 +#define MAX_ENTROPY 4.8 +/** + * @brief Calculates the Shannon entropy of a given string to estimate its randomness. + * + * This function computes the Shannon entropy of the input string by analyzing the + * frequency distribution of its characters. Higher entropy indicates more randomness, + * while lower entropy suggests more predictable or structured content. + * + * The function is useful for detecting potentially random or pseudo-random strings, + * such as passwords, tokens, or hashes. + * + * @param str A null-terminated input string to analyze. + * @return The calculated entropy as a double. Returns 0.0 for empty strings. + * + * @note Typical entropy values range between MIN_ENTROPY (2.5) and MAX_ENTROPY (4.8) + * for short strings. These thresholds can be used to classify the randomness + * level of the string. + */ +static double calculate_entropy(const char *str) { + int freq[256] = {0}; + int len = strlen(str); + if (len == 0) return 0.0; + + for (int i = 0; i < len; i++) + freq[(unsigned char)str[i]]++; + + double entropy = 0.0; + for (int i = 0; i < 256; i++) { + if (freq[i] > 0) { + double p = (double)freq[i] / len; + entropy -= p * log2(p); + } + } + return entropy; +} + +/** + * @brief Checks if a character is valid for inclusion in a readable string. + * + * This function determines whether the given character is acceptable as part of + * a "readable" string based on specific rules: + * + * - Accepts the following characters explicitly: ':', '.', space, '@', and '/'. + * - Rejects characters classified as punctuation (via ndpi_ispunct). + * - Accepts alphanumeric characters (via ndpi_isdigit and ndpi_isalpha). + * + * @param c The character to validate. + * @return int + * - 1 if the character is considered valid. + * - 0 otherwise. + */ +static int readable_string_is_valid_char(char c) { + const char allowed[] = ":. @/"; + + if (strchr(allowed, c)) + return 1; + + if (ndpi_ispunct(c)) + return 0; + + return (ndpi_isdigit(c) || ndpi_isalpha(c)); +} + +/** + * @brief Validates if a string is considered human-readable. + * + * This function applies a set of heuristic rules to determine whether the provided string + * contains meaningful, human-readable content. It filters out strings that are too short, + * contain excessive special characters, or appear to be random or overly repetitive. + * + * Rules applied: + * - Must be at least 3 characters long. + * - Maximum length allowed is 1024 characters. + * - Must contain at least 2 alphabetic characters. + * - More than half the characters must not be special symbols. + * - No 3 or more consecutive non-alphanumeric, non-space characters. + * - Entropy must be within the defined range [MIN_ENTROPY, MAX_ENTROPY]. + * - Each character must be validated by readable_string_is_valid_char(). + * + * @note Ensure that the string is null-terminated before calling this function. + * + * @param str A pointer to the null-terminated string to validate. + * @return true if the string is considered human-readable, false otherwise. + */ +static bool ndpi_filter_readable_string(char *str) { + if (!str || strlen(str) < 3) + return false; + + size_t len = strlen(str); + if (len > 1024) + return false; + + size_t letters = 0, specials = 0; + for (size_t i = 0; i < len; i++) { + if (isalpha(str[i])) letters++; + else specials++; + + if (!readable_string_is_valid_char(str[i])) + return false; + } + + // Reject if too few alphabetic characters + if (letters < 2) + return false; + + // Reject if half or more of the characters are special symbols + if (specials > len / 2) + return false; + + // Reject if there are 3 or more consecutive uncommon symbols + int consecutive_specials = 0; + for (size_t i = 0; i < len; i++) { + if (!isalnum(str[i]) && !isspace(str[i])) { + consecutive_specials++; + if (consecutive_specials >= 3) + return false; + } else { + consecutive_specials = 0; + } + } + + // Reject if entropy is too low (monotonous) or too high (random noise) + double entropy = calculate_entropy(str); + if (entropy < MIN_ENTROPY || entropy > MAX_ENTROPY) + return false; + + return true; +} + +/* ****************************************************** */ + /** Function to process the packet: determine the flow of a packet and try to decode it @@ -1935,28 +2067,55 @@ static struct ndpi_proto packet_processing(struct ndpi_workflow * workflow, memset(&flow->flow_last_pkt_time, '\0', sizeof(flow->flow_last_pkt_time)); } - if((human_readeable_string_len != 0) && (!flow->has_human_readeable_strings)) { + if ((human_readable_string_len != 0) && (!flow->has_human_readable_strings)) { u_int8_t skip = 0; - if(proto == IPPROTO_TCP && - (is_ndpi_proto(flow, NDPI_PROTOCOL_TLS) || - is_ndpi_proto(flow, NDPI_PROTOCOL_SSH))) { - if((flow->src2dst_packets+flow->dst2src_packets) < 10 /* MIN_NUM_ENCRYPT_SKIP_PACKETS */) - skip = 1; /* Skip initial negotiation packets */ + if (proto == IPPROTO_TCP && (is_ndpi_proto(flow, NDPI_PROTOCOL_TLS) || + is_ndpi_proto(flow, NDPI_PROTOCOL_SSH))) { + if ((flow->src2dst_packets+flow->dst2src_packets) < + 10 /* MIN_NUM_ENCRYPT_SKIP_PACKETS */) + skip = 1; /* Skip initial negotiation packets */ } - if((!skip) && ((flow->src2dst_packets+flow->dst2src_packets) < 100)) { - if(ndpi_has_human_readeable_string((char*)packet, header->caplen, - human_readeable_string_len, - flow->human_readeable_string_buffer, - sizeof(flow->human_readeable_string_buffer)) == 1) - flow->has_human_readeable_strings = 1; + if ((!skip) && ((flow->src2dst_packets+flow->dst2src_packets) < 100)) { + ndpi_string_list_t* human_readable_string_list = + ndpi_extract_readable_strings( + (const unsigned char*)packet, + header->caplen, + human_readable_string_len, + 2, + ndpi_filter_readable_string + ); + if (human_readable_string_list) { + if (human_readable_string_list->count > 0 && human_readable_string_list->items[0]) { + if (human_readable_string_list->count > 1 && + human_readable_string_list->items[1] && + strcmp(human_readable_string_list->items[0], + human_readable_string_list->items[1]) != 0) { + snprintf(flow->human_readable_string_buffer, + sizeof(flow->human_readable_string_buffer), + "%s %s", + human_readable_string_list->items[0], + human_readable_string_list->items[1]); + } else { + snprintf(flow->human_readable_string_buffer, + sizeof(flow->human_readable_string_buffer), + "%s", + human_readable_string_list->items[0]); + } + + flow->has_human_readable_strings = 1; + } + + ndpi_string_list_free(human_readable_string_list); + } } } else { - if(proto == IPPROTO_TCP && - (is_ndpi_proto(flow, NDPI_PROTOCOL_TLS) || - is_ndpi_proto(flow, NDPI_PROTOCOL_SSH))) - flow->has_human_readeable_strings = 0; + if (proto == IPPROTO_TCP && + (is_ndpi_proto(flow, NDPI_PROTOCOL_TLS) || + is_ndpi_proto(flow, NDPI_PROTOCOL_SSH))){ + flow->has_human_readable_strings = 0; + } } } else { // flow is NULL workflow->stats.total_discarded_bytes += header->len; diff --git a/example/reader_util.h b/example/reader_util.h index 4b2b2284cc4..acb274e40a0 100644 --- a/example/reader_util.h +++ b/example/reader_util.h @@ -209,8 +209,8 @@ typedef struct ndpi_flow_info { u_int64_t src2dst_bytes, dst2src_bytes; u_int64_t src2dst_goodput_bytes, dst2src_goodput_bytes; u_int32_t src2dst_packets, dst2src_packets; - u_int32_t has_human_readeable_strings; - char human_readeable_string_buffer[32]; + u_int32_t has_human_readable_strings; + char human_readable_string_buffer[128]; char *risk_str; // result only, not used for flow identification diff --git a/fuzz/fuzz_ndpi_reader.c b/fuzz/fuzz_ndpi_reader.c index bf0894a2653..7044ba03078 100644 --- a/fuzz/fuzz_ndpi_reader.c +++ b/fuzz/fuzz_ndpi_reader.c @@ -18,7 +18,7 @@ struct ndpi_global_context *g_ctx; u_int8_t enable_payload_analyzer = 0; u_int8_t enable_flow_stats = 1; -u_int8_t human_readeable_string_len = 5; +u_int8_t human_readable_string_len = 5; u_int8_t max_num_udp_dissected_pkts = 0, max_num_tcp_dissected_pkts = 0; /* Disable limits at application layer */; int malloc_size_stats = 0; FILE *fingerprint_fp = NULL; diff --git a/fuzz/fuzz_readerutils_parseprotolist.cpp b/fuzz/fuzz_readerutils_parseprotolist.cpp index 64b3ec3f1b2..a7e3d578c80 100644 --- a/fuzz/fuzz_readerutils_parseprotolist.cpp +++ b/fuzz/fuzz_readerutils_parseprotolist.cpp @@ -8,7 +8,7 @@ u_int8_t enable_payload_analyzer = 0; u_int8_t enable_flow_stats = 0; -u_int8_t human_readeable_string_len = 5; +u_int8_t human_readable_string_len = 5; u_int8_t max_num_udp_dissected_pkts = 16 /* 8 is enough for most protocols, Signal requires more */, max_num_tcp_dissected_pkts = 80 /* due to telnet */; int malloc_size_stats = 0; FILE *fingerprint_fp = NULL; diff --git a/fuzz/fuzz_readerutils_workflow.cpp b/fuzz/fuzz_readerutils_workflow.cpp index a92877aaac1..325e7c082fc 100644 --- a/fuzz/fuzz_readerutils_workflow.cpp +++ b/fuzz/fuzz_readerutils_workflow.cpp @@ -10,7 +10,7 @@ extern u_int8_t enable_doh_dot_detection; u_int8_t enable_payload_analyzer = 0; u_int8_t enable_flow_stats = 0; -u_int8_t human_readeable_string_len = 5; +u_int8_t human_readable_string_len = 5; u_int8_t max_num_udp_dissected_pkts = 16 /* 8 is enough for most protocols, Signal requires more */, max_num_tcp_dissected_pkts = 80 /* due to telnet */; int malloc_size_stats = 0; FILE *fingerprint_fp = NULL; diff --git a/src/include/ndpi_api.h b/src/include/ndpi_api.h index 60d0ea3e35d..00a275c0636 100644 --- a/src/include/ndpi_api.h +++ b/src/include/ndpi_api.h @@ -1169,9 +1169,25 @@ extern "C" { const char* ndpi_tunnel2str(ndpi_packet_tunnel tt); u_int16_t ndpi_guess_host_protocol_id(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow); - int ndpi_has_human_readeable_string(char *buffer, u_int buffer_size, - u_int8_t min_string_match_len, /* Will return 0 if no string > min_string_match_len have been found */ - char *outbuf, u_int outbuf_len); + + /** + * @struct ndpi_string_list_t + * @brief Represents a dynamic list of strings. + * + * This structure manages a dynamic array of strings, keeping track of the number of + * stored items and the allocated capacity. + */ + typedef struct { + char **items; ///< Array of strings + size_t count; ///< Number of strings currently stored + size_t capacity; ///< Allocated capacity of the list + } ndpi_string_list_t; + + void ndpi_string_list_free(ndpi_string_list_t *list); + + ndpi_string_list_t* ndpi_extract_readable_strings(const unsigned char *buffer, size_t buffer_len, + size_t min_len, size_t list_limit, bool (*filter_func)(char *)); + /* Return a flow info string (summarized). Does only work for DNS/HTTP/TLS/QUIC. */ const char* ndpi_get_flow_info(struct ndpi_flow_struct const * const flow, ndpi_protocol const * const l7_protocol); diff --git a/src/lib/ndpi_readable_string.c b/src/lib/ndpi_readable_string.c new file mode 100644 index 00000000000..e413a203fb2 --- /dev/null +++ b/src/lib/ndpi_readable_string.c @@ -0,0 +1,192 @@ +/* + * ndpi_readable_string.c + * + * Copyright (C) 2025 - ntop.org and contributors + * + * 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 +#include +#include + +#include "ndpi_api.h" +#include "ndpi_replace_printf.h" + +#define MAX_EXTRACTION_SIZE 1024 + +/** + * @brief Creates a new string list with a specified initial capacity. + * + * This function allocates and initializes a ndpi_string_list_t structure, allowing dynamic + * addition of strings. + * + * @param initial_capacity The initial number of elements that can be stored in the list. + * If zero, the default value of 8 is used. + * @return A pointer to the newly allocated ndpi_string_list_t structure, or NULL on failure. + * + * @note The caller is responsible for freeing the allocated memory using `ndpi_string_list_free()` + * or equivalent deallocation logic to prevent memory leaks. + */ +static ndpi_string_list_t *string_list_create(size_t initial_capacity) { + ndpi_string_list_t *list = (ndpi_string_list_t *) ndpi_malloc(sizeof(ndpi_string_list_t)); + if (!list) + return NULL; + + list->capacity = (initial_capacity > 0) ? initial_capacity : 8; + list->count = 0; + list->items = (char **) ndpi_malloc(list->capacity * sizeof(char *)); + + if (!list->items) { + ndpi_free(list); + return NULL; + } + + return list; +} + +/** + * @brief Frees the memory allocated for a string list. + * + * This function releases all allocated memory associated with the given string list, + * including stored strings. + * + * @param list Pointer to the string list to be freed. + */ +void ndpi_string_list_free(ndpi_string_list_t *list) { + if (!list) + return; + + for (size_t i = 0; i < list->count; i++) { + ndpi_free(list->items[i]); + } + ndpi_free(list->items); + ndpi_free(list); +} + +/** + * @brief Adds a string to the string list. + * + * This function appends a copy of the given string to the string list. If needed, + * the list is automatically resized to accommodate more entries. + * + * @param list Pointer to the string list. + * @param str The string to be added. + * @return true if the string was added successfully, false on allocation failure. + */ +static bool string_list_add(ndpi_string_list_t *list, const char *str) { + if (!list || !str) return false; + + if (list->count == list->capacity) { // Resize if needed + size_t new_capacity = list->capacity * 2; + char **new_items = (char **) ndpi_malloc(new_capacity * sizeof(char *)); + if (!new_items) return false; + + // Copy existing data to the new allocated block + memcpy(new_items, list->items, list->count * sizeof(char *)); + ndpi_free(list->items); + + list->items = new_items; + list->capacity = new_capacity; + } + + list->items[list->count] = ndpi_strdup(str); + if (!list->items[list->count]) + return false; + + list->count++; + return true; +} +/** + * @brief Extracts readable strings from a binary buffer. + * + * This function scans a given binary buffer for readable strings based on the minimum + * length and an optional filtering function. Extracted strings are stored in a + * dynamically allocated string list. + * + * @param buffer Pointer to the input buffer. + * @param buffer_len Size of the input buffer. + * @param min_len Minimum length of readable strings to be extracted. + * @param list_limit Maximum number of strings to store in the list. + * @param filter_func A filtering function that determines whether a string should be included. + * @return A pointer to a ndpi_string_list_t containing the extracted strings, or NULL on failure. + */ +ndpi_string_list_t* ndpi_extract_readable_strings(const unsigned char *buffer, size_t buffer_len, size_t min_len, + size_t list_limit, bool (*filter_func)(char *)) { + + if (!buffer || buffer_len == 0) + return NULL; + + // Create a string list with an initial capacity of 5. + ndpi_string_list_t *result = string_list_create(5); + if (!result) + return NULL; + + char temp[MAX_EXTRACTION_SIZE + 1]; + size_t temp_idx = 0; + + // Simple "state machine": we assemble blocks of printable characters + for (size_t i = 0; i < buffer_len; i++) { + unsigned char c = buffer[i]; + + // Check if it is printable ASCII (32..126) or something you want to accept + // Adjust as needed (e.g.: allow Latin accents, UTF-8, etc.) + if (c >= 32 && c < 127) { + if (temp_idx < MAX_EXTRACTION_SIZE) { + temp[temp_idx++] = (char)c; + } + } else { + // Encountered non-printable character -> end of a block + if (temp_idx > 0) { + temp[temp_idx] = '\0'; + // Check minimum size + if (temp_idx >= min_len) { + // If there is a filter, call it here + bool ok = true; + if (filter_func) { + ok = filter_func(temp); + } + if (ok) { + string_list_add(result, temp); + } + } + temp_idx = 0; + } + } + + if (result->count >= list_limit && temp_idx == 0) + break; + } + + // If you end the loop with temp_idx > 0, end the last block + if (temp_idx > 0) { + temp[temp_idx] = '\0'; + if (temp_idx >= min_len) { + bool ok = true; + if (filter_func) { + ok = filter_func(temp); + } + if (ok) { + string_list_add(result, temp); + } + } + temp_idx = 0; + } + + return result; +} diff --git a/src/lib/ndpi_utils.c b/src/lib/ndpi_utils.c index fc171d5e758..98878b77d85 100644 --- a/src/lib/ndpi_utils.c +++ b/src/lib/ndpi_utils.c @@ -670,118 +670,6 @@ const char* ndpi_cipher2str(u_int32_t cipher, char unknown_cipher[8]) { } } -/* ******************************************************************** */ - -static inline int ndpi_is_other_char(char c) { - return((c == '.') - || (c == ' ') - || (c == '@') - || (c == '/') - ); -} - -/* ******************************************************************** */ - -static int _ndpi_is_valid_char(char c) { - if(ndpi_ispunct(c) && (!ndpi_is_other_char(c))) - return(0); - else - return(ndpi_isdigit(c) - || ndpi_isalpha(c) - || ndpi_is_other_char(c)); -} -static char ndpi_is_valid_char_tbl[256],ndpi_is_valid_char_tbl_init=0; - -static void _ndpi_is_valid_char_init(void) { - int c; - for(c=0; c < 256; c++) ndpi_is_valid_char_tbl[c] = _ndpi_is_valid_char(c); - ndpi_is_valid_char_tbl_init = 1; -} -static inline int ndpi_is_valid_char(char c) { - if(!ndpi_is_valid_char_tbl_init) - _ndpi_is_valid_char_init(); - return ndpi_is_valid_char_tbl[(unsigned char)c]; -} - -/* ******************************************************************** */ - -static int ndpi_find_non_eng_bigrams(char *str) { - char s[3]; - - if((ndpi_isdigit(str[0]) && ndpi_isdigit(str[1])) - || ndpi_is_other_char(str[0]) - || ndpi_is_other_char(str[1]) - ) - return(1); - - s[0] = tolower(str[0]), s[1] = tolower(str[1]), s[2] = '\0'; - - return(ndpi_match_bigram(s)); -} - -/* ******************************************************************** */ - -/* #define PRINT_STRINGS 1 */ - -int ndpi_has_human_readeable_string(char *buffer, u_int buffer_size, - u_int8_t min_string_match_len, - char *outbuf, u_int outbuf_len) { - u_int ret = 0, i, do_cr = 0, len = 0, o_idx = 0, being_o_idx = 0; - - if(buffer_size <= 0) - return(0); - - outbuf_len--; - outbuf[outbuf_len] = '\0'; - - for(i=0; i> %c%c\n", ndpi_isprint(buffer[i]) ? buffer[i] : '.', ndpi_isprint(buffer[i+1]) ? buffer[i+1] : '.'); - if(do_cr) { - if(len > min_string_match_len) - ret = 1; - else { - o_idx = being_o_idx; - being_o_idx = o_idx; - outbuf[o_idx] = '\0'; - } - -#ifdef PRINT_STRINGS - printf(" [len: %u]%s\n", len, ret ? "<-- HIT" : ""); -#endif - - if(ret) - break; - - do_cr = 0, len = 0; - } - } - } - -#ifdef PRINT_STRINGS - printf("=======>> Found string: %u\n", ret); -#endif - - return(ret); -} - /* ********************************** */ static const char* ndpi_get_flow_info_by_proto_id(struct ndpi_flow_struct const * const flow, diff --git a/src/lib/protocols/http.c b/src/lib/protocols/http.c index 61d75cc8d46..61eb7c0595f 100644 --- a/src/lib/protocols/http.c +++ b/src/lib/protocols/http.c @@ -232,7 +232,7 @@ static void ndpi_validate_http_content(struct ndpi_detection_module_struct *ndpi || ndpi_strnstr((const char *)packet->content_line.ptr, "text/", packet->content_line.len) || ndpi_strnstr((const char *)packet->content_line.ptr, "/json", packet->content_line.len) ) { - /* This is supposed to be a human-readeable text file */ + /* This is supposed to be a human-readable text file */ packet->http_check_content = 1; if(len >= 8 /* 4 chars for \r\n\r\n and at least 4 charts for content guess */) { diff --git a/src/lib/protocols/tls.c b/src/lib/protocols/tls.c index c892c896121..752ece709ec 100644 --- a/src/lib/protocols/tls.c +++ b/src/lib/protocols/tls.c @@ -608,7 +608,7 @@ static void cleanupServerName(char *buffer, u_int buffer_len) { /* Return code -1: error (buffer too short) - 0: OK but buffer is not human readeable (so something went wrong) + 0: OK but buffer is not human readable (so something went wrong) 1: OK */ static int extractRDNSequence(struct ndpi_packet_struct *packet,