diff options
| -rw-r--r-- | data/tp-monitor-gtk.gschema.xml | 12 | ||||
| -rw-r--r-- | src/.gitignore | 1 | ||||
| -rw-r--r-- | src/Makefile.am | 27 | ||||
| -rw-r--r-- | src/gui_attrtext.hh | 2 | ||||
| -rw-r--r-- | src/gui_gtk.cc | 7 | ||||
| -rw-r--r-- | src/gui_htmlattrtext.cc | 5 | ||||
| -rw-r--r-- | src/main.cc | 7 | ||||
| -rw-r--r-- | src/monitor-gui.cc | 252 | ||||
| -rw-r--r-- | src/monitor.cc | 6 | ||||
| -rw-r--r-- | src/monitor.hh | 1 | ||||
| -rw-r--r-- | src/proxy.cc | 54 | ||||
| -rw-r--r-- | src/proxy.hh | 6 |
12 files changed, 328 insertions, 52 deletions
diff --git a/data/tp-monitor-gtk.gschema.xml b/data/tp-monitor-gtk.gschema.xml index 9dde64c..d1e3987 100644 --- a/data/tp-monitor-gtk.gschema.xml +++ b/data/tp-monitor-gtk.gschema.xml @@ -6,5 +6,17 @@ Last monitor host connected to </description> </key> + <key name="bind" type="s"> + <default>""</default> + <description> + Last proxy bind address + </description> + </key> + <key name="port" type="s"> + <default>"8080"</default> + <description> + Last proxy port + </description> + </key> </schema> </schemalist> diff --git a/src/.gitignore b/src/.gitignore index 6b82970..5c67110 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -4,6 +4,7 @@ /libmonitor.a /libmonitor_gui.a /libmitm.a +/libproxy.a /libtp.a /tp /tp-genca diff --git a/src/Makefile.am b/src/Makefile.am index 9cd0884..b25378d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,7 +6,7 @@ AM_CXXFLAGS = @DEFINES@ ARFLAGS = cr bin_PROGRAMS = tp tp-monitor -noinst_LIBRARIES = libtp.a libmonitor.a libattrstr.a +noinst_LIBRARIES = libtp.a libproxy.a libmonitor.a libattrstr.a if HAVE_SSL bin_PROGRAMS += tp-genca noinst_LIBRARIES += libmitm.a @@ -20,13 +20,16 @@ bin_PROGRAMS += tp-monitor-qt noinst_LIBRARIES += libmonitor_gui.a endif -tp_SOURCES = main.cc proxy.cc logger.cc resolver.cc -tp_LDADD = libtp.a @THREAD_LIBS@ +tp_SOURCES = main.cc +tp_LDADD = libproxy.a libtp.a @THREAD_LIBS@ if HAVE_SSL tp_LDADD += libmitm.a @SSL_LIBS@ endif tp_CXXFLAGS = $(AM_CXXFLAGS) -DVERSION='"@VERSION@"' @THREAD_CFLAGS@ +libproxy_a_SOURCES = proxy.cc resolver.cc logger.cc +libproxy_a_CXXFLAGS = $(AM_CXXFLAGS) -DVERSION='"@VERSION@"' @THREAD_CFLAGS@ + libtp_a_SOURCES = args.cc xdg.cc terminal.cc http.cc url.cc paths.cc \ character.cc config.cc strings.cc io.cc looper.cc \ buffer.cc chunked.cc @@ -48,10 +51,10 @@ tp_genca_SOURCES = genca.cc logger.cc tp_genca_LDADD = libmitm.a libtp.a @SSL_LIBS@ tp_genca_CXXFLAGS = $(AM_CXXFLAGS) -DVERSION='"@VERSION@"' -libmonitor_a_SOURCES = monitor.cc resolver.cc +libmonitor_a_SOURCES = monitor.cc libmonitor_a_CXXFLAGS = $(AM_CXXFLAGS) -DVERSION='"@VERSION@"' @THREAD_CFLAGS@ -tp_monitor_SOURCES = monitor-cmd.cc +tp_monitor_SOURCES = monitor-cmd.cc resolver.cc tp_monitor_LDADD = libmonitor.a libtp.a @THREAD_LIBS@ tp_monitor_CXXFLAGS = $(AM_CXXFLAGS) -DVERSION='"@VERSION@"' @THREAD_CFLAGS@ @@ -62,13 +65,19 @@ libmonitor_gui_a_CXXFLAGS = $(AM_CXXFLAGS) -DVERSION='"@VERSION@"' \ 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 libmonitor.a libtp.a \ - @GTK_LIBS@ @THREAD_LIBS@ +tp_monitor_gtk_LDADD = libmonitor_gui.a libattrstr.a libproxy.a libmonitor.a \ + libtp.a @GTK_LIBS@ @THREAD_LIBS@ +if HAVE_SSL +tp_monitor_gtk_LDADD += libmitm.a @SSL_LIBS@ +endif tp_monitor_gtk_CXXFLAGS = $(AM_CXXFLAGS) -DVERSION='"@VERSION@"' \ @GTK_CFLAGS@ @THREAD_CFLAGS@ -Wno-unused-function tp_monitor_qt_SOURCES = gui_qt.cc -tp_monitor_qt_LDADD = libmonitor_gui.a libattrstr.a libmonitor.a libtp.a \ - @QT_LIBS@ @THREAD_LIBS@ +tp_monitor_qt_LDADD = libmonitor_gui.a libattrstr.a libmonitor.a libproxy.a \ + libtp.a @QT_LIBS@ @THREAD_LIBS@ +if HAVE_SSL +tp_monitor_qt_LDADD += libmitm.a @SSL_LIBS@ +endif tp_monitor_qt_CXXFLAGS = $(AM_CXXFLAGS) -DVERSION='"@VERSION@"' \ @QT_CFLAGS@ @THREAD_CFLAGS@ diff --git a/src/gui_attrtext.hh b/src/gui_attrtext.hh index 129f905..06fb753 100644 --- a/src/gui_attrtext.hh +++ b/src/gui_attrtext.hh @@ -131,6 +131,8 @@ public: static AttributedText* create(); + virtual void reset() = 0; + void append(std::string const& str, Attribute const& attr = EMPTY, size_t start = 0, size_t length = std::string::npos); void append(const char* str, Attribute const& attr = EMPTY, diff --git a/src/gui_gtk.cc b/src/gui_gtk.cc index be9fa96..afccd85 100644 --- a/src/gui_gtk.cc +++ b/src/gui_gtk.cc @@ -445,6 +445,13 @@ public: return ret; } + void reset() override { + GtkTextIter begin, end; + gtk_text_buffer_get_start_iter(buffer_.get(), &begin); + gtk_text_buffer_get_end_iter(buffer_.get(), &end); + gtk_text_buffer_delete(buffer_.get(), &begin, &end); + } + GtkTextBuffer* buffer() const { return buffer_.get(); } diff --git a/src/gui_htmlattrtext.cc b/src/gui_htmlattrtext.cc index 24380af..ceaeba8 100644 --- a/src/gui_htmlattrtext.cc +++ b/src/gui_htmlattrtext.cc @@ -160,6 +160,11 @@ public: return text_; } + void reset() override { + text_.clear(); + ranges_.clear(); + } + private: struct Range { size_t start_; diff --git a/src/main.cc b/src/main.cc index f9e1af6..f4f6a84 100644 --- a/src/main.cc +++ b/src/main.cc @@ -11,7 +11,9 @@ #include "config.hh" #include "io.hh" #include "logger.hh" +#include "looper.hh" #include "proxy.hh" +#include "resolver.hh" namespace { @@ -169,13 +171,16 @@ int main(int argc, char** argv) { } auto foreground = config->get("foreground", false); auto cwd = get_cwd(); + std::unique_ptr<Looper> looper(Looper::create()); + std::unique_ptr<Resolver> resolver(Resolver::create(looper.get())); std::unique_ptr<Proxy> proxy( Proxy::create(config.get(), cwd, configfile, foreground ? "bogus" : (logfile_from_argument ? logfile : nullptr), foreground ? logger.get() : file_logger.get(), accept_socket.release(), - monitor_socket.release())); + monitor_socket.release(), + looper.get(), resolver.get())); if (!foreground) { if (daemon(0, 0)) { logger->out(Logger::ERR, "Failed to fork: %s", strerror(errno)); diff --git a/src/monitor-gui.cc b/src/monitor-gui.cc index fbbcfb9..c88bb8e 100644 --- a/src/monitor-gui.cc +++ b/src/monitor-gui.cc @@ -2,26 +2,34 @@ #include "common.hh" +#include <fcntl.h> #include <memory> #include <string.h> +#include <sys/types.h> +#include <sys/socket.h> #include <unordered_map> #include <vector> #include "config.hh" #include "gui_about.hh" +#include "gui_config.hh" #include "gui_formapply.hh" #include "gui_hexdump.hh" #include "gui_listmodel.hh" #include "gui_menu.hh" #include "gui_main.hh" #include "gui_statusbar.hh" +#include "io.hh" +#include "logger.hh" #include "looper.hh" #include "monitor.hh" #include "observers.hh" +#include "proxy.hh" #include "resolver.hh" namespace { +std::string const ACTION_SETUP = "setup"; std::string const ACTION_CONNECT = "connect"; std::string const ACTION_DISCONNECT = "disconnect"; std::string const ACTION_EXIT = "exit"; @@ -223,6 +231,90 @@ const PackageList::Package PackageList::EMPTY; // static const std::string PackageList::empty_; +class MemoryConfig : public GuiConfig { +public: + using Config::get; + std::string const& get(std::string const& key, + std::string const& fallback) override { + auto it = memory_.find(key); + if (it == memory_.end()) return fallback; + return it->second; + } + char const* get(std::string const& key, char const* fallback) override { + auto it = memory_.find(key); + if (it == memory_.end()) return fallback; + return it->second.c_str(); + } + bool is_set(std::string const& key) override { + return memory_.count(key); + } + + void set(std::string const& key, std::string const& value) override { + memory_[key] = value; + } + +private: + std::unordered_map<std::string, std::string> memory_; +}; + +class StringLogger : public Logger { +public: + void out(Level lvl, char const* format, ...) override { + if (lvl == DBG) { +#ifdef NDEBUG + return; +#endif + } + char* tmp; + va_list args; + va_start(args, format); + auto ret = vasprintf(&tmp, format, args); + va_end(args); + if (ret == -1) return; + switch (lvl) { + case ERR: + text_->append("Error", ATTR_ERROR); + break; + case WARN: + text_->append("Warning", ATTR_WARNING); + break; + case INFO: + text_->append("Info", ATTR_INFO); + break; + case DBG: + text_->append("Debug", ATTR_DEBUG); + break; + } + text_->append(": "); + text_->append(tmp, ret); + text_->append("\n"); + free(tmp); + } + + AttributedText const* text() const { + return text_.get(); + } + + void clear() { + text_->reset(); + } + + StringLogger() + : ATTR_ERROR(0xff, 0, 0), + ATTR_WARNING(0xff, 0xff, 0), + ATTR_INFO(), + ATTR_DEBUG(0, 0xff, 0), + text_(AttributedText::create()) { + } + +private: + const AttributedText::Attribute ATTR_ERROR; + const AttributedText::Attribute ATTR_WARNING; + const AttributedText::Attribute ATTR_INFO; + const AttributedText::Attribute ATTR_DEBUG; + std::unique_ptr<AttributedText> text_; +}; + class MonitorGui : GuiMenu::Listener, GuiMain::Listener, Monitor::Delegate { private: class ConnectFormListener : public GuiFormApply::Listener { @@ -265,6 +357,82 @@ private: Config* config_; }; + class SetupFormListener : public GuiFormApply::Listener { + public: + bool about_to_close(GuiForm* form) override { + auto const& port = form->get_string("port"); + if (port.empty()) { + form->set_error("Empty proxy port"); + return false; + } + return true; + } + }; + + class SetupFormDelegate : public GuiFormApply::Delegate { + public: + SetupFormDelegate(std::unique_ptr<Proxy>& proxy, + Config* proxy_config, + StringLogger* proxy_logger, + Monitor* monitor, + Config* config, + Looper* looper, + Resolver* resolver) + : proxy_(proxy), proxy_config_(proxy_config), proxy_logger_(proxy_logger), + monitor_(monitor), config_(config), looper_(looper), + resolver_(resolver) { + } + + void apply(GuiFormApply* form) override { + auto const& bind = form->get_string("bind"); + auto const& port = form->get_string("port"); + if (port.empty()) { + form->set_error("Empty proxy port"); + form->applied(false); + return; + } + config_->set("bind", bind); + config_->set("port", port); + proxy_config_->set("proxy_bind", bind); + proxy_config_->set("proxy_port", port); + proxy_config_->set("__one_single_monitor", "true"); + io::auto_fd proxy_fd( + Proxy::setup_accept_socket(proxy_config_, proxy_logger_)); + if (!proxy_fd) { + auto error = proxy_logger_->text()->text(); + proxy_logger_->clear(); + std::string errmsg = + "Unable to bind " + (bind.empty() ? "*" : bind) + ":" + port; + if (!error.empty()) errmsg += "\n" + error; + form->set_error(errmsg); + form->applied(false); + return; + } + int monitor_fd[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, monitor_fd)) { + form->set_error("Unable to create socketpair"); + form->applied(false); + return; + } + fcntl(monitor_fd[0], F_SETFL, O_NONBLOCK); + fcntl(monitor_fd[1], F_SETFL, O_NONBLOCK); + proxy_.reset(Proxy::create(proxy_config_, "", + nullptr, nullptr, proxy_logger_, + proxy_fd.release(), monitor_fd[0], + looper_, resolver_)); + monitor_->connect(monitor_fd[1]); + } + + private: + std::unique_ptr<Proxy>& proxy_; + Config* proxy_config_; + StringLogger* proxy_logger_; + Monitor* monitor_; + Config* config_; + Looper* looper_; + Resolver* resolver_; + }; + public: MonitorGui() : packages_(new PackageList()), @@ -275,6 +443,7 @@ public: has_selection_(false), selection_(0) { auto file = menu_->add_menu("File"); + file->add_item(ACTION_SETUP, "Setup..."); file->add_item(ACTION_CONNECT, "Connect..."); file->add_item(ACTION_DISCONNECT, "Disconnect"); file->add_separator(); @@ -318,6 +487,35 @@ public: nullptr)); } main_->show(about_.get()); + } else if (id == ACTION_SETUP) { + setup_monitor(); + setup_proxy(); + proxy_logger_->clear(); + auto dlg = std::unique_ptr<GuiFormApply::Delegate>( + new SetupFormDelegate(proxy_, proxy_config_.get(), proxy_logger_.get(), + monitor_.get(), main_->config(), + looper_.get(), resolver_.get())); + auto lst = std::unique_ptr<GuiFormApply::Listener>( + new SetupFormListener()); + connect_.reset( + GuiFormApply::create("Setup...", + "Setup a proxy to start monitoring traffic." + " Leave bind empty to listen on all interfaces.", + "Setup", + dlg.get())); + connect_->add_string("bind", "Address", + main_->config()->get("bind", "")); + connect_->add_string("port", "port", + main_->config()->get("port", "8080")); + connect_->add_listener(lst.get()); + if (connect_->show(main_.get())) { + monitor_->attach(); + connect_.reset(); + } else { + connect_.reset(); + monitor_->disconnect(); + proxy_.reset(); + } } else if (id == ACTION_CONNECT) { setup_monitor(); auto dlg = std::unique_ptr<GuiFormApply::Delegate>( @@ -343,6 +541,7 @@ public: if (monitor_) { monitor_->disconnect(); } + proxy_.reset(); } else if (id == ACTION_COPY_TEXT) { if (!has_selection_) { assert(false); @@ -421,27 +620,28 @@ public: } bool allow_connect, allow_disconnect; switch (state) { - case Monitor::DISCONNECTED: - statusbar_->set_status("Not connected"); - allow_connect = true; - allow_disconnect = false; - break; - case Monitor::CONNECTED: - statusbar_->set_status("Connected"); - allow_connect = false; - allow_disconnect = true; - break; - case Monitor::CONNECTING: - statusbar_->set_status("Connecting..."); - allow_connect = false; - allow_disconnect = false; - break; - case Monitor::ATTACHED: - statusbar_->set_status("Connected and attached"); - allow_connect = false; - allow_disconnect = true; - break; + case Monitor::DISCONNECTED: + statusbar_->set_status("Not connected"); + allow_connect = true; + allow_disconnect = false; + break; + case Monitor::CONNECTED: + statusbar_->set_status("Connected"); + allow_connect = false; + allow_disconnect = true; + break; + case Monitor::CONNECTING: + statusbar_->set_status("Connecting..."); + allow_connect = false; + allow_disconnect = false; + break; + case Monitor::ATTACHED: + statusbar_->set_status("Connected and attached"); + allow_connect = false; + allow_disconnect = true; + break; } + menu_->enable_item(ACTION_SETUP, allow_connect); menu_->enable_item(ACTION_CONNECT, allow_connect); menu_->enable_item(ACTION_DISCONNECT, allow_disconnect); } @@ -474,6 +674,15 @@ private: } } + void setup_proxy() { + if (!proxy_config_) { + proxy_config_.reset(new MemoryConfig()); + } + if (!proxy_logger_) { + proxy_logger_.reset(new StringLogger()); + } + } + std::unique_ptr<PackageList> packages_; std::unique_ptr<GuiMain> main_; std::unique_ptr<GuiMenu> menu_; @@ -483,6 +692,9 @@ private: std::unique_ptr<Looper> looper_; std::unique_ptr<Resolver> resolver_; std::unique_ptr<Monitor> monitor_; + std::unique_ptr<Config> proxy_config_; + std::unique_ptr<StringLogger> proxy_logger_; + std::unique_ptr<Proxy> proxy_; bool has_selection_; size_t selection_; }; diff --git a/src/monitor.cc b/src/monitor.cc index fdf226c..9b57141 100644 --- a/src/monitor.cc +++ b/src/monitor.cc @@ -39,6 +39,12 @@ public: } } + void connect(int fd) override { + do_disconnect(); + new_state(CONNECTING); + resolved(fd, true, nullptr); + } + void disconnect() override { do_disconnect(); new_state(DISCONNECTED); diff --git a/src/monitor.hh b/src/monitor.hh index 83652ee..6c2a557 100644 --- a/src/monitor.hh +++ b/src/monitor.hh @@ -53,6 +53,7 @@ public: Looper* looper, Resolver* resolver, Delegate* delegate); virtual void connect(std::string const& host, uint16_t port) = 0; + virtual void connect(int fd) = 0; virtual void disconnect() = 0; virtual void attach() = 0; diff --git a/src/proxy.cc b/src/proxy.cc index 639bb64..5e9f177 100644 --- a/src/proxy.cc +++ b/src/proxy.cc @@ -261,10 +261,11 @@ struct Monitor : public BaseClient { class ProxyImpl : public Proxy { public: ProxyImpl(Config* config, std::string const& cwd, char const* configfile, - char const* logfile, Logger* logger, int accept_fd, int monitor_fd) + char const* logfile, Logger* logger, int accept_fd, int monitor_fd, + Looper* looper, Resolver* resolver) : config_(config), cwd_(cwd), configfile_(configfile), logfile_(logfile), logger_(logger), accept_socket_(accept_fd), monitor_socket_(monitor_fd), - looper_(Looper::create()), resolver_(Resolver::create(looper_.get())), + looper_(looper), resolver_(resolver), new_timeout_(nullptr), timeout_(nullptr), next_package_id_(1), monitor_send_proxied_(false) { setup(); @@ -320,6 +321,7 @@ private: void fatal_error(); void new_client(int fd, uint8_t events); void new_monitor(int fd, uint8_t events); + void setup_monitor(int fd); void new_base(BaseClient* client, int fd); void signal_event(int fd, uint8_t events); bool base_event(BaseClient* client, uint8_t events, @@ -379,8 +381,8 @@ private: io::auto_fd accept_socket_; io::auto_fd monitor_socket_; io::auto_pipe signal_pipe_; - std::unique_ptr<Looper> looper_; - std::unique_ptr<Resolver> resolver_; + Looper* looper_; + Resolver* resolver_; std::unique_ptr<Mitm> mitm_; bool good_; void* new_timeout_; @@ -423,11 +425,15 @@ void ProxyImpl::setup() { std::placeholders::_1, std::placeholders::_2)); if (monitor_socket_) { - looper_->add(monitor_socket_.get(), - monitors_.full() ? 0 :Looper::EVENT_READ, - std::bind(&ProxyImpl::new_monitor, this, - std::placeholders::_1, - std::placeholders::_2)); + if (config_->get("__one_single_monitor", false)) { + setup_monitor(monitor_socket_.release()); + } else { + looper_->add(monitor_socket_.get(), + monitors_.full() ? 0 :Looper::EVENT_READ, + std::bind(&ProxyImpl::new_monitor, this, + std::placeholders::_1, + std::placeholders::_2)); + } } else { monitors_.clear(); } @@ -1749,6 +1755,18 @@ void ProxyImpl::new_client(int fd, uint8_t events) { } } +void ProxyImpl::setup_monitor(int fd) { + auto index = monitors_.new_client(); + new_base(&monitors_[index], fd); + monitors_[index].got_hello = false; + looper_->add(fd, monitors_[index].read_flag + | monitors_[index].write_flag, + std::bind(&ProxyImpl::monitor_event, this, + index, + std::placeholders::_1, + std::placeholders::_2)); +} + void ProxyImpl::new_monitor(int fd, uint8_t events) { assert(fd == monitor_socket_.get()); if (events == Looper::EVENT_READ) { @@ -1761,15 +1779,7 @@ void ProxyImpl::new_monitor(int fd, uint8_t events) { logger_->out(Logger::WARN, "Accept failed: %s", strerror(errno)); return; } - auto index = monitors_.new_client(); - new_base(&monitors_[index], ret); - monitors_[index].got_hello = false; - looper_->add(ret, monitors_[index].read_flag - | monitors_[index].write_flag, - std::bind(&ProxyImpl::monitor_event, this, - index, - std::placeholders::_1, - std::placeholders::_2)); + setup_monitor(ret); break; } if (monitors_.full()) looper_->modify(fd, 0); @@ -1948,7 +1958,8 @@ int setup_socket(char const* host, std::string const& port, Logger* logger) { hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_PASSIVE; struct addrinfo* result; - auto retval = getaddrinfo(host, port.c_str(), &hints, &result); + auto retval = getaddrinfo(host && *host ? host : nullptr, port.c_str(), + &hints, &result); if (retval) { logger->out(Logger::ERR, "getaddrinfo failed for %s:%s: %s", host ? host : "*", port.c_str(), gai_strerror(retval)); @@ -1987,9 +1998,10 @@ Proxy* Proxy::create(Config* config, std::string const& cwd, char const* logfile, Logger* logger, int accept_fd, - int monitor_fd) { + int monitor_fd, + Looper* looper, Resolver* resolver) { return new ProxyImpl(config, cwd, configfile, logfile, logger, - accept_fd, monitor_fd); + accept_fd, monitor_fd, looper, resolver); } // static diff --git a/src/proxy.hh b/src/proxy.hh index 6545152..a1ddce2 100644 --- a/src/proxy.hh +++ b/src/proxy.hh @@ -7,6 +7,8 @@ class Config; class Logger; +class Looper; +class Resolver; class Proxy { public: @@ -16,7 +18,9 @@ public: char const* configfile, char const* logfile, Logger* logger, - int accept_fd, int monitor_fd); + int accept_fd, int monitor_fd, + Looper* looper, + Resolver* resolver); static int setup_accept_socket(Config* config, Logger* logger); static int setup_monitor_socket(Config* config, Logger* logger); |
