Browse Source

Merge branch '79-fixme-us44-covert-channel-extended-status-and-queue' into 'develop'

US44: Covert Channel -  Extended Status and Queue and US41.1: Covert channel queueing (Server) and US38.1: Extended Status Command Server

Closes #65, #73, and #79

See merge request tobias.wach/ccats!85
Pflanzer, Jonas 4 years ago
parent
commit
12953e86dc

+ 24 - 43
daemon/include/CovertChannel/TCPUrgencyChannel.hpp → daemon/include/CovertChannel/BidirectionalChannels.hpp

@@ -1,18 +1,18 @@
-#ifndef TCPURGENCYCHANNEL_H
-#define TCPURGENCYCHANNEL_H
+#ifndef BIDIRECTIONALCHANNELS_H
+#define BIDIRECTIONALCHANNELS_H
 
 #include "CovertChannel.h"
-
-#include "CovertProtocolBidirectional.hpp"
+#include "Protocols/CovertProtocolBidirectional.hpp"
 
 /**
- * @class TCPUrgencyChannel
+ * @class BidirectionalChannels
  *
- * A CovertChannel which hides data in the TCP urgency pointer
+ * Abstract class which implements the methods and constructors which are equal in all bidirectional channels.
  *
+ * @param N number of bytes which can be used to transmit data
  * @param PASSIVE true - server only reacts to incoming channel | false - server initiates channel
  */
