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 /* deny CONNECT via intercepted ports */
1303 if (hp->method() == Http::METHOD_CONNECT && port != nullptr && port->flags.isIntercepted()) {
1304 debugs(33, 2, "WARNING: Rejecting CONNECT request received on " << transferProtocol << " intercepting port " << port->s.port());
1305 static const auto d = MakeNamedErrorDetail("INTERCEPTED_CONNECT");
1306 updateError(ERR_NONE, d); // buildHttpRequest() sets ERR_UNSUP_REQ based on parseStatusCode set below
1307 hp->parseStatusCode = Http::scMethodNotAllowed;
1308 return abortRequestParsing("error:method-not-allowed");
1309 }
1310
1311 /* HTTP/2 connection magic prefix starts with "PRI ".
1312 * Deny "PRI" method if used in HTTP/1.x or 0.9 versions.
1313 * If seen it signals a broken client or proxy has corrupted the traffic.
1314 */
1315 if (hp->method() == Http::METHOD_PRI && hp->messageProtocol() < Http::ProtocolVersion(2,0)) {
1316 debugs(33, DBG_IMPORTANT, "WARNING: PRI method received on " << transferProtocol << " port " << port->s.port());
1317 debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
1318 hp->parseStatusCode = Http::scMethodNotAllowed;
1319 return abortRequestParsing("error:method-not-allowed");
1320 }
1321
1322 if (hp->method() == Http::METHOD_NONE) {
1323 debugs(33, DBG_IMPORTANT, "WARNING: Unsupported method: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
1324 hp->parseStatusCode = Http::scMethodNotAllowed;
1325 return abortRequestParsing("error:unsupported-request-method");
1326 }
1327
1328 // Process headers after request line
1329 debugs(33, 3, "complete request received. " <<
1330 "prefix_sz = " << hp->messageHeaderSize() <<
1331 ", request-line-size=" << hp->firstLineSize() <<
1332 ", mime-header-size=" << hp->headerBlockSize() <<
1333 ", mime header block:\n" << hp->mimeHeader() << "\n----------");
1334
1335 /* Ok, all headers are received */
1336 ClientHttpRequest *http = new ClientHttpRequest(this);
1337
1338 http->req_sz = hp->messageHeaderSize();
1339 Http::Stream *result = new Http::Stream(clientConnection, http);
1340
1341 StoreIOBuffer tempBuffer;
1342 tempBuffer.data = result->reqbuf;
1343 tempBuffer.length = HTTP_REQBUF_SZ;
1344
1345 ClientStreamData newServer = new clientReplyContext(http);
1346 ClientStreamData newClient = result;
1349 clientSocketDetach, newClient, tempBuffer);
1350
1351 /* set url */
1352 debugs(33,5, "Prepare absolute URL from " <<
1353 (transparent()?"intercept":(port->flags.accelSurrogate ? "accel":"")));
1354 /* Rewrite the URL in transparent or accelerator mode */
1355 /* NP: there are several cases to traverse here:
1356 * - standard mode (forward proxy)
1357 * - transparent mode (TPROXY)
1358 * - transparent mode with failures
1359 * - intercept mode (NAT)
1360 * - intercept mode with failures
1361 * - accelerator mode (reverse proxy)
1362 * - internal relative-URL
1363 * - mixed combos of the above with internal URL
1364 * - remote interception with PROXY protocol
1365 * - remote reverse-proxy with PROXY protocol
1366 */
1367 if (switchedToHttps()) {
1368 http->uri = prepareTlsSwitchingURL(hp);
1369 } else if (transparent()) {
1370 /* intercept or transparent mode, properly working with no failures */
1371 http->uri = prepareTransparentURL(this, hp);
1372
1373 } else if (internalCheck(hp->requestUri())) { // NP: only matches relative-URI
1374 /* internal URL mode */
1375 // XXX: By prepending our name and port, we create an absolute URL
1376 // that may mismatch the (yet unparsed) Host header in the request.
1377 http->uri = xstrdup(internalLocalUri(nullptr, hp->requestUri()));
1378
1379 } else if (port->flags.accelSurrogate) {
1380 /* accelerator mode */
1381 http->uri = prepareAcceleratedURL(this, hp);
1382 http->flags.accel = true;
1383 }
1384
1385 if (!http->uri) {
1386 /* No special rewrites have been applied above, use the
1387 * requested url. may be rewritten later, so make extra room */
1388 int url_sz = hp->requestUri().length() + Config.appendDomainLen + 5;
1389 http->uri = (char *)xcalloc(url_sz, 1);
1390 SBufToCstring(http->uri, hp->requestUri());
1391 }
1392
1393 result->flags.parsed_ok = 1;
1394 return result;
1395}
1396
1397bool
1399{
1400 if (pipeline.empty() && inBuf.isEmpty()) {
1401 debugs(33, 4, "yes, without active requests and unparsed input");
1402 return true;
1403 }
1404
1406 debugs(33, 3, "yes, without half_closed_clients");
1407 return true;
1408 }
1409
1410 // Squid currently tries to parse (possibly again) a partially received
1411 // request after an EOF with half_closed_clients. To give that last parse in
1412 // afterClientRead() a chance, we ignore partially parsed requests here.
1413 debugs(33, 3, "no, honoring half_closed_clients");
1414 return false;
1415}
1416
1417void
1418ConnStateData::consumeInput(const size_t byteCount)
1419{
1420 assert(byteCount > 0 && byteCount <= inBuf.length());
1421 inBuf.consume(byteCount);
1422 debugs(33, 5, "inBuf has " << inBuf.length() << " unused bytes");
1423}
1424
1425void
1427{
1428 // Were we expecting to read more request body from half-closed connection?
1430 debugs(33, 3, "truncated body: closing half-closed " << clientConnection);
1432 return;
1433 }
1434
1435 if (flags.readMore)
1436 readSomeData();
1437}
1438
1439void
1441{
1442 // From HTTP p.o.v., we do not have to close after every error detected
1443 // at the client-side, but many such errors do require closure and the
1444 // client-side code is bad at handling errors so we play it safe.
1445 if (request)
1446 request->flags.proxyKeepalive = false;
1447 flags.readMore = false;
1448 debugs(33,4, "Will close after error: " << clientConnection);
1449}
1450
1451#if USE_OPENSSL
1453{
1454 ClientHttpRequest *http = context->http;
1455
1456 if (!sslServerBump)
1457 return false;
1458
1460 // Did we create an error entry while processing CONNECT?
1461 if (!sslServerBump->entry->isEmpty()) {
1462 quitAfterError(http->request);
1463
1464 // Get the saved error entry and send it to the client by replacing the
1465 // ClientHttpRequest store entry with it.
1467 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1468 assert(repContext);
1469 debugs(33, 5, "Responding with delated error for " << http->uri);
1470 repContext->setReplyToStoreEntry(sslServerBump->entry, "delayed SslBump error");
1471
1472 // Get error details from the fake certificate-peeking request.
1474 context->pullData();
1475 return true;
1476 }
1477
1478 // In bump-server-first mode, we have not necessarily seen the intended
1479 // server name at certificate-peeking time. Check for domain mismatch now,
1480 // when we can extract the intended name from the bumped HTTP request.
1481 if (const Security::CertPointer &srvCert = sslServerBump->serverCert) {
1482 HttpRequest *request = http->request;
1483 const auto host = request->url.parsedHost();
1484 if (host && Ssl::HasSubjectName(*srvCert, *host)) {
1485 debugs(33, 5, "certificate matches requested host: " << *host);
1486 return false;
1487 } else {
1488 debugs(33, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " <<
1489 "does not match request target " << RawPointer(host));
1490
1491 bool allowDomainMismatch = false;
1494 const auto sslErrors = std::make_unique<Security::CertErrors>(Security::CertError(SQUID_X509_V_ERR_DOMAIN_MISMATCH, srvCert));
1495 check.sslErrors = sslErrors.get();
1496 clientAclChecklistFill(check, http);
1497 allowDomainMismatch = check.fastCheck().allowed();
1498 }
1499
1500 if (!allowDomainMismatch) {
1501 quitAfterError(request);
1502
1504 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1505 assert (repContext);
1506
1507 request->hier = sslServerBump->request->hier;
1508
1509 // Create an error object and fill it
1510 const auto err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request, http->al);
1511 err->src_addr = clientConnection->remote;
1514 srvCert, nullptr);
1515 err->detailError(errDetail);
1517 repContext->setReplyToError(request->method, err);
1518 assert(context->http->out.offset == 0);
1519 context->pullData();
1520 return true;
1521 }
1522 }
1523 }
1524
1525 return false;
1526}
1527#endif // USE_OPENSSL
1528
1530bool
1532{
1534 debugs(33, 5, "disabled; send error: " << requestError);
1535 return false;
1536 }
1537
1538 if (!preservingClientData_) {
1539 debugs(33, 3, "may have forgotten client data; send error: " << requestError);
1540 return false;
1541 }
1542
1544 checklist.requestErrorType = requestError;
1545 fillChecklist(checklist);
1546 const auto &answer = checklist.fastCheck();
1547 if (answer.allowed() && answer.kind == 1) {
1548 debugs(33, 3, "Request will be tunneled to server");
1549 const auto context = pipeline.front();
1550 const auto http = context ? context->http : nullptr;
1551 const auto request = http ? http->request : nullptr;
1552 if (context)
1553 context->finished(); // Will remove from pipeline queue
1554 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, nullptr, nullptr, 0);
1555 return initiateTunneledRequest(request, "unknown-protocol", preservedClientData);
1556 }
1557 debugs(33, 3, "denied; send error: " << requestError);
1558 return false;
1559}
1560
1561void
1563{
1564 /*
1565 * DPW 2007-05-18
1566 * Moved the TCP_RESET feature from clientReplyContext::sendMoreData
1567 * to here because calling comm_reset_close() causes http to
1568 * be freed before accessing.
1569 */
1570 if (request != nullptr && request->flags.resetTcp && Comm::IsConnOpen(conn->clientConnection)) {
1571 debugs(33, 3, "Sending TCP RST on " << conn->clientConnection);
1572 conn->flags.readMore = false;
1574 }
1575}
1576
1577void
1579{
1580 ClientHttpRequest *http = context->http;
1581 bool mustReplyToOptions = false;
1582 bool expectBody = false;
1583
1584 // We already have the request parsed and checked, so we
1585 // only need to go through the final body/conn setup to doCallouts().
1586 assert(http->request);
1587 HttpRequest::Pointer request = http->request;
1588
1589 // temporary hack to avoid splitting this huge function with sensitive code
1590 const bool isFtp = !hp;
1591
1592 // Some blobs below are still HTTP-specific, but we would have to rewrite
1593 // this entire function to remove them from the FTP code path. Connection
1594 // setup and body_pipe preparation blobs are needed for FTP.
1595
1596 request->manager(conn, http->al);
1597
1598 request->flags.accelerated = http->flags.accel;
1599 request->flags.sslBumped=conn->switchedToHttps();
1600 // TODO: decouple http->flags.accel from request->flags.sslBumped
1601 request->flags.noDirect = (request->flags.accelerated && !request->flags.sslBumped) ?
1602 !conn->port->allow_direct : 0;
1603 request->sources |= isFtp ? Http::Message::srcFtp :
1604 ((request->flags.sslBumped || conn->port->transport.protocol == AnyP::PROTO_HTTPS) ? Http::Message::srcHttps : Http::Message::srcHttp);
1605#if USE_AUTH
1606 if (request->flags.sslBumped) {
1607 if (conn->getAuth() != nullptr)
1608 request->auth_user_request = conn->getAuth();
1609 }
1610#endif
1611
1612 http->checkForInternalAccess();
1613
1614 if (!isFtp) {
1615 // XXX: for non-HTTP messages instantiate a different Http::Message child type
1616 // for now Squid only supports HTTP requests
1617 const AnyP::ProtocolVersion &http_ver = hp->messageProtocol();
1618 assert(request->http_ver.protocol == http_ver.protocol);
1619 request->http_ver.major = http_ver.major;
1620 request->http_ver.minor = http_ver.minor;
1621 }
1622
1623 mustReplyToOptions = (request->method == Http::METHOD_OPTIONS) &&
1625 if (!urlCheckRequest(request.getRaw()) || mustReplyToOptions) {
1627 conn->quitAfterError(request.getRaw());
1628 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1629 assert (repContext);
1631 conn, request.getRaw(), nullptr, nullptr);
1632 assert(context->http->out.offset == 0);
1633 context->pullData();
1634 clientProcessRequestFinished(conn, request);
1635 return;
1636 }
1637
1638 const auto frameStatus = request->checkEntityFraming();
1639 if (frameStatus != Http::scNone) {
1641 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1642 assert (repContext);
1643 conn->quitAfterError(request.getRaw());
1644 repContext->setReplyToError(ERR_INVALID_REQ, frameStatus, nullptr, conn, request.getRaw(), nullptr, nullptr);
1645 assert(context->http->out.offset == 0);
1646 context->pullData();
1647 clientProcessRequestFinished(conn, request);
1648 return;
1649 }
1650
1652 // Let tunneling code be fully responsible for CONNECT requests
1653 if (http->request->method == Http::METHOD_CONNECT) {
1654 context->mayUseConnection(true);
1655 conn->flags.readMore = false;
1656 }
1657
1658#if USE_OPENSSL
1659 if (conn->switchedToHttps() && conn->serveDelayedError(context)) {
1660 clientProcessRequestFinished(conn, request);
1661 return;
1662 }
1663#endif
1664
1665 /* Do we expect a request-body? */
1666 const auto chunked = request->header.chunked();
1667 expectBody = chunked || request->content_length > 0;
1668 if (!context->mayUseConnection() && expectBody) {
1669 request->body_pipe = conn->expectRequestBody(
1670 chunked ? -1 : request->content_length);
1671
1672 /* Is it too large? */
1673 if (!chunked && // if chunked, we will check as we accumulate
1676 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
1677 assert (repContext);
1678 conn->quitAfterError(request.getRaw());
1679 repContext->setReplyToError(ERR_TOO_BIG,
1680 Http::scContentTooLarge, nullptr,
1681 conn, http->request, nullptr, nullptr);
1682 assert(context->http->out.offset == 0);
1683 context->pullData();
1684 clientProcessRequestFinished(conn, request);
1685 return;
1686 }
1687
1688 if (!isFtp) {
1689 // We may stop producing, comm_close, and/or call setReplyToError()
1690 // below, so quit on errors to avoid http->doCallouts()
1691 if (!conn->handleRequestBodyData()) {
1692 clientProcessRequestFinished(conn, request);
1693 return;
1694 }
1695
1696 if (!request->body_pipe->productionEnded()) {
1697 debugs(33, 5, "need more request body");
1698 context->mayUseConnection(true);
1699 assert(conn->flags.readMore);
1700 }
1701 }
1702 }
1703
1704 http->calloutContext = new ClientRequestContext(http);
1705
1706 http->doCallouts();
1707
1708 clientProcessRequestFinished(conn, request);
1709}
1710
1711void
1713{
1714 debugs(33, 3, context << " to " << pipeline.count() << '/' << pipeline.nrequests);
1715 if (bareError) {
1716 debugs(33, 5, "assigning " << bareError);
1717 assert(context);
1718 assert(context->http);
1719 context->http->updateError(bareError);
1720 bareError.clear();
1721 }
1722 pipeline.add(context);
1723}
1724
1725int
1727{
1728 // TODO: Support pipelined requests through pinned connections.
1729 if (pinning.pinned)
1730 return 0;
1732}
1733
1739bool
1741{
1742 const int existingRequestCount = pipeline.count();
1743
1744 // default to the configured pipeline size.
1745 // add 1 because the head of pipeline is counted in concurrent requests and not prefetch queue
1746#if USE_OPENSSL
1747 const int internalRequest = (transparent() && sslBumpMode == Ssl::bumpSplice) ? 1 : 0;
1748#else
1749 const int internalRequest = 0;
1750#endif
1751 const int concurrentRequestLimit = pipelinePrefetchMax() + 1 + internalRequest;
1752
1753 // when queue filled already we can't add more.
1754 if (existingRequestCount >= concurrentRequestLimit) {
1755 debugs(33, 3, clientConnection << " max concurrent requests reached (" << concurrentRequestLimit << ")");
1756 debugs(33, 5, clientConnection << " deferring new request until one is done");
1757 return true;
1758 }
1759
1760 return false;
1761}
1762
1768bool
1770{
1772 return proxyProtocolError("PROXY client not permitted by default ACL");
1773
1775 fillChecklist(ch);
1776 if (!ch.fastCheck().allowed())
1777 return proxyProtocolError("PROXY client not permitted by ACLs");
1778
1779 return true;
1780}
1781
1787bool
1789{
1790 if (msg) {
1791 // This is important to know, but maybe not so much that flooding the log is okay.
1792#if QUIET_PROXY_PROTOCOL
1793 // display the first of every 32 occurrences at level 1, the others at level 2.
1794 static uint8_t hide = 0;
1795 debugs(33, (hide++ % 32 == 0 ? DBG_IMPORTANT : 2), msg << " from " << clientConnection);
1796#else
1797 debugs(33, DBG_IMPORTANT, msg << " from " << clientConnection);
1798#endif
1799 mustStop(msg);
1800 }
1801 return false;
1802}
1803
1808bool
1810{
1811 try {
1812 const auto parsed = ProxyProtocol::Parse(inBuf);
1813 proxyProtocolHeader_ = parsed.header;
1815 inBuf.consume(parsed.size);
1817 if (proxyProtocolHeader_->hasForwardedAddresses()) {
1818 clientConnection->local = proxyProtocolHeader_->destinationAddress;
1821 clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY spoofing of this new IP.
1822 debugs(33, 5, "PROXY/" << proxyProtocolHeader_->version() << " upgrade: " << clientConnection);
1823 }
1825 debugs(33, 3, "PROXY protocol: waiting for more than " << inBuf.length() << " bytes");
1826 return false;
1827 } catch (const std::exception &e) {
1828 return proxyProtocolError(e.what());
1829 }
1830 return true;
1831}
1832
1833void
1842
1845void
1847{
1848 debugs(33, 5, clientConnection << ": attempting to parse");
1849
1850 // Loop while we have read bytes that are not needed for producing the body
1851 // On errors, bodyPipe may become nil, but readMore will be cleared
1852 while (!inBuf.isEmpty() && !bodyPipe && flags.readMore) {
1853
1854 // Prohibit concurrent requests when using a pinned to-server connection
1855 // because our Client classes do not support request pipelining.
1856 if (pinning.pinned && !pinning.readHandler) {
1857 debugs(33, 3, clientConnection << " waits for busy " << pinning.serverConnection);
1858 break;
1859 }
1860
1861 /* Limit the number of concurrent requests */
1863 break;
1864
1865 // try to parse the PROXY protocol header magic bytes
1868 break;
1869
1870 // we have been waiting for PROXY to provide client-IP
1871 // for some lookups, ie rDNS
1873
1874 // Done with PROXY protocol which has cleared preservingClientData_.
1875 // If the next protocol supports on_unsupported_protocol, then its
1876 // parseOneRequest() must reset preservingClientData_.
1878 }
1879
1880 if (Http::StreamPointer context = parseOneRequest()) {
1881 debugs(33, 5, clientConnection << ": done parsing a request");
1883 context->registerWithConn();
1884
1885#if USE_OPENSSL
1886 if (switchedToHttps())
1888#endif
1889
1890 processParsedRequest(context);
1891
1892 if (context->mayUseConnection()) {
1893 debugs(33, 3, "Not parsing new requests, as this request may need the connection");
1894 break;
1895 }
1896 } else {
1897 debugs(33, 5, clientConnection << ": not enough request data: " <<
1898 inBuf.length() << " < " << Config.maxRequestHeaderSize);
1900 break;
1901 }
1902 }
1903
1904 debugs(33, 7, "buffered leftovers: " << inBuf.length());
1905
1907 if (pipeline.empty()) {
1908 // we processed what we could parse, and no more data is coming
1909 debugs(33, 5, "closing half-closed without parsed requests: " << clientConnection);
1911 } else {
1912 // we parsed what we could, and no more data is coming
1913 debugs(33, 5, "monitoring half-closed while processing parsed requests: " << clientConnection);
1914 flags.readMore = false; // may already be false
1915 }
1916 }
1917}
1918
1919void
1921{
1922#if USE_OPENSSL
1923 if (parsingTlsHandshake) {
1925 return;
1926 }
1927#endif
1928
1929 /* Process next request */
1930 if (pipeline.empty())
1931 fd_note(clientConnection->fd, "Reading next request");
1932
1933 parseRequests();
1934
1935 if (!isOpen())
1936 return;
1937
1939}
1940
1947bool
1949{
1950 // if we are reading a body, stuff data into the body pipe
1951 if (bodyPipe != nullptr)
1952 return handleRequestBodyData();
1953 return true;
1954}
1955
1963bool
1965{
1966 assert(bodyPipe != nullptr);
1967
1968 if (bodyParser) { // chunked encoding
1969 if (const err_type error = handleChunkedRequestBody()) {
1971 return false;
1972 }
1973 } else { // identity encoding
1974 debugs(33,5, "handling plain request body for " << clientConnection);
1975 const auto putSize = bodyPipe->putMoreData(inBuf.rawContent(), inBuf.length());
1976 if (putSize > 0)
1977 consumeInput(putSize);
1978
1979 if (!bodyPipe->mayNeedMoreData()) {
1980 // BodyPipe will clear us automagically when we produced everything
1981 bodyPipe = nullptr;
1982 }
1983 }
1984
1985 if (!bodyPipe) {
1986 debugs(33,5, "produced entire request body for " << clientConnection);
1987
1988 if (const char *reason = stoppedSending()) {
1989 /* we've finished reading like good clients,
1990 * now do the close that initiateClose initiated.
1991 */
1992 debugs(33, 3, "closing for earlier sending error: " << reason);
1994 return false;
1995 }
1996 }
1997
1998 return true;
1999}
2000
2004{
2005 debugs(33, 7, "chunked from " << clientConnection << ": " << inBuf.length());
2006
2007 try { // the parser will throw on errors
2008
2009 if (inBuf.isEmpty()) // nothing to do
2010 return ERR_NONE;
2011
2014 const bool parsed = bodyParser->parse(inBuf);
2015 inBuf = bodyParser->remaining(); // sync buffers
2016 bpc.checkIn();
2017
2018 // dechunk then check: the size limit applies to _dechunked_ content
2020 return ERR_TOO_BIG;
2021
2022 if (parsed) {
2024 Must(!bodyPipe);
2025 return ERR_NONE; // nil bodyPipe implies body end for the caller
2026 }
2027
2028 // if chunk parser needs data, then the body pipe must need it too
2030
2031 // if parser needs more space and we can consume nothing, we will stall
2033 } catch (...) { // TODO: be more specific
2034 debugs(33, 3, "malformed chunks" << bodyPipe->status());
2035 return ERR_INVALID_REQ;
2036 }
2037
2038 debugs(33, 7, "need more chunked data" << bodyPipe->status());
2039 return ERR_NONE;
2040}
2041
2043void
2045{
2047
2048 // XXX: The code below works if we fail during initial request parsing,
2049 // but if we fail when the server connection is used already, the server may send
2050 // us its response too, causing various assertions. How to prevent that?
2051#if WE_KNOW_HOW_TO_SEND_ERRORS
2053 if (context != NULL && !context->http->out.offset) { // output nothing yet
2054 clientStreamNode *node = context->getClientReplyContext();
2055 clientReplyContext *repContext = dynamic_cast<clientReplyContext*>(node->data.getRaw());
2056 assert(repContext);
2057 const Http::StatusCode scode = (error == ERR_TOO_BIG) ?
2058 Http::scContentTooLarge : HTTP_BAD_REQUEST;
2059 repContext->setReplyToError(error, scode,
2060 repContext->http->uri,
2061 CachePeer,
2062 repContext->http->request,
2063 inBuf, nullptr);
2064 context->pullData();
2065 } else {
2066 // close or otherwise we may get stuck as nobody will notice the error?
2068 }
2069#else
2070 debugs(33, 3, "aborting chunked request without error " << error);
2072#endif
2073 flags.readMore = false;
2074}
2075
2076void
2078{
2079 // request reader may get stuck waiting for space if nobody consumes body
2080 if (bodyPipe != nullptr)
2082
2083 // kids extend
2084}
2085
2087void
2089{
2090 if (!Comm::IsConnOpen(io.conn))
2091 return;
2092
2095 if (tunnelOnError(error))
2096 return;
2097
2098 /*
2099 * Just close the connection to not confuse browsers
2100 * using persistent connections. Some browsers open
2101 * a connection and then do not use it until much
2102 * later (presumably because the request triggering
2103 * the open has already been completed on another
2104 * connection)
2105 */
2106 debugs(33, 3, "requestTimeout: FD " << io.fd << ": lifetime is expired.");
2107 io.conn->close();
2108}
2109
2110void
2112{
2113 debugs(33, DBG_IMPORTANT, "WARNING: Closing client connection due to lifetime timeout" <<
2114 Debug::Extra << "connection: " << io.conn);
2115
2116 LogTagsErrors lte;
2117 lte.timedout = true;
2119}
2120
2122 AsyncJob("ConnStateData"), // kids overwrite
2123 Server(xact)
2124#if USE_OPENSSL
2125 , tlsParser(Security::HandshakeParser::fromClient)
2126#endif
2127{
2128 // store the details required for creating more MasterXaction objects as new requests come in
2129 log_addr = xact->tcpClient->remote;
2131
2132 // register to receive notice of Squid signal events
2133 // which may affect long persisting client connections
2135}
2136
2137void
2139{
2142
2143 if (port->disable_pmtu_discovery != DISABLE_PMTU_OFF &&
2144 (transparent() || port->disable_pmtu_discovery == DISABLE_PMTU_ALWAYS)) {
2145#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
2146 int i = IP_PMTUDISC_DONT;
2147 if (xsetsockopt(clientConnection->fd, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0) {
2148 int xerrno = errno;
2149 debugs(33, 2, "WARNING: Path MTU discovery disabling failed on " << clientConnection << " : " << xstrerr(xerrno));
2150 }
2151#else
2152 static bool reported = false;
2153
2154 if (!reported) {
2155 debugs(33, DBG_IMPORTANT, "WARNING: Path MTU discovery disabling is not supported on your platform.");
2156 reported = true;
2157 }
2158#endif
2159 }
2160
2164
2165 needProxyProtocolHeader_ = port->flags.proxySurrogate;
2167 if (!proxyProtocolValidateClient()) // will close the connection on failure
2168 return;
2169 } else
2171
2172 // requires needProxyProtocolHeader_ which is initialized above
2174}
2175
2176void
2178{
2182
2184
2185#if USE_DELAY_POOLS
2186 fd_table[clientConnection->fd].clientInfo = nullptr;
2187
2188 if (!Config.onoff.client_db)
2189 return; // client delay pools require client_db
2190
2191 const auto &pools = ClientDelayPools::Instance()->pools;
2192 if (pools.size()) {
2193 ACLFilledChecklist ch(nullptr, nullptr);
2194 fillChecklist(ch);
2195 // TODO: we check early to limit error response bandwidth but we
2196 // should recheck when we can honor delay_pool_uses_indirect
2197 for (unsigned int pool = 0; pool < pools.size(); ++pool) {
2198
2199 /* pools require explicit 'allow' to assign a client into them */
2200 if (pools[pool]->access) {
2201 ch.changeAcl(pools[pool]->access);
2202 const auto &answer = ch.fastCheck();
2203 if (answer.allowed()) {
2204
2205 /* request client information from db after we did all checks
2206 this will save hash lookup if client failed checks */
2208 assert(cli);
2209
2210 /* put client info in FDE */
2211 fd_table[clientConnection->fd].clientInfo = cli;
2212
2213 /* setup write limiter for this request */
2214 const double burst = floor(0.5 +
2215 (pools[pool]->highwatermark * Config.ClientDelay.initial)/100.0);
2216 cli->setWriteLimiter(pools[pool]->rate, burst, pools[pool]->highwatermark);
2217 break;
2218 } else {
2219 debugs(83, 4, "Delay pool " << pool << " skipped because ACL " << answer);
2220 }
2221 }
2222 }
2223 }
2224#endif
2225
2226 // kids must extend to actually start doing something (e.g., reading)
2227}
2228
2231{
2232 const auto handshakeResult = Security::Accept(*clientConnection);
2233
2234#if USE_OPENSSL
2235 // log ASAP, even if the handshake has not completed (or failed)
2236 const auto fd = clientConnection->fd;
2237 assert(fd >= 0);
2238 keyLogger.checkpoint(*fd_table[fd].ssl, *this);
2239#else
2240 // TODO: Support fd_table[fd].ssl dereference in other builds.
2241#endif
2242
2243 return handshakeResult;
2244}
2245
2247void
2249{
2250 Assure(params.port);
2251
2252 // NP: it is possible the port was reconfigured when the call or accept() was queued.
2253
2254 if (params.flag != Comm::OK) {
2255 // Its possible the call was still queued when the client disconnected
2256 debugs(33, 2, params.port->listenConn << ": accept failure: " << xstrerr(params.xerrno));
2257 return;
2258 }
2259
2260 debugs(33, 4, params.conn << ": accepted");
2261 fd_note(params.conn->fd, "client http connect");
2262 const auto xact = MasterXaction::MakePortful(params.port);
2263 xact->tcpClient = params.conn;
2264
2265 // Socket is ready, setup the connection manager to start using it
2266 auto *srv = Http::NewServer(xact);
2267 // XXX: do not abandon the MasterXaction object
2268 AsyncJob::Start(srv); // usually async-calls readSomeData()
2269}
2270
2272static bool
2274{
2275 const auto conn = connState->clientConnection;
2276 if (Security::CreateServerSession(ctx, conn, connState->port->secure, "client https start")) {
2277 debugs(33, 5, "will negotiate TLS on " << conn);
2278 return true;
2279 }
2280
2281 debugs(33, DBG_IMPORTANT, "ERROR: could not create TLS server context for " << conn);
2282 conn->close();
2283 return false;
2284}
2285
2287static void
2288clientNegotiateSSL(int fd, void *data)
2289{
2290 ConnStateData *conn = (ConnStateData *)data;
2291
2292 const auto handshakeResult = conn->acceptTls();
2293 switch (handshakeResult.category) {
2295 break;
2296
2299 return;
2300
2303 return;
2304
2306 debugs(83, (handshakeResult.important ? Important(62) : 2), "ERROR: Cannot accept a TLS connection" <<
2307 Debug::Extra << "problem: " << WithExtras(handshakeResult));
2308 // TODO: No ConnStateData::tunnelOnError() on this forward-proxy code
2309 // path because we cannot know the intended connection target?
2310 conn->updateError(ERR_SECURE_ACCEPT_FAIL, handshakeResult.errorDetail);
2311 conn->clientConnection->close();
2312 return;
2313 }
2314
2315 Security::SessionPointer session(fd_table[fd].ssl);
2316
2317#if USE_OPENSSL
2318 if (Security::SessionIsResumed(session)) {
2319 debugs(83, 2, "Session " << SSL_get_session(session.get()) <<
2320 " reused on FD " << fd << " (" << fd_table[fd].ipaddr <<
2321 ":" << (int)fd_table[fd].remote_port << ")");
2322 } else {
2323 if (Debug::Enabled(83, 4)) {
2324 /* Write out the SSL session details.. actually the call below, but
2325 * OpenSSL headers do strange typecasts confusing GCC.. */
2326 /* PEM_write_SSL_SESSION(DebugStream(), SSL_get_session(ssl)); */
2327#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00908000L
2328 PEM_ASN1_write(reinterpret_cast<i2d_of_void *>(i2d_SSL_SESSION),
2329 PEM_STRING_SSL_SESSION, DebugStream(),
2330 reinterpret_cast<char *>(SSL_get_session(session.get())),
2331 nullptr, nullptr, 0, nullptr, nullptr);
2332
2333#elif (ALLOW_ALWAYS_SSL_SESSION_DETAIL == 1)
2334
2335 /* When using gcc 3.3.x and OpenSSL 0.9.7x sometimes a compile error can occur here.
2336 * This is caused by an unpredictable gcc behaviour on a cast of the first argument
2337 * of PEM_ASN1_write(). For this reason this code section is disabled. To enable it,
2338 * define ALLOW_ALWAYS_SSL_SESSION_DETAIL=1.
2339 * Because there are two possible usable cast, if you get an error here, try the other
2340 * commented line. */
2341
2342 PEM_ASN1_write((int(*)())i2d_SSL_SESSION, PEM_STRING_SSL_SESSION,
2343 DebugStream(),
2344 reinterpret_cast<char *>(SSL_get_session(session.get())),
2345 nullptr, nullptr, 0, nullptr, nullptr);
2346 /* PEM_ASN1_write((int(*)(...))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION,
2347 DebugStream(),
2348 reinterpret_cast<char *>(SSL_get_session(session.get())),
2349 nullptr, nullptr, 0, nullptr, nullptr);
2350 */
2351#else
2352 debugs(83, 4, "With " OPENSSL_VERSION_TEXT ", session details are available only defining ALLOW_ALWAYS_SSL_SESSION_DETAIL=1 in the source.");
2353
2354#endif
2355 /* Note: This does not automatically fflush the log file.. */
2356 }
2357
2358 debugs(83, 2, "New session " << SSL_get_session(session.get()) <<
2359 " on FD " << fd << " (" << fd_table[fd].ipaddr << ":" <<
2360 fd_table[fd].remote_port << ")");
2361 }
2362#else
2363 debugs(83, 2, "TLS session reuse not yet implemented.");
2364#endif
2365
2366 // Connection established. Retrieve TLS connection parameters for logging.
2368
2369#if USE_OPENSSL
2370 X509 *client_cert = SSL_get_peer_certificate(session.get());
2371
2372 if (client_cert) {
2373 debugs(83, 3, "FD " << fd << " client certificate: subject: " <<
2374 Security::SubjectName(*client_cert));
2375
2376 debugs(83, 3, "FD " << fd << " client certificate: issuer: " <<
2377 Security::IssuerName(*client_cert));
2378
2379 X509_free(client_cert);
2380 } else {
2381 debugs(83, 5, "FD " << fd << " has no client certificate.");
2382 }
2383#else
2384 debugs(83, 2, "Client certificate requesting not yet implemented.");
2385#endif
2386
2387 // If we are called, then bumped CONNECT has succeeded. Finalize it.
2388 if (auto xact = conn->pipeline.front()) {
2389 if (xact->http && xact->http->request && xact->http->request->method == Http::METHOD_CONNECT)
2390 xact->finished();
2391 // cannot proceed with encryption if requests wait for plain responses
2392 Must(conn->pipeline.empty());
2393 }
2394 /* careful: finished() above frees request, host, etc. */
2395
2396 conn->readSomeData();
2397}
2398
2403static void
2405{
2406 assert(connState);
2407 const Comm::ConnectionPointer &details = connState->clientConnection;
2408
2409 if (!ctx || !httpsCreate(connState, ctx))
2410 return;
2411
2413
2414 Comm::SetSelect(details->fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
2415}
2416
2417#if USE_OPENSSL
2421static void
2423{
2424 ConnStateData *connState = (ConnStateData *) data;
2425
2426 // if the connection is closed or closing, just return.
2427 if (!connState->isOpen())
2428 return;
2429
2430 if (answer.allowed()) {
2431 debugs(33, 2, "sslBump action " << Ssl::bumpMode(answer.kind) << "needed for " << connState->clientConnection);
2432 connState->sslBumpMode = static_cast<Ssl::BumpMode>(answer.kind);
2433 } else {
2434 debugs(33, 3, "sslBump not needed for " << connState->clientConnection);
2435 connState->sslBumpMode = Ssl::bumpSplice;
2436 }
2437
2438 if (connState->sslBumpMode == Ssl::bumpTerminate) {
2439 connState->clientConnection->close();
2440 return;
2441 }
2442
2443 if (!connState->fakeAConnectRequest("ssl-bump", connState->inBuf))
2444 connState->clientConnection->close();
2445}
2446#endif
2447
2449static void
2451{
2452 Assure(params.port);
2453
2454 // NP: it is possible the port was reconfigured when the call or accept() was queued.
2455
2456 if (params.flag != Comm::OK) {
2457 // Its possible the call was still queued when the client disconnected
2458 debugs(33, 2, "httpsAccept: " << params.port->listenConn << ": accept failure: " << xstrerr(params.xerrno));
2459 return;
2460 }
2461
2462 const auto xact = MasterXaction::MakePortful(params.port);
2463 xact->tcpClient = params.conn;
2464
2465 debugs(33, 4, params.conn << " accepted, starting SSL negotiation.");
2466 fd_note(params.conn->fd, "client https connect");
2467
2468 // Socket is ready, setup the connection manager to start using it
2469 auto *srv = Https::NewServer(xact);
2470 // XXX: do not abandon the MasterXaction object
2471 AsyncJob::Start(srv); // usually async-calls postHttpsAccept()
2472}
2473
2474void
2476{
2477 if (port->flags.tunnelSslBumping) {
2478#if USE_OPENSSL
2479 debugs(33, 5, "accept transparent connection: " << clientConnection);
2480
2481 if (!Config.accessList.ssl_bump) {
2483 return;
2484 }
2485
2486 const auto mx = MasterXaction::MakePortful(port);
2487 mx->tcpClient = clientConnection;
2488 // Create a fake HTTP request and ALE for the ssl_bump ACL check,
2489 // using tproxy/intercept provided destination IP and port.
2490 // XXX: Merge with subsequent fakeAConnectRequest(), buildFakeRequest().
2491 // XXX: Do this earlier (e.g., in Http[s]::One::Server constructor).
2492 HttpRequest *request = new HttpRequest(mx);
2493 static char ip[MAX_IPSTRLEN];
2495 request->url.host(clientConnection->local.toStr(ip, sizeof(ip)));
2496 request->url.port(clientConnection->local.port());
2497 request->myportname = port->name;
2498 const AccessLogEntry::Pointer connectAle = new AccessLogEntry;
2499 CodeContext::Reset(connectAle);
2500 // TODO: Use these request/ALE when waiting for new bumped transactions.
2501
2502 auto acl_checklist = ACLFilledChecklist::Make(Config.accessList.ssl_bump, request);
2503 fillChecklist(*acl_checklist);
2504 // Build a local AccessLogEntry to allow requiresAle() acls work
2505 acl_checklist->al = connectAle;
2506 acl_checklist->al->cache.start_time = current_time;
2507 acl_checklist->al->tcpClient = clientConnection;
2508 acl_checklist->al->cache.port = port;
2509 acl_checklist->al->cache.caddr = log_addr;
2510 acl_checklist->al->proxyProtocolHeader = proxyProtocolHeader_;
2511 acl_checklist->al->updateError(bareError);
2512 HTTPMSGUNLOCK(acl_checklist->al->request);
2513 acl_checklist->al->request = request;
2514 HTTPMSGLOCK(acl_checklist->al->request);
2516 ClientHttpRequest *http = context ? context->http : nullptr;
2517 const char *log_uri = http ? http->log_uri : nullptr;
2518 acl_checklist->syncAle(request, log_uri);
2520#else
2521 fatal("FATAL: SSL-Bump requires --with-openssl");
2522#endif
2523 return;
2524 } else {
2525 httpsEstablish(this, port->secure.staticContext);
2526 }
2527}
2528
2529#if USE_OPENSSL
2530void
2532{
2533 ConnStateData * state_data = (ConnStateData *)(data);
2534 state_data->sslCrtdHandleReply(reply);
2535}
2536
2537void
2539{
2540 if (!isOpen()) {
2541 debugs(33, 3, "Connection gone while waiting for ssl_crtd helper reply; helper reply:" << reply);
2542 return;
2543 }
2544
2545 if (reply.result == Helper::BrokenHelper) {
2546 debugs(33, 5, "Certificate for " << tlsConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply);
2547 } else if (!reply.other().hasContent()) {
2548 debugs(1, DBG_IMPORTANT, "\"ssl_crtd\" helper returned <NULL> reply.");
2549 } else {
2551 if (reply_message.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK) {
2552 debugs(33, 5, "Reply from ssl_crtd for " << tlsConnectHostOrIp << " is incorrect");
2553 } else {
2554 if (reply.result != Helper::Okay) {
2555 debugs(33, 5, "Certificate for " << tlsConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply_message.getBody());
2556 } else {
2557 debugs(33, 5, "Certificate for " << tlsConnectHostOrIp << " was successfully received from ssl_crtd");
2560 auto ssl = fd_table[clientConnection->fd].ssl.get();
2561 bool ret = Ssl::configureSSLUsingPkeyAndCertFromMemory(ssl, reply_message.getBody().c_str(), *port);
2562 if (!ret)
2563 debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
2564
2567 } else {
2569 if (ctx && !sslBumpCertKey.isEmpty())
2571 getSslContextDone(ctx);
2572 }
2573 return;
2574 }
2575 }
2576 }
2578 getSslContextDone(nil);
2579}
2580
2582{
2584
2585 const bool connectedOk = sslServerBump && sslServerBump->connectedOk();
2586 if (connectedOk) {
2587 if (X509 *mimicCert = sslServerBump->serverCert.get())
2588 certProperties.mimicCert.resetAndLock(mimicCert);
2589
2590 ACLFilledChecklist checklist(nullptr, sslServerBump->request.getRaw());
2591 fillChecklist(checklist);
2592
2593 for (sslproxy_cert_adapt *ca = Config.ssl_client.cert_adapt; ca != nullptr; ca = ca->next) {
2594 // If the algorithm already set, then ignore it.
2595 if ((ca->alg == Ssl::algSetCommonName && certProperties.setCommonName) ||
2596 (ca->alg == Ssl::algSetValidAfter && certProperties.setValidAfter) ||
2597 (ca->alg == Ssl::algSetValidBefore && certProperties.setValidBefore) )
2598 continue;
2599
2600 if (ca->aclList && checklist.fastCheck(ca->aclList).allowed()) {
2601 const char *alg = Ssl::CertAdaptAlgorithmStr[ca->alg];
2602 const char *param = ca->param;
2603
2604 // For parameterless CN adaptation, use hostname from the
2605 // CONNECT request.
2606 if (ca->alg == Ssl::algSetCommonName) {
2607 if (!param)
2608 param = tlsConnectHostOrIp.c_str();
2609 certProperties.commonName = param;
2610 certProperties.setCommonName = true;
2611 } else if (ca->alg == Ssl::algSetValidAfter)
2612 certProperties.setValidAfter = true;
2613 else if (ca->alg == Ssl::algSetValidBefore)
2614 certProperties.setValidBefore = true;
2615
2616 debugs(33, 5, "Matches certificate adaptation algorithm: " <<
2617 alg << " param: " << (param ? param : "-"));
2618 }
2619 }
2620
2621 certProperties.signAlgorithm = Ssl::algSignEnd;
2622 for (sslproxy_cert_sign *sg = Config.ssl_client.cert_sign; sg != nullptr; sg = sg->next) {
2623 if (sg->aclList && checklist.fastCheck(sg->aclList).allowed()) {
2624 certProperties.signAlgorithm = (Ssl::CertSignAlgorithm)sg->alg;
2625 break;
2626 }
2627 }
2628 } else {// did not try to connect (e.g. client-first) or failed to connect
2629 // In case of an error while connecting to the secure server, use a
2630 // trusted certificate, with no mimicked fields and no adaptation
2631 // algorithms. There is nothing we can mimic, so we want to minimize the
2632 // number of warnings the user will have to see to get to the error page.
2633 // We will close the connection, so that the trust is not extended to
2634 // non-Squid content.
2635 certProperties.signAlgorithm = Ssl::algSignTrusted;
2636 }
2637
2638 assert(certProperties.signAlgorithm != Ssl::algSignEnd);
2639
2640 if (certProperties.signAlgorithm == Ssl::algSignUntrusted) {
2641 assert(port->secure.untrustedSigningCa.cert);
2642 certProperties.signWithX509.resetAndLock(port->secure.untrustedSigningCa.cert.get());
2643 certProperties.signWithPkey.resetAndLock(port->secure.untrustedSigningCa.pkey.get());
2644 } else {
2645 assert(port->secure.signingCa.cert.get());
2646 certProperties.signWithX509.resetAndLock(port->secure.signingCa.cert.get());
2647
2648 if (port->secure.signingCa.pkey)
2649 certProperties.signWithPkey.resetAndLock(port->secure.signingCa.pkey.get());
2650 }
2651 signAlgorithm = certProperties.signAlgorithm;
2652
2653 certProperties.signHash = Ssl::DefaultSignHash;
2654}
2655
2658{
2659 debugs(33, 5, "Finding SSL certificate for " << cacheKey << " in cache");
2660 const auto ssl_ctx_cache = Ssl::TheGlobalContextStorage().getLocalStorage(port->s);
2661 if (const auto ctx = ssl_ctx_cache ? ssl_ctx_cache->get(cacheKey) : nullptr) {
2662 if (Ssl::verifySslCertificate(*ctx, certProperties)) {
2663 debugs(33, 5, "Cached SSL certificate for " << certProperties.commonName << " is valid");
2664 return *ctx;
2665 } else {
2666 debugs(33, 5, "Cached SSL certificate for " << certProperties.commonName << " is out of date. Delete this certificate from cache");
2667 if (ssl_ctx_cache)
2668 ssl_ctx_cache->del(cacheKey);
2669 }
2670 }
2671 return Security::ContextPointer(nullptr);
2672}
2673
2674void
2676{
2677 const auto ssl_ctx_cache = Ssl::TheGlobalContextStorage().getLocalStorage(port->s);
2678 if (!ssl_ctx_cache || !ssl_ctx_cache->add(cacheKey, ctx)) {
2679 // If it is not in storage delete after using. Else storage deleted it.
2680 fd_table[clientConnection->fd].dynamicTlsContext = ctx;
2681 }
2682}
2683
2684void
2686{
2687 if (port->secure.generateHostCertificates) {
2688 Ssl::CertificateProperties certProperties;
2689 buildSslCertGenerationParams(certProperties);
2690
2691 // Disable caching for bumpPeekAndSplice mode
2696
2698 if (ctx) {
2699 getSslContextDone(ctx);
2700 return;
2701 }
2702 }
2703
2704#if USE_SSL_CRTD
2705 try {
2706 debugs(33, 5, "Generating SSL certificate for " << certProperties.commonName << " using ssl_crtd.");
2709 request_message.composeRequest(certProperties);
2710 debugs(33, 5, "SSL crtd request: " << request_message.compose().c_str());
2711 Ssl::Helper::Submit(request_message, sslCrtdHandleReplyWrapper, this);
2712 return;
2713 } catch (const std::exception &e) {
2714 debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtd " <<
2715 "request for " << certProperties.commonName <<
2716 " certificate: " << e.what() << "; will now block to " <<
2717 "generate that certificate.");
2718 // fall through to do blocking in-process generation.
2719 }
2720#endif // USE_SSL_CRTD
2721
2722 debugs(33, 5, "Generating SSL certificate for " << certProperties.commonName);
2725 auto ssl = fd_table[clientConnection->fd].ssl.get();
2726 if (!Ssl::configureSSL(ssl, certProperties, *port))
2727 debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
2728
2731 } else {
2733 if (dynCtx && !sslBumpCertKey.isEmpty())
2735 getSslContextDone(dynCtx);
2736 }
2737 return;
2738 }
2739
2741 getSslContextDone(nil);
2742}
2743
2744void
2746{
2747 if (port->secure.generateHostCertificates && !ctx) {
2748 debugs(33, 2, "Failed to generate TLS context for " << tlsConnectHostOrIp);
2749 }
2750
2751 // If generated ssl context = nullptr, try to use static ssl context.
2752 if (!ctx) {
2753 if (!port->secure.staticContext) {
2754 debugs(83, DBG_IMPORTANT, "Closing " << clientConnection->remote << " as lacking TLS context");
2756 return;
2757 } else {
2758 debugs(33, 5, "Using static TLS context.");
2759 ctx = port->secure.staticContext;
2760 }
2761 }
2762
2763 if (!httpsCreate(this, ctx))
2764 return;
2765
2766 // bumped intercepted conns should already have Config.Timeout.request set
2767 // but forwarded connections may only have Config.Timeout.lifetime. [Re]set
2768 // to make sure the connection does not get stuck on non-SSL clients.
2770
2771 switchedToHttps_ = true;
2772
2773 auto ssl = fd_table[clientConnection->fd].ssl.get();
2774 BIO *b = SSL_get_rbio(ssl);
2775 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(BIO_get_data(b));
2776 bio->setReadBufData(inBuf);
2777 inBuf.clear();
2779}
2780
2781void
2783{
2785 Must(http->request);
2786 auto &request = http->request;
2787
2788 // Depending on receivedFirstByte_, we are at the start of either an
2789 // established CONNECT tunnel with the client or an intercepted TCP (and
2790 // presumably TLS) connection from the client. Expect TLS Client Hello.
2791 const auto insideConnectTunnel = receivedFirstByte_;
2792 debugs(33, 5, (insideConnectTunnel ? "post-CONNECT " : "raw TLS ") << clientConnection);
2793
2794 tlsConnectHostOrIp = request->url.hostOrIp();
2795 tlsConnectPort = request->url.port();
2796 resetSslCommonName(request->url.host());
2797
2798 // We are going to read new request
2799 flags.readMore = true;
2800
2801 // keep version major.minor details the same.
2802 // but we are now performing the HTTPS handshake traffic
2804
2805 // If sslServerBump is set, then we have decided to deny CONNECT
2806 // and now want to switch to SSL to send the error to the client
2807 // without even peeking at the origin server certificate.
2808 if (bumpServerMode == Ssl::bumpServerFirst && !sslServerBump) {
2809 request->flags.sslPeek = true;
2810 sslServerBump = new Ssl::ServerBump(http);
2811 } else if (bumpServerMode == Ssl::bumpPeek || bumpServerMode == Ssl::bumpStare) {
2812 request->flags.sslPeek = true;
2813 sslServerBump = new Ssl::ServerBump(http, nullptr, bumpServerMode);
2814 }
2815
2816 // commSetConnTimeout() was called for this request before we switched.
2817 // Fix timeout to request_start_timeout
2819 // Also reset receivedFirstByte_ flag to allow this timeout work in the case we have
2820 // a bumped "connect" request on non transparent port.
2821 receivedFirstByte_ = false;
2822 // Get more data to peek at Tls
2823 parsingTlsHandshake = true;
2824
2825 // If the protocol has changed, then reset preservingClientData_.
2826 // Otherwise, its value initially set in start() is still valid/fresh.
2827 // shouldPreserveClientData() uses parsingTlsHandshake which is reset above.
2828 if (insideConnectTunnel)
2830
2831 readSomeData();
2832}
2833
2834void
2836{
2838
2839 assert(!inBuf.isEmpty());
2841 fd_note(clientConnection->fd, "Parsing TLS handshake");
2842
2843 // stops being nil if we fail to parse the handshake
2844 ErrorDetail::Pointer parseErrorDetails;
2845
2846 try {
2847 if (!tlsParser.parseHello(inBuf)) {
2848 // need more data to finish parsing
2849 readSomeData();
2850 return;
2851 }
2852 }
2853 catch (const TextException &ex) {
2854 debugs(83, 2, "exception: " << ex);
2855 parseErrorDetails = new ExceptionErrorDetail(ex.id());
2856 }
2857 catch (...) {
2858 debugs(83, 2, "exception: " << CurrentException);
2859 static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_PARSE");
2860 parseErrorDetails = d;
2861 }
2862
2863 parsingTlsHandshake = false;
2864
2865 // client data may be needed for splicing and for
2866 // tunneling unsupportedProtocol after an error
2868
2869 // Even if the parser failed, each TLS detail should either be set
2870 // correctly or still be "unknown"; copying unknown detail is a no-op.
2873 if (details && !details->serverName.isEmpty()) {
2874 resetSslCommonName(details->serverName.c_str());
2875 tlsClientSni_ = details->serverName;
2876 }
2877
2878 // We should disable read/write handlers
2880
2881 if (parseErrorDetails) {
2883 Must(context && context->http);
2884 HttpRequest::Pointer request = context->http->request;
2885 debugs(83, 5, "Got something other than TLS Client Hello. Cannot SslBump.");
2886 updateError(ERR_PROTOCOL_UNKNOWN, parseErrorDetails);
2889 return;
2890 }
2891
2892 if (!sslServerBump || sslServerBump->act.step1 == Ssl::bumpClientFirst) { // Either means client-first.
2894 return;
2896 debugs(83, 5, "server-first skips step2; start forwarding the request");
2899 ClientHttpRequest *http = context ? context->http : nullptr;
2900 // will call httpsPeeked() with certificate and connection, eventually
2902 } else {
2905 }
2906}
2907
2908static void
2910{
2911 ConnStateData *connState = (ConnStateData *) data;
2912
2913 // if the connection is closed or closing, just return.
2914 if (!connState->isOpen())
2915 return;
2916
2917 debugs(33, 5, "Answer: " << answer << " kind:" << answer.kind);
2918 assert(connState->serverBump());
2919 Ssl::BumpMode bumpAction;
2920 if (answer.allowed()) {
2921 bumpAction = (Ssl::BumpMode)answer.kind;
2922 } else
2923 bumpAction = Ssl::bumpSplice;
2924
2925 connState->serverBump()->act.step2 = bumpAction;
2926 connState->sslBumpMode = bumpAction;
2927 Http::StreamPointer context = connState->pipeline.front();
2928 if (ClientHttpRequest *http = (context ? context->http : nullptr))
2929 http->al->ssl.bumpMode = bumpAction;
2930
2931 if (bumpAction == Ssl::bumpTerminate) {
2932 connState->clientConnection->close();
2933 } else if (bumpAction != Ssl::bumpSplice) {
2934 connState->startPeekAndSplice();
2935 } else if (!connState->splice())
2936 connState->clientConnection->close();
2937}
2938
2939bool
2941{
2942 // normally we can splice here, because we just got client hello message
2943
2944 // fde::ssl/tls_read_method() probably reads from our own inBuf. If so, then
2945 // we should not lose any raw bytes when switching to raw I/O here.
2946 if (fd_table[clientConnection->fd].ssl.get())
2947 fd_table[clientConnection->fd].useDefaultIo();
2948
2949 // XXX: assuming that there was an HTTP/1.1 CONNECT to begin with...
2950 // reset the current protocol to HTTP/1.1 (was "HTTPS" for the bumping process)
2952 assert(!pipeline.empty());
2954 Must(context);
2955 Must(context->http);
2956 ClientHttpRequest *http = context->http;
2957 HttpRequest::Pointer request = http->request;
2958 context->finished();
2959 if (transparent()) {
2960 // For transparent connections, make a new fake CONNECT request, now
2961 // with SNI as target. doCallout() checks, adaptations may need that.
2962 return fakeAConnectRequest("splice", preservedClientData);
2963 } else {
2964 // For non transparent connections make a new tunneled CONNECT, which
2965 // also sets the HttpRequest::flags::forceTunnel flag to avoid
2966 // respond with "Connection Established" to the client.
2967 // This fake CONNECT request required to allow use of SNI in
2968 // doCallout() checks and adaptations.
2969 return initiateTunneledRequest(request, "splice", preservedClientData);
2970 }
2971}
2972
2973void
2975{
2976 // This is the Step2 of the SSL bumping
2979 ClientHttpRequest *http = context ? context->http : nullptr;
2980
2983 // Run a accessList check to check if want to splice or continue bumping
2984
2986 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpNone));
2987 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpClientFirst));
2988 acl_checklist->banAction(Acl::Answer(ACCESS_ALLOWED, Ssl::bumpServerFirst));
2989 fillChecklist(*acl_checklist);
2991 return;
2992 }
2993
2994 // will call httpsPeeked() with certificate and connection, eventually
2995 Security::ContextPointer unConfiguredCTX(Ssl::createSSLContext(port->secure.signingCa.cert, port->secure.signingCa.pkey, port->secure));
2996 fd_table[clientConnection->fd].dynamicTlsContext = unConfiguredCTX;
2997
2998 if (!httpsCreate(this, unConfiguredCTX))
2999 return;
3000
3001 switchedToHttps_ = true;
3002
3003 auto ssl = fd_table[clientConnection->fd].ssl.get();
3004 BIO *b = SSL_get_rbio(ssl);
3005 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(BIO_get_data(b));
3006 bio->setReadBufData(inBuf);
3007 bio->hold(true);
3008
3009 // We have successfully parsed client Hello, but our TLS handshake parser is
3010 // forgiving. Now we use a TLS library to parse the same bytes, so that we
3011 // can honor on_unsupported_protocol if needed. If there are no errors, we
3012 // expect Security::Accept() to ask us to write (our) TLS server Hello. We
3013 // also allow an ioWantRead result in case some fancy TLS extension that
3014 // Squid does not yet understand requires reading post-Hello client bytes.
3015 const auto handshakeResult = acceptTls();
3016 if (!handshakeResult.wantsIo())
3017 return handleSslBumpHandshakeError(handshakeResult);
3018
3019 // We need to reset inBuf here, to be used by incoming requests in the case
3020 // of SSL bump
3021 inBuf.clear();
3022
3023 debugs(83, 5, "Peek and splice at step2 done. Start forwarding the request!!! ");
3026}
3027
3029void
3031{
3032 auto errCategory = ERR_NONE;
3033
3034 switch (handshakeResult.category) {
3036 static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_UNEXPECTED_SUCCESS");
3037 updateError(errCategory = ERR_GATEWAY_FAILURE, d);
3038 break;
3039 }
3040
3042 static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_UNEXPECTED_READ");
3043 updateError(errCategory = ERR_GATEWAY_FAILURE, d);
3044 break;
3045 }
3046
3048 static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_UNEXPECTED_WRITE");
3049 updateError(errCategory = ERR_GATEWAY_FAILURE, d);
3050 break;
3051 }
3052
3054 debugs(83, (handshakeResult.important ? DBG_IMPORTANT : 2), "ERROR: Cannot SslBump-accept a TLS connection" <<
3055 Debug::Extra << "problem: " << WithExtras(handshakeResult));
3056 updateError(errCategory = ERR_SECURE_ACCEPT_FAIL, handshakeResult.errorDetail);
3057 break;
3058
3059 }
3060
3061 if (!tunnelOnError(errCategory))
3063}
3064
3065void
3067{
3068 auto ssl = fd_table[clientConnection->fd].ssl.get();
3069 BIO *b = SSL_get_rbio(ssl);
3070 assert(b);
3071 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(BIO_get_data(b));
3072
3073 debugs(33, 5, "PeekAndSplice mode, proceed with client negotiation. Current state:" << SSL_state_string_long(ssl));
3074 bio->hold(false);
3075
3077 switchedToHttps_ = true;
3078}
3079
3080void
3082{
3083 Must(sslServerBump != nullptr);
3085 Must(pipeline.empty() || pipeline.front()->http == nullptr || pipeline.front()->http->request == pic.request.getRaw());
3086
3087 if (Comm::IsConnOpen(pic.connection)) {
3089 debugs(33, 5, "bumped HTTPS server: " << tlsConnectHostOrIp);
3090 } else
3091 debugs(33, 5, "Error while bumping: " << tlsConnectHostOrIp);
3092
3094}
3095
3096#endif /* USE_OPENSSL */
3097
3098bool
3099ConnStateData::initiateTunneledRequest(HttpRequest::Pointer const &cause, const char *reason, const SBuf &payload)
3100{
3101 // fake a CONNECT request to force connState to tunnel
3102 SBuf connectHost;
3103 AnyP::Port connectPort;
3104
3105 if (pinning.serverConnection != nullptr) {
3106 static char ip[MAX_IPSTRLEN];
3107 connectHost = pinning.serverConnection->remote.toStr(ip, sizeof(ip));
3108 if (const auto remotePort = pinning.serverConnection->remote.port())
3109 connectPort = remotePort;
3110 } else if (cause) {
3111 connectHost = cause->url.hostOrIp();
3112 connectPort = cause->url.port();
3113#if USE_OPENSSL
3114 } else if (!tlsConnectHostOrIp.isEmpty()) {
3115 connectHost = tlsConnectHostOrIp;
3116 connectPort = tlsConnectPort;
3117#endif
3118 } else if (transparent()) {
3119 static char ip[MAX_IPSTRLEN];
3120 connectHost = clientConnection->local.toStr(ip, sizeof(ip));
3121 connectPort = clientConnection->local.port();
3122 }
3123
3124 if (!connectPort) {
3125 // Typical cases are malformed HTTP requests on http_port and malformed
3126 // TLS handshakes on non-bumping https_port. TODO: Discover these
3127 // problems earlier so that they can be classified/detailed better.
3128 debugs(33, 2, "Not able to compute URL, abort request tunneling for " << reason);
3129 // TODO: throw when NonBlockingCheck() callbacks gain job protections
3130 static const auto d = MakeNamedErrorDetail("TUNNEL_TARGET");
3132 return false;
3133 }
3134
3135 debugs(33, 2, "Request tunneling for " << reason);
3136 const auto http = buildFakeRequest(connectHost, *connectPort, payload);
3137 HttpRequest::Pointer request = http->request;
3138 request->flags.forceTunnel = true;
3139 http->calloutContext = new ClientRequestContext(http);
3140 http->doCallouts();
3141 clientProcessRequestFinished(this, request);
3142 return true;
3143}
3144
3145bool
3146ConnStateData::fakeAConnectRequest(const char *reason, const SBuf &payload)
3147{
3148 debugs(33, 2, "fake a CONNECT request to force connState to tunnel for " << reason);
3149
3150 SBuf connectHost;
3152 const unsigned short connectPort = clientConnection->local.port();
3153
3154#if USE_OPENSSL
3155 if (!tlsClientSni_.isEmpty())
3156 connectHost.assign(tlsClientSni_);
3157 else
3158#endif
3159 {
3160 static char ip[MAX_IPSTRLEN];
3161 clientConnection->local.toHostStr(ip, sizeof(ip));
3162 connectHost.assign(ip);
3163 }
3164
3165 ClientHttpRequest *http = buildFakeRequest(connectHost, connectPort, payload);
3166
3167 http->calloutContext = new ClientRequestContext(http);
3168 HttpRequest::Pointer request = http->request;
3169 http->doCallouts();
3170 clientProcessRequestFinished(this, request);
3171 return true;
3172}
3173
3175ConnStateData::buildFakeRequest(SBuf &useHost, const AnyP::KnownPort usePort, const SBuf &payload)
3176{
3177 ClientHttpRequest *http = new ClientHttpRequest(this);
3178 Http::Stream *stream = new Http::Stream(clientConnection, http);
3179
3180 StoreIOBuffer tempBuffer;
3181 tempBuffer.data = stream->reqbuf;
3182 tempBuffer.length = HTTP_REQBUF_SZ;
3183
3184 ClientStreamData newServer = new clientReplyContext(http);
3185 ClientStreamData newClient = stream;
3188 clientSocketDetach, newClient, tempBuffer);
3189
3190 stream->flags.parsed_ok = 1; // Do we need it?
3191 stream->mayUseConnection(true);
3193 stream->registerWithConn();
3194
3195 const auto mx = MasterXaction::MakePortful(port);
3196 mx->tcpClient = clientConnection;
3197 // Setup Http::Request object. Maybe should be replaced by a call to (modified)
3198 // clientProcessRequest
3199 HttpRequest::Pointer request = new HttpRequest(mx);
3200 request->url.setScheme(AnyP::PROTO_AUTHORITY_FORM, nullptr);
3201 request->method = Http::METHOD_CONNECT;
3202 request->url.host(useHost.c_str());
3203 request->url.port(usePort);
3204
3205 http->uri = SBufToCstring(request->effectiveRequestUri());
3206 http->initRequest(request.getRaw());
3207
3208 request->manager(this, http->al);
3209
3210 request->header.putStr(Http::HOST, useHost.c_str());
3211
3212 request->sources |= ((switchedToHttps() || port->transport.protocol == AnyP::PROTO_HTTPS) ? Http::Message::srcHttps : Http::Message::srcHttp);
3213#if USE_AUTH
3214 if (getAuth())
3215 request->auth_user_request = getAuth();
3216#endif
3217
3218 inBuf = payload;
3219 flags.readMore = false;
3220
3221 return http;
3222}
3223
3225static bool
3227{
3228 if (!Comm::IsConnOpen(c)) {
3229 Must(NHttpSockets > 0); // we tried to open some
3230 --NHttpSockets; // there will be fewer sockets than planned
3231 Must(HttpSockets[NHttpSockets] < 0); // no extra fds received
3232
3233 if (!NHttpSockets) // we could not open any listen sockets at all
3234 fatalf("Unable to open %s",FdNote(portType));
3235
3236 return false;
3237 }
3238 return true;
3239}
3240
3242static bool
3244{
3245 bool found = false;
3246 for (int i = 0; i < NHttpSockets && !found; ++i) {
3247 if ((found = HttpSockets[i] < 0))
3248 HttpSockets[i] = conn->fd;
3249 }
3250 return found;
3251}
3252
3253static void
3255{
3256 const auto savedContext = CodeContext::Current();
3257 for (AnyP::PortCfgPointer s = HttpPortList; s != nullptr; s = s->next) {
3259 const SBuf &scheme = AnyP::UriScheme(s->transport.protocol).image();
3260
3262 debugs(1, DBG_IMPORTANT, "WARNING: You have too many '" << scheme << "_port' lines." <<
3263 Debug::Extra << "The limit is " << MAXTCPLISTENPORTS << " HTTP ports.");
3264 continue;
3265 }
3266
3267#if USE_OPENSSL
3268 if (s->flags.tunnelSslBumping) {
3269 if (!Config.accessList.ssl_bump) {
3270 debugs(33, DBG_IMPORTANT, "WARNING: No ssl_bump configured. Disabling ssl-bump on " << scheme << "_port " << s->s);
3271 s->flags.tunnelSslBumping = false;
3272 }
3273 if (!s->secure.staticContext && !s->secure.generateHostCertificates) {
3274 debugs(1, DBG_IMPORTANT, "Will not bump SSL at " << scheme << "_port " << s->s << " due to TLS initialization failure.");
3275 s->flags.tunnelSslBumping = false;
3276 if (s->transport.protocol == AnyP::PROTO_HTTP)
3277 s->secure.encryptTransport = false;
3278 }
3279 if (s->flags.tunnelSslBumping) {
3280 // Create ssl_ctx cache for this port.
3281 Ssl::TheGlobalContextStorage().addLocalStorage(s->s, s->secure.dynamicCertMemCacheSize);
3282 }
3283 }
3284#endif
3285
3286 if (s->secure.encryptTransport && !s->secure.staticContext) {
3287 debugs(1, DBG_CRITICAL, "ERROR: Ignoring " << scheme << "_port " << s->s << " due to TLS context initialization failure.");
3288 continue;
3289 }
3290
3291 const auto protocol = s->transport.protocol;
3292 assert(protocol == AnyP::PROTO_HTTP || protocol == AnyP::PROTO_HTTPS);
3293 const auto isHttps = protocol == AnyP::PROTO_HTTPS;
3294 using AcceptCall = CommCbFunPtrCallT<CommAcceptCbPtrFun>;
3295 RefCount<AcceptCall> subCall = commCbCall(5, 5, isHttps ? "httpsAccept" : "httpAccept",
3298 }
3299 CodeContext::Reset(savedContext);
3300}
3301
3302void
3304{
3305 // Fill out a Comm::Connection which IPC will open as a listener for us
3306 port->listenConn = new Comm::Connection;
3307 port->listenConn->local = port->s;
3308 port->listenConn->flags =
3310 (port->flags.tproxyIntercept ? COMM_TRANSPARENT : 0) |
3311 (port->flags.natIntercept ? COMM_INTERCEPTION : 0) |
3312 (port->workerQueues ? COMM_REUSEPORT : 0);
3313
3314 // route new connections to subCall
3315 typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
3317 const auto listenCall =
3318 asyncCall(33, 2, "clientListenerConnectionOpened",
3320 port, fdNote, sub));
3321 AsyncCallback<Ipc::StartListeningAnswer> callback(listenCall);
3322 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, port->listenConn, fdNote, callback);
3323
3326 ++NHttpSockets;
3327}
3328
3330static void
3332{
3333 Must(s != nullptr);
3334
3335 if (!OpenedHttpSocket(s->listenConn, portTypeNote))
3336 return;
3337
3338 Must(Comm::IsConnOpen(s->listenConn));
3339
3340 // TCP: setup a job to handle accept() with subscribed handler
3341 AsyncJob::Start(new Comm::TcpAcceptor(s, FdNote(portTypeNote), sub));
3342
3343 debugs(1, Important(13), "Accepting " <<
3344 (s->flags.natIntercept ? "NAT intercepted " : "") <<
3345 (s->flags.tproxyIntercept ? "TPROXY intercepted " : "") <<
3346 (s->flags.tunnelSslBumping ? "SSL bumped " : "") <<
3347 (s->flags.accelSurrogate ? "reverse-proxy " : "")
3348 << FdNote(portTypeNote) << " connections at "
3349 << s->listenConn);
3350
3351 Must(AddOpenedHttpSocket(s->listenConn)); // otherwise, we have received a fd we did not ask for
3352
3353#if HAVE_LIBSYSTEMD
3354 // When the very first port opens, tell systemd we are able to serve connections.
3355 // Subsequent sd_notify() calls, including calls during reconfiguration,
3356 // do nothing because the first call parameter is 1.
3357 // XXX: Send the notification only after opening all configured ports.
3359 const auto result = sd_notify(1, "READY=1");
3360 if (result < 0) {
3361 debugs(1, DBG_IMPORTANT, "WARNING: failed to send start-up notification to systemd" <<
3362 Debug::Extra << "sd_notify() error: " << xstrerr(-result));
3363 }
3364 }
3365#endif
3366}
3367
3368void
3370{
3373
3374 if (NHttpSockets < 1)
3375 fatal("No HTTP, HTTPS, or FTP ports configured");
3376}
3377
3378void
3380{
3381 const auto savedContext = CodeContext::Current();
3382 for (AnyP::PortCfgPointer s = HttpPortList; s != nullptr; s = s->next) {
3384 if (s->listenConn != nullptr) {
3385 debugs(1, Important(14), "Closing HTTP(S) port " << s->listenConn->local);
3386 s->listenConn->close();
3387 s->listenConn = nullptr;
3388 }
3389 }
3390 CodeContext::Reset(savedContext);
3391
3393
3394 // TODO see if we can drop HttpSockets array entirely */
3395 for (int i = 0; i < NHttpSockets; ++i) {
3396 HttpSockets[i] = -1;
3397 }
3398
3399 NHttpSockets = 0;
3400}
3401
3402int
3404{
3405 SBuf vary(request->vary_headers);
3406 const auto &reply = entry->mem().freshestReply();
3407 auto has_vary = reply.header.has(Http::HdrType::VARY);
3408#if X_ACCELERATOR_VARY
3409
3410 has_vary |=
3411 reply.header.has(Http::HdrType::HDR_X_ACCELERATOR_VARY);
3412#endif
3413
3414 if (!has_vary || entry->mem_obj->vary_headers.isEmpty()) {
3415 if (!vary.isEmpty()) {
3416 /* Oops... something odd is going on here.. */
3417 debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary object on second attempt, '" <<
3418 entry->mem_obj->urlXXX() << "' '" << vary << "'");
3419 request->vary_headers.clear();
3420 return VARY_CANCEL;
3421 }
3422
3423 if (!has_vary) {
3424 /* This is not a varying object */
3425 return VARY_NONE;
3426 }
3427
3428 /* virtual "vary" object found. Calculate the vary key and
3429 * continue the search
3430 */
3431 vary = httpMakeVaryMark(request, &reply);
3432
3433 if (!vary.isEmpty()) {
3434 request->vary_headers = vary;
3435 return VARY_OTHER;
3436 } else {
3437 /* Ouch.. we cannot handle this kind of variance */
3438 /* XXX This cannot really happen, but just to be complete */
3439 return VARY_CANCEL;
3440 }
3441 } else {
3442 if (vary.isEmpty()) {
3443 vary = httpMakeVaryMark(request, &reply);
3444
3445 if (!vary.isEmpty())
3446 request->vary_headers = vary;
3447 }
3448
3449 if (vary.isEmpty()) {
3450 /* Ouch.. we cannot handle this kind of variance */
3451 /* XXX This cannot really happen, but just to be complete */
3452 return VARY_CANCEL;
3453 } else if (vary.cmp(entry->mem_obj->vary_headers) == 0) {
3454 return VARY_MATCH;
3455 } else {
3456 /* Oops.. we have already been here and still haven't
3457 * found the requested variant. Bail out
3458 */
3459 debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary match on second attempt, '" <<
3460 entry->mem_obj->urlXXX() << "' '" << vary << "'");
3461 return VARY_CANCEL;
3462 }
3463 }
3464}
3465
3468{
3469 auto checklist = ACLFilledChecklist::Make(acl, nullptr);
3470 clientAclChecklistFill(*checklist, http);
3471 return checklist;
3472}
3473
3474void
3476{
3477 assert(http);
3478
3479 if (!checklist.request && http->request)
3480 checklist.setRequest(http->request);
3481
3482 if (!checklist.al && http->al) {
3483 checklist.updateAle(http->al);
3484 checklist.syncAle(http->request, http->log_uri);
3485 }
3486
3487 if (const auto conn = http->getConn())
3488 checklist.setConn(conn); // may already be set
3489}
3490
3491void
3493{
3494 const auto context = pipeline.front();
3495 if (const auto http = context ? context->http : nullptr)
3496 return clientAclChecklistFill(checklist, http); // calls checklist.setConn()
3497
3498 // no requests, but we always have connection-level details
3499 // TODO: ACL checks should not require a mutable ConnStateData. Adjust the
3500 // code that accidentally violates that principle to remove this const_cast!
3501 checklist.setConn(const_cast<ConnStateData*>(this));
3502
3503 // Set other checklist fields inside our fillConnectionLevelDetails() rather
3504 // than here because clientAclChecklistFill() code path calls that method
3505 // (via ACLFilledChecklist::setConn()) rather than calling us directly.
3506}
3507
3508void
3510{
3511 assert(checklist.conn() == this);
3513
3514 if (!checklist.request) { // preserve (better) addresses supplied by setRequest()
3515 checklist.src_addr = clientConnection->remote;
3516 checklist.my_addr = clientConnection->local; // TODO: or port->s?
3517 }
3518
3519#if USE_OPENSSL
3520 if (!checklist.sslErrors && sslServerBump)
3521 checklist.sslErrors = sslServerBump->sslErrors();
3522#endif
3523}
3524
3525bool
3530
3533{
3534 bodyPipe = new BodyPipe(this);
3535 if (size >= 0)
3537 else
3539 return bodyPipe;
3540}
3541
3542int64_t
3544{
3545 if (!bodyPipe)
3546 return 0; // request without a body or read/produced all body bytes
3547
3548 if (!bodyPipe->bodySizeKnown())
3549 return -1; // probably need to read more, but we cannot be sure
3550
3551 const int64_t needToProduce = bodyPipe->unproducedSize();
3552 const int64_t haveAvailable = static_cast<int64_t>(inBuf.length());
3553
3554 if (needToProduce <= haveAvailable)
3555 return 0; // we have read what we need (but are waiting for pipe space)
3556
3557 return needToProduce - haveAvailable;
3558}
3559
3560void
3562{
3563 debugs(33, 4, "receiving error (" << clientConnection << "): " << error <<
3564 "; old sending error: " <<
3565 (stoppedSending() ? stoppedSending_ : "none"));
3566
3567 if (const char *oldError = stoppedReceiving()) {
3568 debugs(33, 3, "already stopped receiving: " << oldError);
3569 return; // nothing has changed as far as this connection is concerned
3570 }
3571
3573
3574 if (const char *sendError = stoppedSending()) {
3575 debugs(33, 3, "closing because also stopped sending: " << sendError);
3577 }
3578}
3579
3580void
3582{
3583 if (bodyPipe != nullptr) {
3584 debugs(33, 4, "no consumer for virgin body " << bodyPipe->status());
3586 }
3587}
3588
3590void
3592{
3593 Must(bodyPipe != nullptr);
3594 debugs(33, 5, "start dechunking" << bodyPipe->status());
3597}
3598
3600void
3602{
3603 debugs(33, 5, "finish dechunking: " << withSuccess);
3604
3605 if (bodyPipe != nullptr) {
3606 debugs(33, 7, "dechunked tail: " << bodyPipe->status());
3607 BodyPipe::Pointer myPipe = bodyPipe;
3608 stopProducingFor(bodyPipe, withSuccess); // sets bodyPipe->bodySize()
3609 Must(!bodyPipe); // we rely on it being nil after we are done with body
3610 if (withSuccess) {
3611 Must(myPipe->bodySizeKnown());
3613 if (context != nullptr && context->http && context->http->request)
3614 context->http->request->setContentLength(myPipe->bodySize());
3615 }
3616 }
3617
3618 delete bodyParser;
3619 bodyParser = nullptr;
3620}
3621
3622// XXX: this is an HTTP/1-only operation
3623void
3625{
3626 if (const auto context = pipeline.front()) {
3627 if (context->http)
3628 context->http->al->reply = msg.reply;
3629 }
3630
3631 if (!isOpen()) {
3632 debugs(33, 3, "ignoring 1xx due to earlier closure");
3633 return;
3634 }
3635
3636 // HTTP/1 1xx status messages are only valid when there is a transaction to trigger them
3637 if (!pipeline.empty()) {
3638 HttpReply::Pointer rep(msg.reply);
3639 Must(rep);
3640 // remember the callback
3642
3645
3646 if (!writeControlMsgAndCall(rep.getRaw(), call)) {
3647 // but still inform the caller (so it may resume its operation)
3649 }
3650 return;
3651 }
3652
3653 debugs(33, 3, " closing due to missing context for 1xx");
3655}
3656
3657void
3659{
3661
3662 if (Http::StreamPointer deferredRequest = pipeline.front()) {
3663 debugs(33, 3, clientConnection << ": calling PushDeferredIfNeeded after control msg wrote");
3664 ClientSocketContextPushDeferredIfNeeded(deferredRequest, this);
3665 }
3666}
3667
3669void
3671{
3672 // FwdState might repin a failed connection sooner than this close
3673 // callback is called for the failed connection.
3674 assert(pinning.serverConnection == io.conn);
3675 pinning.closeHandler = nullptr; // Comm unregisters handlers before calling
3676 const bool sawZeroReply = pinning.zeroReply; // reset when unpinning
3677 pinning.serverConnection->noteClosure();
3678 unpinConnection(false);
3679
3680 if (sawZeroReply && clientConnection != nullptr) {
3681 debugs(33, 3, "Closing client connection on pinned zero reply.");
3683 }
3684
3685}
3686
3687void
3689{
3690 pinConnection(pinServer, *request);
3691}
3692
3693void
3695{
3696 Must(pic.connection);
3697 Must(pic.request);
3698 pinConnection(pic.connection, *pic.request);
3699
3700 // monitor pinned server connection for remote-end closures.
3702
3703 if (pipeline.empty())
3704 kick(); // in case parseRequests() was blocked by a busy pic.connection
3705}
3706
3708void
3710{
3711 if (Comm::IsConnOpen(pinning.serverConnection) &&
3712 pinning.serverConnection->fd == pinServer->fd) {
3713 debugs(33, 3, "already pinned" << pinServer);
3714 return;
3715 }
3716
3717 unpinConnection(true); // closes pinned connection, if any, and resets fields
3718
3719 pinning.serverConnection = pinServer;
3720
3721 debugs(33, 3, pinning.serverConnection);
3722
3723 Must(pinning.serverConnection != nullptr);
3724
3725 const char *pinnedHost = "[unknown]";
3726 pinning.host = xstrdup(request.url.host());
3727 pinning.port = request.url.port();
3728 pinnedHost = pinning.host;
3729 pinning.pinned = true;
3730 pinning.auth = request.flags.connectionAuth;
3731 char stmp[MAX_IPSTRLEN];
3732 char desc[FD_DESC_SZ];
3733 const auto peer = pinning.peer();
3734 snprintf(desc, FD_DESC_SZ, "%s pinned connection for %s (%d)",
3735 (pinning.auth || !peer) ? pinnedHost : peer->name,
3738 fd_note(pinning.serverConnection->fd, desc);
3739
3741 pinning.closeHandler = JobCallback(33, 5,
3743 // remember the pinned connection so that cb does not unpin a fresher one
3744 typedef CommCloseCbParams Params;
3745 Params &params = GetCommParams<Params>(pinning.closeHandler);
3746 params.conn = pinning.serverConnection;
3747 comm_add_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
3748}
3749
3752void
3754{
3755 if (pinning.readHandler != nullptr)
3756 return; // already monitoring
3757
3759 pinning.readHandler = JobCallback(33, 3,
3761 Comm::Read(pinning.serverConnection, pinning.readHandler);
3762}
3763
3764void
3766{
3767 if (pinning.readHandler != nullptr) {
3768 Comm::ReadCancel(pinning.serverConnection->fd, pinning.readHandler);
3769 pinning.readHandler = nullptr;
3770 }
3771}
3772
3773#if USE_OPENSSL
3774bool
3776{
3777 // A ready-for-reading connection means that the TLS server either closed
3778 // the connection, sent us some unexpected HTTP data, or started TLS
3779 // renegotiations. We should close the connection except for the last case.
3780
3781 Must(pinning.serverConnection != nullptr);
3782 auto ssl = fd_table[pinning.serverConnection->fd].ssl.get();
3783 if (!ssl)
3784 return false;
3785
3786 char buf[1];
3787 const int readResult = SSL_read(ssl, buf, sizeof(buf));
3788
3789 if (readResult > 0 || SSL_pending(ssl) > 0) {
3790 debugs(83, 2, pinning.serverConnection << " TLS application data read");
3791 return false;
3792 }
3793
3794 switch(const int error = SSL_get_error(ssl, readResult)) {
3795 case SSL_ERROR_WANT_WRITE:
3796 debugs(83, DBG_IMPORTANT, pinning.serverConnection << " TLS SSL_ERROR_WANT_WRITE request for idle pinned connection");
3797 [[fallthrough]]; // to restart monitoring, for now
3798
3799 case SSL_ERROR_NONE:
3800 case SSL_ERROR_WANT_READ:
3802 return true;
3803
3804 default:
3805 debugs(83, 2, pinning.serverConnection << " TLS error: " << error);
3806 return false;
3807 }
3808
3809 // not reached
3810 return true;
3811}
3812#endif
3813
3816void
3818{
3819 pinning.readHandler = nullptr; // Comm unregisters handlers before calling
3820
3821 if (io.flag == Comm::ERR_CLOSING)
3822 return; // close handler will clean up
3823
3824 Must(pinning.serverConnection == io.conn);
3825
3826#if USE_OPENSSL
3828 return;
3829#endif
3830
3831 const bool clientIsIdle = pipeline.empty();
3832
3833 debugs(33, 3, "idle pinned " << pinning.serverConnection << " read " <<
3834 io.size << (clientIsIdle ? " with idle client" : ""));
3835
3836 pinning.serverConnection->close();
3837
3838 // If we are still sending data to the client, do not close now. When we are done sending,
3839 // ConnStateData::kick() checks pinning.serverConnection and will close.
3840 // However, if we are idle, then we must close to inform the idle client and minimize races.
3841 if (clientIsIdle && clientConnection != nullptr)
3843}
3844
3847{
3848 debugs(33, 7, pinning.serverConnection);
3849 Must(request);
3850
3851 const auto pinningError = [&](const err_type type) {
3852 unpinConnection(true);
3853 HttpRequestPointer requestPointer = request;
3854 return ErrorState::NewForwarding(type, requestPointer, ale);
3855 };
3856
3857 if (!Comm::IsConnOpen(pinning.serverConnection))
3858 throw pinningError(ERR_ZERO_SIZE_OBJECT);
3859
3860 if (pinning.auth && pinning.host && strcasecmp(pinning.host, request->url.host()) != 0)
3861 throw pinningError(ERR_CANNOT_FORWARD); // or generalize ERR_CONFLICT_HOST
3862
3863 if (pinning.port != request->url.port())
3864 throw pinningError(ERR_CANNOT_FORWARD); // or generalize ERR_CONFLICT_HOST
3865
3866 if (pinning.serverConnection->toGoneCachePeer())
3867 throw pinningError(ERR_ZERO_SIZE_OBJECT);
3868
3869 if (pinning.peerAccessDenied)
3870 throw pinningError(ERR_CANNOT_FORWARD); // or generalize ERR_FORWARDING_DENIED
3871
3873 return pinning.serverConnection;
3874}
3875
3878{
3879 if (const auto connManager = request ? request->pinnedConnection() : nullptr)
3880 return connManager->borrowPinnedConnection(request, ale);
3881
3882 // ERR_CANNOT_FORWARD is somewhat misleading here; we can still forward, but
3883 // there is no point since the client connection is now gone
3884 HttpRequestPointer requestPointer = request;
3885 throw ErrorState::NewForwarding(ERR_CANNOT_FORWARD, requestPointer, ale);
3886}
3887
3888void
3890{
3891 debugs(33, 3, pinning.serverConnection);
3892
3893 if (Comm::IsConnOpen(pinning.serverConnection)) {
3894 if (pinning.closeHandler != nullptr) {
3895 comm_remove_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
3896 pinning.closeHandler = nullptr;
3897 }
3898
3900
3901 // close the server side socket if requested
3902 if (andClose)
3903 pinning.serverConnection->close();
3904 pinning.serverConnection = nullptr;
3905 }
3906
3907 safe_free(pinning.host);
3908
3909 pinning.zeroReply = false;
3910 pinning.peerAccessDenied = false;
3911
3912 /* NOTE: pinning.pinned should be kept. This combined with fd == -1 at the end of a request indicates that the host
3913 * connection has gone away */
3914}
3915
3916void
3918{
3919 auto error = rawError; // (cheap) copy so that we can detail
3920 // We detail even ERR_NONE: There should be no transactions left, and
3921 // detailed ERR_NONE will be unused. Otherwise, this detail helps in triage.
3922 if (error.details.empty()) {
3923 static const auto d = MakeNamedErrorDetail("WITH_CLIENT");
3924 error.details.push_back(d);
3925 }
3926
3927 debugs(33, 3, pipeline.count() << '/' << pipeline.nrequests << " after " << error);
3928
3929 if (pipeline.empty()) {
3930 bareError.update(error); // XXX: bareLogTagsErrors
3931 } else {
3932 // We terminate the current CONNECT/PUT/etc. context below, logging any
3933 // error details, but that context may leave unparsed bytes behind.
3934 // Consume them to stop checkLogging() from logging them again later.
3935 const auto intputToConsume =
3936#if USE_OPENSSL
3937 parsingTlsHandshake ? "TLS handshake" : // more specific than CONNECT
3938#endif
3939 bodyPipe ? "HTTP request body" :
3940 pipeline.back()->mayUseConnection() ? "HTTP CONNECT" :
3941 nullptr;
3942
3943 while (const auto context = pipeline.front()) {
3944 context->noteIoError(error, lte);
3945 context->finished(); // cleanup and self-deregister
3946 assert(context != pipeline.front());
3947 }
3948
3949 if (intputToConsume && !inBuf.isEmpty()) {
3950 debugs(83, 5, "forgetting client " << intputToConsume << " bytes: " << inBuf.length());
3951 inBuf.clear();
3952 }
3953 }
3954
3956}
3957
3959void
3961{
3962 // to simplify our logic, we assume that terminateAll() has been called
3964
3965 // do not log connections that closed after a transaction (it is normal)
3966 // TODO: access_log needs ACLs to match received-no-bytes connections
3968 return;
3969
3970 /* Create a temporary ClientHttpRequest object. Its destructor will log. */
3971 ClientHttpRequest http(this);
3972 http.req_sz = inBuf.length();
3973 // XXX: Or we died while waiting for the pinned connection to become idle.
3974 http.setErrorUri("error:transaction-end-before-headers");
3975 http.updateError(bareError);
3976}
3977
3978bool
3980{
3981 // PROXY protocol bytes are meant for us and, hence, cannot be tunneled
3983 return false;
3984
3985 // If our decision here is negative, configuration changes are irrelevant.
3986 // Otherwise, clientTunnelOnError() rechecks configuration before tunneling.
3988 return false;
3989
3990 // TODO: Figure out whether/how we can support FTP tunneling.
3991 if (port->transport.protocol == AnyP::PROTO_FTP)
3992 return false;
3993
3994#if USE_OPENSSL
3996 return true;
3997
3998 // the 1st HTTP request on a bumped connection
4000 return true;
4001#endif
4002
4003 // the 1st HTTP(S) request on a connection to an intercepting port
4004 if (!pipeline.nrequests && transparent())
4005 return true;
4006
4007 return false;
4008}
4009
4012{
4013 if (!theNotes)
4014 theNotes = new NotePairs;
4015 return theNotes;
4016}
4017
4018std::ostream &
4019operator <<(std::ostream &os, const ConnStateData::PinnedIdleContext &pic)
4020{
4021 return os << pic.connection << ", request=" << pic.request;
4022}
4023
4024std::ostream &
4025operator <<(std::ostream &os, const ConnStateData::ServerConnectionContext &scc)
4026{
4027 return os << scc.conn_ << ", srv_bytes=" << scc.preReadServerBytes.length();
4028}
4029
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:693
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:97
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:125
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