/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 *
 * (c) ZeroTier, Inc.
 * https://www.zerotier.com/
 */

#include "WinFWHelper.hpp"

namespace ZeroTier {

void ZeroTier::WinFWHelper::newICMPRule(const InetAddress& ip, uint64_t nwid)
{
	char nwString[32] = { 0 };
	char ipbuf[64];

	sprintf(nwString, "%.16llx", nwid);
	std::string nwString2 = { nwString };

	ip.toString(ipbuf);

	if (ip.isV4()) {
		WinFWHelper::newICMPv4Rule(ipbuf, nwid);
	}
	else {
		WinFWHelper::newICMPv6Rule(ipbuf, nwid);
	}
}

void ZeroTier::WinFWHelper::removeICMPRule(const InetAddress& ip, uint64_t nwid)
{
	char nwString[32] = { 0 };
	char ipbuf[64];

	sprintf(nwString, "%.16llx", nwid);
	std::string nwString2 = { nwString };

	ip.toString(ipbuf);

	if (ip.isV4()) {
		WinFWHelper::removeICMPv4Rule(ipbuf, nwid);
	}
	else {
		WinFWHelper::removeICMPv6Rule(ipbuf, nwid);
	}
}

void WinFWHelper::newICMPv4Rule(std::string address, uint64_t nwid)
{
	// allows icmp, scoped to a specific ip address and interface name

	char nwString[32] = { 0 };
	sprintf(nwString, "%.16llx", nwid);
	std::string nwString2 = { nwString };

	std::string cmd = R"(C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe "New-NetFirewallRule -DisplayName zerotier-icmpv4-)" + nwString2 + address + R"( -InterfaceAlias 'ZeroTier One `[)" + nwString2 + R"(`]')"
					  + " -Protocol ICMPv4 -Action Allow" + " -LocalAddress " + address + "\"\r\n";

	_run(cmd);
}

void WinFWHelper::newICMPv6Rule(std::string address, uint64_t nwid)
{
	// allows icmp, scoped to a specific ip address and interface name

	char nwString[32] = { 0 };
	sprintf(nwString, "%.16llx", nwid);
	std::string nwString2 = { nwString };

	std::string cmd = R"(C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe "New-NetFirewallRule -DisplayName zerotier-icmpv6-)" + nwString2 + address + R"( -InterfaceAlias 'ZeroTier One `[)" + nwString2 + R"(`]')"
					  + " -Protocol ICMPv6 -Action Allow" + " -LocalAddress " + address + "\"\r\n";

	_run(cmd);
}

void WinFWHelper::removeICMPv4Rule(std::string addr, uint64_t nwid)
{
	// removes 1 icmp firewall rule

	char nwString[32] = { 0 };
	sprintf(nwString, "%.16llx", nwid);
	std::string nwString2 = { nwString };

	std::string cmd = R"(C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe "Remove-NetFirewallRule -DisplayName zerotier-icmpv4-)" + nwString2 + addr + "\"\r\n";

	_run(cmd);
}

void WinFWHelper::removeICMPv6Rule(std::string addr, uint64_t nwid)
{
	// removes 1 icmp firewall rule

	char nwString[32] = { 0 };
	sprintf(nwString, "%.16llx", nwid);
	std::string nwString2 = { nwString };

	std::string cmd = R"(C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe "Remove-NetFirewallRule -DisplayName zerotier-icmpv6-)" + nwString2 + addr + "\"\r\n";

	_run(cmd);
}

void WinFWHelper::removeICMPv4Rules(uint64_t nwid)
{
	// removes all icmp firewall rules for this network id

	char nwString[32] = { 0 };
	sprintf(nwString, "%.16llx", nwid);
	std::string nwString2 = { nwString };

	std::string cmd = R"(C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe "Remove-NetFirewallRule -DisplayName zerotier-icmpv4-)" + nwString2 + "*\" \r\n";

	_run(cmd);
}

void WinFWHelper::removeICMPv6Rules(uint64_t nwid)
{
	// removes all icmp firewall rules for this network id

	char nwString[32] = { 0 };
	sprintf(nwString, "%.16llx", nwid);
	std::string nwString2 = { nwString };

	std::string cmd = R"(C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe "Remove-NetFirewallRule -DisplayName zerotier-icmpv6-)" + nwString2 + "*\" \r\n";

	_run(cmd);
}

void WinFWHelper::removeICMPRules()
{
	// removes all icmp firewall rules for all networks

	std::string cmd = R"(C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe "Remove-NetFirewallRule -DisplayName zerotier-icmp*)" + std::string("\r\n");

	_run(cmd);
}

void WinFWHelper::removeICMPRules(uint64_t nwid)
{
	// removes all icmp firewall rules for this network
	WinFWHelper::removeICMPv4Rules(nwid);
	WinFWHelper::removeICMPv6Rules(nwid);
}

void WinFWHelper::_run(std::string cmd)
{
#ifdef ZT_DEBUG
	fprintf(stderr, cmd.c_str());
#endif

	STARTUPINFOA startupInfo;
	PROCESS_INFORMATION processInfo;
	startupInfo.cb = sizeof(startupInfo);
	memset(&startupInfo, 0, sizeof(STARTUPINFOA));
	memset(&processInfo, 0, sizeof(PROCESS_INFORMATION));

	if (CreateProcessA(NULL, (LPSTR)cmd.c_str(), NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &startupInfo, &processInfo)) {
		WaitForSingleObject(processInfo.hProcess, INFINITE);

		CloseHandle(processInfo.hProcess);
		CloseHandle(processInfo.hThread);
	}
}

}	// namespace ZeroTier
