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