503 lines
15 KiB
C++
503 lines
15 KiB
C++
// acetone, 2025
|
|
// I hate copyright of any kind. This is a public domain.
|
|
// Original source: http://git.community.i2p/acetone/i2pdtunnelwizard
|
|
|
|
#include "tunnelconstructor.h"
|
|
#include "notepad.h"
|
|
#include "randomstringgenerator.h"
|
|
#include "codec/base64_i2p.hpp"
|
|
#include "x25519/x25519cpp.h"
|
|
|
|
#include <algorithm>
|
|
|
|
TunnelConstructor::TunnelConstructor()
|
|
{
|
|
lengthVariance_.inbound = 0;
|
|
lengthVariance_.outbound = 0;
|
|
}
|
|
|
|
bool TunnelConstructor::setName(std::string name)
|
|
{
|
|
auto pos = name.find(' ');
|
|
while (pos != std::string::npos)
|
|
{
|
|
name.erase(pos, 1);
|
|
pos = name.find(' ');
|
|
}
|
|
|
|
if (name.length() > 30)
|
|
{
|
|
name.erase(30);
|
|
}
|
|
|
|
if (name.empty() or name.find('[') != std::string::npos or name.find(']') != std::string::npos)
|
|
{
|
|
errorString_ = Notepad::SetterError::name(lang_);
|
|
return false;
|
|
}
|
|
|
|
name_ = name;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TunnelConstructor::setTunnelType(TunnelType type)
|
|
{
|
|
tunnelType_ = type;
|
|
return true;
|
|
}
|
|
|
|
bool TunnelConstructor::setLength(int8_t inbound, int8_t outbound)
|
|
{
|
|
if (inbound < 0 or inbound > 8)
|
|
{
|
|
errorString_ = Notepad::SetterError::length(lang_);
|
|
return false;
|
|
}
|
|
if (outbound < 0 or outbound > 8)
|
|
{
|
|
errorString_ = Notepad::SetterError::length(lang_);
|
|
return false;
|
|
}
|
|
length_.inbound = inbound;
|
|
length_.outbound = outbound;
|
|
return true;
|
|
}
|
|
|
|
bool TunnelConstructor::setLengthVariance(int8_t inbound, int8_t outbound)
|
|
{
|
|
if (inbound > 3 or inbound < -3)
|
|
{
|
|
errorString_ = Notepad::SetterError::variance(lang_);
|
|
return false;
|
|
}
|
|
if (outbound > 3 or outbound < -3)
|
|
{
|
|
errorString_ = Notepad::SetterError::variance(lang_);
|
|
return false;
|
|
}
|
|
lengthVariance_.inbound = inbound;
|
|
lengthVariance_.outbound = outbound;
|
|
return true;
|
|
}
|
|
|
|
bool TunnelConstructor::setQuantity(int8_t inbound, int8_t outbound)
|
|
{
|
|
if (inbound > 16 or inbound < 1)
|
|
{
|
|
errorString_ = Notepad::SetterError::quantity(lang_);
|
|
return false;
|
|
}
|
|
if (outbound > 16 or outbound < 1)
|
|
{
|
|
errorString_ = Notepad::SetterError::quantity(lang_);
|
|
return false;
|
|
}
|
|
quantity_.inbound = inbound;
|
|
quantity_.outbound = outbound;
|
|
return true;
|
|
}
|
|
|
|
bool TunnelConstructor::setBlindedLeaseSet(TunnelConstructor::BlindedType type)
|
|
{
|
|
blinded_ = type;
|
|
return true;
|
|
}
|
|
|
|
bool TunnelConstructor::setTransient(bool isTransient)
|
|
{
|
|
transient_ = isTransient;
|
|
return true;
|
|
}
|
|
|
|
bool TunnelConstructor::setKeepAlive(int8_t interval)
|
|
{
|
|
if (interval < 0)
|
|
{
|
|
interval = 0;
|
|
}
|
|
keepAliveInterval_ = interval;
|
|
return true;
|
|
}
|
|
|
|
bool TunnelConstructor::setHostOverride(bool wantCustomHost)
|
|
{
|
|
hostoverride_ = wantCustomHost;
|
|
return true;
|
|
}
|
|
|
|
bool TunnelConstructor::setSsl(const bool isSslUpstream)
|
|
{
|
|
ssl_ = isSslUpstream;
|
|
return true;
|
|
}
|
|
|
|
bool TunnelConstructor::setMaxStreamCount(int count)
|
|
{
|
|
if (count < 1)
|
|
{
|
|
errorString_ = Notepad::SetterError::maxStreamCount(lang_);
|
|
return false;
|
|
}
|
|
maxStreamCount_ = count;
|
|
return true;
|
|
}
|
|
|
|
bool TunnelConstructor::setProxyOutproxy(bool isEnabled)
|
|
{
|
|
proxyOutproxy_ = isEnabled;
|
|
return true;
|
|
}
|
|
|
|
bool TunnelConstructor::setComments(bool enabled)
|
|
{
|
|
comments_ = enabled;
|
|
return true;
|
|
}
|
|
|
|
bool TunnelConstructor::setB33UserCount(uint16_t number)
|
|
{
|
|
if (number > 30)
|
|
{
|
|
errorString_ = Notepad::SetterError::b33UserCount(lang_);
|
|
return false;
|
|
}
|
|
b33UserCount_ = number;
|
|
return true;
|
|
}
|
|
|
|
bool TunnelConstructor::setIrcWebPassword(bool wantToUse)
|
|
{
|
|
webircpassword_ = wantToUse;
|
|
return true;
|
|
}
|
|
|
|
std::u8string TunnelConstructor::generate() const
|
|
{
|
|
std::u8string config;
|
|
config += u8"[" + std::u8string{ name_.begin(), name_.end() } + u8"]\n";
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::type(lang_, tunnelType_);
|
|
const auto typeString = tunnelTypeToString(tunnelType_);
|
|
config += u8"type = " + std::u8string{ typeString.begin(), typeString.end() } + u8"\n";
|
|
|
|
if (isClientType(tunnelType_))
|
|
{
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::clientAddress(lang_);
|
|
config += u8"address = LOCAL_INTERFACE_ADDRESS\n";
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::clientPort(lang_);
|
|
config += u8"port = LOCAL_INTERFACE_PORT\n";
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::clientDestination(lang_);
|
|
config += u8"destination = I2P_SERVER_ADDRESS\n";
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::clientDestinationPort(lang_);
|
|
config += u8"destinationport = I2P_SERVER_PORT\n";
|
|
}
|
|
else
|
|
{
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::serverAddress(lang_);
|
|
config += u8"address = LOCAL_INTERFACE_ADDRESS\n";
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::serverHost(lang_);
|
|
config += u8"host = IP_ADDRESS_OF_APPLICATION\n";
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::serverPort(lang_);
|
|
config += u8"port = APPLICATION_PORT\n";
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::serverInport(lang_);
|
|
config += u8"inport = I2P_SERVER_PORT\n";
|
|
}
|
|
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::inbound(lang_);
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::length(lang_);
|
|
config += u8"inbound.length = " + std::u8string( reinterpret_cast<const char8_t*>(std::to_string(length_.inbound).c_str()) ) + u8"\n";
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::quantity(lang_);
|
|
config += u8"inbound.quantity = " + std::u8string( reinterpret_cast<const char8_t*>(std::to_string(quantity_.inbound).c_str()) ) + u8"\n";
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::variance(lang_);
|
|
config += u8"inbound.lengthVariance = " + std::u8string( reinterpret_cast<const char8_t*>(std::to_string(lengthVariance_.inbound).c_str()) ) + u8"\n";
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::outbound(lang_);
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::length(lang_);
|
|
config += u8"outbound.length = " + std::u8string( reinterpret_cast<const char8_t*>(std::to_string(length_.outbound).c_str()) ) + u8"\n";
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::quantity(lang_);
|
|
config += u8"outbound.quantity = " + std::u8string( reinterpret_cast<const char8_t*>(std::to_string(quantity_.outbound).c_str()) ) + u8"\n";
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::variance(lang_);
|
|
config += u8"outbound.lengthVariance = " + std::u8string( reinterpret_cast<const char8_t*>(std::to_string(lengthVariance_.outbound).c_str()) ) + u8"\n";
|
|
|
|
if (isClientType(tunnelType_))
|
|
{
|
|
if (keepAliveInterval_)
|
|
{
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::clientKeepAlive(lang_);
|
|
config += u8"keepaliveinterval = " + std::u8string( reinterpret_cast<const char8_t*>(std::to_string(keepAliveInterval_).c_str()) ) + u8"\n";
|
|
}
|
|
|
|
if (blinded_ != BlindedType::none)
|
|
{
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::b33ClientKey(lang_);
|
|
config += u8"i2cp.leaseSetPrivKey = BASE64_ENCODED_DATA\n";
|
|
}
|
|
|
|
if (tunnelType_ == TunnelType::HttpProxy and proxyOutproxy_)
|
|
{
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::httpOutproxy(lang_);
|
|
config += u8"outproxy = PROXYSERVER\n";
|
|
}
|
|
else if (tunnelType_ == TunnelType::SocksProxy and proxyOutproxy_)
|
|
{
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::socksOutproxy(lang_);
|
|
config += u8"outproxy = PROXY_ADDRES\n"
|
|
"outproxyport = PROXY_PORT\n";
|
|
}
|
|
}
|
|
else // server
|
|
{
|
|
if (ssl_)
|
|
{
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::ssl(lang_);
|
|
config += u8"ssl = true\n";
|
|
}
|
|
|
|
if (tunnelType_ != TunnelType::UdpServer)
|
|
{
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::maxStreamCount(lang_);
|
|
config += u8"i2p.streaming.maxConcurrentStreams = " + std::u8string( reinterpret_cast<const char8_t*>(std::to_string(maxStreamCount_).c_str()) ) + u8"\n";
|
|
}
|
|
|
|
if (tunnelType_ == TunnelType::HttpServer and hostoverride_)
|
|
{
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::hostoverride(lang_);
|
|
config += u8"hostoverride = HOSTNAME\n";
|
|
}
|
|
else if (tunnelType_ == TunnelType::IrcServer and webircpassword_)
|
|
{
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::ircPassword(lang_);
|
|
config += u8"webircpassword = PASSWORD_FROM_YOUR_IRC_SERVER_SETTINGS\n";
|
|
}
|
|
|
|
if (blinded_ != BlindedType::none and blinded_ != BlindedType::client) // blinded server
|
|
{
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::serverBlinded(lang_);
|
|
|
|
config += u8"signaturetype = 11\n"
|
|
"i2cp.leaseSetType = 5\n";
|
|
|
|
if (blinded_ == BlindedType::password)
|
|
{
|
|
config += u8"i2cp.leaseSetAuthType = 2\n";
|
|
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::serverBlindedWithPsk(lang_);
|
|
|
|
for (uint16_t i = 1; i < b33UserCount_+1; ++i)
|
|
{
|
|
// yes. this is C++ casting hell :)
|
|
// i2cp.leaseSetClient.psk.NUMBER = userNUMBER:b64_password
|
|
const auto password = RandomStringGenerator::getU8string(32);
|
|
config += u8"i2cp.leaseSetClient.psk." + std::u8string(reinterpret_cast<const char8_t*>(std::to_string(i).c_str())) + u8" = user" + std::u8string(reinterpret_cast<const char8_t*>(std::to_string(i).c_str())) + u8":";
|
|
config += std::u8string(reinterpret_cast<const char8_t*>( cppcodec_samty::base64_i2p::encode( reinterpret_cast<const char*>(password.c_str()), password.size() ).c_str() ));
|
|
config += u8"\n";
|
|
}
|
|
}
|
|
else if (blinded_ == BlindedType::keys)
|
|
{
|
|
config += u8"i2cp.leaseSetAuthType = 1\n";
|
|
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::serverBlindedWithDh(lang_);
|
|
|
|
for (uint16_t i = 1; i < b33UserCount_+1; ++i)
|
|
{
|
|
// i2cp.leaseSetClient.dh.NUMBER = userNUMBER:b64_public_key
|
|
X25519Keys keys;
|
|
keys.generateKeys();
|
|
config += Notepad::ConfigComment::serverBlindedWithDhPrivateKey(lang_);
|
|
config += u8"user" + std::u8string(reinterpret_cast<const char8_t*>(std::to_string(i).c_str())) + u8": ";
|
|
config += reinterpret_cast<const char8_t*>( keys.getSecretKeyBase64().c_str() );
|
|
config += u8"\n";
|
|
config += u8"i2cp.leaseSetClient.dh." + std::u8string(reinterpret_cast<const char8_t*>(std::to_string(i).c_str())) + u8" = user" + std::u8string(reinterpret_cast<const char8_t*>(std::to_string(i).c_str())) + u8":";
|
|
config += std::u8string(reinterpret_cast<const char8_t*>( keys.getPublicKeyBase64().c_str() ));
|
|
config += u8"\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (transient_)
|
|
{
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::keysTransient(lang_);
|
|
config += u8"keys = transient-" + RandomStringGenerator::getU8string(5) + u8"\n";
|
|
}
|
|
else
|
|
{
|
|
std::string name(name_);
|
|
std::transform(name.begin(), name.end(), name.begin(),
|
|
[](unsigned char c) { return std::tolower(c); });
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::keys(lang_);
|
|
config += u8"keys = " + std::u8string( reinterpret_cast<const char8_t*>(name.c_str()) ) + u8"-" + RandomStringGenerator::getU8string(5) + u8".dat\n";
|
|
}
|
|
|
|
if (comments_)
|
|
config += Notepad::ConfigComment::footer(lang_, not isClientType(tunnelType_) and blinded_ != BlindedType::none, name_);
|
|
|
|
return config;
|
|
}
|
|
|
|
std::string TunnelConstructor::blindedTypeToString(BlindedType type)
|
|
{
|
|
switch(type)
|
|
{
|
|
case BlindedType::keys: {
|
|
return "dh";
|
|
}
|
|
case BlindedType::password: {
|
|
return "psk";
|
|
}
|
|
case BlindedType::simple: {
|
|
return "simple";
|
|
}
|
|
case BlindedType::client: {
|
|
return "client";
|
|
}
|
|
default :{
|
|
return "none";
|
|
}
|
|
}
|
|
}
|
|
|
|
TunnelConstructor::BlindedType TunnelConstructor::stringToBlindedType(const std::string &type)
|
|
{
|
|
if (type == "dh")
|
|
{
|
|
return BlindedType::keys;
|
|
}
|
|
else if (type == "psk")
|
|
{
|
|
return BlindedType::password;
|
|
}
|
|
else if (type == "simple")
|
|
{
|
|
return BlindedType::simple;
|
|
}
|
|
else if (type == "client")
|
|
{
|
|
return BlindedType::client;
|
|
}
|
|
return BlindedType::none;
|
|
}
|
|
|
|
std::string TunnelConstructor::tunnelTypeToString(TunnelType type)
|
|
{
|
|
switch(type)
|
|
{
|
|
case TunnelType::TcpClient: {
|
|
return "client";
|
|
}
|
|
case TunnelType::TcpServer: {
|
|
return "server";
|
|
}
|
|
case TunnelType::UdpClient: {
|
|
return "udpclient";
|
|
}
|
|
case TunnelType::UdpServer: {
|
|
return "udpserver";
|
|
}
|
|
case TunnelType::HttpServer: {
|
|
return "http";
|
|
}
|
|
case TunnelType::IrcServer: {
|
|
return "irc";
|
|
}
|
|
case TunnelType::SocksProxy: {
|
|
return "socks";
|
|
}
|
|
case TunnelType::HttpProxy: {
|
|
return "httpproxy";
|
|
}
|
|
}
|
|
|
|
return "undefined(bug)";
|
|
}
|
|
|
|
TunnelType TunnelConstructor::stringToTunnelType(const std::string &type)
|
|
{
|
|
if (type == "client")
|
|
{
|
|
return TunnelType::TcpClient;
|
|
}
|
|
else if (type == "server")
|
|
{
|
|
return TunnelType::TcpServer;
|
|
}
|
|
else if (type == "udpclient")
|
|
{
|
|
return TunnelType::UdpClient;
|
|
}
|
|
else if (type == "udpserver")
|
|
{
|
|
return TunnelType::UdpServer;
|
|
}
|
|
else if (type == "http")
|
|
{
|
|
return TunnelType::HttpServer;
|
|
}
|
|
else if (type == "irc")
|
|
{
|
|
return TunnelType::IrcServer;
|
|
}
|
|
else if (type == "socks")
|
|
{
|
|
return TunnelType::SocksProxy;
|
|
}
|
|
else if (type == "httpproxy")
|
|
{
|
|
return TunnelType::HttpProxy;
|
|
}
|
|
|
|
return TunnelType::TcpClient;
|
|
}
|
|
|
|
bool TunnelConstructor::isClientType(TunnelType type)
|
|
{
|
|
switch(type)
|
|
{
|
|
case TunnelType::TcpClient: {
|
|
return true;
|
|
}
|
|
case TunnelType::UdpClient: {
|
|
return true;
|
|
}
|
|
case TunnelType::HttpProxy: {
|
|
return true;
|
|
}
|
|
case TunnelType::SocksProxy: {
|
|
return true;
|
|
}
|
|
default : {
|
|
return false;
|
|
}
|
|
}
|
|
}
|