forked from Mirrors/openclonk
Change netpuncher protocol to be more extensible
- Each packet has a version field. - Clients connecting to the netpuncher always send a request packet. This allows the netpuncher to react differently depending on the client's version. - Encode packets as binary instead of ASCII. This allows adding fields while maintaining compatibility.install-platforms
parent
3476d76e61
commit
298feab441
|
@ -18,19 +18,24 @@
|
||||||
|
|
||||||
#include "network/C4Network2Address.h"
|
#include "network/C4Network2Address.h"
|
||||||
|
|
||||||
|
static const char C4NetpuncherProtocolVersion = 1;
|
||||||
|
// Netpuncher packet header: (1 byte type "Status"), 1 byte version
|
||||||
|
static const size_t HeaderSize = 2, HeaderPSize = 1;
|
||||||
|
|
||||||
void C4NetpuncherID::CompileFunc(StdCompiler *pComp) {
|
void C4NetpuncherID::CompileFunc(StdCompiler *pComp) {
|
||||||
pComp->Value(mkNamingAdapt(v4, "IPv4", 0u));
|
pComp->Value(mkNamingAdapt(v4, "IPv4", 0u));
|
||||||
pComp->Value(mkNamingAdapt(v6, "IPv6", 0u));
|
pComp->Value(mkNamingAdapt(v6, "IPv6", 0u));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<C4NetpuncherPacket> C4NetpuncherPacket::Construct(const C4NetIOPacket& rpack) {
|
std::unique_ptr<C4NetpuncherPacket> C4NetpuncherPacket::Construct(const C4NetIOPacket& rpack) {
|
||||||
if (!rpack.getPData()) return nullptr;
|
if (!rpack.getPData() || *rpack.getPData() != C4NetpuncherProtocolVersion) return nullptr;
|
||||||
try {
|
try {
|
||||||
switch (rpack.getStatus())
|
switch (rpack.getStatus())
|
||||||
{
|
{
|
||||||
case PID_Puncher_AssID: return uptr(new C4NetpuncherPacketAssID(rpack));
|
case PID_Puncher_AssID: return uptr(new C4NetpuncherPacketAssID(rpack));
|
||||||
case PID_Puncher_SReq: return uptr(new C4NetpuncherPacketSReq(rpack));
|
case PID_Puncher_SReq: return uptr(new C4NetpuncherPacketSReq(rpack));
|
||||||
case PID_Puncher_CReq: return uptr(new C4NetpuncherPacketCReq(rpack));
|
case PID_Puncher_CReq: return uptr(new C4NetpuncherPacketCReq(rpack));
|
||||||
|
case PID_Puncher_IDReq: return uptr(new C4NetpuncherPacketIDReq(rpack));
|
||||||
default: return nullptr;
|
default: return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,36 +47,47 @@ C4NetIOPacket C4NetpuncherPacket::PackTo(const C4NetIO::addr_t& addr) const {
|
||||||
pkt.SetAddr(addr);
|
pkt.SetAddr(addr);
|
||||||
StdBuf content(PackInto());
|
StdBuf content(PackInto());
|
||||||
char type = GetType();
|
char type = GetType();
|
||||||
pkt.New(sizeof(type) + content.getSize());
|
pkt.New(sizeof(type) + sizeof(C4NetpuncherProtocolVersion) + content.getSize());
|
||||||
pkt.Write(&type, sizeof(type));
|
size_t offset = 0;
|
||||||
pkt.Write(content, /*offset*/sizeof(type));
|
pkt.Write(&type, sizeof(type), offset);
|
||||||
|
offset += sizeof(type);
|
||||||
|
pkt.Write(&C4NetpuncherProtocolVersion, sizeof(C4NetpuncherProtocolVersion), offset);
|
||||||
|
offset += sizeof(C4NetpuncherProtocolVersion);
|
||||||
|
pkt.Write(content, offset);
|
||||||
return pkt;
|
return pkt;
|
||||||
}
|
}
|
||||||
|
|
||||||
C4NetpuncherPacketCReq::C4NetpuncherPacketCReq(const C4NetIOPacket& rpack) {
|
C4NetpuncherPacketCReq::C4NetpuncherPacketCReq(const C4NetIOPacket& rpack) {
|
||||||
C4Network2Address parse_addr;
|
if (rpack.getPSize() < HeaderPSize + 2 + 16) throw "invalid size";
|
||||||
CompileFromBuf<StdCompilerBinRead>(parse_addr, rpack.getPBuf());
|
uint16_t port = *getBufPtr<uint16_t>(rpack, HeaderSize);
|
||||||
if (parse_addr.getProtocol() != P_UDP) throw P_UDP;
|
addr.SetAddress(C4NetIO::addr_t::Any, port);
|
||||||
addr = parse_addr.getAddr();
|
memcpy(&static_cast<sockaddr_in6*>(&addr)->sin6_addr, getBufPtr<char>(rpack, HeaderSize + sizeof(port)), 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
StdBuf C4NetpuncherPacketCReq::PackInto() const {
|
StdBuf C4NetpuncherPacketCReq::PackInto() const {
|
||||||
C4Network2Address ser_addr;
|
StdBuf buf;
|
||||||
ser_addr.SetAddr(addr);
|
auto sin6 = static_cast<sockaddr_in6>(addr.AsIPv6());
|
||||||
ser_addr.SetProtocol(P_UDP);
|
auto port = addr.GetPort();
|
||||||
return DecompileToBuf<StdCompilerBinWrite>(ser_addr);
|
buf.New(sizeof(port) + sizeof(sin6.sin6_addr));
|
||||||
|
size_t offset = 0;
|
||||||
|
buf.Write(&port, sizeof(port), offset);
|
||||||
|
offset += sizeof(port);
|
||||||
|
buf.Write(&sin6.sin6_addr, sizeof(sin6.sin6_addr), offset);
|
||||||
|
static_assert(sizeof(sin6.sin6_addr) == 16, "expected sin6_addr to be 16 bytes");
|
||||||
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<C4NetpuncherPacketType TYPE>
|
template<C4NetpuncherPacketType TYPE>
|
||||||
C4NetpuncherPacketID<TYPE>::C4NetpuncherPacketID(const C4NetIOPacket& rpack) {
|
C4NetpuncherPacketID<TYPE>::C4NetpuncherPacketID(const C4NetIOPacket& rpack) {
|
||||||
std::istringstream iss(std::string(rpack.getPData(), rpack.getPSize()));
|
if (rpack.getPSize() < HeaderPSize + sizeof(id)) throw "invalid size";
|
||||||
iss >> id;
|
id = *getBufPtr<CID>(rpack, HeaderSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<C4NetpuncherPacketType TYPE>
|
template<C4NetpuncherPacketType TYPE>
|
||||||
StdBuf C4NetpuncherPacketID<TYPE>::PackInto() const {
|
StdBuf C4NetpuncherPacketID<TYPE>::PackInto() const {
|
||||||
std::ostringstream oss;
|
StdBuf buf;
|
||||||
oss << GetID();
|
auto id = GetID();
|
||||||
std::string s = oss.str();
|
buf.New(sizeof(id));
|
||||||
return StdCopyBuf(s.c_str(), s.length());
|
buf.Write(&id, sizeof(id));
|
||||||
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,10 @@
|
||||||
#include "network/C4NetIO.h"
|
#include "network/C4NetIO.h"
|
||||||
|
|
||||||
enum C4NetpuncherPacketType {
|
enum C4NetpuncherPacketType {
|
||||||
PID_Puncher_AssID = 0x51, // Puncher announcing ID to client
|
PID_Puncher_AssID = 0x51, // Puncher announcing ID to host
|
||||||
PID_Puncher_SReq = 0x52, // Client requesting to be served with punching (for an ID)
|
PID_Puncher_SReq = 0x52, // Client requesting to be served with punching (for an ID)
|
||||||
PID_Puncher_CReq = 0x53, // Puncher requesting clients to punch (towards an address)
|
PID_Puncher_CReq = 0x53, // Puncher requesting clients to punch (towards an address)
|
||||||
|
PID_Puncher_IDReq = 0x54, // Host requesting an ID
|
||||||
// extend this with exchanging ICE parameters, some day?
|
// extend this with exchanging ICE parameters, some day?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,6 +49,15 @@ protected:
|
||||||
typedef C4NetpuncherID::value CID;
|
typedef C4NetpuncherID::value CID;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class C4NetpuncherPacketIDReq : public C4NetpuncherPacket {
|
||||||
|
private:
|
||||||
|
StdBuf PackInto() const override { return StdBuf(); }
|
||||||
|
public:
|
||||||
|
C4NetpuncherPacketIDReq() = default;
|
||||||
|
C4NetpuncherPacketIDReq(const C4NetIOPacket& rpack) { }
|
||||||
|
C4NetpuncherPacketType GetType() const final { return PID_Puncher_IDReq; }
|
||||||
|
};
|
||||||
|
|
||||||
template<C4NetpuncherPacketType TYPE>
|
template<C4NetpuncherPacketType TYPE>
|
||||||
class C4NetpuncherPacketID : public C4NetpuncherPacket {
|
class C4NetpuncherPacketID : public C4NetpuncherPacket {
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -43,18 +43,32 @@ private:
|
||||||
} while(peer_addrs.count(nid) && !nid);
|
} while(peer_addrs.count(nid) && !nid);
|
||||||
peer_ids.emplace(AddrPeer, nid);
|
peer_ids.emplace(AddrPeer, nid);
|
||||||
peer_addrs.emplace(nid, AddrPeer);
|
peer_addrs.emplace(nid, AddrPeer);
|
||||||
Send(C4NetpuncherPacketAssID(nid).PackTo(AddrPeer));
|
|
||||||
printf("Punched %s... #%u\n", AddrPeer.ToString().getData(), nid);
|
printf("Punched %s... #%u\n", AddrPeer.ToString().getData(), nid);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO) override {
|
void OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO) override {
|
||||||
auto& addr = rPacket.getAddr();
|
auto& addr = rPacket.getAddr();
|
||||||
auto unpack = C4NetpuncherPacket::Construct(rPacket);
|
auto unpack = C4NetpuncherPacket::Construct(rPacket);
|
||||||
if (!unpack || unpack->GetType() != PID_Puncher_SReq) { Close(addr); return; }
|
if (!unpack) { Close(addr); return; }
|
||||||
auto other_it = peer_addrs.find(dynamic_cast<C4NetpuncherPacketSReq*>(unpack.get())->GetID());
|
switch (unpack->GetType()) {
|
||||||
if (other_it == peer_addrs.end()) return; // Might be nice to return some kind of error, for purposes of debugging.
|
case PID_Puncher_IDReq: {
|
||||||
Send(C4NetpuncherPacketCReq(other_it->second).PackTo(addr));
|
auto it = peer_ids.find(addr);
|
||||||
Send(C4NetpuncherPacketCReq(addr).PackTo(other_it->second));
|
if (it != peer_ids.end()) {
|
||||||
|
Send(C4NetpuncherPacketAssID(it->second).PackTo(addr));
|
||||||
|
printf("Host: #%u\n", it->second);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PID_Puncher_SReq: {
|
||||||
|
auto other_it = peer_addrs.find(dynamic_cast<C4NetpuncherPacketSReq*>(unpack.get())->GetID());
|
||||||
|
if (other_it == peer_addrs.end()) return; // Might be nice to return some kind of error, for purposes of debugging.
|
||||||
|
Send(C4NetpuncherPacketCReq(other_it->second).PackTo(addr));
|
||||||
|
Send(C4NetpuncherPacketCReq(addr).PackTo(other_it->second));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
Close(addr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void OnDisconn(const addr_t &AddrPeer, C4NetIO *pNetIO, const char *szReason) override {
|
void OnDisconn(const addr_t &AddrPeer, C4NetIO *pNetIO, const char *szReason) override {
|
||||||
auto it = peer_ids.find(AddrPeer);
|
auto it = peer_ids.find(AddrPeer);
|
||||||
|
|
|
@ -1032,10 +1032,15 @@ void C4Network2::OnPuncherConnect(C4NetIO::addr_t addr)
|
||||||
}
|
}
|
||||||
// Do not ::Network.InvalidateReference(); yet, we're expecting an ID from the netpuncher
|
// Do not ::Network.InvalidateReference(); yet, we're expecting an ID from the netpuncher
|
||||||
}
|
}
|
||||||
// Client connection: request packet from host.
|
auto family = maybe_v4.GetFamily();
|
||||||
if (!isHost())
|
if (isHost())
|
||||||
{
|
{
|
||||||
auto family = maybe_v4.GetFamily();
|
// Host connection: request ID from netpuncher
|
||||||
|
NetIO.SendPuncherPacket(C4NetpuncherPacketIDReq(), family);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Client connection: request packet from host.
|
||||||
if (Status.getState() == GS_Init && getNetpuncherGameID(family))
|
if (Status.getState() == GS_Init && getNetpuncherGameID(family))
|
||||||
NetIO.SendPuncherPacket(C4NetpuncherPacketSReq(getNetpuncherGameID(family)), family);
|
NetIO.SendPuncherPacket(C4NetpuncherPacketSReq(getNetpuncherGameID(family)), family);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue