diff options
Diffstat (limited to 'src/proxy.cc')
| -rw-r--r-- | src/proxy.cc | 160 |
1 files changed, 154 insertions, 6 deletions
diff --git a/src/proxy.cc b/src/proxy.cc index 5abe257..fdb6df0 100644 --- a/src/proxy.cc +++ b/src/proxy.cc @@ -27,6 +27,7 @@ #include "io.hh" #include "logger.hh" #include "looper.hh" +#include "mitm.hh" #include "resolver.hh" #include "paths.hh" #include "proxy.hh" @@ -217,6 +218,9 @@ struct Content { struct Connect { std::string host; uint16_t port; + Mitm::DetectResult mitm_detect; + std::unique_ptr<Mitm::Connection> mitm; + std::unique_ptr<Mitm::Monitor> mitm_monitor; }; enum RemoteState { @@ -290,6 +294,27 @@ public: bool run() override; private: + class MitmMonitor : public Mitm::Monitor { + public: + MitmMonitor(ProxyImpl* proxy, uint32_t local_pkg_id, uint32_t remote_pkg_id) + : proxy_(proxy), local_pkg_id_(local_pkg_id), + remote_pkg_id_(remote_pkg_id) { + assert(local_pkg_id_ && remote_pkg_id_); + } + + void local2remote(void const* data, size_t size) override { + proxy_->send_attached_data(local_pkg_id_, data, size, false); + } + void remote2local(void const* data, size_t size) override { + proxy_->send_attached_data(remote_pkg_id_, data, size, false); + } + + private: + ProxyImpl* const proxy_; + uint32_t const local_pkg_id_; + uint32_t const remote_pkg_id_; + }; + void setup(); bool reload_config(); void fatal_error(); @@ -312,6 +337,8 @@ private: std::chrono::duration<float> const& timeout); bool base_send(BaseClient* client, void const* data, size_t size, size_t index, char const* name); + bool base_continue_send(BaseClient* client, bool was_empty, + size_t index, char const* name); void client_error(size_t index, uint16_t status_code, std::string const& status); bool client_request(size_t index); @@ -354,6 +381,7 @@ private: io::auto_pipe signal_pipe_; std::unique_ptr<Looper> looper_; std::unique_ptr<Resolver> resolver_; + std::unique_ptr<Mitm> mitm_; bool good_; void* new_timeout_; void* timeout_; @@ -385,6 +413,10 @@ void ProxyImpl::setup() { monitor_send_proxied_ = !config_->get("monitor_proxy_request", false); clients_.resize(get_size(config_, logger_, "max_clients", 1024)); monitors_.resize(get_size(config_, logger_, "max_monitors", 2)); + mitm_.reset(Mitm::create(logger_, config_, cwd_)); + if (mitm_) { + logger_->out(Logger::INFO, "MITM SSL Interception active"); + } looper_->add(accept_socket_.get(), clients_.full() ? 0 : Looper::EVENT_READ, std::bind(&ProxyImpl::new_client, this, @@ -437,6 +469,13 @@ bool ProxyImpl::reload_config() { logger_ = priv_logger_.get(); } } + if (mitm_) { + if (!mitm_->reload(config_, cwd_)) { + logger_->out(Logger::WARN, "Invalid mitm config, ignored"); + } + } else { + mitm_.reset(Mitm::create(logger_, config_, cwd_)); + } auto const old_accept = accept_socket_.get(); auto const old_monitor = monitor_socket_.get(); looper_->remove(old_accept); @@ -653,6 +692,37 @@ bool ProxyImpl::base_send(BaseClient* client, void const* data, size_t size, return true; } +bool ProxyImpl::base_continue_send(BaseClient* client, bool was_empty, + size_t index, char const* name) { + if (client->out->empty()) return true; + if (!was_empty) { + // Already waiting for write event + return true; + } + size_t avail; + auto ptr = client->out->read_ptr(&avail); + auto ret = io::write(client->fd.get(), ptr, avail); + if (ret == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + logger_->out(Logger::INFO, "%zu: %s write error: %s", index, name, + strerror(errno)); + return false; + } + } else { + client->out->consume(ret); + if (static_cast<size_t>(ret) == avail) { + if (!client->in) { + // If input is closed, close after sending all data + return false; + } + return true; + } + } + client->write_flag = Looper::EVENT_WRITE; + looper_->modify(client->fd.get(), client->read_flag | client->write_flag); + return true; +} + bool ProxyImpl::base_event(BaseClient* client, uint8_t events, size_t index, char const* name) { if (events & Looper::EVENT_READ) { @@ -964,14 +1034,54 @@ void ProxyImpl::client_empty_input(size_t index) { // falltrough case CONTENT_NONE: { if (client.connect && client.remote_state == CONNECTED) { - if (client.pkg_id != 0) { - send_attached_data(client.pkg_id, ptr, avail, false); + if (client.connect->mitm_detect == Mitm::NEED_MORE) { + client.connect->mitm_detect = mitm_->detect(ptr, avail); + if (client.connect->mitm_detect == Mitm::NEED_MORE) { + // Need more data + return; + } + if (client.connect->mitm_detect == Mitm::SSL) { + client.connect->mitm.reset(mitm_->open(client.connect->host)); + if (!client.connect->mitm) { + client.connect->mitm_detect = Mitm::OTHER; + } else if (client.pkg_id != 0) { + client.connect->mitm_monitor.reset( + new MitmMonitor(this, client.pkg_id, client.remote.pkg_id)); + } + } } - if (!base_send(&client.remote, ptr, avail, index, "Client remote")) { - return; + if (client.connect->mitm) { + bool local_empty = client.out->empty(); + bool remote_empty = client.remote.out->empty(); + // TODO(the_jk): Monitor + if (!client.connect->mitm->transfer( + client.in.get(), client.out.get(), + client.remote.in.get(), client.remote.out.get(), + client.connect->mitm_monitor.get())) { + close_client(index); + return; + } + if (!base_continue_send(&client, local_empty, index, "Client")) { + close_client(index); + return; + } + if (!base_continue_send(&client.remote, remote_empty, index, + "Client remote")) { + client_remote_error(index, 502); + return; + } + break; + } else { + if (client.pkg_id != 0) { + send_attached_data(client.pkg_id, ptr, avail, false); + } + if (!base_send(&client.remote, ptr, avail, index, "Client remote")) { + client_remote_error(index, 502); + return; + } + client.in->consume(avail); + break; } - client.in->consume(avail); - break; } if (client.remote_state != CLOSED && client.remote_state != WAITING) { // Still working on the last request, wait @@ -1003,6 +1113,11 @@ void ProxyImpl::client_empty_input(size_t index) { client_error(index, 400, "Bad request"); return; } + if (mitm_) { + client.connect->mitm_detect = Mitm::NEED_MORE; + } else { + client.connect->mitm_detect = Mitm::OTHER; + } } else { client.url.reset(Url::parse(client.request->url())); if (!client.url) { @@ -1132,6 +1247,7 @@ void ProxyImpl::client_remote_event(size_t index, int fd, uint8_t events) { client.source_host, client.source_port, false); } if (!base_send(&client, data.data(), data.size(), index, "Client")) { + close_client(index); return; } events &= ~Looper::EVENT_WRITE; @@ -1280,6 +1396,7 @@ void ProxyImpl::client_remote_event(size_t index, int fd, uint8_t events) { } } if (!base_send(&client, ptr, response->size(), index, "Client")) { + close_client(index); return; } client.remote.in->consume(response->size()); @@ -1291,6 +1408,7 @@ void ProxyImpl::client_remote_event(size_t index, int fd, uint8_t events) { send_attached_data(client.remote.pkg_id, ptr, avail, false); } if (!base_send(&client, ptr, avail, index, "Client")) { + close_client(index); return; } client.remote.in->consume(avail); @@ -1304,6 +1422,7 @@ void ProxyImpl::client_remote_event(size_t index, int fd, uint8_t events) { } if (!base_send(&client, ptr, client.remote.content.len, index, "Client")) { + close_client(index); return; } client.remote.in->consume(client.remote.content.len); @@ -1326,6 +1445,7 @@ void ProxyImpl::client_remote_event(size_t index, int fd, uint8_t events) { send_attached_data(client.remote.pkg_id, ptr, used, false); } if (!base_send(&client, ptr, used, index, "Client")) { + close_client(index); return; } client.remote.in->consume(used); @@ -1344,10 +1464,38 @@ void ProxyImpl::client_remote_event(size_t index, int fd, uint8_t events) { break; } case CONTENT_CLOSE: + if (client.connect) { + if (client.connect->mitm_detect == Mitm::NEED_MORE) { + // Need more data (on client side) + return; + } + if (client.connect->mitm) { + bool local_empty = client.out->empty(); + bool remote_empty = client.remote.out->empty(); + // TODO(the_jk): Monitor + if (!client.connect->mitm->transfer( + client.in.get(), client.out.get(), + client.remote.in.get(), client.remote.out.get(), + client.connect->mitm_monitor.get())) { + close_client(index); + return; + } + if (!base_continue_send(&client, local_empty, index, "Client")) { + return; + } + if (!base_continue_send(&client.remote, remote_empty, index, + "Client remote")) { + client_remote_error(index, 502); + return; + } + break; + } + } if (client.remote.pkg_id != 0) { send_attached_data(client.remote.pkg_id, ptr, avail, false); } if (!base_send(&client, ptr, avail, index, "Client")) { + close_client(index); return; } client.remote.in->consume(avail); |
