#pragma once
#include "common.hpp"
#include "config.hpp"
#include "dns.hpp"
#include "hooks.hpp"
#include "sock.hpp"
#include "stats.hpp"
#include "sni.hpp"
#include "proxy.hpp"
#include "bump.hpp"
#include "io.hpp"
#include "ac.hpp"
/**\verbatim
* AioFile IoSource client_in --> chain_t upstream --> IoSink server_out --> chain_t upstream_splice
* ^ v v v
* AioSink client_splice IoWrapper client IoWrapper server AioSink server_splice
* ^ ^ ^ v
* chain_t downstream_splice <-- IoSink client_out <-- chain_t downstream <-- IoSource server_in AioFile
\endverbatim*/
class Ctx {
private:
typedef enum {
STATE_INIT,
STATE_PROXY,
STATE_DNS,
STATE_SNI,
STATE_BUMP_OP,
STATE_AC,
STATE_BUMP,
STATE_HTTP_REQ,
STATE_DENY,
STATE_CONNECT,
STATE_CERT_CN,
STATE_CN_AC,
STATE_IO
} state_t;
state_t state, max_state;
typedef struct {
bool internal; ///< not possible outside handler
bool short_timeout; ///< want to be notified after the short timeout elapsed?
event_t client_ign_ev; ///< if != EVENT_NONE, don't listen for server events (only CLOSE, if any), don't care about server status for client events, and don't listen for given client events, even if they would be possible chain-wise (might be combined w/ #server_ign_ev)
event_t server_ign_ev; ///< @see #client_ign_ev
} state_info_t; ///< state metadata for sanity checks and update_events()
static const state_info_t state_info[STATE_IO+1];
const sockaddr_t src;
sockaddr_t dst;
IoWrapper client, server;
IoSource client_in, server_in;
IoSink client_out, server_out;
chain_t upstream, downstream;
#if (SPLICE)
AioSink client_splice, server_splice;
chain_t upstream_splice, downstream_splice;
#endif
char* host; ///< the SNI
buf_t* host_peek_buf; ///< pre-read client data peeked for getting the SNI
DNSResult* resolver; ///< in case we have to resolve a HTTP CONNECT tunnel destination
AccessControlResult* ac; ///< holds the ac result, if any
bump_op_t bump_op;
Bump* bump; ///< holds the ssl stuff for the client side, if any
tristate_t is_http; ///< whether we already have found a client-side HTTP request (if bumped)
time_t last_io; ///< for long downloads etc. one side might legitimately timeout. so we use the idle time to timeout only if we did not see anything from either side.
unsigned last_pollid; ///< remember when we're called the last time
static unsigned last_ctx_no;
const unsigned ctx_no;
logctx_t log_ctx;
const msec_t start_msec;
void do_io();
void s_handler(event_t);
void c_handler(event_t);
void access_log();
public:
Ctx(int, sockaddr_t, sockaddr_t); ///< on freshly accepted connection for #client
~Ctx();
static unsigned ctx_num; ///< how many instances exist right now
bool update_events(poll_handler_t); ///< returns whether both sides are finished/closed
bool handler(int, event_t, unsigned); ///< returns false upon noop
};
class CtxAcceptor {
private:
static void handler(int, event_t, unsigned, void*);
public:
static void accept_handler(int, event_t, unsigned, void*); ///< creates new instance for every accepted connection
};