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