Squid Web Cache master
Loading...
Searching...
No Matches
client_side.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 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/Host.h"
63#include "anyp/PortCfg.h"
64#include "base/AsyncCallbacks.h"
65#include "base/Subscription.h"
66#include "base/TextException.h"
67#include "CachePeer.h"
68#include "client_db.h"
69#include "client_side.h"
70#include "client_side_reply.h"
71#include "client_side_request.h"
73#include "clientStream.h"
74#include "comm.h"
75#include "comm/Connection.h"
76#include "comm/Loops.h"
77#include "comm/Read.h"
78#include "comm/TcpAcceptor.h"
79#include "comm/Write.h"
80#include "CommCalls.h"
81#include "compat/socket.h"
82#include "debug/Messages.h"
84#include "errorpage.h"
85#include "fd.h"
86#include "fde.h"
87#include "fqdncache.h"
88#include "FwdState.h"
89#include "globals.h"
90#include "helper.h"
91#include "helper/Reply.h"
92#include "http.h"
95#include "http/Stream.h"
96#include "HttpHdrContRange.h"
97#include "HttpReply.h"
98#include "HttpRequest.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
148// TODO: Remove this custom dialer and simplify by creating the TcpAcceptor
149// subscription later, inside clientListenerConnectionOpened() callback, just
150// like htcpOpenPorts(), icpOpenPorts(), and snmpPortOpened() do it.
153 public CallDialer,
154 public WithAnswer<Ipc::StartListeningAnswer>
155{
156public:
159 handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}
160
161 /* CallDialer API */
162 void print(std::ostream &os) const override {
163 os << '(' << answer_ << ", " << FdNote(portTypeNote) << " port=" << (void*)&portCfg << ')';
164 }
165
166 virtual bool canDial(AsyncCall &) const { return true; }
167 virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }
168
169 /* WithAnswer API */
171
172public:
174
175private:
176 // answer_.conn (set/updated by IPC code) is portCfg.listenConn (used by us)
181};
182
183static void clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub);
184
186static int clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength);
187
188static void clientUpdateStatHistCounters(const LogTags &logType, int svc_time);
189static void clientUpdateStatCounters(const LogTags &logType);
191static bool clientPingHasFinished(ping_data const *aPing);
194
195char *skipLeadingSpace(char *aString);
196
197void
199{
201
202 if (logType.isTcpHit())
204
205 if (logType.oldType == LOG_TCP_HIT)
207 else if (logType.oldType == LOG_TCP_MEM_HIT)
209}
210
211void
212clientUpdateStatHistCounters(const LogTags &logType, int svc_time)
213{
222 switch (logType.oldType) {
226 break;
227
228 case LOG_TCP_INM_HIT:
229 case LOG_TCP_IMS_HIT:
231 break;
232
233 case LOG_TCP_HIT:
234
235 case LOG_TCP_MEM_HIT:
236
239 break;
240
241 case LOG_TCP_MISS:
242
245 break;
246
247 default:
248 /* make compiler warnings go away */
249 break;
250 }
251}
252
253bool
255{
256 if (0 != aPing->stop.tv_sec && 0 != aPing->start.tv_sec)
257 return true;
258
259 return false;
260}
261
262void
264{
265 ping_data *i;
266
267 switch (someEntry->code) {
268#if USE_CACHE_DIGESTS
269
270 case CD_PARENT_HIT:
271
272 case CD_SIBLING_HIT:
274 break;
275#endif
276
277 case SIBLING_HIT:
278
279 case PARENT_HIT:
280
282
285 i = &someEntry->ping;
286
289
290 if (i->timeout)
292
293 break;
294
295 case CLOSEST_PARENT:
296
297 case CLOSEST_DIRECT:
299
300 break;
301
302 default:
303 break;
304 }
305}
306
307void
320
321void
323{
324 assert(request);
325 assert(aLogEntry != nullptr);
326
328 MemBuf mb;
329 mb.init();
330 request->header.packInto(&mb);
331 //This is the request after adaptation or redirection
332 aLogEntry->headers.adapted_request = xstrdup(mb.buf);
333
334 // the virgin request is saved to aLogEntry->request
335 if (aLogEntry->request) {
336 mb.reset();
337 aLogEntry->request->header.packInto(&mb);
338 aLogEntry->headers.request = xstrdup(mb.buf);
339 }
340
341#if USE_ADAPTATION
342 const Adaptation::History::Pointer ah = request->adaptLogHistory();
343 if (ah != nullptr) {
344 mb.reset();
345 ah->lastMeta.packInto(&mb);
346 aLogEntry->adapt.last_meta = xstrdup(mb.buf);
347 }
348#endif
349
350 mb.clean();
351 }
352
353#if ICAP_CLIENT
355 if (ih != nullptr)
356 ih->processingTime(aLogEntry->icap.processingTime);
357#endif
358
359 aLogEntry->http.method = request->method;
360 aLogEntry->http.version = request->http_ver;
361 aLogEntry->hier = request->hier;
362 aLogEntry->cache.extuser = request->extacl_user.termedBuf();
363
364 // Adapted request, if any, inherits and then collects all the stats, but
365 // the virgin request gets logged instead; copy the stats to log them.
366 // TODO: avoid losses by keeping these stats in a shared history object?
367 if (aLogEntry->request) {
368 aLogEntry->request->dnsWait = request->dnsWait;
369 aLogEntry->request->error = request->error;
370 }
371}
372
373void
375{
376 if (!out.size && loggingTags().oldType == LOG_TAG_NONE)
377 debugs(33, 5, "logging half-baked transaction: " << log_uri);
378
380 al->url = log_uri;
381 debugs(33, 9, "clientLogRequest: al.url='" << al->url << "'");
382
383 const auto findReply = [this]() -> const HttpReply * {
384 if (al->reply)
385 return al->reply.getRaw();
386 if (const auto le = loggingEntry())
387 return le->hasFreshestReply();
388 return nullptr;
389 };
390 if (const auto reply = findReply()) {
391 al->http.code = reply->sline.status();
392 al->http.content_type = reply->content_type.termedBuf();
393 }
394
395 debugs(33, 9, "clientLogRequest: http.code='" << al->http.code << "'");
396
397 if (loggingEntry() && loggingEntry()->mem_obj && loggingEntry()->objectLen() >= 0)
398 al->cache.objectSize = loggingEntry()->contentLen(); // payload duplicate ?? with or without TE ?
399
401 // the virgin request is saved to al->request
402 if (al->request && al->request->body_pipe)
405 // XXX: calculate without payload encoding or headers !!
406 al->http.clientReplySz.payloadData = out.size - out.headers_sz; // pretend its all un-encoded data for now.
407
409
411
412 if (request)
414
415#if USE_OPENSSL && 0
416
417 /* This is broken. Fails if the connection has been closed. Needs
418 * to snarf the ssl details some place earlier..
419 */
420 if (getConn() != NULL)
422
423#endif
424
425 if (request) {
426 SBuf matched;
427 for (auto h: Config.notes) {
428 if (h->match(request, al->reply.getRaw(), al, matched)) {
429 request->notes()->add(h->key(), matched);
430 debugs(33, 3, h->key() << " " << matched);
431 }
432 }
433 // The al->notes and request->notes must point to the same object.
435
439 }
440
441 ACLFilledChecklist checklist(nullptr, request);
442 checklist.updateAle(al);
443 // no need checklist.syncAle(): already synced
444 accessLogLog(al, &checklist);
445
446 bool updatePerformanceCounters = true;
449 statsCheck.updateAle(al);
450 updatePerformanceCounters = statsCheck.fastCheck().allowed();
451 }
452
453 if (updatePerformanceCounters) {
454 if (request)
456
457 if (getConn() != nullptr && getConn()->clientConnection != nullptr)
458 clientdbUpdate(getConn()->clientConnection->remote, loggingTags(), AnyP::PROTO_HTTP, out.size);
459 }
460}
461
462void
473
474void
476{
477 ClientHttpRequest *http = (ClientHttpRequest *)data;
478 assert(http != nullptr);
479 delete http;
480}
481
482/* This is a handler normally called by comm_close() */
484{
485 if (clientConnection) {
487 // keep closed clientConnection for logging, clientdb cleanup, etc.
488 }
489 deleteThis("ConnStateData::connStateClosed");
490}
491
492#if USE_AUTH
493void
495{
496 if (auth_ == nullptr) {
497 if (aur != nullptr) {
498 debugs(33, 2, "Adding connection-auth to " << clientConnection << " from " << by);
499 auth_ = aur;
500 }
501 return;
502 }
503
504 // clobered with self-pointer
505 // NP: something nasty is going on in Squid, but harmless.
506 if (aur == auth_) {
507 debugs(33, 2, "WARNING: Ignoring duplicate connection-auth for " << clientConnection << " from " << by);
508 return;
509 }
510
511 /*
512 * Connection-auth relies on a single set of credentials being preserved
513 * for all requests on a connection once they have been setup.
514 * There are several things which need to happen to preserve security
515 * when connection-auth credentials change unexpectedly or are unset.
516 *
517 * 1) auth helper released from any active state
518 *
519 * They can only be reserved by a handshake process which this
520 * connection can now never complete.
521 * This prevents helpers hanging when their connections close.
522 *
523 * 2) pinning is expected to be removed and server conn closed
524 *
525 * The upstream link is authenticated with the same credentials.
526 * Expecting the same level of consistency we should have received.
527 * This prevents upstream being faced with multiple or missing
528 * credentials after authentication.
529 * NP: un-pin is left to the cleanup in ConnStateData::swanSong()
530 * we just trigger that cleanup here via comm_reset_close() or
531 * ConnStateData::stopReceiving()
532 *
533 * 3) the connection needs to close.
534 *
535 * This prevents attackers injecting requests into a connection,
536 * or gateways wrongly multiplexing users into a single connection.
537 *
538 * When credentials are missing closure needs to follow an auth
539 * challenge for best recovery by the client.
540 *
541 * When credentials change there is nothing we can do but abort as
542 * fast as possible. Sending TCP RST instead of an HTTP response
543 * is the best-case action.
544 */
545
546 // clobbered with nul-pointer
547 if (aur == nullptr) {
548 debugs(33, 2, "WARNING: Graceful closure on " << clientConnection << " due to connection-auth erase from " << by);
550 auth_ = nullptr;
551 // XXX: need to test whether the connection re-auth challenge is sent. If not, how to trigger it from here.
552 // NP: the current situation seems to fix challenge loops in Safari without visible issues in others.
553 // we stop receiving more traffic but can leave the Job running to terminate after the error or challenge is delivered.
554 stopReceiving("connection-auth removed");
555 return;
556 }
557
558 // clobbered with alternative credentials
559 if (aur != auth_) {
560 debugs(33, 2, "ERROR: Closing " << clientConnection << " due to change of connection-auth from " << by);
562 auth_ = nullptr;
563 // this is a fatal type of problem.
564 // Close the connection immediately with TCP RST to abort all traffic flow
566 return;
567 }
568
569 /* NOT REACHABLE */
570}
571#endif
572
573void
575{
577 AsyncCall::Pointer callback = JobCallback(33, 5, TimeoutDialer, this, ConnStateData::requestTimeout);
578 commSetConnTimeout(clientConnection, timeout, callback);
579}
580
581void
588
589// cleans up before destructor is called
590void
592{
593 debugs(33, 2, clientConnection);
594
595 flags.readMore = false;
596 clientdbEstablished(clientConnection->remote, -1); /* decrement */
597
599 checkLogging();
600
601 // XXX: Closing pinned conn is too harsh: The Client may want to continue!
602 unpinConnection(true);
603
605
606#if USE_AUTH
607 // NP: do this bit after closing the connections to avoid side effects from unwanted TCP RST
608 setAuth(nullptr, "ConnStateData::SwanSong cleanup");
609#endif
610
611 flags.swanSang = true;
612}
613
614void
615ConnStateData::callException(const std::exception &ex)
616{
617 Server::callException(ex); // logs ex and stops the job
618
619 ErrorDetail::Pointer errorDetail;
620 if (const auto tex = dynamic_cast<const TextException*>(&ex))
621 errorDetail = new ExceptionErrorDetail(tex->id());
622 else
623 errorDetail = new ExceptionErrorDetail(Here().id());
624 updateError(ERR_GATEWAY_FAILURE, errorDetail);
625}
626
627void
629{
630 if (const auto context = pipeline.front()) {
631 const auto http = context->http;
632 assert(http);
633 http->updateError(error);
634 } else {
636 }
637}
638
639bool
641{
642 return cbdataReferenceValid(this) && // XXX: checking "this" in a method
644 !fd_table[clientConnection->fd].closing();
645}
646
648{
649 debugs(33, 3, clientConnection);
650
651 if (isOpen())
652 debugs(33, DBG_IMPORTANT, "ERROR: Squid BUG: ConnStateData did not close " << clientConnection);
653
654 if (!flags.swanSang)
655 debugs(33, DBG_IMPORTANT, "ERROR: Squid BUG: ConnStateData was not destroyed properly; " << clientConnection);
656
657 if (bodyPipe != nullptr)
659
660 delete bodyParser; // TODO: pool
661
662#if USE_OPENSSL
663 delete sslServerBump;
664#endif
665}
666
673void
675{
676 HttpRequest *request = http->request;
677
678 debugs(33, 3, "http_ver = " << request->http_ver);
679 debugs(33, 3, "method = " << request->method);
680
681 // TODO: move to HttpRequest::hdrCacheInit, just like HttpReply.
682 request->flags.proxyKeepalive = request->persistent();
683}
684
685int
687{
689 bodyLength > Config.maxRequestBodySize)
690 return 1; /* too large */
691
692 return 0;
693}
694
695bool
700
701void
703{
704 mb->appendf("\r\n--" SQUIDSTRINGPH "--\r\n", SQUIDSTRINGPRINT(boundary));
705 debugs(33, 6, "buf offset: " << mb->size);
706}
707
708void
709clientPackRangeHdr(const HttpReplyPointer &rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb)
710{
711 HttpHeader hdr(hoReply);
712 assert(rep);
713 assert(spec);
714
715 /* put boundary */
716 debugs(33, 5, "appending boundary: " << boundary);
717 /* rfc2046 requires to _prepend_ boundary with <crlf>! */
718 mb->appendf("\r\n--" SQUIDSTRINGPH "\r\n", SQUIDSTRINGPRINT(boundary));
719
720 /* stuff the header with required entries and pack it */
721
724
725 httpHeaderAddContRange(&hdr, *spec, rep->content_length);
726
727 hdr.packInto(mb);
728 hdr.clean();
729
730 /* append <crlf> (we packed a header, not a reply) */
731 mb->append("\r\n", 2);
732}
733
739int64_t
741{
742 int64_t clen = 0;
743 MemBuf mb;
744
745 assert(memObject());
746
747 mb.init();
749
750 while (pos != request->range->end()) {
751 /* account for headers for this range */
752 mb.reset();
753 clientPackRangeHdr(&storeEntry()->mem().freshestReply(),
754 *pos, range_iter.boundary, &mb);
755 clen += mb.size;
756
757 /* account for range content */
758 clen += (*pos)->length;
759
760 debugs(33, 6, "clientMRangeCLen: (clen += " << mb.size << " + " << (*pos)->length << ") == " << clen);
761 ++pos;
762 }
763
764 /* account for the terminating boundary */
765 mb.reset();
766
768
769 clen += mb.size;
770
771 mb.clean();
772
773 return clen;
774}
775
779String
781{
782 const char *key;
784 b.append(":",1);
785 key = storeEntry()->getMD5Text();
786 b.append(key, strlen(key));
787 return b;
788}
789
799void
801 HttpReply * rep, StoreIOBuffer receivedData)
802{
803 // do not try to deliver if client already ABORTED
804 if (!http->getConn() || !cbdataReferenceValid(http->getConn()) || !Comm::IsConnOpen(http->getConn()->clientConnection))
805 return;
806
807 /* Test preconditions */
808 assert(node != nullptr);
809 /* TODO: handle this rather than asserting
810 * - it should only ever happen if we cause an abort and
811 * the callback chain loops back to here, so we can simply return.
812 * However, that itself shouldn't happen, so it stays as an assert for now.
813 */
815 assert(node->node.next == nullptr);
816 Http::StreamPointer context = dynamic_cast<Http::Stream *>(node->data.getRaw());
817 assert(context != nullptr);
818
819 /* TODO: check offset is what we asked for */
820
821 // TODO: enforces HTTP/1 MUST on pipeline order, but is irrelevant to HTTP/2
822 if (context != http->getConn()->pipeline.front())
823 context->deferRecipientForLater(node, rep, receivedData);
824 else if (http->getConn()->cbControlMsgSent) // 1xx to the user is pending
825 context->deferRecipientForLater(node, rep, receivedData);
826 else
827 http->getConn()->handleReply(rep, receivedData);
828}
829
835void
837{
838 /* Test preconditions */
839 assert(node != nullptr);
840 /* TODO: handle this rather than asserting
841 * - it should only ever happen if we cause an abort and
842 * the callback chain loops back to here, so we can simply return.
843 * However, that itself shouldn't happen, so it stays as an assert for now.
844 */
846 /* Set null by ContextFree */
847 assert(node->node.next == nullptr);
848 /* this is the assert discussed above */
849 assert(nullptr == dynamic_cast<Http::Stream *>(node->data.getRaw()));
850 /* We are only called when the client socket shutsdown.
851 * Tell the prev pipeline member we're finished
852 */
854}
855
856void
858{
859 debugs(33, 5, clientConnection << " reading next req");
860
861 fd_note(clientConnection->fd, "Idle client: Waiting for next request");
866
867 readSomeData();
869}
870
871static void
873{
874 debugs(33, 2, conn->clientConnection << " Sending next");
875
878 if (deferredRequest->flags.deferred) {
880 assert(deferredRequest->http->out.size == 0);
882 clientSocketRecipient(deferredRequest->deferredparams.node,
883 deferredRequest->http,
884 deferredRequest->deferredparams.rep,
885 deferredRequest->deferredparams.queuedBuffer);
886 }
887
891}
892
893void
895{
897 debugs(33, 2, clientConnection << " Connection was closed");
898 return;
899 }
900
901 if (pinning.pinned && !Comm::IsConnOpen(pinning.serverConnection)) {
902 debugs(33, 2, clientConnection << " Connection was pinned but server side gone. Terminating client connection");
904 return;
905 }
906
921 if (const char *reason = stoppedReceiving()) {
922 debugs(33, 3, "closing for earlier request error: " << reason);
924 return;
925 }
926
934
935 if (!isOpen())
936 return;
937
945 Http::StreamPointer deferredRequest = pipeline.front();
946 if (deferredRequest != nullptr) {
947 debugs(33, 3, clientConnection << ": calling PushDeferredIfNeeded");
948 ClientSocketContextPushDeferredIfNeeded(deferredRequest, this);
949 } else if (flags.readMore) {
950 debugs(33, 3, clientConnection << ": calling readNextRequest()");
952 } else {
953 // XXX: Can this happen? CONNECT tunnels have deferredRequest set.
954 debugs(33, DBG_IMPORTANT, MYNAME << "abandoning " << clientConnection);
955 }
956}
957
958void
960{
961 debugs(33, 4, "sending error (" << clientConnection << "): " << error <<
962 "; old receiving error: " <<
963 (stoppedReceiving() ? stoppedReceiving_ : "none"));
964
965 if (const char *oldError = stoppedSending()) {
966 debugs(33, 3, "already stopped sending: " << oldError);
967 return; // nothing has changed as far as this connection is concerned
968 }
970
971 if (!stoppedReceiving()) {
972 if (const int64_t expecting = mayNeedToReadMoreBody()) {
973 debugs(33, 5, "must still read " << expecting <<
974 " request body bytes with " << inBuf.length() << " unused");
975 return; // wait for the request receiver to finish reading
976 }
977 }
978
980}
981
982void
984{
985 if (pipeline.empty())
986 return;
987
988 auto ctx = pipeline.front();
989 if (size) {
991 if (ctx->http->loggingTags().isTcpHit())
993 }
994 ctx->writeComplete(size);
995}
996
999{
1000 ClientHttpRequest *http = new ClientHttpRequest(this);
1001 http->req_sz = inBuf.length();
1002 http->setErrorUri(uri);
1003 auto *context = new Http::Stream(clientConnection, http);
1004 StoreIOBuffer tempBuffer;
1005 tempBuffer.data = context->reqbuf;
1006 tempBuffer.length = HTTP_REQBUF_SZ;
1009 clientSocketDetach, context, tempBuffer);
1010 return context;
1011}
1012
1013void
1015{
1016 // RegisteredRunner API callback - Squid has been shut down
1017
1018 // if connection is idle terminate it now,
1019 // otherwise wait for grace period to end
1020 if (pipeline.empty())
1022}
1023
1024void
1026{
1027 // RegisteredRunner API callback - Squid shutdown grace period is over
1028
1029 // force the client connection to close immediately
1030 // swanSong() in the close handler will cleanup.
1033}
1034
1035char *
1036skipLeadingSpace(char *aString)
1037{
1038 char *result = aString;
1039
1040 while (xisspace(*aString))
1041 ++aString;
1042
1043 return result;
1044}
1045
1051const char *
1052findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end)
1053{
1054 if (nullptr == end) {
1055 end = uriAndHTTPVersion + strcspn(uriAndHTTPVersion, "\r\n");
1056 assert(end);
1057 }
1058
1059 for (; end > uriAndHTTPVersion; --end) {
1060 if (*end == '\n' || *end == '\r')
1061 continue;
1062
1063 if (xisspace(*end)) {
1064 if (strncasecmp(end + 1, "HTTP/", 5) == 0)
1065 return end + 1;
1066 else
1067 break;
1068 }
1069 }
1070
1071 return nullptr;
1072}
1073
1074static char *
1076{
1077 int vhost = conn->port->vhost;
1078 int vport = conn->port->vport;
1079 static char ipbuf[MAX_IPSTRLEN];
1080
1081 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
1082
1083 // XXX: reuse proper URL parser for this
1084 SBuf url = hp->requestUri(); // use full provided URI if we abort
1085 do { // use a loop so we can break out of it
1087 if (tok.skip('/')) // origin-form URL already.
1088 break;
1089
1090 if (conn->port->vhost)
1091 return nullptr; /* already in good shape */
1092
1093 // skip the URI scheme
1094 static const CharacterSet uriScheme = CharacterSet("URI-scheme","+-.") + CharacterSet::ALPHA + CharacterSet::DIGIT;
1095 static const SBuf uriSchemeEnd("://");
1096 if (!tok.skipAll(uriScheme) || !tok.skip(uriSchemeEnd))
1097 break;
1098
1099 // skip the authority segment
1100 // RFC 3986 complex nested ABNF for "authority" boils down to this:
1101 static const CharacterSet authority = CharacterSet("authority","-._~%:@[]!$&'()*+,;=") +
1103 if (!tok.skipAll(authority))
1104 break;
1105
1106 static const SBuf slashUri("/");
1107 const SBuf t = tok.remaining();
1108 if (t.isEmpty())
1109 url = slashUri;
1110 else if (t[0]=='/') // looks like path
1111 url = t;
1112 else if (t[0]=='?' || t[0]=='#') { // looks like query or fragment. fix '/'
1113 url = slashUri;
1114 url.append(t);
1115 } // else do nothing. invalid path
1116
1117 } while(false);
1118
1119#if SHOULD_REJECT_UNKNOWN_URLS
1120 // reject URI which are not well-formed even after the processing above
1121 if (url.isEmpty() || url[0] != '/') {
1122 hp->parseStatusCode = Http::scBadRequest;
1123 return conn->abortRequestParsing("error:invalid-request");
1124 }
1125#endif
1126
1127 if (vport < 0)
1128 vport = conn->clientConnection->local.port();
1129
1130 char *receivedHost = nullptr;
1131 if (vhost && (receivedHost = hp->getHostHeaderField())) {
1132 SBuf host(receivedHost);
1133 debugs(33, 5, "ACCEL VHOST REWRITE: vhost=" << host << " + vport=" << vport);
1134 if (vport > 0) {
1135 // remove existing :port (if any), cope with IPv6+ without port
1136 const auto lastColonPos = host.rfind(':');
1137 if (lastColonPos != SBuf::npos && *host.rbegin() != ']') {
1138 host.chop(0, lastColonPos); // truncate until the last colon
1139 }
1140 host.appendf(":%d", vport);
1141 } // else nothing to alter port-wise.
1142 const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
1143 const auto url_sz = scheme.length() + host.length() + url.length() + 32;
1144 char *uri = static_cast<char *>(xcalloc(url_sz, 1));
1145 snprintf(uri, url_sz, SQUIDSBUFPH "://" SQUIDSBUFPH SQUIDSBUFPH, SQUIDSBUFPRINT(scheme), SQUIDSBUFPRINT(host), SQUIDSBUFPRINT(url));
1146 debugs(33, 5, "ACCEL VHOST REWRITE: " << uri);
1147 return uri;
1148 } else if (conn->port->defaultsite /* && !vhost */) {
1149 debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: defaultsite=" << conn->port->defaultsite << " + vport=" << vport);
1150 char vportStr[32];
1151 vportStr[0] = '\0';
1152 if (vport > 0) {
1153 snprintf(vportStr, sizeof(vportStr),":%d",vport);
1154 }
1155 const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
1156 const int url_sz = scheme.length() + strlen(conn->port->defaultsite) + sizeof(vportStr) + url.length() + 32;
1157 char *uri = static_cast<char *>(xcalloc(url_sz, 1));
1158 snprintf(uri, url_sz, SQUIDSBUFPH "://%s%s" SQUIDSBUFPH,
1159 SQUIDSBUFPRINT(scheme), conn->port->defaultsite, vportStr, SQUIDSBUFPRINT(url));
1160 debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: " << uri);
1161 return uri;
1162 } else if (vport > 0 /* && (!vhost || no Host:) */) {
1163 debugs(33, 5, "ACCEL VPORT REWRITE: *_port IP + vport=" << vport);
1164 /* Put the local socket IP address as the hostname, with whatever vport we found */
1166 const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
1167 const int url_sz = scheme.length() + sizeof(ipbuf) + url.length() + 32;
1168 char *uri = static_cast<char *>(xcalloc(url_sz, 1));
1169 snprintf(uri, url_sz, SQUIDSBUFPH "://%s:%d" SQUIDSBUFPH,
1170 SQUIDSBUFPRINT(scheme), ipbuf, vport, SQUIDSBUFPRINT(url));
1171 debugs(33, 5, "ACCEL VPORT REWRITE: " << uri);
1172 return uri;
1173 }
1174
1175 return nullptr;
1176}
1177
1178static char *
1180{
1181 char *uri = nullptr;
1182 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
1183 if (const char *host = hp->getHostHeaderField()) {
1184 const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
1185 const int url_sz = scheme.length() + strlen(host) + hp->requestUri().length() + 32;
1186 uri = static_cast<char *>(xcalloc(url_sz, 1));
1187 snprintf(uri, url_sz, SQUIDSBUFPH "://%s" SQUIDSBUFPH,
1188 SQUIDSBUFPRINT(scheme),
1189 host,
1190 SQUIDSBUFPRINT(hp->requestUri()));
1191 }
1192 return uri;
1193}
1194
1195char *
1197{
1199
1200 if (!hp->requestUri().isEmpty() && hp->requestUri()[0] != '/')
1201 return nullptr; /* already in good shape */
1202
1203 char *uri = buildUrlFromHost(this, hp);
1204#if USE_OPENSSL
1205 if (!uri) {
1208 SBuf useHost;
1209 if (!tlsClientSni().isEmpty())
1210 useHost = tlsClientSni();
1211 else
1212 useHost = tlsConnectHostOrIp;
1213
1215 const int url_sz = scheme.length() + useHost.length() + hp->requestUri().length() + 32;
1216 uri = static_cast<char *>(xcalloc(url_sz, 1));
1217 snprintf(uri, url_sz, SQUIDSBUFPH "://" SQUIDSBUFPH ":%hu" SQUIDSBUFPH,
1218 SQUIDSBUFPRINT(scheme),
1219 SQUIDSBUFPRINT(useHost),
1221 SQUIDSBUFPRINT(hp->requestUri()));
1222 }
1223#endif
1224 if (uri)
1225 debugs(33, 5, "TLS switching host rewrite: " << uri);
1226 return uri;
1227}
1228
1229static char *
1231{
1232 // TODO Must() on URI !empty when the parser supports throw. For now avoid assert().
1233 if (!hp->requestUri().isEmpty() && hp->requestUri()[0] != '/')
1234 return nullptr; /* already in good shape */
1235
1236 char *uri = buildUrlFromHost(conn, hp);
1237 if (!uri) {
1238 /* Put the local socket IP address as the hostname. */
1239 static char ipbuf[MAX_IPSTRLEN];
1241 const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
1242 const int url_sz = sizeof(ipbuf) + hp->requestUri().length() + 32;
1243 uri = static_cast<char *>(xcalloc(url_sz, 1));
1244 snprintf(uri, url_sz, SQUIDSBUFPH "://%s:%d" SQUIDSBUFPH,
1245 SQUIDSBUFPRINT(scheme),
1246 ipbuf, conn->clientConnection->local.port(), SQUIDSBUFPRINT(hp->requestUri()));
1247 }
1248
1249 if (uri)
1250 debugs(33, 5, "TRANSPARENT REWRITE: " << uri);
1251 return uri;
1252}
1253
1256{
1257 /* Attempt to parse the first line; this will define where the method, url, version and header begin */
1258 {
1259 Must(hp);
1260
1263
1264 const bool parsedOk = hp->parse(inBuf);
1265
1266 // sync the buffers after parsing.
1267 inBuf = hp->remaining();
1268
1269 if (hp->needsMoreData()) {
1270 debugs(33, 5, "Incomplete request, waiting for end of request line");
1271 return nullptr;
1272 }
1273
1274 if (!parsedOk) {
1275 const bool tooBig =
1276 hp->parseStatusCode == Http::scRequestHeaderFieldsTooLarge ||
1277 hp->parseStatusCode == Http::scUriTooLong;
1278 auto result = abortRequestParsing(
1279 tooBig ? "error:request-too-large" : "error:invalid-request");
1280 // assume that remaining leftovers belong to this bad request
1281 if (!inBuf.isEmpty())
1283 return result;
1284 }
1285 }
1286
1287 /* We know the whole request is in parser now */
1288 debugs(11, 2, "HTTP Client " << clientConnection);
1289 debugs(11, 2, "HTTP Client REQUEST:\n---------\n" <<
1290 hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol() << "\n" <<
1291 hp->mimeHeader() <<
1292 "\n----------");
1293
1294 /* deny CONNECT via accelerated ports */
1295 if (hp->method() == Http::METHOD_CONNECT && port != nullptr && port->flags.accelSurrogate) {
1296 debugs(33, DBG_IMPORTANT, "WARNING: CONNECT method received on " << transferProtocol << " Accelerator port " << port->s.port());
1297 debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
1298 hp->parseStatusCode = Http::scMethodNotAllowed;
1299 return abortRequestParsing("error:method-not-allowed");
1300 }
1301
1302 /* HTTP/2 connection magic prefix starts with "PRI ".
1303 * Deny "PRI" method if used in HTTP/1.x or 0.9 versions.
1304 * If seen it signals a broken client or proxy has corrupted the traffic.
1305 */
1306 if (hp->method() == Http::METHOD_PRI && hp->messageProtocol() < Http::ProtocolVersion(2,0)) {
1307 debugs(33, DBG_IMPORTANT, "WARNING: PRI method received on " << transferProtocol << " port " << port->s.port());
1308 debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
1309 hp->parseStatusCode = Http::scMethodNotAllowed;
1310 return abortRequestParsing("error:method-not-allowed");
1311 }
1312
1313 if (hp->method() == Http::METHOD_NONE) {
1314 debugs(33, DBG_IMPORTANT, "WARNING: Unsupported method: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
1315 hp->parseStatusCode = Http::scMethodNotAllowed;
1316 return abortRequestParsing("error:unsupported-request-method");
1317 }
1318
1319 // Process headers after request line
1320 debugs(33, 3, "complete request received. " <<
1321 "prefix_sz = " << hp->messageHeaderSize() <<
1322 ", request-line-size=" << hp->firstLineSize() <<
1323 ", mime-header-size=" << hp->headerBlockSize() <<
1324 ", mime header block:\n" << hp->mimeHeader() << "\n----------");
1325
1326 /* Ok, all headers are received */
1327 ClientHttpRequest *http = new ClientHttpRequest(this);
1328
1329 http->req_sz = hp->messageHeaderSize();
1330 Http::Stream *result = new Http::Stream(clientConnection, http);
1331
1332 StoreIOBuffer tempBuffer;
1333 tempBuffer.data = result->reqbuf;
1334 tempBuffer.length = HTTP_REQBUF_SZ;
1335
1336 ClientStreamData newServer = new clientReplyContext(http);
1337 ClientStreamData newClient = result;
1340 clientSocketDetach, newClient, tempBuffer);
1341
1342 /* set url */
1343 debugs(33,5, "Prepare absolute URL from " <<
1344 (transparent()?"intercept":(port->flags.accelSurrogate ? "accel":"")));
1345 /* Rewrite the URL in transparent or accelerator mode */
1346 /* NP: there are several cases to traverse here:
1347 * - standard mode (forward proxy)
1348 * - transparent mode (TPROXY)
1349 * - transparent mode with failures
1350 * - intercept mode (NAT)
1351 * - intercept mode with failures
1352 * - accelerator mode (reverse proxy)
1353 * - internal relative-URL
1354 * - mixed combos of the above with internal URL
1355 * - remote interception with PROXY protocol
1356 * - remote reverse-proxy with PROXY protocol
1357 */
1358 if (switchedToHttps()) {
1359 http->uri = prepareTlsSwitchingURL(hp);
1360 } else if (transparent()) {
1361 /* intercept or transparent mode, properly working with no failures */
1362 http->uri = prepareTransparentURL(this, hp);
1363
1364 } else if (internalCheck(hp->requestUri())) { // NP: only matches relative-URI
1365 /* internal URL mode */
1366 // XXX: By prepending our name and port, we create an absolute URL
1367 // that may mismatch the (yet unparsed) Host header in the request.
1368 http->uri = xstrdup(internalLocalUri(nullptr, hp->requestUri()));
1369
1370 } else if (port->flags.accelSurrogate) {
1371 /* accelerator mode */
1372 http->uri = prepareAcceleratedURL(this, hp);
1373 http->flags.accel = true;
1374 }
1375
1376 if (!http->uri) {
1377 /* No special rewrites have been applied above, use the
1378 * requested url. may be rewritten later, so make extra room */
1379 int url_sz = hp->requestUri().length() + Config.appendDomainLen + 5;
1380 http->uri = (char *)xcalloc(url_sz, 1);
1381 SBufToCstring(http->uri, hp->requestUri());
1382 }
1383
1384 result->flags.parsed_ok = 1;
1385 return result;
1386}
1387
1388bool
1390{
1391 if (pipeline.empty() && inBuf.isEmpty()) {
1392 debugs(33, 4, "yes, without active requests and unparsed input");
1393 return true;
1394 }
1395
1397 debugs(33, 3, "yes, without half_closed_clients");
1398 return true;
1399 }
1400
1401 // Squid currently tries to parse (possibly again) a partially received
1402 // request after an EOF with half_closed_clients. To give that last parse in
1403 // afterClientRead() a chance, we ignore partially parsed requests here.
1404 debugs(33, 3, "no, honoring half_closed_clients");
1405 return false;
1406}
1407
1408void
1409ConnStateData::consumeInput(const size_t byteCount)
1410{
1411 assert(byteCount > 0 && byteCount <= inBuf.length());
1412 inBuf.consume(byteCount);
1413 debugs(33, 5, "inBuf has " << inBuf.length() << " unused bytes");
1414}
1415
1416void
1418{
1419 // Were we expecting to read more request body from half-closed connection?
1421 debugs(33, 3, "truncated body: closing half-closed " << clientConnection);
1423 return;
1424 }
1425
1426 if (flags.readMore)
1427 readSomeData();
1428}
1429
1430void
1432{
1433 // From HTTP p.o.v., we do not have to close after every error detected
1434 // at the client-side, but many such errors do require closure and the
1435 // client-side code is bad at handling errors so we play it safe.
1436 if (request)
1437 request->flags.proxyKeepalive = false;
1438 flags.readMore = false;
1439 debugs(33,4, "Will close after error: " << clientConnection);
1440}
1441
1442#if USE_OPENSSL
1444{
1445 ClientHttpRequest *http = context->http;
1446
1447 if (!sslServerBump)
1448 return false;
1449
1451 // Did we create an error entry while processing CONNECT?
1452 if (!sslServerBump->entry->isEmpty()) {
1453 quitAfterError(http->request);
1454
1455 // Get the saved error entry and send it to the client by replacing the
1456 // ClientHttpRequest store entry with it.
1458 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1459 assert(repContext);
1460 debugs(33, 5, "Responding with delated error for " << http->uri);
1461 repContext->setReplyToStoreEntry(sslServerBump->entry, "delayed SslBump error");
1462
1463 // Get error details from the fake certificate-peeking request.
1465 context->pullData();
1466 return true;
1467 }
1468
1469 // In bump-server-first mode, we have not necessarily seen the intended
1470 // server name at certificate-peeking time. Check for domain mismatch now,
1471 // when we can extract the intended name from the bumped HTTP request.
1472 if (const Security::CertPointer &srvCert = sslServerBump->serverCert) {
1473 HttpRequest *request = http->request;
1474 const auto host = request->url.parsedHost();
1475 if (host && Ssl::HasSubjectName(*srvCert, *host)) {
1476 debugs(33, 5, "certificate matches requested host: " << *host);
1477 return false;
1478 } else {
1479 debugs(33, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " <<
1480 "does not match request target " << RawPointer(host));
1481
1482 bool allowDomainMismatch = false;
1485 const auto sslErrors = std::make_unique<Security::CertErrors>(Security::CertError(SQUID_X509_V_ERR_DOMAIN_MISMATCH, srvCert));
1486 check.sslErrors = sslErrors.get();
1487 clientAclChecklistFill(check, http);
1488 allowDomainMismatch = check.fastCheck().allowed();
1489 }
1490
1491 if (!allowDomainMismatch) {
1492 quitAfterError(request);
1493
1495 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1496 assert (repContext);
1497
1498 request->hier = sslServerBump->request->hier;
1499
1500 // Create an error object and fill it
1501 const auto err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request, http->al);
1502 err->src_addr = clientConnection->remote;
1505 srvCert, nullptr);
1507 repContext->setReplyToError(request->method, err);
1508 assert(context->http->out.offset == 0);
1509 context->pullData();
1510 return true;
1511 }
1512 }
1513 }
1514
1515 return false;
1516}
1517#endif // USE_OPENSSL
1518
1520bool
1522{
1524 debugs(33, 5, "disabled; send error: " << requestError);
1525 return false;
1526 }
1527
1528 if (!preservingClientData_) {
1529 debugs(33, 3, "may have forgotten client data; send error: " << requestError);
1530 return false;
1531 }
1532
1534 checklist.requestErrorType = requestError;
1535 fillChecklist(checklist);
1536 const auto &answer = checklist.fastCheck();
1537 if (answer.allowed() && answer.kind == 1) {
1538 debugs(33, 3, "Request will be tunneled to server");
1539 const auto context = pipeline.front();
1540 const auto http = context ? context->http : nullptr;
1541 const auto request = http ? http->request : nullptr;
1542 if (context)
1543 context->finished(); // Will remove from pipeline queue
1544 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, nullptr, nullptr, 0);
1545 return initiateTunneledRequest(request, "unknown-protocol", preservedClientData);
1546 }
1547 debugs(33, 3, "denied; send error: " << requestError);
1548 return false;
1549}
1550
1551void
1553{
1554 /*
1555 * DPW 2007-05-18
1556 * Moved the TCP_RESET feature from clientReplyContext::sendMoreData
1557 * to here because calling comm_reset_close() causes http to
1558 * be freed before accessing.
1559 */
1560 if (request != nullptr && request->flags.resetTcp && Comm::IsConnOpen(conn->clientConnection)) {
1561 debugs(33, 3, "Sending TCP RST on " << conn->clientConnection);
1562 conn->flags.readMore = false;
1564 }
1565}
1566
1567void
1569{
1570 ClientHttpRequest *http = context->http;
1571 bool mustReplyToOptions = false;
1572 bool expectBody = false;
1573
1574 // We already have the request parsed and checked, so we
1575 // only need to go through the final body/conn setup to doCallouts().
1576 assert(http->request);
1577 HttpRequest::Pointer request = http->request;
1578
1579 // temporary hack to avoid splitting this huge function with sensitive code
1580 const bool isFtp = !hp;
1581
1582 // Some blobs below are still HTTP-specific, but we would have to rewrite
1583 // this entire function to remove them from the FTP code path. Connection
1584 // setup and body_pipe preparation blobs are needed for FTP.
1585
1586 request->manager(conn, http->al);
1587
1588 request->flags.accelerated = http->flags.accel;
1589 request->flags.sslBumped=conn->switchedToHttps();
1590 // TODO: decouple http->flags.accel from request->flags.sslBumped
1591 request->flags.noDirect = (request->flags.accelerated && !request->flags.sslBumped) ?
1592 !conn->port->allow_direct : 0;
1593 request->sources |= isFtp ? Http::Message::srcFtp :
1594 ((request->flags.sslBumped || conn->port->transport.protocol == AnyP::PROTO_HTTPS) ? Http::Message::srcHttps : Http::Message::srcHttp);
1595#if USE_AUTH
1596 if (request->flags.sslBumped) {
1597 if (conn->getAuth() != nullptr)
1598 request->auth_user_request = conn->getAuth();
1599 }
1600#endif
1601
1602 http->checkForInternalAccess();
1603
1604 if (!isFtp) {
1605 // XXX: for non-HTTP messages instantiate a different Http::Message child type
1606 // for now Squid only supports HTTP requests
1607 const AnyP::ProtocolVersion &http_ver = hp->messageProtocol();
1608 assert(request->http_ver.protocol == http_ver.protocol);
1609 request->http_ver.major = http_ver.major;
1610 request->http_ver.minor = http_ver.minor;
1611 }
1612
1613 mustReplyToOptions = (request->method == Http::METHOD_OPTIONS) &&
1615 if (!urlCheckRequest(request.getRaw()) || mustReplyToOptions) {
1617 conn->quitAfterError(request.getRaw());
1618 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1619 assert (repContext);
1621 conn, request.getRaw(), nullptr, nullptr);
1622 assert(context->http->out.offset == 0);
1623 context->pullData();
1624 clientProcessRequestFinished(conn, request);
1625 return;
1626 }
1627
1628 const auto frameStatus = request->checkEntityFraming();
1629 if (frameStatus != Http::scNone) {
1631 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1632 assert (repContext);
1633 conn->quitAfterError(request.getRaw());
1634 repContext->setReplyToError(ERR_INVALID_REQ, frameStatus, nullptr, conn, request.getRaw(), nullptr, nullptr);
1635 assert(context->http->out.offset == 0);
1636 context->pullData();
1637 clientProcessRequestFinished(conn, request);
1638 return;
1639 }
1640
1642 // Let tunneling code be fully responsible for CONNECT requests
1643 if (http->request->method == Http::METHOD_CONNECT) {
1644 context->mayUseConnection(true);
1645 conn->flags.readMore = false;
1646 }
1647
1648#if USE_OPENSSL
1649 if (conn->switchedToHttps() && conn->serveDelayedError(context)) {
1650 clientProcessRequestFinished(conn, request);
1651 return;
1652 }
1653#endif
1654
1655 /* Do we expect a request-body? */
1656 const auto chunked = request->header.chunked();
1657 expectBody = chunked || request->content_length > 0;
1658 if (!context->mayUseConnection() && expectBody) {
1659 request->body_pipe = conn->expectRequestBody(
1660 chunked ? -1 : request->content_length);
1661
1662 /* Is it too large? */
1663 if (!chunked && // if chunked, we will check as we accumulate
1666 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1667 assert (repContext);
1668 conn->quitAfterError(request.getRaw());
1669 repContext->setReplyToError(ERR_TOO_BIG,
1670 Http::scContentTooLarge, nullptr,
1671 conn, http->request, nullptr, nullptr);
1672 assert(context->http->out.offset == 0);
1673 context->pullData();
1674 clientProcessRequestFinished(conn, request);
1675 return;
1676 }
1677
1678 if (!isFtp) {
1679 // We may stop producing, comm_close, and/or call setReplyToError()
1680 // below, so quit on errors to avoid http->doCallouts()
1681 if (!conn->handleRequestBodyData()) {
1682 clientProcessRequestFinished(conn, request);
1683 return;
1684 }
1685
1686 if (!request->body_pipe->productionEnded()) {
1687 debugs(33, 5, "need more request body");
1688 context->mayUseConnection(true);
1689 assert(conn->flags.readMore);
1690 }
1691 }
1692 }
1693
1694 http->calloutContext = new ClientRequestContext(http);
1695
1696 http->doCallouts();
1697
1698 clientProcessRequestFinished(conn, request);
1699}
1700
1701void
1703{
1704 debugs(33, 3, context << " to " << pipeline.count() << '/' << pipeline.nrequests);
1705 if (bareError) {
1706 debugs(33, 5, "assigning " << bareError);
1707 assert(context);
1708 assert(context->http);
1709 context->http->updateError(bareError);
1710 bareError.clear();
1711 }
1712 pipeline.add(context);
1713}
1714
1715int
1717{
1718 // TODO: Support pipelined requests through pinned connections.
1719 if (pinning.pinned)
1720 return 0;
1722}
1723
1729bool
1731{
1732 const int existingRequestCount = pipeline.count();
1733
1734 // default to the configured pipeline size.
1735 // add 1 because the head of pipeline is counted in concurrent requests and not prefetch queue
1736#if USE_OPENSSL
1737 const int internalRequest = (transparent() && sslBumpMode == Ssl::bumpSplice) ? 1 : 0;
1738#else
1739 const int internalRequest = 0;
1740#endif
1741 const int concurrentRequestLimit = pipelinePrefetchMax() + 1 + internalRequest;
1742
1743 // when queue filled already we can't add more.
1744 if (existingRequestCount >= concurrentRequestLimit) {
1745 debugs(33, 3, clientConnection << " max concurrent requests reached (" << concurrentRequestLimit << ")");
1746 debugs(33, 5, clientConnection << " deferring new request until one is done");
1747 return true;
1748 }
1749
1750 return false;
1751}
1752
1758bool
1760{
1762 return proxyProtocolError("PROXY client not permitted by default ACL");
1763
1765 fillChecklist(ch);
1766 if (!ch.fastCheck().allowed())
1767 return proxyProtocolError("PROXY client not permitted by ACLs");
1768
1769 return true;
1770}
1771
1777bool
1779{
1780 if (msg) {
1781 // This is important to know, but maybe not so much that flooding the log is okay.
1782#if QUIET_PROXY_PROTOCOL
1783 // display the first of every 32 occurrences at level 1, the others at level 2.
1784 static uint8_t hide = 0;
1785 debugs(33, (hide++ % 32 == 0 ? DBG_IMPORTANT : 2), msg << " from " << clientConnection);
1786#else
1787 debugs(33, DBG_IMPORTANT, msg << " from " << clientConnection);
1788#endif
1789 mustStop(msg);
1790 }
1791 return false;
1792}
1793
1798bool
1800{
1801 try {
1802 const auto parsed = ProxyProtocol::Parse(inBuf);
1803 proxyProtocolHeader_ = parsed.header;
1805 inBuf.consume(parsed.size);
1807 if (proxyProtocolHeader_->hasForwardedAddresses()) {
1808 clientConnection->local = proxyProtocolHeader_->destinationAddress;
1811 clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY spoofing of this new IP.
1812 debugs(33, 5, "PROXY/" << proxyProtocolHeader_->version() << " upgrade: " << clientConnection);
1813 }
1815 debugs(33, 3, "PROXY protocol: waiting for more than " << inBuf.length() << " bytes");
1816 return false;
1817 } catch (const std::exception &e) {
1818 return proxyProtocolError(e.what());
1819 }
1820 return true;
1821}
1822
1823void
1832
1835void
1837{
1838 debugs(33, 5, clientConnection << ": attempting to parse");
1839
1840 // Loop while we have read bytes that are not needed for producing the body
1841 // On errors, bodyPipe may become nil, but readMore will be cleared
1842 while (!inBuf.isEmpty() && !bodyPipe && flags.readMore) {
1843
1844 // Prohibit concurrent requests when using a pinned to-server connection
1845 // because our Client classes do not support request pipelining.
1846 if (pinning.pinned && !pinning.readHandler) {
1847 debugs(33, 3, clientConnection << " waits for busy " << pinning.serverConnection);
1848 break;
1849 }
1850
1851 /* Limit the number of concurrent requests */
1853 break;
1854
1855 // try to parse the PROXY protocol header magic bytes
1858 break;
1859
1860 // we have been waiting for PROXY to provide client-IP
1861 // for some lookups, ie rDNS
1863
1864 // Done with PROXY protocol which has cleared preservingClientData_.
1865 // If the next protocol supports on_unsupported_protocol, then its
1866 // parseOneRequest() must reset preservingClientData_.
1868 }
1869
1870 if (Http::StreamPointer context = parseOneRequest()) {
1871 debugs(33, 5, clientConnection << ": done parsing a request");
1873 context->registerWithConn();
1874
1875#if USE_OPENSSL
1876 if (switchedToHttps())
1878#endif
1879
1880 processParsedRequest(context);
1881
1882 if (context->mayUseConnection()) {
1883 debugs(33, 3, "Not parsing new requests, as this request may need the connection");
1884 break;
1885 }
1886 } else {
1887 debugs(33, 5, clientConnection << ": not enough request data: " <<
1888 inBuf.length() << " < " << Config.maxRequestHeaderSize);
1890 break;
1891 }
1892 }
1893
1894 debugs(33, 7, "buffered leftovers: " << inBuf.length());
1895
1897 if (pipeline.empty()) {
1898 // we processed what we could parse, and no more data is coming
1899 debugs(33, 5, "closing half-closed without parsed requests: " << clientConnection);
1901 } else {
1902 // we parsed what we could, and no more data is coming
1903 debugs(33, 5, "monitoring half-closed while processing parsed requests: " << clientConnection);
1904 flags.readMore = false; // may already be false
1905 }
1906 }
1907}
1908
1909void
1911{
1912#if USE_OPENSSL
1913 if (parsingTlsHandshake) {
1915 return;
1916 }
1917#endif
1918
1919 /* Process next request */
1920 if (pipeline.empty())
1921 fd_note(clientConnection->fd, "Reading next request");
1922
1923 parseRequests();
1924
1925 if (!isOpen())
1926 return;
1927
1929}
1930
1937bool
1939{
1940 // if we are reading a body, stuff data into the body pipe
1941 if (bodyPipe != nullptr)
1942 return handleRequestBodyData();
1943 return true;
1944}
1945
1953bool
1955{
1956 assert(bodyPipe != nullptr);
1957
1958 if (bodyParser) { // chunked encoding
1959 if (const err_type error = handleChunkedRequestBody()) {
1961 return false;
1962 }
1963 } else { // identity encoding
1964 debugs(33,5, "handling plain request body for " << clientConnection);
1965 const auto putSize = bodyPipe->putMoreData(inBuf.rawContent(), inBuf.length());
1966 if (putSize > 0)
1967 consumeInput(putSize);
1968
1969 if (!bodyPipe->mayNeedMoreData()) {
1970 // BodyPipe will clear us automagically when we produced everything
1971 bodyPipe = nullptr;
1972 }
1973 }
1974
1975 if (!bodyPipe) {
1976 debugs(33,5, "produced entire request body for " << clientConnection);
1977
1978 if (const char *reason = stoppedSending()) {
1979 /* we've finished reading like good clients,
1980 * now do the close that initiateClose initiated.
1981 */
1982 debugs(33, 3, "closing for earlier sending error: " << reason);
1984 return false;
1985 }
1986 }
1987
1988 return true;
1989}
1990
1994{
1995 debugs(33, 7, "chunked from " << clientConnection << ": " << inBuf.length());
1996
1997 try { // the parser will throw on errors
1998
1999 if (inBuf.isEmpty()) // nothing to do
2000 return ERR_NONE;
2001
2004 const bool parsed = bodyParser->parse(inBuf);
2005 inBuf = bodyParser->remaining(); // sync buffers
2006 bpc.checkIn();
2007
2008 // dechunk then check: the size limit applies to _dechunked_ content
2010 return ERR_TOO_BIG;
2011
2012 if (parsed) {
2014 Must(!bodyPipe);
2015 return ERR_NONE; // nil bodyPipe implies body end for the caller
2016 }
2017
2018 // if chunk parser needs data, then the body pipe must need it too
2020
2021 // if parser needs more space and we can consume nothing, we will stall
2023 } catch (...) { // TODO: be more specific
2024 debugs(33, 3, "malformed chunks" << bodyPipe->status());
2025 return ERR_INVALID_REQ;
2026 }
2027
2028 debugs(33, 7, "need more chunked data" << bodyPipe->status());
2029 return ERR_NONE;
2030}
2031
2033void
2035{
2037
2038 // XXX: The code below works if we fail during initial request parsing,
2039 // but if we fail when the server connection is used already, the server may send
2040 // us its response too, causing various assertions. How to prevent that?
2041#if WE_KNOW_HOW_TO_SEND_ERRORS
2043 if (context != NULL && !context->http->out.offset) { // output nothing yet
2044 clientStreamNode *node = context->getClientReplyContext();
2045 clientReplyContext *repContext = dynamic_cast<clientReplyContext*>(node->data.getRaw());
2046 assert(repContext);
2047 const Http::StatusCode scode = (error == ERR_TOO_BIG) ?
2048 Http::scContentTooLarge : HTTP_BAD_REQUEST;
2049 repContext->setReplyToError(error, scode,
2050 repContext->http->uri,
2051 CachePeer,
2052 repContext->http->request,
2053 inBuf, nullptr);
2054 context->pullData();
2055 } else {
2056 // close or otherwise we may get stuck as nobody will notice the error?
2058 }
2059#else
2060 debugs(33, 3, "aborting chunked request without error " << error);
2062#endif
2063 flags.readMore = false;
2064}
2065
2066void
2068{
2069 // request reader may get stuck waiting for space if nobody consumes body
2070 if (bodyPipe != nullptr)
2072
2073 // kids extend
2074}
2075
2077void
2079{
2080 if (!Comm::IsConnOpen(io.conn))
2081 return;
2082
2085 if (tunnelOnError(error))
2086 return;
2087
2088 /*
2089 * Just close the connection to not confuse browsers
2090 * using persistent connections. Some browsers open
2091 * a connection and then do not use it until much
2092 * later (presumably because the request triggering
2093 * the open has already been completed on another
2094 * connection)
2095 */
2096 debugs(33, 3, "requestTimeout: FD " << io.fd << ": lifetime is expired.");
2097 io.conn->close();
2098}
2099
2100void
2102{
2103 debugs(33, DBG_IMPORTANT, "WARNING: Closing client connection due to lifetime timeout" <<
2104 Debug::Extra << "connection: " << io.conn);
2105
2106 LogTagsErrors lte;
2107 lte.timedout = true;
2109}
2110
2112 AsyncJob("ConnStateData"), // kids overwrite
2113 Server(xact)
2114#if USE_OPENSSL
2115 , tlsParser(Security::HandshakeParser::fromClient)
2116#endif
2117{
2118 // store the details required for creating more MasterXaction objects as new requests come in
2119 log_addr = xact->tcpClient->remote;
2121
2122 // register to receive notice of Squid signal events
2123 // which may affect long persisting client connections
2125}
2126
2127void
2129{
2132
2133 if (port->disable_pmtu_discovery != DISABLE_PMTU_OFF &&
2134 (transparent() || port->disable_pmtu_discovery == DISABLE_PMTU_ALWAYS)) {
2135#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
2136 int i = IP_PMTUDISC_DONT;
2137 if (xsetsockopt(clientConnection->fd, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0) {
2138 int xerrno = errno;
2139 debugs(33, 2, "WARNING: Path MTU discovery disabling failed on " << clientConnection << " : " << xstrerr(xerrno));
2140 }
2141#else
2142 static bool reported = false;
2143
2144 if (!reported) {
2145 debugs(33, DBG_IMPORTANT, "WARNING: Path MTU discovery disabling is not supported on your platform.");
2146 reported = true;
2147 }
2148#endif
2149 }
2150
2154
2155 needProxyProtocolHeader_ = port->flags.proxySurrogate;
2157 if (!proxyProtocolValidateClient()) // will close the connection on failure
2158 return;
2159 } else
2161
2162 // requires needProxyProtocolHeader_ which is initialized above
2164}
2165
2166void
2168{
2172
2174
2175#if USE_DELAY_POOLS
2176 fd_table[clientConnection->fd].clientInfo = nullptr;
2177
2178 if (!Config.onoff.client_db)
2179 return; // client delay pools require client_db
2180
2181 const auto &pools = ClientDelayPools::Instance()->pools;
2182 if (pools.size()) {
2183 ACLFilledChecklist ch(nullptr, nullptr);
2184 fillChecklist(ch);
2185 // TODO: we check early to limit error response bandwidth but we
2186 // should recheck when we can honor delay_pool_uses_indirect
2187 for (unsigned int pool = 0; pool < pools.size(); ++pool) {
2188
2189 /* pools require explicit 'allow' to assign a client into them */
2190 if (pools[pool]->access) {
2191 ch.changeAcl(pools[pool]->access);
2192 const auto &answer = ch.fastCheck();
2193 if (answer.allowed()) {
2194
2195 /* request client information from db after we did all checks
2196 this will save hash lookup if client failed checks */
2198 assert(cli);
2199
2200 /* put client info in FDE */
2201 fd_table[clientConnection->fd].clientInfo = cli;
2202
2203 /* setup write limiter for this request */
2204 const double burst = floor(0.5 +
2205 (pools[pool]->highwatermark * Config.ClientDelay.initial)/100.0);
2206 cli->setWriteLimiter(pools[pool]->rate, burst, pools[pool]->highwatermark);
2207 break;
2208 } else {
2209 debugs(83, 4, "Delay pool " << pool << " skipped because ACL " << answer);
2210 }
2211 }
2212 }
2213 }
2214#endif
2215
2216 // kids must extend to actually start doing something (e.g., reading)
2217}
2218
2221{
2222 const auto handshakeResult = Security::Accept(*clientConnection);
2223
2224#if USE_OPENSSL
2225 // log ASAP, even if the handshake has not completed (or failed)
2226 const auto fd = clientConnection->fd;
2227 assert(fd >= 0);
2228 keyLogger.checkpoint(*fd_table[fd].ssl, *this);
2229#else
2230 // TODO: Support fd_table[fd].ssl dereference in other builds.
2231#endif
2232
2233 return handshakeResult;
2234}
2235
2237void
2239{
2240 Assure(params.port);
2241
2242 // NP: it is possible the port was reconfigured when the call or accept() was queued.
2243
2244 if (params.flag != Comm::OK) {
2245 // Its possible the call was still queued when the client disconnected
2246 debugs(33, 2, params.port->listenConn << ": accept failure: " << xstrerr(params.xerrno));
2247 return;
2248 }
2249
2250 debugs(33, 4, params.conn << ": accepted");
2251 fd_note(params.conn->fd, "client http connect");
2252 const auto xact = MasterXaction::MakePortful(params.port);
2253 xact->tcpClient = params.conn;
2254
2255 // Socket is ready, setup the connection manager to start using it
2256 auto *srv = Http::NewServer(xact);
2257 // XXX: do not abandon the MasterXaction object
2258 AsyncJob::Start(srv); // usually async-calls readSomeData()
2259}
2260
2262static bool
2264{
2265 const auto conn = connState->clientConnection;
2266 if (Security::CreateServerSession(ctx, conn, connState->port->secure, "client https start")) {
2267 debugs(33, 5, "will negotiate TLS on " << conn);
2268 return true;
2269 }
2270
2271 debugs(33, DBG_IMPORTANT, "ERROR: could not create TLS server context for " << conn);
2272 conn->close();
2273 return false;
2274}
2275
2277static void
2278clientNegotiateSSL(int fd, void *data)
2279{
2280 ConnStateData *conn = (ConnStateData *)data;
2281
2282 const auto handshakeResult = conn->acceptTls();
2283 switch (handshakeResult.category) {
2285 break;
2286
2289 return;
2290
2293 return;
2294
2296 debugs(83, (handshakeResult.important ? Important(62) : 2), "ERROR: Cannot accept a TLS connection" <<
2297 Debug::Extra << "problem: " << WithExtras(handshakeResult));
2298 // TODO: No ConnStateData::tunnelOnError() on this forward-proxy code
2299 // path because we cannot know the intended connection target?
2300 conn->updateError(ERR_SECURE_ACCEPT_FAIL, handshakeResult.errorDetail);
2301 conn->clientConnection->close();
2302 return;
2303 }
2304
2305 Security::SessionPointer session(fd_table[fd].ssl);
2306
2307#if USE_OPENSSL
2308 if (Security::SessionIsResumed(session)) {
2309 debugs(83, 2, "Session " << SSL_get_session(session.get()) <<
2310 " reused on FD " << fd << " (" << fd_table[fd].ipaddr <<
2311 ":" << (int)fd_table[fd].remote_port << ")");
2312 } else {
2313 if (Debug::Enabled(83, 4)) {
2314 /* Write out the SSL session details.. actually the call below, but
2315 * OpenSSL headers do strange typecasts confusing GCC.. */
2316 /* PEM_write_SSL_SESSION(DebugStream(), SSL_get_session(ssl)); */
2317#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00908000L
2318 PEM_ASN1_write(reinterpret_cast<i2d_of_void *>(i2d_SSL_SESSION),
2319 PEM_STRING_SSL_SESSION, DebugStream(),
2320 reinterpret_cast<char *>(SSL_get_session(session.get())),
2321 nullptr, nullptr, 0, nullptr, nullptr);
2322
2323#elif (ALLOW_ALWAYS_SSL_SESSION_DETAIL == 1)
2324
2325 /* When using gcc 3.3.x and OpenSSL 0.9.7x sometimes a compile error can occur here.
2326 * This is caused by an unpredictable gcc behaviour on a cast of the first argument
2327 * of PEM_ASN1_write(). For this reason this code section is disabled. To enable it,
2328 * define ALLOW_ALWAYS_SSL_SESSION_DETAIL=1.
2329 * Because there are two possible usable cast, if you get an error here, try the other
2330 * commented line. */
2331
2332 PEM_ASN1_write((int(*)())i2d_SSL_SESSION, PEM_STRING_SSL_SESSION,
2333 DebugStream(),
2334 reinterpret_cast<char *>(SSL_get_session(session.get())),
2335 nullptr, nullptr, 0, nullptr, nullptr);
2336 /* PEM_ASN1_write((int(*)(...))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION,
2337 DebugStream(),
2338 reinterpret_cast<char *>(SSL_get_session(session.get())),
2339 nullptr, nullptr, 0, nullptr, nullptr);
2340 */
2341#else
2342 debugs(83, 4, "With " OPENSSL_VERSION_TEXT ", session details are available only defining ALLOW_ALWAYS_SSL_SESSION_DETAIL=1 in the source.");
2343
2344#endif
2345 /* Note: This does not automatically fflush the log file.. */
2346 }
2347
2348 debugs(83, 2, "New session " << SSL_get_session(session.get()) <<
2349 " on FD " << fd << " (" << fd_table[fd].ipaddr << ":" <<
2350 fd_table[fd].remote_port << ")");
2351 }
2352#else
2353 debugs(83, 2, "TLS session reuse not yet implemented.");
2354#endif
2355
2356 // Connection established. Retrieve TLS connection parameters for logging.
2358
2359#if USE_OPENSSL
2360 X509 *client_cert = SSL_get_peer_certificate(session.get());
2361
2362 if (client_cert) {
2363 debugs(83, 3, "FD " << fd << " client certificate: subject: " <<
2364 Security::SubjectName(*client_cert));
2365
2366 debugs(83, 3, "FD " << fd << " client certificate: issuer: " <<
2367 Security::IssuerName(*client_cert));
2368
2369 X509_free(client_cert);
2370 } else {
2371 debugs(83, 5, "FD " << fd << " has no client certificate.");
2372 }
2373#else
2374 debugs(83, 2, "Client certificate requesting not yet implemented.");
2375#endif
2376
2377 // If we are called, then bumped CONNECT has succeeded. Finalize it.
2378 if (auto xact = conn->pipeline.front()) {
2379 if (xact->http && xact->http->request && xact->http->request->method == Http::METHOD_CONNECT)
2380 xact->finished();
2381 // cannot proceed with encryption if requests wait for plain responses
2382 Must(conn->pipeline.empty());
2383 }
2384 /* careful: finished() above frees request, host, etc. */
2385
2386 conn->readSomeData();
2387}
2388
2393static void
2395{
2396 assert(connState);
2397 const Comm::ConnectionPointer &details = connState->clientConnection;
2398
2399 if (!ctx || !httpsCreate(connState, ctx))
2400 return;
2401
2403
2404 Comm::SetSelect(details->fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
2405}
2406
2407#if USE_OPENSSL
2411static void
2413{
2414 ConnStateData *connState = (ConnStateData *) data;
2415
2416 // if the connection is closed or closing, just return.
2417 if (!connState->isOpen())
2418 return;
2419
2420 if (answer.allowed()) {
2421 debugs(33, 2, "sslBump action " << Ssl::bumpMode(answer.kind) << "needed for " << connState->clientConnection);
2422 connState->sslBumpMode = static_cast<Ssl::BumpMode>(answer.kind);
2423 } else {
2424 debugs(33, 3, "sslBump not needed for " << connState->clientConnection);
2425 connState->sslBumpMode = Ssl::bumpSplice;
2426 }
2427
2428 if (connState->sslBumpMode == Ssl::bumpTerminate) {
2429 connState->clientConnection->close();
2430 return;
2431 }
2432
2433 if (!connState->fakeAConnectRequest("ssl-bump", connState->inBuf))
2434 connState->clientConnection->close();
2435}
2436#endif
2437
2439static void
2441{
2442 Assure(params.port);
2443
2444 // NP: it is possible the port was reconfigured when the call or accept() was queued.
2445
2446 if (params.flag != Comm::OK) {
2447 // Its possible the call was still queued when the client disconnected
2448 debugs(33, 2, "httpsAccept: " << params.port->listenConn << ": accept failure: " << xstrerr(params.xerrno));
2449 return;
2450 }
2451
2452 const auto xact = MasterXaction::MakePortful(params.port);
2453 xact->tcpClient = params.conn;
2454
2455 debugs(33, 4, params.conn << " accepted, starting SSL negotiation.");
2456 fd_note(params.conn->fd, "client https connect");
2457
2458 // Socket is ready, setup the connection manager to start using it
2459 auto *srv = Https::NewServer(xact);
2460 // XXX: do not abandon the MasterXaction object
2461 AsyncJob::Start(srv); // usually async-calls postHttpsAccept()
2462}
2463
2464void
2466{
2467 if (port->flags.tunnelSslBumping) {
2468#if USE_OPENSSL
2469 debugs(33, 5, "accept transparent connection: " << clientConnection);
2470
2471 if (!Config.accessList.ssl_bump) {
2473 return;
2474 }
2475
2476 const auto mx = MasterXaction::MakePortful(port);
2477 mx->tcpClient = clientConnection;
2478 // Create a fake HTTP request and ALE for the ssl_bump ACL check,
2479 // using tproxy/intercept provided destination IP and port.
2480 // XXX: Merge with subsequent fakeAConnectRequest(), buildFakeRequest().
2481 // XXX: Do this earlier (e.g., in Http[s]::One::Server constructor).
2482 HttpRequest *request = new HttpRequest(mx);
2483 static char ip[MAX_IPSTRLEN];
2485 request->url.host(clientConnection->local.toStr(ip, sizeof(ip)));
2486 request->url.port(clientConnection->local.port());
2487 request->myportname = port->name;
2488 const AccessLogEntry::Pointer connectAle = new AccessLogEntry;
2489 CodeContext::Reset(connectAle);
2490 // TODO: Use these request/ALE when waiting for new bumped transactions.
2491
2492 auto acl_checklist = ACLFilledChecklist::Make(Config.accessList.ssl_bump, request);
2493 fillChecklist(*acl_checklist);
2494 // Build a local AccessLogEntry to allow requiresAle() acls work
2495 acl_checklist->al = connectAle;
2496 acl_checklist->al->cache.start_time = current_time;
2497 acl_checklist->al->tcpClient = clientConnection;
2498 acl_checklist->al->cache.port = port;
2499 acl_checklist->al->cache.caddr = log_addr;
2500 acl_checklist->al->proxyProtocolHeader = proxyProtocolHeader_;
2501 acl_checklist->al->updateError(bareError);
2502 HTTPMSGUNLOCK(acl_checklist->al->request);
2503 acl_checklist->al->request = request;
2504 HTTPMSGLOCK(acl_checklist->al->request);
2506 ClientHttpRequest *http = context ? context->http : nullptr;
2507 const char *log_uri = http ? http->log_uri : nullptr;
2508 acl_checklist->syncAle(request, log_uri);
2510#else
2511 fatal("FATAL: SSL-Bump requires --with-openssl");
2512#endif
2513 return;
2514 } else {
2515 httpsEstablish(this, port->secure.staticContext);
2516 }
2517}
2518
2519#if USE_OPENSSL
2520void
2522{
2523 ConnStateData * state_data = (ConnStateData *)(data);
2524 state_data->sslCrtdHandleReply(reply);
2525}
2526
2527void
2529{
2530 if (!isOpen()) {
2531 debugs(33, 3, "Connection gone while waiting for ssl_crtd helper reply; helper reply:" << reply);
2532 return;
2533 }
2534
2535 if (reply.result == Helper::BrokenHelper) {
2536 debugs(33, 5, "Certificate for " << tlsConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply);
2537 } else if (!reply.other().hasContent()) {
2538 debugs(1, DBG_IMPORTANT, "\"ssl_crtd\" helper returned <NULL> reply.");
2539 } else {
2541 if (reply_message.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK) {
2542 debugs(33, 5, "Reply from ssl_crtd for " << tlsConnectHostOrIp << " is incorrect");
2543 } else {
2544 if (reply.result != Helper::Okay) {
2545 debugs(33, 5, "Certificate for " << tlsConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply_message.getBody());
2546 } else {
2547 debugs(33, 5, "Certificate for " << tlsConnectHostOrIp << " was successfully received from ssl_crtd");
2550 auto ssl = fd_table[clientConnection->fd].ssl.get();
2551 bool ret = Ssl::configureSSLUsingPkeyAndCertFromMemory(ssl, reply_message.getBody().c_str(), *port);
2552 if (!ret)
2553 debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
2554
2557 } else {
2559 if (ctx && !sslBumpCertKey.isEmpty())
2561 getSslContextDone(ctx);
2562 }
2563 return;
2564 }
2565 }
2566 }
2568 getSslContextDone(nil);
2569}
2570
2572{
2574
2575 const bool connectedOk = sslServerBump && sslServerBump->connectedOk();
2576 if (connectedOk) {
2577 if (X509 *mimicCert = sslServerBump->serverCert.get())
2578 certProperties.mimicCert.resetAndLock(mimicCert);
2579
2580 ACLFilledChecklist checklist(nullptr, sslServerBump->request.getRaw());
2581 fillChecklist(checklist);
2582
2583 for (sslproxy_cert_adapt *ca = Config.ssl_client.cert_adapt; ca != nullptr; ca = ca->next) {
2584 // If the algorithm already set, then ignore it.
2585 if ((ca->alg == Ssl::algSetCommonName && certProperties.setCommonName) ||
2586 (ca->alg == Ssl::algSetValidAfter && certProperties.setValidAfter) ||
2587 (ca->alg == Ssl::algSetValidBefore && certProperties.setValidBefore) )
2588 continue;
2589
2590 if (ca->aclList && checklist.fastCheck(ca->aclList).allowed()) {
2591 const char *alg = Ssl::CertAdaptAlgorithmStr[ca->alg];
2592 const char *param = ca->param;
2593
2594 // For parameterless CN adaptation, use hostname from the
2595 // CONNECT request.
2596 if (ca->alg == Ssl::algSetCommonName) {
2597 if (!param)
2598 param = tlsConnectHostOrIp.c_str();
2599 certProperties.commonName = param;
2600 certProperties.setCommonName = true;
2601 } else if (ca->alg == Ssl::algSetValidAfter)
2602 certProperties.setValidAfter = true;
2603 else if (ca->alg == Ssl::algSetValidBefore)
2604 certProperties.setValidBefore = true;
2605
2606 debugs(33, 5, "Matches certificate adaptation algorithm: " <<
2607 alg << " param: " << (param ? param : "-"));
2608 }
2609 }
2610
2611 certProperties.signAlgorithm = Ssl::algSignEnd;
2612 for (sslproxy_cert_sign *sg = Config.ssl_client.cert_sign; sg != nullptr; sg = sg->next) {
2613 if (sg->aclList && checklist.fastCheck(sg->aclList).allowed()) {
2614 certProperties.signAlgorithm = (Ssl::CertSignAlgorithm)sg->alg;
2615 break;
2616 }
2617 }
2618 } else {// did not try to connect (e.g. client-first) or failed to connect
2619 // In case of an error while connecting to the secure server, use a
2620 // trusted certificate, with no mimicked fields and no adaptation
2621 // algorithms. There is nothing we can mimic, so we want to minimize the
2622 // number of warnings the user will have to see to get to the error page.
2623 // We will close the connection, so that the trust is not extended to
2624 // non-Squid content.
2625 certProperties.signAlgorithm = Ssl::algSignTrusted;
2626 }
2627
2628 assert(certProperties.signAlgorithm != Ssl::algSignEnd);
2629
2630 if (certProperties.signAlgorithm == Ssl::algSignUntrusted) {
2631 assert(port->secure.untrustedSigningCa.cert);
2632 certProperties.signWithX509.resetAndLock(port->secure.untrustedSigningCa.cert.get());
2633 certProperties.signWithPkey.resetAndLock(port->secure.untrustedSigningCa.pkey.get());
2634 } else {
2635 assert(port->secure.signingCa.cert.get());
2636 certProperties.signWithX509.resetAndLock(port->secure.signingCa.cert.get());
2637
2638 if (port->secure.signingCa.pkey)
2639 certProperties.signWithPkey.resetAndLock(port->secure.signingCa.pkey.get());
2640 }
2641 signAlgorithm = certProperties.signAlgorithm;
2642
2643 certProperties.signHash = Ssl::DefaultSignHash;
2644}
2645
2648{
2649 debugs(33, 5, "Finding SSL certificate for " << cacheKey << " in cache");
2650 const auto ssl_ctx_cache = Ssl::TheGlobalContextStorage().getLocalStorage(port->s);
2651 if (const auto ctx = ssl_ctx_cache ? ssl_ctx_cache->get(cacheKey) : nullptr) {
2652 if (Ssl::verifySslCertificate(*ctx, certProperties)) {
2653 debugs(33, 5, "Cached SSL certificate for " << certProperties.commonName << " is valid");
2654 return *ctx;
2655 } else {
2656 debugs(33, 5, "Cached SSL certificate for " << certProperties.commonName << " is out of date. Delete this certificate from cache");
2657 if (ssl_ctx_cache)
2658 ssl_ctx_cache->del(cacheKey);
2659 }
2660 }
2661 return Security::ContextPointer(nullptr);
2662}
2663
2664void
2666{
2667 const auto ssl_ctx_cache = Ssl::TheGlobalContextStorage().getLocalStorage(port->s);
2668 if (!ssl_ctx_cache || !ssl_ctx_cache->add(cacheKey, ctx)) {
2669 // If it is not in storage delete after using. Else storage deleted it.
2670 fd_table[clientConnection->fd].dynamicTlsContext = ctx;
2671 }
2672}
2673
2674void
2676{
2677 if (port->secure.generateHostCertificates) {
2678 Ssl::CertificateProperties certProperties;
2679 buildSslCertGenerationParams(certProperties);
2680
2681 // Disable caching for bumpPeekAndSplice mode
2686
2688 if (ctx) {
2689 getSslContextDone(ctx);
2690 return;
2691 }
2692 }
2693
2694#if USE_SSL_CRTD
2695 try {
2696 debugs(33, 5, "Generating SSL certificate for " << certProperties.commonName << " using ssl_crtd.");
2699 request_message.composeRequest(certProperties);
2700 debugs(33, 5, "SSL crtd request: " << request_message.compose().c_str());
2701 Ssl::Helper::Submit(request_message, sslCrtdHandleReplyWrapper, this);
2702 return;
2703 } catch (const std::exception &e) {
2704 debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtd " <<
2705 "request for " << certProperties.commonName <<
2706 " certificate: " << e.what() << "; will now block to " <<
2707 "generate that certificate.");
2708 // fall through to do blocking in-process generation.
2709 }
2710#endif // USE_SSL_CRTD
2711
2712 debugs(33, 5, "Generating SSL certificate for " << certProperties.commonName);
2715 auto ssl = fd_table[clientConnection->fd].ssl.get();
2716 if (!Ssl::configureSSL(ssl, certProperties, *port))
2717 debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
2718
2721 } else {
2723 if (dynCtx && !sslBumpCertKey.isEmpty())
2725 getSslContextDone(dynCtx);
2726 }
2727 return;
2728 }
2729
2731 getSslContextDone(nil);
2732}
2733
2734void
2736{
2737 if (port->secure.generateHostCertificates && !ctx) {
2738 debugs(33, 2, "Failed to generate TLS context for " << tlsConnectHostOrIp);
2739 }
2740
2741 // If generated ssl context = nullptr, try to use static ssl context.
2742 if (!ctx) {
2743 if (!port->secure.staticContext) {
2744 debugs(83, DBG_IMPORTANT, "Closing " << clientConnection->remote << " as lacking TLS context");
2746 return;
2747 } else {
2748 debugs(33, 5, "Using static TLS context.");
2749 ctx = port->secure.staticContext;
2750 }
2751 }
2752
2753 if (!httpsCreate(this, ctx))
2754 return;
2755
2756 // bumped intercepted conns should already have Config.Timeout.request set
2757 // but forwarded connections may only have Config.Timeout.lifetime. [Re]set
2758 // to make sure the connection does not get stuck on non-SSL clients.
2760
2761 switchedToHttps_ = true;
2762
2763 auto ssl = fd_table[clientConnection->fd].ssl.get();
2764 BIO *b = SSL_get_rbio(ssl);
2765 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(BIO_get_data(b));
2766 bio->setReadBufData(inBuf);
2767 inBuf.clear();
2769}
2770
2771void
2773{
2775 Must(http->request);
2776 auto &request = http->request;
2777
2778 // Depending on receivedFirstByte_, we are at the start of either an
2779 // established CONNECT tunnel with the client or an intercepted TCP (and
2780 // presumably TLS) connection from the client. Expect TLS Client Hello.
2781 const auto insideConnectTunnel = receivedFirstByte_;
2782 debugs(33, 5, (insideConnectTunnel ? "post-CONNECT " : "raw TLS ") << clientConnection);
2783
2784 tlsConnectHostOrIp = request->url.hostOrIp();
2785 tlsConnectPort = request->url.port();
2786 resetSslCommonName(request->url.host());
2787
2788 // We are going to read new request
2789 flags.readMore = true;
2790
2791 // keep version major.minor details the same.
2792 // but we are now performing the HTTPS handshake traffic
2794
2795 // If sslServerBump is set, then we have decided to deny CONNECT
2796 // and now want to switch to SSL to send the error to the client
2797 // without even peeking at the origin server certificate.
2798 if (bumpServerMode == Ssl::bumpServerFirst && !sslServerBump) {
2799 request->flags.sslPeek = true;
2800 sslServerBump = new Ssl::ServerBump(http);
2801 } else if (bumpServerMode == Ssl::bumpPeek || bumpServerMode == Ssl::bumpStare) {
2802 request->flags.sslPeek = true;
2803 sslServerBump = new Ssl::ServerBump(http, nullptr, bumpServerMode);
2804 }
2805
2806 // commSetConnTimeout() was called for this request before we switched.
2807 // Fix timeout to request_start_timeout
2809 // Also reset receivedFirstByte_ flag to allow this timeout work in the case we have
2810 // a bumped "connect" request on non transparent port.
2811 receivedFirstByte_ = false;
2812 // Get more data to peek at Tls
2813 parsingTlsHandshake = true;
2814
2815 // If the protocol has changed, then reset preservingClientData_.
2816 // Otherwise, its value initially set in start() is still valid/fresh.
2817 // shouldPreserveClientData() uses parsingTlsHandshake which is reset above.
2818 if (insideConnectTunnel)
2820
2821 readSomeData();
2822}
2823
2824void
2826{
2828
2829 assert(!inBuf.isEmpty());
2831 fd_note(clientConnection->fd, "Parsing TLS handshake");
2832
2833 // stops being nil if we fail to parse the handshake
2834 ErrorDetail::Pointer parseErrorDetails;
2835
2836 try {
2837 if (!tlsParser.parseHello(inBuf)) {
2838 // need more data to finish parsing
2839 readSomeData();
2840 return;
2841 }
2842 }
2843 catch (const TextException &ex) {
2844 debugs(83, 2, "exception: " << ex);
2845 parseErrorDetails = new ExceptionErrorDetail(ex.id());
2846 }
2847 catch (...) {
2848 debugs(83, 2, "exception: " << CurrentException);
2849 static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_PARSE");
2850 parseErrorDetails = d;
2851 }
2852
2853 parsingTlsHandshake = false;
2854
2855 // client data may be needed for splicing and for
2856 // tunneling unsupportedProtocol after an error
2858
2859 // Even if the parser failed, each TLS detail should either be set
2860 // correctly or still be "unknown"; copying unknown detail is a no-op.
2863 if (details && !details->serverName.isEmpty()) {
2864 resetSslCommonName(details->serverName.c_str());
2865 tlsClientSni_ = details->serverName;
2866 }
2867
2868 // We should disable read/write handlers
2870
2871 if (parseErrorDetails) {
2873 Must(context && context->http);
2874 HttpRequest::Pointer request = context->http->request;
2875 debugs(83, 5, "Got something other than TLS Client Hello. Cannot SslBump.");
2876 updateError(ERR_PROTOCOL_UNKNOWN, parseErrorDetails);
2879 return;
2880 }
2881
2882 if (!sslServerBump || sslServerBump->act.step1 == Ssl::bumpClientFirst) { // Either means client-first.
2884 return;
2886 debugs(83, 5, "server-first skips step2; start forwarding the request");
2889 ClientHttpRequest *http = context ? context->http : nullptr;
2890 // will call httpsPeeked() with certificate and connection, eventually
2892 } else {
2895 }
2896}
2897
2898static void
2900{
2901 ConnStateData *connState = (ConnStateData *) data;
2902
2903 // if the connection is closed or closing, just return.
2904 if (!connState->isOpen())
2905 return;
2906
2907 debugs(33, 5, "Answer: " << answer << " kind:" << answer.kind);
2908 assert(connState->serverBump());
2909 Ssl::BumpMode bumpAction;
2910 if (answer.allowed()) {
2911 bumpAction = (Ssl::BumpMode)answer.kind;
2912 } else
2913 bumpAction = Ssl::bumpSplice;
2914
2915 connState->serverBump()->act.step2 = bumpAction;
2916 connState->sslBumpMode = bumpAction;
2917 Http::StreamPointer context = connState->pipeline.front();
2918 if (ClientHttpRequest *http = (context ? context->http : nullptr))
2919 http->al->ssl.bumpMode = bumpAction;
2920
2921 if (bumpAction == Ssl::bumpTerminate) {
2922 connState->clientConnection->close();
2923 } else if (bumpAction != Ssl::bumpSplice) {
2924 connState->startPeekAndSplice();
2925 } else if (!connState->splice())
2926 connState->clientConnection->close();
2927}
2928
2929bool
2931{
2932 // normally we can splice here, because we just got client hello message
2933
2934 // fde::ssl/tls_read_method() probably reads from our own inBuf. If so, then
2935 // we should not lose any raw bytes when switching to raw I/O here.
2936 if (fd_table[clientConnection->fd].ssl.get())
2937 fd_table[clientConnection->fd].useDefaultIo();
2938
2939 // XXX: assuming that there was an HTTP/1.1 CONNECT to begin with...
2940 // reset the current protocol to HTTP/1.1 (was "HTTPS" for the bumping process)
2942 assert(!pipeline.empty());
2944 Must(context);
2945 Must(context->http);
2946 ClientHttpRequest *http = context->http;
2947 HttpRequest::Pointer request = http->request;
2948 context->finished();
2949 if (transparent()) {
2950 // For transparent connections, make a new fake CONNECT request, now
2951 // with SNI as target. doCallout() checks, adaptations may need that.
2952 return fakeAConnectRequest("splice", preservedClientData);
2953 } else {
2954 // For non transparent connections make a new tunneled CONNECT, which
2955 // also sets the HttpRequest::flags::forceTunnel flag to avoid
2956 // respond with "Connection Established" to the client.
2957 // This fake CONNECT request required to allow use of SNI in
2958 // doCallout() checks and adaptations.
2959 return initiateTunneledRequest(request, "splice", preservedClientData);
2960 }
2961}
2962
2963void
2965{
2966 // This is the Step2 of the SSL bumping
2969 ClientHttpRequest *http = context ? context->http : nullptr;
2970
2973 // Run a accessList check to check if want to splice or continue bumping
2974
2976 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpNone));
2977 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpClientFirst));
2978 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpServerFirst));
2979 fillChecklist(*acl_checklist);
2981 return;
2982 }
2983
2984 // will call httpsPeeked() with certificate and connection, eventually
2985 Security::ContextPointer unConfiguredCTX(Ssl::createSSLContext(port->secure.signingCa.cert, port->secure.signingCa.pkey, port->secure));
2986 fd_table[clientConnection->fd].dynamicTlsContext = unConfiguredCTX;
2987
2988 if (!httpsCreate(this, unConfiguredCTX))
2989 return;
2990
2991 switchedToHttps_ = true;
2992
2993 auto ssl = fd_table[clientConnection->fd].ssl.get();
2994 BIO *b = SSL_get_rbio(ssl);
2995 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(BIO_get_data(b));
2996 bio->setReadBufData(inBuf);
2997 bio->hold(true);
2998
2999 // We have successfully parsed client Hello, but our TLS handshake parser is
3000 // forgiving. Now we use a TLS library to parse the same bytes, so that we
3001 // can honor on_unsupported_protocol if needed. If there are no errors, we
3002 // expect Security::Accept() to ask us to write (our) TLS server Hello. We
3003 // also allow an ioWantRead result in case some fancy TLS extension that
3004 // Squid does not yet understand requires reading post-Hello client bytes.
3005 const auto handshakeResult = acceptTls();
3006 if (!handshakeResult.wantsIo())
3007 return handleSslBumpHandshakeError(handshakeResult);
3008
3009 // We need to reset inBuf here, to be used by incoming requests in the case
3010 // of SSL bump
3011 inBuf.clear();
3012
3013 debugs(83, 5, "Peek and splice at step2 done. Start forwarding the request!!! ");
3016}
3017
3019void
3021{
3022 auto errCategory = ERR_NONE;
3023
3024 switch (handshakeResult.category) {
3026 static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_UNEXPECTED_SUCCESS");
3027 updateError(errCategory = ERR_GATEWAY_FAILURE, d);
3028 break;
3029 }
3030
3032 static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_UNEXPECTED_READ");
3033 updateError(errCategory = ERR_GATEWAY_FAILURE, d);
3034 break;
3035 }
3036
3038 static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_UNEXPECTED_WRITE");
3039 updateError(errCategory = ERR_GATEWAY_FAILURE, d);
3040 break;
3041 }
3042
3044 debugs(83, (handshakeResult.important ? DBG_IMPORTANT : 2), "ERROR: Cannot SslBump-accept a TLS connection" <<
3045 Debug::Extra << "problem: " << WithExtras(handshakeResult));
3046 updateError(errCategory = ERR_SECURE_ACCEPT_FAIL, handshakeResult.errorDetail);
3047 break;
3048
3049 }
3050
3051 if (!tunnelOnError(errCategory))
3053}
3054
3055void
3057{
3058 auto ssl = fd_table[clientConnection->fd].ssl.get();
3059 BIO *b = SSL_get_rbio(ssl);
3060 assert(b);
3061 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(BIO_get_data(b));
3062
3063 debugs(33, 5, "PeekAndSplice mode, proceed with client negotiation. Current state:" << SSL_state_string_long(ssl));
3064 bio->hold(false);
3065
3067 switchedToHttps_ = true;
3068}
3069
3070void
3072{
3073 Must(sslServerBump != nullptr);
3075 Must(pipeline.empty() || pipeline.front()->http == nullptr || pipeline.front()->http->request == pic.request.getRaw());
3076
3077 if (Comm::IsConnOpen(pic.connection)) {
3079 debugs(33, 5, "bumped HTTPS server: " << tlsConnectHostOrIp);
3080 } else
3081 debugs(33, 5, "Error while bumping: " << tlsConnectHostOrIp);
3082
3084}
3085
3086#endif /* USE_OPENSSL */
3087
3088bool
3089ConnStateData::initiateTunneledRequest(HttpRequest::Pointer const &cause, const char *reason, const SBuf &payload)
3090{
3091 // fake a CONNECT request to force connState to tunnel
3092 SBuf connectHost;
3093 AnyP::Port connectPort;
3094
3095 if (pinning.serverConnection != nullptr) {
3096 static char ip[MAX_IPSTRLEN];
3097 connectHost = pinning.serverConnection->remote.toStr(ip, sizeof(ip));
3098 if (const auto remotePort = pinning.serverConnection->remote.port())
3099 connectPort = remotePort;
3100 } else if (cause) {
3101 connectHost = cause->url.hostOrIp();
3102 connectPort = cause->url.port();
3103#if USE_OPENSSL
3104 } else if (!tlsConnectHostOrIp.isEmpty()) {
3105 connectHost = tlsConnectHostOrIp;
3106 connectPort = tlsConnectPort;
3107#endif
3108 } else if (transparent()) {
3109 static char ip[MAX_IPSTRLEN];
3110 connectHost = clientConnection->local.toStr(ip, sizeof(ip));
3111 connectPort = clientConnection->local.port();
3112 }
3113
3114 if (!connectPort) {
3115 // Typical cases are malformed HTTP requests on http_port and malformed
3116 // TLS handshakes on non-bumping https_port. TODO: Discover these
3117 // problems earlier so that they can be classified/detailed better.
3118 debugs(33, 2, "Not able to compute URL, abort request tunneling for " << reason);
3119 // TODO: throw when NonBlockingCheck() callbacks gain job protections
3120 static const auto d = MakeNamedErrorDetail("TUNNEL_TARGET");
3122 return false;
3123 }
3124
3125 debugs(33, 2, "Request tunneling for " << reason);
3126 const auto http = buildFakeRequest(connectHost, *connectPort, payload);
3127 HttpRequest::Pointer request = http->request;
3128 request->flags.forceTunnel = true;
3129 http->calloutContext = new ClientRequestContext(http);
3130 http->doCallouts();
3131 clientProcessRequestFinished(this, request);
3132 return true;
3133}
3134
3135bool
3136ConnStateData::fakeAConnectRequest(const char *reason, const SBuf &payload)
3137{
3138 debugs(33, 2, "fake a CONNECT request to force connState to tunnel for " << reason);
3139
3140 SBuf connectHost;
3142 const unsigned short connectPort = clientConnection->local.port();
3143
3144#if USE_OPENSSL
3145 if (!tlsClientSni_.isEmpty())
3146 connectHost.assign(tlsClientSni_);
3147 else
3148#endif
3149 {
3150 static char ip[MAX_IPSTRLEN];
3151 clientConnection->local.toHostStr(ip, sizeof(ip));
3152 connectHost.assign(ip);
3153 }
3154
3155 ClientHttpRequest *http = buildFakeRequest(connectHost, connectPort, payload);
3156
3157 http->calloutContext = new ClientRequestContext(http);
3158 HttpRequest::Pointer request = http->request;
3159 http->doCallouts();
3160 clientProcessRequestFinished(this, request);
3161 return true;
3162}
3163
3165ConnStateData::buildFakeRequest(SBuf &useHost, const AnyP::KnownPort usePort, const SBuf &payload)
3166{
3167 ClientHttpRequest *http = new ClientHttpRequest(this);
3168 Http::Stream *stream = new Http::Stream(clientConnection, http);
3169
3170 StoreIOBuffer tempBuffer;
3171 tempBuffer.data = stream->reqbuf;
3172 tempBuffer.length = HTTP_REQBUF_SZ;
3173
3174 ClientStreamData newServer = new clientReplyContext(http);
3175 ClientStreamData newClient = stream;
3178 clientSocketDetach, newClient, tempBuffer);
3179
3180 stream->flags.parsed_ok = 1; // Do we need it?
3181 stream->mayUseConnection(true);
3183 stream->registerWithConn();
3184
3185 const auto mx = MasterXaction::MakePortful(port);
3186 mx->tcpClient = clientConnection;
3187 // Setup Http::Request object. Maybe should be replaced by a call to (modified)
3188 // clientProcessRequest
3189 HttpRequest::Pointer request = new HttpRequest(mx);
3190 request->url.setScheme(AnyP::PROTO_AUTHORITY_FORM, nullptr);
3191 request->method = Http::METHOD_CONNECT;
3192 request->url.host(useHost.c_str());
3193 request->url.port(usePort);
3194
3195 http->uri = SBufToCstring(request->effectiveRequestUri());
3196 http->initRequest(request.getRaw());
3197
3198 request->manager(this, http->al);
3199
3200 request->header.putStr(Http::HOST, useHost.c_str());
3201
3202 request->sources |= ((switchedToHttps() || port->transport.protocol == AnyP::PROTO_HTTPS) ? Http::Message::srcHttps : Http::Message::srcHttp);
3203#if USE_AUTH
3204 if (getAuth())
3205 request->auth_user_request = getAuth();
3206#endif
3207
3208 inBuf = payload;
3209 flags.readMore = false;
3210
3211 return http;
3212}
3213
3215static bool
3217{
3218 if (!Comm::IsConnOpen(c)) {
3219 Must(NHttpSockets > 0); // we tried to open some
3220 --NHttpSockets; // there will be fewer sockets than planned
3221 Must(HttpSockets[NHttpSockets] < 0); // no extra fds received
3222
3223 if (!NHttpSockets) // we could not open any listen sockets at all
3224 fatalf("Unable to open %s",FdNote(portType));
3225
3226 return false;
3227 }
3228 return true;
3229}
3230
3232static bool
3234{
3235 bool found = false;
3236 for (int i = 0; i < NHttpSockets && !found; ++i) {
3237 if ((found = HttpSockets[i] < 0))
3238 HttpSockets[i] = conn->fd;
3239 }
3240 return found;
3241}
3242
3243static void
3245{
3246 const auto savedContext = CodeContext::Current();
3247 for (AnyP::PortCfgPointer s = HttpPortList; s != nullptr; s = s->next) {
3249 const SBuf &scheme = AnyP::UriScheme(s->transport.protocol).image();
3250
3252 debugs(1, DBG_IMPORTANT, "WARNING: You have too many '" << scheme << "_port' lines." <<
3253 Debug::Extra << "The limit is " << MAXTCPLISTENPORTS << " HTTP ports.");
3254 continue;
3255 }
3256
3257#if USE_OPENSSL
3258 if (s->flags.tunnelSslBumping) {
3259 if (!Config.accessList.ssl_bump) {
3260 debugs(33, DBG_IMPORTANT, "WARNING: No ssl_bump configured. Disabling ssl-bump on " << scheme << "_port " << s->s);
3261 s->flags.tunnelSslBumping = false;
3262 }
3263 if (!s->secure.staticContext && !s->secure.generateHostCertificates) {
3264 debugs(1, DBG_IMPORTANT, "Will not bump SSL at " << scheme << "_port " << s->s << " due to TLS initialization failure.");
3265 s->flags.tunnelSslBumping = false;
3266 if (s->transport.protocol == AnyP::PROTO_HTTP)
3267 s->secure.encryptTransport = false;
3268 }
3269 if (s->flags.tunnelSslBumping) {
3270 // Create ssl_ctx cache for this port.
3271 Ssl::TheGlobalContextStorage().addLocalStorage(s->s, s->secure.dynamicCertMemCacheSize);
3272 }
3273 }
3274#endif
3275
3276 if (s->secure.encryptTransport && !s->secure.staticContext) {
3277 debugs(1, DBG_CRITICAL, "ERROR: Ignoring " << scheme << "_port " << s->s << " due to TLS context initialization failure.");
3278 continue;
3279 }
3280
3281 const auto protocol = s->transport.protocol;
3282 assert(protocol == AnyP::PROTO_HTTP || protocol == AnyP::PROTO_HTTPS);
3283 const auto isHttps = protocol == AnyP::PROTO_HTTPS;
3284 using AcceptCall = CommCbFunPtrCallT<CommAcceptCbPtrFun>;
3285 RefCount<AcceptCall> subCall = commCbCall(5, 5, isHttps ? "httpsAccept" : "httpAccept",
3288 }
3289 CodeContext::Reset(savedContext);
3290}
3291
3292void
3294{
3295 // Fill out a Comm::Connection which IPC will open as a listener for us
3296 port->listenConn = new Comm::Connection;
3297 port->listenConn->local = port->s;
3298 port->listenConn->flags =
3300 (port->flags.tproxyIntercept ? COMM_TRANSPARENT : 0) |
3301 (port->flags.natIntercept ? COMM_INTERCEPTION : 0) |
3302 (port->workerQueues ? COMM_REUSEPORT : 0);
3303
3304 // route new connections to subCall
3305 typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
3307 const auto listenCall =
3308 asyncCall(33, 2, "clientListenerConnectionOpened",
3310 port, fdNote, sub));
3311 AsyncCallback<Ipc::StartListeningAnswer> callback(listenCall);
3312 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, port->listenConn, fdNote, callback);
3313
3316 ++NHttpSockets;
3317}
3318
3320static void
3322{
3323 Must(s != nullptr);
3324
3325 if (!OpenedHttpSocket(s->listenConn, portTypeNote))
3326 return;
3327
3328 Must(Comm::IsConnOpen(s->listenConn));
3329
3330 // TCP: setup a job to handle accept() with subscribed handler
3331 AsyncJob::Start(new Comm::TcpAcceptor(s, FdNote(portTypeNote), sub));
3332
3333 debugs(1, Important(13), "Accepting " <<
3334 (s->flags.natIntercept ? "NAT intercepted " : "") <<
3335 (s->flags.tproxyIntercept ? "TPROXY intercepted " : "") <<
3336 (s->flags.tunnelSslBumping ? "SSL bumped " : "") <<
3337 (s->flags.accelSurrogate ? "reverse-proxy " : "")
3338 << FdNote(portTypeNote) << " connections at "
3339 << s->listenConn);
3340
3341 Must(AddOpenedHttpSocket(s->listenConn)); // otherwise, we have received a fd we did not ask for
3342
3343#if HAVE_LIBSYSTEMD
3344 // When the very first port opens, tell systemd we are able to serve connections.
3345 // Subsequent sd_notify() calls, including calls during reconfiguration,
3346 // do nothing because the first call parameter is 1.
3347 // XXX: Send the notification only after opening all configured ports.
3349 const auto result = sd_notify(1, "READY=1");
3350 if (result < 0) {
3351 debugs(1, DBG_IMPORTANT, "WARNING: failed to send start-up notification to systemd" <<
3352 Debug::Extra << "sd_notify() error: " << xstrerr(-result));
3353 }
3354 }
3355#endif
3356}
3357
3358void
3360{
3363
3364 if (NHttpSockets < 1)
3365 fatal("No HTTP, HTTPS, or FTP ports configured");
3366}
3367
3368void
3370{
3371 const auto savedContext = CodeContext::Current();
3372 for (AnyP::PortCfgPointer s = HttpPortList; s != nullptr; s = s->next) {
3374 if (s->listenConn != nullptr) {
3375 debugs(1, Important(14), "Closing HTTP(S) port " << s->listenConn->local);
3376 s->listenConn->close();
3377 s->listenConn = nullptr;
3378 }
3379 }
3380 CodeContext::Reset(savedContext);
3381
3383
3384 // TODO see if we can drop HttpSockets array entirely */
3385 for (int i = 0; i < NHttpSockets; ++i) {
3386 HttpSockets[i] = -1;
3387 }
3388
3389 NHttpSockets = 0;
3390}
3391
3392int
3394{
3395 SBuf vary(request->vary_headers);
3396 const auto &reply = entry->mem().freshestReply();
3397 auto has_vary = reply.header.has(Http::HdrType::VARY);
3398#if X_ACCELERATOR_VARY
3399
3400 has_vary |=
3401 reply.header.has(Http::HdrType::HDR_X_ACCELERATOR_VARY);
3402#endif
3403
3404 if (!has_vary || entry->mem_obj->vary_headers.isEmpty()) {
3405 if (!vary.isEmpty()) {
3406 /* Oops... something odd is going on here.. */
3407 debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary object on second attempt, '" <<
3408 entry->mem_obj->urlXXX() << "' '" << vary << "'");
3409 request->vary_headers.clear();
3410 return VARY_CANCEL;
3411 }
3412
3413 if (!has_vary) {
3414 /* This is not a varying object */
3415 return VARY_NONE;
3416 }
3417
3418 /* virtual "vary" object found. Calculate the vary key and
3419 * continue the search
3420 */
3421 vary = httpMakeVaryMark(request, &reply);
3422
3423 if (!vary.isEmpty()) {
3424 request->vary_headers = vary;
3425 return VARY_OTHER;
3426 } else {
3427 /* Ouch.. we cannot handle this kind of variance */
3428 /* XXX This cannot really happen, but just to be complete */
3429 return VARY_CANCEL;
3430 }
3431 } else {
3432 if (vary.isEmpty()) {
3433 vary = httpMakeVaryMark(request, &reply);
3434
3435 if (!vary.isEmpty())
3436 request->vary_headers = vary;
3437 }
3438
3439 if (vary.isEmpty()) {
3440 /* Ouch.. we cannot handle this kind of variance */
3441 /* XXX This cannot really happen, but just to be complete */
3442 return VARY_CANCEL;
3443 } else if (vary.cmp(entry->mem_obj->vary_headers) == 0) {
3444 return VARY_MATCH;
3445 } else {
3446 /* Oops.. we have already been here and still haven't
3447 * found the requested variant. Bail out
3448 */
3449 debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary match on second attempt, '" <<
3450 entry->mem_obj->urlXXX() << "' '" << vary << "'");
3451 return VARY_CANCEL;
3452 }
3453 }
3454}
3455
3458{
3459 auto checklist = ACLFilledChecklist::Make(acl, nullptr);
3460 clientAclChecklistFill(*checklist, http);
3461 return checklist;
3462}
3463
3464void
3466{
3467 assert(http);
3468
3469 if (!checklist.request && http->request)
3470 checklist.setRequest(http->request);
3471
3472 if (!checklist.al && http->al) {
3473 checklist.updateAle(http->al);
3474 checklist.syncAle(http->request, http->log_uri);
3475 }
3476
3477 if (const auto conn = http->getConn())
3478 checklist.setConn(conn); // may already be set
3479}
3480
3481void
3483{
3484 const auto context = pipeline.front();
3485 if (const auto http = context ? context->http : nullptr)
3486 return clientAclChecklistFill(checklist, http); // calls checklist.setConn()
3487
3488 // no requests, but we always have connection-level details
3489 // TODO: ACL checks should not require a mutable ConnStateData. Adjust the
3490 // code that accidentally violates that principle to remove this const_cast!
3491 checklist.setConn(const_cast<ConnStateData*>(this));
3492
3493 // Set other checklist fields inside our fillConnectionLevelDetails() rather
3494 // than here because clientAclChecklistFill() code path calls that method
3495 // (via ACLFilledChecklist::setConn()) rather than calling us directly.
3496}
3497
3498void
3500{
3501 assert(checklist.conn() == this);
3503
3504 if (!checklist.request) { // preserve (better) addresses supplied by setRequest()
3505 checklist.src_addr = clientConnection->remote;
3506 checklist.my_addr = clientConnection->local; // TODO: or port->s?
3507 }
3508
3509#if USE_OPENSSL
3510 if (!checklist.sslErrors && sslServerBump)
3511 checklist.sslErrors = sslServerBump->sslErrors();
3512#endif
3513}
3514
3515bool
3520
3523{
3524 bodyPipe = new BodyPipe(this);
3525 if (size >= 0)
3527 else
3529 return bodyPipe;
3530}
3531
3532int64_t
3534{
3535 if (!bodyPipe)
3536 return 0; // request without a body or read/produced all body bytes
3537
3538 if (!bodyPipe->bodySizeKnown())
3539 return -1; // probably need to read more, but we cannot be sure
3540
3541 const int64_t needToProduce = bodyPipe->unproducedSize();
3542 const int64_t haveAvailable = static_cast<int64_t>(inBuf.length());
3543
3544 if (needToProduce <= haveAvailable)
3545 return 0; // we have read what we need (but are waiting for pipe space)
3546
3547 return needToProduce - haveAvailable;
3548}
3549
3550void
3552{
3553 debugs(33, 4, "receiving error (" << clientConnection << "): " << error <<
3554 "; old sending error: " <<
3555 (stoppedSending() ? stoppedSending_ : "none"));
3556
3557 if (const char *oldError = stoppedReceiving()) {
3558 debugs(33, 3, "already stopped receiving: " << oldError);
3559 return; // nothing has changed as far as this connection is concerned
3560 }
3561
3563
3564 if (const char *sendError = stoppedSending()) {
3565 debugs(33, 3, "closing because also stopped sending: " << sendError);
3567 }
3568}
3569
3570void
3572{
3573 if (bodyPipe != nullptr) {
3574 debugs(33, 4, "no consumer for virgin body " << bodyPipe->status());
3576 }
3577}
3578
3580void
3582{
3583 Must(bodyPipe != nullptr);
3584 debugs(33, 5, "start dechunking" << bodyPipe->status());
3587}
3588
3590void
3592{
3593 debugs(33, 5, "finish dechunking: " << withSuccess);
3594
3595 if (bodyPipe != nullptr) {
3596 debugs(33, 7, "dechunked tail: " << bodyPipe->status());
3597 BodyPipe::Pointer myPipe = bodyPipe;
3598 stopProducingFor(bodyPipe, withSuccess); // sets bodyPipe->bodySize()
3599 Must(!bodyPipe); // we rely on it being nil after we are done with body
3600 if (withSuccess) {
3601 Must(myPipe->bodySizeKnown());
3603 if (context != nullptr && context->http && context->http->request)
3604 context->http->request->setContentLength(myPipe->bodySize());
3605 }
3606 }
3607
3608 delete bodyParser;
3609 bodyParser = nullptr;
3610}
3611
3612// XXX: this is an HTTP/1-only operation
3613void
3615{
3616 if (const auto context = pipeline.front()) {
3617 if (context->http)
3618 context->http->al->reply = msg.reply;
3619 }
3620
3621 if (!isOpen()) {
3622 debugs(33, 3, "ignoring 1xx due to earlier closure");
3623 return;
3624 }
3625
3626 // HTTP/1 1xx status messages are only valid when there is a transaction to trigger them
3627 if (!pipeline.empty()) {
3628 HttpReply::Pointer rep(msg.reply);
3629 Must(rep);
3630 // remember the callback
3632
3635
3636 if (!writeControlMsgAndCall(rep.getRaw(), call)) {
3637 // but still inform the caller (so it may resume its operation)
3639 }
3640 return;
3641 }
3642
3643 debugs(33, 3, " closing due to missing context for 1xx");
3645}
3646
3647void
3649{
3651
3652 if (Http::StreamPointer deferredRequest = pipeline.front()) {
3653 debugs(33, 3, clientConnection << ": calling PushDeferredIfNeeded after control msg wrote");
3654 ClientSocketContextPushDeferredIfNeeded(deferredRequest, this);
3655 }
3656}
3657
3659void
3661{
3662 // FwdState might repin a failed connection sooner than this close
3663 // callback is called for the failed connection.
3664 assert(pinning.serverConnection == io.conn);
3665 pinning.closeHandler = nullptr; // Comm unregisters handlers before calling
3666 const bool sawZeroReply = pinning.zeroReply; // reset when unpinning
3667 pinning.serverConnection->noteClosure();
3668 unpinConnection(false);
3669
3670 if (sawZeroReply && clientConnection != nullptr) {
3671 debugs(33, 3, "Closing client connection on pinned zero reply.");
3673 }
3674
3675}
3676
3677void
3679{
3680 pinConnection(pinServer, *request);
3681}
3682
3683void
3685{
3686 Must(pic.connection);
3687 Must(pic.request);
3688 pinConnection(pic.connection, *pic.request);
3689
3690 // monitor pinned server connection for remote-end closures.
3692
3693 if (pipeline.empty())
3694 kick(); // in case parseRequests() was blocked by a busy pic.connection
3695}
3696
3698void
3700{
3701 if (Comm::IsConnOpen(pinning.serverConnection) &&
3702 pinning.serverConnection->fd == pinServer->fd) {
3703 debugs(33, 3, "already pinned" << pinServer);
3704 return;
3705 }
3706
3707 unpinConnection(true); // closes pinned connection, if any, and resets fields
3708
3709 pinning.serverConnection = pinServer;
3710
3711 debugs(33, 3, pinning.serverConnection);
3712
3713 Must(pinning.serverConnection != nullptr);
3714
3715 const char *pinnedHost = "[unknown]";
3716 pinning.host = xstrdup(request.url.host());
3717 pinning.port = request.url.port();
3718 pinnedHost = pinning.host;
3719 pinning.pinned = true;
3720 pinning.auth = request.flags.connectionAuth;
3721 char stmp[MAX_IPSTRLEN];
3722 char desc[FD_DESC_SZ];
3723 const auto peer = pinning.peer();
3724 snprintf(desc, FD_DESC_SZ, "%s pinned connection for %s (%d)",
3725 (pinning.auth || !peer) ? pinnedHost : peer->name,
3728 fd_note(pinning.serverConnection->fd, desc);
3729
3731 pinning.closeHandler = JobCallback(33, 5,
3733 // remember the pinned connection so that cb does not unpin a fresher one
3734 typedef CommCloseCbParams Params;
3735 Params &params = GetCommParams<Params>(pinning.closeHandler);
3736 params.conn = pinning.serverConnection;
3737 comm_add_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
3738}
3739
3742void
3744{
3745 if (pinning.readHandler != nullptr)
3746 return; // already monitoring
3747
3749 pinning.readHandler = JobCallback(33, 3,
3751 Comm::Read(pinning.serverConnection, pinning.readHandler);
3752}
3753
3754void
3756{
3757 if (pinning.readHandler != nullptr) {
3758 Comm::ReadCancel(pinning.serverConnection->fd, pinning.readHandler);
3759 pinning.readHandler = nullptr;
3760 }
3761}
3762
3763#if USE_OPENSSL
3764bool
3766{
3767 // A ready-for-reading connection means that the TLS server either closed
3768 // the connection, sent us some unexpected HTTP data, or started TLS
3769 // renegotiations. We should close the connection except for the last case.
3770
3771 Must(pinning.serverConnection != nullptr);
3772 auto ssl = fd_table[pinning.serverConnection->fd].ssl.get();
3773 if (!ssl)
3774 return false;
3775
3776 char buf[1];
3777 const int readResult = SSL_read(ssl, buf, sizeof(buf));
3778
3779 if (readResult > 0 || SSL_pending(ssl) > 0) {
3780 debugs(83, 2, pinning.serverConnection << " TLS application data read");
3781 return false;
3782 }
3783
3784 switch(const int error = SSL_get_error(ssl, readResult)) {
3785 case SSL_ERROR_WANT_WRITE:
3786 debugs(83, DBG_IMPORTANT, pinning.serverConnection << " TLS SSL_ERROR_WANT_WRITE request for idle pinned connection");
3787 [[fallthrough]]; // to restart monitoring, for now
3788
3789 case SSL_ERROR_NONE:
3790 case SSL_ERROR_WANT_READ:
3792 return true;
3793
3794 default:
3795 debugs(83, 2, pinning.serverConnection << " TLS error: " << error);
3796 return false;
3797 }
3798
3799 // not reached
3800 return true;
3801}
3802#endif
3803
3806void
3808{
3809 pinning.readHandler = nullptr; // Comm unregisters handlers before calling
3810
3811 if (io.flag == Comm::ERR_CLOSING)
3812 return; // close handler will clean up
3813
3814 Must(pinning.serverConnection == io.conn);
3815
3816#if USE_OPENSSL
3818 return;
3819#endif
3820
3821 const bool clientIsIdle = pipeline.empty();
3822
3823 debugs(33, 3, "idle pinned " << pinning.serverConnection << " read " <<
3824 io.size << (clientIsIdle ? " with idle client" : ""));
3825
3826 pinning.serverConnection->close();
3827
3828 // If we are still sending data to the client, do not close now. When we are done sending,
3829 // ConnStateData::kick() checks pinning.serverConnection and will close.
3830 // However, if we are idle, then we must close to inform the idle client and minimize races.
3831 if (clientIsIdle && clientConnection != nullptr)
3833}
3834
3837{
3838 debugs(33, 7, pinning.serverConnection);
3839 Must(request);
3840
3841 const auto pinningError = [&](const err_type type) {
3842 unpinConnection(true);
3843 HttpRequestPointer requestPointer = request;
3844 return ErrorState::NewForwarding(type, requestPointer, ale);
3845 };
3846
3847 if (!Comm::IsConnOpen(pinning.serverConnection))
3848 throw pinningError(ERR_ZERO_SIZE_OBJECT);
3849
3850 if (pinning.auth && pinning.host && strcasecmp(pinning.host, request->url.host()) != 0)
3851 throw pinningError(ERR_CANNOT_FORWARD); // or generalize ERR_CONFLICT_HOST
3852
3853 if (pinning.port != request->url.port())
3854 throw pinningError(ERR_CANNOT_FORWARD); // or generalize ERR_CONFLICT_HOST
3855
3856 if (pinning.serverConnection->toGoneCachePeer())
3857 throw pinningError(ERR_ZERO_SIZE_OBJECT);
3858
3859 if (pinning.peerAccessDenied)
3860 throw pinningError(ERR_CANNOT_FORWARD); // or generalize ERR_FORWARDING_DENIED
3861
3863 return pinning.serverConnection;
3864}
3865
3868{
3869 if (const auto connManager = request ? request->pinnedConnection() : nullptr)
3870 return connManager->borrowPinnedConnection(request, ale);
3871
3872 // ERR_CANNOT_FORWARD is somewhat misleading here; we can still forward, but
3873 // there is no point since the client connection is now gone
3874 HttpRequestPointer requestPointer = request;
3875 throw ErrorState::NewForwarding(ERR_CANNOT_FORWARD, requestPointer, ale);
3876}
3877
3878void
3880{
3881 debugs(33, 3, pinning.serverConnection);
3882
3883 if (Comm::IsConnOpen(pinning.serverConnection)) {
3884 if (pinning.closeHandler != nullptr) {
3885 comm_remove_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
3886 pinning.closeHandler = nullptr;
3887 }
3888
3890
3891 // close the server side socket if requested
3892 if (andClose)
3893 pinning.serverConnection->close();
3894 pinning.serverConnection = nullptr;
3895 }
3896
3897 safe_free(pinning.host);
3898
3899 pinning.zeroReply = false;
3900 pinning.peerAccessDenied = false;
3901
3902 /* NOTE: pinning.pinned should be kept. This combined with fd == -1 at the end of a request indicates that the host
3903 * connection has gone away */
3904}
3905
3906void
3908{
3909 auto error = rawError; // (cheap) copy so that we can detail
3910 // We detail even ERR_NONE: There should be no transactions left, and
3911 // detailed ERR_NONE will be unused. Otherwise, this detail helps in triage.
3912 if (error.details.empty()) {
3913 static const auto d = MakeNamedErrorDetail("WITH_CLIENT");
3914 error.details.push_back(d);
3915 }
3916
3917 debugs(33, 3, pipeline.count() << '/' << pipeline.nrequests << " after " << error);
3918
3919 if (pipeline.empty()) {
3920 bareError.update(error); // XXX: bareLogTagsErrors
3921 } else {
3922 // We terminate the current CONNECT/PUT/etc. context below, logging any
3923 // error details, but that context may leave unparsed bytes behind.
3924 // Consume them to stop checkLogging() from logging them again later.
3925 const auto intputToConsume =
3926#if USE_OPENSSL
3927 parsingTlsHandshake ? "TLS handshake" : // more specific than CONNECT
3928#endif
3929 bodyPipe ? "HTTP request body" :
3930 pipeline.back()->mayUseConnection() ? "HTTP CONNECT" :
3931 nullptr;
3932
3933 while (const auto context = pipeline.front()) {
3934 context->noteIoError(error, lte);
3935 context->finished(); // cleanup and self-deregister
3936 assert(context != pipeline.front());
3937 }
3938
3939 if (intputToConsume && !inBuf.isEmpty()) {
3940 debugs(83, 5, "forgetting client " << intputToConsume << " bytes: " << inBuf.length());
3941 inBuf.clear();
3942 }
3943 }
3944
3946}
3947
3949void
3951{
3952 // to simplify our logic, we assume that terminateAll() has been called
3954
3955 // do not log connections that closed after a transaction (it is normal)
3956 // TODO: access_log needs ACLs to match received-no-bytes connections
3958 return;
3959
3960 /* Create a temporary ClientHttpRequest object. Its destructor will log. */
3961 ClientHttpRequest http(this);
3962 http.req_sz = inBuf.length();
3963 // XXX: Or we died while waiting for the pinned connection to become idle.
3964 http.setErrorUri("error:transaction-end-before-headers");
3965 http.updateError(bareError);
3966}
3967
3968bool
3970{
3971 // PROXY protocol bytes are meant for us and, hence, cannot be tunneled
3973 return false;
3974
3975 // If our decision here is negative, configuration changes are irrelevant.
3976 // Otherwise, clientTunnelOnError() rechecks configuration before tunneling.
3978 return false;
3979
3980 // TODO: Figure out whether/how we can support FTP tunneling.
3981 if (port->transport.protocol == AnyP::PROTO_FTP)
3982 return false;
3983
3984#if USE_OPENSSL
3986 return true;
3987
3988 // the 1st HTTP request on a bumped connection
3990 return true;
3991#endif
3992
3993 // the 1st HTTP(S) request on a connection to an intercepting port
3994 if (!pipeline.nrequests && transparent())
3995 return true;
3996
3997 return false;
3998}
3999
4002{
4003 if (!theNotes)
4004 theNotes = new NotePairs;
4005 return theNotes;
4006}
4007
4008std::ostream &
4009operator <<(std::ostream &os, const ConnStateData::PinnedIdleContext &pic)
4010{
4011 return os << pic.connection << ", request=" << pic.request;
4012}
4013
4014std::ostream &
4015operator <<(std::ostream &os, const ConnStateData::ServerConnectionContext &scc)
4016{
4017 return os << scc.conn_ << ", srv_bytes=" << scc.preReadServerBytes.length();
4018}
4019
void accessLogLog(const AccessLogEntryPointer &, ACLChecklist *)
#define Assure(condition)
Definition Assure.h:35
RefCount< AsyncCallT< Dialer > > asyncCall(int aDebugSection, int aDebugLevel, const char *aName, const Dialer &aDialer)
Definition AsyncCall.h:156
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
CommCbFunPtrCallT< Dialer > * commCbCall(int debugSection, int debugLevel, const char *callName, const Dialer &dialer)
Definition CommCalls.h:312
void IOACB(const CommAcceptCbParams &params)
Definition CommCalls.h:31
#define COMM_TRANSPARENT
Definition Connection.h:50
#define COMM_REUSEPORT
Definition Connection.h:52
#define COMM_INTERCEPTION
Definition Connection.h:51
#define COMM_NONBLOCKING
Definition Connection.h:46
ErrorDetail::Pointer MakeNamedErrorDetail(const char *name)
Definition Detail.cc:54
#define Here()
source code location of the caller
Definition Here.h:15
void httpHeaderAddContRange(HttpHeader *, HttpHdrRangeSpec, int64_t)
@ hoReply
Definition HttpHeader.h:37
RawPointerT< Pointer > RawPointer(const char *label, const Pointer &ptr)
convenience wrapper for creating RawPointerT<> objects
Definition IoManip.h:73
@ LOG_TCP_OFFLINE_HIT
Definition LogTags.h:57
@ LOG_TCP_HIT
Definition LogTags.h:42
@ LOG_TCP_MISS
Definition LogTags.h:43
@ LOG_TCP_MEM_HIT
Definition LogTags.h:54
@ LOG_TCP_IMS_HIT
Definition LogTags.h:50
@ LOG_TAG_NONE
Definition LogTags.h:41
@ LOG_TCP_REFRESH_UNMODIFIED
Definition LogTags.h:44
@ LOG_TCP_CLIENT_REFRESH_MISS
Definition LogTags.h:49
@ LOG_TCP_INM_HIT
Definition LogTags.h:51
int size
Definition ModDevPoll.cc:70
void prepareLogWithRequestDetails(HttpRequest *, const AccessLogEntryPointer &)
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:86
#define SQUIDSBUFPH
Definition SBuf.h:31
void SBufToCstring(char *d, const SBuf &s)
Definition SBuf.h:756
#define SQUIDSBUFPRINT(s)
Definition SBuf.h:32
class SquidConfig Config
#define SQUIDSTRINGPH
Definition SquidString.h:22
#define SQUIDSTRINGPRINT(s)
Definition SquidString.h:23
StatCounters statCounter
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
#define Must(condition)
bool urlCheckRequest(const HttpRequest *r)
Definition Uri.cc:1002
void error(char *format,...)
#define assert(EX)
Definition assert.h:17
#define USE_OPENSSL
Definition autoconf.h:1557
int cbdataReferenceValid(const void *p)
Definition cbdata.cc:270
Acl::Answer const & fastCheck()
Definition Checklist.cc:298
void changeAcl(const acl_access *)
change the current ACL list
Definition Checklist.cc:187
static MakingPointer Make(const acl_access *a, HttpRequest *r)
std::unique_ptr< ACLFilledChecklist > MakingPointer
ConnStateData * conn() const
The client connection manager.
void setRequest(HttpRequest *)
configure client request-related fields for the first time
void setConn(ConnStateData *)
set either conn
void updateAle(const AccessLogEntry::Pointer &)
static void NonBlockingCheck(MakingPointer &&p, ACLCB *cb, void *data)
CbcPointer< Security::CertErrors > sslErrors
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
void syncAle(HttpRequest *adaptedRequest, const char *logUri) const override
assigns uninitialized adapted_request and url ALE components
HttpRequest::Pointer request
char * last_meta
image of the last ICAP response header or eCAP meta received
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
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
class AccessLogEntry::HttpDetails http
HttpRequest * adapted_request
HttpRequest * request
class AccessLogEntry::AdaptationDetails adapt
class AccessLogEntry::IcpDetails icp
int kind
the matched custom access list verb (or zero)
Definition Acl.h:99
bool allowed() const
Definition Acl.h:82
HttpHeader lastMeta
Last received meta header (REQMOD or RESPMOD, whichever comes last).
Definition History.h:61
unsigned int major
major version number
ProtocolType protocol
which protocol this version is for
unsigned int minor
minor version number
SBuf image() const
Definition UriScheme.h:57
void setScheme(const AnyP::ProtocolType &p, const char *str)
convert the URL scheme to that given
Definition Uri.h:61
std::optional< Host > parsedHost() const
Definition Uri.cc:181
void port(const Port p)
reset authority port subcomponent
Definition Uri.h:90
void host(const char *src)
Definition Uri.cc:154
SBuf hostOrIp() const
Definition Uri.cc:170
a smart AsyncCall pointer for delivery of future results
static void Start(const Pointer &job)
Definition AsyncJob.cc:37
virtual void start()
called by AsyncStart; do not call directly
Definition AsyncJob.cc:59
void mustStop(const char *aReason)
Definition AsyncJob.cc:85
void deleteThis(const char *aReason)
Definition AsyncJob.cc:65
virtual void callException(const std::exception &e)
called when the job throws during an async call
Definition AsyncJob.cc:143
virtual void releaseAuthServer()
MemBuf & buf
Definition BodyPipe.h:74
uint64_t producedSize() const
Definition BodyPipe.h:112
void expectNoConsumption()
there will be no more setConsumer() calls
Definition BodyPipe.cc:267
size_t putMoreData(const char *buf, size_t size)
Definition BodyPipe.cc:213
const MemBuf & buf() const
Definition BodyPipe.h:137
bool bodySizeKnown() const
Definition BodyPipe.h:109
uint64_t unproducedSize() const
Definition BodyPipe.cc:179
void setBodySize(uint64_t aSize)
Definition BodyPipe.cc:147
const char * status() const
Definition BodyPipe.cc:446
bool productionEnded() const
Definition BodyPipe.h:113
bool mayNeedMoreData() const
Definition BodyPipe.h:118
uint64_t bodySize() const
Definition BodyPipe.cc:161
void enableAutoConsumption()
start or continue consuming when producing without consumer
Definition BodyPipe.cc:316
void stopProducingFor(RefCount< BodyPipe > &, bool atEof)
Definition BodyPipe.cc:107
optimized set of C chars, with quick membership test and merge support
static const CharacterSet DIGIT
static const CharacterSet ALPHA
static const CharacterSet HEXDIG
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
ConnStateData * getConn() const
String rangeBoundaryStr() const
void initRequest(HttpRequest *)
void checkForInternalAccess()
Checks whether the current request is internal and adjusts it accordingly.
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
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:1343
static const Pointer & Current()
static void Reset()
forgets the current context, setting it to nil/unknown
AnyP::PortCfgPointer port
the configuration listening port this call relates to (may be nil)
Definition CommCalls.h:100
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition CommCalls.h:83
int fd
FD which the call was about. Set by the async call creator.
Definition CommCalls.h:85
Comm::Flag flag
comm layer result status.
Definition CommCalls.h:82
Comm::ConnectionPointer conn
Definition CommCalls.h:80
time_t timeLeft(const time_t idleTimeout) const
Ip::Address remote
Definition Connection.h:152
Ip::Address local
Definition Connection.h:149
Security::NegotiationHistory * tlsNegotiations()
parameters for the async notePinnedConnectionBecameIdle() call
Comm::ConnectionPointer connection
to-server connection to be pinned
HttpRequest::Pointer request
to-server request that initiated serverConnection
noteTakeServerConnectionControl() callback parameter
Comm::ConnectionPointer conn_
to-server connection
SBuf preReadServerBytes
post-101 bytes received from the server
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()
void endingShutdown() override
struct ConnStateData::@29 pinning
bool fakeAConnectRequest(const char *reason, const SBuf &payload)
bool switchedToHttps() const
void readNextRequest()
Traffic parsing.
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
void afterClientRead() override
processing to be done after a Comm::Read()
Ssl::ServerBump * sslServerBump
HTTPS server cert. fetching state for bump-ssl-server-first.
Http::Stream * parseHttpRequest(const Http1::RequestParserPointer &)
virtual bool writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call)=0
handle a control message received by context from a peer and call back
void switchToHttps(ClientHttpRequest *, Ssl::BumpMode bumpServerMode)
void startPinnedConnectionMonitoring()
const char * stoppedReceiving() const
true if we stopped receiving the request
void consumeInput(const size_t byteCount)
remove no longer needed leading bytes from the input buffer
void clientAfterReadingRequests()
ClientHttpRequest * buildFakeRequest(SBuf &useHost, AnyP::KnownPort usePort, const SBuf &payload)
build a fake http request
virtual void handleReply(HttpReply *header, StoreIOBuffer receivedData)=0
void setAuth(const Auth::UserRequest::Pointer &aur, const char *cause)
virtual Http::Stream * parseOneRequest()=0
BodyPipe::Pointer bodyPipe
set when we are reading request body
const SBuf & tlsClientSni() const
void sendControlMsg(HttpControlMsg) override
called to send the 1xx message and notify the Source
void kick()
try to make progress on a transaction or read more I/O
const Auth::UserRequest::Pointer & getAuth() const
void getSslContextStart()
Start to create dynamic Security::ContextPointer for host or uses static port SSL context.
void httpsPeeked(PinnedIdleContext pic)
called by FwdState when it is done bumping the server
bool initiateTunneledRequest(HttpRequest::Pointer const &cause, const char *reason, const SBuf &payload)
generates and sends to tunnel.cc a fake request with a given payload
void swanSong() override
bool splice()
Splice a bumped client connection on peek-and-splice mode.
void start() override
called by AsyncStart; do not call directly
SBuf tlsClientSni_
TLS client delivered SNI value. Empty string if none has been received.
Error bareError
a problem that occurred without a request (e.g., while parsing headers)
bool handleReadData() override
void expectNoForwarding()
cleans up virgin request [body] forwarding state
Http::Stream * abortRequestParsing(const char *const errUri)
stop parsing the request and create context for relaying error info
void startShutdown() override
void sslCrtdHandleReply(const Helper::Reply &reply)
Process response from ssl_crtd.
err_type handleChunkedRequestBody()
parses available chunked encoded body bytes, checks size, returns errors
SBuf sslCommonName_
CN name for SSL certificate generation.
void resetSslCommonName(const char *name)
void afterClientWrite(size_t) override
processing to sync state after a Comm::Write()
void doneWithControlMsg() override
void terminateAll(const Error &, const LogTagsErrors &) override
abort any pending transactions and prevent new ones (by closing)
void abortChunkedRequestBody(const err_type error)
quit on errors related to chunked request body handling
void pinConnection(const Comm::ConnectionPointer &pinServerConn, const HttpRequest &request)
Forward future client requests using the given server connection.
bool preservingClientData_
whether preservedClientData is valid and should be kept up to date
void callException(const std::exception &) override
called when the job throws during an async call
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.
Security::ContextPointer getTlsContextFromCache(const SBuf &cacheKey, const Ssl::CertificateProperties &certProperties)
AnyP::Port tlsConnectPort
The TLS server port number as passed in the CONNECT request.
struct ConnStateData::@28 flags
Auth::UserRequest::Pointer auth_
some user details that can be used to perform authentication on this connection
Comm::ConnectionPointer borrowPinnedConnection(HttpRequest *, const AccessLogEntryPointer &)
ConnStateData-specific part of BorrowPinnedConnection()
bool shouldPreserveClientData() const
void parseTlsHandshake()
void notePinnedConnectionBecameIdle(PinnedIdleContext pic)
Called when a pinned connection becomes available for forwarding the next request.
void stopPinnedConnectionMonitoring()
The caller assumes responsibility for connection closure detection.
void pinBusyConnection(const Comm::ConnectionPointer &pinServerConn, const HttpRequest::Pointer &request)
bool handleIdleClientPinnedTlsRead()
bool proxyProtocolError(const char *reason)
bool serveDelayedError(Http::Stream *)
SBuf preservedClientData
const char * stoppedSending_
the reason why we no longer write the response or nil
void resetReadTimeout(time_t timeout)
(re)sets timeout for receiving more bytes from the client
void quitAfterError(HttpRequest *request)
SBuf tlsConnectHostOrIp
The TLS server host name appears in CONNECT request or the server ip address for the intercepted requ...
~ConnStateData() override
void receivedFirstByte() override
Update flags and timeout after the first byte received.
uint64_t parsedBumpedRequestCount
The number of parsed HTTP requests headers on a bumped client connection.
Security::IoResult acceptTls()
BodyPipe::Pointer expectRequestBody(int64_t size)
bool switchedToHttps_
bool isOpen() const
void clientPinnedConnectionRead(const CommIoCbParams &io)
static Comm::ConnectionPointer BorrowPinnedConnection(HttpRequest *, const AccessLogEntryPointer &)
void stopReceiving(const char *error)
note request receiving error and close as soon as we write the response
Ip::Address log_addr
void checkLogging()
log the last (attempt at) transaction if nobody else did
void unpinConnection(const bool andClose)
Undo pinConnection() and, optionally, close the pinned connection.
void handleSslBumpHandshakeError(const Security::IoResult &)
process a problematic Security::Accept() result on the SslBump code path
void startDechunkingRequest()
initialize dechunking state
void startPeekAndSplice()
Initializes and starts a peek-and-splice negotiation with the SSL client.
Ssl::BumpMode sslBumpMode
ssl_bump decision (Ssl::bumpEnd if n/a).
bool concurrentRequestQueueFilled() const
bool readMore
needs comm_read (for this request or new requests)
ConnStateData(const MasterXactionPointer &xact)
void extendLifetime()
(re)sets client_lifetime timeout
ProxyProtocol::HeaderPointer proxyProtocolHeader_
the parsed PROXY protocol header
bool shouldCloseOnEof() const override
whether to stop serving our client after reading EOF on its connection
void fillChecklist(ACLFilledChecklist &) const override
configure the given checklist (to reflect the current transaction state)
void requestTimeout(const CommTimeoutCbParams &params)
void whenClientIpKnown()
virtual time_t idleTimeout() const =0
timeout to use when waiting for the next request
void finishDechunkingRequest(bool withSuccess)
put parsed content into input buffer and clean up
virtual void processParsedRequest(Http::StreamPointer &)=0
start processing a freshly parsed request
void getSslContextDone(Security::ContextPointer &)
finish configuring the newly created SSL context"
bool transparent() const
bool parsingTlsHandshake
bool parseProxyProtocolHeader()
AnyP::Port port
destination port of the request that caused serverConnection
bool handleRequestBodyData()
void updateError(const Error &)
if necessary, stores new error information (if any)
void doPeekAndSpliceStep()
const char * stoppedReceiving_
the reason why we no longer read the request or nil
bool needProxyProtocolHeader_
whether PROXY protocol header is still expected
void noteBodyConsumerAborted(BodyPipe::Pointer) override=0
bool tunnelOnError(const err_type)
initiate tunneling if possible or return false otherwise
void stopSending(const char *error)
note response sending error and close as soon as we read the request
int64_t mayNeedToReadMoreBody() const
char * host
host name of pinned connection
Security::HandshakeParser tlsParser
void storeTlsContextToCache(const SBuf &cacheKey, Security::ContextPointer &ctx)
const char * stoppedSending() const
true if we stopped sending the response
SBuf sslBumpCertKey
Key to use to store/retrieve generated certificate.
NotePairs::Pointer notes()
Http1::TeChunkedParser * bodyParser
parses HTTP/1.1 chunked request body
void fillConnectionLevelDetails(ACLFilledChecklist &) const
NotePairs::Pointer theNotes
void connStateClosed(const CommCloseCbParams &io)
bool proxyProtocolValidateClient()
void add(const Http::StreamPointer &context)
registers a newly created stream
void buildSslCertGenerationParams(Ssl::CertificateProperties &certProperties)
static bool Enabled(const int section, const int level)
whether debugging the given section and the given level produces output
Definition Stream.h:75
static std::ostream & Extra(std::ostream &)
Definition debug.cc:1316
static ErrorState * NewForwarding(err_type, HttpRequestPointer &, const AccessLogEntryPointer &)
Creates a general request forwarding error with the right http_status.
Definition errorpage.cc:691
a transaction problem
Definition Error.h:27
void update(const Error &)
if necessary, stores the given error information (if any)
Definition Error.cc:51
void clear()
switch to the default "no error information" state
Definition Error.h:53
static void Start(const Comm::ConnectionPointer &client, StoreEntry *, HttpRequest *, const AccessLogEntryPointer &alp)
Initiates request forwarding to a peer or origin server.
Definition FwdState.cc:338
Helper::ResultCode result
The helper response 'result' field.
Definition Reply.h:59
const MemBuf & other() const
Definition Reply.h:42
AsyncCall::Pointer cbControlMsgSent
Call to schedule when the control msg has been sent.
void wroteControlMsg(const CommIoCbParams &)
callback to handle Comm::Write completion
virtual void doneWithControlMsg()
bundles HTTP 1xx reply and the "successfully forwarded" callback
Callback cbSuccess
called after successfully writing the 1xx message
HttpReply::Pointer reply
the 1xx message being forwarded
std::vector< HttpHdrRangeSpec * >::iterator iterator
iterator begin()
iterator end()
void putStr(Http::HdrType id, const char *str)
bool chunked() const
Definition HttpHeader.h:169
const char * getStr(Http::HdrType id) const
int has(Http::HdrType id) const
int64_t getInt64(Http::HdrType id) const
void clean()
void packInto(Packable *p, bool mask_sensitive_info=false) const
HttpHdrRange * range
HttpRequestMethod method
String myportname
String extacl_user
bool multipartRangeRequest() const
HierarchyLogEntry hier
RequestFlags flags
NotePairs::Pointer notes()
SBuf vary_headers
The variant second-stage cache key. Generated from Vary header pattern for this request.
ConnStateData * pinnedConnection()
int dnsWait
sum of DNS lookup delays in milliseconds, for dt
Adaptation::History::Pointer adaptLogHistory() const
Returns possibly nil history, creating it if adapt. logging is enabled.
Auth::UserRequest::Pointer auth_user_request
Http::StatusCode checkEntityFraming() const
Error error
the first transaction problem encountered (or falsy)
Adaptation::Icap::History::Pointer icapHistory() const
Returns possibly nil history, creating it if icap logging is enabled.
AnyP::Uri url
the request URI
void manager(const CbcPointer< ConnStateData > &aMgr, const AccessLogEntryPointer &al)
associates the request with a from-client connection manager
const SBuf & effectiveRequestUri() const
RFC 7230 section 5.5 - Effective Request URI.
@ srcHttp
http_port or HTTP server
Definition Message.h:39
@ srcHttps
https_port or bumped http_port tunnel; HTTPS server
Definition Message.h:33
@ srcFtp
ftp_port or FTP server
Definition Message.h:40
bool persistent() const
Definition Message.cc:236
uint32_t sources
The message sources.
Definition Message.h:99
HttpHeader header
Definition Message.h:74
int64_t content_length
Definition Message.h:83
BodyPipe::Pointer body_pipe
optional pipeline to receive message body
Definition Message.h:97
AnyP::ProtocolVersion http_ver
Definition Message.h:72
const SBuf & remaining() const
the remaining unprocessed section of buffer
Definition Parser.h:98
bool needsMoreData() const
Definition Parser.h:66
bool parse(const SBuf &) override
void setPayloadBuffer(MemBuf *parsedContent)
set the buffer to be used to store decoded chunk data
unsigned parsed_ok
Was this parsed correctly?
Definition Stream.h:140
void registerWithConn()
register this stream with the Server
Definition Stream.cc:53
struct Http::Stream::@58 flags
char reqbuf[HTTP_REQBUF_SZ]
Definition Stream.h:137
clientStreamNode * getClientReplyContext() const
Definition Stream.cc:511
bool mayUseConnection() const
Definition Stream.h:143
void pullData()
get more data to send
Definition Stream.cc:110
ClientHttpRequest * http
Definition Stream.h:135
char * toStr(char *buf, const unsigned int blen, int force=AF_UNSPEC) const
Definition Address.cc:804
unsigned int toHostStr(char *buf, const unsigned int len) const
Definition Address.cc:854
void applyClientMask(const Address &mask)
Definition Address.cc:125
char * toUrl(char *buf, unsigned int len) const
Definition Address.cc:886
unsigned short port() const
Definition Address.cc:790
StartListening() result.
dials clientListenerConnectionOpened call
Subscription::Pointer sub
The handler to be subscribed for this connection listener.
ListeningStartedDialer(Handler aHandler, AnyP::PortCfgPointer &aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub)
AnyP::PortCfgPointer portCfg
from HttpPortList
Ipc::StartListeningAnswer & answer() override
callback results setter
void print(std::ostream &os) const override
virtual void dial(AsyncCall &)
void(* Handler)(AnyP::PortCfgPointer &portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub)
Ipc::FdNoteId portTypeNote
Type of IPC socket being opened.
Ipc::StartListeningAnswer answer_
StartListening() results.
virtual bool canDial(AsyncCall &) const
bool timedout
_TIMEDOUT: terminated due to a lifetime or I/O timeout
Definition LogTags.h:28
LogTags_ot oldType
a set of client protocol, cache use, and other transaction outcome tags
Definition LogTags.h:96
bool isTcpHit() const
determine if the log tag code indicates a cache HIT
Definition LogTags.cc:110
Comm::ConnectionPointer tcpClient
the client TCP connection which originated this transaction
static Pointer MakePortful(const AnyP::PortCfgPointer &aPort)
void clean()
Definition MemBuf.cc:110
void append(const char *c, int sz) override
Definition MemBuf.cc:209
void init(mb_size_t szInit, mb_size_t szMax)
Definition MemBuf.cc:93
mb_size_t size
Definition MemBuf.h:135
char * buf
Definition MemBuf.h:134
char * content()
start of the added data
Definition MemBuf.h:41
mb_size_t contentSize() const
available data size
Definition MemBuf.h:47
void reset()
Definition MemBuf.cc:129
bool hasContent() const
Definition MemBuf.h:54
const char * urlXXX() const
Definition MemObject.h:138
SBuf vary_headers
Definition MemObject.h:221
const HttpReply & freshestReply() const
Definition MemObject.h:68
uint64_t payloadData
total size of payload block(s) excluding transfer encoding overheads
uint64_t header
void add(const SBuf &key, const SBuf &value)
Definition Notes.cc:322
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition Packable.h:61
thrown by modern "incremental" parsers when they need more data
Definition forward.h:18
bool empty() const
whether there are none or any requests currently pipelined
Definition Pipeline.h:56
size_t count() const
how many requests are currently pipelined
Definition Pipeline.h:53
uint32_t nrequests
Definition Pipeline.h:63
Http::StreamPointer front() const
get the first request context in the pipeline
Definition Pipeline.cc:28
void add(const Http::StreamPointer &)
register a new request context to the pipeline
Definition Pipeline.cc:20
Http::StreamPointer back() const
get the last request context in the pipeline
Definition Pipeline.cc:40
C * getRaw() const
Definition RefCount.h:89
bool connectionAuth
bool forceTunnel
whether to forward via TunnelStateData (instead of FwdState)
bool proxyKeepalive
Definition SBuf.h:94
const char * rawContent() const
Definition SBuf.cc:509
static const size_type npos
Definition SBuf.h:100
SBuf consume(size_type n=npos)
Definition SBuf.cc:481
const char * c_str()
Definition SBuf.cc:516
SBuf & chop(size_type pos, size_type n=npos)
Definition SBuf.cc:530
size_type length() const
Returns the number of bytes stored in SBuf.
Definition SBuf.h:419
SBuf & appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Definition SBuf.cc:229
size_type rfind(char c, size_type endPos=npos) const
Definition SBuf.cc:692
int cmp(const SBuf &S, const size_type n) const
shorthand version for compare()
Definition SBuf.h:279
bool isEmpty() const
Definition SBuf.h:435
SBuf & append(const SBuf &S)
Definition SBuf.cc:185
const_reverse_iterator rbegin() const
Definition SBuf.h:595
void clear()
Definition SBuf.cc:175
SBuf & assign(const SBuf &S)
Definition SBuf.cc:83
TlsDetails::Pointer details
TLS handshake meta info. Never nil.
Definition Handshake.h:77
bool parseHello(const SBuf &data)
Definition Handshake.cc:641
a summary a TLS I/O operation outcome
Definition Io.h:19
ErrorDetailPointer errorDetail
ioError case details (or nil)
Definition Io.h:43
Category category
primary outcome classification
Definition Io.h:45
bool important
whether the error was serious/unusual
Definition Io.h:49
void checkpoint(const Connection &, const Acl::ChecklistFiller &)
Definition KeyLogger.h:50
T * get() const
Returns raw and possibly nullptr pointer.
void retrieveParsedInfo(Security::TlsDetails::Pointer const &details)
Extract information from parser stored in TlsDetails object.
void retrieveNegotiatedInfo(const Security::SessionPointer &)
Extract negotiation information from TLS object.
void readSomeData()
maybe grow the inBuf and schedule Comm::Read()
Definition Server.cc:101
Pipeline pipeline
set of requests waiting to be serviced
Definition Server.h:118
AnyP::ProtocolVersion transferProtocol
Definition Server.h:107
Comm::ConnectionPointer clientConnection
Definition Server.h:100
bool receivedFirstByte_
true if at least one byte received on this connection
Definition Server.h:115
SBuf inBuf
read I/O buffer for the client connection
Definition Server.h:113
void swanSong() override
Definition Server.cc:51
time_t request_start_timeout
struct SquidConfig::@99 ssl_client
size_t appendDomainLen
sslproxy_cert_adapt * cert_adapt
ClientDelayConfig ClientDelay
Notes notes
note
time_t request
int64_t maxRequestBodySize
struct SquidConfig::@77 Timeout
time_t lifetime
sslproxy_cert_sign * cert_sign
struct SquidConfig::@85 Addrs
acl_access * cert_error
struct SquidConfig::@90 onoff
acl_access * stats_collection
size_t maxRequestHeaderSize
acl_access * ssl_bump
acl_access * on_unsupported_protocol
Ip::Address client_netmask
struct SquidConfig::@91 accessList
int pipeline_max_prefetch
acl_access * proxyProtocol
acceptable PROXY protocol clients
int half_closed_clients
Security::PrivateKeyPointer signWithPkey
The key of the signing certificate.
Definition gadgets.h:237
Security::CertPointer signWithX509
Certificate to sign the generated request.
Definition gadgets.h:236
bool setCommonName
Replace the CN field of the mimicking subject with the given.
Definition gadgets.h:240
bool setValidAfter
Do not mimic "Not Valid After" field.
Definition gadgets.h:238
CertSignAlgorithm signAlgorithm
The signing algorithm to use.
Definition gadgets.h:242
bool setValidBefore
Do not mimic "Not Valid Before" field.
Definition gadgets.h:239
Security::CertPointer mimicCert
Certificate to mimic.
Definition gadgets.h:235
const EVP_MD * signHash
The signing hash to use.
Definition gadgets.h:243
std::string commonName
A CN to use for the generated certificate.
Definition gadgets.h:241
void setReadBufData(SBuf &data)
Definition bio.h:91
void hold(bool h)
Prevents or allow writing on socket.
Definition bio.h:86
void setCode(std::string const &aCode)
Set new request/reply code to compose.
static const std::string code_new_certificate
String code for "new_certificate" messages.
std::string const & getBody() const
Current body. If parsing is not finished the method returns incompleted body.
ParseResult parse(const char *buffer, size_t len)
void composeRequest(Ssl::CertificateProperties const &)
std::string compose() const
void addLocalStorage(Ip::Address const &address, size_t size_of_store)
Create new SSL context storage for the local listening address/port.
LocalContextStorage * getLocalStorage(Ip::Address const &address)
Return the local storage for the given listening address/port.
static void Submit(CrtdMessage const &message, HLPCB *callback, void *data)
Submit crtd message to external crtd server.
Definition helper.cc:128
struct Ssl::ServerBump::@103 act
bumping actions at various bumping steps
StoreEntry * entry
Definition ServerBump.h:54
bool at(const BumpStep stp) const
whether we are currently performing the given processing step
Definition ServerBump.h:47
Security::CertErrors * sslErrors() const
SSL [certificate validation] errors.
Definition ServerBump.cc:66
Ssl::BumpMode step2
The SSL bump mode at step2.
Definition ServerBump.h:60
Ssl::BumpMode step1
The SSL bump mode at step1.
Definition ServerBump.h:59
Ssl::BumpStep step
The SSL bumping step.
Definition ServerBump.h:63
HttpRequest::Pointer request
faked, minimal request; required by Client API
Definition ServerBump.h:53
bool connectedOk() const
whether there was a successful connection to (and peeking at) the origin server
Definition ServerBump.h:44
Security::CertPointer serverCert
Definition ServerBump.h:57
StatHist querySvcTime
StatHist nearHitSvcTime
StatHist missSvcTime
StatHist hitSvcTime
StatHist allSvcTime
struct StatCounters::@111 netdb
ByteCounter kbytes_out
struct StatCounters::@110 cd
StatHist nearMissSvcTime
ByteCounter hit_kbytes_out
struct StatCounters::@106 icp
struct StatCounters::@104 client_http
void count(double val)
Definition StatHist.cc:55
MemObject & mem()
Definition Store.h:47
const char * getMD5Text() const
Definition store.cc:207
MemObject * mem_obj
Definition Store.h:220
bool isEmpty() const
Definition Store.h:65
int64_t contentLen() const
Definition Store.h:254
void clean()
Definition String.cc:104
char const * termedBuf() const
Definition SquidString.h:93
void append(char const *buf, int len)
Definition String.cc:131
an std::runtime_error with thrower location info
SourceLocationId id() const
same-location exceptions have the same ID
access to a callback result carried by an asynchronous CallDialer
Helps prints T object using object's T::printWithExtras() method.
Definition IoManip.h:294
void setReplyToStoreEntry(StoreEntry *e, const char *reason)
replaces current response store entry with the given one
ClientHttpRequest * http
void setReplyToError(err_type, Http::StatusCode, char const *, const ConnStateData *, HttpRequest *, const char *, Auth::UserRequest::Pointer)
builds error using clientBuildError() and calls setReplyToError() below
ICP probing of cache_peers during peer selection.
Definition PingData.h:26
struct timeval start
Definition PingData.h:35
int timeout
Definition PingData.h:41
struct timeval stop
Definition PingData.h:37
int clientdbEstablished(const Ip::Address &addr, int delta)
Definition client_db.cc:182
void clientdbUpdate(const Ip::Address &addr, const LogTags &ltype, AnyP::ProtocolType p, size_t size)
Definition client_db.cc:138
ClientInfo * clientdbGetInfo(const Ip::Address &addr)
Definition client_db.cc:119
static void clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub)
process clientHttpConnectionsOpen result
static void clientUpdateStatHistCounters(const LogTags &logType, int svc_time)
static char * prepareTransparentURL(ConnStateData *conn, const Http1::RequestParserPointer &hp)
static void clientHttpConnectionsOpen(void)
const char * findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end)
static bool clientPingHasFinished(ping_data const *aPing)
static void httpsEstablish(ConnStateData *connState, const Security::ContextPointer &ctx)
void prepareLogWithRequestDetails(HttpRequest *, const AccessLogEntryPointer &)
void clientProcessRequestFinished(ConnStateData *conn, const HttpRequest::Pointer &request)
static void httpsSslBumpAccessCheckDone(Acl::Answer answer, void *data)
void clientOpenListenSockets(void)
void clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp, Http::Stream *context)
static bool httpsCreate(const ConnStateData *connState, const Security::ContextPointer &ctx)
Create TLS connection structure and update fd_table.
static bool OpenedHttpSocket(const Comm::ConnectionPointer &c, const Ipc::FdNoteId portType)
check FD after clientHttp[s]ConnectionOpened, adjust HttpSockets as needed
static void clientNegotiateSSL(int fd, void *data)
static char * buildUrlFromHost(ConnStateData *conn, const Http1::RequestParserPointer &hp)
static int clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength)
void httpRequestFree(void *data)
static bool AddOpenedHttpSocket(const Comm::ConnectionPointer &conn)
find any unused HttpSockets[] slot and store fd there or return false
static void httpsAccept(const CommAcceptCbParams &params)
static void clientUpdateHierCounters(HierarchyLogEntry *)
ACLFilledChecklist::MakingPointer clientAclChecklistCreate(const acl_access *acl, ClientHttpRequest *http)
void clientSetKeepaliveFlag(ClientHttpRequest *http)
decide whether to expect multiple requests on the corresponding connection
void clientPackTermBound(String boundary, MemBuf *mb)
put terminating boundary for multiparts to the buffer
static char * prepareAcceleratedURL(ConnStateData *conn, const Http1::RequestParserPointer &hp)
static void ClientSocketContextPushDeferredIfNeeded(Http::StreamPointer deferredRequest, ConnStateData *conn)
char * skipLeadingSpace(char *aString)
int varyEvaluateMatch(StoreEntry *entry, HttpRequest *request)
static void clientUpdateStatCounters(const LogTags &logType)
std::ostream & operator<<(std::ostream &os, const ConnStateData::PinnedIdleContext &pic)
void clientAclChecklistFill(ACLFilledChecklist &checklist, ClientHttpRequest *http)
static void httpsSslBumpStep2AccessCheckDone(Acl::Answer answer, void *data)
void clientStartListeningOn(AnyP::PortCfgPointer &port, const RefCount< CommCbFunPtrCallT< CommAcceptCbPtrFun > > &subCall, const Ipc::FdNoteId fdNote)
accept requests to a given port and inform subCall about them
void clientConnectionsClose()
void clientPackRangeHdr(const HttpReplyPointer &rep, const HttpHdrRangeSpec *spec, String boundary, MemBuf *mb)
append a "part" HTTP header (as in a multi-part/range reply) to the buffer
static IOACB httpAccept
CSD clientSocketDetach
void clientProcessRequestFinished(ConnStateData *, const HttpRequest::Pointer &)
CSS clientReplyStatus
CSCB clientSocketRecipient
CSD clientReplyDetach
CSR clientGetMoreData
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition comm.cc:942
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
Definition comm.cc:971
void commSetConnTimeout(const Comm::ConnectionPointer &conn, time_t timeout, AsyncCall::Pointer &callback)
Definition comm.cc:594
void comm_reset_close(const Comm::ConnectionPointer &conn)
Definition comm.cc:787
bool commIsHalfClosed(int fd)
Definition comm.h:104
#define Important(id)
Definition Messages.h:93
#define MYNAME
Definition Stream.h:219
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
#define COMM_SELECT_READ
Definition defines.h:24
#define FQDN_LOOKUP_IF_MISS
Definition defines.h:34
#define COMM_SELECT_WRITE
Definition defines.h:25
#define FD_DESC_SZ
Definition defines.h:32
static int port
@ VARY_OTHER
Definition enums.h:190
@ VARY_NONE
Definition enums.h:188
@ VARY_CANCEL
Definition enums.h:191
@ VARY_MATCH
Definition enums.h:189
@ DISABLE_PMTU_ALWAYS
Definition enums.h:226
@ DISABLE_PMTU_OFF
Definition enums.h:225
err_type
Definition forward.h:14
@ ERR_SECURE_ACCEPT_FAIL
Definition forward.h:80
@ ERR_LIFETIME_EXP
Definition forward.h:27
@ ERR_PROTOCOL_UNKNOWN
Definition forward.h:73
@ ERR_SECURE_CONNECT_FAIL
Definition forward.h:31
@ ERR_UNSUP_REQ
Definition forward.h:44
@ ERR_GATEWAY_FAILURE
Definition forward.h:67
@ ERR_NONE
Definition forward.h:15
@ ERR_REQUEST_PARSE_TIMEOUT
Definition forward.h:82
@ ERR_REQUEST_START_TIMEOUT
Definition forward.h:81
@ ERR_TOO_BIG
Definition forward.h:40
@ ERR_ZERO_SIZE_OBJECT
Definition forward.h:46
@ ERR_CANNOT_FORWARD
Definition forward.h:23
@ ERR_INVALID_REQ
Definition forward.h:43
void fatal(const char *message)
Definition fatal.cc:28
void fatalf(const char *fmt,...)
Definition fatal.cc:68
void fd_note(int fd, const char *s)
Definition fd.cc:211
#define fd_table
Definition fde.h:189
int opt_no_daemon
int opt_foreground
char const * visible_appname_string
@ ACCESS_DENIED
Definition Acl.h:41
@ ACCESS_ALLOWED
Definition Acl.h:42
void clientStreamAbort(clientStreamNode *thisObject, ClientHttpRequest *http)
void clientStreamInit(dlink_list *list, CSR *func, CSD *rdetach, CSS *readstatus, const ClientStreamData &readdata, CSCB *callback, CSD *cdetach, const ClientStreamData &callbackdata, StoreIOBuffer tailBuffer)
void clientStreamDetach(clientStreamNode *thisObject, ClientHttpRequest *http)
const char * fqdncache_gethostbyaddr(const Ip::Address &addr, int flags)
Definition fqdncache.cc:481
@ ICP_INVALID
Definition icp_opcode.h:15
Security::ContextPointer GenerateSslContext(CertificateProperties const &, Security::ServerOptions &, bool trusted)
Definition support.cc:1052
const char * bumpMode(int bm)
Definition support.h:144
bool configureSSL(SSL *ssl, CertificateProperties const &properties, AnyP::PortCfg &port)
Definition support.cc:1098
void InRamCertificateDbKey(const Ssl::CertificateProperties &certProperties, SBuf &key)
Definition support.cc:1486
Security::ContextPointer createSSLContext(Security::CertPointer &x509, Security::PrivateKeyPointer &pkey, Security::ServerOptions &)
Create SSL context and apply ssl certificate and private key to it.
Definition support.cc:1021
bool verifySslCertificate(const Security::ContextPointer &, CertificateProperties const &)
Definition support.cc:1141
bool configureSSLUsingPkeyAndCertFromMemory(SSL *ssl, const char *data, AnyP::PortCfg &port)
Definition support.cc:1121
Security::ContextPointer GenerateSslContextUsingPkeyAndCertFromMemory(const char *data, Security::ServerOptions &, bool trusted)
Definition support.cc:1038
void configureUnconfiguredSslContext(Security::ContextPointer &, Ssl::CertSignAlgorithm signAlgorithm, AnyP::PortCfg &)
Definition support.cc:1091
const char * sslGetUserEmail(SSL *ssl)
Definition support.cc:981
BumpMode
Definition support.h:132
@ bumpTerminate
Definition support.h:132
@ bumpPeek
Definition support.h:132
@ bumpClientFirst
Definition support.h:132
@ bumpNone
Definition support.h:132
@ bumpStare
Definition support.h:132
@ bumpSplice
Definition support.h:132
@ bumpServerFirst
Definition support.h:132
CertSignAlgorithm
Definition gadgets.h:169
const char * CertAdaptAlgorithmStr[]
Definition gadgets.cc:285
@ algSetValidAfter
Definition gadgets.h:207
@ algSetCommonName
Definition gadgets.h:207
@ algSetValidBefore
Definition gadgets.h:207
@ algSignEnd
Definition gadgets.h:169
@ algSignTrusted
Definition gadgets.h:169
@ algSignUntrusted
Definition gadgets.h:169
@ CD_SIBLING_HIT
Definition hier_code.h:29
@ CD_PARENT_HIT
Definition hier_code.h:28
@ PARENT_HIT
Definition hier_code.h:16
@ CLOSEST_PARENT
Definition hier_code.h:22
@ SIBLING_HIT
Definition hier_code.h:15
@ CLOSEST_DIRECT
Definition hier_code.h:23
@ FIRST_PARENT_MISS
Definition hier_code.h:20
@ CLOSEST_PARENT_MISS
Definition hier_code.h:21
void HTTPMSGUNLOCK(M *&a)
Definition Message.h:150
void HTTPMSGLOCK(Http::Message *a)
Definition Message.h:161
#define HTTP_REQBUF_SZ
Definition forward.h:14
SBuf httpMakeVaryMark(HttpRequest *request, HttpReply const *reply)
Definition http.cc:590
char * internalLocalUri(const char *dir, const SBuf &name)
Definition internal.cc:139
bool internalCheck(const SBuf &urlPath)
Definition internal.cc:72
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition forward.h:25
std::optional< KnownPort > Port
validated/supported port number (if any)
Definition UriScheme.h:26
uint16_t KnownPort
validated/supported port number; these values are never zero
Definition UriScheme.h:23
@ PROTO_HTTPS
@ PROTO_AUTHORITY_FORM
@ PROTO_HTTP
@ PROTO_FTP
void ReadCancel(int fd, AsyncCall::Pointer &callback)
Cancel the read pending on FD. No action if none pending.
Definition Read.cc:219
void ResetSelect(int fd)
reset/undo/unregister the watch for an FD which was set by Comm::SetSelect()
Definition Loops.h:30
void Read(const Comm::ConnectionPointer &conn, AsyncCall::Pointer &callback)
Definition Read.cc:40
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition Connection.cc:27
@ OK
Definition Flag.h:16
@ ERR_CLOSING
Definition Flag.h:24
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
bool ResolveClientAddressesAsap
whether to do reverse DNS lookups for source IPs of accepted connections
Definition fqdncache.cc:30
void StopListening()
reject new connections to any configured ftp_port
Definition FtpServer.cc:287
void StartListening()
accept connections on all configured ftp_ports
Definition FtpServer.cc:265
@ BrokenHelper
Definition ResultCode.h:20
StatusCode
Definition StatusCode.h:20
@ scUriTooLong
Definition StatusCode.h:59
@ scNotImplemented
Definition StatusCode.h:74
@ scBadRequest
Definition StatusCode.h:45
@ scMethodNotAllowed
Definition StatusCode.h:50
@ scNone
Definition StatusCode.h:21
@ scContentTooLarge
Definition StatusCode.h:58
@ scRequestHeaderFieldsTooLarge
Definition StatusCode.h:71
@ scServiceUnavailable
Definition StatusCode.h:76
ConnStateData * NewServer(const MasterXactionPointer &xact)
create a new HTTP connection handler; never returns NULL
@ METHOD_NONE
Definition MethodType.h:22
@ METHOD_OPTIONS
Definition MethodType.h:31
@ METHOD_PRI
Definition MethodType.h:89
@ METHOD_CONNECT
Definition MethodType.h:29
AnyP::ProtocolVersion ProtocolVersion()
@ HDR_X_ACCELERATOR_VARY
ConnStateData * NewServer(const MasterXactionPointer &xact)
create a new HTTPS connection handler; never returns NULL
FdNoteId
We cannot send char* FD notes to other processes. Pass int IDs and convert.
Definition FdNotes.h:20
@ fdnHttpSocket
Definition FdNotes.h:20
@ fdnHttpsSocket
Definition FdNotes.h:20
void StartListening(int sock_type, int proto, const Comm::ConnectionPointer &listenConn, FdNoteId, StartListeningCallback &)
Parsed Parse(const SBuf &)
Definition Parser.cc:252
Network/connection security abstraction layer.
Definition Connection.h:34
SBuf IssuerName(Certificate &)
The Issuer field of the given certificate (if found) or an empty SBuf.
std::shared_ptr< SSL_CTX > ContextPointer
Definition Context.h:29
bool CreateServerSession(const Security::ContextPointer &, const Comm::ConnectionPointer &, Security::PeerOptions &, const char *squidCtx)
Definition Session.cc:227
IoResult Accept(Comm::Connection &transport)
accept a TLS connection over the specified to-Squid transport connection
Definition Io.cc:208
std::shared_ptr< SSL > SessionPointer
Definition Session.h:53
bool SessionIsResumed(const Security::SessionPointer &)
whether the session is a resumed one
Definition Session.cc:246
SBuf SubjectName(Certificate &)
The SubjectName field of the given certificate (if found) or an empty SBuf.
Security::ContextPointer GetFrom(Security::SessionPointer &s)
Helper function to retrieve a (non-locked) ContextPointer from a SessionPointer.
Definition Session.h:109
bool HasSubjectName(X509 &, const AnyP::Host &)
whether at least one common or alternate subject name matches the given one
Definition support.cc:338
GlobalContextStorage & TheGlobalContextStorage()
Global cache for store all SSL server certificates.
const EVP_MD * DefaultSignHash
Definition support.cc:44
#define xstrdup
void * BIO_get_data(BIO *table)
Definition openssl.h:62
@ SQUID_X509_V_ERR_DOMAIN_MISMATCH
Definition forward.h:238
int xsetsockopt(int socketFd, int level, int option, const void *value, socklen_t valueLength)
POSIX setsockopt(2) equivalent.
Definition socket.h:122
FILE * DebugStream()
Definition debug.cc:355
size_t headers_sz
Response header bytes written to the client connection.
uint64_t size
Response header and body bytes written to the client connection.
Definition parse.c:104
struct node * next
Definition parse.c:105
Definition parse.c:160
void tvSub(struct timeval &res, struct timeval const &t1, struct timeval const &t2)
Definition gadgets.cc:58
int tvSubUsec(struct timeval t1, struct timeval t2)
Definition gadgets.cc:37
struct timeval current_time
the current UNIX time in timeval {seconds, microseconds} format
Definition gadgets.cc:18
int tvSubMsec(struct timeval t1, struct timeval t2)
Definition gadgets.cc:51
#define NULL
Definition types.h:145
void * xcalloc(size_t n, size_t sz)
Definition xalloc.cc:71
#define safe_free(x)
Definition xalloc.h:73
#define xisspace(x)
Definition xis.h:15
const char * xstrerr(int error)
Definition xstrerror.cc:83