qboolean Netchan_Validate(netchan_t *chan, qboolean *frag_message, unsigned int *fragid, int *frag_offset, int *frag_length) { for (int i = 0; i < MAX_STREAMS; i++) { if (!frag_message[i]) continue; #ifndef REHLDS_FIXES if (FRAG_GETID(fragid[i]) > MAX_FRAGMENTS || FRAG_GETCOUNT(fragid[i]) > MAX_FRAGMENTS) { return FALSE; } if ((unsigned int)frag_length[i] > 0x800 || (unsigned int)frag_offset[i] > 0x4000) { return FALSE; } #else // REHLDS_FIXES // total fragments should be <= MAX_FRAGMENTS and current fragment can't be > total fragments if (i == FRAG_NORMAL_STREAM && FRAG_GETCOUNT(fragid[i]) > MAX_NORMAL_FRAGMENTS) return FALSE; if (i == FRAG_FILE_STREAM && FRAG_GETCOUNT(fragid[i]) > MAX_FILE_FRAGMENTS) return FALSE; if (FRAG_GETID(fragid[i]) > FRAG_GETCOUNT(fragid[i])) return FALSE; if (!frag_length[i]) return FALSE; if ((size_t)frag_length[i] > FRAGMENT_MAX_SIZE || (size_t)frag_offset[i] > NET_MAX_PAYLOAD - 1) return FALSE; int frag_end = frag_offset[i] + frag_length[i]; // end of fragment is out of the packet if (frag_end + msg_readcount > net_message.cursize) return FALSE; // fragment overlaps next stream's fragment or placed after it for (int j = i + 1; j < MAX_STREAMS; j++) { if (frag_end > frag_offset[j] && frag_message[j]) // don't add msg_readcount for comparison return FALSE; } #endif // REHLDS_FIXES } return TRUE; }
bool NetChannel::CheckForCompletion(int stream, int intotalbuffers) { int c; int size; int id; fragbuf_t *p; size = 0; c = 0; if (stream != FRAG_NORMAL_STREAM && stream != FRAG_FILE_STREAM) { m_System->DPrintf("ERROR! NetChannel::CheckForCompletion: invalid stream number %i.\n"); return false; } p = m_incomingbufs[stream]; if (!p) { return false; } while (p) { size += p->size; c++; id = FRAG_GETID(p->bufferId); if (id != c) { m_System->DPrintf("WARNING! NetChannel::CheckForCompletion: lost/dropped fragment Lost/dropped fragment would cause stall, retrying connection\n"); m_crashed = true; return false; } p = p->next; } // Received final message if (c == intotalbuffers) { switch (stream) { case FRAG_NORMAL_STREAM: CopyNormalFragments(); break; case FRAG_FILE_STREAM: m_System->Printf("TODO! NetChannel::CheckForCompletion: create file from fragments.\n"); break; } return true; } return false; }
void Netchan_AddBufferToList(fragbuf_t **pplist, fragbuf_t *pbuf) { // Find best slot fragbuf_t *pprev, *n; int id1, id2; pbuf->next = nullptr; if (!pplist) return; if (!*pplist) { pbuf->next = *pplist; *pplist = pbuf; return; } pprev = *pplist; while (pprev->next) { n = pprev->next; // Next item in list id1 = FRAG_GETID(n->bufferid); id2 = FRAG_GETID(pbuf->bufferid); if (id1 > id2) { // Insert here pbuf->next = n->next; pprev->next = pbuf; return; } pprev = pprev->next; } // Insert at end pprev->next = pbuf; }
void Netchan_CheckForCompletion(netchan_t *chan, int stream, int intotalbuffers) { int c; int size; int id; fragbuf_t *p; size = 0; c = 0; p = chan->incomingbufs[stream]; if (!p) return; while (p) { size += p->frag_message.cursize; c++; id = FRAG_GETID(p->bufferid); if (id != c && chan == &g_pcls.netchan) { if (chan->sock == NS_MULTICAST) { char szCommand[32]; Q_snprintf(szCommand, sizeof(szCommand), "listen %s\n", NET_AdrToString(chan->remote_address)); Cbuf_AddText(szCommand); return; } Con_Printf("%s: Lost/dropped fragment would cause stall, retrying connection\n", __func__); Cbuf_AddText("retry\n"); } p = p->next; } // Received final message if (c == intotalbuffers) { chan->incomingready[stream] = true; } }
/* ============================== Netchan_CheckForCompletion ============================== */ void Netchan_CheckForCompletion( netchan_t *chan, int stream, int intotalbuffers ) { int c, id; int size; fragbuf_t *p; size = 0; c = 0; p = chan->incomingbufs[stream]; if( !p ) return; while( p ) { size += BF_GetNumBytesWritten( &p->frag_message ); c++; id = FRAG_GETID( p->bufferid ); if( id != c ) { if( chan->sock == NS_CLIENT ) { MsgDev( D_ERROR, "Lost/dropped fragment would cause stall, retrying connection\n" ); Cbuf_AddText( "reconnect\n" ); } } p = p->next; } // received final message if( c == intotalbuffers ) { chan->incomingready[stream] = true; MsgDev( D_NOTE, "\nincoming is complete %i bytes waiting\n", size ); } }
qboolean Netchan_Process(netchan_t *chan) { int i; unsigned int sequence, sequence_ack; unsigned int reliable_ack, reliable_message; unsigned int fragid[MAX_STREAMS] = { 0, 0 }; qboolean frag_message[MAX_STREAMS] = { false, false }; int frag_offset[MAX_STREAMS] = { 0, 0 }; int frag_length[MAX_STREAMS] = { 0, 0 }; qboolean message_contains_fragments; if (!g_pcls.demoplayback && !g_pcls.passive) { if (!NET_CompareAdr(net_from, chan->remote_address)) return FALSE; } chan->last_received = realtime; // get sequence numbers MSG_BeginReading(); sequence = MSG_ReadLong(); sequence_ack = MSG_ReadLong(); if (sequence_ack & 0x40000000) { if (!g_modfuncs.m_pfnProcessIncomingNet) return FALSE; } if (g_modfuncs.m_pfnProcessIncomingNet) { if (!g_modfuncs.m_pfnProcessIncomingNet(chan, &net_message)) return FALSE; } reliable_message = sequence >> 31; reliable_ack = sequence_ack >> 31; message_contains_fragments = sequence & (1 << 30) ? true : false; COM_UnMunge2(&net_message.data[8], net_message.cursize - 8, sequence & 0xFF); if (message_contains_fragments) { for (i = 0; i < MAX_STREAMS; i++) { if (MSG_ReadByte()) { frag_message[i] = true; fragid[i] = MSG_ReadLong(); frag_offset[i] = MSG_ReadShort(); frag_length[i] = MSG_ReadShort(); } } if (!Netchan_Validate(chan, frag_message, fragid, frag_offset, frag_length)) return FALSE; } sequence &= ~(1 << 31); sequence &= ~(1 << 30); sequence_ack &= ~(1 << 31); sequence_ack &= ~(1 << 30); if (net_showpackets.value != 0.0 && net_showpackets.value != 3.0) { char c = (chan == &g_pcls.netchan) ? 'c' : 's'; Con_Printf( " %c <-- sz=%i seq=%i ack=%i rel=%i tm=%f\n", c, net_message.cursize, sequence, sequence_ack, reliable_message, (chan == &g_pcls.netchan) ? g_pcl.time : g_psv.time); } if (sequence <= (unsigned)chan->incoming_sequence) { if (net_showdrop.value != 0.0) { if (sequence == (unsigned)chan->incoming_sequence) Con_Printf("%s:duplicate packet %i at %i\n", NET_AdrToString(chan->remote_address), sequence, chan->incoming_sequence); else Con_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 // net_drop = sequence - (chan->incoming_sequence + 1); if (net_drop > 0 && net_showdrop.value != 0.0) { Con_Printf("%s:Dropped %i packets at %i\n", NET_AdrToString(chan->remote_address), net_drop, sequence); } // // if the current outgoing reliable message has been acknowledged // clear the buffer to make way for the next // if (reliable_ack == (unsigned)chan->reliable_sequence) { // Make sure we actually could have ack'd this message #ifdef REHLDS_FIXES if (sequence_ack >= (unsigned)chan->last_reliable_sequence) #else // REHLDS_FIXES if (chan->incoming_acknowledged + 1 >= chan->last_reliable_sequence) #endif // REHLDS_FIXES { 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; } int statId = chan->flow[FLOW_INCOMING].current & 0x1F; chan->flow[FLOW_INCOMING].stats[statId].size = net_message.cursize + UDP_HEADER_SIZE; chan->flow[FLOW_INCOMING].stats[statId].time = realtime; chan->flow[FLOW_INCOMING].current++; Netchan_UpdateFlow(chan); if (message_contains_fragments) { for (i = 0; i < MAX_STREAMS; i++) { int j; fragbuf_t *pbuf; int inbufferid; int intotalbuffers; if (!frag_message[i]) continue; inbufferid = FRAG_GETID(fragid[i]); intotalbuffers = FRAG_GETCOUNT(fragid[i]); if (fragid[i] != 0) { pbuf = Netchan_FindBufferById(&chan->incomingbufs[i], fragid[i], true); if (pbuf) { int len = frag_length[i]; SZ_Clear(&pbuf->frag_message); SZ_Write(&pbuf->frag_message, &net_message.data[msg_readcount + frag_offset[i]], len); } else { Con_Printf("Netchan_Process: Couldn't allocate or find buffer %i\n", inbufferid); } // Count # of incoming bufs we've queued? are we done? Netchan_CheckForCompletion(chan, i, intotalbuffers); } // Rearrange incoming data to not have the frag stuff in the middle of it int wpos = msg_readcount + frag_offset[i]; int rpos = wpos + frag_length[i]; Q_memmove(net_message.data + wpos, net_message.data + rpos, net_message.cursize - rpos); net_message.cursize -= frag_length[i]; for (j = i + 1; j < MAX_STREAMS; j++) { frag_offset[j] -= frag_length[i]; // fragments order already validated } } // Is there anything left to process? if (net_message.cursize <= 16) return FALSE; } return TRUE; }
void NetChannel::ProcessIncoming(unsigned char *data, int size) { BitBuffer message(data, size); int i; unsigned int sequence, sequence_ack; unsigned int reliable_ack, reliable_message; unsigned int fragid[MAX_STREAMS] = { 0, 0 }; bool frag_message[MAX_STREAMS] = { false, false }; int frag_offset[MAX_STREAMS] = { 0, 0 }; int frag_length[MAX_STREAMS] = { 0, 0 }; bool message_contains_fragments; int net_drop; float newLoss; float weight; // get sequence numbers sequence = message.ReadLong(); if (sequence == CONNECTIONLESS_HEADER) { NetPacket *p = new NetPacket; p->connectionless = true; p->time = m_System->GetTime(); p->seqnr = -1; p->address.FromNetAddress(&m_remote_address); p->data.Resize(size - 4); p->data.WriteBuf(data + 4, size - 4); p->data.Reset(); m_incomingPackets.AddHead(p); return; } if (!m_connected) { return; } sequence_ack = message.ReadLong(); COM_UnMunge2(message.GetData() + 8, size - 8, sequence & 0xFF); reliable_message = sequence >> 31; reliable_ack = sequence_ack >> 31; message_contains_fragments = sequence & (1 << 30) ? true : false; // TODO: Looks like need to move it above COM_UnMunge2 if (sequence_ack & 0x40000000) { m_crashed = true; return; } if (message_contains_fragments) { for (i = 0; i < MAX_STREAMS; i++) { if (message.ReadByte()) { frag_message[i] = true; fragid[i] = message.ReadLong(); frag_offset[i] = message.ReadShort(); frag_length[i] = message.ReadShort(); } } } sequence &= ~(1 << 31); sequence &= ~(1 << 30); sequence_ack &= ~(1 << 31); sequence_ack &= ~(1 << 30); if (sequence <= (unsigned int)m_incoming_sequence) { if (sequence == (unsigned int)m_incoming_sequence) m_System->DPrintf("NetChannel::ProcessIncoming: duplicate packet %i at %i from %s\n", sequence, m_incoming_sequence, m_remote_address.ToString()); else m_System->DPrintf("NetChannel::ProcessIncoming: out of order packet %i at %i from %s\n", sequence, m_incoming_sequence, m_remote_address.ToString()); return; } // dropped packets don't keep the message from being used net_drop = sequence - (m_incoming_sequence + 1); if (net_drop < 0) { net_drop = 0; } newLoss = (net_drop + 1) * (1.0f / 200.0f); if (newLoss < 1.0f) { weight = (float)net_drop / (float)(net_drop + 1); m_loss = (1.0 - newLoss) * m_loss + weight * newLoss; } else m_loss = 1; // if the current outgoing reliable message has been acknowledged // clear the buffer to make way for the next if (reliable_ack == (unsigned int)m_reliable_sequence) { if (m_incoming_acknowledged + 1 >= m_last_reliable_sequence) { // it has been received m_reliableOutSize = 0; } } // if this message contains a reliable message, bump incoming_reliable_sequence m_incoming_sequence = sequence; m_incoming_acknowledged = sequence_ack; m_incoming_reliable_acknowledged = reliable_ack; if (reliable_message) { m_incoming_reliable_sequence ^= 1u; } int statId = m_flow[FLOW_INCOMING].current & 0x1f; m_flow[FLOW_INCOMING].stats[statId].size = size + UDP_HEADER_SIZE; m_flow[FLOW_INCOMING].stats[statId].time = m_System->GetTime(); m_flow[FLOW_INCOMING].current++; m_last_received = m_System->GetTime(); if (message_contains_fragments) { for (i = 0; i <= 1; ++i) { int j; fragbuf_t *pbuf; int inbufferid; int intotalbuffers; if (!frag_message[i]) continue; inbufferid = FRAG_GETID(fragid[i]); intotalbuffers = FRAG_GETCOUNT(fragid[i]); if (fragid[i]) { pbuf = FindBufferById(&m_incomingbufs[i], fragid[i], true); if (pbuf) { memcpy(pbuf->data, message.GetData() + message.CurrentSize() + frag_offset[i], frag_length[i]); pbuf->size = frag_length[i]; } else { m_System->Printf("NetChannel::ProcessIncoming: couldn't allocate or find buffer %i\n", inbufferid); } // Count # of incoming bufs we've queued? are we done? CheckForCompletion(i, intotalbuffers); } // Rearrange incoming data to not have the frag stuff in the middle of it int wpos = message.CurrentSize() + frag_offset[i]; int rpos = wpos + frag_length[i]; memmove(message.GetData() + wpos, message.GetData() + rpos, message.GetMaxSize() - rpos); message.m_MaxSize -= frag_length[i]; for (j = i + 1; j < MAX_STREAMS; j++) { // fragments order already validated frag_offset[j] -= frag_length[i]; } } } int curLen = message.GetMaxSize() - message.CurrentSize(); if (curLen > 0) { NetPacket *p = new NetPacket; p->connectionless = 0; p->hasReliableData = reliable_message != 0; p->time = m_System->GetTime(); p->seqnr = m_incoming_sequence; p->address.FromNetAddress(&m_remote_address); p->data.Resize(curLen); p->data.WriteBuf(message.m_CurByte, curLen); p->data.Reset(); m_incomingPackets.AddHead(p); } }