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