All colors are beautiful for py-flow-info.

* instance alias is now mandatory (will be required for future use-cases)

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
This commit is contained in:
Toni Uhlig
2021-01-26 19:07:16 +01:00
parent 25c23e3d23
commit 102b61175c
3 changed files with 83 additions and 11 deletions

View File

@@ -8,6 +8,12 @@ import os
import scapy.all import scapy.all
import stat import stat
import socket import socket
try:
from colorama import Back, Fore, Style
USE_COLORAMA=True
except ModuleNotFoundError:
print('Python module colorama not found, using fallback.')
USE_COLORAMA=False
DEFAULT_HOST = '127.0.0.1' DEFAULT_HOST = '127.0.0.1'
DEFAULT_PORT = 7000 DEFAULT_PORT = 7000
@@ -34,11 +40,52 @@ FLOW_EVENTS = [ ('Invalid','invalid'), ('New','new'), ('End','end'), ('Idle','id
('Detected','detected'), ('Detection-Update','detection-update'), ('Not-Detected','not-detected') ] ('Detected','detected'), ('Detection-Update','detection-update'), ('Not-Detected','not-detected') ]
class TermColor: class TermColor:
HINT = '\033[33m'
WARNING = '\033[93m' WARNING = '\033[93m'
FAIL = '\033[91m' FAIL = '\033[91m'
BOLD = '\033[1m' BOLD = '\033[1m'
END = '\033[0m' END = '\033[0m'
BLINK = "\x1b[5m" BLINK = '\x1b[5m'
if USE_COLORAMA is True:
COLOR_TUPLES = [ (Fore.BLUE, [Back.RED, Back.MAGENTA, Back.WHITE]),
(Fore.CYAN, [Back.MAGENTA, Back.RED, Back.WHITE]),
(Fore.GREEN, [Back.YELLOW, Back.RED, Back.MAGENTA, Back.WHITE]),
(Fore.MAGENTA, [Back.CYAN, Back.BLUE, Back.WHITE]),
(Fore.RED, [Back.GREEN, Back.BLUE, Back.WHITE]),
(Fore.WHITE, [Back.BLACK, Back.MAGENTA, Back.RED, Back.BLUE]),
(Fore.YELLOW, [Back.RED, Back.CYAN, Back.BLUE, Back.WHITE]),
(Fore.LIGHTBLUE_EX, [Back.LIGHTRED_EX, Back.RED]),
(Fore.LIGHTCYAN_EX, [Back.LIGHTMAGENTA_EX, Back.MAGENTA]),
(Fore.LIGHTGREEN_EX, [Back.LIGHTYELLOW_EX, Back.YELLOW]),
(Fore.LIGHTMAGENTA_EX, [Back.LIGHTCYAN_EX, Back.CYAN]),
(Fore.LIGHTRED_EX, [Back.LIGHTGREEN_EX, Back.GREEN]),
(Fore.LIGHTWHITE_EX, [Back.LIGHTBLACK_EX, Back.BLACK]),
(Fore.LIGHTYELLOW_EX, [Back.LIGHTRED_EX, Back.RED]) ]
@staticmethod
def calcColorHash(string):
h = 0
for char in string:
h += ord(char)
return h
@staticmethod
def getColorsByHash(string):
h = TermColor.calcColorHash(string)
tuple_index = h % len(TermColor.COLOR_TUPLES)
bg_tuple_index = h % len(TermColor.COLOR_TUPLES[tuple_index][1])
return (TermColor.COLOR_TUPLES[tuple_index][0],
TermColor.COLOR_TUPLES[tuple_index][1][bg_tuple_index])
@staticmethod
def setColorByString(string):
if USE_COLORAMA is True:
fg_color, bg_color = TermColor.getColorsByHash(string)
color_hash = TermColor.calcColorHash(string)
return '{}{}{}{}{}'.format(Style.BRIGHT, fg_color, bg_color, string, Style.RESET_ALL)
else:
return '{}{}{}'.format(TermColor.BOLD, string, TermColor.END)
class nDPIsrvdSocket: class nDPIsrvdSocket:
def __init__(self): def __init__(self):

View File

