Update to 12.0-beta1
This commit is contained in:
+179
-82
@@ -17,44 +17,66 @@
|
||||
#include "../../safeguards.h"
|
||||
|
||||
/**
|
||||
* Create a packet that is used to read from a network socket
|
||||
* @param cs the socket handler associated with the socket we are reading from
|
||||
* Create a packet that is used to read from a network socket.
|
||||
* @param cs The socket handler associated with the socket we are reading from.
|
||||
* @param limit The maximum size of packets to accept.
|
||||
* @param initial_read_size The initial amount of data to transfer from the socket into the
|
||||
* packet. This defaults to just the required bytes to determine the
|
||||
* packet's size. That default is the wanted for streams such as TCP
|
||||
* as you do not want to read data of the next packet yet. For UDP
|
||||
* you need to read the whole packet at once otherwise you might
|
||||
* loose some the data of the packet, so there you pass the maximum
|
||||
* size for the packet you expect from the network.
|
||||
*/
|
||||
Packet::Packet(NetworkSocketHandler *cs)
|
||||
Packet::Packet(NetworkSocketHandler *cs, size_t limit, size_t initial_read_size) : next(nullptr), pos(0), limit(limit)
|
||||
{
|
||||
assert(cs != nullptr);
|
||||
|
||||
this->cs = cs;
|
||||
this->next = nullptr;
|
||||
this->pos = 0; // We start reading from here
|
||||
this->size = 0;
|
||||
this->buffer = MallocT<byte>(SEND_MTU);
|
||||
this->cs = cs;
|
||||
this->buffer.resize(initial_read_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet to send
|
||||
* @param type of the packet to send
|
||||
* @param type The type of the packet to send
|
||||
* @param limit The maximum number of bytes the packet may have. Default is COMPAT_MTU.
|
||||
* Be careful of compatibility with older clients/servers when changing
|
||||
* the limit as it might break things if the other side is not expecting
|
||||
* much larger packets than what they support.
|
||||
*/
|
||||
Packet::Packet(PacketType type)
|
||||
Packet::Packet(PacketType type, size_t limit) : next(nullptr), pos(0), limit(limit), cs(nullptr)
|
||||
{
|
||||
this->cs = nullptr;
|
||||
this->next = nullptr;
|
||||
|
||||
/* Skip the size so we can write that in before sending the packet */
|
||||
this->pos = 0;
|
||||
this->size = sizeof(PacketSize);
|
||||
this->buffer = MallocT<byte>(SEND_MTU);
|
||||
this->buffer[this->size++] = type;
|
||||
/* Allocate space for the the size so we can write that in just before sending the packet. */
|
||||
this->Send_uint16(0);
|
||||
this->Send_uint8(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the buffer of this packet.
|
||||
* Add the given Packet to the end of the queue of packets.
|
||||
* @param queue The pointer to the begin of the queue.
|
||||
* @param packet The packet to append to the queue.
|
||||
*/
|
||||
Packet::~Packet()
|
||||
/* static */ void Packet::AddToQueue(Packet **queue, Packet *packet)
|
||||
{
|
||||
free(this->buffer);
|
||||
while (*queue != nullptr) queue = &(*queue)->next;
|
||||
*queue = packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop the packet from the begin of the queue and set the
|
||||
* begin of the queue to the second element in the queue.
|
||||
* @param queue The pointer to the begin of the queue.
|
||||
* @return The Packet that used to be a the begin of the queue.
|
||||
*/
|
||||
/* static */ Packet *Packet::PopFromQueue(Packet **queue)
|
||||
{
|
||||
Packet *p = *queue;
|
||||
*queue = p->next;
|
||||
p->next = nullptr;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes the packet size from the raw packet from packet->size
|
||||
*/
|
||||
@@ -62,10 +84,21 @@ void Packet::PrepareToSend()
|
||||
{
|
||||
assert(this->cs == nullptr && this->next == nullptr);
|
||||
|
||||
this->buffer[0] = GB(this->size, 0, 8);
|
||||
this->buffer[1] = GB(this->size, 8, 8);
|
||||
this->buffer[0] = GB(this->Size(), 0, 8);
|
||||
this->buffer[1] = GB(this->Size(), 8, 8);
|
||||
|
||||
this->pos = 0; // We start reading from here
|
||||
this->buffer.shrink_to_fit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is it safe to write to the packet, i.e. didn't we run over the buffer?
|
||||
* @param bytes_to_write The amount of bytes we want to try to write.
|
||||
* @return True iff the given amount of bytes can be written to the packet.
|
||||
*/
|
||||
bool Packet::CanWriteToPacket(size_t bytes_to_write)
|
||||
{
|
||||
return this->Size() + bytes_to_write <= this->limit;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -95,8 +128,8 @@ void Packet::Send_bool(bool data)
|
||||
*/
|
||||
void Packet::Send_uint8(uint8 data)
|
||||
{
|
||||
assert(this->size < SEND_MTU - sizeof(data));
|
||||
this->buffer[this->size++] = data;
|
||||
assert(this->CanWriteToPacket(sizeof(data)));
|
||||
this->buffer.emplace_back(data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,9 +138,9 @@ void Packet::Send_uint8(uint8 data)
|
||||
*/
|
||||
void Packet::Send_uint16(uint16 data)
|
||||
{
|
||||
assert(this->size < SEND_MTU - sizeof(data));
|
||||
this->buffer[this->size++] = GB(data, 0, 8);
|
||||
this->buffer[this->size++] = GB(data, 8, 8);
|
||||
assert(this->CanWriteToPacket(sizeof(data)));
|
||||
this->buffer.emplace_back(GB(data, 0, 8));
|
||||
this->buffer.emplace_back(GB(data, 8, 8));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,11 +149,11 @@ void Packet::Send_uint16(uint16 data)
|
||||
*/
|
||||
void Packet::Send_uint32(uint32 data)
|
||||
{
|
||||
assert(this->size < SEND_MTU - sizeof(data));
|
||||
this->buffer[this->size++] = GB(data, 0, 8);
|
||||
this->buffer[this->size++] = GB(data, 8, 8);
|
||||
this->buffer[this->size++] = GB(data, 16, 8);
|
||||
this->buffer[this->size++] = GB(data, 24, 8);
|
||||
assert(this->CanWriteToPacket(sizeof(data)));
|
||||
this->buffer.emplace_back(GB(data, 0, 8));
|
||||
this->buffer.emplace_back(GB(data, 8, 8));
|
||||
this->buffer.emplace_back(GB(data, 16, 8));
|
||||
this->buffer.emplace_back(GB(data, 24, 8));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,15 +162,15 @@ void Packet::Send_uint32(uint32 data)
|
||||
*/
|
||||
void Packet::Send_uint64(uint64 data)
|
||||
{
|
||||
assert(this->size < SEND_MTU - sizeof(data));
|
||||
this->buffer[this->size++] = GB(data, 0, 8);
|
||||
this->buffer[this->size++] = GB(data, 8, 8);
|
||||
this->buffer[this->size++] = GB(data, 16, 8);
|
||||
this->buffer[this->size++] = GB(data, 24, 8);
|
||||
this->buffer[this->size++] = GB(data, 32, 8);
|
||||
this->buffer[this->size++] = GB(data, 40, 8);
|
||||
this->buffer[this->size++] = GB(data, 48, 8);
|
||||
this->buffer[this->size++] = GB(data, 56, 8);
|
||||
assert(this->CanWriteToPacket(sizeof(data)));
|
||||
this->buffer.emplace_back(GB(data, 0, 8));
|
||||
this->buffer.emplace_back(GB(data, 8, 8));
|
||||
this->buffer.emplace_back(GB(data, 16, 8));
|
||||
this->buffer.emplace_back(GB(data, 24, 8));
|
||||
this->buffer.emplace_back(GB(data, 32, 8));
|
||||
this->buffer.emplace_back(GB(data, 40, 8));
|
||||
this->buffer.emplace_back(GB(data, 48, 8));
|
||||
this->buffer.emplace_back(GB(data, 56, 8));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,14 +178,27 @@ void Packet::Send_uint64(uint64 data)
|
||||
* the string + '\0'. No size-byte or something.
|
||||
* @param data The string to send
|
||||
*/
|
||||
void Packet::Send_string(const char *data)
|
||||
void Packet::Send_string(const std::string_view data)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
/* The <= *is* valid due to the fact that we are comparing sizes and not the index. */
|
||||
assert(this->size + strlen(data) + 1 <= SEND_MTU);
|
||||
while ((this->buffer[this->size++] = *data++) != '\0') {}
|
||||
assert(this->CanWriteToPacket(data.size() + 1));
|
||||
this->buffer.insert(this->buffer.end(), data.begin(), data.end());
|
||||
this->buffer.emplace_back('\0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send as many of the bytes as possible in the packet. This can mean
|
||||
* that it is possible that not all bytes are sent. To cope with this
|
||||
* the function returns the amount of bytes that were actually sent.
|
||||
* @param begin The begin of the buffer to send.
|
||||
* @param end The end of the buffer to send.
|
||||
* @return The number of bytes that were added to this packet.
|
||||
*/
|
||||
size_t Packet::Send_bytes(const byte *begin, const byte *end)
|
||||
{
|
||||
size_t amount = std::min<size_t>(end - begin, this->limit - this->Size());
|
||||
this->buffer.insert(this->buffer.end(), begin, begin + amount);
|
||||
return amount;
|
||||
}
|
||||
|
||||
/*
|
||||
* Receiving commands
|
||||
@@ -162,18 +208,21 @@ void Packet::Send_string(const char *data)
|
||||
|
||||
|
||||
/**
|
||||
* Is it safe to read from the packet, i.e. didn't we run over the buffer ?
|
||||
* @param bytes_to_read The amount of bytes we want to try to read.
|
||||
* Is it safe to read from the packet, i.e. didn't we run over the buffer?
|
||||
* In case \c close_connection is true, the connection will be closed when one would
|
||||
* overrun the buffer. When it is false, the connection remains untouched.
|
||||
* @param bytes_to_read The amount of bytes we want to try to read.
|
||||
* @param close_connection Whether to close the connection if one cannot read that amount.
|
||||
* @return True if that is safe, otherwise false.
|
||||
*/
|
||||
bool Packet::CanReadFromPacket(uint bytes_to_read)
|
||||
bool Packet::CanReadFromPacket(size_t bytes_to_read, bool close_connection)
|
||||
{
|
||||
/* Don't allow reading from a quit client/client who send bad data */
|
||||
if (this->cs->HasClientQuit()) return false;
|
||||
|
||||
/* Check if variable is within packet-size */
|
||||
if (this->pos + bytes_to_read > this->size) {
|
||||
this->cs->NetworkSocketHandler::CloseConnection();
|
||||
if (this->pos + bytes_to_read > this->Size()) {
|
||||
if (close_connection) this->cs->NetworkSocketHandler::MarkClosed();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -181,13 +230,45 @@ bool Packet::CanReadFromPacket(uint bytes_to_read)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the packet size from the raw packet and stores it in the packet->size
|
||||
* Check whether the packet, given the position of the "write" pointer, has read
|
||||
* enough of the packet to contain its size.
|
||||
* @return True iff there is enough data in the packet to contain the packet's size.
|
||||
*/
|
||||
void Packet::ReadRawPacketSize()
|
||||
bool Packet::HasPacketSizeData() const
|
||||
{
|
||||
return this->pos >= sizeof(PacketSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bytes in the packet.
|
||||
* When sending a packet this is the size of the data up to that moment.
|
||||
* When receiving a packet (before PrepareToRead) this is the allocated size for the data to be read.
|
||||
* When reading a packet (after PrepareToRead) this is the full size of the packet.
|
||||
* @return The packet's size.
|
||||
*/
|
||||
size_t Packet::Size() const
|
||||
{
|
||||
return this->buffer.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the packet size from the raw packet and stores it in the packet->size
|
||||
* @return True iff the packet size seems plausible.
|
||||
*/
|
||||
bool Packet::ParsePacketSize()
|
||||
{
|
||||
assert(this->cs != nullptr && this->next == nullptr);
|
||||
this->size = (PacketSize)this->buffer[0];
|
||||
this->size += (PacketSize)this->buffer[1] << 8;
|
||||
size_t size = (size_t)this->buffer[0];
|
||||
size += (size_t)this->buffer[1] << 8;
|
||||
|
||||
/* If the size of the packet is less than the bytes required for the size and type of
|
||||
* the packet, or more than the allowed limit, then something is wrong with the packet.
|
||||
* In those cases the packet can generally be regarded as containing garbage data. */
|
||||
if (size < sizeof(PacketSize) + sizeof(PacketType) || size > this->limit) return false;
|
||||
|
||||
this->buffer.resize(size);
|
||||
this->pos = sizeof(PacketSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,12 +276,20 @@ void Packet::ReadRawPacketSize()
|
||||
*/
|
||||
void Packet::PrepareToRead()
|
||||
{
|
||||
this->ReadRawPacketSize();
|
||||
|
||||
/* Put the position on the right place */
|
||||
this->pos = sizeof(PacketSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the \c PacketType from this packet.
|
||||
* @return The packet type.
|
||||
*/
|
||||
PacketType Packet::GetPacketType() const
|
||||
{
|
||||
assert(this->Size() >= sizeof(PacketSize) + sizeof(PacketType));
|
||||
return static_cast<PacketType>(buffer[sizeof(PacketSize)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a boolean from the packet.
|
||||
* @return The read data.
|
||||
@@ -218,7 +307,7 @@ uint8 Packet::Recv_uint8()
|
||||
{
|
||||
uint8 n;
|
||||
|
||||
if (!this->CanReadFromPacket(sizeof(n))) return 0;
|
||||
if (!this->CanReadFromPacket(sizeof(n), true)) return 0;
|
||||
|
||||
n = this->buffer[this->pos++];
|
||||
return n;
|
||||
@@ -232,7 +321,7 @@ uint16 Packet::Recv_uint16()
|
||||
{
|
||||
uint16 n;
|
||||
|
||||
if (!this->CanReadFromPacket(sizeof(n))) return 0;
|
||||
if (!this->CanReadFromPacket(sizeof(n), true)) return 0;
|
||||
|
||||
n = (uint16)this->buffer[this->pos++];
|
||||
n += (uint16)this->buffer[this->pos++] << 8;
|
||||
@@ -247,7 +336,7 @@ uint32 Packet::Recv_uint32()
|
||||
{
|
||||
uint32 n;
|
||||
|
||||
if (!this->CanReadFromPacket(sizeof(n))) return 0;
|
||||
if (!this->CanReadFromPacket(sizeof(n), true)) return 0;
|
||||
|
||||
n = (uint32)this->buffer[this->pos++];
|
||||
n += (uint32)this->buffer[this->pos++] << 8;
|
||||
@@ -264,7 +353,7 @@ uint64 Packet::Recv_uint64()
|
||||
{
|
||||
uint64 n;
|
||||
|
||||
if (!this->CanReadFromPacket(sizeof(n))) return 0;
|
||||
if (!this->CanReadFromPacket(sizeof(n), true)) return 0;
|
||||
|
||||
n = (uint64)this->buffer[this->pos++];
|
||||
n += (uint64)this->buffer[this->pos++] << 8;
|
||||
@@ -278,31 +367,39 @@ uint64 Packet::Recv_uint64()
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a string till it finds a '\0' in the stream.
|
||||
* @param buffer The buffer to put the data into.
|
||||
* @param size The size of the buffer.
|
||||
* Reads characters (bytes) from the packet until it finds a '\0', or reaches a
|
||||
* maximum of \c length characters.
|
||||
* When the '\0' has not been reached in the first \c length read characters,
|
||||
* more characters are read from the packet until '\0' has been reached. However,
|
||||
* these characters will not end up in the returned string.
|
||||
* The length of the returned string will be at most \c length - 1 characters.
|
||||
* @param length The maximum length of the string including '\0'.
|
||||
* @param settings The string validation settings.
|
||||
* @return The validated string.
|
||||
*/
|
||||
void Packet::Recv_string(char *buffer, size_t size, StringValidationSettings settings)
|
||||
std::string Packet::Recv_string(size_t length, StringValidationSettings settings)
|
||||
{
|
||||
PacketSize pos;
|
||||
char *bufp = buffer;
|
||||
const char *last = buffer + size - 1;
|
||||
assert(length > 1);
|
||||
|
||||
/* Don't allow reading from a closed socket */
|
||||
if (cs->HasClientQuit()) return;
|
||||
/* Both loops with Recv_uint8 terminate when reading past the end of the
|
||||
* packet as Recv_uint8 then closes the connection and returns 0. */
|
||||
std::string str;
|
||||
char character;
|
||||
while (--length > 0 && (character = this->Recv_uint8()) != '\0') str.push_back(character);
|
||||
|
||||
pos = this->pos;
|
||||
while (--size > 0 && pos < this->size && (*buffer++ = this->buffer[pos++]) != '\0') {}
|
||||
|
||||
if (size == 0 || pos == this->size) {
|
||||
*buffer = '\0';
|
||||
/* If size was sooner to zero then the string in the stream
|
||||
* skip till the \0, so than packet can be read out correctly for the rest */
|
||||
while (pos < this->size && this->buffer[pos] != '\0') pos++;
|
||||
pos++;
|
||||
if (length == 0) {
|
||||
/* The string in the packet was longer. Read until the termination. */
|
||||
while (this->Recv_uint8() != '\0') {}
|
||||
}
|
||||
this->pos = pos;
|
||||
|
||||
str_validate(bufp, last, settings);
|
||||
return StrMakeValid(str, settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the amount of bytes that are still available for the Transfer functions.
|
||||
* @return The number of bytes that still have to be transfered.
|
||||
*/
|
||||
size_t Packet::RemainingBytesToTransfer() const
|
||||
{
|
||||
return this->Size() - this->pos;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user