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