1/*
2 * LegacyClonk
3 *
4 * Copyright (c) 2016-2017, The OpenClonk Team and contributors
5 * Copyright (c) 2019-2020, The LegacyClonk Team and contributors
6 *
7 * Distributed under the terms of the ISC license; see accompanying file
8 * "COPYING" for details.
9 *
10 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11 * See accompanying file "TRADEMARK" for details.
12 *
13 * To redistribute this file separately, substitute the full license texts
14 * for the above references.
15 */
16
17#include "C4Include.h"
18#include "C4PuncherPacket.h"
19
20#include "C4Network2Address.h"
21#include "StdAdaptors.h"
22
23#include <stdexcept>
24
25static constexpr char C4NetpuncherProtocolVersion{1};
26// Netpuncher packet header: (1 byte type "Status"), 1 byte version
27static constexpr std::size_t HeaderSize{2}, HeaderPSize{1};
28
29void C4NetpuncherID::CompileFunc(StdCompiler *const comp)
30{
31 comp->Value(rStruct: mkNamingAdapt(rValue&: v4, szName: "IPv4", rDefault: 0u));
32 comp->Value(rStruct: mkNamingAdapt(rValue&: v6, szName: "IPv6", rDefault: 0u));
33}
34
35auto C4NetpuncherPacket::Construct(const C4NetIOPacket &pkt) -> uptr
36{
37 if (!pkt.getPData() || *pkt.getPData() != C4NetpuncherProtocolVersion) return nullptr;
38 try
39 {
40 switch (pkt.getStatus())
41 {
42 case PID_Puncher_AssID: return uptr{new C4NetpuncherPacketAssID{pkt}};
43 case PID_Puncher_SReq: return uptr{new C4NetpuncherPacketSReq{pkt}};
44 case PID_Puncher_CReq: return uptr{new C4NetpuncherPacketCReq{pkt}};
45 case PID_Puncher_IDReq: return uptr{new C4NetpuncherPacketIDReq{pkt}};
46 default: return nullptr;
47 }
48 }
49 catch (const StdCompiler::Exception &)
50 {
51 return nullptr;
52 }
53 catch (const std::runtime_error &)
54 {
55 return nullptr;
56 }
57}
58
59C4NetIOPacket C4NetpuncherPacket::PackTo(const C4NetIO::addr_t &addr) const
60{
61 C4NetIOPacket pkt;
62 pkt.SetAddr(addr);
63 StdBuf content{PackInto()};
64 const auto &type = GetType();
65 pkt.New(inSize: sizeof(type) + sizeof(C4NetpuncherProtocolVersion) + content.getSize());
66 std::size_t offset{0};
67 pkt.Write(pnData: &type, inSize: sizeof(type), iAt: offset);
68 offset += sizeof(type);
69 pkt.Write(pnData: &C4NetpuncherProtocolVersion, inSize: sizeof(C4NetpuncherProtocolVersion), iAt: offset);
70 offset += sizeof(C4NetpuncherProtocolVersion);
71 pkt.Write(Buf2: content, iAt: offset);
72 return pkt;
73}
74
75C4NetpuncherPacketCReq::C4NetpuncherPacketCReq(const C4NetIOPacket &pkt)
76{
77 if (pkt.getPSize() < HeaderPSize + 2 + 16) throw std::runtime_error{"invalid size"};
78 const std::uint16_t port{*pkt.getPtr<std::uint16_t>(pos: HeaderSize)};
79 addr.SetAddress(addr: C4NetIO::addr_t::Any, port);
80 std::memcpy(dest: &static_cast<sockaddr_in6 *>(&addr)->sin6_addr, src: pkt.getPtr<char>(pos: HeaderSize + sizeof(port)), n: 16);
81}
82
83StdBuf C4NetpuncherPacketCReq::PackInto() const
84{
85 StdBuf buf;
86 const auto sin6 = static_cast<sockaddr_in6>(addr.AsIPv6());
87 const auto port = addr.GetPort();
88 buf.New(inSize: sizeof(port) + sizeof(sin6.sin6_addr));
89 std::size_t offset{0};
90 buf.Write(pnData: &port, inSize: sizeof(port), iAt: offset);
91 offset += sizeof(port);
92 buf.Write(pnData: &sin6.sin6_addr, inSize: sizeof(sin6.sin6_addr), iAt: offset);
93 static_assert(sizeof(sin6.sin6_addr) == 16, "expected sin6_addr to be 16 bytes");
94 return buf;
95}
96
97template<C4NetpuncherPacketType Type>
98C4NetpuncherPacketID<Type>::C4NetpuncherPacketID(const C4NetIOPacket &pkt)
99{
100 if (pkt.getPSize() < HeaderPSize + sizeof(id)) throw std::runtime_error{"invalid size"};
101 id = *pkt.getPtr<CID>(pos: HeaderSize);
102}
103
104template<C4NetpuncherPacketType Type>
105StdBuf C4NetpuncherPacketID<Type>::PackInto() const
106{
107 StdBuf buf;
108 const auto &id = GetID();
109 buf.New(inSize: sizeof(id));
110 buf.Write(&id, sizeof(id));
111 return buf;
112}
113