Squid Web Cache master
Loading...
Searching...
No Matches
FtpClient.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2026 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9/* DEBUG: section 09 File Transfer Protocol (FTP) */
10
11#include "squid.h"
12#include "acl/FilledChecklist.h"
13#include "base/AsyncJobCalls.h"
14#include "base/Range.h"
15#include "client_side.h"
16#include "clients/FtpClient.h"
17#include "comm/ConnOpener.h"
18#include "comm/Read.h"
19#include "comm/TcpAcceptor.h"
20#include "comm/Write.h"
22#include "errorpage.h"
23#include "fd.h"
24#include "ftp/Parsing.h"
25#include "http/Stream.h"
26#include "ip/tools.h"
27#include "sbuf/SBuf.h"
28#include "sbuf/Stream.h"
29#include "SquidConfig.h"
30#include "SquidString.h"
31#include "StatCounters.h"
32#include "tools.h"
33#include "wordlist.h"
34
35#include <set>
36
37namespace Ftp
38{
39
40const char *const crlf = "\r\n";
41
42static char *
43escapeIAC(const char *buf)
44{
45 int n;
46 char *ret;
47 unsigned const char *p;
48 unsigned char *r;
49
50 for (p = (unsigned const char *)buf, n = 1; *p; ++n, ++p)
51 if (*p == 255)
52 ++n;
53
54 ret = (char *)xmalloc(n);
55
56 for (p = (unsigned const char *)buf, r=(unsigned char *)ret; *p; ++p) {
57 *r = *p;
58 ++r;
59
60 if (*p == 255) {
61 *r = 255;
62 ++r;
63 }
64 }
65
66 *r = '\0';
67 ++r;
68 assert((r - (unsigned char *)ret) == n );
69 return ret;
70}
71
72/* Ftp::ErrorDetail */
73
74SBuf
76{
77 return ToSBuf("FTP_REPLY_CODE=", completionCode);
78}
79
80SBuf
82{
83 return ToSBuf("FTP reply with completion code ", completionCode);
84}
85
86/* Ftp::Channel */
87
89void
91 const AsyncCall::Pointer &aCloser)
92{
94 assert(closer == nullptr);
95
96 assert(Comm::IsConnOpen(newConn));
97 assert(aCloser != nullptr);
98
99 conn = newConn;
100 conn->leaveOrphanage();
101 closer = aCloser;
102 comm_add_close_handler(conn->fd, closer);
103}
104
106void
108{
109 // channels with active listeners will be closed when the listener handler dies.
110 if (Comm::IsConnOpen(conn)) {
111 comm_remove_close_handler(conn->fd, closer);
112 conn->close(); // we do not expect to be called back
113 }
114 clear();
115}
116
117void
119{
120 if (Comm::IsConnOpen(conn)) {
122 comm_remove_close_handler(conn->fd, closer);
123 }
124 clear();
125}
126
127void
129{
130 conn = nullptr;
131 closer = nullptr;
132}
133
134/* Ftp::CtrlChannel */
135
137 buf(nullptr),
138 size(0),
139 offset(0),
140 message(nullptr),
141 last_command(nullptr),
142 last_reply(nullptr),
143 replycode(0)
144{
145 // min() limits the initial read size when Config.maxReplyHeaderSize is huge
146 buf = static_cast<char*>(memAllocBuf(min(size_t(4096), Config.maxReplyHeaderSize), &size));
147}
148
150{
151 memFreeBuf(size, buf);
152 if (message)
153 wordlistDestroy(&message);
154 safe_free(last_command);
155 safe_free(last_reply);
156}
157
158/* Ftp::DataChannel */
159
161 readBuf(nullptr),
162 host(nullptr),
163 port(0),
164 read_pending(false)
165{
166}
167
169{
170 xfree(host);
171 delete readBuf;
172}
173
174void
176{
177 static char addrBuf[MAX_IPSTRLEN];
178 import.toStr(addrBuf, sizeof(addrBuf));
179 xfree(host);
180 host = xstrdup(addrBuf);
181 port = import.port();
182}
183
184/* Ftp::Client */
185
187 AsyncJob("Ftp::Client"),
188 ::Client(fwdState),
189 ctrl(),
190 data(),
191 state(BEGIN),
192 old_request(nullptr),
193 old_reply(nullptr),
194 shortenReadTimeout(false)
195{
198
199 ctrl.last_command = xstrdup("Connect to server");
200
202 const AsyncCall::Pointer closer = JobCallback(9, 5, Dialer, this,
204 ctrl.opened(fwdState->serverConnection(), closer);
205}
206
208{
209 data.close();
210
211 safe_free(old_request);
212 safe_free(old_reply);
213 fwd = nullptr; // refcounted
214}
215
216void
218{
219 scheduleReadControlReply(0);
220}
221
222void
224{
225 if (data.readBuf == nullptr) {
226 data.readBuf = new MemBuf;
227 data.readBuf->init(4096, SQUID_TCP_SO_RCVBUF);
228 }
229}
230
234void
236{
237 if (Comm::IsConnOpen(ctrl.conn)) {
238 debugs(9, 3, "closing FTP server FD " << ctrl.conn->fd << ", this " << this);
239 fwd->unregister(ctrl.conn);
240 ctrl.close();
241 }
242
243 if (Comm::IsConnOpen(data.conn)) {
244 debugs(9, 3, "closing FTP data FD " << data.conn->fd << ", this " << this);
245 data.close();
246 }
247
248 debugs(9, 3, "FTP ctrl and data connections closed. this " << this);
249}
250
257bool
259{
260 return !Comm::IsConnOpen(ctrl.conn) && !Comm::IsConnOpen(data.conn);
261}
262
263void
265{
266 debugs(9, 3, "entry-null=" << (entry?entry->isEmpty():0) << ", entry=" << entry);
267
268 const char *command, *reply;
269 ErrorState *ftperr;
270
271 if (err) {
272 debugs(9, 6, "error=" << err->type << ", code=" << xerrno <<
273 ", status=" << err->httpStatus);
274 error = err->type;
275 ftperr = err;
276 } else {
277 Http::StatusCode httpStatus = failedHttpStatus(error);
278 ftperr = new ErrorState(error, httpStatus, fwd->request, fwd->al);
279 }
280
281 ftperr->xerrno = xerrno;
282
283 ftperr->ftp.server_msg = ctrl.message;
284 ctrl.message = nullptr;
285
286 if (old_request)
287 command = old_request;
288 else
289 command = ctrl.last_command;
290
291 if (command && strncmp(command, "PASS", 4) == 0)
292 command = "PASS <yourpassword>";
293
294 if (old_reply)
295 reply = old_reply;
296 else
297 reply = ctrl.last_reply;
298
299 if (command)
300 ftperr->ftp.request = xstrdup(command);
301
302 if (reply)
303 ftperr->ftp.reply = xstrdup(reply);
304
305 if (!err) {
306 fwd->request->detailError(error, SysErrorDetail::NewIfAny(xerrno));
307 fwd->fail(ftperr);
308 closeServer(); // we failed, so no serverComplete()
309 }
310}
311
320
326void
328{
329 debugs(9, 3, ctrl.conn);
330
331 if (buffered_ok && ctrl.offset > 0) {
332 /* We've already read some reply data */
333 handleControlReply();
334 } else {
335
336 if (!Comm::IsConnOpen(ctrl.conn)) {
337 debugs(9, 3, "cannot read without ctrl " << ctrl.conn);
338 return;
339 }
340 /*
341 * Cancel the timeout on the Data socket (if any) and
342 * establish one on the control socket.
343 */
344 if (Comm::IsConnOpen(data.conn)) {
345 commUnsetConnTimeout(data.conn);
346 }
347
348 const auto maxSize = min(Config.maxReplyHeaderSize, std::numeric_limits<decltype(ctrl.size)>::max());
349 if (ctrl.offset >= maxSize) {
350 debugs(9, 2, "FTP control reply size will exceed " << maxSize << "; reply_header_max_size=" << Config.maxReplyHeaderSize);
351 failed(ERR_FTP_FAILURE, 0);
352 return;
353 }
354
355 if (ctrl.offset == ctrl.size) {
356 const auto newSize = (ctrl.size <= maxSize/2) ? (ctrl.size*2) : maxSize;
357 Assure(newSize > ctrl.size);
358 ctrl.buf = static_cast<char*>(memReallocBuf(ctrl.buf, newSize, &ctrl.size));
359 Assure(ctrl.offset < ctrl.size);
360 }
361
362 const time_t tout = shortenReadTimeout ?
365 shortenReadTimeout = false; // we only need to do this once, after PASV
366
367 typedef CommCbMemFunT<Client, CommTimeoutCbParams> TimeoutDialer;
368 AsyncCall::Pointer timeoutCall = JobCallback(9, 5, TimeoutDialer, this, Ftp::Client::timeout);
369 commSetConnTimeout(ctrl.conn, tout, timeoutCall);
370
373 // Do not accumulate more than Config.maxReplyHeaderSize bytes,
374 // even if we happened to have enough buffer space to do so.
375 const auto maxOffset = min(ctrl.size, Config.maxReplyHeaderSize);
376 Assure(maxOffset > ctrl.offset); // we can make progress (and no underflows)
377 Assure(maxOffset <= ctrl.size); // paranoid: we will not read beyond our buffer space
378 const auto maxReadSize = maxOffset - ctrl.offset;
379 comm_read(ctrl.conn, ctrl.buf + ctrl.offset, maxReadSize, reader);
380 }
381}
382
383void
385{
386 debugs(9, 3, "FD " << io.fd << ", Read " << io.size << " bytes");
387
388 if (io.size > 0) {
391 }
392
393 if (io.flag == Comm::ERR_CLOSING)
394 return;
395
396 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
397 if (abortOnData("entry aborted during control reply read"))
398 return;
399 }
400
401 assert(ctrl.offset < ctrl.size);
402
403 if (io.flag == Comm::OK && io.size > 0) {
405 }
406
407 if (io.flag != Comm::OK) {
409 "ERROR: FTP control reply read failure: " << xstrerr(io.xerrno));
410
411 if (ignoreErrno(io.xerrno)) {
412 scheduleReadControlReply(0);
413 } else {
414 failed(ERR_READ_ERROR, io.xerrno);
415 /* failed closes ctrl.conn and frees ftpState */
416 }
417 return;
418 }
419
420 if (io.size == 0) {
421 if (entry->store_status == STORE_PENDING) {
422 failed(ERR_FTP_FAILURE, 0);
423 /* failed closes ctrl.conn and frees ftpState */
424 return;
425 }
426
427 /* XXX this may end up having to be serverComplete() .. */
428 abortAll("zero control reply read");
429 return;
430 }
431
432 unsigned int len =io.size + ctrl.offset;
433 ctrl.offset = len;
434 assert(len <= ctrl.size);
435 if (Comm::IsConnOpen(ctrl.conn))
436 commUnsetConnTimeout(ctrl.conn); // we are done waiting for ctrl reply
437 handleControlReply();
438}
439
440void
442{
443 debugs(9, 3, status());
444
445 size_t bytes_used = 0;
446 wordlistDestroy(&ctrl.message);
447 try {
448 if (!parseControlReply(bytes_used)) {
449 /* didn't get complete reply yet */
450 scheduleReadControlReply(0);
451 return;
452 }
453 } catch (...) {
454 debugs(9, 2, "ERROR: Cannot parse control reply: " << CurrentException);
455 failed(ERR_FTP_FAILURE, 0);
456 return;
457 }
458
459 assert(ctrl.message); // the entire FTP server response, line by line
460 assert(ctrl.replycode >= 0); // FTP status code (from the last line)
461 assert(ctrl.last_reply); // FTP reason (from the last line)
462
463 if (ctrl.offset == bytes_used) {
464 /* used it all up */
465 ctrl.offset = 0;
466 } else {
467 /* Got some data past the complete reply */
468 assert(bytes_used < ctrl.offset);
469 ctrl.offset -= bytes_used;
470 memmove(ctrl.buf, ctrl.buf + bytes_used, ctrl.offset);
471 }
472
473 debugs(9, 3, "state=" << state << ", code=" << ctrl.replycode);
474}
475
476bool
478{
479 int code = ctrl.replycode;
480 char *buf;
481 debugs(9, 3, status());
482
483 if (!Comm::IsConnOpen(ctrl.conn)) {
484 debugs(9, 5, "The control connection to the remote end is closed");
485 return false;
486 }
487
488 if (code != 227) {
489 debugs(9, 2, "PASV not supported by remote end");
490 return false;
491 }
492
493 /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
494 /* ANSI sez [^0-9] is undefined, it breaks on Watcom cc */
495 debugs(9, 5, "scanning: " << ctrl.last_reply);
496
497 buf = ctrl.last_reply + strcspn(ctrl.last_reply, "0123456789");
498
499 const char *forceIp = Config.Ftp.sanitycheck ?
500 fd_table[ctrl.conn->fd].ipaddr : nullptr;
501 if (!Ftp::ParseIpPort(buf, forceIp, srvAddr)) {
502 debugs(9, DBG_IMPORTANT, "Unsafe PASV reply from " <<
503 ctrl.conn->remote << ": " << ctrl.last_reply);
504 return false;
505 }
506
507 data.addr(srvAddr);
508
509 return true;
510}
511
512bool
514{
515 int code = ctrl.replycode;
516 char *buf;
517 debugs(9, 3, status());
518
519 if (!Comm::IsConnOpen(ctrl.conn)) {
520 debugs(9, 5, "The control connection to the remote end is closed");
521 return false;
522 }
523
524 if (code != 229 && code != 522) {
525 if (code == 200) {
526 /* handle broken servers (RFC 2428 says OK code for EPSV MUST be 229 not 200) */
527 /* vsftpd for one send '200 EPSV ALL ok.' without even port info.
528 * Its okay to re-send EPSV 1/2 but nothing else. */
529 debugs(9, DBG_IMPORTANT, "ERROR: Broken FTP Server at " << ctrl.conn->remote << ". Wrong accept code for EPSV");
530 } else {
531 debugs(9, 2, "EPSV not supported by remote end");
532 }
533 return sendPassive();
534 }
535
536 if (code == 522) {
537 /* Peer responded with a list of supported methods:
538 * 522 Network protocol not supported, use (1)
539 * 522 Network protocol not supported, use (1,2)
540 * 522 Network protocol not supported, use (2)
541 * TODO: Handle the (1,2) case which may happen after EPSV ALL. Close
542 * data + control without self-destructing and re-open from scratch.
543 */
544 debugs(9, 5, "scanning: " << ctrl.last_reply);
545 buf = ctrl.last_reply;
546 while (buf != nullptr && *buf != '\0' && *buf != '\n' && *buf != '(')
547 ++buf;
548 if (buf != nullptr && *buf == '\n')
549 ++buf;
550
551 if (buf == nullptr || *buf == '\0') {
552 /* handle broken server (RFC 2428 says MUST specify supported protocols in 522) */
553 debugs(9, DBG_IMPORTANT, "ERROR: Broken FTP Server at " << ctrl.conn->remote << ". 522 error missing protocol negotiation hints");
554 return sendPassive();
555 } else if (strcmp(buf, "(1)") == 0) {
556 state = SENT_EPSV_2; /* simulate having sent and failed EPSV 2 */
557 return sendPassive();
558 } else if (strcmp(buf, "(2)") == 0) {
559 if (Ip::EnableIpv6) {
560 /* If server only supports EPSV 2 and we have already tried that. Go straight to EPRT */
561 if (state == SENT_EPSV_2) {
562 return sendEprt();
563 } else {
564 /* or try the next Passive mode down the chain. */
565 return sendPassive();
566 }
567 } else {
568 /* Server only accept EPSV in IPv6 traffic. */
569 state = SENT_EPSV_1; /* simulate having sent and failed EPSV 1 */
570 return sendPassive();
571 }
572 } else {
573 /* handle broken server (RFC 2428 says MUST specify supported protocols in 522) */
574 debugs(9, DBG_IMPORTANT, "WARNING: Server at " << ctrl.conn->remote << " sent unknown protocol negotiation hint: " << buf);
575 return sendPassive();
576 }
577 /* coverity[unreachable] */
578 /* safeguard against possible future bugs in above conditions */
579 failed(ERR_FTP_FAILURE, 0);
580 return false;
581 }
582
583 /* 229 Entering Extended Passive Mode (|||port|) */
584 /* ANSI sez [^0-9] is undefined, it breaks on Watcom cc */
585 debugs(9, 5, "scanning: " << ctrl.last_reply);
586
587 buf = ctrl.last_reply + strcspn(ctrl.last_reply, "(");
588
589 char h1, h2, h3, h4;
590 unsigned short port;
591 int n = sscanf(buf, "(%c%c%c%hu%c)", &h1, &h2, &h3, &port, &h4);
592
593 if (n < 4 || h1 != h2 || h1 != h3 || h1 != h4) {
594 debugs(9, DBG_IMPORTANT, "ERROR: Invalid EPSV reply from " <<
595 ctrl.conn->remote << ": " <<
596 ctrl.last_reply);
597
598 return sendPassive();
599 }
600
601 if (0 == port) {
602 debugs(9, DBG_IMPORTANT, "Unsafe EPSV reply from " <<
603 ctrl.conn->remote << ": " <<
604 ctrl.last_reply);
605
606 return sendPassive();
607 }
608
609 if (Config.Ftp.sanitycheck) {
610 if (port < 1024) {
611 debugs(9, DBG_IMPORTANT, "Unsafe EPSV reply from " <<
612 ctrl.conn->remote << ": " <<
613 ctrl.last_reply);
614
615 return sendPassive();
616 }
617 }
618
619 remoteAddr = ctrl.conn->remote;
620 remoteAddr.port(port);
621 data.addr(remoteAddr);
622 return true;
623}
624
625// FTP clients do not support EPRT and PORT commands yet.
626// The Ftp::Client::sendEprt() will fail because of the unimplemented
627// openListenSocket() or sendPort() methods
628bool
630{
631 if (!Config.Ftp.eprt) {
632 /* Disabled. Switch immediately to attempting old PORT command. */
633 debugs(9, 3, "EPRT disabled by local administrator");
634 return sendPort();
635 }
636
637 debugs(9, 3, status());
638
639 if (!openListenSocket()) {
640 failed(ERR_FTP_FAILURE, 0);
641 return false;
642 }
643
644 debugs(9, 3, "Listening for FTP data connection with FD " << data.conn);
645 if (!Comm::IsConnOpen(data.conn)) {
646 // TODO: Set error message.
647 failed(ERR_FTP_FAILURE, 0);
648 return false;
649 }
650
651 static MemBuf mb;
652 mb.reset();
653 char buf[MAX_IPSTRLEN];
654 /* RFC 2428 defines EPRT as IPv6 equivalent to IPv4 PORT command. */
655 /* Which can be used by EITHER protocol. */
656 debugs(9, 3, "Listening for FTP data connection on port" << comm_local_port(data.conn->fd) << " or port?" << data.conn->local.port());
657 mb.appendf("EPRT |%d|%s|%d|%s",
658 ( data.conn->local.isIPv6() ? 2 : 1 ),
659 data.conn->local.toStr(buf,MAX_IPSTRLEN),
660 comm_local_port(data.conn->fd), Ftp::crlf );
661
662 state = SENT_EPRT;
663 writeCommand(mb.content());
664 return true;
665}
666
667bool
669{
670 failed(ERR_FTP_FAILURE, 0);
671 return false;
672}
673
674bool
676{
677 debugs(9, 3, status());
678
684 if (Config.Ftp.epsv_all && state == SENT_EPSV_1 ) {
685 // We are here because the last "EPSV 1" failed, but because of epsv_all
686 // no other method allowed.
687 debugs(9, DBG_IMPORTANT, "FTP does not allow PASV method after 'EPSV ALL' has been sent.");
688 failed(ERR_FTP_FAILURE, 0);
689 return false;
690 }
691
693 data.close();
694
698 if (!Config.Ftp.passive || state == SENT_PASV) {
699 sendEprt();
700 return true;
701 }
702
703 static MemBuf mb;
704 mb.reset();
713 switch (state) {
714 case SENT_EPSV_ALL: /* EPSV ALL resulted in a bad response. Try ther EPSV methods. */
715 if (ctrl.conn->local.isIPv6()) {
716 debugs(9, 5, "FTP Channel is IPv6 (" << ctrl.conn->remote << ") attempting EPSV 2 after EPSV ALL has failed.");
717 mb.appendf("EPSV 2%s", Ftp::crlf);
718 state = SENT_EPSV_2;
719 break;
720 }
721 [[fallthrough]]; // to skip EPSV 2
722
723 case SENT_EPSV_2: /* EPSV IPv6 failed. Try EPSV IPv4 */
724 if (ctrl.conn->local.isIPv4()) {
725 debugs(9, 5, "FTP Channel is IPv4 (" << ctrl.conn->remote << ") attempting EPSV 1 after EPSV ALL has failed.");
726 mb.appendf("EPSV 1%s", Ftp::crlf);
727 state = SENT_EPSV_1;
728 break;
729 } else if (Config.Ftp.epsv_all) {
730 debugs(9, DBG_IMPORTANT, "FTP does not allow PASV method after 'EPSV ALL' has been sent.");
731 failed(ERR_FTP_FAILURE, 0);
732 return false;
733 }
734 [[fallthrough]]; // to skip EPSV 1
735
736 case SENT_EPSV_1: /* EPSV options exhausted. Try PASV now. */
737 debugs(9, 5, "FTP Channel (" << ctrl.conn->remote << ") rejects EPSV connection attempts. Trying PASV instead.");
738 mb.appendf("PASV%s", Ftp::crlf);
739 state = SENT_PASV;
740 break;
741
742 default: {
743 bool doEpsv = true;
745 ACLFilledChecklist checklist(Config.accessList.ftp_epsv, fwd->request);
746 doEpsv = checklist.fastCheck().allowed();
747 }
748 if (!doEpsv) {
749 debugs(9, 5, "EPSV support manually disabled. Sending PASV for FTP Channel (" << ctrl.conn->remote <<")");
750 mb.appendf("PASV%s", Ftp::crlf);
751 state = SENT_PASV;
752 } else if (Config.Ftp.epsv_all) {
753 debugs(9, 5, "EPSV ALL manually enabled. Attempting with FTP Channel (" << ctrl.conn->remote <<")");
754 mb.appendf("EPSV ALL%s", Ftp::crlf);
755 state = SENT_EPSV_ALL;
756 } else {
757 if (ctrl.conn->local.isIPv6()) {
758 debugs(9, 5, "FTP Channel (" << ctrl.conn->remote << "). Sending default EPSV 2");
759 mb.appendf("EPSV 2%s", Ftp::crlf);
760 state = SENT_EPSV_2;
761 }
762 if (ctrl.conn->local.isIPv4()) {
763 debugs(9, 5, "Channel (" << ctrl.conn->remote <<"). Sending default EPSV 1");
764 mb.appendf("EPSV 1%s", Ftp::crlf);
765 state = SENT_EPSV_1;
766 }
767 }
768 break;
769 }
770 }
771
772 if (ctrl.message)
773 wordlistDestroy(&ctrl.message);
774 ctrl.message = nullptr; //No message to return to client.
775 ctrl.offset = 0; //reset readed response, to make room read the next response
776
777 writeCommand(mb.content());
778
779 shortenReadTimeout = true;
780 return true;
781}
782
783void
785{
786 if (!Comm::IsConnOpen(ctrl.conn)) {
787 debugs(9, 5, "The control connection to the remote end is closed");
788 return;
789 }
790
791 safe_free(ctrl.last_command);
792
793 safe_free(ctrl.last_reply);
794
795 ctrl.last_command = xstrdup("Connect to server data port");
796
797 // Generate a new data channel descriptor to be opened.
799 conn->setAddrs(ctrl.conn->local, data.host);
800 conn->local.port(0);
801 conn->remote.port(data.port);
802 conn->tos = ctrl.conn->tos;
803 conn->nfmark = ctrl.conn->nfmark;
804 // Using non-local addresses in TPROXY mode requires appropriate socket option.
805 conn->flags |= ctrl.conn->flags & COMM_TRANSPARENT;
806
807 debugs(9, 3, "connecting to " << conn->remote);
808
811 const auto cs = new Comm::ConnOpener(conn, callback, Config.Timeout.connect);
812 cs->setHost(data.host);
813 dataConnWait.start(cs, callback);
814}
815
816bool
818{
819 return false;
820}
821
825{
827 return JobCallback(9, 5, Dialer, this, Ftp::Client::dataClosed);
828}
829
831void
833{
834 debugs(9, 4, status());
835 if (data.conn)
836 data.conn->noteClosure();
837 if (data.listenConn != nullptr) {
838 data.listenConn->close();
839 data.listenConn = nullptr;
840 }
841 data.clear();
842}
843
844void
846{
847 char *ebuf;
848 /* trace FTP protocol communications at level 2 */
849 debugs(9, 2, "ftp<< " << buf);
850
851 if (Config.Ftp.telnet)
852 ebuf = escapeIAC(buf);
853 else
854 ebuf = xstrdup(buf);
855
856 safe_free(ctrl.last_command);
857
858 safe_free(ctrl.last_reply);
859
860 ctrl.last_command = ebuf;
861
862 if (!Comm::IsConnOpen(ctrl.conn)) {
863 debugs(9, 2, "cannot send to closing ctrl " << ctrl.conn);
864 // TODO: assert(ctrl.closer != NULL);
865 return;
866 }
867
869 AsyncCall::Pointer call = JobCallback(9, 5, Dialer, this,
871 Comm::Write(ctrl.conn, ctrl.last_command, strlen(ctrl.last_command), call, nullptr);
872
873 scheduleReadControlReply(0);
874}
875
876void
878{
879
880 debugs(9, 5, "wrote " << io.size << " bytes");
881
882 if (io.size > 0) {
886 }
887
888 if (io.flag == Comm::ERR_CLOSING)
889 return;
890
891 if (io.flag) {
892 debugs(9, DBG_IMPORTANT, "ERROR: FTP command write failure: " << io.conn << ": " << xstrerr(io.xerrno));
893 failed(ERR_WRITE_ERROR, io.xerrno);
894 /* failed closes ctrl.conn and frees ftpState */
895 return;
896 }
897}
898
900void
902{
903 debugs(9, 4, status());
904 if (ctrl.conn)
905 ctrl.conn->noteClosure();
906 ctrl.clear();
907 doneWithFwd = "ctrlClosed()"; // assume FwdState is monitoring too
908 mustStop("Ftp::Client::ctrlClosed");
909}
910
911void
913{
914 debugs(9, 4, io.conn << ": '" << entry->url() << "'" );
915
916 if (abortOnBadEntry("entry went bad while waiting for a timeout"))
917 return;
918
919 failed(ERR_READ_TIMEOUT, 0);
920 /* failed() closes ctrl.conn and frees ftpState */
921}
922
925{
926 return data.conn;
927}
928
929void
931{
932 // TODO: Merge with HttpStateData::noteDelayAwareReadChance()
933 waitingForDelayAwareReadChance = false;
934 data.read_pending = false;
935 maybeReadVirginBody();
936}
937
938void
940{
941 // too late to read
942 if (!Comm::IsConnOpen(data.conn) || fd_table[data.conn->fd].closing())
943 return;
944
945 if (data.read_pending)
946 return;
947
948 initReadBuf();
949
950 // XXX: We only use this call to decide whether to read; we never increase data.readBuf space.
951 // TODO: Upgrade data.readBuf to SBuf and merge this with similar HttpStateData::readReply() code.
952 const auto read_sz = calcBufferSpaceToReserve(data.readBuf->spaceSize(), data.readBuf->spaceSize());
953
954 debugs(9, 9, "FTP may read up to " << read_sz << " bytes");
955
956 if (!read_sz)
957 return;
958
959 data.read_pending = true;
960
961 typedef CommCbMemFunT<Client, CommTimeoutCbParams> TimeoutDialer;
962 AsyncCall::Pointer timeoutCall = JobCallback(9, 5,
963 TimeoutDialer, this, Ftp::Client::timeout);
964 commSetConnTimeout(data.conn, Config.Timeout.read, timeoutCall);
965
966 debugs(9,5,"queueing read on FD " << data.conn->fd);
967
968 const auto amountToRead = entry->bytesWanted(Range<size_t>(0, read_sz));
969
970 if (amountToRead <= 0) {
971 delayRead();
972 return;
973 }
974
975 using ReadDialer = CommCbMemFunT<Client, CommIoCbParams>;
976 AsyncCall::Pointer readCallback = JobCallback(9, 5, ReadDialer, this, Client::dataRead);
977 comm_read(data.conn, data.readBuf->space(), amountToRead, readCallback);
978}
979
980void
982{
983 int j;
984 int bin;
985
986 data.read_pending = false;
987
988 debugs(9, 3, "FD " << io.fd << " Read " << io.size << " bytes");
989
990 if (io.size > 0) {
993 }
994
995 if (io.flag == Comm::ERR_CLOSING)
996 return;
997
998 assert(io.fd == data.conn->fd);
999
1000 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1001 abortOnData("entry aborted during dataRead");
1002 return;
1003 }
1004
1005 if (io.flag == Comm::OK && io.size > 0) {
1006 debugs(9, 5, "appended " << io.size << " bytes to readBuf");
1007 data.readBuf->appended(io.size);
1008#if USE_DELAY_POOLS
1009 DelayId delayId = entry->mem_obj->mostBytesAllowed();
1010 delayId.bytesIn(io.size);
1011#endif
1012 ++ IOStats.Ftp.reads;
1013
1014 for (j = io.size - 1, bin = 0; j; ++bin)
1015 j >>= 1;
1016
1017 ++ IOStats.Ftp.read_hist[bin];
1018 }
1019
1020 if (io.flag != Comm::OK) {
1021 debugs(50, ignoreErrno(io.xerrno) ? 3 : DBG_IMPORTANT,
1022 "ERROR: FTP data read failure: " << xstrerr(io.xerrno));
1023
1024 if (ignoreErrno(io.xerrno)) {
1025 maybeReadVirginBody();
1026 } else {
1027 failed(ERR_READ_ERROR, 0);
1028 /* failed closes ctrl.conn and frees ftpState */
1029 return;
1030 }
1031 } else if (io.size == 0) {
1032 debugs(9, 3, "Calling dataComplete() because io.size == 0");
1033 /*
1034 * DPW 2007-04-23
1035 * Dangerous curves ahead. This call to dataComplete was
1036 * calling scheduleReadControlReply, handleControlReply,
1037 * and then ftpReadTransferDone. If ftpReadTransferDone
1038 * gets unexpected status code, it closes down the control
1039 * socket and our FtpStateData object gets destroyed. As
1040 * a workaround we no longer set the 'buffered_ok' flag in
1041 * the scheduleReadControlReply call.
1042 */
1043 dataComplete();
1044 }
1045
1046 processReplyBody();
1047}
1048
1049void
1051{
1052 debugs(9, 3,status());
1053
1054 /* Connection closed; transfer done. */
1055
1057 data.close();
1058
1059 /* expect the "transfer complete" message on the control socket */
1060 /*
1061 * DPW 2007-04-23
1062 * Previously, this was the only place where we set the
1063 * 'buffered_ok' flag when calling scheduleReadControlReply().
1064 * It caused some problems if the FTP server returns an unexpected
1065 * status code after the data command. FtpStateData was being
1066 * deleted in the middle of dataRead().
1067 */
1068 /* AYJ: 2011-01-13: Bug 2581.
1069 * 226 status is possibly waiting in the ctrl buffer.
1070 * The connection will hang if we DONT send buffered_ok.
1071 * This happens on all transfers which can be completely sent by the
1072 * server before the 150 started status message is read in by Squid.
1073 * ie all transfers of about one packet hang.
1074 */
1075 scheduleReadControlReply(1);
1076}
1077
1078void
1079Ftp::Client::abortAll(const char *reason)
1080{
1081 debugs(9, 3, "aborting transaction for " << reason <<
1082 "; FD " << (ctrl.conn!=nullptr?ctrl.conn->fd:-1) << ", Data FD " << (data.conn!=nullptr?data.conn->fd:-1) << ", this " << this);
1083 mustStop(reason);
1084}
1085
1090void
1092{
1093 commUnsetConnTimeout(ctrl.conn);
1094
1095 typedef CommCbMemFunT<Client, CommTimeoutCbParams> TimeoutDialer;
1096 AsyncCall::Pointer timeoutCall = JobCallback(9, 5, TimeoutDialer, this,
1098 commSetConnTimeout(data.conn, Config.Timeout.read, timeoutCall);
1099}
1100
1101void
1108
1112void
1114{
1116 debugs(9, 3, status());
1117 dataComplete();
1118 /* NP: RFC 959 3.3. DATA CONNECTION MANAGEMENT
1119 * if transfer type is 'stream' call dataComplete()
1120 * otherwise leave open. (reschedule control channel read?)
1121 */
1122}
1123
1126bool
1128{
1129 char *s;
1130 char *end;
1131 int usable;
1132 int complete = 0;
1133 wordlist *head = nullptr;
1134 auto headDeleter = [](wordlist *h) { wordlistDestroy(&h); };
1135 auto headGuard = std::unique_ptr<wordlist, decltype(headDeleter)>(head, headDeleter);
1136 wordlist *list;
1137 wordlist **tail = &head;
1138 size_t linelen;
1139 debugs(9, 3, status());
1140 /*
1141 * We need a NULL-terminated buffer for scanning, ick
1142 */
1143 const size_t len = ctrl.offset;
1144 const auto sbufOwner = std::unique_ptr<void, decltype(&xfree)>(xmalloc(len + 1), xfree);
1145 const auto sbuf = static_cast<char*>(sbufOwner.get());
1146 xstrncpy(sbuf, ctrl.buf, len + 1);
1147 end = sbuf + len - 1;
1148
1149 while (*end != '\r' && *end != '\n' && end > sbuf)
1150 --end;
1151
1152 usable = end - sbuf;
1153
1154 debugs(9, 3, "usable = " << usable);
1155
1156 if (usable == 0) {
1157 debugs(9, 3, "didn't find end of line");
1158 return false;
1159 }
1160
1161 debugs(9, 3, len << " bytes to play with");
1162 ++end;
1163 s = sbuf;
1164 s += strspn(s, crlf);
1165
1166 // cumulative length of parsed control reply lines added to the list
1167 size_t replyLength = 0;
1168
1169 for (; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
1170 if (complete)
1171 break;
1172
1173 debugs(9, 5, "s = {" << s << "}");
1174
1175 linelen = strcspn(s, crlf) + 1;
1176 replyLength += linelen;
1177
1178 if (linelen < 2)
1179 break;
1180
1181 if (replyLength > String::RawSizeMaxXXX())
1182 throw TextException(ToSBuf("control reply too long: ", replyLength, " exceeds safe limit of ", String::RawSizeMaxXXX(), " bytes"), Here());
1183
1184 if (linelen > 3)
1185 complete = (*s >= '0' && *s <= '9' && *(s + 3) == ' ');
1186
1187 list = new wordlist();
1188 if (!headGuard)
1189 headGuard.reset(list);
1190
1191 list->key = (char *)xmalloc(linelen);
1192
1193 xstrncpy(list->key, s, linelen);
1194
1195 /* trace the FTP communication chat at level 2 */
1196 debugs(9, 2, "ftp>> " << list->key);
1197
1198 if (complete) {
1199 // use list->key for last_reply because s contains the new line
1200 safe_free(ctrl.last_reply);
1201 ctrl.last_reply = xstrdup(list->key + 4);
1202 ctrl.replycode = atoi(list->key);
1203 }
1204
1205 *tail = list;
1206
1207 tail = &list->next;
1208 }
1209
1210 bytesUsed = static_cast<size_t>(s - sbuf);
1211
1212 if (!complete) {
1213 return false;
1214 }
1215
1216 ctrl.message = headGuard.release();
1217 assert(ctrl.replycode >= 0);
1218 assert(ctrl.last_reply);
1219 assert(ctrl.message);
1220 return true;
1221}
1222
1223}; // namespace Ftp
1224
#define Assure(condition)
Definition Assure.h:35
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
#define COMM_TRANSPARENT
Definition Connection.h:50
#define Here()
source code location of the caller
Definition Here.h:15
int size
Definition ModDevPoll.cc:70
void comm_read(const Comm::ConnectionPointer &conn, char *buf, int len, AsyncCall::Pointer &callback)
Definition Read.h:59
class SquidConfig Config
StatCounters statCounter
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
void error(char *format,...)
squidaio_request_t * head
Definition aiops.cc:129
#define assert(EX)
Definition assert.h:17
#define SQUID_TCP_SO_RCVBUF
Definition autoconf.h:1445
Acl::Answer const & fastCheck()
Definition Checklist.cc:298
bool allowed() const
Definition Acl.h:82
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition CommCalls.h:83
int fd
FD which the call was about. Set by the async call creator.
Definition CommCalls.h:85
Comm::Flag flag
comm layer result status.
Definition CommCalls.h:82
Comm::ConnectionPointer conn
Definition CommCalls.h:80
Ip::Address remote
Definition Connection.h:152
void setAddrs(const Ip::Address &aLocal, const Ip::Address &aRemote)
Definition Connection.h:106
void leaveOrphanage()
resume relying on owner(s) to initiate an explicit connection closure
Definition Connection.h:92
Ip::Address local
Definition Connection.h:149
void bytesIn(int qty)
Definition DelayId.cc:135
err_type type
Definition errorpage.h:170
char * reply
Definition errorpage.h:191
wordlist * server_msg
Definition errorpage.h:189
HttpRequestPointer request
Definition errorpage.h:177
struct ErrorState::@47 ftp
Http::StatusCode httpStatus
Definition errorpage.h:173
void forget()
Definition FtpClient.cc:118
void close()
planned close: removes the close handler and calls comm_close
Definition FtpClient.cc:107
void opened(const Comm::ConnectionPointer &conn, const AsyncCall::Pointer &aCloser)
called after the socket is opened, sets up close handler
Definition FtpClient.cc:90
void clear()
remove the close handler, leave connection open
Definition FtpClient.cc:128
FTP client functionality shared among FTP Gateway and Relay clients.
Definition FtpClient.h:111
virtual Http::StatusCode failedHttpStatus(err_type &error)
Definition FtpClient.cc:313
void start() override
called by AsyncStart; do not call directly
Definition FtpClient.cc:217
bool handleEpsvReply(Ip::Address &remoteAddr)
Definition FtpClient.cc:513
virtual void handleControlReply()
Definition FtpClient.cc:441
void dataRead(const CommIoCbParams &io)
Definition FtpClient.cc:981
void initReadBuf()
Definition FtpClient.cc:223
bool sendPort()
Definition FtpClient.cc:668
void dataComplete()
void closeServer() override
Definition FtpClient.cc:235
void scheduleReadControlReply(int buffered_ok)
Definition FtpClient.cc:327
bool openListenSocket()
Definition FtpClient.cc:817
void sentRequestBody(const CommIoCbParams &io) override
void writeCommand(const char *buf)
Definition FtpClient.cc:845
virtual void timeout(const CommTimeoutCbParams &io)
read timeout handler
Definition FtpClient.cc:912
bool handlePasvReply(Ip::Address &remoteAddr)
Definition FtpClient.cc:477
void switchTimeoutToDataChannel()
void connectDataChannel()
Definition FtpClient.cc:784
bool doneWithServer() const override
Definition FtpClient.cc:258
void writeCommandCallback(const CommIoCbParams &io)
Definition FtpClient.cc:877
Client(FwdState *fwdState)
Definition FtpClient.cc:186
bool sendEprt()
Definition FtpClient.cc:629
void readControlReply(const CommIoCbParams &io)
Definition FtpClient.cc:384
void doneSendingRequestBody() override
void maybeReadVirginBody() override
read response data from the network
Definition FtpClient.cc:939
const Comm::ConnectionPointer & dataConnection() const override
Definition FtpClient.cc:924
virtual void failed(err_type error=ERR_NONE, int xerrno=0, ErrorState *ftperr=nullptr)
handle a fatal transaction error, closing the control connection
Definition FtpClient.cc:264
CtrlChannel ctrl
FTP control channel state.
Definition FtpClient.h:142
bool sendPassive()
Definition FtpClient.cc:675
bool parseControlReply(size_t &bytesUsed)
AsyncCall::Pointer dataCloser()
creates a data channel Comm close callback
Definition FtpClient.cc:824
void ctrlClosed(const CommCloseCbParams &io)
handler called by Comm when FTP control channel is closed unexpectedly
Definition FtpClient.cc:901
virtual void dataChannelConnected(const CommConnectCbParams &io)=0
void abortAll(const char *reason) override
abnormal transaction termination; reason is for debugging only
~Client() override
Definition FtpClient.cc:207
virtual void dataClosed(const CommCloseCbParams &io)
handler called by Comm when FTP data channel is closed unexpectedly
Definition FtpClient.cc:832
void noteDelayAwareReadChance() override
Definition FtpClient.cc:930
char * last_command
Definition FtpClient.h:83
void addr(const Ip::Address &addr)
import host and port
Definition FtpClient.cc:175
SBuf verbose(const HttpRequestPointer &) const override
Definition FtpClient.cc:81
SBuf brief() const override
Definition FtpClient.cc:75
Comm::ConnectionPointer const & serverConnection() const
Definition FwdState.h:138
int read_hist[histSize]
Definition IoStats.h:21
int reads
Definition IoStats.h:19
struct IoStats::@59 Ftp
unsigned short port() const
Definition Address.cc:790
void init(mb_size_t szInit, mb_size_t szMax)
Definition MemBuf.cc:93
char * content()
start of the added data
Definition MemBuf.h:41
void reset()
Definition MemBuf.cc:129
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition Packable.h:61
Definition Range.h:19
Definition SBuf.h:94
size_t maxReplyHeaderSize
struct SquidConfig::@77 Timeout
struct SquidConfig::@92 Ftp
time_t connect
struct SquidConfig::@91 accessList
acl_access * ftp_epsv
ByteCounter kbytes_out
struct StatCounters::@105::@115 ftp
struct StatCounters::@105 server
ByteCounter kbytes_in
struct StatCounters::@105::@115 all
static size_type RawSizeMaxXXX()
Definition SquidString.h:76
static ErrorDetail::Pointer NewIfAny(const int errorNo)
an std::runtime_error with thrower location info
char * key
Definition wordlist.h:59
wordlist * next
Definition wordlist.h:60
void commUnsetConnTimeout(const Comm::ConnectionPointer &conn)
Definition comm.cc:618
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition comm.cc:942
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
Definition comm.cc:971
unsigned short comm_local_port(int fd)
Definition comm.cc:167
void commSetConnTimeout(const Comm::ConnectionPointer &conn, time_t timeout, AsyncCall::Pointer &callback)
Definition comm.cc:594
int ignoreErrno(int ierrno)
Definition comm.cc:1407
A const & max(A const &lhs, A const &rhs)
A const & min(A const &lhs, A const &rhs)
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define EBIT_TEST(flag, bit)
Definition defines.h:67
static int port
@ ENTRY_ABORTED
Definition enums.h:110
@ STORE_PENDING
Definition enums.h:46
err_type
Definition forward.h:14
@ ERR_READ_TIMEOUT
Definition forward.h:26
@ ERR_FTP_FAILURE
Definition forward.h:53
@ ERR_NONE
Definition forward.h:15
@ ERR_WRITE_ERROR
Definition forward.h:29
@ ERR_READ_ERROR
Definition forward.h:28
void fd_bytes(const int fd, const int len, const IoDirection direction)
Definition fd.cc:221
#define fd_table
Definition fde.h:189
IoStats IOStats
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition forward.h:25
void memFreeBuf(size_t size, void *)
Definition minimal.cc:67
void * memAllocBuf(size_t net_size, size_t *gross_size)
Definition minimal.cc:46
void * memReallocBuf(void *buf, size_t net_size, size_t *gross_size)
Definition minimal.cc:54
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition Connection.cc:27
void Write(const Comm::ConnectionPointer &conn, const char *buf, int size, AsyncCall::Pointer &callback, FREE *free_func)
Definition Write.cc:33
@ OK
Definition Flag.h:16
@ ERR_CLOSING
Definition Flag.h:24
Definition forward.h:24
const char *const crlf
Definition FtpClient.cc:40
static char * escapeIAC(const char *buf)
Definition FtpClient.cc:43
bool ParseIpPort(const char *buf, const char *forceIp, Ip::Address &addr)
parses and validates "A1,A2,A3,A4,P1,P2" IP,port sequence
Definition Parsing.cc:18
StatusCode
Definition StatusCode.h:20
@ scGatewayTimeout
Definition StatusCode.h:77
@ scBadGateway
Definition StatusCode.h:75
#define xfree
#define xstrdup
#define xmalloc
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition Stream.h:63
void wordlistDestroy(wordlist **list)
destroy a wordlist
Definition wordlist.cc:16
#define safe_free(x)
Definition xalloc.h:73
const char * xstrerr(int error)
Definition xstrerror.cc:83
char * xstrncpy(char *dst, const char *src, size_t n)
Definition xstring.cc:37