Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>master
@@ -5,7 +5,6 @@ | |||
package com.wireguard.android; | |||
import android.app.PendingIntent; | |||
import android.content.Context; | |||
import android.content.Intent; | |||
import android.content.SharedPreferences; | |||
@@ -20,7 +19,6 @@ import androidx.preference.PreferenceManager; | |||
import androidx.annotation.Nullable; | |||
import androidx.appcompat.app.AppCompatDelegate; | |||
import com.wireguard.android.activity.MainActivity; | |||
import com.wireguard.android.backend.Backend; | |||
import com.wireguard.android.backend.GoBackend; | |||
import com.wireguard.android.backend.WgQuickBackend; | |||
@@ -88,7 +86,7 @@ public class Application extends android.app.Application { | |||
try { | |||
if (!didStartRootShell) | |||
app.rootShell.start(); | |||
backend = new WgQuickBackend(app.getApplicationContext()); | |||
backend = new WgQuickBackend(app.getApplicationContext(), app.rootShell, app.toolsInstaller); | |||
} catch (final Exception ignored) { | |||
} | |||
} | |||
@@ -119,6 +117,7 @@ public class Application extends android.app.Application { | |||
public static ToolsInstaller getToolsInstaller() { | |||
return get().toolsInstaller; | |||
} | |||
public static ModuleLoader getModuleLoader() { | |||
return get().moduleLoader; | |||
} | |||
@@ -152,8 +151,8 @@ public class Application extends android.app.Application { | |||
asyncWorker = new AsyncWorker(AsyncTask.SERIAL_EXECUTOR, new Handler(Looper.getMainLooper())); | |||
rootShell = new RootShell(getApplicationContext()); | |||
toolsInstaller = new ToolsInstaller(getApplicationContext()); | |||
moduleLoader = new ModuleLoader(getApplicationContext()); | |||
toolsInstaller = new ToolsInstaller(getApplicationContext(), rootShell); | |||
moduleLoader = new ModuleLoader(getApplicationContext(), rootShell, USER_AGENT); | |||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); | |||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { | |||
@@ -10,9 +10,10 @@ import androidx.annotation.Nullable; | |||
import android.content.Context; | |||
import android.util.Log; | |||
import com.wireguard.android.Application; | |||
import com.wireguard.android.backend.BackendException.Reason; | |||
import com.wireguard.android.backend.Tunnel.State; | |||
import com.wireguard.android.util.RootShell; | |||
import com.wireguard.android.util.ToolsInstaller; | |||
import com.wireguard.config.Config; | |||
import com.wireguard.crypto.Key; | |||
@@ -40,12 +41,16 @@ import java9.util.stream.Stream; | |||
public final class WgQuickBackend implements Backend { | |||
private static final String TAG = "WireGuard/" + WgQuickBackend.class.getSimpleName(); | |||
private final RootShell rootShell; | |||
private final ToolsInstaller toolsInstaller; | |||
private final File localTemporaryDir; | |||
private final Map<Tunnel, Config> runningConfigs = new HashMap<>(); | |||
private final Set<TunnelStateChangeNotificationReceiver> notifiers = new HashSet<>(); | |||
public WgQuickBackend(final Context context) { | |||
public WgQuickBackend(final Context context, final RootShell rootShell, final ToolsInstaller toolsInstaller) { | |||
localTemporaryDir = new File(context.getCacheDir(), "tmp"); | |||
this.rootShell = rootShell; | |||
this.toolsInstaller = toolsInstaller; | |||
} | |||
@Override | |||
@@ -53,8 +58,8 @@ public final class WgQuickBackend implements Backend { | |||
final List<String> output = new ArrayList<>(); | |||
// Don't throw an exception here or nothing will show up in the UI. | |||
try { | |||
Application.getToolsInstaller().ensureToolsAvailable(); | |||
if (Application.getRootShell().run(output, "wg show interfaces") != 0 || output.isEmpty()) | |||
toolsInstaller.ensureToolsAvailable(); | |||
if (rootShell.run(output, "wg show interfaces") != 0 || output.isEmpty()) | |||
return Collections.emptySet(); | |||
} catch (final Exception e) { | |||
Log.w(TAG, "Unable to enumerate running tunnels", e); | |||
@@ -74,7 +79,7 @@ public final class WgQuickBackend implements Backend { | |||
final Statistics stats = new Statistics(); | |||
final Collection<String> output = new ArrayList<>(); | |||
try { | |||
if (Application.getRootShell().run(output, String.format("wg show '%s' transfer", tunnel.getName())) != 0) | |||
if (rootShell.run(output, String.format("wg show '%s' transfer", tunnel.getName())) != 0) | |||
return stats; | |||
} catch (final Exception ignored) { | |||
return stats; | |||
@@ -94,8 +99,7 @@ public final class WgQuickBackend implements Backend { | |||
@Override | |||
public String getVersion() throws Exception { | |||
final List<String> output = new ArrayList<>(); | |||
if (Application.getRootShell() | |||
.run(output, "cat /sys/module/wireguard/version") != 0 || output.isEmpty()) | |||
if (rootShell.run(output, "cat /sys/module/wireguard/version") != 0 || output.isEmpty()) | |||
throw new BackendException(Reason.UNKNOWN_KERNEL_MODULE_NAME); | |||
return output.get(0); | |||
} | |||
@@ -111,7 +115,7 @@ public final class WgQuickBackend implements Backend { | |||
(state == State.DOWN && originalState == State.DOWN)) | |||
return originalState; | |||
if (state == State.UP) { | |||
Application.getToolsInstaller().ensureToolsAvailable(); | |||
toolsInstaller.ensureToolsAvailable(); | |||
if (originalState == State.UP) | |||
setStateInternal(tunnel, originalConfig == null ? config : originalConfig, State.DOWN); | |||
try { | |||
@@ -140,7 +144,7 @@ public final class WgQuickBackend implements Backend { | |||
state.toString().toLowerCase(Locale.ENGLISH), tempFile.getAbsolutePath()); | |||
if (state == State.UP) | |||
command = "cat /sys/module/wireguard/version && " + command; | |||
final int result = Application.getRootShell().run(null, command); | |||
final int result = rootShell.run(null, command); | |||
// noinspection ResultOfMethodCallIgnored | |||
tempFile.delete(); | |||
if (result != 0) | |||
@@ -9,7 +9,6 @@ import android.content.Context; | |||
import android.system.OsConstants; | |||
import android.util.Base64; | |||
import com.wireguard.android.Application; | |||
import com.wireguard.android.util.RootShell.RootShellException; | |||
import net.i2p.crypto.eddsa.EdDSAEngine; | |||
@@ -43,12 +42,16 @@ public class ModuleLoader { | |||
private static final String MODULE_URL = "https://download.wireguard.com/android-module/%s"; | |||
private static final String MODULE_NAME = "wireguard-%s.ko"; | |||
private final RootShell rootShell; | |||
private final String userAgent; | |||
private final File moduleDir; | |||
private final File tmpDir; | |||
public ModuleLoader(final Context context) { | |||
public ModuleLoader(final Context context, final RootShell rootShell, final String userAgent) { | |||
moduleDir = new File(context.getCacheDir(), "kmod"); | |||
tmpDir = new File(context.getCacheDir(), "tmp"); | |||
this.rootShell = rootShell; | |||
this.userAgent = userAgent; | |||
} | |||
public boolean moduleMightExist() { | |||
@@ -56,7 +59,7 @@ public class ModuleLoader { | |||
} | |||
public void loadModule() throws IOException, RootShellException { | |||
Application.getRootShell().run(null, String.format("insmod \"%s/wireguard-$(sha256sum /proc/version|cut -d ' ' -f 1).ko\"", moduleDir.getAbsolutePath())); | |||
rootShell.run(null, String.format("insmod \"%s/wireguard-$(sha256sum /proc/version|cut -d ' ' -f 1).ko\"", moduleDir.getAbsolutePath())); | |||
} | |||
public static boolean isModuleLoaded() { | |||
@@ -124,12 +127,12 @@ public class ModuleLoader { | |||
public Integer download() throws IOException, RootShellException, NoSuchAlgorithmException { | |||
final List<String> output = new ArrayList<>(); | |||
Application.getRootShell().run(output, "sha256sum /proc/version|cut -d ' ' -f 1"); | |||
rootShell.run(output, "sha256sum /proc/version|cut -d ' ' -f 1"); | |||
if (output.size() != 1 || output.get(0).length() != 64) | |||
throw new InvalidParameterException("Invalid sha256 of /proc/version"); | |||
final String moduleName = String.format(MODULE_NAME, output.get(0)); | |||
HttpURLConnection connection = (HttpURLConnection)new URL(MODULE_LIST_URL).openConnection(); | |||
connection.setRequestProperty("User-Agent", Application.USER_AGENT); | |||
connection.setRequestProperty("User-Agent", userAgent); | |||
connection.connect(); | |||
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) | |||
throw new IOException("Hash list could not be found"); | |||
@@ -146,7 +149,7 @@ public class ModuleLoader { | |||
if (!modules.containsKey(moduleName)) | |||
return OsConstants.ENOENT; | |||
connection = (HttpURLConnection)new URL(String.format(MODULE_URL, moduleName)).openConnection(); | |||
connection.setRequestProperty("User-Agent", Application.USER_AGENT); | |||
connection.setRequestProperty("User-Agent", userAgent); | |||
connection.connect(); | |||
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) | |||
throw new IOException("Module file could not be found, despite being on hash list"); | |||
@@ -10,7 +10,6 @@ import androidx.annotation.Nullable; | |||
import android.system.OsConstants; | |||
import android.util.Log; | |||
import com.wireguard.android.Application; | |||
import com.wireguard.android.BuildConfig; | |||
import com.wireguard.android.util.RootShell.RootShellException; | |||
@@ -39,14 +38,16 @@ public final class ToolsInstaller { | |||
private static final String TAG = "WireGuard/" + ToolsInstaller.class.getSimpleName(); | |||
private final Context context; | |||
private final RootShell rootShell; | |||
private final File localBinaryDir; | |||
private final Object lock = new Object(); | |||
@Nullable private Boolean areToolsAvailable; | |||
@Nullable private Boolean installAsMagiskModule; | |||
public ToolsInstaller(final Context context) { | |||
public ToolsInstaller(final Context context, final RootShell rootShell) { | |||
localBinaryDir = new File(context.getCodeCacheDir(), "bin"); | |||
this.context = context; | |||
this.rootShell = rootShell; | |||
} | |||
@Nullable | |||
@@ -73,7 +74,7 @@ public final class ToolsInstaller { | |||
} | |||
script.append("exit ").append(OsConstants.EALREADY).append(';'); | |||
try { | |||
final int ret = Application.getRootShell().run(null, script.toString()); | |||
final int ret = rootShell.run(null, script.toString()); | |||
if (ret == OsConstants.EALREADY) | |||
return willInstallAsMagiskModule() ? YES | MAGISK : YES | SYSTEM; | |||
else | |||
@@ -124,7 +125,7 @@ public final class ToolsInstaller { | |||
script.append("trap - INT TERM EXIT;"); | |||
try { | |||
return Application.getRootShell().run(null, script.toString()) == 0 ? YES | MAGISK : ERROR; | |||
return rootShell.run(null, script.toString()) == 0 ? YES | MAGISK : ERROR; | |||
} catch (final IOException ignored) { | |||
return ERROR; | |||
} catch (final RootShellException e) { | |||
@@ -146,7 +147,7 @@ public final class ToolsInstaller { | |||
new File(localBinaryDir, name), destination, destination, destination)); | |||
} | |||
try { | |||
return Application.getRootShell().run(null, script.toString()) == 0 ? YES | SYSTEM : ERROR; | |||
return rootShell.run(null, script.toString()) == 0 ? YES | SYSTEM : ERROR; | |||
} catch (final IOException ignored) { | |||
return ERROR; | |||
} catch (final RootShellException e) { | |||
@@ -183,7 +184,7 @@ public final class ToolsInstaller { | |||
synchronized (lock) { | |||
if (installAsMagiskModule == null) { | |||
try { | |||
installAsMagiskModule = Application.getRootShell().run(null, "[ -d /sbin/.magisk/mirror -a -d /sbin/.magisk/img -a ! -f /cache/.disable_magisk ]") == OsConstants.EXIT_SUCCESS; | |||
installAsMagiskModule = rootShell.run(null, "[ -d /sbin/.magisk/mirror -a -d /sbin/.magisk/img -a ! -f /cache/.disable_magisk ]") == OsConstants.EXIT_SUCCESS; | |||
} catch (final Exception ignored) { | |||
installAsMagiskModule = false; | |||
} | |||