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