summaryrefslogtreecommitdiff
path: root/src/proxy.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/proxy.cc')
-rw-r--r--src/proxy.cc160
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);