diff --git a/client.cpp b/client.cpp index c3a47cf..e6de074 100644 --- a/client.cpp +++ b/client.cpp @@ -7,8 +7,9 @@ #include "logger.h" -Client::Client(int fd, ThreadData_p threadData, SSL *ssl, bool websocket, std::shared_ptr settings) : +Client::Client(int fd, ThreadData_p threadData, SSL *ssl, bool websocket, std::shared_ptr settings, bool fuzzMode) : fd(fd), + fuzzMode(fuzzMode), initialBufferSize(settings->clientInitialBufferSize), // The client is constructed in the main thread, so we need to use its settings copy maxPacketSize(settings->maxPacketSize), // Same as initialBufferSize comment. ioWrapper(ssl, websocket, initialBufferSize, this), @@ -264,6 +265,11 @@ void Client::resetBuffersIfEligible() // Call this from a place you know the writeBufMutex is locked, or we're still only doing SSL accept. void Client::setReadyForWriting(bool val) { +#ifndef NDEBUG + if (fuzzMode) + return; +#endif + if (disconnecting) return; @@ -286,6 +292,11 @@ void Client::setReadyForWriting(bool val) void Client::setReadyForReading(bool val) { +#ifndef NDEBUG + if (fuzzMode) + return; +#endif + if (disconnecting) return; diff --git a/client.h b/client.h index 8c6aa98..369cb3f 100644 --- a/client.h +++ b/client.h @@ -29,6 +29,7 @@ class Client friend class IoWrapper; int fd; + bool fuzzMode = false; ProtocolVersion protocolVersion = ProtocolVersion::None; @@ -70,7 +71,7 @@ class Client void setReadyForReading(bool val); public: - Client(int fd, ThreadData_p threadData, SSL *ssl, bool websocket, std::shared_ptr settings); + Client(int fd, ThreadData_p threadData, SSL *ssl, bool websocket, std::shared_ptr settings, bool fuzzMode=false); Client(const Client &other) = delete; Client(Client &&other) = delete; ~Client(); diff --git a/fuzztests/close.dat b/fuzztests/close.dat new file mode 100644 index 0000000..10525cd --- /dev/null +++ b/fuzztests/close.dat diff --git a/fuzztests/connect-publish-close.dat b/fuzztests/connect-publish-close.dat new file mode 100644 index 0000000..a9bffb5 --- /dev/null +++ b/fuzztests/connect-publish-close.dat diff --git a/fuzztests/connect.dat b/fuzztests/connect.dat new file mode 100644 index 0000000..a908318 --- /dev/null +++ b/fuzztests/connect.dat diff --git a/fuzztests/publish.dat b/fuzztests/publish.dat new file mode 100644 index 0000000..d3a20a8 --- /dev/null +++ b/fuzztests/publish.dat diff --git a/mainapp.cpp b/mainapp.cpp index 31de8ee..969b292 100644 --- a/mainapp.cpp +++ b/mainapp.cpp @@ -185,6 +185,9 @@ void MainApp::doHelp(const char *arg) puts(" -h, --help Print help"); puts(" -c, --config-file Configuration file."); puts(" -t, --test-config Test configuration file."); +#ifndef NDEBUG + puts(" -z, --fuzz-file For fuzzing, provides the bytes that would be sent by a client."); +#endif puts(" -V, --version Show version"); puts(" -l, --license Show license"); } @@ -256,6 +259,11 @@ void MainApp::queueKeepAliveCheckAtAllThreads() } } +void MainApp::setFuzzFile(const std::string &fuzzFilePath) +{ + this->fuzzFilePath = fuzzFilePath; +} + void MainApp::initMainApp(int argc, char *argv[]) { if (instance != nullptr) @@ -266,17 +274,19 @@ void MainApp::initMainApp(int argc, char *argv[]) {"help", no_argument, nullptr, 'h'}, {"config-file", required_argument, nullptr, 'c'}, {"test-config", no_argument, nullptr, 't'}, + {"fuzz-file", required_argument, nullptr, 'z'}, {"version", no_argument, nullptr, 'V'}, {"license", no_argument, nullptr, 'l'}, {nullptr, 0, nullptr, 0} }; std::string configFile; + std::string fuzzFile; int option_index = 0; int opt; bool testConfig = false; - while((opt = getopt_long(argc, argv, "hc:Vlt", long_options, &option_index)) != -1) + while((opt = getopt_long(argc, argv, "hc:Vltz:", long_options, &option_index)) != -1) { switch(opt) { @@ -289,6 +299,9 @@ void MainApp::initMainApp(int argc, char *argv[]) case 'V': MainApp::showLicense(); exit(0); + case 'z': + fuzzFile = optarg; + break; case 'h': MainApp::doHelp(argv[0]); exit(16); @@ -325,6 +338,7 @@ void MainApp::initMainApp(int argc, char *argv[]) } instance = new MainApp(configFile); + instance->setFuzzFile(fuzzFile); } @@ -365,6 +379,37 @@ void MainApp::start() threads.push_back(t); } +#ifndef NDEBUG + // I fuzzed using afl-fuzz. You need to compile it with their compiler. + if (!fuzzFilePath.empty()) + { + int fd = open(fuzzFilePath.c_str(), O_RDONLY); + + if (fd < 0) + return; + + try + { + std::vector packetQueueIn; + + Client_p client(new Client(fd, threads[0], nullptr, false, settings, true)); + client->readFdIntoBuffer(); + client->bufferToMqttPackets(packetQueueIn, client); + + for (MqttPacket &packet : packetQueueIn) + { + packet.handle(); + } + } + catch (ProtocolError &ex) + { + logger->logf(LOG_ERR, "Expected MqttPacket handling error: %s", ex.what()); + } + + running = false; + } +#endif + uint next_thread_index = 0; struct epoll_event events[MAX_EVENTS]; diff --git a/mainapp.h b/mainapp.h index d119b3f..6008567 100644 --- a/mainapp.h +++ b/mainapp.h @@ -39,6 +39,7 @@ class MainApp std::shared_ptr settings; std::list> listeners; std::mutex quitMutex; + std::string fuzzFilePath; Logger *logger = Logger::getInstance(); @@ -49,6 +50,7 @@ class MainApp int createListenSocket(const std::shared_ptr &listener); void wakeUpThread(); void queueKeepAliveCheckAtAllThreads(); + void setFuzzFile(const std::string &fuzzFilePath); MainApp(const std::string &configFilePath); public: