Files
BloomPatched/src/DebugServer/Gdb/Connection.cpp

302 lines
9.9 KiB
C++
Raw Normal View History

2021-10-02 17:39:27 +01:00
#include "Connection.hpp"
#include <arpa/inet.h>
2022-04-05 22:37:00 +01:00
#include <unistd.h>
#include <cerrno>
2021-04-04 21:04:12 +01:00
#include <fcntl.h>
#include <algorithm>
2021-04-04 21:04:12 +01:00
#include "Exceptions/ClientDisconnected.hpp"
#include "Exceptions/DebugServerInterrupted.hpp"
2021-04-04 21:04:12 +01:00
#include "Exceptions/ClientCommunicationError.hpp"
2021-10-02 17:39:27 +01:00
#include "src/Exceptions/Exception.hpp"
2021-10-02 17:39:27 +01:00
#include "src/Logger/Logger.hpp"
#include "src/Services/StringService.hpp"
2021-04-04 21:04:12 +01:00
namespace DebugServer::Gdb
{
using namespace Exceptions;
using namespace ::Exceptions;
using ResponsePackets::ResponsePacket;
Connection::Connection(int serverSocketFileDescriptor, EventFdNotifier& interruptEventNotifier)
: interruptEventNotifier(interruptEventNotifier)
{
this->accept(serverSocketFileDescriptor);
2021-04-04 21:04:12 +01:00
::fcntl(
this->socketFileDescriptor.value(),
F_SETFL,
::fcntl(this->socketFileDescriptor.value(), F_GETFL, 0) | O_NONBLOCK
);
this->epollInstance.addEntry(
this->socketFileDescriptor.value(),
static_cast<std::uint16_t>(::EPOLL_EVENTS::EPOLLIN)
);
this->enableReadInterrupts();
}
2021-04-04 21:04:12 +01:00
Connection::~Connection() {
this->close();
}
std::string Connection::getIpAddress() const {
auto ipAddress = std::array<char, INET_ADDRSTRLEN>{};
if (::inet_ntop(AF_INET, &(socketAddress.sin_addr), ipAddress.data(), INET_ADDRSTRLEN) == nullptr) {
throw Exception{"Failed to convert client IP address to text form."};
}
return {ipAddress.data()};
2021-04-04 21:04:12 +01:00
}
2022-10-01 21:01:37 +01:00
std::vector<RawPacket> Connection::readRawPackets() {
auto output = std::vector<RawPacket>{};
do {
const auto bytes = this->read();
auto bufferSize = bytes.size();
for (auto byteIndex = std::size_t{0}; byteIndex < bufferSize; byteIndex++) {
auto byte = bytes[byteIndex];
if (byte == 0x03) {
/*
* This is an interrupt packet - it doesn't carry any of the usual packet frame bytes, so we'll
* just add them here, in order to keep things consistent.
*
* Because we're effectively faking the packet frame, we can use any value for the checksum.
*/
output.push_back({'$', byte, '#', 'F', 'F'});
continue;
}
if (byte == '$') {
// Beginning of packet
RawPacket rawPacket;
rawPacket.push_back('$');
auto packetIndex = byteIndex;
bool validPacket = false;
bool isByteEscaped = false;
for (packetIndex++; packetIndex < bufferSize; packetIndex++) {
byte = bytes[packetIndex];
if (byte == '}' && !isByteEscaped) {
isByteEscaped = true;
continue;
}
if (!isByteEscaped) {
if (byte == '$') {
// Unexpected end of packet
break;
}
if (byte == '#') {
// End of packet data
if ((bufferSize - 1) < (packetIndex + 2)) {
// There should be at least two more bytes in the buffer, for the checksum.
break;
}
rawPacket.push_back(byte);
// Add the checksum bytes and break the loop
rawPacket.push_back(bytes[++packetIndex]);
rawPacket.push_back(bytes[++packetIndex]);
validPacket = true;
break;
}
} else {
// Escaped bytes are XOR'd with a 0x20 mask.
byte ^= 0x20;
isByteEscaped = false;
}
rawPacket.push_back(byte);
}
if (!validPacket) {
Logger::warning("GDB client sent invalid packet data - ignoring");
continue;
}
Logger::debug(
"Read GDB packet: "
+ Services::StringService::replaceUnprintable(
std::string{rawPacket.begin(), rawPacket.end()}
)
);
2022-04-08 22:17:46 +01:00
if (this->packetAcknowledgement) {
// Acknowledge receipt
this->write({'+'});
}
output.emplace_back(std::move(rawPacket));
byteIndex = packetIndex;
}
}
} while (output.empty());
return output;
2021-04-04 21:04:12 +01:00
}
void Connection::writePacket(const ResponsePacket& packet) {
2022-04-08 22:17:46 +01:00
const auto rawPacket = packet.toRawPacket();
Logger::debug("Writing GDB packet: " + std::string{rawPacket.begin(), rawPacket.end()});
2021-04-04 21:04:12 +01:00
this->write(rawPacket);
if (this->packetAcknowledgement) {
auto attempts = std::size_t{0};
auto ackByte = this->readSingleByte(false);
while (ackByte != '+') {
if (attempts > 10) {
throw ClientCommunicationError{
"Failed to write GDB response packet - client failed to acknowledge receipt - retry limit reached"
};
}
2021-04-04 21:04:12 +01:00
if (ackByte == '-') {
// GDB has requested retransmission
Logger::debug("Sending packet again, upon GDB's request");
this->write(rawPacket);
}
ackByte = this->readSingleByte(false);
++attempts;
}
}
}
2021-04-04 21:04:12 +01:00
void Connection::accept(int serverSocketFileDescriptor) {
int socketAddressLength = sizeof(this->socketAddress);
const auto socketFileDescriptor = ::accept(
serverSocketFileDescriptor,
reinterpret_cast<sockaddr*>(&(this->socketAddress)),
reinterpret_cast<socklen_t*>(&socketAddressLength)
);
if (socketFileDescriptor < 0) {
throw Exception{"Failed to accept GDB Remote Serial Protocol connection"};
}
this->socketFileDescriptor = socketFileDescriptor;
}
void Connection::close() noexcept {
if (this->socketFileDescriptor.value_or(-1) >= 0) {
::close(this->socketFileDescriptor.value());
this->socketFileDescriptor = std::nullopt;
}
}
std::vector<unsigned char> Connection::read(
std::optional<std::size_t> bytes,
bool interruptible,
std::optional<std::chrono::milliseconds> timeout
) {
auto output = std::vector<unsigned char>();
2021-04-04 21:04:12 +01:00
2022-04-16 21:23:03 +01:00
if (this->readInterruptEnabled != interruptible) {
if (interruptible) {
this->enableReadInterrupts();
2021-04-04 21:04:12 +01:00
} else {
2022-04-16 21:23:03 +01:00
this->disableReadInterrupts();
}
2021-04-04 21:04:12 +01:00
}
2022-04-16 21:23:03 +01:00
// Clear any previous interrupts that are still hanging around
this->interruptEventNotifier.clear();
2021-04-04 21:04:12 +01:00
const auto eventFileDescriptor = this->epollInstance.waitForEvent(timeout);
2021-04-04 21:04:12 +01:00
if (!eventFileDescriptor.has_value()) {
// Timed out
return output;
}
if (eventFileDescriptor.value() == this->interruptEventNotifier.getFileDescriptor()) {
// Interrupted
this->interruptEventNotifier.clear();
throw DebugServerInterrupted{};
}
2021-04-04 21:04:12 +01:00
const auto bytesToRead = bytes.value_or(Connection::ABSOLUTE_MAXIMUM_PACKET_READ_SIZE);
output.resize(bytesToRead, 0x00);
2021-04-04 21:04:12 +01:00
const auto bytesRead = ::read(
this->socketFileDescriptor.value(),
output.data(),
bytesToRead
);
if (bytesRead < 0) {
throw ClientCommunicationError{
"Failed to read data from GDB client - error code: " + std::to_string(errno)
};
}
2021-04-04 21:04:12 +01:00
if (bytesRead == 0) {
// Client has disconnected
throw ClientDisconnected{};
2021-04-04 21:04:12 +01:00
}
if (bytesRead != output.size()) {
output.resize(static_cast<unsigned long>(std::max(ssize_t{0}, bytesRead)));
}
return output;
2021-04-04 21:04:12 +01:00
}
std::optional<unsigned char> Connection::readSingleByte(bool interruptible) {
auto bytes = this->read(1, interruptible, std::chrono::milliseconds{300});
2021-04-04 21:04:12 +01:00
if (!bytes.empty()) {
return bytes.front();
}
2021-04-04 21:04:12 +01:00
return std::nullopt;
2021-04-04 21:04:12 +01:00
}
void Connection::write(const std::vector<unsigned char>& buffer) {
if (::write(this->socketFileDescriptor.value(), buffer.data(), buffer.size()) == -1) {
if (errno == EPIPE || errno == ECONNRESET) {
// Connection was closed
throw ClientDisconnected{};
}
2021-04-04 21:04:12 +01:00
throw ClientCommunicationError{
"Failed to write " + std::to_string(buffer.size()) + " bytes to GDP client socket - error no: "
+ std::to_string(errno)
};
}
}
2021-04-04 21:04:12 +01:00
void Connection::disableReadInterrupts() {
this->epollInstance.removeEntry(this->interruptEventNotifier.getFileDescriptor());
this->readInterruptEnabled = false;
}
2021-04-04 21:04:12 +01:00
void Connection::enableReadInterrupts() {
this->epollInstance.addEntry(
this->interruptEventNotifier.getFileDescriptor(),
static_cast<std::uint16_t>(::EPOLL_EVENTS::EPOLLIN)
);
this->readInterruptEnabled = true;
2021-04-04 21:04:12 +01:00
}
}