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 "config.h"
4#include "network.h"
5
6#include <base/log.h>
7#include <base/system.h>
8
9void CNetConnection::SetPeerAddr(const NETADDR *pAddr)
10{
11 m_PeerAddr = *pAddr;
12 net_addr_str(addr: pAddr, string: m_aPeerAddrStr.data(), max_length: m_aPeerAddrStr.size(), add_port: true);
13 net_addr_str(addr: pAddr, string: m_aPeerAddrStrNoPort.data(), max_length: m_aPeerAddrStrNoPort.size(), add_port: false);
14}
15
16void CNetConnection::ClearPeerAddr()
17{
18 mem_zero(block: &m_PeerAddr, size: sizeof(m_PeerAddr));
19 m_aPeerAddrStr[0] = '\0';
20 m_aPeerAddrStrNoPort[0] = '\0';
21}
22
23void CNetConnection::ResetStats()
24{
25 m_Stats = {};
26 ClearPeerAddr();
27 m_LastUpdateTime = 0;
28}
29
30void CNetConnection::Reset(bool Rejoin)
31{
32 m_Sequence = 0;
33 m_Ack = 0;
34 m_PeerAck = 0;
35 m_RemoteClosed = 0;
36
37 if(!Rejoin)
38 {
39 m_TimeoutProtected = false;
40 m_TimeoutSituation = false;
41
42 m_State = EState::OFFLINE;
43 m_Token = -1;
44 m_SecurityToken = NET_SECURITY_TOKEN_UNKNOWN;
45 m_Sixup = false;
46 }
47
48 m_LastSendTime = 0;
49 m_LastRecvTime = 0;
50
51 mem_zero(block: &m_aConnectAddrs, size: sizeof(m_aConnectAddrs));
52 m_NumConnectAddrs = 0;
53 m_UnknownSeq = false;
54
55 m_Buffer.Init();
56
57 mem_zero(block: &m_Construct, size: sizeof(m_Construct));
58}
59
60const char *CNetConnection::ErrorString()
61{
62 return m_aErrorString;
63}
64
65void CNetConnection::SetError(const char *pString)
66{
67 str_copy(dst&: m_aErrorString, src: pString);
68}
69
70void CNetConnection::Init(NETSOCKET Socket, bool BlockCloseMsg)
71{
72 Reset();
73 ResetStats();
74
75 m_Socket = Socket;
76 m_BlockCloseMsg = BlockCloseMsg;
77 m_aErrorString[0] = '\0';
78}
79
80void CNetConnection::AckChunks(int Ack)
81{
82 while(true)
83 {
84 CNetChunkResend *pResend = m_Buffer.First();
85 if(!pResend)
86 break;
87
88 if(CNetBase::IsSeqInBackroom(Seq: pResend->m_Sequence, Ack))
89 m_Buffer.PopFirst();
90 else
91 break;
92 }
93}
94
95void CNetConnection::SignalResend()
96{
97 m_Construct.m_Flags |= NET_PACKETFLAG_RESEND;
98}
99
100int CNetConnection::Flush()
101{
102 // Only flush the connection if there is at least one chunk to flush,
103 // or if a resend should be signaled.
104 const int NumChunks = m_Construct.m_NumChunks;
105 if(!NumChunks && (m_Construct.m_Flags & NET_PACKETFLAG_RESEND) == 0)
106 {
107 return 0;
108 }
109
110 // send of the packets
111 m_Construct.m_Ack = m_Ack;
112 CNetBase::SendPacket(Socket: m_Socket, pAddr: &m_PeerAddr, pPacket: &m_Construct, SecurityToken: m_SecurityToken, Sixup: m_Sixup);
113
114 // update send times
115 m_LastSendTime = time_get();
116
117 // clear construct so we can start building a new package
118 mem_zero(block: &m_Construct, size: sizeof(m_Construct));
119 return NumChunks;
120}
121
122int CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence)
123{
124 if(m_State == EState::OFFLINE || m_State == EState::ERROR)
125 return -1;
126
127 unsigned char *pChunkData;
128
129 // check if we have space for it, if not, flush the connection
130 if(m_Construct.m_DataSize + DataSize + NET_MAX_CHUNKHEADERSIZE > (int)sizeof(m_Construct.m_aChunkData) - (int)sizeof(SECURITY_TOKEN) ||
131 m_Construct.m_NumChunks == NET_MAX_PACKET_CHUNKS)
132 {
133 Flush();
134 }
135
136 // pack all the data
137 CNetChunkHeader Header;
138 Header.m_Flags = Flags;
139 Header.m_Size = DataSize;
140 Header.m_Sequence = Sequence;
141 pChunkData = &m_Construct.m_aChunkData[m_Construct.m_DataSize];
142 pChunkData = Header.Pack(pData: pChunkData, Split: m_Sixup ? 6 : 4);
143 mem_copy(dest: pChunkData, source: pData, size: DataSize);
144 pChunkData += DataSize;
145
146 //
147 m_Construct.m_NumChunks++;
148 m_Construct.m_DataSize = (int)(pChunkData - m_Construct.m_aChunkData);
149
150 // set packet flags as well
151
152 if(Flags & NET_CHUNKFLAG_VITAL && !(Flags & NET_CHUNKFLAG_RESEND))
153 {
154 // save packet if we need to resend
155 CNetChunkResend *pResend = m_Buffer.Allocate(Size: sizeof(CNetChunkResend) + DataSize);
156 if(pResend)
157 {
158 pResend->m_Sequence = Sequence;
159 pResend->m_Flags = Flags;
160 pResend->m_DataSize = DataSize;
161 pResend->m_pData = (unsigned char *)(pResend + 1);
162 pResend->m_FirstSendTime = time_get();
163 pResend->m_LastSendTime = pResend->m_FirstSendTime;
164 mem_copy(dest: pResend->m_pData, source: pData, size: DataSize);
165 }
166 else
167 {
168 // out of buffer, don't save the packet and hope nobody will ask for resend
169 return -1;
170 }
171 }
172
173 return 0;
174}
175
176int CNetConnection::QueueChunk(int Flags, int DataSize, const void *pData)
177{
178 if(Flags & NET_CHUNKFLAG_VITAL)
179 m_Sequence = (m_Sequence + 1) % NET_MAX_SEQUENCE;
180 return QueueChunkEx(Flags, DataSize, pData, Sequence: m_Sequence);
181}
182
183void CNetConnection::SendConnect()
184{
185 // send the connect message
186 m_LastSendTime = time_get();
187 for(int i = 0; i < m_NumConnectAddrs; i++)
188 {
189 CNetBase::SendControlMsg(Socket: m_Socket, pAddr: &m_aConnectAddrs[i], Ack: m_Ack, ControlMsg: NET_CTRLMSG_CONNECT, pExtra: SECURITY_TOKEN_MAGIC, ExtraSize: sizeof(SECURITY_TOKEN_MAGIC), SecurityToken: m_SecurityToken, Sixup: m_Sixup);
190 }
191}
192
193void CNetConnection::SendControl(int ControlMsg, const void *pExtra, int ExtraSize)
194{
195 // send the control message
196 m_LastSendTime = time_get();
197 CNetBase::SendControlMsg(Socket: m_Socket, pAddr: &m_PeerAddr, Ack: m_Ack, ControlMsg, pExtra, ExtraSize, SecurityToken: m_SecurityToken, Sixup: m_Sixup);
198}
199
200void CNetConnection::ResendChunk(CNetChunkResend *pResend)
201{
202 QueueChunkEx(Flags: pResend->m_Flags | NET_CHUNKFLAG_RESEND, DataSize: pResend->m_DataSize, pData: pResend->m_pData, Sequence: pResend->m_Sequence);
203 pResend->m_LastSendTime = time_get();
204}
205
206void CNetConnection::Resend()
207{
208 for(CNetChunkResend *pResend = m_Buffer.First(); pResend; pResend = m_Buffer.Next(pCurrent: pResend))
209 ResendChunk(pResend);
210}
211
212int CNetConnection::Connect(const NETADDR *pAddr, int NumAddrs)
213{
214 if(State() != EState::OFFLINE)
215 return -1;
216
217 // init connection
218 Reset();
219 ClearPeerAddr();
220
221 for(int i = 0; i < NumAddrs; i++)
222 {
223 m_aConnectAddrs[i] = pAddr[i];
224 }
225 m_NumConnectAddrs = NumAddrs;
226 m_aErrorString[0] = '\0';
227 m_State = EState::CONNECT;
228 SendConnect();
229 return 0;
230}
231
232void CNetConnection::SendControlWithToken7(int ControlMsg, SECURITY_TOKEN ResponseToken)
233{
234 m_LastSendTime = time_get();
235
236 CNetBase::SendControlMsgWithToken7(Socket: m_Socket, pAddr: &m_PeerAddr, Token: ResponseToken, Ack: 0, ControlMsg, MyToken: m_Token, Extended: true);
237}
238
239int CNetConnection::Connect7(const NETADDR *pAddr, int NumAddrs)
240{
241 if(State() != EState::OFFLINE)
242 return -1;
243
244 // init connection
245 Reset();
246 for(int i = 0; i < NumAddrs; i++)
247 {
248 m_aConnectAddrs[i] = pAddr[i];
249 }
250 m_LastRecvTime = time_get();
251 m_NumConnectAddrs = NumAddrs;
252 SetPeerAddr(pAddr);
253 SetToken7(GenerateToken7(pPeerAddr: pAddr));
254 m_aErrorString[0] = '\0';
255 m_State = EState::WANT_TOKEN;
256 SendControlWithToken7(ControlMsg: protocol7::NET_CTRLMSG_TOKEN, ResponseToken: NET_TOKEN_NONE);
257 m_Sixup = true;
258 return 0;
259}
260
261void CNetConnection::SetToken7(TOKEN Token)
262{
263 if(State() != EState::OFFLINE)
264 return;
265
266 m_Token = Token;
267}
268
269TOKEN CNetConnection::GenerateToken7(const NETADDR *pPeerAddr)
270{
271 TOKEN Token;
272 secure_random_fill(bytes: &Token, length: sizeof(Token));
273 return Token;
274}
275
276void CNetConnection::Disconnect(const char *pReason)
277{
278 if(State() == EState::OFFLINE)
279 return;
280
281 if(m_RemoteClosed == 0)
282 {
283 if(!m_TimeoutSituation)
284 {
285 if(pReason)
286 SendControl(ControlMsg: NET_CTRLMSG_CLOSE, pExtra: pReason, ExtraSize: str_length(str: pReason) + 1);
287 else
288 SendControl(ControlMsg: NET_CTRLMSG_CLOSE, pExtra: nullptr, ExtraSize: 0);
289 }
290
291 if(pReason != m_aErrorString)
292 {
293 m_aErrorString[0] = 0;
294 if(pReason)
295 str_copy(dst&: m_aErrorString, src: pReason);
296 }
297 }
298
299 Reset();
300}
301
302void CNetConnection::DirectInit(const NETADDR &Addr, SECURITY_TOKEN SecurityToken, SECURITY_TOKEN Token, bool Sixup)
303{
304 Reset();
305
306 m_State = EState::ONLINE;
307
308 SetPeerAddr(&Addr);
309 m_aErrorString[0] = '\0';
310
311 int64_t Now = time_get();
312 m_LastSendTime = Now;
313 m_LastRecvTime = Now;
314 m_LastUpdateTime = Now;
315
316 m_SecurityToken = SecurityToken;
317 m_Token = Token;
318 m_Sixup = Sixup;
319}
320
321int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_TOKEN SecurityToken, SECURITY_TOKEN ResponseToken)
322{
323 // Disregard packets from the wrong address, unless we don't know our peer yet.
324 if(State() != EState::OFFLINE && State() != EState::CONNECT && *pAddr != m_PeerAddr)
325 {
326 return 0;
327 }
328
329 if(!m_Sixup && State() != EState::OFFLINE && m_SecurityToken != NET_SECURITY_TOKEN_UNKNOWN && m_SecurityToken != NET_SECURITY_TOKEN_UNSUPPORTED)
330 {
331 // supposed to have a valid token in this packet, check it
332 if(pPacket->m_DataSize < (int)sizeof(m_SecurityToken))
333 return 0;
334 pPacket->m_DataSize -= sizeof(m_SecurityToken);
335 if(m_SecurityToken != ToSecurityToken(pData: &pPacket->m_aChunkData[pPacket->m_DataSize]))
336 {
337 if(g_Config.m_Debug)
338 dbg_msg(sys: "security", fmt: "token mismatch, expected %d got %d", m_SecurityToken, ToSecurityToken(pData: &pPacket->m_aChunkData[pPacket->m_DataSize]));
339 return 0;
340 }
341 }
342
343 if(m_Sixup && SecurityToken != m_Token)
344 return 0;
345
346 // check if actual ack value is valid(own sequence..latest peer ack)
347 if(m_Sequence >= m_PeerAck)
348 {
349 if(pPacket->m_Ack < m_PeerAck || pPacket->m_Ack > m_Sequence)
350 return 0;
351 }
352 else
353 {
354 if(pPacket->m_Ack < m_PeerAck && pPacket->m_Ack > m_Sequence)
355 return 0;
356 }
357 m_PeerAck = pPacket->m_Ack;
358
359 int64_t Now = time_get();
360
361 // check if resend is requested
362 if(pPacket->m_Flags & NET_PACKETFLAG_RESEND)
363 Resend();
364
365 //
366 if(pPacket->m_Flags & NET_PACKETFLAG_CONTROL)
367 {
368 int CtrlMsg = pPacket->m_aChunkData[0];
369
370 if(CtrlMsg == NET_CTRLMSG_CLOSE)
371 {
372 bool IsPeer;
373 if(m_State != EState::CONNECT)
374 {
375 IsPeer = m_PeerAddr == *pAddr;
376 }
377 else
378 {
379 IsPeer = false;
380 for(int i = 0; i < m_NumConnectAddrs; i++)
381 {
382 if(m_aConnectAddrs[i] == *pAddr)
383 {
384 IsPeer = true;
385 break;
386 }
387 }
388 }
389 if(IsPeer)
390 {
391 m_State = EState::ERROR;
392 m_RemoteClosed = 1;
393
394 char aStr[256] = {0};
395 if(pPacket->m_DataSize > 1)
396 {
397 // make sure to sanitize the error string from the other party
398 str_copy(dst: aStr, src: (char *)&pPacket->m_aChunkData[1], dst_size: minimum(a: pPacket->m_DataSize, b: (int)sizeof(aStr)));
399 str_sanitize_cc(str: aStr);
400 }
401
402 if(!m_BlockCloseMsg)
403 {
404 // set the error string
405 SetError(aStr);
406 }
407
408 if(g_Config.m_Debug)
409 dbg_msg(sys: "conn", fmt: "closed reason='%s'", aStr);
410 }
411 return 0;
412 }
413 else
414 {
415 if(m_Sixup && CtrlMsg == protocol7::NET_CTRLMSG_TOKEN)
416 {
417 if(State() == EState::WANT_TOKEN)
418 {
419 m_LastRecvTime = Now;
420 m_State = EState::CONNECT;
421 m_SecurityToken = ResponseToken;
422 SendControlWithToken7(ControlMsg: NET_CTRLMSG_CONNECT, ResponseToken: m_SecurityToken);
423 if(g_Config.m_Debug)
424 {
425 log_debug("connection", "got token, replying, token=%x mytoken=%x", m_SecurityToken, m_Token);
426 }
427 }
428 else if(g_Config.m_Debug)
429 {
430 log_debug("connection", "got token, token=%x", ResponseToken);
431 }
432 }
433 else
434 {
435 if(State() == EState::OFFLINE)
436 {
437 if(CtrlMsg == NET_CTRLMSG_CONNECT)
438 {
439 if(net_addr_comp_noport(a: &m_PeerAddr, b: pAddr) == 0 && time_get() - m_LastUpdateTime < time_freq() * 3)
440 return 0;
441
442 // send response and init connection
443 Reset();
444 m_State = EState::PENDING;
445 SetPeerAddr(pAddr);
446 m_aErrorString[0] = '\0';
447 m_LastSendTime = Now;
448 m_LastRecvTime = Now;
449 m_LastUpdateTime = Now;
450 if(m_SecurityToken == NET_SECURITY_TOKEN_UNKNOWN && pPacket->m_DataSize >= (int)(1 + sizeof(SECURITY_TOKEN_MAGIC) + sizeof(m_SecurityToken)) && !mem_comp(a: &pPacket->m_aChunkData[1], b: SECURITY_TOKEN_MAGIC, size: sizeof(SECURITY_TOKEN_MAGIC)))
451 {
452 m_SecurityToken = NET_SECURITY_TOKEN_UNSUPPORTED;
453 if(g_Config.m_Debug)
454 dbg_msg(sys: "security", fmt: "generated token %d", m_SecurityToken);
455 }
456 else
457 {
458 if(g_Config.m_Debug)
459 dbg_msg(sys: "security", fmt: "token not supported by client (packet size %d)", pPacket->m_DataSize);
460 m_SecurityToken = NET_SECURITY_TOKEN_UNSUPPORTED;
461 }
462 SendControl(ControlMsg: NET_CTRLMSG_CONNECTACCEPT, pExtra: SECURITY_TOKEN_MAGIC, ExtraSize: sizeof(SECURITY_TOKEN_MAGIC));
463 if(g_Config.m_Debug)
464 dbg_msg(sys: "connection", fmt: "got connection, sending connect+accept");
465 }
466 }
467 else if(State() == EState::CONNECT)
468 {
469 // connection made
470 if(CtrlMsg == NET_CTRLMSG_CONNECTACCEPT)
471 {
472 SetPeerAddr(pAddr);
473 if(m_SecurityToken == NET_SECURITY_TOKEN_UNKNOWN && pPacket->m_DataSize >= (int)(1 + sizeof(SECURITY_TOKEN_MAGIC) + sizeof(m_SecurityToken)) && !mem_comp(a: &pPacket->m_aChunkData[1], b: SECURITY_TOKEN_MAGIC, size: sizeof(SECURITY_TOKEN_MAGIC)))
474 {
475 m_SecurityToken = ToSecurityToken(pData: &pPacket->m_aChunkData[1 + sizeof(SECURITY_TOKEN_MAGIC)]);
476 if(g_Config.m_Debug)
477 dbg_msg(sys: "security", fmt: "got token %d", m_SecurityToken);
478 }
479 else if(!IsSixup())
480 {
481 m_SecurityToken = NET_SECURITY_TOKEN_UNSUPPORTED;
482 if(g_Config.m_Debug)
483 dbg_msg(sys: "security", fmt: "token not supported by server");
484 }
485 if(!IsSixup())
486 SendControl(ControlMsg: NET_CTRLMSG_ACCEPT, pExtra: nullptr, ExtraSize: 0);
487 m_LastRecvTime = Now;
488 m_State = EState::ONLINE;
489 if(g_Config.m_Debug)
490 dbg_msg(sys: "connection", fmt: "got connect+accept, sending accept. connection online");
491 }
492 }
493 }
494 }
495 }
496 else
497 {
498 if(State() == EState::PENDING)
499 {
500 m_LastRecvTime = Now;
501 m_State = EState::ONLINE;
502 if(g_Config.m_Debug)
503 dbg_msg(sys: "connection", fmt: "connecting online");
504 }
505 }
506
507 if(State() == EState::ONLINE)
508 {
509 m_LastRecvTime = Now;
510 AckChunks(Ack: pPacket->m_Ack);
511 }
512
513 return 1;
514}
515
516int CNetConnection::Update()
517{
518 int64_t Now = time_get();
519
520 if(State() == EState::ERROR && m_TimeoutSituation && (Now - m_LastRecvTime) > time_freq() * g_Config.m_ConnTimeoutProtection)
521 {
522 m_TimeoutSituation = false;
523 SetError("Timeout Protection over");
524 }
525
526 if(State() == EState::OFFLINE || State() == EState::ERROR)
527 return 0;
528
529 m_TimeoutSituation = false;
530
531 // check for timeout
532 if(State() != EState::OFFLINE &&
533 State() != EState::CONNECT &&
534 (Now - m_LastRecvTime) > time_freq() * g_Config.m_ConnTimeout)
535 {
536 m_State = EState::ERROR;
537 SetError("Timeout");
538 m_TimeoutSituation = true;
539 }
540
541 // fix resends
542 if(m_Buffer.First())
543 {
544 CNetChunkResend *pResend = m_Buffer.First();
545
546 // check if we have some really old stuff laying around and abort if not acked
547 if(Now - pResend->m_FirstSendTime > time_freq() * g_Config.m_ConnTimeout)
548 {
549 m_State = EState::ERROR;
550 char aBuf[128];
551 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Too weak connection (not acked for %d seconds)", g_Config.m_ConnTimeout);
552 SetError(aBuf);
553 m_TimeoutSituation = true;
554 }
555 else
556 {
557 // resend packet if we haven't got it acked in 1 second
558 if(Now - pResend->m_LastSendTime > time_freq())
559 ResendChunk(pResend);
560 }
561 }
562
563 // send keep alives if nothing has happened for 250ms
564 if(State() == EState::ONLINE)
565 {
566 if(time_get() - m_LastSendTime > time_freq() / 2) // flush connection after 500ms if needed
567 {
568 int NumFlushedChunks = Flush();
569 if(NumFlushedChunks && g_Config.m_Debug)
570 dbg_msg(sys: "connection", fmt: "flushed connection due to timeout. %d chunks.", NumFlushedChunks);
571 }
572
573 if(time_get() - m_LastSendTime > time_freq())
574 SendControl(ControlMsg: NET_CTRLMSG_KEEPALIVE, pExtra: nullptr, ExtraSize: 0);
575 }
576 else if(State() == EState::CONNECT)
577 {
578 if(time_get() - m_LastSendTime > time_freq() / 2) // send a new connect every 500ms
579 SendConnect();
580 }
581 else if(State() == EState::PENDING)
582 {
583 if(time_get() - m_LastSendTime > time_freq() / 2) // send a new connect/accept every 500ms
584 SendControl(ControlMsg: NET_CTRLMSG_CONNECTACCEPT, pExtra: SECURITY_TOKEN_MAGIC, ExtraSize: sizeof(SECURITY_TOKEN_MAGIC));
585 }
586
587 return 0;
588}
589
590void CNetConnection::ResumeConnection(const NETADDR *pAddr, int Sequence, int Ack, SECURITY_TOKEN SecurityToken, CStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> *pResendBuffer, bool Sixup)
591{
592 int64_t Now = time_get();
593
594 m_Sequence = Sequence;
595 m_Ack = Ack;
596 m_RemoteClosed = 0;
597
598 m_State = EState::ONLINE;
599 SetPeerAddr(pAddr);
600 m_aErrorString[0] = '\0';
601 m_LastSendTime = Now;
602 m_LastRecvTime = Now;
603 m_LastUpdateTime = Now;
604 m_SecurityToken = SecurityToken;
605 m_Sixup = Sixup;
606
607 // copy resend buffer
608 m_Buffer.Init();
609 while(pResendBuffer->First())
610 {
611 CNetChunkResend *pFirst = pResendBuffer->First();
612
613 CNetChunkResend *pResend = m_Buffer.Allocate(Size: sizeof(CNetChunkResend) + pFirst->m_DataSize);
614 mem_copy(dest: pResend, source: pFirst, size: sizeof(CNetChunkResend) + pFirst->m_DataSize);
615
616 pResendBuffer->PopFirst();
617 }
618}
619