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 <base/hash_ctxt.h>
4#include <base/system.h>
5
6#include "config.h"
7#include "netban.h"
8#include "network.h"
9#include <engine/shared/compression.h>
10#include <engine/shared/packer.h>
11#include <engine/shared/protocol.h>
12
13const int g_DummyMapCrc = 0xD6909B17;
14const unsigned char g_aDummyMapData[] = {
15 0x44, 0x41, 0x54, 0x41, 0x04, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x00, 0x00,
16 0xEC, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
17 0x01, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
18 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
19 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
20 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00,
21 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
22 0x1C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
23 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
24 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00,
25 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
26 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
27 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
28 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
29 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
30 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, 0x3C, 0x00, 0x00, 0x00,
31 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
33 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
34 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
35 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
36 0x78, 0x9C, 0x63, 0x64, 0x60, 0x60, 0x60, 0x44, 0xC2, 0x00, 0x00, 0x38,
37 0x00, 0x05};
38
39bool CNetServer::Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int MaxClientsPerIp)
40{
41 // zero out the whole structure
42 this->~CNetServer();
43 new(this) CNetServer{};
44
45 // open socket
46 m_Socket = net_udp_create(bindaddr: BindAddr);
47 if(!m_Socket)
48 return false;
49
50 m_Address = BindAddr;
51 m_pNetBan = pNetBan;
52
53 m_MaxClients = clamp(val: MaxClients, lo: 1, hi: (int)NET_MAX_CLIENTS);
54 m_MaxClientsPerIp = MaxClientsPerIp;
55
56 m_NumConAttempts = 0;
57 m_TimeNumConAttempts = time_get();
58
59 m_VConnNum = 0;
60 m_VConnFirst = 0;
61
62 secure_random_fill(bytes: m_aSecurityTokenSeed, length: sizeof(m_aSecurityTokenSeed));
63
64 for(auto &Slot : m_aSlots)
65 Slot.m_Connection.Init(Socket: m_Socket, BlockCloseMsg: true);
66
67 return true;
68}
69
70int CNetServer::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser)
71{
72 m_pfnNewClient = pfnNewClient;
73 m_pfnDelClient = pfnDelClient;
74 m_pUser = pUser;
75 return 0;
76}
77
78int CNetServer::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_NEWCLIENT_NOAUTH pfnNewClientNoAuth, NETFUNC_CLIENTREJOIN pfnClientRejoin, NETFUNC_DELCLIENT pfnDelClient, void *pUser)
79{
80 m_pfnNewClient = pfnNewClient;
81 m_pfnNewClientNoAuth = pfnNewClientNoAuth;
82 m_pfnClientRejoin = pfnClientRejoin;
83 m_pfnDelClient = pfnDelClient;
84 m_pUser = pUser;
85 return 0;
86}
87
88int CNetServer::Close()
89{
90 if(!m_Socket)
91 return 0;
92 return net_udp_close(sock: m_Socket);
93}
94
95int CNetServer::Drop(int ClientId, const char *pReason)
96{
97 // TODO: insert lots of checks here
98
99 if(m_pfnDelClient)
100 m_pfnDelClient(ClientId, pReason, m_pUser);
101
102 m_aSlots[ClientId].m_Connection.Disconnect(pReason);
103
104 return 0;
105}
106
107int CNetServer::Update()
108{
109 for(int i = 0; i < MaxClients(); i++)
110 {
111 m_aSlots[i].m_Connection.Update();
112 if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR &&
113 (!m_aSlots[i].m_Connection.m_TimeoutProtected ||
114 !m_aSlots[i].m_Connection.m_TimeoutSituation))
115 {
116 Drop(ClientId: i, pReason: m_aSlots[i].m_Connection.ErrorString());
117 }
118 }
119
120 return 0;
121}
122
123SECURITY_TOKEN CNetServer::GetGlobalToken()
124{
125 static NETADDR NullAddr = {.type: 0};
126 return GetToken(Addr: NullAddr);
127}
128SECURITY_TOKEN CNetServer::GetToken(const NETADDR &Addr)
129{
130 SHA256_CTX Sha256;
131 sha256_init(ctxt: &Sha256);
132 sha256_update(ctxt: &Sha256, data: (unsigned char *)m_aSecurityTokenSeed, data_len: sizeof(m_aSecurityTokenSeed));
133 sha256_update(ctxt: &Sha256, data: (unsigned char *)&Addr, data_len: 20); // omit port, bad idea!
134
135 SECURITY_TOKEN SecurityToken = ToSecurityToken(pData: sha256_finish(ctxt: &Sha256).data);
136
137 if(SecurityToken == NET_SECURITY_TOKEN_UNKNOWN ||
138 SecurityToken == NET_SECURITY_TOKEN_UNSUPPORTED)
139 SecurityToken = 1;
140
141 return SecurityToken;
142}
143
144void CNetServer::SendControl(NETADDR &Addr, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken)
145{
146 CNetBase::SendControlMsg(Socket: m_Socket, pAddr: &Addr, Ack: 0, ControlMsg, pExtra, ExtraSize, SecurityToken);
147}
148
149int CNetServer::NumClientsWithAddr(NETADDR Addr)
150{
151 int FoundAddr = 0;
152 for(int i = 0; i < MaxClients(); ++i)
153 {
154 if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE ||
155 (m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR &&
156 (!m_aSlots[i].m_Connection.m_TimeoutProtected ||
157 !m_aSlots[i].m_Connection.m_TimeoutSituation)))
158 continue;
159
160 if(!net_addr_comp_noport(a: &Addr, b: m_aSlots[i].m_Connection.PeerAddress()))
161 FoundAddr++;
162 }
163
164 return FoundAddr;
165}
166
167bool CNetServer::Connlimit(NETADDR Addr)
168{
169 int64_t Now = time_get();
170 int Oldest = 0;
171
172 for(int i = 0; i < NET_CONNLIMIT_IPS; ++i)
173 {
174 if(!net_addr_comp(a: &m_aSpamConns[i].m_Addr, b: &Addr))
175 {
176 if(m_aSpamConns[i].m_Time > Now - time_freq() * g_Config.m_SvConnlimitTime)
177 {
178 if(m_aSpamConns[i].m_Conns >= g_Config.m_SvConnlimit)
179 return true;
180 }
181 else
182 {
183 m_aSpamConns[i].m_Time = Now;
184 m_aSpamConns[i].m_Conns = 0;
185 }
186 m_aSpamConns[i].m_Conns++;
187 return false;
188 }
189
190 if(m_aSpamConns[i].m_Time < m_aSpamConns[Oldest].m_Time)
191 Oldest = i;
192 }
193
194 m_aSpamConns[Oldest].m_Addr = Addr;
195 m_aSpamConns[Oldest].m_Time = Now;
196 m_aSpamConns[Oldest].m_Conns = 1;
197 return false;
198}
199
200int CNetServer::TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth, bool Sixup, SECURITY_TOKEN Token)
201{
202 if(Sixup && !g_Config.m_SvSixup)
203 {
204 const char aMsg[] = "0.7 connections are not accepted at this time";
205 CNetBase::SendControlMsg(Socket: m_Socket, pAddr: &Addr, Ack: 0, ControlMsg: NET_CTRLMSG_CLOSE, pExtra: aMsg, ExtraSize: sizeof(aMsg), SecurityToken, Sixup);
206 return -1; // failed to add client?
207 }
208
209 if(Connlimit(Addr))
210 {
211 const char aMsg[] = "Too many connections in a short time";
212 CNetBase::SendControlMsg(Socket: m_Socket, pAddr: &Addr, Ack: 0, ControlMsg: NET_CTRLMSG_CLOSE, pExtra: aMsg, ExtraSize: sizeof(aMsg), SecurityToken, Sixup);
213 return -1; // failed to add client
214 }
215
216 // check for sv_max_clients_per_ip
217 if(NumClientsWithAddr(Addr) + 1 > m_MaxClientsPerIp)
218 {
219 char aBuf[128];
220 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Only %d players with the same IP are allowed", m_MaxClientsPerIp);
221 CNetBase::SendControlMsg(Socket: m_Socket, pAddr: &Addr, Ack: 0, ControlMsg: NET_CTRLMSG_CLOSE, pExtra: aBuf, ExtraSize: str_length(str: aBuf) + 1, SecurityToken, Sixup);
222 return -1; // failed to add client
223 }
224
225 int Slot = -1;
226 for(int i = 0; i < MaxClients(); i++)
227 {
228 if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE)
229 {
230 Slot = i;
231 break;
232 }
233 }
234
235 if(Slot == -1)
236 {
237 const char aFullMsg[] = "This server is full";
238 CNetBase::SendControlMsg(Socket: m_Socket, pAddr: &Addr, Ack: 0, ControlMsg: NET_CTRLMSG_CLOSE, pExtra: aFullMsg, ExtraSize: sizeof(aFullMsg), SecurityToken, Sixup);
239
240 return -1; // failed to add client
241 }
242
243 // init connection slot
244 m_aSlots[Slot].m_Connection.DirectInit(Addr, SecurityToken, Token, Sixup);
245
246 if(VanillaAuth)
247 {
248 // client sequence is unknown if the auth was done
249 // connection-less
250 m_aSlots[Slot].m_Connection.SetUnknownSeq();
251 // correct sequence
252 m_aSlots[Slot].m_Connection.SetSequence(6);
253 }
254
255 if(g_Config.m_Debug)
256 {
257 char aAddrStr[NETADDR_MAXSTRSIZE];
258 net_addr_str(addr: &Addr, string: aAddrStr, max_length: sizeof(aAddrStr), add_port: true);
259 dbg_msg(sys: "security", fmt: "client accepted %s", aAddrStr);
260 }
261
262 if(VanillaAuth)
263 m_pfnNewClientNoAuth(Slot, m_pUser);
264 else
265 m_pfnNewClient(Slot, m_pUser, Sixup);
266
267 return Slot; // done
268}
269
270void CNetServer::SendMsgs(NETADDR &Addr, const CPacker **ppMsgs, int Num)
271{
272 CNetPacketConstruct Construct;
273 mem_zero(block: &Construct, size: sizeof(Construct));
274 unsigned char *pChunkData = &Construct.m_aChunkData[Construct.m_DataSize];
275
276 for(int i = 0; i < Num; i++)
277 {
278 const CPacker *pMsg = ppMsgs[i];
279 CNetChunkHeader Header;
280 Header.m_Flags = NET_CHUNKFLAG_VITAL;
281 Header.m_Size = pMsg->Size();
282 Header.m_Sequence = i + 1;
283 pChunkData = Header.Pack(pData: pChunkData);
284 mem_copy(dest: pChunkData, source: pMsg->Data(), size: pMsg->Size());
285 pChunkData += pMsg->Size();
286 Construct.m_NumChunks++;
287 }
288
289 Construct.m_DataSize = (int)(pChunkData - Construct.m_aChunkData);
290 CNetBase::SendPacket(Socket: m_Socket, pAddr: &Addr, pPacket: &Construct, SecurityToken: NET_SECURITY_TOKEN_UNSUPPORTED);
291}
292
293// connection-less msg packet without token-support
294void CNetServer::OnPreConnMsg(NETADDR &Addr, CNetPacketConstruct &Packet)
295{
296 bool IsCtrl = Packet.m_Flags & NET_PACKETFLAG_CONTROL;
297 int CtrlMsg = m_RecvUnpacker.m_Data.m_aChunkData[0];
298
299 // log flooding
300 //TODO: remove
301 if(g_Config.m_Debug)
302 {
303 int64_t Now = time_get();
304
305 if(Now - m_TimeNumConAttempts > time_freq())
306 // reset
307 m_NumConAttempts = 0;
308
309 m_NumConAttempts++;
310
311 if(m_NumConAttempts > 100)
312 {
313 dbg_msg(sys: "security", fmt: "flooding detected");
314
315 m_TimeNumConAttempts = Now;
316 m_NumConAttempts = 0;
317 }
318 }
319
320 if(IsCtrl && CtrlMsg == NET_CTRLMSG_CONNECT)
321 {
322 if(g_Config.m_SvVanillaAntiSpoof && g_Config.m_Password[0] == '\0')
323 {
324 bool Flooding = false;
325
326 if(g_Config.m_SvVanConnPerSecond)
327 {
328 // detect flooding
329 Flooding = m_VConnNum > g_Config.m_SvVanConnPerSecond;
330 const int64_t Now = time_get();
331
332 if(Now <= m_VConnFirst + time_freq())
333 {
334 m_VConnNum++;
335 }
336 else
337 {
338 m_VConnNum = 1;
339 m_VConnFirst = Now;
340 }
341 }
342
343 if(g_Config.m_Debug && Flooding)
344 {
345 dbg_msg(sys: "security", fmt: "vanilla connection flooding detected");
346 }
347
348 // simulate accept
349 SendControl(Addr, ControlMsg: NET_CTRLMSG_CONNECTACCEPT, NULL, ExtraSize: 0, SecurityToken: NET_SECURITY_TOKEN_UNSUPPORTED);
350
351 // Begin vanilla compatible token handshake
352 // The idea is to pack a security token in the gametick
353 // parameter of NETMSG_SNAPEMPTY. The Client then will
354 // return the token/gametick in NETMSG_INPUT, allowing
355 // us to validate the token.
356 // https://github.com/eeeee/ddnet/commit/b8e40a244af4e242dc568aa34854c5754c75a39a
357
358 // Before we can send NETMSG_SNAPEMPTY, the client needs
359 // to load a map, otherwise it might crash. The map
360 // should be as small as is possible and directly available
361 // to the client. Therefore a dummy map is sent in the same
362 // packet. To reduce the traffic we'll fallback to a default
363 // map if there are too many connection attempts at once.
364
365 // send mapchange + map data + con_ready + 3 x empty snap (with token)
366 CPacker MapChangeMsg;
367 MapChangeMsg.Reset();
368 MapChangeMsg.AddInt(i: (NETMSG_MAP_CHANGE << 1) | 1);
369 if(Flooding)
370 {
371 // Fallback to dm1
372 MapChangeMsg.AddString(pStr: "dm1", Limit: 0);
373 MapChangeMsg.AddInt(i: 0xf2159e6e);
374 MapChangeMsg.AddInt(i: 5805);
375 }
376 else
377 {
378 // dummy map
379 MapChangeMsg.AddString(pStr: "dummy", Limit: 0);
380 MapChangeMsg.AddInt(i: g_DummyMapCrc);
381 MapChangeMsg.AddInt(i: sizeof(g_aDummyMapData));
382 }
383
384 CPacker MapDataMsg;
385 MapDataMsg.Reset();
386 MapDataMsg.AddInt(i: (NETMSG_MAP_DATA << 1) | 1);
387 if(Flooding)
388 {
389 // send empty map data to keep 0.6.4 support
390 MapDataMsg.AddInt(i: 1); // last chunk
391 MapDataMsg.AddInt(i: 0); // crc
392 MapDataMsg.AddInt(i: 0); // chunk index
393 MapDataMsg.AddInt(i: 0); // map size
394 MapDataMsg.AddRaw(NULL, Size: 0); // map data
395 }
396 else
397 {
398 // send dummy map data
399 MapDataMsg.AddInt(i: 1); // last chunk
400 MapDataMsg.AddInt(i: g_DummyMapCrc); // crc
401 MapDataMsg.AddInt(i: 0); // chunk index
402 MapDataMsg.AddInt(i: sizeof(g_aDummyMapData)); // map size
403 MapDataMsg.AddRaw(pData: g_aDummyMapData, Size: sizeof(g_aDummyMapData)); // map data
404 }
405
406 CPacker ConReadyMsg;
407 ConReadyMsg.Reset();
408 ConReadyMsg.AddInt(i: (NETMSG_CON_READY << 1) | 1);
409
410 CPacker SnapEmptyMsg;
411 SnapEmptyMsg.Reset();
412 SnapEmptyMsg.AddInt(i: (NETMSG_SNAPEMPTY << 1) | 1);
413 SECURITY_TOKEN SecurityToken = GetVanillaToken(Addr);
414 SnapEmptyMsg.AddInt(i: SecurityToken);
415 SnapEmptyMsg.AddInt(i: SecurityToken + 1);
416
417 // send all chunks/msgs in one packet
418 const CPacker *apMsgs[] = {&MapChangeMsg, &MapDataMsg, &ConReadyMsg,
419 &SnapEmptyMsg, &SnapEmptyMsg, &SnapEmptyMsg};
420 SendMsgs(Addr, ppMsgs: apMsgs, Num: std::size(apMsgs));
421 }
422 else
423 {
424 // accept client directly
425 SendControl(Addr, ControlMsg: NET_CTRLMSG_CONNECTACCEPT, NULL, ExtraSize: 0, SecurityToken: NET_SECURITY_TOKEN_UNSUPPORTED);
426
427 TryAcceptClient(Addr, SecurityToken: NET_SECURITY_TOKEN_UNSUPPORTED);
428 }
429 }
430 else if(!IsCtrl && g_Config.m_SvVanillaAntiSpoof && g_Config.m_Password[0] == '\0')
431 {
432 CNetChunkHeader h;
433
434 unsigned char *pData = Packet.m_aChunkData;
435 pData = h.Unpack(pData);
436 CUnpacker Unpacker;
437 Unpacker.Reset(pData, Size: h.m_Size);
438 int Msg = Unpacker.GetInt() >> 1;
439
440 if(Msg == NETMSG_INPUT)
441 {
442 SECURITY_TOKEN SecurityToken = Unpacker.GetInt();
443 if(SecurityToken == GetVanillaToken(Addr))
444 {
445 if(g_Config.m_Debug)
446 dbg_msg(sys: "security", fmt: "new client (vanilla handshake)");
447 // try to accept client skipping auth state
448 TryAcceptClient(Addr, SecurityToken: NET_SECURITY_TOKEN_UNSUPPORTED, VanillaAuth: true);
449 }
450 else if(g_Config.m_Debug)
451 dbg_msg(sys: "security", fmt: "invalid token (vanilla handshake)");
452 }
453 else
454 {
455 if(g_Config.m_Debug)
456 {
457 dbg_msg(sys: "security", fmt: "invalid preconn msg %d", Msg);
458 }
459 }
460 }
461}
462
463void CNetServer::OnConnCtrlMsg(NETADDR &Addr, int ClientId, int ControlMsg, const CNetPacketConstruct &Packet)
464{
465 if(ControlMsg == NET_CTRLMSG_CONNECT)
466 {
467 // got connection attempt inside of valid session
468 // the client probably wants to reconnect
469 bool SupportsToken = Packet.m_DataSize >=
470 (int)(1 + sizeof(SECURITY_TOKEN_MAGIC) + sizeof(SECURITY_TOKEN)) &&
471 !mem_comp(a: &Packet.m_aChunkData[1], b: SECURITY_TOKEN_MAGIC, size: sizeof(SECURITY_TOKEN_MAGIC));
472
473 if(SupportsToken)
474 {
475 // response connection request with token
476 SECURITY_TOKEN Token = GetToken(Addr);
477 SendControl(Addr, ControlMsg: NET_CTRLMSG_CONNECTACCEPT, pExtra: SECURITY_TOKEN_MAGIC, ExtraSize: sizeof(SECURITY_TOKEN_MAGIC), SecurityToken: Token);
478 }
479
480 if(g_Config.m_Debug)
481 dbg_msg(sys: "security", fmt: "client %d wants to reconnect", ClientId);
482 }
483 else if(ControlMsg == NET_CTRLMSG_ACCEPT && Packet.m_DataSize == 1 + sizeof(SECURITY_TOKEN))
484 {
485 SECURITY_TOKEN Token = ToSecurityToken(pData: &Packet.m_aChunkData[1]);
486 if(Token == GetToken(Addr))
487 {
488 // correct token
489 // try to accept client
490 if(g_Config.m_Debug)
491 dbg_msg(sys: "security", fmt: "client %d reconnect", ClientId);
492
493 // reset netconn and process rejoin
494 m_aSlots[ClientId].m_Connection.Reset(Rejoin: true);
495 m_pfnClientRejoin(ClientId, m_pUser);
496 }
497 }
498}
499
500void CNetServer::OnTokenCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketConstruct &Packet)
501{
502 if(ClientExists(Addr))
503 return; // silently ignore
504
505 if(Addr.type == NETTYPE_WEBSOCKET_IPV4)
506 {
507 // websocket client doesn't send token
508 // direct accept
509 SendControl(Addr, ControlMsg: NET_CTRLMSG_CONNECTACCEPT, pExtra: SECURITY_TOKEN_MAGIC, ExtraSize: sizeof(SECURITY_TOKEN_MAGIC), SecurityToken: NET_SECURITY_TOKEN_UNSUPPORTED);
510 TryAcceptClient(Addr, SecurityToken: NET_SECURITY_TOKEN_UNSUPPORTED);
511 }
512 else if(ControlMsg == NET_CTRLMSG_CONNECT)
513 {
514 // response connection request with token
515 SECURITY_TOKEN Token = GetToken(Addr);
516 SendControl(Addr, ControlMsg: NET_CTRLMSG_CONNECTACCEPT, pExtra: SECURITY_TOKEN_MAGIC, ExtraSize: sizeof(SECURITY_TOKEN_MAGIC), SecurityToken: Token);
517 }
518 else if(ControlMsg == NET_CTRLMSG_ACCEPT)
519 {
520 SECURITY_TOKEN Token = ToSecurityToken(pData: &Packet.m_aChunkData[1]);
521 if(Token == GetToken(Addr))
522 {
523 // correct token
524 // try to accept client
525 if(g_Config.m_Debug)
526 dbg_msg(sys: "security", fmt: "new client (ddnet token)");
527 TryAcceptClient(Addr, SecurityToken: Token);
528 }
529 else
530 {
531 // invalid token
532 if(g_Config.m_Debug)
533 dbg_msg(sys: "security", fmt: "invalid token");
534 }
535 }
536}
537
538int CNetServer::OnSixupCtrlMsg(NETADDR &Addr, CNetChunk *pChunk, int ControlMsg, const CNetPacketConstruct &Packet, SECURITY_TOKEN &ResponseToken, SECURITY_TOKEN Token)
539{
540 if(m_RecvUnpacker.m_Data.m_DataSize < 5 || ClientExists(Addr))
541 return 0; // silently ignore
542
543 mem_copy(dest: &ResponseToken, source: Packet.m_aChunkData + 1, size: 4);
544
545 if(ControlMsg == 5)
546 {
547 if(m_RecvUnpacker.m_Data.m_DataSize >= 512)
548 {
549 SendTokenSixup(Addr, Token: ResponseToken);
550 return 0;
551 }
552
553 // Is this behaviour safe to rely on?
554 pChunk->m_Flags = 0;
555 pChunk->m_ClientId = -1;
556 pChunk->m_Address = Addr;
557 pChunk->m_DataSize = 0;
558 return 1;
559 }
560 else if(ControlMsg == NET_CTRLMSG_CONNECT)
561 {
562 SECURITY_TOKEN MyToken = GetToken(Addr);
563 unsigned char aToken[sizeof(SECURITY_TOKEN)];
564 mem_copy(dest: aToken, source: &MyToken, size: sizeof(aToken));
565
566 CNetBase::SendControlMsg(Socket: m_Socket, pAddr: &Addr, Ack: 0, ControlMsg: NET_CTRLMSG_CONNECTACCEPT, pExtra: aToken, ExtraSize: sizeof(aToken), SecurityToken: ResponseToken, Sixup: true);
567 if(Token == MyToken)
568 TryAcceptClient(Addr, SecurityToken: ResponseToken, VanillaAuth: false, Sixup: true, Token);
569 }
570
571 return 0;
572}
573
574int CNetServer::GetClientSlot(const NETADDR &Addr)
575{
576 int Slot = -1;
577
578 for(int i = 0; i < MaxClients(); i++)
579 {
580 if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE &&
581 m_aSlots[i].m_Connection.State() != NET_CONNSTATE_ERROR &&
582 net_addr_comp(a: m_aSlots[i].m_Connection.PeerAddress(), b: &Addr) == 0)
583
584 {
585 Slot = i;
586 }
587 }
588
589 return Slot;
590}
591
592static bool IsDDNetControlMsg(const CNetPacketConstruct *pPacket)
593{
594 if(!(pPacket->m_Flags & NET_PACKETFLAG_CONTROL) || pPacket->m_DataSize < 1)
595 {
596 return false;
597 }
598 if(pPacket->m_aChunkData[0] == NET_CTRLMSG_CONNECT && pPacket->m_DataSize >= (int)(1 + sizeof(SECURITY_TOKEN_MAGIC) + sizeof(SECURITY_TOKEN)) && mem_comp(a: &pPacket->m_aChunkData[1], b: SECURITY_TOKEN_MAGIC, size: sizeof(SECURITY_TOKEN_MAGIC)) == 0)
599 {
600 // DDNet CONNECT
601 return true;
602 }
603 if(pPacket->m_aChunkData[0] == NET_CTRLMSG_ACCEPT && pPacket->m_DataSize >= 1 + (int)sizeof(SECURITY_TOKEN))
604 {
605 // DDNet ACCEPT
606 return true;
607 }
608 return false;
609}
610
611/*
612 TODO: chopp up this function into smaller working parts
613*/
614int CNetServer::Recv(CNetChunk *pChunk, SECURITY_TOKEN *pResponseToken)
615{
616 while(true)
617 {
618 NETADDR Addr;
619
620 // check for a chunk
621 if(m_RecvUnpacker.FetchChunk(pChunk))
622 return 1;
623
624 // TODO: empty the recvinfo
625 unsigned char *pData;
626 int Bytes = net_udp_recv(sock: m_Socket, addr: &Addr, data: &pData);
627
628 // no more packets for now
629 if(Bytes <= 0)
630 break;
631
632 // check if we just should drop the packet
633 char aBuf[128];
634 if(NetBan() && NetBan()->IsBanned(pOrigAddr: &Addr, pBuf: aBuf, BufferSize: sizeof(aBuf)))
635 {
636 // banned, reply with a message
637 CNetBase::SendControlMsg(Socket: m_Socket, pAddr: &Addr, Ack: 0, ControlMsg: NET_CTRLMSG_CLOSE, pExtra: aBuf, ExtraSize: str_length(str: aBuf) + 1, SecurityToken: NET_SECURITY_TOKEN_UNSUPPORTED);
638 continue;
639 }
640
641 SECURITY_TOKEN Token;
642 bool Sixup = false;
643 *pResponseToken = NET_SECURITY_TOKEN_UNKNOWN;
644 if(CNetBase::UnpackPacket(pBuffer: pData, Size: Bytes, pPacket: &m_RecvUnpacker.m_Data, Sixup, pSecurityToken: &Token, pResponseToken) == 0)
645 {
646 if(m_RecvUnpacker.m_Data.m_Flags & NET_PACKETFLAG_CONNLESS)
647 {
648 if(Sixup && Token != GetToken(Addr) && Token != GetGlobalToken())
649 continue;
650
651 pChunk->m_Flags = NETSENDFLAG_CONNLESS;
652 pChunk->m_ClientId = -1;
653 pChunk->m_Address = Addr;
654 pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize;
655 pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData;
656 if(m_RecvUnpacker.m_Data.m_Flags & NET_PACKETFLAG_EXTENDED)
657 {
658 pChunk->m_Flags |= NETSENDFLAG_EXTENDED;
659 mem_copy(dest: pChunk->m_aExtraData, source: m_RecvUnpacker.m_Data.m_aExtraData, size: sizeof(pChunk->m_aExtraData));
660 }
661 return 1;
662 }
663 else
664 {
665 // drop invalid ctrl packets
666 if(m_RecvUnpacker.m_Data.m_Flags & NET_PACKETFLAG_CONTROL &&
667 m_RecvUnpacker.m_Data.m_DataSize == 0)
668 continue;
669
670 // normal packet, find matching slot
671 int Slot = GetClientSlot(Addr);
672
673 if(!Sixup && Slot != -1 && m_aSlots[Slot].m_Connection.m_Sixup)
674 {
675 Sixup = true;
676 if(CNetBase::UnpackPacket(pBuffer: pData, Size: Bytes, pPacket: &m_RecvUnpacker.m_Data, Sixup, pSecurityToken: &Token))
677 continue;
678 }
679
680 if(Slot != -1)
681 {
682 // found
683
684 // control
685 if(m_RecvUnpacker.m_Data.m_Flags & NET_PACKETFLAG_CONTROL)
686 OnConnCtrlMsg(Addr, ClientId: Slot, ControlMsg: m_RecvUnpacker.m_Data.m_aChunkData[0], Packet: m_RecvUnpacker.m_Data);
687
688 if(m_aSlots[Slot].m_Connection.Feed(pPacket: &m_RecvUnpacker.m_Data, pAddr: &Addr, SecurityToken: Token))
689 {
690 if(m_RecvUnpacker.m_Data.m_DataSize)
691 m_RecvUnpacker.Start(pAddr: &Addr, pConnection: &m_aSlots[Slot].m_Connection, ClientId: Slot);
692 }
693 }
694 else
695 {
696 // not found, client that wants to connect
697
698 if(Sixup)
699 {
700 // got 0.7 control msg
701 if(OnSixupCtrlMsg(Addr, pChunk, ControlMsg: m_RecvUnpacker.m_Data.m_aChunkData[0], Packet: m_RecvUnpacker.m_Data, ResponseToken&: *pResponseToken, Token) == 1)
702 return 1;
703 }
704 else if(IsDDNetControlMsg(pPacket: &m_RecvUnpacker.m_Data))
705 {
706 // got ddnet control msg
707 OnTokenCtrlMsg(Addr, ControlMsg: m_RecvUnpacker.m_Data.m_aChunkData[0], Packet: m_RecvUnpacker.m_Data);
708 }
709 else
710 {
711 // got connection-less ctrl or sys msg
712 OnPreConnMsg(Addr, Packet&: m_RecvUnpacker.m_Data);
713 }
714 }
715 }
716 }
717 }
718 return 0;
719}
720
721int CNetServer::Send(CNetChunk *pChunk)
722{
723 if(pChunk->m_DataSize >= NET_MAX_PAYLOAD)
724 {
725 dbg_msg(sys: "netserver", fmt: "packet payload too big. %d. dropping packet", pChunk->m_DataSize);
726 return -1;
727 }
728
729 if(pChunk->m_Flags & NETSENDFLAG_CONNLESS)
730 {
731 // send connectionless packet
732 CNetBase::SendPacketConnless(Socket: m_Socket, pAddr: &pChunk->m_Address, pData: pChunk->m_pData, DataSize: pChunk->m_DataSize,
733 Extended: pChunk->m_Flags & NETSENDFLAG_EXTENDED, aExtra: pChunk->m_aExtraData);
734 }
735 else
736 {
737 int Flags = 0;
738 dbg_assert(pChunk->m_ClientId >= 0, "erroneous client id");
739 dbg_assert(pChunk->m_ClientId < MaxClients(), "erroneous client id");
740
741 if(pChunk->m_Flags & NETSENDFLAG_VITAL)
742 Flags = NET_CHUNKFLAG_VITAL;
743
744 if(m_aSlots[pChunk->m_ClientId].m_Connection.QueueChunk(Flags, DataSize: pChunk->m_DataSize, pData: pChunk->m_pData) == 0)
745 {
746 if(pChunk->m_Flags & NETSENDFLAG_FLUSH)
747 m_aSlots[pChunk->m_ClientId].m_Connection.Flush();
748 }
749 else
750 {
751 //Drop(pChunk->m_ClientId, "Error sending data");
752 }
753 }
754 return 0;
755}
756
757void CNetServer::SendTokenSixup(NETADDR &Addr, SECURITY_TOKEN Token)
758{
759 SECURITY_TOKEN MyToken = GetToken(Addr);
760 unsigned char aBuf[512] = {};
761 mem_copy(dest: aBuf, source: &MyToken, size: 4);
762 int Size = (Token == NET_SECURITY_TOKEN_UNKNOWN) ? 512 : 4;
763 CNetBase::SendControlMsg(Socket: m_Socket, pAddr: &Addr, Ack: 0, ControlMsg: 5, pExtra: aBuf, ExtraSize: Size, SecurityToken: Token, Sixup: true);
764}
765
766int CNetServer::SendConnlessSixup(CNetChunk *pChunk, SECURITY_TOKEN ResponseToken)
767{
768 if(pChunk->m_DataSize > NET_MAX_PACKETSIZE - 9)
769 return -1;
770
771 unsigned char aBuffer[NET_MAX_PACKETSIZE];
772 aBuffer[0] = NET_PACKETFLAG_CONNLESS << 2 | 1;
773 SECURITY_TOKEN Token = GetToken(Addr: pChunk->m_Address);
774 mem_copy(dest: aBuffer + 1, source: &ResponseToken, size: 4);
775 mem_copy(dest: aBuffer + 5, source: &Token, size: 4);
776 mem_copy(dest: aBuffer + 9, source: pChunk->m_pData, size: pChunk->m_DataSize);
777 net_udp_send(sock: m_Socket, addr: &pChunk->m_Address, data: aBuffer, size: pChunk->m_DataSize + 9);
778
779 return 0;
780}
781
782void CNetServer::SetMaxClientsPerIp(int Max)
783{
784 // clamp
785 if(Max < 1)
786 Max = 1;
787 else if(Max > NET_MAX_CLIENTS)
788 Max = NET_MAX_CLIENTS;
789
790 m_MaxClientsPerIp = Max;
791}
792
793bool CNetServer::SetTimedOut(int ClientId, int OrigId)
794{
795 if(m_aSlots[ClientId].m_Connection.State() != NET_CONNSTATE_ERROR)
796 return false;
797
798 m_aSlots[ClientId].m_Connection.SetTimedOut(pAddr: ClientAddr(ClientId: OrigId), Sequence: m_aSlots[OrigId].m_Connection.SeqSequence(), Ack: m_aSlots[OrigId].m_Connection.AckSequence(), SecurityToken: m_aSlots[OrigId].m_Connection.SecurityToken(), pResendBuffer: m_aSlots[OrigId].m_Connection.ResendBuffer(), Sixup: m_aSlots[OrigId].m_Connection.m_Sixup);
799 m_aSlots[OrigId].m_Connection.Reset();
800 return true;
801}
802
803void CNetServer::SetTimeoutProtected(int ClientId)
804{
805 m_aSlots[ClientId].m_Connection.m_TimeoutProtected = true;
806}
807
808int CNetServer::ResetErrorString(int ClientId)
809{
810 m_aSlots[ClientId].m_Connection.ResetErrorString();
811 return 0;
812}
813
814const char *CNetServer::ErrorString(int ClientId)
815{
816 return m_aSlots[ClientId].m_Connection.ErrorString();
817}
818