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