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 | #ifndef ENGINE_SHARED_NETWORK_H |
4 | #define ENGINE_SHARED_NETWORK_H |
5 | |
6 | #include "ringbuffer.h" |
7 | #include "stun.h" |
8 | |
9 | #include <base/math.h> |
10 | #include <base/system.h> |
11 | |
12 | class CHuffman; |
13 | class CNetBan; |
14 | class CPacker; |
15 | |
16 | /* |
17 | |
18 | CURRENT: |
19 | packet header: 3 bytes |
20 | unsigned char flags_ack; // 6bit flags, 2bit ack |
21 | 0.6: ORNCaaAA |
22 | 0.6.5: ORNCTUAA |
23 | 0.7: --NORCAA |
24 | |
25 | unsigned char ack; // 8 bit ack |
26 | unsigned char num_chunks; // 8 bit chunks |
27 | |
28 | (unsigned char padding[3]) // 24 bit extra in case it's a connection less packet |
29 | // this is to make sure that it's compatible with the |
30 | // old protocol |
31 | |
32 | chunk header: 2-3 bytes |
33 | unsigned char flags_size; // 2bit flags, 6 bit size |
34 | unsigned char size_seq; // 4bit size, 4bit seq |
35 | (unsigned char seq;) // 8bit seq, if vital flag is set |
36 | */ |
37 | |
38 | enum |
39 | { |
40 | NETFLAG_ALLOWSTATELESS = 1, |
41 | NETSENDFLAG_VITAL = 1, |
42 | NETSENDFLAG_CONNLESS = 2, |
43 | NETSENDFLAG_FLUSH = 4, |
44 | NETSENDFLAG_EXTENDED = 8, |
45 | |
46 | NETSTATE_OFFLINE = 0, |
47 | NETSTATE_CONNECTING, |
48 | NETSTATE_ONLINE, |
49 | |
50 | NETBANTYPE_SOFT = 1, |
51 | NETBANTYPE_DROP = 2 |
52 | }; |
53 | |
54 | enum |
55 | { |
56 | NET_VERSION = 2, |
57 | |
58 | NET_MAX_PACKETSIZE = 1400, |
59 | NET_MAX_PAYLOAD = NET_MAX_PACKETSIZE - 6, |
60 | = 3, |
61 | = 3, |
62 | NET_MAX_CLIENTS = 64, |
63 | NET_MAX_CONSOLE_CLIENTS = 4, |
64 | NET_MAX_SEQUENCE = 1 << 10, |
65 | NET_SEQUENCE_MASK = NET_MAX_SEQUENCE - 1, |
66 | |
67 | NET_CONNSTATE_OFFLINE = 0, |
68 | NET_CONNSTATE_CONNECT = 1, |
69 | NET_CONNSTATE_PENDING = 2, |
70 | NET_CONNSTATE_ONLINE = 3, |
71 | NET_CONNSTATE_ERROR = 4, |
72 | |
73 | NET_PACKETFLAG_UNUSED = 1 << 0, |
74 | NET_PACKETFLAG_TOKEN = 1 << 1, |
75 | NET_PACKETFLAG_CONTROL = 1 << 2, |
76 | NET_PACKETFLAG_CONNLESS = 1 << 3, |
77 | NET_PACKETFLAG_RESEND = 1 << 4, |
78 | NET_PACKETFLAG_COMPRESSION = 1 << 5, |
79 | // NOT SENT VIA THE NETWORK DIRECTLY: |
80 | NET_PACKETFLAG_EXTENDED = 1 << 6, |
81 | |
82 | NET_CHUNKFLAG_VITAL = 1, |
83 | NET_CHUNKFLAG_RESEND = 2, |
84 | |
85 | NET_CTRLMSG_KEEPALIVE = 0, |
86 | NET_CTRLMSG_CONNECT = 1, |
87 | NET_CTRLMSG_CONNECTACCEPT = 2, |
88 | NET_CTRLMSG_ACCEPT = 3, |
89 | NET_CTRLMSG_CLOSE = 4, |
90 | |
91 | NET_CONN_BUFFERSIZE = 1024 * 32, |
92 | |
93 | NET_CONNLIMIT_IPS = 16, |
94 | |
95 | NET_ENUM_TERMINATOR |
96 | }; |
97 | |
98 | typedef int SECURITY_TOKEN; |
99 | |
100 | SECURITY_TOKEN ToSecurityToken(unsigned char *pData); |
101 | |
102 | extern const unsigned char SECURITY_TOKEN_MAGIC[4]; |
103 | |
104 | enum |
105 | { |
106 | NET_SECURITY_TOKEN_UNKNOWN = -1, |
107 | NET_SECURITY_TOKEN_UNSUPPORTED = 0, |
108 | }; |
109 | |
110 | typedef int (*NETFUNC_DELCLIENT)(int ClientId, const char *pReason, void *pUser); |
111 | typedef int (*NETFUNC_NEWCLIENT_CON)(int ClientId, void *pUser); |
112 | typedef int (*NETFUNC_NEWCLIENT)(int ClientId, void *pUser, bool Sixup); |
113 | typedef int (*NETFUNC_NEWCLIENT_NOAUTH)(int ClientId, void *pUser); |
114 | typedef int (*NETFUNC_CLIENTREJOIN)(int ClientId, void *pUser); |
115 | |
116 | struct CNetChunk |
117 | { |
118 | // -1 means that it's a stateless packet |
119 | // 0 on the client means the server |
120 | int m_ClientId; |
121 | NETADDR m_Address; // only used when client_id == -1 |
122 | int m_Flags; |
123 | int m_DataSize; |
124 | const void *m_pData; |
125 | // only used if the flags contain NETSENDFLAG_EXTENDED and NETSENDFLAG_CONNLESS |
126 | unsigned char [4]; |
127 | }; |
128 | |
129 | class |
130 | { |
131 | public: |
132 | int ; |
133 | int ; |
134 | int ; |
135 | |
136 | unsigned char *(unsigned char *pData, int Split = 4) const; |
137 | unsigned char *(unsigned char *pData, int Split = 4); |
138 | }; |
139 | |
140 | class CNetChunkResend |
141 | { |
142 | public: |
143 | int m_Flags; |
144 | int m_DataSize; |
145 | unsigned char *m_pData; |
146 | |
147 | int m_Sequence; |
148 | int64_t m_LastSendTime; |
149 | int64_t m_FirstSendTime; |
150 | }; |
151 | |
152 | class CNetPacketConstruct |
153 | { |
154 | public: |
155 | int m_Flags; |
156 | int m_Ack; |
157 | int m_NumChunks; |
158 | int m_DataSize; |
159 | unsigned char m_aChunkData[NET_MAX_PAYLOAD]; |
160 | unsigned char [4]; |
161 | }; |
162 | |
163 | enum class CONNECTIVITY |
164 | { |
165 | UNKNOWN, |
166 | CHECKING, |
167 | UNREACHABLE, |
168 | REACHABLE, |
169 | ADDRESS_KNOWN, |
170 | }; |
171 | |
172 | class CStun |
173 | { |
174 | class CProtocol |
175 | { |
176 | int m_Index; |
177 | NETSOCKET m_Socket; |
178 | CStunData m_Stun; |
179 | bool m_HaveStunServer = false; |
180 | NETADDR m_StunServer; |
181 | bool m_HaveAddr = false; |
182 | NETADDR m_Addr; |
183 | int64_t m_LastResponse = -1; |
184 | int64_t m_NextTry = -1; |
185 | int m_NumUnsuccessfulTries = -1; |
186 | |
187 | public: |
188 | CProtocol(int Index, NETSOCKET Socket); |
189 | void FeedStunServer(NETADDR StunServer); |
190 | void Refresh(); |
191 | void Update(); |
192 | bool OnPacket(NETADDR Addr, unsigned char *pData, int DataSize); |
193 | CONNECTIVITY GetConnectivity(NETADDR *pGlobalAddr); |
194 | }; |
195 | CProtocol m_aProtocols[2]; |
196 | |
197 | public: |
198 | CStun(NETSOCKET Socket); |
199 | void FeedStunServer(NETADDR StunServer); |
200 | void Refresh(); |
201 | void Update(); |
202 | bool OnPacket(NETADDR Addr, unsigned char *pData, int DataSize); |
203 | CONNECTIVITY GetConnectivity(int NetType, NETADDR *pGlobalAddr); |
204 | }; |
205 | |
206 | class CNetConnection |
207 | { |
208 | // TODO: is this needed because this needs to be aware of |
209 | // the ack sequencing number and is also responible for updating |
210 | // that. this should be fixed. |
211 | friend class CNetRecvUnpacker; |
212 | |
213 | private: |
214 | unsigned short m_Sequence; |
215 | unsigned short m_Ack; |
216 | unsigned short m_PeerAck; |
217 | unsigned m_State; |
218 | |
219 | SECURITY_TOKEN m_SecurityToken; |
220 | int m_RemoteClosed; |
221 | bool m_BlockCloseMsg; |
222 | bool m_UnknownSeq; |
223 | |
224 | CStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> m_Buffer; |
225 | |
226 | int64_t m_LastUpdateTime; |
227 | int64_t m_LastRecvTime; |
228 | int64_t m_LastSendTime; |
229 | |
230 | char m_aErrorString[256]; |
231 | |
232 | CNetPacketConstruct m_Construct; |
233 | |
234 | NETADDR m_aConnectAddrs[16]; |
235 | int m_NumConnectAddrs; |
236 | NETADDR m_PeerAddr; |
237 | NETSOCKET m_Socket; |
238 | NETSTATS m_Stats; |
239 | |
240 | // |
241 | void ResetStats(); |
242 | void SetError(const char *pString); |
243 | void AckChunks(int Ack); |
244 | |
245 | int QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence); |
246 | void SendConnect(); |
247 | void SendControl(int ControlMsg, const void *, int ); |
248 | void ResendChunk(CNetChunkResend *pResend); |
249 | void Resend(); |
250 | |
251 | public: |
252 | bool m_TimeoutProtected; |
253 | bool m_TimeoutSituation; |
254 | |
255 | void Reset(bool Rejoin = false); |
256 | void Init(NETSOCKET Socket, bool BlockCloseMsg); |
257 | int Connect(const NETADDR *pAddr, int NumAddrs); |
258 | void Disconnect(const char *pReason); |
259 | |
260 | int Update(); |
261 | int Flush(); |
262 | |
263 | int Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_TOKEN SecurityToken = NET_SECURITY_TOKEN_UNSUPPORTED); |
264 | int QueueChunk(int Flags, int DataSize, const void *pData); |
265 | |
266 | const char *ErrorString(); |
267 | void SignalResend(); |
268 | int State() const { return m_State; } |
269 | const NETADDR *PeerAddress() const { return &m_PeerAddr; } |
270 | void ConnectAddresses(const NETADDR **ppAddrs, int *pNumAddrs) const |
271 | { |
272 | *ppAddrs = m_aConnectAddrs; |
273 | *pNumAddrs = m_NumConnectAddrs; |
274 | } |
275 | |
276 | void ResetErrorString() { m_aErrorString[0] = 0; } |
277 | const char *ErrorString() const { return m_aErrorString; } |
278 | |
279 | // Needed for GotProblems in NetClient |
280 | int64_t LastRecvTime() const { return m_LastRecvTime; } |
281 | int64_t ConnectTime() const { return m_LastUpdateTime; } |
282 | |
283 | int AckSequence() const { return m_Ack; } |
284 | int SeqSequence() const { return m_Sequence; } |
285 | int SecurityToken() const { return m_SecurityToken; } |
286 | CStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> *ResendBuffer() { return &m_Buffer; } |
287 | |
288 | void SetTimedOut(const NETADDR *pAddr, int Sequence, int Ack, SECURITY_TOKEN SecurityToken, CStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> *pResendBuffer, bool Sixup); |
289 | |
290 | // anti spoof |
291 | void DirectInit(const NETADDR &Addr, SECURITY_TOKEN SecurityToken, SECURITY_TOKEN Token, bool Sixup); |
292 | void SetUnknownSeq() { m_UnknownSeq = true; } |
293 | void SetSequence(int Sequence) { m_Sequence = Sequence; } |
294 | |
295 | bool m_Sixup; |
296 | SECURITY_TOKEN m_Token; |
297 | }; |
298 | |
299 | class CConsoleNetConnection |
300 | { |
301 | private: |
302 | int m_State; |
303 | |
304 | NETADDR m_PeerAddr; |
305 | NETSOCKET m_Socket; |
306 | |
307 | char m_aBuffer[NET_MAX_PACKETSIZE]; |
308 | int m_BufferOffset; |
309 | |
310 | char m_aErrorString[256]; |
311 | |
312 | bool m_LineEndingDetected; |
313 | char m_aLineEnding[3]; |
314 | |
315 | public: |
316 | void Init(NETSOCKET Socket, const NETADDR *pAddr); |
317 | void Disconnect(const char *pReason); |
318 | |
319 | int State() const { return m_State; } |
320 | const NETADDR *PeerAddress() const { return &m_PeerAddr; } |
321 | const char *ErrorString() const { return m_aErrorString; } |
322 | |
323 | void Reset(); |
324 | int Update(); |
325 | int Send(const char *pLine); |
326 | int Recv(char *pLine, int MaxLength); |
327 | }; |
328 | |
329 | class CNetRecvUnpacker |
330 | { |
331 | public: |
332 | bool m_Valid; |
333 | |
334 | NETADDR m_Addr; |
335 | CNetConnection *m_pConnection; |
336 | int m_CurrentChunk; |
337 | int m_ClientId; |
338 | CNetPacketConstruct m_Data; |
339 | unsigned char m_aBuffer[NET_MAX_PACKETSIZE]; |
340 | |
341 | CNetRecvUnpacker() { Clear(); } |
342 | void Clear(); |
343 | void Start(const NETADDR *pAddr, CNetConnection *pConnection, int ClientId); |
344 | int FetchChunk(CNetChunk *pChunk); |
345 | }; |
346 | |
347 | // server side |
348 | class CNetServer |
349 | { |
350 | struct CSlot |
351 | { |
352 | public: |
353 | CNetConnection m_Connection; |
354 | }; |
355 | |
356 | struct CSpamConn |
357 | { |
358 | NETADDR m_Addr; |
359 | int64_t m_Time; |
360 | int m_Conns; |
361 | }; |
362 | |
363 | NETADDR m_Address; |
364 | NETSOCKET m_Socket; |
365 | CNetBan *m_pNetBan; |
366 | CSlot m_aSlots[NET_MAX_CLIENTS]; |
367 | int m_MaxClients; |
368 | int m_MaxClientsPerIp; |
369 | |
370 | NETFUNC_NEWCLIENT m_pfnNewClient; |
371 | NETFUNC_NEWCLIENT_NOAUTH m_pfnNewClientNoAuth; |
372 | NETFUNC_DELCLIENT m_pfnDelClient; |
373 | NETFUNC_CLIENTREJOIN m_pfnClientRejoin; |
374 | void *m_pUser; |
375 | |
376 | int m_NumConAttempts; // log flooding attacks |
377 | int64_t ; |
378 | unsigned char m_aSecurityTokenSeed[16]; |
379 | |
380 | // vanilla connect flood detection |
381 | int64_t m_VConnFirst; |
382 | int m_VConnNum; |
383 | |
384 | CSpamConn m_aSpamConns[NET_CONNLIMIT_IPS]; |
385 | |
386 | CNetRecvUnpacker m_RecvUnpacker; |
387 | |
388 | void OnTokenCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketConstruct &Packet); |
389 | int OnSixupCtrlMsg(NETADDR &Addr, CNetChunk *pChunk, int ControlMsg, const CNetPacketConstruct &Packet, SECURITY_TOKEN &ResponseToken, SECURITY_TOKEN Token); |
390 | void OnPreConnMsg(NETADDR &Addr, CNetPacketConstruct &Packet); |
391 | void OnConnCtrlMsg(NETADDR &Addr, int ClientId, int ControlMsg, const CNetPacketConstruct &Packet); |
392 | bool ClientExists(const NETADDR &Addr) { return GetClientSlot(Addr) != -1; } |
393 | int GetClientSlot(const NETADDR &Addr); |
394 | void SendControl(NETADDR &Addr, int ControlMsg, const void *, int , SECURITY_TOKEN SecurityToken); |
395 | |
396 | int TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth = false, bool Sixup = false, SECURITY_TOKEN Token = 0); |
397 | int NumClientsWithAddr(NETADDR Addr); |
398 | bool Connlimit(NETADDR Addr); |
399 | void SendMsgs(NETADDR &Addr, const CPacker **ppMsgs, int Num); |
400 | |
401 | public: |
402 | int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser); |
403 | int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_NEWCLIENT_NOAUTH pfnNewClientNoAuth, NETFUNC_CLIENTREJOIN pfnClientRejoin, NETFUNC_DELCLIENT pfnDelClient, void *pUser); |
404 | |
405 | // |
406 | bool Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int MaxClientsPerIp); |
407 | int Close(); |
408 | |
409 | // |
410 | int Recv(CNetChunk *pChunk, SECURITY_TOKEN *pResponseToken); |
411 | int Send(CNetChunk *pChunk); |
412 | int Update(); |
413 | |
414 | // |
415 | int Drop(int ClientId, const char *pReason); |
416 | |
417 | // status requests |
418 | const NETADDR *ClientAddr(int ClientId) const { return m_aSlots[ClientId].m_Connection.PeerAddress(); } |
419 | bool HasSecurityToken(int ClientId) const { return m_aSlots[ClientId].m_Connection.SecurityToken() != NET_SECURITY_TOKEN_UNSUPPORTED; } |
420 | NETADDR Address() const { return m_Address; } |
421 | NETSOCKET Socket() const { return m_Socket; } |
422 | CNetBan *NetBan() const { return m_pNetBan; } |
423 | int NetType() const { return net_socket_type(sock: m_Socket); } |
424 | int MaxClients() const { return m_MaxClients; } |
425 | |
426 | void SendTokenSixup(NETADDR &Addr, SECURITY_TOKEN Token); |
427 | int SendConnlessSixup(CNetChunk *pChunk, SECURITY_TOKEN ResponseToken); |
428 | |
429 | // |
430 | void SetMaxClientsPerIp(int Max); |
431 | bool SetTimedOut(int ClientId, int OrigId); |
432 | void SetTimeoutProtected(int ClientId); |
433 | |
434 | int ResetErrorString(int ClientId); |
435 | const char *ErrorString(int ClientId); |
436 | |
437 | // anti spoof |
438 | SECURITY_TOKEN GetGlobalToken(); |
439 | SECURITY_TOKEN GetToken(const NETADDR &Addr); |
440 | // vanilla token/gametick shouldn't be negative |
441 | SECURITY_TOKEN GetVanillaToken(const NETADDR &Addr) { return absolute(a: GetToken(Addr)); } |
442 | }; |
443 | |
444 | class CNetConsole |
445 | { |
446 | struct CSlot |
447 | { |
448 | CConsoleNetConnection m_Connection; |
449 | }; |
450 | |
451 | NETSOCKET m_Socket; |
452 | CNetBan *m_pNetBan; |
453 | CSlot m_aSlots[NET_MAX_CONSOLE_CLIENTS]; |
454 | |
455 | NETFUNC_NEWCLIENT_CON m_pfnNewClient; |
456 | NETFUNC_DELCLIENT m_pfnDelClient; |
457 | void *m_pUser; |
458 | |
459 | CNetRecvUnpacker m_RecvUnpacker; |
460 | |
461 | public: |
462 | void SetCallbacks(NETFUNC_NEWCLIENT_CON pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser); |
463 | |
464 | // |
465 | bool Open(NETADDR BindAddr, CNetBan *pNetBan); |
466 | int Close(); |
467 | |
468 | // |
469 | int Recv(char *pLine, int MaxLength, int *pClientId = nullptr); |
470 | int Send(int ClientId, const char *pLine); |
471 | int Update(); |
472 | |
473 | // |
474 | int AcceptClient(NETSOCKET Socket, const NETADDR *pAddr); |
475 | int Drop(int ClientId, const char *pReason); |
476 | |
477 | // status requests |
478 | const NETADDR *ClientAddr(int ClientId) const { return m_aSlots[ClientId].m_Connection.PeerAddress(); } |
479 | CNetBan *NetBan() const { return m_pNetBan; } |
480 | }; |
481 | |
482 | // client side |
483 | class CNetClient |
484 | { |
485 | CNetConnection m_Connection; |
486 | CNetRecvUnpacker m_RecvUnpacker; |
487 | |
488 | CStun *m_pStun = nullptr; |
489 | |
490 | public: |
491 | NETSOCKET m_Socket; |
492 | // openness |
493 | bool Open(NETADDR BindAddr); |
494 | int Close(); |
495 | |
496 | // connection state |
497 | int Disconnect(const char *pReason); |
498 | int Connect(const NETADDR *pAddr, int NumAddrs); |
499 | |
500 | // communication |
501 | int Recv(CNetChunk *pChunk); |
502 | int Send(CNetChunk *pChunk); |
503 | |
504 | // pumping |
505 | int Update(); |
506 | int Flush(); |
507 | |
508 | int ResetErrorString(); |
509 | |
510 | // error and state |
511 | int NetType() const { return net_socket_type(sock: m_Socket); } |
512 | int State(); |
513 | const NETADDR *ServerAddress() const { return m_Connection.PeerAddress(); } |
514 | void ConnectAddresses(const NETADDR **ppAddrs, int *pNumAddrs) const { m_Connection.ConnectAddresses(ppAddrs, pNumAddrs); } |
515 | int GotProblems(int64_t MaxLatency) const; |
516 | const char *ErrorString() const; |
517 | |
518 | // stun |
519 | void FeedStunServer(NETADDR StunServer); |
520 | void RefreshStun(); |
521 | CONNECTIVITY GetConnectivity(int NetType, NETADDR *pGlobalAddr); |
522 | }; |
523 | |
524 | // TODO: both, fix these. This feels like a junk class for stuff that doesn't fit anywhere |
525 | class CNetBase |
526 | { |
527 | static IOHANDLE ms_DataLogSent; |
528 | static IOHANDLE ms_DataLogRecv; |
529 | static CHuffman ms_Huffman; |
530 | |
531 | public: |
532 | static void OpenLog(IOHANDLE DataLogSent, IOHANDLE DataLogRecv); |
533 | static void CloseLog(); |
534 | static void Init(); |
535 | static int Compress(const void *pData, int DataSize, void *pOutput, int OutputSize); |
536 | static int Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize); |
537 | |
538 | static void SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *, int , SECURITY_TOKEN SecurityToken, bool Sixup = false); |
539 | static void SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize, bool Extended, unsigned char [4]); |
540 | static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken, bool Sixup = false, bool NoCompress = false); |
541 | |
542 | static int UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket, bool &Sixup, SECURITY_TOKEN *pSecurityToken = nullptr, SECURITY_TOKEN *pResponseToken = nullptr); |
543 | |
544 | // The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not |
545 | static bool IsSeqInBackroom(int Seq, int Ack); |
546 | }; |
547 | |
548 | #endif |
549 | |