summaryrefslogtreecommitdiff
path: root/src/monitor-cmd.cc
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@yahoo.com>2017-03-16 23:28:09 +0100
committerJoel Klinghed <the_jk@yahoo.com>2017-03-16 23:38:19 +0100
commit87774d8981ae7a079492d8949e205065ba72a8e4 (patch)
treef056ffbdfb436143db1d968ffc7c82b1cb3d79a3 /src/monitor-cmd.cc
parent719d90a40e83e870be19f8d46cc55caed618aa35 (diff)
Add basic console monitor and implement monitor support
Diffstat (limited to 'src/monitor-cmd.cc')
-rw-r--r--src/monitor-cmd.cc271
1 files changed, 271 insertions, 0 deletions
diff --git a/src/monitor-cmd.cc b/src/monitor-cmd.cc
new file mode 100644
index 0000000..5221db0
--- /dev/null
+++ b/src/monitor-cmd.cc
@@ -0,0 +1,271 @@
+// -*- mode: c++; c-basic-offset: 2; -*-
+
+#include "common.hh"
+
+#include <cstring>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <signal.h>
+#include <unordered_map>
+
+#include "args.hh"
+#include "buffer.hh"
+#include "io.hh"
+#include "ios_save.hh"
+#include "looper.hh"
+#include "resolver.hh"
+#include "monitor.hh"
+
+namespace {
+
+class Delegate : public Monitor::Delegate {
+public:
+ Delegate(std::ostream& out, bool interleave, Looper* looper)
+ : out_(out), interleave_(interleave), looper_(looper), attached_(false) {
+ }
+
+ void state(Monitor* monitor, Monitor::State state) override {
+ switch (state) {
+ case Monitor::DISCONNECTED:
+ std::cout << "# Disconnected" << std::endl;
+ looper_->quit();
+ break;
+ case Monitor::CONNECTING:
+ break;
+ case Monitor::CONNECTED:
+ if (attached_) {
+ std::cout << "# Detached" << std::endl;
+ attached_ = false;
+ } else {
+ std::cout << "# Connected" << std::endl;
+ monitor->attach();
+ }
+ break;
+ case Monitor::ATTACHED:
+ std::cout << "# Attached" << std::endl;
+ attached_ = true;
+ break;
+ }
+ }
+
+ void error(Monitor* UNUSED(monitor), std::string const& error) override {
+ std::cerr << "# Error: " << error << std::endl;
+ }
+
+ void package(
+ Monitor* UNUSED(monitor), Monitor::Package const& package) override {
+ packages_.insert(std::make_pair(package.id, package));
+ }
+
+ void package_data(Monitor* UNUSED(monitor), uint32_t id,
+ char const* data, size_t size, bool last) override {
+ auto it = packages_.find(id);
+ if (it == packages_.end()) {
+ assert(false);
+ return;
+ }
+ if (interleave_) {
+ print_package(it->second, last, data, size);
+ if (last) packages_.erase(it);
+ return;
+ }
+ auto buf = data_.find(id);
+ if (last) {
+ if (buf == data_.end()) {
+ print_package(it->second, true, data, size);
+ } else {
+ buf->second->write(data, size);
+ size_t avail;
+ auto ptr = buf->second->read_ptr(&avail);
+ print_package(it->second, true,
+ reinterpret_cast<char const*>(ptr), avail);
+ data_.erase(buf);
+ }
+ packages_.erase(it);
+ } else {
+ if (buf == data_.end()) {
+ buf = data_.insert(std::make_pair(id, Buffer::create(8192, 0))).first;
+ }
+ buf->second->write(data, size);
+ }
+ }
+
+private:
+ void print_package(
+ Monitor::Package& pkg, bool last, char const* data, size_t size) {
+ if (size == 0 && !last) return;
+ {
+ ios_save save(out_);
+ out_ << "*** " << pkg.timestamp.tv_sec << '.'
+ << std::setfill('0') << std::setw(9) << pkg.timestamp.tv_nsec
+ << '\n';
+ }
+ out_ << "* Source: " << pkg.source_host << ':' << pkg.source_port << '\n'
+ << "* Target: " << pkg.target_host << ':' << pkg.target_port << '\n';
+ if (interleave_) {
+ auto offset = offset_.find(pkg.id);
+ if (offset != offset_.end()) {
+ out_ << "* Bytes: " << offset->second << "-";
+ if (last) {
+ out_ << (offset->second + size);
+ offset_.erase(offset);
+ } else {
+ offset->second += size;
+ }
+ out_ << '\n';
+ } else if (!last) {
+ offset_.insert(std::make_pair(pkg.id, size));
+ out_ << "* Bytes: 0-";
+ }
+ }
+ out_ << "* Size: " << size << '\n';
+ if (size > 0) {
+ ios_save save(out_);
+ auto d = reinterpret_cast<uint8_t const*>(data);
+ out_.flags(std::ios::hex);
+ out_.fill('0');
+ for (size_t i = 0; i < size; i += 16) {
+ out_ << std::setw(8) << i << ' ';
+ unsigned j = 0;
+ for (; j < 8; ++j) {
+ auto k = i + j;
+ if (k >= size) break;
+ out_ << ' ' << std::setw(2) << static_cast<int>(d[k]);
+ }
+ for (; j < 8; ++j) {
+ out_ << " ";
+ }
+ out_ << ' ';
+ for (; j < 16; ++j) {
+ auto k = i + j;
+ if (k >= size) break;
+ out_ << ' ' << std::setw(2) << static_cast<int>(d[k]);
+ }
+ for (; j < 16; ++j) {
+ out_ << " ";
+ }
+ out_ << " |";
+ j = 0;
+ for (; j < 16; ++j) {
+ auto k = i + j;
+ if (k >= size) break;
+ out_ << printable(data[k]);
+ }
+ for (; j < 16; ++j) {
+ out_ << ' ';
+ }
+ out_ << "|\n";
+ }
+ }
+ out_ << std::endl;
+ }
+
+ static char printable(char c) {
+ return (c & 0x80 || c < ' ' || c >= 0x7f) ? '.' : c;
+ }
+
+ std::ostream& out_;
+ bool interleave_;
+ Looper* looper_;
+ bool attached_;
+ std::unordered_map<uint32_t, Monitor::Package> packages_;
+ // Used when interleaving
+ std::unordered_map<uint32_t, uint64_t> offset_;
+ // Used when not interleaving
+ std::unordered_map<uint32_t, std::unique_ptr<Buffer>> data_;
+};
+
+io::auto_pipe signal_pipe;
+
+void signal(int UNUSED(signum)) {
+ io::write_all(signal_pipe.write(), "", 1);
+ std::cerr << "# Caught signal" << std::endl;
+}
+
+void quit_loop(Looper* looper, int UNUSED(fd), uint8_t UNUSED(events)) {
+ looper->quit();
+}
+
+bool run(std::ostream& out, bool interleave,
+ std::string const& host, uint16_t port) {
+ std::unique_ptr<Looper> looper(Looper::create());
+ std::unique_ptr<Resolver> resolver(Resolver::create(looper.get()));
+ std::unique_ptr<Delegate> delegate(
+ new Delegate(out, interleave, looper.get()));
+ std::unique_ptr<Monitor> monitor(
+ Monitor::create(looper.get(), resolver.get(), delegate.get()));
+ std::cout << "# Connecting to " << host << ':' << port << std::endl;
+ monitor->connect(host, port);
+ if (signal_pipe.open()) {
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = signal;
+ sigaction(SIGINT, &action, nullptr);
+ sigaction(SIGTERM, &action, nullptr);
+ looper->add(signal_pipe.read(), Looper::EVENT_READ,
+ std::bind(&quit_loop, looper.get(), std::placeholders::_1,
+ std::placeholders::_2));
+ }
+ return looper->run();
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ std::unique_ptr<Args> args(Args::create());
+ args->add('i', "interleave",
+ "unless set packages are not output until they are complete");
+ args->add('o', "output", "FILE", "output packages to FILE instead of stdout");
+ args->add('h', "help", "display this text and exit");
+ args->add('V', "version", "display version and exit");
+ if (!args->run(argc, argv)) {
+ std::cerr << "Try `tp-monitor --help` for usage." << std::endl;
+ return EXIT_FAILURE;
+ }
+ if (args->is_set('h')) {
+ std::cout << "Usage: `tp-monitor [OPTIONS...] HOST[:PORT]`\n"
+ << "Connect to transparent proxy running on HOST[:PORT] and "
+ << "print logged packages\n"
+ << std::endl;
+ args->print_help();
+ return EXIT_SUCCESS;
+ }
+ if (args->is_set('V')) {
+ std::cout << "TransparentProxy monitor version " VERSION
+ << " written by Joel Klinghed <the_jk@yahoo.com>" << std::endl;
+ return EXIT_SUCCESS;
+ }
+ if (args->arguments().size() != 1) {
+ std::cerr << "Unexpected number of arguments, expects one argument."
+ << std::endl;
+ return EXIT_FAILURE;
+ }
+ std::string host = args->arguments().front();
+ uint16_t port;
+ auto i = host.rfind(':');
+ if (i != std::string::npos && i > 0 && host[i - 1] != ':') {
+ errno = 0;
+ char* end = nullptr;
+ auto tmp = strtoul(host.c_str() + i + 1, &end, 10);
+ if (errno || !end || *end) {
+ std::cerr << "Invalid port number: " << host.substr(i + 1) << std::endl;
+ return EXIT_FAILURE;
+ }
+ port = tmp & 0xffff;
+ } else {
+ port = 9000;
+ }
+ auto interleave = args->is_set('i');
+ auto output = args->arg('o', nullptr);
+ if (output) {
+ std::ofstream out(output, std::ofstream::trunc);
+ if (!out.good()) {
+ std::cerr << "Unable to open " << output << " for writing." << std::endl;
+ return EXIT_FAILURE;
+ }
+ return run(out, interleave, host, port);
+ } else {
+ return run(std::cout, interleave, host, port);
+ }
+}