diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..4ef8594 --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,54 @@ +name: TVPN Actions Workflow +run-name: Deploy to ${{ inputs.deploy_target }} by ${{ gitea.actor }} +on: [push] + +jobs: + build: + runs-on: windows-10 + steps: + - uses: actions/checkout@v4 + #- name: Setup JDK 21 + # uses: actions-setup-java@v4.4.0 + # with: + # java-version: 21 + # distribution: 'temurin' + - name: Test Java + run: java --version + - name: Configure Git + run: | + git config --global user.email "git@nevets.tech" + git config --global user.name "Gitea Actions" + - name: Build TVPN Jar + run: .\gradlew.bat clean shadowJar createExe + - name: Copy Header to C Dir + run: | + cp build/generated/sources/headers/java/main/tech_nevets_tvpn_wg_WireGuardJNI.h src/main/cpp/ + - name: Build DLL + run: | + cmake -DCMAKE_BUILD_TYPE=Release -G "CodeBlocks - MinGW Makefiles" -S ./src/main/cpp -B ./src/main/cpp/build + cmake --build ./src/main/cpp/build --target wireguard_wrapper -- -j 14 + # uses: actions/cmake-action@v2.0.0 + # with: + # source-dir: src/main/cpp + # output-dir: src/main/cpp/build + - name: Prepare artifacts + run: | + Get-ChildItem -Path artifacts -Recurse | Remove-Item -force -recurse + if (Test-Path -Path .\artifacts) { Remove-Item -Path .\artifacts -Recurse -Force } + $version = (Select-String -Path "build.gradle" -Pattern "^\s*version\s*=\s*'([^']+)'" | ForEach-Object { $_.Matches[0].Groups[1].Value }) + echo Version: $version + New-Item -Path "artifacts" -ItemType Directory + Copy-Item -Path "./build/libs/TVPN-$version-all.jar" -Destination "./artifacts/TVPN-$version.jar" -Verbose + Copy-Item -Path "./build/launch4j/TVPN.exe" -Destination "./artifacts/TVPN.exe" -Verbose + Copy-Item -Path "./src/main/cpp/build/libwireguard_wrapper.dll" -Destination "./artifacts/libwireguard_wrapper.dll" -Verbose + Copy-Item -Path "./libs/tunnel.dll" -Destination "./artifacts/tunnel.dll" -Verbose + Copy-Item -Path "./libs/wireguard.dll" -Destination "./artifacts/wireguard.dll" -Verbose + - name: Create Installer + run: | + git clone https://git.nevets.tech/Steven/TVPN-jre.git jre + ISCC /DMyAppVersion=$version "./installer-script.iss" + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: artifacts + path: artifacts\* diff --git a/.gitignore b/.gitignore index 2d797e8..3a46706 100644 --- a/.gitignore +++ b/.gitignore @@ -152,3 +152,8 @@ fabric.properties # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser +src/main/cpp/tech_nevets_tvpn_wg_WireGuardJNI.h + +jre/ +src/main/cpp/build +inno/ \ No newline at end of file diff --git a/TVPN.exe.manifest b/TVPN.exe.manifest new file mode 100644 index 0000000..3119ef3 --- /dev/null +++ b/TVPN.exe.manifest @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 262d827..a889fc5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,18 @@ plugins { id 'java' id 'com.github.johnrengelman.shadow' version '8.1.1' + id 'edu.sc.seis.launch4j' version '3.0.6' } group = 'tech.nevets' -version = '0.2.0' +version = '0.3.0' repositories { mavenCentral() + maven { + name = "Gitea" + url = uri("https://git.nevets.tech/api/packages/Steven/maven") + } } dependencies { @@ -18,6 +23,8 @@ dependencies { //implementation "com.formdev:flatlaf:3.5.1" implementation files("libs/gson-2.11.0.jar") //implementation "com.google.code.gson:gson:2.11.0" + + implementation "tech.nevets:JConf:0.1.0:no-deps" } tasks.withType(JavaCompile).configureEach { @@ -33,4 +40,17 @@ jar { 'Main-Class': 'tech.nevets.tvpn.Main' ) } +} + +launch4j { + mainClassName = 'tech.nevets.tvpn.Main' + outfile = 'TVPN.exe' + jarTask = project.tasks.shadowJar + fileDescription = 'TVPN Client' + icon = "${projectDir}/src/main/resources/icon.ico" + productName = 'TVPN' + version = "$this.version" + textVersion = "$this.version" + bundledJrePath = './jre' + manifest = "${projectDir}/TVPN.exe.manifest" } \ No newline at end of file diff --git a/installer-script.iss b/installer-script.iss new file mode 100644 index 0000000..f09c050 --- /dev/null +++ b/installer-script.iss @@ -0,0 +1,51 @@ +; Script generated by the Inno Setup Script Wizard. +; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! + +#define MyAppName "TVPN" +#define MyAppPublisher "nevetS Tech" +#define MyAppURL "https://git.nevets.tech/Steven/TVPN" +#define MyAppExeName "TVPN.exe" + +[Setup] +; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications. +; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) +AppId={{79AFBC44-4C4B-4509-9723-5CB4F30E875F} +AppName={#MyAppName} +AppVersion={#MyAppVersion} +;AppVerName={#MyAppName} {#MyAppVersion} +AppPublisher={#MyAppPublisher} +AppPublisherURL={#MyAppURL} +AppSupportURL={#MyAppURL} +AppUpdatesURL={#MyAppURL} +DefaultDirName={autopf}\{#MyAppName} +DisableProgramGroupPage=yes +; Uncomment the following line to run in non administrative install mode (install for current user only.) +;PrivilegesRequired=lowest +OutputDir=.\artifacts +OutputBaseFilename=tvpn-installer +SetupIconFile=.\src\main\resources\icon.ico +Compression=lzma +SolidCompression=yes +WizardStyle=modern + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl" + +[Tasks] +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked + +[Files] +Source: ".\artifacts\TVPN.exe"; DestDir: "{app}"; Flags: ignoreversion +Source: ".\jre\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs +Source: ".\artifacts\wireguard.dll"; DestDir: "{app}"; Flags: ignoreversion +Source: ".\artifacts\tunnel.dll"; DestDir: "{app}"; Flags: ignoreversion +Source: ".\artifacts\libwireguard_wrapper.dll"; DestDir: "{app}"; Flags: ignoreversion +; NOTE: Don't use "Flags: ignoreversion" on any shared system files + +[Icons] +Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" +Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon + +[Run] +Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent + diff --git a/src/main/cpp/CMakeLists.txt b/src/main/cpp/CMakeLists.txt index 26936ff..10e1a71 100644 --- a/src/main/cpp/CMakeLists.txt +++ b/src/main/cpp/CMakeLists.txt @@ -17,8 +17,10 @@ include_directories(${JNI_INCLUDE_DIR} ${JNI_INCLUDE_DIR2}) # Add the source file that includes your JNI C wrapper code add_library(wireguard_wrapper SHARED wireguard_wrapper.c) +target_link_libraries(wireguard_wrapper PRIVATE Rpcrt4) + # Optionally, you can specify output directory for the compiled library set_target_properties(wireguard_wrapper PROPERTIES - LIBRARY_OUTPUT_DIRECTORY build/lib + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" OUTPUT_NAME "wireguard_wrapper" ) diff --git a/src/main/cpp/example.c b/src/main/cpp/example.c new file mode 100644 index 0000000..7d8e5bf --- /dev/null +++ b/src/main/cpp/example.c @@ -0,0 +1,444 @@ +// +// Created by steven on 10/27/2024. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wireguard.h" + +static WIREGUARD_CREATE_ADAPTER_FUNC *WireGuardCreateAdapter; +static WIREGUARD_OPEN_ADAPTER_FUNC *WireGuardOpenAdapter; +static WIREGUARD_CLOSE_ADAPTER_FUNC *WireGuardCloseAdapter; +static WIREGUARD_GET_ADAPTER_LUID_FUNC *WireGuardGetAdapterLUID; +static WIREGUARD_GET_RUNNING_DRIVER_VERSION_FUNC *WireGuardGetRunningDriverVersion; +static WIREGUARD_DELETE_DRIVER_FUNC *WireGuardDeleteDriver; +static WIREGUARD_SET_LOGGER_FUNC *WireGuardSetLogger; +static WIREGUARD_SET_ADAPTER_LOGGING_FUNC *WireGuardSetAdapterLogging; +static WIREGUARD_GET_ADAPTER_STATE_FUNC *WireGuardGetAdapterState; +static WIREGUARD_SET_ADAPTER_STATE_FUNC *WireGuardSetAdapterState; +static WIREGUARD_GET_CONFIGURATION_FUNC *WireGuardGetConfiguration; +static WIREGUARD_SET_CONFIGURATION_FUNC *WireGuardSetConfiguration; + +static HMODULE +InitializeWireGuardNT(void) +{ + HMODULE WireGuardDll = + LoadLibraryExW(L"wireguard.dll", NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32); + if (!WireGuardDll) + return NULL; +#define X(Name) ((*(FARPROC *)&Name = GetProcAddress(WireGuardDll, #Name)) == NULL) + if (X(WireGuardCreateAdapter) || X(WireGuardOpenAdapter) || X(WireGuardCloseAdapter) || + X(WireGuardGetAdapterLUID) || X(WireGuardGetRunningDriverVersion) || X(WireGuardDeleteDriver) || + X(WireGuardSetLogger) || X(WireGuardSetAdapterLogging) || X(WireGuardGetAdapterState) || + X(WireGuardSetAdapterState) || X(WireGuardGetConfiguration) || X(WireGuardSetConfiguration)) +#undef X + { + DWORD LastError = GetLastError(); + FreeLibrary(WireGuardDll); + SetLastError(LastError); + return NULL; + } + return WireGuardDll; +} + +static void CALLBACK +ConsoleLogger(_In_ WIREGUARD_LOGGER_LEVEL Level, _In_ DWORD64 Timestamp, _In_z_ const WCHAR *LogLine) +{ + SYSTEMTIME SystemTime; + FileTimeToSystemTime((FILETIME *)&Timestamp, &SystemTime); + WCHAR LevelMarker; + switch (Level) + { + case WIREGUARD_LOG_INFO: + LevelMarker = L'+'; + break; + case WIREGUARD_LOG_WARN: + LevelMarker = L'-'; + break; + case WIREGUARD_LOG_ERR: + LevelMarker = L'!'; + break; + default: + return; + } + fwprintf( + stderr, + L"%04u-%02u-%02u %02u:%02u:%02u.%04u [%c] %s\n", + SystemTime.wYear, + SystemTime.wMonth, + SystemTime.wDay, + SystemTime.wHour, + SystemTime.wMinute, + SystemTime.wSecond, + SystemTime.wMilliseconds, + LevelMarker, + LogLine); +} + +static DWORD64 Now(VOID) +{ + LARGE_INTEGER Timestamp; + NtQuerySystemTime(&Timestamp); + return Timestamp.QuadPart; +} + +static DWORD +LogError(_In_z_ const WCHAR *Prefix, _In_ DWORD Error) +{ + WCHAR *SystemMessage = NULL, *FormattedMessage = NULL; + FormatMessageW( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, + HRESULT_FROM_SETUPAPI(Error), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (void *)&SystemMessage, + 0, + NULL); + FormatMessageW( + FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY | + FORMAT_MESSAGE_MAX_WIDTH_MASK, + SystemMessage ? L"%1: %3(Code 0x%2!08X!)" : L"%1: Code 0x%2!08X!", + 0, + 0, + (void *)&FormattedMessage, + 0, + (va_list *)(DWORD_PTR[]){ (DWORD_PTR)Prefix, (DWORD_PTR)Error, (DWORD_PTR)SystemMessage }); + if (FormattedMessage) + ConsoleLogger(WIREGUARD_LOG_ERR, Now(), FormattedMessage); + LocalFree(FormattedMessage); + LocalFree(SystemMessage); + return Error; +} + +static DWORD +LogLastError(_In_z_ const WCHAR *Prefix) +{ + DWORD LastError = GetLastError(); + LogError(Prefix, LastError); + SetLastError(LastError); + return LastError; +} + +static void +Log(_In_ WIREGUARD_LOGGER_LEVEL Level, _In_z_ const WCHAR *Format, ...) +{ + WCHAR LogLine[0x200]; + va_list args; + va_start(args, Format); + _vsnwprintf_s(LogLine, _countof(LogLine), _TRUNCATE, Format, args); + va_end(args); + ConsoleLogger(Level, Now(), LogLine); +} + +_Must_inspect_result_ +_Return_type_success_(return != FALSE) +static BOOL +GenerateKeyPair( + _Out_writes_bytes_all_(WIREGUARD_KEY_LENGTH) BYTE PublicKey[WIREGUARD_KEY_LENGTH], + _Out_writes_bytes_all_(WIREGUARD_KEY_LENGTH) BYTE PrivateKey[WIREGUARD_KEY_LENGTH]) +{ + BCRYPT_ALG_HANDLE Algorithm; + BCRYPT_KEY_HANDLE Key; + NTSTATUS Status; + struct + { + BCRYPT_ECCKEY_BLOB Header; + BYTE Public[32]; + BYTE Unused[32]; + BYTE Private[32]; + } ExportedKey; + ULONG Bytes; + + Status = BCryptOpenAlgorithmProvider(&Algorithm, BCRYPT_ECDH_ALGORITHM, NULL, 0); + if (!NT_SUCCESS(Status)) + goto out; + + Status = BCryptSetProperty( + Algorithm, BCRYPT_ECC_CURVE_NAME, (PUCHAR)BCRYPT_ECC_CURVE_25519, sizeof(BCRYPT_ECC_CURVE_25519), 0); + if (!NT_SUCCESS(Status)) + goto cleanupProvider; + + Status = BCryptGenerateKeyPair(Algorithm, &Key, 255, 0); + if (!NT_SUCCESS(Status)) + goto cleanupProvider; + + Status = BCryptFinalizeKeyPair(Key, 0); + if (!NT_SUCCESS(Status)) + goto cleanupKey; + + Status = BCryptExportKey(Key, NULL, BCRYPT_ECCPRIVATE_BLOB, (PUCHAR)&ExportedKey, sizeof(ExportedKey), &Bytes, 0); + if (!NT_SUCCESS(Status)) + goto cleanupKey; + + memcpy(PublicKey, ExportedKey.Public, WIREGUARD_KEY_LENGTH); + memcpy(PrivateKey, ExportedKey.Private, WIREGUARD_KEY_LENGTH); + SecureZeroMemory(&ExportedKey, sizeof(ExportedKey)); + + cleanupKey: + BCryptDestroyKey(Key); + cleanupProvider: + BCryptCloseAlgorithmProvider(Algorithm, 0); + out: + SetLastError(RtlNtStatusToDosError(Status)); + return NT_SUCCESS(Status); +} + +static HANDLE QuitEvent; + +static BOOL WINAPI +CtrlHandler(_In_ DWORD CtrlType) +{ + switch (CtrlType) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + case CTRL_CLOSE_EVENT: + case CTRL_LOGOFF_EVENT: + case CTRL_SHUTDOWN_EVENT: + Log(WIREGUARD_LOG_INFO, L"Cleaning up and shutting down"); + SetEvent(QuitEvent); + return TRUE; + } + return FALSE; +} + +_Return_type_success_(return != FALSE) +static BOOL +TalkToDemoServer( + _In_reads_bytes_(InputLength) const CHAR *Input, + _In_ DWORD InputLength, + _Out_writes_bytes_(*OutputLength) CHAR *Output, + _Inout_ DWORD *OutputLength, + _Out_ SOCKADDR_INET *ResolvedDemoServer) +{ + SOCKET Socket = INVALID_SOCKET; + ADDRINFOW Hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_protocol = IPPROTO_TCP }, *Resolution; + BOOL Ret = FALSE; + + if (GetAddrInfoW(L"demo.wireguard.com", L"42912", &Hints, &Resolution)) + return FALSE; + for (ADDRINFOW *Candidate = Resolution; Candidate; Candidate = Candidate->ai_next) + { + if (Candidate->ai_family != AF_INET && Candidate->ai_family != AF_INET6) + continue; + Socket = socket(Candidate->ai_family, Candidate->ai_socktype, Candidate->ai_protocol); + if (Socket == INVALID_SOCKET) + goto cleanupResolution; + if (connect(Socket, Candidate->ai_addr, (int)Candidate->ai_addrlen) == SOCKET_ERROR) + { + closesocket(Socket); + Socket = INVALID_SOCKET; + } + memcpy(ResolvedDemoServer, Candidate->ai_addr, Candidate->ai_addrlen); + break; + } + if (Socket == INVALID_SOCKET) + goto cleanupResolution; + if (send(Socket, Input, InputLength, 0) == SOCKET_ERROR) + goto cleanupSocket; + if ((*OutputLength = recv(Socket, Output, *OutputLength, 0)) == SOCKET_ERROR) + goto cleanupSocket; + Ret = TRUE; + cleanupSocket: + closesocket(Socket); + cleanupResolution: + FreeAddrInfoW(Resolution); + return Ret; +} + +int __cdecl main(void) +{ + DWORD LastError; + WSADATA WsaData; + if (WSAStartup(MAKEWORD(2, 2), &WsaData)) + return LogError(L"Failed to initialize Winsock", GetLastError()); + HMODULE WireGuard = InitializeWireGuardNT(); + if (!WireGuard) + { + LastError = LogError(L"Failed to initialize WireGuardNT", GetLastError()); + goto cleanupWinsock; + } + WireGuardSetLogger(ConsoleLogger); + Log(WIREGUARD_LOG_INFO, L"WireGuardNT library loaded"); + + struct + { + WIREGUARD_INTERFACE Interface; + WIREGUARD_PEER DemoServer; + WIREGUARD_ALLOWED_IP AllV4; + } Config = { .Interface = { .Flags = WIREGUARD_INTERFACE_HAS_PRIVATE_KEY, .PeersCount = 1 }, + .DemoServer = { .Flags = WIREGUARD_PEER_HAS_PUBLIC_KEY | WIREGUARD_PEER_HAS_ENDPOINT, + .AllowedIPsCount = 1 }, + .AllV4 = { .AddressFamily = AF_INET } }; + + Log(WIREGUARD_LOG_INFO, L"Generating keypair"); + BYTE PublicKey[WIREGUARD_KEY_LENGTH]; + if (!GenerateKeyPair(PublicKey, Config.Interface.PrivateKey)) + { + LastError = LogError(L"Failed to generate keypair", GetLastError()); + goto cleanupWireGuard; + } + CHAR PublicKeyString[46] = { 0 }; + DWORD Bytes = sizeof(PublicKeyString); + CryptBinaryToStringA( + PublicKey, sizeof(PublicKey), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCR, PublicKeyString, &Bytes); + CHAR ServerResponse[256] = { 0 }; + Log(WIREGUARD_LOG_INFO, L"Talking to demo server"); + Bytes = sizeof(ServerResponse) - 1; + if (!TalkToDemoServer( + PublicKeyString, (DWORD)strlen(PublicKeyString), ServerResponse, &Bytes, &Config.DemoServer.Endpoint)) + { + LastError = LogError(L"Failed to talk to demo server", GetLastError()); + goto cleanupWireGuard; + } + + CHAR *Colon1 = strchr(ServerResponse, ':'); + CHAR *Colon2 = Colon1 ? strchr(Colon1 + 1, ':') : NULL; + CHAR *Colon3 = Colon2 ? strchr(Colon2 + 1, ':') : NULL; + if (!Colon1 || !Colon2 || !Colon3) + { + LastError = LogError(L"Failed to parse demo server response", ERROR_UNDEFINED_CHARACTER); + goto cleanupWireGuard; + } + if (Bytes && ServerResponse[--Bytes] == '\n') + ServerResponse[Bytes] = '\0'; + *Colon1 = *Colon2 = *Colon3 = '\0'; + + MIB_UNICASTIPADDRESS_ROW AddressRow; + InitializeUnicastIpAddressEntry(&AddressRow); + AddressRow.Address.Ipv4.sin_family = AF_INET; + AddressRow.OnLinkPrefixLength = 24; /* This is a /24 network */ + AddressRow.DadState = IpDadStatePreferred; + Bytes = sizeof(Config.DemoServer.PublicKey); + if (strcmp(ServerResponse, "OK") || InetPtonA(AF_INET, Colon3 + 1, &AddressRow.Address.Ipv4.sin_addr) != 1 || + !CryptStringToBinaryA(Colon1 + 1, 0, CRYPT_STRING_BASE64, Config.DemoServer.PublicKey, &Bytes, NULL, NULL)) + { + LastError = LogError(L"Failed to parse demo server response", ERROR_UNDEFINED_CHARACTER); + goto cleanupWireGuard; + } + if (Config.DemoServer.Endpoint.si_family == AF_INET) + Config.DemoServer.Endpoint.Ipv4.sin_port = htons((u_short)atoi(Colon2 + 1)); + else if (Config.DemoServer.Endpoint.si_family == AF_INET6) + Config.DemoServer.Endpoint.Ipv6.sin6_port = htons((u_short)atoi(Colon2 + 1)); + + QuitEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + if (!QuitEvent) + { + LastError = LogError(L"Failed to create event", GetLastError()); + goto cleanupWireGuard; + } + if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) + { + LastError = LogError(L"Failed to set console handler", GetLastError()); + goto cleanupQuit; + } + + GUID ExampleGuid = { 0xdeadc001, 0xbeef, 0xbabe, { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef } }; + WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardCreateAdapter(L"Demo", L"Example", &ExampleGuid); + if (!Adapter) + { + LastError = GetLastError(); + LogError(L"Failed to create adapter", LastError); + goto cleanupQuit; + } + + if (!WireGuardSetAdapterLogging(Adapter, WIREGUARD_ADAPTER_LOG_ON)) + LogError(L"Failed to enable adapter logging", GetLastError()); + + DWORD Version = WireGuardGetRunningDriverVersion(); + Log(WIREGUARD_LOG_INFO, L"WireGuardNT v%u.%u loaded", (Version >> 16) & 0xff, (Version >> 0) & 0xff); + + WireGuardGetAdapterLUID(Adapter, &AddressRow.InterfaceLuid); + MIB_IPFORWARD_ROW2 DefaultRoute = { 0 }; + InitializeIpForwardEntry(&DefaultRoute); + DefaultRoute.InterfaceLuid = AddressRow.InterfaceLuid; + DefaultRoute.DestinationPrefix.Prefix.si_family = AF_INET; + DefaultRoute.NextHop.si_family = AF_INET; + DefaultRoute.Metric = 0; + LastError = CreateIpForwardEntry2(&DefaultRoute); + if (LastError != ERROR_SUCCESS && LastError != ERROR_OBJECT_ALREADY_EXISTS) + { + LogError(L"Failed to set default route", LastError); + goto cleanupAdapter; + } + LastError = CreateUnicastIpAddressEntry(&AddressRow); + if (LastError != ERROR_SUCCESS && LastError != ERROR_OBJECT_ALREADY_EXISTS) + { + LogError(L"Failed to set IP address", LastError); + goto cleanupAdapter; + } + MIB_IPINTERFACE_ROW IpInterface = { 0 }; + InitializeIpInterfaceEntry(&IpInterface); + IpInterface.InterfaceLuid = AddressRow.InterfaceLuid; + IpInterface.Family = AF_INET; + LastError = GetIpInterfaceEntry(&IpInterface); + if (LastError != ERROR_SUCCESS) + { + LogError(L"Failed to get IP interface", LastError); + goto cleanupAdapter; + } + IpInterface.UseAutomaticMetric = FALSE; + IpInterface.Metric = 0; + IpInterface.NlMtu = 1420; + IpInterface.SitePrefixLength = 0; + LastError = SetIpInterfaceEntry(&IpInterface); + if (LastError != ERROR_SUCCESS) + { + LogError(L"Failed to set metric and MTU", LastError); + goto cleanupAdapter; + } + + Log(WIREGUARD_LOG_INFO, L"Setting configuration and adapter up"); + if (!WireGuardSetConfiguration(Adapter, &Config.Interface, sizeof(Config)) || + !WireGuardSetAdapterState(Adapter, WIREGUARD_ADAPTER_STATE_UP)) + { + LastError = LogError(L"Failed to set configuration and adapter up", GetLastError()); + goto cleanupAdapter; + } + + do + { + Bytes = sizeof(Config); + if (!WireGuardGetConfiguration(Adapter, &Config.Interface, &Bytes) || !Config.Interface.PeersCount) + { + LastError = LogError(L"Failed to get configuration", GetLastError()); + goto cleanupAdapter; + } + DWORD64 Timestamp = Now(); + SYSTEMTIME SystemTime; + FileTimeToSystemTime((FILETIME *)&Timestamp, &SystemTime); + fwprintf( + stderr, + L"%04u-%02u-%02u %02u:%02u:%02u.%04u [#] RX: %llu, TX: %llu\r", + SystemTime.wYear, + SystemTime.wMonth, + SystemTime.wDay, + SystemTime.wHour, + SystemTime.wMinute, + SystemTime.wSecond, + SystemTime.wMilliseconds, + Config.DemoServer.RxBytes, + Config.DemoServer.TxBytes); + } while (WaitForSingleObject(QuitEvent, 1000) == WAIT_TIMEOUT); + + cleanupAdapter: + WireGuardCloseAdapter(Adapter); + cleanupQuit: + SetConsoleCtrlHandler(CtrlHandler, FALSE); + CloseHandle(QuitEvent); + cleanupWireGuard: + FreeLibrary(WireGuard); + cleanupWinsock: + WSACleanup(); + return LastError; +} \ No newline at end of file diff --git a/src/main/cpp/tech_nevets_tvpn_wg_WireGuardJNI.h b/src/main/cpp/tech_nevets_tvpn_wg_WireGuardJNI.h deleted file mode 100644 index b3141ff..0000000 --- a/src/main/cpp/tech_nevets_tvpn_wg_WireGuardJNI.h +++ /dev/null @@ -1,117 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class tech_nevets_tvpn_wg_WireGuardJNI */ - -#ifndef _Included_tech_nevets_tvpn_wg_WireGuardJNI -#define _Included_tech_nevets_tvpn_wg_WireGuardJNI -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: tech_nevets_tvpn_wg_WireGuardJNI - * Method: installAdapter - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_installAdapter - (JNIEnv *, jobject); - -/* - * Class: tech_nevets_tvpn_wg_WireGuardJNI - * Method: removeAdapter - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_removeAdapter - (JNIEnv *, jobject); - -/* - * Class: tech_nevets_tvpn_wg_WireGuardJNI - * Method: startTunnel - * Signature: (Ljava/lang/String;)I - */ -JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_startTunnel - (JNIEnv *, jobject, jstring); - -/* - * Class: tech_nevets_tvpn_wg_WireGuardJNI - * Method: stopTunnel - * Signature: (Ljava/lang/String;)I - */ -JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_stopTunnel - (JNIEnv *, jobject, jstring); - -/* - * Class: tech_nevets_tvpn_wg_WireGuardJNI - * Method: createTunnel - * Signature: (Ljava/lang/String;)I - */ -JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_createTunnel - (JNIEnv *, jobject, jstring); - -/* - * Class: tech_nevets_tvpn_wg_WireGuardJNI - * Method: deleteTunnel - * Signature: (Ljava/lang/String;)I - */ -JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_deleteTunnel - (JNIEnv *, jobject, jstring); - -/* - * Class: tech_nevets_tvpn_wg_WireGuardJNI - * Method: getConfig - * Signature: (Ljava/lang/String;)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_getConfig - (JNIEnv *, jobject, jstring); - -/* - * Class: tech_nevets_tvpn_wg_WireGuardJNI - * Method: updateConfig - * Signature: (Ljava/lang/String;Ljava/lang/String;)I - */ -JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_updateConfig - (JNIEnv *, jobject, jstring, jstring); - -/* - * Class: tech_nevets_tvpn_wg_WireGuardJNI - * Method: addConfig - * Signature: (Ljava/lang/String;Ljava/lang/String;)I - */ -JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_addConfig - (JNIEnv *, jobject, jstring, jstring); - -/* - * Class: tech_nevets_tvpn_wg_WireGuardJNI - * Method: getActiveTunnel - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_getActiveTunnel - (JNIEnv *, jobject); - -/* - * Class: tech_nevets_tvpn_wg_WireGuardJNI - * Method: isActive - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_isActive - (JNIEnv *, jobject); - -/* - * Class: tech_nevets_tvpn_wg_WireGuardJNI - * Method: initializeWireGuard - * Signature: (Ljava/lang/String;)I - */ -JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_initializeWireGuard - (JNIEnv *, jobject, jstring); - -/* - * Class: tech_nevets_tvpn_wg_WireGuardJNI - * Method: cleanup - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_cleanup - (JNIEnv *, jobject); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/main/cpp/wireguard_wrapper.c b/src/main/cpp/wireguard_wrapper.c index 22e0bb4..8af66fb 100644 --- a/src/main/cpp/wireguard_wrapper.c +++ b/src/main/cpp/wireguard_wrapper.c @@ -1,28 +1,133 @@ #include +#include #include +#include +#include #include #include #include "wireguard.h" -#include #include "tech_nevets_tvpn_wg_WireGuardJNI.h" -const char *APP_DIR = "C:\\Program Files\\TVPN\\"; +#pragma comment(lib, "Rpcrt4.lib") -JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_createTunnel(JNIEnv *env, jobject obj, jstring tunnelName) { - const char *tunnelCharArr = (*env)->GetStringUTFChars(env, tunnelName, NULL); - if (tunnelCharArr == NULL) { - return 1; +const wchar_t *APP_DIR = L"C:\\Program Files\\TVPN\\"; + +static WIREGUARD_CREATE_ADAPTER_FUNC *WireGuardCreateAdapter; +static WIREGUARD_OPEN_ADAPTER_FUNC *WireGuardOpenAdapter; +static WIREGUARD_CLOSE_ADAPTER_FUNC *WireGuardCloseAdapter; +static WIREGUARD_GET_ADAPTER_LUID_FUNC *WireGuardGetAdapterLUID; +static WIREGUARD_GET_RUNNING_DRIVER_VERSION_FUNC *WireGuardGetRunningDriverVersion; +static WIREGUARD_DELETE_DRIVER_FUNC *WireGuardDeleteDriver; +static WIREGUARD_SET_LOGGER_FUNC *WireGuardSetLogger; +static WIREGUARD_SET_ADAPTER_LOGGING_FUNC *WireGuardSetAdapterLogging; +static WIREGUARD_GET_ADAPTER_STATE_FUNC *WireGuardGetAdapterState; +static WIREGUARD_SET_ADAPTER_STATE_FUNC *WireGuardSetAdapterState; +static WIREGUARD_GET_CONFIGURATION_FUNC *WireGuardGetConfiguration; +static WIREGUARD_SET_CONFIGURATION_FUNC *WireGuardSetConfiguration; + +static HMODULE InitializeWireGuardNT(void) { + HMODULE WireGuardDll = LoadLibraryExW( + L"wireguard.dll", + NULL, + LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32 + ); + if (!WireGuardDll) return NULL; +#define X(Name) ((*(FARPROC *)&Name = GetProcAddress(WireGuardDll, #Name)) == NULL) + if (X(WireGuardCreateAdapter) || X(WireGuardOpenAdapter) || X(WireGuardCloseAdapter) || + X(WireGuardGetAdapterLUID) || X(WireGuardGetRunningDriverVersion) || X(WireGuardDeleteDriver) || + X(WireGuardSetLogger) || X(WireGuardSetAdapterLogging) || X(WireGuardGetAdapterState) || + X(WireGuardSetAdapterState) || X(WireGuardGetConfiguration) || X(WireGuardSetConfiguration)) +#undef X + { + DWORD LastError = GetLastError(); + FreeLibrary(WireGuardDll); + SetLastError(LastError); + return NULL; + } + return WireGuardDll; +} + +GUID generateGUID() { + UUID uuid; + RPC_STATUS status = UuidCreate(&uuid); + + // Handle GUID generation error + if (status != RPC_S_OK) { + fprintf(stderr, "[C] Failed to create GUID\n"); + exit(1); // Exiting here to indicate a critical error; you may handle differently } - char *initialServiceName = "WireGuardTunnel$"; - char *tnlName = malloc(strlen(initialServiceName) + strlen(tunnelCharArr) + 1); - strcpy(tnlName, initialServiceName); - strcat(tnlName, tunnelCharArr); + // Copy the generated UUID into a GUID struct + GUID guid = { uuid.Data1, uuid.Data2, uuid.Data3 }; + for (int i = 0; i < 8; i++) { + guid.Data4[i] = uuid.Data4[i]; + } - char *initialDisplayName = "WireGuard Tunnel: "; - char *displayName = malloc(strlen(initialDisplayName) + strlen(tunnelCharArr) + 1); - strcpy(displayName, initialDisplayName); - strcat(displayName, tunnelCharArr); + return guid; +} + +JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_installAdapter(JNIEnv *env, jobject obj) { + HMODULE WireGuardDLL = InitializeWireGuardNT(); + + GUID guid = generateGUID(); + WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardCreateAdapter(L"TVPNet", L"WireGuard", &guid); + + struct { + WIREGUARD_INTERFACE Interface; + WIREGUARD_PEER DemoServer; + WIREGUARD_ALLOWED_IP AllV4; + } Config = { + .Interface = { + .Flags = WIREGUARD_INTERFACE_HAS_PRIVATE_KEY, + .PeersCount = 1 + }, + .DemoServer = { + .Flags = WIREGUARD_PEER_HAS_PUBLIC_KEY | WIREGUARD_PEER_HAS_ENDPOINT, + .AllowedIPsCount = 1 + }, + .AllV4 = { + .AddressFamily = AF_INET + } + }; + + WireGuardSetConfiguration(Adapter, &Config.Interface, sizeof(Config)); + WireGuardSetAdapterState(Adapter, WIREGUARD_ADAPTER_STATE_UP); + + FreeLibrary(WireGuardDLL); + return 0; +} + +JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_removeAdapter(JNIEnv *env, jobject obj) { + HMODULE WireGuardDLL = InitializeWireGuardNT(); + + WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardOpenAdapter(L"TVPNet"); + + printf("[C] Closing Adapter...\n"); + WireGuardCloseAdapter(Adapter); + + FreeLibrary(WireGuardDLL); + return 0; +} + +JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_installTunnel(JNIEnv *env, jobject obj, jstring tunnelName, jstring pathToConfig) { + printf("[C] Started installTunnel\n"); + + const jchar *rawStr = (*env)->GetStringChars(env, tunnelName, 0); //TODO mem cleanup + LPWSTR tnlName = (LPWSTR) rawStr; + + wprintf(L"[C] Tunnel Name: %ls", tnlName); + + wchar_t *servicePrefix = L"WireGuardTunnel$"; + wchar_t *serviceName = malloc(sizeof(wchar_t) * (wcslen(servicePrefix) + wcslen(tnlName) + 1)); //TODO mem cleanup + wcscpy(serviceName, servicePrefix); + wcscat(serviceName, tnlName); + + wchar_t *displayNamePrefix = L"WireGuard Tunnel: "; + wchar_t *displayName = malloc(sizeof(wchar_t) * (wcslen(displayNamePrefix) + wcslen(tnlName) + 1)); //TODO mem cleanup + wcscpy(displayName, displayNamePrefix); + wcscat(displayName, tnlName); + + printf("[C] Opening Service Manager\n"); SC_HANDLE hSCManager = OpenSCManager( NULL, @@ -30,21 +135,23 @@ JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_createTunnel(JNIEnv SC_MANAGER_ALL_ACCESS ); if (hSCManager == NULL) { - printf("OpenSCManager failed (%lu)\n", GetLastError()); - return 2; + printf("[C] OpenSCManager failed (%lu)\n", GetLastError()); + return 3; } - char *params = "TVPN.exe /installtunnelservice "; - char *conf = ".conf"; - char *exePath = malloc(strlen(APP_DIR) + strlen(params) + strlen(tunnelCharArr) + strlen(conf) + 1); - strcpy(exePath, APP_DIR); - strcat(exePath, params); - strcat(exePath, tunnelCharArr); - strcat(exePath, conf); + wchar_t *params = L"TVPN.exe /installtunnelservice "; + wchar_t *conf = L".conf"; + wchar_t *exePath = malloc(sizeof(wchar_t) * (wcslen(APP_DIR) + wcslen(params) + wcslen(tnlName) + wcslen(conf) + 1)); //TODO mem cleanup + wcscpy(exePath, APP_DIR); + wcscat(exePath, params); + wcscat(exePath, tnlName); + wcscat(exePath, conf); - SC_HANDLE hService = CreateServiceA( + wprintf(L"[C] Creating Service: %ls\n", exePath); + + SC_HANDLE hService = CreateServiceW( hSCManager, - tnlName, + serviceName, displayName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, @@ -53,42 +160,62 @@ JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_createTunnel(JNIEnv exePath, NULL, NULL, - "Nsi\0TcpIp\0\0", + L"Nsi\0TcpIp\0\0", NULL, NULL - ); + ); DWORD sid_info = SERVICE_SID_TYPE_UNRESTRICTED; - if (!ChangeServiceConfig2A(hService, SERVICE_CONFIG_SERVICE_SID_INFO, &sid_info)) { - printf("ChangeServiceConfig2W failed (%lu)\n", GetLastError()); - (*env)->ReleaseStringUTFChars(env, tunnelName, tunnelCharArr); - free(tnlName); + printf("[C] Changing Service Config\n"); + + if (!ChangeServiceConfig2W(hService, SERVICE_CONFIG_SERVICE_SID_INFO, &sid_info)) { + printf("[C] ChangeServiceConfig2W failed (%lu)\n", GetLastError()); + (*env)->ReleaseStringChars(env, tunnelName, rawStr); + free(serviceName); free(displayName); free(exePath); CloseServiceHandle(hSCManager); CloseServiceHandle(hService); - return 3; + return 4; } + printf("[C] Cleaning up creation\n"); - (*env)->ReleaseStringUTFChars(env, tunnelName, tunnelCharArr); - free(tnlName); - free(displayName); - free(exePath); CloseServiceHandle(hSCManager); CloseServiceHandle(hService); + free(serviceName); + free(displayName); + free(exePath); + (*env)->ReleaseStringChars(env, tunnelName, rawStr); - return 0; + printf("[C] Loading tunnel.dll\n"); + + HMODULE tunnel_lib = LoadLibrary("tunnel.dll"); + if (!tunnel_lib) abort(); + + BOOL (_cdecl *tunnel_proc) (_In_ LPCWSTR conf_file); + *(FARPROC*) &tunnel_proc = GetProcAddress(tunnel_lib, "WireGuardTunnelService"); + if (!tunnel_proc) abort(); + + const jchar *rawString = (*env)->GetStringChars(env, pathToConfig, NULL); + LPCWSTR configPath = (LPCWSTR) rawString; + + //TODO figure out how to handle this + //(*env)->ReleaseStringChars(env, tunnelName, rawStr); + + wprintf(L"[C] Calling function with arg: %ls\n", configPath); + + return tunnel_proc(configPath); } -JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_deleteTunnel(JNIEnv *env, jobject obj, jstring tunnelName) { - const char *nativeString = (*env)->GetStringUTFChars(env, tunnelName, NULL); +JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_removeTunnel(JNIEnv *env, jobject obj, jstring tunnelName) { + const jchar *nativeString = (*env)->GetStringChars(env, tunnelName, NULL); - char *initialServiceName = "WireGuardTunnel$"; - char *tnlName = malloc(strlen(initialServiceName) + strlen(nativeString) + 1); - strcpy(tnlName, initialServiceName); - strcat(tnlName, nativeString); + wchar_t *initialServiceName = L"WireGuardTunnel$"; + wchar_t *tnlName = malloc(sizeof(wchar_t) * (wcslen(initialServiceName) + wcslen(nativeString) + 1)); + wcscpy(tnlName, initialServiceName); + wcscat(tnlName, nativeString); SC_HANDLE hSCManager = OpenSCManager( NULL, @@ -100,7 +227,7 @@ JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_deleteTunnel(JNIEnv return 1; } - SC_HANDLE hService = OpenServiceA(hSCManager, tnlName, DELETE); + SC_HANDLE hService = OpenServiceW(hSCManager, tnlName, DELETE); if (hService != NULL) { if (!DeleteService(hService)) { // Handle error @@ -113,49 +240,8 @@ JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_deleteTunnel(JNIEnv } CloseServiceHandle(hSCManager); - (*env)->ReleaseStringUTFChars(env, tunnelName, nativeString); + (*env)->ReleaseStringChars(env, tunnelName, nativeString); free(tnlName); return 0; -} - -JNIEXPORT jint JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_initializeWireGuard(JNIEnv *env, jobject obj, jstring configFilePath) { - const char *nativeString = (*env)->GetStringUTFChars(env, configFilePath, 0); - - int confLength = strlen(nativeString); - - (*env)->ReleaseStringUTFChars(env, configFilePath, nativeString); - - return confLength; - -// const char *nativeConfigFilePath = (*env)->GetStringUTFChars(env, configFilePath, 0); - - // Load the WireGuard DLL dynamically -// hInstLibrary = LoadLibrary("tunnel.dll"); // Adjust with the actual DLL name - -// if (hInstLibrary) { - // Example: Load the WireGuard function from DLL -// wg_func_t wg_init_func = (wg_func_t) GetProcAddress(hInstLibrary, "WireGuardInitFunction"); // Adjust based on the WireGuard API - - -// if (wg_init_func) { -// wg_init_func(nativeConfigFilePath); -// (*env)->ReleaseStringUTFChars(env, configFilePath, nativeConfigFilePath); -// return 0; // Success -// } else { -// printf("Failed to get function from WireGuard DLL\n"); -// return -2; // Failed to get function -// } -// } else { -// printf("Failed to load WireGuard DLL\n"); -// return -1; // Failed to load DLL -// } -} - -JNIEXPORT void JNICALL Java_tech_nevets_tvpn_wg_WireGuardJNI_cleanup(JNIEnv *env, jobject obj) { - printf("Silly goose, this isn't implemented yet!\n"); - -// if (hInstLibrary) { -// FreeLibrary(hInstLibrary); -// } -} +} \ No newline at end of file diff --git a/src/main/java/tech/nevets/tvpn/Main.java b/src/main/java/tech/nevets/tvpn/Main.java index 185ec85..b01e2cb 100644 --- a/src/main/java/tech/nevets/tvpn/Main.java +++ b/src/main/java/tech/nevets/tvpn/Main.java @@ -1,6 +1,8 @@ package tech.nevets.tvpn; import com.formdev.flatlaf.FlatDarkLaf; +import tech.nevets.jconf.JsonConfig; +import tech.nevets.tvpn.cli.CommandBrigadier; import tech.nevets.tvpn.ui.UIManager; import tech.nevets.tvpn.wg.WireGuardJNI; @@ -9,11 +11,16 @@ import java.io.File; public class Main { private static final Logger LOGGER = new Logger(Main.class); - public static void main(String[] args) { LogOutputStream logOut = new LogOutputStream(); Logger.loadLogOutStream(logOut); + JsonConfig conf = new JsonConfig(""); + + if (args.length > 0) { + new CommandBrigadier().execute(args); + } + try { javax.swing.UIManager.setLookAndFeel(new FlatDarkLaf()); } catch (UnsupportedLookAndFeelException e) { @@ -26,9 +33,8 @@ public class Main { UIManager uim = new UIManager(logOut); uim.startLoginFrame(); - WireGuardJNI wgjni = new WireGuardJNI(); + //WireGuardJNI wgjni = new WireGuardJNI(); //System.out.println(wgjni.createTunnel("test")); - System.out.println(wgjni.deleteTunnel("test")); + //System.out.println(wgjni.removeTunnel("test")); } - } diff --git a/src/main/java/tech/nevets/tvpn/Utils.java b/src/main/java/tech/nevets/tvpn/Utils.java index 5e10dd8..b388a8f 100644 --- a/src/main/java/tech/nevets/tvpn/Utils.java +++ b/src/main/java/tech/nevets/tvpn/Utils.java @@ -1,5 +1,7 @@ package tech.nevets.tvpn; +import tech.nevets.jconf.JsonConfig; + import java.io.File; import java.io.IOException; import java.net.URI; @@ -10,14 +12,24 @@ import java.util.Map; public class Utils { private static final Logger LOGGER = new Logger(Utils.class); - public static final File APP_DIR = new File("./tvpn/"); + public static final String WG_CONF_DIR = "C:\\Program Files\\WireGuard\\Data\\Configurations\\"; - public static final String API_ENDPOINT_BASE = "http://tvpn.lan/testing/"; + public static final File APP_DIR = new File("C:\\Program Files\\TVPN\\"); + public static final JsonConfig CONFIG = new JsonConfig(APP_DIR + "/config.json", getConfStr()); + public static final String API_ENDPOINT_BASE = CONFIG.getString("apiEndpoint"); static { if (!APP_DIR.exists()) APP_DIR.mkdirs(); } + private static String getConfStr() { + return """ + { + "apiEndpoint":"http://tvpn.lan" + } + """; + } + public static String get(URI url, Map headers) { HttpClient client = HttpClient.newHttpClient(); HttpRequest.Builder reqBuilder = HttpRequest.newBuilder() diff --git a/src/main/java/tech/nevets/tvpn/cli/Command.java b/src/main/java/tech/nevets/tvpn/cli/Command.java new file mode 100644 index 0000000..64f0e28 --- /dev/null +++ b/src/main/java/tech/nevets/tvpn/cli/Command.java @@ -0,0 +1,12 @@ +package tech.nevets.tvpn.cli; + +public interface Command { + + void execute(String[] args); + + String getName(); + + String getUsage(); + + String getDescription(); +} diff --git a/src/main/java/tech/nevets/tvpn/cli/CommandBrigadier.java b/src/main/java/tech/nevets/tvpn/cli/CommandBrigadier.java new file mode 100644 index 0000000..b09f4f5 --- /dev/null +++ b/src/main/java/tech/nevets/tvpn/cli/CommandBrigadier.java @@ -0,0 +1,49 @@ +package tech.nevets.tvpn.cli; + +import tech.nevets.tvpn.Logger; +import tech.nevets.tvpn.cli.commands.HelpCmd; +import tech.nevets.tvpn.cli.commands.InstallTunnelServiceCmd; +import tech.nevets.tvpn.cli.commands.UninstallTunnelServiceCmd; + +import java.util.ArrayList; +import java.util.List; + +public class CommandBrigadier { + private static final Logger LOGGER = new Logger(CommandBrigadier.class); + + private final List registeredCommands; + private final HelpCmd helpCmd; + + public CommandBrigadier() { + this.registeredCommands = new ArrayList<>(); + helpCmd = new HelpCmd(this); + + registerCommand(helpCmd); + registerCommand(new InstallTunnelServiceCmd()); + registerCommand(new UninstallTunnelServiceCmd()); + } + + public void registerCommand(Command cmd) { + for (Command c : registeredCommands) { + if (c.getName().equals(cmd.getName())) { + LOGGER.warn("Command with name " + cmd.getName() + " already registered, skipping."); + return; + } + } + registeredCommands.add(cmd); + } + + public void execute(String[] args) { + for (Command c : registeredCommands) { + if (c.getName().equals(args[0])) { + c.execute(args); + return; + } + } + helpCmd.execute(new String[0]); + } + + public List getCommands() { + return registeredCommands; + } +} diff --git a/src/main/java/tech/nevets/tvpn/cli/commands/HelpCmd.java b/src/main/java/tech/nevets/tvpn/cli/commands/HelpCmd.java new file mode 100644 index 0000000..366a69e --- /dev/null +++ b/src/main/java/tech/nevets/tvpn/cli/commands/HelpCmd.java @@ -0,0 +1,52 @@ +package tech.nevets.tvpn.cli.commands; + +import tech.nevets.tvpn.cli.Command; +import tech.nevets.tvpn.cli.CommandBrigadier; + +public class HelpCmd implements Command { + private final CommandBrigadier cb; + + public HelpCmd(CommandBrigadier cb) { + this.cb = cb; + } + + @Override + public void execute(String[] args) { + if (args.length > 0) { + for (Command c : cb.getCommands()) { + if (!(args.length == 1) && args[1].equals(c.getName())) { + System.out.println("TVPN - Usage Menu [" + c.getName() + "]"); + System.out.println(c.getName() + " " + c.getUsage() + ": " + c.getDescription()); + System.exit(0); + } + } + } + + StringBuilder sb = new StringBuilder(); + sb.append("TVPN - Help Menu\n"); + for (Command c : cb.getCommands()) { + sb.append(" - ").append(c.getName()) + .append(" ").append(c.getUsage()) + .append(": ").append(c.getDescription()) + .append("\n"); + } + + System.out.println(sb); + System.exit(0); + } + + @Override + public String getName() { + return "help"; + } + + @Override + public String getUsage() { + return ""; + } + + @Override + public String getDescription() { + return "Displays the help menu"; + } +} diff --git a/src/main/java/tech/nevets/tvpn/cli/commands/InstallTunnelServiceCmd.java b/src/main/java/tech/nevets/tvpn/cli/commands/InstallTunnelServiceCmd.java new file mode 100644 index 0000000..6602669 --- /dev/null +++ b/src/main/java/tech/nevets/tvpn/cli/commands/InstallTunnelServiceCmd.java @@ -0,0 +1,71 @@ +package tech.nevets.tvpn.cli.commands; + +import tech.nevets.tvpn.Utils; +import tech.nevets.tvpn.cli.Command; +import tech.nevets.tvpn.wg.WireGuardJNI; + +import java.util.regex.Pattern; + +public class InstallTunnelServiceCmd implements Command { + @Override + public void execute(String[] args) { + + + System.out.println("Executing InstallTunnelServiceCmd"); + String path = Utils.APP_DIR + "/configurations/" + args[1] + ".conf"; + System.out.println(new WireGuardJNI().installTunnel(args[1], path)); + System.exit(0); + } + + @Override + public String getName() { + return "/installtunnelservice"; + } + + @Override + public String getUsage() { + return "(/path/to/tunnel/config)"; + } + + @Override + public String getDescription() { + return "Creates and starts a WireGuard Tunnel Service"; + } + + String[] reservedNames = new String[]{ + "CON", "PRN", "AUX", "NUL", + "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", + "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", + }; + + + private boolean isReserved(String name) { + if (name.isEmpty()) { + return false; + } + for (String s : reservedNames) { + if (name.contains(s)) { + return true; + } + } + return false; + } + + private static final String[] SPECIAL_CHARS = new String[]{"$", "\\", "/", ":", "*", "?", "\"", "'", "<", ">", "|", "\t", "\00"}; + private boolean hasSpecialChars(String name) { + for (String s : SPECIAL_CHARS) { + if (name.contains(s)) { + return true; + } + } + return false; + } + + private static final Pattern PATTERN = Pattern.compile("^[a-zA-Z0-9_=+.-]{1,32}$"); + private boolean tunnelNameIsValid(String name) { + if (isReserved(name) || hasSpecialChars(name)) { + return false; + } + return PATTERN.matcher(name).hasMatch(); + } +} diff --git a/src/main/java/tech/nevets/tvpn/cli/commands/UninstallTunnelServiceCmd.java b/src/main/java/tech/nevets/tvpn/cli/commands/UninstallTunnelServiceCmd.java new file mode 100644 index 0000000..9f812e0 --- /dev/null +++ b/src/main/java/tech/nevets/tvpn/cli/commands/UninstallTunnelServiceCmd.java @@ -0,0 +1,28 @@ +package tech.nevets.tvpn.cli.commands; + +import tech.nevets.tvpn.cli.Command; +import tech.nevets.tvpn.wg.WireGuardJNI; + +public class UninstallTunnelServiceCmd implements Command { + @Override + public void execute(String[] args) { + System.out.println("Executing UnInstallTunnelServiceCmd"); + System.out.println(new WireGuardJNI().removeTunnel(args[1])); + System.exit(0); + } + + @Override + public String getName() { + return "/uninstalltunnelservice"; + } + + @Override + public String getUsage() { + return "(tunnel name)"; + } + + @Override + public String getDescription() { + return "Stops and removes WireGuard Tunnel Service"; + } +} diff --git a/src/main/java/tech/nevets/tvpn/ui/UIManager.java b/src/main/java/tech/nevets/tvpn/ui/UIManager.java index fa01607..c38efd3 100644 --- a/src/main/java/tech/nevets/tvpn/ui/UIManager.java +++ b/src/main/java/tech/nevets/tvpn/ui/UIManager.java @@ -13,7 +13,7 @@ public class UIManager { public UIManager(LogOutputStream logOut) { frame = new JFrame(); - tabPane = new JTabbedPane(); + tabPane = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT); homePanel = new HomePanel(); tabPane.addTab("Home", homePanel); diff --git a/src/main/java/tech/nevets/tvpn/wg/WireGuardJNI.java b/src/main/java/tech/nevets/tvpn/wg/WireGuardJNI.java index 328649b..5251a65 100644 --- a/src/main/java/tech/nevets/tvpn/wg/WireGuardJNI.java +++ b/src/main/java/tech/nevets/tvpn/wg/WireGuardJNI.java @@ -10,10 +10,14 @@ public class WireGuardJNI { public native int installAdapter(); public native int removeAdapter(); - public native int startTunnel(String tunnelName); - public native int stopTunnel(String tunnelName); - public native int createTunnel(String tunnelName); - public native int deleteTunnel(String tunnelName); + /** + * Creates and starts the WireGuard service + * @param tunnelName Name of the tunnel + * @param pathToConfig Full path to the tunnel's configuration file + * @return 0 if successful, 1 if failed + */ + public native int installTunnel(String tunnelName, String pathToConfig); + public native int removeTunnel(String tunnelName); public native String getConfig(String tunnelName); public native int updateConfig(String tunnelName, String newConfig); diff --git a/src/main/resources/icon.ico b/src/main/resources/icon.ico new file mode 100644 index 0000000..9df27e0 Binary files /dev/null and b/src/main/resources/icon.ico differ diff --git a/src/main/resources/icon.png b/src/main/resources/icon.png new file mode 100644 index 0000000..827f8ef Binary files /dev/null and b/src/main/resources/icon.png differ