FtpClient.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2022 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 buf = static_cast<char*>(memAllocBuf(4096, &size));
146}
147
149{
150 memFreeBuf(size, buf);
151 if (message)
152 wordlistDestroy(&message);
153 safe_free(last_command);
154 safe_free(last_reply);
155}
156
157/* Ftp::DataChannel */
158
160 readBuf(nullptr),
161 host(nullptr),
162 port(0),
163 read_pending(false)
164{
165}
166
168{
169 delete readBuf;
170}
171
172void
174{
175 static char addrBuf[MAX_IPSTRLEN];
176 import.toStr(addrBuf, sizeof(addrBuf));
177 xfree(host);
178 host = xstrdup(addrBuf);
179 port = import.port();
180}
181
182/* Ftp::Client */
183
185 AsyncJob("Ftp::Client"),
186 ::Client(fwdState),
187 ctrl(),
188 data(),
189 state(BEGIN),
190 old_request(nullptr),
191 old_reply(nullptr),
192 shortenReadTimeout(false)
193{
196
197 ctrl.last_command = xstrdup("Connect to server");
198
200 const AsyncCall::Pointer closer = JobCallback(9, 5, Dialer, this,
202 ctrl.opened(fwdState->serverConnection(), closer);
203}
204
206{
207 data.close();
208
209 safe_free(old_request);
210 safe_free(old_reply);
211 fwd = nullptr; // refcounted
212}
213
214void
216{
217 scheduleReadControlReply(0);
218}
219
220void
222{
223 if (data.readBuf == nullptr) {
224 data.readBuf = new MemBuf;
225 data.readBuf->init(4096, SQUID_TCP_SO_RCVBUF);
226 }
227}
228
232void
234{
235 if (Comm::IsConnOpen(ctrl.conn)) {
236 debugs(9, 3, "closing FTP server FD " << ctrl.conn->fd << ", this " << this);
237 fwd->unregister(ctrl.conn);
238 ctrl.close();
239 }
240
241 if (Comm::IsConnOpen(data.conn)) {
242 debugs(9, 3, "closing FTP data FD " << data.conn->fd << ", this " << this);
243 data.close();
244 }
245
246 debugs(9, 3, "FTP ctrl and data connections closed. this " << this);
247}
248
255bool
257{
258 return !Comm::IsConnOpen(ctrl.conn) && !Comm::IsConnOpen(data.conn);
259}
260
261void
263{
264 debugs(9, 3, "entry-null=" << (entry?entry->isEmpty():0) << ", entry=" << entry);
265
266 const char *command, *reply;
267 ErrorState *ftperr;
268
269 if (err) {
270 debugs(9, 6, "error=" << err->type << ", code=" << xerrno <<
271 ", status=" << err->httpStatus);
272 error = err->type;
273 ftperr = err;
274 } else {
275 Http::StatusCode httpStatus = failedHttpStatus(error);
276 ftperr = new ErrorState(error, httpStatus, fwd->request, fwd->al);
277 }
278
279 ftperr->xerrno = xerrno;
280
281 ftperr->ftp.server_msg = ctrl.message;
282 ctrl.message = nullptr;
283
284 if (old_request)
285 command = old_request;
286 else
287 command = ctrl.last_command;
288
289 if (command && strncmp(command, "PASS", 4) == 0)
290 command = "PASS <yourpassword>";
291
292 if (old_reply)
293 reply = old_reply;
294 else
295 reply = ctrl.last_reply;
296
297 if (command)
298 ftperr->ftp.request = xstrdup(command);
299
300 if (reply)
301 ftperr->ftp.reply = xstrdup(reply);
302
303 if (!err) {
304 fwd->request->detailError(error, SysErrorDetail::NewIfAny(xerrno));
305 fwd->fail(ftperr);
306 closeServer(); // we failed, so no serverComplete()
307 }
308}
309
312{
313 if (error == ERR_NONE)
317}
318
324void
326{
327 debugs(9, 3, ctrl.conn);
328
329 if (buffered_ok && ctrl.offset > 0) {
330 /* We've already read some reply data */
331 handleControlReply();
332 } else {
333
334 if (!Comm::IsConnOpen(ctrl.conn)) {
335 debugs(9, 3, "cannot read without ctrl " << ctrl.conn);
336 return;
337 }
338 /*
339 * Cancel the timeout on the Data socket (if any) and
340 * establish one on the control socket.
341 */
342 if (Comm::IsConnOpen(data.conn)) {
343 commUnsetConnTimeout(data.conn);
344 }
345
346 const time_t tout = shortenReadTimeout ?
349 shortenReadTimeout = false; // we only need to do this once, after PASV
350
351 typedef CommCbMemFunT<Client, CommTimeoutCbParams> TimeoutDialer;
352 AsyncCall::Pointer timeoutCall = JobCallback(9, 5, TimeoutDialer, this, Ftp::Client::timeout);
353 commSetConnTimeout(ctrl.conn, tout, timeoutCall);
354
357 comm_read(ctrl.conn, ctrl.buf + ctrl.offset, ctrl.size - ctrl.offset, reader);
358 }
359}
360
361void
363{
364 debugs(9, 3, "FD " << io.fd << ", Read " << io.size << " bytes");
365
366 if (io.size > 0) {
369 }
370
371 if (io.flag == Comm::ERR_CLOSING)
372 return;
373
374 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
375 if (abortOnData("entry aborted during control reply read"))
376 return;
377 }
378
379 assert(ctrl.offset < ctrl.size);
380
381 if (io.flag == Comm::OK && io.size > 0) {
382 fd_bytes(io.fd, io.size, FD_READ);
383 }
384
385 if (io.flag != Comm::OK) {
387 "ERROR: FTP control reply read failure: " << xstrerr(io.xerrno));
388
389 if (ignoreErrno(io.xerrno)) {
390 scheduleReadControlReply(0);
391 } else {
392 failed(ERR_READ_ERROR, io.xerrno);
393 /* failed closes ctrl.conn and frees ftpState */
394 }
395 return;
396 }
397
398 if (io.size == 0) {
399 if (entry->store_status == STORE_PENDING) {
400 failed(ERR_FTP_FAILURE, 0);
401 /* failed closes ctrl.conn and frees ftpState */
402 return;
403 }
404
405 /* XXX this may end up having to be serverComplete() .. */
406 abortAll("zero control reply read");
407 return;
408 }
409
410 unsigned int len =io.size + ctrl.offset;
411 ctrl.offset = len;
412 assert(len <= ctrl.size);
413 if (Comm::IsConnOpen(ctrl.conn))
414 commUnsetConnTimeout(ctrl.conn); // we are done waiting for ctrl reply
415 handleControlReply();
416}
417
418void
420{
421 debugs(9, 3, status());
422
423 size_t bytes_used = 0;
424 wordlistDestroy(&ctrl.message);
425
426 if (!parseControlReply(bytes_used)) {
427 /* didn't get complete reply yet */
428
429 if (ctrl.offset == ctrl.size) {
430 ctrl.buf = static_cast<char*>(memReallocBuf(ctrl.buf, ctrl.size << 1, &ctrl.size));
431 }
432
433 scheduleReadControlReply(0);
434 return;
435 }
436
437 assert(ctrl.message); // the entire FTP server response, line by line
438 assert(ctrl.replycode >= 0); // FTP status code (from the last line)
439 assert(ctrl.last_reply); // FTP reason (from the last line)
440
441 if (ctrl.offset == bytes_used) {
442 /* used it all up */
443 ctrl.offset = 0;
444 } else {
445 /* Got some data past the complete reply */
446 assert(bytes_used < ctrl.offset);
447 ctrl.offset -= bytes_used;
448 memmove(ctrl.buf, ctrl.buf + bytes_used, ctrl.offset);
449 }
450
451 debugs(9, 3, "state=" << state << ", code=" << ctrl.replycode);
452}
453
454bool
456{
457 int code = ctrl.replycode;
458 char *buf;
459 debugs(9, 3, status());
460
461 if (!Comm::IsConnOpen(ctrl.conn)) {
462 debugs(9, 5, "The control connection to the remote end is closed");
463 return false;
464 }
465
466 if (code != 227) {
467 debugs(9, 2, "PASV not supported by remote end");
468 return false;
469 }
470
471 /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
472 /* ANSI sez [^0-9] is undefined, it breaks on Watcom cc */
473 debugs(9, 5, "scanning: " << ctrl.last_reply);
474
475 buf = ctrl.last_reply + strcspn(ctrl.last_reply, "0123456789");
476
477 const char *forceIp = Config.Ftp.sanitycheck ?
478 fd_table[ctrl.conn->fd].ipaddr : nullptr;
479 if (!Ftp::ParseIpPort(buf, forceIp, srvAddr)) {
480 debugs(9, DBG_IMPORTANT, "Unsafe PASV reply from " <<
481 ctrl.conn->remote << ": " << ctrl.last_reply);
482 return false;
483 }
484
485 data.addr(srvAddr);
486
487 return true;
488}
489
490bool
492{
493 int code = ctrl.replycode;
494 char *buf;
495 debugs(9, 3, status());
496
497 if (!Comm::IsConnOpen(ctrl.conn)) {
498 debugs(9, 5, "The control connection to the remote end is closed");
499 return false;
500 }
501
502 if (code != 229 && code != 522) {
503 if (code == 200) {
504 /* handle broken servers (RFC 2428 says OK code for EPSV MUST be 229 not 200) */
505 /* vsftpd for one send '200 EPSV ALL ok.' without even port info.
506 * Its okay to re-send EPSV 1/2 but nothing else. */
507 debugs(9, DBG_IMPORTANT, "ERROR: Broken FTP Server at " << ctrl.conn->remote << ". Wrong accept code for EPSV");
508 } else {
509 debugs(9, 2, "EPSV not supported by remote end");
510 }
511 return sendPassive();
512 }
513
514 if (code == 522) {
515 /* Peer responded with a list of supported methods:
516 * 522 Network protocol not supported, use (1)
517 * 522 Network protocol not supported, use (1,2)
518 * 522 Network protocol not supported, use (2)
519 * TODO: Handle the (1,2) case which may happen after EPSV ALL. Close
520 * data + control without self-destructing and re-open from scratch.
521 */
522 debugs(9, 5, "scanning: " << ctrl.last_reply);
523 buf = ctrl.last_reply;
524 while (buf != nullptr && *buf != '\0' && *buf != '\n' && *buf != '(')
525 ++buf;
526 if (buf != nullptr && *buf == '\n')
527 ++buf;
528
529 if (buf == nullptr || *buf == '\0') {
530 /* handle broken server (RFC 2428 says MUST specify supported protocols in 522) */
531 debugs(9, DBG_IMPORTANT, "ERROR: Broken FTP Server at " << ctrl.conn->remote << ". 522 error missing protocol negotiation hints");
532 return sendPassive();
533 } else if (strcmp(buf, "(1)") == 0) {
534 state = SENT_EPSV_2; /* simulate having sent and failed EPSV 2 */
535 return sendPassive();
536 } else if (strcmp(buf, "(2)") == 0) {
537 if (Ip::EnableIpv6) {
538 /* If server only supports EPSV 2 and we have already tried that. Go straight to EPRT */
539 if (state == SENT_EPSV_2) {
540 return sendEprt();
541 } else {
542 /* or try the next Passive mode down the chain. */
543 return sendPassive();
544 }
545 } else {
546 /* Server only accept EPSV in IPv6 traffic. */
547 state = SENT_EPSV_1; /* simulate having sent and failed EPSV 1 */
548 return sendPassive();
549 }
550 } else {
551 /* handle broken server (RFC 2428 says MUST specify supported protocols in 522) */
552 debugs(9, DBG_IMPORTANT, "WARNING: Server at " << ctrl.conn->remote << " sent unknown protocol negotiation hint: " << buf);
553 return sendPassive();
554 }
555 /* coverity[unreachable] */
556 /* safeguard against possible future bugs in above conditions */
557 failed(ERR_FTP_FAILURE, 0);
558 return false;
559 }
560
561 /* 229 Entering Extended Passive Mode (|||port|) */
562 /* ANSI sez [^0-9] is undefined, it breaks on Watcom cc */
563 debugs(9, 5, "scanning: " << ctrl.last_reply);
564
565 buf = ctrl.last_reply + strcspn(ctrl.last_reply, "(");
566
567 char h1, h2, h3, h4;
568 unsigned short port;
569 int n = sscanf(buf, "(%c%c%c%hu%c)", &h1, &h2, &h3, &port, &h4);
570
571 if (n < 4 || h1 != h2 || h1 != h3 || h1 != h4) {
572 debugs(9, DBG_IMPORTANT, "ERROR: Invalid EPSV reply from " <<
573 ctrl.conn->remote << ": " <<
574 ctrl.last_reply);
575
576 return sendPassive();
577 }
578
579 if (0 == port) {
580 debugs(9, DBG_IMPORTANT, "Unsafe EPSV reply from " <<
581 ctrl.conn->remote << ": " <<
582 ctrl.last_reply);
583
584 return sendPassive();
585 }
586
587 if (Config.Ftp.sanitycheck) {
588 if (port < 1024) {
589 debugs(9, DBG_IMPORTANT, "Unsafe EPSV reply from " <<
590 ctrl.conn->remote << ": " <<
591 ctrl.last_reply);
592
593 return sendPassive();
594 }
595 }
596
597 remoteAddr = ctrl.conn->remote;
598 remoteAddr.port(port);
599 data.addr(remoteAddr);
600 return true;
601}
602
603// FTP clients do not support EPRT and PORT commands yet.
604// The Ftp::Client::sendEprt() will fail because of the unimplemented
605// openListenSocket() or sendPort() methods
606bool
608{
609 if (!Config.Ftp.eprt) {
610 /* Disabled. Switch immediately to attempting old PORT command. */
611 debugs(9, 3, "EPRT disabled by local administrator");
612 return sendPort();
613 }
614
615 debugs(9, 3, status());
616
617 if (!openListenSocket()) {
618 failed(ERR_FTP_FAILURE, 0);
619 return false;
620 }
621
622 debugs(9, 3, "Listening for FTP data connection with FD " << data.conn);
623 if (!Comm::IsConnOpen(data.conn)) {
624 // TODO: Set error message.
625 failed(ERR_FTP_FAILURE, 0);
626 return false;
627 }
628
629 static MemBuf mb;
630 mb.reset();
631 char buf[MAX_IPSTRLEN];
632 /* RFC 2428 defines EPRT as IPv6 equivalent to IPv4 PORT command. */
633 /* Which can be used by EITHER protocol. */
634 debugs(9, 3, "Listening for FTP data connection on port" << comm_local_port(data.conn->fd) << " or port?" << data.conn->local.port());
635 mb.appendf("EPRT |%d|%s|%d|%s",
636 ( data.conn->local.isIPv6() ? 2 : 1 ),
637 data.conn->local.toStr(buf,MAX_IPSTRLEN),
638 comm_local_port(data.conn->fd), Ftp::crlf );
639
640 state = SENT_EPRT;
641 writeCommand(mb.content());
642 return true;
643}
644
645bool
647{
648 failed(ERR_FTP_FAILURE, 0);
649 return false;
650}
651
652bool
654{
655 debugs(9, 3, status());
656
662 if (Config.Ftp.epsv_all && state == SENT_EPSV_1 ) {
663 // We are here because the last "EPSV 1" failed, but because of epsv_all
664 // no other method allowed.
665 debugs(9, DBG_IMPORTANT, "FTP does not allow PASV method after 'EPSV ALL' has been sent.");
666 failed(ERR_FTP_FAILURE, 0);
667 return false;
668 }
669
671 data.close();
672
676 if (!Config.Ftp.passive || state == SENT_PASV) {
677 sendEprt();
678 return true;
679 }
680
681 static MemBuf mb;
682 mb.reset();
691 switch (state) {
692 case SENT_EPSV_ALL: /* EPSV ALL resulted in a bad response. Try ther EPSV methods. */
693 if (ctrl.conn->local.isIPv6()) {
694 debugs(9, 5, "FTP Channel is IPv6 (" << ctrl.conn->remote << ") attempting EPSV 2 after EPSV ALL has failed.");
695 mb.appendf("EPSV 2%s", Ftp::crlf);
696 state = SENT_EPSV_2;
697 break;
698 }
699 /* [[fallthrough]] to skip EPSV 2 */
700
701 case SENT_EPSV_2: /* EPSV IPv6 failed. Try EPSV IPv4 */
702 if (ctrl.conn->local.isIPv4()) {
703 debugs(9, 5, "FTP Channel is IPv4 (" << ctrl.conn->remote << ") attempting EPSV 1 after EPSV ALL has failed.");
704 mb.appendf("EPSV 1%s", Ftp::crlf);
705 state = SENT_EPSV_1;
706 break;
707 } else if (Config.Ftp.epsv_all) {
708 debugs(9, DBG_IMPORTANT, "FTP does not allow PASV method after 'EPSV ALL' has been sent.");
709 failed(ERR_FTP_FAILURE, 0);
710 return false;
711 }
712 /* [[fallthrough]] to skip EPSV 1 */
713
714 case SENT_EPSV_1: /* EPSV options exhausted. Try PASV now. */
715 debugs(9, 5, "FTP Channel (" << ctrl.conn->remote << ") rejects EPSV connection attempts. Trying PASV instead.");
716 mb.appendf("PASV%s", Ftp::crlf);
717 state = SENT_PASV;
718 break;
719
720 default: {
721 bool doEpsv = true;
723 ACLFilledChecklist checklist(Config.accessList.ftp_epsv, fwd->request, nullptr);
724 doEpsv = checklist.fastCheck().allowed();
725 }
726 if (!doEpsv) {
727 debugs(9, 5, "EPSV support manually disabled. Sending PASV for FTP Channel (" << ctrl.conn->remote <<")");
728 mb.appendf("PASV%s", Ftp::crlf);
729 state = SENT_PASV;
730 } else if (Config.Ftp.epsv_all) {
731 debugs(9, 5, "EPSV ALL manually enabled. Attempting with FTP Channel (" << ctrl.conn->remote <<")");
732 mb.appendf("EPSV ALL%s", Ftp::crlf);
733 state = SENT_EPSV_ALL;
734 } else {
735 if (ctrl.conn->local.isIPv6()) {
736 debugs(9, 5, "FTP Channel (" << ctrl.conn->remote << "). Sending default EPSV 2");
737 mb.appendf("EPSV 2%s", Ftp::crlf);
738 state = SENT_EPSV_2;
739 }
740 if (ctrl.conn->local.isIPv4()) {
741 debugs(9, 5, "Channel (" << ctrl.conn->remote <<"). Sending default EPSV 1");
742 mb.appendf("EPSV 1%s", Ftp::crlf);
743 state = SENT_EPSV_1;
744 }
745 }
746 break;
747 }
748 }
749
750 if (ctrl.message)
751 wordlistDestroy(&ctrl.message);
752 ctrl.message = nullptr; //No message to return to client.
753 ctrl.offset = 0; //reset readed response, to make room read the next response
754
755 writeCommand(mb.content());
756
757 shortenReadTimeout = true;
758 return true;
759}
760
761void
763{
764 if (!Comm::IsConnOpen(ctrl.conn)) {
765 debugs(9, 5, "The control connection to the remote end is closed");
766 return;
767 }
768
769 safe_free(ctrl.last_command);
770
771 safe_free(ctrl.last_reply);
772
773 ctrl.last_command = xstrdup("Connect to server data port");
774
775 // Generate a new data channel descriptor to be opened.
777 conn->setAddrs(ctrl.conn->local, data.host);
778 conn->local.port(0);
779 conn->remote.port(data.port);
780 conn->tos = ctrl.conn->tos;
781 conn->nfmark = ctrl.conn->nfmark;
782 // Using non-local addresses in TPROXY mode requires appropriate socket option.
783 conn->flags |= ctrl.conn->flags & COMM_TRANSPARENT;
784
785 debugs(9, 3, "connecting to " << conn->remote);
786
789 const auto cs = new Comm::ConnOpener(conn, callback, Config.Timeout.connect);
790 cs->setHost(data.host);
791 dataConnWait.start(cs, callback);
792}
793
794bool
796{
797 return false;
798}
799
803{
805 return JobCallback(9, 5, Dialer, this, Ftp::Client::dataClosed);
806}
807
809void
811{
812 debugs(9, 4, status());
813 if (data.conn)
814 data.conn->noteClosure();
815 if (data.listenConn != nullptr) {
816 data.listenConn->close();
817 data.listenConn = nullptr;
818 }
819 data.clear();
820}
821
822void
824{
825 char *ebuf;
826 /* trace FTP protocol communications at level 2 */
827 debugs(9, 2, "ftp<< " << buf);
828
829 if (Config.Ftp.telnet)
830 ebuf = escapeIAC(buf);
831 else
832 ebuf = xstrdup(buf);
833
834 safe_free(ctrl.last_command);
835
836 safe_free(ctrl.last_reply);
837
838 ctrl.last_command = ebuf;
839
840 if (!Comm::IsConnOpen(ctrl.conn)) {
841 debugs(9, 2, "cannot send to closing ctrl " << ctrl.conn);
842 // TODO: assert(ctrl.closer != NULL);
843 return;
844 }
845
847 AsyncCall::Pointer call = JobCallback(9, 5, Dialer, this,
849 Comm::Write(ctrl.conn, ctrl.last_command, strlen(ctrl.last_command), call, nullptr);
850
851 scheduleReadControlReply(0);
852}
853
854void
856{
857
858 debugs(9, 5, "wrote " << io.size << " bytes");
859
860 if (io.size > 0) {
861 fd_bytes(io.fd, io.size, FD_WRITE);
864 }
865
866 if (io.flag == Comm::ERR_CLOSING)
867 return;
868
869 if (io.flag) {
870 debugs(9, DBG_IMPORTANT, "ERROR: FTP command write failure: " << io.conn << ": " << xstrerr(io.xerrno));
871 failed(ERR_WRITE_ERROR, io.xerrno);
872 /* failed closes ctrl.conn and frees ftpState */
873 return;
874 }
875}
876
878void
880{
881 debugs(9, 4, status());
882 if (ctrl.conn)
883 ctrl.conn->noteClosure();
884 ctrl.clear();
885 doneWithFwd = "ctrlClosed()"; // assume FwdState is monitoring too
886 mustStop("Ftp::Client::ctrlClosed");
887}
888
889void
891{
892 debugs(9, 4, io.conn << ": '" << entry->url() << "'" );
893
894 if (abortOnBadEntry("entry went bad while waiting for a timeout"))
895 return;
896
897 failed(ERR_READ_TIMEOUT, 0);
898 /* failed() closes ctrl.conn and frees ftpState */
899}
900
903{
904 return data.conn;
905}
906
907void
909{
910 data.read_pending = false;
911 maybeReadVirginBody();
912}
913
914void
916{
917 // too late to read
918 if (!Comm::IsConnOpen(data.conn) || fd_table[data.conn->fd].closing())
919 return;
920
921 if (data.read_pending)
922 return;
923
924 initReadBuf();
925
926 const int read_sz = replyBodySpace(*data.readBuf, 0);
927
928 debugs(9, 9, "FTP may read up to " << read_sz << " bytes");
929
930 if (read_sz < 2) // see http.cc
931 return;
932
933 data.read_pending = true;
934
935 typedef CommCbMemFunT<Client, CommTimeoutCbParams> TimeoutDialer;
936 AsyncCall::Pointer timeoutCall = JobCallback(9, 5,
937 TimeoutDialer, this, Ftp::Client::timeout);
938 commSetConnTimeout(data.conn, Config.Timeout.read, timeoutCall);
939
940 debugs(9,5,"queueing read on FD " << data.conn->fd);
941
942 const auto amountToRead = entry->bytesWanted(Range<size_t>(0, read_sz));
943
944 if (amountToRead <= 0) {
945 delayRead();
946 return;
947 }
948
949 using ReadDialer = CommCbMemFunT<Client, CommIoCbParams>;
950 AsyncCall::Pointer readCallback = JobCallback(9, 5, ReadDialer, this, Client::dataRead);
951 comm_read(data.conn, data.readBuf->space(), amountToRead, readCallback);
952}
953
954void
956{
957 int j;
958 int bin;
959
960 data.read_pending = false;
961
962 debugs(9, 3, "FD " << io.fd << " Read " << io.size << " bytes");
963
964 if (io.size > 0) {
967 }
968
969 if (io.flag == Comm::ERR_CLOSING)
970 return;
971
972 assert(io.fd == data.conn->fd);
973
974 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
975 abortOnData("entry aborted during dataRead");
976 return;
977 }
978
979 if (io.flag == Comm::OK && io.size > 0) {
980 debugs(9, 5, "appended " << io.size << " bytes to readBuf");
981 data.readBuf->appended(io.size);
982#if USE_DELAY_POOLS
983 DelayId delayId = entry->mem_obj->mostBytesAllowed();
984 delayId.bytesIn(io.size);
985#endif
986 ++ IOStats.Ftp.reads;
987
988 for (j = io.size - 1, bin = 0; j; ++bin)
989 j >>= 1;
990
991 ++ IOStats.Ftp.read_hist[bin];
992 }
993
994 if (io.flag != Comm::OK) {
996 "ERROR: FTP data read failure: " << xstrerr(io.xerrno));
997
998 if (ignoreErrno(io.xerrno)) {
999 maybeReadVirginBody();
1000 } else {
1001 failed(ERR_READ_ERROR, 0);
1002 /* failed closes ctrl.conn and frees ftpState */
1003 return;
1004 }
1005 } else if (io.size == 0) {
1006 debugs(9, 3, "Calling dataComplete() because io.size == 0");
1007 /*
1008 * DPW 2007-04-23
1009 * Dangerous curves ahead. This call to dataComplete was
1010 * calling scheduleReadControlReply, handleControlReply,
1011 * and then ftpReadTransferDone. If ftpReadTransferDone
1012 * gets unexpected status code, it closes down the control
1013 * socket and our FtpStateData object gets destroyed. As
1014 * a workaround we no longer set the 'buffered_ok' flag in
1015 * the scheduleReadControlReply call.
1016 */
1017 dataComplete();
1018 }
1019
1020 processReplyBody();
1021}
1022
1023void
1025{
1026 debugs(9, 3,status());
1027
1028 /* Connection closed; transfer done. */
1029
1031 data.close();
1032
1033 /* expect the "transfer complete" message on the control socket */
1034 /*
1035 * DPW 2007-04-23
1036 * Previously, this was the only place where we set the
1037 * 'buffered_ok' flag when calling scheduleReadControlReply().
1038 * It caused some problems if the FTP server returns an unexpected
1039 * status code after the data command. FtpStateData was being
1040 * deleted in the middle of dataRead().
1041 */
1042 /* AYJ: 2011-01-13: Bug 2581.
1043 * 226 status is possibly waiting in the ctrl buffer.
1044 * The connection will hang if we DONT send buffered_ok.
1045 * This happens on all transfers which can be completely sent by the
1046 * server before the 150 started status message is read in by Squid.
1047 * ie all transfers of about one packet hang.
1048 */
1049 scheduleReadControlReply(1);
1050}
1051
1052void
1053Ftp::Client::abortAll(const char *reason)
1054{
1055 debugs(9, 3, "aborting transaction for " << reason <<
1056 "; FD " << (ctrl.conn!=nullptr?ctrl.conn->fd:-1) << ", Data FD " << (data.conn!=nullptr?data.conn->fd:-1) << ", this " << this);
1057 mustStop(reason);
1058}
1059
1064void
1066{
1067 commUnsetConnTimeout(ctrl.conn);
1068
1069 typedef CommCbMemFunT<Client, CommTimeoutCbParams> TimeoutDialer;
1070 AsyncCall::Pointer timeoutCall = JobCallback(9, 5, TimeoutDialer, this,
1072 commSetConnTimeout(data.conn, Config.Timeout.read, timeoutCall);
1073}
1074
1075void
1077{
1078 if (io.size > 0)
1081}
1082
1086void
1088{
1090 debugs(9, 3, status());
1091 dataComplete();
1092 /* NP: RFC 959 3.3. DATA CONNECTION MANAGEMENT
1093 * if transfer type is 'stream' call dataComplete()
1094 * otherwise leave open. (reschedule control channel read?)
1095 */
1096}
1097
1100bool
1102{
1103 char *s;
1104 char *sbuf;
1105 char *end;
1106 int usable;
1107 int complete = 0;
1108 wordlist *head = nullptr;
1109 wordlist *list;
1110 wordlist **tail = &head;
1111 size_t linelen;
1112 debugs(9, 3, status());
1113 /*
1114 * We need a NULL-terminated buffer for scanning, ick
1115 */
1116 const size_t len = ctrl.offset;
1117 sbuf = (char *)xmalloc(len + 1);
1118 xstrncpy(sbuf, ctrl.buf, len + 1);
1119 end = sbuf + len - 1;
1120
1121 while (*end != '\r' && *end != '\n' && end > sbuf)
1122 --end;
1123
1124 usable = end - sbuf;
1125
1126 debugs(9, 3, "usable = " << usable);
1127
1128 if (usable == 0) {
1129 debugs(9, 3, "didn't find end of line");
1130 safe_free(sbuf);
1131 return false;
1132 }
1133
1134 debugs(9, 3, len << " bytes to play with");
1135 ++end;
1136 s = sbuf;
1137 s += strspn(s, crlf);
1138
1139 for (; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
1140 if (complete)
1141 break;
1142
1143 debugs(9, 5, "s = {" << s << "}");
1144
1145 linelen = strcspn(s, crlf) + 1;
1146
1147 if (linelen < 2)
1148 break;
1149
1150 if (linelen > 3)
1151 complete = (*s >= '0' && *s <= '9' && *(s + 3) == ' ');
1152
1153 list = new wordlist();
1154
1155 list->key = (char *)xmalloc(linelen);
1156
1157 xstrncpy(list->key, s, linelen);
1158
1159 /* trace the FTP communication chat at level 2 */
1160 debugs(9, 2, "ftp>> " << list->key);
1161
1162 if (complete) {
1163 // use list->key for last_reply because s contains the new line
1164 ctrl.last_reply = xstrdup(list->key + 4);
1165 ctrl.replycode = atoi(list->key);
1166 }
1167
1168 *tail = list;
1169
1170 tail = &list->next;
1171 }
1172
1173 bytesUsed = static_cast<size_t>(s - sbuf);
1174 safe_free(sbuf);
1175
1176 if (!complete) {
1178 return false;
1179 }
1180
1181 ctrl.message = head;
1182 assert(ctrl.replycode >= 0);
1183 assert(ctrl.last_reply);
1184 assert(ctrl.message);
1185 return true;
1186}
1187
1188}; // namespace Ftp
1189
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
#define COMM_TRANSPARENT
Definition: Connection.h:50
#define false
Definition: GnuRegex.c:240
int size
Definition: ModDevPoll.cc:75
void comm_read(const Comm::ConnectionPointer &conn, char *buf, int len, AsyncCall::Pointer &callback)
Definition: Read.h:59
class SquidConfig Config
Definition: SquidConfig.cc:12
StatCounters statCounter
Definition: StatCounters.cc:12
int conn
the current server connection FD
Definition: Transport.cc:26
void error(char *format,...)
squidaio_request_t * head
Definition: aiops.cc:126
#define assert(EX)
Definition: assert.h:19
Acl::Answer const & fastCheck()
Definition: Checklist.cc:332
bool allowed() const
Definition: Acl.h:150
virtual void sentRequestBody(const CommIoCbParams &io)=0
Definition: Client.cc:364
virtual void doneSendingRequestBody()=0
Definition: Client.cc:340
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition: CommCalls.h:88
int fd
FD which the call was about. Set by the async call creator.
Definition: CommCalls.h:90
Comm::Flag flag
comm layer result status.
Definition: CommCalls.h:87
Comm::ConnectionPointer conn
Definition: CommCalls.h:85
void bytesIn(int qty)
Definition: DelayId.cc:152
err_type type
Definition: errorpage.h:168
int xerrno
Definition: errorpage.h:177
char * reply
Definition: errorpage.h:190
wordlist * server_msg
Definition: errorpage.h:188
HttpRequestPointer request
Definition: errorpage.h:175
Http::StatusCode httpStatus
Definition: errorpage.h:171
struct ErrorState::@62 ftp
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:311
bool handleEpsvReply(Ip::Address &remoteAddr)
Definition: FtpClient.cc:491
virtual void closeServer()
Definition: FtpClient.cc:233
virtual void handleControlReply()
Definition: FtpClient.cc:419
void dataRead(const CommIoCbParams &io)
Definition: FtpClient.cc:955
void initReadBuf()
Definition: FtpClient.cc:221
bool sendPort()
Definition: FtpClient.cc:646
virtual const Comm::ConnectionPointer & dataConnection() const
Definition: FtpClient.cc:902
virtual void abortAll(const char *reason)
abnormal transaction termination; reason is for debugging only
Definition: FtpClient.cc:1053
void dataComplete()
Definition: FtpClient.cc:1024
void scheduleReadControlReply(int buffered_ok)
Definition: FtpClient.cc:325
virtual void doneSendingRequestBody()
Definition: FtpClient.cc:1087
bool openListenSocket()
Definition: FtpClient.cc:795
void writeCommand(const char *buf)
Definition: FtpClient.cc:823
virtual void maybeReadVirginBody()
read response data from the network
Definition: FtpClient.cc:915
virtual void timeout(const CommTimeoutCbParams &io)
read timeout handler
Definition: FtpClient.cc:890
bool handlePasvReply(Ip::Address &remoteAddr)
Definition: FtpClient.cc:455
void switchTimeoutToDataChannel()
Definition: FtpClient.cc:1065
virtual void start()
called by AsyncStart; do not call directly
Definition: FtpClient.cc:215
void connectDataChannel()
Definition: FtpClient.cc:762
virtual ~Client()
Definition: FtpClient.cc:205
void writeCommandCallback(const CommIoCbParams &io)
Definition: FtpClient.cc:855
virtual bool doneWithServer() const
Definition: FtpClient.cc:256
Client(FwdState *fwdState)
Definition: FtpClient.cc:184
bool sendEprt()
Definition: FtpClient.cc:607
void readControlReply(const CommIoCbParams &io)
Definition: FtpClient.cc:362
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:262
CtrlChannel ctrl
FTP control channel state.
Definition: FtpClient.h:142
bool sendPassive()
Definition: FtpClient.cc:653
bool parseControlReply(size_t &bytesUsed)
Definition: FtpClient.cc:1101
AsyncCall::Pointer dataCloser()
creates a data channel Comm close callback
Definition: FtpClient.cc:802
void ctrlClosed(const CommCloseCbParams &io)
handler called by Comm when FTP control channel is closed unexpectedly
Definition: FtpClient.cc:879
virtual void noteDelayAwareReadChance()
Definition: FtpClient.cc:908
virtual void dataChannelConnected(const CommConnectCbParams &io)=0
virtual void sentRequestBody(const CommIoCbParams &io)
Definition: FtpClient.cc:1076
virtual void dataClosed(const CommCloseCbParams &io)
handler called by Comm when FTP data channel is closed unexpectedly
Definition: FtpClient.cc:810
char * last_command
Definition: FtpClient.h:83
void addr(const Ip::Address &addr)
import host and port
Definition: FtpClient.cc:173
virtual SBuf verbose(const HttpRequestPointer &) const override
Definition: FtpClient.cc:81
virtual SBuf brief() const override
Definition: FtpClient.cc:75
Comm::ConnectionPointer const & serverConnection() const
Definition: FwdState.h:106
int read_hist[histSize]
Definition: IoStats.h:21
struct IoStats::@80 Ftp
int reads
Definition: IoStats.h:19
unsigned short port() const
Definition: Address.cc:778
Definition: MemBuf.h:24
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
struct SquidConfig::@113 Ftp
struct SquidConfig::@112 accessList
time_t read
Definition: SquidConfig.h:110
time_t connect
Definition: SquidConfig.h:113
acl_access * ftp_epsv
Definition: SquidConfig.h:400
struct SquidConfig::@98 Timeout
struct StatCounters::@129::@139 all
struct StatCounters::@129::@139 ftp
ByteCounter kbytes_out
Definition: StatCounters.h:46
ByteCounter kbytes_in
Definition: StatCounters.h:45
struct StatCounters::@129 server
static ErrorDetail::Pointer NewIfAny(const int errorNo)
char * key
Definition: wordlist.h:32
wordlist * next
Definition: wordlist.h:33
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:947
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:976
int commSetConnTimeout(const Comm::ConnectionPointer &conn, int timeout, AsyncCall::Pointer &callback)
Definition: comm.cc:589
unsigned short comm_local_port(int fd)
Definition: comm.cc:163
int ignoreErrno(int ierrno)
Definition: comm.cc:1438
int commUnsetConnTimeout(const Comm::ConnectionPointer &conn)
Definition: comm.cc:615
A const & min(A const &lhs, A const &rhs)
#define DBG_IMPORTANT
Definition: Stream.h:41
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
#define EBIT_TEST(flag, bit)
Definition: defines.h:69
static int port
Definition: ldap_backend.cc:70
@ FD_WRITE
Definition: enums.h:24
@ FD_READ
Definition: enums.h:23
@ ENTRY_ABORTED
Definition: enums.h:115
@ STORE_PENDING
Definition: enums.h:51
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(int fd, int len, unsigned int type)
Definition: fd.cc:227
#define fd_table
Definition: fde.h:189
IoStats IOStats
unsigned char code
Definition: html_quote.c:20
#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:84
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:53
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:25
Definition: forward.h:28
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:75
@ scBadGateway
Definition: StatusCode.h:73
SSL Connection
Definition: Session.h:45
#define xfree
#define xstrdup
#define xmalloc
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:63
int EnableIpv6
Whether IPv6 is supported and type of support.
Definition: tools.h:25
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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors