FtpGateway.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 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/PackableStream.h"
14#include "clients/forward.h"
15#include "clients/FtpClient.h"
16#include "comm.h"
17#include "comm/ConnOpener.h"
18#include "comm/Read.h"
19#include "comm/TcpAcceptor.h"
20#include "CommCalls.h"
21#include "compat/strtoll.h"
22#include "errorpage.h"
23#include "fd.h"
24#include "fde.h"
25#include "FwdState.h"
26#include "html/Quoting.h"
27#include "HttpHdrContRange.h"
28#include "HttpHeader.h"
29#include "HttpHeaderRange.h"
30#include "HttpReply.h"
31#include "ip/tools.h"
32#include "MemBuf.h"
33#include "mime.h"
34#include "rfc1738.h"
35#include "SquidConfig.h"
36#include "SquidString.h"
37#include "StatCounters.h"
38#include "Store.h"
39#include "tools.h"
40#include "util.h"
41#include "wordlist.h"
42
43#if USE_DELAY_POOLS
44#include "DelayPools.h"
45#include "MemObject.h"
46#endif
47
48#include <cerrno>
49#if HAVE_REGEX_H
50#include <regex.h>
51#endif
52
53namespace Ftp
54{
55
57
58 /* passive mode */
62 bool pasv_failed; // was FwdState::flags.ftp_pasv_failed
63
64 /* authentication */
68
69 /* other */
70 bool isdir;
79 bool binary;
81 bool put;
84 bool listing;
86};
87
88class Gateway;
89typedef void (StateMethod)(Ftp::Gateway *);
90
94class Gateway : public Ftp::Client
95{
97
98public:
100 ~Gateway() override;
111 time_t mdtm;
112 int64_t theSize;
114 char *filepath;
115 char *dirpath;
123
125
126public:
127 // these should all be private
128 void start() override;
130 int restartable();
131 void appendSuccessHeader();
132 void hackShortcut(StateMethod *nextState);
133 void unhack();
134 void readStor();
135 void parseListing();
136 bool htmlifyListEntry(const char *line, PackableStream &);
137 void completedListing(void);
138
141
142 int checkAuth(const HttpHeader * req_hdr);
143 void checkUrlpath();
144 void buildTitleUrl();
145 void writeReplyBody(const char *, size_t len);
146 void completeForwarding() override;
147 void processHeadResponse();
148 void processReplyBody() override;
149 void setCurrentOffset(int64_t offset) { currentOffset = offset; }
150 int64_t getCurrentOffset() const { return currentOffset; }
151
152 void dataChannelConnected(const CommConnectCbParams &io) override;
154 void timeout(const CommTimeoutCbParams &io) override;
156
158 SBuf ftpRealm();
159 void loginFailed(void);
160
161 void haveParsedReplyHeaders() override;
162
163 virtual bool haveControlChannel(const char *caller_name) const;
164
165protected:
166 void handleControlReply() override;
167 void dataClosed(const CommCloseCbParams &io) override;
168
169private:
170 bool mayReadVirginReplyBody() const override;
171 // BodyConsumer for HTTP: consume request body.
172 void handleRequestBodyProducerAborted() override;
173
174 void loginParser(const SBuf &login, bool escaped);
175};
176
177} // namespace Ftp
178
179typedef Ftp::StateMethod FTPSM; // to avoid lots of non-changes
180
182
183typedef struct {
184 char type;
185 int64_t size;
186 char *date;
187 char *name;
188 char *showname;
189 char *link;
191
192#define CTRL_BUFLEN 16*1024
193static char cbuf[CTRL_BUFLEN];
194
195/*
196 * State machine functions
197 * send == state transition
198 * read == wait for response, and select next state transition
199 * other == Transition logic
200 */
241
242/************************************************
243** Debugs Levels used here **
244*************************************************
2450 CRITICAL Events
2461 IMPORTANT Events
247 Protocol and Transmission failures.
2482 FTP Protocol Chatter
2493 Logic Flows
2504 Data Parsing Flows
2515 Data Dumps
2527 ??
253************************************************/
254
255/************************************************
256** State Machine Description (excluding hacks) **
257*************************************************
258From To
259---------------------------------------
260Welcome User
261User Pass
262Pass Type
263Type TraverseDirectory / GetFile
264TraverseDirectory Cwd / GetFile / ListDir
265Cwd TraverseDirectory / Mkdir
266GetFile Mdtm
267Mdtm Size
268Size Epsv
269ListDir Epsv
270Epsv FileOrList
271FileOrList Rest / Retr / Nlst / List / Mkdir (PUT /xxx;type=d)
272Rest Retr
273Retr / Nlst / List DataRead* (on datachannel)
274DataRead* ReadTransferDone
275ReadTransferDone DataTransferDone
276Stor DataWrite* (on datachannel)
277DataWrite* RequestPutBody** (from client)
278RequestPutBody** DataWrite* / WriteTransferDone
279WriteTransferDone DataTransferDone
280DataTransferDone Quit
281Quit -
282************************************************/
283
285 ftpReadWelcome, /* BEGIN */
286 ftpReadUser, /* SENT_USER */
287 ftpReadPass, /* SENT_PASS */
288 ftpReadType, /* SENT_TYPE */
289 ftpReadMdtm, /* SENT_MDTM */
290 ftpReadSize, /* SENT_SIZE */
291 ftpReadEPRT, /* SENT_EPRT */
292 ftpReadPORT, /* SENT_PORT */
293 ftpReadEPSV, /* SENT_EPSV_ALL */
294 ftpReadEPSV, /* SENT_EPSV_1 */
295 ftpReadEPSV, /* SENT_EPSV_2 */
296 ftpReadPasv, /* SENT_PASV */
297 ftpReadCwd, /* SENT_CWD */
298 ftpReadList, /* SENT_LIST */
299 ftpReadList, /* SENT_NLST */
300 ftpReadRest, /* SENT_REST */
301 ftpReadRetr, /* SENT_RETR */
302 ftpReadStor, /* SENT_STOR */
303 ftpReadQuit, /* SENT_QUIT */
304 ftpReadTransferDone, /* READING_DATA (RETR,LIST,NLST) */
305 ftpWriteTransferDone, /* WRITING_DATA (STOR) */
306 ftpReadMkdir, /* SENT_MKDIR */
307 nullptr, /* SENT_FEAT */
308 nullptr, /* SENT_PWD */
309 nullptr, /* SENT_CDUP*/
310 nullptr, /* SENT_DATA_REQUEST */
311 nullptr /* SENT_COMMAND */
312};
313
315void
317{
320 /* failed closes ctrl.conn and frees ftpState */
321
322 /* NP: failure recovery may be possible when its only a data.conn failure.
323 * if the ctrl.conn is still fine, we can send ABOR down it and retry.
324 * Just need to watch out for wider Squid states like shutting down or reconfigure.
325 */
326}
327
329 AsyncJob("FtpStateData"),
330 Ftp::Client(fwdState),
331 password_url(0),
332 reply_hdr(nullptr),
333 reply_hdr_state(0),
334 conn_att(0),
335 login_att(0),
336 mdtm(-1),
337 theSize(-1),
338 pathcomps(nullptr),
339 filepath(nullptr),
340 dirpath(nullptr),
341 restart_offset(0),
342 proxy_host(nullptr),
343 list_width(0),
344 old_filepath(nullptr),
345 typecode('\0')
346{
347 debugs(9, 3, entry->url());
348
349 *user = 0;
350 *password = 0;
351 memset(&flags, 0, sizeof(flags));
352
355
357
359 flags.put = 1;
360
361 initReadBuf();
362}
363
365{
366 debugs(9, 3, entry->url());
367
368 if (Comm::IsConnOpen(ctrl.conn)) {
369 debugs(9, DBG_IMPORTANT, "ERROR: Squid BUG: FTP Gateway left open " <<
370 "control channel " << ctrl.conn);
371 }
372
373 if (reply_hdr) {
374 memFree(reply_hdr, MEM_8K_BUF);
375 reply_hdr = nullptr;
376 }
377
378 if (pathcomps)
379 wordlistDestroy(&pathcomps);
380
381 cwd_message.clean();
382 xfree(old_filepath);
383 title_url.clean();
384 base_href.clean();
385 xfree(filepath);
386 xfree(dirpath);
387}
388
396void
397Ftp::Gateway::loginParser(const SBuf &login, bool escaped)
398{
399 debugs(9, 4, "login=" << login << ", escaped=" << escaped);
400 debugs(9, 9, "IN : login=" << login << ", escaped=" << escaped << ", user=" << user << ", password=" << password);
401
402 if (login.isEmpty())
403 return;
404
405 const SBuf::size_type colonPos = login.find(':');
406
407 /* If there was a username part with at least one character use it.
408 * Ignore 0-length username portion, retain what we have already.
409 */
410 if (colonPos == SBuf::npos || colonPos > 0) {
411 const SBuf userName = login.substr(0, colonPos);
412 SBuf::size_type upto = userName.copy(user, sizeof(user)-1);
413 user[upto]='\0';
414 debugs(9, 9, "found user=" << userName << ' ' <<
415 (upto != userName.length() ? ", truncated-to=" : ", length=") << upto <<
416 ", escaped=" << escaped);
417 if (escaped)
418 rfc1738_unescape(user);
419 debugs(9, 9, "found user=" << user << " (" << strlen(user) << ") unescaped.");
420 }
421
422 /* If there was a password part.
423 * For 0-length password clobber what we have already, this means explicitly none
424 */
425 if (colonPos != SBuf::npos) {
426 const SBuf pass = login.substr(colonPos+1, SBuf::npos);
427 SBuf::size_type upto = pass.copy(password, sizeof(password)-1);
428 password[upto]='\0';
429 debugs(9, 9, "found password=" << pass << " " <<
430 (upto != pass.length() ? ", truncated-to=" : ", length=") << upto <<
431 ", escaped=" << escaped);
432 if (escaped) {
433 rfc1738_unescape(password);
434 password_url = 1;
435 }
436 debugs(9, 9, "found password=" << password << " (" << strlen(password) << ") unescaped.");
437 }
438
439 debugs(9, 9, "OUT: login=" << login << ", escaped=" << escaped << ", user=" << user << ", password=" << password);
440}
441
442void
444{
445 if (!Comm::IsConnOpen(ctrl.conn)) {
446 debugs(9, 5, "The control connection to the remote end is closed");
447 return;
448 }
449
450 assert(!Comm::IsConnOpen(data.conn));
451
453 typedef AsyncCallT<AcceptDialer> AcceptCall;
454 const auto call = JobCallback(11, 5, AcceptDialer, this, Ftp::Gateway::ftpAcceptDataConnection);
456 const char *note = entry->url();
457
458 /* open the conn if its not already open */
459 if (!Comm::IsConnOpen(conn)) {
460 conn->fd = comm_open_listener(SOCK_STREAM, IPPROTO_TCP, conn->local, conn->flags, note);
461 if (!Comm::IsConnOpen(conn)) {
462 debugs(5, DBG_CRITICAL, "ERROR: comm_open_listener failed:" << conn->local << " error: " << errno);
463 return;
464 }
465 debugs(9, 3, "Unconnected data socket created on " << conn);
466 }
467
468 conn->tos = ctrl.conn->tos;
469 conn->nfmark = ctrl.conn->nfmark;
470
472 AsyncJob::Start(new Comm::TcpAcceptor(conn, note, sub));
473
474 // Ensure we have a copy of the FD opened for listening and a close handler on it.
475 data.opened(conn, dataCloser());
476 switchTimeoutToDataChannel();
477}
478
479void
481{
482 if (SENT_PASV == state) {
483 /* stupid ftp.netscape.com, of FTP server behind stupid firewall rules */
484 flags.pasv_supported = false;
485 debugs(9, DBG_IMPORTANT, "FTP Gateway timeout in SENT_PASV state");
486
487 // cancel the data connection setup, if any
488 dataConnWait.cancel("timeout");
489
490 data.close();
491 }
492
494}
495
496static const char *Month[] = {
497 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
498 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
499};
500
501static int
502is_month(const char *buf)
503{
504 int i;
505
506 for (i = 0; i < 12; ++i)
507 if (!strcasecmp(buf, Month[i]))
508 return 1;
509
510 return 0;
511}
512
513static void
515{
516 safe_free((*parts)->date);
517 safe_free((*parts)->name);
518 safe_free((*parts)->showname);
519 safe_free((*parts)->link);
520 safe_free(*parts);
521}
522
523#define MAX_TOKENS 64
524
525static ftpListParts *
526ftpListParseParts(const char *buf, struct Ftp::GatewayFlags flags)
527{
528 ftpListParts *p = nullptr;
529 char *t = nullptr;
530 struct FtpLineToken {
531 char *token = nullptr;
532 size_t pos = 0;
534 int i;
535 int n_tokens;
536 static char tbuf[128];
537 char *xbuf = nullptr;
538 static int scan_ftp_initialized = 0;
539 static regex_t scan_ftp_integer;
540 static regex_t scan_ftp_time;
541 static regex_t scan_ftp_dostime;
542 static regex_t scan_ftp_dosdate;
543
544 if (!scan_ftp_initialized) {
545 scan_ftp_initialized = 1;
546 regcomp(&scan_ftp_integer, "^[0123456789]+$", REG_EXTENDED | REG_NOSUB);
547 regcomp(&scan_ftp_time, "^[0123456789:]+$", REG_EXTENDED | REG_NOSUB);
548 regcomp(&scan_ftp_dosdate, "^[0123456789]+-[0123456789]+-[0123456789]+$", REG_EXTENDED | REG_NOSUB);
549 regcomp(&scan_ftp_dostime, "^[0123456789]+:[0123456789]+[AP]M$", REG_EXTENDED | REG_NOSUB | REG_ICASE);
550 }
551
552 if (buf == nullptr)
553 return nullptr;
554
555 if (*buf == '\0')
556 return nullptr;
557
558 p = (ftpListParts *)xcalloc(1, sizeof(ftpListParts));
559
560 n_tokens = 0;
561
562 xbuf = xstrdup(buf);
563
564 if (flags.tried_nlst) {
565 /* Machine readable format, one name per line */
566 p->name = xbuf;
567 p->type = '\0';
568 return p;
569 }
570
571 for (t = strtok(xbuf, w_space); t && n_tokens < MAX_TOKENS; t = strtok(nullptr, w_space)) {
572 tokens[n_tokens].token = xstrdup(t);
573 tokens[n_tokens].pos = t - xbuf;
574 ++n_tokens;
575 }
576
577 xfree(xbuf);
578
579 /* locate the Month field */
580 for (i = 3; i < n_tokens - 2; ++i) {
581 const auto size = tokens[i - 1].token;
582 char *month = tokens[i].token;
583 char *day = tokens[i + 1].token;
584 char *year = tokens[i + 2].token;
585
586 if (!is_month(month))
587 continue;
588
589 if (regexec(&scan_ftp_integer, size, 0, nullptr, 0) != 0)
590 continue;
591
592 if (regexec(&scan_ftp_integer, day, 0, nullptr, 0) != 0)
593 continue;
594
595 if (regexec(&scan_ftp_time, year, 0, nullptr, 0) != 0) /* Yr | hh:mm */
596 continue;
597
598 const auto *copyFrom = buf + tokens[i].pos;
599
600 // "MMM DD [ YYYY|hh:mm]" with at most two spaces between DD and YYYY
601 auto dateSize = snprintf(tbuf, sizeof(tbuf), "%s %2s %5s", month, day, year);
602 bool isTypeA = (dateSize == 12) && (strncmp(copyFrom, tbuf, dateSize) == 0);
603
604 // "MMM DD [YYYY|hh:mm]" with one space between DD and YYYY
605 dateSize = snprintf(tbuf, sizeof(tbuf), "%s %2s %-5s", month, day, year);
606 bool isTypeB = (dateSize == 12 || dateSize == 11) && (strncmp(copyFrom, tbuf, dateSize) == 0);
607
608 // TODO: replace isTypeA and isTypeB with a regex.
609 if (isTypeA || isTypeB) {
610 p->type = *tokens[0].token;
611 p->size = strtoll(size, nullptr, 10);
612 const auto finalDateSize = snprintf(tbuf, sizeof(tbuf), "%s %2s %5s", month, day, year);
613 assert(finalDateSize >= 0);
614 p->date = xstrdup(tbuf);
615
616 // point after tokens[i+2] :
617 copyFrom = buf + tokens[i + 2].pos + strlen(tokens[i + 2].token);
618 if (flags.skip_whitespace) {
619 while (strchr(w_space, *copyFrom))
620 ++copyFrom;
621 } else {
622 /* Handle the following four formats:
623 * "MMM DD YYYY Name"
624 * "MMM DD YYYYName"
625 * "MMM DD YYYY Name"
626 * "MMM DD YYYY Name"
627 * Assuming a single space between date and filename
628 * suggested by: Nathan.Bailey@cc.monash.edu.au and
629 * Mike Battersby <mike@starbug.bofh.asn.au> */
630 if (strchr(w_space, *copyFrom))
631 ++copyFrom;
632 }
633
634 p->name = xstrdup(copyFrom);
635
636 if (p->type == 'l' && (t = strstr(p->name, " -> "))) {
637 *t = '\0';
638 p->link = xstrdup(t + 4);
639 }
640
641 goto found;
642 }
643
644 break;
645 }
646
647 /* try it as a DOS listing, 04-05-70 09:33PM ... */
648 if (n_tokens > 3 &&
649 regexec(&scan_ftp_dosdate, tokens[0].token, 0, nullptr, 0) == 0 &&
650 regexec(&scan_ftp_dostime, tokens[1].token, 0, nullptr, 0) == 0) {
651 if (!strcasecmp(tokens[2].token, "<dir>")) {
652 p->type = 'd';
653 } else {
654 p->type = '-';
655 p->size = strtoll(tokens[2].token, nullptr, 10);
656 }
657
658 snprintf(tbuf, sizeof(tbuf), "%s %s", tokens[0].token, tokens[1].token);
659 p->date = xstrdup(tbuf);
660
661 if (p->type == 'd') {
662 // Directory.. name begins with first printable after <dir>
663 // Because of the "n_tokens > 3", the next printable after <dir>
664 // is stored at token[3]. No need for more checks here.
665 } else {
666 // A file. Name begins after size, with a space in between.
667 // Also a space should exist before size.
668 // But there is not needed to be very strict with spaces.
669 // The name is stored at token[3], take it from here.
670 }
671
672 p->name = xstrdup(tokens[3].token);
673 goto found;
674 }
675
676 /* Try EPLF format; carson@lehman.com */
677 if (buf[0] == '+') {
678 const char *ct = buf + 1;
679 p->type = 0;
680
681 while (ct && *ct) {
682 time_t tm;
683 int l = strcspn(ct, ",");
684 char *tmp;
685
686 if (l < 1)
687 goto blank;
688
689 switch (*ct) {
690
691 case '\t':
692 p->name = xstrndup(ct + 1, l + 1);
693 break;
694
695 case 's':
696 p->size = atoi(ct + 1);
697 break;
698
699 case 'm':
700 tm = (time_t) strtol(ct + 1, &tmp, 0);
701
702 if (tmp != ct + 1)
703 break; /* not a valid integer */
704
705 p->date = xstrdup(ctime(&tm));
706
707 *(strstr(p->date, "\n")) = '\0';
708
709 break;
710
711 case '/':
712 p->type = 'd';
713
714 break;
715
716 case 'r':
717 p->type = '-';
718
719 break;
720
721 case 'i':
722 break;
723
724 default:
725 break;
726 }
727
728blank:
729 ct = strstr(ct, ",");
730
731 if (ct) {
732 ++ct;
733 }
734 }
735
736 if (p->type == 0) {
737 p->type = '-';
738 }
739
740 if (p->name)
741 goto found;
742 else
743 safe_free(p->date);
744 }
745
746found:
747
748 for (i = 0; i < n_tokens; ++i)
749 xfree(tokens[i].token);
750
751 if (!p->name)
752 ftpListPartsFree(&p); /* cleanup */
753
754 return p;
755}
756
757bool
759{
760 debugs(9, 7, "line={" << line << "}");
761
762 if (strlen(line) > 1024) {
763 html << "<tr><td colspan=\"5\">" << line << "</td></tr>\n";
764 return true;
765 }
766
767 SBuf prefix;
768 if (flags.dir_slash && dirpath && typecode != 'D') {
769 prefix.append(rfc1738_escape_part(dirpath));
770 prefix.append("/", 1);
771 }
772
773 ftpListParts *parts = ftpListParseParts(line, flags);
774 if (!parts) {
775 html << "<tr class=\"entry\"><td colspan=\"5\">" << line << "</td></tr>\n";
776
777 const char *p;
778 for (p = line; *p && xisspace(*p); ++p);
779 if (*p && !xisspace(*p))
780 flags.listformat_unknown = 1;
781
782 return true;
783 }
784
785 if (!strcmp(parts->name, ".") || !strcmp(parts->name, "..")) {
786 ftpListPartsFree(&parts);
787 return false;
788 }
789
790 parts->size += 1023;
791 parts->size >>= 10;
792 parts->showname = xstrdup(parts->name);
793
794 /* {icon} {text} . . . {date}{size}{chdir}{view}{download}{link}\n */
795 SBuf href(prefix);
796 href.append(rfc1738_escape_part(parts->name));
797
798 SBuf text(parts->showname);
799
800 SBuf icon, size, chdir, link;
801 switch (parts->type) {
802
803 case 'd':
804 icon.appendf("<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
805 mimeGetIconURL("internal-dir"),
806 "[DIR]");
807 href.append("/", 1); /* margin is allocated above */
808 break;
809
810 case 'l':
811 icon.appendf("<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
812 mimeGetIconURL("internal-link"),
813 "[LINK]");
814 /* sometimes there is an 'l' flag, but no "->" link */
815
816 if (parts->link) {
817 SBuf link2(html_quote(rfc1738_escape(parts->link)));
818 link.appendf(" -&gt; <a href=\"%s" SQUIDSBUFPH "\">%s</a>",
819 link2[0] != '/' ? prefix.c_str() : "", SQUIDSBUFPRINT(link2),
820 html_quote(parts->link));
821 }
822
823 break;
824
825 case '\0':
826 icon.appendf("<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
827 mimeGetIconURL(parts->name),
828 "[UNKNOWN]");
829 chdir.appendf("<a href=\"%s/;type=d\"><img border=\"0\" src=\"%s\" "
830 "alt=\"[DIR]\"></a>",
832 mimeGetIconURL("internal-dir"));
833 break;
834
835 case '-':
836
837 default:
838 icon.appendf("<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
839 mimeGetIconURL(parts->name),
840 "[FILE]");
841 size.appendf(" %6" PRId64 "k", parts->size);
842 break;
843 }
844
845 SBuf view, download;
846 if (parts->type != 'd') {
847 if (mimeGetViewOption(parts->name)) {
848 view.appendf("<a href=\"" SQUIDSBUFPH ";type=a\"><img border=\"0\" src=\"%s\" "
849 "alt=\"[VIEW]\"></a>",
850 SQUIDSBUFPRINT(href), mimeGetIconURL("internal-view"));
851 }
852
853 if (mimeGetDownloadOption(parts->name)) {
854 download.appendf("<a href=\"" SQUIDSBUFPH ";type=i\"><img border=\"0\" src=\"%s\" "
855 "alt=\"[DOWNLOAD]\"></a>",
856 SQUIDSBUFPRINT(href), mimeGetIconURL("internal-download"));
857 }
858 }
859
860 /* construct the table row from parts. */
861 html << "<tr class=\"entry\">"
862 "<td class=\"icon\"><a href=\"" << href << "\">" << icon << "</a></td>"
863 "<td class=\"filename\"><a href=\"" << href << "\">" << html_quote(text.c_str()) << "</a></td>"
864 "<td class=\"date\">" << parts->date << "</td>"
865 "<td class=\"size\">" << size << "</td>"
866 "<td class=\"actions\">" << chdir << view << download << link << "</td>"
867 "</tr>\n";
868
869 ftpListPartsFree(&parts);
870 return true;
871}
872
873void
875{
876 char *buf = data.readBuf->content();
877 char *sbuf; /* NULL-terminated copy of termedBuf */
878 char *end;
879 char *line;
880 char *s;
881 size_t linelen;
882 size_t usable;
883 size_t len = data.readBuf->contentSize();
884
885 if (!len) {
886 debugs(9, 3, "no content to parse for " << entry->url() );
887 return;
888 }
889
890 /*
891 * We need a NULL-terminated buffer for scanning, ick
892 */
893 sbuf = (char *)xmalloc(len + 1);
894 xstrncpy(sbuf, buf, len + 1);
895 end = sbuf + len - 1;
896
897 while (*end != '\r' && *end != '\n' && end > sbuf)
898 --end;
899
900 usable = end - sbuf;
901
902 debugs(9, 3, "usable = " << usable << " of " << len << " bytes.");
903
904 if (usable == 0) {
905 if (buf[0] == '\0' && len == 1) {
906 debugs(9, 3, "NIL ends data from " << entry->url() << " transfer problem?");
907 data.readBuf->consume(len);
908 } else {
909 debugs(9, 3, "didn't find end for " << entry->url());
910 debugs(9, 3, "buffer remains (" << len << " bytes) '" << rfc1738_do_escape(buf,0) << "'");
911 }
912 xfree(sbuf);
913 return;
914 }
915
916 debugs(9, 3, (unsigned long int)len << " bytes to play with");
917
918 line = (char *)memAllocate(MEM_4K_BUF);
919 ++end;
920 s = sbuf;
921 s += strspn(s, crlf);
922
923 for (; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
924 debugs(9, 7, "s = {" << s << "}");
925 linelen = strcspn(s, crlf) + 1;
926
927 if (linelen < 2)
928 break;
929
930 if (linelen > 4096)
931 linelen = 4096;
932
933 xstrncpy(line, s, linelen);
934
935 debugs(9, 7, "{" << line << "}");
936
937 if (!strncmp(line, "total", 5))
938 continue;
939
940 MemBuf htmlPage;
941 htmlPage.init();
942 PackableStream html(htmlPage);
943
944 if (htmlifyListEntry(line, html)) {
945 html.flush();
946 debugs(9, 7, "listing append: t = {" << htmlPage.contentSize() << ", '" << htmlPage.content() << "'}");
947 listing.append(htmlPage.content(), htmlPage.contentSize());
948 }
949 }
950
951 debugs(9, 7, "Done.");
952 data.readBuf->consume(usable);
953 memFree(line, MEM_4K_BUF);
954 xfree(sbuf);
955}
956
957void
959{
960 debugs(9, 3, status());
961
962 if (request->method == Http::METHOD_HEAD && (flags.isdir || theSize != -1)) {
963 serverComplete();
964 return;
965 }
966
967 /* Directory listings are special. They write ther own headers via the error objects */
968 if (!flags.http_header_sent && data.readBuf->contentSize() >= 0 && !flags.isdir)
969 appendSuccessHeader();
970
971 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
972 /*
973 * probably was aborted because content length exceeds one
974 * of the maximum size limits.
975 */
976 abortAll("entry aborted after calling appendSuccessHeader()");
977 return;
978 }
979
980#if USE_ADAPTATION
981
982 if (adaptationAccessCheckPending) {
983 debugs(9, 3, "returning from Ftp::Gateway::processReplyBody due to adaptationAccessCheckPending");
984 return;
985 }
986
987#endif
988
989 if (flags.isdir) {
990 if (!flags.listing) {
991 flags.listing = 1;
992 listing.reset();
993 }
994 parseListing();
995 maybeReadVirginBody();
996 return;
997 } else if (const int csize = data.readBuf->contentSize()) {
998 writeReplyBody(data.readBuf->content(), csize);
999 debugs(9, 5, "consuming " << csize << " bytes of readBuf");
1000 data.readBuf->consume(csize);
1001 }
1002
1003 entry->flush();
1004
1005 maybeReadVirginBody();
1006}
1007
1024int
1026{
1027 /* default username */
1028 xstrncpy(user, "anonymous", MAX_URL);
1029
1030#if HAVE_AUTH_MODULE_BASIC
1031 /* Check HTTP Authorization: headers (better than defaults, but less than URL) */
1032 const auto auth(req_hdr->getAuthToken(Http::HdrType::AUTHORIZATION, "Basic"));
1033 if (!auth.isEmpty()) {
1034 flags.authenticated = 1;
1035 loginParser(auth, false);
1036 }
1037 /* we fail with authorization-required error later IFF the FTP server requests it */
1038#else
1039 (void)req_hdr;
1040#endif
1041
1042 /* Test URL login syntax. Overrides any headers received. */
1043 loginParser(request->url.userInfo(), true);
1044
1045 /* name is missing. that's fatal. */
1046 if (!user[0])
1047 fatal("FTP login parsing destroyed username info");
1048
1049 /* name + password == success */
1050 if (password[0])
1051 return 1;
1052
1053 /* Setup default FTP password settings */
1054 /* this has to be done last so that we can have a no-password case above. */
1055 if (!password[0]) {
1056 if (strcmp(user, "anonymous") == 0 && !flags.tried_auth_anonymous) {
1057 xstrncpy(password, Config.Ftp.anon_user, MAX_URL);
1058 flags.tried_auth_anonymous=1;
1059 return 1;
1060 } else if (!flags.tried_auth_nopass) {
1061 xstrncpy(password, null_string, MAX_URL);
1062 flags.tried_auth_nopass=1;
1063 return 1;
1064 }
1065 }
1066
1067 return 0; /* different username */
1068}
1069
1070void
1072{
1073 // If typecode was specified, extract it and leave just the filename in
1074 // url.path. Tolerate trailing garbage or missing typecode value. Roughly:
1075 // [filename] ;type=[typecode char] [trailing garbage]
1076 static const SBuf middle(";type=");
1077 const auto typeSpecStart = request->url.path().find(middle);
1078 if (typeSpecStart != SBuf::npos) {
1079 const auto fullPath = request->url.path();
1080 const auto typecodePos = typeSpecStart + middle.length();
1081 typecode = (typecodePos < fullPath.length()) ?
1082 static_cast<char>(xtoupper(fullPath[typecodePos])) : '\0';
1083 request->url.path(fullPath.substr(0, typeSpecStart));
1084 }
1085
1086 int l = request->url.path().length();
1087 /* check for null path */
1088
1089 if (!l) {
1090 flags.isdir = 1;
1091 flags.root_dir = 1;
1092 flags.need_base_href = 1; /* Work around broken browsers */
1093 } else if (!request->url.path().cmp("/%2f/")) {
1094 /* UNIX root directory */
1095 flags.isdir = 1;
1096 flags.root_dir = 1;
1097 } else if ((l >= 1) && (request->url.path()[l-1] == '/')) {
1098 /* Directory URL, ending in / */
1099 flags.isdir = 1;
1100
1101 if (l == 1)
1102 flags.root_dir = 1;
1103 } else {
1104 flags.dir_slash = 1;
1105 }
1106}
1107
1108void
1110{
1111 title_url = "ftp://";
1112
1113 if (strcmp(user, "anonymous")) {
1114 title_url.append(user);
1115 title_url.append("@");
1116 }
1117
1118 SBuf authority = request->url.authority(request->url.getScheme() != AnyP::PROTO_FTP);
1119
1120 title_url.append(authority.rawContent(), authority.length());
1121 title_url.append(request->url.path().rawContent(), request->url.path().length());
1122
1123 base_href = "ftp://";
1124
1125 if (strcmp(user, "anonymous") != 0) {
1126 base_href.append(rfc1738_escape_part(user));
1127
1128 if (password_url) {
1129 base_href.append(":");
1130 base_href.append(rfc1738_escape_part(password));
1131 }
1132
1133 base_href.append("@");
1134 }
1135
1136 base_href.append(authority.rawContent(), authority.length());
1137 base_href.append(request->url.path().rawContent(), request->url.path().length());
1138 base_href.append("/");
1139}
1140
1141void
1143{
1144 if (!checkAuth(&request->header)) {
1145 /* create appropriate reply */
1146 SBuf realm(ftpRealm()); // local copy so SBuf will not disappear too early
1147 const auto reply = ftpAuthRequired(request.getRaw(), realm, fwd->al);
1148 entry->replaceHttpReply(reply);
1149 serverComplete();
1150 return;
1151 }
1152
1153 checkUrlpath();
1154 buildTitleUrl();
1155 debugs(9, 5, "FD " << (ctrl.conn ? ctrl.conn->fd : -1) << " : host=" << request->url.host() <<
1156 ", path=" << request->url.path() << ", user=" << user << ", passwd=" << password);
1157 state = BEGIN;
1159}
1160
1161/* ====================================================================== */
1162
1163void
1165{
1167 if (ctrl.message == nullptr)
1168 return; // didn't get complete reply yet
1169
1170 /* Copy the message except for the last line to cwd_message to be
1171 * printed in error messages.
1172 */
1173 for (wordlist *w = ctrl.message; w && w->next; w = w->next) {
1174 cwd_message.append('\n');
1175 cwd_message.append(w->key);
1176 }
1177
1178 FTP_SM_FUNCS[state] (this);
1179}
1180
1181/* ====================================================================== */
1182
1183static void
1185{
1186 int code = ftpState->ctrl.replycode;
1187 debugs(9, 3, MYNAME);
1188
1189 if (ftpState->flags.pasv_only)
1190 ++ ftpState->login_att;
1191
1192 if (code == 220) {
1193 if (ftpState->ctrl.message) {
1194 if (strstr(ftpState->ctrl.message->key, "NetWare"))
1195 ftpState->flags.skip_whitespace = 1;
1196 }
1197
1198 ftpSendUser(ftpState);
1199 } else if (code == 120) {
1200 if (nullptr != ftpState->ctrl.message)
1201 debugs(9, DBG_IMPORTANT, "FTP server is busy: " << ftpState->ctrl.message->key);
1202
1203 return;
1204 } else {
1205 ftpFail(ftpState);
1206 }
1207}
1208
1214void
1216{
1217 ErrorState *err = nullptr;
1218
1219 if ((state == SENT_USER || state == SENT_PASS) && ctrl.replycode >= 400) {
1220 if (ctrl.replycode == 421 || ctrl.replycode == 426) {
1221 // 421/426 - Service Overload - retry permitted.
1222 err = new ErrorState(ERR_FTP_UNAVAILABLE, Http::scServiceUnavailable, fwd->request, fwd->al);
1223 } else if (ctrl.replycode >= 430 && ctrl.replycode <= 439) {
1224 // 43x - Invalid or Credential Error - retry challenge required.
1225 err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scUnauthorized, fwd->request, fwd->al);
1226 } else if (ctrl.replycode >= 530 && ctrl.replycode <= 539) {
1227 // 53x - Credentials Missing - retry challenge required
1228 if (password_url) // but they were in the URI! major fail.
1229 err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scForbidden, fwd->request, fwd->al);
1230 else
1231 err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scUnauthorized, fwd->request, fwd->al);
1232 }
1233 }
1234
1235 if (!err) {
1236 ftpFail(this);
1237 return;
1238 }
1239
1240 failed(ERR_NONE, ctrl.replycode, err);
1241 // any other problems are general falures.
1242
1243 HttpReply *newrep = err->BuildHttpReply();
1244 delete err;
1245
1246#if HAVE_AUTH_MODULE_BASIC
1247 /* add Authenticate header */
1248 // XXX: performance regression. c_str() may reallocate
1249 SBuf realm(ftpRealm()); // local copy so SBuf will not disappear too early
1250 newrep->header.putAuth("Basic", realm.c_str());
1251#endif
1252
1253 // add it to the store entry for response....
1254 entry->replaceHttpReply(newrep);
1255 serverComplete();
1256}
1257
1258SBuf
1260{
1261 SBuf realm;
1262
1263 /* This request is not fully authenticated */
1264 realm.appendf("FTP %s ", user);
1265 if (!request)
1266 realm.append("unknown", 7);
1267 else {
1268 realm.append(request->url.host());
1269 const auto &rport = request->url.port();
1270 if (rport && *rport != 21)
1271 realm.appendf(" port %hu", *rport);
1272 }
1273 return realm;
1274}
1275
1276static void
1278{
1279 /* check the server control channel is still available */
1280 if (!ftpState || !ftpState->haveControlChannel("ftpSendUser"))
1281 return;
1282
1283 if (ftpState->proxy_host != nullptr)
1284 snprintf(cbuf, CTRL_BUFLEN, "USER %s@%s\r\n", ftpState->user, ftpState->request->url.host());
1285 else
1286 snprintf(cbuf, CTRL_BUFLEN, "USER %s\r\n", ftpState->user);
1287
1288 ftpState->writeCommand(cbuf);
1289
1290 ftpState->state = Ftp::Client::SENT_USER;
1291}
1292
1293static void
1295{
1296 int code = ftpState->ctrl.replycode;
1297 debugs(9, 3, MYNAME);
1298
1299 if (code == 230) {
1300 ftpReadPass(ftpState);
1301 } else if (code == 331) {
1302 ftpSendPass(ftpState);
1303 } else {
1304 ftpState->loginFailed();
1305 }
1306}
1307
1308static void
1310{
1311 /* check the server control channel is still available */
1312 if (!ftpState || !ftpState->haveControlChannel("ftpSendPass"))
1313 return;
1314
1315 snprintf(cbuf, CTRL_BUFLEN, "PASS %s\r\n", ftpState->password);
1316 ftpState->writeCommand(cbuf);
1317 ftpState->state = Ftp::Client::SENT_PASS;
1318}
1319
1320static void
1322{
1323 int code = ftpState->ctrl.replycode;
1324 debugs(9, 3, "code=" << code);
1325
1326 if (code == 230) {
1327 ftpSendType(ftpState);
1328 } else {
1329 ftpState->loginFailed();
1330 }
1331}
1332
1333static void
1335{
1336 /* check the server control channel is still available */
1337 if (!ftpState || !ftpState->haveControlChannel("ftpSendType"))
1338 return;
1339
1340 /*
1341 * Ref section 3.2.2 of RFC 1738
1342 */
1343 char mode = ftpState->typecode;
1344
1345 switch (mode) {
1346
1347 case 'D':
1348 mode = 'A';
1349 break;
1350
1351 case 'A':
1352
1353 case 'I':
1354 break;
1355
1356 default:
1357
1358 if (ftpState->flags.isdir) {
1359 mode = 'A';
1360 } else {
1361 auto t = ftpState->request->url.path().rfind('/');
1362 // XXX: performance regression, c_str() may reallocate
1363 SBuf filename = ftpState->request->url.path().substr(t != SBuf::npos ? t + 1 : 0);
1364 mode = mimeGetTransferMode(filename.c_str());
1365 }
1366
1367 break;
1368 }
1369
1370 if (mode == 'I')
1371 ftpState->flags.binary = 1;
1372 else
1373 ftpState->flags.binary = 0;
1374
1375 snprintf(cbuf, CTRL_BUFLEN, "TYPE %c\r\n", mode);
1376
1377 ftpState->writeCommand(cbuf);
1378
1379 ftpState->state = Ftp::Client::SENT_TYPE;
1380}
1381
1382static void
1384{
1385 int code = ftpState->ctrl.replycode;
1386 char *path;
1387 char *d, *p;
1388 debugs(9, 3, "code=" << code);
1389
1390 if (code == 200) {
1391 p = path = SBufToCstring(ftpState->request->url.path());
1392
1393 if (*p == '/')
1394 ++p;
1395
1396 while (*p) {
1397 d = p;
1398 p += strcspn(p, "/");
1399
1400 if (*p) {
1401 *p = '\0';
1402 ++p;
1403 }
1404
1406
1407 if (*d)
1408 wordlistAdd(&ftpState->pathcomps, d);
1409 }
1410
1411 xfree(path);
1412
1413 if (ftpState->pathcomps)
1414 ftpTraverseDirectory(ftpState);
1415 else
1416 ftpListDir(ftpState);
1417 } else {
1418 ftpFail(ftpState);
1419 }
1420}
1421
1422static void
1424{
1425 debugs(9, 4, (ftpState->filepath ? ftpState->filepath : "<NULL>"));
1426
1427 safe_free(ftpState->dirpath);
1428 ftpState->dirpath = ftpState->filepath;
1429 ftpState->filepath = nullptr;
1430
1431 /* Done? */
1432
1433 if (ftpState->pathcomps == nullptr) {
1434 debugs(9, 3, "the final component was a directory");
1435 ftpListDir(ftpState);
1436 return;
1437 }
1438
1439 /* Go to next path component */
1440 ftpState->filepath = wordlistChopHead(& ftpState->pathcomps);
1441
1442 /* Check if we are to CWD or RETR */
1443 if (ftpState->pathcomps != nullptr || ftpState->flags.isdir) {
1444 ftpSendCwd(ftpState);
1445 } else {
1446 debugs(9, 3, "final component is probably a file");
1447 ftpGetFile(ftpState);
1448 return;
1449 }
1450}
1451
1452static void
1454{
1455 char *path = nullptr;
1456
1457 /* check the server control channel is still available */
1458 if (!ftpState || !ftpState->haveControlChannel("ftpSendCwd"))
1459 return;
1460
1461 debugs(9, 3, MYNAME);
1462
1463 path = ftpState->filepath;
1464
1465 if (!strcmp(path, "..") || !strcmp(path, "/")) {
1466 ftpState->flags.no_dotdot = 1;
1467 } else {
1468 ftpState->flags.no_dotdot = 0;
1469 }
1470
1471 snprintf(cbuf, CTRL_BUFLEN, "CWD %s\r\n", path);
1472
1473 ftpState->writeCommand(cbuf);
1474
1475 ftpState->state = Ftp::Client::SENT_CWD;
1476}
1477
1478static void
1480{
1481 int code = ftpState->ctrl.replycode;
1482 debugs(9, 3, MYNAME);
1483
1484 if (code >= 200 && code < 300) {
1485 /* CWD OK */
1486 ftpState->unhack();
1487
1488 /* Reset cwd_message to only include the last message */
1489 ftpState->cwd_message.reset("");
1490 for (wordlist *w = ftpState->ctrl.message; w; w = w->next) {
1491 ftpState->cwd_message.append('\n');
1492 ftpState->cwd_message.append(w->key);
1493 }
1494 ftpState->ctrl.message = nullptr;
1495
1496 /* Continue to traverse the path */
1497 ftpTraverseDirectory(ftpState);
1498 } else {
1499 /* CWD FAILED */
1500
1501 if (!ftpState->flags.put)
1502 ftpFail(ftpState);
1503 else
1504 ftpSendMkdir(ftpState);
1505 }
1506}
1507
1508static void
1510{
1511 char *path = nullptr;
1512
1513 /* check the server control channel is still available */
1514 if (!ftpState || !ftpState->haveControlChannel("ftpSendMkdir"))
1515 return;
1516
1517 path = ftpState->filepath;
1518 debugs(9, 3, "with path=" << path);
1519 snprintf(cbuf, CTRL_BUFLEN, "MKD %s\r\n", path);
1520 ftpState->writeCommand(cbuf);
1521 ftpState->state = Ftp::Client::SENT_MKDIR;
1522}
1523
1524static void
1526{
1527 char *path = ftpState->filepath;
1528 int code = ftpState->ctrl.replycode;
1529
1530 debugs(9, 3, "path " << path << ", code " << code);
1531
1532 if (code == 257) { /* success */
1533 ftpSendCwd(ftpState);
1534 } else if (code == 550) { /* dir exists */
1535
1536 if (ftpState->flags.put_mkdir) {
1537 ftpState->flags.put_mkdir = 1;
1538 ftpSendCwd(ftpState);
1539 } else
1540 ftpSendReply(ftpState);
1541 } else
1542 ftpSendReply(ftpState);
1543}
1544
1545static void
1547{
1548 assert(*ftpState->filepath != '\0');
1549 ftpState->flags.isdir = 0;
1550 ftpSendMdtm(ftpState);
1551}
1552
1553static void
1555{
1556 if (ftpState->flags.dir_slash) {
1557 debugs(9, 3, "Directory path did not end in /");
1558 ftpState->title_url.append("/");
1559 ftpState->flags.isdir = 1;
1560 }
1561
1562 ftpSendPassive(ftpState);
1563}
1564
1565static void
1567{
1568 /* check the server control channel is still available */
1569 if (!ftpState || !ftpState->haveControlChannel("ftpSendMdtm"))
1570 return;
1571
1572 assert(*ftpState->filepath != '\0');
1573 snprintf(cbuf, CTRL_BUFLEN, "MDTM %s\r\n", ftpState->filepath);
1574 ftpState->writeCommand(cbuf);
1575 ftpState->state = Ftp::Client::SENT_MDTM;
1576}
1577
1578static void
1580{
1581 int code = ftpState->ctrl.replycode;
1582 debugs(9, 3, MYNAME);
1583
1584 if (code == 213) {
1585 ftpState->mdtm = Time::ParseIso3307(ftpState->ctrl.last_reply);
1586 ftpState->unhack();
1587 } else if (code < 0) {
1588 ftpFail(ftpState);
1589 return;
1590 }
1591
1592 ftpSendSize(ftpState);
1593}
1594
1595static void
1597{
1598 /* check the server control channel is still available */
1599 if (!ftpState || !ftpState->haveControlChannel("ftpSendSize"))
1600 return;
1601
1602 /* Only send SIZE for binary transfers. The returned size
1603 * is useless on ASCII transfers */
1604
1605 if (ftpState->flags.binary) {
1606 assert(ftpState->filepath != nullptr);
1607 assert(*ftpState->filepath != '\0');
1608 snprintf(cbuf, CTRL_BUFLEN, "SIZE %s\r\n", ftpState->filepath);
1609 ftpState->writeCommand(cbuf);
1610 ftpState->state = Ftp::Client::SENT_SIZE;
1611 } else
1612 /* Skip to next state no non-binary transfers */
1613 ftpSendPassive(ftpState);
1614}
1615
1616static void
1618{
1619 int code = ftpState->ctrl.replycode;
1620 debugs(9, 3, MYNAME);
1621
1622 if (code == 213) {
1623 ftpState->unhack();
1624 ftpState->theSize = strtoll(ftpState->ctrl.last_reply, nullptr, 10);
1625
1626 if (ftpState->theSize == 0) {
1627 debugs(9, 2, "SIZE reported " <<
1628 ftpState->ctrl.last_reply << " on " <<
1629 ftpState->title_url);
1630 ftpState->theSize = -1;
1631 }
1632 } else if (code < 0) {
1633 ftpFail(ftpState);
1634 return;
1635 }
1636
1637 ftpSendPassive(ftpState);
1638}
1639
1640static void
1642{
1643 Ip::Address srvAddr; // unused
1644 if (ftpState->handleEpsvReply(srvAddr)) {
1645 if (ftpState->ctrl.message == nullptr)
1646 return; // didn't get complete reply yet
1647
1648 ftpState->connectDataChannel();
1649 }
1650}
1651
1656static void
1658{
1660 if (!ftpState || !ftpState->haveControlChannel("ftpSendPassive"))
1661 return;
1662
1663 debugs(9, 3, MYNAME);
1664
1667 if (ftpState->request->method == Http::METHOD_HEAD && (ftpState->flags.isdir || ftpState->theSize != -1)) {
1668 ftpState->processHeadResponse(); // may call serverComplete
1669 return;
1670 }
1671
1672 if (ftpState->sendPassive()) {
1673 // SENT_EPSV_ALL blocks other non-EPSV connections being attempted
1674 if (ftpState->state == Ftp::Client::SENT_EPSV_ALL)
1675 ftpState->flags.epsv_all_sent = true;
1676 }
1677}
1678
1679void
1681{
1682 debugs(9, 5, "handling HEAD response");
1683 ftpSendQuit(this);
1684 appendSuccessHeader();
1685
1686 /*
1687 * On rare occasions I'm seeing the entry get aborted after
1688 * readControlReply() and before here, probably when
1689 * trying to write to the client.
1690 */
1691 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1692 abortAll("entry aborted while processing HEAD");
1693 return;
1694 }
1695
1696#if USE_ADAPTATION
1697 if (adaptationAccessCheckPending) {
1698 debugs(9,3, "returning due to adaptationAccessCheckPending");
1699 return;
1700 }
1701#endif
1702
1703 // processReplyBody calls serverComplete() since there is no body
1704 processReplyBody();
1705}
1706
1707static void
1709{
1710 Ip::Address srvAddr; // unused
1711 if (ftpState->handlePasvReply(srvAddr))
1712 ftpState->connectDataChannel();
1713 else {
1714 ftpFail(ftpState);
1715 // Currently disabled, does not work correctly:
1716 // ftpSendEPRT(ftpState);
1717 return;
1718 }
1719}
1720
1721void
1723{
1724 debugs(9, 3, MYNAME);
1725 dataConnWait.finish();
1726
1727 if (io.flag != Comm::OK) {
1728 debugs(9, 2, "Failed to connect. Retrying via another method.");
1729
1730 // ABORT on timeouts. server may be waiting on a broken TCP link.
1731 if (io.xerrno == Comm::TIMEOUT)
1732 writeCommand("ABOR\r\n");
1733
1734 // try another connection attempt with some other method
1735 ftpSendPassive(this);
1736 return;
1737 }
1738
1739 data.opened(io.conn, dataCloser());
1740 ftpRestOrList(this);
1741}
1742
1743static void
1744ftpOpenListenSocket(Ftp::Gateway * ftpState, int fallback)
1745{
1747 if (ftpState->data.conn != nullptr) {
1748 if ((ftpState->data.conn->flags & COMM_REUSEADDR))
1749 // NP: in fact it points to the control channel. just clear it.
1750 ftpState->data.clear();
1751 else
1752 ftpState->data.close();
1753 }
1754 safe_free(ftpState->data.host);
1755
1756 if (!Comm::IsConnOpen(ftpState->ctrl.conn)) {
1757 debugs(9, 5, "The control connection to the remote end is closed");
1758 return;
1759 }
1760
1761 /*
1762 * Set up a listen socket on the same local address as the
1763 * control connection.
1764 */
1766 temp->local = ftpState->ctrl.conn->local;
1767
1768 /*
1769 * REUSEADDR is needed in fallback mode, since the same port is
1770 * used for both control and data.
1771 */
1772 if (fallback) {
1773 int on = 1;
1774 errno = 0;
1775 if (setsockopt(ftpState->ctrl.conn->fd, SOL_SOCKET, SO_REUSEADDR,
1776 (char *) &on, sizeof(on)) == -1) {
1777 int xerrno = errno;
1778 // SO_REUSEADDR is only an optimization, no need to be verbose about error
1779 debugs(9, 4, "setsockopt failed: " << xstrerr(xerrno));
1780 }
1781 ftpState->ctrl.conn->flags |= COMM_REUSEADDR;
1782 temp->flags |= COMM_REUSEADDR;
1783 } else {
1784 /* if not running in fallback mode a new port needs to be retrieved */
1785 temp->local.port(0);
1786 }
1787
1788 ftpState->listenForDataChannel(temp);
1789}
1790
1791static void
1793{
1794 /* check the server control channel is still available */
1795 if (!ftpState || !ftpState->haveControlChannel("ftpSendPort"))
1796 return;
1797
1798 if (Config.Ftp.epsv_all && ftpState->flags.epsv_all_sent) {
1799 debugs(9, DBG_IMPORTANT, "FTP does not allow PORT method after 'EPSV ALL' has been sent.");
1800 return;
1801 }
1802
1803 debugs(9, 3, MYNAME);
1804 ftpState->flags.pasv_supported = 0;
1805 ftpOpenListenSocket(ftpState, 0);
1806
1807 if (!Comm::IsConnOpen(ftpState->data.listenConn)) {
1808 if ( ftpState->data.listenConn != nullptr && !ftpState->data.listenConn->local.isIPv4() ) {
1809 /* non-IPv4 CANNOT send PORT command. */
1810 /* we got here by attempting and failing an EPRT */
1811 /* using the same reply code should simulate a PORT failure */
1812 ftpReadPORT(ftpState);
1813 return;
1814 }
1815
1816 /* XXX Need to set error message */
1817 ftpFail(ftpState);
1818 return;
1819 }
1820
1821 // pull out the internal IP address bytes to send in PORT command...
1822 // source them from the listen_conn->local
1823
1824 struct addrinfo *AI = nullptr;
1825 ftpState->data.listenConn->local.getAddrInfo(AI, AF_INET);
1826 unsigned char *addrptr = (unsigned char *) &((struct sockaddr_in*)AI->ai_addr)->sin_addr;
1827 unsigned char *portptr = (unsigned char *) &((struct sockaddr_in*)AI->ai_addr)->sin_port;
1828 snprintf(cbuf, CTRL_BUFLEN, "PORT %d,%d,%d,%d,%d,%d\r\n",
1829 addrptr[0], addrptr[1], addrptr[2], addrptr[3],
1830 portptr[0], portptr[1]);
1831 ftpState->writeCommand(cbuf);
1832 ftpState->state = Ftp::Client::SENT_PORT;
1833
1835}
1836
1837static void
1839{
1840 int code = ftpState->ctrl.replycode;
1841 debugs(9, 3, MYNAME);
1842
1843 if (code != 200) {
1844 /* Fall back on using the same port as the control connection */
1845 debugs(9, 3, "PORT not supported by remote end");
1846 ftpOpenListenSocket(ftpState, 1);
1847 }
1848
1849 ftpRestOrList(ftpState);
1850}
1851
1852static void
1854{
1855 int code = ftpState->ctrl.replycode;
1856 debugs(9, 3, MYNAME);
1857
1858 if (code != 200) {
1859 /* Failover to attempting old PORT command. */
1860 debugs(9, 3, "EPRT not supported by remote end");
1861 ftpSendPORT(ftpState);
1862 return;
1863 }
1864
1865 ftpRestOrList(ftpState);
1866}
1867
1872void
1874{
1875 debugs(9, 3, MYNAME);
1876
1877 if (!Comm::IsConnOpen(ctrl.conn)) { /*Close handlers will cleanup*/
1878 debugs(9, 5, "The control connection to the remote end is closed");
1879 return;
1880 }
1881
1882 if (io.flag != Comm::OK) {
1883 data.listenConn->close();
1884 data.listenConn = nullptr;
1885 debugs(9, DBG_IMPORTANT, "FTP AcceptDataConnection: " << io.conn << ": " << xstrerr(io.xerrno));
1886 // TODO: need to send error message on control channel
1887 ftpFail(this);
1888 return;
1889 }
1890
1891 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1892 abortAll("entry aborted when accepting data conn");
1893 data.listenConn->close();
1894 data.listenConn = nullptr;
1895 io.conn->close();
1896 return;
1897 }
1898
1899 /* data listening conn is no longer even open. abort. */
1900 if (!Comm::IsConnOpen(data.listenConn)) {
1901 data.listenConn = nullptr; // ensure that it's cleared and not just closed.
1902 return;
1903 }
1904
1905 /* data listening conn is no longer even open. abort. */
1906 if (!Comm::IsConnOpen(data.conn)) {
1907 data.clear(); // ensure that it's cleared and not just closed.
1908 return;
1909 }
1910
1917 if (Config.Ftp.sanitycheck) {
1918 // accept if either our data or ctrl connection is talking to this remote peer.
1919 if (data.conn->remote != io.conn->remote && ctrl.conn->remote != io.conn->remote) {
1921 "ERROR: FTP data connection from unexpected server (" <<
1922 io.conn->remote << "), expecting " <<
1923 data.conn->remote << " or " << ctrl.conn->remote);
1924
1925 /* close the bad sources connection down ASAP. */
1926 io.conn->close();
1927
1928 /* drop the bad connection (io) by ignoring the attempt. */
1929 return;
1930 }
1931 }
1932
1934 data.close();
1935 data.opened(io.conn, dataCloser());
1936 data.addr(io.conn->remote);
1937
1938 debugs(9, 3, "Connected data socket on " <<
1939 io.conn << ". FD table says: " <<
1940 "ctrl-peer= " << fd_table[ctrl.conn->fd].ipaddr << ", " <<
1941 "data-peer= " << fd_table[data.conn->fd].ipaddr);
1942
1943 assert(haveControlChannel("ftpAcceptDataConnection"));
1944 assert(ctrl.message == nullptr);
1945
1946 // Ctrl channel operations will determine what happens to this data connection
1947}
1948
1949static void
1951{
1952 debugs(9, 3, MYNAME);
1953
1954 if (ftpState->typecode == 'D') {
1955 ftpState->flags.isdir = 1;
1956
1957 if (ftpState->flags.put) {
1958 ftpSendMkdir(ftpState); /* PUT name;type=d */
1959 } else {
1960 ftpSendNlst(ftpState); /* GET name;type=d sec 3.2.2 of RFC 1738 */
1961 }
1962 } else if (ftpState->flags.put) {
1963 ftpSendStor(ftpState);
1964 } else if (ftpState->flags.isdir)
1965 ftpSendList(ftpState);
1966 else if (ftpState->restartable())
1967 ftpSendRest(ftpState);
1968 else
1969 ftpSendRetr(ftpState);
1970}
1971
1972static void
1974{
1975 /* check the server control channel is still available */
1976 if (!ftpState || !ftpState->haveControlChannel("ftpSendStor"))
1977 return;
1978
1979 debugs(9, 3, MYNAME);
1980
1981 if (ftpState->filepath != nullptr) {
1982 /* Plain file upload */
1983 snprintf(cbuf, CTRL_BUFLEN, "STOR %s\r\n", ftpState->filepath);
1984 ftpState->writeCommand(cbuf);
1985 ftpState->state = Ftp::Client::SENT_STOR;
1986 } else if (ftpState->request->header.getInt64(Http::HdrType::CONTENT_LENGTH) > 0) {
1987 /* File upload without a filename. use STOU to generate one */
1988 snprintf(cbuf, CTRL_BUFLEN, "STOU\r\n");
1989 ftpState->writeCommand(cbuf);
1990 ftpState->state = Ftp::Client::SENT_STOR;
1991 } else {
1992 /* No file to transfer. Only create directories if needed */
1993 ftpSendReply(ftpState);
1994 }
1995}
1996
1998static void
2000{
2001 ftpState->readStor();
2002}
2003
2005{
2006 int code = ctrl.replycode;
2007 debugs(9, 3, MYNAME);
2008
2009 if (code == 125 || (code == 150 && Comm::IsConnOpen(data.conn))) {
2010 if (!originalRequest()->body_pipe) {
2011 debugs(9, 3, "zero-size STOR?");
2012 state = WRITING_DATA; // make ftpWriteTransferDone() responsible
2013 dataComplete(); // XXX: keep in sync with doneSendingRequestBody()
2014 return;
2015 }
2016
2017 if (!startRequestBodyFlow()) { // register to receive body data
2018 ftpFail(this);
2019 return;
2020 }
2021
2022 /* When client status is 125, or 150 and the data connection is open, Begin data transfer. */
2023 debugs(9, 3, "starting data transfer");
2024 switchTimeoutToDataChannel();
2025 sendMoreRequestBody();
2026 fwd->dontRetry(true); // do not permit re-trying if the body was sent.
2027 state = WRITING_DATA;
2028 debugs(9, 3, "writing data channel");
2029 } else if (code == 150) {
2030 /* When client code is 150 with no data channel, Accept data channel. */
2031 debugs(9, 3, "ftpReadStor: accepting data channel");
2032 listenForDataChannel(data.conn);
2033 } else {
2034 debugs(9, DBG_IMPORTANT, "ERROR: Unexpected reply code "<< std::setfill('0') << std::setw(3) << code);
2035 ftpFail(this);
2036 }
2037}
2038
2039static void
2041{
2042 /* check the server control channel is still available */
2043 if (!ftpState || !ftpState->haveControlChannel("ftpSendRest"))
2044 return;
2045
2046 debugs(9, 3, MYNAME);
2047
2048 snprintf(cbuf, CTRL_BUFLEN, "REST %" PRId64 "\r\n", ftpState->restart_offset);
2049 ftpState->writeCommand(cbuf);
2050 ftpState->state = Ftp::Client::SENT_REST;
2051}
2052
2053int
2055{
2056 if (restart_offset > 0)
2057 return 1;
2058
2059 if (!request->range)
2060 return 0;
2061
2062 if (!flags.binary)
2063 return 0;
2064
2065 if (theSize <= 0)
2066 return 0;
2067
2068 int64_t desired_offset = request->range->lowestOffset(theSize);
2069
2070 if (desired_offset <= 0)
2071 return 0;
2072
2073 if (desired_offset >= theSize)
2074 return 0;
2075
2076 restart_offset = desired_offset;
2077 return 1;
2078}
2079
2080static void
2082{
2083 int code = ftpState->ctrl.replycode;
2084 debugs(9, 3, MYNAME);
2085 assert(ftpState->restart_offset > 0);
2086
2087 if (code == 350) {
2088 ftpState->setCurrentOffset(ftpState->restart_offset);
2089 ftpSendRetr(ftpState);
2090 } else if (code > 0) {
2091 debugs(9, 3, "REST not supported");
2092 ftpState->flags.rest_supported = 0;
2093 ftpSendRetr(ftpState);
2094 } else {
2095 ftpFail(ftpState);
2096 }
2097}
2098
2099static void
2101{
2102 /* check the server control channel is still available */
2103 if (!ftpState || !ftpState->haveControlChannel("ftpSendList"))
2104 return;
2105
2106 debugs(9, 3, MYNAME);
2107
2108 if (ftpState->filepath) {
2109 snprintf(cbuf, CTRL_BUFLEN, "LIST %s\r\n", ftpState->filepath);
2110 } else {
2111 snprintf(cbuf, CTRL_BUFLEN, "LIST\r\n");
2112 }
2113
2114 ftpState->writeCommand(cbuf);
2115 ftpState->state = Ftp::Client::SENT_LIST;
2116}
2117
2118static void
2120{
2121 /* check the server control channel is still available */
2122 if (!ftpState || !ftpState->haveControlChannel("ftpSendNlst"))
2123 return;
2124
2125 debugs(9, 3, MYNAME);
2126
2127 ftpState->flags.tried_nlst = 1;
2128
2129 if (ftpState->filepath) {
2130 snprintf(cbuf, CTRL_BUFLEN, "NLST %s\r\n", ftpState->filepath);
2131 } else {
2132 snprintf(cbuf, CTRL_BUFLEN, "NLST\r\n");
2133 }
2134
2135 ftpState->writeCommand(cbuf);
2136 ftpState->state = Ftp::Client::SENT_NLST;
2137}
2138
2139static void
2141{
2142 int code = ftpState->ctrl.replycode;
2143 debugs(9, 3, MYNAME);
2144
2145 if (code == 125 || (code == 150 && Comm::IsConnOpen(ftpState->data.conn))) {
2146 /* Begin data transfer */
2147 debugs(9, 3, "begin data transfer from " << ftpState->data.conn->remote << " (" << ftpState->data.conn->local << ")");
2148 ftpState->switchTimeoutToDataChannel();
2149 ftpState->maybeReadVirginBody();
2150 ftpState->state = Ftp::Client::READING_DATA;
2151 return;
2152 } else if (code == 150) {
2153 /* Accept data channel */
2154 debugs(9, 3, "accept data channel from " << ftpState->data.conn->remote << " (" << ftpState->data.conn->local << ")");
2155 ftpState->listenForDataChannel(ftpState->data.conn);
2156 return;
2157 } else if (!ftpState->flags.tried_nlst && code > 300) {
2158 ftpSendNlst(ftpState);
2159 } else {
2160 ftpFail(ftpState);
2161 return;
2162 }
2163}
2164
2165static void
2167{
2168 /* check the server control channel is still available */
2169 if (!ftpState || !ftpState->haveControlChannel("ftpSendRetr"))
2170 return;
2171
2172 debugs(9, 3, MYNAME);
2173
2174 assert(ftpState->filepath != nullptr);
2175 snprintf(cbuf, CTRL_BUFLEN, "RETR %s\r\n", ftpState->filepath);
2176 ftpState->writeCommand(cbuf);
2177 ftpState->state = Ftp::Client::SENT_RETR;
2178}
2179
2180static void
2182{
2183 int code = ftpState->ctrl.replycode;
2184 debugs(9, 3, MYNAME);
2185
2186 if (code == 125 || (code == 150 && Comm::IsConnOpen(ftpState->data.conn))) {
2187 /* Begin data transfer */
2188 debugs(9, 3, "begin data transfer from " << ftpState->data.conn->remote << " (" << ftpState->data.conn->local << ")");
2189 ftpState->switchTimeoutToDataChannel();
2190 ftpState->maybeReadVirginBody();
2191 ftpState->state = Ftp::Client::READING_DATA;
2192 } else if (code == 150) {
2193 /* Accept data channel */
2194 ftpState->listenForDataChannel(ftpState->data.conn);
2195 } else if (code >= 300) {
2196 if (!ftpState->flags.try_slash_hack) {
2197 /* Try this as a directory missing trailing slash... */
2198 ftpState->hackShortcut(ftpSendCwd);
2199 } else {
2200 ftpFail(ftpState);
2201 }
2202 } else {
2203 ftpFail(ftpState);
2204 }
2205}
2206
2211void
2213{
2214 assert(entry);
2215 entry->lock("Ftp::Gateway");
2216 ErrorState ferr(ERR_DIR_LISTING, Http::scOkay, request.getRaw(), fwd->al);
2217 ferr.ftp.listing = &listing;
2218 ferr.ftp.cwd_msg = xstrdup(cwd_message.size()? cwd_message.termedBuf() : "");
2219 ferr.ftp.server_msg = ctrl.message;
2220 ctrl.message = nullptr;
2221 entry->replaceHttpReply(ferr.BuildHttpReply());
2222 entry->flush();
2223 entry->unlock("Ftp::Gateway");
2224}
2225
2226static void
2228{
2229 int code = ftpState->ctrl.replycode;
2230 debugs(9, 3, MYNAME);
2231
2232 if (code == 226 || code == 250) {
2233 /* Connection closed; retrieval done. */
2234 if (ftpState->flags.listing) {
2235 ftpState->completedListing();
2236 /* QUIT operation handles sending the reply to client */
2237 }
2238 ftpState->markParsedVirginReplyAsWhole("ftpReadTransferDone code 226 or 250");
2239 ftpSendQuit(ftpState);
2240 } else { /* != 226 */
2241 debugs(9, DBG_IMPORTANT, "Got code " << code << " after reading data");
2242 ftpState->failed(ERR_FTP_FAILURE, 0);
2243 /* failed closes ctrl.conn and frees ftpState */
2244 return;
2245 }
2246}
2247
2248// premature end of the request body
2249void
2251{
2253 debugs(9, 3, "ftpState=" << this);
2254 failed(ERR_READ_ERROR, 0);
2255}
2256
2257static void
2259{
2260 int code = ftpState->ctrl.replycode;
2261 debugs(9, 3, MYNAME);
2262
2263 if (!(code == 226 || code == 250)) {
2264 debugs(9, DBG_IMPORTANT, "Got code " << code << " after sending data");
2265 ftpState->failed(ERR_FTP_PUT_ERROR, 0);
2266 return;
2267 }
2268
2269 ftpState->entry->timestampsSet(); /* XXX Is this needed? */
2270 ftpSendReply(ftpState);
2271}
2272
2273static void
2275{
2276 /* check the server control channel is still available */
2277 if (!ftpState || !ftpState->haveControlChannel("ftpSendQuit"))
2278 return;
2279
2280 snprintf(cbuf, CTRL_BUFLEN, "QUIT\r\n");
2281 ftpState->writeCommand(cbuf);
2282 ftpState->state = Ftp::Client::SENT_QUIT;
2283}
2284
2288static void
2290{
2291 ftpState->serverComplete();
2292}
2293
2294static void
2296{
2297 char *path;
2298 ftpState->flags.try_slash_hack = 1;
2299 /* Free old paths */
2300
2301 debugs(9, 3, MYNAME);
2302
2303 if (ftpState->pathcomps)
2304 wordlistDestroy(&ftpState->pathcomps);
2305
2306 safe_free(ftpState->filepath);
2307
2308 /* Build the new path (urlpath begins with /) */
2309 path = SBufToCstring(ftpState->request->url.path());
2310
2311 rfc1738_unescape(path);
2312
2313 ftpState->filepath = path;
2314
2315 /* And off we go */
2316 ftpGetFile(ftpState);
2317}
2318
2322void
2324{
2325 debugs(9, 3, MYNAME);
2326
2327 if (old_request != nullptr) {
2328 safe_free(old_request);
2329 safe_free(old_reply);
2330 }
2331}
2332
2333void
2335{
2336 /* Clear some unwanted state */
2337 setCurrentOffset(0);
2338 restart_offset = 0;
2339 /* Save old error message & some state info */
2340
2341 debugs(9, 3, MYNAME);
2342
2343 if (old_request == nullptr) {
2344 old_request = ctrl.last_command;
2345 ctrl.last_command = nullptr;
2346 old_reply = ctrl.last_reply;
2347 ctrl.last_reply = nullptr;
2348
2349 if (pathcomps == nullptr && filepath != nullptr)
2350 old_filepath = xstrdup(filepath);
2351 }
2352
2353 /* Jump to the "hack" state */
2354 nextState(this);
2355}
2356
2357static void
2359{
2360 const bool slashHack = ftpState->request->url.path().caseCmp("/%2f", 4)==0;
2361 int code = ftpState->ctrl.replycode;
2362 err_type error_code = ERR_NONE;
2363
2364 debugs(9, 6, "state " << ftpState->state <<
2365 " reply code " << code << "flags(" <<
2366 (ftpState->flags.isdir?"IS_DIR,":"") <<
2367 (ftpState->flags.try_slash_hack?"TRY_SLASH_HACK":"") << "), " <<
2368 "mdtm=" << ftpState->mdtm << ", size=" << ftpState->theSize <<
2369 "slashhack=" << (slashHack? "T":"F"));
2370
2371 /* Try the / hack to support "Netscape" FTP URL's for retrieving files */
2372 if (!ftpState->flags.isdir && /* Not a directory */
2373 !ftpState->flags.try_slash_hack && !slashHack && /* Not doing slash hack */
2374 ftpState->mdtm <= 0 && ftpState->theSize < 0) { /* Not known as a file */
2375
2376 switch (ftpState->state) {
2377
2379
2381 /* Try the / hack */
2382 ftpState->hackShortcut(ftpTrySlashHack);
2383 return;
2384
2385 default:
2386 break;
2387 }
2388 }
2389
2390 Http::StatusCode sc = ftpState->failedHttpStatus(error_code);
2391 const auto ftperr = new ErrorState(error_code, sc, ftpState->fwd->request, ftpState->fwd->al);
2392 ftpState->failed(error_code, 0, ftperr);
2393 ftperr->detailError(new Ftp::ErrorDetail(code));
2394 HttpReply *newrep = ftperr->BuildHttpReply();
2395 delete ftperr;
2396
2397 ftpState->entry->replaceHttpReply(newrep);
2398 ftpSendQuit(ftpState);
2399}
2400
2403{
2404 if (error == ERR_NONE) {
2405 switch (state) {
2406
2407 case SENT_USER:
2408
2409 case SENT_PASS:
2410
2411 if (ctrl.replycode > 500) {
2413 return password_url ? Http::scForbidden : Http::scUnauthorized;
2414 } else if (ctrl.replycode == 421) {
2417 }
2418 break;
2419
2420 case SENT_CWD:
2421
2422 case SENT_RETR:
2423 if (ctrl.replycode == 550) {
2425 return Http::scNotFound;
2426 }
2427 break;
2428
2429 default:
2430 break;
2431 }
2432 }
2434}
2435
2436static void
2438{
2439 int code = ftpState->ctrl.replycode;
2440 Http::StatusCode http_code;
2441 err_type err_code = ERR_NONE;
2442
2443 debugs(9, 3, ftpState->entry->url() << ", code " << code);
2444
2445 if (cbdataReferenceValid(ftpState))
2446 debugs(9, 5, "ftpState (" << ftpState << ") is valid!");
2447
2448 if (code == 226 || code == 250) {
2449 err_code = (ftpState->mdtm > 0) ? ERR_FTP_PUT_MODIFIED : ERR_FTP_PUT_CREATED;
2450 http_code = (ftpState->mdtm > 0) ? Http::scAccepted : Http::scCreated;
2451 } else if (code == 227) {
2452 err_code = ERR_FTP_PUT_CREATED;
2453 http_code = Http::scCreated;
2454 } else {
2455 err_code = ERR_FTP_PUT_ERROR;
2456 http_code = Http::scInternalServerError;
2457 }
2458
2459 ErrorState err(err_code, http_code, ftpState->request.getRaw(), ftpState->fwd->al);
2460
2461 if (ftpState->old_request)
2462 err.ftp.request = xstrdup(ftpState->old_request);
2463 else
2464 err.ftp.request = xstrdup(ftpState->ctrl.last_command);
2465
2466 if (ftpState->old_reply)
2467 err.ftp.reply = xstrdup(ftpState->old_reply);
2468 else if (ftpState->ctrl.last_reply)
2469 err.ftp.reply = xstrdup(ftpState->ctrl.last_reply);
2470 else
2471 err.ftp.reply = xstrdup("");
2472
2474
2475 ftpState->entry->replaceHttpReply(err.BuildHttpReply());
2476
2477 ftpSendQuit(ftpState);
2478}
2479
2480void
2482{
2483 debugs(9, 3, MYNAME);
2484
2485 if (flags.http_header_sent)
2486 return;
2487
2488 HttpReply *reply = new HttpReply;
2489
2490 flags.http_header_sent = 1;
2491
2492 assert(entry->isEmpty());
2493
2494 entry->buffer(); /* released when done processing current data payload */
2495
2496 SBuf urlPath = request->url.path();
2497 auto t = urlPath.rfind('/');
2498 SBuf filename = urlPath.substr(t != SBuf::npos ? t : 0);
2499
2500 const char *mime_type = nullptr;
2501 const char *mime_enc = nullptr;
2502
2503 if (flags.isdir) {
2504 mime_type = "text/html";
2505 } else {
2506 switch (typecode) {
2507
2508 case 'I':
2509 mime_type = "application/octet-stream";
2510 // XXX: performance regression, c_str() may reallocate
2511 mime_enc = mimeGetContentEncoding(filename.c_str());
2512 break;
2513
2514 case 'A':
2515 mime_type = "text/plain";
2516 break;
2517
2518 default:
2519 // XXX: performance regression, c_str() may reallocate
2520 mime_type = mimeGetContentType(filename.c_str());
2521 mime_enc = mimeGetContentEncoding(filename.c_str());
2522 break;
2523 }
2524 }
2525
2526 /* set standard stuff */
2527
2528 if (0 == getCurrentOffset()) {
2529 /* Full reply */
2530 reply->setHeaders(Http::scOkay, "Gatewaying", mime_type, theSize, mdtm, -2);
2531 } else if (theSize < getCurrentOffset()) {
2532 /*
2533 * DPW 2007-05-04
2534 * offset should not be larger than theSize. We should
2535 * not be seeing this condition any more because we'll only
2536 * send REST if we know the theSize and if it is less than theSize.
2537 */
2538 debugs(0, DBG_CRITICAL, "ERROR: " <<
2539 " current offset=" << getCurrentOffset() <<
2540 ", but theSize=" << theSize <<
2541 ". assuming full content response");
2542 reply->setHeaders(Http::scOkay, "Gatewaying", mime_type, theSize, mdtm, -2);
2543 } else {
2544 /* Partial reply */
2545 HttpHdrRangeSpec range_spec;
2546 range_spec.offset = getCurrentOffset();
2547 range_spec.length = theSize - getCurrentOffset();
2548 reply->setHeaders(Http::scPartialContent, "Gatewaying", mime_type, theSize - getCurrentOffset(), mdtm, -2);
2549 httpHeaderAddContRange(&reply->header, range_spec, theSize);
2550 }
2551
2552 /* additional info */
2553 if (mime_enc)
2555
2557 setVirginReply(reply);
2558 adaptOrFinalizeReply();
2559}
2560
2561void
2563{
2565
2566 StoreEntry *e = entry;
2567
2568 e->timestampsSet();
2569
2570 // makePublic() if allowed/possible or release() otherwise
2571 if (flags.authenticated || // authenticated requests can't be cached
2572 getCurrentOffset() ||
2573 !e->makePublic()) {
2574 e->release();
2575 }
2576}
2577
2578HttpReply *
2580{
2582 HttpReply *newrep = err.BuildHttpReply();
2583#if HAVE_AUTH_MODULE_BASIC
2584 /* add Authenticate header */
2585 // XXX: performance regression. c_str() may reallocate
2586 newrep->header.putAuth("Basic", realm.c_str());
2587#else
2588 (void)realm;
2589#endif
2590 return newrep;
2591}
2592
2593const SBuf &
2595{
2596 SBuf newbuf("%2f");
2597
2598 if (request->url.getScheme() != AnyP::PROTO_FTP) {
2599 static const SBuf nil;
2600 return nil;
2601 }
2602
2603 if (request->url.path()[0] == '/') {
2604 newbuf.append(request->url.path());
2605 request->url.path(newbuf);
2606 } else if (!request->url.path().startsWith(newbuf)) {
2607 newbuf.append(request->url.path().substr(1));
2608 request->url.path(newbuf);
2609 }
2610
2611 return request->effectiveRequestUri();
2612}
2613
2618void
2619Ftp::Gateway::writeReplyBody(const char *dataToWrite, size_t dataLength)
2620{
2621 debugs(9, 5, "writing " << dataLength << " bytes to the reply");
2622 addVirginReplyBody(dataToWrite, dataLength);
2623}
2624
2631void
2633{
2634 if (fwd == nullptr || flags.completed_forwarding) {
2635 debugs(9, 3, "avoid double-complete on FD " <<
2636 (ctrl.conn ? ctrl.conn->fd : -1) << ", Data FD " << data.conn->fd <<
2637 ", this " << this << ", fwd " << fwd);
2638 return;
2639 }
2640
2641 flags.completed_forwarding = true;
2643}
2644
2651bool
2652Ftp::Gateway::haveControlChannel(const char *caller_name) const
2653{
2654 if (doneWithServer())
2655 return false;
2656
2657 /* doneWithServer() only checks BOTH channels are closed. */
2658 if (!Comm::IsConnOpen(ctrl.conn)) {
2659 debugs(9, DBG_IMPORTANT, "WARNING: FTP Server Control channel is closed, but Data channel still active.");
2660 debugs(9, 2, caller_name << ": attempted on a closed FTP channel.");
2661 return false;
2662 }
2663
2664 return true;
2665}
2666
2667bool
2669{
2670 // TODO: Can we do what Ftp::Relay::mayReadVirginReplyBody() does instead?
2671 return !doneWithServer();
2672}
2673
2674void
2676{
2677 AsyncJob::Start(new Ftp::Gateway(fwdState));
2678}
2679
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
#define COMM_REUSEADDR
Definition: Connection.h:48
static FTPSM ftpReadPass
Definition: FtpGateway.cc:205
static FTPSM ftpReadTransferDone
Definition: FtpGateway.cc:231
static FTPSM ftpReadMkdir
Definition: FtpGateway.cc:237
static FTPSM ftpFail
Definition: FtpGateway.cc:238
static FTPSM ftpReadStor
Definition: FtpGateway.cc:233
#define MAX_TOKENS
Definition: FtpGateway.cc:523
static void ftpListPartsFree(ftpListParts **parts)
Definition: FtpGateway.cc:514
static FTPSM ftpWriteTransferDone
Definition: FtpGateway.cc:234
static FTPSM ftpSendMdtm
Definition: FtpGateway.cc:208
static void ftpOpenListenSocket(Ftp::Gateway *ftpState, int fallback)
Definition: FtpGateway.cc:1744
FTPSM * FTP_SM_FUNCS[]
Definition: FtpGateway.cc:284
static FTPSM ftpSendStor
Definition: FtpGateway.cc:232
static FTPSM ftpReadPORT
Definition: FtpGateway.cc:214
static FTPSM ftpGetFile
Definition: FtpGateway.cc:220
#define CTRL_BUFLEN
Definition: FtpGateway.cc:192
static FTPSM ftpSendList
Definition: FtpGateway.cc:224
static char cbuf[CTRL_BUFLEN]
Definition: FtpGateway.cc:193
static FTPSM ftpSendRetr
Definition: FtpGateway.cc:229
static const char * Month[]
Definition: FtpGateway.cc:496
Ftp::StateMethod FTPSM
Definition: FtpGateway.cc:179
static void ftpTrySlashHack(Ftp::Gateway *ftpState)
Definition: FtpGateway.cc:2295
static FTPSM ftpSendQuit
Definition: FtpGateway.cc:239
static FTPSM ftpSendNlst
Definition: FtpGateway.cc:225
static FTPSM ftpSendCwd
Definition: FtpGateway.cc:221
static FTPSM ftpReadPasv
Definition: FtpGateway.cc:217
static FTPSM ftpSendSize
Definition: FtpGateway.cc:210
CBDATA_NAMESPACED_CLASS_INIT(Ftp, Gateway)
static FTPSM ftpReadEPSV
Definition: FtpGateway.cc:216
static FTPSM ftpReadList
Definition: FtpGateway.cc:226
static FTPSM ftpReadWelcome
Definition: FtpGateway.cc:201
static FTPSM ftpSendReply
Definition: FtpGateway.cc:235
static FTPSM ftpSendType
Definition: FtpGateway.cc:206
static FTPSM ftpSendUser
Definition: FtpGateway.cc:202
static FTPSM ftpRestOrList
Definition: FtpGateway.cc:223
static FTPSM ftpListDir
Definition: FtpGateway.cc:219
static int is_month(const char *buf)
Definition: FtpGateway.cc:502
static FTPSM ftpReadUser
Definition: FtpGateway.cc:203
static FTPSM ftpReadRest
Definition: FtpGateway.cc:228
static FTPSM ftpSendRest
Definition: FtpGateway.cc:227
static FTPSM ftpSendPORT
Definition: FtpGateway.cc:213
static FTPSM ftpSendPass
Definition: FtpGateway.cc:204
static FTPSM ftpReadQuit
Definition: FtpGateway.cc:240
static FTPSM ftpTraverseDirectory
Definition: FtpGateway.cc:218
static FTPSM ftpReadRetr
Definition: FtpGateway.cc:230
static FTPSM ftpReadSize
Definition: FtpGateway.cc:211
static FTPSM ftpReadMdtm
Definition: FtpGateway.cc:209
static FTPSM ftpSendPassive
Definition: FtpGateway.cc:215
static FTPSM ftpSendMkdir
Definition: FtpGateway.cc:236
static FTPSM ftpReadCwd
Definition: FtpGateway.cc:222
static ftpListParts * ftpListParseParts(const char *buf, struct Ftp::GatewayFlags flags)
Definition: FtpGateway.cc:526
static FTPSM ftpReadEPRT
Definition: FtpGateway.cc:212
static FTPSM ftpReadType
Definition: FtpGateway.cc:207
void httpHeaderAddContRange(HttpHeader *, HttpHdrRangeSpec, int64_t)
int size
Definition: ModDevPoll.cc:75
#define SQUIDSBUFPH
Definition: SBuf.h:31
void SBufToCstring(char *d, const SBuf &s)
Definition: SBuf.h:752
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
class SquidConfig Config
Definition: SquidConfig.cc:12
int conn
the current server connection FD
Definition: Transport.cc:26
void error(char *format,...)
#define assert(EX)
Definition: assert.h:17
int cbdataReferenceValid(const void *p)
Definition: cbdata.cc:265
AnyP::UriScheme const & getScheme() const
Definition: Uri.h:67
void path(const char *p)
Definition: Uri.h:101
void host(const char *src)
Definition: Uri.cc:100
static void Start(const Pointer &job)
Definition: AsyncJob.cc:37
virtual void completeForwarding()
Definition: Client.cc:230
void serverComplete()
Definition: Client.cc:180
int64_t currentOffset
Definition: Client.h:172
virtual void handleRequestBodyProducerAborted()=0
Definition: Client.cc:351
HttpRequestPointer request
Definition: Client.h:178
void markParsedVirginReplyAsWhole(const char *reasonWeAreSure)
Definition: Client.cc:158
StoreEntry * entry
Definition: Client.h:176
FwdState::Pointer fwd
Definition: Client.h:177
virtual void haveParsedReplyHeaders()
called when we have final (possibly adapted) reply headers; kids extend
Definition: Client.cc:537
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition: CommCalls.h:83
Comm::Flag flag
comm layer result status.
Definition: CommCalls.h:82
Comm::ConnectionPointer conn
Definition: CommCalls.h:80
Ip::Address remote
Definition: Connection.h:149
Ip::Address local
Definition: Connection.h:146
MemBuf * listing
Definition: errorpage.h:193
char * reply
Definition: errorpage.h:191
char * cwd_msg
Definition: errorpage.h:192
void detailError(const ErrorDetail::Pointer &dCode)
set error type-specific detail code
Definition: errorpage.h:111
wordlist * server_msg
Definition: errorpage.h:189
HttpRequestPointer request
Definition: errorpage.h:177
HttpReply * BuildHttpReply(void)
Definition: errorpage.cc:1277
struct ErrorState::@56 ftp
Comm::ConnectionPointer listenConn
Definition: FtpClient.h:65
void close()
planned close: removes the close handler and calls comm_close
Definition: FtpClient.cc:107
void clear()
remove the close handler, leave connection open
Definition: FtpClient.cc:128
Comm::ConnectionPointer conn
channel descriptor
Definition: FtpClient.h:58
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
void start() override
called by AsyncStart; do not call directly
Definition: FtpClient.cc:215
bool handleEpsvReply(Ip::Address &remoteAddr)
Definition: FtpClient.cc:491
virtual void handleControlReply()
Definition: FtpClient.cc:419
void initReadBuf()
Definition: FtpClient.cc:221
void writeCommand(const char *buf)
Definition: FtpClient.cc:823
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
void connectDataChannel()
Definition: FtpClient.cc:762
DataChannel data
FTP data channel state.
Definition: FtpClient.h:143
void maybeReadVirginBody() override
read response data from the network
Definition: FtpClient.cc:915
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
char * old_reply
Definition: FtpClient.h:178
char * old_request
Definition: FtpClient.h:177
virtual void dataClosed(const CommCloseCbParams &io)
handler called by Comm when FTP data channel is closed unexpectedly
Definition: FtpClient.cc:810
char * last_reply
Definition: FtpClient.h:84
char * last_command
Definition: FtpClient.h:83
wordlist * message
Definition: FtpClient.h:82
int checkAuth(const HttpHeader *req_hdr)
Definition: FtpGateway.cc:1025
Gateway(FwdState *)
Definition: FtpGateway.cc:328
Http::StatusCode failedHttpStatus(err_type &error) override
Definition: FtpGateway.cc:2402
char * dirpath
Definition: FtpGateway.cc:115
bool htmlifyListEntry(const char *line, PackableStream &)
Definition: FtpGateway.cc:758
char user[MAX_URL]
Definition: FtpGateway.cc:101
char * proxy_host
Definition: FtpGateway.cc:117
size_t list_width
Definition: FtpGateway.cc:118
CBDATA_CHILD(Gateway)
char * reply_hdr
Definition: FtpGateway.cc:104
void handleControlReply() override
Definition: FtpGateway.cc:1164
void writeReplyBody(const char *, size_t len)
Definition: FtpGateway.cc:2619
void processHeadResponse()
Definition: FtpGateway.cc:1680
int64_t theSize
Definition: FtpGateway.cc:112
void listenForDataChannel(const Comm::ConnectionPointer &conn)
create a data channel acceptor and start listening.
Definition: FtpGateway.cc:443
void dataClosed(const CommCloseCbParams &io) override
handler called by Comm when FTP data channel is closed unexpectedly
Definition: FtpGateway.cc:316
void loginFailed(void)
Definition: FtpGateway.cc:1215
bool mayReadVirginReplyBody() const override
whether we may receive more virgin response body bytes
Definition: FtpGateway.cc:2668
void hackShortcut(StateMethod *nextState)
Definition: FtpGateway.cc:2334
void setCurrentOffset(int64_t offset)
Definition: FtpGateway.cc:149
void buildTitleUrl()
Definition: FtpGateway.cc:1109
void handleRequestBodyProducerAborted() override
Definition: FtpGateway.cc:2250
void start() override
called by AsyncStart; do not call directly
Definition: FtpGateway.cc:1142
char * old_filepath
Definition: FtpGateway.cc:120
int reply_hdr_state
Definition: FtpGateway.cc:105
wordlist * pathcomps
Definition: FtpGateway.cc:113
virtual bool haveControlChannel(const char *caller_name) const
Definition: FtpGateway.cc:2652
void haveParsedReplyHeaders() override
called when we have final (possibly adapted) reply headers; kids extend
Definition: FtpGateway.cc:2562
void parseListing()
Definition: FtpGateway.cc:874
int64_t restart_offset
Definition: FtpGateway.cc:116
GatewayFlags flags
Definition: FtpGateway.cc:124
void dataChannelConnected(const CommConnectCbParams &io) override
Definition: FtpGateway.cc:1722
char password[MAX_URL]
Definition: FtpGateway.cc:102
~Gateway() override
Definition: FtpGateway.cc:364
void completeForwarding() override
Definition: FtpGateway.cc:2632
int restartable()
Definition: FtpGateway.cc:2054
static HttpReply * ftpAuthRequired(HttpRequest *request, SBuf &realm, AccessLogEntry::Pointer &)
Definition: FtpGateway.cc:2579
void timeout(const CommTimeoutCbParams &io) override
read timeout handler
Definition: FtpGateway.cc:480
void loginParser(const SBuf &login, bool escaped)
Definition: FtpGateway.cc:397
void appendSuccessHeader()
Definition: FtpGateway.cc:2481
char * filepath
Definition: FtpGateway.cc:114
String title_url
Definition: FtpGateway.cc:107
static PF ftpDataWrite
Definition: FtpGateway.cc:153
MemBuf listing
FTP directory listing in HTML format.
Definition: FtpGateway.cc:122
void processReplyBody() override
Definition: FtpGateway.cc:958
void completedListing(void)
Definition: FtpGateway.cc:2212
String cwd_message
Definition: FtpGateway.cc:119
String base_href
Definition: FtpGateway.cc:108
int64_t getCurrentOffset() const
Definition: FtpGateway.cc:150
void readStor()
Definition: FtpGateway.cc:2004
String clean_url
Definition: FtpGateway.cc:106
SBuf ftpRealm()
Definition: FtpGateway.cc:1259
void checkUrlpath()
Definition: FtpGateway.cc:1071
void ftpAcceptDataConnection(const CommAcceptCbParams &io)
Definition: FtpGateway.cc:1873
HttpRequest * request
Definition: FwdState.h:169
AccessLogEntryPointer al
info for the future access.log entry
Definition: FwdState.h:170
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:996
SBuf getAuthToken(Http::HdrType id, const char *auth_scheme) const
Definition: HttpHeader.cc:1276
int64_t getInt64(Http::HdrType id) const
Definition: HttpHeader.cc:1134
void putAuth(const char *auth_scheme, const char *realm)
Definition: HttpHeader.cc:1005
void setHeaders(Http::StatusCode status, const char *reason, const char *ctype, int64_t clen, time_t lmt, time_t expires)
Definition: HttpReply.cc:170
HttpRequestMethod method
Definition: HttpRequest.h:114
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
const SBuf & effectiveRequestUri() const
RFC 7230 section 5.5 - Effective Request URI.
Definition: HttpRequest.cc:744
@ srcFtp
ftp_port or FTP server
Definition: Message.h:40
uint32_t sources
The message sources.
Definition: Message.h:99
HttpHeader header
Definition: Message.h:74
static void FreeAddr(struct addrinfo *&ai)
Definition: Address.cc:686
void getAddrInfo(struct addrinfo *&ai, int force=AF_UNSPEC) const
Definition: Address.cc:599
bool isIPv4() const
Definition: Address.cc:158
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
mb_size_t contentSize() const
available data size
Definition: MemBuf.h:47
C * getRaw() const
Definition: RefCount.h:89
Definition: SBuf.h:94
const char * rawContent() const
Definition: SBuf.cc:509
static const size_type npos
Definition: SBuf.h:99
const char * c_str()
Definition: SBuf.cc:516
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:415
SBuf & appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Definition: SBuf.cc:229
size_type rfind(char c, size_type endPos=npos) const
Definition: SBuf.cc:692
size_type find(char c, size_type startPos=0) const
Definition: SBuf.cc:584
size_type copy(char *dest, size_type n) const
Definition: SBuf.cc:500
bool isEmpty() const
Definition: SBuf.h:431
SBuf & append(const SBuf &S)
Definition: SBuf.cc:185
SBuf substr(size_type pos, size_type n=npos) const
Definition: SBuf.cc:576
MemBlob::size_type size_type
Definition: SBuf.h:96
struct SquidConfig::@108 Ftp
char * anon_user
Definition: SquidConfig.h:413
const char * url() const
Definition: store.cc:1552
void release(const bool shareable=false)
Definition: store.cc:1132
bool makePublic(const KeyScope keyScope=ksDefault)
Definition: store.cc:166
bool timestampsSet()
Definition: store.cc:1373
void replaceHttpReply(const HttpReplyPointer &, const bool andStartWriting=true)
Definition: store.cc:1691
void append(char const *buf, int len)
Definition: String.cc:130
void reset(char const *str)
Definition: String.cc:122
char * key
Definition: wordlist.h:32
wordlist * next
Definition: wordlist.h:33
void PF(int, void *)
Definition: forward.h:18
void comm_open_listener(int sock_type, int proto, Comm::ConnectionPointer &conn, const char *note)
Definition: comm.cc:256
#define w_space
#define MYNAME
Definition: Stream.h:236
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
#define DBG_CRITICAL
Definition: Stream.h:37
#define EBIT_TEST(flag, bit)
Definition: defines.h:69
#define MAX_URL
Definition: defines.h:78
@ ENTRY_ABORTED
Definition: enums.h:115
err_type
Definition: forward.h:14
@ ERR_FTP_PUT_CREATED
Definition: forward.h:57
@ ERR_FTP_UNAVAILABLE
Definition: forward.h:52
@ ERR_FTP_NOT_FOUND
Definition: forward.h:55
@ ERR_FTP_PUT_ERROR
Definition: forward.h:54
@ ERR_FTP_FORBIDDEN
Definition: forward.h:56
@ ERR_FTP_FAILURE
Definition: forward.h:53
@ ERR_NONE
Definition: forward.h:15
@ ERR_DIR_LISTING
Definition: forward.h:70
@ ERR_READ_ERROR
Definition: forward.h:28
@ ERR_CACHE_ACCESS_DENIED
Definition: forward.h:19
@ ERR_FTP_PUT_MODIFIED
Definition: forward.h:58
void fatal(const char *message)
Definition: fatal.cc:28
#define fd_table
Definition: fde.h:189
const char * null_string
char * html_quote(const char *string)
Definition: Quoting.cc:49
void memFree(void *, int type)
Free a element allocated by memAllocate()
Definition: minimal.cc:60
void * memAllocate(mem_type)
Allocate one element from the typed pool.
Definition: old_api.cc:213
@ MEM_4K_BUF
Definition: forward.h:43
@ MEM_8K_BUF
Definition: forward.h:44
const char * mimeGetIconURL(const char *fn)
Definition: mime.cc:162
const char * mimeGetContentEncoding(const char *fn)
Definition: mime.cc:195
char mimeGetTransferMode(const char *fn)
Definition: mime.cc:209
bool mimeGetViewOption(const char *fn)
Definition: mime.cc:223
const char * mimeGetContentType(const char *fn)
Definition: mime.cc:181
bool mimeGetDownloadOption(const char *fn)
Definition: mime.cc:216
@ PROTO_FTP
Definition: ProtocolType.h:26
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:27
@ OK
Definition: Flag.h:16
@ TIMEOUT
Definition: Flag.h:18
Definition: forward.h:24
const SBuf & UrlWith2f(HttpRequest *)
Definition: FtpGateway.cc:2594
void StartGateway(FwdState *const fwdState)
A new FTP Gateway job.
Definition: FtpGateway.cc:2675
const char *const crlf
Definition: FtpClient.cc:40
void() StateMethod(Ftp::Gateway *)
Definition: FtpGateway.cc:89
StatusCode
Definition: StatusCode.h:20
@ scAccepted
Definition: StatusCode.h:28
@ scForbidden
Definition: StatusCode.h:47
@ scUnauthorized
Definition: StatusCode.h:45
@ scInternalServerError
Definition: StatusCode.h:71
@ scCreated
Definition: StatusCode.h:27
@ scNotFound
Definition: StatusCode.h:48
@ scOkay
Definition: StatusCode.h:26
@ scPartialContent
Definition: StatusCode.h:32
@ scServiceUnavailable
Definition: StatusCode.h:74
@ METHOD_PUT
Definition: MethodType.h:27
@ METHOD_HEAD
Definition: MethodType.h:28
@ CONTENT_ENCODING
@ CONTENT_LENGTH
SSL Connection
Definition: Session.h:45
void Controller::create() STUB void Controller Controller nil
time_t ParseIso3307(const char *)
Convert from ISO 3307 style time: YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx.
Definition: iso3307.cc:18
#define xfree
#define xstrdup
#define xmalloc
struct tok tokens[]
Definition: parse.c:168
char * rfc1738_do_escape(const char *url, int flags)
Definition: rfc1738.c:56
#define rfc1738_escape_part(x)
Definition: rfc1738.h:55
#define rfc1738_escape(x)
Definition: rfc1738.h:52
void rfc1738_unescape(char *url)
Definition: rfc1738.c:146
int code
Definition: smb-errors.c:145
static int sc[16]
Definition: smbdes.c:121
int64_t strtoll(const char *nptr, char **endptr, int base)
Definition: strtoll.c:61
bool completed_forwarding
Definition: FtpGateway.cc:85
bool pasv_supported
PASV command is allowed.
Definition: FtpGateway.cc:59
bool tried_auth_anonymous
auth has tried to use anonymous credentials already.
Definition: FtpGateway.cc:66
bool epsv_all_sent
EPSV ALL has been used. Must abort on failures.
Definition: FtpGateway.cc:60
bool authenticated
authentication success
Definition: FtpGateway.cc:65
bool tried_auth_nopass
auth tried username with no password already.
Definition: FtpGateway.cc:67
char * showname
Definition: FtpGateway.cc:188
int64_t size
Definition: FtpGateway.cc:185
int token
Definition: parse.c:163
SBuf text("GET http://resource.com/path HTTP/1.1\r\n" "Host: resource.com\r\n" "Cookie: laijkpk3422r j1noin \r\n" "\r\n")
#define PRId64
Definition: types.h:104
const char * wordlistAdd(wordlist **list, const char *key)
Definition: wordlist.cc:25
void wordlistDestroy(wordlist **list)
destroy a wordlist
Definition: wordlist.cc:16
char * wordlistChopHead(wordlist **wl)
Definition: wordlist.cc:55
void * xcalloc(size_t n, size_t sz)
Definition: xalloc.cc:71
#define safe_free(x)
Definition: xalloc.h:73
#define xtoupper(x)
Definition: xis.h:16
#define xisspace(x)
Definition: xis.h:15
const char * xstrerr(int error)
Definition: xstrerror.cc:83
char * xstrncpy(char *dst, const char *src, size_t n)
Definition: xstring.cc:37
char * xstrndup(const char *s, size_t n)
Definition: xstring.cc:56

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors