You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

267 lines
8.3 KiB

#include "sodium.h"
#include <asio/ts/buffer.hpp>
#include <asio/ts/internet.hpp>
#include <asio/ts/io_context.hpp>
#include <asio/ts/net.hpp>
class dvlnet {
public:
typedef std::vector<unsigned char> buffer_t;
static std::unique_ptr<dvlnet> inst;
virtual int create(std::string addrstr, std::string passwd) = 0;
virtual int join(std::string addrstr, std::string passwd) = 0;
virtual bool SNetReceiveMessage(int *sender, char **data, int *size) = 0;
virtual bool SNetSendMessage(int dest, void *data, unsigned int size) = 0;
virtual bool SNetReceiveTurns(char **data, unsigned int *size, DWORD *status) = 0;
virtual bool SNetSendTurn(char *data, unsigned int size) = 0;
virtual int SNetGetProviderCaps(struct _SNETCAPS *caps) = 0;
virtual void *SNetRegisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *)) = 0;
virtual void *SNetUnregisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *)) = 0;
virtual ~dvlnet() {}
};
class dvlnet_null: public dvlnet {
private:
std::queue<buffer_t> message_queue;
buffer_t message_last;
const int plr_single = 0;
public:
virtual int create(std::string addrstr, std::string passwd);
virtual int join(std::string addrstr, std::string passwd);
virtual bool SNetReceiveMessage(int *sender, char **data, int *size);
virtual bool SNetSendMessage(int dest, void *data, unsigned int size);
virtual bool SNetReceiveTurns(char **data, unsigned int *size, DWORD *status);
virtual bool SNetSendTurn(char *data, unsigned int size);
virtual int SNetGetProviderCaps(struct _SNETCAPS *caps);
virtual void *SNetRegisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *));
virtual void *SNetUnregisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *));
};
// exact meaning yet to be worked out
#define PS_HASMSG 0x20000
#define PS_ACTIVE 0x40000
#define PS_CONNECTED 0x10000
class dvlnet_udp : public dvlnet {
public:
dvlnet_udp(buffer_t info);
virtual int create(std::string addrstr, std::string passwd);
virtual int join(std::string addrstr, std::string passwd);
virtual bool SNetReceiveMessage(int *sender, char **data, int *size);
virtual bool SNetSendMessage(int dest, void *data, unsigned int size);
virtual bool SNetReceiveTurns(char **data, unsigned int *size, DWORD *status);
virtual bool SNetSendTurn(char *data, unsigned int size);
virtual int SNetGetProviderCaps(struct _SNETCAPS *caps);
virtual void *SNetRegisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *));
virtual void *SNetUnregisterEventHandler(event_type evtype, void(__stdcall *func)(struct _SNETEVENT *));
static constexpr unsigned short max_packet_size = 0xFFFF;
enum packet_type : uint8_t {
PT_MESSAGE = 0x01,
PT_TURN = 0x02,
PT_JOIN_REQUEST = 0x11,
PT_JOIN_ACCEPT = 0x12,
PT_LEAVE_GAME = 0x13,
};
typedef uint8_t plr_t;
typedef uint32_t cookie_t;
typedef int turn_t; // change int to something else in devilution code later
typedef std::array<unsigned char, crypto_secretbox_KEYBYTES> key_t;
class packet_exception : public std::exception {};
typedef asio::ip::udp::endpoint endpoint;
static const endpoint none;
class packet {
protected:
packet_type m_type;
plr_t m_src;
plr_t m_dest;
buffer_t m_message;
turn_t m_turn;
cookie_t m_cookie;
plr_t m_newplr;
plr_t m_oldplr;
buffer_t m_info;
const key_t &key;
bool have_encrypted = false;
bool have_decrypted = false;
buffer_t encrypted_buffer;
buffer_t decrypted_buffer;
public:
packet(const key_t &k) : key(k) {};
const buffer_t &data();
packet_type type();
plr_t src();
plr_t dest();
const buffer_t &message();
turn_t turn();
cookie_t cookie();
plr_t newplr();
plr_t oldplr();
const buffer_t &info();
};
template<class P> class packet_proc : public packet {
public:
using packet::packet;
void process_data();
};
class packet_in : public packet_proc<packet_in> {
public:
using packet_proc<packet_in>::packet_proc;
void create(buffer_t buf);
void process_element(buffer_t &x);
template <class T> void process_element(T &x);
void decrypt();
};
class packet_out : public packet_proc<packet_out> {
public:
using packet_proc<packet_out>::packet_proc;
void create(packet_type t, plr_t s, plr_t d, buffer_t m);
void create(packet_type t, plr_t s, plr_t d, turn_t u);
void create(packet_type t, plr_t s, plr_t d, cookie_t c);
void create(packet_type t, plr_t s, plr_t d, cookie_t c, plr_t n, buffer_t i);
void create(packet_type t, plr_t s, plr_t d, plr_t o);
void process_element(buffer_t &x);
template <class T> void process_element(T &x);
template <class T> static const unsigned char *begin(const T &x);
template <class T> static const unsigned char *end(const T &x);
void encrypt();
};
typedef std::unique_ptr<packet> upacket;
upacket make_packet(buffer_t buf);
template<typename T, typename... Args> upacket make_packet(T t, Args... args);
private:
static constexpr unsigned short default_port = 6112;
static constexpr unsigned short try_ports = 512;
static constexpr daddr_t ADDR_BROADCAST = 0xFF;
static constexpr daddr_t ADDR_MASTER = 0xFE;
static constexpr int ACTIVE = 60;
std::map<event_type, void(__stdcall *)(struct _SNETEVENT *)> registered_handlers;
buffer_t game_init_info;
struct message_t {
int sender; // change int to something else in devilution code later
buffer_t payload;
message_t() : sender(-1), payload({}) {};
message_t(int s, buffer_t p) : sender(s), payload(p) {};
};
message_t message_last;
std::queue<message_t> message_queue;
std::array<turn_t, MAX_PLRS> turn_last = { 0 };
std::array<bool, MAX_PLRS> turn_new = { false };
plr_t plr_self = ADDR_BROADCAST;
unsigned short udpport_self = 0;
cookie_t cookie_self = 0;
key_t key = { 0 };
endpoint master;
std::set<endpoint> connection_requests_pending;
std::array<endpoint, MAX_PLRS> nexthop_table;
std::array<int, MAX_PLRS> active_table = { 0 };
asio::io_context context;
asio::ip::udp::socket sock = asio::ip::udp::socket(context);
unsigned short bind();
void setup_password(std::string pw);
void handle_join_request(upacket &pkt, endpoint sender);
void handle_accept(upacket &pkt);
void recv();
void send(upacket &pkt, endpoint sender = none);
void recv_decrypted(upacket &pkt, endpoint sender);
std::set<endpoint> dests_for_addr(plr_t dest, endpoint sender);
void run_event_handler(_SNETEVENT &ev);
};
template<class P> void dvlnet_udp::packet_proc<P>::process_data()
{
P &self = static_cast<P&>(*this);
self.process_element(m_type);
self.process_element(m_src);
self.process_element(m_dest);
switch (m_type) {
case PT_MESSAGE:
self.process_element(m_message);
break;
case PT_TURN:
self.process_element(m_turn);
break;
case PT_JOIN_REQUEST:
self.process_element(m_cookie);
break;
case PT_JOIN_ACCEPT:
self.process_element(m_cookie);
self.process_element(m_newplr);
self.process_element(m_info);
break;
case PT_LEAVE_GAME:
break;
}
}
inline dvlnet_udp::upacket dvlnet_udp::make_packet(buffer_t buf)
{
auto ret = std::make_unique<packet_in>(key);
ret->create(std::move(buf));
ret->decrypt();
return ret;
}
template<typename T, typename... Args> dvlnet_udp::upacket dvlnet_udp::make_packet(T t, Args... args)
{
auto ret = std::make_unique<packet_out>(key);
ret->create(t, args...);
ret->encrypt();
return ret;
}
inline void dvlnet_udp::packet_in::process_element(buffer_t &x)
{
x.insert(x.begin(), decrypted_buffer.begin(), decrypted_buffer.end());
decrypted_buffer.resize(0);
}
template <class T> void dvlnet_udp::packet_in::process_element(T &x)
{
if (decrypted_buffer.size() < sizeof(T))
throw packet_exception();
x = *reinterpret_cast<T *>(decrypted_buffer.data());
decrypted_buffer.erase(decrypted_buffer.begin(), decrypted_buffer.begin() + sizeof(T));
}
inline void dvlnet_udp::packet_out::process_element(buffer_t &x)
{
encrypted_buffer.insert(encrypted_buffer.end(), x.begin(), x.end());
}
template <class T> void dvlnet_udp::packet_out::process_element(T &x)
{
encrypted_buffer.insert(encrypted_buffer.end(), begin(x), end(x));
}
template <class T> const unsigned char *dvlnet_udp::packet_out::begin(const T &x)
{
return reinterpret_cast<const unsigned char *>(&x);
}
template <class T> const unsigned char *dvlnet_udp::packet_out::end(const T &x)
{
return reinterpret_cast<const unsigned char *>(&x) + sizeof(T);
}