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