Started moving from WireGuard for Windows to embeddable-dll-service

This commit is contained in:
2024-10-17 23:49:10 -04:00
parent 27be80e430
commit 8c484f9c23
32 changed files with 939 additions and 1016 deletions

View File

@@ -0,0 +1,48 @@
package tech.nevets.tvpn;
import javax.swing.*;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class LogOutputStream extends OutputStream {
private JTextArea textArea;
private FileOutputStream fileOut;
private final char[] buf = new char[4096];
private int pointer = 0;
public LogOutputStream() {
try {
this.fileOut = new FileOutputStream(Utils.APP_DIR + "/log.txt");
} catch (IOException e) {
System.err.println("Error Creating log stream: " + e.getMessage());
this.fileOut = null;
}
}
public void initLogTab(JTextArea textArea) {
this.textArea = textArea;
}
@Override
public void write(int b) throws IOException {
fileOut.write(b);
if ((char) b == '\n') {
flush();
pointer = 0;
return;
}
buf[pointer] = (char) b;
pointer++;
}
@Override
public void flush() {
StringBuilder sb = new StringBuilder(pointer + 1);
for (int i = 0; i < pointer; i++) {
sb.append(buf[i]);
}
textArea.append(sb.toString());
}
}

View File

@@ -0,0 +1,43 @@
package tech.nevets.tvpn;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class Logger {
private static LogOutputStream logOut;
public static void loadLogOutStream(LogOutputStream lo) {
logOut = lo;
}
private String className;
public Logger(Class<?> clazz) {
className = clazz.getSimpleName();
}
public void log(String message) {
write(message, "INFO");
}
public void warn(String message) {
write(message, "WARN");
}
public void error(String message) {
write(message, "ERROR");
}
private void write(String message, String type) {
try {
logOut.write(("[" + className + "] (" + type + ") " + message + '\n').getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
System.err.println("Error logging message: " + e.getMessage());
}
// DEBUG
System.out.print("[" + className + "] (" + type + ") " + message + '\n');
}
}

View File

@@ -2,27 +2,33 @@ package tech.nevets.tvpn;
import com.formdev.flatlaf.FlatDarkLaf;
import tech.nevets.tvpn.ui.UIManager;
import tech.nevets.tvpn.wg.WireGuardJNI;
import javax.swing.*;
import java.io.File;
import java.io.IOException;
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);
try {
javax.swing.UIManager.setLookAndFeel(new FlatDarkLaf());
} catch (UnsupportedLookAndFeelException e) {
e.printStackTrace();
LOGGER.error(e.getMessage());
}
File f = new File(Utils.APP_DIR + "/temp/");
f.delete();
f.mkdirs();
File f = new File(Utils.APP_DIR + "/logs/");
if (!f.exists()) f.mkdirs();
UIManager uim = new UIManager();
uim.startUIFrame();
UIManager uim = new UIManager(logOut);
uim.startLoginFrame();
WireGuardJNI wgjni = new WireGuardJNI();
//System.out.println(wgjni.createTunnel("test"));
System.out.println(wgjni.deleteTunnel("test"));
}
}

View File

