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