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