mirror of
https://github.com/wiiu-env/ftpiiu_plugin.git
synced 2024-12-26 20:51:47 +01:00
383 lines
8.1 KiB
C++
383 lines
8.1 KiB
C++
// ftpd is a server implementation based on the following:
|
|
// - RFC 959 (https://tools.ietf.org/html/rfc959)
|
|
// - RFC 3659 (https://tools.ietf.org/html/rfc3659)
|
|
// - suggested implementation details from https://cr.yp.to/ftp/filesystem.html
|
|
//
|
|
// Copyright (C) 2024 Michael Theall
|
|
//
|
|
// 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 "sockAddr.h"
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <cassert>
|
|
#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;
|
|
|
|
SockAddr &SockAddr::operator= (SockAddr const &that_) = default;
|
|
|
|
SockAddr &SockAddr::operator= (SockAddr &&that_) = default;
|
|
|
|
SockAddr::SockAddr (sockaddr_in const &addr_)
|
|
{
|
|
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 (sockaddr_in));
|
|
break;
|
|
|
|
#ifndef NO_IPV6
|
|
case AF_INET6:
|
|
std::memcpy (&m_addr, &addr_, sizeof (sockaddr_in6));
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
std::abort ();
|
|
}
|
|
}
|
|
|
|
SockAddr::operator sockaddr_in const & () const
|
|
{
|
|
assert (m_addr.ss_family == AF_INET);
|
|
return reinterpret_cast<sockaddr_in const &> (m_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::operator sockaddr_storage const & () const
|
|
{
|
|
return m_addr;
|
|
}
|
|
|
|
SockAddr::operator sockaddr * ()
|
|
{
|
|
return reinterpret_cast<sockaddr *> (&m_addr);
|
|
}
|
|
|
|
SockAddr::operator sockaddr const * () const
|
|
{
|
|
return reinterpret_cast<sockaddr const *> (&m_addr);
|
|
}
|
|
|
|
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:
|
|
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:
|
|
return std::memcmp (&m_addr, &that_.m_addr, sizeof (sockaddr_in6)) == 0;
|
|
#endif
|
|
|
|
default:
|
|
std::abort ();
|
|
}
|
|
}
|
|
|
|
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<sockaddr_in const *> (&m_addr)->sin_port);
|
|
|
|
#ifndef NO_IPV6
|
|
case AF_INET6:
|
|
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 ();
|
|
}
|
|
}
|
|
|
|
char const *SockAddr::name (char *buffer_, std::size_t size_) const
|
|
{
|
|
switch (m_addr.ss_family)
|
|
{
|
|
case AF_INET:
|
|
#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<sockaddr_in const *> (&m_addr)->sin_addr, buffer_, size_);
|
|
#endif
|
|
|
|
#ifndef NO_IPV6
|
|
case AF_INET6:
|
|
return inet_ntop (
|
|
AF_INET6, &reinterpret_cast<sockaddr_in6 const *> (&m_addr)->sin6_addr, buffer_, size_);
|
|
#endif
|
|
|
|
default:
|
|
std::abort ();
|
|
}
|
|
}
|
|
|
|
char const *SockAddr::name () const
|
|
{
|
|
#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];
|
|
#else
|
|
thread_local static char buffer[INET6_ADDRSTRLEN];
|
|
#endif
|
|
|
|
return name (buffer, sizeof (buffer));
|
|
#endif
|
|
}
|