/* * netpacket.cpp * * Copyright (C) 2001 Atomic Blue (info@planeshift.it, http://www.atomicblue.org) * * * 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 (version 2 of the License) * 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 #include // csTicks #include "util/log.h" #include "net/netpacket.h" // #define PACKETDEBUG psNetPacketEntry::psNetPacketEntry (psNetPacket* packet, uint32_t cnum, uint16_t sz) : clientnum(cnum), packet(packet) { packet->pktsize = sz - sizeof(psNetPacket); timestamp = csGetTicks(); } /** construct a new PacketEntry for a single or partial message */ psNetPacketEntry::psNetPacketEntry (uint8_t pri, uint32_t cnum, uint32_t id, uint32_t off, uint32_t totalsize, uint16_t sz, psMessageBytes *msg) { packet = (psNetPacket*) malloc (sizeof(psNetPacket) + sz); CS_ASSERT(packet != NULL); clientnum = cnum; packet->flags = pri; packet->pktid = id; packet->offset = off; packet->pktsize = sz; packet->msgsize = totalsize; timestamp = csGetTicks(); if (msg && sz && sz != PKTSIZE_ACK) memcpy(packet->data, ((char *)msg) + off, sz); } psNetPacketEntry::psNetPacketEntry (uint8_t pri, uint32_t cnum, uint32_t id, uint32_t off, uint32_t totalsize, uint16_t sz, const char *bytes) { packet = (psNetPacket*) malloc (sizeof(psNetPacket) + sz); CS_ASSERT(packet != NULL); clientnum = cnum; packet->flags = pri; packet->pktid = id; packet->offset = off; packet->pktsize = sz; packet->msgsize = totalsize; timestamp = csGetTicks(); if (bytes && sz && sz != PKTSIZE_ACK) memcpy(packet->data, bytes, sz); } psNetPacketEntry::~psNetPacketEntry() { if (packet) free(packet); } bool psNetPacketEntry::Append(psNetPacketEntry *next) { #ifdef PACKETDEBUG Debug5(LOG_NET,0,"Appending packet into MULTIPACKET (ID %d Size: %d). (ID %d Size: %d)\n", packet->pktid, packet->GetPacketSize(), next->packet->pktid, next->packet->GetPacketSize()); #endif /// We do not allow multipackets to be appended to multipackets. if(next->packet->IsMultiPacket()) return false; if(next->clientnum != clientnum) { Error1("Attempted to merge two packets with different destinations!"); // This should not happen return false; } /** * If the msgid is not MULTIPACKET, then we need to set up * the initial merged packet structure before copying the * second packet in. */ if (! packet->IsMultiPacket()) { // Check for size if (packet->GetPacketSize() + next->packet->GetPacketSize() + sizeof(psNetPacket) >= MAXPACKETSIZE) return false; psNetPacket *merge; /** * Allocate the MULTIPACKET to max size, so we don't have to realloc * or copy data more than once. Only exact number of bytes will be * sent on the wire. */ merge = (psNetPacket*) malloc (MAXPACKETSIZE); CS_ASSERT(merge != NULL); /** * priority is copied from first packet now. Later, if any packet merged is HIGH * then the whole packet is HIGH */ merge->flags = packet->flags | FLAG_MULTIPACKET; merge->pktid = 0; merge->offset = 0; merge->pktsize = (uint16_t)packet->GetPacketSize(); merge->msgsize = merge->pktsize; /** * After marshalling for network, copy entire first packet, with header, into data section * of new packet. */ uint16_t size = (uint16_t)packet->GetPacketSize(); packet->MarshallEndian(); memcpy(merge->data, packet, size); packet->UnmarshallEndian(); free(packet); // done with old packet packet = merge; } else { if (packet->GetPacketSize() + next->packet->GetPacketSize() >= MAXPACKETSIZE) return false; } /** * now copy the second packet onto the end of the merged one * and update appropriate header fields */ if (next->packet->GetPriority() == PRIORITY_HIGH) packet->flags = PRIORITY_HIGH | FLAG_MULTIPACKET; // HIGH overrides LOW but not vice versa /* Copy size information temporarily, and pack the next packet for transmission. * Do not reference the next->packet after MarshallEndian() has been called. * copy the entire 2nd packet into 1st packet after existing data */ uint16_t nextSize = (uint16_t)next->packet->GetPacketSize(); next->packet->MarshallEndian(); memcpy(packet->data+packet->pktsize, next->packet, nextSize); next->packet->UnmarshallEndian(); /** * now update length of outer packet */ packet->pktsize += nextSize; packet->msgsize = packet->pktsize; timestamp = csGetTicks(); return true; }; psNetPacketEntry *psNetPacketEntry::GetNextPacket(psNetPacket * &packetdata) { if (!packet->IsMultiPacket()) { packetdata = NULL; return this; } else { if (!packetdata) { // There must be at least enough data in the packet for the header if (packet->pktsize < sizeof(psNetPacket)) { // Only debug because we don't have any information about the remote client here, so this isn't useful as a security warning Debug1(LOG_NET,0,"Dropped multipacket too short to contain even one internal packet header.\n"); return NULL; } packetdata = (psNetPacket *)packet->data; // we need the packet INSIDE the packet packetdata->UnmarshallEndian(); /* The packet size plus offset must be less than or equal to the message size in all sub packets of a multipacket * (packet cannot terminate after end of messsage) */ if (packetdata->offset + packetdata->pktsize > packetdata->msgsize) { // Only debug because we don't have any information about the remote client here, so this isn't useful as a security warning Debug4(LOG_NET,0,"Dropped multipacket containing internal packet with bad header. offset=%d msgsize=%d pktsize=%d\n", packetdata->offset,packetdata->msgsize,packetdata->pktsize); packetdata=NULL; return NULL; } // There also must be enough data for the contained message section if (packet->pktsize < (int)(packetdata->data - packet->data) + packetdata->pktsize) { // Only debug because we don't have any information about the remote client here, so this isn't useful as a security warning Debug2(LOG_NET,0,"Dropped multipacket containing internal packet with invalid length in header. pktsize=%d\n",packet->pktsize); packetdata=NULL; return NULL; } } else { // We've been handed a partially unpacked packet. packetdata = (psNetPacket *)((char *)packetdata + packetdata->GetPacketSize()); // Before we convert endian, make sure we are within the buffer if (packet->pktsize - (int)((char *)packetdata - packet->data) < sizeof(psNetPacket)) { // This just means we've reached the end of the buffer packetdata=NULL; return NULL; } packetdata->UnmarshallEndian(); /* The packet size plus offset must be less than or equal to the message size in all sub packets of a multipacket * (packet cannot terminate after end of messsage) */ if (packetdata->offset + packetdata->pktsize > packetdata->msgsize) { // Only debug because we don't have any information about the remote client here, so this isn't useful as a security warning Debug4(LOG_NET,0,"Dropped remainder of multipacket containing internal packet with bad header. offset=%d msgsize=%d pktsize=%d\n", packetdata->offset,packetdata->msgsize,packetdata->pktsize); packetdata=NULL; return NULL; } // There also must be enough data for the contained message section if (packet->pktsize < (int)(packetdata->data - packet->data) + packetdata->pktsize) { // Only debug because we don't have any information about the remote client here, so this isn't useful as a security warning Debug2(LOG_NET,0,"Dropped remainder of multipacket containing internal packet with invalid length in header. pktsize=%d\n",packet->pktsize); packetdata=NULL; return NULL; } } #ifdef PACKETDEBUG Debug4(LOG_NET,0,"Extracting packet (ID %d) from MULTIPACKET (%p - %d).\n", packetdata->pktid, this, packet->pktid); #endif psNetPacketEntry *pnew = new psNetPacketEntry (packetdata->flags, clientnum, packetdata->pktid, packetdata->offset, packetdata->msgsize, packetdata->pktsize, (const char *)packetdata->data); return pnew; } }