1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#ifndef ENGINE_SHARED_NETWORK_H
4#define ENGINE_SHARED_NETWORK_H
5
6#include "ringbuffer.h"
7#include "stun.h"
8
9#include <base/types.h>
10
11#include <array>
12#include <optional>
13
14class CHuffman;
15class CNetBan;
16class CPacker;
17
18/*
19
20CURRENT:
21 packet header: 3 bytes
22 unsigned char flags_ack; // 6bit flags, 2bit ack
23 0.6: ORNCaaAA
24 0.6.5: ORNCT-AA
25 0.7: --NORCAA
26
27 O = flag compression
28 R = flag resend
29 N = flag connless
30 C = flag control
31 T = flag token (0.6.5 only not supported by ddnet)
32 - = unused, should be zero
33 a = should be zero otherwise it messes up the ack number
34 A = bit of ack number
35
36
37 unsigned char ack; // 8 bit ack
38 unsigned char num_chunks; // 8 bit chunks
39
40 (unsigned char padding[3]) // 24 bit extra in case it's a connection less packet
41 // this is to make sure that it's compatible with the
42 // old protocol
43
44 chunk header: 2-3 bytes
45 unsigned char flags_size; // 2bit flags, 6 bit size
46 unsigned char size_seq; // 4bit size, 4bit seq
47 (unsigned char seq;) // 8bit seq, if vital flag is set
48*/
49
50enum
51{
52 NETSENDFLAG_VITAL = 1 << 0,
53 NETSENDFLAG_CONNLESS = 1 << 1,
54 NETSENDFLAG_FLUSH = 1 << 2,
55 NETSENDFLAG_EXTENDED = 1 << 3,
56};
57
58enum
59{
60 NETSTATE_OFFLINE = 0,
61 NETSTATE_CONNECTING,
62 NETSTATE_ONLINE,
63};
64
65enum
66{
67 NET_MAX_PACKETSIZE = 1400,
68 NET_MAX_PAYLOAD = NET_MAX_PACKETSIZE - 6,
69 NET_MAX_CHUNKHEADERSIZE = 3,
70 NET_PACKETHEADERSIZE = 3,
71 NET_CONNLESS_EXTRA_SIZE = 4,
72 NET_MAX_CLIENTS = 64,
73 NET_MAX_CONSOLE_CLIENTS = 4,
74 NET_MAX_SEQUENCE = 1 << 10,
75 NET_MAX_PACKET_CHUNKS = 0xFF,
76
77 NET_PACKETFLAG_UNUSED = 1 << 0,
78 NET_PACKETFLAG_TOKEN = 1 << 1,
79 NET_PACKETFLAG_CONTROL = 1 << 2,
80 NET_PACKETFLAG_CONNLESS = 1 << 3,
81 NET_PACKETFLAG_RESEND = 1 << 4,
82 NET_PACKETFLAG_COMPRESSION = 1 << 5,
83 // NOT SENT VIA THE NETWORK DIRECTLY:
84 NET_PACKETFLAG_EXTENDED = 1 << 6,
85
86 NET_CHUNKFLAG_VITAL = 1,
87 NET_CHUNKFLAG_RESEND = 2,
88
89 NET_CTRLMSG_KEEPALIVE = 0,
90 NET_CTRLMSG_CONNECT = 1,
91 NET_CTRLMSG_CONNECTACCEPT = 2,
92 NET_CTRLMSG_ACCEPT = 3,
93 NET_CTRLMSG_CLOSE = 4,
94
95 NET_CONN_BUFFERSIZE = 1024 * 32,
96
97 NET_CONNLIMIT_IPS = 16,
98
99 NET_TOKENCACHE_ADDRESSEXPIRY = 64,
100 NET_TOKENCACHE_PACKETEXPIRY = 5,
101};
102enum
103{
104 NET_TOKEN_MAX = 0xffffffff,
105 NET_TOKEN_NONE = NET_TOKEN_MAX,
106 NET_TOKEN_MASK = NET_TOKEN_MAX,
107
108 NET_TOKENREQUEST_DATASIZE = 512,
109};
110
111typedef int SECURITY_TOKEN;
112typedef unsigned int TOKEN;
113
114SECURITY_TOKEN ToSecurityToken(const unsigned char *pData);
115void WriteSecurityToken(unsigned char *pData, SECURITY_TOKEN Token);
116
117extern const unsigned char SECURITY_TOKEN_MAGIC[4];
118
119enum
120{
121 NET_SECURITY_TOKEN_UNKNOWN = -1,
122 NET_SECURITY_TOKEN_UNSUPPORTED = 0,
123};
124
125typedef int (*NETFUNC_DELCLIENT)(int ClientId, const char *pReason, void *pUser);
126typedef int (*NETFUNC_NEWCLIENT_CON)(int ClientId, void *pUser);
127typedef int (*NETFUNC_NEWCLIENT)(int ClientId, void *pUser, bool Sixup);
128typedef int (*NETFUNC_NEWCLIENT_NOAUTH)(int ClientId, void *pUser);
129typedef int (*NETFUNC_CLIENTREJOIN)(int ClientId, void *pUser);
130
131struct CNetChunk
132{
133 // -1 means that it's a stateless packet
134 // 0 on the client means the server
135 int m_ClientId;
136 NETADDR m_Address; // only used when client_id == -1
137 int m_Flags;
138 int m_DataSize;
139 const void *m_pData;
140 // only used if the flags contain NETSENDFLAG_EXTENDED and NETSENDFLAG_CONNLESS
141 unsigned char m_aExtraData[NET_CONNLESS_EXTRA_SIZE];
142};
143
144class CNetChunkHeader
145{
146public:
147 int m_Flags;
148 int m_Size;
149 int m_Sequence;
150
151 unsigned char *Pack(unsigned char *pData, int Split = 4) const;
152 unsigned char *Unpack(unsigned char *pData, int Split = 4);
153};
154
155class CNetChunkResend
156{
157public:
158 int m_Flags;
159 int m_DataSize;
160 unsigned char *m_pData;
161
162 int m_Sequence;
163 int64_t m_LastSendTime;
164 int64_t m_FirstSendTime;
165};
166
167class CNetPacketConstruct
168{
169public:
170 int m_Flags;
171 int m_Ack;
172 int m_NumChunks;
173 int m_DataSize;
174 unsigned char m_aChunkData[NET_MAX_PAYLOAD];
175 unsigned char m_aExtraData[NET_CONNLESS_EXTRA_SIZE];
176};
177
178enum class CONNECTIVITY
179{
180 UNKNOWN,
181 CHECKING,
182 UNREACHABLE,
183 REACHABLE,
184 ADDRESS_KNOWN,
185};
186
187class CStun
188{
189 class CProtocol
190 {
191 int m_Index;
192 NETSOCKET m_Socket;
193 CStunData m_Stun;
194 bool m_HaveStunServer = false;
195 NETADDR m_StunServer;
196 bool m_HaveAddr = false;
197 NETADDR m_Addr;
198 int64_t m_LastResponse = -1;
199 int64_t m_NextTry = -1;
200 int m_NumUnsuccessfulTries = -1;
201
202 public:
203 CProtocol(int Index, NETSOCKET Socket);
204 void FeedStunServer(NETADDR StunServer);
205 void Refresh();
206 void Update();
207 bool OnPacket(NETADDR Addr, unsigned char *pData, int DataSize);
208 CONNECTIVITY GetConnectivity(NETADDR *pGlobalAddr);
209 };
210 CProtocol m_aProtocols[2];
211
212public:
213 CStun(NETSOCKET Socket);
214 void FeedStunServer(NETADDR StunServer);
215 void Refresh();
216 void Update();
217 bool OnPacket(NETADDR Addr, unsigned char *pData, int DataSize);
218 CONNECTIVITY GetConnectivity(int NetType, NETADDR *pGlobalAddr);
219};
220
221class CNetConnection
222{
223 // TODO: is this needed because this needs to be aware of
224 // the ack sequencing number and is also responsible for updating
225 // that. this should be fixed.
226 friend class CPacketChunkUnpacker;
227
228public:
229 enum class EState
230 {
231 OFFLINE,
232 WANT_TOKEN,
233 CONNECT,
234 PENDING,
235 ONLINE,
236 ERROR,
237 };
238
239 SECURITY_TOKEN m_SecurityToken;
240
241private:
242 unsigned short m_Sequence;
243 unsigned short m_Ack;
244 unsigned short m_PeerAck;
245 EState m_State = EState::OFFLINE;
246 int m_RemoteClosed;
247 bool m_BlockCloseMsg;
248 bool m_UnknownSeq;
249
250 CStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> m_Buffer;
251
252 int64_t m_LastUpdateTime;
253 int64_t m_LastRecvTime;
254 int64_t m_LastSendTime;
255
256 char m_aErrorString[256];
257
258 CNetPacketConstruct m_Construct;
259
260 NETADDR m_aConnectAddrs[16];
261 int m_NumConnectAddrs;
262 NETADDR m_PeerAddr;
263 NETSOCKET m_Socket;
264 NETSTATS m_Stats;
265
266 std::array<char, NETADDR_MAXSTRSIZE> m_aPeerAddrStr;
267 std::array<char, NETADDR_MAXSTRSIZE> m_aPeerAddrStrNoPort;
268 // client 0.7
269 static TOKEN GenerateToken7(const NETADDR *pPeerAddr);
270 class CNetBase *m_pNetBase;
271 bool IsSixup() const { return m_Sixup; }
272
273 //
274 void SetPeerAddr(const NETADDR *pAddr);
275 void ClearPeerAddr();
276 void ResetStats();
277 void SetError(const char *pString);
278 void AckChunks(int Ack);
279
280 int QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence);
281 void SendConnect();
282 void SendControl(int ControlMsg, const void *pExtra, int ExtraSize);
283 void SendControlWithToken7(int ControlMsg, SECURITY_TOKEN ResponseToken);
284 void ResendChunk(CNetChunkResend *pResend);
285 void Resend();
286
287public:
288 bool m_TimeoutProtected;
289 bool m_TimeoutSituation;
290
291 void SetToken7(TOKEN Token);
292
293 void Reset(bool Rejoin = false);
294 void Init(NETSOCKET Socket, bool BlockCloseMsg);
295 int Connect(const NETADDR *pAddr, int NumAddrs);
296 int Connect7(const NETADDR *pAddr, int NumAddrs);
297 void Disconnect(const char *pReason);
298
299 int Update();
300 int Flush();
301
302 int Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_TOKEN SecurityToken = NET_SECURITY_TOKEN_UNSUPPORTED, SECURITY_TOKEN ResponseToken = NET_SECURITY_TOKEN_UNSUPPORTED);
303 int QueueChunk(int Flags, int DataSize, const void *pData);
304
305 const char *ErrorString();
306 void SignalResend();
307 EState State() const { return m_State; }
308 const NETADDR *PeerAddress() const { return &m_PeerAddr; }
309 const std::array<char, NETADDR_MAXSTRSIZE> &PeerAddressString(bool IncludePort) const
310 {
311 return IncludePort ? m_aPeerAddrStr : m_aPeerAddrStrNoPort;
312 }
313 void ConnectAddresses(const NETADDR **ppAddrs, int *pNumAddrs) const
314 {
315 *ppAddrs = m_aConnectAddrs;
316 *pNumAddrs = m_NumConnectAddrs;
317 }
318
319 void ResetErrorString() { m_aErrorString[0] = 0; }
320 const char *ErrorString() const { return m_aErrorString; }
321
322 // Needed for GotProblems in NetClient
323 int64_t LastRecvTime() const { return m_LastRecvTime; }
324 int64_t ConnectTime() const { return m_LastUpdateTime; }
325
326 int AckSequence() const { return m_Ack; }
327 int SeqSequence() const { return m_Sequence; }
328 int SecurityToken() const { return m_SecurityToken; }
329 CStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> *ResendBuffer() { return &m_Buffer; }
330
331 void ResumeConnection(const NETADDR *pAddr, int Sequence, int Ack, SECURITY_TOKEN SecurityToken, CStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> *pResendBuffer, bool Sixup);
332
333 // anti spoof
334 void DirectInit(const NETADDR &Addr, SECURITY_TOKEN SecurityToken, SECURITY_TOKEN Token, bool Sixup);
335 void SetUnknownSeq() { m_UnknownSeq = true; }
336 void SetSequence(int Sequence) { m_Sequence = Sequence; }
337
338 bool m_Sixup;
339 SECURITY_TOKEN m_Token;
340};
341
342class CConsoleNetConnection
343{
344public:
345 enum class EState
346 {
347 OFFLINE,
348 ONLINE,
349 ERROR,
350 };
351
352private:
353 EState m_State;
354
355 NETADDR m_PeerAddr;
356 NETSOCKET m_Socket;
357
358 char m_aBuffer[NET_MAX_PACKETSIZE];
359 int m_BufferOffset;
360
361 char m_aErrorString[256];
362
363 bool m_LineEndingDetected;
364 char m_aLineEnding[3];
365
366public:
367 int Init(NETSOCKET Socket, const NETADDR *pAddr);
368 void Disconnect(const char *pReason);
369
370 EState State() const { return m_State; }
371 const NETADDR *PeerAddress() const { return &m_PeerAddr; }
372 const char *ErrorString() const { return m_aErrorString; }
373
374 void Reset();
375 int Update();
376 int Send(const char *pLine);
377 int Recv(char *pLine, int MaxLength);
378};
379
380/**
381 * Accepts a non-control packet containing one or more chunks and unpacks each chunk individually.
382 * After a packet has been fed into the unpacker by calling @link FeedPacket @endlink, all chunks have
383 * to be unpacked by calling @link UnpackNextChunk @endlink until the function returns `false`, before
384 * the unpacker can be fed another packet.
385 */
386class CPacketChunkUnpacker
387{
388public:
389 void FeedPacket(const NETADDR &Addr, const CNetPacketConstruct &Packet, CNetConnection *pConnection, int ClientId);
390 bool UnpackNextChunk(CNetChunk *pChunk);
391
392private:
393 bool m_Valid = false;
394 NETADDR m_Addr;
395 CNetConnection *m_pConnection;
396 int m_CurrentChunk;
397 int m_ClientId;
398 CNetPacketConstruct m_Data;
399};
400
401// server side
402class CNetServer
403{
404 struct CSlot
405 {
406 public:
407 CNetConnection m_Connection;
408 };
409
410 struct CSpamConn
411 {
412 NETADDR m_Addr;
413 int64_t m_Time;
414 int m_Conns;
415 };
416
417 NETADDR m_Address;
418 NETSOCKET m_Socket;
419 CNetBan *m_pNetBan;
420 CSlot m_aSlots[NET_MAX_CLIENTS];
421 int m_MaxClients = NET_MAX_CLIENTS;
422 int m_MaxClientsPerIp;
423
424 NETFUNC_NEWCLIENT m_pfnNewClient;
425 NETFUNC_NEWCLIENT_NOAUTH m_pfnNewClientNoAuth;
426 NETFUNC_DELCLIENT m_pfnDelClient;
427 NETFUNC_CLIENTREJOIN m_pfnClientRejoin;
428 void *m_pUser;
429
430 unsigned char m_aSecurityTokenSeed[16];
431
432 // vanilla connect flood detection
433 int64_t m_VConnFirst;
434 int m_VConnNum;
435
436 CSpamConn m_aSpamConns[NET_CONNLIMIT_IPS];
437
438 CPacketChunkUnpacker m_PacketChunkUnpacker;
439 CNetPacketConstruct m_RecvBuffer;
440
441 void OnTokenCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketConstruct &Packet);
442 int OnSixupCtrlMsg(NETADDR &Addr, CNetChunk *pChunk, int ControlMsg, const CNetPacketConstruct &Packet, SECURITY_TOKEN &ResponseToken, SECURITY_TOKEN Token);
443 void OnPreConnMsg(NETADDR &Addr, CNetPacketConstruct &Packet);
444 void OnConnCtrlMsg(NETADDR &Addr, int ClientId, int ControlMsg, const CNetPacketConstruct &Packet);
445 bool ClientExists(const NETADDR &Addr) { return GetClientSlot(Addr) != -1; }
446 int GetClientSlot(const NETADDR &Addr);
447 void SendControl(NETADDR &Addr, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken);
448
449 int TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth = false, bool Sixup = false, SECURITY_TOKEN Token = 0);
450 int NumClientsWithAddr(NETADDR Addr);
451 bool Connlimit(NETADDR Addr);
452 void SendMsgs(NETADDR &Addr, const CPacker **ppMsgs, int Num);
453
454public:
455 int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
456 int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_NEWCLIENT_NOAUTH pfnNewClientNoAuth, NETFUNC_CLIENTREJOIN pfnClientRejoin, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
457
458 //
459 bool Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int MaxClientsPerIp);
460 void Close();
461
462 //
463 int Recv(CNetChunk *pChunk, SECURITY_TOKEN *pResponseToken);
464 int Send(CNetChunk *pChunk);
465 void Update();
466
467 //
468 void Drop(int ClientId, const char *pReason);
469
470 // status requests
471 const NETADDR *ClientAddr(int ClientId) const { return m_aSlots[ClientId].m_Connection.PeerAddress(); }
472 const std::array<char, NETADDR_MAXSTRSIZE> &ClientAddrString(int ClientId, bool IncludePort) const { return m_aSlots[ClientId].m_Connection.PeerAddressString(IncludePort); }
473 bool HasSecurityToken(int ClientId) const { return m_aSlots[ClientId].m_Connection.SecurityToken() != NET_SECURITY_TOKEN_UNSUPPORTED; }
474 NETADDR Address() const { return m_Address; }
475 NETSOCKET Socket() const { return m_Socket; }
476 CNetBan *NetBan() const { return m_pNetBan; }
477 int NetType() const { return net_socket_type(sock: m_Socket); }
478 int MaxClients() const { return m_MaxClients; }
479
480 void SendTokenSixup(NETADDR &Addr, SECURITY_TOKEN Token);
481
482 //
483 void SetMaxClientsPerIp(int Max);
484 bool HasErrored(int ClientId);
485 void ResumeOldConnection(int ClientId, int OrigId);
486 void IgnoreTimeouts(int ClientId);
487
488 void ResetErrorString(int ClientId);
489 const char *ErrorString(int ClientId);
490
491 // anti spoof
492 SECURITY_TOKEN GetGlobalToken();
493 SECURITY_TOKEN GetToken(const NETADDR &Addr);
494 SECURITY_TOKEN GetVanillaToken(const NETADDR &Addr);
495};
496
497class CNetConsole
498{
499 struct CSlot
500 {
501 CConsoleNetConnection m_Connection;
502 };
503
504 NETSOCKET m_Socket;
505 CNetBan *m_pNetBan;
506 CSlot m_aSlots[NET_MAX_CONSOLE_CLIENTS];
507
508 NETFUNC_NEWCLIENT_CON m_pfnNewClient;
509 NETFUNC_DELCLIENT m_pfnDelClient;
510 void *m_pUser;
511
512public:
513 void SetCallbacks(NETFUNC_NEWCLIENT_CON pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
514
515 //
516 bool Open(NETADDR BindAddr, CNetBan *pNetBan);
517 void Close();
518
519 //
520 int Recv(char *pLine, int MaxLength, int *pClientId = nullptr);
521 int Send(int ClientId, const char *pLine);
522 void Update();
523
524 //
525 int AcceptClient(NETSOCKET Socket, const NETADDR *pAddr);
526 void Drop(int ClientId, const char *pReason);
527
528 // status requests
529 const NETADDR *ClientAddr(int ClientId) const { return m_aSlots[ClientId].m_Connection.PeerAddress(); }
530 CNetBan *NetBan() const { return m_pNetBan; }
531};
532
533class CNetTokenCache
534{
535public:
536 void Init(NETSOCKET Socket);
537 void SendPacketConnless(CNetChunk *pChunk);
538 void FetchToken(NETADDR *pAddr);
539 void AddToken(const NETADDR *pAddr, TOKEN Token);
540 TOKEN GetToken(const NETADDR *pAddr);
541 TOKEN GenerateToken();
542 void Update();
543
544private:
545 class CConnlessPacketInfo
546 {
547 public:
548 NETADDR m_Addr;
549 int m_DataSize;
550 unsigned char m_aData[NET_MAX_PAYLOAD];
551 int64_t m_Expiry;
552 };
553
554 class CAddressInfo
555 {
556 public:
557 NETADDR m_Addr;
558 TOKEN m_Token;
559 int64_t m_Expiry;
560 };
561
562 NETSOCKET m_Socket;
563 std::vector<CAddressInfo> m_TokenCache;
564 std::vector<CConnlessPacketInfo> m_ConnlessPackets;
565};
566
567// client side
568class CNetClient
569{
570 CNetConnection m_Connection;
571 CPacketChunkUnpacker m_PacketChunkUnpacker;
572 CNetPacketConstruct m_RecvBuffer;
573 CNetTokenCache m_TokenCache;
574
575 CStun *m_pStun = nullptr;
576
577public:
578 NETSOCKET m_Socket = nullptr;
579 // openness
580 bool Open(NETADDR BindAddr);
581 void Close();
582
583 // connection state
584 void Disconnect(const char *pReason);
585 void Connect(const NETADDR *pAddr, int NumAddrs);
586 void Connect7(const NETADDR *pAddr, int NumAddrs);
587
588 // communication
589 int Recv(CNetChunk *pChunk, SECURITY_TOKEN *pResponseToken, bool Sixup);
590 int Send(CNetChunk *pChunk);
591
592 // pumping
593 void Update();
594 int Flush();
595
596 void ResetErrorString();
597
598 // error and state
599 int NetType() const { return net_socket_type(sock: m_Socket); }
600 int State();
601 const NETADDR *ServerAddress() const { return m_Connection.PeerAddress(); }
602 void ConnectAddresses(const NETADDR **ppAddrs, int *pNumAddrs) const { m_Connection.ConnectAddresses(ppAddrs, pNumAddrs); }
603 bool GotProblems(int64_t MaxLatency) const;
604 const char *ErrorString() const;
605
606 // stun
607 void FeedStunServer(NETADDR StunServer);
608 void RefreshStun();
609 CONNECTIVITY GetConnectivity(int NetType, NETADDR *pGlobalAddr);
610};
611
612// TODO: both, fix these. This feels like a junk class for stuff that doesn't fit anywhere
613class CNetBase
614{
615 static IOHANDLE ms_DataLogSent;
616 static IOHANDLE ms_DataLogRecv;
617 static CHuffman ms_Huffman;
618
619public:
620 static void OpenLog(IOHANDLE DataLogSent, IOHANDLE DataLogRecv);
621 static void CloseLog();
622 static void Init();
623 static int Compress(const void *pData, int DataSize, void *pOutput, int OutputSize);
624 static int Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize);
625
626 static bool IsValidConnectionOrientedPacket(const CNetPacketConstruct *pPacket);
627
628 static void SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken, bool Sixup = false);
629 static void SendControlMsgWithToken7(NETSOCKET Socket, NETADDR *pAddr, TOKEN Token, int Ack, int ControlMsg, TOKEN MyToken, bool Extended);
630 static void SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize, bool Extended, unsigned char aExtra[NET_CONNLESS_EXTRA_SIZE]);
631 static void SendPacketConnlessWithToken7(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize, SECURITY_TOKEN Token, SECURITY_TOKEN ResponseToken);
632 static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken, bool Sixup = false);
633
634 static std::optional<int> UnpackPacketFlags(unsigned char *pBuffer, int Size);
635 static int UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket, bool &Sixup, SECURITY_TOKEN *pSecurityToken = nullptr, SECURITY_TOKEN *pResponseToken = nullptr);
636
637 // The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not
638 static bool IsSeqInBackroom(int Seq, int Ack);
639};
640
641#endif
642