Commit e6dd8bcf authored by Victor Seva's avatar Victor Seva

New upstream version 1.4.2

parent 59855b0c
2016-12-19 Ivan Alonso <kaian@irontec.com>
* sngrep 1.4.2 released
* Capture
* Interface network mask and IP address are no longer a must to capture
* TLS
* gnutls: Add support for initial ClientHello in SSLv2
* gnutls: Initial support for GCM cipher modes
* gnutls: Improve compatibility with old libgcrypt versions
* Call List
* Add setting for default sorting field and order
* Call Flow
* Added a keybinding to only display RTP streams
* Added a new mode to only display _active_ RTP streams
* Packages
* Irontec Debian packages now compile using gnutls
* Updated spec file for RPM packages
2016-10-28 Ivan Alonso <kaian@irontec.com>
* sngrep 1.4.1 released
......
......@@ -16,6 +16,7 @@ as PCAP viewer.
* [Gentoo](https://github.com/irontec/sngrep/wiki/Installing-Binaries#gentoo)
* [Arch](https://github.com/irontec/sngrep/wiki/Installing-Binaries#arch)
* [OSX] (https://github.com/irontec/sngrep/wiki/Installing-Binaries#osx)
* [OpenWRT/LEDE] (https://github.com/irontec/sngrep/wiki/Installing-Binaries#openwrtlede)
### Building from sources
Prerequisites
......
......@@ -14,8 +14,8 @@ check_for_app() {
case `uname -sr` in
OpenBSD*)
export AUTOCONF_VERSION=2.63
export AUTOMAKE_VERSION=1.9
export AUTOCONF_VERSION=2.69
export AUTOMAKE_VERSION=1.15
;;
FreeBSD*)
AUTOCONF_VERSION=2.69
......
AC_PREREQ([2.59])
AC_INIT([sngrep], [1.4.1], [kaian@irontec.com], [sngrep], [http://www.irontec.com/])
AC_INIT([sngrep], [1.4.2], [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])
......@@ -110,10 +110,6 @@ AS_IF([test "x$WITH_GNUTLS" == "xyes"], [
AC_MSG_ERROR([ You need to have gnutls installed to compile sngrep])
])
AC_CHECK_LIB([gnutls-openssl], [SSL_new], [], [
AC_MSG_ERROR([ You need to have gnutls installed to compile sngrep])
])
AC_CHECK_LIB([gcrypt], [gcry_md_map_name], [], [
AC_MSG_ERROR([ You need to have libgcrypt installed to compile sngrep])
])
......
......@@ -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.4.1"
.TH SNGREP 8 "December 2016" "sngrep 1.4.2"
.SH NAME
......
sngrep (1.4.2) experimental; urgency=low
* sngrep 1.4.2 released
-- Ivan Alonso <kaian@irontec.com> Mon, 19 Dec 2016 14:00:26 +0100
sngrep (1.4.1) experimental; urgency=low
* sngrep 1.4.1 released
......
Source: sngrep
Section: comm
Build-Depends: dh-autoreconf, debhelper, libpcap-dev, libncursesw5-dev, libssl-dev, libpcre3-dev, libncurses5-dev
Build-Depends: dh-autoreconf, debhelper, libpcap-dev, libncursesw5-dev, gnutls-dev, libgcrypt-dev, libpcre3-dev, libncurses5-dev
Priority: optional
Standards-Version: 3.9.6
Homepage: https://github.com/irontec/sngrep
......@@ -9,7 +9,7 @@ Maintainer: Ivan Alonso <kaian@irontec.com>
Package: sngrep
Architecture: any
Pre-Depends: ${misc:Pre-Depends}
Depends: ${misc:Depends}, ${shlibs:Depends}, libpcap0.8, libncursesw5, libssl1.0.0 | libssl0.9.8, libpcre3
Depends: ${misc:Depends}, ${shlibs:Depends}, libpcap0.8, libncursesw5, libpcre3
Description: Ncurses SIP Messages flow viewer
sngrep displays SIP Messages grouped by Call-Id into flow
diagrams. It can be used as an offline PCAP viewer or online
......
#!/usr/bin/make -f
override_dh_auto_configure:
dh_auto_configure -- --with-openssl --with-pcre --enable-unicode --enable-ipv6 --enable-eep
dh_auto_configure -- --with-gnutls --with-pcre --enable-unicode --enable-ipv6 --enable-eep
%:
dh $@ --with autoreconf
%bcond_with openssl
Summary: SIP Messages flow viewer
Name: sngrep
Version: 1.4.1
Release: 0%{?dist}
License: GPLv3
Group: Applications/Engineering
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
Source: https://github.com/irontec/%{name}/archive/v%{version}.zip
URL: http://github.com/irontec/sngrep
BuildRequires: ncurses-devel
BuildRequires: make
BuildRequires: libpcap-devel
BuildRequires: pcre-devel
BuildRequires: autoconf
BuildRequires: automake
BuildRequires: gcc
%if %{with openssl}
BuildRequires: openssl-devel
%endif
Requires: ncurses
Requires: libpcap
Requires: pcre
%description
sngrep displays SIP Messages grouped by Call-Id into flow
diagrams. It can be used as an offline PCAP viewer or online
capture using libpcap functions.
It supports SIP UDP, TCP and TLS transports (when each message is
delivered in one packet).
You can also create new PCAP files from captures or displayed dialogs.
%prep
%setup -q
%build
./bootstrap.sh
%configure --with-pcre \
--enable-unicode \
--enable-ipv6 \
--enable-eep \
%{?_with_openssl}
make %{?_smp_mflags}
%install
%{__make} install DESTDIR=%{buildroot}
%files
%doc README TODO COPYING ChangeLog
%{_bindir}/*
%{_mandir}/man8/*
%config(noreplace) %{_sysconfdir}/*
%clean
%{__rm} -rf %{buildroot}
%changelog
* Fri Dec 19 2016 Ivan Alonso <kaian@irontec.com> - 1.4.2
- Version 1.4.2
* Fri Oct 28 2016 Ivan Alonso <kaian@irontec.com> - 1.4.1
- Version 1.4.1
* Tue Aug 23 2016 Ivan Alonso <kaian@irontec.com> - 1.4.0
- Version 1.4.0
* Mon Mar 28 2016 Ivan Alonso <kaian@irontec.com> - 1.3.1
- Version 1.3.1
* Tue Mar 15 2016 Ivan Alonso <kaian@irontec.com> - 1.3.0
- Version 1.3.0
* Thu Dec 10 2015 Ivan Alonso <kaian@irontec.com> - 1.2.0
- Version 1.2.0
* Wed Oct 28 2015 Ivan Alonso <kaian@irontec.com> - 1.1.0
- Version 1.1.0
* Tue Oct 06 2015 Ivan Alonso <kaian@irontec.com> - 1.0.0
- Version 1.0.0
* Mon Aug 31 2015 Ivan Alonso <kaian@irontec.com> - 0.4.2
- Version 0.4.2
* Tue Jul 07 2015 Ivan Alonso <kaian@irontec.com> - 0.4.1
- Version 0.4.1
* Mon Jun 29 2015 Ivan Alonso <kaian@irontec.com> - 0.4.0
- Version 0.4.0
* Tue Apr 14 2015 Ivan Alonso <kaian@irontec.com> - 0.3.1
- Version 0.3.1
* Wed Mar 04 2015 Ivan Alonso <kaian@irontec.com> - 0.3.0
- First RPM version of sngrep
......@@ -121,10 +121,8 @@ capture_online(const char *dev, const char *outfile)
// Try to find capture device information
if (pcap_lookupnet(dev, &capinfo->net, &capinfo->mask, errbuf) == -1) {
fprintf(stderr, "Can't get netmask for device %s\n", dev);
capinfo->net = 0;
capinfo->mask = 0;
return 2;
}
// Open capture device
......
......@@ -47,7 +47,12 @@
#define _BSD_SOURCE 1
#endif
#if defined (__OpenBSD__)
/* Old versions of libpcap in OpenBSD use <net/bpf.h>
* which actually defines timestamps as bpf_timeval instead
* of simple timeval. This no longer happens in newest libpcap
* versions, where header packets have timestamps in timeval
* structs */
#if defined (__OpenBSD__) && defined(_NET_BPF_H_)
#define timeval bpf_timeval
#endif
......
This diff is collapsed.
......@@ -48,8 +48,6 @@
#include "config.h"
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#include <gnutls/abstract.h>
#include <gnutls/x509.h>
#include <gcrypt.h>
#include "capture.h"
......@@ -88,6 +86,25 @@ enum SSLConnectionState {
TCP_STATE_CLOSED
};
//! SSL Encoders algo
enum SSLCipherEncoders {
ENC_AES = 1,
ENC_AES256 = 2
};
//! SSL Digests algo
enum SSLCIpherDigest {
DIG_SHA1 = 1,
DIG_SHA256 = 2,
DIG_SHA384 = 3
};
//! SSL Decode mode
enum SSLCipherMode {
MODE_CBC,
MODE_GCM
};
//! ContentType values as defined in RFC5246
enum ContentType {
change_cipher_spec = 20,
......@@ -106,7 +123,6 @@ enum HandshakeType {
server_hello_done = GNUTLS_HANDSHAKE_SERVER_HELLO_DONE,
certificate_verify = GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY,
client_key_exchange = GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE,
new_session_ticket = GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
finished = GNUTLS_HANDSHAKE_FINISHED
};
......@@ -140,6 +156,25 @@ struct CipherSuite {
uint8_t cs2;
};
struct CipherData {
int num;
int enc;
int ivblock;
int bits;
int digest;
int diglen;
int mode;
};
struct ClientHelloSSLv2 {
struct ProtocolVersion client_version;
uint16 cipherlist_len;
uint16 sessionid_len;
uint16 random_len;
// CipherSuite cipher_suite;
// struct Random random;
};
//! ClientHello type in Handshake records
struct ClientHello {
struct ProtocolVersion client_version;
......@@ -204,20 +239,21 @@ struct SSLConnection {
gnutls_session_t ssl;
int ciph;
gnutls_privkey_t server_private_key;
gnutls_x509_privkey_t server_private_key;
struct Random client_random;
struct Random server_random;
struct CipherSuite cipher_suite;
struct CipherData cipher_data;
struct PreMasterSecret pre_master_secret;
struct MasterSecret master_secret;
struct tls_data {
uint8_t client_write_MAC_key[20];
uint8_t server_write_MAC_key[20];
uint8_t client_write_key[32];
uint8_t server_write_key[32];
uint8_t client_write_IV[16];
uint8_t server_write_IV[16];
uint8_t *client_write_MAC_key;
uint8_t *server_write_MAC_key;
uint8_t *client_write_key;
uint8_t *server_write_key;
uint8_t *client_write_IV;
uint8_t *server_write_IV;
} key_material;
gcry_cipher_hd_t client_cipher_ctx;
......@@ -246,25 +282,6 @@ int
P_hash(const char *digest, unsigned char *dest, int dlen, unsigned char *secret, int sslen,
unsigned char *seed, int slen);
/**
* @brief Pseudorandom Function as defined in RFC5246
*
* This function will generate MasterSecret and KeyMaterial data from PreMasterSecret and Seed
*
* @param dest Destination of PRF function result. Memory must be already allocated
* @param dlen Destination length in bytes
* @param pre_master_secret PreMasterSecret decrypted from ClientKeyExchange Handhsake record
* @param pslen PreMasterSecret length in bytes
* @param label Fixed ASCII string
* @param seed Concatenation of Random data from Hello Handshake records
* @param slen Seed length in bytes
* @return destination length in bytes
*/
int
PRF12(unsigned char *dest, int dlen, unsigned char *pre_master_secret,
int plen, unsigned char *label, unsigned char *seed, int slen);
/**
* @brief Pseudorandom Function as defined in RFC2246
*
......@@ -280,7 +297,7 @@ PRF12(unsigned char *dest, int dlen, unsigned char *pre_master_secret,
* @return destination length in bytes
*/
int
PRF(unsigned char *dest, int dlen, unsigned char *pre_master_secret,
PRF(struct SSLConnection *conn, unsigned char *dest, int dlen, unsigned char *pre_master_secret,
int plen, unsigned char *label, unsigned char *seed, int slen);
/**
......@@ -345,7 +362,7 @@ tls_connection_dir(struct SSLConnection *conn, struct in_addr addr, uint16_t por
* @return an existing Connection pointer or NULL if not found
*/
struct SSLConnection*
tls_connection_find(struct in_addr addr, uint16_t port);
tls_connection_find(struct in_addr src, uint16_t sport, struct in_addr dst, uint16_t dport);
/**
* @brief Process a TCP segment to check TLS data
......@@ -379,6 +396,37 @@ int
tls_process_record(struct SSLConnection *conn, const uint8_t *payload, const int len, uint8_t **out,
uint32_t *outl);
/**
* @brief Check if this Record looks like SSLv2
*
* Some devices send the initial ClientHello in a SSLv2 record for compatibility
* with only SSLv2 protocol.
*
* We will only parse SSLv2 Client hello fragments that have TLSv1/SSLv3 content
* so this does not make us SSLv2 compatible ;-p
* @param conn Existing connection pointer
* @param payload Packet peyload
* @param len Payload length
* @return 1 if payload seems a SSLv2 record, 0 otherwise
*/
int
tls_record_handshake_is_ssl2(struct SSLConnection *conn, const uint8_t *payload,
const int len);
/**
* @brief Process TLS Handshake SSLv2 record types
*
* Process all types of Handshake records to store and compute all required
* data to decrypt application data packets
*
* @param conn Existing connection pointer
* @param fragment Handshake record data
* @param len Decimal length of the fragment
* @return 0 on valid record processed, 1 otherwise
*/
int
tls_process_record_ssl2(struct SSLConnection *conn, const uint8_t *payload,
const int len, uint8_t **out, uint32_t *outl);
/**
* @brief Process TLS Handshake record types
*
......@@ -424,4 +472,31 @@ tls_process_record_data(struct SSLConnection *conn, const opaque *fragment, cons
int
tls_connection_load_cipher(struct SSLConnection *conn);
/**
* @brief Determine if the given version is valid for us
*
* We only handle some SSL/TLS versions. This function will filter out
* records from unsupported versions.
*
* @return 0 if the version is supported, 1 otherwise
*/
int
tls_valid_version(struct ProtocolVersion version);
/**
* @brief Decrypt data using private RSA key
*
* This function code has been taken from wireshark.
* Because wireshark simply rocks.
*
* @param key Imported RSA key data
* @param flags decrpyt flag (no used)
* @param ciphertext Encrypted data
* @param plaintext Decrypted data
* @return number of bytes of decrypted data
*/
int
tls_privkey_decrypt_data(gnutls_x509_privkey_t key, unsigned int flags,
const gnutls_datum_t * ciphertext, gnutls_datum_t * plaintext);
#endif
......@@ -271,11 +271,16 @@ tls_connection_dir(struct SSLConnection *conn, struct in_addr addr, uint16_t por
}
struct SSLConnection*
tls_connection_find(struct in_addr addr, uint16_t port) {
tls_connection_find(struct in_addr src, uint16_t sport, struct in_addr dst, uint16_t dport) {
struct SSLConnection *conn;
for (conn = connections; conn; conn = conn->next) {
if (tls_connection_dir(conn, addr, port) != -1) {
if (tls_connection_dir(conn, src, sport) == 0 &&
tls_connection_dir(conn, dst, dport) == 1) {
return conn;
}
if (tls_connection_dir(conn, src, sport) == 1 &&
tls_connection_dir(conn, dst, dport) == 0) {
return conn;
}
}
......@@ -300,7 +305,7 @@ tls_process_segment(packet_t *packet, struct tcphdr *tcp)
inet_pton(AF_INET, packet->dst.ip, &ip_dst);
// Try to find a session for this ip
if ((conn = tls_connection_find(ip_src, sport))) {
if ((conn = tls_connection_find(ip_src, sport, ip_dst, dport))) {
// Update last connection direction
conn->direction = tls_connection_dir(conn, ip_src, sport);
......@@ -318,13 +323,22 @@ tls_process_segment(packet_t *packet, struct tcphdr *tcp)
break;
case TCP_STATE_ACK:
case TCP_STATE_ESTABLISHED:
// Process data segment!
if (tls_process_record(conn, payload, size_payload, &out, &outl) == 0) {
if ((int32_t) outl > 0) {
packet_set_payload(packet, out, outl);
packet_set_type(packet, PACKET_SIP_TLS);
return 0;
}
// Check if we have a SSLv2 Handshake
if(tls_record_handshake_is_ssl2(conn, payload, size_payload)) {
if (tls_process_record_ssl2(conn, payload, size_payload, &out, &outl) != 0)
outl = 0;
} else {
// Process data segment!
if (tls_process_record(conn, payload, size_payload, &out, &outl) != 0)
outl = 0;
}
// This seems a SIP TLS packet ;-)
if ((int32_t) outl > 0) {
packet_set_payload(packet, out, outl);
packet_set_type(packet, PACKET_SIP_TLS);
return 0;
}
break;
case TCP_STATE_FIN:
......@@ -344,6 +358,88 @@ tls_process_segment(packet_t *packet, struct tcphdr *tcp)
return 0;
}
int
tls_record_handshake_is_ssl2(struct SSLConnection *conn, const uint8_t *payload,
const int len)
{
// This magic belongs to wireshark people <3
if (len < 3) return 0;
// v2 client hello should start this way
if (payload[0] != 0x80) return 0;
// v2 client hello msg type
if (payload[2] != 0x01) return 0;
// Seems SSLv2
return 1;
}
int
tls_process_record_ssl2(struct SSLConnection *conn, const uint8_t *payload,
const int len, uint8_t **out, uint32_t *outl)
{
int record_len_len;
uint16 record_len16;
uint24 record_len24;
uint8_t record_type;
const opaque *fragment;
int flen;
// No record data here!
if (len == 0)
return 0;
// Record header length
record_len_len = (payload[0] & 0x80) ? 2 : 3;
// Two bytes SSLv2 record length field
if (record_len_len == 2) {
record_len16.x[0] = (payload[0] & 0x7f) << 8;
record_len16.x[1] = (payload[1]);
record_type = payload[2];
fragment = payload + 3;
flen = UINT16_INT(record_len16) - 1 /* record type */;
} else {
record_len24.x[0] = (payload[0] & 0x3f) << 8;
record_len24.x[1] = payload[1];
record_len24.x[2] = payload[2];
record_type = payload[3];
fragment = payload + 4;
flen = UINT24_INT(record_len24) - 1 /* record type */;
}
// We only handle Client Hello handshake SSLv2 records
if (record_type == 0x01 && flen > sizeof(struct ClientHelloSSLv2)) {
// Client Hello SSLv2
struct ClientHelloSSLv2 *clienthello = (struct ClientHelloSSLv2 *) fragment;
// Check we have a TLS handshake
if (clienthello->client_version.major != 0x03) {
tls_connection_destroy(conn);
return 1;
}
// Only TLS 1.0, 1.1 or 1.2 connections
if (clienthello->client_version.minor != 0x01
&& clienthello->client_version.minor != 0x02
&& clienthello->client_version.minor != 0x03) {
tls_connection_destroy(conn);
return 1;
}
// Store TLS version
conn->version = clienthello->client_version.minor;
// Calculate where client random starts
const opaque *random = fragment + sizeof(struct ClientHelloSSLv2)
+ UINT16_INT(clienthello->cipherlist_len)
+ UINT16_INT(clienthello->sessionid_len);
// Get Client random
memcpy(&conn->client_random, random, sizeof(struct Random));
}
return 0;
}
int
tls_process_record(struct SSLConnection *conn, const uint8_t *payload,
const int len, uint8_t **out, uint32_t *outl)
......@@ -520,7 +616,9 @@ tls_process_record_handshake(struct SSLConnection *conn, const opaque *fragment,
0);
break;
#ifndef OLD_OPENSSL_VERSION
case new_session_ticket:
#endif
case finished:
break;
default:
......
......@@ -51,6 +51,7 @@
#include <openssl/tls1.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include "capture.h"
//! Cast two bytes into decimal (Big Endian)
......@@ -58,6 +59,12 @@
//! Cast three bytes into decimal (Big Endian)
#define UINT24_INT(i) ((i.x[0] << 16) | (i.x[1] << 8) | i.x[2])
//The symbol SSL3_MT_NEWSESSION_TICKET appears to have been introduced at around
//openssl 0.9.8f, and the use of if breaks builds with older openssls
#if OPENSSL_VERSION_NUMBER < 0x00908070L
#define OLD_OPENSSL_VERSION 1
#endif
//! Three bytes unsigned integer
typedef struct uint16 {
unsigned char x[2];
......@@ -105,7 +112,9 @@ enum HandshakeType {
server_hello_done = SSL3_MT_SERVER_DONE,
certificate_verify = SSL3_MT_CERTIFICATE_VERIFY,
client_key_exchange = SSL3_MT_CLIENT_KEY_EXCHANGE,
#ifndef OLD_OPENSSL_VERSION
new_session_ticket = SSL3_MT_NEWSESSION_TICKET,
#endif
finished = SSL3_MT_FINISHED
};
......@@ -139,6 +148,15 @@ struct CipherSuite {
uint8_t cs2;
};
struct ClientHelloSSLv2 {
struct ProtocolVersion client_version;
uint16 cipherlist_len;
uint16 sessionid_len;
uint16 random_len;
// CipherSuite cipher_suite;
// struct Random random;
};
//! ClientHello type in Handshake records
struct ClientHello {
struct ProtocolVersion client_version;
......@@ -344,7 +362,7 @@ tls_connection_dir(struct SSLConnection *conn, struct in_addr addr, uint16_t por
* @return an existing Connection pointer or NULL if not found
*/
struct SSLConnection*
tls_connection_find(struct in_addr addr, uint16_t port);
tls_connection_find(struct in_addr src, uint16_t sport, struct in_addr dst, uint16_t dport);
/**
* @brief Process a TCP segment to check TLS data
......@@ -378,6 +396,37 @@ int
tls_process_record(struct SSLConnection *conn, const uint8_t *payload, const int len, uint8_t **out,
uint32_t *outl);
/**
* @brief Check if this Record looks like SSLv2
*
* Some devices send the initial ClientHello in a SSLv2 record for compatibility
* with only SSLv2 protocol.
*
* We will only parse SSLv2 Client hello fragments that have TLSv1/SSLv3 content
* so this does not make us SSLv2 compatible ;-p
* @param conn Existing connection pointer
* @param payload Packet peyload
* @param len Payload length
* @return 1 if payload seems a SSLv2 record, 0 otherwise
*/
int
tls_record_handshake_is_ssl2(struct SSLConnection *conn, const uint8_t *payload,
const int len);
/**
* @brief Process TLS Handshake SSLv2 record types
*
* Process all types of Handshake records to store and compute all required
* data to decrypt application data packets
*
* @param conn Existing connection pointer
* @param fragment Handshake record data
* @param len Decimal length of the fragment
* @return 0 on valid record processed, 1 otherwise
*/
int
tls_process_record_ssl2(struct SSLConnection *conn, const uint8_t *payload,
const int len, uint8_t **out, uint32_t *outl);
/**
* @brief Process TLS Handshake record types
*
......
......@@ -196,10 +196,6 @@ call_flow_draw(ui_t *ui)
if (vector_iterator_current(&it) == info->first_arrow) {
info->scroll.pos = info->scroll.max;
}
// Skip RTP arrows if not displayed
if (arrow->type == CF_ARROW_RTP && setting_disabled(SETTING_CF_MEDIA)) {
continue;
}
info->scroll.max += call_flow_arrow_height(ui, arrow);
}
ui_scrollbar_draw(info->scroll);
......@@ -654,7 +650,8 @@ call_flow_draw_rtp_stream(ui_t *ui, call_flow_arrow_t *arrow, int cline)
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 origin and destination column
arrow->dcolumn = call_flow_column_get(ui, msg->call->callid, msg->packet->src);
}
}
......@@ -678,7 +675,20 @@ call_flow_draw_rtp_stream(ui_t *ui, call_flow_arrow_t *arrow, int cline)
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;
// Get origin and destination column
arrow->scolumn = call_flow_column_get(ui, msg->call->callid, msg->packet->src);
}
}
// Prefer message that configured this stream rather than any column
if (!arrow->scolumn) {
msg = stream->media->msg;
// If message and stream share the same IP address
if (address_equals(msg->packet->dst, stream->src)) {
// Reuse the msg arrow columns as destination column
if ((msgarrow = call_flow_arrow_find(ui, msg))) {
arrow->scolumn = call_flow_column_get(ui, msg->call->callid, msg->packet->dst);
}
}
}
......@@ -738,16 +748,23 @@ call_flow_draw_rtp_stream(ui_t *ui, call_flow_arrow_t *arrow, int cline)
}
}
// Check if displayed stream is active
int active = stream_is_active(stream);
// Clear the line
mvwprintw(win, cline, startpos + 2, "%*s", distance, "");
// Draw method
// Draw RTP arrow text
mvwprintw(win, cline, startpos + (distance) / 2 - strlen(text) / 2 + 2, "%s", text);
if (!setting_has_value(SETTING_CF_SDP_INFO, "compressed"))
cline++;
// Draw line between columns
mvwhline(win, cline, startpos + 2, ACS_HLINE, distance);
if (active)
mvwhline(win, cline, startpos + 2, '-', distance);
else
mvwhline(win, cline, startpos + 2, ACS_HLINE, distance);
// Write the arrow at the end of the message (two arrows if this is a retrans)
if (arrow_dir == CF_ARROW_RIGHT) {
if (!setting_has_value(SETTING_CF_SDP_INFO, "compressed")) {
......@@ -755,7 +772,7 @@ call_flow_draw_rtp_stream(ui_t *ui, call_flow_arrow_t *arrow, int cline)
mvwprintw(win, cline, endpos, "%d", stream->dst.port);
}
mvwaddch(win, cline, endpos - 2, '>');
if (arrow->rtp_count != stream_get_count(stream)) {
if (active) {
arrow->rtp_count = stream_get_count(stream);
arrow->rtp_ind_pos = (arrow->rtp_ind_pos + 1) % distance;
mvwaddch(win, cline, startpos + arrow->rtp_ind_pos + 2, '>');