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