From d94bfd80b7bdb55c8948e98d6f02debdd8fc798b Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Sun, 21 Nov 2021 18:04:37 +0100 Subject: [PATCH 1/2] Simplify opus parsing --- .../parser/media/vorbis/OggInputStream.java | 81 ------------------- .../vorbis/VorbisCommentChapterReader.java | 5 -- .../media/vorbis/VorbisCommentReader.java | 58 +++++++------ 3 files changed, 32 insertions(+), 112 deletions(-) delete mode 100644 parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/OggInputStream.java diff --git a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/OggInputStream.java b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/OggInputStream.java deleted file mode 100644 index ed495bcf3..000000000 --- a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/OggInputStream.java +++ /dev/null @@ -1,81 +0,0 @@ -package de.danoeh.antennapod.parser.media.vorbis; - -import org.apache.commons.io.IOUtils; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; - -class OggInputStream extends InputStream { - private final InputStream input; - - /** True if OggInputStream is currently inside an Ogg page. */ - private boolean isInPage; - private long bytesLeft; - - public OggInputStream(InputStream input) { - super(); - isInPage = false; - this.input = input; - } - - @Override - public int read() throws IOException { - if (!isInPage) { - readOggPage(); - } - - if (isInPage && bytesLeft > 0) { - int result = input.read(); - bytesLeft -= 1; - if (bytesLeft == 0) { - isInPage = false; - } - return result; - } - return -1; - } - - private void readOggPage() throws IOException { - // find OggS - int[] buffer = new int[4]; - int c; - boolean isInOggS = false; - while ((c = input.read()) != -1) { - switch (c) { - case 'O': - isInOggS = true; - buffer[0] = c; - break; - case 'g': - if (buffer[1] != c) { - buffer[1] = c; - } else { - buffer[2] = c; - } - break; - case 'S': - buffer[3] = c; - break; - default: - if (isInOggS) { - Arrays.fill(buffer, 0); - isInOggS = false; - } - } - if (buffer[0] == 'O' && buffer[1] == 'g' && buffer[2] == 'g' - && buffer[3] == 'S') { - break; - } - } - // read segments - IOUtils.skipFully(input, 22); - bytesLeft = 0; - int numSegments = input.read(); - for (int i = 0; i < numSegments; i++) { - bytesLeft += input.read(); - } - isInPage = true; - } - -} diff --git a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentChapterReader.java b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentChapterReader.java index f833f683b..82455d180 100644 --- a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentChapterReader.java +++ b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentChapterReader.java @@ -65,11 +65,6 @@ public class VorbisCommentChapterReader extends VorbisCommentReader { } } - @Override - public void onNoVorbisCommentFound() { - System.out.println("No vorbis comment found"); - } - @Override public void onEndOfComment() { System.out.println("End of comment"); diff --git a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReader.java b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReader.java index 319d3759c..37ea14c65 100644 --- a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReader.java +++ b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReader.java @@ -35,36 +35,43 @@ public abstract class VorbisCommentReader { */ protected abstract void onContentVectorValue(String key, String value) throws VorbisCommentReaderException; - protected abstract void onNoVorbisCommentFound(); - protected abstract void onEndOfComment(); protected abstract void onError(VorbisCommentReaderException exception); public void readInputStream(InputStream input) throws VorbisCommentReaderException { try { - // look for identification header - if (findIdentificationHeader(input)) { - onVorbisCommentFound(); - input = new OggInputStream(input); - if (findCommentHeader(input)) { - VorbisCommentHeader commentHeader = readCommentHeader(input); - onVorbisCommentHeaderFound(commentHeader); - for (int i = 0; i < commentHeader.getUserCommentLength(); i++) { - readUserComment(input); - } - onEndOfComment(); - } else { - onError(new VorbisCommentReaderException("No comment header found")); - } - } else { - onNoVorbisCommentFound(); + findIdentificationHeader(input); + onVorbisCommentFound(); + findOggPage(input); + findCommentHeader(input); + VorbisCommentHeader commentHeader = readCommentHeader(input); + onVorbisCommentHeaderFound(commentHeader); + for (int i = 0; i < commentHeader.getUserCommentLength(); i++) { + readUserComment(input); } + onEndOfComment(); } catch (IOException e) { onError(new VorbisCommentReaderException(e)); } } + private void findOggPage(InputStream input) throws IOException { + // find OggS + byte[] buffer = new byte[4]; + final byte[] oggPageHeader = {'O', 'g', 'g', 'S'}; + for (int bytesRead = 0; bytesRead < SECOND_PAGE_MAX_LENGTH; bytesRead++) { + buffer[bytesRead % buffer.length] = (byte) input.read(); + if (bufferMatches(buffer, oggPageHeader, bytesRead)) { + break; + } + } + // read segments + IOUtils.skipFully(input, 22); + int numSegments = input.read(); + IOUtils.skipFully(input, numSegments); + } + private void readUserComment(InputStream input) throws VorbisCommentReaderException { try { long vectorLength = EndianUtils.readSwappedUnsignedInteger(input); @@ -93,33 +100,32 @@ public abstract class VorbisCommentReader { * identification header is found, it will be skipped completely and the * method will return true, otherwise false. */ - private boolean findIdentificationHeader(InputStream input) throws IOException { + private void findIdentificationHeader(InputStream input) throws IOException { byte[] buffer = new byte[FIRST_OPUS_PAGE_LENGTH]; IOUtils.readFully(input, buffer); final byte[] oggIdentificationHeader = new byte[]{ PACKET_TYPE_IDENTIFICATION, 'v', 'o', 'r', 'b', 'i', 's' }; for (int i = 6; i < buffer.length; i++) { if (bufferMatches(buffer, oggIdentificationHeader, i)) { IOUtils.skip(input, FIRST_OGG_PAGE_LENGTH - FIRST_OPUS_PAGE_LENGTH); - return true; } else if (bufferMatches(buffer, "OpusHead".getBytes(), i)) { - return true; + return; } } - return false; + throw new IOException("No identification header found"); } - private boolean findCommentHeader(InputStream input) throws IOException { + private void findCommentHeader(InputStream input) throws IOException { byte[] buffer = new byte[64]; // Enough space for some bytes. Used circularly. final byte[] oggCommentHeader = new byte[]{ PACKET_TYPE_COMMENT, 'v', 'o', 'r', 'b', 'i', 's' }; for (int bytesRead = 0; bytesRead < SECOND_PAGE_MAX_LENGTH; bytesRead++) { buffer[bytesRead % buffer.length] = (byte) input.read(); if (bufferMatches(buffer, oggCommentHeader, bytesRead)) { - return true; + return; } else if (bufferMatches(buffer, "OpusTags".getBytes(), bytesRead)) { - return true; + return; } } - return false; + throw new IOException("No comment header found"); } /** From e2600f54e4ec8abe55b5e95fb338b5eb11b5db9a Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Sun, 21 Nov 2021 18:49:50 +0100 Subject: [PATCH 2/2] Stop parsing if we encounter an unrealistically long comment --- .../antennapod/parser/media/vorbis/VorbisCommentReader.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReader.java b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReader.java index 37ea14c65..b4f87bd70 100644 --- a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReader.java +++ b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReader.java @@ -75,10 +75,14 @@ public abstract class VorbisCommentReader { private void readUserComment(InputStream input) throws VorbisCommentReaderException { try { long vectorLength = EndianUtils.readSwappedUnsignedInteger(input); + if (vectorLength > 20 * 1024 * 1024) { + // Avoid reading entire file if it is encoded incorrectly + throw new VorbisCommentReaderException("User comment unrealistically long: " + vectorLength); + } String key = readContentVectorKey(input, vectorLength).toLowerCase(Locale.US); boolean readValue = onContentVectorKey(key); if (readValue) { - String value = readUtf8String(input, (int) (vectorLength - key.length() - 1)); + String value = readUtf8String(input, vectorLength - key.length() - 1); onContentVectorValue(key, value); } else { IOUtils.skipFully(input, vectorLength - key.length() - 1);