diff options
| -rw-r--r-- | src/Makefile.am | 3 | ||||
| -rw-r--r-- | src/monitor-gui.cc | 107 | ||||
| -rw-r--r-- | src/packages.cc | 125 | ||||
| -rw-r--r-- | src/packages.hh | 49 | ||||
| -rw-r--r-- | test/.gitignore | 1 | ||||
| -rw-r--r-- | test/Makefile.am | 5 | ||||
| -rw-r--r-- | test/test-package.cc | 23 | ||||
| -rw-r--r-- | test/test-packages.cc | 112 | ||||
| -rw-r--r-- | test/test_package.hh | 32 |
9 files changed, 357 insertions, 100 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 41eeecf..607a056 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -38,7 +38,8 @@ libtp_a_SOURCES = args.cc args.hh xdg.cc xdg.hh terminal.cc terminal.hh \ character.cc character.hh config.cc config.hh \ strings.cc strings.hh io.cc io.hh looper.cc looper.hh \ buffer.cc buffer.hh chunked.cc chunked.hh \ - package.cc package.hh data.hh common.hh utf.hh utf.cc + package.cc package.hh data.hh common.hh utf.hh utf.cc \ + packages.cc packages.hh if !HAVE_SSL libtp_a_SOURCES += mitm_stub.cc endif diff --git a/src/monitor-gui.cc b/src/monitor-gui.cc index 6805814..b02c4de 100644 --- a/src/monitor-gui.cc +++ b/src/monitor-gui.cc @@ -39,6 +39,7 @@ #include "looper.hh" #include "monitor.hh" #include "observers.hh" +#include "packages.hh" #include "protocols.hh" #include "proxy.hh" #include "resolver.hh" @@ -682,6 +683,24 @@ private: } }; + class PackagesReaderDelegate : public PackagesReader::Delegate { + public: + PackagesReaderDelegate(PackageList* packages) + : packages_(packages) { + } + + void package(Package const& package) override { + packages_->package(package); + } + + void data(uint32_t id, char const* data, size_t size, bool last) override { + packages_->package_data(id, data, size, last); + } + + private: + PackageList* const packages_; + }; + public: MonitorGui() : packages_(new PackageList()), @@ -1593,65 +1612,18 @@ private: show_error("Unable to open " + file + " for reading"); return false; } - uint8_t header[8]; - in.read(reinterpret_cast<char*>(header), 8); - if (!in.good() || memcmp(header, "TPP", 3) || header[3] != 1) { - show_error("Not a valid package file: " +file); + auto delegate = std::unique_ptr<PackagesReaderDelegate>( + new PackagesReaderDelegate(packages)); + switch (PackagesReader::read(in, delegate.get())) { + case PackagesReader::INVALID: + show_error("Not a valid package file: " + file); return false; - } - uint32_t count = read_u32(header + 4); - bool good = true; - while (count--) { - good = false; - in.read(reinterpret_cast<char*>(header), 8); - if (!in.good()) break; - size_t size = read_u64(header); - if (size == 0) break; - uint8_t buf[8192]; - size_t avail = std::min(size, sizeof(buf)); - in.read(reinterpret_cast<char*>(buf), avail); - if (!in.good()) break; - ::Package pkg; - auto pkg_size = read_package(&pkg, buf, avail); - if (pkg_size == 0) { - if (avail == size) break; - size_t need = std::min(static_cast<size_t>(1024) * 1024, size); - std::unique_ptr<uint8_t[]> mem(new uint8_t[need]); - memcpy(mem.get(), buf, avail); - in.read(reinterpret_cast<char*>(mem.get()) + avail, need - avail); - if (!in.good()) break; - pkg_size = read_package(&pkg, mem.get(), need); - if (pkg_size == 0) break; - packages->package(pkg); - packages->package_data(pkg.id, reinterpret_cast<char*>(mem.get()) - + pkg_size, need - pkg_size, need == size); - size -= need; - } else { - packages->package(pkg); - packages->package_data(pkg.id, reinterpret_cast<char*>(buf) + pkg_size, - avail - pkg_size, avail == size); - size -= avail; - } - while (size) { - avail = std::min(sizeof(buf), size); - in.read(reinterpret_cast<char*>(buf), avail); - if (!in.good()) break; - packages->package_data(pkg.id, reinterpret_cast<char*>(buf), avail, - avail == size); - size -= avail; - } - if (size) break; - good = true; - } - if (good) { - in.read(reinterpret_cast<char*>(header), 8); - if (!in.good() || read_u64(header) != 0) good = false; - } - if (!good) { + case PackagesReader::IO_ERROR: show_error("Error reading from " + file); return false; + case PackagesReader::GOOD: + return true; } - return true; } bool save(PackageList const* packages, std::string const& file) { @@ -1661,30 +1633,13 @@ private: show_error("Unable to open " + file + " for writing"); return false; } - uint8_t header[8]; - memcpy(header, "TPP", 3); - header[3] = 0x1; // Version - write_u32(header + 4, packages->rows()); // Count - out.write(reinterpret_cast<char*>(header), 8); + auto writer = std::unique_ptr<PackagesWriter>( + PackagesWriter::create(packages->rows(), &out)); for (size_t i = 0; i < packages->rows(); ++i) { auto const& pkg = packages->package(i); - uint8_t buf[8192]; - std::unique_ptr<uint8_t[]> backup; - uint8_t* ptr = buf; - size_t need = write_package(pkg.pkg, buf, sizeof(buf)); - if (need > sizeof(buf)) { - backup.reset(new uint8_t[need]); - ptr = backup.get(); - write_package(pkg.pkg, ptr, need); - } - write_u64(header, need + pkg.data.size()); - out.write(reinterpret_cast<char*>(header), 8); - out.write(reinterpret_cast<char*>(ptr), need); - backup.reset(); - out.write(pkg.data.data(), pkg.data.size()); + writer->write(pkg.pkg, pkg.data); } - write_u64(header, 0); // EOF - out.write(reinterpret_cast<char*>(header), 8); + writer.reset(); if (!out.good()) { show_error("Error writing " + file); return false; diff --git a/src/packages.cc b/src/packages.cc new file mode 100644 index 0000000..0309df1 --- /dev/null +++ b/src/packages.cc @@ -0,0 +1,125 @@ +// -*- mode: c++; c-basic-offset: 2; -*- + +#include "common.hh" + +#include <istream> +#include <memory> +#include <ostream> +#include <string.h> + +#include "data.hh" +#include "packages.hh" + +namespace { + +class PackagesWriterImpl : public PackagesWriter { +public: + PackagesWriterImpl(size_t count, std::ostream* out) + : count_(count), out_(out) { + uint8_t header[8]; + memcpy(header, "TPP", 3); + header[3] = 0x1; // Version + write_u32(header + 4, count_); // Count + out->write(reinterpret_cast<char*>(header), 8); + + if (count == 0) { + write_u64(header, 0); // EOF + out_->write(reinterpret_cast<char*>(header), 8); + } + } + + ~PackagesWriterImpl() { + assert(count_ == 0); + } + + void write(Package const& package, std::string const& data) override { + if (count_ == 0) { + assert(false); + return; + } + uint8_t buf[8192]; + std::unique_ptr<uint8_t[]> backup; + uint8_t* ptr = buf; + size_t need = write_package(package, buf, sizeof(buf)); + if (need > sizeof(buf)) { + backup.reset(new uint8_t[need]); + ptr = backup.get(); + write_package(package, ptr, need); + } + uint8_t size[8]; + write_u64(size, need + data.size()); + out_->write(reinterpret_cast<char*>(size), 8); + out_->write(reinterpret_cast<char*>(ptr), need); + backup.reset(); + out_->write(data.data(), data.size()); + + if (--count_ == 0) { + write_u64(size, 0); // EOF + out_->write(reinterpret_cast<char*>(size), 8); + } + } + +private: + size_t count_; + std::ostream* const out_; +}; + +} // namespace + +// static +PackagesWriter* PackagesWriter::create(size_t count, std::ostream* out) { + return new PackagesWriterImpl(count, out); +} + +// static +PackagesReader::Status PackagesReader::read(std::istream& in, + Delegate* delegate) { + uint8_t header[8]; + in.read(reinterpret_cast<char*>(header), 8); + if (!in.good() || memcmp(header, "TPP", 3) || header[3] != 1) { + return INVALID; + } + auto count = read_u32(header + 4); + while (count--) { + in.read(reinterpret_cast<char*>(header), 8); + if (!in.good()) return IO_ERROR; + size_t size = read_u64(header); + if (size == 0) return INVALID; + uint8_t buf[8192]; + size_t avail = std::min(size, sizeof(buf)); + in.read(reinterpret_cast<char*>(buf), avail); + if (!in.good()) return IO_ERROR; + Package pkg; + auto pkg_size = read_package(&pkg, buf, avail); + if (pkg_size == 0) { + if (avail == size) return INVALID; + size_t need = std::min(static_cast<size_t>(1024) * 1024, size); + std::unique_ptr<uint8_t[]> mem(new uint8_t[need]); + memcpy(mem.get(), buf, avail); + in.read(reinterpret_cast<char*>(mem.get()) + avail, need - avail); + if (!in.good()) return IO_ERROR; + pkg_size = read_package(&pkg, mem.get(), need); + if (pkg_size == 0) return INVALID; + delegate->package(pkg); + delegate->data(pkg.id, reinterpret_cast<char*>(mem.get()) + + pkg_size, need - pkg_size, need == size); + size -= need; + } else { + delegate->package(pkg); + delegate->data(pkg.id, reinterpret_cast<char*>(buf) + pkg_size, + avail - pkg_size, avail == size); + size -= avail; + } + while (size) { + avail = std::min(sizeof(buf), size); + in.read(reinterpret_cast<char*>(buf), avail); + if (!in.good()) return IO_ERROR; + delegate->data(pkg.id, reinterpret_cast<char*>(buf), avail, + avail == size); + size -= avail; + } + } + in.read(reinterpret_cast<char*>(header), 8); + if (!in.good() || read_u64(header) != 0) return INVALID; + return GOOD; +} diff --git a/src/packages.hh b/src/packages.hh new file mode 100644 index 0000000..e646f92 --- /dev/null +++ b/src/packages.hh @@ -0,0 +1,49 @@ +// -*- mode: c++; c-basic-offset: 2; -*- + +#ifndef PACKAGES_HH +#define PACKAGES_HH + +#include "package.hh" + +class PackagesWriter { +public: + virtual ~PackagesWriter() {} + + static PackagesWriter* create(size_t count, std::ostream* out); + + virtual void write(Package const& package, std::string const& data) = 0; + +protected: + PackagesWriter() {} + PackagesWriter(PackagesWriter const&) = delete; + PackagesWriter& operator=(PackagesWriter const&) = delete; +}; + +class PackagesReader { +public: + enum Status { + GOOD, + INVALID, + IO_ERROR, + }; + + class Delegate { + public: + virtual ~Delegate() {} + + virtual void package(Package const& package) = 0; + virtual void data(uint32_t id, char const* data, size_t size, + bool last) = 0; + + protected: + Delegate() {} + }; + + static Status read(std::istream& in, Delegate* delegate); + +private: + ~PackagesReader() {} + PackagesReader() {} +}; + +#endif // PACKAGES_HH diff --git a/test/.gitignore b/test/.gitignore index f1a5a11..c651602 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -11,3 +11,4 @@ /test-htmlattrtext /test-package /test-utf +/test-packages diff --git a/test/Makefile.am b/test/Makefile.am index 5248547..16dd3eb 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -3,7 +3,7 @@ MAINTAINERCLEANFILES = Makefile.in AM_CXXFLAGS = @DEFINES@ -I$(top_srcdir)/src TESTS = test-url test-http test-args test-xdg test-paths test-strings \ - test-observers test-htmlattrtext test-package test-utf + test-observers test-htmlattrtext test-package test-utf test-packages check_PROGRAMS = $(TESTS) @@ -35,3 +35,6 @@ test_package_LDADD = $(top_builddir)/src/libtp.a test_utf_SOURCES = test-utf.cc test.hh test_utf_LDADD = $(top_builddir)/src/libtp.a + +test_packages_SOURCES = test-packages.cc test.hh +test_packages_LDADD = $(top_builddir)/src/libtp.a diff --git a/test/test-package.cc b/test/test-package.cc index ce15c88..64a4fe3 100644 --- a/test/test-package.cc +++ b/test/test-package.cc @@ -2,31 +2,10 @@ #include "common.hh" #include "test.hh" - -#include "package.hh" +#include "test_package.hh" namespace { -void setup(Package* pkg) { - pkg->id = 42; - pkg->timestamp.tv_sec = 123; - pkg->timestamp.tv_nsec = 999999999; - pkg->source_port = 0; - pkg->source_host = "source"; - pkg->target_port = 65535; - pkg->target_host = "target"; -} - -bool pkg_eq(Package const& p1, Package const& p2) { - return p1.id == p2.id - && p1.timestamp.tv_sec == p2.timestamp.tv_sec - && p1.timestamp.tv_nsec == p2.timestamp.tv_nsec - && p1.source_port == p2.source_port - && p1.source_host.compare(p2.source_host) == 0 - && p1.target_port == p2.target_port - && p1.target_host.compare(p2.target_host) == 0; -} - bool test_sanity() { Package pkg1; Package pkg2; diff --git a/test/test-packages.cc b/test/test-packages.cc new file mode 100644 index 0000000..d0c526c --- /dev/null +++ b/test/test-packages.cc @@ -0,0 +1,112 @@ +// -*- mode: c++; c-basic-offset: 2; -*- + +#include "common.hh" +#include "test.hh" +#include "test_package.hh" + +#include <memory> +#include <sstream> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +#include "packages.hh" + +namespace { + +class Delegate : public PackagesReader::Delegate { +public: + struct Entry { + Package package; + std::string data; + + Entry(Package const& package) + : package(package) { + } + }; + + std::vector<Entry> packages; + bool good; + + Delegate() + : good(true) { + } + + void package(Package const& package) override { + if (open_.find(package.id) != open_.end()) { + std::cerr << "Duplicate id" << std::endl; + good = false; + return; + } + if (closed_.count(package.id) > 0) { + std::cerr << "Duplicate id" << std::endl; + good = false; + return; + } + open_[package.id] = packages.size(); + packages.emplace_back(package); + } + + void data(uint32_t id, char const* data, size_t size, bool last) override { + auto it = open_.find(id); + if (it == open_.end()) { + if (closed_.count(id)) { + std::cerr << "Data after last == true" << std::endl; + } else { + std::cerr << "Data for unknown package" << std::endl; + } + good = false; + return; + } + packages[it->second].data.append(data, size); + if (last) { + open_.erase(it); + closed_.emplace(id); + } + } + +private: + std::unordered_map<uint32_t, size_t> open_; + std::unordered_set<uint32_t> closed_; +}; + +bool test_sanity() { + Package pkg1; + + setup(&pkg1); + + std::stringstream stream; + auto writer = std::unique_ptr<PackagesWriter>( + PackagesWriter::create(1, &stream)); + writer->write(pkg1, "Hello World!"); + writer.reset(); + + Delegate delegate; + ASSERT_EQ(PackagesReader::GOOD, PackagesReader::read(stream, &delegate)); + ASSERT_TRUE(delegate.good); + ASSERT_EQ(static_cast<size_t>(1), delegate.packages.size()); + ASSERT_TRUE(pkg_eq(pkg1, delegate.packages[0].package)); + ASSERT_EQ("Hello World!", delegate.packages[0].data); + + return true; +} + +bool test_empty() { + std::stringstream stream; + delete PackagesWriter::create(0, &stream); + ASSERT_TRUE(!stream.str().empty()); + Delegate delegate; + ASSERT_EQ(PackagesReader::GOOD, PackagesReader::read(stream, &delegate)); + ASSERT_TRUE(delegate.good); + ASSERT_EQ(static_cast<size_t>(0), delegate.packages.size()); + return true; +} + +} // namespace + +int main(void) { + BEFORE; + RUN(test_sanity()); + RUN(test_empty()); + AFTER; +} diff --git a/test/test_package.hh b/test/test_package.hh new file mode 100644 index 0000000..f546c8b --- /dev/null +++ b/test/test_package.hh @@ -0,0 +1,32 @@ +// -*- mode: c++; c-basic-offset: 2; -*- + +#ifndef TEST_PACKAGE_HH +#define TEST_PACKAGE_HH + +#include "package.hh" + +namespace { + +void setup(Package* pkg) { + pkg->id = 42; + pkg->timestamp.tv_sec = 123; + pkg->timestamp.tv_nsec = 999999999; + pkg->source_port = 0; + pkg->source_host = "source"; + pkg->target_port = 65535; + pkg->target_host = "target"; +} + +bool pkg_eq(Package const& p1, Package const& p2) { + return p1.id == p2.id + && p1.timestamp.tv_sec == p2.timestamp.tv_sec + && p1.timestamp.tv_nsec == p2.timestamp.tv_nsec + && p1.source_port == p2.source_port + && p1.source_host.compare(p2.source_host) == 0 + && p1.target_port == p2.target_port + && p1.target_host.compare(p2.target_host) == 0; +} + +} // namespace + +#endif // TEST_PACKAGE_HH |
