123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- /*
- Copyright (C) 1997-2001 Id Software, Inc.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
- #include "qcommon.h"
- /*
- packet header
- -------------
- 31 sequence
- 1 does this message contain a reliable payload
- 31 acknowledge sequence
- 1 acknowledge receipt of even/odd message
- 16 qport
- The remote connection never knows if it missed a reliable message, the
- local side detects that it has been dropped by seeing a sequence acknowledge
- higher thatn the last reliable sequence, but without the correct evon/odd
- bit for the reliable set.
- If the sender notices that a reliable message has been dropped, it will be
- retransmitted. It will not be retransmitted again until a message after
- the retransmit has been acknowledged and the reliable still failed to get there.
- if the sequence number is -1, the packet should be handled without a netcon
- The reliable message can be added to at any time by doing
- MSG_Write* (&netchan->message, <data>).
- If the message buffer is overflowed, either by a single message, or by
- multiple frames worth piling up while the last reliable transmit goes
- unacknowledged, the netchan signals a fatal error.
- Reliable messages are always placed first in a packet, then the unreliable
- message is included if there is sufficient room.
- To the receiver, there is no distinction between the reliable and unreliable
- parts of the message, they are just processed out as a single larger message.
- Illogical packet sequence numbers cause the packet to be dropped, but do
- not kill the connection. This, combined with the tight window of valid
- reliable acknowledgement numbers provides protection against malicious
- address spoofing.
- The qport field is a workaround for bad address translating routers that
- sometimes remap the client's source port on a packet during gameplay.
- If the base part of the net address matches and the qport matches, then the
- channel matches even if the IP port differs. The IP port should be updated
- to the new value before sending out any replies.
- If there is no information that needs to be transfered on a given frame,
- such as during the connection stage while waiting for the client to load,
- then a packet only needs to be delivered if there is something in the
- unacknowledged reliable
- */
- cvar_t *showpackets;
- cvar_t *showdrop;
- cvar_t *qport;
- netadr_t net_from;
- sizebuf_t net_message;
- byte net_message_buffer[MAX_MSGLEN];
- /*
- ===============
- Netchan_Init
- ===============
- */
- void Netchan_Init (void)
- {
- int port;
- // pick a port value that should be nice and random
- port = Sys_Milliseconds() & 0xffff;
- showpackets = Cvar_Get ("showpackets", "0", 0);
- showdrop = Cvar_Get ("showdrop", "0", 0);
- qport = Cvar_Get ("qport", va("%i", port), CVAR_NOSET);
- }
- /*
- ===============
- Netchan_OutOfBand
- Sends an out-of-band datagram
- ================
- */
- void Netchan_OutOfBand (int net_socket, netadr_t adr, int length, byte *data)
- {
- sizebuf_t send;
- byte send_buf[MAX_MSGLEN];
- // write the packet header
- SZ_Init (&send, send_buf, sizeof(send_buf));
-
- MSG_WriteLong (&send, -1); // -1 sequence means out of band
- SZ_Write (&send, data, length);
- // send the datagram
- NET_SendPacket (net_socket, send.cursize, send.data, adr);
- }
- /*
- ===============
- Netchan_OutOfBandPrint
- Sends a text message in an out-of-band datagram
- ================
- */
- void Netchan_OutOfBandPrint (int net_socket, netadr_t adr, char *format, ...)
- {
- va_list argptr;
- static char string[MAX_MSGLEN - 4];
-
- va_start (argptr, format);
- vsprintf (string, format,argptr);
- va_end (argptr);
- Netchan_OutOfBand (net_socket, adr, strlen(string), (byte *)string);
- }
- /*
- ==============
- Netchan_Setup
- called to open a channel to a remote system
- ==============
- */
- void Netchan_Setup (netsrc_t sock, netchan_t *chan, netadr_t adr, int qport)
- {
- memset (chan, 0, sizeof(*chan));
-
- chan->sock = sock;
- chan->remote_address = adr;
- chan->qport = qport;
- chan->last_received = curtime;
- chan->incoming_sequence = 0;
- chan->outgoing_sequence = 1;
- SZ_Init (&chan->message, chan->message_buf, sizeof(chan->message_buf));
- chan->message.allowoverflow = true;
- }
- /*
- ===============
- Netchan_CanReliable
- Returns true if the last reliable message has acked
- ================
- */
- qboolean Netchan_CanReliable (netchan_t *chan)
- {
- if (chan->reliable_length)
- return false; // waiting for ack
- return true;
- }
- qboolean Netchan_NeedReliable (netchan_t *chan)
- {
- qboolean send_reliable;
- // if the remote side dropped the last reliable message, resend it
- send_reliable = false;
- if (chan->incoming_acknowledged > chan->last_reliable_sequence
- && chan->incoming_reliable_acknowledged != chan->reliable_sequence)
- send_reliable = true;
- // if the reliable transmit buffer is empty, copy the current message out
- if (!chan->reliable_length && chan->message.cursize)
- {
- send_reliable = true;
- }
- return send_reliable;
- }
- /*
- ===============
- Netchan_Transmit
- tries to send an unreliable message to a connection, and handles the
- transmition / retransmition of the reliable messages.
- A 0 length will still generate a packet and deal with the reliable messages.
- ================
- */
- void Netchan_Transmit (netchan_t *chan, int length, byte *data)
- {
- sizebuf_t send;
- byte send_buf[MAX_MSGLEN];
- qboolean send_reliable;
- unsigned w1, w2;
- // check for message overflow
- if (chan->message.overflowed)
- {
- chan->fatal_error = true;
- Com_Printf ("%s:Outgoing message overflow\n"
- , NET_AdrToString (chan->remote_address));
- return;
- }
- send_reliable = Netchan_NeedReliable (chan);
- if (!chan->reliable_length && chan->message.cursize)
- {
- memcpy (chan->reliable_buf, chan->message_buf, chan->message.cursize);
- chan->reliable_length = chan->message.cursize;
- chan->message.cursize = 0;
- chan->reliable_sequence ^= 1;
- }
- // write the packet header
- SZ_Init (&send, send_buf, sizeof(send_buf));
- w1 = ( chan->outgoing_sequence & ~(1<<31) ) | (send_reliable<<31);
- w2 = ( chan->incoming_sequence & ~(1<<31) ) | (chan->incoming_reliable_sequence<<31);
- chan->outgoing_sequence++;
- chan->last_sent = curtime;
- MSG_WriteLong (&send, w1);
- MSG_WriteLong (&send, w2);
- // send the qport if we are a client
- if (chan->sock == NS_CLIENT)
- MSG_WriteShort (&send, qport->value);
- // copy the reliable message to the packet first
- if (send_reliable)
- {
- SZ_Write (&send, chan->reliable_buf, chan->reliable_length);
- chan->last_reliable_sequence = chan->outgoing_sequence;
- }
-
- // add the unreliable part if space is available
- if (send.maxsize - send.cursize >= length)
- SZ_Write (&send, data, length);
- else
- Com_Printf ("Netchan_Transmit: dumped unreliable\n");
- // send the datagram
- NET_SendPacket (chan->sock, send.cursize, send.data, chan->remote_address);
- if (showpackets->value)
- {
- if (send_reliable)
- Com_Printf ("send %4i : s=%i reliable=%i ack=%i rack=%i\n"
- , send.cursize
- , chan->outgoing_sequence - 1
- , chan->reliable_sequence
- , chan->incoming_sequence
- , chan->incoming_reliable_sequence);
- else
- Com_Printf ("send %4i : s=%i ack=%i rack=%i\n"
- , send.cursize
- , chan->outgoing_sequence - 1
- , chan->incoming_sequence
- , chan->incoming_reliable_sequence);
- }
- }
- /*
- =================
- Netchan_Process
- called when the current net_message is from remote_address
- modifies net_message so that it points to the packet payload
- =================
- */
- qboolean Netchan_Process (netchan_t *chan, sizebuf_t *msg)
- {
- unsigned sequence, sequence_ack;
- unsigned reliable_ack, reliable_message;
- int qport;
- // get sequence numbers
- MSG_BeginReading (msg);
- sequence = MSG_ReadLong (msg);
- sequence_ack = MSG_ReadLong (msg);
- // read the qport if we are a server
- if (chan->sock == NS_SERVER)
- qport = MSG_ReadShort (msg);
- reliable_message = sequence >> 31;
- reliable_ack = sequence_ack >> 31;
- sequence &= ~(1<<31);
- sequence_ack &= ~(1<<31);
- if (showpackets->value)
- {
- if (reliable_message)
- Com_Printf ("recv %4i : s=%i reliable=%i ack=%i rack=%i\n"
- , msg->cursize
- , sequence
- , chan->incoming_reliable_sequence ^ 1
- , sequence_ack
- , reliable_ack);
- else
- Com_Printf ("recv %4i : s=%i ack=%i rack=%i\n"
- , msg->cursize
- , sequence
- , sequence_ack
- , reliable_ack);
- }
- //
- // discard stale or duplicated packets
- //
- if (sequence <= chan->incoming_sequence)
- {
- if (showdrop->value)
- Com_Printf ("%s:Out of order packet %i at %i\n"
- , NET_AdrToString (chan->remote_address)
- , sequence
- , chan->incoming_sequence);
- return false;
- }
- //
- // dropped packets don't keep the message from being used
- //
- chan->dropped = sequence - (chan->incoming_sequence+1);
- if (chan->dropped > 0)
- {
- if (showdrop->value)
- Com_Printf ("%s:Dropped %i packets at %i\n"
- , NET_AdrToString (chan->remote_address)
- , chan->dropped
- , sequence);
- }
- //
- // if the current outgoing reliable message has been acknowledged
- // clear the buffer to make way for the next
- //
- if (reliable_ack == chan->reliable_sequence)
- chan->reliable_length = 0; // it has been received
-
- //
- // if this message contains a reliable message, bump incoming_reliable_sequence
- //
- chan->incoming_sequence = sequence;
- chan->incoming_acknowledged = sequence_ack;
- chan->incoming_reliable_acknowledged = reliable_ack;
- if (reliable_message)
- {
- chan->incoming_reliable_sequence ^= 1;
- }
- //
- // the message can now be read from the current message pointer
- //
- chan->last_received = curtime;
- return true;
- }
|