@@ -41,9 +41,19 @@ def parse_json_str(json_str):
else TermColor.FAIL + TermColor.BOLD + TermColor.BLINK + 'RISK' + TermColor.END, else TermColor.FAIL + TermColor.BOLD + TermColor.BLINK + 'RISK' + TermColor.END,
ndpi_frisk[:-2]) ndpi_frisk[:-2])
instance_and_source = ''
instance_and_source += '[{}]'.format(TermColor.setColorByString(j['alias']))
instance_and_source += '[{}]'.format(TermColor.setColorByString(j['source']))
flow_event_name = ''
if nDPIdEvent.FlowEventName == 'guessed' or nDPIdEvent.FlowEventName == 'undetected':
flow_event_name += '{}{:>16}{}'.format(TermColor.HINT, nDPIdEvent.FlowEventPrettyName, TermColor.END)
else:
flow_event_name += '{:>16}'.format(nDPIdEvent.FlowEventPrettyName)
if j['l3_proto'] == 'ip4': if j['l3_proto'] == 'ip4':
print('{:>16}: [{:.>6}] [{}][{:.>5}] [{:.>15}]{} -> [{:.>15}]{} {}' \ print('{} {}: [{:.>6}] [{}][{:.>5}] [{:.>15}]{} -> [{:.>15}]{} {}' \
''.format(nDPIdEvent.FlowEventPrettyName, ''.format(instance_and_source, flow_event_name,
j['flow_id'], j['l3_proto'], j['l4_proto'], j['flow_id'], j['l3_proto'], j['l4_proto'],
j['src_ip'].lower(), j['src_ip'].lower(),
'[{:.>5}]'.format(j['src_port']) if 'src_port' in j else '', '[{:.>5}]'.format(j['src_port']) if 'src_port' in j else '',
@@ -51,8 +61,8 @@ def parse_json_str(json_str):
'[{:.>5}]'.format(j['dst_port']) if 'dst_port' in j else '', '[{:.>5}]'.format(j['dst_port']) if 'dst_port' in j else '',
ndpi_proto_categ)) ndpi_proto_categ))
elif j['l3_proto'] == 'ip6': elif j['l3_proto'] == 'ip6':
print('{:>16}: [{:.>6}] [{}][{:.>5}] [{:.>39}]{} -> [{:.>39}]{} {}' \ print('{} {}: [{:.>6}] [{}][{:.>5}] [{:.>39}]{} -> [{:.>39}]{} {}' \
''.format(nDPIdEvent.FlowEventPrettyName, ''.format(instance_and_source, flow_event_name,
j['flow_id'], j['l3_proto'], j['l4_proto'], j['flow_id'], j['l3_proto'], j['l4_proto'],
j['src_ip'].lower(), j['src_ip'].lower(),
'[{:.>5}]'.format(j['src_port']) if 'src_port' in j else '', '[{:.>5}]'.format(j['src_port']) if 'src_port' in j else '',
@@ -63,7 +73,7 @@ def parse_json_str(json_str):
raise RuntimeError('unsupported l3 protocol: {}'.format(j['l3_proto'])) raise RuntimeError('unsupported l3 protocol: {}'.format(j['l3_proto']))
if len(ndpi_frisk) > 0: if len(ndpi_frisk) > 0:
print('{:>18}{}'.format('', ndpi_frisk)) print('{} {:>18}{}'.format(instance_and_source, '', ndpi_frisk))
if __name__ == '__main__': if __name__ == '__main__':

23
nDPId.c
View File

@@ -940,10 +940,7 @@ static void jsonize_basic(struct nDPId_reader_thread * const reader_thread)
ndpi_serialize_string_int32(&workflow->ndpi_serializer, "thread_id", reader_thread->array_index); ndpi_serialize_string_int32(&workflow->ndpi_serializer, "thread_id", reader_thread->array_index);
ndpi_serialize_string_uint32(&workflow->ndpi_serializer, "packet_id", workflow->packets_captured); ndpi_serialize_string_uint32(&workflow->ndpi_serializer, "packet_id", workflow->packets_captured);
ndpi_serialize_string_string(&workflow->ndpi_serializer, "source", pcap_file_or_interface); ndpi_serialize_string_string(&workflow->ndpi_serializer, "source", pcap_file_or_interface);
if (instance_alias != NULL)
{
ndpi_serialize_string_string(&workflow->ndpi_serializer, "alias", instance_alias); ndpi_serialize_string_string(&workflow->ndpi_serializer, "alias", instance_alias);
}
} }
static void jsonize_daemon(struct nDPId_reader_thread * const reader_thread, enum daemon_event event) static void jsonize_daemon(struct nDPId_reader_thread * const reader_thread, enum daemon_event event)
@@ -2554,7 +2551,9 @@ static int parse_options(int argc, char ** argv)
"\t-p\tWrite the daemon PID to the given file path.\n" "\t-p\tWrite the daemon PID to the given file path.\n"
"\t-u\tChange UID to the numeric value of user.\n" "\t-u\tChange UID to the numeric value of user.\n"
"\t-g\tChange GID to the numeric value of group.\n" "\t-g\tChange GID to the numeric value of group.\n"
"\t-a\tSet an optional name of this daemon instance which will be part of every JSON message.\n" "\t-a\tSet an alias name of this daemon instance which will be part of every JSON message.\n"
"\t \tThis value is required for correct flow handling of multiple instances and should be unique.\n"
"\t \tDefaults to your hostname.\n"
"\t-o\t(Carefully) Tune some daemon options. See subopts below.\n\n"; "\t-o\t(Carefully) Tune some daemon options. See subopts below.\n\n";
while ((opt = getopt(argc, argv, "hi:IEP:lc:dp:u:g:a:o:")) != -1) while ((opt = getopt(argc, argv, "hi:IEP:lc:dp:u:g:a:o:")) != -1)
@@ -2688,6 +2687,22 @@ static int validate_options(char const * const arg0)
{ {
int retval = 0; int retval = 0;
if (instance_alias == NULL) {
char hname[256];
errno = 0;
if (gethostname(hname, sizeof(hname)) != 0) {
fprintf(stderr, "%s: Could not retrieve your hostname: %s\n", arg0, strerror(errno));
retval = 1;
} else {
instance_alias = strdup(hname);
fprintf(stderr,
"%s: No instance alias given, using your hostname '%s'\n", arg0, instance_alias);
if (instance_alias == NULL) {
retval = 1;
}
}
}
if (max_flows_per_thread < 128 || max_flows_per_thread > nDPId_MAX_FLOWS_PER_THREAD) if (max_flows_per_thread < 128 || max_flows_per_thread > nDPId_MAX_FLOWS_PER_THREAD)
{ {
fprintf(stderr, fprintf(stderr,