summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@yahoo.com>2017-07-29 01:20:23 +0200
committerJoel Klinghed <the_jk@yahoo.com>2017-07-29 01:20:23 +0200
commitee34164a6c1c4f905332cfcfef938a0ccb48333b (patch)
treee3f889dbcd3bc8bacd2aa5ed2e236a13af69a3f3
parent4c7efd97af6ca1028279d40ebe674dc88bdaafc4 (diff)
Add basic pcap import/export support
-rw-r--r--configure.ac17
-rw-r--r--src/Makefile.am6
-rw-r--r--src/monitor-gui.cc249
3 files changed, 269 insertions, 3 deletions
diff --git a/configure.ac b/configure.ac
index ff77415..5c227cc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -119,6 +119,23 @@ AM_CONDITIONAL([HAVE_SSL],[test "x$have_ssl" = "x1"])
AM_CONDITIONAL([HAVE_MBEDTLS],[test "x$ssl_mbedtls" = "x1"])
AM_CONDITIONAL([HAVE_OPENSSL],[test "x$ssl_openssl" = "x1"])
+# pcap
+have_pcap=0
+AC_ARG_ENABLE([pcap],
+ [AC_HELP_STRING([--disable-pcap], [do not use libpcap even if found])],
+ [pcap_check=$enableval], [pcap_check=yes])
+AS_IF([test x$pcap_check = xyes],
+ [AC_PATH_TOOL([pcap_config], [pcap-config])
+ AS_IF([test -n "$pcap_config"],
+ [PCAP_CFLAGS=`$pcap_config --cflags`
+ PCAP_LIBS=`$pcap_config --libs`
+ have_pcap=1])
+ ])
+AC_SUBST([PCAP_CFLAGS])
+AC_SUBST([PCAP_LIBS])
+AC_DEFINE_UNQUOTED([HAVE_PCAP],[$have_pcap],[define to 1 if libpcap is linked])
+AM_CONDITIONAL([HAVE_PCAP],[test "x$have_pcap" = "x1"])
+
# GTK
have_gtk=0
AC_ARG_ENABLE([gtk],
diff --git a/src/Makefile.am b/src/Makefile.am
index ae46e53..0144d21 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -60,13 +60,13 @@ tp_monitor_CXXFLAGS = $(AM_CXXFLAGS) -DVERSION='"@VERSION@"' @THREAD_CFLAGS@
libmonitor_gui_a_SOURCES = monitor-gui.cc gui_hexdump.cc gui_config.cc
libmonitor_gui_a_CXXFLAGS = $(AM_CXXFLAGS) -DVERSION='"@VERSION@"' \
- @THREAD_CFLAGS@
+ @THREAD_CFLAGS@ @PCAP_CFLAGS@
libattrstr_a_SOURCES = gui_attrtext.cc gui_htmlattrtext.cc
tp_monitor_gtk_SOURCES = gui_gtk.cc
tp_monitor_gtk_LDADD = libmonitor_gui.a libattrstr.a libproxy.a libmonitor.a \
- libtp.a @GTK_LIBS@ @THREAD_LIBS@
+ libtp.a @GTK_LIBS@ @THREAD_LIBS@ @PCAP_LIBS@
if HAVE_SSL
tp_monitor_gtk_LDADD += libmitm.a @SSL_LIBS@
endif
@@ -75,7 +75,7 @@ tp_monitor_gtk_CXXFLAGS = $(AM_CXXFLAGS) -DVERSION='"@VERSION@"' \
tp_monitor_qt_SOURCES = gui_qt.cc
tp_monitor_qt_LDADD = libmonitor_gui.a libattrstr.a libmonitor.a libproxy.a \
- libtp.a @QT_LIBS@ @THREAD_LIBS@
+ libtp.a @QT_LIBS@ @THREAD_LIBS@ @PCAP_LIBS@
if HAVE_SSL
tp_monitor_qt_LDADD += libmitm.a @SSL_LIBS@
endif
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");