/* ================= Netchan_TransmitNextFragment Send one fragment of the current message ================= */ void Netchan_TransmitNextFragment( netchan_t *chan ) { msg_t send; byte send_buf[MAX_PACKETLEN]; int fragmentLength; int outgoingSequence; // write the packet header MSG_InitOOB (&send, send_buf, sizeof(send_buf)); // <-- only do the oob here outgoingSequence = chan->outgoingSequence | FRAGMENT_BIT; MSG_WriteLong(&send, outgoingSequence); // send the qport if we are a client if ( chan->sock == NS_CLIENT ) { MSG_WriteShort( &send, qport->integer ); } #ifdef LEGACY_PROTOCOL if(!chan->compat) #endif MSG_WriteLong(&send, NETCHAN_GENCHECKSUM(chan->challenge, chan->outgoingSequence)); // copy the reliable message to the packet first fragmentLength = FRAGMENT_SIZE; if ( chan->unsentFragmentStart + fragmentLength > chan->unsentLength ) { fragmentLength = chan->unsentLength - chan->unsentFragmentStart; } MSG_WriteShort( &send, chan->unsentFragmentStart ); MSG_WriteShort( &send, fragmentLength ); MSG_WriteData( &send, chan->unsentBuffer + chan->unsentFragmentStart, fragmentLength ); // send the datagram NET_SendPacket( chan->sock, send.cursize, send.data, chan->remoteAddress ); // Store send time and size of this packet for rate control chan->lastSentTime = Sys_Milliseconds(); chan->lastSentSize = send.cursize; if ( showpackets->integer ) { Com_Printf ("%s send %4i : s=%i fragment=%i,%i\n" , netsrcString[ chan->sock ] , send.cursize , chan->outgoingSequence , chan->unsentFragmentStart, fragmentLength); } chan->unsentFragmentStart += fragmentLength; // this exit condition is a little tricky, because a packet // that is exactly the fragment length still needs to send // a second packet of zero length so that the other side // can tell there aren't more to follow if ( chan->unsentFragmentStart == chan->unsentLength && fragmentLength != FRAGMENT_SIZE ) { chan->outgoingSequence++; chan->unsentFragments = qfalse; } }
/* =============== Netchan_Transmit Sends a message to a connection, fragmenting if necessary A 0 length will still generate a packet. ================ */ void Netchan_Transmit( netchan_t* chan, int length, const byte* data ) { msg_s send; byte send_buf[MAX_PACKETLEN]; if ( length > MAX_MSGLEN ) { Com_Error( ERR_DROP, "Netchan_Transmit: length = %i", length ); } chan->unsentFragmentStart = 0; // fragment large reliable messages if ( length >= FRAGMENT_SIZE ) { chan->unsentFragments = true; chan->unsentLength = length; memcpy( chan->unsentBuffer, data, length ); // only send the first fragment now Netchan_TransmitNextFragment( chan ); return; } // write the packet header MSG_InitOOB( &send, send_buf, sizeof( send_buf ) ); MSG_WriteLong( &send, chan->outgoingSequence ); // send the qport if we are a client if ( chan->sock == NS_CLIENT ) MSG_WriteShort( &send, qport->integer ); #ifdef LEGACY_PROTOCOL if ( !chan->compat ) #endif MSG_WriteLong( &send, NETCHAN_GENCHECKSUM( chan->challenge, chan->outgoingSequence ) ); chan->outgoingSequence++; MSG_WriteData( &send, data, length ); // send the datagram NET_SendPacket( chan->sock, send.cursize, send.data, chan->remoteAddress ); // Store send time and size of this packet for rate control chan->lastSentTime = Sys_Milliseconds(); chan->lastSentSize = send.cursize; if ( showpackets->integer ) { Com_Printf( "%s send %4i : s=%i ack=%i\n" , netsrcString[ chan->sock ] , send.cursize , chan->outgoingSequence - 1 , chan->incomingSequence ); } }
/* ================= Netchan_Process Returns qfalse if the message should not be processed due to being out of order or a fragment. Msg must be large enough to hold MAX_MSGLEN, because if this is the final fragment of a multi-part message, the entire thing will be copied out. ================= */ qboolean Netchan_Process( netchan_t *chan, msg_t *msg ) { int sequence; int fragmentStart, fragmentLength; qboolean fragmented; // XOR unscramble all data in the packet after the header // Netchan_UnScramblePacket( msg ); // get sequence numbers MSG_BeginReadingOOB( msg ); sequence = MSG_ReadLong( msg ); // check for fragment information if ( sequence & FRAGMENT_BIT ) { sequence &= ~FRAGMENT_BIT; fragmented = qtrue; } else { fragmented = qfalse; } // read the qport if we are a server if ( chan->sock == NS_SERVER ) { MSG_ReadShort( msg ); } { int checksum = MSG_ReadLong(msg); // UDP spoofing protection if(NETCHAN_GENCHECKSUM(chan->challenge, sequence) != checksum) return qfalse; } // read the fragment information if ( fragmented ) { fragmentStart = MSG_ReadShort( msg ); fragmentLength = MSG_ReadShort( msg ); } else { fragmentStart = 0; // stop warning message fragmentLength = 0; } if ( showpackets->integer ) { if ( fragmented ) { Com_Printf( "%s recv %4i : s=%i fragment=%i,%i\n" , netsrcString[ chan->sock ] , msg->cursize , sequence , fragmentStart, fragmentLength ); } else { Com_Printf( "%s recv %4i : s=%i\n" , netsrcString[ chan->sock ] , msg->cursize , sequence ); } } // // discard out of order or duplicated packets // if ( sequence <= chan->incomingSequence ) { if ( showdrop->integer || showpackets->integer ) { Com_Printf( "%s:Out of order packet %i at %i\n" , NET_AdrToString( chan->remoteAddress ) , sequence , chan->incomingSequence ); } return qfalse; } // // dropped packets don't keep the message from being used // chan->dropped = sequence - (chan->incomingSequence+1); if ( chan->dropped > 0 ) { if ( showdrop->integer || showpackets->integer ) { Com_Printf( "%s:Dropped %i packets at %i\n" , NET_AdrToString( chan->remoteAddress ) , chan->dropped , sequence ); } } // // if this is the final framgent of a reliable message, // bump incoming_reliable_sequence // if ( fragmented ) { // TTimo // make sure we add the fragments in correct order // either a packet was dropped, or we received this one too soon // we don't reconstruct the fragments. we will wait till this fragment gets to us again // (NOTE: we could probably try to rebuild by out of order chunks if needed) if ( sequence != chan->fragmentSequence ) { chan->fragmentSequence = sequence; chan->fragmentLength = 0; } // if we missed a fragment, dump the message if ( fragmentStart != chan->fragmentLength ) { if ( showdrop->integer || showpackets->integer ) { Com_Printf( "%s:Dropped a message fragment\n" , NET_AdrToString( chan->remoteAddress )); } // we can still keep the part that we have so far, // so we don't need to clear chan->fragmentLength return qfalse; } // copy the fragment to the fragment buffer if ( fragmentLength < 0 || msg->readcount + fragmentLength > msg->cursize || chan->fragmentLength + fragmentLength > sizeof( chan->fragmentBuffer ) ) { if ( showdrop->integer || showpackets->integer ) { Com_Printf ("%s:illegal fragment length\n" , NET_AdrToString (chan->remoteAddress ) ); } return qfalse; } Com_Memcpy( chan->fragmentBuffer + chan->fragmentLength, msg->data + msg->readcount, fragmentLength ); chan->fragmentLength += fragmentLength; // if this wasn't the last fragment, don't process anything if ( fragmentLength == FRAGMENT_SIZE ) { return qfalse; } if ( chan->fragmentLength > msg->maxsize ) { Com_Printf( "%s:fragmentLength %i > msg->maxsize\n" , NET_AdrToString (chan->remoteAddress ), chan->fragmentLength ); return qfalse; } // copy the full message over the partial fragment // make sure the sequence number is still there *(int *)msg->data = LittleLong( sequence ); Com_Memcpy( msg->data + 4, chan->fragmentBuffer, chan->fragmentLength ); msg->cursize = chan->fragmentLength + 4; chan->fragmentLength = 0; msg->readcount = 4; // past the sequence number msg->bit = 32; // past the sequence number // TTimo // clients were not acking fragmented messages chan->incomingSequence = sequence; return qtrue; } // // the message can now be read from the current message pointer // chan->incomingSequence = sequence; return qtrue; }