Commit 0793f4da authored by Victor Seva's avatar Victor Seva

New upstream version 1.4.0

parent 3f6ffffd
......@@ -30,3 +30,11 @@ test-driver
# Ignore Doxygen generated files
doc/html
# Tags file
tags
TAGS
# Vim swap files
.*.swp
.*.swo
2016-08-08 Ivan Alonso <kaian@irontec.com>
* sngrep 1.4.0 released
* Capture
* Added a setting and command line option ('-R') to rotate captured dialogs
* Improved RTP stream detection
* Call List
* Added capture match and bpf filters labels
* Added capture device label for online captures
* Call Flow
* Rework how RTP streams arrows select their source and destination columns
* Settings
* Added a setting to configure EEP/HEP capture id
* Changed default savepath to current directory
* Other
* Fixed a crash with malformed From and To headers
* Fixed a crash while comparing two big SIP messages
* Fixed a crash while trying to decode TLS packets too early
* Fixed incorrect state detection of Call status column
* Increased the timeout to consider a test unsuccessful
2016-04-28 Ivan Alonso <kaian@irontec.com>
* sngrep 1.3.1 released
* sngrep 1.3.1 released
* Fixed a crash where From or To headers where too long
* Improved call list screen resize
* Add support for IEEE 802.1Q Ethernet VLAN headers
* Fixed a bug in displayed callstate after failed authentication
* Associate RTP stream with the most recent dialog that uses stream rtp ports
* Fixed a crash where From or To headers where too long
* Improved call list screen resize
* Add support for IEEE 802.1Q Ethernet VLAN headers
* Fixed a bug in displayed callstate after failed authentication
* Associate RTP stream with the most recent dialog that uses stream rtp ports
2016-03-02 Ivan Alonso <kaian@irontec.com>
* sngrep 1.3.0 released
......
AC_PREREQ([2.59])
AC_INIT([sngrep], [1.3.1], [kaian@irontec.com], [sngrep], [http://www.irontec.com/])
AC_INIT([sngrep], [1.4.0], [kaian@irontec.com], [sngrep], [http://www.irontec.com/])
AM_INIT_AUTOMAKE([1.9])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AC_CONFIG_HEADERS([src/config.h])
......
......@@ -3,7 +3,7 @@
.\" Copyright (c) 2013-2016 Ivan Alonso <kaian@irontec.com>
.\" Copyright (c) 2013-2016 Irontec S.L.
.TH SNGREP 8 "January 2016" "sngrep 1.3.1"
.TH SNGREP 8 "January 2016" "sngrep 1.4.0"
.SH NAME
......
sngrep (1.4.0) experimental; urgency=low
* sngrep 1.4.0 released
-- Ivan Alonso <kaian@irontec.com> Mon, 08 Aug 2016 10:57:07 +0200
sngrep (1.3.1) experimental; urgency=low
* sngrep 1.3.1 released
......
......@@ -12,7 +12,7 @@ sngrep_SOURCES+=capture_openssl.c
endif
sngrep_SOURCES+=address.c packet.c sip.c sip_call.c sip_msg.c sip_attr.c main.c
sngrep_SOURCES+=option.c group.c filter.c keybinding.c media.c setting.c rtp.c
sngrep_SOURCES+=util.c vector.c ui_panel.c scrollbar.c
sngrep_SOURCES+=util.c hash.c vector.c ui_panel.c scrollbar.c
sngrep_SOURCES+=ui_manager.c ui_call_list.c ui_call_flow.c ui_call_raw.c
sngrep_SOURCES+=ui_stats.c ui_filter.c ui_save.c ui_msg_diff.c
sngrep_SOURCES+=ui_column_select.c ui_settings.c
......
......@@ -54,10 +54,11 @@ capture_config_t capture_cfg =
{ 0 };
void
capture_init(size_t limit, bool rtp_capture)
capture_init(size_t limit, bool rtp_capture, bool rotate)
{
capture_cfg.limit = limit;
capture_cfg.rtp_capture = rtp_capture;
capture_cfg.rotate = rotate;
capture_cfg.sources = vector_create(1, 1);
capture_cfg.tcp_reasm = vector_create(0, 10);
capture_cfg.ip_reasm = vector_create(0, 10);
......@@ -133,6 +134,9 @@ capture_online(const char *dev, const char *outfile)
return 2;
}
// Store capture device
capinfo->device = dev;
// Get datalink to parse packets correctly
capinfo->link = pcap_datalink(capinfo->handle);
......@@ -236,8 +240,12 @@ parse_packet(u_char *info, const struct pcap_pkthdr *header, const u_char *packe
return;
// Check if we have reached capture limit
if (capture_cfg.limit && sip_calls_count() >= capture_cfg.limit)
return;
if (capture_cfg.limit && sip_calls_count() >= capture_cfg.limit) {
// If capture rotation is disabled, just skip this packet
if (!capture_cfg.rotate) {
return;
}
}
// Check maximum capture length
if (header->caplen > MAX_CAPTURE_LEN)
......@@ -798,9 +806,19 @@ capture_set_bpf_filter(const char *filter)
}
// Store valid capture filter
capture_cfg.filter = filter;
return 0;
}
const char *
capture_get_bpf_filter()
{
return capture_cfg.filter;
}
void
capture_set_paused(int pause)
{
......@@ -852,7 +870,18 @@ capture_input_file()
} else {
return "Multiple files";
}
}
const char *
capture_device()
{
capture_info_t *capinfo;
if (vector_count(capture_cfg.sources) == 1) {
capinfo = vector_first(capture_cfg.sources);
return capinfo->device;
}
return NULL;
}
const char*
......
......@@ -119,10 +119,14 @@ struct capture_config {
size_t limit;
//! Also capture RTP packets
bool rtp_capture;
//! Rotate capturad dialogs when limit have reached
bool rotate;
//! Where should we store captured packets
enum capture_storage storage;
//! Key file for TLS decrypt
const char *keyfile;
//! capture filter expression text
const char *filter;
//! The compiled filter expression
struct bpf_program fp;
//! libpcap dump file handler
......@@ -156,6 +160,8 @@ struct capture_info
bpf_u_int32 net;
//! Input file in Offline capture
const char *infile;
//! Capture device in Online mode
const char *device;
//! Capture thread for online capturing
pthread_t capture_t;
};
......@@ -165,9 +171,10 @@ struct capture_info
*
* @param limit Numbers of calls >0
* @param rtp_catpure Enable rtp capture
* @param rotate Enable capture rotation
*/
void
capture_init(size_t limit, bool rtp_capture);
capture_init(size_t limit, bool rtp_capture, bool rotate);
/**
* @brief Deinitialize capture data
......@@ -316,6 +323,14 @@ capture_is_online();
int
capture_set_bpf_filter(const char *filter);
/**
* @brief Get the configured BPF filter
*
* @return String containing the BPF filter text or NULL
*/
const char *
capture_get_bpf_filter();
/**
* @brief Pause/Resume capture
*
......@@ -353,6 +368,15 @@ capture_status_desc();
const char*
capture_input_file();
/**
* @brief Get Device interface from Online mode
*
* @return Device name used to capture packets
* @return NULL in Offline or Mixed mode
*/
const char *
capture_device();
/**
* @brief Get Key file from decrypting TLS packets
*
......
......@@ -67,7 +67,7 @@ capture_eep_init()
eep_cfg.capt_host = setting_get_value(SETTING_EEP_SEND_ADDR);
eep_cfg.capt_port = setting_get_value(SETTING_EEP_SEND_PORT);
eep_cfg.capt_password = setting_get_value(SETTING_EEP_SEND_PASS);
eep_cfg.capt_id = 2002;
eep_cfg.capt_id = setting_get_intvalue(SETTING_EEP_SEND_ID);;
hints->ai_flags = AI_NUMERICSERV;
hints->ai_family = AF_UNSPEC;
......
......@@ -187,13 +187,9 @@ tls_connection_create(struct in_addr caddr, uint16_t cport, struct in_addr saddr
memcpy(&conn->client_port, &cport, sizeof(uint16_t));
memcpy(&conn->server_port, &sport, sizeof(uint16_t));
SSL_library_init();
OpenSSL_add_all_algorithms();
gnutls_global_init();
if (!(conn->ssl_ctx = SSL_CTX_new(SSLv23_server_method())))
return NULL;
if (!(conn->ssl = SSL_new(conn->ssl_ctx)))
if (gnutls_init(&conn->ssl, GNUTLS_SERVER) < GNUTLS_E_SUCCESS)
return NULL;
if (!(keyfp = fopen(capture_keyfile(), "rb")))
......@@ -237,8 +233,7 @@ tls_connection_destroy(struct SSLConnection *conn)
}
// Deallocate connection memory
SSL_CTX_free(conn->ssl_ctx);
SSL_free(conn->ssl);
gnutls_deinit(conn->ssl);
sng_free(conn);
}
......@@ -252,12 +247,13 @@ int
tls_check_keyfile(const char *keyfile)
{
gnutls_x509_privkey_t key;
gnutls_privkey_t privkey;
gnutls_datum_t keycontent = { NULL, 0 };
FILE *keyfp;
size_t br;
int ret;
SSL_library_init();
OpenSSL_add_all_algorithms();
gnutls_global_init();
if (access(capture_keyfile(), R_OK) != 0)
return 0;
......@@ -272,11 +268,37 @@ tls_check_keyfile(const char *keyfile)
br = fread(keycontent.data, 1, keycontent.size, keyfp);
fclose(keyfp);
gnutls_x509_privkey_init(&key);
if (gnutls_x509_privkey_import(key, &keycontent, GNUTLS_X509_FMT_PEM) < 0)
// Check we have read something from keyfile
if (!keycontent.data)
return 0;
// Initialize keyfile structure
ret = gnutls_x509_privkey_init(&key);
if (ret < GNUTLS_E_SUCCESS) {
fprintf (stderr, "Error initializing keyfile: %s\n", gnutls_strerror(ret));
return 0;
}
ret = gnutls_privkey_init(&privkey);
if (ret < GNUTLS_E_SUCCESS) {
fprintf (stderr, "Error initializing keyfile: %s\n", gnutls_strerror(ret));
return 0;
}
// Import RSA keyfile
ret = gnutls_x509_privkey_import(key, &keycontent, GNUTLS_X509_FMT_PEM);
if (ret < GNUTLS_E_SUCCESS) {
fprintf (stderr, "Error loading keyfile: %s\n", gnutls_strerror(ret));
return 0;
}
sng_free(keycontent.data);
ret = gnutls_privkey_import_x509(privkey, key, 0);
if (ret < GNUTLS_E_SUCCESS) {
fprintf (stderr, "Error loading keyfile: %s\n", gnutls_strerror(ret));
return 0;
}
return 1;
}
......@@ -393,7 +415,8 @@ tls_process_record(struct SSLConnection *conn, const uint8_t *payload,
break;
case change_cipher_spec:
// From now on, this connection will be encrypted using MasterSecret
conn->encrypted = 1;
if (conn->client_cipher_ctx && conn->server_cipher_ctx)
conn->encrypted = 1;
break;
case application_data:
if (conn->encrypted) {
......@@ -487,6 +510,9 @@ tls_process_record_handshake(struct SSLConnection *conn, const opaque *fragment,
tls_debug_print_hex("exchange keys",exkeys.data, exkeys.size);
gnutls_privkey_decrypt_data(conn->server_private_key, 0, &exkeys, &pms);
if (!pms.data) break;
memcpy(&conn->pre_master_secret, pms.data, pms.size);
tls_debug_print_hex("pre_master_secret", pms.data, pms.size);
tls_debug_print_hex("client_random", &conn->client_random, 32);
......
......@@ -47,7 +47,7 @@
#define __SNGREP_CAPTURE_TLS_
#include "config.h"
#include <gnutls/openssl.h>
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#include <gnutls/abstract.h>
#include <gnutls/x509.h>
......@@ -202,8 +202,7 @@ struct SSLConnection {
//! Server port
uint16_t server_port;
SSL *ssl;
SSL_CTX *ssl_ctx;
gnutls_session_t ssl;
int ciph;
gnutls_privkey_t server_private_key;
struct Random client_random;
......
......@@ -232,8 +232,10 @@ tls_check_keyfile(const char *keyfile)
{
SSL *ssl;
SSL_CTX *ssl_ctx;
char errbuf[1024];
SSL_library_init();
SSL_load_error_strings();
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
......@@ -243,7 +245,12 @@ tls_check_keyfile(const char *keyfile)
if (!(ssl_ctx = SSL_CTX_new(SSLv23_server_method())))
return 0;
SSL_CTX_use_PrivateKey_file(ssl_ctx, capture_keyfile(), SSL_FILETYPE_PEM);
if (!(SSL_CTX_use_PrivateKey_file(ssl_ctx, capture_keyfile(), SSL_FILETYPE_PEM))) {
unsigned long n = ERR_get_error();
fprintf(stderr, "%s\n", ERR_error_string(n, errbuf));
return 0;
}
if (!(ssl = SSL_new(ssl_ctx)))
return 0;
......@@ -366,7 +373,8 @@ tls_process_record(struct SSLConnection *conn, const uint8_t *payload,
break;
case change_cipher_spec:
// From now on, this connection will be encrypted using MasterSecret
conn->encrypted = 1;
if (conn->client_cipher_ctx.cipher && conn->server_cipher_ctx.cipher)
conn->encrypted = 1;
break;
case application_data:
if (conn->encrypted) {
......
......@@ -267,8 +267,9 @@ call_flow_draw_columns(ui_t *ui)
if (!setting_disabled(SETTING_CF_MEDIA)) {
while ((call = call_group_get_next(info->group, call)) ) {
streams = vector_iterator(call->streams);
while ((stream = vector_iterator_next(&streams))) {
if (stream_get_count(stream)) {
if (stream->type == PACKET_RTP && stream_get_count(stream)) {
addr = stream->src;
addr.port = 0;
call_flow_column_add(ui, NULL, addr);
......@@ -406,12 +407,10 @@ call_flow_draw_message(ui_t *ui, call_flow_arrow_t *arrow, int cline)
char delta[15] = { };
int flowh, floww;
char mediastr[40];
call_flow_column_t *column1, *column2;
sip_msg_t *msg = arrow->item;
vector_iter_t medias;
int color = 0;
int msglen;
int arrow_dir = CF_ARROW_RIGHT;
// Get panel information
info = call_flow_info(ui);
......@@ -472,19 +471,20 @@ call_flow_draw_message(ui_t *ui, call_flow_arrow_t *arrow, int cline)
msglen = (strlen(method) > 24) ? 24 : strlen(method);
// Get origin and destination column
column1 = call_flow_column_get(ui, callid, src);
column2 = call_flow_column_get(ui, callid, dst);
call_flow_column_t *tmp;
if (column1->colpos > column2->colpos) {
tmp = column1;
column1 = column2;
column2 = tmp;
arrow->scolumn = call_flow_column_get(ui, callid, src);
arrow->dcolumn = call_flow_column_get(ui, callid, dst);
// Determine start and end position of the arrow line
int arrow_dir, startpos, endpos;
if (arrow->scolumn->colpos < arrow->dcolumn->colpos) {
arrow_dir = CF_ARROW_RIGHT;
startpos = 20 + 30 * arrow->scolumn->colpos;
endpos = 20 + 30 * arrow->dcolumn->colpos;
} else {
arrow_dir = CF_ARROW_LEFT;
startpos = 20 + 30 * arrow->dcolumn->colpos;
endpos = 20 + 30 * arrow->scolumn->colpos;
}
int startpos = 20 + 30 * column1->colpos;
int endpos = 20 + 30 * column2->colpos;
int distance = abs(endpos - startpos) - 3;
// Highlight current message
......@@ -545,13 +545,13 @@ call_flow_draw_message(ui_t *ui, call_flow_arrow_t *arrow, int cline)
// Write the arrow at the end of the message (two arros if this is a retrans)
if (arrow_dir == CF_ARROW_RIGHT) {
mvwaddch(flow_win, cline, endpos - 2, '>');
if (call_msg_is_retrans(msg)) {
if (msg->retrans) {
mvwaddch(flow_win, cline, endpos - 3, '>');
mvwaddch(flow_win, cline, endpos - 4, '>');
}
} else {
mvwaddch(flow_win, cline, startpos + 2, '<');
if (call_msg_is_retrans(msg)) {
if (msg->retrans) {
mvwaddch(flow_win, cline, startpos + 3, '<');
mvwaddch(flow_win, cline, startpos + 4, '<');
}
......@@ -571,7 +571,14 @@ call_flow_draw_message(ui_t *ui, call_flow_arrow_t *arrow, int cline)
if (info->arrowtime) {
if (arrow == call_flow_arrow_selected(ui))
wattron(flow_win, COLOR_PAIR(CP_CYAN_ON_DEF));
mvwprintw(flow_win, cline, 2, "%s", msg_time);
if (arrow == vector_item(info->darrows, info->cur_arrow)) {
wattron(flow_win, A_BOLD);
mvwprintw(flow_win, cline, 2, "%s", msg_time);
wattroff(flow_win, A_BOLD);
} else {
mvwprintw(flow_win, cline, 2, "%s", msg_time);
}
// Print delta from selected message
if (!setting_has_value(SETTING_CF_SDP_INFO, "compressed")) {
......@@ -606,10 +613,11 @@ call_flow_draw_rtp_stream(ui_t *ui, call_flow_arrow_t *arrow, int cline)
char text[50], time[20];
int height, width;
const char *callid;
address_t msg_src, msg_dst, stream_src, stream_dst;
call_flow_column_t *column1, *column2;
rtp_stream_t *stream = arrow->item;
int arrow_dir = CF_ARROW_RIGHT;
sip_msg_t *msg;
sip_call_t *call;
call_flow_arrow_t *msgarrow;
address_t addr;
// Get panel information
info = call_flow_info(ui);
......@@ -627,48 +635,71 @@ call_flow_draw_rtp_stream(ui_t *ui, call_flow_arrow_t *arrow, int cline)
if (cline > height + arrow->height)
return 0;
// Get Message method (include extra info)
// Get arrow text
sprintf(text, "RTP (%s) %d", stream_get_format(stream), stream_get_count(stream));
// Get message data
callid = stream->media->msg->call->callid;
msg_src = stream->media->msg->packet->src;
msg_dst = stream->media->msg->packet->dst;
stream_src = stream->src;
stream_src.port = 0;
stream_dst = stream->dst;
stream_dst.port = 0;
// Get origin column for this stream.
// If we share the same Address from its setup SIP packet, use that column instead.
if (!strcmp(stream->src.ip, msg_src.ip)) {
column1 = call_flow_column_get(ui, callid, msg_src);
} else if (!strcmp(stream->src.ip, msg_dst.ip)) {
column1 = call_flow_column_get(ui, callid, msg_dst);
} else {
column1 = call_flow_column_get(ui, 0, stream_src);
call = stream->media->msg->call;
callid = call->callid;
/**
* This logic will try to use the same columns for the stream representation
* that are used in the SIP messages that configured the streams in their SDP
* if they share the same IP addresses.
*/
// Message with Stream destination configured in SDP content
msg = stream->media->msg;
// If message and stream share the same IP address
if (address_equals(msg->packet->src, stream->dst)) {
// Reuse the msg arrow columns as destination column
if ((msgarrow = call_flow_arrow_find(ui, msg))) {
arrow->dcolumn = msgarrow->scolumn;
}
}
// Get destination column for this stream.
// If we share the same Address from its setup SIP packet, use that column instead.
if (!strcmp(stream->dst.ip, msg_dst.ip)) {
column2 = call_flow_column_get(ui, callid, msg_dst);
} else if (!strcmp(stream->dst.ip, msg_src.ip)) {
column2 = call_flow_column_get(ui, callid, msg_src);
} else {
column2 = call_flow_column_get(ui, 0, stream_dst);
// fallback: Just use any column that have the destination IP printed
if (!arrow->dcolumn) {
// FIXME figure a better way to find ignoring port :(
addr = stream->dst; addr.port = 0;
arrow->dcolumn = call_flow_column_get(ui, 0, addr);
}
call_flow_column_t *tmp;
if (column1->colpos > column2->colpos) {
tmp = column1;
column1 = column2;
column2 = tmp;
arrow_dir = CF_ARROW_LEFT;
/**
* For source address of the stream, first try to find a message that have
* the stream source configured in their SDP as destination and then apply
* the same previous logic address: if IP address matches, reuse message
* column, othwerise any column with the source IP will be used.
*/
// Message with Stream source configured in SDP content
msg = call_msg_with_media(call, stream->src);
// Try to find a message with configured SDP matching the source of this stream
if (msg && address_equals(msg->packet->src, stream->src)) {
// Reuse the msg arrow columns as destination column
if ((msgarrow = call_flow_arrow_find(ui, msg))) {
arrow->scolumn = msgarrow->scolumn;
}
}
int startpos = 20 + 30 * column1->colpos;
int endpos = 20 + 30 * column2->colpos;
// fallback: Just use any column that have the soruce IP printed
if (!arrow->scolumn) {
// FIXME figure a better way to find ignoring port :(
addr = stream->src; addr.port = 0;
arrow->scolumn = call_flow_column_get(ui, 0, addr);
}
// Determine start and end position of the arrow line
int arrow_dir, startpos, endpos;
if (arrow->scolumn->colpos < arrow->dcolumn->colpos) {
arrow_dir = CF_ARROW_RIGHT;
startpos = 20 + 30 * arrow->scolumn->colpos;
endpos = 20 + 30 * arrow->dcolumn->colpos;
} else {
arrow_dir = CF_ARROW_LEFT;
startpos = 20 + 30 * arrow->dcolumn->colpos;
endpos = 20 + 30 * arrow->scolumn->colpos;
}
int distance = 0;
if (startpos != endpos) {
......@@ -750,7 +781,14 @@ call_flow_draw_rtp_stream(ui_t *ui, call_flow_arrow_t *arrow, int cline)
// Print timestamp
if (info->arrowtime) {
timeval_to_time(stream->time, time);
mvwprintw(win, cline, 2, "%s", time);
if (arrow == vector_item(info->darrows, info->cur_arrow)) {
wattron(win, A_BOLD);
mvwprintw(win, cline, 2, "%s", time);
wattroff(win, A_BOLD);
} else {
mvwprintw(win, cline, 2, "%s", time);
}
}
return arrow->height;
......
......@@ -100,6 +100,10 @@ struct call_flow_arrow {
int height;
//! Line of flow window this line starts
int line;
//! Source column for this arrow
call_flow_column_t *scolumn;
//! Destination column for this arrow
call_flow_column_t *dcolumn;
};
/**
......
......@@ -92,7 +92,7 @@ call_list_create(ui_t *ui)
}
// Initialize the fields
info->fields[FLD_LIST_FILTER] = new_field(1, ui->width - 19, 2, 18, 0, 0);
info->fields[FLD_LIST_FILTER] = new_field(1, ui->width - 19, 3, 18, 0, 0);
info->fields[FLD_LIST_COUNT] = NULL;
// Create the form and post it
......@@ -104,7 +104,7 @@ call_list_create(ui_t *ui)
info->menu_active = 0;
// Calculate available printable area
info->list_win = subwin(ui->win, ui->height - 5, ui->width, 4, 0);
info->list_win = subwin(ui->win, ui->height - 6, ui->width, 5, 0);
info->scroll = ui_set_scrollbar(info->list_win, SB_VERTICAL, SB_LEFT);
// Group of selected calls
......@@ -190,6 +190,7 @@ call_list_draw_header(ui_t *ui)
int colpos, collen, i;
char sortind;
const char *countlb;
const char *device, *filterexpr, *filterbpf;
// Get panel info
call_list_info_t *info = call_list_info(ui);
......@@ -202,9 +203,38 @@ call_list_draw_header(ui_t *ui)
// Print Open filename in Offline mode
if ((infile = capture_input_file()))
mvwprintw(ui->win, 1, ui->width - strlen(infile) - 11, "Filename: %s", infile);
mvwprintw(ui->win, 2, 2, "Display Filter: ");
mvwprintw(ui->win, 1, 2, "Current Mode: %s", capture_status_desc());
mvwprintw(ui->win, 1, 77, "Filename: %s", infile);
mvwprintw(ui->win, 1, 2, "Current Mode: ");
if (capture_is_online()) {
wattron(ui->win, COLOR_PAIR(CP_GREEN_ON_DEF));
} else {
wattron(ui->win, COLOR_PAIR(CP_RED_ON_DEF));
}
wprintw(ui->win, "%s ", capture_status_desc());
// Get online mode capture device
if ((device = capture_device()))
wprintw(ui->win, "[%s]", device);
wattroff(ui->win, COLOR_PAIR(CP_GREEN_ON_DEF));
wattroff(ui->win, COLOR_PAIR(CP_RED_ON_DEF));
// Label for Display filter
mvwprintw(ui->win, 3, 2, "Display Filter: ");
mvwprintw(ui->win, 2, 2, "Match Expression: ");
wattron(ui->win, COLOR_PAIR(CP_YELLOW_ON_DEF));
if ((filterexpr = sip_get_match_expression()))
wprintw(ui->win, "%s", filterexpr);
wattroff(ui->win, COLOR_PAIR(CP_YELLOW_ON_DEF));
mvwprintw(ui->win, 2, 45, "BPF Filter: ");
wattron(ui->win, COLOR_PAIR(CP_YELLOW_ON_DEF));
if ((filterbpf = capture_get_bpf_filter()))
wprintw(ui->win, "%s", filterbpf);
wattroff(ui->win, COLOR_PAIR(CP_YELLOW_ON_DEF));
// Reverse colors on monochrome terminals
if (!has_colors())
......@@ -215,14 +245,14 @@ call_list_draw_header(ui_t *ui)
// Draw columns titles
wattron(ui->win, A_BOLD | COLOR_PAIR(CP_DEF_ON_CYAN));
mvwprintw(ui->win, 3, 0, "%*s", ui->width, "");
ui_clear_line(ui, 4);
// Draw separator line
if (info->menu_active) {
colpos = 18;
mvwprintw(ui->win, 3, 0, "Sort by");
mvwprintw(ui->win, 4, 0, "Sort by");
wattron(ui->win, A_BOLD | COLOR_PAIR(CP_CYAN_ON_BLACK));
mvwprintw(ui->win, 3, 11, "%*s", 1, "");
mvwprintw(ui->win, 4, 11, "%*s", 1, "");
wattron(ui->win, A_BOLD | COLOR_PAIR(CP_DEF_ON_CYAN));
} else {
colpos = 6;
......@@ -242,16 +272,16 @@ call_list_draw_header(ui_t *ui)
if (info->columns[i].id == sort.by) {
wattron(ui->win, A_BOLD | COLOR_PAIR(CP_YELLOW_ON_CYAN));
sortind = (sort.asc) ? '^' : 'v';
mvwprintw(ui->win, 3, colpos, "%c%.*s", sortind, collen, coldesc);
mvwprintw(ui->win, 4, colpos, "%c%.*s", sortind, collen, coldesc);
wattron(ui->win, A_BOLD | COLOR_PAIR(CP_DEF_ON_CYAN));
} else {
mvwprintw(ui->win, 3, colpos, "%.*s", collen, coldesc);
mvwprintw(ui->win, 4, colpos, "%.*s", collen, coldesc);
}
colpos += collen + 1;
}
// Print Autoscroll indicator
if (info->autoscroll)
mvwprintw(ui->win, 3, 0, "A");
mvwprintw(ui->win, 4, 0, "A");
wattroff(ui->win, A_BOLD | A_REVERSE | COLOR_PAIR(CP_DEF_ON_CYAN));
// Print Dialogs or Calls in label depending on calls filter
......@@ -265,9 +295,9 @@ call_list_draw_header(ui_t *ui)