From 0596211e959401dd07dc7189c4fd4949e7f7540e Mon Sep 17 00:00:00 2001 From: Joel Klinghed Date: Tue, 8 Aug 2017 22:23:56 +0200 Subject: Handle pipelined http requests over SSL --- src/proxy.cc | 280 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 266 insertions(+), 14 deletions(-) diff --git a/src/proxy.cc b/src/proxy.cc index 3582b4f..293e9be 100644 --- a/src/proxy.cc +++ b/src/proxy.cc @@ -317,23 +317,35 @@ public: 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_); - } + typedef std::function NewPackageIdCallback; - 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); - } + MitmMonitor(ProxyImpl* proxy, uint32_t local_pkg_id, uint32_t remote_pkg_id, + NewPackageIdCallback const& new_pkg_id_callback); + + void local2remote(void const* data, size_t size) override; + void remote2local(void const* data, size_t size) override; private: + enum State { + NEED_MORE, + HTTP, + UNKNOWN, + }; + + size_t handle_http_data(Content* content, std::string* buffer, + uint32_t pkg_id, void const* data, size_t size); + ProxyImpl* const proxy_; - uint32_t const local_pkg_id_; - uint32_t const remote_pkg_id_; + uint32_t local_pkg_id_; + uint32_t remote_pkg_id_; + bool first_local_; + bool first_remote_; + NewPackageIdCallback const new_pkg_id_callback_; + State state_; + std::string req_buffer_; + Content request_; + std::string resp_buffer_; + Content response_; }; void setup(); @@ -384,6 +396,7 @@ private: void send_attached_data(uint32_t id, void const* ptr, size_t size, bool last); void send_attached(void const* header, size_t header_size, void const* data, size_t data_size); + uint32_t new_package_id_for_connection(size_t index, bool remote); Config* const config_; std::string cwd_; @@ -1048,7 +1061,10 @@ void ProxyImpl::client_empty_input(size_t index) { 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)); + new MitmMonitor( + this, client.pkg_id, client.remote.pkg_id, + std::bind(&ProxyImpl::new_package_id_for_connection, + this, index, std::placeholders::_1))); } } } @@ -1966,6 +1982,242 @@ int setup_socket(char const* host, std::string const& port, Logger* logger) { return ret.release(); } +uint32_t ProxyImpl::new_package_id_for_connection(size_t index, bool remote) { + auto& client = clients_[index]; + if (remote) { + if (client.remote.pkg_id != 0) { + send_attached_data(client.remote.pkg_id, nullptr, 0, true); + } + client.remote.pkg_id = get_next_package_id(); + if (client.remote.pkg_id != 0) { + send_attached_package(client.remote.pkg_id, 0, + client.remote.host, client.remote.port, + client.source_host, client.source_port, false); + } + return client.remote.pkg_id; + } + + if (client.pkg_id != 0) { + send_attached_data(client.pkg_id, nullptr, 0, true); + } + client.pkg_id = get_next_package_id(); + if (client.pkg_id != 0) { + send_attached_package(client.pkg_id, 0, + client.source_host, client.source_port, + client.remote.host, client.remote.port, false); + } + return client.pkg_id; +} + +ProxyImpl::MitmMonitor::MitmMonitor( + ProxyImpl* proxy, uint32_t local_pkg_id, uint32_t remote_pkg_id, + NewPackageIdCallback const& new_pkg_id_callback) + : proxy_(proxy), local_pkg_id_(local_pkg_id), + remote_pkg_id_(remote_pkg_id), + first_local_(true), + first_remote_(true), + new_pkg_id_callback_(new_pkg_id_callback), + state_(NEED_MORE) { + assert(local_pkg_id_ && remote_pkg_id_); + request_.type = CONTENT_NONE; + response_.type = CONTENT_NONE; +} + +void ProxyImpl::MitmMonitor::local2remote(void const* data, size_t size) { + switch (state_) { + case NEED_MORE: + case HTTP: { + while (size) { + if (state_ == HTTP && request_.type != CONTENT_NONE) { + auto used = handle_http_data(&request_, &req_buffer_, local_pkg_id_, + data, size); + if (used == size) return; + data = reinterpret_cast(data) + used; + size -= used; + } + auto d = reinterpret_cast(data); + std::unique_ptr req; + size_t offset; + if (req_buffer_.empty()) { + req.reset(HttpRequest::parse(d, size, false)); + if (!req) { + req_buffer_.assign(d, size); + break; + } + offset = 0; + } else { + offset = req_buffer_.size(); + req_buffer_.append(d, size); + req.reset(HttpRequest::parse(req_buffer_.data(), + req_buffer_.size(), false)); + if (!req) break; + } + if (!req->good()) { + state_ = UNKNOWN; + req_buffer_.clear(); + break; + } + if (!setup_content(req.get(), &request_)) { + state_ = UNKNOWN; + req_buffer_.clear(); + break; + } + req_buffer_.clear(); + state_ = HTTP; + auto used = req->size() - offset; + if (first_local_) { + first_local_ = false; + } else { + local_pkg_id_ = new_pkg_id_callback_(false); + } + if (local_pkg_id_) { + proxy_->send_attached_data(local_pkg_id_, data, used, false); + } + size -= used; + data = d + used; + } + } + case UNKNOWN: + break; + } + + if (local_pkg_id_) { + proxy_->send_attached_data(local_pkg_id_, data, size, false); + } +} + +void ProxyImpl::MitmMonitor::remote2local(void const* data, size_t size) { + switch (state_) { + case NEED_MORE: + // If we're already getting a response then give up on detection + // on request + state_ = UNKNOWN; + req_buffer_.clear(); + break; + case HTTP: { + while (size) { + if (response_.type != CONTENT_NONE) { + auto used = handle_http_data(&response_, &resp_buffer_, remote_pkg_id_, + data, size); + if (used == size) return; + data = reinterpret_cast(data) + used; + size -= used; + } + auto d = reinterpret_cast(data); + std::unique_ptr resp; + size_t offset; + if (resp_buffer_.empty()) { + resp.reset(HttpResponse::parse(d, size, false)); + if (!resp) { + resp_buffer_.assign(d, size); + break; + } + offset = 0; + } else { + offset = resp_buffer_.size(); + resp_buffer_.append(d, size); + resp.reset(HttpResponse::parse(resp_buffer_.data(), + resp_buffer_.size(), false)); + if (!resp) break; + } + if (!resp->good()) { + resp_buffer_.clear(); + break; + } + if (!setup_content(resp.get(), &response_)) { + resp_buffer_.clear(); + break; + } + resp_buffer_.clear(); + auto used = resp->size() - offset; + if (first_remote_) { + first_remote_ = false; + } else { + remote_pkg_id_ = new_pkg_id_callback_(true); + } + if (remote_pkg_id_) { + proxy_->send_attached_data(remote_pkg_id_, data, used, false); + } + size -= used; + data = d + used; + } + } + case UNKNOWN: + break; + } + + if (remote_pkg_id_) { + proxy_->send_attached_data(remote_pkg_id_, data, size, false); + } +} + +size_t ProxyImpl::MitmMonitor::handle_http_data(Content* content, + std::string* buffer, + uint32_t pkg_id, + void const* data, + size_t size) { + switch (content->type) { + case CONTENT_NONE: + assert(false); + return 0; + case CONTENT_CLOSE: + if (pkg_id) { + proxy_->send_attached_data(pkg_id, data, size, false); + } + return size; + case CONTENT_LEN: + if (size < content->len) { + if (pkg_id) { + proxy_->send_attached_data(pkg_id, data, size, false); + } + content->len -= size; + return size; + } + if (pkg_id) { + // Don't set last == true even tho it is because that is handled + // by new_package_id_callback + proxy_->send_attached_data(pkg_id, data, content->len, false); + } + content->type = CONTENT_NONE; + return content->len; + case CONTENT_CHUNKED: { + size_t used; + if (buffer->empty()) { + used = content->chunked->add(data, size); + } else { + auto offset = buffer->size(); + buffer->append(reinterpret_cast(data), size); + used = content->chunked->add(buffer->data(), buffer->size()); + buffer->erase(0, used); + used -= offset; + } + if (!content->chunked->good()) { + // TODO: Can we handle this better? + content->type = CONTENT_NONE; + buffer->clear(); + return used; + } + if (pkg_id) { + proxy_->send_attached_data(pkg_id, data, used, false); + } + if (content->chunked->eof()) { + assert(buffer->size() <= size - used); + content->type = CONTENT_NONE; + buffer->clear(); + return used; + } + if (used < size) { + auto d = reinterpret_cast(data); + buffer->append(d + used, size - used); + if (pkg_id) { + proxy_->send_attached_data(pkg_id, d + used, size - used, false); + } + } + return size; + } + } +} + } // namespace // static -- cgit v1.2.3-70-g09d2