#ifndef COVERTPROTOCOL_H #define COVERTPROTOCOL_H #include #include #include #include #include "../../include/Config.h" /** * @class CovertProtocol * * An unidirectional Covert Channel protocol with a variable size template. * * The protocol works unidirectional so the active side uses send to encode data to be sent and the passive side uses receive to extract data. * * @param N number of bytes which can be used to transmit data * @param PASSIVE passive mode */ template class CovertProtocol { static_assert(N >= 1); public: /** * CovertProtocol constructor */ CovertProtocol() : fileDirectory(Config::getValue("filedirectory")) {} /** * CovertProtocol destructor * * Closes the file */ ~CovertProtocol() { file.close(); } /** * Starts sending a file. * * Starts sending a file if no transmission is running and the file exists. * * @param fileName name of the file in the file directory * @return true - file will be sent | false - file was not accepted */ bool sendFile(const std::string &fileName) { static_assert(!PASSIVE); if (state != ProtocolState::idle || file.is_open()) { return false; } file.open(fileDirectory + fileName, std::ios::in); if (file.is_open()) { file.close(); this->fileName = fileName; state = ProtocolState::fileNameSize; std::cout << "starting sending file \"" << fileName << "\"" << std::endl; return true; } else { return false; } } /** * send encodes the data into the data array * * 1. Send file name size (length) * 2. Send file name * 3. Send data size (length) * 4. Send data * * @param data must be an array of size N */ void send(uint8_t *const data) { static_assert(!PASSIVE); switch (state) { case ProtocolState::idle: data[0] = 0; return; case ProtocolState::fileNameSize: fileNameSize = fileName.size(); data[0] = fileNameSize; fileNamePosition = 0; state = ProtocolState::fileName; break; case ProtocolState::fileName: if (fileNameSize - fileNamePosition >= N) { for (int i = 0; i < N; i++) { data[i] = fileName.at(fileNamePosition++); } } else { int diff = fileNameSize - fileNamePosition; for (int i = 0; i < diff; i++) { data[i] = fileName.at(fileNamePosition++); } } if (fileNamePosition == fileNameSize) { file.open(fileDirectory + fileName, std::ios::in | std::ios::binary | std::ios::ate); if (!file.is_open()) { file.close(); std::cerr << "File \"" << fileName << "\" does exists. Error state!!!" << std::endl; state = ProtocolState::error; return; } dataSize = file.tellg(); dataPosition = 0; file.seekg(std::ios::beg); state = ProtocolState::dataSize; } break; case ProtocolState::dataSize: if constexpr (N >= 4) { for (; dataPosition < 4; dataPosition++) { data[dataPosition] = dataSize >> ((3 - dataPosition) * 8); } } else { uint32_t oldDataPosition = dataPosition; uint32_t limit = dataPosition + N; if (limit > 4) { limit = 4; } for (; dataPosition < limit; dataPosition++) { data[dataPosition - oldDataPosition] = dataSize >> ((3 - dataPosition) * 8); } } if (dataPosition == 4) { dataPosition = 0; state = ProtocolState::data; } break; case ProtocolState::data: if (dataSize - dataPosition >= N) { file.read((char *)data, N); dataPosition += N; } else { file.read((char *)data, dataSize - dataPosition); dataPosition = dataSize; } std::cout << "sent " << dataPosition << "/" << dataSize << std::endl; if (dataPosition == dataSize) { file.close(); state = ProtocolState::idle; std::cout << "finished sending file \"" << fileName << "\"" << std::endl; } break; case ProtocolState::error: break; } return; } /** * receive extracts data from the data array * * 1. Receive file name size (length) * 2. Receive file name * 3. Receive data size (length) * 4. Receive data * * @param data must be an array of size N */ void receive(const uint8_t *const data) { static_assert(PASSIVE); switch (state) { case ProtocolState::idle: if (data[0] == 0) { return; } // no break because the first data received is the filename size case ProtocolState::fileNameSize: std::cout << "incoming file transmission" << std::endl; fileNameSize = data[0]; fileNamePosition = 0; fileName = ""; state = ProtocolState::fileName; break; case ProtocolState::fileName: if (fileNameSize - fileNamePosition >= N) { uint8_t oldFileNamePosition = fileNamePosition; for (; fileNamePosition < oldFileNamePosition + N; fileNamePosition++) { fileName += data[fileNamePosition - oldFileNamePosition]; } } else { uint8_t oldFileNamePosition = fileNamePosition; for (; fileNamePosition < fileNameSize; fileNamePosition++) { fileName += data[fileNamePosition - oldFileNamePosition]; } } if (fileNamePosition == fileNameSize) { file.open(fileDirectory + fileName, std::ios::in); if (file.is_open()) { file.close(); std::cerr << "File \"" << fileName << "\" already exists. Error state!!!" << std::endl; state = ProtocolState::error; return; } file.close(); file.open(fileDirectory + fileName, std::ios::out | std::ios::binary | std::ios::app); if (!file.is_open()) { std::cerr << "File \"" << fileName << "\" could not be opened! Error state!!!" << std::endl; state = ProtocolState::error; return; } dataSize = 0; dataPosition = 0; state = ProtocolState::dataSize; std::cout << "starting receiving file \"" << fileName << "\"" << std::endl; } break; case ProtocolState::dataSize: if constexpr (N >= 4) { for (; dataPosition < 4; dataPosition++) { dataSize = dataSize | data[dataPosition] << ((3 - dataPosition) * 8); } } else { uint32_t oldDataPosition = dataPosition; uint32_t limit = dataPosition + N; if (limit > 4) { limit = 4; } for (; dataPosition < limit; dataPosition++) { dataSize = dataSize | data[dataPosition - oldDataPosition] << ((3 - dataPosition) * 8); } } if (dataPosition == 4) { dataPosition = 0; state = ProtocolState::data; } break; case ProtocolState::data: if (dataSize - dataPosition >= N) { file.write((char *)data, N); dataPosition += N; } else { file.write((char *)data, dataSize - dataPosition); dataPosition = dataSize; } std::cout << "received " << dataPosition << "/" << dataSize << std::endl; if (dataPosition == dataSize) { file.close(); state = ProtocolState::idle; std::cout << "finished receiving file \"" << fileName << "\"" << std::endl; } break; case ProtocolState::error: // break; } } private: /** * folder of the files */ const std::string fileDirectory; /** * States of the data transmission * * @warning error state cannot be recovered!!! */ enum struct ProtocolState { idle, fileNameSize, fileName, dataSize, data, error }; /** * State of the data transmission */ ProtocolState state = ProtocolState::idle; /** * size of the file to be sent/received */ uint32_t dataSize; /** * position in the file to be sent/received */ uint32_t dataPosition; /** * file to be sent/received */ std::fstream file; /** * size of the file name */ uint8_t fileNameSize; /** * position of the file name so far sent/received */ uint8_t fileNamePosition; /** * name of the file to be sent/received */ std::string fileName; }; #endif