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