]> git.ipfire.org Git - thirdparty/squid.git/blob - src/client_side.cc
Add connections_encrypted ACL
[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 request->sources |= isFtp ? HttpMsg::srcFtp :
2346 ((request->flags.sslBumped || conn->port->transport.protocol == AnyP::PROTO_HTTPS) ? HttpMsg::srcHttps : HttpMsg::srcHttp);
2347 #if USE_AUTH
2348 if (request->flags.sslBumped) {
2349 if (conn->getAuth() != NULL)
2350 request->auth_user_request = conn->getAuth();
2351 }
2352 #endif
2353
2354 /** \par
2355 * If transparent or interception mode is working clone the transparent and interception flags
2356 * from the port settings to the request.
2357 */
2358 if (http->clientConnection != NULL) {
2359 request->flags.intercepted = ((http->clientConnection->flags & COMM_INTERCEPTION) != 0);
2360 request->flags.interceptTproxy = ((http->clientConnection->flags & COMM_TRANSPARENT) != 0 ) ;
2361 static const bool proxyProtocolPort = (conn->port != NULL) ? conn->port->flags.proxySurrogate : false;
2362 if (request->flags.interceptTproxy && !proxyProtocolPort) {
2363 if (Config.accessList.spoof_client_ip) {
2364 ACLFilledChecklist *checklist = clientAclChecklistCreate(Config.accessList.spoof_client_ip, http);
2365 request->flags.spoofClientIp = (checklist->fastCheck() == ACCESS_ALLOWED);
2366 delete checklist;
2367 } else
2368 request->flags.spoofClientIp = true;
2369 } else
2370 request->flags.spoofClientIp = false;
2371 }
2372
2373 if (internalCheck(request->url.path())) {
2374 if (internalHostnameIs(request->url.host()) && request->url.port() == getMyPort()) {
2375 debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true));
2376 http->flags.internal = true;
2377 } else if (Config.onoff.global_internal_static && internalStaticCheck(request->url.path())) {
2378 debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true) << " (global_internal_static on)");
2379 request->url.setScheme(AnyP::PROTO_HTTP);
2380 request->url.host(internalHostname());
2381 request->url.port(getMyPort());
2382 http->flags.internal = true;
2383 } else
2384 debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true) << " (not this proxy)");
2385 }
2386
2387 request->flags.internal = http->flags.internal;
2388 setLogUri (http, urlCanonicalClean(request.getRaw()));
2389 request->client_addr = conn->clientConnection->remote; // XXX: remove reuest->client_addr member.
2390 #if FOLLOW_X_FORWARDED_FOR
2391 // indirect client gets stored here because it is an HTTP header result (from X-Forwarded-For:)
2392 // not a details about teh TCP connection itself
2393 request->indirect_client_addr = conn->clientConnection->remote;
2394 #endif /* FOLLOW_X_FORWARDED_FOR */
2395 request->my_addr = conn->clientConnection->local;
2396 request->myportname = conn->port->name;
2397
2398 if (!isFtp) {
2399 // XXX: for non-HTTP messages instantiate a different HttpMsg child type
2400 // for now Squid only supports HTTP requests
2401 const AnyP::ProtocolVersion &http_ver = hp->messageProtocol();
2402 assert(request->http_ver.protocol == http_ver.protocol);
2403 request->http_ver.major = http_ver.major;
2404 request->http_ver.minor = http_ver.minor;
2405 }
2406
2407 // Link this HttpRequest to ConnStateData relatively early so the following complex handling can use it
2408 // TODO: this effectively obsoletes a lot of conn->FOO copying. That needs cleaning up later.
2409 request->clientConnectionManager = conn;
2410
2411 if (request->header.chunked()) {
2412 chunked = true;
2413 } else if (request->header.has(Http::HdrType::TRANSFER_ENCODING)) {
2414 const String te = request->header.getList(Http::HdrType::TRANSFER_ENCODING);
2415 // HTTP/1.1 requires chunking to be the last encoding if there is one
2416 unsupportedTe = te.size() && te != "identity";
2417 } // else implied identity coding
2418
2419 mustReplyToOptions = (request->method == Http::METHOD_OPTIONS) &&
2420 (request->header.getInt64(Http::HdrType::MAX_FORWARDS) == 0);
2421 if (!urlCheckRequest(request.getRaw()) || mustReplyToOptions || unsupportedTe) {
2422 clientStreamNode *node = context->getClientReplyContext();
2423 conn->quitAfterError(request.getRaw());
2424 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2425 assert (repContext);
2426 repContext->setReplyToError(ERR_UNSUP_REQ, Http::scNotImplemented, request->method, NULL,
2427 conn->clientConnection->remote, request.getRaw(), NULL, NULL);
2428 assert(context->http->out.offset == 0);
2429 context->pullData();
2430 clientProcessRequestFinished(conn, request);
2431 return;
2432 }
2433
2434 if (!chunked && !clientIsContentLengthValid(request.getRaw())) {
2435 clientStreamNode *node = context->getClientReplyContext();
2436 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2437 assert (repContext);
2438 conn->quitAfterError(request.getRaw());
2439 repContext->setReplyToError(ERR_INVALID_REQ,
2440 Http::scLengthRequired, request->method, NULL,
2441 conn->clientConnection->remote, request.getRaw(), NULL, NULL);
2442 assert(context->http->out.offset == 0);
2443 context->pullData();
2444 clientProcessRequestFinished(conn, request);
2445 return;
2446 }
2447
2448 clientSetKeepaliveFlag(http);
2449 // Let tunneling code be fully responsible for CONNECT requests
2450 if (http->request->method == Http::METHOD_CONNECT) {
2451 context->mayUseConnection(true);
2452 conn->flags.readMore = false;
2453 }
2454
2455 #if USE_OPENSSL
2456 if (conn->switchedToHttps() && conn->serveDelayedError(context)) {
2457 clientProcessRequestFinished(conn, request);
2458 return;
2459 }
2460 #endif
2461
2462 /* Do we expect a request-body? */
2463 expectBody = chunked || request->content_length > 0;
2464 if (!context->mayUseConnection() && expectBody) {
2465 request->body_pipe = conn->expectRequestBody(
2466 chunked ? -1 : request->content_length);
2467
2468 /* Is it too large? */
2469 if (!chunked && // if chunked, we will check as we accumulate
2470 clientIsRequestBodyTooLargeForPolicy(request->content_length)) {
2471 clientStreamNode *node = context->getClientReplyContext();
2472 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
2473 assert (repContext);
2474 conn->quitAfterError(request.getRaw());
2475 repContext->setReplyToError(ERR_TOO_BIG,
2476 Http::scPayloadTooLarge, Http::METHOD_NONE, NULL,
2477 conn->clientConnection->remote, http->request, NULL, NULL);
2478 assert(context->http->out.offset == 0);
2479 context->pullData();
2480 clientProcessRequestFinished(conn, request);
2481 return;
2482 }
2483
2484 if (!isFtp) {
2485 // We may stop producing, comm_close, and/or call setReplyToError()
2486 // below, so quit on errors to avoid http->doCallouts()
2487 if (!conn->handleRequestBodyData()) {
2488 clientProcessRequestFinished(conn, request);
2489 return;
2490 }
2491
2492 if (!request->body_pipe->productionEnded()) {
2493 debugs(33, 5, "need more request body");
2494 context->mayUseConnection(true);
2495 assert(conn->flags.readMore);
2496 }
2497 }
2498 }
2499
2500 http->calloutContext = new ClientRequestContext(http);
2501
2502 http->doCallouts();
2503
2504 clientProcessRequestFinished(conn, request);
2505 }
2506
2507 int
2508 ConnStateData::pipelinePrefetchMax() const
2509 {
2510 // TODO: Support pipelined requests through pinned connections.
2511 if (pinning.pinned)
2512 return 0;
2513 return Config.pipeline_max_prefetch;
2514 }
2515
2516 /**
2517 * Limit the number of concurrent requests.
2518 * \return true when there are available position(s) in the pipeline queue for another request.
2519 * \return false when the pipeline queue is full or disabled.
2520 */
2521 bool
2522 ConnStateData::concurrentRequestQueueFilled() const
2523 {
2524 const int existingRequestCount = pipeline.count();
2525
2526 // default to the configured pipeline size.
2527 // add 1 because the head of pipeline is counted in concurrent requests and not prefetch queue
2528 #if USE_OPENSSL
2529 const int internalRequest = (transparent() && sslBumpMode == Ssl::bumpSplice) ? 1 : 0;
2530 #else
2531 const int internalRequest = 0;
2532 #endif
2533 const int concurrentRequestLimit = pipelinePrefetchMax() + 1 + internalRequest;
2534
2535 // when queue filled already we cant add more.
2536 if (existingRequestCount >= concurrentRequestLimit) {
2537 debugs(33, 3, clientConnection << " max concurrent requests reached (" << concurrentRequestLimit << ")");
2538 debugs(33, 5, clientConnection << " deferring new request until one is done");
2539 return true;
2540 }
2541
2542 return false;
2543 }
2544
2545 /**
2546 * Perform proxy_protocol_access ACL tests on the client which
2547 * connected to PROXY protocol port to see if we trust the
2548 * sender enough to accept their PROXY header claim.
2549 */
2550 bool
2551 ConnStateData::proxyProtocolValidateClient()
2552 {
2553 if (!Config.accessList.proxyProtocol)
2554 return proxyProtocolError("PROXY client not permitted by default ACL");
2555
2556 ACLFilledChecklist ch(Config.accessList.proxyProtocol, NULL, clientConnection->rfc931);
2557 ch.src_addr = clientConnection->remote;
2558 ch.my_addr = clientConnection->local;
2559 ch.conn(this);
2560
2561 if (ch.fastCheck() != ACCESS_ALLOWED)
2562 return proxyProtocolError("PROXY client not permitted by ACLs");
2563
2564 return true;
2565 }
2566
2567 /**
2568 * Perform cleanup on PROXY protocol errors.
2569 * If header parsing hits a fatal error terminate the connection,
2570 * otherwise wait for more data.
2571 */
2572 bool
2573 ConnStateData::proxyProtocolError(const char *msg)
2574 {
2575 if (msg) {
2576 // This is important to know, but maybe not so much that flooding the log is okay.
2577 #if QUIET_PROXY_PROTOCOL
2578 // display the first of every 32 occurances at level 1, the others at level 2.
2579 static uint8_t hide = 0;
2580 debugs(33, (hide++ % 32 == 0 ? DBG_IMPORTANT : 2), msg << " from " << clientConnection);
2581 #else
2582 debugs(33, DBG_IMPORTANT, msg << " from " << clientConnection);
2583 #endif
2584 mustStop(msg);
2585 }
2586 return false;
2587 }
2588
2589 /// magic octet prefix for PROXY protocol version 1
2590 static const SBuf Proxy1p0magic("PROXY ", 6);
2591
2592 /// magic octet prefix for PROXY protocol version 2
2593 static const SBuf Proxy2p0magic("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
2594
2595 /**
2596 * Test the connection read buffer for PROXY protocol header.
2597 * Version 1 and 2 header currently supported.
2598 */
2599 bool
2600 ConnStateData::parseProxyProtocolHeader()
2601 {
2602 // http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
2603
2604 // detect and parse PROXY/2.0 protocol header
2605 if (inBuf.startsWith(Proxy2p0magic))
2606 return parseProxy2p0();
2607
2608 // detect and parse PROXY/1.0 protocol header
2609 if (inBuf.startsWith(Proxy1p0magic))
2610 return parseProxy1p0();
2611
2612 // detect and terminate other protocols
2613 if (inBuf.length() >= Proxy2p0magic.length()) {
2614 // PROXY/1.0 magic is shorter, so we know that
2615 // the input does not start with any PROXY magic
2616 return proxyProtocolError("PROXY protocol error: invalid header");
2617 }
2618
2619 // TODO: detect short non-magic prefixes earlier to avoid
2620 // waiting for more data which may never come
2621
2622 // not enough bytes to parse yet.
2623 return false;
2624 }
2625
2626 /// parse the PROXY/1.0 protocol header from the connection read buffer
2627 bool
2628 ConnStateData::parseProxy1p0()
2629 {
2630 ::Parser::Tokenizer tok(inBuf);
2631 tok.skip(Proxy1p0magic);
2632
2633 // skip to first LF (assumes it is part of CRLF)
2634 static const CharacterSet lineContent = CharacterSet::LF.complement("non-LF");
2635 SBuf line;
2636 if (tok.prefix(line, lineContent, 107-Proxy1p0magic.length())) {
2637 if (tok.skip('\n')) {
2638 // found valid header
2639 inBuf = tok.remaining();
2640 needProxyProtocolHeader_ = false;
2641 // reset the tokenizer to work on found line only.
2642 tok.reset(line);
2643 } else
2644 return false; // no LF yet
2645
2646 } else // protocol error only if there are more than 107 bytes prefix header
2647 return proxyProtocolError(inBuf.length() > 107? "PROXY/1.0 error: missing CRLF" : NULL);
2648
2649 static const SBuf unknown("UNKNOWN"), tcpName("TCP");
2650 if (tok.skip(tcpName)) {
2651
2652 // skip TCP/IP version number
2653 static const CharacterSet tcpVersions("TCP-version","46");
2654 if (!tok.skipOne(tcpVersions))
2655 return proxyProtocolError("PROXY/1.0 error: missing TCP version");
2656
2657 // skip SP after protocol version
2658 if (!tok.skip(' '))
2659 return proxyProtocolError("PROXY/1.0 error: missing SP");
2660
2661 SBuf ipa, ipb;
2662 int64_t porta, portb;
2663 static const CharacterSet ipChars = CharacterSet("IP Address",".:") + CharacterSet::HEXDIG;
2664
2665 // parse: src-IP SP dst-IP SP src-port SP dst-port CR
2666 // leave the LF until later.
2667 const bool correct = tok.prefix(ipa, ipChars) && tok.skip(' ') &&
2668 tok.prefix(ipb, ipChars) && tok.skip(' ') &&
2669 tok.int64(porta) && tok.skip(' ') &&
2670 tok.int64(portb) &&
2671 tok.skip('\r');
2672 if (!correct)
2673 return proxyProtocolError("PROXY/1.0 error: invalid syntax");
2674
2675 // parse IP and port strings
2676 Ip::Address originalClient, originalDest;
2677
2678 if (!originalClient.GetHostByName(ipa.c_str()))
2679 return proxyProtocolError("PROXY/1.0 error: invalid src-IP address");
2680
2681 if (!originalDest.GetHostByName(ipb.c_str()))
2682 return proxyProtocolError("PROXY/1.0 error: invalid dst-IP address");
2683
2684 if (porta > 0 && porta <= 0xFFFF) // max uint16_t
2685 originalClient.port(static_cast<uint16_t>(porta));
2686 else
2687 return proxyProtocolError("PROXY/1.0 error: invalid src port");
2688
2689 if (portb > 0 && portb <= 0xFFFF) // max uint16_t
2690 originalDest.port(static_cast<uint16_t>(portb));
2691 else
2692 return proxyProtocolError("PROXY/1.0 error: invalid dst port");
2693
2694 // we have original client and destination details now
2695 // replace the client connection values
2696 debugs(33, 5, "PROXY/1.0 protocol on connection " << clientConnection);
2697 clientConnection->local = originalDest;
2698 clientConnection->remote = originalClient;
2699 if ((clientConnection->flags & COMM_TRANSPARENT))
2700 clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY spoofing of this new IP.
2701 debugs(33, 5, "PROXY/1.0 upgrade: " << clientConnection);
2702
2703 // repeat fetch ensuring the new client FQDN can be logged
2704 if (Config.onoff.log_fqdn)
2705 fqdncache_gethostbyaddr(clientConnection->remote, FQDN_LOOKUP_IF_MISS);
2706
2707 return true;
2708
2709 } else if (tok.skip(unknown)) {
2710 // found valid but unusable header
2711 return true;
2712
2713 } else
2714 return proxyProtocolError("PROXY/1.0 error: invalid protocol family");
2715
2716 return false;
2717 }
2718
2719 /// parse the PROXY/2.0 protocol header from the connection read buffer
2720 bool
2721 ConnStateData::parseProxy2p0()
2722 {
2723 static const SBuf::size_type prefixLen = Proxy2p0magic.length();
2724 if (inBuf.length() < prefixLen + 4)
2725 return false; // need more bytes
2726
2727 if ((inBuf[prefixLen] & 0xF0) != 0x20) // version == 2 is mandatory
2728 return proxyProtocolError("PROXY/2.0 error: invalid version");
2729
2730 const char command = (inBuf[prefixLen] & 0x0F);
2731 if ((command & 0xFE) != 0x00) // values other than 0x0-0x1 are invalid
2732 return proxyProtocolError("PROXY/2.0 error: invalid command");
2733
2734 const char family = (inBuf[prefixLen+1] & 0xF0) >>4;
2735 if (family > 0x3) // values other than 0x0-0x3 are invalid
2736 return proxyProtocolError("PROXY/2.0 error: invalid family");
2737
2738 const char proto = (inBuf[prefixLen+1] & 0x0F);
2739 if (proto > 0x2) // values other than 0x0-0x2 are invalid
2740 return proxyProtocolError("PROXY/2.0 error: invalid protocol type");
2741
2742 const char *clen = inBuf.rawContent() + prefixLen + 2;
2743 uint16_t len;
2744 memcpy(&len, clen, sizeof(len));
2745 len = ntohs(len);
2746
2747 if (inBuf.length() < prefixLen + 4 + len)
2748 return false; // need more bytes
2749
2750 inBuf.consume(prefixLen + 4); // 4 being the extra bytes
2751 const SBuf extra = inBuf.consume(len);
2752 needProxyProtocolHeader_ = false; // found successfully
2753
2754 // LOCAL connections do nothing with the extras
2755 if (command == 0x00/* LOCAL*/)
2756 return true;
2757
2758 union pax {
2759 struct { /* for TCP/UDP over IPv4, len = 12 */
2760 struct in_addr src_addr;
2761 struct in_addr dst_addr;
2762 uint16_t src_port;
2763 uint16_t dst_port;
2764 } ipv4_addr;
2765 struct { /* for TCP/UDP over IPv6, len = 36 */
2766 struct in6_addr src_addr;
2767 struct in6_addr dst_addr;
2768 uint16_t src_port;
2769 uint16_t dst_port;
2770 } ipv6_addr;
2771 #if NOT_SUPPORTED
2772 struct { /* for AF_UNIX sockets, len = 216 */
2773 uint8_t src_addr[108];
2774 uint8_t dst_addr[108];
2775 } unix_addr;
2776 #endif
2777 };
2778
2779 pax ipu;
2780 memcpy(&ipu, extra.rawContent(), sizeof(pax));
2781
2782 // replace the client connection values
2783 debugs(33, 5, "PROXY/2.0 protocol on connection " << clientConnection);
2784 switch (family) {
2785 case 0x1: // IPv4
2786 clientConnection->local = ipu.ipv4_addr.dst_addr;
2787 clientConnection->local.port(ntohs(ipu.ipv4_addr.dst_port));
2788 clientConnection->remote = ipu.ipv4_addr.src_addr;
2789 clientConnection->remote.port(ntohs(ipu.ipv4_addr.src_port));
2790 if ((clientConnection->flags & COMM_TRANSPARENT))
2791 clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY spoofing of this new IP.
2792 break;
2793 case 0x2: // IPv6
2794 clientConnection->local = ipu.ipv6_addr.dst_addr;
2795 clientConnection->local.port(ntohs(ipu.ipv6_addr.dst_port));
2796 clientConnection->remote = ipu.ipv6_addr.src_addr;
2797 clientConnection->remote.port(ntohs(ipu.ipv6_addr.src_port));
2798 if ((clientConnection->flags & COMM_TRANSPARENT))
2799 clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY spoofing of this new IP.
2800 break;
2801 default: // do nothing
2802 break;
2803 }
2804 debugs(33, 5, "PROXY/2.0 upgrade: " << clientConnection);
2805
2806 // repeat fetch ensuring the new client FQDN can be logged
2807 if (Config.onoff.log_fqdn)
2808 fqdncache_gethostbyaddr(clientConnection->remote, FQDN_LOOKUP_IF_MISS);
2809
2810 return true;
2811 }
2812
2813 void
2814 ConnStateData::receivedFirstByte()
2815 {
2816 if (receivedFirstByte_)
2817 return;
2818
2819 receivedFirstByte_ = true;
2820 // Set timeout to Config.Timeout.request
2821 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
2822 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
2823 TimeoutDialer, this, ConnStateData::requestTimeout);
2824 commSetConnTimeout(clientConnection, Config.Timeout.request, timeoutCall);
2825 }
2826
2827 /**
2828 * Attempt to parse one or more requests from the input buffer.
2829 * Returns true after completing parsing of at least one request [header]. That
2830 * includes cases where parsing ended with an error (e.g., a huge request).
2831 */
2832 bool
2833 ConnStateData::clientParseRequests()
2834 {
2835 bool parsed_req = false;
2836
2837 debugs(33, 5, HERE << clientConnection << ": attempting to parse");
2838
2839 // Loop while we have read bytes that are not needed for producing the body
2840 // On errors, bodyPipe may become nil, but readMore will be cleared
2841 while (!inBuf.isEmpty() && !bodyPipe && flags.readMore) {
2842
2843 /* Don't try to parse if the buffer is empty */
2844 if (inBuf.isEmpty())
2845 break;
2846
2847 /* Limit the number of concurrent requests */
2848 if (concurrentRequestQueueFilled())
2849 break;
2850
2851 // try to parse the PROXY protocol header magic bytes
2852 if (needProxyProtocolHeader_ && !parseProxyProtocolHeader())
2853 break;
2854
2855 if (ClientSocketContext *context = parseOneRequest()) {
2856 debugs(33, 5, clientConnection << ": done parsing a request");
2857
2858 AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "clientLifetimeTimeout",
2859 CommTimeoutCbPtrFun(clientLifetimeTimeout, context->http));
2860 commSetConnTimeout(clientConnection, Config.Timeout.lifetime, timeoutCall);
2861
2862 context->registerWithConn();
2863
2864 processParsedRequest(context);
2865
2866 parsed_req = true; // XXX: do we really need to parse everything right NOW ?
2867
2868 if (context->mayUseConnection()) {
2869 debugs(33, 3, HERE << "Not parsing new requests, as this request may need the connection");
2870 break;
2871 }
2872 } else {
2873 debugs(33, 5, clientConnection << ": not enough request data: " <<
2874 inBuf.length() << " < " << Config.maxRequestHeaderSize);
2875 Must(inBuf.length() < Config.maxRequestHeaderSize);
2876 break;
2877 }
2878 }
2879
2880 /* XXX where to 'finish' the parsing pass? */
2881 return parsed_req;
2882 }
2883
2884 void
2885 ConnStateData::afterClientRead()
2886 {
2887 /* Process next request */
2888 if (pipeline.empty())
2889 fd_note(clientConnection->fd, "Reading next request");
2890
2891 if (!clientParseRequests()) {
2892 if (!isOpen())
2893 return;
2894 /*
2895 * If the client here is half closed and we failed
2896 * to parse a request, close the connection.
2897 * The above check with connFinishedWithConn() only
2898 * succeeds _if_ the buffer is empty which it won't
2899 * be if we have an incomplete request.
2900 * XXX: This duplicates ConnStateData::kick
2901 */
2902 if (pipeline.empty() && commIsHalfClosed(clientConnection->fd)) {
2903 debugs(33, 5, clientConnection << ": half-closed connection, no completed request parsed, connection closing.");
2904 clientConnection->close();
2905 return;
2906 }
2907 }
2908
2909 if (!isOpen())
2910 return;
2911
2912 clientAfterReadingRequests();
2913 }
2914
2915 /**
2916 * called when new request data has been read from the socket
2917 *
2918 * \retval false called comm_close or setReplyToError (the caller should bail)
2919 * \retval true we did not call comm_close or setReplyToError
2920 */
2921 bool
2922 ConnStateData::handleReadData()
2923 {
2924 // if we are reading a body, stuff data into the body pipe
2925 if (bodyPipe != NULL)
2926 return handleRequestBodyData();
2927 return true;
2928 }
2929
2930 /**
2931 * called when new request body data has been buffered in inBuf
2932 * may close the connection if we were closing and piped everything out
2933 *
2934 * \retval false called comm_close or setReplyToError (the caller should bail)
2935 * \retval true we did not call comm_close or setReplyToError
2936 */
2937 bool
2938 ConnStateData::handleRequestBodyData()
2939 {
2940 assert(bodyPipe != NULL);
2941
2942 if (bodyParser) { // chunked encoding
2943 if (const err_type error = handleChunkedRequestBody()) {
2944 abortChunkedRequestBody(error);
2945 return false;
2946 }
2947 } else { // identity encoding
2948 debugs(33,5, HERE << "handling plain request body for " << clientConnection);
2949 const size_t putSize = bodyPipe->putMoreData(inBuf.c_str(), inBuf.length());
2950 if (putSize > 0)
2951 consumeInput(putSize);
2952
2953 if (!bodyPipe->mayNeedMoreData()) {
2954 // BodyPipe will clear us automagically when we produced everything
2955 bodyPipe = NULL;
2956 }
2957 }
2958
2959 if (!bodyPipe) {
2960 debugs(33,5, HERE << "produced entire request body for " << clientConnection);
2961
2962 if (const char *reason = stoppedSending()) {
2963 /* we've finished reading like good clients,
2964 * now do the close that initiateClose initiated.
2965 */
2966 debugs(33, 3, HERE << "closing for earlier sending error: " << reason);
2967 clientConnection->close();
2968 return false;
2969 }
2970 }
2971
2972 return true;
2973 }
2974
2975 /// parses available chunked encoded body bytes, checks size, returns errors
2976 err_type
2977 ConnStateData::handleChunkedRequestBody()
2978 {
2979 debugs(33, 7, "chunked from " << clientConnection << ": " << inBuf.length());
2980
2981 try { // the parser will throw on errors
2982
2983 if (inBuf.isEmpty()) // nothing to do
2984 return ERR_NONE;
2985
2986 BodyPipeCheckout bpc(*bodyPipe);
2987 bodyParser->setPayloadBuffer(&bpc.buf);
2988 const bool parsed = bodyParser->parse(inBuf);
2989 inBuf = bodyParser->remaining(); // sync buffers
2990 bpc.checkIn();
2991
2992 // dechunk then check: the size limit applies to _dechunked_ content
2993 if (clientIsRequestBodyTooLargeForPolicy(bodyPipe->producedSize()))
2994 return ERR_TOO_BIG;
2995
2996 if (parsed) {
2997 finishDechunkingRequest(true);
2998 Must(!bodyPipe);
2999 return ERR_NONE; // nil bodyPipe implies body end for the caller
3000 }
3001
3002 // if chunk parser needs data, then the body pipe must need it too
3003 Must(!bodyParser->needsMoreData() || bodyPipe->mayNeedMoreData());
3004
3005 // if parser needs more space and we can consume nothing, we will stall
3006 Must(!bodyParser->needsMoreSpace() || bodyPipe->buf().hasContent());
3007 } catch (...) { // TODO: be more specific
3008 debugs(33, 3, HERE << "malformed chunks" << bodyPipe->status());
3009 return ERR_INVALID_REQ;
3010 }
3011
3012 debugs(33, 7, HERE << "need more chunked data" << *bodyPipe->status());
3013 return ERR_NONE;
3014 }
3015
3016 /// quit on errors related to chunked request body handling
3017 void
3018 ConnStateData::abortChunkedRequestBody(const err_type error)
3019 {
3020 finishDechunkingRequest(false);
3021
3022 // XXX: The code below works if we fail during initial request parsing,
3023 // but if we fail when the server connection is used already, the server may send
3024 // us its response too, causing various assertions. How to prevent that?
3025 #if WE_KNOW_HOW_TO_SEND_ERRORS
3026 ClientSocketContext::Pointer context = pipeline.front();
3027 if (context != NULL && !context->http->out.offset) { // output nothing yet
3028 clientStreamNode *node = context->getClientReplyContext();
3029 clientReplyContext *repContext = dynamic_cast<clientReplyContext*>(node->data.getRaw());
3030 assert(repContext);
3031 const Http::StatusCode scode = (error == ERR_TOO_BIG) ?
3032 Http::scPayloadTooLarge : HTTP_BAD_REQUEST;
3033 repContext->setReplyToError(error, scode,
3034 repContext->http->request->method,
3035 repContext->http->uri,
3036 CachePeer,
3037 repContext->http->request,
3038 inBuf, NULL);
3039 context->pullData();
3040 } else {
3041 // close or otherwise we may get stuck as nobody will notice the error?
3042 comm_reset_close(clientConnection);
3043 }
3044 #else
3045 debugs(33, 3, HERE << "aborting chunked request without error " << error);
3046 comm_reset_close(clientConnection);
3047 #endif
3048 flags.readMore = false;
3049 }
3050
3051 void
3052 ConnStateData::noteBodyConsumerAborted(BodyPipe::Pointer )
3053 {
3054 // request reader may get stuck waiting for space if nobody consumes body
3055 if (bodyPipe != NULL)
3056 bodyPipe->enableAutoConsumption();
3057
3058 // kids extend
3059 }
3060
3061 /** general lifetime handler for HTTP requests */
3062 void
3063 ConnStateData::requestTimeout(const CommTimeoutCbParams &io)
3064 {
3065 if (!Comm::IsConnOpen(io.conn))
3066 return;
3067
3068 if (Config.accessList.on_unsupported_protocol && !receivedFirstByte_) {
3069 #if USE_OPENSSL
3070 if (serverBump() && (serverBump()->act.step1 == Ssl::bumpPeek || serverBump()->act.step1 == Ssl::bumpStare)) {
3071 if (spliceOnError(ERR_REQUEST_START_TIMEOUT)) {
3072 receivedFirstByte();
3073 return;
3074 }
3075 } else if (fd_table[io.conn->fd].ssl == NULL)
3076 #endif
3077 {
3078 const HttpRequestMethod method;
3079 if (clientTunnelOnError(this, NULL, NULL, method, ERR_REQUEST_START_TIMEOUT, Http::scNone, NULL)) {
3080 // Tunnel established. Set receivedFirstByte to avoid loop.
3081 receivedFirstByte();
3082 return;
3083 }
3084 }
3085 }
3086 /*
3087 * Just close the connection to not confuse browsers
3088 * using persistent connections. Some browsers open
3089 * a connection and then do not use it until much
3090 * later (presumeably because the request triggering
3091 * the open has already been completed on another
3092 * connection)
3093 */
3094 debugs(33, 3, "requestTimeout: FD " << io.fd << ": lifetime is expired.");
3095 io.conn->close();
3096 }
3097
3098 static void
3099 clientLifetimeTimeout(const CommTimeoutCbParams &io)
3100 {
3101 ClientHttpRequest *http = static_cast<ClientHttpRequest *>(io.data);
3102 debugs(33, DBG_IMPORTANT, "WARNING: Closing client connection due to lifetime timeout");
3103 debugs(33, DBG_IMPORTANT, "\t" << http->uri);
3104 http->logType.err.timedout = true;
3105 if (Comm::IsConnOpen(io.conn))
3106 io.conn->close();
3107 }
3108
3109 ConnStateData::ConnStateData(const MasterXaction::Pointer &xact) :
3110 AsyncJob("ConnStateData"), // kids overwrite
3111 Server(xact),
3112 bodyParser(nullptr),
3113 #if USE_OPENSSL
3114 sslBumpMode(Ssl::bumpEnd),
3115 #endif
3116 needProxyProtocolHeader_(false),
3117 #if USE_OPENSSL
3118 switchedToHttps_(false),
3119 sslServerBump(NULL),
3120 signAlgorithm(Ssl::algSignTrusted),
3121 #endif
3122 stoppedSending_(NULL),
3123 stoppedReceiving_(NULL)
3124 {
3125 flags.readMore = true; // kids may overwrite
3126 flags.swanSang = false;
3127
3128 pinning.host = NULL;
3129 pinning.port = -1;
3130 pinning.pinned = false;
3131 pinning.auth = false;
3132 pinning.zeroReply = false;
3133 pinning.peer = NULL;
3134
3135 // store the details required for creating more MasterXaction objects as new requests come in
3136 log_addr = xact->tcpClient->remote;
3137 log_addr.applyMask(Config.Addrs.client_netmask);
3138
3139 // register to receive notice of Squid signal events
3140 // which may affect long persisting client connections
3141 RegisterRunner(this);
3142 }
3143
3144 void
3145 ConnStateData::start()
3146 {
3147 BodyProducer::start();
3148 HttpControlMsgSink::start();
3149
3150 if (port->disable_pmtu_discovery != DISABLE_PMTU_OFF &&
3151 (transparent() || port->disable_pmtu_discovery == DISABLE_PMTU_ALWAYS)) {
3152 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
3153 int i = IP_PMTUDISC_DONT;
3154 if (setsockopt(clientConnection->fd, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)) < 0)
3155 debugs(33, 2, "WARNING: Path MTU discovery disabling failed on " << clientConnection << " : " << xstrerror());
3156 #else
3157 static bool reported = false;
3158
3159 if (!reported) {
3160 debugs(33, DBG_IMPORTANT, "NOTICE: Path MTU discovery disabling is not supported on your platform.");
3161 reported = true;
3162 }
3163 #endif
3164 }
3165
3166 typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
3167 AsyncCall::Pointer call = JobCallback(33, 5, Dialer, this, ConnStateData::connStateClosed);
3168 comm_add_close_handler(clientConnection->fd, call);
3169
3170 if (Config.onoff.log_fqdn)
3171 fqdncache_gethostbyaddr(clientConnection->remote, FQDN_LOOKUP_IF_MISS);
3172
3173 #if USE_IDENT
3174 if (Ident::TheConfig.identLookup) {
3175 ACLFilledChecklist identChecklist(Ident::TheConfig.identLookup, NULL, NULL);
3176 identChecklist.src_addr = clientConnection->remote;
3177 identChecklist.my_addr = clientConnection->local;
3178 if (identChecklist.fastCheck() == ACCESS_ALLOWED)
3179 Ident::Start(clientConnection, clientIdentDone, this);
3180 }
3181 #endif
3182
3183 clientdbEstablished(clientConnection->remote, 1);
3184
3185 needProxyProtocolHeader_ = port->flags.proxySurrogate;
3186 if (needProxyProtocolHeader_) {
3187 if (!proxyProtocolValidateClient()) // will close the connection on failure
3188 return;
3189 }
3190
3191 #if USE_DELAY_POOLS
3192 fd_table[clientConnection->fd].clientInfo = NULL;
3193
3194 if (Config.onoff.client_db) {
3195 /* it was said several times that client write limiter does not work if client_db is disabled */
3196
3197 ClientDelayPools& pools(Config.ClientDelay.pools);
3198 ACLFilledChecklist ch(NULL, NULL, NULL);
3199
3200 // TODO: we check early to limit error response bandwith but we
3201 // should recheck when we can honor delay_pool_uses_indirect
3202 // TODO: we should also pass the port details for myportname here.
3203 ch.src_addr = clientConnection->remote;
3204 ch.my_addr = clientConnection->local;
3205
3206 for (unsigned int pool = 0; pool < pools.size(); ++pool) {
3207
3208 /* pools require explicit 'allow' to assign a client into them */
3209 if (pools[pool].access) {
3210 ch.changeAcl(pools[pool].access);
3211 allow_t answer = ch.fastCheck();
3212 if (answer == ACCESS_ALLOWED) {
3213
3214 /* request client information from db after we did all checks
3215 this will save hash lookup if client failed checks */
3216 ClientInfo * cli = clientdbGetInfo(clientConnection->remote);
3217 assert(cli);
3218
3219 /* put client info in FDE */
3220 fd_table[clientConnection->fd].clientInfo = cli;
3221
3222 /* setup write limiter for this request */
3223 const double burst = floor(0.5 +
3224 (pools[pool].highwatermark * Config.ClientDelay.initial)/100.0);
3225 cli->setWriteLimiter(pools[pool].rate, burst, pools[pool].highwatermark);
3226 break;
3227 } else {
3228 debugs(83, 4, HERE << "Delay pool " << pool << " skipped because ACL " << answer);
3229 }
3230 }
3231 }
3232 }
3233 #endif
3234
3235 // kids must extend to actually start doing something (e.g., reading)
3236 }
3237
3238 /** Handle a new connection on an HTTP socket. */
3239 void
3240 httpAccept(const CommAcceptCbParams &params)
3241 {
3242 MasterXaction::Pointer xact = params.xaction;
3243 AnyP::PortCfgPointer s = xact->squidPort;
3244
3245 // NP: it is possible the port was reconfigured when the call or accept() was queued.
3246
3247 if (params.flag != Comm::OK) {
3248 // Its possible the call was still queued when the client disconnected
3249 debugs(33, 2, s->listenConn << ": accept failure: " << xstrerr(params.xerrno));
3250 return;
3251 }
3252
3253 debugs(33, 4, params.conn << ": accepted");
3254 fd_note(params.conn->fd, "client http connect");
3255
3256 if (s->tcp_keepalive.enabled)
3257 commSetTcpKeepalive(params.conn->fd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
3258
3259 ++incoming_sockets_accepted;
3260
3261 // Socket is ready, setup the connection manager to start using it
3262 ConnStateData *connState = Http::NewServer(xact);
3263 AsyncJob::Start(connState); // usually async-calls readSomeData()
3264 }
3265
3266 #if USE_OPENSSL
3267
3268 /** Create SSL connection structure and update fd_table */
3269 static Security::SessionPointer
3270 httpsCreate(const Comm::ConnectionPointer &conn, Security::ContextPtr sslContext)
3271 {
3272 if (auto ssl = Ssl::CreateServer(sslContext, conn->fd, "client https start")) {
3273 debugs(33, 5, "will negotate SSL on " << conn);
3274 return ssl;
3275 }
3276
3277 conn->close();
3278 return nullptr;
3279 }
3280
3281 /**
3282 *
3283 * \retval 1 on success
3284 * \retval 0 when needs more data
3285 * \retval -1 on error
3286 */
3287 static int
3288 Squid_SSL_accept(ConnStateData *conn, PF *callback)
3289 {
3290 int fd = conn->clientConnection->fd;
3291 auto ssl = fd_table[fd].ssl;
3292 int ret;
3293
3294 errno = 0;
3295 if ((ret = SSL_accept(ssl)) <= 0) {
3296 const int xerrno = errno;
3297 const int ssl_error = SSL_get_error(ssl, ret);
3298
3299 switch (ssl_error) {
3300
3301 case SSL_ERROR_WANT_READ:
3302 Comm::SetSelect(fd, COMM_SELECT_READ, callback, conn, 0);
3303 return 0;
3304
3305 case SSL_ERROR_WANT_WRITE:
3306 Comm::SetSelect(fd, COMM_SELECT_WRITE, callback, conn, 0);
3307 return 0;
3308
3309 case SSL_ERROR_SYSCALL:
3310 if (ret == 0) {
3311 debugs(83, 2, "Error negotiating SSL connection on FD " << fd << ": Aborted by client: " << ssl_error);
3312 } else {
3313 debugs(83, (xerrno == ECONNRESET) ? 1 : 2, "Error negotiating SSL connection on FD " << fd << ": " <<
3314 (xerrno == 0 ? ERR_error_string(ssl_error, NULL) : xstrerr(xerrno)));
3315 }
3316 return -1;
3317
3318 case SSL_ERROR_ZERO_RETURN:
3319 debugs(83, DBG_IMPORTANT, "Error negotiating SSL connection on FD " << fd << ": Closed by client");
3320 return -1;
3321
3322 default:
3323 debugs(83, DBG_IMPORTANT, "Error negotiating SSL connection on FD " <<
3324 fd << ": " << ERR_error_string(ERR_get_error(), NULL) <<
3325 " (" << ssl_error << "/" << ret << ")");
3326 return -1;
3327 }
3328
3329 /* NOTREACHED */
3330 }
3331 return 1;
3332 }
3333
3334 /** negotiate an SSL connection */
3335 static void
3336 clientNegotiateSSL(int fd, void *data)
3337 {
3338 ConnStateData *conn = (ConnStateData *)data;
3339 X509 *client_cert;
3340 auto ssl = fd_table[fd].ssl;
3341
3342 int ret;
3343 if ((ret = Squid_SSL_accept(conn, clientNegotiateSSL)) <= 0) {
3344 if (ret < 0) // An error
3345 conn->clientConnection->close();
3346 return;
3347 }
3348
3349 if (SSL_session_reused(ssl)) {
3350 debugs(83, 2, "clientNegotiateSSL: Session " << SSL_get_session(ssl) <<
3351 " reused on FD " << fd << " (" << fd_table[fd].ipaddr << ":" << (int)fd_table[fd].remote_port << ")");
3352 } else {
3353 if (do_debug(83, 4)) {
3354 /* Write out the SSL session details.. actually the call below, but
3355 * OpenSSL headers do strange typecasts confusing GCC.. */
3356 /* PEM_write_SSL_SESSION(debug_log, SSL_get_session(ssl)); */
3357 #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00908000L
3358 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);
3359
3360 #elif (ALLOW_ALWAYS_SSL_SESSION_DETAIL == 1)
3361
3362 /* When using gcc 3.3.x and OpenSSL 0.9.7x sometimes a compile error can occur here.
3363 * This is caused by an unpredicatble gcc behaviour on a cast of the first argument
3364 * of PEM_ASN1_write(). For this reason this code section is disabled. To enable it,
3365 * define ALLOW_ALWAYS_SSL_SESSION_DETAIL=1.
3366 * Because there are two possible usable cast, if you get an error here, try the other
3367 * commented line. */
3368
3369 PEM_ASN1_write((int(*)())i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL);
3370 /* PEM_ASN1_write((int(*)(...))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL); */
3371
3372 #else
3373
3374 debugs(83, 4, "With " OPENSSL_VERSION_TEXT ", session details are available only defining ALLOW_ALWAYS_SSL_SESSION_DETAIL=1 in the source." );
3375
3376 #endif
3377 /* Note: This does not automatically fflush the log file.. */
3378 }
3379
3380 debugs(83, 2, "clientNegotiateSSL: New session " <<
3381 SSL_get_session(ssl) << " on FD " << fd << " (" <<
3382 fd_table[fd].ipaddr << ":" << (int)fd_table[fd].remote_port <<
3383 ")");
3384 }
3385
3386 // Connection established. Retrieve TLS connection parameters for logging.
3387 conn->clientConnection->tlsNegotiations()->fillWith(ssl);
3388
3389 client_cert = SSL_get_peer_certificate(ssl);
3390
3391 if (client_cert != NULL) {
3392 debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
3393 " client certificate: subject: " <<
3394 X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0));
3395
3396 debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
3397 " client certificate: issuer: " <<
3398 X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0));
3399
3400 X509_free(client_cert);
3401 } else {
3402 debugs(83, 5, "clientNegotiateSSL: FD " << fd <<
3403 " has no certificate.");
3404 }
3405
3406 #if defined(TLSEXT_NAMETYPE_host_name)
3407 if (!conn->serverBump()) {
3408 // when in bumpClientFirst mode, get the server name from SNI
3409 if (const char *server = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))
3410 conn->resetSslCommonName(server);
3411 }
3412 #endif
3413
3414 conn->readSomeData();
3415 }
3416
3417 /**
3418 * If Security::ContextPtr is given, starts reading the TLS handshake.
3419 * Otherwise, calls switchToHttps to generate a dynamic Security::ContextPtr.
3420 */
3421 static void
3422 httpsEstablish(ConnStateData *connState, Security::ContextPtr sslContext)
3423 {
3424 Security::SessionPointer ssl = nullptr;
3425 assert(connState);
3426 const Comm::ConnectionPointer &details = connState->clientConnection;
3427
3428 if (!sslContext || !(ssl = httpsCreate(details, sslContext)))
3429 return;
3430
3431 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
3432 AsyncCall::Pointer timeoutCall = JobCallback(33, 5, TimeoutDialer,
3433 connState, ConnStateData::requestTimeout);
3434 commSetConnTimeout(details, Config.Timeout.request, timeoutCall);
3435
3436 Comm::SetSelect(details->fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
3437 }
3438
3439 /**
3440 * A callback function to use with the ACLFilledChecklist callback.
3441 * In the case of ACCESS_ALLOWED answer initializes a bumped SSL connection,
3442 * else reverts the connection to tunnel mode.
3443 */
3444 static void
3445 httpsSslBumpAccessCheckDone(allow_t answer, void *data)
3446 {
3447 ConnStateData *connState = (ConnStateData *) data;
3448
3449 // if the connection is closed or closing, just return.
3450 if (!connState->isOpen())
3451 return;
3452
3453 // Require both a match and a positive bump mode to work around exceptional
3454 // cases where ACL code may return ACCESS_ALLOWED with zero answer.kind.
3455 if (answer == ACCESS_ALLOWED && (answer.kind != Ssl::bumpNone && answer.kind != Ssl::bumpSplice)) {
3456 debugs(33, 2, "sslBump needed for " << connState->clientConnection << " method " << answer.kind);
3457 connState->sslBumpMode = static_cast<Ssl::BumpMode>(answer.kind);
3458 } else {
3459 debugs(33, 2, HERE << "sslBump not needed for " << connState->clientConnection);
3460 connState->sslBumpMode = Ssl::bumpNone;
3461 }
3462 connState->fakeAConnectRequest("ssl-bump", connState->inBuf);
3463 }
3464
3465 /** handle a new HTTPS connection */
3466 static void
3467 httpsAccept(const CommAcceptCbParams &params)
3468 {
3469 MasterXaction::Pointer xact = params.xaction;
3470 const AnyP::PortCfgPointer s = xact->squidPort;
3471
3472 // NP: it is possible the port was reconfigured when the call or accept() was queued.
3473
3474 if (params.flag != Comm::OK) {
3475 // Its possible the call was still queued when the client disconnected
3476 debugs(33, 2, "httpsAccept: " << s->listenConn << ": accept failure: " << xstrerr(params.xerrno));
3477 return;
3478 }
3479
3480 debugs(33, 4, HERE << params.conn << " accepted, starting SSL negotiation.");
3481 fd_note(params.conn->fd, "client https connect");
3482
3483 if (s->tcp_keepalive.enabled) {
3484 commSetTcpKeepalive(params.conn->fd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
3485 }
3486
3487 ++incoming_sockets_accepted;
3488
3489 // Socket is ready, setup the connection manager to start using it
3490 ConnStateData *connState = Https::NewServer(xact);
3491 AsyncJob::Start(connState); // usually async-calls postHttpsAccept()
3492 }
3493
3494 void
3495 ConnStateData::postHttpsAccept()
3496 {
3497 if (port->flags.tunnelSslBumping) {
3498 debugs(33, 5, "accept transparent connection: " << clientConnection);
3499
3500 if (!Config.accessList.ssl_bump) {
3501 httpsSslBumpAccessCheckDone(ACCESS_DENIED, this);
3502 return;
3503 }
3504
3505 // Create a fake HTTP request for ssl_bump ACL check,
3506 // using tproxy/intercept provided destination IP and port.
3507 HttpRequest *request = new HttpRequest();
3508 static char ip[MAX_IPSTRLEN];
3509 assert(clientConnection->flags & (COMM_TRANSPARENT | COMM_INTERCEPTION));
3510 request->url.host(clientConnection->local.toStr(ip, sizeof(ip)));
3511 request->url.port(clientConnection->local.port());
3512 request->myportname = port->name;
3513
3514 ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(Config.accessList.ssl_bump, request, NULL);
3515 acl_checklist->src_addr = clientConnection->remote;
3516 acl_checklist->my_addr = port->s;
3517 acl_checklist->nonBlockingCheck(httpsSslBumpAccessCheckDone, this);
3518 return;
3519 } else {
3520 httpsEstablish(this, port->secure.staticContext.get());
3521 }
3522 }
3523
3524 void
3525 ConnStateData::sslCrtdHandleReplyWrapper(void *data, const Helper::Reply &reply)
3526 {
3527 ConnStateData * state_data = (ConnStateData *)(data);
3528 state_data->sslCrtdHandleReply(reply);
3529 }
3530
3531 void
3532 ConnStateData::sslCrtdHandleReply(const Helper::Reply &reply)
3533 {
3534 if (!isOpen()) {
3535 debugs(33, 3, "Connection gone while waiting for ssl_crtd helper reply; helper reply:" << reply);
3536 return;
3537 }
3538
3539 if (reply.result == Helper::BrokenHelper) {
3540 debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply);
3541 } else if (!reply.other().hasContent()) {
3542 debugs(1, DBG_IMPORTANT, HERE << "\"ssl_crtd\" helper returned <NULL> reply.");
3543 } else {
3544 Ssl::CrtdMessage reply_message(Ssl::CrtdMessage::REPLY);
3545 if (reply_message.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK) {
3546 debugs(33, 5, HERE << "Reply from ssl_crtd for " << sslConnectHostOrIp << " is incorrect");
3547 } else {
3548 if (reply.result != Helper::Okay) {
3549 debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply_message.getBody());
3550 } else {
3551 debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " was successfully recieved from ssl_crtd");
3552 if (sslServerBump && (sslServerBump->act.step1 == Ssl::bumpPeek || sslServerBump->act.step1 == Ssl::bumpStare)) {
3553 doPeekAndSpliceStep();
3554 auto ssl = fd_table[clientConnection->fd].ssl;
3555 bool ret = Ssl::configureSSLUsingPkeyAndCertFromMemory(ssl, reply_message.getBody().c_str(), *port);
3556 if (!ret)
3557 debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
3558 } else {
3559 auto ctx = Ssl::generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str(), *port);
3560 getSslContextDone(ctx, true);
3561 }
3562 return;
3563 }
3564 }
3565 }
3566 getSslContextDone(NULL);
3567 }
3568
3569 void ConnStateData::buildSslCertGenerationParams(Ssl::CertificateProperties &certProperties)
3570 {
3571 certProperties.commonName = sslCommonName_.isEmpty() ? sslConnectHostOrIp.termedBuf() : sslCommonName_.c_str();
3572
3573 // fake certificate adaptation requires bump-server-first mode
3574 if (!sslServerBump) {
3575 assert(port->signingCert.get());
3576 certProperties.signWithX509.resetAndLock(port->signingCert.get());
3577 if (port->signPkey.get())
3578 certProperties.signWithPkey.resetAndLock(port->signPkey.get());
3579 certProperties.signAlgorithm = Ssl::algSignTrusted;
3580 return;
3581 }
3582
3583 // In case of an error while connecting to the secure server, use a fake
3584 // trusted certificate, with no mimicked fields and no adaptation
3585 // algorithms. There is nothing we can mimic so we want to minimize the
3586 // number of warnings the user will have to see to get to the error page.
3587 assert(sslServerBump->entry);
3588 if (sslServerBump->entry->isEmpty()) {
3589 if (X509 *mimicCert = sslServerBump->serverCert.get())
3590 certProperties.mimicCert.resetAndLock(mimicCert);
3591
3592 ACLFilledChecklist checklist(NULL, sslServerBump->request.getRaw(),
3593 clientConnection != NULL ? clientConnection->rfc931 : dash_str);
3594 checklist.sslErrors = cbdataReference(sslServerBump->sslErrors);
3595
3596 for (sslproxy_cert_adapt *ca = Config.ssl_client.cert_adapt; ca != NULL; ca = ca->next) {
3597 // If the algorithm already set, then ignore it.
3598 if ((ca->alg == Ssl::algSetCommonName && certProperties.setCommonName) ||
3599 (ca->alg == Ssl::algSetValidAfter && certProperties.setValidAfter) ||
3600 (ca->alg == Ssl::algSetValidBefore && certProperties.setValidBefore) )
3601 continue;
3602
3603 if (ca->aclList && checklist.fastCheck(ca->aclList) == ACCESS_ALLOWED) {
3604 const char *alg = Ssl::CertAdaptAlgorithmStr[ca->alg];
3605 const char *param = ca->param;
3606
3607 // For parameterless CN adaptation, use hostname from the
3608 // CONNECT request.
3609 if (ca->alg == Ssl::algSetCommonName) {
3610 if (!param)
3611 param = sslConnectHostOrIp.termedBuf();
3612 certProperties.commonName = param;
3613 certProperties.setCommonName = true;
3614 } else if (ca->alg == Ssl::algSetValidAfter)
3615 certProperties.setValidAfter = true;
3616 else if (ca->alg == Ssl::algSetValidBefore)
3617 certProperties.setValidBefore = true;
3618
3619 debugs(33, 5, HERE << "Matches certificate adaptation aglorithm: " <<
3620 alg << " param: " << (param ? param : "-"));
3621 }
3622 }
3623
3624 certProperties.signAlgorithm = Ssl::algSignEnd;
3625 for (sslproxy_cert_sign *sg = Config.ssl_client.cert_sign; sg != NULL; sg = sg->next) {
3626 if (sg->aclList && checklist.fastCheck(sg->aclList) == ACCESS_ALLOWED) {
3627 certProperties.signAlgorithm = (Ssl::CertSignAlgorithm)sg->alg;
3628 break;
3629 }
3630 }
3631 } else {// if (!sslServerBump->entry->isEmpty())
3632 // Use trusted certificate for a Squid-generated error
3633 // or the user would have to add a security exception
3634 // just to see the error page. We will close the connection
3635 // so that the trust is not extended to non-Squid content.
3636 certProperties.signAlgorithm = Ssl::algSignTrusted;
3637 }
3638
3639 assert(certProperties.signAlgorithm != Ssl::algSignEnd);
3640
3641 if (certProperties.signAlgorithm == Ssl::algSignUntrusted) {
3642 assert(port->untrustedSigningCert.get());
3643 certProperties.signWithX509.resetAndLock(port->untrustedSigningCert.get());
3644 certProperties.signWithPkey.resetAndLock(port->untrustedSignPkey.get());
3645 } else {
3646 assert(port->signingCert.get());
3647 certProperties.signWithX509.resetAndLock(port->signingCert.get());
3648
3649 if (port->signPkey.get())
3650 certProperties.signWithPkey.resetAndLock(port->signPkey.get());
3651 }
3652 signAlgorithm = certProperties.signAlgorithm;
3653
3654 certProperties.signHash = Ssl::DefaultSignHash;
3655 }
3656
3657 void
3658 ConnStateData::getSslContextStart()
3659 {
3660 // XXX starting SSL with a pipeline of requests still waiting for non-SSL replies?
3661 assert(pipeline.count() < 2); // the CONNECT is okay for now. Anything else is a bug.
3662 pipeline.terminateAll(0);
3663 /* careful: terminateAll(0) above frees request, host, etc. */
3664
3665 if (port->generateHostCertificates) {
3666 Ssl::CertificateProperties certProperties;
3667 buildSslCertGenerationParams(certProperties);
3668 sslBumpCertKey = certProperties.dbKey().c_str();
3669 assert(sslBumpCertKey.size() > 0 && sslBumpCertKey[0] != '\0');
3670
3671 // Disable caching for bumpPeekAndSplice mode
3672 if (!(sslServerBump && (sslServerBump->act.step1 == Ssl::bumpPeek || sslServerBump->act.step1 == Ssl::bumpStare))) {
3673 debugs(33, 5, "Finding SSL certificate for " << sslBumpCertKey << " in cache");
3674 Ssl::LocalContextStorage * ssl_ctx_cache = Ssl::TheGlobalContextStorage.getLocalStorage(port->s);
3675 Security::ContextPtr dynCtx = nullptr;
3676 Ssl::SSL_CTX_Pointer *cachedCtx = ssl_ctx_cache ? ssl_ctx_cache->get(sslBumpCertKey.termedBuf()) : NULL;
3677 if (cachedCtx && (dynCtx = cachedCtx->get())) {
3678 debugs(33, 5, "SSL certificate for " << sslBumpCertKey << " found in cache");
3679 if (Ssl::verifySslCertificate(dynCtx, certProperties)) {
3680 debugs(33, 5, "Cached SSL certificate for " << sslBumpCertKey << " is valid");
3681 getSslContextDone(dynCtx);
3682 return;
3683 } else {
3684 debugs(33, 5, "Cached SSL certificate for " << sslBumpCertKey << " is out of date. Delete this certificate from cache");
3685 if (ssl_ctx_cache)
3686 ssl_ctx_cache->del(sslBumpCertKey.termedBuf());
3687 }
3688 } else {
3689 debugs(33, 5, "SSL certificate for " << sslBumpCertKey << " haven't found in cache");
3690 }
3691 }
3692
3693 #if USE_SSL_CRTD
3694 try {
3695 debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName << " using ssl_crtd.");
3696 Ssl::CrtdMessage request_message(Ssl::CrtdMessage::REQUEST);
3697 request_message.setCode(Ssl::CrtdMessage::code_new_certificate);
3698 request_message.composeRequest(certProperties);
3699 debugs(33, 5, HERE << "SSL crtd request: " << request_message.compose().c_str());
3700 Ssl::Helper::GetInstance()->sslSubmit(request_message, sslCrtdHandleReplyWrapper, this);
3701 return;
3702 } catch (const std::exception &e) {
3703 debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtd " <<
3704 "request for " << certProperties.commonName <<
3705 " certificate: " << e.what() << "; will now block to " <<
3706 "generate that certificate.");
3707 // fall through to do blocking in-process generation.
3708 }
3709 #endif // USE_SSL_CRTD
3710
3711 debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName);
3712 if (sslServerBump && (sslServerBump->act.step1 == Ssl::bumpPeek || sslServerBump->act.step1 == Ssl::bumpStare)) {
3713 doPeekAndSpliceStep();
3714 auto ssl = fd_table[clientConnection->fd].ssl;
3715 if (!Ssl::configureSSL(ssl, certProperties, *port))
3716 debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
3717 } else {
3718 auto dynCtx = Ssl::generateSslContext(certProperties, *port);
3719 getSslContextDone(dynCtx, true);
3720 }
3721 return;
3722 }
3723 getSslContextDone(NULL);
3724 }
3725
3726 void
3727 ConnStateData::getSslContextDone(Security::ContextPtr sslContext, bool isNew)
3728 {
3729 // Try to add generated ssl context to storage.
3730 if (port->generateHostCertificates && isNew) {
3731
3732 if (signAlgorithm == Ssl::algSignTrusted) {
3733 // Add signing certificate to the certificates chain
3734 X509 *cert = port->signingCert.get();
3735 if (SSL_CTX_add_extra_chain_cert(sslContext, cert)) {
3736 // increase the certificate lock
3737 CRYPTO_add(&(cert->references),1,CRYPTO_LOCK_X509);
3738 } else {
3739 const int ssl_error = ERR_get_error();
3740 debugs(33, DBG_IMPORTANT, "WARNING: can not add signing certificate to SSL context chain: " << ERR_error_string(ssl_error, NULL));
3741 }
3742 Ssl::addChainToSslContext(sslContext, port->certsToChain.get());
3743 }
3744 //else it is self-signed or untrusted do not attrach any certificate
3745
3746 Ssl::LocalContextStorage *ssl_ctx_cache = Ssl::TheGlobalContextStorage.getLocalStorage(port->s);
3747 assert(sslBumpCertKey.size() > 0 && sslBumpCertKey[0] != '\0');
3748 if (sslContext) {
3749 if (!ssl_ctx_cache || !ssl_ctx_cache->add(sslBumpCertKey.termedBuf(), new Ssl::SSL_CTX_Pointer(sslContext))) {
3750 // If it is not in storage delete after using. Else storage deleted it.
3751 fd_table[clientConnection->fd].dynamicSslContext = sslContext;
3752 }
3753 } else {
3754 debugs(33, 2, HERE << "Failed to generate SSL cert for " << sslConnectHostOrIp);
3755 }
3756 }
3757
3758 // If generated ssl context = NULL, try to use static ssl context.
3759 if (!sslContext) {
3760 if (!port->secure.staticContext) {
3761 debugs(83, DBG_IMPORTANT, "Closing " << clientConnection->remote << " as lacking TLS context");
3762 clientConnection->close();
3763 return;
3764 } else {
3765 debugs(33, 5, "Using static TLS context.");
3766 sslContext = port->secure.staticContext.get();
3767 }
3768 }
3769
3770 if (!httpsCreate(clientConnection, sslContext))
3771 return;
3772
3773 // bumped intercepted conns should already have Config.Timeout.request set
3774 // but forwarded connections may only have Config.Timeout.lifetime. [Re]set
3775 // to make sure the connection does not get stuck on non-SSL clients.
3776 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
3777 AsyncCall::Pointer timeoutCall = JobCallback(33, 5, TimeoutDialer,
3778 this, ConnStateData::requestTimeout);
3779 commSetConnTimeout(clientConnection, Config.Timeout.request, timeoutCall);
3780
3781 // Disable the client read handler until CachePeer selection is complete
3782 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
3783 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, clientNegotiateSSL, this, 0);
3784 switchedToHttps_ = true;
3785 }
3786
3787 void
3788 ConnStateData::switchToHttps(HttpRequest *request, Ssl::BumpMode bumpServerMode)
3789 {
3790 assert(!switchedToHttps_);
3791
3792 sslConnectHostOrIp = request->url.host();
3793 resetSslCommonName(request->url.host());
3794
3795 // We are going to read new request
3796 flags.readMore = true;
3797 debugs(33, 5, HERE << "converting " << clientConnection << " to SSL");
3798
3799 // keep version major.minor details the same.
3800 // but we are now performing the HTTPS handshake traffic
3801 transferProtocol.protocol = AnyP::PROTO_HTTPS;
3802
3803 // If sslServerBump is set, then we have decided to deny CONNECT
3804 // and now want to switch to SSL to send the error to the client
3805 // without even peeking at the origin server certificate.
3806 if (bumpServerMode == Ssl::bumpServerFirst && !sslServerBump) {
3807 request->flags.sslPeek = true;
3808 sslServerBump = new Ssl::ServerBump(request);
3809
3810 // will call httpsPeeked() with certificate and connection, eventually
3811 FwdState::fwdStart(clientConnection, sslServerBump->entry, sslServerBump->request.getRaw());
3812 return;
3813 } else if (bumpServerMode == Ssl::bumpPeek || bumpServerMode == Ssl::bumpStare) {
3814 request->flags.sslPeek = true;
3815 sslServerBump = new Ssl::ServerBump(request, NULL, bumpServerMode);
3816 startPeekAndSplice();
3817 return;
3818 }
3819
3820 // otherwise, use sslConnectHostOrIp
3821 getSslContextStart();
3822 }
3823
3824 bool
3825 ConnStateData::spliceOnError(const err_type err)
3826 {
3827 if (Config.accessList.on_unsupported_protocol) {
3828 assert(serverBump());
3829 ACLFilledChecklist checklist(Config.accessList.on_unsupported_protocol, serverBump()->request.getRaw(), NULL);
3830 checklist.requestErrorType = err;
3831 checklist.conn(this);
3832 allow_t answer = checklist.fastCheck();
3833 if (answer == ACCESS_ALLOWED && answer.kind == 1) {
3834 splice();
3835 return true;
3836 }
3837 }
3838 return false;
3839 }
3840
3841 /** negotiate an SSL connection */
3842 static void
3843 clientPeekAndSpliceSSL(int fd, void *data)
3844 {
3845 ConnStateData *conn = (ConnStateData *)data;
3846 auto ssl = fd_table[fd].ssl;
3847
3848 debugs(83, 5, "Start peek and splice on FD " << fd);
3849
3850 int ret = 0;
3851 if ((ret = Squid_SSL_accept(conn, clientPeekAndSpliceSSL)) < 0)
3852 debugs(83, 2, "SSL_accept failed.");
3853
3854 BIO *b = SSL_get_rbio(ssl);
3855 assert(b);
3856 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
3857 if (ret < 0) {
3858 const err_type err = bio->noSslClient() ? ERR_PROTOCOL_UNKNOWN : ERR_SECURE_ACCEPT_FAIL;
3859 if (!conn->spliceOnError(err))
3860 conn->clientConnection->close();
3861 return;
3862 }
3863
3864 if (bio->rBufData().contentSize() > 0)
3865 conn->receivedFirstByte();
3866
3867 if (bio->gotHello()) {
3868 if (conn->serverBump()) {
3869 Ssl::Bio::sslFeatures const &features = bio->receivedHelloFeatures();
3870 if (!features.serverName.isEmpty()) {
3871 conn->serverBump()->clientSni = features.serverName;
3872 conn->resetSslCommonName(features.serverName.c_str());
3873 }
3874 }
3875
3876 debugs(83, 5, "I got hello. Start forwarding the request!!! ");
3877 Comm::SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
3878 Comm::SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
3879 conn->startPeekAndSpliceDone();
3880 return;
3881 }
3882 }
3883
3884 void ConnStateData::startPeekAndSplice()
3885 {
3886 // will call httpsPeeked() with certificate and connection, eventually
3887 auto unConfiguredCTX = Ssl::createSSLContext(port->signingCert, port->signPkey, *port);
3888 fd_table[clientConnection->fd].dynamicSslContext = unConfiguredCTX;
3889
3890 if (!httpsCreate(clientConnection, unConfiguredCTX))
3891 return;
3892
3893 // commSetConnTimeout() was called for this request before we switched.
3894 // Fix timeout to request_start_timeout
3895 typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
3896 AsyncCall::Pointer timeoutCall = JobCallback(33, 5,
3897 TimeoutDialer, this, ConnStateData::requestTimeout);
3898 commSetConnTimeout(clientConnection, Config.Timeout.request_start_timeout, timeoutCall);
3899 // Also reset receivedFirstByte_ flag to allow this timeout work in the case we have
3900 // a bumbed "connect" request on non transparent port.
3901 receivedFirstByte_ = false;
3902
3903 // Disable the client read handler until CachePeer selection is complete
3904 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
3905 Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, clientPeekAndSpliceSSL, this, 0);
3906 switchedToHttps_ = true;
3907
3908 auto ssl = fd_table[clientConnection->fd].ssl;
3909 BIO *b = SSL_get_rbio(ssl);
3910 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
3911 bio->hold(true);
3912 }
3913
3914 void httpsSslBumpStep2AccessCheckDone(allow_t answer, void *data)
3915 {
3916 ConnStateData *connState = (ConnStateData *) data;
3917
3918 // if the connection is closed or closing, just return.
3919 if (!connState->isOpen())
3920 return;
3921
3922 debugs(33, 5, "Answer: " << answer << " kind:" << answer.kind);
3923 assert(connState->serverBump());
3924 Ssl::BumpMode bumpAction;
3925 if (answer == ACCESS_ALLOWED) {
3926 bumpAction = (Ssl::BumpMode)answer.kind;
3927 } else
3928 bumpAction = Ssl::bumpSplice;
3929
3930 connState->serverBump()->act.step2 = bumpAction;
3931 connState->sslBumpMode = bumpAction;
3932
3933 if (bumpAction == Ssl::bumpTerminate) {
3934 connState->clientConnection->close();
3935 } else if (bumpAction != Ssl::bumpSplice) {
3936 connState->startPeekAndSpliceDone();
3937 } else
3938 connState->splice();
3939 }
3940
3941 void
3942 ConnStateData::splice()
3943 {
3944 //Normally we can splice here, because we just got client hello message
3945 auto ssl = fd_table[clientConnection->fd].ssl;
3946
3947 //retrieve received TLS client information
3948 clientConnection->tlsNegotiations()->fillWith(ssl);
3949
3950 BIO *b = SSL_get_rbio(ssl);
3951 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
3952 MemBuf const &rbuf = bio->rBufData();
3953 debugs(83,5, "Bio for " << clientConnection << " read " << rbuf.contentSize() << " helo bytes");
3954 // Do splice:
3955 fd_table[clientConnection->fd].read_method = &default_read_method;
3956 fd_table[clientConnection->fd].write_method = &default_write_method;
3957
3958 if (transparent()) {
3959 // set the current protocol to something sensible (was "HTTPS" for the bumping process)
3960 // we are sending a faked-up HTTP/1.1 message wrapper, so go with that.
3961 transferProtocol = Http::ProtocolVersion();
3962 // XXX: copy from MemBuf reallocates, not a regression since old code did too
3963 SBuf temp;
3964 temp.append(rbuf.content(), rbuf.contentSize());
3965 fakeAConnectRequest("intercepted TLS spliced", temp);
3966 } else {
3967 // XXX: assuming that there was an HTTP/1.1 CONNECT to begin with...
3968
3969 // reset the current protocol to HTTP/1.1 (was "HTTPS" for the bumping process)
3970 transferProtocol = Http::ProtocolVersion();
3971 // inBuf still has the "CONNECT ..." request data, reset it to SSL hello message
3972 inBuf.append(rbuf.content(), rbuf.contentSize());
3973 ClientSocketContext::Pointer context = pipeline.front();
3974 ClientHttpRequest *http = context->http;
3975 tunnelStart(http);
3976 }
3977 }
3978
3979 void
3980 ConnStateData::startPeekAndSpliceDone()
3981 {
3982 // This is the Step2 of the SSL bumping
3983 assert(sslServerBump);
3984 if (sslServerBump->step == Ssl::bumpStep1) {
3985 sslServerBump->step = Ssl::bumpStep2;
3986 // Run a accessList check to check if want to splice or continue bumping
3987
3988 ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(Config.accessList.ssl_bump, sslServerBump->request.getRaw(), NULL);
3989 //acl_checklist->src_addr = params.conn->remote;
3990 //acl_checklist->my_addr = s->s;
3991 acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpNone));
3992 acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpClientFirst));
3993 acl_checklist->banAction(allow_t(ACCESS_ALLOWED, Ssl::bumpServerFirst));
3994 acl_checklist->nonBlockingCheck(httpsSslBumpStep2AccessCheckDone, this);
3995 return;
3996 }
3997
3998 FwdState::fwdStart(clientConnection, sslServerBump->entry, sslServerBump->request.getRaw());
3999 }
4000
4001 void
4002 ConnStateData::doPeekAndSpliceStep()
4003 {
4004 auto ssl = fd_table[clientConnection->fd].ssl;
4005 BIO *b = SSL_get_rbio(ssl);
4006 assert(b);
4007 Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
4008
4009 debugs(33, 5, "PeekAndSplice mode, proceed with client negotiation. Currrent state:" << SSL_state_string_long(ssl));
4010 bio->hold(false);
4011
4012 Comm::SetSelect(clientConnection->fd, COMM_SELECT_WRITE, clientNegotiateSSL, this, 0);
4013 switchedToHttps_ = true;
4014 }
4015
4016 void
4017 ConnStateData::httpsPeeked(Comm::ConnectionPointer serverConnection)
4018 {
4019 Must(sslServerBump != NULL);
4020
4021 if (Comm::IsConnOpen(serverConnection)) {
4022 pinConnection(serverConnection, NULL, NULL, false);
4023
4024 debugs(33, 5, HERE << "bumped HTTPS server: " << sslConnectHostOrIp);
4025 } else {
4026 debugs(33, 5, HERE << "Error while bumping: " << sslConnectHostOrIp);
4027
4028 // copy error detail from bump-server-first request to CONNECT request
4029 if (!pipeline.empty() && pipeline.front()->http != nullptr && pipeline.front()->http->request)
4030 pipeline.front()->http->request->detailError(sslServerBump->request->errType, sslServerBump->request->errDetail);
4031 }
4032
4033 getSslContextStart();
4034 }
4035
4036 #endif /* USE_OPENSSL */
4037
4038 void
4039 ConnStateData::fakeAConnectRequest(const char *reason, const SBuf &payload)
4040 {
4041 // fake a CONNECT request to force connState to tunnel
4042 SBuf connectHost;
4043 #if USE_OPENSSL
4044 if (serverBump() && !serverBump()->clientSni.isEmpty()) {
4045 connectHost.assign(serverBump()->clientSni);
4046 if (clientConnection->local.port() > 0)
4047 connectHost.appendf(":%d",clientConnection->local.port());
4048 } else
4049 #endif
4050 {
4051 static char ip[MAX_IPSTRLEN];
4052 connectHost.assign(clientConnection->local.toUrl(ip, sizeof(ip)));
4053 }
4054 // Pre-pend this fake request to the TLS bits already in the buffer
4055 SBuf retStr;
4056 retStr.append("CONNECT ");
4057 retStr.append(connectHost);
4058 retStr.append(" HTTP/1.1\r\nHost: ");
4059 retStr.append(connectHost);
4060 retStr.append("\r\n\r\n");
4061 retStr.append(payload);
4062 inBuf = retStr;
4063 bool ret = handleReadData();
4064 if (ret)
4065 ret = clientParseRequests();
4066
4067 if (!ret) {
4068 debugs(33, 2, "Failed to start fake CONNECT request for " << reason << " connection: " << clientConnection);
4069 clientConnection->close();
4070 }
4071 }
4072
4073 /// check FD after clientHttp[s]ConnectionOpened, adjust HttpSockets as needed
4074 static bool
4075 OpenedHttpSocket(const Comm::ConnectionPointer &c, const Ipc::FdNoteId portType)
4076 {
4077 if (!Comm::IsConnOpen(c)) {
4078 Must(NHttpSockets > 0); // we tried to open some
4079 --NHttpSockets; // there will be fewer sockets than planned
4080 Must(HttpSockets[NHttpSockets] < 0); // no extra fds received
4081
4082 if (!NHttpSockets) // we could not open any listen sockets at all
4083 fatalf("Unable to open %s",FdNote(portType));
4084
4085 return false;
4086 }
4087 return true;
4088 }
4089
4090 /// find any unused HttpSockets[] slot and store fd there or return false
4091 static bool
4092 AddOpenedHttpSocket(const Comm::ConnectionPointer &conn)
4093 {
4094 bool found = false;
4095 for (int i = 0; i < NHttpSockets && !found; ++i) {
4096 if ((found = HttpSockets[i] < 0))
4097 HttpSockets[i] = conn->fd;
4098 }
4099 return found;
4100 }
4101
4102 static void
4103 clientHttpConnectionsOpen(void)
4104 {
4105 for (AnyP::PortCfgPointer s = HttpPortList; s != NULL; s = s->next) {
4106 const char *scheme = AnyP::UriScheme(s->transport.protocol).c_str();
4107
4108 if (MAXTCPLISTENPORTS == NHttpSockets) {
4109 debugs(1, DBG_IMPORTANT, "WARNING: You have too many '" << scheme << "_port' lines.");
4110 debugs(1, DBG_IMPORTANT, " The limit is " << MAXTCPLISTENPORTS << " HTTP ports.");
4111 continue;
4112 }
4113
4114 #if USE_OPENSSL
4115 if (s->flags.tunnelSslBumping) {
4116 if (!Config.accessList.ssl_bump) {
4117 debugs(33, DBG_IMPORTANT, "WARNING: No ssl_bump configured. Disabling ssl-bump on " << scheme << "_port " << s->s);
4118 s->flags.tunnelSslBumping = false;
4119 }
4120 if (!s->secure.staticContext && !s->generateHostCertificates) {
4121 debugs(1, DBG_IMPORTANT, "Will not bump SSL at " << scheme << "_port " << s->s << " due to TLS initialization failure.");
4122 s->flags.tunnelSslBumping = false;
4123 if (s->transport.protocol == AnyP::PROTO_HTTP)
4124 s->secure.encryptTransport = false;
4125 }
4126 if (s->flags.tunnelSslBumping) {
4127 // Create ssl_ctx cache for this port.
4128 auto sz = s->dynamicCertMemCacheSize == std::numeric_limits<size_t>::max() ? 4194304 : s->dynamicCertMemCacheSize;
4129 Ssl::TheGlobalContextStorage.addLocalStorage(s->s, sz);
4130 }
4131 }
4132
4133 if (s->secure.encryptTransport && !s->secure.staticContext) {
4134 debugs(1, DBG_CRITICAL, "ERROR: Ignoring " << scheme << "_port " << s->s << " due to TLS context initialization failure.");
4135 continue;
4136 }
4137 #endif
4138
4139 // Fill out a Comm::Connection which IPC will open as a listener for us
4140 // then pass back when active so we can start a TcpAcceptor subscription.
4141 s->listenConn = new Comm::Connection;
4142 s->listenConn->local = s->s;
4143
4144 s->listenConn->flags = COMM_NONBLOCKING | (s->flags.tproxyIntercept ? COMM_TRANSPARENT : 0) |
4145 (s->flags.natIntercept ? COMM_INTERCEPTION : 0);
4146
4147 typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
4148 if (s->transport.protocol == AnyP::PROTO_HTTP) {
4149 // setup the subscriptions such that new connections accepted by listenConn are handled by HTTP
4150 RefCount<AcceptCall> subCall = commCbCall(5, 5, "httpAccept", CommAcceptCbPtrFun(httpAccept, CommAcceptCbParams(NULL)));
4151 Subscription::Pointer sub = new CallSubscription<AcceptCall>(subCall);
4152
4153 AsyncCall::Pointer listenCall = asyncCall(33,2, "clientListenerConnectionOpened",
4154 ListeningStartedDialer(&clientListenerConnectionOpened, s, Ipc::fdnHttpSocket, sub));
4155 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->listenConn, Ipc::fdnHttpSocket, listenCall);
4156
4157 #if USE_OPENSSL
4158 } else if (s->transport.protocol == AnyP::PROTO_HTTPS) {
4159 // setup the subscriptions such that new connections accepted by listenConn are handled by HTTPS
4160 RefCount<AcceptCall> subCall = commCbCall(5, 5, "httpsAccept", CommAcceptCbPtrFun(httpsAccept, CommAcceptCbParams(NULL)));
4161 Subscription::Pointer sub = new CallSubscription<AcceptCall>(subCall);
4162
4163 AsyncCall::Pointer listenCall = asyncCall(33, 2, "clientListenerConnectionOpened",
4164 ListeningStartedDialer(&clientListenerConnectionOpened,
4165 s, Ipc::fdnHttpsSocket, sub));
4166 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->listenConn, Ipc::fdnHttpsSocket, listenCall);
4167 #endif
4168 }
4169
4170 HttpSockets[NHttpSockets] = -1; // set in clientListenerConnectionOpened
4171 ++NHttpSockets;
4172 }
4173 }
4174
4175 void
4176 clientStartListeningOn(AnyP::PortCfgPointer &port, const RefCount< CommCbFunPtrCallT<CommAcceptCbPtrFun> > &subCall, const Ipc::FdNoteId fdNote)
4177 {
4178 // Fill out a Comm::Connection which IPC will open as a listener for us
4179 port->listenConn = new Comm::Connection;
4180 port->listenConn->local = port->s;
4181 port->listenConn->flags =
4182 COMM_NONBLOCKING |
4183 (port->flags.tproxyIntercept ? COMM_TRANSPARENT : 0) |
4184 (port->flags.natIntercept ? COMM_INTERCEPTION : 0);
4185
4186 // route new connections to subCall
4187 typedef CommCbFunPtrCallT<CommAcceptCbPtrFun> AcceptCall;
4188 Subscription::Pointer sub = new CallSubscription<AcceptCall>(subCall);
4189 AsyncCall::Pointer listenCall =
4190 asyncCall(33, 2, "clientListenerConnectionOpened",
4191 ListeningStartedDialer(&clientListenerConnectionOpened,
4192 port, fdNote, sub));
4193 Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, port->listenConn, fdNote, listenCall);
4194
4195 assert(NHttpSockets < MAXTCPLISTENPORTS);
4196 HttpSockets[NHttpSockets] = -1;
4197 ++NHttpSockets;
4198 }
4199
4200 /// process clientHttpConnectionsOpen result
4201 static void
4202 clientListenerConnectionOpened(AnyP::PortCfgPointer &s, const Ipc::FdNoteId portTypeNote, const Subscription::Pointer &sub)
4203 {
4204 Must(s != NULL);
4205
4206 if (!OpenedHttpSocket(s->listenConn, portTypeNote))
4207 return;
4208
4209 Must(Comm::IsConnOpen(s->listenConn));
4210
4211 // TCP: setup a job to handle accept() with subscribed handler
4212 AsyncJob::Start(new Comm::TcpAcceptor(s, FdNote(portTypeNote), sub));
4213
4214 debugs(1, DBG_IMPORTANT, "Accepting " <<
4215 (s->flags.natIntercept ? "NAT intercepted " : "") <<
4216 (s->flags.tproxyIntercept ? "TPROXY intercepted " : "") <<
4217 (s->flags.tunnelSslBumping ? "SSL bumped " : "") <<
4218 (s->flags.accelSurrogate ? "reverse-proxy " : "")
4219 << FdNote(portTypeNote) << " connections at "
4220 << s->listenConn);
4221
4222 Must(AddOpenedHttpSocket(s->listenConn)); // otherwise, we have received a fd we did not ask for
4223 }
4224
4225 void
4226 clientOpenListenSockets(void)
4227 {
4228 clientHttpConnectionsOpen();
4229 Ftp::StartListening();
4230
4231 if (NHttpSockets < 1)
4232 fatal("No HTTP, HTTPS, or FTP ports configured");
4233 }
4234
4235 void
4236 clientConnectionsClose()
4237 {
4238 for (AnyP::PortCfgPointer s = HttpPortList; s != NULL; s = s->next) {
4239 if (s->listenConn != NULL) {
4240 debugs(1, DBG_IMPORTANT, "Closing HTTP(S) port " << s->listenConn->local);
4241 s->listenConn->close();
4242 s->listenConn = NULL;
4243 }
4244 }
4245
4246 Ftp::StopListening();
4247
4248 // TODO see if we can drop HttpSockets array entirely */
4249 for (int i = 0; i < NHttpSockets; ++i) {
4250 HttpSockets[i] = -1;
4251 }
4252
4253 NHttpSockets = 0;
4254 }
4255
4256 int
4257 varyEvaluateMatch(StoreEntry * entry, HttpRequest * request)
4258 {
4259 const char *vary = request->vary_headers;
4260 int has_vary = entry->getReply()->header.has(Http::HdrType::VARY);
4261 #if X_ACCELERATOR_VARY
4262
4263 has_vary |=
4264 entry->getReply()->header.has(Http::HdrType::HDR_X_ACCELERATOR_VARY);
4265 #endif
4266
4267 if (!has_vary || !entry->mem_obj->vary_headers) {
4268 if (vary) {
4269 /* Oops... something odd is going on here.. */
4270 debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary object on second attempt, '" <<
4271 entry->mem_obj->urlXXX() << "' '" << vary << "'");
4272 safe_free(request->vary_headers);
4273 return VARY_CANCEL;
4274 }
4275
4276 if (!has_vary) {
4277 /* This is not a varying object */
4278 return VARY_NONE;
4279 }
4280
4281 /* virtual "vary" object found. Calculate the vary key and
4282 * continue the search
4283 */
4284 vary = httpMakeVaryMark(request, entry->getReply());
4285
4286 if (vary) {
4287 request->vary_headers = xstrdup(vary);
4288 return VARY_OTHER;
4289 } else {
4290 /* Ouch.. we cannot handle this kind of variance */
4291 /* XXX This cannot really happen, but just to be complete */
4292 return VARY_CANCEL;
4293 }
4294 } else {
4295 if (!vary) {
4296 vary = httpMakeVaryMark(request, entry->getReply());
4297
4298 if (vary)
4299 request->vary_headers = xstrdup(vary);
4300 }
4301
4302 if (!vary) {
4303 /* Ouch.. we cannot handle this kind of variance */
4304 /* XXX This cannot really happen, but just to be complete */
4305 return VARY_CANCEL;
4306 } else if (strcmp(vary, entry->mem_obj->vary_headers) == 0) {
4307 return VARY_MATCH;
4308 } else {
4309 /* Oops.. we have already been here and still haven't
4310 * found the requested variant. Bail out
4311 */
4312 debugs(33, DBG_IMPORTANT, "varyEvaluateMatch: Oops. Not a Vary match on second attempt, '" <<
4313 entry->mem_obj->urlXXX() << "' '" << vary << "'");
4314 return VARY_CANCEL;
4315 }
4316 }
4317 }
4318
4319 ACLFilledChecklist *
4320 clientAclChecklistCreate(const acl_access * acl, ClientHttpRequest * http)
4321 {
4322 ConnStateData * conn = http->getConn();
4323 ACLFilledChecklist *ch = new ACLFilledChecklist(acl, http->request,
4324 cbdataReferenceValid(conn) && conn != NULL && conn->clientConnection != NULL ? conn->clientConnection->rfc931 : dash_str);
4325 ch->al = http->al;
4326 /*
4327 * hack for ident ACL. It needs to get full addresses, and a place to store
4328 * the ident result on persistent connections...
4329 */
4330 /* connection oriented auth also needs these two lines for it's operation. */
4331 return ch;
4332 }
4333
4334 bool
4335 ConnStateData::transparent() const
4336 {
4337 return clientConnection != NULL && (clientConnection->flags & (COMM_TRANSPARENT|COMM_INTERCEPTION));
4338 }
4339
4340 BodyPipe::Pointer
4341 ConnStateData::expectRequestBody(int64_t size)
4342 {
4343 bodyPipe = new BodyPipe(this);
4344 if (size >= 0)
4345 bodyPipe->setBodySize(size);
4346 else
4347 startDechunkingRequest();
4348 return bodyPipe;
4349 }
4350
4351 int64_t
4352 ConnStateData::mayNeedToReadMoreBody() const
4353 {
4354 if (!bodyPipe)
4355 return 0; // request without a body or read/produced all body bytes
4356
4357 if (!bodyPipe->bodySizeKnown())
4358 return -1; // probably need to read more, but we cannot be sure
4359
4360 const int64_t needToProduce = bodyPipe->unproducedSize();
4361 const int64_t haveAvailable = static_cast<int64_t>(inBuf.length());
4362
4363 if (needToProduce <= haveAvailable)
4364 return 0; // we have read what we need (but are waiting for pipe space)
4365
4366 return needToProduce - haveAvailable;
4367 }
4368
4369 void
4370 ConnStateData::stopReceiving(const char *error)
4371 {
4372 debugs(33, 4, HERE << "receiving error (" << clientConnection << "): " << error <<
4373 "; old sending error: " <<
4374 (stoppedSending() ? stoppedSending_ : "none"));
4375
4376 if (const char *oldError = stoppedReceiving()) {
4377 debugs(33, 3, HERE << "already stopped receiving: " << oldError);
4378 return; // nothing has changed as far as this connection is concerned
4379 }
4380
4381 stoppedReceiving_ = error;
4382
4383 if (const char *sendError = stoppedSending()) {
4384 debugs(33, 3, HERE << "closing because also stopped sending: " << sendError);
4385 clientConnection->close();
4386 }
4387 }
4388
4389 void
4390 ConnStateData::expectNoForwarding()
4391 {
4392 if (bodyPipe != NULL) {
4393 debugs(33, 4, HERE << "no consumer for virgin body " << bodyPipe->status());
4394 bodyPipe->expectNoConsumption();
4395 }
4396 }
4397
4398 /// initialize dechunking state
4399 void
4400 ConnStateData::startDechunkingRequest()
4401 {
4402 Must(bodyPipe != NULL);
4403 debugs(33, 5, HERE << "start dechunking" << bodyPipe->status());
4404 assert(!bodyParser);
4405 bodyParser = new Http1::TeChunkedParser;
4406 }
4407
4408 /// put parsed content into input buffer and clean up
4409 void
4410 ConnStateData::finishDechunkingRequest(bool withSuccess)
4411 {
4412 debugs(33, 5, HERE << "finish dechunking: " << withSuccess);
4413
4414 if (bodyPipe != NULL) {
4415 debugs(33, 7, HERE << "dechunked tail: " << bodyPipe->status());
4416 BodyPipe::Pointer myPipe = bodyPipe;
4417 stopProducingFor(bodyPipe, withSuccess); // sets bodyPipe->bodySize()
4418 Must(!bodyPipe); // we rely on it being nil after we are done with body
4419 if (withSuccess) {
4420 Must(myPipe->bodySizeKnown());
4421 ClientSocketContext::Pointer context = pipeline.front();
4422 if (context != NULL && context->http && context->http->request)
4423 context->http->request->setContentLength(myPipe->bodySize());
4424 }
4425 }
4426
4427 delete bodyParser;
4428 bodyParser = NULL;
4429 }
4430
4431 // XXX: this is an HTTP/1-only operation
4432 void
4433 ConnStateData::sendControlMsg(HttpControlMsg msg)
4434 {
4435 if (!isOpen()) {
4436 debugs(33, 3, HERE << "ignoring 1xx due to earlier closure");
4437 return;
4438 }
4439
4440 // HTTP/1 1xx status messages are only valid when there is a transaction to trigger them
4441 if (!pipeline.empty()) {
4442 HttpReply::Pointer rep(msg.reply);
4443 Must(rep);
4444 // remember the callback
4445 cbControlMsgSent = msg.cbSuccess;
4446
4447 typedef CommCbMemFunT<HttpControlMsgSink, CommIoCbParams> Dialer;
4448 AsyncCall::Pointer call = JobCallback(33, 5, Dialer, this, HttpControlMsgSink::wroteControlMsg);
4449
4450 writeControlMsgAndCall(rep.getRaw(), call);
4451 return;
4452 }
4453
4454 debugs(33, 3, HERE << " closing due to missing context for 1xx");
4455 clientConnection->close();
4456 }
4457
4458 /// Our close handler called by Comm when the pinned connection is closed
4459 void
4460 ConnStateData::clientPinnedConnectionClosed(const CommCloseCbParams &io)
4461 {
4462 // FwdState might repin a failed connection sooner than this close
4463 // callback is called for the failed connection.
4464 assert(pinning.serverConnection == io.conn);
4465 pinning.closeHandler = NULL; // Comm unregisters handlers before calling
4466 const bool sawZeroReply = pinning.zeroReply; // reset when unpinning
4467 pinning.serverConnection->noteClosure();
4468 unpinConnection(false);
4469
4470 if (sawZeroReply && clientConnection != NULL) {
4471 debugs(33, 3, "Closing client connection on pinned zero reply.");
4472 clientConnection->close();
4473 }
4474
4475 }
4476
4477 void
4478 ConnStateData::pinConnection(const Comm::ConnectionPointer &pinServer, HttpRequest *request, CachePeer *aPeer, bool auth, bool monitor)
4479 {
4480 if (!Comm::IsConnOpen(pinning.serverConnection) ||
4481 pinning.serverConnection->fd != pinServer->fd)
4482 pinNewConnection(pinServer, request, aPeer, auth);
4483
4484 if (monitor)
4485 startPinnedConnectionMonitoring();
4486 }
4487
4488 void
4489 ConnStateData::pinNewConnection(const Comm::ConnectionPointer &pinServer, HttpRequest *request, CachePeer *aPeer, bool auth)
4490 {
4491 unpinConnection(true); // closes pinned connection, if any, and resets fields
4492
4493 pinning.serverConnection = pinServer;
4494
4495 debugs(33, 3, HERE << pinning.serverConnection);
4496
4497 Must(pinning.serverConnection != NULL);
4498
4499 // when pinning an SSL bumped connection, the request may be NULL
4500 const char *pinnedHost = "[unknown]";
4501 if (request) {
4502 pinning.host = xstrdup(request->url.host());
4503 pinning.port = request->url.port();
4504 pinnedHost = pinning.host;
4505 } else {
4506 pinning.port = pinServer->remote.port();
4507 }
4508 pinning.pinned = true;
4509 if (aPeer)
4510 pinning.peer = cbdataReference(aPeer);
4511 pinning.auth = auth;
4512 char stmp[MAX_IPSTRLEN];
4513 char desc[FD_DESC_SZ];
4514 snprintf(desc, FD_DESC_SZ, "%s pinned connection for %s (%d)",
4515 (auth || !aPeer) ? pinnedHost : aPeer->name,
4516 clientConnection->remote.toUrl(stmp,MAX_IPSTRLEN),
4517 clientConnection->fd);
4518 fd_note(pinning.serverConnection->fd, desc);
4519
4520 typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
4521 pinning.closeHandler = JobCallback(33, 5,
4522 Dialer, this, ConnStateData::clientPinnedConnectionClosed);
4523 // remember the pinned connection so that cb does not unpin a fresher one
4524 typedef CommCloseCbParams Params;
4525 Params &params = GetCommParams<Params>(pinning.closeHandler);
4526 params.conn = pinning.serverConnection;
4527 comm_add_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
4528 }
4529
4530 /// [re]start monitoring pinned connection for peer closures so that we can
4531 /// propagate them to an _idle_ client pinned to that peer
4532 void
4533 ConnStateData::startPinnedConnectionMonitoring()
4534 {
4535 if (pinning.readHandler != NULL)
4536 return; // already monitoring
4537
4538 typedef CommCbMemFunT<ConnStateData, CommIoCbParams> Dialer;
4539 pinning.readHandler = JobCallback(33, 3,
4540 Dialer, this, ConnStateData::clientPinnedConnectionRead);
4541 Comm::Read(pinning.serverConnection, pinning.readHandler);
4542 }
4543
4544 void
4545 ConnStateData::stopPinnedConnectionMonitoring()
4546 {
4547 if (pinning.readHandler != NULL) {
4548 Comm::ReadCancel(pinning.serverConnection->fd, pinning.readHandler);
4549 pinning.readHandler = NULL;
4550 }
4551 }
4552
4553 #if USE_OPENSSL
4554 bool
4555 ConnStateData::handleIdleClientPinnedTlsRead()
4556 {
4557 // A ready-for-reading connection means that the TLS server either closed
4558 // the connection, sent us some unexpected HTTP data, or started TLS
4559 // renegotiations. We should close the connection except for the last case.
4560
4561 Must(pinning.serverConnection != nullptr);
4562 SSL *ssl = fd_table[pinning.serverConnection->fd].ssl;
4563 if (!ssl)
4564 return false;
4565
4566 char buf[1];
4567 const int readResult = SSL_read(ssl, buf, sizeof(buf));
4568
4569 if (readResult > 0 || SSL_pending(ssl) > 0) {
4570 debugs(83, 2, pinning.serverConnection << " TLS application data read");
4571 return false;
4572 }
4573
4574 switch(const int error = SSL_get_error(ssl, readResult)) {
4575 case SSL_ERROR_WANT_WRITE:
4576 debugs(83, DBG_IMPORTANT, pinning.serverConnection << " TLS SSL_ERROR_WANT_WRITE request for idle pinned connection");
4577 // fall through to restart monitoring, for now
4578 case SSL_ERROR_NONE:
4579 case SSL_ERROR_WANT_READ:
4580 startPinnedConnectionMonitoring();
4581 return true;
4582
4583 default:
4584 debugs(83, 2, pinning.serverConnection << " TLS error: " << error);
4585 return false;
4586 }
4587
4588 // not reached
4589 return true;
4590 }
4591 #endif
4592
4593 /// Our read handler called by Comm when the server either closes an idle pinned connection or
4594 /// perhaps unexpectedly sends something on that idle (from Squid p.o.v.) connection.
4595 void
4596 ConnStateData::clientPinnedConnectionRead(const CommIoCbParams &io)
4597 {
4598 pinning.readHandler = NULL; // Comm unregisters handlers before calling
4599
4600 if (io.flag == Comm::ERR_CLOSING)
4601 return; // close handler will clean up
4602
4603 Must(pinning.serverConnection == io.conn);
4604
4605 #if USE_OPENSSL
4606 if (handleIdleClientPinnedTlsRead())
4607 return;
4608 #endif
4609
4610 const bool clientIsIdle = pipeline.empty();
4611
4612 debugs(33, 3, "idle pinned " << pinning.serverConnection << " read " <<
4613 io.size << (clientIsIdle ? " with idle client" : ""));
4614
4615 pinning.serverConnection->close();
4616
4617 // If we are still sending data to the client, do not close now. When we are done sending,
4618 // ConnStateData::kick() checks pinning.serverConnection and will close.
4619 // However, if we are idle, then we must close to inform the idle client and minimize races.
4620 if (clientIsIdle && clientConnection != NULL)
4621 clientConnection->close();
4622 }
4623
4624 const Comm::ConnectionPointer
4625 ConnStateData::validatePinnedConnection(HttpRequest *request, const CachePeer *aPeer)
4626 {
4627 debugs(33, 7, HERE << pinning.serverConnection);
4628
4629 bool valid = true;
4630 if (!Comm::IsConnOpen(pinning.serverConnection))
4631 valid = false;
4632 else if (pinning.auth && pinning.host && request && strcasecmp(pinning.host, request->url.host()) != 0)
4633 valid = false;
4634 else if (request && pinning.port != request->url.port())
4635 valid = false;
4636 else if (pinning.peer && !cbdataReferenceValid(pinning.peer))
4637 valid = false;
4638 else if (aPeer != pinning.peer)
4639 valid = false;
4640
4641 if (!valid) {
4642 /* The pinning info is not safe, remove any pinning info */
4643 unpinConnection(true);
4644 }
4645
4646 return pinning.serverConnection;
4647 }
4648
4649 Comm::ConnectionPointer
4650 ConnStateData::borrowPinnedConnection(HttpRequest *request, const CachePeer *aPeer)
4651 {
4652 debugs(33, 7, pinning.serverConnection);
4653 if (validatePinnedConnection(request, aPeer) != NULL)
4654 stopPinnedConnectionMonitoring();
4655
4656 return pinning.serverConnection; // closed if validation failed
4657 }
4658
4659 void
4660 ConnStateData::unpinConnection(const bool andClose)
4661 {
4662 debugs(33, 3, HERE << pinning.serverConnection);
4663
4664 if (pinning.peer)
4665 cbdataReferenceDone(pinning.peer);
4666
4667 if (Comm::IsConnOpen(pinning.serverConnection)) {
4668 if (pinning.closeHandler != NULL) {
4669 comm_remove_close_handler(pinning.serverConnection->fd, pinning.closeHandler);
4670 pinning.closeHandler = NULL;
4671 }
4672
4673 stopPinnedConnectionMonitoring();
4674
4675 // close the server side socket if requested
4676 if (andClose)
4677 pinning.serverConnection->close();
4678 pinning.serverConnection = NULL;
4679 }
4680
4681 safe_free(pinning.host);
4682
4683 pinning.zeroReply = false;
4684
4685 /* NOTE: pinning.pinned should be kept. This combined with fd == -1 at the end of a request indicates that the host
4686 * connection has gone away */
4687 }
4688