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