]> git.ipfire.org Git - thirdparty/squid.git/blob - src/adaptation/icap/Xaction.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / adaptation / icap / Xaction.cc
1 /*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /* DEBUG: section 93 ICAP (RFC 3507) Client */
10
11 #include "squid.h"
12 #include "acl/FilledChecklist.h"
13 #include "adaptation/icap/Config.h"
14 #include "adaptation/icap/Launcher.h"
15 #include "adaptation/icap/Xaction.h"
16 #include "base/AsyncCallbacks.h"
17 #include "base/IoManip.h"
18 #include "base/JobWait.h"
19 #include "base/TextException.h"
20 #include "comm.h"
21 #include "comm/Connection.h"
22 #include "comm/ConnOpener.h"
23 #include "comm/Read.h"
24 #include "comm/Write.h"
25 #include "CommCalls.h"
26 #include "error/Detail.h"
27 #include "fde.h"
28 #include "FwdState.h"
29 #include "globals.h"
30 #include "HttpReply.h"
31 #include "icap_log.h"
32 #include "ipcache.h"
33 #include "pconn.h"
34 #include "security/PeerConnector.h"
35 #include "SquidConfig.h"
36
37 #include <optional>
38
39 namespace Ssl
40 {
41 /// A simple PeerConnector for Secure ICAP services. No SslBump capabilities.
42 class IcapPeerConnector: public Security::PeerConnector {
43 CBDATA_CHILD(IcapPeerConnector);
44 public:
45 IcapPeerConnector(
46 Adaptation::Icap::ServiceRep::Pointer &service,
47 const Comm::ConnectionPointer &aServerConn,
48 const AsyncCallback<Security::EncryptorAnswer> &aCallback,
49 AccessLogEntry::Pointer const &alp,
50 const time_t timeout = 0):
51 AsyncJob("Ssl::IcapPeerConnector"),
52 Security::PeerConnector(aServerConn, aCallback, alp, timeout), icapService(service) {}
53
54 /* Security::PeerConnector API */
55 bool initialize(Security::SessionPointer &) override;
56 void noteNegotiationDone(ErrorState *error) override;
57 Security::ContextPointer getTlsContext() override {
58 return icapService->sslContext;
59 }
60
61 private:
62 /* Acl::ChecklistFiller API */
63 void fillChecklist(ACLFilledChecklist &) const override;
64
65 Adaptation::Icap::ServiceRep::Pointer icapService;
66 };
67 } // namespace Ssl
68
69 CBDATA_NAMESPACED_CLASS_INIT(Ssl, IcapPeerConnector);
70
71 Adaptation::Icap::Xaction::Xaction(const char *aTypeName, Adaptation::Icap::ServiceRep::Pointer &aService):
72 AsyncJob(aTypeName),
73 Adaptation::Initiate(aTypeName),
74 icapRequest(nullptr),
75 icapReply(nullptr),
76 attempts(0),
77 theService(aService),
78 commEof(false),
79 reuseConnection(true),
80 isRetriable(true),
81 isRepeatable(true),
82 ignoreLastWrite(false),
83 waitingForDns(false),
84 alep(new AccessLogEntry),
85 al(*alep)
86 {
87 debugs(93,3, typeName << " constructed, this=" << this <<
88 " [icapx" << id << ']'); // we should not call virtual status() here
89 const auto mx = MasterXaction::MakePortless<XactionInitiator::initAdaptation>();
90 icapRequest = new HttpRequest(mx);
91 HTTPMSGLOCK(icapRequest);
92 icap_tr_start = current_time;
93 memset(&icap_tio_start, 0, sizeof(icap_tio_start));
94 memset(&icap_tio_finish, 0, sizeof(icap_tio_finish));
95 }
96
97 Adaptation::Icap::Xaction::~Xaction()
98 {
99 debugs(93,3, typeName << " destructed, this=" << this <<
100 " [icapx" << id << ']'); // we should not call virtual status() here
101 HTTPMSGUNLOCK(icapRequest);
102 }
103
104 AccessLogEntry::Pointer
105 Adaptation::Icap::Xaction::masterLogEntry()
106 {
107 AccessLogEntry::Pointer nil;
108 return nil;
109 }
110
111 Adaptation::Icap::ServiceRep &
112 Adaptation::Icap::Xaction::service()
113 {
114 Must(theService != nullptr);
115 return *theService;
116 }
117
118 void Adaptation::Icap::Xaction::disableRetries()
119 {
120 debugs(93,5, typeName << (isRetriable ? " from now on" : " still") <<
121 " cannot be retried " << status());
122 isRetriable = false;
123 }
124
125 void Adaptation::Icap::Xaction::disableRepeats(const char *reason)
126 {
127 debugs(93,5, typeName << (isRepeatable ? " from now on" : " still") <<
128 " cannot be repeated because " << reason << status());
129 isRepeatable = false;
130 }
131
132 void Adaptation::Icap::Xaction::start()
133 {
134 Adaptation::Initiate::start();
135 }
136
137 // TODO: Make reusable by moving this (and the printing operator from
138 // ip/Address.h that this code is calling) into ip/print.h or similar.
139 namespace Ip {
140
141 inline std::ostream &
142 operator <<(std::ostream &os, const std::optional<Address> &optional)
143 {
144 if (optional.has_value())
145 os << optional.value();
146 else
147 os << "[no IP]";
148 return os;
149 }
150
151 }
152
153 static void
154 icapLookupDnsResults(const ipcache_addrs *ia, const Dns::LookupDetails &, void *data)
155 {
156 Adaptation::Icap::Xaction *xa = static_cast<Adaptation::Icap::Xaction *>(data);
157 const auto &addr = ia ? std::optional<Ip::Address>(ia->current()) : std::optional<Ip::Address>();
158 CallJobHere1(93, 5, CbcPointer<Adaptation::Icap::Xaction>(xa), Adaptation::Icap::Xaction, dnsLookupDone, addr);
159 }
160
161 // TODO: obey service-specific, OPTIONS-reported connection limit
162 void
163 Adaptation::Icap::Xaction::openConnection()
164 {
165 Must(!haveConnection());
166
167 Adaptation::Icap::ServiceRep &s = service();
168
169 if (!TheConfig.reuse_connections)
170 disableRetries(); // this will also safely drain pconn pool
171
172 if (const auto pconn = s.getIdleConnection(isRetriable)) {
173 useTransportConnection(pconn);
174 return;
175 }
176
177 disableRetries(); // we only retry pconn failures
178
179 // Attempt to open a new connection...
180 debugs(93,3, typeName << " opens connection to " << s.cfg().host.termedBuf() << ":" << s.cfg().port);
181
182 // Locate the Service IP(s) to open
183 assert(!waitingForDns);
184 waitingForDns = true; // before the possibly-synchronous ipcache_nbgethostbyname()
185 ipcache_nbgethostbyname(s.cfg().host.termedBuf(), icapLookupDnsResults, this);
186 }
187
188 void
189 Adaptation::Icap::Xaction::dnsLookupDone(std::optional<Ip::Address> addr)
190 {
191 assert(waitingForDns);
192 waitingForDns = false;
193
194 Adaptation::Icap::ServiceRep &s = service();
195
196 if (!addr.has_value()) {
197 debugs(44, DBG_IMPORTANT, "ERROR: ICAP: Unknown service host: " << s.cfg().host);
198
199 #if WHEN_IPCACHE_NBGETHOSTBYNAME_USES_ASYNC_CALLS
200 dieOnConnectionFailure(); // throws
201 #else // take a step back into protected Async call dialing.
202 CallJobHere(93, 3, this, Xaction, Xaction::dieOnConnectionFailure);
203 #endif
204 return;
205 }
206
207 const Comm::ConnectionPointer conn = new Comm::Connection();
208 conn->remote = addr.value();
209 conn->remote.port(s.cfg().port);
210 getOutgoingAddress(nullptr, conn);
211
212 // TODO: service bypass status may differ from that of a transaction
213 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommConnectCbParams> ConnectDialer;
214 AsyncCall::Pointer callback = JobCallback(93, 3, ConnectDialer, this, Adaptation::Icap::Xaction::noteCommConnected);
215 const auto cs = new Comm::ConnOpener(conn, callback, TheConfig.connect_timeout(service().cfg().bypass));
216 cs->setHost(s.cfg().host.termedBuf());
217 transportWait.start(cs, callback);
218 }
219
220 void Adaptation::Icap::Xaction::closeConnection()
221 {
222 if (haveConnection()) {
223
224 if (closer != nullptr) {
225 comm_remove_close_handler(connection->fd, closer);
226 closer = nullptr;
227 }
228
229 commUnsetConnTimeout(connection);
230
231 cancelRead(); // may not work
232
233 if (reuseConnection && !doneWithIo()) {
234 //status() adds leading spaces.
235 debugs(93,5, "not reusing pconn due to pending I/O" << status());
236 reuseConnection = false;
237 }
238
239 if (reuseConnection)
240 disableRetries();
241
242 const bool reset = !reuseConnection &&
243 (al.icap.outcome == xoGone || al.icap.outcome == xoError);
244
245 Adaptation::Icap::ServiceRep &s = service();
246 s.putConnection(connection, reuseConnection, reset, status());
247
248 writer = nullptr;
249 reader = nullptr;
250 connection = nullptr;
251 }
252 }
253
254 /// called when the connection attempt to an ICAP service completes (successfully or not)
255 void Adaptation::Icap::Xaction::noteCommConnected(const CommConnectCbParams &io)
256 {
257 transportWait.finish();
258
259 if (io.flag != Comm::OK) {
260 dieOnConnectionFailure(); // throws
261 return;
262 }
263
264 useTransportConnection(io.conn);
265 }
266
267 /// React to the availability of a transport connection to the ICAP service.
268 /// The given connection may (or may not) be secured already.
269 void
270 Adaptation::Icap::Xaction::useTransportConnection(const Comm::ConnectionPointer &conn)
271 {
272 assert(Comm::IsConnOpen(conn));
273 assert(!connection);
274
275 // If it is a reused connection and the TLS object is built
276 // we should not negotiate new TLS session
277 const auto &ssl = fd_table[conn->fd].ssl;
278 if (!ssl && service().cfg().secure.encryptTransport) {
279 // XXX: Exceptions orphan conn.
280 const auto callback = asyncCallback(93, 4, Adaptation::Icap::Xaction::handleSecuredPeer, this);
281 const auto sslConnector = new Ssl::IcapPeerConnector(theService, conn, callback, masterLogEntry(), TheConfig.connect_timeout(service().cfg().bypass));
282
283 encryptionWait.start(sslConnector, callback);
284 return;
285 }
286
287 useIcapConnection(conn);
288 }
289
290 /// react to the availability of a fully-ready ICAP connection
291 void
292 Adaptation::Icap::Xaction::useIcapConnection(const Comm::ConnectionPointer &conn)
293 {
294 assert(!connection);
295 assert(conn);
296 assert(Comm::IsConnOpen(conn));
297 connection = conn;
298 service().noteConnectionUse(connection);
299
300 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommCloseCbParams> CloseDialer;
301 closer = asyncCall(93, 5, "Adaptation::Icap::Xaction::noteCommClosed",
302 CloseDialer(this,&Adaptation::Icap::Xaction::noteCommClosed));
303 comm_add_close_handler(connection->fd, closer);
304
305 startShoveling();
306 }
307
308 void Adaptation::Icap::Xaction::dieOnConnectionFailure()
309 {
310 debugs(93, 2, typeName <<
311 " failed to connect to " << service().cfg().uri);
312 service().noteConnectionFailed("failure");
313 static const auto d = MakeNamedErrorDetail("ICAP_XACT_START");
314 detailError(d);
315 throw TexcHere("cannot connect to the ICAP service");
316 }
317
318 void Adaptation::Icap::Xaction::scheduleWrite(MemBuf &buf)
319 {
320 Must(haveConnection());
321
322 // comm module will free the buffer
323 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommIoCbParams> Dialer;
324 writer = JobCallback(93, 3,
325 Dialer, this, Adaptation::Icap::Xaction::noteCommWrote);
326
327 Comm::Write(connection, &buf, writer);
328 updateTimeout();
329 }
330
331 void Adaptation::Icap::Xaction::noteCommWrote(const CommIoCbParams &io)
332 {
333 Must(writer != nullptr);
334 writer = nullptr;
335
336 if (ignoreLastWrite) {
337 // a hack due to comm inability to cancel a pending write
338 ignoreLastWrite = false;
339 debugs(93, 7, "ignoring last write; status: " << io.flag);
340 } else {
341 Must(io.flag == Comm::OK);
342 al.icap.bytesSent += io.size;
343 updateTimeout();
344 handleCommWrote(io.size);
345 }
346 }
347
348 // communication timeout with the ICAP service
349 void Adaptation::Icap::Xaction::noteCommTimedout(const CommTimeoutCbParams &)
350 {
351 debugs(93, 2, typeName << " failed: timeout with " <<
352 theService->cfg().methodStr() << " " <<
353 theService->cfg().uri << status());
354 reuseConnection = false;
355 assert(haveConnection());
356 closeConnection();
357 throw TextException("timed out while talking to the ICAP service", Here());
358 }
359
360 // unexpected connection close while talking to the ICAP service
361 void Adaptation::Icap::Xaction::noteCommClosed(const CommCloseCbParams &)
362 {
363 if (connection) {
364 connection->noteClosure();
365 connection = nullptr;
366 }
367 closer = nullptr;
368
369 static const auto d = MakeNamedErrorDetail("ICAP_XACT_CLOSE");
370 detailError(d);
371 mustStop("ICAP service connection externally closed");
372 }
373
374 void Adaptation::Icap::Xaction::callException(const std::exception &e)
375 {
376 setOutcome(xoError);
377 service().noteFailure();
378 Adaptation::Initiate::callException(e);
379 }
380
381 void Adaptation::Icap::Xaction::callEnd()
382 {
383 if (doneWithIo()) {
384 debugs(93, 5, typeName << " done with I/O" << status());
385 closeConnection();
386 }
387 Adaptation::Initiate::callEnd(); // may destroy us
388 }
389
390 bool Adaptation::Icap::Xaction::doneAll() const
391 {
392 return !waitingForDns && !transportWait && !encryptionWait &&
393 !reader && !writer &&
394 Adaptation::Initiate::doneAll();
395 }
396
397 void Adaptation::Icap::Xaction::updateTimeout()
398 {
399 Must(haveConnection());
400
401 if (reader != nullptr || writer != nullptr) {
402 // restart the timeout before each I/O
403 // XXX: why does Config.Timeout lacks a write timeout?
404 // TODO: service bypass status may differ from that of a transaction
405 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommTimeoutCbParams> TimeoutDialer;
406 AsyncCall::Pointer call = JobCallback(93, 5, TimeoutDialer, this, Adaptation::Icap::Xaction::noteCommTimedout);
407 commSetConnTimeout(connection, TheConfig.io_timeout(service().cfg().bypass), call);
408 } else {
409 // clear timeout when there is no I/O
410 // Do we need a lifetime timeout?
411 commUnsetConnTimeout(connection);
412 }
413 }
414
415 void Adaptation::Icap::Xaction::scheduleRead()
416 {
417 Must(haveConnection());
418 Must(!reader);
419 Must(readBuf.length() < SQUID_TCP_SO_RCVBUF); // will expand later if needed
420
421 typedef CommCbMemFunT<Adaptation::Icap::Xaction, CommIoCbParams> Dialer;
422 reader = JobCallback(93, 3, Dialer, this, Adaptation::Icap::Xaction::noteCommRead);
423 Comm::Read(connection, reader);
424 updateTimeout();
425 }
426
427 // comm module read a portion of the ICAP response for us
428 void Adaptation::Icap::Xaction::noteCommRead(const CommIoCbParams &io)
429 {
430 Must(reader != nullptr);
431 reader = nullptr;
432
433 Must(io.flag == Comm::OK);
434
435 // TODO: tune this better to expected message sizes
436 readBuf.reserveCapacity(SQUID_TCP_SO_RCVBUF);
437 // we are not asked to grow beyond the allowed maximum
438 Must(readBuf.length() < SQUID_TCP_SO_RCVBUF);
439 // now we can ensure that there is space to read new data,
440 // even if readBuf.spaceSize() currently returns zero.
441 readBuf.rawAppendStart(1);
442
443 CommIoCbParams rd(this); // will be expanded with ReadNow results
444 rd.conn = io.conn;
445
446 switch (Comm::ReadNow(rd, readBuf)) {
447 case Comm::INPROGRESS:
448 if (readBuf.isEmpty())
449 debugs(33, 2, io.conn << ": no data to process, " << xstrerr(rd.xerrno));
450 scheduleRead();
451 return;
452
453 case Comm::OK:
454 al.icap.bytesRead += rd.size;
455
456 updateTimeout();
457
458 debugs(93, 3, "read " << rd.size << " bytes");
459
460 disableRetries(); // because pconn did not fail
461
462 /* Continue to process previously read data */
463 break;
464
465 case Comm::ENDFILE: // close detected by 0-byte read
466 commEof = true;
467 reuseConnection = false;
468
469 // detect a pconn race condition: eof on the first pconn read
470 if (!al.icap.bytesRead && retriable()) {
471 setOutcome(xoRace);
472 mustStop("pconn race");
473 return;
474 }
475
476 break;
477
478 // case Comm::COMM_ERROR:
479 default: // no other flags should ever occur
480 debugs(11, 2, io.conn << ": read failure: " << xstrerr(rd.xerrno));
481 mustStop("unknown ICAP I/O read error");
482 return;
483 }
484
485 handleCommRead(io.size);
486 }
487
488 void Adaptation::Icap::Xaction::cancelRead()
489 {
490 if (reader != nullptr) {
491 Must(haveConnection());
492 Comm::ReadCancel(connection->fd, reader);
493 reader = nullptr;
494 }
495 }
496
497 bool
498 Adaptation::Icap::Xaction::parseHttpMsg(Http::Message *msg)
499 {
500 debugs(93, 5, "have " << readBuf.length() << " head bytes to parse");
501
502 Http::StatusCode error = Http::scNone;
503 // XXX: performance regression c_str() data copies
504 const char *buf = readBuf.c_str();
505 const bool parsed = msg->parse(buf, readBuf.length(), commEof, &error);
506 Must(parsed || !error); // success or need more data
507
508 if (!parsed) { // need more data
509 Must(mayReadMore());
510 msg->reset();
511 return false;
512 }
513
514 readBuf.consume(msg->hdr_sz);
515 return true;
516 }
517
518 bool Adaptation::Icap::Xaction::mayReadMore() const
519 {
520 return !doneReading() && // will read more data
521 readBuf.length() < SQUID_TCP_SO_RCVBUF; // have space for more data
522 }
523
524 bool Adaptation::Icap::Xaction::doneReading() const
525 {
526 return commEof;
527 }
528
529 bool Adaptation::Icap::Xaction::doneWriting() const
530 {
531 return !writer;
532 }
533
534 bool Adaptation::Icap::Xaction::doneWithIo() const
535 {
536 return haveConnection() &&
537 !transportWait && !reader && !writer && // fast checks, some redundant
538 doneReading() && doneWriting();
539 }
540
541 bool Adaptation::Icap::Xaction::haveConnection() const
542 {
543 return connection != nullptr && connection->isOpen();
544 }
545
546 // initiator aborted
547 void Adaptation::Icap::Xaction::noteInitiatorAborted()
548 {
549
550 if (theInitiator.set()) {
551 debugs(93,4, "Initiator gone before ICAP transaction ended");
552 clearInitiator();
553 static const auto d = MakeNamedErrorDetail("ICAP_INIT_GONE");
554 detailError(d);
555 setOutcome(xoGone);
556 mustStop("initiator aborted");
557 }
558
559 }
560
561 void Adaptation::Icap::Xaction::setOutcome(const Adaptation::Icap::XactOutcome &xo)
562 {
563 if (al.icap.outcome != xoUnknown) {
564 debugs(93, 3, "WARNING: resetting outcome: from " << al.icap.outcome << " to " << xo);
565 } else {
566 debugs(93, 4, xo);
567 }
568 al.icap.outcome = xo;
569 }
570
571 // This 'last chance' method is called before a 'done' transaction is deleted.
572 // It is wrong to call virtual methods from a destructor. Besides, this call
573 // indicates that the transaction will terminate as planned.
574 void Adaptation::Icap::Xaction::swanSong()
575 {
576 // kids should sing first and then call the parent method.
577 if (transportWait || encryptionWait) {
578 service().noteConnectionFailed("abort");
579 }
580
581 closeConnection(); // TODO: rename because we do not always close
582
583 readBuf.clear();
584
585 tellQueryAborted();
586
587 maybeLog();
588
589 Adaptation::Initiate::swanSong();
590 }
591
592 void Adaptation::Icap::Xaction::tellQueryAborted()
593 {
594 if (theInitiator.set()) {
595 Adaptation::Icap::XactAbortInfo abortInfo(icapRequest, icapReply.getRaw(),
596 retriable(), repeatable());
597 Launcher *launcher = dynamic_cast<Launcher*>(theInitiator.get());
598 // launcher may be nil if initiator is invalid
599 CallJobHere1(91,5, CbcPointer<Launcher>(launcher),
600 Launcher, noteXactAbort, abortInfo);
601 clearInitiator();
602 }
603 }
604
605 void Adaptation::Icap::Xaction::maybeLog()
606 {
607 if (IcapLogfileStatus == LOG_ENABLE) {
608 finalizeLogInfo();
609 icapLogLog(alep);
610 }
611 }
612
613 void Adaptation::Icap::Xaction::finalizeLogInfo()
614 {
615 //prepare log data
616 al.icp.opcode = ICP_INVALID;
617
618 const Adaptation::Icap::ServiceRep &s = service();
619 al.icap.hostAddr = s.cfg().host.termedBuf();
620 al.icap.serviceName = s.cfg().key;
621 al.icap.reqUri = s.cfg().uri;
622
623 tvSub(al.icap.ioTime, icap_tio_start, icap_tio_finish);
624 tvSub(al.icap.trTime, icap_tr_start, current_time);
625
626 al.icap.request = icapRequest;
627 HTTPMSGLOCK(al.icap.request);
628 if (icapReply != nullptr) {
629 al.icap.reply = icapReply.getRaw();
630 HTTPMSGLOCK(al.icap.reply);
631 al.icap.resStatus = icapReply->sline.status();
632 }
633 }
634
635 // returns a temporary string depicting transaction status, for debugging
636 const char *Adaptation::Icap::Xaction::status() const
637 {
638 static MemBuf buf;
639 buf.reset();
640 buf.append(" [", 2);
641 fillPendingStatus(buf);
642 buf.append("/", 1);
643 fillDoneStatus(buf);
644 buf.appendf(" %s%u]", id.prefix(), id.value);
645 buf.terminate();
646
647 return buf.content();
648 }
649
650 void Adaptation::Icap::Xaction::fillPendingStatus(MemBuf &buf) const
651 {
652 if (haveConnection()) {
653 buf.appendf("FD %d", connection->fd);
654
655 if (writer != nullptr)
656 buf.append("w", 1);
657
658 if (reader != nullptr)
659 buf.append("r", 1);
660
661 buf.append(";", 1);
662 }
663
664 if (waitingForDns)
665 buf.append("D", 1);
666 }
667
668 void Adaptation::Icap::Xaction::fillDoneStatus(MemBuf &buf) const
669 {
670 if (haveConnection() && commEof)
671 buf.appendf("Comm(%d)", connection->fd);
672
673 if (stopReason != nullptr)
674 buf.append("Stopped", 7);
675 }
676
677 bool Adaptation::Icap::Xaction::fillVirginHttpHeader(MemBuf &) const
678 {
679 return false;
680 }
681
682 bool
683 Ssl::IcapPeerConnector::initialize(Security::SessionPointer &serverSession)
684 {
685 if (!Security::PeerConnector::initialize(serverSession))
686 return false;
687
688 assert(!icapService->cfg().secure.sslDomain.isEmpty());
689 #if USE_OPENSSL
690 SBuf *host = new SBuf(icapService->cfg().secure.sslDomain);
691 SSL_set_ex_data(serverSession.get(), ssl_ex_index_server, host);
692 setClientSNI(serverSession.get(), host->c_str());
693 #endif
694
695 Security::SetSessionResumeData(serverSession, icapService->sslSession);
696 return true;
697 }
698
699 void
700 Ssl::IcapPeerConnector::fillChecklist(ACLFilledChecklist &checklist) const
701 {
702 Security::PeerConnector::fillChecklist(checklist);
703 if (checklist.dst_peer_name.isEmpty())
704 checklist.dst_peer_name = icapService->cfg().secure.sslDomain;
705 }
706
707 void
708 Ssl::IcapPeerConnector::noteNegotiationDone(ErrorState *error)
709 {
710 if (error)
711 return;
712
713 const int fd = serverConnection()->fd;
714 Security::MaybeGetSessionResumeData(fd_table[fd].ssl, icapService->sslSession);
715 }
716
717 void
718 Adaptation::Icap::Xaction::handleSecuredPeer(Security::EncryptorAnswer &answer)
719 {
720 encryptionWait.finish();
721
722 assert(!answer.tunneled);
723 if (answer.error.get()) {
724 assert(!answer.conn);
725 // TODO: Refactor dieOnConnectionFailure() to be usable here as well.
726 debugs(93, 2, typeName <<
727 " TLS negotiation to " << service().cfg().uri << " failed");
728 service().noteConnectionFailed("failure");
729 static const auto d = MakeNamedErrorDetail("ICAP_XACT_SSL_START");
730 detailError(d);
731 throw TexcHere("cannot connect to the TLS ICAP service");
732 }
733
734 debugs(93, 5, "TLS negotiation to " << service().cfg().uri << " complete");
735
736 assert(answer.conn);
737
738 // The socket could get closed while our callback was queued. Sync
739 // Connection. XXX: Connection::fd may already be stale/invalid here.
740 if (answer.conn->isOpen() && fd_table[answer.conn->fd].closing()) {
741 answer.conn->noteClosure();
742 service().noteConnectionFailed("external TLS connection closure");
743 static const auto d = MakeNamedErrorDetail("ICAP_XACT_SSL_CLOSE");
744 detailError(d);
745 throw TexcHere("external closure of the TLS ICAP service connection");
746 }
747
748 useIcapConnection(answer.conn);
749 }
750