From 07c107173bea3a07f906805e6fae379b8a1addc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20K=C3=BCrten?= <sebastian@mobanisto.de> Date: Fri, 14 Apr 2023 13:30:32 +0200 Subject: [PATCH] Add support for macOS --- .github/workflows/checks.yaml | 12 ++++++ .../onionwrapper/AbstractTorWrapper.java | 16 +++++++ .../onionwrapper/util/OsUtils.java | 32 ++++++++++++++ .../onionwrapper/util/StringUtils.java | 12 ++++++ .../briarproject/onionwrapper/TestUtils.java | 31 +++++--------- onionwrapper-java/build.gradle | 9 ++-- .../onionwrapper/UnixTorWrapper.java | 2 +- .../onionwrapper/BootstrapTest.java | 9 ++-- .../briarproject/onionwrapper/BridgeTest.java | 2 +- .../onionwrapper/ResourcesLinuxTest.java | 2 +- .../onionwrapper/ResourcesMacTest.java | 42 +++++++++++++++++++ .../onionwrapper/ResourcesWindowsTest.java | 2 +- settings.gradle | 1 + 13 files changed, 141 insertions(+), 31 deletions(-) create mode 100644 onionwrapper-core/src/main/java/org/briarproject/onionwrapper/util/OsUtils.java create mode 100644 onionwrapper-core/src/main/java/org/briarproject/onionwrapper/util/StringUtils.java create mode 100644 onionwrapper-java/src/test/java/org/briarproject/onionwrapper/ResourcesMacTest.java diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index d539874..ba100da 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -31,3 +31,15 @@ jobs: java-version: '17' - name: Run Gradle tests run: ./gradlew check --info --stacktrace + build-macos: + runs-on: macos-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Setup Java + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '17' + - name: Run Gradle tests + run: ./gradlew check --info --stacktrace diff --git a/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/AbstractTorWrapper.java b/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/AbstractTorWrapper.java index b2dbb22..9973338 100644 --- a/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/AbstractTorWrapper.java +++ b/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/AbstractTorWrapper.java @@ -50,6 +50,7 @@ import static org.briarproject.onionwrapper.TorWrapper.TorState.STARTED; import static org.briarproject.onionwrapper.TorWrapper.TorState.STARTING; import static org.briarproject.onionwrapper.TorWrapper.TorState.STOPPED; import static org.briarproject.onionwrapper.TorWrapper.TorState.STOPPING; +import static org.briarproject.onionwrapper.util.OsUtils.isMac; @InterfaceNotNullByDefault abstract class AbstractTorWrapper implements EventHandler, TorWrapper { @@ -110,6 +111,10 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper { return new File(torDirectory, "tor"); } + protected File getLibEventFile() { + return new File(torDirectory, "libevent-2.1.7.dylib"); + } + @Override public File getObfs4ExecutableFile() { return new File(torDirectory, "obfs4proxy"); @@ -202,6 +207,9 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper { //noinspection ResultOfMethodCallIgnored doneFile.delete(); installTorExecutable(); + if (isMac()) { + installLibEvent(); + } installObfs4Executable(); installSnowflakeExecutable(); extract(getConfigInputStream(), configFile); @@ -225,6 +233,14 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper { if (!torFile.setExecutable(true, true)) throw new IOException(); } + protected void installLibEvent() throws IOException { + if (LOG.isLoggable(INFO)) { + LOG.info("Installing libevent binary for " + architecture); + } + File libEventFile = getLibEventFile(); + extract(getExecutableInputStream("libevent-2.1.7.dylib"), libEventFile); + } + protected void installObfs4Executable() throws IOException { if (LOG.isLoggable(INFO)) { LOG.info("Installing obfs4proxy binary for " + architecture); diff --git a/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/util/OsUtils.java b/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/util/OsUtils.java new file mode 100644 index 0000000..67373dd --- /dev/null +++ b/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/util/OsUtils.java @@ -0,0 +1,32 @@ +package org.briarproject.onionwrapper.util; + +import org.briarproject.nullsafety.NotNullByDefault; + +import javax.annotation.Nullable; + +import static org.briarproject.onionwrapper.util.StringUtils.startsWithIgnoreCase; + +@NotNullByDefault +public class OsUtils { + + @Nullable + private static final String os = System.getProperty("os.name"); + @Nullable + private static final String vendor = System.getProperty("java.vendor"); + + public static boolean isWindows() { + return os != null && startsWithIgnoreCase(os, "Win"); + } + + public static boolean isMac() { + return os != null && os.equalsIgnoreCase("Mac OS X"); + } + + public static boolean isLinux() { + return os != null && startsWithIgnoreCase(os, "Linux") && !isAndroid(); + } + + public static boolean isAndroid() { + return vendor != null && vendor.contains("Android"); + } +} diff --git a/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/util/StringUtils.java b/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/util/StringUtils.java new file mode 100644 index 0000000..8964d41 --- /dev/null +++ b/onionwrapper-core/src/main/java/org/briarproject/onionwrapper/util/StringUtils.java @@ -0,0 +1,12 @@ +package org.briarproject.onionwrapper.util; + +import org.briarproject.nullsafety.NotNullByDefault; + +@NotNullByDefault +public class StringUtils { + + // see https://stackoverflow.com/a/38947571 + static boolean startsWithIgnoreCase(String s, String prefix) { + return s.regionMatches(true, 0, prefix, 0, prefix.length()); + } +} diff --git a/onionwrapper-core/src/test/java/org/briarproject/onionwrapper/TestUtils.java b/onionwrapper-core/src/test/java/org/briarproject/onionwrapper/TestUtils.java index 2c6b65e..82ad9bd 100644 --- a/onionwrapper-core/src/test/java/org/briarproject/onionwrapper/TestUtils.java +++ b/onionwrapper-core/src/test/java/org/briarproject/onionwrapper/TestUtils.java @@ -12,7 +12,9 @@ import javax.annotation.concurrent.ThreadSafe; import static java.util.Arrays.asList; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; -import static org.briarproject.onionwrapper.StringUtils.startsWithIgnoreCase; +import static org.briarproject.onionwrapper.util.OsUtils.isLinux; +import static org.briarproject.onionwrapper.util.OsUtils.isMac; +import static org.briarproject.onionwrapper.util.OsUtils.isWindows; @ThreadSafe @NotNullByDefault @@ -54,29 +56,18 @@ public class TestUtils { } } - public static boolean isLinux() { - String os = System.getProperty("os.name"); - return os != null && os.contains("Linux"); - } - - public static boolean isWindows() { - String os = System.getProperty("os.name"); - return os != null && startsWithIgnoreCase(os, "Win"); - } - - public static boolean isMac() { - String os = System.getProperty("os.name"); - return os != null && os.equalsIgnoreCase("Mac OS X"); - } - @Nullable public static String getArchitectureForTorBinary() { String arch = System.getProperty("os.arch"); if (arch == null) return null; - //noinspection IfCanBeSwitch - if (arch.equals("amd64")) return "x86_64"; - else if (arch.equals("aarch64")) return "aarch64"; - else if (arch.equals("arm")) return "armhf"; + if (isLinux() || isWindows()) { + //noinspection IfCanBeSwitch + if (arch.equals("amd64")) return "x86_64"; + else if (arch.equals("aarch64")) return "aarch64"; + else if (arch.equals("arm")) return "armhf"; + } else if (isMac()) { + return "any"; + } return null; } diff --git a/onionwrapper-java/build.gradle b/onionwrapper-java/build.gradle index 913f000..0226df4 100644 --- a/onionwrapper-java/build.gradle +++ b/onionwrapper-java/build.gradle @@ -1,5 +1,4 @@ -import static org.briarproject.onionwrapper.OS.Linux -import static org.briarproject.onionwrapper.OS.Windows +import static org.briarproject.onionwrapper.OS.* import static org.briarproject.onionwrapper.OsUtils.currentOS plugins { @@ -19,7 +18,7 @@ checkstyle { dependencies { api project(':onionwrapper-core') - def jna_version = '4.5.2' + def jna_version = '5.10.0' implementation "net.java.dev.jna:jna:$jna_version" implementation "net.java.dev.jna:jna-platform:$jna_version" @@ -29,6 +28,10 @@ dependencies { testImplementation "org.briarproject:tor-$currentOS.id:0.4.7.13-2" testImplementation "org.briarproject:obfs4proxy-$currentOS.id:0.0.14-tor2" testImplementation "org.briarproject:snowflake-$currentOS.id:2.5.1" + } else if (currentOS == MacOS) { + testImplementation "org.briarproject:tor-macos-torbrowser:0.4.7.13" + testImplementation "org.briarproject:obfs4proxy-macos-torbrowser:0.0.14" + testImplementation "org.briarproject:snowflake-macos-torbrowser:2.5.1" } } diff --git a/onionwrapper-java/src/main/java/org/briarproject/onionwrapper/UnixTorWrapper.java b/onionwrapper-java/src/main/java/org/briarproject/onionwrapper/UnixTorWrapper.java index 3d196f9..eaea815 100644 --- a/onionwrapper-java/src/main/java/org/briarproject/onionwrapper/UnixTorWrapper.java +++ b/onionwrapper-java/src/main/java/org/briarproject/onionwrapper/UnixTorWrapper.java @@ -46,7 +46,7 @@ public class UnixTorWrapper extends JavaTorWrapper { private interface CLibrary extends Library { - CLibrary INSTANCE = Native.loadLibrary("c", CLibrary.class); + CLibrary INSTANCE = Native.load("c", CLibrary.class); int getpid(); } diff --git a/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/BootstrapTest.java b/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/BootstrapTest.java index 63d56d8..e8d9cc6 100644 --- a/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/BootstrapTest.java +++ b/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/BootstrapTest.java @@ -15,11 +15,12 @@ import static org.briarproject.nullsafety.NullSafety.requireNonNull; import static org.briarproject.onionwrapper.TestUtils.deleteTestDirectory; import static org.briarproject.onionwrapper.TestUtils.getArchitectureForTorBinary; import static org.briarproject.onionwrapper.TestUtils.getTestDirectory; -import static org.briarproject.onionwrapper.TestUtils.isLinux; -import static org.briarproject.onionwrapper.TestUtils.isWindows; import static org.briarproject.onionwrapper.TorWrapper.TorState.CONNECTED; import static org.briarproject.onionwrapper.TorWrapper.TorState.STARTED; import static org.briarproject.onionwrapper.TorWrapper.TorState.STOPPED; +import static org.briarproject.onionwrapper.util.OsUtils.isLinux; +import static org.briarproject.onionwrapper.util.OsUtils.isMac; +import static org.briarproject.onionwrapper.util.OsUtils.isWindows; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeNotNull; @@ -38,7 +39,7 @@ public class BootstrapTest extends BaseTest { @Before public void setUp() { - assumeTrue(isLinux() || isWindows()); + assumeTrue(isLinux() || isWindows() || isMac()); assumeNotNull(getArchitectureForTorBinary()); } @@ -52,7 +53,7 @@ public class BootstrapTest extends BaseTest { public void testBootstrapping() throws Exception { String architecture = requireNonNull(getArchitectureForTorBinary()); TorWrapper tor; - if (isLinux()) { + if (isLinux() || isMac()) { tor = new UnixTorWrapper(executor, executor, architecture, torDir, CONTROL_PORT, SOCKS_PORT); } else if (isWindows()) { diff --git a/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/BridgeTest.java b/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/BridgeTest.java index 1c822f5..5401107 100644 --- a/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/BridgeTest.java +++ b/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/BridgeTest.java @@ -32,9 +32,9 @@ import static org.briarproject.onionwrapper.CircumventionProvider.BridgeType.VAN import static org.briarproject.onionwrapper.TestUtils.deleteTestDirectory; import static org.briarproject.onionwrapper.TestUtils.getArchitectureForTorBinary; import static org.briarproject.onionwrapper.TestUtils.getTestDirectory; -import static org.briarproject.onionwrapper.TestUtils.isLinux; import static org.briarproject.onionwrapper.TestUtils.isOptionalTestEnabled; import static org.briarproject.onionwrapper.TorWrapper.TorState.CONNECTED; +import static org.briarproject.onionwrapper.util.OsUtils.isLinux; import static org.junit.Assert.fail; import static org.junit.Assume.assumeNotNull; import static org.junit.Assume.assumeTrue; diff --git a/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/ResourcesLinuxTest.java b/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/ResourcesLinuxTest.java index 7ebbf50..666f627 100644 --- a/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/ResourcesLinuxTest.java +++ b/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/ResourcesLinuxTest.java @@ -3,7 +3,7 @@ package org.briarproject.onionwrapper; import org.junit.Before; import org.junit.Test; -import static org.briarproject.onionwrapper.TestUtils.isLinux; +import static org.briarproject.onionwrapper.util.OsUtils.isLinux; import static org.junit.Assert.assertNotNull; import static org.junit.Assume.assumeTrue; diff --git a/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/ResourcesMacTest.java b/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/ResourcesMacTest.java new file mode 100644 index 0000000..884225b --- /dev/null +++ b/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/ResourcesMacTest.java @@ -0,0 +1,42 @@ +package org.briarproject.onionwrapper; + +import org.junit.Before; +import org.junit.Test; + +import static org.briarproject.onionwrapper.util.OsUtils.isMac; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeTrue; + +public class ResourcesMacTest { + + @Before + public void setUp() { + assumeTrue(isMac()); + } + + @Test + public void testCanLoadTor() { + testCanLoadResource("any/tor"); + } + + @Test + public void testCanLoadLibEvent() { + testCanLoadResource("any/libevent-2.1.7.dylib"); + } + + @Test + public void testCanLoadObfs4() { + testCanLoadResource("any/obfs4proxy"); + } + + @Test + public void testCanLoadSnowflake() { + testCanLoadResource("any/snowflake"); + } + + private void testCanLoadResource(String name) { + ClassLoader classLoader = + Thread.currentThread().getContextClassLoader(); + assertNotNull(classLoader.getResourceAsStream(name)); + } +} diff --git a/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/ResourcesWindowsTest.java b/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/ResourcesWindowsTest.java index 01a0137..51c8ddf 100644 --- a/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/ResourcesWindowsTest.java +++ b/onionwrapper-java/src/test/java/org/briarproject/onionwrapper/ResourcesWindowsTest.java @@ -3,7 +3,7 @@ package org.briarproject.onionwrapper; import org.junit.Before; import org.junit.Test; -import static org.briarproject.onionwrapper.TestUtils.isWindows; +import static org.briarproject.onionwrapper.util.OsUtils.isWindows; import static org.junit.Assert.assertNotNull; import static org.junit.Assume.assumeTrue; diff --git a/settings.gradle b/settings.gradle index 2bb9f2a..7412b9f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,6 +10,7 @@ dependencyResolutionManagement { repositories { google() mavenCentral() + maven { url "https://mvntmp.mobanisto.de" } } } rootProject.name = "Onion Wrapper" -- GitLab