]> git.ipfire.org Git - thirdparty/squid.git/blob - src/client_side.cc
Replace ConnStateData::addContextToQueue() with Pipeline::add()
[thirdparty/squid.git] / src / client_side.cc
1 /*
2 * Copyright (C) 1996-2015 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
11 /**
12 \defgroup ClientSide Client-Side Logics
13 *
14 \section cserrors Errors and client side
15 *
16 \par Problem the first:
17 * the store entry is no longer authoritative on the
18 * reply status. EBITTEST (E_ABORT) is no longer a valid test outside
19 * of client_side_reply.c.
20 * Problem the second: resources are wasted if we delay in cleaning up.
21 * Problem the third we can't depend on a connection close to clean up.
22 *
23 \par Nice thing the first:
24 * Any step in the stream can callback with data
25 * representing an error.
26 * Nice thing the second: once you stop requesting reads from upstream,
27 * upstream can be stopped too.
28 *
29 \par Solution #1:
30 * Error has a callback mechanism to hand over a membuf
31 * with the error content. The failing node pushes that back as the
32 * reply. Can this be generalised to reduce duplicate efforts?
33 * A: Possibly. For now, only one location uses this.
34 * How to deal with pre-stream errors?
35 * Tell client_side_reply that we *want* an error page before any
36 * stream calls occur. Then we simply read as normal.
37 *
38 *
39 \section pconn_logic Persistent connection logic:
40 *
41 \par
42 * requests (httpClientRequest structs) get added to the connection
43 * list, with the current one being chr
44 *
45 \par
46 * The request is *immediately* kicked off, and data flows through
47 * to clientSocketRecipient.
48 *
49 \par
50 * If the data that arrives at clientSocketRecipient is not for the current
51 * request, clientSocketRecipient simply returns, without requesting more
52 * data, or sending it.
53 *
54 \par
55 * ClientKeepAliveNextRequest will then detect the presence of data in
56 * the next ClientHttpRequest, and will send it, restablishing the
57 * data flow.
58 */
59
60 #include "squid.h"
61 #include "acl/FilledChecklist.h"
62 #include "anyp/PortCfg.h"
63 #include "base/Subscription.h"
64 #include "base/TextException.h"
65 #include "CachePeer.h"
66 #include "client_db.h"
67 #include "client_side.h"
68 #include "client_side_reply.h"
69 #include "client_side_request.h"
70 #include "ClientRequestContext.h"
71 #include "clientStream.h"
72 #include "comm.h"
73 #include "comm/Connection.h"
74 #include "comm/Loops.h"
75 #include "comm/Read.h"
76 #include "comm/TcpAcceptor.h"
77 #include "comm/Write.h"
78 #include "CommCalls.h"
79 #include "errorpage.h"
80 #include "fd.h"
81 #include "fde.h"
82 #include "fqdncache.h"
83 #include "FwdState.h"
84 #include "globals.h"
85 #include "helper.h"
86 #include "helper/Reply.h"
87 #include "http.h"
88 #include "http/one/RequestParser.h"
89 #include "http/one/TeChunkedParser.h"
90 #include "HttpHdrContRange.h"
91 #include "HttpHeaderTools.h"
92 #include "HttpReply.h"
93 #include "HttpRequest.h"
94 #include "ident/Config.h"
95 #include "ident/Ident.h"
96 #include "internal.h"
97 #include "ipc/FdNotes.h"
98 #include "ipc/StartListening.h"
99 #include "log/access_log.h"
100 #include "MemBuf.h"
101 #include "MemObject.h"
102 #include "mime_header.h"
103 #include "parser/Tokenizer.h"
104 #include "profiler/Profiler.h"
105 #include "rfc1738.h"
106 #include "servers/forward.h"
107 #include "SquidConfig.h"
108 #include "SquidTime.h"
109 #include "StatCounters.h"
110 #include "StatHist.h"
111 #include "Store.h"
112 #include "TimeOrTag.h"
113 #include "tools.h"
114 #include "URL.h"
115
116 #if USE_AUTH
117 #include "auth/UserRequest.h"
118 #endif
119 #if USE_DELAY_POOLS
120 #include "ClientInfo.h"
121 #endif
122 #if USE_OPENSSL
123 #include "ssl/bio.h"
124 #include "ssl/context_storage.h"
125 #include "ssl/gadgets.h"
126 #include "ssl/helper.h"
127 #include "ssl/ProxyCerts.h"
128 #include "ssl/ServerBump.h"
129 #include "ssl/support.h"
130 #endif
131 #if USE_SSL_CRTD
132 #include "ssl/certificate_db.h"
133 #include "ssl/crtd_message.h"
134 #endif
135
136 // for tvSubUsec() which should be in SquidTime.h
137 #include "util.h"
138
139 #include <climits>
140 #include <cmath>
141 #include <limits>
142
143 #if LINGERING_CLOSE
144 #define comm_close comm_lingering_close
145 #endif
146
147 /// dials clientListenerConnectionOpened call
148 class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb
149 {
150 public:
151 typedef void (*Handler)(AnyP::PortCfgPointer &portCfg, const Ipc::FdNoteId note, const Subscription::Pointer &sub);
152 ListeningStartedDialer(Handler aHandler, AnyP::PortCfgPointer &aPortCfg, const Ipc::FdNoteId note, const Subscription::Pointer &aSub):
153 handler(aHandler), portCfg(aPortCfg), portTypeNote(note), sub(aSub) {}
154
155 virtual void print(std::ostream &os) const {
156 startPrint(os) <<
157 ", " << FdNote(portTypeNote) << " port=" << (void*)&portCfg << ')';
158 }
159
160 virtual bool canDial(AsyncCall &) const { return true; }
161 virtual void dial(AsyncCall &) { (handler)(portCfg, portTypeNote, sub); }
162
163 public:
164 Handler handler;
165
166 private:
167 AnyP::PortCfgPointer portCfg; ///< from HttpPortList
168 Ipc::FdNoteId portTypeNote; ///< Type of IPC socket being opened
169 Subscription::Pointer sub; ///< The handler to be subscribed for this connetion listener
170 };
171
172 static void clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub);
173
174 /* our socket-related context */
175
176 CBDATA_CLASS_INIT(ClientSocketContext);
177
178 /* Local functions */
179 static IOCB clientWriteComplete;
180 static IOCB clientWriteBodyComplete;
181 static IOACB httpAccept;
182 #if USE_OPENSSL
183 static IOACB httpsAccept;
184 #endif
185 static CTCB clientLifetimeTimeout;
186 #if USE_IDENT
187 static IDCB clientIdentDone;
188 #endif
189 static int clientIsContentLengthValid(HttpRequest * r);
190 static int clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength);
191
192 static void clientUpdateStatHistCounters(const LogTags &logType, int svc_time);
193 static void clientUpdateStatCounters(const LogTags &logType);
194 static void clientUpdateHierCounters(HierarchyLogEntry *);
195 static bool clientPingHasFinished(ping_data const *aPing);
196 void prepareLogWithRequestDetails(HttpRequest *, AccessLogEntry::Pointer &);
197 static void ClientSocketContextPushDeferredIfNeeded(ClientSocketContext::Pointer deferredRequest, ConnStateData * conn);
198 static void clientUpdateSocketStats(const LogTags &logType, size_t size);
199
200 char *skipLeadingSpace(char *aString);
201
202 clientStreamNode *
203 ClientSocketContext::getTail() const
204 {
205 if (http->client_stream.tail)
206 return (clientStreamNode *)http->client_stream.tail->data;
207
208 return NULL;
209 }
210
211 clientStreamNode *
212 ClientSocketContext::getClientReplyContext() const
213 {
214 return (clientStreamNode *)http->client_stream.tail->prev->data;
215 }
216
217 ConnStateData *
218 ClientSocketContext::getConn() const
219 {
220 return http->getConn();
221 }
222
223 void
224 ClientSocketContext::removeFromConnectionList(ConnStateData * conn)
225 {
226 ClientSocketContext::Pointer *tempContextPointer;
227 assert(conn != NULL && cbdataReferenceValid(conn));
228 assert(conn->getCurrentContext() != NULL);
229 /* Unlink us from the connection request list */
230 tempContextPointer = & conn->currentobject;
231
232 while (tempContextPointer->getRaw()) {
233 if (*tempContextPointer == this)
234 break;
235
236 tempContextPointer = &(*tempContextPointer)->next;
237 }
238
239 assert(tempContextPointer->getRaw() != NULL);
240 *tempContextPointer = next;
241 next = NULL;
242 }
243
244 ClientSocketContext::~ClientSocketContext()
245 {
246 clientStreamNode *node = getTail();
247
248 if (node) {
249 ClientSocketContext *streamContext = dynamic_cast<ClientSocketContext *> (node->data.getRaw());
250
251 if (streamContext) {
252 /* We are *always* the tail - prevent recursive free */
253 assert(this == streamContext);
254 node->data = NULL;
255 }
256 }
257
258 if (connRegistered_)
259 deRegisterWithConn();
260
261 httpRequestFree(http);
262
263 /* clean up connection links to us */
264 assert(this != next.getRaw());
265 }
266
267 void
268 ClientSocketContext::registerWithConn()
269 {
270 assert (!connRegistered_);
271 assert (http);
272 assert (http->getConn() != NULL);
273 connRegistered_ = true;
274 http->getConn()->pipeline.add(ClientSocketContext::Pointer(this));
275 }
276
277 void
278 ClientSocketContext::deRegisterWithConn()
279 {
280 assert (connRegistered_);
281 removeFromConnectionList(http->getConn());
282 connRegistered_ = false;
283 }
284
285 void
286 ClientSocketContext::connIsFinished()
287 {
288 assert (http);
289 assert (http->getConn() != NULL);
290 deRegisterWithConn();
291 /* we can't handle any more stream data - detach */
292 clientStreamDetach(getTail(), http);
293 }
294
295 ClientSocketContext::ClientSocketContext(const Comm::ConnectionPointer &aConn, ClientHttpRequest *aReq) :
296 clientConnection(aConn),
297 http(aReq),
298 reply(NULL),
299 next(NULL),
300 writtenToSocket(0),
301 mayUseConnection_ (false),
302 connRegistered_ (false)
303 {
304 assert(http != NULL);
305 memset (reqbuf, '\0', sizeof (reqbuf));
306 flags.deferred = 0;
307 flags.parsed_ok = 0;
308 deferredparams.node = NULL;
309 deferredparams.rep = NULL;
310 }
311
312 void
313 ClientSocketContext::writeControlMsg(HttpControlMsg &msg)
314 {
315 HttpReply::Pointer rep(msg.reply);
316 Must(rep != NULL);
317
318 // remember the callback
319 cbControlMsgSent = msg.cbSuccess;
320
321 AsyncCall::Pointer call = commCbCall(33, 5, "ClientSocketContext::wroteControlMsg",
322 CommIoCbPtrFun(&WroteControlMsg, this));
323
324 getConn()->writeControlMsgAndCall(this, rep.getRaw(), call);
325 }
326
327 /// called when we wrote the 1xx response
328 void
329 ClientSocketContext::wroteControlMsg(const Comm::ConnectionPointer &conn, char *, size_t, Comm::Flag errflag, int xerrno)
330 {
331 if (errflag == Comm::ERR_CLOSING)
332 return;
333
334 if (errflag == Comm::OK) {
335 ScheduleCallHere(cbControlMsgSent);
336 return;
337 }
338
339 debugs(33, 3, HERE << "1xx writing failed: " << xstrerr(xerrno));
340 // no error notification: see HttpControlMsg.h for rationale and
341 // note that some errors are detected elsewhere (e.g., close handler)
342
343 // close on 1xx errors to be conservative and to simplify the code
344 // (if we do not close, we must notify the source of a failure!)
345 conn->close();
346
347 // XXX: writeControlMsgAndCall() should handle writer-specific writing
348 // results, including errors and then call us with success/failure outcome.
349 }
350
351 /// wroteControlMsg() wrapper: ClientSocketContext is not an AsyncJob
352 void
353 ClientSocketContext::WroteControlMsg(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, Comm::Flag errflag, int xerrno, void *data)
354 {
355 ClientSocketContext *context = static_cast<ClientSocketContext*>(data);
356 context->wroteControlMsg(conn, bufnotused, size, errflag, xerrno);
357 }
358
359 #if USE_IDENT
360 static void
361 clientIdentDone(const char *ident, void *data)
362 {
363 ConnStateData *conn = (ConnStateData *)data;
364 xstrncpy(conn->clientConnection->rfc931, ident ? ident : dash_str, USER_IDENT_SZ);
365 }
366 #endif
367
368 void
369 clientUpdateStatCounters(const LogTags &logType)
370 {
371 ++statCounter.client_http.requests;
372
373 if (logType.isTcpHit())
374 ++statCounter.client_http.hits;
375
376 if (logType.oldType == LOG_TCP_HIT)
377 ++statCounter.client_http.disk_hits;
378 else if (logType.oldType == LOG_TCP_MEM_HIT)
379 ++statCounter.client_http.mem_hits;
380 }
381
382 void
383 clientUpdateStatHistCounters(const LogTags &logType, int svc_time)
384 {
385 statCounter.client_http.allSvcTime.count(svc_time);
386 /**
387 * The idea here is not to be complete, but to get service times
388 * for only well-defined types. For example, we don't include
389 * LOG_TCP_REFRESH_FAIL because its not really a cache hit
390 * (we *tried* to validate it, but failed).
391 */
392
393 switch (logType.oldType) {
394
395 case LOG_TCP_REFRESH_UNMODIFIED:
396 statCounter.client_http.nearHitSvcTime.count(svc_time);
397 break;
398
399 case LOG_TCP_IMS_HIT:
400 statCounter.client_http.nearMissSvcTime.count(svc_time);
401 break;
402
403 case LOG_TCP_HIT:
404
405 case LOG_TCP_MEM_HIT:
406
407 case LOG_TCP_OFFLINE_HIT:
408 statCounter.client_http.hitSvcTime.count(svc_time);
409 break;
410
411 case LOG_TCP_MISS:
412
413 case LOG_TCP_CLIENT_REFRESH_MISS:
414 statCounter.client_http.missSvcTime.count(svc_time);
415 break;
416
417 default:
418 /* make compiler warnings go away */
419 break;
420 }
421 }
422
423 bool
424 clientPingHasFinished(ping_data const *aPing)
425 {
426 if (0 != aPing->stop.tv_sec && 0 != aPing->start.tv_sec)
427 return true;
428
429 return false;
430 }
431
432 void
433 clientUpdateHierCounters(HierarchyLogEntry * someEntry)
434 {
435 ping_data *i;
436
437 switch (someEntry->code) {
438 #if USE_CACHE_DIGESTS
439
440 case CD_PARENT_HIT:
441
442 case CD_SIBLING_HIT:
443 ++ statCounter.cd.times_used;
444 break;
445 #endif
446
447 case SIBLING_HIT:
448
449 case PARENT_HIT:
450
451 case FIRST_PARENT_MISS:
452
453 case CLOSEST_PARENT_MISS:
454 ++ statCounter.icp.times_used;
455 i = &someEntry->ping;
456
457 if (clientPingHasFinished(i))
458 statCounter.icp.querySvcTime.count(tvSubUsec(i->start, i->stop));
459
460 if (i->timeout)
461 ++ statCounter.icp.query_timeouts;
462
463 break;
464
465 case CLOSEST_PARENT:
466
467 case CLOSEST_DIRECT:
468 ++ statCounter.netdb.times_used;
469
470 break;
471
472 default:
473 break;
474 }
475 }
476
477 void
478 ClientHttpRequest::updateCounters()
479 {
480 clientUpdateStatCounters(logType);
481
482 if (request->errType != ERR_NONE)
483 ++ statCounter.client_http.errors;
484
485 clientUpdateStatHistCounters(logType,
486 tvSubMsec(al->cache.start_time, current_time));
487
488 clientUpdateHierCounters(&request->hier);
489 }
490
491 void
492 prepareLogWithRequestDetails(HttpRequest * request, AccessLogEntry::Pointer &aLogEntry)
493 {
494 assert(request);
495 assert(aLogEntry != NULL);
496
497 if (Config.onoff.log_mime_hdrs) {
498 MemBuf mb;
499 mb.init();
500 request->header.packInto(&mb);
501 //This is the request after adaptation or redirection
502 aLogEntry->headers.adapted_request = xstrdup(mb.buf);
503
504 // the virgin request is saved to aLogEntry->request
505 if (aLogEntry->request) {
506 mb.reset();
507 aLogEntry->request->header.packInto(&mb);
508 aLogEntry->headers.request = xstrdup(mb.buf);
509 }
510
511 #if USE_ADAPTATION
512 const Adaptation::History::Pointer ah = request->adaptLogHistory();
513 if (ah != NULL) {
514 mb.reset();
515 ah->lastMeta.packInto(&mb);
516 aLogEntry->adapt.last_meta = xstrdup(mb.buf);
517 }
518 #endif
519
520 mb.clean();
521 }
522
523 #if ICAP_CLIENT
524 const Adaptation::Icap::History::Pointer ih = request->icapHistory();
525 if (ih != NULL)
526 ih->processingTime(aLogEntry->icap.processingTime);
527 #endif
528
529 aLogEntry->http.method = request->method;
530 aLogEntry->http.version = request->http_ver;
531 aLogEntry->hier = request->hier;
532 if (request->content_length > 0) // negative when no body or unknown length
533 aLogEntry->http.clientRequestSz.payloadData += request->content_length; // XXX: actually adaptedRequest payload size ??
534 aLogEntry->cache.extuser = request->extacl_user.termedBuf();
535
536 // Adapted request, if any, inherits and then collects all the stats, but
537 // the virgin request gets logged instead; copy the stats to log them.
538 // TODO: avoid losses by keeping these stats in a shared history object?
539 if (aLogEntry->request) {
540 aLogEntry->request->dnsWait = request->dnsWait;
541 aLogEntry->request->errType = request->errType;
542 aLogEntry->request->errDetail = request->errDetail;
543 }
544 }
545
546 void
547 ClientHttpRequest::logRequest()
548 {
549 if (!out.size && logType.oldType == LOG_TAG_NONE)
550 debugs(33, 5, "logging half-baked transaction: " << log_uri);
551
552 al->icp.opcode = ICP_INVALID;
553 al->url = log_uri;
554 debugs(33, 9, "clientLogRequest: al.url='" << al->url << "'");
555
556 if (al->reply) {
557 al->http.code = al->reply->sline.status();
558 al->http.content_type = al->reply->content_type.termedBuf();
559 } else if (loggingEntry() && loggingEntry()->mem_obj) {
560 al->http.code = loggingEntry()->mem_obj->getReply()->sline.status();
561 al->http.content_type = loggingEntry()->mem_obj->getReply()->content_type.termedBuf();
562 }
563
564 debugs(33, 9, "clientLogRequest: http.code='" << al->http.code << "'");
565
566 if (loggingEntry() && loggingEntry()->mem_obj && loggingEntry()->objectLen() >= 0)
567 al->cache.objectSize = loggingEntry()->contentLen(); // payload duplicate ?? with or without TE ?
568
569 al->http.clientRequestSz.header = req_sz;
570 al->http.clientReplySz.header = out.headers_sz;
571 // XXX: calculate without payload encoding or headers !!
572 al->http.clientReplySz.payloadData = out.size - out.headers_sz; // pretend its all un-encoded data for now.
573
574 al->cache.highOffset = out.offset;
575
576 al->cache.code = logType;
577
578 tvSub(al->cache.trTime, al->cache.start_time, current_time);
579
580 if (request)
581 prepareLogWithRequestDetails(request, al);
582
583 if (getConn() != NULL && getConn()->clientConnection != NULL && getConn()->clientConnection->rfc931[0])
584 al->cache.rfc931 = getConn()->clientConnection->rfc931;
585
586 #if USE_OPENSSL && 0
587
588 /* This is broken. Fails if the connection has been closed. Needs
589 * to snarf the ssl details some place earlier..
590 */
591 if (getConn() != NULL)
592 al->cache.ssluser = sslGetUserEmail(fd_table[getConn()->fd].ssl);
593
594 #endif
595
596 /* Add notes (if we have a request to annotate) */
597 if (request) {
598 // The al->notes and request->notes must point to the same object.
599 (void)SyncNotes(*al, *request);
600 for (auto i = Config.notes.begin(); i != Config.notes.end(); ++i) {
601 if (const char *value = (*i)->match(request, al->reply, NULL)) {
602 NotePairs &notes = SyncNotes(*al, *request);
603 notes.add((*i)->key.termedBuf(), value);
604 debugs(33, 3, (*i)->key.termedBuf() << " " << value);
605 }
606 }
607 }
608
609 ACLFilledChecklist checklist(NULL, request, NULL);
610 if (al->reply) {
611 checklist.reply = al->reply;
612 HTTPMSGLOCK(checklist.reply);
613 }
614
615 if (request) {
616 al->adapted_request = request;
617 HTTPMSGLOCK(al->adapted_request);
618 }
619 accessLogLog(al, &checklist);
620
621 bool updatePerformanceCounters = true;
622 if (Config.accessList.stats_collection) {
623 ACLFilledChecklist statsCheck(Config.accessList.stats_collection, request, NULL);
624 if (al->reply) {
625 statsCheck.reply = al->reply;
626 HTTPMSGLOCK(statsCheck.reply);
627 }
628 updatePerformanceCounters = (statsCheck.fastCheck() == ACCESS_ALLOWED);
629 }
630
631 if (updatePerformanceCounters) {
632 if (request)
633 updateCounters();
634
635 if (getConn() != NULL && getConn()->clientConnection != NULL)
636 clientdbUpdate(getConn()->clientConnection->remote, logType, AnyP::PROTO_HTTP, out.size);
637 }
638 }
639
640 void
641 ClientHttpRequest::freeResources()
642 {
643 safe_free(uri);
644 safe_free(log_uri);
645 safe_free(redirect.location);
646 range_iter.boundary.clean();
647 HTTPMSGUNLOCK(request);
648
649 if (client_stream.tail)
650 clientStreamAbort((clientStreamNode *)client_stream.tail->data, this);
651 }
652
653 void
654 httpRequestFree(void *data)
655 {
656 ClientHttpRequest *http = (ClientHttpRequest *)data;
657 assert(http != NULL);
658 delete http;
659 }
660
661 bool
662 ConnStateData::areAllContextsForThisConnection() const
663 {
664 ClientSocketContext::Pointer context = getCurrentContext();
665
666 while (context.getRaw()) {
667 if (context->http->getConn() != this)
668 return false;
669
670 context = context->next;
671 }
672
673 return true;
674 }
675
676 void
677 ConnStateData::freeAllContexts()
678 {
679 ClientSocketContext::Pointer context;
680
681 while ((context = getCurrentContext()).getRaw() != NULL) {
682 assert(getCurrentContext() !=
683 getCurrentContext()->next);
684 context->connIsFinished();
685 assert (context != currentobject);
686 }
687 }
688
689 /// propagates abort event to all contexts
690 void
691 ConnStateData::notifyAllContexts(int xerrno)
692 {
693 typedef ClientSocketContext::Pointer CSCP;
694 for (CSCP c = getCurrentContext(); c.getRaw(); c = c->next)
695 c->noteIoError(xerrno);
696 }
697
698 /* This is a handler normally called by comm_close() */
699 void ConnStateData::connStateClosed(const CommCloseCbParams &)
700 {
701 deleteThis("ConnStateData::connStateClosed");
702 }
703
704 #if USE_AUTH
705 void
706 ConnStateData::setAuth(const Auth::UserRequest::Pointer &aur, const char *by)
707 {
708 if (auth_ == NULL) {
709 if (aur != NULL) {
710 debugs(33, 2, "Adding connection-auth to " << clientConnection << " from " << by);
711 auth_ = aur;
712 }
713 return;
714 }
715
716 // clobered with self-pointer
717 // NP: something nasty is going on in Squid, but harmless.
718 if (aur == auth_) {
719 debugs(33, 2, "WARNING: Ignoring duplicate connection-auth for " << clientConnection << " from " << by);
720 return;
721 }
722
723 /*
724 * Connection-auth relies on a single set of credentials being preserved
725 * for all requests on a connection once they have been setup.
726 * There are several things which need to happen to preserve security
727 * when connection-auth credentials change unexpectedly or are unset.
728 *
729 * 1) auth helper released from any active state
730 *
731 * They can only be reserved by a handshake process which this
732 * connection can now never complete.
733 * This prevents helpers hanging when their connections close.
734 *
735 * 2) pinning is expected to be removed and server conn closed
736 *
737 * The upstream link is authenticated with the same credentials.
738 * Expecting the same level of consistency we should have received.
739 * This prevents upstream being faced with multiple or missing
740 * credentials after authentication.
741 * NP: un-pin is left to the cleanup in ConnStateData::swanSong()
742 * we just trigger that cleanup here via comm_reset_close() or
743 * ConnStateData::stopReceiving()
744 *
745 * 3) the connection needs to close.
746 *
747 * This prevents attackers injecting requests into a connection,
748 * or gateways wrongly multiplexing users into a single connection.
749 *
750 * When credentials are missing closure needs to follow an auth
751 * challenge for best recovery by the client.
752 *
753 * When credentials change there is nothing we can do but abort as
754 * fast as possible. Sending TCP RST instead of an HTTP response
755 * is the best-case action.
756 */
757
758 // clobbered with nul-pointer
759 if (aur == NULL) {
760 debugs(33, 2, "WARNING: Graceful closure on " << clientConnection << " due to connection-auth erase from " << by);
761 auth_->releaseAuthServer();
762 auth_ = NULL;
763 // XXX: need to test whether the connection re-auth challenge is sent. If not, how to trigger it from here.
764 // NP: the current situation seems to fix challenge loops in Safari without visible issues in others.
765 // we stop receiving more traffic but can leave the Job running to terminate after the error or challenge is delivered.
766 stopReceiving("connection-auth removed");
767 return;
768 }
769
770 // clobbered with alternative credentials
771 if (aur != auth_) {
772 debugs(33, 2, "ERROR: Closing " << clientConnection << " due to change of connection-auth from " << by);
773 auth_->releaseAuthServer();
774 auth_ = NULL;
775 // this is a fatal type of problem.
776 // Close the connection immediately with TCP RST to abort all traffic flow
777 comm_reset_close(clientConnection);
778 return;
779 }
780
781 /* NOT REACHABLE */
782 }
783 #endif
784
785 // cleans up before destructor is called
786 void
787 ConnStateData::swanSong()
788 {
789 debugs(33, 2, HERE << clientConnection);
790 flags.readMore = false;
791 DeregisterRunner(this);
792 clientdbEstablished(clientConnection->remote, -1); /* decrement */
793 assert(areAllContextsForThisConnection());
794 freeAllContexts();
795
796 unpinConnection(true);
797
798 Server::swanSong(); // closes the client connection
799
800 #if USE_AUTH
801 // NP: do this bit after closing the connections to avoid side effects from unwanted TCP RST
802 setAuth(NULL, "ConnStateData::SwanSong cleanup");
803 #endif
804
805 flags.swanSang = true;
806 }
807
808 bool
809 ConnStateData::isOpen() const
810 {
811 return cbdataReferenceValid(this) && // XXX: checking "this" in a method
812 Comm::IsConnOpen(clientConnection) &&
813 !fd_table[clientConnection->fd].closing();
814 }
815
816 ConnStateData::~ConnStateData()
817 {
818 debugs(33, 3, HERE << clientConnection);
819
820 if (isOpen())
821 debugs(33, DBG_IMPORTANT, "BUG: ConnStateData did not close " << clientConnection);
822
823 if (!flags.swanSang)
824 debugs(33, DBG_IMPORTANT, "BUG: ConnStateData was not destroyed properly; " << clientConnection);
825
826 if (bodyPipe != NULL)
827 stopProducingFor(bodyPipe, false);
828
829 delete bodyParser; // TODO: pool
830
831 #if USE_OPENSSL
832 delete sslServerBump;
833 #endif
834 }
835
836 /**
837 * clientSetKeepaliveFlag() sets request->flags.proxyKeepalive.
838 * This is the client-side persistent connection flag. We need
839 * to set this relatively early in the request processing
840 * to handle hacks for broken servers and clients.
841 */
842 void
843 clientSetKeepaliveFlag(ClientHttpRequest * http)
844 {
845 HttpRequest *request = http->request;
846
847 debugs(33, 3, "http_ver = " << request->http_ver);
848 debugs(33, 3, "method = " << request->method);
849
850 // TODO: move to HttpRequest::hdrCacheInit, just like HttpReply.
851 request->flags.proxyKeepalive = request->persistent();
852 }
853
854 /// checks body length of non-chunked requests
855 static int
856 clientIsContentLengthValid(HttpRequest * r)
857 {
858 // No Content-Length means this request just has no body, but conflicting
859 // Content-Lengths mean a message framing error (RFC 7230 Section 3.3.3 #4).
860 if (r->header.conflictingContentLength())
861 return 0;
862
863 switch (r->method.id()) {
864
865 case Http::METHOD_GET:
866
867 case Http::METHOD_HEAD:
868 /* We do not want to see a request entity on GET/HEAD requests */
869 return (r->content_length <= 0 || Config.onoff.request_entities);
870
871 default:
872 /* For other types of requests we don't care */
873 return 1;
874 }
875
876 /* NOT REACHED */
877 }
878
879 int
880 clientIsRequestBodyTooLargeForPolicy(int64_t bodyLength)
881 {
882 if (Config.maxRequestBodySize &&
883 bodyLength > Config.maxRequestBodySize)
884 return 1; /* too large */
885
886 return 0;
887 }
888
889 // careful: the "current" context may be gone if we wrote an early response
890 ClientSocketContext::Pointer
891 ConnStateData::getCurrentContext() const
892 {
893 return currentobject;
894 }
895
896 void
897 ClientSocketContext::deferRecipientForLater(clientStreamNode * node, HttpReply * rep, StoreIOBuffer receivedData)
898 {
899 debugs(33, 2, "clientSocketRecipient: Deferring request " << http->uri);
900 assert(flags.deferred == 0);
901 flags.deferred = 1;
902 deferredparams.node = node;
903 deferredparams.rep = rep;
904 deferredparams.queuedBuffer = receivedData;
905 return;
906 }
907
908 bool
909 ClientSocketContext::startOfOutput() const
910 {
911 return http->out.size == 0;
912 }
913
914 size_t
915 ClientSocketContext::lengthToSend(Range<int64_t> const &available)
916 {
917 /*the size of available range can always fit in a size_t type*/
918 size_t maximum = (size_t)available.size();
919
920 if (!http->request->range)
921 return maximum;
922
923 assert (canPackMoreRanges());
924
925 if (http->range_iter.debt() == -1)
926 return maximum;
927
928 assert (http->range_iter.debt() > 0);
929
930 /* TODO this + the last line could be a range intersection calculation */
931 if (available.start < http->range_iter.currentSpec()->offset)
932 return 0;
933
934 return min(http->range_iter.debt(), (int64_t)maximum);
935 }
936
937 void
938 ClientSocketContext::noteSentBodyBytes(size_t bytes)
939 {
940 debugs(33, 7, bytes << " body bytes");
941
942 http->out.offset += bytes;
943
944 if (!http->request->range)
945 return;
946
947 if (http->range_iter.debt() != -1) {
948 http->range_iter.debt(http->range_iter.debt() - bytes);
949 assert (http->range_iter.debt() >= 0);
950 }
951
952 /* debt() always stops at -1, below that is a bug */
953 assert (http->range_iter.debt() >= -1);
954 }
955
956 bool
957 ClientHttpRequest::multipartRangeRequest() const
958 {
959 return request->multipartRangeRequest();
960 }
961
962 bool
963 ClientSocketContext::multipartRangeRequest() const
964 {
965 return http->multipartRangeRequest();
966 }
967
968 void
969 ClientSocketContext::sendBody(HttpReply * rep, StoreIOBuffer bodyData)
970 {
971 assert(rep == NULL);
972
973 if (!multipartRangeRequest() && !http->request->flags.chunkedReply) {
974 size_t length = lengthToSend(bodyData.range());
975 noteSentBodyBytes (length);
976 AsyncCall::Pointer call = commCbCall(33, 5, "clientWriteBodyComplete",
977 CommIoCbPtrFun(clientWriteBodyComplete, this));
978 Comm::Write(clientConnection, bodyData.data, length, call, NULL);
979 return;
980 }
981
982 MemBuf mb;
983 mb.init();
984 if (multipartRangeRequest())
985 packRange(bodyData, &mb);
986 else
987 packChunk(bodyData, mb);
988
989 if (mb.contentSize()) {
990 /* write */
991 AsyncCall::Pointer call = commCbCall(33, 5, "clientWriteComplete",
992 CommIoCbPtrFun(clientWriteComplete, this));
993 Comm::Write(clientConnection, &mb, call);
994 } else
995 writeComplete(clientConnection, NULL, 0, Comm::OK);
996 }
997
998 /**
999 * Packs bodyData into mb using chunked encoding. Packs the last-chunk
1000 * if bodyData is empty.
1001 */
1002 void
1003 ClientSocketContext::packChunk(const StoreIOBuffer &bodyData, MemBuf &mb)
1004 {
1005 const uint64_t length =
1006 static_cast<uint64_t>(lengthToSend(bodyData.range()));
1007 noteSentBodyBytes(length);
1008
1009 mb.appendf("%" PRIX64 "\r\n", length);
1010 mb.append(bodyData.data, length);
1011 mb.append("\r\n", 2);
1012 }
1013
1014 /** put terminating boundary for multiparts */
1015 static void
1016 clientPackTermBound(String boundary, MemBuf * mb)
1017 {
1018 mb->appendf("\r\n--" SQUIDSTRINGPH "--\r\n", SQUIDSTRINGPRINT(boundary));
1019 debugs(33, 6, "clientPackTermBound: buf offset: " << mb->size);
1020 }
1021
1022 /** appends a "part" HTTP header (as in a multi-part/range reply) to the buffer */
1023 static void
1024 clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb)
1025 {
1026 HttpHeader hdr(hoReply);
1027 assert(rep);
1028 assert(spec);
1029
1030 /* put boundary */
1031 debugs(33, 5, "clientPackRangeHdr: appending boundary: " << boundary);
1032 /* rfc2046 requires to _prepend_ boundary with <crlf>! */
1033 mb->appendf("\r\n--" SQUIDSTRINGPH "\r\n", SQUIDSTRINGPRINT(boundary));
1034
1035 /* stuff the header with required entries and pack it */
1036
1037 if (rep->header.has(Http::HdrType::CONTENT_TYPE))
1038 hdr.putStr(Http::HdrType::CONTENT_TYPE, rep->header.getStr(Http::HdrType::CONTENT_TYPE));
1039
1040 httpHeaderAddContRange(&hdr, *spec, rep->content_length);
1041
1042 hdr.packInto(mb);
1043 hdr.clean();
1044
1045 /* append <crlf> (we packed a header, not a reply) */
1046 mb->append("\r\n", 2);
1047 }
1048
1049 /**
1050 * extracts a "range" from *buf and appends them to mb, updating
1051 * all offsets and such.
1052 */
1053 void
1054 ClientSocketContext::packRange(StoreIOBuffer const &source, MemBuf * mb)
1055 {
1056 HttpHdrRangeIter * i = &http->range_iter;
1057 Range<int64_t> available (source.range());
1058 char const *buf = source.data;
1059
1060 while (i->currentSpec() && available.size()) {
1061 const size_t copy_sz = lengthToSend(available);
1062
1063 if (copy_sz) {
1064 /*
1065 * intersection of "have" and "need" ranges must not be empty
1066 */
1067 assert(http->out.offset < i->currentSpec()->offset + i->currentSpec()->length);
1068 assert(http->out.offset + (int64_t)available.size() > i->currentSpec()->offset);
1069
1070 /*
1071 * put boundary and headers at the beginning of a range in a
1072 * multi-range
1073 */
1074
1075 if (http->multipartRangeRequest() && i->debt() == i->currentSpec()->length) {
1076 assert(http->memObject());
1077 clientPackRangeHdr(
1078 http->memObject()->getReply(), /* original reply */
1079 i->currentSpec(), /* current range */
1080 i->boundary, /* boundary, the same for all */
1081 mb);
1082 }
1083
1084 /*
1085 * append content
1086 */
1087 debugs(33, 3, "clientPackRange: appending " << copy_sz << " bytes");
1088
1089 noteSentBodyBytes (copy_sz);
1090
1091 mb->append(buf, copy_sz);
1092
1093 /*
1094 * update offsets
1095 */
1096 available.start += copy_sz;
1097
1098 buf += copy_sz;
1099
1100 }
1101
1102 if (!canPackMoreRanges()) {
1103 debugs(33, 3, "clientPackRange: Returning because !canPackMoreRanges.");
1104
1105 if (i->debt() == 0)
1106 /* put terminating boundary for multiparts */
1107 clientPackTermBound(i->boundary, mb);
1108
1109 return;
1110 }
1111
1112 int64_t nextOffset = getNextRangeOffset();
1113
1114 assert (nextOffset >= http->out.offset);
1115
1116 int64_t skip = nextOffset - http->out.offset;
1117
1118 /* adjust for not to be transmitted bytes */
1119 http->out.offset = nextOffset;
1120
1121 if (available.size() <= (uint64_t)skip)
1122 return;
1123
1124 available.start += skip;
1125
1126 buf += skip;
1127
1128 if (copy_sz == 0)
1129 return;
1130 }
1131 }
1132
1133 /** returns expected content length for multi-range replies
1134 * note: assumes that httpHdrRangeCanonize has already been called
1135 * warning: assumes that HTTP headers for individual ranges at the
1136 * time of the actuall assembly will be exactly the same as
1137 * the headers when clientMRangeCLen() is called */
1138 int
1139 ClientHttpRequest::mRangeCLen()
1140 {
1141 int64_t clen = 0;
1142 MemBuf mb;
1143
1144 assert(memObject());
1145
1146 mb.init();
1147 HttpHdrRange::iterator pos = request->range->begin();
1148
1149 while (pos != request->range->end()) {
1150 /* account for headers for this range */
1151 mb.reset();
1152 clientPackRangeHdr(memObject()->getReply(),
1153 *pos, range_iter.boundary, &mb);
1154 clen += mb.size;
1155
1156 /* account for range content */
1157 clen += (*pos)->length;
1158
1159 debugs(33, 6, "clientMRangeCLen: (clen += " << mb.size << " + " << (*pos)->length << ") == " << clen);
1160 ++pos;
1161 }
1162
1163 /* account for the terminating boundary */
1164 mb.reset();
1165
1166 clientPackTermBound(range_iter.boundary, &mb);
1167
1168 clen += mb.size;
1169
1170 mb.clean();
1171
1172 return clen;
1173 }
1174
1175 /**
1176 * returns true if If-Range specs match reply, false otherwise
1177 */
1178 static int
1179 clientIfRangeMatch(ClientHttpRequest * http, HttpReply * rep)
1180 {
1181 const TimeOrTag spec = http->request->header.getTimeOrTag(Http::HdrType::IF_RANGE);
1182 /* check for parsing falure */
1183
1184 if (!spec.valid)
1185 return 0;
1186
1187 /* got an ETag? */
1188 if (spec.tag.str) {
1189 ETag rep_tag = rep->header.getETag(Http::HdrType::ETAG);
1190 debugs(33, 3, "clientIfRangeMatch: ETags: " << spec.tag.str << " and " <<
1191 (rep_tag.str ? rep_tag.str : "<none>"));
1192
1193 if (!rep_tag.str)
1194 return 0; /* entity has no etag to compare with! */
1195
1196 if (spec.tag.weak || rep_tag.weak) {
1197 debugs(33, DBG_IMPORTANT, "clientIfRangeMatch: Weak ETags are not allowed in If-Range: " << spec.tag.str << " ? " << rep_tag.str);
1198 return 0; /* must use strong validator for sub-range requests */
1199 }
1200
1201 return etagIsStrongEqual(rep_tag, spec.tag);
1202 }
1203
1204 /* got modification time? */
1205 if (spec.time >= 0) {
1206 return http->storeEntry()->lastmod <= spec.time;
1207 }
1208
1209 assert(0); /* should not happen */
1210 return 0;
1211 }
1212
1213 /**
1214 * generates a "unique" boundary string for multipart responses
1215 * the caller is responsible for cleaning the string */
1216 String
1217 ClientHttpRequest::rangeBoundaryStr() const
1218 {
1219 const char *key;
1220 String b(APP_FULLNAME);
1221 b.append(":",1);
1222 key = storeEntry()->getMD5Text();
1223 b.append(key, strlen(key));
1224 return b;
1225 }
1226
1227 /** adds appropriate Range headers if needed */
1228 void
1229 ClientSocketContext::buildRangeHeader(HttpReply * rep)
1230 {
1231 HttpHeader *hdr = rep ? &rep->header : 0;
1232 const char *range_err = NULL;
1233 HttpRequest *request = http->request;
1234 assert(request->range);
1235 /* check if we still want to do ranges */
1236
1237 int64_t roffLimit = request->getRangeOffsetLimit();
1238
1239 if (!rep)
1240 range_err = "no [parse-able] reply";
1241 else if ((rep->sline.status() != Http::scOkay) && (rep->sline.status() != Http::scPartialContent))
1242 range_err = "wrong status code";
1243 else if (hdr->has(Http::HdrType::CONTENT_RANGE))
1244 range_err = "origin server does ranges";
1245 else if (rep->content_length < 0)
1246 range_err = "unknown length";
1247 else if (rep->content_length != http->memObject()->getReply()->content_length)
1248 range_err = "INCONSISTENT length"; /* a bug? */
1249
1250 /* hits only - upstream CachePeer determines correct behaviour on misses, and client_side_reply determines
1251 * hits candidates
1252 */
1253 else if (http->logType.isTcpHit() && http->request->header.has(Http::HdrType::IF_RANGE) && !clientIfRangeMatch(http, rep))
1254 range_err = "If-Range match failed";
1255 else if (!http->request->range->canonize(rep))
1256 range_err = "canonization failed";
1257 else if (http->request->range->isComplex())
1258 range_err = "too complex range header";
1259 else if (!http->logType.isTcpHit() && http->request->range->offsetLimitExceeded(roffLimit))
1260 range_err = "range outside range_offset_limit";
1261
1262 /* get rid of our range specs on error */
1263 if (range_err) {
1264 /* XXX We do this here because we need canonisation etc. However, this current
1265 * code will lead to incorrect store offset requests - the store will have the
1266 * offset data, but we won't be requesting it.
1267 * So, we can either re-request, or generate an error
1268 */
1269 http->request->ignoreRange(range_err);
1270 } else {
1271 /* XXX: TODO: Review, this unconditional set may be wrong. */
1272 rep->sline.set(rep->sline.version, Http::scPartialContent);
1273 // web server responded with a valid, but unexpected range.
1274 // will (try-to) forward as-is.
1275 //TODO: we should cope with multirange request/responses
1276 bool replyMatchRequest = rep->content_range != NULL ?
1277 request->range->contains(rep->content_range->spec) :
1278 true;
1279 const int spec_count = http->request->range->specs.size();
1280 int64_t actual_clen = -1;
1281
1282 debugs(33, 3, "clientBuildRangeHeader: range spec count: " <<
1283 spec_count << " virgin clen: " << rep->content_length);
1284 assert(spec_count > 0);
1285 /* append appropriate header(s) */
1286
1287 if (spec_count == 1) {
1288 if (!replyMatchRequest) {
1289 hdr->delById(Http::HdrType::CONTENT_RANGE);
1290 hdr->putContRange(rep->content_range);
1291 actual_clen = rep->content_length;
1292 //http->range_iter.pos = rep->content_range->spec.begin();
1293 (*http->range_iter.pos)->offset = rep->content_range->spec.offset;
1294 (*http->range_iter.pos)->length = rep->content_range->spec.length;
1295
1296 } else {
1297 HttpHdrRange::iterator pos = http->request->range->begin();
1298 assert(*pos);
1299 /* append Content-Range */
1300
1301 if (!hdr->has(Http::HdrType::CONTENT_RANGE)) {
1302 /* No content range, so this was a full object we are
1303 * sending parts of.
1304 */
1305 httpHeaderAddContRange(hdr, **pos, rep->content_length);
1306 }
1307
1308 /* set new Content-Length to the actual number of bytes
1309 * transmitted in the message-body */
1310 actual_clen = (*pos)->length;
1311 }
1312 } else {
1313 /* multipart! */
1314 /* generate boundary string */
1315 http->range_iter.boundary = http->rangeBoundaryStr();
1316 /* delete old Content-Type, add ours */
1317 hdr->delById(Http::HdrType::CONTENT_TYPE);
1318 httpHeaderPutStrf(hdr, Http::HdrType::CONTENT_TYPE,
1319 "multipart/byteranges; boundary=\"" SQUIDSTRINGPH "\"",
1320 SQUIDSTRINGPRINT(http->range_iter.boundary));
1321 /* Content-Length is not required in multipart responses
1322 * but it is always nice to have one */
1323 actual_clen = http->mRangeCLen();
1324 /* http->out needs to start where we want data at */
1325 http->out.offset = http->range_iter.currentSpec()->offset;
1326 }
1327
1328 /* replace Content-Length header */
1329 assert(actual_clen >= 0);
1330
1331 hdr->delById(Http::HdrType::CONTENT_LENGTH);
1332
1333 hdr->putInt64(Http::HdrType::CONTENT_LENGTH, actual_clen);
1334
1335 debugs(33, 3, "clientBuildRangeHeader: actual content length: " << actual_clen);
1336
1337 /* And start the range iter off */
1338 http->range_iter.updateSpec();
1339 }
1340 }
1341
1342 void
1343 ClientSocketContext::prepareReply(HttpReply * rep)
1344 {
1345 reply = rep;
1346
1347 if (http->request->range)
1348 buildRangeHeader(rep);
1349 }
1350
1351 void
1352 ClientSocketContext::sendStartOfMessage(HttpReply * rep, StoreIOBuffer bodyData)
1353 {
1354 prepareReply(rep);
1355 assert (rep);
1356 MemBuf *mb = rep->pack();
1357
1358 // dump now, so we dont output any body.
1359 debugs(11, 2, "HTTP Client " << clientConnection);
1360 debugs(11, 2, "HTTP Client REPLY:\n---------\n" << mb->buf << "\n----------");
1361
1362 /* Save length of headers for persistent conn checks */
1363 http->out.headers_sz = mb->contentSize();
1364 #if HEADERS_LOG
1365
1366 headersLog(0, 0, http->request->method, rep);
1367 #endif
1368
1369 if (bodyData.data && bodyData.length) {
1370 if (multipartRangeRequest())
1371 packRange(bodyData, mb);
1372 else if (http->request->flags.chunkedReply) {
1373 packChunk(bodyData, *mb);
1374 } else {
1375 size_t length = lengthToSend(bodyData.range());
1376 noteSentBodyBytes (length);
1377
1378 mb->append(bodyData.data, length);
1379 }
1380 }
1381
1382 /* write */
1383 debugs(33,7, HERE << "sendStartOfMessage schedules clientWriteComplete");
1384 AsyncCall::Pointer call = commCbCall(33, 5, "clientWriteComplete",
1385 CommIoCbPtrFun(clientWriteComplete, this));
1386 Comm::Write(clientConnection, mb, call);
1387 delete mb;
1388 }
1389
1390 /**
1391 * Write a chunk of data to a client socket. If the reply is present,
1392 * send the reply headers down the wire too, and clean them up when
1393 * finished.
1394 * Pre-condition:
1395 * The request is one backed by a connection, not an internal request.
1396 * data context is not NULL
1397 * There are no more entries in the stream chain.
1398 */
1399 void
1400 clientSocketRecipient(clientStreamNode * node, ClientHttpRequest * http,
1401 HttpReply * rep, StoreIOBuffer receivedData)
1402 {
1403 // dont tryt to deliver if client already ABORTED
1404 if (!http->getConn() || !cbdataReferenceValid(http->getConn()) || !Comm::IsConnOpen(http->getConn()->clientConnection))
1405 return;
1406
1407 /* Test preconditions */
1408 assert(node != NULL);
1409 PROF_start(clientSocketRecipient);
1410 /* TODO: handle this rather than asserting
1411 * - it should only ever happen if we cause an abort and
1412 * the callback chain loops back to here, so we can simply return.
1413 * However, that itself shouldn't happen, so it stays as an assert for now.
1414 */
1415 assert(cbdataReferenceValid(node));
1416 assert(node->node.next == NULL);
1417 ClientSocketContext::Pointer context = dynamic_cast<ClientSocketContext *>(node->data.getRaw());
1418 assert(context != NULL);
1419
1420 /* TODO: check offset is what we asked for */
1421
1422 if (context != http->getConn()->getCurrentContext())
1423 context->deferRecipientForLater(node, rep, receivedData);
1424 else
1425 http->getConn()->handleReply(rep, receivedData);
1426
1427 PROF_stop(clientSocketRecipient);
1428 }
1429
1430 /**
1431 * Called when a downstream node is no longer interested in
1432 * our data. As we are a terminal node, this means on aborts
1433 * only
1434 */
1435 void
1436 clientSocketDetach(clientStreamNode * node, ClientHttpRequest * http)
1437 {
1438 /* Test preconditions */
1439 assert(node != NULL);
1440 /* TODO: handle this rather than asserting
1441 * - it should only ever happen if we cause an abort and
1442 * the callback chain loops back to here, so we can simply return.
1443 * However, that itself shouldn't happen, so it stays as an assert for now.
1444 */
1445 assert(cbdataReferenceValid(node));
1446 /* Set null by ContextFree */
1447 assert(node->node.next == NULL);
1448 /* this is the assert discussed above */
1449 assert(NULL == dynamic_cast<ClientSocketContext *>(node->data.getRaw()));
1450 /* We are only called when the client socket shutsdown.
1451 * Tell the prev pipeline member we're finished
1452 */
1453 clientStreamDetach(node, http);
1454 }
1455
1456 static void
1457 clientWriteBodyComplete(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag errflag, int xerrno, void *data)
1458 {
1459 debugs(33,7, "schedule clientWriteComplete");
1460 clientWriteComplete(conn, NULL, size, errflag, xerrno, data);
1461 }
1462
1463 void
1464 ConnStateData::readNextRequest()
1465 {
1466 debugs(33, 5, HERE << clientConnection << " reading next req");
1467
1468 fd_note(clientConnection->fd, "Idle client: Waiting for next request");
1469 /**
1470 * Set the timeout BEFORE calling readSomeData().
1471 */
1472 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
1473 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
1474 TimeoutDialer, this, ConnStateData::requestTimeout);
1475 commSetConnTimeout(clientConnection, clientConnection->timeLeft(idleTimeout()), timeoutCall);
1476
1477 readSomeData();
1478 /** Please don't do anything with the FD past here! */
1479 }
1480
1481 static void
1482 ClientSocketContextPushDeferredIfNeeded(ClientSocketContext::Pointer deferredRequest, ConnStateData * conn)
1483 {
1484 debugs(33, 2, HERE << conn->clientConnection << " Sending next");
1485
1486 /** If the client stream is waiting on a socket write to occur, then */
1487
1488 if (deferredRequest->flags.deferred) {
1489 /** NO data is allowed to have been sent. */
1490 assert(deferredRequest->http->out.size == 0);
1491 /** defer now. */
1492 clientSocketRecipient(deferredRequest->deferredparams.node,
1493 deferredRequest->http,
1494 deferredRequest->deferredparams.rep,
1495 deferredRequest->deferredparams.queuedBuffer);
1496 }
1497
1498 /** otherwise, the request is still active in a callbacksomewhere,
1499 * and we are done
1500 */
1501 }
1502
1503 /// called when we have successfully finished writing the response
1504 void
1505 ClientSocketContext::keepaliveNextRequest()
1506 {
1507 ConnStateData * conn = http->getConn();
1508
1509 debugs(33, 3, HERE << "ConnnStateData(" << conn->clientConnection << "), Context(" << clientConnection << ")");
1510 connIsFinished();
1511
1512 if (conn->pinning.pinned && !Comm::IsConnOpen(conn->pinning.serverConnection)) {
1513 debugs(33, 2, HERE << conn->clientConnection << " Connection was pinned but server side gone. Terminating client connection");
1514 conn->clientConnection->close();
1515 return;
1516 }
1517
1518 /** \par
1519 * We are done with the response, and we are either still receiving request
1520 * body (early response!) or have already stopped receiving anything.
1521 *
1522 * If we are still receiving, then clientParseRequest() below will fail.
1523 * (XXX: but then we will call readNextRequest() which may succeed and
1524 * execute a smuggled request as we are not done with the current request).
1525 *
1526 * If we stopped because we got everything, then try the next request.
1527 *
1528 * If we stopped receiving because of an error, then close now to avoid
1529 * getting stuck and to prevent accidental request smuggling.
1530 */
1531
1532 if (const char *reason = conn->stoppedReceiving()) {
1533 debugs(33, 3, HERE << "closing for earlier request error: " << reason);
1534 conn->clientConnection->close();
1535 return;
1536 }
1537
1538 /** \par
1539 * Attempt to parse a request from the request buffer.
1540 * If we've been fed a pipelined request it may already
1541 * be in our read buffer.
1542 *
1543 \par
1544 * This needs to fall through - if we're unlucky and parse the _last_ request
1545 * from our read buffer we may never re-register for another client read.
1546 */
1547
1548 if (conn->clientParseRequests()) {
1549 debugs(33, 3, HERE << conn->clientConnection << ": parsed next request from buffer");
1550 }
1551
1552 /** \par
1553 * Either we need to kick-start another read or, if we have
1554 * a half-closed connection, kill it after the last request.
1555 * This saves waiting for half-closed connections to finished being
1556 * half-closed _AND_ then, sometimes, spending "Timeout" time in
1557 * the keepalive "Waiting for next request" state.
1558 */
1559 if (commIsHalfClosed(conn->clientConnection->fd) && (conn->getConcurrentRequestCount() == 0)) {
1560 debugs(33, 3, "ClientSocketContext::keepaliveNextRequest: half-closed client with no pending requests, closing");
1561 conn->clientConnection->close();
1562 return;
1563 }
1564
1565 ClientSocketContext::Pointer deferredRequest;
1566
1567 /** \par
1568 * At this point we either have a parsed request (which we've
1569 * kicked off the processing for) or not. If we have a deferred
1570 * request (parsed but deferred for pipeling processing reasons)
1571 * then look at processing it. If not, simply kickstart
1572 * another read.
1573 */
1574
1575 if ((deferredRequest = conn->getCurrentContext()).getRaw()) {
1576 debugs(33, 3, HERE << conn->clientConnection << ": calling PushDeferredIfNeeded");
1577 ClientSocketContextPushDeferredIfNeeded(deferredRequest, conn);
1578 } else if (conn->flags.readMore) {
1579 debugs(33, 3, HERE << conn->clientConnection << ": calling conn->readNextRequest()");
1580 conn->readNextRequest();
1581 } else {
1582 // XXX: Can this happen? CONNECT tunnels have deferredRequest set.
1583 debugs(33, DBG_IMPORTANT, HERE << "abandoning " << conn->clientConnection);
1584 }
1585 }
1586
1587 void
1588 clientUpdateSocketStats(const LogTags &logType, size_t size)
1589 {
1590 if (size == 0)
1591 return;
1592
1593 statCounter.client_http.kbytes_out += size;
1594
1595 if (logType.isTcpHit())
1596 statCounter.client_http.hit_kbytes_out += size;
1597 }
1598
1599 /**
1600 * increments iterator "i"
1601 * used by clientPackMoreRanges
1602 *
1603 \retval true there is still data available to pack more ranges
1604 \retval false
1605 */
1606 bool
1607 ClientSocketContext::canPackMoreRanges() const
1608 {
1609 /** first update iterator "i" if needed */
1610
1611 if (!http->range_iter.debt()) {
1612 debugs(33, 5, HERE << "At end of current range spec for " << clientConnection);
1613
1614 if (http->range_iter.pos != http->range_iter.end)
1615 ++http->range_iter.pos;
1616
1617 http->range_iter.updateSpec();
1618 }
1619
1620 assert(!http->range_iter.debt() == !http->range_iter.currentSpec());
1621
1622 /* paranoid sync condition */
1623 /* continue condition: need_more_data */
1624 debugs(33, 5, "ClientSocketContext::canPackMoreRanges: returning " << (http->range_iter.currentSpec() ? true : false));
1625 return http->range_iter.currentSpec() ? true : false;
1626 }
1627
1628 int64_t
1629 ClientSocketContext::getNextRangeOffset() const
1630 {
1631 debugs (33, 5, "range: " << http->request->range <<
1632 "; http offset " << http->out.offset <<
1633 "; reply " << reply);
1634
1635 // XXX: This method is called from many places, including pullData() which
1636 // may be called before prepareReply() [on some Squid-generated errors].
1637 // Hence, we may not even know yet whether we should honor/do ranges.
1638
1639 if (http->request->range) {
1640 /* offset in range specs does not count the prefix of an http msg */
1641 /* check: reply was parsed and range iterator was initialized */
1642 assert(http->range_iter.valid);
1643 /* filter out data according to range specs */
1644 assert (canPackMoreRanges());
1645 {
1646 int64_t start; /* offset of still missing data */
1647 assert(http->range_iter.currentSpec());
1648 start = http->range_iter.currentSpec()->offset + http->range_iter.currentSpec()->length - http->range_iter.debt();
1649 debugs(33, 3, "clientPackMoreRanges: in: offset: " << http->out.offset);
1650 debugs(33, 3, "clientPackMoreRanges: out:"
1651 " start: " << start <<
1652 " spec[" << http->range_iter.pos - http->request->range->begin() << "]:" <<
1653 " [" << http->range_iter.currentSpec()->offset <<
1654 ", " << http->range_iter.currentSpec()->offset + http->range_iter.currentSpec()->length << "),"
1655 " len: " << http->range_iter.currentSpec()->length <<
1656 " debt: " << http->range_iter.debt());
1657 if (http->range_iter.currentSpec()->length != -1)
1658 assert(http->out.offset <= start); /* we did not miss it */
1659
1660 return start;
1661 }
1662
1663 } else if (reply && reply->content_range) {
1664 /* request does not have ranges, but reply does */
1665 /** \todo FIXME: should use range_iter_pos on reply, as soon as reply->content_range
1666 * becomes HttpHdrRange rather than HttpHdrRangeSpec.
1667 */
1668 return http->out.offset + reply->content_range->spec.offset;
1669 }
1670
1671 return http->out.offset;
1672 }
1673
1674 void
1675 ClientSocketContext::pullData()
1676 {
1677 debugs(33, 5, reply << " written " << http->out.size << " into " << clientConnection);
1678
1679 /* More data will be coming from the stream. */
1680 StoreIOBuffer readBuffer;
1681 /* XXX: Next requested byte in the range sequence */
1682 /* XXX: length = getmaximumrangelenfgth */
1683 readBuffer.offset = getNextRangeOffset();
1684 readBuffer.length = HTTP_REQBUF_SZ;
1685 readBuffer.data = reqbuf;
1686 /* we may note we have reached the end of the wanted ranges */
1687 clientStreamRead(getTail(), http, readBuffer);
1688 }
1689
1690 /** Adapt stream status to account for Range cases
1691 *
1692 */
1693 clientStream_status_t
1694 ClientSocketContext::socketState()
1695 {
1696 switch (clientStreamStatus(getTail(), http)) {
1697
1698 case STREAM_NONE:
1699 /* check for range support ending */
1700
1701 if (http->request->range) {
1702 /* check: reply was parsed and range iterator was initialized */
1703 assert(http->range_iter.valid);
1704 /* filter out data according to range specs */
1705
1706 if (!canPackMoreRanges()) {
1707 debugs(33, 5, HERE << "Range request at end of returnable " <<
1708 "range sequence on " << clientConnection);
1709 // we got everything we wanted from the store
1710 return STREAM_COMPLETE;
1711 }
1712 } else if (reply && reply->content_range) {
1713 /* reply has content-range, but Squid is not managing ranges */
1714 const int64_t &bytesSent = http->out.offset;
1715 const int64_t &bytesExpected = reply->content_range->spec.length;
1716
1717 debugs(33, 7, HERE << "body bytes sent vs. expected: " <<
1718 bytesSent << " ? " << bytesExpected << " (+" <<
1719 reply->content_range->spec.offset << ")");
1720
1721 // did we get at least what we expected, based on range specs?
1722
1723 if (bytesSent == bytesExpected) // got everything
1724 return STREAM_COMPLETE;
1725
1726 if (bytesSent > bytesExpected) // Error: Sent more than expected
1727 return STREAM_UNPLANNED_COMPLETE;
1728 }
1729
1730 return STREAM_NONE;
1731
1732 case STREAM_COMPLETE:
1733 return STREAM_COMPLETE;
1734
1735 case STREAM_UNPLANNED_COMPLETE:
1736 return STREAM_UNPLANNED_COMPLETE;
1737
1738 case STREAM_FAILED:
1739 return STREAM_FAILED;
1740 }
1741
1742 fatal ("unreachable code\n");
1743 return STREAM_NONE;
1744 }
1745
1746 /**
1747 * A write has just completed to the client, or we have just realised there is
1748 * no more data to send.
1749 */
1750 void
1751 clientWriteComplete(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, Comm::Flag errflag, int, void *data)
1752 {
1753 ClientSocketContext *context = (ClientSocketContext *)data;
1754 context->writeComplete(conn, bufnotused, size, errflag);
1755 }
1756
1757 /// remembers the abnormal connection termination for logging purposes
1758 void
1759 ClientSocketContext::noteIoError(const int xerrno)
1760 {
1761 if (http) {
1762 http->logType.err.timedout = (xerrno == ETIMEDOUT);
1763 // aborted even if xerrno is zero (which means read abort/eof)
1764 http->logType.err.aborted = (xerrno != ETIMEDOUT);
1765 }
1766 }
1767
1768 void
1769 ClientSocketContext::doClose()
1770 {
1771 clientConnection->close();
1772 }
1773
1774 /// called when we encounter a response-related error
1775 void
1776 ClientSocketContext::initiateClose(const char *reason)
1777 {
1778 http->getConn()->stopSending(reason); // closes ASAP
1779 }
1780
1781 void
1782 ConnStateData::stopSending(const char *error)
1783 {
1784 debugs(33, 4, HERE << "sending error (" << clientConnection << "): " << error <<
1785 "; old receiving error: " <<
1786 (stoppedReceiving() ? stoppedReceiving_ : "none"));
1787
1788 if (const char *oldError = stoppedSending()) {
1789 debugs(33, 3, HERE << "already stopped sending: " << oldError);
1790 return; // nothing has changed as far as this connection is concerned
1791 }
1792 stoppedSending_ = error;
1793
1794 if (!stoppedReceiving()) {
1795 if (const int64_t expecting = mayNeedToReadMoreBody()) {
1796 debugs(33, 5, HERE << "must still read " << expecting <<
1797 " request body bytes with " << inBuf.length() << " unused");
1798 return; // wait for the request receiver to finish reading
1799 }
1800 }
1801
1802 clientConnection->close();
1803 }
1804
1805 void
1806 ClientSocketContext::writeComplete(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag errflag)
1807 {
1808 const StoreEntry *entry = http->storeEntry();
1809 http->out.size += size;
1810 debugs(33, 5, HERE << conn << ", sz " << size <<
1811 ", err " << errflag << ", off " << http->out.size << ", len " <<
1812 (entry ? entry->objectLen() : 0));
1813 clientUpdateSocketStats(http->logType, size);
1814
1815 /* Bail out quickly on Comm::ERR_CLOSING - close handlers will tidy up */
1816
1817 if (errflag == Comm::ERR_CLOSING || !Comm::IsConnOpen(conn))
1818 return;
1819
1820 if (errflag || clientHttpRequestStatus(conn->fd, http)) {
1821 initiateClose("failure or true request status");
1822 /* Do we leak here ? */
1823 return;
1824 }
1825
1826 switch (socketState()) {
1827
1828 case STREAM_NONE:
1829 pullData();
1830 break;
1831
1832 case STREAM_COMPLETE:
1833 debugs(33, 5, conn << " Stream complete, keepalive is " << http->request->flags.proxyKeepalive);
1834 if (http->request->flags.proxyKeepalive)
1835 keepaliveNextRequest();
1836 else
1837 initiateClose("STREAM_COMPLETE NOKEEPALIVE");
1838 return;
1839
1840 case STREAM_UNPLANNED_COMPLETE:
1841 initiateClose("STREAM_UNPLANNED_COMPLETE");
1842 return;
1843
1844 case STREAM_FAILED:
1845 initiateClose("STREAM_FAILED");
1846 return;
1847
1848 default:
1849 fatal("Hit unreachable code in clientWriteComplete\n");
1850 }
1851 }
1852
1853 ClientSocketContext *
1854 ConnStateData::abortRequestParsing(const char *const uri)
1855 {
1856 ClientHttpRequest *http = new ClientHttpRequest(this);
1857 http->req_sz = inBuf.length();
1858 http->uri = xstrdup(uri);
1859 setLogUri (http, uri);
1860 ClientSocketContext *context = new ClientSocketContext(clientConnection, http);
1861 StoreIOBuffer tempBuffer;
1862 tempBuffer.data = context->reqbuf;
1863 tempBuffer.length = HTTP_REQBUF_SZ;
1864 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
1865 clientReplyStatus, new clientReplyContext(http), clientSocketRecipient,
1866 clientSocketDetach, context, tempBuffer);
1867 return context;
1868 }
1869
1870 void
1871 ConnStateData::startShutdown()
1872 {
1873 // RegisteredRunner API callback - Squid has been shut down
1874
1875 // if connection is idle terminate it now,
1876 // otherwise wait for grace period to end
1877 if (getConcurrentRequestCount() == 0)
1878 endingShutdown();
1879 }
1880
1881 void
1882 ConnStateData::endingShutdown()
1883 {
1884 // RegisteredRunner API callback - Squid shutdown grace period is over
1885
1886 // force the client connection to close immediately
1887 // swanSong() in the close handler will cleanup.
1888 if (Comm::IsConnOpen(clientConnection))
1889 clientConnection->close();
1890
1891 // deregister now to ensure finalShutdown() does not kill us prematurely.
1892 // fd_table purge will cleanup if close handler was not fast enough.
1893 DeregisterRunner(this);
1894 }
1895
1896 char *
1897 skipLeadingSpace(char *aString)
1898 {
1899 char *result = aString;
1900
1901 while (xisspace(*aString))
1902 ++aString;
1903
1904 return result;
1905 }
1906
1907 /**
1908 * 'end' defaults to NULL for backwards compatibility
1909 * remove default value if we ever get rid of NULL-terminated
1910 * request buffers.
1911 */
1912 const char *
1913 findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end)
1914 {
1915 if (NULL == end) {
1916 end = uriAndHTTPVersion + strcspn(uriAndHTTPVersion, "\r\n");
1917 assert(end);
1918 }
1919
1920 for (; end > uriAndHTTPVersion; --end) {
1921 if (*end == '\n' || *end == '\r')
1922 continue;
1923
1924 if (xisspace(*end)) {
1925 if (strncasecmp(end + 1, "HTTP/", 5) == 0)
1926 return end + 1;
1927 else
1928 break;
1929 }
1930 }
1931
1932 return NULL;
1933 }
1934
1935 void
1936 setLogUri(ClientHttpRequest * http, char const *uri, bool cleanUrl)
1937 {
1938 safe_free(http->log_uri);
1939
1940 if (!cleanUrl)
1941 // The uri is already clean just dump it.
1942 http->log_uri = xstrndup(uri, MAX_URL);
1943 else {
1944 int flags = 0;
1945 switch (Config.uri_whitespace) {
1946 case URI_WHITESPACE_ALLOW:
1947 flags |= RFC1738_ESCAPE_NOSPACE;
1948
1949 case URI_WHITESPACE_ENCODE:
1950 flags |= RFC1738_ESCAPE_UNESCAPED;
1951 http->log_uri = xstrndup(rfc1738_do_escape(uri, flags), MAX_URL);
1952 break;
1953
1954 case URI_WHITESPACE_CHOP: {
1955 flags |= RFC1738_ESCAPE_NOSPACE;
1956 flags |= RFC1738_ESCAPE_UNESCAPED;
1957 http->log_uri = xstrndup(rfc1738_do_escape(uri, flags), MAX_URL);
1958 int pos = strcspn(http->log_uri, w_space);
1959 http->log_uri[pos] = '\0';
1960 }
1961 break;
1962
1963 case URI_WHITESPACE_DENY:
1964 case URI_WHITESPACE_STRIP:
1965 default: {
1966 const char *t;
1967 char *tmp_uri = static_cast<char*>(xmalloc(strlen(uri) + 1));
1968 char *q = tmp_uri;
1969 t = uri;
1970 while (*t) {
1971 if (!xisspace(*t)) {
1972 *q = *t;
1973 ++q;
1974 }
1975 ++t;
1976 }
1977 *q = '\0';
1978 http->log_uri = xstrndup(rfc1738_escape_unescaped(tmp_uri), MAX_URL);
1979 xfree(tmp_uri);
1980 }
1981 break;
1982 }
1983 }
1984 }
1985
1986 static void
1987 prepareAcceleratedURL(ConnStateData * conn, ClientHttpRequest *http, const Http1::RequestParserPointer &hp)
1988 {
1989 int vhost = conn->port->vhost;
1990 int vport = conn->port->vport;
1991 static char ipbuf[MAX_IPSTRLEN];
1992
1993 http->flags.accel = true;
1994
1995 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
1996
1997 static const SBuf cache_object("cache_object://");
1998 if (hp->requestUri().startsWith(cache_object))
1999 return; /* already in good shape */
2000
2001 // XXX: re-use proper URL parser for this
2002 SBuf url = hp->requestUri(); // use full provided URI if we abort
2003 do { // use a loop so we can break out of it
2004 ::Parser::Tokenizer tok(url);
2005 if (tok.skip('/')) // origin-form URL already.
2006 break;
2007
2008 if (conn->port->vhost)
2009 return; /* already in good shape */
2010
2011 // skip the URI scheme
2012 static const CharacterSet uriScheme = CharacterSet("URI-scheme","+-.") + CharacterSet::ALPHA + CharacterSet::DIGIT;
2013 static const SBuf uriSchemeEnd("://");
2014 if (!tok.skipAll(uriScheme) || !tok.skip(uriSchemeEnd))
2015 break;
2016
2017 // skip the authority segment
2018 // RFC 3986 complex nested ABNF for "authority" boils down to this:
2019 static const CharacterSet authority = CharacterSet("authority","-._~%:@[]!$&'()*+,;=") +
2020 CharacterSet::HEXDIG + CharacterSet::ALPHA + CharacterSet::DIGIT;
2021 if (!tok.skipAll(authority))
2022 break;
2023
2024 static const SBuf slashUri("/");
2025 const SBuf t = tok.remaining();
2026 if (t.isEmpty())
2027 url = slashUri;
2028 else if (t[0]=='/') // looks like path
2029 url = t;
2030 else if (t[0]=='?' || t[0]=='#') { // looks like query or fragment. fix '/'
2031 url = slashUri;
2032 url.append(t);
2033 } // else do nothing. invalid path
2034
2035 } while(false);
2036
2037 #if SHOULD_REJECT_UNKNOWN_URLS
2038 // reject URI which are not well-formed even after the processing above
2039 if (url.isEmpty() || url[0] != '/') {
2040 hp->parseStatusCode = Http::scBadRequest;
2041 return conn->abortRequestParsing("error:invalid-request");
2042 }
2043 #endif
2044
2045 if (vport < 0)
2046 vport = http->getConn()->clientConnection->local.port();
2047
2048 const bool switchedToHttps = conn->switchedToHttps();
2049 const bool tryHostHeader = vhost || switchedToHttps;
2050 char *host = NULL;
2051 if (tryHostHeader && (host = hp->getHeaderField("Host"))) {
2052 debugs(33, 5, "ACCEL VHOST REWRITE: vhost=" << host << " + vport=" << vport);
2053 char thost[256];
2054 if (vport > 0) {
2055 thost[0] = '\0';
2056 char *t = NULL;
2057 if (host[strlen(host)] != ']' && (t = strrchr(host,':')) != NULL) {
2058 strncpy(thost, host, (t-host));
2059 snprintf(thost+(t-host), sizeof(thost)-(t-host), ":%d", vport);
2060 host = thost;
2061 } else if (!t) {
2062 snprintf(thost, sizeof(thost), "%s:%d",host, vport);
2063 host = thost;
2064 }
2065 } // else nothing to alter port-wise.
2066 const int url_sz = hp->requestUri().length() + 32 + Config.appendDomainLen + strlen(host);
2067 http->uri = (char *)xcalloc(url_sz, 1);
2068 snprintf(http->uri, url_sz, "%s://%s" SQUIDSBUFPH, AnyP::UriScheme(conn->transferProtocol.protocol).c_str(), host, SQUIDSBUFPRINT(url));
2069 debugs(33, 5, "ACCEL VHOST REWRITE: " << http->uri);
2070 } else if (conn->port->defaultsite /* && !vhost */) {
2071 debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: defaultsite=" << conn->port->defaultsite << " + vport=" << vport);
2072 const int url_sz = hp->requestUri().length() + 32 + Config.appendDomainLen +
2073 strlen(conn->port->defaultsite);
2074 http->uri = (char *)xcalloc(url_sz, 1);
2075 char vportStr[32];
2076 vportStr[0] = '\0';
2077 if (vport > 0) {
2078 snprintf(vportStr, sizeof(vportStr),":%d",vport);
2079 }
2080 snprintf(http->uri, url_sz, "%s://%s%s" SQUIDSBUFPH,
2081 AnyP::UriScheme(conn->transferProtocol.protocol).c_str(), conn->port->defaultsite, vportStr, SQUIDSBUFPRINT(url));
2082 debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: " << http->uri);
2083 } else if (vport > 0 /* && (!vhost || no Host:) */) {
2084 debugs(33, 5, "ACCEL VPORT REWRITE: *_port IP + vport=" << vport);
2085 /* Put the local socket IP address as the hostname, with whatever vport we found */
2086 const int url_sz = hp->requestUri().length() + 32 + Config.appendDomainLen;
2087 http->uri = (char *)xcalloc(url_sz, 1);
2088 http->getConn()->clientConnection->local.toHostStr(ipbuf,MAX_IPSTRLEN);
2089 snprintf(http->uri, url_sz, "%s://%s:%d" SQUIDSBUFPH,
2090 AnyP::UriScheme(conn->transferProtocol.protocol).c_str(),
2091 ipbuf, vport, SQUIDSBUFPRINT(url));
2092 debugs(33, 5, "ACCEL VPORT REWRITE: " << http->uri);
2093 }
2094 }
2095
2096 static void
2097 prepareTransparentURL(ConnStateData * conn, ClientHttpRequest *http, const Http1::RequestParserPointer &hp)
2098 {
2099 // TODO Must() on URI !empty when the parser supports throw. For now avoid assert().
2100 if (!hp->requestUri().isEmpty() && hp->requestUri()[0] != '/')
2101 return; /* already in good shape */
2102
2103 /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
2104
2105 if (const char *host = hp->getHeaderField("Host")) {
2106 const int url_sz = hp->requestUri().length() + 32 + Config.appendDomainLen +
2107 strlen(host);
2108 http->uri = (char *)xcalloc(url_sz, 1);
2109 snprintf(http->uri, url_sz, "%s://%s" SQUIDSBUFPH,
2110 AnyP::UriScheme(conn->transferProtocol.protocol).c_str(), host, SQUIDSBUFPRINT(hp->requestUri()));
2111 debugs(33, 5, "TRANSPARENT HOST REWRITE: " << http->uri);
2112 } else {
2113 /* Put the local socket IP address as the hostname. */
2114 const int url_sz = hp->requestUri().length() + 32 + Config.appendDomainLen;
2115 http->uri = (char *)xcalloc(url_sz, 1);
2116 static char ipbuf[MAX_IPSTRLEN];
2117 http->getConn()->clientConnection->local.toHostStr(ipbuf,MAX_IPSTRLEN);
2118 snprintf(http->uri, url_sz, "%s://%s:%d" SQUIDSBUFPH,
2119 AnyP::UriScheme(http->getConn()->transferProtocol.protocol).c_str(),
2120 ipbuf, http->getConn()->clientConnection->local.port(), SQUIDSBUFPRINT(hp->requestUri()));
2121 debugs(33, 5, "TRANSPARENT REWRITE: " << http->uri);
2122 }
2123 }
2124
2125 /** Parse an HTTP request
2126 *
2127 * \note Sets result->flags.parsed_ok to 0 if failed to parse the request,
2128 * to 1 if the request was correctly parsed.
2129 * \param[in] csd a ConnStateData. The caller must make sure it is not null
2130 * \param[in] hp an Http1::RequestParser
2131 * \param[out] mehtod_p will be set as a side-effect of the parsing.
2132 * Pointed-to value will be set to Http::METHOD_NONE in case of
2133 * parsing failure
2134 * \param[out] http_ver will be set as a side-effect of the parsing
2135 * \return NULL on incomplete requests,
2136 * a ClientSocketContext structure on success or failure.
2137 */
2138 ClientSocketContext *
2139 parseHttpRequest(ConnStateData *csd, const Http1::RequestParserPointer &hp)
2140 {
2141 /* Attempt to parse the first line; this will define where the method, url, version and header begin */
2142 {
2143 const bool parsedOk = hp->parse(csd->inBuf);
2144
2145 if (csd->port->flags.isIntercepted() && Config.accessList.on_unsupported_protocol)
2146 csd->preservedClientData = csd->inBuf;
2147 // sync the buffers after parsing.
2148 csd->inBuf = hp->remaining();
2149
2150 if (hp->needsMoreData()) {
2151 debugs(33, 5, "Incomplete request, waiting for end of request line");
2152 return NULL;
2153 }
2154
2155 if (!parsedOk) {
2156 if (hp->parseStatusCode == Http::scRequestHeaderFieldsTooLarge || hp->parseStatusCode == Http::scUriTooLong)
2157 return csd->abortRequestParsing("error:request-too-large");
2158
2159 return csd->abortRequestParsing("error:invalid-request");
2160 }
2161 }
2162
2163 /* We know the whole request is in parser now */
2164 debugs(11, 2, "HTTP Client " << csd->clientConnection);
2165 debugs(11, 2, "HTTP Client REQUEST:\n---------\n" <<
2166 hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol() << "\n" <<
2167 hp->mimeHeader() <<
2168 "\n----------");
2169
2170 /* deny CONNECT via accelerated ports */
2171 if (hp->method() == Http::METHOD_CONNECT && csd->port != NULL && csd->port->flags.accelSurrogate) {
2172 debugs(33, DBG_IMPORTANT, "WARNING: CONNECT method received on " << csd->transferProtocol << " Accelerator port " << csd->port->s.port());
2173 debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
2174 hp->parseStatusCode = Http::scMethodNotAllowed;
2175 return csd->abortRequestParsing("error:method-not-allowed");
2176 }
2177
2178 /* RFC 7540 section 11.6 registers the method PRI as HTTP/2 specific
2179 * Deny "PRI" method if used in HTTP/1.x or 0.9 versions.
2180 * If seen it signals a broken client or proxy has corrupted the traffic.
2181 */
2182 if (hp->method() == Http::METHOD_PRI && hp->messageProtocol() < Http::ProtocolVersion(2,0)) {
2183 debugs(33, DBG_IMPORTANT, "WARNING: PRI method received on " << csd->transferProtocol << " port " << csd->port->s.port());
2184 debugs(33, DBG_IMPORTANT, "WARNING: for request: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
2185 hp->parseStatusCode = Http::scMethodNotAllowed;
2186 return csd->abortRequestParsing("error:method-not-allowed");
2187 }
2188
2189 if (hp->method() == Http::METHOD_NONE) {
2190 debugs(33, DBG_IMPORTANT, "WARNING: Unsupported method: " << hp->method() << " " << hp->requestUri() << " " << hp->messageProtocol());
2191 hp->parseStatusCode = Http::scMethodNotAllowed;
2192 return csd->abortRequestParsing("error:unsupported-request-method");
2193 }
2194
2195 // Process headers after request line
2196 debugs(33, 3, "complete request received. " <<
2197 "prefix_sz = " << hp->messageHeaderSize() <<
2198 ", request-line-size=" << hp->firstLineSize() <<
2199 ", mime-header-size=" << hp->headerBlockSize() <<
2200 ", mime header block:\n" << hp->mimeHeader() << "\n----------");
2201
2202 /* Ok, all headers are received */
2203 ClientHttpRequest *http = new ClientHttpRequest(csd);
2204
2205 http->req_sz = hp->messageHeaderSize();
2206 ClientSocketContext *result = new ClientSocketContext(csd->clientConnection, http);
2207
2208 StoreIOBuffer tempBuffer;
2209 tempBuffer.data = result->reqbuf;
2210 tempBuffer.length = HTTP_REQBUF_SZ;
2211
2212 ClientStreamData newServer = new clientReplyContext(http);
2213 ClientStreamData newClient = result;
2214 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
2215 clientReplyStatus, newServer, clientSocketRecipient,
2216 clientSocketDetach, newClient, tempBuffer);
2217
2218 /* set url */
2219 debugs(33,5, "Prepare absolute URL from " <<
2220 (csd->transparent()?"intercept":(csd->port->flags.accelSurrogate ? "accel":"")));
2221 /* Rewrite the URL in transparent or accelerator mode */
2222 /* NP: there are several cases to traverse here:
2223 * - standard mode (forward proxy)
2224 * - transparent mode (TPROXY)
2225 * - transparent mode with failures
2226 * - intercept mode (NAT)
2227 * - intercept mode with failures
2228 * - accelerator mode (reverse proxy)
2229 * - internal relative-URL
2230 * - mixed combos of the above with internal URL
2231 * - remote interception with PROXY protocol
2232 * - remote reverse-proxy with PROXY protocol
2233 */
2234 if (csd->transparent()) {
2235 /* intercept or transparent mode, properly working with no failures */
2236 prepareTransparentURL(csd, http, hp);
2237
2238 } else if (internalCheck(hp->requestUri())) { // NP: only matches relative-URI
2239 /* internal URL mode */
2240 /* prepend our name & port */
2241 http->uri = xstrdup(internalLocalUri(NULL, hp->requestUri()));
2242 // We just re-wrote the URL. Must replace the Host: header.
2243 // But have not parsed there yet!! flag for local-only handling.
2244 http->flags.internal = true;
2245
2246 } else if (csd->port->flags.accelSurrogate || csd->switchedToHttps()) {
2247 /* accelerator mode */
2248 prepareAcceleratedURL(csd, http, hp);
2249 }
2250
2251 if (!http->uri) {
2252 /* No special rewrites have been applied above, use the
2253 * requested url. may be rewritten later, so make extra room */
2254 int url_sz = hp->requestUri().length() + Config.appendDomainLen + 5;
2255 http->uri = (char *)xcalloc(url_sz, 1);
2256 SBufToCstring(http->uri, hp->requestUri());
2257 }
2258
2259 result->flags.parsed_ok = 1;
2260 return result;
2261 }
2262
2263 int
2264 ConnStateData::getConcurrentRequestCount() const
2265 {
2266 int result = 0;
2267 ClientSocketContext::Pointer *T;
2268
2269 for (T = (ClientSocketContext::Pointer *) &currentobject;
2270 T->getRaw(); T = &(*T)->next, ++result);
2271 return result;
2272 }
2273
2274 bool
2275 ConnStateData::connFinishedWithConn(int size)
2276 {
2277 if (size == 0) {
2278 if (getConcurrentRequestCount() == 0 && inBuf.isEmpty()) {
2279 /* no current or pending requests */
2280 debugs(33, 4, HERE << clientConnection << " closed");
2281 return true;
2282 } else if (!Config.onoff.half_closed_clients) {
2283 /* admin doesn't want to support half-closed client sockets */
2284 debugs(33, 3, HERE << clientConnection << " aborted (half_closed_clients disabled)");
2285 notifyAllContexts(0); // no specific error implies abort
2286 return true;
2287 }
2288 }
2289
2290 return false;
2291 }
2292
2293 void
2294 ConnStateData::consumeInput(const size_t byteCount)
2295 {
2296 assert(byteCount > 0 && byteCount <= inBuf.length());
2297 inBuf.consume(byteCount);
2298 debugs(33, 5, "inBuf has " << inBuf.length() << " unused bytes");
2299 }
2300
2301 void
2302 ConnStateData::clientAfterReadingRequests()
2303 {
2304 // Were we expecting to read more request body from half-closed connection?
2305 if (mayNeedToReadMoreBody() && commIsHalfClosed(clientConnection->fd)) {
2306 debugs(33, 3, HERE << "truncated body: closing half-closed " << clientConnection);
2307 clientConnection->close();
2308 return;
2309 }
2310
2311 if (flags.readMore)
2312 readSomeData();
2313 }
2314
2315 void
2316 ConnStateData::quitAfterError(HttpRequest *request)
2317 {
2318 // From HTTP p.o.v., we do not have to close after every error detected
2319 // at the client-side, but many such errors do require closure and the
2320 // client-side code is bad at handling errors so we play it safe.
2321 if (request)
2322 request->flags.proxyKeepalive = false;
2323 flags.readMore = false;
2324 debugs(33,4, HERE << "Will close after error: " << clientConnection);
2325 }
2326
2327 #if USE_OPENSSL
2328 bool ConnStateData::serveDelayedError(ClientSocketContext *context)
2329 {
2330 ClientHttpRequest *http = context->http;
2331
2332 if (!sslServerBump)
2333 return false;
2334
2335 assert(sslServerBump->entry);
2336 // Did we create an error entry while processing CONNECT?
2337 if (!sslServerBump->entry->isEmpty()) {
2338 quitAfterError(http->request);
2339
2340 // Get the saved error entry and send it to the client by replacing the
2341 // ClientHttpRequest store entry with it.
2342 clientStreamNode *node = context->getClientReplyContext();
2343 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2344 assert(repContext);
2345 debugs(33, 5, "Responding with delated error for " << http->uri);
2346 repContext->setReplyToStoreEntry(sslServerBump->entry, "delayed SslBump error");
2347
2348 // save the original request for logging purposes
2349 if (!context->http->al->request) {
2350 context->http->al->request = http->request;
2351 HTTPMSGLOCK(context->http->al->request);
2352 }
2353
2354 // Get error details from the fake certificate-peeking request.
2355 http->request->detailError(sslServerBump->request->errType, sslServerBump->request->errDetail);
2356 context->pullData();
2357 return true;
2358 }
2359
2360 // In bump-server-first mode, we have not necessarily seen the intended
2361 // server name at certificate-peeking time. Check for domain mismatch now,
2362 // when we can extract the intended name from the bumped HTTP request.
2363 if (X509 *srvCert = sslServerBump->serverCert.get()) {
2364 HttpRequest *request = http->request;
2365 if (!Ssl::checkX509ServerValidity(srvCert, request->url.host())) {
2366 debugs(33, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " <<
2367 "does not match domainname " << request->url.host());
2368
2369 bool allowDomainMismatch = false;
2370 if (Config.ssl_client.cert_error) {
2371 ACLFilledChecklist check(Config.ssl_client.cert_error, request, dash_str);
2372 check.sslErrors = new Ssl::CertErrors(Ssl::CertError(SQUID_X509_V_ERR_DOMAIN_MISMATCH, srvCert));
2373 allowDomainMismatch = (check.fastCheck() == ACCESS_ALLOWED);
2374 delete check.sslErrors;
2375 check.sslErrors = NULL;
2376 }
2377
2378 if (!allowDomainMismatch) {
2379 quitAfterError(request);
2380
2381 clientStreamNode *node = context->getClientReplyContext();
2382 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2383 assert (repContext);
2384
2385 // Fill the server IP and hostname for error page generation.
2386 HttpRequest::Pointer const & peekerRequest = sslServerBump->request;
2387 request->hier.note(peekerRequest->hier.tcpServer, request->url.host());
2388
2389 // Create an error object and fill it
2390 ErrorState *err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request);
2391 err->src_addr = clientConnection->remote;
2392 Ssl::ErrorDetail *errDetail = new Ssl::ErrorDetail(
2393 SQUID_X509_V_ERR_DOMAIN_MISMATCH,
2394 srvCert, NULL);
2395 err->detail = errDetail;
2396 // Save the original request for logging purposes.
2397 if (!context->http->al->request) {
2398 context->http->al->request = request;
2399 HTTPMSGLOCK(context->http->al->request);
2400 }
2401 repContext->setReplyToError(request->method, err);
2402 assert(context->http->out.offset == 0);
2403 context->pullData();
2404 return true;
2405 }
2406 }
2407 }
2408
2409 return false;
2410 }
2411 #endif // USE_OPENSSL
2412
2413 /**
2414 * Check on_unsupported_protocol checklist and return true if tunnel mode selected
2415 * or false otherwise
2416 */
2417 bool
2418 clientTunnelOnError(ConnStateData *conn, ClientSocketContext *context, HttpRequest *request, const HttpRequestMethod& method, err_type requestError, Http::StatusCode errStatusCode, const char *requestErrorBytes)
2419 {
2420 if (conn->port->flags.isIntercepted() &&
2421 Config.accessList.on_unsupported_protocol && conn->pipeline.nrequests <= 1) {
2422 ACLFilledChecklist checklist(Config.accessList.on_unsupported_protocol, request, NULL);
2423 checklist.requestErrorType = requestError;
2424 checklist.src_addr = conn->clientConnection->remote;
2425 checklist.my_addr = conn->clientConnection->local;
2426 checklist.conn(conn);
2427 allow_t answer = checklist.fastCheck();
2428 if (answer == ACCESS_ALLOWED && answer.kind == 1) {
2429 debugs(33, 3, "Request will be tunneled to server");
2430 if (context)
2431 context->removeFromConnectionList(conn);
2432 Comm::SetSelect(conn->clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
2433 conn->fakeAConnectRequest("unknown-protocol", conn->preservedClientData);
2434 return true;
2435 } else {
2436 debugs(33, 3, "Continue with returning the error: " << requestError);
2437 }
2438 }
2439
2440 if (context) {
2441 conn->quitAfterError(request);
2442 clientStreamNode *node = context->getClientReplyContext();
2443 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2444 assert (repContext);
2445
2446 repContext->setReplyToError(requestError, errStatusCode, method, context->http->uri, conn->clientConnection->remote, NULL, requestErrorBytes, NULL);
2447
2448 assert(context->http->out.offset == 0);
2449 context->pullData();
2450 } // else Probably an ERR_REQUEST_START_TIMEOUT error so just return.
2451 return false;
2452 }
2453
2454 void
2455 clientProcessRequestFinished(ConnStateData *conn, const HttpRequest::Pointer &request)
2456 {
2457 /*
2458 * DPW 2007-05-18
2459 * Moved the TCP_RESET feature from clientReplyContext::sendMoreData
2460 * to here because calling comm_reset_close() causes http to
2461 * be freed before accessing.
2462 */
2463 if (request != NULL && request->flags.resetTcp && Comm::IsConnOpen(conn->clientConnection)) {
2464 debugs(33, 3, HERE << "Sending TCP RST on " << conn->clientConnection);
2465 conn->flags.readMore = false;
2466 comm_reset_close(conn->clientConnection);
2467 }
2468 }
2469
2470 void
2471 clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp, ClientSocketContext *context)
2472 {
2473 ClientHttpRequest *http = context->http;
2474 bool chunked = false;
2475 bool mustReplyToOptions = false;
2476 bool unsupportedTe = false;
2477 bool expectBody = false;
2478
2479 // We already have the request parsed and checked, so we
2480 // only need to go through the final body/conn setup to doCallouts().
2481 assert(http->request);
2482 HttpRequest::Pointer request = http->request;
2483
2484 // temporary hack to avoid splitting this huge function with sensitive code
2485 const bool isFtp = !hp;
2486
2487 // Some blobs below are still HTTP-specific, but we would have to rewrite
2488 // this entire function to remove them from the FTP code path. Connection
2489 // setup and body_pipe preparation blobs are needed for FTP.
2490
2491 request->clientConnectionManager = conn;
2492
2493 request->flags.accelerated = http->flags.accel;
2494 request->flags.sslBumped=conn->switchedToHttps();
2495 request->flags.ignoreCc = conn->port->ignore_cc;
2496 // TODO: decouple http->flags.accel from request->flags.sslBumped
2497 request->flags.noDirect = (request->flags.accelerated && !request->flags.sslBumped) ?
2498 !conn->port->allow_direct : 0;
2499 #if USE_AUTH
2500 if (request->flags.sslBumped) {
2501 if (conn->getAuth() != NULL)
2502 request->auth_user_request = conn->getAuth();
2503 }
2504 #endif
2505
2506 /** \par
2507 * If transparent or interception mode is working clone the transparent and interception flags
2508 * from the port settings to the request.
2509 */
2510 if (http->clientConnection != NULL) {
2511 request->flags.intercepted = ((http->clientConnection->flags & COMM_INTERCEPTION) != 0);
2512 request->flags.interceptTproxy = ((http->clientConnection->flags & COMM_TRANSPARENT) != 0 ) ;
2513 static const bool proxyProtocolPort = (conn->port != NULL) ? conn->port->flags.proxySurrogate : false;
2514 if (request->flags.interceptTproxy && !proxyProtocolPort) {
2515 if (Config.accessList.spoof_client_ip) {
2516 ACLFilledChecklist *checklist = clientAclChecklistCreate(Config.accessList.spoof_client_ip, http);
2517 request->flags.spoofClientIp = (checklist->fastCheck() == ACCESS_ALLOWED);
2518 delete checklist;
2519 } else
2520 request->flags.spoofClientIp = true;
2521 } else
2522 request->flags.spoofClientIp = false;
2523 }
2524
2525 if (internalCheck(request->url.path())) {
2526 if (internalHostnameIs(request->url.host()) && request->url.port() == getMyPort()) {
2527 debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true));
2528 http->flags.internal = true;
2529 } else if (Config.onoff.global_internal_static && internalStaticCheck(request->url.path())) {
2530 debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true) << " (global_internal_static on)");
2531 request->url.setScheme(AnyP::PROTO_HTTP);
2532 request->url.host(internalHostname());
2533 request->url.port(getMyPort());
2534 http->flags.internal = true;
2535 } else
2536 debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true) << " (not this proxy)");
2537 }
2538
2539 request->flags.internal = http->flags.internal;
2540 setLogUri (http, urlCanonicalClean(request.getRaw()));
2541 request->client_addr = conn->clientConnection->remote; // XXX: remove reuest->client_addr member.
2542 #if FOLLOW_X_FORWARDED_FOR
2543 // indirect client gets stored here because it is an HTTP header result (from X-Forwarded-For:)
2544 // not a details about teh TCP connection itself
2545 request->indirect_client_addr = conn->clientConnection->remote;
2546 #endif /* FOLLOW_X_FORWARDED_FOR */
2547 request->my_addr = conn->clientConnection->local;
2548 request->myportname = conn->port->name;
2549
2550 if (!isFtp) {
2551 // XXX: for non-HTTP messages instantiate a different HttpMsg child type
2552 // for now Squid only supports HTTP requests
2553 const AnyP::ProtocolVersion &http_ver = hp->messageProtocol();
2554 assert(request->http_ver.protocol == http_ver.protocol);
2555 request->http_ver.major = http_ver.major;
2556 request->http_ver.minor = http_ver.minor;
2557 }
2558
2559 // Link this HttpRequest to ConnStateData relatively early so the following complex handling can use it
2560 // TODO: this effectively obsoletes a lot of conn->FOO copying. That needs cleaning up later.
2561 request->clientConnectionManager = conn;
2562
2563 if (request->header.chunked()) {
2564 chunked = true;
2565 } else if (request->header.has(Http::HdrType::TRANSFER_ENCODING)) {
2566 const String te = request->header.getList(Http::HdrType::TRANSFER_ENCODING);
2567 // HTTP/1.1 requires chunking to be the last encoding if there is one
2568 unsupportedTe = te.size() && te != "identity";
2569 } // else implied identity coding
2570
2571 mustReplyToOptions = (request->method == Http::METHOD_OPTIONS) &&
2572 (request->header.getInt64(Http::HdrType::MAX_FORWARDS) == 0);
2573 if (!urlCheckRequest(request.getRaw()) || mustReplyToOptions || unsupportedTe) {
2574 clientStreamNode *node = context->getClientReplyContext();
2575 conn->quitAfterError(request.getRaw());
2576 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2577 assert (repContext);
2578 repContext->setReplyToError(ERR_UNSUP_REQ, Http::scNotImplemented, request->method, NULL,
2579 conn->clientConnection->remote, request.getRaw(), NULL, NULL);
2580 assert(context->http->out.offset == 0);
2581 context->pullData();
2582 clientProcessRequestFinished(conn, request);
2583 return;
2584 }
2585
2586 if (!chunked && !clientIsContentLengthValid(request.getRaw())) {
2587 clientStreamNode *node = context->getClientReplyContext();
2588 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2589 assert (repContext);
2590 conn->quitAfterError(request.getRaw());
2591 repContext->setReplyToError(ERR_INVALID_REQ,
2592 Http::scLengthRequired, request->method, NULL,
2593 conn->clientConnection->remote, request.getRaw(), NULL, NULL);
2594 assert(context->http->out.offset == 0);
2595 context->pullData();
2596 clientProcessRequestFinished(conn, request);
2597 return;
2598 }
2599
2600 clientSetKeepaliveFlag(http);
2601 // Let tunneling code be fully responsible for CONNECT requests
2602 if (http->request->method == Http::METHOD_CONNECT) {
2603 context->mayUseConnection(true);
2604 conn->flags.readMore = false;
2605 }
2606
2607 #if USE_OPENSSL
2608 if (conn->switchedToHttps() && conn->serveDelayedError(context)) {
2609 clientProcessRequestFinished(conn, request);
2610 return;
2611 }
2612 #endif
2613
2614 /* Do we expect a request-body? */
2615 expectBody = chunked || request->content_length > 0;
2616 if (!context->mayUseConnection() && expectBody) {
2617 request->body_pipe = conn->expectRequestBody(
2618 chunked ? -1 : request->content_length);
2619
2620 /* Is it too large? */
2621 if (!chunked && // if chunked, we will check as we accumulate
2622 clientIsRequestBodyTooLargeForPolicy(request->content_length)) {
2623 clientStreamNode *node = context->getClientReplyContext();
2624 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2625 assert (repContext);
2626 conn->quitAfterError(request.getRaw());
2627 repContext->setReplyToError(ERR_TOO_BIG,
2628 Http::scPayloadTooLarge, Http::METHOD_NONE, NULL,
2629 conn->clientConnection->remote, http->request, NULL, NULL);
2630 assert(context->http->out.offset == 0);
2631 context->pullData();
2632 clientProcessRequestFinished(conn, request);
2633 return;
2634 }
2635
2636 if (!isFtp) {
2637 // We may stop producing, comm_close, and/or call setReplyToError()
2638 // below, so quit on errors to avoid http->doCallouts()
2639 if (!conn->handleRequestBodyData()) {
2640 clientProcessRequestFinished(conn, request);
2641 return;
2642 }
2643
2644 if (!request->body_pipe->productionEnded()) {
2645 debugs(33, 5, "need more request body");
2646 context->mayUseConnection(true);
2647 assert(conn->flags.readMore);
2648 }
2649 }
2650 }
2651
2652 http->calloutContext = new ClientRequestContext(http);
2653
2654 http->doCallouts();
2655
2656 clientProcessRequestFinished(conn, request);
2657 }
2658
2659 int
2660 ConnStateData::pipelinePrefetchMax() const
2661 {
2662 // TODO: Support pipelined requests through pinned connections.
2663 if (pinning.pinned)
2664 return 0;
2665 return Config.pipeline_max_prefetch;
2666 }
2667
2668 /**
2669 * Limit the number of concurrent requests.
2670 * \return true when there are available position(s) in the pipeline queue for another request.
2671 * \return false when the pipeline queue is full or disabled.
2672 */
2673 bool
2674 ConnStateData::concurrentRequestQueueFilled() const
2675 {
2676 const int existingRequestCount = getConcurrentRequestCount();
2677
2678 // default to the configured pipeline size.
2679 // add 1 because the head of pipeline is counted in concurrent requests and not prefetch queue
2680 #if USE_OPENSSL
2681 const int internalRequest = (transparent() && sslBumpMode == Ssl::bumpSplice) ? 1 : 0;
2682 #else
2683 const int internalRequest = 0;
2684 #endif
2685 const int concurrentRequestLimit = pipelinePrefetchMax() + 1 + internalRequest;
2686
2687 // when queue filled already we cant add more.
2688 if (existingRequestCount >= concurrentRequestLimit) {
2689 debugs(33, 3, clientConnection << " max concurrent requests reached (" << concurrentRequestLimit << ")");
2690 debugs(33, 5, clientConnection << " deferring new request until one is done");
2691 return true;
2692 }
2693
2694 return false;
2695 }
2696
2697 /**
2698 * Perform proxy_protocol_access ACL tests on the client which
2699 * connected to PROXY protocol port to see if we trust the
2700 * sender enough to accept their PROXY header claim.
2701 */
2702 bool
2703 ConnStateData::proxyProtocolValidateClient()
2704 {
2705 if (!Config.accessList.proxyProtocol)
2706 return proxyProtocolError("PROXY client not permitted by default ACL");
2707
2708 ACLFilledChecklist ch(Config.accessList.proxyProtocol, NULL, clientConnection->rfc931);
2709 ch.src_addr = clientConnection->remote;
2710 ch.my_addr = clientConnection->local;
2711 ch.conn(this);
2712
2713 if (ch.fastCheck() != ACCESS_ALLOWED)
2714 return proxyProtocolError("PROXY client not permitted by ACLs");
2715
2716 return true;
2717 }
2718
2719 /**
2720 * Perform cleanup on PROXY protocol errors.
2721 * If header parsing hits a fatal error terminate the connection,
2722 * otherwise wait for more data.
2723 */
2724 bool
2725 ConnStateData::proxyProtocolError(const char *msg)
2726 {
2727 if (msg) {
2728 // This is important to know, but maybe not so much that flooding the log is okay.
2729 #if QUIET_PROXY_PROTOCOL
2730 // display the first of every 32 occurances at level 1, the others at level 2.
2731 static uint8_t hide = 0;
2732 debugs(33, (hide++ % 32 == 0 ? DBG_IMPORTANT : 2), msg << " from " << clientConnection);
2733 #else
2734 debugs(33, DBG_IMPORTANT, msg << " from " << clientConnection);
2735 #endif
2736 mustStop(msg);
2737 }
2738 return false;
2739 }
2740
2741 /// magic octet prefix for PROXY protocol version 1
2742 static const SBuf Proxy1p0magic("PROXY ", 6);
2743
2744 /// magic octet prefix for PROXY protocol version 2
2745 static const SBuf Proxy2p0magic("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
2746
2747 /**
2748 * Test the connection read buffer for PROXY protocol header.
2749 * Version 1 and 2 header currently supported.
2750 */
2751 bool
2752 ConnStateData::parseProxyProtocolHeader()
2753 {
2754 // http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
2755
2756 // detect and parse PROXY/2.0 protocol header
2757 if (inBuf.startsWith(Proxy2p0magic))
2758 return parseProxy2p0();
2759
2760 // detect and parse PROXY/1.0 protocol header
2761 if (inBuf.startsWith(Proxy1p0magic))
2762 return parseProxy1p0();
2763
2764 // detect and terminate other protocols
2765 if (inBuf.length() >= Proxy2p0magic.length()) {
2766 // PROXY/1.0 magic is shorter, so we know that
2767 // the input does not start with any PROXY magic
2768 return proxyProtocolError("PROXY protocol error: invalid header");
2769 }
2770
2771 // TODO: detect short non-magic prefixes earlier to avoid
2772 // waiting for more data which may never come
2773
2774 // not enough bytes to parse yet.
2775 return false;
2776 }
2777
2778 /// parse the PROXY/1.0 protocol header from the connection read buffer
2779 bool
2780 ConnStateData::parseProxy1p0()
2781 {
2782 ::Parser::Tokenizer tok(inBuf);
2783 tok.skip(Proxy1p0magic);
2784
2785 // skip to first LF (assumes it is part of CRLF)
2786 static const CharacterSet lineContent = CharacterSet::LF.complement("non-LF");
2787 SBuf line;
2788 if (tok.prefix(line, lineContent, 107-Proxy1p0magic.length())) {
2789 if (tok.skip('\n')) {
2790 // found valid header
2791 inBuf = tok.remaining();
2792 needProxyProtocolHeader_ = false;
2793 // reset the tokenizer to work on found line only.
2794 tok.reset(line);
2795 } else
2796 return false; // no LF yet
2797
2798 } else // protocol error only if there are more than 107 bytes prefix header
2799 return proxyProtocolError(inBuf.length() > 107? "PROXY/1.0 error: missing CRLF" : NULL);
2800
2801 static const SBuf unknown("UNKNOWN"), tcpName("TCP");
2802 if (tok.skip(tcpName)) {
2803
2804 // skip TCP/IP version number
2805 static const CharacterSet tcpVersions("TCP-version","46");
2806 if (!tok.skipOne(tcpVersions))
2807 return proxyProtocolError("PROXY/1.0 error: missing TCP version");
2808
2809 // skip SP after protocol version
2810 if (!tok.skip(' '))
2811 return proxyProtocolError("PROXY/1.0 error: missing SP");
2812
2813 SBuf ipa, ipb;
2814 int64_t porta, portb;
2815 static const CharacterSet ipChars = CharacterSet("IP Address",".:") + CharacterSet::HEXDIG;
2816
2817 // parse: src-IP SP dst-IP SP src-port SP dst-port CR
2818 // leave the LF until later.
2819 const bool correct = tok.prefix(ipa, ipChars) && tok.skip(' ') &&
2820 tok.prefix(ipb, ipChars) && tok.skip(' ') &&
2821 tok.int64(porta) && tok.skip(' ') &&
2822 tok.int64(portb) &&
2823 tok.skip('\r');
2824 if (!correct)
2825 return proxyProtocolError("PROXY/1.0 error: invalid syntax");
2826
2827 // parse IP and port strings
2828 Ip::Address originalClient, originalDest;
2829
2830 if (!originalClient.GetHostByName(ipa.c_str()))
2831 return proxyProtocolError("PROXY/1.0 error: invalid src-IP address");
2832
2833 if (!originalDest.GetHostByName(ipb.c_str()))
2834 return proxyProtocolError("PROXY/1.0 error: invalid dst-IP address");
2835
2836 if (porta > 0 && porta <= 0xFFFF) // max uint16_t
2837 originalClient.port(static_cast<uint16_t>(porta));
2838 else
2839 return proxyProtocolError("PROXY/1.0 error: invalid src port");
2840
2841 if (portb > 0 && portb <= 0xFFFF) // max uint16_t
2842 originalDest.port(static_cast<uint16_t>(portb));
2843 else
2844 return proxyProtocolError("PROXY/1.0 error: invalid dst port");
2845
2846 // we have original client and destination details now
2847 // replace the client connection values
2848 debugs(33, 5, "PROXY/1.0 protocol on connection " << clientConnection);
2849 clientConnection->local = originalDest;
2850 clientConnection->remote = originalClient;
2851 if ((clientConnection->flags & COMM_TRANSPARENT))
2852 clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY spoofing of this new IP.
2853 debugs(33, 5, "PROXY/1.0 upgrade: " << clientConnection);
2854
2855 // repeat fetch ensuring the new client FQDN can be logged
2856 if (Config.onoff.log_fqdn)
2857 fqdncache_gethostbyaddr(clientConnection->remote, FQDN_LOOKUP_IF_MISS);
2858
2859 return true;
2860
2861 } else if (tok.skip(unknown)) {
2862 // found valid but unusable header
2863 return true;
2864
2865 } else
2866 return proxyProtocolError("PROXY/1.0 error: invalid protocol family");
2867
2868 return false;
2869 }
2870
2871 /// parse the PROXY/2.0 protocol header from the connection read buffer
2872 bool
2873 ConnStateData::parseProxy2p0()
2874 {
2875 static const SBuf::size_type prefixLen = Proxy2p0magic.length();
2876 if (inBuf.length() < prefixLen + 4)
2877 return false; // need more bytes
2878
2879 if ((inBuf[prefixLen] & 0xF0) != 0x20) // version == 2 is mandatory
2880 return proxyProtocolError("PROXY/2.0 error: invalid version");
2881
2882 const char command = (inBuf[prefixLen] & 0x0F);
2883 if ((command & 0xFE) != 0x00) // values other than 0x0-0x1 are invalid
2884 return proxyProtocolError("PROXY/2.0 error: invalid command");
2885
2886 const char family = (inBuf[prefixLen+1] & 0xF0) >>4;
2887 if (family > 0x3) // values other than 0x0-0x3 are invalid
2888 return proxyProtocolError("PROXY/2.0 error: invalid family");
2889
2890 const char proto = (inBuf[prefixLen+1] & 0x0F);
2891 if (proto > 0x2) // values other than 0x0-0x2 are invalid
2892 return proxyProtocolError("PROXY/2.0 error: invalid protocol type");
2893
2894 const char *clen = inBuf.rawContent() + prefixLen + 2;
2895 uint16_t len;
2896 memcpy(&len, clen, sizeof(len));
2897 len = ntohs(len);
2898
2899 if (inBuf.length() < prefixLen + 4 + len)
2900 return false; // need more bytes
2901
2902 inBuf.consume(prefixLen + 4); // 4 being the extra bytes
2903 const SBuf extra = inBuf.consume(len);
2904 needProxyProtocolHeader_ = false; // found successfully
2905
2906 // LOCAL connections do nothing with the extras
2907 if (command == 0x00/* LOCAL*/)
2908 return true;
2909
2910 union pax {
2911 struct { /* for TCP/UDP over IPv4, len = 12 */
2912 struct in_addr src_addr;
2913 struct in_addr dst_addr;
2914 uint16_t src_port;
2915 uint16_t dst_port;
2916 } ipv4_addr;
2917 struct { /* for TCP/UDP over IPv6, len = 36 */
2918 struct in6_addr src_addr;
2919 struct in6_addr dst_addr;
2920 uint16_t src_port;
2921 uint16_t dst_port;
2922 } ipv6_addr;
2923 #if NOT_SUPPORTED
2924 struct { /* for AF_UNIX sockets, len = 216 */
2925 uint8_t src_addr[108];
2926 uint8_t dst_addr[108];
2927 } unix_addr;
2928 #endif
2929 };
2930
2931 pax ipu;
2932 memcpy(&ipu, extra.rawContent(), sizeof(pax));
2933
2934 // replace the client connection values
2935 debugs(33, 5, "PROXY/2.0 protocol on connection " << clientConnection);
2936 switch (family) {
2937 case 0x1: // IPv4
2938 clientConnection->local = ipu.ipv4_addr.dst_addr;
2939 clientConnection->local.port(ntohs(ipu.ipv4_addr.dst_port));
2940 clientConnection->remote = ipu.ipv4_addr.src_addr;
2941 clientConnection->remote.port(ntohs(ipu.ipv4_addr.src_port));
2942 if ((clientConnection->flags & COMM_TRANSPARENT))
2943 clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY spoofing of this new IP.
2944 break;
2945 case 0x2: // IPv6
2946 clientConnection->local = ipu.ipv6_addr.dst_addr;
2947 clientConnection->local.port(ntohs(ipu.ipv6_addr.dst_port));
2948 clientConnection->remote = ipu.ipv6_addr.src_addr;
2949 clientConnection->remote.port(ntohs(ipu.ipv6_addr.src_port));
2950 if ((clientConnection->flags & COMM_TRANSPARENT))
2951 clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY spoofing of this new IP.
2952 break;
2953 default: // do nothing
2954 break;
2955 }
2956 debugs(33, 5, "PROXY/2.0 upgrade: " << clientConnection);
2957
2958 // repeat fetch ensuring the new client FQDN can be logged
2959 if (Config.onoff.log_fqdn)
2960 fqdncache_gethostbyaddr(clientConnection->remote, FQDN_LOOKUP_IF_MISS);
2961
2962 return true;
2963 }
2964
2965 void
2966 ConnStateData::receivedFirstByte()
2967 {
2968 if (receivedFirstByte_)
2969 return;
2970
2971 receivedFirstByte_ = true;
2972 // Set timeout to Config.Timeout.request
2973 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
2974 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
2975 TimeoutDialer, this, ConnStateData::requestTimeout);
2976 commSetConnTimeout(clientConnection, Config.Timeout.request, timeoutCall);
2977 }
2978
2979 /**
2980 * Attempt to parse one or more requests from the input buffer.
2981 * Returns true after completing parsing of at least one request [header]. That
2982 * includes cases where parsing ended with an error (e.g., a huge request).
2983 */
2984 bool
2985 ConnStateData::clientParseRequests()
2986 {
2987 bool parsed_req = false;
2988
2989 debugs(33, 5, HERE << clientConnection << ": attempting to parse");
2990
2991 // Loop while we have read bytes that are not needed for producing the body
2992 // On errors, bodyPipe may become nil, but readMore will be cleared
2993 while (!inBuf.isEmpty() && !bodyPipe && flags.readMore) {
2994
2995 /* Don't try to parse if the buffer is empty */
2996 if (inBuf.isEmpty())
2997 break;
2998
2999 /* Limit the number of concurrent requests */
3000 if (concurrentRequestQueueFilled())
3001 break;
3002
3003 // try to parse the PROXY protocol header magic bytes
3004 if (needProxyProtocolHeader_ && !parseProxyProtocolHeader())
3005 break;
3006
3007 if (ClientSocketContext *context = parseOneRequest()) {
3008 debugs(33, 5, clientConnection << ": done parsing a request");
3009
3010 AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "clientLifetimeTimeout",
3011 CommTimeoutCbPtrFun(clientLifetimeTimeout, context->http));
3012 commSetConnTimeout(clientConnection, Config.Timeout.lifetime, timeoutCall);
3013
3014 context->registerWithConn();
3015
3016 processParsedRequest(context);
3017
3018 parsed_req = true; // XXX: do we really need to parse everything right NOW ?
3019
3020 if (context->mayUseConnection()) {
3021 debugs(33, 3, HERE << "Not parsing new requests, as this request may need the connection");
3022 break;
3023 }
3024 } else {
3025 debugs(33, 5, clientConnection << ": not enough request data: " <<
3026 inBuf.length() << " < " << Config.maxRequestHeaderSize);
3027 Must(inBuf.length() < Config.maxRequestHeaderSize);
3028 break;
3029 }
3030 }
3031
3032 /* XXX where to 'finish' the parsing pass? */
3033 return parsed_req;
3034 }
3035
3036 void
3037 ConnStateData::afterClientRead()
3038 {
3039 /* Process next request */
3040 if (getConcurrentRequestCount() == 0)
3041 fd_note(clientConnection->fd, "Reading next request");
3042
3043 if (!clientParseRequests()) {
3044 if (!isOpen())
3045 return;
3046 /*
3047 * If the client here is half closed and we failed
3048 * to parse a request, close the connection.
3049 * The above check with connFinishedWithConn() only
3050 * succeeds _if_ the buffer is empty which it won't
3051 * be if we have an incomplete request.
3052 * XXX: This duplicates ClientSocketContext::keepaliveNextRequest
3053 */
3054 if (getConcurrentRequestCount() == 0 && commIsHalfClosed(clientConnection->fd)) {
3055 debugs(33, 5, clientConnection << ": half-closed connection, no completed request parsed, connection closing.");
3056 clientConnection->close();
3057 return;
3058 }
3059 }
3060
3061 if (!isOpen())
3062 return;
3063
3064 clientAfterReadingRequests();
3065 }
3066
3067 /**
3068 * called when new request data has been read from the socket
3069 *
3070 * \retval false called comm_close or setReplyToError (the caller should bail)
3071 * \retval true we did not call comm_close or setReplyToError
3072 */
3073 bool
3074 ConnStateData::handleReadData()
3075 {
3076 // if we are reading a body, stuff data into the body pipe
3077 if (bodyPipe != NULL)
3078 return handleRequestBodyData();
3079 return true;
3080 }
3081
3082 /**
3083 * called when new request body data has been buffered in inBuf
3084 * may close the connection if we were closing and piped everything out
3085 *
3086 * \retval false called comm_close or setReplyToError (the caller should bail)
3087 * \retval true we did not call comm_close or setReplyToError
3088 */
3089 bool
3090 ConnStateData::handleRequestBodyData()
3091 {
3092 assert(bodyPipe != NULL);
3093
3094 if (bodyParser) { // chunked encoding
3095 if (const err_type error = handleChunkedRequestBody()) {
3096 abortChunkedRequestBody(error);
3097 return false;
3098 }
3099 } else { // identity encoding
3100 debugs(33,5, HERE << "handling plain request body for " << clientConnection);
3101 const size_t putSize = bodyPipe->putMoreData(inBuf.c_str(), inBuf.length());
3102 if (putSize > 0)
3103 consumeInput(putSize);
3104
3105 if (!bodyPipe->mayNeedMoreData()) {
3106 // BodyPipe will clear us automagically when we produced everything
3107 bodyPipe = NULL;
3108 }
3109 }
3110
3111 if (!bodyPipe) {
3112 debugs(33,5, HERE << "produced entire request body for " << clientConnection);
3113
3114 if (const char *reason = stoppedSending()) {
3115 /* we've finished reading like good clients,
3116 * now do the close that initiateClose initiated.
3117 */
3118 debugs(33, 3, HERE << "closing for earlier sending error: " << reason);
3119 clientConnection->close();
3120 return false;
3121 }
3122 }
3123
3124 return true;
3125 }
3126
3127 /// parses available chunked encoded body bytes, checks size, returns errors
3128 err_type
3129 ConnStateData::handleChunkedRequestBody()
3130 {
3131 debugs(33, 7, "chunked from " << clientConnection << ": " << inBuf.length());
3132
3133 try { // the parser will throw on errors
3134
3135 if (inBuf.isEmpty()) // nothing to do
3136 return ERR_NONE;
3137
3138 BodyPipeCheckout bpc(*bodyPipe);
3139 bodyParser->setPayloadBuffer(&bpc.buf);
3140 const bool parsed = bodyParser->parse(inBuf);
3141 inBuf = bodyParser->remaining(); // sync buffers
3142 bpc.checkIn();
3143
3144 // dechunk then check: the size limit applies to _dechunked_ content
3145 if (clientIsRequestBodyTooLargeForPolicy(bodyPipe->producedSize()))
3146 return ERR_TOO_BIG;
3147
3148 if (parsed) {
3149 finishDechunkingRequest(true);
3150 Must(!bodyPipe);
3151 return ERR_NONE; // nil bodyPipe implies body end for the caller
3152 }
3153
3154 // if chunk parser needs data, then the body pipe must need it too
3155 Must(!bodyParser->needsMoreData() || bodyPipe->mayNeedMoreData());
3156
3157 // if parser needs more space and we can consume nothing, we will stall
3158 Must(!bodyParser->needsMoreSpace() || bodyPipe->buf().hasContent());
3159 } catch (...) { // TODO: be more specific
3160 debugs(33, 3, HERE << "malformed chunks" << bodyPipe->status());
3161 return ERR_INVALID_REQ;
3162 }
3163
3164 debugs(33, 7, HERE << "need more chunked data" << *bodyPipe->status());
3165 return ERR_NONE;
3166 }
3167
3168 /// quit on errors related to chunked request body handling
3169 void
3170 ConnStateData::abortChunkedRequestBody(const err_type error)
3171 {
3172 finishDechunkingRequest(false);
3173
3174 // XXX: The code below works if we fail during initial request parsing,
3175 // but if we fail when the server connection is used already, the server may send
3176 // us its response too, causing various assertions. How to prevent that?
3177 #if WE_KNOW_HOW_TO_SEND_ERRORS
3178 ClientSocketContext::Pointer context = getCurrentContext();
3179 if (context != NULL && !context->http->out.offset) { // output nothing yet
3180 clientStreamNode *node = context->getClientReplyContext();
3181 clientReplyContext *repContext = dynamic_cast<clientReplyContext*>(node->data.getRaw());
3182 assert(repContext);
3183 const Http::StatusCode scode = (error == ERR_TOO_BIG) ?
3184 Http::scPayloadTooLarge : HTTP_BAD_REQUEST;
3185 repContext->setReplyToError(error, scode,
3186 repContext->http->request->method,
3187 repContext->http->uri,
3188 CachePeer,
3189 repContext->http->request,
3190 inBuf, NULL);
3191 context->pullData();
3192 } else {
3193 // close or otherwise we may get stuck as nobody will notice the error?
3194 comm_reset_close(clientConnection);
3195 }
3196 #else
3197 debugs(33, 3, HERE << "aborting chunked request without error " << error);
3198 comm_reset_close(clientConnection);
3199 #endif
3200 flags.readMore = false;
3201 }
3202
3203 void
3204 ConnStateData::noteBodyConsumerAborted(BodyPipe::Pointer )
3205 {
3206 // request reader may get stuck waiting for space if nobody consumes body
3207 if (bodyPipe != NULL)
3208 bodyPipe->enableAutoConsumption();
3209
3210 // kids extend
3211 }
3212
3213 /** general lifetime handler for HTTP requests */
3214 void
3215 ConnStateData::requestTimeout(const CommTimeoutCbParams &io)
3216 {
3217 if (!Comm::IsConnOpen(io.conn))
3218 return;
3219
3220 if (Config.accessList.on_unsupported_protocol && !receivedFirstByte_) {
3221 #if USE_OPENSSL
3222 if (serverBump() && (serverBump()->act.step1 == Ssl::bumpPeek || serverBump()->act.step1 == Ssl::bumpStare)) {
3223 if (spliceOnError(ERR_REQUEST_START_TIMEOUT)) {
3224 receivedFirstByte();
3225 return;
3226 }
3227 } else if (fd_table[io.conn->fd].ssl == NULL)
3228 #endif
3229 {
3230 const HttpRequestMethod method;
3231 if (clientTunnelOnError(this, NULL, NULL, method, ERR_REQUEST_START_TIMEOUT, Http::scNone, NULL)) {
3232 // Tunnel established. Set receivedFirstByte to avoid loop.
3233 receivedFirstByte();
3234 return;
3235 }
3236 }
3237 }
3238 /*
3239 * Just close the connection to not confuse browsers
3240 * using persistent connections. Some browsers open
3241 * a connection and then do not use it until much
3242 * later (presumeably because the request triggering
3243 * the open has already been completed on another
3244 * connection)
3245 */
3246 debugs(33, 3, "requestTimeout: FD " << io.fd << ": lifetime is expired.");
3247 io.conn->close();
3248 }
3249
3250 static void
3251 clientLifetimeTimeout(const CommTimeoutCbParams &io)
3252 {
3253 ClientHttpRequest *http = static_cast<ClientHttpRequest *>(io.data);
3254 debugs(33, DBG_IMPORTANT, "WARNING: Closing client connection due to lifetime timeout");
3255 debugs(33, DBG_IMPORTANT, "\t" << http->uri);
3256 http->logType.err.timedout = true;
3257 if (Comm::IsConnOpen(io.conn))
3258 io.conn->close();
3259 }
3260
3261 ConnStateData::ConnStateData(const MasterXaction::Pointer &xact) :
3262 AsyncJob("ConnStateData"), // kids overwrite
3263 Server(xact),
3264 bodyParser(nullptr),
3265 #if USE_OPENSSL
3266 sslBumpMode(Ssl::bumpEnd),
3267 #endif
3268 needProxyProtocolHeader_(false),
3269 #if USE_OPENSSL
3270 switchedToHttps_(false),
3271 sslServerBump(NULL),
3272 signAlgorithm(Ssl::algSignTrusted),
3273 #endif
3274 stoppedSending_(NULL),
3275 stoppedReceiving_(NULL)
3276 {
3277 flags.readMore = true; // kids may overwrite
3278 flags.swanSang = false;
3279
3280 pinning.host = NULL;
3281 pinning.port = -1;
3282 pinning.pinned = false;
3283 pinning.auth = false;
3284 pinning.zeroReply = false;
3285 pinning.peer = NULL;
3286
3287 // store the details required for creating more MasterXaction objects as new requests come in
3288 log_addr = xact->tcpClient->remote;
3289 log_addr.applyMask(Config.Addrs.client_netmask);
3290
3291 // register to receive notice of Squid signal events
3292 // which may affect long persisting client connections
3293 RegisterRunner(this);
3294 }
3295
3296 void
3297 ConnStateData::start()
3298 {
3299 BodyProducer::start();
3300 HttpControlMsgSink::start();
3301
3302 if (port->disable_pmtu_discovery != DISABLE_PMTU_OFF &&
3303 (transparent() || port->disable_pmtu_discovery == DISABLE_PMTU_ALWAYS)) {
3304 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
3305 int i = IP_PMTUDISC_DONT;
3306 if (setsockopt(clientConnection->fd, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0)
3307 debugs(33, 2, "WARNING: Path MTU discovery disabling failed on " << clientConnection << " : " << xstrerror());
3308 #else
3309 static bool reported = false;
3310
3311 if (!reported) {
3312 debugs(33, DBG_IMPORTANT, "NOTICE: Path MTU discovery disabling is not supported on your platform.");
3313 reported = true;
3314 }
3315 #endif
3316 }
3317
3318 typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
3319 AsyncCall::Pointer call = JobCallback(33, 5, Dialer, this, ConnStateData::connStateClosed);
3320 comm_add_close_handler(clientConnection->fd, call);
3321
3322 if (Config.onoff.log_fqdn)
3323 fqdncache_gethostbyaddr(clientConnection->remote, FQDN_LOOKUP_IF_MISS);
3324
3325 #if USE_IDENT
3326 if (Ident::TheConfig.identLookup) {
3327 ACLFilledChecklist identChecklist(Ident::TheConfig.identLookup, NULL, NULL);
3328 identChecklist.src_addr = clientConnection->remote;
3329 identChecklist.my_addr = clientConnection->local;
3330 if (identChecklist.fastCheck() == ACCESS_ALLOWED)
3331 Ident::Start(clientConnection, clientIdentDone, this);
3332 }
3333 #endif
3334
3335 clientdbEstablished(clientConnection->remote, 1);
3336
3337 needProxyProtocolHeader_ = port->flags.proxySurrogate;
3338 if (needProxyProtocolHeader_) {
3339 if (!proxyProtocolValidateClient()) // will close the connection on failure
3340 return;
3341 }
3342
3343 #if USE_DELAY_POOLS
3344 fd_table[clientConnection->fd].clientInfo = NULL;
3345
3346 if (Config.onoff.client_db) {
3347 /* it was said several times that client write limiter does not work if client_db is disabled */
3348
3349 ClientDelayPools& pools(Config.ClientDelay.pools);
3350 ACLFilledChecklist ch(NULL, NULL, NULL);
3351
3352 // TODO: we check early to limit error response bandwith but we
3353 // should recheck when we can honor delay_pool_uses_indirect
3354 // TODO: we should also pass the port details for myportname here.
3355 ch.src_addr = clientConnection->remote;
3356 ch.my_addr = clientConnection->local;
3357
3358 for (unsigned int pool = 0; pool < pools.size(); ++pool) {
3359
3360 /* pools require explicit 'allow' to assign a client into them */
3361 if (pools[pool].access) {
3362 ch.changeAcl(pools[pool].access);
3363 allow_t answer = ch.fastCheck();
3364 if (answer == ACCESS_ALLOWED) {
3365
3366 /* request client information from db after we did all checks
3367 this will save hash lookup if client failed checks */
3368 ClientInfo * cli = clientdbGetInfo(clientConnection->remote);
3369 assert(cli);
3370
3371 /* put client info in FDE */
3372 fd_table[clientConnection->fd].clientInfo = cli;
3373
3374 /* setup write limiter for this request */
3375 const double burst = floor(0.5 +
3376 (pools[pool].highwatermark * Config.ClientDelay.initial)/100.0);
3377 cli->setWriteLimiter(pools[pool].rate, burst, pools[pool].highwatermark);
3378 break;
3379 } else {
3380 debugs(83, 4, HERE << "Delay pool " << pool << " skipped because ACL " << answer);
3381 }
3382 }
3383 }
3384 }
3385 #endif
3386
3387 // kids must extend to actually start doing something (e.g., reading)
3388 }
3389
3390 /** Handle a new connection on an HTTP socket. */
3391 void
3392 httpAccept(const CommAcceptCbParams &params)
3393 {
3394 MasterXaction::Pointer xact = params.xaction;
3395 AnyP::PortCfgPointer s = xact->squidPort;
3396
3397 // NP: it is possible the port was reconfigured when the call or accept() was queued.
3398
3399 if (params.flag != Comm::OK) {
3400 // Its possible the call was still queued when the client disconnected
3401 debugs(33, 2, s->listenConn << ": accept failure: " << xstrerr(params.xerrno));
3402 return;
3403 }
3404
3405 debugs(33, 4, params.conn << ": accepted");
3406 fd_note(params.conn->fd, "client http connect");
3407
3408 if (s->tcp_keepalive.enabled)
3409 commSetTcpKeepalive(params.conn->fd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
3410
3411 ++incoming_sockets_accepted;
3412
3413 // Socket is ready, setup the connection manager to start using it
3414 ConnStateData *connState = Http::NewServer(xact);
3415 AsyncJob::Start(connState); // usually async-calls readSomeData()
3416 }
3417
3418 #if USE_OPENSSL
3419
3420 /** Create SSL connection structure and update fd_table */
3421 static Security::SessionPointer
3422 httpsCreate(const Comm::ConnectionPointer &conn, Security::ContextPointer sslContext)
3423 {
3424 if (auto ssl = Ssl::CreateServer(sslContext, conn->fd, "client https start")) {
3425 debugs(33, 5, "will negotate SSL on " << conn);
3426 return ssl;
3427 }
3428
3429 conn->close();
3430 return nullptr;
3431 }
3432
3433 /**
3434 *
3435 * \retval 1 on success
3436 * \retval 0 when needs more data
3437 * \retval -1 on error
3438 */
3439 static int
3440 Squid_SSL_accept(ConnStateData *conn, PF *callback)
3441 {
3442 int fd = conn->clientConnection->fd;
3443 auto ssl = fd_table[fd].ssl;
3444 int ret;
3445
3446 errno = 0;
3447 if ((ret = SSL_accept(ssl)) <= 0) {
3448 const int xerrno = errno;
3449 const int ssl_error = SSL_get_error(ssl, ret);
3450
3451 switch (ssl_error) {
3452
3453 case SSL_ERROR_WANT_READ:
3454 Comm::SetSelect(fd, COMM_SELECT_READ, callback, conn, 0);
3455 return 0;
3456
3457 case SSL_ERROR_WANT_WRITE:
3458 Comm::SetSelect(fd, COMM_SELECT_WRITE, callback, conn, 0);
3459 return 0;
3460
3461 case SSL_ERROR_SYSCALL:
3462 if (ret == 0) {
3463 debugs(83, 2, "Error negotiating SSL connection on FD " << fd << ": Aborted by client: " << ssl_error);
3464 } else {
3465 debugs(83, (xerrno == ECONNRESET) ? 1 : 2, "Error negotiating SSL connection on FD " << fd << ": " <<
3466 (xerrno == 0 ? ERR_error_string(ssl_error, NULL) : xstrerr(xerrno)));
3467 }
3468 return -1;
3469
3470 case SSL_ERROR_ZERO_RETURN:
3471 debugs(83, DBG_IMPORTANT, "Error negotiating SSL connection on FD " << fd << ": Closed by client");
3472 return -1;
3473
3474 default:
3475 debugs(83, DBG_IMPORTANT, "Error negotiating SSL connection on FD " <<
3476 fd << ": " << ERR_error_string(ERR_get_error(), NULL) <<
3477 " (" << ssl_error << "/" << ret << ")");
3478 return -1;
3479 }
3480
3481 /* NOTREACHED */
3482 }
3483 return 1;
3484 }
3485
3486 /** negotiate an SSL connection */
3487 static void
3488 clientNegotiateSSL(int fd, void *data)
3489 {
3490 ConnStateData *conn = (ConnStateData *)data;
3491 X509 *client_cert;
3492 auto ssl = fd_table[fd].ssl;
3493
3494 int ret;
3495 if ((ret = Squid_SSL_accept(conn, clientNegotiateSSL)) <= 0) {
3496 if (ret < 0) // An error
3497 conn->clientConnection->close();
3498 return;
3499 }
3500
3501 if (SSL_session_reused(ssl)) {
3502 debugs(83, 2, "clientNegotiateSSL: Session " << SSL_get_session(ssl) <<
3503 " reused on FD " << fd << " (" << fd_table[fd].ipaddr << ":" << (int)fd_table[fd].remote_port << ")");
3504 } else {
3505 if (do_debug(83, 4)) {
3506 /* Write out the SSL session details.. actually the call below, but
3507 * OpenSSL headers do strange typecasts confusing GCC.. */
3508 /* PEM_write_SSL_SESSION(debug_log, SSL_get_session(ssl)); */
3509 #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00908000L
3510 PEM_ASN1_write((i2d_of_void *)i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL);
3511
3512 #elif (ALLOW_ALWAYS_SSL_SESSION_DETAIL == 1)
3513
3514 /* When using gcc 3.3.x and OpenSSL 0.9.7x sometimes a compile error can occur here.
3515 * This is caused by an unpredicatble gcc behaviour on a cast of the first argument
3516 * of PEM_ASN1_write(). For this reason this code section is disabled. To enable it,
3517 * define ALLOW_ALWAYS_SSL_SESSION_DETAIL=1.
3518 * Because there are two possible usable cast, if you get an error here, try the other
3519 * commented line. */
3520
3521 PEM_ASN1_write((int(*)())i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL);
3522 /* PEM_ASN1_write((int(*)(...))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL); */
3523
3524 #else
3525
3526 debugs(83, 4, "With " OPENSSL_VERSION_TEXT ", session details are available only defining ALLOW_ALWAYS_SSL_SESSION_DETAIL=1 in the source." );
3527
3528 #endif
3529 /* Note: This does not automatically fflush the log file.. */
3530 }
3531
3532 debugs(83, 2, "clientNegotiateSSL: New session " <<
3533 SSL_get_session(ssl) << " on FD " << fd << " (" <<
3534 fd_table[fd].ipaddr << ":" << (int)fd_table[fd].remote_port <<
3535 ")");
3536 }
3537
3538 debugs(83, 3, "clientNegotiateSSL: FD " << fd << " negotiated cipher " <<
3539 SSL_get_cipher(ssl));
3540
3541 client_cert = SSL_get_peer_certificate(ssl);
3542
3543 if (client_cert != NULL) {
3544 debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
3545 " client certificate: subject: " <<
3546 X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0));
3547
3548 debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
3549 " client certificate: issuer: " <<
3550 X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0));
3551
3552 X509_free(client_cert);
3553 } else {
3554 debugs(83, 5, "clientNegotiateSSL: FD " << fd <<
3555 " has no certificate.");
3556 }
3557
3558 #if defined(TLSEXT_NAMETYPE_host_name)
3559 if (!conn->serverBump()) {
3560 // when in bumpClientFirst mode, get the server name from SNI
3561 if (const char *server = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))
3562 conn->resetSslCommonName(server);
3563 }
3564 #endif
3565
3566 conn->readSomeData();
3567 }
3568
3569 /**
3570 * If Security::ContextPointer is given, starts reading the TLS handshake.
3571 * Otherwise, calls switchToHttps to generate a dynamic Security::ContextPointer.
3572 */
3573 static void
3574 httpsEstablish(ConnStateData *connState, Security::ContextPointer sslContext)
3575 {
3576 Security::SessionPointer ssl = nullptr;
3577 assert(connState);
3578 const Comm::ConnectionPointer &details = connState->clientConnection;
3579
3580 if (!sslContext || !(ssl = httpsCreate(details, sslContext)))
3581 return;
3582
3583 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
3584 AsyncCall::Pointer timeoutCall = JobCallback(33, 5, TimeoutDialer,
3585 connState, ConnStateData::requestTimeout);
3586 commSetConnTimeout(details, Config.Timeout.request, timeoutCall);
3587
3588 Comm::SetSelect(details->fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
3589 }
3590
3591 /**
3592 * A callback function to use with the ACLFilledChecklist callback.
3593 * In the case of ACCESS_ALLOWED answer initializes a bumped SSL connection,
3594 * else reverts the connection to tunnel mode.
3595 */
3596 static void
3597 httpsSslBumpAccessCheckDone(allow_t answer, void *data)
3598 {
3599 ConnStateData *connState = (ConnStateData *) data;
3600
3601 // if the connection is closed or closing, just return.
3602 if (!connState->isOpen())
3603 return;
3604
3605 // Require both a match and a positive bump mode to work around exceptional
3606 // cases where ACL code may return ACCESS_ALLOWED with zero answer.kind.
3607 if (answer == ACCESS_ALLOWED && (answer.kind != Ssl::bumpNone && answer.kind != Ssl::bumpSplice)) {
3608 debugs(33, 2, "sslBump needed for " << connState->clientConnection << " method " << answer.kind);
3609 connState->sslBumpMode = static_cast<Ssl::BumpMode>(answer.kind);
3610 } else {
3611 debugs(33, 2, HERE << "sslBump not needed for " << connState->clientConnection);
3612 connState->sslBumpMode = Ssl::bumpNone;
3613 }
3614 connState->fakeAConnectRequest("ssl-bump", connState->inBuf);
3615 }
3616
3617 /** handle a new HTTPS connection */
3618 static void
3619 httpsAccept(const CommAcceptCbParams &params)
3620 {
3621 MasterXaction::Pointer xact = params.xaction;
3622 const AnyP::PortCfgPointer s = xact->squidPort;
3623
3624 // NP: it is possible the port was reconfigured when the call or accept() was queued.
3625
3626 if (params.flag != Comm::OK) {
3627 // Its possible the call was still queued when the client disconnected
3628 debugs(33, 2, "httpsAccept: " << s->listenConn << ": accept failure: " << xstrerr(params.xerrno));
3629 return;
3630 }
3631
3632 debugs(33, 4, HERE << params.conn << " accepted, starting SSL negotiation.");
3633 fd_note(params.conn->fd, "client https connect");
3634
3635 if (s->tcp_keepalive.enabled) {
3636 commSetTcpKeepalive(params.conn->fd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
3637 }
3638
3639 ++incoming_sockets_accepted;
3640
3641 // Socket is ready, setup the connection manager to start using it
3642 ConnStateData *connState = Https::NewServer(xact);
3643 AsyncJob::Start(connState); // usually async-calls postHttpsAccept()
3644 }
3645
3646 void
3647 ConnStateData::postHttpsAccept()
3648 {
3649 if (port->flags.tunnelSslBumping) {
3650 debugs(33, 5, "accept transparent connection: " << clientConnection);
3651
3652 if (!Config.accessList.ssl_bump) {
3653 httpsSslBumpAccessCheckDone(ACCESS_DENIED, this);
3654 return;
3655 }
3656
3657 // Create a fake HTTP request for ssl_bump ACL check,
3658 // using tproxy/intercept provided destination IP and port.
3659 HttpRequest *request = new HttpRequest();
3660 static char ip[MAX_IPSTRLEN];
3661 assert(clientConnection->flags & (COMM_TRANSPARENT | COMM_INTERCEPTION));
3662 request->url.host(clientConnection->local.toStr(ip, sizeof(ip)));
3663 request->url.port(clientConnection->local.port());
3664 request->myportname = port->name;
3665
3666 ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(Config.accessList.ssl_bump, request, NULL);
3667 acl_checklist->src_addr = clientConnection->remote;
3668 acl_checklist->my_addr = port->s;
3669 acl_checklist->nonBlockingCheck(httpsSslBumpAccessCheckDone, this);
3670 return;
3671 } else {
3672 Security::ContextPointer sslContext = port->staticSslContext.get();
3673 httpsEstablish(this, sslContext);
3674 }
3675 }
3676
3677 void
3678 ConnStateData::sslCrtdHandleReplyWrapper(void *data, const Helper::Reply &reply)
3679 {
3680 ConnStateData * state_data = (ConnStateData *)(data);
3681 state_data->sslCrtdHandleReply(reply);
3682 }
3683
3684 void
3685 ConnStateData::sslCrtdHandleReply(const Helper::Reply &reply)
3686 {
3687 if (!isOpen()) {
3688 debugs(33, 3, "Connection gone while waiting for ssl_crtd helper reply; helper reply:" << reply);
3689 return;
3690 }
3691
3692 if (reply.result == Helper::BrokenHelper) {
3693 debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply);
3694 } else if (!reply.other().hasContent()) {
3695 debugs(1, DBG_IMPORTANT, HERE << "\"ssl_crtd\" helper returned <NULL> reply.");
3696 } else {
3697 Ssl::CrtdMessage reply_message(Ssl::CrtdMessage::REPLY);
3698 if (reply_message.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK) {
3699 debugs(33, 5, HERE << "Reply from ssl_crtd for " << sslConnectHostOrIp << " is incorrect");
3700 } else {
3701 if (reply.result != Helper::Okay) {
3702 debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply_message.getBody());
3703 } else {
3704 debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " was successfully recieved from ssl_crtd");
3705 if (sslServerBump && (sslServerBump->act.step1 == Ssl::bumpPeek || sslServerBump->act.step1 == Ssl::bumpStare)) {
3706 doPeekAndSpliceStep();
3707 auto ssl = fd_table[clientConnection->fd].ssl;
3708 bool ret = Ssl::configureSSLUsingPkeyAndCertFromMemory(ssl, reply_message.getBody().c_str(), *port);
3709 if (!ret)
3710 debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
3711 } else {
3712 auto ctx = Ssl::generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str(), *port);
3713 getSslContextDone(ctx, true);
3714 }
3715 return;
3716 }
3717 }
3718 }
3719 getSslContextDone(NULL);
3720 }
3721
3722 void ConnStateData::buildSslCertGenerationParams(Ssl::CertificateProperties &certProperties)
3723 {
3724 certProperties.commonName = sslCommonName_.isEmpty() ? sslConnectHostOrIp.termedBuf() : sslCommonName_.c_str();
3725
3726 // fake certificate adaptation requires bump-server-first mode
3727 if (!sslServerBump) {
3728 assert(port->signingCert.get());
3729 certProperties.signWithX509.resetAndLock(port->signingCert.get());
3730 if (port->signPkey.get())
3731 certProperties.signWithPkey.resetAndLock(port->signPkey.get());
3732 certProperties.signAlgorithm = Ssl::algSignTrusted;
3733 return;
3734 }
3735
3736 // In case of an error while connecting to the secure server, use a fake
3737 // trusted certificate, with no mimicked fields and no adaptation
3738 // algorithms. There is nothing we can mimic so we want to minimize the
3739 // number of warnings the user will have to see to get to the error page.
3740 assert(sslServerBump->entry);
3741 if (sslServerBump->entry->isEmpty()) {
3742 if (X509 *mimicCert = sslServerBump->serverCert.get())
3743 certProperties.mimicCert.resetAndLock(mimicCert);
3744
3745 ACLFilledChecklist checklist(NULL, sslServerBump->request.getRaw(),
3746 clientConnection != NULL ? clientConnection->rfc931 : dash_str);
3747 checklist.sslErrors = cbdataReference(sslServerBump->sslErrors);
3748
3749 for (sslproxy_cert_adapt *ca = Config.ssl_client.cert_adapt; ca != NULL; ca = ca->next) {
3750 // If the algorithm already set, then ignore it.
3751 if ((ca->alg == Ssl::algSetCommonName && certProperties.setCommonName) ||
3752 (ca->alg == Ssl::algSetValidAfter && certProperties.setValidAfter) ||
3753 (ca->alg == Ssl::algSetValidBefore && certProperties.setValidBefore) )
3754 continue;
3755
3756 if (ca->aclList && checklist.fastCheck(ca->aclList) == ACCESS_ALLOWED) {
3757 const char *alg = Ssl::CertAdaptAlgorithmStr[ca->alg];
3758 const char *param = ca->param;
3759
3760 // For parameterless CN adaptation, use hostname from the
3761 // CONNECT request.
3762 if (ca->alg == Ssl::algSetCommonName) {
3763 if (!param)
3764 param = sslConnectHostOrIp.termedBuf();
3765 certProperties.commonName = param;
3766 certProperties.setCommonName = true;
3767 } else if (ca->alg == Ssl::algSetValidAfter)
3768 certProperties.setValidAfter = true;
3769 else if (ca->alg == Ssl::algSetValidBefore)
3770 certProperties.setValidBefore = true;
3771
3772 debugs(33, 5, HERE << "Matches certificate adaptation aglorithm: " <<
3773 alg << " param: " << (param ? param : "-"));
3774 }
3775 }
3776
3777 certProperties.signAlgorithm = Ssl::algSignEnd;
3778 for (sslproxy_cert_sign *sg = Config.ssl_client.cert_sign; sg != NULL; sg = sg->next) {
3779 if (sg->aclList && checklist.fastCheck(sg->aclList) == ACCESS_ALLOWED) {
3780 certProperties.signAlgorithm = (Ssl::CertSignAlgorithm)sg->alg;
3781 break;
3782 }
3783 }
3784 } else {// if (!sslServerBump->entry->isEmpty())
3785 // Use trusted certificate for a Squid-generated error
3786 // or the user would have to add a security exception
3787 // just to see the error page. We will close the connection
3788 // so that the trust is not extended to non-Squid content.
3789 certProperties.signAlgorithm = Ssl::algSignTrusted;
3790 }
3791
3792 assert(certProperties.signAlgorithm != Ssl::algSignEnd);
3793
3794 if (certProperties.signAlgorithm == Ssl::algSignUntrusted) {
3795 assert(port->untrustedSigningCert.get());
3796 certProperties.signWithX509.resetAndLock(port->untrustedSigningCert.get());
3797 certProperties.signWithPkey.resetAndLock(port->untrustedSignPkey.get());
3798 } else {
3799 assert(port->signingCert.get());
3800 certProperties.signWithX509.resetAndLock(port->signingCert.get());
3801
3802 if (port->signPkey.get())
3803 certProperties.signWithPkey.resetAndLock(port->signPkey.get());
3804 }
3805 signAlgorithm = certProperties.signAlgorithm;
3806
3807 certProperties.signHash = Ssl::DefaultSignHash;
3808 }
3809
3810 void
3811 ConnStateData::getSslContextStart()
3812 {
3813 assert(areAllContextsForThisConnection());
3814 freeAllContexts();
3815 /* careful: freeAllContexts() above frees request, host, etc. */
3816
3817 if (port->generateHostCertificates) {
3818 Ssl::CertificateProperties certProperties;
3819 buildSslCertGenerationParams(certProperties);
3820 sslBumpCertKey = certProperties.dbKey().c_str();
3821 assert(sslBumpCertKey.size() > 0 && sslBumpCertKey[0] != '\0');
3822
3823 // Disable caching for bumpPeekAndSplice mode
3824 if (!(sslServerBump && (sslServerBump->act.step1 == Ssl::bumpPeek || sslServerBump->act.step1 == Ssl::bumpStare))) {
3825 debugs(33, 5, "Finding SSL certificate for " << sslBumpCertKey << " in cache");
3826 Ssl::LocalContextStorage * ssl_ctx_cache = Ssl::TheGlobalContextStorage.getLocalStorage(port->s);
3827 Security::ContextPointer dynCtx = nullptr;
3828 Ssl::SSL_CTX_Pointer *cachedCtx = ssl_ctx_cache ? ssl_ctx_cache->get(sslBumpCertKey.termedBuf()) : NULL;
3829 if (cachedCtx && (dynCtx = cachedCtx->get())) {
3830 debugs(33, 5, "SSL certificate for " << sslBumpCertKey << " found in cache");
3831 if (Ssl::verifySslCertificate(dynCtx, certProperties)) {
3832 debugs(33, 5, "Cached SSL certificate for " << sslBumpCertKey << " is valid");
3833 getSslContextDone(dynCtx);
3834 return;
3835 } else {
3836 debugs(33, 5, "Cached SSL certificate for " << sslBumpCertKey << " is out of date. Delete this certificate from cache");
3837 if (ssl_ctx_cache)
3838 ssl_ctx_cache->del(sslBumpCertKey.termedBuf());
3839 }
3840 } else {
3841 debugs(33, 5, "SSL certificate for " << sslBumpCertKey << " haven't found in cache");
3842 }
3843 }
3844
3845 #if USE_SSL_CRTD
3846 try {
3847 debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName << " using ssl_crtd.");
3848 Ssl::CrtdMessage request_message(Ssl::CrtdMessage::REQUEST);
3849 request_message.setCode(Ssl::CrtdMessage::code_new_certificate);
3850 request_message.composeRequest(certProperties);
3851 debugs(33, 5, HERE << "SSL crtd request: " << request_message.compose().c_str());
3852 Ssl::Helper::GetInstance()->sslSubmit(request_message, sslCrtdHandleReplyWrapper, this);
3853 return;
3854 } catch (const std::exception &e) {
3855 debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtd " <<
3856 "request for " << certProperties.commonName <<
3857 " certificate: " << e.what() << "; will now block to " <<
3858 "generate that certificate.");
3859 // fall through to do blocking in-process generation.
3860 }
3861 #endif // USE_SSL_CRTD
3862
3863 debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName);
3864 if (sslServerBump && (sslServerBump->act.step1 == Ssl::bumpPeek || sslServerBump->act.step1 == Ssl::bumpStare)) {
3865 doPeekAndSpliceStep();
3866 auto ssl = fd_table[clientConnection->fd].ssl;
3867 if (!Ssl::configureSSL(ssl, certProperties, *port))
3868 debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
3869 } else {
3870 auto dynCtx = Ssl::generateSslContext(certProperties, *port);
3871 getSslContextDone(dynCtx, true);
3872 }
3873 return;
3874 }
3875 getSslContextDone(NULL);
3876 }
3877
3878 void
3879 ConnStateData::getSslContextDone(Security::ContextPointer sslContext, bool isNew)
3880 {
3881 // Try to add generated ssl context to storage.
3882 if (port->generateHostCertificates && isNew) {
3883
3884 if (signAlgorithm == Ssl::algSignTrusted) {
3885 // Add signing certificate to the certificates chain
3886 X509 *cert = port->signingCert.get();
3887 if (SSL_CTX_add_extra_chain_cert(sslContext, cert)) {
3888 // increase the certificate lock
3889 CRYPTO_add(&(cert->references),1,CRYPTO_LOCK_X509);
3890 } else {
3891 const int ssl_error = ERR_get_error();
3892 debugs(33, DBG_IMPORTANT, "WARNING: can not add signing certificate to SSL context chain: " << ERR_error_string(ssl_error, NULL));
3893 }
3894 Ssl::addChainToSslContext(sslContext, port->certsToChain.get());
3895 }
3896 //else it is self-signed or untrusted do not attrach any certificate
3897
3898 Ssl::LocalContextStorage *ssl_ctx_cache = Ssl::TheGlobalContextStorage.getLocalStorage(port->s);
3899 assert(sslBumpCertKey.size() > 0 && sslBumpCertKey[0] != '\0');
3900 if (sslContext) {
3901 if (!ssl_ctx_cache || !ssl_ctx_cache->add(sslBumpCertKey.termedBuf(), new Ssl::SSL_CTX_Pointer(sslContext))) {
3902 // If it is not in storage delete after using. Else storage deleted it.
3903 fd_table[clientConnection->fd].dynamicSslContext = sslContext;
3904 }
3905 } else {
3906 debugs(33, 2, HERE << "Failed to generate SSL cert for " << sslConnectHostOrIp);
3907 }
3908 }
3909
3910 // If generated ssl context = NULL, try to use static ssl context.
3911 if (!sslContext) {
3912 if (!port->staticSslContext) {
3913 debugs(83, DBG_IMPORTANT, "Closing SSL " << clientConnection->remote << " as lacking SSL context");
3914 clientConnection->close();
3915 return;
3916 } else {
3917 debugs(33, 5, HERE << "Using static ssl context.");
3918 sslContext = port->staticSslContext.get();
3919 }
3920 }
3921
3922 if (!httpsCreate(clientConnection, sslContext))
3923 return;
3924
3925 // bumped intercepted conns should already have Config.Timeout.request set
3926 // but forwarded connections may only have Config.Timeout.lifetime. [Re]set
3927 // to make sure the connection does not get stuck on non-SSL clients.
3928 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
3929 AsyncCall::Pointer timeoutCall = JobCallback(33, 5, TimeoutDialer,
3930 this, ConnStateData::requestTimeout);
3931 commSetConnTimeout(clientConnection, Config.Timeout.request, timeoutCall);
3932
3933 // Disable the client read handler until CachePeer selection is complete
3934 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
3935 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, clientNegotiateSSL, this, 0);
3936 switchedToHttps_ = true;
3937 }
3938
3939 void
3940 ConnStateData::switchToHttps(HttpRequest *request, Ssl::BumpMode bumpServerMode)
3941 {
3942 assert(!switchedToHttps_);
3943
3944 sslConnectHostOrIp = request->url.host();
3945 resetSslCommonName(request->url.host());
3946
3947 // We are going to read new request
3948 flags.readMore = true;
3949 debugs(33, 5, HERE << "converting " << clientConnection << " to SSL");
3950
3951 // keep version major.minor details the same.
3952 // but we are now performing the HTTPS handshake traffic
3953 transferProtocol.protocol = AnyP::PROTO_HTTPS;
3954
3955 // If sslServerBump is set, then we have decided to deny CONNECT
3956 // and now want to switch to SSL to send the error to the client
3957 // without even peeking at the origin server certificate.
3958 if (bumpServerMode == Ssl::bumpServerFirst && !sslServerBump) {
3959 request->flags.sslPeek = true;
3960 sslServerBump = new Ssl::ServerBump(request);
3961
3962 // will call httpsPeeked() with certificate and connection, eventually
3963 FwdState::fwdStart(clientConnection, sslServerBump->entry, sslServerBump->request.getRaw());
3964 return;
3965 } else if (bumpServerMode == Ssl::bumpPeek || bumpServerMode == Ssl::bumpStare) {
3966 request->flags.sslPeek = true;
3967 sslServerBump = new Ssl::ServerBump(request, NULL, bumpServerMode);
3968 startPeekAndSplice();
3969 return;
3970 }
3971
3972 // otherwise, use sslConnectHostOrIp
3973 getSslContextStart();
3974 }
3975
3976 bool
3977 ConnStateData::spliceOnError(const err_type err)
3978 {
3979 if (Config.accessList.on_unsupported_protocol) {
3980 assert(serverBump());
3981 ACLFilledChecklist checklist(Config.accessList.on_unsupported_protocol, serverBump()->request.getRaw(), NULL);
3982 checklist.requestErrorType = err;
3983 checklist.conn(this);
3984 allow_t answer = checklist.fastCheck();
3985 if (answer == ACCESS_ALLOWED && answer.kind == 1) {
3986 splice();
3987 return true;
3988 }
3989 }
3990 return false;
3991 }
3992
3993 /** negotiate an SSL connection */
3994 static void
3995 clientPeekAndSpliceSSL(int fd, void *data)
3996 {
3997 ConnStateData *conn = (ConnStateData *)data;
3998 auto ssl = fd_table[fd].ssl;
3999
4000 debugs(83, 5, "Start peek and splice on FD " << fd);
4001
4002 int ret = 0;
4003 if ((ret = Squid_SSL_accept(conn, clientPeekAndSpliceSSL)) < 0)
4004 debugs(83, 2, "SSL_accept failed.");
4005
4006 BIO *b = SSL_get_rbio(ssl);
4007 assert(b);
4008 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
4009 if (ret < 0) {
4010 const err_type err = bio->noSslClient() ? ERR_PROTOCOL_UNKNOWN : ERR_SECURE_ACCEPT_FAIL;
4011 if (!conn->spliceOnError(err))
4012 conn->clientConnection->close();
4013 return;
4014 }
4015
4016 if (bio->rBufData().contentSize() > 0)
4017 conn->receivedFirstByte();
4018
4019 if (bio->gotHello()) {
4020 if (conn->serverBump()) {
4021 Ssl::Bio::sslFeatures const &features = bio->getFeatures();
4022 if (!features.serverName.isEmpty()) {
4023 conn->serverBump()->clientSni = features.serverName;
4024 conn->resetSslCommonName(features.serverName.c_str());
4025 }
4026 }
4027
4028 debugs(83, 5, "I got hello. Start forwarding the request!!! ");
4029 Comm::SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
4030 Comm::SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
4031 conn->startPeekAndSpliceDone();
4032 return;
4033 }
4034 }
4035
4036 void ConnStateData::startPeekAndSplice()
4037 {
4038 // will call httpsPeeked() with certificate and connection, eventually
4039 auto unConfiguredCTX = Ssl::createSSLContext(port->signingCert, port->signPkey, *port);
4040 fd_table[clientConnection->fd].dynamicSslContext = unConfiguredCTX;
4041
4042 if (!httpsCreate(clientConnection, unConfiguredCTX))
4043 return;
4044
4045 // commSetConnTimeout() was called for this request before we switched.
4046 // Fix timeout to request_start_timeout
4047 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
4048 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
4049 TimeoutDialer, this, ConnStateData::requestTimeout);
4050 commSetConnTimeout(clientConnection, Config.Timeout.request_start_timeout, timeoutCall);
4051 // Also reset receivedFirstByte_ flag to allow this timeout work in the case we have
4052 // a bumbed "connect" request on non transparent port.
4053 receivedFirstByte_ = false;
4054
4055 // Disable the client read handler until CachePeer selection is complete
4056 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
4057 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, clientPeekAndSpliceSSL, this, 0);
4058 switchedToHttps_ = true;
4059
4060 auto ssl = fd_table[clientConnection->fd].ssl;
4061 BIO *b = SSL_get_rbio(ssl);
4062 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
4063 bio->hold(true);
4064 }
4065
4066 void httpsSslBumpStep2AccessCheckDone(allow_t answer, void *data)
4067 {
4068 ConnStateData *connState = (ConnStateData *) data;
4069
4070 // if the connection is closed or closing, just return.
4071 if (!connState->isOpen())
4072 return;
4073
4074 debugs(33, 5, "Answer: " << answer << " kind:" << answer.kind);
4075 assert(connState->serverBump());
4076 Ssl::BumpMode bumpAction;
4077 if (answer == ACCESS_ALLOWED) {
4078 bumpAction = (Ssl::BumpMode)answer.kind;
4079 } else
4080 bumpAction = Ssl::bumpSplice;
4081
4082 connState->serverBump()->act.step2 = bumpAction;
4083 connState->sslBumpMode = bumpAction;
4084
4085 if (bumpAction == Ssl::bumpTerminate) {
4086 connState->clientConnection->close();
4087 } else if (bumpAction != Ssl::bumpSplice) {
4088 connState->startPeekAndSpliceDone();
4089 } else
4090 connState->splice();
4091 }
4092
4093 void
4094 ConnStateData::splice()
4095 {
4096 //Normally we can splice here, because we just got client hello message
4097 auto ssl = fd_table[clientConnection->fd].ssl;
4098 BIO *b = SSL_get_rbio(ssl);
4099 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
4100 MemBuf const &rbuf = bio->rBufData();
4101 debugs(83,5, "Bio for " << clientConnection << " read " << rbuf.contentSize() << " helo bytes");
4102 // Do splice:
4103 fd_table[clientConnection->fd].read_method = &default_read_method;
4104 fd_table[clientConnection->fd].write_method = &default_write_method;
4105
4106 if (transparent()) {
4107 // set the current protocol to something sensible (was "HTTPS" for the bumping process)
4108 // we are sending a faked-up HTTP/1.1 message wrapper, so go with that.
4109 transferProtocol = Http::ProtocolVersion();
4110 // XXX: copy from MemBuf reallocates, not a regression since old code did too
4111 SBuf temp;
4112 temp.append(rbuf.content(), rbuf.contentSize());
4113 fakeAConnectRequest("intercepted TLS spliced", temp);
4114 } else {
4115 // XXX: assuming that there was an HTTP/1.1 CONNECT to begin with...
4116
4117 // reset the current protocol to HTTP/1.1 (was "HTTPS" for the bumping process)
4118 transferProtocol = Http::ProtocolVersion();
4119 // inBuf still has the "CONNECT ..." request data, reset it to SSL hello message
4120 inBuf.append(rbuf.content(), rbuf.contentSize());
4121 ClientSocketContext::Pointer context = getCurrentContext();
4122 ClientHttpRequest *http = context->http;
4123 tunnelStart(http);
4124 }
4125 }
4126
4127 void
4128 ConnStateData::startPeekAndSpliceDone()
4129 {
4130 // This is the Step2 of the SSL bumping
4131 assert(sslServerBump);
4132 if (sslServerBump->step == Ssl::bumpStep1) {
4133 sslServerBump->step = Ssl::bumpStep2;
4134 // Run a accessList check to check if want to splice or continue bumping
4135
4136 ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(Config.accessList.ssl_bump, sslServerBump->request.getRaw(), NULL);
4137 //acl_checklist->src_addr = params.conn->remote;
4138 //acl_checklist->my_addr = s->s;
4139 acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpNone));
4140 acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpClientFirst));
4141 acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpServerFirst));
4142 acl_checklist->nonBlockingCheck(httpsSslBumpStep2AccessCheckDone, this);
4143 return;
4144 }
4145
4146 FwdState::fwdStart(clientConnection, sslServerBump->entry, sslServerBump->request.getRaw());
4147 }
4148
4149 void
4150 ConnStateData::doPeekAndSpliceStep()
4151 {
4152 auto ssl = fd_table[clientConnection->fd].ssl;
4153 BIO *b = SSL_get_rbio(ssl);
4154 assert(b);
4155 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
4156
4157 debugs(33, 5, "PeekAndSplice mode, proceed with client negotiation. Currrent state:" << SSL_state_string_long(ssl));
4158 bio->hold(false);
4159
4160 Comm::SetSelect(clientConnection->fd, COMM_SELECT_WRITE, clientNegotiateSSL, this, 0);
4161 switchedToHttps_ = true;
4162 }
4163
4164 void
4165 ConnStateData::httpsPeeked(Comm::ConnectionPointer serverConnection)
4166 {
4167 Must(sslServerBump != NULL);
4168
4169 if (Comm::IsConnOpen(serverConnection)) {
4170 pinConnection(serverConnection, NULL, NULL, false);
4171
4172 debugs(33, 5, HERE << "bumped HTTPS server: " << sslConnectHostOrIp);
4173 } else {
4174 debugs(33, 5, HERE << "Error while bumping: " << sslConnectHostOrIp);
4175
4176 // copy error detail from bump-server-first request to CONNECT request
4177 if (currentobject != NULL && currentobject->http != NULL && currentobject->http->request)
4178 currentobject->http->request->detailError(sslServerBump->request->errType, sslServerBump->request->errDetail);
4179 }
4180
4181 getSslContextStart();
4182 }
4183
4184 #endif /* USE_OPENSSL */
4185
4186 void
4187 ConnStateData::fakeAConnectRequest(const char *reason, const SBuf &payload)
4188 {
4189 // fake a CONNECT request to force connState to tunnel
4190 SBuf connectHost;
4191 #if USE_OPENSSL
4192 if (serverBump() && !serverBump()->clientSni.isEmpty()) {
4193 connectHost.assign(serverBump()->clientSni);
4194 if (clientConnection->local.port() > 0)
4195 connectHost.appendf(":%d",clientConnection->local.port());
4196 } else
4197 #endif
4198 {
4199 static char ip[MAX_IPSTRLEN];
4200 connectHost.assign(clientConnection->local.toUrl(ip, sizeof(ip)));
4201 }
4202 // Pre-pend this fake request to the TLS bits already in the buffer
4203 SBuf retStr;
4204 retStr.append("CONNECT ");
4205 retStr.append(connectHost);
4206 retStr.append(" HTTP/1.1\r\nHost: ");
4207 retStr.append(connectHost);
4208 retStr.append("\r\n\r\n");
4209 retStr.append(payload);
4210 inBuf = retStr;
4211 bool ret = handleReadData();
4212 if (ret)
4213 ret = clientParseRequests();
4214
4215 if (!ret) {
4216 debugs(33, 2, "Failed to start fake CONNECT request for " << reason << " connection: " << clientConnection);
4217 clientConnection->close();
4218 }
4219 }
4220
4221 /// check FD after clientHttp[s]ConnectionOpened, adjust HttpSockets as needed
4222 static bool
4223 OpenedHttpSocket(const Comm::ConnectionPointer &c, const Ipc::FdNoteId portType)
4224 {
4225 if (!Comm::IsConnOpen(c)) {
4226 Must(NHttpSockets > 0); // we tried to open some
4227 --NHttpSockets; // there will be fewer sockets than planned
4228 Must(HttpSockets[NHttpSockets] < 0); // no extra fds received
4229
4230 if (!NHttpSockets) // we could not open any listen sockets at all
4231 fatalf("Unable to open %s",FdNote(portType));
4232
4233 return false;
4234 }
4235 return true;
4236 }
4237
4238 /// find any unused HttpSockets[] slot and store fd there or return false
4239 static bool
4240 AddOpenedHttpSocket(const Comm::ConnectionPointer &conn)
4241 {
4242 bool found = false;
4243 for (int i = 0; i < NHttpSockets && !found; ++i) {
4244 if ((found = HttpSockets[i] < 0))
4245 HttpSockets[i] = conn->fd;
4246 }
4247 return found;
4248 }
4249
4250 static void
4251 clientHttpConnectionsOpen(void)
4252 {
4253 for (AnyP::PortCfgPointer s = HttpPortList; s != NULL; s = s->next) {
4254 if (MAXTCPLISTENPORTS == NHttpSockets) {
4255 debugs(1, DBG_IMPORTANT, "WARNING: You have too many 'http_port' lines.");
4256 debugs(1, DBG_IMPORTANT, " The limit is " << MAXTCPLISTENPORTS << " HTTP ports.");
4257 continue;
4258 }
4259
4260 #if USE_OPENSSL
4261 if (s->flags.tunnelSslBumping && !Config.accessList.ssl_bump) {
4262 debugs(33, DBG_IMPORTANT, "WARNING: No ssl_bump configured. Disabling ssl-bump on " << AnyP::UriScheme(s->transport.protocol) << "_port " << s->s);
4263 s->flags.tunnelSslBumping = false;
4264 }
4265
4266 if (s->flags.tunnelSslBumping &&
4267 !s->staticSslContext &&
4268 !s->generateHostCertificates) {
4269 debugs(1, DBG_IMPORTANT, "Will not bump SSL at http_port " << s->s << " due to SSL initialization failure.");
4270 s->flags.tunnelSslBumping = false;
4271 }
4272 if (s->flags.tunnelSslBumping) {
4273 // Create ssl_ctx cache for this port.
4274 Ssl::TheGlobalContextStorage.addLocalStorage(s->s, s->dynamicCertMemCacheSize == std::numeric_limits<size_t>::max() ? 4194304 : s->dynamicCertMemCacheSize);
4275 }
4276 #endif
4277
4278 // Fill out a Comm::Connection which IPC will open as a listener for us
4279 // then pass back when active so we can start a TcpAcceptor subscription.
4280 s->listenConn = new Comm::Connection;
4281 s->listenConn->local = s->s;
4282 s->listenConn->flags = COMM_NONBLOCKING | (s->flags.tproxyIntercept ? COMM_TRANSPARENT : 0) | (s->flags.natIntercept ? COMM_INTERCEPTION : 0);
4283
4284 // setup the subscriptions such that new connections accepted by listenConn are handled by HTTP
4285 typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
4286 RefCount<AcceptCall> subCall = commCbCall(5, 5, "httpAccept", CommAcceptCbPtrFun(httpAccept, CommAcceptCbParams(NULL)));
4287 Subscription::Pointer sub = new CallSubscription<AcceptCall>(subCall);
4288
4289 AsyncCall::Pointer listenCall = asyncCall(33,2, "clientListenerConnectionOpened",
4290 ListeningStartedDialer(&clientListenerConnectionOpened, s, Ipc::fdnHttpSocket, sub));
4291 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->listenConn, Ipc::fdnHttpSocket, listenCall);
4292
4293 HttpSockets[NHttpSockets] = -1; // set in clientListenerConnectionOpened
4294 ++NHttpSockets;
4295 }
4296 }
4297
4298 #if USE_OPENSSL
4299 static void
4300 clientHttpsConnectionsOpen(void)
4301 {
4302 for (AnyP::PortCfgPointer s = HttpsPortList; s != NULL; s = s->next) {
4303 if (MAXTCPLISTENPORTS == NHttpSockets) {
4304 debugs(1, DBG_IMPORTANT, "Ignoring 'https_port' lines exceeding the limit.");
4305 debugs(1, DBG_IMPORTANT, "The limit is " << MAXTCPLISTENPORTS << " HTTPS ports.");
4306 continue;
4307 }
4308
4309 if (!s->staticSslContext) {
4310 debugs(1, DBG_IMPORTANT, "Ignoring https_port " << s->s <<
4311 " due to SSL initialization failure.");
4312 continue;
4313 }
4314
4315 // TODO: merge with similar code in clientHttpConnectionsOpen()
4316 if (s->flags.tunnelSslBumping && !Config.accessList.ssl_bump) {
4317 debugs(33, DBG_IMPORTANT, "WARNING: No ssl_bump configured. Disabling ssl-bump on " << AnyP::UriScheme(s->transport.protocol) << "_port " << s->s);
4318 s->flags.tunnelSslBumping = false;
4319 }
4320
4321 if (s->flags.tunnelSslBumping && !s->staticSslContext && !s->generateHostCertificates) {
4322 debugs(1, DBG_IMPORTANT, "Will not bump SSL at https_port " << s->s << " due to SSL initialization failure.");
4323 s->flags.tunnelSslBumping = false;
4324 }
4325
4326 if (s->flags.tunnelSslBumping) {
4327 // Create ssl_ctx cache for this port.
4328 Ssl::TheGlobalContextStorage.addLocalStorage(s->s, s->dynamicCertMemCacheSize == std::numeric_limits<size_t>::max() ? 4194304 : s->dynamicCertMemCacheSize);
4329 }
4330
4331 // Fill out a Comm::Connection which IPC will open as a listener for us
4332 s->listenConn = new Comm::Connection;
4333 s->listenConn->local = s->s;
4334 s->listenConn->flags = COMM_NONBLOCKING | (s->flags.tproxyIntercept ? COMM_TRANSPARENT : 0) |
4335 (s->flags.natIntercept ? COMM_INTERCEPTION : 0);
4336
4337 // setup the subscriptions such that new connections accepted by listenConn are handled by HTTPS
4338 typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
4339 RefCount<AcceptCall> subCall = commCbCall(5, 5, "httpsAccept", CommAcceptCbPtrFun(httpsAccept, CommAcceptCbParams(NULL)));
4340 Subscription::Pointer sub = new CallSubscription<AcceptCall>(subCall);
4341
4342 AsyncCall::Pointer listenCall = asyncCall(33, 2, "clientListenerConnectionOpened",
4343 ListeningStartedDialer(&clientListenerConnectionOpened,
4344 s, Ipc::fdnHttpsSocket, sub));
4345 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->listenConn, Ipc::fdnHttpsSocket, listenCall);
4346 HttpSockets[NHttpSockets] = -1;
4347 ++NHttpSockets;
4348 }
4349 }
4350 #endif
4351
4352 void
4353 clientStartListeningOn(AnyP::PortCfgPointer &port, const RefCount< CommCbFunPtrCallT<CommAcceptCbPtrFun> > &subCall, const Ipc::FdNoteId fdNote)
4354 {
4355 // Fill out a Comm::Connection which IPC will open as a listener for us
4356 port->listenConn = new Comm::Connection;
4357 port->listenConn->local = port->s;
4358 port->listenConn->flags =
4359 COMM_NONBLOCKING |
4360 (port->flags.tproxyIntercept ? COMM_TRANSPARENT : 0) |
4361 (port->flags.natIntercept ? COMM_INTERCEPTION : 0);
4362
4363 // route new connections to subCall
4364 typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
4365 Subscription::Pointer sub = new CallSubscription<AcceptCall>(subCall);
4366 AsyncCall::Pointer listenCall =
4367 asyncCall(33, 2, "clientListenerConnectionOpened",
4368 ListeningStartedDialer(&clientListenerConnectionOpened,
4369 port, fdNote, sub));
4370 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, port->listenConn, fdNote, listenCall);
4371
4372 assert(NHttpSockets < MAXTCPLISTENPORTS);
4373 HttpSockets[NHttpSockets] = -1;
4374 ++NHttpSockets;
4375 }
4376
4377 /// process clientHttpConnectionsOpen result
4378 static void
4379 clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub)
4380 {
4381 Must(s != NULL);
4382
4383 if (!OpenedHttpSocket(s->listenConn, portTypeNote))
4384 return;
4385
4386 Must(Comm::IsConnOpen(s->listenConn));
4387
4388 // TCP: setup a job to handle accept() with subscribed handler
4389 AsyncJob::Start(new Comm::TcpAcceptor(s, FdNote(portTypeNote), sub));
4390
4391 debugs(1, DBG_IMPORTANT, "Accepting " <<
4392 (s->flags.natIntercept ? "NAT intercepted " : "") <<
4393 (s->flags.tproxyIntercept ? "TPROXY intercepted " : "") <<
4394 (s->flags.tunnelSslBumping ? "SSL bumped " : "") <<
4395 (s->flags.accelSurrogate ? "reverse-proxy " : "")
4396 << FdNote(portTypeNote) << " connections at "
4397 << s->listenConn);
4398
4399 Must(AddOpenedHttpSocket(s->listenConn)); // otherwise, we have received a fd we did not ask for
4400 }
4401
4402 void
4403 clientOpenListenSockets(void)
4404 {
4405 clientHttpConnectionsOpen();
4406 #if USE_OPENSSL
4407 clientHttpsConnectionsOpen();
4408 #endif
4409 Ftp::StartListening();
4410
4411 if (NHttpSockets < 1)
4412 fatal("No HTTP, HTTPS, or FTP ports configured");
4413 }
4414
4415 void
4416 clientConnectionsClose()
4417 {
4418 for (AnyP::PortCfgPointer s = HttpPortList; s != NULL; s = s->next) {
4419 if (s->listenConn != NULL) {
4420 debugs(1, DBG_IMPORTANT, "Closing HTTP port " << s->listenConn->local);
4421 s->listenConn->close();
4422 s->listenConn = NULL;
4423 }
4424 }
4425
4426 #if USE_OPENSSL
4427 for (AnyP::PortCfgPointer s = HttpsPortList; s != NULL; s = s->next) {
4428 if (s->listenConn != NULL) {
4429 debugs(1, DBG_IMPORTANT, "Closing HTTPS port " << s->listenConn->local);
4430 s->listenConn->close();
4431 s->listenConn = NULL;
4432 }
4433 }
4434 #endif
4435
4436 Ftp::StopListening();
4437
4438 // TODO see if we can drop HttpSockets array entirely */
4439 for (int i = 0; i < NHttpSockets; ++i) {
4440 HttpSockets[i] = -1;
4441 }
4442
4443 NHttpSockets = 0;
4444 }
4445
4446 int
4447 varyEvaluateMatch(StoreEntry * entry, HttpRequest * request)
4448 {
4449 const char *vary = request->vary_headers;
4450 int has_vary = entry->getReply()->header.has(Http::HdrType::VARY);
4451 #if X_ACCELERATOR_VARY
4452
4453 has_vary |=
4454 entry->getReply()->header.has(Http::HdrType::HDR_X_ACCELERATOR_VARY);
4455 #endif
4456
4457 if (!has_vary || !entry->mem_obj->vary_headers) {
4458 if (vary) {
4459 /* Oops... something odd is going on here.. */
4460 debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary object on second attempt, '" <<
4461 entry->mem_obj->urlXXX() << "' '" << vary << "'");
4462 safe_free(request->vary_headers);
4463 return VARY_CANCEL;
4464 }
4465
4466 if (!has_vary) {
4467 /* This is not a varying object */
4468 return VARY_NONE;
4469 }
4470
4471 /* virtual "vary" object found. Calculate the vary key and
4472 * continue the search
4473 */
4474 vary = httpMakeVaryMark(request, entry->getReply());
4475
4476 if (vary) {
4477 request->vary_headers = xstrdup(vary);
4478 return VARY_OTHER;
4479 } else {
4480 /* Ouch.. we cannot handle this kind of variance */
4481 /* XXX This cannot really happen, but just to be complete */
4482 return VARY_CANCEL;
4483 }
4484 } else {
4485 if (!vary) {
4486 vary = httpMakeVaryMark(request, entry->getReply());
4487
4488 if (vary)
4489 request->vary_headers = xstrdup(vary);
4490 }
4491
4492 if (!vary) {
4493 /* Ouch.. we cannot handle this kind of variance */
4494 /* XXX This cannot really happen, but just to be complete */
4495 return VARY_CANCEL;
4496 } else if (strcmp(vary, entry->mem_obj->vary_headers) == 0) {
4497 return VARY_MATCH;
4498 } else {
4499 /* Oops.. we have already been here and still haven't
4500 * found the requested variant. Bail out
4501 */
4502 debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary match on second attempt, '" <<
4503 entry->mem_obj->urlXXX() << "' '" << vary << "'");
4504 return VARY_CANCEL;
4505 }
4506 }
4507 }
4508
4509 ACLFilledChecklist *
4510 clientAclChecklistCreate(const acl_access * acl, ClientHttpRequest * http)
4511 {
4512 ConnStateData * conn = http->getConn();
4513 ACLFilledChecklist *ch = new ACLFilledChecklist(acl, http->request,
4514 cbdataReferenceValid(conn) && conn != NULL && conn->clientConnection != NULL ? conn->clientConnection->rfc931 : dash_str);
4515 ch->al = http->al;
4516 /*
4517 * hack for ident ACL. It needs to get full addresses, and a place to store
4518 * the ident result on persistent connections...
4519 */
4520 /* connection oriented auth also needs these two lines for it's operation. */
4521 return ch;
4522 }
4523
4524 bool
4525 ConnStateData::transparent() const
4526 {
4527 return clientConnection != NULL && (clientConnection->flags & (COMM_TRANSPARENT|COMM_INTERCEPTION));
4528 }
4529
4530 BodyPipe::Pointer
4531 ConnStateData::expectRequestBody(int64_t size)
4532 {
4533 bodyPipe = new BodyPipe(this);
4534 if (size >= 0)
4535 bodyPipe->setBodySize(size);
4536 else
4537 startDechunkingRequest();
4538 return bodyPipe;
4539 }
4540
4541 int64_t
4542 ConnStateData::mayNeedToReadMoreBody() const
4543 {
4544 if (!bodyPipe)
4545 return 0; // request without a body or read/produced all body bytes
4546
4547 if (!bodyPipe->bodySizeKnown())
4548 return -1; // probably need to read more, but we cannot be sure
4549
4550 const int64_t needToProduce = bodyPipe->unproducedSize();
4551 const int64_t haveAvailable = static_cast<int64_t>(inBuf.length());
4552
4553 if (needToProduce <= haveAvailable)
4554 return 0; // we have read what we need (but are waiting for pipe space)
4555
4556 return needToProduce - haveAvailable;
4557 }
4558
4559 void
4560 ConnStateData::stopReceiving(const char *error)
4561 {
4562 debugs(33, 4, HERE << "receiving error (" << clientConnection << "): " << error <<
4563 "; old sending error: " <<
4564 (stoppedSending() ? stoppedSending_ : "none"));
4565
4566 if (const char *oldError = stoppedReceiving()) {
4567 debugs(33, 3, HERE << "already stopped receiving: " << oldError);
4568 return; // nothing has changed as far as this connection is concerned
4569 }
4570
4571 stoppedReceiving_ = error;
4572
4573 if (const char *sendError = stoppedSending()) {
4574 debugs(33, 3, HERE << "closing because also stopped sending: " << sendError);
4575 clientConnection->close();
4576 }
4577 }
4578
4579 void
4580 ConnStateData::expectNoForwarding()
4581 {
4582 if (bodyPipe != NULL) {
4583 debugs(33, 4, HERE << "no consumer for virgin body " << bodyPipe->status());
4584 bodyPipe->expectNoConsumption();
4585 }
4586 }
4587
4588 /// initialize dechunking state
4589 void
4590 ConnStateData::startDechunkingRequest()
4591 {
4592 Must(bodyPipe != NULL);
4593 debugs(33, 5, HERE << "start dechunking" << bodyPipe->status());
4594 assert(!bodyParser);
4595 bodyParser = new Http1::TeChunkedParser;
4596 }
4597
4598 /// put parsed content into input buffer and clean up
4599 void
4600 ConnStateData::finishDechunkingRequest(bool withSuccess)
4601 {
4602 debugs(33, 5, HERE << "finish dechunking: " << withSuccess);
4603
4604 if (bodyPipe != NULL) {
4605 debugs(33, 7, HERE << "dechunked tail: " << bodyPipe->status());
4606 BodyPipe::Pointer myPipe = bodyPipe;
4607 stopProducingFor(bodyPipe, withSuccess); // sets bodyPipe->bodySize()
4608 Must(!bodyPipe); // we rely on it being nil after we are done with body
4609 if (withSuccess) {
4610 Must(myPipe->bodySizeKnown());
4611 ClientSocketContext::Pointer context = getCurrentContext();
4612 if (context != NULL && context->http && context->http->request)
4613 context->http->request->setContentLength(myPipe->bodySize());
4614 }
4615 }
4616
4617 delete bodyParser;
4618 bodyParser = NULL;
4619 }
4620
4621 void
4622 ConnStateData::sendControlMsg(HttpControlMsg msg)
4623 {
4624 if (!isOpen()) {
4625 debugs(33, 3, HERE << "ignoring 1xx due to earlier closure");
4626 return;
4627 }
4628
4629 ClientSocketContext::Pointer context = getCurrentContext();
4630 if (context != NULL) {
4631 context->writeControlMsg(msg); // will call msg.cbSuccess
4632 return;
4633 }
4634
4635 debugs(33, 3, HERE << " closing due to missing context for 1xx");
4636 clientConnection->close();
4637 }
4638
4639 /// Our close handler called by Comm when the pinned connection is closed
4640 void
4641 ConnStateData::clientPinnedConnectionClosed(const CommCloseCbParams &io)
4642 {
4643 // FwdState might repin a failed connection sooner than this close
4644 // callback is called for the failed connection.
4645 assert(pinning.serverConnection == io.conn);
4646 pinning.closeHandler = NULL; // Comm unregisters handlers before calling
4647 const bool sawZeroReply = pinning.zeroReply; // reset when unpinning
4648 pinning.serverConnection->noteClosure();
4649 unpinConnection(false);
4650
4651 if (sawZeroReply && clientConnection != NULL) {
4652 debugs(33, 3, "Closing client connection on pinned zero reply.");
4653 clientConnection->close();
4654 }
4655
4656 }
4657
4658 void
4659 ConnStateData::pinConnection(const Comm::ConnectionPointer &pinServer, HttpRequest *request, CachePeer *aPeer, bool auth, bool monitor)
4660 {
4661 if (!Comm::IsConnOpen(pinning.serverConnection) ||
4662 pinning.serverConnection->fd != pinServer->fd)
4663 pinNewConnection(pinServer, request, aPeer, auth);
4664
4665 if (monitor)
4666 startPinnedConnectionMonitoring();
4667 }
4668
4669 void
4670 ConnStateData::pinNewConnection(const Comm::ConnectionPointer &pinServer, HttpRequest *request, CachePeer *aPeer, bool auth)
4671 {
4672 unpinConnection(true); // closes pinned connection, if any, and resets fields
4673
4674 pinning.serverConnection = pinServer;
4675
4676 debugs(33, 3, HERE << pinning.serverConnection);
4677
4678 Must(pinning.serverConnection != NULL);
4679
4680 // when pinning an SSL bumped connection, the request may be NULL
4681 const char *pinnedHost = "[unknown]";
4682 if (request) {
4683 pinning.host = xstrdup(request->url.host());
4684 pinning.port = request->url.port();
4685 pinnedHost = pinning.host;
4686 } else {
4687 pinning.port = pinServer->remote.port();
4688 }
4689 pinning.pinned = true;
4690 if (aPeer)
4691 pinning.peer = cbdataReference(aPeer);
4692 pinning.auth = auth;
4693 char stmp[MAX_IPSTRLEN];
4694 char desc[FD_DESC_SZ];
4695 snprintf(desc, FD_DESC_SZ, "%s pinned connection for %s (%d)",
4696 (auth || !aPeer) ? pinnedHost : aPeer->name,
4697 clientConnection->remote.toUrl(stmp,MAX_IPSTRLEN),
4698 clientConnection->fd);
4699 fd_note(pinning.serverConnection->fd, desc);
4700
4701 typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
4702 pinning.closeHandler = JobCallback(33, 5,
4703 Dialer, this, ConnStateData::clientPinnedConnectionClosed);
4704 // remember the pinned connection so that cb does not unpin a fresher one
4705 typedef CommCloseCbParams Params;
4706 Params &params = GetCommParams<Params>(pinning.closeHandler);
4707 params.conn = pinning.serverConnection;
4708 comm_add_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
4709 }
4710
4711 /// [re]start monitoring pinned connection for peer closures so that we can
4712 /// propagate them to an _idle_ client pinned to that peer
4713 void
4714 ConnStateData::startPinnedConnectionMonitoring()
4715 {
4716 if (pinning.readHandler != NULL)
4717 return; // already monitoring
4718
4719 typedef CommCbMemFunT<ConnStateData, CommIoCbParams> Dialer;
4720 pinning.readHandler = JobCallback(33, 3,
4721 Dialer, this, ConnStateData::clientPinnedConnectionRead);
4722 Comm::Read(pinning.serverConnection, pinning.readHandler);
4723 }
4724
4725 void
4726 ConnStateData::stopPinnedConnectionMonitoring()
4727 {
4728 if (pinning.readHandler != NULL) {
4729 Comm::ReadCancel(pinning.serverConnection->fd, pinning.readHandler);
4730 pinning.readHandler = NULL;
4731 }
4732 }
4733
4734 #if USE_OPENSSL
4735 bool
4736 ConnStateData::handleIdleClientPinnedTlsRead()
4737 {
4738 // A ready-for-reading connection means that the TLS server either closed
4739 // the connection, sent us some unexpected HTTP data, or started TLS
4740 // renegotiations. We should close the connection except for the last case.
4741
4742 Must(pinning.serverConnection != nullptr);
4743 SSL *ssl = fd_table[pinning.serverConnection->fd].ssl;
4744 if (!ssl)
4745 return false;
4746
4747 char buf[1];
4748 const int readResult = SSL_read(ssl, buf, sizeof(buf));
4749
4750 if (readResult > 0 || SSL_pending(ssl) > 0) {
4751 debugs(83, 2, pinning.serverConnection << " TLS application data read");
4752 return false;
4753 }
4754
4755 switch(const int error = SSL_get_error(ssl, readResult)) {
4756 case SSL_ERROR_WANT_WRITE:
4757 debugs(83, DBG_IMPORTANT, pinning.serverConnection << " TLS SSL_ERROR_WANT_WRITE request for idle pinned connection");
4758 // fall through to restart monitoring, for now
4759 case SSL_ERROR_NONE:
4760 case SSL_ERROR_WANT_READ:
4761 startPinnedConnectionMonitoring();
4762 return true;
4763
4764 default:
4765 debugs(83, 2, pinning.serverConnection << " TLS error: " << error);
4766 return false;
4767 }
4768
4769 // not reached
4770 return true;
4771 }
4772 #endif
4773
4774 /// Our read handler called by Comm when the server either closes an idle pinned connection or
4775 /// perhaps unexpectedly sends something on that idle (from Squid p.o.v.) connection.
4776 void
4777 ConnStateData::clientPinnedConnectionRead(const CommIoCbParams &io)
4778 {
4779 pinning.readHandler = NULL; // Comm unregisters handlers before calling
4780
4781 if (io.flag == Comm::ERR_CLOSING)
4782 return; // close handler will clean up
4783
4784 Must(pinning.serverConnection == io.conn);
4785
4786 #if USE_OPENSSL
4787 if (handleIdleClientPinnedTlsRead())
4788 return;
4789 #endif
4790
4791 // We could use getConcurrentRequestCount(), but this may be faster.
4792 const bool clientIsIdle = !getCurrentContext();
4793
4794 debugs(33, 3, "idle pinned " << pinning.serverConnection << " read " <<
4795 io.size << (clientIsIdle ? " with idle client" : ""));
4796
4797 pinning.serverConnection->close();
4798
4799 // If we are still sending data to the client, do not close now. When we are done sending,
4800 // ClientSocketContext::keepaliveNextRequest() checks pinning.serverConnection and will close.
4801 // However, if we are idle, then we must close to inform the idle client and minimize races.
4802 if (clientIsIdle && clientConnection != NULL)
4803 clientConnection->close();
4804 }
4805
4806 const Comm::ConnectionPointer
4807 ConnStateData::validatePinnedConnection(HttpRequest *request, const CachePeer *aPeer)
4808 {
4809 debugs(33, 7, HERE << pinning.serverConnection);
4810
4811 bool valid = true;
4812 if (!Comm::IsConnOpen(pinning.serverConnection))
4813 valid = false;
4814 else if (pinning.auth && pinning.host && request && strcasecmp(pinning.host, request->url.host()) != 0)
4815 valid = false;
4816 else if (request && pinning.port != request->url.port())
4817 valid = false;
4818 else if (pinning.peer && !cbdataReferenceValid(pinning.peer))
4819 valid = false;
4820 else if (aPeer != pinning.peer)
4821 valid = false;
4822
4823 if (!valid) {
4824 /* The pinning info is not safe, remove any pinning info */
4825 unpinConnection(true);
4826 }
4827
4828 return pinning.serverConnection;
4829 }
4830
4831 Comm::ConnectionPointer
4832 ConnStateData::borrowPinnedConnection(HttpRequest *request, const CachePeer *aPeer)
4833 {
4834 debugs(33, 7, pinning.serverConnection);
4835 if (validatePinnedConnection(request, aPeer) != NULL)
4836 stopPinnedConnectionMonitoring();
4837
4838 return pinning.serverConnection; // closed if validation failed
4839 }
4840
4841 void
4842 ConnStateData::unpinConnection(const bool andClose)
4843 {
4844 debugs(33, 3, HERE << pinning.serverConnection);
4845
4846 if (pinning.peer)
4847 cbdataReferenceDone(pinning.peer);
4848
4849 if (Comm::IsConnOpen(pinning.serverConnection)) {
4850 if (pinning.closeHandler != NULL) {
4851 comm_remove_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
4852 pinning.closeHandler = NULL;
4853 }
4854
4855 stopPinnedConnectionMonitoring();
4856
4857 // close the server side socket if requested
4858 if (andClose)
4859 pinning.serverConnection->close();
4860 pinning.serverConnection = NULL;
4861 }
4862
4863 safe_free(pinning.host);
4864
4865 pinning.zeroReply = false;
4866
4867 /* NOTE: pinning.pinned should be kept. This combined with fd == -1 at the end of a request indicates that the host
4868 * connection has gone away */
4869 }
4870