@@ -9,9 +9,10 @@ import java.net.http.HttpResponse;
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/";
public static final String API_ENDPOINT_BASE = "http://tvpn.lan/testing/";
static {
if (!APP_DIR.exists()) APP_DIR.mkdirs();
@@ -22,18 +23,7 @@ public class Utils {
HttpRequest.Builder reqBuilder = HttpRequest.newBuilder()
.GET()
.uri(url);
for (Map.Entry<String, String> header : headers.entrySet()) {
reqBuilder.header(header.getKey(), header.getValue());
}
HttpRequest req = reqBuilder.build();
HttpResponse<String> res;
try {
res = client.send(req, HttpResponse.BodyHandlers.ofString());
return res.body();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
return "";
}
return req(headers, client, reqBuilder);
}
public static String post(URI url, Map<String, String> headers, HttpRequest.BodyPublisher bodyPub) {
@@ -41,6 +31,10 @@ public class Utils {
HttpRequest.Builder reqBuilder = HttpRequest.newBuilder()
.POST(bodyPub)
.uri(url);
return req(headers, client, reqBuilder);
}
private static String req(Map<String, String> headers, HttpClient client, HttpRequest.Builder reqBuilder) {
for (Map.Entry<String, String> header : headers.entrySet()) {
reqBuilder.header(header.getKey(), header.getValue());
}
@@ -50,8 +44,12 @@ public class Utils {
res = client.send(req, HttpResponse.BodyHandlers.ofString());
return res.body();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
LOGGER.error(e.getMessage());
return "";
}
}
public static void throwException() throws RuntimeException {
throw new RuntimeException("Yay an exception!");
}
}

View File

@@ -2,6 +2,7 @@ package tech.nevets.tvpn.auth;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import tech.nevets.tvpn.Logger;
import tech.nevets.tvpn.Utils;
import java.io.IOException;
@@ -11,28 +12,10 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
public class AuthManager {
public static UserToken serverAuth() {
CompletableFuture<String> cf = new CompletableFuture<>();
AuthServer as = new AuthServer(cf);
// TODO create new thread to handle login so the app doesn't hang while logging in
String response = cf.join();
String[] resSplit = response.split(":");
if (resSplit.length != 2) return new UserToken(false, "", 0, "Error");
UserToken userToken = new UserToken(true, resSplit[0], Long.parseLong(resSplit[1]), "");
try {
if (userToken.getToken().isEmpty()) return userToken;
Files.writeString(Path.of(Utils.APP_DIR + "/token.txt"), userToken.getToken());
} catch (IOException e) {
e.printStackTrace();
}
return userToken;
}
private static final Logger LOGGER = new Logger(AuthManager.class);
public static UserToken httpAuth(String username, String password) {
Map<String, String> headers = new HashMap<>();
@@ -49,7 +32,7 @@ public class AuthManager {
if (token.getToken().isEmpty()) return token;
Files.writeString(Path.of(Utils.APP_DIR + "/token.txt"), token.getToken());
} catch (IOException e) {
e.printStackTrace();
LOGGER.error(e.getMessage());
}
return token;

View File

@@ -1,76 +0,0 @@
package tech.nevets.tvpn.auth;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class AuthServer {
private HttpServer server;
public AuthServer(CompletableFuture<String> cf) {
try {
server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 8342), 0);
server.createContext("/", new AuthHandler(cf));
server.setExecutor(null);
server.start();
System.out.println("Server is running on port 8342");
} catch (IOException e) {
e.printStackTrace();
}
}
public void stop() {
server.stop(0);
}
// define a custom HttpHandler
static class AuthHandler implements HttpHandler {
private CompletableFuture<String> cf;
public AuthHandler(CompletableFuture<String> cf) {
this.cf = cf;
}
@Override
public void handle(HttpExchange exchange) throws IOException {
// handle the request
Headers headers = exchange.getRequestHeaders();
List<String> strHeaders = headers.get("Token");
String response;
int status;
String token = "";
if (exchange.getRequestMethod().equals("POST")) {
if (strHeaders == null || strHeaders.isEmpty()) {
response = "Error, not token found.";
status = 412;
} else {
response = "Successfully authenticated, you can now return to the app!";
status = 200;
token = strHeaders.getFirst();
}
} else {
response = "Invalid Request Method";
status = 405;
}
exchange.sendResponseHeaders(status, response.length());
OutputStream os = exchange.getResponseBody();
os.write(response.getBytes());
os.flush();
os.close();
if (status == 200) {
cf.complete(token);
}
}
}
}

View File

@@ -1,6 +1,7 @@
package tech.nevets.tvpn.auth;
import com.google.gson.Gson;
import tech.nevets.tvpn.Logger;
import tech.nevets.tvpn.Utils;
import java.io.BufferedReader;
@@ -10,6 +11,8 @@ import java.nio.file.Files;
import java.nio.file.Path;
public class Session {
private static final Logger LOGGER = new Logger(Session.class);
private final UserToken token;
public static Session loadSession() {
@@ -20,7 +23,7 @@ public class Session {
BufferedReader br = Files.newBufferedReader(Path.of(Utils.APP_DIR + "/session.json"));
return g.fromJson(br, Session.class);
} catch (IOException e) {
e.printStackTrace();
LOGGER.error(e.getMessage());
}
}
return null;
@@ -43,7 +46,7 @@ public class Session {
try {
Files.writeString(Path.of(Utils.APP_DIR + "/session.json"), new Gson().toJson(token));
} catch (IOException e) {
e.printStackTrace();
LOGGER.error(e.getMessage());
}
}
}

View File

@@ -6,10 +6,10 @@ package tech.nevets.tvpn.ui;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import net.miginfocom.swing.*;
import tech.nevets.tvpn.Logger;
import tech.nevets.tvpn.Utils;
import tech.nevets.tvpn.wg.WireGuardHandler;
@@ -22,13 +22,12 @@ import java.util.List;
/**
* @author steven
*/
public class UI extends JPanel {
private final UIManager uim;
public class HomePanel extends JPanel {
private static final Logger LOGGER = new Logger(HomePanel.class);
public UI(UIManager uim) {
public HomePanel() {
initComponents();
createTunnelsUI(WireGuardHandler.getTunnelConfigs());
this.uim = uim;
updateActiveTunnels();
}
@@ -101,7 +100,7 @@ public class UI extends JPanel {
String tunnelConfig = Files.readString(Path.of(Utils.APP_DIR + "/" + getSelectedTunnel() + ".conf"));
configArea.setText(tunnelConfig);
} catch (IOException ex) {
ex.printStackTrace();
LOGGER.error(ex.getMessage());
}
return;
}
@@ -111,7 +110,7 @@ public class UI extends JPanel {
String tunnelConfig = Files.readString(Path.of(Utils.APP_DIR + "/" + getSelectedTunnel() + ".conf"));
configArea.setText(tunnelConfig);
} catch (IOException ex) {
ex.printStackTrace();
LOGGER.error(ex.getMessage());
}
loadTunnel(originalTunnel);
}
@@ -133,11 +132,19 @@ public class UI extends JPanel {
try {
WireGuardHandler.loadTunnel(Path.of(f.getAbsolutePath()));
} catch (IOException ex) {
ex.printStackTrace();
LOGGER.error(ex.getMessage());
}
listModel.addElement("" + f.getName().replace(".conf", ""));
}
private void exceptionBtn(ActionEvent e) {
try {
Utils.throwException();
} catch (Exception ex) {
LOGGER.error(ex.getMessage());
}
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents @formatter:off
tunnelsLabel = new JLabel();
@@ -151,6 +158,7 @@ public class UI extends JPanel {
showConfigBtn = new JButton();
importBtn = new JButton();
startTunnelBtn = new JButton();
exceptionBtn = new JButton();
stopTunnelBtn = new JButton();
//======== this ========
@@ -214,6 +222,11 @@ public class UI extends JPanel {
startTunnelBtn.addActionListener(e -> loadBtn(e));
add(startTunnelBtn, "cell 0 10 2 1");
//---- exceptionBtn ----
exceptionBtn.setText("Exceptional");
exceptionBtn.addActionListener(e -> exceptionBtn(e));
add(exceptionBtn, "cell 3 10");
//---- stopTunnelBtn ----
stopTunnelBtn.setText("Stop Tunnel");
stopTunnelBtn.addActionListener(e -> stopTunnelBtn(e));
@@ -232,6 +245,7 @@ public class UI extends JPanel {
private JButton showConfigBtn;
private JButton importBtn;
private JButton startTunnelBtn;
private JButton exceptionBtn;
private JButton stopTunnelBtn;
// JFormDesigner - End of variables declaration //GEN-END:variables @formatter:on

View File

@@ -69,6 +69,13 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 10 2 1"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "exceptionBtn"
"text": "Exceptional"
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "exceptionBtn", true ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 10"
} )
add( new FormComponent( "javax.swing.JButton" ) {
name: "stopTunnelBtn"
"text": "Stop Tunnel"

View File

@@ -8,6 +8,7 @@ import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import net.miginfocom.swing.*;
import tech.nevets.tvpn.Logger;
import tech.nevets.tvpn.auth.AuthManager;
import tech.nevets.tvpn.auth.UserToken;
@@ -15,6 +16,7 @@ import tech.nevets.tvpn.auth.UserToken;
* @author steven
*/
public class LoginPanel extends JPanel {
private static final Logger LOGGER = new Logger(LoginPanel.class);
private final UIManager uim;
public LoginPanel(UIManager uim) {
@@ -40,7 +42,7 @@ public class LoginPanel extends JPanel {
statusLabel.setText("Unable to authenticate with stored token, please login");
}
} catch (Exception ex) {
ex.printStackTrace();
LOGGER.error(ex.getMessage());
statusLabel.setText("An error occurred during login.");
}
}
@@ -79,7 +81,7 @@ public class LoginPanel extends JPanel {
uim.getUIPanel().updateUserInfo(token.getUsername(), token.getEmail());
uim.disposeLoginFrame();
} catch (Exception ex) {
ex.printStackTrace();
LOGGER.error(ex.getMessage());
statusLabel.setText("An error occurred during login.");
}
}

View File

@@ -0,0 +1,51 @@
/*
* Created by JFormDesigner on Thu Oct 17 14:11:06 EDT 2024
*/
package tech.nevets.tvpn.ui;
import javax.swing.*;
import net.miginfocom.swing.*;
import tech.nevets.tvpn.LogOutputStream;
import tech.nevets.tvpn.Logger;
import tech.nevets.tvpn.Utils;
/**
* @author steven
*/
public class LogsPanel extends JPanel {
public LogsPanel(LogOutputStream logOut) {
initComponents();
logOut.initLogTab(logsArea);
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents @formatter:off
logsScroll = new JScrollPane();
logsArea = new JTextArea();
//======== this ========
setLayout(new MigLayout(
"hidemode 3",
// columns
"[grow,fill]",
// rows
"[grow]"));
//======== logsScroll ========
{
//---- logsArea ----
logsArea.setEditable(false);
logsScroll.setViewportView(logsArea);
}
add(logsScroll, "cell 0 0,grow");
// JFormDesigner - End of component initialization //GEN-END:initComponents @formatter:on
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables @formatter:off
private JScrollPane logsScroll;
private JTextArea logsArea;
// JFormDesigner - End of variables declaration //GEN-END:variables @formatter:on
}

View File

@@ -0,0 +1,26 @@
JFDML JFormDesigner: "8.2.4.0.393" Java: "17.0.9" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
root: new FormRoot {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[grow,fill]"
"$rowConstraints": "[grow]"
} ) {
name: "this"
add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) {
name: "logsScroll"
add( new FormComponent( "javax.swing.JTextArea" ) {
name: "logsArea"
"editable": false
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0,grow"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 400, 300 )
} )
}
}

View File

@@ -1,21 +1,38 @@
package tech.nevets.tvpn.ui;
import tech.nevets.tvpn.LogOutputStream;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
public class UIManager {
private List<JPanel> panels = new ArrayList<>();
private JFrame loginFrame;
private JFrame uiFrame;
private UI uiPanel;
private final JFrame frame;
private final JTabbedPane tabPane;
private JFrame loginFrame;
private final HomePanel homePanel;
public UIManager(LogOutputStream logOut) {
frame = new JFrame();
tabPane = new JTabbedPane();
homePanel = new HomePanel();
tabPane.addTab("Home", homePanel);
tabPane.addTab("Logs", new LogsPanel(logOut));
frame.add(tabPane);
frame.pack();
frame.setSize(720, 480);
frame.setTitle("TVPN");
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SwingUtilities.updateComponentTreeUI(frame);
frame.setVisible(true);
}
public void startLoginFrame() {
loginFrame = new JFrame();
JPanel p = new LoginPanel(this);
loginFrame.add(p);
panels.add(p);
loginFrame.pack();
loginFrame.setTitle("Login");
loginFrame.setSize(340, 170);
@@ -29,31 +46,8 @@ public class UIManager {
loginFrame.dispose();
}
public void startUIFrame() {
uiFrame = new JFrame();
uiPanel = new UI(this);
uiFrame.add(uiPanel);
panels.add(uiPanel);
uiFrame.pack();
uiFrame.setTitle("TVPN");
uiFrame.setSize(720, 480);
uiFrame.setLocationRelativeTo(null);
uiFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
uiFrame.setVisible(true);
}
protected UI getUIPanel() {
return uiPanel;
}
public void startFileChooserFrame(JFileChooser fileChooser) {
JFrame f = new JFrame();
f.add(fileChooser);
f.pack();
f.setTitle("Choose WireGuard Config File");
f.setLocationRelativeTo(null);
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setVisible(true);
protected HomePanel getUIPanel() {
return homePanel;
}
}

View File

@@ -1,79 +0,0 @@
package tech.nevets.tvpn.wg;
import tech.nevets.tvpn.Utils;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
public class WGTesting {
public static void main(String[] args) throws IOException, InterruptedException {
List<String> configs = listConfigs();
//String currentTunnel = getCurrentTunnel();
//System.out.println(currentTunnel);
//pullTunnelConfig(currentTunnel);
//WireGuardHandler.stopTunnel(currentTunnel);
Random r = ThreadLocalRandom.current();
int index = r.nextInt(configs.size());
System.out.println("Index: " + index);
System.out.println("Random Tunnel: " + configs.get(index).substring(0, configs.get(index).length() - 11));
//WireGuardHandler.startTunnel(configs.get(index).substring(0, configs.get(index).length() - 11));
}
public static String getCurrentTunnel() throws IOException {
String[] rSplit = getWGShowLines();
for (String s : rSplit) {
if (s.toLowerCase().contains("interface")) {
return s.split(":")[1].trim();
}
}
return "";
}
public static List<String> getCurrentTunnels() throws IOException {
String[] rSplit = getWGShowLines();
List<String> tunnels = new ArrayList<>();
for (String s : rSplit) {
if (s.toLowerCase().contains("interface")) {
tunnels.add(s.split(":")[1].trim());
}
}
return tunnels;
}
public static String[] getWGShowLines() throws IOException {
ProcessBuilder pb = new ProcessBuilder();
pb.command("cmd", "/C", "wg.exe", "show");
Process p = pb.start();
String result = new String(p.getInputStream().readAllBytes());
return result.split("\n");
}
public static void pullTunnelConfig(String tunnelName) throws IOException {
ProcessBuilder pb = new ProcessBuilder();
pb.redirectOutput(new File(Utils.APP_DIR + "/" + tunnelName + ".conf"));
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
pb.command("cmd", "/C", "wg.exe", "showconf", tunnelName);
pb.start();
}
public static List<String> listConfigs() throws IOException {
List<Path> filePaths = new ArrayList<>();
Path path = Path.of(Utils.WG_CONF_DIR);
Files.walk(path)
.filter(Files::isRegularFile)
.forEach(filePaths::add);
List<String> fileNames = new ArrayList<>();
for(Path p : filePaths) {
String fullPath = p.toString();
fileNames.add(fullPath.substring(fullPath.lastIndexOf("\\") + 1));
}
return fileNames;
}
}

View File

@@ -1,5 +1,6 @@
package tech.nevets.tvpn.wg;
import tech.nevets.tvpn.Logger;
import tech.nevets.tvpn.Utils;
import java.io.File;
@@ -13,6 +14,7 @@ import java.util.stream.Stream;
import static tech.nevets.tvpn.Utils.*;
public class WireGuardHandler {
private static final Logger LOGGER = new Logger(WireGuardHandler.class);
public static void loadTunnel(Path tunnelConfig) throws IOException {
Files.copy(tunnelConfig, Path.of(WG_CONF_DIR + tunnelConfig.getFileName().toString()));
@@ -43,7 +45,7 @@ public class WireGuardHandler {
}
System.out.println("Tunnel Started");
} catch (IOException | InterruptedException e) {
e.printStackTrace();
LOGGER.error(e.getMessage());
}
}
@@ -67,7 +69,7 @@ public class WireGuardHandler {
}
System.out.println("Tunnel Stopped");
} catch (IOException | InterruptedException e) {
e.printStackTrace();
LOGGER.error(e.getMessage());
}
}
@@ -84,7 +86,7 @@ public class WireGuardHandler {
}
return fileNames;
} catch (IOException e) {
e.printStackTrace();
LOGGER.error(e.getMessage());
return new ArrayList<>();
}
}
@@ -107,7 +109,7 @@ public class WireGuardHandler {
Process p = pb.start();
return new String(p.getInputStream().readAllBytes());
} catch (IOException e) {
e.printStackTrace();
LOGGER.error(e.getMessage());
return null;
}
}
@@ -132,7 +134,7 @@ public class WireGuardHandler {
Process p = pb.start();
p.waitFor();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
LOGGER.error(e.getMessage());
}
}

View File

@@ -0,0 +1,29 @@
package tech.nevets.tvpn.wg;
public class WireGuardJNI {
// Load the native library
static {
System.loadLibrary("libwireguard_wrapper"); // This will load wireguard_wrapper.dll (or .so on Linux)
}
// Native method declaration
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);
public native String getConfig(String tunnelName);
public native int updateConfig(String tunnelName, String newConfig);
public native int addConfig(String tunnelName, String config);
public native String getActiveTunnel();
public native boolean isActive();
public native int initializeWireGuard(String configFilePath);
public native void cleanup();
// Other methods for WireGuard interaction can be added here if needed
}