Add support for deflate and brotli content encodings

This commit is contained in:
emanuele-f 2022-03-26 23:20:33 +01:00
parent ab4ac8f272
commit 32b71fef6f
3 changed files with 70 additions and 19 deletions

View File

@ -79,6 +79,7 @@ dependencies {
// Google
implementation 'com.google.android.material:material:1.5.0'
implementation 'com.google.code.gson:gson:2.8.9'
implementation 'org.brotli:dec:0.1.2'
// Third-party
implementation 'cat.ereza:customactivityoncrash:2.3.0'

View File

@ -27,16 +27,20 @@ import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;
import java.util.zip.Inflater;
import org.brotli.dec.BrotliInputStream;
public class HTTPReassembly {
private static final String TAG = "HTTPReassembly";
private static final int MAX_HEADERS_SIZE = 1024;
private boolean mReadingHeaders;
private boolean mGzipEncoding;
private boolean mChunkedEncoding;
private ContentEncoding mContentEncoding;
private int mContentLength;
private int mHeadersSize;
private final ArrayList<PayloadChunk> mHeaders = new ArrayList<>();
@ -51,9 +55,16 @@ public class HTTPReassembly {
reset();
}
private enum ContentEncoding {
UNKNOWN,
GZIP,
DEFLATE,
BROTLI,
}
private void reset() {
mReadingHeaders = true;
mGzipEncoding = false;
mContentEncoding = ContentEncoding.UNKNOWN;
mChunkedEncoding = false;
mContentLength = -1;
mHeadersSize = 0;
@ -95,7 +106,19 @@ public class HTTPReassembly {
String contentEncoding = line.substring(18);
Log.d(TAG, "Content-Encoding: " + contentEncoding);
mGzipEncoding = contentEncoding.equals("gzip");
switch (contentEncoding) {
case "gzip":
mContentEncoding = ContentEncoding.GZIP;
break;
case "deflate":
// test with http://carsten.codimi.de/gzip.yaws/daniels.html?deflate=on
mContentEncoding = ContentEncoding.DEFLATE;
break;
case "br":
// test with google.com
mContentEncoding = ContentEncoding.BROTLI;
break;
}
} else if(line.startsWith("content-type: ")) {
String contentType = line.substring(14);
Log.d(TAG, "Content-Type: " + contentType);
@ -197,22 +220,9 @@ public class HTTPReassembly {
PayloadChunk headers = reassembleChunks(mHeaders);
PayloadChunk body = mBody.size() > 0 ? reassembleChunks(mBody) : null;
if((body != null) && mGzipEncoding) {
// Decode the body
try(GZIPInputStream gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(body.payload))) {
try(ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
byte[] buf = new byte[1024];
int read;
while ((read = gzipInputStream.read(buf)) != -1)
bos.write(buf, 0, read);
body.payload = bos.toByteArray();
}
} catch (IOException ignored) {
Log.d(TAG, "GZIP decoding failed");
}
}
// Decode body
if((body != null) && (mContentEncoding != ContentEncoding.UNKNOWN))
decodeBody(body);
PayloadChunk to_add;
@ -241,6 +251,44 @@ public class HTTPReassembly {
}
}
private void decodeBody(PayloadChunk body) {
InputStream inputStream = null;
//Log.d(TAG, "Decoding as " + mContentEncoding.name().toLowerCase());
try(ByteArrayInputStream bis = new ByteArrayInputStream(body.payload)) {
switch (mContentEncoding) {
case GZIP:
inputStream = new GZIPInputStream(bis);
break;
case DEFLATE:
inputStream = new InflaterInputStream(bis, new Inflater(true));
break;
case BROTLI:
inputStream = new BrotliInputStream(bis);
break;
}
if(inputStream != null) {
try(ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
byte[] buf = new byte[1024];
int read;
while ((read = inputStream.read(buf)) != -1)
bos.write(buf, 0, read);
// success
body.payload = bos.toByteArray();
}
}
} catch (IOException ignored) {
Log.d(TAG, mContentEncoding.name().toLowerCase() + " decoding failed");
//ignored.printStackTrace();
} finally {
Utils.safeClose(inputStream);
}
}
private PayloadChunk reassembleChunks(ArrayList<PayloadChunk> chunks) {
if(chunks.size() == 1)
return chunks.get(0);

View File

@ -17,6 +17,8 @@
- nDPI: <a href='https://github.com/ntop/nDPI/blob/dev/COPYING'>LGPL-3.0</a>\n\n
- CustomActivityOnCrash: <a href='https://github.com/Ereza/CustomActivityOnCrash/blob/master/LICENSE'>Apache-2.0</a>\n\n
- Gson: <a href='https://github.com/google/gson/blob/master/LICENSE'>Apache-2.0</a>\n\n
- Brotli decoder: <a href='https://github.com/google/brotli/blob/master/LICENSE'>MIT</a>\n\n
- mitmproxy: <a href='https://github.com/mitmproxy/mitmproxy/blob/main/LICENSE'>MIT</a>\n\n
- MaxMind DB Reader: <a href='https://github.com/maxmind/MaxMind-DB-Reader-java/blob/main/LICENSE'>Apache-2.0</a>\n\n
- FlagKit: <a href='https://github.com/madebybowtie/FlagKit/blob/master/LICENSE'>MIT</a>\n\n
- IP Geolocation by <a href='https://db-ip.com'>DB-IP</a>\n\n