Teaspeak-Server/server/src/server/VoiceServer.cpp

529 lines
21 KiB
C++

#define TIMING_DISABLED
#include "POWHandler.h"
#include <thread>
#include <algorithm>
#include <unistd.h>
#include <fcntl.h>
#include "VoiceServer.h"
#include "../client/voice/VoiceClient.h"
#include "../Configuration.h"
#include <log/LogUtils.h>
#include "src/VirtualServer.h"
#include <misc/endianness.h>
#include "src/VirtualServerManager.h"
#include "../InstanceHandler.h"
#include <ThreadPool/Timer.h>
#include <pipes/buffer.h>
#include <netinet/in.h>
#include <misc/sassert.h>
#include "misc/timer.h"
using namespace std;
using namespace std::chrono;
using namespace ts::server;
using namespace ts::buffer;
using namespace ts;
extern InstanceHandler* serverInstance;
VoiceServer::VoiceServer(const std::shared_ptr<VirtualServer>& server) {
this->server = server;
this->pow_handler = make_unique<POWHandler>(this);
}
VoiceServer::~VoiceServer() { }
#define SET_OPTION(type, option, flag, error) \
if(setsockopt(bind->file_descriptor, type, option, &flag, sizeof(flag)) < 0) { \
error; \
::close(bind->file_descriptor); \
bind->file_descriptor = 0; \
continue; \
}
bool VoiceServer::start(const std::deque<std::shared_ptr<VoiceServerBinding>>& binding, std::string& error) {
if(this->running) return false;
if(binding.empty()) {
error = "Missing bindings!";
return false;
}
this->running = true;
this->bindings = binding;
int enable = 1, disable = 0;
for (auto &bind : binding) {
bind->file_descriptor = socket(bind->address.ss_family, SOCK_DGRAM, 0);
if(!bind->file_descriptor) {
logError(this->server->getServerId(), "Failed to create socket for {}", bind->address_string());
continue;
}
if(setsockopt(bind->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &disable, sizeof(int)) < 0) logError(this->server->getServerId(), "Could not disable flag reuse address for bound {}!", bind->address_string());
//if(setsockopt(bind->file_descriptor, SOL_SOCKET, SO_REUSEPORT, &disable, sizeof(int)) < 0) logError(this->server->getServerId(), "Could not disable flag reuse port for bound {}!", bind->address_string());
/* We're never sending over MTU size packets! */
int pmtu = IP_PMTUDISC_DO;
setsockopt(bind->file_descriptor, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
if(fcntl(bind->file_descriptor, F_SETFD, FD_CLOEXEC) < 0)
logError(this->server->getServerId(), "Failed to enable FD_CLOEXEC for {} ({}) (VoiceServer)", bind->file_descriptor, bind->address_string());
if(bind->address.ss_family == AF_INET6) {
SET_OPTION(IPPROTO_IPV6, IPV6_RECVPKTINFO, enable, {
logError(this->server->getServerId(), "Failed to enable packet info (v6) for {}", bind->address_string());
});
SET_OPTION(IPPROTO_IPV6, IPV6_V6ONLY, enable, {
logError(this->server->getServerId(), "Failed to enable ip v6 only for {}", bind->address_string());
});
} else {
SET_OPTION(IPPROTO_IP, IP_PKTINFO, enable, {
logError(this->server->getServerId(), "Failed to enable packet info for {}", bind->address_string());
});
}
if(::bind(bind->file_descriptor, (const sockaddr*) &bind->address, net::address_size(bind->address)) < 0) {
logError(this->server->getServerId(), "Failed to bind to {} ({} => {})", bind->address_string(), errno, strerror(errno));
::close(bind->file_descriptor);
bind->file_descriptor = 0;
continue;
}
fcntl(bind->file_descriptor, F_SETFL, fcntl(bind->file_descriptor, F_GETFL, 0) | O_NONBLOCK);
}
{
auto bindings = this->activeBindings();
if(bindings.empty()) {
error = "Failed to bind any address!";
this->running = false;
return false;
}
string str;
for(auto it = bindings.begin(); it != bindings.end(); it++) {
str += net::to_string((*it)->address) + (it + 1 == bindings.end() ? "" : " | ");
}
logMessage(this->server->getServerId(), "Started server on {}.", str);
}
this->io = serverInstance->getVoiceServerManager()->ioManager()->enableIo(this->server.get());
return true;
}
void VoiceServer::triggerWrite(const std::shared_ptr<VoiceClient>& client) {
if(!client) {
logError(this->server->getServerId(), "Invalid client for triggerWrite()");
return;
}
if(auto io{this->io}; io) {
io->invoke_write(client);
}
}
void VoiceServer::schedule_command_handling(const ts::server::VoiceClient *client) {
auto vmanager = serverInstance->getVoiceServerManager();
if(!vmanager)
return;
auto evloop = vmanager->get_executor_loop();
if(!evloop)
return;
evloop->schedule(client->event_handle_packet);
}
void VoiceServer::tickHandshakingClients() {
this->pow_handler->execute_tick();
decltype(this->activeConnections) connections;
{
lock_guard lock(this->connectionLock);
connections = this->activeConnections;
}
for(const auto& client : connections)
if(client->state == ConnectionState::INIT_HIGH || client->state == ConnectionState::INIT_LOW)
client->tick(system_clock::now());
}
void VoiceServer::execute_resend(const std::chrono::system_clock::time_point &now, std::chrono::system_clock::time_point &next) {
decltype(this->activeConnections) connections;
{
lock_guard lock(this->connectionLock);
connections = this->activeConnections;
}
string error;
for(const auto& client : connections) {
auto connection = client->getConnection();
sassert(connection); /* its not possible that a client hasn't a connection! */
connection->packet_encoder().execute_resend(now, next);
}
}
bool VoiceServer::stop(const std::chrono::milliseconds& flushTimeout) {
if(!this->running) return false;
this->running = false;
this->connectionLock.lock();
auto list = this->activeConnections;
this->connectionLock.unlock();
for(const auto &e : list)
e->close_connection(system_clock::now() + seconds(1));
auto beg = system_clock::now();
while(!this->activeConnections.empty() && flushTimeout.count() != 0 && system_clock::now() - beg < flushTimeout)
threads::self::sleep_for(milliseconds(10));
for(const auto& connection : this->activeConnections)
connection->voice_server = nullptr;
this->activeConnections.clear();
serverInstance->getVoiceServerManager()->ioManager()->disableIo(this->server.get());
this->io = nullptr;
for(const auto& bind : this->bindings) {
if(bind->file_descriptor > 0){
if(!shutdown(bind->file_descriptor, SHUT_RDWR)) logError(this->server->getServerId(), "Failed to shutdown socket {} ({}) Reason: {}/{}", bind->file_descriptor, bind->address_string(), errno, strerror(errno));
if(!close(bind->file_descriptor)) {
if(errno != ENOTCONN)
logError(this->server->getServerId(), "Failed to close socket {} ({}) Reason: {}/{}", bind->file_descriptor, bind->address_string(), errno, strerror(errno));
}
bind->file_descriptor = 0;
}
}
this->bindings.clear();
return true;
}
std::shared_ptr<VoiceClient> VoiceServer::findClient(ts::ClientId client) {
lock_guard lock(this->connectionLock);
for(const auto& elm : this->activeConnections) {
if(elm->getClientId() == client)
return elm;
}
return nullptr;
}
std::shared_ptr<VoiceClient> VoiceServer::findClient(sockaddr_in *addr, bool) {
lock_guard lock(this->connectionLock);
for(const auto& elm : this->activeConnections) {
if(elm->isAddressV4())
if(elm->getAddressV4()->sin_addr.s_addr == addr->sin_addr.s_addr)
if(elm->getAddressV4()->sin_port == addr->sin_port)
return elm;
}
return nullptr;
}
std::shared_ptr<VoiceClient> VoiceServer::findClient(sockaddr_in6 *addr, bool) {
lock_guard lock(this->connectionLock);
for(const auto& elm : this->activeConnections) {
if(elm->isAddressV6())
if(memcmp(elm->getAddressV6()->sin6_addr.__in6_u.__u6_addr8, addr->sin6_addr.__in6_u.__u6_addr8, 16) == 0)
if(elm->getAddressV6()->sin6_port == addr->sin6_port)
return elm;
}
return nullptr;
}
bool VoiceServer::unregisterConnection(std::shared_ptr<VoiceClient> connection) {
lock_guard lock(this->connectionLock);
auto found = std::find(this->activeConnections.begin(), this->activeConnections.end(), connection);
if(found != activeConnections.end())
this->activeConnections.erase(found);
else logError(LOG_GENERAL, "unregisterConnection(...) -> could not find client");
return true;
}
static union {
char literal[8] = {'T', 'S', '3', 'I', 'N', 'I', 'T', '1'};
uint64_t integral;
} TS3INIT;
void VoiceServer::handleMessageRead(int fd, short events, void *_event_handle) {
auto event_handle = (io::IOEventLoopEntry*) _event_handle;
auto voice_server = event_handle->voice_server;
auto ts_server = event_handle->server;
size_t raw_read_buffer_length = event_handle->family == AF_INET ? 600 : 1600; //IPv6 MTU: 1500 | IPv4 MTU: 576
uint8_t raw_read_buffer[raw_read_buffer_length]; //Allocate on stack, so we dont need heap here
ssize_t bytes_read = 0;
pipes::buffer_view read_buffer{raw_read_buffer, raw_read_buffer_length}; /* will not allocate anything, just sets its mode to ptr and thats it :) */
sockaddr_storage remote_address{};
iovec io_vector{};
io_vector.iov_base = (void*) raw_read_buffer;
io_vector.iov_len = raw_read_buffer_length;
char message_headers[0x100];
msghdr message{};
message.msg_name = &remote_address;
message.msg_namelen = sizeof(remote_address);
message.msg_iov = &io_vector;
message.msg_iovlen = 1;
message.msg_control = message_headers;
message.msg_controllen = 0x100;
auto read_timeout = system_clock::now() + microseconds(2500); /* read 2.5ms long at a time or 'till nothing more is there */
while(system_clock::now() <= read_timeout){
message.msg_flags = 0;
bytes_read = recvmsg(fd, &message, 0);
if((message.msg_flags & MSG_TRUNC) > 0)
logError(ts_server->getServerId(), "Received truncated message from {}", net::to_string(remote_address));
if(bytes_read < 0) {
if(errno == EAGAIN)
break;
//Nothing more to read
logCritical(ts_server->getServerId(), "Could not receive datagram packet! Code: {} Reason: {}", errno, strerror(errno));
break;
} else if(bytes_read == 0){
//This should never happen
break;
}
if(bytes_read < MAC_SIZE + CLIENT_HEADER_SIZE) {
/* reenable for debug. else short packages could be a dos attach */
//logError(ts_server->getServerId(), "Received an too short packet!");
continue;
}
shared_ptr<VoiceClient> client;
{
if(*(uint64_t*) raw_read_buffer == TS3INIT.integral) {
//Handle ddos protection...
voice_server->pow_handler->handle_datagram(event_handle->socket_id, remote_address, message, read_buffer.view(0, bytes_read));
} else {
auto client_id = (ClientId) be2le16(&raw_read_buffer[10]);
if(client_id > 0) {
client = dynamic_pointer_cast<VoiceClient>(voice_server->server->find_client_by_id(client_id));
} else {
client = voice_server->findClient(&remote_address, true);
}
}
}
if(!client)
continue;
if(memcmp(&client->remote_address, &remote_address, sizeof(sockaddr_storage)) != 0) { /* verify the remote address */
if((read_buffer[12] & 0x80) == 0 && client->state == ConnectionState::CONNECTED) { /* only encrypted packets are allowed */
if(client->connection->verify_encryption(read_buffer.view(0, bytes_read))) { /* the ip had changed */
auto old_address = net::to_string(client->remote_address);
auto new_address = net::to_string(remote_address);
auto command = "dummy_ipchange old_ip=" + old_address + " new_ip=" + new_address;
client->server_command_executor().force_insert_command(pipes::buffer_view{command.data(), command.length()});
memcpy(&client->remote_address, &remote_address, sizeof(remote_address));
udp::DatagramPacket::extract_info(message, client->connection->remote_address_info_);
}
} else {
continue; /* we've no clue */
}
}
if(client->state != ConnectionState::DISCONNECTED){
client->connection->handle_incoming_datagram(read_buffer.view(0, bytes_read));
client = nullptr;
}
}
}
#ifndef USE_TIMER
#ifdef ALARM_TIMER
#undef ALARM_TIMER
#endif
#define ALARM_TIMER(...)
#endif
template <int MHS>
struct IOData {
int file_descriptor = 0;
iovec vector{};
struct msghdr message{};
char message_headers[MHS]{};
IOData() {
/* Speed is key here, we dont need to zero paddings!
memset(&this->vector, 0, sizeof(this->vector));
memset(&this->message, 0, sizeof(this->message));
memset(this->message_headers, 0, sizeof(this->message_headers));
*/
this->vector.iov_base = nullptr;
this->vector.iov_len = 0;
this->message.msg_name = nullptr;
this->message.msg_namelen = 0;
this->message.msg_iov = &vector;
this->message.msg_iovlen = 1;
this->message.msg_control = this->message_headers;
this->message.msg_controllen = sizeof(this->message_headers);
}
};
template <int MHS>
inline ssize_t write_datagram(IOData<MHS>& io, const sockaddr_storage& address, udp::pktinfo_storage* info, size_t length, const void* buffer) {
io.message.msg_flags = 0;
io.message.msg_name = (void*) &address;
io.message.msg_namelen = address.ss_family == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
io.vector.iov_len = length;
io.vector.iov_base = (void*) buffer;
if(info) {
auto cmsg = CMSG_FIRSTHDR(&io.message);
if(address.ss_family == AF_INET) {
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
memcpy(CMSG_DATA(cmsg), info, sizeof(in_pktinfo));
io.message.msg_controllen = CMSG_SPACE(sizeof(in_pktinfo));
} else if(address.ss_family == AF_INET6) {
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
memcpy(CMSG_DATA(cmsg), info, sizeof(in6_pktinfo));
io.message.msg_controllen = CMSG_SPACE(sizeof(in6_pktinfo));
} else if(address.ss_family == 0)
return length; /* address is unset (testing ip loss i guess) */
} else {
io.message.msg_controllen = 0;
}
auto status = sendmsg(io.file_descriptor, &io.message, 0);
if(status< 0 && errno == EINVAL) {
/* may something is wrong here */
status = send(io.file_descriptor, buffer, length, 0);
if(status < 0)
return -0xFEB;
}
return status;
}
void VoiceServer::handleMessageWrite(int fd, short events, void *_event_handle) {
using WBufferPopResult = server::udp::PacketEncoder::BufferPopResult;
auto event_handle = (io::IOEventLoopEntry*) _event_handle;
auto voice_server = event_handle->voice_server;
bool retrigger = false;
IOData<0x100> io{};
io.file_descriptor = fd;
{ /* write and process clients */
shared_ptr<VoiceClient> client;
protocol::OutgoingServerPacket* packet;
WBufferPopResult client_wbuffer_state;
bool more_clients;
auto write_timeout = system_clock::now() + microseconds(2500); /* read 2.5ms long at a time or 'till nothing more is there */
while(system_clock::now() <= write_timeout){
if(!client) {
auto client_queue_state = event_handle->pop_voice_write_queue(client); /* we need a new client, the old client has nothing more to do */
if(client_queue_state == 2)
break;
assert(client);
more_clients = (bool) client_queue_state;
}
while(system_clock::now() <= write_timeout) {
packet = nullptr;
client_wbuffer_state = client->connection->packet_encoder().pop_write_buffer(packet);
if(!packet) {
assert(client_wbuffer_state == WBufferPopResult::DRAINED);
break;
}
ssize_t res = write_datagram(io, client->remote_address, &client->connection->remote_address_info_, packet->packet_length(), packet->packet_data());
if(res != packet->packet_length()) {
if(errno == EAGAIN) {
logCritical(voice_server->server->getServerId(), "Failed to write datagram packet for client {} (EAGAIN).", client->getLoggingPeerIp() + ":" + to_string(client->getPeerPort()));
packet->unref();
return;
} else if(errno == EINVAL || res == -0xFEB) {
/* needs more debug */
auto voice_client = dynamic_pointer_cast<VoiceClient>(client);
logCritical(
voice_server->server->getServerId(),
"Failed to write datagram packet ({} @ {}) for client {} ({}) {}. Dropping packet! Extra data: [fd: {}/{}, supposed socket: {}/{} => {}, client family: {}, socket family: {}]",
packet->packet_length(), packet->packet_data(),
client->getLoggingPeerIp() + ":" + to_string(client->getPeerPort()),
strerror(errno),
res,
fd,
event_handle->file_descriptor,
voice_client->connection->socket_id(),
event_handle->socket_id,
voice_server->io->resolve_file_descriptor(voice_client),
voice_client->isAddressV4() ? "v4" : voice_client->isAddressV6() ? "v6" : "v?",
event_handle->family == AF_INET ? "v4" : "v6"
);
} else {
logCritical(
voice_server->server->getServerId(),
"Failed to write datagram packet for client {} (errno: {} message: {}). Dropping packet!",
client->getLoggingPeerIp() + ":" + to_string(client->getPeerPort()),
errno,
strerror(errno)
);
}
packet->unref();
break;
}
packet->unref();
if(client_wbuffer_state == WBufferPopResult::DRAINED)
break;
}
if(client_wbuffer_state == WBufferPopResult::MORE_AVAILABLE) {
/* we exceeded the max write time, rescheduling write */
voice_server->triggerWrite(client);
}
client.reset();
}
retrigger |= more_clients;
}
/* write all manually specified datagram packets */
{
auto write_timeout = system_clock::now() + microseconds(2500); /* read 2.5ms long at a time or 'till nothing more is there */
udp::DatagramPacket* packet;
while(system_clock::now() <= write_timeout && (packet = event_handle->pop_dg_write_queue())) {
ssize_t res = write_datagram(io, packet->address, &packet->pktinfo, packet->data_length, packet->data);
if(res != packet->data_length) {
if(errno == EAGAIN) {
event_handle->push_dg_write_queue(packet);
} else
udp::DatagramPacket::destroy(packet);
logError(voice_server->server->getServerId(), "Failed to send datagram. Wrote {} out of {}. {}/{}", res, packet->data_length, errno, strerror(errno));
retrigger = false;
break;
}
udp::DatagramPacket::destroy(packet);
}
retrigger |= packet != nullptr; /* memory stored at packet is not accessible anymore. But anyways pop_dg_write_queue returns 0 if there is nothing more */
}
if(retrigger)
event_add(event_handle->event_write, nullptr);
}
void VoiceServer::send_datagram(int socket, udp::DatagramPacket* packet) {
this->io->send_datagram(packet, socket);
}