File: qcommon\net_chan.c
1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20
21 #include "qcommon.h"
22
23 /*
24
25 packet header
26 -------------
27 31 sequence
28 1 does this message contain a reliable payload
29 31 acknowledge sequence
30 1 acknowledge receipt of even/odd message
31 16 qport
32
33 The remote connection never knows if it missed a reliable message, the
34 local side detects that it has been dropped by seeing a sequence acknowledge
35 higher thatn the last reliable sequence, but without the correct evon/odd
36 bit for the reliable set.
37
38 If the sender notices that a reliable message has been dropped, it will be
39 retransmitted. It will not be retransmitted again until a message after
40 the retransmit has been acknowledged and the reliable still failed to get there.
41
42 if the sequence number is -1, the packet should be handled without a netcon
43
44 The reliable message can be added to at any time by doing
45 MSG_Write* (&netchan->message, <data>).
46
47 If the message buffer is overflowed, either by a single message, or by
48 multiple frames worth piling up while the last reliable transmit goes
49 unacknowledged, the netchan signals a fatal error.
50
51 Reliable messages are always placed first in a packet, then the unreliable
52 message is included if there is sufficient room.
53
54 To the receiver, there is no distinction between the reliable and unreliable
55 parts of the message, they are just processed out as a single larger message.
56
57 Illogical packet sequence numbers cause the packet to be dropped, but do
58 not kill the connection. This, combined with the tight window of valid
59 reliable acknowledgement numbers provides protection against malicious
60 address spoofing.
61
62
63 The qport field is a workaround for bad address translating routers that
64 sometimes remap the client's source port on a packet during gameplay.
65
66 If the base part of the net address matches and the qport matches, then the
67 channel matches even if the IP port differs. The IP port should be updated
68 to the new value before sending out any replies.
69
70
71 If there is no information that needs to be transfered on a given frame,
72 such as during the connection stage while waiting for the client to load,
73 then a packet only needs to be delivered if there is something in the
74 unacknowledged reliable
75 */
76
77 cvar_t *showpackets;
78 cvar_t *showdrop;
79 cvar_t *qport;
80
81 netadr_t net_from;
82 sizebuf_t net_message;
83 byte net_message_buffer[MAX_MSGLEN];
84
85 /*
86 ===============
87 Netchan_Init
88
89 ===============
90 */
91 void Netchan_Init (void)
92 {
93 int port;
94
95 // pick a port value that should be nice and random
96 port = Sys_Milliseconds() & 0xffff;
97
98 showpackets = Cvar_Get ("showpackets", "0", 0);
99 showdrop = Cvar_Get ("showdrop", "0", 0);
100 qport = Cvar_Get ("qport", va("%i", port), CVAR_NOSET);
101 }
102
103 /*
104 ===============
105 Netchan_OutOfBand
106
107 Sends an out-of-band datagram
108 ================
109 */
110 void Netchan_OutOfBand (int net_socket, netadr_t adr, int length, byte *data)
111 {
112 sizebuf_t send;
113 byte send_buf[MAX_MSGLEN];
114
115 // write the packet header
116 SZ_Init (&send, send_buf, sizeof(send_buf));
117
118 MSG_WriteLong (&send, -1); // -1 sequence means out of band
119 SZ_Write (&send, data, length);
120
121 // send the datagram
122 NET_SendPacket (net_socket, send.cursize, send.data, adr);
123 }
124
125 /*
126 ===============
127 Netchan_OutOfBandPrint
128
129 Sends a text message in an out-of-band datagram
130 ================
131 */
132 void Netchan_OutOfBandPrint (int net_socket, netadr_t adr, char *format, ...)
133 {
134 va_list argptr;
135 static char string[MAX_MSGLEN - 4];
136
137 va_start (argptr, format);
138 vsprintf (string, format,argptr);
139 va_end (argptr);
140
141 Netchan_OutOfBand (net_socket, adr, strlen(string), (byte *)string);
142 }
143
144
145 /*
146 ==============
147 Netchan_Setup
148
149 called to open a channel to a remote system
150 ==============
151 */
152 void Netchan_Setup (netsrc_t sock, netchan_t *chan, netadr_t adr, int qport)
153 {
154 memset (chan, 0, sizeof(*chan));
155
156 chan->sock = sock;
157 chan->remote_address = adr;
158 chan->qport = qport;
159 chan->last_received = curtime;
160 chan->incoming_sequence = 0;
161 chan->outgoing_sequence = 1;
162
163 SZ_Init (&chan->message, chan->message_buf, sizeof(chan->message_buf));
164 chan->message.allowoverflow = true;
165 }
166
167
168 /*
169 ===============
170 Netchan_CanReliable
171
172 Returns true if the last reliable message has acked
173 ================
174 */
175 qboolean Netchan_CanReliable (netchan_t *chan)
176 {
177 if (chan->reliable_length)
178 return false; // waiting for ack
179 return true;
180 }
181
182
183 qboolean Netchan_NeedReliable (netchan_t *chan)
184 {
185 qboolean send_reliable;
186
187 // if the remote side dropped the last reliable message, resend it
188 send_reliable = false;
189
190 if (chan->incoming_acknowledged > chan->last_reliable_sequence
191 && chan->incoming_reliable_acknowledged != chan->reliable_sequence)
192 send_reliable = true;
193
194 // if the reliable transmit buffer is empty, copy the current message out
195 if (!chan->reliable_length && chan->message.cursize)
196 {
197 send_reliable = true;
198 }
199
200 return send_reliable;
201 }
202
203 /*
204 ===============
205 Netchan_Transmit
206
207 tries to send an unreliable message to a connection, and handles the
208 transmition / retransmition of the reliable messages.
209
210 A 0 length will still generate a packet and deal with the reliable messages.
211 ================
212 */
213 void Netchan_Transmit (netchan_t *chan, int length, byte *data)
214 {
215 sizebuf_t send;
216 byte send_buf[MAX_MSGLEN];
217 qboolean send_reliable;
218 unsigned w1, w2;
219
220 // check for message overflow
221 if (chan->message.overflowed)
222 {
223 chan->fatal_error = true;
224 Com_Printf ("%s:Outgoing message overflow\n"
225 , NET_AdrToString (chan->remote_address));
226 return;
227 }
228
229 send_reliable = Netchan_NeedReliable (chan);
230
231 if (!chan->reliable_length && chan->message.cursize)
232 {
233 memcpy (chan->reliable_buf, chan->message_buf, chan->message.cursize);
234 chan->reliable_length = chan->message.cursize;
235 chan->message.cursize = 0;
236 chan->reliable_sequence ^= 1;
237 }
238
239
240 // write the packet header
241 SZ_Init (&send, send_buf, sizeof(send_buf));
242
243 w1 = ( chan->outgoing_sequence & ~(1<<31) ) | (send_reliable<<31);
244 w2 = ( chan->incoming_sequence & ~(1<<31) ) | (chan->incoming_reliable_sequence<<31);
245
246 chan->outgoing_sequence++;
247 chan->last_sent = curtime;
248
249 MSG_WriteLong (&send, w1);
250 MSG_WriteLong (&send, w2);
251
252 // send the qport if we are a client
253 if (chan->sock == NS_CLIENT)
254 MSG_WriteShort (&send, qport->value);
255
256 // copy the reliable message to the packet first
257 if (send_reliable)
258 {
259 SZ_Write (&send, chan->reliable_buf, chan->reliable_length);
260 chan->last_reliable_sequence = chan->outgoing_sequence;
261 }
262
263 // add the unreliable part if space is available
264 if (send.maxsize - send.cursize >= length)
265 SZ_Write (&send, data, length);
266 else
267 Com_Printf ("Netchan_Transmit: dumped unreliable\n");
268
269 // send the datagram
270 NET_SendPacket (chan->sock, send.cursize, send.data, chan->remote_address);
271
272 if (showpackets->value)
273 {
274 if (send_reliable)
275 Com_Printf ("send %4i : s=%i reliable=%i ack=%i rack=%i\n"
276 , send.cursize
277 , chan->outgoing_sequence - 1
278 , chan->reliable_sequence
279 , chan->incoming_sequence
280 , chan->incoming_reliable_sequence);
281 else
282 Com_Printf ("send %4i : s=%i ack=%i rack=%i\n"
283 , send.cursize
284 , chan->outgoing_sequence - 1
285 , chan->incoming_sequence
286 , chan->incoming_reliable_sequence);
287 }
288 }
289
290 /*
291 =================
292 Netchan_Process
293
294 called when the current net_message is from remote_address
295 modifies net_message so that it points to the packet payload
296 =================
297 */
298 qboolean Netchan_Process (netchan_t *chan, sizebuf_t *msg)
299 {
300 unsigned sequence, sequence_ack;
301 unsigned reliable_ack, reliable_message;
302 int qport;
303
304 // get sequence numbers
305 MSG_BeginReading (msg);
306 sequence = MSG_ReadLong (msg);
307 sequence_ack = MSG_ReadLong (msg);
308
309 // read the qport if we are a server
310 if (chan->sock == NS_SERVER)
311 qport = MSG_ReadShort (msg);
312
313 reliable_message = sequence >> 31;
314 reliable_ack = sequence_ack >> 31;
315
316 sequence &= ~(1<<31);
317 sequence_ack &= ~(1<<31);
318
319 if (showpackets->value)
320 {
321 if (reliable_message)
322 Com_Printf ("recv %4i : s=%i reliable=%i ack=%i rack=%i\n"
323 , msg->cursize
324 , sequence
325 , chan->incoming_reliable_sequence ^ 1
326 , sequence_ack
327 , reliable_ack);
328 else
329 Com_Printf ("recv %4i : s=%i ack=%i rack=%i\n"
330 , msg->cursize
331 , sequence
332 , sequence_ack
333 , reliable_ack);
334 }
335
336 //
337 // discard stale or duplicated packets
338 //
339 if (sequence <= chan->incoming_sequence)
340 {
341 if (showdrop->value)
342 Com_Printf ("%s:Out of order packet %i at %i\n"
343 , NET_AdrToString (chan->remote_address)
344 , sequence
345 , chan->incoming_sequence);
346 return false;
347 }
348
349 //
350 // dropped packets don't keep the message from being used
351 //
352 chan->dropped = sequence - (chan->incoming_sequence+1);
353 if (chan->dropped > 0)
354 {
355 if (showdrop->value)
356 Com_Printf ("%s:Dropped %i packets at %i\n"
357 , NET_AdrToString (chan->remote_address)
358 , chan->dropped
359 , sequence);
360 }
361
362 //
363 // if the current outgoing reliable message has been acknowledged
364 // clear the buffer to make way for the next
365 //
366 if (reliable_ack == chan->reliable_sequence)
367 chan->reliable_length = 0; // it has been received
368
369 //
370 // if this message contains a reliable message, bump incoming_reliable_sequence
371 //
372 chan->incoming_sequence = sequence;
373 chan->incoming_acknowledged = sequence_ack;
374 chan->incoming_reliable_acknowledged = reliable_ack;
375 if (reliable_message)
376 {
377 chan->incoming_reliable_sequence ^= 1;
378 }
379
380 //
381 // the message can now be read from the current message pointer
382 //
383 chan->last_received = curtime;
384
385 return true;
386 }
387
388