forked from osmocom/wireshark
Qt: Cleanup Traffic Table
Remote traffic_table_ui.? and move the JSON stuff into endpoint. This is in preparation for larger work on both the conversation table as well as the endpoint table, and to start using Qt code in the UI where it should be used.
This commit is contained in:
parent
7079d881fa
commit
f5b5d2c3c9
|
@ -52,7 +52,6 @@ set(NONGENERATED_UI_SRC
|
||||||
text_import.c
|
text_import.c
|
||||||
text_import_regex.c
|
text_import_regex.c
|
||||||
time_shift.c
|
time_shift.c
|
||||||
traffic_table_ui.c
|
|
||||||
util.c
|
util.c
|
||||||
voip_calls.c
|
voip_calls.c
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
#include "ui/recent.h"
|
#include "ui/recent.h"
|
||||||
#include "ui/tap-tcp-stream.h"
|
#include "ui/tap-tcp-stream.h"
|
||||||
#include "ui/traffic_table_ui.h"
|
|
||||||
|
|
||||||
#include "wsutil/str_util.h"
|
#include "wsutil/str_util.h"
|
||||||
|
|
||||||
|
@ -52,6 +51,46 @@
|
||||||
// - The value of 'Rel start' and 'Duration' in "Conversations" no need too precise https://gitlab.com/wireshark/wireshark/-/issues/12803
|
// - The value of 'Rel start' and 'Duration' in "Conversations" no need too precise https://gitlab.com/wireshark/wireshark/-/issues/12803
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CONV_COLUMN_SRC_ADDR,
|
||||||
|
CONV_COLUMN_SRC_PORT,
|
||||||
|
CONV_COLUMN_DST_ADDR,
|
||||||
|
CONV_COLUMN_DST_PORT,
|
||||||
|
CONV_COLUMN_PACKETS,
|
||||||
|
CONV_COLUMN_BYTES,
|
||||||
|
CONV_COLUMN_PKT_AB,
|
||||||
|
CONV_COLUMN_BYTES_AB,
|
||||||
|
CONV_COLUMN_PKT_BA,
|
||||||
|
CONV_COLUMN_BYTES_BA,
|
||||||
|
CONV_COLUMN_START,
|
||||||
|
CONV_COLUMN_DURATION,
|
||||||
|
CONV_COLUMN_BPS_AB,
|
||||||
|
CONV_COLUMN_BPS_BA,
|
||||||
|
CONV_NUM_COLUMNS,
|
||||||
|
CONV_INDEX_COLUMN = CONV_NUM_COLUMNS
|
||||||
|
} conversation_column_type_e;
|
||||||
|
|
||||||
|
static char const *conv_column_titles[CONV_NUM_COLUMNS] = {
|
||||||
|
"Address A",
|
||||||
|
"Port A",
|
||||||
|
"Address B",
|
||||||
|
"Port B",
|
||||||
|
"Packets",
|
||||||
|
"Bytes",
|
||||||
|
"Packets A " UTF8_RIGHTWARDS_ARROW " B",
|
||||||
|
"Bytes A " UTF8_RIGHTWARDS_ARROW " B",
|
||||||
|
"Packets B " UTF8_RIGHTWARDS_ARROW " A",
|
||||||
|
"Bytes B " UTF8_RIGHTWARDS_ARROW " A",
|
||||||
|
"Rel Start",
|
||||||
|
"Duration",
|
||||||
|
"Bits/s A " UTF8_RIGHTWARDS_ARROW " B",
|
||||||
|
"Bits/s B " UTF8_RIGHTWARDS_ARROW " A"
|
||||||
|
};
|
||||||
|
|
||||||
|
static char const *conv_conn_a_title = "Connection A";
|
||||||
|
static char const *conv_conn_b_title = "Connection B";
|
||||||
|
static char const *conv_abs_start_title = "Abs Start";
|
||||||
|
|
||||||
static const QString table_name_ = QObject::tr("Conversation");
|
static const QString table_name_ = QObject::tr("Conversation");
|
||||||
ConversationDialog::ConversationDialog(QWidget &parent, CaptureFile &cf, int cli_proto_id, const char *filter) :
|
ConversationDialog::ConversationDialog(QWidget &parent, CaptureFile &cf, int cli_proto_id, const char *filter) :
|
||||||
TrafficTableDialog(parent, cf, filter, table_name_),
|
TrafficTableDialog(parent, cf, filter, table_name_),
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
#include <epan/to_str.h>
|
#include <epan/to_str.h>
|
||||||
|
|
||||||
#include "ui/recent.h"
|
#include "ui/recent.h"
|
||||||
#include "ui/traffic_table_ui.h"
|
|
||||||
|
|
||||||
|
#include "wsutil/filesystem.h"
|
||||||
#include "wsutil/file_util.h"
|
#include "wsutil/file_util.h"
|
||||||
#include "wsutil/pint.h"
|
#include "wsutil/pint.h"
|
||||||
#include "wsutil/str_util.h"
|
#include "wsutil/str_util.h"
|
||||||
|
@ -35,6 +35,47 @@
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonValue>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
ENDP_COLUMN_ADDR,
|
||||||
|
ENDP_COLUMN_PORT,
|
||||||
|
ENDP_COLUMN_PACKETS,
|
||||||
|
ENDP_COLUMN_BYTES,
|
||||||
|
ENDP_COLUMN_PKT_AB,
|
||||||
|
ENDP_COLUMN_BYTES_AB,
|
||||||
|
ENDP_COLUMN_PKT_BA,
|
||||||
|
ENDP_COLUMN_BYTES_BA,
|
||||||
|
ENDP_NUM_COLUMNS,
|
||||||
|
ENDP_COLUMN_GEO_COUNTRY = ENDP_NUM_COLUMNS,
|
||||||
|
ENDP_COLUMN_GEO_CITY,
|
||||||
|
ENDP_COLUMN_GEO_AS_NUM,
|
||||||
|
ENDP_COLUMN_GEO_AS_ORG,
|
||||||
|
ENDP_NUM_GEO_COLUMNS
|
||||||
|
} endpoint_column_type_e;
|
||||||
|
|
||||||
|
static char const *endp_column_titles[ENDP_NUM_GEO_COLUMNS] = {
|
||||||
|
"Address",
|
||||||
|
"Port",
|
||||||
|
"Packets",
|
||||||
|
"Bytes",
|
||||||
|
"Tx Packets",
|
||||||
|
"Tx Bytes",
|
||||||
|
"Rx Packets",
|
||||||
|
"Rx Bytes",
|
||||||
|
"Country",
|
||||||
|
"City",
|
||||||
|
"AS Number",
|
||||||
|
"AS Organization"
|
||||||
|
};
|
||||||
|
|
||||||
|
static char const *endp_conn_title = "Connection";
|
||||||
|
|
||||||
static const QString table_name_ = QObject::tr("Endpoint");
|
static const QString table_name_ = QObject::tr("Endpoint");
|
||||||
EndpointDialog::EndpointDialog(QWidget &parent, CaptureFile &cf, int cli_proto_id, const char *filter) :
|
EndpointDialog::EndpointDialog(QWidget &parent, CaptureFile &cf, int cli_proto_id, const char *filter) :
|
||||||
TrafficTableDialog(parent, cf, filter, table_name_)
|
TrafficTableDialog(parent, cf, filter, table_name_)
|
||||||
|
@ -180,6 +221,137 @@ void EndpointDialog::tabChanged()
|
||||||
map_bt_->setEnabled(cur_tree && cur_tree->hasGeoIPData());
|
map_bt_->setEnabled(cur_tree && cur_tree->hasGeoIPData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
EndpointDialog::writeEndpointGeoipMap(QFile * fp, bool json_only, hostlist_talker_t *const *hosts)
|
||||||
|
{
|
||||||
|
QTextStream out(fp);
|
||||||
|
|
||||||
|
if (!json_only) {
|
||||||
|
QFile ipmap(get_datafile_path("ipmap.html"));
|
||||||
|
|
||||||
|
if (!ipmap.open(QIODevice::ReadOnly)) {
|
||||||
|
QMessageBox::warning(this, tr("Map file error"), tr("Could not open base file %1 for reading: %2")
|
||||||
|
.arg(get_datafile_path("ipmap.html"))
|
||||||
|
.arg(g_strerror(errno))
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy ipmap.html to map file. */
|
||||||
|
QTextStream in(&ipmap);
|
||||||
|
QString line;
|
||||||
|
while (in.readLineInto(&line)) {
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||||
|
out << line << Qt::endl;
|
||||||
|
#else
|
||||||
|
out << line << endl;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
out << QString("<script id=\"ipmap-data\" type=\"application/json\">\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes a feature for each resolved address, the output will look like:
|
||||||
|
* {
|
||||||
|
* "type": "FeatureCollection",
|
||||||
|
* "features": [
|
||||||
|
* {
|
||||||
|
* "type": "Feature",
|
||||||
|
* "geometry": {
|
||||||
|
* "type": "Point",
|
||||||
|
* "coordinates": [ -97.821999, 37.750999 ]
|
||||||
|
* },
|
||||||
|
* "properties": {
|
||||||
|
* "ip": "8.8.4.4",
|
||||||
|
* "autonomous_system_number": 15169,
|
||||||
|
* "autonomous_system_organization": "Google LLC",
|
||||||
|
* "city": "(omitted, but key is shown for documentation reasons)",
|
||||||
|
* "country": "United States",
|
||||||
|
* "radius": 1000,
|
||||||
|
* "packets": 1,
|
||||||
|
* "bytes": 1543
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
QJsonObject root;
|
||||||
|
root["type"] = "FeatureCollection";
|
||||||
|
QJsonArray features;
|
||||||
|
|
||||||
|
/* Append map data. */
|
||||||
|
size_t count = 0;
|
||||||
|
const hostlist_talker_t *host;
|
||||||
|
for (hostlist_talker_t *const *iter = hosts; (host = *iter) != NULL; ++iter) {
|
||||||
|
QJsonObject arrEntry;
|
||||||
|
|
||||||
|
char addr[WS_INET6_ADDRSTRLEN];
|
||||||
|
const mmdb_lookup_t *result = NULL;
|
||||||
|
if (host->myaddress.type == AT_IPv4) {
|
||||||
|
const ws_in4_addr *ip4 = (const ws_in4_addr *)host->myaddress.data;
|
||||||
|
result = maxmind_db_lookup_ipv4(ip4);
|
||||||
|
ws_inet_ntop4(ip4, addr, sizeof(addr));
|
||||||
|
} else if (host->myaddress.type == AT_IPv6) {
|
||||||
|
const ws_in6_addr *ip6 = (const ws_in6_addr *)host->myaddress.data;
|
||||||
|
result = maxmind_db_lookup_ipv6(ip6);
|
||||||
|
ws_inet_ntop6(ip6, addr, sizeof(addr));
|
||||||
|
}
|
||||||
|
if (!maxmind_db_has_coords(result)) {
|
||||||
|
// result could be NULL if the caller did not trigger a lookup
|
||||||
|
// before. result->found could be FALSE if no MMDB entry exists.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
++count;
|
||||||
|
arrEntry["type"] = "Feature";
|
||||||
|
QJsonObject geometry;
|
||||||
|
geometry["type"] = "Point";
|
||||||
|
QJsonArray coordinates;
|
||||||
|
coordinates.append(QJsonValue(result->longitude));
|
||||||
|
coordinates.append(QJsonValue(result->latitude));
|
||||||
|
geometry["coordinates"] = coordinates;
|
||||||
|
arrEntry["geometry"] = geometry;
|
||||||
|
|
||||||
|
QJsonObject property;
|
||||||
|
property["ip"] = addr;
|
||||||
|
if (result->as_number && result->as_org) {
|
||||||
|
property["autonomous_system_number"] = QJsonValue((int)(result->as_number));
|
||||||
|
property["autonomous_system_organization"] = QJsonValue(result->as_org);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result->city)
|
||||||
|
property["city"] = result->city;
|
||||||
|
if (result->country)
|
||||||
|
property["country"] = result->country;
|
||||||
|
if (result->accuracy)
|
||||||
|
property["radius"] = QJsonValue(result->accuracy);
|
||||||
|
|
||||||
|
property["packets"] = QJsonValue((qint64)(host->rx_frames + host->tx_frames));
|
||||||
|
property["bytes"] = QJsonValue((qint64)(host->rx_bytes + host->tx_bytes));
|
||||||
|
arrEntry["properties"] = property;
|
||||||
|
features.append(arrEntry);
|
||||||
|
}
|
||||||
|
root["features"] = features;
|
||||||
|
QJsonDocument doc;
|
||||||
|
doc.setObject(root);
|
||||||
|
|
||||||
|
out << doc.toJson();
|
||||||
|
|
||||||
|
if (!json_only)
|
||||||
|
out << QString("</script>\n");
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
QMessageBox::warning(this, tr("Map file error"), tr("No endpoints available to map"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.flush();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
QUrl EndpointDialog::createMap(bool json_only)
|
QUrl EndpointDialog::createMap(bool json_only)
|
||||||
{
|
{
|
||||||
EndpointTreeWidget *cur_tree = qobject_cast<EndpointTreeWidget *>(trafficTableTabWidget()->currentWidget());
|
EndpointTreeWidget *cur_tree = qobject_cast<EndpointTreeWidget *>(trafficTableTabWidget()->currentWidget());
|
||||||
|
@ -214,49 +386,12 @@ QUrl EndpointDialog::createMap(bool json_only)
|
||||||
return QUrl();
|
return QUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
if (!writeEndpointGeoipMap(&tf, json_only, hosts)) {
|
||||||
// XXX - At least with Qt 5.12 retrieving the name only works when
|
|
||||||
// it has been retrieved at least once when the file is open.
|
|
||||||
//
|
|
||||||
QString tempfilename = tf.fileName();
|
|
||||||
int fd = tf.handle();
|
|
||||||
//
|
|
||||||
// XXX - QFileDevice.handle() can return -1, but can QTemporaryFile.handle()
|
|
||||||
// do so if QTemporaryFile.open() has succeeded?
|
|
||||||
//
|
|
||||||
if (fd == -1) {
|
|
||||||
QMessageBox::warning(this, tr("Map file error"), tr("Unable to create temporary file"));
|
|
||||||
g_free(hosts);
|
g_free(hosts);
|
||||||
return QUrl();
|
tf.close();
|
||||||
}
|
|
||||||
// duplicate file descriptor as it is not allowed to perform a fclose before closing QFile
|
|
||||||
int duped_fd = ws_dup(fd);
|
|
||||||
if (duped_fd == -1) {
|
|
||||||
QMessageBox::warning(this, tr("Map file error"), tr("Unable to create temporary file"));
|
|
||||||
g_free(hosts);
|
|
||||||
return QUrl();
|
|
||||||
}
|
|
||||||
FILE* fp = ws_fdopen(duped_fd, "wb");
|
|
||||||
if (fp == NULL) {
|
|
||||||
QMessageBox::warning(this, tr("Map file error"), tr("Unable to create temporary file"));
|
|
||||||
g_free(hosts);
|
|
||||||
ws_close(duped_fd);
|
|
||||||
return QUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
gchar *err_str;
|
|
||||||
if (!write_endpoint_geoip_map(fp, json_only, hosts, &err_str)) {
|
|
||||||
QMessageBox::warning(this, tr("Map file error"), err_str);
|
|
||||||
g_free(err_str);
|
|
||||||
g_free(hosts);
|
|
||||||
fclose(fp);
|
|
||||||
return QUrl();
|
return QUrl();
|
||||||
}
|
}
|
||||||
g_free(hosts);
|
g_free(hosts);
|
||||||
if (fclose(fp) == EOF) {
|
|
||||||
QMessageBox::warning(this, tr("Map file error"), g_strerror(errno));
|
|
||||||
return QUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
tf.setAutoRemove(false);
|
tf.setAutoRemove(false);
|
||||||
return QUrl::fromLocalFile(tf.fileName());
|
return QUrl::fromLocalFile(tf.fileName());
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#ifndef ENDPOINT_DIALOG_H
|
#ifndef ENDPOINT_DIALOG_H
|
||||||
#define ENDPOINT_DIALOG_H
|
#define ENDPOINT_DIALOG_H
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
#include "traffic_table_dialog.h"
|
#include "traffic_table_dialog.h"
|
||||||
|
|
||||||
class EndpointTreeWidget : public TrafficTableTreeWidget
|
class EndpointTreeWidget : public TrafficTableTreeWidget
|
||||||
|
@ -68,6 +70,7 @@ private:
|
||||||
QPushButton *map_bt_;
|
QPushButton *map_bt_;
|
||||||
|
|
||||||
QUrl createMap(bool json_only);
|
QUrl createMap(bool json_only);
|
||||||
|
bool writeEndpointGeoipMap(QFile * fp, bool json_only, hostlist_talker_t *const *hosts);
|
||||||
#endif
|
#endif
|
||||||
bool addTrafficTable(register_ct_t* table);
|
bool addTrafficTable(register_ct_t* table);
|
||||||
|
|
||||||
|
|
|
@ -1,220 +0,0 @@
|
||||||
/* traffic_table_ui.c
|
|
||||||
* Helper routines common to conversation/endpoint tables.
|
|
||||||
*
|
|
||||||
* Wireshark - Network traffic analyzer
|
|
||||||
* By Gerald Combs <gerald@wireshark.org>
|
|
||||||
* Copyright 1998 Gerald Combs
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
|
|
||||||
#include "traffic_table_ui.h"
|
|
||||||
#include <wsutil/utf8_entities.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_MAXMINDDB
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include "wsutil/filesystem.h"
|
|
||||||
#include "wsutil/file_util.h"
|
|
||||||
#include "wsutil/json_dumper.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const char *conv_column_titles[CONV_NUM_COLUMNS] = {
|
|
||||||
"Address A",
|
|
||||||
"Port A",
|
|
||||||
"Address B",
|
|
||||||
"Port B",
|
|
||||||
"Packets",
|
|
||||||
"Bytes",
|
|
||||||
"Packets A " UTF8_RIGHTWARDS_ARROW " B",
|
|
||||||
"Bytes A " UTF8_RIGHTWARDS_ARROW " B",
|
|
||||||
"Packets B " UTF8_RIGHTWARDS_ARROW " A",
|
|
||||||
"Bytes B " UTF8_RIGHTWARDS_ARROW " A",
|
|
||||||
"Rel Start",
|
|
||||||
"Duration",
|
|
||||||
"Bits/s A " UTF8_RIGHTWARDS_ARROW " B",
|
|
||||||
"Bits/s B " UTF8_RIGHTWARDS_ARROW " A"
|
|
||||||
};
|
|
||||||
|
|
||||||
const char *conv_conn_a_title = "Connection A";
|
|
||||||
const char *conv_conn_b_title = "Connection B";
|
|
||||||
const char *conv_abs_start_title = "Abs Start";
|
|
||||||
|
|
||||||
const char *endp_column_titles[ENDP_NUM_GEO_COLUMNS] = {
|
|
||||||
"Address",
|
|
||||||
"Port",
|
|
||||||
"Packets",
|
|
||||||
"Bytes",
|
|
||||||
"Tx Packets",
|
|
||||||
"Tx Bytes",
|
|
||||||
"Rx Packets",
|
|
||||||
"Rx Bytes",
|
|
||||||
"Country",
|
|
||||||
"City",
|
|
||||||
"AS Number",
|
|
||||||
"AS Organization"
|
|
||||||
};
|
|
||||||
|
|
||||||
const char *endp_conn_title = "Connection";
|
|
||||||
|
|
||||||
#ifdef HAVE_MAXMINDDB
|
|
||||||
gboolean
|
|
||||||
write_endpoint_geoip_map(FILE *fp, gboolean json_only, hostlist_talker_t *const *hosts, gchar **err_str)
|
|
||||||
{
|
|
||||||
if (!json_only) {
|
|
||||||
char *base_html_path = get_datafile_path("ipmap.html");
|
|
||||||
FILE *base_html_fp = ws_fopen(base_html_path, "rb");
|
|
||||||
if (!base_html_fp) {
|
|
||||||
*err_str = ws_strdup_printf("Could not open base file %s for reading: %s",
|
|
||||||
base_html_path, g_strerror(errno));
|
|
||||||
g_free(base_html_path);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
g_free(base_html_path);
|
|
||||||
|
|
||||||
/* Copy ipmap.html to map file. */
|
|
||||||
size_t n;
|
|
||||||
char buf[4096];
|
|
||||||
while ((n = fread(buf, 1, sizeof(buf), base_html_fp)) != 0) {
|
|
||||||
if (fwrite(buf, 1, n, fp) != n) {
|
|
||||||
*err_str = ws_strdup_printf("Failed to write to map file: %s", g_strerror(errno));
|
|
||||||
fclose(base_html_fp);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ferror(base_html_fp)) {
|
|
||||||
*err_str = ws_strdup_printf("Failed to read base file: %s", g_strerror(errno));
|
|
||||||
fclose(base_html_fp);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
fclose(base_html_fp);
|
|
||||||
|
|
||||||
fputs("<script id=\"ipmap-data\" type=\"application/json\">\n", fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Writes a feature for each resolved address, the output will look like:
|
|
||||||
* {
|
|
||||||
* "type": "FeatureCollection",
|
|
||||||
* "features": [
|
|
||||||
* {
|
|
||||||
* "type": "Feature",
|
|
||||||
* "geometry": {
|
|
||||||
* "type": "Point",
|
|
||||||
* "coordinates": [ -97.821999, 37.750999 ]
|
|
||||||
* },
|
|
||||||
* "properties": {
|
|
||||||
* "ip": "8.8.4.4",
|
|
||||||
* "autonomous_system_number": 15169,
|
|
||||||
* "autonomous_system_organization": "Google LLC",
|
|
||||||
* "city": "(omitted, but key is shown for documentation reasons)",
|
|
||||||
* "country": "United States",
|
|
||||||
* "radius": 1000,
|
|
||||||
* "packets": 1,
|
|
||||||
* "bytes": 1543
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ]
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
json_dumper dumper = {
|
|
||||||
.output_file = fp,
|
|
||||||
.flags = JSON_DUMPER_FLAGS_PRETTY_PRINT
|
|
||||||
};
|
|
||||||
json_dumper_begin_object(&dumper);
|
|
||||||
json_dumper_set_member_name(&dumper, "type");
|
|
||||||
json_dumper_value_string(&dumper, "FeatureCollection");
|
|
||||||
json_dumper_set_member_name(&dumper, "features");
|
|
||||||
json_dumper_begin_array(&dumper);
|
|
||||||
|
|
||||||
/* Append map data. */
|
|
||||||
size_t count = 0;
|
|
||||||
const hostlist_talker_t *host;
|
|
||||||
for (hostlist_talker_t *const *iter = hosts; (host = *iter) != NULL; ++iter) {
|
|
||||||
char addr[WS_INET6_ADDRSTRLEN];
|
|
||||||
const mmdb_lookup_t *result = NULL;
|
|
||||||
if (host->myaddress.type == AT_IPv4) {
|
|
||||||
const ws_in4_addr *ip4 = (const ws_in4_addr *)host->myaddress.data;
|
|
||||||
result = maxmind_db_lookup_ipv4(ip4);
|
|
||||||
ws_inet_ntop4(ip4, addr, sizeof(addr));
|
|
||||||
} else if (host->myaddress.type == AT_IPv6) {
|
|
||||||
const ws_in6_addr *ip6 = (const ws_in6_addr *)host->myaddress.data;
|
|
||||||
result = maxmind_db_lookup_ipv6(ip6);
|
|
||||||
ws_inet_ntop6(ip6, addr, sizeof(addr));
|
|
||||||
}
|
|
||||||
if (!maxmind_db_has_coords(result)) {
|
|
||||||
// result could be NULL if the caller did not trigger a lookup
|
|
||||||
// before. result->found could be FALSE if no MMDB entry exists.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
++count;
|
|
||||||
json_dumper_begin_object(&dumper);
|
|
||||||
|
|
||||||
json_dumper_set_member_name(&dumper, "type");
|
|
||||||
json_dumper_value_string(&dumper, "Feature");
|
|
||||||
|
|
||||||
json_dumper_set_member_name(&dumper, "geometry");
|
|
||||||
{
|
|
||||||
json_dumper_begin_object(&dumper);
|
|
||||||
json_dumper_set_member_name(&dumper, "type");
|
|
||||||
json_dumper_value_string(&dumper, "Point");
|
|
||||||
json_dumper_set_member_name(&dumper, "coordinates");
|
|
||||||
json_dumper_begin_array(&dumper);
|
|
||||||
json_dumper_value_double(&dumper, result->longitude);
|
|
||||||
json_dumper_value_double(&dumper, result->latitude);
|
|
||||||
json_dumper_end_array(&dumper); // end coordinates
|
|
||||||
}
|
|
||||||
json_dumper_end_object(&dumper); // end geometry
|
|
||||||
|
|
||||||
json_dumper_set_member_name(&dumper, "properties");
|
|
||||||
json_dumper_begin_object(&dumper);
|
|
||||||
{
|
|
||||||
json_dumper_set_member_name(&dumper, "ip");
|
|
||||||
json_dumper_value_string(&dumper, addr);
|
|
||||||
if (result->as_number && result->as_org) {
|
|
||||||
json_dumper_set_member_name(&dumper, "autonomous_system_number");
|
|
||||||
json_dumper_value_anyf(&dumper, "%u", result->as_number);
|
|
||||||
json_dumper_set_member_name(&dumper, "autonomous_system_organization");
|
|
||||||
json_dumper_value_string(&dumper, result->as_org);
|
|
||||||
}
|
|
||||||
if (result->city) {
|
|
||||||
json_dumper_set_member_name(&dumper, "city");
|
|
||||||
json_dumper_value_string(&dumper, result->city);
|
|
||||||
}
|
|
||||||
if (result->country) {
|
|
||||||
json_dumper_set_member_name(&dumper, "country");
|
|
||||||
json_dumper_value_string(&dumper, result->country);
|
|
||||||
}
|
|
||||||
if (result->accuracy) {
|
|
||||||
json_dumper_set_member_name(&dumper, "radius");
|
|
||||||
json_dumper_value_anyf(&dumper, "%u", result->accuracy);
|
|
||||||
}
|
|
||||||
json_dumper_set_member_name(&dumper, "packets");
|
|
||||||
json_dumper_value_anyf(&dumper, "%" PRIu64, host->rx_frames + host->tx_frames);
|
|
||||||
json_dumper_set_member_name(&dumper, "bytes");
|
|
||||||
json_dumper_value_anyf(&dumper, "%" PRIu64, host->rx_bytes + host->tx_bytes);
|
|
||||||
}
|
|
||||||
json_dumper_end_object(&dumper); // end properties
|
|
||||||
|
|
||||||
json_dumper_end_object(&dumper);
|
|
||||||
}
|
|
||||||
|
|
||||||
json_dumper_end_array(&dumper); // end features
|
|
||||||
json_dumper_end_object(&dumper);
|
|
||||||
json_dumper_finish(&dumper);
|
|
||||||
if (!json_only) {
|
|
||||||
fputs("</script>\n", fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count == 0) {
|
|
||||||
*err_str = g_strdup("No endpoints available to map");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -1,96 +0,0 @@
|
||||||
/** @file
|
|
||||||
*
|
|
||||||
* Helper routines common to conversation/endpoint tables.
|
|
||||||
*
|
|
||||||
* Wireshark - Network traffic analyzer
|
|
||||||
* By Gerald Combs <gerald@wireshark.org>
|
|
||||||
* Copyright 1998 Gerald Combs
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __TRAFFIC_TABLE_UI_H__
|
|
||||||
#define __TRAFFIC_TABLE_UI_H__
|
|
||||||
|
|
||||||
#ifdef HAVE_MAXMINDDB
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "epan/maxmind_db.h"
|
|
||||||
#include <epan/conversation_table.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif /* __cplusplus */
|
|
||||||
|
|
||||||
/** @file
|
|
||||||
* Conversation and endpoint lists.
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
CONV_COLUMN_SRC_ADDR,
|
|
||||||
CONV_COLUMN_SRC_PORT,
|
|
||||||
CONV_COLUMN_DST_ADDR,
|
|
||||||
CONV_COLUMN_DST_PORT,
|
|
||||||
CONV_COLUMN_PACKETS,
|
|
||||||
CONV_COLUMN_BYTES,
|
|
||||||
CONV_COLUMN_PKT_AB,
|
|
||||||
CONV_COLUMN_BYTES_AB,
|
|
||||||
CONV_COLUMN_PKT_BA,
|
|
||||||
CONV_COLUMN_BYTES_BA,
|
|
||||||
CONV_COLUMN_START,
|
|
||||||
CONV_COLUMN_DURATION,
|
|
||||||
CONV_COLUMN_BPS_AB,
|
|
||||||
CONV_COLUMN_BPS_BA,
|
|
||||||
CONV_NUM_COLUMNS,
|
|
||||||
CONV_INDEX_COLUMN = CONV_NUM_COLUMNS
|
|
||||||
} conversation_column_type_e;
|
|
||||||
|
|
||||||
extern const char *conv_column_titles[CONV_NUM_COLUMNS];
|
|
||||||
extern const char *conv_conn_a_title;
|
|
||||||
extern const char *conv_conn_b_title;
|
|
||||||
extern const char *conv_abs_start_title;
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
ENDP_COLUMN_ADDR,
|
|
||||||
ENDP_COLUMN_PORT,
|
|
||||||
ENDP_COLUMN_PACKETS,
|
|
||||||
ENDP_COLUMN_BYTES,
|
|
||||||
ENDP_COLUMN_PKT_AB,
|
|
||||||
ENDP_COLUMN_BYTES_AB,
|
|
||||||
ENDP_COLUMN_PKT_BA,
|
|
||||||
ENDP_COLUMN_BYTES_BA,
|
|
||||||
ENDP_NUM_COLUMNS,
|
|
||||||
ENDP_COLUMN_GEO_COUNTRY = ENDP_NUM_COLUMNS,
|
|
||||||
ENDP_COLUMN_GEO_CITY,
|
|
||||||
ENDP_COLUMN_GEO_AS_NUM,
|
|
||||||
ENDP_COLUMN_GEO_AS_ORG,
|
|
||||||
ENDP_NUM_GEO_COLUMNS
|
|
||||||
} endpoint_column_type_e;
|
|
||||||
|
|
||||||
extern const char *endp_column_titles[ENDP_NUM_GEO_COLUMNS];
|
|
||||||
|
|
||||||
extern const char *endp_conn_title;
|
|
||||||
|
|
||||||
#ifdef HAVE_MAXMINDDB
|
|
||||||
/**
|
|
||||||
* Writes an HTML file containing a map showing the geographical locations
|
|
||||||
* of IPv4 and IPv6 addresses.
|
|
||||||
*
|
|
||||||
* @param [in] fp File handle for writing the HTML file.
|
|
||||||
* @param [in] json_only Write GeoJSON data only.
|
|
||||||
* @param [in] hosts A NULL-terminated array of 'hostlist_talker_t'. A MMDB
|
|
||||||
* lookup should have been completed before for these addresses.
|
|
||||||
* @param [in,out] err_str Set to error string on failure. Error string must
|
|
||||||
* be g_freed. May be NULL.
|
|
||||||
* @return Whether the map file was successfully written with non-empty data.
|
|
||||||
*/
|
|
||||||
gboolean write_endpoint_geoip_map(FILE *fp, gboolean json_only, hostlist_talker_t *const *hosts, gchar **err_str);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif /* __cplusplus */
|
|
||||||
|
|
||||||
#endif /* __TRAFFIC_TABLE_UI_H__ */
|
|
Loading…
Reference in New Issue