summaryrefslogtreecommitdiff
path: root/app/src/main/java/de/blinkt/openvpn/core/LogFileHandler.java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/de/blinkt/openvpn/core/LogFileHandler.java')
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/LogFileHandler.java181
1 files changed, 125 insertions, 56 deletions
diff --git a/app/src/main/java/de/blinkt/openvpn/core/LogFileHandler.java b/app/src/main/java/de/blinkt/openvpn/core/LogFileHandler.java
index 288c7934..57d1fb22 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/LogFileHandler.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/LogFileHandler.java
@@ -8,7 +8,6 @@ package de.blinkt.openvpn.core;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.Parcel;
import java.io.BufferedInputStream;
import java.io.File;
@@ -16,6 +15,10 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.util.Locale;
@@ -29,7 +32,8 @@ class LogFileHandler extends Handler {
static final int FLUSH_TO_DISK = 101;
static final int LOG_INIT = 102;
public static final int LOG_MESSAGE = 103;
- private static FileOutputStream mLogFile;
+ public static final int MAGIC_BYTE = 0x55;
+ protected OutputStream mLogFile;
public static final String LOGFILE_NAME = "logcache.dat";
@@ -47,20 +51,20 @@ class LogFileHandler extends Handler {
throw new RuntimeException("mLogFile not null");
readLogCache((File) msg.obj);
openLogFile((File) msg.obj);
- } else if (msg.what == LOG_MESSAGE && msg.obj instanceof VpnStatus.LogItem) {
+ } else if (msg.what == LOG_MESSAGE && msg.obj instanceof LogItem) {
// Ignore log messages if not yet initialized
if (mLogFile == null)
return;
- writeLogItemToDisk((VpnStatus.LogItem) msg.obj);
+ writeLogItemToDisk((LogItem) msg.obj);
} else if (msg.what == TRIM_LOG_FILE) {
trimLogFile();
- for (VpnStatus.LogItem li : VpnStatus.getlogbuffer())
+ for (LogItem li : VpnStatus.getlogbuffer())
writeLogItemToDisk(li);
} else if (msg.what == FLUSH_TO_DISK) {
flushToDisk();
}
- } catch (IOException e) {
+ } catch (IOException | BufferOverflowException e) {
e.printStackTrace();
VpnStatus.logError("Error during log cache: " + msg.what);
VpnStatus.logException(e);
@@ -72,95 +76,160 @@ class LogFileHandler extends Handler {
mLogFile.flush();
}
- private static void trimLogFile() {
+ private void trimLogFile() {
try {
mLogFile.flush();
- mLogFile.getChannel().truncate(0);
+ ((FileOutputStream) mLogFile).getChannel().truncate(0);
} catch (IOException e) {
e.printStackTrace();
}
}
- private void writeLogItemToDisk(VpnStatus.LogItem li) throws IOException {
- Parcel p = Parcel.obtain();
- li.writeToParcel(p, 0);
+ private void writeLogItemToDisk(LogItem li) throws IOException {
+
// We do not really care if the log cache breaks between Android upgrades,
// write binary format to disc
- byte[] liBytes = p.marshall();
- byte[] lenBytes = ByteBuffer.allocate(4).putInt(liBytes.length).array();
- mLogFile.write(lenBytes);
- mLogFile.write(liBytes);
- p.recycle();
+ byte[] liBytes = li.getMarschaledBytes();
+
+ writeEscapedBytes(liBytes);
+ }
+
+ public void writeEscapedBytes(byte[] bytes) throws IOException {
+ int magic = 0;
+ for (byte b : bytes)
+ if (b == MAGIC_BYTE || b == MAGIC_BYTE + 1)
+ magic++;
+
+ byte eBytes[] = new byte[bytes.length + magic];
+
+ int i = 0;
+ for (byte b : bytes) {
+ if (b == MAGIC_BYTE || b == MAGIC_BYTE + 1) {
+ eBytes[i++] = MAGIC_BYTE + 1;
+ eBytes[i++] = (byte) (b - MAGIC_BYTE);
+ } else {
+ eBytes[i++] = b;
+ }
+ }
+
+ byte[] lenBytes = ByteBuffer.allocate(4).putInt(bytes.length).array();
+ synchronized (mLogFile) {
+ mLogFile.write(MAGIC_BYTE);
+ mLogFile.write(lenBytes);
+ mLogFile.write(eBytes);
+ }
}
- private void openLogFile (File cacheDir) throws FileNotFoundException {
+ private void openLogFile(File cacheDir) throws FileNotFoundException {
File logfile = new File(cacheDir, LOGFILE_NAME);
mLogFile = new FileOutputStream(logfile);
}
private void readLogCache(File cacheDir) {
- File logfile = new File(cacheDir, LOGFILE_NAME);
-
+ try {
+ File logfile = new File(cacheDir, LOGFILE_NAME);
- if (!logfile.exists() || !logfile.canRead())
- return;
+ if (!logfile.exists() || !logfile.canRead())
+ return;
+ readCacheContents(new FileInputStream(logfile));
- try {
+ } catch (java.io.IOException | java.lang.RuntimeException e) {
+ VpnStatus.logError("Reading cached logfile failed");
+ VpnStatus.logException(e);
+ e.printStackTrace();
+ // ignore reading file error
+ } finally {
+ synchronized (VpnStatus.readFileLock) {
+ VpnStatus.readFileLog = true;
+ VpnStatus.readFileLock.notifyAll();
+ }
+ }
+ }
- BufferedInputStream logFile = new BufferedInputStream(new FileInputStream(logfile));
- byte[] buf = new byte[8192];
- int read = logFile.read(buf, 0, 4);
- int itemsRead=0;
+ protected void readCacheContents(InputStream in) throws IOException {
+ BufferedInputStream logFile = new BufferedInputStream(in);
- while (read >= 4) {
- int len = ByteBuffer.wrap(buf, 0, 4).asIntBuffer().get();
+ byte[] buf = new byte[16384];
+ int read = logFile.read(buf, 0, 5);
+ int itemsRead = 0;
- // Marshalled LogItem
- read = logFile.read(buf, 0, len);
- Parcel p = Parcel.obtain();
- p.unmarshall(buf, 0, read);
- p.setDataPosition(0);
- VpnStatus.LogItem li = VpnStatus.LogItem.CREATOR.createFromParcel(p);
- if (li.verify()) {
- VpnStatus.newLogItem(li, true);
- } else {
- VpnStatus.logError(String.format(Locale.getDefault(),
- "Could not read log item from file: %d/%d: %s",
- read, len, bytesToHex(buf, Math.max(read,80))));
+ readloop:
+ while (read >= 5) {
+ int skipped = 0;
+ while (buf[skipped] != MAGIC_BYTE) {
+ skipped++;
+ if (!(logFile.read(buf, skipped + 4, 1) == 1) || skipped + 10 > buf.length) {
+ VpnStatus.logDebug(String.format(Locale.US, "Skipped %d bytes and no a magic byte found", skipped));
+ break readloop;
}
- p.recycle();
-
- //Next item
- read = logFile.read(buf, 0, 4);
- itemsRead++;
- if (itemsRead > 2*VpnStatus.MAXLOGENTRIES) {
- VpnStatus.logError("Too many logentries read from cache, aborting.");
- read = 0;
+ }
+ if (skipped > 0)
+ VpnStatus.logDebug(String.format(Locale.US, "Skipped %d bytes before finding a magic byte", skipped));
+
+ int len = ByteBuffer.wrap(buf, skipped + 1, 4).asIntBuffer().get();
+
+ // Marshalled LogItem
+ int pos = 0;
+ byte buf2[] = new byte[buf.length];
+
+ while (pos < len) {
+ byte b = (byte) logFile.read();
+ if (b == MAGIC_BYTE) {
+ VpnStatus.logDebug(String.format(Locale.US, "Unexpected magic byte found at pos %d, abort current log item", pos));
+ read = logFile.read(buf, 1, 4) + 1;
+ continue readloop;
+ } else if (b == MAGIC_BYTE + 1) {
+ b = (byte) logFile.read();
+ if (b == 0)
+ b = MAGIC_BYTE;
+ else if (b == 1)
+ b = MAGIC_BYTE + 1;
+ else {
+ VpnStatus.logDebug(String.format(Locale.US, "Escaped byte not 0 or 1: %d", b));
+ read = logFile.read(buf, 1, 4) + 1;
+ continue readloop;
+ }
}
+ buf2[pos++] = b;
+ }
+
+ restoreLogItem(buf2, len);
+ //Next item
+ read = logFile.read(buf, 0, 5);
+ itemsRead++;
+ if (itemsRead > 2 * VpnStatus.MAXLOGENTRIES) {
+ VpnStatus.logError("Too many logentries read from cache, aborting.");
+ read = 0;
}
- VpnStatus.logDebug(R.string.reread_log, itemsRead);
+ }
+ VpnStatus.logDebug(R.string.reread_log, itemsRead);
+ }
+ protected void restoreLogItem(byte[] buf, int len) throws UnsupportedEncodingException {
- } catch (java.io.IOException | java.lang.RuntimeException e) {
- VpnStatus.logError("Reading cached logfile failed");
- VpnStatus.logException(e);
- e.printStackTrace();
- // ignore reading file error
+ LogItem li = new LogItem(buf, len);
+ if (li.verify()) {
+ VpnStatus.newLogItem(li, true);
+ } else {
+ VpnStatus.logError(String.format(Locale.getDefault(),
+ "Could not read log item from file: %d: %s",
+ len, bytesToHex(buf, Math.max(len, 80))));
}
}
- final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
+ private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
+
public static String bytesToHex(byte[] bytes, int len) {
len = Math.min(bytes.length, len);
char[] hexChars = new char[len * 2];
- for ( int j = 0; j < len; j++ ) {
+ for (int j = 0; j < len; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];