diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 4d378c7b..89d4d320 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2,8 +2,8 @@ + android:versionCode="5" + android:versionName="1.1.1"> diff --git a/jni/Application.mk b/jni/Application.mk index 05e88a76..2539ffd4 100644 --- a/jni/Application.mk +++ b/jni/Application.mk @@ -1 +1 @@ -APP_PLATFORM = anrdoid-8 +APP_PLATFORM = android-9 diff --git a/jni/termExec.cpp b/jni/termExec.cpp index e2c83d2b..d7089e11 100644 --- a/jni/termExec.cpp +++ b/jni/termExec.cpp @@ -49,6 +49,7 @@ #include #include #include +#include static jclass class_fileDescriptor; static jfieldID field_fileDescriptor_descriptor; @@ -98,24 +99,15 @@ static int throwOutOfMemoryError(JNIEnv *env, const char *message) return env->ThrowNew(exClass, message); } -static int create_subprocess(const char *cmd, - char *const argv[], char *const envp[], int* pProcessId) +static int create_subprocess(const char *cmd, char *const argv[], + char *const envp[], const char* scripts, int* pProcessId) { - char filename[] = "/data/data/com.github.shadowsocks/defout"; - char script[] = "/data/data/com.github.shadowsocks/script"; pid_t pid; + int pfds[2]; + int pfds2[2]; - if (chmod(script, 0755) < 0) { - LOGE("error to chmod\n"); - exit(-1); - } - - int defout = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0600); - - if(defout < 0) { - LOGE("open file error\n"); - exit(-1); - } + pipe(pfds); + pipe(pfds2); pid = fork(); @@ -126,28 +118,41 @@ static int create_subprocess(const char *cmd, if(pid == 0){ - //setsid(); - - dup2(defout, 1); - dup2(defout, 2); - if (envp) { for (; *envp; ++envp) { putenv(*envp); } } + dup2(pfds[0], 0); + close(pfds[1]); + + dup2(pfds2[1], 1); + dup2(pfds2[1], 2); + close(pfds2[0]); + execv(cmd, argv); - exit(-1); + + fflush(NULL); + + exit(0); + } else { *pProcessId = (int) pid; - return defout; + + dup2(pfds[1], 1); + close(pfds[0]); + + write(pfds[1], scripts, strlen(scripts)+1); + + close(pfds2[1]); + return pfds2[0]; } } static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz, - jstring cmd, jobjectArray args, jobjectArray envVars, + jstring cmd, jobjectArray args, jobjectArray envVars, jstring scripts, jintArray processIdArray) { const jchar* str = cmd ? env->GetStringCritical(cmd, 0) : 0; @@ -157,6 +162,13 @@ static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz, env->ReleaseStringCritical(cmd, str); } + const jchar* str_scripts = scripts ? env->GetStringCritical(scripts, 0) : 0; + String8 scripts_8; + if (str_scripts) { + scripts_8.set(str_scripts, env->GetStringLength(scripts)); + env->ReleaseStringCritical(scripts, str_scripts); + } + jsize size = args ? env->GetArrayLength(args) : 0; char **argv = NULL; String8 tmp_8; @@ -203,7 +215,8 @@ static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz, } int procId; - int ptm = create_subprocess(cmd_8.string(), argv, envp, &procId); + int ptm = create_subprocess(cmd_8.string(), argv, envp, scripts_8.string(), + &procId); if (argv) { for (char **tmp = argv; *tmp; ++tmp) { @@ -311,7 +324,7 @@ static int register_FileDescriptor(JNIEnv *env) static const char *classPathName = "com/github/shadowsocks/Exec"; static JNINativeMethod method_table[] = { - { "createSubprocess", "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;[I)Ljava/io/FileDescriptor;", + { "createSubprocess", "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[I)Ljava/io/FileDescriptor;", (void*) android_os_Exec_createSubProcess }, { "waitFor", "(I)I", (void*) android_os_Exec_waitFor}, diff --git a/libs/armeabi/libexec.so b/libs/armeabi/libexec.so index e1bb3dd7..a9659fc7 100755 Binary files a/libs/armeabi/libexec.so and b/libs/armeabi/libexec.so differ diff --git a/pom.xml b/pom.xml index 2fd1e3fa..c4d95fa2 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.github.shadowsocks shadowsocks - 1.1 + 1.1.1 apk Shadowsocks diff --git a/src/com/github/shadowsocks/Exec.java b/src/com/github/shadowsocks/Exec.java index 090723ad..edc892bd 100755 --- a/src/com/github/shadowsocks/Exec.java +++ b/src/com/github/shadowsocks/Exec.java @@ -43,16 +43,18 @@ public class Exec { * Callers are responsible for calling Exec.close() on the returned file * descriptor. * - * @param cmd The command to execute - * @param args An array of arguments to the command + * @param cmd The command to execute. + * @param args An array of arguments to the command. * @param envVars An array of strings of the form "VAR=value" to be added to the - * environment of the process + * environment of the process. + * @param scripts The scripts to execute. * @param processId A one-element array to which the process ID of the started * process will be written. - * @return the file descriptor of the started process. + * @return File descriptor */ public static native FileDescriptor createSubprocess(String cmd, - String[] args, String[] envVars, int[] processId); + String[] args, String[] envVars, + String scripts, int[] processId); /** * Send SIGHUP to a process group. diff --git a/src/com/github/shadowsocks/Shadowsocks.java b/src/com/github/shadowsocks/Shadowsocks.java old mode 100644 new mode 100755 diff --git a/src/com/github/shadowsocks/ShadowsocksApplication.java b/src/com/github/shadowsocks/ShadowsocksApplication.java index b0ac76c6..ccb2a208 100755 --- a/src/com/github/shadowsocks/ShadowsocksApplication.java +++ b/src/com/github/shadowsocks/ShadowsocksApplication.java @@ -1,13 +1,21 @@ package com.github.shadowsocks; import android.app.Application; +import android.content.Context; import com.google.analytics.tracking.android.EasyTracker; public class ShadowsocksApplication extends Application { + private static String sTmpDir; + + public static String getTmpDir() { + return sTmpDir; + } + @Override public void onCreate() { EasyTracker.getInstance().setContext(this); + sTmpDir = getCacheDir().getAbsolutePath(); } } diff --git a/src/com/github/shadowsocks/Utils.java b/src/com/github/shadowsocks/Utils.java index 5470ce6a..390b9717 100755 --- a/src/com/github/shadowsocks/Utils.java +++ b/src/com/github/shadowsocks/Utils.java @@ -8,7 +8,10 @@ import android.graphics.drawable.Drawable; import android.os.Environment; import android.util.Log; -import java.io.*; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.InputStream; import java.util.ArrayList; public class Utils { @@ -17,51 +20,32 @@ public class Utils { * Internal thread used to execute scripts (as root or not). */ private static final class ScriptRunner extends Thread { - private final File file; - private final String script; - private final StringBuilder res; + private final String scripts; + private final StringBuilder result; private final boolean asroot; public int exitcode = -1; - // private Process exec; - private int mProcId; - private FileDescriptor mTermFd; /** * Creates a new script runner. * - * @param file temporary script file * @param script script to run - * @param res response output + * @param result result output * @param asroot if true, executes the script as root */ - public ScriptRunner(File file, String script, StringBuilder res, + public ScriptRunner(String script, StringBuilder result, boolean asroot) { - this.file = file; - this.script = script; - this.res = res; + this.scripts = script; + this.result = result; this.asroot = asroot; } - private int createSubprocess(int[] processId, String cmd) { + private FileDescriptor createSubprocess(int[] processId, String cmd) { ArrayList argList = parse(cmd); String arg0 = argList.get(0); String[] args = argList.toArray(new String[1]); - mTermFd = Exec.createSubprocess(arg0, args, null, processId); - return processId[0]; - } - - /** - * Destroy this script runner - */ - @Override - public synchronized void destroy() { - try { - Exec.hangupProcessGroup(mProcId); - Exec.close(mTermFd); - } catch (NoClassDefFoundError ignore) { - // Nothing - } + return Exec.createSubprocess(arg0, args, null, + scripts + "\nexit\n", processId); } private ArrayList parse(String cmd) { @@ -114,61 +98,48 @@ public class Utils { @Override public void run() { + FileDescriptor pipe = null; + int pid[] = new int[1]; + pid[0] = -1; + try { - new File(DEFOUT_FILE).createNewFile(); - file.createNewFile(); - final String abspath = file.getAbsolutePath(); - - // TODO: Rewrite this line - // make sure we have execution permission on the script file - // Runtime.getRuntime().exec("chmod 755 " + abspath).waitFor(); - - // Write the script to be executed - final OutputStreamWriter out = new OutputStreamWriter( - new FileOutputStream(file)); - out.write("#!/system/bin/sh\n"); - out.write(script); - if (!script.endsWith("\n")) - out.write("\n"); - out.write("exit\n"); - out.flush(); - out.close(); if (this.asroot) { // Create the "su" request to run the script - // exec = Runtime.getRuntime().exec( - // root_shell + " -c " + abspath); - - int pid[] = new int[1]; - mProcId = createSubprocess(pid, root_shell + " -c " - + abspath); + pipe = createSubprocess(pid, root_shell); } else { // Create the "sh" request to run the script - // exec = Runtime.getRuntime().exec(getShell() + " " + - // abspath); - - int pid[] = new int[1]; - mProcId = createSubprocess(pid, getShell() + " " + abspath); + pipe = createSubprocess(pid, getShell()); } - final InputStream stdout = new FileInputStream(DEFOUT_FILE); + if (pipe == null) return; + + if (pid[0] != -1) { + Exec.waitFor(pid[0]); + } + + final InputStream stdout = new FileInputStream(pipe); final byte buf[] = new byte[8192]; int read = 0; - exitcode = Exec.waitFor(mProcId); - // Read stdout while (stdout.available() > 0) { read = stdout.read(buf); - if (res != null) - res.append(new String(buf, 0, read)); + if (result != null) + result.append(new String(buf, 0, read)); } } catch (Exception ex) { - if (res != null) - res.append("\n" + ex); + Log.e(TAG, "Cannot execute command", ex); + if (result != null) + result.append("\n").append(ex); } finally { - destroy(); + if (pipe != null) { + Exec.close(pipe); + } + if (pid[0] != -1) { + Exec.hangupProcessGroup(pid[0]); + } } } } @@ -181,8 +152,6 @@ public class Utils { public final static String ALTERNATIVE_ROOT = "/system/xbin/su"; public final static String DEFAULT_IPTABLES = "/data/data/com.github.shadowsocks/iptables"; public final static String ALTERNATIVE_IPTABLES = "/system/bin/iptables"; - public final static String SCRIPT_FILE = "/data/data/com.github.shadowsocks/script"; - public final static String DEFOUT_FILE = "/data/data/com.github.shadowsocks/defout"; public final static int TIME_OUT = -99; private static boolean initialized = false; @@ -281,7 +250,7 @@ public class Utils { public static boolean getHasRedirectSupport() { if (hasRedirectSupport == -1) initHasRedirectSupported(); - return hasRedirectSupport == 1 ? true : false; + return hasRedirectSupport == 1; } public static String getIptables() { @@ -338,7 +307,7 @@ public class Utils { public static boolean isRoot() { if (isRoot != -1) - return isRoot == 1 ? true : false; + return isRoot == 1; // switch between binaries if (new File(DEFAULT_ROOT).exists()) { @@ -352,7 +321,7 @@ public class Utils { String lines = null; StringBuilder sb = new StringBuilder(); - String command = "ls /\n" + "exit\n"; + String command = "ls /\n exit\n"; int exitcode = runScript(command, sb, 10 * 1000, true); @@ -366,7 +335,7 @@ public class Utils { isRoot = 1; } - return isRoot == 1 ? true : false; + return isRoot == 1; } public static boolean runCommand(String command) { @@ -392,8 +361,10 @@ public class Utils { public static boolean runRootCommand(String command, int timeout) { - if (!isRoot()) + if (!isRoot()) { + Log.e(TAG, "Cannot get ROOT permission: " + root_shell); return false; + } Log.d(TAG, command); @@ -402,10 +373,9 @@ public class Utils { return true; } - private synchronized static int runScript(String script, StringBuilder res, + private synchronized static int runScript(String script, StringBuilder result, long timeout, boolean asroot) { - final File file = new File(SCRIPT_FILE); - final ScriptRunner runner = new ScriptRunner(file, script, res, asroot); + final ScriptRunner runner = new ScriptRunner(script, result, asroot); runner.start(); try { if (timeout > 0) {