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