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 = 0x6AF73DAF;
19const unsigned char g_aDummyMapData[] = {
20 0x44, 0x41, 0x54, 0x41, 0x04, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00,
21 0xF4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
22 0x02, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0x1C, 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, 0x0E, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
27 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x00, 0x00,
28 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
29 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30 0x02, 0x00, 0x00, 0x00, 0x00, 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 0x01, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
34 0xFF, 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 0x01, 0x00, 0x05, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
37 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
38 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
39 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
40 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
41 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x78, 0x9C, 0x63, 0x64,
42 0x60, 0x60, 0x60, 0x44, 0xC2, 0x00, 0x00, 0x38, 0x00, 0x05, 0x78, 0x9C,
43 0x63, 0x64, 0x60, 0x60, 0x60, 0x44, 0xC2, 0x00, 0x00, 0x38, 0x00, 0x05};
44
45bool CNetServer::Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int MaxClientsPerIp)
46{
47 // zero out the whole structure
48 this->~CNetServer();
49 new(this) CNetServer{};
50
51 // open socket
52 m_Socket = net_udp_create(bindaddr: BindAddr);
53 if(!m_Socket)
54 return false;
55
56 m_Address = BindAddr;
57 m_pNetBan = pNetBan;
58
59 m_MaxClients = std::clamp(val: MaxClients, lo: 1, hi: (int)NET_MAX_CLIENTS);
60 m_MaxClientsPerIp = MaxClientsPerIp;
61
62 m_VConnNum = 0;
63 m_VConnFirst = 0;
64
65 secure_random_fill(bytes: m_aSecurityTokenSeed, length: sizeof(m_aSecurityTokenSeed));
66
67 for(auto &Slot : m_aSlots)
68 Slot.m_Connection.Init(Socket: m_Socket, BlockCloseMsg: true);
69
70 return true;
71}
72
73int CNetServer::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser)
74{
75 m_pfnNewClient = pfnNewClient;
76 m_pfnDelClient = pfnDelClient;
77 m_pUser = pUser;
78 return 0;
79}
80
81int CNetServer::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_NEWCLIENT_NOAUTH pfnNewClientNoAuth, NETFUNC_CLIENTREJOIN pfnClientRejoin, NETFUNC_DELCLIENT pfnDelClient, void *pUser)
82{
83 m_pfnNewClient = pfnNewClient;
84 m_pfnNewClientNoAuth = pfnNewClientNoAuth;
85 m_pfnClientRejoin = pfnClientRejoin;
86 m_pfnDelClient = pfnDelClient;
87 m_pUser = pUser;
88 return 0;
89}
90
91void CNetServer::Close()
92{
93 if(!m_Socket)
94 {
95 return;
96 }
97 net_udp_close(sock: m_Socket);
98 m_Socket = nullptr;
99}
100
101void CNetServer::Drop(int ClientId, const char *pReason)
102{
103 // TODO: insert lots of checks here
104
105 if(m_pfnDelClient)
106 m_pfnDelClient(ClientId, pReason, m_pUser);
107
108 m_aSlots[ClientId].m_Connection.Disconnect(pReason);
109}
110
111void CNetServer::Update()
112{
113 for(int i = 0; i < MaxClients(); i++)
114 {
115 m_aSlots[i].m_Connection.Update();
116 if(m_aSlots[i].m_Connection.State() == CNetConnection::EState::ERROR &&
117 (!m_aSlots[i].m_Connection.m_TimeoutProtected ||
118 !m_aSlots[i].m_Connection.m_TimeoutSituation))
119 {
120 Drop(ClientId: i, pReason: m_aSlots[i].m_Connection.ErrorString());
121 }
122 }
123}
124
125void CNetServer::EndFlushBatch()
126{
127 m_FlushBatch = false;
128 for(int ClientId = 0; ClientId < MaxClients(); ClientId++)
129 {
130 if(!m_aFlushPending[ClientId])
131 continue;
132 m_aFlushPending[ClientId] = false;
133 if(m_aSlots[ClientId].m_Connection.State() == CNetConnection::EState::ONLINE)
134 m_aSlots[ClientId].m_Connection.Flush();
135 }
136}
137
138SECURITY_TOKEN CNetServer::GetGlobalToken()
139{
140 static const NETADDR NULL_ADDR = {.type: 0};
141 return GetToken(Addr: NULL_ADDR);
142}
143SECURITY_TOKEN CNetServer::GetToken(const NETADDR &Addr)
144{
145 SHA256_CTX Sha256;
146 sha256_init(ctxt: &Sha256);
147 sha256_update(ctxt: &Sha256, data: (unsigned char *)m_aSecurityTokenSeed, data_len: sizeof(m_aSecurityTokenSeed));
148 sha256_update(ctxt: &Sha256, data: (unsigned char *)&Addr, data_len: 20); // omit port, bad idea!
149
150 SECURITY_TOKEN SecurityToken = ToSecurityToken(pData: sha256_finish(ctxt: &Sha256).data);
151
152 if(SecurityToken == NET_SECURITY_TOKEN_UNKNOWN ||
153 SecurityToken == NET_SECURITY_TOKEN_UNSUPPORTED)
154 SecurityToken = 1;
155
156 return SecurityToken;
157}
158
159SECURITY_TOKEN CNetServer::GetVanillaToken(const NETADDR &Addr)
160{
161 // vanilla token/gametick shouldn't be negative
162 return absolute(a: GetToken(Addr));
163}
164
165void CNetServer::SendControl(NETADDR &Addr, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken)
166{
167 CNetBase::SendControlMsg(Socket: m_Socket, pAddr: &Addr, Ack: 0, ControlMsg, pExtra, ExtraSize, SecurityToken);
168}
169
170int CNetServer::NumClientsWithAddr(NETADDR Addr)
171{
172 int FoundAddr = 0;
173 for(int i = 0; i < MaxClients(); ++i)
174 {
175 if(m_aSlots[i].m_Connection.State() == CNetConnection::EState::OFFLINE ||
176 (m_aSlots[i].m_Connection.State() == CNetConnection::EState::ERROR &&
177 (!m_aSlots[i].m_Connection.m_TimeoutProtected ||
178 !m_aSlots[i].m_Connection.m_TimeoutSituation)))
179 continue;
180
181 if(!net_addr_comp_noport(a: &Addr, b: m_aSlots[i].m_Connection.PeerAddress()))
182 FoundAddr++;
183 }
184
185 return FoundAddr;
186}
187
188bool CNetServer::Connlimit(NETADDR Addr)
189{
190 int64_t Now = time_get();
191 int Oldest = 0;
192
193 for(int i = 0; i < NET_CONNLIMIT_IPS; ++i)
194 {
195 if(!net_addr_comp_noport(a: &m_aSpamConns[i].m_Addr, b: &Addr))
196 {
197 if(m_aSpamConns[i].m_Time > Now - time_freq() * g_Config.m_SvConnlimitTime)
198 {
199 if(m_aSpamConns[i].m_Conns >= g_Config.m_SvConnlimit)
200 return true;
201 }
202 else
203 {
204 m_aSpamConns[i].m_Time = Now;
205 m_aSpamConns[i].m_Conns = 0;
206 }
207 m_aSpamConns[i].m_Conns++;
208 return false;
209 }
210
211 if(m_aSpamConns[i].m_Time < m_aSpamConns[Oldest].m_Time)
212 Oldest = i;
213 }
214
215 m_aSpamConns[Oldest].m_Addr = Addr;
216 m_aSpamConns[Oldest].m_Time = Now;
217 m_aSpamConns[Oldest].m_Conns = 1;
218 return false;
219}
220
221int CNetServer::TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth, bool Sixup, SECURITY_TOKEN Token)
222{
223 if(Sixup && !g_Config.m_SvSixup)
224 {
225 const char aMsg[] = "0.7 connections are not accepted at this time";
226 CNetBase::SendControlMsg(Socket: m_Socket, pAddr: &Addr, Ack: 0, ControlMsg: NET_CTRLMSG_CLOSE, pExtra: aMsg, ExtraSize: sizeof(aMsg), SecurityToken, Sixup);
227 return -1; // failed to add client?
228 }
229
230 if(Connlimit(Addr))
231 {
232 const char aMsg[] = "Too many connections in a short time";
233 CNetBase::SendControlMsg(Socket: m_Socket, pAddr: &Addr, Ack: 0, ControlMsg: NET_CTRLMSG_CLOSE, pExtra: aMsg, ExtraSize: sizeof(aMsg), SecurityToken, Sixup);
234 return -1; // failed to add client
235 }
236
237 // check for sv_max_clients_per_ip
238 if(NumClientsWithAddr(Addr) + 1 > m_MaxClientsPerIp)
239 {
240 char aBuf[128];
241 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Only %d players with the same IP are allowed", m_MaxClientsPerIp);
242 CNetBase::SendControlMsg(Socket: m_Socket, pAddr: &Addr, Ack: 0, ControlMsg: NET_CTRLMSG_CLOSE, pExtra: aBuf, ExtraSize: str_length(str: aBuf) + 1, SecurityToken, Sixup);
243 return -1; // failed to add client
244 }
245
246 int Slot = -1;
247 for(int i = 0; i < MaxClients(); i++)
248 {
249 if(m_aSlots[i].m_Connection.State() == CNetConnection::EState::OFFLINE)
250 {
251 Slot = i;
252 break;
253 }
254 }
255
256 if(Slot == -1)
257 {
258 const char aFullMsg[] = "This server is full";
259 CNetBase::SendControlMsg(Socket: m_Socket, pAddr: &Addr, Ack: 0, ControlMsg: NET_CTRLMSG_CLOSE, pExtra: aFullMsg, ExtraSize: sizeof(aFullMsg), SecurityToken, Sixup);
260
261 return -1; // failed to add client
262 }
263
264 // init connection slot
265 m_aSlots[Slot].m_Connection.DirectInit(Addr, SecurityToken, Token, Sixup);
266
267 if(VanillaAuth)
268 {
269 // client sequence is unknown if the auth was done
270 // connection-less
271 m_aSlots[Slot].m_Connection.SetUnknownSeq();
272 // correct sequence
273 m_aSlots[Slot].m_Connection.SetSequence(6);
274 }
275
276 if(g_Config.m_Debug)
277 {
278 dbg_msg(sys: "security", fmt: "client accepted %s", m_aSlots[Slot].m_Connection.PeerAddressString(IncludePort: true).data());
279 }
280
281 if(VanillaAuth)
282 m_pfnNewClientNoAuth(Slot, m_pUser);
283 else
284 m_pfnNewClient(Slot, m_pUser, Sixup);
285
286 return Slot; // done
287}
288
289void CNetServer::SendMsgs(NETADDR &Addr, const CPacker **ppMsgs, int Num)
290{
291 dbg_assert(Num > 0 && Num <= NET_MAX_PACKET_CHUNKS, "Number of messages invalid: %d", Num);
292
293 CNetPacketConstruct Construct;
294 mem_zero(block: &Construct, size: sizeof(Construct));
295 unsigned char *pChunkData = &Construct.m_aChunkData[Construct.m_DataSize];
296
297 for(int i = 0; i < Num; i++)
298 {
299 const CPacker *pMsg = ppMsgs[i];
300 CNetChunkHeader Header;
301 Header.m_Flags = NET_CHUNKFLAG_VITAL;
302 Header.m_Size = pMsg->Size();
303 Header.m_Sequence = i + 1;
304 pChunkData = Header.Pack(pData: pChunkData);
305 mem_copy(dest: pChunkData, source: pMsg->Data(), size: pMsg->Size());
306 pChunkData += pMsg->Size();
307 Construct.m_NumChunks++;
308 }
309
310 Construct.m_DataSize = (int)(pChunkData - Construct.m_aChunkData);
311 CNetBase::SendPacket(Socket: m_Socket, pAddr: &Addr, pPacket: &Construct, SecurityToken: NET_SECURITY_TOKEN_UNSUPPORTED);
312}
313
314// connection-less msg packet without token-support
315void CNetServer::OnPreConnMsg(NETADDR &Addr, CNetPacketConstruct &Packet)
316{
317 bool IsCtrl = Packet.m_Flags & NET_PACKETFLAG_CONTROL;
318 int CtrlMsg = Packet.m_aChunkData[0];
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, pExtra: nullptr, 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 // no 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, pExtra: nullptr, 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 {
452 dbg_msg(sys: "security", fmt: "invalid token (vanilla handshake)");
453 }
454 }
455 else
456 {
457 if(g_Config.m_Debug)
458 {
459 dbg_msg(sys: "security", fmt: "invalid preconn msg %d", Msg);
460 }
461 }
462 }
463}
464
465void CNetServer::OnConnCtrlMsg(NETADDR &Addr, int ClientId, int ControlMsg, const CNetPacketConstruct &Packet)
466{
467 if(ControlMsg == NET_CTRLMSG_CONNECT)
468 {
469 // got connection attempt inside of valid session
470 // the client probably wants to reconnect
471 bool SupportsToken = Packet.m_DataSize >=
472 (int)(1 + sizeof(SECURITY_TOKEN_MAGIC) + sizeof(SECURITY_TOKEN)) &&
473 !mem_comp(a: &Packet.m_aChunkData[1], b: SECURITY_TOKEN_MAGIC, size: sizeof(SECURITY_TOKEN_MAGIC));
474
475 if(SupportsToken)
476 {
477 // response connection request with token
478 SECURITY_TOKEN Token = GetToken(Addr);
479 SendControl(Addr, ControlMsg: NET_CTRLMSG_CONNECTACCEPT, pExtra: SECURITY_TOKEN_MAGIC, ExtraSize: sizeof(SECURITY_TOKEN_MAGIC), SecurityToken: Token);
480 }
481
482 if(g_Config.m_Debug)
483 dbg_msg(sys: "security", fmt: "client %d wants to reconnect", ClientId);
484 }
485 else if(ControlMsg == NET_CTRLMSG_ACCEPT && Packet.m_DataSize == 1 + sizeof(SECURITY_TOKEN))
486 {
487 SECURITY_TOKEN Token = ToSecurityToken(pData: &Packet.m_aChunkData[1]);
488 if(Token == GetToken(Addr))
489 {
490 // correct token
491 // try to accept client
492 if(g_Config.m_Debug)
493 dbg_msg(sys: "security", fmt: "client %d reconnect", ClientId);
494
495 // reset netconn and process rejoin
496 m_aSlots[ClientId].m_Connection.Reset(Rejoin: true);
497 m_pfnClientRejoin(ClientId, m_pUser);
498 }
499 }
500}
501
502void CNetServer::OnTokenCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketConstruct &Packet)
503{
504 if(ClientExists(Addr))
505 return; // silently ignore
506
507 if(ControlMsg == NET_CTRLMSG_CONNECT)
508 {
509 // response connection request with token
510 SECURITY_TOKEN Token = GetToken(Addr);
511 SendControl(Addr, ControlMsg: NET_CTRLMSG_CONNECTACCEPT, pExtra: SECURITY_TOKEN_MAGIC, ExtraSize: sizeof(SECURITY_TOKEN_MAGIC), SecurityToken: Token);
512 }
513 else if(ControlMsg == NET_CTRLMSG_ACCEPT)
514 {
515 SECURITY_TOKEN Token = ToSecurityToken(pData: &Packet.m_aChunkData[1]);
516 if(Token == GetToken(Addr))
517 {
518 // correct token
519 // try to accept client
520 if(g_Config.m_Debug)
521 dbg_msg(sys: "security", fmt: "new client (ddnet token)");
522 TryAcceptClient(Addr, SecurityToken: Token);
523 }
524 else
525 {
526 // invalid token
527 if(g_Config.m_Debug)
528 dbg_msg(sys: "security", fmt: "invalid token");
529 }
530 }
531}
532
533int CNetServer::OnSixupCtrlMsg(NETADDR &Addr, CNetChunk *pChunk, int ControlMsg, const CNetPacketConstruct &Packet, SECURITY_TOKEN &ResponseToken, SECURITY_TOKEN Token)
534{
535 if(Packet.m_DataSize < 1 + (int)sizeof(SECURITY_TOKEN) || ClientExists(Addr))
536 return 0; // silently ignore
537
538 ResponseToken = ToSecurityToken(pData: Packet.m_aChunkData + 1);
539
540 if(ControlMsg == protocol7::NET_CTRLMSG_TOKEN)
541 {
542 if(Packet.m_DataSize >= (int)NET_TOKENREQUEST_DATASIZE)
543 {
544 SendTokenSixup(Addr, Token: ResponseToken);
545 return 0;
546 }
547
548 // Is this behaviour safe to rely on?
549 pChunk->m_Flags = 0;
550 pChunk->m_ClientId = -1;
551 pChunk->m_Address = Addr;
552 pChunk->m_DataSize = 0;
553 return 1;
554 }
555 else if(ControlMsg == NET_CTRLMSG_CONNECT)
556 {
557 SECURITY_TOKEN MyToken = GetToken(Addr);
558 unsigned char aToken[sizeof(SECURITY_TOKEN)];
559 mem_copy(dest: aToken, source: &MyToken, size: sizeof(aToken));
560
561 CNetBase::SendControlMsg(Socket: m_Socket, pAddr: &Addr, Ack: 0, ControlMsg: NET_CTRLMSG_CONNECTACCEPT, pExtra: aToken, ExtraSize: sizeof(aToken), SecurityToken: ResponseToken, Sixup: true);
562 if(Token == MyToken)
563 TryAcceptClient(Addr, SecurityToken: ResponseToken, VanillaAuth: false, Sixup: true, Token);
564 }
565
566 return 0;
567}
568
569int CNetServer::GetClientSlot(const NETADDR &Addr)
570{
571 for(int i = 0; i < MaxClients(); i++)
572 {
573 if(m_aSlots[i].m_Connection.State() != CNetConnection::EState::OFFLINE &&
574 m_aSlots[i].m_Connection.State() != CNetConnection::EState::ERROR &&
575 net_addr_comp(a: m_aSlots[i].m_Connection.PeerAddress(), b: &Addr) == 0)
576 {
577 return i;
578 }
579 }
580 return -1;
581}
582
583static bool IsDDNetControlMsg(const CNetPacketConstruct *pPacket)
584{
585 if(!(pPacket->m_Flags & NET_PACKETFLAG_CONTROL) || pPacket->m_DataSize < 1)
586 {
587 return false;
588 }
589 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)
590 {
591 // DDNet CONNECT
592 return true;
593 }
594 if(pPacket->m_aChunkData[0] == NET_CTRLMSG_ACCEPT && pPacket->m_DataSize >= 1 + (int)sizeof(SECURITY_TOKEN))
595 {
596 // DDNet ACCEPT
597 return true;
598 }
599 return false;
600}
601
602/*
603 TODO: chopp up this function into smaller working parts
604*/
605int CNetServer::Recv(CNetChunk *pChunk, SECURITY_TOKEN *pResponseToken)
606{
607 while(true)
608 {
609 // Unpack next chunk from stored packet if available
610 if(m_PacketChunkUnpacker.UnpackNextChunk(pChunk))
611 {
612 // Only return the pending packet if the client is
613 // still available, the caller might have dropped them
614 // in response to the previous chunk.
615 if(m_aSlots[pChunk->m_ClientId].m_Connection.State() != CNetConnection::EState::OFFLINE)
616 {
617 return 1;
618 }
619 else
620 {
621 m_PacketChunkUnpacker.Reset();
622 }
623 }
624
625 // TODO: empty the recvinfo
626 NETADDR Addr;
627 unsigned char *pData;
628 int Bytes = net_udp_recv(sock: m_Socket, addr: &Addr, data: &pData);
629
630 // no more packets for now
631 if(Bytes <= 0)
632 break;
633
634 // check if we just should drop the packet
635 char aBuf[128];
636 if(NetBan() && NetBan()->IsBanned(pOrigAddr: &Addr, pBuf: aBuf, BufferSize: sizeof(aBuf)))
637 {
638 // banned, reply with a message
639 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);
640 continue;
641 }
642
643 // Check size and unpack packet flags early so we can determine the sixup
644 // state correctly for connection-oriented packets before unpacking them.
645 std::optional<int> Flags = CNetBase::UnpackPacketFlags(pBuffer: pData, Size: Bytes);
646 if(!Flags)
647 {
648 continue;
649 }
650
651 SECURITY_TOKEN Token;
652 int Slot = (*Flags & NET_PACKETFLAG_CONNLESS) == 0 ? GetClientSlot(Addr) : -1;
653 bool Sixup = Slot != -1 && m_aSlots[Slot].m_Connection.m_Sixup;
654 if(CNetBase::UnpackPacket(pBuffer: pData, Size: Bytes, pPacket: &m_RecvBuffer, Sixup, pSecurityToken: &Token, pResponseToken) == 0)
655 {
656 if(m_RecvBuffer.m_Flags & NET_PACKETFLAG_CONNLESS)
657 {
658 if(Sixup && Token != GetToken(Addr) && Token != GetGlobalToken())
659 {
660 continue;
661 }
662
663 pChunk->m_Flags = NETSENDFLAG_CONNLESS;
664 pChunk->m_ClientId = -1;
665 pChunk->m_Address = Addr;
666 pChunk->m_DataSize = m_RecvBuffer.m_DataSize;
667 pChunk->m_pData = m_RecvBuffer.m_aChunkData;
668 if(m_RecvBuffer.m_Flags & NET_PACKETFLAG_EXTENDED)
669 {
670 pChunk->m_Flags |= NETSENDFLAG_EXTENDED;
671 mem_copy(dest: pChunk->m_aExtraData, source: m_RecvBuffer.m_aExtraData, size: sizeof(pChunk->m_aExtraData));
672 }
673 return 1;
674 }
675 else // connection-oriented packet
676 {
677 if(Slot != -1) // connection found
678 {
679 const bool Control = (m_RecvBuffer.m_Flags & NET_PACKETFLAG_CONTROL) != 0;
680 if(Control)
681 {
682 OnConnCtrlMsg(Addr, ClientId: Slot, ControlMsg: m_RecvBuffer.m_aChunkData[0], Packet: m_RecvBuffer);
683 }
684
685 if(m_aSlots[Slot].m_Connection.Feed(pPacket: &m_RecvBuffer, pAddr: &Addr, SecurityToken: Token, ResponseToken: *pResponseToken))
686 {
687 if(!Control &&
688 m_RecvBuffer.m_DataSize > 0 &&
689 m_RecvBuffer.m_NumChunks > 0)
690 {
691 m_PacketChunkUnpacker.FeedPacket(Addr, Packet: m_RecvBuffer, pConnection: &m_aSlots[Slot].m_Connection, ClientId: Slot);
692 }
693 }
694 }
695 else // connection not found, client that wants to connect
696 {
697 if(Sixup)
698 {
699 // got 0.7 control msg
700 if(OnSixupCtrlMsg(Addr, pChunk, ControlMsg: m_RecvBuffer.m_aChunkData[0], Packet: m_RecvBuffer, ResponseToken&: *pResponseToken, Token) == 1)
701 return 1;
702 }
703 else if(IsDDNetControlMsg(pPacket: &m_RecvBuffer))
704 {
705 // got ddnet control msg
706 OnTokenCtrlMsg(Addr, ControlMsg: m_RecvBuffer.m_aChunkData[0], Packet: m_RecvBuffer);
707 }
708 else
709 {
710 // got connection-less ctrl or sys msg
711 OnPreConnMsg(Addr, Packet&: m_RecvBuffer);
712 }
713 }
714 }
715 }
716 }
717 return 0;
718}
719
720int CNetServer::Send(CNetChunk *pChunk)
721{
722 pChunk->AssertSizeSanity();
723
724 if(pChunk->m_Flags & NETSENDFLAG_CONNLESS)
725 {
726 // send connectionless packet
727 CNetBase::SendPacketConnless(Socket: m_Socket, pAddr: &pChunk->m_Address, pData: pChunk->m_pData, DataSize: pChunk->m_DataSize,
728 Extended: pChunk->m_Flags & NETSENDFLAG_EXTENDED, aExtra: pChunk->m_aExtraData);
729 }
730 else
731 {
732 int Flags = 0;
733 dbg_assert(
734 pChunk->m_ClientId >= 0 && pChunk->m_ClientId < MaxClients(),
735 "Invalid pChunk->m_ClientId: %d",
736 pChunk->m_ClientId);
737
738 if(pChunk->m_Flags & NETSENDFLAG_VITAL)
739 Flags = NET_CHUNKFLAG_VITAL;
740
741 if(m_aSlots[pChunk->m_ClientId].m_Connection.QueueChunk(Flags, DataSize: pChunk->m_DataSize, pData: pChunk->m_pData) == 0)
742 {
743 if(pChunk->m_Flags & NETSENDFLAG_FLUSH)
744 {
745 if(m_FlushBatch)
746 m_aFlushPending[pChunk->m_ClientId] = true;
747 else
748 m_aSlots[pChunk->m_ClientId].m_Connection.Flush();
749 }
750 }
751 }
752 return 0;
753}
754
755void CNetServer::SendTokenSixup(NETADDR &Addr, SECURITY_TOKEN Token)
756{
757 unsigned char aRequestTokenBuf[NET_TOKENREQUEST_DATASIZE] = {};
758 WriteSecurityToken(pData: aRequestTokenBuf, Token: GetToken(Addr));
759 const int Size = Token == NET_SECURITY_TOKEN_UNKNOWN ? sizeof(aRequestTokenBuf) : sizeof(SECURITY_TOKEN);
760 CNetBase::SendControlMsg(Socket: m_Socket, pAddr: &Addr, Ack: 0, ControlMsg: protocol7::NET_CTRLMSG_TOKEN, pExtra: aRequestTokenBuf, ExtraSize: Size, SecurityToken: Token, Sixup: true);
761}
762
763void CNetServer::SetMaxClientsPerIp(int Max)
764{
765 m_MaxClientsPerIp = std::clamp<int>(val: Max, lo: 1, hi: NET_MAX_CLIENTS);
766}
767
768bool CNetServer::HasErrored(int ClientId)
769{
770 return m_aSlots[ClientId].m_Connection.State() == CNetConnection::EState::ERROR;
771}
772
773void CNetServer::ResumeOldConnection(int ClientId, int OrigId)
774{
775 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);
776 m_aSlots[OrigId].m_Connection.Reset();
777}
778
779void CNetServer::IgnoreTimeouts(int ClientId)
780{
781 m_aSlots[ClientId].m_Connection.m_TimeoutProtected = true;
782}
783
784void CNetServer::ResetErrorString(int ClientId)
785{
786 m_aSlots[ClientId].m_Connection.ResetErrorString();
787}
788
789const char *CNetServer::ErrorString(int ClientId)
790{
791 return m_aSlots[ClientId].m_Connection.ErrorString();
792}
793