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