Initial commit

This commit is contained in:
2023-02-19 21:41:32 +01:00
commit 74d5334e72
1522 changed files with 753891 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
set(common_network_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/address.cpp
${CMAKE_CURRENT_SOURCE_DIR}/connection.cpp
${CMAKE_CURRENT_SOURCE_DIR}/connectionthreads.cpp
${CMAKE_CURRENT_SOURCE_DIR}/networkpacket.cpp
${CMAKE_CURRENT_SOURCE_DIR}/serverpackethandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/serveropcodes.cpp
${CMAKE_CURRENT_SOURCE_DIR}/socket.cpp
PARENT_SCOPE
)
if (BUILD_CLIENT)
set(client_network_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/clientopcodes.cpp
${CMAKE_CURRENT_SOURCE_DIR}/clientpackethandler.cpp
PARENT_SCOPE
)
endif()

259
src/network/address.cpp Normal file
View File

@@ -0,0 +1,259 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "address.h"
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <sstream>
#include <iomanip>
#include "network/networkexceptions.h"
#include "util/string.h"
#include "util/numeric.h"
#include "constants.h"
#include "debug.h"
#include "settings.h"
#include "log.h"
#ifdef _WIN32
// Without this some of the network functions are not found on mingw
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#define LAST_SOCKET_ERR() WSAGetLastError()
typedef SOCKET socket_t;
typedef int socklen_t;
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#define LAST_SOCKET_ERR() (errno)
typedef int socket_t;
#endif
/*
Address
*/
Address::Address()
{
memset(&m_address, 0, sizeof(m_address));
}
Address::Address(u32 address, u16 port)
{
memset(&m_address, 0, sizeof(m_address));
setAddress(address);
setPort(port);
}
Address::Address(u8 a, u8 b, u8 c, u8 d, u16 port)
{
memset(&m_address, 0, sizeof(m_address));
setAddress(a, b, c, d);
setPort(port);
}
Address::Address(const IPv6AddressBytes *ipv6_bytes, u16 port)
{
memset(&m_address, 0, sizeof(m_address));
setAddress(ipv6_bytes);
setPort(port);
}
// Equality (address family, IP and port must be equal)
bool Address::operator==(const Address &other)
{
if (other.m_addr_family != m_addr_family || other.m_port != m_port)
return false;
if (m_addr_family == AF_INET) {
return m_address.ipv4.s_addr == other.m_address.ipv4.s_addr;
}
if (m_addr_family == AF_INET6) {
return memcmp(m_address.ipv6.s6_addr,
other.m_address.ipv6.s6_addr, 16) == 0;
}
return false;
}
void Address::Resolve(const char *name)
{
if (!name || name[0] == 0) {
if (m_addr_family == AF_INET)
setAddress(static_cast<u32>(0));
else if (m_addr_family == AF_INET6)
setAddress(static_cast<IPv6AddressBytes*>(nullptr));
return;
}
struct addrinfo *resolved, hints;
memset(&hints, 0, sizeof(hints));
// Setup hints
if (g_settings->getBool("enable_ipv6")) {
// AF_UNSPEC allows both IPv6 and IPv4 addresses to be returned
hints.ai_family = AF_UNSPEC;
} else {
hints.ai_family = AF_INET;
}
// Do getaddrinfo()
int e = getaddrinfo(name, NULL, &hints, &resolved);
if (e != 0)
throw ResolveError(gai_strerror(e));
// Copy data
if (resolved->ai_family == AF_INET) {
struct sockaddr_in *t = (struct sockaddr_in *)resolved->ai_addr;
m_addr_family = AF_INET;
m_address.ipv4 = t->sin_addr;
} else if (resolved->ai_family == AF_INET6) {
struct sockaddr_in6 *t = (struct sockaddr_in6 *)resolved->ai_addr;
m_addr_family = AF_INET6;
m_address.ipv6 = t->sin6_addr;
} else {
m_addr_family = 0;
}
freeaddrinfo(resolved);
}
// IP address -> textual representation
std::string Address::serializeString() const
{
// windows XP doesnt have inet_ntop, maybe use better func
#ifdef _WIN32
if (m_addr_family == AF_INET) {
return inet_ntoa(m_address.ipv4);
} else if (m_addr_family == AF_INET6) {
std::ostringstream os;
os << std::hex;
for (int i = 0; i < 16; i += 2) {
u16 section = (m_address.ipv6.s6_addr[i] << 8) |
(m_address.ipv6.s6_addr[i + 1]);
os << section;
if (i < 14)
os << ":";
}
return os.str();
} else {
return "";
}
#else
char str[INET6_ADDRSTRLEN];
if (inet_ntop(m_addr_family, (void*) &m_address, str, sizeof(str)) == nullptr)
return "";
return str;
#endif
}
struct in_addr Address::getAddress() const
{
return m_address.ipv4;
}
struct in6_addr Address::getAddress6() const
{
return m_address.ipv6;
}
u16 Address::getPort() const
{
return m_port;
}
bool Address::isZero() const
{
if (m_addr_family == AF_INET) {
return m_address.ipv4.s_addr == 0;
}
if (m_addr_family == AF_INET6) {
static const char zero[16] = {0};
return memcmp(m_address.ipv6.s6_addr, zero, 16) == 0;
}
return false;
}
void Address::setAddress(u32 address)
{
m_addr_family = AF_INET;
m_address.ipv4.s_addr = htonl(address);
}
void Address::setAddress(u8 a, u8 b, u8 c, u8 d)
{
u32 addr = (a << 24) | (b << 16) | (c << 8) | d;
setAddress(addr);
}
void Address::setAddress(const IPv6AddressBytes *ipv6_bytes)
{
m_addr_family = AF_INET6;
if (ipv6_bytes)
memcpy(m_address.ipv6.s6_addr, ipv6_bytes->bytes, 16);
else
memset(m_address.ipv6.s6_addr, 0, 16);
}
void Address::setPort(u16 port)
{
m_port = port;
}
void Address::print(std::ostream& s) const
{
if (m_addr_family == AF_INET6)
s << "[" << serializeString() << "]:" << m_port;
else if (m_addr_family == AF_INET)
s << serializeString() << ":" << m_port;
else
s << "(undefined)";
}
bool Address::isLocalhost() const
{
if (isIPv6()) {
static const u8 localhost_bytes[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
static const u8 mapped_ipv4_localhost[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f, 0, 0, 0};
auto addr = m_address.ipv6.s6_addr;
return memcmp(addr, localhost_bytes, 16) == 0 ||
memcmp(addr, mapped_ipv4_localhost, 13) == 0;
}
auto addr = ntohl(m_address.ipv4.s_addr);
return (addr >> 24) == 0x7f;
}

82
src/network/address.h Normal file
View File

@@ -0,0 +1,82 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#ifdef _WIN32
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <netinet/in.h>
#include <sys/socket.h>
#endif
#include <ostream>
#include <cstring>
#include "irrlichttypes.h"
#include "networkexceptions.h"
struct IPv6AddressBytes
{
u8 bytes[16];
IPv6AddressBytes() { memset(bytes, 0, 16); }
};
class Address
{
public:
Address();
Address(u32 address, u16 port);
Address(u8 a, u8 b, u8 c, u8 d, u16 port);
Address(const IPv6AddressBytes *ipv6_bytes, u16 port);
bool operator==(const Address &address);
bool operator!=(const Address &address) { return !(*this == address); }
struct in_addr getAddress() const;
struct in6_addr getAddress6() const;
u16 getPort() const;
int getFamily() const { return m_addr_family; }
bool isIPv6() const { return m_addr_family == AF_INET6; }
bool isZero() const;
void print(std::ostream &s) const;
std::string serializeString() const;
bool isLocalhost() const;
// Resolve() may throw ResolveError (address is unchanged in this case)
void Resolve(const char *name);
void setAddress(u32 address);
void setAddress(u8 a, u8 b, u8 c, u8 d);
void setAddress(const IPv6AddressBytes *ipv6_bytes);
void setPort(u16 port);
private:
unsigned short m_addr_family = 0;
union
{
struct in_addr ipv4;
struct in6_addr ipv6;
} m_address;
u16 m_port = 0; // Port is separate from sockaddr structures
};

View File

@@ -0,0 +1,226 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "clientopcodes.h"
const static ToClientCommandHandler null_command_handler = {"TOCLIENT_NULL", TOCLIENT_STATE_ALL, &Client::handleCommand_Null};
const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] =
{
null_command_handler, // 0x00 (never use this)
null_command_handler, // 0x01
{ "TOCLIENT_HELLO", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_Hello }, // 0x02
{ "TOCLIENT_AUTH_ACCEPT", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AuthAccept }, // 0x03
{ "TOCLIENT_ACCEPT_SUDO_MODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AcceptSudoMode}, // 0x04
{ "TOCLIENT_DENY_SUDO_MODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DenySudoMode}, // 0x05
null_command_handler, // 0x06
null_command_handler, // 0x07
null_command_handler, // 0x08
null_command_handler, // 0x09
{ "TOCLIENT_ACCESS_DENIED", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AccessDenied }, // 0x0A
null_command_handler, // 0x0B
null_command_handler, // 0x0C
null_command_handler, // 0x0D
null_command_handler, // 0x0E
null_command_handler, // 0x0F
null_command_handler, // 0x10
null_command_handler,
null_command_handler,
null_command_handler,
null_command_handler,
null_command_handler,
null_command_handler,
null_command_handler,
null_command_handler,
null_command_handler,
null_command_handler,
null_command_handler,
null_command_handler,
null_command_handler,
null_command_handler,
null_command_handler,
{ "TOCLIENT_BLOCKDATA", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_BlockData }, // 0x20
{ "TOCLIENT_ADDNODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AddNode }, // 0x21
{ "TOCLIENT_REMOVENODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_RemoveNode }, // 0x22
null_command_handler,
null_command_handler,
null_command_handler,
null_command_handler,
{ "TOCLIENT_INVENTORY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Inventory }, // 0x27
null_command_handler,
{ "TOCLIENT_TIME_OF_DAY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_TimeOfDay }, // 0x29
{ "TOCLIENT_CSM_RESTRICTION_FLAGS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_CSMRestrictionFlags }, // 0x2A
{ "TOCLIENT_PLAYER_SPEED", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_PlayerSpeed }, // 0x2B
{ "TOCLIENT_MEDIA_PUSH", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MediaPush }, // 0x2C
null_command_handler,
null_command_handler,
{ "TOCLIENT_CHAT_MESSAGE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ChatMessage }, // 0x2F
null_command_handler, // 0x30
{ "TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ActiveObjectRemoveAdd }, // 0x31
{ "TOCLIENT_ACTIVE_OBJECT_MESSAGES", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ActiveObjectMessages }, // 0x32
{ "TOCLIENT_HP", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HP }, // 0x33
{ "TOCLIENT_MOVE_PLAYER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MovePlayer }, // 0x34
{ "TOCLIENT_ACCESS_DENIED_LEGACY", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AccessDenied }, // 0x35
{ "TOCLIENT_FOV", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Fov }, // 0x36
{ "TOCLIENT_DEATHSCREEN", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeathScreen }, // 0x37
{ "TOCLIENT_MEDIA", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Media }, // 0x38
null_command_handler,
{ "TOCLIENT_NODEDEF", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_NodeDef }, // 0x3a
null_command_handler,
{ "TOCLIENT_ANNOUNCE_MEDIA", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AnnounceMedia }, // 0x3c
{ "TOCLIENT_ITEMDEF", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ItemDef }, // 0x3d
null_command_handler,
{ "TOCLIENT_PLAY_SOUND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_PlaySound }, // 0x3f
{ "TOCLIENT_STOP_SOUND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_StopSound }, // 0x40
{ "TOCLIENT_PRIVILEGES", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Privileges }, // 0x41
{ "TOCLIENT_INVENTORY_FORMSPEC", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_InventoryFormSpec }, // 0x42
{ "TOCLIENT_DETACHED_INVENTORY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DetachedInventory }, // 0x43
{ "TOCLIENT_SHOW_FORMSPEC", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ShowFormSpec }, // 0x44
{ "TOCLIENT_MOVEMENT", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Movement }, // 0x45
{ "TOCLIENT_SPAWN_PARTICLE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_SpawnParticle }, // 0x46
{ "TOCLIENT_ADD_PARTICLESPAWNER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AddParticleSpawner }, // 0x47
null_command_handler,
{ "TOCLIENT_HUDADD", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudAdd }, // 0x49
{ "TOCLIENT_HUDRM", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudRemove }, // 0x4a
{ "TOCLIENT_HUDCHANGE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudChange }, // 0x4b
{ "TOCLIENT_HUD_SET_FLAGS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetFlags }, // 0x4c
{ "TOCLIENT_HUD_SET_PARAM", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetParam }, // 0x4d
{ "TOCLIENT_BREATH", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Breath }, // 0x4e
{ "TOCLIENT_SET_SKY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetSky }, // 0x4f
{ "TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_OverrideDayNightRatio }, // 0x50
{ "TOCLIENT_LOCAL_PLAYER_ANIMATIONS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_LocalPlayerAnimations }, // 0x51
{ "TOCLIENT_EYE_OFFSET", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_EyeOffset }, // 0x52
{ "TOCLIENT_DELETE_PARTICLESPAWNER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeleteParticleSpawner }, // 0x53
{ "TOCLIENT_CLOUD_PARAMS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_CloudParams }, // 0x54
{ "TOCLIENT_FADE_SOUND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_FadeSound }, // 0x55
{ "TOCLIENT_UPDATE_PLAYER_LIST", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_UpdatePlayerList }, // 0x56
{ "TOCLIENT_MODCHANNEL_MSG", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ModChannelMsg }, // 0x57
{ "TOCLIENT_MODCHANNEL_SIGNAL", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ModChannelSignal }, // 0x58
{ "TOCLIENT_NODEMETA_CHANGED", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_NodemetaChanged }, // 0x59
{ "TOCLIENT_SET_SUN", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetSun }, // 0x5a
{ "TOCLIENT_SET_MOON", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetMoon }, // 0x5b
{ "TOCLIENT_SET_STARS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetStars }, // 0x5c
null_command_handler,
null_command_handler,
null_command_handler,
{ "TOCLIENT_SRP_BYTES_S_B", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_SrpBytesSandB }, // 0x60
{ "TOCLIENT_FORMSPEC_PREPEND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_FormspecPrepend }, // 0x61,
{ "TOCLIENT_MINIMAP_MODES", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MinimapModes }, // 0x62,
{ "TOCLIENT_SET_LIGHTING", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_SetLighting }, // 0x63,
};
const static ServerCommandFactory null_command_factory = { "TOSERVER_NULL", 0, false };
/*
Channels used for Client -> Server communication
2: Notifications back to the server (e.g. GOTBLOCKS)
1: Init and Authentication
0: everything else
Packet order is only guaranteed inside a channel, so packets that operate on
the same objects are *required* to be in the same channel.
*/
const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] =
{
null_command_factory, // 0x00
null_command_factory, // 0x01
{ "TOSERVER_INIT", 1, false }, // 0x02
null_command_factory, // 0x03
null_command_factory, // 0x04
null_command_factory, // 0x05
null_command_factory, // 0x06
null_command_factory, // 0x07
null_command_factory, // 0x08
null_command_factory, // 0x09
null_command_factory, // 0x0a
null_command_factory, // 0x0b
null_command_factory, // 0x0c
null_command_factory, // 0x0d
null_command_factory, // 0x0e
null_command_factory, // 0x0f
null_command_factory, // 0x10
{ "TOSERVER_INIT2", 1, true }, // 0x11
null_command_factory, // 0x12
null_command_factory, // 0x13
null_command_factory, // 0x14
null_command_factory, // 0x15
null_command_factory, // 0x16
{ "TOSERVER_MODCHANNEL_JOIN", 0, true }, // 0x17
{ "TOSERVER_MODCHANNEL_LEAVE", 0, true }, // 0x18
{ "TOSERVER_MODCHANNEL_MSG", 0, true }, // 0x19
null_command_factory, // 0x1a
null_command_factory, // 0x1b
null_command_factory, // 0x1c
null_command_factory, // 0x1d
null_command_factory, // 0x1e
null_command_factory, // 0x1f
null_command_factory, // 0x20
null_command_factory, // 0x21
null_command_factory, // 0x22
{ "TOSERVER_PLAYERPOS", 0, false }, // 0x23
{ "TOSERVER_GOTBLOCKS", 2, true }, // 0x24
{ "TOSERVER_DELETEDBLOCKS", 2, true }, // 0x25
null_command_factory, // 0x26
null_command_factory, // 0x27
null_command_factory, // 0x28
null_command_factory, // 0x29
null_command_factory, // 0x2a
null_command_factory, // 0x2b
null_command_factory, // 0x2c
null_command_factory, // 0x2d
null_command_factory, // 0x2e
null_command_factory, // 0x2f
null_command_factory, // 0x30
{ "TOSERVER_INVENTORY_ACTION", 0, true }, // 0x31
{ "TOSERVER_CHAT_MESSAGE", 0, true }, // 0x32
null_command_factory, // 0x33
null_command_factory, // 0x34
{ "TOSERVER_DAMAGE", 0, true }, // 0x35
null_command_factory, // 0x36
{ "TOSERVER_PLAYERITEM", 0, true }, // 0x37
{ "TOSERVER_RESPAWN", 0, true }, // 0x38
{ "TOSERVER_INTERACT", 0, true }, // 0x39
{ "TOSERVER_REMOVED_SOUNDS", 2, true }, // 0x3a
{ "TOSERVER_NODEMETA_FIELDS", 0, true }, // 0x3b
{ "TOSERVER_INVENTORY_FIELDS", 0, true }, // 0x3c
null_command_factory, // 0x3d
null_command_factory, // 0x3e
null_command_factory, // 0x3f
{ "TOSERVER_REQUEST_MEDIA", 1, true }, // 0x40
{ "TOSERVER_HAVE_MEDIA", 2, true }, // 0x41
null_command_factory, // 0x42
{ "TOSERVER_CLIENT_READY", 1, true }, // 0x43
null_command_factory, // 0x44
null_command_factory, // 0x45
null_command_factory, // 0x46
null_command_factory, // 0x47
null_command_factory, // 0x48
null_command_factory, // 0x49
null_command_factory, // 0x4a
null_command_factory, // 0x4b
null_command_factory, // 0x4c
null_command_factory, // 0x4d
null_command_factory, // 0x4e
null_command_factory, // 0x4f
{ "TOSERVER_FIRST_SRP", 1, true }, // 0x50
{ "TOSERVER_SRP_BYTES_A", 1, true }, // 0x51
{ "TOSERVER_SRP_BYTES_M", 1, true }, // 0x52
};

View File

@@ -0,0 +1,50 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "client/client.h"
#include "networkprotocol.h"
class NetworkPacket;
enum ToClientConnectionState {
TOCLIENT_STATE_NOT_CONNECTED,
TOCLIENT_STATE_CONNECTED,
TOCLIENT_STATE_ALL,
};
struct ToClientCommandHandler
{
const char* name;
ToClientConnectionState state;
void (Client::*handler)(NetworkPacket* pkt);
};
struct ServerCommandFactory
{
const char* name;
u8 channel;
bool reliable;
};
extern const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES];
extern const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES];

File diff suppressed because it is too large Load Diff

1647
src/network/connection.cpp Normal file

File diff suppressed because it is too large Load Diff

785
src/network/connection.h Normal file
View File

@@ -0,0 +1,785 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irrlichttypes.h"
#include "peerhandler.h"
#include "socket.h"
#include "constants.h"
#include "util/pointer.h"
#include "util/container.h"
#include "util/thread.h"
#include "util/numeric.h"
#include "networkprotocol.h"
#include <iostream>
#include <vector>
#include <map>
#define MAX_UDP_PEERS 65535
/*
=== NOTES ===
A packet is sent through a channel to a peer with a basic header:
Header (7 bytes):
[0] u32 protocol_id
[4] session_t sender_peer_id
[6] u8 channel
sender_peer_id:
Unique to each peer.
value 0 (PEER_ID_INEXISTENT) is reserved for making new connections
value 1 (PEER_ID_SERVER) is reserved for server
these constants are defined in constants.h
channel:
Channel numbers have no intrinsic meaning. Currently only 0, 1, 2 exist.
*/
#define BASE_HEADER_SIZE 7
#define CHANNEL_COUNT 3
/*
Packet types:
CONTROL: This is a packet used by the protocol.
- When this is processed, nothing is handed to the user.
Header (2 byte):
[0] u8 type
[1] u8 controltype
controltype and data description:
CONTROLTYPE_ACK
[2] u16 seqnum
CONTROLTYPE_SET_PEER_ID
[2] session_t peer_id_new
CONTROLTYPE_PING
- There is no actual reply, but this can be sent in a reliable
packet to get a reply
CONTROLTYPE_DISCO
*/
enum ControlType : u8 {
CONTROLTYPE_ACK = 0,
CONTROLTYPE_SET_PEER_ID = 1,
CONTROLTYPE_PING = 2,
CONTROLTYPE_DISCO = 3,
};
/*
ORIGINAL: This is a plain packet with no control and no error
checking at all.
- When this is processed, it is directly handed to the user.
Header (1 byte):
[0] u8 type
*/
//#define TYPE_ORIGINAL 1
#define ORIGINAL_HEADER_SIZE 1
/*
SPLIT: These are sequences of packets forming one bigger piece of
data.
- When processed and all the packet_nums 0...packet_count-1 are
present (this should be buffered), the resulting data shall be
directly handed to the user.
- If the data fails to come up in a reasonable time, the buffer shall
be silently discarded.
- These can be sent as-is or atop of a RELIABLE packet stream.
Header (7 bytes):
[0] u8 type
[1] u16 seqnum
[3] u16 chunk_count
[5] u16 chunk_num
*/
//#define TYPE_SPLIT 2
/*
RELIABLE: Delivery of all RELIABLE packets shall be forced by ACKs,
and they shall be delivered in the same order as sent. This is done
with a buffer in the receiving and transmitting end.
- When this is processed, the contents of each packet is recursively
processed as packets.
Header (3 bytes):
[0] u8 type
[1] u16 seqnum
*/
//#define TYPE_RELIABLE 3
#define RELIABLE_HEADER_SIZE 3
#define SEQNUM_INITIAL 65500
#define SEQNUM_MAX 65535
class NetworkPacket;
namespace con
{
class ConnectionReceiveThread;
class ConnectionSendThread;
typedef enum MTProtocols {
MTP_PRIMARY,
MTP_UDP,
MTP_MINETEST_RELIABLE_UDP
} MTProtocols;
enum PacketType : u8 {
PACKET_TYPE_CONTROL = 0,
PACKET_TYPE_ORIGINAL = 1,
PACKET_TYPE_SPLIT = 2,
PACKET_TYPE_RELIABLE = 3,
PACKET_TYPE_MAX
};
inline bool seqnum_higher(u16 totest, u16 base)
{
if (totest > base)
{
if ((totest - base) > (SEQNUM_MAX/2))
return false;
return true;
}
if ((base - totest) > (SEQNUM_MAX/2))
return true;
return false;
}
inline bool seqnum_in_window(u16 seqnum, u16 next,u16 window_size)
{
u16 window_start = next;
u16 window_end = ( next + window_size ) % (SEQNUM_MAX+1);
if (window_start < window_end) {
return ((seqnum >= window_start) && (seqnum < window_end));
}
return ((seqnum < window_end) || (seqnum >= window_start));
}
static inline float CALC_DTIME(u64 lasttime, u64 curtime)
{
float value = ( curtime - lasttime) / 1000.0;
return MYMAX(MYMIN(value,0.1),0.0);
}
/*
Struct for all kinds of packets. Includes following data:
BASE_HEADER
u8[] packet data (usually copied from SharedBuffer<u8>)
*/
struct BufferedPacket {
BufferedPacket(u32 a_size)
{
m_data.resize(a_size);
data = &m_data[0];
}
DISABLE_CLASS_COPY(BufferedPacket)
u16 getSeqnum() const;
inline size_t size() const { return m_data.size(); }
u8 *data; // Direct memory access
float time = 0.0f; // Seconds from buffering the packet or re-sending
float totaltime = 0.0f; // Seconds from buffering the packet
u64 absolute_send_time = -1;
Address address; // Sender or destination
unsigned int resend_count = 0;
private:
std::vector<u8> m_data; // Data of the packet, including headers
};
typedef std::shared_ptr<BufferedPacket> BufferedPacketPtr;
// This adds the base headers to the data and makes a packet out of it
BufferedPacketPtr makePacket(Address &address, const SharedBuffer<u8> &data,
u32 protocol_id, session_t sender_peer_id, u8 channel);
// Depending on size, make a TYPE_ORIGINAL or TYPE_SPLIT packet
// Increments split_seqnum if a split packet is made
void makeAutoSplitPacket(const SharedBuffer<u8> &data, u32 chunksize_max,
u16 &split_seqnum, std::list<SharedBuffer<u8>> *list);
// Add the TYPE_RELIABLE header to the data
SharedBuffer<u8> makeReliablePacket(const SharedBuffer<u8> &data, u16 seqnum);
struct IncomingSplitPacket
{
IncomingSplitPacket(u32 cc, bool r):
chunk_count(cc), reliable(r) {}
IncomingSplitPacket() = delete;
float time = 0.0f; // Seconds from adding
u32 chunk_count;
bool reliable; // If true, isn't deleted on timeout
bool allReceived() const
{
return (chunks.size() == chunk_count);
}
bool insert(u32 chunk_num, SharedBuffer<u8> &chunkdata);
SharedBuffer<u8> reassemble();
private:
// Key is chunk number, value is data without headers
std::map<u16, SharedBuffer<u8>> chunks;
};
/*
A buffer which stores reliable packets and sorts them internally
for fast access to the smallest one.
*/
typedef std::list<BufferedPacketPtr>::iterator RPBSearchResult;
class ReliablePacketBuffer
{
public:
ReliablePacketBuffer() = default;
bool getFirstSeqnum(u16& result);
BufferedPacketPtr popFirst();
BufferedPacketPtr popSeqnum(u16 seqnum);
void insert(BufferedPacketPtr &p_ptr, u16 next_expected);
void incrementTimeouts(float dtime);
std::list<ConstSharedPtr<BufferedPacket>> getTimedOuts(float timeout, u32 max_packets);
void print();
bool empty();
u32 size();
private:
RPBSearchResult findPacketNoLock(u16 seqnum);
std::list<BufferedPacketPtr> m_list;
u16 m_oldest_non_answered_ack;
std::mutex m_list_mutex;
};
/*
A buffer for reconstructing split packets
*/
class IncomingSplitBuffer
{
public:
~IncomingSplitBuffer();
/*
Returns a reference counted buffer of length != 0 when a full split
packet is constructed. If not, returns one of length 0.
*/
SharedBuffer<u8> insert(BufferedPacketPtr &p_ptr, bool reliable);
void removeUnreliableTimedOuts(float dtime, float timeout);
private:
// Key is seqnum
std::map<u16, IncomingSplitPacket*> m_buf;
std::mutex m_map_mutex;
};
enum ConnectionCommandType{
CONNCMD_NONE,
CONNCMD_SERVE,
CONNCMD_CONNECT,
CONNCMD_DISCONNECT,
CONNCMD_DISCONNECT_PEER,
CONNCMD_SEND,
CONNCMD_SEND_TO_ALL,
CONCMD_ACK,
CONCMD_CREATE_PEER
};
struct ConnectionCommand;
typedef std::shared_ptr<ConnectionCommand> ConnectionCommandPtr;
// This is very similar to ConnectionEvent
struct ConnectionCommand
{
const ConnectionCommandType type;
Address address;
session_t peer_id = PEER_ID_INEXISTENT;
u8 channelnum = 0;
Buffer<u8> data;
bool reliable = false;
bool raw = false;
DISABLE_CLASS_COPY(ConnectionCommand);
static ConnectionCommandPtr serve(Address address);
static ConnectionCommandPtr connect(Address address);
static ConnectionCommandPtr disconnect();
static ConnectionCommandPtr disconnect_peer(session_t peer_id);
static ConnectionCommandPtr send(session_t peer_id, u8 channelnum, NetworkPacket *pkt, bool reliable);
static ConnectionCommandPtr ack(session_t peer_id, u8 channelnum, const Buffer<u8> &data);
static ConnectionCommandPtr createPeer(session_t peer_id, const Buffer<u8> &data);
private:
ConnectionCommand(ConnectionCommandType type_) :
type(type_) {}
static ConnectionCommandPtr create(ConnectionCommandType type);
};
/* maximum window size to use, 0xFFFF is theoretical maximum. don't think about
* touching it, the less you're away from it the more likely data corruption
* will occur
*/
#define MAX_RELIABLE_WINDOW_SIZE 0x8000
/* starting value for window size */
#define START_RELIABLE_WINDOW_SIZE 0x400
/* minimum value for window size */
#define MIN_RELIABLE_WINDOW_SIZE 0x40
class Channel
{
public:
u16 readNextIncomingSeqNum();
u16 incNextIncomingSeqNum();
u16 getOutgoingSequenceNumber(bool& successfull);
u16 readOutgoingSequenceNumber();
bool putBackSequenceNumber(u16);
u16 readNextSplitSeqNum();
void setNextSplitSeqNum(u16 seqnum);
// This is for buffering the incoming packets that are coming in
// the wrong order
ReliablePacketBuffer incoming_reliables;
// This is for buffering the sent packets so that the sender can
// re-send them if no ACK is received
ReliablePacketBuffer outgoing_reliables_sent;
//queued reliable packets
std::queue<BufferedPacketPtr> queued_reliables;
//queue commands prior splitting to packets
std::deque<ConnectionCommandPtr> queued_commands;
IncomingSplitBuffer incoming_splits;
Channel() = default;
~Channel() = default;
void UpdatePacketLossCounter(unsigned int count);
void UpdatePacketTooLateCounter();
void UpdateBytesSent(unsigned int bytes,unsigned int packages=1);
void UpdateBytesLost(unsigned int bytes);
void UpdateBytesReceived(unsigned int bytes);
void UpdateTimers(float dtime);
float getCurrentDownloadRateKB()
{ MutexAutoLock lock(m_internal_mutex); return cur_kbps; };
float getMaxDownloadRateKB()
{ MutexAutoLock lock(m_internal_mutex); return max_kbps; };
float getCurrentLossRateKB()
{ MutexAutoLock lock(m_internal_mutex); return cur_kbps_lost; };
float getMaxLossRateKB()
{ MutexAutoLock lock(m_internal_mutex); return max_kbps_lost; };
float getCurrentIncomingRateKB()
{ MutexAutoLock lock(m_internal_mutex); return cur_incoming_kbps; };
float getMaxIncomingRateKB()
{ MutexAutoLock lock(m_internal_mutex); return max_incoming_kbps; };
float getAvgDownloadRateKB()
{ MutexAutoLock lock(m_internal_mutex); return avg_kbps; };
float getAvgLossRateKB()
{ MutexAutoLock lock(m_internal_mutex); return avg_kbps_lost; };
float getAvgIncomingRateKB()
{ MutexAutoLock lock(m_internal_mutex); return avg_incoming_kbps; };
u16 getWindowSize() const { return m_window_size; };
void setWindowSize(long size)
{
m_window_size = (u16)rangelim(size, MIN_RELIABLE_WINDOW_SIZE, MAX_RELIABLE_WINDOW_SIZE);
}
private:
std::mutex m_internal_mutex;
u16 m_window_size = MIN_RELIABLE_WINDOW_SIZE;
u16 next_incoming_seqnum = SEQNUM_INITIAL;
u16 next_outgoing_seqnum = SEQNUM_INITIAL;
u16 next_outgoing_split_seqnum = SEQNUM_INITIAL;
unsigned int current_packet_loss = 0;
unsigned int current_packet_too_late = 0;
unsigned int current_packet_successful = 0;
float packet_loss_counter = 0.0f;
unsigned int current_bytes_transfered = 0;
unsigned int current_bytes_received = 0;
unsigned int current_bytes_lost = 0;
float max_kbps = 0.0f;
float cur_kbps = 0.0f;
float avg_kbps = 0.0f;
float max_incoming_kbps = 0.0f;
float cur_incoming_kbps = 0.0f;
float avg_incoming_kbps = 0.0f;
float max_kbps_lost = 0.0f;
float cur_kbps_lost = 0.0f;
float avg_kbps_lost = 0.0f;
float bpm_counter = 0.0f;
unsigned int rate_samples = 0;
};
class Peer;
class PeerHelper
{
public:
PeerHelper() = default;
PeerHelper(Peer* peer);
~PeerHelper();
PeerHelper& operator=(Peer* peer);
Peer* operator->() const;
bool operator!();
Peer* operator&() const;
bool operator!=(void* ptr);
private:
Peer *m_peer = nullptr;
};
class Connection;
typedef enum {
CUR_DL_RATE,
AVG_DL_RATE,
CUR_INC_RATE,
AVG_INC_RATE,
CUR_LOSS_RATE,
AVG_LOSS_RATE,
} rate_stat_type;
class Peer {
public:
friend class PeerHelper;
Peer(Address address_,session_t id_,Connection* connection) :
id(id_),
m_connection(connection),
address(address_),
m_last_timeout_check(porting::getTimeMs())
{
};
virtual ~Peer() {
MutexAutoLock usage_lock(m_exclusive_access_mutex);
FATAL_ERROR_IF(m_usage != 0, "Reference counting failure");
};
// Unique id of the peer
const session_t id;
void Drop();
virtual void PutReliableSendCommand(ConnectionCommandPtr &c,
unsigned int max_packet_size) {};
virtual bool getAddress(MTProtocols type, Address& toset) = 0;
bool isPendingDeletion()
{ MutexAutoLock lock(m_exclusive_access_mutex); return m_pending_deletion; };
void ResetTimeout()
{MutexAutoLock lock(m_exclusive_access_mutex); m_timeout_counter = 0.0; };
bool isTimedOut(float timeout);
unsigned int m_increment_packets_remaining = 0;
virtual u16 getNextSplitSequenceNumber(u8 channel) { return 0; };
virtual void setNextSplitSequenceNumber(u8 channel, u16 seqnum) {};
virtual SharedBuffer<u8> addSplitPacket(u8 channel, BufferedPacketPtr &toadd,
bool reliable)
{
errorstream << "Peer::addSplitPacket called,"
<< " this is supposed to be never called!" << std::endl;
return SharedBuffer<u8>(0);
};
virtual bool Ping(float dtime, SharedBuffer<u8>& data) { return false; };
virtual float getStat(rtt_stat_type type) const {
switch (type) {
case MIN_RTT:
return m_rtt.min_rtt;
case MAX_RTT:
return m_rtt.max_rtt;
case AVG_RTT:
return m_rtt.avg_rtt;
case MIN_JITTER:
return m_rtt.jitter_min;
case MAX_JITTER:
return m_rtt.jitter_max;
case AVG_JITTER:
return m_rtt.jitter_avg;
}
return -1;
}
protected:
virtual void reportRTT(float rtt) {};
void RTTStatistics(float rtt,
const std::string &profiler_id = "",
unsigned int num_samples = 1000);
bool IncUseCount();
void DecUseCount();
mutable std::mutex m_exclusive_access_mutex;
bool m_pending_deletion = false;
Connection* m_connection;
// Address of the peer
Address address;
// Ping timer
float m_ping_timer = 0.0f;
private:
struct rttstats {
float jitter_min = FLT_MAX;
float jitter_max = 0.0f;
float jitter_avg = -1.0f;
float min_rtt = FLT_MAX;
float max_rtt = 0.0f;
float avg_rtt = -1.0f;
rttstats() = default;
};
rttstats m_rtt;
float m_last_rtt = -1.0f;
// current usage count
unsigned int m_usage = 0;
// Seconds from last receive
float m_timeout_counter = 0.0f;
u64 m_last_timeout_check;
};
class UDPPeer : public Peer
{
public:
friend class PeerHelper;
friend class ConnectionReceiveThread;
friend class ConnectionSendThread;
friend class Connection;
UDPPeer(u16 a_id, Address a_address, Connection* connection);
virtual ~UDPPeer() = default;
void PutReliableSendCommand(ConnectionCommandPtr &c,
unsigned int max_packet_size);
bool getAddress(MTProtocols type, Address& toset);
u16 getNextSplitSequenceNumber(u8 channel);
void setNextSplitSequenceNumber(u8 channel, u16 seqnum);
SharedBuffer<u8> addSplitPacket(u8 channel, BufferedPacketPtr &toadd,
bool reliable);
protected:
/*
Calculates avg_rtt and resend_timeout.
rtt=-1 only recalculates resend_timeout
*/
void reportRTT(float rtt);
void RunCommandQueues(
unsigned int max_packet_size,
unsigned int maxcommands,
unsigned int maxtransfer);
float getResendTimeout()
{ MutexAutoLock lock(m_exclusive_access_mutex); return resend_timeout; }
void setResendTimeout(float timeout)
{ MutexAutoLock lock(m_exclusive_access_mutex); resend_timeout = timeout; }
bool Ping(float dtime,SharedBuffer<u8>& data);
Channel channels[CHANNEL_COUNT];
bool m_pending_disconnect = false;
private:
// This is changed dynamically
float resend_timeout = 0.5;
bool processReliableSendCommand(
ConnectionCommandPtr &c_ptr,
unsigned int max_packet_size);
};
/*
Connection
*/
enum ConnectionEventType {
CONNEVENT_NONE,
CONNEVENT_DATA_RECEIVED,
CONNEVENT_PEER_ADDED,
CONNEVENT_PEER_REMOVED,
CONNEVENT_BIND_FAILED,
};
struct ConnectionEvent;
typedef std::shared_ptr<ConnectionEvent> ConnectionEventPtr;
// This is very similar to ConnectionCommand
struct ConnectionEvent
{
const ConnectionEventType type;
session_t peer_id = 0;
Buffer<u8> data;
bool timeout = false;
Address address;
// We don't want to copy "data"
DISABLE_CLASS_COPY(ConnectionEvent);
static ConnectionEventPtr create(ConnectionEventType type);
static ConnectionEventPtr dataReceived(session_t peer_id, const Buffer<u8> &data);
static ConnectionEventPtr peerAdded(session_t peer_id, Address address);
static ConnectionEventPtr peerRemoved(session_t peer_id, bool is_timeout, Address address);
static ConnectionEventPtr bindFailed();
const char *describe() const;
private:
ConnectionEvent(ConnectionEventType type_) :
type(type_) {}
};
class PeerHandler;
class Connection
{
public:
friend class ConnectionSendThread;
friend class ConnectionReceiveThread;
Connection(u32 protocol_id, u32 max_packet_size, float timeout, bool ipv6,
PeerHandler *peerhandler);
~Connection();
/* Interface */
ConnectionEventPtr waitEvent(u32 timeout_ms);
void putCommand(ConnectionCommandPtr c);
void SetTimeoutMs(u32 timeout) { m_bc_receive_timeout = timeout; }
void Serve(Address bind_addr);
void Connect(Address address);
bool Connected();
void Disconnect();
void Receive(NetworkPacket* pkt);
bool TryReceive(NetworkPacket *pkt);
void Send(session_t peer_id, u8 channelnum, NetworkPacket *pkt, bool reliable);
session_t GetPeerID() const { return m_peer_id; }
Address GetPeerAddress(session_t peer_id);
float getPeerStat(session_t peer_id, rtt_stat_type type);
float getLocalStat(rate_stat_type type);
u32 GetProtocolID() const { return m_protocol_id; };
const std::string getDesc();
void DisconnectPeer(session_t peer_id);
protected:
PeerHelper getPeerNoEx(session_t peer_id);
u16 lookupPeer(Address& sender);
u16 createPeer(Address& sender, MTProtocols protocol, int fd);
UDPPeer* createServerPeer(Address& sender);
bool deletePeer(session_t peer_id, bool timeout);
void SetPeerID(session_t id) { m_peer_id = id; }
void sendAck(session_t peer_id, u8 channelnum, u16 seqnum);
std::vector<session_t> getPeerIDs()
{
MutexAutoLock peerlock(m_peers_mutex);
return m_peer_ids;
}
UDPSocket m_udpSocket;
// Command queue: user -> SendThread
MutexedQueue<ConnectionCommandPtr> m_command_queue;
bool Receive(NetworkPacket *pkt, u32 timeout);
void putEvent(ConnectionEventPtr e);
void TriggerSend();
bool ConnectedToServer()
{
return getPeerNoEx(PEER_ID_SERVER) != nullptr;
}
private:
// Event queue: ReceiveThread -> user
MutexedQueue<ConnectionEventPtr> m_event_queue;
session_t m_peer_id = 0;
u32 m_protocol_id;
std::map<session_t, Peer *> m_peers;
std::vector<session_t> m_peer_ids;
std::mutex m_peers_mutex;
std::unique_ptr<ConnectionSendThread> m_sendThread;
std::unique_ptr<ConnectionReceiveThread> m_receiveThread;
mutable std::mutex m_info_mutex;
// Backwards compatibility
PeerHandler *m_bc_peerhandler;
u32 m_bc_receive_timeout = 0;
bool m_shutting_down = false;
session_t m_next_remote_peer_id = 2;
};
} // namespace

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,169 @@
/*
Minetest
Copyright (C) 2013-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2017 celeron55, Loic Blot <loic.blot@unix-experience.fr>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <cassert>
#include "threading/thread.h"
#include "connection.h"
namespace con
{
class Connection;
struct OutgoingPacket
{
session_t peer_id;
u8 channelnum;
SharedBuffer<u8> data;
bool reliable;
bool ack;
OutgoingPacket(session_t peer_id_, u8 channelnum_, const SharedBuffer<u8> &data_,
bool reliable_,bool ack_=false):
peer_id(peer_id_),
channelnum(channelnum_),
data(data_),
reliable(reliable_),
ack(ack_)
{
}
};
class ConnectionSendThread : public Thread
{
public:
friend class UDPPeer;
ConnectionSendThread(unsigned int max_packet_size, float timeout);
void *run();
void Trigger();
void setParent(Connection *parent)
{
assert(parent != NULL); // Pre-condition
m_connection = parent;
}
void setPeerTimeout(float peer_timeout) { m_timeout = peer_timeout; }
private:
void runTimeouts(float dtime);
void rawSend(const BufferedPacket *p);
bool rawSendAsPacket(session_t peer_id, u8 channelnum,
const SharedBuffer<u8> &data, bool reliable);
void processReliableCommand(ConnectionCommandPtr &c);
void processNonReliableCommand(ConnectionCommandPtr &c);
void serve(Address bind_address);
void connect(Address address);
void disconnect();
void disconnect_peer(session_t peer_id);
void send(session_t peer_id, u8 channelnum, const SharedBuffer<u8> &data);
void sendReliable(ConnectionCommandPtr &c);
void sendToAll(u8 channelnum, const SharedBuffer<u8> &data);
void sendToAllReliable(ConnectionCommandPtr &c);
void sendPackets(float dtime);
void sendAsPacket(session_t peer_id, u8 channelnum, const SharedBuffer<u8> &data,
bool ack = false);
void sendAsPacketReliable(BufferedPacketPtr &p, Channel *channel);
bool packetsQueued();
Connection *m_connection = nullptr;
unsigned int m_max_packet_size;
float m_timeout;
std::queue<OutgoingPacket> m_outgoing_queue;
Semaphore m_send_sleep_semaphore;
unsigned int m_iteration_packets_avaialble;
unsigned int m_max_commands_per_iteration = 1;
unsigned int m_max_data_packets_per_iteration;
unsigned int m_max_packets_requeued = 256;
};
class ConnectionReceiveThread : public Thread
{
public:
ConnectionReceiveThread(unsigned int max_packet_size);
void *run();
void setParent(Connection *parent)
{
assert(parent); // Pre-condition
m_connection = parent;
}
private:
void receive(SharedBuffer<u8> &packetdata, bool &packet_queued);
// Returns next data from a buffer if possible
// If found, returns true; if not, false.
// If found, sets peer_id and dst
bool getFromBuffers(session_t &peer_id, SharedBuffer<u8> &dst);
bool checkIncomingBuffers(
Channel *channel, session_t &peer_id, SharedBuffer<u8> &dst);
/*
Processes a packet with the basic header stripped out.
Parameters:
packetdata: Data in packet (with no base headers)
peer_id: peer id of the sender of the packet in question
channelnum: channel on which the packet was sent
reliable: true if recursing into a reliable packet
*/
SharedBuffer<u8> processPacket(Channel *channel,
const SharedBuffer<u8> &packetdata, session_t peer_id,
u8 channelnum, bool reliable);
SharedBuffer<u8> handlePacketType_Control(Channel *channel,
const SharedBuffer<u8> &packetdata, Peer *peer, u8 channelnum,
bool reliable);
SharedBuffer<u8> handlePacketType_Original(Channel *channel,
const SharedBuffer<u8> &packetdata, Peer *peer, u8 channelnum,
bool reliable);
SharedBuffer<u8> handlePacketType_Split(Channel *channel,
const SharedBuffer<u8> &packetdata, Peer *peer, u8 channelnum,
bool reliable);
SharedBuffer<u8> handlePacketType_Reliable(Channel *channel,
const SharedBuffer<u8> &packetdata, Peer *peer, u8 channelnum,
bool reliable);
struct PacketTypeHandler
{
SharedBuffer<u8> (ConnectionReceiveThread::*handler)(Channel *channel,
const SharedBuffer<u8> &packet, Peer *peer, u8 channelnum,
bool reliable);
};
static const PacketTypeHandler packetTypeRouter[PACKET_TYPE_MAX];
Connection *m_connection = nullptr;
};
}

View File

@@ -0,0 +1,100 @@
/*
Minetest
Copyright (C) 2013-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "exceptions.h"
namespace con
{
/*
Exceptions
*/
class NotFoundException : public BaseException
{
public:
NotFoundException(const char *s) : BaseException(s) {}
};
class PeerNotFoundException : public BaseException
{
public:
PeerNotFoundException(const char *s) : BaseException(s) {}
};
class ConnectionException : public BaseException
{
public:
ConnectionException(const char *s) : BaseException(s) {}
};
class ConnectionBindFailed : public BaseException
{
public:
ConnectionBindFailed(const char *s) : BaseException(s) {}
};
class InvalidIncomingDataException : public BaseException
{
public:
InvalidIncomingDataException(const char *s) : BaseException(s) {}
};
class NoIncomingDataException : public BaseException
{
public:
NoIncomingDataException(const char *s) : BaseException(s) {}
};
class ProcessedSilentlyException : public BaseException
{
public:
ProcessedSilentlyException(const char *s) : BaseException(s) {}
};
class ProcessedQueued : public BaseException
{
public:
ProcessedQueued(const char *s) : BaseException(s) {}
};
class IncomingDataCorruption : public BaseException
{
public:
IncomingDataCorruption(const char *s) : BaseException(s) {}
};
}
class SocketException : public BaseException
{
public:
SocketException(const std::string &s) : BaseException(s) {}
};
class ResolveError : public BaseException
{
public:
ResolveError(const std::string &s) : BaseException(s) {}
};
class SendFailedException : public BaseException
{
public:
SendFailedException(const std::string &s) : BaseException(s) {}
};

View File

@@ -0,0 +1,559 @@
/*
Minetest
Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "networkpacket.h"
#include <sstream>
#include "networkexceptions.h"
#include "util/serialize.h"
#include "networkprotocol.h"
NetworkPacket::NetworkPacket(u16 command, u32 datasize, session_t peer_id):
m_datasize(datasize), m_command(command), m_peer_id(peer_id)
{
m_data.resize(m_datasize);
}
NetworkPacket::NetworkPacket(u16 command, u32 datasize):
m_datasize(datasize), m_command(command)
{
m_data.resize(m_datasize);
}
NetworkPacket::~NetworkPacket()
{
m_data.clear();
}
void NetworkPacket::checkReadOffset(u32 from_offset, u32 field_size)
{
if (from_offset + field_size > m_datasize) {
std::stringstream ss;
ss << "Reading outside packet (offset: " <<
from_offset << ", packet size: " << getSize() << ")";
throw PacketError(ss.str());
}
}
void NetworkPacket::putRawPacket(const u8 *data, u32 datasize, session_t peer_id)
{
// If a m_command is already set, we are rewriting on same packet
// This is not permitted
assert(m_command == 0);
m_datasize = datasize - 2;
m_peer_id = peer_id;
m_data.resize(m_datasize);
// split command and datas
m_command = readU16(&data[0]);
memcpy(m_data.data(), &data[2], m_datasize);
}
void NetworkPacket::clear()
{
m_data.clear();
m_datasize = 0;
m_read_offset = 0;
m_command = 0;
m_peer_id = 0;
}
const char* NetworkPacket::getString(u32 from_offset)
{
checkReadOffset(from_offset, 0);
return (char*)&m_data[from_offset];
}
void NetworkPacket::putRawString(const char* src, u32 len)
{
if (m_read_offset + len > m_datasize) {
m_datasize = m_read_offset + len;
m_data.resize(m_datasize);
}
if (len == 0)
return;
memcpy(&m_data[m_read_offset], src, len);
m_read_offset += len;
}
NetworkPacket& NetworkPacket::operator>>(std::string& dst)
{
checkReadOffset(m_read_offset, 2);
u16 strLen = readU16(&m_data[m_read_offset]);
m_read_offset += 2;
dst.clear();
if (strLen == 0) {
return *this;
}
checkReadOffset(m_read_offset, strLen);
dst.reserve(strLen);
dst.append((char*)&m_data[m_read_offset], strLen);
m_read_offset += strLen;
return *this;
}
NetworkPacket& NetworkPacket::operator<<(const std::string &src)
{
if (src.size() > STRING_MAX_LEN) {
throw PacketError("String too long");
}
u16 msgsize = src.size();
*this << msgsize;
putRawString(src.c_str(), (u32)msgsize);
return *this;
}
void NetworkPacket::putLongString(const std::string &src)
{
if (src.size() > LONG_STRING_MAX_LEN) {
throw PacketError("String too long");
}
u32 msgsize = src.size();
*this << msgsize;
putRawString(src.c_str(), msgsize);
}
static constexpr bool NEED_SURROGATE_CODING = sizeof(wchar_t) > 2;
NetworkPacket& NetworkPacket::operator>>(std::wstring& dst)
{
checkReadOffset(m_read_offset, 2);
u16 strLen = readU16(&m_data[m_read_offset]);
m_read_offset += 2;
dst.clear();
if (strLen == 0) {
return *this;
}
checkReadOffset(m_read_offset, strLen * 2);
dst.reserve(strLen);
for (u16 i = 0; i < strLen; i++) {
wchar_t c = readU16(&m_data[m_read_offset]);
if (NEED_SURROGATE_CODING && c >= 0xD800 && c < 0xDC00 && i+1 < strLen) {
i++;
m_read_offset += sizeof(u16);
wchar_t c2 = readU16(&m_data[m_read_offset]);
c = 0x10000 + ( ((c & 0x3ff) << 10) | (c2 & 0x3ff) );
}
dst.push_back(c);
m_read_offset += sizeof(u16);
}
return *this;
}
NetworkPacket& NetworkPacket::operator<<(const std::wstring &src)
{
if (src.size() > WIDE_STRING_MAX_LEN) {
throw PacketError("String too long");
}
if (!NEED_SURROGATE_CODING || src.size() == 0) {
*this << static_cast<u16>(src.size());
for (u16 i = 0; i < src.size(); i++)
*this << static_cast<u16>(src[i]);
return *this;
}
// write dummy value, to be overwritten later
const u32 len_offset = m_read_offset;
u32 written = 0;
*this << static_cast<u16>(0xfff0);
for (u16 i = 0; i < src.size(); i++) {
wchar_t c = src[i];
if (c > 0xffff) {
// Encode high code-points as surrogate pairs
u32 n = c - 0x10000;
*this << static_cast<u16>(0xD800 | (n >> 10))
<< static_cast<u16>(0xDC00 | (n & 0x3ff));
written += 2;
} else {
*this << static_cast<u16>(c);
written++;
}
}
if (written > WIDE_STRING_MAX_LEN)
throw PacketError("String too long");
writeU16(&m_data[len_offset], written);
return *this;
}
std::string NetworkPacket::readLongString()
{
checkReadOffset(m_read_offset, 4);
u32 strLen = readU32(&m_data[m_read_offset]);
m_read_offset += 4;
if (strLen == 0) {
return "";
}
if (strLen > LONG_STRING_MAX_LEN) {
throw PacketError("String too long");
}
checkReadOffset(m_read_offset, strLen);
std::string dst;
dst.reserve(strLen);
dst.append((char*)&m_data[m_read_offset], strLen);
m_read_offset += strLen;
return dst;
}
NetworkPacket& NetworkPacket::operator>>(char& dst)
{
checkReadOffset(m_read_offset, 1);
dst = readU8(&m_data[m_read_offset]);
m_read_offset += 1;
return *this;
}
NetworkPacket& NetworkPacket::operator<<(char src)
{
checkDataSize(1);
writeU8(&m_data[m_read_offset], src);
m_read_offset += 1;
return *this;
}
NetworkPacket& NetworkPacket::operator<<(u8 src)
{
checkDataSize(1);
writeU8(&m_data[m_read_offset], src);
m_read_offset += 1;
return *this;
}
NetworkPacket& NetworkPacket::operator<<(bool src)
{
checkDataSize(1);
writeU8(&m_data[m_read_offset], src);
m_read_offset += 1;
return *this;
}
NetworkPacket& NetworkPacket::operator<<(u16 src)
{
checkDataSize(2);
writeU16(&m_data[m_read_offset], src);
m_read_offset += 2;
return *this;
}
NetworkPacket& NetworkPacket::operator<<(u32 src)
{
checkDataSize(4);
writeU32(&m_data[m_read_offset], src);
m_read_offset += 4;
return *this;
}
NetworkPacket& NetworkPacket::operator<<(u64 src)
{
checkDataSize(8);
writeU64(&m_data[m_read_offset], src);
m_read_offset += 8;
return *this;
}
NetworkPacket& NetworkPacket::operator<<(float src)
{
checkDataSize(4);
writeF32(&m_data[m_read_offset], src);
m_read_offset += 4;
return *this;
}
NetworkPacket& NetworkPacket::operator>>(bool& dst)
{
checkReadOffset(m_read_offset, 1);
dst = readU8(&m_data[m_read_offset]);
m_read_offset += 1;
return *this;
}
NetworkPacket& NetworkPacket::operator>>(u8& dst)
{
checkReadOffset(m_read_offset, 1);
dst = readU8(&m_data[m_read_offset]);
m_read_offset += 1;
return *this;
}
u8 NetworkPacket::getU8(u32 offset)
{
checkReadOffset(offset, 1);
return readU8(&m_data[offset]);
}
u8* NetworkPacket::getU8Ptr(u32 from_offset)
{
if (m_datasize == 0) {
return NULL;
}
checkReadOffset(from_offset, 1);
return (u8*)&m_data[from_offset];
}
NetworkPacket& NetworkPacket::operator>>(u16& dst)
{
checkReadOffset(m_read_offset, 2);
dst = readU16(&m_data[m_read_offset]);
m_read_offset += 2;
return *this;
}
u16 NetworkPacket::getU16(u32 from_offset)
{
checkReadOffset(from_offset, 2);
return readU16(&m_data[from_offset]);
}
NetworkPacket& NetworkPacket::operator>>(u32& dst)
{
checkReadOffset(m_read_offset, 4);
dst = readU32(&m_data[m_read_offset]);
m_read_offset += 4;
return *this;
}
NetworkPacket& NetworkPacket::operator>>(u64& dst)
{
checkReadOffset(m_read_offset, 8);
dst = readU64(&m_data[m_read_offset]);
m_read_offset += 8;
return *this;
}
NetworkPacket& NetworkPacket::operator>>(float& dst)
{
checkReadOffset(m_read_offset, 4);
dst = readF32(&m_data[m_read_offset]);
m_read_offset += 4;
return *this;
}
NetworkPacket& NetworkPacket::operator>>(v2f& dst)
{
checkReadOffset(m_read_offset, 8);
dst = readV2F32(&m_data[m_read_offset]);
m_read_offset += 8;
return *this;
}
NetworkPacket& NetworkPacket::operator>>(v3f& dst)
{
checkReadOffset(m_read_offset, 12);
dst = readV3F32(&m_data[m_read_offset]);
m_read_offset += 12;
return *this;
}
NetworkPacket& NetworkPacket::operator>>(s16& dst)
{
checkReadOffset(m_read_offset, 2);
dst = readS16(&m_data[m_read_offset]);
m_read_offset += 2;
return *this;
}
NetworkPacket& NetworkPacket::operator<<(s16 src)
{
*this << (u16) src;
return *this;
}
NetworkPacket& NetworkPacket::operator>>(s32& dst)
{
checkReadOffset(m_read_offset, 4);
dst = readS32(&m_data[m_read_offset]);
m_read_offset += 4;
return *this;
}
NetworkPacket& NetworkPacket::operator<<(s32 src)
{
*this << (u32) src;
return *this;
}
NetworkPacket& NetworkPacket::operator>>(v3s16& dst)
{
checkReadOffset(m_read_offset, 6);
dst = readV3S16(&m_data[m_read_offset]);
m_read_offset += 6;
return *this;
}
NetworkPacket& NetworkPacket::operator>>(v2s32& dst)
{
checkReadOffset(m_read_offset, 8);
dst = readV2S32(&m_data[m_read_offset]);
m_read_offset += 8;
return *this;
}
NetworkPacket& NetworkPacket::operator>>(v3s32& dst)
{
checkReadOffset(m_read_offset, 12);
dst = readV3S32(&m_data[m_read_offset]);
m_read_offset += 12;
return *this;
}
NetworkPacket& NetworkPacket::operator<<(v2f src)
{
*this << (float) src.X;
*this << (float) src.Y;
return *this;
}
NetworkPacket& NetworkPacket::operator<<(v3f src)
{
*this << (float) src.X;
*this << (float) src.Y;
*this << (float) src.Z;
return *this;
}
NetworkPacket& NetworkPacket::operator<<(v3s16 src)
{
*this << (s16) src.X;
*this << (s16) src.Y;
*this << (s16) src.Z;
return *this;
}
NetworkPacket& NetworkPacket::operator<<(v2s32 src)
{
*this << (s32) src.X;
*this << (s32) src.Y;
return *this;
}
NetworkPacket& NetworkPacket::operator<<(v3s32 src)
{
*this << (s32) src.X;
*this << (s32) src.Y;
*this << (s32) src.Z;
return *this;
}
NetworkPacket& NetworkPacket::operator>>(video::SColor& dst)
{
checkReadOffset(m_read_offset, 4);
dst = readARGB8(&m_data[m_read_offset]);
m_read_offset += 4;
return *this;
}
NetworkPacket& NetworkPacket::operator<<(video::SColor src)
{
checkDataSize(4);
writeU32(&m_data[m_read_offset], src.color);
m_read_offset += 4;
return *this;
}
Buffer<u8> NetworkPacket::oldForgePacket()
{
Buffer<u8> sb(m_datasize + 2);
writeU16(&sb[0], m_command);
memcpy(&sb[2], m_data.data(), m_datasize);
return sb;
}

137
src/network/networkpacket.h Normal file
View File

@@ -0,0 +1,137 @@
/*
Minetest
Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "util/pointer.h"
#include "util/numeric.h"
#include "networkprotocol.h"
#include <SColor.h>
class NetworkPacket
{
public:
NetworkPacket(u16 command, u32 datasize, session_t peer_id);
NetworkPacket(u16 command, u32 datasize);
NetworkPacket() = default;
~NetworkPacket();
void putRawPacket(const u8 *data, u32 datasize, session_t peer_id);
void clear();
// Getters
u32 getSize() const { return m_datasize; }
session_t getPeerId() const { return m_peer_id; }
u16 getCommand() { return m_command; }
u32 getRemainingBytes() const { return m_datasize - m_read_offset; }
const char *getRemainingString() { return getString(m_read_offset); }
// Returns a c-string without copying.
// A better name for this would be getRawString()
const char *getString(u32 from_offset);
// major difference to putCString(): doesn't write len into the buffer
void putRawString(const char *src, u32 len);
void putRawString(const std::string &src)
{
putRawString(src.c_str(), src.size());
}
NetworkPacket &operator>>(std::string &dst);
NetworkPacket &operator<<(const std::string &src);
void putLongString(const std::string &src);
NetworkPacket &operator>>(std::wstring &dst);
NetworkPacket &operator<<(const std::wstring &src);
std::string readLongString();
NetworkPacket &operator>>(char &dst);
NetworkPacket &operator<<(char src);
NetworkPacket &operator>>(bool &dst);
NetworkPacket &operator<<(bool src);
u8 getU8(u32 offset);
NetworkPacket &operator>>(u8 &dst);
NetworkPacket &operator<<(u8 src);
u8 *getU8Ptr(u32 offset);
u16 getU16(u32 from_offset);
NetworkPacket &operator>>(u16 &dst);
NetworkPacket &operator<<(u16 src);
NetworkPacket &operator>>(u32 &dst);
NetworkPacket &operator<<(u32 src);
NetworkPacket &operator>>(u64 &dst);
NetworkPacket &operator<<(u64 src);
NetworkPacket &operator>>(float &dst);
NetworkPacket &operator<<(float src);
NetworkPacket &operator>>(v2f &dst);
NetworkPacket &operator<<(v2f src);
NetworkPacket &operator>>(v3f &dst);
NetworkPacket &operator<<(v3f src);
NetworkPacket &operator>>(s16 &dst);
NetworkPacket &operator<<(s16 src);
NetworkPacket &operator>>(s32 &dst);
NetworkPacket &operator<<(s32 src);
NetworkPacket &operator>>(v2s32 &dst);
NetworkPacket &operator<<(v2s32 src);
NetworkPacket &operator>>(v3s16 &dst);
NetworkPacket &operator<<(v3s16 src);
NetworkPacket &operator>>(v3s32 &dst);
NetworkPacket &operator<<(v3s32 src);
NetworkPacket &operator>>(video::SColor &dst);
NetworkPacket &operator<<(video::SColor src);
// Temp, we remove SharedBuffer when migration finished
// ^ this comment has been here for 4 years
Buffer<u8> oldForgePacket();
private:
void checkReadOffset(u32 from_offset, u32 field_size);
inline void checkDataSize(u32 field_size)
{
if (m_read_offset + field_size > m_datasize) {
m_datasize = m_read_offset + field_size;
m_data.resize(m_datasize);
}
}
std::vector<u8> m_data;
u32 m_datasize = 0;
u32 m_read_offset = 0;
u16 m_command = 0;
session_t m_peer_id = 0;
};

File diff suppressed because it is too large Load Diff

77
src/network/peerhandler.h Normal file
View File

@@ -0,0 +1,77 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "networkprotocol.h"
namespace con
{
typedef enum
{
MIN_RTT,
MAX_RTT,
AVG_RTT,
MIN_JITTER,
MAX_JITTER,
AVG_JITTER
} rtt_stat_type;
class Peer;
class PeerHandler
{
public:
PeerHandler() = default;
virtual ~PeerHandler() = default;
/*
This is called after the Peer has been inserted into the
Connection's peer container.
*/
virtual void peerAdded(Peer *peer) = 0;
/*
This is called before the Peer has been removed from the
Connection's peer container.
*/
virtual void deletingPeer(Peer *peer, bool timeout) = 0;
};
enum PeerChangeType : u8
{
PEER_ADDED,
PEER_REMOVED
};
struct PeerChange
{
PeerChange(PeerChangeType t, session_t _peer_id, bool _timeout) :
type(t), peer_id(_peer_id), timeout(_timeout)
{
}
PeerChange() = delete;
PeerChangeType type;
session_t peer_id;
bool timeout;
};
}

View File

@@ -0,0 +1,225 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "serveropcodes.h"
const static ToServerCommandHandler null_command_handler = { "TOSERVER_NULL", TOSERVER_STATE_ALL, &Server::handleCommand_Null };
const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] =
{
null_command_handler, // 0x00 (never use this)
null_command_handler, // 0x01
{ "TOSERVER_INIT", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Init }, // 0x02
null_command_handler, // 0x03
null_command_handler, // 0x04
null_command_handler, // 0x05
null_command_handler, // 0x06
null_command_handler, // 0x07
null_command_handler, // 0x08
null_command_handler, // 0x09
null_command_handler, // 0x0a
null_command_handler, // 0x0b
null_command_handler, // 0x0c
null_command_handler, // 0x0d
null_command_handler, // 0x0e
null_command_handler, // 0x0f
null_command_handler, // 0x10
{ "TOSERVER_INIT2", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Init2 }, // 0x11
null_command_handler, // 0x12
null_command_handler, // 0x13
null_command_handler, // 0x14
null_command_handler, // 0x15
null_command_handler, // 0x16
{ "TOSERVER_MODCHANNEL_JOIN", TOSERVER_STATE_INGAME, &Server::handleCommand_ModChannelJoin }, // 0x17
{ "TOSERVER_MODCHANNEL_LEAVE", TOSERVER_STATE_INGAME, &Server::handleCommand_ModChannelLeave }, // 0x18
{ "TOSERVER_MODCHANNEL_MSG", TOSERVER_STATE_INGAME, &Server::handleCommand_ModChannelMsg }, // 0x19
null_command_handler, // 0x1a
null_command_handler, // 0x1b
null_command_handler, // 0x1c
null_command_handler, // 0x1d
null_command_handler, // 0x1e
null_command_handler, // 0x1f
null_command_handler, // 0x20
null_command_handler, // 0x21
null_command_handler, // 0x22
{ "TOSERVER_PLAYERPOS", TOSERVER_STATE_INGAME, &Server::handleCommand_PlayerPos }, // 0x23
{ "TOSERVER_GOTBLOCKS", TOSERVER_STATE_STARTUP, &Server::handleCommand_GotBlocks }, // 0x24
{ "TOSERVER_DELETEDBLOCKS", TOSERVER_STATE_INGAME, &Server::handleCommand_DeletedBlocks }, // 0x25
null_command_handler, // 0x26
null_command_handler, // 0x27
null_command_handler, // 0x28
null_command_handler, // 0x29
null_command_handler, // 0x2a
null_command_handler, // 0x2b
null_command_handler, // 0x2c
null_command_handler, // 0x2d
null_command_handler, // 0x2e
null_command_handler, // 0x2f
null_command_handler, // 0x30
{ "TOSERVER_INVENTORY_ACTION", TOSERVER_STATE_INGAME, &Server::handleCommand_InventoryAction }, // 0x31
{ "TOSERVER_CHAT_MESSAGE", TOSERVER_STATE_INGAME, &Server::handleCommand_ChatMessage }, // 0x32
null_command_handler, // 0x33
null_command_handler, // 0x34
{ "TOSERVER_DAMAGE", TOSERVER_STATE_INGAME, &Server::handleCommand_Damage }, // 0x35
null_command_handler, // 0x36
{ "TOSERVER_PLAYERITEM", TOSERVER_STATE_INGAME, &Server::handleCommand_PlayerItem }, // 0x37
{ "TOSERVER_RESPAWN", TOSERVER_STATE_INGAME, &Server::handleCommand_Respawn }, // 0x38
{ "TOSERVER_INTERACT", TOSERVER_STATE_INGAME, &Server::handleCommand_Interact }, // 0x39
{ "TOSERVER_REMOVED_SOUNDS", TOSERVER_STATE_INGAME, &Server::handleCommand_RemovedSounds }, // 0x3a
{ "TOSERVER_NODEMETA_FIELDS", TOSERVER_STATE_INGAME, &Server::handleCommand_NodeMetaFields }, // 0x3b
{ "TOSERVER_INVENTORY_FIELDS", TOSERVER_STATE_INGAME, &Server::handleCommand_InventoryFields }, // 0x3c
null_command_handler, // 0x3d
null_command_handler, // 0x3e
null_command_handler, // 0x3f
{ "TOSERVER_REQUEST_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_RequestMedia }, // 0x40
{ "TOSERVER_HAVE_MEDIA", TOSERVER_STATE_INGAME, &Server::handleCommand_HaveMedia }, // 0x41
null_command_handler, // 0x42
{ "TOSERVER_CLIENT_READY", TOSERVER_STATE_STARTUP, &Server::handleCommand_ClientReady }, // 0x43
null_command_handler, // 0x44
null_command_handler, // 0x45
null_command_handler, // 0x46
null_command_handler, // 0x47
null_command_handler, // 0x48
null_command_handler, // 0x49
null_command_handler, // 0x4a
null_command_handler, // 0x4b
null_command_handler, // 0x4c
null_command_handler, // 0x4d
null_command_handler, // 0x4e
null_command_handler, // 0x4f
{ "TOSERVER_FIRST_SRP", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_FirstSrp }, // 0x50
{ "TOSERVER_SRP_BYTES_A", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_SrpBytesA }, // 0x51
{ "TOSERVER_SRP_BYTES_M", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_SrpBytesM }, // 0x52
};
const static ClientCommandFactory null_command_factory = { "TOCLIENT_NULL", 0, false };
/*
Channels used for Server -> Client communication
2: Bulk data (mapblocks, media, ...)
1: HUD packets
0: everything else
Packet order is only guaranteed inside a channel, so packets that operate on
the same objects are *required* to be in the same channel.
*/
const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] =
{
null_command_factory, // 0x00
null_command_factory, // 0x01
{ "TOCLIENT_HELLO", 0, true }, // 0x02
{ "TOCLIENT_AUTH_ACCEPT", 0, true }, // 0x03
{ "TOCLIENT_ACCEPT_SUDO_MODE", 0, true }, // 0x04
{ "TOCLIENT_DENY_SUDO_MODE", 0, true }, // 0x05
null_command_factory, // 0x06
null_command_factory, // 0x07
null_command_factory, // 0x08
null_command_factory, // 0x09
{ "TOCLIENT_ACCESS_DENIED", 0, true }, // 0x0A
null_command_factory, // 0x0B
null_command_factory, // 0x0C
null_command_factory, // 0x0D
null_command_factory, // 0x0E
null_command_factory, // 0x0F
{ "TOCLIENT_INIT", 0, true }, // 0x10
null_command_factory, // 0x11
null_command_factory, // 0x12
null_command_factory, // 0x13
null_command_factory, // 0x14
null_command_factory, // 0x15
null_command_factory, // 0x16
null_command_factory, // 0x17
null_command_factory, // 0x18
null_command_factory, // 0x19
null_command_factory, // 0x1A
null_command_factory, // 0x1B
null_command_factory, // 0x1C
null_command_factory, // 0x1D
null_command_factory, // 0x1E
null_command_factory, // 0x1F
{ "TOCLIENT_BLOCKDATA", 2, true }, // 0x20
{ "TOCLIENT_ADDNODE", 0, true }, // 0x21
{ "TOCLIENT_REMOVENODE", 0, true }, // 0x22
null_command_factory, // 0x23
null_command_factory, // 0x24
null_command_factory, // 0x25
null_command_factory, // 0x26
{ "TOCLIENT_INVENTORY", 0, true }, // 0x27
null_command_factory, // 0x28
{ "TOCLIENT_TIME_OF_DAY", 0, true }, // 0x29
{ "TOCLIENT_CSM_RESTRICTION_FLAGS", 0, true }, // 0x2A
{ "TOCLIENT_PLAYER_SPEED", 0, true }, // 0x2B
{ "TOCLIENT_MEDIA_PUSH", 0, true }, // 0x2C (sent over channel 1 too if legacy)
null_command_factory, // 0x2D
null_command_factory, // 0x2E
{ "TOCLIENT_CHAT_MESSAGE", 0, true }, // 0x2F
null_command_factory, // 0x30
{ "TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD", 0, true }, // 0x31
{ "TOCLIENT_ACTIVE_OBJECT_MESSAGES", 0, true }, // 0x32 (may be sent as unrel over channel 1 too)
{ "TOCLIENT_HP", 0, true }, // 0x33
{ "TOCLIENT_MOVE_PLAYER", 0, true }, // 0x34
null_command_factory, // 0x35
{ "TOCLIENT_FOV", 0, true }, // 0x36
{ "TOCLIENT_DEATHSCREEN", 0, true }, // 0x37
{ "TOCLIENT_MEDIA", 2, true }, // 0x38
null_command_factory, // 0x39
{ "TOCLIENT_NODEDEF", 0, true }, // 0x3A
null_command_factory, // 0x3B
{ "TOCLIENT_ANNOUNCE_MEDIA", 0, true }, // 0x3C
{ "TOCLIENT_ITEMDEF", 0, true }, // 0x3D
null_command_factory, // 0x3E
{ "TOCLIENT_PLAY_SOUND", 0, true }, // 0x3f (may be sent as unrel too)
{ "TOCLIENT_STOP_SOUND", 0, true }, // 0x40
{ "TOCLIENT_PRIVILEGES", 0, true }, // 0x41
{ "TOCLIENT_INVENTORY_FORMSPEC", 0, true }, // 0x42
{ "TOCLIENT_DETACHED_INVENTORY", 0, true }, // 0x43
{ "TOCLIENT_SHOW_FORMSPEC", 0, true }, // 0x44
{ "TOCLIENT_MOVEMENT", 0, true }, // 0x45
{ "TOCLIENT_SPAWN_PARTICLE", 0, true }, // 0x46
{ "TOCLIENT_ADD_PARTICLESPAWNER", 0, true }, // 0x47
null_command_factory, // 0x48
{ "TOCLIENT_HUDADD", 1, true }, // 0x49
{ "TOCLIENT_HUDRM", 1, true }, // 0x4a
{ "TOCLIENT_HUDCHANGE", 1, true }, // 0x4b
{ "TOCLIENT_HUD_SET_FLAGS", 1, true }, // 0x4c
{ "TOCLIENT_HUD_SET_PARAM", 1, true }, // 0x4d
{ "TOCLIENT_BREATH", 0, true }, // 0x4e
{ "TOCLIENT_SET_SKY", 0, true }, // 0x4f
{ "TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO", 0, true }, // 0x50
{ "TOCLIENT_LOCAL_PLAYER_ANIMATIONS", 0, true }, // 0x51
{ "TOCLIENT_EYE_OFFSET", 0, true }, // 0x52
{ "TOCLIENT_DELETE_PARTICLESPAWNER", 0, true }, // 0x53
{ "TOCLIENT_CLOUD_PARAMS", 0, true }, // 0x54
{ "TOCLIENT_FADE_SOUND", 0, true }, // 0x55
{ "TOCLIENT_UPDATE_PLAYER_LIST", 0, true }, // 0x56
{ "TOCLIENT_MODCHANNEL_MSG", 0, true }, // 0x57
{ "TOCLIENT_MODCHANNEL_SIGNAL", 0, true }, // 0x58
{ "TOCLIENT_NODEMETA_CHANGED", 0, true }, // 0x59
{ "TOCLIENT_SET_SUN", 0, true }, // 0x5a
{ "TOCLIENT_SET_MOON", 0, true }, // 0x5b
{ "TOCLIENT_SET_STARS", 0, true }, // 0x5c
null_command_factory, // 0x5d
null_command_factory, // 0x5e
null_command_factory, // 0x5f
{ "TOSERVER_SRP_BYTES_S_B", 0, true }, // 0x60
{ "TOCLIENT_FORMSPEC_PREPEND", 0, true }, // 0x61
{ "TOCLIENT_MINIMAP_MODES", 0, true }, // 0x62
};

View File

@@ -0,0 +1,50 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "server.h"
#include "networkprotocol.h"
class NetworkPacket;
enum ToServerConnectionState {
TOSERVER_STATE_NOT_CONNECTED,
TOSERVER_STATE_STARTUP,
TOSERVER_STATE_INGAME,
TOSERVER_STATE_ALL,
};
struct ToServerCommandHandler
{
const std::string name;
ToServerConnectionState state;
void (Server::*handler)(NetworkPacket* pkt);
};
struct ClientCommandFactory
{
const char* name;
u8 channel;
bool reliable;
};
extern const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES];
extern const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES];

File diff suppressed because it is too large Load Diff

372
src/network/socket.cpp Normal file
View File

@@ -0,0 +1,372 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "socket.h"
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include "util/string.h"
#include "util/numeric.h"
#include "constants.h"
#include "debug.h"
#include "log.h"
#ifdef _WIN32
// Without this some of the network functions are not found on mingw
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#define LAST_SOCKET_ERR() WSAGetLastError()
#define SOCKET_ERR_STR(e) itos(e)
typedef int socklen_t;
#else
#include <cerrno>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#define LAST_SOCKET_ERR() (errno)
#define SOCKET_ERR_STR(e) strerror(e)
#endif
// Set to true to enable verbose debug output
bool socket_enable_debug_output = false; // yuck
static bool g_sockets_initialized = false;
// Initialize sockets
void sockets_init()
{
#ifdef _WIN32
// Windows needs sockets to be initialized before use
WSADATA WsaData;
if (WSAStartup(MAKEWORD(2, 2), &WsaData) != NO_ERROR)
throw SocketException("WSAStartup failed");
#endif
g_sockets_initialized = true;
}
void sockets_cleanup()
{
#ifdef _WIN32
// On Windows, cleanup sockets after use
WSACleanup();
#endif
}
/*
UDPSocket
*/
UDPSocket::UDPSocket(bool ipv6)
{
init(ipv6, false);
}
bool UDPSocket::init(bool ipv6, bool noExceptions)
{
if (!g_sockets_initialized) {
dstream << "Sockets not initialized" << std::endl;
return false;
}
// Use IPv6 if specified
m_addr_family = ipv6 ? AF_INET6 : AF_INET;
m_handle = socket(m_addr_family, SOCK_DGRAM, IPPROTO_UDP);
if (socket_enable_debug_output) {
dstream << "UDPSocket(" << (int)m_handle
<< ")::UDPSocket(): ipv6 = " << (ipv6 ? "true" : "false")
<< std::endl;
}
if (m_handle <= 0) {
if (noExceptions) {
return false;
}
throw SocketException(std::string("Failed to create socket: error ") +
SOCKET_ERR_STR(LAST_SOCKET_ERR()));
}
setTimeoutMs(0);
if (m_addr_family == AF_INET6) {
// Allow our socket to accept both IPv4 and IPv6 connections
// required on Windows:
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx
int value = 0;
setsockopt(m_handle, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast<char *>(&value), sizeof(value));
}
return true;
}
UDPSocket::~UDPSocket()
{
if (socket_enable_debug_output) {
dstream << "UDPSocket( " << (int)m_handle << ")::~UDPSocket()"
<< std::endl;
}
#ifdef _WIN32
closesocket(m_handle);
#else
close(m_handle);
#endif
}
void UDPSocket::Bind(Address addr)
{
if (socket_enable_debug_output) {
dstream << "UDPSocket(" << (int)m_handle
<< ")::Bind(): " << addr.serializeString() << ":"
<< addr.getPort() << std::endl;
}
if (addr.getFamily() != m_addr_family) {
const char *errmsg =
"Socket and bind address families do not match";
errorstream << "Bind failed: " << errmsg << std::endl;
throw SocketException(errmsg);
}
int ret = 0;
if (m_addr_family == AF_INET6) {
struct sockaddr_in6 address;
memset(&address, 0, sizeof(address));
address.sin6_family = AF_INET6;
address.sin6_addr = addr.getAddress6();
address.sin6_port = htons(addr.getPort());
ret = bind(m_handle, (const struct sockaddr *) &address,
sizeof(struct sockaddr_in6));
} else {
struct sockaddr_in address;
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_addr = addr.getAddress();
address.sin_port = htons(addr.getPort());
ret = bind(m_handle, (const struct sockaddr *) &address,
sizeof(struct sockaddr_in));
}
if (ret < 0) {
dstream << (int)m_handle << ": Bind failed: "
<< SOCKET_ERR_STR(LAST_SOCKET_ERR()) << std::endl;
throw SocketException("Failed to bind socket");
}
}
void UDPSocket::Send(const Address &destination, const void *data, int size)
{
bool dumping_packet = false; // for INTERNET_SIMULATOR
if (INTERNET_SIMULATOR)
dumping_packet = myrand() % INTERNET_SIMULATOR_PACKET_LOSS == 0;
if (socket_enable_debug_output) {
// Print packet destination and size
dstream << (int)m_handle << " -> ";
destination.print(dstream);
dstream << ", size=" << size;
// Print packet contents
dstream << ", data=";
for (int i = 0; i < size && i < 20; i++) {
if (i % 2 == 0)
dstream << " ";
unsigned int a = ((const unsigned char *)data)[i];
dstream << std::hex << std::setw(2) << std::setfill('0') << a;
}
if (size > 20)
dstream << "...";
if (dumping_packet)
dstream << " (DUMPED BY INTERNET_SIMULATOR)";
dstream << std::endl;
}
if (dumping_packet) {
// Lol let's forget it
dstream << "UDPSocket::Send(): INTERNET_SIMULATOR: dumping packet."
<< std::endl;
return;
}
if (destination.getFamily() != m_addr_family)
throw SendFailedException("Address family mismatch");
int sent;
if (m_addr_family == AF_INET6) {
struct sockaddr_in6 address = {};
address.sin6_family = AF_INET6;
address.sin6_addr = destination.getAddress6();
address.sin6_port = htons(destination.getPort());
sent = sendto(m_handle, (const char *)data, size, 0,
(struct sockaddr *)&address, sizeof(struct sockaddr_in6));
} else {
struct sockaddr_in address = {};
address.sin_family = AF_INET;
address.sin_addr = destination.getAddress();
address.sin_port = htons(destination.getPort());
sent = sendto(m_handle, (const char *)data, size, 0,
(struct sockaddr *)&address, sizeof(struct sockaddr_in));
}
if (sent != size)
throw SendFailedException("Failed to send packet");
}
int UDPSocket::Receive(Address &sender, void *data, int size)
{
// Return on timeout
if (!WaitData(m_timeout_ms))
return -1;
int received;
if (m_addr_family == AF_INET6) {
struct sockaddr_in6 address;
memset(&address, 0, sizeof(address));
socklen_t address_len = sizeof(address);
received = recvfrom(m_handle, (char *)data, size, 0,
(struct sockaddr *)&address, &address_len);
if (received < 0)
return -1;
u16 address_port = ntohs(address.sin6_port);
const auto *bytes = reinterpret_cast<IPv6AddressBytes*>
(address.sin6_addr.s6_addr);
sender = Address(bytes, address_port);
} else {
struct sockaddr_in address;
memset(&address, 0, sizeof(address));
socklen_t address_len = sizeof(address);
received = recvfrom(m_handle, (char *)data, size, 0,
(struct sockaddr *)&address, &address_len);
if (received < 0)
return -1;
u32 address_ip = ntohl(address.sin_addr.s_addr);
u16 address_port = ntohs(address.sin_port);
sender = Address(address_ip, address_port);
}
if (socket_enable_debug_output) {
// Print packet sender and size
dstream << (int)m_handle << " <- ";
sender.print(dstream);
dstream << ", size=" << received;
// Print packet contents
dstream << ", data=";
for (int i = 0; i < received && i < 20; i++) {
if (i % 2 == 0)
dstream << " ";
unsigned int a = ((const unsigned char *)data)[i];
dstream << std::hex << std::setw(2) << std::setfill('0') << a;
}
if (received > 20)
dstream << "...";
dstream << std::endl;
}
return received;
}
int UDPSocket::GetHandle()
{
return m_handle;
}
void UDPSocket::setTimeoutMs(int timeout_ms)
{
m_timeout_ms = timeout_ms;
}
bool UDPSocket::WaitData(int timeout_ms)
{
fd_set readset;
int result;
// Initialize the set
FD_ZERO(&readset);
FD_SET(m_handle, &readset);
// Initialize time out struct
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = timeout_ms * 1000;
// select()
result = select(m_handle + 1, &readset, NULL, NULL, &tv);
if (result == 0)
return false;
int e = LAST_SOCKET_ERR();
#ifdef _WIN32
if (result < 0 && (e == WSAEINTR || e == WSAEBADF)) {
#else
if (result < 0 && (e == EINTR || e == EBADF)) {
#endif
// N.B. select() fails when sockets are destroyed on Connection's dtor
// with EBADF. Instead of doing tricky synchronization, allow this
// thread to exit but don't throw an exception.
return false;
}
if (result < 0) {
dstream << (int)m_handle << ": Select failed: " << SOCKET_ERR_STR(e)
<< std::endl;
throw SocketException("Select failed");
} else if (!FD_ISSET(m_handle, &readset)) {
// No data
return false;
}
// There is data
return true;
}

56
src/network/socket.h Normal file
View File

@@ -0,0 +1,56 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <ostream>
#include <cstring>
#include "address.h"
#include "irrlichttypes.h"
#include "networkexceptions.h"
extern bool socket_enable_debug_output;
void sockets_init();
void sockets_cleanup();
class UDPSocket
{
public:
UDPSocket() = default;
UDPSocket(bool ipv6);
~UDPSocket();
void Bind(Address addr);
bool init(bool ipv6, bool noExceptions = false);
void Send(const Address &destination, const void *data, int size);
// Returns -1 if there is no data
int Receive(Address &sender, void *data, int size);
int GetHandle(); // For debugging purposes only
void setTimeoutMs(int timeout_ms);
// Returns true if there is data, false if timeout occurred
bool WaitData(int timeout_ms);
private:
int m_handle;
int m_timeout_ms;
int m_addr_family;
};