This commit is contained in:
Michael Theall 2023-03-10 01:49:54 -06:00
parent 9eba777ac9
commit 38f9bde54b
3 changed files with 51 additions and 52 deletions

View File

@ -3,7 +3,7 @@
// - RFC 3659 (https://tools.ietf.org/html/rfc3659) // - RFC 3659 (https://tools.ietf.org/html/rfc3659)
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html // - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
// //
// Copyright (C) 2020 Michael Theall // Copyright (C) 2023 Michael Theall
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -175,6 +175,7 @@ private:
/// \brief Transfer directory /// \brief Transfer directory
/// \param args_ Command arguments /// \param args_ Command arguments
/// \param mode_ Transfer directory mode /// \param mode_ Transfer directory mode
/// \param workaround_ Workaround broken clients who use LIST -a/-l
void xferDir (char const *args_, XferDirMode mode_, bool workaround_); void xferDir (char const *args_, XferDirMode mode_, bool workaround_);
/// \brief Read command /// \brief Read command

View File

@ -3,7 +3,7 @@
// - RFC 3659 (https://tools.ietf.org/html/rfc3659) // - RFC 3659 (https://tools.ietf.org/html/rfc3659)
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html // - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
// //
// Copyright (C) 2022 Michael Theall // Copyright (C) 2023 Michael Theall
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -1196,18 +1196,8 @@ void FtpSession::xferDir (char const *const args_, XferDirMode const mode_, bool
auto const path = buildResolvedPath (m_cwd, args_); auto const path = buildResolvedPath (m_cwd, args_);
if (path.empty ()) if (path.empty ())
{ {
sendResponse ("550 %s\r\n", std::strerror (errno));
setState (State::COMMAND, true, true);
return;
}
struct stat st;
if (::stat (path.c_str (), &st) != 0)
{
auto const rc = errno;
// work around broken clients that think LIST -a/-l is valid // work around broken clients that think LIST -a/-l is valid
if (workaround_ && mode_ == XferDirMode::LIST) if (workaround_)
{ {
if (args_[0] == '-' && (args_[1] == 'a' || args_[1] == 'l')) if (args_[0] == '-' && (args_[1] == 'a' || args_[1] == 'l'))
{ {
@ -1223,12 +1213,32 @@ void FtpSession::xferDir (char const *const args_, XferDirMode const mode_, bool
} }
} }
sendResponse ("550 %s\r\n", std::strerror (errno));
setState (State::COMMAND, true, true);
return;
}
struct stat st;
if (::stat (path.c_str (), &st) != 0)
{
sendResponse ("550 %s\r\n", std::strerror (errno));
setState (State::COMMAND, true, true);
return;
}
if (mode_ == XferDirMode::MLST)
{
auto const rc = fillDirent (st, path);
if (rc != 0)
{
sendResponse ("550 %s\r\n", std::strerror (rc)); sendResponse ("550 %s\r\n", std::strerror (rc));
setState (State::COMMAND, true, true); setState (State::COMMAND, true, true);
return; return;
} }
if (S_ISDIR (st.st_mode)) LOCKED (m_workItem = path);
}
else if (S_ISDIR (st.st_mode))
{ {
if (!m_dir.open (path.c_str ())) if (!m_dir.open (path.c_str ()))
{ {
@ -1243,7 +1253,7 @@ void FtpSession::xferDir (char const *const args_, XferDirMode const mode_, bool
if (mode_ == XferDirMode::MLSD && m_mlstType) if (mode_ == XferDirMode::MLSD && m_mlstType)
{ {
// send this directory as type=cdir // send this directory as type=cdir
auto const rc = fillDirent (m_lwd, "cdir"); auto const rc = fillDirent (st, m_lwd, "cdir");
if (rc != 0) if (rc != 0)
{ {
sendResponse ("550 %s\r\n", std::strerror (rc)); sendResponse ("550 %s\r\n", std::strerror (rc));
@ -1288,6 +1298,18 @@ void FtpSession::xferDir (char const *const args_, XferDirMode const mode_, bool
LOCKED (m_workItem = path); LOCKED (m_workItem = path);
} }
} }
else if (mode_ == XferDirMode::MLST)
{
auto const rc = fillDirent (m_cwd);
if (rc != 0)
{
sendResponse ("550 %s\r\n", std::strerror (rc));
setState (State::COMMAND, true, true);
return;
}
LOCKED (m_workItem = m_cwd);
}
else if (!m_dir.open (m_cwd.c_str ())) else if (!m_dir.open (m_cwd.c_str ()))
{ {
// no argument, but opening cwd failed // no argument, but opening cwd failed
@ -1318,7 +1340,7 @@ void FtpSession::xferDir (char const *const args_, XferDirMode const mode_, bool
if (mode_ == XferDirMode::MLST || mode_ == XferDirMode::STAT) if (mode_ == XferDirMode::MLST || mode_ == XferDirMode::STAT)
{ {
// this is a little different; we have to send the data over the command socket // this is a little different; we have to send the data over the command socket
sendResponse ("213-Status\r\n"); sendResponse ("250-Status\r\n");
setState (State::DATA_TRANSFER, true, true); setState (State::DATA_TRANSFER, true, true);
LOCKED (m_dataSocket = m_commandSocket); LOCKED (m_dataSocket = m_commandSocket);
m_send = true; m_send = true;
@ -1600,10 +1622,10 @@ bool FtpSession::listTransfer ()
// check xfer dir type // check xfer dir type
int rc = 226; int rc = 226;
if (m_xferDirMode == XferDirMode::STAT) if (m_xferDirMode == XferDirMode::MLST || m_xferDirMode == XferDirMode::STAT)
rc = 213; rc = 250;
// check if this was for a file // check if this was for a file/MLST
if (!m_dir) if (!m_dir)
{ {
// we already sent the file's listing // we already sent the file's listing
@ -2070,44 +2092,20 @@ void FtpSession::MLSD (char const *args_)
} }
// open the path in MLSD mode // open the path in MLSD mode
xferDir (args_, XferDirMode::MLSD, true); xferDir (args_, XferDirMode::MLSD, false);
} }
void FtpSession::MLST (char const *args_) void FtpSession::MLST (char const *args_)
{
if (!authorized ())
{ {
setState (State::COMMAND, false, false); setState (State::COMMAND, false, false);
sendResponse ("530 Not logged in\r\n");
// build the path to list
auto const path = buildResolvedPath (m_cwd, args_);
if (path.empty ())
{
sendResponse ("501 %s\r\n", std::strerror (errno));
return; return;
} }
// stat path // open the path in MLST mode
struct stat st; xferDir (args_, XferDirMode::MLST, false);
if (::lstat (path.c_str (), &st) != 0)
{
sendResponse ("550 %s\r\n", std::strerror (errno));
return;
}
// encode path
auto const encodedPath = encodePath (path);
m_xferDirMode = XferDirMode::MLST;
auto const rc = fillDirent (st, path);
if (rc != 0)
{
sendResponse ("550 %s\r\n", std::strerror (rc));
return;
}
sendResponse ("250-Status\r\n"
" %s\r\n"
"250 End\r\n",
encodedPath.c_str ());
} }
void FtpSession::MODE (char const *args_) void FtpSession::MODE (char const *args_)

View File

@ -3,7 +3,7 @@
// - RFC 3659 (https://tools.ietf.org/html/rfc3659) // - RFC 3659 (https://tools.ietf.org/html/rfc3659)
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html // - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
// //
// Copyright (C) 2020 Michael Theall // Copyright (C) 2023 Michael Theall
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -173,7 +173,7 @@ bool platform::networkAddress (SockAddr &addr_)
{ {
struct sockaddr_in addr; struct sockaddr_in addr;
addr.sin_family = AF_INET; addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY; addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
addr_ = addr; addr_ = addr;
return true; return true;