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#include "network.h"
4
5#include "config.h"
6#include "huffman.h"
7
8#include <base/bytes.h>
9#include <base/dbg.h>
10#include <base/io.h>
11#include <base/mem.h>
12#include <base/net.h>
13#include <base/secure.h>
14#include <base/time.h>
15#include <base/types.h>
16
17#include <engine/shared/protocolglue.h>
18
19const unsigned char SECURITY_TOKEN_MAGIC[4] = {'T', 'K', 'E', 'N'};
20
21SECURITY_TOKEN ToSecurityToken(const unsigned char *pData)
22{
23 return bytes_be_to_uint(bytes: pData);
24}
25
26void WriteSecurityToken(unsigned char *pData, SECURITY_TOKEN Token)
27{
28 uint_to_bytes_be(bytes: pData, value: Token);
29}
30
31void CNetChunk::AssertSizeSanity() const
32{
33 if(m_Flags & NETSENDFLAG_CONNLESS)
34 {
35 dbg_assert(m_DataSize <= NET_MAX_CONNLESS_PAYLOAD, "connless packet too large, size=%d", m_DataSize);
36 }
37 else
38 {
39 dbg_assert(m_DataSize <= NET_MAX_CHUNK_SIZE, "chunk too large, size=%d", m_DataSize);
40 }
41}
42
43void CPacketChunkUnpacker::FeedPacket(const NETADDR &Addr, const CNetPacketConstruct &Packet, CNetConnection *pConnection, int ClientId)
44{
45 dbg_assert(!m_Valid, "Chunk unpacker is already unpacking");
46 m_Valid = true;
47 m_Addr = Addr;
48 m_pConnection = pConnection;
49 m_ClientId = ClientId;
50 m_CurrentChunk = 0;
51 m_Data = Packet;
52 dbg_assert((m_Data.m_Flags & (NET_PACKETFLAG_CONNLESS | NET_PACKETFLAG_CONTROL)) == 0 && m_Data.m_DataSize > 0 && m_Data.m_NumChunks > 0,
53 "Invalid packet for chunk unpacker: flags=%d size=%d chunks=%d", m_Data.m_Flags, m_Data.m_DataSize, m_Data.m_NumChunks);
54}
55
56bool CPacketChunkUnpacker::UnpackNextChunk(CNetChunk *pChunk)
57{
58 if(!m_Valid)
59 {
60 return false;
61 }
62
63 const unsigned char *const pEnd = m_Data.m_aChunkData + m_Data.m_DataSize;
64
65 while(true)
66 {
67 if(m_CurrentChunk >= m_Data.m_NumChunks)
68 {
69 m_Valid = false;
70 return false;
71 }
72
73 unsigned char *pData = m_Data.m_aChunkData;
74
75 // TODO: add checking here so we don't read too far
76 const int HeaderSplit = m_pConnection->m_Sixup ? 6 : 4;
77 for(int i = 0; i < m_CurrentChunk; i++)
78 {
79 CNetChunkHeader SkippedHeader;
80 pData = SkippedHeader.Unpack(pData, Split: HeaderSplit);
81 pData += SkippedHeader.m_Size;
82 }
83
84 // unpack the header
85 CNetChunkHeader Header;
86 pData = Header.Unpack(pData, Split: HeaderSplit);
87 m_CurrentChunk++;
88
89 if(pData + Header.m_Size > pEnd)
90 {
91 m_Valid = false;
92 return false;
93 }
94
95 // handle sequence stuff
96 if((Header.m_Flags & NET_CHUNKFLAG_VITAL) != 0)
97 {
98 // anti spoof: ignore unknown sequence
99 if(Header.m_Sequence == (m_pConnection->m_Ack + 1) % NET_MAX_SEQUENCE || m_pConnection->m_UnknownSeq)
100 {
101 m_pConnection->m_UnknownSeq = false;
102
103 // in sequence
104 m_pConnection->m_Ack = Header.m_Sequence;
105 }
106 else
107 {
108 // old packet that we already got
109 if(CNetBase::IsSeqInBackroom(Seq: Header.m_Sequence, Ack: m_pConnection->m_Ack))
110 continue;
111
112 // out of sequence, request resend
113 if(g_Config.m_Debug)
114 dbg_msg(sys: "conn", fmt: "asking for resend %d %d", Header.m_Sequence, (m_pConnection->m_Ack + 1) % NET_MAX_SEQUENCE);
115 m_pConnection->SignalResend();
116 continue; // take the next chunk in the packet
117 }
118 }
119
120 // fill in the info
121 pChunk->m_ClientId = m_ClientId;
122 pChunk->m_Address = m_Addr;
123 pChunk->m_Flags = Header.m_Flags;
124 pChunk->m_DataSize = Header.m_Size;
125 pChunk->m_pData = pData;
126 return true;
127 }
128}
129
130void CPacketChunkUnpacker::Reset()
131{
132 m_Valid = false;
133}
134
135bool CNetBase::IsValidConnectionOrientedPacket(const CNetPacketConstruct *pPacket)
136{
137 if((pPacket->m_Flags & ~(NET_PACKETFLAG_CONTROL | NET_PACKETFLAG_RESEND | NET_PACKETFLAG_COMPRESSION)) != 0)
138 {
139 return false;
140 }
141
142 if((pPacket->m_Flags & NET_PACKETFLAG_CONTROL) != 0)
143 {
144 // At least one byte is required as the control message code in control packets.
145 // Control packets always contain zero chunks and are never compressed.
146 return pPacket->m_NumChunks == 0 &&
147 pPacket->m_DataSize > 0 &&
148 (pPacket->m_Flags & NET_PACKETFLAG_COMPRESSION) == 0;
149 }
150
151 // Packets are allowed to contain no chunks if they are used to request a resend,
152 // otherwise at least one chunk is required or the packet would have no effect.
153 const int MinChunks = (pPacket->m_Flags & NET_PACKETFLAG_RESEND) != 0 ? 0 : 1;
154 return pPacket->m_NumChunks >= MinChunks &&
155 pPacket->m_NumChunks <= NET_MAX_PACKET_CHUNKS;
156}
157
158static const unsigned char NET_HEADER_EXTENDED[] = {'x', 'e'};
159// packs the data tight and sends it
160void CNetBase::SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize, bool Extended, unsigned char aExtra[NET_CONNLESS_EXTRA_SIZE])
161{
162 unsigned char aBuffer[NET_MAX_PACKETSIZE];
163 static constexpr int DATA_OFFSET = sizeof(NET_HEADER_EXTENDED) + NET_CONNLESS_EXTRA_SIZE;
164 dbg_assert(DataSize <= (int)sizeof(aBuffer) - DATA_OFFSET,
165 "Invalid DataSize for CNetBase::SendPacketConnless: %d > %d", DataSize, (int)sizeof(aBuffer) - DATA_OFFSET);
166
167 if(Extended)
168 {
169 mem_copy(dest: aBuffer, source: NET_HEADER_EXTENDED, size: sizeof(NET_HEADER_EXTENDED));
170 mem_copy(dest: aBuffer + sizeof(NET_HEADER_EXTENDED), source: aExtra, size: NET_CONNLESS_EXTRA_SIZE);
171 }
172 else
173 {
174 std::fill(first: aBuffer, last: aBuffer + DATA_OFFSET, value: 0xFF);
175 }
176 mem_copy(dest: aBuffer + DATA_OFFSET, source: pData, size: DataSize);
177 net_udp_send(sock: Socket, addr: pAddr, data: aBuffer, size: DataSize + DATA_OFFSET);
178}
179
180void CNetBase::SendPacketConnlessWithToken7(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize, SECURITY_TOKEN Token, SECURITY_TOKEN ResponseToken)
181{
182 unsigned char aBuffer[NET_MAX_PACKETSIZE];
183 static constexpr int DATA_OFFSET = 1 + 2 * sizeof(SECURITY_TOKEN);
184 dbg_assert(DataSize <= (int)sizeof(aBuffer) - DATA_OFFSET,
185 "Invalid DataSize for CNetBase::SendPacketConnlessWithToken7: %d > %d", DataSize, (int)sizeof(aBuffer) - DATA_OFFSET);
186
187 aBuffer[0] = (NET_PACKETFLAG_CONNLESS << 2) | 1;
188 WriteSecurityToken(pData: aBuffer + 1, Token);
189 WriteSecurityToken(pData: aBuffer + 1 + sizeof(SECURITY_TOKEN), Token: ResponseToken);
190 mem_copy(dest: aBuffer + DATA_OFFSET, source: pData, size: DataSize);
191 net_udp_send(sock: Socket, addr: pAddr, data: aBuffer, size: DataSize + DATA_OFFSET);
192}
193
194void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken, bool Sixup)
195{
196 dbg_assert(IsValidConnectionOrientedPacket(pPacket), "Invalid packet to send. Flags=%d Ack=%d NumChunks=%d Size=%d",
197 pPacket->m_Flags, pPacket->m_Ack, pPacket->m_NumChunks, pPacket->m_DataSize);
198 dbg_assert((pPacket->m_Flags & NET_PACKETFLAG_COMPRESSION) == 0, "Do not set NET_PACKETFLAG_COMPRESSION, it will be set automatically when appropriate");
199
200 unsigned char aBuffer[NET_MAX_PACKETSIZE];
201
202 // log the data
203 if(ms_DataLogSent)
204 {
205 int Type = 1;
206 io_write(io: ms_DataLogSent, buffer: &Type, size: sizeof(Type));
207 io_write(io: ms_DataLogSent, buffer: &pPacket->m_DataSize, size: sizeof(pPacket->m_DataSize));
208 io_write(io: ms_DataLogSent, buffer: &pPacket->m_aChunkData, size: pPacket->m_DataSize);
209 io_flush(io: ms_DataLogSent);
210 }
211
212 int HeaderSize = NET_PACKETHEADERSIZE;
213 if(Sixup)
214 {
215 HeaderSize += sizeof(SecurityToken);
216 WriteSecurityToken(pData: aBuffer + 3, Token: SecurityToken);
217 }
218 else if(SecurityToken != NET_SECURITY_TOKEN_UNSUPPORTED)
219 {
220 // append security token
221 // if SecurityToken is NET_SECURITY_TOKEN_UNKNOWN we will still append it hoping to negotiate it
222 WriteSecurityToken(pData: pPacket->m_aChunkData + pPacket->m_DataSize, Token: SecurityToken);
223 pPacket->m_DataSize += sizeof(SecurityToken);
224 }
225
226 // only compress non-control packets
227 int CompressedSize = -1;
228 if((pPacket->m_Flags & NET_PACKETFLAG_CONTROL) == 0)
229 {
230 CompressedSize = ms_Huffman.Compress(pInput: pPacket->m_aChunkData, InputSize: pPacket->m_DataSize, pOutput: &aBuffer[HeaderSize], OutputSize: NET_MAX_PACKETSIZE - HeaderSize);
231 }
232
233 // check if the compression was enabled, successful and good enough
234 int FinalSize;
235 if(CompressedSize > 0 && CompressedSize < pPacket->m_DataSize)
236 {
237 FinalSize = CompressedSize;
238 pPacket->m_Flags |= NET_PACKETFLAG_COMPRESSION;
239 }
240 else
241 {
242 // use uncompressed data
243 FinalSize = pPacket->m_DataSize;
244 mem_copy(dest: &aBuffer[HeaderSize], source: pPacket->m_aChunkData, size: pPacket->m_DataSize);
245 }
246
247 if(Sixup)
248 {
249 pPacket->m_Flags = PacketFlags_SixToSeven(Flags: pPacket->m_Flags);
250 }
251
252 // set header and send the packet if all things are good
253 if(FinalSize >= 0)
254 {
255 FinalSize += HeaderSize;
256 aBuffer[0] = ((pPacket->m_Flags << 2) & 0xfc) | ((pPacket->m_Ack >> 8) & 0x3);
257 aBuffer[1] = pPacket->m_Ack & 0xff;
258 aBuffer[2] = pPacket->m_NumChunks;
259 net_udp_send(sock: Socket, addr: pAddr, data: aBuffer, size: FinalSize);
260
261 // log raw socket data
262 if(ms_DataLogSent)
263 {
264 int Type = 0;
265 io_write(io: ms_DataLogSent, buffer: &Type, size: sizeof(Type));
266 io_write(io: ms_DataLogSent, buffer: &FinalSize, size: sizeof(FinalSize));
267 io_write(io: ms_DataLogSent, buffer: aBuffer, size: FinalSize);
268 io_flush(io: ms_DataLogSent);
269 }
270 }
271}
272
273std::optional<int> CNetBase::UnpackPacketFlags(unsigned char *pBuffer, int Size)
274{
275 if(Size < NET_PACKETHEADERSIZE || Size > NET_MAX_PACKETSIZE)
276 {
277 return std::nullopt;
278 }
279 return pBuffer[0] >> 2;
280}
281
282// TODO: rename this function
283int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket, bool &Sixup, SECURITY_TOKEN *pSecurityToken, SECURITY_TOKEN *pResponseToken)
284{
285 if(pResponseToken != nullptr)
286 {
287 *pResponseToken = NET_SECURITY_TOKEN_UNKNOWN;
288 }
289
290 std::optional<int> Flags = UnpackPacketFlags(pBuffer, Size);
291 if(!Flags)
292 {
293 return -1;
294 }
295
296 // log the data
297 if(ms_DataLogRecv)
298 {
299 int Type = 0;
300 io_write(io: ms_DataLogRecv, buffer: &Type, size: sizeof(Type));
301 io_write(io: ms_DataLogRecv, buffer: &Size, size: sizeof(Size));
302 io_write(io: ms_DataLogRecv, buffer: pBuffer, size: Size);
303 io_flush(io: ms_DataLogRecv);
304 }
305
306 // read the packet
307 pPacket->m_Flags = *Flags;
308
309 if(pPacket->m_Flags & NET_PACKETFLAG_CONNLESS)
310 {
311 Sixup = (pBuffer[0] & 0x3) == 1;
312 if(Sixup && (pSecurityToken == nullptr || pResponseToken == nullptr))
313 return -1;
314 int Offset = Sixup ? 9 : 6;
315 if(Size < Offset)
316 return -1;
317
318 if(Sixup)
319 {
320 *pSecurityToken = ToSecurityToken(pData: pBuffer + 1);
321 *pResponseToken = ToSecurityToken(pData: pBuffer + 5);
322 }
323
324 pPacket->m_Flags = NET_PACKETFLAG_CONNLESS;
325 pPacket->m_Ack = 0;
326 pPacket->m_NumChunks = 0;
327 pPacket->m_DataSize = Size - Offset;
328 dbg_assert((size_t)pPacket->m_DataSize <= sizeof(pPacket->m_aChunkData), "invalid packet size reached mem_copy, size=%d", pPacket->m_DataSize);
329 mem_copy(dest: pPacket->m_aChunkData, source: pBuffer + Offset, size: pPacket->m_DataSize);
330
331 if(!Sixup && mem_comp(a: pBuffer, b: NET_HEADER_EXTENDED, size: sizeof(NET_HEADER_EXTENDED)) == 0)
332 {
333 pPacket->m_Flags |= NET_PACKETFLAG_EXTENDED;
334 mem_copy(dest: pPacket->m_aExtraData, source: pBuffer + sizeof(NET_HEADER_EXTENDED), size: sizeof(pPacket->m_aExtraData));
335 }
336 }
337 else
338 {
339 if(pPacket->m_Flags & NET_PACKETFLAG_UNUSED)
340 Sixup = true;
341 if(Sixup && pSecurityToken == nullptr)
342 return -1;
343 int DataStart = Sixup ? 7 : NET_PACKETHEADERSIZE;
344 if(Size < DataStart)
345 return -1;
346
347 pPacket->m_Ack = ((pBuffer[0] & 0x3) << 8) | pBuffer[1];
348 pPacket->m_NumChunks = pBuffer[2];
349 pPacket->m_DataSize = Size - DataStart;
350
351 if(Sixup)
352 {
353 pPacket->m_Flags = PacketFlags_SevenToSix(Flags: pPacket->m_Flags);
354 *pSecurityToken = ToSecurityToken(pData: pBuffer + 3);
355 }
356
357 if(!IsValidConnectionOrientedPacket(pPacket))
358 {
359 return -1;
360 }
361
362 if((pPacket->m_Flags & NET_PACKETFLAG_COMPRESSION) != 0)
363 {
364 pPacket->m_DataSize = ms_Huffman.Decompress(pInput: &pBuffer[DataStart], InputSize: pPacket->m_DataSize, pOutput: pPacket->m_aChunkData, OutputSize: sizeof(pPacket->m_aChunkData));
365 if(pPacket->m_DataSize < 0)
366 {
367 return -1;
368 }
369 }
370 else
371 {
372 dbg_assert((size_t)pPacket->m_DataSize <= sizeof(pPacket->m_aChunkData), "invalid packet size reached mem_copy, size=%d", pPacket->m_DataSize);
373 mem_copy(dest: pPacket->m_aChunkData, source: &pBuffer[DataStart], size: pPacket->m_DataSize);
374 }
375 }
376
377 // set the response token (a bit hacky because this function shouldn't know about control packets)
378 if(pPacket->m_Flags & NET_PACKETFLAG_CONTROL)
379 {
380 if(pPacket->m_DataSize >= 1 + (int)sizeof(SECURITY_TOKEN)) // control byte + token
381 {
382 if(pPacket->m_aChunkData[0] == NET_CTRLMSG_CONNECT || (Sixup && pPacket->m_aChunkData[0] == protocol7::NET_CTRLMSG_TOKEN))
383 {
384 *pResponseToken = ToSecurityToken(pData: &pPacket->m_aChunkData[1]);
385 }
386 }
387 }
388
389 // log the data
390 if(ms_DataLogRecv)
391 {
392 int Type = 1;
393 io_write(io: ms_DataLogRecv, buffer: &Type, size: sizeof(Type));
394 io_write(io: ms_DataLogRecv, buffer: &pPacket->m_DataSize, size: sizeof(pPacket->m_DataSize));
395 io_write(io: ms_DataLogRecv, buffer: pPacket->m_aChunkData, size: pPacket->m_DataSize);
396 io_flush(io: ms_DataLogRecv);
397 }
398
399 // return success
400 return 0;
401}
402
403void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken, bool Sixup)
404{
405 CNetPacketConstruct Construct;
406 Construct.m_Flags = NET_PACKETFLAG_CONTROL;
407 Construct.m_Ack = Ack;
408 Construct.m_NumChunks = 0;
409 Construct.m_DataSize = 1 + ExtraSize;
410 Construct.m_aChunkData[0] = ControlMsg;
411 if(pExtra)
412 mem_copy(dest: &Construct.m_aChunkData[1], source: pExtra, size: ExtraSize);
413
414 CNetBase::SendPacket(Socket, pAddr, pPacket: &Construct, SecurityToken, Sixup);
415}
416
417void CNetBase::SendControlMsgWithToken7(NETSOCKET Socket, NETADDR *pAddr, TOKEN Token, int Ack, int ControlMsg, TOKEN MyToken, bool Extended)
418{
419 dbg_assert((Token & ~NET_TOKEN_MASK) == 0, "token out of range");
420 dbg_assert((MyToken & ~NET_TOKEN_MASK) == 0, "resp token out of range");
421
422 unsigned char aRequestTokenBuf[NET_TOKENREQUEST_DATASIZE] = {};
423 aRequestTokenBuf[0] = (MyToken >> 24) & 0xff;
424 aRequestTokenBuf[1] = (MyToken >> 16) & 0xff;
425 aRequestTokenBuf[2] = (MyToken >> 8) & 0xff;
426 aRequestTokenBuf[3] = (MyToken) & 0xff;
427 const int Size = Extended ? sizeof(aRequestTokenBuf) : sizeof(TOKEN);
428 CNetBase::SendControlMsg(Socket, pAddr, Ack, ControlMsg, pExtra: aRequestTokenBuf, ExtraSize: Size, SecurityToken: Token, Sixup: true);
429}
430
431unsigned char *CNetChunkHeader::Pack(unsigned char *pData, int Split) const
432{
433 dbg_assert(m_Size >= 0 && m_Size < 1 << (Split + 6), "Invalid network chunk size: %d", m_Size);
434
435 pData[0] = ((m_Flags & 3) << 6) | ((m_Size >> Split) & 0x3f);
436 pData[1] = (m_Size & ((1 << Split) - 1));
437 if(m_Flags & NET_CHUNKFLAG_VITAL)
438 {
439 pData[1] |= (m_Sequence >> 2) & (~((1 << Split) - 1));
440 pData[2] = m_Sequence & 0xff;
441 return pData + 3;
442 }
443 return pData + 2;
444}
445
446unsigned char *CNetChunkHeader::Unpack(unsigned char *pData, int Split)
447{
448 m_Flags = (pData[0] >> 6) & 3;
449 m_Size = ((pData[0] & 0x3f) << Split) | (pData[1] & ((1 << Split) - 1));
450 m_Sequence = -1;
451 if(m_Flags & NET_CHUNKFLAG_VITAL)
452 {
453 m_Sequence = ((pData[1] & (~((1 << Split) - 1))) << 2) | pData[2];
454 return pData + 3;
455 }
456 return pData + 2;
457}
458
459bool CNetBase::IsSeqInBackroom(int Seq, int Ack)
460{
461 int Bottom = (Ack - NET_MAX_SEQUENCE / 2);
462 if(Bottom < 0)
463 {
464 if(Seq <= Ack)
465 return true;
466 if(Seq >= (Bottom + NET_MAX_SEQUENCE))
467 return true;
468 }
469 else
470 {
471 if(Seq <= Ack && Seq >= Bottom)
472 return true;
473 }
474
475 return false;
476}
477
478IOHANDLE CNetBase::ms_DataLogSent = nullptr;
479IOHANDLE CNetBase::ms_DataLogRecv = nullptr;
480CHuffman CNetBase::ms_Huffman;
481
482void CNetBase::OpenLog(IOHANDLE DataLogSent, IOHANDLE DataLogRecv)
483{
484 if(DataLogSent)
485 {
486 ms_DataLogSent = DataLogSent;
487 dbg_msg(sys: "network", fmt: "logging sent packages");
488 }
489 else
490 {
491 dbg_msg(sys: "network", fmt: "failed to start logging sent packages");
492 }
493
494 if(DataLogRecv)
495 {
496 ms_DataLogRecv = DataLogRecv;
497 dbg_msg(sys: "network", fmt: "logging recv packages");
498 }
499 else
500 {
501 dbg_msg(sys: "network", fmt: "failed to start logging recv packages");
502 }
503}
504
505void CNetBase::CloseLog()
506{
507 if(ms_DataLogSent)
508 {
509 dbg_msg(sys: "network", fmt: "stopped logging sent packages");
510 io_close(io: ms_DataLogSent);
511 ms_DataLogSent = nullptr;
512 }
513
514 if(ms_DataLogRecv)
515 {
516 dbg_msg(sys: "network", fmt: "stopped logging recv packages");
517 io_close(io: ms_DataLogRecv);
518 ms_DataLogRecv = nullptr;
519 }
520}
521
522int CNetBase::Compress(const void *pData, int DataSize, void *pOutput, int OutputSize)
523{
524 return ms_Huffman.Compress(pInput: pData, InputSize: DataSize, pOutput, OutputSize);
525}
526
527int CNetBase::Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize)
528{
529 return ms_Huffman.Decompress(pInput: pData, InputSize: DataSize, pOutput, OutputSize);
530}
531
532void CNetBase::Init()
533{
534 ms_Huffman.Init();
535}
536
537void CNetTokenCache::Init(NETSOCKET Socket)
538{
539 m_Socket = Socket;
540}
541
542void CNetTokenCache::SendPacketConnless(CNetChunk *pChunk)
543{
544 TOKEN Token = GetToken(pAddr: &pChunk->m_Address);
545
546 if(Token != NET_TOKEN_NONE)
547 {
548 CNetBase::SendPacketConnlessWithToken7(Socket: m_Socket, pAddr: &pChunk->m_Address, pData: pChunk->m_pData, DataSize: pChunk->m_DataSize, Token, ResponseToken: GenerateToken());
549 }
550 else
551 {
552 FetchToken(pAddr: &pChunk->m_Address);
553
554 CConnlessPacketInfo ConnlessPacket;
555 ConnlessPacket.m_Addr = pChunk->m_Address;
556 ConnlessPacket.m_Addr.type = pChunk->m_Address.type & ~(NETTYPE_IPV4 | NETTYPE_IPV6);
557 mem_copy(dest: ConnlessPacket.m_aData, source: pChunk->m_pData, size: pChunk->m_DataSize);
558 ConnlessPacket.m_DataSize = pChunk->m_DataSize;
559 ConnlessPacket.m_Expiry = time_get() + time_freq() * NET_TOKENCACHE_PACKETEXPIRY;
560
561 unsigned int NetType = pChunk->m_Address.type;
562 auto SavePacketFor = [&](unsigned int Type) {
563 if(NetType & Type)
564 {
565 ConnlessPacket.m_Addr.type |= Type;
566 m_ConnlessPackets.push_back(x: ConnlessPacket);
567 ConnlessPacket.m_Addr.type &= ~Type;
568 }
569 };
570
571 SavePacketFor(NETTYPE_IPV4);
572 SavePacketFor(NETTYPE_IPV6);
573 }
574}
575
576void CNetTokenCache::FetchToken(NETADDR *pAddr)
577{
578 CNetBase::SendControlMsgWithToken7(Socket: m_Socket, pAddr, Token: NET_TOKEN_NONE, Ack: 0, ControlMsg: protocol7::NET_CTRLMSG_TOKEN, MyToken: GenerateToken(), Extended: true);
579}
580
581void CNetTokenCache::AddToken(const NETADDR *pAddr, TOKEN Token)
582{
583 if(Token == NET_TOKEN_NONE)
584 return;
585
586 NETADDR NullAddr = NETADDR_ZEROED;
587 NullAddr.port = pAddr->port;
588 NullAddr.type = (pAddr->type & ~(NETTYPE_WEBSOCKET_IPV4 | NETTYPE_WEBSOCKET_IPV6)) | NETTYPE_LINK_BROADCAST;
589
590 for(auto Iter = m_ConnlessPackets.begin(); Iter != m_ConnlessPackets.end();)
591 {
592 if(Iter->m_Addr == NullAddr)
593 {
594 CNetBase::SendPacketConnlessWithToken7(Socket: m_Socket, pAddr: &Iter->m_Addr, pData: Iter->m_aData, DataSize: Iter->m_DataSize, Token, ResponseToken: GenerateToken());
595
596 Iter = m_ConnlessPackets.erase(position: Iter);
597 }
598 else
599 {
600 Iter++;
601 }
602 }
603
604 CAddressInfo Info;
605 Info.m_Addr = *pAddr,
606 Info.m_Token = Token,
607 Info.m_Expiry = time_get() + (time_freq() * NET_TOKENCACHE_ADDRESSEXPIRY);
608
609 m_TokenCache.push_back(x: Info);
610}
611
612TOKEN CNetTokenCache::GetToken(const NETADDR *pAddr)
613{
614 for(const auto &AddrInfo : m_TokenCache)
615 {
616 if(AddrInfo.m_Addr == *pAddr)
617 {
618 return AddrInfo.m_Token;
619 }
620 }
621
622 return NET_TOKEN_NONE;
623}
624
625TOKEN CNetTokenCache::GenerateToken()
626{
627 TOKEN Token;
628 secure_random_fill(bytes: &Token, length: sizeof(Token));
629 return Token;
630}
631
632void CNetTokenCache::Update()
633{
634 int64_t Now = time_get();
635
636 m_TokenCache.erase(
637 first: std::remove_if(first: m_TokenCache.begin(), last: m_TokenCache.end(), pred: [&](const CAddressInfo &Info) {
638 return Info.m_Expiry <= Now;
639 }),
640 last: m_TokenCache.end());
641
642 m_ConnlessPackets.erase(
643 first: std::remove_if(first: m_ConnlessPackets.begin(), last: m_ConnlessPackets.end(), pred: [&](CConnlessPacketInfo &Packet) {
644 return Packet.m_Expiry <= Now;
645 }),
646 last: m_ConnlessPackets.end());
647}
648