summaryrefslogtreecommitdiff
path: root/src/monitor-gui.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/monitor-gui.cc')
-rw-r--r--src/monitor-gui.cc249
1 files changed, 249 insertions, 0 deletions
diff --git a/src/monitor-gui.cc b/src/monitor-gui.cc
index 8a2509d..d73c98d 100644
--- a/src/monitor-gui.cc
+++ b/src/monitor-gui.cc
@@ -14,6 +14,12 @@
#include <unordered_map>
#include <vector>
+#if HAVE_PCAP
+# include <arpa/inet.h>
+# include <netdb.h>
+# include <pcap/pcap.h>
+#endif
+
#include "config.hh"
#include "data.hh"
#include "gui_about.hh"
@@ -679,6 +685,11 @@ public:
file_filter_.emplace_back();
file_filter_.back().name = "TransparentProxy Packages (*.tpp)";
file_filter_.back().masks.emplace_back("*.tpp");
+#if HAVE_PCAP
+ file_filter_.emplace_back();
+ file_filter_.back().name = "Packet Capture (*.pcap)";
+ file_filter_.back().masks.emplace_back("*.pcap");
+#endif // HAVE_PCAP
file_filter_.emplace_back();
file_filter_.back().name = "All files";
file_filter_.back().masks.emplace_back("*.*");
@@ -1084,7 +1095,244 @@ private:
message->show(main_.get());
}
+#if HAVE_PCAP
+ static bool is_pcap(std::string const& file) {
+ return file.size() >= 5 && file.compare(file.size() - 5, 5, ".pcap") == 0;
+ }
+
+ bool load_pcap(std::string const& file, PackageList* packages) {
+ char errbuf[PCAP_ERRBUF_SIZE];
+ auto pcap = pcap_open_offline(file.c_str(), errbuf);
+ if (!pcap) {
+ show_error(errbuf);
+ return false;
+ }
+ size_t offset;
+ switch (pcap_datalink(pcap)) {
+ case DLT_RAW:
+ offset = 0;
+ break;
+ case DLT_EN10MB:
+ offset = 14;
+ break;
+ default:
+ snprintf(errbuf, sizeof(errbuf), "Unsupported dataline: %d",
+ pcap_datalink(pcap));
+ show_error(errbuf);
+ return false;
+ }
+ while (true) {
+ struct pcap_pkthdr* header;
+ u_char const* data;
+ auto ret = pcap_next_ex(pcap, &header, &data);
+ if (ret == 1) {
+ auto len = std::min(header->caplen, header->len);
+ if (len < offset) continue;
+ ::Package pkg;
+ pkg.timestamp.tv_sec = header->ts.tv_sec;
+ pkg.timestamp.tv_nsec = header->ts.tv_usec * 1000;
+ auto len_ip = is_ipv4(data + offset, len - offset, &pkg);
+ if (len_ip < 0) continue;
+ auto len_tcp = is_tcp(data + offset + len_ip, len - len_ip - offset,
+ &pkg);
+ if (len_tcp < 0) continue;
+ if (offset + len_ip + len_tcp == len) continue;
+ packages->package(pkg);
+ packages->package_data(pkg.id, reinterpret_cast<char const*>(data)
+ + offset + len_ip + len_tcp,
+ len - offset - len_ip - len_tcp, true);
+ } else if (ret == -2) {
+ break;
+ } else if (ret == -1) {
+ show_error(pcap_geterr(pcap));
+ pcap_close(pcap);
+ return false;
+ } else {
+ assert(false);
+ show_error("Unexpected return value");
+ pcap_close(pcap);
+ return false;
+ }
+ }
+ pcap_close(pcap);
+ return true;
+ }
+
+ static ssize_t is_ipv4(const uint8_t* data, size_t max, ::Package* package) {
+ if (max < 1) return -1;
+ if ((data[0] >> 4) != 4) return -1;
+ uint8_t ihl = data[0] & 0xf;
+ if (ihl < 5) return -1;
+ if (ihl * 4 > max) return -1;
+ if (data[9] != 0x6) return -1;
+ char tmp[INET_ADDRSTRLEN];
+ struct in_addr addr;
+ addr.s_addr = htonl(read_u32(data + 12));
+ if (!inet_ntop(AF_INET, &addr, tmp, sizeof(tmp))) return -1;
+ package->source_host.assign(tmp);
+ addr.s_addr = htonl(read_u32(data + 16));
+ if (!inet_ntop(AF_INET, &addr, tmp, sizeof(tmp))) return -1;
+ package->target_host.assign(tmp);
+ return ihl * 4;
+ }
+
+ static ssize_t is_tcp(const uint8_t* data, size_t max, ::Package* package) {
+ if (max < 20) return -1;
+ uint8_t data_offset = data[12] >> 4;
+ if (data_offset < 5) return -1;
+ if (data_offset * 4 > max) return -1;
+ package->source_port = read_u16(data);
+ package->target_port = read_u16(data + 2);
+ package->id = read_u32(data + 4);
+ return data_offset * 4;
+ }
+
+ bool save_pcap(PackageList const* packages, std::string const& file) {
+ auto pcap = pcap_open_dead(DLT_RAW, 65535);
+ auto dumper = pcap_dump_open(pcap, file.c_str());
+ if (!dumper) {
+ show_error(pcap_geterr(pcap));
+ pcap_close(pcap);
+ return false;
+ }
+ uint16_t id = 0;
+ std::string data;
+ std::unordered_map<std::string,uint32_t> cache;
+ for (size_t index = 0; index < packages->rows(); ++index) {
+ auto const& pkg = packages->package(index);
+ struct pcap_pkthdr header;
+ header.ts.tv_sec = pkg.pkg.timestamp.tv_sec;
+ header.ts.tv_usec = pkg.pkg.timestamp.tv_nsec / 1000;
+
+ uint8_t ip[20];
+ ip[0] = (4 << 4) | 5; // Version | IHL
+ ip[1] = 0; // DCSP | ECN
+ ip[6] = 0; // Flags | Fragment Offset
+ ip[7] = 0; // Fragment Offset
+ ip[8] = 127; // TTL
+ ip[9] = 6; // Protocol (TCP)
+ auto it = cache.find(pkg.pkg.source_host);
+ if (it == cache.end()) {
+ it = cache.emplace(pkg.pkg.source_host,
+ get_ip(pkg.pkg.source_host)).first;
+ }
+ write_u32(ip + 12, it->second);
+ it = cache.find(pkg.pkg.target_host);
+ if (it == cache.end()) {
+ it = cache.emplace(pkg.pkg.target_host,
+ get_ip(pkg.pkg.target_host)).first;
+ }
+ write_u32(ip + 16, it->second);
+
+ uint8_t tcp[20];
+ write_u16(tcp, pkg.pkg.source_port);
+ write_u16(tcp + 2, pkg.pkg.target_port);
+ write_u32(tcp + 8, 0); // ACK
+ tcp[12] = (5 << 4); // Data offset | NS
+ write_u16(tcp + 14, 0); // Window size
+ write_u16(tcp + 18, 0); // Urgent pointer
+
+ size_t offset = 0;
+ size_t len = pkg.data.size();
+ while (offset < len) {
+ size_t partlen = std::min(len - offset,
+ 65535 - sizeof(ip) - sizeof(tcp));
+ header.len = sizeof(ip) + sizeof(tcp) + partlen;
+ header.caplen = header.len;
+ write_u16(ip + 2, header.len); // Total Length
+ write_u16(ip + 4, id); // Identificiation
+ ++id;
+ write_u16(ip + 10, 0);
+ uint16_t checksum = ipv4_checksum(ip, sizeof(ip));
+ write_u16(ip + 10, checksum); // Checksum
+
+ write_u32(tcp + 4, pkg.pkg.id);
+ tcp[13] = ((offset == 0) ? 2 : 0) | ((offset + partlen == len) ? 1 : 0);
+ write_u16(tcp + 16, 0);
+ checksum = tcp_checksum(ip, tcp,
+ reinterpret_cast<uint8_t const*>(pkg.data.data())
+ + offset, partlen);
+ write_u16(tcp + 16, checksum); // Checksum
+
+ data.assign(reinterpret_cast<char*>(ip), sizeof(ip));
+ data.append(reinterpret_cast<char*>(tcp), sizeof(tcp));
+ data.append(pkg.data.data() + offset, partlen);
+ pcap_dump(reinterpret_cast<u_char*>(dumper), &header,
+ reinterpret_cast<u_char const*>(data.data()));
+ offset += partlen;
+ }
+ }
+ pcap_dump_close(dumper);
+ pcap_close(pcap);
+ return true;
+ }
+
+ static uint16_t ipv4_checksum(uint8_t const* header, size_t size) {
+ assert((size % 2) == 0);
+ uint16_t sum = 0;
+ for (size_t i = 0; i < size; i += 2) {
+ sum += (header[i] << 8) | header[i + 1];
+ }
+ return ~sum;
+ }
+
+ static uint32_t get_ip(std::string const& host) {
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ struct addrinfo* ret;
+ if (getaddrinfo(host.c_str(), nullptr, &hints, &ret)) {
+ return 0;
+ }
+ uint32_t ip = 0;
+ for (auto p = ret; p; p = p->ai_next) {
+ if (p->ai_family == AF_INET) {
+ ip = ntohl(
+ reinterpret_cast<struct sockaddr_in*>(p->ai_addr)->sin_addr.s_addr);
+ break;
+ }
+ }
+ freeaddrinfo(ret);
+ return ip;
+ }
+
+ static uint16_t tcp_checksum(uint8_t const* ip, uint8_t const* tcp,
+ uint8_t const* data, size_t size) {
+ uint16_t sum = 0;
+ sum += ip[12] << 8 | ip[13];
+ sum += ip[14] << 8 | ip[15];
+ sum += ip[16] << 8 | ip[17];
+ sum += ip[18] << 8 | ip[19];
+ sum += 0x06;
+ sum += 20;
+ for (size_t i = 0; i < 20; i += 2) {
+ sum += (tcp[i] << 8) | tcp[i + 1];
+ }
+ for (size_t i = 0; i < size / 2; ++i) {
+ sum += (data[i * 2] << 8) | data[i * 2 + 1];
+ }
+ if (size % 2) sum += data[size - 1] << 8;
+ return ~size;
+ }
+#else // HAVE_PCAP
+ static bool is_pcap(std::string const& UNUSED(file)) {
+ return false;
+ }
+
+ static bool load_pcap(std::string const& UNUSED(file),
+ PackageList* UNUSED(packages)) {
+ return false;
+ }
+
+ static bool save_pcap(PackageList const* UNUSED(packages),
+ std::string const& UNUSED(file)) {
+ return false;
+ }
+#endif // HAVE_PCAP
+
bool load(std::string const& file, PackageList* packages) {
+ if (is_pcap(file)) return load_pcap(file, packages);
std::ifstream in(file);
if (!in.good()) {
show_error("Unable to open " + file + " for reading");
@@ -1156,6 +1404,7 @@ private:
}
bool save(PackageList const* packages, std::string const& file) {
+ if (is_pcap(file)) return save_pcap(packages, file);
std::ofstream out(file);
if (!out.good()) {
show_error("Unable to open " + file + " for writing");