Throw IOException for all JS5 decompression errors

Should be nicer than the mixture of exception types we used before.

Signed-off-by: Graham <gpe@openrs2.dev>
Graham 4 years ago
parent 78f8069177
commit 0d384fa8f2
  1. 27
      cache/src/main/java/dev/openrs2/cache/Js5Compression.kt
  2. 16
      cache/src/test/java/dev/openrs2/cache/Js5CompressionTest.kt

@ -7,7 +7,7 @@ import dev.openrs2.crypto.xteaEncrypt
import io.netty.buffer.ByteBuf import io.netty.buffer.ByteBuf
import io.netty.buffer.ByteBufInputStream import io.netty.buffer.ByteBufInputStream
import io.netty.buffer.ByteBufOutputStream import io.netty.buffer.ByteBufOutputStream
import java.io.EOFException import java.io.IOException
public object Js5Compression { public object Js5Compression {
public fun compress(input: ByteBuf, type: Js5CompressionType, key: XteaKey = XteaKey.ZERO): ByteBuf { public fun compress(input: ByteBuf, type: Js5CompressionType, key: XteaKey = XteaKey.ZERO): ByteBuf {
@ -102,16 +102,18 @@ public object Js5Compression {
public fun uncompress(input: ByteBuf, key: XteaKey = XteaKey.ZERO): ByteBuf { public fun uncompress(input: ByteBuf, key: XteaKey = XteaKey.ZERO): ByteBuf {
val typeId = input.readUnsignedByte().toInt() val typeId = input.readUnsignedByte().toInt()
val type = Js5CompressionType.fromOrdinal(typeId) val type = Js5CompressionType.fromOrdinal(typeId)
require(type != null) { ?: throw IOException("Invalid compression type: $typeId")
"Invalid compression type: $typeId"
}
val len = input.readInt() val len = input.readInt()
require(len >= 0) { if (len < 0) {
"Length is negative: $len" throw IOException("Length is negative: $len")
} }
if (type == Js5CompressionType.NONE) { if (type == Js5CompressionType.NONE) {
if (input.readableBytes() < len) {
throw IOException("Data truncated")
}
input.readBytes(len).use { output -> input.readBytes(len).use { output ->
if (!key.isZero) { if (!key.isZero) {
output.xteaDecrypt(0, len, key) output.xteaDecrypt(0, len, key)
@ -120,10 +122,15 @@ public object Js5Compression {
} }
} }
decrypt(input, len + 4, key).use { plaintext -> val lenWithUncompressedLen = len + 4
if (input.readableBytes() < lenWithUncompressedLen) {
throw IOException("Compressed data truncated")
}
decrypt(input, lenWithUncompressedLen, key).use { plaintext ->
val uncompressedLen = plaintext.readInt() val uncompressedLen = plaintext.readInt()
require(uncompressedLen >= 0) { if (uncompressedLen < 0) {
"Uncompressed length is negative: $uncompressedLen" throw IOException("Uncompressed length is negative: $uncompressedLen")
} }
plaintext.alloc().buffer(uncompressedLen, uncompressedLen).use { output -> plaintext.alloc().buffer(uncompressedLen, uncompressedLen).use { output ->
@ -132,7 +139,7 @@ public object Js5Compression {
while (remaining > 0) { while (remaining > 0) {
val n = output.writeBytes(inputStream, remaining) val n = output.writeBytes(inputStream, remaining)
if (n == -1) { if (n == -1) {
throw EOFException() throw IOException("Uncompressed data truncated")
} }
remaining -= n remaining -= n
} }

@ -5,7 +5,7 @@ import dev.openrs2.crypto.XteaKey
import io.netty.buffer.ByteBuf import io.netty.buffer.ByteBuf
import io.netty.buffer.Unpooled import io.netty.buffer.Unpooled
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import java.io.EOFException import java.io.IOException
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotEquals import kotlin.test.assertNotEquals
@ -260,7 +260,7 @@ object Js5CompressionTest {
@Test @Test
fun testInvalidType() { fun testInvalidType() {
read("invalid-type.dat").use { compressed -> read("invalid-type.dat").use { compressed ->
assertThrows<IllegalArgumentException> { assertThrows<IOException> {
Js5Compression.uncompress(compressed).release() Js5Compression.uncompress(compressed).release()
} }
} }
@ -269,7 +269,7 @@ object Js5CompressionTest {
@Test @Test
fun testInvalidLength() { fun testInvalidLength() {
read("invalid-length.dat").use { compressed -> read("invalid-length.dat").use { compressed ->
assertThrows<IllegalArgumentException> { assertThrows<IOException> {
Js5Compression.uncompress(compressed).release() Js5Compression.uncompress(compressed).release()
} }
} }
@ -278,7 +278,7 @@ object Js5CompressionTest {
@Test @Test
fun testInvalidUncompressedLength() { fun testInvalidUncompressedLength() {
read("invalid-uncompressed-length.dat").use { compressed -> read("invalid-uncompressed-length.dat").use { compressed ->
assertThrows<IllegalArgumentException> { assertThrows<IOException> {
Js5Compression.uncompress(compressed).release() Js5Compression.uncompress(compressed).release()
} }
} }
@ -287,7 +287,7 @@ object Js5CompressionTest {
@Test @Test
fun testNoneEof() { fun testNoneEof() {
read("none-eof.dat").use { compressed -> read("none-eof.dat").use { compressed ->
assertThrows<IndexOutOfBoundsException> { assertThrows<IOException> {
Js5Compression.uncompress(compressed).release() Js5Compression.uncompress(compressed).release()
} }
} }
@ -296,7 +296,7 @@ object Js5CompressionTest {
@Test @Test
fun testBzip2Eof() { fun testBzip2Eof() {
read("bzip2-eof.dat").use { compressed -> read("bzip2-eof.dat").use { compressed ->
assertThrows<EOFException> { assertThrows<IOException> {
Js5Compression.uncompress(compressed).release() Js5Compression.uncompress(compressed).release()
} }
} }
@ -305,7 +305,7 @@ object Js5CompressionTest {
@Test @Test
fun testGzipEof() { fun testGzipEof() {
read("gzip-eof.dat").use { compressed -> read("gzip-eof.dat").use { compressed ->
assertThrows<EOFException> { assertThrows<IOException> {
Js5Compression.uncompress(compressed).release() Js5Compression.uncompress(compressed).release()
} }
} }
@ -314,7 +314,7 @@ object Js5CompressionTest {
@Test @Test
fun testLzmaEof() { fun testLzmaEof() {
read("lzma-eof.dat").use { compressed -> read("lzma-eof.dat").use { compressed ->
assertThrows<EOFException> { assertThrows<IOException> {
Js5Compression.uncompress(compressed).release() Js5Compression.uncompress(compressed).release()
} }
} }

Loading…
Cancel
Save