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 | |
9 | const unsigned char SECURITY_TOKEN_MAGIC[4] = {'T', 'K', 'E', 'N'}; |
10 | |
11 | SECURITY_TOKEN ToSecurityToken(const unsigned char *pData) |
12 | { |
13 | return bytes_be_to_uint(bytes: pData); |
14 | } |
15 | |
16 | void WriteSecurityToken(unsigned char *pData, SECURITY_TOKEN Token) |
17 | { |
18 | uint_to_bytes_be(bytes: pData, value: Token); |
19 | } |
20 | |
21 | void CNetRecvUnpacker::Clear() |
22 | { |
23 | m_Valid = false; |
24 | } |
25 | |
26 | void 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 |
36 | int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk) |
37 | { |
38 | CNetChunkHeader ; |
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 | |
104 | static const unsigned char [] = {'x', 'e'}; |
105 | // packs the data tight and sends it |
106 | void CNetBase::SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize, bool Extended, unsigned char [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 | |
124 | void 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 = 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 |
206 | int 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 | |
315 | void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *, int , 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 | |
330 | unsigned char *CNetChunkHeader::(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 | |
343 | unsigned char *CNetChunkHeader::(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 | |
356 | bool 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 | |
375 | IOHANDLE CNetBase::ms_DataLogSent = 0; |
376 | IOHANDLE CNetBase::ms_DataLogRecv = 0; |
377 | CHuffman CNetBase::ms_Huffman; |
378 | |
379 | void 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 | |
398 | void 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 | |
415 | int 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 | |
420 | int 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 | |
425 | void CNetBase::Init() |
426 | { |
427 | ms_Huffman.Init(); |
428 | } |
429 | |