client_side.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9/* DEBUG: section 33 Client-side Routines */
10
60#include "squid.h"
61#include "acl/FilledChecklist.h"
62#include "anyp/PortCfg.h"
63#include "base/AsyncCallbacks.h"
64#include "base/Subscription.h"
65#include "base/TextException.h"
66#include "CachePeer.h"
67#include "client_db.h"
68#include "client_side.h"
69#include "client_side_reply.h"
70#include "client_side_request.h"
72#include "clientStream.h"
73#include "comm.h"
74#include "comm/Connection.h"
75#include "comm/Loops.h"
76#include "comm/Read.h"
77#include "comm/TcpAcceptor.h"
78#include "comm/Write.h"
79#include "CommCalls.h"
80#include "debug/Messages.h"
82#include "errorpage.h"
83#include "fd.h"
84#include "fde.h"
85#include "fqdncache.h"
86#include "FwdState.h"
87#include "globals.h"
88#include "helper.h"
89#include "helper/Reply.h"
90#include "http.h"
93#include "http/Stream.h"
94#include "HttpHdrContRange.h"
95#include "HttpHeaderTools.h"
96#include "HttpReply.h"
97#include "HttpRequest.h"
98#include "ident/Config.h"
99#include "ident/Ident.h"
100#include "internal.h"
101#include "ipc/FdNotes.h"
102#include "ipc/StartListening.h"
103#include "log/access_log.h"
104#include "MemBuf.h"
105#include "MemObject.h"
106#include "mime_header.h"
107#include "parser/Tokenizer.h"
108#include "proxyp/Header.h"
109#include "proxyp/Parser.h"
110#include "sbuf/Stream.h"
111#include "security/Certificate.h"
113#include "security/Io.h"
114#include "security/KeyLog.h"
116#include "servers/forward.h"
117#include "SquidConfig.h"
118#include "StatCounters.h"
119#include "StatHist.h"
120#include "Store.h"
121#include "TimeOrTag.h"
122#include "tools.h"
123
124#if USE_AUTH
125#include "auth/UserRequest.h"
126#endif
127#if USE_DELAY_POOLS
128#include "ClientInfo.h"
129#include "MessageDelayPools.h"
130#endif
131#if USE_OPENSSL
132#include "ssl/bio.h"
133#include "ssl/context_storage.h"
134#include "ssl/gadgets.h"
135#include "ssl/helper.h"
136#include "ssl/ProxyCerts.h"
137#include "ssl/ServerBump.h"
138#include "ssl/support.h"
139#endif
140
141#include <climits>
142#include <cmath>
143#include <limits>
144
145#if HAVE_SYSTEMD_SD_DAEMON_H
146#include <systemd/sd-daemon.h>
147#endif
148
149// TODO: Remove this custom dialer and simplify by creating the TcpAcceptor
150// subscription later, inside clientListenerConnectionOpened() callback, just
151// like htcpOpenPorts(), icpOpenPorts(), and snmpPortOpened() do it.
154 public CallDialer,
155 public WithAnswer<Ipc::StartListeningAnswer>
156{
157public:
160 handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}
161
162 /* CallDialer API */
163 void print(std::ostream &os) const override {
164 os << '(' << answer_ << ", " << FdNote(portTypeNote) << " port=" << (void*)&portCfg << ')';
165 }
166
167 virtual bool canDial(AsyncCall &) const { return true; }
168 virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }
169
170 /* WithAnswer API */
172
173public:
175
176private:
177 // answer_.conn (set/updated by IPC code) is portCfg.listenConn (used by us)
182};
183
184static void clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub);
185
187#if USE_IDENT
189#endif
190static int clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength);
191
192static void clientUpdateStatHistCounters(const LogTags &logType, int svc_time);
193static void clientUpdateStatCounters(const LogTags &logType);
195static bool clientPingHasFinished(ping_data const *aPing);
198
199char *skipLeadingSpace(char *aString);
200
201#if USE_IDENT
202static void
203clientIdentDone(const char *ident, void *data)
204{
206 xstrncpy(conn->clientConnection->rfc931, ident ? ident : dash_str, USER_IDENT_SZ);
207}
208#endif
209
210void
212{
214
215 if (logType.isTcpHit())
217
218 if (logType.oldType == LOG_TCP_HIT)
220 else if (logType.oldType == LOG_TCP_MEM_HIT)
222}
223
224void
225clientUpdateStatHistCounters(const LogTags &logType, int svc_time)
226{
235 switch (logType.oldType) {
236
239 break;
240
241 case LOG_TCP_INM_HIT:
242 case LOG_TCP_IMS_HIT:
244 break;
245
246 case LOG_TCP_HIT:
247
248 case LOG_TCP_MEM_HIT:
249
252 break;
253
254 case LOG_TCP_MISS:
255
258 break;
259
260 default:
261 /* make compiler warnings go away */
262 break;
263 }
264}
265
266bool
268{
269 if (0 != aPing->stop.tv_sec && 0 != aPing->start.tv_sec)
270 return true;
271
272 return false;
273}
274
275void
277{
278 ping_data *i;
279
280 switch (someEntry->code) {
281#if USE_CACHE_DIGESTS
282
283 case CD_PARENT_HIT:
284
285 case CD_SIBLING_HIT:
287 break;
288#endif
289
290 case SIBLING_HIT:
291
292 case PARENT_HIT:
293
295
298 i = &someEntry->ping;
299
302
303 if (i->timeout)
305
306 break;
307
308 case CLOSEST_PARENT:
309
310 case CLOSEST_DIRECT:
312
313 break;
314
315 default:
316 break;
317 }
318}
319
320void
322{
324
325 if (request->error)
327
330
332}
333
334void
336{
337 assert(request);
338 assert(aLogEntry != nullptr);
339
341 MemBuf mb;
342 mb.init();
343 request->header.packInto(&mb);
344 //This is the request after adaptation or redirection
345 aLogEntry->headers.adapted_request = xstrdup(mb.buf);
346
347 // the virgin request is saved to aLogEntry->request
348 if (aLogEntry->request) {
349 mb.reset();
350 aLogEntry->request->header.packInto(&mb);
351 aLogEntry->headers.request = xstrdup(mb.buf);
352 }
353
354#if USE_ADAPTATION
355 const Adaptation::History::Pointer ah = request->adaptLogHistory();
356 if (ah != nullptr) {
357 mb.reset();
358 ah->lastMeta.packInto(&mb);
359 aLogEntry->adapt.last_meta = xstrdup(mb.buf);
360 }
361#endif
362
363 mb.clean();
364 }
365
366#if ICAP_CLIENT
368 if (ih != nullptr)
369 ih->processingTime(aLogEntry->icap.processingTime);
370#endif
371
372 aLogEntry->http.method = request->method;
373 aLogEntry->http.version = request->http_ver;
374 aLogEntry->hier = request->hier;
375 aLogEntry->cache.extuser = request->extacl_user.termedBuf();
376
377 // Adapted request, if any, inherits and then collects all the stats, but
378 // the virgin request gets logged instead; copy the stats to log them.
379 // TODO: avoid losses by keeping these stats in a shared history object?
380 if (aLogEntry->request) {
381 aLogEntry->request->dnsWait = request->dnsWait;
382 aLogEntry->request->error = request->error;
383 }
384}
385
386void
388{
389 if (!out.size && loggingTags().oldType == LOG_TAG_NONE)
390 debugs(33, 5, "logging half-baked transaction: " << log_uri);
391
393 al->url = log_uri;
394 debugs(33, 9, "clientLogRequest: al.url='" << al->url << "'");
395
396 const auto findReply = [this]() -> const HttpReply * {
397 if (al->reply)
398 return al->reply.getRaw();
399 if (const auto le = loggingEntry())
400 return le->hasFreshestReply();
401 return nullptr;
402 };
403 if (const auto reply = findReply()) {
404 al->http.code = reply->sline.status();
405 al->http.content_type = reply->content_type.termedBuf();
406 }
407
408 debugs(33, 9, "clientLogRequest: http.code='" << al->http.code << "'");
409
410 if (loggingEntry() && loggingEntry()->mem_obj && loggingEntry()->objectLen() >= 0)
411 al->cache.objectSize = loggingEntry()->contentLen(); // payload duplicate ?? with or without TE ?
412
414 // the virgin request is saved to al->request
415 if (al->request && al->request->body_pipe)
418 // XXX: calculate without payload encoding or headers !!
419 al->http.clientReplySz.payloadData = out.size - out.headers_sz; // pretend its all un-encoded data for now.
420
422
424
425 if (request)
427
428#if USE_OPENSSL && 0
429
430 /* This is broken. Fails if the connection has been closed. Needs
431 * to snarf the ssl details some place earlier..
432 */
433 if (getConn() != NULL)
435
436#endif
437
438 /* Add notes (if we have a request to annotate) */
439 if (request) {
440 SBuf matched;
441 for (auto h: Config.notes) {
442 if (h->match(request, al->reply.getRaw(), al, matched)) {
443 request->notes()->add(h->key(), matched);
444 debugs(33, 3, h->key() << " " << matched);
445 }
446 }
447 // The al->notes and request->notes must point to the same object.
449 }
450
451 ACLFilledChecklist checklist(nullptr, request, nullptr);
452 if (al->reply) {
453 checklist.reply = al->reply.getRaw();
454 HTTPMSGLOCK(checklist.reply);
455 }
456
457 if (request) {
461 }
462 // no need checklist.syncAle(): already synced
463 checklist.al = al;
464 accessLogLog(al, &checklist);
465
466 bool updatePerformanceCounters = true;
469 statsCheck.al = al;
470 if (al->reply) {
471 statsCheck.reply = al->reply.getRaw();
472 HTTPMSGLOCK(statsCheck.reply);
473 }
474 updatePerformanceCounters = statsCheck.fastCheck().allowed();
475 }
476
477 if (updatePerformanceCounters) {
478 if (request)
480
481 if (getConn() != nullptr && getConn()->clientConnection != nullptr)
482 clientdbUpdate(getConn()->clientConnection->remote, loggingTags(), AnyP::PROTO_HTTP, out.size);
483 }
484}
485
486void
488{
489 safe_free(uri);
492 clearRequest();
493
496}
497
498void
500{
501 ClientHttpRequest *http = (ClientHttpRequest *)data;
502 assert(http != nullptr);
503 delete http;
504}
505
506/* This is a handler normally called by comm_close() */
508{
509 if (clientConnection) {
511 // keep closed clientConnection for logging, clientdb cleanup, etc.
512 }
513 deleteThis("ConnStateData::connStateClosed");
514}
515
516#if USE_AUTH
517void
519{
520 if (auth_ == nullptr) {
521 if (aur != nullptr) {
522 debugs(33, 2, "Adding connection-auth to " << clientConnection << " from " << by);
523 auth_ = aur;
524 }
525 return;
526 }
527
528 // clobered with self-pointer
529 // NP: something nasty is going on in Squid, but harmless.
530 if (aur == auth_) {
531 debugs(33, 2, "WARNING: Ignoring duplicate connection-auth for " << clientConnection << " from " << by);
532 return;
533 }
534
535 /*
536 * Connection-auth relies on a single set of credentials being preserved
537 * for all requests on a connection once they have been setup.
538 * There are several things which need to happen to preserve security
539 * when connection-auth credentials change unexpectedly or are unset.
540 *
541 * 1) auth helper released from any active state
542 *
543 * They can only be reserved by a handshake process which this
544 * connection can now never complete.
545 * This prevents helpers hanging when their connections close.
546 *
547 * 2) pinning is expected to be removed and server conn closed
548 *
549 * The upstream link is authenticated with the same credentials.
550 * Expecting the same level of consistency we should have received.
551 * This prevents upstream being faced with multiple or missing
552 * credentials after authentication.
553 * NP: un-pin is left to the cleanup in ConnStateData::swanSong()
554 * we just trigger that cleanup here via comm_reset_close() or
555 * ConnStateData::stopReceiving()
556 *
557 * 3) the connection needs to close.
558 *
559 * This prevents attackers injecting requests into a connection,
560 * or gateways wrongly multiplexing users into a single connection.
561 *
562 * When credentials are missing closure needs to follow an auth
563 * challenge for best recovery by the client.
564 *
565 * When credentials change there is nothing we can do but abort as
566 * fast as possible. Sending TCP RST instead of an HTTP response
567 * is the best-case action.
568 */
569
570 // clobbered with nul-pointer
571 if (aur == nullptr) {
572 debugs(33, 2, "WARNING: Graceful closure on " << clientConnection << " due to connection-auth erase from " << by);
574 auth_ = nullptr;
575 // XXX: need to test whether the connection re-auth challenge is sent. If not, how to trigger it from here.
576 // NP: the current situation seems to fix challenge loops in Safari without visible issues in others.
577 // we stop receiving more traffic but can leave the Job running to terminate after the error or challenge is delivered.
578 stopReceiving("connection-auth removed");
579 return;
580 }
581
582 // clobbered with alternative credentials
583 if (aur != auth_) {
584 debugs(33, 2, "ERROR: Closing " << clientConnection << " due to change of connection-auth from " << by);
586 auth_ = nullptr;
587 // this is a fatal type of problem.
588 // Close the connection immediately with TCP RST to abort all traffic flow
590 return;
591 }
592
593 /* NOT REACHABLE */
594}
595#endif
596
597void
599{
601 AsyncCall::Pointer callback = JobCallback(33, 5, TimeoutDialer, this, ConnStateData::requestTimeout);
602 commSetConnTimeout(clientConnection, timeout, callback);
603}
604
605void
607{
609 AsyncCall::Pointer callback = JobCallback(5, 4, TimeoutDialer, this, ConnStateData::lifetimeTimeout);
611}
612
613// cleans up before destructor is called
614void
616{
617 debugs(33, 2, clientConnection);
618
619 flags.readMore = false;
620 clientdbEstablished(clientConnection->remote, -1); /* decrement */
621
623 checkLogging();
624
625 // XXX: Closing pinned conn is too harsh: The Client may want to continue!
626 unpinConnection(true);
627
629
630#if USE_AUTH
631 // NP: do this bit after closing the connections to avoid side effects from unwanted TCP RST
632 setAuth(nullptr, "ConnStateData::SwanSong cleanup");
633#endif
634
635 flags.swanSang = true;
636}
637
638void
639ConnStateData::callException(const std::exception &ex)
640{
641 Server::callException(ex); // logs ex and stops the job
642
643 ErrorDetail::Pointer errorDetail;
644 if (const auto tex = dynamic_cast<const TextException*>(&ex))
645 errorDetail = new ExceptionErrorDetail(tex->id());
646 else
647 errorDetail = new ExceptionErrorDetail(Here().id());
648 updateError(ERR_GATEWAY_FAILURE, errorDetail);
649}
650
651void
653{
654 if (const auto context = pipeline.front()) {
655 const auto http = context->http;
656 assert(http);
657 http->updateError(error);
658 } else {
660 }
661}
662
663bool
665{
666 return cbdataReferenceValid(this) && // XXX: checking "this" in a method
668 !fd_table[clientConnection->fd].closing();
669}
670
672{
673 debugs(33, 3, clientConnection);
674
675 if (isOpen())
676 debugs(33, DBG_IMPORTANT, "ERROR: Squid BUG: ConnStateData did not close " << clientConnection);
677
678 if (!flags.swanSang)
679 debugs(33, DBG_IMPORTANT, "ERROR: Squid BUG: ConnStateData was not destroyed properly; " << clientConnection);
680
681 if (bodyPipe != nullptr)
683
684 delete bodyParser; // TODO: pool
685
686#if USE_OPENSSL
687 delete sslServerBump;
688#endif
689}
690
697void
699{
700 HttpRequest *request = http->request;
701
702 debugs(33, 3, "http_ver = " << request->http_ver);
703 debugs(33, 3, "method = " << request->method);
704
705 // TODO: move to HttpRequest::hdrCacheInit, just like HttpReply.
706 request->flags.proxyKeepalive = request->persistent();
707}
708
709int
711{
713 bodyLength > Config.maxRequestBodySize)
714 return 1; /* too large */
715
716 return 0;
717}
718
719bool
721{
723}
724
725void
727{
728 mb->appendf("\r\n--" SQUIDSTRINGPH "--\r\n", SQUIDSTRINGPRINT(boundary));
729 debugs(33, 6, "buf offset: " << mb->size);
730}
731
732void
733clientPackRangeHdr(const HttpReplyPointer &rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb)
734{
735 HttpHeader hdr(hoReply);
736 assert(rep);
737 assert(spec);
738
739 /* put boundary */
740 debugs(33, 5, "appending boundary: " << boundary);
741 /* rfc2046 requires to _prepend_ boundary with <crlf>! */
742 mb->appendf("\r\n--" SQUIDSTRINGPH "\r\n", SQUIDSTRINGPRINT(boundary));
743
744 /* stuff the header with required entries and pack it */
745
748
749 httpHeaderAddContRange(&hdr, *spec, rep->content_length);
750
751 hdr.packInto(mb);
752 hdr.clean();
753
754 /* append <crlf> (we packed a header, not a reply) */
755 mb->append("\r\n", 2);
756}
757
763int64_t
765{
766 int64_t clen = 0;
767 MemBuf mb;
768
769 assert(memObject());
770
771 mb.init();
773
774 while (pos != request->range->end()) {
775 /* account for headers for this range */
776 mb.reset();
777 clientPackRangeHdr(&storeEntry()->mem().freshestReply(),
778 *pos, range_iter.boundary, &mb);
779 clen += mb.size;
780
781 /* account for range content */
782 clen += (*pos)->length;
783
784 debugs(33, 6, "clientMRangeCLen: (clen += " << mb.size << " + " << (*pos)->length << ") == " << clen);
785 ++pos;
786 }
787
788 /* account for the terminating boundary */
789 mb.reset();
790
792
793 clen += mb.size;
794
795 mb.clean();
796
797 return clen;
798}
799
803String
805{
806 const char *key;
808 b.append(":",1);
809 key = storeEntry()->getMD5Text();
810 b.append(key, strlen(key));
811 return b;
812}
813
823void
825 HttpReply * rep, StoreIOBuffer receivedData)
826{
827 // do not try to deliver if client already ABORTED
828 if (!http->getConn() || !cbdataReferenceValid(http->getConn()) || !Comm::IsConnOpen(http->getConn()->clientConnection))
829 return;
830
831 /* Test preconditions */
832 assert(node != nullptr);
833 /* TODO: handle this rather than asserting
834 * - it should only ever happen if we cause an abort and
835 * the callback chain loops back to here, so we can simply return.
836 * However, that itself shouldn't happen, so it stays as an assert for now.
837 */
839 assert(node->node.next == nullptr);
840 Http::StreamPointer context = dynamic_cast<Http::Stream *>(node->data.getRaw());
841 assert(context != nullptr);
842
843 /* TODO: check offset is what we asked for */
844
845 // TODO: enforces HTTP/1 MUST on pipeline order, but is irrelevant to HTTP/2
846 if (context != http->getConn()->pipeline.front())
847 context->deferRecipientForLater(node, rep, receivedData);
848 else if (http->getConn()->cbControlMsgSent) // 1xx to the user is pending
849 context->deferRecipientForLater(node, rep, receivedData);
850 else
851 http->getConn()->handleReply(rep, receivedData);
852}
853
859void
861{
862 /* Test preconditions */
863 assert(node != nullptr);
864 /* TODO: handle this rather than asserting
865 * - it should only ever happen if we cause an abort and
866 * the callback chain loops back to here, so we can simply return.
867 * However, that itself shouldn't happen, so it stays as an assert for now.
868 */
870 /* Set null by ContextFree */
871 assert(node->node.next == nullptr);
872 /* this is the assert discussed above */
873 assert(nullptr == dynamic_cast<Http::Stream *>(node->data.getRaw()));
874 /* We are only called when the client socket shutsdown.
875 * Tell the prev pipeline member we're finished
876 */
878}
879
880void
882{
883 debugs(33, 5, clientConnection << " reading next req");
884
885 fd_note(clientConnection->fd, "Idle client: Waiting for next request");
890
891 readSomeData();
893}
894
895static void
897{
898 debugs(33, 2, conn->clientConnection << " Sending next");
899
902 if (deferredRequest->flags.deferred) {
904 assert(deferredRequest->http->out.size == 0);
906 clientSocketRecipient(deferredRequest->deferredparams.node,
907 deferredRequest->http,
908 deferredRequest->deferredparams.rep,
909 deferredRequest->deferredparams.queuedBuffer);
910 }
911
915}
916
917void
919{
921 debugs(33, 2, clientConnection << " Connection was closed");
922 return;
923 }
924
925 if (pinning.pinned && !Comm::IsConnOpen(pinning.serverConnection)) {
926 debugs(33, 2, clientConnection << " Connection was pinned but server side gone. Terminating client connection");
928 return;
929 }
930
945 if (const char *reason = stoppedReceiving()) {
946 debugs(33, 3, "closing for earlier request error: " << reason);
948 return;
949 }
950
961 if (clientParseRequests()) {
962 debugs(33, 3, clientConnection << ": parsed next request from buffer");
963 }
964
973 debugs(33, 3, "half-closed client with no pending requests, closing");
975 return;
976 }
977
985 Http::StreamPointer deferredRequest = pipeline.front();
986 if (deferredRequest != nullptr) {
987 debugs(33, 3, clientConnection << ": calling PushDeferredIfNeeded");
988 ClientSocketContextPushDeferredIfNeeded(deferredRequest, this);
989 } else if (flags.readMore) {
990 debugs(33, 3, clientConnection << ": calling readNextRequest()");
992 } else {
993 // XXX: Can this happen? CONNECT tunnels have deferredRequest set.
994 debugs(33, DBG_IMPORTANT, MYNAME << "abandoning " << clientConnection);
995 }
996}
997
998void
1000{
1001 debugs(33, 4, "sending error (" << clientConnection << "): " << error <<
1002 "; old receiving error: " <<
1003 (stoppedReceiving() ? stoppedReceiving_ : "none"));
1004
1005 if (const char *oldError = stoppedSending()) {
1006 debugs(33, 3, "already stopped sending: " << oldError);
1007 return; // nothing has changed as far as this connection is concerned
1008 }
1010
1011 if (!stoppedReceiving()) {
1012 if (const int64_t expecting = mayNeedToReadMoreBody()) {
1013 debugs(33, 5, "must still read " << expecting <<
1014 " request body bytes with " << inBuf.length() << " unused");
1015 return; // wait for the request receiver to finish reading
1016 }
1017 }
1018
1020}
1021
1022void
1024{
1025 if (pipeline.empty())
1026 return;
1027
1028 auto ctx = pipeline.front();
1029 if (size) {
1031 if (ctx->http->loggingTags().isTcpHit())
1033 }
1034 ctx->writeComplete(size);
1035}
1036
1039{
1040 ClientHttpRequest *http = new ClientHttpRequest(this);
1041 http->req_sz = inBuf.length();
1042 http->setErrorUri(uri);
1043 auto *context = new Http::Stream(clientConnection, http);
1044 StoreIOBuffer tempBuffer;
1045 tempBuffer.data = context->reqbuf;
1046 tempBuffer.length = HTTP_REQBUF_SZ;
1049 clientSocketDetach, context, tempBuffer);
1050 return context;
1051}
1052
1053void
1055{
1056 // RegisteredRunner API callback - Squid has been shut down
1057
1058 // if connection is idle terminate it now,
1059 // otherwise wait for grace period to end
1060 if (pipeline.empty())
1062}
1063
1064void
1066{
1067 // RegisteredRunner API callback - Squid shutdown grace period is over
1068
1069 // force the client connection to close immediately
1070 // swanSong() in the close handler will cleanup.
1073}
1074
1075char *
1076skipLeadingSpace(char *aString)
1077{
1078 char *result = aString;
1079
1080 while (xisspace(*aString))
1081 ++aString;
1082
1083 return result;
1084}
1085
1091const char *
1092findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end)
1093{
1094 if (nullptr == end) {
1095 end = uriAndHTTPVersion + strcspn(uriAndHTTPVersion, "\r\n");
1096 assert(end);
1097 }
1098
1099 for (; end > uriAndHTTPVersion; --end) {
1100 if (*end == '\n' || *end == '\r')
1101 continue;
1102
1103 if (xisspace(*end)) {
1104 if (strncasecmp(end + 1, "HTTP/", 5) == 0)
1105 return end + 1;
1106 else
1107 break;
1108 }
1109 }
1110
1111 return nullptr;
1112}
1113
1114static char *
1116{
1117 int vhost = conn->port->vhost;
1118 int vport = conn->port->vport;
1119 static char ipbuf[MAX_IPSTRLEN];
1120
1121 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
1122
1123 // XXX: re-use proper URL parser for this
1124 SBuf url = hp->requestUri(); // use full provided URI if we abort
1125 do { // use a loop so we can break out of it
1127 if (tok.skip('/')) // origin-form URL already.
1128 break;
1129
1130 if (conn->port->vhost)
1131 return nullptr; /* already in good shape */
1132
1133 // skip the URI scheme
1134 static const CharacterSet uriScheme = CharacterSet("URI-scheme","+-.") + CharacterSet::ALPHA + CharacterSet::DIGIT;
1135 static const SBuf uriSchemeEnd("://");
1136 if (!tok.skipAll(uriScheme) || !tok.skip(uriSchemeEnd))
1137 break;
1138
1139 // skip the authority segment
1140 // RFC 3986 complex nested ABNF for "authority" boils down to this:
1141 static const CharacterSet authority = CharacterSet("authority","-._~%:@[]!$&'()*+,;=") +
1143 if (!tok.skipAll(authority))
1144 break;
1145
1146 static const SBuf slashUri("/");
1147 const SBuf t = tok.remaining();
1148 if (t.isEmpty())
1149 url = slashUri;
1150 else if (t[0]=='/') // looks like path
1151 url = t;
1152 else if (t[0]=='?' || t[0]=='#') { // looks like query or fragment. fix '/'
1153 url = slashUri;
1154 url.append(t);
1155 } // else do nothing. invalid path
1156
1157 } while(false);
1158
1159#if SHOULD_REJECT_UNKNOWN_URLS
1160 // reject URI which are not well-formed even after the processing above
1161 if (url.isEmpty() || url[0] != '/') {
1162 hp->parseStatusCode = Http::scBadRequest;
1163 return conn->abortRequestParsing("error:invalid-request");
1164 }
1165#endif
1166
1167 if (vport < 0)
1168 vport = conn->clientConnection->local.port();
1169
1170 char *receivedHost = nullptr;
1171 if (vhost && (receivedHost = hp->getHostHeaderField())) {
1172 SBuf host(receivedHost);
1173 debugs(33, 5, "ACCEL VHOST REWRITE: vhost=" << host << " + vport=" << vport);
1174 if (vport > 0) {
1175 // remove existing :port (if any), cope with IPv6+ without port
1176 const auto lastColonPos = host.rfind(':');
1177 if (lastColonPos != SBuf::npos && *host.rbegin() != ']') {
1178 host.chop(0, lastColonPos); // truncate until the last colon
1179 }
1180 host.appendf(":%d", vport);
1181 } // else nothing to alter port-wise.
1182 const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
1183 const auto url_sz = scheme.length() + host.length() + url.length() + 32;
1184 char *uri = static_cast<char *>(xcalloc(url_sz, 1));
1185 snprintf(uri, url_sz, SQUIDSBUFPH "://" SQUIDSBUFPH SQUIDSBUFPH, SQUIDSBUFPRINT(scheme), SQUIDSBUFPRINT(host), SQUIDSBUFPRINT(url));
1186 debugs(33, 5, "ACCEL VHOST REWRITE: " << uri);
1187 return uri;
1188 } else if (conn->port->defaultsite /* && !vhost */) {
1189 debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: defaultsite=" << conn->port->defaultsite << " + vport=" << vport);
1190 char vportStr[32];
1191 vportStr[0] = '\0';
1192 if (vport > 0) {
1193 snprintf(vportStr, sizeof(vportStr),":%d",vport);
1194 }
1195 const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
1196 const int url_sz = scheme.length() + strlen(conn->port->defaultsite) + sizeof(vportStr) + url.length() + 32;
1197 char *uri = static_cast<char *>(xcalloc(url_sz, 1));
1198 snprintf(uri, url_sz, SQUIDSBUFPH "://%s%s" SQUIDSBUFPH,
1199 SQUIDSBUFPRINT(scheme), conn->port->defaultsite, vportStr, SQUIDSBUFPRINT(url));
1200 debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: " << uri);
1201 return uri;
1202 } else if (vport > 0 /* && (!vhost || no Host:) */) {
1203 debugs(33, 5, "ACCEL VPORT REWRITE: *_port IP + vport=" << vport);
1204 /* Put the local socket IP address as the hostname, with whatever vport we found */
1205 conn->clientConnection->local.toHostStr(ipbuf,MAX_IPSTRLEN);
1206 const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
1207 const int url_sz = scheme.length() + sizeof(ipbuf) + url.length() + 32;
1208 char *uri = static_cast<char *>(xcalloc(url_sz, 1));
1209 snprintf(uri, url_sz, SQUIDSBUFPH "://%s:%d" SQUIDSBUFPH,
1210 SQUIDSBUFPRINT(scheme), ipbuf, vport, SQUIDSBUFPRINT(url));
1211 debugs(33, 5, "ACCEL VPORT REWRITE: " << uri);
1212 return uri;
1213 }
1214
1215 return nullptr;
1216}
1217
1218static char *
1220{
1221 char *uri = nullptr;
1222 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
1223 if (const char *host = hp->getHostHeaderField()) {
1224 const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
1225 const int url_sz = scheme.length() + strlen(host) + hp->requestUri().length() + 32;
1226 uri = static_cast<char *>(xcalloc(url_sz, 1));
1227 snprintf(uri, url_sz, SQUIDSBUFPH "://%s" SQUIDSBUFPH,
1228 SQUIDSBUFPRINT(scheme),
1229 host,
1230 SQUIDSBUFPRINT(hp->requestUri()));
1231 }
1232 return uri;
1233}
1234
1235char *
1237{
1239
1240 if (!hp->requestUri().isEmpty() && hp->requestUri()[0] != '/')
1241 return nullptr; /* already in good shape */
1242
1243 char *uri = buildUrlFromHost(this, hp);
1244#if USE_OPENSSL
1245 if (!uri) {
1248 SBuf useHost;
1249 if (!tlsClientSni().isEmpty())
1250 useHost = tlsClientSni();
1251 else
1252 useHost = tlsConnectHostOrIp;
1253
1255 const int url_sz = scheme.length() + useHost.length() + hp->requestUri().length() + 32;
1256 uri = static_cast<char *>(xcalloc(url_sz, 1));
1257 snprintf(uri, url_sz, SQUIDSBUFPH "://" SQUIDSBUFPH ":%hu" SQUIDSBUFPH,
1258 SQUIDSBUFPRINT(scheme),
1259 SQUIDSBUFPRINT(useHost),
1261 SQUIDSBUFPRINT(hp->requestUri()));
1262 }
1263#endif
1264 if (uri)
1265 debugs(33, 5, "TLS switching host rewrite: " << uri);
1266 return uri;
1267}
1268
1269static char *
1271{
1272 // TODO Must() on URI !empty when the parser supports throw. For now avoid assert().
1273 if (!hp->requestUri().isEmpty() && hp->requestUri()[0] != '/')
1274 return nullptr; /* already in good shape */
1275
1276 char *uri = buildUrlFromHost(conn, hp);
1277 if (!uri) {
1278 /* Put the local socket IP address as the hostname. */
1279 static char ipbuf[MAX_IPSTRLEN];
1280 conn->clientConnection->local.toHostStr(ipbuf,MAX_IPSTRLEN);
1281 const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
1282 const int url_sz = sizeof(ipbuf) + hp->requestUri().length() + 32;
1283 uri = static_cast<char *>(xcalloc(url_sz, 1));
1284 snprintf(uri, url_sz, SQUIDSBUFPH "://%s:%d" SQUIDSBUFPH,
1285 SQUIDSBUFPRINT(scheme),
1286 ipbuf, conn->clientConnection->local.port(), SQUIDSBUFPRINT(hp->requestUri()));
1287 }
1288
1289 if (uri)
1290 debugs(33, 5, "TRANSPARENT REWRITE: " << uri);
1291 return uri;
1292}
1293
1296{
1297 /* Attempt to parse the first line; this will define where the method, url, version and header begin */
1298 {
1299 Must(hp);
1300
1303
1304 const bool parsedOk = hp->parse(inBuf);
1305
1306 // sync the buffers after parsing.
1307 inBuf = hp->remaining();
1308
1309 if (hp->needsMoreData()) {
1310 debugs(33, 5, "Incomplete request, waiting for end of request line");
1311 return nullptr;
1312 }
1313
1314 if (!parsedOk) {
1315 const bool tooBig =
1316 hp->parseStatusCode == Http::scRequestHeaderFieldsTooLarge ||
1317 hp->parseStatusCode == Http::scUriTooLong;
1318 auto result = abortRequestParsing(
1319 tooBig ? "error:request-too-large" : "error:invalid-request");
1320 // assume that remaining leftovers belong to this bad request
1321 if (!inBuf.isEmpty())
1323 return result;
1324 }
1325 }
1326
1327 /* We know the whole request is in parser now */
1328 debugs(11, 2, "HTTP Client " << clientConnection);
1329 debugs(11, 2, "HTTP Client REQUEST:\n---------\n" <<
1330 hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol() << "\n" <<
1331 hp->mimeHeader() <<
1332 "\n----------");
1333
1334 /* deny CONNECT via accelerated ports */
1335 if (hp->method() == Http::METHOD_CONNECT && port != nullptr && port->flags.accelSurrogate) {
1336 debugs(33, DBG_IMPORTANT, "WARNING: CONNECT method received on " << transferProtocol << " Accelerator port " << port->s.port());
1337 debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
1338 hp->parseStatusCode = Http::scMethodNotAllowed;
1339 return abortRequestParsing("error:method-not-allowed");
1340 }
1341
1342 /* HTTP/2 connection magic prefix starts with "PRI ".
1343 * Deny "PRI" method if used in HTTP/1.x or 0.9 versions.
1344 * If seen it signals a broken client or proxy has corrupted the traffic.
1345 */
1346 if (hp->method() == Http::METHOD_PRI && hp->messageProtocol() < Http::ProtocolVersion(2,0)) {
1347 debugs(33, DBG_IMPORTANT, "WARNING: PRI method received on " << transferProtocol << " port " << port->s.port());
1348 debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
1349 hp->parseStatusCode = Http::scMethodNotAllowed;
1350 return abortRequestParsing("error:method-not-allowed");
1351 }
1352
1353 if (hp->method() == Http::METHOD_NONE) {
1354 debugs(33, DBG_IMPORTANT, "WARNING: Unsupported method: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
1355 hp->parseStatusCode = Http::scMethodNotAllowed;
1356 return abortRequestParsing("error:unsupported-request-method");
1357 }
1358
1359 // Process headers after request line
1360 debugs(33, 3, "complete request received. " <<
1361 "prefix_sz = " << hp->messageHeaderSize() <<
1362 ", request-line-size=" << hp->firstLineSize() <<
1363 ", mime-header-size=" << hp->headerBlockSize() <<
1364 ", mime header block:\n" << hp->mimeHeader() << "\n----------");
1365
1366 /* Ok, all headers are received */
1367 ClientHttpRequest *http = new ClientHttpRequest(this);
1368
1369 http->req_sz = hp->messageHeaderSize();
1370 Http::Stream *result = new Http::Stream(clientConnection, http);
1371
1372 StoreIOBuffer tempBuffer;
1373 tempBuffer.data = result->reqbuf;
1374 tempBuffer.length = HTTP_REQBUF_SZ;
1375
1376 ClientStreamData newServer = new clientReplyContext(http);
1377 ClientStreamData newClient = result;
1380 clientSocketDetach, newClient, tempBuffer);
1381
1382 /* set url */
1383 debugs(33,5, "Prepare absolute URL from " <<
1384 (transparent()?"intercept":(port->flags.accelSurrogate ? "accel":"")));
1385 /* Rewrite the URL in transparent or accelerator mode */
1386 /* NP: there are several cases to traverse here:
1387 * - standard mode (forward proxy)
1388 * - transparent mode (TPROXY)
1389 * - transparent mode with failures
1390 * - intercept mode (NAT)
1391 * - intercept mode with failures
1392 * - accelerator mode (reverse proxy)
1393 * - internal relative-URL
1394 * - mixed combos of the above with internal URL
1395 * - remote interception with PROXY protocol
1396 * - remote reverse-proxy with PROXY protocol
1397 */
1398 if (switchedToHttps()) {
1399 http->uri = prepareTlsSwitchingURL(hp);
1400 } else if (transparent()) {
1401 /* intercept or transparent mode, properly working with no failures */
1402 http->uri = prepareTransparentURL(this, hp);
1403
1404 } else if (internalCheck(hp->requestUri())) { // NP: only matches relative-URI
1405 /* internal URL mode */
1406 // XXX: By prepending our name and port, we create an absolute URL
1407 // that may mismatch the (yet unparsed) Host header in the request.
1408 http->uri = xstrdup(internalLocalUri(nullptr, hp->requestUri()));
1409
1410 } else if (port->flags.accelSurrogate) {
1411 /* accelerator mode */
1412 http->uri = prepareAcceleratedURL(this, hp);
1413 http->flags.accel = true;
1414 }
1415
1416 if (!http->uri) {
1417 /* No special rewrites have been applied above, use the
1418 * requested url. may be rewritten later, so make extra room */
1419 int url_sz = hp->requestUri().length() + Config.appendDomainLen + 5;
1420 http->uri = (char *)xcalloc(url_sz, 1);
1421 SBufToCstring(http->uri, hp->requestUri());
1422 }
1423
1424 result->flags.parsed_ok = 1;
1425 return result;
1426}
1427
1428bool
1430{
1431 if (pipeline.empty() && inBuf.isEmpty()) {
1432 debugs(33, 4, "yes, without active requests and unparsed input");
1433 return true;
1434 }
1435
1437 debugs(33, 3, "yes, without half_closed_clients");
1438 return true;
1439 }
1440
1441 // Squid currently tries to parse (possibly again) a partially received
1442 // request after an EOF with half_closed_clients. To give that last parse in
1443 // afterClientRead() a chance, we ignore partially parsed requests here.
1444 debugs(33, 3, "no, honoring half_closed_clients");
1445 return false;
1446}
1447
1448void
1449ConnStateData::consumeInput(const size_t byteCount)
1450{
1451 assert(byteCount > 0 && byteCount <= inBuf.length());
1452 inBuf.consume(byteCount);
1453 debugs(33, 5, "inBuf has " << inBuf.length() << " unused bytes");
1454}
1455
1456void
1458{
1459 // Were we expecting to read more request body from half-closed connection?
1461 debugs(33, 3, "truncated body: closing half-closed " << clientConnection);
1463 return;
1464 }
1465
1466 if (flags.readMore)
1467 readSomeData();
1468}
1469
1470void
1472{
1473 // From HTTP p.o.v., we do not have to close after every error detected
1474 // at the client-side, but many such errors do require closure and the
1475 // client-side code is bad at handling errors so we play it safe.
1476 if (request)
1477 request->flags.proxyKeepalive = false;
1478 flags.readMore = false;
1479 debugs(33,4, "Will close after error: " << clientConnection);
1480}
1481
1482#if USE_OPENSSL
1484{
1485 ClientHttpRequest *http = context->http;
1486
1487 if (!sslServerBump)
1488 return false;
1489
1491 // Did we create an error entry while processing CONNECT?
1492 if (!sslServerBump->entry->isEmpty()) {
1493 quitAfterError(http->request);
1494
1495 // Get the saved error entry and send it to the client by replacing the
1496 // ClientHttpRequest store entry with it.
1498 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1499 assert(repContext);
1500 debugs(33, 5, "Responding with delated error for " << http->uri);
1501 repContext->setReplyToStoreEntry(sslServerBump->entry, "delayed SslBump error");
1502
1503 // Get error details from the fake certificate-peeking request.
1505 context->pullData();
1506 return true;
1507 }
1508
1509 // In bump-server-first mode, we have not necessarily seen the intended
1510 // server name at certificate-peeking time. Check for domain mismatch now,
1511 // when we can extract the intended name from the bumped HTTP request.
1512 if (const Security::CertPointer &srvCert = sslServerBump->serverCert) {
1513 HttpRequest *request = http->request;
1514 if (!Ssl::checkX509ServerValidity(srvCert.get(), request->url.host())) {
1515 debugs(33, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " <<
1516 "does not match domainname " << request->url.host());
1517
1518 bool allowDomainMismatch = false;
1521 const auto sslErrors = std::make_unique<Security::CertErrors>(Security::CertError(SQUID_X509_V_ERR_DOMAIN_MISMATCH, srvCert));
1522 check.sslErrors = sslErrors.get();
1523 clientAclChecklistFill(check, http);
1524 allowDomainMismatch = check.fastCheck().allowed();
1525 }
1526
1527 if (!allowDomainMismatch) {
1528 quitAfterError(request);
1529
1531 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1532 assert (repContext);
1533
1534 request->hier = sslServerBump->request->hier;
1535
1536 // Create an error object and fill it
1537 const auto err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request, http->al);
1538 err->src_addr = clientConnection->remote;
1541 srvCert, nullptr);
1543 repContext->setReplyToError(request->method, err);
1544 assert(context->http->out.offset == 0);
1545 context->pullData();
1546 return true;
1547 }
1548 }
1549 }
1550
1551 return false;
1552}
1553#endif // USE_OPENSSL
1554
1556bool
1558{
1560 debugs(33, 5, "disabled; send error: " << requestError);
1561 return false;
1562 }
1563
1564 if (!preservingClientData_) {
1565 debugs(33, 3, "may have forgotten client data; send error: " << requestError);
1566 return false;
1567 }
1568
1570 checklist.requestErrorType = requestError;
1571 fillChecklist(checklist);
1572 auto answer = checklist.fastCheck();
1573 if (answer.allowed() && answer.kind == 1) {
1574 debugs(33, 3, "Request will be tunneled to server");
1575 const auto context = pipeline.front();
1576 const auto http = context ? context->http : nullptr;
1577 const auto request = http ? http->request : nullptr;
1578 if (context)
1579 context->finished(); // Will remove from pipeline queue
1580 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, nullptr, nullptr, 0);
1581 return initiateTunneledRequest(request, "unknown-protocol", preservedClientData);
1582 }
1583 debugs(33, 3, "denied; send error: " << requestError);
1584 return false;
1585}
1586
1587void
1589{
1590 /*
1591 * DPW 2007-05-18
1592 * Moved the TCP_RESET feature from clientReplyContext::sendMoreData
1593 * to here because calling comm_reset_close() causes http to
1594 * be freed before accessing.
1595 */
1596 if (request != nullptr && request->flags.resetTcp && Comm::IsConnOpen(conn->clientConnection)) {
1597 debugs(33, 3, "Sending TCP RST on " << conn->clientConnection);
1598 conn->flags.readMore = false;
1599 comm_reset_close(conn->clientConnection);
1600 }
1601}
1602
1603void
1605{
1606 ClientHttpRequest *http = context->http;
1607 bool mustReplyToOptions = false;
1608 bool expectBody = false;
1609
1610 // We already have the request parsed and checked, so we
1611 // only need to go through the final body/conn setup to doCallouts().
1612 assert(http->request);
1613 HttpRequest::Pointer request = http->request;
1614
1615 // temporary hack to avoid splitting this huge function with sensitive code
1616 const bool isFtp = !hp;
1617
1618 // Some blobs below are still HTTP-specific, but we would have to rewrite
1619 // this entire function to remove them from the FTP code path. Connection
1620 // setup and body_pipe preparation blobs are needed for FTP.
1621
1622 request->manager(conn, http->al);
1623
1624 request->flags.accelerated = http->flags.accel;
1625 request->flags.sslBumped=conn->switchedToHttps();
1626 // TODO: decouple http->flags.accel from request->flags.sslBumped
1627 request->flags.noDirect = (request->flags.accelerated && !request->flags.sslBumped) ?
1628 !conn->port->allow_direct : 0;
1629 request->sources |= isFtp ? Http::Message::srcFtp :
1630 ((request->flags.sslBumped || conn->port->transport.protocol == AnyP::PROTO_HTTPS) ? Http::Message::srcHttps : Http::Message::srcHttp);
1631#if USE_AUTH
1632 if (request->flags.sslBumped) {
1633 if (conn->getAuth() != nullptr)
1634 request->auth_user_request = conn->getAuth();
1635 }
1636#endif
1637
1638 if (internalCheck(request->url.path())) {
1639 if (internalHostnameIs(request->url.host()) && request->url.port() == getMyPort()) {
1640 debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true));
1641 request->flags.internal = true;
1642 } else if (Config.onoff.global_internal_static && internalStaticCheck(request->url.path())) {
1643 debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true) << " (global_internal_static on)");
1644 request->url.setScheme(AnyP::PROTO_HTTP, "http");
1645 request->url.host(internalHostname());
1646 request->url.port(getMyPort());
1647 request->flags.internal = true;
1648 http->setLogUriToRequestUri();
1649 } else
1650 debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true) << " (not this proxy)");
1651
1652 if (ForSomeCacheManager(request->url.path()))
1653 request->flags.disableCacheUse("cache manager URL");
1654 }
1655
1656 if (!isFtp) {
1657 // XXX: for non-HTTP messages instantiate a different Http::Message child type
1658 // for now Squid only supports HTTP requests
1659 const AnyP::ProtocolVersion &http_ver = hp->messageProtocol();
1660 assert(request->http_ver.protocol == http_ver.protocol);
1661 request->http_ver.major = http_ver.major;
1662 request->http_ver.minor = http_ver.minor;
1663 }
1664
1665 mustReplyToOptions = (request->method == Http::METHOD_OPTIONS) &&
1667 if (!urlCheckRequest(request.getRaw()) || mustReplyToOptions) {
1669 conn->quitAfterError(request.getRaw());
1670 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1671 assert (repContext);
1673 conn, request.getRaw(), nullptr, nullptr);
1674 assert(context->http->out.offset == 0);
1675 context->pullData();
1677 return;
1678 }
1679
1680 const auto frameStatus = request->checkEntityFraming();
1681 if (frameStatus != Http::scNone) {
1683 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1684 assert (repContext);
1685 conn->quitAfterError(request.getRaw());
1686 repContext->setReplyToError(ERR_INVALID_REQ, frameStatus, nullptr, conn, request.getRaw(), nullptr, nullptr);
1687 assert(context->http->out.offset == 0);
1688 context->pullData();
1690 return;
1691 }
1692
1694 // Let tunneling code be fully responsible for CONNECT requests
1695 if (http->request->method == Http::METHOD_CONNECT) {
1696 context->mayUseConnection(true);
1697 conn->flags.readMore = false;
1698 }
1699
1700#if USE_OPENSSL
1701 if (conn->switchedToHttps() && conn->serveDelayedError(context)) {
1703 return;
1704 }
1705#endif
1706
1707 /* Do we expect a request-body? */
1708 const auto chunked = request->header.chunked();
1709 expectBody = chunked || request->content_length > 0;
1710 if (!context->mayUseConnection() && expectBody) {
1711 request->body_pipe = conn->expectRequestBody(
1712 chunked ? -1 : request->content_length);
1713
1714 /* Is it too large? */
1715 if (!chunked && // if chunked, we will check as we accumulate
1718 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1719 assert (repContext);
1720 conn->quitAfterError(request.getRaw());
1721 repContext->setReplyToError(ERR_TOO_BIG,
1722 Http::scContentTooLarge, nullptr,
1723 conn, http->request, nullptr, nullptr);
1724 assert(context->http->out.offset == 0);
1725 context->pullData();
1727 return;
1728 }
1729
1730 if (!isFtp) {
1731 // We may stop producing, comm_close, and/or call setReplyToError()
1732 // below, so quit on errors to avoid http->doCallouts()
1733 if (!conn->handleRequestBodyData()) {
1735 return;
1736 }
1737
1738 if (!request->body_pipe->productionEnded()) {
1739 debugs(33, 5, "need more request body");
1740 context->mayUseConnection(true);
1741 assert(conn->flags.readMore);
1742 }
1743 }
1744 }
1745
1746 http->calloutContext = new ClientRequestContext(http);
1747
1748 http->doCallouts();
1749
1751}
1752
1753void
1755{
1756 debugs(33, 3, context << " to " << pipeline.count() << '/' << pipeline.nrequests);
1757 if (bareError) {
1758 debugs(33, 5, "assigning " << bareError);
1759 assert(context);
1760 assert(context->http);
1761 context->http->updateError(bareError);
1762 bareError.clear();
1763 }
1764 pipeline.add(context);
1765}
1766
1767int
1769{
1770 // TODO: Support pipelined requests through pinned connections.
1771 if (pinning.pinned)
1772 return 0;
1774}
1775
1781bool
1783{
1784 const int existingRequestCount = pipeline.count();
1785
1786 // default to the configured pipeline size.
1787 // add 1 because the head of pipeline is counted in concurrent requests and not prefetch queue
1788#if USE_OPENSSL
1789 const int internalRequest = (transparent() && sslBumpMode == Ssl::bumpSplice) ? 1 : 0;
1790#else
1791 const int internalRequest = 0;
1792#endif
1793 const int concurrentRequestLimit = pipelinePrefetchMax() + 1 + internalRequest;
1794
1795 // when queue filled already we can't add more.
1796 if (existingRequestCount >= concurrentRequestLimit) {
1797 debugs(33, 3, clientConnection << " max concurrent requests reached (" << concurrentRequestLimit << ")");
1798 debugs(33, 5, clientConnection << " deferring new request until one is done");
1799 return true;
1800 }
1801
1802 return false;
1803}
1804
1810bool
1812{
1814 return proxyProtocolError("PROXY client not permitted by default ACL");
1815
1817 fillChecklist(ch);
1818 if (!ch.fastCheck().allowed())
1819 return proxyProtocolError("PROXY client not permitted by ACLs");
1820
1821 return true;
1822}
1823
1829bool
1831{
1832 if (msg) {
1833 // This is important to know, but maybe not so much that flooding the log is okay.
1834#if QUIET_PROXY_PROTOCOL
1835 // display the first of every 32 occurrences at level 1, the others at level 2.
1836 static uint8_t hide = 0;
1837 debugs(33, (hide++ % 32 == 0 ? DBG_IMPORTANT : 2), msg << " from " << clientConnection);
1838#else
1839 debugs(33, DBG_IMPORTANT, msg << " from " << clientConnection);
1840#endif
1841 mustStop(msg);
1842 }
1843 return false;
1844}
1845
1850bool
1852{
1853 try {
1854 const auto parsed = ProxyProtocol::Parse(inBuf);
1855 proxyProtocolHeader_ = parsed.header;
1857 inBuf.consume(parsed.size);
1859 if (proxyProtocolHeader_->hasForwardedAddresses()) {
1860 clientConnection->local = proxyProtocolHeader_->destinationAddress;
1863 clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY spoofing of this new IP.
1864 debugs(33, 5, "PROXY/" << proxyProtocolHeader_->version() << " upgrade: " << clientConnection);
1865 }
1867 debugs(33, 3, "PROXY protocol: waiting for more than " << inBuf.length() << " bytes");
1868 return false;
1869 } catch (const std::exception &e) {
1870 return proxyProtocolError(e.what());
1871 }
1872 return true;
1873}
1874
1875void
1877{
1879 return;
1880
1881 receivedFirstByte_ = true;
1883}
1884
1890bool
1892{
1893 bool parsed_req = false;
1894
1895 debugs(33, 5, clientConnection << ": attempting to parse");
1896
1897 // Loop while we have read bytes that are not needed for producing the body
1898 // On errors, bodyPipe may become nil, but readMore will be cleared
1899 while (!inBuf.isEmpty() && !bodyPipe && flags.readMore) {
1900
1901 // Prohibit concurrent requests when using a pinned to-server connection
1902 // because our Client classes do not support request pipelining.
1903 if (pinning.pinned && !pinning.readHandler) {
1904 debugs(33, 3, clientConnection << " waits for busy " << pinning.serverConnection);
1905 break;
1906 }
1907
1908 /* Limit the number of concurrent requests */
1910 break;
1911
1912 // try to parse the PROXY protocol header magic bytes
1915 break;
1916
1917 // we have been waiting for PROXY to provide client-IP
1918 // for some lookups, ie rDNS and IDENT.
1920
1921 // Done with PROXY protocol which has cleared preservingClientData_.
1922 // If the next protocol supports on_unsupported_protocol, then its
1923 // parseOneRequest() must reset preservingClientData_.
1925 }
1926
1927 if (Http::StreamPointer context = parseOneRequest()) {
1928 debugs(33, 5, clientConnection << ": done parsing a request");
1930 context->registerWithConn();
1931
1932#if USE_OPENSSL
1933 if (switchedToHttps())
1935#endif
1936
1937 processParsedRequest(context);
1938
1939 parsed_req = true; // XXX: do we really need to parse everything right NOW ?
1940
1941 if (context->mayUseConnection()) {
1942 debugs(33, 3, "Not parsing new requests, as this request may need the connection");
1943 break;
1944 }
1945 } else {
1946 debugs(33, 5, clientConnection << ": not enough request data: " <<
1947 inBuf.length() << " < " << Config.maxRequestHeaderSize);
1949 break;
1950 }
1951 }
1952
1953 /* XXX where to 'finish' the parsing pass? */
1954 return parsed_req;
1955}
1956
1957void
1959{
1960#if USE_OPENSSL
1961 if (parsingTlsHandshake) {
1963 return;
1964 }
1965#endif
1966
1967 /* Process next request */
1968 if (pipeline.empty())
1969 fd_note(clientConnection->fd, "Reading next request");
1970
1971 if (!clientParseRequests()) {
1972 if (!isOpen())
1973 return;
1974 // We may get here if the client half-closed after sending a partial
1975 // request. See doClientRead() and shouldCloseOnEof().
1976 // XXX: This partially duplicates ConnStateData::kick().
1978 debugs(33, 5, clientConnection << ": half-closed connection, no completed request parsed, connection closing.");
1980 return;
1981 }
1982 }
1983
1984 if (!isOpen())
1985 return;
1986
1988}
1989
1996bool
1998{
1999 // if we are reading a body, stuff data into the body pipe
2000 if (bodyPipe != nullptr)
2001 return handleRequestBodyData();
2002 return true;
2003}
2004
2012bool
2014{
2015 assert(bodyPipe != nullptr);
2016
2017 if (bodyParser) { // chunked encoding
2018 if (const err_type error = handleChunkedRequestBody()) {
2020 return false;
2021 }
2022 } else { // identity encoding
2023 debugs(33,5, "handling plain request body for " << clientConnection);
2024 const size_t putSize = bodyPipe->putMoreData(inBuf.c_str(), inBuf.length());
2025 if (putSize > 0)
2026 consumeInput(putSize);
2027
2028 if (!bodyPipe->mayNeedMoreData()) {
2029 // BodyPipe will clear us automagically when we produced everything
2030 bodyPipe = nullptr;
2031 }
2032 }
2033
2034 if (!bodyPipe) {
2035 debugs(33,5, "produced entire request body for " << clientConnection);
2036
2037 if (const char *reason = stoppedSending()) {
2038 /* we've finished reading like good clients,
2039 * now do the close that initiateClose initiated.
2040 */
2041 debugs(33, 3, "closing for earlier sending error: " << reason);
2043 return false;
2044 }
2045 }
2046
2047 return true;
2048}
2049
2053{
2054 debugs(33, 7, "chunked from " << clientConnection << ": " << inBuf.length());
2055
2056 try { // the parser will throw on errors
2057
2058 if (inBuf.isEmpty()) // nothing to do
2059 return ERR_NONE;
2060
2063 const bool parsed = bodyParser->parse(inBuf);
2064 inBuf = bodyParser->remaining(); // sync buffers
2065 bpc.checkIn();
2066
2067 // dechunk then check: the size limit applies to _dechunked_ content
2069 return ERR_TOO_BIG;
2070
2071 if (parsed) {
2073 Must(!bodyPipe);
2074 return ERR_NONE; // nil bodyPipe implies body end for the caller
2075 }
2076
2077 // if chunk parser needs data, then the body pipe must need it too
2079
2080 // if parser needs more space and we can consume nothing, we will stall
2082 } catch (...) { // TODO: be more specific
2083 debugs(33, 3, "malformed chunks" << bodyPipe->status());
2084 return ERR_INVALID_REQ;
2085 }
2086
2087 debugs(33, 7, "need more chunked data" << *bodyPipe->status());
2088 return ERR_NONE;
2089}
2090
2092void
2094{
2096
2097 // XXX: The code below works if we fail during initial request parsing,
2098 // but if we fail when the server connection is used already, the server may send
2099 // us its response too, causing various assertions. How to prevent that?
2100#if WE_KNOW_HOW_TO_SEND_ERRORS
2102 if (context != NULL && !context->http->out.offset) { // output nothing yet
2103 clientStreamNode *node = context->getClientReplyContext();
2104 clientReplyContext *repContext = dynamic_cast<clientReplyContext*>(node->data.getRaw());
2105 assert(repContext);
2106 const Http::StatusCode scode = (error == ERR_TOO_BIG) ?
2107 Http::scContentTooLarge : HTTP_BAD_REQUEST;
2108 repContext->setReplyToError(error, scode,
2109 repContext->http->uri,
2110 CachePeer,
2111 repContext->http->request,
2112 inBuf, nullptr);
2113 context->pullData();
2114 } else {
2115 // close or otherwise we may get stuck as nobody will notice the error?
2117 }
2118#else
2119 debugs(33, 3, "aborting chunked request without error " << error);
2121#endif
2122 flags.readMore = false;
2123}
2124
2125void
2127{
2128 // request reader may get stuck waiting for space if nobody consumes body
2129 if (bodyPipe != nullptr)
2131
2132 // kids extend
2133}
2134
2136void
2138{
2139 if (!Comm::IsConnOpen(io.conn))
2140 return;
2141
2144 if (tunnelOnError(error))
2145 return;
2146
2147 /*
2148 * Just close the connection to not confuse browsers
2149 * using persistent connections. Some browsers open
2150 * a connection and then do not use it until much
2151 * later (presumeably because the request triggering
2152 * the open has already been completed on another
2153 * connection)
2154 */
2155 debugs(33, 3, "requestTimeout: FD " << io.fd << ": lifetime is expired.");
2156 io.conn->close();
2157}
2158
2159void
2161{
2162 debugs(33, DBG_IMPORTANT, "WARNING: Closing client connection due to lifetime timeout" <<
2163 Debug::Extra << "connection: " << io.conn);
2164
2165 LogTagsErrors lte;
2166 lte.timedout = true;
2168}
2169
2171 AsyncJob("ConnStateData"), // kids overwrite
2172 Server(xact)
2173#if USE_OPENSSL
2174 , tlsParser(Security::HandshakeParser::fromClient)
2175#endif
2176{
2177 // store the details required for creating more MasterXaction objects as new requests come in
2178 log_addr = xact->tcpClient->remote;
2180
2181 // register to receive notice of Squid signal events
2182 // which may affect long persisting client connections
2184}
2185
2186void
2188{
2191
2192 if (port->disable_pmtu_discovery != DISABLE_PMTU_OFF &&
2193 (transparent() || port->disable_pmtu_discovery == DISABLE_PMTU_ALWAYS)) {
2194#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
2195 int i = IP_PMTUDISC_DONT;
2196 if (setsockopt(clientConnection->fd, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0) {
2197 int xerrno = errno;
2198 debugs(33, 2, "WARNING: Path MTU discovery disabling failed on " << clientConnection << " : " << xstrerr(xerrno));
2199 }
2200#else
2201 static bool reported = false;
2202
2203 if (!reported) {
2204 debugs(33, DBG_IMPORTANT, "WARNING: Path MTU discovery disabling is not supported on your platform.");
2205 reported = true;
2206 }
2207#endif
2208 }
2209
2213
2214 needProxyProtocolHeader_ = port->flags.proxySurrogate;
2216 if (!proxyProtocolValidateClient()) // will close the connection on failure
2217 return;
2218 } else
2220
2221 // requires needProxyProtocolHeader_ which is initialized above
2223}
2224
2225void
2227{
2231
2232#if USE_IDENT
2233 if (Ident::TheConfig.identLookup) {
2234 ACLFilledChecklist identChecklist(Ident::TheConfig.identLookup, nullptr, nullptr);
2235 fillChecklist(identChecklist);
2236 if (identChecklist.fastCheck().allowed())
2238 }
2239#endif
2240
2242
2243#if USE_DELAY_POOLS
2244 fd_table[clientConnection->fd].clientInfo = nullptr;
2245
2246 if (!Config.onoff.client_db)
2247 return; // client delay pools require client_db
2248
2249 const auto &pools = ClientDelayPools::Instance()->pools;
2250 if (pools.size()) {
2251 ACLFilledChecklist ch(nullptr, nullptr, nullptr);
2252 fillChecklist(ch);
2253 // TODO: we check early to limit error response bandwidth but we
2254 // should recheck when we can honor delay_pool_uses_indirect
2255 for (unsigned int pool = 0; pool < pools.size(); ++pool) {
2256
2257 /* pools require explicit 'allow' to assign a client into them */
2258 if (pools[pool]->access) {
2259 ch.changeAcl(pools[pool]->access);
2260 auto answer = ch.fastCheck();
2261 if (answer.allowed()) {
2262
2263 /* request client information from db after we did all checks
2264 this will save hash lookup if client failed checks */
2266 assert(cli);
2267
2268 /* put client info in FDE */
2269 fd_table[clientConnection->fd].clientInfo = cli;
2270
2271 /* setup write limiter for this request */
2272 const double burst = floor(0.5 +
2273 (pools[pool]->highwatermark * Config.ClientDelay.initial)/100.0);
2274 cli->setWriteLimiter(pools[pool]->rate, burst, pools[pool]->highwatermark);
2275 break;
2276 } else {
2277 debugs(83, 4, "Delay pool " << pool << " skipped because ACL " << answer);
2278 }
2279 }
2280 }
2281 }
2282#endif
2283
2284 // kids must extend to actually start doing something (e.g., reading)
2285}
2286
2289{
2290 const auto handshakeResult = Security::Accept(*clientConnection);
2291
2292#if USE_OPENSSL
2293 // log ASAP, even if the handshake has not completed (or failed)
2294 const auto fd = clientConnection->fd;
2295 assert(fd >= 0);
2296 keyLogger.checkpoint(*fd_table[fd].ssl, *this);
2297#else
2298 // TODO: Support fd_table[fd].ssl dereference in other builds.
2299#endif
2300
2301 return handshakeResult;
2302}
2303
2305void
2307{
2308 Assure(params.port);
2309
2310 // NP: it is possible the port was reconfigured when the call or accept() was queued.
2311
2312 if (params.flag != Comm::OK) {
2313 // Its possible the call was still queued when the client disconnected
2314 debugs(33, 2, params.port->listenConn << ": accept failure: " << xstrerr(params.xerrno));
2315 return;
2316 }
2317
2318 debugs(33, 4, params.conn << ": accepted");
2319 fd_note(params.conn->fd, "client http connect");
2320 const auto xact = MasterXaction::MakePortful(params.port);
2321 xact->tcpClient = params.conn;
2322
2323 // Socket is ready, setup the connection manager to start using it
2324 auto *srv = Http::NewServer(xact);
2325 // XXX: do not abandon the MasterXaction object
2326 AsyncJob::Start(srv); // usually async-calls readSomeData()
2327}
2328
2330static bool
2332{
2333 const auto conn = connState->clientConnection;
2334 if (Security::CreateServerSession(ctx, conn, connState->port->secure, "client https start")) {
2335 debugs(33, 5, "will negotiate TLS on " << conn);
2336 return true;
2337 }
2338
2339 debugs(33, DBG_IMPORTANT, "ERROR: could not create TLS server context for " << conn);
2340 conn->close();
2341 return false;
2342}
2343
2345static void
2346clientNegotiateSSL(int fd, void *data)
2347{
2348 ConnStateData *conn = (ConnStateData *)data;
2349
2350 const auto handshakeResult = conn->acceptTls();
2351 switch (handshakeResult.category) {
2353 break;
2354
2356 Comm::SetSelect(conn->clientConnection->fd, COMM_SELECT_READ, clientNegotiateSSL, conn, 0);
2357 return;
2358
2360 Comm::SetSelect(conn->clientConnection->fd, COMM_SELECT_WRITE, clientNegotiateSSL, conn, 0);
2361 return;
2362
2364 debugs(83, (handshakeResult.important ? Important(62) : 2), "ERROR: " << handshakeResult.errorDescription <<
2365 " while accepting a TLS connection on " << conn->clientConnection << ": " << handshakeResult.errorDetail);
2366 // TODO: No ConnStateData::tunnelOnError() on this forward-proxy code
2367 // path because we cannot know the intended connection target?
2368 conn->updateError(ERR_SECURE_ACCEPT_FAIL, handshakeResult.errorDetail);
2369 conn->clientConnection->close();
2370 return;
2371 }
2372
2373 Security::SessionPointer session(fd_table[fd].ssl);
2374
2375#if USE_OPENSSL
2376 if (Security::SessionIsResumed(session)) {
2377 debugs(83, 2, "Session " << SSL_get_session(session.get()) <<
2378 " reused on FD " << fd << " (" << fd_table[fd].ipaddr <<
2379 ":" << (int)fd_table[fd].remote_port << ")");
2380 } else {
2381 if (Debug::Enabled(83, 4)) {
2382 /* Write out the SSL session details.. actually the call below, but
2383 * OpenSSL headers do strange typecasts confusing GCC.. */
2384 /* PEM_write_SSL_SESSION(debug_log, SSL_get_session(ssl)); */
2385#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00908000L
2386 PEM_ASN1_write(reinterpret_cast<i2d_of_void *>(i2d_SSL_SESSION),
2387 PEM_STRING_SSL_SESSION, debug_log,
2388 reinterpret_cast<char *>(SSL_get_session(session.get())),
2389 nullptr, nullptr, 0, nullptr, nullptr);
2390
2391#elif (ALLOW_ALWAYS_SSL_SESSION_DETAIL == 1)
2392
2393 /* When using gcc 3.3.x and OpenSSL 0.9.7x sometimes a compile error can occur here.
2394 * This is caused by an unpredicatble gcc behaviour on a cast of the first argument
2395 * of PEM_ASN1_write(). For this reason this code section is disabled. To enable it,
2396 * define ALLOW_ALWAYS_SSL_SESSION_DETAIL=1.
2397 * Because there are two possible usable cast, if you get an error here, try the other
2398 * commented line. */
2399
2400 PEM_ASN1_write((int(*)())i2d_SSL_SESSION, PEM_STRING_SSL_SESSION,
2401 debug_log,
2402 reinterpret_cast<char *>(SSL_get_session(session.get())),
2403 nullptr, nullptr, 0, nullptr, nullptr);
2404 /* PEM_ASN1_write((int(*)(...))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION,
2405 debug_log,
2406 reinterpret_cast<char *>(SSL_get_session(session.get())),
2407 nullptr, nullptr, 0, nullptr, nullptr);
2408 */
2409#else
2410 debugs(83, 4, "With " OPENSSL_VERSION_TEXT ", session details are available only defining ALLOW_ALWAYS_SSL_SESSION_DETAIL=1 in the source.");
2411
2412#endif
2413 /* Note: This does not automatically fflush the log file.. */
2414 }
2415
2416 debugs(83, 2, "New session " << SSL_get_session(session.get()) <<
2417 " on FD " << fd << " (" << fd_table[fd].ipaddr << ":" <<
2418 fd_table[fd].remote_port << ")");
2419 }
2420#else
2421 debugs(83, 2, "TLS session reuse not yet implemented.");
2422#endif
2423
2424 // Connection established. Retrieve TLS connection parameters for logging.
2425 conn->clientConnection->tlsNegotiations()->retrieveNegotiatedInfo(session);
2426
2427#if USE_OPENSSL
2428 X509 *client_cert = SSL_get_peer_certificate(session.get());
2429
2430 if (client_cert) {
2431 debugs(83, 3, "FD " << fd << " client certificate: subject: " <<
2432 Security::SubjectName(*client_cert));
2433
2434 debugs(83, 3, "FD " << fd << " client certificate: issuer: " <<
2435 Security::IssuerName(*client_cert));
2436
2437 X509_free(client_cert);
2438 } else {
2439 debugs(83, 5, "FD " << fd << " has no client certificate.");
2440 }
2441#else
2442 debugs(83, 2, "Client certificate requesting not yet implemented.");
2443#endif
2444
2445 // If we are called, then bumped CONNECT has succeeded. Finalize it.
2446 if (auto xact = conn->pipeline.front()) {
2447 if (xact->http && xact->http->request && xact->http->request->method == Http::METHOD_CONNECT)
2448 xact->finished();
2449 // cannot proceed with encryption if requests wait for plain responses
2450 Must(conn->pipeline.empty());
2451 }
2452 /* careful: finished() above frees request, host, etc. */
2453
2454 conn->readSomeData();
2455}
2456
2461static void
2463{
2464 assert(connState);
2465 const Comm::ConnectionPointer &details = connState->clientConnection;
2466
2467 if (!ctx || !httpsCreate(connState, ctx))
2468 return;
2469
2471
2472 Comm::SetSelect(details->fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
2473}
2474
2475#if USE_OPENSSL
2479static void
2481{
2482 ConnStateData *connState = (ConnStateData *) data;
2483
2484 // if the connection is closed or closing, just return.
2485 if (!connState->isOpen())
2486 return;
2487
2488 if (answer.allowed()) {
2489 debugs(33, 2, "sslBump action " << Ssl::bumpMode(answer.kind) << "needed for " << connState->clientConnection);
2490 connState->sslBumpMode = static_cast<Ssl::BumpMode>(answer.kind);
2491 } else {
2492 debugs(33, 3, "sslBump not needed for " << connState->clientConnection);
2493 connState->sslBumpMode = Ssl::bumpSplice;
2494 }
2495
2496 if (connState->sslBumpMode == Ssl::bumpTerminate) {
2497 connState->clientConnection->close();
2498 return;
2499 }
2500
2501 if (!connState->fakeAConnectRequest("ssl-bump", connState->inBuf))
2502 connState->clientConnection->close();
2503}
2504#endif
2505
2507static void
2509{
2510 Assure(params.port);
2511
2512 // NP: it is possible the port was reconfigured when the call or accept() was queued.
2513
2514 if (params.flag != Comm::OK) {
2515 // Its possible the call was still queued when the client disconnected
2516 debugs(33, 2, "httpsAccept: " << params.port->listenConn << ": accept failure: " << xstrerr(params.xerrno));
2517 return;
2518 }
2519
2520 const auto xact = MasterXaction::MakePortful(params.port);
2521 xact->tcpClient = params.conn;
2522
2523 debugs(33, 4, params.conn << " accepted, starting SSL negotiation.");
2524 fd_note(params.conn->fd, "client https connect");
2525
2526 // Socket is ready, setup the connection manager to start using it
2527 auto *srv = Https::NewServer(xact);
2528 // XXX: do not abandon the MasterXaction object
2529 AsyncJob::Start(srv); // usually async-calls postHttpsAccept()
2530}
2531
2532void
2534{
2535 if (port->flags.tunnelSslBumping) {
2536#if USE_OPENSSL
2537 debugs(33, 5, "accept transparent connection: " << clientConnection);
2538
2539 if (!Config.accessList.ssl_bump) {
2541 return;
2542 }
2543
2544 const auto mx = MasterXaction::MakePortful(port);
2545 mx->tcpClient = clientConnection;
2546 // Create a fake HTTP request and ALE for the ssl_bump ACL check,
2547 // using tproxy/intercept provided destination IP and port.
2548 // XXX: Merge with subsequent fakeAConnectRequest(), buildFakeRequest().
2549 // XXX: Do this earlier (e.g., in Http[s]::One::Server constructor).
2550 HttpRequest *request = new HttpRequest(mx);
2551 static char ip[MAX_IPSTRLEN];
2553 request->url.host(clientConnection->local.toStr(ip, sizeof(ip)));
2554 request->url.port(clientConnection->local.port());
2555 request->myportname = port->name;
2556 const AccessLogEntry::Pointer connectAle = new AccessLogEntry;
2557 CodeContext::Reset(connectAle);
2558 // TODO: Use these request/ALE when waiting for new bumped transactions.
2559
2560 ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(Config.accessList.ssl_bump, request, nullptr);
2561 fillChecklist(*acl_checklist);
2562 // Build a local AccessLogEntry to allow requiresAle() acls work
2563 acl_checklist->al = connectAle;
2564 acl_checklist->al->cache.start_time = current_time;
2565 acl_checklist->al->tcpClient = clientConnection;
2566 acl_checklist->al->cache.port = port;
2567 acl_checklist->al->cache.caddr = log_addr;
2569 acl_checklist->al->updateError(bareError);
2570 HTTPMSGUNLOCK(acl_checklist->al->request);
2571 acl_checklist->al->request = request;
2572 HTTPMSGLOCK(acl_checklist->al->request);
2574 ClientHttpRequest *http = context ? context->http : nullptr;
2575 const char *log_uri = http ? http->log_uri : nullptr;
2576 acl_checklist->syncAle(request, log_uri);
2577 acl_checklist->nonBlockingCheck(httpsSslBumpAccessCheckDone, this);
2578#else
2579 fatal("FATAL: SSL-Bump requires --with-openssl");
2580#endif
2581 return;
2582 } else {
2583 httpsEstablish(this, port->secure.staticContext);
2584 }
2585}
2586
2587#if USE_OPENSSL
2588void
2590{
2591 ConnStateData * state_data = (ConnStateData *)(data);
2592 state_data->sslCrtdHandleReply(reply);
2593}
2594
2595void
2597{
2598 if (!isOpen()) {
2599 debugs(33, 3, "Connection gone while waiting for ssl_crtd helper reply; helper reply:" << reply);
2600 return;
2601 }
2602
2603 if (reply.result == Helper::BrokenHelper) {
2604 debugs(33, 5, "Certificate for " << tlsConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply);
2605 } else if (!reply.other().hasContent()) {
2606 debugs(1, DBG_IMPORTANT, "\"ssl_crtd\" helper returned <NULL> reply.");
2607 } else {
2609 if (reply_message.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK) {
2610 debugs(33, 5, "Reply from ssl_crtd for " << tlsConnectHostOrIp << " is incorrect");
2611 } else {
2612 if (reply.result != Helper::Okay) {
2613 debugs(33, 5, "Certificate for " << tlsConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply_message.getBody());
2614 } else {
2615 debugs(33, 5, "Certificate for " << tlsConnectHostOrIp << " was successfully received from ssl_crtd");
2618 auto ssl = fd_table[clientConnection->fd].ssl.get();
2619 bool ret = Ssl::configureSSLUsingPkeyAndCertFromMemory(ssl, reply_message.getBody().c_str(), *port);
2620 if (!ret)
2621 debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
2622
2625 } else {
2627 if (ctx && !sslBumpCertKey.isEmpty())
2629 getSslContextDone(ctx);
2630 }
2631 return;
2632 }
2633 }
2634 }
2637}
2638
2640{
2642
2643 const bool connectedOk = sslServerBump && sslServerBump->connectedOk();
2644 if (connectedOk) {
2645 if (X509 *mimicCert = sslServerBump->serverCert.get())
2646 certProperties.mimicCert.resetAndLock(mimicCert);
2647
2648 ACLFilledChecklist checklist(nullptr, sslServerBump->request.getRaw());
2649 fillChecklist(checklist);
2650
2651 for (sslproxy_cert_adapt *ca = Config.ssl_client.cert_adapt; ca != nullptr; ca = ca->next) {
2652 // If the algorithm already set, then ignore it.
2653 if ((ca->alg == Ssl::algSetCommonName && certProperties.setCommonName) ||
2654 (ca->alg == Ssl::algSetValidAfter && certProperties.setValidAfter) ||
2655 (ca->alg == Ssl::algSetValidBefore && certProperties.setValidBefore) )
2656 continue;
2657
2658 if (ca->aclList && checklist.fastCheck(ca->aclList).allowed()) {
2659 const char *alg = Ssl::CertAdaptAlgorithmStr[ca->alg];
2660 const char *param = ca->param;
2661
2662 // For parameterless CN adaptation, use hostname from the
2663 // CONNECT request.
2664 if (ca->alg == Ssl::algSetCommonName) {
2665 if (!param)
2666 param = tlsConnectHostOrIp.c_str();
2667 certProperties.commonName = param;
2668 certProperties.setCommonName = true;
2669 } else if (ca->alg == Ssl::algSetValidAfter)
2670 certProperties.setValidAfter = true;
2671 else if (ca->alg == Ssl::algSetValidBefore)
2672 certProperties.setValidBefore = true;
2673
2674 debugs(33, 5, "Matches certificate adaptation aglorithm: " <<
2675 alg << " param: " << (param ? param : "-"));
2676 }
2677 }
2678
2679 certProperties.signAlgorithm = Ssl::algSignEnd;
2680 for (sslproxy_cert_sign *sg = Config.ssl_client.cert_sign; sg != nullptr; sg = sg->next) {
2681 if (sg->aclList && checklist.fastCheck(sg->aclList).allowed()) {
2682 certProperties.signAlgorithm = (Ssl::CertSignAlgorithm)sg->alg;
2683 break;
2684 }
2685 }
2686 } else {// did not try to connect (e.g. client-first) or failed to connect
2687 // In case of an error while connecting to the secure server, use a
2688 // trusted certificate, with no mimicked fields and no adaptation
2689 // algorithms. There is nothing we can mimic, so we want to minimize the
2690 // number of warnings the user will have to see to get to the error page.
2691 // We will close the connection, so that the trust is not extended to
2692 // non-Squid content.
2693 certProperties.signAlgorithm = Ssl::algSignTrusted;
2694 }
2695
2696 assert(certProperties.signAlgorithm != Ssl::algSignEnd);
2697
2698 if (certProperties.signAlgorithm == Ssl::algSignUntrusted) {
2699 assert(port->secure.untrustedSigningCa.cert);
2700 certProperties.signWithX509.resetAndLock(port->secure.untrustedSigningCa.cert.get());
2701 certProperties.signWithPkey.resetAndLock(port->secure.untrustedSigningCa.pkey.get());
2702 } else {
2703 assert(port->secure.signingCa.cert.get());
2704 certProperties.signWithX509.resetAndLock(port->secure.signingCa.cert.get());
2705
2706 if (port->secure.signingCa.pkey)
2707 certProperties.signWithPkey.resetAndLock(port->secure.signingCa.pkey.get());
2708 }
2709 signAlgorithm = certProperties.signAlgorithm;
2710
2711 certProperties.signHash = Ssl::DefaultSignHash;
2712}
2713
2716{
2717 debugs(33, 5, "Finding SSL certificate for " << cacheKey << " in cache");
2719 if (const auto ctx = ssl_ctx_cache ? ssl_ctx_cache->get(cacheKey) : nullptr) {
2720 if (Ssl::verifySslCertificate(*ctx, certProperties)) {
2721 debugs(33, 5, "Cached SSL certificate for " << certProperties.commonName << " is valid");
2722 return *ctx;
2723 } else {
2724 debugs(33, 5, "Cached SSL certificate for " << certProperties.commonName << " is out of date. Delete this certificate from cache");
2725 if (ssl_ctx_cache)
2726 ssl_ctx_cache->del(cacheKey);
2727 }
2728 }
2729 return Security::ContextPointer(nullptr);
2730}
2731
2732void
2734{
2736 if (!ssl_ctx_cache || !ssl_ctx_cache->add(cacheKey, ctx)) {
2737 // If it is not in storage delete after using. Else storage deleted it.
2738 fd_table[clientConnection->fd].html?amicTlsContext = ctx;
2739 }
2740}
2741
2742void
2744{
2745 if (port->secure.generateHostCertificates) {
2746 Ssl::CertificateProperties certProperties;
2747 buildSslCertGenerationParams(certProperties);
2748
2749 // Disable caching for bumpPeekAndSplice mode
2754
2756 if (ctx) {
2757 getSslContextDone(ctx);
2758 return;
2759 }
2760 }
2761
2762#if USE_SSL_CRTD
2763 try {
2764 debugs(33, 5, "Generating SSL certificate for " << certProperties.commonName << " using ssl_crtd.");
2767 request_message.composeRequest(certProperties);
2768 debugs(33, 5, "SSL crtd request: " << request_message.compose().c_str());
2769 Ssl::Helper::Submit(request_message, sslCrtdHandleReplyWrapper, this);
2770 return;
2771 } catch (const std::exception &e) {
2772 debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtd " <<
2773 "request for " << certProperties.commonName <<
2774 " certificate: " << e.what() << "; will now block to " <<
2775 "generate that certificate.");
2776 // fall through to do blocking in-process generation.
2777 }
2778#endif // USE_SSL_CRTD
2779
2780 debugs(33, 5, "Generating SSL certificate for " << certProperties.commonName);
2783 auto ssl = fd_table[clientConnection->fd].ssl.get();
2784 if (!Ssl::configureSSL(ssl, certProperties, *port))
2785 debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
2786
2789 } else {
2791 if (dynCtx && !sslBumpCertKey.isEmpty())
2793 getSslContextDone(dynCtx);
2794 }
2795 return;
2796 }
2797
2800}
2801
2802void
2804{
2805 if (port->secure.generateHostCertificates && !ctx) {
2806 debugs(33, 2, "Failed to generate TLS context for " << tlsConnectHostOrIp);
2807 }
2808
2809 // If generated ssl context = nullptr, try to use static ssl context.
2810 if (!ctx) {
2811 if (!port->secure.staticContext) {
2812 debugs(83, DBG_IMPORTANT, "Closing " << clientConnection->remote << " as lacking TLS context");
2814 return;
2815 } else {
2816 debugs(33, 5, "Using static TLS context.");
2817 ctx = port->secure.staticContext;
2818 }
2819 }
2820
2821 if (!httpsCreate(this, ctx))
2822 return;
2823
2824 // bumped intercepted conns should already have Config.Timeout.request set
2825 // but forwarded connections may only have Config.Timeout.lifetime. [Re]set
2826 // to make sure the connection does not get stuck on non-SSL clients.
2828
2829 switchedToHttps_ = true;
2830
2831 auto ssl = fd_table[clientConnection->fd].ssl.get();
2832 BIO *b = SSL_get_rbio(ssl);
2833 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(BIO_get_data(b));
2834 bio->setReadBufData(inBuf);
2835 inBuf.clear();
2837}
2838
2839void
2841{
2843 Must(http->request);
2844 auto &request = http->request;
2845
2846 // Depending on receivedFirstByte_, we are at the start of either an
2847 // established CONNECT tunnel with the client or an intercepted TCP (and
2848 // presumably TLS) connection from the client. Expect TLS Client Hello.
2849 const auto insideConnectTunnel = receivedFirstByte_;
2850 debugs(33, 5, (insideConnectTunnel ? "post-CONNECT " : "raw TLS ") << clientConnection);
2851
2852 tlsConnectHostOrIp = request->url.hostOrIp();
2853 tlsConnectPort = request->url.port();
2854 resetSslCommonName(request->url.host());
2855
2856 // We are going to read new request
2857 flags.readMore = true;
2858
2859 // keep version major.minor details the same.
2860 // but we are now performing the HTTPS handshake traffic
2862
2863 // If sslServerBump is set, then we have decided to deny CONNECT
2864 // and now want to switch to SSL to send the error to the client
2865 // without even peeking at the origin server certificate.
2866 if (bumpServerMode == Ssl::bumpServerFirst && !sslServerBump) {
2867 request->flags.sslPeek = true;
2868 sslServerBump = new Ssl::ServerBump(http);
2869 } else if (bumpServerMode == Ssl::bumpPeek || bumpServerMode == Ssl::bumpStare) {
2870 request->flags.sslPeek = true;
2871 sslServerBump = new Ssl::ServerBump(http, nullptr, bumpServerMode);
2872 }
2873
2874 // commSetConnTimeout() was called for this request before we switched.
2875 // Fix timeout to request_start_timeout
2877 // Also reset receivedFirstByte_ flag to allow this timeout work in the case we have
2878 // a bumbed "connect" request on non transparent port.
2879 receivedFirstByte_ = false;
2880 // Get more data to peek at Tls
2881 parsingTlsHandshake = true;
2882
2883 // If the protocol has changed, then reset preservingClientData_.
2884 // Otherwise, its value initially set in start() is still valid/fresh.
2885 // shouldPreserveClientData() uses parsingTlsHandshake which is reset above.
2886 if (insideConnectTunnel)
2888
2889 readSomeData();
2890}
2891
2892void
2894{
2896
2897 assert(!inBuf.isEmpty());
2899 fd_note(clientConnection->fd, "Parsing TLS handshake");
2900
2901 // stops being nil if we fail to parse the handshake
2902 ErrorDetail::Pointer parseErrorDetails;
2903
2904 try {
2905 if (!tlsParser.parseHello(inBuf)) {
2906 // need more data to finish parsing
2907 readSomeData();
2908 return;
2909 }
2910 }
2911 catch (const TextException &ex) {
2912 debugs(83, 2, "exception: " << ex);
2913 parseErrorDetails = new ExceptionErrorDetail(ex.id());
2914 }
2915 catch (...) {
2916 debugs(83, 2, "exception: " << CurrentException);
2917 static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_PARSE");
2918 parseErrorDetails = d;
2919 }
2920
2921 parsingTlsHandshake = false;
2922
2923 // client data may be needed for splicing and for
2924 // tunneling unsupportedProtocol after an error
2926
2927 // Even if the parser failed, each TLS detail should either be set
2928 // correctly or still be "unknown"; copying unknown detail is a no-op.
2931 if (details && !details->serverName.isEmpty()) {
2932 resetSslCommonName(details->serverName.c_str());
2933 tlsClientSni_ = details->serverName;
2934 }
2935
2936 // We should disable read/write handlers
2938
2939 if (parseErrorDetails) {
2941 Must(context && context->http);
2942 HttpRequest::Pointer request = context->http->request;
2943 debugs(83, 5, "Got something other than TLS Client Hello. Cannot SslBump.");
2944 updateError(ERR_PROTOCOL_UNKNOWN, parseErrorDetails);
2947 return;
2948 }
2949
2950 if (!sslServerBump || sslServerBump->act.step1 == Ssl::bumpClientFirst) { // Either means client-first.
2952 return;
2954 debugs(83, 5, "server-first skips step2; start forwarding the request");
2957 ClientHttpRequest *http = context ? context->http : nullptr;
2958 // will call httpsPeeked() with certificate and connection, eventually
2960 } else {
2963 }
2964}
2965
2966static void
2968{
2969 ConnStateData *connState = (ConnStateData *) data;
2970
2971 // if the connection is closed or closing, just return.
2972 if (!connState->isOpen())
2973 return;
2974
2975 debugs(33, 5, "Answer: " << answer << " kind:" << answer.kind);
2976 assert(connState->serverBump());
2977 Ssl::BumpMode bumpAction;
2978 if (answer.allowed()) {
2979 bumpAction = (Ssl::BumpMode)answer.kind;
2980 } else
2981 bumpAction = Ssl::bumpSplice;
2982
2983 connState->serverBump()->act.step2 = bumpAction;
2984 connState->sslBumpMode = bumpAction;
2985 Http::StreamPointer context = connState->pipeline.front();
2986 if (ClientHttpRequest *http = (context ? context->http : nullptr))
2987 http->al->ssl.bumpMode = bumpAction;
2988
2989 if (bumpAction == Ssl::bumpTerminate) {
2990 connState->clientConnection->close();
2991 } else if (bumpAction != Ssl::bumpSplice) {
2992 connState->startPeekAndSplice();
2993 } else if (!connState->splice())
2994 connState->clientConnection->close();
2995}
2996
2997bool
2999{
3000 // normally we can splice here, because we just got client hello message
3001
3002 // fde::ssl/tls_read_method() probably reads from our own inBuf. If so, then
3003 // we should not lose any raw bytes when switching to raw I/O here.
3004 if (fd_table[clientConnection->fd].ssl.get())
3005 fd_table[clientConnection->fd].useDefaultIo();
3006
3007 // XXX: assuming that there was an HTTP/1.1 CONNECT to begin with...
3008 // reset the current protocol to HTTP/1.1 (was "HTTPS" for the bumping process)
3010 assert(!pipeline.empty());
3012 Must(context);
3013 Must(context->http);
3014 ClientHttpRequest *http = context->http;
3015 HttpRequest::Pointer request = http->request;
3016 context->finished();
3017 if (transparent()) {
3018 // For transparent connections, make a new fake CONNECT request, now
3019 // with SNI as target. doCallout() checks, adaptations may need that.
3020 return fakeAConnectRequest("splice", preservedClientData);
3021 } else {
3022 // For non transparent connections make a new tunneled CONNECT, which
3023 // also sets the HttpRequest::flags::forceTunnel flag to avoid
3024 // respond with "Connection Established" to the client.
3025 // This fake CONNECT request required to allow use of SNI in
3026 // doCallout() checks and adaptations.
3027 return initiateTunneledRequest(request, "splice", preservedClientData);
3028 }
3029}
3030
3031void
3033{
3034 // This is the Step2 of the SSL bumping
3037 ClientHttpRequest *http = context ? context->http : nullptr;
3038
3041 // Run a accessList check to check if want to splice or continue bumping
3042
3047 fillChecklist(*acl_checklist);
3049 return;
3050 }
3051
3052 // will call httpsPeeked() with certificate and connection, eventually
3053 Security::ContextPointer unConfiguredCTX(Ssl::createSSLContext(port->secure.signingCa.cert, port->secure.signingCa.pkey, port->secure));
3054 fd_table[clientConnection->fd].html?amicTlsContext = unConfiguredCTX;
3055
3056 if (!httpsCreate(this, unConfiguredCTX))
3057 return;
3058
3059 switchedToHttps_ = true;
3060
3061 auto ssl = fd_table[clientConnection->fd].ssl.get();
3062 BIO *b = SSL_get_rbio(ssl);
3063 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(BIO_get_data(b));
3064 bio->setReadBufData(inBuf);
3065 bio->hold(true);
3066
3067 // We have successfully parsed client Hello, but our TLS handshake parser is
3068 // forgiving. Now we use a TLS library to parse the same bytes, so that we
3069 // can honor on_unsupported_protocol if needed. If there are no errors, we
3070 // expect Security::Accept() to ask us to write (our) TLS server Hello. We
3071 // also allow an ioWantRead result in case some fancy TLS extension that
3072 // Squid does not yet understand requires reading post-Hello client bytes.
3073 const auto handshakeResult = acceptTls();
3074 if (!handshakeResult.wantsIo())
3075 return handleSslBumpHandshakeError(handshakeResult);
3076
3077 // We need to reset inBuf here, to be used by incoming requests in the case
3078 // of SSL bump
3079 inBuf.clear();
3080
3081 debugs(83, 5, "Peek and splice at step2 done. Start forwarding the request!!! ");
3084}
3085
3087void
3089{
3090 auto errCategory = ERR_NONE;
3091
3092 switch (handshakeResult.category) {
3094 static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_UNEXPECTED_SUCCESS");
3095 updateError(errCategory = ERR_GATEWAY_FAILURE, d);
3096 break;
3097 }
3098
3100 static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_UNEXPECTED_READ");
3101 updateError(errCategory = ERR_GATEWAY_FAILURE, d);
3102 break;
3103 }
3104
3106 static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_UNEXPECTED_WRITE");
3107 updateError(errCategory = ERR_GATEWAY_FAILURE, d);
3108 break;
3109 }
3110
3112 debugs(83, (handshakeResult.important ? DBG_IMPORTANT : 2), "ERROR: " << handshakeResult.errorDescription <<
3113 " while SslBump-accepting a TLS connection on " << clientConnection << ": " << handshakeResult.errorDetail);
3114 updateError(errCategory = ERR_SECURE_ACCEPT_FAIL, handshakeResult.errorDetail);
3115 break;
3116
3117 }
3118
3119 if (!tunnelOnError(errCategory))
3121}
3122
3123void
3125{
3126 auto ssl = fd_table[clientConnection->fd].ssl.get();
3127 BIO *b = SSL_get_rbio(ssl);
3128 assert(b);
3129 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(BIO_get_data(b));
3130
3131 debugs(33, 5, "PeekAndSplice mode, proceed with client negotiation. Current state:" << SSL_state_string_long(ssl));
3132 bio->hold(false);
3133
3135 switchedToHttps_ = true;
3136}
3137
3138void
3140{
3141 Must(sslServerBump != nullptr);
3143 Must(pipeline.empty() || pipeline.front()->http == nullptr || pipeline.front()->http->request == pic.request.getRaw());
3144
3145 if (Comm::IsConnOpen(pic.connection)) {
3147 debugs(33, 5, "bumped HTTPS server: " << tlsConnectHostOrIp);
3148 } else
3149 debugs(33, 5, "Error while bumping: " << tlsConnectHostOrIp);
3150
3152}
3153
3154#endif /* USE_OPENSSL */
3155
3156bool
3157ConnStateData::initiateTunneledRequest(HttpRequest::Pointer const &cause, const char *reason, const SBuf &payload)
3158{
3159 // fake a CONNECT request to force connState to tunnel
3160 SBuf connectHost;
3161 AnyP::Port connectPort;
3162
3163 if (pinning.serverConnection != nullptr) {
3164 static char ip[MAX_IPSTRLEN];
3165 connectHost = pinning.serverConnection->remote.toStr(ip, sizeof(ip));
3166 if (const auto remotePort = pinning.serverConnection->remote.port())
3167 connectPort = remotePort;
3168 } else if (cause) {
3169 connectHost = cause->url.hostOrIp();
3170 connectPort = cause->url.port();
3171#if USE_OPENSSL
3172 } else if (!tlsConnectHostOrIp.isEmpty()) {
3173 connectHost = tlsConnectHostOrIp;
3174 connectPort = tlsConnectPort;
3175#endif
3176 } else if (transparent()) {
3177 static char ip[MAX_IPSTRLEN];
3178 connectHost = clientConnection->local.toStr(ip, sizeof(ip));
3179 connectPort = clientConnection->local.port();
3180 }
3181
3182 if (!connectPort) {
3183 // Typical cases are malformed HTTP requests on http_port and malformed
3184 // TLS handshakes on non-bumping https_port. TODO: Discover these
3185 // problems earlier so that they can be classified/detailed better.
3186 debugs(33, 2, "Not able to compute URL, abort request tunneling for " << reason);
3187 // TODO: throw when nonBlockingCheck() callbacks gain job protections
3188 static const auto d = MakeNamedErrorDetail("TUNNEL_TARGET");
3190 return false;
3191 }
3192
3193 debugs(33, 2, "Request tunneling for " << reason);
3194 const auto http = buildFakeRequest(connectHost, *connectPort, payload);
3195 HttpRequest::Pointer request = http->request;
3196 request->flags.forceTunnel = true;
3197 http->calloutContext = new ClientRequestContext(http);
3198 http->doCallouts();
3199 clientProcessRequestFinished(this, request);
3200 return true;
3201}
3202
3203bool
3204ConnStateData::fakeAConnectRequest(const char *reason, const SBuf &payload)
3205{
3206 debugs(33, 2, "fake a CONNECT request to force connState to tunnel for " << reason);
3207
3208 SBuf connectHost;
3210 const unsigned short connectPort = clientConnection->local.port();
3211
3212#if USE_OPENSSL
3213 if (!tlsClientSni_.isEmpty())
3214 connectHost.assign(tlsClientSni_);
3215 else
3216#endif
3217 {
3218 static char ip[MAX_IPSTRLEN];
3219 clientConnection->local.toHostStr(ip, sizeof(ip));
3220 connectHost.assign(ip);
3221 }
3222
3223 ClientHttpRequest *http = buildFakeRequest(connectHost, connectPort, payload);
3224
3225 http->calloutContext = new ClientRequestContext(http);
3226 HttpRequest::Pointer request = http->request;
3227 http->doCallouts();
3228 clientProcessRequestFinished(this, request);
3229 return true;
3230}
3231
3233ConnStateData::buildFakeRequest(SBuf &useHost, const AnyP::KnownPort usePort, const SBuf &payload)
3234{
3235 ClientHttpRequest *http = new ClientHttpRequest(this);
3236 Http::Stream *stream = new Http::Stream(clientConnection, http);
3237
3238 StoreIOBuffer tempBuffer;
3239 tempBuffer.data = stream->reqbuf;
3240 tempBuffer.length = HTTP_REQBUF_SZ;
3241
3242 ClientStreamData newServer = new clientReplyContext(http);
3243 ClientStreamData newClient = stream;
3246 clientSocketDetach, newClient, tempBuffer);
3247
3248 stream->flags.parsed_ok = 1; // Do we need it?
3249 stream->mayUseConnection(true);
3251 stream->registerWithConn();
3252
3253 const auto mx = MasterXaction::MakePortful(port);
3254 mx->tcpClient = clientConnection;
3255 // Setup Http::Request object. Maybe should be replaced by a call to (modified)
3256 // clientProcessRequest
3257 HttpRequest::Pointer request = new HttpRequest(mx);
3258 request->url.setScheme(AnyP::PROTO_AUTHORITY_FORM, nullptr);
3259 request->method = Http::METHOD_CONNECT;
3260 request->url.host(useHost.c_str());
3261 request->url.port(usePort);
3262
3263 http->uri = SBufToCstring(request->effectiveRequestUri());
3264 http->initRequest(request.getRaw());
3265
3266 request->manager(this, http->al);
3267
3268 request->header.putStr(Http::HOST, useHost.c_str());
3269
3270 request->sources |= ((switchedToHttps() || port->transport.protocol == AnyP::PROTO_HTTPS) ? Http::Message::srcHttps : Http::Message::srcHttp);
3271#if USE_AUTH
3272 if (getAuth())
3273 request->auth_user_request = getAuth();
3274#endif
3275
3276 inBuf = payload;
3277 flags.readMore = false;
3278
3279 return http;
3280}
3281
3283static bool
3285{
3286 if (!Comm::IsConnOpen(c)) {
3287 Must(NHttpSockets > 0); // we tried to open some
3288 --NHttpSockets; // there will be fewer sockets than planned
3289 Must(HttpSockets[NHttpSockets] < 0); // no extra fds received
3290
3291 if (!NHttpSockets) // we could not open any listen sockets at all
3292 fatalf("Unable to open %s",FdNote(portType));
3293
3294 return false;
3295 }
3296 return true;
3297}
3298
3300static bool
3302{
3303 bool found = false;
3304 for (int i = 0; i < NHttpSockets && !found; ++i) {
3305 if ((found = HttpSockets[i] < 0))
3306 HttpSockets[i] = conn->fd;
3307 }
3308 return found;
3309}
3310
3311static void
3313{
3314 const auto savedContext = CodeContext::Current();
3315 for (AnyP::PortCfgPointer s = HttpPortList; s != nullptr; s = s->next) {
3317 const SBuf &scheme = AnyP::UriScheme(s->transport.protocol).image();
3318
3320 debugs(1, DBG_IMPORTANT, "WARNING: You have too many '" << scheme << "_port' lines." <<
3321 Debug::Extra << "The limit is " << MAXTCPLISTENPORTS << " HTTP ports.");
3322 continue;
3323 }
3324
3325#if USE_OPENSSL
3326 if (s->flags.tunnelSslBumping) {
3327 if (!Config.accessList.ssl_bump) {
3328 debugs(33, DBG_IMPORTANT, "WARNING: No ssl_bump configured. Disabling ssl-bump on " << scheme << "_port " << s->s);
3329 s->flags.tunnelSslBumping = false;
3330 }
3331 if (!s->secure.staticContext && !s->secure.generateHostCertificates) {
3332 debugs(1, DBG_IMPORTANT, "Will not bump SSL at " << scheme << "_port " << s->s << " due to TLS initialization failure.");
3333 s->flags.tunnelSslBumping = false;
3334 if (s->transport.protocol == AnyP::PROTO_HTTP)
3335 s->secure.encryptTransport = false;
3336 }
3337 if (s->flags.tunnelSslBumping) {
3338 // Create ssl_ctx cache for this port.
3339 Ssl::TheGlobalContextStorage.addLocalStorage(s->s, s->secure.html?amicCertMemCacheSize);
3340 }
3341 }
3342#endif
3343
3344 if (s->secure.encryptTransport && !s->secure.staticContext) {
3345 debugs(1, DBG_CRITICAL, "ERROR: Ignoring " << scheme << "_port " << s->s << " due to TLS context initialization failure.");
3346 continue;
3347 }
3348
3349 const auto protocol = s->transport.protocol;
3350 assert(protocol == AnyP::PROTO_HTTP || protocol == AnyP::PROTO_HTTPS);
3351 const auto isHttps = protocol == AnyP::PROTO_HTTPS;
3352 using AcceptCall = CommCbFunPtrCallT<CommAcceptCbPtrFun>;
3353 RefCount<AcceptCall> subCall = commCbCall(5, 5, isHttps ? "httpsAccept" : "httpAccept",
3356 }
3357 CodeContext::Reset(savedContext);
3358}
3359
3360void
3362{
3363 // Fill out a Comm::Connection which IPC will open as a listener for us
3364 port->listenConn = new Comm::Connection;
3365 port->listenConn->local = port->s;
3366 port->listenConn->flags =
3368 (port->flags.tproxyIntercept ? COMM_TRANSPARENT : 0) |
3369 (port->flags.natIntercept ? COMM_INTERCEPTION : 0) |
3370 (port->workerQueues ? COMM_REUSEPORT : 0);
3371
3372 // route new connections to subCall
3373 typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
3375 const auto listenCall =
3376 asyncCall(33, 2, "clientListenerConnectionOpened",
3378 port, fdNote, sub));
3379 AsyncCallback<Ipc::StartListeningAnswer> callback(listenCall);
3380 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, port->listenConn, fdNote, callback);
3381
3384 ++NHttpSockets;
3385}
3386
3388static void
3390{
3391 Must(s != nullptr);
3392
3393 if (!OpenedHttpSocket(s->listenConn, portTypeNote))
3394 return;
3395
3396 Must(Comm::IsConnOpen(s->listenConn));
3397
3398 // TCP: setup a job to handle accept() with subscribed handler
3399 AsyncJob::Start(new Comm::TcpAcceptor(s, FdNote(portTypeNote), sub));
3400
3401 debugs(1, Important(13), "Accepting " <<
3402 (s->flags.natIntercept ? "NAT intercepted " : "") <<
3403 (s->flags.tproxyIntercept ? "TPROXY intercepted " : "") <<
3404 (s->flags.tunnelSslBumping ? "SSL bumped " : "") <<
3405 (s->flags.accelSurrogate ? "reverse-proxy " : "")
3406 << FdNote(portTypeNote) << " connections at "
3407 << s->listenConn);
3408
3409 Must(AddOpenedHttpSocket(s->listenConn)); // otherwise, we have received a fd we did not ask for
3410
3411#if USE_SYSTEMD
3412 // When the very first port opens, tell systemd we are able to serve connections.
3413 // Subsequent sd_notify() calls, including calls during reconfiguration,
3414 // do nothing because the first call parameter is 1.
3415 // XXX: Send the notification only after opening all configured ports.
3417 const auto result = sd_notify(1, "READY=1");
3418 if (result < 0) {
3419 debugs(1, DBG_IMPORTANT, "WARNING: failed to send start-up notification to systemd" <<
3420 Debug::Extra << "sd_notify() error: " << xstrerr(-result));
3421 }
3422 }
3423#endif
3424}
3425
3426void
3428{
3431
3432 if (NHttpSockets < 1)
3433 fatal("No HTTP, HTTPS, or FTP ports configured");
3434}
3435
3436void
3438{
3439 const auto savedContext = CodeContext::Current();
3440 for (AnyP::PortCfgPointer s = HttpPortList; s != nullptr; s = s->next) {
3442 if (s->listenConn != nullptr) {
3443 debugs(1, Important(14), "Closing HTTP(S) port " << s->listenConn->local);
3444 s->listenConn->close();
3445 s->listenConn = nullptr;
3446 }
3447 }
3448 CodeContext::Reset(savedContext);
3449
3451
3452 // TODO see if we can drop HttpSockets array entirely */
3453 for (int i = 0; i < NHttpSockets; ++i) {
3454 HttpSockets[i] = -1;
3455 }
3456
3457 NHttpSockets = 0;
3458}
3459
3460int
3462{
3463 SBuf vary(request->vary_headers);
3464 const auto &reply = entry->mem().freshestReply();
3465 auto has_vary = reply.header.has(Http::HdrType::VARY);
3466#if X_ACCELERATOR_VARY
3467
3468 has_vary |=
3469 reply.header.has(Http::HdrType::HDR_X_ACCELERATOR_VARY);
3470#endif
3471
3472 if (!has_vary || entry->mem_obj->vary_headers.isEmpty()) {
3473 if (!vary.isEmpty()) {
3474 /* Oops... something odd is going on here.. */
3475 debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary object on second attempt, '" <<
3476 entry->mem_obj->urlXXX() << "' '" << vary << "'");
3477 request->vary_headers.clear();
3478 return VARY_CANCEL;
3479 }
3480
3481 if (!has_vary) {
3482 /* This is not a varying object */
3483 return VARY_NONE;
3484 }
3485
3486 /* virtual "vary" object found. Calculate the vary key and
3487 * continue the search
3488 */
3489 vary = httpMakeVaryMark(request, &reply);
3490
3491 if (!vary.isEmpty()) {
3492 request->vary_headers = vary;
3493 return VARY_OTHER;
3494 } else {
3495 /* Ouch.. we cannot handle this kind of variance */
3496 /* XXX This cannot really happen, but just to be complete */
3497 return VARY_CANCEL;
3498 }
3499 } else {
3500 if (vary.isEmpty()) {
3501 vary = httpMakeVaryMark(request, &reply);
3502
3503 if (!vary.isEmpty())
3504 request->vary_headers = vary;
3505 }
3506
3507 if (vary.isEmpty()) {
3508 /* Ouch.. we cannot handle this kind of variance */
3509 /* XXX This cannot really happen, but just to be complete */
3510 return VARY_CANCEL;
3511 } else if (vary.cmp(entry->mem_obj->vary_headers) == 0) {
3512 return VARY_MATCH;
3513 } else {
3514 /* Oops.. we have already been here and still haven't
3515 * found the requested variant. Bail out
3516 */
3517 debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary match on second attempt, '" <<
3518 entry->mem_obj->urlXXX() << "' '" << vary << "'");
3519 return VARY_CANCEL;
3520 }
3521 }
3522}
3523
3526{
3527 const auto checklist = new ACLFilledChecklist(acl, nullptr, nullptr);
3528 clientAclChecklistFill(*checklist, http);
3529 return checklist;
3530}
3531
3532void
3534{
3535 assert(http);
3536
3537 if (!checklist.request && http->request)
3538 checklist.setRequest(http->request);
3539
3540 if (!checklist.al && http->al) {
3541 checklist.al = http->al;
3542 checklist.syncAle(http->request, http->log_uri);
3543 if (!checklist.reply && http->al->reply) {
3544 checklist.reply = http->al->reply.getRaw();
3545 HTTPMSGLOCK(checklist.reply);
3546 }
3547 }
3548
3549 if (const auto conn = http->getConn())
3550 checklist.setConn(conn); // may already be set
3551}
3552
3553void
3555{
3556 const auto context = pipeline.front();
3557 if (const auto http = context ? context->http : nullptr)
3558 return clientAclChecklistFill(checklist, http); // calls checklist.setConn()
3559
3560 // no requests, but we always have connection-level details
3561 // TODO: ACL checks should not require a mutable ConnStateData. Adjust the
3562 // code that accidentally violates that principle to remove this const_cast!
3563 checklist.setConn(const_cast<ConnStateData*>(this));
3564
3565 // Set other checklist fields inside our fillConnectionLevelDetails() rather
3566 // than here because clientAclChecklistFill() code path calls that method
3567 // (via ACLFilledChecklist::setConn()) rather than calling us directly.
3568}
3569
3570void
3572{
3573 assert(checklist.conn() == this);
3575
3576 if (!checklist.request) { // preserve (better) addresses supplied by setRequest()
3577 checklist.src_addr = clientConnection->remote;
3578 checklist.my_addr = clientConnection->local; // TODO: or port->s?
3579 }
3580
3581#if USE_OPENSSL
3582 if (!checklist.sslErrors && sslServerBump)
3583 checklist.sslErrors = sslServerBump->sslErrors();
3584#endif
3585
3586 if (!checklist.rfc931[0]) // checklist creator may have supplied it already
3587 checklist.setIdent(clientConnection->rfc931);
3588
3589}
3590
3591bool
3593{
3595}
3596
3599{
3600 bodyPipe = new BodyPipe(this);
3601 if (size >= 0)
3603 else
3605 return bodyPipe;
3606}
3607
3608int64_t
3610{
3611 if (!bodyPipe)
3612 return 0; // request without a body or read/produced all body bytes
3613
3614 if (!bodyPipe->bodySizeKnown())
3615 return -1; // probably need to read more, but we cannot be sure
3616
3617 const int64_t needToProduce = bodyPipe->unproducedSize();
3618 const int64_t haveAvailable = static_cast<int64_t>(inBuf.length());
3619
3620 if (needToProduce <= haveAvailable)
3621 return 0; // we have read what we need (but are waiting for pipe space)
3622
3623 return needToProduce - haveAvailable;
3624}
3625
3626void
3628{
3629 debugs(33, 4, "receiving error (" << clientConnection << "): " << error <<
3630 "; old sending error: " <<
3631 (stoppedSending() ? stoppedSending_ : "none"));
3632
3633 if (const char *oldError = stoppedReceiving()) {
3634 debugs(33, 3, "already stopped receiving: " << oldError);
3635 return; // nothing has changed as far as this connection is concerned
3636 }
3637
3639
3640 if (const char *sendError = stoppedSending()) {
3641 debugs(33, 3, "closing because also stopped sending: " << sendError);
3643 }
3644}
3645
3646void
3648{
3649 if (bodyPipe != nullptr) {
3650 debugs(33, 4, "no consumer for virgin body " << bodyPipe->status());
3652 }
3653}
3654
3656void
3658{
3659 Must(bodyPipe != nullptr);
3660 debugs(33, 5, "start dechunking" << bodyPipe->status());
3663}
3664
3666void
3668{
3669 debugs(33, 5, "finish dechunking: " << withSuccess);
3670
3671 if (bodyPipe != nullptr) {
3672 debugs(33, 7, "dechunked tail: " << bodyPipe->status());
3673 BodyPipe::Pointer myPipe = bodyPipe;
3674 stopProducingFor(bodyPipe, withSuccess); // sets bodyPipe->bodySize()
3675 Must(!bodyPipe); // we rely on it being nil after we are done with body
3676 if (withSuccess) {
3677 Must(myPipe->bodySizeKnown());
3679 if (context != nullptr && context->http && context->http->request)
3680 context->http->request->setContentLength(myPipe->bodySize());
3681 }
3682 }
3683
3684 delete bodyParser;
3685 bodyParser = nullptr;
3686}
3687
3688// XXX: this is an HTTP/1-only operation
3689void
3691{
3692 if (const auto context = pipeline.front()) {
3693 if (context->http)
3694 context->http->al->reply = msg.reply;
3695 }
3696
3697 if (!isOpen()) {
3698 debugs(33, 3, "ignoring 1xx due to earlier closure");
3699 return;
3700 }
3701
3702 // HTTP/1 1xx status messages are only valid when there is a transaction to trigger them
3703 if (!pipeline.empty()) {
3704 HttpReply::Pointer rep(msg.reply);
3705 Must(rep);
3706 // remember the callback
3708
3711
3712 if (!writeControlMsgAndCall(rep.getRaw(), call)) {
3713 // but still inform the caller (so it may resume its operation)
3715 }
3716 return;
3717 }
3718
3719 debugs(33, 3, " closing due to missing context for 1xx");
3721}
3722
3723void
3725{
3727
3728 if (Http::StreamPointer deferredRequest = pipeline.front()) {
3729 debugs(33, 3, clientConnection << ": calling PushDeferredIfNeeded after control msg wrote");
3730 ClientSocketContextPushDeferredIfNeeded(deferredRequest, this);
3731 }
3732}
3733
3735void
3737{
3738 // FwdState might repin a failed connection sooner than this close
3739 // callback is called for the failed connection.
3740 assert(pinning.serverConnection == io.conn);
3741 pinning.closeHandler = nullptr; // Comm unregisters handlers before calling
3742 const bool sawZeroReply = pinning.zeroReply; // reset when unpinning
3743 pinning.serverConnection->noteClosure();
3744 unpinConnection(false);
3745
3746 if (sawZeroReply && clientConnection != nullptr) {
3747 debugs(33, 3, "Closing client connection on pinned zero reply.");
3749 }
3750
3751}
3752
3753void
3755{
3756 pinConnection(pinServer, *request);
3757}
3758
3759void
3761{
3762 Must(pic.connection);
3763 Must(pic.request);
3764 pinConnection(pic.connection, *pic.request);
3765
3766 // monitor pinned server connection for remote-end closures.
3768
3769 if (pipeline.empty())
3770 kick(); // in case clientParseRequests() was blocked by a busy pic.connection
3771}
3772
3774void
3776{
3777 if (Comm::IsConnOpen(pinning.serverConnection) &&
3778 pinning.serverConnection->fd == pinServer->fd) {
3779 debugs(33, 3, "already pinned" << pinServer);
3780 return;
3781 }
3782
3783 unpinConnection(true); // closes pinned connection, if any, and resets fields
3784
3785 pinning.serverConnection = pinServer;
3786
3787 debugs(33, 3, pinning.serverConnection);
3788
3789 Must(pinning.serverConnection != nullptr);
3790
3791 const char *pinnedHost = "[unknown]";
3792 pinning.host = xstrdup(request.url.host());
3793 pinning.port = request.url.port();
3794 pinnedHost = pinning.host;
3795 pinning.pinned = true;
3796 if (CachePeer *aPeer = pinServer->getPeer())
3797 pinning.peer = cbdataReference(aPeer);
3798 pinning.auth = request.flags.connectionAuth;
3799 char stmp[MAX_IPSTRLEN];
3800 char desc[FD_DESC_SZ];
3801 snprintf(desc, FD_DESC_SZ, "%s pinned connection for %s (%d)",
3802 (pinning.auth || !pinning.peer) ? pinnedHost : pinning.peer->name,
3805 fd_note(pinning.serverConnection->fd, desc);
3806
3808 pinning.closeHandler = JobCallback(33, 5,
3810 // remember the pinned connection so that cb does not unpin a fresher one
3811 typedef CommCloseCbParams Params;
3812 Params &params = GetCommParams<Params>(pinning.closeHandler);
3813 params.conn = pinning.serverConnection;
3814 comm_add_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
3815}
3816
3819void
3821{
3822 if (pinning.readHandler != nullptr)
3823 return; // already monitoring
3824
3826 pinning.readHandler = JobCallback(33, 3,
3828 Comm::Read(pinning.serverConnection, pinning.readHandler);
3829}
3830
3831void
3833{
3834 if (pinning.readHandler != nullptr) {
3835 Comm::ReadCancel(pinning.serverConnection->fd, pinning.readHandler);
3836 pinning.readHandler = nullptr;
3837 }
3838}
3839
3840#if USE_OPENSSL
3841bool
3843{
3844 // A ready-for-reading connection means that the TLS server either closed
3845 // the connection, sent us some unexpected HTTP data, or started TLS
3846 // renegotiations. We should close the connection except for the last case.
3847
3848 Must(pinning.serverConnection != nullptr);
3849 auto ssl = fd_table[pinning.serverConnection->fd].ssl.get();
3850 if (!ssl)
3851 return false;
3852
3853 char buf[1];
3854 const int readResult = SSL_read(ssl, buf, sizeof(buf));
3855
3856 if (readResult > 0 || SSL_pending(ssl) > 0) {
3857 debugs(83, 2, pinning.serverConnection << " TLS application data read");
3858 return false;
3859 }
3860
3861 switch(const int error = SSL_get_error(ssl, readResult)) {
3862 case SSL_ERROR_WANT_WRITE:
3863 debugs(83, DBG_IMPORTANT, pinning.serverConnection << " TLS SSL_ERROR_WANT_WRITE request for idle pinned connection");
3864 [[fallthrough]]; // to restart monitoring, for now
3865
3866 case SSL_ERROR_NONE:
3867 case SSL_ERROR_WANT_READ:
3869 return true;
3870
3871 default:
3872 debugs(83, 2, pinning.serverConnection << " TLS error: " << error);
3873 return false;
3874 }
3875
3876 // not reached
3877 return true;
3878}
3879#endif
3880
3883void
3885{
3886 pinning.readHandler = nullptr; // Comm unregisters handlers before calling
3887
3888 if (io.flag == Comm::ERR_CLOSING)
3889 return; // close handler will clean up
3890
3891 Must(pinning.serverConnection == io.conn);
3892
3893#if USE_OPENSSL
3895 return;
3896#endif
3897
3898 const bool clientIsIdle = pipeline.empty();
3899
3900 debugs(33, 3, "idle pinned " << pinning.serverConnection << " read " <<
3901 io.size << (clientIsIdle ? " with idle client" : ""));
3902
3903 pinning.serverConnection->close();
3904
3905 // If we are still sending data to the client, do not close now. When we are done sending,
3906 // ConnStateData::kick() checks pinning.serverConnection and will close.
3907 // However, if we are idle, then we must close to inform the idle client and minimize races.
3908 if (clientIsIdle && clientConnection != nullptr)
3910}
3911
3914{
3915 debugs(33, 7, pinning.serverConnection);
3916 Must(request);
3917
3918 const auto pinningError = [&](const err_type type) {
3919 unpinConnection(true);
3920 HttpRequestPointer requestPointer = request;
3921 return ErrorState::NewForwarding(type, requestPointer, ale);
3922 };
3923
3924 if (!Comm::IsConnOpen(pinning.serverConnection))
3925 throw pinningError(ERR_ZERO_SIZE_OBJECT);
3926
3927 if (pinning.auth && pinning.host && strcasecmp(pinning.host, request->url.host()) != 0)
3928 throw pinningError(ERR_CANNOT_FORWARD); // or generalize ERR_CONFLICT_HOST
3929
3930 if (pinning.port != request->url.port())
3931 throw pinningError(ERR_CANNOT_FORWARD); // or generalize ERR_CONFLICT_HOST
3932
3933 if (pinning.peer && !cbdataReferenceValid(pinning.peer))
3934 throw pinningError(ERR_ZERO_SIZE_OBJECT);
3935
3936 if (pinning.peerAccessDenied)
3937 throw pinningError(ERR_CANNOT_FORWARD); // or generalize ERR_FORWARDING_DENIED
3938
3940 return pinning.serverConnection;
3941}
3942
3945{
3946 if (const auto connManager = request ? request->pinnedConnection() : nullptr)
3947 return connManager->borrowPinnedConnection(request, ale);
3948
3949 // ERR_CANNOT_FORWARD is somewhat misleading here; we can still forward, but
3950 // there is no point since the client connection is now gone
3951 HttpRequestPointer requestPointer = request;
3952 throw ErrorState::NewForwarding(ERR_CANNOT_FORWARD, requestPointer, ale);
3953}
3954
3955void
3957{
3958 debugs(33, 3, pinning.serverConnection);
3959
3960 if (pinning.peer)
3962
3963 if (Comm::IsConnOpen(pinning.serverConnection)) {
3964 if (pinning.closeHandler != nullptr) {
3965 comm_remove_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
3966 pinning.closeHandler = nullptr;
3967 }
3968
3970
3971 // close the server side socket if requested
3972 if (andClose)
3973 pinning.serverConnection->close();
3974 pinning.serverConnection = nullptr;
3975 }
3976
3977 safe_free(pinning.host);
3978
3979 pinning.zeroReply = false;
3980 pinning.peerAccessDenied = false;
3981
3982 /* NOTE: pinning.pinned should be kept. This combined with fd == -1 at the end of a request indicates that the host
3983 * connection has gone away */
3984}
3985
3986void
3988{
3989 auto error = rawError; // (cheap) copy so that we can detail
3990 // We detail even ERR_NONE: There should be no transactions left, and
3991 // detailed ERR_NONE will be unused. Otherwise, this detail helps in triage.
3992 if (!error.detail) {
3993 static const auto d = MakeNamedErrorDetail("WITH_CLIENT");
3994 error.detail = d;
3995 }
3996
3997 debugs(33, 3, pipeline.count() << '/' << pipeline.nrequests << " after " << error);
3998
3999 if (pipeline.empty()) {
4000 bareError.update(error); // XXX: bareLogTagsErrors
4001 } else {
4002 // We terminate the current CONNECT/PUT/etc. context below, logging any
4003 // error details, but that context may leave unparsed bytes behind.
4004 // Consume them to stop checkLogging() from logging them again later.
4005 const auto intputToConsume =
4006#if USE_OPENSSL
4007 parsingTlsHandshake ? "TLS handshake" : // more specific than CONNECT
4008#endif
4009 bodyPipe ? "HTTP request body" :
4010 pipeline.back()->mayUseConnection() ? "HTTP CONNECT" :
4011 nullptr;
4012
4013 while (const auto context = pipeline.front()) {
4014 context->noteIoError(error, lte);
4015 context->finished(); // cleanup and self-deregister
4016 assert(context != pipeline.front());
4017 }
4018
4019 if (intputToConsume && !inBuf.isEmpty()) {
4020 debugs(83, 5, "forgetting client " << intputToConsume << " bytes: " << inBuf.length());
4021 inBuf.clear();
4022 }
4023 }
4024
4026}
4027
4029void
4031{
4032 // to simplify our logic, we assume that terminateAll() has been called
4034
4035 // do not log connections that closed after a transaction (it is normal)
4036 // TODO: access_log needs ACLs to match received-no-bytes connections
4038 return;
4039
4040 /* Create a temporary ClientHttpRequest object. Its destructor will log. */
4041 ClientHttpRequest http(this);
4042 http.req_sz = inBuf.length();
4043 // XXX: Or we died while waiting for the pinned connection to become idle.
4044 http.setErrorUri("error:transaction-end-before-headers");
4045 http.updateError(bareError);
4046}
4047
4048bool
4050{
4051 // PROXY protocol bytes are meant for us and, hence, cannot be tunneled
4053 return false;
4054
4055 // If our decision here is negative, configuration changes are irrelevant.
4056 // Otherwise, clientTunnelOnError() rechecks configuration before tunneling.
4058 return false;
4059
4060 // TODO: Figure out whether/how we can support FTP tunneling.
4061 if (port->transport.protocol == AnyP::PROTO_FTP)
4062 return false;
4063
4064#if USE_OPENSSL
4066 return true;
4067
4068 // the 1st HTTP request on a bumped connection
4070 return true;
4071#endif
4072
4073 // the 1st HTTP(S) request on a connection to an intercepting port
4074 if (!pipeline.nrequests && transparent())
4075 return true;
4076
4077 return false;
4078}
4079
4082{
4083 if (!theNotes)
4084 theNotes = new NotePairs;
4085 return theNotes;
4086}
4087
4088std::ostream &
4089operator <<(std::ostream &os, const ConnStateData::PinnedIdleContext &pic)
4090{
4091 return os << pic.connection << ", request=" << pic.request;
4092}
4093
4094std::ostream &
4095operator <<(std::ostream &os, const ConnStateData::ServerConnectionContext &scc)
4096{
4097 return os << scc.conn_ << ", srv_bytes=" << scc.preReadServerBytes.length();
4098}
4099
void accessLogLog(const AccessLogEntryPointer &, ACLChecklist *)
Definition: access_log.cc:136
#define Assure(condition)
Definition: Assure.h:35
RefCount< AsyncCallT< Dialer > > asyncCall(int aDebugSection, int aDebugLevel, const char *aName, const Dialer &aDialer)
Definition: AsyncCall.h:155
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
CommCbFunPtrCallT< Dialer > * commCbCall(int debugSection, int debugLevel, const char *callName, const Dialer &dialer)
Definition: CommCalls.h:312
void IOACB(const CommAcceptCbParams &params)
Definition: CommCalls.h:31
#define COMM_TRANSPARENT
Definition: Connection.h:50
#define COMM_REUSEPORT
Definition: Connection.h:52
#define COMM_INTERCEPTION
Definition: Connection.h:51
#define COMM_NONBLOCKING
Definition: Connection.h:46
ErrorDetail::Pointer MakeNamedErrorDetail(const char *name)
Definition: Detail.cc:54
#define Here()
source code location of the caller
Definition: Here.h:15
void httpHeaderAddContRange(HttpHeader *, HttpHdrRangeSpec, int64_t)
@ hoReply
Definition: HttpHeader.h:37
void IDCB(const char *ident, void *data)
Definition: Ident.h:17
@ LOG_TCP_OFFLINE_HIT
Definition: LogTags.h:54
@ LOG_TCP_HIT
Definition: LogTags.h:39
@ LOG_TCP_MISS
Definition: LogTags.h:40
@ LOG_TCP_MEM_HIT
Definition: LogTags.h:51
@ LOG_TCP_IMS_HIT
Definition: LogTags.h:47
@ LOG_TAG_NONE
Definition: LogTags.h:38
@ LOG_TCP_REFRESH_UNMODIFIED
Definition: LogTags.h:41
@ LOG_TCP_CLIENT_REFRESH_MISS
Definition: LogTags.h:46
@ LOG_TCP_INM_HIT
Definition: LogTags.h:48
int size
Definition: ModDevPoll.cc:75
int NHttpSockets
Definition: PortCfg.cc:25
int HttpSockets[MAXTCPLISTENPORTS]
Definition: PortCfg.cc:26
AnyP::PortCfgPointer HttpPortList
list of Squid http(s)_port configured
Definition: PortCfg.cc:22
#define MAXTCPLISTENPORTS
Definition: PortCfg.h:87
#define SQUIDSBUFPH
Definition: SBuf.h:31
void SBufToCstring(char *d, const SBuf &s)
Definition: SBuf.h:752
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
class SquidConfig Config
Definition: SquidConfig.cc:12
#define SQUIDSTRINGPH
Definition: SquidString.h:21
#define SQUIDSTRINGPRINT(s)
Definition: SquidString.h:22
StatCounters statCounter
Definition: StatCounters.cc:12
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
#define Must(condition)
Definition: TextException.h:75
int conn
the current server connection FD
Definition: Transport.cc:26
bool urlCheckRequest(const HttpRequest *r)
Definition: Uri.cc:913
void error(char *format,...)
#define acl_access
Definition: forward.h:45
#define assert(EX)
Definition: assert.h:17
int cbdataReferenceValid(const void *p)
Definition: cbdata.cc:265
#define cbdataReferenceDone(var)
Definition: cbdata.h:352
#define cbdataReference(var)
Definition: cbdata.h:343
void banAction(const Acl::Answer &action)
add action to the list of banned actions
Definition: Checklist.cc:395
const Acl::Tree * changeAcl(const Acl::Tree *t)
Definition: Checklist.h:176
Acl::Answer const & fastCheck()
Definition: Checklist.cc:332
void nonBlockingCheck(ACLCB *callback, void *callback_data)
Definition: Checklist.cc:237
void setIdent(const char *userIdentity)
configure rfc931 user identity for the first time
HttpRequest * request
ConnStateData * conn() const
The client connection manager.
void setRequest(HttpRequest *)
configure client request-related fields for the first time
void setConn(ConnStateData *)
set either conn
char rfc931[USER_IDENT_SZ]
Ip::Address src_addr
CbcPointer< Security::CertErrors > sslErrors
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
void syncAle(HttpRequest *adaptedRequest, const char *logUri) const override
assigns uninitialized adapted_request and url ALE components
char * last_meta
image of the last ICAP response header or eCAP meta received
AnyP::PortCfgPointer port
struct timeval start_time
The time the master transaction started.
struct timeval trTime
The response time.
MessageSizes clientReplySz
counters for the response sent to client
HttpRequestMethod method
MessageSizes clientRequestSz
counters for the original request received from client
AnyP::ProtocolVersion version
struct timeval processingTime
total ICAP processing time
HttpReplyPointer reply
class AccessLogEntry::CacheDetails cache
HierarchyLogEntry hier
void syncNotes(HttpRequest *request)
class AccessLogEntry::IcapLogEntry icap
class AccessLogEntry::Headers headers
Comm::ConnectionPointer tcpClient
TCP/IP level details about the client connection.
class AccessLogEntry::HttpDetails http
HttpRequest * adapted_request
HttpRequest * request
class AccessLogEntry::AdaptationDetails adapt
class AccessLogEntry::IcpDetails icp
ProxyProtocol::HeaderPointer proxyProtocolHeader
see ConnStateData::proxyProtocolHeader_
void updateError(const Error &)
sets (or updates the already stored) transaction error as needed
int kind
the matched custom access list verb (or zero)
Definition: Acl.h:170
bool allowed() const
Definition: Acl.h:156
HttpHeader lastMeta
Last received meta header (REQMOD or RESPMOD, whichever comes last).
Definition: History.h:61
unsigned int major
major version number
ProtocolType protocol
which protocol this version is for
unsigned int minor
minor version number
SBuf image() const
Definition: UriScheme.h:57
AnyP::UriScheme const & getScheme() const
Definition: Uri.h:67
SBuf & authority(bool requirePort=false) const
Definition: Uri.cc:642
void setScheme(const AnyP::ProtocolType &p, const char *str)
convert the URL scheme to that given
Definition: Uri.h:70
void path(const char *p)
Definition: Uri.h:101
void port(const Port p)
reset authority port subcomponent
Definition: Uri.h:95
void host(const char *src)
Definition: Uri.cc:100
SBuf hostOrIp() const
Definition: Uri.cc:115
a smart AsyncCall pointer for delivery of future results
static void Start(const Pointer &job)
Definition: AsyncJob.cc:24
virtual void start()
called by AsyncStart; do not call directly
Definition: AsyncJob.cc:44
void mustStop(const char *aReason)
Definition: AsyncJob.cc:70
void deleteThis(const char *aReason)
Definition: AsyncJob.cc:50
virtual void callException(const std::exception &e)
called when the job throws during an async call
Definition: AsyncJob.cc:128
virtual void releaseAuthServer()
Definition: UserRequest.cc:205
MemBuf & buf
Definition: BodyPipe.h:74
uint64_t producedSize() const
Definition: BodyPipe.h:112
void expectNoConsumption()
there will be no more setConsumer() calls
Definition: BodyPipe.cc:267
size_t putMoreData(const char *buf, size_t size)
Definition: BodyPipe.cc:213
const MemBuf & buf() const
Definition: BodyPipe.h:137
bool bodySizeKnown() const
Definition: BodyPipe.h:109
uint64_t unproducedSize() const
Definition: BodyPipe.cc:179
void setBodySize(uint64_t aSize)
Definition: BodyPipe.cc:147
const char * status() const
Definition: BodyPipe.cc:446
bool productionEnded() const
Definition: BodyPipe.h:113
bool mayNeedMoreData() const
Definition: BodyPipe.h:118
uint64_t bodySize() const
Definition: BodyPipe.cc:161
void enableAutoConsumption()
start or continue consuming when producing without consumer
Definition: BodyPipe.cc:316
void stopProducingFor(RefCount< BodyPipe > &, bool atEof)
Definition: BodyPipe.cc:107
optimized set of C chars, with quick membership test and merge support
Definition: CharacterSet.h:18
static const CharacterSet DIGIT
Definition: CharacterSet.h:84
static const CharacterSet ALPHA
Definition: CharacterSet.h:76
static const CharacterSet HEXDIG
Definition: CharacterSet.h:88
unsigned short initial
std::vector< ClientDelayPool::Pointer > pools
static ClientDelayPools * Instance()
struct ClientHttpRequest::Out out
void clearRequest()
resets the current request and log_uri to nil
HttpRequest *const request
int64_t mRangeCLen() const
Definition: client_side.cc:764
ConnStateData * getConn() const
String rangeBoundaryStr() const
Definition: client_side.cc:804
void initRequest(HttpRequest *)
void setLogUriToRequestUri()
sets log_uri when we know the current request
void updateError(const Error &)
if necessary, stores new error information (if any)
MemObject * memObject() const
size_t req_sz
raw request size on input, not current request size
void setErrorUri(const char *)
HttpHdrRangeIter range_iter
struct ClientHttpRequest::Flags flags
bool multipartRangeRequest() const
Definition: client_side.cc:720
StoreEntry * storeEntry() const
ClientRequestContext * calloutContext
const LogTags & loggingTags() const
the processing tags associated with this request transaction.
const AccessLogEntry::Pointer al
access.log entry
StoreEntry * loggingEntry() const
struct ClientHttpRequest::Redirect redirect
void setWriteLimiter(const int aWriteSpeedLimit, const double anInitialBurst, const double aHighWatermark)
Definition: comm.cc:1376
Definition: ClpMap.h:41
const Value * get(const Key &)
Definition: ClpMap.h:188
void del(const Key &)
Remove the corresponding entry (if any)
Definition: ClpMap.h:268
bool add(const Key &, const Value &, Ttl)
Definition: ClpMap.h:220
static const Pointer & Current()
Definition: CodeContext.cc:33
static void Reset()
forgets the current context, setting it to nil/unknown
Definition: CodeContext.cc:77
AnyP::PortCfgPointer port
the configuration listening port this call relates to (may be nil)
Definition: CommCalls.h:100
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition: CommCalls.h:83
int fd
FD which the call was about. Set by the async call creator.
Definition: CommCalls.h:85
Comm::Flag flag
comm layer result status.
Definition: CommCalls.h:82
Comm::ConnectionPointer conn
Definition: CommCalls.h:80
time_t timeLeft(const time_t idleTimeout) const
Definition: Connection.cc:146
CachePeer * getPeer() const
Definition: Connection.cc:124
Ip::Address remote
Definition: Connection.h:149
char rfc931[USER_IDENT_SZ]
Definition: Connection.h:176
Ip::Address local
Definition: Connection.h:146
Security::NegotiationHistory * tlsNegotiations()
Definition: Connection.cc:156
parameters for the async notePinnedConnectionBecameIdle() call
Definition: client_side.h:184
Comm::ConnectionPointer connection
to-server connection to be pinned
Definition: client_side.h:188
HttpRequest::Pointer request
to-server request that initiated serverConnection
Definition: client_side.h:189
noteTakeServerConnectionControl() callback parameter
Definition: client_side.h:214
Comm::ConnectionPointer conn_
to-server connection
Definition: client_side.h:225
SBuf preReadServerBytes
post-101 bytes received from the server
Definition: client_side.h:221
virtual int pipelinePrefetchMax() const
returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
void postHttpsAccept()
the second part of old httpsAccept, waiting for future HttpsServer home
Ssl::ServerBump * serverBump()
Definition: client_side.h:286
void endingShutdown() override
bool fakeAConnectRequest(const char *reason, const SBuf &payload)
bool switchedToHttps() const
Definition: client_side.h:285
void readNextRequest()
Definition: client_side.cc:881
virtual void clientPinnedConnectionClosed(const CommCloseCbParams &io)
Our close handler called by Comm when the pinned connection is closed.
char * prepareTlsSwitchingURL(const Http1::RequestParserPointer &hp)
Security::KeyLogger keyLogger
managers logging of the being-accepted TLS connection secrets
Definition: client_side.h:385
void afterClientRead() override
processing to be done after a Comm::Read()
Ssl::ServerBump * sslServerBump
HTTPS server cert. fetching state for bump-ssl-server-first.
Definition: client_side.h:494
Http::Stream * parseHttpRequest(const Http1::RequestParserPointer &)
virtual bool writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call)=0
handle a control message received by context from a peer and call back
void switchToHttps(ClientHttpRequest *, Ssl::BumpMode bumpServerMode)
void startPinnedConnectionMonitoring()
const char * stoppedReceiving() const
true if we stopped receiving the request
Definition: client_side.h:160
void consumeInput(const size_t byteCount)
remove no longer needed leading bytes from the input buffer
void clientAfterReadingRequests()
ClientHttpRequest * buildFakeRequest(SBuf &useHost, AnyP::KnownPort usePort, const SBuf &payload)
build a fake http request
virtual void handleReply(HttpReply *header, StoreIOBuffer receivedData)=0
void setAuth(const Auth::UserRequest::Pointer &aur, const char *cause)
Definition: client_side.cc:518
virtual Http::Stream * parseOneRequest()=0
BodyPipe::Pointer bodyPipe
set when we are reading request body
Definition: client_side.h:432
const SBuf & tlsClientSni() const
Definition: client_side.h:295
void sendControlMsg(HttpControlMsg) override
called to send the 1xx message and notify the Source
void kick()
try to make progress on a transaction or read more I/O
Definition: client_side.cc:918
const Auth::UserRequest::Pointer & getAuth() const
Definition: client_side.h:124
void getSslContextStart()
Start to create dynamic Security::ContextPointer for host or uses static port SSL context.
void httpsPeeked(PinnedIdleContext pic)
called by FwdState when it is done bumping the server
bool initiateTunneledRequest(HttpRequest::Pointer const &cause, const char *reason, const SBuf &payload)
generates and sends to tunnel.cc a fake request with a given payload
void swanSong() override
Definition: client_side.cc:615
bool splice()
Splice a bumped client connection on peek-and-splice mode.
void start() override
called by AsyncStart; do not call directly
SBuf tlsClientSni_
TLS client delivered SNI value. Empty string if none has been received.
Definition: client_side.h:490
Error bareError
a problem that occurred without a request (e.g., while parsing headers)
Definition: client_side.h:382
bool handleReadData() override
void expectNoForwarding()
cleans up virgin request [body] forwarding state
Http::Stream * abortRequestParsing(const char *const errUri)
stop parsing the request and create context for relaying error info
void startShutdown() override
void sslCrtdHandleReply(const Helper::Reply &reply)
Process response from ssl_crtd.
err_type handleChunkedRequestBody()
parses available chunked encoded body bytes, checks size, returns errors
SBuf sslCommonName_
CN name for SSL certificate generation.
Definition: client_side.h:487
void resetSslCommonName(const char *name)
Definition: client_side.h:294
void afterClientWrite(size_t) override
processing to sync state after a Comm::Write()
void doneWithControlMsg() override
void terminateAll(const Error &, const LogTagsErrors &) override
abort any pending transactions and prevent new ones (by closing)
void abortChunkedRequestBody(const err_type error)
quit on errors related to chunked request body handling
void pinConnection(const Comm::ConnectionPointer &pinServerConn, const HttpRequest &request)
Forward future client requests using the given server connection.
bool preservingClientData_
whether preservedClientData is valid and should be kept up to date
Definition: client_side.h:435
void callException(const std::exception &) override
called when the job throws during an async call
Definition: client_side.cc:639
static void sslCrtdHandleReplyWrapper(void *data, const Helper::Reply &reply)
Callback function. It is called when squid receive message from ssl_crtd.
void lifetimeTimeout(const CommTimeoutCbParams &params)
Ssl::CertSignAlgorithm signAlgorithm
The signing algorithm to use.
Definition: client_side.h:495
Security::ContextPointer getTlsContextFromCache(const SBuf &cacheKey, const Ssl::CertificateProperties &certProperties)
AnyP::Port tlsConnectPort
The TLS server port number as passed in the CONNECT request.
Definition: client_side.h:486
Auth::UserRequest::Pointer auth_
some user details that can be used to perform authentication on this connection
Definition: client_side.h:474
Comm::ConnectionPointer borrowPinnedConnection(HttpRequest *, const AccessLogEntryPointer &)
ConnStateData-specific part of BorrowPinnedConnection()
struct ConnStateData::@37 flags
bool shouldPreserveClientData() const
void parseTlsHandshake()
void notePinnedConnectionBecameIdle(PinnedIdleContext pic)
Called when a pinned connection becomes available for forwarding the next request.
void stopPinnedConnectionMonitoring()
The caller assumes responsibility for connection closure detection.
void pinBusyConnection(const Comm::ConnectionPointer &pinServerConn, const HttpRequest::Pointer &request)
bool handleIdleClientPinnedTlsRead()
bool proxyProtocolError(const char *reason)
bool serveDelayedError(Http::Stream *)
SBuf preservedClientData
Definition: client_side.h:350
const char * stoppedSending_
the reason why we no longer write the response or nil
Definition: client_side.h:499
void resetReadTimeout(time_t timeout)
(re)sets timeout for receiving more bytes from the client
Definition: client_side.cc:598
void quitAfterError(HttpRequest *request)
SBuf tlsConnectHostOrIp
The TLS server host name appears in CONNECT request or the server ip address for the intercepted requ...
Definition: client_side.h:485
~ConnStateData() override
Definition: client_side.cc:671
void receivedFirstByte() override
Update flags and timeout after the first byte received.
uint64_t parsedBumpedRequestCount
The number of parsed HTTP requests headers on a bumped client connection.
Definition: client_side.h:481
Security::IoResult acceptTls()
bool clientParseRequests()
Traffic parsing.
BodyPipe::Pointer expectRequestBody(int64_t size)