aLogEntry->http.method = request->method;
aLogEntry->http.version = request->http_ver;
aLogEntry->hier = request->hier;
- if (request->content_length > 0) // negative when no body or unknown length
- aLogEntry->http.clientRequestSz.payloadData += request->content_length; // XXX: actually adaptedRequest payload size ??
aLogEntry->cache.extuser = request->extacl_user.termedBuf();
// Adapted request, if any, inherits and then collects all the stats, but
al->cache.objectSize = loggingEntry()->contentLen(); // payload duplicate ?? with or without TE ?
al->http.clientRequestSz.header = req_sz;
+ // the virgin request is saved to al->request
+ if (al->request && al->request->body_pipe)
+ al->http.clientRequestSz.payloadData = al->request->body_pipe->producedSize();
al->http.clientReplySz.header = out.headers_sz;
// XXX: calculate without payload encoding or headers !!
al->http.clientReplySz.payloadData = out.size - out.headers_sz; // pretend its all un-encoded data for now.
ConnStateData::swanSong()
{
debugs(33, 2, HERE << clientConnection);
+ checkLogging();
+
flags.readMore = false;
- DeregisterRunner(this);
clientdbEstablished(clientConnection->remote, -1); /* decrement */
pipeline.terminateAll(0);
// swanSong() in the close handler will cleanup.
if (Comm::IsConnOpen(clientConnection))
clientConnection->close();
-
- // deregister now to ensure finalShutdown() does not kill us prematurely.
- // fd_table purge will cleanup if close handler was not fast enough.
- DeregisterRunner(this);
}
char *
} // else nothing to alter port-wise.
const int url_sz = hp->requestUri().length() + 32 + Config.appendDomainLen + strlen(host);
http->uri = (char *)xcalloc(url_sz, 1);
- snprintf(http->uri, url_sz, "%s://%s" SQUIDSBUFPH, AnyP::UriScheme(conn->transferProtocol.protocol).c_str(), host, SQUIDSBUFPRINT(url));
+ const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
+ snprintf(http->uri, url_sz, SQUIDSBUFPH "://%s" SQUIDSBUFPH, SQUIDSBUFPRINT(scheme), host, SQUIDSBUFPRINT(url));
debugs(33, 5, "ACCEL VHOST REWRITE: " << http->uri);
} else if (conn->port->defaultsite /* && !vhost */) {
debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: defaultsite=" << conn->port->defaultsite << " + vport=" << vport);
if (vport > 0) {
snprintf(vportStr, sizeof(vportStr),":%d",vport);
}
- snprintf(http->uri, url_sz, "%s://%s%s" SQUIDSBUFPH,
- AnyP::UriScheme(conn->transferProtocol.protocol).c_str(), conn->port->defaultsite, vportStr, SQUIDSBUFPRINT(url));
+ const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
+ snprintf(http->uri, url_sz, SQUIDSBUFPH "://%s%s" SQUIDSBUFPH,
+ SQUIDSBUFPRINT(scheme), conn->port->defaultsite, vportStr, SQUIDSBUFPRINT(url));
debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: " << http->uri);
} else if (vport > 0 /* && (!vhost || no Host:) */) {
debugs(33, 5, "ACCEL VPORT REWRITE: *_port IP + vport=" << vport);
const int url_sz = hp->requestUri().length() + 32 + Config.appendDomainLen;
http->uri = (char *)xcalloc(url_sz, 1);
http->getConn()->clientConnection->local.toHostStr(ipbuf,MAX_IPSTRLEN);
- snprintf(http->uri, url_sz, "%s://%s:%d" SQUIDSBUFPH,
- AnyP::UriScheme(conn->transferProtocol.protocol).c_str(),
- ipbuf, vport, SQUIDSBUFPRINT(url));
+ const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
+ snprintf(http->uri, url_sz, SQUIDSBUFPH "://%s:%d" SQUIDSBUFPH,
+ SQUIDSBUFPRINT(scheme), ipbuf, vport, SQUIDSBUFPRINT(url));
debugs(33, 5, "ACCEL VPORT REWRITE: " << http->uri);
}
}
const int url_sz = hp->requestUri().length() + 32 + Config.appendDomainLen +
strlen(host);
http->uri = (char *)xcalloc(url_sz, 1);
- snprintf(http->uri, url_sz, "%s://%s" SQUIDSBUFPH,
- AnyP::UriScheme(conn->transferProtocol.protocol).c_str(), host, SQUIDSBUFPRINT(hp->requestUri()));
+ const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
+ snprintf(http->uri, url_sz, SQUIDSBUFPH "://%s" SQUIDSBUFPH,
+ SQUIDSBUFPRINT(scheme), host, SQUIDSBUFPRINT(hp->requestUri()));
debugs(33, 5, "TRANSPARENT HOST REWRITE: " << http->uri);
} else {
/* Put the local socket IP address as the hostname. */
http->uri = (char *)xcalloc(url_sz, 1);
static char ipbuf[MAX_IPSTRLEN];
http->getConn()->clientConnection->local.toHostStr(ipbuf,MAX_IPSTRLEN);
- snprintf(http->uri, url_sz, "%s://%s:%d" SQUIDSBUFPH,
- AnyP::UriScheme(http->getConn()->transferProtocol.protocol).c_str(),
+ const SBuf &scheme = AnyP::UriScheme(http->getConn()->transferProtocol.protocol).image();
+ snprintf(http->uri, url_sz, SQUIDSBUFPH "://%s:%d" SQUIDSBUFPH,
+ SQUIDSBUFPRINT(scheme),
ipbuf, http->getConn()->clientConnection->local.port(), SQUIDSBUFPRINT(hp->requestUri()));
debugs(33, 5, "TRANSPARENT REWRITE: " << http->uri);
}
}
if (!parsedOk) {
- if (hp->parseStatusCode == Http::scRequestHeaderFieldsTooLarge || hp->parseStatusCode == Http::scUriTooLong)
- return csd->abortRequestParsing("error:request-too-large");
-
- return csd->abortRequestParsing("error:invalid-request");
+ const bool tooBig =
+ hp->parseStatusCode == Http::scRequestHeaderFieldsTooLarge ||
+ hp->parseStatusCode == Http::scUriTooLong;
+ auto result = csd->abortRequestParsing(
+ tooBig ? "error:request-too-large" : "error:invalid-request");
+ // assume that remaining leftovers belong to this bad request
+ csd->consumeInput(csd->inBuf.length());
+ return result;
}
}
// In bump-server-first mode, we have not necessarily seen the intended
// server name at certificate-peeking time. Check for domain mismatch now,
// when we can extract the intended name from the bumped HTTP request.
- if (X509 *srvCert = sslServerBump->serverCert.get()) {
+ if (const Security::CertPointer &srvCert = sslServerBump->serverCert) {
HttpRequest *request = http->request;
- if (!Ssl::checkX509ServerValidity(srvCert, request->url.host())) {
+ if (!Ssl::checkX509ServerValidity(srvCert.get(), request->url.host())) {
debugs(33, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " <<
"does not match domainname " << request->url.host());
bool allowDomainMismatch = false;
if (Config.ssl_client.cert_error) {
ACLFilledChecklist check(Config.ssl_client.cert_error, request, dash_str);
- check.sslErrors = new Ssl::CertErrors(Ssl::CertError(SQUID_X509_V_ERR_DOMAIN_MISMATCH, srvCert));
+ check.sslErrors = new Security::CertErrors(Security::CertError(SQUID_X509_V_ERR_DOMAIN_MISMATCH, srvCert));
allowDomainMismatch = (check.fastCheck() == ACCESS_ALLOWED);
delete check.sslErrors;
check.sslErrors = NULL;
err->src_addr = clientConnection->remote;
Ssl::ErrorDetail *errDetail = new Ssl::ErrorDetail(
SQUID_X509_V_ERR_DOMAIN_MISMATCH,
- srvCert, NULL);
+ srvCert.get(), nullptr);
err->detail = errDetail;
// Save the original request for logging purposes.
if (!context->http->al->request) {
conn->pipeline.popMe(Http::StreamPointer(context));
}
Comm::SetSelect(conn->clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
- conn->fakeAConnectRequest("unknown-protocol", conn->preservedClientData);
- return true;
+ return conn->fakeAConnectRequest("unknown-protocol", conn->preservedClientData);
} else {
debugs(33, 3, "Continue with returning the error: " << requestError);
}
http->flags.internal = true;
} else if (Config.onoff.global_internal_static && internalStaticCheck(request->url.path())) {
debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true) << " (global_internal_static on)");
- request->url.setScheme(AnyP::PROTO_HTTP);
+ request->url.setScheme(AnyP::PROTO_HTTP, "http");
request->url.host(internalHostname());
request->url.port(getMyPort());
http->flags.internal = true;
// On errors, bodyPipe may become nil, but readMore will be cleared
while (!inBuf.isEmpty() && !bodyPipe && flags.readMore) {
- /* Don't try to parse if the buffer is empty */
- if (inBuf.isEmpty())
- break;
-
/* Limit the number of concurrent requests */
if (concurrentRequestQueueFilled())
break;
void
ConnStateData::afterClientRead()
{
+#if USE_OPENSSL
+ if (parsingTlsHandshake) {
+ parseTlsHandshake();
+ return;
+ }
+#endif
+
/* Process next request */
if (pipeline.empty())
fd_note(clientConnection->fd, "Reading next request");
needProxyProtocolHeader_(false),
#if USE_OPENSSL
switchedToHttps_(false),
+ parsingTlsHandshake(false),
sslServerBump(NULL),
signAlgorithm(Ssl::algSignTrusted),
#endif
// register to receive notice of Squid signal events
// which may affect long persisting client connections
- RegisterRunner(this);
+ registerRunner();
}
void
(transparent() || port->disable_pmtu_discovery == DISABLE_PMTU_ALWAYS)) {
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
int i = IP_PMTUDISC_DONT;
- if (setsockopt(clientConnection->fd, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0)
- debugs(33, 2, "WARNING: Path MTU discovery disabling failed on " << clientConnection << " : " << xstrerror());
+ if (setsockopt(clientConnection->fd, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0) {
+ int xerrno = errno;
+ debugs(33, 2, "WARNING: Path MTU discovery disabling failed on " << clientConnection << " : " << xstrerr(xerrno));
+ }
#else
static bool reported = false;
++incoming_sockets_accepted;
// Socket is ready, setup the connection manager to start using it
- ConnStateData *connState = Http::NewServer(xact);
- AsyncJob::Start(connState); // usually async-calls readSomeData()
+ auto *srv = Http::NewServer(xact);
+ AsyncJob::Start(srv); // usually async-calls readSomeData()
}
#if USE_OPENSSL
/** Create SSL connection structure and update fd_table */
-static Security::SessionPtr
+static bool
httpsCreate(const Comm::ConnectionPointer &conn, Security::ContextPtr sslContext)
{
- if (auto ssl = Ssl::CreateServer(sslContext, conn->fd, "client https start")) {
+ if (Ssl::CreateServer(sslContext, conn, "client https start")) {
debugs(33, 5, "will negotate SSL on " << conn);
- return ssl;
+ return true;
}
conn->close();
- return nullptr;
+ return false;
}
/**
switch (ssl_error) {
case SSL_ERROR_WANT_READ:
- Comm::SetSelect(fd, COMM_SELECT_READ, callback, conn, 0);
+ Comm::SetSelect(fd, COMM_SELECT_READ, callback, (callback != NULL ? conn : NULL), 0);
return 0;
case SSL_ERROR_WANT_WRITE:
- Comm::SetSelect(fd, COMM_SELECT_WRITE, callback, conn, 0);
+ Comm::SetSelect(fd, COMM_SELECT_WRITE, callback, (callback != NULL ? conn : NULL), 0);
return 0;
case SSL_ERROR_SYSCALL:
return;
}
- if (SSL_session_reused(ssl)) {
+ if (Security::SessionIsResumed(fd_table[fd].ssl)) {
debugs(83, 2, "clientNegotiateSSL: Session " << SSL_get_session(ssl) <<
" reused on FD " << fd << " (" << fd_table[fd].ipaddr << ":" << (int)fd_table[fd].remote_port << ")");
} else {
- if (do_debug(83, 4)) {
+ if (Debug::Enabled(83, 4)) {
/* Write out the SSL session details.. actually the call below, but
* OpenSSL headers do strange typecasts confusing GCC.. */
/* PEM_write_SSL_SESSION(debug_log, SSL_get_session(ssl)); */
}
// Connection established. Retrieve TLS connection parameters for logging.
- conn->clientConnection->tlsNegotiations()->fillWith(ssl);
+ conn->clientConnection->tlsNegotiations()->retrieveNegotiatedInfo(ssl);
client_cert = SSL_get_peer_certificate(ssl);
static void
httpsEstablish(ConnStateData *connState, Security::ContextPtr sslContext)
{
- Security::SessionPtr ssl = nullptr;
assert(connState);
const Comm::ConnectionPointer &details = connState->clientConnection;
- if (!sslContext || !(ssl = httpsCreate(details, sslContext)))
+ if (!sslContext || !httpsCreate(details, sslContext))
return;
typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
debugs(33, 2, HERE << "sslBump not needed for " << connState->clientConnection);
connState->sslBumpMode = Ssl::bumpNone;
}
- connState->fakeAConnectRequest("ssl-bump", connState->inBuf);
+ if (!connState->fakeAConnectRequest("ssl-bump", connState->inBuf))
+ connState->clientConnection->close();
}
/** handle a new HTTPS connection */
++incoming_sockets_accepted;
// Socket is ready, setup the connection manager to start using it
- ConnStateData *connState = Https::NewServer(xact);
- AsyncJob::Start(connState); // usually async-calls postHttpsAccept()
+ auto *srv = Https::NewServer(xact);
+ AsyncJob::Start(srv); // usually async-calls postHttpsAccept()
}
void
bool ret = Ssl::configureSSLUsingPkeyAndCertFromMemory(ssl, reply_message.getBody().c_str(), *port);
if (!ret)
debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
+
+ SSL_CTX *sslContext = SSL_get_SSL_CTX(ssl);
+ Ssl::configureUnconfiguredSslContext(sslContext, signAlgorithm, *port);
} else {
auto ctx = Ssl::generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str(), *port);
getSslContextDone(ctx, true);
void
ConnStateData::getSslContextStart()
{
- // XXX starting SSL with a pipeline of requests still waiting for non-SSL replies?
- assert(pipeline.count() < 2); // the CONNECT is okay for now. Anything else is a bug.
- pipeline.terminateAll(0);
- /* careful: terminateAll(0) above frees request, host, etc. */
+ // If we are called, then CONNECT has succeeded. Finalize it.
+ if (auto xact = pipeline.front()) {
+ if (xact->http && xact->http->request && xact->http->request->method == Http::METHOD_CONNECT)
+ xact->finished();
+ // cannot proceed with encryption if requests wait for plain responses
+ Must(pipeline.empty());
+ }
+ /* careful: finished() above frees request, host, etc. */
if (port->generateHostCertificates) {
Ssl::CertificateProperties certProperties;
auto ssl = fd_table[clientConnection->fd].ssl.get();
if (!Ssl::configureSSL(ssl, certProperties, *port))
debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
+
+ SSL_CTX *sslContext = SSL_get_SSL_CTX(ssl);
+ Ssl::configureUnconfiguredSslContext(sslContext, certProperties.signAlgorithm, *port);
} else {
auto dynCtx = Ssl::generateSslContext(certProperties, *port);
getSslContextDone(dynCtx, true);
// Try to add generated ssl context to storage.
if (port->generateHostCertificates && isNew) {
- if (signAlgorithm == Ssl::algSignTrusted) {
- // Add signing certificate to the certificates chain
- X509 *cert = port->signingCert.get();
- if (SSL_CTX_add_extra_chain_cert(sslContext, cert)) {
- // increase the certificate lock
- CRYPTO_add(&(cert->references),1,CRYPTO_LOCK_X509);
- } else {
- const int ssl_error = ERR_get_error();
- debugs(33, DBG_IMPORTANT, "WARNING: can not add signing certificate to SSL context chain: " << ERR_error_string(ssl_error, NULL));
- }
- Ssl::addChainToSslContext(sslContext, port->certsToChain.get());
+ if (sslContext && (signAlgorithm == Ssl::algSignTrusted)) {
+ Ssl::chainCertificatesToSSLContext(sslContext, *port);
+ } else if (signAlgorithm == Ssl::algSignTrusted) {
+ debugs(33, DBG_IMPORTANT, "WARNING: can not add signing certificate to SSL context chain because SSL context chain is invalid!");
}
//else it is self-signed or untrusted do not attrach any certificate
this, ConnStateData::requestTimeout);
commSetConnTimeout(clientConnection, Config.Timeout.request, timeoutCall);
- // Disable the client read handler until CachePeer selection is complete
- Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
- Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, clientNegotiateSSL, this, 0);
switchedToHttps_ = true;
+
+ auto ssl = fd_table[clientConnection->fd].ssl.get();
+ BIO *b = SSL_get_rbio(ssl);
+ Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
+ bio->setReadBufData(inBuf);
+ inBuf.clear();
+ clientNegotiateSSL(clientConnection->fd, this);
}
void
if (bumpServerMode == Ssl::bumpServerFirst && !sslServerBump) {
request->flags.sslPeek = true;
sslServerBump = new Ssl::ServerBump(request);
-
- // will call httpsPeeked() with certificate and connection, eventually
- FwdState::fwdStart(clientConnection, sslServerBump->entry, sslServerBump->request.getRaw());
- return;
} else if (bumpServerMode == Ssl::bumpPeek || bumpServerMode == Ssl::bumpStare) {
request->flags.sslPeek = true;
sslServerBump = new Ssl::ServerBump(request, NULL, bumpServerMode);
- startPeekAndSplice();
- return;
}
- // otherwise, use sslConnectHostOrIp
- getSslContextStart();
+ // commSetConnTimeout() was called for this request before we switched.
+ // Fix timeout to request_start_timeout
+ typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
+ AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
+ TimeoutDialer, this, ConnStateData::requestTimeout);
+ commSetConnTimeout(clientConnection, Config.Timeout.request_start_timeout, timeoutCall);
+ // Also reset receivedFirstByte_ flag to allow this timeout work in the case we have
+ // a bumbed "connect" request on non transparent port.
+ receivedFirstByte_ = false;
+ // Get more data to peek at Tls
+ parsingTlsHandshake = true;
+ readSomeData();
+}
+
+void
+ConnStateData::parseTlsHandshake()
+{
+ Must(parsingTlsHandshake);
+
+ assert(!inBuf.isEmpty());
+ receivedFirstByte();
+ fd_note(clientConnection->fd, "Parsing TLS handshake");
+
+ bool unsupportedProtocol = false;
+ try {
+ if (!tlsParser.parseHello(inBuf)) {
+ // need more data to finish parsing
+ readSomeData();
+ return;
+ }
+ }
+ catch (const std::exception &ex) {
+ debugs(83, 2, "error on FD " << clientConnection->fd << ": " << ex.what());
+ unsupportedProtocol = true;
+ }
+
+ parsingTlsHandshake = false;
+
+ // Even if the parser failed, each TLS detail should either be set
+ // correctly or still be "unknown"; copying unknown detail is a no-op.
+ clientConnection->tlsNegotiations()->retrieveParsedInfo(tlsParser.details);
+
+ // We should disable read/write handlers
+ Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
+ Comm::SetSelect(clientConnection->fd, COMM_SELECT_WRITE, NULL, NULL, 0);
+
+ if (!sslServerBump) { // BumpClientFirst mode does not use this member
+ getSslContextStart();
+ return;
+ } else if (sslServerBump->act.step1 == Ssl::bumpServerFirst) {
+ // will call httpsPeeked() with certificate and connection, eventually
+ FwdState::fwdStart(clientConnection, sslServerBump->entry, sslServerBump->request.getRaw());
+ } else {
+ Must(sslServerBump->act.step1 == Ssl::bumpPeek || sslServerBump->act.step1 == Ssl::bumpStare);
+ startPeekAndSplice(unsupportedProtocol);
+ }
}
bool
checklist.conn(this);
allow_t answer = checklist.fastCheck();
if (answer == ACCESS_ALLOWED && answer.kind == 1) {
- splice();
- return true;
+ return splice();
}
}
return false;
}
-/** negotiate an SSL connection */
-static void
-clientPeekAndSpliceSSL(int fd, void *data)
+void
+ConnStateData::startPeekAndSplice(const bool unsupportedProtocol)
{
- ConnStateData *conn = (ConnStateData *)data;
- auto ssl = fd_table[fd].ssl.get();
-
- debugs(83, 5, "Start peek and splice on FD " << fd);
-
- int ret = 0;
- if ((ret = Squid_SSL_accept(conn, clientPeekAndSpliceSSL)) < 0)
- debugs(83, 2, "SSL_accept failed.");
-
- BIO *b = SSL_get_rbio(ssl);
- assert(b);
- Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
- if (ret < 0) {
- const err_type err = bio->noSslClient() ? ERR_PROTOCOL_UNKNOWN : ERR_SECURE_ACCEPT_FAIL;
- if (!conn->spliceOnError(err))
- conn->clientConnection->close();
+ if (unsupportedProtocol) {
+ if (!spliceOnError(ERR_PROTOCOL_UNKNOWN))
+ clientConnection->close();
return;
}
- if (!bio->rBufData().isEmpty() > 0)
- conn->receivedFirstByte();
-
- if (bio->gotHello()) {
- if (conn->serverBump()) {
- Security::TlsDetails::Pointer const &details = bio->receivedHelloDetails();
- if (!details->serverName.isEmpty()) {
- conn->serverBump()->clientSni = details->serverName;
- conn->resetSslCommonName(details->serverName.c_str());
- }
+ if (serverBump()) {
+ Security::TlsDetails::Pointer const &details = tlsParser.details;
+ if (details && !details->serverName.isEmpty()) {
+ serverBump()->clientSni = details->serverName;
+ resetSslCommonName(details->serverName.c_str());
}
-
- debugs(83, 5, "I got hello. Start forwarding the request!!! ");
- Comm::SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
- Comm::SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
- conn->startPeekAndSpliceDone();
- return;
}
-}
-
-void ConnStateData::startPeekAndSplice()
-{
- // will call httpsPeeked() with certificate and connection, eventually
- auto unConfiguredCTX = Ssl::createSSLContext(port->signingCert, port->signPkey, *port);
- fd_table[clientConnection->fd].dynamicSslContext = unConfiguredCTX;
- if (!httpsCreate(clientConnection, unConfiguredCTX))
- return;
-
- // commSetConnTimeout() was called for this request before we switched.
- // Fix timeout to request_start_timeout
- typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
- AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
- TimeoutDialer, this, ConnStateData::requestTimeout);
- commSetConnTimeout(clientConnection, Config.Timeout.request_start_timeout, timeoutCall);
- // Also reset receivedFirstByte_ flag to allow this timeout work in the case we have
- // a bumbed "connect" request on non transparent port.
- receivedFirstByte_ = false;
-
- // Disable the client read handler until CachePeer selection is complete
- Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
- Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, clientPeekAndSpliceSSL, this, 0);
- switchedToHttps_ = true;
-
- auto ssl = fd_table[clientConnection->fd].ssl.get();
- BIO *b = SSL_get_rbio(ssl);
- Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
- bio->hold(true);
+ startPeekAndSpliceDone();
}
void httpsSslBumpStep2AccessCheckDone(allow_t answer, void *data)
connState->clientConnection->close();
} else if (bumpAction != Ssl::bumpSplice) {
connState->startPeekAndSpliceDone();
- } else
- connState->splice();
+ } else if (!connState->splice())
+ connState->clientConnection->close();
}
-void
+bool
ConnStateData::splice()
{
// normally we can splice here, because we just got client hello message
- auto ssl = fd_table[clientConnection->fd].ssl.get();
- //retrieve received TLS client information
- clientConnection->tlsNegotiations()->fillWith(ssl);
-
- BIO *b = SSL_get_rbio(ssl);
- Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
- SBuf const &rbuf = bio->rBufData();
- debugs(83,5, "Bio for " << clientConnection << " read " << rbuf.length() << " helo bytes");
- // Do splice:
- fd_table[clientConnection->fd].read_method = &default_read_method;
- fd_table[clientConnection->fd].write_method = &default_write_method;
+ if (fd_table[clientConnection->fd].ssl.get()) {
+ // Restore default read methods
+ fd_table[clientConnection->fd].read_method = &default_read_method;
+ fd_table[clientConnection->fd].write_method = &default_write_method;
+ }
if (transparent()) {
// set the current protocol to something sensible (was "HTTPS" for the bumping process)
// we are sending a faked-up HTTP/1.1 message wrapper, so go with that.
transferProtocol = Http::ProtocolVersion();
- // XXX: copy from MemBuf reallocates, not a regression since old code did too
- fakeAConnectRequest("intercepted TLS spliced", rbuf);
+ return fakeAConnectRequest("intercepted TLS spliced", inBuf);
} else {
// XXX: assuming that there was an HTTP/1.1 CONNECT to begin with...
// reset the current protocol to HTTP/1.1 (was "HTTPS" for the bumping process)
transferProtocol = Http::ProtocolVersion();
- // inBuf still has the "CONNECT ..." request data, reset it to SSL hello message
- inBuf.append(rbuf);
Http::StreamPointer context = pipeline.front();
ClientHttpRequest *http = context->http;
tunnelStart(http);
+ return true;
}
}
return;
}
+ // will call httpsPeeked() with certificate and connection, eventually
+ auto unConfiguredCTX = Ssl::createSSLContext(port->signingCert, port->signPkey, *port);
+ fd_table[clientConnection->fd].dynamicSslContext = unConfiguredCTX;
+
+ if (!httpsCreate(clientConnection, unConfiguredCTX))
+ return;
+
+ switchedToHttps_ = true;
+
+ auto ssl = fd_table[clientConnection->fd].ssl.get();
+ BIO *b = SSL_get_rbio(ssl);
+ Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
+ bio->setReadBufData(inBuf);
+ bio->hold(true);
+
+ // Here squid should have all of the client hello message so the
+ // Squid_SSL_accept should return 0;
+ // This block exist only to force openSSL parse client hello and detect
+ // ERR_SECURE_ACCEPT_FAIL error, which should be checked and splice if required.
+ int ret = 0;
+ if ((ret = Squid_SSL_accept(this, NULL)) < 0) {
+ debugs(83, 2, "SSL_accept failed.");
+ const err_type err = ERR_SECURE_ACCEPT_FAIL;
+ if (!spliceOnError(err))
+ clientConnection->close();
+ return;
+ }
+
+ // We need to reset inBuf here, to be used by incoming requests in the case
+ // of SSL bump
+ inBuf.clear();
+
+ debugs(83, 5, "Peek and splice at step2 done. Start forwarding the request!!! ");
FwdState::Start(clientConnection, sslServerBump->entry, sslServerBump->request.getRaw(), http ? http->al : NULL);
}
#endif /* USE_OPENSSL */
-void
+bool
ConnStateData::fakeAConnectRequest(const char *reason, const SBuf &payload)
{
// fake a CONNECT request to force connState to tunnel
if (!ret) {
debugs(33, 2, "Failed to start fake CONNECT request for " << reason << " connection: " << clientConnection);
- clientConnection->close();
+ return false;
}
+ return true;
}
/// check FD after clientHttp[s]ConnectionOpened, adjust HttpSockets as needed
clientHttpConnectionsOpen(void)
{
for (AnyP::PortCfgPointer s = HttpPortList; s != NULL; s = s->next) {
- const char *scheme = AnyP::UriScheme(s->transport.protocol).c_str();
+ const SBuf &scheme = AnyP::UriScheme(s->transport.protocol).image();
if (MAXTCPLISTENPORTS == NHttpSockets) {
debugs(1, DBG_IMPORTANT, "WARNING: You have too many '" << scheme << "_port' lines.");
int
varyEvaluateMatch(StoreEntry * entry, HttpRequest * request)
{
- const char *vary = request->vary_headers;
+ SBuf vary(request->vary_headers);
int has_vary = entry->getReply()->header.has(Http::HdrType::VARY);
#if X_ACCELERATOR_VARY
entry->getReply()->header.has(Http::HdrType::HDR_X_ACCELERATOR_VARY);
#endif
- if (!has_vary || !entry->mem_obj->vary_headers) {
- if (vary) {
+ if (!has_vary || entry->mem_obj->vary_headers.isEmpty()) {
+ if (!vary.isEmpty()) {
/* Oops... something odd is going on here.. */
debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary object on second attempt, '" <<
entry->mem_obj->urlXXX() << "' '" << vary << "'");
- safe_free(request->vary_headers);
+ request->vary_headers.clear();
return VARY_CANCEL;
}
*/
vary = httpMakeVaryMark(request, entry->getReply());
- if (vary) {
- request->vary_headers = xstrdup(vary);
+ if (!vary.isEmpty()) {
+ request->vary_headers = vary;
return VARY_OTHER;
} else {
/* Ouch.. we cannot handle this kind of variance */
return VARY_CANCEL;
}
} else {
- if (!vary) {
+ if (vary.isEmpty()) {
vary = httpMakeVaryMark(request, entry->getReply());
- if (vary)
- request->vary_headers = xstrdup(vary);
+ if (!vary.isEmpty())
+ request->vary_headers = vary;
}
- if (!vary) {
+ if (vary.isEmpty()) {
/* Ouch.. we cannot handle this kind of variance */
/* XXX This cannot really happen, but just to be complete */
return VARY_CANCEL;
- } else if (strcmp(vary, entry->mem_obj->vary_headers) == 0) {
+ } else if (vary.cmp(entry->mem_obj->vary_headers) == 0) {
return VARY_MATCH;
} else {
/* Oops.. we have already been here and still haven't
* connection has gone away */
}
+void
+ConnStateData::checkLogging()
+{
+ // if we are parsing request body, its request is responsible for logging
+ if (bodyPipe)
+ return;
+
+ // a request currently using this connection is responsible for logging
+ if (!pipeline.empty() && pipeline.back()->mayUseConnection())
+ return;
+
+ /* Either we are waiting for the very first transaction, or
+ * we are done with the Nth transaction and are waiting for N+1st.
+ * XXX: We assume that if anything was added to inBuf, then it could
+ * only be consumed by actions already covered by the above checks.
+ */
+
+ // do not log connections that closed after a transaction (it is normal)
+ // TODO: access_log needs ACLs to match received-no-bytes connections
+ // XXX: TLS may return here even though we got no transactions yet
+ // XXX: PROXY protocol may return here even though we got no
+ // transactions yet
+ if (receivedFirstByte_ && inBuf.isEmpty())
+ return;
+
+ /* Create a temporary ClientHttpRequest object. Its destructor will log. */
+ ClientHttpRequest http(this);
+ http.req_sz = inBuf.length();
+ char const *uri = "error:transaction-end-before-headers";
+ http.uri = xstrdup(uri);
+ setLogUri(&http, uri);
+}
+