diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e4150f1c..9f2b78cb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -58,6 +58,10 @@ android:name=".activities.AboutActivity" android:launchMode="singleTop" android:parentActivityName=".activities.MainActivity" /> + . + * + * Copyright 2020-21 - Emanuele Faranda + */ + +package com.emanuelef.remote_capture.activities; + +import android.content.Intent; +import android.os.Bundle; +import android.text.Html; +import android.text.method.LinkMovementMethod; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import com.emanuelef.remote_capture.CaptureService; +import com.emanuelef.remote_capture.R; +import com.emanuelef.remote_capture.Utils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; + +public class LogviewActivity extends BaseActivity { + private static final String TAG = "LogviewActivity"; + private String mLogText; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setTitle(R.string.root_log); + setContentView(R.layout.logview_activity); + + TextView logView = findViewById(R.id.log); + mLogText = readLog(); + + logView.setText(!mLogText.isEmpty() ? mLogText : getString(R.string.error)); + } + + private String readLog() { + try { + String logpath = CaptureService.getPcapdWorkingDir(this) + "/pcapd.log"; + BufferedReader reader = new BufferedReader(new FileReader(logpath)); + + StringBuilder builder = new StringBuilder(); + String line; + + while((line = reader.readLine()) != null) { + builder.append(line); + builder.append("\n"); + } + + return builder.toString(); + } catch (IOException e) { + e.printStackTrace(); + } + + return ""; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.copy_share_menu, menu); + + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + int id = item.getItemId(); + + if(id == R.id.copy_to_clipboard) { + Utils.copyToClipboard(this, mLogText); + return true; + } else if(id == R.id.share) { + Intent intent = new Intent(android.content.Intent.ACTION_SEND); + intent.setType("text/plain"); + intent.putExtra(android.content.Intent.EXTRA_SUBJECT, getString(R.string.root_log)); + intent.putExtra(android.content.Intent.EXTRA_TEXT, mLogText); + + startActivity(Intent.createChooser(intent, getResources().getString(R.string.share))); + + return true; + } + + return super.onOptionsItemSelected(item); + } +} diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java index 61120852..7d0369b7 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java @@ -51,6 +51,7 @@ import android.provider.DocumentsContract; import android.provider.OpenableColumns; import android.util.Log; import android.view.KeyEvent; +import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.TextView; @@ -187,6 +188,11 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(GITHUB_PROJECT_URL + "/tree/" + verStr)); startActivity(browserIntent); }); + + if(Prefs.isRootCaptureEnabled(mPrefs)) { + Menu navMenu = navView.getMenu(); + navMenu.findItem(R.id.open_root_log).setVisible(true); + } } @Override @@ -345,6 +351,9 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig startActivity(intent); } else Utils.showToast(this, R.string.capture_not_started); + } else if(id == R.id.open_root_log) { + Intent intent = new Intent(MainActivity.this, LogviewActivity.class); + startActivity(intent); } else if (id == R.id.action_donate) { Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(DONATE_URL)); startActivity(browserIntent); diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/StatsActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/StatsActivity.java index dba500f2..942c0696 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/StatsActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/StatsActivity.java @@ -145,12 +145,7 @@ public class StatsActivity extends BaseActivity { if(id == R.id.copy_to_clipboard) { String contents = Utils.table2Text(mTable); - - ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText(getString(R.string.stats), contents); - clipboard.setPrimaryClip(clip); - - Utils.showToast(this, R.string.copied_to_clipboard); + Utils.copyToClipboard(this, contents); return true; } else if(id == R.id.share) { String contents = Utils.table2Text(mTable); diff --git a/app/src/main/jni/common/utils.c b/app/src/main/jni/common/utils.c index 41dba7bf..617808a0 100644 --- a/app/src/main/jni/common/utils.c +++ b/app/src/main/jni/common/utils.c @@ -34,8 +34,8 @@ void set_log_level(int lvl) { /* ******************************************************* */ -void log_android(int prio, const char *fmt, ...) { - if(prio >= loglevel) { +void log_android(int lvl, const char *fmt, ...) { + if(lvl >= loglevel) { char line[1024]; va_list argptr; @@ -43,10 +43,23 @@ void log_android(int prio, const char *fmt, ...) { vsnprintf(line, sizeof(line), fmt, argptr); va_end(argptr); - __android_log_print(prio, logtag, "%s", line); + __android_log_print(lvl, logtag, "%s", line); if(logcallback != NULL) - logcallback(prio, line); + logcallback(lvl, line); + } +} + +/* ******************************************************* */ + +char loglvl2char(int lvl) { + switch (lvl) { + case ANDROID_LOG_DEBUG: return 'D'; + case ANDROID_LOG_INFO: return 'I'; + case ANDROID_LOG_WARN: return 'W'; + case ANDROID_LOG_ERROR: return 'E'; + case ANDROID_LOG_FATAL: return 'F'; + default: return '?'; } } diff --git a/app/src/main/jni/common/utils.h b/app/src/main/jni/common/utils.h index b2342c83..23a7cc3b 100644 --- a/app/src/main/jni/common/utils.h +++ b/app/src/main/jni/common/utils.h @@ -35,10 +35,11 @@ extern void (*logcallback)(int lvl, const char *msg); #define log_e(...) log_android(ANDROID_LOG_ERROR, __VA_ARGS__) #define log_f(...) log_android(ANDROID_LOG_FATAL, __VA_ARGS__) -void log_android(int prio, const char *fmt, ...); +void log_android(int lvl, const char *fmt, ...); ssize_t xwrite(int fd, const void *buf, size_t count); ssize_t xread(int fd, void *buf, size_t count); void tupleSwapPeers(zdtun_5tuple_t *tuple); +char loglvl2char(int lvl); jclass jniFindClass(JNIEnv *env, const char *name); jmethodID jniGetMethodID(JNIEnv *env, jclass cls, const char *name, const char *signature); diff --git a/app/src/main/jni/pcapd/pcapd.c b/app/src/main/jni/pcapd/pcapd.c index f838c7f0..b71c562d 100644 --- a/app/src/main/jni/pcapd/pcapd.c +++ b/app/src/main/jni/pcapd/pcapd.c @@ -71,6 +71,8 @@ typedef struct { } pcapd_runtime_t; static char errbuf[PCAP_ERRBUF_SIZE]; +static FILE *logf = NULL; +static uint8_t no_logf = 0; /* ******************************************************* */ @@ -84,6 +86,35 @@ static uint64_t bytes2mac(const uint8_t *buf) { /* ******************************************************* */ +static void log_to_file(int lvl, const char *msg) { + char datetime[64]; + struct tm res; + time_t now; + + if(no_logf) + return; + + if(logf == NULL) { + mode_t old_mask = umask(033); + logf = fopen(PCAPD_LOGFILE_PATH, "w"); + umask(old_mask); + + if(logf == NULL) { + log_e("Could not open log file[%d]: %s", errno, strerror(errno)); + no_logf = 1; + return; + } + } + + now = time(NULL); + strftime(datetime, sizeof(datetime), "%d/%b/%Y %H:%M:%S", localtime_r(&now, &res)); + + fprintf(logf, "[%c] %s - %s\n", loglvl2char(lvl), datetime, msg); + fflush(logf); +} + +/* ******************************************************* */ + static int get_iface_mac(const char *iface, uint64_t *mac) { struct ifreq ifr; int fd; @@ -103,7 +134,7 @@ static int get_iface_mac(const char *iface, uint64_t *mac) { /* ******************************************************* */ -int get_iface_ip(const char *iface, uint32_t *ip, uint32_t *netmask) { +static int get_iface_ip(const char *iface, uint32_t *ip, uint32_t *netmask) { struct ifreq ifr; int fd; int rv; @@ -148,6 +179,9 @@ static void sighandler(__unused int signo) { log_i("SIGTERM received, terminating"); unlink(PCAPD_PID); + if(logf) + fclose(logf); + exit(0); } @@ -493,7 +527,7 @@ static int run_pcap_dump(int uid_filter) { } if(FD_ISSET(rt.client, &fds)) { - log_i("client closed"); + log_i("Client closed"); break; } if(FD_ISSET(rt.nlsock, &fds)) { @@ -593,6 +627,7 @@ static void usage() { int main(int argc, char *argv[]) { logtag = "pcapd"; + logcallback = log_to_file; if(argc < 2) usage(); diff --git a/app/src/main/jni/pcapd/pcapd.h b/app/src/main/jni/pcapd/pcapd.h index b6c9d98a..4ed52184 100644 --- a/app/src/main/jni/pcapd/pcapd.h +++ b/app/src/main/jni/pcapd/pcapd.h @@ -21,6 +21,7 @@ #define __PCAPD_H__ #define PCAPD_SOCKET_PATH "pcapsock" +#define PCAPD_LOGFILE_PATH "pcapd.log" #define PCAPD_PID "pcapd.pid" #define PCAPD_FLAG_TX (1 << 0) diff --git a/app/src/main/res/drawable/ic_bug_report.xml b/app/src/main/res/drawable/ic_bug_report.xml new file mode 100644 index 00000000..c72d5c25 --- /dev/null +++ b/app/src/main/res/drawable/ic_bug_report.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/logview_activity.xml b/app/src/main/res/layout/logview_activity.xml new file mode 100644 index 00000000..baab217a --- /dev/null +++ b/app/src/main/res/layout/logview_activity.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/nav_items.xml b/app/src/main/res/menu/nav_items.xml index a8b9d849..95aa7d2f 100644 --- a/app/src/main/res/menu/nav_items.xml +++ b/app/src/main/res/menu/nav_items.xml @@ -9,6 +9,11 @@ android:id="@+id/action_stats" android:title="@string/stats" android:icon="@drawable/ic_list" /> + Capturing packets as root allows PCAPdroid to run with other VPN apps. Donate HTTP Request + Root Log