1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#include <base/system.h>
4
5#include "config.h"
6#include "huffman.h"
7#include "network.h"
8
9const unsigned char SECURITY_TOKEN_MAGIC[4] = {'T', 'K', 'E', 'N'};
10
11SECURITY_TOKEN ToSecurityToken(const unsigned char *pData)
12{
13 return bytes_be_to_uint(bytes: pData);
14}
15
16void WriteSecurityToken(unsigned char *pData, SECURITY_TOKEN Token)
17{
18 uint_to_bytes_be(bytes: pData, value: Token);
19}
20
21void CNetRecvUnpacker::Clear()
22{
23 m_Valid = false;
24}
25
26void CNetRecvUnpacker::Start(const NETADDR *pAddr, CNetConnection *pConnection, int ClientId)
27{
28 m_Addr = *pAddr;
29 m_pConnection = pConnection;
30 m_ClientId = ClientId;
31 m_CurrentChunk = 0;
32 m_Valid = true;
33}
34
35// TODO: rename this function
36int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk)
37{
38 CNetChunkHeader Header;
39 unsigned char *pEnd = m_Data.m_aChunkData + m_Data.m_DataSize;
40
41 while(true)
42 {
43 unsigned char *pData = m_Data.m_aChunkData;
44
45 // check for old data to unpack
46 if(!m_Valid || m_CurrentChunk >= m_Data.m_NumChunks)
47 {
48 Clear();
49 return 0;
50 }
51
52 // TODO: add checking here so we don't read too far
53 for(int i = 0; i < m_CurrentChunk; i++)
54 {
55 pData = Header.Unpack(pData, Split: (m_pConnection && m_pConnection->m_Sixup) ? 6 : 4);
56 pData += Header.m_Size;
57 }
58
59 // unpack the header
60 pData = Header.Unpack(pData, Split: (m_pConnection && m_pConnection->m_Sixup) ? 6 : 4);
61 m_CurrentChunk++;
62
63 if(pData + Header.m_Size > pEnd)
64 {
65 Clear();
66 return 0;
67 }
68
69 // handle sequence stuff
70 if(m_pConnection && (Header.m_Flags & NET_CHUNKFLAG_VITAL))
71 {
72 // anti spoof: ignore unknown sequence
73 if(Header.m_Sequence == (m_pConnection->m_Ack + 1) % NET_MAX_SEQUENCE || m_pConnection->m_UnknownSeq)
74 {
75 m_pConnection->m_UnknownSeq = false;
76
77 // in sequence
78 m_pConnection->m_Ack = Header.m_Sequence;
79 }
80 else
81 {
82 // old packet that we already got
83 if(CNetBase::IsSeqInBackroom(Seq: Header.m_Sequence, Ack: m_pConnection->m_Ack))
84 continue;
85
86 // out of sequence, request resend
87 if(g_Config.m_Debug)
88 dbg_msg(sys: "conn", fmt: "asking for resend %d %d", Header.m_Sequence, (m_pConnection->m_Ack + 1) % NET_MAX_SEQUENCE);
89 m_pConnection->SignalResend();
90 continue; // take the next chunk in the packet
91 }
92 }
93
94 // fill in the info
95 pChunk->m_ClientId = m_ClientId;
96 pChunk->m_Address = m_Addr;
97 pChunk->m_Flags = Header.m_Flags;
98 pChunk->m_DataSize = Header.m_Size;
99 pChunk->m_pData = pData;
100 return 1;
101 }
102}
103
104static const unsigned char NET_HEADER_EXTENDED[] = {'x', 'e'};
105// packs the data tight and sends it
106void CNetBase::SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize, bool Extended, unsigned char aExtra[4])
107{
108 unsigned char aBuffer[NET_MAX_PACKETSIZE];
109 const int DATA_OFFSET = 6;
110 if(!Extended)
111 {
112 for(int i = 0; i < DATA_OFFSET; i++)
113 aBuffer[i] = 0xff;
114 }
115 else
116 {
117 mem_copy(dest: aBuffer, source: NET_HEADER_EXTENDED, size: sizeof(NET_HEADER_EXTENDED));
118 mem_copy(dest: aBuffer + sizeof(NET_HEADER_EXTENDED), source: aExtra, size: 4);
119 }
120 mem_copy(dest: aBuffer + DATA_OFFSET, source: pData, size: DataSize);
121 net_udp_send(sock: Socket, addr: pAddr, data: aBuffer, size: DataSize + DATA_OFFSET);
122}
123
124void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken, bool Sixup, bool NoCompress)
125{
126 unsigned char aBuffer[NET_MAX_PACKETSIZE];
127 int CompressedSize = -1;
128 int FinalSize = -1;
129
130 // log the data
131 if(ms_DataLogSent)
132 {
133 int Type = 1;
134 io_write(io: ms_DataLogSent, buffer: &Type, size: sizeof(Type));
135 io_write(io: ms_DataLogSent, buffer: &pPacket->m_DataSize, size: sizeof(pPacket->m_DataSize));
136 io_write(io: ms_DataLogSent, buffer: &pPacket->m_aChunkData, size: pPacket->m_DataSize);
137 io_flush(io: ms_DataLogSent);
138 }
139
140 int HeaderSize = NET_PACKETHEADERSIZE;
141 if(Sixup)
142 {
143 HeaderSize += sizeof(SecurityToken);
144 WriteSecurityToken(pData: aBuffer + 3, Token: SecurityToken);
145 }
146 else if(SecurityToken != NET_SECURITY_TOKEN_UNSUPPORTED)
147 {
148 // append security token
149 // if SecurityToken is NET_SECURITY_TOKEN_UNKNOWN we will still append it hoping to negotiate it
150 WriteSecurityToken(pData: pPacket->m_aChunkData + pPacket->m_DataSize, Token: SecurityToken);
151 pPacket->m_DataSize += sizeof(SecurityToken);
152 }
153
154 // compress
155 if(!NoCompress)
156 CompressedSize = ms_Huffman.Compress(pInput: pPacket->m_aChunkData, InputSize: pPacket->m_DataSize, pOutput: &aBuffer[HeaderSize], OutputSize: NET_MAX_PACKETSIZE - HeaderSize);
157
158 // check if the compression was enabled, successful and good enough
159 if(!NoCompress && CompressedSize > 0 && CompressedSize < pPacket->m_DataSize)
160 {
161 FinalSize = CompressedSize;
162 pPacket->m_Flags |= NET_PACKETFLAG_COMPRESSION;
163 }
164 else
165 {
166 // use uncompressed data
167 FinalSize = pPacket->m_DataSize;
168 mem_copy(dest: &aBuffer[HeaderSize], source: pPacket->m_aChunkData, size: pPacket->m_DataSize);
169 pPacket->m_Flags &= ~NET_PACKETFLAG_COMPRESSION;
170 }
171
172 if(Sixup)
173 {
174 unsigned Flags = 0;
175 if(pPacket->m_Flags & NET_PACKETFLAG_CONTROL)
176 Flags |= 1;
177 if(pPacket->m_Flags & NET_PACKETFLAG_RESEND)
178 Flags |= 2;
179 if(pPacket->m_Flags & NET_PACKETFLAG_COMPRESSION)
180 Flags |= 4;
181 pPacket->m_Flags = Flags;
182 }
183
184 // set header and send the packet if all things are good
185 if(FinalSize >= 0)
186 {
187 FinalSize += HeaderSize;
188 aBuffer[0] = ((pPacket->m_Flags << 2) & 0xfc) | ((pPacket->m_Ack >> 8) & 0x3);
189 aBuffer[1] = pPacket->m_Ack & 0xff;
190 aBuffer[2] = pPacket->m_NumChunks;
191 net_udp_send(sock: Socket, addr: pAddr, data: aBuffer, size: FinalSize);
192
193 // log raw socket data
194 if(ms_DataLogSent)
195 {
196 int Type = 0;
197 io_write(io: ms_DataLogSent, buffer: &Type, size: sizeof(Type));
198 io_write(io: ms_DataLogSent, buffer: &FinalSize, size: sizeof(FinalSize));
199 io_write(io: ms_DataLogSent, buffer: aBuffer, size: FinalSize);
200 io_flush(io: ms_DataLogSent);
201 }
202 }
203}
204
205// TODO: rename this function
206int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket, bool &Sixup, SECURITY_TOKEN *pSecurityToken, SECURITY_TOKEN *pResponseToken)
207{
208 // check the size
209 if(Size < NET_PACKETHEADERSIZE || Size > NET_MAX_PACKETSIZE)
210 return -1;
211
212 // log the data
213 if(ms_DataLogRecv)
214 {
215 int Type = 0;
216 io_write(io: ms_DataLogRecv, buffer: &Type, size: sizeof(Type));
217 io_write(io: ms_DataLogRecv, buffer: &Size, size: sizeof(Size));
218 io_write(io: ms_DataLogRecv, buffer: pBuffer, size: Size);
219 io_flush(io: ms_DataLogRecv);
220 }
221
222 // read the packet
223 pPacket->m_Flags = pBuffer[0] >> 2;
224
225 if(pPacket->m_Flags & NET_PACKETFLAG_CONNLESS)
226 {
227 Sixup = (pBuffer[0] & 0x3) == 1;
228 if(Sixup && (pSecurityToken == nullptr || pResponseToken == nullptr))
229 return -1;
230 int Offset = Sixup ? 9 : 6;
231 if(Size < Offset)
232 return -1;
233
234 if(Sixup)
235 {
236 *pSecurityToken = ToSecurityToken(pData: pBuffer + 1);
237 *pResponseToken = ToSecurityToken(pData: pBuffer + 5);
238 }
239
240 pPacket->m_Flags = NET_PACKETFLAG_CONNLESS;
241 pPacket->m_Ack = 0;
242 pPacket->m_NumChunks = 0;
243 pPacket->m_DataSize = Size - Offset;
244 mem_copy(dest: pPacket->m_aChunkData, source: pBuffer + Offset, size: pPacket->m_DataSize);
245
246 if(!Sixup && mem_comp(a: pBuffer, b: NET_HEADER_EXTENDED, size: sizeof(NET_HEADER_EXTENDED)) == 0)
247 {
248 pPacket->m_Flags |= NET_PACKETFLAG_EXTENDED;
249 mem_copy(dest: pPacket->m_aExtraData, source: pBuffer + sizeof(NET_HEADER_EXTENDED), size: sizeof(pPacket->m_aExtraData));
250 }
251 }
252 else
253 {
254 if(pPacket->m_Flags & NET_PACKETFLAG_UNUSED)
255 Sixup = true;
256 if(Sixup && pSecurityToken == nullptr)
257 return -1;
258 int DataStart = Sixup ? 7 : NET_PACKETHEADERSIZE;
259 if(Size < DataStart)
260 return -1;
261
262 pPacket->m_Ack = ((pBuffer[0] & 0x3) << 8) | pBuffer[1];
263 pPacket->m_NumChunks = pBuffer[2];
264 pPacket->m_DataSize = Size - DataStart;
265
266 if(Sixup)
267 {
268 unsigned Flags = 0;
269 if(pPacket->m_Flags & 1)
270 Flags |= NET_PACKETFLAG_CONTROL;
271 if(pPacket->m_Flags & 2)
272 Flags |= NET_PACKETFLAG_RESEND;
273 if(pPacket->m_Flags & 4)
274 Flags |= NET_PACKETFLAG_COMPRESSION;
275 pPacket->m_Flags = Flags;
276
277 *pSecurityToken = ToSecurityToken(pData: pBuffer + 3);
278 }
279
280 if(pPacket->m_Flags & NET_PACKETFLAG_COMPRESSION)
281 {
282 // Don't allow compressed control packets.
283 if(pPacket->m_Flags & NET_PACKETFLAG_CONTROL)
284 {
285 return -1;
286 }
287 pPacket->m_DataSize = ms_Huffman.Decompress(pInput: &pBuffer[DataStart], InputSize: pPacket->m_DataSize, pOutput: pPacket->m_aChunkData, OutputSize: sizeof(pPacket->m_aChunkData));
288 }
289 else
290 mem_copy(dest: pPacket->m_aChunkData, source: &pBuffer[DataStart], size: pPacket->m_DataSize);
291 }
292
293 // check for errors
294 if(pPacket->m_DataSize < 0)
295 {
296 if(g_Config.m_Debug)
297 dbg_msg(sys: "network", fmt: "error during packet decoding");
298 return -1;
299 }
300
301 // log the data
302 if(ms_DataLogRecv)
303 {
304 int Type = 1;
305 io_write(io: ms_DataLogRecv, buffer: &Type, size: sizeof(Type));
306 io_write(io: ms_DataLogRecv, buffer: &pPacket->m_DataSize, size: sizeof(pPacket->m_DataSize));
307 io_write(io: ms_DataLogRecv, buffer: pPacket->m_aChunkData, size: pPacket->m_DataSize);
308 io_flush(io: ms_DataLogRecv);
309 }
310
311 // return success
312 return 0;
313}
314
315void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken, bool Sixup)
316{
317 CNetPacketConstruct Construct;
318 Construct.m_Flags = NET_PACKETFLAG_CONTROL;
319 Construct.m_Ack = Ack;
320 Construct.m_NumChunks = 0;
321 Construct.m_DataSize = 1 + ExtraSize;
322 Construct.m_aChunkData[0] = ControlMsg;
323 if(pExtra)
324 mem_copy(dest: &Construct.m_aChunkData[1], source: pExtra, size: ExtraSize);
325
326 // send the control message
327 CNetBase::SendPacket(Socket, pAddr, pPacket: &Construct, SecurityToken, Sixup, NoCompress: true);
328}
329
330unsigned char *CNetChunkHeader::Pack(unsigned char *pData, int Split) const
331{
332 pData[0] = ((m_Flags & 3) << 6) | ((m_Size >> Split) & 0x3f);
333 pData[1] = (m_Size & ((1 << Split) - 1));
334 if(m_Flags & NET_CHUNKFLAG_VITAL)
335 {
336 pData[1] |= (m_Sequence >> 2) & (~((1 << Split) - 1));
337 pData[2] = m_Sequence & 0xff;
338 return pData + 3;
339 }
340 return pData + 2;
341}
342
343unsigned char *CNetChunkHeader::Unpack(unsigned char *pData, int Split)
344{
345 m_Flags = (pData[0] >> 6) & 3;
346 m_Size = ((pData[0] & 0x3f) << Split) | (pData[1] & ((1 << Split) - 1));
347 m_Sequence = -1;
348 if(m_Flags & NET_CHUNKFLAG_VITAL)
349 {
350 m_Sequence = ((pData[1] & (~((1 << Split) - 1))) << 2) | pData[2];
351 return pData + 3;
352 }
353 return pData + 2;
354}
355
356bool CNetBase::IsSeqInBackroom(int Seq, int Ack)
357{
358 int Bottom = (Ack - NET_MAX_SEQUENCE / 2);
359 if(Bottom < 0)
360 {
361 if(Seq <= Ack)
362 return true;
363 if(Seq >= (Bottom + NET_MAX_SEQUENCE))
364 return true;
365 }
366 else
367 {
368 if(Seq <= Ack && Seq >= Bottom)
369 return true;
370 }
371
372 return false;
373}
374
375IOHANDLE CNetBase::ms_DataLogSent = 0;
376IOHANDLE CNetBase::ms_DataLogRecv = 0;
377CHuffman CNetBase::ms_Huffman;
378
379void CNetBase::OpenLog(IOHANDLE DataLogSent, IOHANDLE DataLogRecv)
380{
381 if(DataLogSent)
382 {
383 ms_DataLogSent = DataLogSent;
384 dbg_msg(sys: "network", fmt: "logging sent packages");
385 }
386 else
387 dbg_msg(sys: "network", fmt: "failed to start logging sent packages");
388
389 if(DataLogRecv)
390 {
391 ms_DataLogRecv = DataLogRecv;
392 dbg_msg(sys: "network", fmt: "logging recv packages");
393 }
394 else
395 dbg_msg(sys: "network", fmt: "failed to start logging recv packages");
396}
397
398void CNetBase::CloseLog()
399{
400 if(ms_DataLogSent)
401 {
402 dbg_msg(sys: "network", fmt: "stopped logging sent packages");
403 io_close(io: ms_DataLogSent);
404 ms_DataLogSent = 0;
405 }
406
407 if(ms_DataLogRecv)
408 {
409 dbg_msg(sys: "network", fmt: "stopped logging recv packages");
410 io_close(io: ms_DataLogRecv);
411 ms_DataLogRecv = 0;
412 }
413}
414
415int CNetBase::Compress(const void *pData, int DataSize, void *pOutput, int OutputSize)
416{
417 return ms_Huffman.Compress(pInput: pData, InputSize: DataSize, pOutput, OutputSize);
418}
419
420int CNetBase::Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize)
421{
422 return ms_Huffman.Decompress(pInput: pData, InputSize: DataSize, pOutput, OutputSize);
423}
424
425void CNetBase::Init()
426{
427 ms_Huffman.Init();
428}
429