-template <bool PASSIVE> class TCPUrgencyChannel : public CovertChannel {
+template <int N, bool PASSIVE> class BidirectionalChannels : public CovertChannel {
 public:
 	/**
 	 * Sets up a CovertChannel.
@@ -25,8 +25,8 @@ public:
 	 * @param targetIP IP of the target server
 	 * @param targetPort Port of the target server
 	 */
-	TCPUrgencyChannel(const std::string &innerInterface, const std::string &outerInterface, const std::string &ownIP, const std::string &targetIP,
-	                  const std::string &targetPort)
+	BidirectionalChannels(const std::string &innerInterface, const std::string &outerInterface, const std::string &ownIP, const std::string &targetIP,
+	                      const std::string &targetPort)
 	    : CovertChannel(innerInterface, outerInterface,
 	                    "(not (tcp and " + std::string(PASSIVE ? "src" : "dst") + " host " + targetIP + " and " + std::string(PASSIVE ? "src" : "dst") +
 	                        " port " + targetPort + ")) and (not (dst host " + ownIP + "))",
@@ -40,7 +40,7 @@ public:
 	/**
 	 * Destroys the CovertChannel.
 	 */
-	virtual ~TCPUrgencyChannel() {}
+	virtual ~BidirectionalChannels() {}
 
 	/**
 	 * Send a file over the covert channel.
@@ -52,55 +52,36 @@ public:
 		if constexpr (PASSIVE) {
 			return false;
 		} else {
+			transferStart = std::time(nullptr);
 			return protocol.sendFile(fileName);
 		}
 	}
 
-protected:
 	/**
-	 * Handler for sniffed packets filterd to forward from the outer network.
-	 *
-	 * Handles incoming packets and forwards them.
-	 *
-	 * @param pdu sniffed packet
+	 * Get the progress
 	 *
-	 * @return false = stop loop | true = continue loop
+	 * @return progress counters
 	 */
-	virtual bool handleChannelFromOuter(Tins::PDU &pdu) {
-		Tins::TCP &tcp = pdu.rfind_pdu<Tins::TCP>();
-
-		uint16_t data = tcp.urg_ptr();
-		protocol.receive((uint8_t *)(&data));
-		tcp.urg_ptr(0);
-		innerSender.send(pdu);
-
-		return true;
+	virtual std::pair<uint32_t, uint32_t> getProgress() {
+		if (isTransferRunning()) {
+			return protocol.getProgress();
+		} else {
+			return std::pair<uint32_t, uint32_t>(0, 0);
+		}
 	}
 
 	/**
-	 * Handler for sniffed packets filterd to forward from the inner network.
-	 *
-	 * Handles incoming packets and forwards them.
+	 * Test if a transfer is running
 	 *
-	 * @param pdu sniffed packet
-	 *
-	 * @return false = stop loop | true = continue loop
+	 * @return true - a transfer runs | false - no transfer runs
 	 */
-	virtual bool handleChannelFromInner(Tins::PDU &pdu) {
-		Tins::TCP &tcp = pdu.rfind_pdu<Tins::TCP>();
-
-		uint16_t data = 0;
-		protocol.send((uint8_t *)(&data));
-		tcp.urg_ptr(data);
-		outerSender.send(pdu);
-
-		return true;
-	}
+	virtual bool isTransferRunning() { return protocol.isTransferRunning(); }
 
+protected:
 	/**
 	 * protocol used to transmit data
 	 */
-	CovertProtocolBidirectional<2, PASSIVE> protocol;
+	CovertProtocolBidirectional<N, PASSIVE> protocol;
 };
 
 #endif

+ 46 - 0
daemon/include/CovertChannel/ChannelControls.h

@@ -0,0 +1,46 @@
+#ifndef CHANNELCONTROLS_H
+#define CHANNELCONTROLS_H
+
+#include <ctime>
+#include <string>
+#include <utility>
+
+class ChannelControls {
+public:
+	/**
+	 * Send a file over the covert channel.
+	 *
+	 * @param fileName name of the file in the file directory
+	 * @return true - file will be sent | false - file was not accepted
+	 */
+	virtual bool sendFile(const std::string &fileName) = 0;
+
+	/**
+	 * Get the progress
+	 *
+	 * @return progress counters
+	 */
+	virtual std::pair<uint32_t, uint32_t> getProgress() = 0;
+
+	/**
+	 * Get the transfer start time
+	 *
+	 * @return start time of the transfer
+	 */
+	virtual std::time_t getTransferStart();
+
+	/**
+	 * Test if a transfer is running
+	 *
+	 * @return true - a transfer runs | false - no transfer runs
+	 */
+	virtual bool isTransferRunning() = 0;
+
+protected:
+	/**
+	 * Time when the transfer was started with sendFile
+	 */
+	std::time_t transferStart;
+};
+
+#endif

+ 7 - 36
daemon/include/CovertChannel/TCPAppendChannel.hpp → daemon/include/CovertChannel/Channels/TCPAppendChannel.hpp

@@ -1,9 +1,7 @@
 #ifndef TCPAPPENDCHANNEL_H
 #define TCPAPPENDCHANNEL_H
 
-#include "CovertChannel.h"
-
-#include "CovertProtocolBidirectional.hpp"
+#include "../BidirectionalChannels.hpp"
 
 /**
  * @class TCPAppendChannel
@@ -13,7 +11,7 @@
  * @param N number of bytes which can be used to transmit data
  * @param PASSIVE true - server only reacts to incoming channel | false - server initiates channel
  */
-template <int N, bool PASSIVE> class TCPAppendChannel : public CovertChannel {
+template <int N, bool PASSIVE> class TCPAppendChannel : public BidirectionalChannels<N, PASSIVE> {
 public:
 	/**
 	 * Sets up a CovertChannel.
@@ -28,35 +26,13 @@ public:
 	 */
 	TCPAppendChannel(const std::string &innerInterface, const std::string &outerInterface, const std::string &ownIP, const std::string &targetIP,
 	                 const std::string &targetPort)
-	    : CovertChannel(innerInterface, outerInterface,
-	                    "(not (tcp and " + std::string(PASSIVE ? "src" : "dst") + " host " + targetIP + " and " + std::string(PASSIVE ? "src" : "dst") +
-	                        " port " + targetPort + ")) and (not (dst host " + ownIP + "))",
-	                    "(not (tcp and " + std::string(PASSIVE ? "dst" : "src") + " host " + targetIP + " and " + std::string(PASSIVE ? "dst" : "src") +
-	                        " port " + targetPort + ")) and (not (dst host " + ownIP + "))",
-	                    "tcp and " + std::string(PASSIVE ? "src" : "dst") + " host " + targetIP + " and " + std::string(PASSIVE ? "src" : "dst") + " port " +
-	                        targetPort,
-	                    "tcp and " + std::string(PASSIVE ? "dst" : "src") + " host " + targetIP + " and " + std::string(PASSIVE ? "dst" : "src") + " port " +
-	                        targetPort) {}
+	    : BidirectionalChannels<N, PASSIVE>(innerInterface, outerInterface, ownIP, targetIP, targetPort) {}
 
 	/**
 	 * Destroys the CovertChannel.
 	 */
 	virtual ~TCPAppendChannel() {}
 
-	/**
-	 * Send a file over the covert channel.
-	 *
-	 * @param fileName name of the file in the file directory
-	 * @return true - file will be sent | false - file was not accepted
-	 */
-	virtual bool sendFile(const std::string &fileName) {
-		if constexpr (PASSIVE) {
-			return false;
-		} else {
-			return protocol.sendFile(fileName);
-		}
-	}
-
 protected:
 	/**
 	 * Handler for sniffed packets filterd to forward from the outer network.
@@ -80,13 +56,13 @@ protected:
 			uint8_t *data = &payload.front();
 			data += size - N;
 
-			protocol.receive(data);
+			BidirectionalChannels<N, PASSIVE>::protocol.receive(data);
 
 			// resize payload
 			payload.resize(size - N);
 		}
 
-		innerSender.send(pdu);
+		BidirectionalChannels<N, PASSIVE>::innerSender.send(pdu);
 
 		return true;
 	}
@@ -116,18 +92,13 @@ protected:
 			uint8_t *data = &payload.front();
 			data += size;
 
-			protocol.send(data);
+			BidirectionalChannels<N, PASSIVE>::protocol.send(data);
 		}
 
-		outerSender.send(pdu);
+		BidirectionalChannels<N, PASSIVE>::outerSender.send(pdu);
 
 		return true;
 	}
-
-	/**
-	 * protocol used to transmit data
-	 */
-	CovertProtocolBidirectional<N, PASSIVE> protocol;
 };
 
 #endif

+ 102 - 0
daemon/include/CovertChannel/Channels/TCPOptionCustomChannel.hpp

@@ -0,0 +1,102 @@
+#ifndef TCPOPTIONCUSTOMCHANNEL_H
+#define TCPOPTIONCUSTOMCHANNEL_H
+
+#include "../BidirectionalChannels.hpp"
+
+/**
+ * @class TCPOptionCustom
+ *
+ * A CovertChannel which hides data in a custom field in the TCP options data
+ *
+ * In theory, any options field can be used to store data. This implementation specifically uses field 11 (CC).
+ * For (un)usable fields, refer to the IANA listing at
+ * https://www.iana.org/assignments/tcp-parameters/tcp-parameters.xhtml
+ *
+ * @param N number of bytes which can be used to transmit data
+ * @param PASSIVE true - server only reacts to incoming channel | false - server initiates channel
+ */
+template <int N, bool PASSIVE> class TCPOptionCustomChannel : public BidirectionalChannels<N, PASSIVE> {
+	static_assert(N <= 255 - 2);
+
+public:
+	/**
+	 * Sets up a CovertChannel.
+	 *
+	 * Creates a CovertChannel, sets the network interfaces for sniffing and sending and sets the filter.
+	 *
+	 * @param innerInterface name of the interface of the inner network
+	 * @param outerInterface name of the interface of the outer network
+	 * @param ownIP IP of this server
+	 * @param targetIP IP of the target server
+	 * @param targetPort Port of the target server
+	 */
+	TCPOptionCustomChannel(const std::string &innerInterface, const std::string &outerInterface, const std::string &ownIP, const std::string &targetIP,
+	                       const std::string &targetPort)
+	    : BidirectionalChannels<N, PASSIVE>(innerInterface, outerInterface, ownIP, targetIP, targetPort) {}
+
+	/**
+	 * Destroys the CovertChannel.
+	 */
+	virtual ~TCPOptionCustomChannel() {}
+
+protected:
+	// this is the id of the option as found in the article found at the top
+	const unsigned int target_options_id = 11;
+
+	/**
+	 * Handler for sniffed packets filterd to forward from the outer network.
+	 *
+	 * Handles incoming packets and forwards them.
+	 *
+	 * @param pdu sniffed packet
+	 *
+	 * @return false = stop loop | true = continue loop
+	 */
+	virtual bool handleChannelFromOuter(Tins::PDU &pdu) {
+		Tins::TCP &tcp = pdu.rfind_pdu<Tins::TCP>();
+
+		const Tins::TCP::options_type &options = tcp.options();
+		Tins::TCP::option op;
+		size_t i;
+		// find option field
+		for (i = 0; i < options.size(); i++) {
+			if (options[i].option() == target_options_id) {
+				op = options[i];
+				break;
+			}
+		}
+		if (i != options.size() && options[i].data_size()) {
+			// found the option
+			BidirectionalChannels<N, PASSIVE>::protocol.receive((uint8_t *)(options[i].data_ptr()));
+			tcp.remove_option((Tins::TCP::OptionTypes)target_options_id);
+		}
+
+		BidirectionalChannels<N, PASSIVE>::innerSender.send(pdu);
+
+		return true;
+	}
+
+	/**
+	 * Handler for sniffed packets filterd to forward from the inner network.
+	 *
+	 * Handles incoming packets and forwards them.
+	 *
+	 * @param pdu sniffed packet
+	 *
+	 * @return false = stop loop | true = continue loop
+	 */
+	virtual bool handleChannelFromInner(Tins::PDU &pdu) {
+		Tins::TCP &tcp = pdu.rfind_pdu<Tins::TCP>();
+		uint8_t data[N];
+		BidirectionalChannels<N, PASSIVE>::protocol.send(data);
+
+		Tins::TCP::option op(target_options_id, N, data);
+		tcp.add_option(op);
+
+		BidirectionalChannels<N, PASSIVE>::outerSender.send(pdu);
+
+		return true;
+	}
+};
+
+#endif

+ 7 - 35
daemon/include/CovertChannel/TCPOptionTimestampChannel.hpp → daemon/include/CovertChannel/Channels/TCPOptionTimestampChannel.hpp

@@ -1,8 +1,7 @@
 #ifndef TCPOPTIONTIMESTAMPCHANNEL_H
 #define TCPOPTIONTIMESTAMPCHANNEL_H
 
-#include "CovertChannel.h"
-#include "CovertProtocolBidirectional.hpp"
+#include "../BidirectionalChannels.hpp"
 
 #include <utility>
 
@@ -15,7 +14,7 @@
  *
  * @param PASSIVE true - server only reacts to incoming channel | false - server initiates channel
  */
-template <bool PASSIVE> class TCPOptionTimestampChannel : public CovertChannel {
+template <bool PASSIVE> class TCPOptionTimestampChannel : public BidirectionalChannels<8, PASSIVE> {
 public:
 	/**
 	 * Sets up a CovertChannel.
@@ -30,35 +29,13 @@ public:
 	 */
 	TCPOptionTimestampChannel(const std::string &innerInterface, const std::string &outerInterface, const std::string &ownIP, const std::string &targetIP,
 	                          const std::string &targetPort)
-	    : CovertChannel(innerInterface, outerInterface,
-	                    "(not (tcp and " + std::string(PASSIVE ? "src" : "dst") + " host " + targetIP + " and " + std::string(PASSIVE ? "src" : "dst") +
-	                        " port " + targetPort + ")) and (not (dst host " + ownIP + "))",
-	                    "(not (tcp and " + std::string(PASSIVE ? "dst" : "src") + " host " + targetIP + " and " + std::string(PASSIVE ? "dst" : "src") +
-	                        " port " + targetPort + ")) and (not (dst host " + ownIP + "))",
-	                    "tcp and " + std::string(PASSIVE ? "src" : "dst") + " host " + targetIP + " and " + std::string(PASSIVE ? "src" : "dst") + " port " +
-	                        targetPort,
-	                    "tcp and " + std::string(PASSIVE ? "dst" : "src") + " host " + targetIP + " and " + std::string(PASSIVE ? "dst" : "src") + " port " +
-	                        targetPort) {}
+	    : BidirectionalChannels<8, PASSIVE>(innerInterface, outerInterface, ownIP, targetIP, targetPort) {}
 
 	/**
 	 * Destroys the CovertChannel.
 	 */
 	virtual ~TCPOptionTimestampChannel() {}
 
-	/**
-	 * Send a file over the covert channel.
-	 *
-	 * @param fileName name of the file in the file directory
-	 * @return true - file will be sent | false - file was not accepted
-	 */
-	virtual bool sendFile(const std::string &fileName) {
-		if constexpr (PASSIVE) {
-			return false;
-		} else {
-			return protocol.sendFile(fileName);
-		}
-	}
-
 protected:
 	/**
 	 * Handler for sniffed packets filterd to forward from the outer network.
@@ -74,9 +51,9 @@ protected:
 
 		std::pair<uint32_t, uint32_t> timestamp = tcp.timestamp();
 		uint64_t data = ((uint64_t)timestamp.first) << 32 | timestamp.second;
-		protocol.receive((uint8_t *)(&data));
+		BidirectionalChannels<8, PASSIVE>::protocol.receive((uint8_t *)(&data));
 		tcp.remove_option(Tins::TCP::OptionTypes::TSOPT);
-		innerSender.send(pdu);
+		BidirectionalChannels<8, PASSIVE>::innerSender.send(pdu);
 
 		return true;
 	}
@@ -94,17 +71,12 @@ protected:
 		Tins::TCP &tcp = pdu.rfind_pdu<Tins::TCP>();
 
 		uint64_t data = 0;
-		protocol.send((uint8_t *)(&data));
+		BidirectionalChannels<8, PASSIVE>::protocol.send((uint8_t *)(&data));
 		tcp.timestamp(data >> 32, data);
-		outerSender.send(pdu);
+		BidirectionalChannels<8, PASSIVE>::outerSender.send(pdu);
 
 		return true;
 	}
-
-	/**
-	 * protocol used to transmit data
-	 */
-	CovertProtocolBidirectional<8, PASSIVE> protocol;
 };
 
 #endif

+ 77 - 0
daemon/include/CovertChannel/Channels/TCPUrgencyChannel.hpp

@@ -0,0 +1,77 @@
+#ifndef TCPURGENCYCHANNEL_H
+#define TCPURGENCYCHANNEL_H
+
+#include "../BidirectionalChannels.hpp"
+
+/**
+ * @class TCPUrgencyChannel
+ *
+ * A CovertChannel which hides data in the TCP urgency pointer
+ *
+ * @param PASSIVE true - server only reacts to incoming channel | false - server initiates channel
+ */
+template <bool PASSIVE> class TCPUrgencyChannel : public BidirectionalChannels<2, PASSIVE> {
+public:
+	/**
+	 * Sets up a CovertChannel.
+	 *
+	 * Creates a CovertChannel, sets the network interfaces for sniffing and sending and sets the filter.
+	 *
+	 * @param innerInterface name of the interface of the inner network
+	 * @param outerInterface name of the interface of the outer network
+	 * @param ownIP IP of this server
+	 * @param targetIP IP of the target server
+	 * @param targetPort Port of the target server
+	 */
+	TCPUrgencyChannel(const std::string &innerInterface, const std::string &outerInterface, const std::string &ownIP, const std::string &targetIP,
+	                  const std::string &targetPort)
+	    : BidirectionalChannels<2, PASSIVE>(innerInterface, outerInterface, ownIP, targetIP, targetPort) {}
+
+	/**
+	 * Destroys the CovertChannel.
+	 */
+	virtual ~TCPUrgencyChannel() {}
+
+protected:
+	/**
+	 * Handler for sniffed packets filterd to forward from the outer network.
+	 *
+	 * Handles incoming packets and forwards them.
+	 *
+	 * @param pdu sniffed packet
+	 *
+	 * @return false = stop loop | true = continue loop
+	 */
+	virtual bool handleChannelFromOuter(Tins::PDU &pdu) {
+		Tins::TCP &tcp = pdu.rfind_pdu<Tins::TCP>();
+
+		uint16_t data = tcp.urg_ptr();
+		BidirectionalChannels<2, PASSIVE>::protocol.receive((uint8_t *)(&data));
+		tcp.urg_ptr(0);
+		BidirectionalChannels<2, PASSIVE>::innerSender.send(pdu);
+
+		return true;
+	}
+
+	/**
+	 * Handler for sniffed packets filterd to forward from the inner network.
+	 *
+	 * Handles incoming packets and forwards them.
+	 *
+	 * @param pdu sniffed packet
+	 *
+	 * @return false = stop loop | true = continue loop
+	 */
+	virtual bool handleChannelFromInner(Tins::PDU &pdu) {
+		Tins::TCP &tcp = pdu.rfind_pdu<Tins::TCP>();
+
+		uint16_t data = 0;
+		BidirectionalChannels<2, PASSIVE>::protocol.send((uint8_t *)(&data));
+		tcp.urg_ptr(data);
+		BidirectionalChannels<2, PASSIVE>::outerSender.send(pdu);
+
+		return true;
+	}
+};
+
+#endif

+ 17 - 1
daemon/include/CovertChannel/CovertChannel.h

@@ -4,6 +4,8 @@
 #include <thread>
 #include <tins/tins.h>
 
+#include "ChannelControls.h"
+
 /**
  * @class CovertChannel
  *
@@ -12,7 +14,7 @@
  * CovertChannel class which will sniff on two network interfacees. It handles filtered traffic with a virtual handler
  * function.
  */
-class CovertChannel {
+class CovertChannel : public ChannelControls {
 public:
 	/**
 	 * Sets up a CovertChannel.
@@ -61,6 +63,20 @@ public:
 	void setFilter(const std::string &innerForwardFilter = "", const std::string &outerForwardFilter = "", const std::string &innerChannelFilter = "",
 	               const std::string &outerChannelFilter = "");
 
+	/**
+	 * Get the progress
+	 *
+	 * @return progress counters
+	 */
+	virtual std::pair<uint32_t, uint32_t> getProgress() = 0;
+
+	/**
+	 * Test if a transfer is running
+	 *
+	 * @return true - a transfer runs | false - no transfer runs
+	 */
+	virtual bool isTransferRunning();
+
 protected:
 	/**
 	 * Handler for sniffed packets filterd to forward from the outer network.

+ 7 - 0
daemon/include/CovertChannel/ForwardChannel.h

@@ -26,6 +26,13 @@ public:
 	 */
 	virtual ~ForwardChannel();
 
+	/**
+	 * Get the progress
+	 *
+	 * @return progress counters
+	 */
+	virtual std::pair<uint32_t, uint32_t> getProgress();
+
 protected:
 	/**
 	 * Handler for sniffed packets filterd to forward from the outer network.

+ 25 - 1
daemon/include/CovertChannel/CovertProtocol.hpp → daemon/include/CovertChannel/Protocols/CovertProtocol.hpp

@@ -6,7 +6,8 @@
 #include <string>
 #include <type_traits>
 
-#include "../../include/Config.h"
+#include "../../../include/Config.h"
+#include "../../../include/Queue.h"
 
 /**
  * @class CovertProtocol
@@ -155,6 +156,9 @@ public:
 				file.close();
 				state = ProtocolState::idle;
 				std::cout << "finished sending file \"" << fileName << "\"" << std::endl;
+
+				// schedule next file transfer
+				Queue::schedule();
 			}
 
 			break;
@@ -285,6 +289,26 @@ public:
 		}
 	}
 
+	/**
+	 * Get the progress
+	 *
+	 * @return progress counters
+	 */
+	virtual std::pair<uint32_t, uint32_t> getProgress() {
+		if (state == ProtocolState::data) {
+			return std::pair<uint32_t, uint32_t>(dataPosition, dataSize);
+		} else {
+			return std::pair<uint32_t, uint32_t>(0, 0);
+		}
+	}
+
+	/**
+	 * Test if a transfer is running
+	 *
+	 * @return true - a transfer runs | false - no transfer runs
+	 */
+	virtual bool isTransferRunning() { return state != ProtocolState::idle; }
+
 private:
 	/**
 	 * folder of the files

+ 14 - 0
daemon/include/CovertChannel/CovertProtocolBidirectional.hpp → daemon/include/CovertChannel/Protocols/CovertProtocolBidirectional.hpp

@@ -89,6 +89,20 @@ public:
 		}
 	}
 
+	/**
+	 * Get the progress
+	 *
+	 * @return progress counters
+	 */
+	virtual std::pair<uint32_t, uint32_t> getProgress() { return protocol.getProgress(); }
+
+	/**
+	 * Test if a transfer is running
+	 *
+	 * @return true - a transfer runs | false - no transfer runs
+	 */
+	virtual bool isTransferRunning() { return protocol.isTransferRunning(); }
+
 private:
 	/**
 	 * current segment counter

+ 0 - 129
daemon/include/CovertChannel/TCPOptionCustomChannel.hpp

@@ -1,129 +0,0 @@
-#ifndef TCPOPTIONCUSTOMCHANNEL_H
-#define TCPOPTIONCUSTOMCHANNEL_H
-
-#include "CovertChannel.h"
-#include "CovertProtocolBidirectional.hpp"
-
-/**
- * @class TCPOptionCustom
- *
- * A CovertChannel which hides data in a custom field in the TCP options data
- *
- * In theory, any options field can be used to store data. This implementation specifically uses field 11 (CC).
- * For (un)usable fields, refer to the IANA listing at
- * https://www.iana.org/assignments/tcp-parameters/tcp-parameters.xhtml
- *
- * @param N number of bytes which can be used to transmit data
- * @param PASSIVE true - server only reacts to incoming channel | false - server initiates channel
- */
-template <int N, bool PASSIVE> class TCPOptionCustomChannel : public CovertChannel {
-	static_assert(N <= 255 - 2);
-	public :
-	    /**
-	     * Sets up a CovertChannel.
-	     *
-	     * Creates a CovertChannel, sets the network interfaces for sniffing and sending and sets the filter.
-	     *
-	     * @param innerInterface name of the interface of the inner network
-	     * @param outerInterface name of the interface of the outer network
-	     * @param ownIP IP of this server
-	     * @param targetIP IP of the target server
-	     * @param targetPort Port of the target server
-	     */
-	    TCPOptionCustomChannel(const std::string &innerInterface, const std::string &outerInterface, const std::string &ownIP, const std::string &targetIP,
-	                    const std::string &targetPort)
-	    : CovertChannel(innerInterface, outerInterface,
-	                    "(not (tcp and " + std::string(PASSIVE ? "src" : "dst") + " host " + targetIP + " and " + std::string(PASSIVE ? "src" : "dst") +
-	                        " port " + targetPort + ")) and (not (dst host " + ownIP + "))",
-	                    "(not (tcp and " + std::string(PASSIVE ? "dst" : "src") + " host " + targetIP + " and " + std::string(PASSIVE ? "dst" : "src") +
-	                        " port " + targetPort + ")) and (not (dst host " + ownIP + "))",
-	                    "tcp and " + std::string(PASSIVE ? "src" : "dst") + " host " + targetIP + " and " + std::string(PASSIVE ? "src" : "dst") + " port " +
-	                        targetPort,
-	                    "tcp and " + std::string(PASSIVE ? "dst" : "src") + " host " + targetIP + " and " + std::string(PASSIVE ? "dst" : "src") + " port " +
-	                        targetPort) {}
-
-	/**
-	 * Destroys the CovertChannel.
-	 */
-	virtual ~TCPOptionCustomChannel() {}
-
-	/**
-	 * Send a file over the covert channel.
-	 *
-	 * @param fileName name of the file in the file directory
-	 * @return true - file will be sent | false - file was not accepted
-	 */
-	virtual bool sendFile(const std::string &fileName) {
-		if constexpr (PASSIVE) {
-			return false;
-		} else {
-			return protocol.sendFile(fileName);
-		}
-	}
-
-protected:
-	// this is the id of the option as found in the article found at the top
-	const unsigned int target_options_id = 11;
-	
-	/**
-	 * Handler for sniffed packets filterd to forward from the outer network.
-	 *
-	 * Handles incoming packets and forwards them.
-	 *
-	 * @param pdu sniffed packet
-	 *
-	 * @return false = stop loop | true = continue loop
-	 */
-	virtual bool handleChannelFromOuter(Tins::PDU &pdu) {
-		Tins::TCP &tcp = pdu.rfind_pdu<Tins::TCP>();
-
-		const Tins::TCP::options_type &options = tcp.options();
-		Tins::TCP::option op;
-		size_t i;
-		// find option field
-		for (i = 0; i < options.size(); i++) {
-			if (options[i].option() == target_options_id) {
-				op = options[i];
-				break;
-			}
-		}
-		if (i != options.size() && options[i].data_size()) {
-			// found the option
-			protocol.receive((uint8_t *)(options[i].data_ptr()));
-			tcp.remove_option((Tins::TCP::OptionTypes)target_options_id);
-		}
-		
-		innerSender.send(pdu);
-
-		return true;
-	}
-
-	/**
-	 * Handler for sniffed packets filterd to forward from the inner network.
-	 *
-	 * Handles incoming packets and forwards them.
-	 *
-	 * @param pdu sniffed packet
-	 *
-	 * @return false = stop loop | true = continue loop
-	 */
-	virtual bool handleChannelFromInner(Tins::PDU &pdu) {
-		Tins::TCP &tcp = pdu.rfind_pdu<Tins::TCP>();
-		uint8_t data[N];
-		protocol.send(data);
-		
-		Tins::TCP::option op(target_options_id, N, data);
-		tcp.add_option(op);
-		
-		outerSender.send(pdu);
-
-		return true;
-	}
-
-	/**
-	 * protocol used to transmit data
-	 */
-	CovertProtocolBidirectional<N, PASSIVE> protocol;
-};
-
-#endif

+ 25 - 0
daemon/include/JsonCommander.h

@@ -92,6 +92,16 @@ private:
 	 */
 	int putFileReceived;
 
+	/**
+	 * Total chunk number from current put transfer
+	 */
+	int putSize;
+
+	/**
+	 * Total chunk number from current get transfer
+	 */
+	int getSize;
+
 	/**
 	 * Executes the status command
 	 */
@@ -147,10 +157,25 @@ private:
 	 */
 	Response executeDeleteFile(const Json::Value &message);
 
+	/**
+	 * Executes the extendedstatus command
+	 */
+	Response executeExtendedStatus(const Json::Value &message);
+
 	/**
 	 * Executes the notifications command
 	 */
 	Response executeNotifications(const Json::Value &message);
+
+	/**
+	 * Executes the queue command
+	 */
+	Response executeQueue(const Json::Value &message);
+
+	/**
+	 * Executes the dequeue command
+	 */
+	Response executeDequeue(const Json::Value &message);
 };
 
 #endif

+ 53 - 0
daemon/include/Queue.h

@@ -0,0 +1,53 @@
+#ifndef QUEUE_H
+#define QUEUE_H
+
+#include <deque>
+#include <mutex>
+#include <string>
+
+#include "CovertChannel/ChannelControls.h"
+
+/**
+ * Namespace wich manages the queue for the covert channel
+ */
+namespace Queue {
+/**
+ * Adds file to queue
+ * @param file add file to queue
+ */
+bool push(const std::string &file);
+
+/**
+ * Remove file from queue. Checks if remove is allowed
+ * @param file file to remove from queue
+ */
+bool remove(const std::string &file);
+
+/**
+ * Schedules one file to be transfered over the covert channel.
+ * schedule() must be called again to send the next file.
+ */
+void schedule();
+
+/**
+ * The queue. Stores filenames. Acts as FIFO.
+ */
+extern std::deque<std::string> queue;
+
+/**
+ * Holds the name of the file of the current transfer. Empty otherwise.
+ */
+extern std::string curTransfer;
+
+/**
+ * Mutex to lock queue while editing it.
+ */
+extern std::mutex mtx;
+
+/**
+ * Covert channel which will be used to send files.
+ */
+extern ChannelControls *channel;
+}; // namespace Queue
+
+#endif // QUEUE_H

+ 1 - 1
daemon/src/CMakeLists.txt

@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8)
 
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
 
-add_executable(ccats src/main.cpp src/Server.cpp src/base64.cpp src/JsonCommander.cpp src/FileManager.cpp src/UserManager.cpp src/Config.cpp src/CovertChannel/CovertChannel.cpp src/CovertChannel/ForwardChannel.cpp src/Notifications.cpp)
+add_executable(ccats src/main.cpp src/Server.cpp src/base64.cpp src/JsonCommander.cpp src/FileManager.cpp src/UserManager.cpp src/Config.cpp src/CovertChannel/CovertChannel.cpp src/CovertChannel/ForwardChannel.cpp src/Notifications.cpp src/Queue.cpp src/CovertChannel/ChannelControls.cpp)
 
 # dependencies used by server only
 find_package(libtins 4.2 REQUIRED)

+ 3 - 0
daemon/src/CovertChannel/ChannelControls.cpp

@@ -0,0 +1,3 @@
+#include "../../include/CovertChannel/ChannelControls.h"
+
+std::time_t ChannelControls::getTransferStart() { return transferStart; }

+ 2 - 0
daemon/src/CovertChannel/CovertChannel.cpp

@@ -75,3 +75,5 @@ bool CovertChannel::handleForwardFromInner(Tins::PDU &pdu) {
 }
 
 bool CovertChannel::sendFile(const std::string &fileName) { return false; }
+
+bool CovertChannel::isTransferRunning() { return false; }

+ 2 - 0
daemon/src/CovertChannel/ForwardChannel.cpp

@@ -7,3 +7,5 @@ ForwardChannel::~ForwardChannel() {}
 bool ForwardChannel::handleChannelFromOuter(Tins::PDU &pdu) { return false; }
 
 bool ForwardChannel::handleChannelFromInner(Tins::PDU &pdu) { return false; }
+
+std::pair<uint32_t, uint32_t> ForwardChannel::getProgress() { return std::pair<uint32_t, uint32_t>(0, 0); }

+ 125 - 0
daemon/src/JsonCommander.cpp

@@ -1,5 +1,7 @@
 #include "../include/JsonCommander.h"
+#include "../include/Config.h"
 #include "../include/Notifications.h"
+#include "../include/Queue.h"
 #include "../include/UserManager.h"
 #include "../include/base64.h"
 
@@ -15,7 +17,10 @@ JsonCommander::JsonCommander(FileManager &fileManager) : fileManager(fileManager
 	commandsMap["head"] = &JsonCommander::executeHead;
 	commandsMap["deleteme"] = &JsonCommander::executeDeleteMe;
 	commandsMap["deletefile"] = &JsonCommander::executeDeleteFile;
+	commandsMap["extendedstatus"] = &JsonCommander::executeExtendedStatus;
 	commandsMap["notifications"] = &JsonCommander::executeNotifications;
+	commandsMap["queue"] = &JsonCommander::executeQueue;
+	commandsMap["dequeue"] = &JsonCommander::executeDequeue;
 }
 
 JsonCommander::~JsonCommander() {}
@@ -163,6 +168,7 @@ JsonCommander::Response JsonCommander::executePut(const Json::Value &message) {
 			response.json["accept"] = true;
 			response.json["error"] = "";
 			this->putFileReceived = message["chunks"].asInt();
+			this->putSize = message["chunks"].asInt();
 		} else {
 			response.json["accept"] = false;
 			response.json["error"] = "file already exists";
@@ -268,6 +274,7 @@ JsonCommander::Response JsonCommander::executeGet(const Json::Value &message) {
 
 		if (opened.first) {
 			this->getFileRemaining = opened.second;
+			this->getSize = opened.second;
 			response.json["accept"] = true;
 			response.json["chunks"] = this->getFileRemaining;
 			response.json["error"] = "";
@@ -519,6 +526,70 @@ JsonCommander::Response JsonCommander::executeDeleteFile(const Json::Value &mess
 	return response;
 }
 
+JsonCommander::Response JsonCommander::executeExtendedStatus(const Json::Value &message) {
+	JsonCommander::Response response;
+	response.json["command"] = "extendedstatus";
+
+	// get status from client server transfers
+	int index = 0;
+	if (fileManager.isUploading()) {
+		response.json["transfersclientserver"][index]["upload"] = true;
+		response.json["transfersclientserver"][index]["file"] = fileManager.getPutBaseFileName();
+		int progress = 0;
+		if (this->putSize != 0) {
+			double d = (double)this->putFileReceived / (double)this->putSize;
+			progress = (int)(d * 100);
+		}
+		response.json["transfersclientserver"][index]["progress"] = progress;
+		index++;
+	}
+	if (fileManager.isDownloading()) {
+		response.json["transfersclientserver"][index]["upload"] = false;
+		response.json["transfersclientserver"][index]["file"] = fileManager.getGetBaseFileName();
+		int progress = 0;
+		if (this->getSize != 0) {
+			double d = 1 - ((double)this->getFileRemaining / (double)this->getSize);
+			progress = (int)(d * 100);
+		}
+		response.json["transfersclientserver"][index]["progress"] = progress;
+	}
+
+	// get status from covert channel
+	index = 0;
+	if (Queue::channel != nullptr && Queue::channel->isTransferRunning()) {
+		response.json["transfersserverserver"][index]["type"] = Config::getValue("passiveMode").compare("true") == 0 ? "download" : "upload";
+		response.json["transfersserverserver"][index]["file"] = Queue::curTransfer;
+
+		auto rawprogress = Queue::channel->getProgress();
+		double d = ((double)rawprogress.first / (double)rawprogress.second);
+		int progress = (int)(d * 100);
+		response.json["transfersserverserver"][index]["progress"] = progress;
+
+		std::time_t startTime = Queue::channel->getTransferStart();
+		std::time_t curTime = std::time(nullptr);
+		std::time_t diffTime = curTime - startTime;
+		double speed = (double)rawprogress.first / (double)diffTime;
+
+		response.json["transfersserverserver"][index]["speed"] = speed;
+		response.json["transfersserverserver"][index]["method"] = Config::getValue("covertChannelMode");
+		index++;
+	}
+
+	for (int i = 0; i < Queue::queue.size(); i++) {
+		response.json["transfersserverserver"][index]["type"] = "queued";
+		response.json["transfersserverserver"][index]["file"] = Queue::queue[i];
+		response.json["transfersserverserver"][index]["progress"] = 0;
+		response.json["transfersserverserver"][index]["speed"] = 0;
+		response.json["transfersserverserver"][index]["method"] = Config::getValue("covertChannelMode");
+		index++;
+	}
+
+	response.json["accept"] = true;
+	response.json["error"] = "";
+	response.action = send;
+	return response;
+}
+
 JsonCommander::Response JsonCommander::executeNotifications(const Json::Value &message) {
 	JsonCommander::Response response;
 	response.json["command"] = "notifications";
@@ -535,3 +606,57 @@ JsonCommander::Response JsonCommander::executeNotifications(const Json::Value &m
 
 	return response;
 }
+
+JsonCommander::Response JsonCommander::executeQueue(const Json::Value &message) {
+	JsonCommander::Response response;
+	response.json["command"] = "queue";
+
+	if (!message["file"].isString()) {
+		response.action = closeAndSend;
+		response.json["file"] = "";
+		response.json["accept"] = false;
+		response.json["error"] = "invalid request";
+	} else {
+		bool res = Queue::push(message["file"].asString());
+		if (res) {
+			response.action = send;
+			response.json["file"] = message["file"].asString();
+			response.json["accept"] = true;
+			response.json["error"] = "";
+		} else {
+			response.action = send;
+			response.json["file"] = message["file"].asString();
+			response.json["accept"] = false;
+			response.json["error"] = "file could not be queued";
+		}
+	}
+
+	return response;
+}
+
+JsonCommander::Response JsonCommander::executeDequeue(const Json::Value &message) {
+	JsonCommander::Response response;
+	response.json["command"] = "dequeue";
+
+	if (!message["file"].isString()) {
+		response.action = closeAndSend;
+		response.json["file"] = "";
+		response.json["accept"] = false;
+		response.json["error"] = "invalid request";
+	} else {
+		bool res = Queue::remove(message["file"].asString());
+		if (res) {
+			response.action = send;
+			response.json["file"] = message["file"].asString();
+			response.json["accept"] = true;
+			response.json["error"] = "";
+		} else {
+			response.action = send;
+			response.json["file"] = message["file"].asString();
+			response.json["accept"] = false;
+			response.json["error"] = "cannot remove this file from queue";
+		}
+	}
+
+	return response;
+}

+ 66 - 0
daemon/src/Queue.cpp

@@ -0,0 +1,66 @@
+#include "../include/Queue.h"
+#include <algorithm>
+
+namespace Queue {
+std::deque<std::string> queue;
+std::string curTransfer = "";
+std::mutex mtx;
+ChannelControls *channel;
+} // namespace Queue
+
+bool Queue::push(const std::string &file) {
+	// Don't queue if no channel is available
+	if (channel == nullptr) {
+		return false;
+	}
+
+	mtx.lock();
+	queue.push_back(file);
+	mtx.unlock();
+
+	// TODO run in extra thread??
+	if (curTransfer.compare("") == 0) {
+		schedule();
+	}
+
+	return true;
+}
+
+bool Queue::remove(const std::string &file) {
+	if (curTransfer.compare(file) == 0) {
+		// TODO check if remove is allowed
+		return false;
+	} else {
+		mtx.lock();
+		auto it = std::find(queue.begin(), queue.end(), file);
+		if (it == queue.end()) {
+			return false;
+		}
+		queue.erase(it);
+		mtx.unlock();
+		return true;
+	}
+}
+
+void Queue::schedule() {
+	if (!queue.empty()) {
+		mtx.lock();
+		std::string file = queue.front();
+		queue.pop_front();
+		curTransfer = file;
+
+		// success is false if file could not be sent or found
+		bool success = channel->sendFile(curTransfer);
+		mtx.unlock();
+
+		if (!success) {
+			schedule();
+		}
+
+		// don't wait until transer finished because schedule must be called again
+	} else {
+		mtx.lock();
+		curTransfer = "";
+		mtx.unlock();
+	}
+}

+ 8 - 19
daemon/src/main.cpp

@@ -1,12 +1,13 @@
 #include <iostream>
 
 #include "../include/Config.h"
+#include "../include/CovertChannel/Channels/TCPAppendChannel.hpp"
+#include "../include/CovertChannel/Channels/TCPOptionCustomChannel.hpp"
+#include "../include/CovertChannel/Channels/TCPOptionTimestampChannel.hpp"
+#include "../include/CovertChannel/Channels/TCPUrgencyChannel.hpp"
 #include "../include/CovertChannel/CovertChannel.h"
 #include "../include/CovertChannel/ForwardChannel.h"
-#include "../include/CovertChannel/TCPAppendChannel.hpp"
-#include "../include/CovertChannel/TCPOptionTimestampChannel.hpp"
-#include "../include/CovertChannel/TCPOptionCustomChannel.hpp"
-#include "../include/CovertChannel/TCPUrgencyChannel.hpp"
+#include "../include/Queue.h"
 #include "../include/Server.h"
 #include "../include/UserManager.h"
 
@@ -29,7 +30,6 @@ int main(int argc, char *argv[]) {
 		const string targetIP = Config::getValue("targetIP");
 		const string targetPort = Config::getValue("targetPort");
 		const string passiveMode = Config::getValue("passiveMode");
-		const string sendFile = Config::getValue("sendFile");
 
 		if (passiveMode == "true") {
 			covertchannel = new TCPUrgencyChannel<true>(innerInterface, outerInterface, ownIP, targetIP, targetPort);
@@ -37,10 +37,6 @@ int main(int argc, char *argv[]) {
 			covertchannel = new TCPUrgencyChannel<false>(innerInterface, outerInterface, ownIP, targetIP, targetPort);
 		}
 
-		// test sending file
-		if (passiveMode != "true" && sendFile != "")
-			covertchannel->sendFile(sendFile);
-
 		// covertchannel = new ForwardChannel(innerInterface, outerInterface);
 		covertchannel->startSniffing();
 	} else if (covertChannelMode == "tcpappend") {
@@ -51,7 +47,6 @@ int main(int argc, char *argv[]) {
 		const string targetIP = Config::getValue("targetIP");
 		const string targetPort = Config::getValue("targetPort");
 		const string passiveMode = Config::getValue("passiveMode");
-		const string sendFile = Config::getValue("sendFile");
 
 		if (passiveMode == "true") {
 			covertchannel = new TCPAppendChannel<8, true>(innerInterface, outerInterface, ownIP, targetIP, targetPort);
@@ -59,10 +54,6 @@ int main(int argc, char *argv[]) {
 			covertchannel = new TCPAppendChannel<8, false>(innerInterface, outerInterface, ownIP, targetIP, targetPort);
 		}
 
-		// test sending file
-		if (passiveMode != "true" && sendFile != "")
-			covertchannel->sendFile(sendFile);
-
 		// covertchannel = new ForwardChannel(innerInterface, outerInterface);
 		covertchannel->startSniffing();
 	} else if (covertChannelMode == "tcpoptiontimestamp") {
@@ -73,7 +64,6 @@ int main(int argc, char *argv[]) {
 		const string targetIP = Config::getValue("targetIP");
 		const string targetPort = Config::getValue("targetPort");
 		const string passiveMode = Config::getValue("passiveMode");
-		const string sendFile = Config::getValue("sendFile");
 
 		if (passiveMode == "true") {
 			covertchannel = new TCPOptionTimestampChannel<true>(innerInterface, outerInterface, ownIP, targetIP, targetPort);
@@ -81,10 +71,6 @@ int main(int argc, char *argv[]) {
 			covertchannel = new TCPOptionTimestampChannel<false>(innerInterface, outerInterface, ownIP, targetIP, targetPort);
 		}
 
-		// test sending file
-		if (passiveMode != "true" && sendFile != "")
-			covertchannel->sendFile(sendFile);
-
 		// covertchannel = new ForwardChannel(innerInterface, outerInterface);
 		covertchannel->startSniffing();
 	} else if (covertChannelMode == "tcpoptioncustom") {
@@ -121,6 +107,9 @@ int main(int argc, char *argv[]) {
 	// if not create one
 	UserManager::init(Config::getValue("userdatabase"));
 
+	// Init queue so the file transfers can be executed
+	Queue::channel = covertchannel;
+
 	try {
 		io_service io_service;
 		Server server(io_service);

+ 1 - 1
daemon/test/CMakeLists.txt

@@ -12,7 +12,7 @@ find_package(GMock REQUIRED)
 include_directories(${Boost_INCLUDE_DIR} ${JSONCPP_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS})
 
 # Add test cpp file
-add_executable(jsonCommanderTest test/JsonCommanderTest.cpp src/JsonCommander.cpp src/FileManager.cpp src/base64.cpp test/ConfigMock.cpp test/UserManagerMock.cpp src/Notifications.cpp)
+add_executable(jsonCommanderTest test/JsonCommanderTest.cpp src/JsonCommander.cpp src/FileManager.cpp src/base64.cpp test/ConfigMock.cpp test/UserManagerMock.cpp src/Notifications.cpp test/QueueMock.cpp src/CovertChannel/ChannelControls.cpp)
 target_link_libraries(jsonCommanderTest ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} ${JSONCPP_LIBRARIES} ${GTEST_BOTH_LIBRARIES} ${GMOCK_BOTH_LIBRARIES})
 
 add_test(

+ 20 - 0
daemon/test/ChannelControlsMock.h

@@ -0,0 +1,20 @@
+#ifndef CHANNELCONTROLSMOCK_H
+#define CHANNELCONTROLSMOCK_H
+
+#include "../include/CovertChannel/ChannelControls.h"
+#include <gmock/gmock.h>
+
+/**
+ * @class ChannelControlsMock
+ *
+ * Gmock stub class for ChannelControls so you can test without starting an actual covert channel.
+ */
+class ChannelControlsMock : public ChannelControls {
+public:
+	MOCK_METHOD(bool, sendFile, (const std::string &fileName), (override));
+	MOCK_METHOD((std::pair<uint32_t, uint32_t>), getProgress, (), (override));
+	MOCK_METHOD(std::time_t, getTransferStart, (), (override));
+	MOCK_METHOD(bool, isTransferRunning, (), (override));
+};
+
+#endif

+ 227 - 0
daemon/test/JsonCommanderTest.cpp

@@ -1,8 +1,11 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include "../include/Config.h"
 #include "../include/JsonCommander.h"
 #include "../include/Notifications.h"
+#include "../include/Queue.h"
+#include "ChannelControlsMock.h"
 #include "FileManagerMock.h"
 
 namespace {
@@ -1197,6 +1200,230 @@ TEST(DeleteFile, DisabledInConfig) {
 	EXPECT_TRUE(response.json["error"].asString().compare("") != 0);
 }
 
+TEST(ExtendedStatus, NoUploadOrDownload) {
+	FileManagerMock fileManager;
+	JsonCommander jsonCommander(fileManager);
+	ChannelControlsMock channelControls;
+	Queue::channel = &channelControls;
+	Queue::queue.clear();
+
+	const std::string command = "extendedstatus";
+
+	Json::Value message;
+	message["command"] = command;
+
+	EXPECT_CALL(fileManager, isUploading()).WillOnce(testing::Return(false));
+	EXPECT_CALL(fileManager, isDownloading()).WillOnce(testing::Return(false));
+	EXPECT_CALL(channelControls, isTransferRunning()).WillOnce(testing::Return(false));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+	EXPECT_TRUE(response.action == JsonCommander::Action::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_TRUE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["error"].asString(), "");
+	EXPECT_TRUE(response.json["transfersclientserver"].isNull());
+	EXPECT_TRUE(response.json["transfersserverserver"].isNull());
+}
+
+TEST(ExtendedStatus, ClientServerDownload) {
+	FileManagerMock fileManager;
+	JsonCommander jsonCommander(fileManager);
+	ChannelControlsMock channelControls;
+	Queue::channel = &channelControls;
+	Queue::queue.clear();
+
+	const std::string command = "extendedstatus";
+
+	Json::Value message;
+	message["command"] = command;
+
+	EXPECT_CALL(fileManager, isUploading()).WillOnce(testing::Return(false));
+	EXPECT_CALL(fileManager, isDownloading()).WillOnce(testing::Return(true));
+	EXPECT_CALL(fileManager, getGetBaseFileName()).WillOnce(testing::Return("asdf"));
+	EXPECT_CALL(channelControls, isTransferRunning()).WillOnce(testing::Return(false));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+	EXPECT_TRUE(response.action == JsonCommander::Action::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_TRUE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["error"].asString(), "");
+	EXPECT_EQ(response.json["transfersclientserver"].size(), 1);
+	EXPECT_FALSE(response.json["transfersclientserver"][0]["upload"].asBool());
+	EXPECT_EQ(response.json["transfersclientserver"][0]["file"].asString(), "asdf");
+	EXPECT_EQ(response.json["transfersclientserver"][0]["progress"].asInt(), 0);
+}
+
+TEST(ExtendedStatus, ClientServerUpload) {
+	FileManagerMock fileManager;
+	JsonCommander jsonCommander(fileManager);
+	ChannelControlsMock channelControls;
+	Queue::channel = &channelControls;
+	Queue::queue.clear();
+
+	const std::string command = "extendedstatus";
+
+	Json::Value message;
+	message["command"] = command;
+
+	EXPECT_CALL(fileManager, isUploading()).WillOnce(testing::Return(true));
+	EXPECT_CALL(fileManager, isDownloading()).WillOnce(testing::Return(false));
+	EXPECT_CALL(fileManager, getPutBaseFileName()).WillOnce(testing::Return("asdf"));
+	EXPECT_CALL(channelControls, isTransferRunning()).WillOnce(testing::Return(false));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+	EXPECT_TRUE(response.action == JsonCommander::Action::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_TRUE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["error"].asString(), "");
+	EXPECT_EQ(response.json["transfersclientserver"].size(), 1);
+	EXPECT_TRUE(response.json["transfersclientserver"][0]["upload"].asBool());
+	EXPECT_EQ(response.json["transfersclientserver"][0]["file"].asString(), "asdf");
+	EXPECT_EQ(response.json["transfersclientserver"][0]["progress"].asInt(), 0);
+}
+
+TEST(ExtendedStatus, ClientServerDonwloadAndUpload) {
+	FileManagerMock fileManager;
+	JsonCommander jsonCommander(fileManager);
+	ChannelControlsMock channelControls;
+	Queue::channel = &channelControls;
+	Queue::queue.clear();
+
+	const std::string command = "extendedstatus";
+
+	Json::Value message;
+	message["command"] = command;
+
+	EXPECT_CALL(fileManager, isUploading()).WillOnce(testing::Return(true));
+	EXPECT_CALL(fileManager, isDownloading()).WillOnce(testing::Return(true));
+	EXPECT_CALL(fileManager, getGetBaseFileName()).WillOnce(testing::Return("asdfGet"));
+	EXPECT_CALL(fileManager, getPutBaseFileName()).WillOnce(testing::Return("asdfPut"));
+	EXPECT_CALL(channelControls, isTransferRunning()).WillOnce(testing::Return(false));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+	EXPECT_TRUE(response.action == JsonCommander::Action::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_TRUE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["error"].asString(), "");
+	EXPECT_EQ(response.json["transfersclientserver"].size(), 2);
+	EXPECT_TRUE(response.json["transfersclientserver"][0]["upload"].asBool());
+	EXPECT_EQ(response.json["transfersclientserver"][0]["file"].asString(), "asdfPut");
+	EXPECT_EQ(response.json["transfersclientserver"][0]["progress"].asInt(), 0);
+	EXPECT_FALSE(response.json["transfersclientserver"][1]["upload"].asBool());
+	EXPECT_EQ(response.json["transfersclientserver"][1]["file"].asString(), "asdfGet");
+	EXPECT_EQ(response.json["transfersclientserver"][1]["progress"].asInt(), 0);
+}
+
+TEST(ExtendedStatus, ServerServerDownload) {
+	FileManagerMock fileManager;
+	JsonCommander jsonCommander(fileManager);
+	ChannelControlsMock channelControls;
+	Queue::channel = &channelControls;
+	Queue::queue.clear();
+	Queue::curTransfer = "a.txt";
+	Config::storage.clear();
+	Config::storage.insert(std::pair<std::string, std::string>("passiveMode", "true"));
+	Config::storage.insert(std::pair<std::string, std::string>("covertChannelMode", "m"));
+
+	const std::string command = "extendedstatus";
+
+	Json::Value message;
+	message["command"] = command;
+
+	EXPECT_CALL(fileManager, isUploading()).WillOnce(testing::Return(false));
+	EXPECT_CALL(fileManager, isDownloading()).WillOnce(testing::Return(false));
+	EXPECT_CALL(channelControls, isTransferRunning()).WillOnce(testing::Return(true));
+	EXPECT_CALL(channelControls, getProgress()).WillOnce(testing::Return(std::pair<int, int>(1, 2)));
+	EXPECT_CALL(channelControls, getTransferStart()).WillOnce(testing::Return(0));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+	EXPECT_TRUE(response.action == JsonCommander::Action::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_TRUE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["error"].asString(), "");
+	EXPECT_EQ(response.json["transfersclientserver"].size(), 0);
+	EXPECT_EQ(response.json["transfersserverserver"].size(), 1);
+	EXPECT_EQ(response.json["transfersserverserver"][0]["type"].asString(), "download");
+	EXPECT_EQ(response.json["transfersserverserver"][0]["file"].asString(), "a.txt");
+	EXPECT_EQ(response.json["transfersserverserver"][0]["progress"].asInt(), 50);
+	EXPECT_TRUE(response.json["transfersserverserver"][0]["speed"].isDouble());
+	EXPECT_EQ(response.json["transfersserverserver"][0]["method"].asString(), "m");
+}
+
+TEST(ExtendedStatus, ServerServerUpload) {
+	FileManagerMock fileManager;
+	JsonCommander jsonCommander(fileManager);
+	ChannelControlsMock channelControls;
+	Queue::channel = &channelControls;
+	Queue::queue.clear();
+	Queue::curTransfer = "a.txt";
+	Config::storage.clear();
+	Config::storage.insert(std::pair<std::string, std::string>("passiveMode", "false"));
+	Config::storage.insert(std::pair<std::string, std::string>("covertChannelMode", "m"));
+
+	const std::string command = "extendedstatus";
+
+	Json::Value message;
+	message["command"] = command;
+
+	EXPECT_CALL(fileManager, isUploading()).WillOnce(testing::Return(false));
+	EXPECT_CALL(fileManager, isDownloading()).WillOnce(testing::Return(false));
+	EXPECT_CALL(channelControls, isTransferRunning()).WillOnce(testing::Return(true));
+	EXPECT_CALL(channelControls, getProgress()).WillOnce(testing::Return(std::pair<int, int>(1, 2)));
+	EXPECT_CALL(channelControls, getTransferStart()).WillOnce(testing::Return(0));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+	EXPECT_TRUE(response.action == JsonCommander::Action::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_TRUE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["error"].asString(), "");
+	EXPECT_EQ(response.json["transfersclientserver"].size(), 0);
+	EXPECT_EQ(response.json["transfersserverserver"].size(), 1);
+	EXPECT_EQ(response.json["transfersserverserver"][0]["type"].asString(), "upload");
+	EXPECT_EQ(response.json["transfersserverserver"][0]["file"].asString(), "a.txt");
+	EXPECT_EQ(response.json["transfersserverserver"][0]["progress"].asInt(), 50);
+	EXPECT_TRUE(response.json["transfersserverserver"][0]["speed"].isDouble());
+	EXPECT_EQ(response.json["transfersserverserver"][0]["method"].asString(), "m");
+}
+
+TEST(ExtendedStatus, QueueNotEmpty) {
+	FileManagerMock fileManager;
+	JsonCommander jsonCommander(fileManager);
+	ChannelControlsMock channelControls;
+	Queue::channel = &channelControls;
+	Queue::queue.clear();
+	Queue::queue.push_back("a");
+	Queue::queue.push_back("b");
+	Config::storage.clear();
+	Config::storage.insert(std::pair<std::string, std::string>("covertChannelMode", "m"));
+
+	const std::string command = "extendedstatus";
+
+	Json::Value message;
+	message["command"] = command;
+
+	EXPECT_CALL(fileManager, isUploading()).WillOnce(testing::Return(false));
+	EXPECT_CALL(fileManager, isDownloading()).WillOnce(testing::Return(false));
+	EXPECT_CALL(channelControls, isTransferRunning()).WillOnce(testing::Return(false));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+	EXPECT_TRUE(response.action == JsonCommander::Action::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_TRUE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["error"].asString(), "");
+	EXPECT_EQ(response.json["transfersclientserver"].size(), 0);
+	EXPECT_EQ(response.json["transfersserverserver"].size(), 2);
+	EXPECT_EQ(response.json["transfersserverserver"][0]["type"], "queued");
+	EXPECT_EQ(response.json["transfersserverserver"][0]["file"], "a");
+	EXPECT_EQ(response.json["transfersserverserver"][0]["progress"], 0);
+	EXPECT_EQ(response.json["transfersserverserver"][0]["speed"], 0);
+	EXPECT_EQ(response.json["transfersserverserver"][0]["method"], "m");
+	EXPECT_EQ(response.json["transfersserverserver"][1]["type"], "queued");
+	EXPECT_EQ(response.json["transfersserverserver"][1]["file"], "b");
+	EXPECT_EQ(response.json["transfersserverserver"][1]["progress"], 0);
+	EXPECT_EQ(response.json["transfersserverserver"][1]["speed"], 0);
+	EXPECT_EQ(response.json["transfersserverserver"][1]["method"], "m");
+}
+
 TEST(Notifications, NoMessage) {
 	FileManagerMock fileManager;
 

+ 17 - 0
daemon/test/QueueMock.cpp

@@ -0,0 +1,17 @@
+#include "../include/Queue.h"
+
+namespace Queue {
+std::deque<std::string> queue;
+std::string curTransfer = "";
+ChannelControls *channel;
+} // namespace Queue
+
+bool Queue::push(const std::string &file) {
+	queue.push_back(file);
+
+	return true;
+}
+
+bool Queue::remove(const std::string &file) { return true; }
+
+void Queue::schedule() {}