mirror of
https://github.com/wiiu-env/ftpiiu_plugin.git
synced 2024-12-28 05:11:48 +01:00
Merge branch 'master' of https://github.com/mtheall/ftpd into upstream_changes
This commit is contained in:
parent
00191956b8
commit
618b2fb136
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -18,6 +18,8 @@ jobs:
|
||||
needs: clang-format
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: create version.h
|
||||
run: |
|
||||
git_hash=$(git rev-parse --short "$GITHUB_SHA")
|
||||
|
2
.github/workflows/pr.yml
vendored
2
.github/workflows/pr.yml
vendored
@ -15,6 +15,8 @@ jobs:
|
||||
needs: clang-format
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: create version.h
|
||||
run: |
|
||||
git_hash=$(git rev-parse --short "${{ github.event.pull_request.head.sha }}")
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -12,6 +12,7 @@
|
||||
*.nso
|
||||
*.pfs0
|
||||
*.smdh
|
||||
*~
|
||||
.gdb_history
|
||||
3ds/build
|
||||
3ds-classic/build
|
||||
@ -24,7 +25,7 @@ switch-classic/build
|
||||
switch/romfs/*.zst
|
||||
switch/romfs/shaders/*.dksh
|
||||
.idea/
|
||||
build/
|
||||
build*/
|
||||
*.rpx
|
||||
*.wuhb
|
||||
*.wps
|
||||
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "3rd/gls"]
|
||||
path = 3rd/gls
|
||||
url = git@github.com:microsoft/GSL.git
|
1
3rd/gls
Submodule
1
3rd/gls
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit a3534567187d2edc428efd3f13466ff75fe5805c
|
4
Makefile
4
Makefile
@ -22,9 +22,9 @@ WUPS_ROOT := $(DEVKITPRO)/wups
|
||||
#-------------------------------------------------------------------------------
|
||||
TARGET := ftpiiu
|
||||
BUILD := build
|
||||
SOURCES := source source/wiiu
|
||||
SOURCES := source source/wiiu source/posix
|
||||
DATA := data
|
||||
INCLUDES := source include
|
||||
INCLUDES := source include 3rd/gls/include
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
|
28
include/fs.h
28
include/fs.h
@ -3,7 +3,7 @@
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2020 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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
|
||||
@ -22,6 +22,8 @@
|
||||
|
||||
#include "ioBuffer.h"
|
||||
|
||||
#include <gsl/gsl>
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <cstdint>
|
||||
@ -29,6 +31,7 @@
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace fs
|
||||
{
|
||||
@ -69,7 +72,7 @@ public:
|
||||
/// \brief Open file
|
||||
/// \param path_ Path to open
|
||||
/// \param mode_ Access mode (\sa std::fopen)
|
||||
bool open (char const *path_, char const *mode_ = "rb");
|
||||
bool open (gsl::not_null<gsl::czstring> path_, gsl::not_null<gsl::czstring> mode_ = "rb");
|
||||
|
||||
/// \brief Close file
|
||||
void close ();
|
||||
@ -77,13 +80,13 @@ public:
|
||||
/// \brief Seek to file position
|
||||
/// \param pos_ File position
|
||||
/// \param origin_ Reference position (\sa std::fseek)
|
||||
std::make_signed_t<std::size_t> seek (std::size_t pos_, int origin_);
|
||||
std::make_signed_t<std::size_t> seek (std::make_signed_t<std::size_t> pos_, int origin_);
|
||||
|
||||
/// \brief Read data
|
||||
/// \param buffer_ Output buffer
|
||||
/// \param size_ Size to read
|
||||
/// \note Can return partial reads
|
||||
std::make_signed_t<std::size_t> read (void *buffer_, std::size_t size_);
|
||||
std::make_signed_t<std::size_t> read (gsl::not_null<void *> buffer_, std::size_t size_);
|
||||
|
||||
/// \brief Read data
|
||||
/// \param buffer_ Output buffer
|
||||
@ -97,13 +100,13 @@ public:
|
||||
/// \param buffer_ Output buffer
|
||||
/// \param size_ Size to read
|
||||
/// \note Fails on partial reads and errors
|
||||
bool readAll (void *buffer_, std::size_t size_);
|
||||
bool readAll (gsl::not_null<void *> buffer_, std::size_t size_);
|
||||
|
||||
/// \brief Write data
|
||||
/// \param buffer_ Input data
|
||||
/// \param size_ Size to write
|
||||
/// \note Can return partial writes
|
||||
std::make_signed_t<std::size_t> write (void const *buffer_, std::size_t size_);
|
||||
std::make_signed_t<std::size_t> write (gsl::not_null<void const *> buffer_, std::size_t size_);
|
||||
|
||||
/// \brief Write data
|
||||
/// \param buffer_ Input data
|
||||
@ -114,20 +117,17 @@ public:
|
||||
/// \param buffer_ Input data
|
||||
/// \param size_ Size to write
|
||||
/// \note Fails on partials writes and errors
|
||||
bool writeAll (void const *buffer_, std::size_t size_);
|
||||
bool writeAll (gsl::not_null<void const *> buffer_, std::size_t size_);
|
||||
|
||||
private:
|
||||
/// \brief Underlying std::FILE*
|
||||
std::unique_ptr<std::FILE, int (*) (std::FILE *)> m_fp{nullptr, nullptr};
|
||||
|
||||
/// \brief Buffer
|
||||
std::unique_ptr<char[]> m_buffer;
|
||||
|
||||
/// \brief Buffer size
|
||||
std::size_t m_bufferSize = 0;
|
||||
std::vector<char> m_buffer;
|
||||
|
||||
/// \brief Line buffer
|
||||
char *m_lineBuffer = nullptr;
|
||||
gsl::owner<char *> m_lineBuffer = nullptr;
|
||||
|
||||
/// \brief Line buffer size
|
||||
std::size_t m_lineBufferSize = 0;
|
||||
@ -161,14 +161,14 @@ public:
|
||||
|
||||
/// \brief Open directory
|
||||
/// \param path_ Path to open
|
||||
bool open (char const *const path_);
|
||||
bool open (gsl::not_null<gsl::czstring> path_);
|
||||
|
||||
/// \brief Close directory
|
||||
void close ();
|
||||
|
||||
/// \brief Read a directory entry
|
||||
/// \note Returns nullptr on end-of-directory or error; check errno
|
||||
struct dirent *read ();
|
||||
dirent *read ();
|
||||
|
||||
private:
|
||||
/// \brief Underlying DIR*
|
||||
|
@ -3,7 +3,7 @@
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2022 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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
|
||||
@ -22,10 +22,13 @@
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#include <gsl/gsl>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
class FtpConfig;
|
||||
using UniqueFtpConfig = std::unique_ptr<FtpConfig>;
|
||||
@ -41,15 +44,15 @@ public:
|
||||
|
||||
/// \brief Load config
|
||||
/// \param path_ Path to config file
|
||||
static UniqueFtpConfig load (char const *path_);
|
||||
static UniqueFtpConfig load (gsl::not_null<gsl::czstring> path_);
|
||||
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
std::scoped_lock<platform::Mutex> lockGuard ();
|
||||
#endif
|
||||
|
||||
/// \brief Save config
|
||||
/// \param path_ Path to config file
|
||||
bool save (char const *path_);
|
||||
bool save (gsl::not_null<gsl::czstring> path_);
|
||||
|
||||
/// \brief Get user
|
||||
std::string const &user () const;
|
||||
@ -57,6 +60,9 @@ public:
|
||||
/// \brief Get password
|
||||
std::string const &pass () const;
|
||||
|
||||
/// \brief Get hostname
|
||||
std::string const &hostname () const;
|
||||
|
||||
/// \brief Get port
|
||||
std::uint16_t port () const;
|
||||
|
||||
@ -79,15 +85,19 @@ public:
|
||||
|
||||
/// \brief Set user
|
||||
/// \param user_ User
|
||||
void setUser (std::string const &user_);
|
||||
void setUser (std::string user_);
|
||||
|
||||
/// \brief Set password
|
||||
/// \param pass_ Password
|
||||
void setPass (std::string const &pass_);
|
||||
void setPass (std::string pass_);
|
||||
|
||||
/// \brief Set hostname
|
||||
/// \param hostname_ Hostname
|
||||
void setHostname (std::string hostname_);
|
||||
|
||||
/// \brief Set listen port
|
||||
/// \param port_ Listen port
|
||||
bool setPort (std::string const &port_);
|
||||
bool setPort (std::string_view port_);
|
||||
|
||||
/// \brief Set listen port
|
||||
/// \param port_ Listen port
|
||||
@ -106,17 +116,17 @@ public:
|
||||
|
||||
/// \brief Set access point SSID
|
||||
/// \param ssid_ SSID
|
||||
void setSSID (std::string const &ssid_);
|
||||
void setSSID (std::string_view ssid_);
|
||||
|
||||
/// \brief Set access point passphrase
|
||||
/// \param passphrase_ Passphrase
|
||||
void setPassphrase (std::string const &passphrase_);
|
||||
void setPassphrase (std::string_view passphrase_);
|
||||
#endif
|
||||
|
||||
private:
|
||||
FtpConfig ();
|
||||
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
/// \brief Mutex
|
||||
mutable platform::Mutex m_lock;
|
||||
#endif
|
||||
@ -127,6 +137,9 @@ private:
|
||||
/// \brief Password
|
||||
std::string m_pass;
|
||||
|
||||
/// \brief Hostname
|
||||
std::string m_hostname;
|
||||
|
||||
/// \brief Listen port
|
||||
std::uint16_t m_port;
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2022 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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
|
||||
@ -48,6 +48,9 @@ public:
|
||||
/// \brief Draw server and all of its sessions
|
||||
void draw ();
|
||||
|
||||
/// \brief Whether server wants to quit
|
||||
bool quit ();
|
||||
|
||||
/// \brief Create server
|
||||
static UniqueFtpServer create ();
|
||||
|
||||
@ -60,6 +63,11 @@ public:
|
||||
/// \brief Server start time
|
||||
static std::time_t startTime ();
|
||||
|
||||
#ifdef __3DS__
|
||||
/// \brief Get timezone offset in seconds (only used on 3DS)
|
||||
static int tzOffset ();
|
||||
#endif
|
||||
|
||||
private:
|
||||
/// \brief Paramterized constructor
|
||||
/// \param config_ FTP config
|
||||
@ -88,7 +96,7 @@ private:
|
||||
/// \brief Thread entry point
|
||||
void threadFunc ();
|
||||
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
/// \brief Thread
|
||||
platform::Thread m_thread;
|
||||
|
||||
@ -102,6 +110,11 @@ private:
|
||||
/// \brief Listen socket
|
||||
UniqueSocket m_socket;
|
||||
|
||||
#ifndef __NDS__
|
||||
/// \brief mDNS socket
|
||||
UniqueSocket m_mdnsSocket;
|
||||
#endif
|
||||
|
||||
/// \brief ImGui window name
|
||||
std::string m_name;
|
||||
|
||||
@ -109,7 +122,7 @@ private:
|
||||
std::vector<UniqueFtpSession> m_sessions;
|
||||
|
||||
/// \brief Whether thread should quit
|
||||
std::atomic<bool> m_quit;
|
||||
std::atomic_bool m_quit = false;
|
||||
|
||||
#ifndef CLASSIC
|
||||
/// \brief Log upload cURL context
|
||||
@ -143,8 +156,11 @@ private:
|
||||
/// \brief Password setting
|
||||
std::string m_passSetting;
|
||||
|
||||
/// \brief Hostname setting
|
||||
std::string m_hostnameSetting;
|
||||
|
||||
/// \brief Port setting
|
||||
std::uint16_t m_portSetting;
|
||||
std::uint16_t m_portSetting = 0;
|
||||
|
||||
#ifdef __3DS__
|
||||
/// \brief getMTime setting
|
||||
@ -153,7 +169,7 @@ private:
|
||||
|
||||
#ifdef __SWITCH__
|
||||
/// \brief Whether an error occurred enabling access point
|
||||
std::atomic<bool> m_apError = false;
|
||||
std::atomic_bool m_apError = false;
|
||||
|
||||
/// \brief Enable access point setting
|
||||
bool m_enableAPSetting;
|
||||
|
@ -3,7 +3,7 @@
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2023 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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
|
||||
@ -26,8 +26,20 @@
|
||||
#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;
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@ -47,6 +59,9 @@ public:
|
||||
/// \brief Draw session status
|
||||
void draw ();
|
||||
|
||||
/// \brief Draw session connections
|
||||
void drawConnections ();
|
||||
|
||||
/// \brief Create session
|
||||
/// \param config_ FTP config
|
||||
/// \param commandSocket_ Command socket
|
||||
@ -60,7 +75,7 @@ private:
|
||||
/// \brief Command buffer size
|
||||
constexpr static auto COMMAND_BUFFERSIZE = 4096;
|
||||
|
||||
#ifdef NDS
|
||||
#ifdef __NDS__
|
||||
/// \brief Response buffer size
|
||||
constexpr static auto RESPONSE_BUFFERSIZE = 4096;
|
||||
|
||||
@ -77,7 +92,7 @@ private:
|
||||
/// \brief File buffersize
|
||||
constexpr static auto FILE_BUFFERSIZE = 4 * XFER_BUFFERSIZE;
|
||||
|
||||
#if defined(NDS)
|
||||
#if defined(__NDS__)
|
||||
/// \brief Socket buffer size
|
||||
constexpr static auto SOCK_BUFFERSIZE = 4096;
|
||||
|
||||
@ -156,11 +171,21 @@ private:
|
||||
/// \brief Connect data socket
|
||||
bool dataConnect ();
|
||||
|
||||
/// \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_);
|
||||
|
||||
/// \brief Fill directory entry
|
||||
/// \param st_ Entry status
|
||||
/// \param path_ Path name
|
||||
/// \param type_ MLST type
|
||||
int fillDirent (struct stat const &st_, std::string_view path_, char const *type_ = nullptr);
|
||||
int fillDirent (stat_t const &st_, std::string_view path_, char const *type_ = nullptr);
|
||||
|
||||
/// \brief Fill directory entry
|
||||
/// \param path_ Path name
|
||||
@ -199,13 +224,18 @@ private:
|
||||
/// \brief Transfer directory list
|
||||
bool listTransfer ();
|
||||
|
||||
#if FTPD_HAS_GLOB
|
||||
/// \brief Transfer glob list
|
||||
bool globTransfer ();
|
||||
#endif
|
||||
|
||||
/// \brief Transfer download
|
||||
bool retrieveTransfer ();
|
||||
|
||||
/// \brief Transfer upload
|
||||
bool storeTransfer ();
|
||||
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
/// \brief Mutex
|
||||
platform::Mutex m_lock;
|
||||
#endif
|
||||
@ -285,10 +315,41 @@ private:
|
||||
/// \brief Directory being transferred
|
||||
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
|
||||
|
||||
/// \brief Directory transfer mode
|
||||
XferDirMode m_xferDirMode;
|
||||
|
||||
/// \brief Last command timestamp
|
||||
/// \brief Last activity timestamp
|
||||
time_t m_timestamp;
|
||||
|
||||
/// \brief Whether user has been authorized
|
||||
|
@ -3,7 +3,7 @@
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - 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
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@ -24,6 +24,10 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#ifdef DEBUG
|
||||
#undef DEBUG
|
||||
#endif
|
||||
|
||||
/// \brief Log level
|
||||
enum LogLevel
|
||||
{
|
||||
|
40
include/mdns.h
Normal file
40
include/mdns.h
Normal file
@ -0,0 +1,40 @@
|
||||
|
||||
// ftpd is a server implementation based on the following:
|
||||
// - RFC 959 (https://datatracker.ietf.org/doc/html/rfc959)
|
||||
// - RFC 3659 (https://datatracker.ietf.org/doc/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// ftpd implements mdns based on the following:
|
||||
// - RFC 1035 (https://datatracker.ietf.org/doc/html/rfc1035)
|
||||
// - RFC 6762 (https://datatracker.ietf.org/doc/html/rfc6762)
|
||||
//
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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 "sockAddr.h"
|
||||
#include "socket.h"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace mdns
|
||||
{
|
||||
void setHostname (std::string hostname_);
|
||||
|
||||
UniqueSocket createSocket ();
|
||||
|
||||
void handleSocket (Socket *socket_, SockAddr const &addr_);
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2020 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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
|
||||
@ -22,7 +22,7 @@
|
||||
|
||||
#include "sockAddr.h"
|
||||
|
||||
#if defined(NDS)
|
||||
#if defined(__NDS__)
|
||||
#include <nds.h>
|
||||
#elif defined(__3DS__)
|
||||
#include <3ds.h>
|
||||
@ -37,6 +37,7 @@
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#if defined(CLASSIC) && !defined(__WIIU__)
|
||||
extern PrintConsole g_statusConsole;
|
||||
@ -74,6 +75,9 @@ bool networkVisible ();
|
||||
/// \param[out] addr_ Network address
|
||||
bool networkAddress (SockAddr &addr_);
|
||||
|
||||
/// \brief Get hostname
|
||||
std::string const &hostname ();
|
||||
|
||||
/// \brief Platform loop
|
||||
bool loop ();
|
||||
|
||||
@ -110,7 +114,7 @@ struct steady_clock
|
||||
using steady_clock = std::chrono::steady_clock;
|
||||
#endif
|
||||
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
/// \brief Platform thread
|
||||
class Thread
|
||||
{
|
||||
|
@ -3,7 +3,7 @@
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2020 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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
|
||||
@ -23,24 +23,55 @@
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef NDS
|
||||
struct sockaddr_storage
|
||||
{
|
||||
unsigned short ss_family;
|
||||
char ss_data[sizeof (struct sockaddr_in) - sizeof (unsigned short)];
|
||||
};
|
||||
#endif
|
||||
|
||||
/// \brief Socket address
|
||||
class SockAddr
|
||||
{
|
||||
public:
|
||||
enum class Domain
|
||||
{
|
||||
IPv4 = AF_INET,
|
||||
#ifndef NO_IPV6
|
||||
IPv6 = AF_INET6,
|
||||
#endif
|
||||
};
|
||||
|
||||
/// \brief 0.0.0.0
|
||||
static SockAddr const AnyIPv4;
|
||||
|
||||
#ifndef NO_IPV6
|
||||
/// \brief ::
|
||||
static SockAddr const AnyIPv6;
|
||||
#endif
|
||||
|
||||
~SockAddr ();
|
||||
|
||||
SockAddr ();
|
||||
|
||||
/// \brief Parameterized constructor
|
||||
/// \param domain_ Socket domain
|
||||
/// \note Initial address is INADDR_ANY/in6addr_any
|
||||
SockAddr (Domain domain_);
|
||||
|
||||
/// \brief Parameterized constructor
|
||||
/// \param addr_ Socket address (network byte order)
|
||||
/// \param port_ Socket port (host byte order)
|
||||
SockAddr (in_addr_t addr_, std::uint16_t port_ = 0);
|
||||
|
||||
/// \brief Parameterized constructor
|
||||
/// \param addr_ Socket address (network byte order)
|
||||
/// \param port_ Socket port (host byte order)
|
||||
SockAddr (in_addr const &addr_, std::uint16_t port_ = 0);
|
||||
|
||||
#ifndef NO_IPV6
|
||||
/// \brief Parameterized constructor
|
||||
/// \param addr_ Socket address
|
||||
/// \param port_ Socket port (host byte order)
|
||||
SockAddr (in6_addr const &addr_, std::uint16_t port_ = 0);
|
||||
#endif
|
||||
|
||||
/// \brief Copy constructor
|
||||
/// \param that_ Object to copy
|
||||
SockAddr (SockAddr const &that_);
|
||||
@ -57,46 +88,69 @@ public:
|
||||
/// \param that_ Object to move from
|
||||
SockAddr &operator= (SockAddr &&that_);
|
||||
|
||||
/// \param Parameterized constructor
|
||||
/// \param addr_ Address
|
||||
SockAddr (struct sockaddr const &addr_);
|
||||
/// \brief Parameterized constructor
|
||||
/// \param addr_ Address (network byte order)
|
||||
SockAddr (sockaddr_in const &addr_);
|
||||
|
||||
/// \param Parameterized constructor
|
||||
/// \param addr_ Address
|
||||
SockAddr (struct sockaddr_in const &addr_);
|
||||
|
||||
#ifndef __3DS__
|
||||
/// \param Parameterized constructor
|
||||
/// \param addr_ Address
|
||||
SockAddr (struct sockaddr_in6 const &addr_);
|
||||
#ifndef NO_IPV6
|
||||
/// \brief Parameterized constructor
|
||||
/// \param addr_ Address (network byte order)
|
||||
SockAddr (sockaddr_in6 const &addr_);
|
||||
#endif
|
||||
|
||||
/// \param Parameterized constructor
|
||||
/// \param addr_ Address
|
||||
SockAddr (struct sockaddr_storage const &addr_);
|
||||
/// \brief Parameterized constructor
|
||||
/// \param addr_ Address (network byte order)
|
||||
SockAddr (sockaddr_storage const &addr_);
|
||||
|
||||
/// \param sockaddr_in cast operator
|
||||
operator struct sockaddr_in const & () const;
|
||||
/// \brief sockaddr_in cast operator (network byte order)
|
||||
operator sockaddr_in const & () const;
|
||||
|
||||
#ifndef __3DS__
|
||||
/// \param sockaddr_in6 cast operator
|
||||
operator struct sockaddr_in6 const & () const;
|
||||
#ifndef NO_IPV6
|
||||
/// \brief sockaddr_in6 cast operator (network byte order)
|
||||
operator sockaddr_in6 const & () const;
|
||||
#endif
|
||||
|
||||
/// \param sockaddr_storage cast operator
|
||||
operator struct sockaddr_storage const & () const;
|
||||
/// \brief sockaddr_storage cast operator (network byte order)
|
||||
operator sockaddr_storage const & () const;
|
||||
|
||||
/// \param sockaddr* cast operator
|
||||
operator struct sockaddr * ();
|
||||
/// \param sockaddr const* cast operator
|
||||
operator struct sockaddr const * () const;
|
||||
/// \brief sockaddr* cast operator (network byte order)
|
||||
operator sockaddr * ();
|
||||
|
||||
/// \brief Address port
|
||||
/// \brief sockaddr const* cast operator (network byte order)
|
||||
operator sockaddr const * () const;
|
||||
|
||||
/// \brief Equality operator
|
||||
bool operator== (SockAddr const &that_) const;
|
||||
|
||||
/// \brief Comparison operator
|
||||
std::strong_ordering operator<=> (SockAddr const &that_) const;
|
||||
|
||||
/// \brief sockaddr domain
|
||||
Domain domain () const;
|
||||
|
||||
/// \brief sockaddr size
|
||||
socklen_t size () const;
|
||||
|
||||
/// \brief Set address
|
||||
/// \param addr_ Address to set (network byte order)
|
||||
void setAddr (in_addr_t addr_);
|
||||
|
||||
/// \brief Set address
|
||||
/// \param addr_ Address to set (network byte order)
|
||||
void setAddr (in_addr const &addr_);
|
||||
|
||||
#ifndef NO_IPV6
|
||||
/// \brief Set address
|
||||
/// \param addr_ Address to set (network byte order)
|
||||
void setAddr (in6_addr const &addr_);
|
||||
#endif
|
||||
|
||||
/// \brief Address port (host byte order)
|
||||
std::uint16_t port () const;
|
||||
|
||||
/// \brief Set address port
|
||||
/// \param port_ Port to set
|
||||
bool setPort (std::uint16_t port_);
|
||||
/// \param port_ Port to set (host byte order)
|
||||
void setPort (std::uint16_t port_);
|
||||
|
||||
/// \brief Address name
|
||||
/// \param buffer_ Buffer to hold name
|
||||
@ -111,6 +165,6 @@ public:
|
||||
char const *name () const;
|
||||
|
||||
private:
|
||||
/// \brief Address storage
|
||||
struct sockaddr_storage m_addr = {};
|
||||
/// \brief Address storage (network byte order)
|
||||
sockaddr_storage m_addr = {};
|
||||
};
|
||||
|
@ -3,7 +3,7 @@
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2020 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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
|
||||
@ -26,7 +26,7 @@
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
#ifdef NDS
|
||||
#ifdef __NDS__
|
||||
struct pollfd
|
||||
{
|
||||
int fd;
|
||||
@ -34,10 +34,9 @@ struct pollfd
|
||||
int revents;
|
||||
};
|
||||
|
||||
using socklen_t = int;
|
||||
using nfds_t = unsigned int;
|
||||
using nfds_t = unsigned int;
|
||||
|
||||
extern "C" int poll (struct pollfd *fds_, nfds_t nfds_, int timeout_);
|
||||
extern "C" int poll (pollfd *fds_, nfds_t nfds_, int timeout_);
|
||||
|
||||
#define POLLIN (1 << 0)
|
||||
#define POLLPRI (1 << 1)
|
||||
@ -56,6 +55,12 @@ using SharedSocket = std::shared_ptr<Socket>;
|
||||
class Socket
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
eStream = SOCK_STREAM, ///< Stream socket
|
||||
eDatagram = SOCK_DGRAM, ///< Datagram socket
|
||||
};
|
||||
|
||||
/// \brief Poll info
|
||||
struct PollInfo
|
||||
{
|
||||
@ -116,6 +121,18 @@ public:
|
||||
/// \param size_ Buffer size
|
||||
bool setSendBufferSize (std::size_t size_);
|
||||
|
||||
#ifndef __NDS__
|
||||
/// \brief Join multicast group
|
||||
/// \param addr_ Multicast group address
|
||||
/// \param iface_ Interface address
|
||||
bool joinMulticastGroup (SockAddr const &addr_, SockAddr const &iface_);
|
||||
|
||||
/// \brief Drop multicast group
|
||||
/// \param addr_ Multicast group address
|
||||
/// \param iface_ Interface address
|
||||
bool dropMulticastGroup (SockAddr const &addr_, SockAddr const &iface_);
|
||||
#endif
|
||||
|
||||
/// \brief Read data
|
||||
/// \param buffer_ Output buffer
|
||||
/// \param size_ Size to read
|
||||
@ -127,6 +144,12 @@ public:
|
||||
/// \param oob_ Whether to read from out-of-band
|
||||
std::make_signed_t<std::size_t> read (IOBuffer &buffer_, bool oob_ = false);
|
||||
|
||||
/// \brief Read data
|
||||
/// \param buffer_ Output buffer
|
||||
/// \param size_ Size to read
|
||||
/// \param[out] addr_ Source address
|
||||
std::make_signed_t<std::size_t> readFrom (void *buffer_, std::size_t size_, SockAddr &addr_);
|
||||
|
||||
/// \brief Write data
|
||||
/// \param buffer_ Input buffer
|
||||
/// \param size_ Size to write
|
||||
@ -137,13 +160,21 @@ public:
|
||||
/// \param size_ Size to write
|
||||
std::make_signed_t<std::size_t> write (IOBuffer &buffer_);
|
||||
|
||||
/// \brief Write data
|
||||
/// \param buffer_ Input buffer
|
||||
/// \param size_ Size to write
|
||||
/// \param[out] addr_ Destination address
|
||||
std::make_signed_t<std::size_t>
|
||||
writeTo (void const *buffer_, std::size_t size_, SockAddr const &addr_);
|
||||
|
||||
/// \brief Local name
|
||||
SockAddr const &sockName () const;
|
||||
/// \brief Peer name
|
||||
SockAddr const &peerName () const;
|
||||
|
||||
/// \brief Create socket
|
||||
static UniqueSocket create ();
|
||||
/// \param type_ Socket type
|
||||
static UniqueSocket create (Type type_);
|
||||
|
||||
/// \brief Poll sockets
|
||||
/// \param info_ Poll info
|
||||
|
102
source/fs.cpp
102
source/fs.cpp
@ -3,7 +3,7 @@
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2020 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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
|
||||
@ -20,13 +20,26 @@
|
||||
|
||||
#include "fs.h"
|
||||
#include "IOAbstraction.h"
|
||||
#include "ioBuffer.h"
|
||||
|
||||
#include <gsl/pointers>
|
||||
#include <gsl/util>
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cinttypes>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <dirent.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#if defined(NDS) || defined(__3DS__) || defined(__SWITCH__) || defined(__WIIU__)
|
||||
#if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__) || defined(__WIIU__)
|
||||
#define getline __getline
|
||||
#endif
|
||||
|
||||
@ -39,7 +52,7 @@ std::string fs::printSize (std::uint64_t const size_)
|
||||
constexpr std::uint64_t const PiB = 1024 * TiB;
|
||||
constexpr std::uint64_t const EiB = 1024 * PiB;
|
||||
|
||||
char buffer[64] = {};
|
||||
std::array<char, 64> buffer{};
|
||||
|
||||
for (auto const &[name, bin] : {
|
||||
// clang-format off
|
||||
@ -57,30 +70,32 @@ std::string fs::printSize (std::uint64_t const size_)
|
||||
if (size_ >= 100 * bin)
|
||||
{
|
||||
// >= 100, print xxxXiB
|
||||
std::sprintf (buffer, "%" PRIu64 "%s", whole, name);
|
||||
return buffer;
|
||||
std::size_t const size = std::sprintf (buffer.data (), "%" PRIu64 "%s", whole, name);
|
||||
return {buffer.data (), size};
|
||||
}
|
||||
|
||||
// get the fractional portion of the number
|
||||
auto const frac = size_ - whole * bin;
|
||||
auto const frac = size_ - (whole * bin);
|
||||
if (size_ >= 10 * bin)
|
||||
{
|
||||
// >= 10, print xx.xXiB
|
||||
std::sprintf (buffer, "%" PRIu64 ".%" PRIu64 "%s", whole, frac * 10 / bin, name);
|
||||
return buffer;
|
||||
std::size_t const size = std::sprintf (
|
||||
buffer.data (), "%" PRIu64 ".%" PRIu64 "%s", whole, frac * 10 / bin, name);
|
||||
return {buffer.data (), size};
|
||||
}
|
||||
|
||||
if (size_ >= 1000 * (bin / KiB))
|
||||
{
|
||||
// >= 1000 of lesser bin, print x.xxXiB
|
||||
std::sprintf (buffer, "%" PRIu64 ".%02" PRIu64 "%s", whole, frac * 100 / bin, name);
|
||||
return buffer;
|
||||
std::size_t const size = std::sprintf (
|
||||
buffer.data (), "%" PRIu64 ".%02" PRIu64 "%s", whole, frac * 100 / bin, name);
|
||||
return {buffer.data (), size};
|
||||
}
|
||||
}
|
||||
|
||||
// < 1KiB, just print the number
|
||||
std::sprintf (buffer, "%" PRIu64 "B", size_);
|
||||
return buffer;
|
||||
std::size_t const size = std::sprintf (buffer.data (), "%" PRIu64 "B", size_);
|
||||
return {buffer.data (), size};
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@ -107,26 +122,24 @@ fs::File::operator FILE * () const
|
||||
|
||||
void fs::File::setBufferSize (std::size_t const size_)
|
||||
{
|
||||
if (m_bufferSize != size_)
|
||||
{
|
||||
m_buffer = std::make_unique<char[]> (size_);
|
||||
m_bufferSize = size_;
|
||||
}
|
||||
if (m_buffer.size () != size_)
|
||||
m_buffer.resize (size_);
|
||||
|
||||
if (m_fp)
|
||||
std::setvbuf (m_fp.get (), m_buffer.get (), _IOFBF, m_bufferSize);
|
||||
(void)std::setvbuf (m_fp.get (), m_buffer.data (), _IOFBF, m_buffer.size ());
|
||||
}
|
||||
|
||||
bool fs::File::open (char const *const path_, char const *const mode_)
|
||||
bool fs::File::open (gsl::not_null<char const *> const path_,
|
||||
gsl::not_null<char const *> const mode_)
|
||||
{
|
||||
auto const fp = IOAbstraction::fopen (path_, mode_);
|
||||
gsl::owner<FILE *> fp = IOAbstraction::fopen (path_, mode_);
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
m_fp = std::unique_ptr<std::FILE, int (*) (std::FILE *)> (fp, &std::fclose);
|
||||
|
||||
if (m_buffer)
|
||||
std::setvbuf (m_fp.get (), m_buffer.get (), _IOFBF, m_bufferSize);
|
||||
if (!m_buffer.empty ())
|
||||
(void)std::setvbuf (m_fp.get (), m_buffer.data (), _IOFBF, m_buffer.size ());
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -136,17 +149,27 @@ void fs::File::close ()
|
||||
m_fp.reset ();
|
||||
}
|
||||
|
||||
std::make_signed_t<std::size_t> fs::File::seek (std::size_t const pos_, int const origin_)
|
||||
std::make_signed_t<std::size_t> fs::File::seek (std::make_signed_t<std::size_t> const pos_,
|
||||
int const origin_)
|
||||
{
|
||||
return IOAbstraction::fseek (m_fp.get (), pos_, origin_);
|
||||
}
|
||||
|
||||
std::make_signed_t<std::size_t> fs::File::read (void *const buffer_, std::size_t const size_)
|
||||
std::make_signed_t<std::size_t> fs::File::read (gsl::not_null<void *> const buffer_,
|
||||
std::size_t const size_)
|
||||
{
|
||||
assert (buffer_);
|
||||
assert (size_ > 0);
|
||||
|
||||
return IOAbstraction::fread (buffer_, 1, size_, m_fp.get ());
|
||||
auto const rc = IOAbstraction::fread (buffer_, 1, size_, m_fp.get ());
|
||||
if (rc == 0)
|
||||
{
|
||||
if (std::feof (m_fp.get ()))
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return gsl::narrow_cast<std::make_signed_t<std::size_t>> (rc);
|
||||
}
|
||||
|
||||
std::make_signed_t<std::size_t> fs::File::read (IOBuffer &buffer_)
|
||||
@ -177,37 +200,41 @@ std::string_view fs::File::readLine ()
|
||||
}
|
||||
|
||||
if (rc > 0)
|
||||
return std::string_view (m_lineBuffer, rc);
|
||||
return {m_lineBuffer, gsl::narrow_cast<std::size_t> (rc)};
|
||||
}
|
||||
}
|
||||
|
||||
bool fs::File::readAll (void *const buffer_, std::size_t const size_)
|
||||
bool fs::File::readAll (gsl::not_null<void *> const buffer_, std::size_t const size_)
|
||||
{
|
||||
assert (buffer_);
|
||||
assert (size_ > 0);
|
||||
|
||||
auto p = static_cast<char *> (buffer_);
|
||||
auto const p = static_cast<char *> (buffer_.get ());
|
||||
|
||||
std::size_t bytes = 0;
|
||||
while (bytes < size_)
|
||||
{
|
||||
auto const rc = read (p, size_ - bytes);
|
||||
auto const rc = read (p + bytes, size_ - bytes);
|
||||
if (rc <= 0)
|
||||
return false;
|
||||
|
||||
p += rc;
|
||||
bytes += rc;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::make_signed_t<std::size_t> fs::File::write (void const *const buffer_, std::size_t const size_)
|
||||
std::make_signed_t<std::size_t> fs::File::write (gsl::not_null<void const *> const buffer_,
|
||||
std::size_t const size_)
|
||||
{
|
||||
assert (buffer_);
|
||||
assert (size_ > 0);
|
||||
|
||||
return IOAbstraction::fwrite (buffer_, 1, size_, m_fp.get ());
|
||||
auto const rc = IOAbstraction::fwrite (buffer_, 1, size_, m_fp.get ());
|
||||
if (rc == 0)
|
||||
return -1;
|
||||
|
||||
return gsl::narrow_cast<std::make_signed_t<std::size_t>> (rc);
|
||||
}
|
||||
|
||||
std::make_signed_t<std::size_t> fs::File::write (IOBuffer &buffer_)
|
||||
@ -221,21 +248,20 @@ std::make_signed_t<std::size_t> fs::File::write (IOBuffer &buffer_)
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool fs::File::writeAll (void const *const buffer_, std::size_t const size_)
|
||||
bool fs::File::writeAll (gsl::not_null<void const *> const buffer_, std::size_t const size_)
|
||||
{
|
||||
assert (buffer_);
|
||||
assert (size_ > 0);
|
||||
|
||||
auto p = static_cast<char const *> (buffer_);
|
||||
auto const p = static_cast<char const *> (buffer_.get ());
|
||||
|
||||
std::size_t bytes = 0;
|
||||
while (bytes < size_)
|
||||
{
|
||||
auto const rc = write (p, size_ - bytes);
|
||||
auto const rc = write (p + bytes, size_ - bytes);
|
||||
if (rc <= 0)
|
||||
return false;
|
||||
|
||||
p += rc;
|
||||
bytes += rc;
|
||||
}
|
||||
|
||||
@ -261,7 +287,7 @@ fs::Dir::operator DIR * () const
|
||||
return m_dp.get ();
|
||||
}
|
||||
|
||||
bool fs::Dir::open (char const *const path_)
|
||||
bool fs::Dir::open (gsl::not_null<char const *> const path_)
|
||||
{
|
||||
auto const dp = IOAbstraction::opendir (path_);
|
||||
if (!dp)
|
||||
@ -276,7 +302,7 @@ void fs::Dir::close ()
|
||||
m_dp.reset ();
|
||||
}
|
||||
|
||||
struct dirent *fs::Dir::read ()
|
||||
dirent *fs::Dir::read ()
|
||||
{
|
||||
errno = 0;
|
||||
return IOAbstraction::readdir (m_dp.get ());
|
||||
|
@ -3,7 +3,7 @@
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2022 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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
|
||||
@ -22,11 +22,23 @@
|
||||
|
||||
#include "fs.h"
|
||||
#include "log.h"
|
||||
#include "platform.h"
|
||||
|
||||
#include <gsl/pointers>
|
||||
|
||||
#include <sys/stat.h>
|
||||
using stat_t = struct stat;
|
||||
|
||||
#include <limits>
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -36,14 +48,14 @@ constexpr std::uint16_t DEFAULT_PORT = 21;
|
||||
constexpr std::uint16_t DEFAULT_PORT = 5000;
|
||||
#endif
|
||||
|
||||
bool mkdirParent (std::string const &path_)
|
||||
bool mkdirParent (std::string_view const path_)
|
||||
{
|
||||
auto pos = path_.find_first_of ('/');
|
||||
while (pos != std::string::npos)
|
||||
{
|
||||
auto const dir = path_.substr (0, pos);
|
||||
auto const dir = std::string (path_.substr (0, pos));
|
||||
|
||||
struct stat st;
|
||||
stat_t st{};
|
||||
auto const rc = ::stat (dir.c_str (), &st);
|
||||
if (rc < 0 && errno != ENOENT)
|
||||
return false;
|
||||
@ -61,14 +73,13 @@ bool mkdirParent (std::string const &path_)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string strip (std::string const &str_)
|
||||
std::string_view strip (std::string_view const str_)
|
||||
{
|
||||
auto const start = str_.find_first_not_of (" \t");
|
||||
if (start == std::string::npos)
|
||||
return {};
|
||||
|
||||
auto const end = str_.find_last_not_of (" \t");
|
||||
|
||||
if (end == std::string::npos)
|
||||
return str_.substr (start);
|
||||
|
||||
@ -76,37 +87,21 @@ std::string strip (std::string const &str_)
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool parseInt (T &out_, std::string const &val_)
|
||||
bool parseInt (T &out_, std::string_view const val_)
|
||||
{
|
||||
T val = 0;
|
||||
|
||||
for (auto const &c : val_)
|
||||
auto const rc = std::from_chars (val_.data (), val_.data () + val_.size (), out_);
|
||||
if (rc.ec != std::errc{})
|
||||
{
|
||||
if (!std::isdigit (c))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::numeric_limits<T>::max () / 10 < val)
|
||||
{
|
||||
errno = EOVERFLOW;
|
||||
return false;
|
||||
}
|
||||
|
||||
val *= 10;
|
||||
|
||||
auto const v = c - '0';
|
||||
if (std::numeric_limits<T>::max () - v < val)
|
||||
{
|
||||
errno = EOVERFLOW;
|
||||
return false;
|
||||
}
|
||||
|
||||
val += v;
|
||||
errno = static_cast<int> (rc.ec);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rc.ptr != val_.data () + val_.size ())
|
||||
{
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
out_ = val;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -123,7 +118,7 @@ UniqueFtpConfig FtpConfig::create ()
|
||||
return UniqueFtpConfig (new FtpConfig ());
|
||||
}
|
||||
|
||||
UniqueFtpConfig FtpConfig::load (char const *const path_)
|
||||
UniqueFtpConfig FtpConfig::load (gsl::not_null<gsl::czstring> const path_)
|
||||
{
|
||||
auto config = create ();
|
||||
|
||||
@ -143,8 +138,8 @@ UniqueFtpConfig FtpConfig::load (char const *const path_)
|
||||
continue;
|
||||
}
|
||||
|
||||
auto const key = strip (line.substr (0, pos));
|
||||
auto const val = strip (line.substr (pos + 1));
|
||||
auto const key = strip (std::string_view (line).substr (0, pos));
|
||||
auto const val = strip (std::string_view (line).substr (pos + 1));
|
||||
if (key.empty () || val.empty ())
|
||||
{
|
||||
error ("Ignoring '%s'\n", line.c_str ());
|
||||
@ -165,7 +160,9 @@ UniqueFtpConfig FtpConfig::load (char const *const path_)
|
||||
else if (val == "1")
|
||||
config->m_getMTime = true;
|
||||
else
|
||||
error ("Invalid value for mtime: %s\n", val.c_str ());
|
||||
error ("Invalid value for mtime: %.*s\n",
|
||||
gsl::narrow_cast<int> (val.size ()),
|
||||
val.data ());
|
||||
}
|
||||
#endif
|
||||
#ifdef __SWITCH__
|
||||
@ -176,7 +173,9 @@ UniqueFtpConfig FtpConfig::load (char const *const path_)
|
||||
else if (val == "1")
|
||||
config->m_enableAP = true;
|
||||
else
|
||||
error ("Invalid value for ap: %s\n", val.c_str ());
|
||||
error ("Invalid value for ap: %.*s\n",
|
||||
gsl::narrow_cast<int> (val.size ()),
|
||||
val.data ());
|
||||
}
|
||||
else if (key == "ssid")
|
||||
config->m_ssid = val;
|
||||
@ -190,16 +189,16 @@ UniqueFtpConfig FtpConfig::load (char const *const path_)
|
||||
return config;
|
||||
}
|
||||
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
std::scoped_lock<platform::Mutex> FtpConfig::lockGuard ()
|
||||
{
|
||||
return std::scoped_lock<platform::Mutex> (m_lock);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool FtpConfig::save (char const *const path_)
|
||||
bool FtpConfig::save (gsl::not_null<gsl::czstring> const path_)
|
||||
{
|
||||
if (!mkdirParent (path_))
|
||||
if (!mkdirParent (path_.get ()))
|
||||
return false;
|
||||
|
||||
auto fp = fs::File ();
|
||||
@ -207,21 +206,21 @@ bool FtpConfig::save (char const *const path_)
|
||||
return false;
|
||||
|
||||
if (!m_user.empty ())
|
||||
std::fprintf (fp, "user=%s\n", m_user.c_str ());
|
||||
(void)std::fprintf (fp, "user=%s\n", m_user.c_str ());
|
||||
if (!m_pass.empty ())
|
||||
std::fprintf (fp, "pass=%s\n", m_pass.c_str ());
|
||||
std::fprintf (fp, "port=%u\n", m_port);
|
||||
(void)std::fprintf (fp, "pass=%s\n", m_pass.c_str ());
|
||||
(void)std::fprintf (fp, "port=%u\n", m_port);
|
||||
|
||||
#ifdef __3DS__
|
||||
std::fprintf (fp, "mtime=%u\n", m_getMTime);
|
||||
(void)std::fprintf (fp, "mtime=%u\n", m_getMTime);
|
||||
#endif
|
||||
|
||||
#ifdef __SWITCH__
|
||||
std::fprintf (fp, "ap=%u\n", m_enableAP);
|
||||
(void)std::fprintf (fp, "ap=%u\n", m_enableAP);
|
||||
if (!m_ssid.empty ())
|
||||
std::fprintf (fp, "ssid=%s\n", m_ssid.c_str ());
|
||||
(void)std::fprintf (fp, "ssid=%s\n", m_ssid.c_str ());
|
||||
if (!m_passphrase.empty ())
|
||||
std::fprintf (fp, "passphrase=%s\n", m_passphrase.c_str ());
|
||||
(void)std::fprintf (fp, "passphrase=%s\n", m_passphrase.c_str ());
|
||||
#endif
|
||||
|
||||
return true;
|
||||
@ -237,6 +236,11 @@ std::string const &FtpConfig::pass () const
|
||||
return m_pass;
|
||||
}
|
||||
|
||||
std::string const &FtpConfig::hostname () const
|
||||
{
|
||||
return m_hostname;
|
||||
}
|
||||
|
||||
std::uint16_t FtpConfig::port () const
|
||||
{
|
||||
return m_port;
|
||||
@ -266,19 +270,24 @@ std::string const &FtpConfig::passphrase () const
|
||||
}
|
||||
#endif
|
||||
|
||||
void FtpConfig::setUser (std::string const &user_)
|
||||
void FtpConfig::setUser (std::string user_)
|
||||
{
|
||||
m_user = user_.substr (0, user_.find_first_of ('\0'));
|
||||
m_user = std::move (user_);
|
||||
}
|
||||
|
||||
void FtpConfig::setPass (std::string const &pass_)
|
||||
void FtpConfig::setPass (std::string pass_)
|
||||
{
|
||||
m_pass = pass_.substr (0, pass_.find_first_of ('\0'));
|
||||
m_pass = std::move (pass_);
|
||||
}
|
||||
|
||||
bool FtpConfig::setPort (std::string const &port_)
|
||||
void FtpConfig::setHostname (std::string hostname_)
|
||||
{
|
||||
std::uint16_t parsed;
|
||||
m_hostname = std::move (hostname_);
|
||||
}
|
||||
|
||||
bool FtpConfig::setPort (std::string_view const port_)
|
||||
{
|
||||
std::uint16_t parsed{};
|
||||
if (!parseInt (parsed, port_))
|
||||
return false;
|
||||
|
||||
@ -294,7 +303,7 @@ bool FtpConfig::setPort (std::uint16_t const port_)
|
||||
errno = EPERM;
|
||||
return false;
|
||||
}
|
||||
#elif defined(NDS) || defined(__3DS__)
|
||||
#elif defined(__NDS__) || defined(__3DS__)
|
||||
// 3DS is allowed < 1024, but not 0
|
||||
// NDS is allowed < 1024, but 0 crashes the app
|
||||
if (port_ == 0)
|
||||
@ -321,12 +330,12 @@ void FtpConfig::setEnableAP (bool const enable_)
|
||||
m_enableAP = enable_;
|
||||
}
|
||||
|
||||
void FtpConfig::setSSID (std::string const &ssid_)
|
||||
void FtpConfig::setSSID (std::string_view const ssid_)
|
||||
{
|
||||
m_ssid = ssid_.substr (0, ssid_.find_first_of ('\0'));
|
||||
}
|
||||
|
||||
void FtpConfig::setPassphrase (std::string const &passphrase_)
|
||||
void FtpConfig::setPassphrase (std::string_view const passphrase_)
|
||||
{
|
||||
m_passphrase = passphrase_.substr (0, passphrase_.find_first_of ('\0'));
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2023 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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
|
||||
@ -21,36 +21,62 @@
|
||||
#include "ftpServer.h"
|
||||
|
||||
#include "fs.h"
|
||||
#ifndef __WIIU__
|
||||
#include "licenses.h"
|
||||
#endif
|
||||
#include "ftpConfig.h"
|
||||
#include "ftpSession.h"
|
||||
#include "log.h"
|
||||
#include "platform.h"
|
||||
#include "sockAddr.h"
|
||||
#include "socket.h"
|
||||
|
||||
#ifndef __WIIU__
|
||||
#include "imgui.h"
|
||||
#include "licenses.h"
|
||||
#endif
|
||||
#ifndef __NDS__
|
||||
#include "mdns.h"
|
||||
#endif
|
||||
|
||||
#ifdef NDS
|
||||
#ifdef __NDS__
|
||||
#include <dswifi9.h>
|
||||
#endif
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#ifdef __3DS__
|
||||
#include <citro3d.h>
|
||||
#endif
|
||||
|
||||
#ifndef CLASSIC
|
||||
#include <imgui.h>
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
#include <curl/easy.h>
|
||||
#include <curl/multi.h>
|
||||
#ifndef NDEBUG
|
||||
#include <curl/curl.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <sys/statvfs.h>
|
||||
#include <unistd.h>
|
||||
using statvfs_t = struct statvfs;
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cctype>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
#ifdef NDS
|
||||
#ifdef __NDS__
|
||||
#define LOCKED(x) x
|
||||
#else
|
||||
#define LOCKED(x) \
|
||||
@ -66,7 +92,12 @@ namespace
|
||||
/// \brief Application start time
|
||||
auto const s_startTime = std::time (nullptr);
|
||||
|
||||
#ifndef NDS
|
||||
#ifdef __3DS__
|
||||
/// \brief Timezone offset in seconds (only used on 3DS)
|
||||
int s_tzOffset = 0;
|
||||
#endif
|
||||
|
||||
#ifndef __NDS__
|
||||
/// \brief Mutex for s_freeSpace
|
||||
platform::Mutex s_lock;
|
||||
#endif
|
||||
@ -76,21 +107,33 @@ std::string s_freeSpace;
|
||||
|
||||
#ifndef CLASSIC
|
||||
#ifndef NDEBUG
|
||||
std::string printable (char *const data_, std::size_t const size_)
|
||||
std::string printable (std::string_view const data_)
|
||||
{
|
||||
std::string result;
|
||||
result.reserve (size_);
|
||||
|
||||
for (std::size_t i = 0; i < size_; ++i)
|
||||
unsigned count = 0;
|
||||
for (auto const &c : data_)
|
||||
{
|
||||
if (std::isprint (data_[i]) || std::isspace (data_[i]))
|
||||
result.push_back (data_[i]);
|
||||
if (c != '%' && (std::isprint (c) || std::isspace (c)))
|
||||
++count;
|
||||
else
|
||||
count += 3;
|
||||
}
|
||||
|
||||
std::string result;
|
||||
result.reserve (count);
|
||||
|
||||
for (auto const &c : data_)
|
||||
{
|
||||
if (c != '%' && (std::isprint (c) || std::isspace (c)))
|
||||
result.push_back (c);
|
||||
else
|
||||
{
|
||||
char buffer[5];
|
||||
std::snprintf (
|
||||
buffer, sizeof (buffer), "%%%02u", static_cast<unsigned char> (data_[i]));
|
||||
result += buffer;
|
||||
result.push_back ('%');
|
||||
|
||||
auto const upper = (static_cast<unsigned char> (c) >> 4u) & 0xF;
|
||||
auto const lower = (static_cast<unsigned char> (c) >> 0u) & 0xF;
|
||||
|
||||
result.push_back (gsl::narrow_cast<char> (upper < 10 ? upper + '0' : upper + 'A' - 10));
|
||||
result.push_back (gsl::narrow_cast<char> (lower < 10 ? lower + '0' : lower + 'A' - 10));
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,9 +146,10 @@ int curlDebug (CURL *const handle_,
|
||||
std::size_t const size_,
|
||||
void *const user_)
|
||||
{
|
||||
(void)handle_;
|
||||
(void)user_;
|
||||
|
||||
auto const text = printable (data_, size_);
|
||||
auto const text = printable (std::string_view (data_, size_));
|
||||
|
||||
switch (type_)
|
||||
{
|
||||
@ -167,7 +211,7 @@ FtpServer::~FtpServer ()
|
||||
{
|
||||
m_quit = true;
|
||||
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
m_thread.join ();
|
||||
#endif
|
||||
|
||||
@ -184,23 +228,36 @@ FtpServer::~FtpServer ()
|
||||
#endif
|
||||
}
|
||||
|
||||
FtpServer::FtpServer (UniqueFtpConfig config_) : m_config (std::move (config_)), m_quit (false)
|
||||
FtpServer::FtpServer (UniqueFtpConfig config_)
|
||||
: m_config (std::move (config_))
|
||||
#ifndef CLASSIC
|
||||
,
|
||||
m_hostnameSetting (m_config->hostname ())
|
||||
#endif
|
||||
{
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
mdns::setHostname (m_config->hostname ());
|
||||
|
||||
m_thread = platform::Thread (std::bind (&FtpServer::threadFunc, this));
|
||||
#endif
|
||||
|
||||
#ifdef __3DS__
|
||||
s64 tzOffsetMinutes;
|
||||
if (R_SUCCEEDED (svcGetSystemInfo (&tzOffsetMinutes, 0x10000, 0x103)))
|
||||
s_tzOffset = tzOffsetMinutes * 60;
|
||||
#endif
|
||||
}
|
||||
|
||||
void FtpServer::draw ()
|
||||
{
|
||||
#ifdef NDS
|
||||
#ifdef __NDS__
|
||||
loop ();
|
||||
#endif
|
||||
|
||||
#ifdef CLASSIC
|
||||
{
|
||||
char port[7];
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
auto const lock = std::scoped_lock (m_lock);
|
||||
#endif
|
||||
if (m_socket)
|
||||
@ -214,7 +271,7 @@ void FtpServer::draw ()
|
||||
m_socket ? port : "");
|
||||
#endif
|
||||
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
char timeBuffer[16];
|
||||
auto const now = std::time (nullptr);
|
||||
std::strftime (timeBuffer, sizeof (timeBuffer), "%H:%M:%S", std::localtime (&now));
|
||||
@ -227,7 +284,7 @@ void FtpServer::draw ()
|
||||
}
|
||||
|
||||
{
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
auto const lock = std::scoped_lock (s_lock);
|
||||
#endif
|
||||
if (!s_freeSpace.empty ())
|
||||
@ -244,7 +301,7 @@ void FtpServer::draw ()
|
||||
}
|
||||
|
||||
{
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
auto const lock = std::scoped_lock (m_lock);
|
||||
#endif
|
||||
#ifndef NO_CONSOLE
|
||||
@ -274,17 +331,17 @@ void FtpServer::draw ()
|
||||
ImGui::SetNextWindowSize (ImVec2 (width, height));
|
||||
#endif
|
||||
{
|
||||
char title[64];
|
||||
std::array<char, 64> title{};
|
||||
|
||||
{
|
||||
auto const serverLock = std::scoped_lock (m_lock);
|
||||
std::snprintf (title,
|
||||
sizeof (title),
|
||||
std::snprintf (title.data (),
|
||||
title.size (),
|
||||
STATUS_STRING " %s###ftpd",
|
||||
m_socket ? m_name.c_str () : "Waiting for WiFi...");
|
||||
}
|
||||
|
||||
ImGui::Begin (title,
|
||||
ImGui::Begin (title.data (),
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize
|
||||
#ifndef __3DS__
|
||||
@ -332,6 +389,11 @@ void FtpServer::draw ()
|
||||
#endif
|
||||
}
|
||||
|
||||
bool FtpServer::quit ()
|
||||
{
|
||||
return m_quit;
|
||||
}
|
||||
|
||||
UniqueFtpServer FtpServer::create ()
|
||||
{
|
||||
updateFreeSpace ();
|
||||
@ -343,7 +405,7 @@ UniqueFtpServer FtpServer::create ()
|
||||
|
||||
std::string FtpServer::getFreeSpace ()
|
||||
{
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
auto const lock = std::scoped_lock (s_lock);
|
||||
#endif
|
||||
return s_freeSpace;
|
||||
@ -351,8 +413,8 @@ std::string FtpServer::getFreeSpace ()
|
||||
|
||||
void FtpServer::updateFreeSpace ()
|
||||
{
|
||||
struct statvfs st;
|
||||
#if defined(NDS) || defined(__3DS__) || defined(__SWITCH__)
|
||||
statvfs_t st = {};
|
||||
#if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__)
|
||||
if (::statvfs ("sdmc:/", &st) != 0)
|
||||
#else
|
||||
if (::statvfs ("/", &st) != 0)
|
||||
@ -361,7 +423,7 @@ void FtpServer::updateFreeSpace ()
|
||||
|
||||
auto freeSpace = fs::printSize (static_cast<std::uint64_t> (st.f_bsize) * st.f_bfree);
|
||||
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
auto const lock = std::scoped_lock (s_lock);
|
||||
#endif
|
||||
if (freeSpace != s_freeSpace)
|
||||
@ -373,6 +435,13 @@ std::time_t FtpServer::startTime ()
|
||||
return s_startTime;
|
||||
}
|
||||
|
||||
#ifdef __3DS__
|
||||
int FtpServer::tzOffset ()
|
||||
{
|
||||
return s_tzOffset;
|
||||
}
|
||||
#endif
|
||||
|
||||
void FtpServer::handleNetworkFound ()
|
||||
{
|
||||
SockAddr addr;
|
||||
@ -382,7 +451,7 @@ void FtpServer::handleNetworkFound ()
|
||||
std::uint16_t port;
|
||||
|
||||
{
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
auto const lock = m_config->lockGuard ();
|
||||
#endif
|
||||
port = m_config->port ();
|
||||
@ -390,7 +459,7 @@ void FtpServer::handleNetworkFound ()
|
||||
|
||||
addr.setPort (port);
|
||||
|
||||
auto socket = Socket::create ();
|
||||
auto socket = Socket::create (Socket::eStream);
|
||||
if (!socket)
|
||||
return;
|
||||
|
||||
@ -412,6 +481,14 @@ void FtpServer::handleNetworkFound ()
|
||||
info ("Started server at %s\n", m_name.c_str ());
|
||||
|
||||
LOCKED (m_socket = std::move (socket));
|
||||
|
||||
#ifndef __NDS__
|
||||
socket = mdns::createSocket ();
|
||||
if (!socket)
|
||||
return;
|
||||
|
||||
LOCKED (m_mdnsSocket = std::move (socket));
|
||||
#endif
|
||||
}
|
||||
|
||||
void FtpServer::handleNetworkLost ()
|
||||
@ -423,9 +500,15 @@ void FtpServer::handleNetworkLost ()
|
||||
}
|
||||
|
||||
{
|
||||
// destroy command socket
|
||||
UniqueSocket sock;
|
||||
|
||||
// destroy command socket
|
||||
LOCKED (sock = std::move (m_socket));
|
||||
|
||||
#ifndef __NDS__
|
||||
// destroy mDNS socket
|
||||
LOCKED (sock = std::move (m_mdnsSocket));
|
||||
#endif
|
||||
}
|
||||
|
||||
info ("Stopped server at %s\n", m_name.c_str ());
|
||||
@ -450,7 +533,7 @@ void FtpServer::showMenu ()
|
||||
|
||||
if (ImGui::MenuItem ("Upload Log"))
|
||||
{
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
auto const lock = std::scoped_lock (m_lock);
|
||||
#endif
|
||||
if (!m_uploadLogCurlM)
|
||||
@ -462,11 +545,6 @@ void FtpServer::showMenu ()
|
||||
|
||||
auto const handle = curl_easy_init ();
|
||||
|
||||
#ifdef __3DS__
|
||||
// 3DS CA fails peer verification, so add CA here
|
||||
curl_easy_setopt (handle, CURLOPT_CAINFO, "romfs:/sni.cloudflaressl.com.ca");
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
curl_easy_setopt (handle, CURLOPT_DEBUGFUNCTION, &curlDebug);
|
||||
curl_easy_setopt (handle, CURLOPT_DEBUGDATA, nullptr);
|
||||
@ -479,9 +557,9 @@ void FtpServer::showMenu ()
|
||||
curl_easy_setopt (handle, CURLOPT_WRITEDATA, &m_uploadLogResult);
|
||||
|
||||
// set headers
|
||||
static char contentType[] = "Content-Type: multipart/form-data";
|
||||
static char contentType[] = "Content-Type: text/plain";
|
||||
static curl_slist const headers = {contentType, nullptr};
|
||||
curl_easy_setopt (handle, CURLOPT_URL, "https://hastebin.com/documents");
|
||||
curl_easy_setopt (handle, CURLOPT_URL, "https://pastie.io/documents");
|
||||
curl_easy_setopt (handle, CURLOPT_HTTPHEADER, &headers);
|
||||
|
||||
// set form data
|
||||
@ -500,9 +578,16 @@ void FtpServer::showMenu ()
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator ();
|
||||
|
||||
if (ImGui::MenuItem ("About"))
|
||||
m_showAbout = true;
|
||||
|
||||
ImGui::Separator ();
|
||||
|
||||
if (ImGui::MenuItem ("Quit"))
|
||||
m_quit = true;
|
||||
|
||||
ImGui::EndMenu ();
|
||||
}
|
||||
ImGui::EndMenuBar ();
|
||||
@ -512,7 +597,7 @@ void FtpServer::showMenu ()
|
||||
{
|
||||
if (!prevShowSettings)
|
||||
{
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
auto const lock = m_config->lockGuard ();
|
||||
#endif
|
||||
|
||||
@ -522,6 +607,9 @@ void FtpServer::showMenu ()
|
||||
m_passSetting = m_config->pass ();
|
||||
m_passSetting.resize (32);
|
||||
|
||||
m_hostnameSetting = m_config->hostname ();
|
||||
m_hostnameSetting.resize (32);
|
||||
|
||||
m_portSetting = m_config->port ();
|
||||
|
||||
#ifdef __3DS__
|
||||
@ -579,6 +667,11 @@ void FtpServer::showSettings ()
|
||||
m_passSetting.size (),
|
||||
ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_Password);
|
||||
|
||||
ImGui::InputText ("Hostname",
|
||||
m_hostnameSetting.data (),
|
||||
m_hostnameSetting.size (),
|
||||
ImGuiInputTextFlags_AutoSelectAll);
|
||||
|
||||
ImGui::InputScalar ("Port",
|
||||
ImGuiDataType_U16,
|
||||
&m_portSetting,
|
||||
@ -645,12 +738,13 @@ void FtpServer::showSettings ()
|
||||
m_showSettings = false;
|
||||
ImGui::CloseCurrentPopup ();
|
||||
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
auto const lock = m_config->lockGuard ();
|
||||
#endif
|
||||
|
||||
m_config->setUser (m_userSetting);
|
||||
m_config->setPass (m_passSetting);
|
||||
m_config->setHostname (m_hostnameSetting);
|
||||
m_config->setPort (m_portSetting);
|
||||
|
||||
#ifdef __3DS__
|
||||
@ -666,11 +760,13 @@ void FtpServer::showSettings ()
|
||||
|
||||
UniqueSocket socket;
|
||||
LOCKED (socket = std::move (m_socket));
|
||||
|
||||
mdns::setHostname (m_hostnameSetting);
|
||||
}
|
||||
|
||||
if (save)
|
||||
{
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
auto const lock = m_config->lockGuard ();
|
||||
#endif
|
||||
if (!m_config->save (FTPDCONFIG))
|
||||
@ -681,9 +777,10 @@ void FtpServer::showSettings ()
|
||||
{
|
||||
static auto const defaults = FtpConfig::create ();
|
||||
|
||||
m_userSetting = defaults->user ();
|
||||
m_passSetting = defaults->pass ();
|
||||
m_portSetting = defaults->port ();
|
||||
m_userSetting = defaults->user ();
|
||||
m_passSetting = defaults->pass ();
|
||||
m_hostnameSetting = defaults->hostname ();
|
||||
m_portSetting = defaults->port ();
|
||||
#ifdef __3DS__
|
||||
m_getMTimeSetting = defaults->getMTime ();
|
||||
#endif
|
||||
@ -723,17 +820,31 @@ void FtpServer::showAbout ()
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize))
|
||||
{
|
||||
ImGui::TextUnformatted (STATUS_STRING);
|
||||
ImGui::TextWrapped ("Copyright © 2023 Michael Theall, Dave Murphy, TuxSH");
|
||||
ImGui::TextWrapped ("Copyright © 2024 Michael Theall, Dave Murphy, TuxSH");
|
||||
ImGui::Separator ();
|
||||
ImGui::Text ("Platform: %s", io.BackendPlatformName);
|
||||
ImGui::Text ("Renderer: %s", io.BackendRendererName);
|
||||
|
||||
#ifdef __3DS__
|
||||
ImGui::Text ("Command Buffer Usage: %.1f%%", 100.0f * C3D_GetCmdBufUsage ());
|
||||
ImGui::Text ("GPU Processing Usage: %.1f%%", 6.0f * C3D_GetProcessingTime ());
|
||||
ImGui::Text ("GPU Drawing Usage: %.1f%%", 6.0f * C3D_GetDrawingTime ());
|
||||
#endif
|
||||
|
||||
if (ImGui::Button ("OK", ImVec2 (100, 0)))
|
||||
{
|
||||
m_showAbout = false;
|
||||
ImGui::CloseCurrentPopup ();
|
||||
}
|
||||
|
||||
ImGui::Separator ();
|
||||
if (ImGui::TreeNode ("Connections"))
|
||||
{
|
||||
for (auto const &session : m_sessions)
|
||||
session->drawConnections ();
|
||||
ImGui::TreePop ();
|
||||
}
|
||||
|
||||
ImGui::Separator ();
|
||||
if (ImGui::TreeNode (g_dearImGuiVersion))
|
||||
{
|
||||
@ -743,7 +854,7 @@ void FtpServer::showAbout ()
|
||||
ImGui::TreePop ();
|
||||
}
|
||||
|
||||
#if defined(NDS)
|
||||
#if defined(__NDS__)
|
||||
#elif defined(__3DS__)
|
||||
if (ImGui::TreeNode (g_libctruVersion))
|
||||
{
|
||||
@ -782,7 +893,7 @@ void FtpServer::showAbout ()
|
||||
{
|
||||
ImGui::TextWrapped ("%s", g_zstdCopyright);
|
||||
ImGui::Separator ();
|
||||
ImGui::TextWrapped ("%s", g_bsdLicense);
|
||||
ImGui::TextWrapped ("%s", g_zstdLicense);
|
||||
ImGui::TreePop ();
|
||||
}
|
||||
#else
|
||||
@ -795,6 +906,24 @@ void FtpServer::showAbout ()
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__NDS__) || defined(__3DS__) || defined(__SWITCH__)
|
||||
if (ImGui::TreeNode (g_globVersion))
|
||||
{
|
||||
ImGui::TextWrapped ("%s", g_globCopyright);
|
||||
ImGui::Separator ();
|
||||
ImGui::TextWrapped ("%s", g_globLicense);
|
||||
ImGui::TreePop ();
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode (g_collateVersion))
|
||||
{
|
||||
ImGui::TextWrapped ("%s", g_collateCopyright);
|
||||
ImGui::Separator ();
|
||||
ImGui::TextWrapped ("%s", g_collateLicense);
|
||||
ImGui::TreePop ();
|
||||
}
|
||||
#endif
|
||||
|
||||
ImGui::EndPopup ();
|
||||
}
|
||||
}
|
||||
@ -854,11 +983,19 @@ void FtpServer::loop ()
|
||||
if (msg->data.result != CURLE_OK)
|
||||
info ("cURL finished with status %d\n", msg->data.result);
|
||||
|
||||
if (m_uploadLogResult.starts_with ("{\"key\":\""))
|
||||
json_error_t err;
|
||||
auto const root = json_loads (m_uploadLogResult.c_str (), 0, &err);
|
||||
if (json_is_object (root))
|
||||
{
|
||||
auto const key = m_uploadLogResult.substr (8, 10);
|
||||
info ("https://hastebin.com/%s\n", key.c_str ());
|
||||
auto const key = json_object_get (root, "key");
|
||||
if (json_is_string (key))
|
||||
info (
|
||||
"Log uploaded to https://pastie.io/%s\n", json_string_value (key));
|
||||
}
|
||||
else
|
||||
error ("Failed to upload log\n");
|
||||
|
||||
json_decref (root);
|
||||
|
||||
curl_multi_remove_handle (m_uploadLogCurlM, m_uploadLogCurl);
|
||||
curl_easy_cleanup (m_uploadLogCurl);
|
||||
@ -898,11 +1035,17 @@ void FtpServer::loop ()
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef __NDS__
|
||||
// poll mDNS socket
|
||||
if (m_socket && m_mdnsSocket)
|
||||
mdns::handleSocket (m_mdnsSocket.get (), m_socket->sockName ());
|
||||
#endif
|
||||
|
||||
{
|
||||
std::vector<UniqueFtpSession> deadSessions;
|
||||
{
|
||||
// remove dead sessions
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
auto const lock = std::scoped_lock (m_lock);
|
||||
#endif
|
||||
auto it = std::begin (m_sessions);
|
||||
@ -926,7 +1069,7 @@ void FtpServer::loop ()
|
||||
if (!FtpSession::poll (m_sessions))
|
||||
handleNetworkLost ();
|
||||
}
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
// avoid busy polling in background thread
|
||||
else
|
||||
platform::Thread::sleep (16ms);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2022 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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
|
||||
@ -22,7 +22,7 @@
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#ifndef __WIIU__
|
||||
#if !defined(__WIIU__) && !defined(CLASSIC)
|
||||
#include "imgui.h"
|
||||
#endif
|
||||
|
||||
@ -77,7 +77,7 @@ struct Message
|
||||
/// \brief Log messages
|
||||
std::vector<Message> s_messages;
|
||||
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
/// \brief Log lock
|
||||
platform::Mutex s_lock;
|
||||
#endif
|
||||
@ -85,7 +85,7 @@ platform::Mutex s_lock;
|
||||
|
||||
void drawLog ()
|
||||
{
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
auto const lock = std::scoped_lock (s_lock);
|
||||
#endif
|
||||
|
||||
@ -166,7 +166,7 @@ void drawLog ()
|
||||
#ifndef CLASSIC
|
||||
std::string getLog ()
|
||||
{
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
auto const lock = std::scoped_lock (s_lock);
|
||||
#endif
|
||||
|
||||
@ -204,6 +204,8 @@ void debug (char const *const fmt_, ...)
|
||||
va_start (ap, fmt_);
|
||||
addLog (DEBUGLOG, fmt_, ap);
|
||||
va_end (ap);
|
||||
#else
|
||||
(void)fmt_;
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -253,7 +255,8 @@ void addLog (LogLevel const level_, char const *const fmt_, va_list ap_)
|
||||
if (level_ == DEBUGLOG)
|
||||
return;
|
||||
#endif
|
||||
#ifndef NDS
|
||||
|
||||
#ifndef __NDS__
|
||||
auto const lock = std::scoped_lock (s_lock);
|
||||
#endif
|
||||
|
||||
@ -296,7 +299,7 @@ void addLog (LogLevel const level_, std::string_view const message_)
|
||||
c = '?';
|
||||
}
|
||||
|
||||
#ifndef NDS
|
||||
#ifndef __NDS__
|
||||
auto const lock = std::scoped_lock (s_lock);
|
||||
#endif
|
||||
#ifndef NDEBUG
|
||||
|
@ -3,7 +3,7 @@
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2022 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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
|
||||
@ -27,13 +27,15 @@
|
||||
#endif
|
||||
|
||||
#ifndef CLASSIC
|
||||
#include <imgui.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
int main (int argc_, char *argv_[])
|
||||
int main ()
|
||||
{
|
||||
#ifndef CLASSIC
|
||||
curl_global_init (CURL_GLOBAL_ALL);
|
||||
@ -58,7 +60,7 @@ int main (int argc_, char *argv_[])
|
||||
|
||||
auto server = FtpServer::create ();
|
||||
|
||||
while (platform::loop ())
|
||||
while (!server->quit () && platform::loop ())
|
||||
{
|
||||
#ifndef NO_CONSOLE
|
||||
server->draw ();
|
||||
|
597
source/mdns.cpp
Normal file
597
source/mdns.cpp
Normal file
@ -0,0 +1,597 @@
|
||||
// ftpd is a server implementation based on the following:
|
||||
// - RFC 959 (https://datatracker.ietf.org/doc/html/rfc959)
|
||||
// - RFC 3659 (https://datatracker.ietf.org/doc/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// ftpd implements mdns based on the following:
|
||||
// - RFC 1035 (https://datatracker.ietf.org/doc/html/rfc1035)
|
||||
// - RFC 6762 (https://datatracker.ietf.org/doc/html/rfc6762)
|
||||
//
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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/>.
|
||||
|
||||
#include "mdns.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "platform.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <chrono>
|
||||
#include <concepts>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static_assert (
|
||||
std::endian::native == std::endian::big || std::endian::native == std::endian::little);
|
||||
|
||||
static_assert (sizeof (in_addr_t) == 4);
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr auto MDNS_TTL = 120;
|
||||
|
||||
SockAddr const s_multicastAddress{inet_addr ("224.0.0.251"), 5353};
|
||||
|
||||
platform::steady_clock::time_point s_lastAnnounce{};
|
||||
platform::steady_clock::time_point s_lastProbe{};
|
||||
|
||||
std::string s_hostname = platform::hostname ();
|
||||
std::string s_hostnameLocal = s_hostname + ".local";
|
||||
|
||||
enum class State
|
||||
{
|
||||
Probe1,
|
||||
Probe2,
|
||||
Probe3,
|
||||
Announce1,
|
||||
Announce2,
|
||||
Complete,
|
||||
Conflict,
|
||||
};
|
||||
|
||||
auto s_state = State::Probe1;
|
||||
|
||||
#if __has_cpp_attribute(__cpp_lib_byteswap)
|
||||
template <std::integral T>
|
||||
using byteswap = std::byteswap<T>;
|
||||
#else
|
||||
template <std::integral T>
|
||||
constexpr T byteswap (T const value_) noexcept
|
||||
{
|
||||
static_assert (std::has_unique_object_representations_v<T>, "T may not have padding bits");
|
||||
auto buffer = std::bit_cast<std::array<std::byte, sizeof (T)>> (value_);
|
||||
std::ranges::reverse (buffer);
|
||||
return std::bit_cast<T> (buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <std::integral T>
|
||||
constexpr T hton (T const value_) noexcept
|
||||
{
|
||||
if constexpr (std::endian::native == std::endian::big)
|
||||
return value_;
|
||||
else
|
||||
return byteswap (value_);
|
||||
}
|
||||
|
||||
template <std::integral T>
|
||||
constexpr T ntoh (T const value_) noexcept
|
||||
{
|
||||
if constexpr (std::endian::native == std::endian::big)
|
||||
return value_;
|
||||
else
|
||||
return byteswap (value_);
|
||||
}
|
||||
|
||||
template <std::integral T, std::integral U>
|
||||
void const *decode (void const *const buffer_, U &size_, T &out_, bool networkToHost_ = true)
|
||||
{
|
||||
if (!buffer_)
|
||||
return nullptr;
|
||||
|
||||
if (size_ < 0 || static_cast<std::make_unsigned_t<T>> (size_) < sizeof (T))
|
||||
return nullptr;
|
||||
|
||||
std::memcpy (&out_, buffer_, sizeof (T));
|
||||
|
||||
if (networkToHost_)
|
||||
out_ = ntoh (out_);
|
||||
|
||||
size_ -= sizeof (T);
|
||||
return static_cast<std::uint8_t const *> (buffer_) + sizeof (T);
|
||||
}
|
||||
|
||||
template <std::integral T>
|
||||
void const *decode (void const *buffer_, T &size_, std::string &out_)
|
||||
{
|
||||
auto p = static_cast<char const *> (buffer_);
|
||||
auto const end = p + size_;
|
||||
|
||||
std::string result;
|
||||
result.reserve (size_);
|
||||
|
||||
while (p < end && *p)
|
||||
{
|
||||
auto const len = *p++;
|
||||
|
||||
// punt on compressed labels
|
||||
if (len & 0xC0)
|
||||
return nullptr;
|
||||
|
||||
if (p + len >= end)
|
||||
return nullptr;
|
||||
|
||||
if (!result.empty ())
|
||||
result.push_back ('.');
|
||||
|
||||
result.insert (std::end (result), p, p + len);
|
||||
p += len;
|
||||
}
|
||||
|
||||
++p;
|
||||
|
||||
out_ = std::move (result);
|
||||
|
||||
size_ = end - p;
|
||||
return p;
|
||||
}
|
||||
|
||||
template <std::integral T, std::integral U>
|
||||
void *encode (void *const buffer_, U &size_, T in_, bool hostToNetwork_ = true)
|
||||
{
|
||||
if (!buffer_)
|
||||
return nullptr;
|
||||
|
||||
if (size_ < sizeof (T))
|
||||
return nullptr;
|
||||
|
||||
if (hostToNetwork_)
|
||||
in_ = hton (in_);
|
||||
|
||||
std::memcpy (buffer_, &in_, sizeof (T));
|
||||
|
||||
size_ -= sizeof (T);
|
||||
return static_cast<std::uint8_t *> (buffer_) + sizeof (T);
|
||||
}
|
||||
|
||||
template <std::integral T>
|
||||
void *encode (void *const buffer_, T &size_, std::string const &in_)
|
||||
{
|
||||
// names are limited to 255 bytes
|
||||
if (in_.size () > 0xFF)
|
||||
return nullptr;
|
||||
|
||||
auto p = static_cast<char *> (buffer_);
|
||||
auto const end = p + size_;
|
||||
|
||||
std::string::size_type prev = 0;
|
||||
std::string::size_type pos = 0;
|
||||
while (p < end && pos != std::string::npos)
|
||||
{
|
||||
pos = in_.find ('.', prev);
|
||||
|
||||
auto const label = std::string_view (in_).substr (prev, pos);
|
||||
|
||||
// labels are limited to 63 bytes
|
||||
if (label.size () >= size_ || label.size () > 0x3F)
|
||||
return nullptr;
|
||||
|
||||
p = static_cast<char *> (encode<std::uint8_t> (p, size_, label.size ()));
|
||||
if (!p)
|
||||
return nullptr;
|
||||
|
||||
std::memcpy (p, label.data (), label.size ());
|
||||
|
||||
p += label.size ();
|
||||
|
||||
if (pos != std::string::npos)
|
||||
prev = pos + 1;
|
||||
}
|
||||
|
||||
if (p == end)
|
||||
return nullptr;
|
||||
|
||||
*p++ = 0;
|
||||
|
||||
size_ = end - p;
|
||||
return p;
|
||||
}
|
||||
|
||||
struct DNSHeader
|
||||
{
|
||||
std::uint16_t id{};
|
||||
std::uint16_t flags{};
|
||||
std::uint16_t qdCount{};
|
||||
std::uint16_t anCount{};
|
||||
std::uint16_t nsCount{};
|
||||
std::uint16_t arCount{};
|
||||
|
||||
template <std::integral T>
|
||||
void const *decode (void const *const buffer_, T &size_)
|
||||
{
|
||||
auto in = ::decode (buffer_, size_, id);
|
||||
in = ::decode (buffer_, size_, flags);
|
||||
in = ::decode (buffer_, size_, qdCount);
|
||||
in = ::decode (buffer_, size_, anCount);
|
||||
in = ::decode (buffer_, size_, nsCount);
|
||||
in = ::decode (buffer_, size_, arCount);
|
||||
|
||||
return buffer_;
|
||||
}
|
||||
|
||||
template <std::integral T>
|
||||
void *encode (void *buffer_, T &size_)
|
||||
{
|
||||
buffer_ = ::encode (buffer_, size_, id);
|
||||
buffer_ = ::encode (buffer_, size_, flags);
|
||||
buffer_ = ::encode (buffer_, size_, qdCount);
|
||||
buffer_ = ::encode (buffer_, size_, anCount);
|
||||
buffer_ = ::encode (buffer_, size_, nsCount);
|
||||
buffer_ = ::encode (buffer_, size_, arCount);
|
||||
|
||||
return buffer_;
|
||||
}
|
||||
};
|
||||
|
||||
struct QueryRecord
|
||||
{
|
||||
std::string qname{};
|
||||
std::uint16_t qtype{};
|
||||
std::uint16_t qclass{};
|
||||
|
||||
template <std::integral T>
|
||||
void const *decode (void const *buffer_, T &size_)
|
||||
{
|
||||
buffer_ = ::decode (buffer_, size_, qname);
|
||||
buffer_ = ::decode (buffer_, size_, qtype);
|
||||
buffer_ = ::decode (buffer_, size_, qclass);
|
||||
|
||||
return buffer_;
|
||||
}
|
||||
|
||||
template <std::integral T>
|
||||
void *encode (void *buffer_, T &size_)
|
||||
{
|
||||
buffer_ = ::encode (buffer_, size_, qname);
|
||||
buffer_ = ::encode (buffer_, size_, qtype);
|
||||
buffer_ = ::encode (buffer_, size_, qclass);
|
||||
|
||||
return buffer_;
|
||||
}
|
||||
};
|
||||
|
||||
struct ResourceRecord
|
||||
{
|
||||
std::string rname{};
|
||||
std::uint16_t rtype{};
|
||||
std::uint16_t rclass{};
|
||||
std::uint32_t rttl{};
|
||||
std::uint16_t rlen{};
|
||||
std::vector<std::uint8_t> rdata{};
|
||||
|
||||
template <std::integral T>
|
||||
void const *decode (void const *buffer_, T &size_)
|
||||
{
|
||||
buffer_ = ::decode (buffer_, size_, rname);
|
||||
buffer_ = ::decode (buffer_, size_, rtype);
|
||||
buffer_ = ::decode (buffer_, size_, rclass);
|
||||
buffer_ = ::decode (buffer_, size_, rttl);
|
||||
buffer_ = ::decode (buffer_, size_, rlen);
|
||||
|
||||
return buffer_;
|
||||
}
|
||||
|
||||
template <std::integral T>
|
||||
void *encode (void *buffer_, T &size_)
|
||||
{
|
||||
if (rttl > std::numeric_limits<std::int32_t>::max ())
|
||||
return nullptr;
|
||||
|
||||
buffer_ = ::encode (buffer_, size_, rname);
|
||||
buffer_ = ::encode (buffer_, size_, rtype);
|
||||
buffer_ = ::encode (buffer_, size_, rclass);
|
||||
buffer_ = ::encode (buffer_, size_, rttl);
|
||||
buffer_ = ::encode (buffer_, size_, rlen);
|
||||
|
||||
if (rlen > size_)
|
||||
return nullptr;
|
||||
|
||||
rdata.resize (rlen);
|
||||
std::memcpy (rdata.data (), buffer_, rlen);
|
||||
|
||||
size_ -= rlen;
|
||||
return static_cast<std::uint8_t *> (buffer_) + rlen;
|
||||
}
|
||||
};
|
||||
|
||||
void probe (Socket *const socket_, std::string const &qname_)
|
||||
{
|
||||
std::vector<std::uint8_t> response (65536);
|
||||
auto available = response.size ();
|
||||
|
||||
auto out = DNSHeader{.qdCount = 1}.encode (response.data (), available);
|
||||
out = QueryRecord{.qname = qname_, .qtype = 255, .qclass = 1}.encode (out, available);
|
||||
|
||||
if (!out)
|
||||
return;
|
||||
|
||||
info ("Probe mDNS %s\n", qname_.c_str ());
|
||||
|
||||
socket_->writeTo (response.data (), response.size () - available, s_multicastAddress);
|
||||
s_lastProbe = platform::steady_clock::now ();
|
||||
}
|
||||
|
||||
void announce (Socket *const socket_,
|
||||
SockAddr const *srcAddr_,
|
||||
std::uint16_t const id_,
|
||||
std::uint16_t const flags_,
|
||||
QueryRecord const &record_,
|
||||
SockAddr const &addr_)
|
||||
{
|
||||
std::vector<std::uint8_t> response (65536);
|
||||
auto available = response.size ();
|
||||
|
||||
// header
|
||||
auto out = encode<std::uint16_t> (response.data (), available, id_);
|
||||
out =
|
||||
encode<std::uint16_t> (out, available, flags_ | (1 << 15) | (1 << 10)); // mark response/AA
|
||||
out = encode<std::uint16_t> (out, available, 0);
|
||||
out = encode<std::uint16_t> (out, available, 1);
|
||||
out = encode<std::uint16_t> (out, available, 0);
|
||||
out = encode<std::uint16_t> (out, available, 0);
|
||||
|
||||
// answer section
|
||||
out = encode (out, available, record_.qname);
|
||||
out = encode<std::uint16_t> (out, available, record_.qtype);
|
||||
out = encode<std::uint16_t> (out, available, record_.qclass | (1 << 15)); // mark unique/flush
|
||||
out = encode<std::uint32_t> (out, available, MDNS_TTL);
|
||||
out = encode<std::uint16_t> (out, available, sizeof (in_addr_t));
|
||||
out = encode<in_addr_t> (
|
||||
out, available, static_cast<sockaddr_in const &> (addr_).sin_addr.s_addr, false);
|
||||
|
||||
if (!out)
|
||||
return;
|
||||
|
||||
auto const preferUnicast = srcAddr_ && ((record_.qclass >> 15) & 0x1);
|
||||
|
||||
if (preferUnicast)
|
||||
{
|
||||
auto const name = std::string (addr_.name ());
|
||||
info (
|
||||
"Respond mDNS %s %s to %s\n", record_.qname.c_str (), name.c_str (), srcAddr_->name ());
|
||||
socket_->writeTo (response.data (), response.size () - available, *srcAddr_);
|
||||
}
|
||||
|
||||
auto const now = platform::steady_clock::now ();
|
||||
if (!preferUnicast || now - s_lastAnnounce > std::chrono::seconds (MDNS_TTL / 4))
|
||||
{
|
||||
info ("Announce mDNS %s %s\n", record_.qname.c_str (), addr_.name ());
|
||||
socket_->writeTo (response.data (), response.size () - available, s_multicastAddress);
|
||||
s_lastAnnounce = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mdns::setHostname (std::string hostname_)
|
||||
{
|
||||
if (hostname_.empty ())
|
||||
hostname_ = platform::hostname ();
|
||||
|
||||
if (s_hostname == hostname_)
|
||||
return;
|
||||
|
||||
s_hostname = std::move (hostname_);
|
||||
s_hostnameLocal = s_hostname + ".local";
|
||||
|
||||
s_state = State::Probe1;
|
||||
s_lastProbe = platform::steady_clock::now ();
|
||||
}
|
||||
|
||||
UniqueSocket mdns::createSocket ()
|
||||
{
|
||||
auto socket = Socket::create (Socket::eDatagram);
|
||||
if (!socket)
|
||||
return nullptr;
|
||||
|
||||
if (!socket->setReuseAddress ())
|
||||
return nullptr;
|
||||
|
||||
auto iface = SockAddr::AnyIPv4;
|
||||
iface.setPort (s_multicastAddress.port ());
|
||||
if (!socket->bind (iface))
|
||||
return nullptr;
|
||||
|
||||
if (!socket->joinMulticastGroup (s_multicastAddress, iface))
|
||||
return nullptr;
|
||||
|
||||
s_state = State::Probe1;
|
||||
s_lastProbe = platform::steady_clock::now ();
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
void mdns::handleSocket (Socket *socket_, SockAddr const &addr_)
|
||||
{
|
||||
if (!socket_)
|
||||
return;
|
||||
|
||||
// only support IPv4 for now
|
||||
if (addr_.domain () != SockAddr::Domain::IPv4)
|
||||
return;
|
||||
|
||||
auto const now = platform::steady_clock::now ();
|
||||
|
||||
switch (s_state)
|
||||
{
|
||||
case State::Probe1:
|
||||
case State::Probe2:
|
||||
case State::Probe3:
|
||||
if (now - s_lastProbe > 250ms)
|
||||
{
|
||||
probe (socket_, s_hostname);
|
||||
s_state = static_cast<State> (static_cast<int> (s_state) + 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::Announce1:
|
||||
case State::Announce2:
|
||||
if (now - s_lastAnnounce > 1s)
|
||||
{
|
||||
announce (socket_,
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
QueryRecord{.qname = s_hostname, .qtype = 1, .qclass = 1},
|
||||
addr_);
|
||||
s_state = static_cast<State> (static_cast<int> (s_state) + 1);
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Socket::PollInfo pollInfo{*socket_, POLLIN, 0};
|
||||
auto const rc = Socket::poll (&pollInfo, 1, 0ms);
|
||||
if (rc <= 0 || !(pollInfo.revents & POLLIN))
|
||||
return;
|
||||
|
||||
SockAddr srcAddr;
|
||||
std::vector<std::uint8_t> buffer (65536);
|
||||
auto bytes = socket_->readFrom (buffer.data (), buffer.size (), srcAddr);
|
||||
if (bytes <= 0)
|
||||
return;
|
||||
|
||||
// only support IPv4 for now
|
||||
if (srcAddr.domain () != SockAddr::Domain::IPv4)
|
||||
return;
|
||||
|
||||
// ignore loopback
|
||||
if (std::memcmp (&reinterpret_cast<sockaddr_in const &> (srcAddr).sin_addr.s_addr,
|
||||
&reinterpret_cast<sockaddr_in const &> (addr_).sin_addr.s_addr,
|
||||
sizeof (in_addr_t)) == 0)
|
||||
return;
|
||||
|
||||
std::uint16_t id;
|
||||
std::uint16_t flags;
|
||||
std::uint16_t qdCount;
|
||||
std::uint16_t anCount;
|
||||
std::uint16_t nsCount;
|
||||
std::uint16_t arCount;
|
||||
|
||||
// parse header
|
||||
auto in = decode (buffer.data (), bytes, id);
|
||||
in = decode (in, bytes, flags);
|
||||
in = decode (in, bytes, qdCount);
|
||||
in = decode (in, bytes, anCount);
|
||||
in = decode (in, bytes, nsCount);
|
||||
in = decode (in, bytes, arCount);
|
||||
|
||||
if (!in)
|
||||
return;
|
||||
|
||||
auto const qr = (flags >> 15) & 0x1;
|
||||
|
||||
// ill-formed on queries and responses
|
||||
auto const opcode = (flags >> 11) & 0xF;
|
||||
if (opcode != 0)
|
||||
return;
|
||||
|
||||
// ill-formed on queries
|
||||
if (!qr && ((flags >> 10) & 0x1))
|
||||
return;
|
||||
|
||||
// punt on truncated messages
|
||||
if ((flags >> 9) & 0x1)
|
||||
return;
|
||||
|
||||
// ill-formed on queries
|
||||
if (!qr && ((flags >> 7) & 0x1))
|
||||
return;
|
||||
|
||||
// must be zero
|
||||
if ((flags >> 4) & 0x7)
|
||||
return;
|
||||
|
||||
// ill-formed on queries and responses
|
||||
if ((flags >> 0) & 0xF)
|
||||
return;
|
||||
|
||||
// std::vector<std::uint8_t> response (65536);
|
||||
// void *out = response.data ();
|
||||
// auto available = response.size ();
|
||||
|
||||
std::vector<ResourceRecord> answers;
|
||||
|
||||
bool announced = false;
|
||||
for (unsigned i = 0; i < qdCount; ++i)
|
||||
{
|
||||
QueryRecord record;
|
||||
in = record.decode (in, bytes);
|
||||
|
||||
if (!in)
|
||||
return;
|
||||
|
||||
// only respond to queries
|
||||
if (qr)
|
||||
continue;
|
||||
|
||||
// only accept A or ANY type
|
||||
if (record.qtype != 1 && record.qtype != 255)
|
||||
continue;
|
||||
|
||||
// only accept IN or ANY class
|
||||
if ((record.qclass & 0x7FFF) != 1 && (record.qclass & 0x7FFF) != 255)
|
||||
continue;
|
||||
|
||||
if (record.qname != s_hostname && record.qname != s_hostnameLocal)
|
||||
continue;
|
||||
|
||||
if (!announced)
|
||||
{
|
||||
std::vector<std::uint8_t> data (sizeof (in_addr_t));
|
||||
auto n = data.size ();
|
||||
encode (
|
||||
data.data (), n, static_cast<sockaddr_in const &> (addr_).sin_addr.s_addr, false);
|
||||
|
||||
answers.emplace_back (ResourceRecord{// answer
|
||||
.rname = record.qname,
|
||||
.rtype = 1,
|
||||
.rclass = static_cast<std::uint16_t> (1 | (1 << 15)),
|
||||
.rttl = MDNS_TTL,
|
||||
.rlen = sizeof (in_addr_t),
|
||||
.rdata = std::move (data)});
|
||||
|
||||
announce (socket_, &srcAddr, id, flags, record, addr_);
|
||||
announced = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < anCount; ++i)
|
||||
{
|
||||
ResourceRecord record;
|
||||
in = record.decode (in, bytes);
|
||||
|
||||
if (!in)
|
||||
return;
|
||||
}
|
||||
}
|
209
source/posix/collate.c
Normal file
209
source/posix/collate.c
Normal file
@ -0,0 +1,209 @@
|
||||
/*-
|
||||
* Copyright (c) 1995 Alex Tatmanjants <alex@elvisti.kiev.ua>
|
||||
* at Electronni Visti IA, Kiev, Ukraine.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "collate.h"
|
||||
|
||||
extern char *_PathLocale;
|
||||
int __collate_load_error = 1;
|
||||
int __collate_substitute_nontrivial;
|
||||
char __collate_version[STR_LEN];
|
||||
u_char __collate_substitute_table[UCHAR_MAX + 1][STR_LEN];
|
||||
struct __collate_st_char_pri __collate_char_pri_table[UCHAR_MAX + 1];
|
||||
struct __collate_st_chain_pri __collate_chain_pri_table[TABLE_SIZE];
|
||||
|
||||
#define FREAD(a, b, c, d) \
|
||||
do \
|
||||
{ \
|
||||
if (fread (a, b, c, d) != c) \
|
||||
{ \
|
||||
fclose (d); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
void __collate_err (int ex, const char *f);
|
||||
|
||||
int __collate_load_tables (encoding)
|
||||
char *encoding;
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
FILE *fp;
|
||||
int i, save_load_error;
|
||||
|
||||
save_load_error = __collate_load_error;
|
||||
__collate_load_error = 1;
|
||||
if (!encoding)
|
||||
{
|
||||
__collate_load_error = save_load_error;
|
||||
return -1;
|
||||
}
|
||||
if (!strcmp (encoding, "C") || !strcmp (encoding, "POSIX"))
|
||||
return 0;
|
||||
if (!_PathLocale)
|
||||
{
|
||||
__collate_load_error = save_load_error;
|
||||
return -1;
|
||||
}
|
||||
/* Range checking not needed, encoding has fixed size */
|
||||
(void)strcpy (buf, _PathLocale);
|
||||
(void)strcat (buf, "/");
|
||||
(void)strcat (buf, encoding);
|
||||
(void)strcat (buf, "/LC_COLLATE");
|
||||
if ((fp = fopen (buf, "r")) == NULL)
|
||||
{
|
||||
__collate_load_error = save_load_error;
|
||||
return -1;
|
||||
}
|
||||
FREAD (__collate_version, sizeof (__collate_version), 1, fp);
|
||||
if (strcmp (__collate_version, COLLATE_VERSION) != 0)
|
||||
{
|
||||
fclose (fp);
|
||||
return -1;
|
||||
}
|
||||
FREAD (__collate_substitute_table, sizeof (__collate_substitute_table), 1, fp);
|
||||
FREAD (__collate_char_pri_table, sizeof (__collate_char_pri_table), 1, fp);
|
||||
FREAD (__collate_chain_pri_table, sizeof (__collate_chain_pri_table), 1, fp);
|
||||
fclose (fp);
|
||||
__collate_load_error = 0;
|
||||
|
||||
__collate_substitute_nontrivial = 0;
|
||||
for (i = 0; i < UCHAR_MAX + 1; i++)
|
||||
{
|
||||
if (__collate_substitute_table[i][0] != i || __collate_substitute_table[i][1] != 0)
|
||||
{
|
||||
__collate_substitute_nontrivial = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u_char *__collate_substitute (s) const u_char *s;
|
||||
{
|
||||
int dest_len, len, nlen;
|
||||
int delta = strlen ((const char *)s);
|
||||
u_char *dest_str = NULL;
|
||||
|
||||
if (s == NULL || *s == '\0')
|
||||
return __collate_strdup ((u_char *)"");
|
||||
delta += delta / 8;
|
||||
dest_str = (u_char *)malloc (dest_len = delta);
|
||||
if (dest_str == NULL)
|
||||
__collate_err (EXIT_FAILURE, __FUNCTION__);
|
||||
len = 0;
|
||||
while (*s)
|
||||
{
|
||||
nlen = len + strlen ((const char *)__collate_substitute_table[*s]);
|
||||
if (dest_len <= nlen)
|
||||
{
|
||||
dest_str = reallocf (dest_str, dest_len = nlen + delta);
|
||||
if (dest_str == NULL)
|
||||
__collate_err (EXIT_FAILURE, __FUNCTION__);
|
||||
}
|
||||
strcpy ((char *)dest_str + len, (const char *)__collate_substitute_table[*s++]);
|
||||
len = nlen;
|
||||
}
|
||||
return dest_str;
|
||||
}
|
||||
|
||||
void __collate_lookup (t, len, prim, sec) const u_char *t;
|
||||
int *len, *prim, *sec;
|
||||
{
|
||||
struct __collate_st_chain_pri *p2;
|
||||
|
||||
*len = 1;
|
||||
*prim = *sec = 0;
|
||||
for (p2 = __collate_chain_pri_table; p2->str[0]; p2++)
|
||||
{
|
||||
if (strncmp ((const char *)t, (const char *)p2->str, strlen ((const char *)p2->str)) == 0)
|
||||
{
|
||||
*len = strlen ((const char *)p2->str);
|
||||
*prim = p2->prim;
|
||||
*sec = p2->sec;
|
||||
return;
|
||||
}
|
||||
}
|
||||
*prim = __collate_char_pri_table[*t].prim;
|
||||
*sec = __collate_char_pri_table[*t].sec;
|
||||
}
|
||||
|
||||
u_char *__collate_strdup (s)
|
||||
u_char *s;
|
||||
{
|
||||
u_char *t = (u_char *)strdup ((const char *)s);
|
||||
|
||||
if (t == NULL)
|
||||
__collate_err (EXIT_FAILURE, __FUNCTION__);
|
||||
return t;
|
||||
}
|
||||
|
||||
void __collate_err (int ex, const char *f)
|
||||
{
|
||||
const char *s;
|
||||
int serrno = errno;
|
||||
int dummy;
|
||||
|
||||
/* Be careful to change write counts if you change the strings */
|
||||
write (STDERR_FILENO, "collate_error: ", 15);
|
||||
write (STDERR_FILENO, f, strlen (f));
|
||||
write (STDERR_FILENO, ": ", 2);
|
||||
s = _strerror_r (_REENT, serrno, 1, &dummy);
|
||||
write (STDERR_FILENO, s, strlen (s));
|
||||
write (STDERR_FILENO, "\n", 1);
|
||||
exit (ex);
|
||||
}
|
||||
|
||||
#ifdef COLLATE_DEBUG
|
||||
void __collate_print_tables ()
|
||||
{
|
||||
int i;
|
||||
struct __collate_st_chain_pri *p2;
|
||||
|
||||
printf ("Substitute table:\n");
|
||||
for (i = 0; i < UCHAR_MAX + 1; i++)
|
||||
if (i != *__collate_substitute_table[i])
|
||||
printf ("\t'%c' --> \"%s\"\n", i, __collate_substitute_table[i]);
|
||||
printf ("Chain priority table:\n");
|
||||
for (p2 = __collate_chain_pri_table; p2->str[0]; p2++)
|
||||
printf ("\t\"%s\" : %d %d\n\n", p2->str, p2->prim, p2->sec);
|
||||
printf ("Char priority table:\n");
|
||||
for (i = 0; i < UCHAR_MAX + 1; i++)
|
||||
printf ("\t'%c' : %d %d\n",
|
||||
i,
|
||||
__collate_char_pri_table[i].prim,
|
||||
__collate_char_pri_table[i].sec);
|
||||
}
|
||||
#endif
|
69
source/posix/collate.h
Normal file
69
source/posix/collate.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*-
|
||||
* Copyright (c) 1995 Alex Tatmanjants <alex@elvisti.kiev.ua>
|
||||
* at Electronni Visti IA, Kiev, Ukraine.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD: src/lib/libc/locale/collate.h,v 1.11 2002/03/21 22:46:54 obrien Exp $
|
||||
*/
|
||||
|
||||
#ifndef _COLLATE_H_
|
||||
#define _COLLATE_H_
|
||||
|
||||
#include <limits.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define STR_LEN 10
|
||||
#define TABLE_SIZE 100
|
||||
#define COLLATE_VERSION "1.0\n"
|
||||
|
||||
struct __collate_st_char_pri
|
||||
{
|
||||
int prim, sec;
|
||||
};
|
||||
struct __collate_st_chain_pri
|
||||
{
|
||||
u_char str[STR_LEN];
|
||||
int prim, sec;
|
||||
};
|
||||
|
||||
extern int __collate_load_error;
|
||||
extern int __collate_substitute_nontrivial;
|
||||
extern char __collate_version[STR_LEN];
|
||||
extern u_char __collate_substitute_table[UCHAR_MAX + 1][STR_LEN];
|
||||
extern struct __collate_st_char_pri __collate_char_pri_table[UCHAR_MAX + 1];
|
||||
extern struct __collate_st_chain_pri __collate_chain_pri_table[TABLE_SIZE];
|
||||
|
||||
__BEGIN_DECLS
|
||||
u_char *__collate_strdup (u_char *);
|
||||
u_char *__collate_substitute (const u_char *);
|
||||
int __collate_load_tables (char *);
|
||||
void __collate_lookup (const u_char *, int *, int *, int *);
|
||||
int __collate_range_cmp (int, int);
|
||||
#ifdef COLLATE_DEBUG
|
||||
void __collate_print_tables (void);
|
||||
#endif
|
||||
__END_DECLS
|
||||
|
||||
#endif /* !_COLLATE_H_ */
|
88
source/posix/collcmp.c
Normal file
88
source/posix/collcmp.c
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 1996 by Andrey A. Chernov, Moscow, Russia.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#define ASCII_COMPATIBLE_COLLATE /* see share/colldef */
|
||||
|
||||
#include "collate.h"
|
||||
#include <string.h>
|
||||
#ifndef ASCII_COMPATIBLE_COLLATE
|
||||
#include <ctype.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Compare two characters converting collate information
|
||||
* into ASCII-compatible range, it allows to handle
|
||||
* "[a-z]"-type ranges with national characters.
|
||||
*/
|
||||
|
||||
int __collate_range_cmp (c1, c2)
|
||||
int c1, c2;
|
||||
{
|
||||
static char s1[2], s2[2];
|
||||
int ret;
|
||||
#ifndef ASCII_COMPATIBLE_COLLATE
|
||||
int as1, as2, al1, al2;
|
||||
#endif
|
||||
|
||||
c1 &= UCHAR_MAX;
|
||||
c2 &= UCHAR_MAX;
|
||||
if (c1 == c2)
|
||||
return (0);
|
||||
|
||||
#ifndef ASCII_COMPATIBLE_COLLATE
|
||||
as1 = isascii (c1);
|
||||
as2 = isascii (c2);
|
||||
al1 = isalpha (c1);
|
||||
al2 = isalpha (c2);
|
||||
|
||||
if (as1 || as2 || al1 || al2)
|
||||
{
|
||||
if ((as1 && as2) || (!al1 && !al2))
|
||||
return (c1 - c2);
|
||||
if (al1 && !al2)
|
||||
{
|
||||
if (isupper (c1))
|
||||
return ('A' - c2);
|
||||
else
|
||||
return ('a' - c2);
|
||||
}
|
||||
else if (al2 && !al1)
|
||||
{
|
||||
if (isupper (c2))
|
||||
return (c1 - 'A');
|
||||
else
|
||||
return (c1 - 'a');
|
||||
}
|
||||
}
|
||||
#endif
|
||||
s1[0] = c1;
|
||||
s2[0] = c2;
|
||||
if ((ret = strcoll (s1, s2)) != 0)
|
||||
return (ret);
|
||||
return (c1 - c2);
|
||||
}
|
839
source/posix/glob.c
Normal file
839
source/posix/glob.c
Normal file
@ -0,0 +1,839 @@
|
||||
/*
|
||||
* Copyright (c) 1989, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Guido van Rossum.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
#define _NO_GLOB /* Cygwin provides its own glob. */
|
||||
#endif
|
||||
|
||||
#ifndef _NO_GLOB
|
||||
|
||||
#if defined(LIBC_SCCS) && !defined(lint)
|
||||
static char sccsid[] = "@(#)glob.c 8.3 (Berkeley) 10/13/93";
|
||||
#endif /* LIBC_SCCS and not lint */
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
/*
|
||||
* glob(3) -- a superset of the one defined in POSIX 1003.2.
|
||||
*
|
||||
* The [!...] convention to negate a range is supported (SysV, Posix, ksh).
|
||||
*
|
||||
* Optional extra services, controlled by flags not defined by POSIX:
|
||||
*
|
||||
* GLOB_QUOTE:
|
||||
* Escaping convention: \ inhibits any special meaning the following
|
||||
* character might have (except \ at end of string is retained).
|
||||
* GLOB_MAGCHAR:
|
||||
* Set in gl_flags if pattern contained a globbing character.
|
||||
* GLOB_NOMAGIC:
|
||||
* Same as GLOB_NOCHECK, but it will only append pattern if it did
|
||||
* not contain any magic characters. [Used in csh style globbing]
|
||||
* GLOB_ALTDIRFUNC:
|
||||
* Use alternately specified directory access functions.
|
||||
* GLOB_TILDE:
|
||||
* expand ~user/foo to the /home/dir/of/user/foo
|
||||
* GLOB_BRACE:
|
||||
* expand {1,2}{a,b} to 1a 1b 2a 2b
|
||||
* gl_matchc:
|
||||
* Number of matches in the current invocation of glob.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <glob.h>
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "collate.h"
|
||||
|
||||
#define DOLLAR '$'
|
||||
#define DOT '.'
|
||||
#define EOS '\0'
|
||||
#define LBRACKET '['
|
||||
#define NOT '!'
|
||||
#define QUESTION '?'
|
||||
#define QUOTE '\\'
|
||||
#define RANGE '-'
|
||||
#define RBRACKET ']'
|
||||
#define SEP '/'
|
||||
#define STAR '*'
|
||||
#define TILDE '~'
|
||||
#define UNDERSCORE '_'
|
||||
#define LBRACE '{'
|
||||
#define RBRACE '}'
|
||||
#define SLASH '/'
|
||||
#define COMMA ','
|
||||
|
||||
#ifndef DEBUG
|
||||
|
||||
#define M_QUOTE 0x8000
|
||||
#define M_PROTECT 0x4000
|
||||
#define M_MASK 0xffff
|
||||
#define M_ASCII 0x00ff
|
||||
|
||||
typedef u_short Char;
|
||||
|
||||
#else
|
||||
|
||||
#define M_QUOTE 0x80
|
||||
#define M_PROTECT 0x40
|
||||
#define M_MASK 0xff
|
||||
#define M_ASCII 0x7f
|
||||
|
||||
typedef char Char;
|
||||
|
||||
#endif
|
||||
|
||||
#define CHAR(c) ((Char)((c)&M_ASCII))
|
||||
#define META(c) ((Char)((c) | M_QUOTE))
|
||||
#define M_ALL META ('*')
|
||||
#define M_END META (']')
|
||||
#define M_NOT META ('!')
|
||||
#define M_ONE META ('?')
|
||||
#define M_RNG META ('-')
|
||||
#define M_SET META ('[')
|
||||
#define ismeta(c) (((c)&M_QUOTE) != 0)
|
||||
|
||||
static int compare (const void *, const void *);
|
||||
static int g_Ctoc (const Char *, char *, u_int);
|
||||
static int g_lstat (Char *, struct stat *, glob_t *);
|
||||
static DIR *g_opendir (Char *, glob_t *);
|
||||
static Char *g_strchr (Char *, int);
|
||||
#ifdef notdef
|
||||
static Char *g_strcat (Char *, const Char *);
|
||||
#endif
|
||||
static int g_stat (Char *, struct stat *, glob_t *);
|
||||
static int glob0 (const Char *, glob_t *, int *);
|
||||
static int glob1 (Char *, glob_t *, int *);
|
||||
static int glob2 (Char *, Char *, Char *, Char *, glob_t *, int *);
|
||||
static int glob3 (Char *, Char *, Char *, Char *, Char *, glob_t *, int *);
|
||||
static int globextend (const Char *, glob_t *, int *);
|
||||
static int globexp1 (const Char *, glob_t *, int *);
|
||||
static int globexp2 (const Char *, const Char *, glob_t *, int *, int *);
|
||||
static int match (Char *, Char *, Char *);
|
||||
#ifdef DEBUG
|
||||
static void qprintf (const char *, Char *);
|
||||
#endif
|
||||
|
||||
int glob (pattern, flags, errfunc, pglob) const char *__restrict pattern;
|
||||
int flags, (*errfunc) (const char *, int);
|
||||
glob_t *__restrict pglob;
|
||||
{
|
||||
const u_char *patnext;
|
||||
int c, limit;
|
||||
Char *bufnext, *bufend, patbuf[MAXPATHLEN];
|
||||
|
||||
patnext = (u_char *)pattern;
|
||||
if (!(flags & GLOB_APPEND))
|
||||
{
|
||||
pglob->gl_pathc = 0;
|
||||
pglob->gl_pathv = NULL;
|
||||
if (!(flags & GLOB_DOOFFS))
|
||||
pglob->gl_offs = 0;
|
||||
}
|
||||
if (flags & GLOB_LIMIT)
|
||||
{
|
||||
limit = pglob->gl_matchc;
|
||||
if (limit == 0)
|
||||
limit = ARG_MAX;
|
||||
}
|
||||
else
|
||||
limit = 0;
|
||||
pglob->gl_flags = flags & ~GLOB_MAGCHAR;
|
||||
pglob->gl_errfunc = errfunc;
|
||||
pglob->gl_matchc = 0;
|
||||
|
||||
bufnext = patbuf;
|
||||
bufend = bufnext + MAXPATHLEN - 1;
|
||||
if (flags & GLOB_QUOTE)
|
||||
{
|
||||
/* Protect the quoted characters. */
|
||||
while (bufnext < bufend && (c = *patnext++) != EOS)
|
||||
if (c == QUOTE)
|
||||
{
|
||||
if ((c = *patnext++) == EOS)
|
||||
{
|
||||
c = QUOTE;
|
||||
--patnext;
|
||||
}
|
||||
*bufnext++ = c | M_PROTECT;
|
||||
}
|
||||
else
|
||||
*bufnext++ = c;
|
||||
}
|
||||
else
|
||||
while (bufnext < bufend && (c = *patnext++) != EOS)
|
||||
*bufnext++ = c;
|
||||
*bufnext = EOS;
|
||||
|
||||
if (flags & GLOB_BRACE)
|
||||
return globexp1 (patbuf, pglob, &limit);
|
||||
else
|
||||
return glob0 (patbuf, pglob, &limit);
|
||||
}
|
||||
|
||||
/*
|
||||
* Expand recursively a glob {} pattern. When there is no more expansion
|
||||
* invoke the standard globbing routine to glob the rest of the magic
|
||||
* characters
|
||||
*/
|
||||
static int globexp1 (pattern, pglob, limit) const Char *pattern;
|
||||
glob_t *pglob;
|
||||
int *limit;
|
||||
{
|
||||
const Char *ptr = pattern;
|
||||
int rv;
|
||||
|
||||
/* Protect a single {}, for find(1), like csh */
|
||||
if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS)
|
||||
return glob0 (pattern, pglob, limit);
|
||||
|
||||
while ((ptr = (const Char *)g_strchr ((Char *)ptr, LBRACE)) != NULL)
|
||||
if (!globexp2 (ptr, pattern, pglob, &rv, limit))
|
||||
return rv;
|
||||
|
||||
return glob0 (pattern, pglob, limit);
|
||||
}
|
||||
|
||||
/*
|
||||
* Recursive brace globbing helper. Tries to expand a single brace.
|
||||
* If it succeeds then it invokes globexp1 with the new pattern.
|
||||
* If it fails then it tries to glob the rest of the pattern and returns.
|
||||
*/
|
||||
static int globexp2 (ptr, pattern, pglob, rv, limit) const Char *ptr, *pattern;
|
||||
glob_t *pglob;
|
||||
int *rv, *limit;
|
||||
{
|
||||
int i;
|
||||
Char *lm, *ls;
|
||||
const Char *pe, *pm, *pl;
|
||||
Char patbuf[MAXPATHLEN];
|
||||
|
||||
/* copy part up to the brace */
|
||||
for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
|
||||
continue;
|
||||
*lm = EOS;
|
||||
ls = lm;
|
||||
|
||||
/* Find the balanced brace */
|
||||
for (i = 0, pe = ++ptr; *pe; pe++)
|
||||
if (*pe == LBRACKET)
|
||||
{
|
||||
/* Ignore everything between [] */
|
||||
for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++)
|
||||
continue;
|
||||
if (*pe == EOS)
|
||||
{
|
||||
/*
|
||||
* We could not find a matching RBRACKET.
|
||||
* Ignore and just look for RBRACE
|
||||
*/
|
||||
pe = pm;
|
||||
}
|
||||
}
|
||||
else if (*pe == LBRACE)
|
||||
i++;
|
||||
else if (*pe == RBRACE)
|
||||
{
|
||||
if (i == 0)
|
||||
break;
|
||||
i--;
|
||||
}
|
||||
|
||||
/* Non matching braces; just glob the pattern */
|
||||
if (i != 0 || *pe == EOS)
|
||||
{
|
||||
*rv = glob0 (patbuf, pglob, limit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0, pl = pm = ptr; pm <= pe; pm++)
|
||||
switch (*pm)
|
||||
{
|
||||
case LBRACKET:
|
||||
/* Ignore everything between [] */
|
||||
for (pl = pm++; *pm != RBRACKET && *pm != EOS; pm++)
|
||||
continue;
|
||||
if (*pm == EOS)
|
||||
{
|
||||
/*
|
||||
* We could not find a matching RBRACKET.
|
||||
* Ignore and just look for RBRACE
|
||||
*/
|
||||
pm = pl;
|
||||
}
|
||||
break;
|
||||
|
||||
case LBRACE:
|
||||
i++;
|
||||
break;
|
||||
|
||||
case RBRACE:
|
||||
if (i)
|
||||
{
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case COMMA:
|
||||
if (i && *pm == COMMA)
|
||||
break;
|
||||
else
|
||||
{
|
||||
/* Append the current string */
|
||||
for (lm = ls; (pl < pm); *lm++ = *pl++)
|
||||
continue;
|
||||
/*
|
||||
* Append the rest of the pattern after the
|
||||
* closing brace
|
||||
*/
|
||||
for (pl = pe + 1; (*lm++ = *pl++) != EOS;)
|
||||
continue;
|
||||
|
||||
/* Expand the current pattern */
|
||||
#ifdef DEBUG
|
||||
qprintf ("globexp2:", patbuf);
|
||||
#endif
|
||||
*rv = globexp1 (patbuf, pglob, limit);
|
||||
|
||||
/* move after the comma, to the next string */
|
||||
pl = pm + 1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
*rv = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The main glob() routine: compiles the pattern (optionally processing
|
||||
* quotes), calls glob1() to do the real pattern matching, and finally
|
||||
* sorts the list (unless unsorted operation is requested). Returns 0
|
||||
* if things went well, nonzero if errors occurred. It is not an error
|
||||
* to find no matches.
|
||||
*/
|
||||
static int glob0 (pattern, pglob, limit) const Char *pattern;
|
||||
glob_t *pglob;
|
||||
int *limit;
|
||||
{
|
||||
const Char *qpatnext;
|
||||
int c, err, oldpathc;
|
||||
Char *bufnext, patbuf[MAXPATHLEN];
|
||||
|
||||
qpatnext = pattern;
|
||||
oldpathc = pglob->gl_pathc;
|
||||
bufnext = patbuf;
|
||||
|
||||
/* We don't need to check for buffer overflow any more. */
|
||||
while ((c = *qpatnext++) != EOS)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case LBRACKET:
|
||||
c = *qpatnext;
|
||||
if (c == NOT)
|
||||
++qpatnext;
|
||||
if (*qpatnext == EOS || g_strchr ((Char *)qpatnext + 1, RBRACKET) == NULL)
|
||||
{
|
||||
*bufnext++ = LBRACKET;
|
||||
if (c == NOT)
|
||||
--qpatnext;
|
||||
break;
|
||||
}
|
||||
*bufnext++ = M_SET;
|
||||
if (c == NOT)
|
||||
*bufnext++ = M_NOT;
|
||||
c = *qpatnext++;
|
||||
do
|
||||
{
|
||||
*bufnext++ = CHAR (c);
|
||||
if (*qpatnext == RANGE && (c = qpatnext[1]) != RBRACKET)
|
||||
{
|
||||
*bufnext++ = M_RNG;
|
||||
*bufnext++ = CHAR (c);
|
||||
qpatnext += 2;
|
||||
}
|
||||
} while ((c = *qpatnext++) != RBRACKET);
|
||||
pglob->gl_flags |= GLOB_MAGCHAR;
|
||||
*bufnext++ = M_END;
|
||||
break;
|
||||
case QUESTION:
|
||||
pglob->gl_flags |= GLOB_MAGCHAR;
|
||||
*bufnext++ = M_ONE;
|
||||
break;
|
||||
case STAR:
|
||||
pglob->gl_flags |= GLOB_MAGCHAR;
|
||||
/* collapse adjacent stars to one,
|
||||
* to avoid exponential behavior
|
||||
*/
|
||||
if (bufnext == patbuf || bufnext[-1] != M_ALL)
|
||||
*bufnext++ = M_ALL;
|
||||
break;
|
||||
default:
|
||||
*bufnext++ = CHAR (c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
*bufnext = EOS;
|
||||
#ifdef DEBUG
|
||||
qprintf ("glob0:", patbuf);
|
||||
#endif
|
||||
|
||||
if ((err = glob1 (patbuf, pglob, limit)) != 0)
|
||||
return (err);
|
||||
|
||||
/*
|
||||
* If there was no match we are going to append the pattern
|
||||
* if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
|
||||
* and the pattern did not contain any magic characters
|
||||
* GLOB_NOMAGIC is there just for compatibility with csh.
|
||||
*/
|
||||
if (pglob->gl_pathc == oldpathc &&
|
||||
((pglob->gl_flags & GLOB_NOCHECK) ||
|
||||
((pglob->gl_flags & GLOB_NOMAGIC) && !(pglob->gl_flags & GLOB_MAGCHAR))))
|
||||
return (globextend (pattern, pglob, limit));
|
||||
else if (!(pglob->gl_flags & GLOB_NOSORT))
|
||||
qsort (pglob->gl_pathv + pglob->gl_offs + oldpathc,
|
||||
pglob->gl_pathc - oldpathc,
|
||||
sizeof (char *),
|
||||
compare);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int compare (p, q) const void *p, *q;
|
||||
{
|
||||
return (strcmp (*(char **)p, *(char **)q));
|
||||
}
|
||||
|
||||
static int glob1 (pattern, pglob, limit)
|
||||
Char *pattern;
|
||||
glob_t *pglob;
|
||||
int *limit;
|
||||
{
|
||||
Char pathbuf[MAXPATHLEN];
|
||||
|
||||
/* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
|
||||
if (*pattern == EOS)
|
||||
return (0);
|
||||
return (glob2 (pathbuf, pathbuf, pathbuf + MAXPATHLEN - 1, pattern, pglob, limit));
|
||||
}
|
||||
|
||||
/*
|
||||
* The functions glob2 and glob3 are mutually recursive; there is one level
|
||||
* of recursion for each segment in the pattern that contains one or more
|
||||
* meta characters.
|
||||
*/
|
||||
static int glob2 (pathbuf, pathend, pathend_last, pattern, pglob, limit)
|
||||
Char *pathbuf, *pathend, *pathend_last, *pattern;
|
||||
glob_t *pglob;
|
||||
int *limit;
|
||||
{
|
||||
struct stat sb;
|
||||
Char *p, *q;
|
||||
int anymeta;
|
||||
|
||||
/*
|
||||
* Loop over pattern segments until end of pattern or until
|
||||
* segment with meta character found.
|
||||
*/
|
||||
for (anymeta = 0;;)
|
||||
{
|
||||
if (*pattern == EOS)
|
||||
{ /* End of pattern? */
|
||||
*pathend = EOS;
|
||||
if (g_lstat (pathbuf, &sb, pglob))
|
||||
return (0);
|
||||
|
||||
if (((pglob->gl_flags & GLOB_MARK) && pathend[-1] != SEP) &&
|
||||
(S_ISDIR (sb.st_mode) ||
|
||||
(S_ISLNK (sb.st_mode) && (g_stat (pathbuf, &sb, pglob) == 0) &&
|
||||
S_ISDIR (sb.st_mode))))
|
||||
{
|
||||
if (pathend + 1 > pathend_last)
|
||||
return (1);
|
||||
*pathend++ = SEP;
|
||||
*pathend = EOS;
|
||||
}
|
||||
++pglob->gl_matchc;
|
||||
return (globextend (pathbuf, pglob, limit));
|
||||
}
|
||||
|
||||
/* Find end of next segment, copy tentatively to pathend. */
|
||||
q = pathend;
|
||||
p = pattern;
|
||||
while (*p != EOS && *p != SEP)
|
||||
{
|
||||
if (ismeta (*p))
|
||||
anymeta = 1;
|
||||
if (q + 1 > pathend_last)
|
||||
return (1);
|
||||
*q++ = *p++;
|
||||
}
|
||||
|
||||
if (!anymeta)
|
||||
{ /* No expansion, do next segment. */
|
||||
pathend = q;
|
||||
pattern = p;
|
||||
while (*pattern == SEP)
|
||||
{
|
||||
if (pathend + 1 > pathend_last)
|
||||
return (1);
|
||||
*pathend++ = *pattern++;
|
||||
}
|
||||
}
|
||||
else /* Need expansion, recurse. */
|
||||
return (glob3 (pathbuf, pathend, pathend_last, pattern, p, pglob, limit));
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
static int glob3 (pathbuf, pathend, pathend_last, pattern, restpattern, pglob, limit)
|
||||
Char *pathbuf, *pathend, *pathend_last, *pattern, *restpattern;
|
||||
glob_t *pglob;
|
||||
int *limit;
|
||||
{
|
||||
struct dirent *dp;
|
||||
DIR *dirp;
|
||||
int err;
|
||||
char buf[MAXPATHLEN];
|
||||
|
||||
/*
|
||||
* The readdirfunc declaration can't be prototyped, because it is
|
||||
* assigned, below, to two functions which are prototyped in glob.h
|
||||
* and dirent.h as taking pointers to differently typed opaque
|
||||
* structures.
|
||||
*/
|
||||
struct dirent *(*readdirfunc) ();
|
||||
|
||||
if (pathend > pathend_last)
|
||||
return (1);
|
||||
*pathend = EOS;
|
||||
errno = 0;
|
||||
|
||||
if ((dirp = g_opendir (pathbuf, pglob)) == NULL)
|
||||
{
|
||||
/* TODO: don't call for ENOENT or ENOTDIR? */
|
||||
if (pglob->gl_errfunc)
|
||||
{
|
||||
if (g_Ctoc (pathbuf, buf, sizeof (buf)))
|
||||
return (GLOB_ABEND);
|
||||
if (pglob->gl_errfunc (buf, errno) || pglob->gl_flags & GLOB_ERR)
|
||||
return (GLOB_ABEND);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
/* Search directory for matching names. */
|
||||
if (pglob->gl_flags & GLOB_ALTDIRFUNC)
|
||||
readdirfunc = pglob->gl_readdir;
|
||||
else
|
||||
readdirfunc = readdir;
|
||||
while ((dp = (*readdirfunc) (dirp)))
|
||||
{
|
||||
u_char *sc;
|
||||
Char *dc;
|
||||
|
||||
/* Initial DOT must be matched literally. */
|
||||
if (dp->d_name[0] == DOT && *pattern != DOT)
|
||||
continue;
|
||||
dc = pathend;
|
||||
sc = (u_char *)dp->d_name;
|
||||
while (dc < pathend_last && (*dc++ = *sc++) != EOS)
|
||||
;
|
||||
if (!match (pathend, pattern, restpattern))
|
||||
{
|
||||
*pathend = EOS;
|
||||
continue;
|
||||
}
|
||||
err = glob2 (pathbuf, --dc, pathend_last, restpattern, pglob, limit);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
if (pglob->gl_flags & GLOB_ALTDIRFUNC)
|
||||
(*pglob->gl_closedir) (dirp);
|
||||
else
|
||||
closedir (dirp);
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extend the gl_pathv member of a glob_t structure to accomodate a new item,
|
||||
* add the new item, and update gl_pathc.
|
||||
*
|
||||
* This assumes the BSD realloc, which only copies the block when its size
|
||||
* crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
|
||||
* behavior.
|
||||
*
|
||||
* Return 0 if new item added, error code if memory couldn't be allocated.
|
||||
*
|
||||
* Invariant of the glob_t structure:
|
||||
* Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
|
||||
* gl_pathv points to (gl_offs + gl_pathc + 1) items.
|
||||
*/
|
||||
static int globextend (path, pglob, limit) const Char *path;
|
||||
glob_t *pglob;
|
||||
int *limit;
|
||||
{
|
||||
char **pathv;
|
||||
int i;
|
||||
u_int newsize, len;
|
||||
char *copy;
|
||||
const Char *p;
|
||||
|
||||
if (*limit && pglob->gl_pathc > *limit)
|
||||
{
|
||||
errno = 0;
|
||||
return (GLOB_NOSPACE);
|
||||
}
|
||||
|
||||
newsize = sizeof (*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
|
||||
pathv = pglob->gl_pathv ? realloc ((char *)pglob->gl_pathv, newsize) : malloc (newsize);
|
||||
if (pathv == NULL)
|
||||
{
|
||||
if (pglob->gl_pathv)
|
||||
{
|
||||
free (pglob->gl_pathv);
|
||||
pglob->gl_pathv = NULL;
|
||||
}
|
||||
return (GLOB_NOSPACE);
|
||||
}
|
||||
|
||||
if (pglob->gl_pathv == NULL && pglob->gl_offs > 0)
|
||||
{
|
||||
/* first time around -- clear initial gl_offs items */
|
||||
pathv += pglob->gl_offs;
|
||||
for (i = pglob->gl_offs; --i >= 0;)
|
||||
*--pathv = NULL;
|
||||
}
|
||||
pglob->gl_pathv = pathv;
|
||||
|
||||
for (p = path; *p++;)
|
||||
continue;
|
||||
len = (size_t)(p - path);
|
||||
if ((copy = malloc (len)) != NULL)
|
||||
{
|
||||
if (g_Ctoc (path, copy, len))
|
||||
{
|
||||
free (copy);
|
||||
return (GLOB_NOSPACE);
|
||||
}
|
||||
pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
|
||||
}
|
||||
pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
|
||||
return (copy == NULL ? GLOB_NOSPACE : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* pattern matching function for filenames. Each occurrence of the *
|
||||
* pattern causes a recursion level.
|
||||
*/
|
||||
static int match (name, pat, patend)
|
||||
Char *name, *pat, *patend;
|
||||
{
|
||||
int ok, negate_range;
|
||||
Char c, k;
|
||||
|
||||
while (pat < patend)
|
||||
{
|
||||
c = *pat++;
|
||||
switch (c & M_MASK)
|
||||
{
|
||||
case M_ALL:
|
||||
if (pat == patend)
|
||||
return (1);
|
||||
do
|
||||
if (match (name, pat, patend))
|
||||
return (1);
|
||||
while (*name++ != EOS);
|
||||
return (0);
|
||||
case M_ONE:
|
||||
if (*name++ == EOS)
|
||||
return (0);
|
||||
break;
|
||||
case M_SET:
|
||||
ok = 0;
|
||||
if ((k = *name++) == EOS)
|
||||
return (0);
|
||||
if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS)
|
||||
++pat;
|
||||
while (((c = *pat++) & M_MASK) != M_END)
|
||||
if ((*pat & M_MASK) == M_RNG)
|
||||
{
|
||||
if (__collate_load_error
|
||||
? CHAR (c) <= CHAR (k) && CHAR (k) <= CHAR (pat[1])
|
||||
: __collate_range_cmp (CHAR (c), CHAR (k)) <= 0 &&
|
||||
__collate_range_cmp (CHAR (k), CHAR (pat[1])) <= 0)
|
||||
ok = 1;
|
||||
pat += 2;
|
||||
}
|
||||
else if (c == k)
|
||||
ok = 1;
|
||||
if (ok == negate_range)
|
||||
return (0);
|
||||
break;
|
||||
default:
|
||||
if (*name++ != c)
|
||||
return (0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (*name == EOS);
|
||||
}
|
||||
|
||||
/* Free allocated data belonging to a glob_t structure. */
|
||||
void globfree (pglob) glob_t *pglob;
|
||||
{
|
||||
int i;
|
||||
char **pp;
|
||||
|
||||
if (pglob->gl_pathv != NULL)
|
||||
{
|
||||
pp = pglob->gl_pathv + pglob->gl_offs;
|
||||
for (i = pglob->gl_pathc; i--; ++pp)
|
||||
if (*pp)
|
||||
free (*pp);
|
||||
free (pglob->gl_pathv);
|
||||
pglob->gl_pathv = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static DIR *g_opendir (str, pglob)
|
||||
Char *str;
|
||||
glob_t *pglob;
|
||||
{
|
||||
char buf[MAXPATHLEN];
|
||||
|
||||
if (!*str)
|
||||
strcpy (buf, ".");
|
||||
else
|
||||
{
|
||||
if (g_Ctoc (str, buf, sizeof (buf)))
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (pglob->gl_flags & GLOB_ALTDIRFUNC)
|
||||
return ((*pglob->gl_opendir) (buf));
|
||||
|
||||
return (opendir (buf));
|
||||
}
|
||||
|
||||
static int g_lstat (fn, sb, pglob)
|
||||
Char *fn;
|
||||
struct stat *sb;
|
||||
glob_t *pglob;
|
||||
{
|
||||
char buf[MAXPATHLEN];
|
||||
|
||||
if (g_Ctoc (fn, buf, sizeof (buf)))
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return (-1);
|
||||
}
|
||||
if (pglob->gl_flags & GLOB_ALTDIRFUNC)
|
||||
return ((*pglob->gl_lstat) (buf, sb));
|
||||
return (lstat (buf, sb));
|
||||
}
|
||||
|
||||
static int g_stat (fn, sb, pglob)
|
||||
Char *fn;
|
||||
struct stat *sb;
|
||||
glob_t *pglob;
|
||||
{
|
||||
char buf[MAXPATHLEN];
|
||||
|
||||
if (g_Ctoc (fn, buf, sizeof (buf)))
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return (-1);
|
||||
}
|
||||
if (pglob->gl_flags & GLOB_ALTDIRFUNC)
|
||||
return ((*pglob->gl_stat) (buf, sb));
|
||||
return (stat (buf, sb));
|
||||
}
|
||||
|
||||
static Char *g_strchr (str, ch)
|
||||
Char *str;
|
||||
int ch;
|
||||
{
|
||||
do
|
||||
{
|
||||
if (*str == ch)
|
||||
return (str);
|
||||
} while (*str++);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static int g_Ctoc (str, buf, len) const Char *str;
|
||||
char *buf;
|
||||
u_int len;
|
||||
{
|
||||
while (len--)
|
||||
{
|
||||
if ((*buf++ = *str++) == '\0')
|
||||
return (0);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void qprintf (str, s) const char *str;
|
||||
Char *s;
|
||||
{
|
||||
Char *p;
|
||||
|
||||
(void)printf ("%s:\n", str);
|
||||
for (p = s; *p; p++)
|
||||
(void)printf ("%c", CHAR (*p));
|
||||
(void)printf ("\n");
|
||||
for (p = s; *p; p++)
|
||||
(void)printf ("%c", *p & M_PROTECT ? '"' : ' ');
|
||||
(void)printf ("\n");
|
||||
for (p = s; *p; p++)
|
||||
(void)printf ("%c", ismeta (*p) ? '_' : ' ');
|
||||
(void)printf ("\n");
|
||||
}
|
||||
#endif
|
||||
#endif /* !_NO_GLOB */
|
@ -3,7 +3,7 @@
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2020 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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
|
||||
@ -26,11 +26,79 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef __3DS__
|
||||
static_assert (sizeof (sockaddr_storage) == 0x1c);
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
in_addr inaddr_any = {.s_addr = htonl (INADDR_ANY)};
|
||||
|
||||
std::strong_ordering
|
||||
strongMemCompare (void const *const a_, void const *const b_, std::size_t const size_)
|
||||
{
|
||||
auto const cmp = std::memcmp (a_, b_, size_);
|
||||
if (cmp < 0)
|
||||
return std::strong_ordering::less;
|
||||
if (cmp > 0)
|
||||
return std::strong_ordering::greater;
|
||||
return std::strong_ordering::equal;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
SockAddr const SockAddr::AnyIPv4{inaddr_any};
|
||||
|
||||
#ifndef NO_IPV6
|
||||
SockAddr const SockAddr::AnyIPv6{in6addr_any};
|
||||
#endif
|
||||
|
||||
SockAddr::~SockAddr () = default;
|
||||
|
||||
SockAddr::SockAddr () = default;
|
||||
|
||||
SockAddr::SockAddr (Domain const domain_)
|
||||
{
|
||||
switch (domain_)
|
||||
{
|
||||
case Domain::IPv4:
|
||||
*this = AnyIPv4;
|
||||
break;
|
||||
|
||||
#ifndef NO_IPV6
|
||||
case Domain::IPv6:
|
||||
*this = AnyIPv6;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
std::abort ();
|
||||
}
|
||||
}
|
||||
|
||||
SockAddr::SockAddr (in_addr_t const addr_, std::uint16_t const port_)
|
||||
: SockAddr (in_addr{.s_addr = addr_}, port_)
|
||||
{
|
||||
}
|
||||
|
||||
SockAddr::SockAddr (in_addr const &addr_, std::uint16_t const port_)
|
||||
{
|
||||
std::memset (&m_addr, 0, sizeof (m_addr));
|
||||
m_addr.ss_family = AF_INET;
|
||||
setAddr (addr_);
|
||||
setPort (port_);
|
||||
}
|
||||
|
||||
#ifndef NO_IPV6
|
||||
SockAddr::SockAddr (in6_addr const &addr_, std::uint16_t const port_)
|
||||
{
|
||||
std::memset (&m_addr, 0, sizeof (m_addr));
|
||||
m_addr.ss_family = AF_INET6;
|
||||
setAddr (addr_);
|
||||
setPort (port_);
|
||||
}
|
||||
#endif
|
||||
|
||||
SockAddr::SockAddr (SockAddr const &that_) = default;
|
||||
|
||||
SockAddr::SockAddr (SockAddr &&that_) = default;
|
||||
@ -39,109 +107,237 @@ SockAddr &SockAddr::operator= (SockAddr const &that_) = default;
|
||||
|
||||
SockAddr &SockAddr::operator= (SockAddr &&that_) = default;
|
||||
|
||||
SockAddr::SockAddr (struct sockaddr const &addr_)
|
||||
SockAddr::SockAddr (sockaddr_in const &addr_)
|
||||
{
|
||||
switch (addr_.sa_family)
|
||||
assert (addr_.sin_family == AF_INET);
|
||||
std::memcpy (&m_addr, &addr_, sizeof (sockaddr_in));
|
||||
}
|
||||
|
||||
#ifndef NO_IPV6
|
||||
SockAddr::SockAddr (sockaddr_in6 const &addr_)
|
||||
{
|
||||
assert (addr_.sin6_family == AF_INET6);
|
||||
std::memcpy (&m_addr, &addr_, sizeof (sockaddr_in6));
|
||||
}
|
||||
#endif
|
||||
|
||||
SockAddr::SockAddr (sockaddr_storage const &addr_)
|
||||
{
|
||||
switch (addr_.ss_family)
|
||||
{
|
||||
case AF_INET:
|
||||
std::memcpy (&m_addr, &addr_, sizeof (struct sockaddr_in));
|
||||
std::memcpy (&m_addr, &addr_, sizeof (sockaddr_in));
|
||||
break;
|
||||
|
||||
#ifndef NO_IPV6
|
||||
case AF_INET6:
|
||||
std::memcpy (&m_addr, &addr_, sizeof (struct sockaddr_in6));
|
||||
std::memcpy (&m_addr, &addr_, sizeof (sockaddr_in6));
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
std::abort ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SockAddr::SockAddr (struct sockaddr_in const &addr_)
|
||||
: SockAddr (reinterpret_cast<struct sockaddr const &> (addr_))
|
||||
SockAddr::operator sockaddr_in const & () const
|
||||
{
|
||||
assert (m_addr.ss_family == AF_INET);
|
||||
return reinterpret_cast<sockaddr_in const &> (m_addr);
|
||||
}
|
||||
|
||||
#if !defined(__3DS__) && !defined(__WIIU__)
|
||||
SockAddr::SockAddr (struct sockaddr_in6 const &addr_)
|
||||
: SockAddr (reinterpret_cast<struct sockaddr const &> (addr_))
|
||||
#ifndef NO_IPV6
|
||||
SockAddr::operator sockaddr_in6 const & () const
|
||||
{
|
||||
assert (m_addr.ss_family == AF_INET6);
|
||||
return reinterpret_cast<sockaddr_in6 const &> (m_addr);
|
||||
}
|
||||
#endif
|
||||
|
||||
SockAddr::SockAddr (struct sockaddr_storage const &addr_)
|
||||
: SockAddr (reinterpret_cast<struct sockaddr const &> (addr_))
|
||||
{
|
||||
}
|
||||
|
||||
SockAddr::operator struct sockaddr_in const & () const
|
||||
{
|
||||
assert (m_addr.ss_family == AF_INET);
|
||||
return reinterpret_cast<struct sockaddr_in const &> (m_addr);
|
||||
}
|
||||
|
||||
#if !defined(__3DS__) && !defined(__WIIU__)
|
||||
SockAddr::operator struct sockaddr_in6 const & () const
|
||||
{
|
||||
assert (m_addr.ss_family == AF_INET6);
|
||||
return reinterpret_cast<struct sockaddr_in6 const &> (m_addr);
|
||||
}
|
||||
#endif
|
||||
|
||||
SockAddr::operator struct sockaddr_storage const & () const
|
||||
SockAddr::operator sockaddr_storage const & () const
|
||||
{
|
||||
return m_addr;
|
||||
}
|
||||
|
||||
SockAddr::operator struct sockaddr * ()
|
||||
SockAddr::operator sockaddr * ()
|
||||
{
|
||||
return reinterpret_cast<struct sockaddr *> (&m_addr);
|
||||
return reinterpret_cast<sockaddr *> (&m_addr);
|
||||
}
|
||||
|
||||
SockAddr::operator struct sockaddr const * () const
|
||||
SockAddr::operator sockaddr const * () const
|
||||
{
|
||||
return reinterpret_cast<struct sockaddr const *> (&m_addr);
|
||||
return reinterpret_cast<sockaddr const *> (&m_addr);
|
||||
}
|
||||
|
||||
bool SockAddr::setPort (std::uint16_t const port_)
|
||||
bool SockAddr::operator== (SockAddr const &that_) const
|
||||
{
|
||||
if (m_addr.ss_family != that_.m_addr.ss_family)
|
||||
return false;
|
||||
|
||||
switch (m_addr.ss_family)
|
||||
{
|
||||
case AF_INET:
|
||||
reinterpret_cast<struct sockaddr_in *> (&m_addr)->sin_port = htons (port_);
|
||||
return true;
|
||||
if (port () != that_.port ())
|
||||
return false;
|
||||
|
||||
// ignore sin_zero
|
||||
return static_cast<sockaddr_in const &> (*this).sin_addr.s_addr ==
|
||||
static_cast<sockaddr_in const &> (that_).sin_addr.s_addr;
|
||||
|
||||
#ifndef NO_IPV6
|
||||
case AF_INET6:
|
||||
reinterpret_cast<struct sockaddr_in6 *> (&m_addr)->sin6_port = htons (port_);
|
||||
return true;
|
||||
return std::memcmp (&m_addr, &that_.m_addr, sizeof (sockaddr_in6)) == 0;
|
||||
#endif
|
||||
|
||||
default:
|
||||
std::abort ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::strong_ordering SockAddr::operator<=> (SockAddr const &that_) const
|
||||
{
|
||||
if (m_addr.ss_family != that_.m_addr.ss_family)
|
||||
return m_addr.ss_family <=> that_.m_addr.ss_family;
|
||||
|
||||
switch (m_addr.ss_family)
|
||||
{
|
||||
case AF_INET:
|
||||
{
|
||||
auto const cmp =
|
||||
strongMemCompare (&static_cast<sockaddr_in const &> (*this).sin_addr.s_addr,
|
||||
&static_cast<sockaddr_in const &> (that_).sin_addr.s_addr,
|
||||
sizeof (in_addr_t));
|
||||
|
||||
if (cmp != std::strong_ordering::equal)
|
||||
return cmp;
|
||||
|
||||
return port () <=> that_.port ();
|
||||
}
|
||||
|
||||
#ifndef NO_IPV6
|
||||
case AF_INET6:
|
||||
{
|
||||
auto const &addr1 = static_cast<sockaddr_in6 const &> (*this);
|
||||
auto const &addr2 = static_cast<sockaddr_in6 const &> (that_);
|
||||
|
||||
if (auto const cmp =
|
||||
strongMemCompare (&addr1.sin6_addr, &addr2.sin6_addr, sizeof (in6_addr));
|
||||
cmp != std::strong_ordering::equal)
|
||||
return cmp;
|
||||
|
||||
auto const p1 = port ();
|
||||
auto const p2 = that_.port ();
|
||||
|
||||
if (p1 < p2)
|
||||
return std::strong_ordering::less;
|
||||
else if (p1 > p2)
|
||||
return std::strong_ordering::greater;
|
||||
|
||||
if (auto const cmp = strongMemCompare (
|
||||
&addr1.sin6_flowinfo, &addr2.sin6_flowinfo, sizeof (std::uint32_t));
|
||||
cmp != std::strong_ordering::equal)
|
||||
return cmp;
|
||||
|
||||
return strongMemCompare (
|
||||
&addr1.sin6_flowinfo, &addr2.sin6_flowinfo, sizeof (std::uint32_t));
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
std::abort ();
|
||||
}
|
||||
}
|
||||
|
||||
void SockAddr::setAddr (in_addr_t const addr_)
|
||||
{
|
||||
setAddr (in_addr{.s_addr = addr_});
|
||||
}
|
||||
|
||||
void SockAddr::setAddr (in_addr const &addr_)
|
||||
{
|
||||
if (m_addr.ss_family != AF_INET)
|
||||
std::abort ();
|
||||
|
||||
std::memcpy (&reinterpret_cast<sockaddr_in &> (m_addr).sin_addr, &addr_, sizeof (addr_));
|
||||
;
|
||||
}
|
||||
|
||||
#ifndef NO_IPV6
|
||||
void SockAddr::setAddr (in6_addr const &addr_)
|
||||
{
|
||||
if (m_addr.ss_family != AF_INET6)
|
||||
std::abort ();
|
||||
|
||||
std::memcpy (&reinterpret_cast<sockaddr_in6 &> (m_addr).sin6_addr, &addr_, sizeof (addr_));
|
||||
;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::uint16_t SockAddr::port () const
|
||||
{
|
||||
switch (m_addr.ss_family)
|
||||
{
|
||||
case AF_INET:
|
||||
return ntohs (reinterpret_cast<struct sockaddr_in const *> (&m_addr)->sin_port);
|
||||
return ntohs (reinterpret_cast<sockaddr_in const *> (&m_addr)->sin_port);
|
||||
|
||||
#ifndef NO_IPV6
|
||||
case AF_INET6:
|
||||
return ntohs (reinterpret_cast<struct sockaddr_in6 const *> (&m_addr)->sin6_port);
|
||||
return ntohs (reinterpret_cast<sockaddr_in6 const *> (&m_addr)->sin6_port);
|
||||
#endif
|
||||
|
||||
default:
|
||||
std::abort ();
|
||||
}
|
||||
}
|
||||
|
||||
void SockAddr::setPort (std::uint16_t const port_)
|
||||
{
|
||||
switch (m_addr.ss_family)
|
||||
{
|
||||
case AF_INET:
|
||||
reinterpret_cast<sockaddr_in *> (&m_addr)->sin_port = htons (port_);
|
||||
break;
|
||||
|
||||
#ifndef NO_IPV6
|
||||
case AF_INET6:
|
||||
reinterpret_cast<sockaddr_in6 *> (&m_addr)->sin6_port = htons (port_);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
std::abort ();
|
||||
}
|
||||
}
|
||||
|
||||
SockAddr::Domain SockAddr::domain () const
|
||||
{
|
||||
switch (m_addr.ss_family)
|
||||
{
|
||||
case AF_INET:
|
||||
#ifndef NO_IPV6
|
||||
case AF_INET6:
|
||||
#endif
|
||||
return static_cast<Domain> (m_addr.ss_family);
|
||||
|
||||
default:
|
||||
std::abort ();
|
||||
}
|
||||
}
|
||||
|
||||
socklen_t SockAddr::size () const
|
||||
{
|
||||
switch (m_addr.ss_family)
|
||||
{
|
||||
case AF_INET:
|
||||
return sizeof (sockaddr_in);
|
||||
|
||||
#ifndef NO_IPV6
|
||||
case AF_INET6:
|
||||
return sizeof (sockaddr_in6);
|
||||
#endif
|
||||
|
||||
default:
|
||||
std::abort ();
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,33 +346,30 @@ char const *SockAddr::name (char *buffer_, std::size_t size_) const
|
||||
switch (m_addr.ss_family)
|
||||
{
|
||||
case AF_INET:
|
||||
#ifdef NDS
|
||||
return inet_ntoa (reinterpret_cast<struct sockaddr_in const *> (&m_addr)->sin_addr);
|
||||
#ifdef __NDS__
|
||||
(void)buffer_;
|
||||
(void)size_;
|
||||
return inet_ntoa (reinterpret_cast<sockaddr_in const *> (&m_addr)->sin_addr);
|
||||
#else
|
||||
return inet_ntop (AF_INET,
|
||||
&reinterpret_cast<struct sockaddr_in const *> (&m_addr)->sin_addr,
|
||||
buffer_,
|
||||
size_);
|
||||
return inet_ntop (
|
||||
AF_INET, &reinterpret_cast<sockaddr_in const *> (&m_addr)->sin_addr, buffer_, size_);
|
||||
#endif
|
||||
|
||||
#ifndef NO_IPV6
|
||||
case AF_INET6:
|
||||
return inet_ntop (AF_INET6,
|
||||
&reinterpret_cast<struct sockaddr_in6 const *> (&m_addr)->sin6_addr,
|
||||
buffer_,
|
||||
size_);
|
||||
return inet_ntop (
|
||||
AF_INET6, &reinterpret_cast<sockaddr_in6 const *> (&m_addr)->sin6_addr, buffer_, size_);
|
||||
#endif
|
||||
|
||||
default:
|
||||
std::abort ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
char const *SockAddr::name () const
|
||||
{
|
||||
#if defined(NDS) || defined(__WIIU__)
|
||||
return inet_ntoa (reinterpret_cast<struct sockaddr_in const *> (&m_addr)->sin_addr);
|
||||
#if defined(__NDS__) || defined(__WIIU__)
|
||||
return inet_ntoa (reinterpret_cast<sockaddr_in const *> (&m_addr)->sin_addr);
|
||||
#else
|
||||
#ifdef NO_IPV6
|
||||
thread_local static char buffer[INET_ADDRSTRLEN];
|
||||
|
@ -3,7 +3,7 @@
|
||||
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
||||
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
||||
//
|
||||
// Copyright (C) 2020 Michael Theall
|
||||
// Copyright (C) 2024 Michael Theall
|
||||
//
|
||||
// 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
|
||||
@ -24,6 +24,7 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -43,7 +44,7 @@ Socket::~Socket ()
|
||||
if (m_connected)
|
||||
info ("Closing connection to [%s]:%u\n", m_peerName.name (), m_peerName.port ());
|
||||
|
||||
#ifdef NDS
|
||||
#ifdef __NDS__
|
||||
if (::closesocket (m_fd) != 0)
|
||||
error ("closesocket: %s\n", std::strerror (errno));
|
||||
#else
|
||||
@ -68,7 +69,7 @@ Socket::Socket (int const fd_, SockAddr const &sockName_, SockAddr const &peerNa
|
||||
UniqueSocket Socket::accept ()
|
||||
{
|
||||
SockAddr addr;
|
||||
socklen_t addrLen = sizeof (struct sockaddr_storage);
|
||||
socklen_t addrLen = sizeof (sockaddr_storage);
|
||||
|
||||
auto const fd = ::accept (m_fd, addr, &addrLen);
|
||||
if (fd < 0)
|
||||
@ -83,7 +84,7 @@ UniqueSocket Socket::accept ()
|
||||
|
||||
int Socket::atMark ()
|
||||
{
|
||||
#ifdef NDS
|
||||
#ifdef __NDS__
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
#else
|
||||
@ -98,38 +99,16 @@ int Socket::atMark ()
|
||||
|
||||
bool Socket::bind (SockAddr const &addr_)
|
||||
{
|
||||
switch (static_cast<struct sockaddr_storage const &> (addr_).ss_family)
|
||||
if (::bind (m_fd, addr_, addr_.size ()) != 0)
|
||||
{
|
||||
case AF_INET:
|
||||
if (::bind (m_fd, addr_, sizeof (struct sockaddr_in)) != 0)
|
||||
{
|
||||
platform::Thread::sleep (5000ms);
|
||||
error ("bind: %s\n", std::strerror (errno));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
#ifndef NO_IPV6
|
||||
case AF_INET6:
|
||||
if (::bind (m_fd, addr_, sizeof (struct sockaddr_in6)) != 0)
|
||||
{
|
||||
error ("bind: %s\n", std::strerror (errno));
|
||||
platform::Thread::sleep (5000ms);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
errno = EINVAL;
|
||||
error ("bind: %s\n", std::strerror (errno));
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (addr_.port () == 0)
|
||||
{
|
||||
// get socket name due to request for ephemeral port
|
||||
socklen_t addrLen = sizeof (struct sockaddr_storage);
|
||||
socklen_t addrLen = sizeof (sockaddr_storage);
|
||||
if (::getsockname (m_fd, m_sockName, &addrLen) != 0)
|
||||
error ("getsockname: %s\n", std::strerror (errno));
|
||||
}
|
||||
@ -141,7 +120,7 @@ bool Socket::bind (SockAddr const &addr_)
|
||||
|
||||
bool Socket::connect (SockAddr const &addr_)
|
||||
{
|
||||
if (::connect (m_fd, addr_, sizeof (struct sockaddr_storage)) != 0)
|
||||
if (::connect (m_fd, addr_, addr_.size ()) != 0)
|
||||
{
|
||||
if (errno != EINPROGRESS)
|
||||
error ("connect: %s\n", std::strerror (errno));
|
||||
@ -185,11 +164,13 @@ bool Socket::shutdown (int const how_)
|
||||
|
||||
bool Socket::setLinger (bool const enable_, std::chrono::seconds const time_)
|
||||
{
|
||||
#ifdef NDS
|
||||
#ifdef __NDS__
|
||||
(void)enable_;
|
||||
(void)time_;
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
#else
|
||||
struct linger linger;
|
||||
linger linger;
|
||||
linger.l_onoff = enable_;
|
||||
linger.l_linger = time_.count ();
|
||||
|
||||
@ -209,7 +190,7 @@ bool Socket::setLinger (bool const enable_, std::chrono::seconds const time_)
|
||||
|
||||
bool Socket::setNonBlocking (bool const nonBlocking_)
|
||||
{
|
||||
#ifdef NDS
|
||||
#ifdef __NDS__
|
||||
unsigned long enable = nonBlocking_;
|
||||
|
||||
auto const rc = ::ioctl (m_fd, FIONBIO, &enable);
|
||||
@ -290,6 +271,38 @@ bool Socket::setSendBufferSize (std::size_t const size_)
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef __NDS__
|
||||
bool Socket::joinMulticastGroup (SockAddr const &addr_, SockAddr const &iface_)
|
||||
{
|
||||
ip_mreq group;
|
||||
group.imr_multiaddr = static_cast<sockaddr_in const &> (addr_).sin_addr;
|
||||
group.imr_interface = static_cast<sockaddr_in const &> (iface_).sin_addr;
|
||||
|
||||
if (::setsockopt (m_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof (group)) != 0)
|
||||
{
|
||||
error ("setsockopt(IP_ADD_MEMBERSHIP, %s): %s\n", addr_.name (), std::strerror (errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Socket::dropMulticastGroup (SockAddr const &addr_, SockAddr const &iface_)
|
||||
{
|
||||
ip_mreq group;
|
||||
group.imr_multiaddr = static_cast<sockaddr_in const &> (addr_).sin_addr;
|
||||
group.imr_interface = static_cast<sockaddr_in const &> (iface_).sin_addr;
|
||||
|
||||
if (::setsockopt (m_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &group, sizeof (group)) != 0)
|
||||
{
|
||||
error ("setsockopt(IP_DROP_MEMBERSHIP, %s): %s\n", addr_.name (), std::strerror (errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::make_signed_t<std::size_t>
|
||||
Socket::read (void *const buffer_, std::size_t const size_, bool const oob_)
|
||||
{
|
||||
@ -314,6 +327,21 @@ std::make_signed_t<std::size_t> Socket::read (IOBuffer &buffer_, bool const oob_
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::make_signed_t<std::size_t>
|
||||
Socket::readFrom (void *const buffer_, std::size_t const size_, SockAddr &addr_)
|
||||
{
|
||||
assert (buffer_);
|
||||
assert (size_);
|
||||
|
||||
socklen_t addrLen = sizeof (sockaddr_storage);
|
||||
|
||||
auto const rc = ::recvfrom (m_fd, buffer_, size_, 0, addr_, &addrLen);
|
||||
if (rc < 0 && errno != EWOULDBLOCK)
|
||||
error ("recvfrom: %s\n", std::strerror (errno));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::make_signed_t<std::size_t> Socket::write (void const *const buffer_, std::size_t const size_)
|
||||
{
|
||||
assert (buffer_);
|
||||
@ -337,6 +365,19 @@ std::make_signed_t<std::size_t> Socket::write (IOBuffer &buffer_)
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::make_signed_t<std::size_t>
|
||||
Socket::writeTo (void const *buffer_, std::size_t size_, SockAddr const &addr_)
|
||||
{
|
||||
assert (buffer_);
|
||||
assert (size_ > 0);
|
||||
|
||||
auto const rc = ::sendto (m_fd, buffer_, size_, 0, addr_, addr_.size ());
|
||||
if (rc < 0 && errno != EWOULDBLOCK)
|
||||
error ("sendto: %s\n", std::strerror (errno));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
SockAddr const &Socket::sockName () const
|
||||
{
|
||||
return m_sockName;
|
||||
@ -347,9 +388,9 @@ SockAddr const &Socket::peerName () const
|
||||
return m_peerName;
|
||||
}
|
||||
|
||||
UniqueSocket Socket::create ()
|
||||
UniqueSocket Socket::create (Type const type_)
|
||||
{
|
||||
auto const fd = ::socket (AF_INET, SOCK_STREAM, 0);
|
||||
auto const fd = ::socket (AF_INET, static_cast<int> (type_), 0);
|
||||
if (fd < 0)
|
||||
{
|
||||
error ("socket: %s\n", std::strerror (errno));
|
||||
@ -366,7 +407,7 @@ int Socket::poll (PollInfo *const info_,
|
||||
if (count_ == 0)
|
||||
return 0;
|
||||
|
||||
auto const pfd = std::make_unique<struct pollfd[]> (count_);
|
||||
auto const pfd = std::make_unique<pollfd[]> (count_);
|
||||
for (std::size_t i = 0; i < count_; ++i)
|
||||
{
|
||||
pfd[i].fd = info_[i].socket.get ().m_fd;
|
||||
@ -387,8 +428,8 @@ int Socket::poll (PollInfo *const info_,
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef NDS
|
||||
extern "C" int poll (struct pollfd *const fds_, nfds_t const nfds_, int const timeout_)
|
||||
#ifdef __NDS__
|
||||
extern "C" int poll (pollfd *const fds_, nfds_t const nfds_, int const timeout_)
|
||||
{
|
||||
fd_set readFds;
|
||||
fd_set writeFds;
|
||||
@ -406,7 +447,7 @@ extern "C" int poll (struct pollfd *const fds_, nfds_t const nfds_, int const ti
|
||||
FD_SET (fds_[i].fd, &writeFds);
|
||||
}
|
||||
|
||||
struct timeval tv;
|
||||
timeval tv;
|
||||
tv.tv_sec = timeout_ / 1000;
|
||||
tv.tv_usec = (timeout_ % 1000) * 1000;
|
||||
auto const rc = ::select (nfds_, &readFds, &writeFds, &exceptFds, &tv);
|
||||
|
@ -459,6 +459,12 @@ void platform::Thread::sleep (std::chrono::milliseconds const timeout_)
|
||||
std::this_thread::sleep_for (timeout_);
|
||||
}
|
||||
|
||||
std::string const &platform::hostname ()
|
||||
{
|
||||
static std::string const hostname = "wiiu-ftpd";
|
||||
return hostname;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
#define USE_STD_MUTEX 1
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user