Squid Web Cache master
Loading...
Searching...
No Matches
client_side.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2026 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);
1506 err->detailError(errDetail);
1508 repContext->setReplyToError(request->method, err);
1509 assert(context->http->out.offset == 0);
1510 context->pullData();
1511 return true;
1512 }
1513 }
1514 }
1515
1516 return false;
1517}
1518#endif // USE_OPENSSL
1519
1521bool
1523{
1525 debugs(33, 5, "disabled; send error: " << requestError);
1526 return false;
1527 }
1528
1529 if (!preservingClientData_) {
1530 debugs(33, 3, "may have forgotten client data; send error: " << requestError);
1531 return false;
1532 }
1533
1535 checklist.requestErrorType = requestError;
1536 fillChecklist(checklist);
1537 const auto &answer = checklist.fastCheck();
1538 if (answer.allowed() && answer.kind == 1) {
1539 debugs(33, 3, "Request will be tunneled to server");
1540 const auto context = pipeline.front();
1541 const auto http = context ? context->http : nullptr;
1542 const auto request = http ? http->request : nullptr;
1543 if (context)
1544 context->finished(); // Will remove from pipeline queue
1545 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, nullptr, nullptr, 0);
1546 return initiateTunneledRequest(request, "unknown-protocol", preservedClientData);
1547 }
1548 debugs(33, 3, "denied; send error: " << requestError);
1549 return false;
1550}
1551
1552void
1554{
1555 /*
1556 * DPW 2007-05-18
1557 * Moved the TCP_RESET feature from clientReplyContext::sendMoreData
1558 * to here because calling comm_reset_close() causes http to
1559 * be freed before accessing.
1560 */
1561 if (request != nullptr && request->flags.resetTcp && Comm::IsConnOpen(conn->clientConnection)) {
1562 debugs(33, 3, "Sending TCP RST on " << conn->clientConnection);
1563 conn->flags.readMore = false;
1565 }
1566}
1567
1568void
1570{
1571 ClientHttpRequest *http = context->http;
1572 bool mustReplyToOptions = false;
1573 bool expectBody = false;
1574
1575 // We already have the request parsed and checked, so we
1576 // only need to go through the final body/conn setup to doCallouts().
1577 assert(http->request);
1578 HttpRequest::Pointer request = http->request;
1579
1580 // temporary hack to avoid splitting this huge function with sensitive code
1581 const bool isFtp = !hp;
1582
1583 // Some blobs below are still HTTP-specific, but we would have to rewrite
1584 // this entire function to remove them from the FTP code path. Connection
1585 // setup and body_pipe preparation blobs are needed for FTP.
1586
1587 request->manager(conn, http->al);
1588
1589 request->flags.accelerated = http->flags.accel;
1590 request->flags.sslBumped=conn->switchedToHttps();
1591 // TODO: decouple http->flags.accel from request->flags.sslBumped
1592 request->flags.noDirect = (request->flags.accelerated && !request->flags.sslBumped) ?
1593 !conn->port->allow_direct : 0;
1594 request->sources |= isFtp ? Http::Message::srcFtp :
1595 ((request->flags.sslBumped || conn->port->transport.protocol == AnyP::PROTO_HTTPS) ? Http::Message::srcHttps : Http::Message::srcHttp);
1596#if USE_AUTH
1597 if (request->flags.sslBumped) {
1598 if (conn->getAuth() != nullptr)
1599 request->auth_user_request = conn->getAuth();
1600 }
1601#endif
1602
1603 http->checkForInternalAccess();
1604
1605 if (!isFtp) {
1606 // XXX: for non-HTTP messages instantiate a different Http::Message child type
1607 // for now Squid only supports HTTP requests
1608 const AnyP::ProtocolVersion &http_ver = hp->messageProtocol();
1609 assert(request->http_ver.protocol == http_ver.protocol);
1610 request->http_ver.major = http_ver.major;
1611 request->http_ver.minor = http_ver.minor;
1612 }
1613
1614 mustReplyToOptions = (request->method == Http::METHOD_OPTIONS) &&
1616 if (!urlCheckRequest(request.getRaw()) || mustReplyToOptions) {
1618 conn->quitAfterError(request.getRaw());
1619 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1620 assert (repContext);
1622 conn, request.getRaw(), nullptr, nullptr);
1623 assert(context->http->out.offset == 0);
1624 context->pullData();
1625 clientProcessRequestFinished(conn, request);
1626 return;
1627 }
1628
1629 const auto frameStatus = request->checkEntityFraming();
1630 if (frameStatus != Http::scNone) {
1632 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1633 assert (repContext);
1634 conn->quitAfterError(request.getRaw());
1635 repContext->setReplyToError(ERR_INVALID_REQ, frameStatus, nullptr, conn, request.getRaw(), nullptr, nullptr);
1636 assert(context->http->out.offset == 0);
1637 context->pullData();
1638 clientProcessRequestFinished(conn, request);
1639 return;
1640 }
1641
1643 // Let tunneling code be fully responsible for CONNECT requests
1644 if (http->request->method == Http::METHOD_CONNECT) {
1645 context->mayUseConnection(true);
1646 conn->flags.readMore = false;
1647 }
1648
1649#if USE_OPENSSL
1650 if (conn->switchedToHttps() && conn->serveDelayedError(context)) {
1651 clientProcessRequestFinished(conn, request);
1652 return;
1653 }
1654#endif
1655
1656 /* Do we expect a request-body? */
1657 const auto chunked = request->header.chunked();
1658 expectBody = chunked || request->content_length > 0;
1659 if (!context->mayUseConnection() && expectBody) {
1660 request->body_pipe = conn->expectRequestBody(
1661 chunked ? -1 : request->content_length);
1662
1663 /* Is it too large? */
1664 if (!chunked && // if chunked, we will check as we accumulate
1667 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1668 assert (repContext);
1669 conn->quitAfterError(request.getRaw());
1670 repContext->setReplyToError(ERR_TOO_BIG,
1671 Http::scContentTooLarge, nullptr,
1672 conn, http->request, nullptr, nullptr);
1673 assert(context->http->out.offset == 0);
1674 context->pullData();
1675 clientProcessRequestFinished(conn, request);
1676 return;
1677 }
1678
1679 if (!isFtp) {
1680 // We may stop producing, comm_close, and/or call setReplyToError()
1681 // below, so quit on errors to avoid http->doCallouts()
1682 if (!conn->handleRequestBodyData()) {
1683 clientProcessRequestFinished(conn, request);
1684 return;
1685 }
1686
1687 if (!request->body_pipe->productionEnded()) {
1688 debugs(33, 5, "need more request body");
1689 context->mayUseConnection(true);
1690 assert(conn->flags.readMore);
1691 }
1692 }
1693 }
1694
1695 http->calloutContext = new ClientRequestContext(http);
1696
1697 http->doCallouts();
1698
1699 clientProcessRequestFinished(conn, request);
1700}
1701
1702void
1704{
1705 debugs(33, 3, context << " to " << pipeline.count() << '/' << pipeline.nrequests);
1706 if (bareError) {
1707 debugs(33, 5, "assigning " << bareError);
1708 assert(context);
1709 assert(context->http);
1710 context->http->updateError(bareError);
1711 bareError.clear();
1712 }
1713 pipeline.add(context);
1714}
1715
1716int
1718{
1719 // TODO: Support pipelined requests through pinned connections.
1720 if (pinning.pinned)
1721 return 0;
1723}
1724
1730bool
1732{
1733 const int existingRequestCount = pipeline.count();
1734
1735 // default to the configured pipeline size.
1736 // add 1 because the head of pipeline is counted in concurrent requests and not prefetch queue
1737#if USE_OPENSSL
1738 const int internalRequest = (transparent() && sslBumpMode == Ssl::bumpSplice) ? 1 : 0;
1739#else
1740 const int internalRequest = 0;
1741#endif
1742 const int concurrentRequestLimit = pipelinePrefetchMax() + 1 + internalRequest;
1743
1744 // when queue filled already we can't add more.
1745 if (existingRequestCount >= concurrentRequestLimit) {
1746 debugs(33, 3, clientConnection << " max concurrent requests reached (" << concurrentRequestLimit << ")");
1747 debugs(33, 5, clientConnection << " deferring new request until one is done");
1748 return true;
1749 }
1750
1751 return false;
1752}
1753
1759bool
1761{
1763 return proxyProtocolError("PROXY client not permitted by default ACL");
1764
1766 fillChecklist(ch);
1767 if (!ch.fastCheck().allowed())
1768 return proxyProtocolError("PROXY client not permitted by ACLs");
1769
1770 return true;
1771}
1772
1778bool
1780{
1781 if (msg) {
1782 // This is important to know, but maybe not so much that flooding the log is okay.
1783#if QUIET_PROXY_PROTOCOL
1784 // display the first of every 32 occurrences at level 1, the others at level 2.
1785 static uint8_t hide = 0;
1786 debugs(33, (hide++ % 32 == 0 ? DBG_IMPORTANT : 2), msg << " from " << clientConnection);
1787#else
1788 debugs(33, DBG_IMPORTANT, msg << " from " << clientConnection);
1789#endif
1790 mustStop(msg);
1791 }
1792 return false;
1793}
1794
1799bool
1801{
1802 try {
1803 const auto parsed = ProxyProtocol::Parse(inBuf);
1804 proxyProtocolHeader_ = parsed.header;
1806 inBuf.consume(parsed.size);
1808 if (proxyProtocolHeader_->hasForwardedAddresses()) {
1809 clientConnection->local = proxyProtocolHeader_->destinationAddress;
1812 clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY spoofing of this new IP.
1813 debugs(33, 5, "PROXY/" << proxyProtocolHeader_->version() << " upgrade: " << clientConnection);
1814 }
1816 debugs(33, 3, "PROXY protocol: waiting for more than " << inBuf.length() << " bytes");
1817 return false;
1818 } catch (const std::exception &e) {
1819 return proxyProtocolError(e.what());
1820 }
1821 return true;
1822}
1823
1824void
1833
1836void
1838{
1839 debugs(33, 5, clientConnection << ": attempting to parse");
1840
1841 // Loop while we have read bytes that are not needed for producing the body
1842 // On errors, bodyPipe may become nil, but readMore will be cleared
1843 while (!inBuf.isEmpty() && !bodyPipe && flags.readMore) {
1844
1845 // Prohibit concurrent requests when using a pinned to-server connection
1846 // because our Client classes do not support request pipelining.
1847 if (pinning.pinned && !pinning.readHandler) {
1848 debugs(33, 3, clientConnection << " waits for busy " << pinning.serverConnection);
1849 break;
1850 }
1851
1852 /* Limit the number of concurrent requests */
1854 break;
1855
1856 // try to parse the PROXY protocol header magic bytes
1859 break;
1860
1861 // we have been waiting for PROXY to provide client-IP
1862 // for some lookups, ie rDNS
1864
1865 // Done with PROXY protocol which has cleared preservingClientData_.
1866 // If the next protocol supports on_unsupported_protocol, then its
1867 // parseOneRequest() must reset preservingClientData_.
1869 }
1870
1871 if (Http::StreamPointer context = parseOneRequest()) {
1872 debugs(33, 5, clientConnection << ": done parsing a request");
1874 context->registerWithConn();
1875
1876#if USE_OPENSSL
1877 if (switchedToHttps())
1879#endif
1880
1881 processParsedRequest(context);
1882
1883 if (context->mayUseConnection()) {
1884 debugs(33, 3, "Not parsing new requests, as this request may need the connection");
1885 break;
1886 }
1887 } else {
1888 debugs(33, 5, clientConnection << ": not enough request data: " <<
1889 inBuf.length() << " < " << Config.maxRequestHeaderSize);
1891 break;
1892 }
1893 }
1894
1895 debugs(33, 7, "buffered leftovers: " << inBuf.length());
1896
1898 if (pipeline.empty()) {
1899 // we processed what we could parse, and no more data is coming
1900 debugs(33, 5, "closing half-closed without parsed requests: " << clientConnection);
1902 } else {
1903 // we parsed what we could, and no more data is coming
1904 debugs(33, 5, "monitoring half-closed while processing parsed requests: " << clientConnection);
1905 flags.readMore = false; // may already be false
1906 }
1907 }
1908}
1909
1910void
1912{
1913#if USE_OPENSSL
1914 if (parsingTlsHandshake) {
1916 return;
1917 }
1918#endif
1919
1920 /* Process next request */
1921 if (pipeline.empty())
1922 fd_note(clientConnection->fd, "Reading next request");
1923
1924 parseRequests();
1925
1926 if (!isOpen())
1927 return;
1928
1930}
1931
1938bool
1940{
1941 // if we are reading a body, stuff data into the body pipe
1942 if (bodyPipe != nullptr)
1943 return handleRequestBodyData();
1944 return true;
1945}
1946
1954bool
1956{
1957 assert(bodyPipe != nullptr);
1958
1959 if (bodyParser) { // chunked encoding
1960 if (const err_type error = handleChunkedRequestBody()) {
1962 return false;
1963 }
1964 } else { // identity encoding
1965 debugs(33,5, "handling plain request body for " << clientConnection);
1966 const auto putSize = bodyPipe->putMoreData(inBuf.rawContent(), inBuf.length());
1967 if (putSize > 0)
1968 consumeInput(putSize);
1969
1970 if (!bodyPipe->mayNeedMoreData()) {
1971 // BodyPipe will clear us automagically when we produced everything
1972 bodyPipe = nullptr;
1973 }
1974 }
1975
1976 if (!bodyPipe) {
1977 debugs(33,5, "produced entire request body for " << clientConnection);
1978
1979 if (const char *reason = stoppedSending()) {
1980 /* we've finished reading like good clients,
1981 * now do the close that initiateClose initiated.
1982 */
1983 debugs(33, 3, "closing for earlier sending error: " << reason);
1985 return false;
1986 }
1987 }
1988
1989 return true;
1990}
1991
1995{
1996 debugs(33, 7, "chunked from " << clientConnection << ": " << inBuf.length());
1997
1998 try { // the parser will throw on errors
1999
2000 if (inBuf.isEmpty()) // nothing to do
2001 return ERR_NONE;
2002
2005 const bool parsed = bodyParser->parse(inBuf);
2006 inBuf = bodyParser->remaining(); // sync buffers
2007 bpc.checkIn();
2008
2009 // dechunk then check: the size limit applies to _dechunked_ content
2011 return ERR_TOO_BIG;
2012
2013 if (parsed) {
2015 Must(!bodyPipe);
2016 return ERR_NONE; // nil bodyPipe implies body end for the caller
2017 }
2018
2019 // if chunk parser needs data, then the body pipe must need it too
2021
2022 // if parser needs more space and we can consume nothing, we will stall
2024 } catch (...) { // TODO: be more specific
2025 debugs(33, 3, "malformed chunks" << bodyPipe->status());
2026 return ERR_INVALID_REQ;
2027 }
2028
2029 debugs(33, 7, "need more chunked data" << bodyPipe->status());
2030 return ERR_NONE;
2031}
2032
2034void
2036{
2038
2039 // XXX: The code below works if we fail during initial request parsing,
2040 // but if we fail when the server connection is used already, the server may send
2041 // us its response too, causing various assertions. How to prevent that?
2042#if WE_KNOW_HOW_TO_SEND_ERRORS
2044 if (context != NULL && !context->http->out.offset) { // output nothing yet
2045 clientStreamNode *node = context->getClientReplyContext();
2046 clientReplyContext *repContext = dynamic_cast<clientReplyContext*>(node->data.getRaw());
2047 assert(repContext);
2048 const Http::StatusCode scode = (error == ERR_TOO_BIG) ?
2049 Http::scContentTooLarge : HTTP_BAD_REQUEST;
2050 repContext->setReplyToError(error, scode,
2051 repContext->http->uri,
2052 CachePeer,
2053 repContext->http->request,
2054 inBuf, nullptr);
2055 context->pullData();
2056 } else {
2057 // close or otherwise we may get stuck as nobody will notice the error?
2059 }
2060#else
2061 debugs(33, 3, "aborting chunked request without error " << error);
2063#endif
2064 flags.readMore = false;
2065}
2066
2067void
2069{
2070 // request reader may get stuck waiting for space if nobody consumes body
2071 if (bodyPipe != nullptr)
2073
2074 // kids extend
2075}
2076
2078void
2080{
2081 if (!Comm::IsConnOpen(io.conn))
2082 return;
2083
2086 if (tunnelOnError(error))
2087 return;
2088
2089 /*
2090 * Just close the connection to not confuse browsers
2091 * using persistent connections. Some browsers open
2092 * a connection and then do not use it until much
2093 * later (presumably because the request triggering
2094 * the open has already been completed on another
2095 * connection)
2096 */
2097 debugs(33, 3, "requestTimeout: FD " << io.fd << ": lifetime is expired.");
2098 io.conn->close();
2099}
2100
2101void
2103{
2104 debugs(33, DBG_IMPORTANT, "WARNING: Closing client connection due to lifetime timeout" <<
2105 Debug::Extra << "connection: " << io.conn);
2106
2107 LogTagsErrors lte;
2108 lte.timedout = true;
2110}
2111
2113 AsyncJob("ConnStateData"), // kids overwrite
2114 Server(xact)
2115#if USE_OPENSSL
2116 , tlsParser(Security::HandshakeParser::fromClient)
2117#endif
2118{
2119 // store the details required for creating more MasterXaction objects as new requests come in
2120 log_addr = xact->tcpClient->remote;
2122
2123 // register to receive notice of Squid signal events
2124 // which may affect long persisting client connections
2126}
2127
2128void
2130{
2133
2134 if (port->disable_pmtu_discovery != DISABLE_PMTU_OFF &&
2135 (transparent() || port->disable_pmtu_discovery == DISABLE_PMTU_ALWAYS)) {
2136#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
2137 int i = IP_PMTUDISC_DONT;
2138 if (xsetsockopt(clientConnection->fd, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0) {
2139 int xerrno = errno;
2140 debugs(33, 2, "WARNING: Path MTU discovery disabling failed on " << clientConnection << " : " << xstrerr(xerrno));
2141 }
2142#else
2143 static bool reported = false;
2144
2145 if (!reported) {
2146 debugs(33, DBG_IMPORTANT, "WARNING: Path MTU discovery disabling is not supported on your platform.");
2147 reported = true;
2148 }
2149#endif
2150 }
2151
2155
2156 needProxyProtocolHeader_ = port->flags.proxySurrogate;
2158 if (!proxyProtocolValidateClient()) // will close the connection on failure
2159 return;
2160 } else
2162
2163 // requires needProxyProtocolHeader_ which is initialized above
2165}
2166
2167void
2169{
2173
2175
2176#if USE_DELAY_POOLS
2177 fd_table[clientConnection->fd].clientInfo = nullptr;
2178
2179 if (!Config.onoff.client_db)
2180 return; // client delay pools require client_db
2181
2182 const auto &pools = ClientDelayPools::Instance()->pools;
2183 if (pools.size()) {
2184 ACLFilledChecklist ch(nullptr, nullptr);
2185 fillChecklist(ch);
2186 // TODO: we check early to limit error response bandwidth but we
2187 // should recheck when we can honor delay_pool_uses_indirect
2188 for (unsigned int pool = 0; pool < pools.size(); ++pool) {
2189
2190 /* pools require explicit 'allow' to assign a client into them */
2191 if (pools[pool]->access) {
2192 ch.changeAcl(pools[pool]->access);
2193 const auto &answer = ch.fastCheck();
2194 if (answer.allowed()) {
2195
2196 /* request client information from db after we did all checks
2197 this will save hash lookup if client failed checks */
2199 assert(cli);
2200
2201 /* put client info in FDE */
2202 fd_table[clientConnection->fd].clientInfo = cli;
2203
2204 /* setup write limiter for this request */
2205 const double burst = floor(0.5 +
2206 (pools[pool]->highwatermark * Config.ClientDelay.initial)/100.0);
2207 cli->setWriteLimiter(pools[pool]->rate, burst, pools[pool]->highwatermark);
2208 break;
2209 } else {
2210 debugs(83, 4, "Delay pool " << pool << " skipped because ACL " << answer);
2211 }
2212 }
2213 }
2214 }
2215#endif
2216
2217 // kids must extend to actually start doing something (e.g., reading)
2218}
2219
2222{
2223 const auto handshakeResult = Security::Accept(*clientConnection);
2224
2225#if USE_OPENSSL
2226 // log ASAP, even if the handshake has not completed (or failed)
2227 const auto fd = clientConnection->fd;
2228 assert(fd >= 0);
2229 keyLogger.checkpoint(*fd_table[fd].ssl, *this);
2230#else
2231 // TODO: Support fd_table[fd].ssl dereference in other builds.
2232#endif
2233
2234 return handshakeResult;
2235}
2236
2238void
2240{
2241 Assure(params.port);
2242
2243 // NP: it is possible the port was reconfigured when the call or accept() was queued.
2244
2245 if (params.flag != Comm::OK) {
2246 // Its possible the call was still queued when the client disconnected
2247 debugs(33, 2, params.port->listenConn << ": accept failure: " << xstrerr(params.xerrno));
2248 return;
2249 }
2250
2251 debugs(33, 4, params.conn << ": accepted");
2252 fd_note(params.conn->fd, "client http connect");
2253 const auto xact = MasterXaction::MakePortful(params.port);
2254 xact->tcpClient = params.conn;
2255
2256 // Socket is ready, setup the connection manager to start using it
2257 auto *srv = Http::NewServer(xact);
2258 // XXX: do not abandon the MasterXaction object
2259 AsyncJob::Start(srv); // usually async-calls readSomeData()
2260}
2261
2263static bool
2265{
2266 const auto conn = connState->clientConnection;
2267 if (Security::CreateServerSession(ctx, conn, connState->port->secure, "client https start")) {
2268 debugs(33, 5, "will negotiate TLS on " << conn);
2269 return true;
2270 }
2271
2272 debugs(33, DBG_IMPORTANT, "ERROR: could not create TLS server context for " << conn);
2273 conn->close();
2274 return false;
2275}
2276
2278static void
2279clientNegotiateSSL(int fd, void *data)
2280{
2281 ConnStateData *conn = (ConnStateData *)data;
2282
2283 const auto handshakeResult = conn->acceptTls();
2284 switch (handshakeResult.category) {
2286 break;
2287
2290 return;
2291
2294 return;
2295
2297 debugs(83, (handshakeResult.important ? Important(62) : 2), "ERROR: Cannot accept a TLS connection" <<
2298 Debug::Extra << "problem: " << WithExtras(handshakeResult));
2299 // TODO: No ConnStateData::tunnelOnError() on this forward-proxy code
2300 // path because we cannot know the intended connection target?
2301 conn->updateError(ERR_SECURE_ACCEPT_FAIL, handshakeResult.errorDetail);
2302 conn->clientConnection->close();
2303 return;
2304 }
2305
2306 Security::SessionPointer session(fd_table[fd].ssl);
2307
2308#if USE_OPENSSL
2309 if (Security::SessionIsResumed(session)) {
2310 debugs(83, 2, "Session " << SSL_get_session(session.get()) <<
2311 " reused on FD " << fd << " (" << fd_table[fd].ipaddr <<
2312 ":" << (int)fd_table[fd].remote_port << ")");
2313 } else {
2314 if (Debug::Enabled(83, 4)) {
2315 /* Write out the SSL session details.. actually the call below, but
2316 * OpenSSL headers do strange typecasts confusing GCC.. */
2317 /* PEM_write_SSL_SESSION(DebugStream(), SSL_get_session(ssl)); */
2318#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00908000L
2319 PEM_ASN1_write(reinterpret_cast<i2d_of_void *>(i2d_SSL_SESSION),
2320 PEM_STRING_SSL_SESSION, DebugStream(),
2321 reinterpret_cast<char *>(SSL_get_session(session.get())),
2322 nullptr, nullptr, 0, nullptr, nullptr);
2323
2324#elif (ALLOW_ALWAYS_SSL_SESSION_DETAIL == 1)
2325
2326 /* When using gcc 3.3.x and OpenSSL 0.9.7x sometimes a compile error can occur here.
2327 * This is caused by an unpredictable gcc behaviour on a cast of the first argument
2328 * of PEM_ASN1_write(). For this reason this code section is disabled. To enable it,
2329 * define ALLOW_ALWAYS_SSL_SESSION_DETAIL=1.
2330 * Because there are two possible usable cast, if you get an error here, try the other
2331 * commented line. */
2332
2333 PEM_ASN1_write((int(*)())i2d_SSL_SESSION, PEM_STRING_SSL_SESSION,
2334 DebugStream(),
2335 reinterpret_cast<char *>(SSL_get_session(session.get())),
2336 nullptr, nullptr, 0, nullptr, nullptr);
2337 /* PEM_ASN1_write((int(*)(...))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION,
2338 DebugStream(),
2339 reinterpret_cast<char *>(SSL_get_session(session.get())),
2340 nullptr, nullptr, 0, nullptr, nullptr);
2341 */
2342#else
2343 debugs(83, 4, "With " OPENSSL_VERSION_TEXT ", session details are available only defining ALLOW_ALWAYS_SSL_SESSION_DETAIL=1 in the source.");
2344
2345#endif
2346 /* Note: This does not automatically fflush the log file.. */
2347 }
2348
2349 debugs(83, 2, "New session " << SSL_get_session(session.get()) <<
2350 " on FD " << fd << " (" << fd_table[fd].ipaddr << ":" <<
2351 fd_table[fd].remote_port << ")");
2352 }
2353#else
2354 debugs(83, 2, "TLS session reuse not yet implemented.");
2355#endif
2356
2357 // Connection established. Retrieve TLS connection parameters for logging.
2359
2360#if USE_OPENSSL
2361 X509 *client_cert = SSL_get_peer_certificate(session.get());
2362
2363 if (client_cert) {
2364 debugs(83, 3, "FD " << fd << " client certificate: subject: " <<
2365 Security::SubjectName(*client_cert));
2366
2367 debugs(83, 3, "FD " << fd << " client certificate: issuer: " <<
2368 Security::IssuerName(*client_cert));
2369
2370 X509_free(client_cert);
2371 } else {
2372 debugs(83, 5, "FD " << fd << " has no client certificate.");
2373 }
2374#else
2375 debugs(83, 2, "Client certificate requesting not yet implemented.");
2376#endif
2377
2378 // If we are called, then bumped CONNECT has succeeded. Finalize it.
2379 if (auto xact = conn->pipeline.front()) {
2380 if (xact->http && xact->http->request && xact->http->request->method == Http::METHOD_CONNECT)
2381 xact->finished();
2382 // cannot proceed with encryption if requests wait for plain responses
2383 Must(conn->pipeline.empty());
2384 }
2385 /* careful: finished() above frees request, host, etc. */
2386
2387 conn->readSomeData();
2388}
2389
2394static void
2396{
2397 assert(connState);
2398 const Comm::ConnectionPointer &details = connState->clientConnection;
2399
2400 if (!ctx || !httpsCreate(connState, ctx))
2401 return;
2402
2404
2405 Comm::SetSelect(details->fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
2406}
2407
2408#if USE_OPENSSL
2412static void
2414{
2415 ConnStateData *connState = (ConnStateData *) data;
2416
2417 // if the connection is closed or closing, just return.
2418 if (!connState->isOpen())
2419 return;
2420
2421 if (answer.allowed()) {
2422 debugs(33, 2, "sslBump action " << Ssl::bumpMode(answer.kind) << "needed for " << connState->clientConnection);
2423 connState->sslBumpMode = static_cast<Ssl::BumpMode>(answer.kind);
2424 } else {
2425 debugs(33, 3, "sslBump not needed for " << connState->clientConnection);
2426 connState->sslBumpMode = Ssl::bumpSplice;
2427 }
2428
2429 if (connState->sslBumpMode == Ssl::bumpTerminate) {
2430 connState->clientConnection->close();
2431 return;
2432 }
2433
2434 if (!connState->fakeAConnectRequest("ssl-bump", connState->inBuf))
2435 connState->clientConnection->close();
2436}
2437#endif
2438
2440static void
2442{
2443 Assure(params.port);
2444
2445 // NP: it is possible the port was reconfigured when the call or accept() was queued.
2446
2447 if (params.flag != Comm::OK) {
2448 // Its possible the call was still queued when the client disconnected
2449 debugs(33, 2, "httpsAccept: " << params.port->listenConn << ": accept failure: " << xstrerr(params.xerrno));
2450 return;
2451 }
2452
2453 const auto xact = MasterXaction::MakePortful(params.port);
2454 xact->tcpClient = params.conn;
2455
2456 debugs(33, 4, params.conn << " accepted, starting SSL negotiation.");
2457 fd_note(params.conn->fd, "client https connect");
2458
2459 // Socket is ready, setup the connection manager to start using it
2460 auto *srv = Https::NewServer(xact);
2461 // XXX: do not abandon the MasterXaction object
2462 AsyncJob::Start(srv); // usually async-calls postHttpsAccept()
2463}
2464
2465void
2467{
2468 if (port->flags.tunnelSslBumping) {
2469#if USE_OPENSSL
2470 debugs(33, 5, "accept transparent connection: " << clientConnection);
2471
2472 if (!Config.accessList.ssl_bump) {
2474 return;
2475 }
2476
2477 const auto mx = MasterXaction::MakePortful(port);
2478 mx->tcpClient = clientConnection;
2479 // Create a fake HTTP request and ALE for the ssl_bump ACL check,
2480 // using tproxy/intercept provided destination IP and port.
2481 // XXX: Merge with subsequent fakeAConnectRequest(), buildFakeRequest().
2482 // XXX: Do this earlier (e.g., in Http[s]::One::Server constructor).
2483 HttpRequest *request = new HttpRequest(mx);
2484 static char ip[MAX_IPSTRLEN];
2486 request->url.host(clientConnection->local.toStr(ip, sizeof(ip)));
2487 request->url.port(clientConnection->local.port());
2488 request->myportname = port->name;
2489 const AccessLogEntry::Pointer connectAle = new AccessLogEntry;
2490 CodeContext::Reset(connectAle);
2491 // TODO: Use these request/ALE when waiting for new bumped transactions.
2492
2493 auto acl_checklist = ACLFilledChecklist::Make(Config.accessList.ssl_bump, request);
2494 fillChecklist(*acl_checklist);
2495 // Build a local AccessLogEntry to allow requiresAle() acls work
2496 acl_checklist->al = connectAle;
2497 acl_checklist->al->cache.start_time = current_time;
2498 acl_checklist->al->tcpClient = clientConnection;
2499 acl_checklist->al->cache.port = port;
2500 acl_checklist->al->cache.caddr = log_addr;
2501 acl_checklist->al->proxyProtocolHeader = proxyProtocolHeader_;
2502 acl_checklist->al->updateError(bareError);
2503 HTTPMSGUNLOCK(acl_checklist->al->request);
2504 acl_checklist->al->request = request;
2505 HTTPMSGLOCK(acl_checklist->al->request);
2507 ClientHttpRequest *http = context ? context->http : nullptr;
2508 const char *log_uri = http ? http->log_uri : nullptr;
2509 acl_checklist->syncAle(request, log_uri);
2511#else
2512 fatal("FATAL: SSL-Bump requires --with-openssl");
2513#endif
2514 return;
2515 } else {
2516 httpsEstablish(this, port->secure.staticContext);
2517 }
2518}
2519
2520#if USE_OPENSSL
2521void
2523{
2524 ConnStateData * state_data = (ConnStateData *)(data);
2525 state_data->sslCrtdHandleReply(reply);
2526}
2527
2528void
2530{
2531 if (!isOpen()) {
2532 debugs(33, 3, "Connection gone while waiting for ssl_crtd helper reply; helper reply:" << reply);
2533 return;
2534 }
2535
2536 if (reply.result == Helper::BrokenHelper) {
2537 debugs(33, 5, "Certificate for " << tlsConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply);
2538 } else if (!reply.other().hasContent()) {
2539 debugs(1, DBG_IMPORTANT, "\"ssl_crtd\" helper returned <NULL> reply.");
2540 } else {
2542 if (reply_message.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK) {
2543 debugs(33, 5, "Reply from ssl_crtd for " << tlsConnectHostOrIp << " is incorrect");
2544 } else {
2545 if (reply.result != Helper::Okay) {
2546 debugs(33, 5, "Certificate for " << tlsConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply_message.getBody());
2547 } else {
2548 debugs(33, 5, "Certificate for " << tlsConnectHostOrIp << " was successfully received from ssl_crtd");
2551 auto ssl = fd_table[clientConnection->fd].ssl.get();
2552 bool ret = Ssl::configureSSLUsingPkeyAndCertFromMemory(ssl, reply_message.getBody().c_str(), *port);
2553 if (!ret)
2554 debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
2555
2558 } else {
2560 if (ctx && !sslBumpCertKey.isEmpty())
2562 getSslContextDone(ctx);
2563 }
2564 return;
2565 }
2566 }
2567 }
2569 getSslContextDone(nil);
2570}
2571
2573{
2575
2576 const bool connectedOk = sslServerBump && sslServerBump->connectedOk();
2577 if (connectedOk) {
2578 if (X509 *mimicCert = sslServerBump->serverCert.get())
2579 certProperties.mimicCert.resetAndLock(mimicCert);
2580
2581 ACLFilledChecklist checklist(nullptr, sslServerBump->request.getRaw());
2582 fillChecklist(checklist);
2583
2584 for (sslproxy_cert_adapt *ca = Config.ssl_client.cert_adapt; ca != nullptr; ca = ca->next) {
2585 // If the algorithm already set, then ignore it.
2586 if ((ca->alg == Ssl::algSetCommonName && certProperties.setCommonName) ||
2587 (ca->alg == Ssl::algSetValidAfter && certProperties.setValidAfter) ||
2588 (ca->alg == Ssl::algSetValidBefore && certProperties.setValidBefore) )
2589 continue;
2590
2591 if (ca->aclList && checklist.fastCheck(ca->aclList).allowed()) {
2592 const char *alg = Ssl::CertAdaptAlgorithmStr[ca->alg];
2593 const char *param = ca->param;
2594
2595 // For parameterless CN adaptation, use hostname from the
2596 // CONNECT request.
2597 if (ca->alg == Ssl::algSetCommonName) {
2598 if (!param)
2599 param = tlsConnectHostOrIp.c_str();
2600 certProperties.commonName = param;
2601 certProperties.setCommonName = true;
2602 } else if (ca->alg == Ssl::algSetValidAfter)
2603 certProperties.setValidAfter = true;
2604 else if (ca->alg == Ssl::algSetValidBefore)
2605 certProperties.setValidBefore = true;
2606
2607 debugs(33, 5, "Matches certificate adaptation algorithm: " <<
2608 alg << " param: " << (param ? param : "-"));
2609 }
2610 }
2611
2612 certProperties.signAlgorithm = Ssl::algSignEnd;
2613 for (sslproxy_cert_sign *sg = Config.ssl_client.cert_sign; sg != nullptr; sg = sg->next) {
2614 if (sg->aclList && checklist.fastCheck(sg->aclList).allowed()) {
2615 certProperties.signAlgorithm = (Ssl::CertSignAlgorithm)sg->alg;
2616 break;
2617 }
2618 }
2619 } else {// did not try to connect (e.g. client-first) or failed to connect
2620 // In case of an error while connecting to the secure server, use a
2621 // trusted certificate, with no mimicked fields and no adaptation
2622 // algorithms. There is nothing we can mimic, so we want to minimize the
2623 // number of warnings the user will have to see to get to the error page.
2624 // We will close the connection, so that the trust is not extended to
2625 // non-Squid content.
2626 certProperties.signAlgorithm = Ssl::algSignTrusted;
2627 }
2628
2629 assert(certProperties.signAlgorithm != Ssl::algSignEnd);
2630
2631 if (certProperties.signAlgorithm == Ssl::algSignUntrusted) {
2632 assert(port->secure.untrustedSigningCa.cert);
2633 certProperties.signWithX509.resetAndLock(port->secure.untrustedSigningCa.cert.get());
2634 certProperties.signWithPkey.resetAndLock(port->secure.untrustedSigningCa.pkey.get());
2635 } else {
2636 assert(port->secure.signingCa.cert.get());
2637 certProperties.signWithX509.resetAndLock(port->secure.signingCa.cert.get());
2638
2639 if (port->secure.signingCa.pkey)
2640 certProperties.signWithPkey.resetAndLock(port->secure.signingCa.pkey.get());
2641 }
2642 signAlgorithm = certProperties.signAlgorithm;
2643
2644 certProperties.signHash = Ssl::DefaultSignHash;
2645}
2646
2649{
2650 debugs(33, 5, "Finding SSL certificate for " << cacheKey << " in cache");
2651 const auto ssl_ctx_cache = Ssl::TheGlobalContextStorage().getLocalStorage(port->s);
2652 if (const auto ctx = ssl_ctx_cache ? ssl_ctx_cache->get(cacheKey) : nullptr) {
2653 if (Ssl::verifySslCertificate(*ctx, certProperties)) {
2654 debugs(33, 5, "Cached SSL certificate for " << certProperties.commonName << " is valid");
2655 return *ctx;
2656 } else {
2657 debugs(33, 5, "Cached SSL certificate for " << certProperties.commonName << " is out of date. Delete this certificate from cache");
2658 if (ssl_ctx_cache)
2659 ssl_ctx_cache->del(cacheKey);
2660 }
2661 }
2662 return Security::ContextPointer(nullptr);
2663}
2664
2665void
2667{
2668 const auto ssl_ctx_cache = Ssl::TheGlobalContextStorage().getLocalStorage(port->s);
2669 if (!ssl_ctx_cache || !ssl_ctx_cache->add(cacheKey, ctx)) {
2670 // If it is not in storage delete after using. Else storage deleted it.
2671 fd_table[clientConnection->fd].dynamicTlsContext = ctx;
2672 }
2673}
2674
2675void
2677{
2678 if (port->secure.generateHostCertificates) {
2679 Ssl::CertificateProperties certProperties;
2680 buildSslCertGenerationParams(certProperties);
2681
2682 // Disable caching for bumpPeekAndSplice mode
2687
2689 if (ctx) {
2690 getSslContextDone(ctx);
2691 return;
2692 }
2693 }
2694
2695#if USE_SSL_CRTD
2696 try {
2697 debugs(33, 5, "Generating SSL certificate for " << certProperties.commonName << " using ssl_crtd.");
2700 request_message.composeRequest(certProperties);
2701 debugs(33, 5, "SSL crtd request: " << request_message.compose().c_str());
2702 Ssl::Helper::Submit(request_message, sslCrtdHandleReplyWrapper, this);
2703 return;
2704 } catch (const std::exception &e) {
2705 debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtd " <<
2706 "request for " << certProperties.commonName <<
2707 " certificate: " << e.what() << "; will now block to " <<
2708 "generate that certificate.");
2709 // fall through to do blocking in-process generation.
2710 }
2711#endif // USE_SSL_CRTD
2712
2713 debugs(33, 5, "Generating SSL certificate for " << certProperties.commonName);
2716 auto ssl = fd_table[clientConnection->fd].ssl.get();
2717 if (!Ssl::configureSSL(ssl, certProperties, *port))
2718 debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
2719
2722 } else {
2724 if (dynCtx && !sslBumpCertKey.isEmpty())
2726 getSslContextDone(dynCtx);
2727 }
2728 return;
2729 }
2730
2732 getSslContextDone(nil);
2733}
2734
2735void
2737{
2738 if (port->secure.generateHostCertificates && !ctx) {
2739 debugs(33, 2, "Failed to generate TLS context for " << tlsConnectHostOrIp);
2740 }
2741
2742 // If generated ssl context = nullptr, try to use static ssl context.
2743 if (!ctx) {
2744 if (!port->secure.staticContext) {
2745 debugs(83, DBG_IMPORTANT, "Closing " << clientConnection->remote << " as lacking TLS context");
2747 return;
2748 } else {
2749 debugs(33, 5, "Using static TLS context.");
2750 ctx = port->secure.staticContext;
2751 }
2752 }
2753
2754 if (!httpsCreate(this, ctx))
2755 return;
2756
2757 // bumped intercepted conns should already have Config.Timeout.request set
2758 // but forwarded connections may only have Config.Timeout.lifetime. [Re]set
2759 // to make sure the connection does not get stuck on non-SSL clients.
2761
2762 switchedToHttps_ = true;
2763
2764 auto ssl = fd_table[clientConnection->fd].ssl.get();
2765 BIO *b = SSL_get_rbio(ssl);
2766 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(BIO_get_data(b));
2767 bio->setReadBufData(inBuf);
2768 inBuf.clear();
2770}
2771
2772void
2774{
2776 Must(http->request);
2777 auto &request = http->request;
2778
2779 // Depending on receivedFirstByte_, we are at the start of either an
2780 // established CONNECT tunnel with the client or an intercepted TCP (and
2781 // presumably TLS) connection from the client. Expect TLS Client Hello.
2782 const auto insideConnectTunnel = receivedFirstByte_;
2783 debugs(33, 5, (insideConnectTunnel ? "post-CONNECT " : "raw TLS ") << clientConnection);
2784
2785 tlsConnectHostOrIp = request->url.hostOrIp();
2786 tlsConnectPort = request->url.port();
2787 resetSslCommonName(request->url.host());
2788
2789 // We are going to read new request
2790 flags.readMore = true;
2791
2792 // keep version major.minor details the same.
2793 // but we are now performing the HTTPS handshake traffic
2795
2796 // If sslServerBump is set, then we have decided to deny CONNECT
2797 // and now want to switch to SSL to send the error to the client
2798 // without even peeking at the origin server certificate.
2799 if (bumpServerMode == Ssl::bumpServerFirst && !sslServerBump) {
2800 request->flags.sslPeek = true;
2801 sslServerBump = new Ssl::ServerBump(http);
2802 } else if (bumpServerMode == Ssl::bumpPeek || bumpServerMode == Ssl::bumpStare) {
2803 request->flags.sslPeek = true;
2804 sslServerBump = new Ssl::ServerBump(http, nullptr, bumpServerMode);
2805 }
2806
2807 // commSetConnTimeout() was called for this request before we switched.
2808 // Fix timeout to request_start_timeout
2810 // Also reset receivedFirstByte_ flag to allow this timeout work in the case we have
2811 // a bumped "connect" request on non transparent port.
2812 receivedFirstByte_ = false;
2813 // Get more data to peek at Tls
2814 parsingTlsHandshake = true;
2815
2816 // If the protocol has changed, then reset preservingClientData_.
2817 // Otherwise, its value initially set in start() is still valid/fresh.
2818 // shouldPreserveClientData() uses parsingTlsHandshake which is reset above.
2819 if (insideConnectTunnel)
2821
2822 readSomeData();
2823}
2824
2825void
2827{
2829
2830 assert(!inBuf.isEmpty());
2832 fd_note(clientConnection->fd, "Parsing TLS handshake");
2833
2834 // stops being nil if we fail to parse the handshake
2835 ErrorDetail::Pointer parseErrorDetails;
2836
2837 try {
2838 if (!tlsParser.parseHello(inBuf)) {
2839 // need more data to finish parsing
2840 readSomeData();
2841 return;
2842 }
2843 }
2844 catch (const TextException &ex) {
2845 debugs(83, 2, "exception: " << ex);
2846 parseErrorDetails = new ExceptionErrorDetail(ex.id());
2847 }
2848 catch (...) {
2849 debugs(83, 2, "exception: " << CurrentException);
2850 static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_PARSE");
2851 parseErrorDetails = d;
2852 }
2853
2854 parsingTlsHandshake = false;
2855
2856 // client data may be needed for splicing and for
2857 // tunneling unsupportedProtocol after an error
2859
2860 // Even if the parser failed, each TLS detail should either be set
2861 // correctly or still be "unknown"; copying unknown detail is a no-op.
2864 if (details && !details->serverName.isEmpty()) {
2865 resetSslCommonName(details->serverName.c_str());
2866 tlsClientSni_ = details->serverName;
2867 }
2868
2869 // We should disable read/write handlers
2871
2872 if (parseErrorDetails) {
2874 Must(context && context->http);
2875 HttpRequest::Pointer request = context->http->request;
2876 debugs(83, 5, "Got something other than TLS Client Hello. Cannot SslBump.");
2877 updateError(ERR_PROTOCOL_UNKNOWN, parseErrorDetails);
2880 return;
2881 }
2882
2883 if (!sslServerBump || sslServerBump->act.step1 == Ssl::bumpClientFirst) { // Either means client-first.
2885 return;
2887 debugs(83, 5, "server-first skips step2; start forwarding the request");
2890 ClientHttpRequest *http = context ? context->http : nullptr;
2891 // will call httpsPeeked() with certificate and connection, eventually
2893 } else {
2896 }
2897}
2898
2899static void
2901{
2902 ConnStateData *connState = (ConnStateData *) data;
2903
2904 // if the connection is closed or closing, just return.
2905 if (!connState->isOpen())
2906 return;
2907
2908 debugs(33, 5, "Answer: " << answer << " kind:" << answer.kind);
2909 assert(connState->serverBump());
2910 Ssl::BumpMode bumpAction;
2911 if (answer.allowed()) {
2912 bumpAction = (Ssl::BumpMode)answer.kind;
2913 } else
2914 bumpAction = Ssl::bumpSplice;
2915
2916 connState->serverBump()->act.step2 = bumpAction;
2917 connState->sslBumpMode = bumpAction;
2918 Http::StreamPointer context = connState->pipeline.front();
2919 if (ClientHttpRequest *http = (context ? context->http : nullptr))
2920 http->al->ssl.bumpMode = bumpAction;
2921
2922 if (bumpAction == Ssl::bumpTerminate) {
2923 connState->clientConnection->close();
2924 } else if (bumpAction != Ssl::bumpSplice) {
2925 connState->startPeekAndSplice();
2926 } else if (!connState->splice())
2927 connState->clientConnection->close();
2928}
2929
2930bool
2932{
2933 // normally we can splice here, because we just got client hello message
2934
2935 // fde::ssl/tls_read_method() probably reads from our own inBuf. If so, then
2936 // we should not lose any raw bytes when switching to raw I/O here.
2937 if (fd_table[clientConnection->fd].ssl.get())
2938 fd_table[clientConnection->fd].useDefaultIo();
2939
2940 // XXX: assuming that there was an HTTP/1.1 CONNECT to begin with...
2941 // reset the current protocol to HTTP/1.1 (was "HTTPS" for the bumping process)
2943 assert(!pipeline.empty());
2945 Must(context);
2946 Must(context->http);
2947 ClientHttpRequest *http = context->http;
2948 HttpRequest::Pointer request = http->request;
2949 context->finished();
2950 if (transparent()) {
2951 // For transparent connections, make a new fake CONNECT request, now
2952 // with SNI as target. doCallout() checks, adaptations may need that.
2953 return fakeAConnectRequest("splice", preservedClientData);
2954 } else {
2955 // For non transparent connections make a new tunneled CONNECT, which
2956 // also sets the HttpRequest::flags::forceTunnel flag to avoid
2957 // respond with "Connection Established" to the client.
2958 // This fake CONNECT request required to allow use of SNI in
2959 // doCallout() checks and adaptations.
2960 return initiateTunneledRequest(request, "splice", preservedClientData);
2961 }
2962}
2963
2964void
2966{
2967 // This is the Step2 of the SSL bumping
2970 ClientHttpRequest *http = context ? context->http : nullptr;
2971
2974 // Run a accessList check to check if want to splice or continue bumping
2975
2977 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpNone));
2978 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpClientFirst));
2979 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpServerFirst));
2980 fillChecklist(*acl_checklist);
2982 return;
2983 }
2984
2985 // will call httpsPeeked() with certificate and connection, eventually
2986 Security::ContextPointer unConfiguredCTX(Ssl::createSSLContext(port->secure.signingCa.cert, port->secure.signingCa.pkey, port->secure));
2987 fd_table[clientConnection->fd].dynamicTlsContext = unConfiguredCTX;
2988
2989 if (!httpsCreate(this, unConfiguredCTX))
2990 return;
2991
2992 switchedToHttps_ = true;
2993
2994 auto ssl = fd_table[clientConnection->fd].ssl.get();
2995 BIO *b = SSL_get_rbio(ssl);
2996 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(BIO_get_data(b));
2997 bio->setReadBufData(inBuf);
2998 bio->hold(true);
2999
3000 // We have successfully parsed client Hello, but our TLS handshake parser is
3001 // forgiving. Now we use a TLS library to parse the same bytes, so that we
3002 // can honor on_unsupported_protocol if needed. If there are no errors, we
3003 // expect Security::Accept() to ask us to write (our) TLS server Hello. We
3004 // also allow an ioWantRead result in case some fancy TLS extension that
3005 // Squid does not yet understand requires reading post-Hello client bytes.
3006 const auto handshakeResult = acceptTls();
3007 if (!handshakeResult.wantsIo())
3008 return handleSslBumpHandshakeError(handshakeResult);
3009
3010 // We need to reset inBuf here, to be used by incoming requests in the case
3011 // of SSL bump
3012 inBuf.clear();
3013
3014 debugs(83, 5, "Peek and splice at step2 done. Start forwarding the request!!! ");
3017}
3018
3020void
3022{
3023 auto errCategory = ERR_NONE;
3024
3025 switch (handshakeResult.category) {
3027 static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_UNEXPECTED_SUCCESS");
3028 updateError(errCategory = ERR_GATEWAY_FAILURE, d);
3029 break;
3030 }
3031
3033 static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_UNEXPECTED_READ");
3034 updateError(errCategory = ERR_GATEWAY_FAILURE, d);
3035 break;
3036 }
3037
3039 static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_UNEXPECTED_WRITE");
3040 updateError(errCategory = ERR_GATEWAY_FAILURE, d);
3041 break;
3042 }
3043
3045 debugs(83, (handshakeResult.important ? DBG_IMPORTANT : 2), "ERROR: Cannot SslBump-accept a TLS connection" <<
3046 Debug::Extra << "problem: " << WithExtras(handshakeResult));
3047 updateError(errCategory = ERR_SECURE_ACCEPT_FAIL, handshakeResult.errorDetail);
3048 break;
3049
3050 }
3051
3052 if (!tunnelOnError(errCategory))
3054}
3055
3056void
3058{
3059 auto ssl = fd_table[clientConnection->fd].ssl.get();
3060 BIO *b = SSL_get_rbio(ssl);
3061 assert(b);
3062 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(BIO_get_data(b));
3063
3064 debugs(33, 5, "PeekAndSplice mode, proceed with client negotiation. Current state:" << SSL_state_string_long(ssl));
3065 bio->hold(false);
3066
3068 switchedToHttps_ = true;
3069}
3070
3071void
3073{
3074 Must(sslServerBump != nullptr);
3076 Must(pipeline.empty() || pipeline.front()->http == nullptr || pipeline.front()->http->request == pic.request.getRaw());
3077
3078 if (Comm::IsConnOpen(pic.connection)) {
3080 debugs(33, 5, "bumped HTTPS server: " << tlsConnectHostOrIp);
3081 } else
3082 debugs(33, 5, "Error while bumping: " << tlsConnectHostOrIp);
3083
3085}
3086
3087#endif /* USE_OPENSSL */
3088
3089bool
3090ConnStateData::initiateTunneledRequest(HttpRequest::Pointer const &cause, const char *reason, const SBuf &payload)
3091{
3092 // fake a CONNECT request to force connState to tunnel
3093 SBuf connectHost;
3094 AnyP::Port connectPort;
3095
3096 if (pinning.serverConnection != nullptr) {
3097 static char ip[MAX_IPSTRLEN];
3098 connectHost = pinning.serverConnection->remote.toStr(ip, sizeof(ip));
3099 if (const auto remotePort = pinning.serverConnection->remote.port())
3100 connectPort = remotePort;
3101 } else if (cause) {
3102 connectHost = cause->url.hostOrIp();
3103 connectPort = cause->url.port();
3104#if USE_OPENSSL
3105 } else if (!tlsConnectHostOrIp.isEmpty()) {
3106 connectHost = tlsConnectHostOrIp;
3107 connectPort = tlsConnectPort;
3108#endif
3109 } else if (transparent()) {
3110 static char ip[MAX_IPSTRLEN];
3111 connectHost = clientConnection->local.toStr(ip, sizeof(ip));
3112 connectPort = clientConnection->local.port();
3113 }
3114
3115 if (!connectPort) {
3116 // Typical cases are malformed HTTP requests on http_port and malformed
3117 // TLS handshakes on non-bumping https_port. TODO: Discover these
3118 // problems earlier so that they can be classified/detailed better.
3119 debugs(33, 2, "Not able to compute URL, abort request tunneling for " << reason);
3120 // TODO: throw when NonBlockingCheck() callbacks gain job protections
3121 static const auto d = MakeNamedErrorDetail("TUNNEL_TARGET");
3123 return false;
3124 }
3125
3126 debugs(33, 2, "Request tunneling for " << reason);
3127 const auto http = buildFakeRequest(connectHost, *connectPort, payload);
3128 HttpRequest::Pointer request = http->request;
3129 request->flags.forceTunnel = true;
3130 http->calloutContext = new ClientRequestContext(http);
3131 http->doCallouts();
3132 clientProcessRequestFinished(this, request);
3133 return true;
3134}
3135
3136bool
3137ConnStateData::fakeAConnectRequest(const char *reason, const SBuf &payload)
3138{
3139 debugs(33, 2, "fake a CONNECT request to force connState to tunnel for " << reason);
3140
3141 SBuf connectHost;
3143 const unsigned short connectPort = clientConnection->local.port();
3144
3145#if USE_OPENSSL
3146 if (!tlsClientSni_.isEmpty())
3147 connectHost.assign(tlsClientSni_);
3148 else
3149#endif
3150 {
3151 static char ip[MAX_IPSTRLEN];
3152 clientConnection->local.toHostStr(ip, sizeof(ip));
3153 connectHost.assign(ip);
3154 }
3155
3156 ClientHttpRequest *http = buildFakeRequest(connectHost, connectPort, payload);
3157
3158 http->calloutContext = new ClientRequestContext(http);
3159 HttpRequest::Pointer request = http->request;
3160 http->doCallouts();
3161 clientProcessRequestFinished(this, request);
3162 return true;
3163}
3164
3166ConnStateData::buildFakeRequest(SBuf &useHost, const AnyP::KnownPort usePort, const SBuf &payload)
3167{
3168 ClientHttpRequest *http = new ClientHttpRequest(this);
3169 Http::Stream *stream = new Http::Stream(clientConnection, http);
3170
3171 StoreIOBuffer tempBuffer;
3172 tempBuffer.data = stream->reqbuf;
3173 tempBuffer.length = HTTP_REQBUF_SZ;
3174
3175 ClientStreamData newServer = new clientReplyContext(http);
3176 ClientStreamData newClient = stream;
3179 clientSocketDetach, newClient, tempBuffer);
3180
3181 stream->flags.parsed_ok = 1; // Do we need it?
3182 stream->mayUseConnection(true);
3184 stream->registerWithConn();
3185
3186 const auto mx = MasterXaction::MakePortful(port);
3187 mx->tcpClient = clientConnection;
3188 // Setup Http::Request object. Maybe should be replaced by a call to (modified)
3189 // clientProcessRequest
3190 HttpRequest::Pointer request = new HttpRequest(mx);
3191 request->url.setScheme(AnyP::PROTO_AUTHORITY_FORM, nullptr);
3192 request->method = Http::METHOD_CONNECT;
3193 request->url.host(useHost.c_str());
3194 request->url.port(usePort);
3195
3196 http->uri = SBufToCstring(request->effectiveRequestUri());
3197 http->initRequest(request.getRaw());
3198
3199 request->manager(this, http->al);
3200
3201 request->header.putStr(Http::HOST, useHost.c_str());
3202
3203 request->sources |= ((switchedToHttps() || port->transport.protocol == AnyP::PROTO_HTTPS) ? Http::Message::srcHttps : Http::Message::srcHttp);
3204#if USE_AUTH
3205 if (getAuth())
3206 request->auth_user_request = getAuth();
3207#endif
3208
3209 inBuf = payload;
3210 flags.readMore = false;
3211
3212 return http;
3213}
3214
3216static bool
3218{
3219 if (!Comm::IsConnOpen(c)) {
3220 Must(NHttpSockets > 0); // we tried to open some
3221 --NHttpSockets; // there will be fewer sockets than planned
3222 Must(HttpSockets[NHttpSockets] < 0); // no extra fds received
3223
3224 if (!NHttpSockets) // we could not open any listen sockets at all
3225 fatalf("Unable to open %s",FdNote(portType));
3226
3227 return false;
3228 }
3229 return true;
3230}
3231
3233static bool
3235{
3236 bool found = false;
3237 for (int i = 0; i < NHttpSockets && !found; ++i) {
3238 if ((found = HttpSockets[i] < 0))
3239 HttpSockets[i] = conn->fd;
3240 }
3241 return found;
3242}
3243
3244static void
3246{
3247 const auto savedContext = CodeContext::Current();
3248 for (AnyP::PortCfgPointer s = HttpPortList; s != nullptr; s = s->next) {
3250 const SBuf &scheme = AnyP::UriScheme(s->transport.protocol).image();
3251
3253 debugs(1, DBG_IMPORTANT, "WARNING: You have too many '" << scheme << "_port' lines." <<
3254 Debug::Extra << "The limit is " << MAXTCPLISTENPORTS << " HTTP ports.");
3255 continue;
3256 }
3257
3258#if USE_OPENSSL
3259 if (s->flags.tunnelSslBumping) {
3260 if (!Config.accessList.ssl_bump) {
3261 debugs(33, DBG_IMPORTANT, "WARNING: No ssl_bump configured. Disabling ssl-bump on " << scheme << "_port " << s->s);
3262 s->flags.tunnelSslBumping = false;
3263 }
3264 if (!s->secure.staticContext && !s->secure.generateHostCertificates) {
3265 debugs(1, DBG_IMPORTANT, "Will not bump SSL at " << scheme << "_port " << s->s << " due to TLS initialization failure.");
3266 s->flags.tunnelSslBumping = false;
3267 if (s->transport.protocol == AnyP::PROTO_HTTP)
3268 s->secure.encryptTransport = false;
3269 }
3270 if (s->flags.tunnelSslBumping) {
3271 // Create ssl_ctx cache for this port.
3272 Ssl::TheGlobalContextStorage().addLocalStorage(s->s, s->secure.dynamicCertMemCacheSize);
3273 }
3274 }
3275#endif
3276
3277 if (s->secure.encryptTransport && !s->secure.staticContext) {
3278 debugs(1, DBG_CRITICAL, "ERROR: Ignoring " << scheme << "_port " << s->s << " due to TLS context initialization failure.");
3279 continue;
3280 }
3281
3282 const auto protocol = s->transport.protocol;
3283 assert(protocol == AnyP::PROTO_HTTP || protocol == AnyP::PROTO_HTTPS);
3284 const auto isHttps = protocol == AnyP::PROTO_HTTPS;
3285 using AcceptCall = CommCbFunPtrCallT<CommAcceptCbPtrFun>;
3286 RefCount<AcceptCall> subCall = commCbCall(5, 5, isHttps ? "httpsAccept" : "httpAccept",
3289 }
3290 CodeContext::Reset(savedContext);
3291}
3292
3293void
3295{
3296 // Fill out a Comm::Connection which IPC will open as a listener for us
3297 port->listenConn = new Comm::Connection;
3298 port->listenConn->local = port->s;
3299 port->listenConn->flags =
3301 (port->flags.tproxyIntercept ? COMM_TRANSPARENT : 0) |
3302 (port->flags.natIntercept ? COMM_INTERCEPTION : 0) |
3303 (port->workerQueues ? COMM_REUSEPORT : 0);
3304
3305 // route new connections to subCall
3306 typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
3308 const auto listenCall =
3309 asyncCall(33, 2, "clientListenerConnectionOpened",
3311 port, fdNote, sub));
3312 AsyncCallback<Ipc::StartListeningAnswer> callback(listenCall);
3313 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, port->listenConn, fdNote, callback);
3314
3317 ++NHttpSockets;
3318}
3319
3321static void
3323{
3324 Must(s != nullptr);
3325
3326 if (!OpenedHttpSocket(s->listenConn, portTypeNote))
3327 return;
3328
3329 Must(Comm::IsConnOpen(s->listenConn));
3330
3331 // TCP: setup a job to handle accept() with subscribed handler
3332 AsyncJob::Start(new Comm::TcpAcceptor(s, FdNote(portTypeNote), sub));
3333
3334 debugs(1, Important(13), "Accepting " <<
3335 (s->flags.natIntercept ? "NAT intercepted " : "") <<
3336 (s->flags.tproxyIntercept ? "TPROXY intercepted " : "") <<
3337 (s->flags.tunnelSslBumping ? "SSL bumped " : "") <<
3338 (s->flags.accelSurrogate ? "reverse-proxy " : "")
3339 << FdNote(portTypeNote) << " connections at "
3340 << s->listenConn);
3341
3342 Must(AddOpenedHttpSocket(s->listenConn)); // otherwise, we have received a fd we did not ask for
3343
3344#if HAVE_LIBSYSTEMD
3345 // When the very first port opens, tell systemd we are able to serve connections.
3346 // Subsequent sd_notify() calls, including calls during reconfiguration,
3347 // do nothing because the first call parameter is 1.
3348 // XXX: Send the notification only after opening all configured ports.
3350 const auto result = sd_notify(1, "READY=1");
3351 if (result < 0) {
3352 debugs(1, DBG_IMPORTANT, "WARNING: failed to send start-up notification to systemd" <<
3353 Debug::Extra << "sd_notify() error: " << xstrerr(-result));
3354 }
3355 }
3356#endif
3357}
3358
3359void
3361{
3364
3365 if (NHttpSockets < 1)
3366 fatal("No HTTP, HTTPS, or FTP ports configured");
3367}
3368
3369void
3371{
3372 const auto savedContext = CodeContext::Current();
3373 for (AnyP::PortCfgPointer s = HttpPortList; s != nullptr; s = s->next) {
3375 if (s->listenConn != nullptr) {
3376 debugs(1, Important(14), "Closing HTTP(S) port " << s->listenConn->local);
3377 s->listenConn->close();
3378 s->listenConn = nullptr;
3379 }
3380 }
3381 CodeContext::Reset(savedContext);
3382
3384
3385 // TODO see if we can drop HttpSockets array entirely */
3386 for (int i = 0; i < NHttpSockets; ++i) {
3387 HttpSockets[i] = -1;
3388 }
3389
3390 NHttpSockets = 0;
3391}
3392
3393int
3395{
3396 SBuf vary(request->vary_headers);
3397 const auto &reply = entry->mem().freshestReply();
3398 auto has_vary = reply.header.has(Http::HdrType::VARY);
3399#if X_ACCELERATOR_VARY
3400
3401 has_vary |=
3402 reply.header.has(Http::HdrType::HDR_X_ACCELERATOR_VARY);
3403#endif
3404
3405 if (!has_vary || entry->mem_obj->vary_headers.isEmpty()) {
3406 if (!vary.isEmpty()) {
3407 /* Oops... something odd is going on here.. */
3408 debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary object on second attempt, '" <<
3409 entry->mem_obj->urlXXX() << "' '" << vary << "'");
3410 request->vary_headers.clear();
3411 return VARY_CANCEL;
3412 }
3413
3414 if (!has_vary) {
3415 /* This is not a varying object */
3416 return VARY_NONE;
3417 }
3418
3419 /* virtual "vary" object found. Calculate the vary key and
3420 * continue the search
3421 */
3422 vary = httpMakeVaryMark(request, &reply);
3423
3424 if (!vary.isEmpty()) {
3425 request->vary_headers = vary;
3426 return VARY_OTHER;
3427 } else {
3428 /* Ouch.. we cannot handle this kind of variance */
3429 /* XXX This cannot really happen, but just to be complete */
3430 return VARY_CANCEL;
3431 }
3432 } else {
3433 if (vary.isEmpty()) {
3434 vary = httpMakeVaryMark(request, &reply);
3435
3436 if (!vary.isEmpty())
3437 request->vary_headers = vary;
3438 }
3439
3440 if (vary.isEmpty()) {
3441 /* Ouch.. we cannot handle this kind of variance */
3442 /* XXX This cannot really happen, but just to be complete */
3443 return VARY_CANCEL;
3444 } else if (vary.cmp(entry->mem_obj->vary_headers) == 0) {
3445 return VARY_MATCH;
3446 } else {
3447 /* Oops.. we have already been here and still haven't
3448 * found the requested variant. Bail out
3449 */
3450 debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary match on second attempt, '" <<
3451 entry->mem_obj->urlXXX() << "' '" << vary << "'");
3452 return VARY_CANCEL;
3453 }
3454 }
3455}
3456
3459{
3460 auto checklist = ACLFilledChecklist::Make(acl, nullptr);
3461 clientAclChecklistFill(*checklist, http);
3462 return checklist;
3463}
3464
3465void
3467{
3468 assert(http);
3469
3470 if (!checklist.request && http->request)
3471 checklist.setRequest(http->request);
3472
3473 if (!checklist.al && http->al) {
3474 checklist.updateAle(http->al);
3475 checklist.syncAle(http->request, http->log_uri);
3476 }
3477
3478 if (const auto conn = http->getConn())
3479 checklist.setConn(conn); // may already be set
3480}
3481
3482void
3484{
3485 const auto context = pipeline.front();
3486 if (const auto http = context ? context->http : nullptr)
3487 return clientAclChecklistFill(checklist, http); // calls checklist.setConn()
3488
3489 // no requests, but we always have connection-level details
3490 // TODO: ACL checks should not require a mutable ConnStateData. Adjust the
3491 // code that accidentally violates that principle to remove this const_cast!
3492 checklist.setConn(const_cast<ConnStateData*>(this));
3493
3494 // Set other checklist fields inside our fillConnectionLevelDetails() rather
3495 // than here because clientAclChecklistFill() code path calls that method
3496 // (via ACLFilledChecklist::setConn()) rather than calling us directly.
3497}
3498
3499void
3501{
3502 assert(checklist.conn() == this);
3504
3505 if (!checklist.request) { // preserve (better) addresses supplied by setRequest()
3506 checklist.src_addr = clientConnection->remote;
3507 checklist.my_addr = clientConnection->local; // TODO: or port->s?
3508 }
3509
3510#if USE_OPENSSL
3511 if (!checklist.sslErrors && sslServerBump)
3512 checklist.sslErrors = sslServerBump->sslErrors();
3513#endif
3514}
3515
3516bool
3521
3524{
3525 bodyPipe = new BodyPipe(this);
3526 if (size >= 0)
3528 else
3530 return bodyPipe;
3531}
3532
3533int64_t
3535{
3536 if (!bodyPipe)
3537 return 0; // request without a body or read/produced all body bytes
3538
3539 if (!bodyPipe->bodySizeKnown())
3540 return -1; // probably need to read more, but we cannot be sure
3541
3542 const int64_t needToProduce = bodyPipe->unproducedSize();
3543 const int64_t haveAvailable = static_cast<int64_t>(inBuf.length());
3544
3545 if (needToProduce <= haveAvailable)
3546 return 0; // we have read what we need (but are waiting for pipe space)
3547
3548 return needToProduce - haveAvailable;
3549}
3550
3551void
3553{
3554 debugs(33, 4, "receiving error (" << clientConnection << "): " << error <<
3555 "; old sending error: " <<
3556 (stoppedSending() ? stoppedSending_ : "none"));
3557
3558 if (const char *oldError = stoppedReceiving()) {
3559 debugs(33, 3, "already stopped receiving: " << oldError);
3560 return; // nothing has changed as far as this connection is concerned
3561 }
3562
3564
3565 if (const char *sendError = stoppedSending()) {
3566 debugs(33, 3, "closing because also stopped sending: " << sendError);
3568 }
3569}
3570
3571void
3573{
3574 if (bodyPipe != nullptr) {
3575 debugs(33, 4, "no consumer for virgin body " << bodyPipe->status());
3577 }
3578}
3579
3581void
3583{
3584 Must(bodyPipe != nullptr);
3585 debugs(33, 5, "start dechunking" << bodyPipe->status());
3588}
3589
3591void
3593{
3594 debugs(33, 5, "finish dechunking: " << withSuccess);
3595
3596 if (bodyPipe != nullptr) {
3597 debugs(33, 7, "dechunked tail: " << bodyPipe->status());
3598 BodyPipe::Pointer myPipe = bodyPipe;
3599 stopProducingFor(bodyPipe, withSuccess); // sets bodyPipe->bodySize()
3600 Must(!bodyPipe); // we rely on it being nil after we are done with body
3601 if (withSuccess) {
3602 Must(myPipe->bodySizeKnown());
3604 if (context != nullptr && context->http && context->http->request)
3605 context->http->request->setContentLength(myPipe->bodySize());
3606 }
3607 }
3608
3609 delete bodyParser;
3610 bodyParser = nullptr;
3611}
3612
3613// XXX: this is an HTTP/1-only operation
3614void
3616{
3617 if (const auto context = pipeline.front()) {
3618 if (context->http)
3619 context->http->al->reply = msg.reply;
3620 }
3621
3622 if (!isOpen()) {
3623 debugs(33, 3, "ignoring 1xx due to earlier closure");
3624 return;
3625 }
3626
3627 // HTTP/1 1xx status messages are only valid when there is a transaction to trigger them
3628 if (!pipeline.empty()) {
3629 HttpReply::Pointer rep(msg.reply);
3630 Must(rep);
3631 // remember the callback
3633
3636
3637 if (!writeControlMsgAndCall(rep.getRaw(), call)) {
3638 // but still inform the caller (so it may resume its operation)
3640 }
3641 return;
3642 }
3643
3644 debugs(33, 3, " closing due to missing context for 1xx");
3646}
3647
3648void
3650{
3652
3653 if (Http::StreamPointer deferredRequest = pipeline.front()) {
3654 debugs(33, 3, clientConnection << ": calling PushDeferredIfNeeded after control msg wrote");
3655 ClientSocketContextPushDeferredIfNeeded(deferredRequest, this);
3656 }
3657}
3658
3660void
3662{
3663 // FwdState might repin a failed connection sooner than this close
3664 // callback is called for the failed connection.
3665 assert(pinning.serverConnection == io.conn);
3666 pinning.closeHandler = nullptr; // Comm unregisters handlers before calling
3667 const bool sawZeroReply = pinning.zeroReply; // reset when unpinning
3668 pinning.serverConnection->noteClosure();
3669 unpinConnection(false);
3670
3671 if (sawZeroReply && clientConnection != nullptr) {
3672 debugs(33, 3, "Closing client connection on pinned zero reply.");
3674 }
3675
3676}
3677
3678void
3680{
3681 pinConnection(pinServer, *request);
3682}
3683
3684void
3686{
3687 Must(pic.connection);
3688 Must(pic.request);
3689 pinConnection(pic.connection, *pic.request);
3690
3691 // monitor pinned server connection for remote-end closures.
3693
3694 if (pipeline.empty())
3695 kick(); // in case parseRequests() was blocked by a busy pic.connection
3696}
3697
3699void
3701{
3702 if (Comm::IsConnOpen(pinning.serverConnection) &&
3703 pinning.serverConnection->fd == pinServer->fd) {
3704 debugs(33, 3, "already pinned" << pinServer);
3705 return;
3706 }
3707
3708 unpinConnection(true); // closes pinned connection, if any, and resets fields
3709
3710 pinning.serverConnection = pinServer;
3711
3712 debugs(33, 3, pinning.serverConnection);
3713
3714 Must(pinning.serverConnection != nullptr);
3715
3716 const char *pinnedHost = "[unknown]";
3717 pinning.host = xstrdup(request.url.host());
3718 pinning.port = request.url.port();
3719 pinnedHost = pinning.host;
3720 pinning.pinned = true;
3721 pinning.auth = request.flags.connectionAuth;
3722 char stmp[MAX_IPSTRLEN];
3723 char desc[FD_DESC_SZ];
3724 const auto peer = pinning.peer();
3725 snprintf(desc, FD_DESC_SZ, "%s pinned connection for %s (%d)",
3726 (pinning.auth || !peer) ? pinnedHost : peer->name,
3729 fd_note(pinning.serverConnection->fd, desc);
3730
3732 pinning.closeHandler = JobCallback(33, 5,
3734 // remember the pinned connection so that cb does not unpin a fresher one
3735 typedef CommCloseCbParams Params;
3736 Params &params = GetCommParams<Params>(pinning.closeHandler);
3737 params.conn = pinning.serverConnection;
3738 comm_add_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
3739}
3740
3743void
3745{
3746 if (pinning.readHandler != nullptr)
3747 return; // already monitoring
3748
3750 pinning.readHandler = JobCallback(33, 3,
3752 Comm::Read(pinning.serverConnection, pinning.readHandler);
3753}
3754
3755void
3757{
3758 if (pinning.readHandler != nullptr) {
3759 Comm::ReadCancel(pinning.serverConnection->fd, pinning.readHandler);
3760 pinning.readHandler = nullptr;
3761 }
3762}
3763
3764#if USE_OPENSSL
3765bool
3767{
3768 // A ready-for-reading connection means that the TLS server either closed
3769 // the connection, sent us some unexpected HTTP data, or started TLS
3770 // renegotiations. We should close the connection except for the last case.
3771
3772 Must(pinning.serverConnection != nullptr);
3773 auto ssl = fd_table[pinning.serverConnection->fd].ssl.get();
3774 if (!ssl)
3775 return false;
3776
3777 char buf[1];
3778 const int readResult = SSL_read(ssl, buf, sizeof(buf));
3779
3780 if (readResult > 0 || SSL_pending(ssl) > 0) {
3781 debugs(83, 2, pinning.serverConnection << " TLS application data read");
3782 return false;
3783 }
3784
3785 switch(const int error = SSL_get_error(ssl, readResult)) {
3786 case SSL_ERROR_WANT_WRITE:
3787 debugs(83, DBG_IMPORTANT, pinning.serverConnection << " TLS SSL_ERROR_WANT_WRITE request for idle pinned connection");
3788 [[fallthrough]]; // to restart monitoring, for now
3789
3790 case SSL_ERROR_NONE:
3791 case SSL_ERROR_WANT_READ:
3793 return true;
3794
3795 default:
3796 debugs(83, 2, pinning.serverConnection << " TLS error: " << error);
3797 return false;
3798 }
3799
3800 // not reached
3801 return true;
3802}
3803#endif
3804
3807void
3809{
3810 pinning.readHandler = nullptr; // Comm unregisters handlers before calling
3811
3812 if (io.flag == Comm::ERR_CLOSING)
3813 return; // close handler will clean up
3814
3815 Must(pinning.serverConnection == io.conn);
3816
3817#if USE_OPENSSL
3819 return;
3820#endif
3821
3822 const bool clientIsIdle = pipeline.empty();
3823
3824 debugs(33, 3, "idle pinned " << pinning.serverConnection << " read " <<
3825 io.size << (clientIsIdle ? " with idle client" : ""));
3826
3827 pinning.serverConnection->close();
3828
3829 // If we are still sending data to the client, do not close now. When we are done sending,
3830 // ConnStateData::kick() checks pinning.serverConnection and will close.
3831 // However, if we are idle, then we must close to inform the idle client and minimize races.
3832 if (clientIsIdle && clientConnection != nullptr)
3834}
3835
3838{
3839 debugs(33, 7, pinning.serverConnection);
3840 Must(request);
3841
3842 const auto pinningError = [&](const err_type type) {
3843 unpinConnection(true);
3844 HttpRequestPointer requestPointer = request;
3845 return ErrorState::NewForwarding(type, requestPointer, ale);
3846 };
3847
3848 if (!Comm::IsConnOpen(pinning.serverConnection))
3849 throw pinningError(ERR_ZERO_SIZE_OBJECT);
3850
3851 if (pinning.auth && pinning.host && strcasecmp(pinning.host, request->url.host()) != 0)
3852 throw pinningError(ERR_CANNOT_FORWARD); // or generalize ERR_CONFLICT_HOST
3853
3854 if (pinning.port != request->url.port())
3855 throw pinningError(ERR_CANNOT_FORWARD); // or generalize ERR_CONFLICT_HOST
3856
3857 if (pinning.serverConnection->toGoneCachePeer())
3858 throw pinningError(ERR_ZERO_SIZE_OBJECT);
3859
3860 if (pinning.peerAccessDenied)
3861 throw pinningError(ERR_CANNOT_FORWARD); // or generalize ERR_FORWARDING_DENIED
3862
3864 return pinning.serverConnection;
3865}
3866
3869{
3870 if (const auto connManager = request ? request->pinnedConnection() : nullptr)
3871 return connManager->borrowPinnedConnection(request, ale);
3872
3873 // ERR_CANNOT_FORWARD is somewhat misleading here; we can still forward, but
3874 // there is no point since the client connection is now gone
3875 HttpRequestPointer requestPointer = request;
3876 throw ErrorState::NewForwarding(ERR_CANNOT_FORWARD, requestPointer, ale);
3877}
3878
3879void
3881{
3882 debugs(33, 3, pinning.serverConnection);
3883
3884 if (Comm::IsConnOpen(pinning.serverConnection)) {
3885 if (pinning.closeHandler != nullptr) {
3886 comm_remove_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
3887 pinning.closeHandler = nullptr;
3888 }
3889
3891
3892 // close the server side socket if requested
3893 if (andClose)
3894 pinning.serverConnection->close();
3895 pinning.serverConnection = nullptr;
3896 }
3897
3898 safe_free(pinning.host);
3899
3900 pinning.zeroReply = false;
3901 pinning.peerAccessDenied = false;
3902
3903 /* NOTE: pinning.pinned should be kept. This combined with fd == -1 at the end of a request indicates that the host
3904 * connection has gone away */
3905}
3906
3907void
3909{
3910 auto error = rawError; // (cheap) copy so that we can detail
3911 // We detail even ERR_NONE: There should be no transactions left, and
3912 // detailed ERR_NONE will be unused. Otherwise, this detail helps in triage.
3913 if (error.details.empty()) {
3914 static const auto d = MakeNamedErrorDetail("WITH_CLIENT");
3915 error.details.push_back(d);
3916 }
3917
3918 debugs(33, 3, pipeline.count() << '/' << pipeline.nrequests << " after " << error);
3919
3920 if (pipeline.empty()) {
3921 bareError.update(error); // XXX: bareLogTagsErrors
3922 } else {
3923 // We terminate the current CONNECT/PUT/etc. context below, logging any
3924 // error details, but that context may leave unparsed bytes behind.
3925 // Consume them to stop checkLogging() from logging them again later.
3926 const auto intputToConsume =
3927#if USE_OPENSSL
3928 parsingTlsHandshake ? "TLS handshake" : // more specific than CONNECT
3929#endif
3930 bodyPipe ? "HTTP request body" :
3931 pipeline.back()->mayUseConnection() ? "HTTP CONNECT" :
3932 nullptr;
3933
3934 while (const auto context = pipeline.front()) {
3935 context->noteIoError(error, lte);
3936 context->finished(); // cleanup and self-deregister
3937 assert(context != pipeline.front());
3938 }
3939
3940 if (intputToConsume && !inBuf.isEmpty()) {
3941 debugs(83, 5, "forgetting client " << intputToConsume << " bytes: " << inBuf.length());
3942 inBuf.clear();
3943 }
3944 }
3945
3947}
3948
3950void
3952{
3953 // to simplify our logic, we assume that terminateAll() has been called
3955
3956 // do not log connections that closed after a transaction (it is normal)
3957 // TODO: access_log needs ACLs to match received-no-bytes connections
3959 return;
3960
3961 /* Create a temporary ClientHttpRequest object. Its destructor will log. */
3962 ClientHttpRequest http(this);
3963 http.req_sz = inBuf.length();
3964 // XXX: Or we died while waiting for the pinned connection to become idle.
3965 http.setErrorUri("error:transaction-end-before-headers");
3966 http.updateError(bareError);
3967}
3968
3969bool
3971{
3972 // PROXY protocol bytes are meant for us and, hence, cannot be tunneled
3974 return false;
3975
3976 // If our decision here is negative, configuration changes are irrelevant.
3977 // Otherwise, clientTunnelOnError() rechecks configuration before tunneling.
3979 return false;
3980
3981 // TODO: Figure out whether/how we can support FTP tunneling.
3982 if (port->transport.protocol == AnyP::PROTO_FTP)
3983 return false;
3984
3985#if USE_OPENSSL
3987 return true;
3988
3989 // the 1st HTTP request on a bumped connection
3991 return true;
3992#endif
3993
3994 // the 1st HTTP(S) request on a connection to an intercepting port
3995 if (!pipeline.nrequests && transparent())
3996 return true;
3997
3998 return false;
3999}
4000
4003{
4004 if (!theNotes)
4005 theNotes = new NotePairs;
4006 return theNotes;
4007}
4008
4009std::ostream &
4010operator <<(std::ostream &os, const ConnStateData::PinnedIdleContext &pic)
4011{
4012 return os << pic.connection << ", request=" << pic.request;
4013}
4014
4015std::ostream &
4016operator <<(std::ostream &os, const ConnStateData::ServerConnectionContext &scc)
4017{
4018 return os << scc.conn_ << ", srv_bytes=" << scc.preReadServerBytes.length();
4019}
4020
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:1544
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