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