mirror of
https://github.com/anope/anope.git
synced 2026-06-25 23:46:37 +02:00
29e7674e56
Having these throw is terrible for ergonomics and there are loads of places where the exception was either silently ignored or not handled at all. Having a function which returns an optional and another that returns a default works a lot better imo.
648 lines
13 KiB
C++
648 lines
13 KiB
C++
/*
|
|
*
|
|
* (C) 2003-2024 Anope Team
|
|
* Contact us at team@anope.org
|
|
*
|
|
* Please read COPYING and README for further details.
|
|
*
|
|
* Based on the original code of Epona by Lara.
|
|
* Based on the original code of Services by Andy Church.
|
|
*/
|
|
|
|
#include "services.h"
|
|
#include "sockets.h"
|
|
#include "socketengine.h"
|
|
#include "logger.h"
|
|
|
|
#ifndef _WIN32
|
|
#include <arpa/inet.h>
|
|
#include <cerrno>
|
|
#include <fcntl.h>
|
|
#endif
|
|
|
|
std::map<int, Socket *> SocketEngine::Sockets;
|
|
|
|
uint32_t TotalRead = 0;
|
|
uint32_t TotalWritten = 0;
|
|
|
|
SocketIO NormalSocketIO;
|
|
|
|
sockaddrs::sockaddrs(const Anope::string &address)
|
|
{
|
|
this->clear();
|
|
if (!address.empty() && address.find_first_not_of_ci("0123456789abcdef.:") == Anope::string::npos)
|
|
this->pton(address.find(':') != Anope::string::npos ? AF_INET6 : AF_INET, address);
|
|
}
|
|
|
|
void sockaddrs::clear()
|
|
{
|
|
memset(this, 0, sizeof(*this));
|
|
}
|
|
|
|
int sockaddrs::family() const
|
|
{
|
|
return sa.sa_family;
|
|
}
|
|
|
|
size_t sockaddrs::size() const
|
|
{
|
|
switch (sa.sa_family)
|
|
{
|
|
case AF_INET:
|
|
return sizeof(sa4);
|
|
case AF_INET6:
|
|
return sizeof(sa6);
|
|
case AF_UNIX:
|
|
return sizeof(saun);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sockaddrs::port() const
|
|
{
|
|
switch (sa.sa_family)
|
|
{
|
|
case AF_INET:
|
|
return ntohs(sa4.sin_port);
|
|
case AF_INET6:
|
|
return ntohs(sa6.sin6_port);
|
|
case AF_UNIX:
|
|
return 0;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
Anope::string sockaddrs::addr() const
|
|
{
|
|
switch (sa.sa_family)
|
|
{
|
|
case AF_INET:
|
|
{
|
|
char v4address[INET_ADDRSTRLEN];
|
|
if (inet_ntop(AF_INET, &sa4.sin_addr, v4address, sizeof(v4address)))
|
|
return v4address;
|
|
break;
|
|
}
|
|
case AF_INET6:
|
|
{
|
|
char v6address[INET6_ADDRSTRLEN];
|
|
if (inet_ntop(AF_INET6, &sa6.sin6_addr, v6address, sizeof(v6address)))
|
|
return v6address;
|
|
break;
|
|
}
|
|
case AF_UNIX:
|
|
return saun.sun_path;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
Anope::string sockaddrs::str() const
|
|
{
|
|
switch (sa.sa_family)
|
|
{
|
|
case AF_INET:
|
|
{
|
|
char v4address[INET_ADDRSTRLEN];
|
|
if (!inet_ntop(AF_INET, &sa4.sin_addr, v4address, sizeof(v4address)))
|
|
strcpy(v4address, "0.0.0.0");
|
|
return Anope::printf("%s:%u", v4address, sa4.sin_port);
|
|
}
|
|
case AF_INET6:
|
|
{
|
|
char v6address[INET6_ADDRSTRLEN];
|
|
if (!inet_ntop(AF_INET6, &sa6.sin6_addr, v6address, sizeof(v6address)))
|
|
strcpy(v6address, "0:0:0:0:0:0:0:0");
|
|
return Anope::printf("[%s]:%u", v6address, sa6.sin6_port);
|
|
}
|
|
case AF_UNIX:
|
|
return saun.sun_path;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
Anope::string sockaddrs::reverse() const
|
|
{
|
|
char address[128];
|
|
|
|
switch (sa.sa_family)
|
|
{
|
|
case AF_INET6:
|
|
{
|
|
static const char hex[] = "0123456789abcdef";
|
|
unsigned reverse_ip_count = 0;
|
|
for (int j = 15; j >= 0; --j)
|
|
{
|
|
address[reverse_ip_count++] = hex[sa6.sin6_addr.s6_addr[j] & 0xF];
|
|
address[reverse_ip_count++] = '.';
|
|
address[reverse_ip_count++] = hex[sa6.sin6_addr.s6_addr[j] >> 4];
|
|
address[reverse_ip_count++] = '.';
|
|
}
|
|
/* Remove the last '.' */
|
|
address[reverse_ip_count - 1] = 0;
|
|
return address;
|
|
}
|
|
case AF_INET:
|
|
{
|
|
unsigned long forward = sa4.sin_addr.s_addr;
|
|
in_addr rev;
|
|
rev.s_addr = forward << 24 | (forward & 0xFF00) << 8 | (forward & 0xFF0000) >> 8 | forward >> 24;
|
|
if (inet_ntop(AF_INET, &rev, address, sizeof(address)))
|
|
return address;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
bool sockaddrs::ipv6() const
|
|
{
|
|
return sa.sa_family == AF_INET6;
|
|
}
|
|
|
|
bool sockaddrs::valid() const
|
|
{
|
|
return size() != 0;
|
|
}
|
|
|
|
bool sockaddrs::operator==(const sockaddrs &other) const
|
|
{
|
|
if (sa.sa_family != other.sa.sa_family)
|
|
return false;
|
|
switch (sa.sa_family)
|
|
{
|
|
case AF_INET:
|
|
return (sa4.sin_port == other.sa4.sin_port) && (sa4.sin_addr.s_addr == other.sa4.sin_addr.s_addr);
|
|
case AF_INET6:
|
|
return (sa6.sin6_port == other.sa6.sin6_port) && !memcmp(sa6.sin6_addr.s6_addr, other.sa6.sin6_addr.s6_addr, 16);
|
|
default:
|
|
return !memcmp(this, &other, sizeof(*this));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void sockaddrs::pton(int type, const Anope::string &address, int pport)
|
|
{
|
|
this->clear();
|
|
|
|
switch (type)
|
|
{
|
|
case AF_INET:
|
|
{
|
|
int i = inet_pton(type, address.c_str(), &sa4.sin_addr);
|
|
if (i <= 0)
|
|
this->clear();
|
|
else
|
|
{
|
|
sa4.sin_family = type;
|
|
sa4.sin_port = htons(pport);
|
|
}
|
|
break;
|
|
}
|
|
case AF_INET6:
|
|
{
|
|
int i = inet_pton(type, address.c_str(), &sa6.sin6_addr);
|
|
if (i <= 0)
|
|
this->clear();
|
|
else
|
|
{
|
|
sa6.sin6_family = type;
|
|
sa6.sin6_port = htons(pport);
|
|
}
|
|
break;
|
|
}
|
|
case AF_UNIX:
|
|
{
|
|
if (address.length() < sizeof(saun.sun_path))
|
|
{
|
|
saun.sun_family = AF_UNIX;
|
|
memcpy(&saun.sun_path, address.c_str(), address.length() + 1);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void sockaddrs::ntop(int type, const void *src)
|
|
{
|
|
char buf[INET6_ADDRSTRLEN];
|
|
|
|
if (inet_ntop(type, src, buf, sizeof(buf)) != buf)
|
|
{
|
|
this->clear();
|
|
return;
|
|
}
|
|
|
|
switch (type)
|
|
{
|
|
case AF_INET:
|
|
sa4.sin_addr = *reinterpret_cast<const in_addr *>(src);
|
|
sa4.sin_family = type;
|
|
return;
|
|
case AF_INET6:
|
|
sa6.sin6_addr = *reinterpret_cast<const in6_addr *>(src);
|
|
sa6.sin6_family = type;
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
this->clear();
|
|
}
|
|
|
|
cidr::cidr(const Anope::string &ip)
|
|
{
|
|
bool ipv6 = ip.find(':') != Anope::string::npos;
|
|
size_t sl = ip.find_last_of('/');
|
|
|
|
if (sl == Anope::string::npos)
|
|
{
|
|
this->cidr_ip = ip;
|
|
this->cidr_len = ipv6 ? 128 : 32;
|
|
this->addr.pton(ipv6 ? AF_INET6 : AF_INET, ip);
|
|
}
|
|
else
|
|
{
|
|
Anope::string real_ip = ip.substr(0, sl);
|
|
Anope::string cidr_range = ip.substr(sl + 1);
|
|
|
|
this->cidr_ip = real_ip;
|
|
this->cidr_len = Anope::Convert<unsigned int>(cidr_range, ipv6 ? 128 : 32);
|
|
this->addr.pton(ipv6 ? AF_INET6 : AF_INET, real_ip);
|
|
}
|
|
}
|
|
|
|
cidr::cidr(const Anope::string &ip, unsigned char len)
|
|
{
|
|
bool ipv6 = ip.find(':') != Anope::string::npos;
|
|
this->addr.pton(ipv6 ? AF_INET6 : AF_INET, ip);
|
|
this->cidr_ip = ip;
|
|
this->cidr_len = len;
|
|
}
|
|
|
|
cidr::cidr(const sockaddrs &a, unsigned char len) : addr(a)
|
|
{
|
|
this->cidr_ip = a.addr();
|
|
this->cidr_len = len;
|
|
}
|
|
|
|
Anope::string cidr::mask() const
|
|
{
|
|
if ((this->addr.ipv6() && this->cidr_len == 128) || (!this->addr.ipv6() && this->cidr_len == 32))
|
|
return this->cidr_ip;
|
|
else
|
|
return Anope::printf("%s/%d", this->cidr_ip.c_str(), this->cidr_len);
|
|
}
|
|
|
|
bool cidr::match(const sockaddrs &other)
|
|
{
|
|
if (!valid() || !other.valid() || this->addr.sa.sa_family != other.sa.sa_family)
|
|
return false;
|
|
|
|
const uint8_t *ip, *their_ip;
|
|
uint8_t byte, len = this->cidr_len;
|
|
|
|
switch (this->addr.sa.sa_family)
|
|
{
|
|
case AF_INET:
|
|
ip = reinterpret_cast<const uint8_t *>(&this->addr.sa4.sin_addr);
|
|
if (len > 32)
|
|
len = 32;
|
|
byte = len / 8;
|
|
their_ip = reinterpret_cast<const uint8_t *>(&other.sa4.sin_addr);
|
|
break;
|
|
case AF_INET6:
|
|
ip = reinterpret_cast<const uint8_t *>(&this->addr.sa6.sin6_addr);
|
|
if (len > 128)
|
|
len = 128;
|
|
byte = len / 8;
|
|
their_ip = reinterpret_cast<const uint8_t *>(&other.sa6.sin6_addr);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if (memcmp(ip, their_ip, byte))
|
|
return false;
|
|
|
|
ip += byte;
|
|
their_ip += byte;
|
|
|
|
byte = len % 8;
|
|
if (byte)
|
|
{
|
|
uint8_t m = ~0u << (8 - byte);
|
|
return (*ip & m) == (*their_ip & m);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool cidr::operator<(const cidr &other) const
|
|
{
|
|
if (this->addr.sa.sa_family != other.addr.sa.sa_family)
|
|
return this->addr.sa.sa_family < other.addr.sa.sa_family;
|
|
|
|
switch (this->addr.sa.sa_family)
|
|
{
|
|
case AF_INET:
|
|
{
|
|
unsigned int m = 0xFFFFFFFFU >> (32 - this->cidr_len);
|
|
|
|
return (this->addr.sa4.sin_addr.s_addr & m) < (other.addr.sa4.sin_addr.s_addr & m);
|
|
}
|
|
case AF_INET6:
|
|
{
|
|
int i = memcmp(&this->addr.sa6.sin6_addr.s6_addr, &other.addr.sa6.sin6_addr.s6_addr, this->cidr_len / 8);
|
|
if (i || this->cidr_len >= 128)
|
|
return i < 0;
|
|
|
|
// Now all thats left is to compare 'remaining' bits at offset this->cidr_len / 8
|
|
int remaining = this->cidr_len % 8;
|
|
unsigned char m = 0xFF << (8 - remaining);
|
|
|
|
return (this->addr.sa6.sin6_addr.s6_addr[this->cidr_len / 8] & m) < (other.addr.sa6.sin6_addr.s6_addr[this->cidr_len / 8] & m);
|
|
}
|
|
default:
|
|
throw CoreException("Unknown AFTYPE for cidr");
|
|
}
|
|
}
|
|
|
|
bool cidr::operator==(const cidr &other) const
|
|
{
|
|
return !(*this < other) && !(other < *this);
|
|
}
|
|
|
|
bool cidr::operator!=(const cidr &other) const
|
|
{
|
|
return !(*this == other);
|
|
}
|
|
|
|
bool cidr::valid() const
|
|
{
|
|
return this->addr.valid();
|
|
}
|
|
|
|
size_t cidr::hash::operator()(const cidr &s) const
|
|
{
|
|
switch (s.addr.sa.sa_family)
|
|
{
|
|
case AF_INET:
|
|
{
|
|
unsigned int m = 0xFFFFFFFFU >> (32 - s.cidr_len);
|
|
return s.addr.sa4.sin_addr.s_addr & m;
|
|
}
|
|
case AF_INET6:
|
|
{
|
|
size_t h = 0;
|
|
|
|
for (unsigned i = 0; i < s.cidr_len / 8; ++i)
|
|
h ^= (s.addr.sa6.sin6_addr.s6_addr[i] << ((i * 8) % sizeof(size_t)));
|
|
|
|
int remaining = s.cidr_len % 8;
|
|
unsigned char m = 0xFF << (8 - remaining);
|
|
|
|
h ^= s.addr.sa6.sin6_addr.s6_addr[s.cidr_len / 8] & m;
|
|
|
|
return h;
|
|
}
|
|
default:
|
|
throw CoreException("Unknown AFTYPE for cidr");
|
|
}
|
|
}
|
|
|
|
int SocketIO::Recv(Socket *s, char *buf, size_t sz)
|
|
{
|
|
int i = recv(s->GetFD(), buf, sz, 0);
|
|
if (i > 0)
|
|
TotalRead += i;
|
|
return i;
|
|
}
|
|
|
|
int SocketIO::Send(Socket *s, const char *buf, size_t sz)
|
|
{
|
|
int i = send(s->GetFD(), buf, sz, 0);
|
|
if (i > 0)
|
|
TotalWritten += i;
|
|
return i;
|
|
}
|
|
|
|
int SocketIO::Send(Socket *s, const Anope::string &buf)
|
|
{
|
|
return this->Send(s, buf.c_str(), buf.length());
|
|
}
|
|
|
|
ClientSocket *SocketIO::Accept(ListenSocket *s)
|
|
{
|
|
sockaddrs conaddr;
|
|
|
|
socklen_t size = sizeof(conaddr);
|
|
int newsock = accept(s->GetFD(), &conaddr.sa, &size);
|
|
|
|
if (newsock >= 0)
|
|
{
|
|
ClientSocket *ns = s->OnAccept(newsock, conaddr);
|
|
ns->flags[SF_ACCEPTED] = true;
|
|
ns->OnAccept();
|
|
return ns;
|
|
}
|
|
else
|
|
throw SocketException("Unable to accept connection: " + Anope::LastError());
|
|
}
|
|
|
|
SocketFlag SocketIO::FinishAccept(ClientSocket *cs)
|
|
{
|
|
return SF_ACCEPTED;
|
|
}
|
|
|
|
void SocketIO::Bind(Socket *s, const Anope::string &ip, int port)
|
|
{
|
|
s->bindaddr.pton(s->GetFamily(), ip, port);
|
|
if (bind(s->GetFD(), &s->bindaddr.sa, s->bindaddr.size()) == -1)
|
|
throw SocketException("Unable to bind to address: " + Anope::LastError());
|
|
}
|
|
|
|
void SocketIO::Connect(ConnectionSocket *s, const Anope::string &target, int port)
|
|
{
|
|
s->flags[SF_CONNECTING] = s->flags[SF_CONNECTED] = false;
|
|
s->conaddr.pton(s->GetFamily(), target, port);
|
|
int c = connect(s->GetFD(), &s->conaddr.sa, s->conaddr.size());
|
|
if (c == -1)
|
|
{
|
|
if (!SocketEngine::IgnoreErrno())
|
|
s->OnError(Anope::LastError());
|
|
else
|
|
{
|
|
SocketEngine::Change(s, true, SF_WRITABLE);
|
|
s->flags[SF_CONNECTING] = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
s->flags[SF_CONNECTED] = true;
|
|
s->OnConnect();
|
|
}
|
|
}
|
|
|
|
SocketFlag SocketIO::FinishConnect(ConnectionSocket *s)
|
|
{
|
|
if (s->flags[SF_CONNECTED])
|
|
return SF_CONNECTED;
|
|
else if (!s->flags[SF_CONNECTING])
|
|
throw SocketException("SocketIO::FinishConnect called for a socket not connected nor connecting?");
|
|
|
|
int optval = 0;
|
|
socklen_t optlen = sizeof(optval);
|
|
if (!getsockopt(s->GetFD(), SOL_SOCKET, SO_ERROR, reinterpret_cast<char *>(&optval), &optlen) && !optval)
|
|
{
|
|
s->flags[SF_CONNECTED] = true;
|
|
s->flags[SF_CONNECTING] = false;
|
|
s->OnConnect();
|
|
return SF_CONNECTED;
|
|
}
|
|
else
|
|
{
|
|
errno = optval;
|
|
s->OnError(optval ? Anope::LastError() : "");
|
|
return SF_DEAD;
|
|
}
|
|
}
|
|
|
|
Socket::Socket()
|
|
{
|
|
throw CoreException("Socket::Socket() ?");
|
|
}
|
|
|
|
Socket::Socket(int s, int f, int type)
|
|
{
|
|
this->io = &NormalSocketIO;
|
|
this->family = f;
|
|
if (s == -1)
|
|
this->sock = socket(this->family, type, 0);
|
|
else
|
|
this->sock = s;
|
|
this->SetBlocking(false);
|
|
SocketEngine::Sockets[this->sock] = this;
|
|
SocketEngine::Change(this, true, SF_READABLE);
|
|
}
|
|
|
|
Socket::~Socket()
|
|
{
|
|
SocketEngine::Change(this, false, SF_READABLE);
|
|
SocketEngine::Change(this, false, SF_WRITABLE);
|
|
anope_close(this->sock);
|
|
this->io->Destroy();
|
|
SocketEngine::Sockets.erase(this->sock);
|
|
}
|
|
|
|
int Socket::GetFamily() const
|
|
{
|
|
return family;
|
|
}
|
|
|
|
int Socket::GetFD() const
|
|
{
|
|
return sock;
|
|
}
|
|
|
|
bool Socket::SetBlocking(bool state)
|
|
{
|
|
int f = fcntl(this->GetFD(), F_GETFL, 0);
|
|
if (state)
|
|
return !fcntl(this->GetFD(), F_SETFL, f & ~O_NONBLOCK);
|
|
else
|
|
return !fcntl(this->GetFD(), F_SETFL, f | O_NONBLOCK);
|
|
}
|
|
|
|
void Socket::Bind(const Anope::string &ip, int port)
|
|
{
|
|
this->io->Bind(this, ip, port);
|
|
}
|
|
|
|
bool Socket::Process()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool Socket::ProcessRead()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool Socket::ProcessWrite()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void Socket::ProcessError()
|
|
{
|
|
}
|
|
|
|
ListenSocket::ListenSocket(const Anope::string &bindip, int port, bool i)
|
|
{
|
|
this->SetBlocking(false);
|
|
|
|
const int op = 1;
|
|
setsockopt(this->GetFD(), SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char *>(&op), sizeof(op));
|
|
|
|
this->bindaddr.pton(i ? AF_INET6 : AF_INET, bindip, port);
|
|
this->io->Bind(this, bindip, port);
|
|
|
|
if (listen(sock, SOMAXCONN) == -1)
|
|
throw SocketException("Unable to listen: " + Anope::LastError());
|
|
}
|
|
|
|
bool ListenSocket::ProcessRead()
|
|
{
|
|
try
|
|
{
|
|
this->io->Accept(this);
|
|
}
|
|
catch (const SocketException &ex)
|
|
{
|
|
Log() << ex.GetReason();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int SocketEngine::GetLastError()
|
|
{
|
|
#ifndef _WIN32
|
|
return errno;
|
|
#else
|
|
return WSAGetLastError();
|
|
#endif
|
|
}
|
|
|
|
void SocketEngine::SetLastError(int err)
|
|
{
|
|
#ifndef _WIN32
|
|
errno = err;
|
|
#else
|
|
WSASetLastError(err);
|
|
#endif
|
|
}
|
|
|
|
bool SocketEngine::IgnoreErrno()
|
|
{
|
|
return GetLastError() == EAGAIN
|
|
|| GetLastError() == EWOULDBLOCK
|
|
|| GetLastError() == EINTR
|
|
|| GetLastError() == EINPROGRESS;
|
|
}
|