Added DNS statistics support
This affects both the dissector (that has been added with a tap interface and a stats generator) and the UI (to recall the stats menu). Change-Id: I90658f7aa6707aa39bdd787a51b20fed4dbddc53 Reviewed-on: https://code.wireshark.org/review/6236 Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com>
This commit is contained in:
parent
087723adb0
commit
696fcdba21
6
NEWS
6
NEWS
|
@ -30,6 +30,12 @@ What's New
|
|||
+ The VoIP Calls and SIP Flows dialogs have been added.
|
||||
+ Support for HiDPI / Retina displays has been improved in the
|
||||
official packages.
|
||||
* DNS stats:
|
||||
+ A new stats tree has been added to the Statistics menu. Now it
|
||||
is possible to collect stats such as qtype/qclass distribution,
|
||||
number of resource record per response section, and stats data
|
||||
(min, max, avg) for values such as query name length or DNS
|
||||
payload.
|
||||
|
||||
The following features are new (or have been significantly updated)
|
||||
since version 1.12.0:
|
||||
|
|
|
@ -958,6 +958,11 @@ Several fields with same name within one diameter message are supported, e.g. I<
|
|||
|
||||
Note: B<tshark -q> option is recommended to suppress default B<tshark> output.
|
||||
|
||||
=item B<-z> dns,tree[,I<filter>]
|
||||
|
||||
Create a summary of the captured DNS packets. General information are collected such as qtype and qclass distribution.
|
||||
For some data (as qname length or DNS payload) max, min and average values are also displayed.
|
||||
|
||||
=item B<-z> endpoints,I<type>[,I<filter>]
|
||||
|
||||
Create a table that lists all endpoints that could be seen in the
|
||||
|
|
|
@ -42,10 +42,68 @@
|
|||
#include <epan/strutil.h>
|
||||
#include <epan/expert.h>
|
||||
#include <epan/afn.h>
|
||||
#include <epan/tap.h>
|
||||
#include <epan/stats_tree.h>
|
||||
|
||||
void proto_register_dns(void);
|
||||
void proto_reg_handoff_dns(void);
|
||||
|
||||
struct DnsTap {
|
||||
guint packet_qr;
|
||||
guint packet_qtype;
|
||||
guint packet_qclass;
|
||||
guint packet_rcode;
|
||||
guint packet_opcode;
|
||||
guint payload_size;
|
||||
guint qname_len;
|
||||
guint qname_labels;
|
||||
guint nquestions;
|
||||
guint nanswers;
|
||||
guint nauthorities;
|
||||
guint nadditionals;
|
||||
};
|
||||
|
||||
static int dns_tap = -1;
|
||||
|
||||
static const guint8* st_str_packets = "Total Packets";
|
||||
static const guint8* st_str_packet_qr = "Query/Response";
|
||||
static const guint8* st_str_packet_qtypes = "Query Type";
|
||||
static const guint8* st_str_packet_qclasses = "Class";
|
||||
static const guint8* st_str_packet_rcodes = "rcode";
|
||||
static const guint8* st_str_packet_opcodes = "opcodes";
|
||||
static const guint8* st_str_packets_avg_size = "Payload size";
|
||||
static const guint8* st_str_query_stats = "Query Stats";
|
||||
static const guint8* st_str_query_qname_len = "Qname Len";
|
||||
static const guint8* st_str_query_domains = "Label Stats";
|
||||
static const guint8* st_str_query_domains_l1 = "1st Level";
|
||||
static const guint8* st_str_query_domains_l2 = "2nd Level";
|
||||
static const guint8* st_str_query_domains_l3 = "3rd Level";
|
||||
static const guint8* st_str_query_domains_lmore = "4th Level or more";
|
||||
static const guint8* st_str_response_stats = "Response Stats";
|
||||
static const guint8* st_str_response_nquestions = "no. of questions";
|
||||
static const guint8* st_str_response_nanswers = "no. of answers";
|
||||
static const guint8* st_str_response_nauthorities = "no. of authorities";
|
||||
static const guint8* st_str_response_nadditionals = "no. of additionals";
|
||||
static int st_node_packets = -1;
|
||||
static int st_node_packet_qr = -1;
|
||||
static int st_node_packet_qtypes = -1;
|
||||
static int st_node_packet_qclasses = -1;
|
||||
static int st_node_packet_rcodes = -1;
|
||||
static int st_node_packet_opcodes = -1;
|
||||
static int st_node_packets_avg_size = -1;
|
||||
static int st_node_query_stats = -1;
|
||||
static int st_node_query_qname_len = -1;
|
||||
static int st_node_query_domains = -1;
|
||||
static int st_node_query_domains_l1 = -1;
|
||||
static int st_node_query_domains_l2 = -1;
|
||||
static int st_node_query_domains_l3 = -1;
|
||||
static int st_node_query_domains_lmore = -1;
|
||||
static int st_node_response_stats = -1;
|
||||
static int st_node_response_nquestions = -1;
|
||||
static int st_node_response_nanswers = -1;
|
||||
static int st_node_response_nauthorities = -1;
|
||||
static int st_node_response_nadditionals = -1;
|
||||
|
||||
static int proto_dns = -1;
|
||||
static int hf_dns_length = -1;
|
||||
static int hf_dns_flags = -1;
|
||||
|
@ -742,6 +800,11 @@ http://www.microsoft.com/windows2000/library/resources/reskit/samplechapters/cnc
|
|||
which discuss them to some extent. */
|
||||
/* http://www.iana.org/assignments/dns-parameters (last updated 2014-08-12)*/
|
||||
|
||||
static const value_string dns_qr_vals[] = {
|
||||
{ 0, "Query" },
|
||||
{ 1, "Response" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
static const value_string dns_types_vals[] = {
|
||||
{ 0, "Unused" },
|
||||
{ T_A, "A" },
|
||||
|
@ -1004,6 +1067,27 @@ const value_string dns_classes[] = {
|
|||
{0,NULL}
|
||||
};
|
||||
|
||||
|
||||
/* This function counts how many '.' are in the string, plus 1, in order to count the number
|
||||
* of labels
|
||||
*/
|
||||
guint
|
||||
qname_labels_count(const guchar* name, guint name_len)
|
||||
{
|
||||
guint labels = 0;
|
||||
unsigned i;
|
||||
|
||||
if (name_len > 1) {
|
||||
/* it was not a Zero-length name */
|
||||
for (i = 0; i < strlen(name); i++) {
|
||||
if (name[i] == '.')
|
||||
labels++;
|
||||
}
|
||||
labels++;
|
||||
}
|
||||
return labels;
|
||||
}
|
||||
|
||||
/* This function returns the number of bytes consumed and the expanded string
|
||||
* in *name.
|
||||
* The string is allocated with wmem_packet_scope scope and does not need to be freed.
|
||||
|
@ -1280,7 +1364,6 @@ dissect_dns_query(tvbuff_t *tvb, int offset, int dns_data_offset,
|
|||
int type;
|
||||
int dns_class;
|
||||
int qu;
|
||||
unsigned i;
|
||||
const char *type_name;
|
||||
int data_start;
|
||||
guint16 labels;
|
||||
|
@ -1291,6 +1374,7 @@ dissect_dns_query(tvbuff_t *tvb, int offset, int dns_data_offset,
|
|||
|
||||
len = get_dns_name_type_class(tvb, offset, dns_data_offset, &name, &name_len,
|
||||
&type, &dns_class);
|
||||
|
||||
if (is_mdns) {
|
||||
/* Split the QU flag and the class */
|
||||
qu = dns_class & C_QU;
|
||||
|
@ -1325,17 +1409,7 @@ dissect_dns_query(tvbuff_t *tvb, int offset, int dns_data_offset,
|
|||
tq = proto_tree_add_uint(q_tree, hf_dns_qry_name_len, tvb, offset, name_len, name_len > 1 ? (guint32)strlen(name) : 0);
|
||||
PROTO_ITEM_SET_GENERATED(tq);
|
||||
|
||||
/* Count how many '.' are in the string, plus 1, in order to count the number
|
||||
of labels */
|
||||
labels = 0;
|
||||
if (name_len > 1) {
|
||||
/* it was not a Zero-length name */
|
||||
for (i = 0; i < strlen(name); i++) {
|
||||
if (name[i] == '.')
|
||||
labels++;
|
||||
}
|
||||
labels++;
|
||||
}
|
||||
labels = qname_labels_count(name, name_len);
|
||||
tq = proto_tree_add_uint(q_tree, hf_dns_count_labels, tvb, offset, name_len, labels);
|
||||
PROTO_ITEM_SET_GENERATED(tq);
|
||||
|
||||
|
@ -1652,6 +1726,7 @@ dissect_dns_answer(tvbuff_t *tvb, int offsetx, int dns_data_offset,
|
|||
|
||||
len = get_dns_name_type_class(tvb, offsetx, dns_data_offset, &name, &name_len,
|
||||
&dns_type, &dns_class);
|
||||
|
||||
data_offset += len;
|
||||
cur_offset += len;
|
||||
if (is_mdns) {
|
||||
|
@ -3582,12 +3657,16 @@ dissect_dns_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|||
dns_conv_info_t *dns_info;
|
||||
dns_transaction_t *dns_trans;
|
||||
wmem_tree_key_t key[3];
|
||||
struct DnsTap *dns_stats;
|
||||
guint qtype = 0;
|
||||
guint qclass = 0;
|
||||
const guchar *name;
|
||||
int name_len;
|
||||
|
||||
dns_data_offset = offset;
|
||||
|
||||
col_clear(pinfo->cinfo, COL_INFO);
|
||||
|
||||
|
||||
/* To do: check for errs, etc. */
|
||||
id = tvb_get_ntohs(tvb, offset + DNS_ID);
|
||||
flags = tvb_get_ntohs(tvb, offset + DNS_FLAGS);
|
||||
|
@ -3809,12 +3888,28 @@ dissect_dns_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|||
}
|
||||
cur_off = offset + DNS_HDRLEN;
|
||||
|
||||
/* Collect stats */
|
||||
dns_stats = wmem_new0(wmem_packet_scope(), struct DnsTap);
|
||||
dns_stats->packet_rcode = rcode;
|
||||
dns_stats->packet_opcode = opcode;
|
||||
dns_stats->packet_qr = flags >> 15;
|
||||
get_dns_name_type_class(tvb, cur_off, dns_data_offset, &name, &name_len, &qtype, &qclass);
|
||||
dns_stats->packet_qtype = qtype;
|
||||
dns_stats->packet_qclass = qclass;
|
||||
dns_stats->payload_size = tvb_captured_length(tvb);
|
||||
dns_stats->nquestions = quest;
|
||||
dns_stats->nanswers = ans;
|
||||
dns_stats->nauthorities = auth;
|
||||
dns_stats->nadditionals = add;
|
||||
|
||||
if (quest > 0) {
|
||||
/* If this is a response, don't add information about the queries
|
||||
to the summary, just add information about the answers. */
|
||||
cur_off += dissect_query_records(tvb, cur_off, dns_data_offset, quest,
|
||||
(!(flags & F_RESPONSE) ? pinfo->cinfo : NULL),
|
||||
dns_tree, isupdate, is_mdns);
|
||||
dns_stats->qname_len = name_len;
|
||||
dns_stats->qname_labels = qname_labels_count(name, name_len);
|
||||
}
|
||||
|
||||
if (ans > 0) {
|
||||
|
@ -3842,6 +3937,7 @@ dissect_dns_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|||
NULL, dns_tree, "Additional records",
|
||||
pinfo, is_mdns);
|
||||
}
|
||||
tap_queue_packet(dns_tap, pinfo, dns_stats);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -3909,6 +4005,76 @@ dissect_dns_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
|
|||
return tvb_reported_length(tvb);
|
||||
}
|
||||
|
||||
static void dns_stats_tree_init(stats_tree* st)
|
||||
{
|
||||
st_node_packets = stats_tree_create_node(st, st_str_packets, 0, TRUE);
|
||||
st_node_packet_qr = stats_tree_create_pivot(st, st_str_packet_qr, st_node_packets);
|
||||
st_node_packet_qtypes = stats_tree_create_pivot(st, st_str_packet_qtypes, st_node_packets);
|
||||
st_node_packet_qclasses = stats_tree_create_pivot(st, st_str_packet_qclasses, st_node_packets);
|
||||
st_node_packet_rcodes = stats_tree_create_pivot(st, st_str_packet_rcodes, st_node_packets);
|
||||
st_node_packet_opcodes = stats_tree_create_pivot(st, st_str_packet_opcodes, st_node_packets);
|
||||
st_node_packets_avg_size = stats_tree_create_node(st, st_str_packets_avg_size, 0, FALSE);
|
||||
st_node_query_stats = stats_tree_create_node(st, st_str_query_stats, 0, TRUE);
|
||||
st_node_query_qname_len = stats_tree_create_node(st, st_str_query_qname_len, st_node_query_stats, FALSE);
|
||||
st_node_query_domains = stats_tree_create_node(st, st_str_query_domains, st_node_query_stats, TRUE);
|
||||
st_node_query_domains_l1 = stats_tree_create_node(st, st_str_query_domains_l1, st_node_query_domains, FALSE);
|
||||
st_node_query_domains_l2 = stats_tree_create_node(st, st_str_query_domains_l2, st_node_query_domains, FALSE);
|
||||
st_node_query_domains_l3 = stats_tree_create_node(st, st_str_query_domains_l3, st_node_query_domains, FALSE);
|
||||
st_node_query_domains_lmore = stats_tree_create_node(st, st_str_query_domains_lmore, st_node_query_domains, FALSE);
|
||||
st_node_response_stats = stats_tree_create_node(st, st_str_response_stats, 0, TRUE);
|
||||
st_node_response_nquestions = stats_tree_create_node(st, st_str_response_nquestions,
|
||||
st_node_response_stats, FALSE);
|
||||
st_node_response_nanswers = stats_tree_create_node(st, st_str_response_nanswers,
|
||||
st_node_response_stats, FALSE);
|
||||
st_node_response_nauthorities = stats_tree_create_node(st, st_str_response_nauthorities,
|
||||
st_node_response_stats, FALSE);
|
||||
st_node_response_nadditionals = stats_tree_create_node(st, st_str_response_nadditionals,
|
||||
st_node_response_stats, FALSE);
|
||||
}
|
||||
|
||||
static int dns_stats_tree_packet(stats_tree* st, packet_info* pinfo _U_, epan_dissect_t* edt _U_, const void* p)
|
||||
{
|
||||
struct DnsTap *pi = (struct DnsTap *)p;
|
||||
tick_stat_node(st, st_str_packets, 0, FALSE);
|
||||
stats_tree_tick_pivot(st, st_node_packet_qr,
|
||||
val_to_str(pi->packet_qr, dns_qr_vals, "Unknown qr (%d)"));
|
||||
stats_tree_tick_pivot(st, st_node_packet_qtypes,
|
||||
val_to_str(pi->packet_qtype, dns_types_description_vals, "Unknown packet type (%d)"));
|
||||
stats_tree_tick_pivot(st, st_node_packet_qclasses,
|
||||
val_to_str(pi->packet_qclass, dns_classes, "Unknown class (%d)"));
|
||||
stats_tree_tick_pivot(st, st_node_packet_rcodes,
|
||||
val_to_str(pi->packet_rcode, rcode_vals, "Unknown rcode (%d)"));
|
||||
stats_tree_tick_pivot(st, st_node_packet_opcodes,
|
||||
val_to_str(pi->packet_opcode, opcode_vals, "Unknown opcode (%d)"));
|
||||
avg_stat_node_add_value(st, st_str_packets_avg_size, 0, FALSE,
|
||||
pi->payload_size);
|
||||
|
||||
/* split up stats for queries and responses */
|
||||
if (pi->packet_qr == 0) {
|
||||
avg_stat_node_add_value(st, st_str_query_qname_len, 0, FALSE, pi->qname_len);
|
||||
switch(pi->qname_labels) {
|
||||
case 1:
|
||||
tick_stat_node(st, st_str_query_domains_l1, 0, FALSE);
|
||||
break;
|
||||
case 2:
|
||||
tick_stat_node(st, st_str_query_domains_l2, 0, FALSE);
|
||||
break;
|
||||
case 3:
|
||||
tick_stat_node(st, st_str_query_domains_l3, 0, FALSE);
|
||||
break;
|
||||
default:
|
||||
tick_stat_node(st, st_str_query_domains_lmore, 0, FALSE);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
avg_stat_node_add_value(st, st_str_response_nquestions, 0, FALSE, pi->nquestions);
|
||||
avg_stat_node_add_value(st, st_str_response_nanswers, 0, FALSE, pi->nanswers);
|
||||
avg_stat_node_add_value(st, st_str_response_nauthorities, 0, FALSE, pi->nauthorities);
|
||||
avg_stat_node_add_value(st, st_str_response_nadditionals, 0, FALSE, pi->nadditionals);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
proto_reg_handoff_dns(void)
|
||||
{
|
||||
|
@ -3924,6 +4090,7 @@ proto_reg_handoff_dns(void)
|
|||
if (!Initialized) {
|
||||
dns_udp_handle = create_dissector_handle(dissect_dns_udp, proto_dns);
|
||||
dns_tcp_handle = new_create_dissector_handle(dissect_dns_tcp, proto_dns);
|
||||
stats_tree_register("dns", "dns", "DNS", 0, dns_stats_tree_packet, dns_stats_tree_init, NULL);
|
||||
Initialized = TRUE;
|
||||
|
||||
} else {
|
||||
|
@ -5389,7 +5556,10 @@ proto_register_dns(void)
|
|||
&dns_use_for_addr_resolution);
|
||||
|
||||
dns_tsig_dissector_table = register_dissector_table("dns.tsig.mac", "DNS TSIG MAC Dissectors", FT_STRING, BASE_NONE);
|
||||
|
||||
dns_tap = register_tap("dns");
|
||||
}
|
||||
|
||||
/*
|
||||
* Editor modelines
|
||||
*
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
#define STAT_TREE_ROOT "root"
|
||||
|
||||
#define ST_FLG_AVERAGE 0x10000000 /* Calculate overages for nodes, rather than totals */
|
||||
#define ST_FLG_AVERAGE 0x10000000 /* Calculate averages for nodes, rather than totals */
|
||||
#define ST_FLG_ROOTCHILD 0x20000000 /* This node is a direct child of the root node */
|
||||
#define ST_FLG_DEF_NOEXPAND 0x01000000 /* This node should not be expanded by default */
|
||||
#define ST_FLG_SORT_DESC 0x00800000 /* When sorting, sort ascending instead of decending */
|
||||
|
|
|
@ -1043,6 +1043,7 @@ static const char *ui_desc_menubar =
|
|||
" </menu>\n"
|
||||
" <menuitem name='Collectd' action='/Statistics/collectd'/>\n"
|
||||
" <menuitem name='Compare' action='/Statistics/compare'/>\n"
|
||||
" <menuitem name= 'DNS' action='/Statistics/dns'/>\n"
|
||||
" <menuitem name='FlowGraph' action='/Statistics/FlowGraph'/>\n"
|
||||
" <menuitem name='HART-IP' action='/Statistics/hart_ip'/>\n"
|
||||
" <menu name= 'HTTPMenu' action='/Statistics/HTTP'>\n"
|
||||
|
@ -1473,16 +1474,15 @@ static const GtkActionEntry main_menu_bar_entries[] = {
|
|||
{ "/Statistics/BACnet/bacapp_ip", NULL, "Packets sorted by IP", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
|
||||
{ "/Statistics/BACnet/bacapp_objectid", NULL, "Packets sorted by Object Type", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
|
||||
{ "/Statistics/BACnet/bacapp_service", NULL, "Packets sorted by Service", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
|
||||
|
||||
{ "/Statistics/collectd", NULL, "Collectd...", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
|
||||
{ "/Statistics/compare", NULL, "Compare...", NULL, NULL, G_CALLBACK(gtk_comparestat_cb) },
|
||||
{ "/Statistics/dns", NULL, "DNS", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
|
||||
{ "/Statistics/FlowGraph", WIRESHARK_STOCK_FLOW_GRAPH, "Flo_w Graph...", NULL, NULL, G_CALLBACK(flow_graph_launch) },
|
||||
{ "/Statistics/hart_ip", NULL, "HART-IP", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
|
||||
{ "/Statistics/HTTP", NULL, "HTTP", NULL, NULL, NULL },
|
||||
{ "/Statistics/HTTP/http", NULL, "Packet Counter", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
|
||||
{ "/Statistics/HTTP/http_req", NULL, "Requests", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
|
||||
{ "/Statistics/HTTP/http_srv", NULL, "Load Distribution", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
|
||||
|
||||
{ "/Statistics/ONC-RPC-Programs", NULL, "ONC-RPC Programs", NULL, NULL, G_CALLBACK(gtk_rpcprogs_cb) },
|
||||
{ "/Statistics/Sametime", NULL, "Sametime", NULL, NULL, NULL },
|
||||
{ "/Statistics/Sametime/sametime", NULL, "Messages", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
|
||||
|
|
|
@ -417,6 +417,7 @@ private slots:
|
|||
void statCommandIOGraph(const char *arg = NULL, void *userdata = NULL);
|
||||
void on_actionStatisticsIOGraph_triggered();
|
||||
void on_actionStatisticsSametime_triggered();
|
||||
void on_actionStatisticsDNS_triggered();
|
||||
|
||||
void openVoipCallsDialog(bool all_flows = false);
|
||||
void on_actionTelephonyVoipCalls_triggered();
|
||||
|
|
|
@ -434,6 +434,7 @@
|
|||
<addaction name="actionStatisticsANCP"/>
|
||||
<addaction name="menuBACnet"/>
|
||||
<addaction name="actionStatisticsCollectd"/>
|
||||
<addaction name="actionStatisticsDNS"/>
|
||||
<addaction name="actionStatisticsFlowGraph"/>
|
||||
<addaction name="actionStatisticsHART_IP"/>
|
||||
<addaction name="menuHTTP"/>
|
||||
|
@ -1559,6 +1560,14 @@
|
|||
<string>Collectd statistics</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionStatisticsDNS">
|
||||
<property name="text">
|
||||
<string>DNS</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>DNS statistics</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionStatisticsHART_IP">
|
||||
<property name="text">
|
||||
<string>HART-IP</string>
|
||||
|
@ -1606,7 +1615,7 @@
|
|||
<property name="toolTip">
|
||||
<string>Sametime statistics</string>
|
||||
</property>
|
||||
</action>
|
||||
</action>
|
||||
<action name="actionTelephonyISUPMessages">
|
||||
<property name="text">
|
||||
<string>&ISUP Messages</string>
|
||||
|
|
|
@ -2365,11 +2365,17 @@ void MainWindow::on_actionStatisticsIOGraph_triggered()
|
|||
{
|
||||
statCommandIOGraph(NULL, NULL);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionStatisticsSametime_triggered()
|
||||
{
|
||||
openStatisticsTreeDialog("sametime");
|
||||
}
|
||||
|
||||
void MainWindow::on_actionStatisticsDNS_triggered()
|
||||
{
|
||||
openStatisticsTreeDialog("dns");
|
||||
}
|
||||
|
||||
// Telephony Menu
|
||||
|
||||
void MainWindow::openVoipCallsDialog(bool all_flows)
|
||||
|
|
Loading…
Reference in New Issue