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 | |
7 | void 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 | |
14 | void 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 | |
46 | const char *CNetConnection::ErrorString() |
47 | { |
48 | return m_aErrorString; |
49 | } |
50 | |
51 | void CNetConnection::SetError(const char *pString) |
52 | { |
53 | str_copy(dst&: m_aErrorString, src: pString); |
54 | } |
55 | |
56 | void 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 | |
66 | void 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 | |
81 | void CNetConnection::SignalResend() |
82 | { |
83 | m_Construct.m_Flags |= NET_PACKETFLAG_RESEND; |
84 | } |
85 | |
86 | int 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 | |
104 | int 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 ; |
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 | |
155 | int 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 | |
162 | void 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 | |
172 | void CNetConnection::SendControl(int ControlMsg, const void *, int ) |
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 | |
179 | void 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 | |
185 | void CNetConnection::Resend() |
186 | { |
187 | for(CNetChunkResend *pResend = m_Buffer.First(); pResend; pResend = m_Buffer.Next(pCurrent: pResend)) |
188 | ResendChunk(pResend); |
189 | } |
190 | |
191 | int 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 | |
210 | void 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 | |
236 | void 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 | |
255 | int 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[256] = {0}; |
325 | if(pPacket->m_DataSize > 1) |
326 | { |
327 | // make sure to sanitize the error string from 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 | |
424 | int 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[128]; |
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 | |
498 | void 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 | |