ftpiiu_plugin/include/ftpSession.h

537 lines
13 KiB
C
Raw Normal View History

2020-04-05 21:16:16 +02:00
// ftpd is a server implementation based on the following:
// - RFC 959 (https://tools.ietf.org/html/rfc959)
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
//
// Copyright (C) 2024 Michael Theall
2020-04-05 21:16:16 +02:00
//
// 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
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include "fs.h"
2020-04-24 22:51:43 +02:00
#include "ftpConfig.h"
2020-04-05 21:16:16 +02:00
#include "ioBuffer.h"
#include "platform.h"
#include "socket.h"
#if __has_include(<glob.h>)
#include <glob.h>
#define FTPD_HAS_GLOB 1
#else
#define FTPD_HAS_GLOB 0
#endif
#include <sys/stat.h>
using stat_t = struct stat;
2020-04-05 21:16:16 +02:00
#include <chrono>
#include <ctime>
2020-04-05 21:16:16 +02:00
#include <memory>
#include <optional>
2020-04-05 21:16:16 +02:00
#include <string_view>
#include <utility>
#include <vector>
class FtpSession;
using UniqueFtpSession = std::unique_ptr<FtpSession>;
2020-04-06 07:36:03 +02:00
/// \brief FTP session
2020-04-05 21:16:16 +02:00
class FtpSession
{
public:
~FtpSession ();
2020-04-06 07:36:03 +02:00
/// \brief Whether session sockets are all inactive
2020-04-05 21:16:16 +02:00
bool dead ();
2020-04-06 07:36:03 +02:00
/// \brief Draw session status
2020-04-05 21:16:16 +02:00
void draw ();
/// \brief Draw session connections
void drawConnections ();
2020-04-06 07:36:03 +02:00
/// \brief Create session
2020-04-24 22:51:43 +02:00
/// \param config_ FTP config
2020-04-06 07:36:03 +02:00
/// \param commandSocket_ Command socket
2020-04-24 22:51:43 +02:00
static UniqueFtpSession create (FtpConfig &config_, UniqueSocket commandSocket_);
2020-04-05 21:16:16 +02:00
2020-04-06 07:36:03 +02:00
/// \brief Poll for activity
/// \param sessions_ Sessions to poll
2020-04-07 04:17:30 +02:00
static bool poll (std::vector<UniqueFtpSession> const &sessions_);
2020-04-05 21:16:16 +02:00
private:
2020-04-06 07:36:03 +02:00
/// \brief Command buffer size
constexpr static auto COMMAND_BUFFERSIZE = 4096;
#ifdef __NDS__
2020-04-17 22:32:39 +02:00
/// \brief Response buffer size
constexpr static auto RESPONSE_BUFFERSIZE = 4096;
/// \brief Transfer buffersize
constexpr static auto XFER_BUFFERSIZE = 8192;
#else
2020-04-06 07:36:03 +02:00
/// \brief Response buffer size
2023-11-19 16:44:10 +01:00
constexpr static auto RESPONSE_BUFFERSIZE = 16 * 1024;
2020-04-06 07:36:03 +02:00
/// \brief Transfer buffersize
2023-11-19 16:44:10 +01:00
constexpr static auto XFER_BUFFERSIZE = 32 * 1024;
2020-04-17 22:32:39 +02:00
#endif
2020-04-06 07:36:03 +02:00
/// \brief File buffersize
constexpr static auto FILE_BUFFERSIZE = 4 * XFER_BUFFERSIZE;
2020-04-05 21:16:16 +02:00
#if defined(__NDS__)
2020-04-17 22:32:39 +02:00
/// \brief Socket buffer size
constexpr static auto SOCK_BUFFERSIZE = 4096;
/// \brief Amount of file position history to keep
constexpr static auto POSITION_HISTORY = 60;
2022-03-31 18:53:47 +02:00
#elif defined(__3DS__)
2020-04-06 07:36:03 +02:00
/// \brief Socket buffer size
constexpr static auto SOCK_BUFFERSIZE = 32768;
/// \brief Amount of file position history to keep
2020-04-05 21:16:16 +02:00
constexpr static auto POSITION_HISTORY = 100;
#else
2020-04-06 07:36:03 +02:00
/// \brief Socket buffer size
constexpr static auto SOCK_BUFFERSIZE = XFER_BUFFERSIZE;
/// \brief Amount of file position history to keep
2020-04-05 21:16:16 +02:00
constexpr static auto POSITION_HISTORY = 300;
#endif
2020-04-06 07:36:03 +02:00
/// \brief Session state
2020-04-05 21:16:16 +02:00
enum class State
{
COMMAND,
DATA_CONNECT,
DATA_TRANSFER,
};
2020-04-06 07:36:03 +02:00
/// \brief Transfer file mode
2020-04-05 21:16:16 +02:00
enum class XferFileMode
{
RETR,
STOR,
APPE,
};
2020-04-06 07:36:03 +02:00
/// \brief Transfer directory mode
2020-04-05 21:16:16 +02:00
enum class XferDirMode
{
LIST,
MLSD,
MLST,
NLST,
STAT,
};
2020-04-06 07:36:03 +02:00
/// \brief Parameterized constructor
2020-04-24 22:51:43 +02:00
/// \param config_ FTP config
2020-04-06 07:36:03 +02:00
/// \param commandSocket_ Command socket
2020-04-24 22:51:43 +02:00
FtpSession (FtpConfig &config_, UniqueSocket commandSocket_);
/// \brief Whether session is authorized
bool authorized () const;
2020-04-05 21:16:16 +02:00
2020-04-06 07:36:03 +02:00
/// \brief Set session state
/// \param state_ State to set
/// \param closePasv_ Whether to close listening socket
/// \param closeData_ Whether to close data socket
2020-04-05 21:16:16 +02:00
void setState (State state_, bool closePasv_, bool closeData_);
2020-04-08 00:34:45 +02:00
/// \brief Close socket
/// \param socket_ Socket to close
void closeSocket (SharedSocket &socket_);
/// \brief Close command socket
void closeCommand ();
/// \brief Close passive socket
void closePasv ();
2020-04-06 07:36:03 +02:00
/// \brief Close data socket
2020-04-05 21:16:16 +02:00
void closeData ();
2020-04-06 07:36:03 +02:00
/// \brief Change working directory
2020-04-05 21:16:16 +02:00
bool changeDir (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Accept connection as data socket
2020-04-05 21:16:16 +02:00
bool dataAccept ();
2020-04-06 07:36:03 +02:00
/// \brief Connect data socket
bool dataConnect ();
2020-04-05 21:16:16 +02:00
/// \brief Perform stat and apply tz offset to mtime
/// \param path_ Path to stat
/// \param st_ Output stat
int tzStat (char const *const path_, stat_t *st_);
/// \brief Perform lstat and apply tz offset to mtime
/// \param path_ Path to lstat
/// \param st_ Output stat
int tzLStat (char const *const path_, stat_t *st_);
2020-04-06 07:36:03 +02:00
/// \brief Fill directory entry
/// \param st_ Entry status
/// \param path_ Path name
/// \param type_ MLST type
int fillDirent (stat_t const &st_, std::string_view path_, char const *type_ = nullptr);
2020-04-06 07:36:03 +02:00
/// \brief Fill directory entry
/// \param path_ Path name
/// \param type_ MLST type
2020-04-05 21:16:16 +02:00
int fillDirent (std::string const &path_, char const *type_ = nullptr);
2020-04-06 07:36:03 +02:00
/// \brief Transfer file
/// \param args_ Command arguments
/// \param mode_ Transfer file mode
2020-04-05 21:16:16 +02:00
void xferFile (char const *args_, XferFileMode mode_);
2020-04-06 07:36:03 +02:00
/// \brief Transfer directory
/// \param args_ Command arguments
/// \param mode_ Transfer directory mode
2023-03-10 08:49:54 +01:00
/// \param workaround_ Workaround broken clients who use LIST -a/-l
2020-04-05 21:16:16 +02:00
void xferDir (char const *args_, XferDirMode mode_, bool workaround_);
2020-04-06 07:36:03 +02:00
/// \brief Read command
/// \param events_ Poll events
2020-04-05 21:16:16 +02:00
void readCommand (int events_);
2020-04-06 07:36:03 +02:00
/// \brief Write response
2020-04-05 21:16:16 +02:00
void writeResponse ();
2020-04-06 07:36:03 +02:00
/// \brief Send response
/// \param fmt_ Message format
2020-04-05 21:16:16 +02:00
__attribute__ ((format (printf, 2, 3))) void sendResponse (char const *fmt_, ...);
2020-04-06 07:36:03 +02:00
/// \brief Send response
/// \param response_ Response message
2020-04-05 21:16:16 +02:00
void sendResponse (std::string_view response_);
2020-04-06 07:36:03 +02:00
/// \brief Transfer function
2020-04-05 21:16:16 +02:00
bool (FtpSession::*m_transfer) () = nullptr;
2020-04-06 07:36:03 +02:00
/// \brief Transfer directory list
2020-04-05 21:16:16 +02:00
bool listTransfer ();
2020-04-06 07:36:03 +02:00
#if FTPD_HAS_GLOB
/// \brief Transfer glob list
bool globTransfer ();
#endif
2020-04-06 07:36:03 +02:00
/// \brief Transfer download
2020-04-05 21:16:16 +02:00
bool retrieveTransfer ();
2020-04-06 07:36:03 +02:00
/// \brief Transfer upload
2020-04-05 21:16:16 +02:00
bool storeTransfer ();
#ifndef __NDS__
2020-04-06 07:36:03 +02:00
/// \brief Mutex
2020-04-05 21:16:16 +02:00
platform::Mutex m_lock;
2020-04-17 22:32:39 +02:00
#endif
2020-04-05 21:16:16 +02:00
2020-04-24 22:51:43 +02:00
/// \brief FTP config
FtpConfig &m_config;
2020-04-06 07:36:03 +02:00
/// \brief Command socket
2020-04-05 21:16:16 +02:00
SharedSocket m_commandSocket;
2020-04-06 07:36:03 +02:00
/// \brief Data listen socker
2020-04-05 21:16:16 +02:00
UniqueSocket m_pasvSocket;
2020-04-06 07:36:03 +02:00
/// \brief Data socket
2020-04-05 21:16:16 +02:00
SharedSocket m_dataSocket;
2020-04-06 07:36:03 +02:00
/// \brief Sockets pending close
2020-04-05 21:16:16 +02:00
std::vector<SharedSocket> m_pendingCloseSocket;
2020-04-06 07:36:03 +02:00
/// \brief Command buffer
2020-04-05 21:16:16 +02:00
IOBuffer m_commandBuffer;
2020-04-06 07:36:03 +02:00
/// \brief Response buffer
2020-04-05 21:16:16 +02:00
IOBuffer m_responseBuffer;
2020-04-06 07:36:03 +02:00
/// \brief Transfer buffer
2020-04-05 21:16:16 +02:00
IOBuffer m_xferBuffer;
2020-04-06 07:36:03 +02:00
/// \brief Address from last PORT command
2020-04-05 21:16:16 +02:00
SockAddr m_portAddr;
2020-04-06 07:36:03 +02:00
/// \brief Current working directory
std::string m_cwd = "/";
2020-04-06 07:36:03 +02:00
/// \brief List working directory
2020-04-05 21:16:16 +02:00
std::string m_lwd;
2020-04-06 07:36:03 +02:00
/// \brief Path from RNFR command
2020-04-05 21:16:16 +02:00
std::string m_rename;
2020-04-06 07:36:03 +02:00
/// \brief Current work item
2020-04-05 21:16:16 +02:00
std::string m_workItem;
2020-04-06 07:36:03 +02:00
/// \brief ImGui window name
2020-04-05 21:16:16 +02:00
std::string m_windowName;
2020-04-06 07:36:03 +02:00
/// \brief ImGui plot widget name
2020-04-05 21:16:16 +02:00
std::string m_plotName;
2020-04-06 07:36:03 +02:00
/// \brief Position from REST command
2020-04-05 21:16:16 +02:00
std::uint64_t m_restartPosition = 0;
2020-04-06 07:36:03 +02:00
/// \brief Current file position
std::uint64_t m_filePosition = 0;
/// \brief File size of current transfer
std::uint64_t m_fileSize = 0;
/// \brief Last file position update timestamp
2020-04-05 21:16:16 +02:00
platform::steady_clock::time_point m_filePositionTime;
2020-04-06 07:36:03 +02:00
/// \brief File position history
2020-04-05 21:16:16 +02:00
std::uint64_t m_filePositionHistory[POSITION_HISTORY];
2020-04-06 07:36:03 +02:00
/// \brief File position history deltas
2020-04-05 21:16:16 +02:00
float m_filePositionDeltas[POSITION_HISTORY];
2020-04-06 07:36:03 +02:00
/// \brief Transfer rate (EWMA low-pass filtered)
2020-04-05 21:16:16 +02:00
float m_xferRate;
2020-04-06 07:36:03 +02:00
/// \brief Session state
2020-04-05 21:16:16 +02:00
State m_state = State::COMMAND;
2020-04-06 07:36:03 +02:00
/// \brief File being transferred
2020-04-05 21:16:16 +02:00
fs::File m_file;
2020-04-06 07:36:03 +02:00
/// \brief Directory being transferred
2020-04-05 21:16:16 +02:00
fs::Dir m_dir;
#if FTPD_HAS_GLOB
/// \brief Glob wrappre
class Glob
{
public:
~Glob () noexcept;
Glob () noexcept;
/// \brief Perform glob
/// \param pattern_ Glob pattern
bool glob (char const *pattern_) noexcept;
/// \brief Get next glob result
/// \note returns nullptr when no more entries exist
char const *next () noexcept;
private:
/// \brief Clear glob
void clear () noexcept;
/// \brief Glob result
std::optional<glob_t> m_glob = std::nullopt;
/// \brief Result counter
unsigned m_offset = 0;
};
/// \brief Glob
Glob m_glob;
#endif
2020-04-06 07:36:03 +02:00
/// \brief Directory transfer mode
2020-04-05 21:16:16 +02:00
XferDirMode m_xferDirMode;
/// \brief Last activity timestamp
2020-04-24 22:51:43 +02:00
time_t m_timestamp;
/// \brief Whether user has been authorized
bool m_authorizedUser : 1;
/// \brief Whether password has been authorized
bool m_authorizedPass : 1;
2020-04-06 07:36:03 +02:00
/// \brief Whether previous command was PASV
2020-04-05 21:16:16 +02:00
bool m_pasv : 1;
2020-04-06 07:36:03 +02:00
/// \brief Whether previous command was PORT
2020-04-05 21:16:16 +02:00
bool m_port : 1;
2020-04-06 07:36:03 +02:00
/// \brief Whether receiving data
2020-04-05 21:16:16 +02:00
bool m_recv : 1;
2020-04-06 07:36:03 +02:00
/// \brief Whether sending data
2020-04-05 21:16:16 +02:00
bool m_send : 1;
2020-04-06 07:36:03 +02:00
/// \brief Whether urgent (out-of-band) data is on the way
2020-04-05 21:16:16 +02:00
bool m_urgent : 1;
2020-04-06 07:36:03 +02:00
/// \brief Whether MLST type fact is enabled
2020-04-05 21:16:16 +02:00
bool m_mlstType : 1;
2020-04-06 07:36:03 +02:00
/// \brief Whether MLST size fact is enabled
2020-04-05 21:16:16 +02:00
bool m_mlstSize : 1;
2020-04-06 07:36:03 +02:00
/// \brief Whether MLST modify fact is enabled
2020-04-05 21:16:16 +02:00
bool m_mlstModify : 1;
2020-04-06 07:36:03 +02:00
/// \brief Whether MLST perm fact is enabled
2020-04-05 21:16:16 +02:00
bool m_mlstPerm : 1;
2020-04-06 07:36:03 +02:00
/// \brief Whether MLST unix.mode fact is enabled
2020-04-05 21:16:16 +02:00
bool m_mlstUnixMode : 1;
/// \brief Whether emulating /dev/zero
bool m_devZero : 1;
2020-04-06 07:36:03 +02:00
/// \brief Abort a transfer
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void ABOR (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Allocate space
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void ALLO (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Append data to a file
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void APPE (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief CWD to parent directory
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void CDUP (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Change working directory
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void CWD (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Delete a file
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void DELE (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief List server features
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void FEAT (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Print server help
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void HELP (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief List directory
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void LIST (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Last modification time
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void MDTM (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Create a directory
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void MKD (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Machine list directory
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void MLSD (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Machine list
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void MLST (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Set transfer mode
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void MODE (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Name list
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void NLST (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief No-op
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void NOOP (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Set server options
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void OPTS (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Password
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void PASS (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Request an address to connect to for data transfers
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void PASV (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Provide an address to connect to for data transfers
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void PORT (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Print working directory
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void PWD (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Terminate session
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void QUIT (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Restart a file transfer
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void REST (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Retrieve a file
/// \param args_ Command arguments
/// \note Requires a PASV or PORT connection
2020-04-05 21:16:16 +02:00
void RETR (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Remove a directory
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void RMD (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Rename from
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void RNFR (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Rename to
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void RNTO (char const *args_);
2020-04-06 07:36:03 +02:00
2020-04-24 22:51:43 +02:00
/// \brief Site command
/// \param args_ Command arguments
void SITE (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Get file size
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void SIZE (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Get status
/// \param args_ Command arguments
/// \note If no argument is supplied, and a transfer is occurring, get the current transfer
/// status. If no argument is supplied, and no transfer is occurring, get the server status. If
/// an argument is supplied, this is equivalent to LIST, except the data is sent over the
/// command socket.
2020-04-05 21:16:16 +02:00
void STAT (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Store a file
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void STOR (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Store a unique file
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void STOU (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Set file structure
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void STRU (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Identify system
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void SYST (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Set representation type
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void TYPE (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief User name
/// \param args_ Command arguments
2020-04-05 21:16:16 +02:00
void USER (char const *args_);
2020-04-06 07:36:03 +02:00
/// \brief Map of command handlers
2020-04-05 21:16:16 +02:00
static std::vector<std::pair<std::string_view, void (FtpSession::*) (char const *)>> const
handlers;
};