diff --git a/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java b/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java index 714eee0d..828b9f66 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java +++ b/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java @@ -28,6 +28,7 @@ import android.net.ConnectivityManager; import android.net.VpnService; import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.os.ParcelFileDescriptor; import android.util.Log; import android.widget.Toast; @@ -42,6 +43,7 @@ public class CaptureService extends VpnService implements Runnable { private static final String TAG = "CaptureService"; private static final String VpnSessionName = "PCAPdroid VPN"; private ParcelFileDescriptor mParcelFileDescriptor = null; + private Handler mHandler; private Thread mThread; private String vpn_ipv4; private String vpn_dns; @@ -95,6 +97,8 @@ public class CaptureService extends VpnService implements Runnable { public int onStartCommand(Intent intent, int flags, int startId) { Context app_ctx = getApplicationContext(); + mHandler = new Handler(); + if (intent == null) { Log.d(CaptureService.TAG, "NULL intent onStartCommand"); return super.onStartCommand(null, flags, startId); @@ -392,6 +396,12 @@ public class CaptureService extends VpnService implements Runnable { mHttpServer.pushData(data); } + public void reportError(String msg) { + mHandler.post(() -> { + Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); + }); + } + public static native void runPacketLoop(int fd, CaptureService vpn, int sdk); public static native void stopPacketLoop(); public static native void askConnectionsDump(); diff --git a/app/src/main/java/com/emanuelef/remote_capture/MainActivity.java b/app/src/main/java/com/emanuelef/remote_capture/MainActivity.java index 86b3df19..af33ce08 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/MainActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/MainActivity.java @@ -161,6 +161,10 @@ public class MainActivity extends AppCompatActivity implements LoaderManager.Loa if (status.equals(CaptureService.SERVICE_STATUS_STARTED) && (mState == AppState.starting)) { appStateRunning(); } else if (status.equals(CaptureService.SERVICE_STATUS_STOPPED)) { + // The service may still be active (on premature native termination) + if (CaptureService.isServiceActive()) + CaptureService.stopService(); + appStateReady(); } } @@ -168,6 +172,14 @@ public class MainActivity extends AppCompatActivity implements LoaderManager.Loa }, new IntentFilter(CaptureService.ACTION_SERVICE_STATUS)); } + @Override + public void onResume() { + super.onResume(); + + if(mMenu != null) + initAppState(); + } + private void notifyAppState() { for(AppStateListener listener: mStateListeners) listener.appStateChanged(mState); diff --git a/app/src/main/jni/vpnproxy-jni/jni_helpers.c b/app/src/main/jni/vpnproxy-jni/jni_helpers.c index 9c5a5f87..5d8820eb 100644 --- a/app/src/main/jni/vpnproxy-jni/jni_helpers.c +++ b/app/src/main/jni/vpnproxy-jni/jni_helpers.c @@ -27,6 +27,33 @@ #include static int loglevel = 0; +static JNIEnv *cur_env = NULL; +static jclass vpnclass = 0; +static jclass vpn_inst = 0; +static jmethodID reportError = NULL; + +jmethodID jniGetMethodID(JNIEnv *env, jclass cls, const char *name, const char *signature); +int jniCheckException(JNIEnv *env); +void DeleteLocalRef(JNIEnv *env, jobject *jresource); + +/* ******************************************************* */ + +void init_log(int lvl, JNIEnv *env, jclass _vpnclass, jclass _vpn_inst) { + loglevel = lvl; + cur_env = env; + vpnclass = _vpnclass; + vpn_inst = _vpn_inst; + reportError = jniGetMethodID(cur_env, vpnclass, "reportError", "(Ljava/lang/String;)V"); +} + +/* ******************************************************* */ + +void finish_log() { + cur_env = NULL; + vpnclass = 0; + vpn_inst = 0; + reportError = 0; +} /* ******************************************************* */ @@ -37,8 +64,22 @@ void log_android(int prio, const char *fmt, ...) { va_start(argptr, fmt); vsnprintf(line, sizeof(line), fmt, argptr); - __android_log_print(prio, "VPNProxy", "%s", line); va_end(argptr); + + __android_log_print(prio, "VPNProxy", "%s", line); + + if((prio >= ANDROID_LOG_FATAL) && (cur_env != NULL) && (reportError != NULL)) { + // This is a fatal error, report it to the gui + jobject info_string = (*cur_env)->NewStringUTF(cur_env, line); + + if((jniCheckException(cur_env) != 0) || (info_string == NULL)) + return; + + (*cur_env)->CallVoidMethod(cur_env, vpn_inst, reportError, info_string); + jniCheckException(cur_env); + + DeleteLocalRef(cur_env, info_string); + } } } diff --git a/app/src/main/jni/vpnproxy-jni/vpnproxy.c b/app/src/main/jni/vpnproxy-jni/vpnproxy.c index efb50aec..f7fc8a1a 100644 --- a/app/src/main/jni/vpnproxy-jni/vpnproxy.c +++ b/app/src/main/jni/vpnproxy-jni/vpnproxy.c @@ -638,9 +638,11 @@ static int net2tap(zdtun_t *tun, char *pkt_buf, int pkt_size, const zdtun_conn_t if(errno == EIO) { log_android(ANDROID_LOG_INFO, "Got I/O error (terminating?)"); running = false; - } else - log_android(ANDROID_LOG_ERROR, - "tap write (%d) failed [%d]: %s", pkt_size, errno, strerror(errno)); + } else { + log_android(ANDROID_LOG_FATAL, + "tap write (%d) failed [%d]: %s", pkt_size, errno, strerror(errno)); + running = false; + } } else if(rv != pkt_size) log_android(ANDROID_LOG_WARN, "partial tap write (%d / %d)", rv, pkt_size); @@ -830,7 +832,7 @@ static int connect_dumper(vpnproxy_data_t *proxy) { dumper_socket = socket(AF_INET, proxy->pcap_dump.tcp_socket ? SOCK_STREAM : SOCK_DGRAM, 0); if (!dumper_socket) { - log_android(ANDROID_LOG_ERROR, + log_android(ANDROID_LOG_FATAL, "could not open UDP pcap dump socket [%d]: %s", errno, strerror(errno)); return(-1); @@ -845,7 +847,7 @@ static int connect_dumper(vpnproxy_data_t *proxy) { servaddr.sin_addr.s_addr = proxy->pcap_dump.collector_addr; if(connect(dumper_socket, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { - log_android(ANDROID_LOG_ERROR, + log_android(ANDROID_LOG_FATAL, "connection to the PCAP receiver failed [%d]: %s", errno, strerror(errno)); return(-2); @@ -867,6 +869,8 @@ static int run_tun(JNIEnv *env, jclass vpn, int tapfd, jint sdk) { time_t last_connections_dump = (time(NULL) * 1000) - CONNECTION_DUMP_UPDATE_FREQUENCY_MS + 1000 /* update in a second */; jclass vpn_class = (*env)->GetObjectClass(env, vpn); + init_log(ANDROID_LOG_DEBUG, env, vpn_class, vpn); + zdtun_parse_pkt(mitmproxy_pkt_buffer, sizeof(mitmproxy_pkt_buffer)-1, &mitm_pkt); /* Classes */ @@ -972,7 +976,7 @@ static int run_tun(JNIEnv *env, jclass vpn, int tapfd, jint sdk) { proxy.java_dump.buffer_idx = 0; if(!proxy.java_dump.buffer) { - log_android(ANDROID_LOG_ERROR, "malloc(java_dump.buffer) failed with code %d/%s", + log_android(ANDROID_LOG_FATAL, "malloc(java_dump.buffer) failed with code %d/%s", errno, strerror(errno)); running = false; } @@ -1099,6 +1103,8 @@ housekeeping: } notifyServiceStatus(&proxy, "stopped"); + + finish_log(); return(0); }