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