in modules/http/mod-ssltunnel.cpp [141:252]
int tunnel(conn_rec* const conn, const string& ca, const string& cert, const string& key, const string& url, const string& preamble, const gc_pool& p, unused ap_filter_t* const ifilter, ap_filter_t* const ofilter) {
// Create input/output bucket brigades
apr_bucket_brigade* const ib = apr_brigade_create(pool(p), conn->bucket_alloc);
apr_bucket_brigade* const ob = apr_brigade_create(pool(p), conn->bucket_alloc);
// Get client connection socket
apr_socket_t* const csock = (apr_socket_t*)ap_get_module_config(conn->conn_config, &core_module);
// Open connection to target
const http::CURLSession cs(ca, cert, key, emptyString, 0);
const failable<bool> crc = http::connect(url, cs);
if (!hasContent(crc))
return abort(conn, csock, reason(crc));
apr_socket_t* const tsock = http::sock(cs);
// Send preamble
if (length(preamble) != 0) {
debug(preamble, "modssltunnel::tunnel::sendPreambleToTarget");
const failable<bool> src = http::send(c_str(preamble), length(preamble), cs);
if (!hasContent(src))
return abort(conn, csock, string("Couldn't send to target: ") + reason(src));
}
// Create a pollset for the client and target sockets
apr_pollset_t* pollset;
const apr_status_t cprc = apr_pollset_create(&pollset, 2, pool(p), 0);
if (cprc != APR_SUCCESS)
return abort(conn, csock, http::apreason(cprc));
const apr_pollfd_t* cpollfd = http::pollfd(csock, APR_POLLIN, p);
apr_pollset_add(pollset, cpollfd);
const apr_pollfd_t* tpollfd = http::pollfd(tsock, APR_POLLIN, p);
apr_pollset_add(pollset, tpollfd);
// Relay traffic in both directions until end of stream
const apr_pollfd_t* pollfds = cpollfd;
apr_int32_t pollcount = 1;
for(;;) {
for (; pollcount > 0; pollcount--, pollfds++) {
if (pollfds->rtnevents & APR_POLLIN) {
if (pollfds->desc.s == csock) {
// Receive buckets from client
const apr_status_t getrc = ap_get_brigade(conn->input_filters, ib, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN);
if (getrc != APR_SUCCESS)
return abort(conn, csock, string("Couldn't receive from client"));
for (apr_bucket* bucket = APR_BRIGADE_FIRST(ib); bucket != APR_BRIGADE_SENTINEL(ib); bucket = APR_BUCKET_NEXT(bucket)) {
if (APR_BUCKET_IS_FLUSH(bucket))
continue;
// Client connection closed
if (APR_BUCKET_IS_EOS(bucket))
return close(conn, csock);
const char *data;
apr_size_t rl;
apr_bucket_read(bucket, &data, &rl, APR_BLOCK_READ);
if (rl > 0) {
debug(string(data, rl), "modssltunnel::tunnel::sendToTarget");
// Send to target
const failable<bool> src = http::send(data, rl, cs);
if (!hasContent(src))
return abort(conn, csock, string("Couldn't send to target: ") + reason(src));
}
}
apr_brigade_cleanup(ib);
} else {
// Receive from target
char data[8192];
const failable<size_t> frl = http::recv(data, sizeof(data), cs);
if (!hasContent(frl))
return abort(conn, csock, string("Couldn't receive from target") + reason(frl));
const size_t rl = content(frl);
// Target connection closed
if (rl == 0)
return close(conn, csock);
// Send bucket to client
debug(string(data, rl), "modssltunnel::tunnel::sendToClient");
APR_BRIGADE_INSERT_TAIL(ob, apr_bucket_transient_create(data, rl, conn->bucket_alloc));
APR_BRIGADE_INSERT_TAIL(ob, apr_bucket_flush_create(conn->bucket_alloc));
if (ap_pass_brigade(ofilter, ob) != APR_SUCCESS)
return abort(conn, csock, "Couldn't send data bucket to client");
apr_brigade_cleanup(ob);
}
}
// Error
if (pollfds->rtnevents & (APR_POLLERR | APR_POLLHUP | APR_POLLNVAL)) {
if (pollfds->desc.s == csock)
return abort(conn, csock, "Couldn't receive from client");
else
return abort(conn, csock, "Couldn't receive from target");
}
}
// Poll the client and target sockets
debug("modssltunnel::tunnel::poll");
const apr_status_t pollrc = apr_pollset_poll(pollset, -1, &pollcount, &pollfds);
if (pollrc != APR_SUCCESS)
return abort(conn, csock, "Couldn't poll sockets");
debug(pollcount, "modssltunnel::tunnel::pollfds");
}
// Close client connection
return close(conn, csock);
}